My Devcon 2008 contribution: port for Philips GoGear HDD1630 (PP5022-based). Current...
[Rockbox.git] / firmware / pcm.c
bloba4e107ad4dc62cf49e1c912168be5295156e546c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Michael Sevakis
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdlib.h>
20 #include "system.h"
21 #include "kernel.h"
22 #include "logf.h"
23 #include "audio.h"
24 #include "sound.h"
26 /**
27 * Aspects implemented in the target-specific portion:
29 * ==Playback==
30 * Public -
31 * pcm_postinit
32 * pcm_get_bytes_waiting
33 * pcm_play_lock
34 * pcm_play_unlock
35 * Semi-private -
36 * pcm_play_dma_init
37 * pcm_play_dma_init
38 * pcm_play_dma_start
39 * pcm_play_dma_stop
40 * pcm_play_dma_pause
41 * pcm_play_dma_get_peak_buffer
42 * Data Read/Written within TSP -
43 * pcm_curr_sampr (RW)
44 * pcm_callback_for_more (R)
45 * pcm_playing (R)
46 * pcm_paused (R)
48 * ==Recording==
49 * Public -
50 * pcm_rec_lock
51 * pcm_rec_unlock
52 * Semi-private -
53 * pcm_rec_dma_init
54 * pcm_rec_dma_close
55 * pcm_rec_dma_start
56 * pcm_rec_dma_stop
57 * pcm_rec_dma_get_peak_buffer
58 * Data Read/Written within TSP -
59 * pcm_rec_peak_addr (RW)
60 * pcm_callback_more_ready (R)
61 * pcm_recording (R)
63 * States are set _after_ the target's pcm driver is called so that it may
64 * know from whence the state is changed. One exception is init.
68 /* the registered callback function to ask for more mp3 data */
69 volatile pcm_more_callback_type pcm_callback_for_more
70 SHAREDBSS_ATTR = NULL;
71 /* PCM playback state */
72 volatile bool pcm_playing SHAREDBSS_ATTR = false;
73 /* PCM paused state. paused implies playing */
74 volatile bool pcm_paused SHAREDBSS_ATTR = false;
75 /* samplerate of currently playing audio - undefined if stopped */
76 unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
78 /**
79 * Do peak calculation using distance squared from axis and save a lot
80 * of jumps and negation. Don't bother with the calculations of left or
81 * right only as it's never really used and won't save much time.
83 * Used for recording and playback.
85 static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
87 int32_t peak_l = 0, peak_r = 0;
88 int32_t peaksq_l = 0, peaksq_r = 0;
92 int32_t value = *(int32_t *)addr;
93 int32_t ch, chsq;
94 #ifdef ROCKBOX_BIG_ENDIAN
95 ch = value >> 16;
96 #else
97 ch = (int16_t)value;
98 #endif
99 chsq = ch*ch;
100 if (chsq > peaksq_l)
101 peak_l = ch, peaksq_l = chsq;
103 #ifdef ROCKBOX_BIG_ENDIAN
104 ch = (int16_t)value;
105 #else
106 ch = value >> 16;
107 #endif
108 chsq = ch*ch;
109 if (chsq > peaksq_r)
110 peak_r = ch, peaksq_r = chsq;
112 addr += 16;
113 count -= 4;
115 while (count > 0);
117 peaks[0] = abs(peak_l);
118 peaks[1] = abs(peak_r);
121 void pcm_calculate_peaks(int *left, int *right)
123 static int peaks[2] = { 0, 0 };
124 static unsigned long last_peak_tick = 0;
125 static unsigned long frame_period = 0;
127 long tick = current_tick;
129 /* Throttled peak ahead based on calling period */
130 long period = tick - last_peak_tick;
132 /* Keep reasonable limits on period */
133 if (period < 1)
134 period = 1;
135 else if (period > HZ/5)
136 period = HZ/5;
138 frame_period = (3*frame_period + period) >> 2;
140 last_peak_tick = tick;
142 if (pcm_playing && !pcm_paused)
144 const void *addr;
145 int count, framecount;
147 addr = pcm_play_dma_get_peak_buffer(&count);
149 framecount = frame_period*pcm_curr_sampr / HZ;
150 count = MIN(framecount, count);
152 if (count > 0)
153 pcm_peak_peeker(addr, count, peaks);
155 else
157 peaks[0] = peaks[1] = 0;
160 if (left)
161 *left = peaks[0];
163 if (right)
164 *right = peaks[1];
167 /****************************************************************************
168 * Functions that do not require targeted implementation but only a targeted
169 * interface
172 /* This should only be called at startup before any audio playback or
173 recording is attempted */
174 void pcm_init(void)
176 logf("pcm_init");
178 pcm_play_dma_stopped_callback();
180 logf(" pcm_play_dma_init");
181 pcm_play_dma_init();
184 /* Common code to pcm_play_data and pcm_play_pause */
185 static void pcm_play_data_start(unsigned char *start, size_t size)
187 if (!(start && size))
189 pcm_more_callback_type get_more = pcm_callback_for_more;
190 size = 0;
191 if (get_more)
193 logf(" get_more");
194 get_more(&start, &size);
198 if (start && size)
200 logf(" pcm_play_dma_start");
201 pcm_play_dma_start(start, size);
202 pcm_playing = true;
203 pcm_paused = false;
204 return;
207 /* Force a stop */
208 logf(" pcm_play_dma_stop");
209 pcm_play_dma_stop();
210 pcm_play_dma_stopped_callback();
213 void pcm_play_data(pcm_more_callback_type get_more,
214 unsigned char *start, size_t size)
216 logf("pcm_play_data");
218 pcm_play_lock();
220 pcm_callback_for_more = get_more;
222 logf(" pcm_play_dma_start");
223 pcm_play_data_start(start, size);
225 pcm_play_unlock();
228 void pcm_play_pause(bool play)
230 logf("pcm_play_pause: %s", play ? "play" : "pause");
232 pcm_play_lock();
234 if (play == pcm_paused && pcm_playing)
236 if (!play)
238 logf(" pcm_play_dma_pause");
239 pcm_play_dma_pause(true);
240 pcm_paused = true;
242 else if (pcm_get_bytes_waiting() > 0)
244 logf(" pcm_play_dma_pause");
245 pcm_play_dma_pause(false);
246 pcm_paused = false;
248 else
250 logf(" pcm_play_dma_start: no data");
251 pcm_play_data_start(NULL, 0);
254 else
256 logf(" no change");
259 pcm_play_unlock();
262 void pcm_play_stop(void)
264 logf("pcm_play_stop");
266 pcm_play_lock();
268 if (pcm_playing)
270 logf(" pcm_play_dma_stop");
271 pcm_play_dma_stop();
272 pcm_play_dma_stopped_callback();
274 else
276 logf(" not playing");
279 pcm_play_unlock();
282 void pcm_play_dma_stopped_callback(void)
284 pcm_callback_for_more = NULL;
285 pcm_paused = false;
286 pcm_playing = false;
289 /**/
291 bool pcm_is_playing(void)
293 return pcm_playing;
296 bool pcm_is_paused(void)
298 return pcm_paused;
301 void pcm_mute(bool mute)
303 #ifndef SIMULATOR
304 audiohw_mute(mute);
305 #endif
307 if (mute)
308 sleep(HZ/16);
311 #ifdef HAVE_RECORDING
312 /** Low level pcm recording apis **/
314 /* Next start for recording peaks */
315 const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
316 /* the registered callback function for when more data is available */
317 volatile pcm_more_callback_type2
318 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
319 /* DMA transfer in is currently active */
320 volatile bool pcm_recording SHAREDBSS_ATTR = false;
323 * Return recording peaks - From the end of the last peak up to
324 * current write position.
326 void pcm_calculate_rec_peaks(int *left, int *right)
328 static int peaks[2];
330 if (pcm_recording)
332 const void *addr;
333 int count;
335 addr = pcm_rec_dma_get_peak_buffer(&count);
337 if (count > 0)
339 pcm_peak_peeker(addr, count, peaks);
341 if (addr == pcm_rec_peak_addr)
342 pcm_rec_peak_addr = (int32_t *)addr + count;
345 else
347 peaks[0] = peaks[1] = 0;
350 if (left)
351 *left = peaks[0];
353 if (right)
354 *right = peaks[1];
355 } /* pcm_calculate_rec_peaks */
357 /****************************************************************************
358 * Functions that do not require targeted implementation but only a targeted
359 * interface
361 void pcm_init_recording(void)
363 logf("pcm_init_recording");
365 /* Recording init is locked unlike general pcm init since this is not
366 * just a one-time event at startup and it should and must be safe by
367 * now. */
368 pcm_rec_lock();
370 logf(" pcm_rec_dma_init");
371 pcm_rec_dma_stopped_callback();
372 pcm_rec_dma_init();
374 pcm_rec_unlock();
377 void pcm_close_recording(void)
379 logf("pcm_close_recording");
381 pcm_rec_lock();
383 if (pcm_recording)
385 logf(" pcm_rec_dma_stop");
386 pcm_rec_dma_stop();
387 pcm_rec_dma_stopped_callback();
390 logf(" pcm_rec_dma_close");
391 pcm_rec_dma_close();
393 pcm_rec_unlock();
396 void pcm_record_data(pcm_more_callback_type2 more_ready,
397 void *start, size_t size)
399 logf("pcm_record_data");
401 if (!(start && size))
403 logf(" no buffer");
404 return;
407 pcm_rec_lock();
409 pcm_callback_more_ready = more_ready;
411 logf(" pcm_rec_dma_start");
412 pcm_rec_dma_start(start, size);
413 pcm_recording = true;
415 pcm_rec_unlock();
416 } /* pcm_record_data */
418 void pcm_stop_recording(void)
420 logf("pcm_stop_recording");
422 pcm_rec_lock();
424 if (pcm_recording)
426 logf(" pcm_rec_dma_stop");
427 pcm_rec_dma_stop();
428 pcm_rec_dma_stopped_callback();
431 pcm_rec_unlock();
432 } /* pcm_stop_recording */
434 void pcm_rec_dma_stopped_callback(void)
436 pcm_recording = false;
437 pcm_callback_more_ready = NULL;
440 #endif /* HAVE_RECORDING */