summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/metadata/metadata_common.c11
-rw-r--r--lib/rbcodec/test/Makefile74
-rw-r--r--lib/rbcodec/test/autoconf.h17
-rw-r--r--lib/rbcodec/test/warble.c837
4 files changed, 936 insertions, 3 deletions
diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c
index 781123717a..e861644025 100644
--- a/apps/metadata/metadata_common.c
+++ b/apps/metadata/metadata_common.c
@@ -29,7 +29,6 @@
29#include "metadata_common.h" 29#include "metadata_common.h"
30#include "metadata_parsers.h" 30#include "metadata_parsers.h"
31#include "replaygain.h" 31#include "replaygain.h"
32#include "misc.h"
33 32
34/* Read a string from the file. Read up to size bytes, or, if eos != -1, 33/* Read a string from the file. Read up to size bytes, or, if eos != -1,
35 * until the eos character is found (eos is not stored in buf, unless it is 34 * until the eos character is found (eos is not stored in buf, unless it is
@@ -202,7 +201,10 @@ uint32_t get_itunes_int32(char* value, int count)
202 201
203 while (count-- > 0) 202 while (count-- > 0)
204 { 203 {
205 value = skip_whitespace(value); 204 while (isspace(*value))
205 {
206 value++;
207 }
206 208
207 while (*value && !isspace(*value)) 209 while (*value && !isspace(*value))
208 { 210 {
@@ -210,7 +212,10 @@ uint32_t get_itunes_int32(char* value, int count)
210 } 212 }
211 } 213 }
212 214
213 value = skip_whitespace(value); 215 while (isspace(*value))
216 {
217 value++;
218 }
214 219
215 while (*value && ((c = strchr(hexdigits, toupper(*value))) != NULL)) 220 while (*value && ((c = strchr(hexdigits, toupper(*value))) != NULL))
216 { 221 {
diff --git a/lib/rbcodec/test/Makefile b/lib/rbcodec/test/Makefile
new file mode 100644
index 0000000000..cbda9acbdb
--- /dev/null
+++ b/lib/rbcodec/test/Makefile
@@ -0,0 +1,74 @@
1default: all
2
3.PHONY: default all clean dep
4
5ROOTDIR = $(shell readlink -e ../../..)
6BUILDDIR = $(shell pwd)/build
7APPSDIR = $(ROOTDIR)/apps
8TOOLSDIR = $(ROOTDIR)/tools
9DEPFILE = $(BUILDDIR)/make.dep
10APP_TYPE = sdl-sim
11
12INCLUDES = -I$(shell pwd)
13INCLUDES += -I$(APPSDIR) -I$(APPSDIR)/codecs -I$(APPSDIR)/codecs/lib \
14 -I$(APPSDIR)/gui -I$(APPSDIR)/metadata
15INCLUDES += -I$(ROOTDIR)/firmware/export -I$(ROOTDIR)/firmware/include \
16 -I$(ROOTDIR)/firmware/target/hosted \
17 -I$(ROOTDIR)/firmware/target/hosted/sdl
18
19CFLAGS = $(INCLUDES) -DROCKBOX -DSIMULATOR=1
20CFLAGS += -O0 -ggdb -DDEBUG -DLOGF_ENABLE -Wall -Wno-pointer-sign
21CFLAGS += -Wstrict-prototypes -pipe -std=gnu99
22PPCFLAGS = $(CFLAGS)
23
24SHARED_CFLAGS = -fPIC -fvisibility=hidden
25SHARED_LDFLAG = -shared
26
27WARBLE_OBJS = $(BUILDDIR)/warble.o
28WARBLE_CFLAGS = '-DCODECDIR="$(CODECDIR)"' $(shell sdl-config --cflags)
29WARBLE_LDFLAGS = -lm -ldl $(shell sdl-config --libs)
30
31include $(ROOTDIR)/tools/functions.make
32include $(APPSDIR)/codecs/codecs.make
33
34SRC = $(ROOTDIR)/apps/metadata.c $(ROOTDIR)/apps/replaygain.c \
35 $(ROOTDIR)/firmware/buflib.c \
36 $(ROOTDIR)/firmware/core_alloc.c \
37 $(ROOTDIR)/firmware/common/strlcpy.c \
38 $(ROOTDIR)/firmware/common/unicode.c \
39 $(ROOTDIR)/firmware/common/structec.c $(ROOTDIR)/apps/mp3data.c \
40 $(ROOTDIR)/apps/fixedpoint.c $(ROOTDIR)/uisimulator/common/io.c
41SRC += $(APPSDIR)/compressor.c $(APPSDIR)/dsp.c $(APPSDIR)/eq.c $(APPSDIR)/tdspeed.c
42SRC += $(wildcard $(ROOTDIR)/apps/metadata/*.c)
43
44OBJ := $(SRC:.c=.o)
45OBJ := $(OBJ:.S=.o)
46OBJ := $(subst $(ROOTDIR),$(BUILDDIR),$(OBJ))
47
48all: warble $(CODECS)
49
50dep $(DEPFILE):
51 $(SILENT)mkdir -p $(dir $(DEPFILE))
52 $(call PRINTS,Generating dependencies)
53 @rm -f $(DEPFILE)_
54 $(call mkdepfile,$(DEPFILE)_,$(SRC))
55 $(call mkdepfile,$(DEPFILE)_,$(OTHER_SRC))
56 $(call mkdepfile,$(DEPFILE)_,$(ASMDEFS_SRC))
57 @mv $(DEPFILE)_ $(DEPFILE)
58
59-include $(DEPFILE)
60
61warble: $(WARBLE_OBJS) $(OBJ)
62 $(call PRINTS,LD $@)$(CC) $(LDFLAGS) $^ -o $@ $(WARBLE_LDFLAGS)
63
64$(BUILDDIR)/%.o: %.c
65 $(SILENT)mkdir -p $(dir $@)
66 $(call PRINTS,CC $<)$(CC) $(CFLAGS) -c $< -o $@ $(WARBLE_CFLAGS)
67
68$(BUILDDIR)/%.o: $(ROOTDIR)/%.c
69 $(SILENT)mkdir -p $(dir $@)
70 $(call PRINTS,CC $<)$(CC) $(CFLAGS) -c $< -o $@
71
72clean:
73 $(SILENT)echo Cleaning build directory
74 $(SILENT)rm -rf warble $(BUILDDIR)/*
diff --git a/lib/rbcodec/test/autoconf.h b/lib/rbcodec/test/autoconf.h
new file mode 100644
index 0000000000..0908ade420
--- /dev/null
+++ b/lib/rbcodec/test/autoconf.h
@@ -0,0 +1,17 @@
1#ifndef __BUILD_AUTOCONF_H
2#define __BUILD_AUTOCONF_H
3
4#define __PCTOOL__
5#define CONFIG_CODEC SWCODEC
6#define TARGET_ID 73 /* sdlapp */
7#define MEMORYSIZE 64
8#define ROCKBOX_LITTLE_ENDIAN 1
9#define HAVE_PITCHSCREEN
10#define HAVE_SW_TONE_CONTROLS
11#define HAVE_SW_VOLUME_CONTROL
12#define VOLUME_MIN -100
13#define VOLUME_MAX 100
14#define SW_VOLUME_MIN -100
15#define SW_VOLUME_MAX 100
16
17#endif /* __BUILD_AUTOCONF_H */
diff --git a/lib/rbcodec/test/warble.c b/lib/rbcodec/test/warble.c
new file mode 100644
index 0000000000..2cba6c0d59
--- /dev/null
+++ b/lib/rbcodec/test/warble.c
@@ -0,0 +1,837 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2011 Sean Bartell
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#define _BSD_SOURCE /* htole64 from endian.h */
22#include <sys/types.h>
23#include <SDL.h>
24#include <dlfcn.h>
25#include <endian.h>
26#include <fcntl.h>
27#include <math.h>
28#include <stdarg.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <unistd.h>
34#include "buffering.h" /* TYPE_PACKET_AUDIO */
35#include "codecs.h"
36#include "core_alloc.h" /* core_allocator_init */
37#include "debug.h"
38#include "dsp.h"
39#include "metadata.h"
40#include "settings.h"
41#include "sound.h"
42#include "tdspeed.h"
43
44/***************** EXPORTED *****************/
45
46struct user_settings global_settings;
47volatile long current_tick = 0;
48
49void yield(void)
50{
51}
52
53int set_irq_level(int level)
54{
55 return 0;
56}
57
58void mutex_init(struct mutex *m)
59{
60}
61
62void mutex_lock(struct mutex *m)
63{
64}
65
66void mutex_unlock(struct mutex *m)
67{
68}
69
70void debugf(const char *fmt, ...)
71{
72 va_list ap;
73 va_start(ap, fmt);
74 vfprintf(stderr, fmt, ap);
75 va_end(ap);
76}
77
78/***************** INTERNAL *****************/
79
80static enum { MODE_PLAY, MODE_WRITE } mode;
81static bool use_dsp = true;
82static bool enable_loop = false;
83static const char *config = "";
84
85static int input_fd;
86static enum codec_command_action codec_action;
87static intptr_t codec_action_param = 0;
88static unsigned long num_output_samples = 0;
89static struct codec_api ci;
90
91static struct {
92 intptr_t freq;
93 intptr_t stereo_mode;
94 intptr_t depth;
95 int channels;
96} format;
97
98/***** MODE_WRITE *****/
99
100#define WAVE_HEADER_SIZE 0x2e
101#define WAVE_FORMAT_PCM 1
102#define WAVE_FORMAT_IEEE_FLOAT 3
103static int output_fd;
104static bool write_raw = false;
105static bool write_header_written = false;
106
107static void write_init(const char *output_fn)
108{
109 mode = MODE_WRITE;
110 if (!strcmp(output_fn, "-")) {
111 output_fd = STDOUT_FILENO;
112 } else {
113 output_fd = creat(output_fn, 0666);
114 if (output_fd == -1) {
115 perror(output_fn);
116 exit(1);
117 }
118 }
119}
120
121static void set_le16(char *buf, uint16_t val)
122{
123 buf[0] = val;
124 buf[1] = val >> 8;
125}
126
127static void set_le32(char *buf, uint32_t val)
128{
129 buf[0] = val;
130 buf[1] = val >> 8;
131 buf[2] = val >> 16;
132 buf[3] = val >> 24;
133}
134
135static void write_wav_header(void)
136{
137 int channels, sample_size, freq, type;
138 if (use_dsp) {
139 channels = 2;
140 sample_size = 16;
141 freq = NATIVE_FREQUENCY;
142 type = WAVE_FORMAT_PCM;
143 } else {
144 channels = format.channels;
145 sample_size = 64;
146 freq = format.freq;
147 type = WAVE_FORMAT_IEEE_FLOAT;
148 }
149
150 /* The size fields are normally overwritten by write_quit(). If that fails,
151 * this fake size ensures the file can still be played. */
152 off_t total_size = 0x7fffff00 + WAVE_HEADER_SIZE;
153 char header[WAVE_HEADER_SIZE] = {"RIFF____WAVEfmt \x12\0\0\0"
154 "________________\0\0data____"};
155 set_le32(header + 0x04, total_size - 8);
156 set_le16(header + 0x14, type);
157 set_le16(header + 0x16, channels);
158 set_le32(header + 0x18, freq);
159 set_le32(header + 0x1c, freq * channels * sample_size / 8);
160 set_le16(header + 0x20, channels * sample_size / 8);
161 set_le16(header + 0x22, sample_size);
162 set_le32(header + 0x2a, total_size - WAVE_HEADER_SIZE);
163 write(output_fd, header, sizeof(header));
164 write_header_written = true;
165}
166
167static void write_quit(void)
168{
169 if (!write_raw) {
170 /* Write the correct size fields in the header. If lseek fails (e.g.
171 * for a pipe) nothing is written. */
172 off_t total_size = lseek(output_fd, 0, SEEK_CUR);
173 if (total_size != (off_t)-1) {
174 char buf[4];
175 set_le32(buf, total_size - 8);
176 lseek(output_fd, 4, SEEK_SET);
177 write(output_fd, buf, 4);
178 set_le32(buf, total_size - WAVE_HEADER_SIZE);
179 lseek(output_fd, 0x2a, SEEK_SET);
180 write(output_fd, buf, 4);
181 }
182 }
183 if (output_fd != STDOUT_FILENO)
184 close(output_fd);
185}
186
187static uint64_t make_float64(int32_t sample, int shift)
188{
189 /* TODO: be more portable */
190 double val = ldexp(sample, -shift);
191 return *(uint64_t*)&val;
192}
193
194static void write_pcm(int16_t *pcm, int count)
195{
196 if (!write_header_written)
197 write_wav_header();
198 int i;
199 for (i = 0; i < 2 * count; i++)
200 pcm[i] = htole16(pcm[i]);
201 write(output_fd, pcm, 4 * count);
202}
203
204static void write_pcm_raw(int32_t *pcm, int count)
205{
206 if (write_raw) {
207 write(output_fd, pcm, count * sizeof(*pcm));
208 } else {
209 if (!write_header_written)
210 write_wav_header();
211 int i;
212 uint64_t buf[count];
213
214 for (i = 0; i < count; i++)
215 buf[i] = htole64(make_float64(pcm[i], format.depth));
216 write(output_fd, buf, count * sizeof(*buf));
217 }
218}
219
220/***** MODE_PLAY *****/
221
222/* MODE_PLAY uses a double buffer: one half is read by the playback thread and
223 * the other half is written to by the main thread. When a thread is done with
224 * its current half, it waits for the other thread and then switches. The main
225 * advantage of this method is its simplicity; the main disadvantage is that it
226 * has long latency. ALSA buffer underruns still occur sometimes, but this is
227 * SDL's fault. */
228
229#define PLAYBACK_BUFFER_SIZE 0x10000
230static bool playback_running = false;
231static char playback_buffer[2][PLAYBACK_BUFFER_SIZE];
232static int playback_play_ind, playback_decode_ind;
233static int playback_play_pos, playback_decode_pos;
234static SDL_sem *playback_play_sema, *playback_decode_sema;
235
236static void playback_init(void)
237{
238 mode = MODE_PLAY;
239 if (SDL_Init(SDL_INIT_AUDIO)) {
240 fprintf(stderr, "error: Can't initialize SDL: %s\n", SDL_GetError());
241 exit(1);
242 }
243 playback_play_ind = 1;
244 playback_play_pos = PLAYBACK_BUFFER_SIZE;
245 playback_decode_ind = 0;
246 playback_decode_pos = 0;
247 playback_play_sema = SDL_CreateSemaphore(0);
248 playback_decode_sema = SDL_CreateSemaphore(0);
249}
250
251static void playback_callback(void *userdata, Uint8 *stream, int len)
252{
253 while (len > 0) {
254 if (!playback_running && playback_play_ind == playback_decode_ind
255 && playback_play_pos >= playback_decode_pos) {
256 /* end of data */
257 memset(stream, 0, len);
258 SDL_SemPost(playback_play_sema);
259 return;
260 }
261 if (playback_play_pos >= PLAYBACK_BUFFER_SIZE) {
262 SDL_SemPost(playback_play_sema);
263 SDL_SemWait(playback_decode_sema);
264 playback_play_ind = !playback_play_ind;
265 playback_play_pos = 0;
266 }
267 char *play_buffer = playback_buffer[playback_play_ind];
268 int copy_len = MIN(len, PLAYBACK_BUFFER_SIZE - playback_play_pos);
269 memcpy(stream, play_buffer + playback_play_pos, copy_len);
270 len -= copy_len;
271 stream += copy_len;
272 playback_play_pos += copy_len;
273 }
274}
275
276static void playback_start(void)
277{
278 playback_running = true;
279 SDL_AudioSpec spec = {0};
280 spec.freq = NATIVE_FREQUENCY;
281 spec.format = AUDIO_S16SYS;
282 spec.channels = 2;
283 spec.samples = 0x400;
284 spec.callback = playback_callback;
285 spec.userdata = NULL;
286 if (SDL_OpenAudio(&spec, NULL)) {
287 fprintf(stderr, "error: Can't open SDL audio: %s\n", SDL_GetError());
288 exit(1);
289 }
290 SDL_PauseAudio(0);
291}
292
293static void playback_quit(void)
294{
295 if (!playback_running)
296 playback_start();
297 memset(playback_buffer[playback_decode_ind] + playback_decode_pos, 0,
298 PLAYBACK_BUFFER_SIZE - playback_decode_pos);
299 playback_running = false;
300 SDL_SemPost(playback_decode_sema);
301 SDL_SemWait(playback_play_sema);
302 SDL_SemWait(playback_play_sema);
303 SDL_Quit();
304}
305
306static void playback_pcm(int16_t *pcm, int count)
307{
308 const char *stream = (const char *)pcm;
309 count *= 4;
310
311 while (count > 0) {
312 if (playback_decode_pos >= PLAYBACK_BUFFER_SIZE) {
313 if (!playback_running)
314 playback_start();
315 SDL_SemPost(playback_decode_sema);
316 SDL_SemWait(playback_play_sema);
317 playback_decode_ind = !playback_decode_ind;
318 playback_decode_pos = 0;
319 }
320 char *decode_buffer = playback_buffer[playback_decode_ind];
321 int copy_len = MIN(count, PLAYBACK_BUFFER_SIZE - playback_decode_pos);
322 memcpy(decode_buffer + playback_decode_pos, stream, copy_len);
323 stream += copy_len;
324 count -= copy_len;
325 playback_decode_pos += copy_len;
326 }
327}
328
329/***** ALL MODES *****/
330
331static void perform_config(void)
332{
333 /* TODO: equalizer, etc. */
334 while (config) {
335 const char *name = config;
336 const char *eq = strchr(config, '=');
337 if (!eq)
338 break;
339 const char *val = eq + 1;
340 const char *end = val + strcspn(val, ": \t\n");
341
342 if (!strncmp(name, "wait=", 5)) {
343 if (atoi(val) > num_output_samples)
344 return;
345 } else if (!strncmp(name, "dither=", 7)) {
346 dsp_dither_enable(atoi(val) ? true : false);
347 } else if (!strncmp(name, "halt=", 5)) {
348 if (atoi(val))
349 codec_action = CODEC_ACTION_HALT;
350 } else if (!strncmp(name, "loop=", 5)) {
351 enable_loop = atoi(val) != 0;
352 } else if (!strncmp(name, "offset=", 7)) {
353 ci.id3->offset = atoi(val);
354 } else if (!strncmp(name, "rate=", 5)) {
355 sound_set_pitch(atof(val) * PITCH_SPEED_100);
356 } else if (!strncmp(name, "seek=", 5)) {
357 codec_action = CODEC_ACTION_SEEK_TIME;
358 codec_action_param = atoi(val);
359 } else if (!strncmp(name, "tempo=", 6)) {
360 dsp_set_timestretch(atof(val) * PITCH_SPEED_100);
361 } else if (!strncmp(name, "vol=", 4)) {
362 global_settings.volume = atoi(val);
363 dsp_callback(DSP_CALLBACK_SET_SW_VOLUME, 0);
364 } else {
365 fprintf(stderr, "error: unrecognized config \"%.*s\"\n",
366 (int)(eq - name), name);
367 exit(1);
368 }
369
370 if (*end)
371 config = end + 1;
372 else
373 config = NULL;
374 }
375}
376
377static void *ci_codec_get_buffer(size_t *size)
378{
379 static char buffer[64 * 1024 * 1024];
380 char *ptr = buffer;
381 *size = sizeof(buffer);
382 if ((intptr_t)ptr & (CACHEALIGN_SIZE - 1))
383 ptr += CACHEALIGN_SIZE - ((intptr_t)ptr & (CACHEALIGN_SIZE - 1));
384 return ptr;
385}
386
387static void ci_pcmbuf_insert(const void *ch1, const void *ch2, int count)
388{
389 num_output_samples += count;
390
391 if (use_dsp) {
392 const char *src[2] = {ch1, ch2};
393 while (count > 0) {
394 int out_count = dsp_output_count(ci.dsp, count);
395 int in_count = MIN(dsp_input_count(ci.dsp, out_count), count);
396 int16_t buf[2 * out_count];
397 out_count = dsp_process(ci.dsp, (char *)buf, src, in_count);
398 if (mode == MODE_WRITE)
399 write_pcm(buf, out_count);
400 else if (mode == MODE_PLAY)
401 playback_pcm(buf, out_count);
402 count -= in_count;
403 }
404 } else {
405 /* Convert to 32-bit interleaved. */
406 count *= format.channels;
407 int i;
408 int32_t buf[count];
409 if (format.depth > 16) {
410 if (format.stereo_mode == STEREO_NONINTERLEAVED) {
411 for (i = 0; i < count; i += 2) {
412 buf[i+0] = ((int32_t*)ch1)[i/2];
413 buf[i+1] = ((int32_t*)ch2)[i/2];
414 }
415 } else {
416 memcpy(buf, ch1, sizeof(buf));
417 }
418 } else {
419 if (format.stereo_mode == STEREO_NONINTERLEAVED) {
420 for (i = 0; i < count; i += 2) {
421 buf[i+0] = ((int16_t*)ch1)[i/2];
422 buf[i+1] = ((int16_t*)ch2)[i/2];
423 }
424 } else {
425 for (i = 0; i < count; i++) {
426 buf[i] = ((int16_t*)ch1)[i];
427 }
428 }
429 }
430
431 if (mode == MODE_WRITE)
432 write_pcm_raw(buf, count);
433 }
434
435 perform_config();
436}
437
438static void ci_set_elapsed(unsigned long value)
439{
440 //debugf("Time elapsed: %lu\n", value);
441}
442
443static char *input_buffer = 0;
444
445/*
446 * Read part of the input file into a provided buffer.
447 *
448 * The entire size requested will be provided except at the end of the file.
449 * The current file position will be moved, just like with advance_buffer, but
450 * the offset is not updated. This invalidates buffers returned by
451 * request_buffer.
452 */
453static size_t ci_read_filebuf(void *ptr, size_t size)
454{
455 free(input_buffer);
456 input_buffer = NULL;
457
458 ssize_t actual = read(input_fd, ptr, size);
459 if (actual < 0)
460 actual = 0;
461 ci.curpos += actual;
462 return actual;
463}
464
465/*
466 * Request a buffer containing part of the input file.
467 *
468 * The size provided will be the requested size, or the remaining size of the
469 * file, whichever is smaller. Packet audio has an additional maximum of 32
470 * KiB. The returned buffer remains valid until the next time read_filebuf,
471 * request_buffer, advance_buffer, or seek_buffer is called.
472 */
473static void *ci_request_buffer(size_t *realsize, size_t reqsize)
474{
475 free(input_buffer);
476 if (get_audio_base_data_type(ci.id3->codectype) == TYPE_PACKET_AUDIO)
477 reqsize = MIN(reqsize, 32 * 1024);
478 input_buffer = malloc(reqsize);
479 *realsize = read(input_fd, input_buffer, reqsize);
480 if (*realsize < 0)
481 *realsize = 0;
482 lseek(input_fd, -*realsize, SEEK_CUR);
483 return input_buffer;
484}
485
486/*
487 * Advance the current position in the input file.
488 *
489 * This automatically updates the current offset. This invalidates buffers
490 * returned by request_buffer.
491 */
492static void ci_advance_buffer(size_t amount)
493{
494 free(input_buffer);
495 input_buffer = NULL;
496
497 lseek(input_fd, amount, SEEK_CUR);
498 ci.curpos += amount;
499 ci.id3->offset = ci.curpos;
500}
501
502/*
503 * Seek to a position in the input file.
504 *
505 * This invalidates buffers returned by request_buffer.
506 */
507static bool ci_seek_buffer(size_t newpos)
508{
509 free(input_buffer);
510 input_buffer = NULL;
511
512 off_t actual = lseek(input_fd, newpos, SEEK_SET);
513 if (actual >= 0)
514 ci.curpos = actual;
515 return actual != -1;
516}
517
518static void ci_seek_complete(void)
519{
520}
521
522static void ci_set_offset(size_t value)
523{
524 ci.id3->offset = value;
525}
526
527static void ci_configure(int setting, intptr_t value)
528{
529 if (use_dsp) {
530 dsp_configure(ci.dsp, setting, value);
531 } else {
532 if (setting == DSP_SET_FREQUENCY
533 || setting == DSP_SWITCH_FREQUENCY)
534 format.freq = value;
535 else if (setting == DSP_SET_SAMPLE_DEPTH)
536 format.depth = value;
537 else if (setting == DSP_SET_STEREO_MODE) {
538 format.stereo_mode = value;
539 format.channels = (value == STEREO_MONO) ? 1 : 2;
540 }
541 }
542}
543
544static enum codec_command_action ci_get_command(intptr_t *param)
545{
546 enum codec_command_action ret = codec_action;
547 *param = codec_action_param;
548 codec_action = CODEC_ACTION_NULL;
549 return ret;
550}
551
552static bool ci_should_loop(void)
553{
554 return enable_loop;
555}
556
557static unsigned ci_sleep(unsigned ticks)
558{
559 return 0;
560}
561
562static void ci_cpucache_flush(void)
563{
564}
565
566static void ci_cpucache_invalidate(void)
567{
568}
569
570static struct codec_api ci = {
571
572 0, /* filesize */
573 0, /* curpos */
574 NULL, /* id3 */
575 -1, /* audio_hid */
576 NULL, /* struct dsp_config *dsp */
577 ci_codec_get_buffer,
578 ci_pcmbuf_insert,
579 ci_set_elapsed,
580 ci_read_filebuf,
581 ci_request_buffer,
582 ci_advance_buffer,
583 ci_seek_buffer,
584 ci_seek_complete,
585 ci_set_offset,
586 ci_configure,
587 ci_get_command,
588 ci_should_loop,
589
590 ci_sleep,
591 yield,
592
593#if NUM_CORES > 1
594 ci_create_thread,
595 ci_thread_thaw,
596 ci_thread_wait,
597 ci_semaphore_init,
598 ci_semaphore_wait,
599 ci_semaphore_release,
600#endif
601
602 ci_cpucache_flush,
603 ci_cpucache_invalidate,
604
605 /* strings and memory */
606 strcpy,
607 strlen,
608 strcmp,
609 strcat,
610 memset,
611 memcpy,
612 memmove,
613 memcmp,
614 memchr,
615#if defined(DEBUG) || defined(SIMULATOR)
616 debugf,
617#endif
618#ifdef ROCKBOX_HAS_LOGF
619 debugf, /* logf */
620#endif
621
622 qsort,
623
624#ifdef HAVE_RECORDING
625 ci_enc_get_inputs,
626 ci_enc_set_parameters,
627 ci_enc_get_chunk,
628 ci_enc_finish_chunk,
629 ci_enc_get_pcm_data,
630 ci_enc_unget_pcm_data,
631
632 /* file */
633 open,
634 close,
635 read,
636 lseek,
637 write,
638 ci_round_value_to_list32,
639
640#endif /* HAVE_RECORDING */
641};
642
643static void print_mp3entry(const struct mp3entry *id3, FILE *f)
644{
645 fprintf(f, "Path: %s\n", id3->path);
646 if (id3->title) fprintf(f, "Title: %s\n", id3->title);
647 if (id3->artist) fprintf(f, "Artist: %s\n", id3->artist);
648 if (id3->album) fprintf(f, "Album: %s\n", id3->album);
649 if (id3->genre_string) fprintf(f, "Genre: %s\n", id3->genre_string);
650 if (id3->disc_string || id3->discnum) fprintf(f, "Disc: %s (%d)\n", id3->disc_string, id3->discnum);
651 if (id3->track_string || id3->tracknum) fprintf(f, "Track: %s (%d)\n", id3->track_string, id3->tracknum);
652 if (id3->year_string || id3->year) fprintf(f, "Year: %s (%d)\n", id3->year_string, id3->year);
653 if (id3->composer) fprintf(f, "Composer: %s\n", id3->composer);
654 if (id3->comment) fprintf(f, "Comment: %s\n", id3->comment);
655 if (id3->albumartist) fprintf(f, "Album artist: %s\n", id3->albumartist);
656 if (id3->grouping) fprintf(f, "Grouping: %s\n", id3->grouping);
657 if (id3->layer) fprintf(f, "Layer: %d\n", id3->layer);
658 if (id3->id3version) fprintf(f, "ID3 version: %u\n", (int)id3->id3version);
659 fprintf(f, "Codec: %s\n", audio_formats[id3->codectype].label);
660 fprintf(f, "Bitrate: %d kb/s\n", id3->bitrate);
661 fprintf(f, "Frequency: %lu Hz\n", id3->frequency);
662 if (id3->id3v2len) fprintf(f, "ID3v2 length: %lu\n", id3->id3v2len);
663 if (id3->id3v1len) fprintf(f, "ID3v1 length: %lu\n", id3->id3v1len);
664 if (id3->first_frame_offset) fprintf(f, "First frame offset: %lu\n", id3->first_frame_offset);
665 fprintf(f, "File size without headers: %lu\n", id3->filesize);
666 fprintf(f, "Song length: %lu ms\n", id3->length);
667 if (id3->lead_trim > 0 || id3->tail_trim > 0) fprintf(f, "Trim: %d/%d\n", id3->lead_trim, id3->tail_trim);
668 if (id3->samples) fprintf(f, "Number of samples: %lu\n", id3->samples);
669 if (id3->frame_count) fprintf(f, "Number of frames: %lu\n", id3->frame_count);
670 if (id3->bytesperframe) fprintf(f, "Bytes per frame: %lu\n", id3->bytesperframe);
671 if (id3->vbr) fprintf(f, "VBR: true\n");
672 if (id3->has_toc) fprintf(f, "Has TOC: true\n");
673 if (id3->channels) fprintf(f, "Number of channels: %u\n", id3->channels);
674 if (id3->extradata_size) fprintf(f, "Size of extra data: %u\n", id3->extradata_size);
675 if (id3->needs_upsampling_correction) fprintf(f, "Needs upsampling correction: true\n");
676 /* TODO: replaygain; albumart; cuesheet */
677 if (id3->mb_track_id) fprintf(f, "Musicbrainz track ID: %s\n", id3->mb_track_id);
678}
679
680static void decode_file(const char *input_fn)
681{
682 /* Set up global settings */
683 memset(&global_settings, 0, sizeof(global_settings));
684 global_settings.timestretch_enabled = true;
685 dsp_timestretch_enable(true);
686 tdspeed_init();
687
688 /* Open file */
689 if (!strcmp(input_fn, "-")) {
690 input_fd = STDIN_FILENO;
691 } else {
692 input_fd = open(input_fn, O_RDONLY);
693 if (input_fd == -1) {
694 perror(input_fn);
695 exit(1);
696 }
697 }
698
699 /* Set up ci */
700 struct mp3entry id3;
701 if (!get_metadata(&id3, input_fd, input_fn)) {
702 fprintf(stderr, "error: metadata parsing failed\n");
703 exit(1);
704 }
705 print_mp3entry(&id3, stderr);
706 ci.filesize = filesize(input_fd);
707 ci.id3 = &id3;
708 if (use_dsp) {
709 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, CODEC_IDX_AUDIO);
710 dsp_configure(ci.dsp, DSP_RESET, 0);
711 dsp_dither_enable(false);
712 }
713 perform_config();
714
715 /* Load codec */
716 char str[MAX_PATH];
717 snprintf(str, sizeof(str), CODECDIR"/%s.codec", audio_formats[id3.codectype].codec_root_fn);
718 debugf("Loading %s\n", str);
719 void *dlcodec = dlopen(str, RTLD_NOW);
720 if (!dlcodec) {
721 fprintf(stderr, "error: dlopen failed: %s\n", dlerror());
722 exit(1);
723 }
724 struct codec_header *c_hdr = NULL;
725 c_hdr = dlsym(dlcodec, "__header");
726 if (c_hdr->lc_hdr.magic != CODEC_MAGIC) {
727 fprintf(stderr, "error: %s invalid: incorrect magic\n", str);
728 exit(1);
729 }
730 if (c_hdr->lc_hdr.target_id != TARGET_ID) {
731 fprintf(stderr, "error: %s invalid: incorrect target id\n", str);
732 exit(1);
733 }
734 if (c_hdr->lc_hdr.api_version != CODEC_API_VERSION) {
735 fprintf(stderr, "error: %s invalid: incorrect API version\n", str);
736 exit(1);
737 }
738
739 /* Run the codec */
740 *c_hdr->api = &ci;
741 if (c_hdr->entry_point(CODEC_LOAD) != CODEC_OK) {
742 fprintf(stderr, "error: codec returned error from codec_main\n");
743 exit(1);
744 }
745 if (c_hdr->run_proc() != CODEC_OK) {
746 fprintf(stderr, "error: codec error\n");
747 }
748 c_hdr->entry_point(CODEC_UNLOAD);
749
750 /* Close */
751 dlclose(dlcodec);
752 if (input_fd != STDIN_FILENO)
753 close(input_fd);
754}
755
756static void print_help(const char *progname)
757{
758 fprintf(stderr, "Usage:\n"
759 " Play: %s [options] INPUTFILE\n"
760 "Write to WAV: %s [options] INPUTFILE OUTPUTFILE\n"
761 "\n"
762 "general options:\n"
763 " -c a=1:b=2 Configuration (see below)\n"
764 " -h Show this help\n"
765 "\n"
766 "write to WAV options:\n"
767 " -f Write raw codec output converted to 64-bit float\n"
768 " -r Write raw 32-bit codec output without WAV header\n"
769 "\n"
770 "configuration:\n"
771 " dither=<0|1> Enable/disable dithering [0]\n"
772 " halt=<0|1> Stop decoding if 1 [0]\n"
773 " loop=<0|1> Enable/disable looping [0]\n"
774 " offset=<n> Start at byte offset within the file [0]\n"
775 " rate=<n> Multiply rate by <n> [1.0]\n"
776 " seek=<n> Seek <n> ms into the file\n"
777 " tempo=<n> Timestretch by <n> [1.0]\n"
778 " vol=<n> Set volume to <n> dB [0]\n"
779 " wait=<n> Don't apply remaining configuration until\n"
780 " <n> total samples have output\n"
781 "\n"
782 "examples:\n"
783 " # Play while looping; stop after 44100 output samples\n"
784 " %s in.adx -c loop=1:wait=44100:halt=1\n"
785 " # Lower pitch 1 octave and write to out.wav\n"
786 " %s in.ogg -c rate=0.5:tempo=2 out.wav\n"
787 , progname, progname, progname, progname);
788}
789
790int main(int argc, char **argv)
791{
792 int opt;
793 while ((opt = getopt(argc, argv, "c:fhr")) != -1) {
794 switch (opt) {
795 case 'c':
796 config = optarg;
797 break;
798 case 'f':
799 use_dsp = false;
800 break;
801 case 'r':
802 use_dsp = false;
803 write_raw = true;
804 break;
805 case 'h': /* fallthrough */
806 default:
807 print_help(argv[0]);
808 exit(1);
809 }
810 }
811
812 core_allocator_init();
813 if (argc == optind + 2) {
814 write_init(argv[optind + 1]);
815 } else if (argc == optind + 1) {
816 if (!use_dsp) {
817 fprintf(stderr, "error: -r can't be used for playback\n");
818 print_help(argv[0]);
819 exit(1);
820 }
821 playback_init();
822 } else {
823 if (argc > 1)
824 fprintf(stderr, "error: wrong number of arguments\n");
825 print_help(argv[0]);
826 exit(1);
827 }
828
829 decode_file(argv[optind]);
830
831 if (mode == MODE_WRITE)
832 write_quit();
833 else if (mode == MODE_PLAY)
834 playback_quit();
835
836 return 0;
837}