1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "gcc_extensions.h"
24 #include "pcm_record.h"
29 #include "string-extra.h"
34 #include "codec_thread.h"
38 #include "appevents.h"
43 /***************************************************************************/
45 /** General recording state **/
46 static bool is_recording
; /* We are recording */
47 static bool is_paused
; /* We have paused */
48 static unsigned long errors
; /* An error has occured */
49 static unsigned long warnings
; /* Warning */
50 static int flush_interrupts
= 0; /* Number of messages queued that
51 should interrupt a flush in
53 for a safety net and a prompt
54 response to stop, split and pause
56 only interrupts a flush initiated
59 /* Utility functions for setting/clearing flushing interrupt flag */
60 static inline void flush_interrupt(void)
63 logf("flush int: %d", flush_interrupts
);
66 static inline void clear_flush_interrupt(void)
68 if (--flush_interrupts
< 0)
72 /** Stats on encoded data for current file **/
73 static size_t num_rec_bytes
; /* Num bytes recorded */
74 static unsigned long num_rec_samples
; /* Number of PCM samples recorded */
76 /** Stats on encoded data for all files from start to stop **/
78 static unsigned long long accum_rec_bytes
; /* total size written to chunks */
79 static unsigned long long accum_pcm_samples
; /* total pcm count processed */
82 /* Keeps data about current file and is sent as event data for codec */
83 static struct enc_file_event_data rec_fdata IDATA_ATTR
=
92 /** These apply to current settings **/
93 static int rec_source
; /* current rec_source setting */
94 static int rec_frequency
; /* current frequency setting */
95 static unsigned long sample_rate
; /* Sample rate in HZ */
96 static int num_channels
; /* Current number of channels */
97 static int rec_mono_mode
; /* how mono is created */
98 static struct encoder_config enc_config
; /* Current encoder configuration */
99 static unsigned long pre_record_ticks
; /* pre-record time in ticks */
101 /****************************************************************************
102 use 2 circular buffers:
103 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
104 enc_buffer=encoded audio buffer: storage for encoder output data
107 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
108 2. if enough pcm data are available the encoder codec does encoding of pcm
109 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
110 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
112 Functions calls (basic encoder steps):
113 1.main: audio_load_encoder(); start the encoder
114 2.encoder: enc_get_inputs(); get encoder recording settings
115 3.encoder: enc_set_parameters(); set the encoder parameters
116 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
117 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
118 6.encoder: enc_get_chunk(); get a ptr to next enc chunk
119 7.encoder: <process enc chunk> compress and store data to enc chunk
120 8.encoder: enc_finish_chunk(); inform main about chunk processed and
121 is available to be written to a file.
122 Encoder can place any number of chunks
123 of PCM data in a single output chunk
124 but must stay within its output chunk
126 9.encoder: repeat 4. to 8.
127 A.pcmrec: enc_events_callback(); called for certain events
130 ****************************************************************************/
132 /** buffer parameters where incoming PCM data is placed **/
134 #define PCM_NUM_CHUNKS 16 /* Power of 2 */
136 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
138 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
139 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
141 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
142 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
143 #define INC_ENC_INDEX(index) \
144 { if (++index >= enc_num_chunks) index = 0; }
145 #define DEC_ENC_INDEX(index) \
146 { if (--index < 0) index = enc_num_chunks - 1; }
148 static size_t rec_buffer_size
; /* size of available buffer */
149 static unsigned char *pcm_buffer
; /* circular recording buffer */
150 static unsigned char *enc_buffer
; /* circular encoding buffer */
152 static unsigned long *wrap_id_p
; /* magic at wrap position - a debugging
153 aid to check if the encoder data
154 spilled out of its chunk */
156 static volatile int dma_wr_pos
; /* current DMA write pos */
157 static int pcm_rd_pos
; /* current PCM read pos */
158 static int pcm_enc_pos
; /* position encoder is processing */
159 static volatile bool dma_lock
; /* lock DMA write position */
160 static int enc_wr_index
; /* encoder chunk write index */
161 static int enc_rd_index
; /* encoder chunk read index */
162 static int enc_num_chunks
; /* number of chunks in ringbuffer */
163 static size_t enc_chunk_size
; /* maximum encoder chunk size */
164 static unsigned long enc_sample_rate
; /* sample rate used by encoder */
165 static bool pcmrec_context
= false; /* called by pcmrec thread? */
166 static bool pcm_buffer_empty
; /* all pcm chunks processed? */
168 /** file flushing **/
169 static int low_watermark
; /* Low watermark to stop flush */
170 static int high_watermark
; /* max chunk limit for data flush */
171 static unsigned long spinup_time
= 35*HZ
/10; /* Fudged spinup time */
172 static int last_storage_spinup_time
= -1;/* previous spin time used */
173 #ifdef HAVE_PRIORITY_SCHEDULING
174 static int flood_watermark
; /* boost thread priority when here */
177 /* Constants that control watermarks */
178 #define MINI_CHUNKS 10 /* chunk count for mini flush */
179 #ifdef HAVE_PRIORITY_SCHEDULING
180 #define PRIO_SECONDS 10 /* max flush time before priority boost */
183 /* fractions must be integer fractions of 4 because they are evaluated with
184 * X*4*XXX_SECONDS, that way we avoid float calculation */
185 #define LOW_SECONDS 1/4 /* low watermark time till empty */
186 #define PANIC_SECONDS 1/2 /* flood watermark time until full */
187 #define FLUSH_SECONDS 1 /* flush watermark time until full */
188 #elif MEMORYSIZE <= 16
189 #define LOW_SECONDS 1 /* low watermark time till empty */
190 #define PANIC_SECONDS 5 /* flood watermark time until full */
191 #define FLUSH_SECONDS 7 /* flush watermark time until full */
193 #define LOW_SECONDS 1 /* low watermark time till empty */
194 #define PANIC_SECONDS 8
195 #define FLUSH_SECONDS 10
196 #endif /* MEMORYSIZE */
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; \
213 #define FNQ_PREV(pos) \
214 ({ int p = (pos) - MAX_PATH; \
216 p = fnq_size - MAX_PATH; \
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
227 PCMREC_FLUSH_IF_HIGH
= 0x0000000, /* Flush if high watermark
231 /***************************************************************************/
233 static struct event_queue pcmrec_queue SHAREDBSS_ATTR
;
234 static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR
;
235 static long pcmrec_stack
[3*DEFAULT_STACK_SIZE
/sizeof(long)];
236 static const char pcmrec_thread_name
[] = "pcmrec";
237 static unsigned int pcmrec_thread_id
= 0;
239 static void pcmrec_thread(void);
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 */
252 PCMREC_FLUSH_NUM
, /* flush a number of files out */
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 void pcm_rec_have_more(int status
, void **start
, size_t *size
)
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);
272 /* else try again next transmission - frame is invalid */
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
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 *start
= GET_PCM_CHUNK(dma_wr_pos
);
288 *size
= PCM_CHUNK_SIZE
;
289 } /* pcm_rec_have_more */
291 static void reset_hardware(void)
293 /* reset pcm to defaults */
294 pcm_set_frequency(REC_SAMPR_DEFAULT
| SAMPR_TYPE_REC
);
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;
317 ret
|= AUDIO_STATUS_RECORD
;
318 else if (pre_record_ticks
)
319 ret
|= AUDIO_STATUS_PRERECORD
;
322 ret
|= AUDIO_STATUS_PAUSE
;
325 ret
|= AUDIO_STATUS_ERROR
;
328 ret
|= AUDIO_STATUS_WARNING
;
331 } /* pcm_rec_status */
334 * Return warnings that have occured since recording started
336 unsigned long pcm_rec_get_warnings(void)
342 int pcm_rec_current_bitrate(void)
344 if (accum_pcm_samples
== 0)
347 return (int)(8*accum_rec_bytes
*enc_sample_rate
/ (1000*accum_pcm_samples
));
348 } /* pcm_rec_current_bitrate */
352 int pcm_rec_encoder_afmt(void)
354 return enc_config
.afmt
;
355 } /* pcm_rec_encoder_afmt */
359 int pcm_rec_rec_format(void)
361 return afmt_rec_format
[enc_config
.afmt
];
362 } /* pcm_rec_rec_format */
366 unsigned long pcm_rec_sample_rate(void)
368 /* Which is better ?? */
370 return enc_sample_rate
;
373 } /* audio_get_sample_rate */
377 * Creates pcmrec_thread
379 void pcm_rec_init(void)
381 queue_init(&pcmrec_queue
, true);
383 create_thread(pcmrec_thread
, pcmrec_stack
, sizeof(pcmrec_stack
),
384 0, pcmrec_thread_name
IF_PRIO(, PRIORITY_RECORDING
)
386 queue_enable_queue_send(&pcmrec_queue
, &pcmrec_queue_send
,
390 /** audio_* group **/
393 * Initializes recording - call before calling any other recording function
395 void audio_init_recording(unsigned int buffer_offset
)
397 logf("audio_init_recording");
398 queue_send(&pcmrec_queue
, PCMREC_INIT
, 0);
399 logf("audio_init_recording done");
401 } /* audio_init_recording */
404 * Closes recording - call audio_stop_recording first
406 void audio_close_recording(void)
408 logf("audio_close_recording");
409 queue_send(&pcmrec_queue
, PCMREC_CLOSE
, 0);
410 logf("audio_close_recording done");
411 } /* audio_close_recording */
414 * Sets recording parameters
416 void audio_set_recording_options(struct audio_recording_options
*options
)
418 logf("audio_set_recording_options");
419 queue_send(&pcmrec_queue
, PCMREC_OPTIONS
, (intptr_t)options
);
420 logf("audio_set_recording_options done");
421 } /* audio_set_recording_options */
424 * Start recording if not recording or else split
426 void audio_record(const char *filename
)
428 logf("audio_record: %s", filename
);
430 queue_send(&pcmrec_queue
, PCMREC_RECORD
, (intptr_t)filename
);
431 logf("audio_record_done");
435 * audio_record wrapper for API compatibility with HW codec
437 void audio_new_file(const char *filename
)
439 audio_record(filename
);
440 } /* audio_new_file */
443 * Stop current recording if recording
445 void audio_stop_recording(void)
447 logf("audio_stop_recording");
449 queue_post(&pcmrec_queue
, PCMREC_STOP
, 0);
450 logf("audio_stop_recording done");
451 } /* audio_stop_recording */
454 * Pause current recording
456 void audio_pause_recording(void)
458 logf("audio_pause_recording");
460 queue_post(&pcmrec_queue
, PCMREC_PAUSE
, 0);
461 logf("audio_pause_recording done");
462 } /* audio_pause_recording */
465 * Resume current recording if paused
467 void audio_resume_recording(void)
469 logf("audio_resume_recording");
470 queue_post(&pcmrec_queue
, PCMREC_RESUME
, 0);
471 logf("audio_resume_recording done");
472 } /* audio_resume_recording */
475 * Note that microphone is mono, only left value is used
476 * See audiohw_set_recvol() for exact ranges.
478 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
481 void audio_set_recording_gain(int left
, int right
, int type
)
483 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
484 audiohw_set_recvol(left
, right
, type
);
485 } /* audio_set_recording_gain */
487 /** Information about current state **/
490 * Return current recorded time in ticks (playback eqivalent time)
492 unsigned long audio_recorded_time(void)
494 if (!is_recording
|| enc_sample_rate
== 0)
497 /* return actual recorded time a la encoded data even if encoder rate
498 doesn't match the pcm rate */
499 return (long)(HZ
*(unsigned long long)num_rec_samples
/ enc_sample_rate
);
500 } /* audio_recorded_time */
503 * Return number of bytes encoded to output
505 unsigned long audio_num_recorded_bytes(void)
510 return num_rec_bytes
;
511 } /* audio_num_recorded_bytes */
513 /***************************************************************************/
515 /* Functions that execute in the context of pcmrec_thread */
517 /***************************************************************************/
519 /** Filename Queue **/
521 /* returns true if the queue is empty */
522 static inline bool pcmrec_fnq_is_empty(void)
524 return fnq_rd_pos
== fnq_wr_pos
;
525 } /* pcmrec_fnq_is_empty */
527 /* empties the filename queue */
528 static inline void pcmrec_fnq_set_empty(void)
530 fnq_rd_pos
= fnq_wr_pos
;
531 } /* pcmrec_fnq_set_empty */
533 /* returns true if the queue is full */
534 static bool pcmrec_fnq_is_full(void)
536 ssize_t size
= fnq_wr_pos
- fnq_rd_pos
;
540 return size
>= fnq_size
- MAX_PATH
;
541 } /* pcmrec_fnq_is_full */
543 /* queue another filename - will overwrite oldest one if full */
544 static bool pcmrec_fnq_add_filename(const char *filename
)
546 strlcpy(fn_queue
+ fnq_wr_pos
, filename
, MAX_PATH
);
547 fnq_wr_pos
= FNQ_NEXT(fnq_wr_pos
);
549 if (fnq_rd_pos
!= fnq_wr_pos
)
553 fnq_rd_pos
= FNQ_NEXT(fnq_rd_pos
);
555 } /* pcmrec_fnq_add_filename */
557 /* replace the last filename added */
558 static bool pcmrec_fnq_replace_tail(const char *filename
)
562 if (pcmrec_fnq_is_empty())
565 pos
= FNQ_PREV(fnq_wr_pos
);
567 strlcpy(fn_queue
+ pos
, filename
, MAX_PATH
);
570 } /* pcmrec_fnq_replace_tail */
572 /* pulls the next filename from the queue */
573 static bool pcmrec_fnq_get_filename(char *filename
)
575 if (pcmrec_fnq_is_empty())
579 strlcpy(filename
, fn_queue
+ fnq_rd_pos
, MAX_PATH
);
581 fnq_rd_pos
= FNQ_NEXT(fnq_rd_pos
);
583 } /* pcmrec_fnq_get_filename */
585 /* close the file number pointed to by fd_p */
586 static void pcmrec_close_file(int *fd_p
)
589 return; /* preserve error */
591 if (close(*fd_p
) != 0)
592 errors
|= PCMREC_E_IO
;
595 } /* pcmrec_close_file */
597 /** Data Flushing **/
600 * called after callback to update sizes if codec changed the amount of data
603 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size
,
604 unsigned long prev_num_pcm
)
606 if (rec_fdata
.new_enc_size
!= prev_enc_size
)
608 ssize_t size_diff
= rec_fdata
.new_enc_size
- prev_enc_size
;
609 num_rec_bytes
+= size_diff
;
611 accum_rec_bytes
+= size_diff
;
615 if (rec_fdata
.new_num_pcm
!= prev_num_pcm
)
617 unsigned long pcm_diff
= rec_fdata
.new_num_pcm
- prev_num_pcm
;
618 num_rec_samples
+= pcm_diff
;
620 accum_pcm_samples
+= pcm_diff
;
623 } /* pcmrec_update_sizes_inl */
625 /* don't need to inline every instance */
626 static void pcmrec_update_sizes(size_t prev_enc_size
,
627 unsigned long prev_num_pcm
)
629 pcmrec_update_sizes_inl(prev_enc_size
, prev_num_pcm
);
630 } /* pcmrec_update_sizes */
632 static void pcmrec_start_file(void)
634 size_t enc_size
= rec_fdata
.new_enc_size
;
635 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
636 int curr_rec_file
= rec_fdata
.rec_file
;
637 char filename
[MAX_PATH
];
639 /* must always pull the filename that matches with this queue */
640 if (!pcmrec_fnq_get_filename(filename
))
642 logf("start file: fnq empty");
644 errors
|= PCMREC_E_FNQ_DESYNC
;
646 else if (errors
!= 0)
648 logf("start file: error already");
650 else if (curr_rec_file
>= 0)
652 /* Any previous file should have been closed */
653 logf("start file: file already open");
654 errors
|= PCMREC_E_FNQ_DESYNC
;
658 rec_fdata
.chunk
->flags
|= CHUNKF_ERROR
;
660 /* encoder can set error flag here and should increase
661 enc_new_size and pcm_new_size to reflect additional
662 data written if any */
663 rec_fdata
.filename
= filename
;
664 enc_events_callback(ENC_START_FILE
, &rec_fdata
);
666 if (errors
== 0 && (rec_fdata
.chunk
->flags
& CHUNKF_ERROR
))
668 logf("start file: enc error");
669 errors
|= PCMREC_E_ENCODER
;
674 pcmrec_close_file(&curr_rec_file
);
675 /* Write no more to this file */
676 rec_fdata
.chunk
->flags
|= CHUNKF_END_FILE
;
680 pcmrec_update_sizes(enc_size
, num_pcm
);
683 rec_fdata
.chunk
->flags
&= ~CHUNKF_START_FILE
;
684 } /* pcmrec_start_file */
686 static inline void pcmrec_write_chunk(void)
688 size_t enc_size
= rec_fdata
.new_enc_size
;
689 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
692 rec_fdata
.chunk
->flags
|= CHUNKF_ERROR
;
694 enc_events_callback(ENC_WRITE_CHUNK
, &rec_fdata
);
696 if ((long)rec_fdata
.chunk
->flags
>= 0)
698 pcmrec_update_sizes_inl(enc_size
, num_pcm
);
700 else if (errors
== 0)
702 logf("wr chk enc error %lu %lu",
703 rec_fdata
.chunk
->enc_size
, rec_fdata
.chunk
->num_pcm
);
704 errors
|= PCMREC_E_ENCODER
;
706 } /* pcmrec_write_chunk */
708 static void pcmrec_end_file(void)
710 /* all data in output buffer for current file will have been
711 written and encoder can now do any nescessary steps to
712 finalize the written file */
713 size_t enc_size
= rec_fdata
.new_enc_size
;
714 unsigned long num_pcm
= rec_fdata
.new_num_pcm
;
716 enc_events_callback(ENC_END_FILE
, &rec_fdata
);
720 if (rec_fdata
.chunk
->flags
& CHUNKF_ERROR
)
722 logf("end file: enc error");
723 errors
|= PCMREC_E_ENCODER
;
727 pcmrec_update_sizes(enc_size
, num_pcm
);
731 /* Force file close if error */
733 pcmrec_close_file(&rec_fdata
.rec_file
);
735 rec_fdata
.chunk
->flags
&= ~CHUNKF_END_FILE
;
736 } /* pcmrec_end_file */
739 * Update buffer watermarks with spinup time compensation
741 * All this assumes reasonable data rates, chunk sizes and sufficient
742 * memory for the most part. Some dumb checks are included but perhaps
743 * are pointless since this all will break down at extreme limits that
744 * are currently not applicable to any supported device.
746 static void pcmrec_refresh_watermarks(void)
748 logf("ata spinup: %d", storage_spinup_time());
750 /* set the low mark for when flushing stops if automatic */
751 /* don't change the order in this expression, LOW_SECONDS can be an
752 * integer fraction of 4 */
753 low_watermark
= (sample_rate
*4*LOW_SECONDS
+ (enc_chunk_size
-1))
755 logf("low wmk: %d", low_watermark
);
757 #ifdef HAVE_PRIORITY_SCHEDULING
758 /* panic boost thread priority if 2 seconds of ground is lost -
759 this allows encoder to boost with just under a second of
760 pcm data (if not yet full enough to boost itself)
761 and not falsely trip the alarm. */
762 /* don't change the order in this expression, PANIC_SECONDS can be an
763 * integer fraction of 4 */
764 flood_watermark
= enc_num_chunks
-
765 (sample_rate
*4*PANIC_SECONDS
+ (enc_chunk_size
-1))
768 if (flood_watermark
< low_watermark
)
770 logf("warning: panic < low");
771 flood_watermark
= low_watermark
;
774 logf("flood at: %d", flood_watermark
);
776 spinup_time
= last_storage_spinup_time
= storage_spinup_time();
777 #if (CONFIG_STORAGE & STORAGE_ATA)
778 /* write at 8s + st remaining in enc_buffer - range 12s to
779 20s total - default to 3.5s spinup. */
780 if (spinup_time
== 0)
781 spinup_time
= 35*HZ
/10; /* default - cozy */
782 else if (spinup_time
< 2*HZ
)
783 spinup_time
= 2*HZ
; /* ludicrous - ramdisk? */
784 else if (spinup_time
> 10*HZ
)
785 spinup_time
= 10*HZ
; /* do you have a functioning HD? */
788 /* try to start writing with 10s remaining after disk spinup */
789 high_watermark
= enc_num_chunks
-
790 ((FLUSH_SECONDS
*HZ
+ spinup_time
)*4*sample_rate
+
791 (enc_chunk_size
-1)*HZ
) / (enc_chunk_size
*HZ
);
793 if (high_watermark
< low_watermark
)
795 logf("warning: low 'write at' (%d)", high_watermark
);
796 high_watermark
= low_watermark
;
800 logf("write at: %d", high_watermark
);
801 } /* pcmrec_refresh_watermarks */
806 * This function is called when queue_get_w_tmo times out.
808 * Set flush_num to the number of files to flush to disk or to
809 * a PCMREC_FLUSH_* constant.
811 static void pcmrec_flush(unsigned flush_num
)
813 #ifdef HAVE_PRIORITY_SCHEDULING
814 static unsigned long last_flush_tick
; /* tick when function returned */
815 unsigned long start_tick
; /* When flush started */
816 unsigned long prio_tick
; /* Timeout for auto boost */
817 int prio_pcmrec
; /* Current thread priority for pcmrec */
818 int prio_codec
; /* Current thread priority for codec */
820 int num_ready
; /* Number of chunks ready at start */
821 unsigned remaining
; /* Number of file starts remaining */
822 unsigned chunks_flushed
; /* Chunks flushed (for mini flush only) */
823 bool interruptable
; /* Flush can be interupted */
825 num_ready
= enc_wr_index
- enc_rd_index
;
827 num_ready
+= enc_num_chunks
;
829 /* save interruptable flag and remove it to get the actual count */
830 interruptable
= (flush_num
& PCMREC_FLUSH_INTERRUPTABLE
) != 0;
831 flush_num
&= ~PCMREC_FLUSH_INTERRUPTABLE
;
838 if (storage_spinup_time() != last_storage_spinup_time
)
839 pcmrec_refresh_watermarks();
841 /* enough available? no? then leave */
842 if (num_ready
< high_watermark
)
844 } /* endif (flush_num == 0) */
846 #ifdef HAVE_PRIORITY_SCHEDULING
847 start_tick
= current_tick
;
848 prio_tick
= start_tick
+ PRIO_SECONDS
*HZ
+ spinup_time
;
850 if (flush_num
== 0 && TIME_BEFORE(current_tick
, last_flush_tick
+ HZ
/2))
852 /* if we're getting called too much and this isn't forced,
853 boost stat by expiring timeout in advance */
854 logf("too frequent flush");
855 prio_tick
= current_tick
- 1;
859 prio_codec
= -1; /* GCC is too stoopid to figure out it doesn't
863 logf("writing:%d(%d):%s%s", num_ready
, flush_num
,
864 interruptable
? "i" : "",
865 flush_num
== PCMREC_FLUSH_MINI
? "m" : "");
869 remaining
= flush_num
;
872 while (num_ready
> 0)
874 /* check current number of encoder chunks */
875 int num
= enc_wr_index
- enc_rd_index
;
877 num
+= enc_num_chunks
;
879 if (num
<= low_watermark
&&
880 (flush_num
== PCMREC_FLUSH_IF_HIGH
|| num
<= 0))
882 logf("low data: %d", num
);
883 break; /* data remaining is below threshold */
886 if (interruptable
&& flush_interrupts
> 0)
888 logf("int at: %d", num
);
889 break; /* interrupted */
892 #ifdef HAVE_PRIORITY_SCHEDULING
893 if (prio_pcmrec
== -1 && (num
>= flood_watermark
||
894 TIME_AFTER(current_tick
, prio_tick
)))
896 /* losing ground or holding without progress - boost
897 priority until finished */
898 logf("pcmrec: boost (%s)",
899 num
>= flood_watermark
? "num" : "time");
900 prio_pcmrec
= thread_set_priority(thread_self(),
901 thread_get_priority(thread_self()) - 4);
902 prio_codec
= codec_thread_set_priority(
903 codec_thread_get_priority() - 4);
907 rec_fdata
.chunk
= GET_ENC_CHUNK(enc_rd_index
);
908 rec_fdata
.new_enc_size
= rec_fdata
.chunk
->enc_size
;
909 rec_fdata
.new_num_pcm
= rec_fdata
.chunk
->num_pcm
;
911 if (rec_fdata
.chunk
->flags
& CHUNKF_START_FILE
)
914 if (--remaining
== 0)
915 num_ready
= 0; /* stop on next loop - must write this
916 chunk if it has data */
919 pcmrec_write_chunk();
921 if (rec_fdata
.chunk
->flags
& CHUNKF_END_FILE
)
924 INC_ENC_INDEX(enc_rd_index
);
932 if (flush_num
== PCMREC_FLUSH_MINI
&&
933 ++chunks_flushed
>= MINI_CHUNKS
)
935 logf("mini flush break");
938 /* no yielding; the file apis called in the codecs do that
943 if (rec_fdata
.rec_file
>= 0 && fsync(rec_fdata
.rec_file
) != 0)
944 errors
|= PCMREC_E_IO
;
948 #ifdef HAVE_PRIORITY_SCHEDULING
949 if (prio_pcmrec
!= -1)
951 /* return to original priorities */
952 logf("pcmrec: unboost priority");
953 thread_set_priority(thread_self(), prio_pcmrec
);
954 codec_thread_set_priority(prio_codec
);
957 last_flush_tick
= current_tick
; /* save tick when we left */
964 * Marks a new stream in the buffer and gives the encoder a chance for special
965 * handling of transition from one to the next. The encoder may change the
966 * chunk that ends the old stream by requesting more chunks and similiarly for
967 * the new but must always advance the position though the interface. It can
968 * later reject any data it cares to when writing the file but should mark the
969 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
970 * a NULL data pointer without error as well.
972 static int pcmrec_get_chunk_index(struct enc_chunk_hdr
*chunk
)
974 return ((char *)chunk
- (char *)enc_buffer
) / enc_chunk_size
;
975 } /* pcmrec_get_chunk_index */
977 static struct enc_chunk_hdr
* pcmrec_get_prev_chunk(int index
)
979 DEC_ENC_INDEX(index
);
980 return GET_ENC_CHUNK(index
);
981 } /* pcmrec_get_prev_chunk */
983 static void pcmrec_new_stream(const char *filename
, /* next file name */
984 unsigned long flags
, /* CHUNKF_* flags */
985 int pre_index
) /* index for prerecorded data */
987 logf("pcmrec_new_stream");
988 char path
[MAX_PATH
]; /* place to copy filename so sender can be released */
990 struct enc_buffer_event_data data
;
991 bool (*fnq_add_fn
)(const char *) = NULL
; /* function to use to add
993 struct enc_chunk_hdr
*start
= NULL
; /* pointer to starting chunk of
995 bool did_flush
= false; /* did a flush occurr? */
998 strlcpy(path
, filename
, MAX_PATH
);
999 queue_reply(&pcmrec_queue
, 0); /* We have all we need */
1001 data
.pre_chunk
= NULL
;
1002 data
.chunk
= GET_ENC_CHUNK(enc_wr_index
);
1005 if (flags
& CHUNKF_END_FILE
)
1007 data
.chunk
->flags
&= CHUNKF_START_FILE
| CHUNKF_END_FILE
;
1009 if (data
.chunk
->flags
& CHUNKF_START_FILE
)
1011 /* cannot start and end on same unprocessed chunk */
1012 logf("file end on start");
1013 flags
&= ~CHUNKF_END_FILE
;
1015 else if (enc_rd_index
== enc_wr_index
)
1017 /* all data flushed but file not ended - chunk will be left
1019 logf("end on dead end");
1020 data
.chunk
->flags
= 0;
1021 data
.chunk
->enc_size
= 0;
1022 data
.chunk
->num_pcm
= 0;
1023 data
.chunk
->enc_data
= NULL
;
1024 INC_ENC_INDEX(enc_wr_index
);
1025 data
.chunk
= GET_ENC_CHUNK(enc_wr_index
);
1029 struct enc_chunk_hdr
*last
= pcmrec_get_prev_chunk(enc_wr_index
);
1031 if (last
->flags
& CHUNKF_END_FILE
)
1033 /* end already processed and marked - can't end twice */
1034 logf("file end again");
1035 flags
&= ~CHUNKF_END_FILE
;
1041 if (flags
& CHUNKF_START_FILE
)
1043 bool pre
= flags
& CHUNKF_PRERECORD
;
1047 logf("stream prerecord start");
1048 start
= data
.pre_chunk
= GET_ENC_CHUNK(pre_index
);
1049 start
->flags
&= CHUNKF_START_FILE
| CHUNKF_PRERECORD
;
1053 logf("stream normal start");
1055 start
->flags
&= CHUNKF_START_FILE
;
1058 /* if encoder hasn't yet processed the last start - abort the start
1059 of the previous file queued or else it will be empty and invalid */
1060 if (start
->flags
& CHUNKF_START_FILE
)
1062 logf("replacing fnq tail: %s", filename
);
1063 fnq_add_fn
= pcmrec_fnq_replace_tail
;
1067 logf("adding filename: %s", filename
);
1068 fnq_add_fn
= pcmrec_fnq_add_filename
;
1073 pcmrec_context
= true; /* switch encoder context */
1074 enc_events_callback(ENC_REC_NEW_STREAM
, &data
);
1075 pcmrec_context
= false; /* switch back */
1077 if (flags
& CHUNKF_END_FILE
)
1079 int i
= pcmrec_get_chunk_index(data
.chunk
);
1080 pcmrec_get_prev_chunk(i
)->flags
|= CHUNKF_END_FILE
;
1085 if (!(flags
& CHUNKF_PRERECORD
))
1087 /* get stats on data added to start - sort of a prerecord
1089 int i
= pcmrec_get_chunk_index(data
.chunk
);
1090 struct enc_chunk_hdr
*chunk
= data
.chunk
;
1092 logf("start data: %d %d", i
, enc_wr_index
);
1095 num_rec_samples
= 0;
1097 while (i
!= enc_wr_index
)
1099 num_rec_bytes
+= chunk
->enc_size
;
1100 num_rec_samples
+= chunk
->num_pcm
;
1102 chunk
= GET_ENC_CHUNK(i
);
1105 start
->flags
&= ~CHUNKF_START_FILE
;
1109 start
->flags
|= CHUNKF_START_FILE
;
1111 /* flush all pending files out if full and adding */
1112 if (fnq_add_fn
== pcmrec_fnq_add_filename
&& pcmrec_fnq_is_full())
1115 pcmrec_flush(PCMREC_FLUSH_ALL
);
1122 /* Make sure to complete any interrupted high watermark */
1124 pcmrec_flush(PCMREC_FLUSH_IF_HIGH
);
1125 } /* pcmrec_new_stream */
1127 /** event handlers for pcmrec thread */
1130 static void pcmrec_init(void)
1132 unsigned char *buffer
;
1133 send_event(RECORDING_EVENT_START
, NULL
);
1135 /* warings and errors */
1139 pcmrec_close_file(&rec_fdata
.rec_file
);
1140 rec_fdata
.rec_file
= -1;
1152 /* filename queue */
1158 num_rec_samples
= 0;
1160 accum_rec_bytes
= 0;
1161 accum_pcm_samples
= 0;
1164 pre_record_ticks
= 0;
1166 is_recording
= false;
1169 buffer
= audio_get_recording_buffer(&rec_buffer_size
);
1171 /* Line align pcm_buffer 2^5=32 bytes */
1172 pcm_buffer
= (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer
, 5);
1173 enc_buffer
= pcm_buffer
+ ALIGN_UP_P2(PCM_NUM_CHUNKS
*PCM_CHUNK_SIZE
+
1174 PCM_MAX_FEED_SIZE
, 2);
1175 /* Adjust available buffer for possible align advancement */
1176 rec_buffer_size
-= pcm_buffer
- buffer
;
1178 pcm_init_recording();
1182 static void pcmrec_close(void)
1185 pre_record_ticks
= 0; /* Can't be prerecording any more */
1187 pcm_close_recording();
1189 audio_remove_encoder();
1190 send_event(RECORDING_EVENT_STOP
, NULL
);
1191 } /* pcmrec_close */
1193 /* PCMREC_OPTIONS */
1194 static void pcmrec_set_recording_options(
1195 struct audio_recording_options
*options
)
1197 /* stop DMA transfer */
1199 pcm_stop_recording();
1201 rec_frequency
= options
->rec_frequency
;
1202 rec_source
= options
->rec_source
;
1203 num_channels
= options
->rec_channels
== 1 ? 1 : 2;
1204 rec_mono_mode
= options
->rec_mono_mode
;
1205 pre_record_ticks
= options
->rec_prerecord_time
* HZ
;
1206 enc_config
= options
->enc_config
;
1207 enc_config
.afmt
= rec_format_afmt
[enc_config
.rec_format
];
1209 #ifdef HAVE_SPDIF_IN
1210 if (rec_source
== AUDIO_SRC_SPDIF
)
1212 /* must measure SPDIF sample rate before configuring codecs */
1213 unsigned long sr
= spdif_measure_frequency();
1214 /* round to master list for SPDIF rate */
1215 int index
= round_value_to_list32(sr
, audio_master_sampr_list
,
1216 SAMPR_NUM_FREQ
, false);
1217 sample_rate
= audio_master_sampr_list
[index
];
1218 /* round to HW playback rates for monitoring */
1219 index
= round_value_to_list32(sr
, hw_freq_sampr
,
1220 HW_NUM_FREQ
, false);
1221 pcm_set_frequency(hw_freq_sampr
[index
] | SAMPR_TYPE_REC
);
1222 /* encoders with a limited number of rates do their own rounding */
1227 /* set sample rate from frequency selection */
1228 sample_rate
= rec_freq_sampr
[rec_frequency
];
1229 pcm_set_frequency(sample_rate
| SAMPR_TYPE_REC
);
1232 /* set monitoring */
1233 audio_set_output_source(rec_source
);
1235 /* apply hardware setting to start monitoring now */
1236 pcm_apply_settings();
1238 queue_reply(&pcmrec_queue
, 0); /* Release sender */
1240 if (audio_load_encoder(enc_config
.afmt
))
1242 /* start DMA transfer */
1243 dma_lock
= pre_record_ticks
== 0;
1244 pcm_record_data(pcm_rec_have_more
, GET_PCM_CHUNK(dma_wr_pos
),
1249 logf("set rec opt: enc load failed");
1250 errors
|= PCMREC_E_LOAD_ENCODER
;
1252 } /* pcmrec_set_recording_options */
1254 /* PCMREC_RECORD - start recording (not gapless)
1255 or split stream (gapless) */
1256 static void pcmrec_record(const char *filename
)
1258 unsigned long pre_sample_ticks
;
1260 unsigned long flags
;
1263 logf("pcmrec_record: %s", filename
);
1267 num_rec_samples
= 0;
1272 accum_rec_bytes
= 0;
1273 accum_pcm_samples
= 0;
1275 warnings
= 0; /* reset warnings */
1277 rd_start
= enc_wr_index
;
1278 pre_sample_ticks
= 0;
1280 pcmrec_refresh_watermarks();
1282 if (pre_record_ticks
)
1285 /* calculate number of available chunks */
1286 unsigned long avail_pre_chunks
= (enc_wr_index
- enc_rd_index
+
1287 enc_num_chunks
) % enc_num_chunks
;
1288 /* overflow at 974 seconds of prerecording at 44.1kHz */
1289 unsigned long pre_record_sample_ticks
=
1290 enc_sample_rate
*pre_record_ticks
;
1291 int pre_chunks
= 0; /* Counter to limit prerecorded time to
1292 prevent flood state at outset */
1294 logf("pre-st: %ld", pre_record_sample_ticks
);
1296 /* Get exact measure of recorded data as number of samples aren't
1297 nescessarily going to be the max for each chunk */
1298 for (; avail_pre_chunks
-- > 0;)
1300 struct enc_chunk_hdr
*chunk
;
1301 unsigned long chunk_sample_ticks
;
1305 chunk
= GET_ENC_CHUNK(i
);
1307 /* must have data to be counted */
1308 if (chunk
->enc_data
== NULL
)
1311 chunk_sample_ticks
= chunk
->num_pcm
*HZ
;
1314 pre_sample_ticks
+= chunk_sample_ticks
;
1315 num_rec_bytes
+= chunk
->enc_size
;
1316 num_rec_samples
+= chunk
->num_pcm
;
1319 /* stop here if enough already */
1320 if (pre_chunks
>= high_watermark
||
1321 pre_sample_ticks
>= pre_record_sample_ticks
)
1323 logf("pre-chks: %d", pre_chunks
);
1329 accum_rec_bytes
= num_rec_bytes
;
1330 accum_pcm_samples
= num_rec_samples
;
1334 enc_rd_index
= rd_start
;
1336 /* filename queue should be empty */
1337 if (!pcmrec_fnq_is_empty())
1339 logf("fnq: not empty!");
1340 pcmrec_fnq_set_empty();
1343 flags
= CHUNKF_START_FILE
;
1344 if (pre_sample_ticks
> 0)
1345 flags
|= CHUNKF_PRERECORD
;
1347 pre_index
= enc_rd_index
;
1351 is_recording
= true;
1355 /* already recording, just split the stream */
1356 logf("inserting split");
1357 flags
= CHUNKF_START_FILE
| CHUNKF_END_FILE
;
1361 pcmrec_new_stream(filename
, flags
, pre_index
);
1362 logf("pcmrec_record done");
1363 } /* pcmrec_record */
1366 static void pcmrec_stop(void)
1368 logf("pcmrec_stop");
1372 dma_lock
= true; /* lock dma write position */
1374 /* flush all available data first to avoid overflow while waiting
1375 for encoding to finish */
1376 pcmrec_flush(PCMREC_FLUSH_ALL
);
1378 /* wait for encoder to finish remaining data */
1379 while (errors
== 0 && !pcm_buffer_empty
)
1382 /* end stream at last data */
1383 pcmrec_new_stream(NULL
, CHUNKF_END_FILE
, 0);
1385 /* flush anything else encoder added */
1386 pcmrec_flush(PCMREC_FLUSH_ALL
);
1388 /* remove any pending file start not yet processed - should be at
1389 most one at enc_wr_index */
1390 pcmrec_fnq_get_filename(NULL
);
1391 /* encoder should abort any chunk it was in midst of processing */
1392 GET_ENC_CHUNK(enc_wr_index
)->flags
= CHUNKF_ABORT
;
1394 /* filename queue should be empty */
1395 if (!pcmrec_fnq_is_empty())
1397 logf("fnq: not empty!");
1398 pcmrec_fnq_set_empty();
1401 /* be absolutely sure the file is closed */
1403 pcmrec_close_file(&rec_fdata
.rec_file
);
1404 rec_fdata
.rec_file
= -1;
1406 is_recording
= false;
1408 dma_lock
= pre_record_ticks
== 0;
1412 logf("not recording");
1415 logf("pcmrec_stop done");
1419 static void pcmrec_pause(void)
1421 logf("pcmrec_pause");
1425 logf("not recording");
1429 logf("already paused");
1437 logf("pcmrec_pause done");
1438 } /* pcmrec_pause */
1441 static void pcmrec_resume(void)
1443 logf("pcmrec_resume");
1447 logf("not recording");
1449 else if (!is_paused
)
1456 is_recording
= true;
1460 logf("pcmrec_resume done");
1461 } /* pcmrec_resume */
1463 static void pcmrec_thread(void) NORETURN_ATTR
;
1464 static void pcmrec_thread(void)
1466 struct queue_event ev
;
1468 logf("thread pcmrec start");
1474 /* Poll periodically to flush data */
1475 queue_wait_w_tmo(&pcmrec_queue
, &ev
, HZ
/5);
1477 if (ev
.id
== SYS_TIMEOUT
)
1479 /* Messages that interrupt this will complete it */
1480 pcmrec_flush(PCMREC_FLUSH_IF_HIGH
|
1481 PCMREC_FLUSH_INTERRUPTABLE
);
1487 /* Not doing anything - sit and wait for commands */
1488 queue_wait(&pcmrec_queue
, &ev
);
1501 case PCMREC_OPTIONS
:
1502 pcmrec_set_recording_options(
1503 (struct audio_recording_options
*)ev
.data
);
1507 clear_flush_interrupt();
1508 pcmrec_record((const char *)ev
.data
);
1512 clear_flush_interrupt();
1517 clear_flush_interrupt();
1525 case PCMREC_FLUSH_NUM
:
1526 pcmrec_flush((unsigned)ev
.data
);
1529 case SYS_USB_CONNECTED
:
1533 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
1534 usb_wait_for_disconnect(&pcmrec_queue
);
1535 flush_interrupts
= 0;
1539 } /* pcmrec_thread */
1541 /****************************************************************************/
1543 /* following functions will be called by the encoder codec */
1544 /* in a free-threaded manner */
1546 /****************************************************************************/
1548 /* pass the encoder settings to the encoder */
1549 void enc_get_inputs(struct enc_inputs
*inputs
)
1551 inputs
->sample_rate
= sample_rate
;
1552 inputs
->num_channels
= num_channels
;
1553 inputs
->rec_mono_mode
= rec_mono_mode
;
1554 inputs
->config
= &enc_config
;
1555 } /* enc_get_inputs */
1557 /* set the encoder dimensions (called by encoder codec at initialization and
1559 void enc_set_parameters(struct enc_parameters
*params
)
1561 size_t bufsize
, resbytes
;
1563 logf("enc_set_parameters");
1568 /* Encoder is terminating */
1569 memset(&enc_config
, 0, sizeof (enc_config
));
1570 enc_sample_rate
= 0;
1571 cancel_cpu_boost(); /* Make sure no boost remains */
1575 enc_sample_rate
= params
->enc_sample_rate
;
1576 logf("enc sampr:%lu", enc_sample_rate
);
1578 pcm_rd_pos
= dma_wr_pos
;
1579 pcm_enc_pos
= pcm_rd_pos
;
1581 enc_config
.afmt
= params
->afmt
;
1582 /* addition of the header is always implied - chunk size 4-byte aligned */
1584 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE
+ params
->chunk_size
, 2);
1585 enc_events_callback
= params
->events_callback
;
1587 logf("chunk size:%lu", enc_chunk_size
);
1589 /*** Configure the buffers ***/
1591 /* Layout of recording buffer:
1592 * [ax] = possible alignment x multiple
1593 * [sx] = possible size alignment of x multiple
1594 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1595 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1597 resbytes
= ALIGN_UP_P2(params
->reserve_bytes
, 2);
1598 logf("resbytes:%lu", resbytes
);
1600 bufsize
= rec_buffer_size
- (enc_buffer
- pcm_buffer
) -
1601 resbytes
- FNQ_MIN_NUM_PATHS
*MAX_PATH
1603 - sizeof (*wrap_id_p
)
1607 enc_num_chunks
= bufsize
/ enc_chunk_size
;
1608 logf("num chunks:%d", enc_num_chunks
);
1610 /* get real amount used by encoder chunks */
1611 bufsize
= enc_num_chunks
*enc_chunk_size
;
1612 logf("enc size:%lu", bufsize
);
1615 /* add magic at wraparound for spillover checks */
1616 wrap_id_p
= SKIPBYTES((unsigned long *)enc_buffer
, bufsize
);
1617 bufsize
+= sizeof (*wrap_id_p
);
1618 *wrap_id_p
= ENC_CHUNK_MAGIC
;
1621 /** set OUT parameters **/
1622 params
->enc_buffer
= enc_buffer
;
1623 params
->buf_chunk_size
= enc_chunk_size
;
1624 params
->num_chunks
= enc_num_chunks
;
1626 /* calculate reserve buffer start and return pointer to encoder */
1627 params
->reserve_buffer
= NULL
;
1630 params
->reserve_buffer
= enc_buffer
+ bufsize
;
1631 bufsize
+= resbytes
;
1634 /* place filename queue at end of buffer using up whatever remains */
1635 fnq_rd_pos
= 0; /* reset */
1636 fnq_wr_pos
= 0; /* reset */
1637 fn_queue
= enc_buffer
+ bufsize
;
1638 fnq_size
= pcm_buffer
+ rec_buffer_size
- fn_queue
;
1639 fnq_size
/= MAX_PATH
;
1640 if (fnq_size
> FNQ_MAX_NUM_PATHS
)
1641 fnq_size
= FNQ_MAX_NUM_PATHS
;
1642 fnq_size
*= MAX_PATH
;
1643 logf("fnq files:%ld", fnq_size
/ MAX_PATH
);
1646 logf("pcm:%08lX", (uintptr_t)pcm_buffer
);
1647 logf("enc:%08lX", (uintptr_t)enc_buffer
);
1648 logf("res:%08lX", (uintptr_t)params
->reserve_buffer
);
1649 logf("wip:%08lX", (uintptr_t)wrap_id_p
);
1650 logf("fnq:%08lX", (uintptr_t)fn_queue
);
1651 logf("end:%08lX", (uintptr_t)fn_queue
+ fnq_size
);
1654 /* init all chunk headers and reset indexes */
1656 for (enc_wr_index
= enc_num_chunks
; enc_wr_index
> 0; )
1658 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(--enc_wr_index
);
1660 chunk
->id
= ENC_CHUNK_MAGIC
;
1665 logf("enc_set_parameters done");
1666 } /* enc_set_parameters */
1668 /* return encoder chunk at current write position -
1669 NOTE: can be called by pcmrec thread when splitting streams */
1670 struct enc_chunk_hdr
* enc_get_chunk(void)
1672 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(enc_wr_index
);
1675 if (chunk
->id
!= ENC_CHUNK_MAGIC
|| *wrap_id_p
!= ENC_CHUNK_MAGIC
)
1677 errors
|= PCMREC_E_CHUNK_OVF
;
1678 logf("finish chk ovf: %d", enc_wr_index
);
1682 chunk
->flags
&= CHUNKF_START_FILE
;
1685 chunk
->flags
|= CHUNKF_PRERECORD
;
1688 } /* enc_get_chunk */
1690 /* releases the current chunk into the available chunks -
1691 NOTE: can be called by pcmrec thread when splitting streams */
1692 void enc_finish_chunk(void)
1694 struct enc_chunk_hdr
*chunk
= GET_ENC_CHUNK(enc_wr_index
);
1696 if ((long)chunk
->flags
< 0)
1698 /* encoder set error flag */
1699 errors
|= PCMREC_E_ENCODER
;
1700 logf("finish chk enc error");
1703 /* advance enc_wr_index to the next encoder chunk */
1704 INC_ENC_INDEX(enc_wr_index
);
1706 if (enc_rd_index
!= enc_wr_index
)
1708 num_rec_bytes
+= chunk
->enc_size
;
1709 num_rec_samples
+= chunk
->num_pcm
;
1711 accum_rec_bytes
+= chunk
->enc_size
;
1712 accum_pcm_samples
+= chunk
->num_pcm
;
1715 else if (is_recording
) /* buffer full */
1717 /* keep current position and put up warning flag */
1718 warnings
|= PCMREC_W_ENC_BUFFER_OVF
;
1719 logf("enc_buffer ovf");
1720 DEC_ENC_INDEX(enc_wr_index
);
1723 /* if stream splitting, keep this out of circulation and
1724 flush a small number, then readd - cannot risk losing
1727 pcmrec_flush(PCMREC_FLUSH_MINI
);
1728 INC_ENC_INDEX(enc_wr_index
);
1733 /* advance enc_rd_index for prerecording */
1734 INC_ENC_INDEX(enc_rd_index
);
1736 } /* enc_finish_chunk */
1738 /* passes a pointer to next chunk of unprocessed wav data */
1739 /* TODO: this really should give the actual size returned */
1740 unsigned char * enc_get_pcm_data(size_t size
)
1742 int wp
= dma_wr_pos
;
1743 size_t avail
= (wp
- pcm_rd_pos
) & PCM_CHUNK_MASK
;
1745 /* limit the requested pcm data size */
1746 if (size
> PCM_MAX_FEED_SIZE
)
1747 size
= PCM_MAX_FEED_SIZE
;
1751 unsigned char *ptr
= pcm_buffer
+ pcm_rd_pos
;
1752 int next_pos
= (pcm_rd_pos
+ size
) & PCM_CHUNK_MASK
;
1754 pcm_enc_pos
= pcm_rd_pos
;
1755 pcm_rd_pos
= next_pos
;
1757 /* ptr must point to continous data at wraparound position */
1758 if ((size_t)pcm_rd_pos
< size
)
1760 memcpy(pcm_buffer
+ PCM_NUM_CHUNKS
*PCM_CHUNK_SIZE
,
1761 pcm_buffer
, pcm_rd_pos
);
1764 if (avail
>= (sample_rate
<< 2))
1766 /* Filling up - boost codec */
1767 trigger_cpu_boost();
1770 pcm_buffer_empty
= false;
1774 /* not enough data available - encoder should idle */
1775 pcm_buffer_empty
= true;
1779 /* Sleep long enough to allow one frame on average */
1783 } /* enc_get_pcm_data */
1785 /* puts some pcm data back in the queue */
1786 size_t enc_unget_pcm_data(size_t size
)
1788 int wp
= dma_wr_pos
;
1789 size_t old_avail
= ((pcm_rd_pos
- wp
) & PCM_CHUNK_MASK
) -
1792 /* allow one interrupt to occur during this call and not have the
1793 new read position inside the DMA destination chunk */
1794 if ((ssize_t
)old_avail
> 0)
1796 /* limit size to amount of old data remaining */
1797 if (size
> old_avail
)
1800 pcm_enc_pos
= (pcm_rd_pos
- size
) & PCM_CHUNK_MASK
;
1801 pcm_rd_pos
= pcm_enc_pos
;
1807 } /* enc_unget_pcm_data */