1 /*
2 * Copyright 2003-2021 The Music Player Daemon Project
3 * http://www.musicpd.org
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 2 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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "Order.hxx"
21 #include "Buffer.hxx"
22 #include "util/ConstBuffer.hxx"
23
24
25 /*
26 * According to:
27 * - https://xiph.org/flac/format.html#frame_header
28 * - https://github.com/nu774/qaac/wiki/Multichannel--handling
29 * the source channel order (after decoding, e.g., flac, alac) is for
30 * - 1ch: mono
31 * - 2ch: left, right
32 * - 3ch: left, right, center
33 * - 4ch: front left, front right, back left, back right
34 * - 5ch: front left, front right, front center, back/surround left, back/surround right
35 * - 6ch (aka 5.1): front left, front right, front center, LFE, back/surround left, back/surround right
36 * - 7ch: front left, front right, front center, LFE, back center, side left, side right
37 * - 8ch: (aka 7.1): front left, front right, front center, LFE, back left, back right, side left, side right
38 *
39 * The ALSA default channel map is (see /usr/share/alsa/pcm/surround71.conf):
40 * - front left, front right, back left, back right, front center, LFE, side left, side right
41 *
42 * Hence, in case of the following source channel orders 3ch, 5ch, 6ch (aka
43 * 5.1), 7ch and 8ch the channel order has to be adapted
44 */
45
46 template<typename V>
47 struct TwoPointers {
48 V *dest;
49 const V *src;
50
CopyOneTwoPointers51 TwoPointers<V> &CopyOne() noexcept {
52 *dest++ = *src++;
53 return *this;
54 }
55
CopyTwoTwoPointers56 TwoPointers<V> &CopyTwo() noexcept {
57 return CopyOne().CopyOne();
58 }
59
SwapTwoPairsTwoPointers60 TwoPointers<V> &SwapTwoPairs() noexcept {
61 *dest++ = src[2];
62 *dest++ = src[3];
63 *dest++ = src[0];
64 *dest++ = src[1];
65 src += 4;
66 return *this;
67 }
68
ToAlsa50TwoPointers69 TwoPointers<V> &ToAlsa50() noexcept {
70 *dest++ = src[0]; // front left
71 *dest++ = src[1]; // front right
72 *dest++ = src[3]; // surround left
73 *dest++ = src[4]; // surround right
74 *dest++ = src[2]; // front center
75 src += 5;
76 return *this;
77 }
78
ToAlsa51TwoPointers79 TwoPointers<V> &ToAlsa51() noexcept {
80 return CopyTwo() // left+right
81 .SwapTwoPairs(); // center, LFE, surround left+right
82 }
83
ToAlsa70TwoPointers84 TwoPointers<V> &ToAlsa70() noexcept {
85 *dest++ = src[0]; // front left
86 *dest++ = src[1]; // front right
87 *dest++ = src[5]; // side left
88 *dest++ = src[6]; // side right
89 *dest++ = src[2]; // front center
90 *dest++ = src[3]; // LFE
91 *dest++ = src[4]; // back center
92 src += 7;
93 return *this;
94 }
95
ToAlsa71TwoPointers96 TwoPointers<V> &ToAlsa71() noexcept {
97 return ToAlsa51()
98 .CopyTwo(); // side left+right
99 }
100 };
101
102 template<typename V>
103 static void
ToAlsaChannelOrder50(V * dest,const V * src,size_t n)104 ToAlsaChannelOrder50(V *dest, const V *src, size_t n) noexcept
105 {
106 TwoPointers<V> p{dest, src};
107 for (size_t i = 0; i != n; ++i)
108 p.ToAlsa50();
109 }
110
111 template<typename V>
112 static inline ConstBuffer<V>
ToAlsaChannelOrder50(PcmBuffer & buffer,ConstBuffer<V> src)113 ToAlsaChannelOrder50(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
114 {
115 auto dest = buffer.GetT<V>(src.size);
116 ToAlsaChannelOrder50(dest, src.data, src.size / 5);
117 return { dest, src.size };
118 }
119
120 template<typename V>
121 static void
ToAlsaChannelOrder51(V * dest,const V * src,size_t n)122 ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept
123 {
124 TwoPointers<V> p{dest, src};
125 for (size_t i = 0; i != n; ++i)
126 p.ToAlsa51();
127 }
128
129 template<typename V>
130 static inline ConstBuffer<V>
ToAlsaChannelOrder51(PcmBuffer & buffer,ConstBuffer<V> src)131 ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
132 {
133 auto dest = buffer.GetT<V>(src.size);
134 ToAlsaChannelOrder51(dest, src.data, src.size / 6);
135 return { dest, src.size };
136 }
137
138 template<typename V>
139 static void
ToAlsaChannelOrder70(V * dest,const V * src,size_t n)140 ToAlsaChannelOrder70(V *dest, const V *src, size_t n) noexcept
141 {
142 TwoPointers<V> p{dest, src};
143 for (size_t i = 0; i != n; ++i)
144 p.ToAlsa70();
145 }
146
147 template<typename V>
148 static inline ConstBuffer<V>
ToAlsaChannelOrder70(PcmBuffer & buffer,ConstBuffer<V> src)149 ToAlsaChannelOrder70(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
150 {
151 auto dest = buffer.GetT<V>(src.size);
152 ToAlsaChannelOrder70(dest, src.data, src.size / 7);
153 return { dest, src.size };
154 }
155
156 template<typename V>
157 static void
ToAlsaChannelOrder71(V * dest,const V * src,size_t n)158 ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept
159 {
160 TwoPointers<V> p{dest, src};
161 for (size_t i = 0; i != n; ++i)
162 p.ToAlsa71();
163 }
164
165 template<typename V>
166 static inline ConstBuffer<V>
ToAlsaChannelOrder71(PcmBuffer & buffer,ConstBuffer<V> src)167 ToAlsaChannelOrder71(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
168 {
169 auto dest = buffer.GetT<V>(src.size);
170 ToAlsaChannelOrder71(dest, src.data, src.size / 8);
171 return { dest, src.size };
172 }
173
174 template<typename V>
175 static ConstBuffer<V>
ToAlsaChannelOrderT(PcmBuffer & buffer,ConstBuffer<V> src,unsigned channels)176 ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer<V> src,
177 unsigned channels) noexcept
178 {
179 switch (channels) {
180 case 5: // 5.0
181 return ToAlsaChannelOrder50(buffer, src);
182
183 case 6: // 5.1
184 return ToAlsaChannelOrder51(buffer, src);
185
186 case 7: // 7.0
187 return ToAlsaChannelOrder70(buffer, src);
188
189 case 8: // 7.1
190 return ToAlsaChannelOrder71(buffer, src);
191
192 default:
193 return src;
194 }
195 }
196
197 ConstBuffer<void>
ToAlsaChannelOrder(PcmBuffer & buffer,ConstBuffer<void> src,SampleFormat sample_format,unsigned channels)198 ToAlsaChannelOrder(PcmBuffer &buffer, ConstBuffer<void> src,
199 SampleFormat sample_format, unsigned channels) noexcept
200 {
201 switch (sample_format) {
202 case SampleFormat::UNDEFINED:
203 case SampleFormat::S8:
204 case SampleFormat::DSD:
205 return src;
206
207 case SampleFormat::S16:
208 return ToAlsaChannelOrderT(buffer,
209 ConstBuffer<int16_t>::FromVoid(src),
210 channels).ToVoid();
211
212 case SampleFormat::S24_P32:
213 case SampleFormat::S32:
214 case SampleFormat::FLOAT:
215 return ToAlsaChannelOrderT(buffer,
216 ConstBuffer<int32_t>::FromVoid(src),
217 channels).ToVoid();
218 }
219
220 gcc_unreachable();
221 }
222