first step at building the main binary
[kugel-rb.git] / apps / recorder / pcm_record.c
blob704d859e579e8f81d0845fee1046aec20899ea80
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-extra.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 #include "appevents.h"
35 #ifdef HAVE_SPDIF_IN
36 #include "spdif.h"
37 #endif
39 /***************************************************************************/
41 extern unsigned int codec_thread_id;
43 /** General recording state **/
44 static bool is_recording; /* We are recording */
45 static bool is_paused; /* We have paused */
46 static unsigned long errors; /* An error has occured */
47 static unsigned long warnings; /* Warning */
48 static int flush_interrupts = 0; /* Number of messages queued that
49 should interrupt a flush in
50 progress -
51 for a safety net and a prompt
52 response to stop, split and pause
53 requests -
54 only interrupts a flush initiated
55 by pcmrec_flush(0) */
57 /* Utility functions for setting/clearing flushing interrupt flag */
58 static inline void flush_interrupt(void)
60 flush_interrupts++;
61 logf("flush int: %d", flush_interrupts);
64 static inline void clear_flush_interrupt(void)
66 if (--flush_interrupts < 0)
67 flush_interrupts = 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 int rec_mono_mode; /* how mono is created */
96 static struct encoder_config enc_config; /* Current encoder configuration */
97 static unsigned long pre_record_ticks; /* pre-record time in ticks */
99 /****************************************************************************
100 use 2 circular buffers:
101 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
102 enc_buffer=encoded audio buffer: storage for encoder output data
104 Flow:
105 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
106 2. if enough pcm data are available the encoder codec does encoding of pcm
107 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
108 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
110 Functions calls (basic encoder steps):
111 1.main: audio_load_encoder(); start the encoder
112 2.encoder: enc_get_inputs(); get encoder recording settings
113 3.encoder: enc_set_parameters(); set the encoder parameters
114 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
115 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
116 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
117 7.encoder: <process enc chunk> compress and store data to enc chunk
118 8.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 9.encoder: repeat 4. to 8.
125 A.pcmrec: enc_events_callback(); called for certain events
127 (*) Optional step
128 ****************************************************************************/
130 /** buffer parameters where incoming PCM data is placed **/
131 #if MEM <= 2
132 #define PCM_NUM_CHUNKS 16 /* Power of 2 */
133 #else
134 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
135 #endif
136 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
137 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
139 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
140 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
141 #define INC_ENC_INDEX(index) \
142 { if (++index >= enc_num_chunks) index = 0; }
143 #define DEC_ENC_INDEX(index) \
144 { if (--index < 0) index = enc_num_chunks - 1; }
146 static size_t rec_buffer_size; /* size of available buffer */
147 static unsigned char *pcm_buffer; /* circular recording buffer */
148 static unsigned char *enc_buffer; /* circular encoding buffer */
149 #ifdef DEBUG
150 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
151 aid to check if the encoder data
152 spilled out of its chunk */
153 #endif /* DEBUG */
154 static volatile int dma_wr_pos; /* current DMA write pos */
155 static int pcm_rd_pos; /* current PCM read pos */
156 static int pcm_enc_pos; /* position encoder is processing */
157 static volatile bool dma_lock; /* lock DMA write position */
158 static int enc_wr_index; /* encoder chunk write index */
159 static int enc_rd_index; /* encoder chunk read index */
160 static int enc_num_chunks; /* number of chunks in ringbuffer */
161 static size_t enc_chunk_size; /* maximum encoder chunk size */
162 static unsigned long enc_sample_rate; /* sample rate used by encoder */
163 static bool pcmrec_context = false; /* called by pcmrec thread? */
164 static bool pcm_buffer_empty; /* all pcm chunks processed? */
166 /** file flushing **/
167 static int low_watermark; /* Low watermark to stop flush */
168 static int high_watermark; /* max chunk limit for data flush */
169 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
170 static int last_storage_spinup_time = -1;/* previous spin time used */
171 #ifdef HAVE_PRIORITY_SCHEDULING
172 static int flood_watermark; /* boost thread priority when here */
173 #endif
175 /* Constants that control watermarks */
176 #define MINI_CHUNKS 10 /* chunk count for mini flush */
177 #ifdef HAVE_PRIORITY_SCHEDULING
178 #define PRIO_SECONDS 10 /* max flush time before priority boost */
179 #endif
180 #if MEM <= 2
181 /* fractions must be integer fractions of 4 because they are evaluated with
182 * X*4*XXX_SECONDS, that way we avoid float calculation */
183 #define LOW_SECONDS 1/4 /* low watermark time till empty */
184 #define PANIC_SECONDS 1/2 /* flood watermark time until full */
185 #define FLUSH_SECONDS 1 /* flush watermark time until full */
186 #elif MEM <= 16
187 #define LOW_SECONDS 1 /* low watermark time till empty */
188 #define PANIC_SECONDS 5 /* flood watermark time until full */
189 #define FLUSH_SECONDS 7 /* flush watermark time until full */
190 #else
191 #define LOW_SECONDS 1 /* low watermark time till empty */
192 #define PANIC_SECONDS 8
193 #define FLUSH_SECONDS 10
194 #endif /* MEM */
196 /** encoder events **/
197 static void (*enc_events_callback)(enum enc_events event, void *data);
199 /** Path queue for files to write **/
200 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
201 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
202 static unsigned char *fn_queue; /* pointer to first filename */
203 static ssize_t fnq_size; /* capacity of queue in bytes */
204 static int fnq_rd_pos; /* current read position */
205 static int fnq_wr_pos; /* current write position */
206 #define FNQ_NEXT(pos) \
207 ({ int p = (pos) + MAX_PATH; \
208 if (p >= fnq_size) \
209 p = 0; \
210 p; })
211 #define FNQ_PREV(pos) \
212 ({ int p = (pos) - MAX_PATH; \
213 if (p < 0) \
214 p = fnq_size - MAX_PATH; \
215 p; })
217 enum
219 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
220 incoming messages - combine
221 with other constants */
222 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
223 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
224 chunks */
225 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
226 reached */
229 /***************************************************************************/
231 static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
232 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
233 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
234 static const char pcmrec_thread_name[] = "pcmrec";
235 static unsigned int pcmrec_thread_id = 0;
237 static void pcmrec_thread(void);
239 enum
241 PCMREC_NULL = 0,
242 PCMREC_INIT, /* enable recording */
243 PCMREC_CLOSE, /* close recording */
244 PCMREC_OPTIONS, /* set recording options */
245 PCMREC_RECORD, /* record a new file */
246 PCMREC_STOP, /* stop the current recording */
247 PCMREC_PAUSE, /* pause the current recording */
248 PCMREC_RESUME, /* resume the current recording */
249 #if 0
250 PCMREC_FLUSH_NUM, /* flush a number of files out */
251 #endif
254 /*******************************************************************/
255 /* Functions that are not executing in the pcmrec_thread first */
256 /*******************************************************************/
258 /* Callback for when more data is ready - called in interrupt context */
259 static void pcm_rec_have_more(int status, void **start, size_t *size)
261 if (status < 0)
263 /* some error condition */
264 if (status == DMA_REC_ERROR_DMA)
266 /* Flush recorded data to disk and stop recording */
267 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
268 return;
270 /* else try again next transmission - frame is invalid */
272 else if (!dma_lock)
274 /* advance write position */
275 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
277 /* set pcm ovf if processing start position is inside current
278 write chunk */
279 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
280 warnings |= PCMREC_W_PCM_BUFFER_OVF;
282 dma_wr_pos = next_pos;
285 *start = GET_PCM_CHUNK(dma_wr_pos);
286 *size = PCM_CHUNK_SIZE;
287 } /* pcm_rec_have_more */
289 static void reset_hardware(void)
291 /* reset pcm to defaults */
292 pcm_set_frequency(REC_SAMPR_DEFAULT | SAMPR_TYPE_REC);
293 audio_set_output_source(AUDIO_SRC_PLAYBACK);
294 pcm_apply_settings();
297 /** pcm_rec_* group **/
300 * Clear all errors and warnings
302 void pcm_rec_error_clear(void)
304 errors = warnings = 0;
305 } /* pcm_rec_error_clear */
308 * Check mode, errors and warnings
310 unsigned long pcm_rec_status(void)
312 unsigned long ret = 0;
314 if (is_recording)
315 ret |= AUDIO_STATUS_RECORD;
316 else if (pre_record_ticks)
317 ret |= AUDIO_STATUS_PRERECORD;
319 if (is_paused)
320 ret |= AUDIO_STATUS_PAUSE;
322 if (errors)
323 ret |= AUDIO_STATUS_ERROR;
325 if (warnings)
326 ret |= AUDIO_STATUS_WARNING;
328 return ret;
329 } /* pcm_rec_status */
332 * Return warnings that have occured since recording started
334 unsigned long pcm_rec_get_warnings(void)
336 return warnings;
339 #if 0
340 int pcm_rec_current_bitrate(void)
342 if (accum_pcm_samples == 0)
343 return 0;
345 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
346 } /* pcm_rec_current_bitrate */
347 #endif
349 #if 0
350 int pcm_rec_encoder_afmt(void)
352 return enc_config.afmt;
353 } /* pcm_rec_encoder_afmt */
354 #endif
356 #if 0
357 int pcm_rec_rec_format(void)
359 return afmt_rec_format[enc_config.afmt];
360 } /* pcm_rec_rec_format */
361 #endif
363 #ifdef HAVE_SPDIF_IN
364 unsigned long pcm_rec_sample_rate(void)
366 /* Which is better ?? */
367 #if 0
368 return enc_sample_rate;
369 #endif
370 return sample_rate;
371 } /* audio_get_sample_rate */
372 #endif
375 * Creates pcmrec_thread
377 void pcm_rec_init(void)
379 queue_init(&pcmrec_queue, true);
380 pcmrec_thread_id =
381 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
382 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
383 IF_COP(, CPU));
384 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
385 pcmrec_thread_id);
386 } /* pcm_rec_init */
388 /** audio_* group **/
391 * Initializes recording - call before calling any other recording function
393 void audio_init_recording(unsigned int buffer_offset)
395 logf("audio_init_recording");
396 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
397 logf("audio_init_recording done");
398 (void)buffer_offset;
399 } /* audio_init_recording */
402 * Closes recording - call audio_stop_recording first
404 void audio_close_recording(void)
406 logf("audio_close_recording");
407 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
408 logf("audio_close_recording done");
409 } /* audio_close_recording */
412 * Sets recording parameters
414 void audio_set_recording_options(struct audio_recording_options *options)
416 logf("audio_set_recording_options");
417 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
418 logf("audio_set_recording_options done");
419 } /* audio_set_recording_options */
422 * Start recording if not recording or else split
424 void audio_record(const char *filename)
426 logf("audio_record: %s", filename);
427 flush_interrupt();
428 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
429 logf("audio_record_done");
430 } /* audio_record */
433 * audio_record wrapper for API compatibility with HW codec
435 void audio_new_file(const char *filename)
437 audio_record(filename);
438 } /* audio_new_file */
441 * Stop current recording if recording
443 void audio_stop_recording(void)
445 logf("audio_stop_recording");
446 flush_interrupt();
447 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
448 logf("audio_stop_recording done");
449 } /* audio_stop_recording */
452 * Pause current recording
454 void audio_pause_recording(void)
456 logf("audio_pause_recording");
457 flush_interrupt();
458 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
459 logf("audio_pause_recording done");
460 } /* audio_pause_recording */
463 * Resume current recording if paused
465 void audio_resume_recording(void)
467 logf("audio_resume_recording");
468 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
469 logf("audio_resume_recording done");
470 } /* audio_resume_recording */
473 * Note that microphone is mono, only left value is used
474 * See audiohw_set_recvol() for exact ranges.
476 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
479 void audio_set_recording_gain(int left, int right, int type)
481 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
482 audiohw_set_recvol(left, right, type);
483 } /* audio_set_recording_gain */
485 /** Information about current state **/
488 * Return current recorded time in ticks (playback eqivalent time)
490 unsigned long audio_recorded_time(void)
492 if (!is_recording || enc_sample_rate == 0)
493 return 0;
495 /* return actual recorded time a la encoded data even if encoder rate
496 doesn't match the pcm rate */
497 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
498 } /* audio_recorded_time */
501 * Return number of bytes encoded to output
503 unsigned long audio_num_recorded_bytes(void)
505 if (!is_recording)
506 return 0;
508 return num_rec_bytes;
509 } /* audio_num_recorded_bytes */
511 /***************************************************************************/
512 /* */
513 /* Functions that execute in the context of pcmrec_thread */
514 /* */
515 /***************************************************************************/
517 /** Filename Queue **/
519 /* returns true if the queue is empty */
520 static inline bool pcmrec_fnq_is_empty(void)
522 return fnq_rd_pos == fnq_wr_pos;
523 } /* pcmrec_fnq_is_empty */
525 /* empties the filename queue */
526 static inline void pcmrec_fnq_set_empty(void)
528 fnq_rd_pos = fnq_wr_pos;
529 } /* pcmrec_fnq_set_empty */
531 /* returns true if the queue is full */
532 static bool pcmrec_fnq_is_full(void)
534 ssize_t size = fnq_wr_pos - fnq_rd_pos;
535 if (size < 0)
536 size += fnq_size;
538 return size >= fnq_size - MAX_PATH;
539 } /* pcmrec_fnq_is_full */
541 /* queue another filename - will overwrite oldest one if full */
542 static bool pcmrec_fnq_add_filename(const char *filename)
544 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
545 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
547 if (fnq_rd_pos != fnq_wr_pos)
548 return true;
550 /* queue full */
551 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
552 return true;
553 } /* pcmrec_fnq_add_filename */
555 /* replace the last filename added */
556 static bool pcmrec_fnq_replace_tail(const char *filename)
558 int pos;
560 if (pcmrec_fnq_is_empty())
561 return false;
563 pos = FNQ_PREV(fnq_wr_pos);
565 strlcpy(fn_queue + pos, filename, MAX_PATH);
567 return true;
568 } /* pcmrec_fnq_replace_tail */
570 /* pulls the next filename from the queue */
571 static bool pcmrec_fnq_get_filename(char *filename)
573 if (pcmrec_fnq_is_empty())
574 return false;
576 if (filename)
577 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
579 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
580 return true;
581 } /* pcmrec_fnq_get_filename */
583 /* close the file number pointed to by fd_p */
584 static void pcmrec_close_file(int *fd_p)
586 if (*fd_p < 0)
587 return; /* preserve error */
589 if (close(*fd_p) != 0)
590 errors |= PCMREC_E_IO;
592 *fd_p = -1;
593 } /* pcmrec_close_file */
595 /** Data Flushing **/
598 * called after callback to update sizes if codec changed the amount of data
599 * a chunk represents
601 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
602 unsigned long prev_num_pcm)
604 if (rec_fdata.new_enc_size != prev_enc_size)
606 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
607 num_rec_bytes += size_diff;
608 #if 0
609 accum_rec_bytes += size_diff;
610 #endif
613 if (rec_fdata.new_num_pcm != prev_num_pcm)
615 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
616 num_rec_samples += pcm_diff;
617 #if 0
618 accum_pcm_samples += pcm_diff;
619 #endif
621 } /* pcmrec_update_sizes_inl */
623 /* don't need to inline every instance */
624 static void pcmrec_update_sizes(size_t prev_enc_size,
625 unsigned long prev_num_pcm)
627 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
628 } /* pcmrec_update_sizes */
630 static void pcmrec_start_file(void)
632 size_t enc_size = rec_fdata.new_enc_size;
633 unsigned long num_pcm = rec_fdata.new_num_pcm;
634 int curr_rec_file = rec_fdata.rec_file;
635 char filename[MAX_PATH];
637 /* must always pull the filename that matches with this queue */
638 if (!pcmrec_fnq_get_filename(filename))
640 logf("start file: fnq empty");
641 *filename = '\0';
642 errors |= PCMREC_E_FNQ_DESYNC;
644 else if (errors != 0)
646 logf("start file: error already");
648 else if (curr_rec_file >= 0)
650 /* Any previous file should have been closed */
651 logf("start file: file already open");
652 errors |= PCMREC_E_FNQ_DESYNC;
655 if (errors != 0)
656 rec_fdata.chunk->flags |= CHUNKF_ERROR;
658 /* encoder can set error flag here and should increase
659 enc_new_size and pcm_new_size to reflect additional
660 data written if any */
661 rec_fdata.filename = filename;
662 enc_events_callback(ENC_START_FILE, &rec_fdata);
664 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
666 logf("start file: enc error");
667 errors |= PCMREC_E_ENCODER;
670 if (errors != 0)
672 pcmrec_close_file(&curr_rec_file);
673 /* Write no more to this file */
674 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
676 else
678 pcmrec_update_sizes(enc_size, num_pcm);
681 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
682 } /* pcmrec_start_file */
684 static inline void pcmrec_write_chunk(void)
686 size_t enc_size = rec_fdata.new_enc_size;
687 unsigned long num_pcm = rec_fdata.new_num_pcm;
689 if (errors != 0)
690 rec_fdata.chunk->flags |= CHUNKF_ERROR;
692 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
694 if ((long)rec_fdata.chunk->flags >= 0)
696 pcmrec_update_sizes_inl(enc_size, num_pcm);
698 else if (errors == 0)
700 logf("wr chk enc error %lu %lu",
701 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
702 errors |= PCMREC_E_ENCODER;
704 } /* pcmrec_write_chunk */
706 static void pcmrec_end_file(void)
708 /* all data in output buffer for current file will have been
709 written and encoder can now do any nescessary steps to
710 finalize the written file */
711 size_t enc_size = rec_fdata.new_enc_size;
712 unsigned long num_pcm = rec_fdata.new_num_pcm;
714 enc_events_callback(ENC_END_FILE, &rec_fdata);
716 if (errors == 0)
718 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
720 logf("end file: enc error");
721 errors |= PCMREC_E_ENCODER;
723 else
725 pcmrec_update_sizes(enc_size, num_pcm);
729 /* Force file close if error */
730 if (errors != 0)
731 pcmrec_close_file(&rec_fdata.rec_file);
733 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
734 } /* pcmrec_end_file */
737 * Update buffer watermarks with spinup time compensation
739 * All this assumes reasonable data rates, chunk sizes and sufficient
740 * memory for the most part. Some dumb checks are included but perhaps
741 * are pointless since this all will break down at extreme limits that
742 * are currently not applicable to any supported device.
744 static void pcmrec_refresh_watermarks(void)
746 logf("ata spinup: %d", storage_spinup_time());
748 /* set the low mark for when flushing stops if automatic */
749 /* don't change the order in this expression, LOW_SECONDS can be an
750 * integer fraction of 4 */
751 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1))
752 / enc_chunk_size;
753 logf("low wmk: %d", low_watermark);
755 #ifdef HAVE_PRIORITY_SCHEDULING
756 /* panic boost thread priority if 2 seconds of ground is lost -
757 this allows encoder to boost with just under a second of
758 pcm data (if not yet full enough to boost itself)
759 and not falsely trip the alarm. */
760 /* don't change the order in this expression, PANIC_SECONDS can be an
761 * integer fraction of 4 */
762 flood_watermark = enc_num_chunks -
763 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
764 / enc_chunk_size;
766 if (flood_watermark < low_watermark)
768 logf("warning: panic < low");
769 flood_watermark = low_watermark;
772 logf("flood at: %d", flood_watermark);
773 #endif
774 spinup_time = last_storage_spinup_time = storage_spinup_time();
775 #if (CONFIG_STORAGE & STORAGE_ATA)
776 /* write at 8s + st remaining in enc_buffer - range 12s to
777 20s total - default to 3.5s spinup. */
778 if (spinup_time == 0)
779 spinup_time = 35*HZ/10; /* default - cozy */
780 else if (spinup_time < 2*HZ)
781 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
782 else if (spinup_time > 10*HZ)
783 spinup_time = 10*HZ; /* do you have a functioning HD? */
784 #endif
786 /* try to start writing with 10s remaining after disk spinup */
787 high_watermark = enc_num_chunks -
788 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
789 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
791 if (high_watermark < low_watermark)
793 logf("warning: low 'write at' (%d)", high_watermark);
794 high_watermark = low_watermark;
795 low_watermark /= 2;
798 logf("write at: %d", high_watermark);
799 } /* pcmrec_refresh_watermarks */
802 * Process the chunks
804 * This function is called when queue_get_w_tmo times out.
806 * Set flush_num to the number of files to flush to disk or to
807 * a PCMREC_FLUSH_* constant.
809 static void pcmrec_flush(unsigned flush_num)
811 #ifdef HAVE_PRIORITY_SCHEDULING
812 static unsigned long last_flush_tick; /* tick when function returned */
813 unsigned long start_tick; /* When flush started */
814 unsigned long prio_tick; /* Timeout for auto boost */
815 int prio_pcmrec; /* Current thread priority for pcmrec */
816 int prio_codec; /* Current thread priority for codec */
817 #endif
818 int num_ready; /* Number of chunks ready at start */
819 unsigned remaining; /* Number of file starts remaining */
820 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
821 bool interruptable; /* Flush can be interupted */
823 num_ready = enc_wr_index - enc_rd_index;
824 if (num_ready < 0)
825 num_ready += enc_num_chunks;
827 /* save interruptable flag and remove it to get the actual count */
828 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
829 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
831 if (flush_num == 0)
833 if (!is_recording)
834 return;
836 if (storage_spinup_time() != last_storage_spinup_time)
837 pcmrec_refresh_watermarks();
839 /* enough available? no? then leave */
840 if (num_ready < high_watermark)
841 return;
842 } /* endif (flush_num == 0) */
844 #ifdef HAVE_PRIORITY_SCHEDULING
845 start_tick = current_tick;
846 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
848 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
850 /* if we're getting called too much and this isn't forced,
851 boost stat by expiring timeout in advance */
852 logf("too frequent flush");
853 prio_tick = current_tick - 1;
856 prio_pcmrec = -1;
857 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
858 need init */
859 #endif
861 logf("writing:%d(%d):%s%s", num_ready, flush_num,
862 interruptable ? "i" : "",
863 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
865 cpu_boost(true);
867 remaining = flush_num;
868 chunks_flushed = 0;
870 while (num_ready > 0)
872 /* check current number of encoder chunks */
873 int num = enc_wr_index - enc_rd_index;
874 if (num < 0)
875 num += enc_num_chunks;
877 if (num <= low_watermark &&
878 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
880 logf("low data: %d", num);
881 break; /* data remaining is below threshold */
884 if (interruptable && flush_interrupts > 0)
886 logf("int at: %d", num);
887 break; /* interrupted */
890 #ifdef HAVE_PRIORITY_SCHEDULING
891 if (prio_pcmrec == -1 && (num >= flood_watermark ||
892 TIME_AFTER(current_tick, prio_tick)))
894 /* losing ground or holding without progress - boost
895 priority until finished */
896 logf("pcmrec: boost (%s)",
897 num >= flood_watermark ? "num" : "time");
898 prio_pcmrec = thread_set_priority(THREAD_ID_CURRENT,
899 thread_get_priority(THREAD_ID_CURRENT) - 4);
900 prio_codec = thread_set_priority(codec_thread_id,
901 thread_get_priority(codec_thread_id) - 4);
903 #endif
905 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
906 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
907 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
909 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
911 pcmrec_start_file();
912 if (--remaining == 0)
913 num_ready = 0; /* stop on next loop - must write this
914 chunk if it has data */
917 pcmrec_write_chunk();
919 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
920 pcmrec_end_file();
922 INC_ENC_INDEX(enc_rd_index);
924 if (errors != 0)
926 pcmrec_end_file();
927 break;
930 if (flush_num == PCMREC_FLUSH_MINI &&
931 ++chunks_flushed >= MINI_CHUNKS)
933 logf("mini flush break");
934 break;
936 /* no yielding; the file apis called in the codecs do that
937 sufficiently */
938 } /* end while */
940 /* sync file */
941 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
942 errors |= PCMREC_E_IO;
944 cpu_boost(false);
946 #ifdef HAVE_PRIORITY_SCHEDULING
947 if (prio_pcmrec != -1)
949 /* return to original priorities */
950 logf("pcmrec: unboost priority");
951 thread_set_priority(THREAD_ID_CURRENT, prio_pcmrec);
952 thread_set_priority(codec_thread_id, prio_codec);
955 last_flush_tick = current_tick; /* save tick when we left */
956 #endif
958 logf("done");
959 } /* pcmrec_flush */
962 * Marks a new stream in the buffer and gives the encoder a chance for special
963 * handling of transition from one to the next. The encoder may change the
964 * chunk that ends the old stream by requesting more chunks and similiarly for
965 * the new but must always advance the position though the interface. It can
966 * later reject any data it cares to when writing the file but should mark the
967 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
968 * a NULL data pointer without error as well.
970 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
972 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
973 } /* pcmrec_get_chunk_index */
975 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
977 DEC_ENC_INDEX(index);
978 return GET_ENC_CHUNK(index);
979 } /* pcmrec_get_prev_chunk */
981 static void pcmrec_new_stream(const char *filename, /* next file name */
982 unsigned long flags, /* CHUNKF_* flags */
983 int pre_index) /* index for prerecorded data */
985 logf("pcmrec_new_stream");
986 char path[MAX_PATH]; /* place to copy filename so sender can be released */
988 struct enc_buffer_event_data data;
989 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
990 new filename */
991 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
992 stream */
993 bool did_flush = false; /* did a flush occurr? */
995 if (filename)
996 strlcpy(path, filename, MAX_PATH);
997 queue_reply(&pcmrec_queue, 0); /* We have all we need */
999 data.pre_chunk = NULL;
1000 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1002 /* end chunk */
1003 if (flags & CHUNKF_END_FILE)
1005 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1007 if (data.chunk->flags & CHUNKF_START_FILE)
1009 /* cannot start and end on same unprocessed chunk */
1010 logf("file end on start");
1011 flags &= ~CHUNKF_END_FILE;
1013 else if (enc_rd_index == enc_wr_index)
1015 /* all data flushed but file not ended - chunk will be left
1016 empty */
1017 logf("end on dead end");
1018 data.chunk->flags = 0;
1019 data.chunk->enc_size = 0;
1020 data.chunk->num_pcm = 0;
1021 data.chunk->enc_data = NULL;
1022 INC_ENC_INDEX(enc_wr_index);
1023 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1025 else
1027 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1029 if (last->flags & CHUNKF_END_FILE)
1031 /* end already processed and marked - can't end twice */
1032 logf("file end again");
1033 flags &= ~CHUNKF_END_FILE;
1038 /* start chunk */
1039 if (flags & CHUNKF_START_FILE)
1041 bool pre = flags & CHUNKF_PRERECORD;
1043 if (pre)
1045 logf("stream prerecord start");
1046 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1047 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1049 else
1051 logf("stream normal start");
1052 start = data.chunk;
1053 start->flags &= CHUNKF_START_FILE;
1056 /* if encoder hasn't yet processed the last start - abort the start
1057 of the previous file queued or else it will be empty and invalid */
1058 if (start->flags & CHUNKF_START_FILE)
1060 logf("replacing fnq tail: %s", filename);
1061 fnq_add_fn = pcmrec_fnq_replace_tail;
1063 else
1065 logf("adding filename: %s", filename);
1066 fnq_add_fn = pcmrec_fnq_add_filename;
1070 data.flags = flags;
1071 pcmrec_context = true; /* switch encoder context */
1072 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1073 pcmrec_context = false; /* switch back */
1075 if (flags & CHUNKF_END_FILE)
1077 int i = pcmrec_get_chunk_index(data.chunk);
1078 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1081 if (start)
1083 if (!(flags & CHUNKF_PRERECORD))
1085 /* get stats on data added to start - sort of a prerecord
1086 operation */
1087 int i = pcmrec_get_chunk_index(data.chunk);
1088 struct enc_chunk_hdr *chunk = data.chunk;
1090 logf("start data: %d %d", i, enc_wr_index);
1092 num_rec_bytes = 0;
1093 num_rec_samples = 0;
1095 while (i != enc_wr_index)
1097 num_rec_bytes += chunk->enc_size;
1098 num_rec_samples += chunk->num_pcm;
1099 INC_ENC_INDEX(i);
1100 chunk = GET_ENC_CHUNK(i);
1103 start->flags &= ~CHUNKF_START_FILE;
1104 start = data.chunk;
1107 start->flags |= CHUNKF_START_FILE;
1109 /* flush all pending files out if full and adding */
1110 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1112 logf("fnq full");
1113 pcmrec_flush(PCMREC_FLUSH_ALL);
1114 did_flush = true;
1117 fnq_add_fn(path);
1120 /* Make sure to complete any interrupted high watermark */
1121 if (!did_flush)
1122 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1123 } /* pcmrec_new_stream */
1125 /** event handlers for pcmrec thread */
1127 /* PCMREC_INIT */
1128 static void pcmrec_init(void)
1130 unsigned char *buffer;
1131 send_event(RECORDING_EVENT_START, NULL);
1133 /* warings and errors */
1134 warnings =
1135 errors = 0;
1137 pcmrec_close_file(&rec_fdata.rec_file);
1138 rec_fdata.rec_file = -1;
1140 /* pcm FIFO */
1141 dma_lock = true;
1142 pcm_rd_pos = 0;
1143 dma_wr_pos = 0;
1144 pcm_enc_pos = 0;
1146 /* encoder FIFO */
1147 enc_wr_index = 0;
1148 enc_rd_index = 0;
1150 /* filename queue */
1151 fnq_rd_pos = 0;
1152 fnq_wr_pos = 0;
1154 /* stats */
1155 num_rec_bytes = 0;
1156 num_rec_samples = 0;
1157 #if 0
1158 accum_rec_bytes = 0;
1159 accum_pcm_samples = 0;
1160 #endif
1162 pre_record_ticks = 0;
1164 is_recording = false;
1165 is_paused = false;
1167 buffer = audio_get_recording_buffer(&rec_buffer_size);
1169 /* Line align pcm_buffer 2^5=32 bytes */
1170 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1171 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1172 PCM_MAX_FEED_SIZE, 2);
1173 /* Adjust available buffer for possible align advancement */
1174 rec_buffer_size -= pcm_buffer - buffer;
1176 pcm_init_recording();
1177 } /* pcmrec_init */
1179 /* PCMREC_CLOSE */
1180 static void pcmrec_close(void)
1182 dma_lock = true;
1183 pre_record_ticks = 0; /* Can't be prerecording any more */
1184 warnings = 0;
1185 pcm_close_recording();
1186 reset_hardware();
1187 audio_remove_encoder();
1188 send_event(RECORDING_EVENT_STOP, NULL);
1189 } /* pcmrec_close */
1191 /* PCMREC_OPTIONS */
1192 static void pcmrec_set_recording_options(
1193 struct audio_recording_options *options)
1195 /* stop DMA transfer */
1196 dma_lock = true;
1197 pcm_stop_recording();
1199 rec_frequency = options->rec_frequency;
1200 rec_source = options->rec_source;
1201 num_channels = options->rec_channels == 1 ? 1 : 2;
1202 rec_mono_mode = options->rec_mono_mode;
1203 pre_record_ticks = options->rec_prerecord_time * HZ;
1204 enc_config = options->enc_config;
1205 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1207 #ifdef HAVE_SPDIF_IN
1208 if (rec_source == AUDIO_SRC_SPDIF)
1210 /* must measure SPDIF sample rate before configuring codecs */
1211 unsigned long sr = spdif_measure_frequency();
1212 /* round to master list for SPDIF rate */
1213 int index = round_value_to_list32(sr, audio_master_sampr_list,
1214 SAMPR_NUM_FREQ, false);
1215 sample_rate = audio_master_sampr_list[index];
1216 /* round to HW playback rates for monitoring */
1217 index = round_value_to_list32(sr, hw_freq_sampr,
1218 HW_NUM_FREQ, false);
1219 pcm_set_frequency(hw_freq_sampr[index] | SAMPR_TYPE_REC);
1220 /* encoders with a limited number of rates do their own rounding */
1222 else
1223 #endif
1225 /* set sample rate from frequency selection */
1226 sample_rate = rec_freq_sampr[rec_frequency];
1227 pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1230 /* set monitoring */
1231 audio_set_output_source(rec_source);
1233 /* apply hardware setting to start monitoring now */
1234 pcm_apply_settings();
1236 queue_reply(&pcmrec_queue, 0); /* Release sender */
1238 if (audio_load_encoder(enc_config.afmt))
1240 /* start DMA transfer */
1241 dma_lock = pre_record_ticks == 0;
1242 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1243 PCM_CHUNK_SIZE);
1245 else
1247 logf("set rec opt: enc load failed");
1248 errors |= PCMREC_E_LOAD_ENCODER;
1250 } /* pcmrec_set_recording_options */
1252 /* PCMREC_RECORD - start recording (not gapless)
1253 or split stream (gapless) */
1254 static void pcmrec_record(const char *filename)
1256 unsigned long pre_sample_ticks;
1257 int rd_start;
1258 unsigned long flags;
1259 int pre_index;
1261 logf("pcmrec_record: %s", filename);
1263 /* reset stats */
1264 num_rec_bytes = 0;
1265 num_rec_samples = 0;
1267 if (!is_recording)
1269 #if 0
1270 accum_rec_bytes = 0;
1271 accum_pcm_samples = 0;
1272 #endif
1273 warnings = 0; /* reset warnings */
1275 rd_start = enc_wr_index;
1276 pre_sample_ticks = 0;
1278 pcmrec_refresh_watermarks();
1280 if (pre_record_ticks)
1282 int i = rd_start;
1283 /* calculate number of available chunks */
1284 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1285 enc_num_chunks) % enc_num_chunks;
1286 /* overflow at 974 seconds of prerecording at 44.1kHz */
1287 unsigned long pre_record_sample_ticks =
1288 enc_sample_rate*pre_record_ticks;
1289 int pre_chunks = 0; /* Counter to limit prerecorded time to
1290 prevent flood state at outset */
1292 logf("pre-st: %ld", pre_record_sample_ticks);
1294 /* Get exact measure of recorded data as number of samples aren't
1295 nescessarily going to be the max for each chunk */
1296 for (; avail_pre_chunks-- > 0;)
1298 struct enc_chunk_hdr *chunk;
1299 unsigned long chunk_sample_ticks;
1301 DEC_ENC_INDEX(i);
1303 chunk = GET_ENC_CHUNK(i);
1305 /* must have data to be counted */
1306 if (chunk->enc_data == NULL)
1307 continue;
1309 chunk_sample_ticks = chunk->num_pcm*HZ;
1311 rd_start = i;
1312 pre_sample_ticks += chunk_sample_ticks;
1313 num_rec_bytes += chunk->enc_size;
1314 num_rec_samples += chunk->num_pcm;
1315 pre_chunks++;
1317 /* stop here if enough already */
1318 if (pre_chunks >= high_watermark ||
1319 pre_sample_ticks >= pre_record_sample_ticks)
1321 logf("pre-chks: %d", pre_chunks);
1322 break;
1326 #if 0
1327 accum_rec_bytes = num_rec_bytes;
1328 accum_pcm_samples = num_rec_samples;
1329 #endif
1332 enc_rd_index = rd_start;
1334 /* filename queue should be empty */
1335 if (!pcmrec_fnq_is_empty())
1337 logf("fnq: not empty!");
1338 pcmrec_fnq_set_empty();
1341 flags = CHUNKF_START_FILE;
1342 if (pre_sample_ticks > 0)
1343 flags |= CHUNKF_PRERECORD;
1345 pre_index = enc_rd_index;
1347 dma_lock = false;
1348 is_paused = false;
1349 is_recording = true;
1351 else
1353 /* already recording, just split the stream */
1354 logf("inserting split");
1355 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1356 pre_index = 0;
1359 pcmrec_new_stream(filename, flags, pre_index);
1360 logf("pcmrec_record done");
1361 } /* pcmrec_record */
1363 /* PCMREC_STOP */
1364 static void pcmrec_stop(void)
1366 logf("pcmrec_stop");
1368 if (is_recording)
1370 dma_lock = true; /* lock dma write position */
1372 /* flush all available data first to avoid overflow while waiting
1373 for encoding to finish */
1374 pcmrec_flush(PCMREC_FLUSH_ALL);
1376 /* wait for encoder to finish remaining data */
1377 while (errors == 0 && !pcm_buffer_empty)
1378 yield();
1380 /* end stream at last data */
1381 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1383 /* flush anything else encoder added */
1384 pcmrec_flush(PCMREC_FLUSH_ALL);
1386 /* remove any pending file start not yet processed - should be at
1387 most one at enc_wr_index */
1388 pcmrec_fnq_get_filename(NULL);
1389 /* encoder should abort any chunk it was in midst of processing */
1390 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1392 /* filename queue should be empty */
1393 if (!pcmrec_fnq_is_empty())
1395 logf("fnq: not empty!");
1396 pcmrec_fnq_set_empty();
1399 /* be absolutely sure the file is closed */
1400 if (errors != 0)
1401 pcmrec_close_file(&rec_fdata.rec_file);
1402 rec_fdata.rec_file = -1;
1404 is_recording = false;
1405 is_paused = false;
1406 dma_lock = pre_record_ticks == 0;
1408 else
1410 logf("not recording");
1413 logf("pcmrec_stop done");
1414 } /* pcmrec_stop */
1416 /* PCMREC_PAUSE */
1417 static void pcmrec_pause(void)
1419 logf("pcmrec_pause");
1421 if (!is_recording)
1423 logf("not recording");
1425 else if (is_paused)
1427 logf("already paused");
1429 else
1431 dma_lock = true;
1432 is_paused = true;
1435 logf("pcmrec_pause done");
1436 } /* pcmrec_pause */
1438 /* PCMREC_RESUME */
1439 static void pcmrec_resume(void)
1441 logf("pcmrec_resume");
1443 if (!is_recording)
1445 logf("not recording");
1447 else if (!is_paused)
1449 logf("not paused");
1451 else
1453 is_paused = false;
1454 is_recording = true;
1455 dma_lock = false;
1458 logf("pcmrec_resume done");
1459 } /* pcmrec_resume */
1461 static void pcmrec_thread(void) __attribute__((noreturn));
1462 static void pcmrec_thread(void)
1464 struct queue_event ev;
1466 logf("thread pcmrec start");
1468 while(1)
1470 if (is_recording)
1472 /* Poll periodically to flush data */
1473 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1475 if (ev.id == SYS_TIMEOUT)
1477 /* Messages that interrupt this will complete it */
1478 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1479 PCMREC_FLUSH_INTERRUPTABLE);
1480 continue;
1483 else
1485 /* Not doing anything - sit and wait for commands */
1486 queue_wait(&pcmrec_queue, &ev);
1489 switch (ev.id)
1491 case PCMREC_INIT:
1492 pcmrec_init();
1493 break;
1495 case PCMREC_CLOSE:
1496 pcmrec_close();
1497 break;
1499 case PCMREC_OPTIONS:
1500 pcmrec_set_recording_options(
1501 (struct audio_recording_options *)ev.data);
1502 break;
1504 case PCMREC_RECORD:
1505 clear_flush_interrupt();
1506 pcmrec_record((const char *)ev.data);
1507 break;
1509 case PCMREC_STOP:
1510 clear_flush_interrupt();
1511 pcmrec_stop();
1512 break;
1514 case PCMREC_PAUSE:
1515 clear_flush_interrupt();
1516 pcmrec_pause();
1517 break;
1519 case PCMREC_RESUME:
1520 pcmrec_resume();
1521 break;
1522 #if 0
1523 case PCMREC_FLUSH_NUM:
1524 pcmrec_flush((unsigned)ev.data);
1525 break;
1526 #endif
1527 case SYS_USB_CONNECTED:
1528 if (is_recording)
1529 break;
1530 pcmrec_close();
1531 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1532 usb_wait_for_disconnect(&pcmrec_queue);
1533 flush_interrupts = 0;
1534 break;
1535 } /* end switch */
1536 } /* end while */
1537 } /* pcmrec_thread */
1539 /****************************************************************************/
1540 /* */
1541 /* following functions will be called by the encoder codec */
1542 /* in a free-threaded manner */
1543 /* */
1544 /****************************************************************************/
1546 /* pass the encoder settings to the encoder */
1547 void enc_get_inputs(struct enc_inputs *inputs)
1549 inputs->sample_rate = sample_rate;
1550 inputs->num_channels = num_channels;
1551 inputs->rec_mono_mode = rec_mono_mode;
1552 inputs->config = &enc_config;
1553 } /* enc_get_inputs */
1555 /* set the encoder dimensions (called by encoder codec at initialization and
1556 termination) */
1557 void enc_set_parameters(struct enc_parameters *params)
1559 size_t bufsize, resbytes;
1561 logf("enc_set_parameters");
1563 if (!params)
1565 logf("reset");
1566 /* Encoder is terminating */
1567 memset(&enc_config, 0, sizeof (enc_config));
1568 enc_sample_rate = 0;
1569 cancel_cpu_boost(); /* Make sure no boost remains */
1570 return;
1573 enc_sample_rate = params->enc_sample_rate;
1574 logf("enc sampr:%lu", enc_sample_rate);
1576 pcm_rd_pos = dma_wr_pos;
1577 pcm_enc_pos = pcm_rd_pos;
1579 enc_config.afmt = params->afmt;
1580 /* addition of the header is always implied - chunk size 4-byte aligned */
1581 enc_chunk_size =
1582 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1583 enc_events_callback = params->events_callback;
1585 logf("chunk size:%lu", enc_chunk_size);
1587 /*** Configure the buffers ***/
1589 /* Layout of recording buffer:
1590 * [ax] = possible alignment x multiple
1591 * [sx] = possible size alignment of x multiple
1592 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1593 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1595 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1596 logf("resbytes:%lu", resbytes);
1598 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1599 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1600 #ifdef DEBUG
1601 - sizeof (*wrap_id_p)
1602 #endif
1605 enc_num_chunks = bufsize / enc_chunk_size;
1606 logf("num chunks:%d", enc_num_chunks);
1608 /* get real amount used by encoder chunks */
1609 bufsize = enc_num_chunks*enc_chunk_size;
1610 logf("enc size:%lu", bufsize);
1612 #ifdef DEBUG
1613 /* add magic at wraparound for spillover checks */
1614 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1615 bufsize += sizeof (*wrap_id_p);
1616 *wrap_id_p = ENC_CHUNK_MAGIC;
1617 #endif
1619 /** set OUT parameters **/
1620 params->enc_buffer = enc_buffer;
1621 params->buf_chunk_size = enc_chunk_size;
1622 params->num_chunks = enc_num_chunks;
1624 /* calculate reserve buffer start and return pointer to encoder */
1625 params->reserve_buffer = NULL;
1626 if (resbytes > 0)
1628 params->reserve_buffer = enc_buffer + bufsize;
1629 bufsize += resbytes;
1632 /* place filename queue at end of buffer using up whatever remains */
1633 fnq_rd_pos = 0; /* reset */
1634 fnq_wr_pos = 0; /* reset */
1635 fn_queue = enc_buffer + bufsize;
1636 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1637 fnq_size /= MAX_PATH;
1638 if (fnq_size > FNQ_MAX_NUM_PATHS)
1639 fnq_size = FNQ_MAX_NUM_PATHS;
1640 fnq_size *= MAX_PATH;
1641 logf("fnq files:%ld", fnq_size / MAX_PATH);
1643 #if defined(DEBUG)
1644 logf("ab :%08lX", (uintptr_t)audiobuf);
1645 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1646 logf("enc:%08lX", (uintptr_t)enc_buffer);
1647 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1648 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1649 logf("fnq:%08lX", (uintptr_t)fn_queue);
1650 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1651 logf("abe:%08lX", (uintptr_t)audiobufend);
1652 #endif
1654 /* init all chunk headers and reset indexes */
1655 enc_rd_index = 0;
1656 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1658 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1659 #ifdef DEBUG
1660 chunk->id = ENC_CHUNK_MAGIC;
1661 #endif
1662 chunk->flags = 0;
1665 logf("enc_set_parameters done");
1666 } /* enc_set_parameters */
1668 /* return encoder chunk at current write position -
1669 NOTE: can be called by pcmrec thread when splitting streams */
1670 struct enc_chunk_hdr * enc_get_chunk(void)
1672 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1674 #ifdef DEBUG
1675 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1677 errors |= PCMREC_E_CHUNK_OVF;
1678 logf("finish chk ovf: %d", enc_wr_index);
1680 #endif
1682 chunk->flags &= CHUNKF_START_FILE;
1684 if (!is_recording)
1685 chunk->flags |= CHUNKF_PRERECORD;
1687 return chunk;
1688 } /* enc_get_chunk */
1690 /* releases the current chunk into the available chunks -
1691 NOTE: can be called by pcmrec thread when splitting streams */
1692 void enc_finish_chunk(void)
1694 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1696 if ((long)chunk->flags < 0)
1698 /* encoder set error flag */
1699 errors |= PCMREC_E_ENCODER;
1700 logf("finish chk enc error");
1703 /* advance enc_wr_index to the next encoder chunk */
1704 INC_ENC_INDEX(enc_wr_index);
1706 if (enc_rd_index != enc_wr_index)
1708 num_rec_bytes += chunk->enc_size;
1709 num_rec_samples += chunk->num_pcm;
1710 #if 0
1711 accum_rec_bytes += chunk->enc_size;
1712 accum_pcm_samples += chunk->num_pcm;
1713 #endif
1715 else if (is_recording) /* buffer full */
1717 /* keep current position and put up warning flag */
1718 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1719 logf("enc_buffer ovf");
1720 DEC_ENC_INDEX(enc_wr_index);
1721 if (pcmrec_context)
1723 /* if stream splitting, keep this out of circulation and
1724 flush a small number, then readd - cannot risk losing
1725 stream markers */
1726 logf("mini flush");
1727 pcmrec_flush(PCMREC_FLUSH_MINI);
1728 INC_ENC_INDEX(enc_wr_index);
1731 else
1733 /* advance enc_rd_index for prerecording */
1734 INC_ENC_INDEX(enc_rd_index);
1736 } /* enc_finish_chunk */
1738 /* passes a pointer to next chunk of unprocessed wav data */
1739 /* TODO: this really should give the actual size returned */
1740 unsigned char * enc_get_pcm_data(size_t size)
1742 int wp = dma_wr_pos;
1743 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1745 /* limit the requested pcm data size */
1746 if (size > PCM_MAX_FEED_SIZE)
1747 size = PCM_MAX_FEED_SIZE;
1749 if (avail >= size)
1751 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1752 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1754 pcm_enc_pos = pcm_rd_pos;
1755 pcm_rd_pos = next_pos;
1757 /* ptr must point to continous data at wraparound position */
1758 if ((size_t)pcm_rd_pos < size)
1760 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1761 pcm_buffer, pcm_rd_pos);
1764 if (avail >= (sample_rate << 2))
1766 /* Filling up - boost codec */
1767 trigger_cpu_boost();
1770 pcm_buffer_empty = false;
1771 return ptr;
1774 /* not enough data available - encoder should idle */
1775 pcm_buffer_empty = true;
1777 cancel_cpu_boost();
1779 /* Sleep long enough to allow one frame on average */
1780 sleep(0);
1782 return NULL;
1783 } /* enc_get_pcm_data */
1785 /* puts some pcm data back in the queue */
1786 size_t enc_unget_pcm_data(size_t size)
1788 int wp = dma_wr_pos;
1789 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1790 2*PCM_CHUNK_SIZE;
1792 /* allow one interrupt to occur during this call and not have the
1793 new read position inside the DMA destination chunk */
1794 if ((ssize_t)old_avail > 0)
1796 /* limit size to amount of old data remaining */
1797 if (size > old_avail)
1798 size = old_avail;
1800 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1801 pcm_rd_pos = pcm_enc_pos;
1803 return size;
1806 return 0;
1807 } /* enc_unget_pcm_data */