Fix Gigabeat F manual. Thanks to Marianne Arnold for pointing out it was broken.
[kugel-rb.git] / firmware / pcm.c
blobbc7ec0282ad9488af320f2e5f93a26ea8945d623
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
56 * pcm_dma_addr
58 * ==Recording==
59 * Public -
60 * pcm_rec_lock
61 * pcm_rec_unlock
62 * Semi-private -
63 * pcm_rec_dma_init
64 * pcm_rec_dma_close
65 * pcm_rec_dma_start
66 * pcm_rec_dma_stop
67 * pcm_rec_dma_get_peak_buffer
68 * Data Read/Written within TSP -
69 * pcm_rec_peak_addr (R/W)
70 * pcm_callback_more_ready (R)
71 * pcm_recording (R)
73 * States are set _after_ the target's pcm driver is called so that it may
74 * know from whence the state is changed. One exception is init.
78 /* the registered callback function to ask for more mp3 data */
79 volatile pcm_more_callback_type pcm_callback_for_more
80 SHAREDBSS_ATTR = NULL;
81 /* PCM playback state */
82 volatile bool pcm_playing SHAREDBSS_ATTR = false;
83 /* PCM paused state. paused implies playing */
84 volatile bool pcm_paused SHAREDBSS_ATTR = false;
85 /* samplerate of currently playing audio - undefined if stopped */
86 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
87 /* samplerate waiting to be set */
88 unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT;
89 /* samplerate frequency selection index */
90 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
92 /**
93 * Do peak calculation using distance squared from axis and save a lot
94 * of jumps and negation. Don't bother with the calculations of left or
95 * right only as it's never really used and won't save much time.
97 * Used for recording and playback.
99 static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
101 int32_t peak_l = 0, peak_r = 0;
102 int32_t peaksq_l = 0, peaksq_r = 0;
106 int32_t value = *(int32_t *)addr;
107 int32_t ch, chsq;
108 #ifdef ROCKBOX_BIG_ENDIAN
109 ch = value >> 16;
110 #else
111 ch = (int16_t)value;
112 #endif
113 chsq = ch*ch;
114 if (chsq > peaksq_l)
115 peak_l = ch, peaksq_l = chsq;
117 #ifdef ROCKBOX_BIG_ENDIAN
118 ch = (int16_t)value;
119 #else
120 ch = value >> 16;
121 #endif
122 chsq = ch*ch;
123 if (chsq > peaksq_r)
124 peak_r = ch, peaksq_r = chsq;
126 addr += 16;
127 count -= 4;
129 while (count > 0);
131 peaks[0] = abs(peak_l);
132 peaks[1] = abs(peak_r);
135 void pcm_calculate_peaks(int *left, int *right)
137 static int peaks[2] = { 0, 0 };
138 static unsigned long last_peak_tick = 0;
139 static unsigned long frame_period = 0;
141 long tick = current_tick;
142 int count;
143 const void *addr;
145 /* Throttled peak ahead based on calling period */
146 long period = tick - last_peak_tick;
148 /* Keep reasonable limits on period */
149 if (period < 1)
150 period = 1;
151 else if (period > HZ/5)
152 period = HZ/5;
154 frame_period = (3*frame_period + period) >> 2;
156 last_peak_tick = tick;
158 addr = pcm_play_dma_get_peak_buffer(&count);
160 if (pcm_playing && !pcm_paused)
162 int framecount;
164 framecount = frame_period*pcm_curr_sampr / HZ;
165 count = MIN(framecount, count);
167 if (count > 0)
168 pcm_peak_peeker(addr, count, peaks);
169 /* else keep previous peak values */
171 else
173 peaks[0] = peaks[1] = 0;
176 if (left)
177 *left = peaks[0];
179 if (right)
180 *right = peaks[1];
183 /****************************************************************************
184 * Functions that do not require targeted implementation but only a targeted
185 * interface
188 /* This should only be called at startup before any audio playback or
189 recording is attempted */
190 void pcm_init(void)
192 logf("pcm_init");
194 pcm_play_dma_stopped_callback();
196 pcm_set_frequency(HW_SAMPR_DEFAULT);
198 logf(" pcm_play_dma_init");
199 pcm_play_dma_init();
202 /* Common code to pcm_play_data and pcm_play_pause */
203 static void pcm_play_data_start(unsigned char *start, size_t size)
205 if (!(start && size))
207 pcm_more_callback_type get_more = pcm_callback_for_more;
208 size = 0;
209 if (get_more)
211 logf(" get_more");
212 get_more(&start, &size);
216 if (start && size)
218 logf(" pcm_play_dma_start");
219 pcm_apply_settings();
220 pcm_play_dma_start(start, size);
221 pcm_playing = true;
222 pcm_paused = false;
223 return;
226 /* Force a stop */
227 logf(" pcm_play_dma_stop");
228 pcm_play_dma_stop();
229 pcm_play_dma_stopped_callback();
232 void pcm_play_data(pcm_more_callback_type get_more,
233 unsigned char *start, size_t size)
235 logf("pcm_play_data");
237 pcm_play_lock();
239 pcm_callback_for_more = get_more;
241 logf(" pcm_play_data_start");
242 pcm_play_data_start(start, size);
244 pcm_play_unlock();
247 void pcm_play_pause(bool play)
249 logf("pcm_play_pause: %s", play ? "play" : "pause");
251 pcm_play_lock();
253 if (play == pcm_paused && pcm_playing)
255 if (!play)
257 logf(" pcm_play_dma_pause");
258 pcm_play_dma_pause(true);
259 pcm_paused = true;
261 else if (pcm_get_bytes_waiting() > 0)
263 logf(" pcm_play_dma_pause");
264 pcm_apply_settings();
265 pcm_play_dma_pause(false);
266 pcm_paused = false;
268 else
270 logf(" pcm_play_dma_start: no data");
271 pcm_play_data_start(NULL, 0);
274 else
276 logf(" no change");
279 pcm_play_unlock();
282 void pcm_play_stop(void)
284 logf("pcm_play_stop");
286 pcm_play_lock();
288 if (pcm_playing)
290 logf(" pcm_play_dma_stop");
291 pcm_play_dma_stop();
292 pcm_play_dma_stopped_callback();
294 else
296 logf(" not playing");
299 pcm_play_unlock();
302 void pcm_play_dma_stopped_callback(void)
304 pcm_callback_for_more = NULL;
305 pcm_paused = false;
306 pcm_playing = false;
309 /**/
311 /* set frequency next frequency used by the audio hardware -
312 * what pcm_apply_settings will set */
313 void pcm_set_frequency(unsigned int samplerate)
315 logf("pcm_set_frequency");
317 int index = round_value_to_list32(samplerate, hw_freq_sampr,
318 HW_NUM_FREQ, false);
320 if (samplerate != hw_freq_sampr[index])
321 index = HW_FREQ_DEFAULT; /* Invalid = default */
323 pcm_sampr = hw_freq_sampr[index];
324 pcm_fsel = index;
327 /* apply pcm settings to the hardware */
328 void pcm_apply_settings(void)
330 logf("pcm_apply_settings");
332 if (pcm_sampr != pcm_curr_sampr)
334 logf(" pcm_dma_apply_settings");
335 pcm_dma_apply_settings();
336 pcm_curr_sampr = pcm_sampr;
340 bool pcm_is_playing(void)
342 return pcm_playing;
345 bool pcm_is_paused(void)
347 return pcm_paused;
350 void pcm_mute(bool mute)
352 #ifndef SIMULATOR
353 audiohw_mute(mute);
354 #endif
356 if (mute)
357 sleep(HZ/16);
360 #ifdef HAVE_RECORDING
361 /** Low level pcm recording apis **/
363 /* Next start for recording peaks */
364 const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
365 /* the registered callback function for when more data is available */
366 volatile pcm_more_callback_type2
367 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
368 /* DMA transfer in is currently active */
369 volatile bool pcm_recording SHAREDBSS_ATTR = false;
372 * Return recording peaks - From the end of the last peak up to
373 * current write position.
375 void pcm_calculate_rec_peaks(int *left, int *right)
377 static int peaks[2];
378 int count;
379 const void *addr = pcm_rec_dma_get_peak_buffer(&count);
381 if (pcm_recording)
383 if (count > 0)
385 pcm_peak_peeker(addr, count, peaks);
387 if (addr == pcm_rec_peak_addr)
388 pcm_rec_peak_addr = (int32_t *)addr + count;
390 /* else keep previous peak values */
392 else
394 peaks[0] = peaks[1] = 0;
397 if (left)
398 *left = peaks[0];
400 if (right)
401 *right = peaks[1];
402 } /* pcm_calculate_rec_peaks */
404 /****************************************************************************
405 * Functions that do not require targeted implementation but only a targeted
406 * interface
408 void pcm_init_recording(void)
410 logf("pcm_init_recording");
412 /* Recording init is locked unlike general pcm init since this is not
413 * just a one-time event at startup and it should and must be safe by
414 * now. */
415 pcm_rec_lock();
417 logf(" pcm_rec_dma_init");
418 pcm_rec_dma_stopped_callback();
419 pcm_rec_dma_init();
421 pcm_rec_unlock();
424 void pcm_close_recording(void)
426 logf("pcm_close_recording");
428 pcm_rec_lock();
430 if (pcm_recording)
432 logf(" pcm_rec_dma_stop");
433 pcm_rec_dma_stop();
434 pcm_rec_dma_stopped_callback();
437 logf(" pcm_rec_dma_close");
438 pcm_rec_dma_close();
440 pcm_rec_unlock();
443 void pcm_record_data(pcm_more_callback_type2 more_ready,
444 void *start, size_t size)
446 logf("pcm_record_data");
448 if (!(start && size))
450 logf(" no buffer");
451 return;
454 pcm_rec_lock();
456 pcm_callback_more_ready = more_ready;
458 logf(" pcm_rec_dma_start");
459 pcm_apply_settings();
460 pcm_rec_dma_start(start, size);
461 pcm_recording = true;
463 pcm_rec_unlock();
464 } /* pcm_record_data */
466 void pcm_stop_recording(void)
468 logf("pcm_stop_recording");
470 pcm_rec_lock();
472 if (pcm_recording)
474 logf(" pcm_rec_dma_stop");
475 pcm_rec_dma_stop();
476 pcm_rec_dma_stopped_callback();
479 pcm_rec_unlock();
480 } /* pcm_stop_recording */
482 bool pcm_is_recording(void)
484 return pcm_recording;
487 void pcm_rec_dma_stopped_callback(void)
489 pcm_recording = false;
490 pcm_callback_more_ready = NULL;
493 #endif /* HAVE_RECORDING */