Fix strcpy from a user-supplied string to fixed size string.
[kugel-rb.git] / firmware / pcm.c
blob0e89cb90c9bf0bcd8cdaf8982d826cae6d2c7cdd
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_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_callback_for_more (R)
53 * pcm_playing (R)
54 * pcm_paused (R)
56 * ==Playback/Recording==
57 * Semi-private -
58 * pcm_dma_apply_settings
59 * pcm_dma_addr
61 * ==Recording==
62 * Public -
63 * pcm_rec_lock
64 * pcm_rec_unlock
65 * Semi-private -
66 * pcm_rec_dma_init
67 * pcm_rec_dma_close
68 * pcm_rec_dma_start
69 * pcm_rec_dma_stop
70 * pcm_rec_dma_get_peak_buffer
71 * Data Read/Written within TSP -
72 * pcm_rec_peak_addr (R/W)
73 * pcm_callback_more_ready (R)
74 * pcm_recording (R)
76 * States are set _after_ the target's pcm driver is called so that it may
77 * know from whence the state is changed. One exception is init.
81 /* the registered callback function to ask for more mp3 data */
82 volatile pcm_more_callback_type pcm_callback_for_more
83 SHAREDBSS_ATTR = NULL;
84 /* PCM playback state */
85 volatile bool pcm_playing SHAREDBSS_ATTR = false;
86 /* PCM paused state. paused implies playing */
87 volatile bool pcm_paused SHAREDBSS_ATTR = false;
88 /* samplerate of currently playing audio - undefined if stopped */
89 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
90 /* samplerate waiting to be set */
91 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
92 /* samplerate frequency selection index */
93 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
95 /**
96 * Do peak calculation using distance squared from axis and save a lot
97 * of jumps and negation. Don't bother with the calculations of left or
98 * right only as it's never really used and won't save much time.
100 * Used for recording and playback.
102 static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
104 int32_t peak_l = 0, peak_r = 0;
105 int32_t peaksq_l = 0, peaksq_r = 0;
109 int32_t value = *(int32_t *)addr;
110 int32_t ch, chsq;
111 #ifdef ROCKBOX_BIG_ENDIAN
112 ch = value >> 16;
113 #else
114 ch = (int16_t)value;
115 #endif
116 chsq = ch*ch;
117 if (chsq > peaksq_l)
118 peak_l = ch, peaksq_l = chsq;
120 #ifdef ROCKBOX_BIG_ENDIAN
121 ch = (int16_t)value;
122 #else
123 ch = value >> 16;
124 #endif
125 chsq = ch*ch;
126 if (chsq > peaksq_r)
127 peak_r = ch, peaksq_r = chsq;
129 addr += 16;
130 count -= 4;
132 while (count > 0);
134 peaks[0] = abs(peak_l);
135 peaks[1] = abs(peak_r);
138 void pcm_calculate_peaks(int *left, int *right)
140 static int peaks[2] = { 0, 0 };
141 static unsigned long last_peak_tick = 0;
142 static unsigned long frame_period = 0;
144 long tick = current_tick;
145 int count;
146 const void *addr;
148 /* Throttled peak ahead based on calling period */
149 long period = tick - last_peak_tick;
151 /* Keep reasonable limits on period */
152 if (period < 1)
153 period = 1;
154 else if (period > HZ/5)
155 period = HZ/5;
157 frame_period = (3*frame_period + period) >> 2;
159 last_peak_tick = tick;
161 addr = pcm_play_dma_get_peak_buffer(&count);
163 if (pcm_playing && !pcm_paused)
165 int framecount;
167 framecount = frame_period*pcm_curr_sampr / HZ;
168 count = MIN(framecount, count);
170 if (count > 0)
171 pcm_peak_peeker(addr, count, peaks);
172 /* else keep previous peak values */
174 else
176 peaks[0] = peaks[1] = 0;
179 if (left)
180 *left = peaks[0];
182 if (right)
183 *right = peaks[1];
186 const void* pcm_get_peak_buffer(int * count)
188 return pcm_play_dma_get_peak_buffer(count);
191 /****************************************************************************
192 * Functions that do not require targeted implementation but only a targeted
193 * interface
196 /* This should only be called at startup before any audio playback or
197 recording is attempted */
198 void pcm_init(void)
200 logf("pcm_init");
202 pcm_play_dma_stopped_callback();
204 pcm_set_frequency(HW_SAMPR_DEFAULT);
206 logf(" pcm_play_dma_init");
207 pcm_play_dma_init();
210 /* Common code to pcm_play_data and pcm_play_pause */
211 static void pcm_play_data_start(unsigned char *start, size_t size)
213 if (!(start && size))
215 pcm_more_callback_type get_more = pcm_callback_for_more;
216 size = 0;
217 if (get_more)
219 logf(" get_more");
220 get_more(&start, &size);
224 if (start && size)
226 logf(" pcm_play_dma_start");
227 pcm_apply_settings();
228 pcm_play_dma_start(start, size);
229 pcm_playing = true;
230 pcm_paused = false;
231 return;
234 /* Force a stop */
235 logf(" pcm_play_dma_stop");
236 pcm_play_dma_stop();
237 pcm_play_dma_stopped_callback();
240 void pcm_play_data(pcm_more_callback_type get_more,
241 unsigned char *start, size_t size)
243 logf("pcm_play_data");
245 pcm_play_lock();
247 pcm_callback_for_more = get_more;
249 logf(" pcm_play_data_start");
250 pcm_play_data_start(start, size);
252 pcm_play_unlock();
255 void pcm_play_pause(bool play)
257 logf("pcm_play_pause: %s", play ? "play" : "pause");
259 pcm_play_lock();
261 if (play == pcm_paused && pcm_playing)
263 if (!play)
265 logf(" pcm_play_dma_pause");
266 pcm_play_dma_pause(true);
267 pcm_paused = true;
269 else if (pcm_get_bytes_waiting() > 0)
271 logf(" pcm_play_dma_pause");
272 pcm_apply_settings();
273 pcm_play_dma_pause(false);
274 pcm_paused = false;
276 else
278 logf(" pcm_play_dma_start: no data");
279 pcm_play_data_start(NULL, 0);
282 else
284 logf(" no change");
287 pcm_play_unlock();
290 void pcm_play_stop(void)
292 logf("pcm_play_stop");
294 pcm_play_lock();
296 if (pcm_playing)
298 logf(" pcm_play_dma_stop");
299 pcm_play_dma_stop();
300 pcm_play_dma_stopped_callback();
302 else
304 logf(" not playing");
307 pcm_play_unlock();
310 void pcm_play_dma_stopped_callback(void)
312 pcm_callback_for_more = NULL;
313 pcm_paused = false;
314 pcm_playing = false;
317 /**/
319 /* set frequency next frequency used by the audio hardware -
320 * what pcm_apply_settings will set */
321 void pcm_set_frequency(unsigned int samplerate)
323 logf("pcm_set_frequency");
325 int index = round_value_to_list32(samplerate, hw_freq_sampr,
326 HW_NUM_FREQ, false);
328 if (samplerate != hw_freq_sampr[index])
329 index = HW_FREQ_DEFAULT; /* Invalid = default */
331 pcm_sampr = hw_freq_sampr[index];
332 pcm_fsel = index;
335 /* apply pcm settings to the hardware */
336 void pcm_apply_settings(void)
338 logf("pcm_apply_settings");
340 if (pcm_sampr != pcm_curr_sampr)
342 logf(" pcm_dma_apply_settings");
343 pcm_dma_apply_settings();
344 pcm_curr_sampr = pcm_sampr;
348 bool pcm_is_playing(void)
350 return pcm_playing;
353 bool pcm_is_paused(void)
355 return pcm_paused;
358 void pcm_mute(bool mute)
360 #ifndef SIMULATOR
361 audiohw_mute(mute);
362 #endif
364 if (mute)
365 sleep(HZ/16);
368 #ifdef HAVE_RECORDING
369 /** Low level pcm recording apis **/
371 /* Next start for recording peaks */
372 const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
373 /* the registered callback function for when more data is available */
374 volatile pcm_more_callback_type2
375 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
376 /* DMA transfer in is currently active */
377 volatile bool pcm_recording SHAREDBSS_ATTR = false;
380 * Return recording peaks - From the end of the last peak up to
381 * current write position.
383 void pcm_calculate_rec_peaks(int *left, int *right)
385 static int peaks[2];
386 int count;
387 const void *addr = pcm_rec_dma_get_peak_buffer(&count);
389 if (pcm_recording)
391 if (count > 0)
393 pcm_peak_peeker(addr, count, peaks);
395 if (addr == pcm_rec_peak_addr)
396 pcm_rec_peak_addr = (int32_t *)addr + count;
398 /* else keep previous peak values */
400 else
402 peaks[0] = peaks[1] = 0;
405 if (left)
406 *left = peaks[0];
408 if (right)
409 *right = peaks[1];
410 } /* pcm_calculate_rec_peaks */
412 /****************************************************************************
413 * Functions that do not require targeted implementation but only a targeted
414 * interface
416 void pcm_init_recording(void)
418 logf("pcm_init_recording");
420 /* Recording init is locked unlike general pcm init since this is not
421 * just a one-time event at startup and it should and must be safe by
422 * now. */
423 pcm_rec_lock();
425 logf(" pcm_rec_dma_init");
426 pcm_rec_dma_stopped_callback();
427 pcm_rec_dma_init();
429 pcm_rec_unlock();
432 void pcm_close_recording(void)
434 logf("pcm_close_recording");
436 pcm_rec_lock();
438 if (pcm_recording)
440 logf(" pcm_rec_dma_stop");
441 pcm_rec_dma_stop();
442 pcm_rec_dma_stopped_callback();
445 logf(" pcm_rec_dma_close");
446 pcm_rec_dma_close();
448 pcm_rec_unlock();
451 void pcm_record_data(pcm_more_callback_type2 more_ready,
452 void *start, size_t size)
454 logf("pcm_record_data");
456 if (!(start && size))
458 logf(" no buffer");
459 return;
462 pcm_rec_lock();
464 pcm_callback_more_ready = more_ready;
466 logf(" pcm_rec_dma_start");
467 pcm_apply_settings();
468 pcm_rec_dma_start(start, size);
469 pcm_recording = true;
471 pcm_rec_unlock();
472 } /* pcm_record_data */
474 void pcm_stop_recording(void)
476 logf("pcm_stop_recording");
478 pcm_rec_lock();
480 if (pcm_recording)
482 logf(" pcm_rec_dma_stop");
483 pcm_rec_dma_stop();
484 pcm_rec_dma_stopped_callback();
487 pcm_rec_unlock();
488 } /* pcm_stop_recording */
490 bool pcm_is_recording(void)
492 return pcm_recording;
495 void pcm_rec_dma_stopped_callback(void)
497 pcm_recording = false;
498 pcm_callback_more_ready = NULL;
501 #endif /* HAVE_RECORDING */