splitted the program into a server and a client, communicating through sockets.
This commit is contained in:
parent
270017b1d3
commit
145d4b8c2c
10 changed files with 304 additions and 148 deletions
|
@ -12,9 +12,12 @@ pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0)
|
|||
|
||||
|
||||
add_executable(M2-PT-DRP
|
||||
source/connection.cpp
|
||||
source/stream.cpp
|
||||
source/stream.hpp
|
||||
source/Client.cpp
|
||||
source/Client.hpp
|
||||
source/Server.cpp
|
||||
source/Server.hpp
|
||||
source/main.cpp
|
||||
source/main.hpp
|
||||
)
|
||||
target_include_directories(M2-PT-DRP PRIVATE
|
||||
${MPG123_INCLUDE_DIRS}
|
||||
|
|
105
source/Client.cpp
Normal file
105
source/Client.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "Client.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <netdb.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
||||
Client::Client(const int channels, const double rate) {
|
||||
this->channels = channels;
|
||||
|
||||
// TODO(Faraphel): make the sampleFormat and the framesPerBuffer arguments.
|
||||
// open a PortAudio stream
|
||||
if (Pa_OpenDefaultStream(
|
||||
&this->stream,
|
||||
0,
|
||||
channels,
|
||||
paInt16,
|
||||
rate,
|
||||
512,
|
||||
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() {
|
||||
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, // any source
|
||||
"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;
|
||||
std::vector<std::uint8_t> buffer(4096);
|
||||
|
||||
// receive new audio data
|
||||
while (ssize_t size = recvfrom(
|
||||
clientSocket,
|
||||
buffer.data(),
|
||||
buffer.size(),
|
||||
0,
|
||||
reinterpret_cast<sockaddr *>(&serverAddress),
|
||||
&serverAddressLength
|
||||
)) {
|
||||
std::cout << "[Client] received: " << size << " bytes" << std::endl;
|
||||
|
||||
// TODO(Faraphel): check for the error ?
|
||||
error = Pa_WriteStream(
|
||||
this->stream,
|
||||
buffer.data(),
|
||||
size / 2 / this->channels
|
||||
);
|
||||
|
||||
// play the audio buffer
|
||||
// TODO(Faraphel): the number of frames could be improved
|
||||
}
|
||||
}
|
17
source/Client.hpp
Normal file
17
source/Client.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
class Client {
|
||||
public:
|
||||
explicit Client(int channels, double rate);
|
||||
~Client();
|
||||
|
||||
void loop();
|
||||
|
||||
private:
|
||||
PaStream* stream = nullptr;
|
||||
int channels;
|
||||
};
|
118
source/Server.cpp
Normal file
118
source/Server.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "Server.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <mpg123.h>
|
||||
#include <netdb.h>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <sys/socket.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
Server::Server() {
|
||||
// create a new mpg123 handle
|
||||
int error;
|
||||
this->mpgHandle = mpg123_new(nullptr, &error);
|
||||
|
||||
// open the mp3 file
|
||||
if (mpg123_open(
|
||||
this->mpgHandle,
|
||||
"./assets/Caravan Palace - Wonderland.mp3"
|
||||
))
|
||||
throw std::runtime_error("Could not open file.");
|
||||
|
||||
// get the format of the file
|
||||
if (mpg123_getformat(
|
||||
this->mpgHandle,
|
||||
&this->rate,
|
||||
&this->channels,
|
||||
&this->encoding
|
||||
) != MPG123_OK)
|
||||
throw std::runtime_error("Could not get the format of the file.");
|
||||
}
|
||||
|
||||
Server::~Server() {
|
||||
// delete the mpg123 handle
|
||||
mpg123_close(this->mpgHandle);
|
||||
mpg123_delete(this->mpgHandle);
|
||||
}
|
||||
|
||||
|
||||
void Server::loop() {
|
||||
int error;
|
||||
|
||||
// create the socket
|
||||
const int serverSocket = socket(
|
||||
AF_INET6,
|
||||
SOCK_DGRAM,
|
||||
0
|
||||
);
|
||||
if (serverSocket < 0)
|
||||
throw std::runtime_error("Could not create the socket.");
|
||||
|
||||
// get the broadcast address
|
||||
addrinfo clientHints = {};
|
||||
clientHints.ai_family = AF_INET6;
|
||||
clientHints.ai_socktype = SOCK_DGRAM;
|
||||
clientHints.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
// TODO(Faraphel): ip / port as argument
|
||||
addrinfo *clientInfo;
|
||||
if((error = getaddrinfo(
|
||||
"localhost",
|
||||
// "ff02::1", // IPv6 local broadcast
|
||||
"5650", // our port
|
||||
&clientHints,
|
||||
&clientInfo
|
||||
)) != 0)
|
||||
throw std::runtime_error("Could not get the address.\n" + std::string(gai_strerror(error)));
|
||||
|
||||
|
||||
// read the file
|
||||
std::vector<std::uint8_t> buffer(4096);
|
||||
std::size_t done;
|
||||
|
||||
while (mpg123_read(
|
||||
this->mpgHandle,
|
||||
buffer.data(),
|
||||
buffer.size(),
|
||||
&done) == MPG123_OK
|
||||
) {
|
||||
// send the content of the file
|
||||
sendto(
|
||||
serverSocket,
|
||||
buffer.data(),
|
||||
buffer.size(),
|
||||
0,
|
||||
clientInfo->ai_addr,
|
||||
clientInfo->ai_addrlen
|
||||
);
|
||||
|
||||
std::cout << "[Server] sent : " << done << " bytes" << std::endl;
|
||||
|
||||
// wait 10ms to simulate lag
|
||||
// TODO(Faraphel): should be extended to simulate live music streaming
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<uint64_t>(
|
||||
(1 / static_cast<double>(this->rate * this->channels * mpg123_encsize(this->encoding))) *
|
||||
1000 *
|
||||
static_cast<double>(done)
|
||||
)));
|
||||
}
|
||||
|
||||
// free the server address
|
||||
freeaddrinfo(clientInfo);
|
||||
}
|
||||
|
||||
|
||||
long Server::getRate() const {
|
||||
return this->rate;
|
||||
}
|
||||
|
||||
int Server::getChannels() const {
|
||||
return this->channels;
|
||||
}
|
||||
|
||||
int Server::getEncoding() const {
|
||||
return this->encoding;
|
||||
}
|
21
source/Server.hpp
Normal file
21
source/Server.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include <mpg123.h>
|
||||
|
||||
|
||||
class Server {
|
||||
public:
|
||||
explicit Server();
|
||||
~Server();
|
||||
void loop();
|
||||
|
||||
long getRate() const;
|
||||
int getChannels() const;
|
||||
int getEncoding() const;
|
||||
|
||||
private:
|
||||
mpg123_handle* mpgHandle;
|
||||
|
||||
long rate;
|
||||
int channels;
|
||||
int encoding;
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
#include "connection.hpp"
|
||||
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
/*
|
||||
int main(int argc, char* argv[]) {
|
||||
// create an IPv6 socket based on datagram (UDP)
|
||||
const int serverSocket = socket(
|
||||
AF_INET6,
|
||||
SOCK_DGRAM,
|
||||
0
|
||||
);
|
||||
|
||||
// get the broadcast address
|
||||
addrinfo broadcastAddressConfig = {};
|
||||
broadcastAddressConfig.ai_family = AF_INET6;
|
||||
broadcastAddressConfig.ai_socktype = SOCK_DGRAM;
|
||||
broadcastAddressConfig.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
addrinfo *broadcastAddress;
|
||||
if(!getaddrinfo(
|
||||
"ff02::1", // multicast address
|
||||
"5650", // our port
|
||||
&broadcastAddressConfig,
|
||||
&broadcastAddress
|
||||
))
|
||||
throw std::runtime_error("Could not get the address.");
|
||||
|
||||
const std::string message = "Salut !";
|
||||
|
||||
// broadcast the message
|
||||
if (sendto(
|
||||
serverSocket,
|
||||
message.c_str(),
|
||||
message.size(),
|
||||
0,
|
||||
broadcastAddress->ai_addr,
|
||||
broadcastAddress->ai_addrlen
|
||||
) == -1)
|
||||
throw std::runtime_error("Could not broadcast the message.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
37
source/main.cpp
Normal file
37
source/main.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "main.hpp"
|
||||
|
||||
#include <mpg123.h>
|
||||
#include <portaudio.h>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#include "Client.hpp"
|
||||
#include "Server.hpp"
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// initialize the mpg123 library
|
||||
if (mpg123_init() != MPG123_OK)
|
||||
throw std::runtime_error("Error while initializing mpg123.");
|
||||
|
||||
// initialize the PortAudio library
|
||||
if (Pa_Initialize() != paNoError)
|
||||
throw std::runtime_error("Could not initialize PortAudio.");
|
||||
|
||||
// start the client and server
|
||||
Server server;
|
||||
Client client (
|
||||
server.getChannels(),
|
||||
static_cast<double>(server.getRate())
|
||||
);
|
||||
|
||||
std::thread serverThread(&Server::loop, server);
|
||||
std::thread clientThread(&Client::loop, client);
|
||||
|
||||
serverThread.join();
|
||||
clientThread.join();
|
||||
|
||||
// terminate the libraries
|
||||
Pa_Terminate();
|
||||
mpg123_exit();
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
#include "stream.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <mpg123.h>
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// initialize the mpg123 library
|
||||
if (mpg123_init() != MPG123_OK)
|
||||
throw std::runtime_error("Error while initializing mpg123.");
|
||||
|
||||
// create a new mpg123 handle
|
||||
int error;
|
||||
mpg123_handle* mpgHandle = mpg123_new(nullptr, &error);
|
||||
|
||||
// open the mp3 file
|
||||
if (mpg123_open(mpgHandle, "./assets/Caravan Palace - Wonderland.mp3"))
|
||||
throw std::runtime_error("Could not open file.");
|
||||
|
||||
// get the format of the file
|
||||
long rate;
|
||||
int channels;
|
||||
int encoding;
|
||||
mpg123_getformat(mpgHandle, &rate, &channels, &encoding);
|
||||
printf("rate: %ld, channels: %d, encoding: %d\n", rate, channels, encoding);
|
||||
|
||||
// ---
|
||||
|
||||
// initialize the PortAudio library
|
||||
if (Pa_Initialize() != paNoError)
|
||||
throw std::runtime_error("Could not initialize PortAudio.");
|
||||
|
||||
// open the PortAudio stream
|
||||
PaStream* stream;
|
||||
if (Pa_OpenDefaultStream(
|
||||
&stream,
|
||||
0,
|
||||
channels,
|
||||
paInt16,
|
||||
static_cast<double>(rate),
|
||||
512,
|
||||
nullptr,
|
||||
nullptr
|
||||
) != paNoError)
|
||||
throw std::runtime_error("Could not open PortAudio stream.");
|
||||
|
||||
// start the PortAudio stream
|
||||
if (Pa_StartStream(stream) != paNoError)
|
||||
throw std::runtime_error("Could not start the PortAudio stream.");
|
||||
|
||||
// ---
|
||||
|
||||
// read the file
|
||||
std::vector<std::uint8_t> buffer(4096);
|
||||
std::size_t done;
|
||||
std::size_t i = 0;
|
||||
|
||||
while ((error = mpg123_read(mpgHandle, buffer.data(), buffer.size(), &done)) == MPG123_OK) {
|
||||
// write the audio data to the PortAudio stream
|
||||
if ((error = Pa_WriteStream(stream, buffer.data(), done / 2 / channels)) != paNoError)
|
||||
throw std::runtime_error(
|
||||
"Could not write audio data in the PortAudio stream.\n" +
|
||||
std::string(Pa_GetErrorText(error))
|
||||
);
|
||||
|
||||
printf("write (%04lu)\n", i++);
|
||||
}
|
||||
|
||||
// display the error message
|
||||
std::printf(mpg123_plain_strerror(error));
|
||||
|
||||
// Stop the audio stream
|
||||
Pa_StopStream(stream);
|
||||
Pa_CloseStream(stream);
|
||||
|
||||
// delete the mpg123 handle
|
||||
mpg123_close(mpgHandle);
|
||||
mpg123_delete(mpgHandle);
|
||||
|
||||
// free the libraries
|
||||
Pa_Terminate();
|
||||
mpg123_exit();
|
||||
|
||||
return error;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
#pragma once
|
Loading…
Reference in a new issue