mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-29 18:27:12 +02:00
127 lines
3.6 KiB
C++
127 lines
3.6 KiB
C++
/***
|
|
This file is part of snapcast
|
|
Copyright (C) 2014-2024 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
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
***/
|
|
|
|
// prototype/interface header file
|
|
#include "pcm_decoder.hpp"
|
|
|
|
// local headers
|
|
#include "common/endian.hpp"
|
|
#include "common/snap_exception.hpp"
|
|
|
|
|
|
namespace decoder
|
|
{
|
|
|
|
static constexpr auto ID_RIFF = 0x46464952;
|
|
static constexpr auto ID_WAVE = 0x45564157;
|
|
static constexpr auto ID_FMT = 0x20746d66;
|
|
static constexpr auto ID_DATA = 0x61746164;
|
|
|
|
struct riff_wave_header
|
|
{
|
|
uint32_t riff_id;
|
|
uint32_t riff_sz;
|
|
uint32_t wave_id;
|
|
};
|
|
|
|
|
|
struct chunk_header
|
|
{
|
|
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;
|
|
};
|
|
|
|
|
|
PcmDecoder::PcmDecoder() : Decoder()
|
|
{
|
|
}
|
|
|
|
|
|
bool PcmDecoder::decode(msg::PcmChunk* /*chunk*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
SampleFormat PcmDecoder::setHeader(msg::CodecHeader* chunk)
|
|
{
|
|
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);
|
|
|
|
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);
|
|
|
|
|
|
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));
|
|
|
|
return sampleFormat;
|
|
}
|
|
|
|
} // namespace decoder
|