M2-PT-DRP/source/Client.cpp

143 lines
4 KiB
C++

#include "Client.hpp"
#include <cstring>
#include <cstdint>
#include <iostream>
#include <list>
#include <map>
#include <netdb.h>
#include <queue>
#include <stdexcept>
#include <vector>
#include <sys/socket.h>
#include "packets/AudioPacket.hpp"
struct AudioPacketsComparator {
bool operator() (const AudioPacket &a, const AudioPacket &b) const {
return a.timePlay > b.timePlay;
}
};
Client::Client(const int channels, const double rate) {
this->channels = channels;
// TODO(Faraphel): make the sampleFormat an argument.
// open a PortAudio stream
if (Pa_OpenDefaultStream(
&this->stream,
0,
channels,
paInt16,
rate,
paFramesPerBufferUnspecified,
nullptr,
nullptr
) != paNoError)
throw std::runtime_error("Could not open PortAudio stream.");
}
Client::~Client() {
// close the audio stream
Pa_StopStream(this->stream);
Pa_CloseStream(this->stream);
}
void Client::loop() const {
int error;
// start the PortAudio stream
if (Pa_StartStream(stream) != paNoError)
throw std::runtime_error("Could not start the PortAudio stream.");
// create the socket
const int clientSocket = socket(
AF_INET6,
SOCK_DGRAM,
0
);
if (clientSocket < 0)
throw std::runtime_error("Could not create the socket.");
// get the broadcast address
addrinfo serverHints = {};
serverHints.ai_family = AF_INET6;
serverHints.ai_socktype = SOCK_DGRAM;
serverHints.ai_protocol = IPPROTO_UDP;
// TODO(Faraphel): port as argument
addrinfo *serverInfo;
if((error = getaddrinfo(
nullptr, // hostname
"5650", // our port
&serverHints,
&serverInfo
)) != 0)
throw std::runtime_error("Could not get the address.\n" + std::string(gai_strerror(error)));
// bind to this address
if (bind(
clientSocket,
serverInfo->ai_addr,
serverInfo->ai_addrlen
) < 0)
throw std::runtime_error("Could not bind to the address.");
// free the server address
freeaddrinfo(serverInfo);
sockaddr_storage serverAddress {};
socklen_t serverAddressLength;
AudioPacket audioPacketReceived;
std::priority_queue<AudioPacket, std::vector<AudioPacket>, AudioPacketsComparator> audioQueue;
// receive new audio data
while (true) {
const ssize_t size = recvfrom(
clientSocket,
&audioPacketReceived,
sizeof(audioPacketReceived),
0,
reinterpret_cast<sockaddr *>(&serverAddress),
&serverAddressLength
);
if (size == -1) {
std::cout << "Could not receive from the socket : " << gai_strerror(errno) << std::endl;
continue;
}
std::cout << "[Client] received: " << size << " bytes" << std::endl;
audioQueue.push(audioPacketReceived);
// TODO(Faraphel): the receiver and the player shall be in two different thread !
// the music can't play nicely if not done.
while (!audioQueue.empty()) {
const auto& audioPacket = audioQueue.top();
if (audioPacket.timePlay <= std::chrono::high_resolution_clock::now()) {
std::cout << "playing: " << audioPacket.timePlay << std::endl;
// TODO(Faraphel): check for the error ?
// TODO(Faraphel) / 2 => / encoding size
// TODO(Faraphel): the number of frames could be improved
// TODO(Faraphel): more comments
// play the audio buffer
error = Pa_WriteStream(
this->stream,
audioPacket.content.data(),
audioPacket.contentSize / 2 / this->channels
);
if (error != paNoError)
std::cerr << "Could not write to the stream : " << Pa_GetErrorText(error) << std::endl;
audioQueue.pop();
}
else
break;
}
}
}