diff options
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r-- | apps/pcmbuf.c | 112 |
1 files changed, 70 insertions, 42 deletions
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 | ||