diff options
-rw-r--r-- | apps/gui/wps.c | 49 | ||||
-rw-r--r-- | apps/pcmbuf.c | 112 | ||||
-rw-r--r-- | apps/pcmbuf.h | 1 | ||||
-rw-r--r-- | apps/playback.c | 40 |
4 files changed, 130 insertions, 72 deletions
diff --git a/apps/gui/wps.c b/apps/gui/wps.c index 80522d04bd..48d9e0912e 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c | |||
@@ -119,14 +119,28 @@ char* wps_default_skin(enum screen_type screen) | |||
119 | return skin_buf[screen]; | 119 | return skin_buf[screen]; |
120 | } | 120 | } |
121 | 121 | ||
122 | static void update_non_static(void) | ||
123 | { | ||
124 | int i; | ||
125 | FOR_NB_SCREENS(i) | ||
126 | skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); | ||
127 | } | ||
128 | |||
122 | void pause_action(bool may_fade, bool updatewps) | 129 | void pause_action(bool may_fade, bool updatewps) |
123 | { | 130 | { |
124 | #if CONFIG_CODEC != SWCODEC | 131 | #if CONFIG_CODEC == SWCODEC |
132 | /* Do audio first, then update, unless skin were to use its local | ||
133 | status in which case, reverse it */ | ||
134 | audio_pause(); | ||
135 | |||
136 | if (updatewps) | ||
137 | update_non_static(); | ||
138 | #else | ||
125 | if (may_fade && global_settings.fade_on_stop) | 139 | if (may_fade && global_settings.fade_on_stop) |
126 | fade(false, updatewps); | 140 | fade(false, updatewps); |
127 | else | 141 | else |
128 | #endif | ||
129 | audio_pause(); | 142 | audio_pause(); |
143 | #endif | ||
130 | 144 | ||
131 | if (global_settings.pause_rewind) { | 145 | if (global_settings.pause_rewind) { |
132 | long newpos; | 146 | long newpos; |
@@ -139,18 +153,26 @@ void pause_action(bool may_fade, bool updatewps) | |||
139 | audio_ff_rewind(newpos > 0 ? newpos : 0); | 153 | audio_ff_rewind(newpos > 0 ? newpos : 0); |
140 | } | 154 | } |
141 | 155 | ||
142 | (void)may_fade; (void)updatewps; | 156 | (void)may_fade; |
143 | } | 157 | } |
144 | 158 | ||
145 | void unpause_action(bool may_fade, bool updatewps) | 159 | void unpause_action(bool may_fade, bool updatewps) |
146 | { | 160 | { |
147 | #if CONFIG_CODEC != SWCODEC | 161 | #if CONFIG_CODEC == SWCODEC |
162 | /* Do audio first, then update, unless skin were to use its local | ||
163 | status in which case, reverse it */ | ||
164 | audio_resume(); | ||
165 | |||
166 | if (updatewps) | ||
167 | update_non_static(); | ||
168 | #else | ||
148 | if (may_fade && global_settings.fade_on_stop) | 169 | if (may_fade && global_settings.fade_on_stop) |
149 | fade(true, updatewps); | 170 | fade(true, updatewps); |
150 | else | 171 | else |
151 | #endif | ||
152 | audio_resume(); | 172 | audio_resume(); |
153 | (void)may_fade; (void)updatewps; | 173 | #endif |
174 | |||
175 | (void)may_fade; | ||
154 | } | 176 | } |
155 | 177 | ||
156 | #if CONFIG_CODEC != SWCODEC | 178 | #if CONFIG_CODEC != SWCODEC |
@@ -159,7 +181,7 @@ void fade(bool fade_in, bool updatewps) | |||
159 | int fp_global_vol = global_settings.volume << 8; | 181 | int fp_global_vol = global_settings.volume << 8; |
160 | int fp_min_vol = sound_min(SOUND_VOLUME) << 8; | 182 | int fp_min_vol = sound_min(SOUND_VOLUME) << 8; |
161 | int fp_step = (fp_global_vol - fp_min_vol) / 10; | 183 | int fp_step = (fp_global_vol - fp_min_vol) / 10; |
162 | int i; | 184 | |
163 | skin_get_global_state()->is_fading = !fade_in; | 185 | skin_get_global_state()->is_fading = !fade_in; |
164 | if (fade_in) { | 186 | if (fade_in) { |
165 | /* fade in */ | 187 | /* fade in */ |
@@ -171,10 +193,8 @@ void fade(bool fade_in, bool updatewps) | |||
171 | sleep(HZ/10); /* let audio thread run */ | 193 | sleep(HZ/10); /* let audio thread run */ |
172 | audio_resume(); | 194 | audio_resume(); |
173 | 195 | ||
174 | if (updatewps) { | 196 | if (updatewps) |
175 | FOR_NB_SCREENS(i) | 197 | update_non_static(); |
176 | skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); | ||
177 | } | ||
178 | 198 | ||
179 | while (fp_volume < fp_global_vol - fp_step) { | 199 | while (fp_volume < fp_global_vol - fp_step) { |
180 | fp_volume += fp_step; | 200 | fp_volume += fp_step; |
@@ -187,10 +207,8 @@ void fade(bool fade_in, bool updatewps) | |||
187 | /* fade out */ | 207 | /* fade out */ |
188 | int fp_volume = fp_global_vol; | 208 | int fp_volume = fp_global_vol; |
189 | 209 | ||
190 | if (updatewps) { | 210 | if (updatewps) |
191 | FOR_NB_SCREENS(i) | 211 | update_non_static(); |
192 | skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); | ||
193 | } | ||
194 | 212 | ||
195 | while (fp_volume > fp_min_vol + fp_step) { | 213 | while (fp_volume > fp_min_vol + fp_step) { |
196 | fp_volume -= fp_step; | 214 | fp_volume -= fp_step; |
@@ -1140,6 +1158,7 @@ long gui_wps_show(void) | |||
1140 | fade(false, true); | 1158 | fade(false, true); |
1141 | #else | 1159 | #else |
1142 | audio_pause(); | 1160 | audio_pause(); |
1161 | update_non_static(); | ||
1143 | #endif | 1162 | #endif |
1144 | if (bookmark) | 1163 | if (bookmark) |
1145 | bookmark_autobookmark(true); | 1164 | bookmark_autobookmark(true); |
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 2e8bc3f47c..2ba6b6f154 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -91,6 +91,13 @@ static bool track_transition; | |||
91 | 91 | ||
92 | /* Fade effect */ | 92 | /* Fade effect */ |
93 | static unsigned int fade_vol = MIX_AMP_UNITY; | 93 | static unsigned int fade_vol = MIX_AMP_UNITY; |
94 | static enum | ||
95 | { | ||
96 | PCM_NOT_FADING = 0, | ||
97 | PCM_FADING_IN, | ||
98 | PCM_FADING_OUT, | ||
99 | } fade_state = PCM_NOT_FADING; | ||
100 | static bool fade_out_complete = false; | ||
94 | 101 | ||
95 | /* Voice */ | 102 | /* Voice */ |
96 | static bool soft_mode = false; | 103 | static bool soft_mode = false; |
@@ -628,8 +635,9 @@ bool pcmbuf_start_track_change(bool auto_skip) | |||
628 | * operations involved in sending a new chunk to the DMA. */ | 635 | * operations involved in sending a new chunk to the DMA. */ |
629 | static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) | 636 | static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) |
630 | { | 637 | { |
638 | struct chunkdesc *pcmbuf_current = read_chunk; | ||
639 | |||
631 | { | 640 | { |
632 | struct chunkdesc *pcmbuf_current = read_chunk; | ||
633 | /* Take the finished chunk out of circulation */ | 641 | /* Take the finished chunk out of circulation */ |
634 | read_chunk = pcmbuf_current->link; | 642 | read_chunk = pcmbuf_current->link; |
635 | 643 | ||
@@ -657,25 +665,28 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) | |||
657 | 665 | ||
658 | { | 666 | { |
659 | /* Commit last samples at end of playlist */ | 667 | /* Commit last samples at end of playlist */ |
660 | if (pcmbuffer_fillpos && !read_chunk) | 668 | if (pcmbuffer_fillpos && !pcmbuf_current) |
661 | { | 669 | { |
662 | logf("pcmbuf_pcm_callback: commit last samples"); | 670 | logf("pcmbuf_pcm_callback: commit last samples"); |
663 | commit_chunk(false); | 671 | commit_chunk(false); |
664 | } | 672 | } |
665 | } | 673 | } |
666 | 674 | ||
675 | /* Stop at this frame */ | ||
676 | pcmbuf_current = fade_out_complete ? NULL : read_chunk; | ||
677 | |||
667 | { | 678 | { |
668 | /* Send the new chunk to the DMA */ | 679 | /* Send the new chunk to the DMA */ |
669 | if(read_chunk) | 680 | if(pcmbuf_current) |
670 | { | 681 | { |
671 | last_chunksize = read_chunk->size; | 682 | last_chunksize = pcmbuf_current->size; |
672 | pcmbuf_unplayed_bytes -= last_chunksize; | 683 | pcmbuf_unplayed_bytes -= last_chunksize; |
673 | *size = last_chunksize; | 684 | *size = last_chunksize; |
674 | *start = read_chunk->addr; | 685 | *start = pcmbuf_current->addr; |
675 | } | 686 | } |
676 | else | 687 | else |
677 | { | 688 | { |
678 | /* No more chunks */ | 689 | /* No more chunks or pause indicated */ |
679 | logf("pcmbuf_pcm_callback: no more chunks"); | 690 | logf("pcmbuf_pcm_callback: no more chunks"); |
680 | last_chunksize = 0; | 691 | last_chunksize = 0; |
681 | *size = 0; | 692 | *size = 0; |
@@ -694,8 +705,8 @@ void pcmbuf_play_start(void) | |||
694 | logf("pcmbuf_play_start"); | 705 | logf("pcmbuf_play_start"); |
695 | last_chunksize = read_chunk->size; | 706 | last_chunksize = read_chunk->size; |
696 | pcmbuf_unplayed_bytes -= last_chunksize; | 707 | pcmbuf_unplayed_bytes -= last_chunksize; |
697 | mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, | 708 | mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, pcmbuf_pcm_callback, |
698 | pcmbuf_pcm_callback, NULL, 0); | 709 | read_chunk->addr, last_chunksize); |
699 | } | 710 | } |
700 | } | 711 | } |
701 | 712 | ||
@@ -1087,58 +1098,75 @@ static void pcmbuf_update_volume(void) | |||
1087 | /* Quiet-down the channel if 'shhh' is true or else play at normal level */ | 1098 | /* Quiet-down the channel if 'shhh' is true or else play at normal level */ |
1088 | void pcmbuf_soft_mode(bool shhh) | 1099 | void pcmbuf_soft_mode(bool shhh) |
1089 | { | 1100 | { |
1101 | /* "Hate this" alert (messing with IRQ in app code): Have to block | ||
1102 | the tick or improper order could leave volume in soft mode if | ||
1103 | fading reads the old value first but updates after us. */ | ||
1104 | int oldlevel = disable_irq_save(); | ||
1090 | soft_mode = shhh; | 1105 | soft_mode = shhh; |
1091 | pcmbuf_update_volume(); | 1106 | pcmbuf_update_volume(); |
1107 | restore_irq(oldlevel); | ||
1092 | } | 1108 | } |
1093 | 1109 | ||
1094 | /* Fade channel in or out */ | 1110 | /* Tick that does the fade for the playback channel */ |
1095 | void pcmbuf_fade(bool fade, bool in) | 1111 | static void pcmbuf_fade_tick(void) |
1096 | { | 1112 | { |
1097 | if (!fade) | 1113 | /* ~1/3 second for full range fade */ |
1098 | { | 1114 | const unsigned int fade_step = MIX_AMP_UNITY / (HZ / 3); |
1099 | /* Simply set the level */ | 1115 | |
1100 | fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE; | 1116 | if (fade_state == PCM_FADING_IN) |
1101 | } | 1117 | fade_vol += MIN(fade_step, MIX_AMP_UNITY - fade_vol); |
1102 | else | 1118 | else if (fade_state == PCM_FADING_OUT) |
1103 | { | 1119 | fade_vol -= MIN(fade_step, fade_vol - MIX_AMP_MUTE); |
1104 | /* Start from the opposing end */ | ||
1105 | fade_vol = in ? MIX_AMP_MUTE : MIX_AMP_UNITY; | ||
1106 | } | ||
1107 | 1120 | ||
1108 | pcmbuf_update_volume(); | 1121 | pcmbuf_update_volume(); |
1109 | 1122 | ||
1110 | if (fade) | 1123 | if (fade_vol == MIX_AMP_MUTE || fade_vol == MIX_AMP_UNITY) |
1111 | { | 1124 | { |
1112 | /* Do this on thread for now */ | 1125 | /* Fade is complete */ |
1113 | #ifdef HAVE_PRIORITY_SCHEDULING | 1126 | tick_remove_task(pcmbuf_fade_tick); |
1114 | int old_prio = thread_set_priority(thread_self(), PRIORITY_REALTIME); | ||
1115 | #endif | ||
1116 | 1127 | ||
1117 | while (1) | 1128 | if (fade_state == PCM_FADING_OUT) |
1118 | { | 1129 | { |
1119 | /* Linear fade actually sounds better */ | 1130 | /* Tell PCM to stop at its earliest convenience */ |
1120 | if (in) | 1131 | fade_out_complete = true; |
1121 | fade_vol += MIN(MIX_AMP_UNITY/16, MIX_AMP_UNITY - fade_vol); | 1132 | } |
1122 | else | ||
1123 | fade_vol -= MIN(MIX_AMP_UNITY/16, fade_vol - MIX_AMP_MUTE); | ||
1124 | 1133 | ||
1125 | pcmbuf_update_volume(); | 1134 | fade_state = PCM_NOT_FADING; |
1135 | } | ||
1136 | } | ||
1126 | 1137 | ||
1127 | if (fade_vol > MIX_AMP_MUTE && fade_vol < MIX_AMP_UNITY) | 1138 | /* Fade channel in or out in the background - must pause it first */ |
1128 | { | 1139 | void pcmbuf_fade(bool fade, bool in) |
1129 | sleep(0); | 1140 | { |
1130 | continue; | 1141 | pcm_play_lock(); |
1131 | } | ||
1132 | 1142 | ||
1133 | break; | 1143 | if (fade_state != PCM_NOT_FADING) |
1134 | } | 1144 | tick_remove_task(pcmbuf_fade_tick); |
1135 | 1145 | ||
1136 | #ifdef HAVE_PRIORITY_SCHEDULING | 1146 | fade_out_complete = false; |
1137 | thread_set_priority(thread_self(), old_prio); | 1147 | |
1138 | #endif | 1148 | pcm_play_unlock(); |
1149 | |||
1150 | if (!fade) | ||
1151 | { | ||
1152 | /* Simply set the level */ | ||
1153 | fade_state = PCM_NOT_FADING; | ||
1154 | fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE; | ||
1155 | pcmbuf_update_volume(); | ||
1156 | } | ||
1157 | else | ||
1158 | { | ||
1159 | /* Set direction and resume fade from current point */ | ||
1160 | fade_state = in ? PCM_FADING_IN : PCM_FADING_OUT; | ||
1161 | tick_add_task(pcmbuf_fade_tick); | ||
1139 | } | 1162 | } |
1140 | } | 1163 | } |
1141 | 1164 | ||
1165 | /* Return 'true' if fade is in progress */ | ||
1166 | bool pcmbuf_fading(void) | ||
1167 | { | ||
1168 | return fade_state != PCM_NOT_FADING; | ||
1169 | } | ||
1142 | 1170 | ||
1143 | /** Misc */ | 1171 | /** Misc */ |
1144 | 1172 | ||
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index b7f5a3c2d6..a5cd3163e1 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h | |||
@@ -65,6 +65,7 @@ unsigned char *pcmbuf_get_meminfo(size_t *length); | |||
65 | 65 | ||
66 | /* Misc */ | 66 | /* Misc */ |
67 | void pcmbuf_fade(bool fade, bool in); | 67 | void pcmbuf_fade(bool fade, bool in); |
68 | bool pcmbuf_fading(void); | ||
68 | void pcmbuf_soft_mode(bool shhh); | 69 | void pcmbuf_soft_mode(bool shhh); |
69 | bool pcmbuf_is_lowdata(void); | 70 | bool pcmbuf_is_lowdata(void); |
70 | void pcmbuf_set_low_latency(bool state); | 71 | void pcmbuf_set_low_latency(bool state); |
diff --git a/apps/playback.c b/apps/playback.c index a38534a2da..3adf6f6a49 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -1100,6 +1100,14 @@ static void audio_clear_paused_pcm(void) | |||
1100 | pcmbuf_play_stop(); | 1100 | pcmbuf_play_stop(); |
1101 | } | 1101 | } |
1102 | 1102 | ||
1103 | /* Wait for any in-progress fade to complete */ | ||
1104 | static void audio_wait_fade_complete(void) | ||
1105 | { | ||
1106 | /* Just loop until it's done */ | ||
1107 | while (pcmbuf_fading()) | ||
1108 | sleep(0); | ||
1109 | } | ||
1110 | |||
1103 | /* End the ff/rw mode */ | 1111 | /* End the ff/rw mode */ |
1104 | static void audio_ff_rewind_end(void) | 1112 | static void audio_ff_rewind_end(void) |
1105 | { | 1113 | { |
@@ -2439,7 +2447,12 @@ static void audio_stop_playback(void) | |||
2439 | if (play_status == PLAY_STOPPED) | 2447 | if (play_status == PLAY_STOPPED) |
2440 | return; | 2448 | return; |
2441 | 2449 | ||
2442 | pcmbuf_fade(global_settings.fade_on_stop, false); | 2450 | bool do_fade = global_settings.fade_on_stop && filling != STATE_ENDED; |
2451 | |||
2452 | pcmbuf_fade(do_fade, false); | ||
2453 | |||
2454 | /* Wait for fade-out */ | ||
2455 | audio_wait_fade_complete(); | ||
2443 | 2456 | ||
2444 | /* Stop the codec and unload it */ | 2457 | /* Stop the codec and unload it */ |
2445 | halt_decoding_track(true); | 2458 | halt_decoding_track(true); |
@@ -2480,27 +2493,22 @@ static void audio_on_pause(bool pause) | |||
2480 | if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) | 2493 | if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) |
2481 | return; | 2494 | return; |
2482 | 2495 | ||
2483 | bool const do_fade = global_settings.fade_on_stop; | 2496 | play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; |
2484 | |||
2485 | if (pause) | ||
2486 | pcmbuf_fade(do_fade, false); | ||
2487 | 2497 | ||
2488 | if (!ff_rw_mode) | 2498 | if (!pause && codec_skip_pending) |
2489 | { | 2499 | { |
2490 | /* Not in ff/rw mode - may set the state (otherwise this could make | 2500 | /* Actually do the skip that is due - resets the status flag */ |
2491 | old data play because seek hasn't completed and cleared it) */ | 2501 | audio_on_codec_complete(codec_skip_status); |
2492 | pcmbuf_pause(pause); | ||
2493 | } | 2502 | } |
2494 | 2503 | ||
2495 | if (!pause) | 2504 | bool do_fade = global_settings.fade_on_stop; |
2496 | pcmbuf_fade(do_fade, true); | ||
2497 | 2505 | ||
2498 | play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; | 2506 | pcmbuf_fade(do_fade, !pause); |
2499 | 2507 | ||
2500 | if (!pause && codec_skip_pending) | 2508 | if (!ff_rw_mode && !(do_fade && pause)) |
2501 | { | 2509 | { |
2502 | /* Actually do the skip that is due - resets the status flag */ | 2510 | /* Not in ff/rw mode - can actually change the audio state now */ |
2503 | audio_on_codec_complete(codec_skip_status); | 2511 | pcmbuf_pause(pause); |
2504 | } | 2512 | } |
2505 | } | 2513 | } |
2506 | 2514 | ||
@@ -2644,6 +2652,8 @@ static void audio_on_pre_ff_rewind(void) | |||
2644 | 2652 | ||
2645 | ff_rw_mode = true; | 2653 | ff_rw_mode = true; |
2646 | 2654 | ||
2655 | audio_wait_fade_complete(); | ||
2656 | |||
2647 | if (play_status == PLAY_PAUSED) | 2657 | if (play_status == PLAY_PAUSED) |
2648 | return; | 2658 | return; |
2649 | 2659 | ||