summaryrefslogtreecommitdiff
path: root/firmware/pcm_playback.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/pcm_playback.c')
-rw-r--r--firmware/pcm_playback.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index e1afea949d..f5fc5e7391 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -207,3 +207,122 @@ void pcm_init(void)
207 207
208 pcm_set_frequency(44100); 208 pcm_set_frequency(44100);
209} 209}
210
211
212#define NUM_PCM_BUFFERS 16 /* Must be a power of 2 */
213#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
214
215struct pcmbufdesc
216{
217 void *addr;
218 int size;
219 void (*callback)(void); /* Call this when the buffer has been played */
220} pcmbuffers[NUM_PCM_BUFFERS];
221
222int pcmbuf_read_index;
223int pcmbuf_write_index;
224int pcmbuf_unplayed_bytes;
225int pcmbuf_watermark;
226void (*pcmbuf_watermark_callback)(int bytes_left);
227
228int pcm_play_num_used_buffers(void)
229{
230 return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
231}
232
233void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left))
234{
235 pcmbuf_watermark = numbytes;
236 pcmbuf_watermark_callback = callback;
237}
238
239bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void))
240{
241 /* We don't use the last buffer, since we can't see the difference
242 between the full and empty condition */
243 if(pcm_play_num_used_buffers() < (NUM_PCM_BUFFERS - 1))
244 {
245 pcmbuffers[pcmbuf_write_index].addr = addr;
246 pcmbuffers[pcmbuf_write_index].size = size;
247 pcmbuffers[pcmbuf_write_index].callback = callback;
248 pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
249 pcmbuf_unplayed_bytes += size;
250 return true;
251 }
252 else
253 return false;
254}
255
256void pcm_play_init(void)
257{
258 pcmbuf_read_index = 0;
259 pcmbuf_write_index = 0;
260 pcmbuf_unplayed_bytes = 0;
261 pcm_play_set_watermark(0x10000, NULL);
262}
263
264static int last_chunksize = 0;
265
266static void pcm_play_callback(unsigned char** start, long* size)
267{
268 struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
269 int sz;
270
271 pcmbuf_unplayed_bytes -= last_chunksize;
272
273 if(desc->size == 0)
274 {
275 /* The buffer is finished, call the callback function */
276 if(desc->callback)
277 desc->callback();
278
279 /* Advance to the next buffer */
280 pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
281 desc = &pcmbuffers[pcmbuf_read_index];
282 }
283
284 if(pcm_play_num_used_buffers())
285 {
286 /* Play max 64K at a time */
287 sz = MIN(desc->size, 32768);
288 *start = desc->addr;
289 *size = sz;
290
291 /* Update the buffer descriptor */
292 desc->size -= sz;
293 desc->addr += sz;
294
295 last_chunksize = sz;
296 }
297 else
298 {
299 /* No more buffers */
300 *size = 0;
301 }
302#if 0
303 if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
304 {
305 if(pcmbuf_watermark_callback)
306 {
307 pcmbuf_watermark_callback(pcmbuf_unplayed_bytes);
308 }
309 }
310#endif
311}
312
313void pcm_play_start(void)
314{
315 struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
316 int size;
317 char *start;
318
319 if(!pcm_is_playing())
320 {
321 size = MIN(desc->size, 32768);
322 start = desc->addr;
323 pcm_play_data(start, size, pcm_play_callback);
324 last_chunksize = size;
325 desc->size -= size;
326 desc->addr += size;
327 }
328}