diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2006-12-06 08:34:55 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2006-12-06 08:34:55 +0000 |
commit | 51189b4cb41fb70f4800286f99573f590941ddea (patch) | |
tree | 09ca4c9ee4f2ade25e811e5a1f9018a7814c78b3 /firmware | |
parent | 1ff7652adf206eec517ae3236d02a5be9324942a (diff) | |
download | rockbox-51189b4cb41fb70f4800286f99573f590941ddea.tar.gz rockbox-51189b4cb41fb70f4800286f99573f590941ddea.zip |
Small change to PCM recording API for low latency effects. Latency can be as low as 14 samples from input to output including the FIFOs (ColdFire) but 16 is more reasonable an expectation if only tranferring one sample per interrupt (\!). May convert PCM playback to use the same method as it can still be used in the old manner with some slight mods but has the potential to enable new features since it is more flexible.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11668 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/pcm_playback.h | 1 | ||||
-rw-r--r-- | firmware/export/pcm_record.h | 17 | ||||
-rw-r--r-- | firmware/pcm_record.c | 42 | ||||
-rw-r--r-- | firmware/target/coldfire/pcm-coldfire.c | 68 |
4 files changed, 57 insertions, 71 deletions
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index e7c00edeed..80a1f557fe 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h | |||
@@ -24,6 +24,7 @@ | |||
24 | /* Typedef for registered callback (play and record) */ | 24 | /* Typedef for registered callback (play and record) */ |
25 | typedef void (*pcm_more_callback_type)(unsigned char **start, | 25 | typedef void (*pcm_more_callback_type)(unsigned char **start, |
26 | size_t *size); | 26 | size_t *size); |
27 | typedef int (*pcm_more_callback_type2)(int status); | ||
27 | 28 | ||
28 | void pcm_init(void); | 29 | void pcm_init(void); |
29 | 30 | ||
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h index 30d2dc7e6f..f6dddb3424 100644 --- a/firmware/export/pcm_record.h +++ b/firmware/export/pcm_record.h | |||
@@ -20,9 +20,9 @@ | |||
20 | #ifndef PCM_RECORD_H | 20 | #ifndef PCM_RECORD_H |
21 | #define PCM_RECORD_H | 21 | #define PCM_RECORD_H |
22 | 22 | ||
23 | #define DMA_REC_ERROR_DMA ((size_t)-1) | 23 | #define DMA_REC_ERROR_DMA (-1) |
24 | #ifdef HAVE_SPDIF_IN | 24 | #ifdef HAVE_SPDIF_IN |
25 | #define DMA_REC_ERROR_SPDIF ((size_t)-2) | 25 | #define DMA_REC_ERROR_SPDIF (-2) |
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | /** | 28 | /** |
@@ -36,12 +36,15 @@ void pcm_init_recording(void); | |||
36 | void pcm_close_recording(void); | 36 | void pcm_close_recording(void); |
37 | 37 | ||
38 | /* Start recording "raw" PCM data */ | 38 | /* Start recording "raw" PCM data */ |
39 | void pcm_record_data(pcm_more_callback_type more_ready, | 39 | void pcm_record_data(pcm_more_callback_type2 more_ready, |
40 | unsigned char *start, size_t size); | 40 | void *start, size_t size); |
41 | 41 | ||
42 | /* Stop tranferring data into supplied buffer */ | 42 | /* Stop tranferring data into supplied buffer */ |
43 | void pcm_stop_recording(void); | 43 | void pcm_stop_recording(void); |
44 | 44 | ||
45 | /* Continue transferring data in - call during interrupt handler */ | ||
46 | void pcm_record_more(void *start, size_t size); | ||
47 | |||
45 | void pcm_calculate_rec_peaks(int *left, int *right); | 48 | void pcm_calculate_rec_peaks(int *left, int *right); |
46 | 49 | ||
47 | /** General functions for high level codec recording **/ | 50 | /** General functions for high level codec recording **/ |
@@ -64,12 +67,12 @@ int pcm_get_num_unprocessed(void); | |||
64 | /** The following are for internal use between pcm_record.c and target- | 67 | /** The following are for internal use between pcm_record.c and target- |
65 | specific portion **/ | 68 | specific portion **/ |
66 | /* the registered callback function for when more data is available */ | 69 | /* the registered callback function for when more data is available */ |
67 | extern volatile pcm_more_callback_type pcm_callback_more_ready; | 70 | extern volatile pcm_more_callback_type2 pcm_callback_more_ready; |
68 | /* DMA transfer in is currently active */ | 71 | /* DMA transfer in is currently active */ |
69 | extern volatile bool pcm_recording; | 72 | extern volatile bool pcm_recording; |
70 | 73 | ||
71 | /* APIs implemented in the target-specific portion */ | 74 | /* APIs implemented in the target-specific portion */ |
72 | extern void pcm_rec_dma_start(const void *addr, size_t size); | 75 | extern void pcm_rec_dma_start(void *addr, size_t size); |
73 | extern void pcm_rec_dma_stop(void); | 76 | extern void pcm_rec_dma_stop(void); |
74 | 77 | ||
75 | #endif /* PCM_RECORD_H */ | 78 | #endif /* PCM_RECORD_H */ |
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index 379adc3a9c..a72641baa6 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c | |||
@@ -55,13 +55,9 @@ | |||
55 | be shared semi-privately **/ | 55 | be shared semi-privately **/ |
56 | 56 | ||
57 | /* the registered callback function for when more data is available */ | 57 | /* the registered callback function for when more data is available */ |
58 | volatile pcm_more_callback_type pcm_callback_more_ready = NULL; | 58 | volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL; |
59 | /* DMA transfer in is currently active */ | 59 | /* DMA transfer in is currently active */ |
60 | volatile bool pcm_recording = false; | 60 | volatile bool pcm_recording = false; |
61 | |||
62 | /* APIs implemented in the target-specific portion */ | ||
63 | void pcm_rec_dma_start(const void *addr, size_t size); | ||
64 | void pcm_rec_dma_stop(void); | ||
65 | 61 | ||
66 | /** General recording state **/ | 62 | /** General recording state **/ |
67 | static bool is_recording; /* We are recording */ | 63 | static bool is_recording; /* We are recording */ |
@@ -225,16 +221,16 @@ static void pcm_thread_wait_for_stop(void) | |||
225 | /*******************************************************************/ | 221 | /*******************************************************************/ |
226 | 222 | ||
227 | /* Callback for when more data is ready */ | 223 | /* Callback for when more data is ready */ |
228 | static void pcm_rec_have_more(unsigned char **data, size_t *size) | 224 | static int pcm_rec_have_more(int status) |
229 | { | 225 | { |
230 | if (*size != 0) | 226 | if (status < 0) |
231 | { | 227 | { |
232 | /* some error condition */ | 228 | /* some error condition */ |
233 | if (*size == DMA_REC_ERROR_DMA) | 229 | if (status == DMA_REC_ERROR_DMA) |
234 | { | 230 | { |
235 | /* Flush recorded data to disk and stop recording */ | 231 | /* Flush recorded data to disk and stop recording */ |
236 | queue_post(&pcmrec_queue, PCMREC_STOP, NULL); | 232 | queue_post(&pcmrec_queue, PCMREC_STOP, NULL); |
237 | return; | 233 | return -1; |
238 | } | 234 | } |
239 | /* else try again next transmission */ | 235 | /* else try again next transmission */ |
240 | } | 236 | } |
@@ -243,9 +239,9 @@ static void pcm_rec_have_more(unsigned char **data, size_t *size) | |||
243 | /* advance write position */ | 239 | /* advance write position */ |
244 | dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; | 240 | dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; |
245 | } | 241 | } |
246 | 242 | ||
247 | *data = (unsigned char *)GET_PCM_CHUNK(dma_wr_pos); | 243 | pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); |
248 | *size = PCM_CHUNK_SIZE; | 244 | return 0; |
249 | } /* pcm_rec_have_more */ | 245 | } /* pcm_rec_have_more */ |
250 | 246 | ||
251 | /** pcm_rec_* group **/ | 247 | /** pcm_rec_* group **/ |
@@ -423,9 +419,9 @@ void audio_set_recording_options(struct audio_recording_options *options) | |||
423 | if (audio_load_encoder(enc_config.afmt)) | 419 | if (audio_load_encoder(enc_config.afmt)) |
424 | { | 420 | { |
425 | /* start DMA transfer */ | 421 | /* start DMA transfer */ |
426 | pcm_record_data(pcm_rec_have_more, NULL, 0); | ||
427 | /* do unlock after starting to prevent preincrement of dma_wr_pos */ | ||
428 | dma_lock = pre_record_ticks == 0; | 422 | dma_lock = pre_record_ticks == 0; |
423 | pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos), | ||
424 | PCM_CHUNK_SIZE); | ||
429 | } | 425 | } |
430 | else | 426 | else |
431 | { | 427 | { |
@@ -1621,20 +1617,14 @@ size_t enc_unget_pcm_data(size_t size) | |||
1621 | * Functions that do not require targeted implementation but only a targeted | 1617 | * Functions that do not require targeted implementation but only a targeted |
1622 | * interface | 1618 | * interface |
1623 | */ | 1619 | */ |
1624 | void pcm_record_data(pcm_more_callback_type more_ready, | 1620 | void pcm_record_data(pcm_more_callback_type2 more_ready, |
1625 | unsigned char *start, size_t size) | 1621 | void *start, size_t size) |
1626 | { | 1622 | { |
1627 | pcm_callback_more_ready = more_ready; | ||
1628 | |||
1629 | if (!(start && size)) | 1623 | if (!(start && size)) |
1630 | { | 1624 | return; |
1631 | size = 0; | ||
1632 | if (more_ready) | ||
1633 | more_ready(&start, &size); | ||
1634 | } | ||
1635 | 1625 | ||
1636 | if (start && size) | 1626 | pcm_callback_more_ready = more_ready; |
1637 | pcm_rec_dma_start(start, size); | 1627 | pcm_rec_dma_start(start, size); |
1638 | } /* pcm_record_data */ | 1628 | } /* pcm_record_data */ |
1639 | 1629 | ||
1640 | void pcm_stop_recording(void) | 1630 | void pcm_stop_recording(void) |
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c index 917800d0b8..4e4e557d9b 100644 --- a/firmware/target/coldfire/pcm-coldfire.c +++ b/firmware/target/coldfire/pcm-coldfire.c | |||
@@ -287,7 +287,7 @@ void DMA0(void) | |||
287 | /**************************************************************************** | 287 | /**************************************************************************** |
288 | ** Recording DMA transfer | 288 | ** Recording DMA transfer |
289 | **/ | 289 | **/ |
290 | void pcm_rec_dma_start(const void *addr, size_t size) | 290 | void pcm_rec_dma_start(void *addr, size_t size) |
291 | { | 291 | { |
292 | logf("pcm_rec_dma_start"); | 292 | logf("pcm_rec_dma_start"); |
293 | 293 | ||
@@ -296,12 +296,6 @@ void pcm_rec_dma_start(const void *addr, size_t size) | |||
296 | 296 | ||
297 | pcm_recording = true; | 297 | pcm_recording = true; |
298 | 298 | ||
299 | DAR1 = (unsigned long)addr; /* Destination address */ | ||
300 | SAR1 = (unsigned long)&PDIR2; /* Source address */ | ||
301 | BCR1 = size; /* Bytes to transfer */ | ||
302 | |||
303 | rec_peak_addr = (unsigned long *)addr; | ||
304 | |||
305 | pcm_apply_settings(false); | 299 | pcm_apply_settings(false); |
306 | 300 | ||
307 | /* Start the DMA transfer.. */ | 301 | /* Start the DMA transfer.. */ |
@@ -309,18 +303,22 @@ void pcm_rec_dma_start(const void *addr, size_t size) | |||
309 | INTERRUPTCLEAR = 0x03c00000; | 303 | INTERRUPTCLEAR = 0x03c00000; |
310 | #endif | 304 | #endif |
311 | 305 | ||
312 | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | | 306 | SAR1 = (unsigned long)&PDIR2; /* Source address */ |
313 | DMA_DSIZE(3) | DMA_START; | 307 | DCR1 = DMA_INT | DMA_CS | DMA_AA | DMA_DINC | DMA_DSIZE(3); |
308 | |||
309 | pcm_record_more(addr, size); | ||
310 | |||
311 | DCR1 |= DMA_START; | ||
314 | } /* pcm_dma_start */ | 312 | } /* pcm_dma_start */ |
315 | 313 | ||
316 | void pcm_rec_dma_stop(void) | 314 | void pcm_rec_dma_stop(void) |
317 | { | 315 | { |
318 | logf("pcm_rec_dma_stop"); | 316 | logf("pcm_rec_dma_stop"); |
319 | 317 | ||
320 | pcm_recording = false; | ||
321 | |||
322 | DCR1 = 0; | ||
323 | DSR1 = 1; /* Clear interrupt */ | 318 | DSR1 = 1; /* Clear interrupt */ |
319 | DCR1 = 0; | ||
320 | |||
321 | pcm_recording = false; | ||
324 | } /* pcm_dma_stop */ | 322 | } /* pcm_dma_stop */ |
325 | 323 | ||
326 | void pcm_init_recording(void) | 324 | void pcm_init_recording(void) |
@@ -338,7 +336,7 @@ void pcm_init_recording(void) | |||
338 | 336 | ||
339 | pcm_rec_dma_stop(); | 337 | pcm_rec_dma_stop(); |
340 | 338 | ||
341 | ICR7 = (7 << 2); /* Enable interrupt at level 7, priority 0 */ | 339 | ICR7 = (6 << 2); /* Enable interrupt at level 6, priority 0 */ |
342 | IMR &= ~(1 << 15); /* bit 15 is DMA1 */ | 340 | IMR &= ~(1 << 15); /* bit 15 is DMA1 */ |
343 | } /* pcm_init_recording */ | 341 | } /* pcm_init_recording */ |
344 | 342 | ||
@@ -359,17 +357,15 @@ void DMA1(void) __attribute__ ((interrupt_handler, section(".icode"))); | |||
359 | void DMA1(void) | 357 | void DMA1(void) |
360 | { | 358 | { |
361 | int res = DSR1; | 359 | int res = DSR1; |
362 | pcm_more_callback_type more_ready; | 360 | int status = 0; |
363 | unsigned char *next_start; | 361 | pcm_more_callback_type2 more_ready; |
364 | ssize_t next_size = 0; /* passing <> 0 is indicates | ||
365 | an error condition */ | ||
366 | 362 | ||
367 | DSR1 = 1; /* Clear interrupt */ | 363 | DSR1 = 1; /* Clear interrupt */ |
368 | DCR1 &= ~DMA_EEXT; | 364 | DCR1 &= ~DMA_EEXT; /* Disable peripheral request */ |
369 | 365 | ||
370 | if (res & 0x70) | 366 | if (res & 0x70) |
371 | { | 367 | { |
372 | next_size = DMA_REC_ERROR_DMA; | 368 | status = DMA_REC_ERROR_DMA; |
373 | logf("DMA1 err: 0x%x", res); | 369 | logf("DMA1 err: 0x%x", res); |
374 | } | 370 | } |
375 | #ifdef HAVE_SPDIF_IN | 371 | #ifdef HAVE_SPDIF_IN |
@@ -377,37 +373,33 @@ void DMA1(void) | |||
377 | (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */ | 373 | (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */ |
378 | { | 374 | { |
379 | INTERRUPTCLEAR = 0x03c00000; | 375 | INTERRUPTCLEAR = 0x03c00000; |
380 | next_size = DMA_REC_ERROR_SPDIF; | 376 | status = DMA_REC_ERROR_SPDIF; |
381 | logf("spdif err"); | 377 | logf("spdif err"); |
382 | } | 378 | } |
383 | #endif | 379 | #endif |
384 | 380 | ||
385 | more_ready = pcm_callback_more_ready; | 381 | more_ready = pcm_callback_more_ready; |
386 | 382 | ||
387 | if (more_ready) | 383 | if (more_ready != NULL && more_ready(status) >= 0) |
388 | more_ready(&next_start, &next_size); | ||
389 | |||
390 | if (next_size > 0) | ||
391 | { | ||
392 | /* Start peaking at dest */ | ||
393 | rec_peak_addr = (unsigned long *)next_start; | ||
394 | DAR1 = (unsigned long)next_start; /* Destination address */ | ||
395 | BCR1 = (unsigned long)next_size; /* Bytes to transfer */ | ||
396 | DCR1 |= DMA_EEXT; | ||
397 | return; | 384 | return; |
398 | } | 385 | |
399 | else | ||
400 | { | ||
401 | #if 0 | 386 | #if 0 |
402 | /* int. logfs can trash the display */ | 387 | /* int. logfs can trash the display */ |
403 | logf("DMA1 No Data:0x%04x", res); | 388 | logf("DMA1 done:%04x %d", res, status); |
404 | #endif | 389 | #endif |
405 | } | ||
406 | |||
407 | /* Finished recording */ | 390 | /* Finished recording */ |
408 | pcm_rec_dma_stop(); | 391 | pcm_rec_dma_stop(); |
409 | } /* DMA1 */ | 392 | } /* DMA1 */ |
410 | 393 | ||
394 | /* Continue transferring data in */ | ||
395 | void pcm_record_more(void *start, size_t size) | ||
396 | { | ||
397 | rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ | ||
398 | DAR1 = (unsigned long)start; /* Destination address */ | ||
399 | BCR1 = (unsigned long)size; /* Bytes to transfer */ | ||
400 | DCR1 |= DMA_EEXT; | ||
401 | } | ||
402 | |||
411 | void pcm_mute(bool mute) | 403 | void pcm_mute(bool mute) |
412 | { | 404 | { |
413 | ac_mute(mute); | 405 | ac_mute(mute); |