No bufclosing of handles after track change
[Rockbox.git] / firmware / pcm_record.c
blobc2d2719d053a8f62fd4011e6904012494415f81b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "system.h"
20 #include "kernel.h"
21 #include "logf.h"
22 #include "thread.h"
23 #include <string.h>
24 #include "ata.h"
25 #include "usb.h"
26 #include "buffer.h"
27 #include "general.h"
28 #include "audio.h"
29 #include "sound.h"
30 #include "id3.h"
31 #ifdef HAVE_SPDIF_IN
32 #include "spdif.h"
33 #endif
35 /***************************************************************************/
37 extern struct thread_entry *codec_thread_p;
39 /** General recording state **/
40 static bool is_recording; /* We are recording */
41 static bool is_paused; /* We have paused */
42 static unsigned long errors; /* An error has occured */
43 static unsigned long warnings; /* Warning */
44 static int flush_interrupts = 0; /* Number of messages queued that
45 should interrupt a flush in
46 progress -
47 for a safety net and a prompt
48 response to stop, split and pause
49 requests -
50 only interrupts a flush initiated
51 by pcmrec_flush(0) */
53 /* Utility functions for setting/clearing flushing interrupt flag */
54 static inline void flush_interrupt(void)
56 flush_interrupts++;
57 logf("flush int: %d", flush_interrupts);
60 static inline void clear_flush_interrupt(void)
62 if (--flush_interrupts < 0)
63 flush_interrupts = 0;
66 /** Stats on encoded data for current file **/
67 static size_t num_rec_bytes; /* Num bytes recorded */
68 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
70 /** Stats on encoded data for all files from start to stop **/
71 #if 0
72 static unsigned long long accum_rec_bytes; /* total size written to chunks */
73 static unsigned long long accum_pcm_samples; /* total pcm count processed */
74 #endif
76 /* Keeps data about current file and is sent as event data for codec */
77 static struct enc_file_event_data rec_fdata IDATA_ATTR =
79 .chunk = NULL,
80 .new_enc_size = 0,
81 .new_num_pcm = 0,
82 .rec_file = -1,
83 .num_pcm_samples = 0
86 /** These apply to current settings **/
87 static int rec_source; /* current rec_source setting */
88 static int rec_frequency; /* current frequency setting */
89 static unsigned long sample_rate; /* Sample rate in HZ */
90 static int num_channels; /* Current number of channels */
91 static struct encoder_config enc_config; /* Current encoder configuration */
92 static unsigned long pre_record_ticks; /* pre-record time in ticks */
94 /****************************************************************************
95 use 2 circular buffers:
96 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
97 enc_buffer=encoded audio buffer: storage for encoder output data
99 Flow:
100 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
101 2. if enough pcm data are available the encoder codec does encoding of pcm
102 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
103 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
105 Functions calls (basic encoder steps):
106 1.main: audio_load_encoder(); start the encoder
107 2.encoder: enc_get_inputs(); get encoder recording settings
108 3.encoder: enc_set_parameters(); set the encoder parameters
109 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
110 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
111 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
112 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
113 8.encoder: <process enc chunk> compress and store data to enc chunk
114 9.encoder: enc_finish_chunk(); inform main about chunk processed and
115 is available to be written to a file.
116 Encoder can place any number of chunks
117 of PCM data in a single output chunk
118 but must stay within its output chunk
119 size
120 A.encoder: repeat 4. to 9.
121 B.pcmrec: enc_events_callback(); called for certain events
123 (*) Optional step
124 ****************************************************************************/
126 /** buffer parameters where incoming PCM data is placed **/
127 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
128 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
129 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
131 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
132 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
133 #define INC_ENC_INDEX(index) \
134 { if (++index >= enc_num_chunks) index = 0; }
135 #define DEC_ENC_INDEX(index) \
136 { if (--index < 0) index = enc_num_chunks - 1; }
138 static size_t rec_buffer_size; /* size of available buffer */
139 static unsigned char *pcm_buffer; /* circular recording buffer */
140 static unsigned char *enc_buffer; /* circular encoding buffer */
141 #ifdef DEBUG
142 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
143 aid to check if the encoder data
144 spilled out of its chunk */
145 #endif /* DEBUG */
146 static volatile int dma_wr_pos; /* current DMA write pos */
147 static int pcm_rd_pos; /* current PCM read pos */
148 static int pcm_enc_pos; /* position encoder is processing */
149 static volatile bool dma_lock; /* lock DMA write position */
150 static int enc_wr_index; /* encoder chunk write index */
151 static int enc_rd_index; /* encoder chunk read index */
152 static int enc_num_chunks; /* number of chunks in ringbuffer */
153 static size_t enc_chunk_size; /* maximum encoder chunk size */
154 static unsigned long enc_sample_rate; /* sample rate used by encoder */
155 static bool pcmrec_context = false; /* called by pcmrec thread? */
156 static bool pcm_buffer_empty; /* all pcm chunks processed? */
158 /** file flushing **/
159 static int low_watermark; /* Low watermark to stop flush */
160 static int high_watermark; /* max chunk limit for data flush */
161 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
162 static int last_ata_spinup_time = -1;/* previous spin time used */
163 #ifdef HAVE_PRIORITY_SCHEDULING
164 static int flood_watermark; /* boost thread priority when here */
165 #endif
167 /* Constants that control watermarks */
168 #define LOW_SECONDS 1 /* low watermark time till empty */
169 #define MINI_CHUNKS 10 /* chunk count for mini flush */
170 #ifdef HAVE_PRIORITY_SCHEDULING
171 #define PRIO_SECONDS 10 /* max flush time before priority boost */
172 #endif
173 #if MEM <= 16
174 #define PANIC_SECONDS 5 /* flood watermark time until full */
175 #define FLUSH_SECONDS 7 /* flush watermark time until full */
176 #else
177 #define PANIC_SECONDS 8
178 #define FLUSH_SECONDS 10
179 #endif /* MEM */
181 /** encoder events **/
182 static void (*enc_events_callback)(enum enc_events event, void *data);
184 /** Path queue for files to write **/
185 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
186 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
187 static unsigned char *fn_queue; /* pointer to first filename */
188 static ssize_t fnq_size; /* capacity of queue in bytes */
189 static int fnq_rd_pos; /* current read position */
190 static int fnq_wr_pos; /* current write position */
191 #define FNQ_NEXT(pos) \
192 ({ int p = (pos) + MAX_PATH; \
193 if (p >= fnq_size) \
194 p = 0; \
195 p; })
196 #define FNQ_PREV(pos) \
197 ({ int p = (pos) - MAX_PATH; \
198 if (p < 0) \
199 p = fnq_size - MAX_PATH; \
200 p; })
202 enum
204 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
205 incoming messages - combine
206 with other constants */
207 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
208 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
209 chunks */
210 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
211 reached */
214 /***************************************************************************/
216 static struct event_queue pcmrec_queue NOCACHEBSS_ATTR;
217 static struct queue_sender_list pcmrec_queue_send NOCACHEBSS_ATTR;
218 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
219 static const char pcmrec_thread_name[] = "pcmrec";
220 static struct thread_entry *pcmrec_thread_p;
222 static void pcmrec_thread(void);
224 enum
226 PCMREC_NULL = 0,
227 PCMREC_INIT, /* enable recording */
228 PCMREC_CLOSE, /* close recording */
229 PCMREC_OPTIONS, /* set recording options */
230 PCMREC_RECORD, /* record a new file */
231 PCMREC_STOP, /* stop the current recording */
232 PCMREC_PAUSE, /* pause the current recording */
233 PCMREC_RESUME, /* resume the current recording */
234 #if 0
235 PCMREC_FLUSH_NUM, /* flush a number of files out */
236 #endif
239 /*******************************************************************/
240 /* Functions that are not executing in the pcmrec_thread first */
241 /*******************************************************************/
243 /* Callback for when more data is ready - called in interrupt context */
244 static int pcm_rec_have_more(int status)
246 if (status < 0)
248 /* some error condition */
249 if (status == DMA_REC_ERROR_DMA)
251 /* Flush recorded data to disk and stop recording */
252 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
253 return -1;
255 /* else try again next transmission */
257 else if (!dma_lock)
259 /* advance write position */
260 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
262 /* set pcm ovf if processing start position is inside current
263 write chunk */
264 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
265 warnings |= PCMREC_W_PCM_BUFFER_OVF;
267 dma_wr_pos = next_pos;
270 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
271 return 0;
272 } /* pcm_rec_have_more */
274 static void reset_hardware(void)
276 /* reset pcm to defaults (playback only) */
277 pcm_set_frequency(HW_SAMPR_DEFAULT);
278 audio_set_output_source(AUDIO_SRC_PLAYBACK);
279 pcm_apply_settings();
282 /** pcm_rec_* group **/
285 * Clear all errors and warnings
287 void pcm_rec_error_clear(void)
289 errors = warnings = 0;
290 } /* pcm_rec_error_clear */
293 * Check mode, errors and warnings
295 unsigned long pcm_rec_status(void)
297 unsigned long ret = 0;
299 if (is_recording)
300 ret |= AUDIO_STATUS_RECORD;
301 else if (pre_record_ticks)
302 ret |= AUDIO_STATUS_PRERECORD;
304 if (is_paused)
305 ret |= AUDIO_STATUS_PAUSE;
307 if (errors)
308 ret |= AUDIO_STATUS_ERROR;
310 if (warnings)
311 ret |= AUDIO_STATUS_WARNING;
313 return ret;
314 } /* pcm_rec_status */
317 * Return warnings that have occured since recording started
319 unsigned long pcm_rec_get_warnings(void)
321 return warnings;
324 #if 0
325 int pcm_rec_current_bitrate(void)
327 if (accum_pcm_samples == 0)
328 return 0;
330 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
331 } /* pcm_rec_current_bitrate */
332 #endif
334 #if 0
335 int pcm_rec_encoder_afmt(void)
337 return enc_config.afmt;
338 } /* pcm_rec_encoder_afmt */
339 #endif
341 #if 0
342 int pcm_rec_rec_format(void)
344 return afmt_rec_format[enc_config.afmt];
345 } /* pcm_rec_rec_format */
346 #endif
348 #ifdef HAVE_SPDIF_IN
349 unsigned long pcm_rec_sample_rate(void)
351 /* Which is better ?? */
352 #if 0
353 return enc_sample_rate;
354 #endif
355 return sample_rate;
356 } /* audio_get_sample_rate */
357 #endif
360 * Creates pcmrec_thread
362 void pcm_rec_init(void)
364 queue_init(&pcmrec_queue, true);
365 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send);
366 pcmrec_thread_p =
367 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
368 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
369 IF_COP(, CPU));
370 } /* pcm_rec_init */
372 /** audio_* group **/
375 * Initializes recording - call before calling any other recording function
377 void audio_init_recording(unsigned int buffer_offset)
379 logf("audio_init_recording");
380 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
381 logf("audio_init_recording done");
382 (void)buffer_offset;
383 } /* audio_init_recording */
386 * Closes recording - call audio_stop_recording first
388 void audio_close_recording(void)
390 logf("audio_close_recording");
391 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
392 logf("audio_close_recording done");
393 } /* audio_close_recording */
396 * Sets recording parameters
398 void audio_set_recording_options(struct audio_recording_options *options)
400 logf("audio_set_recording_options");
401 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
402 logf("audio_set_recording_options done");
403 } /* audio_set_recording_options */
406 * Start recording if not recording or else split
408 void audio_record(const char *filename)
410 logf("audio_record: %s", filename);
411 flush_interrupt();
412 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
413 logf("audio_record_done");
414 } /* audio_record */
417 * audio_record wrapper for API compatibility with HW codec
419 void audio_new_file(const char *filename)
421 audio_record(filename);
422 } /* audio_new_file */
425 * Stop current recording if recording
427 void audio_stop_recording(void)
429 logf("audio_stop_recording");
430 flush_interrupt();
431 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
432 logf("audio_stop_recording done");
433 } /* audio_stop_recording */
436 * Pause current recording
438 void audio_pause_recording(void)
440 logf("audio_pause_recording");
441 flush_interrupt();
442 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
443 logf("audio_pause_recording done");
444 } /* audio_pause_recording */
447 * Resume current recording if paused
449 void audio_resume_recording(void)
451 logf("audio_resume_recording");
452 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
453 logf("audio_resume_recording done");
454 } /* audio_resume_recording */
457 * Note that microphone is mono, only left value is used
458 * See audiohw_set_recvol() for exact ranges.
460 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
463 void audio_set_recording_gain(int left, int right, int type)
465 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
466 audiohw_set_recvol(left, right, type);
467 } /* audio_set_recording_gain */
469 /** Information about current state **/
472 * Return current recorded time in ticks (playback eqivalent time)
474 unsigned long audio_recorded_time(void)
476 if (!is_recording || enc_sample_rate == 0)
477 return 0;
479 /* return actual recorded time a la encoded data even if encoder rate
480 doesn't match the pcm rate */
481 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
482 } /* audio_recorded_time */
485 * Return number of bytes encoded to output
487 unsigned long audio_num_recorded_bytes(void)
489 if (!is_recording)
490 return 0;
492 return num_rec_bytes;
493 } /* audio_num_recorded_bytes */
495 /***************************************************************************/
496 /* */
497 /* Functions that execute in the context of pcmrec_thread */
498 /* */
499 /***************************************************************************/
501 /** Filename Queue **/
503 /* returns true if the queue is empty */
504 static inline bool pcmrec_fnq_is_empty(void)
506 return fnq_rd_pos == fnq_wr_pos;
507 } /* pcmrec_fnq_is_empty */
509 /* empties the filename queue */
510 static inline void pcmrec_fnq_set_empty(void)
512 fnq_rd_pos = fnq_wr_pos;
513 } /* pcmrec_fnq_set_empty */
515 /* returns true if the queue is full */
516 static bool pcmrec_fnq_is_full(void)
518 ssize_t size = fnq_wr_pos - fnq_rd_pos;
519 if (size < 0)
520 size += fnq_size;
522 return size >= fnq_size - MAX_PATH;
523 } /* pcmrec_fnq_is_full */
525 /* queue another filename - will overwrite oldest one if full */
526 static bool pcmrec_fnq_add_filename(const char *filename)
528 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
529 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
531 if (fnq_rd_pos != fnq_wr_pos)
532 return true;
534 /* queue full */
535 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
536 return true;
537 } /* pcmrec_fnq_add_filename */
539 /* replace the last filename added */
540 static bool pcmrec_fnq_replace_tail(const char *filename)
542 int pos;
544 if (pcmrec_fnq_is_empty())
545 return false;
547 pos = FNQ_PREV(fnq_wr_pos);
549 strncpy(fn_queue + pos, filename, MAX_PATH);
551 return true;
552 } /* pcmrec_fnq_replace_tail */
554 /* pulls the next filename from the queue */
555 static bool pcmrec_fnq_get_filename(char *filename)
557 if (pcmrec_fnq_is_empty())
558 return false;
560 if (filename)
561 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
563 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
564 return true;
565 } /* pcmrec_fnq_get_filename */
567 /* close the file number pointed to by fd_p */
568 static void pcmrec_close_file(int *fd_p)
570 if (*fd_p < 0)
571 return; /* preserve error */
573 if (close(*fd_p) != 0)
574 errors |= PCMREC_E_IO;
576 *fd_p = -1;
577 } /* pcmrec_close_file */
579 /** Data Flushing **/
582 * called after callback to update sizes if codec changed the amount of data
583 * a chunk represents
585 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
586 unsigned long prev_num_pcm)
588 if (rec_fdata.new_enc_size != prev_enc_size)
590 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
591 num_rec_bytes += size_diff;
592 #if 0
593 accum_rec_bytes += size_diff;
594 #endif
597 if (rec_fdata.new_num_pcm != prev_num_pcm)
599 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
600 num_rec_samples += pcm_diff;
601 #if 0
602 accum_pcm_samples += pcm_diff;
603 #endif
605 } /* pcmrec_update_sizes_inl */
607 /* don't need to inline every instance */
608 static void pcmrec_update_sizes(size_t prev_enc_size,
609 unsigned long prev_num_pcm)
611 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
612 } /* pcmrec_update_sizes */
614 static void pcmrec_start_file(void)
616 size_t enc_size = rec_fdata.new_enc_size;
617 unsigned long num_pcm = rec_fdata.new_num_pcm;
618 int curr_rec_file = rec_fdata.rec_file;
619 char filename[MAX_PATH];
621 /* must always pull the filename that matches with this queue */
622 if (!pcmrec_fnq_get_filename(filename))
624 logf("start file: fnq empty");
625 *filename = '\0';
626 errors |= PCMREC_E_FNQ_DESYNC;
628 else if (errors != 0)
630 logf("start file: error already");
632 else if (curr_rec_file >= 0)
634 /* Any previous file should have been closed */
635 logf("start file: file already open");
636 errors |= PCMREC_E_FNQ_DESYNC;
639 if (errors != 0)
640 rec_fdata.chunk->flags |= CHUNKF_ERROR;
642 /* encoder can set error flag here and should increase
643 enc_new_size and pcm_new_size to reflect additional
644 data written if any */
645 rec_fdata.filename = filename;
646 enc_events_callback(ENC_START_FILE, &rec_fdata);
648 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
650 logf("start file: enc error");
651 errors |= PCMREC_E_ENCODER;
654 if (errors != 0)
656 pcmrec_close_file(&curr_rec_file);
657 /* Write no more to this file */
658 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
660 else
662 pcmrec_update_sizes(enc_size, num_pcm);
665 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
666 } /* pcmrec_start_file */
668 static inline void pcmrec_write_chunk(void)
670 size_t enc_size = rec_fdata.new_enc_size;
671 unsigned long num_pcm = rec_fdata.new_num_pcm;
673 if (errors != 0)
674 rec_fdata.chunk->flags |= CHUNKF_ERROR;
676 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
678 if ((long)rec_fdata.chunk->flags >= 0)
680 pcmrec_update_sizes_inl(enc_size, num_pcm);
682 else if (errors == 0)
684 logf("wr chk enc error %lu %lu",
685 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
686 errors |= PCMREC_E_ENCODER;
688 } /* pcmrec_write_chunk */
690 static void pcmrec_end_file(void)
692 /* all data in output buffer for current file will have been
693 written and encoder can now do any nescessary steps to
694 finalize the written file */
695 size_t enc_size = rec_fdata.new_enc_size;
696 unsigned long num_pcm = rec_fdata.new_num_pcm;
698 enc_events_callback(ENC_END_FILE, &rec_fdata);
700 if (errors == 0)
702 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
704 logf("end file: enc error");
705 errors |= PCMREC_E_ENCODER;
707 else
709 pcmrec_update_sizes(enc_size, num_pcm);
713 /* Force file close if error */
714 if (errors != 0)
715 pcmrec_close_file(&rec_fdata.rec_file);
717 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
718 } /* pcmrec_end_file */
721 * Update buffer watermarks with spinup time compensation
723 * All this assumes reasonable data rates, chunk sizes and sufficient
724 * memory for the most part. Some dumb checks are included but perhaps
725 * are pointless since this all will break down at extreme limits that
726 * are currently not applicable to any supported device.
728 static void pcmrec_refresh_watermarks(void)
730 logf("ata spinup: %d", ata_spinup_time);
732 /* set the low mark for when flushing stops if automatic */
733 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
734 / enc_chunk_size;
735 logf("low wmk: %d", low_watermark);
737 #ifdef HAVE_PRIORITY_SCHEDULING
738 /* panic boost thread priority if 2 seconds of ground is lost -
739 this allows encoder to boost with just under a second of
740 pcm data (if not yet full enough to boost itself)
741 and not falsely trip the alarm. */
742 flood_watermark = enc_num_chunks -
743 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
744 / enc_chunk_size;
746 if (flood_watermark < low_watermark)
748 logf("warning: panic < low");
749 flood_watermark = low_watermark;
752 logf("flood at: %d", flood_watermark);
753 #endif
754 spinup_time = last_ata_spinup_time = ata_spinup_time;
756 /* write at 8s + st remaining in enc_buffer - range 12s to
757 20s total - default to 3.5s spinup. */
758 if (spinup_time == 0)
759 spinup_time = 35*HZ/10; /* default - cozy */
760 else if (spinup_time < 2*HZ)
761 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
762 else if (spinup_time > 10*HZ)
763 spinup_time = 10*HZ; /* do you have a functioning HD? */
765 /* try to start writing with 10s remaining after disk spinup */
766 high_watermark = enc_num_chunks -
767 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
768 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
770 if (high_watermark < low_watermark)
772 high_watermark = low_watermark;
773 low_watermark /= 2;
774 logf("warning: low 'write at'");
777 logf("write at: %d", high_watermark);
778 } /* pcmrec_refresh_watermarks */
781 * Process the chunks
783 * This function is called when queue_get_w_tmo times out.
785 * Set flush_num to the number of files to flush to disk or to
786 * a PCMREC_FLUSH_* constant.
788 static void pcmrec_flush(unsigned flush_num)
790 #ifdef HAVE_PRIORITY_SCHEDULING
791 static unsigned long last_flush_tick; /* tick when function returned */
792 unsigned long start_tick; /* When flush started */
793 unsigned long prio_tick; /* Timeout for auto boost */
794 int prio_pcmrec; /* Current thread priority for pcmrec */
795 int prio_codec; /* Current thread priority for codec */
796 #endif
797 int num_ready; /* Number of chunks ready at start */
798 unsigned remaining; /* Number of file starts remaining */
799 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
800 bool interruptable; /* Flush can be interupted */
802 num_ready = enc_wr_index - enc_rd_index;
803 if (num_ready < 0)
804 num_ready += enc_num_chunks;
806 /* save interruptable flag and remove it to get the actual count */
807 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
808 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
810 if (flush_num == 0)
812 if (!is_recording)
813 return;
815 if (ata_spinup_time != last_ata_spinup_time)
816 pcmrec_refresh_watermarks();
818 /* enough available? no? then leave */
819 if (num_ready < high_watermark)
820 return;
821 } /* endif (flush_num == 0) */
823 #ifdef HAVE_PRIORITY_SCHEDULING
824 start_tick = current_tick;
825 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
827 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
829 /* if we're getting called too much and this isn't forced,
830 boost stat by expiring timeout in advance */
831 logf("too frequent flush");
832 prio_tick = current_tick - 1;
835 prio_pcmrec = -1;
836 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
837 need init */
838 #endif
840 logf("writing:%d(%d):%s%s", num_ready, flush_num,
841 interruptable ? "i" : "",
842 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
844 cpu_boost(true);
846 remaining = flush_num;
847 chunks_flushed = 0;
849 while (num_ready > 0)
851 /* check current number of encoder chunks */
852 int num = enc_wr_index - enc_rd_index;
853 if (num < 0)
854 num += enc_num_chunks;
856 if (num <= low_watermark &&
857 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
859 logf("low data: %d", num);
860 break; /* data remaining is below threshold */
863 if (interruptable && flush_interrupts > 0)
865 logf("int at: %d", num);
866 break; /* interrupted */
869 #ifdef HAVE_PRIORITY_SCHEDULING
870 if (prio_pcmrec == -1 && (num >= flood_watermark ||
871 TIME_AFTER(current_tick, prio_tick)))
873 /* losing ground or holding without progress - boost
874 priority until finished */
875 logf("pcmrec: boost (%s)",
876 num >= flood_watermark ? "num" : "time");
877 prio_pcmrec = thread_set_priority(NULL,
878 thread_get_priority(NULL) - 1);
879 prio_codec = thread_set_priority(codec_thread_p,
880 thread_get_priority(codec_thread_p) - 1);
882 #endif
884 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
885 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
886 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
888 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
890 pcmrec_start_file();
891 if (--remaining == 0)
892 num_ready = 0; /* stop on next loop - must write this
893 chunk if it has data */
896 pcmrec_write_chunk();
898 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
899 pcmrec_end_file();
901 INC_ENC_INDEX(enc_rd_index);
903 if (errors != 0)
905 pcmrec_end_file();
906 break;
909 if (flush_num == PCMREC_FLUSH_MINI &&
910 ++chunks_flushed >= MINI_CHUNKS)
912 logf("mini flush break");
913 break;
915 /* no yielding; the file apis called in the codecs do that
916 sufficiently */
917 } /* end while */
919 /* sync file */
920 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
921 errors |= PCMREC_E_IO;
923 cpu_boost(false);
925 #ifdef HAVE_PRIORITY_SCHEDULING
926 if (prio_pcmrec != -1)
928 /* return to original priorities */
929 logf("pcmrec: unboost priority");
930 thread_set_priority(NULL, prio_pcmrec);
931 thread_set_priority(codec_thread_p, prio_codec);
934 last_flush_tick = current_tick; /* save tick when we left */
935 #endif
937 logf("done");
938 } /* pcmrec_flush */
941 * Marks a new stream in the buffer and gives the encoder a chance for special
942 * handling of transition from one to the next. The encoder may change the
943 * chunk that ends the old stream by requesting more chunks and similiarly for
944 * the new but must always advance the position though the interface. It can
945 * later reject any data it cares to when writing the file but should mark the
946 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
947 * a NULL data pointer without error as well.
949 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
951 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
952 } /* pcmrec_get_chunk_index */
954 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
956 DEC_ENC_INDEX(index);
957 return GET_ENC_CHUNK(index);
958 } /* pcmrec_get_prev_chunk */
960 static void pcmrec_new_stream(const char *filename, /* next file name */
961 unsigned long flags, /* CHUNKF_* flags */
962 int pre_index) /* index for prerecorded data */
964 logf("pcmrec_new_stream");
965 char path[MAX_PATH]; /* place to copy filename so sender can be released */
967 struct enc_buffer_event_data data;
968 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
969 new filename */
970 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
971 stream */
972 bool did_flush = false; /* did a flush occurr? */
974 if (filename)
975 strncpy(path, filename, MAX_PATH);
976 queue_reply(&pcmrec_queue, 0); /* We have all we need */
978 data.pre_chunk = NULL;
979 data.chunk = GET_ENC_CHUNK(enc_wr_index);
981 /* end chunk */
982 if (flags & CHUNKF_END_FILE)
984 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
986 if (data.chunk->flags & CHUNKF_START_FILE)
988 /* cannot start and end on same unprocessed chunk */
989 logf("file end on start");
990 flags &= ~CHUNKF_END_FILE;
992 else if (enc_rd_index == enc_wr_index)
994 /* all data flushed but file not ended - chunk will be left
995 empty */
996 logf("end on dead end");
997 data.chunk->flags = 0;
998 data.chunk->enc_size = 0;
999 data.chunk->num_pcm = 0;
1000 data.chunk->enc_data = NULL;
1001 INC_ENC_INDEX(enc_wr_index);
1002 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1004 else
1006 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1008 if (last->flags & CHUNKF_END_FILE)
1010 /* end already processed and marked - can't end twice */
1011 logf("file end again");
1012 flags &= ~CHUNKF_END_FILE;
1017 /* start chunk */
1018 if (flags & CHUNKF_START_FILE)
1020 bool pre = flags & CHUNKF_PRERECORD;
1022 if (pre)
1024 logf("stream prerecord start");
1025 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1026 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1028 else
1030 logf("stream normal start");
1031 start = data.chunk;
1032 start->flags &= CHUNKF_START_FILE;
1035 /* if encoder hasn't yet processed the last start - abort the start
1036 of the previous file queued or else it will be empty and invalid */
1037 if (start->flags & CHUNKF_START_FILE)
1039 logf("replacing fnq tail: %s", filename);
1040 fnq_add_fn = pcmrec_fnq_replace_tail;
1042 else
1044 logf("adding filename: %s", filename);
1045 fnq_add_fn = pcmrec_fnq_add_filename;
1049 data.flags = flags;
1050 pcmrec_context = true; /* switch encoder context */
1051 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1052 pcmrec_context = false; /* switch back */
1054 if (flags & CHUNKF_END_FILE)
1056 int i = pcmrec_get_chunk_index(data.chunk);
1057 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1060 if (start)
1062 if (!(flags & CHUNKF_PRERECORD))
1064 /* get stats on data added to start - sort of a prerecord
1065 operation */
1066 int i = pcmrec_get_chunk_index(data.chunk);
1067 struct enc_chunk_hdr *chunk = data.chunk;
1069 logf("start data: %d %d", i, enc_wr_index);
1071 num_rec_bytes = 0;
1072 num_rec_samples = 0;
1074 while (i != enc_wr_index)
1076 num_rec_bytes += chunk->enc_size;
1077 num_rec_samples += chunk->num_pcm;
1078 INC_ENC_INDEX(i);
1079 chunk = GET_ENC_CHUNK(i);
1082 start->flags &= ~CHUNKF_START_FILE;
1083 start = data.chunk;
1086 start->flags |= CHUNKF_START_FILE;
1088 /* flush all pending files out if full and adding */
1089 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1091 logf("fnq full");
1092 pcmrec_flush(PCMREC_FLUSH_ALL);
1093 did_flush = true;
1096 fnq_add_fn(path);
1099 /* Make sure to complete any interrupted high watermark */
1100 if (!did_flush)
1101 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1102 } /* pcmrec_new_stream */
1104 /** event handlers for pcmrec thread */
1106 /* PCMREC_INIT */
1107 static void pcmrec_init(void)
1109 unsigned char *buffer;
1111 /* warings and errors */
1112 warnings =
1113 errors = 0;
1115 pcmrec_close_file(&rec_fdata.rec_file);
1116 rec_fdata.rec_file = -1;
1118 /* pcm FIFO */
1119 dma_lock = true;
1120 pcm_rd_pos = 0;
1121 dma_wr_pos = 0;
1122 pcm_enc_pos = 0;
1124 /* encoder FIFO */
1125 enc_wr_index = 0;
1126 enc_rd_index = 0;
1128 /* filename queue */
1129 fnq_rd_pos = 0;
1130 fnq_wr_pos = 0;
1132 /* stats */
1133 num_rec_bytes = 0;
1134 num_rec_samples = 0;
1135 #if 0
1136 accum_rec_bytes = 0;
1137 accum_pcm_samples = 0;
1138 #endif
1140 pre_record_ticks = 0;
1142 is_recording = false;
1143 is_paused = false;
1145 buffer = audio_get_recording_buffer(&rec_buffer_size);
1147 /* Line align pcm_buffer 2^4=16 bytes */
1148 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1149 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1150 PCM_MAX_FEED_SIZE, 2);
1151 /* Adjust available buffer for possible align advancement */
1152 rec_buffer_size -= pcm_buffer - buffer;
1154 pcm_init_recording();
1155 } /* pcmrec_init */
1157 /* PCMREC_CLOSE */
1158 static void pcmrec_close(void)
1160 dma_lock = true;
1161 pre_record_ticks = 0; /* Can't be prerecording any more */
1162 warnings = 0;
1163 pcm_close_recording();
1164 reset_hardware();
1165 audio_remove_encoder();
1166 } /* pcmrec_close */
1168 /* PCMREC_OPTIONS */
1169 static void pcmrec_set_recording_options(
1170 struct audio_recording_options *options)
1172 /* stop DMA transfer */
1173 dma_lock = true;
1174 pcm_stop_recording();
1176 rec_frequency = options->rec_frequency;
1177 rec_source = options->rec_source;
1178 num_channels = options->rec_channels == 1 ? 1 : 2;
1179 pre_record_ticks = options->rec_prerecord_time * HZ;
1180 enc_config = options->enc_config;
1181 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1183 #ifdef HAVE_SPDIF_IN
1184 if (rec_source == AUDIO_SRC_SPDIF)
1186 /* must measure SPDIF sample rate before configuring codecs */
1187 unsigned long sr = spdif_measure_frequency();
1188 /* round to master list for SPDIF rate */
1189 int index = round_value_to_list32(sr, audio_master_sampr_list,
1190 SAMPR_NUM_FREQ, false);
1191 sample_rate = audio_master_sampr_list[index];
1192 /* round to HW playback rates for monitoring */
1193 index = round_value_to_list32(sr, hw_freq_sampr,
1194 HW_NUM_FREQ, false);
1195 pcm_set_frequency(hw_freq_sampr[index]);
1196 /* encoders with a limited number of rates do their own rounding */
1198 else
1199 #endif
1201 /* set sample rate from frequency selection */
1202 sample_rate = rec_freq_sampr[rec_frequency];
1203 pcm_set_frequency(sample_rate);
1206 /* set monitoring */
1207 audio_set_output_source(rec_source);
1209 /* apply hardware setting to start monitoring now */
1210 pcm_apply_settings();
1212 queue_reply(&pcmrec_queue, 0); /* Release sender */
1214 if (audio_load_encoder(enc_config.afmt))
1216 /* start DMA transfer */
1217 dma_lock = pre_record_ticks == 0;
1218 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1219 PCM_CHUNK_SIZE);
1221 else
1223 logf("set rec opt: enc load failed");
1224 errors |= PCMREC_E_LOAD_ENCODER;
1226 } /* pcmrec_set_recording_options */
1228 /* PCMREC_RECORD - start recording (not gapless)
1229 or split stream (gapless) */
1230 static void pcmrec_record(const char *filename)
1232 unsigned long pre_sample_ticks;
1233 int rd_start;
1234 unsigned long flags;
1235 int pre_index;
1237 logf("pcmrec_record: %s", filename);
1239 /* reset stats */
1240 num_rec_bytes = 0;
1241 num_rec_samples = 0;
1243 if (!is_recording)
1245 #if 0
1246 accum_rec_bytes = 0;
1247 accum_pcm_samples = 0;
1248 #endif
1249 warnings = 0; /* reset warnings */
1251 rd_start = enc_wr_index;
1252 pre_sample_ticks = 0;
1254 pcmrec_refresh_watermarks();
1256 if (pre_record_ticks)
1258 int i = rd_start;
1259 /* calculate number of available chunks */
1260 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1261 enc_num_chunks) % enc_num_chunks;
1262 /* overflow at 974 seconds of prerecording at 44.1kHz */
1263 unsigned long pre_record_sample_ticks =
1264 enc_sample_rate*pre_record_ticks;
1265 int pre_chunks = 0; /* Counter to limit prerecorded time to
1266 prevent flood state at outset */
1268 logf("pre-st: %ld", pre_record_sample_ticks);
1270 /* Get exact measure of recorded data as number of samples aren't
1271 nescessarily going to be the max for each chunk */
1272 for (; avail_pre_chunks-- > 0;)
1274 struct enc_chunk_hdr *chunk;
1275 unsigned long chunk_sample_ticks;
1277 DEC_ENC_INDEX(i);
1279 chunk = GET_ENC_CHUNK(i);
1281 /* must have data to be counted */
1282 if (chunk->enc_data == NULL)
1283 continue;
1285 chunk_sample_ticks = chunk->num_pcm*HZ;
1287 rd_start = i;
1288 pre_sample_ticks += chunk_sample_ticks;
1289 num_rec_bytes += chunk->enc_size;
1290 num_rec_samples += chunk->num_pcm;
1291 pre_chunks++;
1293 /* stop here if enough already */
1294 if (pre_chunks >= high_watermark ||
1295 pre_sample_ticks >= pre_record_sample_ticks)
1297 logf("pre-chks: %d", pre_chunks);
1298 break;
1302 #if 0
1303 accum_rec_bytes = num_rec_bytes;
1304 accum_pcm_samples = num_rec_samples;
1305 #endif
1308 enc_rd_index = rd_start;
1310 /* filename queue should be empty */
1311 if (!pcmrec_fnq_is_empty())
1313 logf("fnq: not empty!");
1314 pcmrec_fnq_set_empty();
1317 flags = CHUNKF_START_FILE;
1318 if (pre_sample_ticks > 0)
1319 flags |= CHUNKF_PRERECORD;
1321 pre_index = enc_rd_index;
1323 dma_lock = false;
1324 is_paused = false;
1325 is_recording = true;
1327 else
1329 /* already recording, just split the stream */
1330 logf("inserting split");
1331 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1332 pre_index = 0;
1335 pcmrec_new_stream(filename, flags, pre_index);
1336 logf("pcmrec_record done");
1337 } /* pcmrec_record */
1339 /* PCMREC_STOP */
1340 static void pcmrec_stop(void)
1342 logf("pcmrec_stop");
1344 if (is_recording)
1346 dma_lock = true; /* lock dma write position */
1348 /* flush all available data first to avoid overflow while waiting
1349 for encoding to finish */
1350 pcmrec_flush(PCMREC_FLUSH_ALL);
1352 /* wait for encoder to finish remaining data */
1353 while (errors == 0 && !pcm_buffer_empty)
1354 yield();
1356 /* end stream at last data */
1357 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1359 /* flush anything else encoder added */
1360 pcmrec_flush(PCMREC_FLUSH_ALL);
1362 /* remove any pending file start not yet processed - should be at
1363 most one at enc_wr_index */
1364 pcmrec_fnq_get_filename(NULL);
1365 /* encoder should abort any chunk it was in midst of processing */
1366 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1368 /* filename queue should be empty */
1369 if (!pcmrec_fnq_is_empty())
1371 logf("fnq: not empty!");
1372 pcmrec_fnq_set_empty();
1375 /* be absolutely sure the file is closed */
1376 if (errors != 0)
1377 pcmrec_close_file(&rec_fdata.rec_file);
1378 rec_fdata.rec_file = -1;
1380 is_recording = false;
1381 is_paused = false;
1382 dma_lock = pre_record_ticks == 0;
1384 else
1386 logf("not recording");
1389 logf("pcmrec_stop done");
1390 } /* pcmrec_stop */
1392 /* PCMREC_PAUSE */
1393 static void pcmrec_pause(void)
1395 logf("pcmrec_pause");
1397 if (!is_recording)
1399 logf("not recording");
1401 else if (is_paused)
1403 logf("already paused");
1405 else
1407 dma_lock = true;
1408 is_paused = true;
1411 logf("pcmrec_pause done");
1412 } /* pcmrec_pause */
1414 /* PCMREC_RESUME */
1415 static void pcmrec_resume(void)
1417 logf("pcmrec_resume");
1419 if (!is_recording)
1421 logf("not recording");
1423 else if (!is_paused)
1425 logf("not paused");
1427 else
1429 is_paused = false;
1430 is_recording = true;
1431 dma_lock = false;
1434 logf("pcmrec_resume done");
1435 } /* pcmrec_resume */
1437 static void pcmrec_thread(void) __attribute__((noreturn));
1438 static void pcmrec_thread(void)
1440 struct queue_event ev;
1442 logf("thread pcmrec start");
1444 while(1)
1446 if (is_recording)
1448 /* Poll periodically to flush data */
1449 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1451 if (ev.id == SYS_TIMEOUT)
1453 /* Messages that interrupt this will complete it */
1454 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1455 PCMREC_FLUSH_INTERRUPTABLE);
1456 continue;
1459 else
1461 /* Not doing anything - sit and wait for commands */
1462 queue_wait(&pcmrec_queue, &ev);
1465 switch (ev.id)
1467 case PCMREC_INIT:
1468 pcmrec_init();
1469 break;
1471 case PCMREC_CLOSE:
1472 pcmrec_close();
1473 break;
1475 case PCMREC_OPTIONS:
1476 pcmrec_set_recording_options(
1477 (struct audio_recording_options *)ev.data);
1478 break;
1480 case PCMREC_RECORD:
1481 clear_flush_interrupt();
1482 pcmrec_record((const char *)ev.data);
1483 break;
1485 case PCMREC_STOP:
1486 clear_flush_interrupt();
1487 pcmrec_stop();
1488 break;
1490 case PCMREC_PAUSE:
1491 clear_flush_interrupt();
1492 pcmrec_pause();
1493 break;
1495 case PCMREC_RESUME:
1496 pcmrec_resume();
1497 break;
1498 #if 0
1499 case PCMREC_FLUSH_NUM:
1500 pcmrec_flush((unsigned)ev.data);
1501 break;
1502 #endif
1503 case SYS_USB_CONNECTED:
1504 if (is_recording)
1505 break;
1506 pcmrec_close();
1507 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1508 usb_wait_for_disconnect(&pcmrec_queue);
1509 flush_interrupts = 0;
1510 break;
1511 } /* end switch */
1512 } /* end while */
1513 } /* pcmrec_thread */
1515 /****************************************************************************/
1516 /* */
1517 /* following functions will be called by the encoder codec */
1518 /* in a free-threaded manner */
1519 /* */
1520 /****************************************************************************/
1522 /* pass the encoder settings to the encoder */
1523 void enc_get_inputs(struct enc_inputs *inputs)
1525 inputs->sample_rate = sample_rate;
1526 inputs->num_channels = num_channels;
1527 inputs->config = &enc_config;
1528 } /* enc_get_inputs */
1530 /* set the encoder dimensions (called by encoder codec at initialization and
1531 termination) */
1532 void enc_set_parameters(struct enc_parameters *params)
1534 size_t bufsize, resbytes;
1536 logf("enc_set_parameters");
1538 if (!params)
1540 logf("reset");
1541 /* Encoder is terminating */
1542 memset(&enc_config, 0, sizeof (enc_config));
1543 enc_sample_rate = 0;
1544 return;
1547 enc_sample_rate = params->enc_sample_rate;
1548 logf("enc sampr:%lu", enc_sample_rate);
1550 pcm_rd_pos = dma_wr_pos;
1551 pcm_enc_pos = pcm_rd_pos;
1553 enc_config.afmt = params->afmt;
1554 /* addition of the header is always implied - chunk size 4-byte aligned */
1555 enc_chunk_size =
1556 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1557 enc_events_callback = params->events_callback;
1559 logf("chunk size:%lu", enc_chunk_size);
1561 /*** Configure the buffers ***/
1563 /* Layout of recording buffer:
1564 * [ax] = possible alignment x multiple
1565 * [sx] = possible size alignment of x multiple
1566 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1567 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1569 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1570 logf("resbytes:%lu", resbytes);
1572 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1573 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1574 #ifdef DEBUG
1575 - sizeof (*wrap_id_p)
1576 #endif
1579 enc_num_chunks = bufsize / enc_chunk_size;
1580 logf("num chunks:%d", enc_num_chunks);
1582 /* get real amount used by encoder chunks */
1583 bufsize = enc_num_chunks*enc_chunk_size;
1584 logf("enc size:%lu", bufsize);
1586 #ifdef DEBUG
1587 /* add magic at wraparound for spillover checks */
1588 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1589 bufsize += sizeof (*wrap_id_p);
1590 *wrap_id_p = ENC_CHUNK_MAGIC;
1591 #endif
1593 /** set OUT parameters **/
1594 params->enc_buffer = enc_buffer;
1595 params->buf_chunk_size = enc_chunk_size;
1596 params->num_chunks = enc_num_chunks;
1598 /* calculate reserve buffer start and return pointer to encoder */
1599 params->reserve_buffer = NULL;
1600 if (resbytes > 0)
1602 params->reserve_buffer = enc_buffer + bufsize;
1603 bufsize += resbytes;
1606 /* place filename queue at end of buffer using up whatever remains */
1607 fnq_rd_pos = 0; /* reset */
1608 fnq_wr_pos = 0; /* reset */
1609 fn_queue = enc_buffer + bufsize;
1610 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1611 fnq_size /= MAX_PATH;
1612 if (fnq_size > FNQ_MAX_NUM_PATHS)
1613 fnq_size = FNQ_MAX_NUM_PATHS;
1614 fnq_size *= MAX_PATH;
1615 logf("fnq files:%ld", fnq_size / MAX_PATH);
1617 #if defined(DEBUG)
1618 logf("ab :%08lX", (uintptr_t)audiobuf);
1619 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1620 logf("enc:%08lX", (uintptr_t)enc_buffer);
1621 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1622 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1623 logf("fnq:%08lX", (uintptr_t)fn_queue);
1624 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1625 logf("abe:%08lX", (uintptr_t)audiobufend);
1626 #endif
1628 /* init all chunk headers and reset indexes */
1629 enc_rd_index = 0;
1630 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1632 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1633 #ifdef DEBUG
1634 chunk->id = ENC_CHUNK_MAGIC;
1635 #endif
1636 chunk->flags = 0;
1639 logf("enc_set_parameters done");
1640 } /* enc_set_parameters */
1642 /* return encoder chunk at current write position -
1643 NOTE: can be called by pcmrec thread when splitting streams */
1644 struct enc_chunk_hdr * enc_get_chunk(void)
1646 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1648 #ifdef DEBUG
1649 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1651 errors |= PCMREC_E_CHUNK_OVF;
1652 logf("finish chk ovf: %d", enc_wr_index);
1654 #endif
1656 chunk->flags &= CHUNKF_START_FILE;
1658 if (!is_recording)
1659 chunk->flags |= CHUNKF_PRERECORD;
1661 return chunk;
1662 } /* enc_get_chunk */
1664 /* releases the current chunk into the available chunks -
1665 NOTE: can be called by pcmrec thread when splitting streams */
1666 void enc_finish_chunk(void)
1668 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1670 if ((long)chunk->flags < 0)
1672 /* encoder set error flag */
1673 errors |= PCMREC_E_ENCODER;
1674 logf("finish chk enc error");
1677 /* advance enc_wr_index to the next encoder chunk */
1678 INC_ENC_INDEX(enc_wr_index);
1680 if (enc_rd_index != enc_wr_index)
1682 num_rec_bytes += chunk->enc_size;
1683 num_rec_samples += chunk->num_pcm;
1684 #if 0
1685 accum_rec_bytes += chunk->enc_size;
1686 accum_pcm_samples += chunk->num_pcm;
1687 #endif
1689 else if (is_recording) /* buffer full */
1691 /* keep current position and put up warning flag */
1692 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1693 logf("enc_buffer ovf");
1694 DEC_ENC_INDEX(enc_wr_index);
1695 if (pcmrec_context)
1697 /* if stream splitting, keep this out of circulation and
1698 flush a small number, then readd - cannot risk losing
1699 stream markers */
1700 logf("mini flush");
1701 pcmrec_flush(PCMREC_FLUSH_MINI);
1702 INC_ENC_INDEX(enc_wr_index);
1705 else
1707 /* advance enc_rd_index for prerecording */
1708 INC_ENC_INDEX(enc_rd_index);
1710 } /* enc_finish_chunk */
1712 /* checks near empty state on pcm input buffer */
1713 int enc_pcm_buf_near_empty(void)
1715 /* less than 1sec raw data? => unboost encoder */
1716 int wp = dma_wr_pos;
1717 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1718 return avail < (sample_rate << 2) ? 1 : 0;
1719 } /* enc_pcm_buf_near_empty */
1721 /* passes a pointer to next chunk of unprocessed wav data */
1722 /* TODO: this really should give the actual size returned */
1723 unsigned char * enc_get_pcm_data(size_t size)
1725 int wp = dma_wr_pos;
1726 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1728 /* limit the requested pcm data size */
1729 if (size > PCM_MAX_FEED_SIZE)
1730 size = PCM_MAX_FEED_SIZE;
1732 if (avail >= size)
1734 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1735 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1737 pcm_enc_pos = pcm_rd_pos;
1738 pcm_rd_pos = next_pos;
1740 /* ptr must point to continous data at wraparound position */
1741 if ((size_t)pcm_rd_pos < size)
1743 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1744 pcm_buffer, pcm_rd_pos);
1747 pcm_buffer_empty = false;
1748 return ptr;
1751 /* not enough data available - encoder should idle */
1752 pcm_buffer_empty = true;
1753 return NULL;
1754 } /* enc_get_pcm_data */
1756 /* puts some pcm data back in the queue */
1757 size_t enc_unget_pcm_data(size_t size)
1759 int wp = dma_wr_pos;
1760 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1761 2*PCM_CHUNK_SIZE;
1763 /* allow one interrupt to occur during this call and not have the
1764 new read position inside the DMA destination chunk */
1765 if ((ssize_t)old_avail > 0)
1767 /* limit size to amount of old data remaining */
1768 if (size > old_avail)
1769 size = old_avail;
1771 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1772 pcm_rd_pos = pcm_enc_pos;
1774 return size;
1777 return 0;
1778 } /* enc_unget_pcm_data */