mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-25 23:16:14 +02:00
configurable PCM format
git-svn-id: svn://elaine/murooma/trunk@193 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
parent
8903cbb0d2
commit
5938890297
3 changed files with 126 additions and 34 deletions
|
@ -29,10 +29,27 @@ namespace po = boost::program_options;
|
||||||
|
|
||||||
|
|
||||||
int deviceIdx;
|
int deviceIdx;
|
||||||
|
uint16_t sampleRate;
|
||||||
|
short channels;
|
||||||
|
uint16_t bps;
|
||||||
Stream* stream;
|
Stream* stream;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void socketRead(tcp::socket* socket, void* to, size_t bytes)
|
||||||
|
{
|
||||||
|
size_t toRead = bytes;
|
||||||
|
size_t len = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
len += socket->read_some(boost::asio::buffer((char*)to + len, toRead));
|
||||||
|
toRead = bytes - len;
|
||||||
|
}
|
||||||
|
while (toRead > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void player(const std::string& ip, int port)
|
void player(const std::string& ip, int port)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -51,17 +68,25 @@ cout << "connect\n";
|
||||||
s.connect(*iterator);
|
s.connect(*iterator);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
void* wireChunk = (void*)malloc(sizeof(WireChunk));
|
WireChunk* wireChunk = new WireChunk();
|
||||||
|
socketRead(&s, wireChunk, Chunk::getHeaderSize());
|
||||||
|
// cout << "WireChunk length: " << wireChunk->length << ", sec: " << wireChunk->tv_sec << ", usec: " << wireChunk->tv_usec << "\n";
|
||||||
|
|
||||||
|
wireChunk->payload = (char*)malloc(wireChunk->length);
|
||||||
|
socketRead(&s, wireChunk->payload, wireChunk->length);
|
||||||
|
|
||||||
|
/* void* wireChunk = (void*)malloc(sizeof(WireChunk));
|
||||||
size_t toRead = sizeof(WireChunk);
|
size_t toRead = sizeof(WireChunk);
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
len += s.read_some(boost::asio::buffer((char*)wireChunk + len, toRead));
|
len += s.read_some(boost::asio::buffer((char*)wireChunk + len, toRead));
|
||||||
toRead = sizeof(WireChunk) - len;
|
toRead = sizeof(WireChunk) - len;
|
||||||
// cout << "len: " << len << "\ttoRead: " << toRead << "\n";
|
cout << "len: " << len << "\ttoRead: " << toRead << "\n";
|
||||||
}
|
}
|
||||||
while (toRead > 0);
|
while (toRead > 0);
|
||||||
stream->addChunk(new Chunk((WireChunk*)wireChunk));
|
*/
|
||||||
|
stream->addChunk(new Chunk(sampleRate, channels, bps, wireChunk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
@ -104,13 +129,11 @@ static int paStreamCallback( const void *inputBuffer, void *outputBuffer,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PaStream* initAudio(PaError& err)
|
PaStream* initAudio(PaError& err, uint16_t sampleRate, short channels, uint16_t bps)
|
||||||
{
|
{
|
||||||
PaStreamParameters outputParameters;
|
PaStreamParameters outputParameters;
|
||||||
PaStream *paStream = NULL;
|
PaStream *paStream = NULL;
|
||||||
// PaError err;
|
// printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||||
|
|
||||||
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
|
||||||
|
|
||||||
err = Pa_Initialize();
|
err = Pa_Initialize();
|
||||||
if( err != paNoError ) goto error;
|
if( err != paNoError ) goto error;
|
||||||
|
@ -137,8 +160,16 @@ PaStream* initAudio(PaError& err)
|
||||||
fprintf(stderr,"Error: No default output device.\n");
|
fprintf(stderr,"Error: No default output device.\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
outputParameters.channelCount = CHANNELS; /* stereo output */
|
outputParameters.channelCount = channels; /* stereo output */
|
||||||
outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
|
if (bps == 16)
|
||||||
|
outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
|
||||||
|
else if (bps == 8)
|
||||||
|
outputParameters.sampleFormat = paUInt8; /* 32 bit floating point output */
|
||||||
|
else if (bps == 32)
|
||||||
|
outputParameters.sampleFormat = paInt32; /* 32 bit floating point output */
|
||||||
|
else if (bps == 24)
|
||||||
|
outputParameters.sampleFormat = paInt24; /* 32 bit floating point output */
|
||||||
|
|
||||||
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
|
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
|
||||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
std::cerr << "HighLatency: " << outputParameters.suggestedLatency << "\t LowLatency: " << Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency << "\n";
|
std::cerr << "HighLatency: " << outputParameters.suggestedLatency << "\t LowLatency: " << Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency << "\n";
|
||||||
|
@ -146,7 +177,7 @@ PaStream* initAudio(PaError& err)
|
||||||
&paStream,
|
&paStream,
|
||||||
NULL, /* no input */
|
NULL, /* no input */
|
||||||
&outputParameters,
|
&outputParameters,
|
||||||
SAMPLE_RATE,
|
sampleRate,
|
||||||
paFramesPerBufferUnspecified, //FRAMES_PER_BUFFER,
|
paFramesPerBufferUnspecified, //FRAMES_PER_BUFFER,
|
||||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||||
paStreamCallback,
|
paStreamCallback,
|
||||||
|
@ -191,6 +222,9 @@ int main (int argc, char *argv[])
|
||||||
("port,p", po::value<size_t>(&port)->default_value(98765), "port where the server listens on")
|
("port,p", po::value<size_t>(&port)->default_value(98765), "port where the server listens on")
|
||||||
("ip,i", po::value<string>(&ip)->default_value("192.168.0.2"), "server IP")
|
("ip,i", po::value<string>(&ip)->default_value("192.168.0.2"), "server IP")
|
||||||
("soundcard,s", po::value<int>(&deviceIdx)->default_value(-1), "index of the soundcard")
|
("soundcard,s", po::value<int>(&deviceIdx)->default_value(-1), "index of the soundcard")
|
||||||
|
("channels,c", po::value<short>(&channels)->default_value(2), "number of channels")
|
||||||
|
("samplerate,r", po::value<uint16_t>(&sampleRate)->default_value(48000), "sample rate")
|
||||||
|
("bps", po::value<uint16_t>(&bps)->default_value(16), "bit per sample")
|
||||||
("buffer,b", po::value<int>(&bufferMs)->default_value(300), "buffer size [ms]")
|
("buffer,b", po::value<int>(&bufferMs)->default_value(300), "buffer size [ms]")
|
||||||
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
||||||
;
|
;
|
||||||
|
@ -212,10 +246,10 @@ int main (int argc, char *argv[])
|
||||||
std::clog << kLogNotice << "daemon started" << std::endl;
|
std::clog << kLogNotice << "daemon started" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = new Stream();
|
stream = new Stream(sampleRate, channels, bps);
|
||||||
stream->setBufferLen(bufferMs);
|
stream->setBufferLen(bufferMs);
|
||||||
PaError paError;
|
PaError paError;
|
||||||
PaStream* paStream = initAudio(paError);
|
PaStream* paStream = initAudio(paError, sampleRate, channels, bps);
|
||||||
stream->setLatency(1000*Pa_GetStreamInfo(paStream)->outputLatency);
|
stream->setLatency(1000*Pa_GetStreamInfo(paStream)->outputLatency);
|
||||||
|
|
||||||
std::thread playerThread(player, ip, port);
|
std::thread playerThread(player, ip, port);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Stream::Stream() : sleep(0), median(0), shortMedian(0), lastUpdate(0), latencyMs(0)
|
Stream::Stream(size_t hz, size_t channels, size_t bps) : hz_(hz), channels_(channels), bytesPerSample_(bps/8), sleep(0), median(0), shortMedian(0), lastUpdate(0), latencyMs(0)
|
||||||
{
|
{
|
||||||
pBuffer = new DoubleBuffer<int>(1000);
|
pBuffer = new DoubleBuffer<int>(1000);
|
||||||
pShortBuffer = new DoubleBuffer<int>(200);
|
pShortBuffer = new DoubleBuffer<int>(200);
|
||||||
|
@ -25,16 +25,17 @@ void Stream::setBufferLen(size_t bufferLenMs)
|
||||||
|
|
||||||
void Stream::addChunk(Chunk* chunk)
|
void Stream::addChunk(Chunk* chunk)
|
||||||
{
|
{
|
||||||
while (chunks.size() * WIRE_CHUNK_MS > 10000)
|
// cout << "new chunk: " << chunk->getDuration() << "\n";
|
||||||
|
while (chunks.size() * chunk->getDuration() > 10000)
|
||||||
chunks.pop();
|
chunks.pop();
|
||||||
chunks.push(shared_ptr<Chunk>(chunk));
|
chunks.push(shared_ptr<Chunk>(chunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Stream::getSilentPlayerChunk(short* outputBuffer, unsigned long framesPerBuffer)
|
void Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer)
|
||||||
{
|
{
|
||||||
memset(outputBuffer, 0, sizeof(short)*framesPerBuffer * CHANNELS);
|
memset(outputBuffer, 0, framesPerBuffer * channels_ * bytesPerSample_);//CHANNELS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,25 +45,25 @@ void Stream::setLatency(size_t latency)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
time_point_ms Stream::getNextPlayerChunk(short* outputBuffer, unsigned long framesPerBuffer, int correction)
|
time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, int correction)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
chunk = chunks.pop();
|
chunk = chunks.pop();
|
||||||
|
|
||||||
time_point_ms tp = chunk->timePoint();
|
time_point_ms tp = chunk->timePoint();
|
||||||
int read = 0;
|
int read = 0;
|
||||||
int toRead = framesPerBuffer*CHANNELS + correction*PLAYER_CHUNK_MS_SIZE;
|
int toRead = framesPerBuffer*channels_ + correction*(hz_*channels_/1000);
|
||||||
short* buffer;
|
char* buffer;
|
||||||
|
|
||||||
if (correction != 0)
|
if (correction != 0)
|
||||||
{
|
{
|
||||||
int msBuffer = floor(framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE);
|
int msBuffer = floor(framesPerBuffer*2 / (hz_*channels_/1000));
|
||||||
if (abs(correction) > msBuffer / 2)
|
if (abs(correction) > msBuffer / 2)
|
||||||
correction = copysign(msBuffer / 2, correction);
|
correction = copysign(msBuffer / 2, correction);
|
||||||
buffer = (short*)malloc(toRead * sizeof(short));
|
buffer = (char*)malloc(toRead * bytesPerSample_);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
buffer = outputBuffer;
|
buffer = (char*)outputBuffer;
|
||||||
|
|
||||||
while (read < toRead)
|
while (read < toRead)
|
||||||
{
|
{
|
||||||
|
@ -73,20 +74,72 @@ time_point_ms Stream::getNextPlayerChunk(short* outputBuffer, unsigned long fram
|
||||||
|
|
||||||
if (correction != 0)
|
if (correction != 0)
|
||||||
{
|
{
|
||||||
float factor = (float)toRead / (float)(framesPerBuffer*CHANNELS);
|
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||||
|
//float factor = 0.9;
|
||||||
|
// std::cout << "correction: " << correction << ", factor: " << factor << "\n";
|
||||||
|
float idx = 0;
|
||||||
|
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||||
|
{
|
||||||
|
size_t index(floor(idx));// = (int)(ceil(n*factor));
|
||||||
|
//cout << "toRead: " << toRead << ", n: " << n << ", idx: " << index << "\n";
|
||||||
|
memcpy((char*)outputBuffer + n*chunk->frameSize_, buffer + index*chunk->frameSize_, chunk->frameSize_);
|
||||||
|
idx += factor;
|
||||||
|
// memcpy((char*)outputBuffer + n*bytesPerSample_*channels_, (char*)(chunk->wireChunk->payload) + index*bytesPerSample_*channels_, bytesPerSample_*channels_);
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
/* float factor = (float)toRead / (float)(framesPerBuffer*channels_);
|
||||||
std::cout << "correction: " << correction << ", factor: " << factor << "\n";
|
std::cout << "correction: " << correction << ", factor: " << factor << "\n";
|
||||||
for (size_t n=0; n<framesPerBuffer; ++n)
|
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||||
{
|
{
|
||||||
size_t index = (int)(floor(n*factor));
|
size_t index = n;//(int)(floor(n*factor));
|
||||||
*(outputBuffer + 2*n) = *(buffer + 2*index);
|
memcpy((char*)outputBuffer + n*bytesPerSample_*channels_, (char*)(chunk->wireChunk->payload) + index*bytesPerSample_*channels_, bytesPerSample_*channels_);
|
||||||
*(outputBuffer + 2*n+1) = *(buffer + 2*index + 1);
|
// memcpy((char*)outputBuffer + n*bytesPerSample_*channels_, (char*)(chunk->wireChunk->payload) + index*bytesPerSample_*channels_, bytesPerSample_*channels_);
|
||||||
}
|
}
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
*/ }
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, int correction)
|
||||||
|
{
|
||||||
|
correction = 0;
|
||||||
|
if (!chunk)
|
||||||
|
chunk = chunks.pop();
|
||||||
|
|
||||||
|
time_point_ms tp = chunk->timePoint();
|
||||||
|
int read = 0;
|
||||||
|
int toRead = framesPerBuffer / 1.5;
|
||||||
|
char* buffer;
|
||||||
|
//cout << "Framesize: " << chunk->frameSize_ << "\n";
|
||||||
|
buffer = (char*)malloc(toRead * chunk->frameSize_);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
read += chunk->read(buffer + read, toRead - read);
|
||||||
|
if (chunk->isEndOfChunk())
|
||||||
|
chunk = chunks.pop();
|
||||||
|
}
|
||||||
|
while (read < toRead);
|
||||||
|
|
||||||
|
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||||
|
//float factor = 0.9;
|
||||||
|
// std::cout << "correction: " << correction << ", factor: " << factor << "\n";
|
||||||
|
float idx = 0;
|
||||||
|
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||||
|
{
|
||||||
|
size_t index(floor(idx));// = (int)(ceil(n*factor));
|
||||||
|
//cout << "toRead: " << toRead << ", n: " << n << ", idx: " << index << "\n";
|
||||||
|
memcpy((char*)outputBuffer + n*chunk->frameSize_, buffer + index*chunk->frameSize_, chunk->frameSize_);
|
||||||
|
idx += factor;
|
||||||
|
// memcpy((char*)outputBuffer + n*bytesPerSample_*channels_, (char*)(chunk->wireChunk->payload) + index*bytesPerSample_*channels_, bytesPerSample_*channels_);
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void Stream::updateBuffers(int age)
|
void Stream::updateBuffers(int age)
|
||||||
{
|
{
|
||||||
|
@ -104,10 +157,11 @@ void Stream::resetBuffers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Stream::getPlayerChunk(short* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer)
|
void Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer)
|
||||||
{
|
{
|
||||||
//cout << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n";
|
//cout << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n";
|
||||||
int msBuffer = floor(framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE);
|
sleep = 0;
|
||||||
|
int msBuffer = floor(framesPerBuffer*2 / (hz_*channels_/1000));
|
||||||
|
|
||||||
int ticks = 0;
|
int ticks = 0;
|
||||||
long currentTick = getTickCount();
|
long currentTick = getTickCount();
|
||||||
|
@ -191,7 +245,7 @@ void Stream::getPlayerChunk(short* outputBuffer, double outputBufferDacTime, uns
|
||||||
}
|
}
|
||||||
else if (pMiniBuffer->full() && (abs(pMiniBuffer->median()) > 50))
|
else if (pMiniBuffer->full() && (abs(pMiniBuffer->median()) > 50))
|
||||||
{
|
{
|
||||||
cout << "pMiniBuffer->full() && (abs(pMiniBuffer->mean()) > 50): " << pMiniBuffer->median() << "\n";
|
// cout << "pMiniBuffer->full() && (abs(pMiniBuffer->mean()) > 50): " << pMiniBuffer->median() << "\n";
|
||||||
sleep = pMiniBuffer->mean();
|
sleep = pMiniBuffer->mean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,22 @@
|
||||||
class Stream
|
class Stream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Stream();
|
Stream(size_t hz, size_t channels, size_t bps);
|
||||||
void addChunk(Chunk* chunk);
|
void addChunk(Chunk* chunk);
|
||||||
void getPlayerChunk(short* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer);
|
void getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer);
|
||||||
void setBufferLen(size_t bufferLenMs);
|
void setBufferLen(size_t bufferLenMs);
|
||||||
void setLatency(size_t latency);
|
void setLatency(size_t latency);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
time_point_ms getNextPlayerChunk(short* outputBuffer, unsigned long framesPerBuffer, int correction = 0);
|
time_point_ms getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, int correction = 0);
|
||||||
void getSilentPlayerChunk(short* outputBuffer, unsigned long framesPerBuffer);
|
void getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
||||||
void updateBuffers(int age);
|
void updateBuffers(int age);
|
||||||
void resetBuffers();
|
void resetBuffers();
|
||||||
|
|
||||||
|
size_t hz_;
|
||||||
|
size_t channels_;
|
||||||
|
size_t bytesPerSample_;
|
||||||
|
|
||||||
long lastTick;
|
long lastTick;
|
||||||
int sleep;
|
int sleep;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue