Enable recording on clipv1/m200v4/c200v2
[kugel-rb.git] / apps / recorder / pcm_record.c
blobe5a51076034180e29dbfc13e88609675ad2775a2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "pcm_record.h"
22 #include "system.h"
23 #include "kernel.h"
24 #include "logf.h"
25 #include "thread.h"
26 #include <string.h>
27 #include "storage.h"
28 #include "usb.h"
29 #include "buffer.h"
30 #include "general.h"
31 #include "audio.h"
32 #include "sound.h"
33 #include "metadata.h"
34 #ifdef HAVE_SPDIF_IN
35 #include "spdif.h"
36 #endif
38 /***************************************************************************/
40 extern unsigned int codec_thread_id;
42 /** General recording state **/
43 static bool is_recording; /* We are recording */
44 static bool is_paused; /* We have paused */
45 static unsigned long errors; /* An error has occured */
46 static unsigned long warnings; /* Warning */
47 static int flush_interrupts = 0; /* Number of messages queued that
48 should interrupt a flush in
49 progress -
50 for a safety net and a prompt
51 response to stop, split and pause
52 requests -
53 only interrupts a flush initiated
54 by pcmrec_flush(0) */
56 /* Utility functions for setting/clearing flushing interrupt flag */
57 static inline void flush_interrupt(void)
59 flush_interrupts++;
60 logf("flush int: %d", flush_interrupts);
63 static inline void clear_flush_interrupt(void)
65 if (--flush_interrupts < 0)
66 flush_interrupts = 0;
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 int rec_mono_mode; /* how mono is created */
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_get_chunk(); get a ptr to next enc chunk
116 7.encoder: <process enc chunk> compress and store data to enc chunk
117 8.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 9.encoder: repeat 4. to 8.
124 A.pcmrec: enc_events_callback(); called for certain events
126 (*) Optional step
127 ****************************************************************************/
129 /** buffer parameters where incoming PCM data is placed **/
130 #if MEM <= 2
131 #define PCM_NUM_CHUNKS 16 /* Power of 2 */
132 #else
133 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
134 #endif
135 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
136 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
138 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
139 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
140 #define INC_ENC_INDEX(index) \
141 { if (++index >= enc_num_chunks) index = 0; }
142 #define DEC_ENC_INDEX(index) \
143 { if (--index < 0) index = enc_num_chunks - 1; }
145 static size_t rec_buffer_size; /* size of available buffer */
146 static unsigned char *pcm_buffer; /* circular recording buffer */
147 static unsigned char *enc_buffer; /* circular encoding buffer */
148 #ifdef DEBUG
149 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
150 aid to check if the encoder data
151 spilled out of its chunk */
152 #endif /* DEBUG */
153 static volatile int dma_wr_pos; /* current DMA write pos */
154 static int pcm_rd_pos; /* current PCM read pos */
155 static int pcm_enc_pos; /* position encoder is processing */
156 static volatile bool dma_lock; /* lock DMA write position */
157 static int enc_wr_index; /* encoder chunk write index */
158 static int enc_rd_index; /* encoder chunk read index */
159 static int enc_num_chunks; /* number of chunks in ringbuffer */
160 static size_t enc_chunk_size; /* maximum encoder chunk size */
161 static unsigned long enc_sample_rate; /* sample rate used by encoder */
162 static bool pcmrec_context = false; /* called by pcmrec thread? */
163 static bool pcm_buffer_empty; /* all pcm chunks processed? */
165 /** file flushing **/
166 static int low_watermark; /* Low watermark to stop flush */
167 static int high_watermark; /* max chunk limit for data flush */
168 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
169 static int last_storage_spinup_time = -1;/* previous spin time used */
170 #ifdef HAVE_PRIORITY_SCHEDULING
171 static int flood_watermark; /* boost thread priority when here */
172 #endif
174 /* Constants that control watermarks */
175 #define MINI_CHUNKS 10 /* chunk count for mini flush */
176 #ifdef HAVE_PRIORITY_SCHEDULING
177 #define PRIO_SECONDS 10 /* max flush time before priority boost */
178 #endif
179 #if MEM <= 2
180 /* fractions must be integer fractions of 4 because they are evaluated with
181 * X*4*XXX_SECONDS, that way we avoid float calculation */
182 #define LOW_SECONDS 1/4 /* low watermark time till empty */
183 #define PANIC_SECONDS 1/2 /* flood watermark time until full */
184 #define FLUSH_SECONDS 1 /* flush watermark time until full */
185 #elif MEM <= 16
186 #define LOW_SECONDS 1 /* low watermark time till empty */
187 #define PANIC_SECONDS 5 /* flood watermark time until full */
188 #define FLUSH_SECONDS 7 /* flush watermark time until full */
189 #else
190 #define LOW_SECONDS 1 /* low watermark time till empty */
191 #define PANIC_SECONDS 8
192 #define FLUSH_SECONDS 10
193 #endif /* MEM */
195 /** encoder events **/
196 static void (*enc_events_callback)(enum enc_events event, void *data);
198 /** Path queue for files to write **/
199 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
200 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
201 static unsigned char *fn_queue; /* pointer to first filename */
202 static ssize_t fnq_size; /* capacity of queue in bytes */
203 static int fnq_rd_pos; /* current read position */
204 static int fnq_wr_pos; /* current write position */
205 #define FNQ_NEXT(pos) \
206 ({ int p = (pos) + MAX_PATH; \
207 if (p >= fnq_size) \
208 p = 0; \
209 p; })
210 #define FNQ_PREV(pos) \
211 ({ int p = (pos) - MAX_PATH; \
212 if (p < 0) \
213 p = fnq_size - MAX_PATH; \
214 p; })
216 enum
218 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
219 incoming messages - combine
220 with other constants */
221 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
222 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
223 chunks */
224 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
225 reached */
228 /***************************************************************************/
230 static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
231 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
232 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
233 static const char pcmrec_thread_name[] = "pcmrec";
234 static unsigned int pcmrec_thread_id = 0;
236 static void pcmrec_thread(void);
238 enum
240 PCMREC_NULL = 0,
241 PCMREC_INIT, /* enable recording */
242 PCMREC_CLOSE, /* close recording */
243 PCMREC_OPTIONS, /* set recording options */
244 PCMREC_RECORD, /* record a new file */
245 PCMREC_STOP, /* stop the current recording */
246 PCMREC_PAUSE, /* pause the current recording */
247 PCMREC_RESUME, /* resume the current recording */
248 #if 0
249 PCMREC_FLUSH_NUM, /* flush a number of files out */
250 #endif
253 /*******************************************************************/
254 /* Functions that are not executing in the pcmrec_thread first */
255 /*******************************************************************/
257 /* Callback for when more data is ready - called in interrupt context */
258 static int pcm_rec_have_more(int status)
260 if (status < 0)
262 /* some error condition */
263 if (status == DMA_REC_ERROR_DMA)
265 /* Flush recorded data to disk and stop recording */
266 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
267 return -1;
269 /* else try again next transmission */
271 else if (!dma_lock)
273 /* advance write position */
274 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
276 /* set pcm ovf if processing start position is inside current
277 write chunk */
278 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
279 warnings |= PCMREC_W_PCM_BUFFER_OVF;
281 dma_wr_pos = next_pos;
284 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
285 return 0;
286 } /* pcm_rec_have_more */
288 static void reset_hardware(void)
290 /* reset pcm to defaults (playback only) */
291 pcm_set_frequency(HW_SAMPR_DEFAULT);
292 audio_set_output_source(AUDIO_SRC_PLAYBACK);
293 pcm_apply_settings();
296 /** pcm_rec_* group **/
299 * Clear all errors and warnings
301 void pcm_rec_error_clear(void)
303 errors = warnings = 0;
304 } /* pcm_rec_error_clear */
307 * Check mode, errors and warnings
309 unsigned long pcm_rec_status(void)
311 unsigned long ret = 0;
313 if (is_recording)
314 ret |= AUDIO_STATUS_RECORD;
315 else if (pre_record_ticks)
316 ret |= AUDIO_STATUS_PRERECORD;
318 if (is_paused)
319 ret |= AUDIO_STATUS_PAUSE;
321 if (errors)
322 ret |= AUDIO_STATUS_ERROR;
324 if (warnings)
325 ret |= AUDIO_STATUS_WARNING;
327 return ret;
328 } /* pcm_rec_status */
331 * Return warnings that have occured since recording started
333 unsigned long pcm_rec_get_warnings(void)
335 return warnings;
338 #if 0
339 int pcm_rec_current_bitrate(void)
341 if (accum_pcm_samples == 0)
342 return 0;
344 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
345 } /* pcm_rec_current_bitrate */
346 #endif
348 #if 0
349 int pcm_rec_encoder_afmt(void)
351 return enc_config.afmt;
352 } /* pcm_rec_encoder_afmt */
353 #endif
355 #if 0
356 int pcm_rec_rec_format(void)
358 return afmt_rec_format[enc_config.afmt];
359 } /* pcm_rec_rec_format */
360 #endif
362 #ifdef HAVE_SPDIF_IN
363 unsigned long pcm_rec_sample_rate(void)
365 /* Which is better ?? */
366 #if 0
367 return enc_sample_rate;
368 #endif
369 return sample_rate;
370 } /* audio_get_sample_rate */
371 #endif
374 * Creates pcmrec_thread
376 void pcm_rec_init(void)
378 queue_init(&pcmrec_queue, true);
379 pcmrec_thread_id =
380 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
381 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
382 IF_COP(, CPU));
383 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
384 pcmrec_thread_id);
385 } /* pcm_rec_init */
387 /** audio_* group **/
390 * Initializes recording - call before calling any other recording function
392 void audio_init_recording(unsigned int buffer_offset)
394 logf("audio_init_recording");
395 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
396 logf("audio_init_recording done");
397 (void)buffer_offset;
398 } /* audio_init_recording */
401 * Closes recording - call audio_stop_recording first
403 void audio_close_recording(void)
405 logf("audio_close_recording");
406 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
407 logf("audio_close_recording done");
408 } /* audio_close_recording */
411 * Sets recording parameters
413 void audio_set_recording_options(struct audio_recording_options *options)
415 logf("audio_set_recording_options");
416 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
417 logf("audio_set_recording_options done");
418 } /* audio_set_recording_options */
421 * Start recording if not recording or else split
423 void audio_record(const char *filename)
425 logf("audio_record: %s", filename);
426 flush_interrupt();
427 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
428 logf("audio_record_done");
429 } /* audio_record */
432 * audio_record wrapper for API compatibility with HW codec
434 void audio_new_file(const char *filename)
436 audio_record(filename);
437 } /* audio_new_file */
440 * Stop current recording if recording
442 void audio_stop_recording(void)
444 logf("audio_stop_recording");
445 flush_interrupt();
446 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
447 logf("audio_stop_recording done");
448 } /* audio_stop_recording */
451 * Pause current recording
453 void audio_pause_recording(void)
455 logf("audio_pause_recording");
456 flush_interrupt();
457 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
458 logf("audio_pause_recording done");
459 } /* audio_pause_recording */
462 * Resume current recording if paused
464 void audio_resume_recording(void)
466 logf("audio_resume_recording");
467 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
468 logf("audio_resume_recording done");
469 } /* audio_resume_recording */
472 * Note that microphone is mono, only left value is used
473 * See audiohw_set_recvol() for exact ranges.
475 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
478 void audio_set_recording_gain(int left, int right, int type)
480 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
481 audiohw_set_recvol(left, right, type);
482 } /* audio_set_recording_gain */
484 /** Information about current state **/
487 * Return current recorded time in ticks (playback eqivalent time)
489 unsigned long audio_recorded_time(void)
491 if (!is_recording || enc_sample_rate == 0)
492 return 0;
494 /* return actual recorded time a la encoded data even if encoder rate
495 doesn't match the pcm rate */
496 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
497 } /* audio_recorded_time */
500 * Return number of bytes encoded to output
502 unsigned long audio_num_recorded_bytes(void)
504 if (!is_recording)
505 return 0;
507 return num_rec_bytes;
508 } /* audio_num_recorded_bytes */
510 /***************************************************************************/
511 /* */
512 /* Functions that execute in the context of pcmrec_thread */
513 /* */
514 /***************************************************************************/
516 /** Filename Queue **/
518 /* returns true if the queue is empty */
519 static inline bool pcmrec_fnq_is_empty(void)
521 return fnq_rd_pos == fnq_wr_pos;
522 } /* pcmrec_fnq_is_empty */
524 /* empties the filename queue */
525 static inline void pcmrec_fnq_set_empty(void)
527 fnq_rd_pos = fnq_wr_pos;
528 } /* pcmrec_fnq_set_empty */
530 /* returns true if the queue is full */
531 static bool pcmrec_fnq_is_full(void)
533 ssize_t size = fnq_wr_pos - fnq_rd_pos;
534 if (size < 0)
535 size += fnq_size;
537 return size >= fnq_size - MAX_PATH;
538 } /* pcmrec_fnq_is_full */
540 /* queue another filename - will overwrite oldest one if full */
541 static bool pcmrec_fnq_add_filename(const char *filename)
543 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
544 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
546 if (fnq_rd_pos != fnq_wr_pos)
547 return true;
549 /* queue full */
550 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
551 return true;
552 } /* pcmrec_fnq_add_filename */
554 /* replace the last filename added */
555 static bool pcmrec_fnq_replace_tail(const char *filename)
557 int pos;
559 if (pcmrec_fnq_is_empty())
560 return false;
562 pos = FNQ_PREV(fnq_wr_pos);
564 strlcpy(fn_queue + pos, filename, MAX_PATH);
566 return true;
567 } /* pcmrec_fnq_replace_tail */
569 /* pulls the next filename from the queue */
570 static bool pcmrec_fnq_get_filename(char *filename)
572 if (pcmrec_fnq_is_empty())
573 return false;
575 if (filename)
576 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
578 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
579 return true;
580 } /* pcmrec_fnq_get_filename */
582 /* close the file number pointed to by fd_p */
583 static void pcmrec_close_file(int *fd_p)
585 if (*fd_p < 0)
586 return; /* preserve error */
588 if (close(*fd_p) != 0)
589 errors |= PCMREC_E_IO;
591 *fd_p = -1;
592 } /* pcmrec_close_file */
594 /** Data Flushing **/
597 * called after callback to update sizes if codec changed the amount of data
598 * a chunk represents
600 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
601 unsigned long prev_num_pcm)
603 if (rec_fdata.new_enc_size != prev_enc_size)
605 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
606 num_rec_bytes += size_diff;
607 #if 0
608 accum_rec_bytes += size_diff;
609 #endif
612 if (rec_fdata.new_num_pcm != prev_num_pcm)
614 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
615 num_rec_samples += pcm_diff;
616 #if 0
617 accum_pcm_samples += pcm_diff;
618 #endif
620 } /* pcmrec_update_sizes_inl */
622 /* don't need to inline every instance */
623 static void pcmrec_update_sizes(size_t prev_enc_size,
624 unsigned long prev_num_pcm)
626 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
627 } /* pcmrec_update_sizes */
629 static void pcmrec_start_file(void)
631 size_t enc_size = rec_fdata.new_enc_size;
632 unsigned long num_pcm = rec_fdata.new_num_pcm;
633 int curr_rec_file = rec_fdata.rec_file;
634 char filename[MAX_PATH];
636 /* must always pull the filename that matches with this queue */
637 if (!pcmrec_fnq_get_filename(filename))
639 logf("start file: fnq empty");
640 *filename = '\0';
641 errors |= PCMREC_E_FNQ_DESYNC;
643 else if (errors != 0)
645 logf("start file: error already");
647 else if (curr_rec_file >= 0)
649 /* Any previous file should have been closed */
650 logf("start file: file already open");
651 errors |= PCMREC_E_FNQ_DESYNC;
654 if (errors != 0)
655 rec_fdata.chunk->flags |= CHUNKF_ERROR;
657 /* encoder can set error flag here and should increase
658 enc_new_size and pcm_new_size to reflect additional
659 data written if any */
660 rec_fdata.filename = filename;
661 enc_events_callback(ENC_START_FILE, &rec_fdata);
663 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
665 logf("start file: enc error");
666 errors |= PCMREC_E_ENCODER;
669 if (errors != 0)
671 pcmrec_close_file(&curr_rec_file);
672 /* Write no more to this file */
673 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
675 else
677 pcmrec_update_sizes(enc_size, num_pcm);
680 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
681 } /* pcmrec_start_file */
683 static inline void pcmrec_write_chunk(void)
685 size_t enc_size = rec_fdata.new_enc_size;
686 unsigned long num_pcm = rec_fdata.new_num_pcm;
688 if (errors != 0)
689 rec_fdata.chunk->flags |= CHUNKF_ERROR;
691 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
693 if ((long)rec_fdata.chunk->flags >= 0)
695 pcmrec_update_sizes_inl(enc_size, num_pcm);
697 else if (errors == 0)
699 logf("wr chk enc error %lu %lu",
700 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
701 errors |= PCMREC_E_ENCODER;
703 } /* pcmrec_write_chunk */
705 static void pcmrec_end_file(void)
707 /* all data in output buffer for current file will have been
708 written and encoder can now do any nescessary steps to
709 finalize the written file */
710 size_t enc_size = rec_fdata.new_enc_size;
711 unsigned long num_pcm = rec_fdata.new_num_pcm;
713 enc_events_callback(ENC_END_FILE, &rec_fdata);
715 if (errors == 0)
717 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
719 logf("end file: enc error");
720 errors |= PCMREC_E_ENCODER;
722 else
724 pcmrec_update_sizes(enc_size, num_pcm);
728 /* Force file close if error */
729 if (errors != 0)
730 pcmrec_close_file(&rec_fdata.rec_file);
732 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
733 } /* pcmrec_end_file */
736 * Update buffer watermarks with spinup time compensation
738 * All this assumes reasonable data rates, chunk sizes and sufficient
739 * memory for the most part. Some dumb checks are included but perhaps
740 * are pointless since this all will break down at extreme limits that
741 * are currently not applicable to any supported device.
743 static void pcmrec_refresh_watermarks(void)
745 logf("ata spinup: %d", storage_spinup_time());
747 /* set the low mark for when flushing stops if automatic */
748 /* don't change the order in this expression, LOW_SECONDS can be an
749 * integer fraction of 4 */
750 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1))
751 / enc_chunk_size;
752 logf("low wmk: %d", low_watermark);
754 #ifdef HAVE_PRIORITY_SCHEDULING
755 /* panic boost thread priority if 2 seconds of ground is lost -
756 this allows encoder to boost with just under a second of
757 pcm data (if not yet full enough to boost itself)
758 and not falsely trip the alarm. */
759 /* don't change the order in this expression, PANIC_SECONDS can be an
760 * integer fraction of 4 */
761 flood_watermark = enc_num_chunks -
762 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
763 / enc_chunk_size;
765 if (flood_watermark < low_watermark)
767 logf("warning: panic < low");
768 flood_watermark = low_watermark;
771 logf("flood at: %d", flood_watermark);
772 #endif
773 spinup_time = last_storage_spinup_time = storage_spinup_time();
774 #if (CONFIG_STORAGE & STORAGE_ATA)
775 /* write at 8s + st remaining in enc_buffer - range 12s to
776 20s total - default to 3.5s spinup. */
777 if (spinup_time == 0)
778 spinup_time = 35*HZ/10; /* default - cozy */
779 else if (spinup_time < 2*HZ)
780 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
781 else if (spinup_time > 10*HZ)
782 spinup_time = 10*HZ; /* do you have a functioning HD? */
783 #endif
785 /* try to start writing with 10s remaining after disk spinup */
786 high_watermark = enc_num_chunks -
787 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
788 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
790 if (high_watermark < low_watermark)
792 logf("warning: low 'write at' (%d)", high_watermark);
793 high_watermark = low_watermark;
794 low_watermark /= 2;
797 logf("write at: %d", high_watermark);
798 } /* pcmrec_refresh_watermarks */
801 * Process the chunks
803 * This function is called when queue_get_w_tmo times out.
805 * Set flush_num to the number of files to flush to disk or to
806 * a PCMREC_FLUSH_* constant.
808 static void pcmrec_flush(unsigned flush_num)
810 #ifdef HAVE_PRIORITY_SCHEDULING
811 static unsigned long last_flush_tick; /* tick when function returned */
812 unsigned long start_tick; /* When flush started */
813 unsigned long prio_tick; /* Timeout for auto boost */
814 int prio_pcmrec; /* Current thread priority for pcmrec */
815 int prio_codec; /* Current thread priority for codec */
816 #endif
817 int num_ready; /* Number of chunks ready at start */
818 unsigned remaining; /* Number of file starts remaining */
819 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
820 bool interruptable; /* Flush can be interupted */
822 num_ready = enc_wr_index - enc_rd_index;
823 if (num_ready < 0)
824 num_ready += enc_num_chunks;
826 /* save interruptable flag and remove it to get the actual count */
827 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
828 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
830 if (flush_num == 0)
832 if (!is_recording)
833 return;
835 if (storage_spinup_time() != last_storage_spinup_time)
836 pcmrec_refresh_watermarks();
838 /* enough available? no? then leave */
839 if (num_ready < high_watermark)
840 return;
841 } /* endif (flush_num == 0) */
843 #ifdef HAVE_PRIORITY_SCHEDULING
844 start_tick = current_tick;
845 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
847 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
849 /* if we're getting called too much and this isn't forced,
850 boost stat by expiring timeout in advance */
851 logf("too frequent flush");
852 prio_tick = current_tick - 1;
855 prio_pcmrec = -1;
856 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
857 need init */
858 #endif
860 logf("writing:%d(%d):%s%s", num_ready, flush_num,
861 interruptable ? "i" : "",
862 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
864 cpu_boost(true);
866 remaining = flush_num;
867 chunks_flushed = 0;
869 while (num_ready > 0)
871 /* check current number of encoder chunks */
872 int num = enc_wr_index - enc_rd_index;
873 if (num < 0)
874 num += enc_num_chunks;
876 if (num <= low_watermark &&
877 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
879 logf("low data: %d", num);
880 break; /* data remaining is below threshold */
883 if (interruptable && flush_interrupts > 0)
885 logf("int at: %d", num);
886 break; /* interrupted */
889 #ifdef HAVE_PRIORITY_SCHEDULING
890 if (prio_pcmrec == -1 && (num >= flood_watermark ||
891 TIME_AFTER(current_tick, prio_tick)))
893 /* losing ground or holding without progress - boost
894 priority until finished */
895 logf("pcmrec: boost (%s)",
896 num >= flood_watermark ? "num" : "time");
897 prio_pcmrec = thread_set_priority(THREAD_ID_CURRENT,
898 thread_get_priority(THREAD_ID_CURRENT) - 4);
899 prio_codec = thread_set_priority(codec_thread_id,
900 thread_get_priority(codec_thread_id) - 4);
902 #endif
904 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
905 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
906 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
908 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
910 pcmrec_start_file();
911 if (--remaining == 0)
912 num_ready = 0; /* stop on next loop - must write this
913 chunk if it has data */
916 pcmrec_write_chunk();
918 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
919 pcmrec_end_file();
921 INC_ENC_INDEX(enc_rd_index);
923 if (errors != 0)
925 pcmrec_end_file();
926 break;
929 if (flush_num == PCMREC_FLUSH_MINI &&
930 ++chunks_flushed >= MINI_CHUNKS)
932 logf("mini flush break");
933 break;
935 /* no yielding; the file apis called in the codecs do that
936 sufficiently */
937 } /* end while */
939 /* sync file */
940 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
941 errors |= PCMREC_E_IO;
943 cpu_boost(false);
945 #ifdef HAVE_PRIORITY_SCHEDULING
946 if (prio_pcmrec != -1)
948 /* return to original priorities */
949 logf("pcmrec: unboost priority");
950 thread_set_priority(THREAD_ID_CURRENT, prio_pcmrec);
951 thread_set_priority(codec_thread_id, prio_codec);
954 last_flush_tick = current_tick; /* save tick when we left */
955 #endif
957 logf("done");
958 } /* pcmrec_flush */
961 * Marks a new stream in the buffer and gives the encoder a chance for special
962 * handling of transition from one to the next. The encoder may change the
963 * chunk that ends the old stream by requesting more chunks and similiarly for
964 * the new but must always advance the position though the interface. It can
965 * later reject any data it cares to when writing the file but should mark the
966 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
967 * a NULL data pointer without error as well.
969 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
971 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
972 } /* pcmrec_get_chunk_index */
974 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
976 DEC_ENC_INDEX(index);
977 return GET_ENC_CHUNK(index);
978 } /* pcmrec_get_prev_chunk */
980 static void pcmrec_new_stream(const char *filename, /* next file name */
981 unsigned long flags, /* CHUNKF_* flags */
982 int pre_index) /* index for prerecorded data */
984 logf("pcmrec_new_stream");
985 char path[MAX_PATH]; /* place to copy filename so sender can be released */
987 struct enc_buffer_event_data data;
988 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
989 new filename */
990 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
991 stream */
992 bool did_flush = false; /* did a flush occurr? */
994 if (filename)
995 strlcpy(path, filename, MAX_PATH);
996 queue_reply(&pcmrec_queue, 0); /* We have all we need */
998 data.pre_chunk = NULL;
999 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1001 /* end chunk */
1002 if (flags & CHUNKF_END_FILE)
1004 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1006 if (data.chunk->flags & CHUNKF_START_FILE)
1008 /* cannot start and end on same unprocessed chunk */
1009 logf("file end on start");
1010 flags &= ~CHUNKF_END_FILE;
1012 else if (enc_rd_index == enc_wr_index)
1014 /* all data flushed but file not ended - chunk will be left
1015 empty */
1016 logf("end on dead end");
1017 data.chunk->flags = 0;
1018 data.chunk->enc_size = 0;
1019 data.chunk->num_pcm = 0;
1020 data.chunk->enc_data = NULL;
1021 INC_ENC_INDEX(enc_wr_index);
1022 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1024 else
1026 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1028 if (last->flags & CHUNKF_END_FILE)
1030 /* end already processed and marked - can't end twice */
1031 logf("file end again");
1032 flags &= ~CHUNKF_END_FILE;
1037 /* start chunk */
1038 if (flags & CHUNKF_START_FILE)
1040 bool pre = flags & CHUNKF_PRERECORD;
1042 if (pre)
1044 logf("stream prerecord start");
1045 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1046 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1048 else
1050 logf("stream normal start");
1051 start = data.chunk;
1052 start->flags &= CHUNKF_START_FILE;
1055 /* if encoder hasn't yet processed the last start - abort the start
1056 of the previous file queued or else it will be empty and invalid */
1057 if (start->flags & CHUNKF_START_FILE)
1059 logf("replacing fnq tail: %s", filename);
1060 fnq_add_fn = pcmrec_fnq_replace_tail;
1062 else
1064 logf("adding filename: %s", filename);
1065 fnq_add_fn = pcmrec_fnq_add_filename;
1069 data.flags = flags;
1070 pcmrec_context = true; /* switch encoder context */
1071 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1072 pcmrec_context = false; /* switch back */
1074 if (flags & CHUNKF_END_FILE)
1076 int i = pcmrec_get_chunk_index(data.chunk);
1077 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1080 if (start)
1082 if (!(flags & CHUNKF_PRERECORD))
1084 /* get stats on data added to start - sort of a prerecord
1085 operation */
1086 int i = pcmrec_get_chunk_index(data.chunk);
1087 struct enc_chunk_hdr *chunk = data.chunk;
1089 logf("start data: %d %d", i, enc_wr_index);
1091 num_rec_bytes = 0;
1092 num_rec_samples = 0;
1094 while (i != enc_wr_index)
1096 num_rec_bytes += chunk->enc_size;
1097 num_rec_samples += chunk->num_pcm;
1098 INC_ENC_INDEX(i);
1099 chunk = GET_ENC_CHUNK(i);
1102 start->flags &= ~CHUNKF_START_FILE;
1103 start = data.chunk;
1106 start->flags |= CHUNKF_START_FILE;
1108 /* flush all pending files out if full and adding */
1109 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1111 logf("fnq full");
1112 pcmrec_flush(PCMREC_FLUSH_ALL);
1113 did_flush = true;
1116 fnq_add_fn(path);
1119 /* Make sure to complete any interrupted high watermark */
1120 if (!did_flush)
1121 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1122 } /* pcmrec_new_stream */
1124 /** event handlers for pcmrec thread */
1126 /* PCMREC_INIT */
1127 static void pcmrec_init(void)
1129 unsigned char *buffer;
1131 /* warings and errors */
1132 warnings =
1133 errors = 0;
1135 pcmrec_close_file(&rec_fdata.rec_file);
1136 rec_fdata.rec_file = -1;
1138 /* pcm FIFO */
1139 dma_lock = true;
1140 pcm_rd_pos = 0;
1141 dma_wr_pos = 0;
1142 pcm_enc_pos = 0;
1144 /* encoder FIFO */
1145 enc_wr_index = 0;
1146 enc_rd_index = 0;
1148 /* filename queue */
1149 fnq_rd_pos = 0;
1150 fnq_wr_pos = 0;
1152 /* stats */
1153 num_rec_bytes = 0;
1154 num_rec_samples = 0;
1155 #if 0
1156 accum_rec_bytes = 0;
1157 accum_pcm_samples = 0;
1158 #endif
1160 pre_record_ticks = 0;
1162 is_recording = false;
1163 is_paused = false;
1165 buffer = audio_get_recording_buffer(&rec_buffer_size);
1167 /* Line align pcm_buffer 2^5=32 bytes */
1168 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1169 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1170 PCM_MAX_FEED_SIZE, 2);
1171 /* Adjust available buffer for possible align advancement */
1172 rec_buffer_size -= pcm_buffer - buffer;
1174 pcm_init_recording();
1175 } /* pcmrec_init */
1177 /* PCMREC_CLOSE */
1178 static void pcmrec_close(void)
1180 dma_lock = true;
1181 pre_record_ticks = 0; /* Can't be prerecording any more */
1182 warnings = 0;
1183 pcm_close_recording();
1184 reset_hardware();
1185 audio_remove_encoder();
1186 } /* pcmrec_close */
1188 /* PCMREC_OPTIONS */
1189 static void pcmrec_set_recording_options(
1190 struct audio_recording_options *options)
1192 /* stop DMA transfer */
1193 dma_lock = true;
1194 pcm_stop_recording();
1196 rec_frequency = options->rec_frequency;
1197 rec_source = options->rec_source;
1198 num_channels = options->rec_channels == 1 ? 1 : 2;
1199 rec_mono_mode = options->rec_mono_mode;
1200 pre_record_ticks = options->rec_prerecord_time * HZ;
1201 enc_config = options->enc_config;
1202 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1204 #ifdef HAVE_SPDIF_IN
1205 if (rec_source == AUDIO_SRC_SPDIF)
1207 /* must measure SPDIF sample rate before configuring codecs */
1208 unsigned long sr = spdif_measure_frequency();
1209 /* round to master list for SPDIF rate */
1210 int index = round_value_to_list32(sr, audio_master_sampr_list,
1211 SAMPR_NUM_FREQ, false);
1212 sample_rate = audio_master_sampr_list[index];
1213 /* round to HW playback rates for monitoring */
1214 index = round_value_to_list32(sr, hw_freq_sampr,
1215 HW_NUM_FREQ, false);
1216 pcm_set_frequency(hw_freq_sampr[index]);
1217 /* encoders with a limited number of rates do their own rounding */
1219 else
1220 #endif
1222 /* set sample rate from frequency selection */
1223 sample_rate = rec_freq_sampr[rec_frequency];
1224 pcm_set_frequency(sample_rate);
1227 /* set monitoring */
1228 audio_set_output_source(rec_source);
1230 /* apply hardware setting to start monitoring now */
1231 pcm_apply_settings();
1233 queue_reply(&pcmrec_queue, 0); /* Release sender */
1235 if (audio_load_encoder(enc_config.afmt))
1237 /* start DMA transfer */
1238 dma_lock = pre_record_ticks == 0;
1239 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1240 PCM_CHUNK_SIZE);
1242 else
1244 logf("set rec opt: enc load failed");
1245 errors |= PCMREC_E_LOAD_ENCODER;
1247 } /* pcmrec_set_recording_options */
1249 /* PCMREC_RECORD - start recording (not gapless)
1250 or split stream (gapless) */
1251 static void pcmrec_record(const char *filename)
1253 unsigned long pre_sample_ticks;
1254 int rd_start;
1255 unsigned long flags;
1256 int pre_index;
1258 logf("pcmrec_record: %s", filename);
1260 /* reset stats */
1261 num_rec_bytes = 0;
1262 num_rec_samples = 0;
1264 if (!is_recording)
1266 #if 0
1267 accum_rec_bytes = 0;
1268 accum_pcm_samples = 0;
1269 #endif
1270 warnings = 0; /* reset warnings */
1272 rd_start = enc_wr_index;
1273 pre_sample_ticks = 0;
1275 pcmrec_refresh_watermarks();
1277 if (pre_record_ticks)
1279 int i = rd_start;
1280 /* calculate number of available chunks */
1281 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1282 enc_num_chunks) % enc_num_chunks;
1283 /* overflow at 974 seconds of prerecording at 44.1kHz */
1284 unsigned long pre_record_sample_ticks =
1285 enc_sample_rate*pre_record_ticks;
1286 int pre_chunks = 0; /* Counter to limit prerecorded time to
1287 prevent flood state at outset */
1289 logf("pre-st: %ld", pre_record_sample_ticks);
1291 /* Get exact measure of recorded data as number of samples aren't
1292 nescessarily going to be the max for each chunk */
1293 for (; avail_pre_chunks-- > 0;)
1295 struct enc_chunk_hdr *chunk;
1296 unsigned long chunk_sample_ticks;
1298 DEC_ENC_INDEX(i);
1300 chunk = GET_ENC_CHUNK(i);
1302 /* must have data to be counted */
1303 if (chunk->enc_data == NULL)
1304 continue;
1306 chunk_sample_ticks = chunk->num_pcm*HZ;
1308 rd_start = i;
1309 pre_sample_ticks += chunk_sample_ticks;
1310 num_rec_bytes += chunk->enc_size;
1311 num_rec_samples += chunk->num_pcm;
1312 pre_chunks++;
1314 /* stop here if enough already */
1315 if (pre_chunks >= high_watermark ||
1316 pre_sample_ticks >= pre_record_sample_ticks)
1318 logf("pre-chks: %d", pre_chunks);
1319 break;
1323 #if 0
1324 accum_rec_bytes = num_rec_bytes;
1325 accum_pcm_samples = num_rec_samples;
1326 #endif
1329 enc_rd_index = rd_start;
1331 /* filename queue should be empty */
1332 if (!pcmrec_fnq_is_empty())
1334 logf("fnq: not empty!");
1335 pcmrec_fnq_set_empty();
1338 flags = CHUNKF_START_FILE;
1339 if (pre_sample_ticks > 0)
1340 flags |= CHUNKF_PRERECORD;
1342 pre_index = enc_rd_index;
1344 dma_lock = false;
1345 is_paused = false;
1346 is_recording = true;
1348 else
1350 /* already recording, just split the stream */
1351 logf("inserting split");
1352 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1353 pre_index = 0;
1356 pcmrec_new_stream(filename, flags, pre_index);
1357 logf("pcmrec_record done");
1358 } /* pcmrec_record */
1360 /* PCMREC_STOP */
1361 static void pcmrec_stop(void)
1363 logf("pcmrec_stop");
1365 if (is_recording)
1367 dma_lock = true; /* lock dma write position */
1369 /* flush all available data first to avoid overflow while waiting
1370 for encoding to finish */
1371 pcmrec_flush(PCMREC_FLUSH_ALL);
1373 /* wait for encoder to finish remaining data */
1374 while (errors == 0 && !pcm_buffer_empty)
1375 yield();
1377 /* end stream at last data */
1378 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1380 /* flush anything else encoder added */
1381 pcmrec_flush(PCMREC_FLUSH_ALL);
1383 /* remove any pending file start not yet processed - should be at
1384 most one at enc_wr_index */
1385 pcmrec_fnq_get_filename(NULL);
1386 /* encoder should abort any chunk it was in midst of processing */
1387 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1389 /* filename queue should be empty */
1390 if (!pcmrec_fnq_is_empty())
1392 logf("fnq: not empty!");
1393 pcmrec_fnq_set_empty();
1396 /* be absolutely sure the file is closed */
1397 if (errors != 0)
1398 pcmrec_close_file(&rec_fdata.rec_file);
1399 rec_fdata.rec_file = -1;
1401 is_recording = false;
1402 is_paused = false;
1403 dma_lock = pre_record_ticks == 0;
1405 else
1407 logf("not recording");
1410 logf("pcmrec_stop done");
1411 } /* pcmrec_stop */
1413 /* PCMREC_PAUSE */
1414 static void pcmrec_pause(void)
1416 logf("pcmrec_pause");
1418 if (!is_recording)
1420 logf("not recording");
1422 else if (is_paused)
1424 logf("already paused");
1426 else
1428 dma_lock = true;
1429 is_paused = true;
1432 logf("pcmrec_pause done");
1433 } /* pcmrec_pause */
1435 /* PCMREC_RESUME */
1436 static void pcmrec_resume(void)
1438 logf("pcmrec_resume");
1440 if (!is_recording)
1442 logf("not recording");
1444 else if (!is_paused)
1446 logf("not paused");
1448 else
1450 is_paused = false;
1451 is_recording = true;
1452 dma_lock = false;
1455 logf("pcmrec_resume done");
1456 } /* pcmrec_resume */
1458 static void pcmrec_thread(void) __attribute__((noreturn));
1459 static void pcmrec_thread(void)
1461 struct queue_event ev;
1463 logf("thread pcmrec start");
1465 while(1)
1467 if (is_recording)
1469 /* Poll periodically to flush data */
1470 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1472 if (ev.id == SYS_TIMEOUT)
1474 /* Messages that interrupt this will complete it */
1475 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1476 PCMREC_FLUSH_INTERRUPTABLE);
1477 continue;
1480 else
1482 /* Not doing anything - sit and wait for commands */
1483 queue_wait(&pcmrec_queue, &ev);
1486 switch (ev.id)
1488 case PCMREC_INIT:
1489 pcmrec_init();
1490 break;
1492 case PCMREC_CLOSE:
1493 pcmrec_close();
1494 break;
1496 case PCMREC_OPTIONS:
1497 pcmrec_set_recording_options(
1498 (struct audio_recording_options *)ev.data);
1499 break;
1501 case PCMREC_RECORD:
1502 clear_flush_interrupt();
1503 pcmrec_record((const char *)ev.data);
1504 break;
1506 case PCMREC_STOP:
1507 clear_flush_interrupt();
1508 pcmrec_stop();
1509 break;
1511 case PCMREC_PAUSE:
1512 clear_flush_interrupt();
1513 pcmrec_pause();
1514 break;
1516 case PCMREC_RESUME:
1517 pcmrec_resume();
1518 break;
1519 #if 0
1520 case PCMREC_FLUSH_NUM:
1521 pcmrec_flush((unsigned)ev.data);
1522 break;
1523 #endif
1524 case SYS_USB_CONNECTED:
1525 if (is_recording)
1526 break;
1527 pcmrec_close();
1528 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1529 usb_wait_for_disconnect(&pcmrec_queue);
1530 flush_interrupts = 0;
1531 break;
1532 } /* end switch */
1533 } /* end while */
1534 } /* pcmrec_thread */
1536 /****************************************************************************/
1537 /* */
1538 /* following functions will be called by the encoder codec */
1539 /* in a free-threaded manner */
1540 /* */
1541 /****************************************************************************/
1543 /* pass the encoder settings to the encoder */
1544 void enc_get_inputs(struct enc_inputs *inputs)
1546 inputs->sample_rate = sample_rate;
1547 inputs->num_channels = num_channels;
1548 inputs->rec_mono_mode = rec_mono_mode;
1549 inputs->config = &enc_config;
1550 } /* enc_get_inputs */
1552 /* set the encoder dimensions (called by encoder codec at initialization and
1553 termination) */
1554 void enc_set_parameters(struct enc_parameters *params)
1556 size_t bufsize, resbytes;
1558 logf("enc_set_parameters");
1560 if (!params)
1562 logf("reset");
1563 /* Encoder is terminating */
1564 memset(&enc_config, 0, sizeof (enc_config));
1565 enc_sample_rate = 0;
1566 cancel_cpu_boost(); /* Make sure no boost remains */
1567 return;
1570 enc_sample_rate = params->enc_sample_rate;
1571 logf("enc sampr:%lu", enc_sample_rate);
1573 pcm_rd_pos = dma_wr_pos;
1574 pcm_enc_pos = pcm_rd_pos;
1576 enc_config.afmt = params->afmt;
1577 /* addition of the header is always implied - chunk size 4-byte aligned */
1578 enc_chunk_size =
1579 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1580 enc_events_callback = params->events_callback;
1582 logf("chunk size:%lu", enc_chunk_size);
1584 /*** Configure the buffers ***/
1586 /* Layout of recording buffer:
1587 * [ax] = possible alignment x multiple
1588 * [sx] = possible size alignment of x multiple
1589 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1590 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1592 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1593 logf("resbytes:%lu", resbytes);
1595 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1596 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1597 #ifdef DEBUG
1598 - sizeof (*wrap_id_p)
1599 #endif
1602 enc_num_chunks = bufsize / enc_chunk_size;
1603 logf("num chunks:%d", enc_num_chunks);
1605 /* get real amount used by encoder chunks */
1606 bufsize = enc_num_chunks*enc_chunk_size;
1607 logf("enc size:%lu", bufsize);
1609 #ifdef DEBUG
1610 /* add magic at wraparound for spillover checks */
1611 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1612 bufsize += sizeof (*wrap_id_p);
1613 *wrap_id_p = ENC_CHUNK_MAGIC;
1614 #endif
1616 /** set OUT parameters **/
1617 params->enc_buffer = enc_buffer;
1618 params->buf_chunk_size = enc_chunk_size;
1619 params->num_chunks = enc_num_chunks;
1621 /* calculate reserve buffer start and return pointer to encoder */
1622 params->reserve_buffer = NULL;
1623 if (resbytes > 0)
1625 params->reserve_buffer = enc_buffer + bufsize;
1626 bufsize += resbytes;
1629 /* place filename queue at end of buffer using up whatever remains */
1630 fnq_rd_pos = 0; /* reset */
1631 fnq_wr_pos = 0; /* reset */
1632 fn_queue = enc_buffer + bufsize;
1633 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1634 fnq_size /= MAX_PATH;
1635 if (fnq_size > FNQ_MAX_NUM_PATHS)
1636 fnq_size = FNQ_MAX_NUM_PATHS;
1637 fnq_size *= MAX_PATH;
1638 logf("fnq files:%ld", fnq_size / MAX_PATH);
1640 #if defined(DEBUG)
1641 logf("ab :%08lX", (uintptr_t)audiobuf);
1642 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1643 logf("enc:%08lX", (uintptr_t)enc_buffer);
1644 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1645 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1646 logf("fnq:%08lX", (uintptr_t)fn_queue);
1647 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1648 logf("abe:%08lX", (uintptr_t)audiobufend);
1649 #endif
1651 /* init all chunk headers and reset indexes */
1652 enc_rd_index = 0;
1653 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1655 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1656 #ifdef DEBUG
1657 chunk->id = ENC_CHUNK_MAGIC;
1658 #endif
1659 chunk->flags = 0;
1662 logf("enc_set_parameters done");
1663 } /* enc_set_parameters */
1665 /* return encoder chunk at current write position -
1666 NOTE: can be called by pcmrec thread when splitting streams */
1667 struct enc_chunk_hdr * enc_get_chunk(void)
1669 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1671 #ifdef DEBUG
1672 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1674 errors |= PCMREC_E_CHUNK_OVF;
1675 logf("finish chk ovf: %d", enc_wr_index);
1677 #endif
1679 chunk->flags &= CHUNKF_START_FILE;
1681 if (!is_recording)
1682 chunk->flags |= CHUNKF_PRERECORD;
1684 return chunk;
1685 } /* enc_get_chunk */
1687 /* releases the current chunk into the available chunks -
1688 NOTE: can be called by pcmrec thread when splitting streams */
1689 void enc_finish_chunk(void)
1691 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1693 if ((long)chunk->flags < 0)
1695 /* encoder set error flag */
1696 errors |= PCMREC_E_ENCODER;
1697 logf("finish chk enc error");
1700 /* advance enc_wr_index to the next encoder chunk */
1701 INC_ENC_INDEX(enc_wr_index);
1703 if (enc_rd_index != enc_wr_index)
1705 num_rec_bytes += chunk->enc_size;
1706 num_rec_samples += chunk->num_pcm;
1707 #if 0
1708 accum_rec_bytes += chunk->enc_size;
1709 accum_pcm_samples += chunk->num_pcm;
1710 #endif
1712 else if (is_recording) /* buffer full */
1714 /* keep current position and put up warning flag */
1715 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1716 logf("enc_buffer ovf");
1717 DEC_ENC_INDEX(enc_wr_index);
1718 if (pcmrec_context)
1720 /* if stream splitting, keep this out of circulation and
1721 flush a small number, then readd - cannot risk losing
1722 stream markers */
1723 logf("mini flush");
1724 pcmrec_flush(PCMREC_FLUSH_MINI);
1725 INC_ENC_INDEX(enc_wr_index);
1728 else
1730 /* advance enc_rd_index for prerecording */
1731 INC_ENC_INDEX(enc_rd_index);
1733 } /* enc_finish_chunk */
1735 /* passes a pointer to next chunk of unprocessed wav data */
1736 /* TODO: this really should give the actual size returned */
1737 unsigned char * enc_get_pcm_data(size_t size)
1739 int wp = dma_wr_pos;
1740 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1742 /* limit the requested pcm data size */
1743 if (size > PCM_MAX_FEED_SIZE)
1744 size = PCM_MAX_FEED_SIZE;
1746 if (avail >= size)
1748 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1749 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1751 pcm_enc_pos = pcm_rd_pos;
1752 pcm_rd_pos = next_pos;
1754 /* ptr must point to continous data at wraparound position */
1755 if ((size_t)pcm_rd_pos < size)
1757 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1758 pcm_buffer, pcm_rd_pos);
1761 if (avail >= (sample_rate << 2))
1763 /* Filling up - boost codec */
1764 trigger_cpu_boost();
1767 pcm_buffer_empty = false;
1768 return ptr;
1771 /* not enough data available - encoder should idle */
1772 pcm_buffer_empty = true;
1774 cancel_cpu_boost();
1776 /* Sleep long enough to allow one frame on average */
1777 sleep(0);
1779 return NULL;
1780 } /* enc_get_pcm_data */
1782 /* puts some pcm data back in the queue */
1783 size_t enc_unget_pcm_data(size_t size)
1785 int wp = dma_wr_pos;
1786 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1787 2*PCM_CHUNK_SIZE;
1789 /* allow one interrupt to occur during this call and not have the
1790 new read position inside the DMA destination chunk */
1791 if ((ssize_t)old_avail > 0)
1793 /* limit size to amount of old data remaining */
1794 if (size > old_avail)
1795 size = old_avail;
1797 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1798 pcm_rd_pos = pcm_enc_pos;
1800 return size;
1803 return 0;
1804 } /* enc_unget_pcm_data */