1 /***
2 This file is part of snapcast
3 Copyright (C) 2014-2021 Johannes Pohl
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 ***/
18
19 #include "pcm_decoder.hpp"
20 #include "common/aixlog.hpp"
21 #include "common/endian.hpp"
22 #include "common/snap_exception.hpp"
23
24 namespace decoder
25 {
26
27 static constexpr auto ID_RIFF = 0x46464952;
28 static constexpr auto ID_WAVE = 0x45564157;
29 static constexpr auto ID_FMT = 0x20746d66;
30 static constexpr auto ID_DATA = 0x61746164;
31
32 struct riff_wave_header
33 {
34 uint32_t riff_id;
35 uint32_t riff_sz;
36 uint32_t wave_id;
37 };
38
39
40 struct chunk_header
41 {
42 uint32_t id;
43 uint32_t sz;
44 };
45
46
47 struct chunk_fmt
48 {
49 uint16_t audio_format;
50 uint16_t num_channels;
51 uint32_t sample_rate;
52 uint32_t byte_rate;
53 uint16_t block_align;
54 uint16_t bits_per_sample;
55 };
56
57
PcmDecoder()58 PcmDecoder::PcmDecoder() : Decoder()
59 {
60 }
61
62
decode(msg::PcmChunk *)63 bool PcmDecoder::decode(msg::PcmChunk* /*chunk*/)
64 {
65 return true;
66 }
67
68
setHeader(msg::CodecHeader * chunk)69 SampleFormat PcmDecoder::setHeader(msg::CodecHeader* chunk)
70 {
71 if (chunk->payloadSize < 44)
72 throw SnapException("PCM header too small");
73
74 struct riff_wave_header riff_wave_header;
75 struct chunk_header chunk_header;
76 struct chunk_fmt chunk_fmt;
77 chunk_fmt.sample_rate = SWAP_32(0);
78 chunk_fmt.bits_per_sample = SWAP_16(0);
79 chunk_fmt.num_channels = SWAP_16(0);
80
81 size_t pos(0);
82 memcpy(&riff_wave_header, chunk->payload + pos, sizeof(riff_wave_header));
83 pos += sizeof(riff_wave_header);
84 if ((SWAP_32(riff_wave_header.riff_id) != ID_RIFF) || (SWAP_32(riff_wave_header.wave_id) != ID_WAVE))
85 throw SnapException("Not a riff/wave header");
86
87 bool moreChunks(true);
88 do
89 {
90 if (pos + sizeof(chunk_header) > chunk->payloadSize)
91 throw SnapException("riff/wave header incomplete");
92 memcpy(&chunk_header, chunk->payload + pos, sizeof(chunk_header));
93 pos += sizeof(chunk_header);
94 switch (SWAP_32(chunk_header.id))
95 {
96 case ID_FMT:
97 if (pos + sizeof(chunk_fmt) > chunk->payloadSize)
98 throw SnapException("riff/wave header incomplete");
99 memcpy(&chunk_fmt, chunk->payload + pos, sizeof(chunk_fmt));
100 pos += sizeof(chunk_fmt);
101 /// If the format header is larger, skip the rest
102 if (SWAP_32(chunk_header.sz) > sizeof(chunk_fmt))
103 pos += (SWAP_32(chunk_header.sz) - sizeof(chunk_fmt));
104 break;
105 case ID_DATA:
106 /// Stop looking for chunks
107 moreChunks = false;
108 break;
109 default:
110 /// Unknown chunk, skip bytes
111 pos += SWAP_32(chunk_header.sz);
112 }
113 } while (moreChunks);
114
115
116 if (SWAP_32(chunk_fmt.sample_rate) == 0)
117 throw SnapException("Sample format not found");
118
119 SampleFormat sampleFormat(SWAP_32(chunk_fmt.sample_rate), SWAP_16(chunk_fmt.bits_per_sample), SWAP_16(chunk_fmt.num_channels));
120
121 return sampleFormat;
122 }
123
124 } // namespace decoder
125