blob: 9f72be971970ce44c9d39132bfc56cff8be8db2c [file] [log] [blame]
Michael Sevakis6077e5b2007-10-06 22:27:27 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Michael Sevakis6077e5b2007-10-06 22:27:27 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
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
28/**
29 * Aspects implemented in the target-specific portion:
30 *
31 * ==Playback==
32 * Public -
33 * pcm_postinit
34 * pcm_get_bytes_waiting
35 * pcm_play_lock
36 * pcm_play_unlock
37 * Semi-private -
38 * pcm_play_dma_init
39 * pcm_play_dma_init
40 * pcm_play_dma_start
41 * pcm_play_dma_stop
42 * pcm_play_dma_pause
43 * pcm_play_dma_get_peak_buffer
44 * Data Read/Written within TSP -
45 * pcm_curr_sampr (RW)
46 * pcm_callback_for_more (R)
47 * pcm_playing (R)
48 * pcm_paused (R)
49 *
50 * ==Recording==
51 * Public -
52 * pcm_rec_lock
53 * pcm_rec_unlock
54 * Semi-private -
55 * pcm_rec_dma_init
56 * pcm_rec_dma_close
57 * pcm_rec_dma_start
58 * pcm_rec_dma_stop
59 * pcm_rec_dma_get_peak_buffer
60 * Data Read/Written within TSP -
61 * pcm_rec_peak_addr (RW)
62 * pcm_callback_more_ready (R)
63 * pcm_recording (R)
64 *
65 * States are set _after_ the target's pcm driver is called so that it may
Michael Sevakis05533852007-10-07 23:19:09 +000066 * know from whence the state is changed. One exception is init.
Michael Sevakis6077e5b2007-10-06 22:27:27 +000067 *
68 */
69
70/* the registered callback function to ask for more mp3 data */
71volatile pcm_more_callback_type pcm_callback_for_more
Michael Sevakis05099142008-04-06 04:34:57 +000072 SHAREDBSS_ATTR = NULL;
Michael Sevakis6077e5b2007-10-06 22:27:27 +000073/* PCM playback state */
Michael Sevakis05099142008-04-06 04:34:57 +000074volatile bool pcm_playing SHAREDBSS_ATTR = false;
Michael Sevakis6077e5b2007-10-06 22:27:27 +000075/* PCM paused state. paused implies playing */
Michael Sevakis05099142008-04-06 04:34:57 +000076volatile bool pcm_paused SHAREDBSS_ATTR = false;
Michael Sevakis6077e5b2007-10-06 22:27:27 +000077/* samplerate of currently playing audio - undefined if stopped */
Michael Sevakis05099142008-04-06 04:34:57 +000078unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0;
Michael Sevakis6077e5b2007-10-06 22:27:27 +000079
80/**
81 * Do peak calculation using distance squared from axis and save a lot
82 * of jumps and negation. Don't bother with the calculations of left or
83 * right only as it's never really used and won't save much time.
84 *
85 * Used for recording and playback.
86 */
87static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
88{
89 int32_t peak_l = 0, peak_r = 0;
90 int32_t peaksq_l = 0, peaksq_r = 0;
91
92 do
93 {
94 int32_t value = *(int32_t *)addr;
95 int32_t ch, chsq;
96#ifdef ROCKBOX_BIG_ENDIAN
97 ch = value >> 16;
98#else
99 ch = (int16_t)value;
100#endif
101 chsq = ch*ch;
102 if (chsq > peaksq_l)
103 peak_l = ch, peaksq_l = chsq;
104
105#ifdef ROCKBOX_BIG_ENDIAN
106 ch = (int16_t)value;
107#else
108 ch = value >> 16;
109#endif
110 chsq = ch*ch;
111 if (chsq > peaksq_r)
112 peak_r = ch, peaksq_r = chsq;
113
114 addr += 16;
115 count -= 4;
116 }
117 while (count > 0);
118
119 peaks[0] = abs(peak_l);
120 peaks[1] = abs(peak_r);
121}
122
123void pcm_calculate_peaks(int *left, int *right)
124{
125 static int peaks[2] = { 0, 0 };
126 static unsigned long last_peak_tick = 0;
127 static unsigned long frame_period = 0;
128
129 long tick = current_tick;
130
131 /* Throttled peak ahead based on calling period */
132 long period = tick - last_peak_tick;
133
134 /* Keep reasonable limits on period */
135 if (period < 1)
136 period = 1;
137 else if (period > HZ/5)
138 period = HZ/5;
139
140 frame_period = (3*frame_period + period) >> 2;
141
142 last_peak_tick = tick;
143
144 if (pcm_playing && !pcm_paused)
145 {
146 const void *addr;
147 int count, framecount;
148
149 addr = pcm_play_dma_get_peak_buffer(&count);
150
151 framecount = frame_period*pcm_curr_sampr / HZ;
152 count = MIN(framecount, count);
153
154 if (count > 0)
155 pcm_peak_peeker(addr, count, peaks);
156 }
157 else
158 {
159 peaks[0] = peaks[1] = 0;
160 }
161
162 if (left)
163 *left = peaks[0];
164
165 if (right)
166 *right = peaks[1];
167}
168
169/****************************************************************************
170 * Functions that do not require targeted implementation but only a targeted
171 * interface
172 */
173
174/* This should only be called at startup before any audio playback or
175 recording is attempted */
176void pcm_init(void)
177{
178 logf("pcm_init");
179
180 pcm_play_dma_stopped_callback();
181
182 logf(" pcm_play_dma_init");
183 pcm_play_dma_init();
184}
185
186/* Common code to pcm_play_data and pcm_play_pause */
187static void pcm_play_data_start(unsigned char *start, size_t size)
188{
189 if (!(start && size))
190 {
191 pcm_more_callback_type get_more = pcm_callback_for_more;
192 size = 0;
193 if (get_more)
194 {
195 logf(" get_more");
196 get_more(&start, &size);
197 }
198 }
199
200 if (start && size)
201 {
202 logf(" pcm_play_dma_start");
203 pcm_play_dma_start(start, size);
204 pcm_playing = true;
205 pcm_paused = false;
206 return;
207 }
208
209 /* Force a stop */
210 logf(" pcm_play_dma_stop");
211 pcm_play_dma_stop();
212 pcm_play_dma_stopped_callback();
213}
214
215void pcm_play_data(pcm_more_callback_type get_more,
216 unsigned char *start, size_t size)
217{
218 logf("pcm_play_data");
219
220 pcm_play_lock();
221
222 pcm_callback_for_more = get_more;
223
224 logf(" pcm_play_dma_start");
225 pcm_play_data_start(start, size);
226
227 pcm_play_unlock();
228}
229
230void pcm_play_pause(bool play)
231{
232 logf("pcm_play_pause: %s", play ? "play" : "pause");
233
234 pcm_play_lock();
235
236 if (play == pcm_paused && pcm_playing)
237 {
238 if (!play)
239 {
240 logf(" pcm_play_dma_pause");
241 pcm_play_dma_pause(true);
242 pcm_paused = true;
243 }
244 else if (pcm_get_bytes_waiting() > 0)
245 {
246 logf(" pcm_play_dma_pause");
247 pcm_play_dma_pause(false);
248 pcm_paused = false;
249 }
250 else
251 {
252 logf(" pcm_play_dma_start: no data");
253 pcm_play_data_start(NULL, 0);
254 }
255 }
256 else
257 {
258 logf(" no change");
259 }
260
261 pcm_play_unlock();
262}
263
264void pcm_play_stop(void)
265{
266 logf("pcm_play_stop");
267
268 pcm_play_lock();
269
270 if (pcm_playing)
271 {
272 logf(" pcm_play_dma_stop");
273 pcm_play_dma_stop();
274 pcm_play_dma_stopped_callback();
275 }
276 else
277 {
278 logf(" not playing");
279 }
280
281 pcm_play_unlock();
282}
283
284void pcm_play_dma_stopped_callback(void)
285{
286 pcm_callback_for_more = NULL;
287 pcm_paused = false;
288 pcm_playing = false;
289}
290
291/**/
292
293bool pcm_is_playing(void)
294{
295 return pcm_playing;
296}
297
298bool pcm_is_paused(void)
299{
300 return pcm_paused;
301}
302
303void pcm_mute(bool mute)
304{
305#ifndef SIMULATOR
306 audiohw_mute(mute);
307#endif
308
309 if (mute)
310 sleep(HZ/16);
311}
312
313#ifdef HAVE_RECORDING
314/** Low level pcm recording apis **/
315
316/* Next start for recording peaks */
Michael Sevakis05099142008-04-06 04:34:57 +0000317const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000318/* the registered callback function for when more data is available */
319volatile pcm_more_callback_type2
Michael Sevakis05099142008-04-06 04:34:57 +0000320 pcm_callback_more_ready SHAREDBSS_ATTR = NULL;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000321/* DMA transfer in is currently active */
Michael Sevakis05099142008-04-06 04:34:57 +0000322volatile bool pcm_recording SHAREDBSS_ATTR = false;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000323
324/**
325 * Return recording peaks - From the end of the last peak up to
326 * current write position.
327 */
328void pcm_calculate_rec_peaks(int *left, int *right)
329{
330 static int peaks[2];
331
332 if (pcm_recording)
333 {
334 const void *addr;
335 int count;
336
337 addr = pcm_rec_dma_get_peak_buffer(&count);
338
339 if (count > 0)
340 {
341 pcm_peak_peeker(addr, count, peaks);
342
343 if (addr == pcm_rec_peak_addr)
344 pcm_rec_peak_addr = (int32_t *)addr + count;
345 }
346 }
347 else
348 {
349 peaks[0] = peaks[1] = 0;
350 }
351
352 if (left)
353 *left = peaks[0];
354
355 if (right)
356 *right = peaks[1];
357} /* pcm_calculate_rec_peaks */
358
359/****************************************************************************
360 * Functions that do not require targeted implementation but only a targeted
361 * interface
362 */
363void pcm_init_recording(void)
364{
365 logf("pcm_init_recording");
366
Michael Sevakis05533852007-10-07 23:19:09 +0000367 /* Recording init is locked unlike general pcm init since this is not
368 * just a one-time event at startup and it should and must be safe by
369 * now. */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000370 pcm_rec_lock();
371
372 logf(" pcm_rec_dma_init");
373 pcm_rec_dma_stopped_callback();
374 pcm_rec_dma_init();
375
376 pcm_rec_unlock();
377}
378
379void pcm_close_recording(void)
380{
381 logf("pcm_close_recording");
382
383 pcm_rec_lock();
384
385 if (pcm_recording)
386 {
387 logf(" pcm_rec_dma_stop");
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000388 pcm_rec_dma_stop();
Michael Sevakis05533852007-10-07 23:19:09 +0000389 pcm_rec_dma_stopped_callback();
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000390 }
391
392 logf(" pcm_rec_dma_close");
393 pcm_rec_dma_close();
394
395 pcm_rec_unlock();
396}
397
398void pcm_record_data(pcm_more_callback_type2 more_ready,
399 void *start, size_t size)
400{
401 logf("pcm_record_data");
402
403 if (!(start && size))
404 {
405 logf(" no buffer");
406 return;
407 }
408
409 pcm_rec_lock();
410
411 pcm_callback_more_ready = more_ready;
412
413 logf(" pcm_rec_dma_start");
414 pcm_rec_dma_start(start, size);
415 pcm_recording = true;
416
417 pcm_rec_unlock();
418} /* pcm_record_data */
419
420void pcm_stop_recording(void)
421{
422 logf("pcm_stop_recording");
423
424 pcm_rec_lock();
425
426 if (pcm_recording)
427 {
428 logf(" pcm_rec_dma_stop");
429 pcm_rec_dma_stop();
430 pcm_rec_dma_stopped_callback();
431 }
432
433 pcm_rec_unlock();
434} /* pcm_stop_recording */
435
436void pcm_rec_dma_stopped_callback(void)
437{
438 pcm_recording = false;
439 pcm_callback_more_ready = NULL;
440}
441
442#endif /* HAVE_RECORDING */