mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-30 09:26:15 +02:00
add clang-format file
reformat code
This commit is contained in:
parent
b733f646ea
commit
b20add3815
105 changed files with 7773 additions and 7723 deletions
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue