201 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
// Headers
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
#include <SFML/Audio.hpp>
 | 
						|
#include <SFML/Network.hpp>
 | 
						|
#include <iomanip>
 | 
						|
#include <iostream>
 | 
						|
#include <iterator>
 | 
						|
 | 
						|
 | 
						|
const sf::Uint8 audioData   = 1;
 | 
						|
const sf::Uint8 endOfStream = 2;
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
/// Customized sound stream for acquiring audio data
 | 
						|
/// from the network
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
class NetworkAudioStream : public sf::SoundStream
 | 
						|
{
 | 
						|
public:
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    /// Default constructor
 | 
						|
    ///
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    NetworkAudioStream() :
 | 
						|
    m_offset     (0),
 | 
						|
    m_hasFinished(false)
 | 
						|
    {
 | 
						|
        // Set the sound parameters
 | 
						|
        initialize(1, 44100);
 | 
						|
    }
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    /// Run the server, stream audio data from the client
 | 
						|
    ///
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    void start(unsigned short port)
 | 
						|
    {
 | 
						|
        if (!m_hasFinished)
 | 
						|
        {
 | 
						|
            // Listen to the given port for incoming connections
 | 
						|
            if (m_listener.listen(port) != sf::Socket::Done)
 | 
						|
                return;
 | 
						|
            std::cout << "Server is listening to port " << port << ", waiting for connections... " << std::endl;
 | 
						|
 | 
						|
            // Wait for a connection
 | 
						|
            if (m_listener.accept(m_client) != sf::Socket::Done)
 | 
						|
                return;
 | 
						|
            std::cout << "Client connected: " << m_client.getRemoteAddress() << std::endl;
 | 
						|
 | 
						|
            // Start playback
 | 
						|
            play();
 | 
						|
 | 
						|
            // Start receiving audio data
 | 
						|
            receiveLoop();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Start playback
 | 
						|
            play();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    /// /see SoundStream::OnGetData
 | 
						|
    ///
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    virtual bool onGetData(sf::SoundStream::Chunk& data)
 | 
						|
    {
 | 
						|
        // We have reached the end of the buffer and all audio data have been played: we can stop playback
 | 
						|
        if ((m_offset >= m_samples.size()) && m_hasFinished)
 | 
						|
            return false;
 | 
						|
 | 
						|
        // No new data has arrived since last update: wait until we get some
 | 
						|
        while ((m_offset >= m_samples.size()) && !m_hasFinished)
 | 
						|
            sf::sleep(sf::milliseconds(10));
 | 
						|
 | 
						|
        // Copy samples into a local buffer to avoid synchronization problems
 | 
						|
        // (don't forget that we run in two separate threads)
 | 
						|
        {
 | 
						|
            sf::Lock lock(m_mutex);
 | 
						|
            m_tempBuffer.assign(m_samples.begin() + m_offset, m_samples.end());
 | 
						|
        }
 | 
						|
 | 
						|
        // Fill audio data to pass to the stream
 | 
						|
        data.samples     = &m_tempBuffer[0];
 | 
						|
        data.sampleCount = m_tempBuffer.size();
 | 
						|
 | 
						|
        // Update the playing offset
 | 
						|
        m_offset += m_tempBuffer.size();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    /// /see SoundStream::OnSeek
 | 
						|
    ///
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    virtual void onSeek(sf::Time timeOffset)
 | 
						|
    {
 | 
						|
        m_offset = timeOffset.asMilliseconds() * getSampleRate() * getChannelCount() / 1000;
 | 
						|
    }
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    /// Get audio data from the client until playback is stopped
 | 
						|
    ///
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    void receiveLoop()
 | 
						|
    {
 | 
						|
        while (!m_hasFinished)
 | 
						|
        {
 | 
						|
            // Get waiting audio data from the network
 | 
						|
            sf::Packet packet;
 | 
						|
            if (m_client.receive(packet) != sf::Socket::Done)
 | 
						|
                break;
 | 
						|
 | 
						|
            // Extract the message ID
 | 
						|
            sf::Uint8 id;
 | 
						|
            packet >> id;
 | 
						|
 | 
						|
            if (id == audioData)
 | 
						|
            {
 | 
						|
                // Extract audio samples from the packet, and append it to our samples buffer
 | 
						|
                const sf::Int16* samples     = reinterpret_cast<const sf::Int16*>(static_cast<const char*>(packet.getData()) + 1);
 | 
						|
                std::size_t      sampleCount = (packet.getDataSize() - 1) / sizeof(sf::Int16);
 | 
						|
 | 
						|
                // Don't forget that the other thread can access the sample array at any time
 | 
						|
                // (so we protect any operation on it with the mutex)
 | 
						|
                {
 | 
						|
                    sf::Lock lock(m_mutex);
 | 
						|
                    std::copy(samples, samples + sampleCount, std::back_inserter(m_samples));
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (id == endOfStream)
 | 
						|
            {
 | 
						|
                // End of stream reached: we stop receiving audio data
 | 
						|
                std::cout << "Audio data has been 100% received!" << std::endl;
 | 
						|
                m_hasFinished = true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Something's wrong...
 | 
						|
                std::cout << "Invalid packet received..." << std::endl;
 | 
						|
                m_hasFinished = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    // Member data
 | 
						|
    ////////////////////////////////////////////////////////////
 | 
						|
    sf::TcpListener        m_listener;
 | 
						|
    sf::TcpSocket          m_client;
 | 
						|
    sf::Mutex              m_mutex;
 | 
						|
    std::vector<sf::Int16> m_samples;
 | 
						|
    std::vector<sf::Int16> m_tempBuffer;
 | 
						|
    std::size_t            m_offset;
 | 
						|
    bool                   m_hasFinished;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
/// Launch a server and wait for incoming audio data from
 | 
						|
/// a connected client
 | 
						|
///
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
void doServer(unsigned short port)
 | 
						|
{
 | 
						|
    // Build an audio stream to play sound data as it is received through the network
 | 
						|
    NetworkAudioStream audioStream;
 | 
						|
    audioStream.start(port);
 | 
						|
 | 
						|
    // Loop until the sound playback is finished
 | 
						|
    while (audioStream.getStatus() != sf::SoundStream::Stopped)
 | 
						|
    {
 | 
						|
        // Leave some CPU time for other threads
 | 
						|
        sf::sleep(sf::milliseconds(100));
 | 
						|
    }
 | 
						|
 | 
						|
    std::cin.ignore(10000, '\n');
 | 
						|
 | 
						|
    // Wait until the user presses 'enter' key
 | 
						|
    std::cout << "Press enter to replay the sound..." << std::endl;
 | 
						|
    std::cin.ignore(10000, '\n');
 | 
						|
 | 
						|
    // Replay the sound (just to make sure replaying the received data is OK)
 | 
						|
    audioStream.play();
 | 
						|
 | 
						|
    // Loop until the sound playback is finished
 | 
						|
    while (audioStream.getStatus() != sf::SoundStream::Stopped)
 | 
						|
    {
 | 
						|
        // Leave some CPU time for other threads
 | 
						|
        sf::sleep(sf::milliseconds(100));
 | 
						|
    }
 | 
						|
}
 |