1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * PCM output buffer definitions
12 * Copyright (c) 2007 Michael Sevakis
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "mpegplayer.h"
29 static struct pcm_frame_header
* ALIGNED_ATTR(4) pcm_buffer
;
30 /* End of buffer (not guard) */
31 static struct pcm_frame_header
* ALIGNED_ATTR(4) pcmbuf_end
;
33 static struct pcm_frame_header
* ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR
;
35 static struct pcm_frame_header
* ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR
;
38 static ssize_t pcmbuf_curr_size IBSS_ATTR
; /* Size of currently playing frame */
39 static ssize_t pcmbuf_read IBSS_ATTR
; /* Number of bytes read by DMA */
40 static ssize_t pcmbuf_written IBSS_ATTR
; /* Number of bytes written by source */
41 static ssize_t pcmbuf_threshold IBSS_ATTR
; /* Non-silence threshold */
44 static uint32_t clock_start IBSS_ATTR
; /* Clock at playback start */
45 static uint32_t volatile clock_tick IBSS_ATTR
; /* Our base clock */
46 static uint32_t volatile clock_time IBSS_ATTR
; /* Timestamp adjusted */
48 static int pcm_skipped
= 0;
49 static int pcm_underruns
= 0;
51 /* Small silence clip. ~5.80ms @ 44.1kHz */
52 static int16_t silence
[256*2] ALIGNED_ATTR(4) = { 0 };
54 /* Delete all buffer contents */
55 static void pcm_reset_buffer(void)
57 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
58 pcmbuf_curr_size
= pcmbuf_read
= pcmbuf_written
= 0;
59 pcmbuf_head
= pcmbuf_tail
= pcm_buffer
;
60 pcm_skipped
= pcm_underruns
= 0;
63 /* Advance a PCM buffer pointer by size bytes circularly */
64 static inline void pcm_advance_buffer(struct pcm_frame_header
**p
,
67 *p
= SKIPBYTES(*p
, size
);
69 *p
= SKIPBYTES(*p
, -PCMOUT_BUFSIZE
);
72 /* Return physical space used */
73 static inline ssize_t
pcm_output_bytes_used(void)
75 return pcmbuf_written
- pcmbuf_read
; /* wrap-safe */
78 /* Return physical space free */
79 static inline ssize_t
pcm_output_bytes_free(void)
81 return PCMOUT_BUFSIZE
- pcm_output_bytes_used();
84 /* Audio DMA handler */
85 static void get_more(unsigned char **start
, size_t *size
)
89 /* Free-up the last frame played frame if any */
90 pcmbuf_read
+= pcmbuf_curr_size
;
93 sz
= pcm_output_bytes_used();
95 if (sz
> pcmbuf_threshold
)
97 pcmbuf_threshold
= PCMOUT_LOW_WM
;
101 uint32_t time
= pcmbuf_head
->time
;
102 int32_t offset
= time
- clock_time
;
104 sz
= pcmbuf_head
->size
;
106 if (sz
< (ssize_t
)(PCM_HDR_SIZE
+ 4) ||
109 /* Just show a warning about this - will never happen
110 * without a corrupted buffer */
111 DEBUGF("get_more: invalid size (%ld)\n", (long)sz
);
114 if (offset
< -100*CLOCK_RATE
/1000)
116 /* Frame more than 100ms late - drop it */
117 pcm_advance_buffer(&pcmbuf_head
, sz
);
120 if (pcm_output_bytes_used() > 0)
123 /* Ran out so revert to default watermark */
124 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
127 else if (offset
< 100*CLOCK_RATE
/1000)
129 /* Frame less than 100ms early - play it */
130 struct pcm_frame_header
*head
= pcmbuf_head
;
132 pcm_advance_buffer(&pcmbuf_head
, sz
);
133 pcmbuf_curr_size
= sz
;
137 /* Audio is time master - keep clock synchronized */
138 clock_time
= time
+ (sz
>> 2);
140 /* Update base clock */
141 clock_tick
+= sz
>> 2;
147 /* Frame will be dropped - play silence clip */
153 /* Ran out so revert to default watermark */
154 if (pcmbuf_threshold
== PCMOUT_LOW_WM
)
157 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
160 /* Keep clock going at all times */
161 clock_time
+= sizeof (silence
) / 4;
162 clock_tick
+= sizeof (silence
) / 4;
164 *start
= (unsigned char *)silence
;
165 *size
= sizeof (silence
);
168 pcmbuf_read
= pcmbuf_written
;
171 /** Public interface **/
173 /* Return a buffer pointer if at least size bytes are available and if so,
174 * give the actual free space */
175 unsigned char * pcm_output_get_buffer(ssize_t
*size
)
178 ssize_t free
= pcm_output_bytes_free() - PCM_HDR_SIZE
;
180 if (sz
>= 0 && free
>= sz
)
182 *size
= free
; /* return actual free space (- header) */
183 return pcmbuf_tail
->data
;
186 /* Leave *size alone so caller doesn't have to reinit */
190 /* Commit the buffer returned by pcm_ouput_get_buffer; timestamp is PCM
191 * clock time units, not video format time units */
192 bool pcm_output_commit_data(ssize_t size
, uint32_t timestamp
)
194 if (size
<= 0 || (size
& 3))
195 return false; /* invalid */
197 size
+= PCM_HDR_SIZE
;
199 if (size
> pcm_output_bytes_free())
200 return false; /* too big */
202 pcmbuf_tail
->size
= size
;
203 pcmbuf_tail
->time
= timestamp
;
205 pcm_advance_buffer(&pcmbuf_tail
, size
);
206 pcmbuf_written
+= size
;
211 /* Returns 'true' if the buffer is completely empty */
212 bool pcm_output_empty(void)
214 return pcm_output_bytes_used() <= 0;
217 /* Flushes the buffer - clock keeps counting */
218 void pcm_output_flush(void)
220 bool playing
, paused
;
224 playing
= rb
->pcm_is_playing();
225 paused
= rb
->pcm_is_paused();
227 /* Stop PCM to clear current buffer */
233 /* Restart if playing state was current */
234 if (playing
&& !paused
)
235 rb
->pcm_play_data(get_more
, NULL
, 0);
237 rb
->pcm_play_unlock();
240 /* Seek the reference clock to the specified time - next audio data ready to
241 go to DMA should be on the buffer with the same time index or else the PCM
242 buffer should be empty */
243 void pcm_output_set_clock(uint32_t time
)
251 rb
->pcm_play_unlock();
254 /* Return the clock as synchronized by audio frame timestamps */
255 uint32_t pcm_output_get_clock(void)
259 /* Reread if data race detected - rem will be 0 if driver hasn't yet
260 * updated to the new buffer size. Also be sure pcm state doesn't
261 * cause indefinite loop.
263 * FYI: NOT scrutinized for rd/wr reordering on different cores. */
267 rem
= rb
->pcm_get_bytes_waiting() >> 2;
269 while (UNLIKELY(time
!= clock_time
||
270 (rem
== 0 && rb
->pcm_is_playing() && !rb
->pcm_is_paused())));
276 /* Return the raw clock as counted from the last pcm_output_set_clock
278 uint32_t pcm_output_get_ticks(uint32_t *start
)
282 /* Same procedure as pcm_output_get_clock */
286 rem
= rb
->pcm_get_bytes_waiting() >> 2;
288 while (UNLIKELY(tick
!= clock_tick
||
289 (rem
== 0 && rb
->pcm_is_playing() && !rb
->pcm_is_paused())));
292 *start
= clock_start
;
297 /* Pauses/Starts pcm playback - and the clock */
298 void pcm_output_play_pause(bool play
)
302 if (rb
->pcm_is_playing())
304 rb
->pcm_play_pause(play
);
308 rb
->pcm_play_data(get_more
, NULL
, 0);
311 rb
->pcm_play_unlock();
314 /* Stops all playback and resets the clock */
315 void pcm_output_stop(void)
319 if (rb
->pcm_is_playing())
323 pcm_output_set_clock(0);
325 rb
->pcm_play_unlock();
328 /* Drains any data if the start threshold hasn't been reached */
329 void pcm_output_drain(void)
332 pcmbuf_threshold
= PCMOUT_LOW_WM
;
333 rb
->pcm_play_unlock();
336 bool pcm_output_init(void)
338 pcm_buffer
= mpeg_malloc(PCMOUT_ALLOC_SIZE
, MPEG_ALLOC_PCMOUT
);
339 if (pcm_buffer
== NULL
)
342 pcmbuf_end
= SKIPBYTES(pcm_buffer
, PCMOUT_BUFSIZE
);
346 /* Some targets could play at the movie frequency without resampling but
347 * as of now DSP assumes a certain frequency (always 44100Hz) so
348 * resampling will be needed for other movie audio rates. */
349 rb
->pcm_set_frequency(NATIVE_FREQUENCY
);
351 #if INPUT_SRC_CAPS != 0
352 /* Select playback */
353 rb
->audio_set_input_source(AUDIO_SRC_PLAYBACK
, SRCF_PLAYBACK
);
354 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
357 #if SILENCE_TEST_TONE
358 /* Make the silence clip a square wave */
359 const int16_t silence_amp
= INT16_MAX
/ 16;
362 for (i
= 0; i
< ARRAYLEN(silence
); i
+= 2)
364 if (i
< ARRAYLEN(silence
)/2)
366 silence
[i
] = silence_amp
;
367 silence
[i
+1] = silence_amp
;
371 silence
[i
] = -silence_amp
;
372 silence
[i
+1] = -silence_amp
;
380 void pcm_output_exit(void)
382 rb
->pcm_set_frequency(HW_SAMPR_DEFAULT
);