diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2005-08-20 11:13:19 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2005-08-20 11:13:19 +0000 |
commit | 159c52dd36e5c008612458192904f57ea6dfdfad (patch) | |
tree | 4b6f7d8329069e90d72284ef73ba542d75705b55 /apps | |
parent | 329caa8ade0b78a3235e9d28983cb1c506e573a0 (diff) | |
download | rockbox-159c52dd36e5c008612458192904f57ea6dfdfad.tar.gz rockbox-159c52dd36e5c008612458192904f57ea6dfdfad.zip |
Initial voice ui support for software codec platforms. Added also a
beep when changing tracks.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7360 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/SOURCES | 2 | ||||
-rw-r--r-- | apps/codecs.c | 13 | ||||
-rw-r--r-- | apps/codecs.h | 5 | ||||
-rw-r--r-- | apps/codecs/wav.c | 13 | ||||
-rw-r--r-- | apps/debug_menu.c | 10 | ||||
-rw-r--r-- | apps/dsp.c | 195 | ||||
-rw-r--r-- | apps/main.c | 3 | ||||
-rw-r--r-- | apps/pcmbuf.c | 104 | ||||
-rw-r--r-- | apps/pcmbuf.h | 7 | ||||
-rw-r--r-- | apps/playback.c | 507 | ||||
-rw-r--r-- | apps/playback.h | 1 | ||||
-rw-r--r-- | apps/playlist.c | 7 | ||||
-rw-r--r-- | apps/talk.c | 111 | ||||
-rw-r--r-- | apps/talk.h | 1 |
14 files changed, 765 insertions, 214 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index c1f3dfc88e..e8fd2d2ddb 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -22,7 +22,7 @@ settings_menu.c | |||
22 | sleeptimer.c | 22 | sleeptimer.c |
23 | sound_menu.c | 23 | sound_menu.c |
24 | status.c | 24 | status.c |
25 | #ifndef SIMULATOR | 25 | #if !defined(SIMULATOR) || CONFIG_HWCODEC == MASNONE |
26 | talk.c | 26 | talk.c |
27 | #endif | 27 | #endif |
28 | tree.c | 28 | tree.c |
diff --git a/apps/codecs.c b/apps/codecs.c index b1d30868bc..004d4681f7 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -54,7 +54,7 @@ | |||
54 | 54 | ||
55 | #ifdef SIMULATOR | 55 | #ifdef SIMULATOR |
56 | #if CONFIG_HWCODEC == MASNONE | 56 | #if CONFIG_HWCODEC == MASNONE |
57 | static unsigned char codecbuf[CODEC_SIZE]; | 57 | unsigned char codecbuf[CODEC_SIZE]; |
58 | #endif | 58 | #endif |
59 | void *sim_codec_load_ram(char* codecptr, int size, | 59 | void *sim_codec_load_ram(char* codecptr, int size, |
60 | void* ptr2, int bufwrap, int *pd); | 60 | void* ptr2, int bufwrap, int *pd); |
@@ -68,6 +68,8 @@ extern void* plugin_get_audio_buffer(int *buffer_size); | |||
68 | 68 | ||
69 | static int codec_test(int api_version, int model, int memsize); | 69 | static int codec_test(int api_version, int model, int memsize); |
70 | 70 | ||
71 | struct codec_api ci_voice; | ||
72 | |||
71 | struct codec_api ci = { | 73 | struct codec_api ci = { |
72 | CODEC_API_VERSION, | 74 | CODEC_API_VERSION, |
73 | codec_test, | 75 | codec_test, |
@@ -247,7 +249,8 @@ struct codec_api ci = { | |||
247 | NULL, | 249 | NULL, |
248 | }; | 250 | }; |
249 | 251 | ||
250 | int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap) | 252 | int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, |
253 | struct codec_api *api) | ||
251 | { | 254 | { |
252 | enum codec_status (*codec_start)(const struct codec_api* api); | 255 | enum codec_status (*codec_start)(const struct codec_api* api); |
253 | int status; | 256 | int status; |
@@ -277,7 +280,7 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap) | |||
277 | #endif /* SIMULATOR */ | 280 | #endif /* SIMULATOR */ |
278 | 281 | ||
279 | invalidate_icache(); | 282 | invalidate_icache(); |
280 | status = codec_start(&ci); | 283 | status = codec_start(api); |
281 | #ifdef SIMULATOR | 284 | #ifdef SIMULATOR |
282 | sim_codec_close(pd); | 285 | sim_codec_close(pd); |
283 | #endif | 286 | #endif |
@@ -285,7 +288,7 @@ int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap) | |||
285 | return status; | 288 | return status; |
286 | } | 289 | } |
287 | 290 | ||
288 | int codec_load_file(const char *plugin) | 291 | int codec_load_file(const char *plugin, struct codec_api *api) |
289 | { | 292 | { |
290 | char msgbuf[80]; | 293 | char msgbuf[80]; |
291 | int fd; | 294 | int fd; |
@@ -309,7 +312,7 @@ int codec_load_file(const char *plugin) | |||
309 | return CODEC_ERROR; | 312 | return CODEC_ERROR; |
310 | } | 313 | } |
311 | 314 | ||
312 | return codec_load_ram(codecbuf, (size_t)rc, NULL, 0); | 315 | return codec_load_ram(codecbuf, (size_t)rc, NULL, 0, api); |
313 | } | 316 | } |
314 | 317 | ||
315 | static int codec_test(int api_version, int model, int memsize) | 318 | static int codec_test(int api_version, int model, int memsize) |
diff --git a/apps/codecs.h b/apps/codecs.h index b2cf9e5a1e..3b8e1d8394 100644 --- a/apps/codecs.h +++ b/apps/codecs.h | |||
@@ -331,8 +331,9 @@ struct codec_api { | |||
331 | 331 | ||
332 | /* defined by the codec loader (codec.c) */ | 332 | /* defined by the codec loader (codec.c) */ |
333 | #if CONFIG_HWCODEC == MASNONE | 333 | #if CONFIG_HWCODEC == MASNONE |
334 | int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap); | 334 | int codec_load_ram(char* codecptr, int size, void* ptr2, int bufwrap, |
335 | int codec_load_file(const char* codec); | 335 | struct codec_api *api); |
336 | int codec_load_file(const char* codec, struct codec_api *api); | ||
336 | #endif | 337 | #endif |
337 | 338 | ||
338 | /* defined by the codec */ | 339 | /* defined by the codec */ |
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c index 527d33d286..d2ae7bd6c3 100644 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c | |||
@@ -71,15 +71,12 @@ enum codec_status codec_start(struct codec_api* api) | |||
71 | return CODEC_ERROR; | 71 | return CODEC_ERROR; |
72 | } | 72 | } |
73 | 73 | ||
74 | while (!rb->taginfo_ready) | 74 | while (!*rb->taginfo_ready) |
75 | rb->yield(); | 75 | rb->yield(); |
76 | 76 | ||
77 | if (rb->id3->frequency != NATIVE_FREQUENCY) { | 77 | /* Always enable DSP to support voice ui. */ |
78 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); | 78 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); |
79 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); | 79 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); |
80 | } else { | ||
81 | rb->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
82 | } | ||
83 | 80 | ||
84 | /* FIX: Correctly parse WAV header - we assume canonical 44-byte header */ | 81 | /* FIX: Correctly parse WAV header - we assume canonical 44-byte header */ |
85 | 82 | ||
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 86e02a1e84..b4cd05699c 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -208,8 +208,8 @@ bool dbg_audio_thread(void) | |||
208 | } | 208 | } |
209 | #else | 209 | #else |
210 | extern size_t audiobuffer_free; | 210 | extern size_t audiobuffer_free; |
211 | extern int codecbuflen; | 211 | extern int filebuflen; |
212 | extern int codecbufused; | 212 | extern int filebufused; |
213 | extern int track_count; | 213 | extern int track_count; |
214 | 214 | ||
215 | static int ticks, boost_ticks; | 215 | static int ticks, boost_ticks; |
@@ -260,12 +260,12 @@ bool dbg_audio_thread(void) | |||
260 | bufsize-audiobuffer_free, HORIZONTAL); | 260 | bufsize-audiobuffer_free, HORIZONTAL); |
261 | line++; | 261 | line++; |
262 | 262 | ||
263 | snprintf(buf, sizeof(buf), "codec: %d/%d", codecbufused, codecbuflen); | 263 | snprintf(buf, sizeof(buf), "codec: %d/%d", filebufused, filebuflen); |
264 | lcd_puts(0, line++, buf); | 264 | lcd_puts(0, line++, buf); |
265 | 265 | ||
266 | /* Playable space left */ | 266 | /* Playable space left */ |
267 | scrollbar(0, line*8, LCD_WIDTH, 6, codecbuflen, 0, | 267 | scrollbar(0, line*8, LCD_WIDTH, 6, filebuflen, 0, |
268 | codecbufused, HORIZONTAL); | 268 | filebufused, HORIZONTAL); |
269 | line++; | 269 | line++; |
270 | 270 | ||
271 | snprintf(buf, sizeof(buf), "track count: %d", track_count); | 271 | snprintf(buf, sizeof(buf), "track count: %d", track_count); |
diff --git a/apps/dsp.c b/apps/dsp.c index 2ba7fb99f6..8064a881a3 100644 --- a/apps/dsp.c +++ b/apps/dsp.c | |||
@@ -132,9 +132,12 @@ struct dither_data | |||
132 | long random; | 132 | long random; |
133 | }; | 133 | }; |
134 | 134 | ||
135 | static struct dsp_config dsp IDATA_ATTR; | 135 | static struct dsp_config dsp_conf[2] IDATA_ATTR; |
136 | static struct dither_data dither_data[2] IDATA_ATTR; | 136 | static struct dither_data dither_data[2] IDATA_ATTR; |
137 | static struct resample_data resample_data[2] IDATA_ATTR; | 137 | static struct resample_data resample_data[2][2] IDATA_ATTR; |
138 | |||
139 | extern int current_codec; | ||
140 | struct dsp_config *dsp; | ||
138 | 141 | ||
139 | /* The internal format is 32-bit samples, non-interleaved, stereo. This | 142 | /* The internal format is 32-bit samples, non-interleaved, stereo. This |
140 | * format is similar to the raw output from several codecs, so the amount | 143 | * format is similar to the raw output from several codecs, so the amount |
@@ -155,20 +158,20 @@ static int convert_to_internal(char* src[], int count, long* dst[]) | |||
155 | { | 158 | { |
156 | count = MIN(SAMPLE_BUF_SIZE / 2, count); | 159 | count = MIN(SAMPLE_BUF_SIZE / 2, count); |
157 | 160 | ||
158 | if ((dsp.sample_depth <= NATIVE_DEPTH) | 161 | if ((dsp->sample_depth <= NATIVE_DEPTH) |
159 | || (dsp.stereo_mode == STEREO_INTERLEAVED)) | 162 | || (dsp->stereo_mode == STEREO_INTERLEAVED)) |
160 | { | 163 | { |
161 | dst[0] = &sample_buf[0]; | 164 | dst[0] = &sample_buf[0]; |
162 | dst[1] = (dsp.stereo_mode == STEREO_MONO) | 165 | dst[1] = (dsp->stereo_mode == STEREO_MONO) |
163 | ? dst[0] : &sample_buf[SAMPLE_BUF_SIZE / 2]; | 166 | ? dst[0] : &sample_buf[SAMPLE_BUF_SIZE / 2]; |
164 | } | 167 | } |
165 | else | 168 | else |
166 | { | 169 | { |
167 | dst[0] = (long*) src[0]; | 170 | dst[0] = (long*) src[0]; |
168 | dst[1] = (long*) ((dsp.stereo_mode == STEREO_MONO) ? src[0] : src[1]); | 171 | dst[1] = (long*) ((dsp->stereo_mode == STEREO_MONO) ? src[0] : src[1]); |
169 | } | 172 | } |
170 | 173 | ||
171 | if (dsp.sample_depth <= NATIVE_DEPTH) | 174 | if (dsp->sample_depth <= NATIVE_DEPTH) |
172 | { | 175 | { |
173 | short* s0 = (short*) src[0]; | 176 | short* s0 = (short*) src[0]; |
174 | long* d0 = dst[0]; | 177 | long* d0 = dst[0]; |
@@ -176,7 +179,7 @@ static int convert_to_internal(char* src[], int count, long* dst[]) | |||
176 | int scale = WORD_SHIFT; | 179 | int scale = WORD_SHIFT; |
177 | int i; | 180 | int i; |
178 | 181 | ||
179 | if (dsp.stereo_mode == STEREO_INTERLEAVED) | 182 | if (dsp->stereo_mode == STEREO_INTERLEAVED) |
180 | { | 183 | { |
181 | for (i = 0; i < count; i++) | 184 | for (i = 0; i < count; i++) |
182 | { | 185 | { |
@@ -184,7 +187,7 @@ static int convert_to_internal(char* src[], int count, long* dst[]) | |||
184 | *d1++ = *s0++ << scale; | 187 | *d1++ = *s0++ << scale; |
185 | } | 188 | } |
186 | } | 189 | } |
187 | else if (dsp.stereo_mode == STEREO_NONINTERLEAVED) | 190 | else if (dsp->stereo_mode == STEREO_NONINTERLEAVED) |
188 | { | 191 | { |
189 | short* s1 = (short*) src[1]; | 192 | short* s1 = (short*) src[1]; |
190 | 193 | ||
@@ -202,7 +205,7 @@ static int convert_to_internal(char* src[], int count, long* dst[]) | |||
202 | } | 205 | } |
203 | } | 206 | } |
204 | } | 207 | } |
205 | else if (dsp.stereo_mode == STEREO_INTERLEAVED) | 208 | else if (dsp->stereo_mode == STEREO_INTERLEAVED) |
206 | { | 209 | { |
207 | long* s0 = (long*) src[0]; | 210 | long* s0 = (long*) src[0]; |
208 | long* d0 = dst[0]; | 211 | long* d0 = dst[0]; |
@@ -216,18 +219,18 @@ static int convert_to_internal(char* src[], int count, long* dst[]) | |||
216 | } | 219 | } |
217 | } | 220 | } |
218 | 221 | ||
219 | if (dsp.stereo_mode == STEREO_NONINTERLEAVED) | 222 | if (dsp->stereo_mode == STEREO_NONINTERLEAVED) |
220 | { | 223 | { |
221 | src[0] += count * dsp.sample_bytes; | 224 | src[0] += count * dsp->sample_bytes; |
222 | src[1] += count * dsp.sample_bytes; | 225 | src[1] += count * dsp->sample_bytes; |
223 | } | 226 | } |
224 | else if (dsp.stereo_mode == STEREO_INTERLEAVED) | 227 | else if (dsp->stereo_mode == STEREO_INTERLEAVED) |
225 | { | 228 | { |
226 | src[0] += count * dsp.sample_bytes * 2; | 229 | src[0] += count * dsp->sample_bytes * 2; |
227 | } | 230 | } |
228 | else | 231 | else |
229 | { | 232 | { |
230 | src[0] += count * dsp.sample_bytes; | 233 | src[0] += count * dsp->sample_bytes; |
231 | } | 234 | } |
232 | 235 | ||
233 | return count; | 236 | return count; |
@@ -310,29 +313,33 @@ static inline int resample(long* src[], int count) | |||
310 | { | 313 | { |
311 | long new_count; | 314 | long new_count; |
312 | 315 | ||
313 | if (dsp.frequency != NATIVE_FREQUENCY) | 316 | if (dsp->frequency != NATIVE_FREQUENCY) |
314 | { | 317 | { |
315 | long* d0 = &resample_buf[0]; | 318 | long* d0 = &resample_buf[0]; |
316 | /* Only process the second channel if needed. */ | 319 | /* Only process the second channel if needed. */ |
317 | long* d1 = (src[0] == src[1]) ? d0 | 320 | long* d1 = (src[0] == src[1]) ? d0 |
318 | : &resample_buf[RESAMPLE_BUF_SIZE / 2]; | 321 | : &resample_buf[RESAMPLE_BUF_SIZE / 2]; |
319 | 322 | ||
320 | if (dsp.frequency < NATIVE_FREQUENCY) | 323 | if (dsp->frequency < NATIVE_FREQUENCY) |
321 | { | 324 | { |
322 | new_count = upsample(d0, src[0], count, &resample_data[0]); | 325 | new_count = upsample(d0, src[0], count, |
326 | &resample_data[current_codec][0]); | ||
323 | 327 | ||
324 | if (d0 != d1) | 328 | if (d0 != d1) |
325 | { | 329 | { |
326 | upsample(d1, src[1], count, &resample_data[1]); | 330 | upsample(d1, src[1], count, |
331 | &resample_data[current_codec][1]); | ||
327 | } | 332 | } |
328 | } | 333 | } |
329 | else | 334 | else |
330 | { | 335 | { |
331 | new_count = downsample(d0, src[0], count, &resample_data[0]); | 336 | new_count = downsample(d0, src[0], count, |
337 | &resample_data[current_codec][0]); | ||
332 | 338 | ||
333 | if (d0 != d1) | 339 | if (d0 != d1) |
334 | { | 340 | { |
335 | downsample(d1, src[1], count, &resample_data[1]); | 341 | downsample(d1, src[1], count, |
342 | &resample_data[current_codec][1]); | ||
336 | } | 343 | } |
337 | } | 344 | } |
338 | 345 | ||
@@ -389,8 +396,8 @@ static long dither_sample(long sample, long bias, long mask, | |||
389 | 396 | ||
390 | /* Clip and quantize */ | 397 | /* Clip and quantize */ |
391 | 398 | ||
392 | min = dsp.clip_min; | 399 | min = dsp->clip_min; |
393 | max = dsp.clip_max; | 400 | max = dsp->clip_max; |
394 | sample = clip_sample(sample, min, max); | 401 | sample = clip_sample(sample, min, max); |
395 | output = clip_sample(output, min, max) & ~mask; | 402 | output = clip_sample(output, min, max) & ~mask; |
396 | 403 | ||
@@ -407,13 +414,13 @@ static long dither_sample(long sample, long bias, long mask, | |||
407 | */ | 414 | */ |
408 | static void apply_gain(long* src[], int count) | 415 | static void apply_gain(long* src[], int count) |
409 | { | 416 | { |
410 | if (dsp.replaygain) | 417 | if (dsp->replaygain) |
411 | { | 418 | { |
412 | long* s0 = src[0]; | 419 | long* s0 = src[0]; |
413 | long* s1 = src[1]; | 420 | long* s1 = src[1]; |
414 | long* d0 = &sample_buf[0]; | 421 | long* d0 = &sample_buf[0]; |
415 | long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2]; | 422 | long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2]; |
416 | long gain = dsp.replaygain; | 423 | long gain = dsp->replaygain; |
417 | long s; | 424 | long s; |
418 | long i; | 425 | long i; |
419 | 426 | ||
@@ -442,11 +449,11 @@ static void write_samples(short* dst, long* src[], int count) | |||
442 | { | 449 | { |
443 | long* s0 = src[0]; | 450 | long* s0 = src[0]; |
444 | long* s1 = src[1]; | 451 | long* s1 = src[1]; |
445 | int scale = dsp.frac_bits + 1 - NATIVE_DEPTH; | 452 | int scale = dsp->frac_bits + 1 - NATIVE_DEPTH; |
446 | 453 | ||
447 | if (dsp.dither_enabled) | 454 | if (dsp->dither_enabled) |
448 | { | 455 | { |
449 | long bias = (1L << (dsp.frac_bits - NATIVE_DEPTH)); | 456 | long bias = (1L << (dsp->frac_bits - NATIVE_DEPTH)); |
450 | long mask = (1L << scale) - 1; | 457 | long mask = (1L << scale) - 1; |
451 | 458 | ||
452 | while (count-- > 0) | 459 | while (count-- > 0) |
@@ -459,8 +466,8 @@ static void write_samples(short* dst, long* src[], int count) | |||
459 | } | 466 | } |
460 | else | 467 | else |
461 | { | 468 | { |
462 | long min = dsp.clip_min; | 469 | long min = dsp->clip_min; |
463 | long max = dsp.clip_max; | 470 | long max = dsp->clip_max; |
464 | 471 | ||
465 | while (count-- > 0) | 472 | while (count-- > 0) |
466 | { | 473 | { |
@@ -482,10 +489,13 @@ long dsp_process(char* dst, char* src[], long size) | |||
482 | { | 489 | { |
483 | long* tmp[2]; | 490 | long* tmp[2]; |
484 | long written = 0; | 491 | long written = 0; |
485 | long factor = (dsp.stereo_mode != STEREO_MONO) ? 2 : 1; | 492 | long factor; |
486 | int samples; | 493 | int samples; |
487 | 494 | ||
488 | size /= dsp.sample_bytes * factor; | 495 | dsp = &dsp_conf[current_codec]; |
496 | |||
497 | factor = (dsp->stereo_mode != STEREO_MONO) ? 2 : 1; | ||
498 | size /= dsp->sample_bytes * factor; | ||
489 | INIT(); | 499 | INIT(); |
490 | dsp_set_replaygain(false); | 500 | dsp_set_replaygain(false); |
491 | 501 | ||
@@ -513,21 +523,23 @@ long dsp_process(char* dst, char* src[], long size) | |||
513 | /* dsp_input_size MUST be called afterwards */ | 523 | /* dsp_input_size MUST be called afterwards */ |
514 | long dsp_output_size(long size) | 524 | long dsp_output_size(long size) |
515 | { | 525 | { |
516 | if (dsp.sample_depth > NATIVE_DEPTH) | 526 | dsp = &dsp_conf[current_codec]; |
527 | |||
528 | if (dsp->sample_depth > NATIVE_DEPTH) | ||
517 | { | 529 | { |
518 | size /= 2; | 530 | size /= 2; |
519 | } | 531 | } |
520 | 532 | ||
521 | if (dsp.frequency != NATIVE_FREQUENCY) | 533 | if (dsp->frequency != NATIVE_FREQUENCY) |
522 | { | 534 | { |
523 | size = (long) ((((unsigned long) size * NATIVE_FREQUENCY) | 535 | size = (long) ((((unsigned long) size * NATIVE_FREQUENCY) |
524 | + (dsp.frequency - 1)) / dsp.frequency); | 536 | + (dsp->frequency - 1)) / dsp->frequency); |
525 | } | 537 | } |
526 | 538 | ||
527 | /* round to the next multiple of 2 (these are shorts) */ | 539 | /* round to the next multiple of 2 (these are shorts) */ |
528 | size = (size + 1) & ~1; | 540 | size = (size + 1) & ~1; |
529 | 541 | ||
530 | if (dsp.stereo_mode == STEREO_MONO) | 542 | if (dsp->stereo_mode == STEREO_MONO) |
531 | { | 543 | { |
532 | size *= 2; | 544 | size *= 2; |
533 | } | 545 | } |
@@ -547,25 +559,28 @@ long dsp_output_size(long size) | |||
547 | */ | 559 | */ |
548 | long dsp_input_size(long size) | 560 | long dsp_input_size(long size) |
549 | { | 561 | { |
562 | dsp = &dsp_conf[current_codec]; | ||
563 | |||
550 | /* convert to number of output stereo samples. */ | 564 | /* convert to number of output stereo samples. */ |
551 | size /= 2; | 565 | size /= 2; |
552 | 566 | ||
553 | /* Mono means we need half input samples to fill the output buffer */ | 567 | /* Mono means we need half input samples to fill the output buffer */ |
554 | if (dsp.stereo_mode == STEREO_MONO) | 568 | if (dsp->stereo_mode == STEREO_MONO) |
555 | size /= 2; | 569 | size /= 2; |
556 | 570 | ||
557 | /* size is now the number of resampled input samples. Convert to | 571 | /* size is now the number of resampled input samples. Convert to |
558 | original input samples. */ | 572 | original input samples. */ |
559 | if (dsp.frequency != NATIVE_FREQUENCY) | 573 | if (dsp->frequency != NATIVE_FREQUENCY) |
560 | { | 574 | { |
561 | /* Use the real resampling delta = | 575 | /* Use the real resampling delta = |
562 | * (unsigned long) dsp.frequency * 65536 / NATIVE_FREQUENCY, and | 576 | * (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY, and |
563 | * round towards zero to avoid buffer overflows. */ | 577 | * round towards zero to avoid buffer overflows. */ |
564 | size = ((unsigned long)size * resample_data[0].delta) >> 16; | 578 | size = ((unsigned long)size * |
579 | resample_data[current_codec][0].delta) >> 16; | ||
565 | } | 580 | } |
566 | 581 | ||
567 | /* Convert back to bytes. */ | 582 | /* Convert back to bytes. */ |
568 | if (dsp.sample_depth > NATIVE_DEPTH) | 583 | if (dsp->sample_depth > NATIVE_DEPTH) |
569 | size *= 4; | 584 | size *= 4; |
570 | else | 585 | else |
571 | size *= 2; | 586 | size *= 2; |
@@ -575,90 +590,96 @@ long dsp_input_size(long size) | |||
575 | 590 | ||
576 | int dsp_stereo_mode(void) | 591 | int dsp_stereo_mode(void) |
577 | { | 592 | { |
578 | return dsp.stereo_mode; | 593 | dsp = &dsp_conf[current_codec]; |
594 | |||
595 | return dsp->stereo_mode; | ||
579 | } | 596 | } |
580 | 597 | ||
581 | bool dsp_configure(int setting, void *value) | 598 | bool dsp_configure(int setting, void *value) |
582 | { | 599 | { |
600 | dsp = &dsp_conf[current_codec]; | ||
601 | |||
583 | switch (setting) | 602 | switch (setting) |
584 | { | 603 | { |
585 | case DSP_SET_FREQUENCY: | 604 | case DSP_SET_FREQUENCY: |
586 | memset(resample_data, 0, sizeof(resample_data)); | 605 | memset(&resample_data[current_codec][0], 0, |
606 | sizeof(struct resample_data) * 2); | ||
587 | /* Fall through!!! */ | 607 | /* Fall through!!! */ |
588 | case DSP_SWITCH_FREQUENCY: | 608 | case DSP_SWITCH_FREQUENCY: |
589 | dsp.frequency = ((int) value == 0) ? NATIVE_FREQUENCY : (int) value; | 609 | dsp->frequency = ((int) value == 0) ? NATIVE_FREQUENCY : (int) value; |
590 | resample_data[0].delta = resample_data[1].delta = | 610 | resample_data[current_codec][0].delta = |
591 | (unsigned long) dsp.frequency * 65536 / NATIVE_FREQUENCY; | 611 | resample_data[current_codec][1].delta = |
612 | (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY; | ||
592 | break; | 613 | break; |
593 | 614 | ||
594 | case DSP_SET_CLIP_MIN: | 615 | case DSP_SET_CLIP_MIN: |
595 | dsp.clip_min = (long) value; | 616 | dsp->clip_min = (long) value; |
596 | break; | 617 | break; |
597 | 618 | ||
598 | case DSP_SET_CLIP_MAX: | 619 | case DSP_SET_CLIP_MAX: |
599 | dsp.clip_max = (long) value; | 620 | dsp->clip_max = (long) value; |
600 | break; | 621 | break; |
601 | 622 | ||
602 | case DSP_SET_SAMPLE_DEPTH: | 623 | case DSP_SET_SAMPLE_DEPTH: |
603 | dsp.sample_depth = (long) value; | 624 | dsp->sample_depth = (long) value; |
604 | 625 | ||
605 | if (dsp.sample_depth <= NATIVE_DEPTH) | 626 | if (dsp->sample_depth <= NATIVE_DEPTH) |
606 | { | 627 | { |
607 | dsp.frac_bits = WORD_FRACBITS; | 628 | dsp->frac_bits = WORD_FRACBITS; |
608 | dsp.sample_bytes = sizeof(short); | 629 | dsp->sample_bytes = sizeof(short); |
609 | dsp.clip_max = ((1 << WORD_FRACBITS) - 1); | 630 | dsp->clip_max = ((1 << WORD_FRACBITS) - 1); |
610 | dsp.clip_min = -((1 << WORD_FRACBITS)); | 631 | dsp->clip_min = -((1 << WORD_FRACBITS)); |
611 | } | 632 | } |
612 | else | 633 | else |
613 | { | 634 | { |
614 | dsp.frac_bits = (long) value; | 635 | dsp->frac_bits = (long) value; |
615 | dsp.sample_bytes = sizeof(long); | 636 | dsp->sample_bytes = sizeof(long); |
616 | } | 637 | } |
617 | 638 | ||
618 | break; | 639 | break; |
619 | 640 | ||
620 | case DSP_SET_STEREO_MODE: | 641 | case DSP_SET_STEREO_MODE: |
621 | dsp.stereo_mode = (long) value; | 642 | dsp->stereo_mode = (long) value; |
622 | break; | 643 | break; |
623 | 644 | ||
624 | case DSP_RESET: | 645 | case DSP_RESET: |
625 | dsp.dither_enabled = false; | 646 | dsp->dither_enabled = false; |
626 | dsp.stereo_mode = STEREO_NONINTERLEAVED; | 647 | dsp->stereo_mode = STEREO_NONINTERLEAVED; |
627 | dsp.clip_max = ((1 << WORD_FRACBITS) - 1); | 648 | dsp->clip_max = ((1 << WORD_FRACBITS) - 1); |
628 | dsp.clip_min = -((1 << WORD_FRACBITS)); | 649 | dsp->clip_min = -((1 << WORD_FRACBITS)); |
629 | dsp.track_gain = 0; | 650 | dsp->track_gain = 0; |
630 | dsp.album_gain = 0; | 651 | dsp->album_gain = 0; |
631 | dsp.track_peak = 0; | 652 | dsp->track_peak = 0; |
632 | dsp.album_peak = 0; | 653 | dsp->album_peak = 0; |
633 | dsp.frequency = NATIVE_FREQUENCY; | 654 | dsp->frequency = NATIVE_FREQUENCY; |
634 | dsp.sample_depth = NATIVE_DEPTH; | 655 | dsp->sample_depth = NATIVE_DEPTH; |
635 | dsp.frac_bits = WORD_FRACBITS; | 656 | dsp->frac_bits = WORD_FRACBITS; |
636 | dsp.new_gain = true; | 657 | dsp->new_gain = true; |
637 | break; | 658 | break; |
638 | 659 | ||
639 | case DSP_DITHER: | 660 | case DSP_DITHER: |
640 | memset(dither_data, 0, sizeof(dither_data)); | 661 | memset(dither_data, 0, sizeof(dither_data)); |
641 | dsp.dither_enabled = (bool) value; | 662 | dsp->dither_enabled = (bool) value; |
642 | break; | 663 | break; |
643 | 664 | ||
644 | case DSP_SET_TRACK_GAIN: | 665 | case DSP_SET_TRACK_GAIN: |
645 | dsp.track_gain = (long) value; | 666 | dsp->track_gain = (long) value; |
646 | dsp.new_gain = true; | 667 | dsp->new_gain = true; |
647 | break; | 668 | break; |
648 | 669 | ||
649 | case DSP_SET_ALBUM_GAIN: | 670 | case DSP_SET_ALBUM_GAIN: |
650 | dsp.album_gain = (long) value; | 671 | dsp->album_gain = (long) value; |
651 | dsp.new_gain = true; | 672 | dsp->new_gain = true; |
652 | break; | 673 | break; |
653 | 674 | ||
654 | case DSP_SET_TRACK_PEAK: | 675 | case DSP_SET_TRACK_PEAK: |
655 | dsp.track_peak = (long) value; | 676 | dsp->track_peak = (long) value; |
656 | dsp.new_gain = true; | 677 | dsp->new_gain = true; |
657 | break; | 678 | break; |
658 | 679 | ||
659 | case DSP_SET_ALBUM_PEAK: | 680 | case DSP_SET_ALBUM_PEAK: |
660 | dsp.album_peak = (long) value; | 681 | dsp->album_peak = (long) value; |
661 | dsp.new_gain = true; | 682 | dsp->new_gain = true; |
662 | break; | 683 | break; |
663 | 684 | ||
664 | default: | 685 | default: |
@@ -670,11 +691,13 @@ bool dsp_configure(int setting, void *value) | |||
670 | 691 | ||
671 | void dsp_set_replaygain(bool always) | 692 | void dsp_set_replaygain(bool always) |
672 | { | 693 | { |
673 | if (always || dsp.new_gain) | 694 | dsp = &dsp_conf[current_codec]; |
695 | |||
696 | if (always || dsp->new_gain) | ||
674 | { | 697 | { |
675 | long gain = 0; | 698 | long gain = 0; |
676 | 699 | ||
677 | dsp.new_gain = false; | 700 | dsp->new_gain = false; |
678 | 701 | ||
679 | if (global_settings.replaygain || global_settings.replaygain_noclip) | 702 | if (global_settings.replaygain || global_settings.replaygain_noclip) |
680 | { | 703 | { |
@@ -682,8 +705,8 @@ void dsp_set_replaygain(bool always) | |||
682 | 705 | ||
683 | if (global_settings.replaygain) | 706 | if (global_settings.replaygain) |
684 | { | 707 | { |
685 | gain = (global_settings.replaygain_track || !dsp.album_gain) | 708 | gain = (global_settings.replaygain_track || !dsp->album_gain) |
686 | ? dsp.track_gain : dsp.album_gain; | 709 | ? dsp->track_gain : dsp->album_gain; |
687 | 710 | ||
688 | if (global_settings.replaygain_preamp) | 711 | if (global_settings.replaygain_preamp) |
689 | { | 712 | { |
@@ -694,8 +717,8 @@ void dsp_set_replaygain(bool always) | |||
694 | } | 717 | } |
695 | } | 718 | } |
696 | 719 | ||
697 | peak = (global_settings.replaygain_track || !dsp.album_peak) | 720 | peak = (global_settings.replaygain_track || !dsp->album_peak) |
698 | ? dsp.track_peak : dsp.album_peak; | 721 | ? dsp->track_peak : dsp->album_peak; |
699 | 722 | ||
700 | if (gain == 0) | 723 | if (gain == 0) |
701 | { | 724 | { |
@@ -718,6 +741,6 @@ void dsp_set_replaygain(bool always) | |||
718 | } | 741 | } |
719 | 742 | ||
720 | /* Store in S8.23 format to simplify calculations. */ | 743 | /* Store in S8.23 format to simplify calculations. */ |
721 | dsp.replaygain = gain >> 1; | 744 | dsp->replaygain = gain >> 1; |
722 | } | 745 | } |
723 | } | 746 | } |
diff --git a/apps/main.c b/apps/main.c index 90be703c6b..55897de5f5 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -129,6 +129,9 @@ void init(void) | |||
129 | global_settings.mdb_enable, | 129 | global_settings.mdb_enable, |
130 | global_settings.superbass); | 130 | global_settings.superbass); |
131 | button_clear_queue(); /* Empty the keyboard buffer */ | 131 | button_clear_queue(); /* Empty the keyboard buffer */ |
132 | #if CONFIG_HWCODEC == MASNONE | ||
133 | talk_init(); | ||
134 | #endif | ||
132 | } | 135 | } |
133 | 136 | ||
134 | #else | 137 | #else |
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 691f8d5a19..5f78901a56 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include "buffer.h" | 34 | #include "buffer.h" |
35 | #include "settings.h" | 35 | #include "settings.h" |
36 | #include "audio.h" | 36 | #include "audio.h" |
37 | #include "dsp.h" | ||
37 | 38 | ||
38 | #define CHUNK_SIZE PCMBUF_GUARD | 39 | #define CHUNK_SIZE PCMBUF_GUARD |
39 | /* Must be a power of 2 */ | 40 | /* Must be a power of 2 */ |
@@ -86,9 +87,11 @@ struct pcmbufdesc | |||
86 | volatile int pcmbuf_read_index; | 87 | volatile int pcmbuf_read_index; |
87 | volatile int pcmbuf_write_index; | 88 | volatile int pcmbuf_write_index; |
88 | int pcmbuf_unplayed_bytes; | 89 | int pcmbuf_unplayed_bytes; |
90 | int pcmbuf_mix_used_bytes; | ||
89 | int pcmbuf_watermark; | 91 | int pcmbuf_watermark; |
90 | void (*pcmbuf_watermark_event)(int bytes_left); | 92 | void (*pcmbuf_watermark_event)(int bytes_left); |
91 | static int last_chunksize; | 93 | static int last_chunksize; |
94 | static long mixpos = 0; | ||
92 | 95 | ||
93 | static void pcmbuf_boost(bool state) | 96 | static void pcmbuf_boost(bool state) |
94 | { | 97 | { |
@@ -173,6 +176,7 @@ bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void)) | |||
173 | pcmbuffers[pcmbuf_write_index].callback = callback; | 176 | pcmbuffers[pcmbuf_write_index].callback = callback; |
174 | pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK; | 177 | pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK; |
175 | pcmbuf_unplayed_bytes += size; | 178 | pcmbuf_unplayed_bytes += size; |
179 | pcmbuf_mix_used_bytes = MAX(0, pcmbuf_mix_used_bytes - size); | ||
176 | return true; | 180 | return true; |
177 | } | 181 | } |
178 | else | 182 | else |
@@ -254,6 +258,7 @@ void pcmbuf_play_stop(void) | |||
254 | pcm_play_stop(); | 258 | pcm_play_stop(); |
255 | last_chunksize = 0; | 259 | last_chunksize = 0; |
256 | pcmbuf_unplayed_bytes = 0; | 260 | pcmbuf_unplayed_bytes = 0; |
261 | pcmbuf_mix_used_bytes = 0; | ||
257 | pcmbuf_read_index = 0; | 262 | pcmbuf_read_index = 0; |
258 | pcmbuf_write_index = 0; | 263 | pcmbuf_write_index = 0; |
259 | audiobuffer_pos = 0; | 264 | audiobuffer_pos = 0; |
@@ -297,6 +302,13 @@ void pcmbuf_flush_audio(void) | |||
297 | crossfade_init = true; | 302 | crossfade_init = true; |
298 | } | 303 | } |
299 | 304 | ||
305 | /* Force playback. */ | ||
306 | void pcmbuf_play_start(void) | ||
307 | { | ||
308 | if (!pcm_is_playing() && pcmbuf_unplayed_bytes) | ||
309 | pcm_play_data(pcmbuf_callback); | ||
310 | } | ||
311 | |||
300 | void pcmbuf_flush_fillpos(void) | 312 | void pcmbuf_flush_fillpos(void) |
301 | { | 313 | { |
302 | int copy_n; | 314 | int copy_n; |
@@ -562,6 +574,98 @@ bool pcmbuf_insert_buffer(char *buf, long length) | |||
562 | return true; | 574 | return true; |
563 | } | 575 | } |
564 | 576 | ||
577 | /* Generates a constant square wave sound with a given frequency | ||
578 | in Hertz for a duration in milliseconds. */ | ||
579 | void pcmbuf_beep(int frequency, int duration, int amplitude) | ||
580 | { | ||
581 | int state = 0, count = 0; | ||
582 | int interval = NATIVE_FREQUENCY / frequency; | ||
583 | int pos; | ||
584 | short *buf = (short *)audiobuffer; | ||
585 | int bufsize = pcmbuf_size / 2; | ||
586 | |||
587 | /* FIXME: Should start playback. */ | ||
588 | //if (pcmbuf_unplayed_bytes * 1000 < 4 * NATIVE_FREQUENCY * duration) | ||
589 | // return ; | ||
590 | |||
591 | pos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2; | ||
592 | if (pos < 0) | ||
593 | pos += bufsize; | ||
594 | |||
595 | duration = NATIVE_FREQUENCY / 1000 * duration; | ||
596 | while (duration-- > 0) | ||
597 | { | ||
598 | if (state) { | ||
599 | buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767); | ||
600 | if (++pos >= bufsize) | ||
601 | pos = 0; | ||
602 | buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767); | ||
603 | } else { | ||
604 | buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767); | ||
605 | if (++pos >= bufsize) | ||
606 | pos = 0; | ||
607 | buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767); | ||
608 | } | ||
609 | |||
610 | if (++count >= interval) | ||
611 | { | ||
612 | count = 0; | ||
613 | if (state) | ||
614 | state = 0; | ||
615 | else | ||
616 | state = 1; | ||
617 | } | ||
618 | pos++; | ||
619 | if (pos >= bufsize) | ||
620 | pos = 0; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | /* Returns pcm buffer usage in percents (0 to 100). */ | ||
625 | int pcmbuf_usage(void) | ||
626 | { | ||
627 | return pcmbuf_unplayed_bytes * 100 / pcmbuf_size; | ||
628 | } | ||
629 | |||
630 | int pcmbuf_mix_usage(void) | ||
631 | { | ||
632 | return pcmbuf_mix_used_bytes * 100 / pcmbuf_unplayed_bytes; | ||
633 | } | ||
634 | |||
635 | void pcmbuf_reset_mixpos(void) | ||
636 | { | ||
637 | int bufsize = pcmbuf_size / 2; | ||
638 | |||
639 | pcmbuf_mix_used_bytes = 0; | ||
640 | mixpos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2; | ||
641 | if (mixpos < 0) | ||
642 | mixpos += bufsize; | ||
643 | if (mixpos >= bufsize) | ||
644 | mixpos -= bufsize; | ||
645 | } | ||
646 | |||
647 | void pcmbuf_mix(char *buf, long length) | ||
648 | { | ||
649 | short *ibuf = (short *)buf; | ||
650 | short *obuf = (short *)audiobuffer; | ||
651 | int bufsize = pcmbuf_size / 2; | ||
652 | |||
653 | if (pcmbuf_mix_used_bytes == 0) | ||
654 | pcmbuf_reset_mixpos(); | ||
655 | |||
656 | pcmbuf_mix_used_bytes += length; | ||
657 | length /= 2; | ||
658 | |||
659 | while (length-- > 0) { | ||
660 | obuf[mixpos] = MIN(MAX(obuf[mixpos] + *ibuf*4, -32768), 32767); | ||
661 | |||
662 | ibuf++; | ||
663 | mixpos++; | ||
664 | if (mixpos >= bufsize) | ||
665 | mixpos = 0; | ||
666 | } | ||
667 | } | ||
668 | |||
565 | void pcmbuf_crossfade_enable(bool on_off) | 669 | void pcmbuf_crossfade_enable(bool on_off) |
566 | { | 670 | { |
567 | crossfade_enabled = on_off; | 671 | crossfade_enabled = on_off; |
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index 6381dbc27e..f2533defe6 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h | |||
@@ -36,6 +36,7 @@ void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left)); | |||
36 | void pcmbuf_set_boost_mode(bool state); | 36 | void pcmbuf_set_boost_mode(bool state); |
37 | bool pcmbuf_is_lowdata(void); | 37 | bool pcmbuf_is_lowdata(void); |
38 | void pcmbuf_flush_audio(void); | 38 | void pcmbuf_flush_audio(void); |
39 | void pcmbuf_play_start(void); | ||
39 | bool pcmbuf_crossfade_init(int type); | 40 | bool pcmbuf_crossfade_init(int type); |
40 | void pcmbuf_add_event(void (*event_handler)(void)); | 41 | void pcmbuf_add_event(void (*event_handler)(void)); |
41 | unsigned int pcmbuf_get_latency(void); | 42 | unsigned int pcmbuf_get_latency(void); |
@@ -45,4 +46,10 @@ void* pcmbuf_request_buffer(long length, long *realsize); | |||
45 | bool pcmbuf_is_crossfade_enabled(void); | 46 | bool pcmbuf_is_crossfade_enabled(void); |
46 | void pcmbuf_crossfade_enable(bool on_off); | 47 | void pcmbuf_crossfade_enable(bool on_off); |
47 | 48 | ||
49 | int pcmbuf_usage(void); | ||
50 | int pcmbuf_mix_usage(void); | ||
51 | void pcmbuf_beep(int frequency, int duration, int amplitude); | ||
52 | void pcmbuf_reset_mixpos(void); | ||
53 | void pcmbuf_mix(char *buf, long length); | ||
54 | |||
48 | #endif | 55 | #endif |
diff --git a/apps/playback.c b/apps/playback.c index e12b01ee55..8829757949 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -60,18 +60,20 @@ | |||
60 | #include "misc.h" | 60 | #include "misc.h" |
61 | #include "sound.h" | 61 | #include "sound.h" |
62 | #include "metadata.h" | 62 | #include "metadata.h" |
63 | #include "talk.h" | ||
63 | 64 | ||
64 | static volatile bool codec_loaded; | 65 | static volatile bool audio_codec_loaded; |
66 | static volatile bool voice_codec_loaded; | ||
65 | static volatile bool playing; | 67 | static volatile bool playing; |
66 | static volatile bool paused; | 68 | static volatile bool paused; |
67 | 69 | ||
68 | #define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec"; | 70 | #define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec" |
69 | #define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec"; | 71 | #define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec" |
70 | #define CODEC_FLAC "/.rockbox/codecs/flac.codec"; | 72 | #define CODEC_FLAC "/.rockbox/codecs/flac.codec" |
71 | #define CODEC_WAV "/.rockbox/codecs/wav.codec"; | 73 | #define CODEC_WAV "/.rockbox/codecs/wav.codec" |
72 | #define CODEC_A52 "/.rockbox/codecs/a52.codec"; | 74 | #define CODEC_A52 "/.rockbox/codecs/a52.codec" |
73 | #define CODEC_MPC "/.rockbox/codecs/mpc.codec"; | 75 | #define CODEC_MPC "/.rockbox/codecs/mpc.codec" |
74 | #define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec"; | 76 | #define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec" |
75 | 77 | ||
76 | #define AUDIO_FILL_CYCLE (1024*256) | 78 | #define AUDIO_FILL_CYCLE (1024*256) |
77 | #define AUDIO_DEFAULT_WATERMARK (1024*512) | 79 | #define AUDIO_DEFAULT_WATERMARK (1024*512) |
@@ -96,6 +98,10 @@ static volatile bool paused; | |||
96 | #define MALLOC_BUFSIZE (512*1024) | 98 | #define MALLOC_BUFSIZE (512*1024) |
97 | #define GUARD_BUFSIZE (8*1024) | 99 | #define GUARD_BUFSIZE (8*1024) |
98 | 100 | ||
101 | /* As defined in plugin.lds */ | ||
102 | #define CODEC_IRAM_ORIGIN 0x10010000 | ||
103 | #define CODEC_IRAM_SIZE 0x8000 | ||
104 | |||
99 | extern bool audio_is_initialized; | 105 | extern bool audio_is_initialized; |
100 | 106 | ||
101 | /* Buffer control thread. */ | 107 | /* Buffer control thread. */ |
@@ -108,19 +114,39 @@ static struct event_queue codec_queue; | |||
108 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)] IDATA_ATTR; | 114 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)] IDATA_ATTR; |
109 | static const char codec_thread_name[] = "codec"; | 115 | static const char codec_thread_name[] = "codec"; |
110 | 116 | ||
117 | /* Voice codec thread. */ | ||
118 | static struct event_queue voice_codec_queue; | ||
119 | /* Not enough IRAM for this. */ | ||
120 | static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)]; | ||
121 | static const char voice_codec_thread_name[] = "voice codec"; | ||
122 | |||
111 | static struct mutex mutex_bufferfill; | 123 | static struct mutex mutex_bufferfill; |
124 | static struct mutex mutex_codecthread; | ||
125 | |||
126 | static struct mp3entry id3_voice; | ||
127 | |||
128 | #define CODEC_IDX_AUDIO 0 | ||
129 | #define CODEC_IDX_VOICE 1 | ||
130 | |||
131 | static char *voicebuf; | ||
132 | static int voice_remaining; | ||
133 | static bool voice_is_playing; | ||
134 | static void (*voice_getmore)(unsigned char** start, int* size); | ||
112 | 135 | ||
113 | /* Is file buffer currently being refilled? */ | 136 | /* Is file buffer currently being refilled? */ |
114 | static volatile bool filling; | 137 | static volatile bool filling; |
115 | 138 | ||
139 | volatile int current_codec; | ||
140 | extern unsigned char codecbuf[]; | ||
141 | |||
116 | /* Ring buffer where tracks and codecs are loaded. */ | 142 | /* Ring buffer where tracks and codecs are loaded. */ |
117 | static char *codecbuf; | 143 | static char *filebuf; |
118 | 144 | ||
119 | /* Total size of the ring buffer. */ | 145 | /* Total size of the ring buffer. */ |
120 | int codecbuflen; | 146 | int filebuflen; |
121 | 147 | ||
122 | /* Bytes available in the buffer. */ | 148 | /* Bytes available in the buffer. */ |
123 | int codecbufused; | 149 | int filebufused; |
124 | 150 | ||
125 | /* Ring buffer read and write indexes. */ | 151 | /* Ring buffer read and write indexes. */ |
126 | static volatile int buf_ridx; | 152 | static volatile int buf_ridx; |
@@ -153,6 +179,7 @@ static struct track_info *cur_ti; | |||
153 | 179 | ||
154 | /* Codec API including function callbacks. */ | 180 | /* Codec API including function callbacks. */ |
155 | extern struct codec_api ci; | 181 | extern struct codec_api ci; |
182 | extern struct codec_api ci_voice; | ||
156 | 183 | ||
157 | /* When we change a song and buffer is not in filling state, this | 184 | /* When we change a song and buffer is not in filling state, this |
158 | variable keeps information about whether to go a next/previous track. */ | 185 | variable keeps information about whether to go a next/previous track. */ |
@@ -174,6 +201,59 @@ static bool v1first = false; | |||
174 | static void mp3_set_elapsed(struct mp3entry* id3); | 201 | static void mp3_set_elapsed(struct mp3entry* id3); |
175 | int mp3_get_file_pos(void); | 202 | int mp3_get_file_pos(void); |
176 | 203 | ||
204 | static void do_swap(int idx_old, int idx_new) | ||
205 | { | ||
206 | #ifndef SIMULATOR | ||
207 | unsigned char *iram_p = (unsigned char *)(CODEC_IRAM_ORIGIN); | ||
208 | unsigned char *iram_buf[2]; | ||
209 | #endif | ||
210 | unsigned char *dram_buf[2]; | ||
211 | |||
212 | |||
213 | #ifndef SIMULATOR | ||
214 | iram_buf[0] = &filebuf[filebuflen]; | ||
215 | iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE]; | ||
216 | memcpy(iram_buf[idx_old], iram_p, CODEC_IRAM_SIZE); | ||
217 | memcpy(iram_p, iram_buf[idx_new], CODEC_IRAM_SIZE); | ||
218 | #endif | ||
219 | |||
220 | dram_buf[0] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2]; | ||
221 | dram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE]; | ||
222 | memcpy(dram_buf[idx_old], codecbuf, CODEC_SIZE); | ||
223 | memcpy(codecbuf, dram_buf[idx_new], CODEC_SIZE); | ||
224 | } | ||
225 | |||
226 | static void swap_codec(void) | ||
227 | { | ||
228 | int last_codec; | ||
229 | |||
230 | logf("swapping codec:%d", current_codec); | ||
231 | |||
232 | /* We should swap codecs' IRAM contents and code space. */ | ||
233 | do_swap(current_codec, !current_codec); | ||
234 | |||
235 | last_codec = current_codec; | ||
236 | current_codec = !current_codec; | ||
237 | |||
238 | /* Release the semaphore and force a task switch. */ | ||
239 | mutex_unlock(&mutex_codecthread); | ||
240 | sleep(1); | ||
241 | |||
242 | /* Waiting until we are ready to run again. */ | ||
243 | mutex_lock(&mutex_codecthread); | ||
244 | |||
245 | /* Check if codec swap did not happen. */ | ||
246 | if (current_codec != last_codec) | ||
247 | { | ||
248 | logf("no codec switch happened!"); | ||
249 | do_swap(current_codec, !current_codec); | ||
250 | current_codec = !current_codec; | ||
251 | } | ||
252 | |||
253 | invalidate_icache(); | ||
254 | logf("codec resuming:%d", current_codec); | ||
255 | } | ||
256 | |||
177 | bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2, | 257 | bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2, |
178 | long length) | 258 | long length) |
179 | { | 259 | { |
@@ -209,12 +289,41 @@ bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2, | |||
209 | pcmbuf_flush_buffer(0); | 289 | pcmbuf_flush_buffer(0); |
210 | DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n", | 290 | DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n", |
211 | output_size, length, input_size); | 291 | output_size, length, input_size); |
212 | /* should we really continue, or should we break? */ | 292 | /* should we really continue, or should we break? |
293 | * We should probably continue because calling pcmbuf_flush_buffer(0) | ||
294 | * will wrap the buffer if it was fully filled and so next call to | ||
295 | * pcmbuf_request_buffer should give the requested output_size. */ | ||
213 | continue; | 296 | continue; |
214 | } | 297 | } |
215 | 298 | ||
216 | output_size = dsp_process(dest, src, input_size); | 299 | output_size = dsp_process(dest, src, input_size); |
217 | pcmbuf_flush_buffer(output_size); | 300 | |
301 | /* Hotswap between audio and voice codecs as necessary. */ | ||
302 | switch (current_codec) | ||
303 | { | ||
304 | case CODEC_IDX_AUDIO: | ||
305 | pcmbuf_flush_buffer(output_size); | ||
306 | if (voice_is_playing && pcmbuf_usage() > 30 | ||
307 | && pcmbuf_mix_usage() < 20) | ||
308 | { | ||
309 | cpu_boost(true); | ||
310 | swap_codec(); | ||
311 | cpu_boost(false); | ||
312 | } | ||
313 | break ; | ||
314 | |||
315 | case CODEC_IDX_VOICE: | ||
316 | if (audio_codec_loaded) { | ||
317 | pcmbuf_mix(dest, output_size); | ||
318 | if ((pcmbuf_usage() < 10) | ||
319 | || pcmbuf_mix_usage() > 70) | ||
320 | swap_codec(); | ||
321 | } else { | ||
322 | pcmbuf_flush_buffer(output_size); | ||
323 | } | ||
324 | break ; | ||
325 | } | ||
326 | |||
218 | length -= input_size; | 327 | length -= input_size; |
219 | } | 328 | } |
220 | 329 | ||
@@ -241,6 +350,9 @@ bool codec_pcmbuf_insert_callback(char *buf, long length) | |||
241 | void* get_codec_memory_callback(long *size) | 350 | void* get_codec_memory_callback(long *size) |
242 | { | 351 | { |
243 | *size = MALLOC_BUFSIZE; | 352 | *size = MALLOC_BUFSIZE; |
353 | if (voice_codec_loaded) | ||
354 | return &audiobuf[talk_get_bufsize()]; | ||
355 | |||
244 | return &audiobuf[0]; | 356 | return &audiobuf[0]; |
245 | } | 357 | } |
246 | 358 | ||
@@ -248,7 +360,7 @@ void codec_set_elapsed_callback(unsigned int value) | |||
248 | { | 360 | { |
249 | unsigned int latency; | 361 | unsigned int latency; |
250 | 362 | ||
251 | if (ci.stop_codec) | 363 | if (ci.stop_codec || current_codec == CODEC_IDX_VOICE) |
252 | return ; | 364 | return ; |
253 | 365 | ||
254 | latency = pcmbuf_get_latency(); | 366 | latency = pcmbuf_get_latency(); |
@@ -265,7 +377,7 @@ void codec_set_offset_callback(unsigned int value) | |||
265 | { | 377 | { |
266 | unsigned int latency; | 378 | unsigned int latency; |
267 | 379 | ||
268 | if (ci.stop_codec) | 380 | if (ci.stop_codec || current_codec == CODEC_IDX_VOICE) |
269 | return ; | 381 | return ; |
270 | 382 | ||
271 | latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8; | 383 | latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8; |
@@ -283,7 +395,7 @@ long codec_filebuf_callback(void *ptr, long size) | |||
283 | int copy_n; | 395 | int copy_n; |
284 | int part_n; | 396 | int part_n; |
285 | 397 | ||
286 | if (ci.stop_codec || !playing) | 398 | if (ci.stop_codec || !playing || current_codec == CODEC_IDX_VOICE) |
287 | return 0; | 399 | return 0; |
288 | 400 | ||
289 | copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem); | 401 | copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem); |
@@ -297,26 +409,78 @@ long codec_filebuf_callback(void *ptr, long size) | |||
297 | if (copy_n == 0) | 409 | if (copy_n == 0) |
298 | return 0; | 410 | return 0; |
299 | 411 | ||
300 | part_n = MIN(copy_n, codecbuflen - buf_ridx); | 412 | part_n = MIN(copy_n, filebuflen - buf_ridx); |
301 | memcpy(buf, &codecbuf[buf_ridx], part_n); | 413 | memcpy(buf, &filebuf[buf_ridx], part_n); |
302 | if (part_n < copy_n) { | 414 | if (part_n < copy_n) { |
303 | memcpy(&buf[part_n], &codecbuf[0], copy_n - part_n); | 415 | memcpy(&buf[part_n], &filebuf[0], copy_n - part_n); |
304 | } | 416 | } |
305 | 417 | ||
306 | buf_ridx += copy_n; | 418 | buf_ridx += copy_n; |
307 | if (buf_ridx >= codecbuflen) | 419 | if (buf_ridx >= filebuflen) |
308 | buf_ridx -= codecbuflen; | 420 | buf_ridx -= filebuflen; |
309 | ci.curpos += copy_n; | 421 | ci.curpos += copy_n; |
310 | cur_ti->available -= copy_n; | 422 | cur_ti->available -= copy_n; |
311 | codecbufused -= copy_n; | 423 | filebufused -= copy_n; |
312 | 424 | ||
313 | return copy_n; | 425 | return copy_n; |
314 | } | 426 | } |
315 | 427 | ||
428 | void* voice_request_data(long *realsize, long reqsize) | ||
429 | { | ||
430 | while (queue_empty(&voice_codec_queue) && (voice_remaining == 0 | ||
431 | || voicebuf == NULL) && !ci_voice.stop_codec) | ||
432 | { | ||
433 | yield(); | ||
434 | if (audio_codec_loaded && (pcmbuf_usage() < 30 | ||
435 | || !voice_is_playing || voicebuf == NULL)) | ||
436 | { | ||
437 | swap_codec(); | ||
438 | } | ||
439 | if (!voice_is_playing) | ||
440 | sleep(HZ/16); | ||
441 | |||
442 | if (voice_remaining) | ||
443 | { | ||
444 | voice_is_playing = true; | ||
445 | break ; | ||
446 | } | ||
447 | |||
448 | if (voice_getmore != NULL) | ||
449 | { | ||
450 | voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining); | ||
451 | |||
452 | if (!voice_remaining) | ||
453 | { | ||
454 | voice_is_playing = false; | ||
455 | /* Force pcm playback. */ | ||
456 | pcmbuf_play_start(); | ||
457 | } | ||
458 | } | ||
459 | } | ||
460 | |||
461 | if (reqsize < 0) | ||
462 | reqsize = 0; | ||
463 | |||
464 | voice_is_playing = true; | ||
465 | *realsize = voice_remaining; | ||
466 | if (*realsize > reqsize) | ||
467 | *realsize = reqsize; | ||
468 | |||
469 | if (*realsize == 0) | ||
470 | return NULL; | ||
471 | |||
472 | return voicebuf; | ||
473 | } | ||
474 | |||
316 | void* codec_request_buffer_callback(long *realsize, long reqsize) | 475 | void* codec_request_buffer_callback(long *realsize, long reqsize) |
317 | { | 476 | { |
318 | long part_n; | 477 | long part_n; |
319 | 478 | ||
479 | /* Voice codec. */ | ||
480 | if (current_codec == CODEC_IDX_VOICE) { | ||
481 | return voice_request_data(realsize, reqsize); | ||
482 | } | ||
483 | |||
320 | if (ci.stop_codec || !playing) { | 484 | if (ci.stop_codec || !playing) { |
321 | *realsize = 0; | 485 | *realsize = 0; |
322 | return NULL; | 486 | return NULL; |
@@ -335,22 +499,22 @@ void* codec_request_buffer_callback(long *realsize, long reqsize) | |||
335 | } | 499 | } |
336 | } | 500 | } |
337 | 501 | ||
338 | part_n = MIN((int)*realsize, codecbuflen - buf_ridx); | 502 | part_n = MIN((int)*realsize, filebuflen - buf_ridx); |
339 | if (part_n < *realsize) { | 503 | if (part_n < *realsize) { |
340 | part_n += GUARD_BUFSIZE; | 504 | part_n += GUARD_BUFSIZE; |
341 | if (part_n < *realsize) | 505 | if (part_n < *realsize) |
342 | *realsize = part_n; | 506 | *realsize = part_n; |
343 | memcpy(&codecbuf[codecbuflen], &codecbuf[0], *realsize - | 507 | memcpy(&filebuf[filebuflen], &filebuf[0], *realsize - |
344 | (codecbuflen - buf_ridx)); | 508 | (filebuflen - buf_ridx)); |
345 | } | 509 | } |
346 | 510 | ||
347 | return (char *)&codecbuf[buf_ridx]; | 511 | return (char *)&filebuf[buf_ridx]; |
348 | } | 512 | } |
349 | 513 | ||
350 | static bool rebuffer_and_seek(int newpos) | 514 | static bool rebuffer_and_seek(int newpos) |
351 | { | 515 | { |
352 | int fd; | 516 | int fd; |
353 | 517 | ||
354 | logf("Re-buffering song"); | 518 | logf("Re-buffering song"); |
355 | mutex_lock(&mutex_bufferfill); | 519 | mutex_lock(&mutex_bufferfill); |
356 | 520 | ||
@@ -367,7 +531,7 @@ static bool rebuffer_and_seek(int newpos) | |||
367 | 531 | ||
368 | /* Clear codec buffer. */ | 532 | /* Clear codec buffer. */ |
369 | audio_invalidate_tracks(); | 533 | audio_invalidate_tracks(); |
370 | codecbufused = 0; | 534 | filebufused = 0; |
371 | buf_ridx = buf_widx = 0; | 535 | buf_ridx = buf_widx = 0; |
372 | cur_ti->filerem = cur_ti->filesize - newpos; | 536 | cur_ti->filerem = cur_ti->filesize - newpos; |
373 | cur_ti->filepos = newpos; | 537 | cur_ti->filepos = newpos; |
@@ -390,6 +554,15 @@ static bool rebuffer_and_seek(int newpos) | |||
390 | 554 | ||
391 | void codec_advance_buffer_callback(long amount) | 555 | void codec_advance_buffer_callback(long amount) |
392 | { | 556 | { |
557 | if (current_codec == CODEC_IDX_VOICE) { | ||
558 | //logf("voice ad.buf:%d", amount); | ||
559 | amount = MAX(0, MIN(amount, voice_remaining)); | ||
560 | voicebuf += amount; | ||
561 | voice_remaining -= amount; | ||
562 | |||
563 | return ; | ||
564 | } | ||
565 | |||
393 | if (amount > cur_ti->available + cur_ti->filerem) | 566 | if (amount > cur_ti->available + cur_ti->filerem) |
394 | amount = cur_ti->available + cur_ti->filerem; | 567 | amount = cur_ti->available + cur_ti->filerem; |
395 | 568 | ||
@@ -400,10 +573,10 @@ void codec_advance_buffer_callback(long amount) | |||
400 | } | 573 | } |
401 | 574 | ||
402 | buf_ridx += amount; | 575 | buf_ridx += amount; |
403 | if (buf_ridx >= codecbuflen) | 576 | if (buf_ridx >= filebuflen) |
404 | buf_ridx -= codecbuflen; | 577 | buf_ridx -= filebuflen; |
405 | cur_ti->available -= amount; | 578 | cur_ti->available -= amount; |
406 | codecbufused -= amount; | 579 | filebufused -= amount; |
407 | ci.curpos += amount; | 580 | ci.curpos += amount; |
408 | codec_set_offset_callback(ci.curpos); | 581 | codec_set_offset_callback(ci.curpos); |
409 | } | 582 | } |
@@ -411,15 +584,18 @@ void codec_advance_buffer_callback(long amount) | |||
411 | void codec_advance_buffer_loc_callback(void *ptr) | 584 | void codec_advance_buffer_loc_callback(void *ptr) |
412 | { | 585 | { |
413 | long amount; | 586 | long amount; |
414 | 587 | ||
415 | amount = (int)ptr - (int)&codecbuf[buf_ridx]; | 588 | if (current_codec == CODEC_IDX_VOICE) |
589 | amount = (int)ptr - (int)voicebuf; | ||
590 | else | ||
591 | amount = (int)ptr - (int)&filebuf[buf_ridx]; | ||
416 | codec_advance_buffer_callback(amount); | 592 | codec_advance_buffer_callback(amount); |
417 | } | 593 | } |
418 | 594 | ||
419 | off_t codec_mp3_get_filepos_callback(int newtime) | 595 | off_t codec_mp3_get_filepos_callback(int newtime) |
420 | { | 596 | { |
421 | off_t newpos; | 597 | off_t newpos; |
422 | 598 | ||
423 | cur_ti->id3.elapsed = newtime; | 599 | cur_ti->id3.elapsed = newtime; |
424 | newpos = mp3_get_file_pos(); | 600 | newpos = mp3_get_file_pos(); |
425 | 601 | ||
@@ -429,7 +605,10 @@ off_t codec_mp3_get_filepos_callback(int newtime) | |||
429 | bool codec_seek_buffer_callback(off_t newpos) | 605 | bool codec_seek_buffer_callback(off_t newpos) |
430 | { | 606 | { |
431 | int difference; | 607 | int difference; |
432 | 608 | ||
609 | if (current_codec == CODEC_IDX_VOICE) | ||
610 | return false; | ||
611 | |||
433 | if (newpos < 0) | 612 | if (newpos < 0) |
434 | newpos = 0; | 613 | newpos = 0; |
435 | 614 | ||
@@ -457,11 +636,11 @@ bool codec_seek_buffer_callback(off_t newpos) | |||
457 | 636 | ||
458 | /* Seeking inside buffer space. */ | 637 | /* Seeking inside buffer space. */ |
459 | logf("seek: -%d", difference); | 638 | logf("seek: -%d", difference); |
460 | codecbufused += difference; | 639 | filebufused += difference; |
461 | cur_ti->available += difference; | 640 | cur_ti->available += difference; |
462 | buf_ridx -= difference; | 641 | buf_ridx -= difference; |
463 | if (buf_ridx < 0) | 642 | if (buf_ridx < 0) |
464 | buf_ridx = codecbuflen + buf_ridx; | 643 | buf_ridx = filebuflen + buf_ridx; |
465 | ci.curpos -= difference; | 644 | ci.curpos -= difference; |
466 | if (!pcmbuf_is_crossfade_active()) | 645 | if (!pcmbuf_is_crossfade_active()) |
467 | pcmbuf_play_stop(); | 646 | pcmbuf_play_stop(); |
@@ -473,8 +652,11 @@ static void set_filebuf_watermark(int seconds) | |||
473 | { | 652 | { |
474 | long bytes; | 653 | long bytes; |
475 | 654 | ||
655 | if (current_codec == CODEC_IDX_VOICE) | ||
656 | return ; | ||
657 | |||
476 | bytes = MAX((int)cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark); | 658 | bytes = MAX((int)cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark); |
477 | bytes = MIN(bytes, codecbuflen / 2); | 659 | bytes = MIN(bytes, filebuflen / 2); |
478 | conf_watermark = bytes; | 660 | conf_watermark = bytes; |
479 | } | 661 | } |
480 | 662 | ||
@@ -540,7 +722,7 @@ void yield_codecs(void) | |||
540 | sleep(5); | 722 | sleep(5); |
541 | while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata()) | 723 | while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata()) |
542 | && !ci.stop_codec && playing && queue_empty(&audio_queue) | 724 | && !ci.stop_codec && playing && queue_empty(&audio_queue) |
543 | && codecbufused > (128*1024)) | 725 | && filebufused > (128*1024)) |
544 | yield(); | 726 | yield(); |
545 | } | 727 | } |
546 | 728 | ||
@@ -552,18 +734,18 @@ void strip_id3v1_tag(void) | |||
552 | int tagptr; | 734 | int tagptr; |
553 | bool found = true; | 735 | bool found = true; |
554 | 736 | ||
555 | if (codecbufused >= 128) | 737 | if (filebufused >= 128) |
556 | { | 738 | { |
557 | tagptr = buf_widx - 128; | 739 | tagptr = buf_widx - 128; |
558 | if (tagptr < 0) | 740 | if (tagptr < 0) |
559 | tagptr += codecbuflen; | 741 | tagptr += filebuflen; |
560 | 742 | ||
561 | for(i = 0;i < 3;i++) | 743 | for(i = 0;i < 3;i++) |
562 | { | 744 | { |
563 | if(tagptr >= codecbuflen) | 745 | if(tagptr >= filebuflen) |
564 | tagptr -= codecbuflen; | 746 | tagptr -= filebuflen; |
565 | 747 | ||
566 | if(codecbuf[tagptr] != tag[i]) | 748 | if(filebuf[tagptr] != tag[i]) |
567 | { | 749 | { |
568 | found = false; | 750 | found = false; |
569 | break; | 751 | break; |
@@ -578,7 +760,7 @@ void strip_id3v1_tag(void) | |||
578 | logf("Skipping ID3v1 tag\n"); | 760 | logf("Skipping ID3v1 tag\n"); |
579 | buf_widx -= 128; | 761 | buf_widx -= 128; |
580 | tracks[track_widx].available -= 128; | 762 | tracks[track_widx].available -= 128; |
581 | codecbufused -= 128; | 763 | filebufused -= 128; |
582 | } | 764 | } |
583 | } | 765 | } |
584 | } | 766 | } |
@@ -603,9 +785,9 @@ void audio_fill_file_buffer(void) | |||
603 | 785 | ||
604 | if (fill_bytesleft == 0) | 786 | if (fill_bytesleft == 0) |
605 | break ; | 787 | break ; |
606 | rc = MIN(conf_filechunk, codecbuflen - buf_widx); | 788 | rc = MIN(conf_filechunk, filebuflen - buf_widx); |
607 | rc = MIN(rc, fill_bytesleft); | 789 | rc = MIN(rc, fill_bytesleft); |
608 | rc = read(current_fd, &codecbuf[buf_widx], rc); | 790 | rc = read(current_fd, &filebuf[buf_widx], rc); |
609 | if (rc <= 0) { | 791 | if (rc <= 0) { |
610 | tracks[track_widx].filerem = 0; | 792 | tracks[track_widx].filerem = 0; |
611 | strip_id3v1_tag(); | 793 | strip_id3v1_tag(); |
@@ -613,13 +795,13 @@ void audio_fill_file_buffer(void) | |||
613 | } | 795 | } |
614 | 796 | ||
615 | buf_widx += rc; | 797 | buf_widx += rc; |
616 | if (buf_widx >= codecbuflen) | 798 | if (buf_widx >= filebuflen) |
617 | buf_widx -= codecbuflen; | 799 | buf_widx -= filebuflen; |
618 | i += rc; | 800 | i += rc; |
619 | tracks[track_widx].available += rc; | 801 | tracks[track_widx].available += rc; |
620 | tracks[track_widx].filerem -= rc; | 802 | tracks[track_widx].filerem -= rc; |
621 | tracks[track_widx].filepos += rc; | 803 | tracks[track_widx].filepos += rc; |
622 | codecbufused += rc; | 804 | filebufused += rc; |
623 | fill_bytesleft -= rc; | 805 | fill_bytesleft -= rc; |
624 | } | 806 | } |
625 | 807 | ||
@@ -725,15 +907,15 @@ bool loadcodec(const char *trackname, bool start_play) | |||
725 | while (i < size) { | 907 | while (i < size) { |
726 | yield_codecs(); | 908 | yield_codecs(); |
727 | 909 | ||
728 | copy_n = MIN(conf_filechunk, codecbuflen - buf_widx); | 910 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); |
729 | rc = read(fd, &codecbuf[buf_widx], copy_n); | 911 | rc = read(fd, &filebuf[buf_widx], copy_n); |
730 | if (rc < 0) | 912 | if (rc < 0) |
731 | return false; | 913 | return false; |
732 | buf_widx += rc; | 914 | buf_widx += rc; |
733 | codecbufused += rc; | 915 | filebufused += rc; |
734 | fill_bytesleft -= rc; | 916 | fill_bytesleft -= rc; |
735 | if (buf_widx >= codecbuflen) | 917 | if (buf_widx >= filebuflen) |
736 | buf_widx -= codecbuflen; | 918 | buf_widx -= filebuflen; |
737 | i += rc; | 919 | i += rc; |
738 | } | 920 | } |
739 | close(fd); | 921 | close(fd); |
@@ -840,20 +1022,23 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
840 | tracks[track_widx].playlist_offset = peek_offset; | 1022 | tracks[track_widx].playlist_offset = peek_offset; |
841 | last_peek_offset = peek_offset; | 1023 | last_peek_offset = peek_offset; |
842 | 1024 | ||
843 | if (buf_widx >= codecbuflen) | 1025 | if (buf_widx >= filebuflen) |
844 | buf_widx -= codecbuflen; | 1026 | buf_widx -= filebuflen; |
845 | 1027 | ||
846 | /* Set default values */ | 1028 | /* Set default values */ |
847 | if (start_play) { | 1029 | if (start_play) { |
1030 | int last_codec = current_codec; | ||
1031 | current_codec = CODEC_IDX_AUDIO; | ||
848 | conf_bufferlimit = 0; | 1032 | conf_bufferlimit = 0; |
849 | conf_watermark = AUDIO_DEFAULT_WATERMARK; | 1033 | conf_watermark = AUDIO_DEFAULT_WATERMARK; |
850 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; | 1034 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; |
851 | dsp_configure(DSP_RESET, 0); | 1035 | dsp_configure(DSP_RESET, 0); |
852 | ci.configure(CODEC_DSP_ENABLE, false); | 1036 | ci.configure(CODEC_DSP_ENABLE, false); |
1037 | current_codec = last_codec; | ||
853 | } | 1038 | } |
854 | 1039 | ||
855 | /* Load the codec. */ | 1040 | /* Load the codec. */ |
856 | tracks[track_widx].codecbuf = &codecbuf[buf_widx]; | 1041 | tracks[track_widx].codecbuf = &filebuf[buf_widx]; |
857 | if (!loadcodec(trackname, start_play)) { | 1042 | if (!loadcodec(trackname, start_play)) { |
858 | close(fd); | 1043 | close(fd); |
859 | /* Stop buffer filling if codec load failed. */ | 1044 | /* Stop buffer filling if codec load failed. */ |
@@ -870,7 +1055,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
870 | } | 1055 | } |
871 | return false; | 1056 | return false; |
872 | } | 1057 | } |
873 | // tracks[track_widx].filebuf = &codecbuf[buf_widx]; | 1058 | // tracks[track_widx].filebuf = &filebuf[buf_widx]; |
874 | tracks[track_widx].start_pos = 0; | 1059 | tracks[track_widx].start_pos = 0; |
875 | 1060 | ||
876 | /* Get track metadata if we don't already have it. */ | 1061 | /* Get track metadata if we don't already have it. */ |
@@ -933,10 +1118,10 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
933 | if (fill_bytesleft == 0) | 1118 | if (fill_bytesleft == 0) |
934 | break ; | 1119 | break ; |
935 | 1120 | ||
936 | copy_n = MIN(conf_filechunk, codecbuflen - buf_widx); | 1121 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); |
937 | copy_n = MIN(size - i, copy_n); | 1122 | copy_n = MIN(size - i, copy_n); |
938 | copy_n = MIN((int)fill_bytesleft, copy_n); | 1123 | copy_n = MIN((int)fill_bytesleft, copy_n); |
939 | rc = read(fd, &codecbuf[buf_widx], copy_n); | 1124 | rc = read(fd, &filebuf[buf_widx], copy_n); |
940 | if (rc < copy_n) { | 1125 | if (rc < copy_n) { |
941 | logf("File error!"); | 1126 | logf("File error!"); |
942 | tracks[track_widx].filesize = 0; | 1127 | tracks[track_widx].filesize = 0; |
@@ -945,12 +1130,12 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
945 | return false; | 1130 | return false; |
946 | } | 1131 | } |
947 | buf_widx += rc; | 1132 | buf_widx += rc; |
948 | if (buf_widx >= codecbuflen) | 1133 | if (buf_widx >= filebuflen) |
949 | buf_widx -= codecbuflen; | 1134 | buf_widx -= filebuflen; |
950 | i += rc; | 1135 | i += rc; |
951 | tracks[track_widx].available += rc; | 1136 | tracks[track_widx].available += rc; |
952 | tracks[track_widx].filerem -= rc; | 1137 | tracks[track_widx].filerem -= rc; |
953 | codecbufused += rc; | 1138 | filebufused += rc; |
954 | fill_bytesleft -= rc; | 1139 | fill_bytesleft -= rc; |
955 | } | 1140 | } |
956 | 1141 | ||
@@ -997,10 +1182,10 @@ void audio_play_start(int offset) | |||
997 | track_ridx = 0; | 1182 | track_ridx = 0; |
998 | buf_ridx = 0; | 1183 | buf_ridx = 0; |
999 | buf_widx = 0; | 1184 | buf_widx = 0; |
1000 | codecbufused = 0; | 1185 | filebufused = 0; |
1001 | pcmbuf_set_boost_mode(true); | 1186 | pcmbuf_set_boost_mode(true); |
1002 | 1187 | ||
1003 | fill_bytesleft = codecbuflen; | 1188 | fill_bytesleft = filebuflen; |
1004 | filling = true; | 1189 | filling = true; |
1005 | last_peek_offset = -1; | 1190 | last_peek_offset = -1; |
1006 | if (audio_load_track(offset, true, 0)) { | 1191 | if (audio_load_track(offset, true, 0)) { |
@@ -1089,7 +1274,7 @@ void initialize_buffer_fill(void) | |||
1089 | int cur_idx, i; | 1274 | int cur_idx, i; |
1090 | 1275 | ||
1091 | 1276 | ||
1092 | fill_bytesleft = codecbuflen - codecbufused; | 1277 | fill_bytesleft = filebuflen - filebufused; |
1093 | cur_ti->start_pos = ci.curpos; | 1278 | cur_ti->start_pos = ci.curpos; |
1094 | 1279 | ||
1095 | pcmbuf_set_boost_mode(true); | 1280 | pcmbuf_set_boost_mode(true); |
@@ -1124,7 +1309,7 @@ void initialize_buffer_fill(void) | |||
1124 | void audio_check_buffer(void) | 1309 | void audio_check_buffer(void) |
1125 | { | 1310 | { |
1126 | /* Start buffer filling as necessary. */ | 1311 | /* Start buffer filling as necessary. */ |
1127 | if ((codecbufused > conf_watermark || !queue_empty(&audio_queue) | 1312 | if ((filebufused > conf_watermark || !queue_empty(&audio_queue) |
1128 | || !playing || ci.stop_codec || ci.reload_codec) && !filling) | 1313 | || !playing || ci.stop_codec || ci.reload_codec) && !filling) |
1129 | return ; | 1314 | return ; |
1130 | 1315 | ||
@@ -1132,8 +1317,8 @@ void audio_check_buffer(void) | |||
1132 | 1317 | ||
1133 | /* Limit buffering size at first run. */ | 1318 | /* Limit buffering size at first run. */ |
1134 | if (conf_bufferlimit && fill_bytesleft > conf_bufferlimit | 1319 | if (conf_bufferlimit && fill_bytesleft > conf_bufferlimit |
1135 | - codecbufused) { | 1320 | - filebufused) { |
1136 | fill_bytesleft = MAX(0, conf_bufferlimit - codecbufused); | 1321 | fill_bytesleft = MAX(0, conf_bufferlimit - filebufused); |
1137 | } | 1322 | } |
1138 | 1323 | ||
1139 | /* Try to load remainings of the file. */ | 1324 | /* Try to load remainings of the file. */ |
@@ -1169,27 +1354,27 @@ void audio_update_trackinfo(void) | |||
1169 | { | 1354 | { |
1170 | if (new_track >= 0) { | 1355 | if (new_track >= 0) { |
1171 | buf_ridx += cur_ti->available; | 1356 | buf_ridx += cur_ti->available; |
1172 | codecbufused -= cur_ti->available; | 1357 | filebufused -= cur_ti->available; |
1173 | 1358 | ||
1174 | cur_ti = &tracks[track_ridx]; | 1359 | cur_ti = &tracks[track_ridx]; |
1175 | buf_ridx += cur_ti->codecsize; | 1360 | buf_ridx += cur_ti->codecsize; |
1176 | codecbufused -= cur_ti->codecsize; | 1361 | filebufused -= cur_ti->codecsize; |
1177 | if (buf_ridx >= codecbuflen) | 1362 | if (buf_ridx >= filebuflen) |
1178 | buf_ridx -= codecbuflen; | 1363 | buf_ridx -= filebuflen; |
1179 | 1364 | ||
1180 | if (!filling) | 1365 | if (!filling) |
1181 | pcmbuf_set_boost_mode(false); | 1366 | pcmbuf_set_boost_mode(false); |
1182 | } else { | 1367 | } else { |
1183 | buf_ridx -= ci.curpos + cur_ti->codecsize; | 1368 | buf_ridx -= ci.curpos + cur_ti->codecsize; |
1184 | codecbufused += ci.curpos + cur_ti->codecsize; | 1369 | filebufused += ci.curpos + cur_ti->codecsize; |
1185 | cur_ti->available = cur_ti->filesize; | 1370 | cur_ti->available = cur_ti->filesize; |
1186 | 1371 | ||
1187 | cur_ti = &tracks[track_ridx]; | 1372 | cur_ti = &tracks[track_ridx]; |
1188 | buf_ridx -= cur_ti->filesize; | 1373 | buf_ridx -= cur_ti->filesize; |
1189 | codecbufused += cur_ti->filesize; | 1374 | filebufused += cur_ti->filesize; |
1190 | cur_ti->available = cur_ti->filesize; | 1375 | cur_ti->available = cur_ti->filesize; |
1191 | if (buf_ridx < 0) | 1376 | if (buf_ridx < 0) |
1192 | buf_ridx = codecbuflen + buf_ridx; | 1377 | buf_ridx = filebuflen + buf_ridx; |
1193 | } | 1378 | } |
1194 | 1379 | ||
1195 | ci.filesize = cur_ti->filesize; | 1380 | ci.filesize = cur_ti->filesize; |
@@ -1220,7 +1405,7 @@ static void audio_stop_playback(void) | |||
1220 | current_fd = -1; | 1405 | current_fd = -1; |
1221 | } | 1406 | } |
1222 | pcmbuf_play_stop(); | 1407 | pcmbuf_play_stop(); |
1223 | while (codec_loaded) | 1408 | while (audio_codec_loaded) |
1224 | yield(); | 1409 | yield(); |
1225 | pcm_play_pause(true); | 1410 | pcm_play_pause(true); |
1226 | track_count = 0; | 1411 | track_count = 0; |
@@ -1267,6 +1452,11 @@ static int get_codec_base_type(int type) | |||
1267 | 1452 | ||
1268 | bool codec_request_next_track_callback(void) | 1453 | bool codec_request_next_track_callback(void) |
1269 | { | 1454 | { |
1455 | if (current_codec == CODEC_IDX_VOICE) { | ||
1456 | voice_remaining = 0; | ||
1457 | return !ci_voice.stop_codec; | ||
1458 | } | ||
1459 | |||
1270 | if (ci.stop_codec || !playing) | 1460 | if (ci.stop_codec || !playing) |
1271 | return false; | 1461 | return false; |
1272 | 1462 | ||
@@ -1309,8 +1499,8 @@ bool codec_request_next_track_callback(void) | |||
1309 | if (--track_ridx < 0) | 1499 | if (--track_ridx < 0) |
1310 | track_ridx = MAX_TRACK-1; | 1500 | track_ridx = MAX_TRACK-1; |
1311 | if (tracks[track_ridx].filesize == 0 || | 1501 | if (tracks[track_ridx].filesize == 0 || |
1312 | codecbufused+ci.curpos+tracks[track_ridx].filesize | 1502 | filebufused+ci.curpos+tracks[track_ridx].filesize |
1313 | /*+ (off_t)tracks[track_ridx].codecsize*/ > codecbuflen) { | 1503 | /*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) { |
1314 | logf("Loading from disk..."); | 1504 | logf("Loading from disk..."); |
1315 | new_track = 0; | 1505 | new_track = 0; |
1316 | last_index = -1; | 1506 | last_index = -1; |
@@ -1379,10 +1569,10 @@ void audio_invalidate_tracks(void) | |||
1379 | track_widx = track_ridx; | 1569 | track_widx = track_ridx; |
1380 | /* Mark all other entries null (also buffered wrong metadata). */ | 1570 | /* Mark all other entries null (also buffered wrong metadata). */ |
1381 | audio_clear_track_entries(false); | 1571 | audio_clear_track_entries(false); |
1382 | codecbufused = cur_ti->available; | 1572 | filebufused = cur_ti->available; |
1383 | buf_widx = buf_ridx + cur_ti->available; | 1573 | buf_widx = buf_ridx + cur_ti->available; |
1384 | if (buf_widx >= codecbuflen) | 1574 | if (buf_widx >= filebuflen) |
1385 | buf_widx -= codecbuflen; | 1575 | buf_widx -= filebuflen; |
1386 | read_next_metadata(); | 1576 | read_next_metadata(); |
1387 | } | 1577 | } |
1388 | 1578 | ||
@@ -1436,7 +1626,7 @@ void audio_thread(void) | |||
1436 | ci.reload_codec = false; | 1626 | ci.reload_codec = false; |
1437 | ci.seek_time = 0; | 1627 | ci.seek_time = 0; |
1438 | pcmbuf_crossfade_init(CROSSFADE_MODE_CROSSFADE); | 1628 | pcmbuf_crossfade_init(CROSSFADE_MODE_CROSSFADE); |
1439 | while (codec_loaded) | 1629 | while (audio_codec_loaded) |
1440 | yield(); | 1630 | yield(); |
1441 | audio_play_start((int)ev.data); | 1631 | audio_play_start((int)ev.data); |
1442 | playlist_update_resume_info(audio_current_track()); | 1632 | playlist_update_resume_info(audio_current_track()); |
@@ -1462,11 +1652,13 @@ void audio_thread(void) | |||
1462 | 1652 | ||
1463 | case AUDIO_NEXT: | 1653 | case AUDIO_NEXT: |
1464 | logf("audio_next"); | 1654 | logf("audio_next"); |
1655 | pcmbuf_beep(5000, 100, 5000); | ||
1465 | initiate_track_change(1); | 1656 | initiate_track_change(1); |
1466 | break ; | 1657 | break ; |
1467 | 1658 | ||
1468 | case AUDIO_PREV: | 1659 | case AUDIO_PREV: |
1469 | logf("audio_prev"); | 1660 | logf("audio_prev"); |
1661 | pcmbuf_beep(5000, 100, 5000); | ||
1470 | initiate_track_change(-1); | 1662 | initiate_track_change(-1); |
1471 | break; | 1663 | break; |
1472 | 1664 | ||
@@ -1514,8 +1706,11 @@ void codec_thread(void) | |||
1514 | switch (ev.id) { | 1706 | switch (ev.id) { |
1515 | case CODEC_LOAD_DISK: | 1707 | case CODEC_LOAD_DISK: |
1516 | ci.stop_codec = false; | 1708 | ci.stop_codec = false; |
1517 | codec_loaded = true; | 1709 | audio_codec_loaded = true; |
1518 | status = codec_load_file((char *)ev.data); | 1710 | mutex_lock(&mutex_codecthread); |
1711 | current_codec = CODEC_IDX_AUDIO; | ||
1712 | status = codec_load_file((char *)ev.data, &ci); | ||
1713 | mutex_unlock(&mutex_codecthread); | ||
1519 | break ; | 1714 | break ; |
1520 | 1715 | ||
1521 | case CODEC_LOAD: | 1716 | case CODEC_LOAD: |
@@ -1531,10 +1726,13 @@ void codec_thread(void) | |||
1531 | } | 1726 | } |
1532 | 1727 | ||
1533 | ci.stop_codec = false; | 1728 | ci.stop_codec = false; |
1534 | wrap = (int)&codecbuf[codecbuflen] - (int)cur_ti->codecbuf; | 1729 | wrap = (int)&filebuf[filebuflen] - (int)cur_ti->codecbuf; |
1535 | codec_loaded = true; | 1730 | audio_codec_loaded = true; |
1536 | status = codec_load_ram(cur_ti->codecbuf, codecsize, | 1731 | mutex_lock(&mutex_codecthread); |
1537 | &codecbuf[0], wrap); | 1732 | current_codec = CODEC_IDX_AUDIO; |
1733 | status = codec_load_ram(cur_ti->codecbuf, codecsize, | ||
1734 | &filebuf[0], wrap, &ci); | ||
1735 | mutex_unlock(&mutex_codecthread); | ||
1538 | break ; | 1736 | break ; |
1539 | 1737 | ||
1540 | #ifndef SIMULATOR | 1738 | #ifndef SIMULATOR |
@@ -1545,7 +1743,7 @@ void codec_thread(void) | |||
1545 | #endif | 1743 | #endif |
1546 | } | 1744 | } |
1547 | 1745 | ||
1548 | codec_loaded = false; | 1746 | audio_codec_loaded = false; |
1549 | 1747 | ||
1550 | switch (ev.id) { | 1748 | switch (ev.id) { |
1551 | case CODEC_LOAD_DISK: | 1749 | case CODEC_LOAD_DISK: |
@@ -1569,6 +1767,83 @@ void codec_thread(void) | |||
1569 | } | 1767 | } |
1570 | } | 1768 | } |
1571 | 1769 | ||
1770 | static void reset_buffer(void) | ||
1771 | { | ||
1772 | filebuf = &audiobuf[MALLOC_BUFSIZE]; | ||
1773 | filebuflen = audiobufend - audiobuf - pcmbuf_get_bufsize() | ||
1774 | - PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE; | ||
1775 | |||
1776 | if (talk_get_bufsize() && voice_codec_loaded) | ||
1777 | { | ||
1778 | filebuf = &filebuf[talk_get_bufsize()]; | ||
1779 | filebuflen -= 2*CODEC_IRAM_SIZE + 2*CODEC_SIZE + talk_get_bufsize(); | ||
1780 | } | ||
1781 | } | ||
1782 | |||
1783 | void voice_codec_thread(void) | ||
1784 | { | ||
1785 | struct event ev; | ||
1786 | int status; | ||
1787 | |||
1788 | current_codec = CODEC_IDX_AUDIO; | ||
1789 | voice_codec_loaded = false; | ||
1790 | while (1) { | ||
1791 | status = 0; | ||
1792 | queue_wait(&voice_codec_queue, &ev); | ||
1793 | switch (ev.id) { | ||
1794 | case CODEC_LOAD_DISK: | ||
1795 | logf("Loading voice codec"); | ||
1796 | audio_stop_playback(); | ||
1797 | mutex_lock(&mutex_codecthread); | ||
1798 | current_codec = CODEC_IDX_VOICE; | ||
1799 | dsp_configure(DSP_RESET, 0); | ||
1800 | ci.configure(CODEC_DSP_ENABLE, (bool *)true); | ||
1801 | voice_remaining = 0; | ||
1802 | voice_getmore = NULL; | ||
1803 | voice_codec_loaded = true; | ||
1804 | reset_buffer(); | ||
1805 | ci_voice.stop_codec = false; | ||
1806 | |||
1807 | status = codec_load_file((char *)ev.data, &ci_voice); | ||
1808 | |||
1809 | logf("Voice codec finished"); | ||
1810 | audio_stop_playback(); | ||
1811 | mutex_unlock(&mutex_codecthread); | ||
1812 | current_codec = CODEC_IDX_AUDIO; | ||
1813 | voice_codec_loaded = false; | ||
1814 | reset_buffer(); | ||
1815 | break ; | ||
1816 | |||
1817 | #ifndef SIMULATOR | ||
1818 | case SYS_USB_CONNECTED: | ||
1819 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1820 | usb_wait_for_disconnect(&voice_codec_queue); | ||
1821 | break ; | ||
1822 | #endif | ||
1823 | } | ||
1824 | } | ||
1825 | } | ||
1826 | |||
1827 | void voice_init(void) | ||
1828 | { | ||
1829 | while (voice_codec_loaded) | ||
1830 | { | ||
1831 | logf("Terminating voice codec"); | ||
1832 | ci_voice.stop_codec = true; | ||
1833 | if (current_codec != CODEC_IDX_VOICE) | ||
1834 | swap_codec(); | ||
1835 | sleep(1); | ||
1836 | } | ||
1837 | |||
1838 | if (!talk_get_bufsize()) | ||
1839 | return ; | ||
1840 | |||
1841 | logf("Starting voice codec"); | ||
1842 | queue_post(&voice_codec_queue, CODEC_LOAD_DISK, (void *)CODEC_MPA_L3); | ||
1843 | while (!voice_codec_loaded) | ||
1844 | sleep(1); | ||
1845 | } | ||
1846 | |||
1572 | struct mp3entry* audio_current_track(void) | 1847 | struct mp3entry* audio_current_track(void) |
1573 | { | 1848 | { |
1574 | // logf("audio_current_track"); | 1849 | // logf("audio_current_track"); |
@@ -1620,7 +1895,7 @@ void audio_stop(void) | |||
1620 | { | 1895 | { |
1621 | logf("audio_stop"); | 1896 | logf("audio_stop"); |
1622 | queue_post(&audio_queue, AUDIO_STOP, 0); | 1897 | queue_post(&audio_queue, AUDIO_STOP, 0); |
1623 | while (playing || codec_loaded) | 1898 | while (playing || audio_codec_loaded) |
1624 | yield(); | 1899 | yield(); |
1625 | } | 1900 | } |
1626 | 1901 | ||
@@ -1805,6 +2080,16 @@ int mp3_get_file_pos(void) | |||
1805 | return pos; | 2080 | return pos; |
1806 | } | 2081 | } |
1807 | 2082 | ||
2083 | void mp3_play_data(const unsigned char* start, int size, | ||
2084 | void (*get_more)(unsigned char** start, int* size)) | ||
2085 | { | ||
2086 | voice_getmore = get_more; | ||
2087 | voicebuf = (unsigned char *)start; | ||
2088 | voice_remaining = size; | ||
2089 | voice_is_playing = true; | ||
2090 | pcmbuf_reset_mixpos(); | ||
2091 | } | ||
2092 | |||
1808 | void audio_set_buffer_margin(int setting) | 2093 | void audio_set_buffer_margin(int setting) |
1809 | { | 2094 | { |
1810 | int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | 2095 | int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; |
@@ -1827,7 +2112,7 @@ void audio_set_crossfade(int type) | |||
1827 | offset = cur_ti->id3.offset; | 2112 | offset = cur_ti->id3.offset; |
1828 | 2113 | ||
1829 | if (type == CROSSFADE_MODE_OFF) | 2114 | if (type == CROSSFADE_MODE_OFF) |
1830 | seconds = 0; | 2115 | seconds = 1; |
1831 | 2116 | ||
1832 | /* Buffer has to be at least 2s long. */ | 2117 | /* Buffer has to be at least 2s long. */ |
1833 | seconds += 2; | 2118 | seconds += 2; |
@@ -1843,12 +2128,13 @@ void audio_set_crossfade(int type) | |||
1843 | if (was_playing) | 2128 | if (was_playing) |
1844 | splash(0, true, str(LANG_RESTARTING_PLAYBACK)); | 2129 | splash(0, true, str(LANG_RESTARTING_PLAYBACK)); |
1845 | pcmbuf_init(size); | 2130 | pcmbuf_init(size); |
1846 | pcmbuf_crossfade_enable(seconds > 2); | 2131 | pcmbuf_crossfade_enable(type != CROSSFADE_MODE_OFF); |
1847 | codecbuflen = audiobufend - audiobuf - pcmbuf_get_bufsize() | 2132 | reset_buffer(); |
1848 | - PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE; | ||
1849 | logf("abuf:%dB", pcmbuf_get_bufsize()); | 2133 | logf("abuf:%dB", pcmbuf_get_bufsize()); |
1850 | logf("fbuf:%dB", codecbuflen); | 2134 | logf("fbuf:%dB", filebuflen); |
1851 | 2135 | ||
2136 | voice_init(); | ||
2137 | |||
1852 | /* Restart playback. */ | 2138 | /* Restart playback. */ |
1853 | if (was_playing) { | 2139 | if (was_playing) { |
1854 | audio_play(offset); | 2140 | audio_play(offset); |
@@ -1856,7 +2142,7 @@ void audio_set_crossfade(int type) | |||
1856 | /* Wait for the playback to start again (and display the splash | 2142 | /* Wait for the playback to start again (and display the splash |
1857 | screen during that period. */ | 2143 | screen during that period. */ |
1858 | playing = true; | 2144 | playing = true; |
1859 | while (playing && !codec_loaded) | 2145 | while (playing && !audio_codec_loaded) |
1860 | yield(); | 2146 | yield(); |
1861 | } | 2147 | } |
1862 | } | 2148 | } |
@@ -1884,13 +2170,17 @@ void test_unbuffer_event(struct mp3entry *id3, bool last_track) | |||
1884 | 2170 | ||
1885 | void audio_init(void) | 2171 | void audio_init(void) |
1886 | { | 2172 | { |
2173 | static bool voicetagtrue = true; | ||
2174 | |||
1887 | logf("audio api init"); | 2175 | logf("audio api init"); |
1888 | pcm_init(); | 2176 | pcm_init(); |
1889 | codecbufused = 0; | 2177 | filebufused = 0; |
1890 | filling = false; | 2178 | filling = false; |
1891 | codecbuf = &audiobuf[MALLOC_BUFSIZE]; | 2179 | current_codec = CODEC_IDX_AUDIO; |
2180 | filebuf = &audiobuf[MALLOC_BUFSIZE]; | ||
1892 | playing = false; | 2181 | playing = false; |
1893 | codec_loaded = false; | 2182 | audio_codec_loaded = false; |
2183 | voice_is_playing = false; | ||
1894 | paused = false; | 2184 | paused = false; |
1895 | track_changed = false; | 2185 | track_changed = false; |
1896 | current_fd = -1; | 2186 | current_fd = -1; |
@@ -1918,12 +2208,25 @@ void audio_init(void) | |||
1918 | ci.set_offset = codec_set_offset_callback; | 2208 | ci.set_offset = codec_set_offset_callback; |
1919 | ci.configure = codec_configure_callback; | 2209 | ci.configure = codec_configure_callback; |
1920 | 2210 | ||
2211 | memcpy(&ci_voice, &ci, sizeof(struct codec_api)); | ||
2212 | memset(&id3_voice, 0, sizeof(struct mp3entry)); | ||
2213 | ci_voice.taginfo_ready = &voicetagtrue; | ||
2214 | ci_voice.id3 = &id3_voice; | ||
2215 | ci_voice.pcmbuf_insert = codec_pcmbuf_insert_callback; | ||
2216 | id3_voice.frequency = 11200; | ||
2217 | id3_voice.length = 1000000L; | ||
2218 | |||
1921 | mutex_init(&mutex_bufferfill); | 2219 | mutex_init(&mutex_bufferfill); |
2220 | mutex_init(&mutex_codecthread); | ||
2221 | |||
1922 | queue_init(&audio_queue); | 2222 | queue_init(&audio_queue); |
1923 | queue_init(&codec_queue); | 2223 | queue_init(&codec_queue); |
2224 | queue_init(&voice_codec_queue); | ||
1924 | 2225 | ||
1925 | create_thread(codec_thread, codec_stack, sizeof(codec_stack), | 2226 | create_thread(codec_thread, codec_stack, sizeof(codec_stack), |
1926 | codec_thread_name); | 2227 | codec_thread_name); |
2228 | create_thread(voice_codec_thread, voice_codec_stack, | ||
2229 | sizeof(voice_codec_stack), voice_codec_thread_name); | ||
1927 | create_thread(audio_thread, audio_stack, sizeof(audio_stack), | 2230 | create_thread(audio_thread, audio_stack, sizeof(audio_stack), |
1928 | audio_thread_name); | 2231 | audio_thread_name); |
1929 | } | 2232 | } |
diff --git a/apps/playback.h b/apps/playback.h index 7ed9a4b700..a5b64ba0e3 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -72,6 +72,7 @@ void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, | |||
72 | void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3, | 72 | void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3, |
73 | bool last_track)); | 73 | bool last_track)); |
74 | void audio_invalidate_tracks(void); | 74 | void audio_invalidate_tracks(void); |
75 | void voice_init(void); | ||
75 | 76 | ||
76 | #endif | 77 | #endif |
77 | 78 | ||
diff --git a/apps/playlist.c b/apps/playlist.c index 68fd8be369..bd443e4f38 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -1413,9 +1413,14 @@ int playlist_resume(void) | |||
1413 | }; | 1413 | }; |
1414 | 1414 | ||
1415 | /* use mp3 buffer for maximum load speed */ | 1415 | /* use mp3 buffer for maximum load speed */ |
1416 | #if CONFIG_HWCODEC != MASNONE | ||
1416 | talk_buffer_steal(); /* we use the mp3 buffer, need to tell */ | 1417 | talk_buffer_steal(); /* we use the mp3 buffer, need to tell */ |
1417 | buflen = (audiobufend - audiobuf); | 1418 | buflen = (audiobufend - audiobuf); |
1418 | buffer = audiobuf; | 1419 | buffer = audiobuf; |
1420 | #else | ||
1421 | buflen = (audiobufend - audiobuf - talk_get_bufsize()); | ||
1422 | buffer = &audiobuf[talk_get_bufsize()]; | ||
1423 | #endif | ||
1419 | 1424 | ||
1420 | empty_playlist(playlist, true); | 1425 | empty_playlist(playlist, true); |
1421 | 1426 | ||
@@ -1827,7 +1832,9 @@ int playlist_start(int start_index, int offset) | |||
1827 | struct playlist_info* playlist = ¤t_playlist; | 1832 | struct playlist_info* playlist = ¤t_playlist; |
1828 | 1833 | ||
1829 | playlist->index = start_index; | 1834 | playlist->index = start_index; |
1835 | #if CONFIG_HWCODEC != MASNONE | ||
1830 | talk_buffer_steal(); /* will use the mp3 buffer */ | 1836 | talk_buffer_steal(); /* will use the mp3 buffer */ |
1837 | #endif | ||
1831 | audio_play(offset); | 1838 | audio_play(offset); |
1832 | 1839 | ||
1833 | return 0; | 1840 | return 0; |
diff --git a/apps/talk.c b/apps/talk.c index a896ca3a1a..b417046a61 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -32,7 +32,11 @@ | |||
32 | #include "lang.h" | 32 | #include "lang.h" |
33 | #include "talk.h" | 33 | #include "talk.h" |
34 | #include "id3.h" | 34 | #include "id3.h" |
35 | #include "logf.h" | ||
35 | #include "bitswap.h" | 36 | #include "bitswap.h" |
37 | #if CONFIG_HWCODEC == MASNONE | ||
38 | #include "playback.h" | ||
39 | #endif | ||
36 | 40 | ||
37 | /***************** Constants *****************/ | 41 | /***************** Constants *****************/ |
38 | 42 | ||
@@ -88,6 +92,7 @@ static int filehandle; /* global, so the MMC variant can keep the file open */ | |||
88 | static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ | 92 | static unsigned char* p_silence; /* VOICE_PAUSE clip, used for termination */ |
89 | static long silence_len; /* length of the VOICE_PAUSE clip */ | 93 | static long silence_len; /* length of the VOICE_PAUSE clip */ |
90 | static unsigned char* p_lastclip; /* address of latest clip, for silence add */ | 94 | static unsigned char* p_lastclip; /* address of latest clip, for silence add */ |
95 | static unsigned long voicefile_size = 0; /* size of the loaded voice file */ | ||
91 | 96 | ||
92 | 97 | ||
93 | /***************** Private prototypes *****************/ | 98 | /***************** Private prototypes *****************/ |
@@ -114,10 +119,28 @@ static int open_voicefile(void) | |||
114 | } | 119 | } |
115 | 120 | ||
116 | snprintf(buf, sizeof(buf), ROCKBOX_DIR LANG_DIR "/%s.voice", p_lang); | 121 | snprintf(buf, sizeof(buf), ROCKBOX_DIR LANG_DIR "/%s.voice", p_lang); |
117 | 122 | ||
118 | return open(buf, O_RDONLY); | 123 | return open(buf, O_RDONLY); |
119 | } | 124 | } |
120 | 125 | ||
126 | int talk_get_bufsize(void) | ||
127 | { | ||
128 | return voicefile_size; | ||
129 | } | ||
130 | |||
131 | #ifdef SIMULATOR | ||
132 | static unsigned short BSWAP16(unsigned short value) | ||
133 | { | ||
134 | return (value >> 8) | (value << 8); | ||
135 | } | ||
136 | |||
137 | static unsigned long BSWAP32(unsigned long value) | ||
138 | { | ||
139 | unsigned long hi = BSWAP16(value >> 16); | ||
140 | unsigned long lo = BSWAP16(value & 0xffff); | ||
141 | return (lo << 16) | hi; | ||
142 | } | ||
143 | #endif | ||
121 | 144 | ||
122 | /* load the voice file into the mp3 buffer */ | 145 | /* load the voice file into the mp3 buffer */ |
123 | static void load_voicefile(void) | 146 | static void load_voicefile(void) |
@@ -125,6 +148,10 @@ static void load_voicefile(void) | |||
125 | int load_size; | 148 | int load_size; |
126 | int got_size; | 149 | int got_size; |
127 | int file_size; | 150 | int file_size; |
151 | #if CONFIG_HWCODEC == MASNONE | ||
152 | int length, i; | ||
153 | unsigned char *buf, temp; | ||
154 | #endif | ||
128 | 155 | ||
129 | filehandle = open_voicefile(); | 156 | filehandle = open_voicefile(); |
130 | if (filehandle < 0) /* failed to open */ | 157 | if (filehandle < 0) /* failed to open */ |
@@ -141,8 +168,20 @@ static void load_voicefile(void) | |||
141 | #endif | 168 | #endif |
142 | 169 | ||
143 | got_size = read(filehandle, audiobuf, load_size); | 170 | got_size = read(filehandle, audiobuf, load_size); |
144 | if (got_size == load_size /* success */ | 171 | if (got_size != load_size /* failure */) |
145 | && ((struct voicefile*)audiobuf)->table /* format check */ | 172 | goto load_err; |
173 | |||
174 | #ifdef SIMULATOR | ||
175 | logf("Byte swapping voice file"); | ||
176 | p_voicefile = (struct voicefile*)audiobuf; | ||
177 | p_voicefile->version = BSWAP32(p_voicefile->version); | ||
178 | p_voicefile->table = BSWAP32(p_voicefile->table); | ||
179 | p_voicefile->id1_max = BSWAP32(p_voicefile->id1_max); | ||
180 | p_voicefile->id2_max = BSWAP32(p_voicefile->id2_max); | ||
181 | p_voicefile = NULL; | ||
182 | #endif | ||
183 | |||
184 | if (((struct voicefile*)audiobuf)->table /* format check */ | ||
146 | == offsetof(struct voicefile, index)) | 185 | == offsetof(struct voicefile, index)) |
147 | { | 186 | { |
148 | p_voicefile = (struct voicefile*)audiobuf; | 187 | p_voicefile = (struct voicefile*)audiobuf; |
@@ -155,7 +194,42 @@ static void load_voicefile(void) | |||
155 | else | 194 | else |
156 | goto load_err; | 195 | goto load_err; |
157 | 196 | ||
158 | #ifdef HAVE_MMC | 197 | #ifdef SIMULATOR |
198 | for (i = 0; i < p_voicefile->id1_max + p_voicefile->id2_max; i++) | ||
199 | { | ||
200 | struct clip_entry *ce; | ||
201 | ce = &p_voicefile->index[i]; | ||
202 | ce->offset = BSWAP32(ce->offset); | ||
203 | ce->size = BSWAP32(ce->size); | ||
204 | } | ||
205 | #endif | ||
206 | |||
207 | /* Do a bitswap as necessary. */ | ||
208 | #if CONFIG_HWCODEC == MASNONE | ||
209 | logf("Bitswapping voice file."); | ||
210 | cpu_boost(true); | ||
211 | buf = (unsigned char *)(&p_voicefile->index) + | ||
212 | (p_voicefile->id1_max + p_voicefile->id2_max) * sizeof(struct clip_entry); | ||
213 | length = file_size - offsetof(struct voicefile, index) - | ||
214 | (p_voicefile->id1_max - p_voicefile->id2_max) * sizeof(struct clip_entry); | ||
215 | |||
216 | for (i = 0; i < length; i++) | ||
217 | { | ||
218 | temp = buf[i]; | ||
219 | buf[i] = ((temp >> 7) & 0x01) | ||
220 | | ((temp >> 5) & 0x02) | ||
221 | | ((temp >> 3) & 0x04) | ||
222 | | ((temp >> 1) & 0x08) | ||
223 | | ((temp << 1) & 0x10) | ||
224 | | ((temp << 3) & 0x20) | ||
225 | | ((temp << 5) & 0x40) | ||
226 | | ((temp << 7) & 0x80); | ||
227 | } | ||
228 | cpu_boost(false); | ||
229 | |||
230 | #endif | ||
231 | |||
232 | #ifdef HAVE_MMC | ||
159 | /* load the index table, now that we know its size from the header */ | 233 | /* load the index table, now that we know its size from the header */ |
160 | load_size = (p_voicefile->id1_max + p_voicefile->id2_max) | 234 | load_size = (p_voicefile->id1_max + p_voicefile->id2_max) |
161 | * sizeof(struct clip_entry); | 235 | * sizeof(struct clip_entry); |
@@ -193,7 +267,11 @@ static void mp3_callback(unsigned char** start, int* size) | |||
193 | 267 | ||
194 | if (queue[queue_read].len > 0) /* current clip not finished? */ | 268 | if (queue[queue_read].len > 0) /* current clip not finished? */ |
195 | { /* feed the next 64K-1 chunk */ | 269 | { /* feed the next 64K-1 chunk */ |
270 | #if CONFIG_HWCODEC != MASNONE | ||
196 | sent = MIN(queue[queue_read].len, 0xFFFF); | 271 | sent = MIN(queue[queue_read].len, 0xFFFF); |
272 | #else | ||
273 | sent = queue[queue_read].len; | ||
274 | #endif | ||
197 | *start = queue[queue_read].buf; | 275 | *start = queue[queue_read].buf; |
198 | *size = sent; | 276 | *size = sent; |
199 | return; | 277 | return; |
@@ -207,7 +285,11 @@ re_check: | |||
207 | 285 | ||
208 | if (QUEUE_LEVEL) /* queue is not empty? */ | 286 | if (QUEUE_LEVEL) /* queue is not empty? */ |
209 | { /* start next clip */ | 287 | { /* start next clip */ |
288 | #if CONFIG_HWCODEC != MASNONE | ||
210 | sent = MIN(queue[queue_read].len, 0xFFFF); | 289 | sent = MIN(queue[queue_read].len, 0xFFFF); |
290 | #else | ||
291 | sent = queue[queue_read].len; | ||
292 | #endif | ||
211 | *start = p_lastclip = queue[queue_read].buf; | 293 | *start = p_lastclip = queue[queue_read].buf; |
212 | *size = sent; | 294 | *size = sent; |
213 | curr_hd[0] = p_lastclip[1]; | 295 | curr_hd[0] = p_lastclip[1]; |
@@ -286,7 +368,7 @@ static int shutup(void) | |||
286 | /* nothing to do, was frame boundary or not our clip */ | 368 | /* nothing to do, was frame boundary or not our clip */ |
287 | mp3_play_stop(); | 369 | mp3_play_stop(); |
288 | queue_write = queue_read = 0; /* reset the queue */ | 370 | queue_write = queue_read = 0; /* reset the queue */ |
289 | 371 | ||
290 | return 0; | 372 | return 0; |
291 | } | 373 | } |
292 | 374 | ||
@@ -317,7 +399,11 @@ static int queue_clip(unsigned char* buf, long size, bool enqueue) | |||
317 | if (queue_level == 0) | 399 | if (queue_level == 0) |
318 | { /* queue was empty, we have to do the initial start */ | 400 | { /* queue was empty, we have to do the initial start */ |
319 | p_lastclip = buf; | 401 | p_lastclip = buf; |
402 | #if CONFIG_HWCODEC != MASNONE | ||
320 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ | 403 | sent = MIN(size, 0xFFFF); /* DMA can do no more */ |
404 | #else | ||
405 | sent = size; | ||
406 | #endif | ||
321 | mp3_play_data(buf, sent, mp3_callback); | 407 | mp3_play_data(buf, sent, mp3_callback); |
322 | curr_hd[0] = buf[1]; | 408 | curr_hd[0] = buf[1]; |
323 | curr_hd[1] = buf[2]; | 409 | curr_hd[1] = buf[2]; |
@@ -400,12 +486,19 @@ void talk_init(void) | |||
400 | #else | 486 | #else |
401 | filehandle = open_voicefile(); | 487 | filehandle = open_voicefile(); |
402 | has_voicefile = (filehandle >= 0); /* test if we can open it */ | 488 | has_voicefile = (filehandle >= 0); /* test if we can open it */ |
489 | voicefile_size = 0; | ||
490 | |||
403 | if (has_voicefile) | 491 | if (has_voicefile) |
404 | { | 492 | { |
493 | voicefile_size = filesize(filehandle); | ||
494 | #if CONFIG_HWCODEC == MASNONE | ||
495 | voice_init(); | ||
496 | #endif | ||
405 | close(filehandle); /* close again, this was just to detect presence */ | 497 | close(filehandle); /* close again, this was just to detect presence */ |
406 | filehandle = -1; | 498 | filehandle = -1; |
407 | } | 499 | } |
408 | #endif | 500 | #endif |
501 | |||
409 | } | 502 | } |
410 | 503 | ||
411 | 504 | ||
@@ -432,8 +525,10 @@ int talk_id(long id, bool enqueue) | |||
432 | unsigned char* clipbuf; | 525 | unsigned char* clipbuf; |
433 | int unit; | 526 | int unit; |
434 | 527 | ||
528 | #if CONFIG_HWCODEC != MASNONE | ||
435 | if (audio_status()) /* busy, buffer in use */ | 529 | if (audio_status()) /* busy, buffer in use */ |
436 | return -1; | 530 | return -1; |
531 | #endif | ||
437 | 532 | ||
438 | if (p_voicefile == NULL && has_voicefile) | 533 | if (p_voicefile == NULL && has_voicefile) |
439 | load_voicefile(); /* reload needed */ | 534 | load_voicefile(); /* reload needed */ |
@@ -514,8 +609,10 @@ int talk_number(long n, bool enqueue) | |||
514 | int level = 0; /* mille count */ | 609 | int level = 0; /* mille count */ |
515 | long mil = 1000000000; /* highest possible "-illion" */ | 610 | long mil = 1000000000; /* highest possible "-illion" */ |
516 | 611 | ||
612 | #if CONFIG_HWCODEC != MASNONE | ||
517 | if (audio_status()) /* busy, buffer in use */ | 613 | if (audio_status()) /* busy, buffer in use */ |
518 | return -1; | 614 | return -1; |
615 | #endif | ||
519 | 616 | ||
520 | if (!enqueue) | 617 | if (!enqueue) |
521 | shutup(); /* cut off all the pending stuff */ | 618 | shutup(); /* cut off all the pending stuff */ |
@@ -593,8 +690,10 @@ int talk_value(long n, int unit, bool enqueue) | |||
593 | VOICE_HERTZ, | 690 | VOICE_HERTZ, |
594 | }; | 691 | }; |
595 | 692 | ||
693 | #if CONFIG_HWCODEC != MASNONE | ||
596 | if (audio_status()) /* busy, buffer in use */ | 694 | if (audio_status()) /* busy, buffer in use */ |
597 | return -1; | 695 | return -1; |
696 | #endif | ||
598 | 697 | ||
599 | if (unit < 0 || unit >= UNIT_LAST) | 698 | if (unit < 0 || unit >= UNIT_LAST) |
600 | unit_id = -1; | 699 | unit_id = -1; |
@@ -625,8 +724,10 @@ int talk_spell(const char* spell, bool enqueue) | |||
625 | { | 724 | { |
626 | char c; /* currently processed char */ | 725 | char c; /* currently processed char */ |
627 | 726 | ||
727 | #if CONFIG_HWCODEC != MASNONE | ||
628 | if (audio_status()) /* busy, buffer in use */ | 728 | if (audio_status()) /* busy, buffer in use */ |
629 | return -1; | 729 | return -1; |
730 | #endif | ||
630 | 731 | ||
631 | if (!enqueue) | 732 | if (!enqueue) |
632 | shutup(); /* cut off all the pending stuff */ | 733 | shutup(); /* cut off all the pending stuff */ |
diff --git a/apps/talk.h b/apps/talk.h index 213e1803d4..18314e52c5 100644 --- a/apps/talk.h +++ b/apps/talk.h | |||
@@ -59,6 +59,7 @@ extern const char* const dir_thumbnail_name; /* "_dirname.talk" */ | |||
59 | extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */ | 59 | extern const char* const file_thumbnail_ext; /* ".talk" for file voicing */ |
60 | 60 | ||
61 | void talk_init(void); | 61 | void talk_init(void); |
62 | int talk_get_bufsize(void); /* get the loaded voice file size */ | ||
62 | int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ | 63 | int talk_buffer_steal(void); /* claim the mp3 buffer e.g. for play/record */ |
63 | int talk_id(long id, bool enqueue); /* play a voice ID from voicefont */ | 64 | int talk_id(long id, bool enqueue); /* play a voice ID from voicefont */ |
64 | int talk_file(const char* filename, bool enqueue); /* play a thumbnail from file */ | 65 | int talk_file(const char* filename, bool enqueue); /* play a thumbnail from file */ |