1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
27 * Aspects implemented in the target-specific portion:
32 * pcm_get_bytes_waiting
41 * pcm_play_dma_get_peak_buffer
42 * Data Read/Written within TSP -
44 * pcm_callback_for_more (R)
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)
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 NOCACHEBSS_ATTR
= NULL
;
71 /* PCM playback state */
72 volatile bool pcm_playing NOCACHEBSS_ATTR
= false;
73 /* PCM paused state. paused implies playing */
74 volatile bool pcm_paused NOCACHEBSS_ATTR
= false;
75 /* samplerate of currently playing audio - undefined if stopped */
76 unsigned long pcm_curr_sampr NOCACHEBSS_ATTR
= 0;
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
;
94 #ifdef ROCKBOX_BIG_ENDIAN
101 peak_l
= ch
, peaksq_l
= chsq
;
103 #ifdef ROCKBOX_BIG_ENDIAN
110 peak_r
= ch
, peaksq_r
= chsq
;
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 */
135 else if (period
> HZ
/5)
138 frame_period
= (3*frame_period
+ period
) >> 2;
140 last_peak_tick
= tick
;
142 if (pcm_playing
&& !pcm_paused
)
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
);
153 pcm_peak_peeker(addr
, count
, peaks
);
157 peaks
[0] = peaks
[1] = 0;
167 /****************************************************************************
168 * Functions that do not require targeted implementation but only a targeted
172 /* This should only be called at startup before any audio playback or
173 recording is attempted */
178 pcm_play_dma_stopped_callback();
180 logf(" 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
;
194 get_more(&start
, &size
);
200 logf(" pcm_play_dma_start");
201 pcm_play_dma_start(start
, size
);
208 logf(" 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");
220 pcm_callback_for_more
= get_more
;
222 logf(" pcm_play_dma_start");
223 pcm_play_data_start(start
, size
);
228 void pcm_play_pause(bool play
)
230 logf("pcm_play_pause: %s", play
? "play" : "pause");
234 if (play
== pcm_paused
&& pcm_playing
)
238 logf(" pcm_play_dma_pause");
239 pcm_play_dma_pause(true);
242 else if (pcm_get_bytes_waiting() > 0)
244 logf(" pcm_play_dma_pause");
245 pcm_play_dma_pause(false);
250 logf(" pcm_play_dma_start: no data");
251 pcm_play_data_start(NULL
, 0);
262 void pcm_play_stop(void)
264 logf("pcm_play_stop");
270 logf(" pcm_play_dma_stop");
272 pcm_play_dma_stopped_callback();
276 logf(" not playing");
282 void pcm_play_dma_stopped_callback(void)
284 pcm_callback_for_more
= NULL
;
291 bool pcm_is_playing(void)
296 bool pcm_is_paused(void)
301 void pcm_mute(bool mute
)
311 #ifdef HAVE_RECORDING
312 /** Low level pcm recording apis **/
314 /* Next start for recording peaks */
315 const volatile void *pcm_rec_peak_addr NOCACHEBSS_ATTR
= NULL
;
316 /* the registered callback function for when more data is available */
317 volatile pcm_more_callback_type2
318 pcm_callback_more_ready NOCACHEBSS_ATTR
= NULL
;
319 /* DMA transfer in is currently active */
320 volatile bool pcm_recording NOCACHEBSS_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
)
335 addr
= pcm_rec_dma_get_peak_buffer(&count
);
339 pcm_peak_peeker(addr
, count
, peaks
);
341 if (addr
== pcm_rec_peak_addr
)
342 pcm_rec_peak_addr
= (int32_t *)addr
+ count
;
347 peaks
[0] = peaks
[1] = 0;
355 } /* pcm_calculate_rec_peaks */
357 /****************************************************************************
358 * Functions that do not require targeted implementation but only a targeted
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
370 logf(" pcm_rec_dma_init");
371 pcm_rec_dma_stopped_callback();
377 void pcm_close_recording(void)
379 logf("pcm_close_recording");
385 logf(" pcm_rec_dma_stop");
387 pcm_rec_dma_stopped_callback();
390 logf(" pcm_rec_dma_close");
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
))
409 pcm_callback_more_ready
= more_ready
;
411 logf(" pcm_rec_dma_start");
412 pcm_rec_dma_start(start
, size
);
413 pcm_recording
= true;
416 } /* pcm_record_data */
418 void pcm_stop_recording(void)
420 logf("pcm_stop_recording");
426 logf(" pcm_rec_dma_stop");
428 pcm_rec_dma_stopped_callback();
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 */