summaryrefslogtreecommitdiff
path: root/firmware/pcm_mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/pcm_mixer.c')
-rw-r--r--firmware/pcm_mixer.c501
1 files changed, 501 insertions, 0 deletions
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
new file mode 100644
index 0000000000..cddd3f0f86
--- /dev/null
+++ b/firmware/pcm_mixer.c
@@ -0,0 +1,501 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "system.h"
23#include "general.h"
24#include "kernel.h"
25#include "pcm.h"
26#include "pcm_mixer.h"
27#include "dsp.h"
28
29/* Channels use standard-style PCM callback interface but a latency of one
30 frame by double-buffering is introduced in order to facilitate mixing and
31 keep the hardware fed. There must be sufficient time to perform operations
32 before the last samples are sent to the codec and so things are done in
33 parallel (as much as possible) with sending-out data. */
34
35/* Define this to nonzero to add a marker pulse at each frame start */
36#define FRAME_BOUNDARY_MARKERS 0
37
38/* Descriptor for each channel */
39struct mixer_channel
40{
41 unsigned char *start; /* Buffer pointer */
42 size_t size; /* Bytes remaining */
43 size_t last_size; /* Size of consumed data in prev. cycle */
44 pcm_play_callback_type get_more; /* Registered callback */
45 enum channel_status status; /* Playback status */
46 uint32_t amplitude; /* Amp. factor: 0x0000 = mute, 0x10000 = unity */
47};
48
49/* Forget about boost here for the moment */
50#define MIX_FRAME_SIZE (MIX_FRAME_SAMPLES*4)
51
52/* Because of the double-buffering, playback is always from here, otherwise a
53 mechanism for the channel callbacks not to free buffers too early would be
54 needed (if we _really_ want it and it's worth it, we _can_ do that ;-) ) */
55static uint32_t downmix_buf[2][MIX_FRAME_SAMPLES] DOWNMIX_BUF_IBSS MEM_ALIGN_ATTR;
56static int downmix_index = 0; /* Which downmix_buf? */
57static size_t next_size = 0; /* Size of buffer to play next time */
58
59/* Descriptors for all available channels */
60static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR;
61
62/* Packed pointer array of all playing (active) channels in "channels" array */
63static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR;
64
65/* Number of silence frames to play after all data has played */
66#define MAX_IDLE_FRAMES (NATIVE_FREQUENCY*3 / MIX_FRAME_SAMPLES)
67static unsigned int idle_counter = 0;
68
69/* Cheapo buffer align macro to align to the 16-16 PCM size */
70#define ALIGN_CHANNEL(start, size) \
71 ({ start = (void *)(((uintptr_t)start + 3) & ~3); \
72 size &= ~3; })
73
74/* Include any implemented CPU-optimized mixdown routines */
75#if defined(CPU_ARM)
76#if ARM_ARCH >= 6
77#include "pcm-mixer-armv6.c"
78#elif ARM_ARCH >= 5
79#include "pcm-mixer-armv5.c"
80#else
81#include "pcm-mixer-armv4.c"
82#endif /* ARM_ARCH */
83#elif defined (CPU_COLDFIRE)
84#include "pcm-mixer-coldfire.c"
85#endif /* CPU_* */
86
87
88/** Generic mixing routines **/
89
90#ifndef MIXER_OPTIMIZED_MIX_SAMPLES
91/* Clip sample to signed 16 bit range */
92static FORCE_INLINE int32_t clip_sample_16(int32_t sample)
93{
94 if ((int16_t)sample != sample)
95 sample = 0x7fff ^ (sample >> 31);
96 return sample;
97}
98
99/* Mix channels' samples and apply gain factors */
100static FORCE_INLINE void mix_samples(uint32_t *out,
101 int16_t *src0,
102 int32_t src0_amp,
103 int16_t *src1,
104 int32_t src1_amp,
105 size_t size)
106{
107 if (src0_amp == MIX_AMP_UNITY && src1_amp == MIX_AMP_UNITY)
108 {
109 /* Both are unity amplitude */
110 do
111 {
112 int32_t l = *src0++ + *src1++;
113 int32_t h = *src0++ + *src1++;
114 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
115 }
116 while ((size -= 4) > 0);
117 }
118 else if (src0_amp != MIX_AMP_UNITY && src1_amp != MIX_AMP_UNITY)
119 {
120 /* Neither are unity amplitude */
121 do
122 {
123 int32_t l = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16);
124 int32_t h = (*src0++ * src0_amp >> 16) + (*src1++ * src1_amp >> 16);
125 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
126 }
127 while ((size -= 4) > 0);
128 }
129 else
130 {
131 /* One is unity amplitude */
132 if (src0_amp != MIX_AMP_UNITY)
133 {
134 /* Keep unity in src0, amp0 */
135 int16_t *src_tmp = src0;
136 src0 = src1;
137 src1 = src_tmp;
138 src1_amp = src0_amp;
139 src0_amp = MIX_AMP_UNITY;
140 }
141
142 do
143 {
144 int32_t l = *src0++ + (*src1++ * src1_amp >> 16);
145 int32_t h = *src0++ + (*src1++ * src1_amp >> 16);
146 *out++ = (uint16_t)clip_sample_16(l) | (clip_sample_16(h) << 16);
147 }
148 while ((size -= 4) > 0);
149 }
150}
151#endif /* MIXER_OPTIMIZED_MIX_SAMPLES */
152
153#ifndef MIXER_OPTIMIZED_WRITE_SAMPLES
154/* Write channel's samples and apply gain factor */
155static FORCE_INLINE void write_samples(uint32_t *out,
156 int16_t *src,
157 int32_t amp,
158 size_t size)
159{
160 if (LIKELY(amp == MIX_AMP_UNITY))
161 {
162 /* Channel is unity amplitude */
163 memcpy(out, src, size);
164 }
165 else
166 {
167 /* Channel needs amplitude cut */
168 do
169 {
170 int32_t l = *src++ * amp >> 16;
171 int32_t h = *src++ * amp & 0xffff0000;
172 *out++ = (uint16_t)l | h;
173 }
174 while ((size -= 4) > 0);
175 }
176}
177#endif /* MIXER_OPTIMIZED_WRITE_SAMPLES */
178
179
180/** Private generic routines **/
181
182/* Mark channel active to mix its data */
183static void mixer_activate_channel(struct mixer_channel *chan)
184{
185 void **elem = find_array_ptr((void **)active_channels, chan);
186
187 if (!*elem)
188 {
189 idle_counter = 0;
190 *elem = chan;
191 }
192}
193
194/* Stop channel from mixing */
195static void mixer_deactivate_channel(struct mixer_channel *chan)
196{
197 remove_array_ptr((void **)active_channels, chan);
198}
199
200/* Deactivate channel and change it to stopped state */
201static void channel_stopped(struct mixer_channel *chan)
202{
203 mixer_deactivate_channel(chan);
204 chan->size = 0;
205 chan->start = NULL;
206 chan->status = CHANNEL_STOPPED;
207}
208
209/* Main PCM callback - sends the current prepared frame to play */
210static void mixer_pcm_callback(unsigned char **start, size_t *size)
211{
212 *start = (unsigned char *)downmix_buf[downmix_index];
213 *size = next_size;
214}
215
216/* Buffering callback - calls sub-callbacks and mixes the data for next
217 buffer to be sent from mixer_pcm_callback() */
218static void ICODE_ATTR mixer_buffer_callback(void)
219{
220 downmix_index ^= 1; /* Next buffer */
221
222 void *mixptr = downmix_buf[downmix_index];
223 size_t mixsize = MIX_FRAME_SIZE;
224 struct mixer_channel **chan_p;
225
226 next_size = 0;
227
228 /* "Loop" back here if one round wasn't enough to fill a frame */
229fill_frame:
230 chan_p = active_channels;
231
232 while (*chan_p)
233 {
234 /* Find the active channel with the least data remaining and call any
235 callbacks for channels that ran out - stopping whichever report
236 "no more" */
237 struct mixer_channel *chan = *chan_p;
238 chan->start += chan->last_size;
239 chan->size -= chan->last_size;
240
241 if (chan->size == 0)
242 {
243 if (chan->get_more)
244 {
245 chan->get_more(&chan->start, &chan->size);
246 ALIGN_CHANNEL(chan->start, chan->size);
247 }
248
249 if (!(chan->start && chan->size))
250 {
251 /* Channel is stopping */
252 channel_stopped(chan);
253 continue;
254 }
255 }
256
257 /* Channel will play for at least part of this frame */
258
259 /* Channel with least amount of data remaining determines the downmix
260 size */
261 if (chan->size < mixsize)
262 mixsize = chan->size;
263
264 chan_p++;
265 }
266
267 /* Add all still-active channels to the downmix */
268 chan_p = active_channels;
269
270 if (LIKELY(*chan_p))
271 {
272 struct mixer_channel *chan = *chan_p++;
273
274 if (LIKELY(!*chan_p))
275 {
276 write_samples(mixptr, (void *)chan->start,
277 chan->amplitude, mixsize);
278 }
279 else
280 {
281 void *src0, *src1;
282 unsigned int amp0, amp1;
283
284 /* Mix first two channels with each other as the downmix */
285 src0 = chan->start;
286 amp0 = chan->amplitude;
287 chan->last_size = mixsize;
288
289 chan = *chan_p++;
290 src1 = chan->start;
291 amp1 = chan->amplitude;
292
293 while (1)
294 {
295 mix_samples(mixptr, src0, amp0, src1, amp1, mixsize);
296
297 if (!*chan_p)
298 break;
299
300 /* More channels to mix - mix each with existing downmix */
301 chan->last_size = mixsize;
302 chan = *chan_p++;
303 src0 = mixptr;
304 amp0 = MIX_AMP_UNITY;
305 src1 = chan->start;
306 amp1 = chan->amplitude;
307 }
308 }
309
310 chan->last_size = mixsize;
311 next_size += mixsize;
312
313 if (next_size < MIX_FRAME_SIZE)
314 {
315 /* There is still space remaining in this frame */
316 mixptr += mixsize;
317 mixsize = MIX_FRAME_SIZE - next_size;
318 goto fill_frame;
319 }
320 }
321 else if (idle_counter++ < MAX_IDLE_FRAMES)
322 {
323 /* Pad incomplete frames with silence */
324 if (idle_counter <= 3)
325 memset(mixptr, 0, MIX_FRAME_SIZE - next_size);
326
327 next_size = MIX_FRAME_SIZE;
328 }
329 /* else silence period ran out - go to sleep */
330
331#if FRAME_BOUNDARY_MARKERS != 0
332 if (next_size)
333 *downmix_buf[downmix_index] = downmix_index ? 0x7fff7fff : 0x80008000;
334#endif
335}
336
337/* Start PCM driver if it's not currently playing */
338static void mixer_start_pcm(void)
339{
340 if (pcm_is_playing())
341 return;
342
343#if defined(HAVE_RECORDING) && !defined(HAVE_PCM_FULL_DUPLEX)
344 if (pcm_is_recording())
345 return;
346#endif
347
348 /* Prepare initial frames and set up the double buffer */
349 mixer_buffer_callback();
350
351 /* Save the previous call's output */
352 void *start = downmix_buf[downmix_index];
353
354 mixer_buffer_callback();
355
356 pcm_play_set_dma_started_callback(mixer_buffer_callback);
357 pcm_play_data(mixer_pcm_callback, start, MIX_FRAME_SIZE);
358}
359
360/* Initialize the channel and start it if it has data */
361static void mixer_channel_play_start(struct mixer_channel *chan,
362 pcm_play_callback_type get_more,
363 unsigned char *start, size_t size)
364{
365 pcm_play_unlock(); /* Allow playback while doing any callback */
366
367 ALIGN_CHANNEL(start, size);
368
369 if (!(start && size))
370 {
371 /* Initial buffer not passed - call the callback now */
372 size = 0;
373 if (get_more)
374 {
375 get_more(&start, &size);
376 ALIGN_CHANNEL(start, size);
377 }
378 }
379
380 if (start && size)
381 {
382 /* We have data - start the channel */
383 chan->status = CHANNEL_PLAYING;
384 chan->start = start;
385 chan->size = size;
386 chan->last_size = 0;
387 chan->get_more = get_more;
388
389 pcm_play_lock();
390 mixer_activate_channel(chan);
391 mixer_start_pcm();
392 }
393 else
394 {
395 /* Never had anything - stop it now */
396 pcm_play_lock();
397 channel_stopped(chan);
398 }
399}
400
401
402/** Public interfaces **/
403
404/* Start playback on a channel */
405void mixer_channel_play_data(enum pcm_mixer_channel channel,
406 pcm_play_callback_type get_more,
407 unsigned char *start, size_t size)
408{
409 struct mixer_channel *chan = &channels[channel];
410
411 pcm_play_lock();
412 mixer_deactivate_channel(chan);
413 mixer_channel_play_start(chan, get_more, start, size);
414 pcm_play_unlock();
415}
416
417/* Pause or resume a channel (when started) */
418void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play)
419{
420 struct mixer_channel *chan = &channels[channel];
421
422 pcm_play_lock();
423
424 if (play == (chan->status == CHANNEL_PAUSED) &&
425 chan->status != CHANNEL_STOPPED)
426 {
427 if (play)
428 {
429 chan->status = CHANNEL_PLAYING;
430 mixer_activate_channel(chan);
431 mixer_start_pcm();
432 }
433 else
434 {
435 mixer_deactivate_channel(chan);
436 chan->status = CHANNEL_PAUSED;
437 }
438 }
439
440 pcm_play_unlock();
441}
442
443/* Stop playback on a channel */
444void mixer_channel_stop(enum pcm_mixer_channel channel)
445{
446 struct mixer_channel *chan = &channels[channel];
447
448 pcm_play_lock();
449 channel_stopped(chan);
450 pcm_play_unlock();
451}
452
453/* Set channel's amplitude factor */
454void mixer_channel_set_amplitude(enum pcm_mixer_channel channel,
455 unsigned int amplitude)
456{
457 channels[channel].amplitude = MIN(amplitude, MIX_AMP_UNITY);
458}
459
460/* Return channel's playback status */
461enum channel_status mixer_channel_status(enum pcm_mixer_channel channel)
462{
463 return channels[channel].status;
464}
465
466/* Returns amount data remaining in channel before next callback */
467size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel)
468{
469 return channels[channel].size;
470}
471
472/* Return pointer to channel's playing audio data and the size remaining */
473void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count)
474{
475 struct mixer_channel *chan = &channels[channel];
476 void * buf = *(unsigned char * volatile *)&chan->start;
477 size_t size = *(size_t volatile *)&chan->size;
478 void * buf2 = *(unsigned char * volatile *)&chan->start;
479
480 /* Still same buffer? */
481 if (buf == buf2)
482 {
483 *count = size >> 2;
484 return buf;
485 }
486 /* else can't be sure buf and size are related */
487
488 *count = 0;
489 return NULL;
490}
491
492/* Stop ALL channels and PCM and reset state */
493void mixer_reset(void)
494{
495 pcm_play_stop();
496
497 while (*active_channels)
498 channel_stopped(*active_channels);
499
500 idle_counter = 0;
501}