Correct setServiceActivity implementation
[maemo-rb.git] / apps / recorder / pcm_record.c
blob6ecb75f608a6fae60d0166919f663c40fa048d66
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 ****************************************************************************/
22 #include "config.h"
23 #include "gcc_extensions.h"
24 #include "pcm_record.h"
25 #include "system.h"
26 #include "kernel.h"
27 #include "logf.h"
28 #include "thread.h"
29 #include "string-extra.h"
30 #include "storage.h"
31 #include "usb.h"
32 #include "buffer.h"
33 #include "general.h"
34 #include "audio.h"
35 #include "sound.h"
36 #include "metadata.h"
37 #include "appevents.h"
38 #ifdef HAVE_SPDIF_IN
39 #include "spdif.h"
40 #endif
42 /***************************************************************************/
44 extern unsigned int codec_thread_id;
46 /** General recording state **/
47 static bool is_recording; /* We are recording */
48 static bool is_paused; /* We have paused */
49 static unsigned long errors; /* An error has occured */
50 static unsigned long warnings; /* Warning */
51 static int flush_interrupts = 0; /* Number of messages queued that
52 should interrupt a flush in
53 progress -
54 for a safety net and a prompt
55 response to stop, split and pause
56 requests -
57 only interrupts a flush initiated
58 by pcmrec_flush(0) */
60 /* Utility functions for setting/clearing flushing interrupt flag */
61 static inline void flush_interrupt(void)
63 flush_interrupts++;
64 logf("flush int: %d", flush_interrupts);
67 static inline void clear_flush_interrupt(void)
69 if (--flush_interrupts < 0)
70 flush_interrupts = 0;
73 /** Stats on encoded data for current file **/
74 static size_t num_rec_bytes; /* Num bytes recorded */
75 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
77 /** Stats on encoded data for all files from start to stop **/
78 #if 0
79 static unsigned long long accum_rec_bytes; /* total size written to chunks */
80 static unsigned long long accum_pcm_samples; /* total pcm count processed */
81 #endif
83 /* Keeps data about current file and is sent as event data for codec */
84 static struct enc_file_event_data rec_fdata IDATA_ATTR =
86 .chunk = NULL,
87 .new_enc_size = 0,
88 .new_num_pcm = 0,
89 .rec_file = -1,
90 .num_pcm_samples = 0
93 /** These apply to current settings **/
94 static int rec_source; /* current rec_source setting */
95 static int rec_frequency; /* current frequency setting */
96 static unsigned long sample_rate; /* Sample rate in HZ */
97 static int num_channels; /* Current number of channels */
98 static int rec_mono_mode; /* how mono is created */
99 static struct encoder_config enc_config; /* Current encoder configuration */
100 static unsigned long pre_record_ticks; /* pre-record time in ticks */
102 /****************************************************************************
103 use 2 circular buffers:
104 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
105 enc_buffer=encoded audio buffer: storage for encoder output data
107 Flow:
108 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
109 2. if enough pcm data are available the encoder codec does encoding of pcm
110 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
111 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
113 Functions calls (basic encoder steps):
114 1.main: audio_load_encoder(); start the encoder
115 2.encoder: enc_get_inputs(); get encoder recording settings
116 3.encoder: enc_set_parameters(); set the encoder parameters
117 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
118 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
119 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
120 7.encoder: <process enc chunk> compress and store data to enc chunk
121 8.encoder: enc_finish_chunk(); inform main about chunk processed and
122 is available to be written to a file.
123 Encoder can place any number of chunks
124 of PCM data in a single output chunk
125 but must stay within its output chunk
126 size
127 9.encoder: repeat 4. to 8.
128 A.pcmrec: enc_events_callback(); called for certain events
130 (*) Optional step
131 ****************************************************************************/
133 /** buffer parameters where incoming PCM data is placed **/
134 #if MEMORYSIZE <= 2
135 #define PCM_NUM_CHUNKS 16 /* Power of 2 */
136 #else
137 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
138 #endif
139 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
140 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
142 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
143 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
144 #define INC_ENC_INDEX(index) \
145 { if (++index >= enc_num_chunks) index = 0; }
146 #define DEC_ENC_INDEX(index) \
147 { if (--index < 0) index = enc_num_chunks - 1; }
149 static size_t rec_buffer_size; /* size of available buffer */
150 static unsigned char *pcm_buffer; /* circular recording buffer */
151 static unsigned char *enc_buffer; /* circular encoding buffer */
152 #ifdef DEBUG
153 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
154 aid to check if the encoder data
155 spilled out of its chunk */
156 #endif /* DEBUG */
157 static volatile int dma_wr_pos; /* current DMA write pos */
158 static int pcm_rd_pos; /* current PCM read pos */
159 static int pcm_enc_pos; /* position encoder is processing */
160 static volatile bool dma_lock; /* lock DMA write position */
161 static int enc_wr_index; /* encoder chunk write index */
162 static int enc_rd_index; /* encoder chunk read index */
163 static int enc_num_chunks; /* number of chunks in ringbuffer */
164 static size_t enc_chunk_size; /* maximum encoder chunk size */
165 static unsigned long enc_sample_rate; /* sample rate used by encoder */
166 static bool pcmrec_context = false; /* called by pcmrec thread? */
167 static bool pcm_buffer_empty; /* all pcm chunks processed? */
169 /** file flushing **/
170 static int low_watermark; /* Low watermark to stop flush */
171 static int high_watermark; /* max chunk limit for data flush */
172 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
173 static int last_storage_spinup_time = -1;/* previous spin time used */
174 #ifdef HAVE_PRIORITY_SCHEDULING
175 static int flood_watermark; /* boost thread priority when here */
176 #endif
178 /* Constants that control watermarks */
179 #define MINI_CHUNKS 10 /* chunk count for mini flush */
180 #ifdef HAVE_PRIORITY_SCHEDULING
181 #define PRIO_SECONDS 10 /* max flush time before priority boost */
182 #endif
183 #if MEMORYSIZE <= 2
184 /* fractions must be integer fractions of 4 because they are evaluated with
185 * X*4*XXX_SECONDS, that way we avoid float calculation */
186 #define LOW_SECONDS 1/4 /* low watermark time till empty */
187 #define PANIC_SECONDS 1/2 /* flood watermark time until full */
188 #define FLUSH_SECONDS 1 /* flush watermark time until full */
189 #elif MEMORYSIZE <= 16
190 #define LOW_SECONDS 1 /* low watermark time till empty */
191 #define PANIC_SECONDS 5 /* flood watermark time until full */
192 #define FLUSH_SECONDS 7 /* flush watermark time until full */
193 #else
194 #define LOW_SECONDS 1 /* low watermark time till empty */
195 #define PANIC_SECONDS 8
196 #define FLUSH_SECONDS 10
197 #endif /* MEMORYSIZE */
199 /** encoder events **/
200 static void (*enc_events_callback)(enum enc_events event, void *data);
202 /** Path queue for files to write **/
203 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
204 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
205 static unsigned char *fn_queue; /* pointer to first filename */
206 static ssize_t fnq_size; /* capacity of queue in bytes */
207 static int fnq_rd_pos; /* current read position */
208 static int fnq_wr_pos; /* current write position */
209 #define FNQ_NEXT(pos) \
210 ({ int p = (pos) + MAX_PATH; \
211 if (p >= fnq_size) \
212 p = 0; \
213 p; })
214 #define FNQ_PREV(pos) \
215 ({ int p = (pos) - MAX_PATH; \
216 if (p < 0) \
217 p = fnq_size - MAX_PATH; \
218 p; })
220 enum
222 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
223 incoming messages - combine
224 with other constants */
225 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
226 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
227 chunks */
228 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
229 reached */
232 /***************************************************************************/
234 static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
235 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
236 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
237 static const char pcmrec_thread_name[] = "pcmrec";
238 static unsigned int pcmrec_thread_id = 0;
240 static void pcmrec_thread(void);
242 enum
244 PCMREC_NULL = 0,
245 PCMREC_INIT, /* enable recording */
246 PCMREC_CLOSE, /* close recording */
247 PCMREC_OPTIONS, /* set recording options */
248 PCMREC_RECORD, /* record a new file */
249 PCMREC_STOP, /* stop the current recording */
250 PCMREC_PAUSE, /* pause the current recording */
251 PCMREC_RESUME, /* resume the current recording */
252 #if 0
253 PCMREC_FLUSH_NUM, /* flush a number of files out */
254 #endif
257 /*******************************************************************/
258 /* Functions that are not executing in the pcmrec_thread first */
259 /*******************************************************************/
261 /* Callback for when more data is ready - called in interrupt context */
262 static void pcm_rec_have_more(int status, void **start, size_t *size)
264 if (status < 0)
266 /* some error condition */
267 if (status == DMA_REC_ERROR_DMA)
269 /* Flush recorded data to disk and stop recording */
270 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
271 return;
273 /* else try again next transmission - frame is invalid */
275 else if (!dma_lock)
277 /* advance write position */
278 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
280 /* set pcm ovf if processing start position is inside current
281 write chunk */
282 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
283 warnings |= PCMREC_W_PCM_BUFFER_OVF;
285 dma_wr_pos = next_pos;
288 *start = GET_PCM_CHUNK(dma_wr_pos);
289 *size = PCM_CHUNK_SIZE;
290 } /* pcm_rec_have_more */
292 static void reset_hardware(void)
294 /* reset pcm to defaults */
295 pcm_set_frequency(REC_SAMPR_DEFAULT | SAMPR_TYPE_REC);
296 audio_set_output_source(AUDIO_SRC_PLAYBACK);
297 pcm_apply_settings();
300 /** pcm_rec_* group **/
303 * Clear all errors and warnings
305 void pcm_rec_error_clear(void)
307 errors = warnings = 0;
308 } /* pcm_rec_error_clear */
311 * Check mode, errors and warnings
313 unsigned long pcm_rec_status(void)
315 unsigned long ret = 0;
317 if (is_recording)
318 ret |= AUDIO_STATUS_RECORD;
319 else if (pre_record_ticks)
320 ret |= AUDIO_STATUS_PRERECORD;
322 if (is_paused)
323 ret |= AUDIO_STATUS_PAUSE;
325 if (errors)
326 ret |= AUDIO_STATUS_ERROR;
328 if (warnings)
329 ret |= AUDIO_STATUS_WARNING;
331 return ret;
332 } /* pcm_rec_status */
335 * Return warnings that have occured since recording started
337 unsigned long pcm_rec_get_warnings(void)
339 return warnings;
342 #if 0
343 int pcm_rec_current_bitrate(void)
345 if (accum_pcm_samples == 0)
346 return 0;
348 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
349 } /* pcm_rec_current_bitrate */
350 #endif
352 #if 0
353 int pcm_rec_encoder_afmt(void)
355 return enc_config.afmt;
356 } /* pcm_rec_encoder_afmt */
357 #endif
359 #if 0
360 int pcm_rec_rec_format(void)
362 return afmt_rec_format[enc_config.afmt];
363 } /* pcm_rec_rec_format */
364 #endif
366 #ifdef HAVE_SPDIF_IN
367 unsigned long pcm_rec_sample_rate(void)
369 /* Which is better ?? */
370 #if 0
371 return enc_sample_rate;
372 #endif
373 return sample_rate;
374 } /* audio_get_sample_rate */
375 #endif
378 * Creates pcmrec_thread
380 void pcm_rec_init(void)
382 queue_init(&pcmrec_queue, true);
383 pcmrec_thread_id =
384 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
385 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
386 IF_COP(, CPU));
387 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
388 pcmrec_thread_id);
389 } /* pcm_rec_init */
391 /** audio_* group **/
394 * Initializes recording - call before calling any other recording function
396 void audio_init_recording(unsigned int buffer_offset)
398 logf("audio_init_recording");
399 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
400 logf("audio_init_recording done");
401 (void)buffer_offset;
402 } /* audio_init_recording */
405 * Closes recording - call audio_stop_recording first
407 void audio_close_recording(void)
409 logf("audio_close_recording");
410 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
411 logf("audio_close_recording done");
412 } /* audio_close_recording */
415 * Sets recording parameters
417 void audio_set_recording_options(struct audio_recording_options *options)
419 logf("audio_set_recording_options");
420 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
421 logf("audio_set_recording_options done");
422 } /* audio_set_recording_options */
425 * Start recording if not recording or else split
427 void audio_record(const char *filename)
429 logf("audio_record: %s", filename);
430 flush_interrupt();
431 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
432 logf("audio_record_done");
433 } /* audio_record */
436 * audio_record wrapper for API compatibility with HW codec
438 void audio_new_file(const char *filename)
440 audio_record(filename);
441 } /* audio_new_file */
444 * Stop current recording if recording
446 void audio_stop_recording(void)
448 logf("audio_stop_recording");
449 flush_interrupt();
450 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
451 logf("audio_stop_recording done");
452 } /* audio_stop_recording */
455 * Pause current recording
457 void audio_pause_recording(void)
459 logf("audio_pause_recording");
460 flush_interrupt();
461 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
462 logf("audio_pause_recording done");
463 } /* audio_pause_recording */
466 * Resume current recording if paused
468 void audio_resume_recording(void)
470 logf("audio_resume_recording");
471 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
472 logf("audio_resume_recording done");
473 } /* audio_resume_recording */
476 * Note that microphone is mono, only left value is used
477 * See audiohw_set_recvol() for exact ranges.
479 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
482 void audio_set_recording_gain(int left, int right, int type)
484 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
485 audiohw_set_recvol(left, right, type);
486 } /* audio_set_recording_gain */
488 /** Information about current state **/
491 * Return current recorded time in ticks (playback eqivalent time)
493 unsigned long audio_recorded_time(void)
495 if (!is_recording || enc_sample_rate == 0)
496 return 0;
498 /* return actual recorded time a la encoded data even if encoder rate
499 doesn't match the pcm rate */
500 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
501 } /* audio_recorded_time */
504 * Return number of bytes encoded to output
506 unsigned long audio_num_recorded_bytes(void)
508 if (!is_recording)
509 return 0;
511 return num_rec_bytes;
512 } /* audio_num_recorded_bytes */
514 /***************************************************************************/
515 /* */
516 /* Functions that execute in the context of pcmrec_thread */
517 /* */
518 /***************************************************************************/
520 /** Filename Queue **/
522 /* returns true if the queue is empty */
523 static inline bool pcmrec_fnq_is_empty(void)
525 return fnq_rd_pos == fnq_wr_pos;
526 } /* pcmrec_fnq_is_empty */
528 /* empties the filename queue */
529 static inline void pcmrec_fnq_set_empty(void)
531 fnq_rd_pos = fnq_wr_pos;
532 } /* pcmrec_fnq_set_empty */
534 /* returns true if the queue is full */
535 static bool pcmrec_fnq_is_full(void)
537 ssize_t size = fnq_wr_pos - fnq_rd_pos;
538 if (size < 0)
539 size += fnq_size;
541 return size >= fnq_size - MAX_PATH;
542 } /* pcmrec_fnq_is_full */
544 /* queue another filename - will overwrite oldest one if full */
545 static bool pcmrec_fnq_add_filename(const char *filename)
547 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
548 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
550 if (fnq_rd_pos != fnq_wr_pos)
551 return true;
553 /* queue full */
554 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
555 return true;
556 } /* pcmrec_fnq_add_filename */
558 /* replace the last filename added */
559 static bool pcmrec_fnq_replace_tail(const char *filename)
561 int pos;
563 if (pcmrec_fnq_is_empty())
564 return false;
566 pos = FNQ_PREV(fnq_wr_pos);
568 strlcpy(fn_queue + pos, filename, MAX_PATH);
570 return true;
571 } /* pcmrec_fnq_replace_tail */
573 /* pulls the next filename from the queue */
574 static bool pcmrec_fnq_get_filename(char *filename)
576 if (pcmrec_fnq_is_empty())
577 return false;
579 if (filename)
580 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
582 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
583 return true;
584 } /* pcmrec_fnq_get_filename */
586 /* close the file number pointed to by fd_p */
587 static void pcmrec_close_file(int *fd_p)
589 if (*fd_p < 0)
590 return; /* preserve error */
592 if (close(*fd_p) != 0)
593 errors |= PCMREC_E_IO;
595 *fd_p = -1;
596 } /* pcmrec_close_file */
598 /** Data Flushing **/
601 * called after callback to update sizes if codec changed the amount of data
602 * a chunk represents
604 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
605 unsigned long prev_num_pcm)
607 if (rec_fdata.new_enc_size != prev_enc_size)
609 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
610 num_rec_bytes += size_diff;
611 #if 0
612 accum_rec_bytes += size_diff;
613 #endif
616 if (rec_fdata.new_num_pcm != prev_num_pcm)
618 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
619 num_rec_samples += pcm_diff;
620 #if 0
621 accum_pcm_samples += pcm_diff;
622 #endif
624 } /* pcmrec_update_sizes_inl */
626 /* don't need to inline every instance */
627 static void pcmrec_update_sizes(size_t prev_enc_size,
628 unsigned long prev_num_pcm)
630 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
631 } /* pcmrec_update_sizes */
633 static void pcmrec_start_file(void)
635 size_t enc_size = rec_fdata.new_enc_size;
636 unsigned long num_pcm = rec_fdata.new_num_pcm;
637 int curr_rec_file = rec_fdata.rec_file;
638 char filename[MAX_PATH];
640 /* must always pull the filename that matches with this queue */
641 if (!pcmrec_fnq_get_filename(filename))
643 logf("start file: fnq empty");
644 *filename = '\0';
645 errors |= PCMREC_E_FNQ_DESYNC;
647 else if (errors != 0)
649 logf("start file: error already");
651 else if (curr_rec_file >= 0)
653 /* Any previous file should have been closed */
654 logf("start file: file already open");
655 errors |= PCMREC_E_FNQ_DESYNC;
658 if (errors != 0)
659 rec_fdata.chunk->flags |= CHUNKF_ERROR;
661 /* encoder can set error flag here and should increase
662 enc_new_size and pcm_new_size to reflect additional
663 data written if any */
664 rec_fdata.filename = filename;
665 enc_events_callback(ENC_START_FILE, &rec_fdata);
667 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
669 logf("start file: enc error");
670 errors |= PCMREC_E_ENCODER;
673 if (errors != 0)
675 pcmrec_close_file(&curr_rec_file);
676 /* Write no more to this file */
677 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
679 else
681 pcmrec_update_sizes(enc_size, num_pcm);
684 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
685 } /* pcmrec_start_file */
687 static inline void pcmrec_write_chunk(void)
689 size_t enc_size = rec_fdata.new_enc_size;
690 unsigned long num_pcm = rec_fdata.new_num_pcm;
692 if (errors != 0)
693 rec_fdata.chunk->flags |= CHUNKF_ERROR;
695 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
697 if ((long)rec_fdata.chunk->flags >= 0)
699 pcmrec_update_sizes_inl(enc_size, num_pcm);
701 else if (errors == 0)
703 logf("wr chk enc error %lu %lu",
704 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
705 errors |= PCMREC_E_ENCODER;
707 } /* pcmrec_write_chunk */
709 static void pcmrec_end_file(void)
711 /* all data in output buffer for current file will have been
712 written and encoder can now do any nescessary steps to
713 finalize the written file */
714 size_t enc_size = rec_fdata.new_enc_size;
715 unsigned long num_pcm = rec_fdata.new_num_pcm;
717 enc_events_callback(ENC_END_FILE, &rec_fdata);
719 if (errors == 0)
721 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
723 logf("end file: enc error");
724 errors |= PCMREC_E_ENCODER;
726 else
728 pcmrec_update_sizes(enc_size, num_pcm);
732 /* Force file close if error */
733 if (errors != 0)
734 pcmrec_close_file(&rec_fdata.rec_file);
736 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
737 } /* pcmrec_end_file */
740 * Update buffer watermarks with spinup time compensation
742 * All this assumes reasonable data rates, chunk sizes and sufficient
743 * memory for the most part. Some dumb checks are included but perhaps
744 * are pointless since this all will break down at extreme limits that
745 * are currently not applicable to any supported device.
747 static void pcmrec_refresh_watermarks(void)
749 logf("ata spinup: %d", storage_spinup_time());
751 /* set the low mark for when flushing stops if automatic */
752 /* don't change the order in this expression, LOW_SECONDS can be an
753 * integer fraction of 4 */
754 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1))
755 / enc_chunk_size;
756 logf("low wmk: %d", low_watermark);
758 #ifdef HAVE_PRIORITY_SCHEDULING
759 /* panic boost thread priority if 2 seconds of ground is lost -
760 this allows encoder to boost with just under a second of
761 pcm data (if not yet full enough to boost itself)
762 and not falsely trip the alarm. */
763 /* don't change the order in this expression, PANIC_SECONDS can be an
764 * integer fraction of 4 */
765 flood_watermark = enc_num_chunks -
766 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
767 / enc_chunk_size;
769 if (flood_watermark < low_watermark)
771 logf("warning: panic < low");
772 flood_watermark = low_watermark;
775 logf("flood at: %d", flood_watermark);
776 #endif
777 spinup_time = last_storage_spinup_time = storage_spinup_time();
778 #if (CONFIG_STORAGE & STORAGE_ATA)
779 /* write at 8s + st remaining in enc_buffer - range 12s to
780 20s total - default to 3.5s spinup. */
781 if (spinup_time == 0)
782 spinup_time = 35*HZ/10; /* default - cozy */
783 else if (spinup_time < 2*HZ)
784 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
785 else if (spinup_time > 10*HZ)
786 spinup_time = 10*HZ; /* do you have a functioning HD? */
787 #endif
789 /* try to start writing with 10s remaining after disk spinup */
790 high_watermark = enc_num_chunks -
791 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
792 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
794 if (high_watermark < low_watermark)
796 logf("warning: low 'write at' (%d)", high_watermark);
797 high_watermark = low_watermark;
798 low_watermark /= 2;
801 logf("write at: %d", high_watermark);
802 } /* pcmrec_refresh_watermarks */
805 * Process the chunks
807 * This function is called when queue_get_w_tmo times out.
809 * Set flush_num to the number of files to flush to disk or to
810 * a PCMREC_FLUSH_* constant.
812 static void pcmrec_flush(unsigned flush_num)
814 #ifdef HAVE_PRIORITY_SCHEDULING
815 static unsigned long last_flush_tick; /* tick when function returned */
816 unsigned long start_tick; /* When flush started */
817 unsigned long prio_tick; /* Timeout for auto boost */
818 int prio_pcmrec; /* Current thread priority for pcmrec */
819 int prio_codec; /* Current thread priority for codec */
820 #endif
821 int num_ready; /* Number of chunks ready at start */
822 unsigned remaining; /* Number of file starts remaining */
823 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
824 bool interruptable; /* Flush can be interupted */
826 num_ready = enc_wr_index - enc_rd_index;
827 if (num_ready < 0)
828 num_ready += enc_num_chunks;
830 /* save interruptable flag and remove it to get the actual count */
831 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
832 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
834 if (flush_num == 0)
836 if (!is_recording)
837 return;
839 if (storage_spinup_time() != last_storage_spinup_time)
840 pcmrec_refresh_watermarks();
842 /* enough available? no? then leave */
843 if (num_ready < high_watermark)
844 return;
845 } /* endif (flush_num == 0) */
847 #ifdef HAVE_PRIORITY_SCHEDULING
848 start_tick = current_tick;
849 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
851 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
853 /* if we're getting called too much and this isn't forced,
854 boost stat by expiring timeout in advance */
855 logf("too frequent flush");
856 prio_tick = current_tick - 1;
859 prio_pcmrec = -1;
860 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
861 need init */
862 #endif
864 logf("writing:%d(%d):%s%s", num_ready, flush_num,
865 interruptable ? "i" : "",
866 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
868 cpu_boost(true);
870 remaining = flush_num;
871 chunks_flushed = 0;
873 while (num_ready > 0)
875 /* check current number of encoder chunks */
876 int num = enc_wr_index - enc_rd_index;
877 if (num < 0)
878 num += enc_num_chunks;
880 if (num <= low_watermark &&
881 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
883 logf("low data: %d", num);
884 break; /* data remaining is below threshold */
887 if (interruptable && flush_interrupts > 0)
889 logf("int at: %d", num);
890 break; /* interrupted */
893 #ifdef HAVE_PRIORITY_SCHEDULING
894 if (prio_pcmrec == -1 && (num >= flood_watermark ||
895 TIME_AFTER(current_tick, prio_tick)))
897 /* losing ground or holding without progress - boost
898 priority until finished */
899 logf("pcmrec: boost (%s)",
900 num >= flood_watermark ? "num" : "time");
901 prio_pcmrec = thread_set_priority(THREAD_ID_CURRENT,
902 thread_get_priority(THREAD_ID_CURRENT) - 4);
903 prio_codec = thread_set_priority(codec_thread_id,
904 thread_get_priority(codec_thread_id) - 4);
906 #endif
908 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
909 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
910 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
912 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
914 pcmrec_start_file();
915 if (--remaining == 0)
916 num_ready = 0; /* stop on next loop - must write this
917 chunk if it has data */
920 pcmrec_write_chunk();
922 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
923 pcmrec_end_file();
925 INC_ENC_INDEX(enc_rd_index);
927 if (errors != 0)
929 pcmrec_end_file();
930 break;
933 if (flush_num == PCMREC_FLUSH_MINI &&
934 ++chunks_flushed >= MINI_CHUNKS)
936 logf("mini flush break");
937 break;
939 /* no yielding; the file apis called in the codecs do that
940 sufficiently */
941 } /* end while */
943 /* sync file */
944 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
945 errors |= PCMREC_E_IO;
947 cpu_boost(false);
949 #ifdef HAVE_PRIORITY_SCHEDULING
950 if (prio_pcmrec != -1)
952 /* return to original priorities */
953 logf("pcmrec: unboost priority");
954 thread_set_priority(THREAD_ID_CURRENT, prio_pcmrec);
955 thread_set_priority(codec_thread_id, prio_codec);
958 last_flush_tick = current_tick; /* save tick when we left */
959 #endif
961 logf("done");
962 } /* pcmrec_flush */
965 * Marks a new stream in the buffer and gives the encoder a chance for special
966 * handling of transition from one to the next. The encoder may change the
967 * chunk that ends the old stream by requesting more chunks and similiarly for
968 * the new but must always advance the position though the interface. It can
969 * later reject any data it cares to when writing the file but should mark the
970 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
971 * a NULL data pointer without error as well.
973 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
975 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
976 } /* pcmrec_get_chunk_index */
978 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
980 DEC_ENC_INDEX(index);
981 return GET_ENC_CHUNK(index);
982 } /* pcmrec_get_prev_chunk */
984 static void pcmrec_new_stream(const char *filename, /* next file name */
985 unsigned long flags, /* CHUNKF_* flags */
986 int pre_index) /* index for prerecorded data */
988 logf("pcmrec_new_stream");
989 char path[MAX_PATH]; /* place to copy filename so sender can be released */
991 struct enc_buffer_event_data data;
992 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
993 new filename */
994 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
995 stream */
996 bool did_flush = false; /* did a flush occurr? */
998 if (filename)
999 strlcpy(path, filename, MAX_PATH);
1000 queue_reply(&pcmrec_queue, 0); /* We have all we need */
1002 data.pre_chunk = NULL;
1003 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1005 /* end chunk */
1006 if (flags & CHUNKF_END_FILE)
1008 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1010 if (data.chunk->flags & CHUNKF_START_FILE)
1012 /* cannot start and end on same unprocessed chunk */
1013 logf("file end on start");
1014 flags &= ~CHUNKF_END_FILE;
1016 else if (enc_rd_index == enc_wr_index)
1018 /* all data flushed but file not ended - chunk will be left
1019 empty */
1020 logf("end on dead end");
1021 data.chunk->flags = 0;
1022 data.chunk->enc_size = 0;
1023 data.chunk->num_pcm = 0;
1024 data.chunk->enc_data = NULL;
1025 INC_ENC_INDEX(enc_wr_index);
1026 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1028 else
1030 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1032 if (last->flags & CHUNKF_END_FILE)
1034 /* end already processed and marked - can't end twice */
1035 logf("file end again");
1036 flags &= ~CHUNKF_END_FILE;
1041 /* start chunk */
1042 if (flags & CHUNKF_START_FILE)
1044 bool pre = flags & CHUNKF_PRERECORD;
1046 if (pre)
1048 logf("stream prerecord start");
1049 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1050 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1052 else
1054 logf("stream normal start");
1055 start = data.chunk;
1056 start->flags &= CHUNKF_START_FILE;
1059 /* if encoder hasn't yet processed the last start - abort the start
1060 of the previous file queued or else it will be empty and invalid */
1061 if (start->flags & CHUNKF_START_FILE)
1063 logf("replacing fnq tail: %s", filename);
1064 fnq_add_fn = pcmrec_fnq_replace_tail;
1066 else
1068 logf("adding filename: %s", filename);
1069 fnq_add_fn = pcmrec_fnq_add_filename;
1073 data.flags = flags;
1074 pcmrec_context = true; /* switch encoder context */
1075 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1076 pcmrec_context = false; /* switch back */
1078 if (flags & CHUNKF_END_FILE)
1080 int i = pcmrec_get_chunk_index(data.chunk);
1081 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1084 if (start)
1086 if (!(flags & CHUNKF_PRERECORD))
1088 /* get stats on data added to start - sort of a prerecord
1089 operation */
1090 int i = pcmrec_get_chunk_index(data.chunk);
1091 struct enc_chunk_hdr *chunk = data.chunk;
1093 logf("start data: %d %d", i, enc_wr_index);
1095 num_rec_bytes = 0;
1096 num_rec_samples = 0;
1098 while (i != enc_wr_index)
1100 num_rec_bytes += chunk->enc_size;
1101 num_rec_samples += chunk->num_pcm;
1102 INC_ENC_INDEX(i);
1103 chunk = GET_ENC_CHUNK(i);
1106 start->flags &= ~CHUNKF_START_FILE;
1107 start = data.chunk;
1110 start->flags |= CHUNKF_START_FILE;
1112 /* flush all pending files out if full and adding */
1113 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1115 logf("fnq full");
1116 pcmrec_flush(PCMREC_FLUSH_ALL);
1117 did_flush = true;
1120 fnq_add_fn(path);
1123 /* Make sure to complete any interrupted high watermark */
1124 if (!did_flush)
1125 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1126 } /* pcmrec_new_stream */
1128 /** event handlers for pcmrec thread */
1130 /* PCMREC_INIT */
1131 static void pcmrec_init(void)
1133 unsigned char *buffer;
1134 send_event(RECORDING_EVENT_START, NULL);
1136 /* warings and errors */
1137 warnings =
1138 errors = 0;
1140 pcmrec_close_file(&rec_fdata.rec_file);
1141 rec_fdata.rec_file = -1;
1143 /* pcm FIFO */
1144 dma_lock = true;
1145 pcm_rd_pos = 0;
1146 dma_wr_pos = 0;
1147 pcm_enc_pos = 0;
1149 /* encoder FIFO */
1150 enc_wr_index = 0;
1151 enc_rd_index = 0;
1153 /* filename queue */
1154 fnq_rd_pos = 0;
1155 fnq_wr_pos = 0;
1157 /* stats */
1158 num_rec_bytes = 0;
1159 num_rec_samples = 0;
1160 #if 0
1161 accum_rec_bytes = 0;
1162 accum_pcm_samples = 0;
1163 #endif
1165 pre_record_ticks = 0;
1167 is_recording = false;
1168 is_paused = false;
1170 buffer = audio_get_recording_buffer(&rec_buffer_size);
1172 /* Line align pcm_buffer 2^5=32 bytes */
1173 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1174 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1175 PCM_MAX_FEED_SIZE, 2);
1176 /* Adjust available buffer for possible align advancement */
1177 rec_buffer_size -= pcm_buffer - buffer;
1179 pcm_init_recording();
1180 } /* pcmrec_init */
1182 /* PCMREC_CLOSE */
1183 static void pcmrec_close(void)
1185 dma_lock = true;
1186 pre_record_ticks = 0; /* Can't be prerecording any more */
1187 warnings = 0;
1188 pcm_close_recording();
1189 reset_hardware();
1190 audio_remove_encoder();
1191 send_event(RECORDING_EVENT_STOP, NULL);
1192 } /* pcmrec_close */
1194 /* PCMREC_OPTIONS */
1195 static void pcmrec_set_recording_options(
1196 struct audio_recording_options *options)
1198 /* stop DMA transfer */
1199 dma_lock = true;
1200 pcm_stop_recording();
1202 rec_frequency = options->rec_frequency;
1203 rec_source = options->rec_source;
1204 num_channels = options->rec_channels == 1 ? 1 : 2;
1205 rec_mono_mode = options->rec_mono_mode;
1206 pre_record_ticks = options->rec_prerecord_time * HZ;
1207 enc_config = options->enc_config;
1208 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1210 #ifdef HAVE_SPDIF_IN
1211 if (rec_source == AUDIO_SRC_SPDIF)
1213 /* must measure SPDIF sample rate before configuring codecs */
1214 unsigned long sr = spdif_measure_frequency();
1215 /* round to master list for SPDIF rate */
1216 int index = round_value_to_list32(sr, audio_master_sampr_list,
1217 SAMPR_NUM_FREQ, false);
1218 sample_rate = audio_master_sampr_list[index];
1219 /* round to HW playback rates for monitoring */
1220 index = round_value_to_list32(sr, hw_freq_sampr,
1221 HW_NUM_FREQ, false);
1222 pcm_set_frequency(hw_freq_sampr[index] | SAMPR_TYPE_REC);
1223 /* encoders with a limited number of rates do their own rounding */
1225 else
1226 #endif
1228 /* set sample rate from frequency selection */
1229 sample_rate = rec_freq_sampr[rec_frequency];
1230 pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1233 /* set monitoring */
1234 audio_set_output_source(rec_source);
1236 /* apply hardware setting to start monitoring now */
1237 pcm_apply_settings();
1239 queue_reply(&pcmrec_queue, 0); /* Release sender */
1241 if (audio_load_encoder(enc_config.afmt))
1243 /* start DMA transfer */
1244 dma_lock = pre_record_ticks == 0;
1245 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1246 PCM_CHUNK_SIZE);
1248 else
1250 logf("set rec opt: enc load failed");
1251 errors |= PCMREC_E_LOAD_ENCODER;
1253 } /* pcmrec_set_recording_options */
1255 /* PCMREC_RECORD - start recording (not gapless)
1256 or split stream (gapless) */
1257 static void pcmrec_record(const char *filename)
1259 unsigned long pre_sample_ticks;
1260 int rd_start;
1261 unsigned long flags;
1262 int pre_index;
1264 logf("pcmrec_record: %s", filename);
1266 /* reset stats */
1267 num_rec_bytes = 0;
1268 num_rec_samples = 0;
1270 if (!is_recording)
1272 #if 0
1273 accum_rec_bytes = 0;
1274 accum_pcm_samples = 0;
1275 #endif
1276 warnings = 0; /* reset warnings */
1278 rd_start = enc_wr_index;
1279 pre_sample_ticks = 0;
1281 pcmrec_refresh_watermarks();
1283 if (pre_record_ticks)
1285 int i = rd_start;
1286 /* calculate number of available chunks */
1287 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1288 enc_num_chunks) % enc_num_chunks;
1289 /* overflow at 974 seconds of prerecording at 44.1kHz */
1290 unsigned long pre_record_sample_ticks =
1291 enc_sample_rate*pre_record_ticks;
1292 int pre_chunks = 0; /* Counter to limit prerecorded time to
1293 prevent flood state at outset */
1295 logf("pre-st: %ld", pre_record_sample_ticks);
1297 /* Get exact measure of recorded data as number of samples aren't
1298 nescessarily going to be the max for each chunk */
1299 for (; avail_pre_chunks-- > 0;)
1301 struct enc_chunk_hdr *chunk;
1302 unsigned long chunk_sample_ticks;
1304 DEC_ENC_INDEX(i);
1306 chunk = GET_ENC_CHUNK(i);
1308 /* must have data to be counted */
1309 if (chunk->enc_data == NULL)
1310 continue;
1312 chunk_sample_ticks = chunk->num_pcm*HZ;
1314 rd_start = i;
1315 pre_sample_ticks += chunk_sample_ticks;
1316 num_rec_bytes += chunk->enc_size;
1317 num_rec_samples += chunk->num_pcm;
1318 pre_chunks++;
1320 /* stop here if enough already */
1321 if (pre_chunks >= high_watermark ||
1322 pre_sample_ticks >= pre_record_sample_ticks)
1324 logf("pre-chks: %d", pre_chunks);
1325 break;
1329 #if 0
1330 accum_rec_bytes = num_rec_bytes;
1331 accum_pcm_samples = num_rec_samples;
1332 #endif
1335 enc_rd_index = rd_start;
1337 /* filename queue should be empty */
1338 if (!pcmrec_fnq_is_empty())
1340 logf("fnq: not empty!");
1341 pcmrec_fnq_set_empty();
1344 flags = CHUNKF_START_FILE;
1345 if (pre_sample_ticks > 0)
1346 flags |= CHUNKF_PRERECORD;
1348 pre_index = enc_rd_index;
1350 dma_lock = false;
1351 is_paused = false;
1352 is_recording = true;
1354 else
1356 /* already recording, just split the stream */
1357 logf("inserting split");
1358 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1359 pre_index = 0;
1362 pcmrec_new_stream(filename, flags, pre_index);
1363 logf("pcmrec_record done");
1364 } /* pcmrec_record */
1366 /* PCMREC_STOP */
1367 static void pcmrec_stop(void)
1369 logf("pcmrec_stop");
1371 if (is_recording)
1373 dma_lock = true; /* lock dma write position */
1375 /* flush all available data first to avoid overflow while waiting
1376 for encoding to finish */
1377 pcmrec_flush(PCMREC_FLUSH_ALL);
1379 /* wait for encoder to finish remaining data */
1380 while (errors == 0 && !pcm_buffer_empty)
1381 yield();
1383 /* end stream at last data */
1384 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1386 /* flush anything else encoder added */
1387 pcmrec_flush(PCMREC_FLUSH_ALL);
1389 /* remove any pending file start not yet processed - should be at
1390 most one at enc_wr_index */
1391 pcmrec_fnq_get_filename(NULL);
1392 /* encoder should abort any chunk it was in midst of processing */
1393 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1395 /* filename queue should be empty */
1396 if (!pcmrec_fnq_is_empty())
1398 logf("fnq: not empty!");
1399 pcmrec_fnq_set_empty();
1402 /* be absolutely sure the file is closed */
1403 if (errors != 0)
1404 pcmrec_close_file(&rec_fdata.rec_file);
1405 rec_fdata.rec_file = -1;
1407 is_recording = false;
1408 is_paused = false;
1409 dma_lock = pre_record_ticks == 0;
1411 else
1413 logf("not recording");
1416 logf("pcmrec_stop done");
1417 } /* pcmrec_stop */
1419 /* PCMREC_PAUSE */
1420 static void pcmrec_pause(void)
1422 logf("pcmrec_pause");
1424 if (!is_recording)
1426 logf("not recording");
1428 else if (is_paused)
1430 logf("already paused");
1432 else
1434 dma_lock = true;
1435 is_paused = true;
1438 logf("pcmrec_pause done");
1439 } /* pcmrec_pause */
1441 /* PCMREC_RESUME */
1442 static void pcmrec_resume(void)
1444 logf("pcmrec_resume");
1446 if (!is_recording)
1448 logf("not recording");
1450 else if (!is_paused)
1452 logf("not paused");
1454 else
1456 is_paused = false;
1457 is_recording = true;
1458 dma_lock = false;
1461 logf("pcmrec_resume done");
1462 } /* pcmrec_resume */
1464 static void pcmrec_thread(void) NORETURN_ATTR;
1465 static void pcmrec_thread(void)
1467 struct queue_event ev;
1469 logf("thread pcmrec start");
1471 while(1)
1473 if (is_recording)
1475 /* Poll periodically to flush data */
1476 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1478 if (ev.id == SYS_TIMEOUT)
1480 /* Messages that interrupt this will complete it */
1481 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1482 PCMREC_FLUSH_INTERRUPTABLE);
1483 continue;
1486 else
1488 /* Not doing anything - sit and wait for commands */
1489 queue_wait(&pcmrec_queue, &ev);
1492 switch (ev.id)
1494 case PCMREC_INIT:
1495 pcmrec_init();
1496 break;
1498 case PCMREC_CLOSE:
1499 pcmrec_close();
1500 break;
1502 case PCMREC_OPTIONS:
1503 pcmrec_set_recording_options(
1504 (struct audio_recording_options *)ev.data);
1505 break;
1507 case PCMREC_RECORD:
1508 clear_flush_interrupt();
1509 pcmrec_record((const char *)ev.data);
1510 break;
1512 case PCMREC_STOP:
1513 clear_flush_interrupt();
1514 pcmrec_stop();
1515 break;
1517 case PCMREC_PAUSE:
1518 clear_flush_interrupt();
1519 pcmrec_pause();
1520 break;
1522 case PCMREC_RESUME:
1523 pcmrec_resume();
1524 break;
1525 #if 0
1526 case PCMREC_FLUSH_NUM:
1527 pcmrec_flush((unsigned)ev.data);
1528 break;
1529 #endif
1530 case SYS_USB_CONNECTED:
1531 if (is_recording)
1532 break;
1533 pcmrec_close();
1534 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1535 usb_wait_for_disconnect(&pcmrec_queue);
1536 flush_interrupts = 0;
1537 break;
1538 } /* end switch */
1539 } /* end while */
1540 } /* pcmrec_thread */
1542 /****************************************************************************/
1543 /* */
1544 /* following functions will be called by the encoder codec */
1545 /* in a free-threaded manner */
1546 /* */
1547 /****************************************************************************/
1549 /* pass the encoder settings to the encoder */
1550 void enc_get_inputs(struct enc_inputs *inputs)
1552 inputs->sample_rate = sample_rate;
1553 inputs->num_channels = num_channels;
1554 inputs->rec_mono_mode = rec_mono_mode;
1555 inputs->config = &enc_config;
1556 } /* enc_get_inputs */
1558 /* set the encoder dimensions (called by encoder codec at initialization and
1559 termination) */
1560 void enc_set_parameters(struct enc_parameters *params)
1562 size_t bufsize, resbytes;
1564 logf("enc_set_parameters");
1566 if (!params)
1568 logf("reset");
1569 /* Encoder is terminating */
1570 memset(&enc_config, 0, sizeof (enc_config));
1571 enc_sample_rate = 0;
1572 cancel_cpu_boost(); /* Make sure no boost remains */
1573 return;
1576 enc_sample_rate = params->enc_sample_rate;
1577 logf("enc sampr:%lu", enc_sample_rate);
1579 pcm_rd_pos = dma_wr_pos;
1580 pcm_enc_pos = pcm_rd_pos;
1582 enc_config.afmt = params->afmt;
1583 /* addition of the header is always implied - chunk size 4-byte aligned */
1584 enc_chunk_size =
1585 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1586 enc_events_callback = params->events_callback;
1588 logf("chunk size:%lu", enc_chunk_size);
1590 /*** Configure the buffers ***/
1592 /* Layout of recording buffer:
1593 * [ax] = possible alignment x multiple
1594 * [sx] = possible size alignment of x multiple
1595 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1596 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1598 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1599 logf("resbytes:%lu", resbytes);
1601 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1602 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1603 #ifdef DEBUG
1604 - sizeof (*wrap_id_p)
1605 #endif
1608 enc_num_chunks = bufsize / enc_chunk_size;
1609 logf("num chunks:%d", enc_num_chunks);
1611 /* get real amount used by encoder chunks */
1612 bufsize = enc_num_chunks*enc_chunk_size;
1613 logf("enc size:%lu", bufsize);
1615 #ifdef DEBUG
1616 /* add magic at wraparound for spillover checks */
1617 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1618 bufsize += sizeof (*wrap_id_p);
1619 *wrap_id_p = ENC_CHUNK_MAGIC;
1620 #endif
1622 /** set OUT parameters **/
1623 params->enc_buffer = enc_buffer;
1624 params->buf_chunk_size = enc_chunk_size;
1625 params->num_chunks = enc_num_chunks;
1627 /* calculate reserve buffer start and return pointer to encoder */
1628 params->reserve_buffer = NULL;
1629 if (resbytes > 0)
1631 params->reserve_buffer = enc_buffer + bufsize;
1632 bufsize += resbytes;
1635 /* place filename queue at end of buffer using up whatever remains */
1636 fnq_rd_pos = 0; /* reset */
1637 fnq_wr_pos = 0; /* reset */
1638 fn_queue = enc_buffer + bufsize;
1639 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1640 fnq_size /= MAX_PATH;
1641 if (fnq_size > FNQ_MAX_NUM_PATHS)
1642 fnq_size = FNQ_MAX_NUM_PATHS;
1643 fnq_size *= MAX_PATH;
1644 logf("fnq files:%ld", fnq_size / MAX_PATH);
1646 #if defined(DEBUG)
1647 logf("ab :%08lX", (uintptr_t)audiobuf);
1648 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1649 logf("enc:%08lX", (uintptr_t)enc_buffer);
1650 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1651 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1652 logf("fnq:%08lX", (uintptr_t)fn_queue);
1653 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1654 logf("abe:%08lX", (uintptr_t)audiobufend);
1655 #endif
1657 /* init all chunk headers and reset indexes */
1658 enc_rd_index = 0;
1659 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1661 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1662 #ifdef DEBUG
1663 chunk->id = ENC_CHUNK_MAGIC;
1664 #endif
1665 chunk->flags = 0;
1668 logf("enc_set_parameters done");
1669 } /* enc_set_parameters */
1671 /* return encoder chunk at current write position -
1672 NOTE: can be called by pcmrec thread when splitting streams */
1673 struct enc_chunk_hdr * enc_get_chunk(void)
1675 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1677 #ifdef DEBUG
1678 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1680 errors |= PCMREC_E_CHUNK_OVF;
1681 logf("finish chk ovf: %d", enc_wr_index);
1683 #endif
1685 chunk->flags &= CHUNKF_START_FILE;
1687 if (!is_recording)
1688 chunk->flags |= CHUNKF_PRERECORD;
1690 return chunk;
1691 } /* enc_get_chunk */
1693 /* releases the current chunk into the available chunks -
1694 NOTE: can be called by pcmrec thread when splitting streams */
1695 void enc_finish_chunk(void)
1697 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1699 if ((long)chunk->flags < 0)
1701 /* encoder set error flag */
1702 errors |= PCMREC_E_ENCODER;
1703 logf("finish chk enc error");
1706 /* advance enc_wr_index to the next encoder chunk */
1707 INC_ENC_INDEX(enc_wr_index);
1709 if (enc_rd_index != enc_wr_index)
1711 num_rec_bytes += chunk->enc_size;
1712 num_rec_samples += chunk->num_pcm;
1713 #if 0
1714 accum_rec_bytes += chunk->enc_size;
1715 accum_pcm_samples += chunk->num_pcm;
1716 #endif
1718 else if (is_recording) /* buffer full */
1720 /* keep current position and put up warning flag */
1721 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1722 logf("enc_buffer ovf");
1723 DEC_ENC_INDEX(enc_wr_index);
1724 if (pcmrec_context)
1726 /* if stream splitting, keep this out of circulation and
1727 flush a small number, then readd - cannot risk losing
1728 stream markers */
1729 logf("mini flush");
1730 pcmrec_flush(PCMREC_FLUSH_MINI);
1731 INC_ENC_INDEX(enc_wr_index);
1734 else
1736 /* advance enc_rd_index for prerecording */
1737 INC_ENC_INDEX(enc_rd_index);
1739 } /* enc_finish_chunk */
1741 /* passes a pointer to next chunk of unprocessed wav data */
1742 /* TODO: this really should give the actual size returned */
1743 unsigned char * enc_get_pcm_data(size_t size)
1745 int wp = dma_wr_pos;
1746 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1748 /* limit the requested pcm data size */
1749 if (size > PCM_MAX_FEED_SIZE)
1750 size = PCM_MAX_FEED_SIZE;
1752 if (avail >= size)
1754 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1755 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1757 pcm_enc_pos = pcm_rd_pos;
1758 pcm_rd_pos = next_pos;
1760 /* ptr must point to continous data at wraparound position */
1761 if ((size_t)pcm_rd_pos < size)
1763 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1764 pcm_buffer, pcm_rd_pos);
1767 if (avail >= (sample_rate << 2))
1769 /* Filling up - boost codec */
1770 trigger_cpu_boost();
1773 pcm_buffer_empty = false;
1774 return ptr;
1777 /* not enough data available - encoder should idle */
1778 pcm_buffer_empty = true;
1780 cancel_cpu_boost();
1782 /* Sleep long enough to allow one frame on average */
1783 sleep(0);
1785 return NULL;
1786 } /* enc_get_pcm_data */
1788 /* puts some pcm data back in the queue */
1789 size_t enc_unget_pcm_data(size_t size)
1791 int wp = dma_wr_pos;
1792 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1793 2*PCM_CHUNK_SIZE;
1795 /* allow one interrupt to occur during this call and not have the
1796 new read position inside the DMA destination chunk */
1797 if ((ssize_t)old_avail > 0)
1799 /* limit size to amount of old data remaining */
1800 if (size > old_avail)
1801 size = old_avail;
1803 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1804 pcm_rd_pos = pcm_enc_pos;
1806 return size;
1809 return 0;
1810 } /* enc_unget_pcm_data */