Merge branch 'kinetic_scroll' into android-test-plugins
[kugel-rb.git] / firmware / pcm.c
blobd15c1290153557276e8c84e8c6e2de374c5b60e9
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"
32 /**
33 * Aspects implemented in the target-specific portion:
35 * ==Playback==
36 * Public -
37 * pcm_postinit
38 * pcm_get_bytes_waiting
39 * pcm_play_lock
40 * pcm_play_unlock
41 * Semi-private -
42 * pcm_play_get_more_callback
43 * pcm_play_dma_init
44 * pcm_play_dma_start
45 * pcm_play_dma_stop
46 * pcm_play_dma_pause
47 * pcm_play_dma_get_peak_buffer
48 * Data Read/Written within TSP -
49 * pcm_sampr (R)
50 * pcm_fsel (R)
51 * pcm_curr_sampr (R)
52 * pcm_playing (R)
53 * pcm_paused (R)
55 * ==Playback/Recording==
56 * Public -
57 * pcm_dma_addr
58 * Semi-private -
59 * pcm_dma_apply_settings
61 * ==Recording==
62 * Public -
63 * pcm_rec_lock
64 * pcm_rec_unlock
65 * Semi-private -
66 * pcm_rec_more_ready_callback
67 * pcm_rec_dma_init
68 * pcm_rec_dma_close
69 * pcm_rec_dma_start
70 * pcm_rec_dma_stop
71 * pcm_rec_dma_get_peak_buffer
72 * Data Read/Written within TSP -
73 * pcm_recording (R)
75 * States are set _after_ the target's pcm driver is called so that it may
76 * know from whence the state is changed. One exception is init.
80 /* the registered callback function to ask for more mp3 data */
81 static volatile pcm_play_callback_type pcm_callback_for_more
82 SHAREDBSS_ATTR = NULL;
83 /* PCM playback state */
84 volatile bool pcm_playing SHAREDBSS_ATTR = false;
85 /* PCM paused state. paused implies playing */
86 volatile bool pcm_paused SHAREDBSS_ATTR = false;
87 /* samplerate of currently playing audio - undefined if stopped */
88 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
89 /* samplerate waiting to be set */
90 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
91 /* samplerate frequency selection index */
92 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
94 /* Called internally by functions to reset the state */
95 static void pcm_play_stopped(void)
97 pcm_callback_for_more = NULL;
98 pcm_paused = false;
99 pcm_playing = false;
103 * Perform peak calculation on a buffer of packed 16-bit samples.
105 * Used for recording and playback.
107 static void pcm_peak_peeker(const int32_t *addr, int count, int peaks[2])
109 int peak_l = 0, peak_r = 0;
110 const int32_t * const end = addr + count;
114 int32_t value = *addr;
115 int ch;
117 #ifdef ROCKBOX_BIG_ENDIAN
118 ch = value >> 16;
119 #else
120 ch = (int16_t)value;
121 #endif
122 if (ch < 0)
123 ch = -ch;
124 if (ch > peak_l)
125 peak_l = ch;
127 #ifdef ROCKBOX_BIG_ENDIAN
128 ch = (int16_t)value;
129 #else
130 ch = value >> 16;
131 #endif
132 if (ch < 0)
133 ch = -ch;
134 if (ch > peak_r)
135 peak_r = ch;
137 addr += 4;
139 while (addr < end);
141 peaks[0] = peak_l;
142 peaks[1] = peak_r;
145 void pcm_calculate_peaks(int *left, int *right)
147 static int peaks[2] = { 0, 0 };
148 static unsigned long last_peak_tick = 0;
149 static unsigned long frame_period = 0;
151 long tick = current_tick;
152 int count;
153 const void *addr;
155 /* Throttled peak ahead based on calling period */
156 long period = tick - last_peak_tick;
158 /* Keep reasonable limits on period */
159 if (period < 1)
160 period = 1;
161 else if (period > HZ/5)
162 period = HZ/5;
164 frame_period = (3*frame_period + period) >> 2;
166 last_peak_tick = tick;
168 addr = pcm_play_dma_get_peak_buffer(&count);
170 if (pcm_playing && !pcm_paused)
172 int framecount;
174 framecount = frame_period*pcm_curr_sampr / HZ;
175 count = MIN(framecount, count);
177 if (count > 0)
178 pcm_peak_peeker((int32_t *)addr, count, peaks);
179 /* else keep previous peak values */
181 else
183 peaks[0] = peaks[1] = 0;
186 if (left)
187 *left = peaks[0];
189 if (right)
190 *right = peaks[1];
193 const void* pcm_get_peak_buffer(int * count)
195 return pcm_play_dma_get_peak_buffer(count);
198 bool pcm_is_playing(void)
200 return pcm_playing;
203 bool pcm_is_paused(void)
205 return pcm_paused;
208 /****************************************************************************
209 * Functions that do not require targeted implementation but only a targeted
210 * interface
213 /* This should only be called at startup before any audio playback or
214 recording is attempted */
215 void pcm_init(void)
217 logf("pcm_init");
219 pcm_play_stopped();
221 pcm_set_frequency(HW_SAMPR_DEFAULT);
223 logf(" pcm_play_dma_init");
224 pcm_play_dma_init();
227 /* Common code to pcm_play_data and pcm_play_pause */
228 static void pcm_play_data_start(unsigned char *start, size_t size)
230 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
231 size &= ~3;
233 if (!(start && size))
235 pcm_play_callback_type get_more = pcm_callback_for_more;
236 size = 0;
237 if (get_more)
239 logf(" get_more");
240 get_more(&start, &size);
242 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
243 size &= ~3;
247 if (start && size)
249 logf(" pcm_play_dma_start");
250 pcm_apply_settings();
251 pcm_play_dma_start(start, size);
252 pcm_playing = true;
253 pcm_paused = false;
254 return;
257 /* Force a stop */
258 logf(" pcm_play_dma_stop");
259 pcm_play_dma_stop();
260 pcm_play_stopped();
263 void pcm_play_data(pcm_play_callback_type get_more,
264 unsigned char *start, size_t size)
266 logf("pcm_play_data");
268 pcm_play_lock();
270 pcm_callback_for_more = get_more;
272 logf(" pcm_play_data_start");
273 pcm_play_data_start(start, size);
275 pcm_play_unlock();
278 void pcm_play_get_more_callback(void **start, size_t *size)
280 pcm_play_callback_type get_more = pcm_callback_for_more;
282 *size = 0;
284 if (get_more && start)
286 /* Call registered callback */
287 get_more((unsigned char **)start, size);
289 *start = (void *)(((uintptr_t)*start + 3) & ~3);
290 *size &= ~3;
292 if (*start && *size)
293 return;
296 /* Error, callback missing or no more DMA to do */
297 pcm_play_dma_stop();
298 pcm_play_stopped();
301 void pcm_play_pause(bool play)
303 logf("pcm_play_pause: %s", play ? "play" : "pause");
305 pcm_play_lock();
307 if (play == pcm_paused && pcm_playing)
309 if (!play)
311 logf(" pcm_play_dma_pause");
312 pcm_play_dma_pause(true);
313 pcm_paused = true;
315 else if (pcm_get_bytes_waiting() > 0)
317 logf(" pcm_play_dma_pause");
318 pcm_apply_settings();
319 pcm_play_dma_pause(false);
320 pcm_paused = false;
322 else
324 logf(" pcm_play_dma_start: no data");
325 pcm_play_data_start(NULL, 0);
328 else
330 logf(" no change");
333 pcm_play_unlock();
336 void pcm_play_stop(void)
338 logf("pcm_play_stop");
340 pcm_play_lock();
342 if (pcm_playing)
344 logf(" pcm_play_dma_stop");
345 pcm_play_dma_stop();
346 pcm_play_stopped();
348 else
350 logf(" not playing");
353 pcm_play_unlock();
356 /**/
358 /* set frequency next frequency used by the audio hardware -
359 * what pcm_apply_settings will set */
360 void pcm_set_frequency(unsigned int samplerate)
362 logf("pcm_set_frequency");
364 int index;
366 #ifdef CONFIG_SAMPR_TYPES
367 #ifdef HAVE_RECORDING
368 unsigned int type = samplerate & SAMPR_TYPE_MASK;
369 #endif
370 samplerate &= ~SAMPR_TYPE_MASK;
372 #ifdef HAVE_RECORDING
373 #if SAMPR_TYPE_REC != 0
374 /* For now, supported targets have direct conversion when configured with
375 * CONFIG_SAMPR_TYPES.
376 * Some hypothetical target with independent rates would need slightly
377 * different handling throughout this source. */
378 if (type == SAMPR_TYPE_REC)
379 samplerate = pcm_sampr_type_rec_to_play(samplerate);
380 #endif
381 #endif /* HAVE_RECORDING */
382 #endif /* CONFIG_SAMPR_TYPES */
384 index = round_value_to_list32(samplerate, hw_freq_sampr,
385 HW_NUM_FREQ, false);
387 if (samplerate != hw_freq_sampr[index])
388 index = HW_FREQ_DEFAULT; /* Invalid = default */
390 pcm_sampr = hw_freq_sampr[index];
391 pcm_fsel = index;
394 /* apply pcm settings to the hardware */
395 void pcm_apply_settings(void)
397 logf("pcm_apply_settings");
399 if (pcm_sampr != pcm_curr_sampr)
401 logf(" pcm_dma_apply_settings");
402 pcm_dma_apply_settings();
403 pcm_curr_sampr = pcm_sampr;
407 #ifdef HAVE_RECORDING
408 /** Low level pcm recording apis **/
410 /* Next start for recording peaks */
411 static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
412 /* the registered callback function for when more data is available */
413 static volatile pcm_rec_callback_type
414 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
415 /* DMA transfer in is currently active */
416 volatile bool pcm_recording SHAREDBSS_ATTR = false;
418 /* Called internally by functions to reset the state */
419 static void pcm_recording_stopped(void)
421 pcm_recording = false;
422 pcm_callback_more_ready = NULL;
426 * Return recording peaks - From the end of the last peak up to
427 * current write position.
429 void pcm_calculate_rec_peaks(int *left, int *right)
431 static int peaks[2];
433 if (pcm_recording)
435 const void *peak_addr = pcm_rec_peak_addr;
436 const void *addr = pcm_rec_dma_get_peak_buffer();
438 if (addr != NULL)
440 int count = (int)(((intptr_t)addr - (intptr_t)peak_addr) >> 2);
442 if (count > 0)
444 pcm_peak_peeker((int32_t *)peak_addr, count, peaks);
446 if (peak_addr == pcm_rec_peak_addr)
447 pcm_rec_peak_addr = addr;
450 /* else keep previous peak values */
452 else
454 peaks[0] = peaks[1] = 0;
457 if (left)
458 *left = peaks[0];
460 if (right)
461 *right = peaks[1];
462 } /* pcm_calculate_rec_peaks */
464 bool pcm_is_recording(void)
466 return pcm_recording;
469 /****************************************************************************
470 * Functions that do not require targeted implementation but only a targeted
471 * interface
474 void pcm_init_recording(void)
476 logf("pcm_init_recording");
478 /* Recording init is locked unlike general pcm init since this is not
479 * just a one-time event at startup and it should and must be safe by
480 * now. */
481 pcm_rec_lock();
483 logf(" pcm_rec_dma_init");
484 pcm_recording_stopped();
485 pcm_rec_dma_init();
487 pcm_rec_unlock();
490 void pcm_close_recording(void)
492 logf("pcm_close_recording");
494 pcm_rec_lock();
496 if (pcm_recording)
498 logf(" pcm_rec_dma_stop");
499 pcm_rec_dma_stop();
500 pcm_recording_stopped();
503 logf(" pcm_rec_dma_close");
504 pcm_rec_dma_close();
506 pcm_rec_unlock();
509 void pcm_record_data(pcm_rec_callback_type more_ready,
510 void *start, size_t size)
512 logf("pcm_record_data");
514 /* 32-bit aligned and sized data only */
515 start = (void *)(((uintptr_t)start + 3) & ~3);
516 size &= ~3;
518 if (!(start && size))
520 logf(" no buffer");
521 return;
524 pcm_rec_lock();
526 pcm_callback_more_ready = more_ready;
528 #ifdef HAVE_PCM_REC_DMA_ADDRESS
529 /* Need a physical DMA address translation, if not already physical. */
530 pcm_rec_peak_addr = pcm_dma_addr(start);
531 #else
532 pcm_rec_peak_addr = start;
533 #endif
535 logf(" pcm_rec_dma_start");
536 pcm_apply_settings();
537 pcm_rec_dma_start(start, size);
538 pcm_recording = true;
540 pcm_rec_unlock();
541 } /* pcm_record_data */
543 void pcm_stop_recording(void)
545 logf("pcm_stop_recording");
547 pcm_rec_lock();
549 if (pcm_recording)
551 logf(" pcm_rec_dma_stop");
552 pcm_rec_dma_stop();
553 pcm_recording_stopped();
556 pcm_rec_unlock();
557 } /* pcm_stop_recording */
559 void pcm_rec_more_ready_callback(int status, void **start, size_t *size)
561 pcm_rec_callback_type have_more = pcm_callback_more_ready;
563 *size = 0;
565 if (have_more && start)
567 have_more(status, start, size);
568 *start = (void *)(((uintptr_t)*start + 3) & ~3);
569 *size &= ~3;
571 if (*start && *size)
573 #ifdef HAVE_PCM_REC_DMA_ADDRESS
574 /* Need a physical DMA address translation, if not already
575 * physical. */
576 pcm_rec_peak_addr = pcm_dma_addr(*start);
577 #else
578 pcm_rec_peak_addr = *start;
579 #endif
580 return;
584 /* Error, callback missing or no more DMA to do */
585 pcm_rec_dma_stop();
586 pcm_recording_stopped();
589 #endif /* HAVE_RECORDING */