add clang-format file

reformat code
This commit is contained in:
badaix 2019-09-24 22:42:36 +02:00
parent b733f646ea
commit b20add3815
105 changed files with 7773 additions and 7723 deletions

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,25 +18,23 @@
#ifndef DECODER_H
#define DECODER_H
#include <mutex>
#include "message/pcmChunk.h"
#include "message/codecHeader.h"
#include "common/sampleFormat.h"
#include "message/codecHeader.h"
#include "message/pcmChunk.h"
#include <mutex>
class Decoder
{
public:
Decoder() {};
virtual ~Decoder() {};
virtual bool decode(msg::PcmChunk* chunk) = 0;
virtual SampleFormat setHeader(msg::CodecHeader* chunk) = 0;
Decoder(){};
virtual ~Decoder(){};
virtual bool decode(msg::PcmChunk* chunk) = 0;
virtual SampleFormat setHeader(msg::CodecHeader* chunk) = 0;
protected:
std::mutex mutex_;
std::mutex mutex_;
};
#endif

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,215 +16,209 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include <iostream>
#include <cstring>
#include <cmath>
#include "flacDecoder.h"
#include "common/snapException.h"
#include "common/endian.hpp"
#include "aixlog.hpp"
#include "common/endian.hpp"
#include "common/snapException.h"
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, void* client_data);
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* decoder, const FLAC__Frame* frame, const FLAC__int32* const buffer[],
void* client_data);
static void metadata_callback(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data);
static void error_callback(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data);
static msg::CodecHeader* flacHeader = NULL;
static msg::PcmChunk* flacChunk = NULL;
static msg::PcmChunk* pcmChunk = NULL;
static SampleFormat sampleFormat;
static FLAC__StreamDecoder *decoder = NULL;
static FLAC__StreamDecoder* decoder = NULL;
FlacDecoder::FlacDecoder() : Decoder(), lastError_(nullptr)
{
flacChunk = new msg::PcmChunk();
flacChunk = new msg::PcmChunk();
}
FlacDecoder::~FlacDecoder()
{
std::lock_guard<std::mutex> lock(mutex_);
delete flacChunk;
delete decoder;
std::lock_guard<std::mutex> lock(mutex_);
delete flacChunk;
delete decoder;
}
bool FlacDecoder::decode(msg::PcmChunk* chunk)
{
std::lock_guard<std::mutex> lock(mutex_);
cacheInfo_.reset();
pcmChunk = chunk;
flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize);
memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize);
flacChunk->payloadSize = chunk->payloadSize;
std::lock_guard<std::mutex> lock(mutex_);
cacheInfo_.reset();
pcmChunk = chunk;
flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize);
memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize);
flacChunk->payloadSize = chunk->payloadSize;
pcmChunk->payload = (char*)realloc(pcmChunk->payload, 0);
pcmChunk->payloadSize = 0;
while (flacChunk->payloadSize > 0)
{
if (!FLAC__stream_decoder_process_single(decoder))
{
return false;
}
pcmChunk->payload = (char*)realloc(pcmChunk->payload, 0);
pcmChunk->payloadSize = 0;
while (flacChunk->payloadSize > 0)
{
if (!FLAC__stream_decoder_process_single(decoder))
{
return false;
}
if (lastError_)
{
LOG(ERROR) << "FLAC decode error: " << FLAC__StreamDecoderErrorStatusString[*lastError_] << "\n";
lastError_= nullptr;
return false;
}
}
if (lastError_)
{
LOG(ERROR) << "FLAC decode error: " << FLAC__StreamDecoderErrorStatusString[*lastError_] << "\n";
lastError_ = nullptr;
return false;
}
}
if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0))
{
double diffMs = cacheInfo_.cachedBlocks_ / ((double)cacheInfo_.sampleRate_ / 1000.);
int32_t s = (diffMs / 1000);
int32_t us = (diffMs * 1000);
us %= 1000000;
LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n";
chunk->timestamp = chunk->timestamp - tv(s, us);
}
return true;
if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0))
{
double diffMs = cacheInfo_.cachedBlocks_ / ((double)cacheInfo_.sampleRate_ / 1000.);
int32_t s = (diffMs / 1000);
int32_t us = (diffMs * 1000);
us %= 1000000;
LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n";
chunk->timestamp = chunk->timestamp - tv(s, us);
}
return true;
}
SampleFormat FlacDecoder::setHeader(msg::CodecHeader* chunk)
{
flacHeader = chunk;
FLAC__StreamDecoderInitStatus init_status;
flacHeader = chunk;
FLAC__StreamDecoderInitStatus init_status;
if ((decoder = FLAC__stream_decoder_new()) == NULL)
throw SnapException("ERROR: allocating decoder");
if ((decoder = FLAC__stream_decoder_new()) == NULL)
throw SnapException("ERROR: allocating decoder");
// (void)FLAC__stream_decoder_set_md5_checking(decoder, true);
init_status = FLAC__stream_decoder_init_stream(decoder, read_callback, NULL, NULL, NULL, NULL, write_callback, metadata_callback, error_callback, this);
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status]));
// (void)FLAC__stream_decoder_set_md5_checking(decoder, true);
init_status = FLAC__stream_decoder_init_stream(decoder, read_callback, NULL, NULL, NULL, NULL, write_callback, metadata_callback, error_callback, this);
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status]));
sampleFormat.rate = 0;
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
if (sampleFormat.rate == 0)
throw SnapException("Sample format not found");
sampleFormat.rate = 0;
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
if (sampleFormat.rate == 0)
throw SnapException("Sample format not found");
return sampleFormat;
return sampleFormat;
}
FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, void* client_data)
{
if (flacHeader != NULL)
{
*bytes = flacHeader->payloadSize;
memcpy(buffer, flacHeader->payload, *bytes);
flacHeader = NULL;
}
else if (flacChunk != NULL)
{
// cerr << "read_callback: " << *bytes << ", avail: " << flacChunk->payloadSize << "\n";
static_cast<FlacDecoder*>(client_data)->cacheInfo_.isCachedChunk_ = false;
if (*bytes > flacChunk->payloadSize)
*bytes = flacChunk->payloadSize;
if (flacHeader != NULL)
{
*bytes = flacHeader->payloadSize;
memcpy(buffer, flacHeader->payload, *bytes);
flacHeader = NULL;
}
else if (flacChunk != NULL)
{
// cerr << "read_callback: " << *bytes << ", avail: " << flacChunk->payloadSize << "\n";
static_cast<FlacDecoder*>(client_data)->cacheInfo_.isCachedChunk_ = false;
if (*bytes > flacChunk->payloadSize)
*bytes = flacChunk->payloadSize;
// if (*bytes == 0)
// return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
// if (*bytes == 0)
// return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
memcpy(buffer, flacChunk->payload, *bytes);
memmove(flacChunk->payload, flacChunk->payload + *bytes, flacChunk->payloadSize - *bytes);
flacChunk->payloadSize = flacChunk->payloadSize - *bytes;
flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize);
}
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
memcpy(buffer, flacChunk->payload, *bytes);
memmove(flacChunk->payload, flacChunk->payload + *bytes, flacChunk->payloadSize - *bytes);
flacChunk->payloadSize = flacChunk->payloadSize - *bytes;
flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize);
}
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* decoder, const FLAC__Frame* frame, const FLAC__int32* const buffer[],
void* client_data)
{
(void)decoder;
(void)decoder;
if (pcmChunk != NULL)
{
size_t bytes = frame->header.blocksize * sampleFormat.frameSize;
if (pcmChunk != NULL)
{
size_t bytes = frame->header.blocksize * sampleFormat.frameSize;
FlacDecoder* flacDecoder = static_cast<FlacDecoder*>(client_data);
if (flacDecoder->cacheInfo_.isCachedChunk_)
flacDecoder->cacheInfo_.cachedBlocks_ += frame->header.blocksize;
FlacDecoder* flacDecoder = static_cast<FlacDecoder*>(client_data);
if (flacDecoder->cacheInfo_.isCachedChunk_)
flacDecoder->cacheInfo_.cachedBlocks_ += frame->header.blocksize;
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
for (size_t channel = 0; channel < sampleFormat.channels; ++channel)
{
if (buffer[channel] == NULL)
{
SLOG(ERROR) << "ERROR: buffer[" << channel << "] is NULL\n";
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
if (sampleFormat.sampleSize == 1)
{
int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels*i + channel] = (int8_t)(buffer[channel][i]);
}
else if (sampleFormat.sampleSize == 2)
{
int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels*i + channel] = SWAP_16((int16_t)(buffer[channel][i]));
}
else if (sampleFormat.sampleSize == 4)
{
int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels*i + channel] = SWAP_32((int32_t)(buffer[channel][i]));
}
}
pcmChunk->payloadSize += bytes;
}
for (size_t channel = 0; channel < sampleFormat.channels; ++channel)
{
if (buffer[channel] == NULL)
{
SLOG(ERROR) << "ERROR: buffer[" << channel << "] is NULL\n";
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
if (sampleFormat.sampleSize == 1)
{
int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels * i + channel] = (int8_t)(buffer[channel][i]);
}
else if (sampleFormat.sampleSize == 2)
{
int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels * i + channel] = SWAP_16((int16_t)(buffer[channel][i]));
}
else if (sampleFormat.sampleSize == 4)
{
int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize);
for (size_t i = 0; i < frame->header.blocksize; i++)
chunkBuffer[sampleFormat.channels * i + channel] = SWAP_32((int32_t)(buffer[channel][i]));
}
}
pcmChunk->payloadSize += bytes;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
void metadata_callback(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data)
{
(void)decoder;
/* print some stats */
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
{
static_cast<FlacDecoder*>(client_data)->cacheInfo_.sampleRate_ = metadata->data.stream_info.sample_rate;
sampleFormat.setFormat(
metadata->data.stream_info.sample_rate,
metadata->data.stream_info.bits_per_sample,
metadata->data.stream_info.channels);
}
(void)decoder;
/* print some stats */
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
{
static_cast<FlacDecoder*>(client_data)->cacheInfo_.sampleRate_ = metadata->data.stream_info.sample_rate;
sampleFormat.setFormat(metadata->data.stream_info.sample_rate, metadata->data.stream_info.bits_per_sample, metadata->data.stream_info.channels);
}
}
void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
void error_callback(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data)
{
(void)decoder, (void)client_data;
SLOG(ERROR) << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << "\n";
static_cast<FlacDecoder*>(client_data)->lastError_ = std::unique_ptr<FLAC__StreamDecoderErrorStatus>(new FLAC__StreamDecoderErrorStatus(status));
(void)decoder, (void)client_data;
SLOG(ERROR) << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << "\n";
static_cast<FlacDecoder*>(client_data)->lastError_ = std::unique_ptr<FLAC__StreamDecoderErrorStatus>(new FLAC__StreamDecoderErrorStatus(status));
/// TODO, see issue #120:
// Thu Nov 10 07:26:44 2016 daemon.warn dnsmasq-dhcp[1194]: no address range available for DHCP request via wlan0
// Thu Nov 10 07:54:39 2016 daemon.err snapclient[1158]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
// Thu Nov 10 07:54:39 2016 daemon.err snapclient[1158]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
//
// and:
// Oct 27 17:37:38 kitchen snapclient[869]: Connected to 192.168.222.10
// Oct 27 17:47:13 kitchen snapclient[869]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
// Oct 27 17:47:13 kitchen snapclient[869]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
/// TODO, see issue #120:
// Thu Nov 10 07:26:44 2016 daemon.warn dnsmasq-dhcp[1194]: no address range available for DHCP request via wlan0
// Thu Nov 10 07:54:39 2016 daemon.err snapclient[1158]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
// Thu Nov 10 07:54:39 2016 daemon.err snapclient[1158]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
//
// and:
// Oct 27 17:37:38 kitchen snapclient[869]: Connected to 192.168.222.10
// Oct 27 17:47:13 kitchen snapclient[869]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
// Oct 27 17:47:13 kitchen snapclient[869]: Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
}

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,36 +29,34 @@
struct CacheInfo
{
CacheInfo() : sampleRate_(0)
{
reset();
}
CacheInfo() : sampleRate_(0)
{
reset();
}
void reset()
{
isCachedChunk_ = true;
cachedBlocks_ = 0;
}
void reset()
{
isCachedChunk_ = true;
cachedBlocks_ = 0;
}
bool isCachedChunk_;
size_t cachedBlocks_;
size_t sampleRate_;
bool isCachedChunk_;
size_t cachedBlocks_;
size_t sampleRate_;
};
class FlacDecoder : public Decoder
{
public:
FlacDecoder();
virtual ~FlacDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
FlacDecoder();
virtual ~FlacDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
CacheInfo cacheInfo_;
std::unique_ptr<FLAC__StreamDecoderErrorStatus> lastError_;
CacheInfo cacheInfo_;
std::unique_ptr<FLAC__StreamDecoderErrorStatus> lastError_;
};
#endif

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstring>
#include <iostream>
#include "oggDecoder.h"
#include "common/snapException.h"
#include "common/endian.hpp"
#include "aixlog.hpp"
#include "common/endian.hpp"
#include "common/snapException.h"
#include "oggDecoder.h"
using namespace std;
@ -31,212 +31,210 @@ using namespace std;
OggDecoder::OggDecoder() : Decoder()
{
ogg_sync_init(&oy); /* Now we can read pages */
ogg_sync_init(&oy); /* Now we can read pages */
}
OggDecoder::~OggDecoder()
{
std::lock_guard<std::mutex> lock(mutex_);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
ogg_stream_clear(&os);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi); /* must be called last */
ogg_sync_clear(&oy);
std::lock_guard<std::mutex> lock(mutex_);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
ogg_stream_clear(&os);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi); /* must be called last */
ogg_sync_clear(&oy);
}
bool OggDecoder::decode(msg::PcmChunk* chunk)
{
std::lock_guard<std::mutex> lock(mutex_);
/* grab some data at the head of the stream. We want the first page
(which is guaranteed to be small and only contain the Vorbis
stream initial header) We need the first page to get the stream
serialno. */
int size = chunk->payloadSize;
char *buffer = ogg_sync_buffer(&oy, size);
memcpy(buffer, chunk->payload, size);
ogg_sync_wrote(&oy, size);
std::lock_guard<std::mutex> lock(mutex_);
/* grab some data at the head of the stream. We want the first page
(which is guaranteed to be small and only contain the Vorbis
stream initial header) We need the first page to get the stream
serialno. */
int size = chunk->payloadSize;
char* buffer = ogg_sync_buffer(&oy, size);
memcpy(buffer, chunk->payload, size);
ogg_sync_wrote(&oy, size);
chunk->payloadSize = 0;
/* The rest is just a straight decode loop until end of stream */
// while(!eos){
while(true)
{
int result = ogg_sync_pageout(&oy, &og);
if (result == 0)
break; /* need more data */
if (result < 0)
{
/* missing or corrupt data at this page position */
LOG(ERROR) << "Corrupt or missing data in bitstream; continuing...\n";
continue;
}
chunk->payloadSize = 0;
/* The rest is just a straight decode loop until end of stream */
// while(!eos){
while (true)
{
int result = ogg_sync_pageout(&oy, &og);
if (result == 0)
break; /* need more data */
if (result < 0)
{
/* missing or corrupt data at this page position */
LOG(ERROR) << "Corrupt or missing data in bitstream; continuing...\n";
continue;
}
ogg_stream_pagein(&os,&og); /* can safely ignore errors at this point */
while(1)
{
result = ogg_stream_packetout(&os, &op);
ogg_stream_pagein(&os, &og); /* can safely ignore errors at this point */
while (1)
{
result = ogg_stream_packetout(&os, &op);
if (result == 0)
break; /* need more data */
if (result < 0)
continue; /* missing or corrupt data at this page position */
/* no reason to complain; already complained above */
/* we have a packet. Decode it */
if (result == 0)
break; /* need more data */
if (result < 0)
continue; /* missing or corrupt data at this page position */
/* no reason to complain; already complained above */
/* we have a packet. Decode it */
#ifdef HAS_TREMOR
ogg_int32_t **pcm;
ogg_int32_t** pcm;
#else
float **pcm;
float** pcm;
#endif
int samples;
int samples;
if (vorbis_synthesis(&vb,&op) == 0) /* test for success! */
vorbis_synthesis_blockin(&vd, &vb);
/*
**pcm is a multichannel float vector. In stereo, for
example, pcm[0] is left, and pcm[1] is right. samples is
the size of each channel. Convert the float values
(-1.<=range<=1.) to whatever PCM format and write it out */
while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0)
{
size_t bytes = sampleFormat_.sampleSize * vi.channels * samples;
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize + bytes);
for (int channel = 0; channel < vi.channels; ++channel)
{
if (sampleFormat_.sampleSize == 1)
{
int8_t* chunkBuffer = (int8_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int8_t& val = chunkBuffer[sampleFormat_.channels*i + channel];
if (vorbis_synthesis(&vb, &op) == 0) /* test for success! */
vorbis_synthesis_blockin(&vd, &vb);
/*
**pcm is a multichannel float vector. In stereo, for
example, pcm[0] is left, and pcm[1] is right. samples is
the size of each channel. Convert the float values
(-1.<=range<=1.) to whatever PCM format and write it out */
while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0)
{
size_t bytes = sampleFormat_.sampleSize * vi.channels * samples;
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize + bytes);
for (int channel = 0; channel < vi.channels; ++channel)
{
if (sampleFormat_.sampleSize == 1)
{
int8_t* chunkBuffer = (int8_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int8_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
#ifdef HAS_TREMOR
val = clip<int8_t>(pcm[channel][i], -128, 127);
val = clip<int8_t>(pcm[channel][i], -128, 127);
#else
val = clip<int8_t>(floor(pcm[channel][i]*127.f + .5f), -128, 127);
val = clip<int8_t>(floor(pcm[channel][i] * 127.f + .5f), -128, 127);
#endif
}
}
else if (sampleFormat_.sampleSize == 2)
{
int16_t* chunkBuffer = (int16_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int16_t& val = chunkBuffer[sampleFormat_.channels*i + channel];
}
}
else if (sampleFormat_.sampleSize == 2)
{
int16_t* chunkBuffer = (int16_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int16_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
#ifdef HAS_TREMOR
val = SWAP_16(clip<int16_t>(pcm[channel][i] >> 9, -32768, 32767));
val = SWAP_16(clip<int16_t>(pcm[channel][i] >> 9, -32768, 32767));
#else
val = SWAP_16(clip<int16_t>(floor(pcm[channel][i]*32767.f + .5f), -32768, 32767));
val = SWAP_16(clip<int16_t>(floor(pcm[channel][i] * 32767.f + .5f), -32768, 32767));
#endif
}
}
else if (sampleFormat_.sampleSize == 4)
{
int32_t* chunkBuffer = (int32_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int32_t& val = chunkBuffer[sampleFormat_.channels*i + channel];
}
}
else if (sampleFormat_.sampleSize == 4)
{
int32_t* chunkBuffer = (int32_t*)(chunk->payload + chunk->payloadSize);
for (int i = 0; i < samples; i++)
{
int32_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
#ifdef HAS_TREMOR
val = SWAP_32(clip<int32_t>(pcm[channel][i] << 7, -2147483648, 2147483647));
val = SWAP_32(clip<int32_t>(pcm[channel][i] << 7, -2147483648, 2147483647));
#else
val = SWAP_32(clip<int32_t>(floor(pcm[channel][i]*2147483647.f + .5f), -2147483648, 2147483647));
val = SWAP_32(clip<int32_t>(floor(pcm[channel][i] * 2147483647.f + .5f), -2147483648, 2147483647));
#endif
}
}
}
}
}
}
chunk->payloadSize += bytes;
vorbis_synthesis_read(&vd, samples);
}
}
}
chunk->payloadSize += bytes;
vorbis_synthesis_read(&vd, samples);
}
}
}
return true;
return true;
}
SampleFormat OggDecoder::setHeader(msg::CodecHeader* chunk)
{
int size = chunk->payloadSize;
char *buffer = ogg_sync_buffer(&oy, size);
memcpy(buffer, chunk->payload, size);
ogg_sync_wrote(&oy, size);
int size = chunk->payloadSize;
char* buffer = ogg_sync_buffer(&oy, size);
memcpy(buffer, chunk->payload, size);
ogg_sync_wrote(&oy, size);
if (ogg_sync_pageout(&oy, &og) != 1)
throw SnapException("Input does not appear to be an Ogg bitstream");
if (ogg_sync_pageout(&oy, &og) != 1)
throw SnapException("Input does not appear to be an Ogg bitstream");
ogg_stream_init(&os,ogg_page_serialno(&og));
ogg_stream_init(&os, ogg_page_serialno(&og));
vorbis_info_init(&vi);
vorbis_comment_init(&vc);
if (ogg_stream_pagein(&os, &og) < 0)
throw SnapException("Error reading first page of Ogg bitstream data");
vorbis_info_init(&vi);
vorbis_comment_init(&vc);
if (ogg_stream_pagein(&os, &og) < 0)
throw SnapException("Error reading first page of Ogg bitstream data");
if (ogg_stream_packetout(&os, &op) != 1)
throw SnapException("Error reading initial header packet");
if (ogg_stream_packetout(&os, &op) != 1)
throw SnapException("Error reading initial header packet");
if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0)
throw SnapException("This Ogg bitstream does not contain Vorbis audio data");
if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0)
throw SnapException("This Ogg bitstream does not contain Vorbis audio data");
int i(0);
while (i < 2)
{
while (i < 2)
{
int result=ogg_sync_pageout(&oy, &og);
if (result == 0)
break; /* Need more data */
/* Don't complain about missing or corrupt data yet. We'll
catch it at the packet output phase */
if (result == 1)
{
ogg_stream_pagein(&os, &og); /* we can ignore any errors here as they'll also become apparent at packetout */
while (i < 2)
{
result=ogg_stream_packetout(&os, &op);
if (result == 0)
break;
/// Uh oh; data at some point was corrupted or missing!
/// We can't tolerate that in a header. Die. */
if (result < 0)
throw SnapException("Corrupt secondary header. Exiting.");
int i(0);
while (i < 2)
{
while (i < 2)
{
int result = ogg_sync_pageout(&oy, &og);
if (result == 0)
break; /* Need more data */
/* Don't complain about missing or corrupt data yet. We'll
catch it at the packet output phase */
if (result == 1)
{
ogg_stream_pagein(&os, &og); /* we can ignore any errors here as they'll also become apparent at packetout */
while (i < 2)
{
result = ogg_stream_packetout(&os, &op);
if (result == 0)
break;
/// Uh oh; data at some point was corrupted or missing!
/// We can't tolerate that in a header. Die. */
if (result < 0)
throw SnapException("Corrupt secondary header. Exiting.");
result=vorbis_synthesis_headerin(&vi, &vc, &op);
if (result < 0)
throw SnapException("Corrupt secondary header. Exiting.");
result = vorbis_synthesis_headerin(&vi, &vc, &op);
if (result < 0)
throw SnapException("Corrupt secondary header. Exiting.");
i++;
}
}
}
}
i++;
}
}
}
}
/// OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder.
if (vorbis_synthesis_init(&vd, &vi) == 0)
vorbis_block_init(&vd, &vb);
/// central decode state
/// local state for most of the decode so multiple block decodes can proceed
/// in parallel. We could init multiple vorbis_block structures for vd here
/// OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder.
if (vorbis_synthesis_init(&vd, &vi) == 0)
vorbis_block_init(&vd, &vb);
/// central decode state
/// local state for most of the decode so multiple block decodes can proceed
/// in parallel. We could init multiple vorbis_block structures for vd here
sampleFormat_.setFormat(vi.rate, 16, vi.channels);
sampleFormat_.setFormat(vi.rate, 16, vi.channels);
/* Throw the comments plus a few lines about the bitstream we're decoding */
char **ptr=vc.user_comments;
while (*ptr)
{
std::string comment(*ptr);
if (comment.find("SAMPLE_FORMAT=") == 0)
sampleFormat_.setFormat(comment.substr(comment.find("=") + 1));
LOG(INFO) << "comment: " << comment << "\n";;
++ptr;
}
/* Throw the comments plus a few lines about the bitstream we're decoding */
char** ptr = vc.user_comments;
while (*ptr)
{
std::string comment(*ptr);
if (comment.find("SAMPLE_FORMAT=") == 0)
sampleFormat_.setFormat(comment.substr(comment.find("=") + 1));
LOG(INFO) << "comment: " << comment << "\n";
;
++ptr;
}
LOG(INFO) << "Encoded by: " << vc.vendor << "\n";
LOG(INFO) << "Encoded by: " << vc.vendor << "\n";
return sampleFormat_;
return sampleFormat_;
}

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,35 +29,35 @@
class OggDecoder : public Decoder
{
public:
OggDecoder();
virtual ~OggDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
OggDecoder();
virtual ~OggDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
private:
bool decodePayload(msg::PcmChunk* chunk);
template <typename T>
T clip(const T& value, const T& lower, const T& upper) const
{
if (value > upper) return upper;
if (value < lower) return lower;
return value;
}
bool decodePayload(msg::PcmChunk* chunk);
template <typename T>
T clip(const T& value, const T& lower, const T& upper) const
{
if (value > upper)
return upper;
if (value < lower)
return lower;
return value;
}
ogg_sync_state oy; /// sync and verify incoming physical bitstream
ogg_stream_state os; /// take physical pages, weld into a logical stream of packets
ogg_page og; /// one Ogg bitstream page. Vorbis packets are inside
ogg_packet op; /// one raw packet of data for decode
ogg_sync_state oy; /// sync and verify incoming physical bitstream
ogg_stream_state os; /// take physical pages, weld into a logical stream of packets
ogg_page og; /// one Ogg bitstream page. Vorbis packets are inside
ogg_packet op; /// one raw packet of data for decode
vorbis_info vi; /// struct that stores all the static vorbis bitstream settings
vorbis_comment vc; /// struct that stores all the bitstream user comments
vorbis_dsp_state vd; /// central working state for the packet->PCM decoder
vorbis_block vb; /// local working space for packet->PCM decode
vorbis_info vi; /// struct that stores all the static vorbis bitstream settings
vorbis_comment vc; /// struct that stores all the bitstream user comments
vorbis_dsp_state vd; /// central working state for the packet->PCM decoder
vorbis_block vb; /// local working space for packet->PCM decode
SampleFormat sampleFormat_;
SampleFormat sampleFormat_;
};
#endif

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,40 +16,40 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "common/snapException.h"
#include "common/endian.hpp"
#include "aixlog.hpp"
#include "pcmDecoder.h"
#include "aixlog.hpp"
#include "common/endian.hpp"
#include "common/snapException.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
struct riff_wave_header
{
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
};
struct chunk_header
{
uint32_t id;
uint32_t sz;
uint32_t id;
uint32_t sz;
};
struct chunk_fmt
{
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};
@ -60,69 +60,61 @@ PcmDecoder::PcmDecoder() : Decoder()
bool PcmDecoder::decode(msg::PcmChunk* chunk)
{
return true;
return true;
}
SampleFormat PcmDecoder::setHeader(msg::CodecHeader* chunk)
{
if (chunk->payloadSize < 44)
throw SnapException("PCM header too small");
if (chunk->payloadSize < 44)
throw SnapException("PCM header too small");
struct riff_wave_header riff_wave_header;
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
chunk_fmt.sample_rate = SWAP_32(0);
chunk_fmt.bits_per_sample = SWAP_16(0);
chunk_fmt.num_channels = SWAP_16(0);
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
chunk_fmt.sample_rate = SWAP_32(0);
chunk_fmt.bits_per_sample = SWAP_16(0);
chunk_fmt.num_channels = SWAP_16(0);
size_t pos(0);
memcpy(&riff_wave_header, chunk->payload + pos, sizeof(riff_wave_header));
pos += sizeof(riff_wave_header);
if ((SWAP_32(riff_wave_header.riff_id) != ID_RIFF) || (SWAP_32(riff_wave_header.wave_id) != ID_WAVE))
throw SnapException("Not a riff/wave header");
size_t pos(0);
memcpy(&riff_wave_header, chunk->payload + pos, sizeof(riff_wave_header));
pos += sizeof(riff_wave_header);
if ((SWAP_32(riff_wave_header.riff_id) != ID_RIFF) || (SWAP_32(riff_wave_header.wave_id) != ID_WAVE))
throw SnapException("Not a riff/wave header");
bool moreChunks(true);
do
{
if (pos + sizeof(chunk_header) > chunk->payloadSize)
throw SnapException("riff/wave header incomplete");
memcpy(&chunk_header, chunk->payload + pos, sizeof(chunk_header));
pos += sizeof(chunk_header);
switch (SWAP_32(chunk_header.id))
{
case ID_FMT:
if (pos + sizeof(chunk_fmt) > chunk->payloadSize)
throw SnapException("riff/wave header incomplete");
memcpy(&chunk_fmt, chunk->payload + pos, sizeof(chunk_fmt));
pos += sizeof(chunk_fmt);
/// If the format header is larger, skip the rest
if (SWAP_32(chunk_header.sz) > sizeof(chunk_fmt))
pos += (SWAP_32(chunk_header.sz) - sizeof(chunk_fmt));
break;
case ID_DATA:
/// Stop looking for chunks
moreChunks = false;
break;
default:
/// Unknown chunk, skip bytes
pos += SWAP_32(chunk_header.sz);
}
}
while (moreChunks);
bool moreChunks(true);
do
{
if (pos + sizeof(chunk_header) > chunk->payloadSize)
throw SnapException("riff/wave header incomplete");
memcpy(&chunk_header, chunk->payload + pos, sizeof(chunk_header));
pos += sizeof(chunk_header);
switch (SWAP_32(chunk_header.id))
{
case ID_FMT:
if (pos + sizeof(chunk_fmt) > chunk->payloadSize)
throw SnapException("riff/wave header incomplete");
memcpy(&chunk_fmt, chunk->payload + pos, sizeof(chunk_fmt));
pos += sizeof(chunk_fmt);
/// If the format header is larger, skip the rest
if (SWAP_32(chunk_header.sz) > sizeof(chunk_fmt))
pos += (SWAP_32(chunk_header.sz) - sizeof(chunk_fmt));
break;
case ID_DATA:
/// Stop looking for chunks
moreChunks = false;
break;
default:
/// Unknown chunk, skip bytes
pos += SWAP_32(chunk_header.sz);
}
} while (moreChunks);
if (SWAP_32(chunk_fmt.sample_rate) == 0)
throw SnapException("Sample format not found");
if (SWAP_32(chunk_fmt.sample_rate) == 0)
throw SnapException("Sample format not found");
SampleFormat sampleFormat(
SWAP_32(chunk_fmt.sample_rate),
SWAP_16(chunk_fmt.bits_per_sample),
SWAP_16(chunk_fmt.num_channels));
SampleFormat sampleFormat(SWAP_32(chunk_fmt.sample_rate), SWAP_16(chunk_fmt.bits_per_sample), SWAP_16(chunk_fmt.num_channels));
return sampleFormat;
return sampleFormat;
}

View file

@ -1,6 +1,6 @@
/***
This file is part of snapcast
Copyright (C) 2014-2018 Johannes Pohl
Copyright (C) 2014-2019 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -24,12 +24,10 @@
class PcmDecoder : public Decoder
{
public:
PcmDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
PcmDecoder();
virtual bool decode(msg::PcmChunk* chunk);
virtual SampleFormat setHeader(msg::CodecHeader* chunk);
};
#endif