Fix typo.
[Rockbox.git] / firmware / pcm_record.c
bloba5d0e51c3097dbf8a86a70b38a0fcebf41a59660
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 /**
38 * APIs implemented in the target tree portion:
39 * Public -
40 * pcm_init_recording
41 * pcm_close_recording
42 * Semi-private -
43 * pcm_rec_dma_start
44 * pcm_rec_dma_stop
47 /** These items may be implemented target specifically or need to
48 be shared semi-privately **/
49 extern struct thread_entry *codec_thread_p;
51 /* the registered callback function for when more data is available */
52 volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
53 /* DMA transfer in is currently active */
54 volatile bool pcm_recording = false;
56 /** General recording state **/
57 static bool is_recording; /* We are recording */
58 static bool is_paused; /* We have paused */
59 static unsigned long errors; /* An error has occured */
60 static unsigned long warnings; /* Warning */
61 static int flush_interrupts = 0; /* Number of messages queued that
62 should interrupt a flush in
63 progress -
64 for a safety net and a prompt
65 response to stop, split and pause
66 requests -
67 only interrupts a flush initiated
68 by pcmrec_flush(0) */
70 /* Utility functions for setting/clearing flushing interrupt flag */
71 static inline void flush_interrupt(void)
73 flush_interrupts++;
74 logf("flush int: %d", flush_interrupts);
77 static inline void clear_flush_interrupt(void)
79 if (--flush_interrupts < 0)
80 flush_interrupts = 0;
83 /** Stats on encoded data for current file **/
84 static size_t num_rec_bytes; /* Num bytes recorded */
85 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
87 /** Stats on encoded data for all files from start to stop **/
88 #if 0
89 static unsigned long long accum_rec_bytes; /* total size written to chunks */
90 static unsigned long long accum_pcm_samples; /* total pcm count processed */
91 #endif
93 /* Keeps data about current file and is sent as event data for codec */
94 static struct enc_file_event_data rec_fdata IDATA_ATTR =
96 .chunk = NULL,
97 .new_enc_size = 0,
98 .new_num_pcm = 0,
99 .rec_file = -1,
100 .num_pcm_samples = 0
103 /** These apply to current settings **/
104 static int rec_source; /* current rec_source setting */
105 static int rec_frequency; /* current frequency setting */
106 static unsigned long sample_rate; /* Sample rate in HZ */
107 static int num_channels; /* Current number of channels */
108 static struct encoder_config enc_config; /* Current encoder configuration */
109 static unsigned long pre_record_ticks; /* pre-record time in ticks */
111 /****************************************************************************
112 use 2 circular buffers:
113 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
114 enc_buffer=encoded audio buffer: storage for encoder output data
116 Flow:
117 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
118 2. if enough pcm data are available the encoder codec does encoding of pcm
119 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
120 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
122 Functions calls (basic encoder steps):
123 1.main: audio_load_encoder(); start the encoder
124 2.encoder: enc_get_inputs(); get encoder recording settings
125 3.encoder: enc_set_parameters(); set the encoder parameters
126 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
127 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
128 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
129 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
130 8.encoder: <process enc chunk> compress and store data to enc chunk
131 9.encoder: enc_finish_chunk(); inform main about chunk processed and
132 is available to be written to a file.
133 Encoder can place any number of chunks
134 of PCM data in a single output chunk
135 but must stay within its output chunk
136 size
137 A.encoder: repeat 4. to 9.
138 B.pcmrec: enc_events_callback(); called for certain events
140 (*) Optional step
141 ****************************************************************************/
143 /** buffer parameters where incoming PCM data is placed **/
144 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
145 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
146 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
148 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
149 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
150 #define INC_ENC_INDEX(index) \
151 { if (++index >= enc_num_chunks) index = 0; }
152 #define DEC_ENC_INDEX(index) \
153 { if (--index < 0) index = enc_num_chunks - 1; }
155 static size_t rec_buffer_size; /* size of available buffer */
156 static unsigned char *pcm_buffer; /* circular recording buffer */
157 static unsigned char *enc_buffer; /* circular encoding buffer */
158 #ifdef DEBUG
159 static unsigned long *wrap_id_p; /* magic at wrap position - a debugging
160 aid to check if the encoder data
161 spilled out of its chunk */
162 #endif /* DEBUG */
163 static volatile int dma_wr_pos; /* current DMA write pos */
164 static int pcm_rd_pos; /* current PCM read pos */
165 static int pcm_enc_pos; /* position encoder is processing */
166 static volatile bool dma_lock; /* lock DMA write position */
167 static int enc_wr_index; /* encoder chunk write index */
168 static int enc_rd_index; /* encoder chunk read index */
169 static int enc_num_chunks; /* number of chunks in ringbuffer */
170 static size_t enc_chunk_size; /* maximum encoder chunk size */
171 static unsigned long enc_sample_rate; /* sample rate used by encoder */
172 static bool pcmrec_context = false; /* called by pcmrec thread? */
173 static bool pcm_buffer_empty; /* all pcm chunks processed? */
175 /** file flushing **/
176 static int low_watermark; /* Low watermark to stop flush */
177 static int high_watermark; /* max chunk limit for data flush */
178 static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */
179 static int last_ata_spinup_time = -1;/* previous spin time used */
180 #ifdef HAVE_PRIORITY_SCHEDULING
181 static int flood_watermark; /* boost thread priority when here */
182 #endif
184 /* Constants that control watermarks */
185 #define LOW_SECONDS 1 /* low watermark time till empty */
186 #define MINI_CHUNKS 10 /* chunk count for mini flush */
187 #ifdef HAVE_PRIORITY_SCHEDULING
188 #define PRIO_SECONDS 10 /* max flush time before priority boost */
189 #endif
190 #if MEM <= 16
191 #define PANIC_SECONDS 5 /* flood watermark time until full */
192 #define FLUSH_SECONDS 7 /* flush watermark time until full */
193 #else
194 #define PANIC_SECONDS 8
195 #define FLUSH_SECONDS 10
196 #endif /* MEM */
198 /** encoder events **/
199 static void (*enc_events_callback)(enum enc_events event, void *data);
201 /** Path queue for files to write **/
202 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
203 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
204 static unsigned char *fn_queue; /* pointer to first filename */
205 static ssize_t fnq_size; /* capacity of queue in bytes */
206 static int fnq_rd_pos; /* current read position */
207 static int fnq_wr_pos; /* current write position */
208 #define FNQ_NEXT(pos) \
209 ({ int p = (pos) + MAX_PATH; \
210 if (p >= fnq_size) \
211 p = 0; \
212 p; })
213 #define FNQ_PREV(pos) \
214 ({ int p = (pos) - MAX_PATH; \
215 if (p < 0) \
216 p = fnq_size - MAX_PATH; \
217 p; })
219 enum
221 PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by
222 incoming messages - combine
223 with other constants */
224 PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */
225 PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of
226 chunks */
227 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
228 reached */
231 /***************************************************************************/
233 static struct event_queue pcmrec_queue;
234 static struct queue_sender_list pcmrec_queue_send;
235 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
236 static const char pcmrec_thread_name[] = "pcmrec";
237 static struct thread_entry *pcmrec_thread_p;
239 static void pcmrec_thread(void);
241 enum
243 PCMREC_NULL = 0,
244 PCMREC_INIT, /* enable recording */
245 PCMREC_CLOSE, /* close recording */
246 PCMREC_OPTIONS, /* set recording options */
247 PCMREC_RECORD, /* record a new file */
248 PCMREC_STOP, /* stop the current recording */
249 PCMREC_PAUSE, /* pause the current recording */
250 PCMREC_RESUME, /* resume the current recording */
251 #if 0
252 PCMREC_FLUSH_NUM, /* flush a number of files out */
253 #endif
256 /*******************************************************************/
257 /* Functions that are not executing in the pcmrec_thread first */
258 /*******************************************************************/
260 /* Callback for when more data is ready - called in interrupt context */
261 static int pcm_rec_have_more(int status)
263 if (status < 0)
265 /* some error condition */
266 if (status == DMA_REC_ERROR_DMA)
268 /* Flush recorded data to disk and stop recording */
269 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
270 return -1;
272 /* else try again next transmission */
274 else if (!dma_lock)
276 /* advance write position */
277 int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
279 /* set pcm ovf if processing start position is inside current
280 write chunk */
281 if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE)
282 warnings |= PCMREC_W_PCM_BUFFER_OVF;
284 dma_wr_pos = next_pos;
287 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
288 return 0;
289 } /* pcm_rec_have_more */
291 static void reset_hardware(void)
293 /* reset pcm to defaults (playback only) */
294 pcm_set_frequency(HW_SAMPR_DEFAULT);
295 audio_set_output_source(AUDIO_SRC_PLAYBACK);
296 pcm_apply_settings();
299 /** pcm_rec_* group **/
302 * Clear all errors and warnings
304 void pcm_rec_error_clear(void)
306 errors = warnings = 0;
307 } /* pcm_rec_error_clear */
310 * Check mode, errors and warnings
312 unsigned long pcm_rec_status(void)
314 unsigned long ret = 0;
316 if (is_recording)
317 ret |= AUDIO_STATUS_RECORD;
318 else if (pre_record_ticks)
319 ret |= AUDIO_STATUS_PRERECORD;
321 if (is_paused)
322 ret |= AUDIO_STATUS_PAUSE;
324 if (errors)
325 ret |= AUDIO_STATUS_ERROR;
327 if (warnings)
328 ret |= AUDIO_STATUS_WARNING;
330 return ret;
331 } /* pcm_rec_status */
334 * Return warnings that have occured since recording started
336 unsigned long pcm_rec_get_warnings(void)
338 return warnings;
341 #if 0
342 int pcm_rec_current_bitrate(void)
344 if (accum_pcm_samples == 0)
345 return 0;
347 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
348 } /* pcm_rec_current_bitrate */
349 #endif
351 #if 0
352 int pcm_rec_encoder_afmt(void)
354 return enc_config.afmt;
355 } /* pcm_rec_encoder_afmt */
356 #endif
358 #if 0
359 int pcm_rec_rec_format(void)
361 return afmt_rec_format[enc_config.afmt];
362 } /* pcm_rec_rec_format */
363 #endif
365 #ifdef HAVE_SPDIF_IN
366 unsigned long pcm_rec_sample_rate(void)
368 /* Which is better ?? */
369 #if 0
370 return enc_sample_rate;
371 #endif
372 return sample_rate;
373 } /* audio_get_sample_rate */
374 #endif
377 * Creates pcmrec_thread
379 void pcm_rec_init(void)
381 queue_init(&pcmrec_queue, true);
382 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send);
383 pcmrec_thread_p =
384 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
385 pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
386 IF_COP(, CPU, false));
387 } /* pcm_rec_init */
389 /** audio_* group **/
392 * Initializes recording - call before calling any other recording function
394 void audio_init_recording(unsigned int buffer_offset)
396 logf("audio_init_recording");
397 queue_send(&pcmrec_queue, PCMREC_INIT, 0);
398 logf("audio_init_recording done");
399 (void)buffer_offset;
400 } /* audio_init_recording */
403 * Closes recording - call audio_stop_recording first
405 void audio_close_recording(void)
407 logf("audio_close_recording");
408 queue_send(&pcmrec_queue, PCMREC_CLOSE, 0);
409 logf("audio_close_recording done");
410 } /* audio_close_recording */
413 * Sets recording parameters
415 void audio_set_recording_options(struct audio_recording_options *options)
417 logf("audio_set_recording_options");
418 queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options);
419 logf("audio_set_recording_options done");
420 } /* audio_set_recording_options */
423 * Start recording if not recording or else split
425 void audio_record(const char *filename)
427 logf("audio_record: %s", filename);
428 flush_interrupt();
429 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
430 logf("audio_record_done");
431 } /* audio_record */
434 * audio_record wrapper for API compatibility with HW codec
436 void audio_new_file(const char *filename)
438 audio_record(filename);
439 } /* audio_new_file */
442 * Stop current recording if recording
444 void audio_stop_recording(void)
446 logf("audio_stop_recording");
447 flush_interrupt();
448 queue_post(&pcmrec_queue, PCMREC_STOP, 0);
449 logf("audio_stop_recording done");
450 } /* audio_stop_recording */
453 * Pause current recording
455 void audio_pause_recording(void)
457 logf("audio_pause_recording");
458 flush_interrupt();
459 queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
460 logf("audio_pause_recording done");
461 } /* audio_pause_recording */
464 * Resume current recording if paused
466 void audio_resume_recording(void)
468 logf("audio_resume_recording");
469 queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
470 logf("audio_resume_recording done");
471 } /* audio_resume_recording */
474 * Note that microphone is mono, only left value is used
475 * See audiohw_set_recvol() for exact ranges.
477 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
480 void audio_set_recording_gain(int left, int right, int type)
482 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
483 audiohw_set_recvol(left, right, type);
484 } /* audio_set_recording_gain */
486 /** Information about current state **/
489 * Return current recorded time in ticks (playback eqivalent time)
491 unsigned long audio_recorded_time(void)
493 if (!is_recording || enc_sample_rate == 0)
494 return 0;
496 /* return actual recorded time a la encoded data even if encoder rate
497 doesn't match the pcm rate */
498 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
499 } /* audio_recorded_time */
502 * Return number of bytes encoded to output
504 unsigned long audio_num_recorded_bytes(void)
506 if (!is_recording)
507 return 0;
509 return num_rec_bytes;
510 } /* audio_num_recorded_bytes */
512 /***************************************************************************/
513 /* */
514 /* Functions that execute in the context of pcmrec_thread */
515 /* */
516 /***************************************************************************/
518 /** Filename Queue **/
520 /* returns true if the queue is empty */
521 static inline bool pcmrec_fnq_is_empty(void)
523 return fnq_rd_pos == fnq_wr_pos;
524 } /* pcmrec_fnq_is_empty */
526 /* empties the filename queue */
527 static inline void pcmrec_fnq_set_empty(void)
529 fnq_rd_pos = fnq_wr_pos;
530 } /* pcmrec_fnq_set_empty */
532 /* returns true if the queue is full */
533 static bool pcmrec_fnq_is_full(void)
535 ssize_t size = fnq_wr_pos - fnq_rd_pos;
536 if (size < 0)
537 size += fnq_size;
539 return size >= fnq_size - MAX_PATH;
540 } /* pcmrec_fnq_is_full */
542 /* queue another filename - will overwrite oldest one if full */
543 static bool pcmrec_fnq_add_filename(const char *filename)
545 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
546 fnq_wr_pos = FNQ_NEXT(fnq_wr_pos);
548 if (fnq_rd_pos != fnq_wr_pos)
549 return true;
551 /* queue full */
552 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
553 return true;
554 } /* pcmrec_fnq_add_filename */
556 /* replace the last filename added */
557 static bool pcmrec_fnq_replace_tail(const char *filename)
559 int pos;
561 if (pcmrec_fnq_is_empty())
562 return false;
564 pos = FNQ_PREV(fnq_wr_pos);
566 strncpy(fn_queue + pos, filename, MAX_PATH);
568 return true;
569 } /* pcmrec_fnq_replace_tail */
571 /* pulls the next filename from the queue */
572 static bool pcmrec_fnq_get_filename(char *filename)
574 if (pcmrec_fnq_is_empty())
575 return false;
577 if (filename)
578 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
580 fnq_rd_pos = FNQ_NEXT(fnq_rd_pos);
581 return true;
582 } /* pcmrec_fnq_get_filename */
584 /* close the file number pointed to by fd_p */
585 static void pcmrec_close_file(int *fd_p)
587 if (*fd_p < 0)
588 return; /* preserve error */
590 if (close(*fd_p) != 0)
591 errors |= PCMREC_E_IO;
593 *fd_p = -1;
594 } /* pcmrec_close_file */
596 /** Data Flushing **/
599 * called after callback to update sizes if codec changed the amount of data
600 * a chunk represents
602 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
603 unsigned long prev_num_pcm)
605 if (rec_fdata.new_enc_size != prev_enc_size)
607 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
608 num_rec_bytes += size_diff;
609 #if 0
610 accum_rec_bytes += size_diff;
611 #endif
614 if (rec_fdata.new_num_pcm != prev_num_pcm)
616 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
617 num_rec_samples += pcm_diff;
618 #if 0
619 accum_pcm_samples += pcm_diff;
620 #endif
622 } /* pcmrec_update_sizes_inl */
624 /* don't need to inline every instance */
625 static void pcmrec_update_sizes(size_t prev_enc_size,
626 unsigned long prev_num_pcm)
628 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
629 } /* pcmrec_update_sizes */
631 static void pcmrec_start_file(void)
633 size_t enc_size = rec_fdata.new_enc_size;
634 unsigned long num_pcm = rec_fdata.new_num_pcm;
635 int curr_rec_file = rec_fdata.rec_file;
636 char filename[MAX_PATH];
638 /* must always pull the filename that matches with this queue */
639 if (!pcmrec_fnq_get_filename(filename))
641 logf("start file: fnq empty");
642 *filename = '\0';
643 errors |= PCMREC_E_FNQ_DESYNC;
645 else if (errors != 0)
647 logf("start file: error already");
649 else if (curr_rec_file >= 0)
651 /* Any previous file should have been closed */
652 logf("start file: file already open");
653 errors |= PCMREC_E_FNQ_DESYNC;
656 if (errors != 0)
657 rec_fdata.chunk->flags |= CHUNKF_ERROR;
659 /* encoder can set error flag here and should increase
660 enc_new_size and pcm_new_size to reflect additional
661 data written if any */
662 rec_fdata.filename = filename;
663 enc_events_callback(ENC_START_FILE, &rec_fdata);
665 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
667 logf("start file: enc error");
668 errors |= PCMREC_E_ENCODER;
671 if (errors != 0)
673 pcmrec_close_file(&curr_rec_file);
674 /* Write no more to this file */
675 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
677 else
679 pcmrec_update_sizes(enc_size, num_pcm);
682 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
683 } /* pcmrec_start_file */
685 static inline void pcmrec_write_chunk(void)
687 size_t enc_size = rec_fdata.new_enc_size;
688 unsigned long num_pcm = rec_fdata.new_num_pcm;
690 if (errors != 0)
691 rec_fdata.chunk->flags |= CHUNKF_ERROR;
693 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
695 if ((long)rec_fdata.chunk->flags >= 0)
697 pcmrec_update_sizes_inl(enc_size, num_pcm);
699 else if (errors == 0)
701 logf("wr chk enc error %lu %lu",
702 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
703 errors |= PCMREC_E_ENCODER;
705 } /* pcmrec_write_chunk */
707 static void pcmrec_end_file(void)
709 /* all data in output buffer for current file will have been
710 written and encoder can now do any nescessary steps to
711 finalize the written file */
712 size_t enc_size = rec_fdata.new_enc_size;
713 unsigned long num_pcm = rec_fdata.new_num_pcm;
715 enc_events_callback(ENC_END_FILE, &rec_fdata);
717 if (errors == 0)
719 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
721 logf("end file: enc error");
722 errors |= PCMREC_E_ENCODER;
724 else
726 pcmrec_update_sizes(enc_size, num_pcm);
730 /* Force file close if error */
731 if (errors != 0)
732 pcmrec_close_file(&rec_fdata.rec_file);
734 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
735 } /* pcmrec_end_file */
738 * Update buffer watermarks with spinup time compensation
740 * All this assumes reasonable data rates, chunk sizes and sufficient
741 * memory for the most part. Some dumb checks are included but perhaps
742 * are pointless since this all will break down at extreme limits that
743 * are currently not applicable to any supported device.
745 static void pcmrec_refresh_watermarks(void)
747 logf("ata spinup: %d", ata_spinup_time);
749 /* set the low mark for when flushing stops if automatic */
750 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
751 / enc_chunk_size;
752 logf("low wmk: %d", low_watermark);
754 #ifdef HAVE_PRIORITY_SCHEDULING
755 /* panic boost thread priority if 2 seconds of ground is lost -
756 this allows encoder to boost with just under a second of
757 pcm data (if not yet full enough to boost itself)
758 and not falsely trip the alarm. */
759 flood_watermark = enc_num_chunks -
760 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
761 / enc_chunk_size;
763 if (flood_watermark < low_watermark)
765 logf("warning: panic < low");
766 flood_watermark = low_watermark;
769 logf("flood at: %d", flood_watermark);
770 #endif
771 spinup_time = last_ata_spinup_time = ata_spinup_time;
773 /* write at 8s + st remaining in enc_buffer - range 12s to
774 20s total - default to 3.5s spinup. */
775 if (spinup_time == 0)
776 spinup_time = 35*HZ/10; /* default - cozy */
777 else if (spinup_time < 2*HZ)
778 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
779 else if (spinup_time > 10*HZ)
780 spinup_time = 10*HZ; /* do you have a functioning HD? */
782 /* try to start writing with 10s remaining after disk spinup */
783 high_watermark = enc_num_chunks -
784 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
785 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
787 if (high_watermark < low_watermark)
789 high_watermark = low_watermark;
790 low_watermark /= 2;
791 logf("warning: low 'write at'");
794 logf("write at: %d", high_watermark);
795 } /* pcmrec_refresh_watermarks */
798 * Process the chunks
800 * This function is called when queue_get_w_tmo times out.
802 * Set flush_num to the number of files to flush to disk or to
803 * a PCMREC_FLUSH_* constant.
805 static void pcmrec_flush(unsigned flush_num)
807 #ifdef HAVE_PRIORITY_SCHEDULING
808 static unsigned long last_flush_tick; /* tick when function returned */
809 unsigned long start_tick; /* When flush started */
810 unsigned long prio_tick; /* Timeout for auto boost */
811 int prio_pcmrec; /* Current thread priority for pcmrec */
812 int prio_codec; /* Current thread priority for codec */
813 #endif
814 int num_ready; /* Number of chunks ready at start */
815 unsigned remaining; /* Number of file starts remaining */
816 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
817 bool interruptable; /* Flush can be interupted */
819 num_ready = enc_wr_index - enc_rd_index;
820 if (num_ready < 0)
821 num_ready += enc_num_chunks;
823 /* save interruptable flag and remove it to get the actual count */
824 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
825 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
827 if (flush_num == 0)
829 if (!is_recording)
830 return;
832 if (ata_spinup_time != last_ata_spinup_time)
833 pcmrec_refresh_watermarks();
835 /* enough available? no? then leave */
836 if (num_ready < high_watermark)
837 return;
838 } /* endif (flush_num == 0) */
840 #ifdef HAVE_PRIORITY_SCHEDULING
841 start_tick = current_tick;
842 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
844 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
846 /* if we're getting called too much and this isn't forced,
847 boost stat by expiring timeout in advance */
848 logf("too frequent flush");
849 prio_tick = current_tick - 1;
852 prio_pcmrec = -1;
853 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
854 need init */
855 #endif
857 logf("writing:%d(%d):%s%s", num_ready, flush_num,
858 interruptable ? "i" : "",
859 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
861 cpu_boost(true);
863 remaining = flush_num;
864 chunks_flushed = 0;
866 while (num_ready > 0)
868 /* check current number of encoder chunks */
869 int num = enc_wr_index - enc_rd_index;
870 if (num < 0)
871 num += enc_num_chunks;
873 if (num <= low_watermark &&
874 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
876 logf("low data: %d", num);
877 break; /* data remaining is below threshold */
880 if (interruptable && flush_interrupts > 0)
882 logf("int at: %d", num);
883 break; /* interrupted */
886 #ifdef HAVE_PRIORITY_SCHEDULING
887 if (prio_pcmrec == -1 && (num >= flood_watermark ||
888 TIME_AFTER(current_tick, prio_tick)))
890 /* losing ground or holding without progress - boost
891 priority until finished */
892 logf("pcmrec: boost (%s)",
893 num >= flood_watermark ? "num" : "time");
894 prio_pcmrec = thread_set_priority(NULL,
895 thread_get_priority(NULL) - 1);
896 prio_codec = thread_set_priority(codec_thread_p,
897 thread_get_priority(codec_thread_p) - 1);
899 #endif
901 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
902 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
903 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
905 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
907 pcmrec_start_file();
908 if (--remaining == 0)
909 num_ready = 0; /* stop on next loop - must write this
910 chunk if it has data */
913 pcmrec_write_chunk();
915 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
916 pcmrec_end_file();
918 INC_ENC_INDEX(enc_rd_index);
920 if (errors != 0)
921 break;
923 if (flush_num == PCMREC_FLUSH_MINI &&
924 ++chunks_flushed >= MINI_CHUNKS)
926 logf("mini flush break");
927 break;
929 /* no yielding; the file apis called in the codecs do that
930 sufficiently */
931 } /* end while */
933 /* sync file */
934 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
935 errors |= PCMREC_E_IO;
937 cpu_boost(false);
939 #ifdef HAVE_PRIORITY_SCHEDULING
940 if (prio_pcmrec != -1)
942 /* return to original priorities */
943 logf("pcmrec: unboost priority");
944 thread_set_priority(NULL, prio_pcmrec);
945 thread_set_priority(codec_thread_p, prio_codec);
948 last_flush_tick = current_tick; /* save tick when we left */
949 #endif
951 logf("done");
952 } /* pcmrec_flush */
955 * Marks a new stream in the buffer and gives the encoder a chance for special
956 * handling of transition from one to the next. The encoder may change the
957 * chunk that ends the old stream by requesting more chunks and similiarly for
958 * the new but must always advance the position though the interface. It can
959 * later reject any data it cares to when writing the file but should mark the
960 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
961 * a NULL data pointer without error as well.
963 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
965 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
966 } /* pcmrec_get_chunk_index */
968 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
970 DEC_ENC_INDEX(index);
971 return GET_ENC_CHUNK(index);
972 } /* pcmrec_get_prev_chunk */
974 static void pcmrec_new_stream(const char *filename, /* next file name */
975 unsigned long flags, /* CHUNKF_* flags */
976 int pre_index) /* index for prerecorded data */
978 logf("pcmrec_new_stream");
979 char path[MAX_PATH]; /* place to copy filename so sender can be released */
981 struct enc_buffer_event_data data;
982 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
983 new filename */
984 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
985 stream */
986 bool did_flush = false; /* did a flush occurr? */
988 if (filename)
989 strncpy(path, filename, MAX_PATH);
990 queue_reply(&pcmrec_queue, 0); /* We have all we need */
992 data.pre_chunk = NULL;
993 data.chunk = GET_ENC_CHUNK(enc_wr_index);
995 /* end chunk */
996 if (flags & CHUNKF_END_FILE)
998 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1000 if (data.chunk->flags & CHUNKF_START_FILE)
1002 /* cannot start and end on same unprocessed chunk */
1003 logf("file end on start");
1004 flags &= ~CHUNKF_END_FILE;
1006 else if (enc_rd_index == enc_wr_index)
1008 /* all data flushed but file not ended - chunk will be left
1009 empty */
1010 logf("end on dead end");
1011 data.chunk->flags = 0;
1012 data.chunk->enc_size = 0;
1013 data.chunk->num_pcm = 0;
1014 data.chunk->enc_data = NULL;
1015 INC_ENC_INDEX(enc_wr_index);
1016 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1018 else
1020 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1022 if (last->flags & CHUNKF_END_FILE)
1024 /* end already processed and marked - can't end twice */
1025 logf("file end again");
1026 flags &= ~CHUNKF_END_FILE;
1031 /* start chunk */
1032 if (flags & CHUNKF_START_FILE)
1034 bool pre = flags & CHUNKF_PRERECORD;
1036 if (pre)
1038 logf("stream prerecord start");
1039 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1040 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1042 else
1044 logf("stream normal start");
1045 start = data.chunk;
1046 start->flags &= CHUNKF_START_FILE;
1049 /* if encoder hasn't yet processed the last start - abort the start
1050 of the previous file queued or else it will be empty and invalid */
1051 if (start->flags & CHUNKF_START_FILE)
1053 logf("replacing fnq tail: %s", filename);
1054 fnq_add_fn = pcmrec_fnq_replace_tail;
1056 else
1058 logf("adding filename: %s", filename);
1059 fnq_add_fn = pcmrec_fnq_add_filename;
1063 data.flags = flags;
1064 pcmrec_context = true; /* switch encoder context */
1065 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1066 pcmrec_context = false; /* switch back */
1068 if (flags & CHUNKF_END_FILE)
1070 int i = pcmrec_get_chunk_index(data.chunk);
1071 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1074 if (start)
1076 if (!(flags & CHUNKF_PRERECORD))
1078 /* get stats on data added to start - sort of a prerecord
1079 operation */
1080 int i = pcmrec_get_chunk_index(data.chunk);
1081 struct enc_chunk_hdr *chunk = data.chunk;
1083 logf("start data: %d %d", i, enc_wr_index);
1085 num_rec_bytes = 0;
1086 num_rec_samples = 0;
1088 while (i != enc_wr_index)
1090 num_rec_bytes += chunk->enc_size;
1091 num_rec_samples += chunk->num_pcm;
1092 INC_ENC_INDEX(i);
1093 chunk = GET_ENC_CHUNK(i);
1096 start->flags &= ~CHUNKF_START_FILE;
1097 start = data.chunk;
1100 start->flags |= CHUNKF_START_FILE;
1102 /* flush all pending files out if full and adding */
1103 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1105 logf("fnq full");
1106 pcmrec_flush(PCMREC_FLUSH_ALL);
1107 did_flush = true;
1110 fnq_add_fn(path);
1113 /* Make sure to complete any interrupted high watermark */
1114 if (!did_flush)
1115 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1116 } /* pcmrec_new_stream */
1118 /** event handlers for pcmrec thread */
1120 /* PCMREC_INIT */
1121 static void pcmrec_init(void)
1123 unsigned char *buffer;
1125 /* warings and errors */
1126 warnings =
1127 errors = 0;
1129 pcmrec_close_file(&rec_fdata.rec_file);
1130 rec_fdata.rec_file = -1;
1132 /* pcm FIFO */
1133 dma_lock = true;
1134 pcm_rd_pos = 0;
1135 dma_wr_pos = 0;
1136 pcm_enc_pos = 0;
1138 /* encoder FIFO */
1139 enc_wr_index = 0;
1140 enc_rd_index = 0;
1142 /* filename queue */
1143 fnq_rd_pos = 0;
1144 fnq_wr_pos = 0;
1146 /* stats */
1147 num_rec_bytes = 0;
1148 num_rec_samples = 0;
1149 #if 0
1150 accum_rec_bytes = 0;
1151 accum_pcm_samples = 0;
1152 #endif
1154 pre_record_ticks = 0;
1156 is_recording = false;
1157 is_paused = false;
1159 buffer = audio_get_recording_buffer(&rec_buffer_size);
1161 /* Line align pcm_buffer 2^4=16 bytes */
1162 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1163 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1164 PCM_MAX_FEED_SIZE, 2);
1165 /* Adjust available buffer for possible align advancement */
1166 rec_buffer_size -= pcm_buffer - buffer;
1168 pcm_init_recording();
1169 } /* pcmrec_init */
1171 /* PCMREC_CLOSE */
1172 static void pcmrec_close(void)
1174 dma_lock = true;
1175 pre_record_ticks = 0; /* Can't be prerecording any more */
1176 warnings = 0;
1177 pcm_close_recording();
1178 reset_hardware();
1179 audio_remove_encoder();
1180 } /* pcmrec_close */
1182 /* PCMREC_OPTIONS */
1183 static void pcmrec_set_recording_options(
1184 struct audio_recording_options *options)
1186 /* stop DMA transfer */
1187 dma_lock = true;
1188 pcm_stop_recording();
1190 rec_frequency = options->rec_frequency;
1191 rec_source = options->rec_source;
1192 num_channels = options->rec_channels == 1 ? 1 : 2;
1193 pre_record_ticks = options->rec_prerecord_time * HZ;
1194 enc_config = options->enc_config;
1195 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1197 #ifdef HAVE_SPDIF_IN
1198 if (rec_source == AUDIO_SRC_SPDIF)
1200 /* must measure SPDIF sample rate before configuring codecs */
1201 unsigned long sr = spdif_measure_frequency();
1202 /* round to master list for SPDIF rate */
1203 int index = round_value_to_list32(sr, audio_master_sampr_list,
1204 SAMPR_NUM_FREQ, false);
1205 sample_rate = audio_master_sampr_list[index];
1206 /* round to HW playback rates for monitoring */
1207 index = round_value_to_list32(sr, hw_freq_sampr,
1208 HW_NUM_FREQ, false);
1209 pcm_set_frequency(hw_freq_sampr[index]);
1210 /* encoders with a limited number of rates do their own rounding */
1212 else
1213 #endif
1215 /* set sample rate from frequency selection */
1216 sample_rate = rec_freq_sampr[rec_frequency];
1217 pcm_set_frequency(sample_rate);
1220 /* set monitoring */
1221 audio_set_output_source(rec_source);
1223 /* apply hardware setting to start monitoring now */
1224 pcm_apply_settings();
1226 queue_reply(&pcmrec_queue, 0); /* Release sender */
1228 if (audio_load_encoder(enc_config.afmt))
1230 /* start DMA transfer */
1231 dma_lock = pre_record_ticks == 0;
1232 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1233 PCM_CHUNK_SIZE);
1235 else
1237 logf("set rec opt: enc load failed");
1238 errors |= PCMREC_E_LOAD_ENCODER;
1240 } /* pcmrec_set_recording_options */
1242 /* PCMREC_RECORD - start recording (not gapless)
1243 or split stream (gapless) */
1244 static void pcmrec_record(const char *filename)
1246 unsigned long pre_sample_ticks;
1247 int rd_start;
1248 unsigned long flags;
1249 int pre_index;
1251 logf("pcmrec_record: %s", filename);
1253 /* reset stats */
1254 num_rec_bytes = 0;
1255 num_rec_samples = 0;
1257 if (!is_recording)
1259 #if 0
1260 accum_rec_bytes = 0;
1261 accum_pcm_samples = 0;
1262 #endif
1263 warnings = 0; /* reset warnings */
1265 rd_start = enc_wr_index;
1266 pre_sample_ticks = 0;
1268 pcmrec_refresh_watermarks();
1270 if (pre_record_ticks)
1272 int i = rd_start;
1273 /* calculate number of available chunks */
1274 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1275 enc_num_chunks) % enc_num_chunks;
1276 /* overflow at 974 seconds of prerecording at 44.1kHz */
1277 unsigned long pre_record_sample_ticks =
1278 enc_sample_rate*pre_record_ticks;
1279 int pre_chunks = 0; /* Counter to limit prerecorded time to
1280 prevent flood state at outset */
1282 logf("pre-st: %ld", pre_record_sample_ticks);
1284 /* Get exact measure of recorded data as number of samples aren't
1285 nescessarily going to be the max for each chunk */
1286 for (; avail_pre_chunks-- > 0;)
1288 struct enc_chunk_hdr *chunk;
1289 unsigned long chunk_sample_ticks;
1291 DEC_ENC_INDEX(i);
1293 chunk = GET_ENC_CHUNK(i);
1295 /* must have data to be counted */
1296 if (chunk->enc_data == NULL)
1297 continue;
1299 chunk_sample_ticks = chunk->num_pcm*HZ;
1301 rd_start = i;
1302 pre_sample_ticks += chunk_sample_ticks;
1303 num_rec_bytes += chunk->enc_size;
1304 num_rec_samples += chunk->num_pcm;
1305 pre_chunks++;
1307 /* stop here if enough already */
1308 if (pre_chunks >= high_watermark ||
1309 pre_sample_ticks >= pre_record_sample_ticks)
1311 logf("pre-chks: %d", pre_chunks);
1312 break;
1316 #if 0
1317 accum_rec_bytes = num_rec_bytes;
1318 accum_pcm_samples = num_rec_samples;
1319 #endif
1322 enc_rd_index = rd_start;
1324 /* filename queue should be empty */
1325 if (!pcmrec_fnq_is_empty())
1327 logf("fnq: not empty!");
1328 pcmrec_fnq_set_empty();
1331 flags = CHUNKF_START_FILE;
1332 if (pre_sample_ticks > 0)
1333 flags |= CHUNKF_PRERECORD;
1335 pre_index = enc_rd_index;
1337 dma_lock = false;
1338 is_paused = false;
1339 is_recording = true;
1341 else
1343 /* already recording, just split the stream */
1344 logf("inserting split");
1345 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1346 pre_index = 0;
1349 pcmrec_new_stream(filename, flags, pre_index);
1350 logf("pcmrec_record done");
1351 } /* pcmrec_record */
1353 /* PCMREC_STOP */
1354 static void pcmrec_stop(void)
1356 logf("pcmrec_stop");
1358 if (is_recording)
1360 dma_lock = true; /* lock dma write position */
1362 /* flush all available data first to avoid overflow while waiting
1363 for encoding to finish */
1364 pcmrec_flush(PCMREC_FLUSH_ALL);
1366 /* wait for encoder to finish remaining data */
1367 while (errors == 0 && !pcm_buffer_empty)
1368 yield();
1370 /* end stream at last data */
1371 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1373 /* flush anything else encoder added */
1374 pcmrec_flush(PCMREC_FLUSH_ALL);
1376 /* remove any pending file start not yet processed - should be at
1377 most one at enc_wr_index */
1378 pcmrec_fnq_get_filename(NULL);
1379 /* encoder should abort any chunk it was in midst of processing */
1380 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1382 /* filename queue should be empty */
1383 if (!pcmrec_fnq_is_empty())
1385 logf("fnq: not empty!");
1386 pcmrec_fnq_set_empty();
1389 /* be absolutely sure the file is closed */
1390 if (errors != 0)
1391 pcmrec_close_file(&rec_fdata.rec_file);
1392 rec_fdata.rec_file = -1;
1394 is_recording = false;
1395 is_paused = false;
1396 dma_lock = pre_record_ticks == 0;
1398 else
1400 logf("not recording");
1403 logf("pcmrec_stop done");
1404 } /* pcmrec_stop */
1406 /* PCMREC_PAUSE */
1407 static void pcmrec_pause(void)
1409 logf("pcmrec_pause");
1411 if (!is_recording)
1413 logf("not recording");
1415 else if (is_paused)
1417 logf("already paused");
1419 else
1421 dma_lock = true;
1422 is_paused = true;
1425 logf("pcmrec_pause done");
1426 } /* pcmrec_pause */
1428 /* PCMREC_RESUME */
1429 static void pcmrec_resume(void)
1431 logf("pcmrec_resume");
1433 if (!is_recording)
1435 logf("not recording");
1437 else if (!is_paused)
1439 logf("not paused");
1441 else
1443 is_paused = false;
1444 is_recording = true;
1445 dma_lock = false;
1448 logf("pcmrec_resume done");
1449 } /* pcmrec_resume */
1451 static void pcmrec_thread(void) __attribute__((noreturn));
1452 static void pcmrec_thread(void)
1454 struct event ev;
1456 logf("thread pcmrec start");
1458 while(1)
1460 if (is_recording)
1462 /* Poll periodically to flush data */
1463 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1465 if (ev.id == SYS_TIMEOUT)
1467 /* Messages that interrupt this will complete it */
1468 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1469 PCMREC_FLUSH_INTERRUPTABLE);
1470 continue;
1473 else
1475 /* Not doing anything - sit and wait for commands */
1476 queue_wait(&pcmrec_queue, &ev);
1479 switch (ev.id)
1481 case PCMREC_INIT:
1482 pcmrec_init();
1483 break;
1485 case PCMREC_CLOSE:
1486 pcmrec_close();
1487 break;
1489 case PCMREC_OPTIONS:
1490 pcmrec_set_recording_options(
1491 (struct audio_recording_options *)ev.data);
1492 break;
1494 case PCMREC_RECORD:
1495 clear_flush_interrupt();
1496 pcmrec_record((const char *)ev.data);
1497 break;
1499 case PCMREC_STOP:
1500 clear_flush_interrupt();
1501 pcmrec_stop();
1502 break;
1504 case PCMREC_PAUSE:
1505 clear_flush_interrupt();
1506 pcmrec_pause();
1507 break;
1509 case PCMREC_RESUME:
1510 pcmrec_resume();
1511 break;
1512 #if 0
1513 case PCMREC_FLUSH_NUM:
1514 pcmrec_flush((unsigned)ev.data);
1515 break;
1516 #endif
1517 case SYS_USB_CONNECTED:
1518 if (is_recording)
1519 break;
1520 pcmrec_close();
1521 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1522 usb_wait_for_disconnect(&pcmrec_queue);
1523 flush_interrupts = 0;
1524 break;
1525 } /* end switch */
1526 } /* end while */
1527 } /* pcmrec_thread */
1529 /****************************************************************************/
1530 /* */
1531 /* following functions will be called by the encoder codec */
1532 /* in a free-threaded manner */
1533 /* */
1534 /****************************************************************************/
1536 /* pass the encoder settings to the encoder */
1537 void enc_get_inputs(struct enc_inputs *inputs)
1539 inputs->sample_rate = sample_rate;
1540 inputs->num_channels = num_channels;
1541 inputs->config = &enc_config;
1542 } /* enc_get_inputs */
1544 /* set the encoder dimensions (called by encoder codec at initialization and
1545 termination) */
1546 void enc_set_parameters(struct enc_parameters *params)
1548 size_t bufsize, resbytes;
1550 logf("enc_set_parameters");
1552 if (!params)
1554 logf("reset");
1555 /* Encoder is terminating */
1556 memset(&enc_config, 0, sizeof (enc_config));
1557 enc_sample_rate = 0;
1558 return;
1561 enc_sample_rate = params->enc_sample_rate;
1562 logf("enc sampr:%lu", enc_sample_rate);
1564 pcm_rd_pos = dma_wr_pos;
1565 pcm_enc_pos = pcm_rd_pos;
1567 enc_config.afmt = params->afmt;
1568 /* addition of the header is always implied - chunk size 4-byte aligned */
1569 enc_chunk_size =
1570 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1571 enc_events_callback = params->events_callback;
1573 logf("chunk size:%lu", enc_chunk_size);
1575 /*** Configure the buffers ***/
1577 /* Layout of recording buffer:
1578 * [ax] = possible alignment x multiple
1579 * [sx] = possible size alignment of x multiple
1580 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1581 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1583 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1584 logf("resbytes:%lu", resbytes);
1586 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1587 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1588 #ifdef DEBUG
1589 - sizeof (*wrap_id_p)
1590 #endif
1593 enc_num_chunks = bufsize / enc_chunk_size;
1594 logf("num chunks:%d", enc_num_chunks);
1596 /* get real amount used by encoder chunks */
1597 bufsize = enc_num_chunks*enc_chunk_size;
1598 logf("enc size:%lu", bufsize);
1600 #ifdef DEBUG
1601 /* add magic at wraparound for spillover checks */
1602 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1603 bufsize += sizeof (*wrap_id_p);
1604 *wrap_id_p = ENC_CHUNK_MAGIC;
1605 #endif
1607 /** set OUT parameters **/
1608 params->enc_buffer = enc_buffer;
1609 params->buf_chunk_size = enc_chunk_size;
1610 params->num_chunks = enc_num_chunks;
1612 /* calculate reserve buffer start and return pointer to encoder */
1613 params->reserve_buffer = NULL;
1614 if (resbytes > 0)
1616 params->reserve_buffer = enc_buffer + bufsize;
1617 bufsize += resbytes;
1620 /* place filename queue at end of buffer using up whatever remains */
1621 fnq_rd_pos = 0; /* reset */
1622 fnq_wr_pos = 0; /* reset */
1623 fn_queue = enc_buffer + bufsize;
1624 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1625 fnq_size /= MAX_PATH;
1626 if (fnq_size > FNQ_MAX_NUM_PATHS)
1627 fnq_size = FNQ_MAX_NUM_PATHS;
1628 fnq_size *= MAX_PATH;
1629 logf("fnq files:%ld", fnq_size / MAX_PATH);
1631 #if defined(DEBUG)
1632 logf("ab :%08lX", (uintptr_t)audiobuf);
1633 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1634 logf("enc:%08lX", (uintptr_t)enc_buffer);
1635 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1636 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1637 logf("fnq:%08lX", (uintptr_t)fn_queue);
1638 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1639 logf("abe:%08lX", (uintptr_t)audiobufend);
1640 #endif
1642 /* init all chunk headers and reset indexes */
1643 enc_rd_index = 0;
1644 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1646 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1647 #ifdef DEBUG
1648 chunk->id = ENC_CHUNK_MAGIC;
1649 #endif
1650 chunk->flags = 0;
1653 logf("enc_set_parameters done");
1654 } /* enc_set_parameters */
1656 /* return encoder chunk at current write position -
1657 NOTE: can be called by pcmrec thread when splitting streams */
1658 struct enc_chunk_hdr * enc_get_chunk(void)
1660 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1662 #ifdef DEBUG
1663 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1665 errors |= PCMREC_E_CHUNK_OVF;
1666 logf("finish chk ovf: %d", enc_wr_index);
1668 #endif
1670 chunk->flags &= CHUNKF_START_FILE;
1672 if (!is_recording)
1673 chunk->flags |= CHUNKF_PRERECORD;
1675 return chunk;
1676 } /* enc_get_chunk */
1678 /* releases the current chunk into the available chunks -
1679 NOTE: can be called by pcmrec thread when splitting streams */
1680 void enc_finish_chunk(void)
1682 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1684 if ((long)chunk->flags < 0)
1686 /* encoder set error flag */
1687 errors |= PCMREC_E_ENCODER;
1688 logf("finish chk enc error");
1691 /* advance enc_wr_index to the next encoder chunk */
1692 INC_ENC_INDEX(enc_wr_index);
1694 if (enc_rd_index != enc_wr_index)
1696 num_rec_bytes += chunk->enc_size;
1697 num_rec_samples += chunk->num_pcm;
1698 #if 0
1699 accum_rec_bytes += chunk->enc_size;
1700 accum_pcm_samples += chunk->num_pcm;
1701 #endif
1703 else if (is_recording) /* buffer full */
1705 /* keep current position and put up warning flag */
1706 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1707 logf("enc_buffer ovf");
1708 DEC_ENC_INDEX(enc_wr_index);
1709 if (pcmrec_context)
1711 /* if stream splitting, keep this out of circulation and
1712 flush a small number, then readd - cannot risk losing
1713 stream markers */
1714 logf("mini flush");
1715 pcmrec_flush(PCMREC_FLUSH_MINI);
1716 INC_ENC_INDEX(enc_wr_index);
1719 else
1721 /* advance enc_rd_index for prerecording */
1722 INC_ENC_INDEX(enc_rd_index);
1724 } /* enc_finish_chunk */
1726 /* checks near empty state on pcm input buffer */
1727 int enc_pcm_buf_near_empty(void)
1729 /* less than 1sec raw data? => unboost encoder */
1730 int wp = dma_wr_pos;
1731 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1732 return avail < (sample_rate << 2) ? 1 : 0;
1733 } /* enc_pcm_buf_near_empty */
1735 /* passes a pointer to next chunk of unprocessed wav data */
1736 /* TODO: this really should give the actual size returned */
1737 unsigned char * enc_get_pcm_data(size_t size)
1739 int wp = dma_wr_pos;
1740 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1742 /* limit the requested pcm data size */
1743 if (size > PCM_MAX_FEED_SIZE)
1744 size = PCM_MAX_FEED_SIZE;
1746 if (avail >= size)
1748 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1749 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1751 pcm_enc_pos = pcm_rd_pos;
1752 pcm_rd_pos = next_pos;
1754 /* ptr must point to continous data at wraparound position */
1755 if ((size_t)pcm_rd_pos < size)
1757 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1758 pcm_buffer, pcm_rd_pos);
1761 pcm_buffer_empty = false;
1762 return ptr;
1765 /* not enough data available - encoder should idle */
1766 pcm_buffer_empty = true;
1767 return NULL;
1768 } /* enc_get_pcm_data */
1770 /* puts some pcm data back in the queue */
1771 size_t enc_unget_pcm_data(size_t size)
1773 int wp = dma_wr_pos;
1774 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1775 2*PCM_CHUNK_SIZE;
1777 /* allow one interrupt to occur during this call and not have the
1778 new read position inside the DMA destination chunk */
1779 if ((ssize_t)old_avail > 0)
1781 /* limit size to amount of old data remaining */
1782 if (size > old_avail)
1783 size = old_avail;
1785 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1786 pcm_rd_pos = pcm_enc_pos;
1788 return size;
1791 return 0;
1792 } /* enc_unget_pcm_data */
1794 /** Low level pcm recording apis **/
1796 /****************************************************************************
1797 * Functions that do not require targeted implementation but only a targeted
1798 * interface
1800 void pcm_record_data(pcm_more_callback_type2 more_ready,
1801 void *start, size_t size)
1803 if (!(start && size))
1804 return;
1806 pcm_callback_more_ready = more_ready;
1807 pcm_rec_dma_start(start, size);
1808 } /* pcm_record_data */
1810 void pcm_stop_recording(void)
1812 if (pcm_recording)
1813 pcm_rec_dma_stop();
1814 } /* pcm_stop_recording */