mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-25 15:06:21 +02:00
sampleFormat
git-svn-id: svn://elaine/murooma/trunk@205 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
parent
d4fcf84fb8
commit
e7803a2c11
13 changed files with 176 additions and 157 deletions
|
@ -3,7 +3,7 @@ CC = /usr/bin/g++
|
|||
CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -O3 -D_REENTRANT -DVERSION=\"$(VERSION)\" -I..
|
||||
LDFLAGS = -lrt -lpthread -lportaudio -lboost_system -lboost_program_options -lasound
|
||||
|
||||
OBJ = snapClient.o stream.o ../common/chunk.o ../common/log.o
|
||||
OBJ = snapClient.o stream.o ../common/chunk.o ../common/log.o ../common/sampleFormat.o
|
||||
BIN = snapclient
|
||||
|
||||
all: client
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <boost/program_options.hpp>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "common/sampleFormat.h"
|
||||
#include "common/chunk.h"
|
||||
#include "common/utils.h"
|
||||
#include "common/log.h"
|
||||
|
@ -30,9 +31,6 @@ namespace po = boost::program_options;
|
|||
|
||||
|
||||
int deviceIdx;
|
||||
uint16_t sampleRate;
|
||||
short channels;
|
||||
uint16_t bps;
|
||||
Stream* stream;
|
||||
|
||||
#define PCM_DEVICE "default"
|
||||
|
@ -52,7 +50,7 @@ void socketRead(tcp::socket* socket, void* to, size_t bytes)
|
|||
|
||||
|
||||
|
||||
void receiver(const std::string& ip, int port)
|
||||
void receiver(const SampleFormat& format, const std::string& ip, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -78,22 +76,10 @@ void receiver(const std::string& ip, int port)
|
|||
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 len = 0;
|
||||
do
|
||||
{
|
||||
len += s.read_some(boost::asio::buffer((char*)wireChunk + len, toRead));
|
||||
toRead = sizeof(WireChunk) - len;
|
||||
cout << "len: " << len << "\ttoRead: " << toRead << "\n";
|
||||
}
|
||||
while (toRead > 0);
|
||||
*/
|
||||
stream->addChunk(new Chunk(sampleRate, channels, bps, wireChunk));
|
||||
stream->addChunk(new Chunk(format, wireChunk));
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -113,58 +99,60 @@ void receiver(const std::string& ip, int port)
|
|||
|
||||
void player(Stream* stream)
|
||||
{
|
||||
unsigned int pcm, tmp, dir, rate;
|
||||
int channels, seconds;
|
||||
unsigned int pcm, tmp, rate;
|
||||
int channels;
|
||||
snd_pcm_t *pcm_handle;
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_uframes_t frames;
|
||||
char *buff;
|
||||
int buff_size;
|
||||
|
||||
rate = stream->getSampleRate();
|
||||
channels = stream->getChannels();
|
||||
rate = stream->format.rate;
|
||||
channels = stream->format.channels;
|
||||
|
||||
|
||||
/* Open the PCM device in playback mode */
|
||||
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
|
||||
SND_PCM_STREAM_PLAYBACK, 0) < 0)
|
||||
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
|
||||
PCM_DEVICE, snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||
cout << "ERROR: Can't open " << PCM_DEVICE << " PCM device. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
/* struct snd_pcm_playback_info_t pinfo;
|
||||
if ( (pcm = snd_pcm_playback_info( pcm_handle, &pinfo )) < 0 )
|
||||
fprintf( stderr, "Error: playback info error: %s\n", snd_strerror( err ) );
|
||||
printf("buffer: '%d'\n", pinfo.buffer_size);
|
||||
*/
|
||||
/* Allocate parameters object and fill it with default values*/
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
||||
snd_pcm_hw_params_any(pcm_handle, params);
|
||||
|
||||
/* Set parameters */
|
||||
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
|
||||
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
cout << "ERROR: Can't set interleaved mode. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
|
||||
SND_PCM_FORMAT_S16_LE) < 0)
|
||||
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE)) < 0)
|
||||
cout << "ERROR: Can't set format. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)
|
||||
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0)
|
||||
cout << "ERROR: Can't set channels number. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
|
||||
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0)
|
||||
cout << "ERROR: Can't set rate. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
long unsigned int periodsize = 2*rate / 50;
|
||||
if (pcm = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, params, &periodsize) < 0)
|
||||
printf("Unable to set buffer size %li: %s\n", (long int)periodsize, snd_strerror(pcm));
|
||||
long unsigned int periodsize = 2*rate / 100;
|
||||
if ((pcm = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, params, &periodsize)) < 0)
|
||||
cout << "Unable to set buffer size " << (long int)periodsize << ": " << snd_strerror(pcm) << "\n";
|
||||
|
||||
/* Write parameters */
|
||||
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
|
||||
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params(pcm_handle, params)) < 0)
|
||||
cout << "ERROR: Can't set harware parameters. " << snd_strerror(pcm) << "\n";
|
||||
|
||||
/* Resume information */
|
||||
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
|
||||
cout << "PCM name: " << snd_pcm_name(pcm_handle) << "\n";
|
||||
|
||||
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
|
||||
cout << "PCM state: " << snd_pcm_state_name(snd_pcm_state(pcm_handle)) << "\n";
|
||||
|
||||
snd_pcm_hw_params_get_channels(params, &tmp);
|
||||
printf("channels: %i ", tmp);
|
||||
cout << "channels: " << tmp << "\n";
|
||||
|
||||
if (tmp == 1)
|
||||
printf("(mono)\n");
|
||||
|
@ -172,19 +160,17 @@ void player(Stream* stream)
|
|||
printf("(stereo)\n");
|
||||
|
||||
snd_pcm_hw_params_get_rate(params, &tmp, 0);
|
||||
printf("rate: %d bps\n", tmp);
|
||||
|
||||
printf("seconds: %d\n", seconds);
|
||||
cout << "rate: " << tmp << " bps\n";
|
||||
|
||||
/* Allocate buffer to hold single period */
|
||||
snd_pcm_hw_params_get_period_size(params, &frames, 0);
|
||||
printf("frames: %d\n", frames);
|
||||
cout << "frames: " << frames << "\n";
|
||||
|
||||
buff_size = frames * channels * 2 /* 2 -> sample size */;
|
||||
buff = (char *) malloc(buff_size);
|
||||
|
||||
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
|
||||
printf("period time: %d\n", tmp);
|
||||
cout << "period time: " << tmp << "\n";
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -197,9 +183,9 @@ void player(Stream* stream)
|
|||
snd_pcm_sframes_t delay;
|
||||
snd_pcm_avail_delay(pcm_handle, &avail, &delay);
|
||||
|
||||
stream->getPlayerChunk(buff, (float)delay / 48000.f, frames);
|
||||
stream->getPlayerChunk(buff, (float)delay / stream->format.msRate(), frames);
|
||||
|
||||
if (pcm = snd_pcm_writei(pcm_handle, buff, frames) == -EPIPE) {
|
||||
if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE) {
|
||||
printf("XRUN.\n");
|
||||
snd_pcm_prepare(pcm_handle);
|
||||
} else if (pcm < 0) {
|
||||
|
@ -324,15 +310,14 @@ int main (int argc, char *argv[])
|
|||
int bufferMs;
|
||||
size_t port;
|
||||
bool runAsDaemon;
|
||||
string sampleFormat;
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("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")
|
||||
("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")
|
||||
("sampleformat,f", po::value<string>(&sampleFormat)->default_value("48000:16:2"), "sample format")
|
||||
("buffer,b", po::value<int>(&bufferMs)->default_value(300), "buffer size [ms]")
|
||||
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
||||
;
|
||||
|
@ -354,35 +339,15 @@ int main (int argc, char *argv[])
|
|||
std::clog << kLogNotice << "daemon started" << std::endl;
|
||||
}
|
||||
|
||||
std::clog << kLogNotice << "test" << std::endl;
|
||||
|
||||
stream = new Stream(sampleRate, channels, bps);
|
||||
stream = new Stream(SampleFormat(sampleFormat));
|
||||
stream->setBufferLen(bufferMs);
|
||||
// PaError paError;
|
||||
// PaStream* paStream = initAudio(paError, sampleRate, channels, bps);
|
||||
// stream->setLatency(1000*Pa_GetStreamInfo(paStream)->outputLatency);
|
||||
|
||||
std::thread receiverThread(receiver, ip, port);
|
||||
std::thread receiverThread(receiver, stream->format, ip, port);
|
||||
std::thread playerThread(player, stream);
|
||||
|
||||
std::string cmd;
|
||||
while (true && (argc > 3))
|
||||
{
|
||||
std::cout << "> ";
|
||||
std::getline(std::cin, cmd);
|
||||
// line = fgets( str, 256, stdin );
|
||||
// if (line == NULL)
|
||||
// continue;
|
||||
// std::string cmd(line);
|
||||
std::cerr << "CMD: " << cmd << "\n";
|
||||
if (cmd == "quit")
|
||||
break;
|
||||
else
|
||||
{
|
||||
stream->setBufferLen(atoi(cmd.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
playerThread.join();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
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)
|
||||
Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(sampleFormat), sleep(0), median(0), shortMedian(0), lastUpdate(0)
|
||||
{
|
||||
frameSize_ = bytesPerSample_*channels_;
|
||||
pBuffer = new DoubleBuffer<int>(1000);
|
||||
pShortBuffer = new DoubleBuffer<int>(200);
|
||||
pMiniBuffer = new DoubleBuffer<int>(20);
|
||||
|
@ -47,16 +46,11 @@ time_point_ms Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long fra
|
|||
if (!chunk)
|
||||
chunk = chunks.pop();
|
||||
time_point_ms tp = chunk->timePoint();
|
||||
memset(outputBuffer, 0, framesPerBuffer * frameSize_);
|
||||
memset(outputBuffer, 0, framesPerBuffer * format.frameSize);
|
||||
return tp;
|
||||
}
|
||||
|
||||
|
||||
void Stream::setLatency(size_t latency)
|
||||
{
|
||||
latencyMs = latency;
|
||||
}
|
||||
|
||||
|
||||
time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, int correction)
|
||||
{
|
||||
|
@ -65,22 +59,22 @@ time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long frame
|
|||
|
||||
time_point_ms tp = chunk->timePoint();
|
||||
int read = 0;
|
||||
int toRead = framesPerBuffer + correction*(hz_/1000);
|
||||
int toRead = framesPerBuffer + correction*format.msRate();
|
||||
char* buffer;
|
||||
|
||||
if (correction != 0)
|
||||
{
|
||||
int msBuffer = floor(framesPerBuffer / (hz_/1000));
|
||||
int msBuffer = floor(framesPerBuffer / format.msRate());
|
||||
if (abs(correction) > msBuffer / 2)
|
||||
correction = copysign(msBuffer / 2, correction);
|
||||
buffer = (char*)malloc(toRead * frameSize_);
|
||||
buffer = (char*)malloc(toRead * format.frameSize);
|
||||
}
|
||||
else
|
||||
buffer = (char*)outputBuffer;
|
||||
|
||||
while (read < toRead)
|
||||
{
|
||||
read += chunk->read(buffer + read*frameSize_, toRead - read);
|
||||
read += chunk->read(buffer + read*format.frameSize, toRead - read);
|
||||
if (chunk->isEndOfChunk())
|
||||
chunk = chunks.pop();
|
||||
}
|
||||
|
@ -93,7 +87,7 @@ time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long frame
|
|||
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||
{
|
||||
size_t index(floor(idx));// = (int)(ceil(n*factor));
|
||||
memcpy((char*)outputBuffer + n*frameSize_, buffer + index*frameSize_, frameSize_);
|
||||
memcpy((char*)outputBuffer + n*format.frameSize, buffer + index*format.frameSize, format.frameSize);
|
||||
idx += factor;
|
||||
}
|
||||
free(buffer);
|
||||
|
@ -122,13 +116,8 @@ void Stream::resetBuffers()
|
|||
|
||||
void Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer)
|
||||
{
|
||||
if (outputBufferDacTime > 1)
|
||||
outputBufferDacTime = 0;
|
||||
else
|
||||
outputBufferDacTime *= 1000;
|
||||
|
||||
//cout << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n";
|
||||
int msBuffer = framesPerBuffer / (hz_/1000);
|
||||
//int msBuffer = framesPerBuffer / (format_.rate/1000);
|
||||
//cout << msBuffer << " ms, " << framesPerBuffer << "\t" << hz_/1000 << "\n";
|
||||
int ticks = 0;
|
||||
long currentTick = getTickCount();
|
||||
|
@ -149,7 +138,7 @@ int msBuffer = framesPerBuffer / (hz_/1000);
|
|||
resetBuffers();
|
||||
if (sleep < -10)
|
||||
{
|
||||
sleep = Chunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + latencyMs + outputBufferDacTime;
|
||||
sleep = Chunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime;
|
||||
std::cerr << "Sleep: " << sleep << ", chunks: " << chunks.size() << "\n";
|
||||
// std::clog << kLogNotice << "sleep: " << sleep << std::endl;
|
||||
// if (sleep > -msBuffer/2)
|
||||
|
@ -161,7 +150,7 @@ int msBuffer = framesPerBuffer / (hz_/1000);
|
|||
// std::clog << kLogNotice << "sleep: " << sleep << std::endl;
|
||||
while (true)
|
||||
{
|
||||
sleep = Chunk::getAge(getNextPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + latencyMs + outputBufferDacTime;
|
||||
sleep = Chunk::getAge(getNextPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime;
|
||||
usleep(100);
|
||||
// std::clog << kLogNotice << "age: " << age << std::endl;
|
||||
if (sleep < 0)
|
||||
|
@ -184,7 +173,7 @@ int msBuffer = framesPerBuffer / (hz_/1000);
|
|||
|
||||
|
||||
|
||||
int age = Chunk::getAge(getNextPlayerChunk(outputBuffer, framesPerBuffer, correction)) - bufferMs + latencyMs + outputBufferDacTime;// + outputBufferDacTime*1000;
|
||||
int age = Chunk::getAge(getNextPlayerChunk(outputBuffer, framesPerBuffer, correction)) - bufferMs + outputBufferDacTime;
|
||||
|
||||
|
||||
// if (pCardBuffer->full())
|
||||
|
|
|
@ -11,25 +11,18 @@
|
|||
#include "common/chunk.h"
|
||||
#include "common/timeUtils.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/sampleFormat.h"
|
||||
|
||||
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream(size_t hz, size_t channels, size_t bps);
|
||||
Stream(const SampleFormat& format);
|
||||
void addChunk(Chunk* chunk);
|
||||
void clearChunks();
|
||||
void getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer);
|
||||
void setBufferLen(size_t bufferLenMs);
|
||||
void setLatency(size_t latency);
|
||||
size_t getSampleRate() const
|
||||
{
|
||||
return hz_;
|
||||
}
|
||||
size_t getChannels() const
|
||||
{
|
||||
return channels_;
|
||||
}
|
||||
const SampleFormat& format;
|
||||
|
||||
private:
|
||||
time_point_ms getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, int correction = 0);
|
||||
|
@ -37,15 +30,11 @@ private:
|
|||
void updateBuffers(int age);
|
||||
void resetBuffers();
|
||||
|
||||
size_t hz_;
|
||||
size_t channels_;
|
||||
size_t bytesPerSample_;
|
||||
size_t frameSize_;
|
||||
SampleFormat format_;
|
||||
|
||||
long lastTick;
|
||||
int sleep;
|
||||
|
||||
// int correction;
|
||||
Queue<std::shared_ptr<Chunk>> chunks;
|
||||
DoubleBuffer<int>* pCardBuffer;
|
||||
DoubleBuffer<int>* pMiniBuffer;
|
||||
|
@ -57,7 +46,6 @@ private:
|
|||
int shortMedian;
|
||||
time_t lastUpdate;
|
||||
int bufferMs;
|
||||
int latencyMs;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
#include "chunk.h"
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include "common/log.h"
|
||||
|
||||
|
||||
Chunk::Chunk(size_t hz, size_t channels, size_t bitPerSample, WireChunk* _wireChunk) : wireChunk(_wireChunk), hz_(hz), channels_(channels), bytesPerSample_(bitPerSample/8), idx(0)
|
||||
Chunk::Chunk(const SampleFormat& sampleFormat, WireChunk* _wireChunk) : wireChunk(_wireChunk), format(format_), format_(sampleFormat), idx(0)
|
||||
{
|
||||
frameSize_ = bytesPerSample_*channels_;
|
||||
}
|
||||
|
||||
|
||||
Chunk::Chunk(size_t hz, size_t channels, size_t bitPerSample, size_t ms) : hz_(hz), channels_(channels), bytesPerSample_(bitPerSample/8), idx(0)
|
||||
Chunk::Chunk(const SampleFormat& sampleFormat, size_t ms) : format(format_), format_(sampleFormat), idx(0)
|
||||
{
|
||||
frameSize_ = bytesPerSample_*channels_;
|
||||
wireChunk = new WireChunk;
|
||||
wireChunk->length = hz*frameSize_*ms / 1000;
|
||||
wireChunk->length = format.rate*format.frameSize*ms / 1000;
|
||||
wireChunk->payload = (char*)malloc(wireChunk->length);
|
||||
}
|
||||
|
||||
|
@ -27,32 +26,31 @@ Chunk::~Chunk()
|
|||
|
||||
bool Chunk::isEndOfChunk() const
|
||||
{
|
||||
return idx >= (wireChunk->length / frameSize_);
|
||||
return idx >= (wireChunk->length / format.frameSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double Chunk::getDuration() const
|
||||
{
|
||||
// std::cout << "len: " << wireChunk->length << ", channels: " << channels_ << ", bytesPerSample: " << bytesPerSample_ << ", hz: " << hz_ << "\n";
|
||||
return wireChunk->length / (frameSize_ * hz_ / 1000.);
|
||||
return (wireChunk->length / format.frameSize) / ((double)format.rate / 1000.);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Chunk::read(void* outputBuffer, size_t frameCount)
|
||||
{
|
||||
//std::cout << "read: " << frameCount << ", total: " << (wireChunk->length / frameSize_) << ", idx: " << idx;// << std::endl;
|
||||
//logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize) << ", idx: " << idx;// << std::endl;
|
||||
int result = frameCount;
|
||||
if (idx + frameCount > (wireChunk->length / frameSize_))
|
||||
result = (wireChunk->length / frameSize_) - idx;
|
||||
if (idx + frameCount > (wireChunk->length / format.frameSize))
|
||||
result = (wireChunk->length / format.frameSize) - idx;
|
||||
|
||||
//std::cout << ", from: " << frameSize_*idx << ", to: " << frameSize_*idx + frameSize_*result;
|
||||
//logd << ", from: " << format.frameSize*idx << ", to: " << format.frameSize*idx + format.frameSize*result;
|
||||
if (outputBuffer != NULL)
|
||||
memcpy((char*)outputBuffer, (char*)(wireChunk->payload) + frameSize_*idx, frameSize_*result);
|
||||
memcpy((char*)outputBuffer, (char*)(wireChunk->payload) + format.frameSize*idx, format.frameSize*result);
|
||||
|
||||
idx += result;
|
||||
//std::cout << ", new idx: " << idx << ", result: " << result << std::endl;
|
||||
//logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize: " << format.frameSize << "\n";//std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/sampleFormat.h"
|
||||
|
||||
|
||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock, std::chrono::milliseconds> time_point_ms;
|
||||
|
@ -22,19 +22,11 @@ struct WireChunk
|
|||
class Chunk
|
||||
{
|
||||
public:
|
||||
Chunk(size_t hz, size_t channels, size_t bitPerSample, WireChunk* _wireChunk);
|
||||
Chunk(size_t hz, size_t channels, size_t bitPerSample, size_t ms);
|
||||
Chunk(const SampleFormat& sampleFormat, WireChunk* _wireChunk);
|
||||
Chunk(const SampleFormat& sampleFormat, size_t ms);
|
||||
~Chunk();
|
||||
|
||||
/* static WireChunk* make_chunk(size_t size, size_t bytesPerSample)
|
||||
{
|
||||
WireChunk* wireChunk = new WireChunk(bytesPerSample*size);
|
||||
wireChunk->length = bytesPerSample*size;
|
||||
wireChunk->payload = (char*)malloc(wireChunk->length);
|
||||
return wireChunk;
|
||||
}
|
||||
*/
|
||||
static size_t getHeaderSize()
|
||||
static inline size_t getHeaderSize()
|
||||
{
|
||||
return sizeof(WireChunk::tv_sec) + sizeof(WireChunk::tv_usec) + sizeof(WireChunk::length);
|
||||
}
|
||||
|
@ -45,7 +37,12 @@ public:
|
|||
inline time_point_ms timePoint() const
|
||||
{
|
||||
time_point_ms tp;
|
||||
return tp + std::chrono::seconds(wireChunk->tv_sec) + std::chrono::milliseconds(wireChunk->tv_usec / 1000) + std::chrono::milliseconds(idx / (hz_/1000));
|
||||
std::chrono::milliseconds::rep relativeIdxTp = ((double)idx / ((double)format.rate/1000.));
|
||||
return
|
||||
tp +
|
||||
std::chrono::seconds(wireChunk->tv_sec) +
|
||||
std::chrono::milliseconds(wireChunk->tv_usec / 1000) +
|
||||
std::chrono::milliseconds(relativeIdxTp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -73,14 +70,11 @@ public:
|
|||
double getDuration() const;
|
||||
|
||||
WireChunk* wireChunk;
|
||||
size_t hz_;
|
||||
size_t channels_;
|
||||
size_t bytesPerSample_;
|
||||
size_t frameSize_;
|
||||
const SampleFormat& format;
|
||||
|
||||
private:
|
||||
|
||||
int32_t idx;
|
||||
SampleFormat format_;
|
||||
uint32_t idx;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,10 @@ Log::Log(std::string ident, int facility) {
|
|||
|
||||
int Log::sync() {
|
||||
if (buffer_.length()) {
|
||||
syslog(priority_, buffer_.c_str());
|
||||
if (priority_ == dbg)
|
||||
std::cout << buffer_.c_str();
|
||||
else
|
||||
syslog(priority_, "%s", buffer_.c_str());
|
||||
buffer_.erase();
|
||||
priority_ = LOG_DEBUG; // default to debug for each message
|
||||
}
|
||||
|
@ -29,6 +32,8 @@ int Log::overflow(int c) {
|
|||
|
||||
std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority) {
|
||||
static_cast<Log *>(os.rdbuf())->priority_ = (int)log_priority;
|
||||
if (log_priority == dbg)
|
||||
os.flush();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#define logd std::clog << dbg
|
||||
|
||||
enum LogPriority {
|
||||
kLogEmerg = LOG_EMERG, // system is unusable
|
||||
|
@ -14,7 +15,8 @@ enum LogPriority {
|
|||
kLogWarning = LOG_WARNING, // warning conditions
|
||||
kLogNotice = LOG_NOTICE, // normal, but significant, condition
|
||||
kLogInfo = LOG_INFO, // informational message
|
||||
kLogDebug = LOG_DEBUG // debug-level message
|
||||
kLogDebug = LOG_DEBUG, // debug-level message
|
||||
dbg
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority);
|
||||
|
|
44
common/sampleFormat.cpp
Normal file
44
common/sampleFormat.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "sampleFormat.h"
|
||||
#include <vector>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
SampleFormat::SampleFormat(const std::string& format) : rate(rate_), bits(bits_), channels(channels_), sampleSize(bytes_), frameSize(frameSize_)
|
||||
{
|
||||
setFormat(format);
|
||||
}
|
||||
|
||||
|
||||
SampleFormat::SampleFormat(uint16_t sampleRate, uint16_t bitsPerSample, uint16_t channelCount) : rate(rate_), bits(bits_), channels(channels_), sampleSize(bytes_), frameSize(frameSize_)
|
||||
{
|
||||
setFormat(sampleRate, bitsPerSample, channelCount);
|
||||
}
|
||||
|
||||
|
||||
void SampleFormat::setFormat(const std::string& format)
|
||||
{
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, format, boost::is_any_of(":"));
|
||||
if (strs.size() == 3)
|
||||
setFormat(
|
||||
boost::lexical_cast<uint16_t>(strs[0]),
|
||||
boost::lexical_cast<uint16_t>(strs[1]),
|
||||
boost::lexical_cast<uint16_t>(strs[2]));
|
||||
}
|
||||
|
||||
|
||||
void SampleFormat::setFormat(uint16_t rate, uint16_t bits, uint16_t channels)
|
||||
{
|
||||
rate_ = rate;
|
||||
bits_ = bits;
|
||||
bytes_ = bits / 8;
|
||||
channels_ = channels;
|
||||
if (bits_ == 24)
|
||||
bytes_ = 4;
|
||||
frameSize_ = channels_*bytes_;
|
||||
}
|
||||
|
||||
|
||||
|
36
common/sampleFormat.h
Normal file
36
common/sampleFormat.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef SAMPLE_FORMAT
|
||||
#define SAMPLE_FORMAT
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class SampleFormat
|
||||
{
|
||||
public:
|
||||
SampleFormat();
|
||||
SampleFormat(const std::string& format);
|
||||
SampleFormat(uint16_t rate = 48000, uint16_t bits = 16, uint16_t channels = 2);
|
||||
|
||||
void setFormat(const std::string& format);
|
||||
void setFormat(uint16_t rate, uint16_t bits, uint16_t channels);
|
||||
|
||||
const uint16_t& rate;
|
||||
const uint16_t& bits;
|
||||
const uint16_t& channels;
|
||||
|
||||
const uint16_t& sampleSize;
|
||||
const uint16_t& frameSize;
|
||||
|
||||
float msRate() const { return (float)rate/1000.f; }
|
||||
|
||||
private:
|
||||
uint16_t rate_;
|
||||
uint16_t bits_;
|
||||
uint16_t channels_;
|
||||
uint16_t bytes_;
|
||||
uint16_t frameSize_;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
BIN
common/sampleFormat.o
Normal file
BIN
common/sampleFormat.o
Normal file
Binary file not shown.
|
@ -3,7 +3,7 @@ CC = /usr/bin/g++
|
|||
CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -D_REENTRANT -DVERSION=\"$(VERSION)\" -I..
|
||||
LDFLAGS = -lrt -lpthread -lportaudio -lboost_system -lboost_program_options
|
||||
|
||||
OBJ = snapServer.o ../common/chunk.o
|
||||
OBJ = snapServer.o ../common/chunk.o ../common/sampleFormat.o
|
||||
BIN = snapserver
|
||||
|
||||
all: server
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "common/queue.h"
|
||||
#include "common/signalHandler.h"
|
||||
#include "common/utils.h"
|
||||
#include "common/sampleFormat.h"
|
||||
#include <syslog.h>
|
||||
|
||||
|
||||
|
@ -201,9 +202,7 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
try
|
||||
{
|
||||
uint16_t sampleRate;
|
||||
short channels;
|
||||
uint16_t bps;
|
||||
string sampleFormat;
|
||||
|
||||
size_t port;
|
||||
string fifoName;
|
||||
|
@ -213,9 +212,7 @@ int main(int argc, char* argv[])
|
|||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("port,p", po::value<size_t>(&port)->default_value(98765), "port to listen on")
|
||||
("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,b", po::value<uint16_t>(&bps)->default_value(16), "bit per sample")
|
||||
("sampleformat,f", po::value<string>(&sampleFormat)->default_value("48000:16:2"), "sample format")
|
||||
("fifo,f", po::value<string>(&fifoName)->default_value("/tmp/snapfifo"), "name of fifo file")
|
||||
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
||||
;
|
||||
|
@ -264,6 +261,7 @@ int main(int argc, char* argv[])
|
|||
mkfifo(fifoName.c_str(), 0777);
|
||||
size_t duration = 50;
|
||||
|
||||
SampleFormat format(sampleFormat);
|
||||
while (!g_terminated)
|
||||
{
|
||||
int fd = open(fifoName.c_str(), O_RDONLY);
|
||||
|
@ -272,7 +270,7 @@ size_t duration = 50;
|
|||
shared_ptr<Chunk> chunk;//(new WireChunk());
|
||||
while (true)//cin.good())
|
||||
{
|
||||
chunk.reset(new Chunk(sampleRate, channels, bps, duration));//2*WIRE_CHUNK_SIZE));
|
||||
chunk.reset(new Chunk(format, duration));//2*WIRE_CHUNK_SIZE));
|
||||
WireChunk* wireChunk = chunk->wireChunk;
|
||||
int toRead = wireChunk->length;
|
||||
// cout << "tr: " << toRead << ", size: " << WIRE_CHUNK_SIZE << "\t";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue