From 9558c4956d3d603c4d132af88633767810f3ba62 Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Tue, 14 Oct 2008 11:12:20 +0000 Subject: Moved pcm_record from firmware to apps. Cleaned up some. Now all code using struct mp3entry is in apps. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18807 a1c6a512-1295-4272-9138-f99709370657 --- firmware/pcm_record.c | 1787 ------------------------------------------------- 1 file changed, 1787 deletions(-) delete mode 100644 firmware/pcm_record.c (limited to 'firmware/pcm_record.c') diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c deleted file mode 100644 index 0e0102af97..0000000000 --- a/firmware/pcm_record.c +++ /dev/null @@ -1,1787 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2005 by Linus Nielsen Feltzing - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "system.h" -#include "kernel.h" -#include "logf.h" -#include "thread.h" -#include -#include "ata.h" -#include "usb.h" -#include "buffer.h" -#include "general.h" -#include "audio.h" -#include "sound.h" -#include "id3.h" -#ifdef HAVE_SPDIF_IN -#include "spdif.h" -#endif - -/***************************************************************************/ - -extern struct thread_entry *codec_thread_p; - -/** General recording state **/ -static bool is_recording; /* We are recording */ -static bool is_paused; /* We have paused */ -static unsigned long errors; /* An error has occured */ -static unsigned long warnings; /* Warning */ -static int flush_interrupts = 0; /* Number of messages queued that - should interrupt a flush in - progress - - for a safety net and a prompt - response to stop, split and pause - requests - - only interrupts a flush initiated - by pcmrec_flush(0) */ - -/* Utility functions for setting/clearing flushing interrupt flag */ -static inline void flush_interrupt(void) -{ - flush_interrupts++; - logf("flush int: %d", flush_interrupts); -} - -static inline void clear_flush_interrupt(void) -{ - if (--flush_interrupts < 0) - flush_interrupts = 0; -} - -/** Stats on encoded data for current file **/ -static size_t num_rec_bytes; /* Num bytes recorded */ -static unsigned long num_rec_samples; /* Number of PCM samples recorded */ - -/** Stats on encoded data for all files from start to stop **/ -#if 0 -static unsigned long long accum_rec_bytes; /* total size written to chunks */ -static unsigned long long accum_pcm_samples; /* total pcm count processed */ -#endif - -/* Keeps data about current file and is sent as event data for codec */ -static struct enc_file_event_data rec_fdata IDATA_ATTR = -{ - .chunk = NULL, - .new_enc_size = 0, - .new_num_pcm = 0, - .rec_file = -1, - .num_pcm_samples = 0 -}; - -/** These apply to current settings **/ -static int rec_source; /* current rec_source setting */ -static int rec_frequency; /* current frequency setting */ -static unsigned long sample_rate; /* Sample rate in HZ */ -static int num_channels; /* Current number of channels */ -static int rec_mono_mode; /* how mono is created */ -static struct encoder_config enc_config; /* Current encoder configuration */ -static unsigned long pre_record_ticks; /* pre-record time in ticks */ - -/**************************************************************************** - use 2 circular buffers: - pcm_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data - enc_buffer=encoded audio buffer: storage for encoder output data - - Flow: - 1. when entering recording_screen DMA feeds the ringbuffer pcm_buffer - 2. if enough pcm data are available the encoder codec does encoding of pcm - chunks (4-8192 Bytes) into ringbuffer enc_buffer in codec_thread - 3. pcmrec_callback detects enc_buffer 'near full' and writes data to disk - - Functions calls (basic encoder steps): - 1.main: audio_load_encoder(); start the encoder - 2.encoder: enc_get_inputs(); get encoder recording settings - 3.encoder: enc_set_parameters(); set the encoder parameters - 4.encoder: enc_get_pcm_data(); get n bytes of unprocessed pcm data - 5.encoder: enc_unget_pcm_data(); put n bytes of data back (optional) - 6.encoder: enc_get_chunk(); get a ptr to next enc chunk - 7.encoder: compress and store data to enc chunk - 8.encoder: enc_finish_chunk(); inform main about chunk processed and - is available to be written to a file. - Encoder can place any number of chunks - of PCM data in a single output chunk - but must stay within its output chunk - size - 9.encoder: repeat 4. to 8. - A.pcmrec: enc_events_callback(); called for certain events - - (*) Optional step -****************************************************************************/ - -/** buffer parameters where incoming PCM data is placed **/ -#define PCM_NUM_CHUNKS 256 /* Power of 2 */ -#define PCM_CHUNK_SIZE 8192 /* Power of 2 */ -#define PCM_CHUNK_MASK (PCM_NUM_CHUNKS*PCM_CHUNK_SIZE - 1) - -#define GET_PCM_CHUNK(offset) ((long *)(pcm_buffer + (offset))) -#define GET_ENC_CHUNK(index) ENC_CHUNK_HDR(enc_buffer + enc_chunk_size*(index)) -#define INC_ENC_INDEX(index) \ - { if (++index >= enc_num_chunks) index = 0; } -#define DEC_ENC_INDEX(index) \ - { if (--index < 0) index = enc_num_chunks - 1; } - -static size_t rec_buffer_size; /* size of available buffer */ -static unsigned char *pcm_buffer; /* circular recording buffer */ -static unsigned char *enc_buffer; /* circular encoding buffer */ -#ifdef DEBUG -static unsigned long *wrap_id_p; /* magic at wrap position - a debugging - aid to check if the encoder data - spilled out of its chunk */ -#endif /* DEBUG */ -static volatile int dma_wr_pos; /* current DMA write pos */ -static int pcm_rd_pos; /* current PCM read pos */ -static int pcm_enc_pos; /* position encoder is processing */ -static volatile bool dma_lock; /* lock DMA write position */ -static int enc_wr_index; /* encoder chunk write index */ -static int enc_rd_index; /* encoder chunk read index */ -static int enc_num_chunks; /* number of chunks in ringbuffer */ -static size_t enc_chunk_size; /* maximum encoder chunk size */ -static unsigned long enc_sample_rate; /* sample rate used by encoder */ -static bool pcmrec_context = false; /* called by pcmrec thread? */ -static bool pcm_buffer_empty; /* all pcm chunks processed? */ - -/** file flushing **/ -static int low_watermark; /* Low watermark to stop flush */ -static int high_watermark; /* max chunk limit for data flush */ -static unsigned long spinup_time = 35*HZ/10; /* Fudged spinup time */ -static int last_ata_spinup_time = -1;/* previous spin time used */ -#ifdef HAVE_PRIORITY_SCHEDULING -static int flood_watermark; /* boost thread priority when here */ -#endif - -/* Constants that control watermarks */ -#define LOW_SECONDS 1 /* low watermark time till empty */ -#define MINI_CHUNKS 10 /* chunk count for mini flush */ -#ifdef HAVE_PRIORITY_SCHEDULING -#define PRIO_SECONDS 10 /* max flush time before priority boost */ -#endif -#if MEM <= 16 -#define PANIC_SECONDS 5 /* flood watermark time until full */ -#define FLUSH_SECONDS 7 /* flush watermark time until full */ -#else -#define PANIC_SECONDS 8 -#define FLUSH_SECONDS 10 -#endif /* MEM */ - -/** encoder events **/ -static void (*enc_events_callback)(enum enc_events event, void *data); - -/** Path queue for files to write **/ -#define FNQ_MIN_NUM_PATHS 16 /* minimum number of paths to hold */ -#define FNQ_MAX_NUM_PATHS 64 /* maximum number of paths to hold */ -static unsigned char *fn_queue; /* pointer to first filename */ -static ssize_t fnq_size; /* capacity of queue in bytes */ -static int fnq_rd_pos; /* current read position */ -static int fnq_wr_pos; /* current write position */ -#define FNQ_NEXT(pos) \ - ({ int p = (pos) + MAX_PATH; \ - if (p >= fnq_size) \ - p = 0; \ - p; }) -#define FNQ_PREV(pos) \ - ({ int p = (pos) - MAX_PATH; \ - if (p < 0) \ - p = fnq_size - MAX_PATH; \ - p; }) - -enum -{ - PCMREC_FLUSH_INTERRUPTABLE = 0x8000000, /* Flush can be interrupted by - incoming messages - combine - with other constants */ - PCMREC_FLUSH_ALL = 0x7ffffff, /* Flush all files */ - PCMREC_FLUSH_MINI = 0x7fffffe, /* Flush a small number of - chunks */ - PCMREC_FLUSH_IF_HIGH = 0x0000000, /* Flush if high watermark - reached */ -}; - -/***************************************************************************/ - -static struct event_queue pcmrec_queue SHAREDBSS_ATTR; -static struct queue_sender_list pcmrec_queue_send SHAREDBSS_ATTR; -static long pcmrec_stack[3*DEFAULT_STACK_SIZE/sizeof(long)]; -static const char pcmrec_thread_name[] = "pcmrec"; -static struct thread_entry *pcmrec_thread_p; - -static void pcmrec_thread(void); - -enum -{ - PCMREC_NULL = 0, - PCMREC_INIT, /* enable recording */ - PCMREC_CLOSE, /* close recording */ - PCMREC_OPTIONS, /* set recording options */ - PCMREC_RECORD, /* record a new file */ - PCMREC_STOP, /* stop the current recording */ - PCMREC_PAUSE, /* pause the current recording */ - PCMREC_RESUME, /* resume the current recording */ -#if 0 - PCMREC_FLUSH_NUM, /* flush a number of files out */ -#endif -}; - -/*******************************************************************/ -/* Functions that are not executing in the pcmrec_thread first */ -/*******************************************************************/ - -/* Callback for when more data is ready - called in interrupt context */ -static int pcm_rec_have_more(int status) -{ - if (status < 0) - { - /* some error condition */ - if (status == DMA_REC_ERROR_DMA) - { - /* Flush recorded data to disk and stop recording */ - queue_post(&pcmrec_queue, PCMREC_STOP, 0); - return -1; - } - /* else try again next transmission */ - } - else if (!dma_lock) - { - /* advance write position */ - int next_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK; - - /* set pcm ovf if processing start position is inside current - write chunk */ - if ((unsigned)(pcm_enc_pos - next_pos) < PCM_CHUNK_SIZE) - warnings |= PCMREC_W_PCM_BUFFER_OVF; - - dma_wr_pos = next_pos; - } - - pcm_record_more(GET_PCM_CHUNK(dma_wr_pos), PCM_CHUNK_SIZE); - return 0; -} /* pcm_rec_have_more */ - -static void reset_hardware(void) -{ - /* reset pcm to defaults (playback only) */ - pcm_set_frequency(HW_SAMPR_DEFAULT); - audio_set_output_source(AUDIO_SRC_PLAYBACK); - pcm_apply_settings(); -} - -/** pcm_rec_* group **/ - -/** - * Clear all errors and warnings - */ -void pcm_rec_error_clear(void) -{ - errors = warnings = 0; -} /* pcm_rec_error_clear */ - -/** - * Check mode, errors and warnings - */ -unsigned long pcm_rec_status(void) -{ - unsigned long ret = 0; - - if (is_recording) - ret |= AUDIO_STATUS_RECORD; - else if (pre_record_ticks) - ret |= AUDIO_STATUS_PRERECORD; - - if (is_paused) - ret |= AUDIO_STATUS_PAUSE; - - if (errors) - ret |= AUDIO_STATUS_ERROR; - - if (warnings) - ret |= AUDIO_STATUS_WARNING; - - return ret; -} /* pcm_rec_status */ - -/** - * Return warnings that have occured since recording started - */ -unsigned long pcm_rec_get_warnings(void) -{ - return warnings; -} - -#if 0 -int pcm_rec_current_bitrate(void) -{ - if (accum_pcm_samples == 0) - return 0; - - return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples)); -} /* pcm_rec_current_bitrate */ -#endif - -#if 0 -int pcm_rec_encoder_afmt(void) -{ - return enc_config.afmt; -} /* pcm_rec_encoder_afmt */ -#endif - -#if 0 -int pcm_rec_rec_format(void) -{ - return afmt_rec_format[enc_config.afmt]; -} /* pcm_rec_rec_format */ -#endif - -#ifdef HAVE_SPDIF_IN -unsigned long pcm_rec_sample_rate(void) -{ - /* Which is better ?? */ -#if 0 - return enc_sample_rate; -#endif - return sample_rate; -} /* audio_get_sample_rate */ -#endif - -/** - * Creates pcmrec_thread - */ -void pcm_rec_init(void) -{ - queue_init(&pcmrec_queue, true); - pcmrec_thread_p = - create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), - 0, pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING) - IF_COP(, CPU)); - queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send, - pcmrec_thread_p); -} /* pcm_rec_init */ - -/** audio_* group **/ - -/** - * Initializes recording - call before calling any other recording function - */ -void audio_init_recording(unsigned int buffer_offset) -{ - logf("audio_init_recording"); - queue_send(&pcmrec_queue, PCMREC_INIT, 0); - logf("audio_init_recording done"); - (void)buffer_offset; -} /* audio_init_recording */ - -/** - * Closes recording - call audio_stop_recording first - */ -void audio_close_recording(void) -{ - logf("audio_close_recording"); - queue_send(&pcmrec_queue, PCMREC_CLOSE, 0); - logf("audio_close_recording done"); -} /* audio_close_recording */ - -/** - * Sets recording parameters - */ -void audio_set_recording_options(struct audio_recording_options *options) -{ - logf("audio_set_recording_options"); - queue_send(&pcmrec_queue, PCMREC_OPTIONS, (intptr_t)options); - logf("audio_set_recording_options done"); -} /* audio_set_recording_options */ - -/** - * Start recording if not recording or else split - */ -void audio_record(const char *filename) -{ - logf("audio_record: %s", filename); - flush_interrupt(); - queue_send(&pcmrec_queue, PCMREC_RECORD, (intptr_t)filename); - logf("audio_record_done"); -} /* audio_record */ - -/** - * audio_record wrapper for API compatibility with HW codec - */ -void audio_new_file(const char *filename) -{ - audio_record(filename); -} /* audio_new_file */ - -/** - * Stop current recording if recording - */ -void audio_stop_recording(void) -{ - logf("audio_stop_recording"); - flush_interrupt(); - queue_post(&pcmrec_queue, PCMREC_STOP, 0); - logf("audio_stop_recording done"); -} /* audio_stop_recording */ - -/** - * Pause current recording - */ -void audio_pause_recording(void) -{ - logf("audio_pause_recording"); - flush_interrupt(); - queue_post(&pcmrec_queue, PCMREC_PAUSE, 0); - logf("audio_pause_recording done"); -} /* audio_pause_recording */ - -/** - * Resume current recording if paused - */ -void audio_resume_recording(void) -{ - logf("audio_resume_recording"); - queue_post(&pcmrec_queue, PCMREC_RESUME, 0); - logf("audio_resume_recording done"); -} /* audio_resume_recording */ - -/** - * Note that microphone is mono, only left value is used - * See audiohw_set_recvol() for exact ranges. - * - * @param type AUDIO_GAIN_MIC, AUDIO_GAIN_LINEIN - * - */ -void audio_set_recording_gain(int left, int right, int type) -{ - //logf("rcmrec: t=%d l=%d r=%d", type, left, right); - audiohw_set_recvol(left, right, type); -} /* audio_set_recording_gain */ - -/** Information about current state **/ - -/** - * Return current recorded time in ticks (playback eqivalent time) - */ -unsigned long audio_recorded_time(void) -{ - if (!is_recording || enc_sample_rate == 0) - return 0; - - /* return actual recorded time a la encoded data even if encoder rate - doesn't match the pcm rate */ - return (long)(HZ*(unsigned long long)num_rec_samples / enc_sample_rate); -} /* audio_recorded_time */ - -/** - * Return number of bytes encoded to output - */ -unsigned long audio_num_recorded_bytes(void) -{ - if (!is_recording) - return 0; - - return num_rec_bytes; -} /* audio_num_recorded_bytes */ - -/***************************************************************************/ -/* */ -/* Functions that execute in the context of pcmrec_thread */ -/* */ -/***************************************************************************/ - -/** Filename Queue **/ - -/* returns true if the queue is empty */ -static inline bool pcmrec_fnq_is_empty(void) -{ - return fnq_rd_pos == fnq_wr_pos; -} /* pcmrec_fnq_is_empty */ - -/* empties the filename queue */ -static inline void pcmrec_fnq_set_empty(void) -{ - fnq_rd_pos = fnq_wr_pos; -} /* pcmrec_fnq_set_empty */ - -/* returns true if the queue is full */ -static bool pcmrec_fnq_is_full(void) -{ - ssize_t size = fnq_wr_pos - fnq_rd_pos; - if (size < 0) - size += fnq_size; - - return size >= fnq_size - MAX_PATH; -} /* pcmrec_fnq_is_full */ - -/* queue another filename - will overwrite oldest one if full */ -static bool pcmrec_fnq_add_filename(const char *filename) -{ - strncpy(fn_queue + fnq_wr_pos, filename, MAX_PATH); - fnq_wr_pos = FNQ_NEXT(fnq_wr_pos); - - if (fnq_rd_pos != fnq_wr_pos) - return true; - - /* queue full */ - fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); - return true; -} /* pcmrec_fnq_add_filename */ - -/* replace the last filename added */ -static bool pcmrec_fnq_replace_tail(const char *filename) -{ - int pos; - - if (pcmrec_fnq_is_empty()) - return false; - - pos = FNQ_PREV(fnq_wr_pos); - - strncpy(fn_queue + pos, filename, MAX_PATH); - - return true; -} /* pcmrec_fnq_replace_tail */ - -/* pulls the next filename from the queue */ -static bool pcmrec_fnq_get_filename(char *filename) -{ - if (pcmrec_fnq_is_empty()) - return false; - - if (filename) - strncpy(filename, fn_queue + fnq_rd_pos, MAX_PATH); - - fnq_rd_pos = FNQ_NEXT(fnq_rd_pos); - return true; -} /* pcmrec_fnq_get_filename */ - -/* close the file number pointed to by fd_p */ -static void pcmrec_close_file(int *fd_p) -{ - if (*fd_p < 0) - return; /* preserve error */ - - if (close(*fd_p) != 0) - errors |= PCMREC_E_IO; - - *fd_p = -1; -} /* pcmrec_close_file */ - -/** Data Flushing **/ - -/** - * called after callback to update sizes if codec changed the amount of data - * a chunk represents - */ -static inline void pcmrec_update_sizes_inl(size_t prev_enc_size, - unsigned long prev_num_pcm) -{ - if (rec_fdata.new_enc_size != prev_enc_size) - { - ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size; - num_rec_bytes += size_diff; -#if 0 - accum_rec_bytes += size_diff; -#endif - } - - if (rec_fdata.new_num_pcm != prev_num_pcm) - { - unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm; - num_rec_samples += pcm_diff; -#if 0 - accum_pcm_samples += pcm_diff; -#endif - } -} /* pcmrec_update_sizes_inl */ - -/* don't need to inline every instance */ -static void pcmrec_update_sizes(size_t prev_enc_size, - unsigned long prev_num_pcm) -{ - pcmrec_update_sizes_inl(prev_enc_size, prev_num_pcm); -} /* pcmrec_update_sizes */ - -static void pcmrec_start_file(void) -{ - size_t enc_size = rec_fdata.new_enc_size; - unsigned long num_pcm = rec_fdata.new_num_pcm; - int curr_rec_file = rec_fdata.rec_file; - char filename[MAX_PATH]; - - /* must always pull the filename that matches with this queue */ - if (!pcmrec_fnq_get_filename(filename)) - { - logf("start file: fnq empty"); - *filename = '\0'; - errors |= PCMREC_E_FNQ_DESYNC; - } - else if (errors != 0) - { - logf("start file: error already"); - } - else if (curr_rec_file >= 0) - { - /* Any previous file should have been closed */ - logf("start file: file already open"); - errors |= PCMREC_E_FNQ_DESYNC; - } - - if (errors != 0) - rec_fdata.chunk->flags |= CHUNKF_ERROR; - - /* encoder can set error flag here and should increase - enc_new_size and pcm_new_size to reflect additional - data written if any */ - rec_fdata.filename = filename; - enc_events_callback(ENC_START_FILE, &rec_fdata); - - if (errors == 0 && (rec_fdata.chunk->flags & CHUNKF_ERROR)) - { - logf("start file: enc error"); - errors |= PCMREC_E_ENCODER; - } - - if (errors != 0) - { - pcmrec_close_file(&curr_rec_file); - /* Write no more to this file */ - rec_fdata.chunk->flags |= CHUNKF_END_FILE; - } - else - { - pcmrec_update_sizes(enc_size, num_pcm); - } - - rec_fdata.chunk->flags &= ~CHUNKF_START_FILE; -} /* pcmrec_start_file */ - -static inline void pcmrec_write_chunk(void) -{ - size_t enc_size = rec_fdata.new_enc_size; - unsigned long num_pcm = rec_fdata.new_num_pcm; - - if (errors != 0) - rec_fdata.chunk->flags |= CHUNKF_ERROR; - - enc_events_callback(ENC_WRITE_CHUNK, &rec_fdata); - - if ((long)rec_fdata.chunk->flags >= 0) - { - pcmrec_update_sizes_inl(enc_size, num_pcm); - } - else if (errors == 0) - { - logf("wr chk enc error %lu %lu", - rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm); - errors |= PCMREC_E_ENCODER; - } -} /* pcmrec_write_chunk */ - -static void pcmrec_end_file(void) -{ - /* all data in output buffer for current file will have been - written and encoder can now do any nescessary steps to - finalize the written file */ - size_t enc_size = rec_fdata.new_enc_size; - unsigned long num_pcm = rec_fdata.new_num_pcm; - - enc_events_callback(ENC_END_FILE, &rec_fdata); - - if (errors == 0) - { - if (rec_fdata.chunk->flags & CHUNKF_ERROR) - { - logf("end file: enc error"); - errors |= PCMREC_E_ENCODER; - } - else - { - pcmrec_update_sizes(enc_size, num_pcm); - } - } - - /* Force file close if error */ - if (errors != 0) - pcmrec_close_file(&rec_fdata.rec_file); - - rec_fdata.chunk->flags &= ~CHUNKF_END_FILE; -} /* pcmrec_end_file */ - -/** - * Update buffer watermarks with spinup time compensation - * - * All this assumes reasonable data rates, chunk sizes and sufficient - * memory for the most part. Some dumb checks are included but perhaps - * are pointless since this all will break down at extreme limits that - * are currently not applicable to any supported device. - */ -static void pcmrec_refresh_watermarks(void) -{ - logf("ata spinup: %d", ata_spinup_time); - - /* set the low mark for when flushing stops if automatic */ - low_watermark = (LOW_SECONDS*4*sample_rate + (enc_chunk_size-1)) - / enc_chunk_size; - logf("low wmk: %d", low_watermark); - -#ifdef HAVE_PRIORITY_SCHEDULING - /* panic boost thread priority if 2 seconds of ground is lost - - this allows encoder to boost with just under a second of - pcm data (if not yet full enough to boost itself) - and not falsely trip the alarm. */ - flood_watermark = enc_num_chunks - - (PANIC_SECONDS*4*sample_rate + (enc_chunk_size-1)) - / enc_chunk_size; - - if (flood_watermark < low_watermark) - { - logf("warning: panic < low"); - flood_watermark = low_watermark; - } - - logf("flood at: %d", flood_watermark); -#endif - spinup_time = last_ata_spinup_time = ata_spinup_time; - - /* write at 8s + st remaining in enc_buffer - range 12s to - 20s total - default to 3.5s spinup. */ - if (spinup_time == 0) - spinup_time = 35*HZ/10; /* default - cozy */ - else if (spinup_time < 2*HZ) - spinup_time = 2*HZ; /* ludicrous - ramdisk? */ - else if (spinup_time > 10*HZ) - spinup_time = 10*HZ; /* do you have a functioning HD? */ - - /* try to start writing with 10s remaining after disk spinup */ - high_watermark = enc_num_chunks - - ((FLUSH_SECONDS*HZ + spinup_time)*4*sample_rate + - (enc_chunk_size-1)*HZ) / (enc_chunk_size*HZ); - - if (high_watermark < low_watermark) - { - high_watermark = low_watermark; - low_watermark /= 2; - logf("warning: low 'write at'"); - } - - logf("write at: %d", high_watermark); -} /* pcmrec_refresh_watermarks */ - -/** - * Process the chunks - * - * This function is called when queue_get_w_tmo times out. - * - * Set flush_num to the number of files to flush to disk or to - * a PCMREC_FLUSH_* constant. - */ -static void pcmrec_flush(unsigned flush_num) -{ -#ifdef HAVE_PRIORITY_SCHEDULING - static unsigned long last_flush_tick; /* tick when function returned */ - unsigned long start_tick; /* When flush started */ - unsigned long prio_tick; /* Timeout for auto boost */ - int prio_pcmrec; /* Current thread priority for pcmrec */ - int prio_codec; /* Current thread priority for codec */ -#endif - int num_ready; /* Number of chunks ready at start */ - unsigned remaining; /* Number of file starts remaining */ - unsigned chunks_flushed; /* Chunks flushed (for mini flush only) */ - bool interruptable; /* Flush can be interupted */ - - num_ready = enc_wr_index - enc_rd_index; - if (num_ready < 0) - num_ready += enc_num_chunks; - - /* save interruptable flag and remove it to get the actual count */ - interruptable = (flush_num & PCMREC_FLUSH_INTERRUPTABLE) != 0; - flush_num &= ~PCMREC_FLUSH_INTERRUPTABLE; - - if (flush_num == 0) - { - if (!is_recording) - return; - - if (ata_spinup_time != last_ata_spinup_time) - pcmrec_refresh_watermarks(); - - /* enough available? no? then leave */ - if (num_ready < high_watermark) - return; - } /* endif (flush_num == 0) */ - -#ifdef HAVE_PRIORITY_SCHEDULING - start_tick = current_tick; - prio_tick = start_tick + PRIO_SECONDS*HZ + spinup_time; - - if (flush_num == 0 && TIME_BEFORE(current_tick, last_flush_tick + HZ/2)) - { - /* if we're getting called too much and this isn't forced, - boost stat by expiring timeout in advance */ - logf("too frequent flush"); - prio_tick = current_tick - 1; - } - - prio_pcmrec = -1; - prio_codec = -1; /* GCC is too stoopid to figure out it doesn't - need init */ -#endif - - logf("writing:%d(%d):%s%s", num_ready, flush_num, - interruptable ? "i" : "", - flush_num == PCMREC_FLUSH_MINI ? "m" : ""); - - cpu_boost(true); - - remaining = flush_num; - chunks_flushed = 0; - - while (num_ready > 0) - { - /* check current number of encoder chunks */ - int num = enc_wr_index - enc_rd_index; - if (num < 0) - num += enc_num_chunks; - - if (num <= low_watermark && - (flush_num == PCMREC_FLUSH_IF_HIGH || num <= 0)) - { - logf("low data: %d", num); - break; /* data remaining is below threshold */ - } - - if (interruptable && flush_interrupts > 0) - { - logf("int at: %d", num); - break; /* interrupted */ - } - -#ifdef HAVE_PRIORITY_SCHEDULING - if (prio_pcmrec == -1 && (num >= flood_watermark || - TIME_AFTER(current_tick, prio_tick))) - { - /* losing ground or holding without progress - boost - priority until finished */ - logf("pcmrec: boost (%s)", - num >= flood_watermark ? "num" : "time"); - prio_pcmrec = thread_set_priority(NULL, - thread_get_priority(NULL) - 4); - prio_codec = thread_set_priority(codec_thread_p, - thread_get_priority(codec_thread_p) - 4); - } -#endif - - rec_fdata.chunk = GET_ENC_CHUNK(enc_rd_index); - rec_fdata.new_enc_size = rec_fdata.chunk->enc_size; - rec_fdata.new_num_pcm = rec_fdata.chunk->num_pcm; - - if (rec_fdata.chunk->flags & CHUNKF_START_FILE) - { - pcmrec_start_file(); - if (--remaining == 0) - num_ready = 0; /* stop on next loop - must write this - chunk if it has data */ - } - - pcmrec_write_chunk(); - - if (rec_fdata.chunk->flags & CHUNKF_END_FILE) - pcmrec_end_file(); - - INC_ENC_INDEX(enc_rd_index); - - if (errors != 0) - { - pcmrec_end_file(); - break; - } - - if (flush_num == PCMREC_FLUSH_MINI && - ++chunks_flushed >= MINI_CHUNKS) - { - logf("mini flush break"); - break; - } - /* no yielding; the file apis called in the codecs do that - sufficiently */ - } /* end while */ - - /* sync file */ - if (rec_fdata.rec_file >= 0 && fsync(rec_fdata.rec_file) != 0) - errors |= PCMREC_E_IO; - - cpu_boost(false); - -#ifdef HAVE_PRIORITY_SCHEDULING - if (prio_pcmrec != -1) - { - /* return to original priorities */ - logf("pcmrec: unboost priority"); - thread_set_priority(NULL, prio_pcmrec); - thread_set_priority(codec_thread_p, prio_codec); - } - - last_flush_tick = current_tick; /* save tick when we left */ -#endif - - logf("done"); -} /* pcmrec_flush */ - -/** - * Marks a new stream in the buffer and gives the encoder a chance for special - * handling of transition from one to the next. The encoder may change the - * chunk that ends the old stream by requesting more chunks and similiarly for - * the new but must always advance the position though the interface. It can - * later reject any data it cares to when writing the file but should mark the - * chunk so it can recognize this. ENC_WRITE_CHUNK event must be able to accept - * a NULL data pointer without error as well. - */ -static int pcmrec_get_chunk_index(struct enc_chunk_hdr *chunk) -{ - return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size; -} /* pcmrec_get_chunk_index */ - -static struct enc_chunk_hdr * pcmrec_get_prev_chunk(int index) -{ - DEC_ENC_INDEX(index); - return GET_ENC_CHUNK(index); -} /* pcmrec_get_prev_chunk */ - -static void pcmrec_new_stream(const char *filename, /* next file name */ - unsigned long flags, /* CHUNKF_* flags */ - int pre_index) /* index for prerecorded data */ -{ - logf("pcmrec_new_stream"); - char path[MAX_PATH]; /* place to copy filename so sender can be released */ - - struct enc_buffer_event_data data; - bool (*fnq_add_fn)(const char *) = NULL; /* function to use to add - new filename */ - struct enc_chunk_hdr *start = NULL; /* pointer to starting chunk of - stream */ - bool did_flush = false; /* did a flush occurr? */ - - if (filename) - strncpy(path, filename, MAX_PATH); - queue_reply(&pcmrec_queue, 0); /* We have all we need */ - - data.pre_chunk = NULL; - data.chunk = GET_ENC_CHUNK(enc_wr_index); - - /* end chunk */ - if (flags & CHUNKF_END_FILE) - { - data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE; - - if (data.chunk->flags & CHUNKF_START_FILE) - { - /* cannot start and end on same unprocessed chunk */ - logf("file end on start"); - flags &= ~CHUNKF_END_FILE; - } - else if (enc_rd_index == enc_wr_index) - { - /* all data flushed but file not ended - chunk will be left - empty */ - logf("end on dead end"); - data.chunk->flags = 0; - data.chunk->enc_size = 0; - data.chunk->num_pcm = 0; - data.chunk->enc_data = NULL; - INC_ENC_INDEX(enc_wr_index); - data.chunk = GET_ENC_CHUNK(enc_wr_index); - } - else - { - struct enc_chunk_hdr *last = pcmrec_get_prev_chunk(enc_wr_index); - - if (last->flags & CHUNKF_END_FILE) - { - /* end already processed and marked - can't end twice */ - logf("file end again"); - flags &= ~CHUNKF_END_FILE; - } - } - } - - /* start chunk */ - if (flags & CHUNKF_START_FILE) - { - bool pre = flags & CHUNKF_PRERECORD; - - if (pre) - { - logf("stream prerecord start"); - start = data.pre_chunk = GET_ENC_CHUNK(pre_index); - start->flags &= CHUNKF_START_FILE | CHUNKF_PRERECORD; - } - else - { - logf("stream normal start"); - start = data.chunk; - start->flags &= CHUNKF_START_FILE; - } - - /* if encoder hasn't yet processed the last start - abort the start - of the previous file queued or else it will be empty and invalid */ - if (start->flags & CHUNKF_START_FILE) - { - logf("replacing fnq tail: %s", filename); - fnq_add_fn = pcmrec_fnq_replace_tail; - } - else - { - logf("adding filename: %s", filename); - fnq_add_fn = pcmrec_fnq_add_filename; - } - } - - data.flags = flags; - pcmrec_context = true; /* switch encoder context */ - enc_events_callback(ENC_REC_NEW_STREAM, &data); - pcmrec_context = false; /* switch back */ - - if (flags & CHUNKF_END_FILE) - { - int i = pcmrec_get_chunk_index(data.chunk); - pcmrec_get_prev_chunk(i)->flags |= CHUNKF_END_FILE; - } - - if (start) - { - if (!(flags & CHUNKF_PRERECORD)) - { - /* get stats on data added to start - sort of a prerecord - operation */ - int i = pcmrec_get_chunk_index(data.chunk); - struct enc_chunk_hdr *chunk = data.chunk; - - logf("start data: %d %d", i, enc_wr_index); - - num_rec_bytes = 0; - num_rec_samples = 0; - - while (i != enc_wr_index) - { - num_rec_bytes += chunk->enc_size; - num_rec_samples += chunk->num_pcm; - INC_ENC_INDEX(i); - chunk = GET_ENC_CHUNK(i); - } - - start->flags &= ~CHUNKF_START_FILE; - start = data.chunk; - } - - start->flags |= CHUNKF_START_FILE; - - /* flush all pending files out if full and adding */ - if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full()) - { - logf("fnq full"); - pcmrec_flush(PCMREC_FLUSH_ALL); - did_flush = true; - } - - fnq_add_fn(path); - } - - /* Make sure to complete any interrupted high watermark */ - if (!did_flush) - pcmrec_flush(PCMREC_FLUSH_IF_HIGH); -} /* pcmrec_new_stream */ - -/** event handlers for pcmrec thread */ - -/* PCMREC_INIT */ -static void pcmrec_init(void) -{ - unsigned char *buffer; - - /* warings and errors */ - warnings = - errors = 0; - - pcmrec_close_file(&rec_fdata.rec_file); - rec_fdata.rec_file = -1; - - /* pcm FIFO */ - dma_lock = true; - pcm_rd_pos = 0; - dma_wr_pos = 0; - pcm_enc_pos = 0; - - /* encoder FIFO */ - enc_wr_index = 0; - enc_rd_index = 0; - - /* filename queue */ - fnq_rd_pos = 0; - fnq_wr_pos = 0; - - /* stats */ - num_rec_bytes = 0; - num_rec_samples = 0; -#if 0 - accum_rec_bytes = 0; - accum_pcm_samples = 0; -#endif - - pre_record_ticks = 0; - - is_recording = false; - is_paused = false; - - buffer = audio_get_recording_buffer(&rec_buffer_size); - - /* Line align pcm_buffer 2^4=16 bytes */ - pcm_buffer = (unsigned char *)ALIGN_UP_P2((uintptr_t)buffer, 4); - enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE + - PCM_MAX_FEED_SIZE, 2); - /* Adjust available buffer for possible align advancement */ - rec_buffer_size -= pcm_buffer - buffer; - - pcm_init_recording(); -} /* pcmrec_init */ - -/* PCMREC_CLOSE */ -static void pcmrec_close(void) -{ - dma_lock = true; - pre_record_ticks = 0; /* Can't be prerecording any more */ - warnings = 0; - pcm_close_recording(); - reset_hardware(); - audio_remove_encoder(); -} /* pcmrec_close */ - -/* PCMREC_OPTIONS */ -static void pcmrec_set_recording_options( - struct audio_recording_options *options) -{ - /* stop DMA transfer */ - dma_lock = true; - pcm_stop_recording(); - - rec_frequency = options->rec_frequency; - rec_source = options->rec_source; - num_channels = options->rec_channels == 1 ? 1 : 2; - rec_mono_mode = options->rec_mono_mode; - pre_record_ticks = options->rec_prerecord_time * HZ; - enc_config = options->enc_config; - enc_config.afmt = rec_format_afmt[enc_config.rec_format]; - -#ifdef HAVE_SPDIF_IN - if (rec_source == AUDIO_SRC_SPDIF) - { - /* must measure SPDIF sample rate before configuring codecs */ - unsigned long sr = spdif_measure_frequency(); - /* round to master list for SPDIF rate */ - int index = round_value_to_list32(sr, audio_master_sampr_list, - SAMPR_NUM_FREQ, false); - sample_rate = audio_master_sampr_list[index]; - /* round to HW playback rates for monitoring */ - index = round_value_to_list32(sr, hw_freq_sampr, - HW_NUM_FREQ, false); - pcm_set_frequency(hw_freq_sampr[index]); - /* encoders with a limited number of rates do their own rounding */ - } - else -#endif - { - /* set sample rate from frequency selection */ - sample_rate = rec_freq_sampr[rec_frequency]; - pcm_set_frequency(sample_rate); - } - - /* set monitoring */ - audio_set_output_source(rec_source); - - /* apply hardware setting to start monitoring now */ - pcm_apply_settings(); - - queue_reply(&pcmrec_queue, 0); /* Release sender */ - - if (audio_load_encoder(enc_config.afmt)) - { - /* start DMA transfer */ - dma_lock = pre_record_ticks == 0; - pcm_record_data(pcm_rec_have_more, GET_PCM_CHUNK(dma_wr_pos), - PCM_CHUNK_SIZE); - } - else - { - logf("set rec opt: enc load failed"); - errors |= PCMREC_E_LOAD_ENCODER; - } -} /* pcmrec_set_recording_options */ - -/* PCMREC_RECORD - start recording (not gapless) - or split stream (gapless) */ -static void pcmrec_record(const char *filename) -{ - unsigned long pre_sample_ticks; - int rd_start; - unsigned long flags; - int pre_index; - - logf("pcmrec_record: %s", filename); - - /* reset stats */ - num_rec_bytes = 0; - num_rec_samples = 0; - - if (!is_recording) - { -#if 0 - accum_rec_bytes = 0; - accum_pcm_samples = 0; -#endif - warnings = 0; /* reset warnings */ - - rd_start = enc_wr_index; - pre_sample_ticks = 0; - - pcmrec_refresh_watermarks(); - - if (pre_record_ticks) - { - int i = rd_start; - /* calculate number of available chunks */ - unsigned long avail_pre_chunks = (enc_wr_index - enc_rd_index + - enc_num_chunks) % enc_num_chunks; - /* overflow at 974 seconds of prerecording at 44.1kHz */ - unsigned long pre_record_sample_ticks = - enc_sample_rate*pre_record_ticks; - int pre_chunks = 0; /* Counter to limit prerecorded time to - prevent flood state at outset */ - - logf("pre-st: %ld", pre_record_sample_ticks); - - /* Get exact measure of recorded data as number of samples aren't - nescessarily going to be the max for each chunk */ - for (; avail_pre_chunks-- > 0;) - { - struct enc_chunk_hdr *chunk; - unsigned long chunk_sample_ticks; - - DEC_ENC_INDEX(i); - - chunk = GET_ENC_CHUNK(i); - - /* must have data to be counted */ - if (chunk->enc_data == NULL) - continue; - - chunk_sample_ticks = chunk->num_pcm*HZ; - - rd_start = i; - pre_sample_ticks += chunk_sample_ticks; - num_rec_bytes += chunk->enc_size; - num_rec_samples += chunk->num_pcm; - pre_chunks++; - - /* stop here if enough already */ - if (pre_chunks >= high_watermark || - pre_sample_ticks >= pre_record_sample_ticks) - { - logf("pre-chks: %d", pre_chunks); - break; - } - } - -#if 0 - accum_rec_bytes = num_rec_bytes; - accum_pcm_samples = num_rec_samples; -#endif - } - - enc_rd_index = rd_start; - - /* filename queue should be empty */ - if (!pcmrec_fnq_is_empty()) - { - logf("fnq: not empty!"); - pcmrec_fnq_set_empty(); - } - - flags = CHUNKF_START_FILE; - if (pre_sample_ticks > 0) - flags |= CHUNKF_PRERECORD; - - pre_index = enc_rd_index; - - dma_lock = false; - is_paused = false; - is_recording = true; - } - else - { - /* already recording, just split the stream */ - logf("inserting split"); - flags = CHUNKF_START_FILE | CHUNKF_END_FILE; - pre_index = 0; - } - - pcmrec_new_stream(filename, flags, pre_index); - logf("pcmrec_record done"); -} /* pcmrec_record */ - -/* PCMREC_STOP */ -static void pcmrec_stop(void) -{ - logf("pcmrec_stop"); - - if (is_recording) - { - dma_lock = true; /* lock dma write position */ - - /* flush all available data first to avoid overflow while waiting - for encoding to finish */ - pcmrec_flush(PCMREC_FLUSH_ALL); - - /* wait for encoder to finish remaining data */ - while (errors == 0 && !pcm_buffer_empty) - yield(); - - /* end stream at last data */ - pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0); - - /* flush anything else encoder added */ - pcmrec_flush(PCMREC_FLUSH_ALL); - - /* remove any pending file start not yet processed - should be at - most one at enc_wr_index */ - pcmrec_fnq_get_filename(NULL); - /* encoder should abort any chunk it was in midst of processing */ - GET_ENC_CHUNK(enc_wr_index)->flags = CHUNKF_ABORT; - - /* filename queue should be empty */ - if (!pcmrec_fnq_is_empty()) - { - logf("fnq: not empty!"); - pcmrec_fnq_set_empty(); - } - - /* be absolutely sure the file is closed */ - if (errors != 0) - pcmrec_close_file(&rec_fdata.rec_file); - rec_fdata.rec_file = -1; - - is_recording = false; - is_paused = false; - dma_lock = pre_record_ticks == 0; - } - else - { - logf("not recording"); - } - - logf("pcmrec_stop done"); -} /* pcmrec_stop */ - -/* PCMREC_PAUSE */ -static void pcmrec_pause(void) -{ - logf("pcmrec_pause"); - - if (!is_recording) - { - logf("not recording"); - } - else if (is_paused) - { - logf("already paused"); - } - else - { - dma_lock = true; - is_paused = true; - } - - logf("pcmrec_pause done"); -} /* pcmrec_pause */ - -/* PCMREC_RESUME */ -static void pcmrec_resume(void) -{ - logf("pcmrec_resume"); - - if (!is_recording) - { - logf("not recording"); - } - else if (!is_paused) - { - logf("not paused"); - } - else - { - is_paused = false; - is_recording = true; - dma_lock = false; - } - - logf("pcmrec_resume done"); -} /* pcmrec_resume */ - -static void pcmrec_thread(void) __attribute__((noreturn)); -static void pcmrec_thread(void) -{ - struct queue_event ev; - - logf("thread pcmrec start"); - - while(1) - { - if (is_recording) - { - /* Poll periodically to flush data */ - queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5); - - if (ev.id == SYS_TIMEOUT) - { - /* Messages that interrupt this will complete it */ - pcmrec_flush(PCMREC_FLUSH_IF_HIGH | - PCMREC_FLUSH_INTERRUPTABLE); - continue; - } - } - else - { - /* Not doing anything - sit and wait for commands */ - queue_wait(&pcmrec_queue, &ev); - } - - switch (ev.id) - { - case PCMREC_INIT: - pcmrec_init(); - break; - - case PCMREC_CLOSE: - pcmrec_close(); - break; - - case PCMREC_OPTIONS: - pcmrec_set_recording_options( - (struct audio_recording_options *)ev.data); - break; - - case PCMREC_RECORD: - clear_flush_interrupt(); - pcmrec_record((const char *)ev.data); - break; - - case PCMREC_STOP: - clear_flush_interrupt(); - pcmrec_stop(); - break; - - case PCMREC_PAUSE: - clear_flush_interrupt(); - pcmrec_pause(); - break; - - case PCMREC_RESUME: - pcmrec_resume(); - break; -#if 0 - case PCMREC_FLUSH_NUM: - pcmrec_flush((unsigned)ev.data); - break; -#endif - case SYS_USB_CONNECTED: - if (is_recording) - break; - pcmrec_close(); - usb_acknowledge(SYS_USB_CONNECTED_ACK); - usb_wait_for_disconnect(&pcmrec_queue); - flush_interrupts = 0; - break; - } /* end switch */ - } /* end while */ -} /* pcmrec_thread */ - -/****************************************************************************/ -/* */ -/* following functions will be called by the encoder codec */ -/* in a free-threaded manner */ -/* */ -/****************************************************************************/ - -/* pass the encoder settings to the encoder */ -void enc_get_inputs(struct enc_inputs *inputs) -{ - inputs->sample_rate = sample_rate; - inputs->num_channels = num_channels; - inputs->rec_mono_mode = rec_mono_mode; - inputs->config = &enc_config; -} /* enc_get_inputs */ - -/* set the encoder dimensions (called by encoder codec at initialization and - termination) */ -void enc_set_parameters(struct enc_parameters *params) -{ - size_t bufsize, resbytes; - - logf("enc_set_parameters"); - - if (!params) - { - logf("reset"); - /* Encoder is terminating */ - memset(&enc_config, 0, sizeof (enc_config)); - enc_sample_rate = 0; - cancel_cpu_boost(); /* Make sure no boost remains */ - return; - } - - enc_sample_rate = params->enc_sample_rate; - logf("enc sampr:%lu", enc_sample_rate); - - pcm_rd_pos = dma_wr_pos; - pcm_enc_pos = pcm_rd_pos; - - enc_config.afmt = params->afmt; - /* addition of the header is always implied - chunk size 4-byte aligned */ - enc_chunk_size = - ALIGN_UP_P2(ENC_CHUNK_HDR_SIZE + params->chunk_size, 2); - enc_events_callback = params->events_callback; - - logf("chunk size:%lu", enc_chunk_size); - - /*** Configure the buffers ***/ - - /* Layout of recording buffer: - * [ax] = possible alignment x multiple - * [sx] = possible size alignment of x multiple - * |[a16]|[s4]:PCM Buffer+PCM Guard|[s4 each]:Encoder Chunks|-> - * |[[s4]:Reserved Bytes]|Filename Queue->|[space]| - */ - resbytes = ALIGN_UP_P2(params->reserve_bytes, 2); - logf("resbytes:%lu", resbytes); - - bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) - - resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH -#ifdef DEBUG - - sizeof (*wrap_id_p) -#endif - ; - - enc_num_chunks = bufsize / enc_chunk_size; - logf("num chunks:%d", enc_num_chunks); - - /* get real amount used by encoder chunks */ - bufsize = enc_num_chunks*enc_chunk_size; - logf("enc size:%lu", bufsize); - -#ifdef DEBUG - /* add magic at wraparound for spillover checks */ - wrap_id_p = SKIPBYTES((unsigned long *)enc_buffer, bufsize); - bufsize += sizeof (*wrap_id_p); - *wrap_id_p = ENC_CHUNK_MAGIC; -#endif - - /** set OUT parameters **/ - params->enc_buffer = enc_buffer; - params->buf_chunk_size = enc_chunk_size; - params->num_chunks = enc_num_chunks; - - /* calculate reserve buffer start and return pointer to encoder */ - params->reserve_buffer = NULL; - if (resbytes > 0) - { - params->reserve_buffer = enc_buffer + bufsize; - bufsize += resbytes; - } - - /* place filename queue at end of buffer using up whatever remains */ - fnq_rd_pos = 0; /* reset */ - fnq_wr_pos = 0; /* reset */ - fn_queue = enc_buffer + bufsize; - fnq_size = pcm_buffer + rec_buffer_size - fn_queue; - fnq_size /= MAX_PATH; - if (fnq_size > FNQ_MAX_NUM_PATHS) - fnq_size = FNQ_MAX_NUM_PATHS; - fnq_size *= MAX_PATH; - logf("fnq files:%ld", fnq_size / MAX_PATH); - -#if defined(DEBUG) - logf("ab :%08lX", (uintptr_t)audiobuf); - logf("pcm:%08lX", (uintptr_t)pcm_buffer); - logf("enc:%08lX", (uintptr_t)enc_buffer); - logf("res:%08lX", (uintptr_t)params->reserve_buffer); - logf("wip:%08lX", (uintptr_t)wrap_id_p); - logf("fnq:%08lX", (uintptr_t)fn_queue); - logf("end:%08lX", (uintptr_t)fn_queue + fnq_size); - logf("abe:%08lX", (uintptr_t)audiobufend); -#endif - - /* init all chunk headers and reset indexes */ - enc_rd_index = 0; - for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; ) - { - struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(--enc_wr_index); -#ifdef DEBUG - chunk->id = ENC_CHUNK_MAGIC; -#endif - chunk->flags = 0; - } - - logf("enc_set_parameters done"); -} /* enc_set_parameters */ - -/* return encoder chunk at current write position - - NOTE: can be called by pcmrec thread when splitting streams */ -struct enc_chunk_hdr * enc_get_chunk(void) -{ - struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); - -#ifdef DEBUG - if (chunk->id != ENC_CHUNK_MAGIC || *wrap_id_p != ENC_CHUNK_MAGIC) - { - errors |= PCMREC_E_CHUNK_OVF; - logf("finish chk ovf: %d", enc_wr_index); - } -#endif - - chunk->flags &= CHUNKF_START_FILE; - - if (!is_recording) - chunk->flags |= CHUNKF_PRERECORD; - - return chunk; -} /* enc_get_chunk */ - -/* releases the current chunk into the available chunks - - NOTE: can be called by pcmrec thread when splitting streams */ -void enc_finish_chunk(void) -{ - struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index); - - if ((long)chunk->flags < 0) - { - /* encoder set error flag */ - errors |= PCMREC_E_ENCODER; - logf("finish chk enc error"); - } - - /* advance enc_wr_index to the next encoder chunk */ - INC_ENC_INDEX(enc_wr_index); - - if (enc_rd_index != enc_wr_index) - { - num_rec_bytes += chunk->enc_size; - num_rec_samples += chunk->num_pcm; -#if 0 - accum_rec_bytes += chunk->enc_size; - accum_pcm_samples += chunk->num_pcm; -#endif - } - else if (is_recording) /* buffer full */ - { - /* keep current position and put up warning flag */ - warnings |= PCMREC_W_ENC_BUFFER_OVF; - logf("enc_buffer ovf"); - DEC_ENC_INDEX(enc_wr_index); - if (pcmrec_context) - { - /* if stream splitting, keep this out of circulation and - flush a small number, then readd - cannot risk losing - stream markers */ - logf("mini flush"); - pcmrec_flush(PCMREC_FLUSH_MINI); - INC_ENC_INDEX(enc_wr_index); - } - } - else - { - /* advance enc_rd_index for prerecording */ - INC_ENC_INDEX(enc_rd_index); - } -} /* enc_finish_chunk */ - -/* passes a pointer to next chunk of unprocessed wav data */ -/* TODO: this really should give the actual size returned */ -unsigned char * enc_get_pcm_data(size_t size) -{ - int wp = dma_wr_pos; - size_t avail = (wp - pcm_rd_pos) & PCM_CHUNK_MASK; - - /* limit the requested pcm data size */ - if (size > PCM_MAX_FEED_SIZE) - size = PCM_MAX_FEED_SIZE; - - if (avail >= size) - { - unsigned char *ptr = pcm_buffer + pcm_rd_pos; - int next_pos = (pcm_rd_pos + size) & PCM_CHUNK_MASK; - - pcm_enc_pos = pcm_rd_pos; - pcm_rd_pos = next_pos; - - /* ptr must point to continous data at wraparound position */ - if ((size_t)pcm_rd_pos < size) - { - memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE, - pcm_buffer, pcm_rd_pos); - } - - if (avail >= (sample_rate << 2)) - { - /* Filling up - boost codec */ - trigger_cpu_boost(); - } - - pcm_buffer_empty = false; - return ptr; - } - - /* not enough data available - encoder should idle */ - pcm_buffer_empty = true; - - cancel_cpu_boost(); - - /* Sleep long enough to allow one frame on average */ - sleep(0); - - return NULL; -} /* enc_get_pcm_data */ - -/* puts some pcm data back in the queue */ -size_t enc_unget_pcm_data(size_t size) -{ - int wp = dma_wr_pos; - size_t old_avail = ((pcm_rd_pos - wp) & PCM_CHUNK_MASK) - - 2*PCM_CHUNK_SIZE; - - /* allow one interrupt to occur during this call and not have the - new read position inside the DMA destination chunk */ - if ((ssize_t)old_avail > 0) - { - /* limit size to amount of old data remaining */ - if (size > old_avail) - size = old_avail; - - pcm_enc_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK; - pcm_rd_pos = pcm_enc_pos; - - return size; - } - - return 0; -} /* enc_unget_pcm_data */ -- cgit v1.2.3