diff options
-rw-r--r-- | apps/playback.c | 65 | ||||
-rw-r--r-- | apps/plugins/codecmpa.c | 1 | ||||
-rw-r--r-- | firmware/export/pcm_playback.h | 1 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 76 |
4 files changed, 111 insertions, 32 deletions
diff --git a/apps/playback.c b/apps/playback.c index 3a9f229e15..c0c71a173d 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -68,7 +68,7 @@ static volatile bool paused; | |||
68 | #define CODEC_WAV "/.rockbox/codecs/codecwav.rock"; | 68 | #define CODEC_WAV "/.rockbox/codecs/codecwav.rock"; |
69 | 69 | ||
70 | #define AUDIO_WATERMARK 0x70000 | 70 | #define AUDIO_WATERMARK 0x70000 |
71 | #define AUDIO_FILE_CHUNK (1024*256) | 71 | #define AUDIO_FILE_CHUNK (1024*512) |
72 | 72 | ||
73 | #define AUDIO_PLAY 1 | 73 | #define AUDIO_PLAY 1 |
74 | #define AUDIO_STOP 2 | 74 | #define AUDIO_STOP 2 |
@@ -107,6 +107,9 @@ static const char codec_thread_name[] = "codec"; | |||
107 | /* Is file buffer currently being refilled? */ | 107 | /* Is file buffer currently being refilled? */ |
108 | static volatile bool filling; | 108 | static volatile bool filling; |
109 | 109 | ||
110 | /* Interrupts buffer filling. */ | ||
111 | static volatile bool interrupt; | ||
112 | |||
110 | /* Ring buffer where tracks and codecs are loaded. */ | 113 | /* Ring buffer where tracks and codecs are loaded. */ |
111 | char *codecbuf; | 114 | char *codecbuf; |
112 | 115 | ||
@@ -398,8 +401,8 @@ int probe_file_format(const char *filename) | |||
398 | 401 | ||
399 | void yield_codecs(void) | 402 | void yield_codecs(void) |
400 | { | 403 | { |
401 | #ifndef SIMULATOR | ||
402 | yield(); | 404 | yield(); |
405 | #ifndef SIMULATOR | ||
403 | if (!pcm_is_playing()) | 406 | if (!pcm_is_playing()) |
404 | sleep(5); | 407 | sleep(5); |
405 | while (pcm_is_lowdata()) | 408 | while (pcm_is_lowdata()) |
@@ -420,7 +423,7 @@ void audio_fill_file_buffer(void) | |||
420 | /* Give codecs some processing time. */ | 423 | /* Give codecs some processing time. */ |
421 | yield_codecs(); | 424 | yield_codecs(); |
422 | 425 | ||
423 | if (!playing) { | 426 | if (interrupt) { |
424 | logf("Filling interrupted"); | 427 | logf("Filling interrupted"); |
425 | close(current_fd); | 428 | close(current_fd); |
426 | current_fd = -1; | 429 | current_fd = -1; |
@@ -540,7 +543,7 @@ bool loadcodec(const char *trackname, bool start_play) | |||
540 | i = 0; | 543 | i = 0; |
541 | while (i < size) { | 544 | while (i < size) { |
542 | yield_codecs(); | 545 | yield_codecs(); |
543 | if (!playing) { | 546 | if (interrupt) { |
544 | logf("Buffering interrupted"); | 547 | logf("Buffering interrupted"); |
545 | close(fd); | 548 | close(fd); |
546 | return false; | 549 | return false; |
@@ -610,7 +613,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
610 | logf("%s", trackname); | 613 | logf("%s", trackname); |
611 | logf("Buffering track:%d/%d", track_widx, track_ridx); | 614 | logf("Buffering track:%d/%d", track_widx, track_ridx); |
612 | 615 | ||
613 | if (!playing) { | 616 | if (interrupt) { |
614 | close(fd); | 617 | close(fd); |
615 | return false; | 618 | return false; |
616 | } | 619 | } |
@@ -717,7 +720,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
717 | while (i < size) { | 720 | while (i < size) { |
718 | /* Give codecs some processing time to prevent glitches. */ | 721 | /* Give codecs some processing time to prevent glitches. */ |
719 | yield_codecs(); | 722 | yield_codecs(); |
720 | if (!playing) { | 723 | if (interrupt) { |
721 | logf("Buffering interrupted"); | 724 | logf("Buffering interrupted"); |
722 | close(fd); | 725 | close(fd); |
723 | return false; | 726 | return false; |
@@ -803,8 +806,14 @@ void audio_check_buffer(void) | |||
803 | int i; | 806 | int i; |
804 | int cur_idx; | 807 | int cur_idx; |
805 | 808 | ||
809 | /* Fill buffer as full as possible for cross-fader. */ | ||
810 | #ifndef SIMULATOR | ||
811 | if (cur_ti->id3.length - cur_ti->id3.elapsed < 20000) | ||
812 | pcm_set_boost_mode(true); | ||
813 | #endif | ||
814 | |||
806 | /* Start buffer filling as necessary. */ | 815 | /* Start buffer filling as necessary. */ |
807 | if (codecbufused > AUDIO_WATERMARK || !playing) | 816 | if (codecbufused > AUDIO_WATERMARK || !interrupt) |
808 | return ; | 817 | return ; |
809 | 818 | ||
810 | filling = true; | 819 | filling = true; |
@@ -863,6 +872,9 @@ void audio_update_trackinfo(void) | |||
863 | buf_ridx += cur_ti->codecsize; | 872 | buf_ridx += cur_ti->codecsize; |
864 | if (buf_ridx >= codecbuflen) | 873 | if (buf_ridx >= codecbuflen) |
865 | buf_ridx -= codecbuflen; | 874 | buf_ridx -= codecbuflen; |
875 | pcm_crossfade_start(); | ||
876 | if (!filling) | ||
877 | pcm_set_boost_mode(false); | ||
866 | } else { | 878 | } else { |
867 | buf_ridx -= ci.curpos; | 879 | buf_ridx -= ci.curpos; |
868 | codecbufused += ci.curpos; | 880 | codecbufused += ci.curpos; |
@@ -978,14 +990,13 @@ void audio_thread(void) | |||
978 | queue_wait_w_tmo(&audio_queue, &ev, 0); | 990 | queue_wait_w_tmo(&audio_queue, &ev, 0); |
979 | switch (ev.id) { | 991 | switch (ev.id) { |
980 | case AUDIO_PLAY: | 992 | case AUDIO_PLAY: |
993 | interrupt = false; | ||
981 | ci.stop_codec = true; | 994 | ci.stop_codec = true; |
982 | ci.reload_codec = false; | 995 | ci.reload_codec = false; |
983 | ci.seek_time = 0; | 996 | ci.seek_time = 0; |
984 | #ifndef SIMULATOR | 997 | #ifndef SIMULATOR |
985 | pcm_play_stop(); | 998 | pcm_play_stop(); |
986 | pcm_play_pause(true); | ||
987 | #endif | 999 | #endif |
988 | playing = true; | ||
989 | paused = false; | 1000 | paused = false; |
990 | audio_play_start((int)ev.data); | 1001 | audio_play_start((int)ev.data); |
991 | break ; | 1002 | break ; |
@@ -993,8 +1004,8 @@ void audio_thread(void) | |||
993 | case AUDIO_STOP: | 1004 | case AUDIO_STOP: |
994 | #ifndef SIMULATOR | 1005 | #ifndef SIMULATOR |
995 | pcm_play_stop(); | 1006 | pcm_play_stop(); |
1007 | pcm_play_pause(true); | ||
996 | #endif | 1008 | #endif |
997 | paused = false; | ||
998 | break ; | 1009 | break ; |
999 | 1010 | ||
1000 | case AUDIO_PAUSE: | 1011 | case AUDIO_PAUSE: |
@@ -1017,6 +1028,7 @@ void audio_thread(void) | |||
1017 | ci.stop_codec = true; | 1028 | ci.stop_codec = true; |
1018 | logf("USB Connection"); | 1029 | logf("USB Connection"); |
1019 | pcm_play_stop(); | 1030 | pcm_play_stop(); |
1031 | pcm_play_pause(true); | ||
1020 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 1032 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
1021 | usb_wait_for_disconnect(&audio_queue); | 1033 | usb_wait_for_disconnect(&audio_queue); |
1022 | break ; | 1034 | break ; |
@@ -1076,12 +1088,14 @@ void codec_thread(void) | |||
1076 | logf("Codec finished"); | 1088 | logf("Codec finished"); |
1077 | } | 1089 | } |
1078 | 1090 | ||
1079 | queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status); | ||
1080 | if (playing && !ci.stop_codec && !ci.reload_codec) { | 1091 | if (playing && !ci.stop_codec && !ci.reload_codec) { |
1081 | audio_change_track(); | 1092 | audio_change_track(); |
1093 | } else if (ci.reload_codec) { | ||
1094 | interrupt = true; | ||
1082 | } else { | 1095 | } else { |
1083 | playing = false; | 1096 | playing = false; |
1084 | } | 1097 | } |
1098 | queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status); | ||
1085 | } | 1099 | } |
1086 | } | 1100 | } |
1087 | } | 1101 | } |
@@ -1129,17 +1143,17 @@ bool audio_has_changed_track(void) | |||
1129 | void audio_play(int offset) | 1143 | void audio_play(int offset) |
1130 | { | 1144 | { |
1131 | logf("audio_play"); | 1145 | logf("audio_play"); |
1132 | playing = false; | ||
1133 | ci.stop_codec = true; | 1146 | ci.stop_codec = true; |
1147 | playing = false; | ||
1148 | #ifndef SIMULATOR | ||
1149 | pcm_play_pause(true); | ||
1150 | #endif | ||
1134 | queue_post(&audio_queue, AUDIO_PLAY, (void *)offset); | 1151 | queue_post(&audio_queue, AUDIO_PLAY, (void *)offset); |
1135 | } | 1152 | } |
1136 | 1153 | ||
1137 | void audio_stop(void) | 1154 | void audio_stop(void) |
1138 | { | 1155 | { |
1139 | logf("audio_stop"); | 1156 | logf("audio_stop"); |
1140 | if (!playing) | ||
1141 | return ; | ||
1142 | |||
1143 | playing = false; | 1157 | playing = false; |
1144 | ci.stop_codec = true; | 1158 | ci.stop_codec = true; |
1145 | if (current_fd) { | 1159 | if (current_fd) { |
@@ -1147,6 +1161,9 @@ void audio_stop(void) | |||
1147 | current_fd = -1; | 1161 | current_fd = -1; |
1148 | } | 1162 | } |
1149 | queue_post(&audio_queue, AUDIO_STOP, 0); | 1163 | queue_post(&audio_queue, AUDIO_STOP, 0); |
1164 | #ifndef SIMULATOR | ||
1165 | pcm_play_pause(true); | ||
1166 | #endif | ||
1150 | } | 1167 | } |
1151 | 1168 | ||
1152 | void audio_pause(void) | 1169 | void audio_pause(void) |
@@ -1174,16 +1191,17 @@ void audio_next(void) | |||
1174 | logf("audio_next"); | 1191 | logf("audio_next"); |
1175 | new_track = 1; | 1192 | new_track = 1; |
1176 | ci.reload_codec = true; | 1193 | ci.reload_codec = true; |
1177 | #ifndef SIMULATOR | ||
1178 | pcm_play_stop(); | ||
1179 | #endif | ||
1180 | 1194 | ||
1181 | /* Detect if disk is spinning.. */ | 1195 | /* Detect if disk is spinning.. */ |
1182 | if (filling) { | 1196 | if (filling) { |
1183 | playlist_next(1); | 1197 | interrupt = true; |
1184 | playing = false; | ||
1185 | ci.stop_codec = true; | 1198 | ci.stop_codec = true; |
1199 | playlist_next(1); | ||
1186 | queue_post(&audio_queue, AUDIO_PLAY, 0); | 1200 | queue_post(&audio_queue, AUDIO_PLAY, 0); |
1201 | } else { | ||
1202 | #ifndef SIMULATOR | ||
1203 | pcm_play_stop(); | ||
1204 | #endif | ||
1187 | } | 1205 | } |
1188 | } | 1206 | } |
1189 | 1207 | ||
@@ -1197,9 +1215,9 @@ void audio_prev(void) | |||
1197 | #endif | 1215 | #endif |
1198 | 1216 | ||
1199 | if (filling) { | 1217 | if (filling) { |
1200 | playlist_next(-1); | 1218 | interrupt = true; |
1201 | playing = false; | ||
1202 | ci.stop_codec = true; | 1219 | ci.stop_codec = true; |
1220 | playlist_next(-1); | ||
1203 | queue_post(&audio_queue, AUDIO_PLAY, 0); | 1221 | queue_post(&audio_queue, AUDIO_PLAY, 0); |
1204 | } | 1222 | } |
1205 | //queue_post(&audio_queue, AUDIO_PREV, 0); | 1223 | //queue_post(&audio_queue, AUDIO_PREV, 0); |
@@ -1377,7 +1395,8 @@ void audio_init(void) | |||
1377 | - MALLOC_BUFSIZE - GUARD_BUFSIZE; | 1395 | - MALLOC_BUFSIZE - GUARD_BUFSIZE; |
1378 | //codecbuflen = 2*512*1024; | 1396 | //codecbuflen = 2*512*1024; |
1379 | codecbufused = 0; | 1397 | codecbufused = 0; |
1380 | filling = 0; | 1398 | filling = false; |
1399 | interrupt = false; | ||
1381 | codecbuf = &audiobuf[MALLOC_BUFSIZE]; | 1400 | codecbuf = &audiobuf[MALLOC_BUFSIZE]; |
1382 | playing = false; | 1401 | playing = false; |
1383 | paused = false; | 1402 | paused = false; |
diff --git a/apps/plugins/codecmpa.c b/apps/plugins/codecmpa.c index 1125b4bf18..46014127b8 100644 --- a/apps/plugins/codecmpa.c +++ b/apps/plugins/codecmpa.c | |||
@@ -381,6 +381,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parm) | |||
381 | /* Flush the buffer if it is full. */ | 381 | /* Flush the buffer if it is full. */ |
382 | if(OutputPtr==OutputBufferEnd) | 382 | if(OutputPtr==OutputBufferEnd) |
383 | { | 383 | { |
384 | rb->yield(); | ||
384 | #ifdef DEBUG_GAPLESS | 385 | #ifdef DEBUG_GAPLESS |
385 | rb->write(fd, OutputBuffer, OUTPUT_BUFFER_SIZE); | 386 | rb->write(fd, OutputBuffer, OUTPUT_BUFFER_SIZE); |
386 | #endif | 387 | #endif |
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index 3c29018394..d101c823ba 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h | |||
@@ -42,6 +42,7 @@ void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)); | |||
42 | 42 | ||
43 | void pcm_set_boost_mode(bool state); | 43 | void pcm_set_boost_mode(bool state); |
44 | bool pcm_is_lowdata(void); | 44 | bool pcm_is_lowdata(void); |
45 | void pcm_crossfade_start(void); | ||
45 | unsigned int audiobuffer_get_latency(void); | 46 | unsigned int audiobuffer_get_latency(void); |
46 | bool audiobuffer_insert(char *buf, size_t length); | 47 | bool audiobuffer_insert(char *buf, size_t length); |
47 | 48 | ||
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index 99a71c2f41..ab4f0c0375 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -56,6 +56,11 @@ static volatile size_t audiobuffer_free; | |||
56 | static size_t audiobuffer_fillpos; | 56 | static size_t audiobuffer_fillpos; |
57 | static bool boost_mode; | 57 | static bool boost_mode; |
58 | 58 | ||
59 | static bool crossfade_active; | ||
60 | static int crossfade_pos; | ||
61 | static int crossfade_amount; | ||
62 | static int crossfade_rem; | ||
63 | |||
59 | static unsigned char *next_start; | 64 | static unsigned char *next_start; |
60 | static long next_size; | 65 | static long next_size; |
61 | 66 | ||
@@ -106,6 +111,9 @@ void pcm_boost(bool state) | |||
106 | { | 111 | { |
107 | static bool boost_state = false; | 112 | static bool boost_state = false; |
108 | 113 | ||
114 | if (crossfade_active) | ||
115 | return ; | ||
116 | |||
109 | if (state != boost_state) { | 117 | if (state != boost_state) { |
110 | cpu_boost(state); | 118 | cpu_boost(state); |
111 | boost_state = state; | 119 | boost_state = state; |
@@ -334,6 +342,7 @@ void pcm_watermark_callback(int bytes_left) | |||
334 | 342 | ||
335 | /* Fill audio buffer by boosting cpu */ | 343 | /* Fill audio buffer by boosting cpu */ |
336 | pcm_boost(true); | 344 | pcm_boost(true); |
345 | crossfade_active = false; | ||
337 | } | 346 | } |
338 | 347 | ||
339 | void pcm_set_boost_mode(bool state) | 348 | void pcm_set_boost_mode(bool state) |
@@ -373,11 +382,43 @@ bool pcm_is_lowdata(void) | |||
373 | return false; | 382 | return false; |
374 | } | 383 | } |
375 | 384 | ||
385 | void pcm_crossfade_start(void) | ||
386 | { | ||
387 | if (audiobuffer_free > CHUNK_SIZE * 4) { | ||
388 | return ; | ||
389 | } | ||
390 | pcm_boost(true); | ||
391 | crossfade_active = true; | ||
392 | crossfade_pos = audiobuffer_pos; | ||
393 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE * 22)/2; | ||
394 | crossfade_rem = crossfade_amount; | ||
395 | audiobuffer_fillpos = 0; | ||
396 | |||
397 | crossfade_pos -= crossfade_amount*2; | ||
398 | if (crossfade_pos < 0) | ||
399 | crossfade_pos = PCMBUF_SIZE + crossfade_pos; | ||
400 | } | ||
401 | |||
402 | static __inline | ||
403 | void crossfade(short *buf, const short *buf2, int length) | ||
404 | { | ||
405 | while (length--) { | ||
406 | *buf = (int)((*buf * ((crossfade_rem)*1000/crossfade_amount))/1000); | ||
407 | *buf += (int)((*buf2 * ((crossfade_amount-crossfade_rem)*1000/crossfade_amount))/1000); | ||
408 | buf++; | ||
409 | buf2++; | ||
410 | if (--crossfade_rem <= 0) { | ||
411 | crossfade_active = false; | ||
412 | break ; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | |||
376 | bool audiobuffer_insert(char *buf, size_t length) | 417 | bool audiobuffer_insert(char *buf, size_t length) |
377 | { | 418 | { |
378 | size_t copy_n = 0; | 419 | size_t copy_n = 0; |
379 | 420 | ||
380 | if (audiobuffer_free < length + CHUNK_SIZE) { | 421 | if (audiobuffer_free < length + CHUNK_SIZE && !crossfade_active) { |
381 | if (!boost_mode) | 422 | if (!boost_mode) |
382 | pcm_boost(false); | 423 | pcm_boost(false); |
383 | return false; | 424 | return false; |
@@ -385,19 +426,35 @@ bool audiobuffer_insert(char *buf, size_t length) | |||
385 | 426 | ||
386 | if (!pcm_is_playing() && !pcm_paused) { | 427 | if (!pcm_is_playing() && !pcm_paused) { |
387 | pcm_boost(true); | 428 | pcm_boost(true); |
429 | crossfade_active = false; | ||
388 | if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*2) | 430 | if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*2) |
389 | pcm_play_start(); | 431 | pcm_play_start(); |
390 | } | 432 | } |
391 | 433 | ||
392 | while (length > 0) { | 434 | while (length > 0) { |
393 | copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos - | 435 | if (!crossfade_active) { |
394 | audiobuffer_fillpos); | 436 | copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos - |
395 | copy_n = MIN(CHUNK_SIZE, copy_n); | 437 | audiobuffer_fillpos); |
396 | memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos], | 438 | copy_n = MIN(CHUNK_SIZE, copy_n); |
397 | buf, copy_n); | 439 | |
398 | buf += copy_n; | 440 | memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos], |
399 | audiobuffer_free -= copy_n; | 441 | buf, copy_n); |
400 | length -= copy_n; | 442 | buf += copy_n; |
443 | audiobuffer_free -= copy_n; | ||
444 | length -= copy_n; | ||
445 | |||
446 | } else { | ||
447 | copy_n = MIN(length, PCMBUF_SIZE - (unsigned int)crossfade_pos); | ||
448 | |||
449 | crossfade((short *)&audiobuffer[crossfade_pos], | ||
450 | (const short *)buf, copy_n/2); | ||
451 | buf += copy_n; | ||
452 | length -= copy_n; | ||
453 | crossfade_pos += copy_n; | ||
454 | if (crossfade_pos >= PCMBUF_SIZE) | ||
455 | crossfade_pos -= PCMBUF_SIZE; | ||
456 | continue ; | ||
457 | } | ||
401 | 458 | ||
402 | /* Pre-buffer to meet CHUNK_SIZE requirement */ | 459 | /* Pre-buffer to meet CHUNK_SIZE requirement */ |
403 | if (copy_n + audiobuffer_fillpos < CHUNK_SIZE && length == 0) { | 460 | if (copy_n + audiobuffer_fillpos < CHUNK_SIZE && length == 0) { |
@@ -452,6 +509,7 @@ void pcm_play_start(void) | |||
452 | int size; | 509 | int size; |
453 | char *start; | 510 | char *start; |
454 | 511 | ||
512 | crossfade_active = false; | ||
455 | if(!pcm_is_playing()) | 513 | if(!pcm_is_playing()) |
456 | { | 514 | { |
457 | size = MIN(desc->size, 32768); | 515 | size = MIN(desc->size, 32768); |