fix red
[Rockbox.git] / firmware / pcm_record.c
blobf467b734c81af5ce9b131daad167d99934c6d201
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 "thread.h"
23 #include <string.h>
24 #include "ata.h"
25 #include "usb.h"
26 #include "buffer.h"
27 #include "general.h"
28 #include "audio.h"
29 #include "sound.h"
30 #include "id3.h"
31 #ifdef HAVE_SPDIF_IN
32 #include "spdif.h"
33 #endif
35 /***************************************************************************/
37 /**
38 * APIs implemented in the target tree portion:
39 * Public -
40 * pcm_init_recording
41 * pcm_close_recording
42 * Semi-private -
43 * pcm_rec_dma_start
44 * pcm_rec_dma_stop
47 /** These items may be implemented target specifically or need to
48 be shared semi-privately **/
49 extern struct thread_entry *codec_thread_p;
51 /* the registered callback function for when more data is available */
52 volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
53 /* DMA transfer in is currently active */
54 volatile bool pcm_recording = false;
56 /** General recording state **/
57 static bool is_recording; /* We are recording */
58 static bool is_paused; /* We have paused */
59 static unsigned long errors; /* An error has occured */
60 static unsigned long warnings; /* Warning */
61 static int flush_interrupts = 0; /* Number of messages queued that
62 should interrupt a flush in
63 progress -
64 for a safety net and a prompt
65 response to stop, split and pause
66 requests -
67 only interrupts a flush initiated
68 by pcmrec_flush(0) */
70 /** Stats on encoded data for current file **/
71 static size_t num_rec_bytes; /* Num bytes recorded */
72 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
74 /** Stats on encoded data for all files from start to stop **/
75 #if 0
76 static unsigned long long accum_rec_bytes; /* total size written to chunks */
77 static unsigned long long accum_pcm_samples; /* total pcm count processed */
78 #endif
80 /* Keeps data about current file and is sent as event data for codec */
81 static struct enc_file_event_data rec_fdata IDATA_ATTR =
83 .chunk = NULL,
84 .new_enc_size = 0,
85 .new_num_pcm = 0,
86 .rec_file = -1,
87 .num_pcm_samples = 0
90 /** These apply to current settings **/
91 static int rec_source; /* current rec_source setting */
92 static int rec_frequency; /* current frequency setting */
93 static unsigned long sample_rate; /* Sample rate in HZ */
94 static int num_channels; /* Current number of channels */
95 static struct encoder_config enc_config; /* Current encoder configuration */
96 static unsigned long pre_record_ticks; /* pre-record time in ticks */
98 /****************************************************************************
99 use 2 circular buffers:
100 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
101 enc_buffer=encoded audio buffer: storage for encoder output data
103 Flow:
104 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
105 2. if enough pcm data are available the encoder codec does encoding of pcm
106 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
107 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
109 Functions calls (basic encoder steps):
110 1.main: audio_load_encoder(); start the encoder
111 2.encoder: enc_get_inputs(); get encoder recording settings
112 3.encoder: enc_set_parameters(); set the encoder parameters
113 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
114 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
115 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
116 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
117 8.encoder: <process enc chunk> compress and store data to enc chunk
118 9.encoder: enc_finish_chunk(); inform main about chunk processed and
119 is available to be written to a file.
120 Encoder can place any number of chunks
121 of PCM data in a single output chunk
122 but must stay within its output chunk
123 size
124 A.encoder: repeat 4. to 9.
125 B.pcmrec: enc_events_callback(); called for certain events
127 (*) Optional step
128 ****************************************************************************/
130 /** buffer parameters where incoming PCM data is placed **/
131 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
132 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
133 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
135 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
136 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
138 #ifdef PCMREC_PARANOID
139 static void paranoid_set_code(unsigned long code, int line)
141 logf("%08X at %d", code, line);
142 if ((long)code < 0)
143 errors |= code;
144 else
145 warnings |= code;
148 #define PARANOID_ENC_INDEX_CHECK(index) \
149 { if (index != index##_last) \
150 paranoid_set_code((&index == &enc_rd_index) ? \
151 PCMREC_E_ENC_RD_INDEX_TRASHED : PCMREC_E_ENC_WR_INDEX_TRASHED, \
152 __LINE__); }
153 #define PARANOID_PCM_POS_CHECK(pos) \
154 { if (pos != pos##_last) \
155 paranoid_set_code((&pos == &pcm_rd_pos) ? \
156 PCMREC_W_PCM_RD_POS_TRASHED : PCMREC_W_DMA_WR_POS_TRASHED, \
157 __LINE__); }
158 #define PARANOID_SET_LAST(var) \
159 ; var##_last = var
160 #define PARANOID_CHUNK_CHECK(chunk) \
161 paranoid_chunk_check(chunk)
162 #else
163 #define PARANOID_ENC_INDEX_CHECK(index)
164 #define PARANOID_PCM_POS_CHECK(pos)
165 #define PARANOID_SET_LAST(var)
166 #define PARANOID_CHUNK_CHECK(chunk)
167 #endif
169 #define INC_ENC_INDEX(index) \
170 PARANOID_ENC_INDEX_CHECK(index) \
171 { if (++index >= enc_num_chunks) index = 0; } \
172 PARANOID_SET_LAST(index)
173 #define DEC_ENC_INDEX(index) \
174 PARANOID_ENC_INDEX_CHECK(index) \
175 { if (--index < 0) index = enc_num_chunks - 1; } \
176 PARANOID_SET_LAST(index)
177 #define SET_ENC_INDEX(index, value) \
178 PARANOID_ENC_INDEX_CHECK(index) \
179 index = value \
180 PARANOID_SET_LAST(index)
181 #define SET_PCM_POS(pos, value) \
182 PARANOID_PCM_POS_CHECK(pos) \
183 pos = value \
184 PARANOID_SET_LAST(pos)
186 static size_t rec_buffer_size; /* size of available buffer */
187 static unsigned char *pcm_buffer; /* circular recording buffer */
188 static unsigned char *enc_buffer; /* circular encoding buffer */
189 static volatile int dma_wr_pos; /* current DMA write pos */
190 static int pcm_rd_pos; /* current PCM read pos */
191 static int pcm_enc_pos; /* position encoder is processing */
192 static volatile bool dma_lock; /* lock DMA write position */
193 static int enc_wr_index; /* encoder chunk write index */
194 static int enc_rd_index; /* encoder chunk read index */
195 static int enc_num_chunks; /* number of chunks in ringbuffer */
196 static size_t enc_chunk_size; /* maximum encoder chunk size */
197 static unsigned long enc_sample_rate; /* sample rate used by encoder */
198 static bool pcmrec_context = false; /* called by pcmrec thread? */
199 static bool pcm_buffer_empty; /* all pcm chunks processed? */
201 /** file flushing **/
202 static int low_watermark; /* Low watermark to stop flush */
203 static int high_watermark; /* max chunk limit for data flush */
204 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
205 static int last_ata_spinup_time = -1;/* previous spin time used */
206 #ifdef HAVE_PRIORITY_SCHEDULING
207 static int flood_watermark; /* boost thread priority when here */
208 #endif
210 /* Constants that control watermarks */
211 #define LOW_SECONDS 1 /* low watermark time till empty */
212 #define MINI_CHUNKS 10 /* chunk count for mini flush */
213 #ifdef HAVE_PRIORITY_SCHEDULING
214 #define PRIO_SECONDS 10 /* max flush time before priority boost */
215 #endif
216 #if MEM <= 16
217 #define PANIC_SECONDS 5 /* flood watermark time until full */
218 #define FLUSH_SECONDS 7 /* flush watermark time until full */
219 #else
220 #define PANIC_SECONDS 8
221 #define FLUSH_SECONDS 10
222 #endif /* MEM */
224 /** encoder events **/
225 static void (*enc_events_callback)(enum enc_events event, void *data);
227 /** Path queue for files to write **/
228 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
229 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
230 static unsigned char *fn_queue; /* pointer to first filename */
231 static ssize_t fnq_size; /* capacity of queue in bytes */
232 static int fnq_rd_pos; /* current read position */
233 static int fnq_wr_pos; /* current write position */
235 enum
237 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
238 incoming messages - combine
239 with other constants */
240 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
241 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
242 chunks */
243 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
244 reached */
247 /** extra debugging info positioned away from other vars **/
248 #ifdef PCMREC_PARANOID
249 static unsigned long *wrap_id_p; /* magic at end of encoding buffer */
250 static volatile int dma_wr_pos_last; /* previous dma write position */
251 static int pcm_rd_pos_last; /* previous pcm read position */
252 static int enc_rd_index_last; /* previsou encoder read position */
253 static int enc_wr_index_last; /* previsou encoder read position */
254 #endif
257 /***************************************************************************/
259 static struct event_queue pcmrec_queue;
260 static struct queue_sender_list pcmrec_queue_send;
261 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
262 static const char pcmrec_thread_name[] = "pcmrec";
263 static struct thread_entry *pcmrec_thread_p;
265 static void pcmrec_thread(void);
267 enum
269 PCMREC_NULL = 0,
270 PCMREC_INIT, /* enable recording */
271 PCMREC_CLOSE, /* close recording */
272 PCMREC_OPTIONS, /* set recording options */
273 PCMREC_RECORD, /* record a new file */
274 PCMREC_STOP, /* stop the current recording */
275 PCMREC_PAUSE, /* pause the current recording */
276 PCMREC_RESUME, /* resume the current recording */
277 #if 0
278 PCMREC_FLUSH_NUM, /* flush a number of files out */
279 #endif
282 /*******************************************************************/
283 /* Functions that are not executing in the pcmrec_thread first */
284 /*******************************************************************/
286 /* Callback for when more data is ready - called in interrupt context */
287 static int pcm_rec_have_more(int status)
289 if (status < 0)
291 /* some error condition */
292 if (status == DMA_REC_ERROR_DMA)
294 /* Flush recorded data to disk and stop recording */
295 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
296 return -1;
298 /* else try again next transmission */
300 else if (!dma_lock)
302 /* advance write position */
303 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
305 /* set pcm ovf if processing start position is inside current
306 write chunk */
307 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
308 warnings |= PCMREC_W_PCM_BUFFER_OVF;
310 #ifdef PCMREC_PARANOID
311 /* write position must always be on PCM_CHUNK_SIZE boundary -
312 anything else is corruption */
313 if (next_pos & (PCM_CHUNK_SIZE-1))
315 logf("dma_wr_pos unalgn: %d", next_pos);
316 warnings |= PCMREC_W_DMA_WR_POS_ALIGN;
317 next_pos &= ~PCM_CHUNK_SIZE; /* re-align */
319 #endif
320 SET_PCM_POS(dma_wr_pos, next_pos);
323 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
324 return 0;
325 } /* pcm_rec_have_more */
327 static void reset_hardware(void)
329 /* reset pcm to defaults (playback only) */
330 pcm_set_frequency(HW_SAMPR_DEFAULT);
331 audio_set_output_source(AUDIO_SRC_PLAYBACK);
332 pcm_apply_settings();
335 /** pcm_rec_* group **/
338 * Clear all errors and warnings
340 void pcm_rec_error_clear(void)
342 errors = warnings = 0;
343 } /* pcm_rec_error_clear */
346 * Check mode, errors and warnings
348 unsigned long pcm_rec_status(void)
350 unsigned long ret = 0;
352 if (is_recording)
353 ret |= AUDIO_STATUS_RECORD;
354 else if (pre_record_ticks)
355 ret |= AUDIO_STATUS_PRERECORD;
357 if (is_paused)
358 ret |= AUDIO_STATUS_PAUSE;
360 if (errors)
361 ret |= AUDIO_STATUS_ERROR;
363 if (warnings)
364 ret |= AUDIO_STATUS_WARNING;
366 return ret;
367 } /* pcm_rec_status */
370 * Return warnings that have occured since recording started
372 unsigned long pcm_rec_get_warnings(void)
374 return warnings;
377 #if 0
378 int pcm_rec_current_bitrate(void)
380 if (accum_pcm_samples == 0)
381 return 0;
383 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
384 } /* pcm_rec_current_bitrate */
385 #endif
387 #if 0
388 int pcm_rec_encoder_afmt(void)
390 return enc_config.afmt;
391 } /* pcm_rec_encoder_afmt */
392 #endif
394 #if 0
395 int pcm_rec_rec_format(void)
397 return afmt_rec_format[enc_config.afmt];
398 } /* pcm_rec_rec_format */
399 #endif
401 #ifdef HAVE_SPDIF_IN
402 unsigned long pcm_rec_sample_rate(void)
404 /* Which is better ?? */
405 #if 0
406 return enc_sample_rate;
407 #endif
408 return sample_rate;
409 } /* audio_get_sample_rate */
410 #endif
413 * Creates pcmrec_thread
415 void pcm_rec_init(void)
417 queue_init(&pcmrec_queue, true);
418 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send);
419 pcmrec_thread_p =
420 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
421 pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
422 IF_COP(, CPU, false));
423 } /* pcm_rec_init */
425 /** audio_* group **/
428 * Initializes recording - call before calling any other recording function
430 void audio_init_recording(unsigned int buffer_offset)
432 logf("audio_init_recording");
433 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
434 logf("audio_init_recording done");
435 (void)buffer_offset;
436 } /* audio_init_recording */
439 * Closes recording - call audio_stop_recording first
441 void audio_close_recording(void)
443 logf("audio_close_recording");
444 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
445 logf("audio_close_recording done");
446 } /* audio_close_recording */
449 * Sets recording parameters
451 void audio_set_recording_options(struct audio_recording_options *options)
453 logf("audio_set_recording_options");
454 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
455 logf("audio_set_recording_options done");
456 } /* audio_set_recording_options */
459 * Start recording if not recording or else split
461 void audio_record(const char *filename)
463 logf("audio_record: %s", filename);
464 flush_interrupts++;
465 logf("flush int: %d", flush_interrupts);
466 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
467 logf("audio_record_done");
468 } /* audio_record */
471 * Stop current recording if recording
473 void audio_stop_recording(void)
475 logf("audio_stop_recording");
476 flush_interrupts++;
477 logf("flush int: %d", flush_interrupts);
478 queue_send(&pcmrec_queue, PCMREC_STOP, 0);
479 logf("audio_stop_recording done");
480 } /* audio_stop_recording */
483 * Pause current recording
485 void audio_pause_recording(void)
487 logf("audio_pause_recording");
488 flush_interrupts++;
489 logf("flush int: %d", flush_interrupts);
490 queue_send(&pcmrec_queue, PCMREC_PAUSE, 0);
491 logf("audio_pause_recording done");
492 } /* audio_pause_recording */
495 * Resume current recording if paused
497 void audio_resume_recording(void)
499 logf("audio_resume_recording");
500 queue_send(&pcmrec_queue, PCMREC_RESUME, 0);
501 logf("audio_resume_recording done");
502 } /* audio_resume_recording */
505 * Note that microphone is mono, only left value is used
506 * See audiohw_set_recvol() for exact ranges.
508 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
511 void audio_set_recording_gain(int left, int right, int type)
513 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
514 audiohw_set_recvol(left, right, type);
515 } /* audio_set_recording_gain */
517 /** Information about current state **/
520 * Return current recorded time in ticks (playback eqivalent time)
522 unsigned long audio_recorded_time(void)
524 if (!is_recording || enc_sample_rate == 0)
525 return 0;
527 /* return actual recorded time a la encoded data even if encoder rate
528 doesn't match the pcm rate */
529 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
530 } /* audio_recorded_time */
533 * Return number of bytes encoded to output
535 unsigned long audio_num_recorded_bytes(void)
537 if (!is_recording)
538 return 0;
540 return num_rec_bytes;
541 } /* audio_num_recorded_bytes */
543 #ifdef HAVE_SPDIF_IN
545 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
546 * our reading on the actual SPDIF sample rate (which might be a bit
547 * inaccurate), we round off to the closest sample rate that is supported by
548 * SPDIF.
550 int audio_get_spdif_sample_rate(void)
552 unsigned long measured_rate = spdif_measure_frequency();
553 /* Find which SPDIF sample rate we're closest to. */
554 return round_value_to_list32(measured_rate, audio_master_sampr_list,
555 SAMPR_NUM_FREQ, false);
556 } /* audio_get_spdif_sample_rate */
557 #endif /* HAVE_SPDIF_IN */
559 /***************************************************************************/
560 /* */
561 /* Functions that execute in the context of pcmrec_thread */
562 /* */
563 /***************************************************************************/
565 /** Filename Queue **/
567 /* returns true if the queue is empty */
568 static inline bool pcmrec_fnq_is_empty(void)
570 return fnq_rd_pos == fnq_wr_pos;
571 } /* pcmrec_fnq_is_empty */
573 /* empties the filename queue */
574 static inline void pcmrec_fnq_set_empty(void)
576 fnq_rd_pos = fnq_wr_pos;
577 } /* pcmrec_fnq_set_empty */
579 /* returns true if the queue is full */
580 static bool pcmrec_fnq_is_full(void)
582 ssize_t size = fnq_wr_pos - fnq_rd_pos;
583 if (size < 0)
584 size += fnq_size;
586 return size >= fnq_size - MAX_PATH;
587 } /* pcmrec_fnq_is_full */
589 /* queue another filename - will overwrite oldest one if full */
590 static bool pcmrec_fnq_add_filename(const char *filename)
592 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
594 if ((fnq_wr_pos += MAX_PATH) >= fnq_size)
595 fnq_wr_pos = 0;
597 if (fnq_rd_pos != fnq_wr_pos)
598 return true;
600 /* queue full */
601 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
602 fnq_rd_pos = 0;
604 return true;
605 } /* pcmrec_fnq_add_filename */
607 /* replace the last filename added */
608 static bool pcmrec_fnq_replace_tail(const char *filename)
610 int pos;
612 if (pcmrec_fnq_is_empty())
613 return false;
615 pos = fnq_wr_pos - MAX_PATH;
616 if (pos < 0)
617 pos = fnq_size - MAX_PATH;
619 strncpy(fn_queue + pos, filename, MAX_PATH);
621 return true;
622 } /* pcmrec_fnq_replace_tail */
624 /* pulls the next filename from the queue */
625 static bool pcmrec_fnq_get_filename(char *filename)
627 if (pcmrec_fnq_is_empty())
628 return false;
630 if (filename)
631 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
633 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
634 fnq_rd_pos = 0;
636 return true;
637 } /* pcmrec_fnq_get_filename */
639 /* close the file number pointed to by fd_p */
640 static void pcmrec_close_file(int *fd_p)
642 if (*fd_p < 0)
643 return; /* preserve error */
645 close(*fd_p);
646 *fd_p = -1;
647 } /* pcmrec_close_file */
649 #ifdef PCMREC_PARANOID
650 static void paranoid_chunk_check(const struct enc_chunk_hdr *chunk)
652 /* check integrity of things that must be ok - data or not */
654 /* check magic in header */
655 if (chunk->id != ENC_CHUNK_MAGIC)
657 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
658 logf("bad chunk: %d", chunk - (struct enc_chunk_hdr *)enc_buffer);
661 /* check magic wrap id */
662 if (*wrap_id_p != ENC_CHUNK_MAGIC)
664 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
665 logf("bad magic at wrap pos");
668 if (chunk->enc_data == NULL) /* has data? */
669 return;
671 /* check that data points to something after header */
672 if (chunk->enc_data < ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk))
674 errors |= PCMREC_E_BAD_CHUNK;
675 logf("chk ptr < hdr end");
678 /* check if data end is within chunk */
679 if (chunk->enc_data + chunk->enc_size >
680 (unsigned char *)chunk + enc_chunk_size)
682 errors |= PCMREC_E_BAD_CHUNK;
683 logf("chk data > chk end");
686 if ((chunk->flags & ~CHUNKF_ALLFLAGS) != 0)
688 errors |= PCMREC_E_BAD_CHUNK;
689 logf("chk bad flags %08X", chunk->flags);
691 } /* paranoid_chunk_check */
692 #endif /* PCMREC_PARANOID */
694 /** Data Flushing **/
697 * called after callback to update sizes if codec changed the amount of data
698 * a chunk represents
700 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
701 unsigned long prev_num_pcm)
703 if (rec_fdata.new_enc_size != prev_enc_size)
705 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
706 num_rec_bytes += size_diff;
707 #if 0
708 accum_rec_bytes += size_diff;
709 #endif
712 if (rec_fdata.new_num_pcm != prev_num_pcm)
714 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
715 num_rec_samples += pcm_diff;
716 #if 0
717 accum_pcm_samples += pcm_diff;
718 #endif
720 } /* pcmrec_update_sizes_inl */
722 /* don't need to inline every instance */
723 static void pcmrec_update_sizes(size_t prev_enc_size,
724 unsigned long prev_num_pcm)
726 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
727 } /* pcmrec_update_sizes */
729 static void pcmrec_start_file(void)
731 size_t enc_size = rec_fdata.new_enc_size;
732 unsigned long num_pcm = rec_fdata.new_num_pcm;
733 int curr_rec_file = rec_fdata.rec_file;
734 char filename[MAX_PATH];
736 /* must always pull the filename that matches with this queue */
737 if (!pcmrec_fnq_get_filename(filename))
739 logf("start file: fnq empty");
740 *filename = '\0';
741 errors |= PCMREC_E_FNQ_DESYNC;
743 else if (errors != 0)
745 logf("start file: error already");
747 else if (curr_rec_file >= 0)
749 /* Any previous file should have been closed */
750 logf("start file: file already open");
751 errors |= PCMREC_E_FNQ_DESYNC;
754 if (errors != 0)
755 rec_fdata.chunk->flags |= CHUNKF_ERROR;
757 /* encoder can set error flag here and should increase
758 enc_new_size and pcm_new_size to reflect additional
759 data written if any */
760 rec_fdata.filename = filename;
761 enc_events_callback(ENC_START_FILE, &rec_fdata);
763 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
765 logf("start file: enc error");
766 errors |= PCMREC_E_ENCODER;
769 if (errors != 0)
771 pcmrec_close_file(&curr_rec_file);
772 /* Write no more to this file */
773 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
775 else
777 pcmrec_update_sizes(enc_size, num_pcm);
780 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
781 } /* pcmrec_start_file */
783 static inline void pcmrec_write_chunk(void)
785 size_t enc_size = rec_fdata.new_enc_size;
786 unsigned long num_pcm = rec_fdata.new_num_pcm;
788 if (errors != 0)
789 rec_fdata.chunk->flags |= CHUNKF_ERROR;
791 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
793 if ((long)rec_fdata.chunk->flags >= 0)
795 pcmrec_update_sizes_inl(enc_size, num_pcm);
797 else if (errors == 0)
799 logf("wr chk enc error %d %d",
800 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
801 errors |= PCMREC_E_ENCODER;
803 } /* pcmrec_write_chunk */
805 static void pcmrec_end_file(void)
807 /* all data in output buffer for current file will have been
808 written and encoder can now do any nescessary steps to
809 finalize the written file */
810 size_t enc_size = rec_fdata.new_enc_size;
811 unsigned long num_pcm = rec_fdata.new_num_pcm;
813 enc_events_callback(ENC_END_FILE, &rec_fdata);
815 if (errors == 0)
817 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
819 logf("end file: enc error");
820 errors |= PCMREC_E_ENCODER;
822 else
824 pcmrec_update_sizes(enc_size, num_pcm);
828 /* Force file close if error */
829 if (errors != 0)
830 pcmrec_close_file(&rec_fdata.rec_file);
832 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
833 } /* pcmrec_end_file */
836 * Update buffer watermarks with spinup time compensation
838 * All this assumes reasonable data rates, chunk sizes and sufficient
839 * memory for the most part. Some dumb checks are included but perhaps
840 * are pointless since this all will break down at extreme limits that
841 * are currently not applicable to any supported device.
843 static void pcmrec_refresh_watermarks(void)
845 logf("ata spinup: %d", ata_spinup_time);
847 /* set the low mark for when flushing stops if automatic */
848 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
849 / enc_chunk_size;
850 logf("low wmk: %d", low_watermark);
852 #ifdef HAVE_PRIORITY_SCHEDULING
853 /* panic boost thread priority if 2 seconds of ground is lost -
854 this allows encoder to boost with just under a second of
855 pcm data (if not yet full enough to boost itself)
856 and not falsely trip the alarm. */
857 flood_watermark = enc_num_chunks -
858 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
859 / enc_chunk_size;
861 if (flood_watermark < low_watermark)
863 logf("warning: panic < low");
864 flood_watermark = low_watermark;
867 logf("flood at: %d", flood_watermark);
868 #endif
869 spinup_time = last_ata_spinup_time = ata_spinup_time;
871 /* write at 8s + st remaining in enc_buffer - range 12s to
872 20s total - default to 3.5s spinup. */
873 if (spinup_time == 0)
874 spinup_time = 35*HZ/10; /* default - cozy */
875 else if (spinup_time < 2*HZ)
876 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
877 else if (spinup_time > 10*HZ)
878 spinup_time = 10*HZ; /* do you have a functioning HD? */
880 /* try to start writing with 10s remaining after disk spinup */
881 high_watermark = enc_num_chunks -
882 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
883 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
885 if (high_watermark < low_watermark)
887 high_watermark = low_watermark;
888 low_watermark /= 2;
889 logf("warning: low 'write at'");
892 logf("write at: %d", high_watermark);
893 } /* pcmrec_refresh_watermarks */
896 * Process the chunks
898 * This function is called when queue_get_w_tmo times out.
900 * Set flush_num to the number of files to flush to disk or to
901 * a PCMREC_FLUSH_* constant.
903 static void pcmrec_flush(unsigned flush_num)
905 #ifdef HAVE_PRIORITY_SCHEDULING
906 static unsigned long last_flush_tick; /* tick when function returned */
907 unsigned long start_tick; /* When flush started */
908 unsigned long prio_tick; /* Timeout for auto boost */
909 int prio_pcmrec; /* Current thread priority for pcmrec */
910 int prio_codec; /* Current thread priority for codec */
911 #endif
912 int num_ready; /* Number of chunks ready at start */
913 unsigned remaining; /* Number of file starts remaining */
914 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
915 bool interruptable; /* Flush can be interupted */
917 num_ready = enc_wr_index - enc_rd_index;
918 if (num_ready < 0)
919 num_ready += enc_num_chunks;
921 /* save interruptable flag and remove it to get the actual count */
922 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
923 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
925 if (flush_num == 0)
927 if (!is_recording)
928 return;
930 if (ata_spinup_time != last_ata_spinup_time)
931 pcmrec_refresh_watermarks();
933 /* enough available? no? then leave */
934 if (num_ready < high_watermark)
935 return;
936 } /* endif (flush_num == 0) */
938 #ifdef HAVE_PRIORITY_SCHEDULING
939 start_tick = current_tick;
940 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
942 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
944 /* if we're getting called too much and this isn't forced,
945 boost stat by expiring timeout in advance */
946 logf("too frequent flush");
947 prio_tick = current_tick - 1;
950 prio_pcmrec = -1;
951 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
952 need init */
953 #endif
955 logf("writing:%d(%d):%s%s", num_ready, flush_num,
956 interruptable ? "i" : "",
957 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
959 cpu_boost(true);
961 remaining = flush_num;
962 chunks_flushed = 0;
964 while (num_ready > 0)
966 /* check current number of encoder chunks */
967 int num = enc_wr_index - enc_rd_index;
968 if (num < 0)
969 num += enc_num_chunks;
971 if (num <= low_watermark &&
972 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
974 logf("low data: %d", num);
975 break; /* data remaining is below threshold */
978 if (interruptable && flush_interrupts > 0)
980 logf("int at: %d", num);
981 break; /* interrupted */
984 #ifdef HAVE_PRIORITY_SCHEDULING
985 if (prio_pcmrec == -1 && (num >= flood_watermark ||
986 TIME_AFTER(current_tick, prio_tick)))
988 /* losing ground or holding without progress - boost
989 priority until finished */
990 logf("pcmrec: boost (%s)",
991 num >= flood_watermark ? "num" : "time");
992 prio_pcmrec = thread_set_priority(NULL,
993 thread_get_priority(NULL) - 1);
994 prio_codec = thread_set_priority(codec_thread_p,
995 thread_get_priority(codec_thread_p) - 1);
997 #endif
999 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
1000 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
1001 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
1003 PARANOID_CHUNK_CHECK(rec_fdata.chunk);
1005 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
1007 pcmrec_start_file();
1008 if (--remaining == 0)
1009 num_ready = 0; /* stop on next loop - must write this
1010 chunk if it has data */
1013 pcmrec_write_chunk();
1015 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
1016 pcmrec_end_file();
1018 INC_ENC_INDEX(enc_rd_index);
1020 if (errors != 0)
1021 break;
1023 if (flush_num == PCMREC_FLUSH_MINI &&
1024 ++chunks_flushed >= MINI_CHUNKS)
1026 logf("mini flush break");
1027 break;
1029 /* no yielding; the file apis called in the codecs do that
1030 sufficiently */
1031 } /* end while */
1033 /* sync file */
1034 if (rec_fdata.rec_file >= 0)
1035 fsync(rec_fdata.rec_file);
1037 cpu_boost(false);
1039 #ifdef HAVE_PRIORITY_SCHEDULING
1040 if (prio_pcmrec != -1)
1042 /* return to original priorities */
1043 logf("pcmrec: unboost priority");
1044 thread_set_priority(NULL, prio_pcmrec);
1045 thread_set_priority(codec_thread_p, prio_codec);
1048 last_flush_tick = current_tick; /* save tick when we left */
1049 #endif
1051 logf("done");
1052 } /* pcmrec_flush */
1055 * Marks a new stream in the buffer and gives the encoder a chance for special
1056 * handling of transition from one to the next. The encoder may change the
1057 * chunk that ends the old stream by requesting more chunks and similiarly for
1058 * the new but must always advance the position though the interface. It can
1059 * later reject any data it cares to when writing the file but should mark the
1060 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
1061 * a NULL data pointer without error as well.
1063 static void pcmrec_new_stream(const char *filename, /* next file name */
1064 unsigned long flags, /* CHUNKF_* flags */
1065 int pre_index) /* index for prerecorded data */
1067 logf("pcmrec_new_stream");
1068 char path[MAX_PATH]; /* place to copy filename so sender can be released */
1070 struct enc_buffer_event_data data;
1071 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
1072 new filename */
1073 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
1074 stream */
1075 bool did_flush = false; /* did a flush occurr? */
1077 int get_chunk_index(struct enc_chunk_hdr *chunk)
1079 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
1082 struct enc_chunk_hdr * get_prev_chunk(int index)
1084 #ifdef PCMREC_PARANOID
1085 int index_last = index;
1086 #endif
1087 DEC_ENC_INDEX(index);
1088 return GET_ENC_CHUNK(index);
1091 if (filename)
1092 strncpy(path, filename, MAX_PATH);
1093 queue_reply(&pcmrec_queue, 0); /* We have all we need */
1095 data.pre_chunk = NULL;
1096 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1098 /* end chunk */
1099 if (flags & CHUNKF_END_FILE)
1101 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1103 if (data.chunk->flags & CHUNKF_START_FILE)
1105 /* cannot start and end on same unprocessed chunk */
1106 logf("file end on start");
1107 flags &= ~CHUNKF_END_FILE;
1109 else if (enc_rd_index == enc_wr_index)
1111 /* all data flushed but file not ended - chunk will be left
1112 empty */
1113 logf("end on dead end");
1114 data.chunk->flags = 0;
1115 data.chunk->enc_size = 0;
1116 data.chunk->num_pcm = 0;
1117 data.chunk->enc_data = NULL;
1118 INC_ENC_INDEX(enc_wr_index);
1119 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1121 else
1123 struct enc_chunk_hdr *last = get_prev_chunk(enc_wr_index);
1125 if (last->flags & CHUNKF_END_FILE)
1127 /* end already processed and marked - can't end twice */
1128 logf("file end again");
1129 flags &= ~CHUNKF_END_FILE;
1134 /* start chunk */
1135 if (flags & CHUNKF_START_FILE)
1137 bool pre = flags & CHUNKF_PRERECORD;
1139 if (pre)
1141 logf("stream prerecord start");
1142 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1143 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1145 else
1147 logf("stream normal start");
1148 start = data.chunk;
1149 start->flags &= CHUNKF_START_FILE;
1152 /* if encoder hasn't yet processed the last start - abort the start
1153 of the previous file queued or else it will be empty and invalid */
1154 if (start->flags & CHUNKF_START_FILE)
1156 logf("replacing fnq tail: %s", filename);
1157 fnq_add_fn = pcmrec_fnq_replace_tail;
1159 else
1161 logf("adding filename: %s", filename);
1162 fnq_add_fn = pcmrec_fnq_add_filename;
1166 data.flags = flags;
1167 pcmrec_context = true; /* switch encoder context */
1168 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1169 pcmrec_context = false; /* switch back */
1171 if (flags & CHUNKF_END_FILE)
1173 int i = get_chunk_index(data.chunk);
1174 get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1177 if (start)
1179 if (!(flags & CHUNKF_PRERECORD))
1181 /* get stats on data added to start - sort of a prerecord
1182 operation */
1183 int i = get_chunk_index(data.chunk);
1184 #ifdef PCMREC_PARANOID
1185 int i_last = i;
1186 #endif
1187 struct enc_chunk_hdr *chunk = data.chunk;
1189 logf("start data: %d %d", i, enc_wr_index);
1191 num_rec_bytes = 0;
1192 num_rec_samples = 0;
1194 while (i != enc_wr_index)
1196 num_rec_bytes += chunk->enc_size;
1197 num_rec_samples += chunk->num_pcm;
1198 INC_ENC_INDEX(i);
1199 chunk = GET_ENC_CHUNK(i);
1202 start->flags &= ~CHUNKF_START_FILE;
1203 start = data.chunk;
1206 start->flags |= CHUNKF_START_FILE;
1208 /* flush all pending files out if full and adding */
1209 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1211 logf("fnq full");
1212 pcmrec_flush(PCMREC_FLUSH_ALL);
1213 did_flush = true;
1216 fnq_add_fn(path);
1219 /* Make sure to complete any interrupted high watermark */
1220 if (!did_flush)
1221 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1222 } /* pcmrec_new_stream */
1224 /** event handlers for pcmrec thread */
1226 /* PCMREC_INIT */
1227 static void pcmrec_init(void)
1229 unsigned char *buffer;
1231 pcmrec_close_file(&rec_fdata.rec_file);
1232 rec_fdata.rec_file = -1;
1234 /* warings and errors */
1235 warnings =
1236 errors = 0;
1238 /* pcm FIFO */
1239 dma_lock = true;
1240 SET_PCM_POS(pcm_rd_pos, 0);
1241 SET_PCM_POS(dma_wr_pos, 0);
1242 pcm_enc_pos = 0;
1244 /* encoder FIFO */
1245 SET_ENC_INDEX(enc_wr_index, 0);
1246 SET_ENC_INDEX(enc_rd_index, 0);
1248 /* filename queue */
1249 fnq_rd_pos = 0;
1250 fnq_wr_pos = 0;
1252 /* stats */
1253 num_rec_bytes = 0;
1254 num_rec_samples = 0;
1255 #if 0
1256 accum_rec_bytes = 0;
1257 accum_pcm_samples = 0;
1258 #endif
1260 pre_record_ticks = 0;
1262 is_recording = false;
1263 is_paused = false;
1265 buffer = audio_get_recording_buffer(&rec_buffer_size);
1267 /* Line align pcm_buffer 2^4=16 bytes */
1268 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1269 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1270 PCM_MAX_FEED_SIZE, 2);
1271 /* Adjust available buffer for possible align advancement */
1272 rec_buffer_size -= pcm_buffer - buffer;
1274 pcm_init_recording();
1275 } /* pcmrec_init */
1277 /* PCMREC_CLOSE */
1278 static void pcmrec_close(void)
1280 dma_lock = true;
1281 pre_record_ticks = 0; /* Can't be prerecording any more */
1282 warnings = 0;
1283 pcm_close_recording();
1284 reset_hardware();
1285 audio_remove_encoder();
1286 } /* pcmrec_close */
1288 /* PCMREC_OPTIONS */
1289 static void pcmrec_set_recording_options(
1290 struct audio_recording_options *options)
1292 /* stop DMA transfer */
1293 dma_lock = true;
1294 pcm_stop_recording();
1296 rec_frequency = options->rec_frequency;
1297 rec_source = options->rec_source;
1298 num_channels = options->rec_channels == 1 ? 1 : 2;
1299 pre_record_ticks = options->rec_prerecord_time * HZ;
1300 enc_config = options->enc_config;
1301 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1303 #ifdef HAVE_SPDIF_IN
1304 if (rec_source == AUDIO_SRC_SPDIF)
1306 /* must measure SPDIF sample rate before configuring codecs */
1307 unsigned long sr = spdif_measure_frequency();
1308 /* round to master list for SPDIF rate */
1309 int index = round_value_to_list32(sr, audio_master_sampr_list,
1310 SAMPR_NUM_FREQ, false);
1311 sample_rate = audio_master_sampr_list[index];
1312 /* round to HW playback rates for monitoring */
1313 index = round_value_to_list32(sr, hw_freq_sampr,
1314 HW_NUM_FREQ, false);
1315 pcm_set_frequency(hw_freq_sampr[index]);
1316 /* encoders with a limited number of rates do their own rounding */
1318 else
1319 #endif
1321 /* set sample rate from frequency selection */
1322 sample_rate = rec_freq_sampr[rec_frequency];
1323 pcm_set_frequency(sample_rate);
1326 /* set monitoring */
1327 audio_set_output_source(rec_source);
1329 /* apply hardware setting to start monitoring now */
1330 pcm_apply_settings();
1332 queue_reply(&pcmrec_queue, 0); /* Release sender */
1334 if (audio_load_encoder(enc_config.afmt))
1336 /* start DMA transfer */
1337 dma_lock = pre_record_ticks == 0;
1338 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1339 PCM_CHUNK_SIZE);
1341 else
1343 logf("set rec opt: enc load failed");
1344 errors |= PCMREC_E_LOAD_ENCODER;
1346 } /* pcmrec_set_recording_options */
1348 /* PCMREC_RECORD - start recording (not gapless)
1349 or split stream (gapless) */
1350 static void pcmrec_record(const char *filename)
1352 unsigned long pre_sample_ticks;
1353 int rd_start;
1354 unsigned long flags;
1355 int pre_index;
1357 logf("pcmrec_record: %s", filename);
1359 /* reset stats */
1360 num_rec_bytes = 0;
1361 num_rec_samples = 0;
1363 if (!is_recording)
1365 #if 0
1366 accum_rec_bytes = 0;
1367 accum_pcm_samples = 0;
1368 #endif
1369 warnings = 0; /* reset warnings */
1371 rd_start = enc_wr_index;
1372 pre_sample_ticks = 0;
1374 pcmrec_refresh_watermarks();
1376 if (pre_record_ticks)
1378 int i = rd_start;
1379 #ifdef PCMREC_PARANOID
1380 int i_last = i;
1381 #endif
1382 /* calculate number of available chunks */
1383 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1384 enc_num_chunks) % enc_num_chunks;
1385 /* overflow at 974 seconds of prerecording at 44.1kHz */
1386 unsigned long pre_record_sample_ticks =
1387 enc_sample_rate*pre_record_ticks;
1388 int pre_chunks = 0; /* Counter to limit prerecorded time to
1389 prevent flood state at outset */
1391 logf("pre-st: %ld", pre_record_sample_ticks);
1393 /* Get exact measure of recorded data as number of samples aren't
1394 nescessarily going to be the max for each chunk */
1395 for (; avail_pre_chunks-- > 0;)
1397 struct enc_chunk_hdr *chunk;
1398 unsigned long chunk_sample_ticks;
1400 DEC_ENC_INDEX(i);
1402 chunk = GET_ENC_CHUNK(i);
1404 /* must have data to be counted */
1405 if (chunk->enc_data == NULL)
1406 continue;
1408 chunk_sample_ticks = chunk->num_pcm*HZ;
1410 rd_start = i;
1411 pre_sample_ticks += chunk_sample_ticks;
1412 num_rec_bytes += chunk->enc_size;
1413 num_rec_samples += chunk->num_pcm;
1414 pre_chunks++;
1416 /* stop here if enough already */
1417 if (pre_chunks >= high_watermark ||
1418 pre_sample_ticks >= pre_record_sample_ticks)
1420 logf("pre-chks: %d", pre_chunks);
1421 break;
1425 #if 0
1426 accum_rec_bytes = num_rec_bytes;
1427 accum_pcm_samples = num_rec_samples;
1428 #endif
1431 SET_ENC_INDEX(enc_rd_index, rd_start);
1433 /* filename queue should be empty */
1434 if (!pcmrec_fnq_is_empty())
1436 logf("fnq: not empty!");
1437 pcmrec_fnq_set_empty();
1440 flags = CHUNKF_START_FILE;
1441 if (pre_sample_ticks > 0)
1442 flags |= CHUNKF_PRERECORD;
1444 pre_index = enc_rd_index;
1446 dma_lock = false;
1447 is_paused = false;
1448 is_recording = true;
1450 else
1452 /* already recording, just split the stream */
1453 logf("inserting split");
1454 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1455 pre_index = 0;
1458 pcmrec_new_stream(filename, flags, pre_index);
1459 logf("pcmrec_record done");
1460 } /* pcmrec_record */
1462 /* PCMREC_STOP */
1463 static void pcmrec_stop(void)
1465 logf("pcmrec_stop");
1467 if (is_recording)
1469 dma_lock = true; /* lock dma write position */
1470 queue_reply(&pcmrec_queue, 0);
1472 /* flush all available data first to avoid overflow while waiting
1473 for encoding to finish */
1474 pcmrec_flush(PCMREC_FLUSH_ALL);
1476 /* wait for encoder to finish remaining data */
1477 while (errors == 0 && !pcm_buffer_empty)
1478 yield();
1480 /* end stream at last data */
1481 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1483 /* flush anything else encoder added */
1484 pcmrec_flush(PCMREC_FLUSH_ALL);
1486 /* remove any pending file start not yet processed - should be at
1487 most one at enc_wr_index */
1488 pcmrec_fnq_get_filename(NULL);
1489 /* encoder should abort any chunk it was in midst of processing */
1490 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1492 /* filename queue should be empty */
1493 if (!pcmrec_fnq_is_empty())
1495 logf("fnq: not empty!");
1496 pcmrec_fnq_set_empty();
1499 /* be absolutely sure the file is closed */
1500 if (errors != 0)
1501 pcmrec_close_file(&rec_fdata.rec_file);
1502 rec_fdata.rec_file = -1;
1504 is_recording = false;
1505 is_paused = false;
1506 dma_lock = pre_record_ticks == 0;
1508 else
1510 logf("not recording");
1513 logf("pcmrec_stop done");
1514 } /* pcmrec_stop */
1516 /* PCMREC_PAUSE */
1517 static void pcmrec_pause(void)
1519 logf("pcmrec_pause");
1521 if (!is_recording)
1523 logf("not recording");
1525 else if (is_paused)
1527 logf("already paused");
1529 else
1531 dma_lock = true;
1532 is_paused = true;
1535 logf("pcmrec_pause done");
1536 } /* pcmrec_pause */
1538 /* PCMREC_RESUME */
1539 static void pcmrec_resume(void)
1541 logf("pcmrec_resume");
1543 if (!is_recording)
1545 logf("not recording");
1547 else if (!is_paused)
1549 logf("not paused");
1551 else
1553 is_paused = false;
1554 is_recording = true;
1555 dma_lock = false;
1558 logf("pcmrec_resume done");
1559 } /* pcmrec_resume */
1561 static void pcmrec_thread(void) __attribute__((noreturn));
1562 static void pcmrec_thread(void)
1564 struct event ev;
1566 logf("thread pcmrec start");
1568 void clear_flush_interrupt(void)
1570 if (--flush_interrupts < 0)
1571 flush_interrupts = 0;
1574 while(1)
1576 if (is_recording)
1578 /* Poll periodically to flush data */
1579 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1581 if (ev.id == SYS_TIMEOUT)
1583 /* Messages that interrupt this will complete it */
1584 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1585 PCMREC_FLUSH_INTERRUPTABLE);
1586 continue;
1589 else
1591 /* Not doing anything - sit and wait for commands */
1592 queue_wait(&pcmrec_queue, &ev);
1595 switch (ev.id)
1597 case PCMREC_INIT:
1598 pcmrec_init();
1599 break;
1601 case PCMREC_CLOSE:
1602 pcmrec_close();
1603 break;
1605 case PCMREC_OPTIONS:
1606 pcmrec_set_recording_options(
1607 (struct audio_recording_options *)ev.data);
1608 break;
1610 case PCMREC_RECORD:
1611 clear_flush_interrupt();
1612 pcmrec_record((const char *)ev.data);
1613 break;
1615 case PCMREC_STOP:
1616 clear_flush_interrupt();
1617 pcmrec_stop();
1618 break;
1620 case PCMREC_PAUSE:
1621 clear_flush_interrupt();
1622 pcmrec_pause();
1623 break;
1625 case PCMREC_RESUME:
1626 pcmrec_resume();
1627 break;
1628 #if 0
1629 case PCMREC_FLUSH_NUM:
1630 pcmrec_flush((unsigned)ev.data);
1631 break;
1632 #endif
1633 case SYS_USB_CONNECTED:
1634 if (is_recording)
1635 break;
1636 pcmrec_close();
1637 reset_hardware();
1638 /* Be sure other threads are released if waiting */
1639 queue_clear(&pcmrec_queue);
1640 flush_interrupts = 0;
1641 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1642 usb_wait_for_disconnect(&pcmrec_queue);
1643 break;
1644 } /* end switch */
1646 queue_reply(&pcmrec_queue, 0);
1647 } /* end while */
1648 } /* pcmrec_thread */
1650 /****************************************************************************/
1651 /* */
1652 /* following functions will be called by the encoder codec */
1653 /* in a free-threaded manner */
1654 /* */
1655 /****************************************************************************/
1657 /* pass the encoder settings to the encoder */
1658 void enc_get_inputs(struct enc_inputs *inputs)
1660 inputs->sample_rate = sample_rate;
1661 inputs->num_channels = num_channels;
1662 inputs->config = &enc_config;
1663 } /* enc_get_inputs */
1665 /* set the encoder dimensions (called by encoder codec at initialization and
1666 termination) */
1667 void enc_set_parameters(struct enc_parameters *params)
1669 size_t bufsize, resbytes;
1671 logf("enc_set_parameters");
1673 if (!params)
1675 logf("reset");
1676 /* Encoder is terminating */
1677 memset(&enc_config, 0, sizeof (enc_config));
1678 enc_sample_rate = 0;
1679 return;
1682 enc_sample_rate = params->enc_sample_rate;
1683 logf("enc sampr:%d", enc_sample_rate);
1685 SET_PCM_POS(pcm_rd_pos, dma_wr_pos);
1686 pcm_enc_pos = pcm_rd_pos;
1688 enc_config.afmt = params->afmt;
1689 /* addition of the header is always implied - chunk size 4-byte aligned */
1690 enc_chunk_size =
1691 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1692 enc_events_callback = params->events_callback;
1694 logf("chunk size:%d", enc_chunk_size);
1696 /*** Configure the buffers ***/
1698 /* Layout of recording buffer:
1699 * [ax] = possible alignment x multiple
1700 * [sx] = possible size alignment of x multiple
1701 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1702 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1704 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1705 logf("resbytes:%d", resbytes);
1707 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1708 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1709 #ifdef PCMREC_PARANOID
1710 - sizeof (*wrap_id_p)
1711 #endif
1714 enc_num_chunks = bufsize / enc_chunk_size;
1715 logf("num chunks:%d", enc_num_chunks);
1717 /* get real amount used by encoder chunks */
1718 bufsize = enc_num_chunks*enc_chunk_size;
1719 logf("enc size:%d", bufsize);
1721 #ifdef PCMREC_PARANOID
1722 /* add magic at wraparound */
1723 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1724 bufsize += sizeof (*wrap_id_p);
1725 *wrap_id_p = ENC_CHUNK_MAGIC;
1726 #endif /* PCMREC_PARANOID */
1728 /** set OUT parameters **/
1729 params->enc_buffer = enc_buffer;
1730 params->buf_chunk_size = enc_chunk_size;
1731 params->num_chunks = enc_num_chunks;
1733 /* calculate reserve buffer start and return pointer to encoder */
1734 params->reserve_buffer = NULL;
1735 if (resbytes > 0)
1737 params->reserve_buffer = enc_buffer + bufsize;
1738 bufsize += resbytes;
1741 /* place filename queue at end of buffer using up whatever remains */
1742 fnq_rd_pos = 0; /* reset */
1743 fnq_wr_pos = 0; /* reset */
1744 fn_queue = enc_buffer + bufsize;
1745 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1746 fnq_size /= MAX_PATH;
1747 if (fnq_size > FNQ_MAX_NUM_PATHS)
1748 fnq_size = FNQ_MAX_NUM_PATHS;
1749 fnq_size *= MAX_PATH;
1750 logf("fnq files: %d", fnq_size / MAX_PATH);
1752 #if 0
1753 logf("ab :%08X", (unsigned long)audiobuf);
1754 logf("pcm:%08X", (unsigned long)pcm_buffer);
1755 logf("enc:%08X", (unsigned long)enc_buffer);
1756 logf("res:%08X", (unsigned long)params->reserve_buffer);
1757 #ifdef PCMREC_PARANOID
1758 logf("wip:%08X", (unsigned long)wrap_id_p);
1759 #endif
1760 logf("fnq:%08X", (unsigned long)fn_queue);
1761 logf("end:%08X", (unsigned long)fn_queue + fnq_size);
1762 logf("abe:%08X", (unsigned long)audiobufend);
1763 #endif
1765 /* init all chunk headers and reset indexes */
1766 SET_ENC_INDEX(enc_rd_index, 0);
1767 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1769 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1770 #ifdef PCMREC_PARANOID
1771 chunk->id = ENC_CHUNK_MAGIC;
1772 #endif
1773 chunk->flags = 0;
1776 logf("enc_set_parameters done");
1777 } /* enc_set_parameters */
1779 /* return encoder chunk at current write position -
1780 NOTE: can be called by pcmrec thread when splitting streams */
1781 struct enc_chunk_hdr * enc_get_chunk(void)
1783 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1785 #ifdef PCMREC_PARANOID
1786 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1788 errors |= PCMREC_E_CHUNK_OVF;
1789 logf("finish chk ovf: %d", enc_wr_index);
1791 #endif
1793 chunk->flags &= CHUNKF_START_FILE;
1795 if (!is_recording)
1796 chunk->flags |= CHUNKF_PRERECORD;
1798 return chunk;
1799 } /* enc_get_chunk */
1801 /* releases the current chunk into the available chunks -
1802 NOTE: can be called by pcmrec thread when splitting streams */
1803 void enc_finish_chunk(void)
1805 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1807 if ((long)chunk->flags < 0)
1809 /* encoder set error flag */
1810 errors |= PCMREC_E_ENCODER;
1811 logf("finish chk enc error");
1814 PARANOID_CHUNK_CHECK(chunk);
1816 /* advance enc_wr_index to the next encoder chunk */
1817 INC_ENC_INDEX(enc_wr_index);
1819 if (enc_rd_index != enc_wr_index)
1821 num_rec_bytes += chunk->enc_size;
1822 num_rec_samples += chunk->num_pcm;
1823 #if 0
1824 accum_rec_bytes += chunk->enc_size;
1825 accum_pcm_samples += chunk->num_pcm;
1826 #endif
1828 else if (is_recording) /* buffer full */
1830 /* keep current position and put up warning flag */
1831 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1832 logf("enc_buffer ovf");
1833 DEC_ENC_INDEX(enc_wr_index);
1834 if (pcmrec_context)
1836 /* if stream splitting, keep this out of circulation and
1837 flush a small number, then readd - cannot risk losing
1838 stream markers */
1839 logf("mini flush");
1840 pcmrec_flush(PCMREC_FLUSH_MINI);
1841 INC_ENC_INDEX(enc_wr_index);
1844 else
1846 /* advance enc_rd_index for prerecording */
1847 INC_ENC_INDEX(enc_rd_index);
1849 } /* enc_finish_chunk */
1851 /* checks near empty state on pcm input buffer */
1852 int enc_pcm_buf_near_empty(void)
1854 /* less than 1sec raw data? => unboost encoder */
1855 int wp = dma_wr_pos;
1856 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1857 return avail < (sample_rate << 2) ? 1 : 0;
1858 } /* enc_pcm_buf_near_empty */
1860 /* passes a pointer to next chunk of unprocessed wav data */
1861 /* TODO: this really should give the actual size returned */
1862 unsigned char * enc_get_pcm_data(size_t size)
1864 int wp = dma_wr_pos;
1865 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1867 /* limit the requested pcm data size */
1868 if (size > PCM_MAX_FEED_SIZE)
1869 size = PCM_MAX_FEED_SIZE;
1871 if (avail >= size)
1873 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1874 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1876 pcm_enc_pos = pcm_rd_pos;
1878 SET_PCM_POS(pcm_rd_pos, next_pos);
1880 /* ptr must point to continous data at wraparound position */
1881 if ((size_t)pcm_rd_pos < size)
1882 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1883 pcm_buffer, pcm_rd_pos);
1885 pcm_buffer_empty = false;
1886 return ptr;
1889 /* not enough data available - encoder should idle */
1890 pcm_buffer_empty = true;
1891 return NULL;
1892 } /* enc_get_pcm_data */
1894 /* puts some pcm data back in the queue */
1895 size_t enc_unget_pcm_data(size_t size)
1897 int wp = dma_wr_pos;
1898 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1899 2*PCM_CHUNK_SIZE;
1901 /* allow one interrupt to occur during this call and not have the
1902 new read position inside the DMA destination chunk */
1903 if ((ssize_t)old_avail > 0)
1905 /* limit size to amount of old data remaining */
1906 if (size > old_avail)
1907 size = old_avail;
1909 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1910 SET_PCM_POS(pcm_rd_pos, pcm_enc_pos);
1912 return size;
1915 return 0;
1916 } /* enc_unget_pcm_data */
1918 /** Low level pcm recording apis **/
1920 /****************************************************************************
1921 * Functions that do not require targeted implementation but only a targeted
1922 * interface
1924 void pcm_record_data(pcm_more_callback_type2 more_ready,
1925 void *start, size_t size)
1927 if (!(start && size))
1928 return;
1930 pcm_callback_more_ready = more_ready;
1931 pcm_rec_dma_start(start, size);
1932 } /* pcm_record_data */
1934 void pcm_stop_recording(void)
1936 if (pcm_recording)
1937 pcm_rec_dma_stop();
1938 } /* pcm_stop_recording */