Coldfire: Put DMA1 priority back at level 7 as that seems to be the cause of the...
[Rockbox.git] / firmware / pcm_record.c
blob34b104d29a5ef4db2653a0050bc3338b000705cb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "system.h"
20 #include "kernel.h"
21 #include "logf.h"
22 #include "panic.h"
23 #include "thread.h"
24 #include <string.h>
25 #include "ata.h"
26 #include "usb.h"
27 #if defined(HAVE_UDA1380)
28 #include "uda1380.h"
29 #include "general.h"
30 #elif defined(HAVE_TLV320)
31 #include "tlv320.h"
32 #endif
33 #include "buffer.h"
34 #include "audio.h"
35 #include "sound.h"
36 #include "id3.h"
37 #ifdef HAVE_SPDIF_IN
38 #include "spdif.h"
39 #endif
41 /***************************************************************************/
43 /**
44 * APIs implemented in the target tree portion:
45 * Public -
46 * pcm_init_recording
47 * pcm_close_recording
48 * pcm_rec_mux
49 * Semi-private -
50 * pcm_rec_dma_start
51 * pcm_rec_dma_stop
54 /** These items may be implemented target specifically or need to
55 be shared semi-privately **/
57 /* the registered callback function for when more data is available */
58 volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
59 /* DMA transfer in is currently active */
60 volatile bool pcm_recording = false;
62 /** General recording state **/
63 static bool is_recording; /* We are recording */
64 static bool is_paused; /* We have paused */
65 static bool is_stopping; /* We are currently stopping */
66 static unsigned long errors; /* An error has occured */
67 static unsigned long warnings; /* Warning */
69 /** Stats on encoded data for current file **/
70 static size_t num_rec_bytes; /* Num bytes recorded */
71 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
73 /** Stats on encoded data for all files from start to stop **/
74 #if 0
75 static unsigned long long accum_rec_bytes; /* total size written to chunks */
76 static unsigned long long accum_pcm_samples; /* total pcm count processed */
77 #endif
79 /* Keeps data about current file and is sent as event data for codec */
80 static struct enc_file_event_data rec_fdata IDATA_ATTR =
82 .chunk = NULL,
83 .new_enc_size = 0,
84 .new_num_pcm = 0,
85 .rec_file = -1,
86 .num_pcm_samples = 0
89 /** These apply to current settings **/
90 static int rec_source; /* current rec_source setting */
91 static int rec_frequency; /* current frequency setting */
92 static unsigned long sample_rate; /* Sample rate in HZ */
93 static int num_channels; /* Current number of channels */
94 static struct encoder_config enc_config; /* Current encoder configuration */
95 static unsigned long pre_record_ticks; /* pre-record time in ticks */
97 /****************************************************************************
98 use 2 circular buffers:
99 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
100 enc_buffer=encoded audio buffer: storage for encoder output data
102 Flow:
103 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
104 2. if enough pcm data are available the encoder codec does encoding of pcm
105 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
106 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
108 Functions calls (basic encoder steps):
109 1.main: audio_load_encoder(); start the encoder
110 2.encoder: enc_get_inputs(); get encoder recording settings
111 3.encoder: enc_set_parameters(); set the encoder parameters
112 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
113 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
114 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
115 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
116 8.encoder: <process enc chunk> compress and store data to enc chunk
117 9.encoder: enc_finish_chunk(); inform main about chunk processed and
118 is available to be written to a file.
119 Encoder can place any number of chunks
120 of PCM data in a single output chunk
121 but must stay within its output chunk
122 size
123 A.encoder: repeat 4. to 9.
124 B.pcmrec: enc_events_callback(); called for certain events
126 (*) Optional step
127 ****************************************************************************/
129 /** buffer parameters where incoming PCM data is placed **/
130 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
131 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
132 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
134 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
135 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
137 #ifdef PCMREC_PARANOID
138 static void paranoid_set_code(unsigned long code, int line)
140 logf("%08X at %d", code, line);
141 if ((long)code < 0)
142 errors |= code;
143 else
144 warnings |= code;
147 #define PARANOID_ENC_INDEX_CHECK(index) \
148 { if (index != index##_last) \
149 paranoid_set_code((&index == &enc_rd_index) ? \
150 PCMREC_E_ENC_RD_INDEX_TRASHED : PCMREC_E_ENC_WR_INDEX_TRASHED, \
151 __LINE__); }
152 #define PARANOID_PCM_POS_CHECK(pos) \
153 { if (pos != pos##_last) \
154 paranoid_set_code((&pos == &pcm_rd_pos) ? \
155 PCMREC_W_PCM_RD_POS_TRASHED : PCMREC_W_DMA_WR_POS_TRASHED, \
156 __LINE__); }
157 #define PARANOID_SET_LAST(var) \
158 ; var##_last = var
159 #define PARANOID_CHUNK_CHECK(chunk) \
160 paranoid_chunk_check(chunk)
161 #else
162 #define PARANOID_ENC_INDEX_CHECK(index)
163 #define PARANOID_PCM_POS_CHECK(pos)
164 #define PARANOID_SET_LAST(var)
165 #define PARANOID_CHUNK_CHECK(chunk)
166 #endif
168 #define INC_ENC_INDEX(index) \
169 PARANOID_ENC_INDEX_CHECK(index) \
170 { if (++index >= enc_num_chunks) index = 0; } \
171 PARANOID_SET_LAST(index)
172 #define DEC_ENC_INDEX(index) \
173 PARANOID_ENC_INDEX_CHECK(index) \
174 { if (--index < 0) index = enc_num_chunks - 1; } \
175 PARANOID_SET_LAST(index)
176 #define SET_ENC_INDEX(index, value) \
177 PARANOID_ENC_INDEX_CHECK(index) \
178 index = value \
179 PARANOID_SET_LAST(index)
180 #define SET_PCM_POS(pos, value) \
181 PARANOID_PCM_POS_CHECK(pos) \
182 pos = value \
183 PARANOID_SET_LAST(pos)
185 static size_t rec_buffer_size; /* size of available buffer */
186 static unsigned char *pcm_buffer; /* circular recording buffer */
187 static unsigned char *enc_buffer; /* circular encoding buffer */
188 static volatile int dma_wr_pos; /* current DMA write pos */
189 static int pcm_rd_pos; /* current PCM read pos */
190 static volatile bool dma_lock; /* lock DMA write position */
191 static int enc_wr_index; /* encoder chunk write index */
192 static int enc_rd_index; /* encoder chunk read index */
193 static int enc_num_chunks; /* number of chunks in ringbuffer */
194 static size_t enc_chunk_size; /* maximum encoder chunk size */
195 static unsigned long enc_sample_rate; /* sample rate used by encoder */
196 static bool wav_queue_empty; /* all wav chunks processed? */
198 /** file flushing **/
199 static int write_threshold; /* max chunk limit for data flush */
200 static int panic_threshold; /* boost thread prio when here */
201 static int spinup_time = -1;/* last ata_spinup_time */
203 /** encoder events **/
204 static void (*enc_events_callback)(enum enc_events event, void *data);
206 /** Path queue for files to write **/
207 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
208 static unsigned char *fn_queue; /* pointer to first filename */
209 static ssize_t fnq_size; /* capacity of queue in bytes */
210 static int fnq_rd_pos; /* current read position */
211 static int fnq_wr_pos; /* current write position */
213 /** extra debugging info positioned away from other vars **/
214 #ifdef PCMREC_PARANOID
215 static unsigned long *wrap_id_p; /* magic at end of encoding buffer */
216 static volatile int dma_wr_pos_last; /* previous dma write position */
217 static int pcm_rd_pos_last; /* previous pcm read position */
218 static int enc_rd_index_last; /* previsou encoder read position */
219 static int enc_wr_index_last; /* previsou encoder read position */
220 #endif
223 /***************************************************************************/
225 static struct event_queue pcmrec_queue;
226 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
227 static const char pcmrec_thread_name[] = "pcmrec";
229 static void pcmrec_thread(void);
231 /* Event values which are also single-bit flags */
232 #define PCMREC_INIT 0x00000001 /* enable recording */
233 #define PCMREC_CLOSE 0x00000002 /* close recording */
234 #define PCMREC_OPTIONS 0x00000004 /* set recording options */
235 #define PCMREC_START 0x00000008 /* start recording */
236 #define PCMREC_STOP 0x00000010 /* stop the current recording */
237 #define PCMREC_PAUSE 0x00000020 /* pause the current recording */
238 #define PCMREC_RESUME 0x00000040 /* resume the current recording */
239 #define PCMREC_NEW_FILE 0x00000080 /* start new file */
240 #define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */
241 #define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */
243 /* mask for signaling events */
244 static volatile long pcm_thread_event_mask = PCMREC_CLOSE;
246 static void pcm_thread_sync_post(long event, void *data)
248 pcm_thread_event_mask &= ~event;
249 queue_post(&pcmrec_queue, event, data);
250 while(!(event & pcm_thread_event_mask))
251 yield();
252 } /* pcm_thread_sync_post */
254 static inline void pcm_thread_signal_event(long event)
256 pcm_thread_event_mask |= event;
257 } /* pcm_thread_signal_event */
259 static inline void pcm_thread_unsignal_event(long event)
261 pcm_thread_event_mask &= ~event;
262 } /* pcm_thread_unsignal_event */
264 static inline bool pcm_thread_event_state(long signaled, long unsignaled)
266 return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled;
267 } /* pcm_thread_event_state */
269 /*******************************************************************/
270 /* Functions that are not executing in the pcmrec_thread first */
271 /*******************************************************************/
273 /* Callback for when more data is ready - called in interrupt context */
274 static int pcm_rec_have_more(int status)
276 if (status < 0)
278 /* some error condition */
279 if (status == DMA_REC_ERROR_DMA)
281 /* Flush recorded data to disk and stop recording */
282 queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
283 return -1;
285 /* else try again next transmission */
287 else if (!dma_lock)
289 /* advance write position */
290 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
292 /* set pcm ovf if read position is inside current write chunk */
293 if ((unsigned)(pcm_rd_pos - next_pos) < PCM_CHUNK_SIZE)
294 warnings |= PCMREC_W_PCM_BUFFER_OVF;
296 #ifdef PCMREC_PARANOID
297 /* write position must always be on PCM_CHUNK_SIZE boundary -
298 anything else is corruption */
299 if (next_pos & (PCM_CHUNK_SIZE-1))
301 logf("dma_wr_pos unalgn: %d", next_pos);
302 warnings |= PCMREC_W_DMA_WR_POS_ALIGN;
303 next_pos &= ~PCM_CHUNK_SIZE; /* re-align */
305 #endif
306 SET_PCM_POS(dma_wr_pos, next_pos);
309 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
310 return 0;
311 } /* pcm_rec_have_more */
313 static void reset_hardware(void)
315 /* reset pcm to defaults (playback only) */
316 pcm_set_frequency(HW_SAMPR_DEFAULT);
317 audio_set_output_source(AUDIO_SRC_PLAYBACK);
318 pcm_apply_settings(true);
321 /** pcm_rec_* group **/
324 * Clear all errors and warnings
326 void pcm_rec_error_clear(void)
328 errors = warnings = 0;
329 } /* pcm_rec_error_clear */
332 * Check mode, errors and warnings
334 unsigned long pcm_rec_status(void)
336 unsigned long ret = 0;
338 if (is_recording)
339 ret |= AUDIO_STATUS_RECORD;
340 else if (pre_record_ticks)
341 ret |= AUDIO_STATUS_PRERECORD;
343 if (is_paused)
344 ret |= AUDIO_STATUS_PAUSE;
346 if (errors)
347 ret |= AUDIO_STATUS_ERROR;
349 if (warnings)
350 ret |= AUDIO_STATUS_WARNING;
352 return ret;
353 } /* pcm_rec_status */
356 * Return warnings that have occured since recording started
358 unsigned long pcm_rec_get_warnings(void)
360 return warnings;
363 #if 0
364 int pcm_rec_current_bitrate(void)
366 if (accum_pcm_samples == 0)
367 return 0;
369 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
370 } /* pcm_rec_current_bitrate */
371 #endif
373 #if 0
374 int pcm_rec_encoder_afmt(void)
376 return enc_config.afmt;
377 } /* pcm_rec_encoder_afmt */
378 #endif
380 #if 0
381 int pcm_rec_rec_format(void)
383 return afmt_rec_format[enc_config.afmt];
384 } /* pcm_rec_rec_format */
385 #endif
387 #ifdef HAVE_SPDIF_IN
388 unsigned long pcm_rec_sample_rate(void)
390 /* Which is better ?? */
391 #if 0
392 return enc_sample_rate;
393 #endif
394 return sample_rate;
395 } /* audio_get_sample_rate */
396 #endif
399 * Creates pcmrec_thread
401 void pcm_rec_init(void)
403 queue_init(&pcmrec_queue, true);
404 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
405 pcmrec_thread_name, PRIORITY_RECORDING);
406 } /* pcm_rec_init */
408 /** audio_* group **/
410 /* NOTE: The following posting functions are really only single-thread safe
411 at the moment since a response to a particular message at a particular
412 position in the queue can't be distinguished */
415 * Initializes recording - call before calling any other recording function
417 void audio_init_recording(unsigned int buffer_offset)
419 logf("audio_init_recording");
420 pcm_thread_sync_post(PCMREC_INIT, NULL);
421 logf("audio_init_recording done");
422 (void)buffer_offset;
423 } /* audio_init_recording */
426 * Closes recording - call audio_stop_recording first
428 void audio_close_recording(void)
430 logf("audio_close_recording");
431 pcm_thread_sync_post(PCMREC_CLOSE, NULL);
432 logf("audio_close_recording done");
433 } /* audio_close_recording */
436 * Sets recording parameters
438 void audio_set_recording_options(struct audio_recording_options *options)
440 logf("audio_set_recording_options");
441 pcm_thread_sync_post(PCMREC_OPTIONS, (void *)options);
442 logf("audio_set_recording_options done");
443 } /* audio_set_recording_options */
446 * Start recording if not recording or else split
448 void audio_record(const char *filename)
450 logf("audio_record: %s", filename);
451 pcm_thread_sync_post(PCMREC_START, (void *)filename);
452 logf("audio_record_done");
453 } /* audio_record */
456 * Equivalent to audio_record()
458 void audio_new_file(const char *filename)
460 logf("audio_new_file: %s", filename);
461 pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename);
462 logf("audio_new_file done");
463 } /* audio_new_file */
466 * Stop current recording if recording
468 void audio_stop_recording(void)
470 logf("audio_stop_recording");
471 pcm_thread_sync_post(PCMREC_STOP, NULL);
472 logf("audio_stop_recording done");
473 } /* audio_stop_recording */
476 * Pause current recording
478 void audio_pause_recording(void)
480 logf("audio_pause_recording");
481 pcm_thread_sync_post(PCMREC_PAUSE, NULL);
482 logf("audio_pause_recording done");
483 } /* audio_pause_recording */
486 * Resume current recording if paused
488 void audio_resume_recording(void)
490 logf("audio_resume_recording");
491 pcm_thread_sync_post(PCMREC_RESUME, NULL);
492 logf("audio_resume_recording done");
493 } /* audio_resume_recording */
495 /** Information about current state **/
498 * Return current recorded time in ticks (playback eqivalent time)
500 unsigned long audio_recorded_time(void)
502 if (!is_recording || enc_sample_rate == 0)
503 return 0;
505 /* return actual recorded time a la encoded data even if encoder rate
506 doesn't match the pcm rate */
507 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
508 } /* audio_recorded_time */
511 * Return number of bytes encoded to output
513 unsigned long audio_num_recorded_bytes(void)
515 if (!is_recording)
516 return 0;
518 return num_rec_bytes;
519 } /* audio_num_recorded_bytes */
521 #ifdef HAVE_SPDIF_IN
523 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
524 * our reading on the actual SPDIF sample rate (which might be a bit
525 * inaccurate), we round off to the closest sample rate that is supported by
526 * SPDIF.
528 int audio_get_spdif_sample_rate(void)
530 unsigned long measured_rate = spdif_measure_frequency();
531 /* Find which SPDIF sample rate we're closest to. */
532 return round_value_to_list32(measured_rate, audio_master_sampr_list,
533 SAMPR_NUM_FREQ, false);
534 } /* audio_get_spdif_sample_rate */
535 #endif /* HAVE_SPDIF_IN */
537 /***************************************************************************/
538 /* */
539 /* Functions that execute in the context of pcmrec_thread */
540 /* */
541 /***************************************************************************/
543 /** Filename Queue **/
545 /* returns true if the queue is empty */
546 static inline bool pcmrec_fnq_is_empty(void)
548 return fnq_rd_pos == fnq_wr_pos;
549 } /* pcmrec_fnq_is_empty */
551 /* empties the filename queue */
552 static inline void pcmrec_fnq_set_empty(void)
554 fnq_rd_pos = fnq_wr_pos;
555 } /* pcmrec_fnq_set_empty */
557 /* returns true if the queue is full */
558 static bool pcmrec_fnq_is_full(void)
560 ssize_t size = fnq_wr_pos - fnq_rd_pos;
561 if (size < 0)
562 size += fnq_size;
564 return size >= fnq_size - MAX_PATH;
565 } /* pcmrec_fnq_is_full */
567 /* queue another filename - will overwrite oldest one if full */
568 static bool pcmrec_fnq_add_filename(const char *filename)
570 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
572 if ((fnq_wr_pos += MAX_PATH) >= fnq_size)
573 fnq_wr_pos = 0;
575 if (fnq_rd_pos != fnq_wr_pos)
576 return true;
578 /* queue full */
579 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
580 fnq_rd_pos = 0;
582 return true;
583 } /* pcmrec_fnq_add_filename */
585 /* replace the last filename added */
586 static bool pcmrec_fnq_replace_tail(const char *filename)
588 int pos;
590 if (pcmrec_fnq_is_empty())
591 return false;
593 pos = fnq_wr_pos - MAX_PATH;
594 if (pos < 0)
595 pos = fnq_size - MAX_PATH;
597 strncpy(fn_queue + pos, filename, MAX_PATH);
599 return true;
600 } /* pcmrec_fnq_replace_tail */
602 /* pulls the next filename from the queue */
603 static bool pcmrec_fnq_get_filename(char *filename)
605 if (pcmrec_fnq_is_empty())
606 return false;
608 if (filename)
609 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
611 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
612 fnq_rd_pos = 0;
614 return true;
615 } /* pcmrec_fnq_get_filename */
617 /* close the file number pointed to by fd_p */
618 static void pcmrec_close_file(int *fd_p)
620 if (*fd_p < 0)
621 return; /* preserve error */
623 close(*fd_p);
624 *fd_p = -1;
625 } /* pcmrec_close_file */
627 #ifdef PCMREC_PARANOID
628 static void paranoid_chunk_check(const struct enc_chunk_hdr *chunk)
630 /* check integrity of things that must be ok - data or not */
632 /* check magic in header */
633 if (chunk->id != ENC_CHUNK_MAGIC)
635 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
636 logf("bad chunk: %d", chunk - (struct enc_chunk_hdr *)enc_buffer);
639 /* check magic wrap id */
640 if (*wrap_id_p != ENC_CHUNK_MAGIC)
642 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
643 logf("bad magic at wrap pos");
646 if (chunk->enc_data == NULL) /* has data? */
647 return;
649 /* check that data points to something after header */
650 if (chunk->enc_data < ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk))
652 errors |= PCMREC_E_BAD_CHUNK;
653 logf("chk ptr < hdr end");
656 /* check if data end is within chunk */
657 if (chunk->enc_data + chunk->enc_size >
658 (unsigned char *)chunk + enc_chunk_size)
660 errors |= PCMREC_E_BAD_CHUNK;
661 logf("chk data > chk end");
664 if ((chunk->flags & ~CHUNKF_ALLFLAGS) != 0)
666 errors |= PCMREC_E_BAD_CHUNK;
667 logf("chk bad flags %08X", chunk->flags);
669 } /* paranoid_chunk_check */
670 #endif /* PCMREC_PARANOID */
672 /** Data Flushing **/
675 * called after callback to update sizes if codec changed the amount of data
676 * a chunk represents
678 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
679 unsigned long prev_num_pcm)
681 if (rec_fdata.new_enc_size != prev_enc_size)
683 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
684 num_rec_bytes += size_diff;
685 #if 0
686 accum_rec_bytes += size_diff;
687 #endif
690 if (rec_fdata.new_num_pcm != prev_num_pcm)
692 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
693 num_rec_samples += pcm_diff;
694 #if 0
695 accum_pcm_samples += pcm_diff;
696 #endif
698 } /* pcmrec_update_sizes_inl */
700 /* don't need to inline every instance */
701 static void pcmrec_update_sizes(size_t prev_enc_size,
702 unsigned long prev_num_pcm)
704 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
705 } /* pcmrec_update_sizes */
707 static void pcmrec_start_file(void)
709 size_t enc_size = rec_fdata.new_enc_size;
710 unsigned long num_pcm = rec_fdata.new_num_pcm;
711 int curr_rec_file = rec_fdata.rec_file;
712 char filename[MAX_PATH];
714 /* must always pull the filename that matches with this queue */
715 if (!pcmrec_fnq_get_filename(filename))
717 logf("start file: fnq empty");
718 *filename = '\0';
719 errors |= PCMREC_E_FNQ_DESYNC;
721 else if (errors != 0)
723 logf("start file: error already");
725 else if (curr_rec_file >= 0)
727 /* Any previous file should have been closed */
728 logf("start file: file already open");
729 errors |= PCMREC_E_FNQ_DESYNC;
732 if (errors != 0)
733 rec_fdata.chunk->flags |= CHUNKF_ERROR;
735 /* encoder can set error flag here and should increase
736 enc_new_size and pcm_new_size to reflect additional
737 data written if any */
738 rec_fdata.filename = filename;
739 enc_events_callback(ENC_START_FILE, &rec_fdata);
741 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
743 logf("start file: enc error");
744 errors |= PCMREC_E_ENCODER;
747 if (errors != 0)
749 pcmrec_close_file(&curr_rec_file);
750 /* Write no more to this file */
751 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
753 else
755 pcmrec_update_sizes(enc_size, num_pcm);
758 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
759 } /* pcmrec_start_file */
761 static inline void pcmrec_write_chunk(void)
763 size_t enc_size = rec_fdata.new_enc_size;
764 unsigned long num_pcm = rec_fdata.new_num_pcm;
766 if (errors != 0)
767 rec_fdata.chunk->flags |= CHUNKF_ERROR;
769 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
771 if ((long)rec_fdata.chunk->flags >= 0)
773 pcmrec_update_sizes_inl(enc_size, num_pcm);
775 else if (errors == 0)
777 logf("wr chk enc error %d %d",
778 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
779 errors |= PCMREC_E_ENCODER;
781 } /* pcmrec_write_chunk */
783 static void pcmrec_end_file(void)
785 /* all data in output buffer for current file will have been
786 written and encoder can now do any nescessary steps to
787 finalize the written file */
788 size_t enc_size = rec_fdata.new_enc_size;
789 unsigned long num_pcm = rec_fdata.new_num_pcm;
791 enc_events_callback(ENC_END_FILE, &rec_fdata);
793 if (errors == 0)
795 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
797 logf("end file: enc error");
798 errors |= PCMREC_E_ENCODER;
800 else
802 pcmrec_update_sizes(enc_size, num_pcm);
806 /* Force file close if error */
807 if (errors != 0)
808 pcmrec_close_file(&rec_fdata.rec_file);
810 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
811 } /* pcmrec_end_file */
814 * Process the chunks
816 * This function is called when queue_get_w_tmo times out.
818 * Set flush_num to the number of files to flush to disk.
819 * flush_num = -1 to flush all available chunks to disk.
820 * flush_num = 0 normal write thresholding
821 * flush_num = 1 or greater - all available chunks of current file plus
822 * flush_num file starts if first chunk has been processed.
825 static void pcmrec_flush(unsigned flush_num)
827 static unsigned long last_flush_tick = 0;
828 unsigned long start_tick;
829 int num_ready, num;
830 int prio;
831 int i;
833 num_ready = enc_wr_index - enc_rd_index;
834 if (num_ready < 0)
835 num_ready += enc_num_chunks;
837 num = num_ready;
839 if (flush_num == 0)
841 if (!is_recording)
842 return;
844 if (ata_spinup_time != spinup_time)
846 /* spinup time has changed, calculate new write threshold */
847 logf("new t spinup : %d", ata_spinup_time);
848 unsigned long st = spinup_time = ata_spinup_time;
850 /* write at 5s + st remaining in enc_buffer */
851 if (st < 2*HZ)
852 st = 2*HZ; /* my drive is usually < 250 ticks :) */
853 else if (st > 10*HZ)
854 st = 10*HZ;
856 write_threshold = enc_num_chunks -
857 (int)(((5ull*HZ + st)*4ull*sample_rate + (enc_chunk_size-1)) /
858 (enc_chunk_size*HZ));
860 if (write_threshold < 0)
861 write_threshold = 0;
862 else if (write_threshold > panic_threshold)
863 write_threshold = panic_threshold;
865 logf("new wr thresh: %d", write_threshold);
868 if (num_ready < write_threshold)
869 return;
871 /* if we're getting called too much and this isn't forced,
872 boost stat */
873 if (current_tick - last_flush_tick < HZ/2)
874 num = panic_threshold;
877 start_tick = current_tick;
878 prio = -1;
880 logf("writing: %d (%d)", num_ready, flush_num);
882 cpu_boost(true);
884 for (i = 0; i < num_ready; i++)
886 if (prio == -1 && (num >= panic_threshold ||
887 current_tick - start_tick > 10*HZ))
889 /* losing ground - boost priority until finished */
890 logf("pcmrec: boost priority");
891 prio = thread_set_priority(NULL, thread_get_priority(NULL)-1);
894 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
895 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
896 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
898 PARANOID_CHUNK_CHECK(rec_fdata.chunk);
900 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
902 pcmrec_start_file();
903 if (--flush_num == 0)
904 i = num_ready; /* stop on next loop - must write this
905 chunk if it has data */
908 pcmrec_write_chunk();
910 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
911 pcmrec_end_file();
913 INC_ENC_INDEX(enc_rd_index);
915 if (errors != 0)
916 break;
918 if (prio == -1)
920 num = enc_wr_index - enc_rd_index;
921 if (num < 0)
922 num += enc_num_chunks;
925 /* no yielding, the file apis called in the codecs do that */
926 } /* end for */
928 /* sync file */
929 if (rec_fdata.rec_file >= 0)
930 fsync(rec_fdata.rec_file);
932 cpu_boost(false);
934 if (prio != -1)
936 /* return to original priority */
937 logf("pcmrec: unboost priority");
938 thread_set_priority(NULL, prio);
941 last_flush_tick = current_tick; /* save tick when we left */
942 logf("done");
943 } /* pcmrec_flush */
946 * Marks a new stream in the buffer and gives the encoder a chance for special
947 * handling of transition from one to the next. The encoder may change the
948 * chunk that ends the old stream by requesting more chunks and similiarly for
949 * the new but must always advance the position though the interface. It can
950 * later reject any data it cares to when writing the file but should mark the
951 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
952 * a NULL data pointer without error as well.
954 static void pcmrec_new_stream(const char *filename, /* next file name */
955 unsigned long flags, /* CHUNKF_* flags */
956 int pre_index) /* index for prerecorded data */
958 logf("pcmrec_new_stream");
960 struct enc_buffer_event_data data;
961 bool (*fnq_add_fn)(const char *) = NULL;
962 struct enc_chunk_hdr *start = NULL;
964 int get_chunk_index(struct enc_chunk_hdr *chunk)
966 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
969 struct enc_chunk_hdr * get_prev_chunk(int index)
971 #ifdef PCMREC_PARANOID
972 int index_last = index;
973 #endif
974 DEC_ENC_INDEX(index);
975 return GET_ENC_CHUNK(index);
978 data.pre_chunk = NULL;
979 data.chunk = GET_ENC_CHUNK(enc_wr_index);
981 /* end chunk */
982 if (flags & CHUNKF_END_FILE)
984 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
986 if (data.chunk->flags & CHUNKF_START_FILE)
988 /* cannot start and end on same unprocessed chunk */
989 logf("file end on start");
990 flags &= ~CHUNKF_END_FILE;
992 else if (enc_rd_index == enc_wr_index)
994 /* all data flushed but file not ended - chunk will be left
995 empty */
996 logf("end on dead end");
997 data.chunk->flags = 0;
998 data.chunk->enc_size = 0;
999 data.chunk->num_pcm = 0;
1000 data.chunk->enc_data = NULL;
1001 INC_ENC_INDEX(enc_wr_index);
1002 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1004 else
1006 struct enc_chunk_hdr *last = get_prev_chunk(enc_wr_index);
1008 if (last->flags & CHUNKF_END_FILE)
1010 /* end already processed and marked - can't end twice */
1011 logf("file end again");
1012 flags &= ~CHUNKF_END_FILE;
1017 /* start chunk */
1018 if (flags & CHUNKF_START_FILE)
1020 bool pre = flags & CHUNKF_PRERECORD;
1022 if (pre)
1024 logf("stream prerecord start");
1025 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1026 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1028 else
1030 logf("stream normal start");
1031 start = data.chunk;
1032 start->flags &= CHUNKF_START_FILE;
1035 /* if encoder hasn't yet processed the last start - abort the start
1036 of the previous file queued or else it will be empty and invalid */
1037 if (start->flags & CHUNKF_START_FILE)
1039 logf("replacing fnq tail: %s", filename);
1040 fnq_add_fn = pcmrec_fnq_replace_tail;
1042 else
1044 logf("adding filename: %s", filename);
1045 fnq_add_fn = pcmrec_fnq_add_filename;
1049 data.flags = flags;
1050 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1052 if (flags & CHUNKF_END_FILE)
1054 int i = get_chunk_index(data.chunk);
1055 get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1058 if (start)
1060 if (!(flags & CHUNKF_PRERECORD))
1062 /* get stats on data added to start - sort of a prerecord operation */
1063 int i = get_chunk_index(data.chunk);
1064 #ifdef PCMREC_PARANOID
1065 int i_last = i;
1066 #endif
1067 struct enc_chunk_hdr *chunk = data.chunk;
1069 logf("start data: %d %d", i, enc_wr_index);
1071 num_rec_bytes = 0;
1072 num_rec_samples = 0;
1074 while (i != enc_wr_index)
1076 num_rec_bytes += chunk->enc_size;
1077 num_rec_samples += chunk->num_pcm;
1078 INC_ENC_INDEX(i);
1079 chunk = GET_ENC_CHUNK(i);
1082 start->flags &= ~CHUNKF_START_FILE;
1083 start = data.chunk;
1086 start->flags |= CHUNKF_START_FILE;
1088 /* flush all pending files out if full and adding */
1089 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1091 logf("fnq full");
1092 pcmrec_flush(-1);
1095 fnq_add_fn(filename);
1097 } /* pcmrec_new_stream */
1099 /** event handlers for pcmrec thread */
1101 /* PCMREC_INIT */
1102 static void pcmrec_init(void)
1104 unsigned char *buffer;
1106 rec_fdata.rec_file = -1;
1108 /* warings and errors */
1109 warnings =
1110 errors = 0;
1112 /* pcm FIFO */
1113 dma_lock = true;
1114 SET_PCM_POS(pcm_rd_pos, 0);
1115 SET_PCM_POS(dma_wr_pos, 0);
1117 /* encoder FIFO */
1118 SET_ENC_INDEX(enc_wr_index, 0);
1119 SET_ENC_INDEX(enc_rd_index, 0);
1121 /* filename queue */
1122 fnq_rd_pos = 0;
1123 fnq_wr_pos = 0;
1125 /* stats */
1126 num_rec_bytes = 0;
1127 num_rec_samples = 0;
1128 #if 0
1129 accum_rec_bytes = 0;
1130 accum_pcm_samples = 0;
1131 #endif
1133 pre_record_ticks = 0;
1135 is_recording = false;
1136 is_paused = false;
1137 is_stopping = false;
1139 buffer = audio_get_recording_buffer(&rec_buffer_size);
1140 /* Line align pcm_buffer 2^4=16 bytes */
1141 pcm_buffer = (unsigned char *)ALIGN_UP_P2((unsigned long)buffer, 4);
1142 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1143 PCM_MAX_FEED_SIZE, 2);
1144 /* Adjust available buffer for possible align advancement */
1145 rec_buffer_size -= pcm_buffer - buffer;
1147 pcm_init_recording();
1148 pcm_thread_unsignal_event(PCMREC_CLOSE);
1149 pcm_thread_signal_event(PCMREC_INIT);
1150 } /* pcmrec_init */
1152 /* PCMREC_CLOSE */
1153 static void pcmrec_close(void)
1155 dma_lock = true;
1156 pre_record_ticks = 0; /* Can't be prerecording any more */
1157 warnings = 0;
1158 pcm_close_recording();
1159 reset_hardware();
1160 audio_remove_encoder();
1161 pcm_thread_unsignal_event(PCMREC_INIT);
1162 pcm_thread_signal_event(PCMREC_CLOSE);
1163 } /* pcmrec_close */
1165 /* PCMREC_OPTIONS */
1166 static void pcmrec_set_recording_options(struct audio_recording_options *options)
1168 /* stop DMA transfer */
1169 dma_lock = true;
1170 pcm_stop_recording();
1172 rec_frequency = options->rec_frequency;
1173 rec_source = options->rec_source;
1174 num_channels = options->rec_channels == 1 ? 1 : 2;
1175 pre_record_ticks = options->rec_prerecord_time * HZ;
1176 enc_config = options->enc_config;
1177 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1179 #ifdef HAVE_SPDIF_IN
1180 if (rec_source == AUDIO_SRC_SPDIF)
1182 /* must measure SPDIF sample rate before configuring codecs */
1183 unsigned long sr = spdif_measure_frequency();
1184 /* round to master list for SPDIF rate */
1185 int index = round_value_to_list32(sr, audio_master_sampr_list,
1186 SAMPR_NUM_FREQ, false);
1187 sample_rate = audio_master_sampr_list[index];
1188 /* round to HW playback rates for monitoring */
1189 index = round_value_to_list32(sr, hw_freq_sampr,
1190 HW_NUM_FREQ, false);
1191 pcm_set_frequency(hw_freq_sampr[index]);
1192 /* encoders with a limited number of rates do their own rounding */
1194 else
1195 #endif
1197 /* set sample rate from frequency selection */
1198 sample_rate = rec_freq_sampr[rec_frequency];
1199 pcm_set_frequency(sample_rate);
1202 /* set monitoring */
1203 audio_set_output_source(rec_source);
1205 /* apply pcm settings to hardware */
1206 pcm_apply_settings(true);
1208 if (audio_load_encoder(enc_config.afmt))
1210 /* start DMA transfer */
1211 dma_lock = pre_record_ticks == 0;
1212 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1213 PCM_CHUNK_SIZE);
1215 else
1217 logf("set rec opt: enc load failed");
1218 errors |= PCMREC_E_LOAD_ENCODER;
1221 pcm_thread_signal_event(PCMREC_OPTIONS);
1222 } /* pcmrec_set_recording_options */
1224 /* PCMREC_START/PCMREC_NEW_FILE - start recording (not gapless)
1225 or split stream (gapless) */
1226 static void pcmrec_start(int event, const char *filename)
1228 unsigned long pre_sample_ticks;
1229 int rd_start;
1231 logf("pcmrec_start: %s", filename);
1233 /* reset stats */
1234 num_rec_bytes = 0;
1235 num_rec_samples = 0;
1237 if (is_recording)
1239 /* already recording, just split the stream */
1240 logf("inserting split");
1241 pcmrec_new_stream(filename,
1242 CHUNKF_START_FILE | CHUNKF_END_FILE,
1244 goto start_done;
1247 #if 0
1248 accum_rec_bytes = 0;
1249 accum_pcm_samples = 0;
1250 #endif
1251 spinup_time = -1;
1252 warnings = 0; /* reset warnings */
1254 rd_start = enc_wr_index;
1255 pre_sample_ticks = 0;
1257 if (pre_record_ticks)
1259 int i = rd_start;
1260 #ifdef PCMREC_PARANOID
1261 int i_last = i;
1262 #endif
1263 /* calculate number of available chunks */
1264 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1265 enc_num_chunks) % enc_num_chunks;
1266 /* overflow at 974 seconds of prerecording at 44.1kHz */
1267 unsigned long pre_record_sample_ticks = enc_sample_rate*pre_record_ticks;
1269 /* Get exact measure of recorded data as number of samples aren't
1270 nescessarily going to be the max for each chunk */
1271 for (; avail_pre_chunks-- > 0;)
1273 struct enc_chunk_hdr *chunk;
1274 unsigned long chunk_sample_ticks;
1276 DEC_ENC_INDEX(i);
1278 chunk = GET_ENC_CHUNK(i);
1280 /* must have data to be counted */
1281 if (chunk->enc_data == NULL)
1282 continue;
1284 chunk_sample_ticks = chunk->num_pcm*HZ;
1286 rd_start = i;
1287 pre_sample_ticks += chunk_sample_ticks;
1288 num_rec_bytes += chunk->enc_size;
1289 num_rec_samples += chunk->num_pcm;
1291 /* stop here if enough already */
1292 if (pre_sample_ticks >= pre_record_sample_ticks)
1293 break;
1296 #if 0
1297 accum_rec_bytes = num_rec_bytes;
1298 accum_pcm_samples = num_rec_samples;
1299 #endif
1302 SET_ENC_INDEX(enc_rd_index, rd_start);
1304 /* filename queue should be empty */
1305 if (!pcmrec_fnq_is_empty())
1307 logf("fnq: not empty!");
1308 pcmrec_fnq_set_empty();
1311 dma_lock = false;
1312 is_paused = false;
1313 is_recording = true;
1315 pcmrec_new_stream(filename,
1316 CHUNKF_START_FILE |
1317 (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0),
1318 enc_rd_index);
1320 start_done:
1321 pcm_thread_signal_event(event);
1322 logf("pcmrec_start done");
1323 } /* pcmrec_start */
1325 /* PCMREC_STOP */
1326 static void pcmrec_stop(void)
1328 logf("pcmrec_stop");
1330 if (!is_recording)
1332 logf("not recording");
1333 goto not_recording_or_stopping;
1335 else if (is_stopping)
1337 logf("already stopping");
1338 goto not_recording_or_stopping;
1341 is_stopping = true;
1342 dma_lock = true; /* lock dma write position */
1343 queue_post(&pcmrec_queue, PCMREC_FINISH_STOP, NULL);
1345 not_recording_or_stopping:
1346 pcm_thread_signal_event(PCMREC_STOP);
1347 logf("pcmrec_stop done");
1348 } /* pcmrec_stop */
1350 /* PCMREC_FINISH_STOP */
1351 static void pcmrec_finish_stop(void)
1353 logf("pcmrec_finish_stop");
1355 if (!is_stopping)
1357 logf("not stopping");
1358 goto not_stopping;
1361 /* flush all available data first to avoid overflow while waiting
1362 for encoding to finish */
1363 pcmrec_flush(-1);
1365 /* wait for encoder to finish remaining data */
1366 while (errors == 0 && !wav_queue_empty)
1367 yield();
1369 /* end stream at last data */
1370 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1372 /* flush anything else encoder added */
1373 pcmrec_flush(-1);
1375 /* remove any pending file start not yet processed - should be at
1376 most one at enc_wr_index */
1377 pcmrec_fnq_get_filename(NULL);
1378 /* encoder should abort any chunk it was in midst of processing */
1379 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1381 /* filename queue should be empty */
1382 if (!pcmrec_fnq_is_empty())
1384 logf("fnq: not empty!");
1385 pcmrec_fnq_set_empty();
1388 /* be absolutely sure the file is closed */
1389 if (errors != 0)
1390 pcmrec_close_file(&rec_fdata.rec_file);
1391 rec_fdata.rec_file = -1;
1393 is_recording = false;
1394 is_paused = false;
1395 is_stopping = false;
1396 dma_lock = pre_record_ticks == 0;
1398 not_stopping:
1399 logf("pcmrec_finish_stop done");
1400 } /* pcmrec_finish_stop */
1402 /* PCMREC_PAUSE */
1403 static void pcmrec_pause(void)
1405 logf("pcmrec_pause");
1407 if (!is_recording)
1409 logf("not recording");
1410 goto not_recording_or_paused;
1412 else if (is_paused)
1414 logf("already paused");
1415 goto not_recording_or_paused;
1418 dma_lock = true; /* fix DMA write pointer at current position */
1419 is_paused = true;
1421 not_recording_or_paused:
1422 pcm_thread_signal_event(PCMREC_PAUSE);
1423 logf("pcmrec_pause done");
1424 } /* pcmrec_pause */
1426 /* PCMREC_RESUME */
1427 static void pcmrec_resume(void)
1429 logf("pcmrec_resume");
1431 if (!is_recording)
1433 logf("not recording");
1434 goto not_recording_or_not_paused;
1436 else if (!is_paused)
1438 logf("not paused");
1439 goto not_recording_or_not_paused;
1442 is_paused = false;
1443 is_recording = true;
1444 dma_lock = false;
1446 not_recording_or_not_paused:
1447 pcm_thread_signal_event(PCMREC_RESUME);
1448 logf("pcmrec_resume done");
1449 } /* pcmrec_resume */
1451 static void pcmrec_thread(void) __attribute__((noreturn));
1452 static void pcmrec_thread(void)
1454 struct event ev;
1456 logf("thread pcmrec start");
1458 while(1)
1460 if (is_recording && !is_stopping)
1462 /* Poll periodically to flush data */
1463 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1465 if (ev.id == SYS_TIMEOUT)
1467 pcmrec_flush(0); /* flush if getting full */
1468 continue;
1471 else
1473 /* Not doing anything - sit and wait for commands */
1474 queue_wait(&pcmrec_queue, &ev);
1477 switch (ev.id)
1479 case PCMREC_INIT:
1480 pcmrec_init();
1481 break;
1483 case PCMREC_CLOSE:
1484 pcmrec_close();
1485 break;
1487 case PCMREC_OPTIONS:
1488 pcmrec_set_recording_options(
1489 (struct audio_recording_options *)ev.data);
1490 break;
1492 case PCMREC_START:
1493 case PCMREC_NEW_FILE:
1494 pcmrec_start(ev.id, (const char *)ev.data);
1495 break;
1497 case PCMREC_STOP:
1498 pcmrec_stop();
1499 break;
1501 case PCMREC_FINISH_STOP:
1502 pcmrec_finish_stop();
1503 break;
1505 case PCMREC_PAUSE:
1506 pcmrec_pause();
1507 break;
1509 case PCMREC_RESUME:
1510 pcmrec_resume();
1511 break;
1513 case PCMREC_FLUSH_NUM:
1514 pcmrec_flush((unsigned)ev.data);
1515 break;
1517 case SYS_USB_CONNECTED:
1518 if (is_recording)
1519 break;
1520 pcmrec_close();
1521 reset_hardware();
1522 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1523 usb_wait_for_disconnect(&pcmrec_queue);
1524 break;
1525 } /* end switch */
1526 } /* end while */
1527 } /* pcmrec_thread */
1529 /****************************************************************************/
1530 /* */
1531 /* following functions will be called by the encoder codec */
1532 /* */
1533 /****************************************************************************/
1535 /* pass the encoder settings to the encoder */
1536 void enc_get_inputs(struct enc_inputs *inputs)
1538 inputs->sample_rate = sample_rate;
1539 inputs->num_channels = num_channels;
1540 inputs->config = &enc_config;
1541 } /* enc_get_inputs */
1543 /* set the encoder dimensions (called by encoder codec at initialization and
1544 termination) */
1545 void enc_set_parameters(struct enc_parameters *params)
1547 size_t bufsize, resbytes;
1549 logf("enc_set_parameters");
1551 if (!params)
1553 logf("reset");
1554 /* Encoder is terminating */
1555 memset(&enc_config, 0, sizeof (enc_config));
1556 enc_sample_rate = 0;
1557 return;
1560 enc_sample_rate = params->enc_sample_rate;
1561 logf("enc sampr:%d", enc_sample_rate);
1563 SET_PCM_POS(pcm_rd_pos, dma_wr_pos);
1565 enc_config.afmt = params->afmt;
1566 /* addition of the header is always implied - chunk size 4-byte aligned */
1567 enc_chunk_size =
1568 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1569 enc_events_callback = params->events_callback;
1571 logf("chunk size:%d", enc_chunk_size);
1573 /*** Configure the buffers ***/
1575 /* Layout of recording buffer:
1576 * [ax] = possible alignment x multiple
1577 * [sx] = possible size alignment of x multiple
1578 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1579 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1581 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1582 logf("resbytes:%d", resbytes);
1584 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1585 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1586 #ifdef PCMREC_PARANOID
1587 - sizeof (*wrap_id_p)
1588 #endif
1591 enc_num_chunks = bufsize / enc_chunk_size;
1592 logf("num chunks:%d", enc_num_chunks);
1594 /* get real amount used by encoder chunks */
1595 bufsize = enc_num_chunks*enc_chunk_size;
1596 logf("enc size:%d", bufsize);
1598 #ifdef PCMREC_PARANOID
1599 /* add magic at wraparound */
1600 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1601 bufsize += sizeof (*wrap_id_p);
1602 *wrap_id_p = ENC_CHUNK_MAGIC;
1603 #endif /* PCMREC_PARANOID */
1605 /* panic boost thread priority at 1 second remaining */
1606 panic_threshold = enc_num_chunks -
1607 (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size;
1608 if (panic_threshold < 0)
1609 panic_threshold = 0;
1611 logf("panic thr:%d", panic_threshold);
1613 /** set OUT parameters **/
1614 params->enc_buffer = enc_buffer;
1615 params->buf_chunk_size = enc_chunk_size;
1616 params->num_chunks = enc_num_chunks;
1618 /* calculate reserve buffer start and return pointer to encoder */
1619 params->reserve_buffer = NULL;
1620 if (resbytes > 0)
1622 params->reserve_buffer = enc_buffer + bufsize;
1623 bufsize += resbytes;
1626 /* place filename queue at end of buffer using up whatever remains */
1627 fnq_rd_pos = 0; /* reset */
1628 fnq_wr_pos = 0; /* reset */
1629 fn_queue = enc_buffer + bufsize;
1630 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1631 fnq_size = ALIGN_DOWN(fnq_size, MAX_PATH);
1632 logf("fnq files: %d", fnq_size / MAX_PATH);
1634 #if 0
1635 logf("ab :%08X", (unsigned long)audiobuf);
1636 logf("pcm:%08X", (unsigned long)pcm_buffer);
1637 logf("enc:%08X", (unsigned long)enc_buffer);
1638 logf("res:%08X", (unsigned long)params->reserve_buffer);
1639 #ifdef PCMREC_PARANOID
1640 logf("wip:%08X", (unsigned long)wrap_id_p);
1641 #endif
1642 logf("fnq:%08X", (unsigned long)fn_queue);
1643 logf("end:%08X", (unsigned long)fn_queue + fnq_size);
1644 logf("abe:%08X", (unsigned long)audiobufend);
1645 #endif
1647 /* init all chunk headers and reset indexes */
1648 SET_ENC_INDEX(enc_rd_index, 0);
1649 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1651 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1652 #ifdef PCMREC_PARANOID
1653 chunk->id = ENC_CHUNK_MAGIC;
1654 #endif
1655 chunk->flags = 0;
1658 logf("enc_set_parameters done");
1659 } /* enc_set_parameters */
1661 /* return encoder chunk at current write position */
1662 struct enc_chunk_hdr * enc_get_chunk(void)
1664 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1666 #ifdef PCMREC_PARANOID
1667 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1669 errors |= PCMREC_E_CHUNK_OVF;
1670 logf("finish chk ovf: %d", enc_wr_index);
1672 #endif
1674 chunk->flags &= CHUNKF_START_FILE;
1676 if (!is_recording)
1677 chunk->flags |= CHUNKF_PRERECORD;
1679 return chunk;
1680 } /* enc_get_chunk */
1682 /* releases the current chunk into the available chunks */
1683 void enc_finish_chunk(void)
1685 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1687 if ((long)chunk->flags < 0)
1689 /* encoder set error flag */
1690 errors |= PCMREC_E_ENCODER;
1691 logf("finish chk enc error");
1694 PARANOID_CHUNK_CHECK(chunk);
1696 /* advance enc_wr_index to the next encoder chunk */
1697 INC_ENC_INDEX(enc_wr_index);
1699 if (enc_rd_index != enc_wr_index)
1701 num_rec_bytes += chunk->enc_size;
1702 num_rec_samples += chunk->num_pcm;
1703 #if 0
1704 accum_rec_bytes += chunk->enc_size;
1705 accum_pcm_samples += chunk->num_pcm;
1706 #endif
1708 else if (is_recording) /* buffer full */
1710 /* keep current position - but put up warning flag */
1711 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1712 logf("enc_buffer ovf");
1713 DEC_ENC_INDEX(enc_wr_index);
1715 else
1717 /* advance enc_rd_index for prerecording */
1718 INC_ENC_INDEX(enc_rd_index);
1720 } /* enc_finish_chunk */
1722 /* checks near empty state on pcm input buffer */
1723 int enc_pcm_buf_near_empty(void)
1725 /* less than 1sec raw data? => unboost encoder */
1726 int wp = dma_wr_pos;
1727 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1728 return avail < (sample_rate << 2) ? 1 : 0;
1729 } /* enc_pcm_buf_near_empty */
1731 /* passes a pointer to next chunk of unprocessed wav data */
1732 /* TODO: this really should give the actual size returned */
1733 unsigned char * enc_get_pcm_data(size_t size)
1735 int wp = dma_wr_pos;
1736 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1738 /* limit the requested pcm data size */
1739 if (size > PCM_MAX_FEED_SIZE)
1740 size = PCM_MAX_FEED_SIZE;
1742 if (avail >= size)
1744 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1745 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1747 SET_PCM_POS(pcm_rd_pos, next_pos);
1748 pcm_rd_pos = next_pos;
1750 /* ptr must point to continous data at wraparound position */
1751 if ((size_t)pcm_rd_pos < size)
1752 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1753 pcm_buffer, pcm_rd_pos);
1755 wav_queue_empty = false;
1756 return ptr;
1759 /* not enough data available - encoder should idle */
1760 wav_queue_empty = true;
1761 return NULL;
1762 } /* enc_get_pcm_data */
1764 /* puts some pcm data back in the queue */
1765 size_t enc_unget_pcm_data(size_t size)
1767 /* can't let DMA advance write position when doing this */
1768 int level = set_irq_level(HIGHEST_IRQ_LEVEL);
1770 if (pcm_rd_pos != dma_wr_pos)
1772 /* disallow backing up into current DMA write chunk */
1773 size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE)
1774 & PCM_CHUNK_MASK;
1775 int next_pos;
1777 /* limit size to amount of old data remaining */
1778 if (size > old_avail)
1779 size = old_avail;
1781 next_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1782 SET_PCM_POS(pcm_rd_pos, next_pos);
1785 set_irq_level(level);
1787 return size;
1788 } /* enc_unget_pcm_data */
1790 /** Low level pcm recording apis **/
1792 /****************************************************************************
1793 * Functions that do not require targeted implementation but only a targeted
1794 * interface
1796 void pcm_record_data(pcm_more_callback_type2 more_ready,
1797 void *start, size_t size)
1799 if (!(start && size))
1800 return;
1802 pcm_callback_more_ready = more_ready;
1803 pcm_rec_dma_start(start, size);
1804 } /* pcm_record_data */
1806 void pcm_stop_recording(void)
1808 if (pcm_recording)
1809 pcm_rec_dma_stop();
1810 } /* pcm_stop_recording */