diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-03-19 22:04:17 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-03-19 22:04:17 +0000 |
commit | e1dd10ddfb49bfdd05fa8997bc097df93a9c9466 (patch) | |
tree | 0c530ecb1ab68eb335edf595829bf7786e6a683e | |
parent | fbf52ae8fe04f27368392b71a3572dc2bb00788f (diff) | |
download | rockbox-e1dd10ddfb49bfdd05fa8997bc097df93a9c9466.tar.gz rockbox-e1dd10ddfb49bfdd05fa8997bc097df93a9c9466.zip |
SWCODEC: Get rid of extra swap buffer and get back 512K of RAM or 100K if the players RAM is <= 1MB. Make any needed changes to things to stabilize and facilitate this including removing flattening out initialization. Comment some things heavily. Fix a few logfs I didn't want to see the compiler complaining about.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12843 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/pcmbuf.c | 92 | ||||
-rw-r--r-- | apps/pcmbuf.h | 10 | ||||
-rw-r--r-- | apps/playback.c | 561 | ||||
-rw-r--r-- | apps/playback.h | 2 | ||||
-rw-r--r-- | apps/talk.c | 3 | ||||
-rw-r--r-- | firmware/SOURCES | 4 | ||||
-rw-r--r-- | firmware/common/memswap128.c | 44 | ||||
-rw-r--r-- | firmware/include/memory.h | 11 | ||||
-rw-r--r-- | firmware/target/arm/memswap128-arm.S | 44 | ||||
-rw-r--r-- | firmware/target/coldfire/memswap128-coldfire.S | 50 |
10 files changed, 579 insertions, 242 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 568cc7f49e..89f9e27798 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -58,7 +58,10 @@ struct pcmbufdesc | |||
58 | void (*callback)(void); | 58 | void (*callback)(void); |
59 | }; | 59 | }; |
60 | 60 | ||
61 | #define PCMBUF_DESCS(bufsize) ((bufsize) / PCMBUF_MINAVG_CHUNK) | 61 | #define PCMBUF_DESCS(bufsize) \ |
62 | ((bufsize) / PCMBUF_MINAVG_CHUNK) | ||
63 | #define PCMBUF_DESCS_SIZE(bufsize) \ | ||
64 | (PCMBUF_DESCS(bufsize)*sizeof(struct pcmbufdesc)) | ||
62 | 65 | ||
63 | /* Size of the PCM buffer. */ | 66 | /* Size of the PCM buffer. */ |
64 | static size_t pcmbuf_size IDATA_ATTR = 0; | 67 | static size_t pcmbuf_size IDATA_ATTR = 0; |
@@ -76,6 +79,7 @@ static void (*position_callback)(size_t size) IDATA_ATTR; | |||
76 | 79 | ||
77 | /* Crossfade related state */ | 80 | /* Crossfade related state */ |
78 | static bool crossfade_enabled; | 81 | static bool crossfade_enabled; |
82 | static bool crossfade_enabled_pending; | ||
79 | static bool crossfade_mixmode; | 83 | static bool crossfade_mixmode; |
80 | static bool crossfade_active IDATA_ATTR; | 84 | static bool crossfade_active IDATA_ATTR; |
81 | static bool crossfade_init IDATA_ATTR; | 85 | static bool crossfade_init IDATA_ATTR; |
@@ -187,9 +191,13 @@ void pcmbuf_set_position_callback(void (*callback)(size_t size)) | |||
187 | position_callback = callback; | 191 | position_callback = callback; |
188 | } | 192 | } |
189 | 193 | ||
190 | static void pcmbuf_set_watermark_bytes(size_t numbytes) | 194 | static void pcmbuf_set_watermark_bytes(void) |
191 | { | 195 | { |
192 | pcmbuf_watermark = numbytes; | 196 | pcmbuf_watermark = (crossfade_enabled && pcmbuf_size) ? |
197 | /* If crossfading, try to keep the buffer full other than 1 second */ | ||
198 | (pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)) : | ||
199 | /* Otherwise, just keep it above 2 second */ | ||
200 | PCMBUF_WATERMARK; | ||
193 | } | 201 | } |
194 | 202 | ||
195 | /* This is really just part of pcmbuf_flush_fillpos, but is easier to keep | 203 | /* This is really just part of pcmbuf_flush_fillpos, but is easier to keep |
@@ -413,30 +421,60 @@ static void pcmbuf_init_pcmbuffers(void) { | |||
413 | } | 421 | } |
414 | } | 422 | } |
415 | 423 | ||
416 | bool pcmbuf_is_same_size(size_t bufsize) | 424 | static size_t pcmbuf_get_next_required_pcmbuf_size(void) |
417 | { | 425 | { |
418 | /* keep calculations synced with pcmbuf_init */ | 426 | #if MEM > 1 |
419 | bufsize += PCMBUF_MIX_CHUNK * 2 + | 427 | size_t seconds = 1; |
420 | PCMBUF_DESCS(bufsize)*sizeof(struct pcmbufdesc); | 428 | |
421 | return bufsize == (size_t)(pcmbuf_bufend - audiobuffer); | 429 | if (crossfade_enabled_pending) |
430 | seconds += global_settings.crossfade_fade_out_delay | ||
431 | + global_settings.crossfade_fade_out_duration; | ||
432 | |||
433 | /* Buffer has to be at least 2s long. */ | ||
434 | seconds += 2; | ||
435 | logf("pcmbuf len: %ld", seconds); | ||
436 | return seconds * (NATIVE_FREQUENCY*4); | ||
437 | #else | ||
438 | return NATIVE_FREQUENCY*2; | ||
439 | #endif | ||
440 | } | ||
441 | |||
442 | static char *pcmbuf_calc_audiobuffer_ptr(size_t bufsize) | ||
443 | { | ||
444 | return pcmbuf_bufend - (bufsize + PCMBUF_MIX_CHUNK * 2 + | ||
445 | PCMBUF_DESCS_SIZE(bufsize)); | ||
446 | } | ||
447 | |||
448 | bool pcmbuf_is_same_size(void) | ||
449 | { | ||
450 | if (audiobuffer == NULL) | ||
451 | return true; /* Not set up yet even once so always */ | ||
452 | |||
453 | size_t bufsize = pcmbuf_get_next_required_pcmbuf_size(); | ||
454 | return pcmbuf_calc_audiobuffer_ptr(bufsize) == audiobuffer; | ||
422 | } | 455 | } |
423 | 456 | ||
424 | /* Initialize the pcmbuffer the structure looks like this: | 457 | /* Initialize the pcmbuffer the structure looks like this: |
425 | * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ | 458 | * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ |
426 | size_t pcmbuf_init(size_t bufsize, char *bufend) | 459 | size_t pcmbuf_init(unsigned char *bufend) |
427 | { | 460 | { |
428 | pcmbuf_size = bufsize; | ||
429 | pcmbuf_bufend = bufend; | 461 | pcmbuf_bufend = bufend; |
430 | pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc); | 462 | pcmbuf_size = pcmbuf_get_next_required_pcmbuf_size(); |
431 | audiobuffer = pcmbuf_bufend - (pcmbuf_size + PCMBUF_MIX_CHUNK * 2 | 463 | audiobuffer = pcmbuf_calc_audiobuffer_ptr(pcmbuf_size); |
432 | + pcmbuf_descsize); | ||
433 | fadebuf = &audiobuffer[pcmbuf_size]; | 464 | fadebuf = &audiobuffer[pcmbuf_size]; |
434 | voicebuf = &fadebuf[PCMBUF_MIX_CHUNK]; | 465 | voicebuf = &fadebuf[PCMBUF_MIX_CHUNK]; |
435 | pcmbuf_write = (struct pcmbufdesc *)&voicebuf[PCMBUF_MIX_CHUNK]; | 466 | pcmbuf_write = (struct pcmbufdesc *)&voicebuf[PCMBUF_MIX_CHUNK]; |
467 | |||
468 | pcmbuf_descsize = PCMBUF_DESCS_SIZE(pcmbuf_size); | ||
436 | pcmbuf_init_pcmbuffers(); | 469 | pcmbuf_init_pcmbuffers(); |
470 | |||
437 | position_callback = NULL; | 471 | position_callback = NULL; |
438 | pcmbuf_event_handler = NULL; | 472 | pcmbuf_event_handler = NULL; |
473 | |||
474 | pcmbuf_crossfade_enable_finished(); | ||
475 | |||
439 | pcmbuf_play_stop(); | 476 | pcmbuf_play_stop(); |
477 | |||
440 | return pcmbuf_bufend - audiobuffer; | 478 | return pcmbuf_bufend - audiobuffer; |
441 | } | 479 | } |
442 | 480 | ||
@@ -445,6 +483,14 @@ size_t pcmbuf_get_bufsize(void) | |||
445 | return pcmbuf_size; | 483 | return pcmbuf_size; |
446 | } | 484 | } |
447 | 485 | ||
486 | #ifdef ROCKBOX_HAS_LOGF | ||
487 | unsigned char * pcmbuf_get_meminfo(size_t *length) | ||
488 | { | ||
489 | *length = pcmbuf_bufend - audiobuffer; | ||
490 | return audiobuffer; | ||
491 | } | ||
492 | #endif | ||
493 | |||
448 | void pcmbuf_pause(bool pause) { | 494 | void pcmbuf_pause(bool pause) { |
449 | #ifdef PCMBUF_MUTING | 495 | #ifdef PCMBUF_MUTING |
450 | if (pause) | 496 | if (pause) |
@@ -1036,15 +1082,18 @@ void pcmbuf_mix_voice(int count) | |||
1036 | 1082 | ||
1037 | void pcmbuf_crossfade_enable(bool on_off) | 1083 | void pcmbuf_crossfade_enable(bool on_off) |
1038 | { | 1084 | { |
1039 | crossfade_enabled = on_off; | 1085 | #if MEM > 1 |
1086 | /* Next setting to be used, not applied now */ | ||
1087 | crossfade_enabled_pending = on_off; | ||
1088 | #endif | ||
1089 | (void)on_off; | ||
1090 | } | ||
1040 | 1091 | ||
1041 | if (crossfade_enabled) { | 1092 | void pcmbuf_crossfade_enable_finished(void) |
1042 | /* If crossfading, try to keep the buffer full other than 1 second */ | 1093 | { |
1043 | pcmbuf_set_watermark_bytes(pcmbuf_size - (NATIVE_FREQUENCY * 4 * 1)); | 1094 | /* Copy the pending setting over now */ |
1044 | } else { | 1095 | crossfade_enabled = crossfade_enabled_pending; |
1045 | /* Otherwise, just keep it above 2 second */ | 1096 | pcmbuf_set_watermark_bytes(); |
1046 | pcmbuf_set_watermark_bytes(PCMBUF_WATERMARK); | ||
1047 | } | ||
1048 | } | 1097 | } |
1049 | 1098 | ||
1050 | bool pcmbuf_is_crossfade_enabled(void) | 1099 | bool pcmbuf_is_crossfade_enabled(void) |
@@ -1054,4 +1103,3 @@ bool pcmbuf_is_crossfade_enabled(void) | |||
1054 | 1103 | ||
1055 | return crossfade_enabled; | 1104 | return crossfade_enabled; |
1056 | } | 1105 | } |
1057 | |||
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index 5c35ecc291..bddc7bb93d 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h | |||
@@ -37,10 +37,14 @@ | |||
37 | for mixing (crossfade or voice) */ | 37 | for mixing (crossfade or voice) */ |
38 | 38 | ||
39 | /* Returns true if the buffer needs to change size */ | 39 | /* Returns true if the buffer needs to change size */ |
40 | bool pcmbuf_is_same_size(size_t bufsize); | 40 | bool pcmbuf_is_same_size(void); |
41 | size_t pcmbuf_init(size_t bufsize, char *bufend); | 41 | size_t pcmbuf_init(unsigned char *bufend); |
42 | /* Size in bytes used by the pcmbuffer */ | 42 | /* Size in bytes used by the pcmbuffer */ |
43 | size_t pcmbuf_get_bufsize(void); | 43 | size_t pcmbuf_get_bufsize(void); |
44 | #ifdef ROCKBOX_HAS_LOGF | ||
45 | /* just used for logging for now */ | ||
46 | unsigned char * pcmbuf_get_meminfo(size_t *length); | ||
47 | #endif | ||
44 | size_t get_pcmbuf_descsize(void); | 48 | size_t get_pcmbuf_descsize(void); |
45 | 49 | ||
46 | void pcmbuf_pause(bool pause); | 50 | void pcmbuf_pause(bool pause); |
@@ -68,7 +72,7 @@ void* pcmbuf_request_buffer(int *count); | |||
68 | void* pcmbuf_request_voice_buffer(int *count, bool mix); | 72 | void* pcmbuf_request_voice_buffer(int *count, bool mix); |
69 | bool pcmbuf_is_crossfade_enabled(void); | 73 | bool pcmbuf_is_crossfade_enabled(void); |
70 | void pcmbuf_crossfade_enable(bool on_off); | 74 | void pcmbuf_crossfade_enable(bool on_off); |
71 | 75 | void pcmbuf_crossfade_enable_finished(void); | |
72 | int pcmbuf_usage(void); | 76 | int pcmbuf_usage(void); |
73 | int pcmbuf_mix_free(void); | 77 | int pcmbuf_mix_free(void); |
74 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); | 78 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); |
diff --git a/apps/playback.c b/apps/playback.c index 9676a107d0..ef525a5776 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include "system.h" | 34 | #include "system.h" |
35 | #include "thread.h" | 35 | #include "thread.h" |
36 | #include "file.h" | 36 | #include "file.h" |
37 | #include "panic.h" | ||
38 | #include "memory.h" | ||
37 | #include "lcd.h" | 39 | #include "lcd.h" |
38 | #include "font.h" | 40 | #include "font.h" |
39 | #include "button.h" | 41 | #include "button.h" |
@@ -167,14 +169,14 @@ enum { | |||
167 | 169 | ||
168 | /* As defined in plugin.lds */ | 170 | /* As defined in plugin.lds */ |
169 | #if defined(CPU_PP) | 171 | #if defined(CPU_PP) |
170 | #define CODEC_IRAM_ORIGIN 0x4000c000 | 172 | #define CODEC_IRAM_ORIGIN ((unsigned char *)0x4000c000) |
171 | #define CODEC_IRAM_SIZE 0xc000 | 173 | #define CODEC_IRAM_SIZE ((size_t)0xc000) |
172 | #elif defined(IAUDIO_X5) || defined(IAUDIO_M5) | 174 | #elif defined(IAUDIO_X5) || defined(IAUDIO_M5) |
173 | #define CODEC_IRAM_ORIGIN 0x10010000 | 175 | #define CODEC_IRAM_ORIGIN ((unsigned char *)0x10010000) |
174 | #define CODEC_IRAM_SIZE 0x10000 | 176 | #define CODEC_IRAM_SIZE ((size_t)0x10000) |
175 | #else | 177 | #else |
176 | #define CODEC_IRAM_ORIGIN 0x1000c000 | 178 | #define CODEC_IRAM_ORIGIN ((unsigned char *)0x1000c000) |
177 | #define CODEC_IRAM_SIZE 0xc000 | 179 | #define CODEC_IRAM_SIZE ((size_t)0xc000) |
178 | #endif | 180 | #endif |
179 | 181 | ||
180 | #ifndef IBSS_ATTR_VOICE_STACK | 182 | #ifndef IBSS_ATTR_VOICE_STACK |
@@ -205,7 +207,7 @@ static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) | |||
205 | 207 | ||
206 | /* Possible arrangements of the buffer */ | 208 | /* Possible arrangements of the buffer */ |
207 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ | 209 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ |
208 | #define BUFFER_STATE_NORMAL 0 /* voice+audio OR audio-only */ | 210 | #define BUFFER_STATE_INITIALIZED 0 /* voice+audio OR audio-only */ |
209 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ | 211 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ |
210 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ | 212 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ |
211 | 213 | ||
@@ -282,7 +284,7 @@ static const char audio_thread_name[] = "audio"; | |||
282 | static void audio_thread(void); | 284 | static void audio_thread(void); |
283 | static void audio_initiate_track_change(long direction); | 285 | static void audio_initiate_track_change(long direction); |
284 | static bool audio_have_tracks(void); | 286 | static bool audio_have_tracks(void); |
285 | static void audio_reset_buffer(size_t pcmbufsize); | 287 | static void audio_reset_buffer(void); |
286 | 288 | ||
287 | /* Codec thread */ | 289 | /* Codec thread */ |
288 | extern struct codec_api ci; | 290 | extern struct codec_api ci; |
@@ -315,10 +317,16 @@ static unsigned char sim_iram[CODEC_IRAM_SIZE]; | |||
315 | #define CODEC_IRAM_ORIGIN sim_iram | 317 | #define CODEC_IRAM_ORIGIN sim_iram |
316 | #endif | 318 | #endif |
317 | 319 | ||
318 | /* Pointer to IRAM buffers for normal/voice codecs */ | 320 | /* iram_buf and dram_buf are either both NULL or both non-NULL */ |
319 | static unsigned char *iram_buf[2] = { NULL, NULL }; | 321 | /* Pointer to IRAM buffer for codec swapping */ |
320 | /* Pointer to DRAM buffers for normal/voice codecs */ | 322 | static unsigned char *iram_buf = NULL; |
321 | static unsigned char *dram_buf[2] = { NULL, NULL }; | 323 | /* Pointer to DRAM buffer for codec swapping */ |
324 | static unsigned char *dram_buf = NULL; | ||
325 | /* Parity of swap_codec calls - needed because one codec swapping itself in | ||
326 | automatically swaps in the other and the swap when unlocking should not | ||
327 | happen if the parity is even. | ||
328 | */ | ||
329 | static bool swap_codec_parity = false; /* true=odd, false=even */ | ||
322 | /* Mutex to control which codec (normal/voice) is running */ | 330 | /* Mutex to control which codec (normal/voice) is running */ |
323 | static struct mutex mutex_codecthread NOCACHEBSS_ATTR; | 331 | static struct mutex mutex_codecthread NOCACHEBSS_ATTR; |
324 | 332 | ||
@@ -342,6 +350,7 @@ struct voice_info { | |||
342 | char *buf; | 350 | char *buf; |
343 | }; | 351 | }; |
344 | static void voice_thread(void); | 352 | static void voice_thread(void); |
353 | static void voice_stop(void); | ||
345 | 354 | ||
346 | #endif /* PLAYBACK_VOICE */ | 355 | #endif /* PLAYBACK_VOICE */ |
347 | 356 | ||
@@ -404,7 +413,7 @@ void mpeg_id3_options(bool _v1first) | |||
404 | static void wait_for_voice_swap_in(void) | 413 | static void wait_for_voice_swap_in(void) |
405 | { | 414 | { |
406 | #ifdef PLAYBACK_VOICE | 415 | #ifdef PLAYBACK_VOICE |
407 | if (NULL == iram_buf[CODEC_IDX_VOICE]) | 416 | if (NULL == iram_buf) |
408 | return; | 417 | return; |
409 | 418 | ||
410 | while (current_codec != CODEC_IDX_VOICE) | 419 | while (current_codec != CODEC_IDX_VOICE) |
@@ -426,7 +435,8 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | |||
426 | 435 | ||
427 | if (buffer_size == NULL) | 436 | if (buffer_size == NULL) |
428 | { | 437 | { |
429 | /* Special case for talk_init to use */ | 438 | /* Special case for talk_init to use since it already knows it's |
439 | trashed */ | ||
430 | buffer_state = BUFFER_STATE_TRASHED; | 440 | buffer_state = BUFFER_STATE_TRASHED; |
431 | return NULL; | 441 | return NULL; |
432 | } | 442 | } |
@@ -434,8 +444,15 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | |||
434 | if (talk_buf || buffer_state == BUFFER_STATE_TRASHED | 444 | if (talk_buf || buffer_state == BUFFER_STATE_TRASHED |
435 | || !talk_voice_required()) | 445 | || !talk_voice_required()) |
436 | { | 446 | { |
437 | logf("get buffer: talk_buf"); | 447 | logf("get buffer: talk, audio"); |
438 | /* ok to use everything from audiobuf to audiobufend */ | 448 | /* Ok to use everything from audiobuf to audiobufend - voice is loaded, |
449 | the talk buffer is not needed because voice isn't being used, or | ||
450 | could be BUFFER_STATE_TRASHED already. If state is | ||
451 | BUFFER_STATE_VOICED_ONLY, no problem as long as memory isn't written | ||
452 | without the caller knowing what's going on. Changing certain settings | ||
453 | may move it to a worse condition but the memory in use by something | ||
454 | else will remain undisturbed. | ||
455 | */ | ||
439 | if (buffer_state != BUFFER_STATE_TRASHED) | 456 | if (buffer_state != BUFFER_STATE_TRASHED) |
440 | { | 457 | { |
441 | talk_buffer_steal(); | 458 | talk_buffer_steal(); |
@@ -447,10 +464,14 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | |||
447 | } | 464 | } |
448 | else | 465 | else |
449 | { | 466 | { |
450 | /* skip talk buffer and move pcm buffer to end */ | 467 | /* Safe to just return this if already BUFFER_STATE_VOICED_ONLY or |
451 | logf("get buffer: voice"); | 468 | still BUFFER_STATE_INITIALIZED */ |
469 | /* Skip talk buffer and move pcm buffer to end to maximize available | ||
470 | contiguous memory - no audio running means voice will not need the | ||
471 | swap space */ | ||
472 | logf("get buffer: audio"); | ||
452 | buf = audiobuf + talk_get_bufsize(); | 473 | buf = audiobuf + talk_get_bufsize(); |
453 | end = audiobufend - pcmbuf_init(pcmbuf_get_bufsize(), audiobufend); | 474 | end = audiobufend - pcmbuf_init(audiobufend); |
454 | buffer_state = BUFFER_STATE_VOICED_ONLY; | 475 | buffer_state = BUFFER_STATE_VOICED_ONLY; |
455 | } | 476 | } |
456 | 477 | ||
@@ -466,18 +487,22 @@ void audio_iram_steal(void) | |||
466 | audio_stop(); | 487 | audio_stop(); |
467 | 488 | ||
468 | #ifdef PLAYBACK_VOICE | 489 | #ifdef PLAYBACK_VOICE |
469 | if (NULL != iram_buf[CODEC_IDX_VOICE]) | 490 | if (NULL != iram_buf) |
470 | { | 491 | { |
471 | /* Can't already be stolen */ | 492 | /* Can't already be stolen */ |
472 | if (voice_iram_stolen) | 493 | if (voice_iram_stolen) |
473 | return; | 494 | return; |
474 | 495 | ||
496 | /* Must wait for voice to be current again if it is swapped which | ||
497 | would cause the caller's buffer to get clobbered when voice locks | ||
498 | and runs - we'll wait for it to lock and yield again then make sure | ||
499 | the ride has come to a complete stop */ | ||
475 | wait_for_voice_swap_in(); | 500 | wait_for_voice_swap_in(); |
476 | voice_stop(); | 501 | voice_stop(); |
477 | 502 | ||
478 | /* Save voice IRAM - safe to do here since state is known */ | 503 | /* Save voice IRAM but just memcpy - safe to do here since voice |
479 | memcpy(iram_buf[CODEC_IDX_VOICE], (void *)CODEC_IRAM_ORIGIN, | 504 | is current and no audio codec is loaded */ |
480 | CODEC_IRAM_SIZE); | 505 | memcpy(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE); |
481 | voice_iram_stolen = true; | 506 | voice_iram_stolen = true; |
482 | } | 507 | } |
483 | else | 508 | else |
@@ -492,21 +517,23 @@ void audio_iram_steal(void) | |||
492 | #ifdef HAVE_RECORDING | 517 | #ifdef HAVE_RECORDING |
493 | unsigned char *audio_get_recording_buffer(size_t *buffer_size) | 518 | unsigned char *audio_get_recording_buffer(size_t *buffer_size) |
494 | { | 519 | { |
495 | /* don't allow overwrite of voice swap area or we'll trash the | 520 | /* Don't allow overwrite of voice swap area or we'll trash the |
496 | swapped-out voice codec but can use whole thing if none */ | 521 | swapped-out voice codec but can use whole thing if none */ |
497 | unsigned char *end; | 522 | unsigned char *end; |
498 | 523 | ||
524 | /* Stop audio and voice. Wait for voice to swap in and be clear | ||
525 | of pending events to ensure trouble-free operation of encoders */ | ||
499 | audio_stop(); | 526 | audio_stop(); |
500 | wait_for_voice_swap_in(); | 527 | wait_for_voice_swap_in(); |
501 | voice_stop(); | 528 | voice_stop(); |
502 | talk_buffer_steal(); | 529 | talk_buffer_steal(); |
503 | 530 | ||
504 | #ifdef PLAYBACK_VOICE | 531 | #ifdef PLAYBACK_VOICE |
505 | #ifdef IRAM_STEAL | 532 | /* If no dram_buf, swap space not used and recording gets more |
506 | end = dram_buf[CODEC_IDX_VOICE]; | 533 | memory. Codec swap areas will remain unaffected by the next init |
507 | #else | 534 | since they're allocated at the end of the buffer and their sizes |
508 | end = iram_buf[CODEC_IDX_VOICE]; | 535 | don't change between calls */ |
509 | #endif /* IRAM_STEAL */ | 536 | end = dram_buf; |
510 | if (NULL == end) | 537 | if (NULL == end) |
511 | #endif /* PLAYBACK_VOICE */ | 538 | #endif /* PLAYBACK_VOICE */ |
512 | end = audiobufend; | 539 | end = audiobufend; |
@@ -781,101 +808,51 @@ void audio_set_buffer_margin(int setting) | |||
781 | { | 808 | { |
782 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | 809 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; |
783 | buffer_margin = lookup[setting]; | 810 | buffer_margin = lookup[setting]; |
784 | logf("buffer margin: %ds", buffer_margin); | 811 | logf("buffer margin: %ld", buffer_margin); |
785 | set_filebuf_watermark(buffer_margin); | 812 | set_filebuf_watermark(buffer_margin); |
786 | } | 813 | } |
787 | 814 | ||
788 | /* Set crossfade & PCM buffer length. */ | 815 | /* Take nescessary steps to enable or disable the crossfade setting */ |
789 | void audio_set_crossfade(int enable) | 816 | void audio_set_crossfade(int enable) |
790 | { | 817 | { |
818 | size_t offset; | ||
819 | bool was_playing; | ||
791 | size_t size; | 820 | size_t size; |
792 | bool was_playing = (playing && audio_is_initialized); | ||
793 | size_t offset = 0; | ||
794 | #if MEM > 1 | ||
795 | int seconds = 1; | ||
796 | #endif | ||
797 | 821 | ||
798 | if (!filebuf) | 822 | /* Tell it the next setting to use */ |
799 | return; /* Audio buffers not yet set up */ | 823 | pcmbuf_crossfade_enable(enable); |
800 | 824 | ||
801 | #if MEM > 1 | 825 | /* Return if size hasn't changed or this is too early to determine |
802 | if (enable) | 826 | which in the second case there's no way we could be playing |
803 | seconds = global_settings.crossfade_fade_out_delay | 827 | anything at all */ |
804 | + global_settings.crossfade_fade_out_duration; | 828 | if (pcmbuf_is_same_size()) |
805 | 829 | { | |
806 | /* Buffer has to be at least 2s long. */ | 830 | /* This function is a copout and just syncs some variables - |
807 | seconds += 2; | 831 | to be removed at a later date */ |
808 | logf("buf len: %d", seconds); | 832 | pcmbuf_crossfade_enable_finished(); |
809 | size = seconds * (NATIVE_FREQUENCY*4); | 833 | return; |
810 | #else | 834 | } |
811 | enable = 0; | ||
812 | size = NATIVE_FREQUENCY*2; | ||
813 | #endif | ||
814 | if (buffer_state == BUFFER_STATE_NORMAL && pcmbuf_is_same_size(size)) | ||
815 | return ; | ||
816 | 835 | ||
836 | offset = 0; | ||
837 | was_playing = playing; | ||
838 | |||
839 | /* Playback has to be stopped before changing the buffer size */ | ||
817 | if (was_playing) | 840 | if (was_playing) |
818 | { | 841 | { |
819 | /* Store the track resume position */ | 842 | /* Store the track resume position */ |
820 | offset = CUR_TI->id3.offset; | 843 | offset = CUR_TI->id3.offset; |
821 | 844 | gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK)); | |
822 | /* Playback has to be stopped before changing the buffer size. */ | ||
823 | gui_syncsplash(0, (char *)str(LANG_RESTARTING_PLAYBACK)); | ||
824 | audio_stop(); | ||
825 | } | 845 | } |
826 | 846 | ||
827 | voice_stop(); | 847 | /* Blast it - audio buffer will have to be setup again next time |
828 | 848 | something plays */ | |
829 | /* Re-initialize audio system. */ | 849 | audio_get_buffer(true, &size); |
830 | audio_reset_buffer(size); | ||
831 | pcmbuf_crossfade_enable(enable); | ||
832 | logf("abuf:%dB", pcmbuf_get_bufsize()); | ||
833 | logf("fbuf:%dB", filebuflen); | ||
834 | |||
835 | voice_init(); | ||
836 | 850 | ||
837 | /* Restart playback. */ | 851 | /* Restart playback if audio was running previously */ |
838 | if (was_playing) | 852 | if (was_playing) |
839 | audio_play(offset); | 853 | audio_play(offset); |
840 | } | 854 | } |
841 | 855 | ||
842 | void voice_init(void) | ||
843 | { | ||
844 | #ifdef PLAYBACK_VOICE | ||
845 | if (voice_thread_p || !filebuf || voice_codec_loaded || | ||
846 | !talk_voice_required()) | ||
847 | return; | ||
848 | |||
849 | logf("Starting voice codec"); | ||
850 | queue_init(&voice_queue, true); | ||
851 | voice_thread_p = create_thread(voice_thread, voice_stack, | ||
852 | sizeof(voice_stack), voice_thread_name | ||
853 | IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU, false)); | ||
854 | |||
855 | while (!voice_codec_loaded) | ||
856 | yield(); | ||
857 | #endif | ||
858 | } /* voice_init */ | ||
859 | |||
860 | void voice_stop(void) | ||
861 | { | ||
862 | #ifdef PLAYBACK_VOICE | ||
863 | /* Messages should not be posted to voice codec queue unless it is the | ||
864 | current codec or deadlocks happen. */ | ||
865 | if (current_codec != CODEC_IDX_VOICE) | ||
866 | return; | ||
867 | |||
868 | LOGFQUEUE("mp3 > voice Q_VOICE_STOP"); | ||
869 | queue_post(&voice_queue, Q_VOICE_STOP, 0); | ||
870 | while (voice_is_playing || !queue_empty(&voice_queue)) | ||
871 | yield(); | ||
872 | if (!playing) | ||
873 | pcmbuf_play_stop(); | ||
874 | #endif | ||
875 | } /* voice_stop */ | ||
876 | |||
877 | |||
878 | |||
879 | /* --- Routines called from multiple threads --- */ | 856 | /* --- Routines called from multiple threads --- */ |
880 | static void set_current_codec(int codec_idx) | 857 | static void set_current_codec(int codec_idx) |
881 | { | 858 | { |
@@ -886,35 +863,61 @@ static void set_current_codec(int codec_idx) | |||
886 | #ifdef PLAYBACK_VOICE | 863 | #ifdef PLAYBACK_VOICE |
887 | static void swap_codec(void) | 864 | static void swap_codec(void) |
888 | { | 865 | { |
889 | int my_codec = current_codec; | 866 | int my_codec; |
890 | 867 | ||
891 | logf("swapping out codec:%d", my_codec); | 868 | /* Swap nothing if no swap buffers exist */ |
892 | 869 | if (dram_buf == NULL) | |
893 | /* Save our current IRAM and DRAM */ | ||
894 | #ifdef IRAM_STEAL | ||
895 | if (voice_iram_stolen) | ||
896 | { | 870 | { |
897 | logf("swap: iram restore"); | 871 | logf("swap: no swap buffers"); |
898 | voice_iram_stolen = false; | 872 | return; |
899 | /* Don't swap trashed data into buffer - _should_ always be the case | ||
900 | if voice_iram_stolen is true since the voice has been swapped in | ||
901 | before hand */ | ||
902 | if (my_codec == CODEC_IDX_VOICE) | ||
903 | goto skip_iram_swap; | ||
904 | } | 873 | } |
874 | |||
875 | my_codec = current_codec; | ||
876 | |||
877 | logf("swapping out codec: %d", my_codec); | ||
878 | |||
879 | /* Invert this when a codec thread enters and leaves */ | ||
880 | swap_codec_parity = !swap_codec_parity; | ||
881 | |||
882 | /* If this is true, an odd number of calls has occurred and there's | ||
883 | no codec thread waiting to swap us out when it locks and runs. This | ||
884 | occurs when playback is stopped or when just starting playback and | ||
885 | the audio thread is loading a codec; parities should always be even | ||
886 | on entry when a thread calls this during playback */ | ||
887 | if (swap_codec_parity) | ||
888 | { | ||
889 | /* Save our current IRAM and DRAM */ | ||
890 | #ifdef IRAM_STEAL | ||
891 | if (voice_iram_stolen) | ||
892 | { | ||
893 | logf("swap: iram restore"); | ||
894 | voice_iram_stolen = false; | ||
895 | /* Don't swap trashed data into buffer as the voice IRAM will | ||
896 | already be swapped out - should _always_ be the case if | ||
897 | voice_iram_stolen is true since the voice has been swapped | ||
898 | in beforehand */ | ||
899 | if (my_codec == CODEC_IDX_VOICE) | ||
900 | { | ||
901 | logf("voice iram already swapped"); | ||
902 | goto skip_iram_swap; | ||
903 | } | ||
904 | } | ||
905 | #endif | 905 | #endif |
906 | 906 | ||
907 | memcpy(iram_buf[my_codec], (unsigned char *)CODEC_IRAM_ORIGIN, | 907 | memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE); |
908 | CODEC_IRAM_SIZE); | ||
909 | 908 | ||
910 | #ifdef IRAM_STEAL | 909 | #ifdef IRAM_STEAL |
911 | skip_iram_swap: | 910 | skip_iram_swap: |
912 | #endif | 911 | #endif |
913 | 912 | ||
914 | memcpy(dram_buf[my_codec], codecbuf, CODEC_SIZE); | 913 | memswap128(dram_buf, codecbuf, CODEC_SIZE); |
914 | /* No cache invalidation needed; it will be done in codec_load_ram | ||
915 | or we won't be here otherwise */ | ||
916 | } | ||
915 | 917 | ||
916 | /* Release my semaphore */ | 918 | /* Release my semaphore */ |
917 | mutex_unlock(&mutex_codecthread); | 919 | mutex_unlock(&mutex_codecthread); |
920 | logf("unlocked: %d", my_codec); | ||
918 | 921 | ||
919 | /* Loop until the other codec has locked and run */ | 922 | /* Loop until the other codec has locked and run */ |
920 | do { | 923 | do { |
@@ -923,28 +926,57 @@ skip_iram_swap: | |||
923 | } while (my_codec == current_codec); | 926 | } while (my_codec == current_codec); |
924 | 927 | ||
925 | /* Wait for other codec to unlock */ | 928 | /* Wait for other codec to unlock */ |
929 | /* FIXME: We need some sort of timed boost cancellation here or the CPU | ||
930 | doesn't unboost during playback when the voice codec goes back to | ||
931 | waiting - recall that mutex_lock calls block_thread which is an | ||
932 | indefinite wait that doesn't cancel the thread's CPU boost */ | ||
926 | mutex_lock(&mutex_codecthread); | 933 | mutex_lock(&mutex_codecthread); |
927 | 934 | ||
928 | /* Take control */ | 935 | /* Take control */ |
936 | logf("waiting for lock: %d", my_codec); | ||
929 | set_current_codec(my_codec); | 937 | set_current_codec(my_codec); |
930 | 938 | ||
931 | /* Reload our IRAM and DRAM */ | 939 | /* Reload our IRAM and DRAM */ |
932 | memcpy((unsigned char *)CODEC_IRAM_ORIGIN, iram_buf[my_codec], | 940 | memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE); |
933 | CODEC_IRAM_SIZE); | 941 | memswap128(dram_buf, codecbuf, CODEC_SIZE); |
934 | invalidate_icache(); | 942 | invalidate_icache(); |
935 | memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE); | ||
936 | 943 | ||
937 | logf("resuming codec:%d", my_codec); | 944 | /* Flip parity again */ |
945 | swap_codec_parity = !swap_codec_parity; | ||
946 | |||
947 | logf("resuming codec: %d", my_codec); | ||
938 | } | 948 | } |
949 | |||
950 | /* This function is meant to be used by the buffer stealing functions to | ||
951 | ensure the codec is no longer active and so voice will be swapped-in | ||
952 | before it is called */ | ||
953 | static void voice_stop(void) | ||
954 | { | ||
955 | #ifdef PLAYBACK_VOICE | ||
956 | /* Must have a voice codec loaded or we'll hang forever here */ | ||
957 | if (!voice_codec_loaded) | ||
958 | return; | ||
959 | |||
960 | LOGFQUEUE("mp3 > voice Q_VOICE_STOP"); | ||
961 | queue_post(&voice_queue, Q_VOICE_STOP, 0); | ||
962 | |||
963 | /* Loop until voice empties it's queue, stops and picks up on the new | ||
964 | track; the voice thread must be stopped and waiting for messages | ||
965 | outside the codec */ | ||
966 | while (voice_is_playing || !queue_empty(&voice_queue) || | ||
967 | ci_voice.new_track) | ||
968 | yield(); | ||
969 | |||
970 | if (!playing) | ||
971 | pcmbuf_play_stop(); | ||
939 | #endif | 972 | #endif |
973 | } /* voice_stop */ | ||
974 | #endif /* PLAYBACK_VOICE */ | ||
940 | 975 | ||
941 | static void set_filebuf_watermark(int seconds) | 976 | static void set_filebuf_watermark(int seconds) |
942 | { | 977 | { |
943 | size_t bytes; | 978 | size_t bytes; |
944 | 979 | ||
945 | if (current_codec == CODEC_IDX_VOICE) | ||
946 | return; | ||
947 | |||
948 | if (!filebuf) | 980 | if (!filebuf) |
949 | return; /* Audio buffers not yet set up */ | 981 | return; /* Audio buffers not yet set up */ |
950 | 982 | ||
@@ -1058,6 +1090,14 @@ static void voice_set_offset_callback(size_t value) | |||
1058 | (void)value; | 1090 | (void)value; |
1059 | } | 1091 | } |
1060 | 1092 | ||
1093 | static void voice_configure_callback(int setting, intptr_t value) | ||
1094 | { | ||
1095 | if (!dsp_configure(setting, value)) | ||
1096 | { | ||
1097 | logf("Illegal key:%d", setting); | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1061 | static size_t voice_filebuf_callback(void *ptr, size_t size) | 1101 | static size_t voice_filebuf_callback(void *ptr, size_t size) |
1062 | { | 1102 | { |
1063 | (void)ptr; | 1103 | (void)ptr; |
@@ -1066,6 +1106,34 @@ static size_t voice_filebuf_callback(void *ptr, size_t size) | |||
1066 | return 0; | 1106 | return 0; |
1067 | } | 1107 | } |
1068 | 1108 | ||
1109 | /* Handle Q_VOICE_STOP and part of SYS_USB_CONNECTED */ | ||
1110 | static bool voice_on_voice_stop(bool aborting, size_t *realsize) | ||
1111 | { | ||
1112 | if (aborting && !playing && pcm_is_playing()) | ||
1113 | { | ||
1114 | /* Aborting: Slight hack - flush PCM buffer if | ||
1115 | only being used for voice */ | ||
1116 | pcmbuf_play_stop(); | ||
1117 | } | ||
1118 | |||
1119 | if (voice_is_playing) | ||
1120 | { | ||
1121 | /* Clear the current buffer */ | ||
1122 | voice_is_playing = false; | ||
1123 | voice_getmore = NULL; | ||
1124 | voice_remaining = 0; | ||
1125 | voicebuf = NULL; | ||
1126 | |||
1127 | /* Force the codec to think it's changing tracks */ | ||
1128 | ci_voice.new_track = 1; | ||
1129 | |||
1130 | *realsize = 0; | ||
1131 | return true; /* Yes, change tracks */ | ||
1132 | } | ||
1133 | |||
1134 | return false; | ||
1135 | } | ||
1136 | |||
1069 | static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) | 1137 | static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) |
1070 | { | 1138 | { |
1071 | struct event ev; | 1139 | struct event ev; |
@@ -1079,15 +1147,21 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1079 | while (1) | 1147 | while (1) |
1080 | { | 1148 | { |
1081 | if (voice_is_playing || playing) | 1149 | if (voice_is_playing || playing) |
1150 | { | ||
1082 | queue_wait_w_tmo(&voice_queue, &ev, 0); | 1151 | queue_wait_w_tmo(&voice_queue, &ev, 0); |
1152 | if (!voice_is_playing && ev.id == SYS_TIMEOUT) | ||
1153 | ev.id = Q_AUDIO_PLAY; | ||
1154 | } | ||
1083 | else | 1155 | else |
1156 | { | ||
1084 | /* We must use queue_wait_w_tmo() because queue_wait() doesn't | 1157 | /* We must use queue_wait_w_tmo() because queue_wait() doesn't |
1085 | unboost the CPU */ | 1158 | unboost the CPU */ |
1086 | queue_wait_w_tmo(&voice_queue, &ev, INT_MAX); | 1159 | /* FIXME: when long timeouts work correctly max out the the timeout |
1087 | if (!voice_is_playing) | 1160 | (we'll still need the timeout guard here) or an infinite timeout |
1088 | { | 1161 | can unboost, use that */ |
1089 | if (ev.id == SYS_TIMEOUT) | 1162 | do |
1090 | ev.id = Q_AUDIO_PLAY; | 1163 | queue_wait_w_tmo(&voice_queue, &ev, HZ*5); |
1164 | while (ev.id == SYS_TIMEOUT); /* Fake infinite wait */ | ||
1091 | } | 1165 | } |
1092 | 1166 | ||
1093 | switch (ev.id) { | 1167 | switch (ev.id) { |
@@ -1110,35 +1184,27 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1110 | 1184 | ||
1111 | case Q_VOICE_STOP: | 1185 | case Q_VOICE_STOP: |
1112 | LOGFQUEUE("voice < Q_VOICE_STOP"); | 1186 | LOGFQUEUE("voice < Q_VOICE_STOP"); |
1113 | if (ev.data == 1 && !playing && pcm_is_playing()) | 1187 | if (voice_on_voice_stop(ev.data, realsize)) |
1114 | { | ||
1115 | /* Aborting: Slight hack - flush PCM buffer if | ||
1116 | only being used for voice */ | ||
1117 | pcmbuf_play_stop(); | ||
1118 | } | ||
1119 | if (voice_is_playing) | ||
1120 | { | ||
1121 | /* Clear the current buffer */ | ||
1122 | voice_is_playing = false; | ||
1123 | voice_getmore = NULL; | ||
1124 | voice_remaining = 0; | ||
1125 | voicebuf = NULL; | ||
1126 | |||
1127 | /* Force the codec to think it's changing tracks */ | ||
1128 | ci_voice.new_track = 1; | ||
1129 | *realsize = 0; | ||
1130 | return NULL; | 1188 | return NULL; |
1131 | } | 1189 | break; |
1132 | else | ||
1133 | break; | ||
1134 | 1190 | ||
1135 | case SYS_USB_CONNECTED: | 1191 | case SYS_USB_CONNECTED: |
1192 | { | ||
1136 | LOGFQUEUE("voice < SYS_USB_CONNECTED"); | 1193 | LOGFQUEUE("voice < SYS_USB_CONNECTED"); |
1137 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 1194 | bool change_tracks = voice_on_voice_stop(ev.data, realsize); |
1195 | /* Voice is obviously current so let us swap ourselves away if | ||
1196 | playing so audio may stop itself - audio_codec_loaded can | ||
1197 | only be true in this case if we're here even if the codec | ||
1198 | is only about to load */ | ||
1138 | if (audio_codec_loaded) | 1199 | if (audio_codec_loaded) |
1139 | swap_codec(); | 1200 | swap_codec(); |
1201 | /* Playback should be finished by now - ack and wait */ | ||
1202 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1140 | usb_wait_for_disconnect(&voice_queue); | 1203 | usb_wait_for_disconnect(&voice_queue); |
1204 | if (change_tracks) | ||
1205 | return NULL; | ||
1141 | break; | 1206 | break; |
1207 | } | ||
1142 | 1208 | ||
1143 | case Q_VOICE_PLAY: | 1209 | case Q_VOICE_PLAY: |
1144 | LOGFQUEUE("voice < Q_VOICE_PLAY"); | 1210 | LOGFQUEUE("voice < Q_VOICE_PLAY"); |
@@ -1149,17 +1215,17 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1149 | #ifdef IRAM_STEAL | 1215 | #ifdef IRAM_STEAL |
1150 | if (voice_iram_stolen) | 1216 | if (voice_iram_stolen) |
1151 | { | 1217 | { |
1218 | /* Voice is the first to run again and is currently | ||
1219 | loaded */ | ||
1152 | logf("voice: iram restore"); | 1220 | logf("voice: iram restore"); |
1153 | memcpy((void*)CODEC_IRAM_ORIGIN, | 1221 | memcpy(CODEC_IRAM_ORIGIN, iram_buf, CODEC_IRAM_SIZE); |
1154 | iram_buf[CODEC_IDX_VOICE], | ||
1155 | CODEC_IRAM_SIZE); | ||
1156 | voice_iram_stolen = false; | 1222 | voice_iram_stolen = false; |
1157 | } | 1223 | } |
1158 | #endif | 1224 | #endif |
1159 | /* must reset the buffer before any playback | 1225 | /* Must reset the buffer before any playback begins if |
1160 | begins if needed */ | 1226 | needed */ |
1161 | if (buffer_state == BUFFER_STATE_TRASHED) | 1227 | if (buffer_state == BUFFER_STATE_TRASHED) |
1162 | audio_reset_buffer(pcmbuf_get_bufsize()); | 1228 | audio_reset_buffer(); |
1163 | 1229 | ||
1164 | voice_is_playing = true; | 1230 | voice_is_playing = true; |
1165 | trigger_cpu_boost(); | 1231 | trigger_cpu_boost(); |
@@ -1168,7 +1234,7 @@ static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1168 | voicebuf = voice_data->buf; | 1234 | voicebuf = voice_data->buf; |
1169 | voice_getmore = voice_data->callback; | 1235 | voice_getmore = voice_data->callback; |
1170 | } | 1236 | } |
1171 | goto voice_play_clip; | 1237 | goto voice_play_clip; /* To exit both switch and while */ |
1172 | 1238 | ||
1173 | case SYS_TIMEOUT: | 1239 | case SYS_TIMEOUT: |
1174 | LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT"); | 1240 | LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT"); |
@@ -1254,11 +1320,14 @@ static void voice_thread(void) | |||
1254 | voice_remaining = 0; | 1320 | voice_remaining = 0; |
1255 | voice_getmore = NULL; | 1321 | voice_getmore = NULL; |
1256 | 1322 | ||
1323 | /* FIXME: If we being starting the voice thread without reboot, the | ||
1324 | voice_queue could be full of old stuff and we must flush it. */ | ||
1257 | codec_load_file(get_codec_filename(AFMT_MPA_L3), &ci_voice); | 1325 | codec_load_file(get_codec_filename(AFMT_MPA_L3), &ci_voice); |
1258 | 1326 | ||
1259 | logf("Voice codec finished"); | 1327 | logf("Voice codec finished"); |
1260 | voice_codec_loaded = false; | 1328 | voice_codec_loaded = false; |
1261 | mutex_unlock(&mutex_codecthread); | 1329 | mutex_unlock(&mutex_codecthread); |
1330 | voice_thread_p = NULL; | ||
1262 | remove_thread(NULL); | 1331 | remove_thread(NULL); |
1263 | } /* voice_thread */ | 1332 | } /* voice_thread */ |
1264 | 1333 | ||
@@ -1898,7 +1967,8 @@ static void codec_thread(void) | |||
1898 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); | 1967 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); |
1899 | audio_codec_loaded = true; | 1968 | audio_codec_loaded = true; |
1900 | #ifdef PLAYBACK_VOICE | 1969 | #ifdef PLAYBACK_VOICE |
1901 | /* Don't sent messages to voice codec if it's not current */ | 1970 | /* Don't sent messages to voice codec if it's already swapped |
1971 | out or it will never get this */ | ||
1902 | if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE) | 1972 | if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE) |
1903 | { | 1973 | { |
1904 | LOGFQUEUE("codec > voice Q_AUDIO_PLAY"); | 1974 | LOGFQUEUE("codec > voice Q_AUDIO_PLAY"); |
@@ -2346,7 +2416,7 @@ strip_ape_tag: | |||
2346 | /* Skip APE tag */ | 2416 | /* Skip APE tag */ |
2347 | if (FILEBUFUSED > len) | 2417 | if (FILEBUFUSED > len) |
2348 | { | 2418 | { |
2349 | logf("Skipping APE tag (%dB)", len); | 2419 | logf("Skipping APE tag (%ldB)", len); |
2350 | buf_widx = RINGBUF_SUB(buf_widx, len); | 2420 | buf_widx = RINGBUF_SUB(buf_widx, len); |
2351 | tracks[track_widx].available -= len; | 2421 | tracks[track_widx].available -= len; |
2352 | tracks[track_widx].filesize -= len; | 2422 | tracks[track_widx].filesize -= len; |
@@ -2388,7 +2458,7 @@ static bool audio_read_file(size_t minimum) | |||
2388 | 2458 | ||
2389 | if (rc < 0) | 2459 | if (rc < 0) |
2390 | { | 2460 | { |
2391 | logf("File ended %dB early", tracks[track_widx].filerem); | 2461 | logf("File ended %ldB early", tracks[track_widx].filerem); |
2392 | tracks[track_widx].filesize -= tracks[track_widx].filerem; | 2462 | tracks[track_widx].filesize -= tracks[track_widx].filerem; |
2393 | tracks[track_widx].filerem = 0; | 2463 | tracks[track_widx].filerem = 0; |
2394 | break; | 2464 | break; |
@@ -2408,7 +2478,7 @@ static bool audio_read_file(size_t minimum) | |||
2408 | 2478 | ||
2409 | if ((unsigned)rc > tracks[track_widx].filerem) | 2479 | if ((unsigned)rc > tracks[track_widx].filerem) |
2410 | { | 2480 | { |
2411 | logf("Bad: rc-filerem=%d, fixing", rc-tracks[track_widx].filerem); | 2481 | logf("Bad: rc-filerem=%ld, fixing", rc-tracks[track_widx].filerem); |
2412 | tracks[track_widx].filesize += rc - tracks[track_widx].filerem; | 2482 | tracks[track_widx].filesize += rc - tracks[track_widx].filerem; |
2413 | tracks[track_widx].filerem = rc; | 2483 | tracks[track_widx].filerem = rc; |
2414 | } | 2484 | } |
@@ -2440,7 +2510,7 @@ static bool audio_read_file(size_t minimum) | |||
2440 | 2510 | ||
2441 | if (tracks[track_widx].filerem == 0) | 2511 | if (tracks[track_widx].filerem == 0) |
2442 | { | 2512 | { |
2443 | logf("Finished buf:%dB", tracks[track_widx].filesize); | 2513 | logf("Finished buf:%ldB", tracks[track_widx].filesize); |
2444 | close(current_fd); | 2514 | close(current_fd); |
2445 | current_fd = -1; | 2515 | current_fd = -1; |
2446 | audio_strip_tags(); | 2516 | audio_strip_tags(); |
@@ -2453,7 +2523,7 @@ static bool audio_read_file(size_t minimum) | |||
2453 | } | 2523 | } |
2454 | else | 2524 | else |
2455 | { | 2525 | { |
2456 | logf("%s buf:%dB", ret_val?"Quick":"Partially", | 2526 | logf("%s buf:%ldB", ret_val?"Quick":"Partially", |
2457 | tracks[track_widx].filesize - tracks[track_widx].filerem); | 2527 | tracks[track_widx].filesize - tracks[track_widx].filerem); |
2458 | return ret_val; | 2528 | return ret_val; |
2459 | } | 2529 | } |
@@ -2548,7 +2618,7 @@ static bool audio_loadcodec(bool start_play) | |||
2548 | tracks[track_widx].has_codec = true; | 2618 | tracks[track_widx].has_codec = true; |
2549 | 2619 | ||
2550 | close(fd); | 2620 | close(fd); |
2551 | logf("Done: %dB", size); | 2621 | logf("Done: %ldB", size); |
2552 | 2622 | ||
2553 | return true; | 2623 | return true; |
2554 | } | 2624 | } |
@@ -2901,9 +2971,11 @@ static void audio_fill_file_buffer( | |||
2901 | bool had_next_track = audio_next_track() != NULL; | 2971 | bool had_next_track = audio_next_track() != NULL; |
2902 | bool continue_buffering; | 2972 | bool continue_buffering; |
2903 | 2973 | ||
2904 | /* must reset the buffer before use if trashed */ | 2974 | /* Must reset the buffer before use if trashed or voice only - voice |
2905 | if (buffer_state != BUFFER_STATE_NORMAL) | 2975 | file size shouldn't have changed so we can go straight from |
2906 | audio_reset_buffer(pcmbuf_get_bufsize()); | 2976 | BUFFER_STATE_VOICED_ONLY to BUFFER_STATE_INITIALIZED */ |
2977 | if (buffer_state != BUFFER_STATE_INITIALIZED) | ||
2978 | audio_reset_buffer(); | ||
2907 | 2979 | ||
2908 | if (!audio_initialize_buffer_fill(!start_play)) | 2980 | if (!audio_initialize_buffer_fill(!start_play)) |
2909 | return ; | 2981 | return ; |
@@ -3342,16 +3414,13 @@ static void audio_initiate_dir_change(long direction) | |||
3342 | } | 3414 | } |
3343 | 3415 | ||
3344 | /* | 3416 | /* |
3345 | * Layout audio buffer as follows: | 3417 | * Layout audio buffer as follows - iram buffer depends on target: |
3346 | * [|TALK]|MALLOC|FILE|GUARD|PCM|AUDIOCODEC|[VOICECODEC|] | 3418 | * [|SWAP:iram][|TALK]|MALLOC|FILE|GUARD|PCM|[SWAP:dram[|iram]|] |
3347 | */ | 3419 | */ |
3348 | static void audio_reset_buffer(size_t pcmbufsize) | 3420 | static void audio_reset_buffer(void) |
3349 | { | 3421 | { |
3350 | /* see audio_get_recording_buffer if this is modified */ | 3422 | /* see audio_get_recording_buffer if this is modified */ |
3351 | size_t offset; | ||
3352 | |||
3353 | logf("audio_reset_buffer"); | 3423 | logf("audio_reset_buffer"); |
3354 | logf(" size:%08X", pcmbufsize); | ||
3355 | 3424 | ||
3356 | /* If the setup of anything allocated before the file buffer is | 3425 | /* If the setup of anything allocated before the file buffer is |
3357 | changed, do check the adjustments after the buffer_alloc call | 3426 | changed, do check the adjustments after the buffer_alloc call |
@@ -3362,19 +3431,34 @@ static void audio_reset_buffer(size_t pcmbufsize) | |||
3362 | /* Align the malloc buf to line size. Especially important to cf | 3431 | /* Align the malloc buf to line size. Especially important to cf |
3363 | targets that do line reads/writes. */ | 3432 | targets that do line reads/writes. */ |
3364 | malloc_buf = (unsigned char *)(((uintptr_t)malloc_buf + 15) & ~15); | 3433 | malloc_buf = (unsigned char *)(((uintptr_t)malloc_buf + 15) & ~15); |
3365 | filebuf = malloc_buf + MALLOC_BUFSIZE; | 3434 | filebuf = malloc_buf + MALLOC_BUFSIZE; /* filebuf line align implied */ |
3366 | filebuflen = audiobufend - filebuf; | 3435 | filebuflen = audiobufend - filebuf; |
3367 | 3436 | ||
3368 | /* Allow for codec(s) at end of audio buffer */ | 3437 | /* Allow for codec swap space at end of audio buffer */ |
3369 | if (talk_voice_required()) | 3438 | if (talk_voice_required()) |
3370 | { | 3439 | { |
3440 | /* Layout of swap buffer: | ||
3441 | * #ifdef IRAM_STEAL (dedicated iram_buf): | ||
3442 | * |iram_buf|...audiobuf...|dram_buf|audiobufend | ||
3443 | * #else: | ||
3444 | * audiobuf...|dram_buf|iram_buf|audiobufend | ||
3445 | */ | ||
3371 | #ifdef PLAYBACK_VOICE | 3446 | #ifdef PLAYBACK_VOICE |
3447 | /* Check for an absolutely nasty situation which should never, | ||
3448 | ever happen - frankly should just panic */ | ||
3449 | if (voice_codec_loaded && current_codec != CODEC_IDX_VOICE) | ||
3450 | { | ||
3451 | logf("buffer reset with voice swapped"); | ||
3452 | } | ||
3453 | /* line align length which line aligns the calculations below since | ||
3454 | all sizes are also at least line aligned - needed for memswap128 */ | ||
3455 | filebuflen &= ~15; | ||
3372 | #ifdef IRAM_STEAL | 3456 | #ifdef IRAM_STEAL |
3373 | filebuflen -= CODEC_IRAM_SIZE + 2*CODEC_SIZE; | 3457 | filebuflen -= CODEC_SIZE; |
3374 | #else | 3458 | #else |
3375 | filebuflen -= 2*(CODEC_IRAM_SIZE + CODEC_SIZE); | 3459 | filebuflen -= CODEC_SIZE + CODEC_IRAM_SIZE; |
3376 | #endif | 3460 | #endif |
3377 | /* Allow 2 codecs at end of audio buffer */ | 3461 | /* Allocate buffers for swapping voice <=> audio */ |
3378 | /* If using IRAM for plugins voice IRAM swap buffer must be dedicated | 3462 | /* If using IRAM for plugins voice IRAM swap buffer must be dedicated |
3379 | and out of the way of buffer usage or else a call to audio_get_buffer | 3463 | and out of the way of buffer usage or else a call to audio_get_buffer |
3380 | and subsequent buffer use might trash the swap space. A plugin | 3464 | and subsequent buffer use might trash the swap space. A plugin |
@@ -3383,56 +3467,77 @@ static void audio_reset_buffer(size_t pcmbufsize) | |||
3383 | has been obtained already or never allowing use of the voice IRAM | 3467 | has been obtained already or never allowing use of the voice IRAM |
3384 | buffer within the audio buffer. Using buffer_alloc basically | 3468 | buffer within the audio buffer. Using buffer_alloc basically |
3385 | implements the second in a more convenient way. */ | 3469 | implements the second in a more convenient way. */ |
3386 | iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen; | 3470 | dram_buf = filebuf + filebuflen; |
3387 | dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE; | ||
3388 | 3471 | ||
3389 | #ifdef IRAM_STEAL | 3472 | #ifdef IRAM_STEAL |
3390 | /* Allocate voice IRAM swap buffer once */ | 3473 | /* Allocate voice IRAM swap buffer once */ |
3391 | if (iram_buf[CODEC_IDX_VOICE] == NULL) | 3474 | if (iram_buf == NULL) |
3392 | { | 3475 | { |
3393 | iram_buf[CODEC_IDX_VOICE] = buffer_alloc(CODEC_IRAM_SIZE); | 3476 | iram_buf = buffer_alloc(CODEC_IRAM_SIZE); |
3394 | /* buffer_alloc moves audiobuf; this is safe because only the end | 3477 | /* buffer_alloc moves audiobuf; this is safe because only the end |
3395 | * has been touched so far in this function and the address of | 3478 | * has been touched so far in this function and the address of |
3396 | * filebuf + filebuflen is not changed */ | 3479 | * filebuf + filebuflen is not changed */ |
3397 | malloc_buf += CODEC_IRAM_SIZE; | 3480 | malloc_buf += CODEC_IRAM_SIZE; |
3398 | filebuf += CODEC_IRAM_SIZE; | 3481 | filebuf += CODEC_IRAM_SIZE; |
3399 | filebuflen -= CODEC_IRAM_SIZE; | 3482 | filebuflen -= CODEC_IRAM_SIZE; |
3400 | } | 3483 | } |
3401 | dram_buf[CODEC_IDX_VOICE] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE; | ||
3402 | #else | 3484 | #else |
3403 | iram_buf[CODEC_IDX_VOICE] = dram_buf[CODEC_IDX_AUDIO] + CODEC_SIZE; | 3485 | /* Allocate iram_buf after dram_buf */ |
3404 | dram_buf[CODEC_IDX_VOICE] = iram_buf[CODEC_IDX_VOICE] + CODEC_IRAM_SIZE; | 3486 | iram_buf = dram_buf + CODEC_SIZE; |
3405 | #endif /* IRAM_STEAL */ | 3487 | #endif /* IRAM_STEAL */ |
3406 | |||
3407 | #endif /* PLAYBACK_VOICE */ | 3488 | #endif /* PLAYBACK_VOICE */ |
3408 | } | 3489 | } |
3409 | else | 3490 | else |
3410 | { | 3491 | { |
3411 | #ifdef PLAYBACK_VOICE | 3492 | #ifdef PLAYBACK_VOICE |
3412 | /* Allow for 1 codec at end of audio buffer */ | 3493 | /* No swap buffers needed */ |
3413 | filebuflen -= CODEC_IRAM_SIZE + CODEC_SIZE; | 3494 | iram_buf = NULL; |
3414 | 3495 | dram_buf = NULL; | |
3415 | iram_buf[CODEC_IDX_AUDIO] = filebuf + filebuflen; | ||
3416 | dram_buf[CODEC_IDX_AUDIO] = iram_buf[CODEC_IDX_AUDIO] + CODEC_IRAM_SIZE; | ||
3417 | iram_buf[CODEC_IDX_VOICE] = NULL; | ||
3418 | dram_buf[CODEC_IDX_VOICE] = NULL; | ||
3419 | #endif | 3496 | #endif |
3420 | } | 3497 | } |
3421 | 3498 | ||
3422 | filebuflen -= pcmbuf_init(pcmbufsize, filebuf + filebuflen) + GUARD_BUFSIZE; | 3499 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ |
3500 | filebuflen -= pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; | ||
3423 | 3501 | ||
3424 | /* Ensure that file buffer is aligned */ | 3502 | /* Make sure filebuflen is a longword multiple after adjustment - filebuf |
3425 | offset = -(size_t)filebuf & 3; | 3503 | will already be line aligned */ |
3426 | filebuf += offset; | ||
3427 | filebuflen -= offset; | ||
3428 | filebuflen &= ~3; | 3504 | filebuflen &= ~3; |
3429 | 3505 | ||
3506 | /* Set the high watermark as 75% full...or 25% empty :) */ | ||
3430 | #if MEM > 8 | 3507 | #if MEM > 8 |
3431 | high_watermark = (3*filebuflen)/4; | 3508 | high_watermark = 3*filebuflen / 4; |
3432 | #endif | 3509 | #endif |
3433 | 3510 | ||
3434 | /* Clear any references to the file buffer */ | 3511 | /* Clear any references to the file buffer */ |
3435 | buffer_state = BUFFER_STATE_NORMAL; | 3512 | buffer_state = BUFFER_STATE_INITIALIZED; |
3513 | |||
3514 | #ifdef ROCKBOX_HAS_LOGF | ||
3515 | /* Make sure everything adds up - yes, some info is a bit redundant but | ||
3516 | aids viewing and the sumation of certain variables should add up to | ||
3517 | the location of others. */ | ||
3518 | { | ||
3519 | size_t pcmbufsize; | ||
3520 | unsigned char * pcmbuf = pcmbuf_get_meminfo(&pcmbufsize); | ||
3521 | logf("mabuf: %08X", (unsigned)malloc_buf); | ||
3522 | logf("mabufe: %08X", (unsigned)(malloc_buf + MALLOC_BUFSIZE)); | ||
3523 | logf("fbuf: %08X", (unsigned)filebuf); | ||
3524 | logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); | ||
3525 | logf("gbuf: %08X", (unsigned)(filebuf + filebuflen)); | ||
3526 | logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE)); | ||
3527 | logf("pcmb: %08X", (unsigned)pcmbuf); | ||
3528 | logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); | ||
3529 | if (dram_buf) | ||
3530 | { | ||
3531 | logf("dramb: %08X", (unsigned)dram_buf); | ||
3532 | logf("drambe: %08X", (unsigned)(dram_buf + CODEC_SIZE)); | ||
3533 | } | ||
3534 | if (iram_buf) | ||
3535 | { | ||
3536 | logf("iramb: %08X", (unsigned)iram_buf); | ||
3537 | logf("irambe: %08X", (unsigned)(iram_buf + CODEC_IRAM_SIZE)); | ||
3538 | } | ||
3539 | } | ||
3540 | #endif | ||
3436 | } | 3541 | } |
3437 | 3542 | ||
3438 | #if MEM > 8 | 3543 | #if MEM > 8 |
@@ -3454,6 +3559,16 @@ static void audio_thread(void) | |||
3454 | #ifdef PLAYBACK_VOICE | 3559 | #ifdef PLAYBACK_VOICE |
3455 | /* Unlock mutex that init stage locks before creating this thread */ | 3560 | /* Unlock mutex that init stage locks before creating this thread */ |
3456 | mutex_unlock(&mutex_codecthread); | 3561 | mutex_unlock(&mutex_codecthread); |
3562 | |||
3563 | /* Buffers must be set up by now - should panic - really */ | ||
3564 | if (buffer_state != BUFFER_STATE_INITIALIZED) | ||
3565 | { | ||
3566 | logf("audio_thread start: no buffer"); | ||
3567 | } | ||
3568 | |||
3569 | /* Have to wait for voice to load up or else the codec swap will be | ||
3570 | invalid when an audio codec is loaded */ | ||
3571 | wait_for_voice_swap_in(); | ||
3457 | #endif | 3572 | #endif |
3458 | 3573 | ||
3459 | while (1) | 3574 | while (1) |
@@ -3604,12 +3719,14 @@ void audio_init(void) | |||
3604 | static struct mp3entry id3_voice; | 3719 | static struct mp3entry id3_voice; |
3605 | #endif | 3720 | #endif |
3606 | 3721 | ||
3607 | logf("audio: %s", audio_is_initialized ? | ||
3608 | "initializing" : "already initialized"); | ||
3609 | |||
3610 | /* Can never do this twice */ | 3722 | /* Can never do this twice */ |
3611 | if (audio_is_initialized) | 3723 | if (audio_is_initialized) |
3724 | { | ||
3725 | logf("audio: already initialized"); | ||
3612 | return; | 3726 | return; |
3727 | } | ||
3728 | |||
3729 | logf("audio: initializing"); | ||
3613 | 3730 | ||
3614 | /* Initialize queues before giving control elsewhere in case it likes | 3731 | /* Initialize queues before giving control elsewhere in case it likes |
3615 | to send messages. Thread creation will be delayed however so nothing | 3732 | to send messages. Thread creation will be delayed however so nothing |
@@ -3620,6 +3737,7 @@ void audio_init(void) | |||
3620 | hardware is initialized - audio thread unlocks it after final init | 3737 | hardware is initialized - audio thread unlocks it after final init |
3621 | stage */ | 3738 | stage */ |
3622 | mutex_lock(&mutex_codecthread); | 3739 | mutex_lock(&mutex_codecthread); |
3740 | queue_init(&voice_queue, true); | ||
3623 | #endif | 3741 | #endif |
3624 | queue_init(&audio_queue, true); | 3742 | queue_init(&audio_queue, true); |
3625 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); | 3743 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); |
@@ -3663,6 +3781,7 @@ void audio_init(void) | |||
3663 | ci_voice.seek_complete = voice_do_nothing; | 3781 | ci_voice.seek_complete = voice_do_nothing; |
3664 | ci_voice.set_elapsed = voice_set_elapsed_callback; | 3782 | ci_voice.set_elapsed = voice_set_elapsed_callback; |
3665 | ci_voice.set_offset = voice_set_offset_callback; | 3783 | ci_voice.set_offset = voice_set_offset_callback; |
3784 | ci_voice.configure = voice_configure_callback; | ||
3666 | ci_voice.discard_codec = voice_do_nothing; | 3785 | ci_voice.discard_codec = voice_do_nothing; |
3667 | ci_voice.taginfo_ready = &voicetagtrue; | 3786 | ci_voice.taginfo_ready = &voicetagtrue; |
3668 | ci_voice.id3 = &id3_voice; | 3787 | ci_voice.id3 = &id3_voice; |
@@ -3671,12 +3790,14 @@ void audio_init(void) | |||
3671 | #endif | 3790 | #endif |
3672 | 3791 | ||
3673 | /* initialize the buffer */ | 3792 | /* initialize the buffer */ |
3674 | filebuf = audiobuf; /* must be non-NULL for audio_set_crossfade */ | 3793 | filebuf = audiobuf; |
3675 | 3794 | ||
3676 | /* audio_reset_buffer must to know the size of voice buffer so init | 3795 | /* audio_reset_buffer must to know the size of voice buffer so init |
3677 | voice first */ | 3796 | talk first */ |
3678 | talk_init(); | 3797 | talk_init(); |
3679 | 3798 | ||
3799 | /* Create the threads late now that we shouldn't be yielding again before | ||
3800 | returning */ | ||
3680 | codec_thread_p = create_thread( | 3801 | codec_thread_p = create_thread( |
3681 | codec_thread, codec_stack, sizeof(codec_stack), | 3802 | codec_thread, codec_stack, sizeof(codec_stack), |
3682 | codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK) | 3803 | codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK) |
@@ -3686,8 +3807,24 @@ void audio_init(void) | |||
3686 | audio_thread_name IF_PRIO(, PRIORITY_BUFFERING) | 3807 | audio_thread_name IF_PRIO(, PRIORITY_BUFFERING) |
3687 | IF_COP(, CPU, false)); | 3808 | IF_COP(, CPU, false)); |
3688 | 3809 | ||
3689 | audio_set_crossfade(global_settings.crossfade); | 3810 | #ifdef PLAYBACK_VOICE |
3811 | /* TODO: Change this around when various speech codecs can be used */ | ||
3812 | if (talk_voice_required()) | ||
3813 | { | ||
3814 | logf("Starting voice codec"); | ||
3815 | create_thread(voice_thread, voice_stack, | ||
3816 | sizeof(voice_stack), voice_thread_name | ||
3817 | IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU, false)); | ||
3818 | } | ||
3819 | #endif | ||
3820 | |||
3821 | /* Set crossfade setting for next buffer init which should be about... */ | ||
3822 | pcmbuf_crossfade_enable(global_settings.crossfade); | ||
3823 | |||
3824 | /* ...now! Set up the buffers */ | ||
3825 | audio_reset_buffer(); | ||
3690 | 3826 | ||
3827 | /* Probably safe to say */ | ||
3691 | audio_is_initialized = true; | 3828 | audio_is_initialized = true; |
3692 | 3829 | ||
3693 | sound_settings_apply(); | 3830 | sound_settings_apply(); |
diff --git a/apps/playback.h b/apps/playback.h index 82179f1ee2..cf7547ec26 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -64,8 +64,6 @@ void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, | |||
64 | bool last_track)); | 64 | bool last_track)); |
65 | void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3, | 65 | void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3, |
66 | bool last_track)); | 66 | bool last_track)); |
67 | void voice_init(void); | ||
68 | void voice_stop(void); | ||
69 | 67 | ||
70 | #if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */ | 68 | #if CONFIG_CODEC == SWCODEC /* This #ifdef is better here than gui/gwps.c */ |
71 | extern void audio_next_dir(void); | 69 | extern void audio_next_dir(void); |
diff --git a/apps/talk.c b/apps/talk.c index f975ca2028..04e37394b6 100644 --- a/apps/talk.c +++ b/apps/talk.c | |||
@@ -528,9 +528,6 @@ void talk_init(void) | |||
528 | if (has_voicefile) | 528 | if (has_voicefile) |
529 | { | 529 | { |
530 | voicefile_size = filesize(filehandle); | 530 | voicefile_size = filesize(filehandle); |
531 | #if CONFIG_CODEC == SWCODEC | ||
532 | voice_init(); | ||
533 | #endif | ||
534 | close(filehandle); /* close again, this was just to detect presence */ | 531 | close(filehandle); /* close again, this was just to detect presence */ |
535 | filehandle = -1; | 532 | filehandle = -1; |
536 | } | 533 | } |
diff --git a/firmware/SOURCES b/firmware/SOURCES index 549e4af286..974326570a 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -248,6 +248,8 @@ target/coldfire/crt0.S | |||
248 | target/coldfire/memcpy-coldfire.S | 248 | target/coldfire/memcpy-coldfire.S |
249 | target/coldfire/memmove-coldfire.S | 249 | target/coldfire/memmove-coldfire.S |
250 | target/coldfire/memset-coldfire.S | 250 | target/coldfire/memset-coldfire.S |
251 | target/coldfire/memswap128-coldfire.S | ||
252 | common/memswap128.c | ||
251 | #if defined(HAVE_LCD_COLOR) \ | 253 | #if defined(HAVE_LCD_COLOR) \ |
252 | || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) | 254 | || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) |
253 | target/coldfire/memset16-coldfire.S | 255 | target/coldfire/memset16-coldfire.S |
@@ -269,6 +271,7 @@ common/strlen.c | |||
269 | #ifndef SIMULATOR | 271 | #ifndef SIMULATOR |
270 | target/arm/memset-arm.S | 272 | target/arm/memset-arm.S |
271 | target/arm/memset16-arm.S | 273 | target/arm/memset16-arm.S |
274 | target/arm/memswap128-arm.S | ||
272 | #if CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002 | 275 | #if CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002 |
273 | target/arm/i2c-pp.c | 276 | target/arm/i2c-pp.c |
274 | #elif CONFIG_I2C == I2C_PNX0101 | 277 | #elif CONFIG_I2C == I2C_PNX0101 |
@@ -295,6 +298,7 @@ common/memcpy.c | |||
295 | common/memmove.c | 298 | common/memmove.c |
296 | common/memset.c | 299 | common/memset.c |
297 | common/memset16.c | 300 | common/memset16.c |
301 | common/memswap128.c | ||
298 | common/strlen.c | 302 | common/strlen.c |
299 | #ifndef SIMULATOR | 303 | #ifndef SIMULATOR |
300 | crt0.S | 304 | crt0.S |
diff --git a/firmware/common/memswap128.c b/firmware/common/memswap128.c new file mode 100644 index 0000000000..af1fe157b6 --- /dev/null +++ b/firmware/common/memswap128.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "config.h" | ||
20 | #include <string.h> | ||
21 | #include <inttypes.h> | ||
22 | |||
23 | void memswap128(void *a, void *b, size_t len) | ||
24 | { | ||
25 | for (len >>= 4; len > 0; len--, a += 16, b += 16) | ||
26 | { | ||
27 | int32_t a0 = *((int32_t *)a + 0); | ||
28 | int32_t a1 = *((int32_t *)a + 1); | ||
29 | int32_t a2 = *((int32_t *)a + 2); | ||
30 | int32_t a3 = *((int32_t *)a + 3); | ||
31 | int32_t b0 = *((int32_t *)b + 0); | ||
32 | int32_t b1 = *((int32_t *)b + 1); | ||
33 | int32_t b2 = *((int32_t *)b + 2); | ||
34 | int32_t b3 = *((int32_t *)b + 3); | ||
35 | *((int32_t *)b + 0) = a0; | ||
36 | *((int32_t *)b + 1) = a1; | ||
37 | *((int32_t *)b + 2) = a2; | ||
38 | *((int32_t *)b + 3) = a3; | ||
39 | *((int32_t *)a + 0) = b0; | ||
40 | *((int32_t *)a + 1) = b1; | ||
41 | *((int32_t *)a + 2) = b2; | ||
42 | *((int32_t *)a + 3) = b3; | ||
43 | } | ||
44 | } | ||
diff --git a/firmware/include/memory.h b/firmware/include/memory.h index 559c6ed96a..75bcb98df7 100644 --- a/firmware/include/memory.h +++ b/firmware/include/memory.h | |||
@@ -24,4 +24,15 @@ | |||
24 | 24 | ||
25 | void memset16(void *dst, int val, size_t len); | 25 | void memset16(void *dst, int val, size_t len); |
26 | 26 | ||
27 | /** | ||
28 | * memswap128 | ||
29 | * | ||
30 | * Exchanges the contents of two buffers. | ||
31 | * For maximum efficiency, this function performs no aligning of addresses | ||
32 | * and buf1, buf2 and len should be 16 byte (128 bit) aligned. Not being at | ||
33 | * least longword aligned will fail on some architechtures. Any len mod 16 | ||
34 | * at the end is not swapped. | ||
35 | */ | ||
36 | void memswap128(void *buf1, void *buf2, size_t len); | ||
37 | |||
27 | #endif /* _MEMORY_H_ */ | 38 | #endif /* _MEMORY_H_ */ |
diff --git a/firmware/target/arm/memswap128-arm.S b/firmware/target/arm/memswap128-arm.S new file mode 100644 index 0000000000..f5276ef353 --- /dev/null +++ b/firmware/target/arm/memswap128-arm.S | |||
@@ -0,0 +1,44 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | /**************************************************************************** | ||
21 | * void memswap128(void *buf1, void *buf2, size_t len) | ||
22 | */ | ||
23 | .section .icode, "ax", %progbits | ||
24 | .align 2 | ||
25 | .global memswap128 | ||
26 | .type memswap128, %function | ||
27 | memswap128: | ||
28 | @ r0 = buf1 | ||
29 | @ r1 = buf2 | ||
30 | @ r2 = len | ||
31 | movs r2, r2, lsr #4 @ bytes => lines, len == 0? | ||
32 | moveq pc, lr @ not at least a line? leave | ||
33 | stmdb sp!, { r4-r10, lr } @ save registers and return address | ||
34 | .loop: @ | ||
35 | ldmia r0, { r3-r6 } @ read four longwords from buf1 | ||
36 | ldmia r1, { r7-r10 } @ read four longwords from buf2 | ||
37 | stmia r0!, { r7-r10 } @ write buf2 data to buf1, buf1 += 16 | ||
38 | stmia r1!, { r3-r6 } @ write buf1 data to buf2, buf2 += 16 | ||
39 | subs r2, r2, #1 @ len -= 1, len > 0 ? | ||
40 | bhi .loop @ yes? keep exchanging | ||
41 | ldmia sp!, { r4-r10, pc } @ restore registers and return | ||
42 | .end: | ||
43 | .size memswap128, .end-memswap128 | ||
44 | |||
diff --git a/firmware/target/coldfire/memswap128-coldfire.S b/firmware/target/coldfire/memswap128-coldfire.S new file mode 100644 index 0000000000..2509fd0e45 --- /dev/null +++ b/firmware/target/coldfire/memswap128-coldfire.S | |||
@@ -0,0 +1,50 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | /**************************************************************************** | ||
21 | * void memswap128(void *buf1, void *buf2, size_t len) | ||
22 | */ | ||
23 | .section .icode, "ax", @progbits | ||
24 | .align 2 | ||
25 | .global memswap128 | ||
26 | .type memswap128, @function | ||
27 | memswap128: | ||
28 | lea.l -28(%sp), %sp | save registers | ||
29 | movem.l %d2-%d7/%a2, (%sp) | | ||
30 | movem.l 32(%sp), %a0-%a2 | %a0 = buf1 | ||
31 | | %a1 = buf2 | ||
32 | | %a2 = len | ||
33 | lea.l -15(%a0, %a2.l), %a2 | %a2 = end address - 15 | ||
34 | cmp.l %a0, %a2 | end address <= buf1? | ||
35 | bls.b .no_lines | not at least a line? leave | ||
36 | .loop: | | ||
37 | movem.l (%a0), %d0-%d3 | read four longwords from buf1 | ||
38 | movem.l (%a1), %d4-%d7 | read four longwords from buf2 | ||
39 | movem.l %d4-%d7, (%a0) | write buf2 data to buf1 | ||
40 | movem.l %d0-%d3, (%a1) | write buf1 data to buf2 | ||
41 | lea.l 16(%a0), %a0 | buf1 += 16 | ||
42 | lea.l 16(%a1), %a1 | buf2 += 16 | ||
43 | cmp.l %a0, %a2 | %a0 < %d0? | ||
44 | bhi.b .loop | yes? keep exchanging | ||
45 | .no_lines: | | ||
46 | movem.l (%sp), %d2-%d7/%a2 | restore registers | ||
47 | lea.l 28(%sp), %sp | | ||
48 | rts | | ||
49 | .end: | ||
50 | .size memswap128, .end-memswap128 | ||