Gigabeat S: Add remote control reading and proper headphone insert detection. We...
[kugel-rb.git] / firmware / pcm.c
blob38204f883bb7082723b8be330dcc3552c3d224c1
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"
24 #include "logf.h"
25 #include "audio.h"
26 #include "sound.h"
27 #include "general.h"
29 /**
30 * Aspects implemented in the target-specific portion:
32 * ==Playback==
33 * Public -
34 * pcm_postinit
35 * pcm_get_bytes_waiting
36 * pcm_play_lock
37 * pcm_play_unlock
38 * Semi-private -
39 * pcm_play_dma_init
40 * pcm_play_dma_init
41 * pcm_play_dma_start
42 * pcm_play_dma_stop
43 * pcm_play_dma_pause
44 * pcm_play_dma_get_peak_buffer
45 * Data Read/Written within TSP -
46 * pcm_sampr (R)
47 * pcm_fsel (R)
48 * pcm_curr_sampr (R)
49 * pcm_callback_for_more (R)
50 * pcm_playing (R)
51 * pcm_paused (R)
53 * ==Playback/Recording==
54 * Semi-private -
55 * pcm_dma_apply_settings
57 * ==Recording==
58 * Public -
59 * pcm_rec_lock
60 * pcm_rec_unlock
61 * Semi-private -
62 * pcm_rec_dma_init
63 * pcm_rec_dma_close
64 * pcm_rec_dma_start
65 * pcm_rec_dma_stop
66 * pcm_rec_dma_get_peak_buffer
67 * Data Read/Written within TSP -
68 * pcm_rec_peak_addr (R)
69 * pcm_callback_more_ready (R)
70 * pcm_recording (R)
72 * States are set _after_ the target's pcm driver is called so that it may
73 * know from whence the state is changed. One exception is init.
77 /* the registered callback function to ask for more mp3 data */
78 volatile pcm_more_callback_type pcm_callback_for_more
79 SHAREDBSS_ATTR = NULL;
80 /* PCM playback state */
81 volatile bool pcm_playing SHAREDBSS_ATTR = false;
82 /* PCM paused state. paused implies playing */
83 volatile bool pcm_paused SHAREDBSS_ATTR = false;
84 /* samplerate of currently playing audio - undefined if stopped */
85 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
86 /* samplerate waiting to be set */
87 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
88 /* samplerate frequency selection index */
89 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
91 /**
92 * Do peak calculation using distance squared from axis and save a lot
93 * of jumps and negation. Don't bother with the calculations of left or
94 * right only as it's never really used and won't save much time.
96 * Used for recording and playback.
98 static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
100 int32_t peak_l = 0, peak_r = 0;
101 int32_t peaksq_l = 0, peaksq_r = 0;
105 int32_t value = *(int32_t *)addr;
106 int32_t ch, chsq;
107 #ifdef ROCKBOX_BIG_ENDIAN
108 ch = value >> 16;
109 #else
110 ch = (int16_t)value;
111 #endif
112 chsq = ch*ch;
113 if (chsq > peaksq_l)
114 peak_l = ch, peaksq_l = chsq;
116 #ifdef ROCKBOX_BIG_ENDIAN
117 ch = (int16_t)value;
118 #else
119 ch = value >> 16;
120 #endif
121 chsq = ch*ch;
122 if (chsq > peaksq_r)
123 peak_r = ch, peaksq_r = chsq;
125 addr += 16;
126 count -= 4;
128 while (count > 0);
130 peaks[0] = abs(peak_l);
131 peaks[1] = abs(peak_r);
134 void pcm_calculate_peaks(int *left, int *right)
136 static int peaks[2] = { 0, 0 };
137 static unsigned long last_peak_tick = 0;
138 static unsigned long frame_period = 0;
140 long tick = current_tick;
141 int count;
142 const void *addr;
144 /* Throttled peak ahead based on calling period */
145 long period = tick - last_peak_tick;
147 /* Keep reasonable limits on period */
148 if (period < 1)
149 period = 1;
150 else if (period > HZ/5)
151 period = HZ/5;
153 frame_period = (3*frame_period + period) >> 2;
155 last_peak_tick = tick;
157 addr = pcm_play_dma_get_peak_buffer(&count);
159 if (pcm_playing && !pcm_paused)
161 int framecount;
163 framecount = frame_period*pcm_curr_sampr / HZ;
164 count = MIN(framecount, count);
166 if (count > 0)
167 pcm_peak_peeker(addr, count, peaks);
168 /* else keep previous peak values */
170 else
172 peaks[0] = peaks[1] = 0;
175 if (left)
176 *left = peaks[0];
178 if (right)
179 *right = peaks[1];
182 /****************************************************************************
183 * Functions that do not require targeted implementation but only a targeted
184 * interface
187 /* This should only be called at startup before any audio playback or
188 recording is attempted */
189 void pcm_init(void)
191 logf("pcm_init");
193 pcm_play_dma_stopped_callback();
195 pcm_set_frequency(HW_SAMPR_DEFAULT);
197 logf(" pcm_play_dma_init");
198 pcm_play_dma_init();
201 /* Common code to pcm_play_data and pcm_play_pause */
202 static void pcm_play_data_start(unsigned char *start, size_t size)
204 if (!(start && size))
206 pcm_more_callback_type get_more = pcm_callback_for_more;
207 size = 0;
208 if (get_more)
210 logf(" get_more");
211 get_more(&start, &size);
215 if (start && size)
217 logf(" pcm_play_dma_start");
218 pcm_apply_settings();
219 pcm_play_dma_start(start, size);
220 pcm_playing = true;
221 pcm_paused = false;
222 return;
225 /* Force a stop */
226 logf(" pcm_play_dma_stop");
227 pcm_play_dma_stop();
228 pcm_play_dma_stopped_callback();
231 void pcm_play_data(pcm_more_callback_type get_more,
232 unsigned char *start, size_t size)
234 logf("pcm_play_data");
236 pcm_play_lock();
238 pcm_callback_for_more = get_more;
240 logf(" pcm_play_data_start");
241 pcm_play_data_start(start, size);
243 pcm_play_unlock();
246 void pcm_play_pause(bool play)
248 logf("pcm_play_pause: %s", play ? "play" : "pause");
250 pcm_play_lock();
252 if (play == pcm_paused && pcm_playing)
254 if (!play)
256 logf(" pcm_play_dma_pause");
257 pcm_play_dma_pause(true);
258 pcm_paused = true;
260 else if (pcm_get_bytes_waiting() > 0)
262 logf(" pcm_play_dma_pause");
263 pcm_apply_settings();
264 pcm_play_dma_pause(false);
265 pcm_paused = false;
267 else
269 logf(" pcm_play_dma_start: no data");
270 pcm_play_data_start(NULL, 0);
273 else
275 logf(" no change");
278 pcm_play_unlock();
281 void pcm_play_stop(void)
283 logf("pcm_play_stop");
285 pcm_play_lock();
287 if (pcm_playing)
289 logf(" pcm_play_dma_stop");
290 pcm_play_dma_stop();
291 pcm_play_dma_stopped_callback();
293 else
295 logf(" not playing");
298 pcm_play_unlock();
301 void pcm_play_dma_stopped_callback(void)
303 pcm_callback_for_more = NULL;
304 pcm_paused = false;
305 pcm_playing = false;
308 /**/
310 /* set frequency next frequency used by the audio hardware -
311 * what pcm_apply_settings will set */
312 void pcm_set_frequency(unsigned int samplerate)
314 logf("pcm_set_frequency");
316 int index = round_value_to_list32(samplerate, hw_freq_sampr,
317 HW_NUM_FREQ, false);
319 if (samplerate != hw_freq_sampr[index])
320 index = HW_FREQ_DEFAULT; /* Invalid = default */
322 pcm_sampr = hw_freq_sampr[index];
323 pcm_fsel = index;
326 /* apply pcm settings to the hardware */
327 void pcm_apply_settings(void)
329 logf("pcm_apply_settings");
331 if (pcm_sampr != pcm_curr_sampr)
333 logf(" pcm_dma_apply_settings");
334 pcm_dma_apply_settings();
335 pcm_curr_sampr = pcm_sampr;
339 bool pcm_is_playing(void)
341 return pcm_playing;
344 bool pcm_is_paused(void)
346 return pcm_paused;
349 void pcm_mute(bool mute)
351 #ifndef SIMULATOR
352 audiohw_mute(mute);
353 #endif
355 if (mute)
356 sleep(HZ/16);
359 #ifdef HAVE_RECORDING
360 /** Low level pcm recording apis **/
362 /* Next start for recording peaks */
363 const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
364 /* the registered callback function for when more data is available */
365 volatile pcm_more_callback_type2
366 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
367 /* DMA transfer in is currently active */
368 volatile bool pcm_recording SHAREDBSS_ATTR = false;
371 * Return recording peaks - From the end of the last peak up to
372 * current write position.
374 void pcm_calculate_rec_peaks(int *left, int *right)
376 static int peaks[2];
377 int count;
378 const void *addr = pcm_rec_dma_get_peak_buffer(&count);
380 if (pcm_recording)
382 if (count > 0)
384 pcm_peak_peeker(addr, count, peaks);
386 if (addr == pcm_rec_peak_addr)
387 pcm_rec_peak_addr = (int32_t *)addr + count;
389 /* else keep previous peak values */
391 else
393 peaks[0] = peaks[1] = 0;
396 if (left)
397 *left = peaks[0];
399 if (right)
400 *right = peaks[1];
401 } /* pcm_calculate_rec_peaks */
403 /****************************************************************************
404 * Functions that do not require targeted implementation but only a targeted
405 * interface
407 void pcm_init_recording(void)
409 logf("pcm_init_recording");
411 /* Recording init is locked unlike general pcm init since this is not
412 * just a one-time event at startup and it should and must be safe by
413 * now. */
414 pcm_rec_lock();
416 logf(" pcm_rec_dma_init");
417 pcm_rec_dma_stopped_callback();
418 pcm_rec_dma_init();
420 pcm_rec_unlock();
423 void pcm_close_recording(void)
425 logf("pcm_close_recording");
427 pcm_rec_lock();
429 if (pcm_recording)
431 logf(" pcm_rec_dma_stop");
432 pcm_rec_dma_stop();
433 pcm_rec_dma_stopped_callback();
436 logf(" pcm_rec_dma_close");
437 pcm_rec_dma_close();
439 pcm_rec_unlock();
442 void pcm_record_data(pcm_more_callback_type2 more_ready,
443 void *start, size_t size)
445 logf("pcm_record_data");
447 if (!(start && size))
449 logf(" no buffer");
450 return;
453 pcm_rec_lock();
455 pcm_callback_more_ready = more_ready;
457 logf(" pcm_rec_dma_start");
458 pcm_apply_settings();
459 pcm_rec_dma_start(start, size);
460 pcm_recording = true;
462 pcm_rec_unlock();
463 } /* pcm_record_data */
465 void pcm_stop_recording(void)
467 logf("pcm_stop_recording");
469 pcm_rec_lock();
471 if (pcm_recording)
473 logf(" pcm_rec_dma_stop");
474 pcm_rec_dma_stop();
475 pcm_rec_dma_stopped_callback();
478 pcm_rec_unlock();
479 } /* pcm_stop_recording */
481 bool pcm_is_recording(void)
483 return pcm_recording;
486 void pcm_rec_dma_stopped_callback(void)
488 pcm_recording = false;
489 pcm_callback_more_ready = NULL;
492 #endif /* HAVE_RECORDING */