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 uint64_t pcmbuf_read IBSS_ATTR
; /* Number of bytes read by DMA */
40 static uint64_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_base IBSS_ATTR
; /* Our base clock */
45 static uint32_t clock_start IBSS_ATTR
; /* Clock at playback start */
46 static int32_t clock_adjust IBSS_ATTR
; /* Clock drift adjustment */
49 int pcm_underruns
= 0;
51 /* Small silence clip. ~5.80ms @ 44.1kHz */
52 static int16_t silence
[256*2] ALIGNED_ATTR(4) = { 0 };
54 /* Advance a PCM buffer pointer by size bytes circularly */
55 static inline void pcm_advance_buffer(struct pcm_frame_header
**p
,
58 *p
= SKIPBYTES(*p
, size
);
60 *p
= SKIPBYTES(*p
, -PCMOUT_BUFSIZE
);
63 /* Inline internally but not externally */
64 inline ssize_t
pcm_output_used(void)
66 return (ssize_t
)(pcmbuf_written
- pcmbuf_read
);
69 inline ssize_t
pcm_output_free(void)
71 return (ssize_t
)(PCMOUT_BUFSIZE
- pcmbuf_written
+ pcmbuf_read
);
74 /* Audio DMA handler */
75 static void get_more(unsigned char **start
, size_t *size
)
79 /* Free-up the last frame played frame if any */
80 pcmbuf_read
+= pcmbuf_curr_size
;
83 sz
= pcm_output_used();
85 if (sz
> pcmbuf_threshold
)
87 pcmbuf_threshold
= PCMOUT_LOW_WM
;
91 uint32_t time
= pcmbuf_head
->time
;
92 int32_t offset
= time
- (clock_base
+ clock_adjust
);
94 sz
= pcmbuf_head
->size
;
96 if (sz
< (ssize_t
)(sizeof(pcmbuf_head
) + 4) ||
99 /* Just show a warning about this - will never happen
100 * without a bug in the audio thread code or a clobbered
102 DEBUGF("get_more: invalid size (%ld)\n", sz
);
105 if (offset
< -100*CLOCK_RATE
/1000)
107 /* Frame more than 100ms late - drop it */
108 pcm_advance_buffer(&pcmbuf_head
, sz
);
111 if (pcmbuf_read
< pcmbuf_written
)
114 /* Ran out so revert to default watermark */
115 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
117 else if (offset
< 100*CLOCK_RATE
/1000)
119 /* Frame less than 100ms early - play it */
120 *start
= pcmbuf_head
->data
;
122 pcm_advance_buffer(&pcmbuf_head
, sz
);
123 pcmbuf_curr_size
= sz
;
125 sz
-= sizeof (struct pcm_frame_header
);
129 /* Audio is time master - keep clock synchronized */
130 clock_adjust
= time
- clock_base
;
132 /* Update base clock */
133 clock_base
+= sz
>> 2;
136 /* Frame will be dropped - play silence clip */
142 /* Ran out so revert to default watermark */
143 if (pcmbuf_threshold
== PCMOUT_LOW_WM
)
146 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
149 /* Keep clock going at all times */
150 *start
= (unsigned char *)silence
;
151 *size
= sizeof (silence
);
153 clock_base
+= sizeof (silence
) / 4;
155 if (pcmbuf_read
> pcmbuf_written
)
156 pcmbuf_read
= pcmbuf_written
;
159 struct pcm_frame_header
* pcm_output_get_buffer(void)
164 void pcm_output_add_data(void)
166 size_t size
= pcmbuf_tail
->size
;
167 pcm_advance_buffer(&pcmbuf_tail
, size
);
168 pcmbuf_written
+= size
;
171 /* Flushes the buffer - clock keeps counting */
172 void pcm_output_flush(void)
174 bool playing
, paused
;
178 playing
= rb
->pcm_is_playing();
179 paused
= rb
->pcm_is_paused();
181 /* Stop PCM to clear current buffer */
185 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
186 pcmbuf_curr_size
= pcmbuf_read
= pcmbuf_written
= 0;
187 pcmbuf_head
= pcmbuf_tail
= pcm_buffer
;
188 pcm_skipped
= pcm_underruns
= 0;
190 /* Restart if playing state was current */
191 if (playing
&& !paused
)
192 rb
->pcm_play_data(get_more
, NULL
, 0);
194 rb
->pcm_play_unlock();
197 /* Seek the reference clock to the specified time - next audio data ready to
198 go to DMA should be on the buffer with the same time index or else the PCM
199 buffer should be empty */
200 void pcm_output_set_clock(uint32_t time
)
208 rb
->pcm_play_unlock();
211 uint32_t pcm_output_get_clock(void)
213 return clock_base
+ clock_adjust
214 - (rb
->pcm_get_bytes_waiting() >> 2);
217 uint32_t pcm_output_get_ticks(uint32_t *start
)
220 *start
= clock_start
;
222 return clock_base
- (rb
->pcm_get_bytes_waiting() >> 2);
225 /* Pauses/Starts pcm playback - and the clock */
226 void pcm_output_play_pause(bool play
)
230 if (rb
->pcm_is_playing())
232 rb
->pcm_play_pause(play
);
236 rb
->pcm_play_data(get_more
, NULL
, 0);
239 rb
->pcm_play_unlock();
242 /* Stops all playback and resets the clock */
243 void pcm_output_stop(void)
247 if (rb
->pcm_is_playing())
251 pcm_output_set_clock(0);
253 rb
->pcm_play_unlock();
256 /* Drains any data if the start threshold hasn't been reached */
257 void pcm_output_drain(void)
260 pcmbuf_threshold
= PCMOUT_LOW_WM
;
261 rb
->pcm_play_unlock();
264 bool pcm_output_init(void)
266 pcm_buffer
= mpeg_malloc(PCMOUT_ALLOC_SIZE
, MPEG_ALLOC_PCMOUT
);
267 if (pcm_buffer
== NULL
)
270 pcmbuf_threshold
= PCMOUT_PLAY_WM
;
271 pcmbuf_head
= pcm_buffer
;
272 pcmbuf_tail
= pcm_buffer
;
273 pcmbuf_end
= SKIPBYTES(pcm_buffer
, PCMOUT_BUFSIZE
);
274 pcmbuf_curr_size
= pcmbuf_read
= pcmbuf_written
= 0;
276 rb
->pcm_set_frequency(NATIVE_FREQUENCY
);
278 #if INPUT_SRC_CAPS != 0
279 /* Select playback */
280 rb
->audio_set_input_source(AUDIO_SRC_PLAYBACK
, SRCF_PLAYBACK
);
281 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
284 #if SILENCE_TEST_TONE
285 /* Make the silence clip a square wave */
286 const int16_t silence_amp
= INT16_MAX
/ 16;
289 for (i
= 0; i
< ARRAYLEN(silence
); i
+= 2)
291 if (i
< ARRAYLEN(silence
)/2)
293 silence
[i
] = silence_amp
;
294 silence
[i
+1] = silence_amp
;
298 silence
[i
] = -silence_amp
;
299 silence
[i
+1] = -silence_amp
;
307 void pcm_output_exit(void)
309 rb
->pcm_set_frequency(HW_SAMPR_DEFAULT
);