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/SOURCES | 3 - firmware/drivers/mas.c | 17 + firmware/drivers/tuner/s1a0903x01.c | 3 +- firmware/export/audio.h | 2 - firmware/export/id3.h | 246 ----- firmware/export/mas.h | 6 + firmware/export/mpeg.h | 66 -- firmware/export/pcm_record.h | 66 -- firmware/export/replaygain.h | 33 - firmware/mp3_playback.c | 5 +- firmware/pcm_record.c | 1787 ----------------------------------- firmware/powermgmt.c | 3 - 12 files changed, 28 insertions(+), 2209 deletions(-) delete mode 100644 firmware/export/id3.h delete mode 100644 firmware/export/mpeg.h delete mode 100644 firmware/export/pcm_record.h delete mode 100644 firmware/export/replaygain.h delete mode 100644 firmware/pcm_record.c (limited to 'firmware') diff --git a/firmware/SOURCES b/firmware/SOURCES index 9f7007245d..2cd1ba131c 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -191,9 +191,6 @@ pcm_sampr.c pcm.c #ifdef HAVE_RECORDING enc_base.c -#ifndef SIMULATOR -pcm_record.c -#endif /* SIMULATOR */ #endif /* HAVE_RECORDING */ #endif /* BOOTLOADER */ diff --git a/firmware/drivers/mas.c b/firmware/drivers/mas.c index 9d1761b419..4f384d3b98 100644 --- a/firmware/drivers/mas.c +++ b/firmware/drivers/mas.c @@ -466,3 +466,20 @@ unsigned long mas_readver(void) } #endif + +#if CONFIG_TUNER & S1A0903X01 +static int pllfreq; + +void mas_store_pllfreq(int freq) +{ + pllfreq = freq; +} + +int mas_get_pllfreq(void) +{ + return pllfreq; +} +#endif + + + diff --git a/firmware/drivers/tuner/s1a0903x01.c b/firmware/drivers/tuner/s1a0903x01.c index 517d41984d..f6442b269f 100644 --- a/firmware/drivers/tuner/s1a0903x01.c +++ b/firmware/drivers/tuner/s1a0903x01.c @@ -26,7 +26,6 @@ #include "kernel.h" #include "tuner.h" /* tuner abstraction interface */ #include "fmradio.h" /* physical interface driver */ -#include "mpeg.h" #include "sound.h" #define DEFAULT_IN1 0x100003 /* Mute */ @@ -64,7 +63,7 @@ int s1a0903x01_set(int setting, int value) int pitch = 1000; /* 4th harmonic falls in the FM frequency range */ - int if_freq = 4 * mpeg_get_mas_pllfreq(); + int if_freq = 4 * mas_get_pllfreq(); /* shift the mas harmonic >= 300 kHz away using the direction * which needs less shifting. */ diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 9530082050..661247df2f 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -30,8 +30,6 @@ #include "pcm_sampr.h" #include "pcm.h" #ifdef HAVE_RECORDING -#include "pcm_record.h" -#include "id3.h" #include "enc_base.h" #endif /* HAVE_RECORDING */ #endif /* CONFIG_CODEC == SWCODEC */ diff --git a/firmware/export/id3.h b/firmware/export/id3.h deleted file mode 100644 index da2faf1b12..0000000000 --- a/firmware/export/id3.h +++ /dev/null @@ -1,246 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 by Daniel Stenberg - * - * 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. - * - ****************************************************************************/ -#ifndef ID3_H -#define ID3_H - -#include -#include "config.h" -#include "file.h" - -#define ID3V2_BUF_SIZE 300 - -/* Audio file types. */ -/* NOTE: The values of the AFMT_* items are used for the %fc tag in the WPS - - so new entries MUST be added to the end to maintain compatibility. - */ -enum -{ - AFMT_UNKNOWN = 0, /* Unknown file format */ - - /* start formats */ - - AFMT_MPA_L1, /* MPEG Audio layer 1 */ - AFMT_MPA_L2, /* MPEG Audio layer 2 */ - AFMT_MPA_L3, /* MPEG Audio layer 3 */ - -#if CONFIG_CODEC == SWCODEC - AFMT_AIFF, /* Audio Interchange File Format */ - AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */ - AFMT_OGG_VORBIS, /* Ogg Vorbis */ - AFMT_FLAC, /* FLAC */ - AFMT_MPC, /* Musepack */ - AFMT_A52, /* A/52 (aka AC3) audio */ - AFMT_WAVPACK, /* WavPack */ - AFMT_ALAC, /* Apple Lossless Audio Codec */ - AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */ - AFMT_SHN, /* Shorten */ - AFMT_SID, /* SID File Format */ - AFMT_ADX, /* ADX File Format */ - AFMT_NSF, /* NESM (NES Sound Format) */ - AFMT_SPEEX, /* Ogg Speex speech */ - AFMT_SPC, /* SPC700 save state */ - AFMT_APE, /* Monkey's Audio (APE) */ - AFMT_WMA, /* WMAV1/V2 in ASF */ - AFMT_MOD, /* Amiga MOD File Format */ - AFMT_SAP, /* Amiga 8Bit SAP Format */ -#endif - - /* add new formats at any index above this line to have a sensible order - - specified array index inits are used */ - /* format arrays defined in id3.c */ - - AFMT_NUM_CODECS, - -#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) - /* masks to decompose parts */ - CODEC_AFMT_MASK = 0x0fff, - CODEC_TYPE_MASK = 0x7000, - - /* switch for specifying codec type when requesting a filename */ - CODEC_TYPE_DECODER = (0 << 12), /* default */ - CODEC_TYPE_ENCODER = (1 << 12), -#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */ -}; - -#if CONFIG_CODEC == SWCODEC -#define CODEC_EXTENSION "codec" - -#ifdef HAVE_RECORDING -#define ENCODER_SUFFIX "_enc" -enum rec_format_indexes -{ - __REC_FORMAT_START_INDEX = -1, - - /* start formats */ - - REC_FORMAT_PCM_WAV, - REC_FORMAT_AIFF, - REC_FORMAT_WAVPACK, - REC_FORMAT_MPA_L3, - - /* add new formats at any index above this line to have a sensible order - - specified array index inits are used - REC_FORMAT_CFG_NUM_BITS should allocate enough bits to hold the range - REC_FORMAT_CFG_VALUE_LIST should be in same order as indexes - */ - - REC_NUM_FORMATS, - - REC_FORMAT_DEFAULT = REC_FORMAT_PCM_WAV, - REC_FORMAT_CFG_NUM_BITS = 2 -}; - -#define REC_FORMAT_CFG_VAL_LIST "wave,aiff,wvpk,mpa3" - -/* get REC_FORMAT_* corresponding AFMT_* */ -extern const int rec_format_afmt[REC_NUM_FORMATS]; -/* get AFMT_* corresponding REC_FORMAT_* */ -extern const int afmt_rec_format[AFMT_NUM_CODECS]; - -#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ - { label, root_fname, enc_root_fname, ext_list } -#else /* !HAVE_RECORDING */ -#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ - { label, root_fname, ext_list } -#endif /* HAVE_RECORDING */ -#else /* !SWCODEC */ - -#define AFMT_ENTRY(label, root_fname, enc_root_fname, ext_list) \ - { label, ext_list } -#endif /* CONFIG_CODEC == SWCODEC */ - -/* record describing the audio format */ -struct afmt_entry -{ - char label[8]; /* format label */ -#if CONFIG_CODEC == SWCODEC - char *codec_root_fn; /* root codec filename (sans _enc and .codec) */ -#ifdef HAVE_RECORDING - char *codec_enc_root_fn; /* filename of encoder codec */ -#endif -#endif - char *ext_list; /* double NULL terminated extension - list for type with the first as - the default for recording */ -}; - -/* database of labels and codecs. add formats per above enum */ -extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS]; - -struct mp3entry { - char path[MAX_PATH]; - char* title; - char* artist; - char* album; - char* genre_string; - char* disc_string; - char* track_string; - char* year_string; - char* composer; - char* comment; - char* albumartist; - char* grouping; - int discnum; - int tracknum; - int version; - int layer; - int year; - unsigned char id3version; - unsigned int codectype; - unsigned int bitrate; - unsigned long frequency; - unsigned long id3v2len; - unsigned long id3v1len; - unsigned long first_frame_offset; /* Byte offset to first real MP3 frame. - Used for skipping leading garbage to - avoid gaps between tracks. */ - unsigned long vbr_header_pos; - unsigned long filesize; /* without headers; in bytes */ - unsigned long length; /* song length in ms */ - unsigned long elapsed; /* ms played */ - - int lead_trim; /* Number of samples to skip at the beginning */ - int tail_trim; /* Number of samples to remove from the end */ - - /* Added for Vorbis */ - unsigned long samples; /* number of samples in track */ - - /* MP3 stream specific info */ - unsigned long frame_count; /* number of frames in the file (if VBR) */ - - /* Used for A52/AC3 */ - unsigned long bytesperframe; /* number of bytes per frame (if CBR) */ - - /* Xing VBR fields */ - bool vbr; - bool has_toc; /* True if there is a VBR header in the file */ - unsigned char toc[100]; /* table of contents */ - - /* these following two fields are used for local buffering */ - char id3v2buf[ID3V2_BUF_SIZE]; - char id3v1buf[4][92]; - - /* resume related */ - unsigned long offset; /* bytes played */ - int index; /* playlist index */ - - /* runtime database fields */ - long tagcache_idx; /* 0=invalid, otherwise idx+1 */ - int rating; - int score; - long playcount; - long lastplayed; - long playtime; - - /* replaygain support */ - -#if CONFIG_CODEC == SWCODEC - char* track_gain_string; - char* album_gain_string; - long track_gain; /* 7.24 signed fixed point. 0 for no gain. */ - long album_gain; - long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ - long album_peak; -#endif - - /* Cuesheet support */ - int cuesheet_type; /* 0: none, 1: external, 2: embedded */ - - /* Musicbrainz Track ID */ - char* mb_track_id; -}; - -enum { - ID3_VER_1_0 = 1, - ID3_VER_1_1, - ID3_VER_2_2, - ID3_VER_2_3, - ID3_VER_2_4 -}; - -bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename); -bool mp3info(struct mp3entry *entry, const char *filename); -char* id3_get_num_genre(unsigned int genre_num); -int getid3v2len(int fd); -void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); -void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig); - -#endif diff --git a/firmware/export/mas.h b/firmware/export/mas.h index ab6135cbfa..493ed6a63c 100644 --- a/firmware/export/mas.h +++ b/firmware/export/mas.h @@ -163,3 +163,9 @@ int mas_codec_readreg(int reg); unsigned long mas_readver(void); #endif + +#if CONFIG_TUNER & S1A0903X01 +void mas_store_pllfreq(int freq); +int mas_get_pllfreq(void); +#endif + diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h deleted file mode 100644 index ce2cff0069..0000000000 --- a/firmware/export/mpeg.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 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. - * - ****************************************************************************/ -#ifndef _MPEG_H_ -#define _MPEG_H_ - -#include -#include "id3.h" - -#define MPEG_SWAP_CHUNKSIZE 0x2000 -#define MPEG_HIGH_WATER 2 /* We leave 2 bytes empty because otherwise we - wouldn't be able to see the difference between - an empty buffer and a full one. */ -#define MPEG_LOW_WATER 0x60000 -#define MPEG_RECORDING_LOW_WATER 0x80000 -#define MPEG_LOW_WATER_CHUNKSIZE 0x40000 -#define MPEG_LOW_WATER_SWAP_CHUNKSIZE 0x10000 -#ifdef HAVE_MMC -#define MPEG_PLAY_PENDING_THRESHOLD 0x20000 -#define MPEG_PLAY_PENDING_SWAPSIZE 0x20000 -#else -#define MPEG_PLAY_PENDING_THRESHOLD 0x10000 -#define MPEG_PLAY_PENDING_SWAPSIZE 0x10000 -#endif - -#define MPEG_MAX_PRERECORD_SECONDS 30 - -/* For ID3 info and VBR header */ -#define MPEG_RESERVED_HEADER_SPACE (4096 + 576) - -#if (CONFIG_CODEC == MAS3587F) || defined(SIMULATOR) - -#if CONFIG_TUNER & S1A0903X01 -int mpeg_get_mas_pllfreq(void); -#endif - -#endif -unsigned long mpeg_get_last_header(void); - -/* in order to keep the recording here, I have to expose this */ -void rec_tick(void); -void playback_tick(void); /* FixMe: get rid of this, use mp3_get_playtime() */ - -void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3)); -void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3)); -void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3)); -void audio_set_cuesheet_callback(bool (*handler)(const char *filename)); - -#endif diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h deleted file mode 100644 index f805313fe5..0000000000 --- a/firmware/export/pcm_record.h +++ /dev/null @@ -1,66 +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. - * - ****************************************************************************/ - -#ifndef PCM_RECORD_H -#define PCM_RECORD_H - -#define DMA_REC_ERROR_DMA (-1) -#ifdef HAVE_SPDIF_REC -#define DMA_REC_ERROR_SPDIF (-2) -#endif - -/** Warnings **/ -/* pcm (dma) buffer has overflowed */ -#define PCMREC_W_PCM_BUFFER_OVF 0x00000001 -/* encoder output buffer has overflowed */ -#define PCMREC_W_ENC_BUFFER_OVF 0x00000002 -/** Errors **/ -/* failed to load encoder */ -#define PCMREC_E_LOAD_ENCODER 0x80001000 -/* error originating in encoder */ -#define PCMREC_E_ENCODER 0x80002000 -/* filename queue has desynced from stream markers */ -#define PCMREC_E_FNQ_DESYNC 0x80004000 -/* I/O error has occurred */ -#define PCMREC_E_IO 0x80008000 -#ifdef DEBUG -/* encoder has written past end of allotted space */ -#define PCMREC_E_CHUNK_OVF 0x80010000 -#endif /* DEBUG */ - -/** General functions for high level codec recording **/ -/* pcm_rec_error_clear is deprecated for general use. audio_error_clear - should be used */ -void pcm_rec_error_clear(void); -/* pcm_rec_status is deprecated for general use. audio_status merges the - results for consistency with the hardware codec version */ -unsigned long pcm_rec_status(void); -unsigned long pcm_rec_get_warnings(void); -void pcm_rec_init(void); -int pcm_rec_current_bitrate(void); -int pcm_rec_encoder_afmt(void); /* AFMT_* value, AFMT_UNKNOWN if none */ -int pcm_rec_rec_format(void); /* Format index or -1 otherwise */ -unsigned long pcm_rec_sample_rate(void); -int pcm_get_num_unprocessed(void); - -/* audio.h contains audio_* recording functions */ - -#endif /* PCM_RECORD_H */ diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h deleted file mode 100644 index dbc079b1d3..0000000000 --- a/firmware/export/replaygain.h +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2005 Magnus Holmgren - * - * 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. - * - ****************************************************************************/ - -#ifndef _REPLAYGAIN_H -#define _REPLAYGAIN_H - -#include "id3.h" - -long get_replaygain_int(long int_gain); -long parse_replaygain(const char* key, const char* value, - struct mp3entry* entry, char* buffer, int length); -long parse_replaygain_int(bool album, long gain, long peak, - struct mp3entry* entry, char* buffer, int length); - -#endif diff --git a/firmware/mp3_playback.c b/firmware/mp3_playback.c index 66ea3159e3..2bbd08d789 100644 --- a/firmware/mp3_playback.c +++ b/firmware/mp3_playback.c @@ -26,7 +26,6 @@ #include "debug.h" #include "panic.h" #include -#include "mpeg.h" /* ToDo: remove crosslinks */ #include "mp3_playback.h" #include "sound.h" #ifndef SIMULATOR @@ -76,6 +75,10 @@ bool audio_is_initialized = false; /* FIX: this code pretty much assumes a MAS */ +/* dirty calls to mpeg.c */ +extern void playback_tick(void); +extern void rec_tick(void); + #ifndef SIMULATOR unsigned long mas_version_code; 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 */ diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index f0fbd7df92..86fadff2a2 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -46,9 +46,6 @@ #ifdef HAVE_LCD_BITMAP #include "font.h" #endif -#if defined(HAVE_RECORDING) && (CONFIG_CODEC == SWCODEC) -#include "pcm_record.h" -#endif #include "logf.h" #include "lcd-remote.h" #ifdef SIMULATOR -- cgit v1.2.3