summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
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 */