Bump version numbers for 3.13
[maemo-rb.git] / apps / recorder / pcm_record.c
blobd904be9a4e640930432967b417b919ad4ea477cc
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 "general.h"
33 #include "codec_thread.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 /** General recording state **/
45 static bool is_recording; /* We are recording */
46 static bool is_paused; /* We have paused */
47 static unsigned long errors; /* An error has occured */
48 static unsigned long warnings; /* Warning */
49 static int flush_interrupts = 0; /* Number of messages queued that
50 should interrupt a flush in
51 progress -
52 for a safety net and a prompt
53 response to stop, split and pause
54 requests -
55 only interrupts a flush initiated
56 by pcmrec_flush(0) */
58 /* Utility functions for setting/clearing flushing interrupt flag */
59 static inline void flush_interrupt(void)
61 flush_interrupts++;
62 logf("flush int: %d", flush_interrupts);
65 static inline void clear_flush_interrupt(void)
67 if (--flush_interrupts < 0)
68 flush_interrupts = 0;
71 /** Stats on encoded data for current file **/
72 static size_t num_rec_bytes; /* Num bytes recorded */
73 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
75 /** Stats on encoded data for all files from start to stop **/
76 #if 0
77 static unsigned long long accum_rec_bytes; /* total size written to chunks */
78 static unsigned long long accum_pcm_samples; /* total pcm count processed */
79 #endif
81 /* Keeps data about current file and is sent as event data for codec */
82 static struct enc_file_event_data rec_fdata IDATA_ATTR =
84 .chunk = NULL,
85 .new_enc_size = 0,
86 .new_num_pcm = 0,
87 .rec_file = -1,
88 .num_pcm_samples = 0
91 /** These apply to current settings **/
92 static int rec_source; /* current rec_source setting */
93 static int rec_frequency; /* current frequency setting */
94 static unsigned long sample_rate; /* Sample rate in HZ */
95 static int num_channels; /* Current number of channels */
96 static int rec_mono_mode; /* how mono is created */
97 static struct encoder_config enc_config; /* Current encoder configuration */
98 static unsigned long pre_record_ticks; /* pre-record time in ticks */
100 /****************************************************************************
101 use 2 circular buffers:
102 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
103 enc_buffer=encoded audio buffer: storage for encoder output data
105 Flow:
106 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
107 2. if enough pcm data are available the encoder codec does encoding of pcm
108 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
109 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
111 Functions calls (basic encoder steps):
112 1.main: audio_load_encoder(); start the encoder
113 2.encoder: enc_get_inputs(); get encoder recording settings
114 3.encoder: enc_set_parameters(); set the encoder parameters
115 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
116 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
117 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
118 7.encoder: <process enc chunk> compress and store data to enc chunk
119 8.encoder: enc_finish_chunk(); inform main about chunk processed and
120 is available to be written to a file.
121 Encoder can place any number of chunks
122 of PCM data in a single output chunk
123 but must stay within its output chunk
124 size
125 9.encoder: repeat 4. to 8.
126 A.pcmrec: enc_events_callback(); called for certain events
128 (*) Optional step
129 ****************************************************************************/
131 /** buffer parameters where incoming PCM data is placed **/
132 #if MEMORYSIZE <= 2
133 #define PCM_NUM_CHUNKS 16 /* Power of 2 */
134 #else
135 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
136 #endif
137 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
138 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
140 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
141 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
142 #define INC_ENC_INDEX(index) \
143 { if (++index >= enc_num_chunks) index = 0; }
144 #define DEC_ENC_INDEX(index) \
145 { if (--index < 0) index = enc_num_chunks - 1; }
147 static size_t rec_buffer_size; /* size of available buffer */
148 static unsigned char *pcm_buffer; /* circular recording buffer */
149 static unsigned char *enc_buffer; /* circular encoding buffer */
150 #ifdef DEBUG
151 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
152 aid to check if the encoder data
153 spilled out of its chunk */
154 #endif /* DEBUG */
155 static volatile int dma_wr_pos; /* current DMA write pos */
156 static int pcm_rd_pos; /* current PCM read pos */
157 static int pcm_enc_pos; /* position encoder is processing */
158 static volatile bool dma_lock; /* lock DMA write position */
159 static int enc_wr_index; /* encoder chunk write index */
160 static int enc_rd_index; /* encoder chunk read index */
161 static int enc_num_chunks; /* number of chunks in ringbuffer */
162 static size_t enc_chunk_size; /* maximum encoder chunk size */
163 static unsigned long enc_sample_rate; /* sample rate used by encoder */
164 static bool pcmrec_context = false; /* called by pcmrec thread? */
165 static bool pcm_buffer_empty; /* all pcm chunks processed? */
167 /** file flushing **/
168 static int low_watermark; /* Low watermark to stop flush */
169 static int high_watermark; /* max chunk limit for data flush */
170 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
171 static int last_storage_spinup_time = -1;/* previous spin time used */
172 #ifdef HAVE_PRIORITY_SCHEDULING
173 static int flood_watermark; /* boost thread priority when here */
174 #endif
176 /* Constants that control watermarks */
177 #define MINI_CHUNKS 10 /* chunk count for mini flush */
178 #ifdef HAVE_PRIORITY_SCHEDULING
179 #define PRIO_SECONDS 10 /* max flush time before priority boost */
180 #endif
181 #if MEMORYSIZE <= 2
182 /* fractions must be integer fractions of 4 because they are evaluated with
183 * X*4*XXX_SECONDS, that way we avoid float calculation */
184 #define LOW_SECONDS 1/4 /* low watermark time till empty */
185 #define PANIC_SECONDS 1/2 /* flood watermark time until full */
186 #define FLUSH_SECONDS 1 /* flush watermark time until full */
187 #elif MEMORYSIZE <= 16
188 #define LOW_SECONDS 1 /* low watermark time till empty */
189 #define PANIC_SECONDS 5 /* flood watermark time until full */
190 #define FLUSH_SECONDS 7 /* flush watermark time until full */
191 #else
192 #define LOW_SECONDS 1 /* low watermark time till empty */
193 #define PANIC_SECONDS 8
194 #define FLUSH_SECONDS 10
195 #endif /* MEMORYSIZE */
197 /** encoder events **/
198 static void (*enc_events_callback)(enum enc_events event, void *data);
200 /** Path queue for files to write **/
201 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
202 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
203 static unsigned char *fn_queue; /* pointer to first filename */
204 static ssize_t fnq_size; /* capacity of queue in bytes */
205 static int fnq_rd_pos; /* current read position */
206 static int fnq_wr_pos; /* current write position */
207 #define FNQ_NEXT(pos) \
208 ({ int p = (pos) + MAX_PATH; \
209 if (p >= fnq_size) \
210 p = 0; \
211 p; })
212 #define FNQ_PREV(pos) \
213 ({ int p = (pos) - MAX_PATH; \
214 if (p < 0) \
215 p = fnq_size - MAX_PATH; \
216 p; })
218 enum
220 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
221 incoming messages - combine
222 with other constants */
223 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
224 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
225 chunks */
226 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
227 reached */
230 /***************************************************************************/
232 static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
233 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
234 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
235 static const char pcmrec_thread_name[] = "pcmrec";
236 static unsigned int pcmrec_thread_id = 0;
238 static void pcmrec_thread(void);
240 enum
242 PCMREC_NULL = 0,
243 PCMREC_INIT, /* enable recording */
244 PCMREC_CLOSE, /* close recording */
245 PCMREC_OPTIONS, /* set recording options */
246 PCMREC_RECORD, /* record a new file */
247 PCMREC_STOP, /* stop the current recording */
248 PCMREC_PAUSE, /* pause the current recording */
249 PCMREC_RESUME, /* resume the current recording */
250 #if 0
251 PCMREC_FLUSH_NUM, /* flush a number of files out */
252 #endif
255 /*******************************************************************/
256 /* Functions that are not executing in the pcmrec_thread first */
257 /*******************************************************************/
259 /* Callback for when more data is ready - called in interrupt context */
260 static void pcm_rec_have_more(void **start, size_t *size)
262 if (!dma_lock)
264 /* advance write position */
265 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
267 /* set pcm ovf if processing start position is inside current
268 write chunk */
269 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
270 warnings |= PCMREC_W_PCM_BUFFER_OVF;
272 dma_wr_pos = next_pos;
275 *start = GET_PCM_CHUNK(dma_wr_pos);
276 *size = PCM_CHUNK_SIZE;
277 } /* pcm_rec_have_more */
279 static enum pcm_dma_status pcm_rec_status_callback(enum pcm_dma_status status)
281 if (status < PCM_DMAST_OK)
283 /* some error condition */
284 if (status == PCM_DMAST_ERR_DMA)
286 /* Flush recorded data to disk and stop recording */
287 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
288 return status;
290 /* else try again next transmission - frame is invalid */
293 return PCM_DMAST_OK;
294 } /* pcm_rec_status_callback */
296 static void reset_hardware(void)
298 /* reset pcm to defaults */
299 pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC);
300 audio_set_output_source(AUDIO_SRC_PLAYBACK);
301 pcm_apply_settings();
304 /** pcm_rec_* group **/
307 * Clear all errors and warnings
309 void pcm_rec_error_clear(void)
311 errors = warnings = 0;
312 } /* pcm_rec_error_clear */
315 * Check mode, errors and warnings
317 unsigned long pcm_rec_status(void)
319 unsigned long ret = 0;
321 if (is_recording)
322 ret |= AUDIO_STATUS_RECORD;
323 else if (pre_record_ticks)
324 ret |= AUDIO_STATUS_PRERECORD;
326 if (is_paused)
327 ret |= AUDIO_STATUS_PAUSE;
329 if (errors)
330 ret |= AUDIO_STATUS_ERROR;
332 if (warnings)
333 ret |= AUDIO_STATUS_WARNING;
335 return ret;
336 } /* pcm_rec_status */
339 * Return warnings that have occured since recording started
341 unsigned long pcm_rec_get_warnings(void)
343 return warnings;
346 #if 0
347 int pcm_rec_current_bitrate(void)
349 if (accum_pcm_samples == 0)
350 return 0;
352 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
353 } /* pcm_rec_current_bitrate */
354 #endif
356 #if 0
357 int pcm_rec_encoder_afmt(void)
359 return enc_config.afmt;
360 } /* pcm_rec_encoder_afmt */
361 #endif
363 #if 0
364 int pcm_rec_rec_format(void)
366 return afmt_rec_format[enc_config.afmt];
367 } /* pcm_rec_rec_format */
368 #endif
370 #ifdef HAVE_SPDIF_IN
371 unsigned long pcm_rec_sample_rate(void)
373 /* Which is better ?? */
374 #if 0
375 return enc_sample_rate;
376 #endif
377 return sample_rate;
378 } /* audio_get_sample_rate */
379 #endif
382 * Creates pcmrec_thread
384 void pcm_rec_init(void)
386 queue_init(&pcmrec_queue, true);
387 pcmrec_thread_id =
388 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
389 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
390 IF_COP(, CPU));
391 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
392 pcmrec_thread_id);
393 } /* pcm_rec_init */
395 /** audio_* group **/
398 * Initializes recording - call before calling any other recording function
400 void audio_init_recording(void)
402 logf("audio_init_recording");
403 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
404 logf("audio_init_recording done");
405 } /* audio_init_recording */
408 * Closes recording - call audio_stop_recording first
410 void audio_close_recording(void)
412 logf("audio_close_recording");
413 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
414 logf("audio_close_recording done");
415 } /* audio_close_recording */
418 * Sets recording parameters
420 void audio_set_recording_options(struct audio_recording_options *options)
422 logf("audio_set_recording_options");
423 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
424 logf("audio_set_recording_options done");
425 } /* audio_set_recording_options */
428 * Start recording if not recording or else split
430 void audio_record(const char *filename)
432 logf("audio_record: %s", filename);
433 flush_interrupt();
434 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
435 logf("audio_record_done");
436 } /* audio_record */
439 * audio_record wrapper for API compatibility with HW codec
441 void audio_new_file(const char *filename)
443 audio_record(filename);
444 } /* audio_new_file */
447 * Stop current recording if recording
449 void audio_stop_recording(void)
451 logf("audio_stop_recording");
452 flush_interrupt();
453 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
454 logf("audio_stop_recording done");
455 } /* audio_stop_recording */
458 * Pause current recording
460 void audio_pause_recording(void)
462 logf("audio_pause_recording");
463 flush_interrupt();
464 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
465 logf("audio_pause_recording done");
466 } /* audio_pause_recording */
469 * Resume current recording if paused
471 void audio_resume_recording(void)
473 logf("audio_resume_recording");
474 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
475 logf("audio_resume_recording done");
476 } /* audio_resume_recording */
479 * Note that microphone is mono, only left value is used
480 * See audiohw_set_recvol() for exact ranges.
482 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
485 void audio_set_recording_gain(int left, int right, int type)
487 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
488 audiohw_set_recvol(left, right, type);
489 } /* audio_set_recording_gain */
491 /** Information about current state **/
494 * Return current recorded time in ticks (playback eqivalent time)
496 unsigned long audio_recorded_time(void)
498 if (!is_recording || enc_sample_rate == 0)
499 return 0;
501 /* return actual recorded time a la encoded data even if encoder rate
502 doesn't match the pcm rate */
503 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
504 } /* audio_recorded_time */
507 * Return number of bytes encoded to output
509 unsigned long audio_num_recorded_bytes(void)
511 if (!is_recording)
512 return 0;
514 return num_rec_bytes;
515 } /* audio_num_recorded_bytes */
517 /***************************************************************************/
518 /* */
519 /* Functions that execute in the context of pcmrec_thread */
520 /* */
521 /***************************************************************************/
523 /** Filename Queue **/
525 /* returns true if the queue is empty */
526 static inline bool pcmrec_fnq_is_empty(void)
528 return fnq_rd_pos == fnq_wr_pos;
529 } /* pcmrec_fnq_is_empty */
531 /* empties the filename queue */
532 static inline void pcmrec_fnq_set_empty(void)
534 fnq_rd_pos = fnq_wr_pos;
535 } /* pcmrec_fnq_set_empty */
537 /* returns true if the queue is full */
538 static bool pcmrec_fnq_is_full(void)
540 ssize_t size = fnq_wr_pos - fnq_rd_pos;
541 if (size < 0)
542 size += fnq_size;
544 return size >= fnq_size - MAX_PATH;
545 } /* pcmrec_fnq_is_full */
547 /* queue another filename - will overwrite oldest one if full */
548 static bool pcmrec_fnq_add_filename(const char *filename)
550 strlcpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
551 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
553 if (fnq_rd_pos != fnq_wr_pos)
554 return true;
556 /* queue full */
557 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
558 return true;
559 } /* pcmrec_fnq_add_filename */
561 /* replace the last filename added */
562 static bool pcmrec_fnq_replace_tail(const char *filename)
564 int pos;
566 if (pcmrec_fnq_is_empty())
567 return false;
569 pos = FNQ_PREV(fnq_wr_pos);
571 strlcpy(fn_queue + pos, filename, MAX_PATH);
573 return true;
574 } /* pcmrec_fnq_replace_tail */
576 /* pulls the next filename from the queue */
577 static bool pcmrec_fnq_get_filename(char *filename)
579 if (pcmrec_fnq_is_empty())
580 return false;
582 if (filename)
583 strlcpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
585 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
586 return true;
587 } /* pcmrec_fnq_get_filename */
589 /* close the file number pointed to by fd_p */
590 static void pcmrec_close_file(int *fd_p)
592 if (*fd_p < 0)
593 return; /* preserve error */
595 if (close(*fd_p) != 0)
596 errors |= PCMREC_E_IO;
598 *fd_p = -1;
599 } /* pcmrec_close_file */
601 /** Data Flushing **/
604 * called after callback to update sizes if codec changed the amount of data
605 * a chunk represents
607 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
608 unsigned long prev_num_pcm)
610 if (rec_fdata.new_enc_size != prev_enc_size)
612 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
613 num_rec_bytes += size_diff;
614 #if 0
615 accum_rec_bytes += size_diff;
616 #endif
619 if (rec_fdata.new_num_pcm != prev_num_pcm)
621 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
622 num_rec_samples += pcm_diff;
623 #if 0
624 accum_pcm_samples += pcm_diff;
625 #endif
627 } /* pcmrec_update_sizes_inl */
629 /* don't need to inline every instance */
630 static void pcmrec_update_sizes(size_t prev_enc_size,
631 unsigned long prev_num_pcm)
633 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
634 } /* pcmrec_update_sizes */
636 static void pcmrec_start_file(void)
638 size_t enc_size = rec_fdata.new_enc_size;
639 unsigned long num_pcm = rec_fdata.new_num_pcm;
640 int curr_rec_file = rec_fdata.rec_file;
641 char filename[MAX_PATH];
643 /* must always pull the filename that matches with this queue */
644 if (!pcmrec_fnq_get_filename(filename))
646 logf("start file: fnq empty");
647 *filename = '\0';
648 errors |= PCMREC_E_FNQ_DESYNC;
650 else if (errors != 0)
652 logf("start file: error already");
654 else if (curr_rec_file >= 0)
656 /* Any previous file should have been closed */
657 logf("start file: file already open");
658 errors |= PCMREC_E_FNQ_DESYNC;
661 if (errors != 0)
662 rec_fdata.chunk->flags |= CHUNKF_ERROR;
664 /* encoder can set error flag here and should increase
665 enc_new_size and pcm_new_size to reflect additional
666 data written if any */
667 rec_fdata.filename = filename;
668 enc_events_callback(ENC_START_FILE, &rec_fdata);
670 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
672 logf("start file: enc error");
673 errors |= PCMREC_E_ENCODER;
676 if (errors != 0)
678 pcmrec_close_file(&curr_rec_file);
679 /* Write no more to this file */
680 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
682 else
684 pcmrec_update_sizes(enc_size, num_pcm);
687 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
688 } /* pcmrec_start_file */
690 static inline void pcmrec_write_chunk(void)
692 size_t enc_size = rec_fdata.new_enc_size;
693 unsigned long num_pcm = rec_fdata.new_num_pcm;
695 if (errors != 0)
696 rec_fdata.chunk->flags |= CHUNKF_ERROR;
698 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
700 if ((long)rec_fdata.chunk->flags >= 0)
702 pcmrec_update_sizes_inl(enc_size, num_pcm);
704 else if (errors == 0)
706 logf("wr chk enc error %lu %lu",
707 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
708 errors |= PCMREC_E_ENCODER;
710 } /* pcmrec_write_chunk */
712 static void pcmrec_end_file(void)
714 /* all data in output buffer for current file will have been
715 written and encoder can now do any nescessary steps to
716 finalize the written file */
717 size_t enc_size = rec_fdata.new_enc_size;
718 unsigned long num_pcm = rec_fdata.new_num_pcm;
720 enc_events_callback(ENC_END_FILE, &rec_fdata);
722 if (errors == 0)
724 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
726 logf("end file: enc error");
727 errors |= PCMREC_E_ENCODER;
729 else
731 pcmrec_update_sizes(enc_size, num_pcm);
735 /* Force file close if error */
736 if (errors != 0)
737 pcmrec_close_file(&rec_fdata.rec_file);
739 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
740 } /* pcmrec_end_file */
743 * Update buffer watermarks with spinup time compensation
745 * All this assumes reasonable data rates, chunk sizes and sufficient
746 * memory for the most part. Some dumb checks are included but perhaps
747 * are pointless since this all will break down at extreme limits that
748 * are currently not applicable to any supported device.
750 static void pcmrec_refresh_watermarks(void)
752 logf("ata spinup: %d", storage_spinup_time());
754 /* set the low mark for when flushing stops if automatic */
755 /* don't change the order in this expression, LOW_SECONDS can be an
756 * integer fraction of 4 */
757 low_watermark = (sample_rate*4*LOW_SECONDS + (enc_chunk_size-1))
758 / enc_chunk_size;
759 logf("low wmk: %d", low_watermark);
761 #ifdef HAVE_PRIORITY_SCHEDULING
762 /* panic boost thread priority if 2 seconds of ground is lost -
763 this allows encoder to boost with just under a second of
764 pcm data (if not yet full enough to boost itself)
765 and not falsely trip the alarm. */
766 /* don't change the order in this expression, PANIC_SECONDS can be an
767 * integer fraction of 4 */
768 flood_watermark = enc_num_chunks -
769 (sample_rate*4*PANIC_SECONDS + (enc_chunk_size-1))
770 / enc_chunk_size;
772 if (flood_watermark < low_watermark)
774 logf("warning: panic < low");
775 flood_watermark = low_watermark;
778 logf("flood at: %d", flood_watermark);
779 #endif
780 spinup_time = last_storage_spinup_time = storage_spinup_time();
781 #if (CONFIG_STORAGE & STORAGE_ATA)
782 /* write at 8s + st remaining in enc_buffer - range 12s to
783 20s total - default to 3.5s spinup. */
784 if (spinup_time == 0)
785 spinup_time = 35*HZ/10; /* default - cozy */
786 else if (spinup_time < 2*HZ)
787 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
788 else if (spinup_time > 10*HZ)
789 spinup_time = 10*HZ; /* do you have a functioning HD? */
790 #endif
792 /* try to start writing with 10s remaining after disk spinup */
793 high_watermark = enc_num_chunks -
794 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
795 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
797 if (high_watermark < low_watermark)
799 logf("warning: low 'write at' (%d)", high_watermark);
800 high_watermark = low_watermark;
801 low_watermark /= 2;
804 logf("write at: %d", high_watermark);
805 } /* pcmrec_refresh_watermarks */
808 * Process the chunks
810 * This function is called when queue_get_w_tmo times out.
812 * Set flush_num to the number of files to flush to disk or to
813 * a PCMREC_FLUSH_* constant.
815 static void pcmrec_flush(unsigned flush_num)
817 #ifdef HAVE_PRIORITY_SCHEDULING
818 static unsigned long last_flush_tick; /* tick when function returned */
819 unsigned long start_tick; /* When flush started */
820 unsigned long prio_tick; /* Timeout for auto boost */
821 int prio_pcmrec; /* Current thread priority for pcmrec */
822 int prio_codec; /* Current thread priority for codec */
823 #endif
824 int num_ready; /* Number of chunks ready at start */
825 unsigned remaining; /* Number of file starts remaining */
826 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
827 bool interruptable; /* Flush can be interupted */
829 num_ready = enc_wr_index - enc_rd_index;
830 if (num_ready < 0)
831 num_ready += enc_num_chunks;
833 /* save interruptable flag and remove it to get the actual count */
834 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
835 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
837 if (flush_num == 0)
839 if (!is_recording)
840 return;
842 if (storage_spinup_time() != last_storage_spinup_time)
843 pcmrec_refresh_watermarks();
845 /* enough available? no? then leave */
846 if (num_ready < high_watermark)
847 return;
848 } /* endif (flush_num == 0) */
850 #ifdef HAVE_PRIORITY_SCHEDULING
851 start_tick = current_tick;
852 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
854 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
856 /* if we're getting called too much and this isn't forced,
857 boost stat by expiring timeout in advance */
858 logf("too frequent flush");
859 prio_tick = current_tick - 1;
862 prio_pcmrec = -1;
863 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
864 need init */
865 #endif
867 logf("writing:%d(%d):%s%s", num_ready, flush_num,
868 interruptable ? "i" : "",
869 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
871 cpu_boost(true);
873 remaining = flush_num;
874 chunks_flushed = 0;
876 while (num_ready > 0)
878 /* check current number of encoder chunks */
879 int num = enc_wr_index - enc_rd_index;
880 if (num < 0)
881 num += enc_num_chunks;
883 if (num <= low_watermark &&
884 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
886 logf("low data: %d", num);
887 break; /* data remaining is below threshold */
890 if (interruptable && flush_interrupts > 0)
892 logf("int at: %d", num);
893 break; /* interrupted */
896 #ifdef HAVE_PRIORITY_SCHEDULING
897 if (prio_pcmrec == -1 && (num >= flood_watermark ||
898 TIME_AFTER(current_tick, prio_tick)))
900 /* losing ground or holding without progress - boost
901 priority until finished */
902 logf("pcmrec: boost (%s)",
903 num >= flood_watermark ? "num" : "time");
904 prio_pcmrec = thread_set_priority(thread_self(),
905 thread_get_priority(thread_self()) - 4);
906 prio_codec = codec_thread_set_priority(
907 codec_thread_get_priority() - 4);
909 #endif
911 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
912 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
913 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
915 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
917 pcmrec_start_file();
918 if (--remaining == 0)
919 num_ready = 0; /* stop on next loop - must write this
920 chunk if it has data */
923 pcmrec_write_chunk();
925 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
926 pcmrec_end_file();
928 INC_ENC_INDEX(enc_rd_index);
930 if (errors != 0)
932 pcmrec_end_file();
933 break;
936 if (flush_num == PCMREC_FLUSH_MINI &&
937 ++chunks_flushed >= MINI_CHUNKS)
939 logf("mini flush break");
940 break;
942 /* no yielding; the file apis called in the codecs do that
943 sufficiently */
944 } /* end while */
946 /* sync file */
947 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
948 errors |= PCMREC_E_IO;
950 cpu_boost(false);
952 #ifdef HAVE_PRIORITY_SCHEDULING
953 if (prio_pcmrec != -1)
955 /* return to original priorities */
956 logf("pcmrec: unboost priority");
957 thread_set_priority(thread_self(), prio_pcmrec);
958 codec_thread_set_priority(prio_codec);
961 last_flush_tick = current_tick; /* save tick when we left */
962 #endif
964 logf("done");
965 } /* pcmrec_flush */
968 * Marks a new stream in the buffer and gives the encoder a chance for special
969 * handling of transition from one to the next. The encoder may change the
970 * chunk that ends the old stream by requesting more chunks and similiarly for
971 * the new but must always advance the position though the interface. It can
972 * later reject any data it cares to when writing the file but should mark the
973 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
974 * a NULL data pointer without error as well.
976 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
978 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
979 } /* pcmrec_get_chunk_index */
981 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
983 DEC_ENC_INDEX(index);
984 return GET_ENC_CHUNK(index);
985 } /* pcmrec_get_prev_chunk */
987 static void pcmrec_new_stream(const char *filename, /* next file name */
988 unsigned long flags, /* CHUNKF_* flags */
989 int pre_index) /* index for prerecorded data */
991 logf("pcmrec_new_stream");
992 char path[MAX_PATH]; /* place to copy filename so sender can be released */
994 struct enc_buffer_event_data data;
995 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
996 new filename */
997 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
998 stream */
999 bool did_flush = false; /* did a flush occurr? */
1001 if (filename)
1002 strlcpy(path, filename, MAX_PATH);
1003 queue_reply(&pcmrec_queue, 0); /* We have all we need */
1005 data.pre_chunk = NULL;
1006 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1008 /* end chunk */
1009 if (flags & CHUNKF_END_FILE)
1011 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1013 if (data.chunk->flags & CHUNKF_START_FILE)
1015 /* cannot start and end on same unprocessed chunk */
1016 logf("file end on start");
1017 flags &= ~CHUNKF_END_FILE;
1019 else if (enc_rd_index == enc_wr_index)
1021 /* all data flushed but file not ended - chunk will be left
1022 empty */
1023 logf("end on dead end");
1024 data.chunk->flags = 0;
1025 data.chunk->enc_size = 0;
1026 data.chunk->num_pcm = 0;
1027 data.chunk->enc_data = NULL;
1028 INC_ENC_INDEX(enc_wr_index);
1029 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1031 else
1033 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1035 if (last->flags & CHUNKF_END_FILE)
1037 /* end already processed and marked - can't end twice */
1038 logf("file end again");
1039 flags &= ~CHUNKF_END_FILE;
1044 /* start chunk */
1045 if (flags & CHUNKF_START_FILE)
1047 bool pre = flags & CHUNKF_PRERECORD;
1049 if (pre)
1051 logf("stream prerecord start");
1052 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1053 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1055 else
1057 logf("stream normal start");
1058 start = data.chunk;
1059 start->flags &= CHUNKF_START_FILE;
1062 /* if encoder hasn't yet processed the last start - abort the start
1063 of the previous file queued or else it will be empty and invalid */
1064 if (start->flags & CHUNKF_START_FILE)
1066 logf("replacing fnq tail: %s", filename);
1067 fnq_add_fn = pcmrec_fnq_replace_tail;
1069 else
1071 logf("adding filename: %s", filename);
1072 fnq_add_fn = pcmrec_fnq_add_filename;
1076 data.flags = flags;
1077 pcmrec_context = true; /* switch encoder context */
1078 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1079 pcmrec_context = false; /* switch back */
1081 if (flags & CHUNKF_END_FILE)
1083 int i = pcmrec_get_chunk_index(data.chunk);
1084 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1087 if (start)
1089 if (!(flags & CHUNKF_PRERECORD))
1091 /* get stats on data added to start - sort of a prerecord
1092 operation */
1093 int i = pcmrec_get_chunk_index(data.chunk);
1094 struct enc_chunk_hdr *chunk = data.chunk;
1096 logf("start data: %d %d", i, enc_wr_index);
1098 num_rec_bytes = 0;
1099 num_rec_samples = 0;
1101 while (i != enc_wr_index)
1103 num_rec_bytes += chunk->enc_size;
1104 num_rec_samples += chunk->num_pcm;
1105 INC_ENC_INDEX(i);
1106 chunk = GET_ENC_CHUNK(i);
1109 start->flags &= ~CHUNKF_START_FILE;
1110 start = data.chunk;
1113 start->flags |= CHUNKF_START_FILE;
1115 /* flush all pending files out if full and adding */
1116 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1118 logf("fnq full");
1119 pcmrec_flush(PCMREC_FLUSH_ALL);
1120 did_flush = true;
1123 fnq_add_fn(path);
1126 /* Make sure to complete any interrupted high watermark */
1127 if (!did_flush)
1128 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1129 } /* pcmrec_new_stream */
1131 /** event handlers for pcmrec thread */
1133 /* PCMREC_INIT */
1134 static void pcmrec_init(void)
1136 unsigned char *buffer;
1137 send_event(RECORDING_EVENT_START, NULL);
1139 /* warings and errors */
1140 warnings =
1141 errors = 0;
1143 pcmrec_close_file(&rec_fdata.rec_file);
1144 rec_fdata.rec_file = -1;
1146 /* pcm FIFO */
1147 dma_lock = true;
1148 pcm_rd_pos = 0;
1149 dma_wr_pos = 0;
1150 pcm_enc_pos = 0;
1152 /* encoder FIFO */
1153 enc_wr_index = 0;
1154 enc_rd_index = 0;
1156 /* filename queue */
1157 fnq_rd_pos = 0;
1158 fnq_wr_pos = 0;
1160 /* stats */
1161 num_rec_bytes = 0;
1162 num_rec_samples = 0;
1163 #if 0
1164 accum_rec_bytes = 0;
1165 accum_pcm_samples = 0;
1166 #endif
1168 pre_record_ticks = 0;
1170 is_recording = false;
1171 is_paused = false;
1173 buffer = audio_get_recording_buffer(&rec_buffer_size);
1175 /* Line align pcm_buffer 2^5=32 bytes */
1176 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 5);
1177 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1178 PCM_MAX_FEED_SIZE, 2);
1179 /* Adjust available buffer for possible align advancement */
1180 rec_buffer_size -= pcm_buffer - buffer;
1182 pcm_init_recording();
1183 } /* pcmrec_init */
1185 /* PCMREC_CLOSE */
1186 static void pcmrec_close(void)
1188 dma_lock = true;
1189 pre_record_ticks = 0; /* Can't be prerecording any more */
1190 warnings = 0;
1191 pcm_close_recording();
1192 reset_hardware();
1193 audio_remove_encoder();
1194 send_event(RECORDING_EVENT_STOP, NULL);
1195 } /* pcmrec_close */
1197 /* PCMREC_OPTIONS */
1198 static void pcmrec_set_recording_options(
1199 struct audio_recording_options *options)
1201 /* stop DMA transfer */
1202 dma_lock = true;
1203 pcm_stop_recording();
1205 rec_frequency = options->rec_frequency;
1206 rec_source = options->rec_source;
1207 num_channels = options->rec_channels == 1 ? 1 : 2;
1208 rec_mono_mode = options->rec_mono_mode;
1209 pre_record_ticks = options->rec_prerecord_time * HZ;
1210 enc_config = options->enc_config;
1211 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1213 #ifdef HAVE_SPDIF_IN
1214 if (rec_source == AUDIO_SRC_SPDIF)
1216 /* must measure SPDIF sample rate before configuring codecs */
1217 unsigned long sr = spdif_measure_frequency();
1218 /* round to master list for SPDIF rate */
1219 int index = round_value_to_list32(sr, audio_master_sampr_list,
1220 SAMPR_NUM_FREQ, false);
1221 sample_rate = audio_master_sampr_list[index];
1222 /* round to HW playback rates for monitoring */
1223 index = round_value_to_list32(sr, hw_freq_sampr,
1224 HW_NUM_FREQ, false);
1225 pcm_set_frequency(hw_freq_sampr[index] | SAMPR_TYPE_REC);
1226 /* encoders with a limited number of rates do their own rounding */
1228 else
1229 #endif
1231 /* set sample rate from frequency selection */
1232 sample_rate = rec_freq_sampr[rec_frequency];
1233 pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1236 /* set monitoring */
1237 audio_set_output_source(rec_source);
1239 /* apply hardware setting to start monitoring now */
1240 pcm_apply_settings();
1242 queue_reply(&pcmrec_queue, 0); /* Release sender */
1244 if (audio_load_encoder(enc_config.afmt))
1246 /* start DMA transfer */
1247 dma_lock = pre_record_ticks == 0;
1248 pcm_record_data(pcm_rec_have_more, pcm_rec_status_callback,
1249 GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
1251 else
1253 logf("set rec opt: enc load failed");
1254 errors |= PCMREC_E_LOAD_ENCODER;
1256 } /* pcmrec_set_recording_options */
1258 /* PCMREC_RECORD - start recording (not gapless)
1259 or split stream (gapless) */
1260 static void pcmrec_record(const char *filename)
1262 unsigned long pre_sample_ticks;
1263 int rd_start;
1264 unsigned long flags;
1265 int pre_index;
1267 logf("pcmrec_record: %s", filename);
1269 /* reset stats */
1270 num_rec_bytes = 0;
1271 num_rec_samples = 0;
1273 if (!is_recording)
1275 #if 0
1276 accum_rec_bytes = 0;
1277 accum_pcm_samples = 0;
1278 #endif
1279 warnings = 0; /* reset warnings */
1281 rd_start = enc_wr_index;
1282 pre_sample_ticks = 0;
1284 pcmrec_refresh_watermarks();
1286 if (pre_record_ticks)
1288 int i = rd_start;
1289 /* calculate number of available chunks */
1290 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1291 enc_num_chunks) % enc_num_chunks;
1292 /* overflow at 974 seconds of prerecording at 44.1kHz */
1293 unsigned long pre_record_sample_ticks =
1294 enc_sample_rate*pre_record_ticks;
1295 int pre_chunks = 0; /* Counter to limit prerecorded time to
1296 prevent flood state at outset */
1298 logf("pre-st: %ld", pre_record_sample_ticks);
1300 /* Get exact measure of recorded data as number of samples aren't
1301 nescessarily going to be the max for each chunk */
1302 for (; avail_pre_chunks-- > 0;)
1304 struct enc_chunk_hdr *chunk;
1305 unsigned long chunk_sample_ticks;
1307 DEC_ENC_INDEX(i);
1309 chunk = GET_ENC_CHUNK(i);
1311 /* must have data to be counted */
1312 if (chunk->enc_data == NULL)
1313 continue;
1315 chunk_sample_ticks = chunk->num_pcm*HZ;
1317 rd_start = i;
1318 pre_sample_ticks += chunk_sample_ticks;
1319 num_rec_bytes += chunk->enc_size;
1320 num_rec_samples += chunk->num_pcm;
1321 pre_chunks++;
1323 /* stop here if enough already */
1324 if (pre_chunks >= high_watermark ||
1325 pre_sample_ticks >= pre_record_sample_ticks)
1327 logf("pre-chks: %d", pre_chunks);
1328 break;
1332 #if 0
1333 accum_rec_bytes = num_rec_bytes;
1334 accum_pcm_samples = num_rec_samples;
1335 #endif
1338 enc_rd_index = rd_start;
1340 /* filename queue should be empty */
1341 if (!pcmrec_fnq_is_empty())
1343 logf("fnq: not empty!");
1344 pcmrec_fnq_set_empty();
1347 flags = CHUNKF_START_FILE;
1348 if (pre_sample_ticks > 0)
1349 flags |= CHUNKF_PRERECORD;
1351 pre_index = enc_rd_index;
1353 dma_lock = false;
1354 is_paused = false;
1355 is_recording = true;
1357 else
1359 /* already recording, just split the stream */
1360 logf("inserting split");
1361 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1362 pre_index = 0;
1365 pcmrec_new_stream(filename, flags, pre_index);
1366 logf("pcmrec_record done");
1367 } /* pcmrec_record */
1369 /* PCMREC_STOP */
1370 static void pcmrec_stop(void)
1372 logf("pcmrec_stop");
1374 if (is_recording)
1376 dma_lock = true; /* lock dma write position */
1378 /* flush all available data first to avoid overflow while waiting
1379 for encoding to finish */
1380 pcmrec_flush(PCMREC_FLUSH_ALL);
1382 /* wait for encoder to finish remaining data */
1383 while (errors == 0 && !pcm_buffer_empty)
1384 yield();
1386 /* end stream at last data */
1387 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1389 /* flush anything else encoder added */
1390 pcmrec_flush(PCMREC_FLUSH_ALL);
1392 /* remove any pending file start not yet processed - should be at
1393 most one at enc_wr_index */
1394 pcmrec_fnq_get_filename(NULL);
1395 /* encoder should abort any chunk it was in midst of processing */
1396 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1398 /* filename queue should be empty */
1399 if (!pcmrec_fnq_is_empty())
1401 logf("fnq: not empty!");
1402 pcmrec_fnq_set_empty();
1405 /* be absolutely sure the file is closed */
1406 if (errors != 0)
1407 pcmrec_close_file(&rec_fdata.rec_file);
1408 rec_fdata.rec_file = -1;
1410 is_recording = false;
1411 is_paused = false;
1412 dma_lock = pre_record_ticks == 0;
1414 else
1416 logf("not recording");
1419 logf("pcmrec_stop done");
1420 } /* pcmrec_stop */
1422 /* PCMREC_PAUSE */
1423 static void pcmrec_pause(void)
1425 logf("pcmrec_pause");
1427 if (!is_recording)
1429 logf("not recording");
1431 else if (is_paused)
1433 logf("already paused");
1435 else
1437 dma_lock = true;
1438 is_paused = true;
1441 logf("pcmrec_pause done");
1442 } /* pcmrec_pause */
1444 /* PCMREC_RESUME */
1445 static void pcmrec_resume(void)
1447 logf("pcmrec_resume");
1449 if (!is_recording)
1451 logf("not recording");
1453 else if (!is_paused)
1455 logf("not paused");
1457 else
1459 is_paused = false;
1460 is_recording = true;
1461 dma_lock = false;
1464 logf("pcmrec_resume done");
1465 } /* pcmrec_resume */
1467 static void pcmrec_thread(void) NORETURN_ATTR;
1468 static void pcmrec_thread(void)
1470 struct queue_event ev;
1472 logf("thread pcmrec start");
1474 while(1)
1476 if (is_recording)
1478 /* Poll periodically to flush data */
1479 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1481 if (ev.id == SYS_TIMEOUT)
1483 /* Messages that interrupt this will complete it */
1484 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1485 PCMREC_FLUSH_INTERRUPTABLE);
1486 continue;
1489 else
1491 /* Not doing anything - sit and wait for commands */
1492 queue_wait(&pcmrec_queue, &ev);
1495 switch (ev.id)
1497 case PCMREC_INIT:
1498 pcmrec_init();
1499 break;
1501 case PCMREC_CLOSE:
1502 pcmrec_close();
1503 break;
1505 case PCMREC_OPTIONS:
1506 pcmrec_set_recording_options(
1507 (struct audio_recording_options *)ev.data);
1508 break;
1510 case PCMREC_RECORD:
1511 clear_flush_interrupt();
1512 pcmrec_record((const char *)ev.data);
1513 break;
1515 case PCMREC_STOP:
1516 clear_flush_interrupt();
1517 pcmrec_stop();
1518 break;
1520 case PCMREC_PAUSE:
1521 clear_flush_interrupt();
1522 pcmrec_pause();
1523 break;
1525 case PCMREC_RESUME:
1526 pcmrec_resume();
1527 break;
1528 #if 0
1529 case PCMREC_FLUSH_NUM:
1530 pcmrec_flush((unsigned)ev.data);
1531 break;
1532 #endif
1533 case SYS_USB_CONNECTED:
1534 if (is_recording)
1535 break;
1536 pcmrec_close();
1537 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1538 usb_wait_for_disconnect(&pcmrec_queue);
1539 flush_interrupts = 0;
1540 break;
1541 } /* end switch */
1542 } /* end while */
1543 } /* pcmrec_thread */
1545 /****************************************************************************/
1546 /* */
1547 /* following functions will be called by the encoder codec */
1548 /* in a free-threaded manner */
1549 /* */
1550 /****************************************************************************/
1552 /* pass the encoder settings to the encoder */
1553 void enc_get_inputs(struct enc_inputs *inputs)
1555 inputs->sample_rate = sample_rate;
1556 inputs->num_channels = num_channels;
1557 inputs->rec_mono_mode = rec_mono_mode;
1558 inputs->config = &enc_config;
1559 } /* enc_get_inputs */
1561 /* set the encoder dimensions (called by encoder codec at initialization and
1562 termination) */
1563 void enc_set_parameters(struct enc_parameters *params)
1565 size_t bufsize, resbytes;
1567 logf("enc_set_parameters");
1569 if (!params)
1571 logf("reset");
1572 /* Encoder is terminating */
1573 memset(&enc_config, 0, sizeof (enc_config));
1574 enc_sample_rate = 0;
1575 cancel_cpu_boost(); /* Make sure no boost remains */
1576 return;
1579 enc_sample_rate = params->enc_sample_rate;
1580 logf("enc sampr:%lu", enc_sample_rate);
1582 pcm_rd_pos = dma_wr_pos;
1583 pcm_enc_pos = pcm_rd_pos;
1585 enc_config.afmt = params->afmt;
1586 /* addition of the header is always implied - chunk size 4-byte aligned */
1587 enc_chunk_size =
1588 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1589 enc_events_callback = params->events_callback;
1591 logf("chunk size:%lu", enc_chunk_size);
1593 /*** Configure the buffers ***/
1595 /* Layout of recording buffer:
1596 * [ax] = possible alignment x multiple
1597 * [sx] = possible size alignment of x multiple
1598 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1599 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1601 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1602 logf("resbytes:%lu", resbytes);
1604 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1605 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1606 #ifdef DEBUG
1607 - sizeof (*wrap_id_p)
1608 #endif
1611 enc_num_chunks = bufsize / enc_chunk_size;
1612 logf("num chunks:%d", enc_num_chunks);
1614 /* get real amount used by encoder chunks */
1615 bufsize = enc_num_chunks*enc_chunk_size;
1616 logf("enc size:%lu", bufsize);
1618 #ifdef DEBUG
1619 /* add magic at wraparound for spillover checks */
1620 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1621 bufsize += sizeof (*wrap_id_p);
1622 *wrap_id_p = ENC_CHUNK_MAGIC;
1623 #endif
1625 /** set OUT parameters **/
1626 params->enc_buffer = enc_buffer;
1627 params->buf_chunk_size = enc_chunk_size;
1628 params->num_chunks = enc_num_chunks;
1630 /* calculate reserve buffer start and return pointer to encoder */
1631 params->reserve_buffer = NULL;
1632 if (resbytes > 0)
1634 params->reserve_buffer = enc_buffer + bufsize;
1635 bufsize += resbytes;
1638 /* place filename queue at end of buffer using up whatever remains */
1639 fnq_rd_pos = 0; /* reset */
1640 fnq_wr_pos = 0; /* reset */
1641 fn_queue = enc_buffer + bufsize;
1642 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1643 fnq_size /= MAX_PATH;
1644 if (fnq_size > FNQ_MAX_NUM_PATHS)
1645 fnq_size = FNQ_MAX_NUM_PATHS;
1646 fnq_size *= MAX_PATH;
1647 logf("fnq files:%ld", fnq_size / MAX_PATH);
1649 #if defined(DEBUG)
1650 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1651 logf("enc:%08lX", (uintptr_t)enc_buffer);
1652 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1653 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1654 logf("fnq:%08lX", (uintptr_t)fn_queue);
1655 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1656 #endif
1658 /* init all chunk headers and reset indexes */
1659 enc_rd_index = 0;
1660 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1662 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1663 #ifdef DEBUG
1664 chunk->id = ENC_CHUNK_MAGIC;
1665 #endif
1666 chunk->flags = 0;
1669 logf("enc_set_parameters done");
1670 } /* enc_set_parameters */
1672 /* return encoder chunk at current write position -
1673 NOTE: can be called by pcmrec thread when splitting streams */
1674 struct enc_chunk_hdr * enc_get_chunk(void)
1676 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1678 #ifdef DEBUG
1679 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1681 errors |= PCMREC_E_CHUNK_OVF;
1682 logf("finish chk ovf: %d", enc_wr_index);
1684 #endif
1686 chunk->flags &= CHUNKF_START_FILE;
1688 if (!is_recording)
1689 chunk->flags |= CHUNKF_PRERECORD;
1691 return chunk;
1692 } /* enc_get_chunk */
1694 /* releases the current chunk into the available chunks -
1695 NOTE: can be called by pcmrec thread when splitting streams */
1696 void enc_finish_chunk(void)
1698 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1700 if ((long)chunk->flags < 0)
1702 /* encoder set error flag */
1703 errors |= PCMREC_E_ENCODER;
1704 logf("finish chk enc error");
1707 /* advance enc_wr_index to the next encoder chunk */
1708 INC_ENC_INDEX(enc_wr_index);
1710 if (enc_rd_index != enc_wr_index)
1712 num_rec_bytes += chunk->enc_size;
1713 num_rec_samples += chunk->num_pcm;
1714 #if 0
1715 accum_rec_bytes += chunk->enc_size;
1716 accum_pcm_samples += chunk->num_pcm;
1717 #endif
1719 else if (is_recording) /* buffer full */
1721 /* keep current position and put up warning flag */
1722 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1723 logf("enc_buffer ovf");
1724 DEC_ENC_INDEX(enc_wr_index);
1725 if (pcmrec_context)
1727 /* if stream splitting, keep this out of circulation and
1728 flush a small number, then readd - cannot risk losing
1729 stream markers */
1730 logf("mini flush");
1731 pcmrec_flush(PCMREC_FLUSH_MINI);
1732 INC_ENC_INDEX(enc_wr_index);
1735 else
1737 /* advance enc_rd_index for prerecording */
1738 INC_ENC_INDEX(enc_rd_index);
1740 } /* enc_finish_chunk */
1742 /* passes a pointer to next chunk of unprocessed wav data */
1743 /* TODO: this really should give the actual size returned */
1744 unsigned char * enc_get_pcm_data(size_t size)
1746 int wp = dma_wr_pos;
1747 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1749 /* limit the requested pcm data size */
1750 if (size > PCM_MAX_FEED_SIZE)
1751 size = PCM_MAX_FEED_SIZE;
1753 if (avail >= size)
1755 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1756 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1758 pcm_enc_pos = pcm_rd_pos;
1759 pcm_rd_pos = next_pos;
1761 /* ptr must point to continous data at wraparound position */
1762 if ((size_t)pcm_rd_pos < size)
1764 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1765 pcm_buffer, pcm_rd_pos);
1768 if (avail >= (sample_rate << 2) ||
1769 avail >= 3*(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE) / 4)
1771 /* Filling up - 1s data available or more or 3/4 full or more -
1772 boost codec */
1773 trigger_cpu_boost();
1776 pcm_buffer_empty = false;
1777 return ptr;
1780 /* not enough data available - encoder should idle */
1781 pcm_buffer_empty = true;
1783 cancel_cpu_boost();
1785 /* Sleep long enough to allow one frame on average */
1786 sleep(0);
1788 return NULL;
1789 } /* enc_get_pcm_data */
1791 /* puts some pcm data back in the queue */
1792 size_t enc_unget_pcm_data(size_t size)
1794 int wp = dma_wr_pos;
1795 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1796 2*PCM_CHUNK_SIZE;
1798 /* allow one interrupt to occur during this call and not have the
1799 new read position inside the DMA destination chunk */
1800 if ((ssize_t)old_avail > 0)
1802 /* limit size to amount of old data remaining */
1803 if (size > old_avail)
1804 size = old_avail;
1806 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1807 pcm_rd_pos = pcm_enc_pos;
1809 return size;
1812 return 0;
1813 } /* enc_unget_pcm_data */