diff options
Diffstat (limited to 'firmware/pcm_playback.c')
-rw-r--r-- | firmware/pcm_playback.c | 119 |
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 | |||
215 | struct 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 | |||
222 | int pcmbuf_read_index; | ||
223 | int pcmbuf_write_index; | ||
224 | int pcmbuf_unplayed_bytes; | ||
225 | int pcmbuf_watermark; | ||
226 | void (*pcmbuf_watermark_callback)(int bytes_left); | ||
227 | |||
228 | int pcm_play_num_used_buffers(void) | ||
229 | { | ||
230 | return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK; | ||
231 | } | ||
232 | |||
233 | void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)) | ||
234 | { | ||
235 | pcmbuf_watermark = numbytes; | ||
236 | pcmbuf_watermark_callback = callback; | ||
237 | } | ||
238 | |||
239 | bool 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 | |||
256 | void 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 | |||
264 | static int last_chunksize = 0; | ||
265 | |||
266 | static 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 | |||
313 | void 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 | } | ||