clean up some debugging output.
[Rockbox.git] / firmware / pcm_record.c
blob045ace98c4dc9bc4539e9cfe6bf85cc0bc0b565a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "system.h"
22 #include "kernel.h"
23 #include "logf.h"
24 #include "thread.h"
25 #include <string.h>
26 #include "ata.h"
27 #include "usb.h"
28 #include "buffer.h"
29 #include "general.h"
30 #include "audio.h"
31 #include "sound.h"
32 #include "id3.h"
33 #ifdef HAVE_SPDIF_IN
34 #include "spdif.h"
35 #endif
37 /***************************************************************************/
39 extern struct thread_entry *codec_thread_p;
41 /** General recording state **/
42 static bool is_recording; /* We are recording */
43 static bool is_paused; /* We have paused */
44 static unsigned long errors; /* An error has occured */
45 static unsigned long warnings; /* Warning */
46 static int flush_interrupts = 0; /* Number of messages queued that
47 should interrupt a flush in
48 progress -
49 for a safety net and a prompt
50 response to stop, split and pause
51 requests -
52 only interrupts a flush initiated
53 by pcmrec_flush(0) */
55 /* Utility functions for setting/clearing flushing interrupt flag */
56 static inline void flush_interrupt(void)
58 flush_interrupts++;
59 logf("flush int: %d", flush_interrupts);
62 static inline void clear_flush_interrupt(void)
64 if (--flush_interrupts < 0)
65 flush_interrupts = 0;
68 /** Stats on encoded data for current file **/
69 static size_t num_rec_bytes; /* Num bytes recorded */
70 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
72 /** Stats on encoded data for all files from start to stop **/
73 #if 0
74 static unsigned long long accum_rec_bytes; /* total size written to chunks */
75 static unsigned long long accum_pcm_samples; /* total pcm count processed */
76 #endif
78 /* Keeps data about current file and is sent as event data for codec */
79 static struct enc_file_event_data rec_fdata IDATA_ATTR =
81 .chunk = NULL,
82 .new_enc_size = 0,
83 .new_num_pcm = 0,
84 .rec_file = -1,
85 .num_pcm_samples = 0
88 /** These apply to current settings **/
89 static int rec_source; /* current rec_source setting */
90 static int rec_frequency; /* current frequency setting */
91 static unsigned long sample_rate; /* Sample rate in HZ */
92 static int num_channels; /* Current number of channels */
93 static struct encoder_config enc_config; /* Current encoder configuration */
94 static unsigned long pre_record_ticks; /* pre-record time in ticks */
96 /****************************************************************************
97 use 2 circular buffers:
98 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
99 enc_buffer=encoded audio buffer: storage for encoder output data
101 Flow:
102 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
103 2. if enough pcm data are available the encoder codec does encoding of pcm
104 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
105 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
107 Functions calls (basic encoder steps):
108 1.main: audio_load_encoder(); start the encoder
109 2.encoder: enc_get_inputs(); get encoder recording settings
110 3.encoder: enc_set_parameters(); set the encoder parameters
111 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
112 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
113 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
114 7.encoder: <process enc chunk> compress and store data to enc chunk
115 8.encoder: enc_finish_chunk(); inform main about chunk processed and
116 is available to be written to a file.
117 Encoder can place any number of chunks
118 of PCM data in a single output chunk
119 but must stay within its output chunk
120 size
121 9.encoder: repeat 4. to 8.
122 A.pcmrec: enc_events_callback(); called for certain events
124 (*) Optional step
125 ****************************************************************************/
127 /** buffer parameters where incoming PCM data is placed **/
128 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
129 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
130 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
132 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
133 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
134 #define INC_ENC_INDEX(index) \
135 { if (++index >= enc_num_chunks) index = 0; }
136 #define DEC_ENC_INDEX(index) \
137 { if (--index < 0) index = enc_num_chunks - 1; }
139 static size_t rec_buffer_size; /* size of available buffer */
140 static unsigned char *pcm_buffer; /* circular recording buffer */
141 static unsigned char *enc_buffer; /* circular encoding buffer */
142 #ifdef DEBUG
143 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
144 aid to check if the encoder data
145 spilled out of its chunk */
146 #endif /* DEBUG */
147 static volatile int dma_wr_pos; /* current DMA write pos */
148 static int pcm_rd_pos; /* current PCM read pos */
149 static int pcm_enc_pos; /* position encoder is processing */
150 static volatile bool dma_lock; /* lock DMA write position */
151 static int enc_wr_index; /* encoder chunk write index */
152 static int enc_rd_index; /* encoder chunk read index */
153 static int enc_num_chunks; /* number of chunks in ringbuffer */
154 static size_t enc_chunk_size; /* maximum encoder chunk size */
155 static unsigned long enc_sample_rate; /* sample rate used by encoder */
156 static bool pcmrec_context = false; /* called by pcmrec thread? */
157 static bool pcm_buffer_empty; /* all pcm chunks processed? */
159 /** file flushing **/
160 static int low_watermark; /* Low watermark to stop flush */
161 static int high_watermark; /* max chunk limit for data flush */
162 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
163 static int last_ata_spinup_time = -1;/* previous spin time used */
164 #ifdef HAVE_PRIORITY_SCHEDULING
165 static int flood_watermark; /* boost thread priority when here */
166 #endif
168 /* Constants that control watermarks */
169 #define LOW_SECONDS 1 /* low watermark time till empty */
170 #define MINI_CHUNKS 10 /* chunk count for mini flush */
171 #ifdef HAVE_PRIORITY_SCHEDULING
172 #define PRIO_SECONDS 10 /* max flush time before priority boost */
173 #endif
174 #if MEM <= 16
175 #define PANIC_SECONDS 5 /* flood watermark time until full */
176 #define FLUSH_SECONDS 7 /* flush watermark time until full */
177 #else
178 #define PANIC_SECONDS 8
179 #define FLUSH_SECONDS 10
180 #endif /* MEM */
182 /** encoder events **/
183 static void (*enc_events_callback)(enum enc_events event, void *data);
185 /** Path queue for files to write **/
186 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
187 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
188 static unsigned char *fn_queue; /* pointer to first filename */
189 static ssize_t fnq_size; /* capacity of queue in bytes */
190 static int fnq_rd_pos; /* current read position */
191 static int fnq_wr_pos; /* current write position */
192 #define FNQ_NEXT(pos) \
193 ({ int p = (pos) + MAX_PATH; \
194 if (p >= fnq_size) \
195 p = 0; \
196 p; })
197 #define FNQ_PREV(pos) \
198 ({ int p = (pos) - MAX_PATH; \
199 if (p < 0) \
200 p = fnq_size - MAX_PATH; \
201 p; })
203 enum
205 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
206 incoming messages - combine
207 with other constants */
208 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
209 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
210 chunks */
211 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
212 reached */
215 /***************************************************************************/
217 static struct event_queue pcmrec_queue SHAREDBSS_ATTR;
218 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR;
219 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
220 static const char pcmrec_thread_name[] = "pcmrec";
221 static struct thread_entry *pcmrec_thread_p;
223 static void pcmrec_thread(void);
225 enum
227 PCMREC_NULL = 0,
228 PCMREC_INIT, /* enable recording */
229 PCMREC_CLOSE, /* close recording */
230 PCMREC_OPTIONS, /* set recording options */
231 PCMREC_RECORD, /* record a new file */
232 PCMREC_STOP, /* stop the current recording */
233 PCMREC_PAUSE, /* pause the current recording */
234 PCMREC_RESUME, /* resume the current recording */
235 #if 0
236 PCMREC_FLUSH_NUM, /* flush a number of files out */
237 #endif
240 /*******************************************************************/
241 /* Functions that are not executing in the pcmrec_thread first */
242 /*******************************************************************/
244 /* Callback for when more data is ready - called in interrupt context */
245 static int pcm_rec_have_more(int status)
247 if (status < 0)
249 /* some error condition */
250 if (status == DMA_REC_ERROR_DMA)
252 /* Flush recorded data to disk and stop recording */
253 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
254 return -1;
256 /* else try again next transmission */
258 else if (!dma_lock)
260 /* advance write position */
261 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
263 /* set pcm ovf if processing start position is inside current
264 write chunk */
265 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
266 warnings |= PCMREC_W_PCM_BUFFER_OVF;
268 dma_wr_pos = next_pos;
271 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
272 return 0;
273 } /* pcm_rec_have_more */
275 static void reset_hardware(void)
277 /* reset pcm to defaults (playback only) */
278 pcm_set_frequency(HW_SAMPR_DEFAULT);
279 audio_set_output_source(AUDIO_SRC_PLAYBACK);
280 pcm_apply_settings();
283 /** pcm_rec_* group **/
286 * Clear all errors and warnings
288 void pcm_rec_error_clear(void)
290 errors = warnings = 0;
291 } /* pcm_rec_error_clear */
294 * Check mode, errors and warnings
296 unsigned long pcm_rec_status(void)
298 unsigned long ret = 0;
300 if (is_recording)
301 ret |= AUDIO_STATUS_RECORD;
302 else if (pre_record_ticks)
303 ret |= AUDIO_STATUS_PRERECORD;
305 if (is_paused)
306 ret |= AUDIO_STATUS_PAUSE;
308 if (errors)
309 ret |= AUDIO_STATUS_ERROR;
311 if (warnings)
312 ret |= AUDIO_STATUS_WARNING;
314 return ret;
315 } /* pcm_rec_status */
318 * Return warnings that have occured since recording started
320 unsigned long pcm_rec_get_warnings(void)
322 return warnings;
325 #if 0
326 int pcm_rec_current_bitrate(void)
328 if (accum_pcm_samples == 0)
329 return 0;
331 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
332 } /* pcm_rec_current_bitrate */
333 #endif
335 #if 0
336 int pcm_rec_encoder_afmt(void)
338 return enc_config.afmt;
339 } /* pcm_rec_encoder_afmt */
340 #endif
342 #if 0
343 int pcm_rec_rec_format(void)
345 return afmt_rec_format[enc_config.afmt];
346 } /* pcm_rec_rec_format */
347 #endif
349 #ifdef HAVE_SPDIF_IN
350 unsigned long pcm_rec_sample_rate(void)
352 /* Which is better ?? */
353 #if 0
354 return enc_sample_rate;
355 #endif
356 return sample_rate;
357 } /* audio_get_sample_rate */
358 #endif
361 * Creates pcmrec_thread
363 void pcm_rec_init(void)
365 queue_init(&pcmrec_queue, true);
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 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send,
371 pcmrec_thread_p);
372 } /* pcm_rec_init */
374 /** audio_* group **/
377 * Initializes recording - call before calling any other recording function
379 void audio_init_recording(unsigned int buffer_offset)
381 logf("audio_init_recording");
382 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
383 logf("audio_init_recording done");
384 (void)buffer_offset;
385 } /* audio_init_recording */
388 * Closes recording - call audio_stop_recording first
390 void audio_close_recording(void)
392 logf("audio_close_recording");
393 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
394 logf("audio_close_recording done");
395 } /* audio_close_recording */
398 * Sets recording parameters
400 void audio_set_recording_options(struct audio_recording_options *options)
402 logf("audio_set_recording_options");
403 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
404 logf("audio_set_recording_options done");
405 } /* audio_set_recording_options */
408 * Start recording if not recording or else split
410 void audio_record(const char *filename)
412 logf("audio_record: %s", filename);
413 flush_interrupt();
414 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
415 logf("audio_record_done");
416 } /* audio_record */
419 * audio_record wrapper for API compatibility with HW codec
421 void audio_new_file(const char *filename)
423 audio_record(filename);
424 } /* audio_new_file */
427 * Stop current recording if recording
429 void audio_stop_recording(void)
431 logf("audio_stop_recording");
432 flush_interrupt();
433 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
434 logf("audio_stop_recording done");
435 } /* audio_stop_recording */
438 * Pause current recording
440 void audio_pause_recording(void)
442 logf("audio_pause_recording");
443 flush_interrupt();
444 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
445 logf("audio_pause_recording done");
446 } /* audio_pause_recording */
449 * Resume current recording if paused
451 void audio_resume_recording(void)
453 logf("audio_resume_recording");
454 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
455 logf("audio_resume_recording done");
456 } /* audio_resume_recording */
459 * Note that microphone is mono, only left value is used
460 * See audiohw_set_recvol() for exact ranges.
462 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
465 void audio_set_recording_gain(int left, int right, int type)
467 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
468 audiohw_set_recvol(left, right, type);
469 } /* audio_set_recording_gain */
471 /** Information about current state **/
474 * Return current recorded time in ticks (playback eqivalent time)
476 unsigned long audio_recorded_time(void)
478 if (!is_recording || enc_sample_rate == 0)
479 return 0;
481 /* return actual recorded time a la encoded data even if encoder rate
482 doesn't match the pcm rate */
483 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
484 } /* audio_recorded_time */
487 * Return number of bytes encoded to output
489 unsigned long audio_num_recorded_bytes(void)
491 if (!is_recording)
492 return 0;
494 return num_rec_bytes;
495 } /* audio_num_recorded_bytes */
497 /***************************************************************************/
498 /* */
499 /* Functions that execute in the context of pcmrec_thread */
500 /* */
501 /***************************************************************************/
503 /** Filename Queue **/
505 /* returns true if the queue is empty */
506 static inline bool pcmrec_fnq_is_empty(void)
508 return fnq_rd_pos == fnq_wr_pos;
509 } /* pcmrec_fnq_is_empty */
511 /* empties the filename queue */
512 static inline void pcmrec_fnq_set_empty(void)
514 fnq_rd_pos = fnq_wr_pos;
515 } /* pcmrec_fnq_set_empty */
517 /* returns true if the queue is full */
518 static bool pcmrec_fnq_is_full(void)
520 ssize_t size = fnq_wr_pos - fnq_rd_pos;
521 if (size < 0)
522 size += fnq_size;
524 return size >= fnq_size - MAX_PATH;
525 } /* pcmrec_fnq_is_full */
527 /* queue another filename - will overwrite oldest one if full */
528 static bool pcmrec_fnq_add_filename(const char *filename)
530 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
531 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
533 if (fnq_rd_pos != fnq_wr_pos)
534 return true;
536 /* queue full */
537 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
538 return true;
539 } /* pcmrec_fnq_add_filename */
541 /* replace the last filename added */
542 static bool pcmrec_fnq_replace_tail(const char *filename)
544 int pos;
546 if (pcmrec_fnq_is_empty())
547 return false;
549 pos = FNQ_PREV(fnq_wr_pos);
551 strncpy(fn_queue + pos, filename, MAX_PATH);
553 return true;
554 } /* pcmrec_fnq_replace_tail */
556 /* pulls the next filename from the queue */
557 static bool pcmrec_fnq_get_filename(char *filename)
559 if (pcmrec_fnq_is_empty())
560 return false;
562 if (filename)
563 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
565 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
566 return true;
567 } /* pcmrec_fnq_get_filename */
569 /* close the file number pointed to by fd_p */
570 static void pcmrec_close_file(int *fd_p)
572 if (*fd_p < 0)
573 return; /* preserve error */
575 if (close(*fd_p) != 0)
576 errors |= PCMREC_E_IO;
578 *fd_p = -1;
579 } /* pcmrec_close_file */
581 /** Data Flushing **/
584 * called after callback to update sizes if codec changed the amount of data
585 * a chunk represents
587 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
588 unsigned long prev_num_pcm)
590 if (rec_fdata.new_enc_size != prev_enc_size)
592 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
593 num_rec_bytes += size_diff;
594 #if 0
595 accum_rec_bytes += size_diff;
596 #endif
599 if (rec_fdata.new_num_pcm != prev_num_pcm)
601 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
602 num_rec_samples += pcm_diff;
603 #if 0
604 accum_pcm_samples += pcm_diff;
605 #endif
607 } /* pcmrec_update_sizes_inl */
609 /* don't need to inline every instance */
610 static void pcmrec_update_sizes(size_t prev_enc_size,
611 unsigned long prev_num_pcm)
613 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
614 } /* pcmrec_update_sizes */
616 static void pcmrec_start_file(void)
618 size_t enc_size = rec_fdata.new_enc_size;
619 unsigned long num_pcm = rec_fdata.new_num_pcm;
620 int curr_rec_file = rec_fdata.rec_file;
621 char filename[MAX_PATH];
623 /* must always pull the filename that matches with this queue */
624 if (!pcmrec_fnq_get_filename(filename))
626 logf("start file: fnq empty");
627 *filename = '\0';
628 errors |= PCMREC_E_FNQ_DESYNC;
630 else if (errors != 0)
632 logf("start file: error already");
634 else if (curr_rec_file >= 0)
636 /* Any previous file should have been closed */
637 logf("start file: file already open");
638 errors |= PCMREC_E_FNQ_DESYNC;
641 if (errors != 0)
642 rec_fdata.chunk->flags |= CHUNKF_ERROR;
644 /* encoder can set error flag here and should increase
645 enc_new_size and pcm_new_size to reflect additional
646 data written if any */
647 rec_fdata.filename = filename;
648 enc_events_callback(ENC_START_FILE, &rec_fdata);
650 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
652 logf("start file: enc error");
653 errors |= PCMREC_E_ENCODER;
656 if (errors != 0)
658 pcmrec_close_file(&curr_rec_file);
659 /* Write no more to this file */
660 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
662 else
664 pcmrec_update_sizes(enc_size, num_pcm);
667 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
668 } /* pcmrec_start_file */
670 static inline void pcmrec_write_chunk(void)
672 size_t enc_size = rec_fdata.new_enc_size;
673 unsigned long num_pcm = rec_fdata.new_num_pcm;
675 if (errors != 0)
676 rec_fdata.chunk->flags |= CHUNKF_ERROR;
678 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
680 if ((long)rec_fdata.chunk->flags >= 0)
682 pcmrec_update_sizes_inl(enc_size, num_pcm);
684 else if (errors == 0)
686 logf("wr chk enc error %lu %lu",
687 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
688 errors |= PCMREC_E_ENCODER;
690 } /* pcmrec_write_chunk */
692 static void pcmrec_end_file(void)
694 /* all data in output buffer for current file will have been
695 written and encoder can now do any nescessary steps to
696 finalize the written file */
697 size_t enc_size = rec_fdata.new_enc_size;
698 unsigned long num_pcm = rec_fdata.new_num_pcm;
700 enc_events_callback(ENC_END_FILE, &rec_fdata);
702 if (errors == 0)
704 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
706 logf("end file: enc error");
707 errors |= PCMREC_E_ENCODER;
709 else
711 pcmrec_update_sizes(enc_size, num_pcm);
715 /* Force file close if error */
716 if (errors != 0)
717 pcmrec_close_file(&rec_fdata.rec_file);
719 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
720 } /* pcmrec_end_file */
723 * Update buffer watermarks with spinup time compensation
725 * All this assumes reasonable data rates, chunk sizes and sufficient
726 * memory for the most part. Some dumb checks are included but perhaps
727 * are pointless since this all will break down at extreme limits that
728 * are currently not applicable to any supported device.
730 static void pcmrec_refresh_watermarks(void)
732 logf("ata spinup: %d", ata_spinup_time);
734 /* set the low mark for when flushing stops if automatic */
735 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
736 / enc_chunk_size;
737 logf("low wmk: %d", low_watermark);
739 #ifdef HAVE_PRIORITY_SCHEDULING
740 /* panic boost thread priority if 2 seconds of ground is lost -
741 this allows encoder to boost with just under a second of
742 pcm data (if not yet full enough to boost itself)
743 and not falsely trip the alarm. */
744 flood_watermark = enc_num_chunks -
745 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
746 / enc_chunk_size;
748 if (flood_watermark < low_watermark)
750 logf("warning: panic < low");
751 flood_watermark = low_watermark;
754 logf("flood at: %d", flood_watermark);
755 #endif
756 spinup_time = last_ata_spinup_time = ata_spinup_time;
758 /* write at 8s + st remaining in enc_buffer - range 12s to
759 20s total - default to 3.5s spinup. */
760 if (spinup_time == 0)
761 spinup_time = 35*HZ/10; /* default - cozy */
762 else if (spinup_time < 2*HZ)
763 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
764 else if (spinup_time > 10*HZ)
765 spinup_time = 10*HZ; /* do you have a functioning HD? */
767 /* try to start writing with 10s remaining after disk spinup */
768 high_watermark = enc_num_chunks -
769 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
770 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
772 if (high_watermark < low_watermark)
774 high_watermark = low_watermark;
775 low_watermark /= 2;
776 logf("warning: low 'write at'");
779 logf("write at: %d", high_watermark);
780 } /* pcmrec_refresh_watermarks */
783 * Process the chunks
785 * This function is called when queue_get_w_tmo times out.
787 * Set flush_num to the number of files to flush to disk or to
788 * a PCMREC_FLUSH_* constant.
790 static void pcmrec_flush(unsigned flush_num)
792 #ifdef HAVE_PRIORITY_SCHEDULING
793 static unsigned long last_flush_tick; /* tick when function returned */
794 unsigned long start_tick; /* When flush started */
795 unsigned long prio_tick; /* Timeout for auto boost */
796 int prio_pcmrec; /* Current thread priority for pcmrec */
797 int prio_codec; /* Current thread priority for codec */
798 #endif
799 int num_ready; /* Number of chunks ready at start */
800 unsigned remaining; /* Number of file starts remaining */
801 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
802 bool interruptable; /* Flush can be interupted */
804 num_ready = enc_wr_index - enc_rd_index;
805 if (num_ready < 0)
806 num_ready += enc_num_chunks;
808 /* save interruptable flag and remove it to get the actual count */
809 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
810 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
812 if (flush_num == 0)
814 if (!is_recording)
815 return;
817 if (ata_spinup_time != last_ata_spinup_time)
818 pcmrec_refresh_watermarks();
820 /* enough available? no? then leave */
821 if (num_ready < high_watermark)
822 return;
823 } /* endif (flush_num == 0) */
825 #ifdef HAVE_PRIORITY_SCHEDULING
826 start_tick = current_tick;
827 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
829 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
831 /* if we're getting called too much and this isn't forced,
832 boost stat by expiring timeout in advance */
833 logf("too frequent flush");
834 prio_tick = current_tick - 1;
837 prio_pcmrec = -1;
838 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
839 need init */
840 #endif
842 logf("writing:%d(%d):%s%s", num_ready, flush_num,
843 interruptable ? "i" : "",
844 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
846 cpu_boost(true);
848 remaining = flush_num;
849 chunks_flushed = 0;
851 while (num_ready > 0)
853 /* check current number of encoder chunks */
854 int num = enc_wr_index - enc_rd_index;
855 if (num < 0)
856 num += enc_num_chunks;
858 if (num <= low_watermark &&
859 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
861 logf("low data: %d", num);
862 break; /* data remaining is below threshold */
865 if (interruptable && flush_interrupts > 0)
867 logf("int at: %d", num);
868 break; /* interrupted */
871 #ifdef HAVE_PRIORITY_SCHEDULING
872 if (prio_pcmrec == -1 && (num >= flood_watermark ||
873 TIME_AFTER(current_tick, prio_tick)))
875 /* losing ground or holding without progress - boost
876 priority until finished */
877 logf("pcmrec: boost (%s)",
878 num >= flood_watermark ? "num" : "time");
879 prio_pcmrec = thread_set_priority(NULL,
880 thread_get_priority(NULL) - 4);
881 prio_codec = thread_set_priority(codec_thread_p,
882 thread_get_priority(codec_thread_p) - 4);
884 #endif
886 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
887 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
888 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
890 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
892 pcmrec_start_file();
893 if (--remaining == 0)
894 num_ready = 0; /* stop on next loop - must write this
895 chunk if it has data */
898 pcmrec_write_chunk();
900 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
901 pcmrec_end_file();
903 INC_ENC_INDEX(enc_rd_index);
905 if (errors != 0)
907 pcmrec_end_file();
908 break;
911 if (flush_num == PCMREC_FLUSH_MINI &&
912 ++chunks_flushed >= MINI_CHUNKS)
914 logf("mini flush break");
915 break;
917 /* no yielding; the file apis called in the codecs do that
918 sufficiently */
919 } /* end while */
921 /* sync file */
922 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
923 errors |= PCMREC_E_IO;
925 cpu_boost(false);
927 #ifdef HAVE_PRIORITY_SCHEDULING
928 if (prio_pcmrec != -1)
930 /* return to original priorities */
931 logf("pcmrec: unboost priority");
932 thread_set_priority(NULL, prio_pcmrec);
933 thread_set_priority(codec_thread_p, prio_codec);
936 last_flush_tick = current_tick; /* save tick when we left */
937 #endif
939 logf("done");
940 } /* pcmrec_flush */
943 * Marks a new stream in the buffer and gives the encoder a chance for special
944 * handling of transition from one to the next. The encoder may change the
945 * chunk that ends the old stream by requesting more chunks and similiarly for
946 * the new but must always advance the position though the interface. It can
947 * later reject any data it cares to when writing the file but should mark the
948 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
949 * a NULL data pointer without error as well.
951 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
953 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
954 } /* pcmrec_get_chunk_index */
956 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
958 DEC_ENC_INDEX(index);
959 return GET_ENC_CHUNK(index);
960 } /* pcmrec_get_prev_chunk */
962 static void pcmrec_new_stream(const char *filename, /* next file name */
963 unsigned long flags, /* CHUNKF_* flags */
964 int pre_index) /* index for prerecorded data */
966 logf("pcmrec_new_stream");
967 char path[MAX_PATH]; /* place to copy filename so sender can be released */
969 struct enc_buffer_event_data data;
970 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
971 new filename */
972 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
973 stream */
974 bool did_flush = false; /* did a flush occurr? */
976 if (filename)
977 strncpy(path, filename, MAX_PATH);
978 queue_reply(&pcmrec_queue, 0); /* We have all we need */
980 data.pre_chunk = NULL;
981 data.chunk = GET_ENC_CHUNK(enc_wr_index);
983 /* end chunk */
984 if (flags & CHUNKF_END_FILE)
986 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
988 if (data.chunk->flags & CHUNKF_START_FILE)
990 /* cannot start and end on same unprocessed chunk */
991 logf("file end on start");
992 flags &= ~CHUNKF_END_FILE;
994 else if (enc_rd_index == enc_wr_index)
996 /* all data flushed but file not ended - chunk will be left
997 empty */
998 logf("end on dead end");
999 data.chunk->flags = 0;
1000 data.chunk->enc_size = 0;
1001 data.chunk->num_pcm = 0;
1002 data.chunk->enc_data = NULL;
1003 INC_ENC_INDEX(enc_wr_index);
1004 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1006 else
1008 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1010 if (last->flags & CHUNKF_END_FILE)
1012 /* end already processed and marked - can't end twice */
1013 logf("file end again");
1014 flags &= ~CHUNKF_END_FILE;
1019 /* start chunk */
1020 if (flags & CHUNKF_START_FILE)
1022 bool pre = flags & CHUNKF_PRERECORD;
1024 if (pre)
1026 logf("stream prerecord start");
1027 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1028 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1030 else
1032 logf("stream normal start");
1033 start = data.chunk;
1034 start->flags &= CHUNKF_START_FILE;
1037 /* if encoder hasn't yet processed the last start - abort the start
1038 of the previous file queued or else it will be empty and invalid */
1039 if (start->flags & CHUNKF_START_FILE)
1041 logf("replacing fnq tail: %s", filename);
1042 fnq_add_fn = pcmrec_fnq_replace_tail;
1044 else
1046 logf("adding filename: %s", filename);
1047 fnq_add_fn = pcmrec_fnq_add_filename;
1051 data.flags = flags;
1052 pcmrec_context = true; /* switch encoder context */
1053 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1054 pcmrec_context = false; /* switch back */
1056 if (flags & CHUNKF_END_FILE)
1058 int i = pcmrec_get_chunk_index(data.chunk);
1059 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1062 if (start)
1064 if (!(flags & CHUNKF_PRERECORD))
1066 /* get stats on data added to start - sort of a prerecord
1067 operation */
1068 int i = pcmrec_get_chunk_index(data.chunk);
1069 struct enc_chunk_hdr *chunk = data.chunk;
1071 logf("start data: %d %d", i, enc_wr_index);
1073 num_rec_bytes = 0;
1074 num_rec_samples = 0;
1076 while (i != enc_wr_index)
1078 num_rec_bytes += chunk->enc_size;
1079 num_rec_samples += chunk->num_pcm;
1080 INC_ENC_INDEX(i);
1081 chunk = GET_ENC_CHUNK(i);
1084 start->flags &= ~CHUNKF_START_FILE;
1085 start = data.chunk;
1088 start->flags |= CHUNKF_START_FILE;
1090 /* flush all pending files out if full and adding */
1091 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1093 logf("fnq full");
1094 pcmrec_flush(PCMREC_FLUSH_ALL);
1095 did_flush = true;
1098 fnq_add_fn(path);
1101 /* Make sure to complete any interrupted high watermark */
1102 if (!did_flush)
1103 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1104 } /* pcmrec_new_stream */
1106 /** event handlers for pcmrec thread */
1108 /* PCMREC_INIT */
1109 static void pcmrec_init(void)
1111 unsigned char *buffer;
1113 /* warings and errors */
1114 warnings =
1115 errors = 0;
1117 pcmrec_close_file(&rec_fdata.rec_file);
1118 rec_fdata.rec_file = -1;
1120 /* pcm FIFO */
1121 dma_lock = true;
1122 pcm_rd_pos = 0;
1123 dma_wr_pos = 0;
1124 pcm_enc_pos = 0;
1126 /* encoder FIFO */
1127 enc_wr_index = 0;
1128 enc_rd_index = 0;
1130 /* filename queue */
1131 fnq_rd_pos = 0;
1132 fnq_wr_pos = 0;
1134 /* stats */
1135 num_rec_bytes = 0;
1136 num_rec_samples = 0;
1137 #if 0
1138 accum_rec_bytes = 0;
1139 accum_pcm_samples = 0;
1140 #endif
1142 pre_record_ticks = 0;
1144 is_recording = false;
1145 is_paused = false;
1147 buffer = audio_get_recording_buffer(&rec_buffer_size);
1149 /* Line align pcm_buffer 2^4=16 bytes */
1150 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1151 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1152 PCM_MAX_FEED_SIZE, 2);
1153 /* Adjust available buffer for possible align advancement */
1154 rec_buffer_size -= pcm_buffer - buffer;
1156 pcm_init_recording();
1157 } /* pcmrec_init */
1159 /* PCMREC_CLOSE */
1160 static void pcmrec_close(void)
1162 dma_lock = true;
1163 pre_record_ticks = 0; /* Can't be prerecording any more */
1164 warnings = 0;
1165 pcm_close_recording();
1166 reset_hardware();
1167 audio_remove_encoder();
1168 } /* pcmrec_close */
1170 /* PCMREC_OPTIONS */
1171 static void pcmrec_set_recording_options(
1172 struct audio_recording_options *options)
1174 /* stop DMA transfer */
1175 dma_lock = true;
1176 pcm_stop_recording();
1178 rec_frequency = options->rec_frequency;
1179 rec_source = options->rec_source;
1180 num_channels = options->rec_channels == 1 ? 1 : 2;
1181 pre_record_ticks = options->rec_prerecord_time * HZ;
1182 enc_config = options->enc_config;
1183 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1185 #ifdef HAVE_SPDIF_IN
1186 if (rec_source == AUDIO_SRC_SPDIF)
1188 /* must measure SPDIF sample rate before configuring codecs */
1189 unsigned long sr = spdif_measure_frequency();
1190 /* round to master list for SPDIF rate */
1191 int index = round_value_to_list32(sr, audio_master_sampr_list,
1192 SAMPR_NUM_FREQ, false);
1193 sample_rate = audio_master_sampr_list[index];
1194 /* round to HW playback rates for monitoring */
1195 index = round_value_to_list32(sr, hw_freq_sampr,
1196 HW_NUM_FREQ, false);
1197 pcm_set_frequency(hw_freq_sampr[index]);
1198 /* encoders with a limited number of rates do their own rounding */
1200 else
1201 #endif
1203 /* set sample rate from frequency selection */
1204 sample_rate = rec_freq_sampr[rec_frequency];
1205 pcm_set_frequency(sample_rate);
1208 /* set monitoring */
1209 audio_set_output_source(rec_source);
1211 /* apply hardware setting to start monitoring now */
1212 pcm_apply_settings();
1214 queue_reply(&pcmrec_queue, 0); /* Release sender */
1216 if (audio_load_encoder(enc_config.afmt))
1218 /* start DMA transfer */
1219 dma_lock = pre_record_ticks == 0;
1220 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1221 PCM_CHUNK_SIZE);
1223 else
1225 logf("set rec opt: enc load failed");
1226 errors |= PCMREC_E_LOAD_ENCODER;
1228 } /* pcmrec_set_recording_options */
1230 /* PCMREC_RECORD - start recording (not gapless)
1231 or split stream (gapless) */
1232 static void pcmrec_record(const char *filename)
1234 unsigned long pre_sample_ticks;
1235 int rd_start;
1236 unsigned long flags;
1237 int pre_index;
1239 logf("pcmrec_record: %s", filename);
1241 /* reset stats */
1242 num_rec_bytes = 0;
1243 num_rec_samples = 0;
1245 if (!is_recording)
1247 #if 0
1248 accum_rec_bytes = 0;
1249 accum_pcm_samples = 0;
1250 #endif
1251 warnings = 0; /* reset warnings */
1253 rd_start = enc_wr_index;
1254 pre_sample_ticks = 0;
1256 pcmrec_refresh_watermarks();
1258 if (pre_record_ticks)
1260 int i = rd_start;
1261 /* calculate number of available chunks */
1262 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1263 enc_num_chunks) % enc_num_chunks;
1264 /* overflow at 974 seconds of prerecording at 44.1kHz */
1265 unsigned long pre_record_sample_ticks =
1266 enc_sample_rate*pre_record_ticks;
1267 int pre_chunks = 0; /* Counter to limit prerecorded time to
1268 prevent flood state at outset */
1270 logf("pre-st: %ld", pre_record_sample_ticks);
1272 /* Get exact measure of recorded data as number of samples aren't
1273 nescessarily going to be the max for each chunk */
1274 for (; avail_pre_chunks-- > 0;)
1276 struct enc_chunk_hdr *chunk;
1277 unsigned long chunk_sample_ticks;
1279 DEC_ENC_INDEX(i);
1281 chunk = GET_ENC_CHUNK(i);
1283 /* must have data to be counted */
1284 if (chunk->enc_data == NULL)
1285 continue;
1287 chunk_sample_ticks = chunk->num_pcm*HZ;
1289 rd_start = i;
1290 pre_sample_ticks += chunk_sample_ticks;
1291 num_rec_bytes += chunk->enc_size;
1292 num_rec_samples += chunk->num_pcm;
1293 pre_chunks++;
1295 /* stop here if enough already */
1296 if (pre_chunks >= high_watermark ||
1297 pre_sample_ticks >= pre_record_sample_ticks)
1299 logf("pre-chks: %d", pre_chunks);
1300 break;
1304 #if 0
1305 accum_rec_bytes = num_rec_bytes;
1306 accum_pcm_samples = num_rec_samples;
1307 #endif
1310 enc_rd_index = rd_start;
1312 /* filename queue should be empty */
1313 if (!pcmrec_fnq_is_empty())
1315 logf("fnq: not empty!");
1316 pcmrec_fnq_set_empty();
1319 flags = CHUNKF_START_FILE;
1320 if (pre_sample_ticks > 0)
1321 flags |= CHUNKF_PRERECORD;
1323 pre_index = enc_rd_index;
1325 dma_lock = false;
1326 is_paused = false;
1327 is_recording = true;
1329 else
1331 /* already recording, just split the stream */
1332 logf("inserting split");
1333 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1334 pre_index = 0;
1337 pcmrec_new_stream(filename, flags, pre_index);
1338 logf("pcmrec_record done");
1339 } /* pcmrec_record */
1341 /* PCMREC_STOP */
1342 static void pcmrec_stop(void)
1344 logf("pcmrec_stop");
1346 if (is_recording)
1348 dma_lock = true; /* lock dma write position */
1350 /* flush all available data first to avoid overflow while waiting
1351 for encoding to finish */
1352 pcmrec_flush(PCMREC_FLUSH_ALL);
1354 /* wait for encoder to finish remaining data */
1355 while (errors == 0 && !pcm_buffer_empty)
1356 yield();
1358 /* end stream at last data */
1359 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1361 /* flush anything else encoder added */
1362 pcmrec_flush(PCMREC_FLUSH_ALL);
1364 /* remove any pending file start not yet processed - should be at
1365 most one at enc_wr_index */
1366 pcmrec_fnq_get_filename(NULL);
1367 /* encoder should abort any chunk it was in midst of processing */
1368 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1370 /* filename queue should be empty */
1371 if (!pcmrec_fnq_is_empty())
1373 logf("fnq: not empty!");
1374 pcmrec_fnq_set_empty();
1377 /* be absolutely sure the file is closed */
1378 if (errors != 0)
1379 pcmrec_close_file(&rec_fdata.rec_file);
1380 rec_fdata.rec_file = -1;
1382 is_recording = false;
1383 is_paused = false;
1384 dma_lock = pre_record_ticks == 0;
1386 else
1388 logf("not recording");
1391 logf("pcmrec_stop done");
1392 } /* pcmrec_stop */
1394 /* PCMREC_PAUSE */
1395 static void pcmrec_pause(void)
1397 logf("pcmrec_pause");
1399 if (!is_recording)
1401 logf("not recording");
1403 else if (is_paused)
1405 logf("already paused");
1407 else
1409 dma_lock = true;
1410 is_paused = true;
1413 logf("pcmrec_pause done");
1414 } /* pcmrec_pause */
1416 /* PCMREC_RESUME */
1417 static void pcmrec_resume(void)
1419 logf("pcmrec_resume");
1421 if (!is_recording)
1423 logf("not recording");
1425 else if (!is_paused)
1427 logf("not paused");
1429 else
1431 is_paused = false;
1432 is_recording = true;
1433 dma_lock = false;
1436 logf("pcmrec_resume done");
1437 } /* pcmrec_resume */
1439 static void pcmrec_thread(void) __attribute__((noreturn));
1440 static void pcmrec_thread(void)
1442 struct queue_event ev;
1444 logf("thread pcmrec start");
1446 while(1)
1448 if (is_recording)
1450 /* Poll periodically to flush data */
1451 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1453 if (ev.id == SYS_TIMEOUT)
1455 /* Messages that interrupt this will complete it */
1456 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1457 PCMREC_FLUSH_INTERRUPTABLE);
1458 continue;
1461 else
1463 /* Not doing anything - sit and wait for commands */
1464 queue_wait(&pcmrec_queue, &ev);
1467 switch (ev.id)
1469 case PCMREC_INIT:
1470 pcmrec_init();
1471 break;
1473 case PCMREC_CLOSE:
1474 pcmrec_close();
1475 break;
1477 case PCMREC_OPTIONS:
1478 pcmrec_set_recording_options(
1479 (struct audio_recording_options *)ev.data);
1480 break;
1482 case PCMREC_RECORD:
1483 clear_flush_interrupt();
1484 pcmrec_record((const char *)ev.data);
1485 break;
1487 case PCMREC_STOP:
1488 clear_flush_interrupt();
1489 pcmrec_stop();
1490 break;
1492 case PCMREC_PAUSE:
1493 clear_flush_interrupt();
1494 pcmrec_pause();
1495 break;
1497 case PCMREC_RESUME:
1498 pcmrec_resume();
1499 break;
1500 #if 0
1501 case PCMREC_FLUSH_NUM:
1502 pcmrec_flush((unsigned)ev.data);
1503 break;
1504 #endif
1505 case SYS_USB_CONNECTED:
1506 if (is_recording)
1507 break;
1508 pcmrec_close();
1509 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1510 usb_wait_for_disconnect(&pcmrec_queue);
1511 flush_interrupts = 0;
1512 break;
1513 } /* end switch */
1514 } /* end while */
1515 } /* pcmrec_thread */
1517 /****************************************************************************/
1518 /* */
1519 /* following functions will be called by the encoder codec */
1520 /* in a free-threaded manner */
1521 /* */
1522 /****************************************************************************/
1524 /* pass the encoder settings to the encoder */
1525 void enc_get_inputs(struct enc_inputs *inputs)
1527 inputs->sample_rate = sample_rate;
1528 inputs->num_channels = num_channels;
1529 inputs->config = &enc_config;
1530 } /* enc_get_inputs */
1532 /* set the encoder dimensions (called by encoder codec at initialization and
1533 termination) */
1534 void enc_set_parameters(struct enc_parameters *params)
1536 size_t bufsize, resbytes;
1538 logf("enc_set_parameters");
1540 if (!params)
1542 logf("reset");
1543 /* Encoder is terminating */
1544 memset(&enc_config, 0, sizeof (enc_config));
1545 enc_sample_rate = 0;
1546 cancel_cpu_boost(); /* Make sure no boost remains */
1547 return;
1550 enc_sample_rate = params->enc_sample_rate;
1551 logf("enc sampr:%lu", enc_sample_rate);
1553 pcm_rd_pos = dma_wr_pos;
1554 pcm_enc_pos = pcm_rd_pos;
1556 enc_config.afmt = params->afmt;
1557 /* addition of the header is always implied - chunk size 4-byte aligned */
1558 enc_chunk_size =
1559 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1560 enc_events_callback = params->events_callback;
1562 logf("chunk size:%lu", enc_chunk_size);
1564 /*** Configure the buffers ***/
1566 /* Layout of recording buffer:
1567 * [ax] = possible alignment x multiple
1568 * [sx] = possible size alignment of x multiple
1569 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1570 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1572 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1573 logf("resbytes:%lu", resbytes);
1575 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1576 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1577 #ifdef DEBUG
1578 - sizeof (*wrap_id_p)
1579 #endif
1582 enc_num_chunks = bufsize / enc_chunk_size;
1583 logf("num chunks:%d", enc_num_chunks);
1585 /* get real amount used by encoder chunks */
1586 bufsize = enc_num_chunks*enc_chunk_size;
1587 logf("enc size:%lu", bufsize);
1589 #ifdef DEBUG
1590 /* add magic at wraparound for spillover checks */
1591 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1592 bufsize += sizeof (*wrap_id_p);
1593 *wrap_id_p = ENC_CHUNK_MAGIC;
1594 #endif
1596 /** set OUT parameters **/
1597 params->enc_buffer = enc_buffer;
1598 params->buf_chunk_size = enc_chunk_size;
1599 params->num_chunks = enc_num_chunks;
1601 /* calculate reserve buffer start and return pointer to encoder */
1602 params->reserve_buffer = NULL;
1603 if (resbytes > 0)
1605 params->reserve_buffer = enc_buffer + bufsize;
1606 bufsize += resbytes;
1609 /* place filename queue at end of buffer using up whatever remains */
1610 fnq_rd_pos = 0; /* reset */
1611 fnq_wr_pos = 0; /* reset */
1612 fn_queue = enc_buffer + bufsize;
1613 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1614 fnq_size /= MAX_PATH;
1615 if (fnq_size > FNQ_MAX_NUM_PATHS)
1616 fnq_size = FNQ_MAX_NUM_PATHS;
1617 fnq_size *= MAX_PATH;
1618 logf("fnq files:%ld", fnq_size / MAX_PATH);
1620 #if defined(DEBUG)
1621 logf("ab :%08lX", (uintptr_t)audiobuf);
1622 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1623 logf("enc:%08lX", (uintptr_t)enc_buffer);
1624 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1625 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1626 logf("fnq:%08lX", (uintptr_t)fn_queue);
1627 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1628 logf("abe:%08lX", (uintptr_t)audiobufend);
1629 #endif
1631 /* init all chunk headers and reset indexes */
1632 enc_rd_index = 0;
1633 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1635 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1636 #ifdef DEBUG
1637 chunk->id = ENC_CHUNK_MAGIC;
1638 #endif
1639 chunk->flags = 0;
1642 logf("enc_set_parameters done");
1643 } /* enc_set_parameters */
1645 /* return encoder chunk at current write position -
1646 NOTE: can be called by pcmrec thread when splitting streams */
1647 struct enc_chunk_hdr * enc_get_chunk(void)
1649 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1651 #ifdef DEBUG
1652 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1654 errors |= PCMREC_E_CHUNK_OVF;
1655 logf("finish chk ovf: %d", enc_wr_index);
1657 #endif
1659 chunk->flags &= CHUNKF_START_FILE;
1661 if (!is_recording)
1662 chunk->flags |= CHUNKF_PRERECORD;
1664 return chunk;
1665 } /* enc_get_chunk */
1667 /* releases the current chunk into the available chunks -
1668 NOTE: can be called by pcmrec thread when splitting streams */
1669 void enc_finish_chunk(void)
1671 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1673 if ((long)chunk->flags < 0)
1675 /* encoder set error flag */
1676 errors |= PCMREC_E_ENCODER;
1677 logf("finish chk enc error");
1680 /* advance enc_wr_index to the next encoder chunk */
1681 INC_ENC_INDEX(enc_wr_index);
1683 if (enc_rd_index != enc_wr_index)
1685 num_rec_bytes += chunk->enc_size;
1686 num_rec_samples += chunk->num_pcm;
1687 #if 0
1688 accum_rec_bytes += chunk->enc_size;
1689 accum_pcm_samples += chunk->num_pcm;
1690 #endif
1692 else if (is_recording) /* buffer full */
1694 /* keep current position and put up warning flag */
1695 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1696 logf("enc_buffer ovf");
1697 DEC_ENC_INDEX(enc_wr_index);
1698 if (pcmrec_context)
1700 /* if stream splitting, keep this out of circulation and
1701 flush a small number, then readd - cannot risk losing
1702 stream markers */
1703 logf("mini flush");
1704 pcmrec_flush(PCMREC_FLUSH_MINI);
1705 INC_ENC_INDEX(enc_wr_index);
1708 else
1710 /* advance enc_rd_index for prerecording */
1711 INC_ENC_INDEX(enc_rd_index);
1713 } /* enc_finish_chunk */
1715 /* passes a pointer to next chunk of unprocessed wav data */
1716 /* TODO: this really should give the actual size returned */
1717 unsigned char * enc_get_pcm_data(size_t size)
1719 int wp = dma_wr_pos;
1720 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1722 /* limit the requested pcm data size */
1723 if (size > PCM_MAX_FEED_SIZE)
1724 size = PCM_MAX_FEED_SIZE;
1726 if (avail >= size)
1728 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1729 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1731 pcm_enc_pos = pcm_rd_pos;
1732 pcm_rd_pos = next_pos;
1734 /* ptr must point to continous data at wraparound position */
1735 if ((size_t)pcm_rd_pos < size)
1737 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1738 pcm_buffer, pcm_rd_pos);
1741 if (avail >= (sample_rate << 2))
1743 /* Filling up - boost codec */
1744 trigger_cpu_boost();
1747 pcm_buffer_empty = false;
1748 return ptr;
1751 /* not enough data available - encoder should idle */
1752 pcm_buffer_empty = true;
1754 cancel_cpu_boost();
1756 /* Sleep long enough to allow one frame on average */
1757 sleep(0);
1759 return NULL;
1760 } /* enc_get_pcm_data */
1762 /* puts some pcm data back in the queue */
1763 size_t enc_unget_pcm_data(size_t size)
1765 int wp = dma_wr_pos;
1766 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1767 2*PCM_CHUNK_SIZE;
1769 /* allow one interrupt to occur during this call and not have the
1770 new read position inside the DMA destination chunk */
1771 if ((ssize_t)old_avail > 0)
1773 /* limit size to amount of old data remaining */
1774 if (size > old_avail)
1775 size = old_avail;
1777 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1778 pcm_rd_pos = pcm_enc_pos;
1780 return size;
1783 return 0;
1784 } /* enc_unget_pcm_data */