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 *start
= pcmbuf_head
->data
;
132 pcm_advance_buffer(&pcmbuf_head
, sz
);
133 pcmbuf_curr_size
= sz
;
139 /* Audio is time master - keep clock synchronized */
140 clock_time
= time
+ (sz
>> 2);
142 /* Update base clock */
143 clock_tick
+= sz
>> 2;
146 /* Frame will be dropped - play silence clip */
152 /* Ran out so revert to default watermark */
153 if (pcmbuf_threshold
== PCMOUT_LOW_WM
)
156 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
159 /* Keep clock going at all times */
160 *start
= (unsigned char *)silence
;
161 *size
= sizeof (silence
);
163 clock_tick
+= sizeof (silence
) / 4;
164 clock_time
+= sizeof (silence
) / 4;
167 pcmbuf_read
= pcmbuf_written
;
170 /** Public interface **/
172 /* Return a buffer pointer if at least size bytes are available and if so,
173 * give the actual free space */
174 unsigned char * pcm_output_get_buffer(ssize_t
*size
)
177 ssize_t free
= pcm_output_bytes_free() - PCM_HDR_SIZE
;
179 if (sz
>= 0 && free
>= sz
)
181 *size
= free
; /* return actual free space (- header) */
182 return pcmbuf_tail
->data
;
185 /* Leave *size alone so caller doesn't have to reinit */
189 /* Commit the buffer returned by pcm_ouput_get_buffer; timestamp is PCM
190 * clock time units, not video format time units */
191 bool pcm_output_commit_data(ssize_t size
, uint32_t timestamp
)
193 if (size
<= 0 || (size
& 3))
194 return false; /* invalid */
196 size
+= PCM_HDR_SIZE
;
198 if (size
> pcm_output_bytes_free())
199 return false; /* too big */
201 pcmbuf_tail
->size
= size
;
202 pcmbuf_tail
->time
= timestamp
;
204 pcm_advance_buffer(&pcmbuf_tail
, size
);
205 pcmbuf_written
+= size
;
210 /* Returns 'true' if the buffer is completely empty */
211 bool pcm_output_empty(void)
213 return pcm_output_bytes_used() <= 0;
216 /* Flushes the buffer - clock keeps counting */
217 void pcm_output_flush(void)
219 bool playing
, paused
;
223 playing
= rb
->pcm_is_playing();
224 paused
= rb
->pcm_is_paused();
226 /* Stop PCM to clear current buffer */
232 /* Restart if playing state was current */
233 if (playing
&& !paused
)
234 rb
->pcm_play_data(get_more
, NULL
, 0);
236 rb
->pcm_play_unlock();
239 /* Seek the reference clock to the specified time - next audio data ready to
240 go to DMA should be on the buffer with the same time index or else the PCM
241 buffer should be empty */
242 void pcm_output_set_clock(uint32_t time
)
250 rb
->pcm_play_unlock();
253 /* Return the clock as synchronized by audio frame timestamps */
254 uint32_t pcm_output_get_clock(void)
258 /* Reread if data race detected - rem will be 0 if driver hasn't yet
259 * updated to the new buffer size. Also be sure pcm state doesn't
260 * cause indefinite loop.
262 * FYI: NOT scrutinized for rd/wr reordering on different cores. */
266 rem
= rb
->pcm_get_bytes_waiting() >> 2;
268 while (UNLIKELY(time
!= clock_time
||
269 (rem
== 0 && rb
->pcm_is_playing() && !rb
->pcm_is_paused())));
275 /* Return the raw clock as counted from the last pcm_output_set_clock
277 uint32_t pcm_output_get_ticks(uint32_t *start
)
281 /* Same procedure as pcm_output_get_clock */
285 rem
= rb
->pcm_get_bytes_waiting() >> 2;
287 while (UNLIKELY(tick
!= clock_tick
||
288 (rem
== 0 && rb
->pcm_is_playing() && !rb
->pcm_is_paused())));
291 *start
= clock_start
;
296 /* Pauses/Starts pcm playback - and the clock */
297 void pcm_output_play_pause(bool play
)
301 if (rb
->pcm_is_playing())
303 rb
->pcm_play_pause(play
);
307 rb
->pcm_play_data(get_more
, NULL
, 0);
310 rb
->pcm_play_unlock();
313 /* Stops all playback and resets the clock */
314 void pcm_output_stop(void)
318 if (rb
->pcm_is_playing())
322 pcm_output_set_clock(0);
324 rb
->pcm_play_unlock();
327 /* Drains any data if the start threshold hasn't been reached */
328 void pcm_output_drain(void)
331 pcmbuf_threshold
= PCMOUT_LOW_WM
;
332 rb
->pcm_play_unlock();
335 bool pcm_output_init(void)
337 pcm_buffer
= mpeg_malloc(PCMOUT_ALLOC_SIZE
, MPEG_ALLOC_PCMOUT
);
338 if (pcm_buffer
== NULL
)
341 pcmbuf_end
= SKIPBYTES(pcm_buffer
, PCMOUT_BUFSIZE
);
345 /* Some targets could play at the movie frequency without resampling but
346 * as of now DSP assumes a certain frequency (always 44100Hz) so
347 * resampling will be needed for other movie audio rates. */
348 rb
->pcm_set_frequency(NATIVE_FREQUENCY
);
350 #if INPUT_SRC_CAPS != 0
351 /* Select playback */
352 rb
->audio_set_input_source(AUDIO_SRC_PLAYBACK
, SRCF_PLAYBACK
);
353 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
356 #if SILENCE_TEST_TONE
357 /* Make the silence clip a square wave */
358 const int16_t silence_amp
= INT16_MAX
/ 16;
361 for (i
= 0; i
< ARRAYLEN(silence
); i
+= 2)
363 if (i
< ARRAYLEN(silence
)/2)
365 silence
[i
] = silence_amp
;
366 silence
[i
+1] = silence_amp
;
370 silence
[i
] = -silence_amp
;
371 silence
[i
+1] = -silence_amp
;
379 void pcm_output_exit(void)
381 rb
->pcm_set_frequency(HW_SAMPR_DEFAULT
);