mirror of
https://github.com/badaix/snapcast.git
synced 2025-06-05 12:21:43 +02:00
samplerate
git-svn-id: svn://elaine/murooma/trunk@307 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
parent
b715396832
commit
42ef09e726
5 changed files with 103 additions and 42 deletions
|
@ -121,14 +121,15 @@ void Player::stop()
|
||||||
void Player::worker()
|
void Player::worker()
|
||||||
{
|
{
|
||||||
unsigned int pcm;
|
unsigned int pcm;
|
||||||
snd_pcm_sframes_t avail;
|
snd_pcm_sframes_t framesAvail;
|
||||||
snd_pcm_sframes_t delay;
|
snd_pcm_sframes_t framesDelay;
|
||||||
active_ = true;
|
active_ = true;
|
||||||
while (active_)
|
while (active_)
|
||||||
{
|
{
|
||||||
snd_pcm_avail_delay(pcm_handle, &avail, &delay);
|
snd_pcm_avail_delay(pcm_handle, &framesAvail, &framesDelay);
|
||||||
|
|
||||||
if (stream_->getPlayerChunk(buff, chronos::usec((chronos::usec::rep)(1000 * (double)delay / stream_->format.msRate())), frames))
|
chronos::usec delay((chronos::usec::rep)(1000 * (double)framesDelay / stream_->format.msRate()));
|
||||||
|
if (stream_->getPlayerChunk(buff, delay, frames))
|
||||||
{
|
{
|
||||||
if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE)
|
if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE)
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,6 +67,10 @@ public:
|
||||||
bufferSize = size;
|
bufferSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::deque<T>& getBuffer() const
|
||||||
|
{
|
||||||
|
return &buffer;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t bufferSize;
|
size_t bufferSize;
|
||||||
|
|
|
@ -8,16 +8,35 @@ using namespace std;
|
||||||
using namespace chronos;
|
using namespace chronos;
|
||||||
|
|
||||||
|
|
||||||
Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(sampleFormat), sleep(0), median(0), shortMedian(0), lastUpdate(0)
|
Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(sampleFormat), sleep(0), median(0), shortMedian(0), lastUpdate(0), playedFrames(0)
|
||||||
{
|
{
|
||||||
buffer.setSize(500);
|
buffer.setSize(500);
|
||||||
shortBuffer.setSize(100);
|
shortBuffer.setSize(100);
|
||||||
miniBuffer.setSize(20);
|
miniBuffer.setSize(20);
|
||||||
// cardBuffer.setSize(50);
|
// cardBuffer.setSize(50);
|
||||||
bufferMs = msec(500);
|
bufferMs = msec(500);
|
||||||
|
|
||||||
|
playedSamples = 0;
|
||||||
|
/*
|
||||||
|
48000 x
|
||||||
|
------- = -----
|
||||||
|
47999,2 x - 1
|
||||||
|
|
||||||
|
x = 1,000016667 / (1,000016667 - 1)
|
||||||
|
*/
|
||||||
|
setRealSampleRate(format.rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Stream::setRealSampleRate(double sampleRate)
|
||||||
|
{
|
||||||
|
if (sampleRate == format.rate)
|
||||||
|
correctAfterXFrames = 0;
|
||||||
|
else
|
||||||
|
correctAfterXFrames = round((format.rate / sampleRate) / (format.rate / sampleRate - 1.));
|
||||||
|
// cout << "Correct after X: " << correctAfterXFrames << " (Real rate: " << sampleRate << ", rate: " << format.rate << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Stream::setBufferLen(size_t bufferLenMs)
|
void Stream::setBufferLen(size_t bufferLenMs)
|
||||||
{
|
{
|
||||||
|
@ -91,38 +110,36 @@ time_point_hrc Stream::seek(long ms)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, const chronos::usec& correction)
|
time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer)
|
||||||
{
|
{
|
||||||
if (!chunk && !chunks.try_pop(chunk, timeout))
|
if (!chunk && !chunks.try_pop(chunk, timeout))
|
||||||
throw 0;
|
throw 0;
|
||||||
|
|
||||||
//cout << "duration: " << chunk->duration<chronos::msec>().count() << ", " << chunk->duration<chronos::usec>().count() << ", " << chunk->duration<chronos::nsec>().count() << "\n";
|
|
||||||
time_point_hrc tp = chunk->start();
|
time_point_hrc tp = chunk->start();
|
||||||
int read = 0;
|
char* buffer = (char*)outputBuffer;
|
||||||
int toRead = framesPerBuffer + correction.count()*format.usRate();
|
unsigned long read = 0;
|
||||||
char* buffer;
|
while (read < framesPerBuffer)
|
||||||
|
|
||||||
if (correction.count() != 0)
|
|
||||||
{
|
{
|
||||||
// chronos::usec usBuffer = (chronos::usec::rep)(framesPerBuffer / format.usRate());
|
read += chunk->readFrames(buffer + read*format.frameSize, framesPerBuffer - read);
|
||||||
// if (abs(correction) > usBuffer / 2)
|
|
||||||
// correction = copysign(usBuffer / 2, correction);
|
|
||||||
buffer = (char*)malloc(toRead * format.frameSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
buffer = (char*)outputBuffer;
|
|
||||||
|
|
||||||
while (read < toRead)
|
|
||||||
{
|
|
||||||
read += chunk->readFrames(buffer + read*format.frameSize, toRead - read);
|
|
||||||
if (chunk->isEndOfChunk() && !chunks.try_pop(chunk, timeout))
|
if (chunk->isEndOfChunk() && !chunks.try_pop(chunk, timeout))
|
||||||
throw 0;
|
throw 0;
|
||||||
}
|
}
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, long framesCorrection)
|
||||||
|
{
|
||||||
|
if (framesCorrection == 0)
|
||||||
|
return getNextPlayerChunk(outputBuffer, timeout, framesPerBuffer);
|
||||||
|
|
||||||
|
long toRead = framesPerBuffer + framesCorrection;
|
||||||
|
char* buffer = (char*)malloc(toRead * format.frameSize);
|
||||||
|
time_point_hrc tp = getNextPlayerChunk(buffer, timeout, toRead);
|
||||||
|
|
||||||
if (correction.count() != 0)
|
|
||||||
{
|
|
||||||
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||||
std::cout << "correction: " << correction.count() << ", factor: " << factor << "\n";
|
if (abs(framesCorrection) > 1)
|
||||||
|
std::cout << "correction: " << framesCorrection << ", factor: " << factor << "\n";
|
||||||
float idx = 0;
|
float idx = 0;
|
||||||
for (size_t n=0; n<framesPerBuffer; ++n)
|
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +148,6 @@ time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::use
|
||||||
idx += factor;
|
idx += factor;
|
||||||
}
|
}
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
@ -157,12 +173,21 @@ void Stream::resetBuffers()
|
||||||
|
|
||||||
bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBufferDacTime, unsigned long framesPerBuffer)
|
bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBufferDacTime, unsigned long framesPerBuffer)
|
||||||
{
|
{
|
||||||
|
/*if (playedSamples == 0)
|
||||||
|
playedSamplesTime = chronos::hrc::now() + outputBufferDacTime;
|
||||||
|
playedSamples += framesPerBuffer;
|
||||||
|
chronos::msec since = std::chrono::duration_cast<msec>(chronos::hrc::now() + outputBufferDacTime - playedSamplesTime);
|
||||||
|
if (since.count() > 0)
|
||||||
|
cout << (double)playedSamples / (double)since.count() << "\n";
|
||||||
|
*/
|
||||||
if (outputBufferDacTime > bufferMs)
|
if (outputBufferDacTime > bufferMs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!chunk && !chunks.try_pop(chunk, outputBufferDacTime))
|
if (!chunk && !chunks.try_pop(chunk, outputBufferDacTime))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
playedFrames += framesPerBuffer;
|
||||||
|
|
||||||
chronos::usec age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
chronos::usec age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
||||||
if ((sleep.count() == 0) && (chronos::abs(age) > chronos::msec(200)))
|
if ((sleep.count() == 0) && (chronos::abs(age) > chronos::msec(200)))
|
||||||
{
|
{
|
||||||
|
@ -196,7 +221,12 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe
|
||||||
{
|
{
|
||||||
cout << "sleep > chunk->getDuration(): " << sleep.count() << " > " << chunk->duration<chronos::msec>().count() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n";
|
cout << "sleep > chunk->getDuration(): " << sleep.count() << " > " << chunk->duration<chronos::msec>().count() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n";
|
||||||
if (!chunks.try_pop(chunk, outputBufferDacTime))
|
if (!chunks.try_pop(chunk, outputBufferDacTime))
|
||||||
|
{
|
||||||
|
cout << "no chunks available\n";
|
||||||
|
chunk = NULL;
|
||||||
|
sleep = chronos::usec(0);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sleep = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
sleep = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
||||||
}
|
}
|
||||||
|
@ -221,15 +251,34 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, correction) - bufferMs + outputBufferDacTime);
|
long framesCorrection = correction.count()*format.usRate();
|
||||||
|
if ((correctAfterXFrames != 0) && (playedFrames >= (unsigned long)abs(correctAfterXFrames)))
|
||||||
|
{
|
||||||
|
framesCorrection += (correctAfterXFrames > 0)?1:-1;
|
||||||
|
playedFrames -= abs(correctAfterXFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, framesCorrection) - bufferMs + outputBufferDacTime);
|
||||||
|
|
||||||
|
// setRealSampleRate(format.rate);
|
||||||
if (sleep.count() == 0)
|
if (sleep.count() == 0)
|
||||||
{
|
{
|
||||||
if (buffer.full() && (chronos::usec(abs(median)) > chronos::msec(1)))
|
if (buffer.full())
|
||||||
|
{
|
||||||
|
if (chronos::usec(abs(median)) > chronos::msec(1))
|
||||||
{
|
{
|
||||||
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
||||||
sleep = chronos::usec(median);
|
sleep = chronos::usec(shortMedian);
|
||||||
}
|
}
|
||||||
|
/* else if (chronos::usec(median) > chronos::usec(300))
|
||||||
|
{
|
||||||
|
setRealSampleRate(format.rate - format.rate / 1000);
|
||||||
|
}
|
||||||
|
else if (chronos::usec(median) < -chronos::usec(300))
|
||||||
|
{
|
||||||
|
setRealSampleRate(format.rate + format.rate / 1000);
|
||||||
|
}
|
||||||
|
*/ }
|
||||||
else if (shortBuffer.full() && (chronos::usec(abs(shortMedian)) > chronos::msec(5)))
|
else if (shortBuffer.full() && (chronos::usec(abs(shortMedian)) > chronos::msec(5)))
|
||||||
{
|
{
|
||||||
cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n";
|
cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n";
|
||||||
|
|
|
@ -27,18 +27,23 @@ public:
|
||||||
const SampleFormat& format;
|
const SampleFormat& format;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, const chronos::usec& correction = chronos::usec(0));
|
chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer);
|
||||||
|
chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, long framesCorrection);
|
||||||
chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
||||||
chronos::time_point_hrc seek(long ms);
|
chronos::time_point_hrc seek(long ms);
|
||||||
// time_point_ms seekTo(const time_point_ms& to);
|
// time_point_ms seekTo(const time_point_ms& to);
|
||||||
void updateBuffers(int age);
|
void updateBuffers(int age);
|
||||||
void resetBuffers();
|
void resetBuffers();
|
||||||
|
void setRealSampleRate(double sampleRate);
|
||||||
|
|
||||||
SampleFormat format_;
|
SampleFormat format_;
|
||||||
|
|
||||||
long lastTick;
|
long lastTick;
|
||||||
chronos::usec sleep;
|
chronos::usec sleep;
|
||||||
|
|
||||||
|
unsigned long playedSamples;
|
||||||
|
chronos::time_point_hrc playedSamplesTime;
|
||||||
|
|
||||||
Queue<std::shared_ptr<PcmChunk>> chunks;
|
Queue<std::shared_ptr<PcmChunk>> chunks;
|
||||||
// DoubleBuffer<chronos::usec::rep> cardBuffer;
|
// DoubleBuffer<chronos::usec::rep> cardBuffer;
|
||||||
DoubleBuffer<chronos::usec::rep> miniBuffer;
|
DoubleBuffer<chronos::usec::rep> miniBuffer;
|
||||||
|
@ -50,6 +55,8 @@ private:
|
||||||
int shortMedian;
|
int shortMedian;
|
||||||
time_t lastUpdate;
|
time_t lastUpdate;
|
||||||
chronos::msec bufferMs;
|
chronos::msec bufferMs;
|
||||||
|
unsigned long playedFrames;
|
||||||
|
long correctAfterXFrames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
VERSION = 0.01
|
VERSION = 0.01
|
||||||
CC = /usr/bin/g++
|
CC = /usr/bin/g++
|
||||||
CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -D_REENTRANT -DVERSION=\"$(VERSION)\" -I..
|
CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -D_REENTRANT -DVERSION=\"$(VERSION)\" -I..
|
||||||
LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg
|
LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg -lFLAC
|
||||||
|
|
||||||
OBJ = snapServer.o controlServer.o pcmEncoder.o oggEncoder.o serverSession.o ../common/log.o ../message/pcmChunk.o ../message/sampleFormat.o
|
OBJ = snapServer.o controlServer.o pcmEncoder.o oggEncoder.o serverSession.o ../common/log.o ../message/pcmChunk.o ../message/sampleFormat.o
|
||||||
BIN = snapserver
|
BIN = snapserver
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue