About the last question that was on my mind. Any suggestions how to start with server sending the voip to all client when one speaks?
Once you have a server that accepts multiple clients, what's the problem with sending the data to all of them? Where are you stuck?
Good news! I found a bug in my code and now voice is working perfectly with multiple clients! I'm sorry if I sound stupid. My Speciality is with Unreal Engine and I am still somewhat unfamiliar with networking this SFML provides. So where I am stuck is, I'm not really sure what I am supposed to do all together. I could maybe use Unreal Engines networking properties to multicast the Received Packet to clients and then put it in buffer and try to play it, but I am pretty sure, that it would be much better to do here in SFML and leave Unreal out of it as much as possible. I really trying to learn at the same time, so bare with me here
Here is the working code that is little cleaned up and the faulty break;s have been removed.
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)
{
// Listen to the given port for incoming connections
if (m_listener.listen(port) != sf::Socket::Done)
return;
UE_LOG(LogTemp, Warning, TEXT("Server Started at port %i"), port);
selector.add(m_listener);
// Start playback
play();
// Start receiving audio data
receiveLoop();
}
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 (true)
{
// Make the selector wait for data on any socket
if (selector.wait())
{
// Test the listener
if (selector.isReady(m_listener))
{
// The listener is ready: there is a pending connection
sf::TcpSocket* client = new sf::TcpSocket;
if (m_listener.accept(*client) == sf::Socket::Done)
{
// Add the new client to the clients list
clients.push_back(client);
// Add the new client to the selector so that we will
// be notified when he sends something
selector.add(*client);
UE_LOG(LogTemp, Warning, TEXT("New Client Joined to chat"));
}
else
{
// Error, we won't get a new connection, delete the socket
delete client;
}
}
else
{
// The listener socket is not ready, test all other sockets (the clients)
for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
sf::TcpSocket& client = **it;
if (selector.isReady(client))
{
// The client has sent some data, we can receive it
sf::Packet packet;
if (client.receive(packet) == sf::Socket::Done)
{
// Get waiting audio data from the network
//sf::Packet packet;
//if (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;
UE_LOG(LogTemp, Warning, TEXT("Audio Stream successfully finished"));
}
else
{
// Something's wrong...
UE_LOG(LogTemp, Warning, TEXT("Invalid packet received..."));
}
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
sf::TcpListener m_listener;
sf::TcpSocket m_client;
// Create a list to store the future clients
std::list<sf::TcpSocket*> clients;
// Create a selector
sf::SocketSelector selector;
sf::Mutex m_mutex;
std::vector<sf::Int16> m_samples;
std::vector<sf::Int16> m_tempBuffer;
std::size_t m_offset;
bool m_hasFinished;
};
edit: I think sending the packet here to other clients is the key. I was wondering to use something like this after Server is sure it received the voice package:
for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
sf::TcpSocket& client2 = **it;
// Don't send same voice to speaker.
if (client2.getLocalPort() != client.getLocalPort())
{
UE_LOG(LogTemp, Warning, TEXT("Sending audio to one of the clients"));
client2.send(packet);
}
}
But if I use that, i'm not still sure how the clients can receive the packet. Am I in the right tracks here? Any help about receiving the packet on the client side? Thanks.
Above code would go inside this IF-sentence
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);