samplerate

git-svn-id: svn://elaine/murooma/trunk@307 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
(no author) 2014-10-16 05:40:17 +00:00
parent b715396832
commit 42ef09e726
5 changed files with 103 additions and 42 deletions

View file

@ -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)
{ {

View file

@ -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;

View file

@ -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";

View file

@ -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;
}; };

View file

@ -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