mpegplayer: Misc seeking tweaks 1) Consolidate some code amongst functions. 2) Be...
[Rockbox.git] / apps / plugins / mpegplayer / pcm_output.c
blobac89308af102f70b80d3a0c9199605faa4e36aef
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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "mpegplayer.h"
24 /* Pointers */
26 /* Start of buffer */
27 static struct pcm_frame_header * ALIGNED_ATTR(4) pcm_buffer;
28 /* End of buffer (not guard) */
29 static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_end;
30 /* Read pointer */
31 static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR;
32 /* Write pointer */
33 static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
35 /* Bytes */
36 static uint64_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */
37 static uint64_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */
38 static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */
40 /* Clock */
41 static uint32_t clock_base IBSS_ATTR; /* Our base clock */
42 static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */
43 static int32_t clock_adjust IBSS_ATTR; /* Clock drift adjustment */
45 /* Small silence clip. ~5.80ms @ 44.1kHz */
46 static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
48 /* Advance a PCM buffer pointer by size bytes circularly */
49 static inline void pcm_advance_buffer(struct pcm_frame_header **p,
50 size_t size)
52 *p = SKIPBYTES(*p, size);
53 if (*p >= pcmbuf_end)
54 *p = pcm_buffer;
57 /* Inline internally but not externally */
58 inline ssize_t pcm_output_used(void)
60 return (ssize_t)(pcmbuf_written - pcmbuf_read);
63 inline ssize_t pcm_output_free(void)
65 return (ssize_t)(PCMOUT_BUFSIZE - pcmbuf_written + pcmbuf_read);
68 /* Audio DMA handler */
69 static void get_more(unsigned char **start, size_t *size)
71 ssize_t sz = pcm_output_used();
73 if (sz > pcmbuf_threshold)
75 pcmbuf_threshold = PCMOUT_LOW_WM;
77 while (1)
79 uint32_t time = pcmbuf_head->time;
80 int32_t offset = time - (clock_base + clock_adjust);
82 sz = pcmbuf_head->size;
84 if (sz < (ssize_t)(sizeof(pcmbuf_head) + 4) ||
85 (sz & 3) != 0)
87 /* Just show a warning about this - will never happen
88 * without a bug in the audio thread code or a clobbered
89 * buffer */
90 DEBUGF("get_more: invalid size (%ld)\n", sz);
93 if (offset < -100*CLOCK_RATE/1000)
95 /* Frame more than 100ms late - drop it */
96 pcm_advance_buffer(&pcmbuf_head, sz);
97 pcmbuf_read += sz;
98 if (pcmbuf_read < pcmbuf_written)
99 continue;
101 else if (offset < 100*CLOCK_RATE/1000)
103 /* Frame less than 100ms early - play it */
104 *start = (unsigned char *)pcmbuf_head->data;
106 pcm_advance_buffer(&pcmbuf_head, sz);
107 pcmbuf_read += sz;
109 sz -= sizeof (struct pcm_frame_header);
111 *size = sz;
113 /* Audio is time master - keep clock synchronized */
114 clock_adjust = time - clock_base;
116 /* Update base clock */
117 clock_base += sz >> 2;
118 return;
120 /* Frame will be dropped - play silence clip */
121 break;
124 else
126 /* Ran out so revert to default watermark */
127 pcmbuf_threshold = PCMOUT_PLAY_WM;
130 /* Keep clock going at all times */
131 *start = (unsigned char *)silence;
132 *size = sizeof (silence);
134 clock_base += sizeof (silence) / 4;
136 if (pcmbuf_read > pcmbuf_written)
137 pcmbuf_read = pcmbuf_written;
140 struct pcm_frame_header * pcm_output_get_buffer(void)
142 return pcmbuf_tail;
145 void pcm_output_add_data(void)
147 size_t size = pcmbuf_tail->size;
148 pcm_advance_buffer(&pcmbuf_tail, size);
149 pcmbuf_written += size;
152 /* Flushes the buffer - clock keeps counting */
153 void pcm_output_flush(void)
155 bool playing, paused;
157 rb->pcm_play_lock();
159 playing = rb->pcm_is_playing();
160 paused = rb->pcm_is_paused();
162 /* Stop PCM to clear current buffer */
163 if (playing)
164 rb->pcm_play_stop();
166 pcmbuf_threshold = PCMOUT_PLAY_WM;
167 pcmbuf_read = pcmbuf_written = 0;
168 pcmbuf_head = pcmbuf_tail = pcm_buffer;
170 /* Restart if playing state was current */
171 if (playing && !paused)
172 rb->pcm_play_data(get_more, NULL, 0);
174 rb->pcm_play_unlock();
177 /* Seek the reference clock to the specified time - next audio data ready to
178 go to DMA should be on the buffer with the same time index or else the PCM
179 buffer should be empty */
180 void pcm_output_set_clock(uint32_t time)
182 rb->pcm_play_lock();
184 clock_base = time;
185 clock_start = time;
186 clock_adjust = 0;
188 rb->pcm_play_unlock();
191 uint32_t pcm_output_get_clock(void)
193 return clock_base + clock_adjust
194 - (rb->pcm_get_bytes_waiting() >> 2);
197 uint32_t pcm_output_get_ticks(uint32_t *start)
199 if (start)
200 *start = clock_start;
202 return clock_base - (rb->pcm_get_bytes_waiting() >> 2);
205 /* Pauses/Starts pcm playback - and the clock */
206 void pcm_output_play_pause(bool play)
208 rb->pcm_play_lock();
210 if (rb->pcm_is_playing())
212 rb->pcm_play_pause(play);
214 else if (play)
216 rb->pcm_play_data(get_more, NULL, 0);
219 rb->pcm_play_unlock();
222 /* Stops all playback and resets the clock */
223 void pcm_output_stop(void)
225 rb->pcm_play_lock();
227 if (rb->pcm_is_playing())
228 rb->pcm_play_stop();
230 pcm_output_flush();
231 pcm_output_set_clock(0);
233 rb->pcm_play_unlock();
236 /* Drains any data if the start threshold hasn't been reached */
237 void pcm_output_drain(void)
239 rb->pcm_play_lock();
240 pcmbuf_threshold = PCMOUT_LOW_WM;
241 rb->pcm_play_unlock();
244 bool pcm_output_init(void)
246 pcm_buffer = mpeg_malloc(PCMOUT_ALLOC_SIZE, MPEG_ALLOC_PCMOUT);
247 if (pcm_buffer == NULL)
248 return false;
250 pcmbuf_threshold = PCMOUT_PLAY_WM;
251 pcmbuf_head = pcm_buffer;
252 pcmbuf_tail = pcm_buffer;
253 pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE);
254 pcmbuf_read = 0;
255 pcmbuf_written = 0;
257 rb->pcm_set_frequency(SAMPR_44);
259 #if INPUT_SRC_CAPS != 0
260 /* Select playback */
261 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
262 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
263 #endif
265 #if SILENCE_TEST_TONE
266 /* Make the silence clip a square wave */
267 const int16_t silence_amp = 32767 / 16;
268 unsigned i;
270 for (i = 0; i < ARRAYLEN(silence); i += 2)
272 if (i < ARRAYLEN(silence)/2)
274 silence[i] = silence_amp;
275 silence[i+1] = silence_amp;
277 else
279 silence[i] = -silence_amp;
280 silence[i+1] = -silence_amp;
283 #endif
285 return true;
288 void pcm_output_exit(void)
290 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);