diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2005-07-07 07:15:05 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2005-07-07 07:15:05 +0000 |
commit | 3eb962d13bd4ca8c29ab33c41428a44e644e59ec (patch) | |
tree | 6ad56c24745868d9c56a56dc1278cfde4f5573ff | |
parent | 8d3855eb536d4b8f1459c9b2da3beb5a0ac328b4 (diff) | |
download | rockbox-3eb962d13bd4ca8c29ab33c41428a44e644e59ec.tar.gz rockbox-3eb962d13bd4ca8c29ab33c41428a44e644e59ec.zip |
PCM buffering fixes. Made a temporary workaround for playback glitch
bug (see the patch).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7049 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/codecs.c | 3 | ||||
-rw-r--r-- | apps/codecs/mpa.c | 20 | ||||
-rw-r--r-- | apps/playback.c | 49 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 87 |
4 files changed, 87 insertions, 72 deletions
diff --git a/apps/codecs.c b/apps/codecs.c index d3a9d9e9c1..400e7fbfcf 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -277,6 +277,9 @@ int codec_load_file(const char *plugin) | |||
277 | int fd; | 277 | int fd; |
278 | int rc; | 278 | int rc; |
279 | 279 | ||
280 | /* zero out codec buffer to ensure a properly zeroed bss area */ | ||
281 | memset(codecbuf, 0, CODEC_SIZE); | ||
282 | |||
280 | fd = open(plugin, O_RDONLY); | 283 | fd = open(plugin, O_RDONLY); |
281 | if (fd < 0) { | 284 | if (fd < 0) { |
282 | snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin); | 285 | snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin); |
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 5cf4eb8730..5d6f7d29ad 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c | |||
@@ -39,12 +39,6 @@ void abort(void) { | |||
39 | 39 | ||
40 | 40 | ||
41 | #define INPUT_CHUNK_SIZE 8192 | 41 | #define INPUT_CHUNK_SIZE 8192 |
42 | #define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */ | ||
43 | |||
44 | unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE]; | ||
45 | unsigned char *OutputPtr; | ||
46 | unsigned char *GuardPtr = NULL; | ||
47 | const unsigned char *OutputBufferEnd = OutputBuffer + OUTPUT_BUFFER_SIZE; | ||
48 | 42 | ||
49 | mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR; | 43 | mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR; |
50 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR; | 44 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR; |
@@ -115,7 +109,6 @@ enum codec_status codec_start(struct codec_api* api) | |||
115 | 109 | ||
116 | first_frame = false; | 110 | first_frame = false; |
117 | file_end = 0; | 111 | file_end = 0; |
118 | OutputPtr = OutputBuffer; | ||
119 | 112 | ||
120 | while (!*ci->taginfo_ready) | 113 | while (!*ci->taginfo_ready) |
121 | ci->yield(); | 114 | ci->yield(); |
@@ -195,7 +188,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
195 | } | 188 | } |
196 | else if(MAD_RECOVERABLE(Stream.error)) | 189 | else if(MAD_RECOVERABLE(Stream.error)) |
197 | { | 190 | { |
198 | if(Stream.error!=MAD_ERROR_LOSTSYNC || Stream.this_frame!=GuardPtr) | 191 | if(Stream.error!=MAD_ERROR_LOSTSYNC) |
199 | { | 192 | { |
200 | // rb->splash(HZ*1, true, "Recoverable...!"); | 193 | // rb->splash(HZ*1, true, "Recoverable...!"); |
201 | } | 194 | } |
@@ -209,9 +202,9 @@ enum codec_status codec_start(struct codec_api* api) | |||
209 | Status=1; | 202 | Status=1; |
210 | break; | 203 | break; |
211 | } | 204 | } |
205 | break ; | ||
212 | } | 206 | } |
213 | if (Stream.next_frame) | 207 | |
214 | ci->advance_buffer_loc((void *)Stream.next_frame); | ||
215 | file_end = false; | 208 | file_end = false; |
216 | /* ?? Do we need the timer module? */ | 209 | /* ?? Do we need the timer module? */ |
217 | // mad_timer_add(&Timer,Frame.header.duration); | 210 | // mad_timer_add(&Timer,Frame.header.duration); |
@@ -222,7 +215,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
222 | /* We skip start_skip number of samples here, this should only happen for | 215 | /* We skip start_skip number of samples here, this should only happen for |
223 | very first frame in the stream. */ | 216 | very first frame in the stream. */ |
224 | /* TODO: possible for start_skip to exceed one frames worth of samples? */ | 217 | /* TODO: possible for start_skip to exceed one frames worth of samples? */ |
225 | 218 | ||
226 | if (MAD_NCHANNELS(&Frame.header) == 2) { | 219 | if (MAD_NCHANNELS(&Frame.header) == 2) { |
227 | if (current_stereo_mode != STEREO_NONINTERLEAVED) { | 220 | if (current_stereo_mode != STEREO_NONINTERLEAVED) { |
228 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED); | 221 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED); |
@@ -241,6 +234,11 @@ enum codec_status codec_start(struct codec_api* api) | |||
241 | } | 234 | } |
242 | start_skip = 0; /* not very elegant, and might want to keep this value */ | 235 | start_skip = 0; /* not very elegant, and might want to keep this value */ |
243 | 236 | ||
237 | if (Stream.next_frame) | ||
238 | ci->advance_buffer_loc((void *)Stream.next_frame); | ||
239 | else | ||
240 | ci->advance_buffer(size); | ||
241 | |||
244 | samplesdone += Synth.pcm.length; | 242 | samplesdone += Synth.pcm.length; |
245 | samplecount -= Synth.pcm.length; | 243 | samplecount -= Synth.pcm.length; |
246 | ci->set_elapsed(samplesdone / (frequency_divider / 10)); | 244 | ci->set_elapsed(samplesdone / (frequency_divider / 10)); |
diff --git a/apps/playback.c b/apps/playback.c index 13c66a43b5..f9caff1c4f 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -659,12 +659,12 @@ void audio_fill_file_buffer(void) | |||
659 | buf_widx -= codecbuflen; | 659 | buf_widx -= codecbuflen; |
660 | i += rc; | 660 | i += rc; |
661 | tracks[track_widx].available += rc; | 661 | tracks[track_widx].available += rc; |
662 | tracks[track_widx].filerem -= rc; | ||
663 | tracks[track_widx].filepos += rc; | ||
662 | codecbufused += rc; | 664 | codecbufused += rc; |
663 | fill_bytesleft -= rc; | 665 | fill_bytesleft -= rc; |
664 | } | 666 | } |
665 | 667 | ||
666 | tracks[track_widx].filerem -= i; | ||
667 | tracks[track_widx].filepos += i; | ||
668 | /*logf("Filled:%d/%d", tracks[track_widx].available, | 668 | /*logf("Filled:%d/%d", tracks[track_widx].available, |
669 | tracks[track_widx].filerem);*/ | 669 | tracks[track_widx].filerem);*/ |
670 | } | 670 | } |
@@ -890,26 +890,29 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
890 | 890 | ||
891 | /* Starting playback from an offset is only support in MPA at the moment */ | 891 | /* Starting playback from an offset is only support in MPA at the moment */ |
892 | if (offset > 0) { | 892 | if (offset > 0) { |
893 | if ((tracks[track_widx].id3.codectype==AFMT_MPA_L2) || | 893 | switch (tracks[track_widx].id3.codectype) { |
894 | (tracks[track_widx].id3.codectype==AFMT_MPA_L3)) { | 894 | case AFMT_MPA_L2: |
895 | lseek(fd, offset, SEEK_SET); | 895 | case AFMT_MPA_L3: |
896 | tracks[track_widx].id3.offset = offset; | 896 | lseek(fd, offset, SEEK_SET); |
897 | mp3_set_elapsed(&tracks[track_widx].id3); | 897 | tracks[track_widx].id3.offset = offset; |
898 | tracks[track_widx].filepos = offset; | 898 | mp3_set_elapsed(&tracks[track_widx].id3); |
899 | tracks[track_widx].filerem = tracks[track_widx].filesize - offset; | 899 | tracks[track_widx].filepos = offset; |
900 | ci.curpos = offset; | 900 | tracks[track_widx].filerem = tracks[track_widx].filesize - offset; |
901 | tracks[track_widx].start_pos = offset; | 901 | ci.curpos = offset; |
902 | } | 902 | tracks[track_widx].start_pos = offset; |
903 | else if (tracks[track_widx].id3.codectype==AFMT_WAVPACK) { | 903 | break; |
904 | lseek(fd, offset, SEEK_SET); | 904 | |
905 | tracks[track_widx].id3.offset = offset; | 905 | case AFMT_WAVPACK: |
906 | tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2; | 906 | lseek(fd, offset, SEEK_SET); |
907 | tracks[track_widx].filepos = offset; | 907 | tracks[track_widx].id3.offset = offset; |
908 | tracks[track_widx].filerem = tracks[track_widx].filesize - offset; | 908 | tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2; |
909 | ci.curpos = offset; | 909 | tracks[track_widx].filepos = offset; |
910 | tracks[track_widx].start_pos = offset; | 910 | tracks[track_widx].filerem = tracks[track_widx].filesize - offset; |
911 | } | 911 | ci.curpos = offset; |
912 | } | 912 | tracks[track_widx].start_pos = offset; |
913 | break; | ||
914 | } | ||
915 | } | ||
913 | 916 | ||
914 | if (start_play) { | 917 | if (start_play) { |
915 | track_count++; | 918 | track_count++; |
@@ -1795,6 +1798,8 @@ void audio_init(void) | |||
1795 | track_buffer_callback = NULL; | 1798 | track_buffer_callback = NULL; |
1796 | track_unbuffer_callback = NULL; | 1799 | track_unbuffer_callback = NULL; |
1797 | track_changed_callback = NULL; | 1800 | track_changed_callback = NULL; |
1801 | /* Just to prevent cur_ti never be anything random. */ | ||
1802 | cur_ti = &tracks[0]; | ||
1798 | 1803 | ||
1799 | logf("abuf:%0x", PCMBUF_SIZE); | 1804 | logf("abuf:%0x", PCMBUF_SIZE); |
1800 | logf("fbuf:%0x", codecbuflen); | 1805 | logf("fbuf:%0x", codecbuflen); |
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index be7f4e4379..6b9a4bbd5c 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -146,7 +146,7 @@ static void dma_stop(void) | |||
146 | /* Reset the FIFO */ | 146 | /* Reset the FIFO */ |
147 | IIS2CONFIG = 0x800; | 147 | IIS2CONFIG = 0x800; |
148 | EBU1CONFIG = 0x800; | 148 | EBU1CONFIG = 0x800; |
149 | 149 | ||
150 | pcmbuf_unplayed_bytes = 0; | 150 | pcmbuf_unplayed_bytes = 0; |
151 | last_chunksize = 0; | 151 | last_chunksize = 0; |
152 | audiobuffer_pos = 0; | 152 | audiobuffer_pos = 0; |
@@ -213,7 +213,8 @@ static void pcm_play_callback(unsigned char** start, long* size) | |||
213 | if(pcm_play_num_used_buffers()) | 213 | if(pcm_play_num_used_buffers()) |
214 | { | 214 | { |
215 | /* Play max 64K at a time */ | 215 | /* Play max 64K at a time */ |
216 | sz = MIN(desc->size, 32768); | 216 | //sz = MIN(desc->size, 32768); |
217 | sz = desc->size; | ||
217 | *start = desc->addr; | 218 | *start = desc->addr; |
218 | *size = sz; | 219 | *size = sz; |
219 | 220 | ||
@@ -245,11 +246,21 @@ void pcm_play_data(const unsigned char* start, int size, | |||
245 | void (*get_more)(unsigned char** start, long* size)) | 246 | void (*get_more)(unsigned char** start, long* size)) |
246 | { | 247 | { |
247 | callback_for_more = get_more; | 248 | callback_for_more = get_more; |
249 | /** FIXME: This is a temporary fix to prevent playback glitches when | ||
250 | * playing the first file. We will just drop the first frame to prevent | ||
251 | * that problem from occurring. | ||
252 | * Some debug data: | ||
253 | * - This problem will occur only when the first file. | ||
254 | * - First frame will be totally corrupt and the song will begin | ||
255 | * from the next frame. But at the next time (when the bug has | ||
256 | * already happened), the song will start from first frame. | ||
257 | * - Dropping some frames directly from (mpa) codec will also | ||
258 | * prevent the problem from happening. So it's unlikely you can | ||
259 | * find the explanation for this bug from this file. | ||
260 | */ | ||
261 | get_more((unsigned char **)&start, (long *)&size); // REMOVE THIS TO TEST | ||
248 | get_more(&next_start, &next_size); | 262 | get_more(&next_start, &next_size); |
249 | dma_start(start, size); | 263 | dma_start(start, size); |
250 | |||
251 | /* Sleep a while, then unmute audio output */ | ||
252 | sleep(HZ/8); | ||
253 | uda1380_mute(false); | 264 | uda1380_mute(false); |
254 | } | 265 | } |
255 | 266 | ||
@@ -310,7 +321,7 @@ void DMA0(void) | |||
310 | if(res & 0x70) | 321 | if(res & 0x70) |
311 | { | 322 | { |
312 | dma_stop(); | 323 | dma_stop(); |
313 | logf("DMA Error"); | 324 | logf("DMA Error:0x%04x", res); |
314 | } | 325 | } |
315 | else | 326 | else |
316 | { | 327 | { |
@@ -326,7 +337,7 @@ void DMA0(void) | |||
326 | { | 337 | { |
327 | /* Finished playing */ | 338 | /* Finished playing */ |
328 | dma_stop(); | 339 | dma_stop(); |
329 | logf("DMA No Data"); | 340 | logf("DMA No Data:0x%04x", res); |
330 | } | 341 | } |
331 | } | 342 | } |
332 | 343 | ||
@@ -431,7 +442,7 @@ bool pcm_is_lowdata(void) | |||
431 | bool pcm_crossfade_init(void) | 442 | bool pcm_crossfade_init(void) |
432 | { | 443 | { |
433 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled | 444 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled |
434 | || crossfade_active) { | 445 | || crossfade_active || crossfade_init) { |
435 | return false; | 446 | return false; |
436 | } | 447 | } |
437 | logf("pcm_crossfade_init"); | 448 | logf("pcm_crossfade_init"); |
@@ -457,21 +468,27 @@ void pcm_flush_audio(void) | |||
457 | 468 | ||
458 | void pcm_flush_fillpos(void) | 469 | void pcm_flush_fillpos(void) |
459 | { | 470 | { |
460 | if (audiobuffer_fillpos) { | 471 | int copy_n; |
461 | while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], | 472 | |
462 | audiobuffer_fillpos, pcm_event_handler)) { | 473 | copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE); |
474 | |||
475 | if (copy_n) { | ||
476 | while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], | ||
477 | copy_n, pcm_event_handler)) { | ||
463 | pcm_boost(false); | 478 | pcm_boost(false); |
464 | yield(); | 479 | yield(); |
465 | /* This is a fatal error situation that should never happen. */ | 480 | /* This is a fatal error situation that should never happen. */ |
466 | if (!pcm_playing) | 481 | if (!pcm_playing) { |
482 | logf("pcm_flush_fillpos error"); | ||
467 | break ; | 483 | break ; |
484 | } | ||
468 | } | 485 | } |
469 | pcm_event_handler = NULL; | 486 | pcm_event_handler = NULL; |
470 | audiobuffer_pos += audiobuffer_fillpos; | 487 | audiobuffer_pos += copy_n; |
471 | if (audiobuffer_pos >= PCMBUF_SIZE) | 488 | if (audiobuffer_pos >= PCMBUF_SIZE) |
472 | audiobuffer_pos -= PCMBUF_SIZE; | 489 | audiobuffer_pos -= PCMBUF_SIZE; |
473 | audiobuffer_free -= audiobuffer_fillpos; | 490 | audiobuffer_free -= copy_n; |
474 | audiobuffer_fillpos = 0; | 491 | audiobuffer_fillpos -= copy_n; |
475 | } | 492 | } |
476 | } | 493 | } |
477 | 494 | ||
@@ -541,7 +558,7 @@ int crossfade(short *buf, const short *buf2, int length) | |||
541 | return size; | 558 | return size; |
542 | } | 559 | } |
543 | 560 | ||
544 | inline static bool prepare_insert(long length) | 561 | static bool prepare_insert(long length) |
545 | { | 562 | { |
546 | if (crossfade_init) | 563 | if (crossfade_init) |
547 | crossfade_start(); | 564 | crossfade_start(); |
@@ -566,9 +583,10 @@ void* pcm_request_buffer(long length, long *realsize) | |||
566 | { | 583 | { |
567 | void *ptr = NULL; | 584 | void *ptr = NULL; |
568 | 585 | ||
569 | if (!prepare_insert(length)) { | 586 | while (audiobuffer_free < length + audiobuffer_fillpos |
570 | *realsize = 0; | 587 | + CHUNK_SIZE && !crossfade_active) { |
571 | return NULL; | 588 | pcm_boost(false); |
589 | yield(); | ||
572 | } | 590 | } |
573 | 591 | ||
574 | if (crossfade_active) { | 592 | if (crossfade_active) { |
@@ -595,6 +613,8 @@ void pcm_flush_buffer(long length) | |||
595 | { | 613 | { |
596 | int copy_n; | 614 | int copy_n; |
597 | char *buf; | 615 | char *buf; |
616 | |||
617 | prepare_insert(length); | ||
598 | 618 | ||
599 | if (crossfade_active) { | 619 | if (crossfade_active) { |
600 | buf = &guardbuf[0]; | 620 | buf = &guardbuf[0]; |
@@ -620,14 +640,14 @@ void pcm_flush_buffer(long length) | |||
620 | pcm_flush_fillpos(); | 640 | pcm_flush_fillpos(); |
621 | } | 641 | } |
622 | } | 642 | } |
623 | 643 | ||
624 | audiobuffer_fillpos += length; | 644 | audiobuffer_fillpos += length; |
625 | 645 | ||
626 | try_flush: | 646 | try_flush: |
627 | if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE | 647 | if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE |
628 | - audiobuffer_pos - audiobuffer_fillpos > 0) | 648 | - audiobuffer_pos - audiobuffer_fillpos > 0) |
629 | return ; | 649 | return ; |
630 | 650 | ||
631 | copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); | 651 | copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); |
632 | if (copy_n > 0) { | 652 | if (copy_n > 0) { |
633 | audiobuffer_fillpos -= copy_n; | 653 | audiobuffer_fillpos -= copy_n; |
@@ -699,15 +719,9 @@ void pcm_play_init(void) | |||
699 | audiobuffer = &audiobuf[(audiobufend - audiobuf) - | 719 | audiobuffer = &audiobuf[(audiobufend - audiobuf) - |
700 | PCMBUF_SIZE - PCMBUF_GUARD]; | 720 | PCMBUF_SIZE - PCMBUF_GUARD]; |
701 | guardbuf = &audiobuffer[PCMBUF_SIZE]; | 721 | guardbuf = &audiobuffer[PCMBUF_SIZE]; |
702 | audiobuffer_free = PCMBUF_SIZE; | 722 | |
703 | audiobuffer_pos = 0; | 723 | /* Call dma_stop to initialize everything. */ |
704 | audiobuffer_fillpos = 0; | 724 | dma_stop(); |
705 | boost_mode = 0; | ||
706 | pcmbuf_read_index = 0; | ||
707 | pcmbuf_write_index = 0; | ||
708 | pcmbuf_unplayed_bytes = 0; | ||
709 | crossfade_active = false; | ||
710 | crossfade_init = false; | ||
711 | pcm_event_handler = NULL; | 725 | pcm_event_handler = NULL; |
712 | } | 726 | } |
713 | 727 | ||
@@ -723,9 +737,8 @@ bool pcm_is_crossfade_enabled(void) | |||
723 | 737 | ||
724 | void pcm_play_start(void) | 738 | void pcm_play_start(void) |
725 | { | 739 | { |
726 | struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; | 740 | unsigned long size; |
727 | int size; | 741 | unsigned char *start; |
728 | char *start; | ||
729 | 742 | ||
730 | if (crossfade_enabled) { | 743 | if (crossfade_enabled) { |
731 | pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); | 744 | pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); |
@@ -736,11 +749,7 @@ void pcm_play_start(void) | |||
736 | 749 | ||
737 | if(!pcm_is_playing()) | 750 | if(!pcm_is_playing()) |
738 | { | 751 | { |
739 | size = MIN(desc->size, 32768); | 752 | pcm_play_callback(&start, &size); |
740 | start = desc->addr; | ||
741 | last_chunksize = size; | ||
742 | desc->size -= size; | ||
743 | desc->addr += size; | ||
744 | pcm_play_data(start, size, pcm_play_callback); | 753 | pcm_play_data(start, size, pcm_play_callback); |
745 | } | 754 | } |
746 | } | 755 | } |