Eliminate %zd tag in printf format strings, replace them with %ld. The %z formatter...
[kugel-rb.git] / apps / plugins / mpegplayer / pcm_output.c
bloba5d8f86e5b2f22d1daf68caf73bb75e926c7f88e
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 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 */
43 /* Clock */
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 */
48 int pcm_skipped = 0;
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,
56 size_t size)
58 *p = SKIPBYTES(*p, size);
59 if (*p >= pcmbuf_end)
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)
77 ssize_t sz;
79 /* Free-up the last frame played frame if any */
80 pcmbuf_read += pcmbuf_curr_size;
81 pcmbuf_curr_size = 0;
83 sz = pcm_output_used();
85 if (sz > pcmbuf_threshold)
87 pcmbuf_threshold = PCMOUT_LOW_WM;
89 while (1)
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) ||
97 (sz & 3) != 0)
99 /* Just show a warning about this - will never happen
100 * without a bug in the audio thread code or a clobbered
101 * buffer */
102 DEBUGF("get_more: invalid size (%ld)\n", (long)sz);
105 if (offset < -100*CLOCK_RATE/1000)
107 /* Frame more than 100ms late - drop it */
108 pcm_advance_buffer(&pcmbuf_head, sz);
109 pcmbuf_read += sz;
110 pcm_skipped++;
111 if (pcmbuf_read < pcmbuf_written)
112 continue;
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);
127 *size = sz;
129 /* Audio is time master - keep clock synchronized */
130 clock_adjust = time - clock_base;
132 /* Update base clock */
133 clock_base += sz >> 2;
134 return;
136 /* Frame will be dropped - play silence clip */
137 break;
140 else
142 /* Ran out so revert to default watermark */
143 if (pcmbuf_threshold == PCMOUT_LOW_WM)
144 pcm_underruns++;
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)
161 return pcmbuf_tail;
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;
176 rb->pcm_play_lock();
178 playing = rb->pcm_is_playing();
179 paused = rb->pcm_is_paused();
181 /* Stop PCM to clear current buffer */
182 if (playing)
183 rb->pcm_play_stop();
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)
202 rb->pcm_play_lock();
204 clock_base = time;
205 clock_start = time;
206 clock_adjust = 0;
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)
219 if (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)
228 rb->pcm_play_lock();
230 if (rb->pcm_is_playing())
232 rb->pcm_play_pause(play);
234 else if (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)
245 rb->pcm_play_lock();
247 if (rb->pcm_is_playing())
248 rb->pcm_play_stop();
250 pcm_output_flush();
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)
259 rb->pcm_play_lock();
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)
268 return false;
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);
282 #endif
284 #if SILENCE_TEST_TONE
285 /* Make the silence clip a square wave */
286 const int16_t silence_amp = INT16_MAX / 16;
287 unsigned i;
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;
296 else
298 silence[i] = -silence_amp;
299 silence[i+1] = -silence_amp;
302 #endif
304 return true;
307 void pcm_output_exit(void)
309 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);