Make the updated %rg tag match playback behaviour (fall back to track gain if album...
[kugel-rb.git] / firmware / pcm_record.c
blob04289f36ece58590ebbc2c75529ae602d291de6b
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 "panic.h"
23 #include "thread.h"
24 #include <string.h>
25 #include "ata.h"
26 #include "usb.h"
27 #if defined(HAVE_UDA1380)
28 #include "uda1380.h"
29 #include "general.h"
30 #elif defined(HAVE_TLV320)
31 #include "tlv320.h"
32 #endif
33 #include "buffer.h"
34 #include "audio.h"
35 #include "sound.h"
36 #include "id3.h"
37 #ifdef HAVE_SPDIF_IN
38 #include "spdif.h"
39 #endif
41 /***************************************************************************/
43 /**
44 * APIs implemented in the target tree portion:
45 * Public -
46 * pcm_init_recording
47 * pcm_close_recording
48 * pcm_rec_mux
49 * Semi-private -
50 * pcm_rec_dma_start
51 * pcm_rec_dma_stop
54 /** These items may be implemented target specifically or need to
55 be shared semi-privately **/
57 /* the registered callback function for when more data is available */
58 pcm_more_callback_type pcm_callback_more_ready = NULL;
59 /* DMA transfer in is currently active */
60 bool pcm_recording = false;
62 /* APIs implemented in the target-specific portion */
63 void pcm_rec_dma_start(const void *addr, size_t size);
64 void pcm_rec_dma_stop(void);
66 /** General recording state **/
67 static bool is_recording; /* We are recording */
68 static bool is_paused; /* We have paused */
69 static bool is_stopping; /* We are currently stopping */
70 static bool is_error; /* An error has occured */
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 **/
77 static unsigned long long accum_rec_bytes; /* total size written to chunks */
78 static unsigned long long accum_pcm_samples; /* total pcm count processed */
80 /* Keeps data about current file and is sent as event data for codec */
81 static struct enc_file_event_data rec_fdata IDATA_ATTR =
83 .chunk = NULL,
84 .new_enc_size = 0,
85 .new_num_pcm = 0,
86 .rec_file = -1,
87 .num_pcm_samples = 0
90 /** These apply to current settings **/
91 static int rec_source; /* current rec_source setting */
92 static int rec_frequency; /* current frequency setting */
93 static unsigned long sample_rate; /* Sample rate in HZ */
94 static int num_channels; /* Current number of channels */
95 static struct encoder_config enc_config; /* Current encoder configuration */
97 /****************************************************************************
98 use 2 circular buffers:
99 pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
100 enc_buffer=encoded audio buffer: storage for encoder output data
102 Flow:
103 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer
104 2. if enough pcm data are available the encoder codec does encoding of pcm
105 chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread
106 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk
108 Functions calls (basic encoder steps):
109 1.main: audio_load_encoder(); start the encoder
110 2.encoder: enc_get_inputs(); get encoder recording settings
111 3.encoder: enc_set_parameters(); set the encoder parameters
112 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data
113 5.encoder: enc_pcm_buf_near_empty(); if 1: reduce cpu_boost
114 6.encoder: enc_alloc_chunk(); get a ptr to next enc chunk
115 7.encoder: <process enc chunk> compress and store data to enc chunk
116 8.encoder: enc_free_chunk(); inform main about chunk process finished
117 9.encoder: repeat 4. to 8.
118 A.pcmrec: enc_events_callback(); called for certain events
119 ****************************************************************************/
121 /** buffer parameters where incoming PCM data is placed **/
122 #define PCM_NUM_CHUNKS 256 /* Power of 2 */
123 #define PCM_CHUNK_SIZE 8192 /* Power of 2 */
124 #define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1)
126 #define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset)))
127 #define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index))
129 #define INC_ENC_INDEX(index) \
130 { if (++index >= enc_num_chunks) index = 0; }
131 #define DEC_ENC_INDEX(index) \
132 { if (--index < 0) index = enc_num_chunks - 1; }
134 static size_t rec_buffer_size; /* size of available buffer */
135 static unsigned char *pcm_buffer; /* circular recording buffer */
136 static unsigned char *enc_buffer; /* circular encoding buffer */
137 static volatile int dma_wr_pos; /* current DMA write pos */
138 static int pcm_rd_pos; /* current PCM read pos */
139 static volatile bool dma_lock; /* lock DMA write position */
140 static unsigned long pre_record_ticks;/* pre-record time in ticks */
141 static int enc_wr_index; /* encoder chunk write index */
142 static int enc_rd_index; /* encoder chunk read index */
143 static int enc_num_chunks; /* number of chunks in ringbuffer */
144 static size_t enc_chunk_size; /* maximum encoder chunk size */
145 static size_t enc_data_size; /* maximum data size for encoder */
146 static unsigned long enc_sample_rate; /* sample rate used by encoder */
147 static bool wav_queue_empty; /* all wav chunks processed? */
149 /** file flushing **/
150 static int write_threshold; /* max chunk limit for data flush */
151 static int panic_threshold; /* boost thread prio when here */
152 static int spinup_time = -1;/* last ata_spinup_time */
154 /** encoder events **/
155 static void (*enc_events_callback)(enum enc_events event, void *data);
157 /** Path queue for files to write **/
158 #define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */
159 static unsigned char *fn_queue; /* pointer to first filename */
160 static ssize_t fnq_size; /* capacity of queue in bytes */
161 static int fnq_rd_pos; /* current read position */
162 static int fnq_wr_pos; /* current write position */
164 /***************************************************************************/
166 static struct event_queue pcmrec_queue;
167 static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)];
168 static const char pcmrec_thread_name[] = "pcmrec";
170 static void pcmrec_thread(void);
172 /* Event values which are also single-bit flags */
173 #define PCMREC_INIT 0x00000001 /* enable recording */
174 #define PCMREC_CLOSE 0x00000002
176 #define PCMREC_START 0x00000004 /* start recording (when stopped) */
177 #define PCMREC_STOP 0x00000008 /* stop the current recording */
178 #define PCMREC_PAUSE 0x00000010 /* pause the current recording */
179 #define PCMREC_RESUME 0x00000020 /* resume the current recording */
180 #define PCMREC_NEW_FILE 0x00000040 /* start new file (when recording) */
181 #define PCMREC_SET_GAIN 0x00000080
182 #define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */
183 #define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */
185 /* mask for signaling events */
186 static volatile long pcm_thread_event_mask;
188 static void pcm_thread_sync_post(long event, void *data)
190 pcm_thread_event_mask &= ~event;
191 queue_post(&pcmrec_queue, event, data);
192 while(!(event & pcm_thread_event_mask))
193 yield();
194 } /* pcm_thread_sync_post */
196 static inline void pcm_thread_signal_event(long event)
198 pcm_thread_event_mask |= event;
199 } /* pcm_thread_signal_event */
201 static inline void pcm_thread_unsignal_event(long event)
203 pcm_thread_event_mask &= ~event;
204 } /* pcm_thread_unsignal_event */
206 static inline bool pcm_thread_event_state(long signaled, long unsignaled)
208 return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled;
209 } /* pcm_thread_event_state */
211 static void pcm_thread_wait_for_stop(void)
213 if (is_stopping)
215 logf("waiting for stop to finish");
216 while (is_stopping)
217 yield();
219 } /* pcm_thread_wait_for_stop */
221 /*******************************************************************/
222 /* Functions that are not executing in the pcmrec_thread first */
223 /*******************************************************************/
225 /* Callback for when more data is ready */
226 static void pcm_rec_have_more(unsigned char **data, size_t *size)
228 if (*size != 0)
230 /* some error condition */
231 if (*size == DMA_REC_ERROR_DMA)
233 /* Flush recorded data to disk and stop recording */
234 queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
235 return;
237 /* else try again next transmission */
239 else if (!dma_lock)
241 /* advance write position */
242 dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
245 *data = (unsigned char *)GET_PCM_CHUNK(dma_wr_pos);
246 *size = PCM_CHUNK_SIZE;
247 } /* pcm_rec_have_more */
249 /** pcm_rec_* group **/
250 void pcm_rec_error_clear(void)
252 is_error = false;
253 } /* pcm_rec_error_clear */
255 unsigned long pcm_rec_status(void)
257 unsigned long ret = 0;
259 if (is_recording)
260 ret |= AUDIO_STATUS_RECORD;
262 if (is_paused)
263 ret |= AUDIO_STATUS_PAUSE;
265 if (is_error)
266 ret |= AUDIO_STATUS_ERROR;
268 if (!is_recording && pre_record_ticks &&
269 pcm_thread_event_state(PCMREC_INIT, PCMREC_CLOSE))
270 ret |= AUDIO_STATUS_PRERECORD;
272 return ret;
273 } /* pcm_rec_status */
275 int pcm_rec_current_bitrate(void)
277 if (accum_pcm_samples == 0)
278 return 0;
280 return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
281 } /* pcm_rec_current_bitrate */
283 int pcm_rec_encoder_afmt(void)
285 return enc_config.afmt;
286 } /* pcm_rec_encoder_afmt */
288 int pcm_rec_rec_format(void)
290 return afmt_rec_format[enc_config.afmt];
291 } /* pcm_rec_rec_format */
293 unsigned long pcm_rec_sample_rate(void)
295 /* Which is better ?? */
296 #if 0
297 return enc_sample_rate;
298 #endif
299 return sample_rate;
300 } /* audio_get_sample_rate */
303 * Creates pcmrec_thread
305 void pcm_rec_init(void)
307 queue_init(&pcmrec_queue, true);
308 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
309 pcmrec_thread_name, PRIORITY_RECORDING);
310 } /* pcm_rec_init */
312 /** audio_* group **/
314 void audio_init_recording(unsigned int buffer_offset)
316 (void)buffer_offset;
317 pcm_thread_wait_for_stop();
318 pcm_thread_sync_post(PCMREC_INIT, NULL);
319 } /* audio_init_recording */
321 void audio_close_recording(void)
323 pcm_thread_wait_for_stop();
324 pcm_thread_sync_post(PCMREC_CLOSE, NULL);
325 /* reset pcm to defaults (playback only) */
326 pcm_set_frequency(-1);
327 pcm_set_monitor(-1);
328 pcm_set_rec_source(-1);
329 #ifdef HAVE_TLV320
330 /* tlv320 screeches if left at 88.2 with no inputs */
331 pcm_apply_settings(true);
332 #endif
333 audio_remove_encoder();
334 } /* audio_close_recording */
336 unsigned long audio_recorded_time(void)
338 if (!is_recording || enc_sample_rate == 0)
339 return 0;
341 /* return actual recorded time a la encoded data even if encoder rate
342 doesn't match the pcm rate */
343 return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate);
344 } /* audio_recorded_time */
346 unsigned long audio_num_recorded_bytes(void)
348 if (!is_recording)
349 return 0;
351 return num_rec_bytes;
352 } /* audio_num_recorded_bytes */
354 #ifdef HAVE_SPDIF_IN
356 * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
357 * our reading on the actual SPDIF sample rate (which might be a bit
358 * inaccurate), we round off to the closest sample rate that is supported by
359 * SPDIF.
361 int audio_get_spdif_sample_rate(void)
363 unsigned long measured_rate = spdif_measure_frequency();
364 /* Find which SPDIF sample rate we're closest to. */
365 return round_value_to_list32(measured_rate, audio_master_sampr_list,
366 SAMPR_NUM_FREQ, false);
367 } /* audio_get_spdif_sample_rate */
368 #endif /* HAVE_SPDIF_IN */
371 * Sets recording parameters
373 void audio_set_recording_options(struct audio_recording_options *options)
375 pcm_thread_wait_for_stop();
377 /* stop DMA transfer */
378 dma_lock = true;
379 pcm_stop_recording();
381 rec_frequency = options->rec_frequency;
382 rec_source = options->rec_source;
383 num_channels = options->rec_channels == 1 ? 1 : 2;
384 pre_record_ticks = options->rec_prerecord_time * HZ;
385 enc_config = options->enc_config;
386 enc_config.afmt = rec_format_afmt[enc_config.rec_format];
388 #ifdef HAVE_SPDIF_IN
389 if (rec_source == AUDIO_SRC_SPDIF)
391 /* must measure SPDIF sample rate before configuring codecs */
392 unsigned long sr = spdif_measure_frequency();
393 /* round to master list for SPDIF rate */
394 int index = round_value_to_list32(sr, audio_master_sampr_list,
395 SAMPR_NUM_FREQ, false);
396 sample_rate = audio_master_sampr_list[index];
397 /* round to HW playback rates for monitoring */
398 index = round_value_to_list32(sr, hw_freq_sampr,
399 HW_NUM_FREQ, false);
400 pcm_set_frequency(hw_freq_sampr[index]);
401 /* encoders with a limited number of rates do their own rounding */
403 else
404 #endif
406 /* set sample rate from frequency selection */
407 sample_rate = rec_freq_sampr[rec_frequency];
408 pcm_set_frequency(sample_rate);
411 pcm_set_monitor(rec_source);
412 pcm_set_rec_source(rec_source);
414 /* apply pcm settings to hardware */
415 pcm_apply_settings(true);
417 if (audio_load_encoder(enc_config.afmt))
419 /* start DMA transfer */
420 pcm_record_data(pcm_rec_have_more, NULL, 0);
421 /* do unlock after starting to prevent preincrement of dma_wr_pos */
422 dma_lock = pre_record_ticks == 0;
424 else
426 logf("set rec opt: enc load failed");
427 is_error = true;
429 } /* audio_set_recording_options */
432 * Note that microphone is mono, only left value is used
433 * See {uda1380,tlv320}_set_recvol() for exact ranges.
435 * @param type 0=line-in (radio), 1=mic
438 void audio_set_recording_gain(int left, int right, int type)
440 //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
441 #if defined(HAVE_UDA1380)
442 uda1380_set_recvol(left, right, type);
443 #elif defined (HAVE_TLV320)
444 tlv320_set_recvol(left, right, type);
445 #endif
446 } /* audio_set_recording_gain */
449 * Start recording
451 * Use audio_set_recording_options first to select recording options
453 void audio_record(const char *filename)
455 logf("audio_record: %s", filename);
457 pcm_thread_wait_for_stop();
458 pcm_thread_sync_post(PCMREC_START, (void *)filename);
460 logf("audio_record_done");
461 } /* audio_record */
463 void audio_new_file(const char *filename)
465 logf("audio_new_file: %s", filename);
467 pcm_thread_wait_for_stop();
468 pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename);
470 logf("audio_new_file done");
471 } /* audio_new_file */
473 void audio_stop_recording(void)
475 logf("audio_stop_recording");
477 pcm_thread_wait_for_stop();
479 if (is_recording)
480 dma_lock = true; /* fix DMA write ptr at current position */
482 pcm_thread_sync_post(PCMREC_STOP, NULL);
484 logf("audio_stop_recording done");
485 } /* audio_stop_recording */
487 void audio_pause_recording(void)
489 logf("audio_pause_recording");
491 pcm_thread_wait_for_stop();
493 if (is_recording)
494 dma_lock = true; /* fix DMA write ptr at current position */
496 pcm_thread_sync_post(PCMREC_PAUSE, NULL);
497 logf("audio_pause_recording done");
498 } /* audio_pause_recording */
500 void audio_resume_recording(void)
502 logf("audio_resume_recording");
504 pcm_thread_wait_for_stop();
505 pcm_thread_sync_post(PCMREC_RESUME, NULL);
507 logf("audio_resume_recording done");
508 } /* audio_resume_recording */
510 /***************************************************************************/
511 /* */
512 /* Functions that execute in the context of pcmrec_thread */
513 /* */
514 /***************************************************************************/
516 /** Filename Queue **/
518 /* returns true if the queue is empty */
519 static inline bool pcmrec_fnq_is_empty(void)
521 return fnq_rd_pos == fnq_wr_pos;
522 } /* pcmrec_fnq_is_empty */
524 /* empties the filename queue */
525 static inline void pcmrec_fnq_set_empty(void)
527 fnq_rd_pos = fnq_wr_pos;
528 } /* pcmrec_fnq_set_empty */
530 /* returns true if the queue is full */
531 static bool pcmrec_fnq_is_full(void)
533 ssize_t size = fnq_wr_pos - fnq_rd_pos;
534 if (size < 0)
535 size += fnq_size;
537 return size >= fnq_size - MAX_PATH;
538 } /* pcmrec_fnq_is_full */
540 /* queue another filename - will overwrite oldest one if full */
541 static bool pcmrec_fnq_add_filename(const char *filename)
543 strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH);
545 if ((fnq_wr_pos += MAX_PATH) >= fnq_size)
546 fnq_wr_pos = 0;
548 if (fnq_rd_pos != fnq_wr_pos)
549 return true;
551 /* queue full */
552 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
553 fnq_rd_pos = 0;
555 return true;
556 } /* pcmrec_fnq_add_filename */
558 /* replace the last filename added */
559 static bool pcmrec_fnq_replace_tail(const char *filename)
561 int pos;
563 if (pcmrec_fnq_is_empty())
564 return false;
566 pos = fnq_wr_pos - MAX_PATH;
567 if (pos < 0)
568 pos = fnq_size - MAX_PATH;
570 strncpy(fn_queue + pos, filename, MAX_PATH);
572 return true;
573 } /* pcmrec_fnq_replace_tail */
575 /* pulls the next filename from the queue */
576 static bool pcmrec_fnq_get_filename(char *filename)
578 if (pcmrec_fnq_is_empty())
579 return false;
581 if (filename)
582 strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH);
584 if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
585 fnq_rd_pos = 0;
587 return true;
588 } /* pcmrec_fnq_get_filename */
590 /* close the file number pointed to by fd_p */
591 static void pcmrec_close_file(int *fd_p)
593 if (*fd_p < 0)
594 return; /* preserve error */
596 close(*fd_p);
597 *fd_p = -1;
598 } /* pcmrec_close_file */
600 /** Data Flushing **/
603 * called after callback to update sizes if codec changed the amount of data
604 * a chunk represents
606 static inline void pcmrec_update_sizes_inl(size_t prev_enc_size,
607 unsigned long prev_num_pcm)
609 if (rec_fdata.new_enc_size != prev_enc_size)
611 ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
612 num_rec_bytes += size_diff;
613 accum_rec_bytes += size_diff;
616 if (rec_fdata.new_num_pcm != prev_num_pcm)
618 unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
619 num_rec_samples += pcm_diff;
620 accum_pcm_samples += pcm_diff;
622 } /* pcmrec_update_sizes_inl */
624 /* don't need to inline every instance */
625 static void pcmrec_update_sizes(size_t prev_enc_size,
626 unsigned long prev_num_pcm)
628 pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm);
629 } /* pcmrec_update_sizes */
631 static void pcmrec_start_file(void)
633 size_t enc_size = rec_fdata.new_enc_size;
634 unsigned long num_pcm = rec_fdata.new_num_pcm;
635 int curr_rec_file = rec_fdata.rec_file;
636 char filename[MAX_PATH];
638 /* must always pull the filename that matches with this queue */
639 if (!pcmrec_fnq_get_filename(filename))
641 logf("start file: fnq empty");
642 *filename = '\0';
643 is_error = true;
645 else if (is_error)
647 logf("start file: is_error already");
649 else if (curr_rec_file >= 0)
651 /* Any previous file should have been closed */
652 logf("start file: file already open");
653 is_error = true;
656 if (is_error)
657 rec_fdata.chunk->flags |= CHUNKF_ERROR;
659 /* encoder can set error flag here and should increase
660 enc_new_size and pcm_new_size to reflect additional
661 data written if any */
662 rec_fdata.filename = filename;
663 enc_events_callback(ENC_START_FILE, &rec_fdata);
665 if (!is_error && (rec_fdata.chunk->flags & CHUNKF_ERROR))
667 logf("start file: enc error");
668 is_error = true;
671 if (is_error)
673 pcmrec_close_file(&curr_rec_file);
674 /* Write no more to this file */
675 rec_fdata.chunk->flags |= CHUNKF_END_FILE;
677 else
679 pcmrec_update_sizes(enc_size, num_pcm);
682 rec_fdata.chunk->flags &= ~CHUNKF_START_FILE;
683 } /* pcmrec_start_file */
685 static inline void pcmrec_write_chunk(void)
687 size_t enc_size = rec_fdata.new_enc_size;
688 unsigned long num_pcm = rec_fdata.new_num_pcm;
690 if (is_error)
691 rec_fdata.chunk->flags |= CHUNKF_ERROR;
693 enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata);
695 if ((long)rec_fdata.chunk->flags >= 0)
697 pcmrec_update_sizes_inl(enc_size, num_pcm);
699 else if (!is_error)
701 logf("wr chk enc error %d %d",
702 rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
703 is_error = true;
705 } /* pcmrec_write_chunk */
707 static void pcmrec_end_file(void)
709 /* all data in output buffer for current file will have been
710 written and encoder can now do any nescessary steps to
711 finalize the written file */
712 size_t enc_size = rec_fdata.new_enc_size;
713 unsigned long num_pcm = rec_fdata.new_num_pcm;
715 enc_events_callback(ENC_END_FILE, &rec_fdata);
717 if (!is_error)
719 if (rec_fdata.chunk->flags & CHUNKF_ERROR)
721 logf("end file: enc error");
722 is_error = true;
724 else
726 pcmrec_update_sizes(enc_size, num_pcm);
730 /* Force file close if error */
731 if (is_error)
732 pcmrec_close_file(&rec_fdata.rec_file);
734 rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
735 } /* pcmrec_end_file */
738 * Process the chunks
740 * This function is called when queue_get_w_tmo times out.
742 * Set flush_num to the number of files to flush to disk.
743 * flush_num = -1 to flush all available chunks to disk.
744 * flush_num = 0 normal write thresholding
745 * flush_num = 1 or greater - all available chunks of current file plus
746 * flush_num file starts if first chunk has been processed.
749 static void pcmrec_flush(unsigned flush_num)
751 static unsigned long last_flush_tick = 0;
752 unsigned long start_tick;
753 int num_ready, num;
754 int prio;
755 int i;
757 num_ready = enc_wr_index - enc_rd_index;
758 if (num_ready < 0)
759 num_ready += enc_num_chunks;
761 num = num_ready;
763 if (flush_num == 0)
765 if (!is_recording)
766 return;
768 if (ata_spinup_time != spinup_time)
770 /* spinup time has changed, calculate new write threshold */
771 logf("new t spinup : %d", ata_spinup_time);
772 unsigned long st = spinup_time = ata_spinup_time;
774 /* write at 5s + st remaining in enc_buffer */
775 if (st < 2*HZ)
776 st = 2*HZ; /* my drive is usually < 250 ticks :) */
777 else if (st > 10*HZ)
778 st = 10*HZ;
780 write_threshold = enc_num_chunks -
781 (int)(((5ull*HZ + st)*4ull*sample_rate + (enc_chunk_size-1)) /
782 (enc_chunk_size*HZ));
784 if (write_threshold < 0)
785 write_threshold = 0;
786 else if (write_threshold > panic_threshold)
787 write_threshold = panic_threshold;
789 logf("new wr thresh: %d", write_threshold);
792 if (num_ready < write_threshold)
793 return;
795 /* if we're getting called too much and this isn't forced,
796 boost stat */
797 if (current_tick - last_flush_tick < HZ/2)
798 num = panic_threshold;
801 start_tick = current_tick;
802 prio = -1;
804 logf("writing: %d (%d)", num_ready, flush_num);
806 cpu_boost_id(true, CPUBOOSTID_PCMRECORD);
808 for (i=0; i<num_ready; i++)
810 if (prio == -1 && (num >= panic_threshold ||
811 current_tick - start_tick > 10*HZ))
813 /* losing ground - boost priority until finished */
814 logf("pcmrec: boost priority");
815 prio = thread_set_priority(NULL, thread_get_priority(NULL)-1);
818 rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index);
819 rec_fdata.new_enc_size = rec_fdata.chunk->enc_size;
820 rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm;
822 if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
824 pcmrec_start_file();
825 if (--flush_num == 0)
826 i = num_ready; /* stop on next loop - must write this
827 chunk if it has data */
830 pcmrec_write_chunk();
832 if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
833 pcmrec_end_file();
835 INC_ENC_INDEX(enc_rd_index);
837 if (is_error)
838 break;
840 if (prio == -1)
842 num = enc_wr_index - enc_rd_index;
843 if (num < 0)
844 num += enc_num_chunks;
847 /* no yielding, the file apis called in the codecs do that */
848 } /* end for */
850 /* sync file */
851 if (rec_fdata.rec_file >= 0)
852 fsync(rec_fdata.rec_file);
854 cpu_boost_id(false, CPUBOOSTID_PCMRECORD);
856 if (prio != -1)
858 /* return to original priority */
859 logf("pcmrec: unboost priority");
860 thread_set_priority(NULL, prio);
863 last_flush_tick = current_tick; /* save tick when we left */
864 logf("done");
865 } /* pcmrec_flush */
868 * Marks a new stream in the buffer and gives the encoder a chance for special
869 * handling of transition from one to the next. The encoder may change the
870 * chunk that ends the old stream by requesting more chunks and similiarly for
871 * the new but must always advance the position though the interface. It can
872 * later reject any data it cares to when writing the file but should mark the
873 * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept
874 * a NULL data pointer without error as well.
876 static void pcmrec_new_stream(const char *filename, /* next file name */
877 unsigned long flags, /* CHUNKF_* flags */
878 int pre_index) /* index for prerecorded data */
880 logf("pcmrec_new_stream");
882 struct enc_buffer_event_data data;
883 bool (*fnq_add_fn)(const char *) = NULL;
884 struct enc_chunk_hdr *start = NULL;
886 int get_chunk_index(struct enc_chunk_hdr *chunk)
888 return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
891 struct enc_chunk_hdr * get_prev_chunk(int index)
893 DEC_ENC_INDEX(index);
894 return GET_ENC_CHUNK(index);
897 data.pre_chunk = NULL;
898 data.chunk = GET_ENC_CHUNK(enc_wr_index);
900 /* end chunk */
901 if (flags & CHUNKF_END_FILE)
903 data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
905 if (data.chunk->flags & CHUNKF_START_FILE)
907 /* cannot start and end on same unprocessed chunk */
908 logf("file end on start");
909 flags &= ~CHUNKF_END_FILE;
911 else if (enc_rd_index == enc_wr_index)
913 /* all data flushed but file not ended - chunk will be left
914 empty */
915 logf("end on dead end");
916 data.chunk->flags = 0;
917 data.chunk->enc_size = 0;
918 data.chunk->num_pcm = 0;
919 data.chunk->enc_data = NULL;
920 INC_ENC_INDEX(enc_wr_index);
921 data.chunk = GET_ENC_CHUNK(enc_wr_index);
923 else
925 struct enc_chunk_hdr *last = get_prev_chunk(enc_wr_index);
927 if (last->flags & CHUNKF_END_FILE)
929 /* end already processed and marked - can't end twice */
930 logf("file end again");
931 flags &= ~CHUNKF_END_FILE;
936 /* start chunk */
937 if (flags & CHUNKF_START_FILE)
939 bool pre = flags & CHUNKF_PRERECORD;
941 if (pre)
943 logf("stream prerecord start");
944 start = data.pre_chunk = GET_ENC_CHUNK(pre_index);
945 start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD;
947 else
949 logf("stream normal start");
950 start = data.chunk;
951 start->flags &= CHUNKF_START_FILE;
954 /* if encoder hasn't yet processed the last start - abort the start
955 of the previous file queued or else it will be empty and invalid */
956 if (start->flags & CHUNKF_START_FILE)
958 logf("replacing fnq tail: %s", filename);
959 fnq_add_fn = pcmrec_fnq_replace_tail;
961 else
963 logf("adding filename: %s", filename);
964 fnq_add_fn = pcmrec_fnq_add_filename;
968 data.flags = flags;
969 enc_events_callback(ENC_REC_NEW_STREAM, &data);
971 if (flags & CHUNKF_END_FILE)
973 int i = get_chunk_index(data.chunk);
974 get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
977 if (start)
979 if (!(flags & CHUNKF_PRERECORD))
981 /* get stats on data added to start - sort of a prerecord operation */
982 int i = get_chunk_index(data.chunk);
983 struct enc_chunk_hdr *chunk = data.chunk;
985 logf("start data: %d %d", i, enc_wr_index);
987 num_rec_bytes = 0;
988 num_rec_samples = 0;
990 while (i != enc_wr_index)
992 num_rec_bytes += chunk->enc_size;
993 num_rec_samples += chunk->num_pcm;
994 INC_ENC_INDEX(i);
995 chunk = GET_ENC_CHUNK(i);
998 start->flags &= ~CHUNKF_START_FILE;
999 start = data.chunk;
1002 start->flags |= CHUNKF_START_FILE;
1004 /* flush one file out if full and adding */
1005 if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
1007 logf("fnq full: flushing 1");
1008 pcmrec_flush(1);
1011 fnq_add_fn(filename);
1013 } /* pcmrec_new_stream */
1015 /** event handlers for pcmrec thread */
1017 /* PCMREC_INIT */
1018 static void pcmrec_init(void)
1020 rec_fdata.rec_file = -1;
1022 /* pcm FIFO */
1023 dma_lock = true;
1024 pcm_rd_pos = 0;
1025 dma_wr_pos = 0;
1027 /* encoder FIFO */
1028 enc_wr_index = 0;
1029 enc_rd_index = 0;
1031 /* filename queue */
1032 fnq_rd_pos = 0;
1033 fnq_wr_pos = 0;
1035 /* stats */
1036 num_rec_bytes = 0;
1037 num_rec_samples = 0;
1038 accum_rec_bytes = 0;
1039 accum_pcm_samples = 0;
1041 pcm_thread_unsignal_event(PCMREC_CLOSE);
1042 is_recording = false;
1043 is_paused = false;
1044 is_stopping = false;
1045 is_error = false;
1047 pcm_buffer = audio_get_recording_buffer(&rec_buffer_size);
1048 /* Line align pcm_buffer 2^4=16 bytes */
1049 pcm_buffer = (unsigned char *)ALIGN_UP_P2((unsigned)pcm_buffer, 4);
1050 enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
1051 PCM_MAX_FEED_SIZE, 2);
1053 pcm_init_recording();
1054 pcm_thread_signal_event(PCMREC_INIT);
1055 } /* pcmrec_init */
1057 /* PCMREC_CLOSE */
1058 static void pcmrec_close(void)
1060 dma_lock = true;
1061 pcm_close_recording();
1062 pcm_thread_unsignal_event(PCMREC_INIT);
1063 pcm_thread_signal_event(PCMREC_CLOSE);
1064 } /* pcmrec_close */
1066 /* PCMREC_START */
1067 static void pcmrec_start(const char *filename)
1069 unsigned long pre_sample_ticks;
1070 int rd_start;
1072 logf("pcmrec_start: %s", filename);
1074 if (is_recording)
1076 logf("already recording");
1077 goto already_recording;
1080 /* reset stats */
1081 num_rec_bytes = 0;
1082 num_rec_samples = 0;
1083 accum_rec_bytes = 0;
1084 accum_pcm_samples = 0;
1085 spinup_time = -1;
1087 rd_start = enc_wr_index;
1088 pre_sample_ticks = 0;
1090 if (pre_record_ticks)
1092 int i;
1094 /* calculate number of available chunks */
1095 unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index +
1096 enc_num_chunks) % enc_num_chunks;
1097 /* overflow at 974 seconds of prerecording at 44.1kHz */
1098 unsigned long pre_record_sample_ticks = enc_sample_rate*pre_record_ticks;
1100 /* Get exact measure of recorded data as number of samples aren't
1101 nescessarily going to be the max for each chunk */
1102 for (i = rd_start; avail_pre_chunks-- > 0;)
1104 struct enc_chunk_hdr *chunk;
1105 unsigned long chunk_sample_ticks;
1107 DEC_ENC_INDEX(i);
1109 chunk = GET_ENC_CHUNK(i);
1111 /* must have data to be counted */
1112 if (chunk->enc_data == NULL)
1113 continue;
1115 chunk_sample_ticks = chunk->num_pcm*HZ;
1117 rd_start = i;
1118 pre_sample_ticks += chunk_sample_ticks;
1119 num_rec_bytes += chunk->enc_size;
1120 num_rec_samples += chunk->num_pcm;
1122 /* stop here if enough already */
1123 if (pre_sample_ticks >= pre_record_sample_ticks)
1124 break;
1127 accum_rec_bytes = num_rec_bytes;
1128 accum_pcm_samples = num_rec_samples;
1131 enc_rd_index = rd_start;
1133 /* filename queue should be empty */
1134 if (!pcmrec_fnq_is_empty())
1136 logf("fnq: not empty!");
1137 pcmrec_fnq_set_empty();
1140 dma_lock = false;
1141 is_paused = false;
1142 is_recording = true;
1144 pcmrec_new_stream(filename,
1145 CHUNKF_START_FILE |
1146 (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0),
1147 enc_rd_index);
1149 already_recording:
1150 pcm_thread_signal_event(PCMREC_START);
1151 logf("pcmrec_start done");
1152 } /* pcmrec_start */
1154 /* PCMREC_STOP */
1155 static void pcmrec_stop(void)
1157 logf("pcmrec_stop");
1159 if (!is_recording)
1161 logf("not recording");
1162 goto not_recording_or_stopping;
1164 else if (is_stopping)
1166 logf("already stopping");
1167 goto not_recording_or_stopping;
1170 is_stopping = true;
1171 dma_lock = true; /* lock dma write position */
1172 queue_post(&pcmrec_queue, PCMREC_FINISH_STOP, NULL);
1174 not_recording_or_stopping:
1175 pcm_thread_signal_event(PCMREC_STOP);
1176 logf("pcmrec_stop done");
1177 } /* pcmrec_stop */
1179 /* PCMREC_FINISH_STOP */
1180 static void pcmrec_finish_stop(void)
1182 logf("pcmrec_finish_stop");
1184 if (!is_stopping)
1186 logf("not stopping");
1187 goto not_stopping;
1190 /* flush all available data first to avoid overflow while waiting
1191 for encoding to finish */
1192 pcmrec_flush(-1);
1194 /* wait for encoder to finish remaining data */
1195 if (!is_error)
1197 while (!wav_queue_empty)
1198 yield();
1201 /* end stream at last data */
1202 pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
1204 /* flush anything else encoder added */
1205 pcmrec_flush(-1);
1207 /* remove any pending file start not yet processed - should be at
1208 most one at enc_wr_index */
1209 pcmrec_fnq_get_filename(NULL);
1210 /* encoder should abort any chunk it was in midst of processing */
1211 GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT;
1213 /* filename queue should be empty */
1214 if (!pcmrec_fnq_is_empty())
1216 logf("fnq: not empty!");
1217 pcmrec_fnq_set_empty();
1220 /* be absolutely sure the file is closed */
1221 if (is_error)
1222 pcmrec_close_file(&rec_fdata.rec_file);
1223 rec_fdata.rec_file = -1;
1225 is_recording = false;
1226 is_paused = false;
1227 is_stopping = false;
1228 dma_lock = pre_record_ticks == 0;
1230 not_stopping:
1231 logf("pcmrec_finish_stop done");
1232 } /* pcmrec_finish_stop */
1234 /* PCMREC_PAUSE */
1235 static void pcmrec_pause(void)
1237 logf("pcmrec_pause");
1239 if (!is_recording)
1241 logf("not recording");
1242 goto not_recording_or_paused;
1244 else if (is_paused)
1246 logf("already paused");
1247 goto not_recording_or_paused;
1250 dma_lock = true; /* fix DMA write pointer at current position */
1251 is_paused = true;
1253 not_recording_or_paused:
1254 pcm_thread_signal_event(PCMREC_PAUSE);
1255 logf("pcmrec_pause done");
1256 } /* pcmrec_pause */
1258 /* PCMREC_RESUME */
1259 static void pcmrec_resume(void)
1261 logf("pcmrec_resume");
1263 if (!is_recording)
1265 logf("not recording");
1266 goto not_recording_or_not_paused;
1268 else if (!is_paused)
1270 logf("not paused");
1271 goto not_recording_or_not_paused;
1274 is_paused = false;
1275 is_recording = true;
1276 dma_lock = false;
1278 not_recording_or_not_paused:
1279 pcm_thread_signal_event(PCMREC_RESUME);
1280 logf("pcmrec_resume done");
1281 } /* pcmrec_resume */
1283 /* PCMREC_NEW_FILE */
1284 static void pcmrec_new_file(const char *filename)
1286 logf("pcmrec_new_file: %s", filename);
1288 if (!is_recording)
1290 logf("not recording");
1291 goto not_recording;
1294 num_rec_bytes = 0;
1295 num_rec_samples = 0;
1297 pcmrec_new_stream(filename,
1298 CHUNKF_START_FILE | CHUNKF_END_FILE,
1301 not_recording:
1302 pcm_thread_signal_event(PCMREC_NEW_FILE);
1303 logf("pcmrec_new_file done");
1304 } /* pcmrec_new_file */
1306 static void pcmrec_thread(void) __attribute__((noreturn));
1307 static void pcmrec_thread(void)
1309 struct event ev;
1311 logf("thread pcmrec start");
1313 while(1)
1315 if (is_recording)
1317 /* Poll periodically to flush data */
1318 queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
1320 if (ev.id == SYS_TIMEOUT)
1322 pcmrec_flush(0); /* flush if getting full */
1323 continue;
1326 else
1328 /* Not doing anything - sit and wait for commands */
1329 queue_wait(&pcmrec_queue, &ev);
1332 switch (ev.id)
1334 case PCMREC_INIT:
1335 pcmrec_init();
1336 break;
1338 case PCMREC_CLOSE:
1339 pcmrec_close();
1340 break;
1342 case PCMREC_START:
1343 pcmrec_start((const char *)ev.data);
1344 break;
1346 case PCMREC_STOP:
1347 pcmrec_stop();
1348 break;
1350 case PCMREC_FINISH_STOP:
1351 pcmrec_finish_stop();
1352 break;
1354 case PCMREC_PAUSE:
1355 pcmrec_pause();
1356 break;
1358 case PCMREC_RESUME:
1359 pcmrec_resume();
1360 break;
1362 case PCMREC_NEW_FILE:
1363 pcmrec_new_file((const char *)ev.data);
1364 break;
1366 case PCMREC_FLUSH_NUM:
1367 pcmrec_flush((unsigned)ev.data);
1368 break;
1370 case SYS_USB_CONNECTED:
1371 if (!is_recording)
1373 pcmrec_close();
1374 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1375 usb_wait_for_disconnect(&pcmrec_queue);
1377 break;
1378 } /* end switch */
1379 } /* end while */
1380 } /* pcmrec_thread */
1382 /****************************************************************************/
1383 /* */
1384 /* following functions will be called by the encoder codec */
1385 /* */
1386 /****************************************************************************/
1388 /* pass the encoder settings to the encoder */
1389 void enc_get_inputs(struct enc_inputs *inputs)
1391 inputs->sample_rate = sample_rate;
1392 inputs->num_channels = num_channels;
1393 inputs->config = &enc_config;
1394 } /* enc_get_inputs */
1396 /* set the encoder dimensions (called by encoder codec at initialization and
1397 termination) */
1398 void enc_set_parameters(struct enc_parameters *params)
1400 size_t bufsize, resbytes;
1402 logf("enc_set_parameters");
1404 if (!params)
1406 logf("reset");
1407 /* Encoder is terminating */
1408 memset(&enc_config, 0, sizeof (enc_config));
1409 enc_sample_rate = 0;
1410 return;
1413 enc_sample_rate = params->enc_sample_rate;
1414 logf("enc sampr:%d", enc_sample_rate);
1416 pcm_rd_pos = dma_wr_pos;
1418 enc_config.afmt = params->afmt;
1419 /* addition of the header is always implied - chunk size 4-byte aligned */
1420 enc_chunk_size =
1421 ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2);
1422 enc_data_size = enc_chunk_size - ENC_CHUNK_HDR_SIZE;
1423 enc_events_callback = params->events_callback;
1425 logf("chunk size:%d", enc_chunk_size);
1427 /*** Configure the buffers ***/
1429 /* Layout of recording buffer:
1430 * [ax] = possible alignment x multiple
1431 * [sx] = possible size alignment of x multiple
1432 * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|->
1433 * |[[s4]:Reserved Bytes]|Filename Queue->|[space]|
1435 resbytes = ALIGN_UP_P2(params->reserve_bytes, 2);
1436 logf("resbytes:%d", resbytes);
1438 bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
1439 resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH;
1441 enc_num_chunks = bufsize / enc_chunk_size;
1442 logf("num chunks:%d", enc_num_chunks);
1444 /* get real amount used by encoder chunks */
1445 bufsize = enc_num_chunks*enc_chunk_size;
1446 logf("enc size:%d", bufsize);
1448 /* panic boost thread priority at 1 second remaining */
1449 panic_threshold = enc_num_chunks -
1450 (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size;
1451 if (panic_threshold < 0)
1452 panic_threshold = 0;
1454 logf("panic thr:%d", panic_threshold);
1456 /** set OUT parameters **/
1457 params->enc_buffer = enc_buffer;
1458 params->buf_chunk_size = enc_chunk_size;
1459 params->num_chunks = enc_num_chunks;
1461 /* calculate reserve buffer start and return pointer to encoder */
1462 params->reserve_buffer = NULL;
1463 if (resbytes > 0)
1465 params->reserve_buffer = enc_buffer + bufsize;
1466 bufsize += resbytes;
1469 /* place filename queue at end of buffer using up whatever remains */
1470 fnq_rd_pos = 0; /* reset */
1471 fnq_wr_pos = 0; /* reset */
1472 fn_queue = enc_buffer + bufsize;
1473 fnq_size = pcm_buffer + rec_buffer_size - fn_queue;
1474 fnq_size = ALIGN_DOWN(fnq_size, MAX_PATH);
1475 logf("fnq files: %d", fnq_size / MAX_PATH);
1477 #if 0
1478 logf("ab :%08X", (unsigned long)audiobuf);
1479 logf("pcm:%08X", (unsigned long)pcm_buffer);
1480 logf("enc:%08X", (unsigned long)enc_buffer);
1481 logf("res:%08X", (unsigned long)params->reserve_buffer);
1482 logf("fnq:%08X", (unsigned long)fn_queue);
1483 logf("end:%08X", (unsigned long)fn_queue + fnq_size);
1484 logf("abe:%08X", (unsigned long)audiobufend);
1485 #endif
1487 /* init all chunk headers and reset indexes */
1488 enc_rd_index = 0;
1489 for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
1490 GET_ENC_CHUNK(--enc_wr_index)->flags = 0;
1492 logf("enc_set_parameters done");
1493 } /* enc_set_parameters */
1495 /* return encoder chunk at current write position */
1496 struct enc_chunk_hdr * enc_get_chunk(void)
1498 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1499 chunk->flags &= CHUNKF_START_FILE;
1501 if (!is_recording)
1502 chunk->flags |= CHUNKF_PRERECORD;
1504 return chunk;
1505 } /* enc_get_chunk */
1507 /* releases the current chunk into the available chunks */
1508 void enc_finish_chunk(void)
1510 struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
1512 /* encoder may have set error flag or written too much data */
1513 if ((long)chunk->flags < 0 || chunk->enc_size > enc_data_size)
1515 is_error = true;
1517 #ifdef ROCKBOX_HAS_LOGF
1518 if (chunk->enc_size > enc_data_size)
1520 /* illegal to scribble over next chunk */
1521 logf("finish chk ovf: %d>%d", chunk->enc_size, enc_data_size);
1523 else
1525 /* encoder set error flag */
1526 logf("finish chk enc error");
1528 #endif
1531 /* advance enc_wr_index to the next encoder chunk */
1532 INC_ENC_INDEX(enc_wr_index);
1534 if (enc_rd_index != enc_wr_index)
1536 num_rec_bytes += chunk->enc_size;
1537 accum_rec_bytes += chunk->enc_size;
1538 num_rec_samples += chunk->num_pcm;
1539 accum_pcm_samples += chunk->num_pcm;
1541 else if (is_recording) /* buffer full */
1543 /* keep current position */
1544 logf("enc_buffer ovf");
1545 DEC_ENC_INDEX(enc_wr_index);
1547 else
1549 /* advance enc_rd_index for prerecording */
1550 INC_ENC_INDEX(enc_rd_index);
1552 } /* enc_finish_chunk */
1554 /* checks near empty state on pcm input buffer */
1555 int enc_pcm_buf_near_empty(void)
1557 /* less than 1sec raw data? => unboost encoder */
1558 size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK;
1559 return avail < (sample_rate << 2) ? 1 : 0;
1560 } /* enc_pcm_buf_near_empty */
1562 /* passes a pointer to next chunk of unprocessed wav data */
1563 /* TODO: this really should give the actual size returned */
1564 unsigned char * enc_get_pcm_data(size_t size)
1566 size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK;
1568 /* limit the requested pcm data size */
1569 if (size > PCM_MAX_FEED_SIZE)
1570 size = PCM_MAX_FEED_SIZE;
1572 if (avail >= size)
1574 unsigned char *ptr = pcm_buffer + pcm_rd_pos;
1575 pcm_rd_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK;
1577 /* ptr must point to continous data at wraparound position */
1578 if ((size_t)pcm_rd_pos < size)
1579 memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
1580 pcm_buffer, pcm_rd_pos);
1582 wav_queue_empty = false;
1583 return ptr;
1586 /* not enough data available - encoder should idle */
1587 wav_queue_empty = true;
1588 return NULL;
1589 } /* enc_get_pcm_data */
1591 /* puts some pcm data back in the queue */
1592 size_t enc_unget_pcm_data(size_t size)
1594 /* can't let DMA advance write position when doing this */
1595 int level = set_irq_level(HIGHEST_IRQ_LEVEL);
1597 if (pcm_rd_pos != dma_wr_pos)
1599 /* disallow backing up into current DMA write chunk */
1600 size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE)
1601 & PCM_CHUNK_MASK;
1603 /* limit size to amount of old data remaining */
1604 if (size > old_avail)
1605 size = old_avail;
1607 pcm_rd_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
1610 set_irq_level(level);
1612 return size;
1613 } /* enc_unget_pcm_data */
1615 /** Low level pcm recording apis **/
1617 /****************************************************************************
1618 * Functions that do not require targeted implementation but only a targeted
1619 * interface
1621 void pcm_record_data(pcm_more_callback_type more_ready,
1622 unsigned char *start, size_t size)
1624 pcm_callback_more_ready = more_ready;
1626 if (!(start && size))
1628 size = 0;
1629 if (more_ready)
1630 more_ready(&start, &size);
1633 if (start && size)
1634 pcm_rec_dma_start(start, size);
1635 } /* pcm_record_data */
1637 void pcm_stop_recording(void)
1639 if (pcm_recording)
1640 pcm_rec_dma_stop();
1641 } /* pcm_stop_recording */