Merge branch 'master' of github.com:LaurentGomila/SFML

This commit is contained in:
Laurent Gomila 2014-07-11 17:43:50 +02:00
commit 337df1ea5f
7 changed files with 196 additions and 68 deletions

View File

@ -144,7 +144,9 @@ public :
/// \brief Change the current playing position of the sound /// \brief Change the current playing position of the sound
/// ///
/// The playing position can be changed when the sound is /// The playing position can be changed when the sound is
/// either paused or playing. /// either paused or playing. Changing the playing position
/// when the sound is stopped has no effect, since playing
/// the sound would reset its position.
/// ///
/// \param timeOffset New playing position, from the beginning of the sound /// \param timeOffset New playing position, from the beginning of the sound
/// ///

View File

@ -32,6 +32,7 @@
#include <SFML/Audio/SoundSource.hpp> #include <SFML/Audio/SoundSource.hpp>
#include <SFML/System/Thread.hpp> #include <SFML/System/Thread.hpp>
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
#include <SFML/System/Mutex.hpp>
#include <cstdlib> #include <cstdlib>
@ -65,8 +66,8 @@ public :
/// \brief Start or resume playing the audio stream /// \brief Start or resume playing the audio stream
/// ///
/// This function starts the stream if it was stopped, resumes /// This function starts the stream if it was stopped, resumes
/// it if it was paused, and restarts it from beginning if it /// it if it was paused, and restarts it from the beginning if
/// was it already playing. /// it was already playing.
/// This function uses its own thread so that it doesn't block /// This function uses its own thread so that it doesn't block
/// the rest of the program while the stream is played. /// the rest of the program while the stream is played.
/// ///
@ -131,7 +132,9 @@ public :
/// \brief Change the current playing position of the stream /// \brief Change the current playing position of the stream
/// ///
/// The playing position can be changed when the stream is /// The playing position can be changed when the stream is
/// either paused or playing. /// either paused or playing. Changing the playing position
/// when the stream is stopped has no effect, since playing
/// the stream would reset its position.
/// ///
/// \param timeOffset New playing position, from the beginning of the stream /// \param timeOffset New playing position, from the beginning of the stream
/// ///
@ -285,6 +288,8 @@ private :
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Thread m_thread; ///< Thread running the background tasks Thread m_thread; ///< Thread running the background tasks
mutable Mutex m_threadMutex; ///< Thread mutex
Status m_threadStartState; ///< State the thread starts in (Playing, Paused, Stopped)
bool m_isStreaming; ///< Streaming state (true = playing, false = stopped) bool m_isStreaming; ///< Streaming state (true = playing, false = stopped)
unsigned int m_buffers[BufferCount]; ///< Sound buffers used to store temporary audio data unsigned int m_buffers[BufferCount]; ///< Sound buffers used to store temporary audio data
unsigned int m_channelCount; ///< Number of channels (1 = mono, 2 = stereo, ...) unsigned int m_channelCount; ///< Number of channels (1 = mono, 2 = stereo, ...)

View File

@ -229,7 +229,7 @@ public :
/// \param data Data containing the raw listing /// \param data Data containing the raw listing
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
ListingResponse(const Response& response, const std::vector<char>& data); ListingResponse(const Response& response, const std::string& data);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the array of directory/file names /// \brief Return the array of directory/file names
@ -452,6 +452,9 @@ public :
/// current working directory of the server, and the local /// current working directory of the server, and the local
/// destination path is relative to the current directory /// destination path is relative to the current directory
/// of your application. /// of your application.
/// If a file with the same filename as the distant file
/// already exists in the local destination path, it will
/// be overwritten.
/// ///
/// \param remoteFile Filename of the distant file to download /// \param remoteFile Filename of the distant file to download
/// \param localPath The directory in which to put the file on the local computer /// \param localPath The directory in which to put the file on the local computer

View File

@ -306,7 +306,9 @@ public :
/// ///
/// \param width Icon's width, in pixels /// \param width Icon's width, in pixels
/// \param height Icon's height, in pixels /// \param height Icon's height, in pixels
/// \param pixels Pointer to the array of pixels in memory /// \param pixels Pointer to the array of pixels in memory. The
/// pixels are copied, so you need not keep the
/// source alive after calling this function.
/// ///
/// \see setTitle /// \see setTitle
/// ///

View File

@ -30,6 +30,7 @@
#include <SFML/Audio/ALCheck.hpp> #include <SFML/Audio/ALCheck.hpp>
#include <SFML/System/Sleep.hpp> #include <SFML/System/Sleep.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/Lock.hpp>
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable : 4355) // 'this' used in base member initializer list #pragma warning(disable : 4355) // 'this' used in base member initializer list
@ -41,6 +42,8 @@ namespace sf
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SoundStream::SoundStream() : SoundStream::SoundStream() :
m_thread (&SoundStream::streamData, this), m_thread (&SoundStream::streamData, this),
m_threadMutex (),
m_threadStartState(Stopped),
m_isStreaming (false), m_isStreaming (false),
m_channelCount (0), m_channelCount (0),
m_sampleRate (0), m_sampleRate (0),
@ -56,7 +59,15 @@ m_samplesProcessed(0)
SoundStream::~SoundStream() SoundStream::~SoundStream()
{ {
// Stop the sound if it was playing // Stop the sound if it was playing
stop();
// Request the thread to terminate
{
Lock lock(m_threadMutex);
m_isStreaming = false;
}
// Wait for the thread to terminate
m_thread.wait();
} }
@ -89,12 +100,30 @@ void SoundStream::play()
return; return;
} }
// If the sound is already playing (probably paused), just resume it bool isStreaming = false;
if (m_isStreaming) Status threadStartState = Stopped;
{ {
Lock lock(m_threadMutex);
isStreaming = m_isStreaming;
threadStartState = m_threadStartState;
}
if (isStreaming && (threadStartState == Paused))
{
// If the sound is paused, resume it
Lock lock(m_threadMutex);
m_threadStartState = Playing;
alCheck(alSourcePlay(m_source)); alCheck(alSourcePlay(m_source));
return; return;
} }
else if (isStreaming && (threadStartState == Playing))
{
// If the sound is playing, stop it and continue as if it was stopped
stop();
}
// Move to the beginning // Move to the beginning
onSeek(Time::Zero); onSeek(Time::Zero);
@ -102,6 +131,7 @@ void SoundStream::play()
// Start updating the stream in a separate thread to avoid blocking the application // Start updating the stream in a separate thread to avoid blocking the application
m_samplesProcessed = 0; m_samplesProcessed = 0;
m_isStreaming = true; m_isStreaming = true;
m_threadStartState = Playing;
m_thread.launch(); m_thread.launch();
} }
@ -109,6 +139,16 @@ void SoundStream::play()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void SoundStream::pause() void SoundStream::pause()
{ {
// Handle pause() being called before the thread has started
{
Lock lock(m_threadMutex);
if (!m_isStreaming)
return;
m_threadStartState = Paused;
}
alCheck(alSourcePause(m_source)); alCheck(alSourcePause(m_source));
} }
@ -116,9 +156,20 @@ void SoundStream::pause()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void SoundStream::stop() void SoundStream::stop()
{ {
// Request the thread to terminate
{
Lock lock(m_threadMutex);
m_isStreaming = false;
}
// Wait for the thread to terminate // Wait for the thread to terminate
m_isStreaming = false;
m_thread.wait(); m_thread.wait();
// Move to the beginning
onSeek(Time::Zero);
// Reset the playing position
m_samplesProcessed = 0;
} }
@ -142,8 +193,13 @@ SoundStream::Status SoundStream::getStatus() const
Status status = SoundSource::getStatus(); Status status = SoundSource::getStatus();
// To compensate for the lag between play() and alSourceplay() // To compensate for the lag between play() and alSourceplay()
if ((status == Stopped) && m_isStreaming) if (status == Stopped)
status = Playing; {
Lock lock(m_threadMutex);
if (m_isStreaming)
status = m_threadStartState;
}
return status; return status;
} }
@ -152,6 +208,9 @@ SoundStream::Status SoundStream::getStatus() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void SoundStream::setPlayingOffset(Time timeOffset) void SoundStream::setPlayingOffset(Time timeOffset)
{ {
// Get old playing status
Status oldStatus = getStatus();
// Stop the stream // Stop the stream
stop(); stop();
@ -160,7 +219,12 @@ void SoundStream::setPlayingOffset(Time timeOffset)
// Restart streaming // Restart streaming
m_samplesProcessed = static_cast<Uint64>(timeOffset.asSeconds() * m_sampleRate * m_channelCount); m_samplesProcessed = static_cast<Uint64>(timeOffset.asSeconds() * m_sampleRate * m_channelCount);
if (oldStatus == Stopped)
return;
m_isStreaming = true; m_isStreaming = true;
m_threadStartState = oldStatus;
m_thread.launch(); m_thread.launch();
} }
@ -199,19 +263,42 @@ bool SoundStream::getLoop() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void SoundStream::streamData() void SoundStream::streamData()
{ {
// Create the buffers bool requestStop = false;
alCheck(alGenBuffers(BufferCount, m_buffers));
for (int i = 0; i < BufferCount; ++i)
m_endBuffers[i] = false;
// Fill the queue
bool requestStop = fillQueue();
// Play the sound
alCheck(alSourcePlay(m_source));
while (m_isStreaming)
{ {
Lock lock(m_threadMutex);
// Check if the thread was launched Stopped
if (m_threadStartState == Stopped)
{
m_isStreaming = false;
return;
}
// Create the buffers
alCheck(alGenBuffers(BufferCount, m_buffers));
for (int i = 0; i < BufferCount; ++i)
m_endBuffers[i] = false;
// Fill the queue
requestStop = fillQueue();
// Play the sound
alCheck(alSourcePlay(m_source));
// Check if the thread was launched Paused
if (m_threadStartState == Paused)
alCheck(alSourcePause(m_source));
}
for (;;)
{
{
Lock lock(m_threadMutex);
if (!m_isStreaming)
break;
}
// The stream has been interrupted! // The stream has been interrupted!
if (SoundSource::getStatus() == Stopped) if (SoundSource::getStatus() == Stopped)
{ {
@ -223,6 +310,7 @@ void SoundStream::streamData()
else else
{ {
// End streaming // End streaming
Lock lock(m_threadMutex);
m_isStreaming = false; m_isStreaming = false;
} }
} }
@ -266,6 +354,7 @@ void SoundStream::streamData()
<< "and initialize() has been called correctly" << std::endl; << "and initialize() has been called correctly" << std::endl;
// Abort streaming (exit main loop) // Abort streaming (exit main loop)
Lock lock(m_threadMutex);
m_isStreaming = false; m_isStreaming = false;
requestStop = true; requestStop = true;
break; break;

View File

@ -27,11 +27,13 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Network/Ftp.hpp> #include <SFML/Network/Ftp.hpp>
#include <SFML/Network/IpAddress.hpp> #include <SFML/Network/IpAddress.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include <sstream> #include <sstream>
#include <cstdio>
namespace sf namespace sf
@ -48,10 +50,10 @@ public :
Ftp::Response open(Ftp::TransferMode mode); Ftp::Response open(Ftp::TransferMode mode);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void send(const std::vector<char>& data); void send(std::istream& stream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void receive(std::vector<char>& data); void receive(std::ostream& stream);
private : private :
@ -115,17 +117,16 @@ const std::string& Ftp::DirectoryResponse::getDirectory() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::vector<char>& data) : Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::string& data) :
Ftp::Response(response) Ftp::Response(response)
{ {
if (isOk()) if (isOk())
{ {
// Fill the array of strings // Fill the array of strings
std::string paths(data.begin(), data.end());
std::string::size_type lastPos = 0; std::string::size_type lastPos = 0;
for (std::string::size_type pos = paths.find("\r\n"); pos != std::string::npos; pos = paths.find("\r\n", lastPos)) for (std::string::size_type pos = data.find("\r\n"); pos != std::string::npos; pos = data.find("\r\n", lastPos))
{ {
m_listing.push_back(paths.substr(lastPos, pos - lastPos)); m_listing.push_back(data.substr(lastPos, pos - lastPos));
lastPos = pos + 2; lastPos = pos + 2;
} }
} }
@ -206,7 +207,7 @@ Ftp::DirectoryResponse Ftp::getWorkingDirectory()
Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory) Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory)
{ {
// Open a data channel on default port (20) using ASCII transfer mode // Open a data channel on default port (20) using ASCII transfer mode
std::vector<char> directoryData; std::ostringstream directoryData;
DataChannel data(*this); DataChannel data(*this);
Response response = data.open(Ascii); Response response = data.open(Ascii);
if (response.isOk()) if (response.isOk())
@ -223,7 +224,7 @@ Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory)
} }
} }
return ListingResponse(response, directoryData); return ListingResponse(response, directoryData.str());
} }
@ -285,33 +286,34 @@ Ftp::Response Ftp::download(const std::string& remoteFile, const std::string& lo
response = sendCommand("RETR", remoteFile); response = sendCommand("RETR", remoteFile);
if (response.isOk()) if (response.isOk())
{ {
// Extract the filename from the file path
std::string filename = remoteFile;
std::string::size_type pos = filename.find_last_of("/\\");
if (pos != std::string::npos)
filename = filename.substr(pos + 1);
// Make sure the destination path ends with a slash
std::string path = localPath;
if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/'))
path += "/";
// Create the file and truncate it if necessary
std::ofstream file((path + filename).c_str(), std::ios_base::binary | std::ios_base::trunc);
if (!file)
return Response(Response::InvalidFile);
// Receive the file data // Receive the file data
std::vector<char> fileData; data.receive(file);
data.receive(fileData);
// Close the file
file.close();
// Get the response from the server // Get the response from the server
response = getResponse(); response = getResponse();
if (response.isOk())
{
// Extract the filename from the file path
std::string filename = remoteFile;
std::string::size_type pos = filename.find_last_of("/\\");
if (pos != std::string::npos)
filename = filename.substr(pos + 1);
// Make sure the destination path ends with a slash // If the download was unsuccessful, delete the partial file
std::string path = localPath; if (!response.isOk())
if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/')) std::remove((path + filename).c_str());
path += "/";
// Create the file and copy the received data into it
std::ofstream file((path + filename).c_str(), std::ios_base::binary);
if (!file)
return Response(Response::InvalidFile);
if (!fileData.empty())
file.write(&fileData[0], static_cast<std::streamsize>(fileData.size()));
}
} }
} }
@ -327,13 +329,6 @@ Ftp::Response Ftp::upload(const std::string& localFile, const std::string& remot
if (!file) if (!file)
return Response(Response::InvalidFile); return Response(Response::InvalidFile);
file.seekg(0, std::ios::end);
std::size_t length = static_cast<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
std::vector<char> fileData(length);
if (length > 0)
file.read(&fileData[0], static_cast<std::streamsize>(length));
// Extract the filename from the file path // Extract the filename from the file path
std::string filename = localFile; std::string filename = localFile;
std::string::size_type pos = filename.find_last_of("/\\"); std::string::size_type pos = filename.find_last_of("/\\");
@ -355,7 +350,7 @@ Ftp::Response Ftp::upload(const std::string& localFile, const std::string& remot
if (response.isOk()) if (response.isOk())
{ {
// Send the file data // Send the file data
data.send(fileData); data.send(file);
// Get the response from the server // Get the response from the server
response = getResponse(); response = getResponse();
@ -584,15 +579,20 @@ Ftp::Response Ftp::DataChannel::open(Ftp::TransferMode mode)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Ftp::DataChannel::receive(std::vector<char>& data) void Ftp::DataChannel::receive(std::ostream& stream)
{ {
// Receive data // Receive data
data.clear();
char buffer[1024]; char buffer[1024];
std::size_t received; std::size_t received;
while (m_dataSocket.receive(buffer, sizeof(buffer), received) == Socket::Done) while (m_dataSocket.receive(buffer, sizeof(buffer), received) == Socket::Done)
{ {
std::copy(buffer, buffer + received, std::back_inserter(data)); stream.write(buffer, static_cast<std::streamsize>(received));
if (!stream.good())
{
err() << "FTP Error: Writing to the file has failed" << std::endl;
break;
}
} }
// Close the data socket // Close the data socket
@ -601,11 +601,37 @@ void Ftp::DataChannel::receive(std::vector<char>& data)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Ftp::DataChannel::send(const std::vector<char>& data) void Ftp::DataChannel::send(std::istream& stream)
{ {
// Send data // Send data
if (!data.empty()) char buffer[1024];
m_dataSocket.send(&data[0], data.size()); std::size_t count;
for (;;)
{
// read some data from the stream
stream.read(buffer, sizeof(buffer));
if (!stream.good() && !stream.eof())
{
err() << "FTP Error: Reading from the file has failed" << std::endl;
break;
}
count = stream.gcount();
if (count > 0)
{
// we could read more data from the stream: send them
if (m_dataSocket.send(buffer, count) != Socket::Done)
break;
}
else
{
// no more data: exit the loop
break;
}
}
// Close the data socket // Close the data socket
m_dataSocket.disconnect(); m_dataSocket.disconnect();

View File

@ -180,7 +180,8 @@ bool EglContext::makeCurrent()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void EglContext::display() void EglContext::display()
{ {
eglCheck(eglSwapBuffers(m_display, m_surface)); if (m_surface != EGL_NO_SURFACE)
eglCheck(eglSwapBuffers(m_display, m_surface));
} }