diff options
author | Dave Chapman <dave@dchapman.com> | 2007-05-07 17:23:31 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2007-05-07 17:23:31 +0000 |
commit | 7cdd0fe6ea8561a94862f0b0dd53fa8b5a7628e9 (patch) | |
tree | 59448bc2cbc66bb7369d87a3864162d90618ba38 | |
parent | 1feb8bd4a11cdfc552799958ad8539090eec2b74 (diff) | |
download | rockbox-7cdd0fe6ea8561a94862f0b0dd53fa8b5a7628e9.tar.gz rockbox-7cdd0fe6ea8561a94862f0b0dd53fa8b5a7628e9.zip |
Initial version of a test_codec plugin (viewer). This loads the audio file into the audio buffer and decodes it as fast as it can via a locally implemented version of the codec API. Intended for use when optimising codecs - so isn't built by default. Remember to add it to both plugins/SOURCES and viewers.config to enable it. Currently the codec is run in the main thread which means mpa.codec doesn't work - it requires more stack than is available on the main thread. The solution will be to create a new thread in the plugin which steals the main codec thread's IRAM stack, but that's not done yet.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13345 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/playback.c | 3 | ||||
-rw-r--r-- | apps/playback.h | 1 | ||||
-rw-r--r-- | apps/plugin.c | 6 | ||||
-rw-r--r-- | apps/plugin.h | 12 | ||||
-rw-r--r-- | apps/plugins/test_codec.c | 345 |
5 files changed, 364 insertions, 3 deletions
diff --git a/apps/playback.c b/apps/playback.c index b26754eb3a..889cf9406e 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -275,7 +275,6 @@ static size_t high_watermark = 0; /* High watermark for rebuffer (A/V/other) */ | |||
275 | 275 | ||
276 | /* Multiple threads */ | 276 | /* Multiple threads */ |
277 | static void set_current_codec(int codec_idx); | 277 | static void set_current_codec(int codec_idx); |
278 | static const char *get_codec_filename(int enc_spec); /* (A-/C-/V-) */ | ||
279 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ | 278 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ |
280 | static void set_filebuf_watermark(int seconds); | 279 | static void set_filebuf_watermark(int seconds); |
281 | 280 | ||
@@ -995,7 +994,7 @@ static void set_filebuf_watermark(int seconds) | |||
995 | conf_watermark = bytes; | 994 | conf_watermark = bytes; |
996 | } | 995 | } |
997 | 996 | ||
998 | static const char * get_codec_filename(int cod_spec) | 997 | const char * get_codec_filename(int cod_spec) |
999 | { | 998 | { |
1000 | const char *fname; | 999 | const char *fname; |
1001 | 1000 | ||
diff --git a/apps/playback.h b/apps/playback.h index 9a8bd10390..eaab4386e0 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -57,6 +57,7 @@ struct track_info { | |||
57 | }; | 57 | }; |
58 | 58 | ||
59 | /* Functions */ | 59 | /* Functions */ |
60 | const char * get_codec_filename(int cod_spec); | ||
60 | void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3)); | 61 | void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3)); |
61 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, | 62 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, |
62 | bool last_track)); | 63 | bool last_track)); |
diff --git a/apps/plugin.c b/apps/plugin.c index ea228c454c..9e17c232ce 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -488,6 +488,12 @@ static const struct plugin_api rockbox_api = { | |||
488 | spinlock_lock, | 488 | spinlock_lock, |
489 | spinlock_unlock, | 489 | spinlock_unlock, |
490 | #endif | 490 | #endif |
491 | |||
492 | #if (CONFIG_CODEC == SWCODEC) | ||
493 | codec_load_file, | ||
494 | get_metadata, | ||
495 | get_codec_filename, | ||
496 | #endif | ||
491 | }; | 497 | }; |
492 | 498 | ||
493 | int plugin_load(const char* plugin, void* parameter) | 499 | int plugin_load(const char* plugin, void* parameter) |
diff --git a/apps/plugin.h b/apps/plugin.h index 987c07067d..cb8324febe 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -59,6 +59,9 @@ | |||
59 | #include "misc.h" | 59 | #include "misc.h" |
60 | #if (CONFIG_CODEC == SWCODEC) | 60 | #if (CONFIG_CODEC == SWCODEC) |
61 | #include "dsp.h" | 61 | #include "dsp.h" |
62 | #include "codecs.h" | ||
63 | #include "playback.h" | ||
64 | #include "metadata.h" | ||
62 | #ifdef HAVE_RECORDING | 65 | #ifdef HAVE_RECORDING |
63 | #include "recording.h" | 66 | #include "recording.h" |
64 | #endif | 67 | #endif |
@@ -110,7 +113,7 @@ | |||
110 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ | 113 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ |
111 | 114 | ||
112 | /* increase this every time the api struct changes */ | 115 | /* increase this every time the api struct changes */ |
113 | #define PLUGIN_API_VERSION 54 | 116 | #define PLUGIN_API_VERSION 55 |
114 | 117 | ||
115 | /* update this to latest version if a change to the api struct breaks | 118 | /* update this to latest version if a change to the api struct breaks |
116 | backwards compatibility (and please take the opportunity to sort in any | 119 | backwards compatibility (and please take the opportunity to sort in any |
@@ -602,6 +605,13 @@ struct plugin_api { | |||
602 | void (*spinlock_lock)(struct mutex *m); | 605 | void (*spinlock_lock)(struct mutex *m); |
603 | void (*spinlock_unlock)(struct mutex *m); | 606 | void (*spinlock_unlock)(struct mutex *m); |
604 | #endif | 607 | #endif |
608 | |||
609 | #if (CONFIG_CODEC == SWCODEC) | ||
610 | int (*codec_load_file)(const char* codec, struct codec_api *api); | ||
611 | bool (*get_metadata)(struct track_info* track, int fd, const char* trackname, | ||
612 | bool v1first); | ||
613 | const char *(*get_codec_filename)(int cod_spec); | ||
614 | #endif | ||
605 | }; | 615 | }; |
606 | 616 | ||
607 | /* plugin header */ | 617 | /* plugin header */ |
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c new file mode 100644 index 0000000000..56d59b3ab3 --- /dev/null +++ b/apps/plugins/test_codec.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: $ | ||
9 | * | ||
10 | * Copyright (C) 2007 Dave Chapman | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "plugin.h" | ||
20 | |||
21 | PLUGIN_HEADER | ||
22 | |||
23 | static struct plugin_api* rb; | ||
24 | |||
25 | static void* audiobuf; | ||
26 | static void* codec_mallocbuf; | ||
27 | static size_t audiosize; | ||
28 | static char str[40]; | ||
29 | |||
30 | /* Our local implementation of the codec API */ | ||
31 | static struct codec_api ci; | ||
32 | |||
33 | |||
34 | static struct track_info track; | ||
35 | |||
36 | static bool taginfo_ready = true; | ||
37 | |||
38 | |||
39 | /* Returns buffer to malloc array. Only codeclib should need this. */ | ||
40 | static void* get_codec_memory(size_t *size) | ||
41 | { | ||
42 | DEBUGF("get_codec_memory(%d)\n",(int)size); | ||
43 | *size = 512*1024; | ||
44 | return codec_mallocbuf; | ||
45 | } | ||
46 | |||
47 | |||
48 | /* Insert PCM data into audio buffer for playback. Playback will start | ||
49 | automatically. */ | ||
50 | static bool pcmbuf_insert(const void *ch1, const void *ch2, int count) | ||
51 | { | ||
52 | /* Always successful - just discard data */ | ||
53 | (void)ch1; | ||
54 | (void)ch2; | ||
55 | (void)count; | ||
56 | |||
57 | return true; | ||
58 | } | ||
59 | |||
60 | |||
61 | static unsigned int prev_value = 0; | ||
62 | |||
63 | /* Set song position in WPS (value in ms). */ | ||
64 | static void set_elapsed(unsigned int value) | ||
65 | { | ||
66 | if ((value - prev_value) > 2000) | ||
67 | { | ||
68 | rb->snprintf(str,sizeof(str),"%d of %d",value,(int)track.id3.length); | ||
69 | rb->lcd_puts(0,0,str); | ||
70 | rb->lcd_update(); | ||
71 | prev_value = value; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | |||
76 | /* Read next <size> amount bytes from file buffer to <ptr>. | ||
77 | Will return number of bytes read or 0 if end of file. */ | ||
78 | static size_t read_filebuf(void *ptr, size_t size) | ||
79 | { | ||
80 | DEBUGF("read_filebuf(_,%d)\n",(int)size); | ||
81 | if (ci.curpos > (off_t)track.filesize) | ||
82 | { | ||
83 | return 0; | ||
84 | } else { | ||
85 | /* TODO: Don't read beyond end of buffer */ | ||
86 | rb->memcpy(ptr, audiobuf + ci.curpos, size); | ||
87 | ci.curpos += size; | ||
88 | DEBUGF("New ci.curpos = %d\n",ci.curpos); | ||
89 | return size; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | |||
94 | /* Request pointer to file buffer which can be used to read | ||
95 | <realsize> amount of data. <reqsize> tells the buffer system | ||
96 | how much data it should try to allocate. If <realsize> is 0, | ||
97 | end of file is reached. */ | ||
98 | static void* request_buffer(size_t *realsize, size_t reqsize) | ||
99 | { | ||
100 | *realsize = MIN(track.filesize-ci.curpos,reqsize); | ||
101 | |||
102 | return (audiobuf + ci.curpos); | ||
103 | } | ||
104 | |||
105 | |||
106 | /* Advance file buffer position by <amount> amount of bytes. */ | ||
107 | static void advance_buffer(size_t amount) | ||
108 | { | ||
109 | ci.curpos += amount; | ||
110 | DEBUGF("advance_buffer(%d) - new ci.curpos=%d\n",(int)amount,(int)ci.curpos); | ||
111 | } | ||
112 | |||
113 | |||
114 | /* Advance file buffer to a pointer location inside file buffer. */ | ||
115 | static void advance_buffer_loc(void *ptr) | ||
116 | { | ||
117 | ci.curpos = ptr - audiobuf; | ||
118 | } | ||
119 | |||
120 | |||
121 | /* Seek file buffer to position <newpos> beginning of file. */ | ||
122 | static bool seek_buffer(size_t newpos) | ||
123 | { | ||
124 | ci.curpos = newpos; | ||
125 | return true; | ||
126 | } | ||
127 | |||
128 | |||
129 | /* Codec should call this function when it has done the seeking. */ | ||
130 | static void seek_complete(void) | ||
131 | { | ||
132 | /* Do nothing */ | ||
133 | } | ||
134 | |||
135 | |||
136 | /* Calculate mp3 seek position from given time data in ms. */ | ||
137 | static off_t mp3_get_filepos(int newtime) | ||
138 | { | ||
139 | /* We don't ask the codec to seek, so no need to implement this. */ | ||
140 | (void)newtime; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | |||
145 | /* Request file change from file buffer. Returns true is next | ||
146 | track is available and changed. If return value is false, | ||
147 | codec should exit immediately with PLUGIN_OK status. */ | ||
148 | static bool request_next_track(void) | ||
149 | { | ||
150 | /* We are only decoding a single track */ | ||
151 | return false; | ||
152 | } | ||
153 | |||
154 | |||
155 | /* Free the buffer area of the current codec after its loaded */ | ||
156 | static void discard_codec(void) | ||
157 | { | ||
158 | /* ??? */ | ||
159 | } | ||
160 | |||
161 | |||
162 | static void set_offset(size_t value) | ||
163 | { | ||
164 | DEBUGF("set_offset(%d)\n",(int)value); | ||
165 | /* ??? */ | ||
166 | (void)value; | ||
167 | } | ||
168 | |||
169 | |||
170 | /* Configure different codec buffer parameters. */ | ||
171 | static void configure(int setting, intptr_t value) | ||
172 | { | ||
173 | (void)setting; | ||
174 | (void)value; | ||
175 | DEBUGF("setting %d = %d\n",setting,(int)value); | ||
176 | } | ||
177 | |||
178 | static void init_ci(void) | ||
179 | { | ||
180 | /* --- Our "fake" implementations of the codec API functions. --- */ | ||
181 | |||
182 | ci.get_codec_memory = get_codec_memory; | ||
183 | ci.pcmbuf_insert = pcmbuf_insert; | ||
184 | ci.set_elapsed = set_elapsed; | ||
185 | ci.read_filebuf = read_filebuf; | ||
186 | ci.request_buffer = request_buffer; | ||
187 | ci.advance_buffer = advance_buffer; | ||
188 | ci.advance_buffer_loc = advance_buffer_loc; | ||
189 | ci.seek_buffer = seek_buffer; | ||
190 | ci.seek_complete = seek_complete; | ||
191 | ci.mp3_get_filepos = mp3_get_filepos; | ||
192 | ci.request_next_track = request_next_track; | ||
193 | ci.discard_codec = discard_codec; | ||
194 | ci.set_offset = set_offset; | ||
195 | ci.configure = configure; | ||
196 | |||
197 | /* --- "Core" functions --- */ | ||
198 | |||
199 | /* kernel/ system */ | ||
200 | ci.PREFIX(sleep) = rb->PREFIX(sleep); | ||
201 | ci.yield = rb->yield; | ||
202 | |||
203 | /* strings and memory */ | ||
204 | ci.strcpy = rb->strcpy; | ||
205 | ci.strncpy = rb->strncpy; | ||
206 | ci.strlen = rb->strlen; | ||
207 | ci.strcmp = rb->strcmp; | ||
208 | ci.strcat = rb->strcat; | ||
209 | ci.memset = rb->memset; | ||
210 | ci.memcpy = rb->memcpy; | ||
211 | ci.memmove = rb->memmove; | ||
212 | ci.memcmp = rb->memcmp; | ||
213 | ci.memchr = rb->memchr; | ||
214 | |||
215 | #if defined(DEBUG) || defined(SIMULATOR) | ||
216 | ci.debugf = rb->debugf; | ||
217 | #endif | ||
218 | #ifdef ROCKBOX_HAS_LOGF | ||
219 | ci.logf = rb->logf; | ||
220 | #endif | ||
221 | |||
222 | ci.qsort = rb->qsort; | ||
223 | ci.global_settings = rb->global_settings; | ||
224 | |||
225 | #ifdef RB_PROFILE | ||
226 | ci.profile_thread = rb->profile_thread; | ||
227 | ci.profstop = rb->profstop; | ||
228 | ci.profile_func_enter = rb->profile_func_enter; | ||
229 | ci.profile_func_exit = rb->profile_func_exit; | ||
230 | #endif | ||
231 | } | ||
232 | |||
233 | /* plugin entry point */ | ||
234 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
235 | { | ||
236 | size_t n; | ||
237 | int fd; | ||
238 | const char* codecname; | ||
239 | int res; | ||
240 | unsigned long starttick; | ||
241 | unsigned long ticks; | ||
242 | unsigned long speed; | ||
243 | unsigned long duration; | ||
244 | |||
245 | rb = api; | ||
246 | |||
247 | if (parameter == NULL) | ||
248 | { | ||
249 | rb->splash(HZ*2, "No File"); | ||
250 | return PLUGIN_ERROR; | ||
251 | } | ||
252 | |||
253 | codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize); | ||
254 | audiobuf = codec_mallocbuf + 512*1024; | ||
255 | audiosize -= 512*1024; | ||
256 | |||
257 | fd = rb->open(parameter,O_RDONLY); | ||
258 | if (fd < 0) | ||
259 | { | ||
260 | rb->splash(HZ*2, "Cannot open file"); | ||
261 | return PLUGIN_ERROR; | ||
262 | } | ||
263 | |||
264 | track.filesize = rb->filesize(fd); | ||
265 | |||
266 | if (!rb->get_metadata(&track, fd, parameter, | ||
267 | rb->global_settings->id3_v1_first)) | ||
268 | { | ||
269 | rb->splash(HZ*2, "Cannot read metadata"); | ||
270 | return PLUGIN_ERROR; | ||
271 | } | ||
272 | |||
273 | if (track.filesize > audiosize) | ||
274 | { | ||
275 | rb->splash(HZ*2, "File too large"); | ||
276 | return PLUGIN_ERROR; | ||
277 | } | ||
278 | |||
279 | rb->splash(0, "Loading..."); | ||
280 | |||
281 | n = rb->read(fd, audiobuf, track.filesize); | ||
282 | |||
283 | if (n != track.filesize) | ||
284 | { | ||
285 | rb->splash(HZ*2, "Read failed."); | ||
286 | return PLUGIN_ERROR; | ||
287 | } | ||
288 | |||
289 | /* Initialise the function pointers in the codec API */ | ||
290 | init_ci(); | ||
291 | |||
292 | /* Prepare the codec struct for playing the whole file */ | ||
293 | ci.filesize = track.filesize; | ||
294 | ci.id3 = &track.id3; | ||
295 | ci.taginfo_ready = &taginfo_ready; | ||
296 | ci.curpos = 0; | ||
297 | ci.stop_codec = false; | ||
298 | ci.new_track = 0; | ||
299 | ci.seek_time = 0; | ||
300 | |||
301 | codecname = rb->get_codec_filename(track.id3.codectype); | ||
302 | |||
303 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
304 | rb->cpu_boost(true); | ||
305 | #endif | ||
306 | rb->lcd_set_backdrop(NULL); | ||
307 | rb->lcd_set_foreground(LCD_WHITE); | ||
308 | rb->lcd_set_background(LCD_BLACK); | ||
309 | rb->lcd_clear_display(); | ||
310 | rb->lcd_update(); | ||
311 | |||
312 | starttick = *rb->current_tick; | ||
313 | |||
314 | /* Load the codec and start decoding. */ | ||
315 | res = rb->codec_load_file(codecname,&ci); | ||
316 | |||
317 | |||
318 | /* Display benchmark information */ | ||
319 | |||
320 | ticks = *rb->current_tick - starttick; | ||
321 | rb->snprintf(str,sizeof(str),"Decode time - %d.%02ds",(int)ticks/100,(int)ticks%100); | ||
322 | rb->lcd_puts(0,1,str); | ||
323 | |||
324 | duration = track.id3.length / 10; | ||
325 | rb->snprintf(str,sizeof(str),"File duration - %d.%02ds",(int)duration/100,(int)duration%100); | ||
326 | rb->lcd_puts(0,2,str); | ||
327 | |||
328 | if (ticks > 0) | ||
329 | speed = duration * 10000 / ticks; | ||
330 | else | ||
331 | speed = 0; | ||
332 | |||
333 | rb->snprintf(str,sizeof(str),"%d.%02d%% realtime",(int)speed/100,(int)speed%100); | ||
334 | rb->lcd_puts(0,3,str); | ||
335 | |||
336 | rb->lcd_update(); | ||
337 | |||
338 | while (rb->button_get(true) != BUTTON_SELECT); | ||
339 | |||
340 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
341 | rb->cpu_boost(false); | ||
342 | #endif | ||
343 | |||
344 | return PLUGIN_OK; | ||
345 | } | ||