summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
commita2b6703a369f6cdbfec1f150c408dadc877631fb (patch)
tree3145a8c1372c44711d38feefeba39c7d4098f139 /apps/pcmbuf.c
parent8411614b8a068a4f274c3841aa55aab1df1bc246 (diff)
downloadrockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.tar.gz
rockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.zip
Commit FS#12150 - Fully-functional audio mixer - and finally whip old limitations about playback of voice and other sounds when paused. Channels are independent in state and amplitude. Fade on stop/pause is handled by the channel's volume control rather than global volume which means it now works from anywhere. Opens up the possibility of plugin sounds during music playback by merely adding an additional channel enum. If any PCM drivers were not properly modified, see one of the last comments in the task for a description of the simple change that is expected. Some params are tunable in firmware/export/pcm-mixer.h as well.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30097 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c319
1 files changed, 95 insertions, 224 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 7201d3981a..f2f94e3bc9 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -23,8 +23,9 @@
23#include "system.h" 23#include "system.h"
24#include "debug.h" 24#include "debug.h"
25#include <kernel.h> 25#include <kernel.h>
26#include "pcmbuf.h"
27#include "pcm.h" 26#include "pcm.h"
27#include "pcm_mixer.h"
28#include "pcmbuf.h"
28#include "playback.h" 29#include "playback.h"
29#include "codec_thread.h" 30#include "codec_thread.h"
30 31
@@ -49,9 +50,6 @@
49#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than 50#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
50 this to the DMA */ 51 this to the DMA */
51#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ 52#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */
52#define AUX_BUFSIZE 512 /* Size of the aux buffer; can be 512 if no
53 resampling or timestretching is allowed in
54 the aux channel, must be 2048 otherwise */
55 53
56/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */ 54/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */
57#define BYTERATE (NATIVE_FREQUENCY * 4) 55#define BYTERATE (NATIVE_FREQUENCY * 4)
@@ -91,6 +89,12 @@ static struct chunkdesc *first_desc;
91/* Gapless playback */ 89/* Gapless playback */
92static bool track_transition IDATA_ATTR; 90static bool track_transition IDATA_ATTR;
93 91
92/* Fade effect */
93static unsigned int fade_vol = MIX_AMP_UNITY;
94
95/* Voice */
96static bool soft_mode = false;
97
94#ifdef HAVE_CROSSFADE 98#ifdef HAVE_CROSSFADE
95/* Crossfade buffer */ 99/* Crossfade buffer */
96static char *fadebuf IDATA_ATTR; 100static char *fadebuf IDATA_ATTR;
@@ -121,11 +125,6 @@ static size_t last_chunksize IDATA_ATTR;
121static size_t pcmbuf_unplayed_bytes IDATA_ATTR; 125static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
122static size_t pcmbuf_watermark IDATA_ATTR; 126static size_t pcmbuf_watermark IDATA_ATTR;
123 127
124/* Voice */
125static char *voicebuf IDATA_ATTR;
126static struct chunkdesc *mix_chunk IDATA_ATTR;
127static size_t pcmbuf_mix_sample IDATA_ATTR;
128
129static bool low_latency_mode = false; 128static bool low_latency_mode = false;
130static bool flush_pcmbuf = false; 129static bool flush_pcmbuf = false;
131 130
@@ -317,10 +316,12 @@ static void boost_codec_thread(int pcm_fill_state)
317 * Also maintain buffer level above the watermark. */ 316 * Also maintain buffer level above the watermark. */
318static bool prepare_insert(size_t length) 317static bool prepare_insert(size_t length)
319{ 318{
319 bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED;
320
320 if (low_latency_mode) 321 if (low_latency_mode)
321 { 322 {
322 /* 1/4s latency. */ 323 /* 1/4s latency. */
323 if (!LOW_DATA(1) && pcm_is_playing()) 324 if (!LOW_DATA(1) && playing)
324 return false; 325 return false;
325 } 326 }
326 327
@@ -329,7 +330,7 @@ static bool prepare_insert(size_t length)
329 return false; 330 return false;
330 331
331 /* Maintain the buffer level above the watermark */ 332 /* Maintain the buffer level above the watermark */
332 if (pcm_is_playing()) 333 if (playing)
333 { 334 {
334 /* Only codec thread initiates boost - voice boosts the cpu when playing 335 /* Only codec thread initiates boost - voice boosts the cpu when playing
335 a clip */ 336 a clip */
@@ -351,7 +352,7 @@ static bool prepare_insert(size_t length)
351 } 352 }
352#endif 353#endif
353 } 354 }
354 else /* pcm_is_playing */ 355 else /* !playing */
355 { 356 {
356 /* Boost CPU for pre-buffer */ 357 /* Boost CPU for pre-buffer */
357 trigger_cpu_boost(); 358 trigger_cpu_boost();
@@ -469,11 +470,14 @@ static size_t get_next_required_pcmbuf_size(void)
469 * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ 470 * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */
470size_t pcmbuf_init(unsigned char *bufend) 471size_t pcmbuf_init(unsigned char *bufend)
471{ 472{
473 unsigned char *voicebuf;
474
472 pcmbuf_bufend = bufend; 475 pcmbuf_bufend = bufend;
473 pcmbuf_size = get_next_required_pcmbuf_size(); 476 pcmbuf_size = get_next_required_pcmbuf_size();
474 write_chunk = (struct chunkdesc *)pcmbuf_bufend - 477 write_chunk = (struct chunkdesc *)pcmbuf_bufend -
475 NUM_CHUNK_DESCS(pcmbuf_size); 478 NUM_CHUNK_DESCS(pcmbuf_size);
476 voicebuf = (char *)write_chunk - AUX_BUFSIZE; 479 voicebuf = (unsigned char *)write_chunk -
480 voicebuf_init((unsigned char *)write_chunk);
477#ifdef HAVE_CROSSFADE 481#ifdef HAVE_CROSSFADE
478 fadebuf = voicebuf - CROSSFADE_BUFSIZE; 482 fadebuf = voicebuf - CROSSFADE_BUFSIZE;
479 pcmbuffer = fadebuf - pcmbuf_size; 483 pcmbuffer = fadebuf - pcmbuf_size;
@@ -491,6 +495,8 @@ size_t pcmbuf_init(unsigned char *bufend)
491 495
492 pcmbuf_play_stop(); 496 pcmbuf_play_stop();
493 497
498 pcmbuf_soft_mode(false);
499
494 return pcmbuf_bufend - pcmbuffer; 500 return pcmbuf_bufend - pcmbuffer;
495} 501}
496 502
@@ -572,7 +578,7 @@ bool pcmbuf_start_track_change(bool auto_skip)
572#ifdef HAVE_CROSSFADE 578#ifdef HAVE_CROSSFADE
573 pcmbuf_is_crossfade_active() || 579 pcmbuf_is_crossfade_active() ||
574#endif 580#endif
575 !pcm_is_playing()) 581 mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED)
576 { 582 {
577 pcmbuf_play_stop(); 583 pcmbuf_play_stop();
578 pcm_play_unlock(); 584 pcm_play_unlock();
@@ -652,10 +658,6 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
652 write_end_chunk->link = pcmbuf_current; 658 write_end_chunk->link = pcmbuf_current;
653 write_end_chunk = pcmbuf_current; 659 write_end_chunk = pcmbuf_current;
654 660
655 /* If we've read over the mix chunk while it's still mixing there */
656 if (pcmbuf_current == mix_chunk)
657 mix_chunk = NULL;
658
659#ifdef HAVE_CROSSFADE 661#ifdef HAVE_CROSSFADE
660 /* If we've read over the crossfade chunk while it's still fading */ 662 /* If we've read over the crossfade chunk while it's still fading */
661 if (pcmbuf_current == crossfade_chunk) 663 if (pcmbuf_current == crossfade_chunk)
@@ -696,23 +698,23 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
696/* Force playback */ 698/* Force playback */
697void pcmbuf_play_start(void) 699void pcmbuf_play_start(void)
698{ 700{
699 if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL) 701 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
702 pcmbuf_unplayed_bytes && read_chunk != NULL)
700 { 703 {
701 logf("pcmbuf_play_start"); 704 logf("pcmbuf_play_start");
702 last_chunksize = read_chunk->size; 705 last_chunksize = read_chunk->size;
703 pcmbuf_unplayed_bytes -= last_chunksize; 706 pcmbuf_unplayed_bytes -= last_chunksize;
704 pcm_play_data(pcmbuf_pcm_callback, 707 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK,
705 read_chunk->addr, last_chunksize); 708 pcmbuf_pcm_callback, NULL, 0);
706 } 709 }
707} 710}
708 711
709void pcmbuf_play_stop(void) 712void pcmbuf_play_stop(void)
710{ 713{
711 logf("pcmbuf_play_stop"); 714 logf("pcmbuf_play_stop");
712 pcm_play_stop(); 715 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
713 716
714 pcmbuf_unplayed_bytes = 0; 717 pcmbuf_unplayed_bytes = 0;
715 mix_chunk = NULL;
716 if (read_chunk) { 718 if (read_chunk) {
717 write_end_chunk->link = read_chunk; 719 write_end_chunk->link = read_chunk;
718 write_end_chunk = read_end_chunk; 720 write_end_chunk = read_end_chunk;
@@ -737,8 +739,9 @@ void pcmbuf_play_stop(void)
737void pcmbuf_pause(bool pause) 739void pcmbuf_pause(bool pause)
738{ 740{
739 logf("pcmbuf_pause: %s", pause?"pause":"play"); 741 logf("pcmbuf_pause: %s", pause?"pause":"play");
740 if (pcm_is_playing()) 742
741 pcm_play_pause(!pause); 743 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
744 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
742 else if (!pause) 745 else if (!pause)
743 pcmbuf_play_start(); 746 pcmbuf_play_start();
744} 747}
@@ -1031,102 +1034,6 @@ bool pcmbuf_is_same_size(void)
1031#endif /* HAVE_CROSSFADE */ 1034#endif /* HAVE_CROSSFADE */
1032 1035
1033 1036
1034/** Voice */
1035
1036/* Returns pcm buffer usage in percents (0 to 100). */
1037static int pcmbuf_usage(void)
1038{
1039 return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
1040}
1041
1042static int pcmbuf_mix_free(void)
1043{
1044 if (mix_chunk)
1045 {
1046 size_t my_mix_end =
1047 (size_t)&((int16_t *)mix_chunk->addr)[pcmbuf_mix_sample];
1048 size_t my_write_pos = (size_t)&pcmbuffer[pcmbuffer_pos];
1049 if (my_write_pos < my_mix_end)
1050 my_write_pos += pcmbuf_size;
1051 return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes;
1052 }
1053 return 100;
1054}
1055
1056void *pcmbuf_request_voice_buffer(int *count)
1057{
1058 /* A get-it-to-work-for-now hack (audio status could change by
1059 completion) */
1060 if (audio_status() & AUDIO_STATUS_PLAY)
1061 {
1062 if (read_chunk == NULL)
1063 {
1064 return NULL;
1065 }
1066 else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
1067 (mix_chunk || read_chunk->link))
1068 {
1069 *count = MIN(*count, AUX_BUFSIZE/4);
1070 return voicebuf;
1071 }
1072 else
1073 {
1074 return NULL;
1075 }
1076 }
1077 else
1078 {
1079 return pcmbuf_request_buffer(count);
1080 }
1081}
1082
1083void pcmbuf_write_voice_complete(int count)
1084{
1085 /* A get-it-to-work-for-now hack (audio status could have changed) */
1086 if (!(audio_status() & AUDIO_STATUS_PLAY))
1087 {
1088 pcmbuf_write_complete(count);
1089 return;
1090 }
1091
1092 int16_t *ibuf = (int16_t *)voicebuf;
1093 int16_t *obuf;
1094 size_t chunk_samples;
1095
1096 if (mix_chunk == NULL && read_chunk != NULL)
1097 {
1098 mix_chunk = read_chunk->link;
1099 /* Start 1/8s into the next chunk */
1100 pcmbuf_mix_sample = BYTERATE / 16;
1101 }
1102
1103 if (!mix_chunk)
1104 return;
1105
1106 obuf = (int16_t *)mix_chunk->addr;
1107 chunk_samples = mix_chunk->size / sizeof (int16_t);
1108
1109 count <<= 1;
1110
1111 while (count-- > 0)
1112 {
1113 int32_t sample = *ibuf++;
1114
1115 if (pcmbuf_mix_sample >= chunk_samples)
1116 {
1117 mix_chunk = mix_chunk->link;
1118 if (!mix_chunk)
1119 return;
1120 pcmbuf_mix_sample = 0;
1121 obuf = (int16_t *)mix_chunk->addr;
1122 chunk_samples = mix_chunk->size / 2;
1123 }
1124 sample += obuf[pcmbuf_mix_sample] >> 2;
1125 obuf[pcmbuf_mix_sample++] = clip_sample_16(sample);
1126 }
1127}
1128
1129
1130/** Debug menu, other metrics */ 1037/** Debug menu, other metrics */
1131 1038
1132/* Amount of bytes left in the buffer. */ 1039/* Amount of bytes left in the buffer. */
@@ -1174,6 +1081,71 @@ unsigned char *pcmbuf_get_meminfo(size_t *length)
1174#endif 1081#endif
1175 1082
1176 1083
1084/** Fading and channel volume control */
1085
1086/* Sync the channel amplitude to all states */
1087static void pcmbuf_update_volume(void)
1088{
1089 unsigned int vol = fade_vol;
1090
1091 if (soft_mode)
1092 vol >>= 2;
1093
1094 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1095}
1096
1097/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1098void pcmbuf_soft_mode(bool shhh)
1099{
1100 soft_mode = shhh;
1101 pcmbuf_update_volume();
1102}
1103
1104/* Fade channel in or out */
1105void pcmbuf_fade(bool fade, bool in)
1106{
1107 if (!fade)
1108 {
1109 /* Simply set the level */
1110 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
1111 }
1112 else
1113 {
1114 /* Start from the opposing end */
1115 fade_vol = in ? MIX_AMP_MUTE : MIX_AMP_UNITY;
1116 }
1117
1118 pcmbuf_update_volume();
1119
1120 if (fade)
1121 {
1122 /* Do this on thread for now */
1123 int old_prio = thread_set_priority(thread_self(), PRIORITY_REALTIME);
1124
1125 while (1)
1126 {
1127 /* Linear fade actually sounds better */
1128 if (in)
1129 fade_vol += MIN(MIX_AMP_UNITY/16, MIX_AMP_UNITY - fade_vol);
1130 else
1131 fade_vol -= MIN(MIX_AMP_UNITY/16, fade_vol - MIX_AMP_MUTE);
1132
1133 pcmbuf_update_volume();
1134
1135 if (fade_vol > MIX_AMP_MUTE && fade_vol < MIX_AMP_UNITY)
1136 {
1137 sleep(0);
1138 continue;
1139 }
1140
1141 break;
1142 }
1143
1144 thread_set_priority(thread_self(), old_prio);
1145 }
1146}
1147
1148
1177/** Misc */ 1149/** Misc */
1178 1150
1179bool pcmbuf_is_lowdata(void) 1151bool pcmbuf_is_lowdata(void)
@@ -1201,107 +1173,6 @@ void pcmbuf_set_low_latency(bool state)
1201 1173
1202unsigned long pcmbuf_get_latency(void) 1174unsigned long pcmbuf_get_latency(void)
1203{ 1175{
1204 return (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting()) * 1000 / BYTERATE; 1176 return (pcmbuf_unplayed_bytes +
1205} 1177 mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE;
1206
1207#ifndef HAVE_HARDWARE_BEEP
1208#define MINIBUF_SAMPLES (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
1209#define MINIBUF_SIZE (MINIBUF_SAMPLES*4)
1210
1211/* Generates a constant square wave sound with a given frequency
1212 in Hertz for a duration in milliseconds. */
1213void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
1214{
1215 unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
1216 int32_t phase = 0;
1217 int16_t *bufptr, *bufstart, *bufend;
1218 int32_t sample;
1219 int nsamples = NATIVE_FREQUENCY / 1000 * duration;
1220 bool mix = read_chunk != NULL && read_chunk->link != NULL;
1221 int i;
1222
1223 bufend = SKIPBYTES((int16_t *)pcmbuffer, pcmbuf_size);
1224
1225 /* Find the insertion point and set bufstart to the start of it */
1226 if (mix)
1227 {
1228 /* Get the currently playing chunk at the current position. */
1229 bufstart = (int16_t *)pcm_play_dma_get_peak_buffer(&i);
1230
1231 /* If above isn't implemented or pcm is stopped, no beepeth. */
1232 if (!bufstart || !pcm_is_playing())
1233 return;
1234
1235 /* Give 5ms clearance. */
1236 bufstart += BYTERATE / 200;
1237
1238#ifdef HAVE_PCM_DMA_ADDRESS
1239 /* Returned peak addresses are DMA addresses */
1240 bufend = pcm_dma_addr(bufend);
1241#endif
1242
1243 /* Wrapped above? */
1244 if (bufstart >= bufend)
1245 bufstart -= pcmbuf_size;
1246
1247 /* NOTE: On some targets using hardware DMA, cache range flushing may
1248 * be required or the writes may not be picked up by the controller.
1249 * An incremental flush should be done periodically during the mixdown. */
1250 }
1251 else if (nsamples <= MINIBUF_SAMPLES)
1252 {
1253 static int16_t minibuf[MINIBUF_SAMPLES*2] __attribute__((aligned(4)));
1254 /* Use mini buffer */
1255 bufstart = minibuf;
1256 bufend = SKIPBYTES(bufstart, MINIBUF_SIZE);
1257 }
1258 else if (!audio_buffer_state_trashed())
1259 {
1260 /* Use pcmbuffer */
1261 bufstart = (int16_t *)pcmbuffer;
1262 }
1263 else
1264 {
1265 /* No place */
1266 return;
1267 }
1268
1269 bufptr = bufstart;
1270
1271 /* Mix square wave into buffer */
1272 for (i = 0; i < nsamples; ++i)
1273 {
1274 int32_t amp = (phase >> 31) ^ (int32_t)amplitude;
1275 sample = mix ? *bufptr : 0;
1276 *bufptr++ = clip_sample_16(sample + amp);
1277 if (bufptr >= bufend)
1278 bufptr = (int16_t *)pcmbuffer;
1279 sample = mix ? *bufptr : 0;
1280 *bufptr++ = clip_sample_16(sample + amp);
1281 if (bufptr >= bufend)
1282 bufptr = (int16_t *)pcmbuffer;
1283
1284 phase += step;
1285 }
1286
1287 pcm_play_lock();
1288#ifdef HAVE_RECORDING
1289 pcm_rec_lock();
1290#endif
1291
1292 /* Kick off playback if required and it won't interfere */
1293 if (!pcm_is_playing()
1294#ifdef HAVE_RECORDING
1295 && !pcm_is_recording()
1296#endif
1297 )
1298 {
1299 pcm_play_data(NULL, (unsigned char *)bufstart, nsamples * 4);
1300 }
1301
1302 pcm_play_unlock();
1303#ifdef HAVE_RECORDING
1304 pcm_rec_unlock();
1305#endif
1306} 1178}
1307#endif /* HAVE_HARDWARE_BEEP */