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