1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
29 * Aspects implemented in the target-specific portion:
34 * pcm_get_bytes_waiting
43 * pcm_play_dma_get_peak_buffer
44 * Data Read/Written within TSP -
46 * pcm_callback_for_more (R)
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)
65 * States are set _after_ the target's pcm driver is called so that it may
66 * know from whence the state is changed. One exception is init.
70 /* the registered callback function to ask for more mp3 data */
71 volatile pcm_more_callback_type pcm_callback_for_more
72 SHAREDBSS_ATTR
= NULL
;
73 /* PCM playback state */
74 volatile bool pcm_playing SHAREDBSS_ATTR
= false;
75 /* PCM paused state. paused implies playing */
76 volatile bool pcm_paused SHAREDBSS_ATTR
= false;
77 /* samplerate of currently playing audio - undefined if stopped */
78 unsigned long pcm_curr_sampr SHAREDBSS_ATTR
= 0;
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.
85 * Used for recording and playback.
87 static void pcm_peak_peeker(const void *addr
, int count
, int peaks
[2])
89 int32_t peak_l
= 0, peak_r
= 0;
90 int32_t peaksq_l
= 0, peaksq_r
= 0;
94 int32_t value
= *(int32_t *)addr
;
96 #ifdef ROCKBOX_BIG_ENDIAN
103 peak_l
= ch
, peaksq_l
= chsq
;
105 #ifdef ROCKBOX_BIG_ENDIAN
112 peak_r
= ch
, peaksq_r
= chsq
;
119 peaks
[0] = abs(peak_l
);
120 peaks
[1] = abs(peak_r
);
123 void pcm_calculate_peaks(int *left
, int *right
)
125 static int peaks
[2] = { 0, 0 };
126 static unsigned long last_peak_tick
= 0;
127 static unsigned long frame_period
= 0;
129 long tick
= current_tick
;
131 /* Throttled peak ahead based on calling period */
132 long period
= tick
- last_peak_tick
;
134 /* Keep reasonable limits on period */
137 else if (period
> HZ
/5)
140 frame_period
= (3*frame_period
+ period
) >> 2;
142 last_peak_tick
= tick
;
144 if (pcm_playing
&& !pcm_paused
)
147 int count
, framecount
;
149 addr
= pcm_play_dma_get_peak_buffer(&count
);
151 framecount
= frame_period
*pcm_curr_sampr
/ HZ
;
152 count
= MIN(framecount
, count
);
155 pcm_peak_peeker(addr
, count
, peaks
);
159 peaks
[0] = peaks
[1] = 0;
169 /****************************************************************************
170 * Functions that do not require targeted implementation but only a targeted
174 /* This should only be called at startup before any audio playback or
175 recording is attempted */
180 pcm_play_dma_stopped_callback();
182 logf(" pcm_play_dma_init");
186 /* Common code to pcm_play_data and pcm_play_pause */
187 static void pcm_play_data_start(unsigned char *start
, size_t size
)
189 if (!(start
&& size
))
191 pcm_more_callback_type get_more
= pcm_callback_for_more
;
196 get_more(&start
, &size
);
202 logf(" pcm_play_dma_start");
203 pcm_play_dma_start(start
, size
);
210 logf(" pcm_play_dma_stop");
212 pcm_play_dma_stopped_callback();
215 void pcm_play_data(pcm_more_callback_type get_more
,
216 unsigned char *start
, size_t size
)
218 logf("pcm_play_data");
222 pcm_callback_for_more
= get_more
;
224 logf(" pcm_play_dma_start");
225 pcm_play_data_start(start
, size
);
230 void pcm_play_pause(bool play
)
232 logf("pcm_play_pause: %s", play
? "play" : "pause");
236 if (play
== pcm_paused
&& pcm_playing
)
240 logf(" pcm_play_dma_pause");
241 pcm_play_dma_pause(true);
244 else if (pcm_get_bytes_waiting() > 0)
246 logf(" pcm_play_dma_pause");
247 pcm_play_dma_pause(false);
252 logf(" pcm_play_dma_start: no data");
253 pcm_play_data_start(NULL
, 0);
264 void pcm_play_stop(void)
266 logf("pcm_play_stop");
272 logf(" pcm_play_dma_stop");
274 pcm_play_dma_stopped_callback();
278 logf(" not playing");
284 void pcm_play_dma_stopped_callback(void)
286 pcm_callback_for_more
= NULL
;
293 bool pcm_is_playing(void)
298 bool pcm_is_paused(void)
303 void pcm_mute(bool mute
)
313 #ifdef HAVE_RECORDING
314 /** Low level pcm recording apis **/
316 /* Next start for recording peaks */
317 const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR
= NULL
;
318 /* the registered callback function for when more data is available */
319 volatile pcm_more_callback_type2
320 pcm_callback_more_ready SHAREDBSS_ATTR
= NULL
;
321 /* DMA transfer in is currently active */
322 volatile bool pcm_recording SHAREDBSS_ATTR
= false;
325 * Return recording peaks - From the end of the last peak up to
326 * current write position.
328 void pcm_calculate_rec_peaks(int *left
, int *right
)
337 addr
= pcm_rec_dma_get_peak_buffer(&count
);
341 pcm_peak_peeker(addr
, count
, peaks
);
343 if (addr
== pcm_rec_peak_addr
)
344 pcm_rec_peak_addr
= (int32_t *)addr
+ count
;
349 peaks
[0] = peaks
[1] = 0;
357 } /* pcm_calculate_rec_peaks */
359 /****************************************************************************
360 * Functions that do not require targeted implementation but only a targeted
363 void pcm_init_recording(void)
365 logf("pcm_init_recording");
367 /* 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
372 logf(" pcm_rec_dma_init");
373 pcm_rec_dma_stopped_callback();
379 void pcm_close_recording(void)
381 logf("pcm_close_recording");
387 logf(" pcm_rec_dma_stop");
389 pcm_rec_dma_stopped_callback();
392 logf(" pcm_rec_dma_close");
398 void pcm_record_data(pcm_more_callback_type2 more_ready
,
399 void *start
, size_t size
)
401 logf("pcm_record_data");
403 if (!(start
&& size
))
411 pcm_callback_more_ready
= more_ready
;
413 logf(" pcm_rec_dma_start");
414 pcm_rec_dma_start(start
, size
);
415 pcm_recording
= true;
418 } /* pcm_record_data */
420 void pcm_stop_recording(void)
422 logf("pcm_stop_recording");
428 logf(" pcm_rec_dma_stop");
430 pcm_rec_dma_stopped_callback();
434 } /* pcm_stop_recording */
436 void pcm_rec_dma_stopped_callback(void)
438 pcm_recording
= false;
439 pcm_callback_more_ready
= NULL
;
442 #endif /* HAVE_RECORDING */