diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-24 01:25:21 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-03-18 12:00:39 +0100 |
commit | b5716df4cb2837bbbc42195cf1aefcf03e21d6a6 (patch) | |
tree | 130cd712e2e00893b6df9959a375a8d9523a1aca /lib/rbcodec | |
parent | 24bd9d5393dbe39a5c6194877bc00ede669b1d5d (diff) | |
download | rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.tar.gz rockbox-b5716df4cb2837bbbc42195cf1aefcf03e21d6a6.zip |
Build librbcodec with DSP and metadata.
All associated files are moved to /lib/rbcodec.
Change-Id: I572ddd2b8a996aae1e98c081d06b1ed356dce222
Diffstat (limited to 'lib/rbcodec')
72 files changed, 15257 insertions, 43 deletions
diff --git a/lib/rbcodec/SOURCES b/lib/rbcodec/SOURCES new file mode 100644 index 0000000000..3ac2660a38 --- /dev/null +++ b/lib/rbcodec/SOURCES | |||
@@ -0,0 +1,54 @@ | |||
1 | metadata/metadata.c | ||
2 | metadata/id3tags.c | ||
3 | metadata/mp3.c | ||
4 | metadata/mp3data.c | ||
5 | #if CONFIG_CODEC == SWCODEC | ||
6 | dsp/compressor.c | ||
7 | dsp/dsp.c | ||
8 | dsp/eq.c | ||
9 | # if defined(CPU_COLDFIRE) | ||
10 | dsp/dsp_cf.S | ||
11 | dsp/eq_cf.S | ||
12 | # elif defined(CPU_ARM) | ||
13 | dsp/dsp_arm.S | ||
14 | dsp/eq_arm.S | ||
15 | # if ARM_ARCH >= 6 | ||
16 | dsp/dsp_arm_v6.S | ||
17 | # endif | ||
18 | # endif | ||
19 | # ifdef HAVE_PITCHSCREEN | ||
20 | dsp/tdspeed.c | ||
21 | # endif | ||
22 | metadata/replaygain.c | ||
23 | metadata/metadata_common.c | ||
24 | metadata/a52.c | ||
25 | metadata/adx.c | ||
26 | metadata/aiff.c | ||
27 | metadata/ape.c | ||
28 | metadata/asap.c | ||
29 | metadata/asf.c | ||
30 | metadata/au.c | ||
31 | metadata/ay.c | ||
32 | metadata/flac.c | ||
33 | metadata/gbs.c | ||
34 | metadata/hes.c | ||
35 | metadata/kss.c | ||
36 | metadata/mod.c | ||
37 | metadata/monkeys.c | ||
38 | metadata/mp4.c | ||
39 | metadata/mpc.c | ||
40 | metadata/nsf.c | ||
41 | metadata/ogg.c | ||
42 | metadata/oma.c | ||
43 | metadata/rm.c | ||
44 | metadata/sgc.c | ||
45 | metadata/sid.c | ||
46 | metadata/smaf.c | ||
47 | metadata/spc.c | ||
48 | metadata/tta.c | ||
49 | metadata/vgm.c | ||
50 | metadata/vorbis.c | ||
51 | metadata/vox.c | ||
52 | metadata/wave.c | ||
53 | metadata/wavpack.c | ||
54 | #endif | ||
diff --git a/lib/rbcodec/dsp/compressor.c b/lib/rbcodec/dsp/compressor.c new file mode 100644 index 0000000000..3a8d52e4da --- /dev/null +++ b/lib/rbcodec/dsp/compressor.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Jeffrey Goode | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "fixedpoint.h" | ||
23 | #include "fracmul.h" | ||
24 | #include "settings.h" | ||
25 | #include "dsp.h" | ||
26 | #include "compressor.h" | ||
27 | |||
28 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
29 | /*#define LOGF_ENABLE*/ | ||
30 | #include "logf.h" | ||
31 | |||
32 | static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */ | ||
33 | static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ | ||
34 | static int32_t comp_curve[66] IBSS_ATTR; /* S7.24 format */ | ||
35 | static int32_t release_gain IBSS_ATTR; /* S7.24 format */ | ||
36 | |||
37 | #define UNITY (1L << 24) /* unity gain in S7.24 format */ | ||
38 | |||
39 | /** COMPRESSOR UPDATE | ||
40 | * Called via the menu system to configure the compressor process */ | ||
41 | bool compressor_update(void) | ||
42 | { | ||
43 | static int curr_set[5]; | ||
44 | int new_set[5] = { | ||
45 | global_settings.compressor_threshold, | ||
46 | global_settings.compressor_makeup_gain, | ||
47 | global_settings.compressor_ratio, | ||
48 | global_settings.compressor_knee, | ||
49 | global_settings.compressor_release_time}; | ||
50 | |||
51 | /* make menu values useful */ | ||
52 | int threshold = new_set[0]; | ||
53 | bool auto_gain = (new_set[1] == 1); | ||
54 | const int comp_ratios[] = {2, 4, 6, 10, 0}; | ||
55 | int ratio = comp_ratios[new_set[2]]; | ||
56 | bool soft_knee = (new_set[3] == 1); | ||
57 | int release = new_set[4] * NATIVE_FREQUENCY / 1000; | ||
58 | |||
59 | bool changed = false; | ||
60 | bool active = (threshold < 0); | ||
61 | |||
62 | for (int i = 0; i < 5; i++) | ||
63 | { | ||
64 | if (curr_set[i] != new_set[i]) | ||
65 | { | ||
66 | changed = true; | ||
67 | curr_set[i] = new_set[i]; | ||
68 | |||
69 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | ||
70 | switch (i) | ||
71 | { | ||
72 | case 0: | ||
73 | logf(" Compressor Threshold: %d dB\tEnabled: %s", | ||
74 | threshold, active ? "Yes" : "No"); | ||
75 | break; | ||
76 | case 1: | ||
77 | logf(" Compressor Makeup Gain: %s", | ||
78 | auto_gain ? "Auto" : "Off"); | ||
79 | break; | ||
80 | case 2: | ||
81 | if (ratio) | ||
82 | { logf(" Compressor Ratio: %d:1", ratio); } | ||
83 | else | ||
84 | { logf(" Compressor Ratio: Limit"); } | ||
85 | break; | ||
86 | case 3: | ||
87 | logf(" Compressor Knee: %s", soft_knee?"Soft":"Hard"); | ||
88 | break; | ||
89 | case 4: | ||
90 | logf(" Compressor Release: %d", release); | ||
91 | break; | ||
92 | } | ||
93 | #endif | ||
94 | } | ||
95 | } | ||
96 | |||
97 | if (changed && active) | ||
98 | { | ||
99 | /* configure variables for compressor operation */ | ||
100 | static const int32_t db[] = { | ||
101 | /* positive db equivalents in S15.16 format */ | ||
102 | 0x000000, 0x241FA4, 0x1E1A5E, 0x1A94C8, | ||
103 | 0x181518, 0x1624EA, 0x148F82, 0x1338BD, | ||
104 | 0x120FD2, 0x1109EB, 0x101FA4, 0x0F4BB6, | ||
105 | 0x0E8A3C, 0x0DD840, 0x0D3377, 0x0C9A0E, | ||
106 | 0x0C0A8C, 0x0B83BE, 0x0B04A5, 0x0A8C6C, | ||
107 | 0x0A1A5E, 0x09ADE1, 0x094670, 0x08E398, | ||
108 | 0x0884F6, 0x082A30, 0x07D2FA, 0x077F0F, | ||
109 | 0x072E31, 0x06E02A, 0x0694C8, 0x064BDF, | ||
110 | 0x060546, 0x05C0DA, 0x057E78, 0x053E03, | ||
111 | 0x04FF5F, 0x04C273, 0x048726, 0x044D64, | ||
112 | 0x041518, 0x03DE30, 0x03A89B, 0x037448, | ||
113 | 0x03412A, 0x030F32, 0x02DE52, 0x02AE80, | ||
114 | 0x027FB0, 0x0251D6, 0x0224EA, 0x01F8E2, | ||
115 | 0x01CDB4, 0x01A359, 0x0179C9, 0x0150FC, | ||
116 | 0x0128EB, 0x010190, 0x00DAE4, 0x00B4E1, | ||
117 | 0x008F82, 0x006AC1, 0x004699, 0x002305}; | ||
118 | |||
119 | struct curve_point | ||
120 | { | ||
121 | int32_t db; /* S15.16 format */ | ||
122 | int32_t offset; /* S15.16 format */ | ||
123 | } db_curve[5]; | ||
124 | |||
125 | /** Set up the shape of the compression curve first as decibel | ||
126 | values */ | ||
127 | /* db_curve[0] = bottom of knee | ||
128 | [1] = threshold | ||
129 | [2] = top of knee | ||
130 | [3] = 0 db input | ||
131 | [4] = ~+12db input (2 bits clipping overhead) */ | ||
132 | |||
133 | db_curve[1].db = threshold << 16; | ||
134 | if (soft_knee) | ||
135 | { | ||
136 | /* bottom of knee is 3dB below the threshold for soft knee*/ | ||
137 | db_curve[0].db = db_curve[1].db - (3 << 16); | ||
138 | /* top of knee is 3dB above the threshold for soft knee */ | ||
139 | db_curve[2].db = db_curve[1].db + (3 << 16); | ||
140 | if (ratio) | ||
141 | /* offset = -3db * (ratio - 1) / ratio */ | ||
142 | db_curve[2].offset = (int32_t)((long long)(-3 << 16) | ||
143 | * (ratio - 1) / ratio); | ||
144 | else | ||
145 | /* offset = -3db for hard limit */ | ||
146 | db_curve[2].offset = (-3 << 16); | ||
147 | } | ||
148 | else | ||
149 | { | ||
150 | /* bottom of knee is at the threshold for hard knee */ | ||
151 | db_curve[0].db = threshold << 16; | ||
152 | /* top of knee is at the threshold for hard knee */ | ||
153 | db_curve[2].db = threshold << 16; | ||
154 | db_curve[2].offset = 0; | ||
155 | } | ||
156 | |||
157 | /* Calculate 0db and ~+12db offsets */ | ||
158 | db_curve[4].db = 0xC0A8C; /* db of 2 bits clipping */ | ||
159 | if (ratio) | ||
160 | { | ||
161 | /* offset = threshold * (ratio - 1) / ratio */ | ||
162 | db_curve[3].offset = (int32_t)((long long)(threshold << 16) | ||
163 | * (ratio - 1) / ratio); | ||
164 | db_curve[4].offset = (int32_t)((long long)-db_curve[4].db | ||
165 | * (ratio - 1) / ratio) + db_curve[3].offset; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | /* offset = threshold for hard limit */ | ||
170 | db_curve[3].offset = (threshold << 16); | ||
171 | db_curve[4].offset = -db_curve[4].db + db_curve[3].offset; | ||
172 | } | ||
173 | |||
174 | /** Now set up the comp_curve table with compression offsets in the | ||
175 | form of gain factors in S7.24 format */ | ||
176 | /* comp_curve[0] is 0 (-infinity db) input */ | ||
177 | comp_curve[0] = UNITY; | ||
178 | /* comp_curve[1 to 63] are intermediate compression values | ||
179 | corresponding to the 6 MSB of the input values of a non-clipped | ||
180 | signal */ | ||
181 | for (int i = 1; i < 64; i++) | ||
182 | { | ||
183 | /* db constants are stored as positive numbers; | ||
184 | make them negative here */ | ||
185 | int32_t this_db = -db[i]; | ||
186 | |||
187 | /* no compression below the knee */ | ||
188 | if (this_db <= db_curve[0].db) | ||
189 | comp_curve[i] = UNITY; | ||
190 | |||
191 | /* if soft knee and below top of knee, | ||
192 | interpolate along soft knee slope */ | ||
193 | else if (soft_knee && (this_db <= db_curve[2].db)) | ||
194 | comp_curve[i] = fp_factor(fp_mul( | ||
195 | ((this_db - db_curve[0].db) / 6), | ||
196 | db_curve[2].offset, 16), 16) << 8; | ||
197 | |||
198 | /* interpolate along ratio slope above the knee */ | ||
199 | else | ||
200 | comp_curve[i] = fp_factor(fp_mul( | ||
201 | fp_div((db_curve[1].db - this_db), db_curve[1].db, 16), | ||
202 | db_curve[3].offset, 16), 16) << 8; | ||
203 | } | ||
204 | /* comp_curve[64] is the compression level of a maximum level, | ||
205 | non-clipped signal */ | ||
206 | comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8; | ||
207 | |||
208 | /* comp_curve[65] is the compression level of a maximum level, | ||
209 | clipped signal */ | ||
210 | comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8; | ||
211 | |||
212 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | ||
213 | logf("\n *** Compression Offsets ***"); | ||
214 | /* some settings for display only, not used in calculations */ | ||
215 | db_curve[0].offset = 0; | ||
216 | db_curve[1].offset = 0; | ||
217 | db_curve[3].db = 0; | ||
218 | |||
219 | for (int i = 0; i <= 4; i++) | ||
220 | { | ||
221 | logf("Curve[%d]: db: % 6.2f\toffset: % 6.2f", i, | ||
222 | (float)db_curve[i].db / (1 << 16), | ||
223 | (float)db_curve[i].offset / (1 << 16)); | ||
224 | } | ||
225 | |||
226 | logf("\nGain factors:"); | ||
227 | for (int i = 1; i <= 65; i++) | ||
228 | { | ||
229 | debugf("%02d: %.6f ", i, (float)comp_curve[i] / UNITY); | ||
230 | if (i % 4 == 0) debugf("\n"); | ||
231 | } | ||
232 | debugf("\n"); | ||
233 | #endif | ||
234 | |||
235 | /* if using auto peak, then makeup gain is max offset - | ||
236 | .1dB headroom */ | ||
237 | comp_makeup_gain = auto_gain ? | ||
238 | fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY; | ||
239 | logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY); | ||
240 | |||
241 | /* calculate per-sample gain change a rate of 10db over release time | ||
242 | */ | ||
243 | comp_rel_slope = 0xAF0BB2 / release; | ||
244 | logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY); | ||
245 | |||
246 | release_gain = UNITY; | ||
247 | } | ||
248 | |||
249 | return active; | ||
250 | } | ||
251 | |||
252 | /** GET COMPRESSION GAIN | ||
253 | * Returns the required gain factor in S7.24 format in order to compress the | ||
254 | * sample in accordance with the compression curve. Always 1 or less. | ||
255 | */ | ||
256 | static inline int32_t get_compression_gain(struct dsp_data *data, | ||
257 | int32_t sample) | ||
258 | { | ||
259 | const int frac_bits_offset = data->frac_bits - 15; | ||
260 | |||
261 | /* sample must be positive */ | ||
262 | if (sample < 0) | ||
263 | sample = -(sample + 1); | ||
264 | |||
265 | /* shift sample into 15 frac bit range */ | ||
266 | if (frac_bits_offset > 0) | ||
267 | sample >>= frac_bits_offset; | ||
268 | if (frac_bits_offset < 0) | ||
269 | sample <<= -frac_bits_offset; | ||
270 | |||
271 | /* normal case: sample isn't clipped */ | ||
272 | if (sample < (1 << 15)) | ||
273 | { | ||
274 | /* index is 6 MSB, rem is 9 LSB */ | ||
275 | int index = sample >> 9; | ||
276 | int32_t rem = (sample & 0x1FF) << 22; | ||
277 | |||
278 | /* interpolate from the compression curve: | ||
279 | higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */ | ||
280 | return comp_curve[index] - (FRACMUL(rem, | ||
281 | (comp_curve[index] - comp_curve[index + 1]))); | ||
282 | } | ||
283 | /* sample is somewhat clipped, up to 2 bits of overhead */ | ||
284 | if (sample < (1 << 17)) | ||
285 | { | ||
286 | /* straight interpolation: | ||
287 | higher gain - ((clipped portion of sample * 4/3 | ||
288 | / (1 << 31)) * (higher gain - lower gain)) */ | ||
289 | return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16, | ||
290 | (comp_curve[64] - comp_curve[65]))); | ||
291 | } | ||
292 | |||
293 | /* sample is too clipped, return invalid value */ | ||
294 | return -1; | ||
295 | } | ||
296 | |||
297 | /** COMPRESSOR PROCESS | ||
298 | * Changes the gain of the samples according to the compressor curve | ||
299 | */ | ||
300 | void compressor_process(int count, struct dsp_data *data, int32_t *buf[]) | ||
301 | { | ||
302 | const int num_chan = data->num_channels; | ||
303 | int32_t *in_buf[2] = {buf[0], buf[1]}; | ||
304 | |||
305 | while (count-- > 0) | ||
306 | { | ||
307 | int ch; | ||
308 | /* use lowest (most compressed) gain factor of the output buffer | ||
309 | sample pair for both samples (mono is also handled correctly here) | ||
310 | */ | ||
311 | int32_t sample_gain = UNITY; | ||
312 | for (ch = 0; ch < num_chan; ch++) | ||
313 | { | ||
314 | int32_t this_gain = get_compression_gain(data, *in_buf[ch]); | ||
315 | if (this_gain < sample_gain) | ||
316 | sample_gain = this_gain; | ||
317 | } | ||
318 | |||
319 | /* perform release slope; skip if no compression and no release slope | ||
320 | */ | ||
321 | if ((sample_gain != UNITY) || (release_gain != UNITY)) | ||
322 | { | ||
323 | /* if larger offset than previous slope, start new release slope | ||
324 | */ | ||
325 | if ((sample_gain <= release_gain) && (sample_gain > 0)) | ||
326 | { | ||
327 | release_gain = sample_gain; | ||
328 | } | ||
329 | else | ||
330 | /* keep sloping towards unity gain (and ignore invalid value) */ | ||
331 | { | ||
332 | release_gain += comp_rel_slope; | ||
333 | if (release_gain > UNITY) | ||
334 | { | ||
335 | release_gain = UNITY; | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /* total gain factor is the product of release gain and makeup gain, | ||
341 | but avoid computation if possible */ | ||
342 | int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain : | ||
343 | (comp_makeup_gain == UNITY) ? release_gain : | ||
344 | FRACMUL_SHL(release_gain, comp_makeup_gain, 7)); | ||
345 | |||
346 | /* Implement the compressor: apply total gain factor (if any) to the | ||
347 | output buffer sample pair/mono sample */ | ||
348 | if (total_gain != UNITY) | ||
349 | { | ||
350 | for (ch = 0; ch < num_chan; ch++) | ||
351 | { | ||
352 | *in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7); | ||
353 | } | ||
354 | } | ||
355 | in_buf[0]++; | ||
356 | in_buf[1]++; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | void compressor_reset(void) | ||
361 | { | ||
362 | release_gain = UNITY; | ||
363 | } | ||
diff --git a/lib/rbcodec/dsp/compressor.h b/lib/rbcodec/dsp/compressor.h new file mode 100644 index 0000000000..6154372e05 --- /dev/null +++ b/lib/rbcodec/dsp/compressor.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Jeffrey Goode | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef COMPRESSOR_H | ||
23 | #define COMPRESSOR_H | ||
24 | |||
25 | void compressor_process(int count, struct dsp_data *data, int32_t *buf[]); | ||
26 | bool compressor_update(void); | ||
27 | void compressor_reset(void); | ||
28 | |||
29 | #endif /* COMPRESSOR_H */ | ||
diff --git a/lib/rbcodec/dsp/dsp.c b/lib/rbcodec/dsp/dsp.c new file mode 100644 index 0000000000..4da555747b --- /dev/null +++ b/lib/rbcodec/dsp/dsp.c | |||
@@ -0,0 +1,1573 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Miika Pekkarinen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include <sound.h> | ||
24 | #include "dsp.h" | ||
25 | #include "dsp-util.h" | ||
26 | #include "eq.h" | ||
27 | #include "compressor.h" | ||
28 | #include "kernel.h" | ||
29 | #include "settings.h" | ||
30 | #include "replaygain.h" | ||
31 | #include "tdspeed.h" | ||
32 | #include "core_alloc.h" | ||
33 | #include "fixedpoint.h" | ||
34 | #include "fracmul.h" | ||
35 | |||
36 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
37 | /*#define LOGF_ENABLE*/ | ||
38 | #include "logf.h" | ||
39 | |||
40 | /* 16-bit samples are scaled based on these constants. The shift should be | ||
41 | * no more than 15. | ||
42 | */ | ||
43 | #define WORD_SHIFT 12 | ||
44 | #define WORD_FRACBITS 27 | ||
45 | |||
46 | #define NATIVE_DEPTH 16 | ||
47 | #define SMALL_SAMPLE_BUF_COUNT 128 /* Per channel */ | ||
48 | #define DEFAULT_GAIN 0x01000000 | ||
49 | |||
50 | /* enums to index conversion properly with stereo mode and other settings */ | ||
51 | enum | ||
52 | { | ||
53 | SAMPLE_INPUT_LE_NATIVE_I_STEREO = STEREO_INTERLEAVED, | ||
54 | SAMPLE_INPUT_LE_NATIVE_NI_STEREO = STEREO_NONINTERLEAVED, | ||
55 | SAMPLE_INPUT_LE_NATIVE_MONO = STEREO_MONO, | ||
56 | SAMPLE_INPUT_GT_NATIVE_I_STEREO = STEREO_INTERLEAVED + STEREO_NUM_MODES, | ||
57 | SAMPLE_INPUT_GT_NATIVE_NI_STEREO = STEREO_NONINTERLEAVED + STEREO_NUM_MODES, | ||
58 | SAMPLE_INPUT_GT_NATIVE_MONO = STEREO_MONO + STEREO_NUM_MODES, | ||
59 | SAMPLE_INPUT_GT_NATIVE_1ST_INDEX = STEREO_NUM_MODES | ||
60 | }; | ||
61 | |||
62 | enum | ||
63 | { | ||
64 | SAMPLE_OUTPUT_MONO = 0, | ||
65 | SAMPLE_OUTPUT_STEREO, | ||
66 | SAMPLE_OUTPUT_DITHERED_MONO, | ||
67 | SAMPLE_OUTPUT_DITHERED_STEREO | ||
68 | }; | ||
69 | |||
70 | /* No asm...yet */ | ||
71 | struct dither_data | ||
72 | { | ||
73 | long error[3]; /* 00h */ | ||
74 | long random; /* 0ch */ | ||
75 | /* 10h */ | ||
76 | }; | ||
77 | |||
78 | struct crossfeed_data | ||
79 | { | ||
80 | int32_t gain; /* 00h - Direct path gain */ | ||
81 | int32_t coefs[3]; /* 04h - Coefficients for the shelving filter */ | ||
82 | int32_t history[4]; /* 10h - Format is x[n - 1], y[n - 1] for both channels */ | ||
83 | int32_t delay[13][2]; /* 20h */ | ||
84 | int32_t *index; /* 88h - Current pointer into the delay line */ | ||
85 | /* 8ch */ | ||
86 | }; | ||
87 | |||
88 | /* Current setup is one lowshelf filters three peaking filters and one | ||
89 | * highshelf filter. Varying the number of shelving filters make no sense, | ||
90 | * but adding peaking filters is possible. | ||
91 | */ | ||
92 | struct eq_state | ||
93 | { | ||
94 | char enabled[5]; /* 00h - Flags for active filters */ | ||
95 | struct eqfilter filters[5]; /* 08h - packing is 4? */ | ||
96 | /* 10ch */ | ||
97 | }; | ||
98 | |||
99 | /* Include header with defines which functions are implemented in assembly | ||
100 | code for the target */ | ||
101 | #include <dsp_asm.h> | ||
102 | |||
103 | /* Typedefs keep things much neater in this case */ | ||
104 | typedef void (*sample_input_fn_type)(int count, const char *src[], | ||
105 | int32_t *dst[]); | ||
106 | typedef int (*resample_fn_type)(int count, struct dsp_data *data, | ||
107 | const int32_t *src[], int32_t *dst[]); | ||
108 | typedef void (*sample_output_fn_type)(int count, struct dsp_data *data, | ||
109 | const int32_t *src[], int16_t *dst); | ||
110 | |||
111 | /* Single-DSP channel processing in place */ | ||
112 | typedef void (*channels_process_fn_type)(int count, int32_t *buf[]); | ||
113 | /* DSP local channel processing in place */ | ||
114 | typedef void (*channels_process_dsp_fn_type)(int count, struct dsp_data *data, | ||
115 | int32_t *buf[]); | ||
116 | |||
117 | /* | ||
118 | ***************************************************************************/ | ||
119 | |||
120 | struct dsp_config | ||
121 | { | ||
122 | struct dsp_data data; /* Config members for use in external routines */ | ||
123 | long codec_frequency; /* Sample rate of data coming from the codec */ | ||
124 | long frequency; /* Effective sample rate after pitch shift (if any) */ | ||
125 | int sample_depth; | ||
126 | int sample_bytes; | ||
127 | int stereo_mode; | ||
128 | int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */ | ||
129 | #ifdef HAVE_PITCHSCREEN | ||
130 | bool tdspeed_active; /* Timestretch is in use */ | ||
131 | #endif | ||
132 | #ifdef HAVE_SW_TONE_CONTROLS | ||
133 | /* Filter struct for software bass/treble controls */ | ||
134 | struct eqfilter tone_filter; | ||
135 | #endif | ||
136 | /* Functions that change depending upon settings - NULL if stage is | ||
137 | disabled */ | ||
138 | sample_input_fn_type input_samples; | ||
139 | resample_fn_type resample; | ||
140 | sample_output_fn_type output_samples; | ||
141 | /* These will be NULL for the voice codec and is more economical that | ||
142 | way */ | ||
143 | channels_process_dsp_fn_type apply_gain; | ||
144 | channels_process_fn_type apply_crossfeed; | ||
145 | channels_process_fn_type eq_process; | ||
146 | channels_process_fn_type channels_process; | ||
147 | channels_process_dsp_fn_type compressor_process; | ||
148 | }; | ||
149 | |||
150 | /* General DSP config */ | ||
151 | static struct dsp_config dsp_conf[2] IBSS_ATTR; /* 0=A, 1=V */ | ||
152 | /* Dithering */ | ||
153 | static struct dither_data dither_data[2] IBSS_ATTR; /* 0=left, 1=right */ | ||
154 | static long dither_mask IBSS_ATTR; | ||
155 | static long dither_bias IBSS_ATTR; | ||
156 | /* Crossfeed */ | ||
157 | struct crossfeed_data crossfeed_data IDATA_ATTR = /* A */ | ||
158 | { | ||
159 | .index = (int32_t *)crossfeed_data.delay | ||
160 | }; | ||
161 | |||
162 | /* Equalizer */ | ||
163 | static struct eq_state eq_data; /* A */ | ||
164 | |||
165 | /* Software tone controls */ | ||
166 | #ifdef HAVE_SW_TONE_CONTROLS | ||
167 | static int prescale; /* A/V */ | ||
168 | static int bass; /* A/V */ | ||
169 | static int treble; /* A/V */ | ||
170 | #endif | ||
171 | |||
172 | /* Settings applicable to audio codec only */ | ||
173 | #ifdef HAVE_PITCHSCREEN | ||
174 | static int32_t pitch_ratio = PITCH_SPEED_100; | ||
175 | static int big_sample_locks; | ||
176 | #endif | ||
177 | static int channels_mode; | ||
178 | long dsp_sw_gain; | ||
179 | long dsp_sw_cross; | ||
180 | static bool dither_enabled; | ||
181 | static long eq_precut; | ||
182 | static long track_gain; | ||
183 | static bool new_gain; | ||
184 | static long album_gain; | ||
185 | static long track_peak; | ||
186 | static long album_peak; | ||
187 | static long replaygain; | ||
188 | static bool crossfeed_enabled; | ||
189 | |||
190 | #define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO]) | ||
191 | #define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE]) | ||
192 | |||
193 | /* The internal format is 32-bit samples, non-interleaved, stereo. This | ||
194 | * format is similar to the raw output from several codecs, so the amount | ||
195 | * of copying needed is minimized for that case. | ||
196 | */ | ||
197 | |||
198 | #define RESAMPLE_RATIO 4 /* Enough for 11,025 Hz -> 44,100 Hz */ | ||
199 | #define SMALL_RESAMPLE_BUF_COUNT (SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO) | ||
200 | #define BIG_SAMPLE_BUF_COUNT SMALL_RESAMPLE_BUF_COUNT | ||
201 | #define BIG_RESAMPLE_BUF_COUNT (BIG_SAMPLE_BUF_COUNT * RESAMPLE_RATIO) | ||
202 | |||
203 | static int32_t small_sample_buf[2][SMALL_SAMPLE_BUF_COUNT] IBSS_ATTR; | ||
204 | static int32_t small_resample_buf[2][SMALL_RESAMPLE_BUF_COUNT] IBSS_ATTR; | ||
205 | |||
206 | #ifdef HAVE_PITCHSCREEN | ||
207 | static int32_t (* big_sample_buf)[BIG_SAMPLE_BUF_COUNT] = NULL; | ||
208 | static int32_t (* big_resample_buf)[BIG_RESAMPLE_BUF_COUNT] = NULL; | ||
209 | #endif | ||
210 | |||
211 | static int sample_buf_count = SMALL_SAMPLE_BUF_COUNT; | ||
212 | static int32_t *sample_buf[2] = { small_sample_buf[0], small_sample_buf[1] }; | ||
213 | static int resample_buf_count = SMALL_RESAMPLE_BUF_COUNT; | ||
214 | static int32_t *resample_buf[2] = { small_resample_buf[0], small_resample_buf[1] }; | ||
215 | |||
216 | #ifdef HAVE_PITCHSCREEN | ||
217 | int32_t sound_get_pitch(void) | ||
218 | { | ||
219 | return pitch_ratio; | ||
220 | } | ||
221 | |||
222 | void sound_set_pitch(int32_t percent) | ||
223 | { | ||
224 | pitch_ratio = percent; | ||
225 | dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY, | ||
226 | AUDIO_DSP.codec_frequency); | ||
227 | } | ||
228 | |||
229 | static void tdspeed_set_pointers( bool time_stretch_active ) | ||
230 | { | ||
231 | if( time_stretch_active ) | ||
232 | { | ||
233 | sample_buf_count = BIG_SAMPLE_BUF_COUNT; | ||
234 | resample_buf_count = BIG_RESAMPLE_BUF_COUNT; | ||
235 | sample_buf[0] = big_sample_buf[0]; | ||
236 | sample_buf[1] = big_sample_buf[1]; | ||
237 | resample_buf[0] = big_resample_buf[0]; | ||
238 | resample_buf[1] = big_resample_buf[1]; | ||
239 | } | ||
240 | else | ||
241 | { | ||
242 | sample_buf_count = SMALL_SAMPLE_BUF_COUNT; | ||
243 | resample_buf_count = SMALL_RESAMPLE_BUF_COUNT; | ||
244 | sample_buf[0] = small_sample_buf[0]; | ||
245 | sample_buf[1] = small_sample_buf[1]; | ||
246 | resample_buf[0] = small_resample_buf[0]; | ||
247 | resample_buf[1] = small_resample_buf[1]; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | static void tdspeed_setup(struct dsp_config *dspc) | ||
252 | { | ||
253 | /* Assume timestretch will not be used */ | ||
254 | dspc->tdspeed_active = false; | ||
255 | |||
256 | tdspeed_set_pointers( false ); | ||
257 | |||
258 | if (!dsp_timestretch_available()) | ||
259 | return; /* Timestretch not enabled or buffer not allocated */ | ||
260 | |||
261 | if (dspc->tdspeed_percent == 0) | ||
262 | dspc->tdspeed_percent = PITCH_SPEED_100; | ||
263 | |||
264 | if (!tdspeed_config( | ||
265 | dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency, | ||
266 | dspc->stereo_mode != STEREO_MONO, | ||
267 | dspc->tdspeed_percent)) | ||
268 | return; /* Timestretch not possible or needed with these parameters */ | ||
269 | |||
270 | /* Timestretch is to be used */ | ||
271 | dspc->tdspeed_active = true; | ||
272 | |||
273 | tdspeed_set_pointers( true ); | ||
274 | } | ||
275 | |||
276 | |||
277 | static int move_callback(int handle, void* current, void* new) | ||
278 | { | ||
279 | (void)handle;(void)current; | ||
280 | |||
281 | if ( big_sample_locks > 0 ) | ||
282 | return BUFLIB_CB_CANNOT_MOVE; | ||
283 | |||
284 | big_sample_buf = new; | ||
285 | |||
286 | /* no allocation without timestretch enabled */ | ||
287 | tdspeed_set_pointers( true ); | ||
288 | return BUFLIB_CB_OK; | ||
289 | } | ||
290 | |||
291 | static void lock_sample_buf( bool lock ) | ||
292 | { | ||
293 | if ( lock ) | ||
294 | big_sample_locks++; | ||
295 | else | ||
296 | big_sample_locks--; | ||
297 | } | ||
298 | |||
299 | static struct buflib_callbacks ops = { | ||
300 | .move_callback = move_callback, | ||
301 | .shrink_callback = NULL, | ||
302 | }; | ||
303 | |||
304 | |||
305 | void dsp_timestretch_enable(bool enabled) | ||
306 | { | ||
307 | /* Hook to set up timestretch buffer on first call to settings_apply() */ | ||
308 | static int handle = -1; | ||
309 | if (enabled) | ||
310 | { | ||
311 | if (big_sample_buf) | ||
312 | return; /* already allocated and enabled */ | ||
313 | |||
314 | /* Set up timestretch buffers */ | ||
315 | big_sample_buf = &small_resample_buf[0]; | ||
316 | handle = core_alloc_ex("resample buf", | ||
317 | 2 * BIG_RESAMPLE_BUF_COUNT * sizeof(int32_t), | ||
318 | &ops); | ||
319 | big_sample_locks = 0; | ||
320 | enabled = handle >= 0; | ||
321 | |||
322 | if (enabled) | ||
323 | { | ||
324 | /* success, now setup tdspeed */ | ||
325 | big_resample_buf = core_get_data(handle); | ||
326 | |||
327 | tdspeed_init(); | ||
328 | tdspeed_setup(&AUDIO_DSP); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | if (!enabled) | ||
333 | { | ||
334 | dsp_set_timestretch(PITCH_SPEED_100); | ||
335 | tdspeed_finish(); | ||
336 | |||
337 | if (handle >= 0) | ||
338 | core_free(handle); | ||
339 | |||
340 | handle = -1; | ||
341 | big_sample_buf = NULL; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | void dsp_set_timestretch(int32_t percent) | ||
346 | { | ||
347 | AUDIO_DSP.tdspeed_percent = percent; | ||
348 | tdspeed_setup(&AUDIO_DSP); | ||
349 | } | ||
350 | |||
351 | int32_t dsp_get_timestretch() | ||
352 | { | ||
353 | return AUDIO_DSP.tdspeed_percent; | ||
354 | } | ||
355 | |||
356 | bool dsp_timestretch_available() | ||
357 | { | ||
358 | return (global_settings.timestretch_enabled && big_sample_buf); | ||
359 | } | ||
360 | #endif /* HAVE_PITCHSCREEN */ | ||
361 | |||
362 | /* Convert count samples to the internal format, if needed. Updates src | ||
363 | * to point past the samples "consumed" and dst is set to point to the | ||
364 | * samples to consume. Note that for mono, dst[0] equals dst[1], as there | ||
365 | * is no point in processing the same data twice. | ||
366 | */ | ||
367 | |||
368 | /* convert count 16-bit mono to 32-bit mono */ | ||
369 | static void sample_input_lte_native_mono( | ||
370 | int count, const char *src[], int32_t *dst[]) | ||
371 | { | ||
372 | const int16_t *s = (int16_t *) src[0]; | ||
373 | const int16_t * const send = s + count; | ||
374 | int32_t *d = dst[0] = dst[1] = sample_buf[0]; | ||
375 | int scale = WORD_SHIFT; | ||
376 | |||
377 | while (s < send) | ||
378 | { | ||
379 | *d++ = *s++ << scale; | ||
380 | } | ||
381 | |||
382 | src[0] = (char *)s; | ||
383 | } | ||
384 | |||
385 | /* convert count 16-bit interleaved stereo to 32-bit noninterleaved */ | ||
386 | static void sample_input_lte_native_i_stereo( | ||
387 | int count, const char *src[], int32_t *dst[]) | ||
388 | { | ||
389 | const int32_t *s = (int32_t *) src[0]; | ||
390 | const int32_t * const send = s + count; | ||
391 | int32_t *dl = dst[0] = sample_buf[0]; | ||
392 | int32_t *dr = dst[1] = sample_buf[1]; | ||
393 | int scale = WORD_SHIFT; | ||
394 | |||
395 | while (s < send) | ||
396 | { | ||
397 | int32_t slr = *s++; | ||
398 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
399 | *dl++ = (slr >> 16) << scale; | ||
400 | *dr++ = (int32_t)(int16_t)slr << scale; | ||
401 | #else /* ROCKBOX_BIG_ENDIAN */ | ||
402 | *dl++ = (int32_t)(int16_t)slr << scale; | ||
403 | *dr++ = (slr >> 16) << scale; | ||
404 | #endif | ||
405 | } | ||
406 | |||
407 | src[0] = (char *)s; | ||
408 | } | ||
409 | |||
410 | /* convert count 16-bit noninterleaved stereo to 32-bit noninterleaved */ | ||
411 | static void sample_input_lte_native_ni_stereo( | ||
412 | int count, const char *src[], int32_t *dst[]) | ||
413 | { | ||
414 | const int16_t *sl = (int16_t *) src[0]; | ||
415 | const int16_t *sr = (int16_t *) src[1]; | ||
416 | const int16_t * const slend = sl + count; | ||
417 | int32_t *dl = dst[0] = sample_buf[0]; | ||
418 | int32_t *dr = dst[1] = sample_buf[1]; | ||
419 | int scale = WORD_SHIFT; | ||
420 | |||
421 | while (sl < slend) | ||
422 | { | ||
423 | *dl++ = *sl++ << scale; | ||
424 | *dr++ = *sr++ << scale; | ||
425 | } | ||
426 | |||
427 | src[0] = (char *)sl; | ||
428 | src[1] = (char *)sr; | ||
429 | } | ||
430 | |||
431 | /* convert count 32-bit mono to 32-bit mono */ | ||
432 | static void sample_input_gt_native_mono( | ||
433 | int count, const char *src[], int32_t *dst[]) | ||
434 | { | ||
435 | dst[0] = dst[1] = (int32_t *)src[0]; | ||
436 | src[0] = (char *)(dst[0] + count); | ||
437 | } | ||
438 | |||
439 | /* convert count 32-bit interleaved stereo to 32-bit noninterleaved stereo */ | ||
440 | static void sample_input_gt_native_i_stereo( | ||
441 | int count, const char *src[], int32_t *dst[]) | ||
442 | { | ||
443 | const int32_t *s = (int32_t *)src[0]; | ||
444 | const int32_t * const send = s + 2*count; | ||
445 | int32_t *dl = dst[0] = sample_buf[0]; | ||
446 | int32_t *dr = dst[1] = sample_buf[1]; | ||
447 | |||
448 | while (s < send) | ||
449 | { | ||
450 | *dl++ = *s++; | ||
451 | *dr++ = *s++; | ||
452 | } | ||
453 | |||
454 | src[0] = (char *)send; | ||
455 | } | ||
456 | |||
457 | /* convert 32 bit-noninterleaved stereo to 32-bit noninterleaved stereo */ | ||
458 | static void sample_input_gt_native_ni_stereo( | ||
459 | int count, const char *src[], int32_t *dst[]) | ||
460 | { | ||
461 | dst[0] = (int32_t *)src[0]; | ||
462 | dst[1] = (int32_t *)src[1]; | ||
463 | src[0] = (char *)(dst[0] + count); | ||
464 | src[1] = (char *)(dst[1] + count); | ||
465 | } | ||
466 | |||
467 | /** | ||
468 | * sample_input_new_format() | ||
469 | * | ||
470 | * set the to-native sample conversion function based on dsp sample parameters | ||
471 | * | ||
472 | * !DSPPARAMSYNC | ||
473 | * needs syncing with changes to the following dsp parameters: | ||
474 | * * dsp->stereo_mode (A/V) | ||
475 | * * dsp->sample_depth (A/V) | ||
476 | */ | ||
477 | static void sample_input_new_format(struct dsp_config *dsp) | ||
478 | { | ||
479 | static const sample_input_fn_type sample_input_functions[] = | ||
480 | { | ||
481 | [SAMPLE_INPUT_LE_NATIVE_I_STEREO] = sample_input_lte_native_i_stereo, | ||
482 | [SAMPLE_INPUT_LE_NATIVE_NI_STEREO] = sample_input_lte_native_ni_stereo, | ||
483 | [SAMPLE_INPUT_LE_NATIVE_MONO] = sample_input_lte_native_mono, | ||
484 | [SAMPLE_INPUT_GT_NATIVE_I_STEREO] = sample_input_gt_native_i_stereo, | ||
485 | [SAMPLE_INPUT_GT_NATIVE_NI_STEREO] = sample_input_gt_native_ni_stereo, | ||
486 | [SAMPLE_INPUT_GT_NATIVE_MONO] = sample_input_gt_native_mono, | ||
487 | }; | ||
488 | |||
489 | int convert = dsp->stereo_mode; | ||
490 | |||
491 | if (dsp->sample_depth > NATIVE_DEPTH) | ||
492 | convert += SAMPLE_INPUT_GT_NATIVE_1ST_INDEX; | ||
493 | |||
494 | dsp->input_samples = sample_input_functions[convert]; | ||
495 | } | ||
496 | |||
497 | |||
498 | #ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO | ||
499 | /* write mono internal format to output format */ | ||
500 | static void sample_output_mono(int count, struct dsp_data *data, | ||
501 | const int32_t *src[], int16_t *dst) | ||
502 | { | ||
503 | const int32_t *s0 = src[0]; | ||
504 | const int scale = data->output_scale; | ||
505 | const int dc_bias = 1 << (scale - 1); | ||
506 | |||
507 | while (count-- > 0) | ||
508 | { | ||
509 | int32_t lr = clip_sample_16((*s0++ + dc_bias) >> scale); | ||
510 | *dst++ = lr; | ||
511 | *dst++ = lr; | ||
512 | } | ||
513 | } | ||
514 | #endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO */ | ||
515 | |||
516 | /* write stereo internal format to output format */ | ||
517 | #ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO | ||
518 | static void sample_output_stereo(int count, struct dsp_data *data, | ||
519 | const int32_t *src[], int16_t *dst) | ||
520 | { | ||
521 | const int32_t *s0 = src[0]; | ||
522 | const int32_t *s1 = src[1]; | ||
523 | const int scale = data->output_scale; | ||
524 | const int dc_bias = 1 << (scale - 1); | ||
525 | |||
526 | while (count-- > 0) | ||
527 | { | ||
528 | *dst++ = clip_sample_16((*s0++ + dc_bias) >> scale); | ||
529 | *dst++ = clip_sample_16((*s1++ + dc_bias) >> scale); | ||
530 | } | ||
531 | } | ||
532 | #endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO */ | ||
533 | |||
534 | /** | ||
535 | * The "dither" code to convert the 24-bit samples produced by libmad was | ||
536 | * taken from the coolplayer project - coolplayer.sourceforge.net | ||
537 | * | ||
538 | * This function handles mono and stereo outputs. | ||
539 | */ | ||
540 | static void sample_output_dithered(int count, struct dsp_data *data, | ||
541 | const int32_t *src[], int16_t *dst) | ||
542 | { | ||
543 | const int32_t mask = dither_mask; | ||
544 | const int32_t bias = dither_bias; | ||
545 | const int scale = data->output_scale; | ||
546 | const int32_t min = data->clip_min; | ||
547 | const int32_t max = data->clip_max; | ||
548 | const int32_t range = max - min; | ||
549 | int ch; | ||
550 | int16_t *d; | ||
551 | |||
552 | for (ch = 0; ch < data->num_channels; ch++) | ||
553 | { | ||
554 | struct dither_data * const dither = &dither_data[ch]; | ||
555 | const int32_t *s = src[ch]; | ||
556 | int i; | ||
557 | |||
558 | for (i = 0, d = &dst[ch]; i < count; i++, s++, d += 2) | ||
559 | { | ||
560 | int32_t output, sample; | ||
561 | int32_t random; | ||
562 | |||
563 | /* Noise shape and bias (for correct rounding later) */ | ||
564 | sample = *s; | ||
565 | sample += dither->error[0] - dither->error[1] + dither->error[2]; | ||
566 | dither->error[2] = dither->error[1]; | ||
567 | dither->error[1] = dither->error[0]/2; | ||
568 | |||
569 | output = sample + bias; | ||
570 | |||
571 | /* Dither, highpass triangle PDF */ | ||
572 | random = dither->random*0x0019660dL + 0x3c6ef35fL; | ||
573 | output += (random & mask) - (dither->random & mask); | ||
574 | dither->random = random; | ||
575 | |||
576 | /* Round sample to output range */ | ||
577 | output &= ~mask; | ||
578 | |||
579 | /* Error feedback */ | ||
580 | dither->error[0] = sample - output; | ||
581 | |||
582 | /* Clip */ | ||
583 | if ((uint32_t)(output - min) > (uint32_t)range) | ||
584 | { | ||
585 | int32_t c = min; | ||
586 | if (output > min) | ||
587 | c += range; | ||
588 | output = c; | ||
589 | } | ||
590 | |||
591 | /* Quantize and store */ | ||
592 | *d = output >> scale; | ||
593 | } | ||
594 | } | ||
595 | |||
596 | if (data->num_channels == 2) | ||
597 | return; | ||
598 | |||
599 | /* Have to duplicate left samples into the right channel since | ||
600 | pcm buffer and hardware is interleaved stereo */ | ||
601 | d = &dst[0]; | ||
602 | |||
603 | while (count-- > 0) | ||
604 | { | ||
605 | int16_t s = *d++; | ||
606 | *d++ = s; | ||
607 | } | ||
608 | } | ||
609 | |||
610 | /** | ||
611 | * sample_output_new_format() | ||
612 | * | ||
613 | * set the from-native to ouput sample conversion routine | ||
614 | * | ||
615 | * !DSPPARAMSYNC | ||
616 | * needs syncing with changes to the following dsp parameters: | ||
617 | * * dsp->stereo_mode (A/V) | ||
618 | * * dither_enabled (A) | ||
619 | */ | ||
620 | static void sample_output_new_format(struct dsp_config *dsp) | ||
621 | { | ||
622 | static const sample_output_fn_type sample_output_functions[] = | ||
623 | { | ||
624 | sample_output_mono, | ||
625 | sample_output_stereo, | ||
626 | sample_output_dithered, | ||
627 | sample_output_dithered | ||
628 | }; | ||
629 | |||
630 | int out = dsp->data.num_channels - 1; | ||
631 | |||
632 | if (dsp == &AUDIO_DSP && dither_enabled) | ||
633 | out += 2; | ||
634 | |||
635 | dsp->output_samples = sample_output_functions[out]; | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * Linear interpolation resampling that introduces a one sample delay because | ||
640 | * of our inability to look into the future at the end of a frame. | ||
641 | */ | ||
642 | #ifndef DSP_HAVE_ASM_RESAMPLING | ||
643 | static int dsp_downsample(int count, struct dsp_data *data, | ||
644 | const int32_t *src[], int32_t *dst[]) | ||
645 | { | ||
646 | int ch = data->num_channels - 1; | ||
647 | uint32_t delta = data->resample_data.delta; | ||
648 | uint32_t phase, pos; | ||
649 | int32_t *d; | ||
650 | |||
651 | /* Rolled channel loop actually showed slightly faster. */ | ||
652 | do | ||
653 | { | ||
654 | /* Just initialize things and not worry too much about the relatively | ||
655 | * uncommon case of not being able to spit out a sample for the frame. | ||
656 | */ | ||
657 | const int32_t *s = src[ch]; | ||
658 | int32_t last = data->resample_data.last_sample[ch]; | ||
659 | |||
660 | data->resample_data.last_sample[ch] = s[count - 1]; | ||
661 | d = dst[ch]; | ||
662 | phase = data->resample_data.phase; | ||
663 | pos = phase >> 16; | ||
664 | |||
665 | /* Do we need last sample of previous frame for interpolation? */ | ||
666 | if (pos > 0) | ||
667 | last = s[pos - 1]; | ||
668 | |||
669 | while (pos < (uint32_t)count) | ||
670 | { | ||
671 | *d++ = last + FRACMUL((phase & 0xffff) << 15, s[pos] - last); | ||
672 | phase += delta; | ||
673 | pos = phase >> 16; | ||
674 | last = s[pos - 1]; | ||
675 | } | ||
676 | } | ||
677 | while (--ch >= 0); | ||
678 | |||
679 | /* Wrap phase accumulator back to start of next frame. */ | ||
680 | data->resample_data.phase = phase - (count << 16); | ||
681 | return d - dst[0]; | ||
682 | } | ||
683 | |||
684 | static int dsp_upsample(int count, struct dsp_data *data, | ||
685 | const int32_t *src[], int32_t *dst[]) | ||
686 | { | ||
687 | int ch = data->num_channels - 1; | ||
688 | uint32_t delta = data->resample_data.delta; | ||
689 | uint32_t phase, pos; | ||
690 | int32_t *d; | ||
691 | |||
692 | /* Rolled channel loop actually showed slightly faster. */ | ||
693 | do | ||
694 | { | ||
695 | /* Should always be able to output a sample for a ratio up to RESAMPLE_RATIO */ | ||
696 | const int32_t *s = src[ch]; | ||
697 | int32_t last = data->resample_data.last_sample[ch]; | ||
698 | |||
699 | data->resample_data.last_sample[ch] = s[count - 1]; | ||
700 | d = dst[ch]; | ||
701 | phase = data->resample_data.phase; | ||
702 | pos = phase >> 16; | ||
703 | |||
704 | while (pos == 0) | ||
705 | { | ||
706 | *d++ = last + FRACMUL((phase & 0xffff) << 15, s[0] - last); | ||
707 | phase += delta; | ||
708 | pos = phase >> 16; | ||
709 | } | ||
710 | |||
711 | while (pos < (uint32_t)count) | ||
712 | { | ||
713 | last = s[pos - 1]; | ||
714 | *d++ = last + FRACMUL((phase & 0xffff) << 15, s[pos] - last); | ||
715 | phase += delta; | ||
716 | pos = phase >> 16; | ||
717 | } | ||
718 | } | ||
719 | while (--ch >= 0); | ||
720 | |||
721 | /* Wrap phase accumulator back to start of next frame. */ | ||
722 | data->resample_data.phase = phase & 0xffff; | ||
723 | return d - dst[0]; | ||
724 | } | ||
725 | #endif /* DSP_HAVE_ASM_RESAMPLING */ | ||
726 | |||
727 | static void resampler_new_delta(struct dsp_config *dsp) | ||
728 | { | ||
729 | dsp->data.resample_data.delta = (unsigned long) | ||
730 | dsp->frequency * 65536LL / NATIVE_FREQUENCY; | ||
731 | |||
732 | if (dsp->frequency == NATIVE_FREQUENCY) | ||
733 | { | ||
734 | /* NOTE: If fully glitch-free transistions from no resampling to | ||
735 | resampling are desired, last_sample history should be maintained | ||
736 | even when not resampling. */ | ||
737 | dsp->resample = NULL; | ||
738 | dsp->data.resample_data.phase = 0; | ||
739 | dsp->data.resample_data.last_sample[0] = 0; | ||
740 | dsp->data.resample_data.last_sample[1] = 0; | ||
741 | } | ||
742 | else if (dsp->frequency < NATIVE_FREQUENCY) | ||
743 | dsp->resample = dsp_upsample; | ||
744 | else | ||
745 | dsp->resample = dsp_downsample; | ||
746 | } | ||
747 | |||
748 | /* Resample count stereo samples. Updates the src array, if resampling is | ||
749 | * done, to refer to the resampled data. Returns number of stereo samples | ||
750 | * for further processing. | ||
751 | */ | ||
752 | static inline int resample(struct dsp_config *dsp, int count, int32_t *src[]) | ||
753 | { | ||
754 | int32_t *dst[2] = | ||
755 | { | ||
756 | resample_buf[0], | ||
757 | resample_buf[1] | ||
758 | }; | ||
759 | lock_sample_buf( true ); | ||
760 | count = dsp->resample(count, &dsp->data, (const int32_t **)src, dst); | ||
761 | |||
762 | src[0] = dst[0]; | ||
763 | src[1] = dst[dsp->data.num_channels - 1]; | ||
764 | lock_sample_buf( false ); | ||
765 | return count; | ||
766 | } | ||
767 | |||
768 | static void dither_init(struct dsp_config *dsp) | ||
769 | { | ||
770 | memset(dither_data, 0, sizeof (dither_data)); | ||
771 | dither_bias = (1L << (dsp->data.frac_bits - NATIVE_DEPTH)); | ||
772 | dither_mask = (1L << (dsp->data.frac_bits + 1 - NATIVE_DEPTH)) - 1; | ||
773 | } | ||
774 | |||
775 | void dsp_dither_enable(bool enable) | ||
776 | { | ||
777 | struct dsp_config *dsp = &AUDIO_DSP; | ||
778 | dither_enabled = enable; | ||
779 | sample_output_new_format(dsp); | ||
780 | } | ||
781 | |||
782 | /* Applies crossfeed to the stereo signal in src. | ||
783 | * Crossfeed is a process where listening over speakers is simulated. This | ||
784 | * is good for old hard panned stereo records, which might be quite fatiguing | ||
785 | * to listen to on headphones with no crossfeed. | ||
786 | */ | ||
787 | #ifndef DSP_HAVE_ASM_CROSSFEED | ||
788 | static void apply_crossfeed(int count, int32_t *buf[]) | ||
789 | { | ||
790 | int32_t *hist_l = &crossfeed_data.history[0]; | ||
791 | int32_t *hist_r = &crossfeed_data.history[2]; | ||
792 | int32_t *delay = &crossfeed_data.delay[0][0]; | ||
793 | int32_t *coefs = &crossfeed_data.coefs[0]; | ||
794 | int32_t gain = crossfeed_data.gain; | ||
795 | int32_t *di = crossfeed_data.index; | ||
796 | |||
797 | int32_t acc; | ||
798 | int32_t left, right; | ||
799 | int i; | ||
800 | |||
801 | for (i = 0; i < count; i++) | ||
802 | { | ||
803 | left = buf[0][i]; | ||
804 | right = buf[1][i]; | ||
805 | |||
806 | /* Filter delayed sample from left speaker */ | ||
807 | acc = FRACMUL(*di, coefs[0]); | ||
808 | acc += FRACMUL(hist_l[0], coefs[1]); | ||
809 | acc += FRACMUL(hist_l[1], coefs[2]); | ||
810 | /* Save filter history for left speaker */ | ||
811 | hist_l[1] = acc; | ||
812 | hist_l[0] = *di; | ||
813 | *di++ = left; | ||
814 | /* Filter delayed sample from right speaker */ | ||
815 | acc = FRACMUL(*di, coefs[0]); | ||
816 | acc += FRACMUL(hist_r[0], coefs[1]); | ||
817 | acc += FRACMUL(hist_r[1], coefs[2]); | ||
818 | /* Save filter history for right speaker */ | ||
819 | hist_r[1] = acc; | ||
820 | hist_r[0] = *di; | ||
821 | *di++ = right; | ||
822 | /* Now add the attenuated direct sound and write to outputs */ | ||
823 | buf[0][i] = FRACMUL(left, gain) + hist_r[1]; | ||
824 | buf[1][i] = FRACMUL(right, gain) + hist_l[1]; | ||
825 | |||
826 | /* Wrap delay line index if bigger than delay line size */ | ||
827 | if (di >= delay + 13*2) | ||
828 | di = delay; | ||
829 | } | ||
830 | /* Write back local copies of data we've modified */ | ||
831 | crossfeed_data.index = di; | ||
832 | } | ||
833 | #endif /* DSP_HAVE_ASM_CROSSFEED */ | ||
834 | |||
835 | /** | ||
836 | * dsp_set_crossfeed(bool enable) | ||
837 | * | ||
838 | * !DSPPARAMSYNC | ||
839 | * needs syncing with changes to the following dsp parameters: | ||
840 | * * dsp->stereo_mode (A) | ||
841 | */ | ||
842 | void dsp_set_crossfeed(bool enable) | ||
843 | { | ||
844 | crossfeed_enabled = enable; | ||
845 | AUDIO_DSP.apply_crossfeed = (enable && AUDIO_DSP.data.num_channels > 1) | ||
846 | ? apply_crossfeed : NULL; | ||
847 | } | ||
848 | |||
849 | void dsp_set_crossfeed_direct_gain(int gain) | ||
850 | { | ||
851 | crossfeed_data.gain = get_replaygain_int(gain * 10) << 7; | ||
852 | /* If gain is negative, the calculation overflowed and we need to clamp */ | ||
853 | if (crossfeed_data.gain < 0) | ||
854 | crossfeed_data.gain = 0x7fffffff; | ||
855 | } | ||
856 | |||
857 | /* Both gains should be below 0 dB */ | ||
858 | void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) | ||
859 | { | ||
860 | int32_t *c = crossfeed_data.coefs; | ||
861 | long scaler = get_replaygain_int(lf_gain * 10) << 7; | ||
862 | |||
863 | cutoff = 0xffffffff/NATIVE_FREQUENCY*cutoff; | ||
864 | hf_gain -= lf_gain; | ||
865 | /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB | ||
866 | * point instead of shelf midpoint. This is for compatibility with the old | ||
867 | * crossfeed shelf filter and should be removed if crossfeed settings are | ||
868 | * ever made incompatible for any other good reason. | ||
869 | */ | ||
870 | cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24); | ||
871 | filter_shelf_coefs(cutoff, hf_gain, false, c); | ||
872 | /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains | ||
873 | * over 1 and can do this safely | ||
874 | */ | ||
875 | c[0] = FRACMUL_SHL(c[0], scaler, 4); | ||
876 | c[1] = FRACMUL_SHL(c[1], scaler, 4); | ||
877 | c[2] <<= 4; | ||
878 | } | ||
879 | |||
880 | /* Apply a constant gain to the samples (e.g., for ReplayGain). | ||
881 | * Note that this must be called before the resampler. | ||
882 | */ | ||
883 | #ifndef DSP_HAVE_ASM_APPLY_GAIN | ||
884 | static void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]) | ||
885 | { | ||
886 | const int32_t gain = data->gain; | ||
887 | int ch; | ||
888 | |||
889 | for (ch = 0; ch < data->num_channels; ch++) | ||
890 | { | ||
891 | int32_t *d = buf[ch]; | ||
892 | int i; | ||
893 | |||
894 | for (i = 0; i < count; i++) | ||
895 | d[i] = FRACMUL_SHL(d[i], gain, 8); | ||
896 | } | ||
897 | } | ||
898 | #endif /* DSP_HAVE_ASM_APPLY_GAIN */ | ||
899 | |||
900 | /* Combine all gains to a global gain. */ | ||
901 | static void set_gain(struct dsp_config *dsp) | ||
902 | { | ||
903 | /* gains are in S7.24 format */ | ||
904 | dsp->data.gain = DEFAULT_GAIN; | ||
905 | |||
906 | /* Replay gain not relevant to voice */ | ||
907 | if (dsp == &AUDIO_DSP && replaygain) | ||
908 | { | ||
909 | dsp->data.gain = replaygain; | ||
910 | } | ||
911 | |||
912 | if (dsp->eq_process && eq_precut) | ||
913 | { | ||
914 | dsp->data.gain = fp_mul(dsp->data.gain, eq_precut, 24); | ||
915 | } | ||
916 | |||
917 | #ifdef HAVE_SW_VOLUME_CONTROL | ||
918 | if (global_settings.volume < SW_VOLUME_MAX || | ||
919 | global_settings.volume > SW_VOLUME_MIN) | ||
920 | { | ||
921 | int vol_gain = get_replaygain_int(global_settings.volume * 100); | ||
922 | dsp->data.gain = (long) (((int64_t) dsp->data.gain * vol_gain) >> 24); | ||
923 | } | ||
924 | #endif | ||
925 | |||
926 | if (dsp->data.gain == DEFAULT_GAIN) | ||
927 | { | ||
928 | dsp->data.gain = 0; | ||
929 | } | ||
930 | else | ||
931 | { | ||
932 | dsp->data.gain >>= 1; /* convert gain to S8.23 format */ | ||
933 | } | ||
934 | |||
935 | dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL; | ||
936 | } | ||
937 | |||
938 | /** | ||
939 | * Update the amount to cut the audio before applying the equalizer. | ||
940 | * | ||
941 | * @param precut to apply in decibels (multiplied by 10) | ||
942 | */ | ||
943 | void dsp_set_eq_precut(int precut) | ||
944 | { | ||
945 | eq_precut = get_replaygain_int(precut * -10); | ||
946 | set_gain(&AUDIO_DSP); | ||
947 | } | ||
948 | |||
949 | /** | ||
950 | * Synchronize the equalizer filter coefficients with the global settings. | ||
951 | * | ||
952 | * @param band the equalizer band to synchronize | ||
953 | */ | ||
954 | void dsp_set_eq_coefs(int band) | ||
955 | { | ||
956 | /* Adjust setting pointer to the band we actually want to change */ | ||
957 | struct eq_band_setting *setting = &global_settings.eq_band_settings[band]; | ||
958 | |||
959 | /* Convert user settings to format required by coef generator functions */ | ||
960 | unsigned long cutoff = 0xffffffff / NATIVE_FREQUENCY * setting->cutoff; | ||
961 | unsigned long q = setting->q; | ||
962 | int gain = setting->gain; | ||
963 | |||
964 | if (q == 0) | ||
965 | q = 1; | ||
966 | |||
967 | /* NOTE: The coef functions assume the EMAC unit is in fractional mode, | ||
968 | which it should be, since we're executed from the main thread. */ | ||
969 | |||
970 | /* Assume a band is disabled if the gain is zero */ | ||
971 | if (gain == 0) | ||
972 | { | ||
973 | eq_data.enabled[band] = 0; | ||
974 | } | ||
975 | else | ||
976 | { | ||
977 | if (band == 0) | ||
978 | eq_ls_coefs(cutoff, q, gain, eq_data.filters[band].coefs); | ||
979 | else if (band == 4) | ||
980 | eq_hs_coefs(cutoff, q, gain, eq_data.filters[band].coefs); | ||
981 | else | ||
982 | eq_pk_coefs(cutoff, q, gain, eq_data.filters[band].coefs); | ||
983 | |||
984 | eq_data.enabled[band] = 1; | ||
985 | } | ||
986 | } | ||
987 | |||
988 | /* Apply EQ filters to those bands that have got it switched on. */ | ||
989 | static void eq_process(int count, int32_t *buf[]) | ||
990 | { | ||
991 | static const int shifts[] = | ||
992 | { | ||
993 | EQ_SHELF_SHIFT, /* low shelf */ | ||
994 | EQ_PEAK_SHIFT, /* peaking */ | ||
995 | EQ_PEAK_SHIFT, /* peaking */ | ||
996 | EQ_PEAK_SHIFT, /* peaking */ | ||
997 | EQ_SHELF_SHIFT, /* high shelf */ | ||
998 | }; | ||
999 | unsigned int channels = AUDIO_DSP.data.num_channels; | ||
1000 | int i; | ||
1001 | |||
1002 | /* filter configuration currently is 1 low shelf filter, 3 band peaking | ||
1003 | filters and 1 high shelf filter, in that order. we need to know this | ||
1004 | so we can choose the correct shift factor. | ||
1005 | */ | ||
1006 | for (i = 0; i < 5; i++) | ||
1007 | { | ||
1008 | if (!eq_data.enabled[i]) | ||
1009 | continue; | ||
1010 | eq_filter(buf, &eq_data.filters[i], count, channels, shifts[i]); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | /** | ||
1015 | * Use to enable the equalizer. | ||
1016 | * | ||
1017 | * @param enable true to enable the equalizer | ||
1018 | */ | ||
1019 | void dsp_set_eq(bool enable) | ||
1020 | { | ||
1021 | AUDIO_DSP.eq_process = enable ? eq_process : NULL; | ||
1022 | set_gain(&AUDIO_DSP); | ||
1023 | } | ||
1024 | |||
1025 | static void dsp_set_stereo_width(int value) | ||
1026 | { | ||
1027 | long width, straight, cross; | ||
1028 | |||
1029 | width = value * 0x7fffff / 100; | ||
1030 | |||
1031 | if (value <= 100) | ||
1032 | { | ||
1033 | straight = (0x7fffff + width) / 2; | ||
1034 | cross = straight - width; | ||
1035 | } | ||
1036 | else | ||
1037 | { | ||
1038 | /* straight = (1 + width) / (2 * width) */ | ||
1039 | straight = ((int64_t)(0x7fffff + width) << 22) / width; | ||
1040 | cross = straight - 0x7fffff; | ||
1041 | } | ||
1042 | |||
1043 | dsp_sw_gain = straight << 8; | ||
1044 | dsp_sw_cross = cross << 8; | ||
1045 | } | ||
1046 | |||
1047 | /** | ||
1048 | * Implements the different channel configurations and stereo width. | ||
1049 | */ | ||
1050 | |||
1051 | /* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for | ||
1052 | * completeness. */ | ||
1053 | #if 0 | ||
1054 | static void channels_process_sound_chan_stereo(int count, int32_t *buf[]) | ||
1055 | { | ||
1056 | /* The channels are each just themselves */ | ||
1057 | (void)count; (void)buf; | ||
1058 | } | ||
1059 | #endif | ||
1060 | |||
1061 | #ifndef DSP_HAVE_ASM_SOUND_CHAN_MONO | ||
1062 | static void channels_process_sound_chan_mono(int count, int32_t *buf[]) | ||
1063 | { | ||
1064 | int32_t *sl = buf[0], *sr = buf[1]; | ||
1065 | |||
1066 | while (count-- > 0) | ||
1067 | { | ||
1068 | int32_t lr = *sl/2 + *sr/2; | ||
1069 | *sl++ = lr; | ||
1070 | *sr++ = lr; | ||
1071 | } | ||
1072 | } | ||
1073 | #endif /* DSP_HAVE_ASM_SOUND_CHAN_MONO */ | ||
1074 | |||
1075 | #ifndef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM | ||
1076 | static void channels_process_sound_chan_custom(int count, int32_t *buf[]) | ||
1077 | { | ||
1078 | const int32_t gain = dsp_sw_gain; | ||
1079 | const int32_t cross = dsp_sw_cross; | ||
1080 | int32_t *sl = buf[0], *sr = buf[1]; | ||
1081 | |||
1082 | while (count-- > 0) | ||
1083 | { | ||
1084 | int32_t l = *sl; | ||
1085 | int32_t r = *sr; | ||
1086 | *sl++ = FRACMUL(l, gain) + FRACMUL(r, cross); | ||
1087 | *sr++ = FRACMUL(r, gain) + FRACMUL(l, cross); | ||
1088 | } | ||
1089 | } | ||
1090 | #endif /* DSP_HAVE_ASM_SOUND_CHAN_CUSTOM */ | ||
1091 | |||
1092 | static void channels_process_sound_chan_mono_left(int count, int32_t *buf[]) | ||
1093 | { | ||
1094 | /* Just copy over the other channel */ | ||
1095 | memcpy(buf[1], buf[0], count * sizeof (*buf)); | ||
1096 | } | ||
1097 | |||
1098 | static void channels_process_sound_chan_mono_right(int count, int32_t *buf[]) | ||
1099 | { | ||
1100 | /* Just copy over the other channel */ | ||
1101 | memcpy(buf[0], buf[1], count * sizeof (*buf)); | ||
1102 | } | ||
1103 | |||
1104 | #ifndef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE | ||
1105 | static void channels_process_sound_chan_karaoke(int count, int32_t *buf[]) | ||
1106 | { | ||
1107 | int32_t *sl = buf[0], *sr = buf[1]; | ||
1108 | |||
1109 | while (count-- > 0) | ||
1110 | { | ||
1111 | int32_t ch = *sl/2 - *sr/2; | ||
1112 | *sl++ = ch; | ||
1113 | *sr++ = -ch; | ||
1114 | } | ||
1115 | } | ||
1116 | #endif /* DSP_HAVE_ASM_SOUND_CHAN_KARAOKE */ | ||
1117 | |||
1118 | static void dsp_set_channel_config(int value) | ||
1119 | { | ||
1120 | static const channels_process_fn_type channels_process_functions[] = | ||
1121 | { | ||
1122 | /* SOUND_CHAN_STEREO = All-purpose index for no channel processing */ | ||
1123 | [SOUND_CHAN_STEREO] = NULL, | ||
1124 | [SOUND_CHAN_MONO] = channels_process_sound_chan_mono, | ||
1125 | [SOUND_CHAN_CUSTOM] = channels_process_sound_chan_custom, | ||
1126 | [SOUND_CHAN_MONO_LEFT] = channels_process_sound_chan_mono_left, | ||
1127 | [SOUND_CHAN_MONO_RIGHT] = channels_process_sound_chan_mono_right, | ||
1128 | [SOUND_CHAN_KARAOKE] = channels_process_sound_chan_karaoke, | ||
1129 | }; | ||
1130 | |||
1131 | if ((unsigned)value >= ARRAYLEN(channels_process_functions) || | ||
1132 | AUDIO_DSP.stereo_mode == STEREO_MONO) | ||
1133 | { | ||
1134 | value = SOUND_CHAN_STEREO; | ||
1135 | } | ||
1136 | |||
1137 | /* This doesn't apply to voice */ | ||
1138 | channels_mode = value; | ||
1139 | AUDIO_DSP.channels_process = channels_process_functions[value]; | ||
1140 | } | ||
1141 | |||
1142 | #if CONFIG_CODEC == SWCODEC | ||
1143 | |||
1144 | #ifdef HAVE_SW_TONE_CONTROLS | ||
1145 | static void set_tone_controls(void) | ||
1146 | { | ||
1147 | filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200, | ||
1148 | 0xffffffff/NATIVE_FREQUENCY*3500, | ||
1149 | bass, treble, -prescale, | ||
1150 | AUDIO_DSP.tone_filter.coefs); | ||
1151 | /* Sync the voice dsp coefficients */ | ||
1152 | memcpy(&VOICE_DSP.tone_filter.coefs, AUDIO_DSP.tone_filter.coefs, | ||
1153 | sizeof (VOICE_DSP.tone_filter.coefs)); | ||
1154 | } | ||
1155 | #endif | ||
1156 | |||
1157 | /* Hook back from firmware/ part of audio, which can't/shouldn't call apps/ | ||
1158 | * code directly. | ||
1159 | */ | ||
1160 | int dsp_callback(int msg, intptr_t param) | ||
1161 | { | ||
1162 | switch (msg) | ||
1163 | { | ||
1164 | #ifdef HAVE_SW_TONE_CONTROLS | ||
1165 | case DSP_CALLBACK_SET_PRESCALE: | ||
1166 | prescale = param; | ||
1167 | set_tone_controls(); | ||
1168 | break; | ||
1169 | /* prescaler is always set after calling any of these, so we wait with | ||
1170 | * calculating coefs until the above case is hit. | ||
1171 | */ | ||
1172 | case DSP_CALLBACK_SET_BASS: | ||
1173 | bass = param; | ||
1174 | break; | ||
1175 | case DSP_CALLBACK_SET_TREBLE: | ||
1176 | treble = param; | ||
1177 | break; | ||
1178 | #ifdef HAVE_SW_VOLUME_CONTROL | ||
1179 | case DSP_CALLBACK_SET_SW_VOLUME: | ||
1180 | set_gain(&AUDIO_DSP); | ||
1181 | break; | ||
1182 | #endif | ||
1183 | #endif | ||
1184 | case DSP_CALLBACK_SET_CHANNEL_CONFIG: | ||
1185 | dsp_set_channel_config(param); | ||
1186 | break; | ||
1187 | case DSP_CALLBACK_SET_STEREO_WIDTH: | ||
1188 | dsp_set_stereo_width(param); | ||
1189 | break; | ||
1190 | default: | ||
1191 | break; | ||
1192 | } | ||
1193 | return 0; | ||
1194 | } | ||
1195 | #endif | ||
1196 | |||
1197 | /* Process and convert src audio to dst based on the DSP configuration, | ||
1198 | * reading count number of audio samples. dst is assumed to be large | ||
1199 | * enough; use dsp_output_count() to get the required number. src is an | ||
1200 | * array of pointers; for mono and interleaved stereo, it contains one | ||
1201 | * pointer to the start of the audio data and the other is ignored; for | ||
1202 | * non-interleaved stereo, it contains two pointers, one for each audio | ||
1203 | * channel. Returns number of bytes written to dst. | ||
1204 | */ | ||
1205 | int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) | ||
1206 | { | ||
1207 | static int32_t *tmp[2]; /* tdspeed_doit() needs it static */ | ||
1208 | static long last_yield; | ||
1209 | long tick; | ||
1210 | int written = 0; | ||
1211 | |||
1212 | #if defined(CPU_COLDFIRE) | ||
1213 | /* set emac unit for dsp processing, and save old macsr, we're running in | ||
1214 | codec thread context at this point, so can't clobber it */ | ||
1215 | unsigned long old_macsr = coldfire_get_macsr(); | ||
1216 | coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); | ||
1217 | #endif | ||
1218 | |||
1219 | if (new_gain) | ||
1220 | dsp_set_replaygain(); /* Gain has changed */ | ||
1221 | |||
1222 | /* Perform at least one yield before starting */ | ||
1223 | last_yield = current_tick; | ||
1224 | yield(); | ||
1225 | |||
1226 | /* Testing function pointers for NULL is preferred since the pointer | ||
1227 | will be preloaded to be used for the call if not. */ | ||
1228 | while (count > 0) | ||
1229 | { | ||
1230 | int samples = MIN(sample_buf_count, count); | ||
1231 | count -= samples; | ||
1232 | |||
1233 | dsp->input_samples(samples, src, tmp); | ||
1234 | |||
1235 | #ifdef HAVE_PITCHSCREEN | ||
1236 | if (dsp->tdspeed_active) | ||
1237 | samples = tdspeed_doit(tmp, samples); | ||
1238 | #endif | ||
1239 | |||
1240 | int chunk_offset = 0; | ||
1241 | while (samples > 0) | ||
1242 | { | ||
1243 | int32_t *t2[2]; | ||
1244 | t2[0] = tmp[0]+chunk_offset; | ||
1245 | t2[1] = tmp[1]+chunk_offset; | ||
1246 | |||
1247 | int chunk = MIN(sample_buf_count, samples); | ||
1248 | chunk_offset += chunk; | ||
1249 | samples -= chunk; | ||
1250 | |||
1251 | if (dsp->apply_gain) | ||
1252 | dsp->apply_gain(chunk, &dsp->data, t2); | ||
1253 | |||
1254 | if (dsp->resample && (chunk = resample(dsp, chunk, t2)) <= 0) | ||
1255 | break; /* I'm pretty sure we're downsampling here */ | ||
1256 | |||
1257 | if (dsp->apply_crossfeed) | ||
1258 | dsp->apply_crossfeed(chunk, t2); | ||
1259 | |||
1260 | if (dsp->eq_process) | ||
1261 | dsp->eq_process(chunk, t2); | ||
1262 | |||
1263 | #ifdef HAVE_SW_TONE_CONTROLS | ||
1264 | if ((bass | treble) != 0) | ||
1265 | eq_filter(t2, &dsp->tone_filter, chunk, | ||
1266 | dsp->data.num_channels, FILTER_BISHELF_SHIFT); | ||
1267 | #endif | ||
1268 | |||
1269 | if (dsp->channels_process) | ||
1270 | dsp->channels_process(chunk, t2); | ||
1271 | |||
1272 | if (dsp->compressor_process) | ||
1273 | dsp->compressor_process(chunk, &dsp->data, t2); | ||
1274 | |||
1275 | dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); | ||
1276 | |||
1277 | written += chunk; | ||
1278 | dst += chunk * sizeof (int16_t) * 2; | ||
1279 | |||
1280 | /* yield at least once each tick */ | ||
1281 | tick = current_tick; | ||
1282 | if (TIME_AFTER(tick, last_yield)) | ||
1283 | { | ||
1284 | last_yield = tick; | ||
1285 | yield(); | ||
1286 | } | ||
1287 | } | ||
1288 | } | ||
1289 | |||
1290 | #if defined(CPU_COLDFIRE) | ||
1291 | /* set old macsr again */ | ||
1292 | coldfire_set_macsr(old_macsr); | ||
1293 | #endif | ||
1294 | return written; | ||
1295 | } | ||
1296 | |||
1297 | /* Given count number of input samples, calculate the maximum number of | ||
1298 | * samples of output data that would be generated (the calculation is not | ||
1299 | * entirely exact and rounds upwards to be on the safe side; during | ||
1300 | * resampling, the number of samples generated depends on the current state | ||
1301 | * of the resampler). | ||
1302 | */ | ||
1303 | /* dsp_input_size MUST be called afterwards */ | ||
1304 | int dsp_output_count(struct dsp_config *dsp, int count) | ||
1305 | { | ||
1306 | #ifdef HAVE_PITCHSCREEN | ||
1307 | if (dsp->tdspeed_active) | ||
1308 | count = tdspeed_est_output_size(); | ||
1309 | #endif | ||
1310 | if (dsp->resample) | ||
1311 | { | ||
1312 | count = (int)(((unsigned long)count * NATIVE_FREQUENCY | ||
1313 | + (dsp->frequency - 1)) / dsp->frequency); | ||
1314 | } | ||
1315 | |||
1316 | /* Now we have the resampled sample count which must not exceed | ||
1317 | * resample_buf_count to avoid resample buffer overflow. One | ||
1318 | * must call dsp_input_count() to get the correct input sample | ||
1319 | * count. | ||
1320 | */ | ||
1321 | if (count > resample_buf_count) | ||
1322 | count = resample_buf_count; | ||
1323 | |||
1324 | return count; | ||
1325 | } | ||
1326 | |||
1327 | /* Given count output samples, calculate number of input samples | ||
1328 | * that would be consumed in order to fill the output buffer. | ||
1329 | */ | ||
1330 | int dsp_input_count(struct dsp_config *dsp, int count) | ||
1331 | { | ||
1332 | /* count is now the number of resampled input samples. Convert to | ||
1333 | original input samples. */ | ||
1334 | if (dsp->resample) | ||
1335 | { | ||
1336 | /* Use the real resampling delta = | ||
1337 | * dsp->frequency * 65536 / NATIVE_FREQUENCY, and | ||
1338 | * round towards zero to avoid buffer overflows. */ | ||
1339 | count = (int)(((unsigned long)count * | ||
1340 | dsp->data.resample_data.delta) >> 16); | ||
1341 | } | ||
1342 | |||
1343 | #ifdef HAVE_PITCHSCREEN | ||
1344 | if (dsp->tdspeed_active) | ||
1345 | count = tdspeed_est_input_size(count); | ||
1346 | #endif | ||
1347 | |||
1348 | return count; | ||
1349 | } | ||
1350 | |||
1351 | static void dsp_set_gain_var(long *var, long value) | ||
1352 | { | ||
1353 | *var = value; | ||
1354 | new_gain = true; | ||
1355 | } | ||
1356 | |||
1357 | static void dsp_update_functions(struct dsp_config *dsp) | ||
1358 | { | ||
1359 | sample_input_new_format(dsp); | ||
1360 | sample_output_new_format(dsp); | ||
1361 | if (dsp == &AUDIO_DSP) | ||
1362 | dsp_set_crossfeed(crossfeed_enabled); | ||
1363 | } | ||
1364 | |||
1365 | intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value) | ||
1366 | { | ||
1367 | switch (setting) | ||
1368 | { | ||
1369 | case DSP_MYDSP: | ||
1370 | switch (value) | ||
1371 | { | ||
1372 | case CODEC_IDX_AUDIO: | ||
1373 | return (intptr_t)&AUDIO_DSP; | ||
1374 | case CODEC_IDX_VOICE: | ||
1375 | return (intptr_t)&VOICE_DSP; | ||
1376 | default: | ||
1377 | return (intptr_t)NULL; | ||
1378 | } | ||
1379 | |||
1380 | case DSP_SET_FREQUENCY: | ||
1381 | memset(&dsp->data.resample_data, 0, sizeof (dsp->data.resample_data)); | ||
1382 | /* Fall through!!! */ | ||
1383 | case DSP_SWITCH_FREQUENCY: | ||
1384 | dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value; | ||
1385 | /* Account for playback speed adjustment when setting dsp->frequency | ||
1386 | if we're called from the main audio thread. Voice UI thread should | ||
1387 | not need this feature. | ||
1388 | */ | ||
1389 | #ifdef HAVE_PITCHSCREEN | ||
1390 | if (dsp == &AUDIO_DSP) | ||
1391 | dsp->frequency = pitch_ratio * dsp->codec_frequency / PITCH_SPEED_100; | ||
1392 | else | ||
1393 | #endif | ||
1394 | dsp->frequency = dsp->codec_frequency; | ||
1395 | |||
1396 | resampler_new_delta(dsp); | ||
1397 | #ifdef HAVE_PITCHSCREEN | ||
1398 | tdspeed_setup(dsp); | ||
1399 | #endif | ||
1400 | break; | ||
1401 | |||
1402 | case DSP_SET_SAMPLE_DEPTH: | ||
1403 | dsp->sample_depth = value; | ||
1404 | |||
1405 | if (dsp->sample_depth <= NATIVE_DEPTH) | ||
1406 | { | ||
1407 | dsp->data.frac_bits = WORD_FRACBITS; | ||
1408 | dsp->sample_bytes = sizeof (int16_t); /* samples are 16 bits */ | ||
1409 | dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1); | ||
1410 | dsp->data.clip_min = -((1 << WORD_FRACBITS)); | ||
1411 | } | ||
1412 | else | ||
1413 | { | ||
1414 | dsp->data.frac_bits = value; | ||
1415 | dsp->sample_bytes = sizeof (int32_t); /* samples are 32 bits */ | ||
1416 | dsp->data.clip_max = (1 << value) - 1; | ||
1417 | dsp->data.clip_min = -(1 << value); | ||
1418 | } | ||
1419 | |||
1420 | dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH; | ||
1421 | sample_input_new_format(dsp); | ||
1422 | dither_init(dsp); | ||
1423 | break; | ||
1424 | |||
1425 | case DSP_SET_STEREO_MODE: | ||
1426 | dsp->stereo_mode = value; | ||
1427 | dsp->data.num_channels = value == STEREO_MONO ? 1 : 2; | ||
1428 | dsp_update_functions(dsp); | ||
1429 | #ifdef HAVE_PITCHSCREEN | ||
1430 | tdspeed_setup(dsp); | ||
1431 | #endif | ||
1432 | break; | ||
1433 | |||
1434 | case DSP_RESET: | ||
1435 | dsp->stereo_mode = STEREO_NONINTERLEAVED; | ||
1436 | dsp->data.num_channels = 2; | ||
1437 | dsp->sample_depth = NATIVE_DEPTH; | ||
1438 | dsp->data.frac_bits = WORD_FRACBITS; | ||
1439 | dsp->sample_bytes = sizeof (int16_t); | ||
1440 | dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH; | ||
1441 | dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1); | ||
1442 | dsp->data.clip_min = -((1 << WORD_FRACBITS)); | ||
1443 | dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY; | ||
1444 | |||
1445 | if (dsp == &AUDIO_DSP) | ||
1446 | { | ||
1447 | track_gain = 0; | ||
1448 | album_gain = 0; | ||
1449 | track_peak = 0; | ||
1450 | album_peak = 0; | ||
1451 | new_gain = true; | ||
1452 | } | ||
1453 | |||
1454 | dsp_update_functions(dsp); | ||
1455 | resampler_new_delta(dsp); | ||
1456 | #ifdef HAVE_PITCHSCREEN | ||
1457 | tdspeed_setup(dsp); | ||
1458 | #endif | ||
1459 | if (dsp == &AUDIO_DSP) | ||
1460 | compressor_reset(); | ||
1461 | break; | ||
1462 | |||
1463 | case DSP_FLUSH: | ||
1464 | memset(&dsp->data.resample_data, 0, | ||
1465 | sizeof (dsp->data.resample_data)); | ||
1466 | resampler_new_delta(dsp); | ||
1467 | dither_init(dsp); | ||
1468 | #ifdef HAVE_PITCHSCREEN | ||
1469 | tdspeed_setup(dsp); | ||
1470 | #endif | ||
1471 | if (dsp == &AUDIO_DSP) | ||
1472 | compressor_reset(); | ||
1473 | break; | ||
1474 | |||
1475 | case DSP_SET_TRACK_GAIN: | ||
1476 | if (dsp == &AUDIO_DSP) | ||
1477 | dsp_set_gain_var(&track_gain, value); | ||
1478 | break; | ||
1479 | |||
1480 | case DSP_SET_ALBUM_GAIN: | ||
1481 | if (dsp == &AUDIO_DSP) | ||
1482 | dsp_set_gain_var(&album_gain, value); | ||
1483 | break; | ||
1484 | |||
1485 | case DSP_SET_TRACK_PEAK: | ||
1486 | if (dsp == &AUDIO_DSP) | ||
1487 | dsp_set_gain_var(&track_peak, value); | ||
1488 | break; | ||
1489 | |||
1490 | case DSP_SET_ALBUM_PEAK: | ||
1491 | if (dsp == &AUDIO_DSP) | ||
1492 | dsp_set_gain_var(&album_peak, value); | ||
1493 | break; | ||
1494 | |||
1495 | default: | ||
1496 | return 0; | ||
1497 | } | ||
1498 | |||
1499 | return 1; | ||
1500 | } | ||
1501 | |||
1502 | int get_replaygain_mode(bool have_track_gain, bool have_album_gain) | ||
1503 | { | ||
1504 | int type; | ||
1505 | |||
1506 | bool track = ((global_settings.replaygain_type == REPLAYGAIN_TRACK) | ||
1507 | || ((global_settings.replaygain_type == REPLAYGAIN_SHUFFLE) | ||
1508 | && global_settings.playlist_shuffle)); | ||
1509 | |||
1510 | type = (!track && have_album_gain) ? REPLAYGAIN_ALBUM | ||
1511 | : have_track_gain ? REPLAYGAIN_TRACK : -1; | ||
1512 | |||
1513 | return type; | ||
1514 | } | ||
1515 | |||
1516 | void dsp_set_replaygain(void) | ||
1517 | { | ||
1518 | long gain = 0; | ||
1519 | |||
1520 | new_gain = false; | ||
1521 | |||
1522 | if ((global_settings.replaygain_type != REPLAYGAIN_OFF) || | ||
1523 | global_settings.replaygain_noclip) | ||
1524 | { | ||
1525 | bool track_mode = get_replaygain_mode(track_gain != 0, | ||
1526 | album_gain != 0) == REPLAYGAIN_TRACK; | ||
1527 | long peak = (track_mode || !album_peak) ? track_peak : album_peak; | ||
1528 | |||
1529 | if (global_settings.replaygain_type != REPLAYGAIN_OFF) | ||
1530 | { | ||
1531 | gain = (track_mode || !album_gain) ? track_gain : album_gain; | ||
1532 | |||
1533 | if (global_settings.replaygain_preamp) | ||
1534 | { | ||
1535 | long preamp = get_replaygain_int( | ||
1536 | global_settings.replaygain_preamp * 10); | ||
1537 | |||
1538 | gain = (long) (((int64_t) gain * preamp) >> 24); | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | if (gain == 0) | ||
1543 | { | ||
1544 | /* So that noclip can work even with no gain information. */ | ||
1545 | gain = DEFAULT_GAIN; | ||
1546 | } | ||
1547 | |||
1548 | if (global_settings.replaygain_noclip && (peak != 0) | ||
1549 | && ((((int64_t) gain * peak) >> 24) >= DEFAULT_GAIN)) | ||
1550 | { | ||
1551 | gain = (((int64_t) DEFAULT_GAIN << 24) / peak); | ||
1552 | } | ||
1553 | |||
1554 | if (gain == DEFAULT_GAIN) | ||
1555 | { | ||
1556 | /* Nothing to do, disable processing. */ | ||
1557 | gain = 0; | ||
1558 | } | ||
1559 | } | ||
1560 | |||
1561 | /* Store in S7.24 format to simplify calculations. */ | ||
1562 | replaygain = gain; | ||
1563 | set_gain(&AUDIO_DSP); | ||
1564 | } | ||
1565 | |||
1566 | /** SET COMPRESSOR | ||
1567 | * Called by the menu system to configure the compressor process */ | ||
1568 | void dsp_set_compressor(void) | ||
1569 | { | ||
1570 | /* enable/disable the compressor */ | ||
1571 | AUDIO_DSP.compressor_process = compressor_update() ? | ||
1572 | compressor_process : NULL; | ||
1573 | } | ||
diff --git a/lib/rbcodec/dsp/dsp.h b/lib/rbcodec/dsp/dsp.h new file mode 100644 index 0000000000..2a00f649f8 --- /dev/null +++ b/lib/rbcodec/dsp/dsp.h | |||
@@ -0,0 +1,125 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Miika Pekkarinen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _DSP_H | ||
23 | #define _DSP_H | ||
24 | |||
25 | #include <stdlib.h> | ||
26 | #include <stdbool.h> | ||
27 | |||
28 | #define NATIVE_FREQUENCY 44100 | ||
29 | |||
30 | enum | ||
31 | { | ||
32 | STEREO_INTERLEAVED = 0, | ||
33 | STEREO_NONINTERLEAVED, | ||
34 | STEREO_MONO, | ||
35 | STEREO_NUM_MODES, | ||
36 | }; | ||
37 | |||
38 | enum | ||
39 | { | ||
40 | CODEC_IDX_AUDIO = 0, | ||
41 | CODEC_IDX_VOICE, | ||
42 | }; | ||
43 | |||
44 | enum | ||
45 | { | ||
46 | DSP_MYDSP = 1, | ||
47 | DSP_SET_FREQUENCY, | ||
48 | DSP_SWITCH_FREQUENCY, | ||
49 | DSP_SET_SAMPLE_DEPTH, | ||
50 | DSP_SET_STEREO_MODE, | ||
51 | DSP_RESET, | ||
52 | DSP_FLUSH, | ||
53 | DSP_SET_TRACK_GAIN, | ||
54 | DSP_SET_ALBUM_GAIN, | ||
55 | DSP_SET_TRACK_PEAK, | ||
56 | DSP_SET_ALBUM_PEAK, | ||
57 | DSP_CROSSFEED | ||
58 | }; | ||
59 | |||
60 | |||
61 | /**************************************************************************** | ||
62 | * NOTE: Any assembly routines that use these structures must be updated | ||
63 | * if current data members are moved or changed. | ||
64 | */ | ||
65 | struct resample_data | ||
66 | { | ||
67 | uint32_t delta; /* 00h */ | ||
68 | uint32_t phase; /* 04h */ | ||
69 | int32_t last_sample[2]; /* 08h */ | ||
70 | /* 10h */ | ||
71 | }; | ||
72 | |||
73 | /* This is for passing needed data to external dsp routines. If another | ||
74 | * dsp parameter needs to be passed, add to the end of the structure | ||
75 | * and remove from dsp_config. | ||
76 | * If another function type becomes assembly/external and requires dsp | ||
77 | * config info, add a pointer paramter of type "struct dsp_data *". | ||
78 | * If removing something from other than the end, reserve the spot or | ||
79 | * else update every implementation for every target. | ||
80 | * Be sure to add the offset of the new member for easy viewing as well. :) | ||
81 | * It is the first member of dsp_config and all members can be accessesed | ||
82 | * through the main aggregate but this is intended to make a safe haven | ||
83 | * for these items whereas the c part can be rearranged at will. dsp_data | ||
84 | * could even moved within dsp_config without disurbing the order. | ||
85 | */ | ||
86 | struct dsp_data | ||
87 | { | ||
88 | int output_scale; /* 00h */ | ||
89 | int num_channels; /* 04h */ | ||
90 | struct resample_data resample_data; /* 08h */ | ||
91 | int32_t clip_min; /* 18h */ | ||
92 | int32_t clip_max; /* 1ch */ | ||
93 | int32_t gain; /* 20h - Note that this is in S8.23 format. */ | ||
94 | int frac_bits; /* 24h */ | ||
95 | /* 28h */ | ||
96 | }; | ||
97 | |||
98 | struct dsp_config; | ||
99 | |||
100 | int dsp_process(struct dsp_config *dsp, char *dest, | ||
101 | const char *src[], int count); | ||
102 | int dsp_input_count(struct dsp_config *dsp, int count); | ||
103 | int dsp_output_count(struct dsp_config *dsp, int count); | ||
104 | intptr_t dsp_configure(struct dsp_config *dsp, int setting, | ||
105 | intptr_t value); | ||
106 | int get_replaygain_mode(bool have_track_gain, bool have_album_gain); | ||
107 | void dsp_set_replaygain(void); | ||
108 | void dsp_set_crossfeed(bool enable); | ||
109 | void dsp_set_crossfeed_direct_gain(int gain); | ||
110 | void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, | ||
111 | long cutoff); | ||
112 | void dsp_set_eq(bool enable); | ||
113 | void dsp_set_eq_precut(int precut); | ||
114 | void dsp_set_eq_coefs(int band); | ||
115 | void dsp_dither_enable(bool enable); | ||
116 | void dsp_timestretch_enable(bool enable); | ||
117 | bool dsp_timestretch_available(void); | ||
118 | void sound_set_pitch(int32_t r); | ||
119 | int32_t sound_get_pitch(void); | ||
120 | void dsp_set_timestretch(int32_t percent); | ||
121 | int32_t dsp_get_timestretch(void); | ||
122 | int dsp_callback(int msg, intptr_t param); | ||
123 | void dsp_set_compressor(void); | ||
124 | |||
125 | #endif | ||
diff --git a/lib/rbcodec/dsp/dsp_arm.S b/lib/rbcodec/dsp/dsp_arm.S new file mode 100644 index 0000000000..7e360749a3 --- /dev/null +++ b/lib/rbcodec/dsp/dsp_arm.S | |||
@@ -0,0 +1,561 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | |||
23 | /**************************************************************************** | ||
24 | * void channels_process_sound_chan_mono(int count, int32_t *buf[]) | ||
25 | */ | ||
26 | |||
27 | #include "config.h" | ||
28 | |||
29 | .section .icode, "ax", %progbits | ||
30 | .align 2 | ||
31 | .global channels_process_sound_chan_mono | ||
32 | .type channels_process_sound_chan_mono, %function | ||
33 | channels_process_sound_chan_mono: | ||
34 | @ input: r0 = count, r1 = buf | ||
35 | stmfd sp!, { r4, lr } @ | ||
36 | @ | ||
37 | ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1] | ||
38 | subs r0, r0, #1 @ odd: end at 0; even: end at -1 | ||
39 | beq .mono_singlesample @ Zero? Only one sample! | ||
40 | @ | ||
41 | .monoloop: @ | ||
42 | ldmia r1, { r3, r4 } @ r3, r4 = Li0, Li1 | ||
43 | ldmia r2, { r12, r14 } @ r12, r14 = Ri0, Ri1 | ||
44 | mov r3, r3, asr #1 @ Mo0 = Li0 / 2 + Ri0 / 2 | ||
45 | mov r4, r4, asr #1 @ Mo1 = Li1 / 2 + Ri1 / 2 | ||
46 | add r12, r3, r12, asr #1 @ | ||
47 | add r14, r4, r14, asr #1 @ | ||
48 | subs r0, r0, #2 @ | ||
49 | stmia r1!, { r12, r14 } @ store Mo0, Mo1 | ||
50 | stmia r2!, { r12, r14 } @ store Mo0, Mo1 | ||
51 | bgt .monoloop @ | ||
52 | @ | ||
53 | ldmpc cond=lt, regs=r4 @ if count was even, we're done | ||
54 | @ | ||
55 | .mono_singlesample: @ | ||
56 | ldr r3, [r1] @ r3 = Ls | ||
57 | ldr r12, [r2] @ r12 = Rs | ||
58 | mov r3, r3, asr #1 @ Mo = Ls / 2 + Rs / 2 | ||
59 | add r12, r3, r12, asr #1 @ | ||
60 | str r12, [r1] @ store Mo | ||
61 | str r12, [r2] @ store Mo | ||
62 | @ | ||
63 | ldmpc regs=r4 @ | ||
64 | .size channels_process_sound_chan_mono, \ | ||
65 | .-channels_process_sound_chan_mono | ||
66 | |||
67 | /**************************************************************************** | ||
68 | * void channels_process_sound_chan_custom(int count, int32_t *buf[]) | ||
69 | */ | ||
70 | .section .icode, "ax", %progbits | ||
71 | .align 2 | ||
72 | .global channels_process_sound_chan_custom | ||
73 | .type channels_process_sound_chan_custom, %function | ||
74 | channels_process_sound_chan_custom: | ||
75 | stmfd sp!, { r4-r10, lr } | ||
76 | |||
77 | ldr r3, =dsp_sw_gain | ||
78 | ldr r4, =dsp_sw_cross | ||
79 | |||
80 | ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1] | ||
81 | ldr r3, [r3] @ r3 = dsp_sw_gain | ||
82 | ldr r4, [r4] @ r4 = dsp_sw_cross | ||
83 | |||
84 | subs r0, r0, #1 | ||
85 | beq .custom_single_sample @ Zero? Only one sample! | ||
86 | |||
87 | .custom_loop: | ||
88 | ldmia r1, { r5, r6 } @ r5 = Li0, r6 = Li1 | ||
89 | ldmia r2, { r7, r8 } @ r7 = Ri0, r8 = Ri1 | ||
90 | |||
91 | subs r0, r0, #2 | ||
92 | |||
93 | smull r9, r10, r5, r3 @ Lc0 = Li0*gain | ||
94 | smull r12, r14, r7, r3 @ Rc0 = Ri0*gain | ||
95 | smlal r9, r10, r7, r4 @ Lc0 += Ri0*cross | ||
96 | smlal r12, r14, r5, r4 @ Rc0 += Li0*cross | ||
97 | |||
98 | mov r9, r9, lsr #31 @ Convert to s0.31 | ||
99 | mov r12, r12, lsr #31 | ||
100 | orr r5, r9, r10, asl #1 | ||
101 | orr r7, r12, r14, asl #1 | ||
102 | |||
103 | smull r9, r10, r6, r3 @ Lc1 = Li1*gain | ||
104 | smull r12, r14, r8, r3 @ Rc1 = Ri1*gain | ||
105 | smlal r9, r10, r8, r4 @ Lc1 += Ri1*cross | ||
106 | smlal r12, r14, r6, r4 @ Rc1 += Li1*cross | ||
107 | |||
108 | mov r9, r9, lsr #31 @ Convert to s0.31 | ||
109 | mov r12, r12, lsr #31 | ||
110 | orr r6, r9, r10, asl #1 | ||
111 | orr r8, r12, r14, asl #1 | ||
112 | |||
113 | stmia r1!, { r5, r6 } @ Store Lc0, Lc1 | ||
114 | stmia r2!, { r7, r8 } @ Store Rc0, Rc1 | ||
115 | |||
116 | bgt .custom_loop | ||
117 | |||
118 | ldmpc cond=lt, regs=r4-r10 @ < 0? even count | ||
119 | |||
120 | .custom_single_sample: | ||
121 | ldr r5, [r1] @ handle odd sample | ||
122 | ldr r7, [r2] | ||
123 | |||
124 | smull r9, r10, r5, r3 @ Lc0 = Li0*gain | ||
125 | smull r12, r14, r7, r3 @ Rc0 = Ri0*gain | ||
126 | smlal r9, r10, r7, r4 @ Lc0 += Ri0*cross | ||
127 | smlal r12, r14, r5, r4 @ Rc0 += Li0*cross | ||
128 | |||
129 | mov r9, r9, lsr #31 @ Convert to s0.31 | ||
130 | mov r12, r12, lsr #31 | ||
131 | orr r5, r9, r10, asl #1 | ||
132 | orr r7, r12, r14, asl #1 | ||
133 | |||
134 | str r5, [r1] @ Store Lc0 | ||
135 | str r7, [r2] @ Store Rc0 | ||
136 | |||
137 | ldmpc regs=r4-r10 | ||
138 | .size channels_process_sound_chan_custom, \ | ||
139 | .-channels_process_sound_chan_custom | ||
140 | |||
141 | /**************************************************************************** | ||
142 | * void channels_process_sound_chan_karaoke(int count, int32_t *buf[]) | ||
143 | */ | ||
144 | .section .icode, "ax", %progbits | ||
145 | .align 2 | ||
146 | .global channels_process_sound_chan_karaoke | ||
147 | .type channels_process_sound_chan_karaoke, %function | ||
148 | channels_process_sound_chan_karaoke: | ||
149 | @ input: r0 = count, r1 = buf | ||
150 | stmfd sp!, { r4, lr } @ | ||
151 | @ | ||
152 | ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1] | ||
153 | subs r0, r0, #1 @ odd: end at 0; even: end at -1 | ||
154 | beq .karaoke_singlesample @ Zero? Only one sample! | ||
155 | @ | ||
156 | .karaokeloop: @ | ||
157 | ldmia r1, { r3, r4 } @ r3, r4 = Li0, Li1 | ||
158 | ldmia r2, { r12, r14 } @ r12, r14 = Ri0, Ri1 | ||
159 | mov r3, r3, asr #1 @ Lo0 = Li0 / 2 - Ri0 / 2 | ||
160 | mov r4, r4, asr #1 @ Lo1 = Li1 / 2 - Ri1 / 2 | ||
161 | sub r3, r3, r12, asr #1 @ | ||
162 | sub r4, r4, r14, asr #1 @ | ||
163 | rsb r12, r3, #0 @ Ro0 = -Lk0 = Rs0 / 2 - Ls0 / 2 | ||
164 | rsb r14, r4, #0 @ Ro1 = -Lk1 = Ri1 / 2 - Li1 / 2 | ||
165 | subs r0, r0, #2 @ | ||
166 | stmia r1!, { r3, r4 } @ store Lo0, Lo1 | ||
167 | stmia r2!, { r12, r14 } @ store Ro0, Ro1 | ||
168 | bgt .karaokeloop @ | ||
169 | @ | ||
170 | ldmpc cond=lt, regs=r4 @ if count was even, we're done | ||
171 | @ | ||
172 | .karaoke_singlesample: @ | ||
173 | ldr r3, [r1] @ r3 = Li | ||
174 | ldr r12, [r2] @ r12 = Ri | ||
175 | mov r3, r3, asr #1 @ Lk = Li / 2 - Ri /2 | ||
176 | sub r3, r3, r12, asr #1 @ | ||
177 | rsb r12, r3, #0 @ Rk = -Lo = Ri / 2 - Li / 2 | ||
178 | str r3, [r1] @ store Lo | ||
179 | str r12, [r2] @ store Ro | ||
180 | @ | ||
181 | ldmpc regs=r4 @ | ||
182 | .size channels_process_sound_chan_karaoke, \ | ||
183 | .-channels_process_sound_chan_karaoke | ||
184 | |||
185 | #if ARM_ARCH < 6 | ||
186 | /**************************************************************************** | ||
187 | * void sample_output_mono(int count, struct dsp_data *data, | ||
188 | * const int32_t *src[], int16_t *dst) | ||
189 | */ | ||
190 | .section .icode, "ax", %progbits | ||
191 | .align 2 | ||
192 | .global sample_output_mono | ||
193 | .type sample_output_mono, %function | ||
194 | sample_output_mono: | ||
195 | @ input: r0 = count, r1 = data, r2 = src, r3 = dst | ||
196 | stmfd sp!, { r4-r6, lr } | ||
197 | |||
198 | ldr r1, [r1] @ lr = data->output_scale | ||
199 | ldr r2, [r2] @ r2 = src[0] | ||
200 | |||
201 | mov r4, #1 | ||
202 | mov r4, r4, lsl r1 @ r4 = 1 << (scale-1) | ||
203 | mov r4, r4, lsr #1 | ||
204 | mvn r14, #0x8000 @ r14 = 0xffff7fff, needed for | ||
205 | @ clipping and masking | ||
206 | subs r0, r0, #1 @ | ||
207 | beq .som_singlesample @ Zero? Only one sample! | ||
208 | |||
209 | .somloop: | ||
210 | ldmia r2!, { r5, r6 } | ||
211 | add r5, r5, r4 @ r6 = (r6 + 1<<(scale-1)) >> scale | ||
212 | mov r5, r5, asr r1 | ||
213 | mov r12, r5, asr #15 | ||
214 | teq r12, r12, asr #31 | ||
215 | eorne r5, r14, r5, asr #31 @ Clip (-32768...+32767) | ||
216 | add r6, r6, r4 | ||
217 | mov r6, r6, asr r1 @ r7 = (r7 + 1<<(scale-1)) >> scale | ||
218 | mov r12, r6, asr #15 | ||
219 | teq r12, r12, asr #31 | ||
220 | eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767) | ||
221 | |||
222 | and r5, r5, r14, lsr #16 | ||
223 | and r6, r6, r14, lsr #16 | ||
224 | orr r5, r5, r5, lsl #16 @ pack first 2 halfwords into 1 word | ||
225 | orr r6, r6, r6, lsl #16 @ pack last 2 halfwords into 1 word | ||
226 | stmia r3!, { r5, r6 } | ||
227 | |||
228 | subs r0, r0, #2 | ||
229 | bgt .somloop | ||
230 | |||
231 | ldmpc cond=lt, regs=r4-r6 @ even 'count'? return | ||
232 | |||
233 | .som_singlesample: | ||
234 | ldr r5, [r2] @ do odd sample | ||
235 | add r5, r5, r4 | ||
236 | mov r5, r5, asr r1 | ||
237 | mov r12, r5, asr #15 | ||
238 | teq r12, r12, asr #31 | ||
239 | eorne r5, r14, r5, asr #31 | ||
240 | |||
241 | and r5, r5, r14, lsr #16 @ pack 2 halfwords into 1 word | ||
242 | orr r5, r5, r5, lsl #16 | ||
243 | str r5, [r3] | ||
244 | |||
245 | ldmpc regs=r4-r6 | ||
246 | .size sample_output_mono, .-sample_output_mono | ||
247 | |||
248 | /**************************************************************************** | ||
249 | * void sample_output_stereo(int count, struct dsp_data *data, | ||
250 | * const int32_t *src[], int16_t *dst) | ||
251 | */ | ||
252 | .section .icode, "ax", %progbits | ||
253 | .align 2 | ||
254 | .global sample_output_stereo | ||
255 | .type sample_output_stereo, %function | ||
256 | sample_output_stereo: | ||
257 | @ input: r0 = count, r1 = data, r2 = src, r3 = dst | ||
258 | stmfd sp!, { r4-r9, lr } | ||
259 | |||
260 | ldr r1, [r1] @ r1 = data->output_scale | ||
261 | ldmia r2, { r2, r5 } @ r2 = src[0], r5 = src[1] | ||
262 | |||
263 | mov r4, #1 | ||
264 | mov r4, r4, lsl r1 @ r4 = 1 << (scale-1) | ||
265 | mov r4, r4, lsr #1 @ | ||
266 | |||
267 | mvn r14, #0x8000 @ r14 = 0xffff7fff, needed for | ||
268 | @ clipping and masking | ||
269 | subs r0, r0, #1 @ | ||
270 | beq .sos_singlesample @ Zero? Only one sample! | ||
271 | |||
272 | .sosloop: | ||
273 | ldmia r2!, { r6, r7 } @ 2 left | ||
274 | ldmia r5!, { r8, r9 } @ 2 right | ||
275 | |||
276 | add r6, r6, r4 @ r6 = (r6 + 1<<(scale-1)) >> scale | ||
277 | mov r6, r6, asr r1 | ||
278 | mov r12, r6, asr #15 | ||
279 | teq r12, r12, asr #31 | ||
280 | eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767) | ||
281 | add r7, r7, r4 | ||
282 | mov r7, r7, asr r1 @ r7 = (r7 + 1<<(scale-1)) >> scale | ||
283 | mov r12, r7, asr #15 | ||
284 | teq r12, r12, asr #31 | ||
285 | eorne r7, r14, r7, asr #31 @ Clip (-32768...+32767) | ||
286 | |||
287 | add r8, r8, r4 @ r8 = (r8 + 1<<(scale-1)) >> scale | ||
288 | mov r8, r8, asr r1 | ||
289 | mov r12, r8, asr #15 | ||
290 | teq r12, r12, asr #31 | ||
291 | eorne r8, r14, r8, asr #31 @ Clip (-32768...+32767) | ||
292 | add r9, r9, r4 @ r9 = (r9 + 1<<(scale-1)) >> scale | ||
293 | mov r9, r9, asr r1 | ||
294 | mov r12, r9, asr #15 | ||
295 | teq r12, r12, asr #31 | ||
296 | eorne r9, r14, r9, asr #31 @ Clip (-32768...+32767) | ||
297 | |||
298 | and r6, r6, r14, lsr #16 @ pack first 2 halfwords into 1 word | ||
299 | orr r8, r6, r8, asl #16 | ||
300 | and r7, r7, r14, lsr #16 @ pack last 2 halfwords into 1 word | ||
301 | orr r9, r7, r9, asl #16 | ||
302 | |||
303 | stmia r3!, { r8, r9 } | ||
304 | |||
305 | subs r0, r0, #2 | ||
306 | bgt .sosloop | ||
307 | |||
308 | ldmpc cond=lt, regs=r4-r9 @ even 'count'? return | ||
309 | |||
310 | .sos_singlesample: | ||
311 | ldr r6, [r2] @ left odd sample | ||
312 | ldr r8, [r5] @ right odd sample | ||
313 | |||
314 | add r6, r6, r4 @ r6 = (r7 + 1<<(scale-1)) >> scale | ||
315 | mov r6, r6, asr r1 | ||
316 | mov r12, r6, asr #15 | ||
317 | teq r12, r12, asr #31 | ||
318 | eorne r6, r14, r6, asr #31 @ Clip (-32768...+32767) | ||
319 | add r8, r8, r4 @ r8 = (r8 + 1<<(scale-1)) >> scale | ||
320 | mov r8, r8, asr r1 | ||
321 | mov r12, r8, asr #15 | ||
322 | teq r12, r12, asr #31 | ||
323 | eorne r8, r14, r8, asr #31 @ Clip (-32768...+32767) | ||
324 | |||
325 | and r6, r6, r14, lsr #16 @ pack 2 halfwords into 1 word | ||
326 | orr r8, r6, r8, asl #16 | ||
327 | |||
328 | str r8, [r3] | ||
329 | |||
330 | ldmpc regs=r4-r9 | ||
331 | .size sample_output_stereo, .-sample_output_stereo | ||
332 | #endif /* ARM_ARCH < 6 */ | ||
333 | |||
334 | /**************************************************************************** | ||
335 | * void apply_crossfeed(int count, int32_t* src[]) | ||
336 | */ | ||
337 | .section .text | ||
338 | .global apply_crossfeed | ||
339 | apply_crossfeed: | ||
340 | @ unfortunately, we ended up in a bit of a register squeeze here, and need | ||
341 | @ to keep the count on the stack :/ | ||
342 | stmdb sp!, { r4-r11, lr } @ stack modified regs | ||
343 | ldmia r1, { r2-r3 } @ r2 = src[0], r3 = src[1] | ||
344 | |||
345 | ldr r1, =crossfeed_data | ||
346 | ldmia r1!, { r4-r11 } @ load direct gain and filter data | ||
347 | mov r12, r0 @ better to ldm delay + count later | ||
348 | add r0, r1, #13*4*2 @ calculate end of delay | ||
349 | stmdb sp!, { r0, r12 } @ stack end of delay adr and count | ||
350 | ldr r0, [r1, #13*4*2] @ fetch current delay line address | ||
351 | |||
352 | /* Register usage in loop: | ||
353 | * r0 = &delay[index][0], r1 = accumulator high, r2 = src[0], r3 = src[1], | ||
354 | * r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs), | ||
355 | * r8-r11 = filter history, r12 = temp, r14 = accumulator low | ||
356 | */ | ||
357 | .cfloop: | ||
358 | smull r14, r1, r6, r8 @ acc = b1*dr[n - 1] | ||
359 | smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1] | ||
360 | ldr r8, [r0, #4] @ r8 = dr[n] | ||
361 | smlal r14, r1, r5, r8 @ acc += b0*dr[n] | ||
362 | mov r9, r1, lsl #1 @ fix format for filter history | ||
363 | ldr r12, [r2] @ load left input | ||
364 | smlal r14, r1, r4, r12 @ acc += gain*x_l[n] | ||
365 | mov r1, r1, lsl #1 @ fix format | ||
366 | str r1, [r2], #4 @ save result | ||
367 | |||
368 | smull r14, r1, r6, r10 @ acc = b1*dl[n - 1] | ||
369 | smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1] | ||
370 | ldr r10, [r0] @ r10 = dl[n] | ||
371 | str r12, [r0], #4 @ save left input to delay line | ||
372 | smlal r14, r1, r5, r10 @ acc += b0*dl[n] | ||
373 | mov r11, r1, lsl #1 @ fix format for filter history | ||
374 | ldr r12, [r3] @ load right input | ||
375 | smlal r14, r1, r4, r12 @ acc += gain*x_r[n] | ||
376 | str r12, [r0], #4 @ save right input to delay line | ||
377 | mov r1, r1, lsl #1 @ fix format | ||
378 | ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack | ||
379 | str r1, [r3], #4 @ save result | ||
380 | |||
381 | cmp r0, r12 @ need to wrap to start of delay? | ||
382 | subeq r0, r0, #13*4*2 @ wrap back delay line ptr to start | ||
383 | |||
384 | subs r14, r14, #1 @ are we finished? | ||
385 | strne r14, [sp, #4] @ nope, save count back to stack | ||
386 | bne .cfloop | ||
387 | |||
388 | @ save data back to struct | ||
389 | ldr r12, =crossfeed_data + 4*4 | ||
390 | stmia r12, { r8-r11 } @ save filter history | ||
391 | str r0, [r12, #30*4] @ save delay line index | ||
392 | add sp, sp, #8 @ remove temp variables from stack | ||
393 | ldmpc regs=r4-r11 | ||
394 | .size apply_crossfeed, .-apply_crossfeed | ||
395 | |||
396 | /**************************************************************************** | ||
397 | * int dsp_downsample(int count, struct dsp_data *data, | ||
398 | * in32_t *src[], int32_t *dst[]) | ||
399 | */ | ||
400 | .section .text | ||
401 | .global dsp_downsample | ||
402 | dsp_downsample: | ||
403 | stmdb sp!, { r4-r11, lr } @ stack modified regs | ||
404 | ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta | ||
405 | sub r5, r5, #1 @ pre-decrement num_channels for use | ||
406 | add r4, r1, #12 @ r4 = &resample_data.phase | ||
407 | mov r12, #0xff | ||
408 | orr r12, r12, #0xff00 @ r12 = 0xffff | ||
409 | .dschannel_loop: | ||
410 | ldr r1, [r4] @ r1 = resample_data.phase | ||
411 | ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1] | ||
412 | ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1] | ||
413 | add r9, r4, #4 @ r9 = &last_sample[0] | ||
414 | ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1] | ||
415 | sub r11, r0, #1 | ||
416 | ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ... | ||
417 | str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample | ||
418 | movs r9, r1, lsr #16 @ r9 = pos = phase >> 16 | ||
419 | ldreq r11, [r7] @ if pos = 0, load src[0] and jump into loop | ||
420 | beq .dsuse_last_start | ||
421 | cmp r9, r0 @ if pos >= count, we're already done | ||
422 | bge .dsloop_skip | ||
423 | |||
424 | @ Register usage in loop: | ||
425 | @ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel, | ||
426 | @ r6 = delta, r7 = s, r8 = d, r9 = pos, r10 = s[pos - 1], r11 = s[pos] | ||
427 | .dsloop: | ||
428 | add r9, r7, r9, lsl #2 @ r9 = &s[pos] | ||
429 | ldmda r9, { r10, r11 } @ r10 = s[pos - 1], r11 = s[pos] | ||
430 | .dsuse_last_start: | ||
431 | sub r11, r11, r10 @ r11 = diff = s[pos] - s[pos - 1] | ||
432 | @ keep frac in lower bits to take advantage of multiplier early termination | ||
433 | and r9, r1, r12 @ frac = phase & 0xffff | ||
434 | smull r9, r14, r11, r9 | ||
435 | add r1, r1, r6 @ phase += delta | ||
436 | add r10, r10, r9, lsr #16 @ r10 = out = s[pos - 1] + frac*diff | ||
437 | add r10, r10, r14, lsl #16 | ||
438 | str r10, [r8], #4 @ *d++ = out | ||
439 | mov r9, r1, lsr #16 @ pos = phase >> 16 | ||
440 | cmp r9, r0 @ pos < count? | ||
441 | blt .dsloop @ yup, do more samples | ||
442 | .dsloop_skip: | ||
443 | subs r5, r5, #1 | ||
444 | bpl .dschannel_loop @ if (--ch) >= 0, do another channel | ||
445 | sub r1, r1, r0, lsl #16 @ wrap phase back to start | ||
446 | str r1, [r4] @ store back | ||
447 | ldr r1, [r3] @ r1 = &dst[0] | ||
448 | sub r8, r8, r1 @ dst - &dst[0] | ||
449 | mov r0, r8, lsr #2 @ convert bytes->samples | ||
450 | ldmpc regs=r4-r11 @ ... and we're out | ||
451 | .size dsp_downsample, .-dsp_downsample | ||
452 | |||
453 | /**************************************************************************** | ||
454 | * int dsp_upsample(int count, struct dsp_data *dsp, | ||
455 | * in32_t *src[], int32_t *dst[]) | ||
456 | */ | ||
457 | .section .text | ||
458 | .global dsp_upsample | ||
459 | dsp_upsample: | ||
460 | stmfd sp!, { r4-r11, lr } @ stack modified regs | ||
461 | ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta | ||
462 | sub r5, r5, #1 @ pre-decrement num_channels for use | ||
463 | add r4, r1, #12 @ r4 = &resample_data.phase | ||
464 | mov r6, r6, lsl #16 @ we'll use carry to detect pos increments | ||
465 | stmfd sp!, { r0, r4 } @ stack count and &resample_data.phase | ||
466 | .uschannel_loop: | ||
467 | ldr r12, [r4] @ r12 = resample_data.phase | ||
468 | ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1] | ||
469 | ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1] | ||
470 | add r9, r4, #4 @ r9 = &last_sample[0] | ||
471 | mov r1, r12, lsl #16 @ we'll use carry to detect pos increments | ||
472 | sub r11, r0, #1 | ||
473 | ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ... | ||
474 | ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1] | ||
475 | str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample | ||
476 | movs r14, r12, lsr #16 @ pos = resample_data.phase >> 16 | ||
477 | beq .usstart_0 @ pos = 0 | ||
478 | cmp r14, r0 @ if pos >= count, we're already done | ||
479 | bge .usloop_skip | ||
480 | add r7, r7, r14, lsl #2 @ r7 = &s[pos] | ||
481 | ldr r10, [r7, #-4] @ r11 = s[pos - 1] | ||
482 | b .usstart_0 | ||
483 | |||
484 | @ Register usage in loop: | ||
485 | @ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel, | ||
486 | @ r6 = delta, r7 = s, r8 = d, r9 = diff, r10 = s[pos - 1], r11 = s[pos] | ||
487 | .usloop_1: | ||
488 | mov r10, r11 @ r10 = previous sample | ||
489 | .usstart_0: | ||
490 | ldr r11, [r7], #4 @ r11 = next sample | ||
491 | mov r4, r1, lsr #16 @ r4 = frac = phase >> 16 | ||
492 | sub r9, r11, r10 @ r9 = diff = s[pos] - s[pos - 1] | ||
493 | .usloop_0: | ||
494 | smull r12, r14, r4, r9 | ||
495 | adds r1, r1, r6 @ phase += delta << 16 | ||
496 | mov r4, r1, lsr #16 @ r4 = frac = phase >> 16 | ||
497 | add r14, r10, r14, lsl #16 | ||
498 | add r14, r14, r12, lsr #16 @ r14 = out = s[pos - 1] + frac*diff | ||
499 | str r14, [r8], #4 @ *d++ = out | ||
500 | bcc .usloop_0 @ if carry is set, pos is incremented | ||
501 | subs r0, r0, #1 @ if count > 0, do another sample | ||
502 | bgt .usloop_1 | ||
503 | .usloop_skip: | ||
504 | subs r5, r5, #1 | ||
505 | ldmfd sp, { r0, r4 } @ reload count and &resample_data.phase | ||
506 | bpl .uschannel_loop @ if (--ch) >= 0, do another channel | ||
507 | mov r1, r1, lsr #16 @ wrap phase back to start of next frame | ||
508 | ldr r2, [r3] @ r1 = &dst[0] | ||
509 | str r1, [r4] @ store phase | ||
510 | sub r8, r8, r2 @ dst - &dst[0] | ||
511 | mov r0, r8, lsr #2 @ convert bytes->samples | ||
512 | add sp, sp, #8 @ adjust stack for temp variables | ||
513 | ldmpc regs=r4-r11 @ ... and we're out | ||
514 | .size dsp_upsample, .-dsp_upsample | ||
515 | |||
516 | /**************************************************************************** | ||
517 | * void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]) | ||
518 | */ | ||
519 | .section .icode, "ax", %progbits | ||
520 | .align 2 | ||
521 | .global dsp_apply_gain | ||
522 | .type dsp_apply_gain, %function | ||
523 | dsp_apply_gain: | ||
524 | @ input: r0 = count, r1 = data, r2 = buf[] | ||
525 | stmfd sp!, { r4-r8, lr } | ||
526 | |||
527 | ldr r3, [r1, #4] @ r3 = data->num_channels | ||
528 | ldr r4, [r1, #32] @ r5 = data->gain | ||
529 | |||
530 | .dag_outerloop: | ||
531 | ldr r1, [r2], #4 @ r1 = buf[0] and increment index of buf[] | ||
532 | subs r12, r0, #1 @ r12 = r0 = count - 1 | ||
533 | beq .dag_singlesample @ Zero? Only one sample! | ||
534 | |||
535 | .dag_innerloop: | ||
536 | ldmia r1, { r5, r6 } @ load r5, r6 from r1 | ||
537 | smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8) | ||
538 | smull r14, r5, r6, r4 @ r14 = FRACMUL_SHL(r6, r4, 8) | ||
539 | subs r12, r12, #2 | ||
540 | mov r7, r7, lsr #23 | ||
541 | mov r14, r14, lsr #23 | ||
542 | orr r7, r7, r8, asl #9 | ||
543 | orr r14, r14, r5, asl #9 | ||
544 | stmia r1!, { r7, r14 } @ save r7, r14 to [r1] and increment r1 | ||
545 | bgt .dag_innerloop @ end of inner loop | ||
546 | |||
547 | blt .dag_evencount @ < 0? even count | ||
548 | |||
549 | .dag_singlesample: | ||
550 | ldr r5, [r1] @ handle odd sample | ||
551 | smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8) | ||
552 | mov r7, r7, lsr #23 | ||
553 | orr r7, r7, r8, asl #9 | ||
554 | str r7, [r1] | ||
555 | |||
556 | .dag_evencount: | ||
557 | subs r3, r3, #1 | ||
558 | bgt .dag_outerloop @ end of outer loop | ||
559 | |||
560 | ldmpc regs=r4-r8 | ||
561 | .size dsp_apply_gain, .-dsp_apply_gain | ||
diff --git a/lib/rbcodec/dsp/dsp_arm_v6.S b/lib/rbcodec/dsp/dsp_arm_v6.S new file mode 100644 index 0000000000..39949498ea --- /dev/null +++ b/lib/rbcodec/dsp/dsp_arm_v6.S | |||
@@ -0,0 +1,127 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /**************************************************************************** | ||
23 | * void sample_output_mono(int count, struct dsp_data *data, | ||
24 | * const int32_t *src[], int16_t *dst) | ||
25 | */ | ||
26 | .section .text, "ax", %progbits | ||
27 | .align 2 | ||
28 | .global sample_output_mono | ||
29 | .type sample_output_mono, %function | ||
30 | sample_output_mono: | ||
31 | @ input: r0 = count, r1 = data, r2 = src, r3 = dst | ||
32 | stmfd sp!, { r4, lr } @ | ||
33 | @ | ||
34 | ldr r1, [r1] @ r1 = data->output_scale | ||
35 | ldr r2, [r2] @ r2 = src[0] | ||
36 | @ | ||
37 | mov r4, #1 @ r4 = 1 << (scale - 1) | ||
38 | mov r4, r4, lsl r1 @ | ||
39 | subs r0, r0, #1 @ odd: end at 0; even: end at -1 | ||
40 | mov r4, r4, lsr #1 @ | ||
41 | beq 2f @ Zero? Only one sample! | ||
42 | @ | ||
43 | 1: @ | ||
44 | ldmia r2!, { r12, r14 } @ load Mi0, Mi1 | ||
45 | qadd r12, r12, r4 @ round, scale, saturate and | ||
46 | qadd r14, r14, r4 @ pack Mi0 to So0, Mi1 to So1 | ||
47 | mov r12, r12, asr r1 @ | ||
48 | mov r14, r14, asr r1 @ | ||
49 | ssat r12, #16, r12 @ | ||
50 | ssat r14, #16, r14 @ | ||
51 | pkhbt r12, r12, r12, asl #16 @ | ||
52 | pkhbt r14, r14, r14, asl #16 @ | ||
53 | subs r0, r0, #2 @ | ||
54 | stmia r3!, { r12, r14 } @ store So0, So1 | ||
55 | bgt 1b @ | ||
56 | @ | ||
57 | ldmltfd sp!, { r4, pc } @ if count was even, we're done | ||
58 | @ | ||
59 | 2: @ | ||
60 | ldr r12, [r2] @ round, scale, saturate | ||
61 | qadd r12, r12, r4 @ and pack Mi to So | ||
62 | mov r12, r12, asr r1 @ | ||
63 | ssat r12, #16, r12 @ | ||
64 | pkhbt r12, r12, r12, asl #16 @ | ||
65 | str r12, [r3] @ store So | ||
66 | @ | ||
67 | ldmfd sp!, { r4, pc } @ | ||
68 | .size sample_output_mono, .-sample_output_mono | ||
69 | |||
70 | /**************************************************************************** | ||
71 | * void sample_output_stereo(int count, struct dsp_data *data, | ||
72 | * const int32_t *src[], int16_t *dst) | ||
73 | */ | ||
74 | .section .text, "ax", %progbits | ||
75 | .align 2 | ||
76 | .global sample_output_stereo | ||
77 | .type sample_output_stereo, %function | ||
78 | sample_output_stereo: | ||
79 | @ input: r0 = count, r1 = data, r2 = src, r3 = dst | ||
80 | stmfd sp!, { r4-r7, lr } @ | ||
81 | @ | ||
82 | ldr r1, [r1] @ r1 = data->output_scale | ||
83 | ldmia r2, { r2, r4 } @ r2 = src[0], r4 = src[1] | ||
84 | @ | ||
85 | mov r5, #1 @ r5 = 1 << (scale - 1) | ||
86 | mov r5, r5, lsl r1 @ | ||
87 | subs r0, r0, #1 @ odd: end at 0; even: end at -1 | ||
88 | mov r5, r5, lsr #1 @ | ||
89 | beq 2f @ Zero? Only one sample! | ||
90 | @ | ||
91 | 1: @ | ||
92 | ldmia r2!, { r6, r7 } @ r6, r7 = Li0, Li1 | ||
93 | ldmia r4!, { r12, r14 } @ r12, r14 = Ri0, Ri1 | ||
94 | qadd r6, r6, r5 @ round, scale, saturate and pack | ||
95 | qadd r7, r7, r5 @ Li0+Ri0 to So0, Li1+Ri1 to So1 | ||
96 | qadd r12, r12, r5 @ | ||
97 | qadd r14, r14, r5 @ | ||
98 | mov r6, r6, asr r1 @ | ||
99 | mov r7, r7, asr r1 @ | ||
100 | mov r12, r12, asr r1 @ | ||
101 | mov r14, r14, asr r1 @ | ||
102 | ssat r6, #16, r6 @ | ||
103 | ssat r12, #16, r12 @ | ||
104 | ssat r7, #16, r7 @ | ||
105 | ssat r14, #16, r14 @ | ||
106 | pkhbt r6, r6, r12, asl #16 @ | ||
107 | pkhbt r7, r7, r14, asl #16 @ | ||
108 | subs r0, r0, #2 @ | ||
109 | stmia r3!, { r6, r7 } @ store So0, So1 | ||
110 | bgt 1b @ | ||
111 | @ | ||
112 | ldmltfd sp!, { r4-r7, pc } @ if count was even, we're done | ||
113 | @ | ||
114 | 2: @ | ||
115 | ldr r6, [r2] @ r6 = Li | ||
116 | ldr r12, [r4] @ r12 = Ri | ||
117 | qadd r6, r6, r5 @ round, scale, saturate | ||
118 | qadd r12, r12, r5 @ and pack Li+Ri to So | ||
119 | mov r6, r6, asr r1 @ | ||
120 | mov r12, r12, asr r1 @ | ||
121 | ssat r6, #16, r6 @ | ||
122 | ssat r12, #16, r12 @ | ||
123 | pkhbt r6, r6, r12, asl #16 @ | ||
124 | str r6, [r3] @ store So | ||
125 | @ | ||
126 | ldmfd sp!, { r4-r7, pc } @ | ||
127 | .size sample_output_stereo, .-sample_output_stereo | ||
diff --git a/lib/rbcodec/dsp/dsp_asm.h b/lib/rbcodec/dsp/dsp_asm.h new file mode 100644 index 0000000000..7bf18370a3 --- /dev/null +++ b/lib/rbcodec/dsp/dsp_asm.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <config.h> | ||
23 | |||
24 | #ifndef _DSP_ASM_H | ||
25 | #define _DSP_ASM_H | ||
26 | |||
27 | /* Set the appropriate #defines based on CPU or whatever matters */ | ||
28 | #if defined(CPU_ARM) | ||
29 | #define DSP_HAVE_ASM_APPLY_GAIN | ||
30 | #define DSP_HAVE_ASM_RESAMPLING | ||
31 | #define DSP_HAVE_ASM_CROSSFEED | ||
32 | #define DSP_HAVE_ASM_SOUND_CHAN_MONO | ||
33 | #define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM | ||
34 | #define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE | ||
35 | #define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO | ||
36 | #define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO | ||
37 | #elif defined (CPU_COLDFIRE) | ||
38 | #define DSP_HAVE_ASM_APPLY_GAIN | ||
39 | #define DSP_HAVE_ASM_RESAMPLING | ||
40 | #define DSP_HAVE_ASM_CROSSFEED | ||
41 | #define DSP_HAVE_ASM_SOUND_CHAN_MONO | ||
42 | #define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM | ||
43 | #define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE | ||
44 | #define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO | ||
45 | #define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO | ||
46 | #endif /* CPU_COLDFIRE */ | ||
47 | |||
48 | /* Declare prototypes based upon what's #defined above */ | ||
49 | #ifdef DSP_HAVE_ASM_CROSSFEED | ||
50 | void apply_crossfeed(int count, int32_t *buf[]); | ||
51 | #endif | ||
52 | |||
53 | #ifdef DSP_HAVE_ASM_APPLY_GAIN | ||
54 | void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]); | ||
55 | #endif /* DSP_HAVE_ASM_APPLY_GAIN* */ | ||
56 | |||
57 | #ifdef DSP_HAVE_ASM_RESAMPLING | ||
58 | int dsp_upsample(int count, struct dsp_data *data, | ||
59 | const int32_t *src[], int32_t *dst[]); | ||
60 | int dsp_downsample(int count, struct dsp_data *data, | ||
61 | const int32_t *src[], int32_t *dst[]); | ||
62 | #endif /* DSP_HAVE_ASM_RESAMPLING */ | ||
63 | |||
64 | #ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO | ||
65 | void channels_process_sound_chan_mono(int count, int32_t *buf[]); | ||
66 | #endif | ||
67 | |||
68 | #ifdef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM | ||
69 | void channels_process_sound_chan_custom(int count, int32_t *buf[]); | ||
70 | #endif | ||
71 | |||
72 | #ifdef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE | ||
73 | void channels_process_sound_chan_karaoke(int count, int32_t *buf[]); | ||
74 | #endif | ||
75 | |||
76 | #ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO | ||
77 | void sample_output_stereo(int count, struct dsp_data *data, | ||
78 | const int32_t *src[], int16_t *dst); | ||
79 | #endif | ||
80 | |||
81 | #ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO | ||
82 | void sample_output_mono(int count, struct dsp_data *data, | ||
83 | const int32_t *src[], int16_t *dst); | ||
84 | #endif | ||
85 | |||
86 | #endif /* _DSP_ASM_H */ | ||
diff --git a/lib/rbcodec/dsp/dsp_cf.S b/lib/rbcodec/dsp/dsp_cf.S new file mode 100644 index 0000000000..cda811a7d5 --- /dev/null +++ b/lib/rbcodec/dsp/dsp_cf.S | |||
@@ -0,0 +1,611 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Thom Johansen | ||
11 | * Portions Copyright (C) 2007 Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | /**************************************************************************** | ||
24 | * void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]) | ||
25 | */ | ||
26 | .section .text | ||
27 | .align 2 | ||
28 | .global dsp_apply_gain | ||
29 | dsp_apply_gain: | ||
30 | lea.l -20(%sp), %sp | save registers | ||
31 | movem.l %d2-%d4/%a2-%a3, (%sp) | | ||
32 | movem.l 28(%sp), %a0-%a1 | %a0 = data, | ||
33 | | %a1 = buf | ||
34 | move.l 4(%a0), %d1 | %d1 = data->num_channels | ||
35 | move.l 32(%a0), %a0 | %a0 = data->gain (in s8.23) | ||
36 | 10: | channel loop | | ||
37 | move.l 24(%sp), %d0 | %d0 = count | ||
38 | move.l -4(%a1, %d1.l*4), %a2 | %a2 = s = buf[ch-1] | ||
39 | move.l %a2, %a3 | %a3 = d = s | ||
40 | move.l (%a2)+, %d2 | %d2 = *s++, | ||
41 | mac.l %a0, %d2, (%a2)+, %d2, %acc0 | %acc0 = S(n)*gain, load S(n+1) | ||
42 | subq.l #1, %d0 | --count > 0 ? : effectively n++ | ||
43 | ble.b 30f | loop done | no? finish up | ||
44 | 20: | loop | | ||
45 | move.l %accext01, %d4 | fetch S(n-1)[7:0] | ||
46 | movclr.l %acc0, %d3 | fetch S(n-1)[40:8] in %d5[31:0] | ||
47 | asl.l #8, %d3 | *s++ = (S(n-1)[40:8] << 8) | S(n-1)[7:0] | ||
48 | mac.l %a0, %d2, (%a2)+, %d2, %acc0 | %acc0 = S(n)*gain, load S(n+1) | ||
49 | move.b %d4, %d3 | | ||
50 | move.l %d3, (%a3)+ | | ||
51 | subq.l #1, %d0 | --count > 0 ? : effectively n++ | ||
52 | bgt.b 20b | loop | yes? do more samples | ||
53 | 30: | loop done | | ||
54 | move.l %accext01, %d4 | fetch S(n-1)[7:0] | ||
55 | movclr.l %acc0, %d3 | fetch S(n-1)[40:8] in %d5[31:0] | ||
56 | asl.l #8, %d3 | *s = (S(n-1)[40:8] << 8) | S(n-1)[7:0] | ||
57 | move.b %d4, %d3 | | ||
58 | move.l %d3, (%a3) | | ||
59 | subq.l #1, %d1 | next channel | ||
60 | bgt.b 10b | channel loop | | ||
61 | movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers | ||
62 | lea.l 20(%sp), %sp | cleanup stack | ||
63 | rts | | ||
64 | .size dsp_apply_gain,.-dsp_apply_gain | ||
65 | |||
66 | /**************************************************************************** | ||
67 | * void apply_crossfeed(int count, int32_t *buf[]) | ||
68 | */ | ||
69 | .section .text | ||
70 | .align 2 | ||
71 | .global apply_crossfeed | ||
72 | apply_crossfeed: | ||
73 | lea.l -44(%sp), %sp | | ||
74 | movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs | ||
75 | movem.l 48(%sp), %d7/%a4 | %d7 = count, %a4 = src | ||
76 | movem.l (%a4), %a4-%a5 | %a4 = src[0], %a5 = src[1] | ||
77 | lea.l crossfeed_data, %a1 | %a1 = &crossfeed_data | ||
78 | move.l (%a1)+, %d6 | %d6 = direct gain | ||
79 | movem.l 12(%a1), %d0-%d3 | fetch filter history samples | ||
80 | move.l 132(%a1), %a0 | fetch delay line address | ||
81 | movem.l (%a1), %a1-%a3 | load filter coefs | ||
82 | lea.l crossfeed_data+136, %a6 | %a6 = delay line wrap limit | ||
83 | bra.b 20f | loop start | go to loop start point | ||
84 | /* Register usage in loop: | ||
85 | * %a0 = delay_p, %a1..%a3 = b0, b1, a1 (filter coefs), | ||
86 | * %a4 = buf[0], %a5 = buf[1], | ||
87 | * %a6 = delay line pointer wrap limit, | ||
88 | * %d0..%d3 = history | ||
89 | * %d4..%d5 = temp. | ||
90 | * %d6 = direct gain, | ||
91 | * %d7 = count | ||
92 | */ | ||
93 | 10: | loop | | ||
94 | movclr.l %acc0, %d4 | write outputs | ||
95 | move.l %d4, (%a4)+ | . | ||
96 | movclr.l %acc1, %d5 | . | ||
97 | move.l %d5, (%a5)+ | . | ||
98 | 20: | loop start | | ||
99 | mac.l %a2, %d0, (%a0)+, %d0, %acc0 | %acc0 = b1*dl[n - 1], %d0 = dl[n] | ||
100 | mac.l %a1, %d0 , %acc0 | %acc0 += b0*dl[n] | ||
101 | mac.l %a3, %d1, (%a5), %d5, %acc0 | %acc0 += a1*y_r[n - 1], load R | ||
102 | mac.l %a2, %d2, (%a0)+, %d2, %acc1 | %acc1 = b1*dr[n - 1], %d2 = dr[n] | ||
103 | mac.l %a1, %d2 , %acc1 | %acc1 += b0*dr[n] | ||
104 | mac.l %a3, %d3, (%a4), %d4, %acc1 | %acc1 += a1*y_l[n - 1], load L | ||
105 | movem.l %d4-%d5, -8(%a0) | save left & right inputs to delay line | ||
106 | move.l %acc0, %d3 | get filtered delayed left sample (y_l[n]) | ||
107 | move.l %acc1, %d1 | get filtered delayed right sample (y_r[n]) | ||
108 | mac.l %d6, %d4, %acc0 | %acc0 += gain*x_l[n] | ||
109 | mac.l %d6, %d5, %acc1 | %acc1 += gain*x_r[n] | ||
110 | cmp.l %a6, %a0 | wrap %a0 if passed end | ||
111 | bhs.b 30f | wrap buffer | | ||
112 | .word 0x51fb | tpf.l | trap the buffer wrap | ||
113 | 30: | wrap buffer | ...fwd taken branches more costly | ||
114 | lea.l -104(%a0), %a0 | wrap it up | ||
115 | subq.l #1, %d7 | --count > 0 ? | ||
116 | bgt.b 10b | loop | yes? do more | ||
117 | movclr.l %acc0, %d4 | write last outputs | ||
118 | move.l %d4, (%a4) | . | ||
119 | movclr.l %acc1, %d5 | . | ||
120 | move.l %d5, (%a5) | . | ||
121 | lea.l crossfeed_data+16, %a1 | save data back to struct | ||
122 | movem.l %d0-%d3, (%a1) | ...history | ||
123 | move.l %a0, 120(%a1) | ...delay_p | ||
124 | movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs | ||
125 | lea.l 44(%sp), %sp | | ||
126 | rts | | ||
127 | .size apply_crossfeed,.-apply_crossfeed | ||
128 | |||
129 | /**************************************************************************** | ||
130 | * int dsp_downsample(int count, struct dsp_data *data, | ||
131 | * in32_t *src[], int32_t *dst[]) | ||
132 | */ | ||
133 | .section .text | ||
134 | .align 2 | ||
135 | .global dsp_downsample | ||
136 | dsp_downsample: | ||
137 | lea.l -40(%sp), %sp | save non-clobberables | ||
138 | movem.l %d2-%d7/%a2-%a5, (%sp) | | ||
139 | movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count | ||
140 | | %a0 = data | ||
141 | | %a1 = src | ||
142 | | %a2 = dst | ||
143 | movem.l 4(%a0), %d3-%d4 | %d3 = ch = data->num_channels | ||
144 | | %d4 = delta = data->resample_data.delta | ||
145 | moveq.l #16, %d7 | %d7 = shift | ||
146 | 10: | channel loop | | ||
147 | move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase | ||
148 | move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1] | ||
149 | move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1] | ||
150 | lea.l 12(%a0, %d3.l*4), %a5 | %a5 = &data->resample_data.ast_sample[ch-1] | ||
151 | move.l (%a5), %d0 | %d0 = last = data->resample_data.last_sample[ch-1] | ||
152 | move.l -4(%a3, %d2.l*4), (%a5) | data->resample_data.last_sample[ch-1] = s[count-1] | ||
153 | move.l %d5, %d6 | %d6 = pos = phase >> 16 | ||
154 | lsr.l %d7, %d6 | | ||
155 | cmp.l %d2, %d6 | past end of samples? | ||
156 | bge.b 40f | skip resample loop| yes? skip loop | ||
157 | tst.l %d6 | need last sample of prev. frame? | ||
158 | bne.b 20f | resample loop | no? start main loop | ||
159 | move.l (%a3, %d6.l*4), %d1 | %d1 = s[pos] | ||
160 | bra.b 30f | resample start last | start with last (last in %d0) | ||
161 | 20: | resample loop | | ||
162 | lea.l -4(%a3, %d6.l*4), %a5 | load s[pos-1] and s[pos] | ||
163 | movem.l (%a5), %d0-%d1 | | ||
164 | 30: | resample start last | | ||
165 | sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1] | ||
166 | move.l %d0, %acc0 | %acc0 = previous sample | ||
167 | move.l %d5, %d0 | frac = (phase << 16) >> 1 | ||
168 | lsl.l %d7, %d0 | | ||
169 | lsr.l #1, %d0 | | ||
170 | mac.l %d0, %d1, %acc0 | %acc0 += frac * diff | ||
171 | add.l %d4, %d5 | phase += delta | ||
172 | move.l %d5, %d6 | pos = phase >> 16 | ||
173 | lsr.l %d7, %d6 | | ||
174 | movclr.l %acc0, %d0 | | ||
175 | move.l %d0, (%a4)+ | *d++ = %d0 | ||
176 | cmp.l %d2, %d6 | pos < count? | ||
177 | blt.b 20b | resample loop | yes? continue resampling | ||
178 | 40: | skip resample loop | | ||
179 | subq.l #1, %d3 | ch > 0? | ||
180 | bgt.b 10b | channel loop | yes? process next channel | ||
181 | lsl.l %d7, %d2 | wrap phase to start of next frame | ||
182 | sub.l %d2, %d5 | data->resample_data.phase = | ||
183 | move.l %d5, 12(%a0) | ... phase - (count << 16) | ||
184 | move.l %a4, %d0 | return d - d[0] | ||
185 | sub.l (%a2), %d0 | | ||
186 | asr.l #2, %d0 | convert bytes->samples | ||
187 | movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables | ||
188 | lea.l 40(%sp), %sp | cleanup stack | ||
189 | rts | buh-bye | ||
190 | .size dsp_downsample,.-dsp_downsample | ||
191 | |||
192 | /**************************************************************************** | ||
193 | * int dsp_upsample(int count, struct dsp_data *dsp, | ||
194 | * const int32_t *src[], int32_t *dst[]) | ||
195 | */ | ||
196 | .section .text | ||
197 | .align 2 | ||
198 | .global dsp_upsample | ||
199 | dsp_upsample: | ||
200 | lea.l -40(%sp), %sp | save non-clobberables | ||
201 | movem.l %d2-%d7/%a2-%a5, (%sp) | | ||
202 | movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count | ||
203 | | %a0 = data | ||
204 | | %a1 = src | ||
205 | | %a2 = dst | ||
206 | movem.l 4(%a0), %d3-%d4 | %d3 = ch = channels | ||
207 | | %d4 = delta = data->resample_data.delta | ||
208 | swap %d4 | swap delta to high word to use... | ||
209 | | ...carries to increment position | ||
210 | 10: | channel loop | | ||
211 | move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase | ||
212 | move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1] | ||
213 | lea.l 12(%a0, %d3.l*4), %a4 | %a4 = &data->resample_data.last_sample[ch-1] | ||
214 | lea.l -4(%a3, %d2.l*4), %a5 | %a5 = src_end = &src[count-1] | ||
215 | move.l (%a4), %d0 | %d0 = last = data->resample_data.last_sample[ch-1] | ||
216 | move.l (%a5), (%a4) | data->resample_data.last_sample[ch-1] = s[count-1] | ||
217 | move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1] | ||
218 | move.l (%a3)+, %d1 | fetch first sample - might throw this... | ||
219 | | ...away later but we'll be preincremented | ||
220 | move.l %d1, %d6 | save sample value | ||
221 | sub.l %d0, %d1 | %d1 = diff = s[0] - last | ||
222 | swap %d5 | swap phase to high word to use | ||
223 | | carries to increment position | ||
224 | move.l %d5, %d7 | %d7 = pos = phase >> 16 | ||
225 | clr.w %d5 | | ||
226 | eor.l %d5, %d7 | pos == 0? | ||
227 | beq.b 40f | loop start | yes? start loop | ||
228 | cmp.l %d2, %d7 | past end of samples? | ||
229 | bge.b 50f | skip resample loop| yes? go to next channel and collect info | ||
230 | lea.l (%a3, %d7.l*4), %a3 | %a3 = s = &s[pos+1] | ||
231 | movem.l -8(%a3), %d0-%d1 | %d0 = s[pos-1], %d1 = s[pos] | ||
232 | move.l %d1, %d6 | save sample value | ||
233 | sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1] | ||
234 | bra.b 40f | loop start | | ||
235 | 20: | next sample loop | | ||
236 | move.l %d6, %d0 | move previous sample to %d0 | ||
237 | move.l (%a3)+, %d1 | fetch next sample | ||
238 | move.l %d1, %d6 | save sample value | ||
239 | sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1] | ||
240 | 30: | same sample loop | | ||
241 | movclr.l %acc0, %d7 | %d7 = result | ||
242 | move.l %d7, (%a4)+ | *d++ = %d7 | ||
243 | 40: | loop start | | ||
244 | lsr.l #1, %d5 | make phase into frac | ||
245 | move.l %d0, %acc0 | %acc0 = s[pos-1] | ||
246 | mac.l %d1, %d5, %acc0 | %acc0 = diff * frac | ||
247 | lsl.l #1, %d5 | restore frac to phase | ||
248 | add.l %d4, %d5 | phase += delta | ||
249 | bcc.b 30b | same sample loop | load next values? | ||
250 | cmp.l %a5, %a3 | src <= src_end? | ||
251 | bls.b 20b | next sample loop | yes? continue resampling | ||
252 | movclr.l %acc0, %d7 | %d7 = result | ||
253 | move.l %d7, (%a4)+ | *d++ = %d7 | ||
254 | 50: | skip resample loop | | ||
255 | subq.l #1, %d3 | ch > 0? | ||
256 | bgt.b 10b | channel loop | yes? process next channel | ||
257 | swap %d5 | wrap phase to start of next frame | ||
258 | move.l %d5, 12(%a0) | ...and save in data->resample_data.phase | ||
259 | move.l %a4, %d0 | return d - d[0] | ||
260 | sub.l (%a2), %d0 | | ||
261 | movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables | ||
262 | asr.l #2, %d0 | convert bytes->samples | ||
263 | lea.l 40(%sp), %sp | cleanup stack | ||
264 | rts | buh-bye | ||
265 | .size dsp_upsample,.-dsp_upsample | ||
266 | |||
267 | /**************************************************************************** | ||
268 | * void channels_process_sound_chan_mono(int count, int32_t *buf[]) | ||
269 | * | ||
270 | * Mix left and right channels 50/50 into a center channel. | ||
271 | */ | ||
272 | .section .text | ||
273 | .align 2 | ||
274 | .global channels_process_sound_chan_mono | ||
275 | channels_process_sound_chan_mono: | ||
276 | movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf | ||
277 | lea.l -20(%sp), %sp | save registers | ||
278 | movem.l %d2-%d4/%a2-%a3, (%sp) | | ||
279 | movem.l (%a0), %a0-%a1 | get channel pointers | ||
280 | move.l %a0, %a2 | use separate dst pointers since read | ||
281 | move.l %a1, %a3 | pointers run one ahead of write | ||
282 | move.l #0x40000000, %d3 | %d3 = 0.5 | ||
283 | move.l (%a0)+, %d1 | prime the input registers | ||
284 | move.l (%a1)+, %d2 | | ||
285 | mac.l %d1, %d3, (%a0)+, %d1, %acc0 | | ||
286 | mac.l %d2, %d3, (%a1)+, %d2, %acc0 | | ||
287 | subq.l #1, %d0 | | ||
288 | ble.s 20f | loop done | | ||
289 | 10: | loop | | ||
290 | movclr.l %acc0, %d4 | L = R = l/2 + r/2 | ||
291 | mac.l %d1, %d3, (%a0)+, %d1, %acc0 | | ||
292 | mac.l %d2, %d3, (%a1)+, %d2, %acc0 | | ||
293 | move.l %d4, (%a2)+ | output to original buffer | ||
294 | move.l %d4, (%a3)+ | | ||
295 | subq.l #1, %d0 | | ||
296 | bgt.s 10b | loop | | ||
297 | 20: | loop done | | ||
298 | movclr.l %acc0, %d4 | output last sample | ||
299 | move.l %d4, (%a2) | | ||
300 | move.l %d4, (%a3) | | ||
301 | movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers | ||
302 | lea.l 20(%sp), %sp | cleanup | ||
303 | rts | | ||
304 | .size channels_process_sound_chan_mono, \ | ||
305 | .-channels_process_sound_chan_mono | ||
306 | |||
307 | /**************************************************************************** | ||
308 | * void channels_process_sound_chan_custom(int count, int32_t *buf[]) | ||
309 | * | ||
310 | * Apply stereo width (narrowing/expanding) effect. | ||
311 | */ | ||
312 | .section .text | ||
313 | .align 2 | ||
314 | .global channels_process_sound_chan_custom | ||
315 | channels_process_sound_chan_custom: | ||
316 | movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf | ||
317 | lea.l -28(%sp), %sp | save registers | ||
318 | movem.l %d2-%d6/%a2-%a3, (%sp) | | ||
319 | movem.l (%a0), %a0-%a1 | get channel pointers | ||
320 | move.l %a0, %a2 | use separate dst pointers since read | ||
321 | move.l %a1, %a3 | pointers run one ahead of write | ||
322 | move.l dsp_sw_gain, %d3 | load straight (mid) gain | ||
323 | move.l dsp_sw_cross, %d4 | load cross (side) gain | ||
324 | move.l (%a0)+, %d1 | prime the input registers | ||
325 | move.l (%a1)+, %d2 | | ||
326 | mac.l %d1, %d3 , %acc0 | L = l*gain + r*cross | ||
327 | mac.l %d1, %d4, (%a0)+, %d1, %acc1 | R = r*gain + l*cross | ||
328 | mac.l %d2, %d4 , %acc0 | | ||
329 | mac.l %d2, %d3, (%a1)+, %d2, %acc1 | | ||
330 | subq.l #1, %d0 | | ||
331 | ble.b 20f | loop done | | ||
332 | 10: | loop | | ||
333 | movclr.l %acc0, %d5 | | ||
334 | movclr.l %acc1, %d6 | | ||
335 | mac.l %d1, %d3 , %acc0 | L = l*gain + r*cross | ||
336 | mac.l %d1, %d4, (%a0)+, %d1, %acc1 | R = r*gain + l*cross | ||
337 | mac.l %d2, %d4 , %acc0 | | ||
338 | mac.l %d2, %d3, (%a1)+, %d2, %acc1 | | ||
339 | move.l %d5, (%a2)+ | | ||
340 | move.l %d6, (%a3)+ | | ||
341 | subq.l #1, %d0 | | ||
342 | bgt.s 10b | loop | | ||
343 | 20: | loop done | | ||
344 | movclr.l %acc0, %d5 | output last sample | ||
345 | movclr.l %acc1, %d6 | | ||
346 | move.l %d5, (%a2) | | ||
347 | move.l %d6, (%a3) | | ||
348 | movem.l (%sp), %d2-%d6/%a2-%a3 | restore registers | ||
349 | lea.l 28(%sp), %sp | cleanup | ||
350 | rts | | ||
351 | .size channels_process_sound_chan_custom, \ | ||
352 | .-channels_process_sound_chan_custom | ||
353 | |||
354 | /**************************************************************************** | ||
355 | * void channels_process_sound_chan_karaoke(int count, int32_t *buf[]) | ||
356 | * | ||
357 | * Separate channels into side channels. | ||
358 | */ | ||
359 | .section .text | ||
360 | .align 2 | ||
361 | .global channels_process_sound_chan_karaoke | ||
362 | channels_process_sound_chan_karaoke: | ||
363 | movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf | ||
364 | lea.l -20(%sp), %sp | save registers | ||
365 | movem.l %d2-%d4/%a2-%a3, (%sp) | | ||
366 | movem.l (%a0), %a0-%a1 | get channel src pointers | ||
367 | move.l %a0, %a2 | use separate dst pointers since read | ||
368 | move.l %a1, %a3 | pointers run one ahead of write | ||
369 | move.l #0x40000000, %d3 | %d3 = 0.5 | ||
370 | move.l (%a0)+, %d1 | prime the input registers | ||
371 | move.l (%a1)+, %d2 | | ||
372 | mac.l %d1, %d3, (%a0)+, %d1, %acc0 | L = l/2 - r/2 | ||
373 | msac.l %d2, %d3, (%a1)+, %d2, %acc0 | | ||
374 | subq.l #1, %d0 | | ||
375 | ble.b 20f | loop done | | ||
376 | 10: | loop | | ||
377 | movclr.l %acc0, %d4 | | ||
378 | mac.l %d1, %d3, (%a0)+, %d1, %acc0 | L = l/2 - r/2 | ||
379 | msac.l %d2, %d3, (%a1)+, %d2, %acc0 | | ||
380 | move.l %d4, (%a2)+ | | ||
381 | neg.l %d4 | R = -L = -(l/2 - r/2) = r/2 - l/2 | ||
382 | move.l %d4, (%a3)+ | | ||
383 | subq.l #1, %d0 | | ||
384 | bgt.s 10b | loop | | ||
385 | 20: | loop done | | ||
386 | movclr.l %acc0, %d4 | output last sample | ||
387 | move.l %d4, (%a2) | | ||
388 | neg.l %d4 | R = -L = -(l/2 - r/2) = r/2 - l/2 | ||
389 | move.l %d4, (%a3) | | ||
390 | movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers | ||
391 | lea.l 20(%sp), %sp | cleanup | ||
392 | rts | | ||
393 | .size channels_process_sound_chan_karaoke, \ | ||
394 | .-channels_process_sound_chan_karaoke | ||
395 | |||
396 | /**************************************************************************** | ||
397 | * void sample_output_stereo(int count, struct dsp_data *data, | ||
398 | * const int32_t *src[], int16_t *dst) | ||
399 | * | ||
400 | * Framework based on the ubiquitous Rockbox line transfer logic for | ||
401 | * Coldfire CPUs. | ||
402 | * | ||
403 | * Does emac clamping and scaling (which proved faster than the usual | ||
404 | * checks and branches - even single test clamping) and writes using | ||
405 | * line burst transfers. Also better than writing a single L-R pair per | ||
406 | * loop but a good deal more code. | ||
407 | * | ||
408 | * Attemping bursting during reads is rather futile since the source and | ||
409 | * destination alignments rarely agree and too much complication will | ||
410 | * slow us up. The parallel loads seem to do a bit better at least until | ||
411 | * a pcm buffer can always give line aligned chunk and then aligning the | ||
412 | * dest can then imply the source is aligned if the source buffers are. | ||
413 | * For now longword alignment is assumed of both the source and dest. | ||
414 | * | ||
415 | */ | ||
416 | .section .text | ||
417 | .align 2 | ||
418 | .global sample_output_stereo | ||
419 | sample_output_stereo: | ||
420 | lea.l -48(%sp), %sp | save registers | ||
421 | move.l %macsr, %d1 | do it now as at many lines will | ||
422 | movem.l %d1-%d7/%a2-%a6, (%sp) | be the far more common condition | ||
423 | move.l #0x80, %macsr | put emac unit in signed int mode | ||
424 | movem.l 52(%sp), %a0-%a2/%a4 | | ||
425 | lea.l (%a4, %a0.l*4), %a0 | %a0 = end address | ||
426 | move.l (%a1), %d1 | %a1 = multiplier: (1 << (16 - scale)) | ||
427 | sub.l #16, %d1 | | ||
428 | neg.l %d1 | | ||
429 | moveq.l #1, %d0 | | ||
430 | asl.l %d1, %d0 | | ||
431 | move.l %d0, %a1 | | ||
432 | move.l #0x8000, %a6 | %a6 = rounding term | ||
433 | movem.l (%a2), %a2-%a3 | get L/R channel pointers | ||
434 | moveq.l #28, %d0 | %d0 = second line bound | ||
435 | add.l %a4, %d0 | | ||
436 | and.l #0xfffffff0, %d0 | | ||
437 | cmp.l %a0, %d0 | at least a full line? | ||
438 | bhi.w 40f | long loop 1 start | no? do as trailing longwords | ||
439 | sub.l #16, %d0 | %d1 = first line bound | ||
440 | cmp.l %a4, %d0 | any leading longwords? | ||
441 | bls.b 20f | line loop start | no? start line loop | ||
442 | 10: | long loop 0 | | ||
443 | move.l (%a2)+, %d1 | read longword from L and R | ||
444 | move.l %a6, %acc0 | | ||
445 | move.l %acc0, %acc1 | | ||
446 | mac.l %d1, %a1, (%a3)+, %d2, %acc0 | shift L to high word | ||
447 | mac.l %d2, %a1, %acc1 | shift R to high word | ||
448 | movclr.l %acc0, %d1 | get possibly saturated results | ||
449 | movclr.l %acc1, %d2 | | ||
450 | swap %d2 | move R to low word | ||
451 | move.w %d2, %d1 | interleave MS 16 bits of each | ||
452 | move.l %d1, (%a4)+ | ...and write both | ||
453 | cmp.l %a4, %d0 | | ||
454 | bhi.b 10b | long loop 0 | | ||
455 | 20: | line loop start | | ||
456 | lea.l -12(%a0), %a5 | %a5 = at or just before last line bound | ||
457 | 30: | line loop | | ||
458 | move.l (%a3)+, %d4 | get next 4 R samples and scale | ||
459 | move.l %a6, %acc0 | | ||
460 | move.l %acc0, %acc1 | | ||
461 | move.l %acc1, %acc2 | | ||
462 | move.l %acc2, %acc3 | | ||
463 | mac.l %d4, %a1, (%a3)+, %d5, %acc0 | with saturation | ||
464 | mac.l %d5, %a1, (%a3)+, %d6, %acc1 | | ||
465 | mac.l %d6, %a1, (%a3)+, %d7, %acc2 | | ||
466 | mac.l %d7, %a1, (%a2)+, %d0, %acc3 | | ||
467 | lea.l 16(%a4), %a4 | increment dest here, mitigate stalls | ||
468 | movclr.l %acc0, %d4 | obtain R results | ||
469 | movclr.l %acc1, %d5 | | ||
470 | movclr.l %acc2, %d6 | | ||
471 | movclr.l %acc3, %d7 | | ||
472 | move.l %a6, %acc0 | | ||
473 | move.l %acc0, %acc1 | | ||
474 | move.l %acc1, %acc2 | | ||
475 | move.l %acc2, %acc3 | | ||
476 | mac.l %d0, %a1, (%a2)+, %d1, %acc0 | get next 4 L samples and scale | ||
477 | mac.l %d1, %a1, (%a2)+, %d2, %acc1 | with saturation | ||
478 | mac.l %d2, %a1, (%a2)+, %d3, %acc2 | | ||
479 | mac.l %d3, %a1 , %acc3 | | ||
480 | swap %d4 | a) interleave most significant... | ||
481 | swap %d5 | | ||
482 | swap %d6 | | ||
483 | swap %d7 | | ||
484 | movclr.l %acc0, %d0 | obtain L results | ||
485 | movclr.l %acc1, %d1 | | ||
486 | movclr.l %acc2, %d2 | | ||
487 | movclr.l %acc3, %d3 | | ||
488 | move.w %d4, %d0 | a) ... 16 bits of L and R | ||
489 | move.w %d5, %d1 | | ||
490 | move.w %d6, %d2 | | ||
491 | move.w %d7, %d3 | | ||
492 | movem.l %d0-%d3, -16(%a4) | write four stereo samples | ||
493 | cmp.l %a4, %a5 | | ||
494 | bhi.b 30b | line loop | | ||
495 | 40: | long loop 1 start | | ||
496 | cmp.l %a4, %a0 | any longwords left? | ||
497 | bls.b 60f | output end | no? stop | ||
498 | 50: | long loop 1 | | ||
499 | move.l (%a2)+, %d1 | handle trailing longwords | ||
500 | move.l %a6, %acc0 | | ||
501 | move.l %acc0, %acc1 | | ||
502 | mac.l %d1, %a1, (%a3)+, %d2, %acc0 | the same way as leading ones | ||
503 | mac.l %d2, %a1, %acc1 | | ||
504 | movclr.l %acc0, %d1 | | ||
505 | movclr.l %acc1, %d2 | | ||
506 | swap %d2 | | ||
507 | move.w %d2, %d1 | | ||
508 | move.l %d1, (%a4)+ | | ||
509 | cmp.l %a4, %a0 | | ||
510 | bhi.b 50b | long loop 1 | ||
511 | 60: | output end | | ||
512 | movem.l (%sp), %d1-%d7/%a2-%a6 | restore registers | ||
513 | move.l %d1, %macsr | | ||
514 | lea.l 48(%sp), %sp | cleanup | ||
515 | rts | | ||
516 | .size sample_output_stereo, .-sample_output_stereo | ||
517 | |||
518 | /**************************************************************************** | ||
519 | * void sample_output_mono(int count, struct dsp_data *data, | ||
520 | * const int32_t *src[], int16_t *dst) | ||
521 | * | ||
522 | * Same treatment as sample_output_stereo but for one channel. | ||
523 | */ | ||
524 | .section .text | ||
525 | .align 2 | ||
526 | .global sample_output_mono | ||
527 | sample_output_mono: | ||
528 | lea.l -32(%sp), %sp | save registers | ||
529 | move.l %macsr, %d1 | do it now as at many lines will | ||
530 | movem.l %d1-%d5/%a2-%a4, (%sp) | be the far more common condition | ||
531 | move.l #0x80, %macsr | put emac unit in signed int mode | ||
532 | movem.l 36(%sp), %a0-%a3 | | ||
533 | lea.l (%a3, %a0.l*4), %a0 | %a0 = end address | ||
534 | move.l (%a1), %d1 | %d5 = multiplier: (1 << (16 - scale)) | ||
535 | sub.l #16, %d1 | | ||
536 | neg.l %d1 | | ||
537 | moveq.l #1, %d5 | | ||
538 | asl.l %d1, %d5 | | ||
539 | move.l #0x8000, %a4 | %a4 = rounding term | ||
540 | movem.l (%a2), %a2 | get source channel pointer | ||
541 | moveq.l #28, %d0 | %d0 = second line bound | ||
542 | add.l %a3, %d0 | | ||
543 | and.l #0xfffffff0, %d0 | | ||
544 | cmp.l %a0, %d0 | at least a full line? | ||
545 | bhi.w 40f | long loop 1 start | no? do as trailing longwords | ||
546 | sub.l #16, %d0 | %d1 = first line bound | ||
547 | cmp.l %a3, %d0 | any leading longwords? | ||
548 | bls.b 20f | line loop start | no? start line loop | ||
549 | 10: | long loop 0 | | ||
550 | move.l (%a2)+, %d1 | read longword from L and R | ||
551 | move.l %a4, %acc0 | | ||
552 | mac.l %d1, %d5, %acc0 | shift L to high word | ||
553 | movclr.l %acc0, %d1 | get possibly saturated results | ||
554 | move.l %d1, %d2 | | ||
555 | swap %d2 | move R to low word | ||
556 | move.w %d2, %d1 | duplicate single channel into | ||
557 | move.l %d1, (%a3)+ | L and R | ||
558 | cmp.l %a3, %d0 | | ||
559 | bhi.b 10b | long loop 0 | | ||
560 | 20: | line loop start | | ||
561 | lea.l -12(%a0), %a1 | %a1 = at or just before last line bound | ||
562 | 30: | line loop | | ||
563 | move.l (%a2)+, %d0 | get next 4 L samples and scale | ||
564 | move.l %a4, %acc0 | | ||
565 | move.l %acc0, %acc1 | | ||
566 | move.l %acc1, %acc2 | | ||
567 | move.l %acc2, %acc3 | | ||
568 | mac.l %d0, %d5, (%a2)+, %d1, %acc0 | with saturation | ||
569 | mac.l %d1, %d5, (%a2)+, %d2, %acc1 | | ||
570 | mac.l %d2, %d5, (%a2)+, %d3, %acc2 | | ||
571 | mac.l %d3, %d5 , %acc3 | | ||
572 | lea.l 16(%a3), %a3 | increment dest here, mitigate stalls | ||
573 | movclr.l %acc0, %d0 | obtain results | ||
574 | movclr.l %acc1, %d1 | | ||
575 | movclr.l %acc2, %d2 | | ||
576 | movclr.l %acc3, %d3 | | ||
577 | move.l %d0, %d4 | duplicate single channel | ||
578 | swap %d4 | into L and R | ||
579 | move.w %d4, %d0 | | ||
580 | move.l %d1, %d4 | | ||
581 | swap %d4 | | ||
582 | move.w %d4, %d1 | | ||
583 | move.l %d2, %d4 | | ||
584 | swap %d4 | | ||
585 | move.w %d4, %d2 | | ||
586 | move.l %d3, %d4 | | ||
587 | swap %d4 | | ||
588 | move.w %d4, %d3 | | ||
589 | movem.l %d0-%d3, -16(%a3) | write four stereo samples | ||
590 | cmp.l %a3, %a1 | | ||
591 | bhi.b 30b | line loop | | ||
592 | 40: | long loop 1 start | | ||
593 | cmp.l %a3, %a0 | any longwords left? | ||
594 | bls.b 60f | output end | no? stop | ||
595 | 50: | loop loop 1 | | ||
596 | move.l (%a2)+, %d1 | handle trailing longwords | ||
597 | move.l %a4, %acc0 | | ||
598 | mac.l %d1, %d5, %acc0 | the same way as leading ones | ||
599 | movclr.l %acc0, %d1 | | ||
600 | move.l %d1, %d2 | | ||
601 | swap %d2 | | ||
602 | move.w %d2, %d1 | | ||
603 | move.l %d1, (%a3)+ | | ||
604 | cmp.l %a3, %a0 | | ||
605 | bhi.b 50b | long loop 1 | | ||
606 | 60: | output end | | ||
607 | movem.l (%sp), %d1-%d5/%a2-%a4 | restore registers | ||
608 | move.l %d1, %macsr | | ||
609 | lea.l 32(%sp), %sp | cleanup | ||
610 | rts | | ||
611 | .size sample_output_mono, .-sample_output_mono | ||
diff --git a/lib/rbcodec/dsp/eq.c b/lib/rbcodec/dsp/eq.c new file mode 100644 index 0000000000..122a46a4c5 --- /dev/null +++ b/lib/rbcodec/dsp/eq.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <inttypes.h> | ||
23 | #include "config.h" | ||
24 | #include "fixedpoint.h" | ||
25 | #include "fracmul.h" | ||
26 | #include "eq.h" | ||
27 | #include "replaygain.h" | ||
28 | |||
29 | /** | ||
30 | * Calculate first order shelving filter. Filter is not directly usable by the | ||
31 | * eq_filter() function. | ||
32 | * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format. | ||
33 | * @param A decibel value multiplied by ten, describing gain/attenuation of | ||
34 | * shelf. Max value is 24 dB. | ||
35 | * @param low true for low-shelf filter, false for high-shelf filter. | ||
36 | * @param c pointer to coefficient storage. Coefficients are s4.27 format. | ||
37 | */ | ||
38 | void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c) | ||
39 | { | ||
40 | long sin, cos; | ||
41 | int32_t b0, b1, a0, a1; /* s3.28 */ | ||
42 | const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */ | ||
43 | |||
44 | sin = fp_sincos(cutoff/2, &cos); | ||
45 | if (low) { | ||
46 | const int32_t sin_div_g = fp_div(sin, g, 25); | ||
47 | const int32_t sin_g = FRACMUL(sin, g); | ||
48 | cos >>= 3; | ||
49 | b0 = sin_g + cos; /* 0.25 .. 4.10 */ | ||
50 | b1 = sin_g - cos; /* -1 .. 3.98 */ | ||
51 | a0 = sin_div_g + cos; /* 0.25 .. 4.10 */ | ||
52 | a1 = sin_div_g - cos; /* -1 .. 3.98 */ | ||
53 | } else { | ||
54 | const int32_t cos_div_g = fp_div(cos, g, 25); | ||
55 | const int32_t cos_g = FRACMUL(cos, g); | ||
56 | sin >>= 3; | ||
57 | b0 = sin + cos_g; /* 0.25 .. 4.10 */ | ||
58 | b1 = sin - cos_g; /* -3.98 .. 1 */ | ||
59 | a0 = sin + cos_div_g; /* 0.25 .. 4.10 */ | ||
60 | a1 = sin - cos_div_g; /* -3.98 .. 1 */ | ||
61 | } | ||
62 | |||
63 | const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */ | ||
64 | *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */ | ||
65 | *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */ | ||
66 | *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */ | ||
67 | } | ||
68 | |||
69 | #ifdef HAVE_SW_TONE_CONTROLS | ||
70 | /** | ||
71 | * Calculate second order section filter consisting of one low-shelf and one | ||
72 | * high-shelf section. | ||
73 | * @param cutoff_low low-shelf midpoint frequency. See eq_pk_coefs for format. | ||
74 | * @param cutoff_high high-shelf midpoint frequency. | ||
75 | * @param A_low decibel value multiplied by ten, describing gain/attenuation of | ||
76 | * low-shelf part. Max value is 24 dB. | ||
77 | * @param A_high decibel value multiplied by ten, describing gain/attenuation of | ||
78 | * high-shelf part. Max value is 24 dB. | ||
79 | * @param A decibel value multiplied by ten, describing additional overall gain. | ||
80 | * @param c pointer to coefficient storage. Coefficients are s4.27 format. | ||
81 | */ | ||
82 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, | ||
83 | long A_low, long A_high, long A, int32_t *c) | ||
84 | { | ||
85 | const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */ | ||
86 | int32_t c_ls[3], c_hs[3]; | ||
87 | |||
88 | filter_shelf_coefs(cutoff_low, A_low, true, c_ls); | ||
89 | filter_shelf_coefs(cutoff_high, A_high, false, c_hs); | ||
90 | c_ls[0] = FRACMUL(g, c_ls[0]); | ||
91 | c_ls[1] = FRACMUL(g, c_ls[1]); | ||
92 | |||
93 | /* now we cascade the two first order filters to one second order filter | ||
94 | * which can be used by eq_filter(). these resulting coefficients have a | ||
95 | * really wide numerical range, so we use a fixed point format which will | ||
96 | * work for the selected cutoff frequencies (in dsp.c) only. | ||
97 | */ | ||
98 | const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1]; | ||
99 | const int32_t a0 = c_ls[2], a1 = c_hs[2]; | ||
100 | *c++ = FRACMUL_SHL(b0, b2, 4); | ||
101 | *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4); | ||
102 | *c++ = FRACMUL_SHL(b1, b3, 4); | ||
103 | *c++ = a0 + a1; | ||
104 | *c++ = -FRACMUL_SHL(a0, a1, 4); | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | /* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson. | ||
109 | * Slightly faster calculation can be done by deriving forms which use tan() | ||
110 | * instead of cos() and sin(), but the latter are far easier to use when doing | ||
111 | * fixed point math, and performance is not a big point in the calculation part. | ||
112 | * All the 'a' filter coefficients are negated so we can use only additions | ||
113 | * in the filtering equation. | ||
114 | */ | ||
115 | |||
116 | /** | ||
117 | * Calculate second order section peaking filter coefficients. | ||
118 | * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and | ||
119 | * 0x80000000 represents the Nyquist frequency (samplerate/2). | ||
120 | * @param Q Q factor value multiplied by ten. Lower bound is artificially set | ||
121 | * at 0.5. | ||
122 | * @param db decibel value multiplied by ten, describing gain/attenuation at | ||
123 | * peak freq. Max value is 24 dB. | ||
124 | * @param c pointer to coefficient storage. Coefficients are s3.28 format. | ||
125 | */ | ||
126 | void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | ||
127 | { | ||
128 | long cs; | ||
129 | const long one = 1 << 28; /* s3.28 */ | ||
130 | const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */ | ||
131 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | ||
132 | int32_t a0, a1, a2; /* these are all s3.28 format */ | ||
133 | int32_t b0, b1, b2; | ||
134 | const long alphadivA = fp_div(alpha, A, 27); | ||
135 | const long alphaA = FRACMUL(alpha, A); | ||
136 | |||
137 | /* possible numerical ranges are in comments by each coef */ | ||
138 | b0 = one + alphaA; /* [1 .. 5] */ | ||
139 | b1 = a1 = -2*(cs >> 3); /* [-2 .. 2] */ | ||
140 | b2 = one - alphaA; /* [-3 .. 1] */ | ||
141 | a0 = one + alphadivA; /* [1 .. 5] */ | ||
142 | a2 = one - alphadivA; /* [-3 .. 1] */ | ||
143 | |||
144 | /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */ | ||
145 | const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */ | ||
146 | *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */ | ||
147 | *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */ | ||
148 | *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */ | ||
149 | *c++ = FRACMUL(-a1, rcp_a0); /* [-2 .. 2] */ | ||
150 | *c++ = FRACMUL(-a2, rcp_a0); /* [-0.6 .. 1] */ | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * Calculate coefficients for lowshelf filter. Parameters are as for | ||
155 | * eq_pk_coefs, but the coefficient format is s5.26 fixed point. | ||
156 | */ | ||
157 | void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | ||
158 | { | ||
159 | long cs; | ||
160 | const long one = 1 << 25; /* s6.25 */ | ||
161 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ | ||
162 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ | ||
163 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | ||
164 | const long ap1 = (A >> 4) + one; | ||
165 | const long am1 = (A >> 4) - one; | ||
166 | const long ap1_cs = FRACMUL(ap1, cs); | ||
167 | const long am1_cs = FRACMUL(am1, cs); | ||
168 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); | ||
169 | int32_t a0, a1, a2; /* these are all s6.25 format */ | ||
170 | int32_t b0, b1, b2; | ||
171 | |||
172 | /* [0.1 .. 40] */ | ||
173 | b0 = FRACMUL_SHL(A, ap1 - am1_cs + twosqrtalpha, 2); | ||
174 | /* [-16 .. 63.4] */ | ||
175 | b1 = FRACMUL_SHL(A, am1 - ap1_cs, 3); | ||
176 | /* [0 .. 31.7] */ | ||
177 | b2 = FRACMUL_SHL(A, ap1 - am1_cs - twosqrtalpha, 2); | ||
178 | /* [0.5 .. 10] */ | ||
179 | a0 = ap1 + am1_cs + twosqrtalpha; | ||
180 | /* [-16 .. 4] */ | ||
181 | a1 = -2*(am1 + ap1_cs); | ||
182 | /* [0 .. 8] */ | ||
183 | a2 = ap1 + am1_cs - twosqrtalpha; | ||
184 | |||
185 | /* [0.1 .. 1.99] */ | ||
186 | const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */ | ||
187 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ | ||
188 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ | ||
189 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ | ||
190 | *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */ | ||
191 | *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */ | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Calculate coefficients for highshelf filter. Parameters are as for | ||
196 | * eq_pk_coefs, but the coefficient format is s5.26 fixed point. | ||
197 | */ | ||
198 | void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | ||
199 | { | ||
200 | long cs; | ||
201 | const long one = 1 << 25; /* s6.25 */ | ||
202 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ | ||
203 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ | ||
204 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | ||
205 | const long ap1 = (A >> 4) + one; | ||
206 | const long am1 = (A >> 4) - one; | ||
207 | const long ap1_cs = FRACMUL(ap1, cs); | ||
208 | const long am1_cs = FRACMUL(am1, cs); | ||
209 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); | ||
210 | int32_t a0, a1, a2; /* these are all s6.25 format */ | ||
211 | int32_t b0, b1, b2; | ||
212 | |||
213 | /* [0.1 .. 40] */ | ||
214 | b0 = FRACMUL_SHL(A, ap1 + am1_cs + twosqrtalpha, 2); | ||
215 | /* [-63.5 .. 16] */ | ||
216 | b1 = -FRACMUL_SHL(A, am1 + ap1_cs, 3); | ||
217 | /* [0 .. 32] */ | ||
218 | b2 = FRACMUL_SHL(A, ap1 + am1_cs - twosqrtalpha, 2); | ||
219 | /* [0.5 .. 10] */ | ||
220 | a0 = ap1 - am1_cs + twosqrtalpha; | ||
221 | /* [-4 .. 16] */ | ||
222 | a1 = 2*(am1 - ap1_cs); | ||
223 | /* [0 .. 8] */ | ||
224 | a2 = ap1 - am1_cs - twosqrtalpha; | ||
225 | |||
226 | /* [0.1 .. 1.99] */ | ||
227 | const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */ | ||
228 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ | ||
229 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ | ||
230 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ | ||
231 | *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */ | ||
232 | *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */ | ||
233 | } | ||
234 | |||
235 | /* We realise the filters as a second order direct form 1 structure. Direct | ||
236 | * form 1 was chosen because of better numerical properties for fixed point | ||
237 | * implementations. | ||
238 | */ | ||
239 | |||
240 | #if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM)) | ||
241 | void eq_filter(int32_t **x, struct eqfilter *f, unsigned num, | ||
242 | unsigned channels, unsigned shift) | ||
243 | { | ||
244 | unsigned c, i; | ||
245 | long long acc; | ||
246 | |||
247 | /* Direct form 1 filtering code. | ||
248 | y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2], | ||
249 | where y[] is output and x[] is input. | ||
250 | */ | ||
251 | |||
252 | for (c = 0; c < channels; c++) { | ||
253 | for (i = 0; i < num; i++) { | ||
254 | acc = (long long) x[c][i] * f->coefs[0]; | ||
255 | acc += (long long) f->history[c][0] * f->coefs[1]; | ||
256 | acc += (long long) f->history[c][1] * f->coefs[2]; | ||
257 | acc += (long long) f->history[c][2] * f->coefs[3]; | ||
258 | acc += (long long) f->history[c][3] * f->coefs[4]; | ||
259 | f->history[c][1] = f->history[c][0]; | ||
260 | f->history[c][0] = x[c][i]; | ||
261 | f->history[c][3] = f->history[c][2]; | ||
262 | x[c][i] = (acc << shift) >> 32; | ||
263 | f->history[c][2] = x[c][i]; | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | #endif | ||
268 | |||
diff --git a/lib/rbcodec/dsp/eq.h b/lib/rbcodec/dsp/eq.h new file mode 100644 index 0000000000..a44e9153ac --- /dev/null +++ b/lib/rbcodec/dsp/eq.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _EQ_H | ||
23 | #define _EQ_H | ||
24 | |||
25 | #include <inttypes.h> | ||
26 | #include <stdbool.h> | ||
27 | |||
28 | /* These depend on the fixed point formats used by the different filter types | ||
29 | and need to be changed when they change. | ||
30 | */ | ||
31 | #define FILTER_BISHELF_SHIFT 5 | ||
32 | #define EQ_PEAK_SHIFT 4 | ||
33 | #define EQ_SHELF_SHIFT 6 | ||
34 | |||
35 | struct eqfilter { | ||
36 | int32_t coefs[5]; /* Order is b0, b1, b2, a1, a2 */ | ||
37 | int32_t history[2][4]; | ||
38 | }; | ||
39 | |||
40 | void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c); | ||
41 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, | ||
42 | long A_low, long A_high, long A, int32_t *c); | ||
43 | void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); | ||
44 | void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); | ||
45 | void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c); | ||
46 | void eq_filter(int32_t **x, struct eqfilter *f, unsigned num, | ||
47 | unsigned channels, unsigned shift); | ||
48 | |||
49 | #endif | ||
50 | |||
diff --git a/lib/rbcodec/dsp/eq_arm.S b/lib/rbcodec/dsp/eq_arm.S new file mode 100644 index 0000000000..b0e1771e89 --- /dev/null +++ b/lib/rbcodec/dsp/eq_arm.S | |||
@@ -0,0 +1,89 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | |||
24 | /* uncomment this to make filtering calculate lower bits after shifting. | ||
25 | * without this, "shift" of the lower bits will be lost here. | ||
26 | */ | ||
27 | /* #define HIGH_PRECISION */ | ||
28 | |||
29 | /* | ||
30 | * void eq_filter(int32_t **x, struct eqfilter *f, unsigned num, | ||
31 | * unsigned channels, unsigned shift) | ||
32 | */ | ||
33 | #if CONFIG_CPU == PP5002 | ||
34 | .section .icode,"ax",%progbits | ||
35 | #else | ||
36 | .text | ||
37 | #endif | ||
38 | .global eq_filter | ||
39 | eq_filter: | ||
40 | ldr r12, [sp] @ get shift parameter | ||
41 | stmdb sp!, { r0-r11, lr } @ save all params and clobbered regs | ||
42 | ldmia r1!, { r4-r8 } @ load coefs | ||
43 | mov r10, r1 @ loop prelude expects filter struct addr in r10 | ||
44 | |||
45 | .filterloop: | ||
46 | ldr r9, [sp] @ get pointer to this channels data | ||
47 | add r0, r9, #4 | ||
48 | str r0, [sp] @ save back pointer to next channels data | ||
49 | ldr r9, [r9] @ r9 = x[] | ||
50 | ldr r14, [sp, #8] @ r14 = numsamples | ||
51 | ldmia r10, { r0-r3 } @ load history, r10 should be filter struct addr | ||
52 | str r10, [sp, #4] @ save it for loop end | ||
53 | |||
54 | /* r0-r3 = history, r4-r8 = coefs, r9 = x[], r10..r11 = accumulator, | ||
55 | * r12 = shift amount, r14 = number of samples. | ||
56 | */ | ||
57 | .loop: | ||
58 | /* Direct form 1 filtering code. | ||
59 | * y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2], | ||
60 | * where y[] is output and x[] is input. This is performed out of order to | ||
61 | * reuse registers, we're pretty short on regs. | ||
62 | */ | ||
63 | smull r10, r11, r6, r1 @ acc = b2*x[i - 2] | ||
64 | mov r1, r0 @ fix input history | ||
65 | smlal r10, r11, r5, r0 @ acc += b1*x[i - 1] | ||
66 | ldr r0, [r9] @ load input and fix history in same operation | ||
67 | smlal r10, r11, r7, r2 @ acc += a1*y[i - 1] | ||
68 | smlal r10, r11, r8, r3 @ acc += a2*y[i - 2] | ||
69 | smlal r10, r11, r4, r0 @ acc += b0*x[i] /* avoid stall on arm9*/ | ||
70 | mov r3, r2 @ fix output history | ||
71 | mov r2, r11, asl r12 @ get upper part of result and shift left | ||
72 | #ifdef HIGH_PRECISION | ||
73 | rsb r11, r12, #32 @ get shift amount for lower part | ||
74 | orr r2, r2, r10, lsr r11 @ then mix in correctly shifted lower part | ||
75 | #endif | ||
76 | str r2, [r9], #4 @ save result | ||
77 | subs r14, r14, #1 @ are we done with this channel? | ||
78 | bne .loop | ||
79 | |||
80 | ldr r10, [sp, #4] @ load filter struct pointer | ||
81 | stmia r10!, { r0-r3 } @ save back history | ||
82 | ldr r11, [sp, #12] @ load number of channels | ||
83 | subs r11, r11, #1 @ all channels processed? | ||
84 | strne r11, [sp, #12] | ||
85 | bne .filterloop | ||
86 | |||
87 | add sp, sp, #16 @ compensate for temp storage | ||
88 | ldmpc regs=r4-r11 | ||
89 | |||
diff --git a/lib/rbcodec/dsp/eq_cf.S b/lib/rbcodec/dsp/eq_cf.S new file mode 100644 index 0000000000..30a28b9d99 --- /dev/null +++ b/lib/rbcodec/dsp/eq_cf.S | |||
@@ -0,0 +1,91 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* uncomment this to make filtering calculate lower bits after shifting. | ||
23 | * without this, "shift" - 1 of the lower bits will be lost here. | ||
24 | */ | ||
25 | /* #define HIGH_PRECISION */ | ||
26 | |||
27 | /* | ||
28 | * void eq_filter(int32_t **x, struct eqfilter *f, unsigned num, | ||
29 | * unsigned channels, unsigned shift) | ||
30 | */ | ||
31 | .text | ||
32 | .global eq_filter | ||
33 | eq_filter: | ||
34 | lea.l (-11*4, %sp), %sp | ||
35 | movem.l %d2-%d7/%a2-%a6, (%sp) | save clobbered regs | ||
36 | move.l (11*4+8, %sp), %a5 | fetch filter structure address | ||
37 | move.l (11*4+20, %sp), %d7 | load shift count | ||
38 | subq.l #1, %d7 | EMAC gives us one free shift | ||
39 | #ifdef HIGH_PRECISION | ||
40 | moveq.l #8, %d6 | ||
41 | sub.l %d7, %d6 | shift for lower part of accumulator | ||
42 | #endif | ||
43 | movem.l (%a5), %a0-%a4 | load coefs | ||
44 | lea.l (5*4, %a5), %a5 | point to filter history | ||
45 | |||
46 | .filterloop: | ||
47 | move.l (11*4+4, %sp), %a6 | load input channel pointer | ||
48 | addq.l #4, (11*4+4, %sp) | point x to next channel | ||
49 | move.l (%a6), %a6 | ||
50 | move.l (11*4+12, %sp), %d5 | number of samples | ||
51 | movem.l (%a5), %d0-%d3 | load filter history | ||
52 | |||
53 | /* d0-d3 = history, d4 = temp, d5 = sample count, d6 = lower shift amount, | ||
54 | * d7 = upper shift amount, a0-a4 = coefs, a5 = history pointer, a6 = x[] | ||
55 | */ | ||
56 | .loop: | ||
57 | /* Direct form 1 filtering code. We assume DSP has put EMAC in frac mode. | ||
58 | * y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2], | ||
59 | * where y[] is output and x[] is input. This is performed out of order | ||
60 | * to do parallel load of input value. | ||
61 | */ | ||
62 | mac.l %a2, %d1, %acc0 | acc = b2*x[i - 2] | ||
63 | move.l %d0, %d1 | fix input history | ||
64 | mac.l %a1, %d0, (%a6), %d0, %acc0 | acc += b1*x[i - 1], x[i] -> d0 | ||
65 | mac.l %a0, %d0, %acc0 | acc += b0*x[i] | ||
66 | mac.l %a3, %d2, %acc0 | acc += a1*y[i - 1] | ||
67 | mac.l %a4, %d3, %acc0 | acc += a2*y[i - 2] | ||
68 | move.l %d2, %d3 | fix output history | ||
69 | #ifdef HIGH_PRECISION | ||
70 | move.l %accext01, %d2 | fetch lower part of accumulator | ||
71 | move.b %d2, %d4 | clear upper three bytes | ||
72 | lsr.l %d6, %d4 | shift lower bits | ||
73 | #endif | ||
74 | movclr.l %acc0, %d2 | fetch upper part of result | ||
75 | asl.l %d7, %d2 | restore fixed point format | ||
76 | #ifdef HIGH_PRECISION | ||
77 | or.l %d2, %d4 | combine lower and upper parts | ||
78 | #endif | ||
79 | move.l %d2, (%a6)+ | save result | ||
80 | subq.l #1, %d5 | are we done with this channel? | ||
81 | jne .loop | ||
82 | |||
83 | movem.l %d0-%d3, (%a5) | save history back to struct | ||
84 | lea.l (4*4, %a5), %a5 | point to next channel's history | ||
85 | subq.l #1, (11*4+16, %sp) | have we processed both channels? | ||
86 | jne .filterloop | ||
87 | |||
88 | movem.l (%sp), %d2-%d7/%a2-%a6 | ||
89 | lea.l (11*4, %sp), %sp | ||
90 | rts | ||
91 | |||
diff --git a/lib/rbcodec/dsp/eqs/Acoustic.cfg b/lib/rbcodec/dsp/eqs/Acoustic.cfg new file mode 100644 index 0000000000..34b5ed8a2b --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Acoustic.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 45 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 45 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 10 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 15 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 30 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 20 | ||
diff --git a/lib/rbcodec/dsp/eqs/Bass.cfg b/lib/rbcodec/dsp/eqs/Bass.cfg new file mode 100644 index 0000000000..2742459081 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Bass.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 50 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 50 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 35 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 15 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 5 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: -5 | ||
diff --git a/lib/rbcodec/dsp/eqs/Classical.cfg b/lib/rbcodec/dsp/eqs/Classical.cfg new file mode 100644 index 0000000000..bf2f9f9566 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Classical.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 50 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 50 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 40 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: -20 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 10 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 20 | ||
diff --git a/lib/rbcodec/dsp/eqs/Default.cfg b/lib/rbcodec/dsp/eqs/Default.cfg new file mode 100644 index 0000000000..d6f345fa9e --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Default.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: off | ||
2 | eq precut: 0 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 0 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 0 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 0 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 0 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 0 | ||
diff --git a/lib/rbcodec/dsp/eqs/Disco.cfg b/lib/rbcodec/dsp/eqs/Disco.cfg new file mode 100644 index 0000000000..f894f26da1 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Disco.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 45 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 30 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 10 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 45 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 25 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 10 | ||
diff --git a/lib/rbcodec/dsp/eqs/Electronic.cfg b/lib/rbcodec/dsp/eqs/Electronic.cfg new file mode 100644 index 0000000000..e70c911272 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Electronic.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 55 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 45 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 5 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 25 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 15 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 55 | ||
diff --git a/lib/rbcodec/dsp/eqs/Hip-Hop.cfg b/lib/rbcodec/dsp/eqs/Hip-Hop.cfg new file mode 100644 index 0000000000..2d38425dc4 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Hip-Hop.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 65 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 65 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 25 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: -10 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 15 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 35 | ||
diff --git a/lib/rbcodec/dsp/eqs/Jazz.cfg b/lib/rbcodec/dsp/eqs/Jazz.cfg new file mode 100644 index 0000000000..f576f9fcc1 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Jazz.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 60 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 40 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 15 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: -25 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 5 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 60 | ||
diff --git a/lib/rbcodec/dsp/eqs/Lounge.cfg b/lib/rbcodec/dsp/eqs/Lounge.cfg new file mode 100644 index 0000000000..39ae23a7e7 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Lounge.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 20 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: -25 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 5 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 20 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: -15 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 15 | ||
diff --git a/lib/rbcodec/dsp/eqs/Pop.cfg b/lib/rbcodec/dsp/eqs/Pop.cfg new file mode 100644 index 0000000000..1d8cefe173 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Pop.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 50 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: -10 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 5 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 50 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 15 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: -10 | ||
diff --git a/lib/rbcodec/dsp/eqs/R&B.cfg b/lib/rbcodec/dsp/eqs/R&B.cfg new file mode 100644 index 0000000000..a460b587f5 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/R&B.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 45 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 35 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 45 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 5 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 25 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 30 | ||
diff --git a/lib/rbcodec/dsp/eqs/Rock.cfg b/lib/rbcodec/dsp/eqs/Rock.cfg new file mode 100644 index 0000000000..ec4f0356a8 --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Rock.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 45 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: 25 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 10 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 0 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 20 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 45 | ||
diff --git a/lib/rbcodec/dsp/eqs/Vocal.cfg b/lib/rbcodec/dsp/eqs/Vocal.cfg new file mode 100644 index 0000000000..1de754f07c --- /dev/null +++ b/lib/rbcodec/dsp/eqs/Vocal.cfg | |||
@@ -0,0 +1,17 @@ | |||
1 | eq enabled: on | ||
2 | eq precut: 45 | ||
3 | eq band 0 cutoff: 60 | ||
4 | eq band 0 q: 7 | ||
5 | eq band 0 gain: -45 | ||
6 | eq band 1 cutoff: 200 | ||
7 | eq band 1 q: 10 | ||
8 | eq band 1 gain: 5 | ||
9 | eq band 2 cutoff: 800 | ||
10 | eq band 2 q: 10 | ||
11 | eq band 2 gain: 45 | ||
12 | eq band 3 cutoff: 4000 | ||
13 | eq band 3 q: 10 | ||
14 | eq band 3 gain: 20 | ||
15 | eq band 4 cutoff: 12000 | ||
16 | eq band 4 q: 7 | ||
17 | eq band 4 gain: 0 | ||
diff --git a/lib/rbcodec/dsp/tdspeed.c b/lib/rbcodec/dsp/tdspeed.c new file mode 100644 index 0000000000..731be12621 --- /dev/null +++ b/lib/rbcodec/dsp/tdspeed.c | |||
@@ -0,0 +1,450 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Nicolas Pitre <nico@cam.org> | ||
11 | * Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | #include <inttypes.h> | ||
24 | #include <stddef.h> | ||
25 | #include <stdio.h> | ||
26 | #include <string.h> | ||
27 | #include "sound.h" | ||
28 | #include "core_alloc.h" | ||
29 | #include "system.h" | ||
30 | #include "tdspeed.h" | ||
31 | #include "settings.h" | ||
32 | |||
33 | #define assert(cond) | ||
34 | |||
35 | #define MIN_RATE 8000 | ||
36 | #define MAX_RATE 48000 /* double buffer for double rate */ | ||
37 | #define MINFREQ 100 | ||
38 | |||
39 | #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ | ||
40 | |||
41 | static int32_t** dsp_src; | ||
42 | static int handles[4]; | ||
43 | static int32_t *overlap_buffer[2] = { NULL, NULL }; | ||
44 | static int32_t *outbuf[2] = { NULL, NULL }; | ||
45 | |||
46 | static int move_callback(int handle, void* current, void* new) | ||
47 | { | ||
48 | /* TODO */ | ||
49 | (void)handle; | ||
50 | if (dsp_src) | ||
51 | { | ||
52 | int ch = (current == outbuf[0]) ? 0 : 1; | ||
53 | dsp_src[ch] = outbuf[ch] = new; | ||
54 | } | ||
55 | return BUFLIB_CB_OK; | ||
56 | } | ||
57 | |||
58 | static struct buflib_callbacks ops = { | ||
59 | .move_callback = move_callback, | ||
60 | .shrink_callback = NULL, | ||
61 | }; | ||
62 | |||
63 | static int ovl_move_callback(int handle, void* current, void* new) | ||
64 | { | ||
65 | /* TODO */ | ||
66 | (void)handle; | ||
67 | if (dsp_src) | ||
68 | { | ||
69 | int ch = (current == overlap_buffer[0]) ? 0 : 1; | ||
70 | overlap_buffer[ch] = new; | ||
71 | } | ||
72 | return BUFLIB_CB_OK; | ||
73 | } | ||
74 | |||
75 | static struct buflib_callbacks ovl_ops = { | ||
76 | .move_callback = ovl_move_callback, | ||
77 | .shrink_callback = NULL, | ||
78 | }; | ||
79 | |||
80 | |||
81 | static struct tdspeed_state_s | ||
82 | { | ||
83 | bool stereo; | ||
84 | int32_t shift_max; /* maximum displacement on a frame */ | ||
85 | int32_t src_step; /* source window pace */ | ||
86 | int32_t dst_step; /* destination window pace */ | ||
87 | int32_t dst_order; /* power of two for dst_step */ | ||
88 | int32_t ovl_shift; /* overlap buffer frame shift */ | ||
89 | int32_t ovl_size; /* overlap buffer used size */ | ||
90 | int32_t ovl_space; /* overlap buffer size */ | ||
91 | int32_t *ovl_buff[2]; /* overlap buffer */ | ||
92 | } tdspeed_state; | ||
93 | |||
94 | void tdspeed_init(void) | ||
95 | { | ||
96 | if (!global_settings.timestretch_enabled) | ||
97 | return; | ||
98 | |||
99 | /* Allocate buffers */ | ||
100 | if (overlap_buffer[0] == NULL) | ||
101 | { | ||
102 | handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); | ||
103 | overlap_buffer[0] = core_get_data(handles[0]); | ||
104 | } | ||
105 | if (overlap_buffer[1] == NULL) | ||
106 | { | ||
107 | handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); | ||
108 | overlap_buffer[1] = core_get_data(handles[1]); | ||
109 | } | ||
110 | if (outbuf[0] == NULL) | ||
111 | { | ||
112 | handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); | ||
113 | outbuf[0] = core_get_data(handles[2]); | ||
114 | } | ||
115 | if (outbuf[1] == NULL) | ||
116 | { | ||
117 | handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); | ||
118 | outbuf[1] = core_get_data(handles[3]); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | void tdspeed_finish(void) | ||
123 | { | ||
124 | for(unsigned i = 0; i < ARRAYLEN(handles); i++) | ||
125 | { | ||
126 | if (handles[i] > 0) | ||
127 | { | ||
128 | core_free(handles[i]); | ||
129 | handles[i] = 0; | ||
130 | } | ||
131 | } | ||
132 | overlap_buffer[0] = overlap_buffer[1] = NULL; | ||
133 | outbuf[0] = outbuf[1] = NULL; | ||
134 | } | ||
135 | |||
136 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor) | ||
137 | { | ||
138 | struct tdspeed_state_s *st = &tdspeed_state; | ||
139 | int src_frame_sz; | ||
140 | |||
141 | /* Check buffers were allocated ok */ | ||
142 | if (overlap_buffer[0] == NULL || overlap_buffer[1] == NULL) | ||
143 | return false; | ||
144 | |||
145 | if (outbuf[0] == NULL || outbuf[1] == NULL) | ||
146 | return false; | ||
147 | |||
148 | /* Check parameters */ | ||
149 | if (factor == PITCH_SPEED_100) | ||
150 | return false; | ||
151 | |||
152 | if (samplerate < MIN_RATE || samplerate > MAX_RATE) | ||
153 | return false; | ||
154 | |||
155 | if (factor < STRETCH_MIN || factor > STRETCH_MAX) | ||
156 | return false; | ||
157 | |||
158 | st->stereo = stereo; | ||
159 | st->dst_step = samplerate / MINFREQ; | ||
160 | |||
161 | if (factor > PITCH_SPEED_100) | ||
162 | st->dst_step = st->dst_step * PITCH_SPEED_100 / factor; | ||
163 | |||
164 | st->dst_order = 1; | ||
165 | |||
166 | while (st->dst_step >>= 1) | ||
167 | st->dst_order++; | ||
168 | |||
169 | st->dst_step = (1 << st->dst_order); | ||
170 | st->src_step = st->dst_step * factor / PITCH_SPEED_100; | ||
171 | st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step; | ||
172 | |||
173 | src_frame_sz = st->shift_max + st->dst_step; | ||
174 | |||
175 | if (st->dst_step > st->src_step) | ||
176 | src_frame_sz += st->dst_step - st->src_step; | ||
177 | |||
178 | st->ovl_space = ((src_frame_sz - 2) / st->src_step) * st->src_step | ||
179 | + src_frame_sz; | ||
180 | |||
181 | if (st->src_step > st->dst_step) | ||
182 | st->ovl_space += 2*st->src_step - st->dst_step; | ||
183 | |||
184 | if (st->ovl_space > FIXED_BUFSIZE) | ||
185 | st->ovl_space = FIXED_BUFSIZE; | ||
186 | |||
187 | st->ovl_size = 0; | ||
188 | st->ovl_shift = 0; | ||
189 | |||
190 | st->ovl_buff[0] = overlap_buffer[0]; | ||
191 | |||
192 | if (stereo) | ||
193 | st->ovl_buff[1] = overlap_buffer[1]; | ||
194 | else | ||
195 | st->ovl_buff[1] = st->ovl_buff[0]; | ||
196 | |||
197 | return true; | ||
198 | } | ||
199 | |||
200 | static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2], | ||
201 | int data_len, int last, int out_size) | ||
202 | /* data_len in samples */ | ||
203 | { | ||
204 | struct tdspeed_state_s *st = &tdspeed_state; | ||
205 | int32_t *dest[2]; | ||
206 | int32_t next_frame, prev_frame, src_frame_sz; | ||
207 | bool stereo = buf_in[0] != buf_in[1]; | ||
208 | |||
209 | assert(stereo == st->stereo); | ||
210 | |||
211 | src_frame_sz = st->shift_max + st->dst_step; | ||
212 | |||
213 | if (st->dst_step > st->src_step) | ||
214 | src_frame_sz += st->dst_step - st->src_step; | ||
215 | |||
216 | /* deal with overlap data first, if any */ | ||
217 | if (st->ovl_size) | ||
218 | { | ||
219 | int32_t have, copy, steps; | ||
220 | have = st->ovl_size; | ||
221 | |||
222 | if (st->ovl_shift > 0) | ||
223 | have -= st->ovl_shift; | ||
224 | |||
225 | /* append just enough data to have all of the overlap buffer consumed */ | ||
226 | steps = (have - 1) / st->src_step; | ||
227 | copy = steps * st->src_step + src_frame_sz - have; | ||
228 | |||
229 | if (copy < src_frame_sz - st->dst_step) | ||
230 | copy += st->src_step; /* one more step to allow for pregap data */ | ||
231 | |||
232 | if (copy > data_len) | ||
233 | copy = data_len; | ||
234 | |||
235 | assert(st->ovl_size + copy <= FIXED_BUFSIZE); | ||
236 | memcpy(st->ovl_buff[0] + st->ovl_size, buf_in[0], | ||
237 | copy * sizeof(int32_t)); | ||
238 | |||
239 | if (stereo) | ||
240 | memcpy(st->ovl_buff[1] + st->ovl_size, buf_in[1], | ||
241 | copy * sizeof(int32_t)); | ||
242 | |||
243 | if (!last && have + copy < src_frame_sz) | ||
244 | { | ||
245 | /* still not enough to process at least one frame */ | ||
246 | st->ovl_size += copy; | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | /* recursively call ourselves to process the overlap buffer */ | ||
251 | have = st->ovl_size; | ||
252 | st->ovl_size = 0; | ||
253 | |||
254 | if (copy == data_len) | ||
255 | { | ||
256 | assert(have + copy <= FIXED_BUFSIZE); | ||
257 | return tdspeed_apply(buf_out, st->ovl_buff, have+copy, last, | ||
258 | out_size); | ||
259 | } | ||
260 | |||
261 | assert(have + copy <= FIXED_BUFSIZE); | ||
262 | int i = tdspeed_apply(buf_out, st->ovl_buff, have+copy, -1, out_size); | ||
263 | |||
264 | dest[0] = buf_out[0] + i; | ||
265 | dest[1] = buf_out[1] + i; | ||
266 | |||
267 | /* readjust pointers to account for data already consumed */ | ||
268 | next_frame = copy - src_frame_sz + st->src_step; | ||
269 | prev_frame = next_frame - st->ovl_shift; | ||
270 | } | ||
271 | else | ||
272 | { | ||
273 | dest[0] = buf_out[0]; | ||
274 | dest[1] = buf_out[1]; | ||
275 | |||
276 | next_frame = prev_frame = 0; | ||
277 | |||
278 | if (st->ovl_shift > 0) | ||
279 | next_frame += st->ovl_shift; | ||
280 | else | ||
281 | prev_frame += -st->ovl_shift; | ||
282 | } | ||
283 | |||
284 | st->ovl_shift = 0; | ||
285 | |||
286 | /* process all complete frames */ | ||
287 | while (data_len - next_frame >= src_frame_sz) | ||
288 | { | ||
289 | /* find frame overlap by autocorelation */ | ||
290 | int const INC1 = 8; | ||
291 | int const INC2 = 32; | ||
292 | |||
293 | int64_t min_delta = INT64_MAX; /* most positive */ | ||
294 | int shift = 0; | ||
295 | |||
296 | /* Power of 2 of a 28bit number requires 56bits, can accumulate | ||
297 | 256times in a 64bit variable. */ | ||
298 | assert(st->dst_step / INC2 <= 256); | ||
299 | assert(next_frame + st->shift_max - 1 + st->dst_step - 1 < data_len); | ||
300 | assert(prev_frame + st->dst_step - 1 < data_len); | ||
301 | |||
302 | for (int i = 0; i < st->shift_max; i += INC1) | ||
303 | { | ||
304 | int64_t delta = 0; | ||
305 | |||
306 | int32_t *curr = buf_in[0] + next_frame + i; | ||
307 | int32_t *prev = buf_in[0] + prev_frame; | ||
308 | |||
309 | for (int j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2) | ||
310 | { | ||
311 | int32_t diff = *curr - *prev; | ||
312 | delta += abs(diff); | ||
313 | |||
314 | if (delta >= min_delta) | ||
315 | goto skip; | ||
316 | } | ||
317 | |||
318 | if (stereo) | ||
319 | { | ||
320 | curr = buf_in[1] + next_frame + i; | ||
321 | prev = buf_in[1] + prev_frame; | ||
322 | |||
323 | for (int j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2) | ||
324 | { | ||
325 | int32_t diff = *curr - *prev; | ||
326 | delta += abs(diff); | ||
327 | |||
328 | if (delta >= min_delta) | ||
329 | goto skip; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | min_delta = delta; | ||
334 | shift = i; | ||
335 | skip:; | ||
336 | } | ||
337 | |||
338 | /* overlap fading-out previous frame with fading-in current frame */ | ||
339 | int32_t *curr = buf_in[0] + next_frame + shift; | ||
340 | int32_t *prev = buf_in[0] + prev_frame; | ||
341 | |||
342 | int32_t *d = dest[0]; | ||
343 | |||
344 | assert(next_frame + shift + st->dst_step - 1 < data_len); | ||
345 | assert(prev_frame + st->dst_step - 1 < data_len); | ||
346 | assert(dest[0] - buf_out[0] + st->dst_step - 1 < out_size); | ||
347 | |||
348 | for (int i = 0, j = st->dst_step; j; i++, j--) | ||
349 | { | ||
350 | *d++ = (*curr++ * (int64_t)i + | ||
351 | *prev++ * (int64_t)j) >> st->dst_order; | ||
352 | } | ||
353 | |||
354 | dest[0] = d; | ||
355 | |||
356 | if (stereo) | ||
357 | { | ||
358 | curr = buf_in[1] + next_frame + shift; | ||
359 | prev = buf_in[1] + prev_frame; | ||
360 | |||
361 | d = dest[1]; | ||
362 | |||
363 | for (int i = 0, j = st->dst_step; j; i++, j--) | ||
364 | { | ||
365 | assert(d < buf_out[1] + out_size); | ||
366 | |||
367 | *d++ = (*curr++ * (int64_t)i + | ||
368 | *prev++ * (int64_t)j) >> st->dst_order; | ||
369 | } | ||
370 | |||
371 | dest[1] = d; | ||
372 | } | ||
373 | |||
374 | /* adjust pointers for next frame */ | ||
375 | prev_frame = next_frame + shift + st->dst_step; | ||
376 | next_frame += st->src_step; | ||
377 | |||
378 | /* here next_frame - prev_frame = src_step - dst_step - shift */ | ||
379 | assert(next_frame - prev_frame == st->src_step - st->dst_step - shift); | ||
380 | } | ||
381 | |||
382 | /* now deal with remaining partial frames */ | ||
383 | if (last == -1) | ||
384 | { | ||
385 | /* special overlap buffer processing: remember frame shift only */ | ||
386 | st->ovl_shift = next_frame - prev_frame; | ||
387 | } | ||
388 | else if (last != 0) | ||
389 | { | ||
390 | /* last call: purge all remaining data to output buffer */ | ||
391 | int i = data_len - prev_frame; | ||
392 | |||
393 | assert(dest[0] + i <= buf_out[0] + out_size); | ||
394 | memcpy(dest[0], buf_in[0] + prev_frame, i * sizeof(int32_t)); | ||
395 | |||
396 | dest[0] += i; | ||
397 | |||
398 | if (stereo) | ||
399 | { | ||
400 | assert(dest[1] + i <= buf_out[1] + out_size); | ||
401 | memcpy(dest[1], buf_in[1] + prev_frame, i * sizeof(int32_t)); | ||
402 | dest[1] += i; | ||
403 | } | ||
404 | } | ||
405 | else | ||
406 | { | ||
407 | /* preserve remaining data + needed overlap data for next call */ | ||
408 | st->ovl_shift = next_frame - prev_frame; | ||
409 | int i = (st->ovl_shift < 0) ? next_frame : prev_frame; | ||
410 | st->ovl_size = data_len - i; | ||
411 | |||
412 | assert(st->ovl_size <= FIXED_BUFSIZE); | ||
413 | memcpy(st->ovl_buff[0], buf_in[0] + i, st->ovl_size * sizeof(int32_t)); | ||
414 | |||
415 | if (stereo) | ||
416 | memcpy(st->ovl_buff[1], buf_in[1] + i, st->ovl_size * sizeof(int32_t)); | ||
417 | } | ||
418 | |||
419 | return dest[0] - buf_out[0]; | ||
420 | } | ||
421 | |||
422 | long tdspeed_est_output_size() | ||
423 | { | ||
424 | return TDSPEED_OUTBUFSIZE; | ||
425 | } | ||
426 | |||
427 | long tdspeed_est_input_size(long size) | ||
428 | { | ||
429 | struct tdspeed_state_s *st = &tdspeed_state; | ||
430 | |||
431 | size = (size - st->ovl_size) * st->src_step / st->dst_step; | ||
432 | |||
433 | if (size < 0) | ||
434 | size = 0; | ||
435 | |||
436 | return size; | ||
437 | } | ||
438 | |||
439 | int tdspeed_doit(int32_t *src[], int count) | ||
440 | { | ||
441 | dsp_src = src; | ||
442 | count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, | ||
443 | src, count, 0, TDSPEED_OUTBUFSIZE); | ||
444 | |||
445 | src[0] = outbuf[0]; | ||
446 | src[1] = outbuf[1]; | ||
447 | |||
448 | return count; | ||
449 | } | ||
450 | |||
diff --git a/lib/rbcodec/dsp/tdspeed.h b/lib/rbcodec/dsp/tdspeed.h new file mode 100644 index 0000000000..e91eeb1701 --- /dev/null +++ b/lib/rbcodec/dsp/tdspeed.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Nicolas Pitre <nico@cam.org> | ||
11 | * Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | #ifndef _TDSPEED_H | ||
24 | #define _TDSPEED_H | ||
25 | |||
26 | #include "dsp.h" | ||
27 | |||
28 | #define TDSPEED_OUTBUFSIZE 4096 | ||
29 | |||
30 | /* some #define functions to get the pitch, stretch and speed values based on */ | ||
31 | /* two known values. Remember that params are alphabetical. */ | ||
32 | #define GET_SPEED(pitch, stretch) \ | ||
33 | ((pitch * stretch + PITCH_SPEED_100 / 2L) / PITCH_SPEED_100) | ||
34 | #define GET_PITCH(speed, stretch) \ | ||
35 | ((speed * PITCH_SPEED_100 + stretch / 2L) / stretch) | ||
36 | #define GET_STRETCH(pitch, speed) \ | ||
37 | ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) | ||
38 | |||
39 | void tdspeed_init(void); | ||
40 | void tdspeed_finish(void); | ||
41 | bool tdspeed_config(int samplerate, bool stereo, int32_t factor); | ||
42 | long tdspeed_est_output_size(void); | ||
43 | long tdspeed_est_input_size(long size); | ||
44 | int tdspeed_doit(int32_t *src[], int count); | ||
45 | |||
46 | #define STRETCH_MAX (250L * PITCH_SPEED_PRECISION) /* 250% */ | ||
47 | #define STRETCH_MIN (35L * PITCH_SPEED_PRECISION) /* 35% */ | ||
48 | |||
49 | #endif | ||
diff --git a/lib/rbcodec/metadata/a52.c b/lib/rbcodec/metadata/a52.c new file mode 100644 index 0000000000..a8aad3fa4f --- /dev/null +++ b/lib/rbcodec/metadata/a52.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include "metadata.h" | ||
24 | #include "logf.h" | ||
25 | |||
26 | #include "metadata_parsers.h" | ||
27 | |||
28 | static const unsigned short a52_bitrates[] = | ||
29 | { | ||
30 | 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, | ||
31 | 192, 224, 256, 320, 384, 448, 512, 576, 640 | ||
32 | }; | ||
33 | |||
34 | /* Only store frame sizes for 44.1KHz - others are simply multiples | ||
35 | of the bitrate */ | ||
36 | static const unsigned short a52_441framesizes[] = | ||
37 | { | ||
38 | 69 * 2, 70 * 2, 87 * 2, 88 * 2, 104 * 2, 105 * 2, 121 * 2, | ||
39 | 122 * 2, 139 * 2, 140 * 2, 174 * 2, 175 * 2, 208 * 2, 209 * 2, | ||
40 | 243 * 2, 244 * 2, 278 * 2, 279 * 2, 348 * 2, 349 * 2, 417 * 2, | ||
41 | 418 * 2, 487 * 2, 488 * 2, 557 * 2, 558 * 2, 696 * 2, 697 * 2, | ||
42 | 835 * 2, 836 * 2, 975 * 2, 976 * 2, 1114 * 2, 1115 * 2, 1253 * 2, | ||
43 | 1254 * 2, 1393 * 2, 1394 * 2 | ||
44 | }; | ||
45 | |||
46 | bool get_a52_metadata(int fd, struct mp3entry *id3) | ||
47 | { | ||
48 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
49 | unsigned char* buf = (unsigned char *)id3->path; | ||
50 | unsigned long totalsamples; | ||
51 | int i; | ||
52 | |||
53 | if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 5) < 5)) | ||
54 | { | ||
55 | return false; | ||
56 | } | ||
57 | |||
58 | if ((buf[0] != 0x0b) || (buf[1] != 0x77)) | ||
59 | { | ||
60 | logf("not an A52/AC3 file\n"); | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | i = buf[4] & 0x3e; | ||
65 | |||
66 | if (i > 36) | ||
67 | { | ||
68 | logf("A52: Invalid frmsizecod: %d\n",i); | ||
69 | return false; | ||
70 | } | ||
71 | |||
72 | id3->bitrate = a52_bitrates[i >> 1]; | ||
73 | id3->vbr = false; | ||
74 | id3->filesize = filesize(fd); | ||
75 | |||
76 | switch (buf[4] & 0xc0) | ||
77 | { | ||
78 | case 0x00: | ||
79 | id3->frequency = 48000; | ||
80 | id3->bytesperframe=id3->bitrate * 2 * 2; | ||
81 | break; | ||
82 | |||
83 | case 0x40: | ||
84 | id3->frequency = 44100; | ||
85 | id3->bytesperframe = a52_441framesizes[i]; | ||
86 | break; | ||
87 | |||
88 | case 0x80: | ||
89 | id3->frequency = 32000; | ||
90 | id3->bytesperframe = id3->bitrate * 3 * 2; | ||
91 | break; | ||
92 | |||
93 | default: | ||
94 | logf("A52: Invalid samplerate code: 0x%02x\n", buf[4] & 0xc0); | ||
95 | return false; | ||
96 | break; | ||
97 | } | ||
98 | |||
99 | /* One A52 frame contains 6 blocks, each containing 256 samples */ | ||
100 | totalsamples = id3->filesize / id3->bytesperframe * 6 * 256; | ||
101 | id3->length = totalsamples / id3->frequency * 1000; | ||
102 | return true; | ||
103 | } | ||
diff --git a/lib/rbcodec/metadata/adx.c b/lib/rbcodec/metadata/adx.c new file mode 100644 index 0000000000..7c341b4835 --- /dev/null +++ b/lib/rbcodec/metadata/adx.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "debug.h" | ||
32 | |||
33 | bool get_adx_metadata(int fd, struct mp3entry* id3) | ||
34 | { | ||
35 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
36 | unsigned char * buf = (unsigned char *)id3->path; | ||
37 | int chanstart, channels; | ||
38 | int looping = 0, start_adr = 0, end_adr = 0; | ||
39 | |||
40 | /* try to get the basic header */ | ||
41 | if ((lseek(fd, 0, SEEK_SET) < 0) | ||
42 | || (read(fd, buf, 0x38) < 0x38)) | ||
43 | { | ||
44 | DEBUGF("lseek or read failed\n"); | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | /* ADX starts with 0x80 */ | ||
49 | if (buf[0] != 0x80) { | ||
50 | DEBUGF("get_adx_metadata: wrong first byte %c\n",buf[0]); | ||
51 | return false; | ||
52 | } | ||
53 | |||
54 | /* check for a reasonable offset */ | ||
55 | chanstart = ((buf[2] << 8) | buf[3]) + 4; | ||
56 | if (chanstart > 4096) { | ||
57 | DEBUGF("get_adx_metadata: bad chanstart %i\n", chanstart); | ||
58 | return false; | ||
59 | } | ||
60 | |||
61 | /* check for a workable number of channels */ | ||
62 | channels = buf[7]; | ||
63 | if (channels != 1 && channels != 2) { | ||
64 | DEBUGF("get_adx_metadata: bad channel count %i\n",channels); | ||
65 | return false; | ||
66 | } | ||
67 | |||
68 | id3->frequency = get_long_be(&buf[8]); | ||
69 | /* 32 samples per 18 bytes */ | ||
70 | id3->bitrate = id3->frequency * channels * 18 * 8 / 32 / 1000; | ||
71 | id3->length = get_long_be(&buf[12]) / id3->frequency * 1000; | ||
72 | id3->vbr = false; | ||
73 | id3->filesize = filesize(fd); | ||
74 | |||
75 | /* get loop info */ | ||
76 | if (!memcmp(buf+0x10,"\x01\xF4\x03",3)) { | ||
77 | /* Soul Calibur 2 style (type 03) */ | ||
78 | DEBUGF("get_adx_metadata: type 03 found\n"); | ||
79 | /* check if header is too small for loop data */ | ||
80 | if (chanstart-6 < 0x2c) looping=0; | ||
81 | else { | ||
82 | looping = get_long_be(&buf[0x18]); | ||
83 | end_adr = get_long_be(&buf[0x28]); | ||
84 | start_adr = get_long_be(&buf[0x1c])/32*channels*18+chanstart; | ||
85 | } | ||
86 | } else if (!memcmp(buf+0x10,"\x01\xF4\x04",3)) { | ||
87 | /* Standard (type 04) */ | ||
88 | DEBUGF("get_adx_metadata: type 04 found\n"); | ||
89 | /* check if header is too small for loop data */ | ||
90 | if (chanstart-6 < 0x38) looping=0; | ||
91 | else { | ||
92 | looping = get_long_be(&buf[0x24]); | ||
93 | end_adr = get_long_be(&buf[0x34]); | ||
94 | start_adr = get_long_be(&buf[0x28])/32*channels*18+chanstart; | ||
95 | } | ||
96 | } else { | ||
97 | DEBUGF("get_adx_metadata: error, couldn't determine ADX type\n"); | ||
98 | return false; | ||
99 | } | ||
100 | |||
101 | /* is file using encryption */ | ||
102 | if (buf[0x13]==0x08) { | ||
103 | DEBUGF("get_adx_metadata: error, encrypted ADX not supported\n"); | ||
104 | return false; | ||
105 | } | ||
106 | |||
107 | if (looping) { | ||
108 | /* 2 loops, 10 second fade */ | ||
109 | id3->length = (start_adr-chanstart + 2*(end_adr-start_adr)) | ||
110 | *8 / id3->bitrate + 10000; | ||
111 | } | ||
112 | |||
113 | /* try to get the channel header */ | ||
114 | if ((lseek(fd, chanstart-6, SEEK_SET) < 0) | ||
115 | || (read(fd, buf, 6) < 6)) | ||
116 | { | ||
117 | return false; | ||
118 | } | ||
119 | |||
120 | /* check channel header */ | ||
121 | if (memcmp(buf, "(c)CRI", 6) != 0) return false; | ||
122 | |||
123 | return true; | ||
124 | } | ||
diff --git a/lib/rbcodec/metadata/aiff.c b/lib/rbcodec/metadata/aiff.c new file mode 100644 index 0000000000..654f37cf98 --- /dev/null +++ b/lib/rbcodec/metadata/aiff.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | |||
32 | #include "debug.h" | ||
33 | |||
34 | /* compressionType: AIFC QuickTime IMA ADPCM */ | ||
35 | #define AIFC_FORMAT_QT_IMA_ADPCM "ima4" | ||
36 | |||
37 | bool get_aiff_metadata(int fd, struct mp3entry* id3) | ||
38 | { | ||
39 | unsigned char buf[512]; | ||
40 | unsigned long numChannels = 0; | ||
41 | unsigned long numSampleFrames = 0; | ||
42 | unsigned long numbytes = 0; | ||
43 | bool is_aifc = false; | ||
44 | |||
45 | if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, &buf[0], 12) < 12) || | ||
46 | (memcmp(&buf[0], "FORM", 4) != 0) || (memcmp(&buf[8], "AIF", 3) != 0) || | ||
47 | (!(is_aifc = (buf[11] == 'C')) && buf[11] != 'F')) | ||
48 | { | ||
49 | return false; | ||
50 | } | ||
51 | |||
52 | while (read(fd, &buf[0], 8) == 8) | ||
53 | { | ||
54 | size_t size = get_long_be(&buf[4]); /* chunkSize */ | ||
55 | |||
56 | if (memcmp(&buf[0], "SSND", 4) == 0) | ||
57 | { | ||
58 | numbytes = size - 8; | ||
59 | break; /* assume COMM was already read */ | ||
60 | } | ||
61 | |||
62 | /* odd chunk sizes must be padded */ | ||
63 | size += size & 1; | ||
64 | |||
65 | if (size > sizeof(buf)) | ||
66 | { | ||
67 | DEBUGF("AIFF \"%4.4s\" chunk too large (%zd > %zd)", | ||
68 | (char*) &buf[0], size, sizeof(buf)); | ||
69 | } | ||
70 | |||
71 | if (memcmp(&buf[0], "COMM", 4) == 0) | ||
72 | { | ||
73 | if (size > sizeof(buf) || read(fd, &buf[0], size) != (ssize_t)size) | ||
74 | return false; | ||
75 | |||
76 | numChannels = ((buf[0]<<8)|buf[1]); | ||
77 | |||
78 | numSampleFrames = get_long_be(&buf[2]); | ||
79 | |||
80 | /* sampleRate */ | ||
81 | id3->frequency = get_long_be(&buf[10]); | ||
82 | id3->frequency >>= (16+14-buf[9]); | ||
83 | |||
84 | /* save format infos */ | ||
85 | id3->bitrate = ((buf[6]<<8)|buf[7]) * numChannels * id3->frequency; | ||
86 | id3->bitrate /= 1000; | ||
87 | |||
88 | if (!is_aifc || memcmp(&buf[18], AIFC_FORMAT_QT_IMA_ADPCM, 4) != 0) | ||
89 | id3->length = ((int64_t) numSampleFrames * 1000) / id3->frequency; | ||
90 | else | ||
91 | { | ||
92 | /* QuickTime IMA ADPCM is 1block = 64 data for each channel */ | ||
93 | id3->length = ((int64_t) numSampleFrames * 64000LL) / id3->frequency; | ||
94 | } | ||
95 | |||
96 | id3->vbr = false; /* AIFF files are CBR */ | ||
97 | id3->filesize = filesize(fd); | ||
98 | } | ||
99 | else | ||
100 | { | ||
101 | /* skip chunk */ | ||
102 | if (lseek(fd, size, SEEK_CUR) < 0) | ||
103 | return false; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | return numbytes && numChannels; | ||
108 | } | ||
diff --git a/lib/rbcodec/metadata/ape.c b/lib/rbcodec/metadata/ape.c new file mode 100644 index 0000000000..0bd2477431 --- /dev/null +++ b/lib/rbcodec/metadata/ape.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "structec.h" | ||
32 | |||
33 | #define APETAG_HEADER_LENGTH 32 | ||
34 | #define APETAG_HEADER_FORMAT "8llll8" | ||
35 | #define APETAG_ITEM_HEADER_FORMAT "ll" | ||
36 | #define APETAG_ITEM_TYPE_MASK 3 | ||
37 | |||
38 | #ifdef HAVE_ALBUMART | ||
39 | /* The AA header consists of the pseudo filename "Album Cover (Front).ext" | ||
40 | * whereas ".ext" is the file extension. For now ".jpg" and ".png" are | ||
41 | * supported by this APE metadata parser. Therefore the length is 22. */ | ||
42 | #define APETAG_AA_HEADER_LENGTH 22 | ||
43 | #endif | ||
44 | |||
45 | struct apetag_header | ||
46 | { | ||
47 | char id[8]; | ||
48 | long version; | ||
49 | long length; | ||
50 | long item_count; | ||
51 | long flags; | ||
52 | char reserved[8]; | ||
53 | }; | ||
54 | |||
55 | struct apetag_item_header | ||
56 | { | ||
57 | long length; | ||
58 | long flags; | ||
59 | }; | ||
60 | |||
61 | /* Read the items in an APEV2 tag. Only looks for a tag at the end of a | ||
62 | * file. Returns true if a tag was found and fully read, false otherwise. | ||
63 | */ | ||
64 | bool read_ape_tags(int fd, struct mp3entry* id3) | ||
65 | { | ||
66 | struct apetag_header header; | ||
67 | |||
68 | if ((lseek(fd, -APETAG_HEADER_LENGTH, SEEK_END) < 0) | ||
69 | || (ecread(fd, &header, 1, APETAG_HEADER_FORMAT, IS_BIG_ENDIAN) | ||
70 | != APETAG_HEADER_LENGTH) | ||
71 | || (memcmp(header.id, "APETAGEX", sizeof(header.id)))) | ||
72 | { | ||
73 | return false; | ||
74 | } | ||
75 | |||
76 | if ((header.version == 2000) && (header.item_count > 0) | ||
77 | && (header.length > APETAG_HEADER_LENGTH)) | ||
78 | { | ||
79 | char *buf = id3->id3v2buf; | ||
80 | unsigned int buf_remaining = sizeof(id3->id3v2buf) | ||
81 | + sizeof(id3->id3v1buf); | ||
82 | unsigned int tag_remaining = header.length - APETAG_HEADER_LENGTH; | ||
83 | int i; | ||
84 | |||
85 | if (lseek(fd, -header.length, SEEK_END) < 0) | ||
86 | { | ||
87 | return false; | ||
88 | } | ||
89 | |||
90 | for (i = 0; i < header.item_count; i++) | ||
91 | { | ||
92 | struct apetag_item_header item; | ||
93 | char name[TAG_NAME_LENGTH]; | ||
94 | char value[TAG_VALUE_LENGTH]; | ||
95 | long r; | ||
96 | |||
97 | if (tag_remaining < sizeof(item)) | ||
98 | { | ||
99 | break; | ||
100 | } | ||
101 | |||
102 | if (ecread(fd, &item, 1, APETAG_ITEM_HEADER_FORMAT, IS_BIG_ENDIAN) | ||
103 | < (long) sizeof(item)) | ||
104 | { | ||
105 | return false; | ||
106 | } | ||
107 | |||
108 | tag_remaining -= sizeof(item); | ||
109 | r = read_string(fd, name, sizeof(name), 0, tag_remaining); | ||
110 | |||
111 | if (r == -1) | ||
112 | { | ||
113 | return false; | ||
114 | } | ||
115 | |||
116 | tag_remaining -= r + item.length; | ||
117 | |||
118 | if ((item.flags & APETAG_ITEM_TYPE_MASK) == 0) | ||
119 | { | ||
120 | long len; | ||
121 | |||
122 | if (read_string(fd, value, sizeof(value), -1, item.length) | ||
123 | != item.length) | ||
124 | { | ||
125 | return false; | ||
126 | } | ||
127 | |||
128 | len = parse_tag(name, value, id3, buf, buf_remaining, | ||
129 | TAGTYPE_APE); | ||
130 | buf += len; | ||
131 | buf_remaining -= len; | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | #ifdef HAVE_ALBUMART | ||
136 | if (strcasecmp(name, "cover art (front)") == 0) | ||
137 | { | ||
138 | /* Allow to read at least APETAG_AA_HEADER_LENGTH bytes. */ | ||
139 | r = read_string(fd, name, sizeof(name), 0, APETAG_AA_HEADER_LENGTH); | ||
140 | if (r == -1) | ||
141 | { | ||
142 | return false; | ||
143 | } | ||
144 | |||
145 | /* Gather the album art format from the pseudo file name's ending. */ | ||
146 | strcpy(name, name + strlen(name) - 4); | ||
147 | id3->albumart.type = AA_TYPE_UNKNOWN; | ||
148 | if (strcasecmp(name, ".jpg") == 0) | ||
149 | { | ||
150 | id3->albumart.type = AA_TYPE_JPG; | ||
151 | } | ||
152 | else if (strcasecmp(name, ".png") == 0) | ||
153 | { | ||
154 | id3->albumart.type = AA_TYPE_PNG; | ||
155 | } | ||
156 | |||
157 | /* Set the album art size and position. */ | ||
158 | if (id3->albumart.type != AA_TYPE_UNKNOWN) | ||
159 | { | ||
160 | id3->albumart.pos = lseek(fd, 0, SEEK_CUR); | ||
161 | id3->albumart.size = item.length - r; | ||
162 | id3->has_embedded_albumart = true; | ||
163 | } | ||
164 | |||
165 | /* Seek back to this APE items begin. */ | ||
166 | if (lseek(fd, -r, SEEK_CUR) < 0) | ||
167 | { | ||
168 | return false; | ||
169 | } | ||
170 | } | ||
171 | #endif | ||
172 | /* Seek to the next APE item. */ | ||
173 | if (lseek(fd, item.length, SEEK_CUR) < 0) | ||
174 | { | ||
175 | return false; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | return true; | ||
182 | } | ||
diff --git a/lib/rbcodec/metadata/asap.c b/lib/rbcodec/metadata/asap.c new file mode 100644 index 0000000000..9e7f227031 --- /dev/null +++ b/lib/rbcodec/metadata/asap.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2008 Dominik Wenger | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "rbunicode.h" | ||
32 | #include "debug.h" | ||
33 | |||
34 | #define MAX_SONGS 32 | ||
35 | |||
36 | static bool parse_dec(int *retval, const char *p, int minval, int maxval) | ||
37 | { | ||
38 | int r = 0; | ||
39 | do { | ||
40 | char c = *p; | ||
41 | if (c >= '0' && c <= '9') | ||
42 | r = 10 * r + c - '0'; | ||
43 | else | ||
44 | return false; | ||
45 | if (r > maxval) | ||
46 | return false; | ||
47 | } while (*++p != '\0'); | ||
48 | if (r < minval) | ||
49 | return false; | ||
50 | *retval = r; | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | static bool parse_text(char *retval, const char *p) | ||
55 | { | ||
56 | int i; | ||
57 | if (*p != '"') | ||
58 | return false; | ||
59 | p++; | ||
60 | if (p[0] == '<' && p[1] == '?' && p[2] == '>' && p[3] == '"') | ||
61 | return true; | ||
62 | i = 0; | ||
63 | while (*p != '"') { | ||
64 | if (i >= 127) | ||
65 | return false; | ||
66 | if (*p == '\0') | ||
67 | return false; | ||
68 | retval[i++] = *p++; | ||
69 | } | ||
70 | retval[i] = '\0'; | ||
71 | return true; | ||
72 | } | ||
73 | |||
74 | static int ASAP_ParseDuration(const char *s) | ||
75 | { | ||
76 | int r; | ||
77 | if (*s < '0' || *s > '9') | ||
78 | return -1; | ||
79 | r = *s++ - '0'; | ||
80 | if (*s >= '0' && *s <= '9') | ||
81 | r = 10 * r + *s++ - '0'; | ||
82 | if (*s == ':') { | ||
83 | s++; | ||
84 | if (*s < '0' || *s > '5') | ||
85 | return -1; | ||
86 | r = 60 * r + (*s++ - '0') * 10; | ||
87 | if (*s < '0' || *s > '9') | ||
88 | return -1; | ||
89 | r += *s++ - '0'; | ||
90 | } | ||
91 | r *= 1000; | ||
92 | if (*s != '.') | ||
93 | return r; | ||
94 | s++; | ||
95 | if (*s < '0' || *s > '9') | ||
96 | return r; | ||
97 | r += 100 * (*s++ - '0'); | ||
98 | if (*s < '0' || *s > '9') | ||
99 | return r; | ||
100 | r += 10 * (*s++ - '0'); | ||
101 | if (*s < '0' || *s > '9') | ||
102 | return r; | ||
103 | r += *s - '0'; | ||
104 | return r; | ||
105 | } | ||
106 | |||
107 | static bool read_asap_string(char* source, char** buf, char** buffer_end, char** dest) | ||
108 | { | ||
109 | if(parse_text(*buf,source) == false) | ||
110 | return false; | ||
111 | |||
112 | /* set dest pointer */ | ||
113 | *dest = *buf; | ||
114 | |||
115 | /* move buf ptr */ | ||
116 | *buf += strlen(*buf)+1; | ||
117 | |||
118 | /* check size */ | ||
119 | if(*buf >= *buffer_end) | ||
120 | { | ||
121 | DEBUGF("Buffer full\n"); | ||
122 | return false; | ||
123 | } | ||
124 | return true; | ||
125 | } | ||
126 | |||
127 | static bool parse_sap_header(int fd, struct mp3entry* id3, int file_len) | ||
128 | { | ||
129 | int module_index = 0; | ||
130 | int sap_signature = -1; | ||
131 | int duration_index = 0; | ||
132 | unsigned char cur_char = 0; | ||
133 | int i; | ||
134 | |||
135 | /* set defaults */ | ||
136 | int numSongs = 1; | ||
137 | int defSong = 0; | ||
138 | int durations[MAX_SONGS]; | ||
139 | for (i = 0; i < MAX_SONGS; i++) | ||
140 | durations[i] = -1; | ||
141 | |||
142 | /* use id3v2 buffer for our strings */ | ||
143 | char* buffer = id3->id3v2buf; | ||
144 | char* buffer_end = id3->id3v2buf + ID3V2_BUF_SIZE; | ||
145 | |||
146 | /* parse file */ | ||
147 | while (1) | ||
148 | { | ||
149 | char line[256]; | ||
150 | char *p; | ||
151 | |||
152 | if (module_index + 8 >= file_len) | ||
153 | return false; | ||
154 | /* read a char */ | ||
155 | read(fd,&cur_char,1); | ||
156 | /* end of header */ | ||
157 | if (cur_char == 0xff) | ||
158 | break; | ||
159 | |||
160 | i = 0; | ||
161 | while (cur_char != 0x0d) | ||
162 | { | ||
163 | line[i++] = cur_char; | ||
164 | module_index++; | ||
165 | if (module_index >= file_len || (unsigned)i >= sizeof(line) - 1) | ||
166 | return false; | ||
167 | /* read a char */ | ||
168 | read(fd,&cur_char,1); | ||
169 | } | ||
170 | if (++module_index >= file_len ) | ||
171 | return false; | ||
172 | /* read a char */ | ||
173 | read(fd,&cur_char,1); | ||
174 | if ( cur_char != 0x0a) | ||
175 | return false; | ||
176 | |||
177 | line[i] = '\0'; | ||
178 | for (p = line; *p != '\0'; p++) { | ||
179 | if (*p == ' ') { | ||
180 | *p++ = '\0'; | ||
181 | break; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /* parse tags */ | ||
186 | if(strcmp(line, "SAP") == 0) | ||
187 | sap_signature = 1; | ||
188 | if (sap_signature == -1) | ||
189 | return false; | ||
190 | if (strcmp(line, "AUTHOR") == 0) | ||
191 | { | ||
192 | if(read_asap_string(p, &buffer, &buffer_end, &id3->artist) == false) | ||
193 | return false; | ||
194 | } | ||
195 | else if(strcmp(line, "NAME") == 0) | ||
196 | { | ||
197 | if(read_asap_string(p, &buffer, &buffer_end, &id3->title) == false) | ||
198 | return false; | ||
199 | } | ||
200 | else if(strcmp(line, "DATE") == 0) | ||
201 | { | ||
202 | if(read_asap_string(p, &buffer, &buffer_end, &id3->year_string) == false) | ||
203 | return false; | ||
204 | } | ||
205 | else if (strcmp(line, "SONGS") == 0) | ||
206 | { | ||
207 | if (parse_dec(&numSongs, p, 1, MAX_SONGS) == false ) | ||
208 | return false; | ||
209 | } | ||
210 | else if (strcmp(line, "DEFSONG") == 0) | ||
211 | { | ||
212 | if (parse_dec(&defSong, p, 0, MAX_SONGS) == false) | ||
213 | return false; | ||
214 | } | ||
215 | else if (strcmp(line, "TIME") == 0) | ||
216 | { | ||
217 | int durationTemp = ASAP_ParseDuration(p); | ||
218 | if (durationTemp < 0 || duration_index >= MAX_SONGS) | ||
219 | return false; | ||
220 | durations[duration_index++] = durationTemp; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /* set length: */ | ||
225 | int length = durations[defSong]; | ||
226 | if (length < 0) | ||
227 | length = 180 * 1000; | ||
228 | id3->length = length; | ||
229 | |||
230 | lseek(fd, 0, SEEK_SET); | ||
231 | return true; | ||
232 | } | ||
233 | |||
234 | |||
235 | bool get_asap_metadata(int fd, struct mp3entry* id3) | ||
236 | { | ||
237 | |||
238 | int filelength = filesize(fd); | ||
239 | |||
240 | if(parse_sap_header(fd, id3, filelength) == false) | ||
241 | { | ||
242 | DEBUGF("parse sap header failed.\n"); | ||
243 | return false; | ||
244 | } | ||
245 | |||
246 | id3->bitrate = 706; | ||
247 | id3->frequency = 44100; | ||
248 | |||
249 | id3->vbr = false; | ||
250 | id3->filesize = filelength; | ||
251 | id3->genre_string = id3_get_num_genre(36); | ||
252 | |||
253 | return true; | ||
254 | } | ||
diff --git a/lib/rbcodec/metadata/asf.c b/lib/rbcodec/metadata/asf.c new file mode 100644 index 0000000000..b815c09769 --- /dev/null +++ b/lib/rbcodec/metadata/asf.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2007 Dave Chapman | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include <stdio.h> | ||
23 | #include <string.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <ctype.h> | ||
26 | #include <inttypes.h> | ||
27 | |||
28 | #include "metadata.h" | ||
29 | #include "replaygain.h" | ||
30 | #include "debug.h" | ||
31 | #include "rbunicode.h" | ||
32 | #include "metadata_common.h" | ||
33 | #include "metadata_parsers.h" | ||
34 | #include "system.h" | ||
35 | #include <codecs/libasf/asf.h> | ||
36 | |||
37 | /* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */ | ||
38 | struct guid_s { | ||
39 | uint32_t v1; | ||
40 | uint16_t v2; | ||
41 | uint16_t v3; | ||
42 | uint8_t v4[8]; | ||
43 | }; | ||
44 | typedef struct guid_s guid_t; | ||
45 | |||
46 | struct asf_object_s { | ||
47 | guid_t guid; | ||
48 | uint64_t size; | ||
49 | uint64_t datalen; | ||
50 | }; | ||
51 | typedef struct asf_object_s asf_object_t; | ||
52 | |||
53 | static const guid_t asf_guid_null = | ||
54 | {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
55 | |||
56 | /* top level object guids */ | ||
57 | |||
58 | static const guid_t asf_guid_header = | ||
59 | {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; | ||
60 | |||
61 | static const guid_t asf_guid_data = | ||
62 | {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; | ||
63 | |||
64 | static const guid_t asf_guid_index = | ||
65 | {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; | ||
66 | |||
67 | /* header level object guids */ | ||
68 | |||
69 | static const guid_t asf_guid_file_properties = | ||
70 | {0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; | ||
71 | |||
72 | static const guid_t asf_guid_stream_properties = | ||
73 | {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; | ||
74 | |||
75 | static const guid_t asf_guid_content_description = | ||
76 | {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; | ||
77 | |||
78 | static const guid_t asf_guid_extended_content_description = | ||
79 | {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; | ||
80 | |||
81 | static const guid_t asf_guid_content_encryption = | ||
82 | {0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}}; | ||
83 | |||
84 | static const guid_t asf_guid_extended_content_encryption = | ||
85 | {0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}}; | ||
86 | |||
87 | /* stream type guids */ | ||
88 | |||
89 | static const guid_t asf_guid_stream_type_audio = | ||
90 | {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; | ||
91 | |||
92 | static int asf_guid_match(const guid_t *guid1, const guid_t *guid2) | ||
93 | { | ||
94 | if((guid1->v1 != guid2->v1) || | ||
95 | (guid1->v2 != guid2->v2) || | ||
96 | (guid1->v3 != guid2->v3) || | ||
97 | (memcmp(guid1->v4, guid2->v4, 8))) { | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | return 1; | ||
102 | } | ||
103 | |||
104 | /* Read the 16 byte GUID from a file */ | ||
105 | static void asf_readGUID(int fd, guid_t* guid) | ||
106 | { | ||
107 | read_uint32le(fd, &guid->v1); | ||
108 | read_uint16le(fd, &guid->v2); | ||
109 | read_uint16le(fd, &guid->v3); | ||
110 | read(fd, guid->v4, 8); | ||
111 | } | ||
112 | |||
113 | static void asf_read_object_header(asf_object_t *obj, int fd) | ||
114 | { | ||
115 | asf_readGUID(fd, &obj->guid); | ||
116 | read_uint64le(fd, &obj->size); | ||
117 | obj->datalen = 0; | ||
118 | } | ||
119 | |||
120 | /* Parse an integer from the extended content object - we always | ||
121 | convert to an int, regardless of native format. | ||
122 | */ | ||
123 | static int asf_intdecode(int fd, int type, int length) | ||
124 | { | ||
125 | uint16_t tmp16; | ||
126 | uint32_t tmp32; | ||
127 | uint64_t tmp64; | ||
128 | |||
129 | if (type == 3) { | ||
130 | read_uint32le(fd, &tmp32); | ||
131 | lseek(fd,length - 4,SEEK_CUR); | ||
132 | return (int)tmp32; | ||
133 | } else if (type == 4) { | ||
134 | read_uint64le(fd, &tmp64); | ||
135 | lseek(fd,length - 8,SEEK_CUR); | ||
136 | return (int)tmp64; | ||
137 | } else if (type == 5) { | ||
138 | read_uint16le(fd, &tmp16); | ||
139 | lseek(fd,length - 2,SEEK_CUR); | ||
140 | return (int)tmp16; | ||
141 | } | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* Decode a LE utf16 string from a disk buffer into a fixed-sized | ||
147 | utf8 buffer. | ||
148 | */ | ||
149 | |||
150 | static void asf_utf16LEdecode(int fd, | ||
151 | uint16_t utf16bytes, | ||
152 | unsigned char **utf8, | ||
153 | int* utf8bytes | ||
154 | ) | ||
155 | { | ||
156 | unsigned long ucs; | ||
157 | int n; | ||
158 | unsigned char utf16buf[256]; | ||
159 | unsigned char* utf16 = utf16buf; | ||
160 | unsigned char* newutf8; | ||
161 | |||
162 | n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes)); | ||
163 | utf16bytes -= n; | ||
164 | |||
165 | while (n > 0) { | ||
166 | /* Check for a surrogate pair */ | ||
167 | if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) { | ||
168 | if (n < 4) { | ||
169 | /* Run out of utf16 bytes, read some more */ | ||
170 | utf16buf[0] = utf16[0]; | ||
171 | utf16buf[1] = utf16[1]; | ||
172 | |||
173 | n = read(fd, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes)); | ||
174 | utf16 = utf16buf; | ||
175 | utf16bytes -= n; | ||
176 | n += 2; | ||
177 | } | ||
178 | |||
179 | if (n < 4) { | ||
180 | /* Truncated utf16 string, abort */ | ||
181 | break; | ||
182 | } | ||
183 | ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18) | ||
184 | | utf16[2] | ((utf16[3] - 0xDC) << 8)); | ||
185 | utf16 += 4; | ||
186 | n -= 4; | ||
187 | } else { | ||
188 | ucs = (utf16[0] | (utf16[1] << 8)); | ||
189 | utf16 += 2; | ||
190 | n -= 2; | ||
191 | } | ||
192 | |||
193 | if (*utf8bytes > 6) { | ||
194 | newutf8 = utf8encode(ucs, *utf8); | ||
195 | *utf8bytes -= (newutf8 - *utf8); | ||
196 | *utf8 += (newutf8 - *utf8); | ||
197 | } | ||
198 | |||
199 | /* We have run out of utf16 bytes, read more if available */ | ||
200 | if ((n == 0) && (utf16bytes > 0)) { | ||
201 | n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes)); | ||
202 | utf16 = utf16buf; | ||
203 | utf16bytes -= n; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | *utf8[0] = 0; | ||
208 | --*utf8bytes; | ||
209 | |||
210 | if (utf16bytes > 0) { | ||
211 | /* Skip any remaining bytes */ | ||
212 | lseek(fd, utf16bytes, SEEK_CUR); | ||
213 | } | ||
214 | return; | ||
215 | } | ||
216 | |||
217 | static int asf_parse_header(int fd, struct mp3entry* id3, | ||
218 | asf_waveformatex_t* wfx) | ||
219 | { | ||
220 | asf_object_t current; | ||
221 | asf_object_t header; | ||
222 | uint64_t datalen; | ||
223 | int i; | ||
224 | int fileprop = 0; | ||
225 | uint64_t play_duration; | ||
226 | uint16_t flags; | ||
227 | uint32_t subobjects; | ||
228 | uint8_t utf8buf[512]; | ||
229 | int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); | ||
230 | unsigned char* id3buf = (unsigned char*)id3->id3v2buf; | ||
231 | |||
232 | asf_read_object_header((asf_object_t *) &header, fd); | ||
233 | |||
234 | //DEBUGF("header.size=%d\n",(int)header.size); | ||
235 | if (header.size < 30) { | ||
236 | /* invalid size for header object */ | ||
237 | return ASF_ERROR_OBJECT_SIZE; | ||
238 | } | ||
239 | |||
240 | read_uint32le(fd, &subobjects); | ||
241 | |||
242 | /* Two reserved bytes - do we need to read them? */ | ||
243 | lseek(fd, 2, SEEK_CUR); | ||
244 | |||
245 | //DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects); | ||
246 | |||
247 | if (subobjects > 0) { | ||
248 | header.datalen = header.size - 30; | ||
249 | |||
250 | /* TODO: Check that we have datalen bytes left in the file */ | ||
251 | datalen = header.datalen; | ||
252 | |||
253 | for (i=0; i<(int)subobjects; i++) { | ||
254 | //DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen); | ||
255 | if (datalen < 24) { | ||
256 | //DEBUGF("not enough data for reading object\n"); | ||
257 | break; | ||
258 | } | ||
259 | |||
260 | asf_read_object_header(¤t, fd); | ||
261 | |||
262 | if (current.size > datalen || current.size < 24) { | ||
263 | //DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen); | ||
264 | break; | ||
265 | } | ||
266 | |||
267 | if (asf_guid_match(¤t.guid, &asf_guid_file_properties)) { | ||
268 | if (current.size < 104) | ||
269 | return ASF_ERROR_OBJECT_SIZE; | ||
270 | |||
271 | if (fileprop) { | ||
272 | /* multiple file properties objects not allowed */ | ||
273 | return ASF_ERROR_INVALID_OBJECT; | ||
274 | } | ||
275 | |||
276 | fileprop = 1; | ||
277 | |||
278 | /* Get the number of logical packets - uint16_t at offset 31 | ||
279 | * (Big endian byte order) */ | ||
280 | lseek(fd, 31, SEEK_CUR); | ||
281 | read_uint16be(fd, &wfx->numpackets); | ||
282 | |||
283 | /* Now get the play duration - uint64_t at offset 40 */ | ||
284 | lseek(fd, 7, SEEK_CUR); | ||
285 | read_uint64le(fd, &play_duration); | ||
286 | id3->length = play_duration / 10000; | ||
287 | |||
288 | //DEBUGF("****** length = %lums\n", id3->length); | ||
289 | |||
290 | /* Read the packet size - uint32_t at offset 68 */ | ||
291 | lseek(fd, 20, SEEK_CUR); | ||
292 | read_uint32le(fd, &wfx->packet_size); | ||
293 | |||
294 | /* Skip bytes remaining in object */ | ||
295 | lseek(fd, current.size - 24 - 72, SEEK_CUR); | ||
296 | } else if (asf_guid_match(¤t.guid, &asf_guid_stream_properties)) { | ||
297 | guid_t guid; | ||
298 | uint32_t propdatalen; | ||
299 | |||
300 | if (current.size < 78) | ||
301 | return ASF_ERROR_OBJECT_SIZE; | ||
302 | |||
303 | #if 0 | ||
304 | asf_byteio_getGUID(&guid, current->data); | ||
305 | datalen = asf_byteio_getDWLE(current->data + 40); | ||
306 | flags = asf_byteio_getWLE(current->data + 48); | ||
307 | #endif | ||
308 | |||
309 | asf_readGUID(fd, &guid); | ||
310 | |||
311 | lseek(fd, 24, SEEK_CUR); | ||
312 | read_uint32le(fd, &propdatalen); | ||
313 | lseek(fd, 4, SEEK_CUR); | ||
314 | read_uint16le(fd, &flags); | ||
315 | |||
316 | if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) { | ||
317 | //DEBUGF("Found stream properties for non audio stream, skipping\n"); | ||
318 | lseek(fd,current.size - 24 - 50,SEEK_CUR); | ||
319 | } else if (wfx->audiostream == -1) { | ||
320 | lseek(fd, 4, SEEK_CUR); | ||
321 | //DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f); | ||
322 | |||
323 | if (propdatalen < 18) { | ||
324 | return ASF_ERROR_INVALID_LENGTH; | ||
325 | } | ||
326 | |||
327 | #if 0 | ||
328 | if (asf_byteio_getWLE(data + 16) > datalen - 16) { | ||
329 | return ASF_ERROR_INVALID_LENGTH; | ||
330 | } | ||
331 | #endif | ||
332 | read_uint16le(fd, &wfx->codec_id); | ||
333 | read_uint16le(fd, &wfx->channels); | ||
334 | read_uint32le(fd, &wfx->rate); | ||
335 | read_uint32le(fd, &wfx->bitrate); | ||
336 | wfx->bitrate *= 8; | ||
337 | read_uint16le(fd, &wfx->blockalign); | ||
338 | read_uint16le(fd, &wfx->bitspersample); | ||
339 | read_uint16le(fd, &wfx->datalen); | ||
340 | |||
341 | /* Round bitrate to the nearest kbit */ | ||
342 | id3->bitrate = (wfx->bitrate + 500) / 1000; | ||
343 | id3->frequency = wfx->rate; | ||
344 | |||
345 | if (wfx->codec_id == ASF_CODEC_ID_WMAV1) { | ||
346 | read(fd, wfx->data, 4); | ||
347 | lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR); | ||
348 | wfx->audiostream = flags&0x7f; | ||
349 | } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) { | ||
350 | read(fd, wfx->data, 6); | ||
351 | lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR); | ||
352 | wfx->audiostream = flags&0x7f; | ||
353 | } else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) { | ||
354 | /* wma pro decoder needs the extra-data */ | ||
355 | read(fd, wfx->data, wfx->datalen); | ||
356 | lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); | ||
357 | wfx->audiostream = flags&0x7f; | ||
358 | /* Correct codectype to redirect playback to the proper .codec */ | ||
359 | id3->codectype = AFMT_WMAPRO; | ||
360 | } else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) { | ||
361 | read(fd, wfx->data, wfx->datalen); | ||
362 | lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR); | ||
363 | wfx->audiostream = flags&0x7f; | ||
364 | id3->codectype = AFMT_WMAVOICE; | ||
365 | } else { | ||
366 | DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); | ||
367 | lseek(fd,current.size - 24 - 72,SEEK_CUR); | ||
368 | } | ||
369 | |||
370 | } | ||
371 | } else if (asf_guid_match(¤t.guid, &asf_guid_content_description)) { | ||
372 | /* Object contains five 16-bit string lengths, followed by the five strings: | ||
373 | title, artist, copyright, description, rating | ||
374 | */ | ||
375 | uint16_t strlength[5]; | ||
376 | int i; | ||
377 | |||
378 | //DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24)); | ||
379 | |||
380 | /* Read the 5 string lengths - number of bytes included trailing zero */ | ||
381 | for (i=0; i<5; i++) { | ||
382 | read_uint16le(fd, &strlength[i]); | ||
383 | //DEBUGF("strlength = %u\n",strlength[i]); | ||
384 | } | ||
385 | |||
386 | if (strlength[0] > 0) { /* 0 - Title */ | ||
387 | id3->title = id3buf; | ||
388 | asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining); | ||
389 | } | ||
390 | |||
391 | if (strlength[1] > 0) { /* 1 - Artist */ | ||
392 | id3->artist = id3buf; | ||
393 | asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining); | ||
394 | } | ||
395 | |||
396 | lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */ | ||
397 | |||
398 | if (strlength[3] > 0) { /* 3 - description */ | ||
399 | id3->comment = id3buf; | ||
400 | asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining); | ||
401 | } | ||
402 | |||
403 | lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */ | ||
404 | } else if (asf_guid_match(¤t.guid, &asf_guid_extended_content_description)) { | ||
405 | uint16_t count; | ||
406 | int i; | ||
407 | int bytesleft = current.size - 24; | ||
408 | //DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n"); | ||
409 | |||
410 | read_uint16le(fd, &count); | ||
411 | bytesleft -= 2; | ||
412 | //DEBUGF("extended metadata count = %u\n",count); | ||
413 | |||
414 | for (i=0; i < count; i++) { | ||
415 | uint16_t length, type; | ||
416 | unsigned char* utf8 = utf8buf; | ||
417 | int utf8length = 512; | ||
418 | |||
419 | read_uint16le(fd, &length); | ||
420 | asf_utf16LEdecode(fd, length, &utf8, &utf8length); | ||
421 | bytesleft -= 2 + length; | ||
422 | |||
423 | read_uint16le(fd, &type); | ||
424 | read_uint16le(fd, &length); | ||
425 | |||
426 | if (!strcmp("WM/TrackNumber",utf8buf)) { | ||
427 | if (type == 0) { | ||
428 | id3->track_string = id3buf; | ||
429 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
430 | id3->tracknum = atoi(id3->track_string); | ||
431 | } else if ((type >=2) && (type <= 5)) { | ||
432 | id3->tracknum = asf_intdecode(fd, type, length); | ||
433 | } else { | ||
434 | lseek(fd, length, SEEK_CUR); | ||
435 | } | ||
436 | } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) { | ||
437 | id3->genre_string = id3buf; | ||
438 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
439 | } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) { | ||
440 | id3->album = id3buf; | ||
441 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
442 | } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) { | ||
443 | id3->albumartist = id3buf; | ||
444 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
445 | } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) { | ||
446 | id3->composer = id3buf; | ||
447 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
448 | } else if (!strcmp("WM/Year", utf8buf)) { | ||
449 | if (type == 0) { | ||
450 | id3->year_string = id3buf; | ||
451 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
452 | id3->year = atoi(id3->year_string); | ||
453 | } else if ((type >=2) && (type <= 5)) { | ||
454 | id3->year = asf_intdecode(fd, type, length); | ||
455 | } else { | ||
456 | lseek(fd, length, SEEK_CUR); | ||
457 | } | ||
458 | } else if (!strncmp("replaygain_", utf8buf, 11)) { | ||
459 | char *value = id3buf; | ||
460 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
461 | parse_replaygain(utf8buf, value, id3); | ||
462 | } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { | ||
463 | id3->mb_track_id = id3buf; | ||
464 | asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); | ||
465 | #ifdef HAVE_ALBUMART | ||
466 | } else if (!strcmp("WM/Picture", utf8buf)) { | ||
467 | uint32_t datalength, strlength; | ||
468 | /* Expected is either "01 00 xx xx 03 yy yy yy yy" or | ||
469 | * "03 yy yy yy yy". xx is the size of the WM/Picture | ||
470 | * container in bytes. yy equals the raw data length of | ||
471 | * the embedded image. */ | ||
472 | lseek(fd, -4, SEEK_CUR); | ||
473 | read(fd, &type, 1); | ||
474 | if (type == 1) { | ||
475 | lseek(fd, 3, SEEK_CUR); | ||
476 | read(fd, &type, 1); | ||
477 | /* In case the parsing will fail in the next step we | ||
478 | * might at least be able to skip the whole section. */ | ||
479 | datalength = length - 1; | ||
480 | } | ||
481 | if (type == 3) { | ||
482 | /* Read the raw data length of the embedded image. */ | ||
483 | read_uint32le(fd, &datalength); | ||
484 | |||
485 | /* Reset utf8 buffer */ | ||
486 | utf8 = utf8buf; | ||
487 | utf8length = 512; | ||
488 | |||
489 | /* Gather the album art format, this string has a | ||
490 | * double zero-termination. */ | ||
491 | asf_utf16LEdecode(fd, 32, &utf8, &utf8length); | ||
492 | strlength = (strlen(utf8buf) + 2) * 2; | ||
493 | lseek(fd, strlength-32, SEEK_CUR); | ||
494 | if (!strcmp("image/jpeg", utf8buf)) { | ||
495 | id3->albumart.type = AA_TYPE_JPG; | ||
496 | } else if (!strcmp("image/png", utf8buf)) { | ||
497 | id3->albumart.type = AA_TYPE_PNG; | ||
498 | } else { | ||
499 | id3->albumart.type = AA_TYPE_UNKNOWN; | ||
500 | } | ||
501 | |||
502 | /* Set the album art size and position. */ | ||
503 | if (id3->albumart.type != AA_TYPE_UNKNOWN) { | ||
504 | id3->albumart.pos = lseek(fd, 0, SEEK_CUR); | ||
505 | id3->albumart.size = datalength; | ||
506 | id3->has_embedded_albumart = true; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | lseek(fd, datalength, SEEK_CUR); | ||
511 | #endif | ||
512 | } else { | ||
513 | lseek(fd, length, SEEK_CUR); | ||
514 | } | ||
515 | bytesleft -= 4 + length; | ||
516 | } | ||
517 | |||
518 | lseek(fd, bytesleft, SEEK_CUR); | ||
519 | } else if (asf_guid_match(¤t.guid, &asf_guid_content_encryption) | ||
520 | || asf_guid_match(¤t.guid, &asf_guid_extended_content_encryption)) { | ||
521 | //DEBUGF("File is encrypted\n"); | ||
522 | return ASF_ERROR_ENCRYPTED; | ||
523 | } else { | ||
524 | //DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24)); | ||
525 | lseek(fd,current.size - 24,SEEK_CUR); | ||
526 | } | ||
527 | |||
528 | //DEBUGF("Parsed object - size = %d\n",(int)current.size); | ||
529 | datalen -= current.size; | ||
530 | } | ||
531 | |||
532 | if (i != (int)subobjects || datalen != 0) { | ||
533 | //DEBUGF("header data doesn't match given subobject count\n"); | ||
534 | return ASF_ERROR_INVALID_VALUE; | ||
535 | } | ||
536 | |||
537 | //DEBUGF("%d subobjects read successfully\n", i); | ||
538 | } | ||
539 | |||
540 | #if 0 | ||
541 | tmp = asf_parse_header_validate(file, &header); | ||
542 | if (tmp < 0) { | ||
543 | /* header read ok but doesn't validate correctly */ | ||
544 | return tmp; | ||
545 | } | ||
546 | #endif | ||
547 | |||
548 | //DEBUGF("header validated correctly\n"); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | bool get_asf_metadata(int fd, struct mp3entry* id3) | ||
554 | { | ||
555 | int res; | ||
556 | asf_object_t obj; | ||
557 | asf_waveformatex_t wfx; | ||
558 | |||
559 | wfx.audiostream = -1; | ||
560 | |||
561 | res = asf_parse_header(fd, id3, &wfx); | ||
562 | |||
563 | if (res < 0) { | ||
564 | DEBUGF("ASF: parsing error - %d\n",res); | ||
565 | return false; | ||
566 | } | ||
567 | |||
568 | if (wfx.audiostream == -1) { | ||
569 | DEBUGF("ASF: No WMA streams found\n"); | ||
570 | return false; | ||
571 | } | ||
572 | |||
573 | asf_read_object_header(&obj, fd); | ||
574 | |||
575 | if (!asf_guid_match(&obj.guid, &asf_guid_data)) { | ||
576 | DEBUGF("ASF: No data object found\n"); | ||
577 | return false; | ||
578 | } | ||
579 | |||
580 | /* Store the current file position - no need to parse the header | ||
581 | again in the codec. The +26 skips the rest of the data object | ||
582 | header. | ||
583 | */ | ||
584 | id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26; | ||
585 | id3->filesize = filesize(fd); | ||
586 | /* We copy the wfx struct to the MP3 TOC field in the id3 struct so | ||
587 | the codec doesn't need to parse the header object again */ | ||
588 | memcpy(id3->toc, &wfx, sizeof(wfx)); | ||
589 | |||
590 | return true; | ||
591 | } | ||
diff --git a/lib/rbcodec/metadata/au.c b/lib/rbcodec/metadata/au.c new file mode 100644 index 0000000000..94e7453644 --- /dev/null +++ b/lib/rbcodec/metadata/au.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Yoshihisa Uchida | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <inttypes.h> | ||
24 | |||
25 | #include "system.h" | ||
26 | #include "metadata.h" | ||
27 | #include "metadata_common.h" | ||
28 | #include "metadata_parsers.h" | ||
29 | #include "logf.h" | ||
30 | |||
31 | static const unsigned char bitspersamples[9] = { | ||
32 | 0, /* encoding */ | ||
33 | 8, /* 1: G.711 MULAW */ | ||
34 | 8, /* 2: Linear PCM 8bit */ | ||
35 | 16, /* 3: Linear PCM 16bit */ | ||
36 | 24, /* 4: Linear PCM 24bit */ | ||
37 | 32, /* 5: Linear PCM 32bit */ | ||
38 | 32, /* 6: IEEE float 32bit */ | ||
39 | 64, /* 7: IEEE float 64bit */ | ||
40 | /* encoding 8 - 26 unsupported. */ | ||
41 | 8, /* 27: G.711 ALAW */ | ||
42 | }; | ||
43 | |||
44 | static inline unsigned char get_au_bitspersample(unsigned int encoding) | ||
45 | { | ||
46 | if (encoding < 8) | ||
47 | return bitspersamples[encoding]; | ||
48 | else if (encoding == 27) | ||
49 | return bitspersamples[8]; | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | bool get_au_metadata(int fd, struct mp3entry* id3) | ||
55 | { | ||
56 | /* temporary buffer */ | ||
57 | unsigned char* buf = (unsigned char *)id3->path; | ||
58 | unsigned long numbytes = 0; | ||
59 | int offset; | ||
60 | |||
61 | id3->vbr = false; /* All Sun audio files are CBR */ | ||
62 | id3->filesize = filesize(fd); | ||
63 | id3->length = 0; | ||
64 | |||
65 | lseek(fd, 0, SEEK_SET); | ||
66 | if ((read(fd, buf, 24) < 24) || (memcmp(buf, ".snd", 4) != 0)) | ||
67 | { | ||
68 | /* | ||
69 | * no header | ||
70 | * | ||
71 | * frequency: 8000 Hz | ||
72 | * bits per sample: 8 bit | ||
73 | * channel: mono | ||
74 | */ | ||
75 | numbytes = id3->filesize; | ||
76 | id3->frequency = 8000; | ||
77 | id3->bitrate = 8; | ||
78 | } | ||
79 | else | ||
80 | { | ||
81 | /* parse header */ | ||
82 | |||
83 | /* data offset */ | ||
84 | offset = get_long_be(buf + 4); | ||
85 | if (offset < 24) | ||
86 | { | ||
87 | DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset); | ||
88 | return false; | ||
89 | } | ||
90 | /* data size */ | ||
91 | numbytes = get_long_be(buf + 8); | ||
92 | if (numbytes == (uint32_t)0xffffffff) | ||
93 | numbytes = id3->filesize - offset; | ||
94 | |||
95 | id3->frequency = get_long_be(buf + 16); | ||
96 | id3->bitrate = get_au_bitspersample(get_long_be(buf + 12)) * get_long_be(buf + 20) | ||
97 | * id3->frequency / 1000; | ||
98 | } | ||
99 | |||
100 | /* Calculate track length [ms] */ | ||
101 | if (id3->bitrate) | ||
102 | id3->length = (numbytes << 3) / id3->bitrate; | ||
103 | |||
104 | return true; | ||
105 | } | ||
diff --git a/lib/rbcodec/metadata/ay.c b/lib/rbcodec/metadata/ay.c new file mode 100644 index 0000000000..5d00264b3d --- /dev/null +++ b/lib/rbcodec/metadata/ay.c | |||
@@ -0,0 +1,148 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | |||
13 | /* Taken from blargg's Game_Music_Emu library */ | ||
14 | |||
15 | typedef unsigned char byte; | ||
16 | |||
17 | /* AY file header */ | ||
18 | enum { header_size = 0x14 }; | ||
19 | struct header_t | ||
20 | { | ||
21 | byte tag[8]; | ||
22 | byte vers; | ||
23 | byte player; | ||
24 | byte unused[2]; | ||
25 | byte author[2]; | ||
26 | byte comment[2]; | ||
27 | byte max_track; | ||
28 | byte first_track; | ||
29 | byte track_info[2]; | ||
30 | }; | ||
31 | |||
32 | struct file_t { | ||
33 | struct header_t const* header; | ||
34 | byte const* tracks; | ||
35 | byte const* end; /* end of file data */ | ||
36 | }; | ||
37 | |||
38 | static int get_be16( const void *a ) | ||
39 | { | ||
40 | return get_short_be( (void*) a ); | ||
41 | } | ||
42 | |||
43 | /* Given pointer to 2-byte offset of data, returns pointer to data, or NULL if | ||
44 | * offset is 0 or there is less than min_size bytes of data available. */ | ||
45 | static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size ) | ||
46 | { | ||
47 | int offset = (int16_t) get_be16( ptr ); | ||
48 | int pos = ptr - (byte const*) file->header; | ||
49 | int size = file->end - (byte const*) file->header; | ||
50 | int limit = size - min_size; | ||
51 | if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit ) | ||
52 | return NULL; | ||
53 | return ptr + offset; | ||
54 | } | ||
55 | |||
56 | static const char *parse_header( byte const in [], int size, struct file_t* out ) | ||
57 | { | ||
58 | if ( size < header_size ) | ||
59 | return "wrong file type"; | ||
60 | |||
61 | out->header = (struct header_t const*) in; | ||
62 | out->end = in + size; | ||
63 | struct header_t const* h = (struct header_t const*) in; | ||
64 | if ( memcmp( h->tag, "ZXAYEMUL", 8 ) ) | ||
65 | return "wrong file type"; | ||
66 | |||
67 | out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 ); | ||
68 | if ( !out->tracks ) | ||
69 | return "missing track data"; | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static void copy_ay_fields( struct file_t const* file, struct mp3entry* id3, int track ) | ||
75 | { | ||
76 | int track_count = file->header->max_track + 1; | ||
77 | |||
78 | /* calculate track length based on number of subtracks */ | ||
79 | if (track_count > 1) { | ||
80 | id3->length = file->header->max_track * 1000; | ||
81 | } else { | ||
82 | byte const* track_info = get_data( file, file->tracks + track * 4 + 2, 6 ); | ||
83 | if (track_info) | ||
84 | id3->length = get_be16( track_info + 4 ) * (1000 / 50); /* frames to msec */ | ||
85 | else id3->length = 120 * 1000; | ||
86 | } | ||
87 | |||
88 | if ( id3->length <= 0 ) | ||
89 | id3->length = 120 * 1000; /* 2 minutes */ | ||
90 | |||
91 | /* If meta info was found in the m3u skip next step */ | ||
92 | if (id3->title && id3->title[0]) return; | ||
93 | |||
94 | /* If file has more than one track will | ||
95 | use file name as title */ | ||
96 | char * tmp; | ||
97 | if (track_count <= 1) { | ||
98 | tmp = (char *) get_data( file, file->tracks + track * 4, 1 ); | ||
99 | if ( tmp ) id3->title = tmp; | ||
100 | } | ||
101 | |||
102 | /* Author */ | ||
103 | tmp = (char *) get_data( file, file->header->author, 1 ); | ||
104 | if (tmp) id3->artist = tmp; | ||
105 | |||
106 | /* Comment */ | ||
107 | tmp = (char *) get_data( file, file->header->comment, 1 ); | ||
108 | if (tmp) id3->comment = tmp; | ||
109 | } | ||
110 | |||
111 | static bool parse_ay_header(int fd, struct mp3entry *id3) | ||
112 | { | ||
113 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
114 | unsigned char* buf = (unsigned char *)id3->id3v2buf; | ||
115 | struct file_t file; | ||
116 | int read_bytes; | ||
117 | |||
118 | lseek(fd, 0, SEEK_SET); | ||
119 | if ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) < header_size) | ||
120 | return false; | ||
121 | |||
122 | buf [ID3V2_BUF_SIZE] = '\0'; | ||
123 | if ( parse_header( buf, read_bytes, &file ) ) | ||
124 | return false; | ||
125 | |||
126 | copy_ay_fields( &file, id3, 0 ); | ||
127 | return true; | ||
128 | } | ||
129 | |||
130 | bool get_ay_metadata(int fd, struct mp3entry* id3) | ||
131 | { | ||
132 | char ay_type[8]; | ||
133 | if ((lseek(fd, 0, SEEK_SET) < 0) || | ||
134 | read(fd, ay_type, 8) < 8) | ||
135 | return false; | ||
136 | |||
137 | id3->vbr = false; | ||
138 | id3->filesize = filesize(fd); | ||
139 | |||
140 | id3->bitrate = 706; | ||
141 | id3->frequency = 44100; | ||
142 | |||
143 | /* Make sure this is a ZX Ay file */ | ||
144 | if (memcmp( ay_type, "ZXAYEMUL", 8 ) != 0) | ||
145 | return false; | ||
146 | |||
147 | return parse_ay_header(fd, id3); | ||
148 | } | ||
diff --git a/lib/rbcodec/metadata/flac.c b/lib/rbcodec/metadata/flac.c new file mode 100644 index 0000000000..29937173fd --- /dev/null +++ b/lib/rbcodec/metadata/flac.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | bool get_flac_metadata(int fd, struct mp3entry* id3) | ||
34 | { | ||
35 | /* A simple parser to read vital metadata from a FLAC file - length, | ||
36 | * frequency, bitrate etc. This code should either be moved to a | ||
37 | * seperate file, or discarded in favour of the libFLAC code. | ||
38 | * The FLAC stream specification can be found at | ||
39 | * http://flac.sourceforge.net/format.html#stream | ||
40 | */ | ||
41 | |||
42 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
43 | unsigned char* buf = (unsigned char *)id3->path; | ||
44 | bool last_metadata = false; | ||
45 | bool rc = false; | ||
46 | |||
47 | if (!skip_id3v2(fd, id3) || (read(fd, buf, 4) < 4)) | ||
48 | { | ||
49 | return rc; | ||
50 | } | ||
51 | |||
52 | if (memcmp(buf, "fLaC", 4) != 0) | ||
53 | { | ||
54 | return rc; | ||
55 | } | ||
56 | |||
57 | while (!last_metadata) | ||
58 | { | ||
59 | unsigned long i; | ||
60 | int type; | ||
61 | |||
62 | if (read(fd, buf, 4) < 0) | ||
63 | { | ||
64 | return rc; | ||
65 | } | ||
66 | |||
67 | last_metadata = buf[0] & 0x80; | ||
68 | type = buf[0] & 0x7f; | ||
69 | /* The length of the block */ | ||
70 | i = (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
71 | |||
72 | if (type == 0) /* 0 is the STREAMINFO block */ | ||
73 | { | ||
74 | unsigned long totalsamples; | ||
75 | |||
76 | if (i >= sizeof(id3->path) || read(fd, buf, i) < 0) | ||
77 | { | ||
78 | return rc; | ||
79 | } | ||
80 | |||
81 | id3->vbr = true; /* All FLAC files are VBR */ | ||
82 | id3->filesize = filesize(fd); | ||
83 | id3->frequency = (buf[10] << 12) | (buf[11] << 4) | ||
84 | | ((buf[12] & 0xf0) >> 4); | ||
85 | rc = true; /* Got vital metadata */ | ||
86 | |||
87 | /* totalsamples is a 36-bit field, but we assume <= 32 bits are used */ | ||
88 | totalsamples = get_long_be(&buf[14]); | ||
89 | |||
90 | if(totalsamples > 0) | ||
91 | { | ||
92 | /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ | ||
93 | id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; | ||
94 | id3->bitrate = (id3->filesize * 8) / id3->length; | ||
95 | } | ||
96 | else if (totalsamples == 0) | ||
97 | { | ||
98 | id3->length = 0; | ||
99 | id3->bitrate = 0; | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | logf("flac length invalid!"); | ||
104 | return false; | ||
105 | } | ||
106 | |||
107 | } | ||
108 | else if (type == 4) /* 4 is the VORBIS_COMMENT block */ | ||
109 | { | ||
110 | /* The next i bytes of the file contain the VORBIS COMMENTS. */ | ||
111 | if (read_vorbis_tags(fd, id3, i) == 0) | ||
112 | { | ||
113 | return rc; | ||
114 | } | ||
115 | } | ||
116 | else if (!last_metadata) | ||
117 | { | ||
118 | /* Skip to next metadata block */ | ||
119 | if (lseek(fd, i, SEEK_CUR) < 0) | ||
120 | { | ||
121 | return rc; | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return true; | ||
127 | } | ||
diff --git a/lib/rbcodec/metadata/gbs.c b/lib/rbcodec/metadata/gbs.c new file mode 100644 index 0000000000..68f2b2a393 --- /dev/null +++ b/lib/rbcodec/metadata/gbs.c | |||
@@ -0,0 +1,65 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | |||
13 | static bool parse_gbs_header(int fd, struct mp3entry* id3) | ||
14 | { | ||
15 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
16 | unsigned char* buf = (unsigned char *)id3->path; | ||
17 | lseek(fd, 0, SEEK_SET); | ||
18 | if (read(fd, buf, 112) < 112) | ||
19 | return false; | ||
20 | |||
21 | /* Calculate track length with number of subtracks */ | ||
22 | id3->length = buf[4] * 1000; | ||
23 | |||
24 | /* If meta info was found in the m3u skip next step */ | ||
25 | if (id3->title && id3->title[0]) return true; | ||
26 | |||
27 | char *p = id3->id3v2buf; | ||
28 | |||
29 | /* Some metadata entries have 32 bytes length */ | ||
30 | /* Game */ | ||
31 | memcpy(p, &buf[16], 32); *(p + 33) = '\0'; | ||
32 | id3->title = p; | ||
33 | p += strlen(p)+1; | ||
34 | |||
35 | /* Artist */ | ||
36 | memcpy(p, &buf[48], 32); *(p + 33) = '\0'; | ||
37 | id3->artist = p; | ||
38 | p += strlen(p)+1; | ||
39 | |||
40 | /* Copyright */ | ||
41 | memcpy(p, &buf[80], 32); *(p + 33) = '\0'; | ||
42 | id3->album = p; | ||
43 | |||
44 | return true; | ||
45 | } | ||
46 | |||
47 | bool get_gbs_metadata(int fd, struct mp3entry* id3) | ||
48 | { | ||
49 | char gbs_type[3]; | ||
50 | if ((lseek(fd, 0, SEEK_SET) < 0) || | ||
51 | (read(fd, gbs_type, 3) < 3)) | ||
52 | return false; | ||
53 | |||
54 | id3->vbr = false; | ||
55 | id3->filesize = filesize(fd); | ||
56 | /* we only render 16 bits, 44.1KHz, Stereo */ | ||
57 | id3->bitrate = 706; | ||
58 | id3->frequency = 44100; | ||
59 | |||
60 | /* Check for GBS magic */ | ||
61 | if (memcmp( gbs_type, "GBS", 3 ) != 0) | ||
62 | return false; | ||
63 | |||
64 | return parse_gbs_header(fd, id3); | ||
65 | } | ||
diff --git a/lib/rbcodec/metadata/hes.c b/lib/rbcodec/metadata/hes.c new file mode 100644 index 0000000000..6d99d523cb --- /dev/null +++ b/lib/rbcodec/metadata/hes.c | |||
@@ -0,0 +1,39 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | #include "plugin.h" | ||
13 | |||
14 | bool get_hes_metadata(int fd, struct mp3entry* id3) | ||
15 | { | ||
16 | /* Use the id3v2 buffer part of the id3 structure as a temporary buffer */ | ||
17 | unsigned char* buf = (unsigned char *)id3->id3v2buf; | ||
18 | int read_bytes; | ||
19 | |||
20 | if ((lseek(fd, 0, SEEK_SET) < 0) | ||
21 | || ((read_bytes = read(fd, buf, 4)) < 4)) | ||
22 | return false; | ||
23 | |||
24 | /* Verify this is a HES file */ | ||
25 | if (memcmp(buf,"HESM",4) != 0) | ||
26 | return false; | ||
27 | |||
28 | id3->vbr = false; | ||
29 | id3->filesize = filesize(fd); | ||
30 | /* we only render 16 bits, 44.1KHz, Stereo */ | ||
31 | id3->bitrate = 706; | ||
32 | id3->frequency = 44100; | ||
33 | |||
34 | /* Set default track count (length)*/ | ||
35 | id3->length = 255 * 1000; | ||
36 | |||
37 | return true; | ||
38 | } | ||
39 | |||
diff --git a/lib/rbcodec/metadata/id3tags.c b/lib/rbcodec/metadata/id3tags.c new file mode 100644 index 0000000000..2dd1c662ed --- /dev/null +++ b/lib/rbcodec/metadata/id3tags.c | |||
@@ -0,0 +1,1199 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | /* | ||
22 | * Parts of this code has been stolen from the Ample project and was written | ||
23 | * by David H�deman. It has since been extended and enhanced pretty much by | ||
24 | * all sorts of friendly Rockbox people. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <errno.h> | ||
35 | #include <stdbool.h> | ||
36 | #include <stddef.h> | ||
37 | #include <ctype.h> | ||
38 | #include "string-extra.h" | ||
39 | #include "config.h" | ||
40 | #include "file.h" | ||
41 | #include "logf.h" | ||
42 | #include "system.h" | ||
43 | #include "replaygain.h" | ||
44 | #include "rbunicode.h" | ||
45 | |||
46 | #include "metadata.h" | ||
47 | #include "mp3data.h" | ||
48 | #if CONFIG_CODEC == SWCODEC | ||
49 | #include "metadata_common.h" | ||
50 | #endif | ||
51 | #include "metadata_parsers.h" | ||
52 | #include "misc.h" | ||
53 | |||
54 | static unsigned long unsync(unsigned long b0, | ||
55 | unsigned long b1, | ||
56 | unsigned long b2, | ||
57 | unsigned long b3) | ||
58 | { | ||
59 | return (((long)(b0 & 0x7F) << (3*7)) | | ||
60 | ((long)(b1 & 0x7F) << (2*7)) | | ||
61 | ((long)(b2 & 0x7F) << (1*7)) | | ||
62 | ((long)(b3 & 0x7F) << (0*7))); | ||
63 | } | ||
64 | |||
65 | static const char* const genres[] = { | ||
66 | "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", | ||
67 | "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", | ||
68 | "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", | ||
69 | "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", | ||
70 | "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", | ||
71 | "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", | ||
72 | "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", | ||
73 | "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", | ||
74 | "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", | ||
75 | "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", | ||
76 | "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", | ||
77 | "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", | ||
78 | "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", | ||
79 | |||
80 | /* winamp extensions */ | ||
81 | "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", | ||
82 | "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", | ||
83 | "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", | ||
84 | "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", | ||
85 | "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", | ||
86 | "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", | ||
87 | "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", | ||
88 | "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", | ||
89 | "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", | ||
90 | "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", | ||
91 | "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", | ||
92 | "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", | ||
93 | "Synthpop" | ||
94 | }; | ||
95 | |||
96 | #if CONFIG_CODEC != SWCODEC | ||
97 | static | ||
98 | #endif | ||
99 | char* id3_get_num_genre(unsigned int genre_num) | ||
100 | { | ||
101 | if (genre_num < ARRAYLEN(genres)) | ||
102 | return (char*)genres[genre_num]; | ||
103 | return NULL; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | HOW TO ADD ADDITIONAL ID3 VERSION 2 TAGS | ||
108 | Code and comments by Thomas Paul Diffenbach | ||
109 | |||
110 | To add another ID3v2 Tag, do the following: | ||
111 | 1. add a char* named for the tag to struct mp3entry in id3.h, | ||
112 | (I (tpd) prefer to use char* rather than ints, even for what seems like | ||
113 | numerical values, for cases where a number won't do, e.g., | ||
114 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" | ||
115 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" | ||
116 | Text is more flexible, and as the main use of id3 data is to | ||
117 | display it, converting it to an int just means reconverting to | ||
118 | display it, at a runtime cost.) | ||
119 | |||
120 | 2. If any special processing beyond copying the tag value from the Id3 | ||
121 | block to the struct mp3entry is rrequired (such as converting to an | ||
122 | int), write a function to perform this special processing. | ||
123 | |||
124 | This function's prototype must match that of | ||
125 | typedef tagPostProcessFunc, that is it must be: | ||
126 | int func( struct mp3entry*, char* tag, int bufferpos ) | ||
127 | the first argument is a pointer to the current mp3entry structure the | ||
128 | second argument is a pointer to the null terminated string value of the | ||
129 | tag found the third argument is the offset of the next free byte in the | ||
130 | mp3entry's buffer your function should return the corrected offset; if | ||
131 | you don't lengthen or shorten the tag string, you can return the third | ||
132 | argument unchanged. | ||
133 | |||
134 | Unless you have a good reason no to, make the function static. | ||
135 | TO JUST COPY THE TAG NO SPECIAL PROCESSING FUNCTION IS NEEDED. | ||
136 | |||
137 | 3. add one or more entries to the tagList array, using the format: | ||
138 | char* ID3 Tag symbolic name -- see the ID3 specification for these, | ||
139 | sizeof() that name minus 1, | ||
140 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), | ||
141 | pointer to your special processing function or NULL | ||
142 | if you need no special processing | ||
143 | flag indicating if this tag is binary or textual | ||
144 | Many ID3 symbolic names come in more than one form. You can add both | ||
145 | forms, each referencing the same variable in struct mp3entry. | ||
146 | If both forms are present, the last found will be used. | ||
147 | Note that the offset can be zero, in which case no entry will be set | ||
148 | in the mp3entry struct; the frame is still read into the buffer and | ||
149 | the special processing function is called (several times, if there | ||
150 | are several frames with the same name). | ||
151 | |||
152 | 4. Alternately, use the TAG_LIST_ENTRY macro with | ||
153 | ID3 tag symbolic name, | ||
154 | variable in struct mp3entry, | ||
155 | special processing function address | ||
156 | |||
157 | 5. Add code to wps-display.c function get_tag to assign a printf-like | ||
158 | format specifier for the tag */ | ||
159 | |||
160 | /* Structure for ID3 Tag extraction information */ | ||
161 | struct tag_resolver { | ||
162 | const char* tag; | ||
163 | int tag_length; | ||
164 | size_t offset; | ||
165 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); | ||
166 | bool binary; | ||
167 | }; | ||
168 | |||
169 | static bool global_ff_found; | ||
170 | |||
171 | static int unsynchronize(char* tag, int len, bool *ff_found) | ||
172 | { | ||
173 | int i; | ||
174 | unsigned char c; | ||
175 | unsigned char *rp, *wp; | ||
176 | |||
177 | wp = rp = (unsigned char *)tag; | ||
178 | |||
179 | rp = (unsigned char *)tag; | ||
180 | for(i = 0;i < len;i++) { | ||
181 | /* Read the next byte and write it back, but don't increment the | ||
182 | write pointer */ | ||
183 | c = *rp++; | ||
184 | *wp = c; | ||
185 | if(*ff_found) { | ||
186 | /* Increment the write pointer if it isn't an unsynch pattern */ | ||
187 | if(c != 0) | ||
188 | wp++; | ||
189 | *ff_found = false; | ||
190 | } else { | ||
191 | if(c == 0xff) | ||
192 | *ff_found = true; | ||
193 | wp++; | ||
194 | } | ||
195 | } | ||
196 | return (long)wp - (long)tag; | ||
197 | } | ||
198 | |||
199 | static int unsynchronize_frame(char* tag, int len) | ||
200 | { | ||
201 | bool ff_found = false; | ||
202 | |||
203 | return unsynchronize(tag, len, &ff_found); | ||
204 | } | ||
205 | |||
206 | static int read_unsynched(int fd, void *buf, int len) | ||
207 | { | ||
208 | int i; | ||
209 | int rc; | ||
210 | int remaining = len; | ||
211 | char *wp; | ||
212 | char *rp; | ||
213 | |||
214 | wp = buf; | ||
215 | |||
216 | while(remaining) { | ||
217 | rp = wp; | ||
218 | rc = read(fd, rp, remaining); | ||
219 | if(rc <= 0) | ||
220 | return rc; | ||
221 | |||
222 | i = unsynchronize(wp, remaining, &global_ff_found); | ||
223 | remaining -= i; | ||
224 | wp += i; | ||
225 | } | ||
226 | |||
227 | return len; | ||
228 | } | ||
229 | |||
230 | static int skip_unsynched(int fd, int len) | ||
231 | { | ||
232 | int rc; | ||
233 | int remaining = len; | ||
234 | int rlen; | ||
235 | char buf[32]; | ||
236 | |||
237 | while(remaining) { | ||
238 | rlen = MIN(sizeof(buf), (unsigned int)remaining); | ||
239 | rc = read(fd, buf, rlen); | ||
240 | if(rc <= 0) | ||
241 | return rc; | ||
242 | |||
243 | remaining -= unsynchronize(buf, rlen, &global_ff_found); | ||
244 | } | ||
245 | |||
246 | return len; | ||
247 | } | ||
248 | |||
249 | /* parse numeric value from string */ | ||
250 | static int parsetracknum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
251 | { | ||
252 | entry->tracknum = atoi( tag ); | ||
253 | return bufferpos; | ||
254 | } | ||
255 | |||
256 | /* parse numeric value from string */ | ||
257 | static int parsediscnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
258 | { | ||
259 | entry->discnum = atoi( tag ); | ||
260 | return bufferpos; | ||
261 | } | ||
262 | |||
263 | /* parse numeric value from string */ | ||
264 | static int parseyearnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
265 | { | ||
266 | entry->year = atoi( tag ); | ||
267 | return bufferpos; | ||
268 | } | ||
269 | |||
270 | /* parse numeric genre from string, version 2.2 and 2.3 */ | ||
271 | static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) | ||
272 | { | ||
273 | /* Use bufferpos to hold current position in entry->id3v2buf. */ | ||
274 | bufferpos = tag - entry->id3v2buf; | ||
275 | |||
276 | if(entry->id3version >= ID3_VER_2_4) { | ||
277 | /* In version 2.4 and up, there are no parentheses, and the genre frame | ||
278 | is a list of strings, either numbers or text. */ | ||
279 | |||
280 | /* Is it a number? */ | ||
281 | if(isdigit(tag[0])) { | ||
282 | entry->genre_string = id3_get_num_genre(atoi( tag )); | ||
283 | return bufferpos; | ||
284 | } else { | ||
285 | entry->genre_string = tag; | ||
286 | return bufferpos + strlen(tag) + 1; | ||
287 | } | ||
288 | } else { | ||
289 | if( tag[0] == '(' && tag[1] != '(' ) { | ||
290 | entry->genre_string = id3_get_num_genre(atoi( tag + 1 )); | ||
291 | return bufferpos; | ||
292 | } | ||
293 | else { | ||
294 | entry->genre_string = tag; | ||
295 | return bufferpos + strlen(tag) + 1; | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | |||
300 | #ifdef HAVE_ALBUMART | ||
301 | /* parse embed albumart */ | ||
302 | static int parsealbumart( struct mp3entry* entry, char* tag, int bufferpos ) | ||
303 | { | ||
304 | entry->has_embedded_albumart = false; | ||
305 | |||
306 | /* we currently don't support unsynchronizing albumart */ | ||
307 | if (entry->albumart.type == AA_TYPE_UNSYNC) | ||
308 | return bufferpos; | ||
309 | |||
310 | entry->albumart.type = AA_TYPE_UNKNOWN; | ||
311 | |||
312 | char *start = tag; | ||
313 | /* skip text encoding */ | ||
314 | tag += 1; | ||
315 | |||
316 | if (memcmp(tag, "image/", 6) == 0) | ||
317 | { | ||
318 | /* ID3 v2.3+ */ | ||
319 | tag += 6; | ||
320 | if (strcmp(tag, "jpeg") == 0) | ||
321 | { | ||
322 | entry->albumart.type = AA_TYPE_JPG; | ||
323 | tag += 5; | ||
324 | } | ||
325 | else if (strcmp(tag, "png") == 0) | ||
326 | { | ||
327 | entry->albumart.type = AA_TYPE_PNG; | ||
328 | tag += 4; | ||
329 | } | ||
330 | } | ||
331 | else | ||
332 | { | ||
333 | /* ID3 v2.2 */ | ||
334 | if (memcmp(tag, "JPG", 3) == 0) | ||
335 | entry->albumart.type = AA_TYPE_JPG; | ||
336 | else if (memcmp(tag, "PNG", 3) == 0) | ||
337 | entry->albumart.type = AA_TYPE_PNG; | ||
338 | tag += 3; | ||
339 | } | ||
340 | |||
341 | if (entry->albumart.type != AA_TYPE_UNKNOWN) | ||
342 | { | ||
343 | /* skip picture type */ | ||
344 | tag += 1; | ||
345 | /* skip description */ | ||
346 | tag = strchr(tag, '\0') + 1; | ||
347 | /* fixup offset&size for image data */ | ||
348 | entry->albumart.pos += tag - start; | ||
349 | entry->albumart.size -= tag - start; | ||
350 | entry->has_embedded_albumart = true; | ||
351 | } | ||
352 | /* return bufferpos as we didn't store anything in id3v2buf */ | ||
353 | return bufferpos; | ||
354 | } | ||
355 | #endif | ||
356 | |||
357 | /* parse user defined text, looking for album artist and replaygain | ||
358 | * information. | ||
359 | */ | ||
360 | static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | ||
361 | { | ||
362 | char* value = NULL; | ||
363 | int desc_len = strlen(tag); | ||
364 | int length = 0; | ||
365 | |||
366 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
367 | /* At least part of the value was read, so we can safely try to | ||
368 | * parse it */ | ||
369 | value = tag + desc_len + 1; | ||
370 | |||
371 | if (!strcasecmp(tag, "ALBUM ARTIST")) { | ||
372 | length = strlen(value) + 1; | ||
373 | strlcpy(tag, value, length); | ||
374 | entry->albumartist = tag; | ||
375 | #if CONFIG_CODEC == SWCODEC | ||
376 | } else { | ||
377 | /* Call parse_replaygain(). */ | ||
378 | parse_replaygain(tag, value, entry); | ||
379 | #endif | ||
380 | } | ||
381 | } | ||
382 | |||
383 | return tag - entry->id3v2buf + length; | ||
384 | } | ||
385 | |||
386 | #if CONFIG_CODEC == SWCODEC | ||
387 | /* parse RVA2 binary data and convert to replaygain information. */ | ||
388 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos) | ||
389 | { | ||
390 | int desc_len = strlen(tag); | ||
391 | int start_pos = tag - entry->id3v2buf; | ||
392 | int end_pos = start_pos + desc_len + 5; | ||
393 | unsigned char* value = tag + desc_len + 1; | ||
394 | |||
395 | /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel | ||
396 | * type is master volume. | ||
397 | */ | ||
398 | if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos | ||
399 | && *value++ == 1) { | ||
400 | long gain = 0; | ||
401 | long peak = 0; | ||
402 | long peakbits; | ||
403 | long peakbytes; | ||
404 | bool album = false; | ||
405 | |||
406 | /* The RVA2 specification is unclear on some things (id string and | ||
407 | * peak volume), but this matches how Quod Libet use them. | ||
408 | */ | ||
409 | |||
410 | gain = (int16_t) ((value[0] << 8) | value[1]); | ||
411 | value += 2; | ||
412 | peakbits = *value++; | ||
413 | peakbytes = (peakbits + 7) / 8; | ||
414 | |||
415 | /* Only use the topmost 24 bits for peak volume */ | ||
416 | if (peakbytes > 3) { | ||
417 | peakbytes = 3; | ||
418 | } | ||
419 | |||
420 | /* Make sure the peak bits were read */ | ||
421 | if (end_pos + peakbytes < bufferpos) { | ||
422 | long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8; | ||
423 | |||
424 | for ( ; peakbytes; peakbytes--) { | ||
425 | peak <<= 8; | ||
426 | peak += *value++; | ||
427 | } | ||
428 | |||
429 | peak <<= shift; | ||
430 | |||
431 | if (peakbits > 24) { | ||
432 | peak += *value >> (8 - shift); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if (strcasecmp(tag, "album") == 0) { | ||
437 | album = true; | ||
438 | } else if (strcasecmp(tag, "track") != 0) { | ||
439 | /* Only accept non-track values if we don't have any previous | ||
440 | * value. | ||
441 | */ | ||
442 | if (entry->track_gain != 0) { | ||
443 | return start_pos; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | parse_replaygain_int(album, gain, peak * 2, entry); | ||
448 | } | ||
449 | |||
450 | return start_pos; | ||
451 | } | ||
452 | #endif | ||
453 | |||
454 | static int parsembtid( struct mp3entry* entry, char* tag, int bufferpos ) | ||
455 | { | ||
456 | char* value = NULL; | ||
457 | int desc_len = strlen(tag); | ||
458 | /*DEBUGF("MBID len: %d\n", desc_len);*/ | ||
459 | /* Musicbrainz track IDs are always 36 chars long */ | ||
460 | const size_t mbtid_len = 36; | ||
461 | |||
462 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) | ||
463 | { | ||
464 | value = tag + desc_len + 1; | ||
465 | |||
466 | if (strcasecmp(tag, "http://musicbrainz.org") == 0) | ||
467 | { | ||
468 | if (mbtid_len == strlen(value)) | ||
469 | { | ||
470 | entry->mb_track_id = value; | ||
471 | return bufferpos + mbtid_len + 1; | ||
472 | } | ||
473 | } | ||
474 | } | ||
475 | |||
476 | return bufferpos; | ||
477 | } | ||
478 | |||
479 | static const struct tag_resolver taglist[] = { | ||
480 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, false }, | ||
481 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL, false }, | ||
482 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL, false }, | ||
483 | { "TT2", 3, offsetof(struct mp3entry, title), NULL, false }, | ||
484 | { "TALB", 4, offsetof(struct mp3entry, album), NULL, false }, | ||
485 | { "TAL", 3, offsetof(struct mp3entry, album), NULL, false }, | ||
486 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
487 | { "TPOS", 4, offsetof(struct mp3entry, disc_string), &parsediscnum, false }, | ||
488 | { "TPA", 3, offsetof(struct mp3entry, disc_string), &parsediscnum, false }, | ||
489 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
490 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
491 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
492 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
493 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false }, | ||
494 | { "TCM", 3, offsetof(struct mp3entry, composer), NULL, false }, | ||
495 | { "TPE2", 4, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
496 | { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
497 | { "TIT1", 4, offsetof(struct mp3entry, grouping), NULL, false }, | ||
498 | { "TT1", 3, offsetof(struct mp3entry, grouping), NULL, false }, | ||
499 | { "COMM", 4, offsetof(struct mp3entry, comment), NULL, false }, | ||
500 | { "COM", 3, offsetof(struct mp3entry, comment), NULL, false }, | ||
501 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
502 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
503 | #ifdef HAVE_ALBUMART | ||
504 | { "APIC", 4, 0, &parsealbumart, true }, | ||
505 | { "PIC", 3, 0, &parsealbumart, true }, | ||
506 | #endif | ||
507 | { "TXXX", 4, 0, &parseuser, false }, | ||
508 | #if CONFIG_CODEC == SWCODEC | ||
509 | { "RVA2", 4, 0, &parserva2, true }, | ||
510 | #endif | ||
511 | { "UFID", 4, 0, &parsembtid, false }, | ||
512 | }; | ||
513 | |||
514 | #define TAGLIST_SIZE ((int)ARRAYLEN(taglist)) | ||
515 | |||
516 | /* Get the length of an ID3 string in the given encoding. Returns the length | ||
517 | * in bytes, including end nil, or -1 if the encoding is unknown. | ||
518 | */ | ||
519 | static int unicode_len(char encoding, const void* string) | ||
520 | { | ||
521 | int len = 0; | ||
522 | |||
523 | if (encoding == 0x01 || encoding == 0x02) { | ||
524 | char first; | ||
525 | const char *s = string; | ||
526 | /* string might be unaligned, so using short* can crash on ARM and SH1 */ | ||
527 | do { | ||
528 | first = *s++; | ||
529 | } while ((first | *s++) != 0); | ||
530 | |||
531 | len = s - (const char*) string; | ||
532 | } else { | ||
533 | len = strlen((char*) string) + 1; | ||
534 | } | ||
535 | |||
536 | return len; | ||
537 | } | ||
538 | |||
539 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 | ||
540 | string. If it is, we convert it to a UTF-8 string. If it's not unicode, | ||
541 | we convert from the default codepage */ | ||
542 | static int unicode_munge(char* string, char* utf8buf, int *len) { | ||
543 | long tmp; | ||
544 | bool le = false; | ||
545 | int i = 0; | ||
546 | unsigned char *str = (unsigned char *)string; | ||
547 | int templen = 0; | ||
548 | unsigned char* utf8 = (unsigned char *)utf8buf; | ||
549 | |||
550 | switch (str[0]) { | ||
551 | case 0x00: /* Type 0x00 is ordinary ISO 8859-1 */ | ||
552 | str++; | ||
553 | (*len)--; | ||
554 | utf8 = iso_decode(str, utf8, -1, *len); | ||
555 | *utf8 = 0; | ||
556 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
557 | break; | ||
558 | |||
559 | case 0x01: /* Unicode with or without BOM */ | ||
560 | case 0x02: | ||
561 | (*len)--; | ||
562 | str++; | ||
563 | |||
564 | /* Handle frames with more than one string | ||
565 | (needed for TXXX frames).*/ | ||
566 | do { | ||
567 | tmp = bytes2int(0, 0, str[0], str[1]); | ||
568 | |||
569 | /* Now check if there is a BOM | ||
570 | (zero-width non-breaking space, 0xfeff) | ||
571 | and if it is in little or big endian format */ | ||
572 | if(tmp == 0xfffe) { /* Little endian? */ | ||
573 | le = true; | ||
574 | str += 2; | ||
575 | (*len)-=2; | ||
576 | } else if(tmp == 0xfeff) { /* Big endian? */ | ||
577 | str += 2; | ||
578 | (*len)-=2; | ||
579 | } else | ||
580 | /* If there is no BOM (which is a specification violation), | ||
581 | let's try to guess it. If one of the bytes is 0x00, it is | ||
582 | probably the most significant one. */ | ||
583 | if(str[1] == 0) | ||
584 | le = true; | ||
585 | |||
586 | while ((i < *len) && (str[0] || str[1])) { | ||
587 | if(le) | ||
588 | utf8 = utf16LEdecode(str, utf8, 1); | ||
589 | else | ||
590 | utf8 = utf16BEdecode(str, utf8, 1); | ||
591 | |||
592 | str+=2; | ||
593 | i += 2; | ||
594 | } | ||
595 | |||
596 | *utf8++ = 0; /* Terminate the string */ | ||
597 | templen += (strlen(&utf8buf[templen]) + 1); | ||
598 | str += 2; | ||
599 | i+=2; | ||
600 | } while(i < *len); | ||
601 | *len = templen - 1; | ||
602 | break; | ||
603 | |||
604 | case 0x03: /* UTF-8 encoded string */ | ||
605 | for(i=0; i < *len; i++) | ||
606 | utf8[i] = str[i+1]; | ||
607 | (*len)--; | ||
608 | break; | ||
609 | |||
610 | default: /* Plain old string */ | ||
611 | utf8 = iso_decode(str, utf8, -1, *len); | ||
612 | *utf8 = 0; | ||
613 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
614 | break; | ||
615 | } | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | /* | ||
620 | * Sets the title of an MP3 entry based on its ID3v1 tag. | ||
621 | * | ||
622 | * Arguments: file - the MP3 file to scen for a ID3v1 tag | ||
623 | * entry - the entry to set the title in | ||
624 | * | ||
625 | * Returns: true if a title was found and created, else false | ||
626 | */ | ||
627 | bool setid3v1title(int fd, struct mp3entry *entry) | ||
628 | { | ||
629 | unsigned char buffer[128]; | ||
630 | static const char offsets[] = {3, 33, 63, 97, 93, 125, 127}; | ||
631 | int i, j; | ||
632 | unsigned char* utf8; | ||
633 | |||
634 | if (-1 == lseek(fd, -128, SEEK_END)) | ||
635 | return false; | ||
636 | |||
637 | if (read(fd, buffer, sizeof buffer) != sizeof buffer) | ||
638 | return false; | ||
639 | |||
640 | if (strncmp((char *)buffer, "TAG", 3)) | ||
641 | return false; | ||
642 | |||
643 | entry->id3v1len = 128; | ||
644 | entry->id3version = ID3_VER_1_0; | ||
645 | |||
646 | for (i=0; i < (int)sizeof offsets; i++) { | ||
647 | unsigned char* ptr = (unsigned char *)buffer + offsets[i]; | ||
648 | |||
649 | switch(i) { | ||
650 | case 0: | ||
651 | case 1: | ||
652 | case 2: | ||
653 | /* kill trailing space in strings */ | ||
654 | for (j=29; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
655 | ptr[j] = 0; | ||
656 | /* convert string to utf8 */ | ||
657 | utf8 = (unsigned char *)entry->id3v1buf[i]; | ||
658 | utf8 = iso_decode(ptr, utf8, -1, 30); | ||
659 | /* make sure string is terminated */ | ||
660 | *utf8 = 0; | ||
661 | break; | ||
662 | |||
663 | case 3: | ||
664 | /* kill trailing space in strings */ | ||
665 | for (j=27; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
666 | ptr[j] = 0; | ||
667 | /* convert string to utf8 */ | ||
668 | utf8 = (unsigned char *)entry->id3v1buf[3]; | ||
669 | utf8 = iso_decode(ptr, utf8, -1, 28); | ||
670 | /* make sure string is terminated */ | ||
671 | *utf8 = 0; | ||
672 | break; | ||
673 | |||
674 | case 4: | ||
675 | ptr[4] = 0; | ||
676 | entry->year = atoi((char *)ptr); | ||
677 | break; | ||
678 | |||
679 | case 5: | ||
680 | /* id3v1.1 uses last two bytes of comment field for track | ||
681 | number: first must be 0 and second is track num */ | ||
682 | if (!ptr[0] && ptr[1]) { | ||
683 | entry->tracknum = ptr[1]; | ||
684 | entry->id3version = ID3_VER_1_1; | ||
685 | } | ||
686 | break; | ||
687 | |||
688 | case 6: | ||
689 | /* genre */ | ||
690 | entry->genre_string = id3_get_num_genre(ptr[0]); | ||
691 | break; | ||
692 | } | ||
693 | } | ||
694 | |||
695 | entry->title = entry->id3v1buf[0]; | ||
696 | entry->artist = entry->id3v1buf[1]; | ||
697 | entry->album = entry->id3v1buf[2]; | ||
698 | entry->comment = entry->id3v1buf[3]; | ||
699 | |||
700 | return true; | ||
701 | } | ||
702 | |||
703 | |||
704 | /* | ||
705 | * Sets the title of an MP3 entry based on its ID3v2 tag. | ||
706 | * | ||
707 | * Arguments: file - the MP3 file to scan for a ID3v2 tag | ||
708 | * entry - the entry to set the title in | ||
709 | * | ||
710 | * Returns: true if a title was found and created, else false | ||
711 | */ | ||
712 | void setid3v2title(int fd, struct mp3entry *entry) | ||
713 | { | ||
714 | int minframesize; | ||
715 | int size; | ||
716 | long bufferpos = 0, totframelen, framelen; | ||
717 | char header[10]; | ||
718 | char tmp[4]; | ||
719 | unsigned char version; | ||
720 | char *buffer = entry->id3v2buf; | ||
721 | int bytesread = 0; | ||
722 | int buffersize = sizeof(entry->id3v2buf); | ||
723 | unsigned char global_flags; | ||
724 | int flags; | ||
725 | bool global_unsynch = false; | ||
726 | bool unsynch = false; | ||
727 | int i, j; | ||
728 | int rc; | ||
729 | #if CONFIG_CODEC == SWCODEC | ||
730 | bool itunes_gapless = false; | ||
731 | #endif | ||
732 | |||
733 | global_ff_found = false; | ||
734 | |||
735 | /* Bail out if the tag is shorter than 10 bytes */ | ||
736 | if(entry->id3v2len < 10) | ||
737 | return; | ||
738 | |||
739 | /* Read the ID3 tag version from the header */ | ||
740 | lseek(fd, 0, SEEK_SET); | ||
741 | if(10 != read(fd, header, 10)) | ||
742 | return; | ||
743 | |||
744 | /* Get the total ID3 tag size */ | ||
745 | size = entry->id3v2len - 10; | ||
746 | |||
747 | version = header[3]; | ||
748 | switch ( version ) { | ||
749 | case 2: | ||
750 | version = ID3_VER_2_2; | ||
751 | minframesize = 8; | ||
752 | break; | ||
753 | |||
754 | case 3: | ||
755 | version = ID3_VER_2_3; | ||
756 | minframesize = 12; | ||
757 | break; | ||
758 | |||
759 | case 4: | ||
760 | version = ID3_VER_2_4; | ||
761 | minframesize = 12; | ||
762 | break; | ||
763 | |||
764 | default: | ||
765 | /* unsupported id3 version */ | ||
766 | return; | ||
767 | } | ||
768 | entry->id3version = version; | ||
769 | entry->tracknum = entry->year = entry->discnum = 0; | ||
770 | entry->title = entry->artist = entry->album = NULL; /* FIXME incomplete */ | ||
771 | |||
772 | global_flags = header[5]; | ||
773 | |||
774 | /* Skip the extended header if it is present */ | ||
775 | if(global_flags & 0x40) { | ||
776 | if(version == ID3_VER_2_3) { | ||
777 | if(10 != read(fd, header, 10)) | ||
778 | return; | ||
779 | /* The 2.3 extended header size doesn't include the header size | ||
780 | field itself. Also, it is not unsynched. */ | ||
781 | framelen = | ||
782 | bytes2int(header[0], header[1], header[2], header[3]) + 4; | ||
783 | |||
784 | /* Skip the rest of the header */ | ||
785 | lseek(fd, framelen - 10, SEEK_CUR); | ||
786 | } | ||
787 | |||
788 | if(version >= ID3_VER_2_4) { | ||
789 | if(4 != read(fd, header, 4)) | ||
790 | return; | ||
791 | |||
792 | /* The 2.4 extended header size does include the entire header, | ||
793 | so here we can just skip it. This header is unsynched. */ | ||
794 | framelen = unsync(header[0], header[1], | ||
795 | header[2], header[3]); | ||
796 | |||
797 | lseek(fd, framelen - 4, SEEK_CUR); | ||
798 | } | ||
799 | } | ||
800 | |||
801 | /* Is unsynchronization applied? */ | ||
802 | if(global_flags & 0x80) { | ||
803 | global_unsynch = true; | ||
804 | } | ||
805 | |||
806 | /* | ||
807 | * We must have at least minframesize bytes left for the | ||
808 | * remaining frames to be interesting | ||
809 | */ | ||
810 | while (size >= minframesize && bufferpos < buffersize - 1) { | ||
811 | flags = 0; | ||
812 | |||
813 | /* Read frame header and check length */ | ||
814 | if(version >= ID3_VER_2_3) { | ||
815 | if(global_unsynch && version <= ID3_VER_2_3) | ||
816 | rc = read_unsynched(fd, header, 10); | ||
817 | else | ||
818 | rc = read(fd, header, 10); | ||
819 | if(rc != 10) | ||
820 | return; | ||
821 | /* Adjust for the 10 bytes we read */ | ||
822 | size -= 10; | ||
823 | |||
824 | flags = bytes2int(0, 0, header[8], header[9]); | ||
825 | |||
826 | if (version >= ID3_VER_2_4) { | ||
827 | framelen = unsync(header[4], header[5], | ||
828 | header[6], header[7]); | ||
829 | } else { | ||
830 | /* version .3 files don't use synchsafe ints for | ||
831 | * size */ | ||
832 | framelen = bytes2int(header[4], header[5], | ||
833 | header[6], header[7]); | ||
834 | } | ||
835 | } else { | ||
836 | if(6 != read(fd, header, 6)) | ||
837 | return; | ||
838 | /* Adjust for the 6 bytes we read */ | ||
839 | size -= 6; | ||
840 | |||
841 | framelen = bytes2int(0, header[3], header[4], header[5]); | ||
842 | } | ||
843 | |||
844 | logf("framelen = %ld, flags = 0x%04x", framelen, flags); | ||
845 | if(framelen == 0){ | ||
846 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) | ||
847 | return; | ||
848 | else | ||
849 | continue; | ||
850 | } | ||
851 | |||
852 | unsynch = false; | ||
853 | |||
854 | if(flags) | ||
855 | { | ||
856 | if (version >= ID3_VER_2_4) { | ||
857 | if(flags & 0x0040) { /* Grouping identity */ | ||
858 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
859 | framelen--; | ||
860 | } | ||
861 | } else { | ||
862 | if(flags & 0x0020) { /* Grouping identity */ | ||
863 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
864 | framelen--; | ||
865 | } | ||
866 | } | ||
867 | |||
868 | if(flags & 0x000c) /* Compression or encryption */ | ||
869 | { | ||
870 | /* Skip it */ | ||
871 | size -= framelen; | ||
872 | lseek(fd, framelen, SEEK_CUR); | ||
873 | continue; | ||
874 | } | ||
875 | |||
876 | if(flags & 0x0002) /* Unsynchronization */ | ||
877 | unsynch = true; | ||
878 | |||
879 | if (version >= ID3_VER_2_4) { | ||
880 | if(flags & 0x0001) { /* Data length indicator */ | ||
881 | if(4 != read(fd, tmp, 4)) | ||
882 | return; | ||
883 | |||
884 | /* We don't need the data length */ | ||
885 | framelen -= 4; | ||
886 | } | ||
887 | } | ||
888 | } | ||
889 | |||
890 | if (framelen == 0) | ||
891 | continue; | ||
892 | |||
893 | if (framelen < 0) | ||
894 | return; | ||
895 | |||
896 | /* Keep track of the remaining frame size */ | ||
897 | totframelen = framelen; | ||
898 | |||
899 | /* If the frame is larger than the remaining buffer space we try | ||
900 | to read as much as would fit in the buffer */ | ||
901 | if(framelen >= buffersize - bufferpos) | ||
902 | framelen = buffersize - bufferpos - 1; | ||
903 | |||
904 | /* Limit the maximum length of an id3 data item to ID3V2_MAX_ITEM_SIZE | ||
905 | bytes. This reduces the chance that the available buffer is filled | ||
906 | by single metadata items like large comments. */ | ||
907 | if (ID3V2_MAX_ITEM_SIZE < framelen) | ||
908 | framelen = ID3V2_MAX_ITEM_SIZE; | ||
909 | |||
910 | logf("id3v2 frame: %.4s", header); | ||
911 | |||
912 | /* Check for certain frame headers | ||
913 | |||
914 | 'size' is the amount of frame bytes remaining. We decrement it by | ||
915 | the amount of bytes we read. If we fail to read as many bytes as | ||
916 | we expect, we assume that we can't read from this file, and bail | ||
917 | out. | ||
918 | |||
919 | For each frame. we will iterate over the list of supported tags, | ||
920 | and read the tag into entry's buffer. All tags will be kept as | ||
921 | strings, for cases where a number won't do, e.g., YEAR: "circa | ||
922 | 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: | ||
923 | "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more | ||
924 | flexible, and as the main use of id3 data is to display it, | ||
925 | converting it to an int just means reconverting to display it, at a | ||
926 | runtime cost. | ||
927 | |||
928 | For tags that the current code does convert to ints, a post | ||
929 | processing function will be called via a pointer to function. */ | ||
930 | |||
931 | for (i=0; i<TAGLIST_SIZE; i++) { | ||
932 | const struct tag_resolver* tr = &taglist[i]; | ||
933 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) | ||
934 | : NULL; | ||
935 | char* tag; | ||
936 | |||
937 | /* Only ID3_VER_2_2 uses frames with three-character names. */ | ||
938 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) | ||
939 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { | ||
940 | continue; | ||
941 | } | ||
942 | |||
943 | if( !memcmp( header, tr->tag, tr->tag_length ) ) { | ||
944 | |||
945 | /* found a tag matching one in tagList, and not yet filled */ | ||
946 | tag = buffer + bufferpos; | ||
947 | |||
948 | if(global_unsynch && version <= ID3_VER_2_3) | ||
949 | bytesread = read_unsynched(fd, tag, framelen); | ||
950 | else | ||
951 | bytesread = read(fd, tag, framelen); | ||
952 | |||
953 | if( bytesread != framelen ) | ||
954 | return; | ||
955 | |||
956 | size -= bytesread; | ||
957 | |||
958 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | ||
959 | bytesread = unsynchronize_frame(tag, bytesread); | ||
960 | |||
961 | /* the COMM frame has a 3 char field to hold an ISO-639-1 | ||
962 | * language string and an optional short description; | ||
963 | * remove them so unicode_munge can work correctly | ||
964 | */ | ||
965 | |||
966 | if((tr->tag_length == 4 && !memcmp( header, "COMM", 4)) || | ||
967 | (tr->tag_length == 3 && !memcmp( header, "COM", 3))) { | ||
968 | int offset; | ||
969 | if(bytesread >= 8 && !strncmp(tag+4, "iTun", 4)) { | ||
970 | #if CONFIG_CODEC == SWCODEC | ||
971 | /* check for iTunes gapless information */ | ||
972 | if(bytesread >= 12 && !strncmp(tag+4, "iTunSMPB", 8)) | ||
973 | itunes_gapless = true; | ||
974 | else | ||
975 | #endif | ||
976 | /* ignore other with iTunes tags */ | ||
977 | break; | ||
978 | } | ||
979 | |||
980 | offset = 3 + unicode_len(*tag, tag + 4); | ||
981 | if(bytesread > offset) { | ||
982 | bytesread -= offset; | ||
983 | memmove(tag + 1, tag + 1 + offset, bytesread - 1); | ||
984 | } | ||
985 | } | ||
986 | |||
987 | /* Attempt to parse Unicode string only if the tag contents | ||
988 | aren't binary */ | ||
989 | if(!tr->binary) { | ||
990 | /* UTF-8 could potentially be 3 times larger */ | ||
991 | /* so we need to create a new buffer */ | ||
992 | char utf8buf[(3 * bytesread) + 1]; | ||
993 | |||
994 | unicode_munge( tag, utf8buf, &bytesread ); | ||
995 | |||
996 | if(bytesread >= buffersize - bufferpos) | ||
997 | bytesread = buffersize - bufferpos - 1; | ||
998 | |||
999 | if ( /* Is it an embedded cuesheet? */ | ||
1000 | (tr->tag_length == 4 && !memcmp(header, "TXXX", 4)) && | ||
1001 | (bytesread >= 14 && !strncmp(utf8buf, "CUESHEET", 8)) | ||
1002 | ) { | ||
1003 | unsigned char char_enc = 0; | ||
1004 | /* [enc type]+"CUESHEET\0" = 10 */ | ||
1005 | unsigned char cuesheet_offset = 10; | ||
1006 | switch (tag[0]) { | ||
1007 | case 0x00: | ||
1008 | char_enc = CHAR_ENC_ISO_8859_1; | ||
1009 | break; | ||
1010 | case 0x01: | ||
1011 | tag++; | ||
1012 | if (!memcmp(tag, | ||
1013 | BOM_UTF_16_BE, BOM_UTF_16_SIZE)) { | ||
1014 | char_enc = CHAR_ENC_UTF_16_BE; | ||
1015 | } else if (!memcmp(tag, | ||
1016 | BOM_UTF_16_LE, BOM_UTF_16_SIZE)) { | ||
1017 | char_enc = CHAR_ENC_UTF_16_LE; | ||
1018 | } | ||
1019 | /* \1 + BOM(2) + C0U0E0S0H0E0E0T000 = 21 */ | ||
1020 | cuesheet_offset = 21; | ||
1021 | break; | ||
1022 | case 0x02: | ||
1023 | char_enc = CHAR_ENC_UTF_16_BE; | ||
1024 | /* \2 + 0C0U0E0S0H0E0E0T00 = 19 */ | ||
1025 | cuesheet_offset = 19; | ||
1026 | break; | ||
1027 | case 0x03: | ||
1028 | char_enc = CHAR_ENC_UTF_8; | ||
1029 | break; | ||
1030 | } | ||
1031 | if (char_enc > 0) { | ||
1032 | entry->has_embedded_cuesheet = true; | ||
1033 | entry->embedded_cuesheet.pos = lseek(fd, 0, SEEK_CUR) | ||
1034 | - framelen + cuesheet_offset; | ||
1035 | entry->embedded_cuesheet.size = totframelen | ||
1036 | - cuesheet_offset; | ||
1037 | entry->embedded_cuesheet.encoding = char_enc; | ||
1038 | } | ||
1039 | break; | ||
1040 | } | ||
1041 | |||
1042 | for (j = 0; j < bytesread; j++) | ||
1043 | tag[j] = utf8buf[j]; | ||
1044 | |||
1045 | /* remove trailing spaces */ | ||
1046 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | ||
1047 | bytesread--; | ||
1048 | } | ||
1049 | |||
1050 | if(bytesread == 0) | ||
1051 | /* Skip empty frames */ | ||
1052 | break; | ||
1053 | |||
1054 | tag[bytesread] = 0; | ||
1055 | bufferpos += bytesread + 1; | ||
1056 | |||
1057 | #if CONFIG_CODEC == SWCODEC | ||
1058 | /* parse the tag if it contains iTunes gapless info */ | ||
1059 | if (itunes_gapless) | ||
1060 | { | ||
1061 | itunes_gapless = false; | ||
1062 | entry->lead_trim = get_itunes_int32(tag, 1); | ||
1063 | entry->tail_trim = get_itunes_int32(tag, 2); | ||
1064 | } | ||
1065 | #endif | ||
1066 | |||
1067 | /* Note that parser functions sometimes set *ptag to NULL, so | ||
1068 | * the "!*ptag" check here doesn't always have the desired | ||
1069 | * effect. Should the parser functions (parsegenre in | ||
1070 | * particular) be updated to handle the case of being called | ||
1071 | * multiple times, or should the "*ptag" check be removed? | ||
1072 | */ | ||
1073 | if (ptag && !*ptag) | ||
1074 | *ptag = tag; | ||
1075 | |||
1076 | #ifdef HAVE_ALBUMART | ||
1077 | /* albumart */ | ||
1078 | if ((!entry->has_embedded_albumart) && | ||
1079 | ((tr->tag_length == 4 && !memcmp( header, "APIC", 4)) || | ||
1080 | (tr->tag_length == 3 && !memcmp( header, "PIC" , 3)))) | ||
1081 | { | ||
1082 | if (unsynch || (global_unsynch && version <= ID3_VER_2_3)) | ||
1083 | entry->albumart.type = AA_TYPE_UNSYNC; | ||
1084 | else | ||
1085 | { | ||
1086 | entry->albumart.pos = lseek(fd, 0, SEEK_CUR) - framelen; | ||
1087 | entry->albumart.size = totframelen; | ||
1088 | entry->albumart.type = AA_TYPE_UNKNOWN; | ||
1089 | } | ||
1090 | } | ||
1091 | #endif | ||
1092 | if( tr->ppFunc ) | ||
1093 | bufferpos = tr->ppFunc(entry, tag, bufferpos); | ||
1094 | break; | ||
1095 | } | ||
1096 | } | ||
1097 | |||
1098 | if( i == TAGLIST_SIZE ) { | ||
1099 | /* no tag in tagList was found, or it was a repeat. | ||
1100 | skip it using the total size */ | ||
1101 | |||
1102 | if(global_unsynch && version <= ID3_VER_2_3) { | ||
1103 | size -= skip_unsynched(fd, totframelen); | ||
1104 | } else { | ||
1105 | size -= totframelen; | ||
1106 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) | ||
1107 | return; | ||
1108 | } | ||
1109 | } else { | ||
1110 | /* Seek to the next frame */ | ||
1111 | if(framelen < totframelen) | ||
1112 | lseek(fd, totframelen - framelen, SEEK_CUR); | ||
1113 | } | ||
1114 | } | ||
1115 | } | ||
1116 | |||
1117 | /* | ||
1118 | * Calculates the size of the ID3v2 tag. | ||
1119 | * | ||
1120 | * Arguments: file - the file to search for a tag. | ||
1121 | * | ||
1122 | * Returns: the size of the tag or 0 if none was found | ||
1123 | */ | ||
1124 | int getid3v2len(int fd) | ||
1125 | { | ||
1126 | char buf[6]; | ||
1127 | int offset; | ||
1128 | |||
1129 | /* Make sure file has a ID3 tag */ | ||
1130 | if((-1 == lseek(fd, 0, SEEK_SET)) || | ||
1131 | (read(fd, buf, 6) != 6) || | ||
1132 | (strncmp(buf, "ID3", strlen("ID3")) != 0)) | ||
1133 | offset = 0; | ||
1134 | |||
1135 | /* Now check what the ID3v2 size field says */ | ||
1136 | else | ||
1137 | if(read(fd, buf, 4) != 4) | ||
1138 | offset = 0; | ||
1139 | else | ||
1140 | offset = unsync(buf[0], buf[1], buf[2], buf[3]) + 10; | ||
1141 | |||
1142 | logf("ID3V2 Length: 0x%x", offset); | ||
1143 | return offset; | ||
1144 | } | ||
1145 | |||
1146 | #ifdef DEBUG_STANDALONE | ||
1147 | |||
1148 | char *secs2str(int ms) | ||
1149 | { | ||
1150 | static char buffer[32]; | ||
1151 | int secs = ms/1000; | ||
1152 | ms %= 1000; | ||
1153 | snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100); | ||
1154 | return buffer; | ||
1155 | } | ||
1156 | |||
1157 | int main(int argc, char **argv) | ||
1158 | { | ||
1159 | int i; | ||
1160 | for(i=1; i<argc; i++) { | ||
1161 | struct mp3entry mp3; | ||
1162 | mp3.album = "Bogus"; | ||
1163 | if(mp3info(&mp3, argv[i], false)) { | ||
1164 | printf("Failed to get %s\n", argv[i]); | ||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | printf("****** File: %s\n" | ||
1169 | " Title: %s\n" | ||
1170 | " Artist: %s\n" | ||
1171 | " Album: %s\n" | ||
1172 | " Genre: %s (%d) \n" | ||
1173 | " Composer: %s\n" | ||
1174 | " Year: %s (%d)\n" | ||
1175 | " Track: %s (%d)\n" | ||
1176 | " Length: %s / %d s\n" | ||
1177 | " Bitrate: %d\n" | ||
1178 | " Frequency: %d\n", | ||
1179 | argv[i], | ||
1180 | mp3.title?mp3.title:"<blank>", | ||
1181 | mp3.artist?mp3.artist:"<blank>", | ||
1182 | mp3.album?mp3.album:"<blank>", | ||
1183 | mp3.genre_string?mp3.genre_string:"<blank>", | ||
1184 | mp3.genre, | ||
1185 | mp3.composer?mp3.composer:"<blank>", | ||
1186 | mp3.year_string?mp3.year_string:"<blank>", | ||
1187 | mp3.year, | ||
1188 | mp3.track_string?mp3.track_string:"<blank>", | ||
1189 | mp3.tracknum, | ||
1190 | secs2str(mp3.length), | ||
1191 | mp3.length/1000, | ||
1192 | mp3.bitrate, | ||
1193 | mp3.frequency); | ||
1194 | } | ||
1195 | |||
1196 | return 0; | ||
1197 | } | ||
1198 | |||
1199 | #endif | ||
diff --git a/lib/rbcodec/metadata/kss.c b/lib/rbcodec/metadata/kss.c new file mode 100644 index 0000000000..2ae0cf50b0 --- /dev/null +++ b/lib/rbcodec/metadata/kss.c | |||
@@ -0,0 +1,53 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | |||
13 | static bool parse_kss_header(int fd, struct mp3entry* id3) | ||
14 | { | ||
15 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
16 | unsigned char* buf = (unsigned char *)id3->path; | ||
17 | |||
18 | lseek(fd, 0, SEEK_SET); | ||
19 | if (read(fd, buf, 0x20) < 0x20) | ||
20 | return false; | ||
21 | |||
22 | /* calculate track length with number of tracks */ | ||
23 | id3->length = 0; | ||
24 | if (buf[14] == 0x10) { | ||
25 | id3->length = (get_short_le((void *)(buf + 26)) + 1) * 1000; | ||
26 | } | ||
27 | |||
28 | if (id3->length <= 0) | ||
29 | id3->length = 255 * 1000; /* 255 tracks */ | ||
30 | |||
31 | return true; | ||
32 | } | ||
33 | |||
34 | |||
35 | bool get_kss_metadata(int fd, struct mp3entry* id3) | ||
36 | { | ||
37 | uint32_t kss_type; | ||
38 | if ((lseek(fd, 0, SEEK_SET) < 0) || | ||
39 | read_uint32be(fd, &kss_type) != (int)sizeof(kss_type)) | ||
40 | return false; | ||
41 | |||
42 | id3->vbr = false; | ||
43 | id3->filesize = filesize(fd); | ||
44 | /* we only render 16 bits, 44.1KHz, Stereo */ | ||
45 | id3->bitrate = 706; | ||
46 | id3->frequency = 44100; | ||
47 | |||
48 | /* Make sure this is an SGC file */ | ||
49 | if (kss_type != FOURCC('K','S','C','C') && kss_type != FOURCC('K','S','S','X')) | ||
50 | return false; | ||
51 | |||
52 | return parse_kss_header(fd, id3); | ||
53 | } | ||
diff --git a/lib/rbcodec/metadata/metadata.c b/lib/rbcodec/metadata/metadata.c new file mode 100644 index 0000000000..b91e00cc4e --- /dev/null +++ b/lib/rbcodec/metadata/metadata.c | |||
@@ -0,0 +1,641 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <ctype.h> | ||
24 | #include "string-extra.h" | ||
25 | |||
26 | #include "debug.h" | ||
27 | #include "logf.h" | ||
28 | #include "settings.h" | ||
29 | #include "cuesheet.h" | ||
30 | #include "metadata.h" | ||
31 | |||
32 | #include "metadata_parsers.h" | ||
33 | |||
34 | #if CONFIG_CODEC == SWCODEC | ||
35 | |||
36 | /* For trailing tag stripping and base audio data types */ | ||
37 | #include "buffering.h" | ||
38 | |||
39 | #include "metadata/metadata_common.h" | ||
40 | |||
41 | static bool get_shn_metadata(int fd, struct mp3entry *id3) | ||
42 | { | ||
43 | /* TODO: read the id3v2 header if it exists */ | ||
44 | id3->vbr = true; | ||
45 | id3->filesize = filesize(fd); | ||
46 | return skip_id3v2(fd, id3); | ||
47 | } | ||
48 | |||
49 | static bool get_other_asap_metadata(int fd, struct mp3entry *id3) | ||
50 | { | ||
51 | id3->bitrate = 706; | ||
52 | id3->frequency = 44100; | ||
53 | id3->vbr = false; | ||
54 | id3->filesize = filesize(fd); | ||
55 | id3->genre_string = id3_get_num_genre(36); | ||
56 | return true; | ||
57 | } | ||
58 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
59 | bool write_metadata_log = false; | ||
60 | |||
61 | const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = | ||
62 | { | ||
63 | /* Unknown file format */ | ||
64 | [0 ... AFMT_NUM_CODECS-1] = | ||
65 | AFMT_ENTRY("???", NULL, NULL, NULL, "\0" ), | ||
66 | |||
67 | /* MPEG Audio layer 2 */ | ||
68 | [AFMT_MPA_L2] = | ||
69 | AFMT_ENTRY("MP2", "mpa", NULL, get_mp3_metadata, "mpa\0mp2\0"), | ||
70 | |||
71 | #if CONFIG_CODEC != SWCODEC | ||
72 | /* MPEG Audio layer 3 on HWCODEC: .talk clips, no encoder */ | ||
73 | [AFMT_MPA_L3] = | ||
74 | AFMT_ENTRY("MP3", "mpa", NULL, get_mp3_metadata, "mp3\0talk\0"), | ||
75 | |||
76 | #else /* CONFIG_CODEC == SWCODEC */ | ||
77 | /* MPEG Audio layer 3 on SWCODEC */ | ||
78 | [AFMT_MPA_L3] = | ||
79 | AFMT_ENTRY("MP3", "mpa", "mp3_enc", get_mp3_metadata, "mp3\0"), | ||
80 | |||
81 | /* MPEG Audio layer 1 */ | ||
82 | [AFMT_MPA_L1] = | ||
83 | AFMT_ENTRY("MP1", "mpa", NULL, get_mp3_metadata, "mp1\0"), | ||
84 | /* Audio Interchange File Format */ | ||
85 | [AFMT_AIFF] = | ||
86 | AFMT_ENTRY("AIFF", "aiff", "aiff_enc", get_aiff_metadata, "aiff\0aif\0"), | ||
87 | /* Uncompressed PCM in a WAV file OR ATRAC3 stream in WAV file (.at3) */ | ||
88 | [AFMT_PCM_WAV] = | ||
89 | AFMT_ENTRY("WAV", "wav", "wav_enc", get_wave_metadata, "wav\0at3\0"), | ||
90 | /* Ogg Vorbis */ | ||
91 | [AFMT_OGG_VORBIS] = | ||
92 | AFMT_ENTRY("Ogg", "vorbis", NULL, get_ogg_metadata, "ogg\0oga\0"), | ||
93 | /* FLAC */ | ||
94 | [AFMT_FLAC] = | ||
95 | AFMT_ENTRY("FLAC", "flac", NULL, get_flac_metadata, "flac\0"), | ||
96 | /* Musepack SV7 */ | ||
97 | [AFMT_MPC_SV7] = | ||
98 | AFMT_ENTRY("MPCv7", "mpc", NULL, get_musepack_metadata,"mpc\0"), | ||
99 | /* A/52 (aka AC3) audio */ | ||
100 | [AFMT_A52] = | ||
101 | AFMT_ENTRY("AC3", "a52", NULL, get_a52_metadata, "a52\0ac3\0"), | ||
102 | /* WavPack */ | ||
103 | [AFMT_WAVPACK] = | ||
104 | AFMT_ENTRY("WV","wavpack","wavpack_enc",get_wavpack_metadata,"wv\0"), | ||
105 | /* Apple Lossless Audio Codec */ | ||
106 | [AFMT_MP4_ALAC] = | ||
107 | AFMT_ENTRY("ALAC", "alac", NULL, get_mp4_metadata, "m4a\0m4b\0"), | ||
108 | /* Advanced Audio Coding in M4A container */ | ||
109 | [AFMT_MP4_AAC] = | ||
110 | AFMT_ENTRY("AAC", "aac", NULL, get_mp4_metadata, "mp4\0"), | ||
111 | /* Shorten */ | ||
112 | [AFMT_SHN] = | ||
113 | AFMT_ENTRY("SHN","shorten", NULL, get_shn_metadata, "shn\0"), | ||
114 | /* SID File Format */ | ||
115 | [AFMT_SID] = | ||
116 | AFMT_ENTRY("SID", "sid", NULL, get_sid_metadata, "sid\0"), | ||
117 | /* ADX File Format */ | ||
118 | [AFMT_ADX] = | ||
119 | AFMT_ENTRY("ADX", "adx", NULL, get_adx_metadata, "adx\0"), | ||
120 | /* NESM (NES Sound Format) */ | ||
121 | [AFMT_NSF] = | ||
122 | AFMT_ENTRY("NSF", "nsf", NULL, get_nsf_metadata, "nsf\0nsfe\0"), | ||
123 | /* Speex File Format */ | ||
124 | [AFMT_SPEEX] = | ||
125 | AFMT_ENTRY("Speex", "speex",NULL, get_ogg_metadata, "spx\0"), | ||
126 | /* SPC700 Save State */ | ||
127 | [AFMT_SPC] = | ||
128 | AFMT_ENTRY("SPC", "spc", NULL, get_spc_metadata, "spc\0"), | ||
129 | /* APE (Monkey's Audio) */ | ||
130 | [AFMT_APE] = | ||
131 | AFMT_ENTRY("APE", "ape", NULL, get_monkeys_metadata,"ape\0mac\0"), | ||
132 | /* WMA (WMAV1/V2 in ASF) */ | ||
133 | [AFMT_WMA] = | ||
134 | AFMT_ENTRY("WMA", "wma", NULL, get_asf_metadata,"wma\0wmv\0asf\0"), | ||
135 | /* WMA Professional in ASF */ | ||
136 | [AFMT_WMAPRO] = | ||
137 | AFMT_ENTRY("WMAPro","wmapro",NULL, NULL, "wma\0wmv\0asf\0"), | ||
138 | /* Amiga MOD File */ | ||
139 | [AFMT_MOD] = | ||
140 | AFMT_ENTRY("MOD", "mod", NULL, get_mod_metadata, "mod\0"), | ||
141 | /* Atari SAP File */ | ||
142 | [AFMT_SAP] = | ||
143 | AFMT_ENTRY("SAP", "asap", NULL, get_asap_metadata, "sap\0"), | ||
144 | /* Cook in RM/RA */ | ||
145 | [AFMT_RM_COOK] = | ||
146 | AFMT_ENTRY("Cook", "cook", NULL, get_rm_metadata,"rm\0ra\0rmvb\0"), | ||
147 | /* AAC in RM/RA */ | ||
148 | [AFMT_RM_AAC] = | ||
149 | AFMT_ENTRY("RAAC", "raac", NULL, NULL, "rm\0ra\0rmvb\0"), | ||
150 | /* AC3 in RM/RA */ | ||
151 | [AFMT_RM_AC3] = | ||
152 | AFMT_ENTRY("AC3", "a52_rm", NULL, NULL, "rm\0ra\0rmvb\0"), | ||
153 | /* ATRAC3 in RM/RA */ | ||
154 | [AFMT_RM_ATRAC3] = | ||
155 | AFMT_ENTRY("ATRAC3","atrac3_rm",NULL, NULL, "rm\0ra\0rmvb\0"), | ||
156 | /* Atari CMC File */ | ||
157 | [AFMT_CMC] = | ||
158 | AFMT_ENTRY("CMC", "asap", NULL, get_other_asap_metadata,"cmc\0"), | ||
159 | /* Atari CM3 File */ | ||
160 | [AFMT_CM3] = | ||
161 | AFMT_ENTRY("CM3", "asap", NULL, get_other_asap_metadata,"cm3\0"), | ||
162 | /* Atari CMR File */ | ||
163 | [AFMT_CMR] = | ||
164 | AFMT_ENTRY("CMR", "asap", NULL, get_other_asap_metadata,"cmr\0"), | ||
165 | /* Atari CMS File */ | ||
166 | [AFMT_CMS] = | ||
167 | AFMT_ENTRY("CMS", "asap", NULL, get_other_asap_metadata,"cms\0"), | ||
168 | /* Atari DMC File */ | ||
169 | [AFMT_DMC] = | ||
170 | AFMT_ENTRY("DMC", "asap", NULL, get_other_asap_metadata,"dmc\0"), | ||
171 | /* Atari DLT File */ | ||
172 | [AFMT_DLT] = | ||
173 | AFMT_ENTRY("DLT", "asap", NULL, get_other_asap_metadata,"dlt\0"), | ||
174 | /* Atari MPT File */ | ||
175 | [AFMT_MPT] = | ||
176 | AFMT_ENTRY("MPT", "asap", NULL, get_other_asap_metadata,"mpt\0"), | ||
177 | /* Atari MPD File */ | ||
178 | [AFMT_MPD] = | ||
179 | AFMT_ENTRY("MPD", "asap", NULL, get_other_asap_metadata,"mpd\0"), | ||
180 | /* Atari RMT File */ | ||
181 | [AFMT_RMT] = | ||
182 | AFMT_ENTRY("RMT", "asap", NULL, get_other_asap_metadata,"rmt\0"), | ||
183 | /* Atari TMC File */ | ||
184 | [AFMT_TMC] = | ||
185 | AFMT_ENTRY("TMC", "asap", NULL, get_other_asap_metadata,"tmc\0"), | ||
186 | /* Atari TM8 File */ | ||
187 | [AFMT_TM8] = | ||
188 | AFMT_ENTRY("TM8", "asap", NULL, get_other_asap_metadata,"tm8\0"), | ||
189 | /* Atari TM2 File */ | ||
190 | [AFMT_TM2] = | ||
191 | AFMT_ENTRY("TM2", "asap", NULL, get_other_asap_metadata,"tm2\0"), | ||
192 | /* Atrac3 in Sony OMA Container */ | ||
193 | [AFMT_OMA_ATRAC3] = | ||
194 | AFMT_ENTRY("ATRAC3","atrac3_oma",NULL, get_oma_metadata, "oma\0aa3\0"), | ||
195 | /* SMAF (Synthetic music Mobile Application Format) */ | ||
196 | [AFMT_SMAF] = | ||
197 | AFMT_ENTRY("SMAF", "smaf", NULL, get_smaf_metadata, "mmf\0"), | ||
198 | /* Sun Audio file */ | ||
199 | [AFMT_AU] = | ||
200 | AFMT_ENTRY("AU", "au", NULL, get_au_metadata, "au\0snd\0"), | ||
201 | /* VOX (Dialogic telephony file formats) */ | ||
202 | [AFMT_VOX] = | ||
203 | AFMT_ENTRY("VOX", "vox", NULL, get_vox_metadata, "vox\0"), | ||
204 | /* Wave64 */ | ||
205 | [AFMT_WAVE64] = | ||
206 | AFMT_ENTRY("WAVE64","wav64",NULL, get_wave64_metadata,"w64\0"), | ||
207 | /* True Audio */ | ||
208 | [AFMT_TTA] = | ||
209 | AFMT_ENTRY("TTA", "tta", NULL, get_tta_metadata, "tta\0"), | ||
210 | /* WMA Voice in ASF */ | ||
211 | [AFMT_WMAVOICE] = | ||
212 | AFMT_ENTRY("WMAVoice","wmavoice",NULL, NULL, "wma\0wmv\0"), | ||
213 | /* Musepack SV8 */ | ||
214 | [AFMT_MPC_SV8] = | ||
215 | AFMT_ENTRY("MPCv8", "mpc", NULL, get_musepack_metadata,"mpc\0"), | ||
216 | /* Advanced Audio Coding High Efficiency in M4A container */ | ||
217 | [AFMT_MP4_AAC_HE] = | ||
218 | AFMT_ENTRY("AAC-HE","aac", NULL, get_mp4_metadata, "mp4\0"), | ||
219 | /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ | ||
220 | [AFMT_AY] = | ||
221 | AFMT_ENTRY("AY", "ay", NULL, get_ay_metadata, "ay\0"), | ||
222 | /* GBS (Game Boy Sound Format) */ | ||
223 | [AFMT_GBS] = | ||
224 | AFMT_ENTRY("GBS", "gbs", NULL, get_gbs_metadata, "gbs\0"), | ||
225 | /* HES (Hudson Entertainment System Sound Format) */ | ||
226 | [AFMT_HES] = | ||
227 | AFMT_ENTRY("HES", "hes", NULL, get_hes_metadata, "hes\0"), | ||
228 | /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */ | ||
229 | [AFMT_SGC] = | ||
230 | AFMT_ENTRY("SGC", "sgc", NULL, get_sgc_metadata, "sgc\0"), | ||
231 | /* VGM (Video Game Music Format) */ | ||
232 | [AFMT_VGM] = | ||
233 | AFMT_ENTRY("VGM", "vgm", NULL, get_vgm_metadata, "vgm\0vgz\0"), | ||
234 | /* KSS (MSX computer KSS Music File) */ | ||
235 | [AFMT_KSS] = | ||
236 | AFMT_ENTRY("KSS", "kss", NULL, get_kss_metadata, "kss\0"), | ||
237 | #endif | ||
238 | }; | ||
239 | |||
240 | #if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) | ||
241 | /* get REC_FORMAT_* corresponding AFMT_* */ | ||
242 | const int rec_format_afmt[REC_NUM_FORMATS] = | ||
243 | { | ||
244 | /* give AFMT_UNKNOWN by default */ | ||
245 | [0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN, | ||
246 | /* add new entries below this line */ | ||
247 | [REC_FORMAT_AIFF] = AFMT_AIFF, | ||
248 | [REC_FORMAT_MPA_L3] = AFMT_MPA_L3, | ||
249 | [REC_FORMAT_WAVPACK] = AFMT_WAVPACK, | ||
250 | [REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV, | ||
251 | }; | ||
252 | |||
253 | #if 0 /* Currently unused, left for reference and future use */ | ||
254 | /* get AFMT_* corresponding REC_FORMAT_* */ | ||
255 | const int afmt_rec_format[AFMT_NUM_CODECS] = | ||
256 | { | ||
257 | /* give -1 by default */ | ||
258 | [0 ... AFMT_NUM_CODECS-1] = -1, | ||
259 | /* add new entries below this line */ | ||
260 | [AFMT_AIFF] = REC_FORMAT_AIFF, | ||
261 | [AFMT_MPA_L3] = REC_FORMAT_MPA_L3, | ||
262 | [AFMT_WAVPACK] = REC_FORMAT_WAVPACK, | ||
263 | [AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV, | ||
264 | }; | ||
265 | #endif | ||
266 | #endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ | ||
267 | |||
268 | #if CONFIG_CODEC == SWCODEC | ||
269 | /* Get the canonical AFMT type */ | ||
270 | int get_audio_base_codec_type(int type) | ||
271 | { | ||
272 | int base_type = type; | ||
273 | switch (type) { | ||
274 | case AFMT_MPA_L1: | ||
275 | case AFMT_MPA_L2: | ||
276 | case AFMT_MPA_L3: | ||
277 | base_type = AFMT_MPA_L3; | ||
278 | break; | ||
279 | case AFMT_MPC_SV7: | ||
280 | case AFMT_MPC_SV8: | ||
281 | base_type = AFMT_MPC_SV7; | ||
282 | break; | ||
283 | case AFMT_MP4_AAC: | ||
284 | case AFMT_MP4_AAC_HE: | ||
285 | base_type = AFMT_MP4_AAC; | ||
286 | break; | ||
287 | case AFMT_SAP: | ||
288 | case AFMT_CMC: | ||
289 | case AFMT_CM3: | ||
290 | case AFMT_CMR: | ||
291 | case AFMT_CMS: | ||
292 | case AFMT_DMC: | ||
293 | case AFMT_DLT: | ||
294 | case AFMT_MPT: | ||
295 | case AFMT_MPD: | ||
296 | case AFMT_RMT: | ||
297 | case AFMT_TMC: | ||
298 | case AFMT_TM8: | ||
299 | case AFMT_TM2: | ||
300 | base_type = AFMT_SAP; | ||
301 | break; | ||
302 | default: | ||
303 | break; | ||
304 | } | ||
305 | |||
306 | return base_type; | ||
307 | } | ||
308 | |||
309 | /* Get the basic audio type */ | ||
310 | enum data_type get_audio_base_data_type(int afmt) | ||
311 | { | ||
312 | if ((unsigned)afmt >= AFMT_NUM_CODECS) | ||
313 | return TYPE_UNKNOWN; | ||
314 | |||
315 | switch (get_audio_base_codec_type(afmt)) | ||
316 | { | ||
317 | case AFMT_NSF: | ||
318 | case AFMT_SPC: | ||
319 | case AFMT_SID: | ||
320 | case AFMT_MOD: | ||
321 | case AFMT_SAP: | ||
322 | case AFMT_AY: | ||
323 | case AFMT_GBS: | ||
324 | case AFMT_HES: | ||
325 | case AFMT_SGC: | ||
326 | case AFMT_VGM: | ||
327 | case AFMT_KSS: | ||
328 | /* Type must be allocated and loaded in its entirety onto | ||
329 | the buffer */ | ||
330 | return TYPE_ATOMIC_AUDIO; | ||
331 | |||
332 | default: | ||
333 | /* Assume type may be loaded and discarded incrementally */ | ||
334 | return TYPE_PACKET_AUDIO; | ||
335 | |||
336 | case AFMT_UNKNOWN: | ||
337 | /* Have no idea at all */ | ||
338 | return TYPE_UNKNOWN; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /* Is the format allowed to buffer starting at some offset other than 0 | ||
343 | or first frame only for resume purposes? */ | ||
344 | bool format_buffers_with_offset(int afmt) | ||
345 | { | ||
346 | switch (afmt) | ||
347 | { | ||
348 | case AFMT_MPA_L1: | ||
349 | case AFMT_MPA_L2: | ||
350 | case AFMT_MPA_L3: | ||
351 | case AFMT_WAVPACK: | ||
352 | /* Format may be loaded at the first needed frame */ | ||
353 | return true; | ||
354 | default: | ||
355 | /* Format must be loaded from the beginning of the file | ||
356 | (does not imply 'atomic', while 'atomic' implies 'no offset') */ | ||
357 | return false; | ||
358 | } | ||
359 | } | ||
360 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
361 | |||
362 | |||
363 | /* Simple file type probing by looking at the filename extension. */ | ||
364 | unsigned int probe_file_format(const char *filename) | ||
365 | { | ||
366 | char *suffix; | ||
367 | unsigned int i; | ||
368 | |||
369 | suffix = strrchr(filename, '.'); | ||
370 | |||
371 | if (suffix == NULL) | ||
372 | { | ||
373 | return AFMT_UNKNOWN; | ||
374 | } | ||
375 | |||
376 | /* skip '.' */ | ||
377 | suffix++; | ||
378 | |||
379 | for (i = 1; i < AFMT_NUM_CODECS; i++) | ||
380 | { | ||
381 | /* search extension list for type */ | ||
382 | const char *ext = audio_formats[i].ext_list; | ||
383 | |||
384 | do | ||
385 | { | ||
386 | if (strcasecmp(suffix, ext) == 0) | ||
387 | { | ||
388 | return i; | ||
389 | } | ||
390 | |||
391 | ext += strlen(ext) + 1; | ||
392 | } | ||
393 | while (*ext != '\0'); | ||
394 | } | ||
395 | |||
396 | return AFMT_UNKNOWN; | ||
397 | } | ||
398 | |||
399 | /* Note, that this returns false for successful, true for error! */ | ||
400 | bool mp3info(struct mp3entry *entry, const char *filename) | ||
401 | { | ||
402 | int fd; | ||
403 | bool result; | ||
404 | |||
405 | fd = open(filename, O_RDONLY); | ||
406 | if (fd < 0) | ||
407 | return true; | ||
408 | |||
409 | result = !get_metadata(entry, fd, filename); | ||
410 | |||
411 | close(fd); | ||
412 | |||
413 | return result; | ||
414 | } | ||
415 | |||
416 | /* Get metadata for track - return false if parsing showed problems with the | ||
417 | * file that would prevent playback. | ||
418 | */ | ||
419 | bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) | ||
420 | { | ||
421 | const struct afmt_entry *entry; | ||
422 | int logfd = 0; | ||
423 | DEBUGF("Read metadata for %s\n", trackname); | ||
424 | if (write_metadata_log) | ||
425 | { | ||
426 | logfd = open("/metadata.log", O_WRONLY | O_APPEND | O_CREAT, 0666); | ||
427 | if (logfd >= 0) | ||
428 | { | ||
429 | write(logfd, trackname, strlen(trackname)); | ||
430 | write(logfd, "\n", 1); | ||
431 | close(logfd); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | /* Clear the mp3entry to avoid having bogus pointers appear */ | ||
436 | wipe_mp3entry(id3); | ||
437 | |||
438 | /* Take our best guess at the codec type based on file extension */ | ||
439 | id3->codectype = probe_file_format(trackname); | ||
440 | |||
441 | /* default values for embedded cuesheets */ | ||
442 | id3->has_embedded_cuesheet = false; | ||
443 | id3->embedded_cuesheet.pos = 0; | ||
444 | |||
445 | entry = &audio_formats[id3->codectype]; | ||
446 | |||
447 | /* Load codec specific track tag information and confirm the codec type. */ | ||
448 | if (!entry->parse_func) | ||
449 | { | ||
450 | DEBUGF("nothing to parse for %s (format %s)", trackname, entry->label); | ||
451 | return false; | ||
452 | } | ||
453 | |||
454 | if (!entry->parse_func(fd, id3)) | ||
455 | { | ||
456 | DEBUGF("parsing %s failed (format: %s)", trackname, entry->label); | ||
457 | return false; | ||
458 | } | ||
459 | |||
460 | lseek(fd, 0, SEEK_SET); | ||
461 | strlcpy(id3->path, trackname, sizeof(id3->path)); | ||
462 | /* We have successfully read the metadata from the file */ | ||
463 | return true; | ||
464 | } | ||
465 | |||
466 | #ifndef __PCTOOL__ | ||
467 | #if CONFIG_CODEC == SWCODEC | ||
468 | void strip_tags(int handle_id) | ||
469 | { | ||
470 | static const unsigned char tag[] = "TAG"; | ||
471 | static const unsigned char apetag[] = "APETAGEX"; | ||
472 | size_t len, version; | ||
473 | void *tail; | ||
474 | |||
475 | if (bufgettail(handle_id, 128, &tail) != 128) | ||
476 | return; | ||
477 | |||
478 | if (memcmp(tail, tag, 3) == 0) | ||
479 | { | ||
480 | /* Skip id3v1 tag */ | ||
481 | logf("Cutting off ID3v1 tag"); | ||
482 | bufcuttail(handle_id, 128); | ||
483 | } | ||
484 | |||
485 | /* Get a new tail, as the old one may have been cut */ | ||
486 | if (bufgettail(handle_id, 32, &tail) != 32) | ||
487 | return; | ||
488 | |||
489 | /* Check for APE tag (look for the APE tag footer) */ | ||
490 | if (memcmp(tail, apetag, 8) != 0) | ||
491 | return; | ||
492 | |||
493 | /* Read the version and length from the footer */ | ||
494 | version = get_long_le(&((unsigned char *)tail)[8]); | ||
495 | len = get_long_le(&((unsigned char *)tail)[12]); | ||
496 | if (version == 2000) | ||
497 | len += 32; /* APEv2 has a 32 byte header */ | ||
498 | |||
499 | /* Skip APE tag */ | ||
500 | logf("Cutting off APE tag (%ldB)", len); | ||
501 | bufcuttail(handle_id, len); | ||
502 | } | ||
503 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
504 | #endif /* ! __PCTOOL__ */ | ||
505 | |||
506 | #define MOVE_ENTRY(x) if (x) x += offset; | ||
507 | |||
508 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig) | ||
509 | { | ||
510 | long offset; | ||
511 | if (orig > dest) | ||
512 | offset = -((size_t)orig - (size_t)dest); | ||
513 | else | ||
514 | offset = ((size_t)dest - (size_t)orig); | ||
515 | |||
516 | MOVE_ENTRY(entry->title) | ||
517 | MOVE_ENTRY(entry->artist) | ||
518 | MOVE_ENTRY(entry->album) | ||
519 | |||
520 | if (entry->genre_string > (char*)orig && | ||
521 | entry->genre_string < (char*)orig + sizeof(struct mp3entry)) | ||
522 | /* Don't adjust that if it points to an entry of the "genres" array */ | ||
523 | entry->genre_string += offset; | ||
524 | |||
525 | MOVE_ENTRY(entry->track_string) | ||
526 | MOVE_ENTRY(entry->disc_string) | ||
527 | MOVE_ENTRY(entry->year_string) | ||
528 | MOVE_ENTRY(entry->composer) | ||
529 | MOVE_ENTRY(entry->comment) | ||
530 | MOVE_ENTRY(entry->albumartist) | ||
531 | MOVE_ENTRY(entry->grouping) | ||
532 | MOVE_ENTRY(entry->mb_track_id) | ||
533 | } | ||
534 | |||
535 | void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig) | ||
536 | { | ||
537 | memcpy(dest, orig, sizeof(struct mp3entry)); | ||
538 | adjust_mp3entry(dest, dest, orig); | ||
539 | } | ||
540 | |||
541 | /* A shortcut to simplify the common task of clearing the struct */ | ||
542 | void wipe_mp3entry(struct mp3entry *id3) | ||
543 | { | ||
544 | memset(id3, 0, sizeof (struct mp3entry)); | ||
545 | } | ||
546 | |||
547 | #if CONFIG_CODEC == SWCODEC | ||
548 | /* Glean what is possible from the filename alone - does not parse metadata */ | ||
549 | void fill_metadata_from_path(struct mp3entry *id3, const char *trackname) | ||
550 | { | ||
551 | char *p; | ||
552 | |||
553 | /* Clear the mp3entry to avoid having bogus pointers appear */ | ||
554 | wipe_mp3entry(id3); | ||
555 | |||
556 | /* Find the filename portion of the path */ | ||
557 | p = strrchr(trackname, '/'); | ||
558 | strlcpy(id3->id3v2buf, p ? ++p : id3->path, ID3V2_BUF_SIZE); | ||
559 | |||
560 | /* Get the format from the extension and trim it off */ | ||
561 | p = strrchr(id3->id3v2buf, '.'); | ||
562 | if (p) | ||
563 | { | ||
564 | /* Might be wrong for container formats - should we bother? */ | ||
565 | id3->codectype = probe_file_format(p); | ||
566 | |||
567 | if (id3->codectype != AFMT_UNKNOWN) | ||
568 | *p = '\0'; | ||
569 | } | ||
570 | |||
571 | /* Set the filename as the title */ | ||
572 | id3->title = id3->id3v2buf; | ||
573 | |||
574 | /* Copy the path info */ | ||
575 | strlcpy(id3->path, trackname, sizeof (id3->path)); | ||
576 | } | ||
577 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
578 | |||
579 | #ifndef __PCTOOL__ | ||
580 | #ifdef HAVE_TAGCACHE | ||
581 | #if CONFIG_CODEC == SWCODEC | ||
582 | |||
583 | enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE }; | ||
584 | |||
585 | bool autoresumable(struct mp3entry *id3) | ||
586 | { | ||
587 | char *endp, *path; | ||
588 | size_t len; | ||
589 | bool is_resumable; | ||
590 | |||
591 | if (id3->autoresumable) /* result cached? */ | ||
592 | return id3->autoresumable == AUTORESUMABLE_TRUE; | ||
593 | |||
594 | is_resumable = false; | ||
595 | |||
596 | if (id3->path) | ||
597 | { | ||
598 | for (path = global_settings.autoresume_paths; | ||
599 | *path; /* search terms left? */ | ||
600 | path++) | ||
601 | { | ||
602 | if (*path == ':') /* Skip empty search patterns */ | ||
603 | continue; | ||
604 | |||
605 | /* FIXME: As soon as strcspn or strchrnul are made available in | ||
606 | the core, the following can be made more efficient. */ | ||
607 | endp = strchr(path, ':'); | ||
608 | if (endp) | ||
609 | len = endp - path; | ||
610 | else | ||
611 | len = strlen(path); | ||
612 | |||
613 | /* Note: At this point, len is always > 0 */ | ||
614 | |||
615 | if (strncasecmp(id3->path, path, len) == 0) | ||
616 | { | ||
617 | /* Full directory-name matches only. Trailing '/' in | ||
618 | search path OK. */ | ||
619 | if (id3->path[len] == '/' || id3->path[len - 1] == '/') | ||
620 | { | ||
621 | is_resumable = true; | ||
622 | break; | ||
623 | } | ||
624 | } | ||
625 | path += len - 1; | ||
626 | } | ||
627 | } | ||
628 | |||
629 | /* cache result */ | ||
630 | id3->autoresumable = | ||
631 | is_resumable ? AUTORESUMABLE_TRUE : AUTORESUMABLE_FALSE; | ||
632 | |||
633 | logf("autoresumable: %s is%s resumable", | ||
634 | id3->path, is_resumable ? "" : " not"); | ||
635 | |||
636 | return is_resumable; | ||
637 | } | ||
638 | |||
639 | #endif /* SWCODEC */ | ||
640 | #endif /* HAVE_TAGCACHE */ | ||
641 | #endif /* __PCTOOL__ */ | ||
diff --git a/lib/rbcodec/metadata/metadata.h b/lib/rbcodec/metadata/metadata.h new file mode 100644 index 0000000000..55e4d76f25 --- /dev/null +++ b/lib/rbcodec/metadata/metadata.h | |||
@@ -0,0 +1,353 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _METADATA_H | ||
23 | #define _METADATA_H | ||
24 | |||
25 | #include <stdbool.h> | ||
26 | #include "config.h" | ||
27 | #include "file.h" | ||
28 | |||
29 | |||
30 | /* Audio file types. */ | ||
31 | /* NOTE: The values of the AFMT_* items are used for the %fc tag in the WPS | ||
32 | - so new entries MUST be added to the end to maintain compatibility. | ||
33 | */ | ||
34 | enum | ||
35 | { | ||
36 | AFMT_UNKNOWN = 0, /* Unknown file format */ | ||
37 | |||
38 | /* start formats */ | ||
39 | AFMT_MPA_L1, /* MPEG Audio layer 1 */ | ||
40 | AFMT_MPA_L2, /* MPEG Audio layer 2 */ | ||
41 | AFMT_MPA_L3, /* MPEG Audio layer 3 */ | ||
42 | |||
43 | #if CONFIG_CODEC == SWCODEC | ||
44 | AFMT_AIFF, /* Audio Interchange File Format */ | ||
45 | AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */ | ||
46 | AFMT_OGG_VORBIS, /* Ogg Vorbis */ | ||
47 | AFMT_FLAC, /* FLAC */ | ||
48 | AFMT_MPC_SV7, /* Musepack SV7 */ | ||
49 | AFMT_A52, /* A/52 (aka AC3) audio */ | ||
50 | AFMT_WAVPACK, /* WavPack */ | ||
51 | AFMT_MP4_ALAC, /* Apple Lossless Audio Codec */ | ||
52 | AFMT_MP4_AAC, /* Advanced Audio Coding (AAC) in M4A container */ | ||
53 | AFMT_SHN, /* Shorten */ | ||
54 | AFMT_SID, /* SID File Format */ | ||
55 | AFMT_ADX, /* ADX File Format */ | ||
56 | AFMT_NSF, /* NESM (NES Sound Format) */ | ||
57 | AFMT_SPEEX, /* Ogg Speex speech */ | ||
58 | AFMT_SPC, /* SPC700 save state */ | ||
59 | AFMT_APE, /* Monkey's Audio (APE) */ | ||
60 | AFMT_WMA, /* WMAV1/V2 in ASF */ | ||
61 | AFMT_WMAPRO, /* WMA Professional in ASF */ | ||
62 | AFMT_MOD, /* Amiga MOD File Format */ | ||
63 | AFMT_SAP, /* Atari 8Bit SAP Format */ | ||
64 | AFMT_RM_COOK, /* Cook in RM/RA */ | ||
65 | AFMT_RM_AAC, /* AAC in RM/RA */ | ||
66 | AFMT_RM_AC3, /* AC3 in RM/RA */ | ||
67 | AFMT_RM_ATRAC3, /* ATRAC3 in RM/RA */ | ||
68 | AFMT_CMC, /* Atari 8bit cmc format */ | ||
69 | AFMT_CM3, /* Atari 8bit cm3 format */ | ||
70 | AFMT_CMR, /* Atari 8bit cmr format */ | ||
71 | AFMT_CMS, /* Atari 8bit cms format */ | ||
72 | AFMT_DMC, /* Atari 8bit dmc format */ | ||
73 | AFMT_DLT, /* Atari 8bit dlt format */ | ||
74 | AFMT_MPT, /* Atari 8bit mpt format */ | ||
75 | AFMT_MPD, /* Atari 8bit mpd format */ | ||
76 | AFMT_RMT, /* Atari 8bit rmt format */ | ||
77 | AFMT_TMC, /* Atari 8bit tmc format */ | ||
78 | AFMT_TM8, /* Atari 8bit tm8 format */ | ||
79 | AFMT_TM2, /* Atari 8bit tm2 format */ | ||
80 | AFMT_OMA_ATRAC3, /* Atrac3 in Sony OMA container */ | ||
81 | AFMT_SMAF, /* SMAF */ | ||
82 | AFMT_AU, /* Sun Audio file */ | ||
83 | AFMT_VOX, /* VOX */ | ||
84 | AFMT_WAVE64, /* Wave64 */ | ||
85 | AFMT_TTA, /* True Audio */ | ||
86 | AFMT_WMAVOICE, /* WMA Voice in ASF */ | ||
87 | AFMT_MPC_SV8, /* Musepack SV8 */ | ||
88 | AFMT_MP4_AAC_HE, /* Advanced Audio Coding (AAC-HE) in M4A container */ | ||
89 | AFMT_AY, /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ | ||
90 | AFMT_GBS, /* GBS (Game Boy Sound Format) */ | ||
91 | AFMT_HES, /* HES (Hudson Entertainment System Sound Format) */ | ||
92 | AFMT_SGC, /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */ | ||
93 | AFMT_VGM, /* VGM (Video Game Music Format) */ | ||
94 | AFMT_KSS, /* KSS (MSX computer KSS Music File) */ | ||
95 | #endif | ||
96 | |||
97 | /* add new formats at any index above this line to have a sensible order - | ||
98 | specified array index inits are used */ | ||
99 | /* format arrays defined in id3.c */ | ||
100 | |||
101 | AFMT_NUM_CODECS, | ||
102 | |||
103 | #if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) | ||
104 | /* masks to decompose parts */ | ||
105 | CODEC_AFMT_MASK = 0x0fff, | ||
106 | CODEC_TYPE_MASK = 0x7000, | ||
107 | |||
108 | /* switch for specifying codec type when requesting a filename */ | ||
109 | CODEC_TYPE_DECODER = (0 << 12), /* default */ | ||
110 | CODEC_TYPE_ENCODER = (1 << 12), | ||
111 | #endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */ | ||
112 | }; | ||
113 | |||
114 | #if CONFIG_CODEC == SWCODEC | ||
115 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
116 | #define CODEC_EXTENSION "so" | ||
117 | #define CODEC_PREFIX "lib" | ||
118 | #else | ||
119 | #define CODEC_EXTENSION "codec" | ||
120 | #define CODEC_PREFIX "" | ||
121 | #endif | ||
122 | |||
123 | #ifdef HAVE_RECORDING | ||
124 | enum rec_format_indexes | ||
125 | { | ||
126 | __REC_FORMAT_START_INDEX = -1, | ||
127 | |||
128 | /* start formats */ | ||
129 | |||
130 | REC_FORMAT_PCM_WAV, | ||
131 | REC_FORMAT_AIFF, | ||
132 | REC_FORMAT_WAVPACK, | ||
133 | REC_FORMAT_MPA_L3, | ||
134 | |||
135 | /* add new formats at any index above this line to have a sensible order - | ||
136 | specified array index inits are used | ||
137 | REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range | ||
138 | REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes | ||
139 | */ | ||
140 | |||
141 | REC_NUM_FORMATS, | ||
142 | |||
143 | REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV, | ||
144 | REC_FORMAT_CFG_NUM_BITS = 2 | ||
145 | }; | ||
146 | |||
147 | #define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3" | ||
148 | |||
149 | /* get REC_FORMAT_* corresponding AFMT_* */ | ||
150 | extern const int rec_format_afmt[REC_NUM_FORMATS]; | ||
151 | /* get AFMT_* corresponding REC_FORMAT_* */ | ||
152 | /* unused: extern const int afmt_rec_format[AFMT_NUM_CODECS]; */ | ||
153 | |||
154 | #define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \ | ||
155 | { label, root_fname, enc_root_fname, func, ext_list } | ||
156 | #else /* !HAVE_RECORDING */ | ||
157 | #define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \ | ||
158 | { label, root_fname, func, ext_list } | ||
159 | #endif /* HAVE_RECORDING */ | ||
160 | |||
161 | #else /* !SWCODEC */ | ||
162 | |||
163 | #define AFMT_ENTRY(label, root_fname, enc_root_fname, func, ext_list) \ | ||
164 | { label, func, ext_list } | ||
165 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
166 | |||
167 | /** Database of audio formats **/ | ||
168 | /* record describing the audio format */ | ||
169 | struct mp3entry; | ||
170 | struct afmt_entry | ||
171 | { | ||
172 | const char *label; /* format label */ | ||
173 | #if CONFIG_CODEC == SWCODEC | ||
174 | const char *codec_root_fn; /* root codec filename (sans _enc and .codec) */ | ||
175 | #ifdef HAVE_RECORDING | ||
176 | const char *codec_enc_root_fn; /* filename of encoder codec */ | ||
177 | #endif | ||
178 | #endif | ||
179 | bool (*parse_func)(int fd, struct mp3entry *id3); /* return true on success */ | ||
180 | const char *ext_list; /* NULL terminated extension | ||
181 | list for type with the first as | ||
182 | the default for recording */ | ||
183 | }; | ||
184 | |||
185 | /* database of labels and codecs. add formats per above enum */ | ||
186 | extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS]; | ||
187 | |||
188 | #if MEMORYSIZE > 2 | ||
189 | #define ID3V2_BUF_SIZE 900 | ||
190 | #define ID3V2_MAX_ITEM_SIZE 240 | ||
191 | #else | ||
192 | #define ID3V2_BUF_SIZE 300 | ||
193 | #define ID3V2_MAX_ITEM_SIZE 90 | ||
194 | #endif | ||
195 | |||
196 | enum { | ||
197 | ID3_VER_1_0 = 1, | ||
198 | ID3_VER_1_1, | ||
199 | ID3_VER_2_2, | ||
200 | ID3_VER_2_3, | ||
201 | ID3_VER_2_4 | ||
202 | }; | ||
203 | |||
204 | #ifdef HAVE_ALBUMART | ||
205 | enum mp3_aa_type { | ||
206 | AA_TYPE_UNSYNC = -1, | ||
207 | AA_TYPE_UNKNOWN, | ||
208 | AA_TYPE_BMP, | ||
209 | AA_TYPE_PNG, | ||
210 | AA_TYPE_JPG, | ||
211 | }; | ||
212 | |||
213 | struct mp3_albumart { | ||
214 | enum mp3_aa_type type; | ||
215 | int size; | ||
216 | off_t pos; | ||
217 | }; | ||
218 | #endif | ||
219 | |||
220 | enum character_encoding { | ||
221 | CHAR_ENC_ISO_8859_1 = 1, | ||
222 | CHAR_ENC_UTF_8, | ||
223 | CHAR_ENC_UTF_16_LE, | ||
224 | CHAR_ENC_UTF_16_BE, | ||
225 | }; | ||
226 | |||
227 | /* cache embedded cuesheet details */ | ||
228 | struct embedded_cuesheet { | ||
229 | int size; | ||
230 | off_t pos; | ||
231 | enum character_encoding encoding; | ||
232 | }; | ||
233 | |||
234 | struct mp3entry { | ||
235 | char path[MAX_PATH]; | ||
236 | char* title; | ||
237 | char* artist; | ||
238 | char* album; | ||
239 | char* genre_string; | ||
240 | char* disc_string; | ||
241 | char* track_string; | ||
242 | char* year_string; | ||
243 | char* composer; | ||
244 | char* comment; | ||
245 | char* albumartist; | ||
246 | char* grouping; | ||
247 | int discnum; | ||
248 | int tracknum; | ||
249 | int layer; | ||
250 | int year; | ||
251 | unsigned char id3version; | ||
252 | unsigned int codectype; | ||
253 | unsigned int bitrate; | ||
254 | unsigned long frequency; | ||
255 | unsigned long id3v2len; | ||
256 | unsigned long id3v1len; | ||
257 | unsigned long first_frame_offset; /* Byte offset to first real MP3 frame. | ||
258 | Used for skipping leading garbage to | ||
259 | avoid gaps between tracks. */ | ||
260 | unsigned long filesize; /* without headers; in bytes */ | ||
261 | unsigned long length; /* song length in ms */ | ||
262 | unsigned long elapsed; /* ms played */ | ||
263 | |||
264 | int lead_trim; /* Number of samples to skip at the beginning */ | ||
265 | int tail_trim; /* Number of samples to remove from the end */ | ||
266 | |||
267 | /* Added for Vorbis, used by mp4 parser as well. */ | ||
268 | unsigned long samples; /* number of samples in track */ | ||
269 | |||
270 | /* MP3 stream specific info */ | ||
271 | unsigned long frame_count; /* number of frames in the file (if VBR) */ | ||
272 | |||
273 | /* Used for A52/AC3 */ | ||
274 | unsigned long bytesperframe; /* number of bytes per frame (if CBR) */ | ||
275 | |||
276 | /* Xing VBR fields */ | ||
277 | bool vbr; | ||
278 | bool has_toc; /* True if there is a VBR header in the file */ | ||
279 | unsigned char toc[100]; /* table of contents */ | ||
280 | |||
281 | /* Added for ATRAC3 */ | ||
282 | unsigned int channels; /* Number of channels in the stream */ | ||
283 | unsigned int extradata_size; /* Size (in bytes) of the codec's extradata from the container */ | ||
284 | |||
285 | /* Added for AAC HE SBR */ | ||
286 | bool needs_upsampling_correction; /* flag used by aac codec */ | ||
287 | |||
288 | /* these following two fields are used for local buffering */ | ||
289 | char id3v2buf[ID3V2_BUF_SIZE]; | ||
290 | char id3v1buf[4][92]; | ||
291 | |||
292 | /* resume related */ | ||
293 | unsigned long offset; /* bytes played */ | ||
294 | int index; /* playlist index */ | ||
295 | |||
296 | #ifdef HAVE_TAGCACHE | ||
297 | unsigned char autoresumable; /* caches result of autoresumable() */ | ||
298 | |||
299 | /* runtime database fields */ | ||
300 | long tagcache_idx; /* 0=invalid, otherwise idx+1 */ | ||
301 | int rating; | ||
302 | int score; | ||
303 | long playcount; | ||
304 | long lastplayed; | ||
305 | long playtime; | ||
306 | #endif | ||
307 | |||
308 | /* replaygain support */ | ||
309 | #if CONFIG_CODEC == SWCODEC | ||
310 | long track_level; /* holds the level in dB * (1<<FP_BITS) */ | ||
311 | long album_level; | ||
312 | long track_gain; /* s19.12 signed fixed point. 0 for no gain. */ | ||
313 | long album_gain; | ||
314 | long track_peak; /* s19.12 signed fixed point. 0 for no peak. */ | ||
315 | long album_peak; | ||
316 | #endif | ||
317 | |||
318 | #ifdef HAVE_ALBUMART | ||
319 | bool has_embedded_albumart; | ||
320 | struct mp3_albumart albumart; | ||
321 | #endif | ||
322 | |||
323 | /* Cuesheet support */ | ||
324 | bool has_embedded_cuesheet; | ||
325 | struct embedded_cuesheet embedded_cuesheet; | ||
326 | struct cuesheet *cuesheet; | ||
327 | |||
328 | /* Musicbrainz Track ID */ | ||
329 | char* mb_track_id; | ||
330 | }; | ||
331 | |||
332 | unsigned int probe_file_format(const char *filename); | ||
333 | bool get_metadata(struct mp3entry* id3, int fd, const char* trackname); | ||
334 | bool mp3info(struct mp3entry *entry, const char *filename); | ||
335 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); | ||
336 | void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig); | ||
337 | void wipe_mp3entry(struct mp3entry *id3); | ||
338 | |||
339 | #if CONFIG_CODEC == SWCODEC | ||
340 | void fill_metadata_from_path(struct mp3entry *id3, const char *trackname); | ||
341 | int get_audio_base_codec_type(int type); | ||
342 | void strip_tags(int handle_id); | ||
343 | enum data_type get_audio_base_data_type(int afmt); | ||
344 | bool format_buffers_with_offset(int afmt); | ||
345 | #endif | ||
346 | |||
347 | #ifdef HAVE_TAGCACHE | ||
348 | bool autoresumable(struct mp3entry *id3); | ||
349 | #endif | ||
350 | |||
351 | #endif | ||
352 | |||
353 | |||
diff --git a/lib/rbcodec/metadata/metadata_common.c b/lib/rbcodec/metadata/metadata_common.c new file mode 100644 index 0000000000..e861644025 --- /dev/null +++ b/lib/rbcodec/metadata/metadata_common.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include "string-extra.h" | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "replaygain.h" | ||
32 | |||
33 | /* Read a string from the file. Read up to size bytes, or, if eos != -1, | ||
34 | * until the eos character is found (eos is not stored in buf, unless it is | ||
35 | * nil). Writes up to buf_size chars to buf, always terminating with a nil. | ||
36 | * Returns number of chars read or -1 on read error. | ||
37 | */ | ||
38 | long read_string(int fd, char* buf, long buf_size, int eos, long size) | ||
39 | { | ||
40 | long read_bytes = 0; | ||
41 | char c; | ||
42 | |||
43 | while (size != 0) | ||
44 | { | ||
45 | if (read(fd, &c, 1) != 1) | ||
46 | { | ||
47 | read_bytes = -1; | ||
48 | break; | ||
49 | } | ||
50 | |||
51 | read_bytes++; | ||
52 | size--; | ||
53 | |||
54 | if ((eos != -1) && (eos == (unsigned char) c)) | ||
55 | { | ||
56 | break; | ||
57 | } | ||
58 | |||
59 | if (buf_size > 1) | ||
60 | { | ||
61 | *buf++ = c; | ||
62 | buf_size--; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | *buf = 0; | ||
67 | return read_bytes; | ||
68 | } | ||
69 | /* Read an unsigned 8-bit integer from a file. */ | ||
70 | int read_uint8(int fd, uint8_t* buf) | ||
71 | { | ||
72 | size_t n; | ||
73 | |||
74 | n = read(fd, (char*) buf, 1); | ||
75 | return n; | ||
76 | } | ||
77 | |||
78 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
79 | /* Read an unsigned 16-bit integer from a big-endian file. */ | ||
80 | int read_uint16be(int fd, uint16_t* buf) | ||
81 | { | ||
82 | size_t n; | ||
83 | |||
84 | n = read(fd, (char*) buf, 2); | ||
85 | *buf = betoh16(*buf); | ||
86 | return n; | ||
87 | } | ||
88 | /* Read an unsigned 32-bit integer from a big-endian file. */ | ||
89 | int read_uint32be(int fd, uint32_t* buf) | ||
90 | { | ||
91 | size_t n; | ||
92 | |||
93 | n = read(fd, (char*) buf, 4); | ||
94 | *buf = betoh32(*buf); | ||
95 | return n; | ||
96 | } | ||
97 | /* Read an unsigned 64-bit integer from a big-endian file. */ | ||
98 | int read_uint64be(int fd, uint64_t* buf) | ||
99 | { | ||
100 | size_t n; | ||
101 | uint8_t data[8]; | ||
102 | int i; | ||
103 | |||
104 | n = read(fd, data, 8); | ||
105 | |||
106 | for (i=0, *buf=0; i<=7; i++) { | ||
107 | *buf <<= 8; | ||
108 | *buf |= data[i]; | ||
109 | } | ||
110 | return n; | ||
111 | } | ||
112 | #else | ||
113 | /* Read unsigned integers from a little-endian file. */ | ||
114 | int read_uint16le(int fd, uint16_t* buf) | ||
115 | { | ||
116 | size_t n; | ||
117 | |||
118 | n = read(fd, (char*) buf, 2); | ||
119 | *buf = letoh16(*buf); | ||
120 | return n; | ||
121 | } | ||
122 | int read_uint32le(int fd, uint32_t* buf) | ||
123 | { | ||
124 | size_t n; | ||
125 | |||
126 | n = read(fd, (char*) buf, 4); | ||
127 | *buf = letoh32(*buf); | ||
128 | return n; | ||
129 | } | ||
130 | int read_uint64le(int fd, uint64_t* buf) | ||
131 | { | ||
132 | size_t n; | ||
133 | uint8_t data[8]; | ||
134 | int i; | ||
135 | |||
136 | n = read(fd, data, 8); | ||
137 | |||
138 | for (i=7, *buf=0; i>=0; i--) { | ||
139 | *buf <<= 8; | ||
140 | *buf |= data[i]; | ||
141 | } | ||
142 | |||
143 | return n; | ||
144 | } | ||
145 | #endif | ||
146 | |||
147 | /* Read an unaligned 64-bit little endian unsigned integer from buffer. */ | ||
148 | uint64_t get_uint64_le(void* buf) | ||
149 | { | ||
150 | unsigned char* p = (unsigned char*) buf; | ||
151 | |||
152 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24) | ((uint64_t)p[4] << 32) | | ||
153 | ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56); | ||
154 | } | ||
155 | |||
156 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
157 | uint32_t get_long_le(void* buf) | ||
158 | { | ||
159 | unsigned char* p = (unsigned char*) buf; | ||
160 | |||
161 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
162 | } | ||
163 | |||
164 | /* Read an unaligned 16-bit little endian short from buffer. */ | ||
165 | uint16_t get_short_le(void* buf) | ||
166 | { | ||
167 | unsigned char* p = (unsigned char*) buf; | ||
168 | |||
169 | return p[0] | (p[1] << 8); | ||
170 | } | ||
171 | |||
172 | /* Read an unaligned 32-bit big endian long from buffer. */ | ||
173 | uint32_t get_long_be(void* buf) | ||
174 | { | ||
175 | unsigned char* p = (unsigned char*) buf; | ||
176 | |||
177 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | ||
178 | } | ||
179 | |||
180 | /* Read an unaligned 16-bit little endian short from buffer. */ | ||
181 | uint16_t get_short_be(void* buf) | ||
182 | { | ||
183 | unsigned char* p = (unsigned char*) buf; | ||
184 | |||
185 | return (p[0] << 8) | p[1]; | ||
186 | } | ||
187 | |||
188 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
189 | int32_t get_slong(void* buf) | ||
190 | { | ||
191 | unsigned char* p = (unsigned char*) buf; | ||
192 | |||
193 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
194 | } | ||
195 | |||
196 | uint32_t get_itunes_int32(char* value, int count) | ||
197 | { | ||
198 | static const char hexdigits[] = "0123456789ABCDEF"; | ||
199 | const char* c; | ||
200 | int r = 0; | ||
201 | |||
202 | while (count-- > 0) | ||
203 | { | ||
204 | while (isspace(*value)) | ||
205 | { | ||
206 | value++; | ||
207 | } | ||
208 | |||
209 | while (*value && !isspace(*value)) | ||
210 | { | ||
211 | value++; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | while (isspace(*value)) | ||
216 | { | ||
217 | value++; | ||
218 | } | ||
219 | |||
220 | while (*value && ((c = strchr(hexdigits, toupper(*value))) != NULL)) | ||
221 | { | ||
222 | r = (r << 4) | (c - hexdigits); | ||
223 | value++; | ||
224 | } | ||
225 | |||
226 | return r; | ||
227 | } | ||
228 | |||
229 | /* Skip an ID3v2 tag if it can be found. We assume the tag is located at the | ||
230 | * start of the file, which should be true in all cases where we need to skip it. | ||
231 | * Returns true if successfully skipped or not skipped, and false if | ||
232 | * something went wrong while skipping. | ||
233 | */ | ||
234 | bool skip_id3v2(int fd, struct mp3entry *id3) | ||
235 | { | ||
236 | char buf[4]; | ||
237 | |||
238 | read(fd, buf, 4); | ||
239 | if (memcmp(buf, "ID3", 3) == 0) | ||
240 | { | ||
241 | /* We have found an ID3v2 tag at the start of the file - find its | ||
242 | length and then skip it. */ | ||
243 | if ((id3->first_frame_offset = getid3v2len(fd)) == 0) | ||
244 | return false; | ||
245 | |||
246 | if ((lseek(fd, id3->first_frame_offset, SEEK_SET) < 0)) | ||
247 | return false; | ||
248 | |||
249 | return true; | ||
250 | } else { | ||
251 | lseek(fd, 0, SEEK_SET); | ||
252 | id3->first_frame_offset = 0; | ||
253 | return true; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | /* Parse the tag (the name-value pair) and fill id3 and buffer accordingly. | ||
258 | * String values to keep are written to buf. Returns number of bytes written | ||
259 | * to buf (including end nil). | ||
260 | */ | ||
261 | long parse_tag(const char* name, char* value, struct mp3entry* id3, | ||
262 | char* buf, long buf_remaining, enum tagtype type) | ||
263 | { | ||
264 | long len = 0; | ||
265 | char** p; | ||
266 | |||
267 | if ((((strcasecmp(name, "track") == 0) && (type == TAGTYPE_APE))) | ||
268 | || ((strcasecmp(name, "tracknumber") == 0) && (type == TAGTYPE_VORBIS))) | ||
269 | { | ||
270 | id3->tracknum = atoi(value); | ||
271 | p = &(id3->track_string); | ||
272 | } | ||
273 | else if (strcasecmp(name, "discnumber") == 0 || strcasecmp(name, "disc") == 0) | ||
274 | { | ||
275 | id3->discnum = atoi(value); | ||
276 | p = &(id3->disc_string); | ||
277 | } | ||
278 | else if (((strcasecmp(name, "year") == 0) && (type == TAGTYPE_APE)) | ||
279 | || ((strcasecmp(name, "date") == 0) && (type == TAGTYPE_VORBIS))) | ||
280 | { | ||
281 | /* Date's can be in any format in Vorbis. However most of them | ||
282 | * are in ISO8601 format so if we try and parse the first part | ||
283 | * of the tag as a number, we should get the year. If we get crap, | ||
284 | * then act like we never parsed it. | ||
285 | */ | ||
286 | id3->year = atoi(value); | ||
287 | if (id3->year < 1900) | ||
288 | { /* yeah, not likely */ | ||
289 | id3->year = 0; | ||
290 | } | ||
291 | p = &(id3->year_string); | ||
292 | } | ||
293 | else if (strcasecmp(name, "title") == 0) | ||
294 | { | ||
295 | p = &(id3->title); | ||
296 | } | ||
297 | else if (strcasecmp(name, "artist") == 0) | ||
298 | { | ||
299 | p = &(id3->artist); | ||
300 | } | ||
301 | else if (strcasecmp(name, "album") == 0) | ||
302 | { | ||
303 | p = &(id3->album); | ||
304 | } | ||
305 | else if (strcasecmp(name, "genre") == 0) | ||
306 | { | ||
307 | p = &(id3->genre_string); | ||
308 | } | ||
309 | else if (strcasecmp(name, "composer") == 0) | ||
310 | { | ||
311 | p = &(id3->composer); | ||
312 | } | ||
313 | else if (strcasecmp(name, "comment") == 0) | ||
314 | { | ||
315 | p = &(id3->comment); | ||
316 | } | ||
317 | else if (strcasecmp(name, "albumartist") == 0) | ||
318 | { | ||
319 | p = &(id3->albumartist); | ||
320 | } | ||
321 | else if (strcasecmp(name, "album artist") == 0) | ||
322 | { | ||
323 | p = &(id3->albumartist); | ||
324 | } | ||
325 | else if (strcasecmp(name, "ensemble") == 0) | ||
326 | { | ||
327 | p = &(id3->albumartist); | ||
328 | } | ||
329 | else if (strcasecmp(name, "grouping") == 0) | ||
330 | { | ||
331 | p = &(id3->grouping); | ||
332 | } | ||
333 | else if (strcasecmp(name, "content group") == 0) | ||
334 | { | ||
335 | p = &(id3->grouping); | ||
336 | } | ||
337 | else if (strcasecmp(name, "contentgroup") == 0) | ||
338 | { | ||
339 | p = &(id3->grouping); | ||
340 | } | ||
341 | else if (strcasecmp(name, "musicbrainz_trackid") == 0 | ||
342 | || strcasecmp(name, "http://musicbrainz.org") == 0 ) | ||
343 | { | ||
344 | p = &(id3->mb_track_id); | ||
345 | } | ||
346 | else | ||
347 | { | ||
348 | parse_replaygain(name, value, id3); | ||
349 | p = NULL; | ||
350 | } | ||
351 | |||
352 | /* Do not overwrite already available metadata. Especially when reading | ||
353 | * tags with e.g. multiple genres / artists. This way only the first | ||
354 | * of multiple entries is used, all following are dropped. */ | ||
355 | if (p!=NULL && *p==NULL) | ||
356 | { | ||
357 | len = strlen(value); | ||
358 | len = MIN(len, buf_remaining - 1); | ||
359 | len = MIN(len, ID3V2_MAX_ITEM_SIZE); /* Limit max. item size. */ | ||
360 | |||
361 | if (len > 0) | ||
362 | { | ||
363 | len++; | ||
364 | strlcpy(buf, value, len); | ||
365 | *p = buf; | ||
366 | } | ||
367 | else | ||
368 | { | ||
369 | len = 0; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | return len; | ||
374 | } | ||
diff --git a/lib/rbcodec/metadata/metadata_common.h b/lib/rbcodec/metadata/metadata_common.h new file mode 100644 index 0000000000..db91729de4 --- /dev/null +++ b/lib/rbcodec/metadata/metadata_common.h | |||
@@ -0,0 +1,69 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <inttypes.h> | ||
22 | #include "metadata.h" | ||
23 | |||
24 | #ifdef ROCKBOX_BIG_ENDIAN | ||
25 | #define IS_BIG_ENDIAN 1 | ||
26 | #else | ||
27 | #define IS_BIG_ENDIAN 0 | ||
28 | #endif | ||
29 | |||
30 | #define TAG_NAME_LENGTH 32 | ||
31 | #define TAG_VALUE_LENGTH 128 | ||
32 | |||
33 | #define FOURCC(a,b,c,d) (((a)<<24) | ((b) << 16) | ((c) << 8) | (d)) | ||
34 | |||
35 | enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS }; | ||
36 | |||
37 | bool read_ape_tags(int fd, struct mp3entry* id3); | ||
38 | long read_vorbis_tags(int fd, struct mp3entry *id3, | ||
39 | long tag_remaining); | ||
40 | |||
41 | bool skip_id3v2(int fd, struct mp3entry *id3); | ||
42 | long read_string(int fd, char* buf, long buf_size, int eos, long size); | ||
43 | |||
44 | int read_uint8(int fd, uint8_t* buf); | ||
45 | #ifdef ROCKBOX_BIG_ENDIAN | ||
46 | #define read_uint16be(fd,buf) read((fd), (buf), 2) | ||
47 | #define read_uint32be(fd,buf) read((fd), (buf), 4) | ||
48 | #define read_uint64be(fd,buf) read((fd), (buf), 8) | ||
49 | int read_uint16le(int fd, uint16_t* buf); | ||
50 | int read_uint32le(int fd, uint32_t* buf); | ||
51 | int read_uint64le(int fd, uint64_t* buf); | ||
52 | #else | ||
53 | int read_uint16be(int fd, uint16_t* buf); | ||
54 | int read_uint32be(int fd, uint32_t* buf); | ||
55 | int read_uint64be(int fd, uint64_t* buf); | ||
56 | #define read_uint16le(fd,buf) read((fd), (buf), 2) | ||
57 | #define read_uint32le(fd,buf) read((fd), (buf), 4) | ||
58 | #define read_uint64le(fd,buf) read((fd), (buf), 8) | ||
59 | #endif | ||
60 | |||
61 | uint64_t get_uint64_le(void* buf); | ||
62 | uint32_t get_long_le(void* buf); | ||
63 | uint16_t get_short_le(void* buf); | ||
64 | uint32_t get_long_be(void* buf); | ||
65 | uint16_t get_short_be(void* buf); | ||
66 | int32_t get_slong(void* buf); | ||
67 | uint32_t get_itunes_int32(char* value, int count); | ||
68 | long parse_tag(const char* name, char* value, struct mp3entry* id3, | ||
69 | char* buf, long buf_remaining, enum tagtype type); | ||
diff --git a/lib/rbcodec/metadata/metadata_parsers.h b/lib/rbcodec/metadata/metadata_parsers.h new file mode 100644 index 0000000000..304e393538 --- /dev/null +++ b/lib/rbcodec/metadata/metadata_parsers.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #if CONFIG_CODEC == SWCODEC | ||
23 | char* id3_get_num_genre(unsigned int genre_num); | ||
24 | #endif | ||
25 | int getid3v2len(int fd); | ||
26 | bool setid3v1title(int fd, struct mp3entry *entry); | ||
27 | void setid3v2title(int fd, struct mp3entry *entry); | ||
28 | bool get_mp3_metadata(int fd, struct mp3entry* id3); | ||
29 | #if CONFIG_CODEC == SWCODEC | ||
30 | bool get_adx_metadata(int fd, struct mp3entry* id3); | ||
31 | bool get_aiff_metadata(int fd, struct mp3entry* id3); | ||
32 | bool get_flac_metadata(int fd, struct mp3entry* id3); | ||
33 | bool get_mp4_metadata(int fd, struct mp3entry* id3); | ||
34 | bool get_monkeys_metadata(int fd, struct mp3entry* id3); | ||
35 | bool get_musepack_metadata(int fd, struct mp3entry *id3); | ||
36 | bool get_sid_metadata(int fd, struct mp3entry* id3); | ||
37 | bool get_mod_metadata(int fd, struct mp3entry* id3); | ||
38 | bool get_spc_metadata(int fd, struct mp3entry* id3); | ||
39 | bool get_ogg_metadata(int fd, struct mp3entry* id3); | ||
40 | bool get_wave_metadata(int fd, struct mp3entry* id3); | ||
41 | bool get_wavpack_metadata(int fd, struct mp3entry* id3); | ||
42 | bool get_a52_metadata(int fd, struct mp3entry* id3); | ||
43 | bool get_asf_metadata(int fd, struct mp3entry* id3); | ||
44 | bool get_asap_metadata(int fd, struct mp3entry* id3); | ||
45 | bool get_rm_metadata(int fd, struct mp3entry* id3); | ||
46 | bool get_nsf_metadata(int fd, struct mp3entry* id3); | ||
47 | bool get_oma_metadata(int fd, struct mp3entry* id3); | ||
48 | bool get_smaf_metadata(int fd, struct mp3entry* id3); | ||
49 | bool get_au_metadata(int fd, struct mp3entry* id3); | ||
50 | bool get_vox_metadata(int fd, struct mp3entry* id3); | ||
51 | bool get_wave64_metadata(int fd, struct mp3entry* id3); | ||
52 | bool get_tta_metadata(int fd, struct mp3entry* id3); | ||
53 | bool get_ay_metadata(int fd, struct mp3entry* id3); | ||
54 | bool get_gbs_metadata(int fd, struct mp3entry* id3); | ||
55 | bool get_hes_metadata(int fd, struct mp3entry* id3); | ||
56 | bool get_sgc_metadata(int fd, struct mp3entry* id3); | ||
57 | bool get_vgm_metadata(int fd, struct mp3entry* id3); | ||
58 | bool get_kss_metadata(int fd, struct mp3entry* id3); | ||
59 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
diff --git a/lib/rbcodec/metadata/mod.c b/lib/rbcodec/metadata/mod.c new file mode 100644 index 0000000000..de76823e91 --- /dev/null +++ b/lib/rbcodec/metadata/mod.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <ctype.h> | ||
24 | #include <inttypes.h> | ||
25 | |||
26 | #include "system.h" | ||
27 | #include "metadata.h" | ||
28 | #include <string-extra.h> | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "rbunicode.h" | ||
32 | |||
33 | #define MODULEHEADERSIZE 0x438 | ||
34 | |||
35 | bool get_mod_metadata(int fd, struct mp3entry* id3) | ||
36 | { | ||
37 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
38 | unsigned char *buf = id3->id3v2buf; | ||
39 | unsigned char id[4]; | ||
40 | bool is_mod_file = false; | ||
41 | |||
42 | /* Seek to file begin */ | ||
43 | if (lseek(fd, 0, SEEK_SET) < 0) | ||
44 | return false; | ||
45 | /* Use id3v2buf as buffer for the track name */ | ||
46 | if (read(fd, buf, sizeof(id3->id3v2buf)) < (ssize_t)sizeof(id3->id3v2buf)) | ||
47 | return false; | ||
48 | /* Seek to MOD ID position */ | ||
49 | if (lseek(fd, MODULEHEADERSIZE, SEEK_SET) < 0) | ||
50 | return false; | ||
51 | /* Read MOD ID */ | ||
52 | if (read(fd, id, sizeof(id)) < (ssize_t)sizeof(id)) | ||
53 | return false; | ||
54 | |||
55 | /* Mod type checking based on MikMod */ | ||
56 | /* Protracker and variants */ | ||
57 | if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) { | ||
58 | is_mod_file = true; | ||
59 | } | ||
60 | |||
61 | /* Star Tracker */ | ||
62 | if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) && | ||
63 | (isdigit(id[3]))) { | ||
64 | char numchn = id[3] - '0'; | ||
65 | if (numchn == 4 || numchn == 8) | ||
66 | is_mod_file = true; | ||
67 | } | ||
68 | |||
69 | /* Oktalyzer (Amiga) */ | ||
70 | if (!memcmp(id, "OKTA", 4)) { | ||
71 | is_mod_file = true; | ||
72 | } | ||
73 | |||
74 | /* Oktalyser (Atari) */ | ||
75 | if (!memcmp(id, "CD81", 4)) { | ||
76 | is_mod_file = true; | ||
77 | } | ||
78 | |||
79 | /* Fasttracker */ | ||
80 | if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) { | ||
81 | is_mod_file = true; | ||
82 | } | ||
83 | /* Fasttracker or Taketracker */ | ||
84 | if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2))) | ||
85 | && (isdigit(id[0])) && (isdigit(id[1]))) { | ||
86 | is_mod_file = true; | ||
87 | } | ||
88 | |||
89 | /* Don't try to play if we can't find a known mod type | ||
90 | * (there are mod files which have nothing to do with music) */ | ||
91 | if (!is_mod_file) | ||
92 | return false; | ||
93 | |||
94 | id3->title = id3->id3v2buf; /* Point title to previous read ID3 buffer. */ | ||
95 | id3->bitrate = filesize(fd)/1024; /* size in kb */ | ||
96 | id3->frequency = 44100; | ||
97 | id3->length = 120*1000; | ||
98 | id3->vbr = false; | ||
99 | id3->filesize = filesize(fd); | ||
100 | |||
101 | return true; | ||
102 | } | ||
103 | |||
diff --git a/lib/rbcodec/metadata/monkeys.c b/lib/rbcodec/metadata/monkeys.c new file mode 100644 index 0000000000..4aff1412aa --- /dev/null +++ b/lib/rbcodec/metadata/monkeys.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | |||
32 | bool get_monkeys_metadata(int fd, struct mp3entry* id3) | ||
33 | { | ||
34 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
35 | unsigned char* buf = (unsigned char *)id3->path; | ||
36 | unsigned char* header; | ||
37 | bool rc = false; | ||
38 | uint32_t descriptorlength; | ||
39 | uint32_t totalsamples; | ||
40 | uint32_t blocksperframe, finalframeblocks, totalframes; | ||
41 | int fileversion; | ||
42 | |||
43 | lseek(fd, 0, SEEK_SET); | ||
44 | |||
45 | if (read(fd, buf, 4) < 4) | ||
46 | { | ||
47 | return rc; | ||
48 | } | ||
49 | |||
50 | if (memcmp(buf, "MAC ", 4) != 0) | ||
51 | { | ||
52 | return rc; | ||
53 | } | ||
54 | |||
55 | read(fd, buf + 4, MAX_PATH - 4); | ||
56 | |||
57 | fileversion = get_short_le(buf+4); | ||
58 | if (fileversion < 3970) | ||
59 | { | ||
60 | /* Not supported */ | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | if (fileversion >= 3980) | ||
65 | { | ||
66 | descriptorlength = get_long_le(buf+8); | ||
67 | |||
68 | header = buf + descriptorlength; | ||
69 | |||
70 | blocksperframe = get_long_le(header+4); | ||
71 | finalframeblocks = get_long_le(header+8); | ||
72 | totalframes = get_long_le(header+12); | ||
73 | id3->frequency = get_long_le(header+20); | ||
74 | } | ||
75 | else | ||
76 | { | ||
77 | /* v3.95 and later files all have a fixed framesize */ | ||
78 | blocksperframe = 73728 * 4; | ||
79 | |||
80 | finalframeblocks = get_long_le(buf+28); | ||
81 | totalframes = get_long_le(buf+24); | ||
82 | id3->frequency = get_long_le(buf+12); | ||
83 | } | ||
84 | |||
85 | id3->vbr = true; /* All APE files are VBR */ | ||
86 | id3->filesize = filesize(fd); | ||
87 | |||
88 | totalsamples = finalframeblocks; | ||
89 | if (totalframes > 1) | ||
90 | totalsamples += blocksperframe * (totalframes-1); | ||
91 | |||
92 | id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; | ||
93 | id3->bitrate = (id3->filesize * 8) / id3->length; | ||
94 | |||
95 | read_ape_tags(fd, id3); | ||
96 | return true; | ||
97 | } | ||
diff --git a/lib/rbcodec/metadata/mp3.c b/lib/rbcodec/metadata/mp3.c new file mode 100644 index 0000000000..feb1a52f77 --- /dev/null +++ b/lib/rbcodec/metadata/mp3.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | /* | ||
22 | * Parts of this code has been stolen from the Ample project and was written | ||
23 | * by David H�deman. It has since been extended and enhanced pretty much by | ||
24 | * all sorts of friendly Rockbox people. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <stdbool.h> | ||
35 | #include "string-extra.h" | ||
36 | #include "config.h" | ||
37 | #include "file.h" | ||
38 | #include "logf.h" | ||
39 | |||
40 | #include "system.h" | ||
41 | #include "metadata.h" | ||
42 | #include "mp3data.h" | ||
43 | #include "metadata_common.h" | ||
44 | #include "metadata_parsers.h" | ||
45 | |||
46 | /* | ||
47 | * Calculates the length (in milliseconds) of an MP3 file. | ||
48 | * | ||
49 | * Modified to only use integers. | ||
50 | * | ||
51 | * Arguments: file - the file to calculate the length upon | ||
52 | * entry - the entry to update with the length | ||
53 | * | ||
54 | * Returns: the song length in milliseconds, | ||
55 | * 0 means that it couldn't be calculated | ||
56 | */ | ||
57 | static int getsonglength(int fd, struct mp3entry *entry) | ||
58 | { | ||
59 | unsigned long filetime = 0; | ||
60 | struct mp3info info; | ||
61 | long bytecount; | ||
62 | |||
63 | /* Start searching after ID3v2 header */ | ||
64 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) | ||
65 | return 0; | ||
66 | |||
67 | bytecount = get_mp3file_info(fd, &info); | ||
68 | |||
69 | logf("Space between ID3V2 tag and first audio frame: 0x%lx bytes", | ||
70 | bytecount); | ||
71 | |||
72 | if(bytecount < 0) | ||
73 | return -1; | ||
74 | |||
75 | bytecount += entry->id3v2len; | ||
76 | |||
77 | /* Validate byte count, in case the file has been edited without | ||
78 | * updating the header. | ||
79 | */ | ||
80 | if (info.byte_count) | ||
81 | { | ||
82 | const unsigned long expected = entry->filesize - entry->id3v1len | ||
83 | - entry->id3v2len; | ||
84 | const unsigned long diff = MAX(10240, info.byte_count / 20); | ||
85 | |||
86 | if ((info.byte_count > expected + diff) | ||
87 | || (info.byte_count < expected - diff)) | ||
88 | { | ||
89 | logf("Note: info.byte_count differs from expected value by " | ||
90 | "%ld bytes", labs((long) (expected - info.byte_count))); | ||
91 | info.byte_count = 0; | ||
92 | info.frame_count = 0; | ||
93 | info.file_time = 0; | ||
94 | info.enc_padding = 0; | ||
95 | |||
96 | /* Even if the bitrate was based on "known bad" values, it | ||
97 | * should still be better for VBR files than using the bitrate | ||
98 | * of the first audio frame. | ||
99 | */ | ||
100 | } | ||
101 | } | ||
102 | |||
103 | entry->bitrate = info.bitrate; | ||
104 | entry->frequency = info.frequency; | ||
105 | entry->layer = info.layer; | ||
106 | switch(entry->layer) { | ||
107 | #if CONFIG_CODEC==SWCODEC | ||
108 | case 0: | ||
109 | entry->codectype=AFMT_MPA_L1; | ||
110 | break; | ||
111 | #endif | ||
112 | case 1: | ||
113 | entry->codectype=AFMT_MPA_L2; | ||
114 | break; | ||
115 | case 2: | ||
116 | entry->codectype=AFMT_MPA_L3; | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | /* If the file time hasn't been established, this may be a fixed | ||
121 | rate MP3, so just use the default formula */ | ||
122 | |||
123 | filetime = info.file_time; | ||
124 | |||
125 | if(filetime == 0) | ||
126 | { | ||
127 | /* Prevent a division by zero */ | ||
128 | if (info.bitrate < 8) | ||
129 | filetime = 0; | ||
130 | else | ||
131 | filetime = (entry->filesize - bytecount) / (info.bitrate / 8); | ||
132 | /* bitrate is in kbps so this delivers milliseconds. Doing bitrate / 8 | ||
133 | * instead of filesize * 8 is exact, because mpeg audio bitrates are | ||
134 | * always multiples of 8, and it avoids overflows. */ | ||
135 | } | ||
136 | |||
137 | entry->frame_count = info.frame_count; | ||
138 | |||
139 | entry->vbr = info.is_vbr; | ||
140 | entry->has_toc = info.has_toc; | ||
141 | |||
142 | #if CONFIG_CODEC==SWCODEC | ||
143 | if (!entry->lead_trim) | ||
144 | entry->lead_trim = info.enc_delay; | ||
145 | if (!entry->tail_trim) | ||
146 | entry->tail_trim = info.enc_padding; | ||
147 | #endif | ||
148 | |||
149 | memcpy(entry->toc, info.toc, sizeof(info.toc)); | ||
150 | |||
151 | /* Update the seek point for the first playable frame */ | ||
152 | entry->first_frame_offset = bytecount; | ||
153 | logf("First frame is at %lx", entry->first_frame_offset); | ||
154 | |||
155 | return filetime; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) | ||
160 | * about an MP3 file and updates it's entry accordingly. | ||
161 | * | ||
162 | Note, that this returns true for successful, false for error! */ | ||
163 | bool get_mp3_metadata(int fd, struct mp3entry *entry) | ||
164 | { | ||
165 | entry->title = NULL; | ||
166 | entry->filesize = filesize(fd); | ||
167 | entry->id3v2len = getid3v2len(fd); | ||
168 | entry->tracknum = 0; | ||
169 | entry->discnum = 0; | ||
170 | |||
171 | if (entry->id3v2len) | ||
172 | setid3v2title(fd, entry); | ||
173 | int len = getsonglength(fd, entry); | ||
174 | if (len < 0) | ||
175 | return false; | ||
176 | entry->length = len; | ||
177 | |||
178 | /* Subtract the meta information from the file size to get | ||
179 | the true size of the MP3 stream */ | ||
180 | entry->filesize -= entry->first_frame_offset; | ||
181 | |||
182 | /* only seek to end of file if no id3v2 tags were found */ | ||
183 | if (!entry->id3v2len) { | ||
184 | setid3v1title(fd, entry); | ||
185 | } | ||
186 | |||
187 | if(!entry->length || (entry->filesize < 8 )) | ||
188 | /* no song length or less than 8 bytes is hereby considered to be an | ||
189 | invalid mp3 and won't be played by us! */ | ||
190 | return false; | ||
191 | |||
192 | return true; | ||
193 | } | ||
diff --git a/lib/rbcodec/metadata/mp3data.c b/lib/rbcodec/metadata/mp3data.c new file mode 100644 index 0000000000..13ff0a87a7 --- /dev/null +++ b/lib/rbcodec/metadata/mp3data.c | |||
@@ -0,0 +1,849 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* | ||
23 | * Parts of this code has been stolen from the Ample project and was written | ||
24 | * by David Härdeman. It has since been extended and enhanced pretty much by | ||
25 | * all sorts of friendly Rockbox people. | ||
26 | * | ||
27 | * A nice reference for MPEG header info: | ||
28 | * http://rockbox.haxx.se/docs/mpeghdr.html | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <stdio.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <string.h> | ||
35 | #include <stdbool.h> | ||
36 | #include <limits.h> | ||
37 | #include "debug.h" | ||
38 | #include "logf.h" | ||
39 | #include "mp3data.h" | ||
40 | #include "file.h" | ||
41 | #include "system.h" | ||
42 | |||
43 | //#define DEBUG_VERBOSE | ||
44 | |||
45 | #ifdef DEBUG_VERBOSE | ||
46 | #define VDEBUGF DEBUGF | ||
47 | #else | ||
48 | #define VDEBUGF(...) do { } while(0) | ||
49 | #endif | ||
50 | |||
51 | #define SYNC_MASK (0x7ffL << 21) | ||
52 | #define VERSION_MASK (3L << 19) | ||
53 | #define LAYER_MASK (3L << 17) | ||
54 | #define PROTECTION_MASK (1L << 16) | ||
55 | #define BITRATE_MASK (0xfL << 12) | ||
56 | #define SAMPLERATE_MASK (3L << 10) | ||
57 | #define PADDING_MASK (1L << 9) | ||
58 | #define PRIVATE_MASK (1L << 8) | ||
59 | #define CHANNELMODE_MASK (3L << 6) | ||
60 | #define MODE_EXT_MASK (3L << 4) | ||
61 | #define COPYRIGHT_MASK (1L << 3) | ||
62 | #define ORIGINAL_MASK (1L << 2) | ||
63 | #define EMPHASIS_MASK (3L) | ||
64 | |||
65 | /* Maximum number of bytes needed by Xing/Info/VBRI parser. */ | ||
66 | #define VBR_HEADER_MAX_SIZE (180) | ||
67 | |||
68 | /* MPEG Version table, sorted by version index */ | ||
69 | static const signed char version_table[4] = { | ||
70 | MPEG_VERSION2_5, -1, MPEG_VERSION2, MPEG_VERSION1 | ||
71 | }; | ||
72 | |||
73 | /* Bitrate table for mpeg audio, indexed by row index and birate index */ | ||
74 | static const short bitrates[5][16] = { | ||
75 | {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, /* V1 L1 */ | ||
76 | {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, /* V1 L2 */ | ||
77 | {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}, /* V1 L3 */ | ||
78 | {0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,0}, /* V2 L1 */ | ||
79 | {0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0} /* V2 L2+L3 */ | ||
80 | }; | ||
81 | |||
82 | /* Bitrate pointer table, indexed by version and layer */ | ||
83 | static const short *bitrate_table[3][3] = | ||
84 | { | ||
85 | {bitrates[0], bitrates[1], bitrates[2]}, | ||
86 | {bitrates[3], bitrates[4], bitrates[4]}, | ||
87 | {bitrates[3], bitrates[4], bitrates[4]} | ||
88 | }; | ||
89 | |||
90 | /* Sampling frequency table, indexed by version and frequency index */ | ||
91 | static const unsigned short freq_table[3][3] = | ||
92 | { | ||
93 | {44100, 48000, 32000}, /* MPEG Version 1 */ | ||
94 | {22050, 24000, 16000}, /* MPEG version 2 */ | ||
95 | {11025, 12000, 8000}, /* MPEG version 2.5 */ | ||
96 | }; | ||
97 | |||
98 | unsigned long bytes2int(unsigned long b0, unsigned long b1, | ||
99 | unsigned long b2, unsigned long b3) | ||
100 | { | ||
101 | return (b0 & 0xFF) << (3*8) | | ||
102 | (b1 & 0xFF) << (2*8) | | ||
103 | (b2 & 0xFF) << (1*8) | | ||
104 | (b3 & 0xFF) << (0*8); | ||
105 | } | ||
106 | |||
107 | /* check if 'head' is a valid mp3 frame header */ | ||
108 | static bool is_mp3frameheader(unsigned long head) | ||
109 | { | ||
110 | if ((head & SYNC_MASK) != (unsigned long)SYNC_MASK) /* bad sync? */ | ||
111 | return false; | ||
112 | if ((head & VERSION_MASK) == (1L << 19)) /* bad version? */ | ||
113 | return false; | ||
114 | if (!(head & LAYER_MASK)) /* no layer? */ | ||
115 | return false; | ||
116 | #if CONFIG_CODEC != SWCODEC | ||
117 | /* The MAS can't decode layer 1, so treat layer 1 data as invalid */ | ||
118 | if ((head & LAYER_MASK) == LAYER_MASK) | ||
119 | return false; | ||
120 | #endif | ||
121 | if ((head & BITRATE_MASK) == BITRATE_MASK) /* bad bitrate? */ | ||
122 | return false; | ||
123 | if (!(head & BITRATE_MASK)) /* no bitrate? */ | ||
124 | return false; | ||
125 | if ((head & SAMPLERATE_MASK) == SAMPLERATE_MASK) /* bad sample rate? */ | ||
126 | return false; | ||
127 | |||
128 | return true; | ||
129 | } | ||
130 | |||
131 | static bool mp3headerinfo(struct mp3info *info, unsigned long header) | ||
132 | { | ||
133 | int bitindex, freqindex; | ||
134 | |||
135 | /* MPEG Audio Version */ | ||
136 | if ((header & VERSION_MASK) >> 19 >= sizeof(version_table)) | ||
137 | return false; | ||
138 | |||
139 | info->version = version_table[(header & VERSION_MASK) >> 19]; | ||
140 | if (info->version < 0) | ||
141 | return false; | ||
142 | |||
143 | /* Layer */ | ||
144 | info->layer = 3 - ((header & LAYER_MASK) >> 17); | ||
145 | if (info->layer == 3) | ||
146 | return false; | ||
147 | |||
148 | /* Rockbox: not used | ||
149 | info->protection = (header & PROTECTION_MASK) ? true : false; | ||
150 | */ | ||
151 | |||
152 | /* Bitrate */ | ||
153 | bitindex = (header & BITRATE_MASK) >> 12; | ||
154 | info->bitrate = bitrate_table[info->version][info->layer][bitindex]; | ||
155 | if(info->bitrate == 0) | ||
156 | return false; | ||
157 | |||
158 | /* Sampling frequency */ | ||
159 | freqindex = (header & SAMPLERATE_MASK) >> 10; | ||
160 | if (freqindex == 3) | ||
161 | return false; | ||
162 | info->frequency = freq_table[info->version][freqindex]; | ||
163 | |||
164 | info->padding = (header & PADDING_MASK) ? 1 : 0; | ||
165 | |||
166 | /* Calculate number of bytes, calculation depends on layer */ | ||
167 | if (info->layer == 0) { | ||
168 | info->frame_samples = 384; | ||
169 | info->frame_size = (12000 * info->bitrate / info->frequency | ||
170 | + info->padding) * 4; | ||
171 | } | ||
172 | else { | ||
173 | if ((info->version > MPEG_VERSION1) && (info->layer == 2)) | ||
174 | info->frame_samples = 576; | ||
175 | else | ||
176 | info->frame_samples = 1152; | ||
177 | info->frame_size = (1000/8) * info->frame_samples * info->bitrate | ||
178 | / info->frequency + info->padding; | ||
179 | } | ||
180 | |||
181 | /* Frametime fraction denominator */ | ||
182 | if (freqindex != 0) { /* 48/32/24/16/12/8 kHz */ | ||
183 | info->ft_den = 1; /* integer number of milliseconds */ | ||
184 | } | ||
185 | else { /* 44.1/22.05/11.025 kHz */ | ||
186 | if (info->layer == 0) /* layer 1 */ | ||
187 | info->ft_den = 147; | ||
188 | else /* layer 2+3 */ | ||
189 | info->ft_den = 49; | ||
190 | } | ||
191 | /* Frametime fraction numerator */ | ||
192 | info->ft_num = 1000 * info->ft_den * info->frame_samples / info->frequency; | ||
193 | |||
194 | info->channel_mode = (header & CHANNELMODE_MASK) >> 6; | ||
195 | /* Rockbox: not used | ||
196 | info->mode_extension = (header & MODE_EXT_MASK) >> 4; | ||
197 | info->emphasis = header & EMPHASIS_MASK; | ||
198 | */ | ||
199 | VDEBUGF( "Header: %08lx, Ver %d, lay %d, bitr %d, freq %ld, " | ||
200 | "chmode %d, bytes: %d time: %d/%d\n", | ||
201 | header, info->version, info->layer+1, info->bitrate, | ||
202 | info->frequency, info->channel_mode, | ||
203 | info->frame_size, info->ft_num, info->ft_den); | ||
204 | return true; | ||
205 | } | ||
206 | |||
207 | static bool headers_have_same_type(unsigned long header1, | ||
208 | unsigned long header2) | ||
209 | { | ||
210 | /* Compare MPEG version, layer and sampling frequency. If header1 is zero | ||
211 | * it is assumed both frame headers are of same type. */ | ||
212 | unsigned int mask = SYNC_MASK | VERSION_MASK | LAYER_MASK | SAMPLERATE_MASK; | ||
213 | header1 &= mask; | ||
214 | header2 &= mask; | ||
215 | return header1 ? (header1 == header2) : true; | ||
216 | } | ||
217 | |||
218 | /* Helper function to read 4-byte in big endian format. */ | ||
219 | static void read_uint32be_mp3data(int fd, unsigned long *data) | ||
220 | { | ||
221 | #ifdef ROCKBOX_BIG_ENDIAN | ||
222 | (void)read(fd, (char*)data, 4); | ||
223 | #else | ||
224 | (void)read(fd, (char*)data, 4); | ||
225 | *data = betoh32(*data); | ||
226 | #endif | ||
227 | } | ||
228 | |||
229 | static unsigned long __find_next_frame(int fd, long *offset, long max_offset, | ||
230 | unsigned long reference_header, | ||
231 | int(*getfunc)(int fd, unsigned char *c), | ||
232 | bool single_header) | ||
233 | { | ||
234 | unsigned long header=0; | ||
235 | unsigned char tmp; | ||
236 | long pos = 0; | ||
237 | |||
238 | /* We will search until we find two consecutive MPEG frame headers with | ||
239 | * the same MPEG version, layer and sampling frequency. The first header | ||
240 | * of this pair is assumed to be the first valid MPEG frame header of the | ||
241 | * whole stream. */ | ||
242 | do { | ||
243 | /* Read 1 new byte. */ | ||
244 | header <<= 8; | ||
245 | if (!getfunc(fd, &tmp)) | ||
246 | return 0; | ||
247 | header |= tmp; | ||
248 | pos++; | ||
249 | |||
250 | /* Abort if max_offset is reached. Stop parsing. */ | ||
251 | if (max_offset > 0 && pos > max_offset) | ||
252 | return 0; | ||
253 | |||
254 | if (is_mp3frameheader(header)) { | ||
255 | if (single_header) { | ||
256 | /* We search for one _single_ valid header that has the same | ||
257 | * type as the reference_header (if reference_header != 0). | ||
258 | * In this case we are finished. */ | ||
259 | if (headers_have_same_type(reference_header, header)) | ||
260 | break; | ||
261 | } else { | ||
262 | /* The current header is valid. Now gather the frame size, | ||
263 | * seek to this byte position and check if there is another | ||
264 | * valid MPEG frame header of the same type. */ | ||
265 | struct mp3info info; | ||
266 | |||
267 | /* Gather frame size from given header and seek to next | ||
268 | * frame header. */ | ||
269 | mp3headerinfo(&info, header); | ||
270 | lseek(fd, info.frame_size-4, SEEK_CUR); | ||
271 | |||
272 | /* Read possible next frame header and seek back to last frame | ||
273 | * headers byte position. */ | ||
274 | reference_header = 0; | ||
275 | read_uint32be_mp3data(fd, &reference_header); | ||
276 | // | ||
277 | lseek(fd, -info.frame_size, SEEK_CUR); | ||
278 | |||
279 | /* If the current header is of the same type as the previous | ||
280 | * header we are finished. */ | ||
281 | if (headers_have_same_type(header, reference_header)) | ||
282 | break; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | } while (true); | ||
287 | |||
288 | *offset = pos - 4; | ||
289 | |||
290 | if(*offset) | ||
291 | VDEBUGF("Warning: skipping %ld bytes of garbage\n", *offset); | ||
292 | |||
293 | return header; | ||
294 | } | ||
295 | |||
296 | static int fileread(int fd, unsigned char *c) | ||
297 | { | ||
298 | return read(fd, c, 1); | ||
299 | } | ||
300 | |||
301 | unsigned long find_next_frame(int fd, | ||
302 | long *offset, | ||
303 | long max_offset, | ||
304 | unsigned long reference_header) | ||
305 | { | ||
306 | return __find_next_frame(fd, offset, max_offset, reference_header, | ||
307 | fileread, true); | ||
308 | } | ||
309 | |||
310 | #ifndef __PCTOOL__ | ||
311 | static int fnf_read_index; | ||
312 | static int fnf_buf_len; | ||
313 | static unsigned char *fnf_buf; | ||
314 | |||
315 | static int buf_getbyte(int fd, unsigned char *c) | ||
316 | { | ||
317 | if(fnf_read_index < fnf_buf_len) | ||
318 | { | ||
319 | *c = fnf_buf[fnf_read_index++]; | ||
320 | return 1; | ||
321 | } | ||
322 | else | ||
323 | { | ||
324 | fnf_buf_len = read(fd, fnf_buf, fnf_buf_len); | ||
325 | if(fnf_buf_len < 0) | ||
326 | return -1; | ||
327 | |||
328 | fnf_read_index = 0; | ||
329 | |||
330 | if(fnf_buf_len > 0) | ||
331 | { | ||
332 | *c = fnf_buf[fnf_read_index++]; | ||
333 | return 1; | ||
334 | } | ||
335 | else | ||
336 | return 0; | ||
337 | } | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static int buf_seek(int fd, int len) | ||
342 | { | ||
343 | fnf_read_index += len; | ||
344 | if(fnf_read_index > fnf_buf_len) | ||
345 | { | ||
346 | len = fnf_read_index - fnf_buf_len; | ||
347 | |||
348 | fnf_buf_len = read(fd, fnf_buf, fnf_buf_len); | ||
349 | if(fnf_buf_len < 0) | ||
350 | return -1; | ||
351 | |||
352 | fnf_read_index = 0; | ||
353 | fnf_read_index += len; | ||
354 | } | ||
355 | |||
356 | if(fnf_read_index > fnf_buf_len) | ||
357 | { | ||
358 | return -1; | ||
359 | } | ||
360 | else | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static void buf_init(unsigned char* buf, size_t buflen) | ||
365 | { | ||
366 | fnf_buf = buf; | ||
367 | fnf_buf_len = buflen; | ||
368 | fnf_read_index = 0; | ||
369 | } | ||
370 | |||
371 | static unsigned long buf_find_next_frame(int fd, long *offset, long max_offset) | ||
372 | { | ||
373 | return __find_next_frame(fd, offset, max_offset, 0, buf_getbyte, true); | ||
374 | } | ||
375 | |||
376 | static size_t mem_buflen; | ||
377 | static unsigned char* mem_buf; | ||
378 | static size_t mem_pos; | ||
379 | static int mem_cnt; | ||
380 | static int mem_maxlen; | ||
381 | |||
382 | static int mem_getbyte(int dummy, unsigned char *c) | ||
383 | { | ||
384 | (void)dummy; | ||
385 | |||
386 | *c = mem_buf[mem_pos++]; | ||
387 | if(mem_pos >= mem_buflen) | ||
388 | mem_pos = 0; | ||
389 | |||
390 | if(mem_cnt++ >= mem_maxlen) | ||
391 | return 0; | ||
392 | else | ||
393 | return 1; | ||
394 | } | ||
395 | |||
396 | unsigned long mem_find_next_frame(int startpos, | ||
397 | long *offset, | ||
398 | long max_offset, | ||
399 | unsigned long reference_header, | ||
400 | unsigned char* buf, size_t buflen) | ||
401 | { | ||
402 | mem_buf = buf; | ||
403 | mem_buflen = buflen; | ||
404 | mem_pos = startpos; | ||
405 | mem_cnt = 0; | ||
406 | mem_maxlen = max_offset; | ||
407 | |||
408 | return __find_next_frame(0, offset, max_offset, reference_header, | ||
409 | mem_getbyte, true); | ||
410 | } | ||
411 | #endif | ||
412 | |||
413 | /* Extract information from a 'Xing' or 'Info' header. */ | ||
414 | static void get_xing_info(struct mp3info *info, unsigned char *buf) | ||
415 | { | ||
416 | int i = 8; | ||
417 | |||
418 | /* Is it a VBR file? */ | ||
419 | info->is_vbr = !memcmp(buf, "Xing", 4); | ||
420 | |||
421 | if (buf[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ | ||
422 | { | ||
423 | info->frame_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]); | ||
424 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
425 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
426 | else | ||
427 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
428 | i += 4; | ||
429 | } | ||
430 | |||
431 | if (buf[7] & VBR_BYTES_FLAG) /* Is byte count there? */ | ||
432 | { | ||
433 | info->byte_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]); | ||
434 | i += 4; | ||
435 | } | ||
436 | |||
437 | if (info->file_time && info->byte_count) | ||
438 | { | ||
439 | if (info->byte_count <= (ULONG_MAX/8)) | ||
440 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
441 | else | ||
442 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
443 | } | ||
444 | |||
445 | if (buf[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ | ||
446 | { | ||
447 | info->has_toc = true; | ||
448 | memcpy( info->toc, buf+i, 100 ); | ||
449 | i += 100; | ||
450 | } | ||
451 | if (buf[7] & VBR_QUALITY_FLAG) | ||
452 | { | ||
453 | /* We don't care about this, but need to skip it */ | ||
454 | i += 4; | ||
455 | } | ||
456 | #if CONFIG_CODEC==SWCODEC | ||
457 | i += 21; | ||
458 | info->enc_delay = ((int)buf[i ] << 4) | (buf[i+1] >> 4); | ||
459 | info->enc_padding = ((int)(buf[i+1]&0xF) << 8) | buf[i+2]; | ||
460 | /* TODO: This sanity checking is rather silly, seeing as how the LAME | ||
461 | header contains a CRC field that can be used to verify integrity. */ | ||
462 | if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 && | ||
463 | info->enc_padding >= 0 && info->enc_padding <= 2*1152)) | ||
464 | { | ||
465 | /* Invalid data */ | ||
466 | info->enc_delay = -1; | ||
467 | info->enc_padding = -1; | ||
468 | } | ||
469 | #endif | ||
470 | } | ||
471 | |||
472 | /* Extract information from a 'VBRI' header. */ | ||
473 | static void get_vbri_info(struct mp3info *info, unsigned char *buf) | ||
474 | { | ||
475 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
476 | /* | ||
477 | int i, num_offsets, offset = 0; | ||
478 | */ | ||
479 | |||
480 | info->is_vbr = true; /* Yes, it is a FhG VBR file */ | ||
481 | info->has_toc = false; /* We don't parse the TOC (yet) */ | ||
482 | |||
483 | info->byte_count = bytes2int(buf[10], buf[11], buf[12], buf[13]); | ||
484 | info->frame_count = bytes2int(buf[14], buf[15], buf[16], buf[17]); | ||
485 | if (info->frame_count <= ULONG_MAX / info->ft_num) | ||
486 | info->file_time = info->frame_count * info->ft_num / info->ft_den; | ||
487 | else | ||
488 | info->file_time = info->frame_count / info->ft_den * info->ft_num; | ||
489 | |||
490 | if (info->byte_count <= (ULONG_MAX/8)) | ||
491 | info->bitrate = info->byte_count * 8 / info->file_time; | ||
492 | else | ||
493 | info->bitrate = info->byte_count / (info->file_time >> 3); | ||
494 | |||
495 | VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n", | ||
496 | info->bitrate, info->frame_size, info->frame_size); | ||
497 | VDEBUGF("Frame count: %lx\n", info->frame_count); | ||
498 | VDEBUGF("Byte count: %lx\n", info->byte_count); | ||
499 | |||
500 | /* We don't parse the TOC, since we don't yet know how to (FIXME) */ | ||
501 | /* | ||
502 | num_offsets = bytes2int(0, 0, buf[18], buf[19]); | ||
503 | VDEBUGF("Offsets: %d\n", num_offsets); | ||
504 | VDEBUGF("Frames/entry: %ld\n", bytes2int(0, 0, buf[24], buf[25])); | ||
505 | |||
506 | for(i = 0; i < num_offsets; i++) | ||
507 | { | ||
508 | offset += bytes2int(0, 0, buf[26+i*2], buf[27+i*2]);; | ||
509 | VDEBUGF("%03d: %lx\n", i, offset - bytecount,); | ||
510 | } | ||
511 | */ | ||
512 | } | ||
513 | |||
514 | /* Seek to next mpeg header and extract relevant information. */ | ||
515 | static int get_next_header_info(int fd, long *bytecount, struct mp3info *info, | ||
516 | bool single_header) | ||
517 | { | ||
518 | long tmp; | ||
519 | unsigned long header = 0; | ||
520 | |||
521 | header = __find_next_frame(fd, &tmp, 0x20000, 0, fileread, single_header); | ||
522 | if(header == 0) | ||
523 | return -1; | ||
524 | |||
525 | if(!mp3headerinfo(info, header)) | ||
526 | return -2; | ||
527 | |||
528 | /* Next frame header is tmp bytes away. */ | ||
529 | *bytecount += tmp; | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | int get_mp3file_info(int fd, struct mp3info *info) | ||
535 | { | ||
536 | unsigned char frame[VBR_HEADER_MAX_SIZE], *vbrheader; | ||
537 | long bytecount = 0; | ||
538 | int result, buf_size; | ||
539 | |||
540 | /* Initialize info and frame */ | ||
541 | memset(info, 0, sizeof(struct mp3info)); | ||
542 | memset(frame, 0, sizeof(frame)); | ||
543 | |||
544 | #if CONFIG_CODEC==SWCODEC | ||
545 | /* These two are needed for proper LAME gapless MP3 playback */ | ||
546 | info->enc_delay = -1; | ||
547 | info->enc_padding = -1; | ||
548 | #endif | ||
549 | |||
550 | /* Get the very first single MPEG frame. */ | ||
551 | result = get_next_header_info(fd, &bytecount, info, true); | ||
552 | if(result) | ||
553 | return result; | ||
554 | |||
555 | /* Read the amount of frame data to the buffer that is required for the | ||
556 | * vbr tag parsing. Skip the rest. */ | ||
557 | buf_size = MIN(info->frame_size-4, (int)sizeof(frame)); | ||
558 | if(read(fd, frame, buf_size) < 0) | ||
559 | return -3; | ||
560 | lseek(fd, info->frame_size - 4 - buf_size, SEEK_CUR); | ||
561 | |||
562 | /* Calculate position of a possible VBR header */ | ||
563 | if (info->version == MPEG_VERSION1) { | ||
564 | if (info->channel_mode == 3) /* mono */ | ||
565 | vbrheader = frame + 17; | ||
566 | else | ||
567 | vbrheader = frame + 32; | ||
568 | } else { | ||
569 | if (info->channel_mode == 3) /* mono */ | ||
570 | vbrheader = frame + 9; | ||
571 | else | ||
572 | vbrheader = frame + 17; | ||
573 | } | ||
574 | |||
575 | if (!memcmp(vbrheader, "Xing", 4) || !memcmp(vbrheader, "Info", 4)) | ||
576 | { | ||
577 | VDEBUGF("-- XING header --\n"); | ||
578 | |||
579 | /* We want to skip the Xing frame when playing the stream */ | ||
580 | bytecount += info->frame_size; | ||
581 | |||
582 | /* Now get the next frame to read the real info about the mp3 stream */ | ||
583 | result = get_next_header_info(fd, &bytecount, info, false); | ||
584 | if(result) | ||
585 | return result; | ||
586 | |||
587 | get_xing_info(info, vbrheader); | ||
588 | } | ||
589 | else if (!memcmp(vbrheader, "VBRI", 4)) | ||
590 | { | ||
591 | VDEBUGF("-- VBRI header --\n"); | ||
592 | |||
593 | /* We want to skip the VBRI frame when playing the stream */ | ||
594 | bytecount += info->frame_size; | ||
595 | |||
596 | /* Now get the next frame to read the real info about the mp3 stream */ | ||
597 | result = get_next_header_info(fd, &bytecount, info, false); | ||
598 | if(result) | ||
599 | return result; | ||
600 | |||
601 | get_vbri_info(info, vbrheader); | ||
602 | } | ||
603 | else | ||
604 | { | ||
605 | VDEBUGF("-- No VBR header --\n"); | ||
606 | |||
607 | /* There was no VBR header found. So, we seek back to beginning and | ||
608 | * search for the first MPEG frame header of the mp3 stream. */ | ||
609 | lseek(fd, -info->frame_size, SEEK_CUR); | ||
610 | result = get_next_header_info(fd, &bytecount, info, false); | ||
611 | if(result) | ||
612 | return result; | ||
613 | } | ||
614 | |||
615 | return bytecount; | ||
616 | } | ||
617 | |||
618 | #ifndef __PCTOOL__ | ||
619 | static void long2bytes(unsigned char *buf, long val) | ||
620 | { | ||
621 | buf[0] = (val >> 24) & 0xff; | ||
622 | buf[1] = (val >> 16) & 0xff; | ||
623 | buf[2] = (val >> 8) & 0xff; | ||
624 | buf[3] = val & 0xff; | ||
625 | } | ||
626 | |||
627 | int count_mp3_frames(int fd, int startpos, int filesize, | ||
628 | void (*progressfunc)(int), | ||
629 | unsigned char* buf, size_t buflen) | ||
630 | { | ||
631 | unsigned long header = 0; | ||
632 | struct mp3info info; | ||
633 | int num_frames; | ||
634 | long bytes; | ||
635 | int cnt; | ||
636 | long progress_chunk = filesize / 50; /* Max is 50%, in 1% increments */ | ||
637 | int progress_cnt = 0; | ||
638 | bool is_vbr = false; | ||
639 | int last_bitrate = 0; | ||
640 | int header_template = 0; | ||
641 | |||
642 | if(lseek(fd, startpos, SEEK_SET) < 0) | ||
643 | return -1; | ||
644 | |||
645 | buf_init(buf, buflen); | ||
646 | |||
647 | /* Find out the total number of frames */ | ||
648 | num_frames = 0; | ||
649 | cnt = 0; | ||
650 | |||
651 | while((header = buf_find_next_frame(fd, &bytes, header_template))) { | ||
652 | mp3headerinfo(&info, header); | ||
653 | |||
654 | if(!header_template) | ||
655 | header_template = header; | ||
656 | |||
657 | /* See if this really is a VBR file */ | ||
658 | if(last_bitrate && info.bitrate != last_bitrate) | ||
659 | { | ||
660 | is_vbr = true; | ||
661 | } | ||
662 | last_bitrate = info.bitrate; | ||
663 | |||
664 | buf_seek(fd, info.frame_size-4); | ||
665 | num_frames++; | ||
666 | if(progressfunc) | ||
667 | { | ||
668 | cnt += bytes + info.frame_size; | ||
669 | if(cnt > progress_chunk) | ||
670 | { | ||
671 | progress_cnt++; | ||
672 | progressfunc(progress_cnt); | ||
673 | cnt = 0; | ||
674 | } | ||
675 | } | ||
676 | } | ||
677 | VDEBUGF("Total number of frames: %d\n", num_frames); | ||
678 | |||
679 | if(is_vbr) | ||
680 | return num_frames; | ||
681 | else | ||
682 | { | ||
683 | DEBUGF("Not a VBR file\n"); | ||
684 | return 0; | ||
685 | } | ||
686 | } | ||
687 | |||
688 | static const char cooltext[] = "Rockbox - rocks your box"; | ||
689 | |||
690 | /* buf needs to be the audio buffer with TOC generation enabled, | ||
691 | and at least MAX_XING_HEADER_SIZE bytes otherwise */ | ||
692 | int create_xing_header(int fd, long startpos, long filesize, | ||
693 | unsigned char *buf, unsigned long num_frames, | ||
694 | unsigned long rec_time, unsigned long header_template, | ||
695 | void (*progressfunc)(int), bool generate_toc, | ||
696 | unsigned char *tempbuf, size_t tempbuflen ) | ||
697 | { | ||
698 | struct mp3info info; | ||
699 | unsigned char toc[100]; | ||
700 | unsigned long header = 0; | ||
701 | unsigned long xing_header_template = header_template; | ||
702 | unsigned long filepos; | ||
703 | long pos, last_pos; | ||
704 | long j; | ||
705 | long bytes; | ||
706 | int i; | ||
707 | int index; | ||
708 | |||
709 | DEBUGF("create_xing_header()\n"); | ||
710 | |||
711 | if(generate_toc) | ||
712 | { | ||
713 | lseek(fd, startpos, SEEK_SET); | ||
714 | buf_init(tempbuf, tempbuflen); | ||
715 | |||
716 | /* Generate filepos table */ | ||
717 | last_pos = 0; | ||
718 | filepos = 0; | ||
719 | header = 0; | ||
720 | for(i = 0;i < 100;i++) { | ||
721 | /* Calculate the absolute frame number for this seek point */ | ||
722 | pos = i * num_frames / 100; | ||
723 | |||
724 | /* Advance from the last seek point to this one */ | ||
725 | for(j = 0;j < pos - last_pos;j++) | ||
726 | { | ||
727 | header = buf_find_next_frame(fd, &bytes, header_template); | ||
728 | filepos += bytes; | ||
729 | mp3headerinfo(&info, header); | ||
730 | buf_seek(fd, info.frame_size-4); | ||
731 | filepos += info.frame_size; | ||
732 | |||
733 | if(!header_template) | ||
734 | header_template = header; | ||
735 | } | ||
736 | |||
737 | /* Save a header for later use if header_template is empty. | ||
738 | We only save one header, and we want to save one in the | ||
739 | middle of the stream, just in case the first and the last | ||
740 | headers are corrupt. */ | ||
741 | if(!xing_header_template && i == 1) | ||
742 | xing_header_template = header; | ||
743 | |||
744 | if(progressfunc) | ||
745 | { | ||
746 | progressfunc(50 + i/2); | ||
747 | } | ||
748 | |||
749 | /* Fill in the TOC entry */ | ||
750 | /* each toc is a single byte indicating how many 256ths of the | ||
751 | * way through the file, is that percent of the way through the | ||
752 | * song. the easy method, filepos*256/filesize, chokes when | ||
753 | * the upper 8 bits of the file position are nonzero | ||
754 | * (i.e. files over 16mb in size). | ||
755 | */ | ||
756 | if (filepos > (ULONG_MAX/256)) | ||
757 | { | ||
758 | /* instead of multiplying filepos by 256, we divide | ||
759 | * filesize by 256. | ||
760 | */ | ||
761 | toc[i] = filepos / (filesize >> 8); | ||
762 | } | ||
763 | else | ||
764 | { | ||
765 | toc[i] = filepos * 256 / filesize; | ||
766 | } | ||
767 | |||
768 | VDEBUGF("Pos %d: %ld relpos: %ld filepos: %lx tocentry: %x\n", | ||
769 | i, pos, pos-last_pos, filepos, toc[i]); | ||
770 | |||
771 | last_pos = pos; | ||
772 | } | ||
773 | } | ||
774 | |||
775 | /* Use the template header and create a new one. | ||
776 | We ignore the Protection bit even if the rest of the stream is | ||
777 | protected. */ | ||
778 | header = xing_header_template & ~(BITRATE_MASK|PROTECTION_MASK|PADDING_MASK); | ||
779 | header |= 8 << 12; /* This gives us plenty of space, 192..576 bytes */ | ||
780 | |||
781 | if (!mp3headerinfo(&info, header)) | ||
782 | return 0; /* invalid header */ | ||
783 | |||
784 | if (num_frames == 0 && rec_time) { | ||
785 | /* estimate the number of frames based on the recording time */ | ||
786 | if (rec_time <= ULONG_MAX / info.ft_den) | ||
787 | num_frames = rec_time * info.ft_den / info.ft_num; | ||
788 | else | ||
789 | num_frames = rec_time / info.ft_num * info.ft_den; | ||
790 | } | ||
791 | |||
792 | /* Clear the frame */ | ||
793 | memset(buf, 0, MAX_XING_HEADER_SIZE); | ||
794 | |||
795 | /* Write the header to the buffer */ | ||
796 | long2bytes(buf, header); | ||
797 | |||
798 | /* Calculate position of VBR header */ | ||
799 | if (info.version == MPEG_VERSION1) { | ||
800 | if (info.channel_mode == 3) /* mono */ | ||
801 | index = 21; | ||
802 | else | ||
803 | index = 36; | ||
804 | } | ||
805 | else { | ||
806 | if (info.channel_mode == 3) /* mono */ | ||
807 | index = 13; | ||
808 | else | ||
809 | index = 21; | ||
810 | } | ||
811 | |||
812 | /* Create the Xing data */ | ||
813 | memcpy(&buf[index], "Xing", 4); | ||
814 | long2bytes(&buf[index+4], (num_frames ? VBR_FRAMES_FLAG : 0) | ||
815 | | (filesize ? VBR_BYTES_FLAG : 0) | ||
816 | | (generate_toc ? VBR_TOC_FLAG : 0)); | ||
817 | index += 8; | ||
818 | if(num_frames) | ||
819 | { | ||
820 | long2bytes(&buf[index], num_frames); | ||
821 | index += 4; | ||
822 | } | ||
823 | |||
824 | if(filesize) | ||
825 | { | ||
826 | long2bytes(&buf[index], filesize - startpos); | ||
827 | index += 4; | ||
828 | } | ||
829 | |||
830 | /* Copy the TOC */ | ||
831 | memcpy(buf + index, toc, 100); | ||
832 | |||
833 | /* And some extra cool info */ | ||
834 | memcpy(buf + index + 100, cooltext, sizeof(cooltext)); | ||
835 | |||
836 | #ifdef DEBUG | ||
837 | for(i = 0;i < info.frame_size;i++) | ||
838 | { | ||
839 | if(i && !(i % 16)) | ||
840 | DEBUGF("\n"); | ||
841 | |||
842 | DEBUGF("%02x ", buf[i]); | ||
843 | } | ||
844 | #endif | ||
845 | |||
846 | return info.frame_size; | ||
847 | } | ||
848 | |||
849 | #endif | ||
diff --git a/lib/rbcodec/metadata/mp3data.h b/lib/rbcodec/metadata/mp3data.h new file mode 100644 index 0000000000..762c2f4583 --- /dev/null +++ b/lib/rbcodec/metadata/mp3data.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _MP3DATA_H_ | ||
23 | #define _MP3DATA_H_ | ||
24 | |||
25 | #define MPEG_VERSION1 0 | ||
26 | #define MPEG_VERSION2 1 | ||
27 | #define MPEG_VERSION2_5 2 | ||
28 | |||
29 | #include <string.h> /* size_t */ | ||
30 | |||
31 | struct mp3info { | ||
32 | /* Standard MP3 frame header fields */ | ||
33 | int version; | ||
34 | int layer; | ||
35 | int bitrate; | ||
36 | long frequency; | ||
37 | int padding; | ||
38 | int channel_mode; | ||
39 | int frame_size; /* Frame size in bytes */ | ||
40 | int frame_samples;/* Samples per frame */ | ||
41 | int ft_num; /* Numerator of frametime in milliseconds */ | ||
42 | int ft_den; /* Denominator of frametime in milliseconds */ | ||
43 | |||
44 | bool is_vbr; /* True if the file is VBR */ | ||
45 | bool has_toc; /* True if there is a VBR header in the file */ | ||
46 | unsigned char toc[100]; | ||
47 | unsigned long frame_count; /* Number of frames in the file (if VBR) */ | ||
48 | unsigned long byte_count; /* File size in bytes */ | ||
49 | unsigned long file_time; /* Length of the whole file in milliseconds */ | ||
50 | int enc_delay; /* Encoder delay, fetched from LAME header */ | ||
51 | int enc_padding; /* Padded samples added to last frame. LAME header */ | ||
52 | }; | ||
53 | |||
54 | /* Xing header information */ | ||
55 | #define VBR_FRAMES_FLAG 0x01 | ||
56 | #define VBR_BYTES_FLAG 0x02 | ||
57 | #define VBR_TOC_FLAG 0x04 | ||
58 | #define VBR_QUALITY_FLAG 0x08 | ||
59 | |||
60 | #define MAX_XING_HEADER_SIZE 576 | ||
61 | |||
62 | unsigned long find_next_frame(int fd, | ||
63 | long *offset, | ||
64 | long max_offset, | ||
65 | unsigned long reference_header); | ||
66 | unsigned long mem_find_next_frame(int startpos, | ||
67 | long *offset, | ||
68 | long max_offset, | ||
69 | unsigned long reference_header, | ||
70 | unsigned char* buf, size_t buflen); | ||
71 | int get_mp3file_info(int fd, | ||
72 | struct mp3info *info); | ||
73 | |||
74 | int count_mp3_frames(int fd, int startpos, int filesize, | ||
75 | void (*progressfunc)(int), | ||
76 | unsigned char* buf, size_t buflen); | ||
77 | |||
78 | int create_xing_header(int fd, long startpos, long filesize, | ||
79 | unsigned char *buf, unsigned long num_frames, | ||
80 | unsigned long rec_time, unsigned long header_template, | ||
81 | void (*progressfunc)(int), bool generate_toc, | ||
82 | unsigned char *tempbuf, size_t tempbuflen ); | ||
83 | |||
84 | extern unsigned long bytes2int(unsigned long b0, | ||
85 | unsigned long b1, | ||
86 | unsigned long b2, | ||
87 | unsigned long b3); | ||
88 | |||
89 | #endif | ||
diff --git a/lib/rbcodec/metadata/mp4.c b/lib/rbcodec/metadata/mp4.c new file mode 100644 index 0000000000..df164436f5 --- /dev/null +++ b/lib/rbcodec/metadata/mp4.c | |||
@@ -0,0 +1,842 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Magnus Holmgren | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "errno.h" | ||
29 | #include "metadata.h" | ||
30 | #include "metadata_common.h" | ||
31 | #include "metadata_parsers.h" | ||
32 | #include "logf.h" | ||
33 | #include "debug.h" | ||
34 | #include "replaygain.h" | ||
35 | |||
36 | #ifdef DEBUGF | ||
37 | #undef DEBUGF | ||
38 | #define DEBUGF(...) | ||
39 | #endif | ||
40 | |||
41 | #define MP4_3gp6 FOURCC('3', 'g', 'p', '6') | ||
42 | #define MP4_aART FOURCC('a', 'A', 'R', 'T') | ||
43 | #define MP4_alac FOURCC('a', 'l', 'a', 'c') | ||
44 | #define MP4_calb FOURCC(0xa9, 'a', 'l', 'b') | ||
45 | #define MP4_cART FOURCC(0xa9, 'A', 'R', 'T') | ||
46 | #define MP4_cgrp FOURCC(0xa9, 'g', 'r', 'p') | ||
47 | #define MP4_cgen FOURCC(0xa9, 'g', 'e', 'n') | ||
48 | #define MP4_chpl FOURCC('c', 'h', 'p', 'l') | ||
49 | #define MP4_cnam FOURCC(0xa9, 'n', 'a', 'm') | ||
50 | #define MP4_cwrt FOURCC(0xa9, 'w', 'r', 't') | ||
51 | #define MP4_ccmt FOURCC(0xa9, 'c', 'm', 't') | ||
52 | #define MP4_cday FOURCC(0xa9, 'd', 'a', 'y') | ||
53 | #define MP4_covr FOURCC('c', 'o', 'v', 'r') | ||
54 | #define MP4_disk FOURCC('d', 'i', 's', 'k') | ||
55 | #define MP4_esds FOURCC('e', 's', 'd', 's') | ||
56 | #define MP4_ftyp FOURCC('f', 't', 'y', 'p') | ||
57 | #define MP4_gnre FOURCC('g', 'n', 'r', 'e') | ||
58 | #define MP4_hdlr FOURCC('h', 'd', 'l', 'r') | ||
59 | #define MP4_ilst FOURCC('i', 'l', 's', 't') | ||
60 | #define MP4_isom FOURCC('i', 's', 'o', 'm') | ||
61 | #define MP4_M4A FOURCC('M', '4', 'A', ' ') | ||
62 | #define MP4_m4a FOURCC('m', '4', 'a', ' ') /*technically its "M4A "*/ | ||
63 | #define MP4_M4B FOURCC('M', '4', 'B', ' ') /*but files exist with lower case*/ | ||
64 | #define MP4_mdat FOURCC('m', 'd', 'a', 't') | ||
65 | #define MP4_mdia FOURCC('m', 'd', 'i', 'a') | ||
66 | #define MP4_mdir FOURCC('m', 'd', 'i', 'r') | ||
67 | #define MP4_meta FOURCC('m', 'e', 't', 'a') | ||
68 | #define MP4_minf FOURCC('m', 'i', 'n', 'f') | ||
69 | #define MP4_moov FOURCC('m', 'o', 'o', 'v') | ||
70 | #define MP4_mp4a FOURCC('m', 'p', '4', 'a') | ||
71 | #define MP4_mp42 FOURCC('m', 'p', '4', '2') | ||
72 | #define MP4_qt FOURCC('q', 't', ' ', ' ') | ||
73 | #define MP4_soun FOURCC('s', 'o', 'u', 'n') | ||
74 | #define MP4_stbl FOURCC('s', 't', 'b', 'l') | ||
75 | #define MP4_stsd FOURCC('s', 't', 's', 'd') | ||
76 | #define MP4_stts FOURCC('s', 't', 't', 's') | ||
77 | #define MP4_trak FOURCC('t', 'r', 'a', 'k') | ||
78 | #define MP4_trkn FOURCC('t', 'r', 'k', 'n') | ||
79 | #define MP4_udta FOURCC('u', 'd', 't', 'a') | ||
80 | #define MP4_extra FOURCC('-', '-', '-', '-') | ||
81 | |||
82 | /* Read the tag data from an MP4 file, storing up to buffer_size bytes in | ||
83 | * buffer. | ||
84 | */ | ||
85 | static unsigned long read_mp4_tag(int fd, unsigned int size_left, char* buffer, | ||
86 | unsigned int buffer_left) | ||
87 | { | ||
88 | unsigned int bytes_read = 0; | ||
89 | |||
90 | if (buffer_left == 0) | ||
91 | { | ||
92 | lseek(fd, size_left, SEEK_CUR); /* Skip everything */ | ||
93 | } | ||
94 | else | ||
95 | { | ||
96 | /* Skip the data tag header - maybe we should parse it properly? */ | ||
97 | lseek(fd, 16, SEEK_CUR); | ||
98 | size_left -= 16; | ||
99 | |||
100 | if (size_left > buffer_left) | ||
101 | { | ||
102 | read(fd, buffer, buffer_left); | ||
103 | lseek(fd, size_left - buffer_left, SEEK_CUR); | ||
104 | bytes_read = buffer_left; | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | read(fd, buffer, size_left); | ||
109 | bytes_read = size_left; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return bytes_read; | ||
114 | } | ||
115 | |||
116 | /* Read a string tag from an MP4 file */ | ||
117 | static unsigned int read_mp4_tag_string(int fd, int size_left, char** buffer, | ||
118 | unsigned int* buffer_left, char** dest) | ||
119 | { | ||
120 | unsigned int bytes_read = read_mp4_tag(fd, size_left, *buffer, | ||
121 | *buffer_left > 0 ? *buffer_left - 1 : 0); | ||
122 | unsigned int length = 0; | ||
123 | |||
124 | if (bytes_read) | ||
125 | { | ||
126 | /* Do not overwrite already available metadata. Especially when reading | ||
127 | * tags with e.g. multiple genres / artists. This way only the first | ||
128 | * of multiple entries is used, all following are dropped. */ | ||
129 | if (*dest == NULL) | ||
130 | { | ||
131 | (*buffer)[bytes_read] = 0; /* zero-terminate for correct strlen().*/ | ||
132 | length = strlen(*buffer) + 1; | ||
133 | length = MIN(length, ID3V2_MAX_ITEM_SIZE); /* Limit item size. */ | ||
134 | |||
135 | *dest = *buffer; | ||
136 | (*buffer)[length-1] = 0; /* zero-terminate buffer. */ | ||
137 | *buffer_left -= length; | ||
138 | *buffer += length; | ||
139 | } | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | *dest = NULL; | ||
144 | } | ||
145 | |||
146 | return length; | ||
147 | } | ||
148 | |||
149 | static unsigned int read_mp4_atom(int fd, uint32_t* size, | ||
150 | uint32_t* type, uint32_t size_left) | ||
151 | { | ||
152 | read_uint32be(fd, size); | ||
153 | read_uint32be(fd, type); | ||
154 | |||
155 | if (*size == 1) | ||
156 | { | ||
157 | /* FAT32 doesn't support files this big, so something seems to | ||
158 | * be wrong. (64-bit sizes should only be used when required.) | ||
159 | */ | ||
160 | errno = EFBIG; | ||
161 | *type = 0; | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | if (*size > 0) | ||
166 | { | ||
167 | if (*size > size_left) | ||
168 | { | ||
169 | size_left = 0; | ||
170 | } | ||
171 | else | ||
172 | { | ||
173 | size_left -= *size; | ||
174 | } | ||
175 | |||
176 | *size -= 8; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | *size = size_left; | ||
181 | size_left = 0; | ||
182 | } | ||
183 | |||
184 | return size_left; | ||
185 | } | ||
186 | |||
187 | static unsigned int read_mp4_length(int fd, uint32_t* size) | ||
188 | { | ||
189 | unsigned int length = 0; | ||
190 | int bytes = 0; | ||
191 | unsigned char c; | ||
192 | |||
193 | do | ||
194 | { | ||
195 | read(fd, &c, 1); | ||
196 | bytes++; | ||
197 | (*size)--; | ||
198 | length = (length << 7) | (c & 0x7F); | ||
199 | } | ||
200 | while ((c & 0x80) && (bytes < 4) && (*size > 0)); | ||
201 | |||
202 | return length; | ||
203 | } | ||
204 | |||
205 | static bool read_mp4_esds(int fd, struct mp3entry* id3, uint32_t* size) | ||
206 | { | ||
207 | unsigned char buf[8]; | ||
208 | bool sbr = false; | ||
209 | |||
210 | lseek(fd, 4, SEEK_CUR); /* Version and flags. */ | ||
211 | read(fd, buf, 1); /* Verify ES_DescrTag. */ | ||
212 | *size -= 5; | ||
213 | |||
214 | if (*buf == 3) | ||
215 | { | ||
216 | /* read length */ | ||
217 | if (read_mp4_length(fd, size) < 20) | ||
218 | { | ||
219 | return sbr; | ||
220 | } | ||
221 | |||
222 | lseek(fd, 3, SEEK_CUR); | ||
223 | *size -= 3; | ||
224 | } | ||
225 | else | ||
226 | { | ||
227 | lseek(fd, 2, SEEK_CUR); | ||
228 | *size -= 2; | ||
229 | } | ||
230 | |||
231 | read(fd, buf, 1); /* Verify DecoderConfigDescrTab. */ | ||
232 | *size -= 1; | ||
233 | |||
234 | if (*buf != 4) | ||
235 | { | ||
236 | return sbr; | ||
237 | } | ||
238 | |||
239 | if (read_mp4_length(fd, size) < 13) | ||
240 | { | ||
241 | return sbr; | ||
242 | } | ||
243 | |||
244 | lseek(fd, 13, SEEK_CUR); /* Skip audio type, bit rates, etc. */ | ||
245 | read(fd, buf, 1); | ||
246 | *size -= 14; | ||
247 | |||
248 | if (*buf != 5) /* Verify DecSpecificInfoTag. */ | ||
249 | { | ||
250 | return sbr; | ||
251 | } | ||
252 | |||
253 | { | ||
254 | static const int sample_rates[] = | ||
255 | { | ||
256 | 96000, 88200, 64000, 48000, 44100, 32000, | ||
257 | 24000, 22050, 16000, 12000, 11025, 8000 | ||
258 | }; | ||
259 | unsigned long bits; | ||
260 | unsigned int length; | ||
261 | unsigned int index; | ||
262 | unsigned int type; | ||
263 | |||
264 | /* Read the (leading part of the) decoder config. */ | ||
265 | length = read_mp4_length(fd, size); | ||
266 | length = MIN(length, *size); | ||
267 | length = MIN(length, sizeof(buf)); | ||
268 | memset(buf, 0, sizeof(buf)); | ||
269 | read(fd, buf, length); | ||
270 | *size -= length; | ||
271 | |||
272 | /* Maybe time to write a simple read_bits function... */ | ||
273 | |||
274 | /* Decoder config format: | ||
275 | * Object type - 5 bits | ||
276 | * Frequency index - 4 bits | ||
277 | * Channel configuration - 4 bits | ||
278 | */ | ||
279 | bits = get_long_be(buf); | ||
280 | type = bits >> 27; /* Object type - 5 bits */ | ||
281 | index = (bits >> 23) & 0xf; /* Frequency index - 4 bits */ | ||
282 | |||
283 | if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
284 | { | ||
285 | id3->frequency = sample_rates[index]; | ||
286 | } | ||
287 | |||
288 | if (type == 5) | ||
289 | { | ||
290 | unsigned int old_index = index; | ||
291 | |||
292 | sbr = true; | ||
293 | index = (bits >> 15) & 0xf; /* Frequency index - 4 bits */ | ||
294 | |||
295 | if (index == 15) | ||
296 | { | ||
297 | /* 17 bits read so far... */ | ||
298 | bits = get_long_be(&buf[2]); | ||
299 | id3->frequency = (bits >> 7) & 0x00ffffff; | ||
300 | } | ||
301 | else if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
302 | { | ||
303 | id3->frequency = sample_rates[index]; | ||
304 | } | ||
305 | |||
306 | if (old_index == index) | ||
307 | { | ||
308 | /* Downsampled SBR */ | ||
309 | id3->frequency *= 2; | ||
310 | } | ||
311 | } | ||
312 | /* Skip 13 bits from above, plus 3 bits, then read 11 bits */ | ||
313 | else if ((length >= 4) && (((bits >> 5) & 0x7ff) == 0x2b7)) | ||
314 | { | ||
315 | /* We found an extensionAudioObjectType */ | ||
316 | type = bits & 0x1f; /* Object type - 5 bits*/ | ||
317 | bits = get_long_be(&buf[4]); | ||
318 | |||
319 | if (type == 5) | ||
320 | { | ||
321 | sbr = bits >> 31; | ||
322 | |||
323 | if (sbr) | ||
324 | { | ||
325 | unsigned int old_index = index; | ||
326 | |||
327 | /* 1 bit read so far */ | ||
328 | index = (bits >> 27) & 0xf; /* Frequency index - 4 bits */ | ||
329 | |||
330 | if (index == 15) | ||
331 | { | ||
332 | /* 5 bits read so far */ | ||
333 | id3->frequency = (bits >> 3) & 0x00ffffff; | ||
334 | } | ||
335 | else if (index < (sizeof(sample_rates) / sizeof(*sample_rates))) | ||
336 | { | ||
337 | id3->frequency = sample_rates[index]; | ||
338 | } | ||
339 | |||
340 | if (old_index == index) | ||
341 | { | ||
342 | /* Downsampled SBR */ | ||
343 | id3->frequency *= 2; | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | if (!sbr && (id3->frequency <= 24000) && (length <= 2)) | ||
350 | { | ||
351 | /* Double the frequency for low-frequency files without a "long" | ||
352 | * DecSpecificConfig header. The file may or may not contain SBR, | ||
353 | * but here we guess it does if the header is short. This can | ||
354 | * fail on some files, but it's the best we can do, short of | ||
355 | * decoding (parts of) the file. | ||
356 | */ | ||
357 | id3->frequency *= 2; | ||
358 | sbr = true; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | return sbr; | ||
363 | } | ||
364 | |||
365 | static bool read_mp4_tags(int fd, struct mp3entry* id3, | ||
366 | uint32_t size_left) | ||
367 | { | ||
368 | uint32_t size; | ||
369 | uint32_t type; | ||
370 | unsigned int buffer_left = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); | ||
371 | char* buffer = id3->id3v2buf; | ||
372 | bool cwrt = false; | ||
373 | |||
374 | do | ||
375 | { | ||
376 | size_left = read_mp4_atom(fd, &size, &type, size_left); | ||
377 | |||
378 | /* DEBUGF("Tag atom: '%c%c%c%c' (%d bytes left)\n", type >> 24 & 0xff, | ||
379 | type >> 16 & 0xff, type >> 8 & 0xff, type & 0xff, size); */ | ||
380 | |||
381 | switch (type) | ||
382 | { | ||
383 | case MP4_cnam: | ||
384 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
385 | &id3->title); | ||
386 | break; | ||
387 | |||
388 | case MP4_cART: | ||
389 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
390 | &id3->artist); | ||
391 | break; | ||
392 | |||
393 | case MP4_aART: | ||
394 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
395 | &id3->albumartist); | ||
396 | break; | ||
397 | |||
398 | case MP4_cgrp: | ||
399 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
400 | &id3->grouping); | ||
401 | break; | ||
402 | |||
403 | case MP4_calb: | ||
404 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
405 | &id3->album); | ||
406 | break; | ||
407 | |||
408 | case MP4_cwrt: | ||
409 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
410 | &id3->composer); | ||
411 | cwrt = false; | ||
412 | break; | ||
413 | |||
414 | case MP4_ccmt: | ||
415 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
416 | &id3->comment); | ||
417 | break; | ||
418 | |||
419 | case MP4_cday: | ||
420 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
421 | &id3->year_string); | ||
422 | |||
423 | /* Try to parse it as a year, for the benefit of the database. | ||
424 | */ | ||
425 | if(id3->year_string) | ||
426 | { | ||
427 | id3->year = atoi(id3->year_string); | ||
428 | if (id3->year < 1900) | ||
429 | { | ||
430 | id3->year = 0; | ||
431 | } | ||
432 | } | ||
433 | else | ||
434 | id3->year = 0; | ||
435 | |||
436 | break; | ||
437 | |||
438 | case MP4_gnre: | ||
439 | { | ||
440 | unsigned short genre; | ||
441 | |||
442 | read_mp4_tag(fd, size, (char*) &genre, sizeof(genre)); | ||
443 | id3->genre_string = id3_get_num_genre(betoh16(genre) - 1); | ||
444 | } | ||
445 | break; | ||
446 | |||
447 | case MP4_cgen: | ||
448 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
449 | &id3->genre_string); | ||
450 | break; | ||
451 | |||
452 | case MP4_disk: | ||
453 | { | ||
454 | unsigned short n[2]; | ||
455 | |||
456 | read_mp4_tag(fd, size, (char*) &n, sizeof(n)); | ||
457 | id3->discnum = betoh16(n[1]); | ||
458 | } | ||
459 | break; | ||
460 | |||
461 | case MP4_trkn: | ||
462 | { | ||
463 | unsigned short n[2]; | ||
464 | |||
465 | read_mp4_tag(fd, size, (char*) &n, sizeof(n)); | ||
466 | id3->tracknum = betoh16(n[1]); | ||
467 | } | ||
468 | break; | ||
469 | |||
470 | #ifdef HAVE_ALBUMART | ||
471 | case MP4_covr: | ||
472 | { | ||
473 | int pos = lseek(fd, 0, SEEK_CUR) + 16; | ||
474 | |||
475 | read_mp4_tag(fd, size, buffer, 8); | ||
476 | id3->albumart.type = AA_TYPE_UNKNOWN; | ||
477 | if (memcmp(buffer, "\xff\xd8\xff\xe0", 4) == 0) | ||
478 | { | ||
479 | id3->albumart.type = AA_TYPE_JPG; | ||
480 | } | ||
481 | else if (memcmp(buffer, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0) | ||
482 | { | ||
483 | id3->albumart.type = AA_TYPE_PNG; | ||
484 | } | ||
485 | |||
486 | if (id3->albumart.type != AA_TYPE_UNKNOWN) | ||
487 | { | ||
488 | id3->albumart.pos = pos; | ||
489 | id3->albumart.size = size - 16; | ||
490 | id3->has_embedded_albumart = true; | ||
491 | } | ||
492 | } | ||
493 | break; | ||
494 | #endif | ||
495 | |||
496 | case MP4_extra: | ||
497 | { | ||
498 | char tag_name[TAG_NAME_LENGTH]; | ||
499 | uint32_t sub_size; | ||
500 | |||
501 | /* "mean" atom */ | ||
502 | read_uint32be(fd, &sub_size); | ||
503 | size -= sub_size; | ||
504 | lseek(fd, sub_size - 4, SEEK_CUR); | ||
505 | /* "name" atom */ | ||
506 | read_uint32be(fd, &sub_size); | ||
507 | size -= sub_size; | ||
508 | lseek(fd, 8, SEEK_CUR); | ||
509 | sub_size -= 12; | ||
510 | |||
511 | if (sub_size > sizeof(tag_name) - 1) | ||
512 | { | ||
513 | read(fd, tag_name, sizeof(tag_name) - 1); | ||
514 | lseek(fd, sub_size - (sizeof(tag_name) - 1), SEEK_CUR); | ||
515 | tag_name[sizeof(tag_name) - 1] = 0; | ||
516 | } | ||
517 | else | ||
518 | { | ||
519 | read(fd, tag_name, sub_size); | ||
520 | tag_name[sub_size] = 0; | ||
521 | } | ||
522 | |||
523 | if ((strcasecmp(tag_name, "composer") == 0) && !cwrt) | ||
524 | { | ||
525 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
526 | &id3->composer); | ||
527 | } | ||
528 | else if (strcasecmp(tag_name, "iTunSMPB") == 0) | ||
529 | { | ||
530 | char value[TAG_VALUE_LENGTH]; | ||
531 | char* value_p = value; | ||
532 | char* any; | ||
533 | unsigned int length = sizeof(value); | ||
534 | |||
535 | read_mp4_tag_string(fd, size, &value_p, &length, &any); | ||
536 | id3->lead_trim = get_itunes_int32(value, 1); | ||
537 | id3->tail_trim = get_itunes_int32(value, 2); | ||
538 | DEBUGF("AAC: lead_trim %d, tail_trim %d\n", | ||
539 | id3->lead_trim, id3->tail_trim); | ||
540 | } | ||
541 | else if (strcasecmp(tag_name, "musicbrainz track id") == 0) | ||
542 | { | ||
543 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
544 | &id3->mb_track_id); | ||
545 | } | ||
546 | else if ((strcasecmp(tag_name, "album artist") == 0)) | ||
547 | { | ||
548 | read_mp4_tag_string(fd, size, &buffer, &buffer_left, | ||
549 | &id3->albumartist); | ||
550 | } | ||
551 | else | ||
552 | { | ||
553 | char* any = NULL; | ||
554 | unsigned int length = read_mp4_tag_string(fd, size, | ||
555 | &buffer, &buffer_left, &any); | ||
556 | |||
557 | if (length > 0) | ||
558 | { | ||
559 | /* Re-use the read buffer as the dest buffer... */ | ||
560 | buffer -= length; | ||
561 | buffer_left += length; | ||
562 | |||
563 | parse_replaygain(tag_name, buffer, id3); | ||
564 | } | ||
565 | } | ||
566 | } | ||
567 | break; | ||
568 | |||
569 | default: | ||
570 | lseek(fd, size, SEEK_CUR); | ||
571 | break; | ||
572 | } | ||
573 | } | ||
574 | while ((size_left > 0) && (errno == 0)); | ||
575 | |||
576 | return true; | ||
577 | } | ||
578 | |||
579 | static bool read_mp4_container(int fd, struct mp3entry* id3, | ||
580 | uint32_t size_left) | ||
581 | { | ||
582 | uint32_t size = 0; | ||
583 | uint32_t type = 0; | ||
584 | uint32_t handler = 0; | ||
585 | bool rc = true; | ||
586 | bool done = false; | ||
587 | |||
588 | do | ||
589 | { | ||
590 | size_left = read_mp4_atom(fd, &size, &type, size_left); | ||
591 | |||
592 | /* DEBUGF("Atom: '%c%c%c%c' (0x%08lx, %lu bytes left)\n", | ||
593 | (int) ((type >> 24) & 0xff), (int) ((type >> 16) & 0xff), | ||
594 | (int) ((type >> 8) & 0xff), (int) (type & 0xff), | ||
595 | type, size); */ | ||
596 | |||
597 | switch (type) | ||
598 | { | ||
599 | case MP4_ftyp: | ||
600 | { | ||
601 | uint32_t id; | ||
602 | |||
603 | read_uint32be(fd, &id); | ||
604 | size -= 4; | ||
605 | |||
606 | if ((id != MP4_M4A) && (id != MP4_M4B) && (id != MP4_mp42) | ||
607 | && (id != MP4_qt) && (id != MP4_3gp6) && (id != MP4_m4a) | ||
608 | && (id != MP4_isom)) | ||
609 | { | ||
610 | DEBUGF("Unknown MP4 file type: '%c%c%c%c'\n", | ||
611 | (int)(id >> 24 & 0xff), (int)(id >> 16 & 0xff), | ||
612 | (int)(id >> 8 & 0xff), (int)(id & 0xff)); | ||
613 | return false; | ||
614 | } | ||
615 | } | ||
616 | break; | ||
617 | |||
618 | case MP4_meta: | ||
619 | lseek(fd, 4, SEEK_CUR); /* Skip version */ | ||
620 | size -= 4; | ||
621 | /* Fall through */ | ||
622 | |||
623 | case MP4_moov: | ||
624 | case MP4_udta: | ||
625 | case MP4_mdia: | ||
626 | case MP4_stbl: | ||
627 | case MP4_trak: | ||
628 | rc = read_mp4_container(fd, id3, size); | ||
629 | size = 0; | ||
630 | break; | ||
631 | |||
632 | case MP4_ilst: | ||
633 | /* We need at least a size of 8 to read the next atom. */ | ||
634 | if (handler == MP4_mdir && size>8) | ||
635 | { | ||
636 | rc = read_mp4_tags(fd, id3, size); | ||
637 | size = 0; | ||
638 | } | ||
639 | break; | ||
640 | |||
641 | case MP4_minf: | ||
642 | if (handler == MP4_soun) | ||
643 | { | ||
644 | rc = read_mp4_container(fd, id3, size); | ||
645 | size = 0; | ||
646 | } | ||
647 | break; | ||
648 | |||
649 | case MP4_stsd: | ||
650 | lseek(fd, 8, SEEK_CUR); | ||
651 | size -= 8; | ||
652 | rc = read_mp4_container(fd, id3, size); | ||
653 | size = 0; | ||
654 | break; | ||
655 | |||
656 | case MP4_hdlr: | ||
657 | lseek(fd, 8, SEEK_CUR); | ||
658 | read_uint32be(fd, &handler); | ||
659 | size -= 12; | ||
660 | /* DEBUGF(" Handler '%c%c%c%c'\n", handler >> 24 & 0xff, | ||
661 | handler >> 16 & 0xff, handler >> 8 & 0xff,handler & 0xff); */ | ||
662 | break; | ||
663 | |||
664 | case MP4_stts: | ||
665 | { | ||
666 | uint32_t entries; | ||
667 | unsigned int i; | ||
668 | |||
669 | /* Reset to false. */ | ||
670 | id3->needs_upsampling_correction = false; | ||
671 | |||
672 | lseek(fd, 4, SEEK_CUR); | ||
673 | read_uint32be(fd, &entries); | ||
674 | id3->samples = 0; | ||
675 | |||
676 | for (i = 0; i < entries; i++) | ||
677 | { | ||
678 | uint32_t n; | ||
679 | uint32_t l; | ||
680 | |||
681 | read_uint32be(fd, &n); | ||
682 | read_uint32be(fd, &l); | ||
683 | |||
684 | /* Some AAC file use HE profile. In this case the number | ||
685 | * of output samples is doubled to a maximum of 2048 | ||
686 | * samples per frame. This means that files which already | ||
687 | * report a frame size of 2048 in their header will not | ||
688 | * need any further special handling. */ | ||
689 | if (id3->codectype==AFMT_MP4_AAC_HE && l<=1024) | ||
690 | { | ||
691 | id3->samples += n * l * 2; | ||
692 | id3->needs_upsampling_correction = true; | ||
693 | } | ||
694 | else | ||
695 | { | ||
696 | id3->samples += n * l; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | size = 0; | ||
701 | } | ||
702 | break; | ||
703 | |||
704 | case MP4_mp4a: | ||
705 | { | ||
706 | uint32_t subsize; | ||
707 | uint32_t subtype; | ||
708 | |||
709 | /* Move to the next expected mp4 atom. */ | ||
710 | lseek(fd, 28, SEEK_CUR); | ||
711 | read_mp4_atom(fd, &subsize, &subtype, size); | ||
712 | size -= 36; | ||
713 | |||
714 | if (subtype == MP4_esds) | ||
715 | { | ||
716 | /* Read esds metadata and return if AAC-HE/SBR is used. */ | ||
717 | if (read_mp4_esds(fd, id3, &size)) | ||
718 | id3->codectype = AFMT_MP4_AAC_HE; | ||
719 | else | ||
720 | id3->codectype = AFMT_MP4_AAC; | ||
721 | } | ||
722 | } | ||
723 | break; | ||
724 | |||
725 | case MP4_alac: | ||
726 | { | ||
727 | uint32_t frequency; | ||
728 | uint32_t subsize; | ||
729 | uint32_t subtype; | ||
730 | |||
731 | /* Move to the next expected mp4 atom. */ | ||
732 | lseek(fd, 28, SEEK_CUR); | ||
733 | read_mp4_atom(fd, &subsize, &subtype, size); | ||
734 | size -= 36; | ||
735 | #if 0 | ||
736 | /* We might need to parse for the alac metadata atom. */ | ||
737 | while (!((subsize==28) && (subtype==MP4_alac)) && (size>0)) | ||
738 | { | ||
739 | lseek(fd, -7, SEEK_CUR); | ||
740 | read_mp4_atom(fd, &subsize, &subtype, size); | ||
741 | size -= 1; | ||
742 | errno = 0; /* will most likely be set while parsing */ | ||
743 | } | ||
744 | #endif | ||
745 | if (subtype == MP4_alac) | ||
746 | { | ||
747 | lseek(fd, 24, SEEK_CUR); | ||
748 | read_uint32be(fd, &frequency); | ||
749 | size -= 28; | ||
750 | id3->frequency = frequency; | ||
751 | id3->codectype = AFMT_MP4_ALAC; | ||
752 | } | ||
753 | } | ||
754 | break; | ||
755 | |||
756 | case MP4_mdat: | ||
757 | /* Some AAC files appear to contain additional empty mdat chunks. | ||
758 | Ignore them. */ | ||
759 | if(size == 0) | ||
760 | break; | ||
761 | id3->filesize = size; | ||
762 | if(id3->samples > 0) { | ||
763 | /* We've already seen the moov chunk. */ | ||
764 | done = true; | ||
765 | } | ||
766 | break; | ||
767 | |||
768 | case MP4_chpl: | ||
769 | { | ||
770 | /* ADDME: add support for real chapters. Right now it's only | ||
771 | * used for Nero's gapless hack */ | ||
772 | uint8_t chapters; | ||
773 | uint64_t timestamp; | ||
774 | |||
775 | lseek(fd, 8, SEEK_CUR); | ||
776 | read_uint8(fd, &chapters); | ||
777 | size -= 9; | ||
778 | |||
779 | /* the first chapter will be used as the lead_trim */ | ||
780 | if (chapters > 0) { | ||
781 | read_uint64be(fd, ×tamp); | ||
782 | id3->lead_trim = (timestamp * id3->frequency) / 10000000; | ||
783 | size -= 8; | ||
784 | } | ||
785 | } | ||
786 | break; | ||
787 | |||
788 | default: | ||
789 | break; | ||
790 | } | ||
791 | |||
792 | /* Skip final seek. */ | ||
793 | if (!done) | ||
794 | { | ||
795 | lseek(fd, size, SEEK_CUR); | ||
796 | } | ||
797 | } while (rc && (size_left > 0) && (errno == 0) && !done); | ||
798 | |||
799 | return rc; | ||
800 | } | ||
801 | |||
802 | bool get_mp4_metadata(int fd, struct mp3entry* id3) | ||
803 | { | ||
804 | id3->codectype = AFMT_UNKNOWN; | ||
805 | id3->filesize = 0; | ||
806 | errno = 0; | ||
807 | |||
808 | if (read_mp4_container(fd, id3, filesize(fd)) && (errno == 0) | ||
809 | && (id3->samples > 0) && (id3->frequency > 0) | ||
810 | && (id3->filesize > 0)) | ||
811 | { | ||
812 | if (id3->codectype == AFMT_UNKNOWN) | ||
813 | { | ||
814 | logf("Not an ALAC or AAC file"); | ||
815 | return false; | ||
816 | } | ||
817 | |||
818 | id3->length = ((int64_t) id3->samples * 1000) / id3->frequency; | ||
819 | |||
820 | id3->vbr = true; /* ALAC is native VBR, AAC very unlikely is CBR. */ | ||
821 | |||
822 | if (id3->length <= 0) | ||
823 | { | ||
824 | logf("mp4 length invalid!"); | ||
825 | return false; | ||
826 | } | ||
827 | |||
828 | id3->bitrate = ((int64_t) id3->filesize * 8) / id3->length; | ||
829 | DEBUGF("MP4 bitrate %d, frequency %ld Hz, length %ld ms\n", | ||
830 | id3->bitrate, id3->frequency, id3->length); | ||
831 | } | ||
832 | else | ||
833 | { | ||
834 | logf("MP4 metadata error"); | ||
835 | DEBUGF("MP4 metadata error. errno %d, samples %ld, frequency %ld, " | ||
836 | "filesize %ld\n", errno, id3->samples, id3->frequency, | ||
837 | id3->filesize); | ||
838 | return false; | ||
839 | } | ||
840 | |||
841 | return true; | ||
842 | } | ||
diff --git a/lib/rbcodec/metadata/mpc.c b/lib/rbcodec/metadata/mpc.c new file mode 100644 index 0000000000..0b75ed04dd --- /dev/null +++ b/lib/rbcodec/metadata/mpc.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Thom Johansen | ||
11 | * Copyright (C) 2010 Andree Buschmann | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | #include <string.h> | ||
24 | #include <stdio.h> | ||
25 | #include <inttypes.h> | ||
26 | #include "system.h" | ||
27 | #include "metadata.h" | ||
28 | #include "metadata_common.h" | ||
29 | #include "metadata_parsers.h" | ||
30 | #include "logf.h" | ||
31 | #include "replaygain.h" | ||
32 | #include "fixedpoint.h" | ||
33 | |||
34 | /* Needed for replay gain and clipping prevention of SV8 files. */ | ||
35 | #define SV8_TO_SV7_CONVERT_GAIN (6482) /* 64.82 * 100, MPC_OLD_GAIN_REF */ | ||
36 | #define SV8_TO_SV7_CONVERT_PEAK (23119) /* 256 * 20 * log10(32768) */ | ||
37 | |||
38 | static int set_replaygain_sv7(struct mp3entry* id3, | ||
39 | bool album, | ||
40 | long value, | ||
41 | long used) | ||
42 | { | ||
43 | long gain = (int16_t) ((value >> 16) & 0xffff); | ||
44 | long peak = (uint16_t) (value & 0xffff); | ||
45 | |||
46 | /* We use a peak value of 0 to indicate a given gain type isn't used. */ | ||
47 | if (peak != 0) { | ||
48 | /* Save the ReplayGain data to id3-structure for further processing. */ | ||
49 | parse_replaygain_int(album, gain * 512 / 100, peak << 9, id3); | ||
50 | } | ||
51 | |||
52 | return used; | ||
53 | } | ||
54 | |||
55 | static int set_replaygain_sv8(struct mp3entry* id3, | ||
56 | bool album, | ||
57 | long gain, | ||
58 | long peak, | ||
59 | long used) | ||
60 | { | ||
61 | gain = (long)(SV8_TO_SV7_CONVERT_GAIN - ((gain*100)/256)); | ||
62 | |||
63 | /* Transform SV8's logarithmic peak representation to the desired linear | ||
64 | * representation: linear = pow(10, peak/256/20). | ||
65 | * | ||
66 | * FP_BITS = 24 bits = desired fp representation for dsp routines | ||
67 | * FRAC_BITS = 12 bits = resolution used for fp_bits | ||
68 | * fp_factor(peak*(1<<FRAC_BITS)/256, FRAC_BITS) << (FP_BITS-FRAC_BITS) | ||
69 | **/ | ||
70 | peak = (fp_factor((peak-SV8_TO_SV7_CONVERT_PEAK)*16, 12) << 12); | ||
71 | |||
72 | /* We use a peak value of 0 to indicate a given gain type isn't used. */ | ||
73 | if (peak != 0) { | ||
74 | /* Save the ReplayGain data to id3-structure for further processing. */ | ||
75 | parse_replaygain_int(album, gain * 512 / 100, peak, id3); | ||
76 | } | ||
77 | |||
78 | return used; | ||
79 | } | ||
80 | |||
81 | static int sv8_get_size(uint8_t *buffer, int index, uint64_t *p_size) | ||
82 | { | ||
83 | unsigned char tmp; | ||
84 | uint64_t size = 0; | ||
85 | |||
86 | do { | ||
87 | tmp = buffer[index++]; | ||
88 | size = (size << 7) | (tmp & 0x7F); | ||
89 | } while((tmp & 0x80)); | ||
90 | |||
91 | *p_size = size; | ||
92 | return index; | ||
93 | } | ||
94 | |||
95 | bool get_musepack_metadata(int fd, struct mp3entry *id3) | ||
96 | { | ||
97 | static const int32_t sfreqs[4] = { 44100, 48000, 37800, 32000 }; | ||
98 | uint32_t header[8]; | ||
99 | uint64_t samples = 0; | ||
100 | int i; | ||
101 | |||
102 | if (!skip_id3v2(fd, id3)) | ||
103 | return false; | ||
104 | if (read(fd, header, 4*8) != 4*8) return false; | ||
105 | /* Musepack files are little endian, might need swapping */ | ||
106 | for (i = 1; i < 8; i++) | ||
107 | header[i] = letoh32(header[i]); | ||
108 | if (!memcmp(header, "MP+", 3)) { /* Compare to sig "MP+" */ | ||
109 | unsigned int streamversion; | ||
110 | header[0] = letoh32(header[0]); | ||
111 | streamversion = (header[0] >> 24) & 15; | ||
112 | if (streamversion == 7) { | ||
113 | unsigned int gapless = (header[5] >> 31) & 0x0001; | ||
114 | unsigned int last_frame_samples = (header[5] >> 20) & 0x07ff; | ||
115 | unsigned int bufused = 0; | ||
116 | |||
117 | id3->frequency = sfreqs[(header[2] >> 16) & 0x0003]; | ||
118 | samples = (uint64_t)header[1]*1152; /* 1152 is mpc frame size */ | ||
119 | if (gapless) | ||
120 | samples -= 1152 - last_frame_samples; | ||
121 | else | ||
122 | samples -= 481; /* Musepack subband synth filter delay */ | ||
123 | |||
124 | bufused = set_replaygain_sv7(id3, false, header[3], bufused); | ||
125 | bufused = set_replaygain_sv7(id3, true , header[4], bufused); | ||
126 | |||
127 | id3->codectype = AFMT_MPC_SV7; | ||
128 | } else { | ||
129 | return false; /* only SV7 is allowed within a "MP+" signature */ | ||
130 | } | ||
131 | } else if (!memcmp(header, "MPCK", 4)) { /* Compare to sig "MPCK" */ | ||
132 | uint8_t sv8_header[32]; | ||
133 | /* 4 bytes 'MPCK' */ | ||
134 | lseek(fd, 4, SEEK_SET); | ||
135 | if (read(fd, sv8_header, 2) != 2) return false; /* read frame ID */ | ||
136 | if (!memcmp(sv8_header, "SH", 2)) { /* Stream Header ID */ | ||
137 | int32_t k = 0; | ||
138 | uint32_t streamversion; | ||
139 | uint64_t size = 0; /* tag size */ | ||
140 | uint64_t dummy = 0; /* used to dummy read data from header */ | ||
141 | |||
142 | /* 4 bytes 'MPCK' + 2 'SH' */ | ||
143 | lseek(fd, 6, SEEK_SET); | ||
144 | if (read(fd, sv8_header, 32) != 32) return false; | ||
145 | |||
146 | /* Read the size of 'SH'-tag */ | ||
147 | k = sv8_get_size(sv8_header, k, &size); | ||
148 | |||
149 | /* Skip crc32 */ | ||
150 | k += 4; | ||
151 | |||
152 | /* Read stream version */ | ||
153 | streamversion = sv8_header[k++]; | ||
154 | if (streamversion != 8) return false; /* Only SV8 is allowed. */ | ||
155 | |||
156 | /* Number of samples */ | ||
157 | k = sv8_get_size(sv8_header, k, &samples); | ||
158 | |||
159 | /* Number of leading zero-samples */ | ||
160 | k = sv8_get_size(sv8_header, k, &dummy); | ||
161 | |||
162 | /* Sampling frequency */ | ||
163 | id3->frequency = sfreqs[(sv8_header[k++] >> 5) & 0x0003]; | ||
164 | |||
165 | /* Number of channels */ | ||
166 | id3->channels = (sv8_header[k++] >> 4) + 1; | ||
167 | |||
168 | /* Skip to next tag: k = size -2 */ | ||
169 | k = size - 2; | ||
170 | |||
171 | if (!memcmp(sv8_header+k, "RG", 2)) { /* Replay Gain ID */ | ||
172 | long peak, gain; | ||
173 | int bufused = 0; | ||
174 | |||
175 | k += 2; /* 2 bytes 'RG' */ | ||
176 | |||
177 | /* sv8_get_size must be called to skip the right amount of | ||
178 | * bits within the header data. */ | ||
179 | k = sv8_get_size(sv8_header, k, &size); | ||
180 | |||
181 | /* Read and set replay gain */ | ||
182 | if (sv8_header[k++] == 1) { | ||
183 | /* Title's peak and gain */ | ||
184 | gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2; | ||
185 | peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2; | ||
186 | bufused += set_replaygain_sv8(id3, false, gain, peak, bufused); | ||
187 | |||
188 | /* Album's peak and gain */ | ||
189 | gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2; | ||
190 | peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2; | ||
191 | bufused += set_replaygain_sv8(id3, true , gain, peak, bufused); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | id3->codectype = AFMT_MPC_SV8; | ||
196 | } else { | ||
197 | /* No sv8 stream header found */ | ||
198 | return false; | ||
199 | } | ||
200 | } else { | ||
201 | return false; /* SV4-6 is not supported anymore */ | ||
202 | } | ||
203 | |||
204 | id3->vbr = true; | ||
205 | /* Estimate bitrate, we should probably subtract the various header sizes | ||
206 | here for super-accurate results */ | ||
207 | id3->length = ((int64_t) samples * 1000) / id3->frequency; | ||
208 | |||
209 | if (id3->length <= 0) | ||
210 | { | ||
211 | logf("mpc length invalid!"); | ||
212 | return false; | ||
213 | } | ||
214 | |||
215 | id3->filesize = filesize(fd); | ||
216 | id3->bitrate = id3->filesize * 8 / id3->length; | ||
217 | |||
218 | read_ape_tags(fd, id3); | ||
219 | return true; | ||
220 | } | ||
diff --git a/lib/rbcodec/metadata/nsf.c b/lib/rbcodec/metadata/nsf.c new file mode 100644 index 0000000000..2fa6f36b12 --- /dev/null +++ b/lib/rbcodec/metadata/nsf.c | |||
@@ -0,0 +1,278 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | #include "string-extra.h" | ||
13 | |||
14 | /* NOTE: This file was modified to work properly with the new nsf codec based | ||
15 | on Game_Music_Emu */ | ||
16 | |||
17 | struct NESM_HEADER | ||
18 | { | ||
19 | uint32_t nHeader; | ||
20 | uint8_t nHeaderExtra; | ||
21 | uint8_t nVersion; | ||
22 | uint8_t nTrackCount; | ||
23 | uint8_t nInitialTrack; | ||
24 | uint16_t nLoadAddress; | ||
25 | uint16_t nInitAddress; | ||
26 | uint16_t nPlayAddress; | ||
27 | uint8_t szGameTitle[32]; | ||
28 | uint8_t szArtist[32]; | ||
29 | uint8_t szCopyright[32]; | ||
30 | uint16_t nSpeedNTSC; | ||
31 | uint8_t nBankSwitch[8]; | ||
32 | uint16_t nSpeedPAL; | ||
33 | uint8_t nNTSC_PAL; | ||
34 | uint8_t nExtraChip; | ||
35 | uint8_t nExpansion[4]; | ||
36 | } __attribute__((packed)); | ||
37 | |||
38 | struct NSFE_INFOCHUNK | ||
39 | { | ||
40 | uint16_t nLoadAddress; | ||
41 | uint16_t nInitAddress; | ||
42 | uint16_t nPlayAddress; | ||
43 | uint8_t nIsPal; | ||
44 | uint8_t nExt; | ||
45 | uint8_t nTrackCount; | ||
46 | uint8_t nStartingTrack; | ||
47 | } __attribute__((packed)); | ||
48 | |||
49 | |||
50 | #define CHAR4_CONST(a, b, c, d) FOURCC(a, b, c, d) | ||
51 | #define CHUNK_INFO 0x0001 | ||
52 | #define CHUNK_DATA 0x0002 | ||
53 | #define CHUNK_NEND 0x0004 | ||
54 | #define CHUNK_plst 0x0008 | ||
55 | #define CHUNK_time 0x0010 | ||
56 | #define CHUNK_fade 0x0020 | ||
57 | #define CHUNK_tlbl 0x0040 | ||
58 | #define CHUNK_auth 0x0080 | ||
59 | #define CHUNK_BANK 0x0100 | ||
60 | |||
61 | static bool parse_nsfe(int fd, struct mp3entry *id3) | ||
62 | { | ||
63 | unsigned int chunks_found = 0; | ||
64 | long track_count = 0; | ||
65 | long playlist_count = 0; | ||
66 | |||
67 | struct NSFE_INFOCHUNK info; | ||
68 | memset(&info, 0, sizeof(struct NSFE_INFOCHUNK)); | ||
69 | |||
70 | /* default values */ | ||
71 | info.nTrackCount = 1; | ||
72 | id3->length = 150 * 1000; | ||
73 | |||
74 | /* begin reading chunks */ | ||
75 | while (!(chunks_found & CHUNK_NEND)) | ||
76 | { | ||
77 | uint32_t chunk_size, chunk_type; | ||
78 | |||
79 | if (read_uint32le(fd, &chunk_size) != (int)sizeof(uint32_t)) | ||
80 | return false; | ||
81 | |||
82 | if (read_uint32be(fd, &chunk_type) != (int)sizeof(uint32_t)) | ||
83 | return false; | ||
84 | |||
85 | switch (chunk_type) | ||
86 | { | ||
87 | /* first three types are mandatory (but don't worry about NEND | ||
88 | anyway) */ | ||
89 | case CHAR4_CONST('I', 'N', 'F', 'O'): | ||
90 | { | ||
91 | if (chunks_found & CHUNK_INFO) | ||
92 | return false; /* only one info chunk permitted */ | ||
93 | |||
94 | chunks_found |= CHUNK_INFO; | ||
95 | |||
96 | /* minimum size */ | ||
97 | if (chunk_size < 8) | ||
98 | return false; | ||
99 | |||
100 | ssize_t size = MIN(sizeof(struct NSFE_INFOCHUNK), chunk_size); | ||
101 | |||
102 | if (read(fd, &info, size) != size) | ||
103 | return false; | ||
104 | |||
105 | if (size >= 9) | ||
106 | track_count = info.nTrackCount; | ||
107 | |||
108 | chunk_size -= size; | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | case CHAR4_CONST('D', 'A', 'T', 'A'): | ||
113 | { | ||
114 | if (!(chunks_found & CHUNK_INFO)) | ||
115 | return false; | ||
116 | |||
117 | if (chunks_found & CHUNK_DATA) | ||
118 | return false; /* only one may exist */ | ||
119 | |||
120 | if (chunk_size < 1) | ||
121 | return false; | ||
122 | |||
123 | chunks_found |= CHUNK_DATA; | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | case CHAR4_CONST('N', 'E', 'N', 'D'): | ||
128 | { | ||
129 | /* just end parsing regardless of whether or not this really is the | ||
130 | last chunk/data (but it _should_ be) */ | ||
131 | chunks_found |= CHUNK_NEND; | ||
132 | continue; | ||
133 | } | ||
134 | |||
135 | /* remaining types are optional */ | ||
136 | |||
137 | case CHAR4_CONST('a', 'u', 't', 'h'): | ||
138 | { | ||
139 | if (chunks_found & CHUNK_auth) | ||
140 | return false; /* only one may exist */ | ||
141 | |||
142 | chunks_found |= CHUNK_auth; | ||
143 | |||
144 | /* szGameTitle, szArtist, szCopyright */ | ||
145 | char ** const ar[] = { &id3->title, &id3->artist, &id3->album }; | ||
146 | |||
147 | char *p = id3->id3v2buf; | ||
148 | long buf_rem = sizeof (id3->id3v2buf); | ||
149 | unsigned int i; | ||
150 | |||
151 | for (i = 0; i < ARRAYLEN(ar) && chunk_size && buf_rem; i++) | ||
152 | { | ||
153 | long len = read_string(fd, p, buf_rem, '\0', chunk_size); | ||
154 | |||
155 | if (len < 0) | ||
156 | return false; | ||
157 | |||
158 | *ar[i] = p; | ||
159 | p += len; | ||
160 | buf_rem -= len; | ||
161 | |||
162 | if (chunk_size >= (uint32_t)len) | ||
163 | chunk_size -= len; | ||
164 | else | ||
165 | chunk_size = 0; | ||
166 | } | ||
167 | |||
168 | break; | ||
169 | } | ||
170 | |||
171 | case CHAR4_CONST('p', 'l', 's', 't'): | ||
172 | { | ||
173 | if (chunks_found & CHUNK_plst) | ||
174 | return false; /* only one may exist */ | ||
175 | |||
176 | chunks_found |= CHUNK_plst; | ||
177 | |||
178 | /* each byte is the index of one track */ | ||
179 | playlist_count = chunk_size; | ||
180 | break; | ||
181 | } | ||
182 | |||
183 | case CHAR4_CONST('t', 'i', 'm', 'e'): | ||
184 | case CHAR4_CONST('f', 'a', 'd', 'e'): | ||
185 | case CHAR4_CONST('t', 'l', 'b', 'l'): /* we unfortunately can't use these anyway */ | ||
186 | { | ||
187 | /* don't care how many of these there are even though there should | ||
188 | be only one */ | ||
189 | if (!(chunks_found & CHUNK_INFO)) | ||
190 | return false; | ||
191 | |||
192 | case CHAR4_CONST('B', 'A', 'N', 'K'): | ||
193 | break; | ||
194 | } | ||
195 | |||
196 | default: /* unknown chunk */ | ||
197 | { | ||
198 | /* check the first byte */ | ||
199 | chunk_type = (uint8_t)chunk_type; | ||
200 | |||
201 | /* chunk is vital... don't continue */ | ||
202 | if(chunk_type >= 'A' && chunk_type <= 'Z') | ||
203 | return false; | ||
204 | |||
205 | /* otherwise, just skip it */ | ||
206 | break; | ||
207 | } | ||
208 | } /* end switch */ | ||
209 | |||
210 | lseek(fd, chunk_size, SEEK_CUR); | ||
211 | } /* end while */ | ||
212 | |||
213 | if (track_count | playlist_count) | ||
214 | id3->length = MAX(track_count, playlist_count)*1000; | ||
215 | |||
216 | /* Single subtrack files will be treated differently | ||
217 | by gme's nsf codec */ | ||
218 | if (id3->length <= 1000) id3->length = 150 * 1000; | ||
219 | |||
220 | /* | ||
221 | * if we exited the while loop without a 'return', we must have hit an NEND | ||
222 | * chunk if this is the case, the file was layed out as it was expected. | ||
223 | * now.. make sure we found both an info chunk, AND a data chunk... since | ||
224 | * these are minimum requirements for a valid NSFE file | ||
225 | */ | ||
226 | return (chunks_found & (CHUNK_INFO | CHUNK_DATA)) == | ||
227 | (CHUNK_INFO | CHUNK_DATA); | ||
228 | } | ||
229 | |||
230 | static bool parse_nesm(int fd, struct mp3entry *id3) | ||
231 | { | ||
232 | struct NESM_HEADER hdr; | ||
233 | char *p = id3->id3v2buf; | ||
234 | |||
235 | lseek(fd, 0, SEEK_SET); | ||
236 | if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) | ||
237 | return false; | ||
238 | |||
239 | /* Length */ | ||
240 | id3->length = (hdr.nTrackCount > 1 ? hdr.nTrackCount : 150) * 1000; | ||
241 | |||
242 | /* Title */ | ||
243 | id3->title = p; | ||
244 | p += strlcpy(p, hdr.szGameTitle, 32) + 1; | ||
245 | |||
246 | /* Artist */ | ||
247 | id3->artist = p; | ||
248 | p += strlcpy(p, hdr.szArtist, 32) + 1; | ||
249 | |||
250 | /* Copyright (per codec) */ | ||
251 | id3->album = p; | ||
252 | strlcpy(p, hdr.szCopyright, 32); | ||
253 | |||
254 | return true; | ||
255 | } | ||
256 | |||
257 | bool get_nsf_metadata(int fd, struct mp3entry* id3) | ||
258 | { | ||
259 | uint32_t nsf_type; | ||
260 | if (lseek(fd, 0, SEEK_SET) < 0 || | ||
261 | read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type)) | ||
262 | return false; | ||
263 | |||
264 | id3->vbr = false; | ||
265 | id3->filesize = filesize(fd); | ||
266 | /* we only render 16 bits, 44.1KHz, Mono */ | ||
267 | id3->bitrate = 706; | ||
268 | id3->frequency = 44100; | ||
269 | |||
270 | if (nsf_type == CHAR4_CONST('N', 'S', 'F', 'E')) | ||
271 | return parse_nsfe(fd, id3); | ||
272 | else if (nsf_type == CHAR4_CONST('N', 'E', 'S', 'M')) | ||
273 | return parse_nesm(fd, id3); | ||
274 | |||
275 | /* not a valid format*/ | ||
276 | return false; | ||
277 | } | ||
278 | |||
diff --git a/lib/rbcodec/metadata/ogg.c b/lib/rbcodec/metadata/ogg.c new file mode 100644 index 0000000000..3a3cb29998 --- /dev/null +++ b/lib/rbcodec/metadata/ogg.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | /* A simple parser to read vital metadata from an Ogg Vorbis file. | ||
34 | * Can also handle parsing Ogg Speex files for metadata. Returns | ||
35 | * false if metadata needed by the codec couldn't be read. | ||
36 | */ | ||
37 | bool get_ogg_metadata(int fd, struct mp3entry* id3) | ||
38 | { | ||
39 | /* An Ogg File is split into pages, each starting with the string | ||
40 | * "OggS". Each page has a timestamp (in PCM samples) referred to as | ||
41 | * the "granule position". | ||
42 | * | ||
43 | * An Ogg Vorbis has the following structure: | ||
44 | * 1) Identification header (containing samplerate, numchannels, etc) | ||
45 | * 2) Comment header - containing the Vorbis Comments | ||
46 | * 3) Setup header - containing codec setup information | ||
47 | * 4) Many audio packets... | ||
48 | * | ||
49 | * An Ogg Speex has the following structure: | ||
50 | * 1) Identification header (containing samplerate, numchannels, etc) | ||
51 | * Described in this page: (http://www.speex.org/manual2/node7.html) | ||
52 | * 2) Comment header - containing the Vorbis Comments | ||
53 | * 3) Many audio packets. | ||
54 | */ | ||
55 | |||
56 | /* Use the path name of the id3 structure as a temporary buffer. */ | ||
57 | unsigned char* buf = (unsigned char *)id3->path; | ||
58 | long comment_size; | ||
59 | long remaining = 0; | ||
60 | long last_serial = 0; | ||
61 | long serial, r; | ||
62 | int segments, header_size; | ||
63 | int i; | ||
64 | bool eof = false; | ||
65 | |||
66 | /* 92 bytes is enough for both Vorbis and Speex headers */ | ||
67 | if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 92) < 92)) | ||
68 | { | ||
69 | return false; | ||
70 | } | ||
71 | |||
72 | /* All Ogg streams start with OggS */ | ||
73 | if (memcmp(buf, "OggS", 4) != 0) | ||
74 | { | ||
75 | return false; | ||
76 | } | ||
77 | |||
78 | /* Check for format magic and then get metadata */ | ||
79 | if (memcmp(&buf[29], "vorbis", 6) == 0) | ||
80 | { | ||
81 | id3->codectype = AFMT_OGG_VORBIS; | ||
82 | id3->frequency = get_long_le(&buf[40]); | ||
83 | id3->vbr = true; | ||
84 | |||
85 | /* Comments are in second Ogg page (byte 58 onwards for Vorbis) */ | ||
86 | if (lseek(fd, 58, SEEK_SET) < 0) | ||
87 | { | ||
88 | return false; | ||
89 | } | ||
90 | } | ||
91 | else if (memcmp(&buf[28], "Speex ", 8) == 0) | ||
92 | { | ||
93 | id3->codectype = AFMT_SPEEX; | ||
94 | id3->frequency = get_slong(&buf[64]); | ||
95 | id3->vbr = get_long_le(&buf[88]); | ||
96 | |||
97 | header_size = get_long_le(&buf[60]); | ||
98 | |||
99 | /* Comments are in second Ogg page (byte 108 onwards for Speex) */ | ||
100 | if (lseek(fd, 28 + header_size, SEEK_SET) < 0) | ||
101 | { | ||
102 | return false; | ||
103 | } | ||
104 | } | ||
105 | else | ||
106 | { | ||
107 | /* Unsupported format, try to print the marker, catches Ogg/FLAC at least */ | ||
108 | DEBUGF("Usupported format in Ogg stream: %16s\n", &buf[28]); | ||
109 | return false; | ||
110 | } | ||
111 | |||
112 | id3->filesize = filesize(fd); | ||
113 | |||
114 | /* We need to ensure the serial number from this page is the same as the | ||
115 | * one from the last page (since we only support a single bitstream). | ||
116 | */ | ||
117 | serial = get_long_le(&buf[14]); | ||
118 | comment_size = read_vorbis_tags(fd, id3, remaining); | ||
119 | |||
120 | /* We now need to search for the last page in the file - identified by | ||
121 | * by ('O','g','g','S',0) and retrieve totalsamples. | ||
122 | */ | ||
123 | |||
124 | /* A page is always < 64 kB */ | ||
125 | if (lseek(fd, -(MIN(64 * 1024, id3->filesize)), SEEK_END) < 0) | ||
126 | { | ||
127 | return false; | ||
128 | } | ||
129 | |||
130 | remaining = 0; | ||
131 | |||
132 | while (!eof) | ||
133 | { | ||
134 | r = read(fd, &buf[remaining], MAX_PATH - remaining); | ||
135 | |||
136 | if (r <= 0) | ||
137 | { | ||
138 | eof = true; | ||
139 | } | ||
140 | else | ||
141 | { | ||
142 | remaining += r; | ||
143 | } | ||
144 | |||
145 | /* Inefficient (but simple) search */ | ||
146 | i = 0; | ||
147 | |||
148 | while (i < (remaining - 3)) | ||
149 | { | ||
150 | if ((buf[i] == 'O') && (memcmp(&buf[i], "OggS", 4) == 0)) | ||
151 | { | ||
152 | if (i < (remaining - 17)) | ||
153 | { | ||
154 | /* Note that this only reads the low 32 bits of a | ||
155 | * 64 bit value. | ||
156 | */ | ||
157 | id3->samples = get_long_le(&buf[i + 6]); | ||
158 | last_serial = get_long_le(&buf[i + 14]); | ||
159 | |||
160 | /* If this page is very small the beginning of the next | ||
161 | * header could be in buffer. Jump near end of this header | ||
162 | * and continue */ | ||
163 | i += 27; | ||
164 | } | ||
165 | else | ||
166 | { | ||
167 | break; | ||
168 | } | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | i++; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | if (i < remaining) | ||
177 | { | ||
178 | /* Move the remaining bytes to start of buffer. | ||
179 | * Reuse var 'segments' as it is no longer needed */ | ||
180 | segments = 0; | ||
181 | while (i < remaining) | ||
182 | { | ||
183 | buf[segments++] = buf[i++]; | ||
184 | } | ||
185 | remaining = segments; | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | /* Discard the rest of the buffer */ | ||
190 | remaining = 0; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* This file has mutiple vorbis bitstreams (or is corrupt). */ | ||
195 | /* FIXME we should display an error here. */ | ||
196 | if (serial != last_serial) | ||
197 | { | ||
198 | logf("serialno mismatch"); | ||
199 | logf("%ld", serial); | ||
200 | logf("%ld", last_serial); | ||
201 | return false; | ||
202 | } | ||
203 | |||
204 | id3->length = ((int64_t) id3->samples * 1000) / id3->frequency; | ||
205 | if (id3->length <= 0) | ||
206 | { | ||
207 | logf("ogg length invalid!"); | ||
208 | return false; | ||
209 | } | ||
210 | |||
211 | id3->bitrate = (((int64_t) id3->filesize - comment_size) * 8) / id3->length; | ||
212 | |||
213 | return true; | ||
214 | } | ||
215 | |||
diff --git a/lib/rbcodec/metadata/oma.c b/lib/rbcodec/metadata/oma.c new file mode 100644 index 0000000000..b82c0a4f73 --- /dev/null +++ b/lib/rbcodec/metadata/oma.c | |||
@@ -0,0 +1,189 @@ | |||
1 | /* | ||
2 | * Sony OpenMG (OMA) demuxer | ||
3 | * | ||
4 | * Copyright (c) 2008 Maxim Poliakovski | ||
5 | * 2008 Benjamin Larsson | ||
6 | * | ||
7 | * This file is part of FFmpeg. | ||
8 | * | ||
9 | * FFmpeg is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU Lesser General Public | ||
11 | * License as published by the Free Software Foundation; either | ||
12 | * version 2.1 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * FFmpeg is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * Lesser General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with FFmpeg; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | */ | ||
23 | |||
24 | /** | ||
25 | * @file oma.c | ||
26 | * This is a demuxer for Sony OpenMG Music files | ||
27 | * | ||
28 | * Known file extensions: ".oma", "aa3" | ||
29 | * The format of such files consists of three parts: | ||
30 | * - "ea3" header carrying overall info and metadata. | ||
31 | * - "EA3" header is a Sony-specific header containing information about | ||
32 | * the OpenMG file: codec type (usually ATRAC, can also be MP3 or WMA), | ||
33 | * codec specific info (packet size, sample rate, channels and so on) | ||
34 | * and DRM related info (file encryption, content id). | ||
35 | * - Sound data organized in packets follow the EA3 header | ||
36 | * (can be encrypted using the Sony DRM!). | ||
37 | * | ||
38 | * LIMITATIONS: This version supports only plain (unencrypted) OMA files. | ||
39 | * If any DRM-protected (encrypted) file is encountered you will get the | ||
40 | * corresponding error message. Try to remove the encryption using any | ||
41 | * Sony software (for example SonicStage). | ||
42 | * CODEC SUPPORT: Only ATRAC3 codec is currently supported! | ||
43 | */ | ||
44 | |||
45 | #include <stdio.h> | ||
46 | #include <stdlib.h> | ||
47 | #include <inttypes.h> | ||
48 | #include <string.h> | ||
49 | #include "metadata.h" | ||
50 | #include "metadata_parsers.h" | ||
51 | |||
52 | #define EA3_HEADER_SIZE 96 | ||
53 | |||
54 | #if 0 | ||
55 | #define DEBUGF printf | ||
56 | #else | ||
57 | #define DEBUGF(...) | ||
58 | #endif | ||
59 | |||
60 | /* Various helper macros taken from ffmpeg for reading * | ||
61 | * and writing buffers with a specified endianess. */ | ||
62 | # define AV_RB16(x) \ | ||
63 | ((((const uint8_t*)(x))[0] << 8) | \ | ||
64 | ((const uint8_t*)(x))[1]) | ||
65 | # define AV_RB24(x) \ | ||
66 | ((((const uint8_t*)(x))[0] << 16) | \ | ||
67 | (((const uint8_t*)(x))[1] << 8) | \ | ||
68 | ((const uint8_t*)(x))[2]) | ||
69 | # define AV_RB32(x) \ | ||
70 | ((((const uint8_t*)(x))[0] << 24) | \ | ||
71 | (((const uint8_t*)(x))[1] << 16) | \ | ||
72 | (((const uint8_t*)(x))[2] << 8) | \ | ||
73 | ((const uint8_t*)(x))[3]) | ||
74 | # define AV_WL32(p, d) do { \ | ||
75 | ((uint8_t*)(p))[0] = (d); \ | ||
76 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
77 | ((uint8_t*)(p))[2] = (d)>>16; \ | ||
78 | ((uint8_t*)(p))[3] = (d)>>24; \ | ||
79 | } while(0) | ||
80 | # define AV_WL16(p, d) do { \ | ||
81 | ((uint8_t*)(p))[0] = (d); \ | ||
82 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
83 | } while(0) | ||
84 | |||
85 | /* Different codecs that could be present in a Sony OMA * | ||
86 | * container file. */ | ||
87 | enum { | ||
88 | OMA_CODECID_ATRAC3 = 0, | ||
89 | OMA_CODECID_ATRAC3P = 1, | ||
90 | OMA_CODECID_MP3 = 3, | ||
91 | OMA_CODECID_LPCM = 4, | ||
92 | OMA_CODECID_WMA = 5, | ||
93 | }; | ||
94 | |||
95 | /* FIXME: This functions currently read different file * | ||
96 | * parameters required for decoding. It still * | ||
97 | * does not read the metadata - which should be * | ||
98 | * present in the ea3 (first) header. The * | ||
99 | * metadata in ea3 is stored as a variation of * | ||
100 | * the ID3v2 metadata format. */ | ||
101 | static int oma_read_header(int fd, struct mp3entry* id3) | ||
102 | { | ||
103 | static const uint16_t srate_tab[6] = {320,441,480,882,960,0}; | ||
104 | int ret, ea3_taglen, EA3_pos, jsflag; | ||
105 | uint32_t codec_params; | ||
106 | int16_t eid; | ||
107 | uint8_t buf[EA3_HEADER_SIZE]; | ||
108 | |||
109 | ret = read(fd, buf, 10); | ||
110 | if (ret != 10) | ||
111 | return -1; | ||
112 | |||
113 | ea3_taglen = ((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f); | ||
114 | |||
115 | EA3_pos = ea3_taglen + 10; | ||
116 | if (buf[5] & 0x10) | ||
117 | EA3_pos += 10; | ||
118 | |||
119 | lseek(fd, EA3_pos, SEEK_SET); | ||
120 | ret = read(fd, buf, EA3_HEADER_SIZE); | ||
121 | if (ret != EA3_HEADER_SIZE) | ||
122 | return -1; | ||
123 | |||
124 | if (memcmp(buf, ((const uint8_t[]){'E', 'A', '3'}),3) || buf[4] != 0 || buf[5] != EA3_HEADER_SIZE) { | ||
125 | DEBUGF("Couldn't find the EA3 header !\n"); | ||
126 | return -1; | ||
127 | } | ||
128 | |||
129 | eid = AV_RB16(&buf[6]); | ||
130 | if (eid != -1 && eid != -128) { | ||
131 | DEBUGF("Encrypted file! Eid: %d\n", eid); | ||
132 | return -1; | ||
133 | } | ||
134 | |||
135 | codec_params = AV_RB24(&buf[33]); | ||
136 | |||
137 | switch (buf[32]) { | ||
138 | case OMA_CODECID_ATRAC3: | ||
139 | id3->frequency = srate_tab[(codec_params >> 13) & 7]*100; | ||
140 | if (id3->frequency != 44100) { | ||
141 | DEBUGF("Unsupported sample rate, send sample file to developers: %d\n", id3->frequency); | ||
142 | return -1; | ||
143 | } | ||
144 | |||
145 | id3->bytesperframe = (codec_params & 0x3FF) * 8; | ||
146 | id3->codectype = AFMT_OMA_ATRAC3; | ||
147 | jsflag = (codec_params >> 17) & 1; /* get stereo coding mode, 1 for joint-stereo */ | ||
148 | |||
149 | id3->bitrate = id3->frequency * id3->bytesperframe * 8 / (1024 * 1000); | ||
150 | |||
151 | /* fake the atrac3 extradata (wav format, makes stream copy to wav work) */ | ||
152 | /* ATRAC3 expects and extra-data size of 14 bytes for wav format, and * | ||
153 | * looks for that in the id3v2buf. */ | ||
154 | id3->extradata_size = 14; | ||
155 | AV_WL16(&id3->id3v2buf[0], 1); // always 1 | ||
156 | AV_WL32(&id3->id3v2buf[2], id3->frequency); // samples rate | ||
157 | AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode | ||
158 | AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode | ||
159 | AV_WL16(&id3->id3v2buf[10], 1); // always 1 | ||
160 | AV_WL16(&id3->id3v2buf[12], 0); // always 0 | ||
161 | |||
162 | id3->channels = 2; | ||
163 | DEBUGF("sample_rate = %d\n", id3->frequency); | ||
164 | DEBUGF("frame_size = %d\n", id3->bytesperframe); | ||
165 | DEBUGF("stereo_coding_mode = %d\n", jsflag); | ||
166 | break; | ||
167 | default: | ||
168 | DEBUGF("Unsupported codec %d!\n",buf[32]); | ||
169 | return -1; | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | /* Store the the offset of the first audio frame, to be able to seek to it * | ||
174 | * directly in atrac3_oma.codec. */ | ||
175 | id3->first_frame_offset = EA3_pos + EA3_HEADER_SIZE; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | bool get_oma_metadata(int fd, struct mp3entry* id3) | ||
180 | { | ||
181 | if(oma_read_header(fd, id3) < 0) | ||
182 | return false; | ||
183 | |||
184 | /* Currently, there's no means of knowing the duration * | ||
185 | * directly from the the file so we calculate it. */ | ||
186 | id3->filesize = filesize(fd); | ||
187 | id3->length = ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate; | ||
188 | return true; | ||
189 | } | ||
diff --git a/lib/rbcodec/metadata/replaygain.c b/lib/rbcodec/metadata/replaygain.c new file mode 100644 index 0000000000..a178321385 --- /dev/null +++ b/lib/rbcodec/metadata/replaygain.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Magnus Holmgren | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <ctype.h> | ||
23 | #include <math.h> | ||
24 | #include <stdbool.h> | ||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <inttypes.h> | ||
28 | #include "strlcpy.h" | ||
29 | #include "strcasecmp.h" | ||
30 | #include "system.h" | ||
31 | #include "metadata.h" | ||
32 | #include "debug.h" | ||
33 | #include "replaygain.h" | ||
34 | #include "fixedpoint.h" | ||
35 | |||
36 | #define FP_BITS (12) | ||
37 | #define FP_ONE (1 << FP_BITS) | ||
38 | #define FP_MIN (-48 * FP_ONE) | ||
39 | #define FP_MAX ( 17 * FP_ONE) | ||
40 | |||
41 | void replaygain_itoa(char* buffer, int length, long int_gain) | ||
42 | { | ||
43 | /* int_gain uses Q19.12 format. */ | ||
44 | int one = abs(int_gain) >> FP_BITS; | ||
45 | int cent = ((abs(int_gain) & 0x0fff) * 100 + (FP_ONE/2)) >> FP_BITS; | ||
46 | snprintf(buffer, length, "%s%d.%02d dB", (int_gain<0) ? "-":"", one, cent); | ||
47 | } | ||
48 | |||
49 | static long fp_atof(const char* s, int precision) | ||
50 | { | ||
51 | long int_part = 0; | ||
52 | long int_one = BIT_N(precision); | ||
53 | long frac_part = 0; | ||
54 | long frac_count = 0; | ||
55 | long frac_max = ((precision * 4) + 12) / 13; | ||
56 | long frac_max_int = 1; | ||
57 | long sign = 1; | ||
58 | bool point = false; | ||
59 | |||
60 | while ((*s != '\0') && isspace(*s)) | ||
61 | { | ||
62 | s++; | ||
63 | } | ||
64 | |||
65 | if (*s == '-') | ||
66 | { | ||
67 | sign = -1; | ||
68 | s++; | ||
69 | } | ||
70 | else if (*s == '+') | ||
71 | { | ||
72 | s++; | ||
73 | } | ||
74 | |||
75 | while (*s != '\0') | ||
76 | { | ||
77 | if (*s == '.') | ||
78 | { | ||
79 | if (point) | ||
80 | { | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | point = true; | ||
85 | } | ||
86 | else if (isdigit(*s)) | ||
87 | { | ||
88 | if (point) | ||
89 | { | ||
90 | if (frac_count < frac_max) | ||
91 | { | ||
92 | frac_part = frac_part * 10 + (*s - '0'); | ||
93 | frac_count++; | ||
94 | frac_max_int *= 10; | ||
95 | } | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | int_part = int_part * 10 + (*s - '0'); | ||
100 | } | ||
101 | } | ||
102 | else | ||
103 | { | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | s++; | ||
108 | } | ||
109 | |||
110 | while (frac_count < frac_max) | ||
111 | { | ||
112 | frac_part *= 10; | ||
113 | frac_count++; | ||
114 | frac_max_int *= 10; | ||
115 | } | ||
116 | |||
117 | return sign * ((int_part * int_one) | ||
118 | + (((int64_t) frac_part * int_one) / frac_max_int)); | ||
119 | } | ||
120 | |||
121 | static long convert_gain(long gain) | ||
122 | { | ||
123 | /* Don't allow unreasonably low or high gain changes. | ||
124 | * Our math code can't handle it properly anyway. :) */ | ||
125 | gain = MAX(gain, FP_MIN); | ||
126 | gain = MIN(gain, FP_MAX); | ||
127 | |||
128 | return fp_factor(gain, FP_BITS) << (24 - FP_BITS); | ||
129 | } | ||
130 | |||
131 | /* Get the sample scale factor in Q19.12 format from a gain value. Returns 0 | ||
132 | * for no gain. | ||
133 | * | ||
134 | * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored. | ||
135 | */ | ||
136 | static long get_replaygain(const char* str) | ||
137 | { | ||
138 | return fp_atof(str, FP_BITS); | ||
139 | } | ||
140 | |||
141 | /* Get the peak volume in Q7.24 format. | ||
142 | * | ||
143 | * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak. | ||
144 | */ | ||
145 | static long get_replaypeak(const char* str) | ||
146 | { | ||
147 | return fp_atof(str, 24); | ||
148 | } | ||
149 | |||
150 | /* Get a sample scale factor in Q7.24 format from a gain value. | ||
151 | * | ||
152 | * int_gain Gain in dB, multiplied by 100. | ||
153 | */ | ||
154 | long get_replaygain_int(long int_gain) | ||
155 | { | ||
156 | return convert_gain(int_gain * FP_ONE / 100); | ||
157 | } | ||
158 | |||
159 | /* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a | ||
160 | * valid tag is found, update mp3entry struct accordingly. Existing values | ||
161 | * are not overwritten. | ||
162 | * | ||
163 | * key Name of the tag. | ||
164 | * value Value of the tag. | ||
165 | * entry mp3entry struct to update. | ||
166 | */ | ||
167 | void parse_replaygain(const char* key, const char* value, | ||
168 | struct mp3entry* entry) | ||
169 | { | ||
170 | if (((strcasecmp(key, "replaygain_track_gain") == 0) || | ||
171 | (strcasecmp(key, "rg_radio") == 0)) && | ||
172 | !entry->track_gain) | ||
173 | { | ||
174 | entry->track_level = get_replaygain(value); | ||
175 | entry->track_gain = convert_gain(entry->track_level); | ||
176 | } | ||
177 | else if (((strcasecmp(key, "replaygain_album_gain") == 0) || | ||
178 | (strcasecmp(key, "rg_audiophile") == 0)) && | ||
179 | !entry->album_gain) | ||
180 | { | ||
181 | entry->album_level = get_replaygain(value); | ||
182 | entry->album_gain = convert_gain(entry->album_level); | ||
183 | } | ||
184 | else if (((strcasecmp(key, "replaygain_track_peak") == 0) || | ||
185 | (strcasecmp(key, "rg_peak") == 0)) && | ||
186 | !entry->track_peak) | ||
187 | { | ||
188 | entry->track_peak = get_replaypeak(value); | ||
189 | } | ||
190 | else if ((strcasecmp(key, "replaygain_album_peak") == 0) && | ||
191 | !entry->album_peak) | ||
192 | { | ||
193 | entry->album_peak = get_replaypeak(value); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /* Set ReplayGain values from integers. Existing values are not overwritten. | ||
198 | * | ||
199 | * album If true, set album values, otherwise set track values. | ||
200 | * gain Gain value in dB, multiplied by 512. 0 for no gain. | ||
201 | * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no | ||
202 | * peak volume. | ||
203 | * entry mp3entry struct to update. | ||
204 | */ | ||
205 | void parse_replaygain_int(bool album, long gain, long peak, | ||
206 | struct mp3entry* entry) | ||
207 | { | ||
208 | gain = gain * FP_ONE / 512; | ||
209 | |||
210 | if (album) | ||
211 | { | ||
212 | entry->album_level = gain; | ||
213 | entry->album_gain = convert_gain(gain); | ||
214 | entry->album_peak = peak; | ||
215 | } | ||
216 | else | ||
217 | { | ||
218 | entry->track_level = gain; | ||
219 | entry->track_gain = convert_gain(gain); | ||
220 | entry->track_peak = peak; | ||
221 | } | ||
222 | } | ||
diff --git a/lib/rbcodec/metadata/replaygain.h b/lib/rbcodec/metadata/replaygain.h new file mode 100644 index 0000000000..215464dfdf --- /dev/null +++ b/lib/rbcodec/metadata/replaygain.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Magnus Holmgren | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _REPLAYGAIN_H | ||
23 | #define _REPLAYGAIN_H | ||
24 | |||
25 | #include "metadata.h" | ||
26 | |||
27 | long get_replaygain_int(long int_gain); | ||
28 | void parse_replaygain(const char* key, const char* value, | ||
29 | struct mp3entry* entry); | ||
30 | void parse_replaygain_int(bool album, long gain, long peak, | ||
31 | struct mp3entry* entry); | ||
32 | void replaygain_itoa(char* buffer, int length, long int_gain); | ||
33 | |||
34 | #endif | ||
diff --git a/lib/rbcodec/metadata/rm.c b/lib/rbcodec/metadata/rm.c new file mode 100644 index 0000000000..27f541cb25 --- /dev/null +++ b/lib/rbcodec/metadata/rm.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Mohamed Tarek | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include <codecs/librm/rm.h> | ||
28 | #include "system.h" | ||
29 | #include "metadata.h" | ||
30 | #include "metadata_common.h" | ||
31 | #include "metadata_parsers.h" | ||
32 | #include "logf.h" | ||
33 | |||
34 | /* Uncomment the following line for debugging */ | ||
35 | //#define DEBUG_RM | ||
36 | #ifndef DEBUG_RM | ||
37 | #undef DEBUGF | ||
38 | #define DEBUGF(...) | ||
39 | #endif | ||
40 | |||
41 | #define ID3V1_OFFSET -128 | ||
42 | #define METADATA_FOOTER_OFFSET -140 | ||
43 | |||
44 | static inline void print_cook_extradata(RMContext *rmctx) { | ||
45 | |||
46 | DEBUGF(" cook_version = 0x%08lx\n", rm_get_uint32be(rmctx->codec_extradata)); | ||
47 | DEBUGF(" samples_per_frame_per_channel = %d\n", rm_get_uint16be(&rmctx->codec_extradata[4])); | ||
48 | DEBUGF(" number_of_subbands_in_freq_domain = %d\n", rm_get_uint16be(&rmctx->codec_extradata[6])); | ||
49 | if(rmctx->extradata_size == 16) { | ||
50 | DEBUGF(" joint_stereo_subband_start = %d\n",rm_get_uint16be(&rmctx->codec_extradata[12])); | ||
51 | DEBUGF(" joint_stereo_vlc_bits = %d\n", rm_get_uint16be(&rmctx->codec_extradata[14])); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | |||
56 | struct real_object_t | ||
57 | { | ||
58 | uint32_t fourcc; | ||
59 | uint32_t size; | ||
60 | uint16_t version; | ||
61 | }; | ||
62 | |||
63 | static int real_read_object_header(int fd, struct real_object_t* obj) | ||
64 | { | ||
65 | int n; | ||
66 | |||
67 | if ((n = read_uint32be(fd, &obj->fourcc)) <= 0) | ||
68 | return n; | ||
69 | if ((n = read_uint32be(fd, &obj->size)) <= 0) | ||
70 | return n; | ||
71 | if ((n = read_uint16be(fd, &obj->version)) <= 0) | ||
72 | return n; | ||
73 | |||
74 | return 1; | ||
75 | } | ||
76 | |||
77 | #if (defined(SIMULATOR) && defined(DEBUG_RM)) | ||
78 | static char* fourcc2str(uint32_t f) | ||
79 | { | ||
80 | static char res[5]; | ||
81 | |||
82 | res[0] = (f & 0xff000000) >> 24; | ||
83 | res[1] = (f & 0xff0000) >> 16; | ||
84 | res[2] = (f & 0xff00) >> 8; | ||
85 | res[3] = (f & 0xff); | ||
86 | res[4] = 0; | ||
87 | |||
88 | return res; | ||
89 | } | ||
90 | #endif | ||
91 | |||
92 | static inline int real_read_audio_stream_info(int fd, RMContext *rmctx) | ||
93 | { | ||
94 | int skipped = 0; | ||
95 | uint32_t version; | ||
96 | struct real_object_t obj; | ||
97 | #ifdef SIMULATOR | ||
98 | uint32_t header_size; | ||
99 | uint16_t flavor; | ||
100 | uint32_t coded_framesize; | ||
101 | uint8_t interleaver_id_length; | ||
102 | uint8_t fourcc_length; | ||
103 | #endif | ||
104 | uint32_t interleaver_id; | ||
105 | uint32_t fourcc = 0; | ||
106 | |||
107 | memset(&obj,0,sizeof(obj)); | ||
108 | read_uint32be(fd, &version); | ||
109 | skipped += 4; | ||
110 | |||
111 | DEBUGF(" version=0x%04lx\n",((version >> 16) & 0xff)); | ||
112 | if (((version >> 16) & 0xff) == 3) { | ||
113 | /* Very old version */ | ||
114 | } else { | ||
115 | #ifdef SIMULATOR | ||
116 | real_read_object_header(fd, &obj); | ||
117 | read_uint32be(fd, &header_size); | ||
118 | /* obj.size will be filled with an unknown value, replaced with header_size */ | ||
119 | DEBUGF(" Object: %s, size: %ld bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); | ||
120 | |||
121 | read_uint16be(fd, &flavor); | ||
122 | read_uint32be(fd, &coded_framesize); | ||
123 | #else | ||
124 | lseek(fd, 20, SEEK_CUR); | ||
125 | #endif | ||
126 | lseek(fd, 12, SEEK_CUR); /* unknown */ | ||
127 | read_uint16be(fd, &rmctx->sub_packet_h); | ||
128 | read_uint16be(fd, &rmctx->block_align); | ||
129 | read_uint16be(fd, &rmctx->sub_packet_size); | ||
130 | lseek(fd, 2, SEEK_CUR); /* unknown */ | ||
131 | skipped += 40; | ||
132 | if (((version >> 16) & 0xff) == 5) | ||
133 | { | ||
134 | lseek(fd, 6, SEEK_CUR); /* unknown */ | ||
135 | skipped += 6; | ||
136 | } | ||
137 | read_uint16be(fd, &rmctx->sample_rate); | ||
138 | lseek(fd, 4, SEEK_CUR); /* unknown */ | ||
139 | read_uint16be(fd, &rmctx->nb_channels); | ||
140 | skipped += 8; | ||
141 | if (((version >> 16) & 0xff) == 4) | ||
142 | { | ||
143 | #ifdef SIMULATOR | ||
144 | read_uint8(fd, &interleaver_id_length); | ||
145 | read_uint32be(fd, &interleaver_id); | ||
146 | read_uint8(fd, &fourcc_length); | ||
147 | #else | ||
148 | lseek(fd, 6, SEEK_CUR); | ||
149 | #endif | ||
150 | read_uint32be(fd, &fourcc); | ||
151 | skipped += 10; | ||
152 | } | ||
153 | if (((version >> 16) & 0xff) == 5) | ||
154 | { | ||
155 | read_uint32be(fd, &interleaver_id); | ||
156 | read_uint32be(fd, &fourcc); | ||
157 | skipped += 8; | ||
158 | } | ||
159 | lseek(fd, 3, SEEK_CUR); /* unknown */ | ||
160 | skipped += 3; | ||
161 | if (((version >> 16) & 0xff) == 5) | ||
162 | { | ||
163 | lseek(fd, 1, SEEK_CUR); /* unknown */ | ||
164 | skipped += 1; | ||
165 | } | ||
166 | |||
167 | switch(fourcc) { | ||
168 | case FOURCC('c','o','o','k'): | ||
169 | rmctx->codec_type = CODEC_COOK; | ||
170 | read_uint32be(fd, &rmctx->extradata_size); | ||
171 | skipped += 4; | ||
172 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
173 | skipped += rmctx->extradata_size; | ||
174 | break; | ||
175 | |||
176 | case FOURCC('r','a','a','c'): | ||
177 | case FOURCC('r','a','c','p'): | ||
178 | rmctx->codec_type = CODEC_AAC; | ||
179 | read_uint32be(fd, &rmctx->extradata_size); | ||
180 | skipped += 4; | ||
181 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
182 | skipped += rmctx->extradata_size; | ||
183 | break; | ||
184 | |||
185 | case FOURCC('d','n','e','t'): | ||
186 | rmctx->codec_type = CODEC_AC3; | ||
187 | break; | ||
188 | |||
189 | case FOURCC('a','t','r','c'): | ||
190 | rmctx->codec_type = CODEC_ATRAC; | ||
191 | read_uint32be(fd, &rmctx->extradata_size); | ||
192 | skipped += 4; | ||
193 | read(fd, rmctx->codec_extradata, rmctx->extradata_size); | ||
194 | skipped += rmctx->extradata_size; | ||
195 | break; | ||
196 | |||
197 | default: /* Not a supported codec */ | ||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | DEBUGF(" flavor = %d\n",flavor); | ||
202 | DEBUGF(" coded_frame_size = %ld\n",coded_framesize); | ||
203 | DEBUGF(" sub_packet_h = %d\n",rmctx->sub_packet_h); | ||
204 | DEBUGF(" frame_size = %d\n",rmctx->block_align); | ||
205 | DEBUGF(" sub_packet_size = %d\n",rmctx->sub_packet_size); | ||
206 | DEBUGF(" sample_rate= %d\n",rmctx->sample_rate); | ||
207 | DEBUGF(" channels= %d\n",rmctx->nb_channels); | ||
208 | DEBUGF(" fourcc = %s\n",fourcc2str(fourcc)); | ||
209 | DEBUGF(" codec_extra_data_length = %ld\n",rmctx->extradata_size); | ||
210 | DEBUGF(" codec_extradata :\n"); | ||
211 | if(rmctx->codec_type == CODEC_COOK) { | ||
212 | DEBUGF(" cook_extradata :\n"); | ||
213 | print_cook_extradata(rmctx); | ||
214 | } | ||
215 | |||
216 | } | ||
217 | |||
218 | return skipped; | ||
219 | } | ||
220 | |||
221 | static int rm_parse_header(int fd, RMContext *rmctx, struct mp3entry *id3) | ||
222 | { | ||
223 | struct real_object_t obj; | ||
224 | int res; | ||
225 | int skipped; | ||
226 | off_t curpos __attribute__((unused)); | ||
227 | uint8_t len; /* Holds a string_length, which is then passed to read_string() */ | ||
228 | |||
229 | #ifdef SIMULATOR | ||
230 | uint32_t avg_bitrate = 0; | ||
231 | uint32_t max_packet_size; | ||
232 | uint32_t avg_packet_size; | ||
233 | uint32_t packet_count; | ||
234 | uint32_t duration; | ||
235 | uint32_t preroll; | ||
236 | uint32_t index_offset; | ||
237 | uint16_t stream_id; | ||
238 | uint32_t start_time; | ||
239 | uint32_t codec_data_size; | ||
240 | #endif | ||
241 | uint32_t v; | ||
242 | uint32_t max_bitrate; | ||
243 | uint16_t num_streams; | ||
244 | uint32_t next_data_off; | ||
245 | uint8_t header_end; | ||
246 | |||
247 | memset(&obj,0,sizeof(obj)); | ||
248 | curpos = lseek(fd, 0, SEEK_SET); | ||
249 | res = real_read_object_header(fd, &obj); | ||
250 | |||
251 | if (obj.fourcc == FOURCC('.','r','a',0xfd)) | ||
252 | { | ||
253 | /* Very old .ra format - not yet supported */ | ||
254 | return -1; | ||
255 | } | ||
256 | else if (obj.fourcc != FOURCC('.','R','M','F')) | ||
257 | { | ||
258 | return -1; | ||
259 | } | ||
260 | |||
261 | lseek(fd, 8, SEEK_CUR); /* unknown */ | ||
262 | |||
263 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
264 | |||
265 | res = real_read_object_header(fd, &obj); | ||
266 | header_end = 0; | ||
267 | while(res) | ||
268 | { | ||
269 | DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); | ||
270 | skipped = 10; | ||
271 | if(obj.fourcc == FOURCC('I','N','D','X')) | ||
272 | break; | ||
273 | switch (obj.fourcc) | ||
274 | { | ||
275 | case FOURCC('P','R','O','P'): /* File properties */ | ||
276 | read_uint32be(fd, &max_bitrate); | ||
277 | read_uint32be(fd, &rmctx->bit_rate); /*avg bitrate*/ | ||
278 | #ifdef SIMULATOR | ||
279 | read_uint32be(fd, &max_packet_size); | ||
280 | read_uint32be(fd, &avg_packet_size); | ||
281 | read_uint32be(fd, &packet_count); | ||
282 | #else | ||
283 | lseek(fd, 3*sizeof(uint32_t), SEEK_CUR); | ||
284 | #endif | ||
285 | read_uint32be(fd, &rmctx->duration); | ||
286 | #ifdef SIMULATOR | ||
287 | read_uint32be(fd, &preroll); | ||
288 | read_uint32be(fd, &index_offset); | ||
289 | #else | ||
290 | lseek(fd, 2*sizeof(uint32_t), SEEK_CUR); | ||
291 | #endif | ||
292 | read_uint32be(fd, &rmctx->data_offset); | ||
293 | read_uint16be(fd, &num_streams); | ||
294 | read_uint16be(fd, &rmctx->flags); | ||
295 | skipped += 40; | ||
296 | |||
297 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
298 | DEBUGF(" avg_bitrate = %ld\n",rmctx->bit_rate); | ||
299 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
300 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
301 | DEBUGF(" packet_count = %ld\n",packet_count); | ||
302 | DEBUGF(" duration = %ld\n",rmctx->duration); | ||
303 | DEBUGF(" preroll = %ld\n",preroll); | ||
304 | DEBUGF(" index_offset = %ld\n",index_offset); | ||
305 | DEBUGF(" data_offset = %ld\n",rmctx->data_offset); | ||
306 | DEBUGF(" num_streams = %d\n",num_streams); | ||
307 | DEBUGF(" flags=0x%04x\n",rmctx->flags); | ||
308 | break; | ||
309 | |||
310 | case FOURCC('C','O','N','T'): | ||
311 | /* Four strings - Title, Author, Copyright, Comment */ | ||
312 | read_uint8(fd,&len); | ||
313 | skipped += (int)read_string(fd, id3->id3v1buf[0], sizeof(id3->id3v1buf[0]), '\0', len); | ||
314 | read_uint8(fd,&len); | ||
315 | skipped += (int)read_string(fd, id3->id3v1buf[1], sizeof(id3->id3v1buf[1]), '\0', len); | ||
316 | read_uint8(fd,&len); | ||
317 | skipped += (int)read_string(fd, id3->id3v1buf[2], sizeof(id3->id3v1buf[2]), '\0', len); | ||
318 | read_uint8(fd,&len); | ||
319 | skipped += (int)read_string(fd, id3->id3v1buf[3], sizeof(id3->id3v1buf[3]), '\0', len); | ||
320 | skipped += 4; | ||
321 | |||
322 | DEBUGF(" title=\"%s\"\n",id3->id3v1buf[0]); | ||
323 | DEBUGF(" author=\"%s\"\n",id3->id3v1buf[1]); | ||
324 | DEBUGF(" copyright=\"%s\"\n",id3->id3v1buf[2]); | ||
325 | DEBUGF(" comment=\"%s\"\n",id3->id3v1buf[3]); | ||
326 | break; | ||
327 | |||
328 | case FOURCC('M','D','P','R'): /* Media properties */ | ||
329 | #ifdef SIMULATOR | ||
330 | read_uint16be(fd,&stream_id); | ||
331 | read_uint32be(fd,&max_bitrate); | ||
332 | read_uint32be(fd,&avg_bitrate); | ||
333 | read_uint32be(fd,&max_packet_size); | ||
334 | read_uint32be(fd,&avg_packet_size); | ||
335 | read_uint32be(fd,&start_time); | ||
336 | read_uint32be(fd,&preroll); | ||
337 | read_uint32be(fd,&duration); | ||
338 | #else | ||
339 | lseek(fd, 30, SEEK_CUR); | ||
340 | #endif | ||
341 | skipped += 30; | ||
342 | read_uint8(fd,&len); | ||
343 | skipped += 1; | ||
344 | lseek(fd, len, SEEK_CUR); /* desc */ | ||
345 | skipped += len; | ||
346 | read_uint8(fd,&len); | ||
347 | skipped += 1; | ||
348 | #ifdef SIMULATOR | ||
349 | lseek(fd, len, SEEK_CUR); /* mimetype */ | ||
350 | read_uint32be(fd,&codec_data_size); | ||
351 | #else | ||
352 | lseek(fd, len + 4, SEEK_CUR); | ||
353 | #endif | ||
354 | skipped += len + 4; | ||
355 | read_uint32be(fd,&v); | ||
356 | skipped += 4; | ||
357 | |||
358 | DEBUGF(" stream_id = 0x%04x\n",stream_id); | ||
359 | DEBUGF(" max_bitrate = %ld\n",max_bitrate); | ||
360 | DEBUGF(" avg_bitrate = %ld\n",avg_bitrate); | ||
361 | DEBUGF(" max_packet_size = %ld\n",max_packet_size); | ||
362 | DEBUGF(" avg_packet_size = %ld\n",avg_packet_size); | ||
363 | DEBUGF(" start_time = %ld\n",start_time); | ||
364 | DEBUGF(" preroll = %ld\n",preroll); | ||
365 | DEBUGF(" duration = %ld\n",duration); | ||
366 | DEBUGF(" codec_data_size = %ld\n",codec_data_size); | ||
367 | DEBUGF(" v=\"%s\"\n", fourcc2str(v)); | ||
368 | |||
369 | if (v == FOURCC('.','r','a',0xfd)) | ||
370 | { | ||
371 | int temp; | ||
372 | temp= real_read_audio_stream_info(fd, rmctx); | ||
373 | if(temp < 0) | ||
374 | return -1; | ||
375 | else | ||
376 | skipped += temp; | ||
377 | } | ||
378 | else if (v == FOURCC('L','S','D',':')) | ||
379 | { | ||
380 | DEBUGF("Real audio lossless is not supported."); | ||
381 | return -1; | ||
382 | } | ||
383 | else | ||
384 | { | ||
385 | /* We shall not abort with -1 here. *.rm file often seem | ||
386 | * to have a second media properties header that contains | ||
387 | * other metadata. */ | ||
388 | DEBUGF("Unknown header signature :\"%s\"\n", fourcc2str(v)); | ||
389 | } | ||
390 | |||
391 | |||
392 | break; | ||
393 | |||
394 | case FOURCC('D','A','T','A'): | ||
395 | read_uint32be(fd,&rmctx->nb_packets); | ||
396 | skipped += 4; | ||
397 | read_uint32be(fd,&next_data_off); | ||
398 | skipped += 4; | ||
399 | |||
400 | /*** | ||
401 | * nb_packets correction : | ||
402 | * in some samples, number of packets may not exactly form | ||
403 | * an integer number of scrambling units. This is corrected | ||
404 | * by constructing a partially filled unit out of the few | ||
405 | * remaining samples at the end of decoding. | ||
406 | ***/ | ||
407 | if(rmctx->nb_packets % rmctx->sub_packet_h) | ||
408 | rmctx->nb_packets += rmctx->sub_packet_h - (rmctx->nb_packets % rmctx->sub_packet_h); | ||
409 | |||
410 | DEBUGF(" data_nb_packets = %ld\n",rmctx->nb_packets); | ||
411 | DEBUGF(" next DATA offset = %ld\n",next_data_off); | ||
412 | header_end = 1; | ||
413 | break; | ||
414 | } | ||
415 | if(header_end) break; | ||
416 | curpos = lseek(fd, obj.size - skipped, SEEK_CUR); | ||
417 | res = real_read_object_header(fd, &obj); | ||
418 | } | ||
419 | |||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | |||
425 | bool get_rm_metadata(int fd, struct mp3entry* id3) | ||
426 | { | ||
427 | RMContext *rmctx = (RMContext*) (( (intptr_t)id3->id3v2buf + 3 ) &~ 3); | ||
428 | memset(rmctx,0,sizeof(RMContext)); | ||
429 | if(rm_parse_header(fd, rmctx, id3) < 0) | ||
430 | return false; | ||
431 | |||
432 | if (!setid3v1title(fd, id3)) { | ||
433 | /* file has no id3v1 tags, use the tags from CONT chunk */ | ||
434 | id3->title = id3->id3v1buf[0]; | ||
435 | id3->artist = id3->id3v1buf[1]; | ||
436 | id3->comment= id3->id3v1buf[3]; | ||
437 | } | ||
438 | |||
439 | switch(rmctx->codec_type) | ||
440 | { | ||
441 | case CODEC_COOK: | ||
442 | /* Already set, do nothing */ | ||
443 | break; | ||
444 | case CODEC_AAC: | ||
445 | id3->codectype = AFMT_RM_AAC; | ||
446 | break; | ||
447 | |||
448 | case CODEC_AC3: | ||
449 | id3->codectype = AFMT_RM_AC3; | ||
450 | break; | ||
451 | |||
452 | case CODEC_ATRAC: | ||
453 | id3->codectype = AFMT_RM_ATRAC3; | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | id3->channels = rmctx->nb_channels; | ||
458 | id3->extradata_size = rmctx->extradata_size; | ||
459 | id3->bitrate = rmctx->bit_rate / 1000; | ||
460 | id3->frequency = rmctx->sample_rate; | ||
461 | id3->length = rmctx->duration; | ||
462 | id3->filesize = filesize(fd); | ||
463 | return true; | ||
464 | } | ||
diff --git a/lib/rbcodec/metadata/sgc.c b/lib/rbcodec/metadata/sgc.c new file mode 100644 index 0000000000..78cacb9b1b --- /dev/null +++ b/lib/rbcodec/metadata/sgc.c | |||
@@ -0,0 +1,67 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | |||
13 | static bool parse_sgc_header(int fd, struct mp3entry* id3) | ||
14 | { | ||
15 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
16 | unsigned char* buf = (unsigned char *)id3->path; | ||
17 | |||
18 | lseek(fd, 0, SEEK_SET); | ||
19 | if (read(fd, buf, 0xA0) < 0xA0) | ||
20 | return false; | ||
21 | |||
22 | /* calculate track length with number of tracks */ | ||
23 | id3->length = buf[37] * 1000; | ||
24 | |||
25 | /* If meta info was found in the m3u skip next step */ | ||
26 | if (id3->title && id3->title[0]) return true; | ||
27 | |||
28 | char *p = id3->id3v2buf; | ||
29 | |||
30 | /* Some metadata entries have 32 bytes length */ | ||
31 | /* Game */ | ||
32 | memcpy(p, &buf[64], 32); *(p + 33) = '\0'; | ||
33 | id3->title = p; | ||
34 | p += strlen(p)+1; | ||
35 | |||
36 | /* Artist */ | ||
37 | memcpy(p, &buf[96], 32); *(p + 33) = '\0'; | ||
38 | id3->artist = p; | ||
39 | p += strlen(p)+1; | ||
40 | |||
41 | /* Copyright */ | ||
42 | memcpy(p, &buf[128], 32); *(p + 33) = '\0'; | ||
43 | id3->album = p; | ||
44 | p += strlen(p)+1; | ||
45 | return true; | ||
46 | } | ||
47 | |||
48 | |||
49 | bool get_sgc_metadata(int fd, struct mp3entry* id3) | ||
50 | { | ||
51 | uint32_t sgc_type; | ||
52 | if ((lseek(fd, 0, SEEK_SET) < 0) || | ||
53 | read_uint32be(fd, &sgc_type) != (int)sizeof(sgc_type)) | ||
54 | return false; | ||
55 | |||
56 | id3->vbr = false; | ||
57 | id3->filesize = filesize(fd); | ||
58 | /* we only render 16 bits, 44.1KHz, Stereo */ | ||
59 | id3->bitrate = 706; | ||
60 | id3->frequency = 44100; | ||
61 | |||
62 | /* Make sure this is an SGC file */ | ||
63 | if (sgc_type != FOURCC('S','G','C',0x1A)) | ||
64 | return false; | ||
65 | |||
66 | return parse_sgc_header(fd, id3); | ||
67 | } | ||
diff --git a/lib/rbcodec/metadata/sid.c b/lib/rbcodec/metadata/sid.c new file mode 100644 index 0000000000..50b879b56d --- /dev/null +++ b/lib/rbcodec/metadata/sid.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "rbunicode.h" | ||
32 | |||
33 | /* PSID metadata info is available here: | ||
34 | http://www.unusedino.de/ec64/technical/formats/sidplay.html */ | ||
35 | bool get_sid_metadata(int fd, struct mp3entry* id3) | ||
36 | { | ||
37 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
38 | unsigned char* buf = (unsigned char *)id3->path; | ||
39 | char *p; | ||
40 | |||
41 | |||
42 | if ((lseek(fd, 0, SEEK_SET) < 0) | ||
43 | || (read(fd, buf, 0x80) < 0x80)) | ||
44 | { | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | if ((memcmp(buf, "PSID", 4) != 0)) | ||
49 | { | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | p = id3->id3v2buf; | ||
54 | |||
55 | /* Copy Title (assumed max 0x1f letters + 1 zero byte) */ | ||
56 | id3->title = p; | ||
57 | buf[0x16+0x1f] = 0; | ||
58 | p = iso_decode(&buf[0x16], p, 0, strlen(&buf[0x16])+1); | ||
59 | |||
60 | /* Copy Artist (assumed max 0x1f letters + 1 zero byte) */ | ||
61 | id3->artist = p; | ||
62 | buf[0x36+0x1f] = 0; | ||
63 | p = iso_decode(&buf[0x36], p, 0, strlen(&buf[0x36])+1); | ||
64 | |||
65 | /* Copy Year (assumed max 4 letters + 1 zero byte) */ | ||
66 | buf[0x56+0x4] = 0; | ||
67 | id3->year = atoi(&buf[0x56]); | ||
68 | |||
69 | /* Copy Album (assumed max 0x1f-0x05 letters + 1 zero byte) */ | ||
70 | id3->album = p; | ||
71 | buf[0x56+0x1f] = 0; | ||
72 | iso_decode(&buf[0x5b], p, 0, strlen(&buf[0x5b])+1); | ||
73 | |||
74 | id3->bitrate = 706; | ||
75 | id3->frequency = 44100; | ||
76 | /* New idea as posted by Marco Alanen (ravon): | ||
77 | * Set the songlength in seconds to the number of subsongs | ||
78 | * so every second represents a subsong. | ||
79 | * Users can then skip the current subsong by seeking | ||
80 | * | ||
81 | * Note: the number of songs is a 16bit value at 0xE, so this code only | ||
82 | * uses the lower 8 bits of the counter. | ||
83 | */ | ||
84 | id3->length = (buf[0xf]-1)*1000; | ||
85 | id3->vbr = false; | ||
86 | id3->filesize = filesize(fd); | ||
87 | |||
88 | return true; | ||
89 | } | ||
diff --git a/lib/rbcodec/metadata/smaf.c b/lib/rbcodec/metadata/smaf.c new file mode 100644 index 0000000000..1b745d3fa1 --- /dev/null +++ b/lib/rbcodec/metadata/smaf.c | |||
@@ -0,0 +1,470 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Yoshihisa Uchida | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <inttypes.h> | ||
22 | #include <stdio.h> | ||
23 | |||
24 | #include "string-extra.h" | ||
25 | #include "system.h" | ||
26 | #include "metadata.h" | ||
27 | #include "metadata_common.h" | ||
28 | #include "metadata_parsers.h" | ||
29 | #include "rbunicode.h" | ||
30 | #include "logf.h" | ||
31 | |||
32 | static const int basebits[4] = { 4, 8, 12, 16 }; | ||
33 | |||
34 | static const int frequency[5] = { 4000, 8000, 11025, 22050, 44100 }; | ||
35 | |||
36 | static const int support_codepages[5] = { | ||
37 | #ifdef HAVE_LCD_BITMAP | ||
38 | SJIS, ISO_8859_1, -1, GB_2312, BIG_5, | ||
39 | #else | ||
40 | -1, ISO_8859_1, -1, -1, -1, | ||
41 | #endif | ||
42 | }; | ||
43 | |||
44 | /* extra codepage */ | ||
45 | #define UCS2 (NUM_CODEPAGES + 1) | ||
46 | |||
47 | /* support id3 tag */ | ||
48 | #define TAG_TITLE (('S'<<8)|'T') | ||
49 | #define TAG_ARTIST (('A'<<8)|'N') | ||
50 | #define TAG_COMPOSER (('S'<<8)|'W') | ||
51 | |||
52 | /* convert functions */ | ||
53 | #define CONVERT_SMAF_CHANNELS(c) (((c) >> 7) + 1) | ||
54 | |||
55 | |||
56 | static inline int convert_smaf_audio_basebit(unsigned int basebit) | ||
57 | { | ||
58 | if (basebit > 3) | ||
59 | return 0; | ||
60 | return basebits[basebit]; | ||
61 | } | ||
62 | |||
63 | static inline int convert_smaf_audio_frequency(unsigned int freq) | ||
64 | { | ||
65 | if (freq > 4) | ||
66 | return 0; | ||
67 | return frequency[freq]; | ||
68 | } | ||
69 | |||
70 | static int convert_smaf_codetype(unsigned int codetype) | ||
71 | { | ||
72 | if (codetype < 5) | ||
73 | return support_codepages[codetype]; | ||
74 | else if (codetype == 0x20 || codetype == 0x24) /* In Rockbox, UCS2 and UTF-16 are same. */ | ||
75 | return UCS2; | ||
76 | else if (codetype == 0x23) | ||
77 | return UTF_8; | ||
78 | else if (codetype == 0xff) | ||
79 | return ISO_8859_1; | ||
80 | return -1; | ||
81 | } | ||
82 | |||
83 | static void set_length(struct mp3entry *id3, unsigned int ch, unsigned int basebit, | ||
84 | unsigned int numbytes) | ||
85 | { | ||
86 | int bitspersample = convert_smaf_audio_basebit(basebit); | ||
87 | |||
88 | if (bitspersample != 0 && id3->frequency != 0) | ||
89 | { | ||
90 | /* Calculate track length [ms] and bitrate [kbit/s] */ | ||
91 | id3->length = (uint64_t)numbytes * 8000LL | ||
92 | / (bitspersample * CONVERT_SMAF_CHANNELS(ch) * id3->frequency); | ||
93 | id3->bitrate = bitspersample * id3->frequency / 1000; | ||
94 | } | ||
95 | |||
96 | /* output contents/wave data/id3 info (for debug) */ | ||
97 | DEBUGF("contents info ----\n"); | ||
98 | DEBUGF(" TITLE: %s\n", (id3->title)? id3->title : "(NULL)"); | ||
99 | DEBUGF(" ARTIST: %s\n", (id3->artist)? id3->artist : "(NULL)"); | ||
100 | DEBUGF(" COMPOSER: %s\n", (id3->composer)? id3->composer : "(NULL)"); | ||
101 | DEBUGF("wave data info ----\n"); | ||
102 | DEBUGF(" channels: %u\n", CONVERT_SMAF_CHANNELS(ch)); | ||
103 | DEBUGF(" bitspersample: %d\n", bitspersample); | ||
104 | DEBUGF(" numbytes; %u\n", numbytes); | ||
105 | DEBUGF("id3 info ----\n"); | ||
106 | DEBUGF(" frquency: %u\n", (unsigned int)id3->frequency); | ||
107 | DEBUGF(" bitrate: %d\n", id3->bitrate); | ||
108 | DEBUGF(" length: %u\n", (unsigned int)id3->length); | ||
109 | } | ||
110 | |||
111 | /* contents parse functions */ | ||
112 | |||
113 | /* Note: | ||
114 | * 1) When the codepage is UTF-8 or UCS2, contents data do not start BOM. | ||
115 | * 2) The byte order of contents data is big endian. | ||
116 | */ | ||
117 | |||
118 | static void decode2utf8(const unsigned char *src, unsigned char **dst, | ||
119 | int srcsize, int *dstsize, int codepage) | ||
120 | { | ||
121 | unsigned char tmpbuf[srcsize * 3 + 1]; | ||
122 | unsigned char *p; | ||
123 | int utf8size; | ||
124 | |||
125 | if (codepage < NUM_CODEPAGES) | ||
126 | p = iso_decode(src, tmpbuf, codepage, srcsize); | ||
127 | else /* codepage == UCS2 */ | ||
128 | p = utf16BEdecode(src, tmpbuf, srcsize); | ||
129 | |||
130 | *p = '\0'; | ||
131 | |||
132 | strlcpy(*dst, tmpbuf, *dstsize); | ||
133 | utf8size = (p - tmpbuf) + 1; | ||
134 | if (utf8size > *dstsize) | ||
135 | { | ||
136 | DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n", | ||
137 | utf8size, *dstsize); | ||
138 | utf8size = *dstsize; | ||
139 | } | ||
140 | *dst += utf8size; | ||
141 | *dstsize -= utf8size; | ||
142 | } | ||
143 | |||
144 | static int read_audio_track_contets(int fd, int codepage, unsigned char **dst, | ||
145 | int *dstsize) | ||
146 | { | ||
147 | /* value length <= 256 bytes */ | ||
148 | unsigned char buf[256]; | ||
149 | unsigned char *p = buf; | ||
150 | unsigned char *q = buf; | ||
151 | int datasize; | ||
152 | |||
153 | read(fd, buf, 256); | ||
154 | |||
155 | while (p - buf < 256 && *p != ',') | ||
156 | { | ||
157 | /* skip yen mark */ | ||
158 | if (codepage != UCS2) | ||
159 | { | ||
160 | if (*p == '\\') | ||
161 | p++; | ||
162 | } | ||
163 | else if (*p == '\0' && *(p+1) == '\\') | ||
164 | p += 2; | ||
165 | |||
166 | if (*p > 0x7f) | ||
167 | { | ||
168 | if (codepage == UTF_8) | ||
169 | { | ||
170 | while ((*p & MASK) != COMP) | ||
171 | *q++ = *p++; | ||
172 | } | ||
173 | #ifdef HAVE_LCD_BITMAP | ||
174 | else if (codepage == SJIS) | ||
175 | { | ||
176 | if (*p <= 0xa0 || *p >= 0xe0) | ||
177 | *q++ = *p++; | ||
178 | } | ||
179 | #endif | ||
180 | } | ||
181 | |||
182 | *q++ = *p++; | ||
183 | if (codepage == UCS2) | ||
184 | *q++ = *p++; | ||
185 | } | ||
186 | datasize = p - buf + 1; | ||
187 | lseek(fd, datasize - 256, SEEK_CUR); | ||
188 | |||
189 | if (dst != NULL) | ||
190 | decode2utf8(buf, dst, q - buf, dstsize, codepage); | ||
191 | |||
192 | return datasize; | ||
193 | } | ||
194 | |||
195 | static void read_score_track_contets(int fd, int codepage, int datasize, | ||
196 | unsigned char **dst, int *dstsize) | ||
197 | { | ||
198 | unsigned char buf[datasize]; | ||
199 | |||
200 | read(fd, buf, datasize); | ||
201 | decode2utf8(buf, dst, datasize, dstsize, codepage); | ||
202 | } | ||
203 | |||
204 | /* traverse chunk functions */ | ||
205 | |||
206 | static unsigned int search_chunk(int fd, const unsigned char *name, int nlen) | ||
207 | { | ||
208 | unsigned char buf[8]; | ||
209 | unsigned int chunksize; | ||
210 | |||
211 | while (read(fd, buf, 8) > 0) | ||
212 | { | ||
213 | chunksize = get_long_be(buf + 4); | ||
214 | if (memcmp(buf, name, nlen) == 0) | ||
215 | return chunksize; | ||
216 | |||
217 | lseek(fd, chunksize, SEEK_CUR); | ||
218 | } | ||
219 | DEBUGF("metadata error: missing '%s' chunk\n", name); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static bool parse_smaf_audio_track(int fd, struct mp3entry *id3, unsigned int datasize) | ||
224 | { | ||
225 | /* temporary buffer */ | ||
226 | unsigned char *tmp = (unsigned char*)id3->path; | ||
227 | /* contents stored buffer */ | ||
228 | unsigned char *buf = id3->id3v2buf; | ||
229 | int bufsize = sizeof(id3->id3v2buf); | ||
230 | |||
231 | unsigned int chunksize = datasize; | ||
232 | int valsize; | ||
233 | |||
234 | int codepage; | ||
235 | |||
236 | /* parse contents info */ | ||
237 | read(fd, tmp, 5); | ||
238 | codepage = convert_smaf_codetype(tmp[2]); | ||
239 | if (codepage < 0) | ||
240 | { | ||
241 | DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[2]); | ||
242 | return false; | ||
243 | } | ||
244 | |||
245 | datasize -= 5; | ||
246 | while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL) | ||
247 | && (datasize > 0 && bufsize > 0)) | ||
248 | { | ||
249 | if (read(fd, tmp, 3) <= 0) | ||
250 | return false; | ||
251 | |||
252 | if (tmp[2] != ':') | ||
253 | { | ||
254 | DEBUGF("metadata error: illegal tag: %c%c%c\n", tmp[0], tmp[1], tmp[2]); | ||
255 | return false; | ||
256 | } | ||
257 | switch ((tmp[0]<<8)|tmp[1]) | ||
258 | { | ||
259 | case TAG_TITLE: | ||
260 | id3->title = buf; | ||
261 | valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); | ||
262 | break; | ||
263 | case TAG_ARTIST: | ||
264 | id3->artist = buf; | ||
265 | valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); | ||
266 | break; | ||
267 | case TAG_COMPOSER: | ||
268 | id3->composer = buf; | ||
269 | valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); | ||
270 | break; | ||
271 | default: | ||
272 | valsize = read_audio_track_contets(fd, codepage, NULL, &bufsize); | ||
273 | break; | ||
274 | } | ||
275 | datasize -= (valsize + 3); | ||
276 | } | ||
277 | |||
278 | /* search PCM Audio Track Chunk */ | ||
279 | lseek(fd, 16 + chunksize, SEEK_SET); | ||
280 | |||
281 | chunksize = search_chunk(fd, "ATR", 3); | ||
282 | if (chunksize == 0) | ||
283 | { | ||
284 | DEBUGF("metadata error: missing PCM Audio Track Chunk\n"); | ||
285 | return false; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * get format | ||
290 | * tmp | ||
291 | * +0: Format Type | ||
292 | * +1: Sequence Type | ||
293 | * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency | ||
294 | * +3: bit 4-7: base bit | ||
295 | * +4: TimeBase_D | ||
296 | * +5: TimeBase_G | ||
297 | * | ||
298 | * Note: If PCM Audio Track does not include Sequence Data Chunk, | ||
299 | * tmp+6 is the start position of Wave Data Chunk. | ||
300 | */ | ||
301 | read(fd, tmp, 6); | ||
302 | |||
303 | /* search Wave Data Chunk */ | ||
304 | chunksize = search_chunk(fd, "Awa", 3); | ||
305 | if (chunksize == 0) | ||
306 | { | ||
307 | DEBUGF("metadata error: missing Wave Data Chunk\n"); | ||
308 | return false; | ||
309 | } | ||
310 | |||
311 | /* set track length and bitrate */ | ||
312 | id3->frequency = convert_smaf_audio_frequency(tmp[2] & 0x0f); | ||
313 | set_length(id3, tmp[2], tmp[3] >> 4, chunksize); | ||
314 | return true; | ||
315 | } | ||
316 | |||
317 | static bool parse_smaf_score_track(int fd, struct mp3entry *id3) | ||
318 | { | ||
319 | /* temporary buffer */ | ||
320 | unsigned char *tmp = (unsigned char*)id3->path; | ||
321 | unsigned char *p = tmp; | ||
322 | /* contents stored buffer */ | ||
323 | unsigned char *buf = id3->id3v2buf; | ||
324 | int bufsize = sizeof(id3->id3v2buf); | ||
325 | |||
326 | unsigned int chunksize; | ||
327 | unsigned int datasize; | ||
328 | int valsize; | ||
329 | |||
330 | int codepage; | ||
331 | |||
332 | /* parse Optional Data Chunk */ | ||
333 | read(fd, tmp, 21); | ||
334 | if (memcmp(tmp + 5, "OPDA", 4) != 0) | ||
335 | { | ||
336 | DEBUGF("metadata error: missing Optional Data Chunk\n"); | ||
337 | return false; | ||
338 | } | ||
339 | |||
340 | /* Optional Data Chunk size */ | ||
341 | chunksize = get_long_be(tmp + 9); | ||
342 | |||
343 | /* parse Data Chunk */ | ||
344 | if (memcmp(tmp + 13, "Dch", 3) != 0) | ||
345 | { | ||
346 | DEBUGF("metadata error: missing Data Chunk\n"); | ||
347 | return false; | ||
348 | } | ||
349 | |||
350 | codepage = convert_smaf_codetype(tmp[16]); | ||
351 | if (codepage < 0) | ||
352 | { | ||
353 | DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[16]); | ||
354 | return false; | ||
355 | } | ||
356 | |||
357 | /* Data Chunk size */ | ||
358 | datasize = get_long_be(tmp + 17); | ||
359 | while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL) | ||
360 | && (datasize > 0 && bufsize > 0)) | ||
361 | { | ||
362 | if (read(fd, tmp, 4) <= 0) | ||
363 | return false; | ||
364 | |||
365 | valsize = (tmp[2] << 8) | tmp[3]; | ||
366 | datasize -= (valsize + 4); | ||
367 | switch ((tmp[0]<<8)|tmp[1]) | ||
368 | { | ||
369 | case TAG_TITLE: | ||
370 | id3->title = buf; | ||
371 | read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); | ||
372 | break; | ||
373 | case TAG_ARTIST: | ||
374 | id3->artist = buf; | ||
375 | read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); | ||
376 | break; | ||
377 | case TAG_COMPOSER: | ||
378 | id3->composer = buf; | ||
379 | read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); | ||
380 | break; | ||
381 | default: | ||
382 | lseek(fd, valsize, SEEK_CUR); | ||
383 | break; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | /* search Score Track Chunk */ | ||
388 | lseek(fd, 29 + chunksize, SEEK_SET); | ||
389 | |||
390 | if (search_chunk(fd, "MTR", 3) == 0) | ||
391 | { | ||
392 | DEBUGF("metadata error: missing Score Track Chunk\n"); | ||
393 | return false; | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * search next chunk | ||
398 | * usually, next chunk ('M***') found within 40 bytes. | ||
399 | */ | ||
400 | chunksize = 40; | ||
401 | read(fd, tmp, chunksize); | ||
402 | |||
403 | tmp[chunksize] = 'M'; /* stopper */ | ||
404 | while (*p != 'M') | ||
405 | p++; | ||
406 | |||
407 | chunksize -= (p - tmp); | ||
408 | if (chunksize == 0) | ||
409 | { | ||
410 | DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk"); | ||
411 | return false; | ||
412 | } | ||
413 | |||
414 | /* search Score Track Stream PCM Data Chunk */ | ||
415 | lseek(fd, -chunksize, SEEK_CUR); | ||
416 | if (search_chunk(fd, "Mtsp", 4) == 0) | ||
417 | { | ||
418 | DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk\n"); | ||
419 | return false; | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * parse Score Track Stream Wave Data Chunk | ||
424 | * tmp | ||
425 | * +4-7: chunk size (WaveType(3bytes) + wave data count) | ||
426 | * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit | ||
427 | * +9: frequency (MSB) | ||
428 | * +10: frequency (LSB) | ||
429 | */ | ||
430 | read(fd, tmp, 11); | ||
431 | if (memcmp(tmp, "Mwa", 3) != 0) | ||
432 | { | ||
433 | DEBUGF("metadata error: missing Score Track Stream Wave Data Chunk\n"); | ||
434 | return false; | ||
435 | } | ||
436 | |||
437 | /* set track length and bitrate */ | ||
438 | id3->frequency = (tmp[9] << 8) | tmp[10]; | ||
439 | set_length(id3, tmp[8], tmp[8] & 0x0f, get_long_be(tmp + 4) - 3); | ||
440 | return true; | ||
441 | } | ||
442 | |||
443 | bool get_smaf_metadata(int fd, struct mp3entry* id3) | ||
444 | { | ||
445 | /* temporary buffer */ | ||
446 | unsigned char *tmp = (unsigned char *)id3->path; | ||
447 | unsigned int chunksize; | ||
448 | |||
449 | id3->title = NULL; | ||
450 | id3->artist = NULL; | ||
451 | id3->composer = NULL; | ||
452 | |||
453 | id3->vbr = false; /* All SMAF files are CBR */ | ||
454 | id3->filesize = filesize(fd); | ||
455 | |||
456 | /* check File Chunk and Contents Info Chunk */ | ||
457 | lseek(fd, 0, SEEK_SET); | ||
458 | read(fd, tmp, 16); | ||
459 | if ((memcmp(tmp, "MMMD", 4) != 0) || (memcmp(tmp + 8, "CNTI", 4) != 0)) | ||
460 | { | ||
461 | DEBUGF("metadata error: does not smaf format\n"); | ||
462 | return false; | ||
463 | } | ||
464 | |||
465 | chunksize = get_long_be(tmp + 12); | ||
466 | if (chunksize > 5) | ||
467 | return parse_smaf_audio_track(fd, id3, chunksize); | ||
468 | |||
469 | return parse_smaf_score_track(fd, id3); | ||
470 | } | ||
diff --git a/lib/rbcodec/metadata/spc.c b/lib/rbcodec/metadata/spc.c new file mode 100644 index 0000000000..1c0206205d --- /dev/null +++ b/lib/rbcodec/metadata/spc.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "debug.h" | ||
32 | #include "rbunicode.h" | ||
33 | |||
34 | bool get_spc_metadata(int fd, struct mp3entry* id3) | ||
35 | { | ||
36 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
37 | unsigned char * buf = (unsigned char *)id3->path; | ||
38 | char * p; | ||
39 | |||
40 | unsigned long length; | ||
41 | unsigned long fade; | ||
42 | bool isbinary = true; | ||
43 | int i; | ||
44 | |||
45 | /* try to get the ID666 tag */ | ||
46 | if ((lseek(fd, 0x2e, SEEK_SET) < 0) | ||
47 | || (read(fd, buf, 0xD2) < 0xD2)) | ||
48 | { | ||
49 | DEBUGF("lseek or read failed\n"); | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | p = id3->id3v2buf; | ||
54 | |||
55 | id3->title = p; | ||
56 | buf[31] = 0; | ||
57 | p = iso_decode(buf, p, 0, 32); | ||
58 | buf += 32; | ||
59 | |||
60 | id3->album = p; | ||
61 | buf[31] = 0; | ||
62 | p = iso_decode(buf, p, 0, 32); | ||
63 | buf += 48; | ||
64 | |||
65 | id3->comment = p; | ||
66 | buf[31] = 0; | ||
67 | p = iso_decode(buf, p, 0, 32); | ||
68 | buf += 32; | ||
69 | |||
70 | /* Date check */ | ||
71 | if(buf[2] == '/' && buf[5] == '/') | ||
72 | isbinary = false; | ||
73 | |||
74 | /* Reserved bytes check */ | ||
75 | if(buf[0xD2 - 0x2E - 112] >= '0' && | ||
76 | buf[0xD2 - 0x2E - 112] <= '9' && | ||
77 | buf[0xD3 - 0x2E - 112] == 0x00) | ||
78 | isbinary = false; | ||
79 | |||
80 | /* is length & fade only digits? */ | ||
81 | for (i=0;i<8 && ( | ||
82 | (buf[0xA9 - 0x2E - 112+i]>='0'&&buf[0xA9 - 0x2E - 112+i]<='9') || | ||
83 | buf[0xA9 - 0x2E - 112+i]=='\0'); | ||
84 | i++); | ||
85 | if (i==8) isbinary = false; | ||
86 | |||
87 | if(isbinary) { | ||
88 | id3->year = buf[0] | (buf[1]<<8); | ||
89 | buf += 11; | ||
90 | |||
91 | length = (buf[0] | (buf[1]<<8) | (buf[2]<<16)) * 1000; | ||
92 | buf += 3; | ||
93 | |||
94 | fade = (buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24)); | ||
95 | buf += 4; | ||
96 | } else { | ||
97 | char tbuf[6]; | ||
98 | |||
99 | buf += 6; | ||
100 | buf[4] = 0; | ||
101 | id3->year = atoi(buf); | ||
102 | buf += 5; | ||
103 | |||
104 | memcpy(tbuf, buf, 3); | ||
105 | tbuf[3] = 0; | ||
106 | length = atoi(tbuf) * 1000; | ||
107 | buf += 3; | ||
108 | |||
109 | memcpy(tbuf, buf, 5); | ||
110 | tbuf[5] = 0; | ||
111 | fade = atoi(tbuf); | ||
112 | buf += 5; | ||
113 | } | ||
114 | |||
115 | id3->artist = p; | ||
116 | buf[31] = 0; | ||
117 | iso_decode(buf, p, 0, 32); | ||
118 | |||
119 | if (length==0) { | ||
120 | length=3*60*1000; /* 3 minutes */ | ||
121 | fade=5*1000; /* 5 seconds */ | ||
122 | } | ||
123 | |||
124 | id3->length = length+fade; | ||
125 | |||
126 | id3->filesize = filesize(fd); | ||
127 | id3->genre_string = id3_get_num_genre(36); | ||
128 | |||
129 | return true; | ||
130 | } | ||
diff --git a/lib/rbcodec/metadata/tta.c b/lib/rbcodec/metadata/tta.c new file mode 100644 index 0000000000..1d3d95f118 --- /dev/null +++ b/lib/rbcodec/metadata/tta.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Yoshihisa Uchida | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | #define TTA1_SIGN 0x31415454 | ||
34 | |||
35 | #define TTA_HEADER_ID 0 | ||
36 | #define TTA_HEADER_AUDIO_FORMAT (TTA_HEADER_ID + sizeof(unsigned int)) | ||
37 | #define TTA_HEADER_NUM_CHANNELS (TTA_HEADER_AUDIO_FORMAT + sizeof(unsigned short)) | ||
38 | #define TTA_HEADER_BITS_PER_SAMPLE (TTA_HEADER_NUM_CHANNELS + sizeof(unsigned short)) | ||
39 | #define TTA_HEADER_SAMPLE_RATE (TTA_HEADER_BITS_PER_SAMPLE + sizeof(unsigned short)) | ||
40 | #define TTA_HEADER_DATA_LENGTH (TTA_HEADER_SAMPLE_RATE + sizeof(unsigned int)) | ||
41 | #define TTA_HEADER_CRC32 (TTA_HEADER_DATA_LENGTH + sizeof(unsigned int)) | ||
42 | #define TTA_HEADER_SIZE (TTA_HEADER_CRC32 + sizeof(unsigned int)) | ||
43 | |||
44 | #define TTA_HEADER_GETTER_ID(x) get_long_le(x) | ||
45 | #define TTA_HEADER_GETTER_AUDIO_FORMAT(x) get_short_le(x) | ||
46 | #define TTA_HEADER_GETTER_NUM_CHANNELS(x) get_short_le(x) | ||
47 | #define TTA_HEADER_GETTER_BITS_PER_SAMPLE(x) get_short_le(x) | ||
48 | #define TTA_HEADER_GETTER_SAMPLE_RATE(x) get_long_le(x) | ||
49 | #define TTA_HEADER_GETTER_DATA_LENGTH(x) get_long_le(x) | ||
50 | #define TTA_HEADER_GETTER_CRC32(x) get_long_le(x) | ||
51 | |||
52 | #define GET_HEADER(x, tag) TTA_HEADER_GETTER_ ## tag((x) + TTA_HEADER_ ## tag) | ||
53 | |||
54 | static void read_id3_tags(int fd, struct mp3entry* id3) | ||
55 | { | ||
56 | id3->title = NULL; | ||
57 | id3->filesize = filesize(fd); | ||
58 | id3->id3v2len = getid3v2len(fd); | ||
59 | id3->tracknum = 0; | ||
60 | id3->discnum = 0; | ||
61 | id3->vbr = false; /* All TTA files are CBR */ | ||
62 | |||
63 | /* first get id3v2 tags. if no id3v2 tags ware found, get id3v1 tags */ | ||
64 | if (id3->id3v2len) | ||
65 | { | ||
66 | setid3v2title(fd, id3); | ||
67 | id3->first_frame_offset = id3->id3v2len; | ||
68 | return; | ||
69 | } | ||
70 | setid3v1title(fd, id3); | ||
71 | } | ||
72 | |||
73 | bool get_tta_metadata(int fd, struct mp3entry* id3) | ||
74 | { | ||
75 | unsigned char ttahdr[TTA_HEADER_SIZE]; | ||
76 | unsigned int datasize; | ||
77 | unsigned int origsize; | ||
78 | int bps; | ||
79 | |||
80 | lseek(fd, 0, SEEK_SET); | ||
81 | |||
82 | /* read id3 tags */ | ||
83 | read_id3_tags(fd, id3); | ||
84 | lseek(fd, id3->id3v2len, SEEK_SET); | ||
85 | |||
86 | /* read TTA header */ | ||
87 | if (read(fd, ttahdr, TTA_HEADER_SIZE) < 0) | ||
88 | return false; | ||
89 | |||
90 | /* check for TTA3 signature */ | ||
91 | if ((GET_HEADER(ttahdr, ID)) != TTA1_SIGN) | ||
92 | return false; | ||
93 | |||
94 | /* skip check CRC */ | ||
95 | |||
96 | id3->channels = (GET_HEADER(ttahdr, NUM_CHANNELS)); | ||
97 | id3->frequency = (GET_HEADER(ttahdr, SAMPLE_RATE)); | ||
98 | id3->length = ((GET_HEADER(ttahdr, DATA_LENGTH)) / id3->frequency) * 1000LL; | ||
99 | bps = (GET_HEADER(ttahdr, BITS_PER_SAMPLE)); | ||
100 | |||
101 | datasize = id3->filesize - id3->first_frame_offset; | ||
102 | origsize = (GET_HEADER(ttahdr, DATA_LENGTH)) * ((bps + 7) / 8) * id3->channels; | ||
103 | |||
104 | id3->bitrate = (int) ((uint64_t) datasize * id3->frequency * id3->channels * bps | ||
105 | / (origsize * 1000LL)); | ||
106 | |||
107 | /* output header info (for debug) */ | ||
108 | DEBUGF("TTA header info ----\n"); | ||
109 | DEBUGF("id: %x\n", (unsigned int)(GET_HEADER(ttahdr, ID))); | ||
110 | DEBUGF("channels: %d\n", id3->channels); | ||
111 | DEBUGF("frequency: %ld\n", id3->frequency); | ||
112 | DEBUGF("length: %ld\n", id3->length); | ||
113 | DEBUGF("bitrate: %d\n", id3->bitrate); | ||
114 | DEBUGF("bits per sample: %d\n", bps); | ||
115 | DEBUGF("compressed size: %d\n", datasize); | ||
116 | DEBUGF("original size: %d\n", origsize); | ||
117 | DEBUGF("id3----\n"); | ||
118 | DEBUGF("artist: %s\n", id3->artist); | ||
119 | DEBUGF("title: %s\n", id3->title); | ||
120 | DEBUGF("genre: %s\n", id3->genre_string); | ||
121 | |||
122 | return true; | ||
123 | } | ||
diff --git a/lib/rbcodec/metadata/vgm.c b/lib/rbcodec/metadata/vgm.c new file mode 100644 index 0000000000..9ea95b3939 --- /dev/null +++ b/lib/rbcodec/metadata/vgm.c | |||
@@ -0,0 +1,195 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <ctype.h> | ||
5 | #include <inttypes.h> | ||
6 | |||
7 | #include "system.h" | ||
8 | #include "metadata.h" | ||
9 | #include "metadata_common.h" | ||
10 | #include "metadata_parsers.h" | ||
11 | #include "rbunicode.h" | ||
12 | |||
13 | /* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ | ||
14 | |||
15 | typedef unsigned char byte; | ||
16 | |||
17 | enum { header_size = 0x40 }; | ||
18 | enum { max_field = 64 }; | ||
19 | |||
20 | struct header_t | ||
21 | { | ||
22 | char tag [4]; | ||
23 | byte data_size [4]; | ||
24 | byte version [4]; | ||
25 | byte psg_rate [4]; | ||
26 | byte ym2413_rate [4]; | ||
27 | byte gd3_offset [4]; | ||
28 | byte track_duration [4]; | ||
29 | byte loop_offset [4]; | ||
30 | byte loop_duration [4]; | ||
31 | byte frame_rate [4]; | ||
32 | byte noise_feedback [2]; | ||
33 | byte noise_width; | ||
34 | byte unused1; | ||
35 | byte ym2612_rate [4]; | ||
36 | byte ym2151_rate [4]; | ||
37 | byte data_offset [4]; | ||
38 | byte unused2 [8]; | ||
39 | }; | ||
40 | |||
41 | static byte const* skip_gd3_str( byte const* in, byte const* end ) | ||
42 | { | ||
43 | while ( end - in >= 2 ) | ||
44 | { | ||
45 | in += 2; | ||
46 | if ( !(in [-2] | in [-1]) ) | ||
47 | break; | ||
48 | } | ||
49 | return in; | ||
50 | } | ||
51 | |||
52 | static byte const* get_gd3_str( byte const* in, byte const* end, char* field ) | ||
53 | { | ||
54 | byte const* mid = skip_gd3_str( in, end ); | ||
55 | int len = (mid - in) / 2 - 1; | ||
56 | if ( field && len > 0 ) | ||
57 | { | ||
58 | len = len < (int) max_field ? len : (int) max_field; | ||
59 | |||
60 | field [len] = 0; | ||
61 | /* Conver to utf8 */ | ||
62 | utf16LEdecode( in, field, len ); | ||
63 | |||
64 | /* Copy string back to id3v2buf */ | ||
65 | strcpy( (char*) in, field ); | ||
66 | } | ||
67 | return mid; | ||
68 | } | ||
69 | |||
70 | static byte const* get_gd3_pair( byte const* in, byte const* end, char* field ) | ||
71 | { | ||
72 | return skip_gd3_str( get_gd3_str( in, end, field ), end ); | ||
73 | } | ||
74 | |||
75 | static void parse_gd3( byte const* in, byte const* end, struct mp3entry* id3 ) | ||
76 | { | ||
77 | char* p = id3->path; | ||
78 | id3->title = (char *) in; | ||
79 | in = get_gd3_pair( in, end, p ); /* Song */ | ||
80 | |||
81 | id3->album = (char *) in; | ||
82 | in = get_gd3_pair( in, end, p ); /* Game */ | ||
83 | |||
84 | in = get_gd3_pair( in, end, NULL ); /* System */ | ||
85 | |||
86 | id3->artist = (char *) in; | ||
87 | in = get_gd3_pair( in, end, p ); /* Author */ | ||
88 | |||
89 | #if MEMORYSIZE > 2 | ||
90 | in = get_gd3_str ( in, end, NULL ); /* Copyright */ | ||
91 | in = get_gd3_pair( in, end, NULL ); /* Dumper */ | ||
92 | |||
93 | id3->comment = (char *) in; | ||
94 | in = get_gd3_str ( in, end, p ); /* Comment */ | ||
95 | #endif | ||
96 | } | ||
97 | |||
98 | int const gd3_header_size = 12; | ||
99 | |||
100 | static long check_gd3_header( byte* h, long remain ) | ||
101 | { | ||
102 | if ( remain < gd3_header_size ) return 0; | ||
103 | if ( memcmp( h, "Gd3 ", 4 ) ) return 0; | ||
104 | if ( get_long_le( h + 4 ) >= 0x200 ) return 0; | ||
105 | |||
106 | long gd3_size = get_long_le( h + 8 ); | ||
107 | if ( gd3_size > remain - gd3_header_size ) | ||
108 | gd3_size = remain - gd3_header_size; | ||
109 | |||
110 | return gd3_size; | ||
111 | } | ||
112 | |||
113 | static void get_vgm_length( struct header_t* h, struct mp3entry* id3 ) | ||
114 | { | ||
115 | long length = get_long_le( h->track_duration ) * 10 / 441; | ||
116 | if ( length > 0 ) | ||
117 | { | ||
118 | long loop_length = 0, intro_length = 0; | ||
119 | long loop = get_long_le( h->loop_duration ); | ||
120 | if ( loop > 0 && get_long_le( h->loop_offset ) ) | ||
121 | { | ||
122 | loop_length = loop * 10 / 441; | ||
123 | intro_length = length - loop_length; | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | intro_length = length; /* make it clear that track is no longer than length */ | ||
128 | loop_length = 0; | ||
129 | } | ||
130 | |||
131 | id3->length = intro_length + 2 * loop_length; /* intro + 2 loops */ | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | id3->length = 150 * 1000; /* 2.5 minutes */ | ||
136 | } | ||
137 | |||
138 | bool get_vgm_metadata(int fd, struct mp3entry* id3) | ||
139 | { | ||
140 | /* Use the id3v2 part of the id3 structure as a temporary buffer */ | ||
141 | unsigned char* buf = (unsigned char *)id3->id3v2buf; | ||
142 | int read_bytes; | ||
143 | |||
144 | memset(buf, 0, ID3V2_BUF_SIZE); | ||
145 | if ((lseek(fd, 0, SEEK_SET) < 0) | ||
146 | || ((read_bytes = read(fd, buf, header_size)) < header_size)) | ||
147 | { | ||
148 | return false; | ||
149 | } | ||
150 | |||
151 | id3->vbr = false; | ||
152 | id3->filesize = filesize(fd); | ||
153 | |||
154 | id3->bitrate = 706; | ||
155 | id3->frequency = 44100; | ||
156 | |||
157 | /* If file is gzipped, will get metadata later */ | ||
158 | if (memcmp(buf, "Vgm ", 4)) | ||
159 | { | ||
160 | /* We must set a default song length here because | ||
161 | the codec can't do it anymore */ | ||
162 | id3->length = 150 * 1000; /* 2.5 minutes */ | ||
163 | return true; | ||
164 | } | ||
165 | |||
166 | /* Get song length from header */ | ||
167 | struct header_t* header = (struct header_t*) buf; | ||
168 | get_vgm_length( header, id3 ); | ||
169 | |||
170 | long gd3_offset = get_long_le( header->gd3_offset ) - 0x2C; | ||
171 | |||
172 | /* No gd3 tag found */ | ||
173 | if ( gd3_offset < 0 ) | ||
174 | return true; | ||
175 | |||
176 | /* Seek to gd3 offset and read as | ||
177 | many bytes posible */ | ||
178 | gd3_offset = id3->filesize - (header_size + gd3_offset); | ||
179 | if ((lseek(fd, -gd3_offset, SEEK_END) < 0) | ||
180 | || ((read_bytes = read(fd, buf, ID3V2_BUF_SIZE)) <= 0)) | ||
181 | return true; | ||
182 | |||
183 | byte* gd3 = buf; | ||
184 | long gd3_size = check_gd3_header( gd3, read_bytes ); | ||
185 | |||
186 | /* GD3 tag is zero */ | ||
187 | if ( gd3_size == 0 ) | ||
188 | return true; | ||
189 | |||
190 | /* Finally, parse gd3 tag */ | ||
191 | if ( gd3 ) | ||
192 | parse_gd3( gd3 + gd3_header_size, gd3 + read_bytes, id3 ); | ||
193 | |||
194 | return true; | ||
195 | } | ||
diff --git a/lib/rbcodec/metadata/vorbis.c b/lib/rbcodec/metadata/vorbis.c new file mode 100644 index 0000000000..58bd781873 --- /dev/null +++ b/lib/rbcodec/metadata/vorbis.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "structec.h" | ||
32 | |||
33 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
34 | /*#define LOGF_ENABLE*/ | ||
35 | #include "logf.h" | ||
36 | |||
37 | struct file | ||
38 | { | ||
39 | int fd; | ||
40 | bool packet_ended; | ||
41 | long packet_remaining; | ||
42 | }; | ||
43 | |||
44 | |||
45 | /* Read an Ogg page header. file->packet_remaining is set to the size of the | ||
46 | * first packet on the page; file->packet_ended is set to true if the packet | ||
47 | * ended on the current page. Returns true if the page header was | ||
48 | * successfully read. | ||
49 | */ | ||
50 | static bool file_read_page_header(struct file* file) | ||
51 | { | ||
52 | unsigned char buffer[64]; | ||
53 | ssize_t table_left; | ||
54 | |||
55 | /* Size of page header without segment table */ | ||
56 | if (read(file->fd, buffer, 27) != 27) | ||
57 | { | ||
58 | return false; | ||
59 | } | ||
60 | |||
61 | if (memcmp("OggS", buffer, 4)) | ||
62 | { | ||
63 | return false; | ||
64 | } | ||
65 | |||
66 | /* Skip pattern (4), version (1), flags (1), granule position (8), | ||
67 | * serial (4), pageno (4), checksum (4) | ||
68 | */ | ||
69 | table_left = buffer[26]; | ||
70 | file->packet_remaining = 0; | ||
71 | |||
72 | /* Read segment table for the first packet */ | ||
73 | do | ||
74 | { | ||
75 | ssize_t count = MIN(sizeof(buffer), (size_t) table_left); | ||
76 | int i; | ||
77 | |||
78 | if (read(file->fd, buffer, count) < count) | ||
79 | { | ||
80 | return false; | ||
81 | } | ||
82 | |||
83 | table_left -= count; | ||
84 | |||
85 | for (i = 0; i < count; i++) | ||
86 | { | ||
87 | file->packet_remaining += buffer[i]; | ||
88 | |||
89 | if (buffer[i] < 255) | ||
90 | { | ||
91 | file->packet_ended = true; | ||
92 | |||
93 | /* Skip remainder of the table */ | ||
94 | if (lseek(file->fd, table_left, SEEK_CUR) < 0) | ||
95 | { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | table_left = 0; | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | while (table_left > 0); | ||
105 | |||
106 | return true; | ||
107 | } | ||
108 | |||
109 | |||
110 | /* Read (up to) buffer_size of data from the file. If buffer is NULL, just | ||
111 | * skip ahead buffer_size bytes (like lseek). Returns number of bytes read, | ||
112 | * 0 if there is no more data to read (in the packet or the file), < 0 if a | ||
113 | * read error occurred. | ||
114 | */ | ||
115 | static ssize_t file_read(struct file* file, void* buffer, size_t buffer_size) | ||
116 | { | ||
117 | ssize_t done = 0; | ||
118 | ssize_t count = -1; | ||
119 | |||
120 | do | ||
121 | { | ||
122 | if (file->packet_remaining <= 0) | ||
123 | { | ||
124 | if (file->packet_ended) | ||
125 | { | ||
126 | break; | ||
127 | } | ||
128 | |||
129 | if (!file_read_page_header(file)) | ||
130 | { | ||
131 | count = -1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | count = MIN(buffer_size, (size_t) file->packet_remaining); | ||
137 | |||
138 | if (buffer) | ||
139 | { | ||
140 | count = read(file->fd, buffer, count); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | if (lseek(file->fd, count, SEEK_CUR) < 0) | ||
145 | { | ||
146 | count = -1; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | if (count <= 0) | ||
151 | { | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | if (buffer) | ||
156 | { | ||
157 | buffer += count; | ||
158 | } | ||
159 | |||
160 | buffer_size -= count; | ||
161 | done += count; | ||
162 | file->packet_remaining -= count; | ||
163 | } | ||
164 | while (buffer_size > 0); | ||
165 | |||
166 | return (count < 0 ? count : done); | ||
167 | } | ||
168 | |||
169 | |||
170 | /* Read an int32 from file. Returns false if a read error occurred. | ||
171 | */ | ||
172 | static bool file_read_int32(struct file* file, int32_t* value) | ||
173 | { | ||
174 | char buf[sizeof(int32_t)]; | ||
175 | |||
176 | if (file_read(file, buf, sizeof(buf)) < (ssize_t) sizeof(buf)) | ||
177 | { | ||
178 | return false; | ||
179 | } | ||
180 | |||
181 | *value = get_long_le(buf); | ||
182 | return true; | ||
183 | } | ||
184 | |||
185 | |||
186 | /* Read a string from the file. Read up to buffer_size bytes, or, if eos | ||
187 | * != -1, until the eos character is found (eos is not stored in buf, | ||
188 | * unless it is nil). Writes up to buffer_size chars to buf, always | ||
189 | * terminating with a nil. Returns number of chars read or < 0 if a read | ||
190 | * error occurred. | ||
191 | * | ||
192 | * Unfortunately this is a slightly modified copy of read_string() in | ||
193 | * metadata_common.c... | ||
194 | */ | ||
195 | static long file_read_string(struct file* file, char* buffer, | ||
196 | long buffer_size, int eos, long size) | ||
197 | { | ||
198 | long read_bytes = 0; | ||
199 | |||
200 | while (size > 0) | ||
201 | { | ||
202 | char c; | ||
203 | |||
204 | if (file_read(file, &c, 1) != 1) | ||
205 | { | ||
206 | read_bytes = -1; | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | read_bytes++; | ||
211 | size--; | ||
212 | |||
213 | if ((eos != -1) && (eos == (unsigned char) c)) | ||
214 | { | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | if (buffer_size > 1) | ||
219 | { | ||
220 | *buffer++ = c; | ||
221 | buffer_size--; | ||
222 | } | ||
223 | else if (eos == -1) | ||
224 | { | ||
225 | /* No point in reading any more, skip remaining data */ | ||
226 | if (file_read(file, NULL, size) < 0) | ||
227 | { | ||
228 | read_bytes = -1; | ||
229 | } | ||
230 | else | ||
231 | { | ||
232 | read_bytes += size; | ||
233 | } | ||
234 | |||
235 | break; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | *buffer = 0; | ||
240 | return read_bytes; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* Init struct file for reading from fd. type is the AFMT_* codec type of | ||
245 | * the file, and determines if Ogg pages are to be read. remaining is the | ||
246 | * max amount to read if codec type is FLAC; it is ignored otherwise. | ||
247 | * Returns true if the file was successfully initialized. | ||
248 | */ | ||
249 | static bool file_init(struct file* file, int fd, int type, int remaining) | ||
250 | { | ||
251 | memset(file, 0, sizeof(*file)); | ||
252 | file->fd = fd; | ||
253 | |||
254 | if (type == AFMT_OGG_VORBIS || type == AFMT_SPEEX) | ||
255 | { | ||
256 | if (!file_read_page_header(file)) | ||
257 | { | ||
258 | return false; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if (type == AFMT_OGG_VORBIS) | ||
263 | { | ||
264 | char buffer[7]; | ||
265 | |||
266 | /* Read packet header (type and id string) */ | ||
267 | if (file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) | ||
268 | { | ||
269 | return false; | ||
270 | } | ||
271 | |||
272 | /* The first byte of a packet is the packet type; comment packets | ||
273 | * are type 3. | ||
274 | */ | ||
275 | if (buffer[0] != 3) | ||
276 | { | ||
277 | return false; | ||
278 | } | ||
279 | } | ||
280 | else if (type == AFMT_FLAC) | ||
281 | { | ||
282 | file->packet_remaining = remaining; | ||
283 | file->packet_ended = true; | ||
284 | } | ||
285 | |||
286 | return true; | ||
287 | } | ||
288 | |||
289 | |||
290 | /* Read the items in a Vorbis comment packet. For Ogg files, the file must | ||
291 | * be located on a page start, for other files, the beginning of the comment | ||
292 | * data (i.e., the vendor string length). Returns total size of the | ||
293 | * comments, or 0 if there was a read error. | ||
294 | */ | ||
295 | long read_vorbis_tags(int fd, struct mp3entry *id3, | ||
296 | long tag_remaining) | ||
297 | { | ||
298 | struct file file; | ||
299 | char *buf = id3->id3v2buf; | ||
300 | int32_t comment_count; | ||
301 | int32_t len; | ||
302 | long comment_size = 0; | ||
303 | int buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); | ||
304 | int i; | ||
305 | |||
306 | if (!file_init(&file, fd, id3->codectype, tag_remaining)) | ||
307 | { | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | /* Skip vendor string */ | ||
312 | |||
313 | if (!file_read_int32(&file, &len) || (file_read(&file, NULL, len) < 0)) | ||
314 | { | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | if (!file_read_int32(&file, &comment_count)) | ||
319 | { | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | comment_size += 4 + len + 4; | ||
324 | |||
325 | for (i = 0; i < comment_count && file.packet_remaining > 0; i++) | ||
326 | { | ||
327 | char name[TAG_NAME_LENGTH]; | ||
328 | int32_t read_len; | ||
329 | |||
330 | if (!file_read_int32(&file, &len)) | ||
331 | { | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | comment_size += 4 + len; | ||
336 | read_len = file_read_string(&file, name, sizeof(name), '=', len); | ||
337 | |||
338 | if (read_len < 0) | ||
339 | { | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | len -= read_len; | ||
344 | read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len); | ||
345 | |||
346 | if (read_len < 0) | ||
347 | { | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | logf("Vorbis comment %d: %s=%s", i, name, id3->path); | ||
352 | |||
353 | /* Is it an embedded cuesheet? */ | ||
354 | if (!strcasecmp(name, "CUESHEET")) | ||
355 | { | ||
356 | id3->has_embedded_cuesheet = true; | ||
357 | id3->embedded_cuesheet.pos = lseek(file.fd, 0, SEEK_CUR) - read_len; | ||
358 | id3->embedded_cuesheet.size = len; | ||
359 | id3->embedded_cuesheet.encoding = CHAR_ENC_UTF_8; | ||
360 | } | ||
361 | else | ||
362 | { | ||
363 | len = parse_tag(name, id3->path, id3, buf, buf_remaining, | ||
364 | TAGTYPE_VORBIS); | ||
365 | } | ||
366 | |||
367 | buf += len; | ||
368 | buf_remaining -= len; | ||
369 | } | ||
370 | |||
371 | /* Skip to the end of the block (needed by FLAC) */ | ||
372 | if (file.packet_remaining) | ||
373 | { | ||
374 | if (file_read(&file, NULL, file.packet_remaining) < 0) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | return comment_size; | ||
381 | } | ||
diff --git a/lib/rbcodec/metadata/vox.c b/lib/rbcodec/metadata/vox.c new file mode 100644 index 0000000000..f6bc849a88 --- /dev/null +++ b/lib/rbcodec/metadata/vox.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Yoshihisa Uchida | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | bool get_vox_metadata(int fd, struct mp3entry* id3) | ||
34 | { | ||
35 | /* | ||
36 | * vox is headerless format | ||
37 | * | ||
38 | * frequency: 8000 Hz | ||
39 | * channels: mono | ||
40 | * bitspersample: 4 | ||
41 | */ | ||
42 | id3->frequency = 8000; | ||
43 | id3->bitrate = 8000 * 4 / 1000; | ||
44 | id3->vbr = false; /* All VOX files are CBR */ | ||
45 | id3->filesize = filesize(fd); | ||
46 | id3->length = id3->filesize >> 2; | ||
47 | |||
48 | return true; | ||
49 | } | ||
diff --git a/lib/rbcodec/metadata/wave.c b/lib/rbcodec/metadata/wave.c new file mode 100644 index 0000000000..45acea1fa1 --- /dev/null +++ b/lib/rbcodec/metadata/wave.c | |||
@@ -0,0 +1,432 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * Copyright (C) 2010 Yoshihisa Uchida | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include <stdio.h> | ||
23 | #include <string.h> | ||
24 | #include <inttypes.h> | ||
25 | |||
26 | #include "system.h" | ||
27 | #include "metadata.h" | ||
28 | #include "metadata_common.h" | ||
29 | #include "metadata_parsers.h" | ||
30 | #include "rbunicode.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | #ifdef DEBUGF | ||
34 | #undef DEBUGF | ||
35 | #define DEBUGF(...) | ||
36 | #endif | ||
37 | |||
38 | /* Wave(RIFF)/Wave64 format */ | ||
39 | |||
40 | |||
41 | # define AV_WL32(p, d) do { \ | ||
42 | ((uint8_t*)(p))[0] = (d); \ | ||
43 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
44 | ((uint8_t*)(p))[2] = (d)>>16; \ | ||
45 | ((uint8_t*)(p))[3] = (d)>>24; \ | ||
46 | } while(0) | ||
47 | # define AV_WL16(p, d) do { \ | ||
48 | ((uint8_t*)(p))[0] = (d); \ | ||
49 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
50 | } while(0) | ||
51 | |||
52 | enum { | ||
53 | RIFF_CHUNK = 0, | ||
54 | WAVE_CHUNK, | ||
55 | FMT_CHUNK, | ||
56 | FACT_CHUNK, | ||
57 | DATA_CHUNK, | ||
58 | LIST_CHUNK, | ||
59 | }; | ||
60 | |||
61 | /* Wave chunk names */ | ||
62 | #define WAVE_CHUNKNAME_LENGTH 4 | ||
63 | #define WAVE_CHUNKSIZE_LENGTH 4 | ||
64 | |||
65 | static const unsigned char * const wave_chunklist | ||
66 | = "RIFF" | ||
67 | "WAVE" | ||
68 | "fmt " | ||
69 | "fact" | ||
70 | "data" | ||
71 | "LIST"; | ||
72 | |||
73 | /* Wave64 GUIDs */ | ||
74 | #define WAVE64_CHUNKNAME_LENGTH 16 | ||
75 | #define WAVE64_CHUNKSIZE_LENGTH 8 | ||
76 | |||
77 | static const unsigned char * const wave64_chunklist | ||
78 | = "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00" | ||
79 | "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
80 | "fmt \xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
81 | "fact\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
82 | "data\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
83 | "\xbc\x94\x5f\x92\x5a\x52\xd2\x11\x86\xdc\x00\xc0\x4f\x8e\xdb\x8a"; | ||
84 | |||
85 | /* list/info chunk */ | ||
86 | |||
87 | struct info_chunk { | ||
88 | const unsigned char* tag; | ||
89 | size_t offset; | ||
90 | }; | ||
91 | |||
92 | /* info chunk names are common wave/wave64 */ | ||
93 | static const struct info_chunk info_chunks[] = { | ||
94 | { "INAM", offsetof(struct mp3entry, title), }, /* title */ | ||
95 | { "IART", offsetof(struct mp3entry, artist), }, /* artist */ | ||
96 | { "ISBJ", offsetof(struct mp3entry, albumartist), }, /* albumartist */ | ||
97 | { "IPRD", offsetof(struct mp3entry, album), }, /* album */ | ||
98 | { "IWRI", offsetof(struct mp3entry, composer), }, /* composer */ | ||
99 | { "ICMT", offsetof(struct mp3entry, comment), }, /* comment */ | ||
100 | { "ISRF", offsetof(struct mp3entry, grouping), }, /* grouping */ | ||
101 | { "IGNR", offsetof(struct mp3entry, genre_string), }, /* genre */ | ||
102 | { "ICRD", offsetof(struct mp3entry, year_string), }, /* date */ | ||
103 | { "IPRT", offsetof(struct mp3entry, track_string), }, /* track/trackcount */ | ||
104 | { "IFRM", offsetof(struct mp3entry, disc_string), }, /* disc/disccount */ | ||
105 | }; | ||
106 | |||
107 | #define INFO_CHUNK_COUNT ((int)ARRAYLEN(info_chunks)) | ||
108 | |||
109 | /* support formats */ | ||
110 | enum | ||
111 | { | ||
112 | WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ | ||
113 | WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ | ||
114 | WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ | ||
115 | WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ | ||
116 | WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ | ||
117 | WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ | ||
118 | WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ | ||
119 | WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ | ||
120 | WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */ | ||
121 | IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ | ||
122 | IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ | ||
123 | WAVE_FORMAT_ATRAC3 = 0x0270, /* Atrac3 stream */ | ||
124 | WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */ | ||
125 | WAVE_FORMAT_EXTENSIBLE = 0xFFFE, | ||
126 | }; | ||
127 | |||
128 | struct wave_fmt { | ||
129 | unsigned int formattag; | ||
130 | unsigned int channels; | ||
131 | unsigned int blockalign; | ||
132 | unsigned int bitspersample; | ||
133 | unsigned int samplesperblock; | ||
134 | uint32_t totalsamples; | ||
135 | uint64_t numbytes; | ||
136 | }; | ||
137 | |||
138 | static unsigned char *convert_utf8(const unsigned char *src, unsigned char *dst, | ||
139 | int size, bool is_64) | ||
140 | { | ||
141 | if (is_64) | ||
142 | { | ||
143 | /* Note: wave64: metadata codepage is UTF-16 only */ | ||
144 | return utf16LEdecode(src, dst, size); | ||
145 | } | ||
146 | return iso_decode(src, dst, -1, size); | ||
147 | } | ||
148 | |||
149 | static void set_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3) | ||
150 | { | ||
151 | switch (fmt->formattag) | ||
152 | { | ||
153 | case WAVE_FORMAT_PCM: | ||
154 | case WAVE_FORMAT_IEEE_FLOAT: | ||
155 | case WAVE_FORMAT_ALAW: | ||
156 | case WAVE_FORMAT_MULAW: | ||
157 | case IBM_FORMAT_ALAW: | ||
158 | case IBM_FORMAT_MULAW: | ||
159 | fmt->blockalign = fmt->bitspersample * fmt->channels >> 3; | ||
160 | fmt->samplesperblock = 1; | ||
161 | break; | ||
162 | case WAVE_FORMAT_YAMAHA_ADPCM: | ||
163 | if (id3->channels != 0) | ||
164 | { | ||
165 | fmt->samplesperblock = | ||
166 | (fmt->blockalign == ((id3->frequency / 60) + 4) * fmt->channels)? | ||
167 | id3->frequency / 30 : (fmt->blockalign << 1) / fmt->channels; | ||
168 | } | ||
169 | break; | ||
170 | case WAVE_FORMAT_DIALOGIC_OKI_ADPCM: | ||
171 | fmt->blockalign = 1; | ||
172 | fmt->samplesperblock = 2; | ||
173 | break; | ||
174 | case WAVE_FORMAT_SWF_ADPCM: | ||
175 | if (fmt->bitspersample != 0 && id3->channels != 0) | ||
176 | { | ||
177 | fmt->samplesperblock | ||
178 | = (((fmt->blockalign << 3) - 2) / fmt->channels - 22) | ||
179 | / fmt->bitspersample + 1; | ||
180 | } | ||
181 | break; | ||
182 | default: | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | if (fmt->blockalign != 0) | ||
187 | fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock; | ||
188 | } | ||
189 | |||
190 | static void parse_riff_format(unsigned char* buf, int fmtsize, struct wave_fmt *fmt, | ||
191 | struct mp3entry* id3) | ||
192 | { | ||
193 | /* wFormatTag */ | ||
194 | fmt->formattag = buf[0] | (buf[1] << 8); | ||
195 | /* wChannels */ | ||
196 | fmt->channels = buf[2] | (buf[3] << 8); | ||
197 | /* dwSamplesPerSec */ | ||
198 | id3->frequency = get_long_le(&buf[4]); | ||
199 | /* dwAvgBytesPerSec */ | ||
200 | id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000; | ||
201 | /* wBlockAlign */ | ||
202 | fmt->blockalign = buf[12] | (buf[13] << 8); | ||
203 | /* wBitsPerSample */ | ||
204 | fmt->bitspersample = buf[14] | (buf[15] << 8); | ||
205 | |||
206 | if (fmt->formattag != WAVE_FORMAT_EXTENSIBLE) | ||
207 | { | ||
208 | if (fmtsize > 19) | ||
209 | { | ||
210 | /* wSamplesPerBlock */ | ||
211 | fmt->samplesperblock = buf[18] | (buf[19] << 8); | ||
212 | } | ||
213 | } | ||
214 | else if (fmtsize > 25) | ||
215 | { | ||
216 | /* wValidBitsPerSample */ | ||
217 | fmt->bitspersample = buf[18] | (buf[19] << 8); | ||
218 | /* SubFormat */ | ||
219 | fmt->formattag = buf[24] | (buf[25] << 8); | ||
220 | } | ||
221 | |||
222 | /* Check for ATRAC3 stream */ | ||
223 | if (fmt->formattag == WAVE_FORMAT_ATRAC3) | ||
224 | { | ||
225 | int jsflag = 0; | ||
226 | if(id3->bitrate == 66 || id3->bitrate == 94) | ||
227 | jsflag = 1; | ||
228 | |||
229 | id3->extradata_size = 14; | ||
230 | id3->channels = 2; | ||
231 | id3->codectype = AFMT_OMA_ATRAC3; | ||
232 | id3->bytesperframe = fmt->blockalign; | ||
233 | |||
234 | /* Store the extradata for the codec */ | ||
235 | AV_WL16(&id3->id3v2buf[0], 1); // always 1 | ||
236 | AV_WL32(&id3->id3v2buf[2], id3->frequency);// samples rate | ||
237 | AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode | ||
238 | AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode | ||
239 | AV_WL16(&id3->id3v2buf[10], 1); // always 1 | ||
240 | AV_WL16(&id3->id3v2buf[12], 0); // always 0 | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void parse_list_chunk(int fd, struct mp3entry* id3, int chunksize, bool is_64) | ||
245 | { | ||
246 | unsigned char tmpbuf[ID3V2_BUF_SIZE]; | ||
247 | unsigned char *bp = tmpbuf; | ||
248 | unsigned char *endp; | ||
249 | unsigned char *data_pos; | ||
250 | unsigned char *tag_pos = id3->id3v2buf; | ||
251 | int datasize; | ||
252 | int infosize; | ||
253 | int remain; | ||
254 | int i; | ||
255 | |||
256 | if (is_64) | ||
257 | lseek(fd, 4, SEEK_CUR); | ||
258 | else if (read(fd, bp, 4) < 4 || memcmp(bp, "INFO", 4)) | ||
259 | return; | ||
260 | |||
261 | /* decrease skip bytes */ | ||
262 | chunksize -= 4; | ||
263 | |||
264 | infosize = read(fd, bp, (ID3V2_BUF_SIZE > chunksize)? chunksize : ID3V2_BUF_SIZE); | ||
265 | if (infosize <= 8) | ||
266 | return; | ||
267 | |||
268 | endp = bp + infosize; | ||
269 | while (bp < endp) | ||
270 | { | ||
271 | datasize = get_long_le(bp + 4); | ||
272 | data_pos = bp + 8; | ||
273 | remain = ID3V2_BUF_SIZE - (tag_pos - (unsigned char*)id3->id3v2buf); | ||
274 | if (remain < 1) | ||
275 | break; | ||
276 | |||
277 | for (i = 0; i < INFO_CHUNK_COUNT; i++) | ||
278 | { | ||
279 | if (memcmp(bp, info_chunks[i].tag, 4) == 0) | ||
280 | { | ||
281 | *((char **)(((char*)id3) + info_chunks[i].offset)) = tag_pos; | ||
282 | tag_pos = convert_utf8(data_pos, tag_pos, | ||
283 | (datasize + 1 >= remain )? remain - 1 : datasize, | ||
284 | is_64); | ||
285 | *tag_pos++ = 0; | ||
286 | break; | ||
287 | } | ||
288 | } | ||
289 | bp = data_pos + datasize + (datasize & 1); | ||
290 | }; | ||
291 | } | ||
292 | |||
293 | static bool read_header(int fd, struct mp3entry* id3, const unsigned char *chunknames, | ||
294 | bool is_64) | ||
295 | { | ||
296 | /* Use the temporary buffer */ | ||
297 | unsigned char* buf = (unsigned char *)id3->path; | ||
298 | |||
299 | struct wave_fmt fmt; | ||
300 | |||
301 | const unsigned int namelen = (is_64)? WAVE64_CHUNKNAME_LENGTH : WAVE_CHUNKNAME_LENGTH; | ||
302 | const unsigned int sizelen = (is_64)? WAVE64_CHUNKSIZE_LENGTH : WAVE_CHUNKSIZE_LENGTH; | ||
303 | const unsigned int len = namelen + sizelen; | ||
304 | uint64_t chunksize; | ||
305 | uint64_t offset = len + namelen; | ||
306 | int read_data; | ||
307 | |||
308 | memset(&fmt, 0, sizeof(struct wave_fmt)); | ||
309 | |||
310 | id3->vbr = false; /* All Wave/Wave64 files are CBR */ | ||
311 | id3->filesize = filesize(fd); | ||
312 | |||
313 | /* get RIFF chunk header */ | ||
314 | lseek(fd, 0, SEEK_SET); | ||
315 | read(fd, buf, offset); | ||
316 | |||
317 | if ((memcmp(buf, chunknames + RIFF_CHUNK * namelen, namelen) != 0) || | ||
318 | (memcmp(buf + len, chunknames + WAVE_CHUNK * namelen, namelen) != 0)) | ||
319 | { | ||
320 | DEBUGF("metadata error: missing riff header.\n"); | ||
321 | return false; | ||
322 | } | ||
323 | |||
324 | /* iterate over WAVE chunks until 'data' chunk */ | ||
325 | while (read(fd, buf, len) > 0) | ||
326 | { | ||
327 | offset += len; | ||
328 | |||
329 | /* get chunk size (when the header is wave64, chunksize includes GUID and data length) */ | ||
330 | chunksize = (is_64) ? get_uint64_le(buf + namelen) - len : | ||
331 | get_long_le(buf + namelen); | ||
332 | |||
333 | read_data = 0; | ||
334 | if (memcmp(buf, chunknames + FMT_CHUNK * namelen, namelen) == 0) | ||
335 | { | ||
336 | DEBUGF("find 'fmt ' chunk\n"); | ||
337 | |||
338 | if (chunksize < 16) | ||
339 | { | ||
340 | DEBUGF("metadata error: 'fmt ' chunk is too small: %d\n", (int)chunksize); | ||
341 | return false; | ||
342 | } | ||
343 | |||
344 | /* get and parse format */ | ||
345 | read_data = (chunksize > 25)? 26 : chunksize; | ||
346 | |||
347 | read(fd, buf, read_data); | ||
348 | parse_riff_format(buf, read_data, &fmt, id3); | ||
349 | } | ||
350 | else if (memcmp(buf, chunknames + FACT_CHUNK * namelen, namelen) == 0) | ||
351 | { | ||
352 | DEBUGF("find 'fact' chunk\n"); | ||
353 | |||
354 | /* dwSampleLength */ | ||
355 | if (chunksize >= sizelen) | ||
356 | { | ||
357 | /* get totalsamples */ | ||
358 | read_data = sizelen; | ||
359 | read(fd, buf, read_data); | ||
360 | fmt.totalsamples = (is_64)? get_uint64_le(buf) : get_long_le(buf); | ||
361 | } | ||
362 | } | ||
363 | else if (memcmp(buf, chunknames + DATA_CHUNK * namelen, namelen) == 0) | ||
364 | { | ||
365 | DEBUGF("find 'data' chunk\n"); | ||
366 | fmt.numbytes = chunksize; | ||
367 | if (fmt.formattag == WAVE_FORMAT_ATRAC3) | ||
368 | id3->first_frame_offset = offset; | ||
369 | } | ||
370 | else if (memcmp(buf, chunknames + LIST_CHUNK * namelen, namelen) == 0) | ||
371 | { | ||
372 | DEBUGF("find 'LIST' chunk\n"); | ||
373 | parse_list_chunk(fd, id3, chunksize, is_64); | ||
374 | lseek(fd, offset, SEEK_SET); | ||
375 | } | ||
376 | |||
377 | /* padded to next chunk */ | ||
378 | chunksize += ((is_64)? ((1 + ~chunksize) & 0x07) : (chunksize & 1)); | ||
379 | |||
380 | offset += chunksize; | ||
381 | if (offset >= id3->filesize) | ||
382 | break; | ||
383 | |||
384 | lseek(fd, chunksize - read_data, SEEK_CUR); | ||
385 | } | ||
386 | |||
387 | if (fmt.numbytes == 0) | ||
388 | { | ||
389 | DEBUGF("metadata error: read error or missing 'data' chunk.\n"); | ||
390 | return false; | ||
391 | } | ||
392 | |||
393 | if (fmt.totalsamples == 0) | ||
394 | set_totalsamples(&fmt, id3); | ||
395 | |||
396 | if (id3->frequency == 0 || id3->bitrate == 0) | ||
397 | { | ||
398 | DEBUGF("metadata error: frequency or bitrate is 0\n"); | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ | ||
403 | id3->length = (fmt.formattag != WAVE_FORMAT_ATRAC3)? | ||
404 | (uint64_t)fmt.totalsamples * 1000 / id3->frequency : | ||
405 | ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate; | ||
406 | |||
407 | /* output header/id3 info (for debug) */ | ||
408 | DEBUGF("%s header info ----\n", (is_64)? "wave64" : "wave"); | ||
409 | DEBUGF(" format: %04x\n", (int)fmt.formattag); | ||
410 | DEBUGF(" channels: %u\n", fmt.channels); | ||
411 | DEBUGF(" blockalign: %u\n", fmt.blockalign); | ||
412 | DEBUGF(" bitspersample: %u\n", fmt.bitspersample); | ||
413 | DEBUGF(" samplesperblock: %u\n", fmt.samplesperblock); | ||
414 | DEBUGF(" totalsamples: %u\n", (unsigned int)fmt.totalsamples); | ||
415 | DEBUGF(" numbytes: %u\n", (unsigned int)fmt.numbytes); | ||
416 | DEBUGF("id3 info ----\n"); | ||
417 | DEBUGF(" frequency: %u\n", (unsigned int)id3->frequency); | ||
418 | DEBUGF(" bitrate: %d\n", id3->bitrate); | ||
419 | DEBUGF(" length: %u\n", (unsigned int)id3->length); | ||
420 | |||
421 | return true; | ||
422 | } | ||
423 | |||
424 | bool get_wave_metadata(int fd, struct mp3entry* id3) | ||
425 | { | ||
426 | return read_header(fd, id3, wave_chunklist, false); | ||
427 | } | ||
428 | |||
429 | bool get_wave64_metadata(int fd, struct mp3entry* id3) | ||
430 | { | ||
431 | return read_header(fd, id3, wave64_chunklist, true); | ||
432 | } | ||
diff --git a/lib/rbcodec/metadata/wavpack.c b/lib/rbcodec/metadata/wavpack.c new file mode 100644 index 0000000000..f2811df8f3 --- /dev/null +++ b/lib/rbcodec/metadata/wavpack.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 David Bryant | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | #define ID_UNIQUE 0x3f | ||
34 | #define ID_LARGE 0x80 | ||
35 | #define ID_SAMPLE_RATE 0x27 | ||
36 | |||
37 | #define MONO_FLAG 4 | ||
38 | #define HYBRID_FLAG 8 | ||
39 | |||
40 | static const long wavpack_sample_rates [] = | ||
41 | { | ||
42 | 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, | ||
43 | 32000, 44100, 48000, 64000, 88200, 96000, 192000 | ||
44 | }; | ||
45 | |||
46 | /* A simple parser to read basic information from a WavPack file. This | ||
47 | * now works with self-extrating WavPack files and also will scan the | ||
48 | * metadata for non-standard sampling rates. This no longer fails on | ||
49 | * WavPack files containing floating-point audio data because these are | ||
50 | * now converted to standard Rockbox format in the decoder, and also | ||
51 | * handles the case where up to 15 non-audio blocks might occur at the | ||
52 | * beginning of the file. | ||
53 | */ | ||
54 | |||
55 | bool get_wavpack_metadata(int fd, struct mp3entry* id3) | ||
56 | { | ||
57 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
58 | unsigned char* buf = (unsigned char *)id3->path; | ||
59 | uint32_t totalsamples = (uint32_t) -1; | ||
60 | int i; | ||
61 | |||
62 | for (i = 0; i < 256; ++i) { | ||
63 | |||
64 | /* at every 256 bytes into file, try to read a WavPack header */ | ||
65 | |||
66 | if ((lseek(fd, i * 256, SEEK_SET) < 0) || (read(fd, buf, 32) < 32)) | ||
67 | return false; | ||
68 | |||
69 | /* if valid WavPack 4 header version, break */ | ||
70 | |||
71 | if (memcmp (buf, "wvpk", 4) == 0 && buf [9] == 4 && | ||
72 | (buf [8] >= 2 && buf [8] <= 0x10)) | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | if (i == 256) { | ||
77 | logf ("Not a WavPack file"); | ||
78 | return false; | ||
79 | } | ||
80 | |||
81 | id3->vbr = true; /* All WavPack files are VBR */ | ||
82 | id3->filesize = filesize (fd); | ||
83 | |||
84 | /* check up to 16 headers before we give up finding one with audio */ | ||
85 | |||
86 | for (i = 0; i < 16; ++i) { | ||
87 | uint32_t meta_bytes = get_long_le(&buf [4]) - 24; | ||
88 | uint32_t trial_totalsamples = get_long_le(&buf[12]); | ||
89 | uint32_t blockindex = get_long_le(&buf[16]); | ||
90 | uint32_t blocksamples = get_long_le(&buf[20]); | ||
91 | uint32_t flags = get_long_le(&buf[24]); | ||
92 | |||
93 | if (totalsamples == (uint32_t) -1 && blockindex == 0) | ||
94 | totalsamples = trial_totalsamples; | ||
95 | |||
96 | if (blocksamples) { | ||
97 | int srindx = ((buf [26] >> 7) & 1) + ((buf [27] << 1) & 14); | ||
98 | |||
99 | if (srindx == 15) { | ||
100 | uint32_t meta_size; | ||
101 | |||
102 | id3->frequency = 44100; | ||
103 | |||
104 | while (meta_bytes >= 6) { | ||
105 | if (read(fd, buf, 2) < 2) | ||
106 | break; | ||
107 | |||
108 | if (buf [0] & ID_LARGE) { | ||
109 | if (read(fd, buf + 2, 2) < 2) | ||
110 | break; | ||
111 | |||
112 | meta_size = (buf [1] << 1) + (buf [2] << 9) + (buf [3] << 17); | ||
113 | meta_bytes -= meta_size + 4; | ||
114 | } | ||
115 | else { | ||
116 | meta_size = buf [1] << 1; | ||
117 | meta_bytes -= meta_size + 2; | ||
118 | |||
119 | if ((buf [0] & ID_UNIQUE) == ID_SAMPLE_RATE) { | ||
120 | if (meta_size == 4 && read(fd, buf + 2, 4) == 4) | ||
121 | id3->frequency = buf [2] + (buf [3] << 8) + (buf [4] << 16); | ||
122 | |||
123 | break; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (meta_size > 0 && lseek(fd, meta_size, SEEK_CUR) < 0) | ||
128 | break; | ||
129 | } | ||
130 | } | ||
131 | else | ||
132 | id3->frequency = wavpack_sample_rates[srindx]; | ||
133 | |||
134 | /* if the total number of samples is still unknown, make a guess on the high side (for now) */ | ||
135 | |||
136 | if (totalsamples == (uint32_t) -1) { | ||
137 | totalsamples = id3->filesize * 3; | ||
138 | |||
139 | if (!(flags & HYBRID_FLAG)) | ||
140 | totalsamples /= 2; | ||
141 | |||
142 | if (!(flags & MONO_FLAG)) | ||
143 | totalsamples /= 2; | ||
144 | } | ||
145 | |||
146 | id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; | ||
147 | id3->bitrate = id3->filesize / (id3->length / 8); | ||
148 | |||
149 | read_ape_tags(fd, id3); | ||
150 | return true; | ||
151 | } | ||
152 | else { /* block did not contain audio, so seek to the end and see if there's another */ | ||
153 | if ((meta_bytes > 0 && lseek(fd, meta_bytes, SEEK_CUR) < 0) || | ||
154 | read(fd, buf, 32) < 32 || memcmp (buf, "wvpk", 4) != 0) | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | return false; | ||
160 | } | ||
diff --git a/lib/rbcodec/rbcodec.make b/lib/rbcodec/rbcodec.make new file mode 100644 index 0000000000..afa289ba28 --- /dev/null +++ b/lib/rbcodec/rbcodec.make | |||
@@ -0,0 +1,19 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | |||
8 | RBCODEC_LIB = $(RBCODEC_BLD)/librbcodec.a | ||
9 | RBCODEC_SRC := $(call preprocess, $(RBCODEC_DIR)/SOURCES) | ||
10 | RBCODEC_OBJ := $(call c2obj, $(RBCODEC_SRC)) | ||
11 | INCLUDES += -I$(RBCODEC_DIR) -I$(RBCODEC_DIR)/dsp -I$(RBCODEC_DIR)/metadata | ||
12 | OTHER_SRC += $(RBCODEC_SRC) | ||
13 | |||
14 | $(RBCODEC_BLD)/%.o: $(RBCODEC_DIR)/%.c | ||
15 | $(SILENT)mkdir -p $(dir $@) | ||
16 | $(call PRINTS,CC $<)$(CC) $(CFLAGS) $(RBCODEC_CFLAGS) -c $< -o $@ | ||
17 | |||
18 | $(RBCODEC_LIB): $(RBCODEC_OBJ) | ||
19 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null | ||
diff --git a/lib/rbcodec/test/SOURCES b/lib/rbcodec/test/SOURCES index 467115e0d6..4c0d906dc5 100644 --- a/lib/rbcodec/test/SOURCES +++ b/lib/rbcodec/test/SOURCES | |||
@@ -1,49 +1,8 @@ | |||
1 | warble.c | 1 | warble.c |
2 | ../../../apps/metadata.c | ||
3 | ../../../apps/replaygain.c | ||
4 | ../../../firmware/buflib.c | 2 | ../../../firmware/buflib.c |
5 | ../../../firmware/core_alloc.c | 3 | ../../../firmware/core_alloc.c |
6 | ../../../firmware/common/strlcpy.c | 4 | ../../../firmware/common/strlcpy.c |
7 | ../../../firmware/common/unicode.c | 5 | ../../../firmware/common/unicode.c |
8 | ../../../firmware/common/structec.c | 6 | ../../../firmware/common/structec.c |
9 | ../../../apps/mp3data.c | ||
10 | ../../../apps/fixedpoint.c | 7 | ../../../apps/fixedpoint.c |
11 | ../../../uisimulator/common/io.c | 8 | ../../../uisimulator/common/io.c |
12 | ../../../apps/compressor.c | ||
13 | ../../../apps/dsp.c | ||
14 | ../../../apps/eq.c | ||
15 | ../../../apps/tdspeed.c | ||
16 | ../../../apps/metadata/a52.c | ||
17 | ../../../apps/metadata/adx.c | ||
18 | ../../../apps/metadata/aiff.c | ||
19 | ../../../apps/metadata/ape.c | ||
20 | ../../../apps/metadata/asap.c | ||
21 | ../../../apps/metadata/asf.c | ||
22 | ../../../apps/metadata/au.c | ||
23 | ../../../apps/metadata/ay.c | ||
24 | ../../../apps/metadata/flac.c | ||
25 | ../../../apps/metadata/gbs.c | ||
26 | ../../../apps/metadata/hes.c | ||
27 | ../../../apps/metadata/id3tags.c | ||
28 | ../../../apps/metadata/kss.c | ||
29 | ../../../apps/metadata/metadata_common.c | ||
30 | ../../../apps/metadata/mod.c | ||
31 | ../../../apps/metadata/monkeys.c | ||
32 | ../../../apps/metadata/mp3.c | ||
33 | ../../../apps/metadata/mp4.c | ||
34 | ../../../apps/metadata/mpc.c | ||
35 | ../../../apps/metadata/nsf.c | ||
36 | ../../../apps/metadata/ogg.c | ||
37 | ../../../apps/metadata/oma.c | ||
38 | ../../../apps/metadata/rm.c | ||
39 | ../../../apps/metadata/sgc.c | ||
40 | ../../../apps/metadata/sid.c | ||
41 | ../../../apps/metadata/smaf.c | ||
42 | ../../../apps/metadata/spc.c | ||
43 | ../../../apps/metadata/tta.c | ||
44 | ../../../apps/metadata/vgm.c | ||
45 | ../../../apps/metadata/vorbis.c | ||
46 | ../../../apps/metadata/vox.c | ||
47 | ../../../apps/metadata/wave.c | ||
48 | ../../../apps/metadata/wavpack.c | ||
49 | |||
diff --git a/lib/rbcodec/test/warble.make b/lib/rbcodec/test/warble.make index 0b70e9a932..11f7ab03ec 100644 --- a/lib/rbcodec/test/warble.make +++ b/lib/rbcodec/test/warble.make | |||
@@ -9,12 +9,15 @@ | |||
9 | 9 | ||
10 | 10 | ||
11 | 11 | ||
12 | RBCODEC_DIR = $(ROOTDIR)/lib/rbcodec | ||
13 | RBCODEC_BLD = $(BUILDDIR)/lib/rbcodec | ||
14 | |||
12 | FLAGS=-g -D__PCTOOL__ $(TARGET) -Wall | 15 | FLAGS=-g -D__PCTOOL__ $(TARGET) -Wall |
13 | 16 | ||
14 | SRC= $(call preprocess, $(ROOTDIR)/lib/rbcodec/test/SOURCES) | 17 | SRC= $(call preprocess, $(ROOTDIR)/lib/rbcodec/test/SOURCES) |
15 | 18 | ||
16 | INCLUDES += -I$(ROOTDIR)/apps -I$(ROOTDIR)/apps/codecs -I$(ROOTDIR)/apps/codecs/lib \ | 19 | INCLUDES += -I$(ROOTDIR)/apps -I$(ROOTDIR)/apps/codecs -I$(ROOTDIR)/apps/codecs/lib \ |
17 | -I$(ROOTDIR)/apps/gui -I$(ROOTDIR)/apps/metadata | 20 | -I$(ROOTDIR)/apps/gui |
18 | INCLUDES += -I$(ROOTDIR)/firmware/export -I$(ROOTDIR)/firmware/include \ | 21 | INCLUDES += -I$(ROOTDIR)/firmware/export -I$(ROOTDIR)/firmware/include \ |
19 | -I$(ROOTDIR)/firmware/target/hosted \ | 22 | -I$(ROOTDIR)/firmware/target/hosted \ |
20 | -I$(ROOTDIR)/firmware/target/hosted/sdl | 23 | -I$(ROOTDIR)/firmware/target/hosted/sdl |
@@ -30,9 +33,10 @@ endif | |||
30 | 33 | ||
31 | include $(ROOTDIR)/tools/functions.make | 34 | include $(ROOTDIR)/tools/functions.make |
32 | include $(ROOTDIR)/apps/codecs/codecs.make | 35 | include $(ROOTDIR)/apps/codecs/codecs.make |
36 | include $(ROOTDIR)/lib/rbcodec/rbcodec.make | ||
33 | 37 | ||
34 | $(BUILDDIR)/$(BINARY): $(CODECS) | 38 | $(BUILDDIR)/$(BINARY): $(CODECS) |
35 | 39 | ||
36 | $(BUILDDIR)/$(BINARY): $$(OBJ) | 40 | $(BUILDDIR)/$(BINARY): $$(OBJ) $(RBCODEC_LIB) |
37 | @echo LD $(BINARY) | 41 | @echo LD $(BINARY) |
38 | $(SILENT)$(HOSTCC) $(SIMFLAGS) $(LIBS) -o $@ $+ | 42 | $(SILENT)$(HOSTCC) $(SIMFLAGS) $(LIBS) -o $@ $+ |