1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
35 /***************************************************************************/
38 * APIs implemented in the target tree portion:
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
64 for a safety net and a prompt
65 response to stop, split and pause
67 only interrupts a flush initiated
70 /* Utility functions for setting/clearing flushing interrupt flag */
71 static inline void flush_interrupt(void)
74 logf("flush int: %d", flush_interrupts
);
77 static inline void clear_flush_interrupt(void)
79 if (--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 **/
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 */
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
=
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
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
137 A.encoder: repeat 4. to 9.
138 B.pcmrec: enc_events_callback(); called for certain events
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))
151 #ifdef PCMREC_PARANOID
152 static void paranoid_set_code(unsigned long code
, int line
)
154 logf("%08X at %d", code
, line
);
161 #define PARANOID_ENC_INDEX_CHECK(index) \
162 { if (index != index##_last) \
163 paranoid_set_code((&index == &enc_rd_index) ? \
164 PCMREC_E_ENC_RD_INDEX_TRASHED : PCMREC_E_ENC_WR_INDEX_TRASHED, \
166 #define PARANOID_PCM_POS_CHECK(pos) \
167 { if (pos != pos##_last) \
168 paranoid_set_code((&pos == &pcm_rd_pos) ? \
169 PCMREC_W_PCM_RD_POS_TRASHED : PCMREC_W_DMA_WR_POS_TRASHED, \
171 #define PARANOID_SET_LAST(var) \
173 #define PARANOID_CHUNK_CHECK(chunk) \
174 paranoid_chunk_check(chunk)
176 #define PARANOID_ENC_INDEX_CHECK(index)
177 #define PARANOID_PCM_POS_CHECK(pos)
178 #define PARANOID_SET_LAST(var)
179 #define PARANOID_CHUNK_CHECK(chunk)
182 #define INC_ENC_INDEX(index) \
183 PARANOID_ENC_INDEX_CHECK(index) \
184 { if (++index >= enc_num_chunks) index = 0; } \
185 PARANOID_SET_LAST(index)
186 #define DEC_ENC_INDEX(index) \
187 PARANOID_ENC_INDEX_CHECK(index) \
188 { if (--index < 0) index = enc_num_chunks - 1; } \
189 PARANOID_SET_LAST(index)
190 #define SET_ENC_INDEX(index, value) \
191 PARANOID_ENC_INDEX_CHECK(index) \
193 PARANOID_SET_LAST(index)
194 #define SET_PCM_POS(pos, value) \
195 PARANOID_PCM_POS_CHECK(pos) \
197 PARANOID_SET_LAST(pos)
199 static size_t rec_buffer_size
; /* size of available buffer */
200 static unsigned char *pcm_buffer
; /* circular recording buffer */
201 static unsigned char *enc_buffer
; /* circular encoding buffer */
202 static volatile int dma_wr_pos
; /* current DMA write pos */
203 static int pcm_rd_pos
; /* current PCM read pos */
204 static int pcm_enc_pos
; /* position encoder is processing */
205 static volatile bool dma_lock
; /* lock DMA write position */
206 static int enc_wr_index
; /* encoder chunk write index */
207 static int enc_rd_index
; /* encoder chunk read index */
208 static int enc_num_chunks
; /* number of chunks in ringbuffer */
209 static size_t enc_chunk_size
; /* maximum encoder chunk size */
210 static unsigned long enc_sample_rate
; /* sample rate used by encoder */
211 static bool pcmrec_context
= false; /* called by pcmrec thread? */
212 static bool pcm_buffer_empty
; /* all pcm chunks processed? */
214 /** file flushing **/
215 static int low_watermark
; /* Low watermark to stop flush */
216 static int high_watermark
; /* max chunk limit for data flush */
217 static unsigned long spinup_time
= 35*HZ
/10; /* Fudged spinup time */
218 static int last_ata_spinup_time
= -1;/* previous spin time used */
219 #ifdef HAVE_PRIORITY_SCHEDULING
220 static int flood_watermark
; /* boost thread priority when here */
223 /* Constants that control watermarks */
224 #define LOW_SECONDS 1 /* low watermark time till empty */
225 #define MINI_CHUNKS 10 /* chunk count for mini flush */
226 #ifdef HAVE_PRIORITY_SCHEDULING
227 #define PRIO_SECONDS 10 /* max flush time before priority boost */
230 #define PANIC_SECONDS 5 /* flood watermark time until full */
231 #define FLUSH_SECONDS 7 /* flush watermark time until full */
233 #define PANIC_SECONDS 8
234 #define FLUSH_SECONDS 10
237 /** encoder events **/
238 static void (*enc_events_callback
)(enum enc_events event
, void *data
);
240 /** Path queue for files to write **/
241 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
242 #define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */
243 static unsigned char *fn_queue
; /* pointer to first filename */
244 static ssize_t fnq_size
; /* capacity of queue in bytes */
245 static int fnq_rd_pos
; /* current read position */
246 static int fnq_wr_pos
; /* current write position */
250 PCMREC_FLUSH_INTERRUPTABLE
= 0x8000000, /* Flush can be interrupted by
251 incoming messages - combine
252 with other constants */
253 PCMREC_FLUSH_ALL
= 0x7ffffff, /* Flush all files */
254 PCMREC_FLUSH_MINI
= 0x7fffffe, /* Flush a small number of
256 PCMREC_FLUSH_IF_HIGH
= 0x0000000, /* Flush if high watermark
260 /** extra debugging info positioned away from other vars **/
261 #ifdef PCMREC_PARANOID
262 static unsigned long *wrap_id_p
; /* magic at end of encoding buffer */
263 static volatile int dma_wr_pos_last
; /* previous dma write position */
264 static int pcm_rd_pos_last
; /* previous pcm read position */
265 static int enc_rd_index_last
; /* previsou encoder read position */
266 static int enc_wr_index_last
; /* previsou encoder read position */
270 /***************************************************************************/
272 static struct event_queue pcmrec_queue
;
273 static struct queue_sender_list pcmrec_queue_send
;
274 static long pcmrec_stack
[3*DEFAULT_STACK_SIZE
/sizeof(long)];
275 static const char pcmrec_thread_name
[] = "pcmrec";
276 static struct thread_entry
*pcmrec_thread_p
;
278 static void pcmrec_thread(void);
283 PCMREC_INIT
, /* enable recording */
284 PCMREC_CLOSE
, /* close recording */
285 PCMREC_OPTIONS
, /* set recording options */
286 PCMREC_RECORD
, /* record a new file */
287 PCMREC_STOP
, /* stop the current recording */
288 PCMREC_PAUSE
, /* pause the current recording */
289 PCMREC_RESUME
, /* resume the current recording */
291 PCMREC_FLUSH_NUM
, /* flush a number of files out */
295 /*******************************************************************/
296 /* Functions that are not executing in the pcmrec_thread first */
297 /*******************************************************************/
299 /* Callback for when more data is ready - called in interrupt context */
300 static int pcm_rec_have_more(int status
)
304 /* some error condition */
305 if (status
== DMA_REC_ERROR_DMA
)
307 /* Flush recorded data to disk and stop recording */
308 queue_post(&pcmrec_queue
, PCMREC_STOP
, 0);
311 /* else try again next transmission */
315 /* advance write position */
316 int next_pos
= (dma_wr_pos
+ PCM_CHUNK_SIZE
) & PCM_CHUNK_MASK
;
318 /* set pcm ovf if processing start position is inside current
320 if ((unsigned)(pcm_enc_pos
- next_pos
) < PCM_CHUNK_SIZE
)
321 warnings
|= PCMREC_W_PCM_BUFFER_OVF
;
323 #ifdef PCMREC_PARANOID
324 /* write position must always be on PCM_CHUNK_SIZE boundary -
325 anything else is corruption */
326 if (next_pos
& (PCM_CHUNK_SIZE
-1))
328 logf("dma_wr_pos unalgn: %d", next_pos
);
329 warnings
|= PCMREC_W_DMA_WR_POS_ALIGN
;
330 next_pos
&= ~PCM_CHUNK_SIZE
; /* re-align */
333 SET_PCM_POS(dma_wr_pos
, next_pos
);
336 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos
), PCM_CHUNK_SIZE
);
338 } /* pcm_rec_have_more */
340 static void reset_hardware(void)
342 /* reset pcm to defaults (playback only) */
343 pcm_set_frequency(HW_SAMPR_DEFAULT
);
344 audio_set_output_source(AUDIO_SRC_PLAYBACK
);
345 pcm_apply_settings();
348 /** pcm_rec_* group **/
351 * Clear all errors and warnings
353 void pcm_rec_error_clear(void)
355 errors
= warnings
= 0;
356 } /* pcm_rec_error_clear */
359 * Check mode, errors and warnings
361 unsigned long pcm_rec_status(void)
363 unsigned long ret
= 0;
366 ret
|= AUDIO_STATUS_RECORD
;
367 else if (pre_record_ticks
)
368 ret
|= AUDIO_STATUS_PRERECORD
;
371 ret
|= AUDIO_STATUS_PAUSE
;
374 ret
|= AUDIO_STATUS_ERROR
;
377 ret
|= AUDIO_STATUS_WARNING
;
380 } /* pcm_rec_status */
383 * Return warnings that have occured since recording started
385 unsigned long pcm_rec_get_warnings(void)
391 int pcm_rec_current_bitrate(void)
393 if (accum_pcm_samples
== 0)
396 return (int)(8*accum_rec_bytes
*enc_sample_rate
/ (1000*accum_pcm_samples
));
397 } /* pcm_rec_current_bitrate */
401 int pcm_rec_encoder_afmt(void)
403 return enc_config
.afmt
;
404 } /* pcm_rec_encoder_afmt */
408 int pcm_rec_rec_format(void)
410 return afmt_rec_format
[enc_config
.afmt
];
411 } /* pcm_rec_rec_format */
415 unsigned long pcm_rec_sample_rate(void)
417 /* Which is better ?? */
419 return enc_sample_rate
;
422 } /* audio_get_sample_rate */
426 * Creates pcmrec_thread
428 void pcm_rec_init(void)
430 queue_init(&pcmrec_queue
, true);
431 queue_set_irq_safe(&pcmrec_queue
, true);
432 queue_enable_queue_send(&pcmrec_queue
, &pcmrec_queue_send
);
434 create_thread(pcmrec_thread
, pcmrec_stack
, sizeof(pcmrec_stack
),
435 pcmrec_thread_name
IF_PRIO(, PRIORITY_RECORDING
)
436 IF_COP(, CPU
, false));
439 /** audio_* group **/
442 * Initializes recording - call before calling any other recording function
444 void audio_init_recording(unsigned int buffer_offset
)
446 logf("audio_init_recording");
447 queue_send(&pcmrec_queue
, PCMREC_INIT
, 0);
448 logf("audio_init_recording done");
450 } /* audio_init_recording */
453 * Closes recording - call audio_stop_recording first
455 void audio_close_recording(void)
457 logf("audio_close_recording");
458 queue_send(&pcmrec_queue
, PCMREC_CLOSE
, 0);
459 logf("audio_close_recording done");
460 } /* audio_close_recording */
463 * Sets recording parameters
465 void audio_set_recording_options(struct audio_recording_options
*options
)
467 logf("audio_set_recording_options");
468 queue_send(&pcmrec_queue
, PCMREC_OPTIONS
, (intptr_t)options
);
469 logf("audio_set_recording_options done");
470 } /* audio_set_recording_options */
473 * Start recording if not recording or else split
475 void audio_record(const char *filename
)
477 logf("audio_record: %s", filename
);
479 queue_send(&pcmrec_queue
, PCMREC_RECORD
, (intptr_t)filename
);
480 logf("audio_record_done");
484 * audio_record wrapper for API compatibility with HW codec
486 void audio_new_file(const char *filename
)
488 audio_record(filename
);
489 } /* audio_new_file */
492 * Stop current recording if recording
494 void audio_stop_recording(void)
496 logf("audio_stop_recording");
498 queue_send(&pcmrec_queue
, PCMREC_STOP
, 0);
499 logf("audio_stop_recording done");
500 } /* audio_stop_recording */
503 * Pause current recording
505 void audio_pause_recording(void)
507 logf("audio_pause_recording");
509 queue_send(&pcmrec_queue
, PCMREC_PAUSE
, 0);
510 logf("audio_pause_recording done");
511 } /* audio_pause_recording */
514 * Resume current recording if paused
516 void audio_resume_recording(void)
518 logf("audio_resume_recording");
519 queue_send(&pcmrec_queue
, PCMREC_RESUME
, 0);
520 logf("audio_resume_recording done");
521 } /* audio_resume_recording */
524 * Note that microphone is mono, only left value is used
525 * See audiohw_set_recvol() for exact ranges.
527 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
530 void audio_set_recording_gain(int left
, int right
, int type
)
532 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
533 audiohw_set_recvol(left
, right
, type
);
534 } /* audio_set_recording_gain */
536 /** Information about current state **/
539 * Return current recorded time in ticks (playback eqivalent time)
541 unsigned long audio_recorded_time(void)
543 if (!is_recording
|| enc_sample_rate
== 0)
546 /* return actual recorded time a la encoded data even if encoder rate
547 doesn't match the pcm rate */
548 return (long)(HZ
*(unsigned long long)num_rec_samples
/ enc_sample_rate
);
549 } /* audio_recorded_time */
552 * Return number of bytes encoded to output
554 unsigned long audio_num_recorded_bytes(void)
559 return num_rec_bytes
;
560 } /* audio_num_recorded_bytes */
562 /***************************************************************************/
564 /* Functions that execute in the context of pcmrec_thread */
566 /***************************************************************************/
568 /** Filename Queue **/
570 /* returns true if the queue is empty */
571 static inline bool pcmrec_fnq_is_empty(void)
573 return fnq_rd_pos
== fnq_wr_pos
;
574 } /* pcmrec_fnq_is_empty */
576 /* empties the filename queue */
577 static inline void pcmrec_fnq_set_empty(void)
579 fnq_rd_pos
= fnq_wr_pos
;
580 } /* pcmrec_fnq_set_empty */
582 /* returns true if the queue is full */
583 static bool pcmrec_fnq_is_full(void)
585 ssize_t size
= fnq_wr_pos
- fnq_rd_pos
;
589 return size
>= fnq_size
- MAX_PATH
;
590 } /* pcmrec_fnq_is_full */
592 /* queue another filename - will overwrite oldest one if full */
593 static bool pcmrec_fnq_add_filename(const char *filename
)
595 strncpy(fn_queue
+ fnq_wr_pos
, filename
, MAX_PATH
);
597 if ((fnq_wr_pos
+= MAX_PATH
) >= fnq_size
)
600 if (fnq_rd_pos
!= fnq_wr_pos
)
604 if ((fnq_rd_pos
+= MAX_PATH
) >= fnq_size
)
608 } /* pcmrec_fnq_add_filename */
610 /* replace the last filename added */
611 static bool pcmrec_fnq_replace_tail(const char *filename
)
615 if (pcmrec_fnq_is_empty())
618 pos
= fnq_wr_pos
- MAX_PATH
;
620 pos
= fnq_size
- MAX_PATH
;
622 strncpy(fn_queue
+ pos
, filename
, MAX_PATH
);
625 } /* pcmrec_fnq_replace_tail */
627 /* pulls the next filename from the queue */
628 static bool pcmrec_fnq_get_filename(char *filename
)
630 if (pcmrec_fnq_is_empty())
634 strncpy(filename
, fn_queue
+ fnq_rd_pos
, MAX_PATH
);
636 if ((fnq_rd_pos
+= MAX_PATH
) >= fnq_size
)
640 } /* pcmrec_fnq_get_filename */
642 /* close the file number pointed to by fd_p */
643 static void pcmrec_close_file(int *fd_p
)
646 return; /* preserve error */
648 if (close(*fd_p
) != 0)
649 errors
|= PCMREC_E_IO
;
652 } /* pcmrec_close_file */
654 #ifdef PCMREC_PARANOID
655 static void paranoid_chunk_check(const struct enc_chunk_hdr
*chunk
)
657 /* check integrity of things that must be ok - data or not */
659 /* check magic in header */
660 if (chunk
->id
!= ENC_CHUNK_MAGIC
)
662 errors
|= PCMREC_E_BAD_CHUNK
| PCMREC_E_CHUNK_OVF
;
663 logf("bad chunk: %d", chunk
- (struct enc_chunk_hdr
*)enc_buffer
);
666 /* check magic wrap id */
667 if (*wrap_id_p
!= ENC_CHUNK_MAGIC
)
669 errors
|= PCMREC_E_BAD_CHUNK
| PCMREC_E_CHUNK_OVF
;
670 logf("bad magic at wrap pos");
673 if (chunk
->enc_data
== NULL
) /* has data? */
676 /* check that data points to something after header */
677 if (chunk
->enc_data
< ENC_CHUNK_SKIP_HDR(chunk
->enc_data
, chunk
))
679 errors
|= PCMREC_E_BAD_CHUNK
;
680 logf("chk ptr < hdr end");
683 /* check if data end is within chunk */
684 if (chunk
->enc_data
+ chunk
->enc_size
>
685 (unsigned char *)chunk
+ enc_chunk_size
)
687 errors
|= PCMREC_E_BAD_CHUNK
;
688 logf("chk data > chk end");
691 if ((chunk
->flags
& ~CHUNKF_ALLFLAGS
) != 0)
693 errors
|= PCMREC_E_BAD_CHUNK
;
694 logf("chk bad flags %08X", chunk
->flags
);
696 } /* paranoid_chunk_check */
697 #endif /* PCMREC_PARANOID */
699 /** Data Flushing **/
702 * called after callback to update sizes if codec changed the amount of data
705 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size
,
706 unsigned long prev_num_pcm
)
708 if (rec_fdata
.new_enc_size
!= prev_enc_size
)
710 ssize_t size_diff
= rec_fdata
.new_enc_size
- prev_enc_size
;
711 num_rec_bytes
+= size_diff
;
713 accum_rec_bytes
+= size_diff
;
717 if (rec_fdata
.new_num_pcm
!= prev_num_pcm
)
719 unsigned long pcm_diff
= rec_fdata
.new_num_pcm
- prev_num_pcm
;
720 num_rec_samples
+= pcm_diff
;
722 accum_pcm_samples
+= pcm_diff
;
725 } /* pcmrec_update_sizes_inl */
727 /* don't need to inline every instance */
728 static void pcmrec_update_sizes(size_t prev_enc_size
,
729 unsigned long prev_num_pcm
)
731 pcmrec_update_sizes_inl(prev_enc_size
, prev_num_pcm
);
732 } /* pcmrec_update_sizes */
734 static void pcmrec_start_file(void)
736 size_t enc_size
= rec_fdata
.new_enc_size
;
737 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
738 int curr_rec_file
= rec_fdata
.rec_file
;
739 char filename
[MAX_PATH
];
741 /* must always pull the filename that matches with this queue */
742 if (!pcmrec_fnq_get_filename(filename
))
744 logf("start file: fnq empty");
746 errors
|= PCMREC_E_FNQ_DESYNC
;
748 else if (errors
!= 0)
750 logf("start file: error already");
752 else if (curr_rec_file
>= 0)
754 /* Any previous file should have been closed */
755 logf("start file: file already open");
756 errors
|= PCMREC_E_FNQ_DESYNC
;
760 rec_fdata
.chunk
->flags
|= CHUNKF_ERROR
;
762 /* encoder can set error flag here and should increase
763 enc_new_size and pcm_new_size to reflect additional
764 data written if any */
765 rec_fdata
.filename
= filename
;
766 enc_events_callback(ENC_START_FILE
, &rec_fdata
);
768 if (errors
== 0 && (rec_fdata
.chunk
->flags
& CHUNKF_ERROR
))
770 logf("start file: enc error");
771 errors
|= PCMREC_E_ENCODER
;
776 pcmrec_close_file(&curr_rec_file
);
777 /* Write no more to this file */
778 rec_fdata
.chunk
->flags
|= CHUNKF_END_FILE
;
782 pcmrec_update_sizes(enc_size
, num_pcm
);
785 rec_fdata
.chunk
->flags
&= ~CHUNKF_START_FILE
;
786 } /* pcmrec_start_file */
788 static inline void pcmrec_write_chunk(void)
790 size_t enc_size
= rec_fdata
.new_enc_size
;
791 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
794 rec_fdata
.chunk
->flags
|= CHUNKF_ERROR
;
796 enc_events_callback(ENC_WRITE_CHUNK
, &rec_fdata
);
798 if ((long)rec_fdata
.chunk
->flags
>= 0)
800 pcmrec_update_sizes_inl(enc_size
, num_pcm
);
802 else if (errors
== 0)
804 logf("wr chk enc error %lu %lu",
805 rec_fdata
.chunk
->enc_size
, rec_fdata
.chunk
->num_pcm
);
806 errors
|= PCMREC_E_ENCODER
;
808 } /* pcmrec_write_chunk */
810 static void pcmrec_end_file(void)
812 /* all data in output buffer for current file will have been
813 written and encoder can now do any nescessary steps to
814 finalize the written file */
815 size_t enc_size
= rec_fdata
.new_enc_size
;
816 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
818 enc_events_callback(ENC_END_FILE
, &rec_fdata
);
822 if (rec_fdata
.chunk
->flags
& CHUNKF_ERROR
)
824 logf("end file: enc error");
825 errors
|= PCMREC_E_ENCODER
;
829 pcmrec_update_sizes(enc_size
, num_pcm
);
833 /* Force file close if error */
835 pcmrec_close_file(&rec_fdata
.rec_file
);
837 rec_fdata
.chunk
->flags
&= ~CHUNKF_END_FILE
;
838 } /* pcmrec_end_file */
841 * Update buffer watermarks with spinup time compensation
843 * All this assumes reasonable data rates, chunk sizes and sufficient
844 * memory for the most part. Some dumb checks are included but perhaps
845 * are pointless since this all will break down at extreme limits that
846 * are currently not applicable to any supported device.
848 static void pcmrec_refresh_watermarks(void)
850 logf("ata spinup: %d", ata_spinup_time
);
852 /* set the low mark for when flushing stops if automatic */
853 low_watermark
= (LOW_SECONDS
*4*sample_rate
+ (enc_chunk_size
-1))
855 logf("low wmk: %d", low_watermark
);
857 #ifdef HAVE_PRIORITY_SCHEDULING
858 /* panic boost thread priority if 2 seconds of ground is lost -
859 this allows encoder to boost with just under a second of
860 pcm data (if not yet full enough to boost itself)
861 and not falsely trip the alarm. */
862 flood_watermark
= enc_num_chunks
-
863 (PANIC_SECONDS
*4*sample_rate
+ (enc_chunk_size
-1))
866 if (flood_watermark
< low_watermark
)
868 logf("warning: panic < low");
869 flood_watermark
= low_watermark
;
872 logf("flood at: %d", flood_watermark
);
874 spinup_time
= last_ata_spinup_time
= ata_spinup_time
;
876 /* write at 8s + st remaining in enc_buffer - range 12s to
877 20s total - default to 3.5s spinup. */
878 if (spinup_time
== 0)
879 spinup_time
= 35*HZ
/10; /* default - cozy */
880 else if (spinup_time
< 2*HZ
)
881 spinup_time
= 2*HZ
; /* ludicrous - ramdisk? */
882 else if (spinup_time
> 10*HZ
)
883 spinup_time
= 10*HZ
; /* do you have a functioning HD? */
885 /* try to start writing with 10s remaining after disk spinup */
886 high_watermark
= enc_num_chunks
-
887 ((FLUSH_SECONDS
*HZ
+ spinup_time
)*4*sample_rate
+
888 (enc_chunk_size
-1)*HZ
) / (enc_chunk_size
*HZ
);
890 if (high_watermark
< low_watermark
)
892 high_watermark
= low_watermark
;
894 logf("warning: low 'write at'");
897 logf("write at: %d", high_watermark
);
898 } /* pcmrec_refresh_watermarks */
903 * This function is called when queue_get_w_tmo times out.
905 * Set flush_num to the number of files to flush to disk or to
906 * a PCMREC_FLUSH_* constant.
908 static void pcmrec_flush(unsigned flush_num
)
910 #ifdef HAVE_PRIORITY_SCHEDULING
911 static unsigned long last_flush_tick
; /* tick when function returned */
912 unsigned long start_tick
; /* When flush started */
913 unsigned long prio_tick
; /* Timeout for auto boost */
914 int prio_pcmrec
; /* Current thread priority for pcmrec */
915 int prio_codec
; /* Current thread priority for codec */
917 int num_ready
; /* Number of chunks ready at start */
918 unsigned remaining
; /* Number of file starts remaining */
919 unsigned chunks_flushed
; /* Chunks flushed (for mini flush only) */
920 bool interruptable
; /* Flush can be interupted */
922 num_ready
= enc_wr_index
- enc_rd_index
;
924 num_ready
+= enc_num_chunks
;
926 /* save interruptable flag and remove it to get the actual count */
927 interruptable
= (flush_num
& PCMREC_FLUSH_INTERRUPTABLE
) != 0;
928 flush_num
&= ~PCMREC_FLUSH_INTERRUPTABLE
;
935 if (ata_spinup_time
!= last_ata_spinup_time
)
936 pcmrec_refresh_watermarks();
938 /* enough available? no? then leave */
939 if (num_ready
< high_watermark
)
941 } /* endif (flush_num == 0) */
943 #ifdef HAVE_PRIORITY_SCHEDULING
944 start_tick
= current_tick
;
945 prio_tick
= start_tick
+ PRIO_SECONDS
*HZ
+ spinup_time
;
947 if (flush_num
== 0 && TIME_BEFORE(current_tick
, last_flush_tick
+ HZ
/2))
949 /* if we're getting called too much and this isn't forced,
950 boost stat by expiring timeout in advance */
951 logf("too frequent flush");
952 prio_tick
= current_tick
- 1;
956 prio_codec
= -1; /* GCC is too stoopid to figure out it doesn't
960 logf("writing:%d(%d):%s%s", num_ready
, flush_num
,
961 interruptable
? "i" : "",
962 flush_num
== PCMREC_FLUSH_MINI
? "m" : "");
966 remaining
= flush_num
;
969 while (num_ready
> 0)
971 /* check current number of encoder chunks */
972 int num
= enc_wr_index
- enc_rd_index
;
974 num
+= enc_num_chunks
;
976 if (num
<= low_watermark
&&
977 (flush_num
== PCMREC_FLUSH_IF_HIGH
|| num
<= 0))
979 logf("low data: %d", num
);
980 break; /* data remaining is below threshold */
983 if (interruptable
&& flush_interrupts
> 0)
985 logf("int at: %d", num
);
986 break; /* interrupted */
989 #ifdef HAVE_PRIORITY_SCHEDULING
990 if (prio_pcmrec
== -1 && (num
>= flood_watermark
||
991 TIME_AFTER(current_tick
, prio_tick
)))
993 /* losing ground or holding without progress - boost
994 priority until finished */
995 logf("pcmrec: boost (%s)",
996 num
>= flood_watermark
? "num" : "time");
997 prio_pcmrec
= thread_set_priority(NULL
,
998 thread_get_priority(NULL
) - 1);
999 prio_codec
= thread_set_priority(codec_thread_p
,
1000 thread_get_priority(codec_thread_p
) - 1);
1004 rec_fdata
.chunk
= GET_ENC_CHUNK(enc_rd_index
);
1005 rec_fdata
.new_enc_size
= rec_fdata
.chunk
->enc_size
;
1006 rec_fdata
.new_num_pcm
= rec_fdata
.chunk
->num_pcm
;
1008 PARANOID_CHUNK_CHECK(rec_fdata
.chunk
);
1010 if (rec_fdata
.chunk
->flags
& CHUNKF_START_FILE
)
1012 pcmrec_start_file();
1013 if (--remaining
== 0)
1014 num_ready
= 0; /* stop on next loop - must write this
1015 chunk if it has data */
1018 pcmrec_write_chunk();
1020 if (rec_fdata
.chunk
->flags
& CHUNKF_END_FILE
)
1023 INC_ENC_INDEX(enc_rd_index
);
1028 if (flush_num
== PCMREC_FLUSH_MINI
&&
1029 ++chunks_flushed
>= MINI_CHUNKS
)
1031 logf("mini flush break");
1034 /* no yielding; the file apis called in the codecs do that
1039 if (rec_fdata
.rec_file
>= 0 && fsync(rec_fdata
.rec_file
) != 0)
1040 errors
|= PCMREC_E_IO
;
1044 #ifdef HAVE_PRIORITY_SCHEDULING
1045 if (prio_pcmrec
!= -1)
1047 /* return to original priorities */
1048 logf("pcmrec: unboost priority");
1049 thread_set_priority(NULL
, prio_pcmrec
);
1050 thread_set_priority(codec_thread_p
, prio_codec
);
1053 last_flush_tick
= current_tick
; /* save tick when we left */
1057 } /* pcmrec_flush */
1060 * Marks a new stream in the buffer and gives the encoder a chance for special
1061 * handling of transition from one to the next. The encoder may change the
1062 * chunk that ends the old stream by requesting more chunks and similiarly for
1063 * the new but must always advance the position though the interface. It can
1064 * later reject any data it cares to when writing the file but should mark the
1065 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
1066 * a NULL data pointer without error as well.
1068 static int pcmrec_get_chunk_index(struct enc_chunk_hdr
*chunk
)
1070 return ((char *)chunk
- (char *)enc_buffer
) / enc_chunk_size
;
1071 } /* pcmrec_get_chunk_index */
1073 static struct enc_chunk_hdr
* pcmrec_get_prev_chunk(int index
)
1075 #ifdef PCMREC_PARANOID
1076 int index_last
= index
;
1078 DEC_ENC_INDEX(index
);
1079 return GET_ENC_CHUNK(index
);
1080 } /* pcmrec_get_prev_chunk */
1082 static void pcmrec_new_stream(const char *filename
, /* next file name */
1083 unsigned long flags
, /* CHUNKF_* flags */
1084 int pre_index
) /* index for prerecorded data */
1086 logf("pcmrec_new_stream");
1087 char path
[MAX_PATH
]; /* place to copy filename so sender can be released */
1089 struct enc_buffer_event_data data
;
1090 bool (*fnq_add_fn
)(const char *) = NULL
; /* function to use to add
1092 struct enc_chunk_hdr
*start
= NULL
; /* pointer to starting chunk of
1094 bool did_flush
= false; /* did a flush occurr? */
1097 strncpy(path
, filename
, MAX_PATH
);
1098 queue_reply(&pcmrec_queue
, 0); /* We have all we need */
1100 data
.pre_chunk
= NULL
;
1101 data
.chunk
= GET_ENC_CHUNK(enc_wr_index
);
1104 if (flags
& CHUNKF_END_FILE
)
1106 data
.chunk
->flags
&= CHUNKF_START_FILE
| CHUNKF_END_FILE
;
1108 if (data
.chunk
->flags
& CHUNKF_START_FILE
)
1110 /* cannot start and end on same unprocessed chunk */
1111 logf("file end on start");
1112 flags
&= ~CHUNKF_END_FILE
;
1114 else if (enc_rd_index
== enc_wr_index
)
1116 /* all data flushed but file not ended - chunk will be left
1118 logf("end on dead end");
1119 data
.chunk
->flags
= 0;
1120 data
.chunk
->enc_size
= 0;
1121 data
.chunk
->num_pcm
= 0;
1122 data
.chunk
->enc_data
= NULL
;
1123 INC_ENC_INDEX(enc_wr_index
);
1124 data
.chunk
= GET_ENC_CHUNK(enc_wr_index
);
1128 struct enc_chunk_hdr
*last
= pcmrec_get_prev_chunk(enc_wr_index
);
1130 if (last
->flags
& CHUNKF_END_FILE
)
1132 /* end already processed and marked - can't end twice */
1133 logf("file end again");
1134 flags
&= ~CHUNKF_END_FILE
;
1140 if (flags
& CHUNKF_START_FILE
)
1142 bool pre
= flags
& CHUNKF_PRERECORD
;
1146 logf("stream prerecord start");
1147 start
= data
.pre_chunk
= GET_ENC_CHUNK(pre_index
);
1148 start
->flags
&= CHUNKF_START_FILE
| CHUNKF_PRERECORD
;
1152 logf("stream normal start");
1154 start
->flags
&= CHUNKF_START_FILE
;
1157 /* if encoder hasn't yet processed the last start - abort the start
1158 of the previous file queued or else it will be empty and invalid */
1159 if (start
->flags
& CHUNKF_START_FILE
)
1161 logf("replacing fnq tail: %s", filename
);
1162 fnq_add_fn
= pcmrec_fnq_replace_tail
;
1166 logf("adding filename: %s", filename
);
1167 fnq_add_fn
= pcmrec_fnq_add_filename
;
1172 pcmrec_context
= true; /* switch encoder context */
1173 enc_events_callback(ENC_REC_NEW_STREAM
, &data
);
1174 pcmrec_context
= false; /* switch back */
1176 if (flags
& CHUNKF_END_FILE
)
1178 int i
= pcmrec_get_chunk_index(data
.chunk
);
1179 pcmrec_get_prev_chunk(i
)->flags
|= CHUNKF_END_FILE
;
1184 if (!(flags
& CHUNKF_PRERECORD
))
1186 /* get stats on data added to start - sort of a prerecord
1188 int i
= pcmrec_get_chunk_index(data
.chunk
);
1189 #ifdef PCMREC_PARANOID
1192 struct enc_chunk_hdr
*chunk
= data
.chunk
;
1194 logf("start data: %d %d", i
, enc_wr_index
);
1197 num_rec_samples
= 0;
1199 while (i
!= enc_wr_index
)
1201 num_rec_bytes
+= chunk
->enc_size
;
1202 num_rec_samples
+= chunk
->num_pcm
;
1204 chunk
= GET_ENC_CHUNK(i
);
1207 start
->flags
&= ~CHUNKF_START_FILE
;
1211 start
->flags
|= CHUNKF_START_FILE
;
1213 /* flush all pending files out if full and adding */
1214 if (fnq_add_fn
== pcmrec_fnq_add_filename
&& pcmrec_fnq_is_full())
1217 pcmrec_flush(PCMREC_FLUSH_ALL
);
1224 /* Make sure to complete any interrupted high watermark */
1226 pcmrec_flush(PCMREC_FLUSH_IF_HIGH
);
1227 } /* pcmrec_new_stream */
1229 /** event handlers for pcmrec thread */
1232 static void pcmrec_init(void)
1234 unsigned char *buffer
;
1236 /* warings and errors */
1240 pcmrec_close_file(&rec_fdata
.rec_file
);
1241 rec_fdata
.rec_file
= -1;
1245 SET_PCM_POS(pcm_rd_pos
, 0);
1246 SET_PCM_POS(dma_wr_pos
, 0);
1250 SET_ENC_INDEX(enc_wr_index
, 0);
1251 SET_ENC_INDEX(enc_rd_index
, 0);
1253 /* filename queue */
1259 num_rec_samples
= 0;
1261 accum_rec_bytes
= 0;
1262 accum_pcm_samples
= 0;
1265 pre_record_ticks
= 0;
1267 is_recording
= false;
1270 buffer
= audio_get_recording_buffer(&rec_buffer_size
);
1272 /* Line align pcm_buffer 2^4=16 bytes */
1273 pcm_buffer
= (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer
, 4);
1274 enc_buffer
= pcm_buffer
+ ALIGN_UP_P2(PCM_NUM_CHUNKS
*PCM_CHUNK_SIZE
+
1275 PCM_MAX_FEED_SIZE
, 2);
1276 /* Adjust available buffer for possible align advancement */
1277 rec_buffer_size
-= pcm_buffer
- buffer
;
1279 pcm_init_recording();
1283 static void pcmrec_close(void)
1286 pre_record_ticks
= 0; /* Can't be prerecording any more */
1288 pcm_close_recording();
1290 audio_remove_encoder();
1291 } /* pcmrec_close */
1293 /* PCMREC_OPTIONS */
1294 static void pcmrec_set_recording_options(
1295 struct audio_recording_options
*options
)
1297 /* stop DMA transfer */
1299 pcm_stop_recording();
1301 rec_frequency
= options
->rec_frequency
;
1302 rec_source
= options
->rec_source
;
1303 num_channels
= options
->rec_channels
== 1 ? 1 : 2;
1304 pre_record_ticks
= options
->rec_prerecord_time
* HZ
;
1305 enc_config
= options
->enc_config
;
1306 enc_config
.afmt
= rec_format_afmt
[enc_config
.rec_format
];
1308 #ifdef HAVE_SPDIF_IN
1309 if (rec_source
== AUDIO_SRC_SPDIF
)
1311 /* must measure SPDIF sample rate before configuring codecs */
1312 unsigned long sr
= spdif_measure_frequency();
1313 /* round to master list for SPDIF rate */
1314 int index
= round_value_to_list32(sr
, audio_master_sampr_list
,
1315 SAMPR_NUM_FREQ
, false);
1316 sample_rate
= audio_master_sampr_list
[index
];
1317 /* round to HW playback rates for monitoring */
1318 index
= round_value_to_list32(sr
, hw_freq_sampr
,
1319 HW_NUM_FREQ
, false);
1320 pcm_set_frequency(hw_freq_sampr
[index
]);
1321 /* encoders with a limited number of rates do their own rounding */
1326 /* set sample rate from frequency selection */
1327 sample_rate
= rec_freq_sampr
[rec_frequency
];
1328 pcm_set_frequency(sample_rate
);
1331 /* set monitoring */
1332 audio_set_output_source(rec_source
);
1334 /* apply hardware setting to start monitoring now */
1335 pcm_apply_settings();
1337 queue_reply(&pcmrec_queue
, 0); /* Release sender */
1339 if (audio_load_encoder(enc_config
.afmt
))
1341 /* start DMA transfer */
1342 dma_lock
= pre_record_ticks
== 0;
1343 pcm_record_data(pcm_rec_have_more
, GET_PCM_CHUNK(dma_wr_pos
),
1348 logf("set rec opt: enc load failed");
1349 errors
|= PCMREC_E_LOAD_ENCODER
;
1351 } /* pcmrec_set_recording_options */
1353 /* PCMREC_RECORD - start recording (not gapless)
1354 or split stream (gapless) */
1355 static void pcmrec_record(const char *filename
)
1357 unsigned long pre_sample_ticks
;
1359 unsigned long flags
;
1362 logf("pcmrec_record: %s", filename
);
1366 num_rec_samples
= 0;
1371 accum_rec_bytes
= 0;
1372 accum_pcm_samples
= 0;
1374 warnings
= 0; /* reset warnings */
1376 rd_start
= enc_wr_index
;
1377 pre_sample_ticks
= 0;
1379 pcmrec_refresh_watermarks();
1381 if (pre_record_ticks
)
1384 #ifdef PCMREC_PARANOID
1387 /* calculate number of available chunks */
1388 unsigned long avail_pre_chunks
= (enc_wr_index
- enc_rd_index
+
1389 enc_num_chunks
) % enc_num_chunks
;
1390 /* overflow at 974 seconds of prerecording at 44.1kHz */
1391 unsigned long pre_record_sample_ticks
=
1392 enc_sample_rate
*pre_record_ticks
;
1393 int pre_chunks
= 0; /* Counter to limit prerecorded time to
1394 prevent flood state at outset */
1396 logf("pre-st: %ld", pre_record_sample_ticks
);
1398 /* Get exact measure of recorded data as number of samples aren't
1399 nescessarily going to be the max for each chunk */
1400 for (; avail_pre_chunks
-- > 0;)
1402 struct enc_chunk_hdr
*chunk
;
1403 unsigned long chunk_sample_ticks
;
1407 chunk
= GET_ENC_CHUNK(i
);
1409 /* must have data to be counted */
1410 if (chunk
->enc_data
== NULL
)
1413 chunk_sample_ticks
= chunk
->num_pcm
*HZ
;
1416 pre_sample_ticks
+= chunk_sample_ticks
;
1417 num_rec_bytes
+= chunk
->enc_size
;
1418 num_rec_samples
+= chunk
->num_pcm
;
1421 /* stop here if enough already */
1422 if (pre_chunks
>= high_watermark
||
1423 pre_sample_ticks
>= pre_record_sample_ticks
)
1425 logf("pre-chks: %d", pre_chunks
);
1431 accum_rec_bytes
= num_rec_bytes
;
1432 accum_pcm_samples
= num_rec_samples
;
1436 SET_ENC_INDEX(enc_rd_index
, rd_start
);
1438 /* filename queue should be empty */
1439 if (!pcmrec_fnq_is_empty())
1441 logf("fnq: not empty!");
1442 pcmrec_fnq_set_empty();
1445 flags
= CHUNKF_START_FILE
;
1446 if (pre_sample_ticks
> 0)
1447 flags
|= CHUNKF_PRERECORD
;
1449 pre_index
= enc_rd_index
;
1453 is_recording
= true;
1457 /* already recording, just split the stream */
1458 logf("inserting split");
1459 flags
= CHUNKF_START_FILE
| CHUNKF_END_FILE
;
1463 pcmrec_new_stream(filename
, flags
, pre_index
);
1464 logf("pcmrec_record done");
1465 } /* pcmrec_record */
1468 static void pcmrec_stop(void)
1470 logf("pcmrec_stop");
1474 dma_lock
= true; /* lock dma write position */
1475 queue_reply(&pcmrec_queue
, 0);
1477 /* flush all available data first to avoid overflow while waiting
1478 for encoding to finish */
1479 pcmrec_flush(PCMREC_FLUSH_ALL
);
1481 /* wait for encoder to finish remaining data */
1482 while (errors
== 0 && !pcm_buffer_empty
)
1485 /* end stream at last data */
1486 pcmrec_new_stream(NULL
, CHUNKF_END_FILE
, 0);
1488 /* flush anything else encoder added */
1489 pcmrec_flush(PCMREC_FLUSH_ALL
);
1491 /* remove any pending file start not yet processed - should be at
1492 most one at enc_wr_index */
1493 pcmrec_fnq_get_filename(NULL
);
1494 /* encoder should abort any chunk it was in midst of processing */
1495 GET_ENC_CHUNK(enc_wr_index
)->flags
= CHUNKF_ABORT
;
1497 /* filename queue should be empty */
1498 if (!pcmrec_fnq_is_empty())
1500 logf("fnq: not empty!");
1501 pcmrec_fnq_set_empty();
1504 /* be absolutely sure the file is closed */
1506 pcmrec_close_file(&rec_fdata
.rec_file
);
1507 rec_fdata
.rec_file
= -1;
1509 is_recording
= false;
1511 dma_lock
= pre_record_ticks
== 0;
1515 logf("not recording");
1518 logf("pcmrec_stop done");
1522 static void pcmrec_pause(void)
1524 logf("pcmrec_pause");
1528 logf("not recording");
1532 logf("already paused");
1540 logf("pcmrec_pause done");
1541 } /* pcmrec_pause */
1544 static void pcmrec_resume(void)
1546 logf("pcmrec_resume");
1550 logf("not recording");
1552 else if (!is_paused
)
1559 is_recording
= true;
1563 logf("pcmrec_resume done");
1564 } /* pcmrec_resume */
1566 static void pcmrec_thread(void) __attribute__((noreturn
));
1567 static void pcmrec_thread(void)
1571 logf("thread pcmrec start");
1577 /* Poll periodically to flush data */
1578 queue_wait_w_tmo(&pcmrec_queue
, &ev
, HZ
/5);
1580 if (ev
.id
== SYS_TIMEOUT
)
1582 /* Messages that interrupt this will complete it */
1583 pcmrec_flush(PCMREC_FLUSH_IF_HIGH
|
1584 PCMREC_FLUSH_INTERRUPTABLE
);
1590 /* Not doing anything - sit and wait for commands */
1591 queue_wait(&pcmrec_queue
, &ev
);
1604 case PCMREC_OPTIONS
:
1605 pcmrec_set_recording_options(
1606 (struct audio_recording_options
*)ev
.data
);
1610 clear_flush_interrupt();
1611 pcmrec_record((const char *)ev
.data
);
1615 clear_flush_interrupt();
1620 clear_flush_interrupt();
1628 case PCMREC_FLUSH_NUM
:
1629 pcmrec_flush((unsigned)ev
.data
);
1632 case SYS_USB_CONNECTED
:
1637 /* Be sure other threads are released if waiting */
1638 queue_clear(&pcmrec_queue
);
1639 flush_interrupts
= 0;
1640 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1641 usb_wait_for_disconnect(&pcmrec_queue
);
1645 queue_reply(&pcmrec_queue
, 0);
1647 } /* pcmrec_thread */
1649 /****************************************************************************/
1651 /* following functions will be called by the encoder codec */
1652 /* in a free-threaded manner */
1654 /****************************************************************************/
1656 /* pass the encoder settings to the encoder */
1657 void enc_get_inputs(struct enc_inputs
*inputs
)
1659 inputs
->sample_rate
= sample_rate
;
1660 inputs
->num_channels
= num_channels
;
1661 inputs
->config
= &enc_config
;
1662 } /* enc_get_inputs */
1664 /* set the encoder dimensions (called by encoder codec at initialization and
1666 void enc_set_parameters(struct enc_parameters
*params
)
1668 size_t bufsize
, resbytes
;
1670 logf("enc_set_parameters");
1675 /* Encoder is terminating */
1676 memset(&enc_config
, 0, sizeof (enc_config
));
1677 enc_sample_rate
= 0;
1681 enc_sample_rate
= params
->enc_sample_rate
;
1682 logf("enc sampr:%lu", enc_sample_rate
);
1684 SET_PCM_POS(pcm_rd_pos
, dma_wr_pos
);
1685 pcm_enc_pos
= pcm_rd_pos
;
1687 enc_config
.afmt
= params
->afmt
;
1688 /* addition of the header is always implied - chunk size 4-byte aligned */
1690 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE
+ params
->chunk_size
, 2);
1691 enc_events_callback
= params
->events_callback
;
1693 logf("chunk size:%lu", enc_chunk_size
);
1695 /*** Configure the buffers ***/
1697 /* Layout of recording buffer:
1698 * [ax] = possible alignment x multiple
1699 * [sx] = possible size alignment of x multiple
1700 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1701 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1703 resbytes
= ALIGN_UP_P2(params
->reserve_bytes
, 2);
1704 logf("resbytes:%lu", resbytes
);
1706 bufsize
= rec_buffer_size
- (enc_buffer
- pcm_buffer
) -
1707 resbytes
- FNQ_MIN_NUM_PATHS
*MAX_PATH
1708 #ifdef PCMREC_PARANOID
1709 - sizeof (*wrap_id_p
)
1713 enc_num_chunks
= bufsize
/ enc_chunk_size
;
1714 logf("num chunks:%d", enc_num_chunks
);
1716 /* get real amount used by encoder chunks */
1717 bufsize
= enc_num_chunks
*enc_chunk_size
;
1718 logf("enc size:%lu", bufsize
);
1720 #ifdef PCMREC_PARANOID
1721 /* add magic at wraparound */
1722 wrap_id_p
= SKIPBYTES((unsigned long *)enc_buffer
, bufsize
);
1723 bufsize
+= sizeof (*wrap_id_p
);
1724 *wrap_id_p
= ENC_CHUNK_MAGIC
;
1725 #endif /* PCMREC_PARANOID */
1727 /** set OUT parameters **/
1728 params
->enc_buffer
= enc_buffer
;
1729 params
->buf_chunk_size
= enc_chunk_size
;
1730 params
->num_chunks
= enc_num_chunks
;
1732 /* calculate reserve buffer start and return pointer to encoder */
1733 params
->reserve_buffer
= NULL
;
1736 params
->reserve_buffer
= enc_buffer
+ bufsize
;
1737 bufsize
+= resbytes
;
1740 /* place filename queue at end of buffer using up whatever remains */
1741 fnq_rd_pos
= 0; /* reset */
1742 fnq_wr_pos
= 0; /* reset */
1743 fn_queue
= enc_buffer
+ bufsize
;
1744 fnq_size
= pcm_buffer
+ rec_buffer_size
- fn_queue
;
1745 fnq_size
/= MAX_PATH
;
1746 if (fnq_size
> FNQ_MAX_NUM_PATHS
)
1747 fnq_size
= FNQ_MAX_NUM_PATHS
;
1748 fnq_size
*= MAX_PATH
;
1749 logf("fnq files:%ld", fnq_size
/ MAX_PATH
);
1752 logf("ab :%08lX", (uintptr_t)audiobuf
);
1753 logf("pcm:%08lX", (uintptr_t)pcm_buffer
);
1754 logf("enc:%08lX", (uintptr_t)enc_buffer
);
1755 logf("res:%08lX", (uintptr_t)params
->reserve_buffer
);
1756 #ifdef PCMREC_PARANOID
1757 logf("wip:%08lX", (uintptr_t)wrap_id_p
);
1759 logf("fnq:%08lX", (uintptr_t)fn_queue
);
1760 logf("end:%08lX", (uintptr_t)fn_queue
+ fnq_size
);
1761 logf("abe:%08lX", (uintptr_t)audiobufend
);
1764 /* init all chunk headers and reset indexes */
1765 SET_ENC_INDEX(enc_rd_index
, 0);
1766 for (enc_wr_index
= enc_num_chunks
; enc_wr_index
> 0; )
1768 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(--enc_wr_index
);
1769 #ifdef PCMREC_PARANOID
1770 chunk
->id
= ENC_CHUNK_MAGIC
;
1775 logf("enc_set_parameters done");
1776 } /* enc_set_parameters */
1778 /* return encoder chunk at current write position -
1779 NOTE: can be called by pcmrec thread when splitting streams */
1780 struct enc_chunk_hdr
* enc_get_chunk(void)
1782 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(enc_wr_index
);
1784 #ifdef PCMREC_PARANOID
1785 if (chunk
->id
!= ENC_CHUNK_MAGIC
|| *wrap_id_p
!= ENC_CHUNK_MAGIC
)
1787 errors
|= PCMREC_E_CHUNK_OVF
;
1788 logf("finish chk ovf: %d", enc_wr_index
);
1792 chunk
->flags
&= CHUNKF_START_FILE
;
1795 chunk
->flags
|= CHUNKF_PRERECORD
;
1798 } /* enc_get_chunk */
1800 /* releases the current chunk into the available chunks -
1801 NOTE: can be called by pcmrec thread when splitting streams */
1802 void enc_finish_chunk(void)
1804 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(enc_wr_index
);
1806 if ((long)chunk
->flags
< 0)
1808 /* encoder set error flag */
1809 errors
|= PCMREC_E_ENCODER
;
1810 logf("finish chk enc error");
1813 PARANOID_CHUNK_CHECK(chunk
);
1815 /* advance enc_wr_index to the next encoder chunk */
1816 INC_ENC_INDEX(enc_wr_index
);
1818 if (enc_rd_index
!= enc_wr_index
)
1820 num_rec_bytes
+= chunk
->enc_size
;
1821 num_rec_samples
+= chunk
->num_pcm
;
1823 accum_rec_bytes
+= chunk
->enc_size
;
1824 accum_pcm_samples
+= chunk
->num_pcm
;
1827 else if (is_recording
) /* buffer full */
1829 /* keep current position and put up warning flag */
1830 warnings
|= PCMREC_W_ENC_BUFFER_OVF
;
1831 logf("enc_buffer ovf");
1832 DEC_ENC_INDEX(enc_wr_index
);
1835 /* if stream splitting, keep this out of circulation and
1836 flush a small number, then readd - cannot risk losing
1839 pcmrec_flush(PCMREC_FLUSH_MINI
);
1840 INC_ENC_INDEX(enc_wr_index
);
1845 /* advance enc_rd_index for prerecording */
1846 INC_ENC_INDEX(enc_rd_index
);
1848 } /* enc_finish_chunk */
1850 /* checks near empty state on pcm input buffer */
1851 int enc_pcm_buf_near_empty(void)
1853 /* less than 1sec raw data? => unboost encoder */
1854 int wp
= dma_wr_pos
;
1855 size_t avail
= (wp
- pcm_rd_pos
) & PCM_CHUNK_MASK
;
1856 return avail
< (sample_rate
<< 2) ? 1 : 0;
1857 } /* enc_pcm_buf_near_empty */
1859 /* passes a pointer to next chunk of unprocessed wav data */
1860 /* TODO: this really should give the actual size returned */
1861 unsigned char * enc_get_pcm_data(size_t size
)
1863 int wp
= dma_wr_pos
;
1864 size_t avail
= (wp
- pcm_rd_pos
) & PCM_CHUNK_MASK
;
1866 /* limit the requested pcm data size */
1867 if (size
> PCM_MAX_FEED_SIZE
)
1868 size
= PCM_MAX_FEED_SIZE
;
1872 unsigned char *ptr
= pcm_buffer
+ pcm_rd_pos
;
1873 int next_pos
= (pcm_rd_pos
+ size
) & PCM_CHUNK_MASK
;
1875 pcm_enc_pos
= pcm_rd_pos
;
1877 SET_PCM_POS(pcm_rd_pos
, next_pos
);
1879 /* ptr must point to continous data at wraparound position */
1880 if ((size_t)pcm_rd_pos
< size
)
1881 memcpy(pcm_buffer
+ PCM_NUM_CHUNKS
*PCM_CHUNK_SIZE
,
1882 pcm_buffer
, pcm_rd_pos
);
1884 pcm_buffer_empty
= false;
1888 /* not enough data available - encoder should idle */
1889 pcm_buffer_empty
= true;
1891 } /* enc_get_pcm_data */
1893 /* puts some pcm data back in the queue */
1894 size_t enc_unget_pcm_data(size_t size
)
1896 int wp
= dma_wr_pos
;
1897 size_t old_avail
= ((pcm_rd_pos
- wp
) & PCM_CHUNK_MASK
) -
1900 /* allow one interrupt to occur during this call and not have the
1901 new read position inside the DMA destination chunk */
1902 if ((ssize_t
)old_avail
> 0)
1904 /* limit size to amount of old data remaining */
1905 if (size
> old_avail
)
1908 pcm_enc_pos
= (pcm_rd_pos
- size
) & PCM_CHUNK_MASK
;
1909 SET_PCM_POS(pcm_rd_pos
, pcm_enc_pos
);
1915 } /* enc_unget_pcm_data */
1917 /** Low level pcm recording apis **/
1919 /****************************************************************************
1920 * Functions that do not require targeted implementation but only a targeted
1923 void pcm_record_data(pcm_more_callback_type2 more_ready
,
1924 void *start
, size_t size
)
1926 if (!(start
&& size
))
1929 pcm_callback_more_ready
= more_ready
;
1930 pcm_rec_dma_start(start
, size
);
1931 } /* pcm_record_data */
1933 void pcm_stop_recording(void)
1937 } /* pcm_stop_recording */