summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c112
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 */
93static unsigned int fade_vol = MIX_AMP_UNITY; 93static unsigned int fade_vol = MIX_AMP_UNITY;
94static enum
95{
96 PCM_NOT_FADING = 0,
97 PCM_FADING_IN,
98 PCM_FADING_OUT,
99} fade_state = PCM_NOT_FADING;
100static bool fade_out_complete = false;
94 101
95/* Voice */ 102/* Voice */
96static bool soft_mode = false; 103static 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. */
629static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) 636static 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 */
1088void pcmbuf_soft_mode(bool shhh) 1099void 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 */
1095void pcmbuf_fade(bool fade, bool in) 1111static 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 { 1139void 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 */
1166bool pcmbuf_fading(void)
1167{
1168 return fade_state != PCM_NOT_FADING;
1169}
1142 1170
1143/** Misc */ 1171/** Misc */
1144 1172