GSoC/Buflib: Enable compaction in buflib.
[kugel-rb.git] / firmware / pcm.c
blobc2ebc67687165300b92db86efc45152303a9af52
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Michael Sevakis
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdlib.h>
22 #include "system.h"
23 #include "kernel.h"
25 /* Define LOGF_ENABLE to enable logf output in this file */
26 /*#define LOGF_ENABLE*/
27 #include "logf.h"
28 #include "audio.h"
29 #include "sound.h"
30 #include "general.h"
31 #include "pcm-internal.h"
32 #include "pcm_mixer.h"
34 /**
35 * Aspects implemented in the target-specific portion:
37 * ==Playback==
38 * Public -
39 * pcm_postinit
40 * pcm_get_bytes_waiting
41 * pcm_play_lock
42 * pcm_play_unlock
43 * Semi-private -
44 * pcm_play_get_more_callback
45 * pcm_play_dma_init
46 * pcm_play_dma_start
47 * pcm_play_dma_stop
48 * pcm_play_dma_pause
49 * pcm_play_dma_get_peak_buffer
50 * Data Read/Written within TSP -
51 * pcm_sampr (R)
52 * pcm_fsel (R)
53 * pcm_curr_sampr (R)
54 * pcm_playing (R)
55 * pcm_paused (R)
57 * ==Playback/Recording==
58 * Public -
59 * pcm_dma_addr
60 * Semi-private -
61 * pcm_dma_apply_settings
63 * ==Recording==
64 * Public -
65 * pcm_rec_lock
66 * pcm_rec_unlock
67 * Semi-private -
68 * pcm_rec_more_ready_callback
69 * pcm_rec_dma_init
70 * pcm_rec_dma_close
71 * pcm_rec_dma_start
72 * pcm_rec_dma_stop
73 * pcm_rec_dma_get_peak_buffer
74 * Data Read/Written within TSP -
75 * pcm_recording (R)
77 * States are set _after_ the target's pcm driver is called so that it may
78 * know from whence the state is changed. One exception is init.
82 /* the registered callback function to ask for more mp3 data */
83 static pcm_play_callback_type pcm_callback_for_more SHAREDBSS_ATTR = NULL;
84 void (* pcm_play_dma_started)(void) SHAREDBSS_ATTR = NULL;
85 /* PCM playback state */
86 volatile bool pcm_playing SHAREDBSS_ATTR = false;
87 /* PCM paused state. paused implies playing */
88 volatile bool pcm_paused SHAREDBSS_ATTR = false;
89 /* samplerate of currently playing audio - undefined if stopped */
90 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
91 /* samplerate waiting to be set */
92 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
93 /* samplerate frequency selection index */
94 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
96 /* peak data for the global peak values - i.e. what the final output is */
97 static struct pcm_peaks global_peaks;
99 /* Called internally by functions to reset the state */
100 static void pcm_play_stopped(void)
102 pcm_callback_for_more = NULL;
103 pcm_play_dma_started = NULL;
104 pcm_paused = false;
105 pcm_playing = false;
109 * Perform peak calculation on a buffer of packed 16-bit samples.
111 * Used for recording and playback.
113 static void pcm_peak_peeker(const int32_t *addr, int count, uint16_t peaks[2])
115 int peak_l = 0, peak_r = 0;
116 const int32_t * const end = addr + count;
120 int32_t value = *addr;
121 int ch;
123 #ifdef ROCKBOX_BIG_ENDIAN
124 ch = value >> 16;
125 #else
126 ch = (int16_t)value;
127 #endif
128 if (ch < 0)
129 ch = -ch;
130 if (ch > peak_l)
131 peak_l = ch;
133 #ifdef ROCKBOX_BIG_ENDIAN
134 ch = (int16_t)value;
135 #else
136 ch = value >> 16;
137 #endif
138 if (ch < 0)
139 ch = -ch;
140 if (ch > peak_r)
141 peak_r = ch;
143 addr += 4;
145 while (addr < end);
147 peaks[0] = peak_l;
148 peaks[1] = peak_r;
151 void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
152 const void *addr, int count)
154 long tick = current_tick;
156 /* Peak no farther ahead than expected period to avoid overcalculation */
157 long period = tick - peaks->tick;
159 /* Keep reasonable limits on period */
160 if (period < 1)
161 period = 1;
162 else if (period > HZ/5)
163 period = HZ/5;
165 peaks->period = (3*peaks->period + period) >> 2;
166 peaks->tick = tick;
168 if (active)
170 int framecount = peaks->period*pcm_curr_sampr / HZ;
171 count = MIN(framecount, count);
173 if (count > 0)
174 pcm_peak_peeker((int32_t *)addr, count, peaks->val);
175 /* else keep previous peak values */
177 else
179 /* peaks are zero */
180 peaks->val[0] = peaks->val[1] = 0;
184 void pcm_calculate_peaks(int *left, int *right)
186 int count;
187 const void *addr = pcm_play_dma_get_peak_buffer(&count);
189 pcm_do_peak_calculation(&global_peaks, pcm_playing && !pcm_paused,
190 addr, count);
192 if (left)
193 *left = global_peaks.val[0];
195 if (right)
196 *right = global_peaks.val[1];
199 const void* pcm_get_peak_buffer(int * count)
201 return pcm_play_dma_get_peak_buffer(count);
204 bool pcm_is_playing(void)
206 return pcm_playing;
209 bool pcm_is_paused(void)
211 return pcm_paused;
214 /****************************************************************************
215 * Functions that do not require targeted implementation but only a targeted
216 * interface
219 /* This should only be called at startup before any audio playback or
220 recording is attempted */
221 void pcm_init(void)
223 logf("pcm_init");
225 pcm_play_stopped();
227 pcm_set_frequency(HW_SAMPR_DEFAULT);
229 logf(" pcm_play_dma_init");
230 pcm_play_dma_init();
233 /* Common code to pcm_play_data and pcm_play_pause */
234 static void pcm_play_data_start(unsigned char *start, size_t size)
236 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
237 size &= ~3;
239 if (!(start && size))
241 pcm_play_callback_type get_more = pcm_callback_for_more;
242 size = 0;
243 if (get_more)
245 logf(" get_more");
246 get_more(&start, &size);
248 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
249 size &= ~3;
253 if (start && size)
255 logf(" pcm_play_dma_start");
256 pcm_apply_settings();
257 pcm_play_dma_start(start, size);
258 pcm_playing = true;
259 pcm_paused = false;
260 return;
263 /* Force a stop */
264 logf(" pcm_play_dma_stop");
265 pcm_play_dma_stop();
266 pcm_play_stopped();
269 void pcm_play_data(pcm_play_callback_type get_more,
270 unsigned char *start, size_t size)
272 logf("pcm_play_data");
274 pcm_play_lock();
276 pcm_callback_for_more = get_more;
278 logf(" pcm_play_data_start");
279 pcm_play_data_start(start, size);
281 pcm_play_unlock();
284 void pcm_play_get_more_callback(void **start, size_t *size)
286 pcm_play_callback_type get_more = pcm_callback_for_more;
288 *size = 0;
290 if (get_more && start)
292 /* Call registered callback */
293 get_more((unsigned char **)start, size);
295 *start = (void *)(((uintptr_t)*start + 3) & ~3);
296 *size &= ~3;
298 if (*start && *size)
299 return;
302 /* Error, callback missing or no more DMA to do */
303 pcm_play_dma_stop();
304 pcm_play_stopped();
307 void pcm_play_pause(bool play)
309 logf("pcm_play_pause: %s", play ? "play" : "pause");
311 pcm_play_lock();
313 if (play == pcm_paused && pcm_playing)
315 if (!play)
317 logf(" pcm_play_dma_pause");
318 pcm_play_dma_pause(true);
319 pcm_paused = true;
321 else if (pcm_get_bytes_waiting() > 0)
323 logf(" pcm_play_dma_pause");
324 pcm_apply_settings();
325 pcm_play_dma_pause(false);
326 pcm_paused = false;
328 else
330 logf(" pcm_play_dma_start: no data");
331 pcm_play_data_start(NULL, 0);
334 else
336 logf(" no change");
339 pcm_play_unlock();
342 void pcm_play_stop(void)
344 logf("pcm_play_stop");
346 pcm_play_lock();
348 if (pcm_playing)
350 logf(" pcm_play_dma_stop");
351 pcm_play_dma_stop();
352 pcm_play_stopped();
354 else
356 logf(" not playing");
359 pcm_play_unlock();
362 /**/
364 /* set frequency next frequency used by the audio hardware -
365 * what pcm_apply_settings will set */
366 void pcm_set_frequency(unsigned int samplerate)
368 logf("pcm_set_frequency");
370 int index;
372 #ifdef CONFIG_SAMPR_TYPES
373 #ifdef HAVE_RECORDING
374 unsigned int type = samplerate & SAMPR_TYPE_MASK;
375 #endif
376 samplerate &= ~SAMPR_TYPE_MASK;
378 #ifdef HAVE_RECORDING
379 #if SAMPR_TYPE_REC != 0
380 /* For now, supported targets have direct conversion when configured with
381 * CONFIG_SAMPR_TYPES.
382 * Some hypothetical target with independent rates would need slightly
383 * different handling throughout this source. */
384 if (type == SAMPR_TYPE_REC)
385 samplerate = pcm_sampr_type_rec_to_play(samplerate);
386 #endif
387 #endif /* HAVE_RECORDING */
388 #endif /* CONFIG_SAMPR_TYPES */
390 index = round_value_to_list32(samplerate, hw_freq_sampr,
391 HW_NUM_FREQ, false);
393 if (samplerate != hw_freq_sampr[index])
394 index = HW_FREQ_DEFAULT; /* Invalid = default */
396 pcm_sampr = hw_freq_sampr[index];
397 pcm_fsel = index;
400 /* apply pcm settings to the hardware */
401 void pcm_apply_settings(void)
403 logf("pcm_apply_settings");
405 if (pcm_sampr != pcm_curr_sampr)
407 logf(" pcm_dma_apply_settings");
408 pcm_dma_apply_settings();
409 pcm_curr_sampr = pcm_sampr;
413 /* register callback to buffer more data */
414 void pcm_play_set_dma_started_callback(void (* callback)(void))
416 pcm_play_dma_started = callback;
419 #ifdef HAVE_RECORDING
420 /** Low level pcm recording apis **/
422 /* Next start for recording peaks */
423 static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
424 /* the registered callback function for when more data is available */
425 static volatile pcm_rec_callback_type
426 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
427 /* DMA transfer in is currently active */
428 volatile bool pcm_recording SHAREDBSS_ATTR = false;
430 /* Called internally by functions to reset the state */
431 static void pcm_recording_stopped(void)
433 pcm_recording = false;
434 pcm_callback_more_ready = NULL;
438 * Return recording peaks - From the end of the last peak up to
439 * current write position.
441 void pcm_calculate_rec_peaks(int *left, int *right)
443 static uint16_t peaks[2];
445 if (pcm_recording)
447 const void *peak_addr = pcm_rec_peak_addr;
448 const void *addr = pcm_rec_dma_get_peak_buffer();
450 if (addr != NULL)
452 int count = (int)(((intptr_t)addr - (intptr_t)peak_addr) >> 2);
454 if (count > 0)
456 pcm_peak_peeker((int32_t *)peak_addr, count, peaks);
458 if (peak_addr == pcm_rec_peak_addr)
459 pcm_rec_peak_addr = addr;
462 /* else keep previous peak values */
464 else
466 peaks[0] = peaks[1] = 0;
469 if (left)
470 *left = peaks[0];
472 if (right)
473 *right = peaks[1];
474 } /* pcm_calculate_rec_peaks */
476 bool pcm_is_recording(void)
478 return pcm_recording;
481 /****************************************************************************
482 * Functions that do not require targeted implementation but only a targeted
483 * interface
486 void pcm_init_recording(void)
488 logf("pcm_init_recording");
490 /* Stop the beasty before attempting recording */
491 mixer_reset();
493 /* Recording init is locked unlike general pcm init since this is not
494 * just a one-time event at startup and it should and must be safe by
495 * now. */
496 pcm_rec_lock();
498 logf(" pcm_rec_dma_init");
499 pcm_recording_stopped();
500 pcm_rec_dma_init();
502 pcm_rec_unlock();
505 void pcm_close_recording(void)
507 logf("pcm_close_recording");
509 pcm_rec_lock();
511 if (pcm_recording)
513 logf(" pcm_rec_dma_stop");
514 pcm_rec_dma_stop();
515 pcm_recording_stopped();
518 logf(" pcm_rec_dma_close");
519 pcm_rec_dma_close();
521 pcm_rec_unlock();
524 void pcm_record_data(pcm_rec_callback_type more_ready,
525 void *start, size_t size)
527 logf("pcm_record_data");
529 /* 32-bit aligned and sized data only */
530 start = (void *)(((uintptr_t)start + 3) & ~3);
531 size &= ~3;
533 if (!(start && size))
535 logf(" no buffer");
536 return;
539 pcm_rec_lock();
541 pcm_callback_more_ready = more_ready;
543 #ifdef HAVE_PCM_REC_DMA_ADDRESS
544 /* Need a physical DMA address translation, if not already physical. */
545 pcm_rec_peak_addr = pcm_dma_addr(start);
546 #else
547 pcm_rec_peak_addr = start;
548 #endif
550 logf(" pcm_rec_dma_start");
551 pcm_apply_settings();
552 pcm_rec_dma_start(start, size);
553 pcm_recording = true;
555 pcm_rec_unlock();
556 } /* pcm_record_data */
558 void pcm_stop_recording(void)
560 logf("pcm_stop_recording");
562 pcm_rec_lock();
564 if (pcm_recording)
566 logf(" pcm_rec_dma_stop");
567 pcm_rec_dma_stop();
568 pcm_recording_stopped();
571 pcm_rec_unlock();
572 } /* pcm_stop_recording */
574 void pcm_rec_more_ready_callback(int status, void **start, size_t *size)
576 pcm_rec_callback_type have_more = pcm_callback_more_ready;
578 *size = 0;
580 if (have_more && start)
582 have_more(status, start, size);
583 *start = (void *)(((uintptr_t)*start + 3) & ~3);
584 *size &= ~3;
586 if (*start && *size)
588 #ifdef HAVE_PCM_REC_DMA_ADDRESS
589 /* Need a physical DMA address translation, if not already
590 * physical. */
591 pcm_rec_peak_addr = pcm_dma_addr(*start);
592 #else
593 pcm_rec_peak_addr = *start;
594 #endif
595 return;
599 /* Error, callback missing or no more DMA to do */
600 pcm_rec_dma_stop();
601 pcm_recording_stopped();
604 #endif /* HAVE_RECORDING */