1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
/***
This file is part of snapcast
Copyright (C) 2014-2025 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;
/// RIFF wave header
/// See https://en.wikipedia.org/wiki/WAV
struct riff_wave_header
{
uint32_t riff_id; ///< "RIFF"
uint32_t riff_sz; ///< file size - 8
uint32_t wave_id; ///< "WAVE"
};
/// Chunk header
/// See https://en.wikipedia.org/wiki/WAV
struct chunk_header
{
uint32_t id; ///< id
uint32_t sz; ///< size
};
/// Chunk format
/// See https://en.wikipedia.org/wiki/WAV
struct chunk_fmt
{
uint16_t audio_format; ///< format
uint16_t num_channels; ///< channels
uint32_t sample_rate; ///< sample rate
uint32_t byte_rate; ///< byte rate
uint16_t block_align; ///< block align
uint16_t bits_per_sample; ///< bps
};
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
|