1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "Server.hpp"
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
#include <iostream>
#include <mutex>
#include <optional>
#include <vector>
#include <cstdint>
#include <cstring>
constexpr std::uint8_t serverAudioData = 1;
constexpr std::uint8_t serverEndOfStream = 2;
////////////////////////////////////////////////////////////
/// Customized sound stream for acquiring audio data
/// from the network
////////////////////////////////////////////////////////////
class NetworkAudioStream : public sf::SoundStream
{
public:
////////////////////////////////////////////////////////////
/// Default constructor
///
////////////////////////////////////////////////////////////
NetworkAudioStream()
{
// Set the sound parameters
initialize(1, 44100, {sf::SoundChannel::Mono});
}
////////////////////////////////////////////////////////////
/// 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::Status::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::Status::Done)
return;
std::cout << "Client connected: " << m_client.getRemoteAddress().value() << std::endl;
// Start playback
play();
// Start receiving audio data
receiveLoop();
}
else
{
// Start playback
play();
}
}
private:
////////////////////////////////////////////////////////////
/// /see SoundStream::OnGetData
///
////////////////////////////////////////////////////////////
bool onGetData(sf::SoundStream::Chunk& data) override
{
// 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)
{
const std::lock_guard lock(m_mutex);
m_tempBuffer.assign(m_samples.begin() + static_cast<std::vector<std::int16_t>::difference_type>(m_offset),
m_samples.end());
}
// Fill audio data to pass to the stream
data.samples = m_tempBuffer.data();
data.sampleCount = m_tempBuffer.size();
// Update the playing offset
m_offset += m_tempBuffer.size();
return true;
}
////////////////////////////////////////////////////////////
/// /see SoundStream::OnSeek
///
////////////////////////////////////////////////////////////
void onSeek(sf::Time timeOffset) override
{
m_offset = static_cast<std::size_t>(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::Status::Done)
break;
// Extract the message ID
std::uint8_t id = 0;
packet >> id;
if (id == serverAudioData)
{
// Extract audio samples from the packet, and append it to our samples buffer
const std::size_t sampleCount = (packet.getDataSize() - 1) / sizeof(std::int16_t);
// 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)
{
const std::lock_guard lock(m_mutex);
const auto* begin = static_cast<const char*>(packet.getData()) + 1;
const auto* end = begin + sampleCount * sizeof(std::int16_t);
m_samples.insert(m_samples.end(), begin, end);
}
}
else if (id == serverEndOfStream)
{
// 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;
std::recursive_mutex m_mutex;
std::vector<std::int16_t> m_samples;
std::vector<std::int16_t> 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::Status::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::Status::Stopped)
{
// Leave some CPU time for other threads
sf::sleep(sf::milliseconds(100));
}
}
|