|
1
|
1 #include "foobar2000-sdk-pch.h"
|
|
|
2 #include "audio_chunk.h"
|
|
|
3
|
|
|
4 #ifdef _WIN32
|
|
|
5 #include <ks.h>
|
|
|
6 #include <ksmedia.h>
|
|
|
7
|
|
|
8 #if 0
|
|
|
9 #define SPEAKER_FRONT_LEFT 0x1
|
|
|
10 #define SPEAKER_FRONT_RIGHT 0x2
|
|
|
11 #define SPEAKER_FRONT_CENTER 0x4
|
|
|
12 #define SPEAKER_LOW_FREQUENCY 0x8
|
|
|
13 #define SPEAKER_BACK_LEFT 0x10
|
|
|
14 #define SPEAKER_BACK_RIGHT 0x20
|
|
|
15 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
|
|
|
16 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
|
|
|
17 #define SPEAKER_BACK_CENTER 0x100
|
|
|
18 #define SPEAKER_SIDE_LEFT 0x200
|
|
|
19 #define SPEAKER_SIDE_RIGHT 0x400
|
|
|
20 #define SPEAKER_TOP_CENTER 0x800
|
|
|
21 #define SPEAKER_TOP_FRONT_LEFT 0x1000
|
|
|
22 #define SPEAKER_TOP_FRONT_CENTER 0x2000
|
|
|
23 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
|
|
|
24 #define SPEAKER_TOP_BACK_LEFT 0x8000
|
|
|
25 #define SPEAKER_TOP_BACK_CENTER 0x10000
|
|
|
26 #define SPEAKER_TOP_BACK_RIGHT 0x20000
|
|
|
27
|
|
|
28 static struct {DWORD m_wfx; unsigned m_native; } const g_translation_table[] =
|
|
|
29 {
|
|
|
30 {SPEAKER_FRONT_LEFT, audio_chunk::channel_front_left},
|
|
|
31 {SPEAKER_FRONT_RIGHT, audio_chunk::channel_front_right},
|
|
|
32 {SPEAKER_FRONT_CENTER, audio_chunk::channel_front_center},
|
|
|
33 {SPEAKER_LOW_FREQUENCY, audio_chunk::channel_lfe},
|
|
|
34 {SPEAKER_BACK_LEFT, audio_chunk::channel_back_left},
|
|
|
35 {SPEAKER_BACK_RIGHT, audio_chunk::channel_back_right},
|
|
|
36 {SPEAKER_FRONT_LEFT_OF_CENTER, audio_chunk::channel_front_center_left},
|
|
|
37 {SPEAKER_FRONT_RIGHT_OF_CENTER, audio_chunk::channel_front_center_right},
|
|
|
38 {SPEAKER_BACK_CENTER, audio_chunk::channel_back_center},
|
|
|
39 {SPEAKER_SIDE_LEFT, audio_chunk::channel_side_left},
|
|
|
40 {SPEAKER_SIDE_RIGHT, audio_chunk::channel_side_right},
|
|
|
41 {SPEAKER_TOP_CENTER, audio_chunk::channel_top_center},
|
|
|
42 {SPEAKER_TOP_FRONT_LEFT, audio_chunk::channel_top_front_left},
|
|
|
43 {SPEAKER_TOP_FRONT_CENTER, audio_chunk::channel_top_front_center},
|
|
|
44 {SPEAKER_TOP_FRONT_RIGHT, audio_chunk::channel_top_front_right},
|
|
|
45 {SPEAKER_TOP_BACK_LEFT, audio_chunk::channel_top_back_left},
|
|
|
46 {SPEAKER_TOP_BACK_CENTER, audio_chunk::channel_top_back_center},
|
|
|
47 {SPEAKER_TOP_BACK_RIGHT, audio_chunk::channel_top_back_right},
|
|
|
48 };
|
|
|
49
|
|
|
50 #endif
|
|
|
51 #endif
|
|
|
52
|
|
|
53
|
|
|
54
|
|
|
55 static constexpr unsigned g_audio_channel_config_table[] =
|
|
|
56 {
|
|
|
57 0,
|
|
|
58 audio_chunk::channel_config_mono,
|
|
|
59 audio_chunk::channel_config_stereo,
|
|
|
60 audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_lfe,
|
|
|
61 audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right,
|
|
|
62 audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_lfe,
|
|
|
63 audio_chunk::channel_config_5point1,
|
|
|
64 audio_chunk::channel_config_5point1_side | audio_chunk::channel_back_center,
|
|
|
65 audio_chunk::channel_config_7point1,
|
|
|
66 0,
|
|
|
67 audio_chunk::channel_config_7point1 | audio_chunk::channel_front_center_right | audio_chunk::channel_front_center_left,
|
|
|
68 };
|
|
|
69
|
|
|
70 unsigned audio_chunk::g_guess_channel_config(unsigned count)
|
|
|
71 {
|
|
|
72 if (count == 0) return 0;
|
|
|
73 if (count > 32) throw exception_io_data();
|
|
|
74 unsigned ret = 0;
|
|
|
75 if (count < PFC_TABSIZE(g_audio_channel_config_table)) ret = g_audio_channel_config_table[count];
|
|
|
76 if (ret == 0) {
|
|
|
77 // Warning: 1u<<32u behaves stupidly
|
|
|
78 ret = (unsigned)( (1ull << count) - 1 );
|
|
|
79 }
|
|
|
80 PFC_ASSERT(g_count_channels(ret) == count);
|
|
|
81 return ret;
|
|
|
82 }
|
|
|
83
|
|
|
84 unsigned audio_chunk::g_guess_channel_config_xiph(unsigned count) {
|
|
|
85 switch (count) {
|
|
|
86 case 3:
|
|
|
87 return audio_chunk::channel_front_left | audio_chunk::channel_front_center | audio_chunk::channel_front_right;
|
|
|
88 case 5:
|
|
|
89 return audio_chunk::channel_front_left | audio_chunk::channel_front_center | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right;
|
|
|
90 case 7:
|
|
|
91 return audio_chunk::channel_config_5point1 | audio_chunk::channel_back_center;
|
|
|
92 default:
|
|
|
93 return g_guess_channel_config(count);
|
|
|
94 }
|
|
|
95 }
|
|
|
96
|
|
|
97 unsigned audio_chunk::g_channel_index_from_flag(unsigned p_config,unsigned p_flag) {
|
|
|
98 if (p_config & p_flag) {
|
|
|
99 unsigned index = 0;
|
|
|
100
|
|
|
101 for (unsigned walk = 0; walk < 32; walk++) {
|
|
|
102 unsigned query = 1 << walk;
|
|
|
103 if (p_flag & query) return index;
|
|
|
104 if (p_config & query) index++;
|
|
|
105 }
|
|
|
106 }
|
|
|
107 return (unsigned)(-1);
|
|
|
108 }
|
|
|
109
|
|
|
110 unsigned audio_chunk::g_extract_channel_flag(unsigned p_config,unsigned p_index)
|
|
|
111 {
|
|
|
112 unsigned toskip = p_index;
|
|
|
113 unsigned flag = 1;
|
|
|
114 while(flag)
|
|
|
115 {
|
|
|
116 if (p_config & flag)
|
|
|
117 {
|
|
|
118 if (toskip == 0) break;
|
|
|
119 toskip--;
|
|
|
120 }
|
|
|
121 flag <<= 1;
|
|
|
122 }
|
|
|
123 return flag;
|
|
|
124 }
|
|
|
125
|
|
|
126
|
|
|
127 static const char * const chanNames[] = {
|
|
|
128 "FL", //channel_front_left = 1<<0,
|
|
|
129 "FR", //channel_front_right = 1<<1,
|
|
|
130 "FC", //channel_front_center = 1<<2,
|
|
|
131 "LFE", //channel_lfe = 1<<3,
|
|
|
132 "BL", //channel_back_left = 1<<4,
|
|
|
133 "BR", //channel_back_right = 1<<5,
|
|
|
134 "FCL", //channel_front_center_left = 1<<6,
|
|
|
135 "FCR", //channel_front_center_right = 1<<7,
|
|
|
136 "BC", //channel_back_center = 1<<8,
|
|
|
137 "SL", //channel_side_left = 1<<9,
|
|
|
138 "SR", //channel_side_right = 1<<10,
|
|
|
139 "TC", //channel_top_center = 1<<11,
|
|
|
140 "TFL", //channel_top_front_left = 1<<12,
|
|
|
141 "TFC", //channel_top_front_center = 1<<13,
|
|
|
142 "TFR", //channel_top_front_right = 1<<14,
|
|
|
143 "TBL", //channel_top_back_left = 1<<15,
|
|
|
144 "TBC", //channel_top_back_center = 1<<16,
|
|
|
145 "TBR", //channel_top_back_right = 1<<17,
|
|
|
146 };
|
|
|
147
|
|
|
148 unsigned audio_chunk::g_find_channel_idx(unsigned p_flag) {
|
|
|
149 unsigned rv = 0;
|
|
|
150 if ((p_flag & 0xFFFF) == 0) {
|
|
|
151 rv += 16; p_flag >>= 16;
|
|
|
152 }
|
|
|
153 if ((p_flag & 0xFF) == 0) {
|
|
|
154 rv += 8; p_flag >>= 8;
|
|
|
155 }
|
|
|
156 if ((p_flag & 0xF) == 0) {
|
|
|
157 rv += 4; p_flag >>= 4;
|
|
|
158 }
|
|
|
159 if ((p_flag & 0x3) == 0) {
|
|
|
160 rv += 2; p_flag >>= 2;
|
|
|
161 }
|
|
|
162 if ((p_flag & 0x1) == 0) {
|
|
|
163 rv += 1; p_flag >>= 1;
|
|
|
164 }
|
|
|
165 PFC_ASSERT( p_flag & 1 );
|
|
|
166 return rv;
|
|
|
167 }
|
|
|
168
|
|
|
169 const char * audio_chunk::g_channel_name(unsigned p_flag) {
|
|
|
170 return g_channel_name_byidx(g_find_channel_idx(p_flag));
|
|
|
171 }
|
|
|
172
|
|
|
173 const char * audio_chunk::g_channel_name_byidx(unsigned p_index) {
|
|
|
174 if (p_index < PFC_TABSIZE(chanNames)) return chanNames[p_index];
|
|
|
175 else return "?";
|
|
|
176 }
|
|
|
177
|
|
|
178 pfc::string8 audio_chunk::g_formatChannelMaskDesc(unsigned flags) {
|
|
|
179 pfc::string8 temp; g_formatChannelMaskDesc(flags, temp); return temp;
|
|
|
180 }
|
|
|
181 void audio_chunk::g_formatChannelMaskDesc(unsigned flags, pfc::string_base & out) {
|
|
|
182 out.reset();
|
|
|
183 unsigned idx = 0;
|
|
|
184 while(flags) {
|
|
|
185 if (flags & 1) {
|
|
|
186 if (!out.is_empty()) out << " ";
|
|
|
187 out << g_channel_name_byidx(idx);
|
|
|
188 }
|
|
|
189 flags >>= 1;
|
|
|
190 ++idx;
|
|
|
191 }
|
|
|
192 }
|
|
|
193
|
|
|
194 namespace {
|
|
|
195 struct maskDesc_t {
|
|
|
196 const char* name;
|
|
|
197 unsigned mask;
|
|
|
198 };
|
|
|
199 static constexpr maskDesc_t maskDesc[] = {
|
|
|
200 {"mono", audio_chunk::channel_config_mono},
|
|
|
201 {"stereo", audio_chunk::channel_config_stereo},
|
|
|
202 {"stereo (rear)", audio_chunk::channel_back_left | audio_chunk::channel_back_right},
|
|
|
203 {"stereo (side)", audio_chunk::channel_side_left | audio_chunk::channel_side_right},
|
|
|
204 {"2.1", audio_chunk::channel_config_2point1},
|
|
|
205 {"3.0", audio_chunk::channel_config_3point0},
|
|
|
206 {"4.0", audio_chunk::channel_config_4point0},
|
|
|
207 {"4.1", audio_chunk::channel_config_4point1},
|
|
|
208 {"5.0", audio_chunk::channel_config_5point0},
|
|
|
209 {"5.1", audio_chunk::channel_config_5point1},
|
|
|
210 {"5.1 (side)", audio_chunk::channel_config_5point1_side},
|
|
|
211 {"6.1", audio_chunk::channel_config_5point1 | audio_chunk::channel_back_center},
|
|
|
212 {"6.1 (side)", audio_chunk::channel_config_5point1_side | audio_chunk::channel_back_center},
|
|
|
213 {"7.1", audio_chunk::channel_config_7point1},
|
|
|
214 };
|
|
|
215 }
|
|
|
216
|
|
|
217 const char* audio_chunk::g_channelMaskName(unsigned flags) {
|
|
|
218 for (auto& walk : maskDesc) {
|
|
|
219 if (flags == walk.mask) return walk.name;
|
|
|
220 }
|
|
|
221 return nullptr;
|
|
|
222 }
|
|
|
223
|
|
|
224 static_assert( pfc::countBits32(audio_chunk::channel_config_mono) == 1 );
|
|
|
225 static_assert( pfc::countBits32(audio_chunk::channel_config_stereo) == 2 );
|
|
|
226 static_assert( pfc::countBits32(audio_chunk::channel_config_4point0) == 4 );
|
|
|
227 static_assert( pfc::countBits32(audio_chunk::channel_config_4point0_side) == 4 );
|
|
|
228 static_assert( pfc::countBits32(audio_chunk::channel_config_4point1) == 5 );
|
|
|
229 static_assert( pfc::countBits32(audio_chunk::channel_config_5point0) == 5 );
|
|
|
230 static_assert( pfc::countBits32(audio_chunk::channel_config_5point1) == 6 );
|
|
|
231 static_assert( pfc::countBits32(audio_chunk::channel_config_5point1_side) == 6 );
|
|
|
232 static_assert( pfc::countBits32(audio_chunk::channel_config_6point0) == 6);
|
|
|
233 static_assert( pfc::countBits32(audio_chunk::channel_config_7point1) == 8 );
|