summaryrefslogtreecommitdiff
path: root/firmware/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/pcm.c')
-rw-r--r--firmware/pcm.c437
1 files changed, 437 insertions, 0 deletions
diff --git a/firmware/pcm.c b/firmware/pcm.c
new file mode 100644
index 0000000000..6e05d57f0c
--- /dev/null
+++ b/firmware/pcm.c
@@ -0,0 +1,437 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
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"
25
26/**
27 * Aspects implemented in the target-specific portion:
28 *
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)
47 *
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)
62 *
63 * States are set _after_ the target's pcm driver is called so that it may
64 * know from whence the state is changed.
65 *
66 */
67
68/* the registered callback function to ask for more mp3 data */
69volatile pcm_more_callback_type pcm_callback_for_more
70 NOCACHEBSS_ATTR = NULL;
71/* PCM playback state */
72volatile bool pcm_playing NOCACHEBSS_ATTR = false;
73/* PCM paused state. paused implies playing */
74volatile bool pcm_paused NOCACHEBSS_ATTR = false;
75/* samplerate of currently playing audio - undefined if stopped */
76unsigned long pcm_curr_sampr NOCACHEBSS_ATTR = 0;
77
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.
82 *
83 * Used for recording and playback.
84 */
85static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
86{
87 int32_t peak_l = 0, peak_r = 0;
88 int32_t peaksq_l = 0, peaksq_r = 0;
89
90 do
91 {
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;
102
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;
111
112 addr += 16;
113 count -= 4;
114 }
115 while (count > 0);
116
117 peaks[0] = abs(peak_l);
118 peaks[1] = abs(peak_r);
119}
120
121void pcm_calculate_peaks(int *left, int *right)
122{
123 static int peaks[2] = { 0, 0 };
124 static unsigned long last_peak_tick = 0;
125 static unsigned long frame_period = 0;
126
127 long tick = current_tick;
128
129 /* Throttled peak ahead based on calling period */
130 long period = tick - last_peak_tick;
131
132 /* Keep reasonable limits on period */
133 if (period < 1)
134 period = 1;
135 else if (period > HZ/5)
136 period = HZ/5;
137
138 frame_period = (3*frame_period + period) >> 2;
139
140 last_peak_tick = tick;
141
142 if (pcm_playing && !pcm_paused)
143 {
144 const void *addr;
145 int count, framecount;
146
147 addr = pcm_play_dma_get_peak_buffer(&count);
148
149 framecount = frame_period*pcm_curr_sampr / HZ;
150 count = MIN(framecount, count);
151
152 if (count > 0)
153 pcm_peak_peeker(addr, count, peaks);
154 }
155 else
156 {
157 peaks[0] = peaks[1] = 0;
158 }
159
160 if (left)
161 *left = peaks[0];
162
163 if (right)
164 *right = peaks[1];
165}
166
167/****************************************************************************
168 * Functions that do not require targeted implementation but only a targeted
169 * interface
170 */
171
172/* This should only be called at startup before any audio playback or
173 recording is attempted */
174void pcm_init(void)
175{
176 logf("pcm_init");
177
178 pcm_play_dma_stopped_callback();
179
180 logf(" pcm_play_dma_init");
181 pcm_play_dma_init();
182}
183
184/* Common code to pcm_play_data and pcm_play_pause */
185static void pcm_play_data_start(unsigned char *start, size_t size)
186{
187 if (!(start && size))
188 {
189 pcm_more_callback_type get_more = pcm_callback_for_more;
190 size = 0;
191 if (get_more)
192 {
193 logf(" get_more");
194 get_more(&start, &size);
195 }
196 }
197
198 if (start && size)
199 {
200 logf(" pcm_play_dma_start");
201 pcm_play_dma_start(start, size);
202 pcm_playing = true;
203 pcm_paused = false;
204 return;
205 }
206
207 /* Force a stop */
208 logf(" pcm_play_dma_stop");
209 pcm_play_dma_stop();
210 pcm_play_dma_stopped_callback();
211}
212
213void pcm_play_data(pcm_more_callback_type get_more,
214 unsigned char *start, size_t size)
215{
216 logf("pcm_play_data");
217
218 pcm_play_lock();
219
220 pcm_callback_for_more = get_more;
221
222 logf(" pcm_play_dma_start");
223 pcm_play_data_start(start, size);
224
225 pcm_play_unlock();
226}
227
228void pcm_play_pause(bool play)
229{
230 logf("pcm_play_pause: %s", play ? "play" : "pause");
231
232 pcm_play_lock();
233
234 if (play == pcm_paused && pcm_playing)
235 {
236 if (!play)
237 {
238 logf(" pcm_play_dma_pause");
239 pcm_play_dma_pause(true);
240 pcm_paused = true;
241 }
242 else if (pcm_get_bytes_waiting() > 0)
243 {
244 logf(" pcm_play_dma_pause");
245 pcm_play_dma_pause(false);
246 pcm_paused = false;
247 }
248 else
249 {
250 logf(" pcm_play_dma_start: no data");
251 pcm_play_data_start(NULL, 0);
252 }
253 }
254 else
255 {
256 logf(" no change");
257 }
258
259 pcm_play_unlock();
260}
261
262void pcm_play_stop(void)
263{
264 logf("pcm_play_stop");
265
266 pcm_play_lock();
267
268 if (pcm_playing)
269 {
270 logf(" pcm_play_dma_stop");
271 pcm_play_dma_stop();
272 pcm_play_dma_stopped_callback();
273 }
274 else
275 {
276 logf(" not playing");
277 }
278
279 pcm_play_unlock();
280}
281
282void pcm_play_dma_stopped_callback(void)
283{
284 pcm_callback_for_more = NULL;
285 pcm_paused = false;
286 pcm_playing = false;
287}
288
289/**/
290
291bool pcm_is_playing(void)
292{
293 return pcm_playing;
294}
295
296bool pcm_is_paused(void)
297{
298 return pcm_paused;
299}
300
301void pcm_mute(bool mute)
302{
303#ifndef SIMULATOR
304 audiohw_mute(mute);
305#endif
306
307 if (mute)
308 sleep(HZ/16);
309}
310
311#ifdef HAVE_RECORDING
312/** Low level pcm recording apis **/
313
314/* Next start for recording peaks */
315const volatile void *pcm_rec_peak_addr NOCACHEBSS_ATTR = NULL;
316/* the registered callback function for when more data is available */
317volatile pcm_more_callback_type2
318 pcm_callback_more_ready NOCACHEBSS_ATTR = NULL;
319/* DMA transfer in is currently active */
320volatile bool pcm_recording NOCACHEBSS_ATTR = false;
321
322/**
323 * Return recording peaks - From the end of the last peak up to
324 * current write position.
325 */
326void pcm_calculate_rec_peaks(int *left, int *right)
327{
328 static int peaks[2];
329
330 if (pcm_recording)
331 {
332 const void *addr;
333 int count;
334
335 addr = pcm_rec_dma_get_peak_buffer(&count);
336
337 if (count > 0)
338 {
339 pcm_peak_peeker(addr, count, peaks);
340
341 if (addr == pcm_rec_peak_addr)
342 pcm_rec_peak_addr = (int32_t *)addr + count;
343 }
344 }
345 else
346 {
347 peaks[0] = peaks[1] = 0;
348 }
349
350 if (left)
351 *left = peaks[0];
352
353 if (right)
354 *right = peaks[1];
355} /* pcm_calculate_rec_peaks */
356
357/****************************************************************************
358 * Functions that do not require targeted implementation but only a targeted
359 * interface
360 */
361void pcm_init_recording(void)
362{
363 logf("pcm_init_recording");
364
365 pcm_rec_lock();
366
367 logf(" pcm_rec_dma_init");
368 pcm_rec_dma_stopped_callback();
369 pcm_rec_dma_init();
370
371 pcm_rec_unlock();
372}
373
374void pcm_close_recording(void)
375{
376 logf("pcm_close_recording");
377
378 pcm_rec_lock();
379
380 if (pcm_recording)
381 {
382 logf(" pcm_rec_dma_stop");
383 pcm_rec_dma_stopped_callback();
384 pcm_rec_dma_stop();
385 }
386
387 logf(" pcm_rec_dma_close");
388 pcm_rec_dma_close();
389
390 pcm_rec_unlock();
391}
392
393void pcm_record_data(pcm_more_callback_type2 more_ready,
394 void *start, size_t size)
395{
396 logf("pcm_record_data");
397
398 if (!(start && size))
399 {
400 logf(" no buffer");
401 return;
402 }
403
404 pcm_rec_lock();
405
406 pcm_callback_more_ready = more_ready;
407
408 logf(" pcm_rec_dma_start");
409 pcm_rec_dma_start(start, size);
410 pcm_recording = true;
411
412 pcm_rec_unlock();
413} /* pcm_record_data */
414
415void pcm_stop_recording(void)
416{
417 logf("pcm_stop_recording");
418
419 pcm_rec_lock();
420
421 if (pcm_recording)
422 {
423 logf(" pcm_rec_dma_stop");
424 pcm_rec_dma_stop();
425 pcm_rec_dma_stopped_callback();
426 }
427
428 pcm_rec_unlock();
429} /* pcm_stop_recording */
430
431void pcm_rec_dma_stopped_callback(void)
432{
433 pcm_recording = false;
434 pcm_callback_more_ready = NULL;
435}
436
437#endif /* HAVE_RECORDING */