MPEGPlyaer: A bit of audio mutation. Remove a useless thread state. Take some control...
[kugel-rb.git] / apps / plugins / mpegplayer / pcm_output.c
blob0b8ad7701a540b8751c0b1c3ea54920a3c4bbd7c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
23 #include "plugin.h"
24 #include "mpegplayer.h"
26 /* Pointers */
28 /* Start of buffer */
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;
32 /* Read pointer */
33 static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR;
34 /* Write pointer */
35 static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
37 /* Bytes */
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 */
43 /* Clock */
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,
65 size_t size)
67 *p = SKIPBYTES(*p, size);
68 if (*p >= pcmbuf_end)
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)
87 ssize_t sz;
89 /* Free-up the last frame played frame if any */
90 pcmbuf_read += pcmbuf_curr_size;
91 pcmbuf_curr_size = 0;
93 sz = pcm_output_bytes_used();
95 if (sz > pcmbuf_threshold)
97 pcmbuf_threshold = PCMOUT_LOW_WM;
99 while (1)
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) ||
107 (sz & 3) != 0)
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);
118 pcmbuf_read += sz;
119 pcm_skipped++;
120 if (pcm_output_bytes_used() > 0)
121 continue;
123 /* Ran out so revert to default watermark */
124 pcmbuf_threshold = PCMOUT_PLAY_WM;
125 pcm_underruns++;
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;
135 sz -= PCM_HDR_SIZE;
137 *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;
144 return;
146 /* Frame will be dropped - play silence clip */
147 break;
150 else
152 /* Ran out so revert to default watermark */
153 if (pcmbuf_threshold == PCMOUT_LOW_WM)
154 pcm_underruns++;
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;
166 if (sz < 0)
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)
176 ssize_t sz = *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 */
186 return NULL;
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;
207 return true;
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;
221 rb->pcm_play_lock();
223 playing = rb->pcm_is_playing();
224 paused = rb->pcm_is_paused();
226 /* Stop PCM to clear current buffer */
227 if (playing)
228 rb->pcm_play_stop();
230 pcm_reset_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)
244 rb->pcm_play_lock();
246 clock_start = time;
247 clock_tick = time;
248 clock_time = time;
250 rb->pcm_play_unlock();
253 /* Return the clock as synchronized by audio frame timestamps */
254 uint32_t pcm_output_get_clock(void)
256 uint32_t time, rem;
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. */
265 time = clock_time;
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())));
271 return time - rem;
275 /* Return the raw clock as counted from the last pcm_output_set_clock
276 * call */
277 uint32_t pcm_output_get_ticks(uint32_t *start)
279 uint32_t tick, rem;
281 /* Same procedure as pcm_output_get_clock */
284 tick = clock_tick;
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())));
290 if (start)
291 *start = clock_start;
293 return tick - rem;
296 /* Pauses/Starts pcm playback - and the clock */
297 void pcm_output_play_pause(bool play)
299 rb->pcm_play_lock();
301 if (rb->pcm_is_playing())
303 rb->pcm_play_pause(play);
305 else if (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)
316 rb->pcm_play_lock();
318 if (rb->pcm_is_playing())
319 rb->pcm_play_stop();
321 pcm_output_flush();
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)
330 rb->pcm_play_lock();
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)
339 return false;
341 pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE);
343 pcm_reset_buffer();
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);
354 #endif
356 #if SILENCE_TEST_TONE
357 /* Make the silence clip a square wave */
358 const int16_t silence_amp = INT16_MAX / 16;
359 unsigned i;
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;
368 else
370 silence[i] = -silence_amp;
371 silence[i+1] = -silence_amp;
374 #endif
376 return true;
379 void pcm_output_exit(void)
381 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);