mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-29 17:06:18 +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()
|
||||
{
|
||||
unsigned int pcm;
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_sframes_t delay;
|
||||
snd_pcm_sframes_t framesAvail;
|
||||
snd_pcm_sframes_t framesDelay;
|
||||
active_ = true;
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -67,6 +67,10 @@ public:
|
|||
bufferSize = size;
|
||||
}
|
||||
|
||||
const std::deque<T>& getBuffer() const
|
||||
{
|
||||
return &buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t bufferSize;
|
||||
|
|
|
@ -8,16 +8,35 @@ using namespace std;
|
|||
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);
|
||||
shortBuffer.setSize(100);
|
||||
miniBuffer.setSize(20);
|
||||
// cardBuffer.setSize(50);
|
||||
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)
|
||||
{
|
||||
|
@ -91,47 +110,44 @@ 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))
|
||||
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();
|
||||
int read = 0;
|
||||
int toRead = framesPerBuffer + correction.count()*format.usRate();
|
||||
char* buffer;
|
||||
|
||||
if (correction.count() != 0)
|
||||
char* buffer = (char*)outputBuffer;
|
||||
unsigned long read = 0;
|
||||
while (read < framesPerBuffer)
|
||||
{
|
||||
// chronos::usec usBuffer = (chronos::usec::rep)(framesPerBuffer / format.usRate());
|
||||
// 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);
|
||||
read += chunk->readFrames(buffer + read*format.frameSize, framesPerBuffer - read);
|
||||
if (chunk->isEndOfChunk() && !chunks.try_pop(chunk, timeout))
|
||||
throw 0;
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
if (correction.count() != 0)
|
||||
|
||||
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);
|
||||
|
||||
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||
if (abs(framesCorrection) > 1)
|
||||
std::cout << "correction: " << framesCorrection << ", factor: " << factor << "\n";
|
||||
float idx = 0;
|
||||
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||
{
|
||||
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||
std::cout << "correction: " << correction.count() << ", factor: " << factor << "\n";
|
||||
float idx = 0;
|
||||
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||
{
|
||||
size_t index(floor(idx));// = (int)(ceil(n*factor));
|
||||
memcpy((char*)outputBuffer + n*format.frameSize, buffer + index*format.frameSize, format.frameSize);
|
||||
idx += factor;
|
||||
}
|
||||
free(buffer);
|
||||
size_t index(floor(idx));// = (int)(ceil(n*factor));
|
||||
memcpy((char*)outputBuffer + n*format.frameSize, buffer + index*format.frameSize, format.frameSize);
|
||||
idx += factor;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
@ -157,12 +173,21 @@ void Stream::resetBuffers()
|
|||
|
||||
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)
|
||||
return false;
|
||||
|
||||
if (!chunk && !chunks.try_pop(chunk, outputBufferDacTime))
|
||||
return false;
|
||||
|
||||
playedFrames += framesPerBuffer;
|
||||
|
||||
chronos::usec age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
||||
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";
|
||||
if (!chunks.try_pop(chunk, outputBufferDacTime))
|
||||
{
|
||||
cout << "no chunks available\n";
|
||||
chunk = NULL;
|
||||
sleep = chronos::usec(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (buffer.full() && (chronos::usec(abs(median)) > chronos::msec(1)))
|
||||
if (buffer.full())
|
||||
{
|
||||
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
||||
sleep = chronos::usec(median);
|
||||
}
|
||||
if (chronos::usec(abs(median)) > chronos::msec(1))
|
||||
{
|
||||
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
||||
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)))
|
||||
{
|
||||
cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n";
|
||||
|
|
|
@ -27,18 +27,23 @@ public:
|
|||
const SampleFormat& format;
|
||||
|
||||
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 seek(long ms);
|
||||
// time_point_ms seekTo(const time_point_ms& to);
|
||||
void updateBuffers(int age);
|
||||
void resetBuffers();
|
||||
void setRealSampleRate(double sampleRate);
|
||||
|
||||
SampleFormat format_;
|
||||
|
||||
long lastTick;
|
||||
chronos::usec sleep;
|
||||
|
||||
unsigned long playedSamples;
|
||||
chronos::time_point_hrc playedSamplesTime;
|
||||
|
||||
Queue<std::shared_ptr<PcmChunk>> chunks;
|
||||
// DoubleBuffer<chronos::usec::rep> cardBuffer;
|
||||
DoubleBuffer<chronos::usec::rep> miniBuffer;
|
||||
|
@ -50,6 +55,8 @@ private:
|
|||
int shortMedian;
|
||||
time_t lastUpdate;
|
||||
chronos::msec bufferMs;
|
||||
unsigned long playedFrames;
|
||||
long correctAfterXFrames;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
VERSION = 0.01
|
||||
CC = /usr/bin/g++
|
||||
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
|
||||
BIN = snapserver
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue