fuzev2: prevent button light flickering when accessing µSD
[kugel-rb.git] / firmware / pcm.c
bloba69f0a82323690126d8db4f903f7abe8c378a40d
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_dma_init
43 * pcm_play_dma_start
44 * pcm_play_dma_stop
45 * pcm_play_dma_pause
46 * pcm_play_dma_get_peak_buffer
47 * Data Read/Written within TSP -
48 * pcm_sampr (R)
49 * pcm_fsel (R)
50 * pcm_curr_sampr (R)
51 * pcm_callback_for_more (R)
52 * pcm_playing (R)
53 * pcm_paused (R)
55 * ==Playback/Recording==
56 * Semi-private -
57 * pcm_dma_apply_settings
58 * pcm_dma_addr
60 * ==Recording==
61 * Public -
62 * pcm_rec_lock
63 * pcm_rec_unlock
64 * Semi-private -
65 * pcm_rec_dma_init
66 * pcm_rec_dma_close
67 * pcm_rec_dma_start
68 * pcm_rec_dma_record_more
69 * pcm_rec_dma_stop
70 * pcm_rec_dma_get_peak_buffer
71 * Data Read/Written within TSP -
72 * pcm_callback_more_ready (R)
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 volatile pcm_more_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 /**
95 * Perform peak calculation on a buffer of packed 16-bit samples.
97 * Used for recording and playback.
99 static void pcm_peak_peeker(const int32_t *addr, int count, int peaks[2])
101 int peak_l = 0, peak_r = 0;
102 const int32_t * const end = addr + count;
106 int32_t value = *addr;
107 int ch;
109 #ifdef ROCKBOX_BIG_ENDIAN
110 ch = value >> 16;
111 #else
112 ch = (int16_t)value;
113 #endif
114 if (ch < 0)
115 ch = -ch;
116 if (ch > peak_l)
117 peak_l = ch;
119 #ifdef ROCKBOX_BIG_ENDIAN
120 ch = (int16_t)value;
121 #else
122 ch = value >> 16;
123 #endif
124 if (ch < 0)
125 ch = -ch;
126 if (ch > peak_r)
127 peak_r = ch;
129 addr += 4;
131 while (addr < end);
133 peaks[0] = peak_l;
134 peaks[1] = peak_r;
137 void pcm_calculate_peaks(int *left, int *right)
139 static int peaks[2] = { 0, 0 };
140 static unsigned long last_peak_tick = 0;
141 static unsigned long frame_period = 0;
143 long tick = current_tick;
144 int count;
145 const void *addr;
147 /* Throttled peak ahead based on calling period */
148 long period = tick - last_peak_tick;
150 /* Keep reasonable limits on period */
151 if (period < 1)
152 period = 1;
153 else if (period > HZ/5)
154 period = HZ/5;
156 frame_period = (3*frame_period + period) >> 2;
158 last_peak_tick = tick;
160 addr = pcm_play_dma_get_peak_buffer(&count);
162 if (pcm_playing && !pcm_paused)
164 int framecount;
166 framecount = frame_period*pcm_curr_sampr / HZ;
167 count = MIN(framecount, count);
169 if (count > 0)
170 pcm_peak_peeker((int32_t *)addr, count, peaks);
171 /* else keep previous peak values */
173 else
175 peaks[0] = peaks[1] = 0;
178 if (left)
179 *left = peaks[0];
181 if (right)
182 *right = peaks[1];
185 const void* pcm_get_peak_buffer(int * count)
187 return pcm_play_dma_get_peak_buffer(count);
190 /****************************************************************************
191 * Functions that do not require targeted implementation but only a targeted
192 * interface
195 /* This should only be called at startup before any audio playback or
196 recording is attempted */
197 void pcm_init(void)
199 logf("pcm_init");
201 pcm_play_dma_stopped_callback();
203 pcm_set_frequency(HW_SAMPR_DEFAULT);
205 logf(" pcm_play_dma_init");
206 pcm_play_dma_init();
209 /* Common code to pcm_play_data and pcm_play_pause */
210 static void pcm_play_data_start(unsigned char *start, size_t size)
212 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
213 size &= ~3;
215 if (!(start && size))
217 pcm_more_callback_type get_more = pcm_callback_for_more;
218 size = 0;
219 if (get_more)
221 logf(" get_more");
222 get_more(&start, &size);
224 start = (unsigned char *)(((uintptr_t)start + 3) & ~3);
225 size &= ~3;
229 if (start && size)
231 logf(" pcm_play_dma_start");
232 pcm_apply_settings();
233 pcm_play_dma_start(start, size);
234 pcm_playing = true;
235 pcm_paused = false;
236 return;
239 /* Force a stop */
240 logf(" pcm_play_dma_stop");
241 pcm_play_dma_stop();
242 pcm_play_dma_stopped_callback();
245 void pcm_play_data(pcm_more_callback_type get_more,
246 unsigned char *start, size_t size)
248 logf("pcm_play_data");
250 pcm_play_lock();
252 pcm_callback_for_more = get_more;
254 logf(" pcm_play_data_start");
255 pcm_play_data_start(start, size);
257 pcm_play_unlock();
260 void pcm_play_pause(bool play)
262 logf("pcm_play_pause: %s", play ? "play" : "pause");
264 pcm_play_lock();
266 if (play == pcm_paused && pcm_playing)
268 if (!play)
270 logf(" pcm_play_dma_pause");
271 pcm_play_dma_pause(true);
272 pcm_paused = true;
274 else if (pcm_get_bytes_waiting() > 0)
276 logf(" pcm_play_dma_pause");
277 pcm_apply_settings();
278 pcm_play_dma_pause(false);
279 pcm_paused = false;
281 else
283 logf(" pcm_play_dma_start: no data");
284 pcm_play_data_start(NULL, 0);
287 else
289 logf(" no change");
292 pcm_play_unlock();
295 void pcm_play_stop(void)
297 logf("pcm_play_stop");
299 pcm_play_lock();
301 if (pcm_playing)
303 logf(" pcm_play_dma_stop");
304 pcm_play_dma_stop();
305 pcm_play_dma_stopped_callback();
307 else
309 logf(" not playing");
312 pcm_play_unlock();
315 void pcm_play_dma_stopped_callback(void)
317 pcm_callback_for_more = NULL;
318 pcm_paused = false;
319 pcm_playing = false;
322 /**/
324 /* set frequency next frequency used by the audio hardware -
325 * what pcm_apply_settings will set */
326 void pcm_set_frequency(unsigned int samplerate)
328 logf("pcm_set_frequency");
330 int index = round_value_to_list32(samplerate, hw_freq_sampr,
331 HW_NUM_FREQ, false);
333 if (samplerate != hw_freq_sampr[index])
334 index = HW_FREQ_DEFAULT; /* Invalid = default */
336 pcm_sampr = hw_freq_sampr[index];
337 pcm_fsel = index;
340 /* apply pcm settings to the hardware */
341 void pcm_apply_settings(void)
343 logf("pcm_apply_settings");
345 if (pcm_sampr != pcm_curr_sampr)
347 logf(" pcm_dma_apply_settings");
348 pcm_dma_apply_settings();
349 pcm_curr_sampr = pcm_sampr;
353 bool pcm_is_playing(void)
355 return pcm_playing;
358 bool pcm_is_paused(void)
360 return pcm_paused;
363 #ifdef HAVE_RECORDING
364 /** Low level pcm recording apis **/
366 /* Next start for recording peaks */
367 static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
368 /* the registered callback function for when more data is available */
369 volatile pcm_more_callback_type2
370 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
371 /* DMA transfer in is currently active */
372 volatile bool pcm_recording SHAREDBSS_ATTR = false;
375 * Return recording peaks - From the end of the last peak up to
376 * current write position.
378 void pcm_calculate_rec_peaks(int *left, int *right)
380 static int peaks[2];
382 if (pcm_recording)
384 const void *peak_addr = pcm_rec_peak_addr;
385 const void *addr = pcm_rec_dma_get_peak_buffer();
387 if (addr != NULL)
389 int count = (int)(((intptr_t)addr - (intptr_t)peak_addr) >> 2);
391 if (count > 0)
393 pcm_peak_peeker((int32_t *)peak_addr, count, peaks);
395 if (peak_addr == pcm_rec_peak_addr)
396 pcm_rec_peak_addr = addr;
399 /* else keep previous peak values */
401 else
403 peaks[0] = peaks[1] = 0;
406 if (left)
407 *left = peaks[0];
409 if (right)
410 *right = peaks[1];
411 } /* pcm_calculate_rec_peaks */
413 /****************************************************************************
414 * Functions that do not require targeted implementation but only a targeted
415 * interface
417 void pcm_init_recording(void)
419 logf("pcm_init_recording");
421 /* Recording init is locked unlike general pcm init since this is not
422 * just a one-time event at startup and it should and must be safe by
423 * now. */
424 pcm_rec_lock();
426 logf(" pcm_rec_dma_init");
427 pcm_rec_dma_stopped_callback();
428 pcm_rec_dma_init();
430 pcm_rec_unlock();
433 void pcm_close_recording(void)
435 logf("pcm_close_recording");
437 pcm_rec_lock();
439 if (pcm_recording)
441 logf(" pcm_rec_dma_stop");
442 pcm_rec_dma_stop();
443 pcm_rec_dma_stopped_callback();
446 logf(" pcm_rec_dma_close");
447 pcm_rec_dma_close();
449 pcm_rec_unlock();
452 void pcm_record_data(pcm_more_callback_type2 more_ready,
453 void *start, size_t size)
455 logf("pcm_record_data");
457 /* 32-bit aligned and sized data only */
458 start = (void *)(((uintptr_t)start + 3) & ~3);
459 size &= ~3;
461 if (!(start && size))
463 logf(" no buffer");
464 return;
467 pcm_rec_lock();
469 pcm_callback_more_ready = more_ready;
471 #ifdef HAVE_PCM_REC_DMA_ADDRESS
472 /* Need a physical DMA address translation, if not already physical. */
473 pcm_rec_peak_addr = pcm_dma_addr(start);
474 #else
475 pcm_rec_peak_addr = start;
476 #endif
478 logf(" pcm_rec_dma_start");
479 pcm_apply_settings();
480 pcm_rec_dma_start(start, size);
481 pcm_recording = true;
483 pcm_rec_unlock();
484 } /* pcm_record_data */
486 void pcm_stop_recording(void)
488 logf("pcm_stop_recording");
490 pcm_rec_lock();
492 if (pcm_recording)
494 logf(" pcm_rec_dma_stop");
495 pcm_rec_dma_stop();
496 pcm_rec_dma_stopped_callback();
499 pcm_rec_unlock();
500 } /* pcm_stop_recording */
502 void pcm_record_more(void *start, size_t size)
504 start = (void *)(((uintptr_t)start + 3) & ~3);
505 size = size & ~3;
507 if (!size)
509 pcm_rec_dma_stop();
510 pcm_rec_dma_stopped_callback();
511 return;
514 #ifdef HAVE_PCM_REC_DMA_ADDRESS
515 /* Need a physical DMA address translation, if not already physical. */
516 pcm_rec_peak_addr = pcm_dma_addr(start);
517 #else
518 pcm_rec_peak_addr = start;
519 #endif
521 pcm_rec_dma_record_more(start, size);
524 bool pcm_is_recording(void)
526 return pcm_recording;
529 void pcm_rec_dma_stopped_callback(void)
531 pcm_recording = false;
532 pcm_callback_more_ready = NULL;
535 #endif /* HAVE_RECORDING */