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 ****************************************************************************/
25 /* Define LOGF_ENABLE to enable logf output in this file */
26 /*#define LOGF_ENABLE*/
31 #include "pcm-internal.h"
32 #include "pcm_mixer.h"
35 * Aspects implemented in the target-specific portion:
40 * pcm_get_bytes_waiting
44 * pcm_play_dma_complete_callback
45 * pcm_play_dma_status_callback
47 * pcm_play_dma_postinit
51 * pcm_play_dma_get_peak_buffer
52 * Data Read/Written within TSP -
59 * ==Playback/Recording==
63 * pcm_dma_apply_settings
70 * pcm_rec_dma_complete_callback
71 * pcm_rec_dma_status_callback
76 * pcm_rec_dma_get_peak_buffer
77 * Data Read/Written within TSP -
80 * States are set _after_ the target's pcm driver is called so that it may
81 * know from whence the state is changed. One exception is init.
85 /* 'true' when all stages of pcm initialization have completed */
86 static bool pcm_is_ready
= false;
88 /* The registered callback function to ask for more mp3 data */
89 static volatile pcm_play_callback_type
90 pcm_callback_for_more SHAREDBSS_ATTR
= NULL
;
91 /* The registered callback function to inform of DMA status */
92 volatile pcm_status_callback_type
93 pcm_play_status_callback SHAREDBSS_ATTR
= NULL
;
94 /* PCM playback state */
95 volatile bool pcm_playing SHAREDBSS_ATTR
= false;
96 /* PCM paused state. paused implies playing */
97 volatile bool pcm_paused SHAREDBSS_ATTR
= false;
98 /* samplerate of currently playing audio - undefined if stopped */
99 unsigned long pcm_curr_sampr SHAREDBSS_ATTR
= 0;
100 /* samplerate waiting to be set */
101 unsigned long pcm_sampr SHAREDBSS_ATTR
= HW_SAMPR_DEFAULT
;
102 /* samplerate frequency selection index */
103 int pcm_fsel SHAREDBSS_ATTR
= HW_FREQ_DEFAULT
;
105 /* peak data for the global peak values - i.e. what the final output is */
106 static struct pcm_peaks global_peaks
;
108 /* Called internally by functions to reset the state */
109 static void pcm_play_stopped(void)
111 pcm_callback_for_more
= NULL
;
112 pcm_play_status_callback
= NULL
;
117 static void pcm_wait_for_init(void)
119 while (!pcm_is_ready
)
124 * Perform peak calculation on a buffer of packed 16-bit samples.
126 * Used for recording and playback.
128 static void pcm_peak_peeker(const int32_t *addr
, int count
, uint16_t peaks
[2])
130 int peak_l
= 0, peak_r
= 0;
131 const int32_t * const end
= addr
+ count
;
135 int32_t value
= *addr
;
138 #ifdef ROCKBOX_BIG_ENDIAN
148 #ifdef ROCKBOX_BIG_ENDIAN
166 void pcm_do_peak_calculation(struct pcm_peaks
*peaks
, bool active
,
167 const void *addr
, int count
)
169 long tick
= current_tick
;
171 /* Peak no farther ahead than expected period to avoid overcalculation */
172 long period
= tick
- peaks
->tick
;
174 /* Keep reasonable limits on period */
177 else if (period
> HZ
/5)
180 peaks
->period
= (3*peaks
->period
+ period
) >> 2;
185 int framecount
= peaks
->period
*pcm_curr_sampr
/ HZ
;
186 count
= MIN(framecount
, count
);
189 pcm_peak_peeker((int32_t *)addr
, count
, peaks
->val
);
190 /* else keep previous peak values */
195 peaks
->val
[0] = peaks
->val
[1] = 0;
199 void pcm_calculate_peaks(int *left
, int *right
)
202 const void *addr
= pcm_play_dma_get_peak_buffer(&count
);
204 pcm_do_peak_calculation(&global_peaks
, pcm_playing
&& !pcm_paused
,
208 *left
= global_peaks
.val
[0];
211 *right
= global_peaks
.val
[1];
214 const void* pcm_get_peak_buffer(int * count
)
216 return pcm_play_dma_get_peak_buffer(count
);
219 bool pcm_is_playing(void)
224 bool pcm_is_paused(void)
229 /****************************************************************************
230 * Functions that do not require targeted implementation but only a targeted
234 /* This should only be called at startup before any audio playback or
235 recording is attempted */
242 pcm_set_frequency(HW_SAMPR_DEFAULT
);
244 logf(" pcm_play_dma_init");
248 /* Finish delayed init */
249 void pcm_postinit(void)
251 logf("pcm_postinit");
253 logf(" pcm_play_dma_postinit");
255 pcm_play_dma_postinit();
260 bool pcm_is_initialized(void)
265 /* Common code to pcm_play_data and pcm_play_pause */
266 static void pcm_play_data_start(const void *addr
, size_t size
)
268 ALIGN_AUDIOBUF(addr
, size
);
272 pcm_play_callback_type get_more
= pcm_callback_for_more
;
279 get_more(&addr
, &size
);
280 ALIGN_AUDIOBUF(addr
, size
);
286 logf(" pcm_play_dma_start");
287 pcm_apply_settings();
288 pcm_play_dma_start(addr
, size
);
295 logf(" pcm_play_dma_stop");
300 void pcm_play_data(pcm_play_callback_type get_more
,
301 pcm_status_callback_type status_cb
,
302 const void *start
, size_t size
)
304 logf("pcm_play_data");
308 pcm_callback_for_more
= get_more
;
309 pcm_play_status_callback
= status_cb
;
311 logf(" pcm_play_data_start");
312 pcm_play_data_start(start
, size
);
317 bool pcm_play_dma_complete_callback(enum pcm_dma_status status
,
318 const void **addr
, size_t *size
)
320 /* Check status callback first if error */
321 if (status
< PCM_DMAST_OK
)
322 status
= pcm_play_dma_status_callback(status
);
324 pcm_play_callback_type get_more
= pcm_callback_for_more
;
326 if (get_more
&& status
>= PCM_DMAST_OK
)
331 /* Call registered callback to obtain next buffer */
332 get_more(addr
, size
);
333 ALIGN_AUDIOBUF(*addr
, *size
);
339 /* Error, callback missing or no more DMA to do */
346 void pcm_play_pause(bool play
)
348 logf("pcm_play_pause: %s", play
? "play" : "pause");
352 if (play
== pcm_paused
&& pcm_playing
)
356 logf(" pcm_play_dma_pause");
357 pcm_play_dma_pause(true);
360 else if (pcm_get_bytes_waiting() > 0)
362 logf(" pcm_play_dma_pause");
363 pcm_apply_settings();
364 pcm_play_dma_pause(false);
369 logf(" pcm_play_dma_start: no data");
370 pcm_play_data_start(NULL
, 0);
381 void pcm_play_stop(void)
383 logf("pcm_play_stop");
389 logf(" pcm_play_dma_stop");
395 logf(" not playing");
403 /* set frequency next frequency used by the audio hardware -
404 * what pcm_apply_settings will set */
405 void pcm_set_frequency(unsigned int samplerate
)
407 logf("pcm_set_frequency");
411 #ifdef CONFIG_SAMPR_TYPES
412 unsigned int type
= samplerate
& SAMPR_TYPE_MASK
;
413 samplerate
&= ~SAMPR_TYPE_MASK
;
415 /* For now, supported targets have direct conversion when configured with
416 * CONFIG_SAMPR_TYPES.
417 * Some hypothetical target with independent rates would need slightly
418 * different handling throughout this source. */
419 samplerate
= pcm_sampr_to_hw_sampr(samplerate
, type
);
420 #endif /* CONFIG_SAMPR_TYPES */
422 index
= round_value_to_list32(samplerate
, hw_freq_sampr
,
425 if (samplerate
!= hw_freq_sampr
[index
])
426 index
= HW_FREQ_DEFAULT
; /* Invalid = default */
428 pcm_sampr
= hw_freq_sampr
[index
];
432 /* apply pcm settings to the hardware */
433 void pcm_apply_settings(void)
435 logf("pcm_apply_settings");
439 if (pcm_sampr
!= pcm_curr_sampr
)
441 logf(" pcm_dma_apply_settings");
442 pcm_dma_apply_settings();
443 pcm_curr_sampr
= pcm_sampr
;
447 #ifdef HAVE_RECORDING
448 /** Low level pcm recording apis **/
450 /* Next start for recording peaks */
451 static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR
= NULL
;
452 /* the registered callback function for when more data is available */
453 static volatile pcm_rec_callback_type
454 pcm_callback_more_ready SHAREDBSS_ATTR
= NULL
;
455 volatile pcm_status_callback_type
456 pcm_rec_status_callback SHAREDBSS_ATTR
= NULL
;
457 /* DMA transfer in is currently active */
458 volatile bool pcm_recording SHAREDBSS_ATTR
= false;
460 /* Called internally by functions to reset the state */
461 static void pcm_recording_stopped(void)
463 pcm_recording
= false;
464 pcm_callback_more_ready
= NULL
;
465 pcm_rec_status_callback
= NULL
;
469 * Return recording peaks - From the end of the last peak up to
470 * current write position.
472 void pcm_calculate_rec_peaks(int *left
, int *right
)
474 static uint16_t peaks
[2];
478 const void *peak_addr
= pcm_rec_peak_addr
;
479 const void *addr
= pcm_rec_dma_get_peak_buffer();
483 int count
= (int)(((intptr_t)addr
- (intptr_t)peak_addr
) >> 2);
487 pcm_peak_peeker((int32_t *)peak_addr
, count
, peaks
);
489 if (peak_addr
== pcm_rec_peak_addr
)
490 pcm_rec_peak_addr
= addr
;
493 /* else keep previous peak values */
497 peaks
[0] = peaks
[1] = 0;
505 } /* pcm_calculate_rec_peaks */
507 bool pcm_is_recording(void)
509 return pcm_recording
;
512 /****************************************************************************
513 * Functions that do not require targeted implementation but only a targeted
517 void pcm_init_recording(void)
519 logf("pcm_init_recording");
523 /* Stop the beasty before attempting recording */
526 /* Recording init is locked unlike general pcm init since this is not
527 * just a one-time event at startup and it should and must be safe by
531 logf(" pcm_rec_dma_init");
532 pcm_recording_stopped();
538 void pcm_close_recording(void)
540 logf("pcm_close_recording");
546 logf(" pcm_rec_dma_stop");
548 pcm_recording_stopped();
551 logf(" pcm_rec_dma_close");
557 void pcm_record_data(pcm_rec_callback_type more_ready
,
558 pcm_status_callback_type status_cb
,
559 void *addr
, size_t size
)
561 logf("pcm_record_data");
563 ALIGN_AUDIOBUF(addr
, size
);
573 pcm_callback_more_ready
= more_ready
;
574 pcm_rec_status_callback
= status_cb
;
576 /* Need a physical DMA address translation, if not already physical. */
577 pcm_rec_peak_addr
= pcm_rec_dma_addr(addr
);
579 logf(" pcm_rec_dma_start");
580 pcm_apply_settings();
581 pcm_rec_dma_start(addr
, size
);
582 pcm_recording
= true;
585 } /* pcm_record_data */
587 void pcm_stop_recording(void)
589 logf("pcm_stop_recording");
595 logf(" pcm_rec_dma_stop");
597 pcm_recording_stopped();
601 } /* pcm_stop_recording */
603 bool pcm_rec_dma_complete_callback(enum pcm_dma_status status
,
604 void **addr
, size_t *size
)
606 /* Check status callback first if error */
607 if (status
< PCM_DMAST_OK
)
608 status
= pcm_rec_dma_status_callback(status
);
610 pcm_rec_callback_type have_more
= pcm_callback_more_ready
;
612 if (have_more
&& status
>= PCM_DMAST_OK
)
614 /* Call registered callback to obtain next buffer */
615 have_more(addr
, size
);
616 ALIGN_AUDIOBUF(*addr
, *size
);
620 /* Need a physical DMA address translation, if not already
622 pcm_rec_peak_addr
= pcm_rec_dma_addr(*addr
);
627 /* Error, callback missing or no more DMA to do */
629 pcm_recording_stopped();
634 #endif /* HAVE_RECORDING */