fix the gigabeat remote button not working when backlight is off and filter first...
[Rockbox.git] / firmware / pcm_record.c
blobc27f80400df3d538c2275d5efbc88b3c3d39a91d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "system.h"
20 #include "kernel.h"
21 #include "logf.h"
22 #include "thread.h"
23 #include <string.h>
24 #include "ata.h"
25 #include "usb.h"
26 #include "buffer.h"
27 #include "general.h"
28 #include "audio.h"
29 #include "sound.h"
30 #include "id3.h"
31 #ifdef HAVE_SPDIF_IN
32 #include "spdif.h"
33 #endif
35 /***************************************************************************/
37 /**
38 * APIs implemented in the target tree portion:
39 * Public -
40 * pcm_init_recording
41 * pcm_close_recording
42 * Semi-private -
43 * pcm_rec_dma_start
44 * pcm_rec_dma_stop
47 /** These items may be implemented target specifically or need to
48 be shared semi-privately **/
49 extern struct thread_entry *codec_thread_p;
51 /* the registered callback function for when more data is available */
52 volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
53 /* DMA transfer in is currently active */
54 volatile bool pcm_recording = false;
56 /** General recording state **/
57 static bool is_recording; /* We are recording */
58 static bool is_paused; /* We have paused */
59 static unsigned long errors; /* An error has occured */
60 static unsigned long warnings; /* Warning */
61 static int flush_interrupts = 0; /* Number of messages queued that
62 should interrupt a flush in
63 progress -
64 for a safety net and a prompt
65 response to stop, split and pause
66 requests -
67 only interrupts a flush initiated
68 by pcmrec_flush(0) */
70 /* Utility functions for setting/clearing flushing interrupt flag */
71 static inline void flush_interrupt(void)
73 flush_interrupts++;
74 logf("flush int: %d", flush_interrupts);
77 static inline void clear_flush_interrupt(void)
79 if (--flush_interrupts < 0)
80 flush_interrupts = 0;
83 /** Stats on encoded data for current file **/
84 static size_t num_rec_bytes; /* Num bytes recorded */
85 static unsigned long num_rec_samples; /* Number of PCM samples recorded */
87 /** Stats on encoded data for all files from start to stop **/
88 #if 0
89 static unsigned long long accum_rec_bytes; /* total size written to chunks */
90 static unsigned long long accum_pcm_samples; /* total pcm count processed */
91 #endif
93 /* Keeps data about current file and is sent as event data for codec */
94 static struct enc_file_event_data rec_fdata IDATA_ATTR =
96 .chunk = NULL,
97 .new_enc_size = 0,
98 .new_num_pcm = 0,
99 .rec_file = -1,
100 .num_pcm_samples = 0
103 /** These apply to current settings **/
104 static int rec_source; /* current rec_source setting */
105 static int rec_frequency; /* current frequency setting */
106 static unsigned long sample_rate; /* Sample rate in HZ */
107 static int num_channels; /* Current number of channels */
108 static struct encoder_config enc_config; /* Current encoder configuration */
109 static unsigned long pre_record_ticks; /* pre-record time in ticks */
111 /****************************************************************************
112 use 2 circular buffers:
113 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
114 enc_buffer=encoded audio buffer: storage for encoder output data
116 Flow:
117 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
118 2. if enough pcm data are available the encoder codec does encoding of pcm
119 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
120 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
122 Functions calls (basic encoder steps):
123 1.main: audio_load_encoder(); start the encoder
124 2.encoder: enc_get_inputs(); get encoder recording settings
125 3.encoder: enc_set_parameters(); set the encoder parameters
126 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
127 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional)
128 6.encoder: enc_pcm_buf_near_empty(); if !0: reduce cpu_boost
129 7.encoder: enc_get_chunk(); get a ptr to next enc chunk
130 8.encoder: <process enc chunk> compress and store data to enc chunk
131 9.encoder: enc_finish_chunk(); inform main about chunk processed and
132 is available to be written to a file.
133 Encoder can place any number of chunks
134 of PCM data in a single output chunk
135 but must stay within its output chunk
136 size
137 A.encoder: repeat 4. to 9.
138 B.pcmrec: enc_events_callback(); called for certain events
140 (*) Optional step
141 ****************************************************************************/
143 /** buffer parameters where incoming PCM data is placed **/
144 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
145 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
146 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
148 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
149 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
151 #ifdef PCMREC_PARANOID
152 static void paranoid_set_code(unsigned long code, int line)
154 logf("%08X at %d", code, line);
155 if ((long)code < 0)
156 errors |= code;
157 else
158 warnings |= code;
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, \
165 __LINE__); }
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, \
170 __LINE__); }
171 #define PARANOID_SET_LAST(var) \
172 ; var##_last = var
173 #define PARANOID_CHUNK_CHECK(chunk) \
174 paranoid_chunk_check(chunk)
175 #else
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)
180 #endif
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) \
192 index = value \
193 PARANOID_SET_LAST(index)
194 #define SET_PCM_POS(pos, value) \
195 PARANOID_PCM_POS_CHECK(pos) \
196 pos = value \
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 */
221 #endif
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 */
228 #endif
229 #if MEM <= 16
230 #define PANIC_SECONDS 5 /* flood watermark time until full */
231 #define FLUSH_SECONDS 7 /* flush watermark time until full */
232 #else
233 #define PANIC_SECONDS 8
234 #define FLUSH_SECONDS 10
235 #endif /* MEM */
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 */
248 enum
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
255 chunks */
256 PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark
257 reached */
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 */
267 #endif
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);
280 enum
282 PCMREC_NULL = 0,
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 */
290 #if 0
291 PCMREC_FLUSH_NUM, /* flush a number of files out */
292 #endif
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)
302 if (status < 0)
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);
309 return -1;
311 /* else try again next transmission */
313 else if (!dma_lock)
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
319 write chunk */
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 */
332 #endif
333 SET_PCM_POS(dma_wr_pos, next_pos);
336 pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE);
337 return 0;
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;
365 if (is_recording)
366 ret |= AUDIO_STATUS_RECORD;
367 else if (pre_record_ticks)
368 ret |= AUDIO_STATUS_PRERECORD;
370 if (is_paused)
371 ret |= AUDIO_STATUS_PAUSE;
373 if (errors)
374 ret |= AUDIO_STATUS_ERROR;
376 if (warnings)
377 ret |= AUDIO_STATUS_WARNING;
379 return ret;
380 } /* pcm_rec_status */
383 * Return warnings that have occured since recording started
385 unsigned long pcm_rec_get_warnings(void)
387 return warnings;
390 #if 0
391 int pcm_rec_current_bitrate(void)
393 if (accum_pcm_samples == 0)
394 return 0;
396 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
397 } /* pcm_rec_current_bitrate */
398 #endif
400 #if 0
401 int pcm_rec_encoder_afmt(void)
403 return enc_config.afmt;
404 } /* pcm_rec_encoder_afmt */
405 #endif
407 #if 0
408 int pcm_rec_rec_format(void)
410 return afmt_rec_format[enc_config.afmt];
411 } /* pcm_rec_rec_format */
412 #endif
414 #ifdef HAVE_SPDIF_IN
415 unsigned long pcm_rec_sample_rate(void)
417 /* Which is better ?? */
418 #if 0
419 return enc_sample_rate;
420 #endif
421 return sample_rate;
422 } /* audio_get_sample_rate */
423 #endif
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);
433 pcmrec_thread_p =
434 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
435 pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
436 IF_COP(, CPU, false));
437 } /* pcm_rec_init */
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");
449 (void)buffer_offset;
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);
478 flush_interrupt();
479 queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename);
480 logf("audio_record_done");
481 } /* audio_record */
484 * Stop current recording if recording
486 void audio_stop_recording(void)
488 logf("audio_stop_recording");
489 flush_interrupt();
490 queue_send(&pcmrec_queue, PCMREC_STOP, 0);
491 logf("audio_stop_recording done");
492 } /* audio_stop_recording */
495 * Pause current recording
497 void audio_pause_recording(void)
499 logf("audio_pause_recording");
500 flush_interrupt();
501 queue_send(&pcmrec_queue, PCMREC_PAUSE, 0);
502 logf("audio_pause_recording done");
503 } /* audio_pause_recording */
506 * Resume current recording if paused
508 void audio_resume_recording(void)
510 logf("audio_resume_recording");
511 queue_send(&pcmrec_queue, PCMREC_RESUME, 0);
512 logf("audio_resume_recording done");
513 } /* audio_resume_recording */
516 * Note that microphone is mono, only left value is used
517 * See audiohw_set_recvol() for exact ranges.
519 * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN
522 void audio_set_recording_gain(int left, int right, int type)
524 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
525 audiohw_set_recvol(left, right, type);
526 } /* audio_set_recording_gain */
528 /** Information about current state **/
531 * Return current recorded time in ticks (playback eqivalent time)
533 unsigned long audio_recorded_time(void)
535 if (!is_recording || enc_sample_rate == 0)
536 return 0;
538 /* return actual recorded time a la encoded data even if encoder rate
539 doesn't match the pcm rate */
540 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
541 } /* audio_recorded_time */
544 * Return number of bytes encoded to output
546 unsigned long audio_num_recorded_bytes(void)
548 if (!is_recording)
549 return 0;
551 return num_rec_bytes;
552 } /* audio_num_recorded_bytes */
554 #ifdef HAVE_SPDIF_IN
556 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
557 * our reading on the actual SPDIF sample rate (which might be a bit
558 * inaccurate), we round off to the closest sample rate that is supported by
559 * SPDIF.
561 int audio_get_spdif_sample_rate(void)
563 unsigned long measured_rate = spdif_measure_frequency();
564 /* Find which SPDIF sample rate we're closest to. */
565 return round_value_to_list32(measured_rate, audio_master_sampr_list,
566 SAMPR_NUM_FREQ, false);
567 } /* audio_get_spdif_sample_rate */
568 #endif /* HAVE_SPDIF_IN */
570 /***************************************************************************/
571 /* */
572 /* Functions that execute in the context of pcmrec_thread */
573 /* */
574 /***************************************************************************/
576 /** Filename Queue **/
578 /* returns true if the queue is empty */
579 static inline bool pcmrec_fnq_is_empty(void)
581 return fnq_rd_pos == fnq_wr_pos;
582 } /* pcmrec_fnq_is_empty */
584 /* empties the filename queue */
585 static inline void pcmrec_fnq_set_empty(void)
587 fnq_rd_pos = fnq_wr_pos;
588 } /* pcmrec_fnq_set_empty */
590 /* returns true if the queue is full */
591 static bool pcmrec_fnq_is_full(void)
593 ssize_t size = fnq_wr_pos - fnq_rd_pos;
594 if (size < 0)
595 size += fnq_size;
597 return size >= fnq_size - MAX_PATH;
598 } /* pcmrec_fnq_is_full */
600 /* queue another filename - will overwrite oldest one if full */
601 static bool pcmrec_fnq_add_filename(const char *filename)
603 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
605 if ((fnq_wr_pos += MAX_PATH) >= fnq_size)
606 fnq_wr_pos = 0;
608 if (fnq_rd_pos != fnq_wr_pos)
609 return true;
611 /* queue full */
612 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
613 fnq_rd_pos = 0;
615 return true;
616 } /* pcmrec_fnq_add_filename */
618 /* replace the last filename added */
619 static bool pcmrec_fnq_replace_tail(const char *filename)
621 int pos;
623 if (pcmrec_fnq_is_empty())
624 return false;
626 pos = fnq_wr_pos - MAX_PATH;
627 if (pos < 0)
628 pos = fnq_size - MAX_PATH;
630 strncpy(fn_queue + pos, filename, MAX_PATH);
632 return true;
633 } /* pcmrec_fnq_replace_tail */
635 /* pulls the next filename from the queue */
636 static bool pcmrec_fnq_get_filename(char *filename)
638 if (pcmrec_fnq_is_empty())
639 return false;
641 if (filename)
642 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
644 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
645 fnq_rd_pos = 0;
647 return true;
648 } /* pcmrec_fnq_get_filename */
650 /* close the file number pointed to by fd_p */
651 static void pcmrec_close_file(int *fd_p)
653 if (*fd_p < 0)
654 return; /* preserve error */
656 if (close(*fd_p) != 0)
657 errors |= PCMREC_E_IO;
659 *fd_p = -1;
660 } /* pcmrec_close_file */
662 #ifdef PCMREC_PARANOID
663 static void paranoid_chunk_check(const struct enc_chunk_hdr *chunk)
665 /* check integrity of things that must be ok - data or not */
667 /* check magic in header */
668 if (chunk->id != ENC_CHUNK_MAGIC)
670 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
671 logf("bad chunk: %d", chunk - (struct enc_chunk_hdr *)enc_buffer);
674 /* check magic wrap id */
675 if (*wrap_id_p != ENC_CHUNK_MAGIC)
677 errors |= PCMREC_E_BAD_CHUNK | PCMREC_E_CHUNK_OVF;
678 logf("bad magic at wrap pos");
681 if (chunk->enc_data == NULL) /* has data? */
682 return;
684 /* check that data points to something after header */
685 if (chunk->enc_data < ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk))
687 errors |= PCMREC_E_BAD_CHUNK;
688 logf("chk ptr < hdr end");
691 /* check if data end is within chunk */
692 if (chunk->enc_data + chunk->enc_size >
693 (unsigned char *)chunk + enc_chunk_size)
695 errors |= PCMREC_E_BAD_CHUNK;
696 logf("chk data > chk end");
699 if ((chunk->flags & ~CHUNKF_ALLFLAGS) != 0)
701 errors |= PCMREC_E_BAD_CHUNK;
702 logf("chk bad flags %08X", chunk->flags);
704 } /* paranoid_chunk_check */
705 #endif /* PCMREC_PARANOID */
707 /** Data Flushing **/
710 * called after callback to update sizes if codec changed the amount of data
711 * a chunk represents
713 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
714 unsigned long prev_num_pcm)
716 if (rec_fdata.new_enc_size != prev_enc_size)
718 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
719 num_rec_bytes += size_diff;
720 #if 0
721 accum_rec_bytes += size_diff;
722 #endif
725 if (rec_fdata.new_num_pcm != prev_num_pcm)
727 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
728 num_rec_samples += pcm_diff;
729 #if 0
730 accum_pcm_samples += pcm_diff;
731 #endif
733 } /* pcmrec_update_sizes_inl */
735 /* don't need to inline every instance */
736 static void pcmrec_update_sizes(size_t prev_enc_size,
737 unsigned long prev_num_pcm)
739 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
740 } /* pcmrec_update_sizes */
742 static void pcmrec_start_file(void)
744 size_t enc_size = rec_fdata.new_enc_size;
745 unsigned long num_pcm = rec_fdata.new_num_pcm;
746 int curr_rec_file = rec_fdata.rec_file;
747 char filename[MAX_PATH];
749 /* must always pull the filename that matches with this queue */
750 if (!pcmrec_fnq_get_filename(filename))
752 logf("start file: fnq empty");
753 *filename = '\0';
754 errors |= PCMREC_E_FNQ_DESYNC;
756 else if (errors != 0)
758 logf("start file: error already");
760 else if (curr_rec_file >= 0)
762 /* Any previous file should have been closed */
763 logf("start file: file already open");
764 errors |= PCMREC_E_FNQ_DESYNC;
767 if (errors != 0)
768 rec_fdata.chunk->flags |= CHUNKF_ERROR;
770 /* encoder can set error flag here and should increase
771 enc_new_size and pcm_new_size to reflect additional
772 data written if any */
773 rec_fdata.filename = filename;
774 enc_events_callback(ENC_START_FILE, &rec_fdata);
776 if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR))
778 logf("start file: enc error");
779 errors |= PCMREC_E_ENCODER;
782 if (errors != 0)
784 pcmrec_close_file(&curr_rec_file);
785 /* Write no more to this file */
786 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
788 else
790 pcmrec_update_sizes(enc_size, num_pcm);
793 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
794 } /* pcmrec_start_file */
796 static inline void pcmrec_write_chunk(void)
798 size_t enc_size = rec_fdata.new_enc_size;
799 unsigned long num_pcm = rec_fdata.new_num_pcm;
801 if (errors != 0)
802 rec_fdata.chunk->flags |= CHUNKF_ERROR;
804 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
806 if ((long)rec_fdata.chunk->flags >= 0)
808 pcmrec_update_sizes_inl(enc_size, num_pcm);
810 else if (errors == 0)
812 logf("wr chk enc error %lu %lu",
813 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
814 errors |= PCMREC_E_ENCODER;
816 } /* pcmrec_write_chunk */
818 static void pcmrec_end_file(void)
820 /* all data in output buffer for current file will have been
821 written and encoder can now do any nescessary steps to
822 finalize the written file */
823 size_t enc_size = rec_fdata.new_enc_size;
824 unsigned long num_pcm = rec_fdata.new_num_pcm;
826 enc_events_callback(ENC_END_FILE, &rec_fdata);
828 if (errors == 0)
830 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
832 logf("end file: enc error");
833 errors |= PCMREC_E_ENCODER;
835 else
837 pcmrec_update_sizes(enc_size, num_pcm);
841 /* Force file close if error */
842 if (errors != 0)
843 pcmrec_close_file(&rec_fdata.rec_file);
845 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
846 } /* pcmrec_end_file */
849 * Update buffer watermarks with spinup time compensation
851 * All this assumes reasonable data rates, chunk sizes and sufficient
852 * memory for the most part. Some dumb checks are included but perhaps
853 * are pointless since this all will break down at extreme limits that
854 * are currently not applicable to any supported device.
856 static void pcmrec_refresh_watermarks(void)
858 logf("ata spinup: %d", ata_spinup_time);
860 /* set the low mark for when flushing stops if automatic */
861 low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1))
862 / enc_chunk_size;
863 logf("low wmk: %d", low_watermark);
865 #ifdef HAVE_PRIORITY_SCHEDULING
866 /* panic boost thread priority if 2 seconds of ground is lost -
867 this allows encoder to boost with just under a second of
868 pcm data (if not yet full enough to boost itself)
869 and not falsely trip the alarm. */
870 flood_watermark = enc_num_chunks -
871 (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1))
872 / enc_chunk_size;
874 if (flood_watermark < low_watermark)
876 logf("warning: panic < low");
877 flood_watermark = low_watermark;
880 logf("flood at: %d", flood_watermark);
881 #endif
882 spinup_time = last_ata_spinup_time = ata_spinup_time;
884 /* write at 8s + st remaining in enc_buffer - range 12s to
885 20s total - default to 3.5s spinup. */
886 if (spinup_time == 0)
887 spinup_time = 35*HZ/10; /* default - cozy */
888 else if (spinup_time < 2*HZ)
889 spinup_time = 2*HZ; /* ludicrous - ramdisk? */
890 else if (spinup_time > 10*HZ)
891 spinup_time = 10*HZ; /* do you have a functioning HD? */
893 /* try to start writing with 10s remaining after disk spinup */
894 high_watermark = enc_num_chunks -
895 ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate +
896 (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ);
898 if (high_watermark < low_watermark)
900 high_watermark = low_watermark;
901 low_watermark /= 2;
902 logf("warning: low 'write at'");
905 logf("write at: %d", high_watermark);
906 } /* pcmrec_refresh_watermarks */
909 * Process the chunks
911 * This function is called when queue_get_w_tmo times out.
913 * Set flush_num to the number of files to flush to disk or to
914 * a PCMREC_FLUSH_* constant.
916 static void pcmrec_flush(unsigned flush_num)
918 #ifdef HAVE_PRIORITY_SCHEDULING
919 static unsigned long last_flush_tick; /* tick when function returned */
920 unsigned long start_tick; /* When flush started */
921 unsigned long prio_tick; /* Timeout for auto boost */
922 int prio_pcmrec; /* Current thread priority for pcmrec */
923 int prio_codec; /* Current thread priority for codec */
924 #endif
925 int num_ready; /* Number of chunks ready at start */
926 unsigned remaining; /* Number of file starts remaining */
927 unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */
928 bool interruptable; /* Flush can be interupted */
930 num_ready = enc_wr_index - enc_rd_index;
931 if (num_ready < 0)
932 num_ready += enc_num_chunks;
934 /* save interruptable flag and remove it to get the actual count */
935 interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0;
936 flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE;
938 if (flush_num == 0)
940 if (!is_recording)
941 return;
943 if (ata_spinup_time != last_ata_spinup_time)
944 pcmrec_refresh_watermarks();
946 /* enough available? no? then leave */
947 if (num_ready < high_watermark)
948 return;
949 } /* endif (flush_num == 0) */
951 #ifdef HAVE_PRIORITY_SCHEDULING
952 start_tick = current_tick;
953 prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time;
955 if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2))
957 /* if we're getting called too much and this isn't forced,
958 boost stat by expiring timeout in advance */
959 logf("too frequent flush");
960 prio_tick = current_tick - 1;
963 prio_pcmrec = -1;
964 prio_codec = -1; /* GCC is too stoopid to figure out it doesn't
965 need init */
966 #endif
968 logf("writing:%d(%d):%s%s", num_ready, flush_num,
969 interruptable ? "i" : "",
970 flush_num == PCMREC_FLUSH_MINI ? "m" : "");
972 cpu_boost(true);
974 remaining = flush_num;
975 chunks_flushed = 0;
977 while (num_ready > 0)
979 /* check current number of encoder chunks */
980 int num = enc_wr_index - enc_rd_index;
981 if (num < 0)
982 num += enc_num_chunks;
984 if (num <= low_watermark &&
985 (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0))
987 logf("low data: %d", num);
988 break; /* data remaining is below threshold */
991 if (interruptable && flush_interrupts > 0)
993 logf("int at: %d", num);
994 break; /* interrupted */
997 #ifdef HAVE_PRIORITY_SCHEDULING
998 if (prio_pcmrec == -1 && (num >= flood_watermark ||
999 TIME_AFTER(current_tick, prio_tick)))
1001 /* losing ground or holding without progress - boost
1002 priority until finished */
1003 logf("pcmrec: boost (%s)",
1004 num >= flood_watermark ? "num" : "time");
1005 prio_pcmrec = thread_set_priority(NULL,
1006 thread_get_priority(NULL) - 1);
1007 prio_codec = thread_set_priority(codec_thread_p,
1008 thread_get_priority(codec_thread_p) - 1);
1010 #endif
1012 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
1013 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
1014 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
1016 PARANOID_CHUNK_CHECK(rec_fdata.chunk);
1018 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
1020 pcmrec_start_file();
1021 if (--remaining == 0)
1022 num_ready = 0; /* stop on next loop - must write this
1023 chunk if it has data */
1026 pcmrec_write_chunk();
1028 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
1029 pcmrec_end_file();
1031 INC_ENC_INDEX(enc_rd_index);
1033 if (errors != 0)
1034 break;
1036 if (flush_num == PCMREC_FLUSH_MINI &&
1037 ++chunks_flushed >= MINI_CHUNKS)
1039 logf("mini flush break");
1040 break;
1042 /* no yielding; the file apis called in the codecs do that
1043 sufficiently */
1044 } /* end while */
1046 /* sync file */
1047 if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0)
1048 errors |= PCMREC_E_IO;
1050 cpu_boost(false);
1052 #ifdef HAVE_PRIORITY_SCHEDULING
1053 if (prio_pcmrec != -1)
1055 /* return to original priorities */
1056 logf("pcmrec: unboost priority");
1057 thread_set_priority(NULL, prio_pcmrec);
1058 thread_set_priority(codec_thread_p, prio_codec);
1061 last_flush_tick = current_tick; /* save tick when we left */
1062 #endif
1064 logf("done");
1065 } /* pcmrec_flush */
1068 * Marks a new stream in the buffer and gives the encoder a chance for special
1069 * handling of transition from one to the next. The encoder may change the
1070 * chunk that ends the old stream by requesting more chunks and similiarly for
1071 * the new but must always advance the position though the interface. It can
1072 * later reject any data it cares to when writing the file but should mark the
1073 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
1074 * a NULL data pointer without error as well.
1076 static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk)
1078 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
1079 } /* pcmrec_get_chunk_index */
1081 static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index)
1083 #ifdef PCMREC_PARANOID
1084 int index_last = index;
1085 #endif
1086 DEC_ENC_INDEX(index);
1087 return GET_ENC_CHUNK(index);
1088 } /* pcmrec_get_prev_chunk */
1090 static void pcmrec_new_stream(const char *filename, /* next file name */
1091 unsigned long flags, /* CHUNKF_* flags */
1092 int pre_index) /* index for prerecorded data */
1094 logf("pcmrec_new_stream");
1095 char path[MAX_PATH]; /* place to copy filename so sender can be released */
1097 struct enc_buffer_event_data data;
1098 bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add
1099 new filename */
1100 struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of
1101 stream */
1102 bool did_flush = false; /* did a flush occurr? */
1104 if (filename)
1105 strncpy(path, filename, MAX_PATH);
1106 queue_reply(&pcmrec_queue, 0); /* We have all we need */
1108 data.pre_chunk = NULL;
1109 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1111 /* end chunk */
1112 if (flags & CHUNKF_END_FILE)
1114 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
1116 if (data.chunk->flags & CHUNKF_START_FILE)
1118 /* cannot start and end on same unprocessed chunk */
1119 logf("file end on start");
1120 flags &= ~CHUNKF_END_FILE;
1122 else if (enc_rd_index == enc_wr_index)
1124 /* all data flushed but file not ended - chunk will be left
1125 empty */
1126 logf("end on dead end");
1127 data.chunk->flags = 0;
1128 data.chunk->enc_size = 0;
1129 data.chunk->num_pcm = 0;
1130 data.chunk->enc_data = NULL;
1131 INC_ENC_INDEX(enc_wr_index);
1132 data.chunk = GET_ENC_CHUNK(enc_wr_index);
1134 else
1136 struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index);
1138 if (last->flags & CHUNKF_END_FILE)
1140 /* end already processed and marked - can't end twice */
1141 logf("file end again");
1142 flags &= ~CHUNKF_END_FILE;
1147 /* start chunk */
1148 if (flags & CHUNKF_START_FILE)
1150 bool pre = flags & CHUNKF_PRERECORD;
1152 if (pre)
1154 logf("stream prerecord start");
1155 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
1156 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
1158 else
1160 logf("stream normal start");
1161 start = data.chunk;
1162 start->flags &= CHUNKF_START_FILE;
1165 /* if encoder hasn't yet processed the last start - abort the start
1166 of the previous file queued or else it will be empty and invalid */
1167 if (start->flags & CHUNKF_START_FILE)
1169 logf("replacing fnq tail: %s", filename);
1170 fnq_add_fn = pcmrec_fnq_replace_tail;
1172 else
1174 logf("adding filename: %s", filename);
1175 fnq_add_fn = pcmrec_fnq_add_filename;
1179 data.flags = flags;
1180 pcmrec_context = true; /* switch encoder context */
1181 enc_events_callback(ENC_REC_NEW_STREAM, &data);
1182 pcmrec_context = false; /* switch back */
1184 if (flags & CHUNKF_END_FILE)
1186 int i = pcmrec_get_chunk_index(data.chunk);
1187 pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
1190 if (start)
1192 if (!(flags & CHUNKF_PRERECORD))
1194 /* get stats on data added to start - sort of a prerecord
1195 operation */
1196 int i = pcmrec_get_chunk_index(data.chunk);
1197 #ifdef PCMREC_PARANOID
1198 int i_last = i;
1199 #endif
1200 struct enc_chunk_hdr *chunk = data.chunk;
1202 logf("start data: %d %d", i, enc_wr_index);
1204 num_rec_bytes = 0;
1205 num_rec_samples = 0;
1207 while (i != enc_wr_index)
1209 num_rec_bytes += chunk->enc_size;
1210 num_rec_samples += chunk->num_pcm;
1211 INC_ENC_INDEX(i);
1212 chunk = GET_ENC_CHUNK(i);
1215 start->flags &= ~CHUNKF_START_FILE;
1216 start = data.chunk;
1219 start->flags |= CHUNKF_START_FILE;
1221 /* flush all pending files out if full and adding */
1222 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1224 logf("fnq full");
1225 pcmrec_flush(PCMREC_FLUSH_ALL);
1226 did_flush = true;
1229 fnq_add_fn(path);
1232 /* Make sure to complete any interrupted high watermark */
1233 if (!did_flush)
1234 pcmrec_flush(PCMREC_FLUSH_IF_HIGH);
1235 } /* pcmrec_new_stream */
1237 /** event handlers for pcmrec thread */
1239 /* PCMREC_INIT */
1240 static void pcmrec_init(void)
1242 unsigned char *buffer;
1244 /* warings and errors */
1245 warnings =
1246 errors = 0;
1248 pcmrec_close_file(&rec_fdata.rec_file);
1249 rec_fdata.rec_file = -1;
1251 /* pcm FIFO */
1252 dma_lock = true;
1253 SET_PCM_POS(pcm_rd_pos, 0);
1254 SET_PCM_POS(dma_wr_pos, 0);
1255 pcm_enc_pos = 0;
1257 /* encoder FIFO */
1258 SET_ENC_INDEX(enc_wr_index, 0);
1259 SET_ENC_INDEX(enc_rd_index, 0);
1261 /* filename queue */
1262 fnq_rd_pos = 0;
1263 fnq_wr_pos = 0;
1265 /* stats */
1266 num_rec_bytes = 0;
1267 num_rec_samples = 0;
1268 #if 0
1269 accum_rec_bytes = 0;
1270 accum_pcm_samples = 0;
1271 #endif
1273 pre_record_ticks = 0;
1275 is_recording = false;
1276 is_paused = false;
1278 buffer = audio_get_recording_buffer(&rec_buffer_size);
1280 /* Line align pcm_buffer 2^4=16 bytes */
1281 pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4);
1282 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1283 PCM_MAX_FEED_SIZE, 2);
1284 /* Adjust available buffer for possible align advancement */
1285 rec_buffer_size -= pcm_buffer - buffer;
1287 pcm_init_recording();
1288 } /* pcmrec_init */
1290 /* PCMREC_CLOSE */
1291 static void pcmrec_close(void)
1293 dma_lock = true;
1294 pre_record_ticks = 0; /* Can't be prerecording any more */
1295 warnings = 0;
1296 pcm_close_recording();
1297 reset_hardware();
1298 audio_remove_encoder();
1299 } /* pcmrec_close */
1301 /* PCMREC_OPTIONS */
1302 static void pcmrec_set_recording_options(
1303 struct audio_recording_options *options)
1305 /* stop DMA transfer */
1306 dma_lock = true;
1307 pcm_stop_recording();
1309 rec_frequency = options->rec_frequency;
1310 rec_source = options->rec_source;
1311 num_channels = options->rec_channels == 1 ? 1 : 2;
1312 pre_record_ticks = options->rec_prerecord_time * HZ;
1313 enc_config = options->enc_config;
1314 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
1316 #ifdef HAVE_SPDIF_IN
1317 if (rec_source == AUDIO_SRC_SPDIF)
1319 /* must measure SPDIF sample rate before configuring codecs */
1320 unsigned long sr = spdif_measure_frequency();
1321 /* round to master list for SPDIF rate */
1322 int index = round_value_to_list32(sr, audio_master_sampr_list,
1323 SAMPR_NUM_FREQ, false);
1324 sample_rate = audio_master_sampr_list[index];
1325 /* round to HW playback rates for monitoring */
1326 index = round_value_to_list32(sr, hw_freq_sampr,
1327 HW_NUM_FREQ, false);
1328 pcm_set_frequency(hw_freq_sampr[index]);
1329 /* encoders with a limited number of rates do their own rounding */
1331 else
1332 #endif
1334 /* set sample rate from frequency selection */
1335 sample_rate = rec_freq_sampr[rec_frequency];
1336 pcm_set_frequency(sample_rate);
1339 /* set monitoring */
1340 audio_set_output_source(rec_source);
1342 /* apply hardware setting to start monitoring now */
1343 pcm_apply_settings();
1345 queue_reply(&pcmrec_queue, 0); /* Release sender */
1347 if (audio_load_encoder(enc_config.afmt))
1349 /* start DMA transfer */
1350 dma_lock = pre_record_ticks == 0;
1351 pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos),
1352 PCM_CHUNK_SIZE);
1354 else
1356 logf("set rec opt: enc load failed");
1357 errors |= PCMREC_E_LOAD_ENCODER;
1359 } /* pcmrec_set_recording_options */
1361 /* PCMREC_RECORD - start recording (not gapless)
1362 or split stream (gapless) */
1363 static void pcmrec_record(const char *filename)
1365 unsigned long pre_sample_ticks;
1366 int rd_start;
1367 unsigned long flags;
1368 int pre_index;
1370 logf("pcmrec_record: %s", filename);
1372 /* reset stats */
1373 num_rec_bytes = 0;
1374 num_rec_samples = 0;
1376 if (!is_recording)
1378 #if 0
1379 accum_rec_bytes = 0;
1380 accum_pcm_samples = 0;
1381 #endif
1382 warnings = 0; /* reset warnings */
1384 rd_start = enc_wr_index;
1385 pre_sample_ticks = 0;
1387 pcmrec_refresh_watermarks();
1389 if (pre_record_ticks)
1391 int i = rd_start;
1392 #ifdef PCMREC_PARANOID
1393 int i_last = i;
1394 #endif
1395 /* calculate number of available chunks */
1396 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1397 enc_num_chunks) % enc_num_chunks;
1398 /* overflow at 974 seconds of prerecording at 44.1kHz */
1399 unsigned long pre_record_sample_ticks =
1400 enc_sample_rate*pre_record_ticks;
1401 int pre_chunks = 0; /* Counter to limit prerecorded time to
1402 prevent flood state at outset */
1404 logf("pre-st: %ld", pre_record_sample_ticks);
1406 /* Get exact measure of recorded data as number of samples aren't
1407 nescessarily going to be the max for each chunk */
1408 for (; avail_pre_chunks-- > 0;)
1410 struct enc_chunk_hdr *chunk;
1411 unsigned long chunk_sample_ticks;
1413 DEC_ENC_INDEX(i);
1415 chunk = GET_ENC_CHUNK(i);
1417 /* must have data to be counted */
1418 if (chunk->enc_data == NULL)
1419 continue;
1421 chunk_sample_ticks = chunk->num_pcm*HZ;
1423 rd_start = i;
1424 pre_sample_ticks += chunk_sample_ticks;
1425 num_rec_bytes += chunk->enc_size;
1426 num_rec_samples += chunk->num_pcm;
1427 pre_chunks++;
1429 /* stop here if enough already */
1430 if (pre_chunks >= high_watermark ||
1431 pre_sample_ticks >= pre_record_sample_ticks)
1433 logf("pre-chks: %d", pre_chunks);
1434 break;
1438 #if 0
1439 accum_rec_bytes = num_rec_bytes;
1440 accum_pcm_samples = num_rec_samples;
1441 #endif
1444 SET_ENC_INDEX(enc_rd_index, rd_start);
1446 /* filename queue should be empty */
1447 if (!pcmrec_fnq_is_empty())
1449 logf("fnq: not empty!");
1450 pcmrec_fnq_set_empty();
1453 flags = CHUNKF_START_FILE;
1454 if (pre_sample_ticks > 0)
1455 flags |= CHUNKF_PRERECORD;
1457 pre_index = enc_rd_index;
1459 dma_lock = false;
1460 is_paused = false;
1461 is_recording = true;
1463 else
1465 /* already recording, just split the stream */
1466 logf("inserting split");
1467 flags = CHUNKF_START_FILE | CHUNKF_END_FILE;
1468 pre_index = 0;
1471 pcmrec_new_stream(filename, flags, pre_index);
1472 logf("pcmrec_record done");
1473 } /* pcmrec_record */
1475 /* PCMREC_STOP */
1476 static void pcmrec_stop(void)
1478 logf("pcmrec_stop");
1480 if (is_recording)
1482 dma_lock = true; /* lock dma write position */
1483 queue_reply(&pcmrec_queue, 0);
1485 /* flush all available data first to avoid overflow while waiting
1486 for encoding to finish */
1487 pcmrec_flush(PCMREC_FLUSH_ALL);
1489 /* wait for encoder to finish remaining data */
1490 while (errors == 0 && !pcm_buffer_empty)
1491 yield();
1493 /* end stream at last data */
1494 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1496 /* flush anything else encoder added */
1497 pcmrec_flush(PCMREC_FLUSH_ALL);
1499 /* remove any pending file start not yet processed - should be at
1500 most one at enc_wr_index */
1501 pcmrec_fnq_get_filename(NULL);
1502 /* encoder should abort any chunk it was in midst of processing */
1503 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1505 /* filename queue should be empty */
1506 if (!pcmrec_fnq_is_empty())
1508 logf("fnq: not empty!");
1509 pcmrec_fnq_set_empty();
1512 /* be absolutely sure the file is closed */
1513 if (errors != 0)
1514 pcmrec_close_file(&rec_fdata.rec_file);
1515 rec_fdata.rec_file = -1;
1517 is_recording = false;
1518 is_paused = false;
1519 dma_lock = pre_record_ticks == 0;
1521 else
1523 logf("not recording");
1526 logf("pcmrec_stop done");
1527 } /* pcmrec_stop */
1529 /* PCMREC_PAUSE */
1530 static void pcmrec_pause(void)
1532 logf("pcmrec_pause");
1534 if (!is_recording)
1536 logf("not recording");
1538 else if (is_paused)
1540 logf("already paused");
1542 else
1544 dma_lock = true;
1545 is_paused = true;
1548 logf("pcmrec_pause done");
1549 } /* pcmrec_pause */
1551 /* PCMREC_RESUME */
1552 static void pcmrec_resume(void)
1554 logf("pcmrec_resume");
1556 if (!is_recording)
1558 logf("not recording");
1560 else if (!is_paused)
1562 logf("not paused");
1564 else
1566 is_paused = false;
1567 is_recording = true;
1568 dma_lock = false;
1571 logf("pcmrec_resume done");
1572 } /* pcmrec_resume */
1574 static void pcmrec_thread(void) __attribute__((noreturn));
1575 static void pcmrec_thread(void)
1577 struct event ev;
1579 logf("thread pcmrec start");
1581 while(1)
1583 if (is_recording)
1585 /* Poll periodically to flush data */
1586 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1588 if (ev.id == SYS_TIMEOUT)
1590 /* Messages that interrupt this will complete it */
1591 pcmrec_flush(PCMREC_FLUSH_IF_HIGH |
1592 PCMREC_FLUSH_INTERRUPTABLE);
1593 continue;
1596 else
1598 /* Not doing anything - sit and wait for commands */
1599 queue_wait(&pcmrec_queue, &ev);
1602 switch (ev.id)
1604 case PCMREC_INIT:
1605 pcmrec_init();
1606 break;
1608 case PCMREC_CLOSE:
1609 pcmrec_close();
1610 break;
1612 case PCMREC_OPTIONS:
1613 pcmrec_set_recording_options(
1614 (struct audio_recording_options *)ev.data);
1615 break;
1617 case PCMREC_RECORD:
1618 clear_flush_interrupt();
1619 pcmrec_record((const char *)ev.data);
1620 break;
1622 case PCMREC_STOP:
1623 clear_flush_interrupt();
1624 pcmrec_stop();
1625 break;
1627 case PCMREC_PAUSE:
1628 clear_flush_interrupt();
1629 pcmrec_pause();
1630 break;
1632 case PCMREC_RESUME:
1633 pcmrec_resume();
1634 break;
1635 #if 0
1636 case PCMREC_FLUSH_NUM:
1637 pcmrec_flush((unsigned)ev.data);
1638 break;
1639 #endif
1640 case SYS_USB_CONNECTED:
1641 if (is_recording)
1642 break;
1643 pcmrec_close();
1644 reset_hardware();
1645 /* Be sure other threads are released if waiting */
1646 queue_clear(&pcmrec_queue);
1647 flush_interrupts = 0;
1648 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1649 usb_wait_for_disconnect(&pcmrec_queue);
1650 break;
1651 } /* end switch */
1653 queue_reply(&pcmrec_queue, 0);
1654 } /* end while */
1655 } /* pcmrec_thread */
1657 /****************************************************************************/
1658 /* */
1659 /* following functions will be called by the encoder codec */
1660 /* in a free-threaded manner */
1661 /* */
1662 /****************************************************************************/
1664 /* pass the encoder settings to the encoder */
1665 void enc_get_inputs(struct enc_inputs *inputs)
1667 inputs->sample_rate = sample_rate;
1668 inputs->num_channels = num_channels;
1669 inputs->config = &enc_config;
1670 } /* enc_get_inputs */
1672 /* set the encoder dimensions (called by encoder codec at initialization and
1673 termination) */
1674 void enc_set_parameters(struct enc_parameters *params)
1676 size_t bufsize, resbytes;
1678 logf("enc_set_parameters");
1680 if (!params)
1682 logf("reset");
1683 /* Encoder is terminating */
1684 memset(&enc_config, 0, sizeof (enc_config));
1685 enc_sample_rate = 0;
1686 return;
1689 enc_sample_rate = params->enc_sample_rate;
1690 logf("enc sampr:%lu", enc_sample_rate);
1692 SET_PCM_POS(pcm_rd_pos, dma_wr_pos);
1693 pcm_enc_pos = pcm_rd_pos;
1695 enc_config.afmt = params->afmt;
1696 /* addition of the header is always implied - chunk size 4-byte aligned */
1697 enc_chunk_size =
1698 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1699 enc_events_callback = params->events_callback;
1701 logf("chunk size:%lu", enc_chunk_size);
1703 /*** Configure the buffers ***/
1705 /* Layout of recording buffer:
1706 * [ax] = possible alignment x multiple
1707 * [sx] = possible size alignment of x multiple
1708 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1709 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1711 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1712 logf("resbytes:%lu", resbytes);
1714 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1715 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH
1716 #ifdef PCMREC_PARANOID
1717 - sizeof (*wrap_id_p)
1718 #endif
1721 enc_num_chunks = bufsize / enc_chunk_size;
1722 logf("num chunks:%d", enc_num_chunks);
1724 /* get real amount used by encoder chunks */
1725 bufsize = enc_num_chunks*enc_chunk_size;
1726 logf("enc size:%lu", bufsize);
1728 #ifdef PCMREC_PARANOID
1729 /* add magic at wraparound */
1730 wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize);
1731 bufsize += sizeof (*wrap_id_p);
1732 *wrap_id_p = ENC_CHUNK_MAGIC;
1733 #endif /* PCMREC_PARANOID */
1735 /** set OUT parameters **/
1736 params->enc_buffer = enc_buffer;
1737 params->buf_chunk_size = enc_chunk_size;
1738 params->num_chunks = enc_num_chunks;
1740 /* calculate reserve buffer start and return pointer to encoder */
1741 params->reserve_buffer = NULL;
1742 if (resbytes > 0)
1744 params->reserve_buffer = enc_buffer + bufsize;
1745 bufsize += resbytes;
1748 /* place filename queue at end of buffer using up whatever remains */
1749 fnq_rd_pos = 0; /* reset */
1750 fnq_wr_pos = 0; /* reset */
1751 fn_queue = enc_buffer + bufsize;
1752 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1753 fnq_size /= MAX_PATH;
1754 if (fnq_size > FNQ_MAX_NUM_PATHS)
1755 fnq_size = FNQ_MAX_NUM_PATHS;
1756 fnq_size *= MAX_PATH;
1757 logf("fnq files:%ld", fnq_size / MAX_PATH);
1759 #if 1
1760 logf("ab :%08lX", (uintptr_t)audiobuf);
1761 logf("pcm:%08lX", (uintptr_t)pcm_buffer);
1762 logf("enc:%08lX", (uintptr_t)enc_buffer);
1763 logf("res:%08lX", (uintptr_t)params->reserve_buffer);
1764 #ifdef PCMREC_PARANOID
1765 logf("wip:%08lX", (uintptr_t)wrap_id_p);
1766 #endif
1767 logf("fnq:%08lX", (uintptr_t)fn_queue);
1768 logf("end:%08lX", (uintptr_t)fn_queue + fnq_size);
1769 logf("abe:%08lX", (uintptr_t)audiobufend);
1770 #endif
1772 /* init all chunk headers and reset indexes */
1773 SET_ENC_INDEX(enc_rd_index, 0);
1774 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1776 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index);
1777 #ifdef PCMREC_PARANOID
1778 chunk->id = ENC_CHUNK_MAGIC;
1779 #endif
1780 chunk->flags = 0;
1783 logf("enc_set_parameters done");
1784 } /* enc_set_parameters */
1786 /* return encoder chunk at current write position -
1787 NOTE: can be called by pcmrec thread when splitting streams */
1788 struct enc_chunk_hdr * enc_get_chunk(void)
1790 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1792 #ifdef PCMREC_PARANOID
1793 if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC)
1795 errors |= PCMREC_E_CHUNK_OVF;
1796 logf("finish chk ovf: %d", enc_wr_index);
1798 #endif
1800 chunk->flags &= CHUNKF_START_FILE;
1802 if (!is_recording)
1803 chunk->flags |= CHUNKF_PRERECORD;
1805 return chunk;
1806 } /* enc_get_chunk */
1808 /* releases the current chunk into the available chunks -
1809 NOTE: can be called by pcmrec thread when splitting streams */
1810 void enc_finish_chunk(void)
1812 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1814 if ((long)chunk->flags < 0)
1816 /* encoder set error flag */
1817 errors |= PCMREC_E_ENCODER;
1818 logf("finish chk enc error");
1821 PARANOID_CHUNK_CHECK(chunk);
1823 /* advance enc_wr_index to the next encoder chunk */
1824 INC_ENC_INDEX(enc_wr_index);
1826 if (enc_rd_index != enc_wr_index)
1828 num_rec_bytes += chunk->enc_size;
1829 num_rec_samples += chunk->num_pcm;
1830 #if 0
1831 accum_rec_bytes += chunk->enc_size;
1832 accum_pcm_samples += chunk->num_pcm;
1833 #endif
1835 else if (is_recording) /* buffer full */
1837 /* keep current position and put up warning flag */
1838 warnings |= PCMREC_W_ENC_BUFFER_OVF;
1839 logf("enc_buffer ovf");
1840 DEC_ENC_INDEX(enc_wr_index);
1841 if (pcmrec_context)
1843 /* if stream splitting, keep this out of circulation and
1844 flush a small number, then readd - cannot risk losing
1845 stream markers */
1846 logf("mini flush");
1847 pcmrec_flush(PCMREC_FLUSH_MINI);
1848 INC_ENC_INDEX(enc_wr_index);
1851 else
1853 /* advance enc_rd_index for prerecording */
1854 INC_ENC_INDEX(enc_rd_index);
1856 } /* enc_finish_chunk */
1858 /* checks near empty state on pcm input buffer */
1859 int enc_pcm_buf_near_empty(void)
1861 /* less than 1sec raw data? => unboost encoder */
1862 int wp = dma_wr_pos;
1863 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1864 return avail < (sample_rate << 2) ? 1 : 0;
1865 } /* enc_pcm_buf_near_empty */
1867 /* passes a pointer to next chunk of unprocessed wav data */
1868 /* TODO: this really should give the actual size returned */
1869 unsigned char * enc_get_pcm_data(size_t size)
1871 int wp = dma_wr_pos;
1872 size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK;
1874 /* limit the requested pcm data size */
1875 if (size > PCM_MAX_FEED_SIZE)
1876 size = PCM_MAX_FEED_SIZE;
1878 if (avail >= size)
1880 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1881 int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1883 pcm_enc_pos = pcm_rd_pos;
1885 SET_PCM_POS(pcm_rd_pos, next_pos);
1887 /* ptr must point to continous data at wraparound position */
1888 if ((size_t)pcm_rd_pos < size)
1889 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1890 pcm_buffer, pcm_rd_pos);
1892 pcm_buffer_empty = false;
1893 return ptr;
1896 /* not enough data available - encoder should idle */
1897 pcm_buffer_empty = true;
1898 return NULL;
1899 } /* enc_get_pcm_data */
1901 /* puts some pcm data back in the queue */
1902 size_t enc_unget_pcm_data(size_t size)
1904 int wp = dma_wr_pos;
1905 size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) -
1906 2*PCM_CHUNK_SIZE;
1908 /* allow one interrupt to occur during this call and not have the
1909 new read position inside the DMA destination chunk */
1910 if ((ssize_t)old_avail > 0)
1912 /* limit size to amount of old data remaining */
1913 if (size > old_avail)
1914 size = old_avail;
1916 pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1917 SET_PCM_POS(pcm_rd_pos, pcm_enc_pos);
1919 return size;
1922 return 0;
1923 } /* enc_unget_pcm_data */
1925 /** Low level pcm recording apis **/
1927 /****************************************************************************
1928 * Functions that do not require targeted implementation but only a targeted
1929 * interface
1931 void pcm_record_data(pcm_more_callback_type2 more_ready,
1932 void *start, size_t size)
1934 if (!(start && size))
1935 return;
1937 pcm_callback_more_ready = more_ready;
1938 pcm_rec_dma_start(start, size);
1939 } /* pcm_record_data */
1941 void pcm_stop_recording(void)
1943 if (pcm_recording)
1944 pcm_rec_dma_stop();
1945 } /* pcm_stop_recording */