summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2006-08-28 22:38:41 +0000
committerMichael Sevakis <jethead71@rockbox.org>2006-08-28 22:38:41 +0000
commit4fc717a4c19a1fe0349977d7b9c86561c5a5cf2d (patch)
treeeb6113c7491072f7b5136e24775737764e2cdede /firmware
parent65c2c58b3aa26164bd919665e4d710efa2fa7c79 (diff)
downloadrockbox-4fc717a4c19a1fe0349977d7b9c86561c5a5cf2d.zip
rockbox-4fc717a4c19a1fe0349977d7b9c86561c5a5cf2d.tar.gz
rockbox-4fc717a4c19a1fe0349977d7b9c86561c5a5cf2d.tar.bz2
rockbox-4fc717a4c19a1fe0349977d7b9c86561c5a5cf2d.tar.xz
Added FS#2939 Encoder Codec Interface + Codecs by Antonius Hellmann with additional FM Recording support and my modifications
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10789 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/drivers/power.c2
-rw-r--r--firmware/export/audio.h46
-rw-r--r--firmware/export/config-h100.h3
-rw-r--r--firmware/export/config-h120.h3
-rw-r--r--firmware/export/config-h300.h3
-rw-r--r--firmware/export/config-iaudiox5.h4
-rw-r--r--firmware/export/fmradio.h16
-rw-r--r--firmware/export/id3.h42
-rw-r--r--firmware/export/pcm_record.h14
-rw-r--r--firmware/id3.c71
-rw-r--r--firmware/mpeg.c2
-rw-r--r--firmware/pcm_record.c736
-rw-r--r--firmware/powermgmt.c3
13 files changed, 564 insertions, 381 deletions
diff --git a/firmware/drivers/power.c b/firmware/drivers/power.c
index 67f34e2..d5b75a1 100644
--- a/firmware/drivers/power.c
+++ b/firmware/drivers/power.c
@@ -38,7 +38,7 @@ bool charger_enabled;
static bool powered = false;
-bool radio_powered()
+bool radio_powered(void)
{
return powered;
}
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 9e3499e..2ee7f89 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -31,6 +31,9 @@
#define AUDIO_STATUS_PRERECORD 8
#define AUDIO_STATUS_ERROR 16
+#define AUDIO_STATUS_STAYON_FLAGS \
+ (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE | AUDIO_STATUS_RECORD | AUDIO_)
+
#define AUDIOERR_DISK_FULL 1
#define AUDIO_GAIN_LINEIN 0
@@ -69,6 +72,7 @@ void audio_resume(void);
void audio_next(void);
void audio_prev(void);
int audio_status(void);
+bool audio_query_poweroff(void);
int audio_track_count(void); /* SWCODEC only */
void audio_pre_ff_rewind(void); /* SWCODEC only */
void audio_ff_rewind(long newtime);
@@ -93,14 +97,56 @@ void audio_stop_recording(void);
void audio_pause_recording(void);
void audio_resume_recording(void);
void audio_new_file(const char *filename);
+
+/* audio sources */
+enum
+{
+ AUDIO_SRC_PLAYBACK = -1, /* for audio playback (default) */
+ AUDIO_SRC_MIC, /* monitor mic */
+ AUDIO_SRC_LINEIN, /* monitor line in */
+#ifdef HAVE_SPDIF_IN
+ AUDIO_SRC_SPDIF, /* monitor spdif */
+#endif
+#if defined(HAVE_FMRADIO_IN) || defined(CONFIG_TUNER)
+ AUDIO_SRC_FMRADIO, /* monitor fm radio */
+#endif
+ /* define new audio sources above this line */
+ AUDIO_SOURCE_LIST_END,
+ /* AUDIO_SRC_FMRADIO must be declared #ifdef CONFIG_TUNER but is not in
+ the list of recordable sources. HAVE_FMRADIO_IN implies CONFIG_TUNER. */
+#if defined(HAVE_FMRADIO_IN) || !defined(CONFIG_TUNER)
+ AUDIO_NUM_SOURCES = AUDIO_SOURCE_LIST_END,
+#else
+ AUDIO_NUM_SOURCES = AUDIO_SOURCE_LIST_END-1,
+#endif
+ AUDIO_SRC_MAX = AUDIO_NUM_SOURCES-1
+};
+
+/* channel modes */
+enum
+{
+ CHN_MODE_MONO = 1,
+ CHN_MODE_STEREO,
+};
void audio_set_recording_options(int frequency, int quality,
int source, int channel_mode,
bool editable, int prerecord_time);
void audio_set_recording_gain(int left, int right, int type);
unsigned long audio_recorded_time(void);
unsigned long audio_num_recorded_bytes(void);
+#if 0
+#ifdef HAVE_SPDIF_POWER
void audio_set_spdif_power_setting(bool on);
+#endif
+#endif
unsigned long audio_get_spdif_sample_rate(void);
+#if CONFIG_CODEC == SWCODEC
+/* audio encoder functions (defined in playback.c) */
+int audio_get_encoder_id(void);
+void audio_load_encoder(int enc_id);
+void audio_remove_encoder(void);
+#endif /* CONFIG_CODEC == SWCODEC */
+
diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h
index c21c8a1..8f93686 100644
--- a/firmware/export/config-h100.h
+++ b/firmware/export/config-h100.h
@@ -135,6 +135,9 @@
/* Someone with H100 and BDM, please verify if this works. */
/* #define HAVE_EEPROM */
+/* Define this for FM radio input available (not for SIMULATOR) */
+#define HAVE_FMRADIO_IN
+
#endif /* !SIMULATOR */
/* Define this for S/PDIF input available */
diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h
index 5ff567c..4ac4b2b 100644
--- a/firmware/export/config-h120.h
+++ b/firmware/export/config-h120.h
@@ -132,6 +132,9 @@
/* Define this if the EEPROM chip is used */
#define HAVE_EEPROM_SETTINGS
+/* Define this for FM radio input available (not for SIMULATOR) */
+#define HAVE_FMRADIO_IN
+
#endif /* !SIMULATOR */
/* Define this for S/PDIF input available */
diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h
index d5c54d8..c9c0b04 100644
--- a/firmware/export/config-h300.h
+++ b/firmware/export/config-h300.h
@@ -140,4 +140,7 @@
/* Define this if there is an EEPROM chip */
#define HAVE_EEPROM
+/* Define this for FM radio input available (not for SIMULATOR) */
+#define HAVE_FMRADIO_IN
+
#endif /* SIMULATOR */
diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h
index 8a2ed7a..d5c67c0 100644
--- a/firmware/export/config-iaudiox5.h
+++ b/firmware/export/config-iaudiox5.h
@@ -17,6 +17,7 @@
/* define this if you have access to the quickscreen */
#define HAVE_QUICKSCREEN
+
/* define this if you have access to the pitchscreen */
#define HAVE_PITCHSCREEN
@@ -81,6 +82,9 @@
should be defined as well. */
#define HAVE_LCD_SLEEP
+/* Define this for FM radio input available (not for SIMULATOR) */
+#define HAVE_FMRADIO_IN
+
/* Define this if you have a Motorola SCF5250 */
#define CONFIG_CPU MCF5250
diff --git a/firmware/export/fmradio.h b/firmware/export/fmradio.h
index 3c55fb7..7311323 100644
--- a/firmware/export/fmradio.h
+++ b/firmware/export/fmradio.h
@@ -20,6 +20,22 @@
#ifndef FMRADIO_H
#define FMRADIO_H
+/** declare some stuff here so powermgmt.c can properly tell if the radio is
+ actually playing and not just paused. This break in heirarchy is allowed
+ for audio_status(). **/
+
+/* set when radio is playing or paused within fm radio screen */
+#define FMRADIO_OFF 0x0
+#define FMRADIO_PLAYING 0x1
+#define FMRADIO_PAUSED 0x2
+
+/* returns the IN flag */
+#define FMRADIO_IN_SCREEN(s) ((s) & FMRADIO_IN_FLAG)
+#define FMRADIO_STATUS_PLAYING(s) ((s) & FMRADIO_PLAYING_OUT)
+#define FMRADIO_STATUS_PAUSED(s) ((s) & FMRADIO_PAUSED_OUT)
+
+extern int get_radio_status(void);
+
extern int fmradio_read(int addr);
extern void fmradio_set(int addr, int data);
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index 7930cd9..dc58178 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -24,7 +24,6 @@
#include "file.h"
/* Audio file types. */
-/* NOTE: When adding new audio types, also add to codec_labels[] in id3.c */
enum {
AFMT_UNKNOWN = 0, /* Unknown file format */
@@ -46,9 +45,48 @@ enum {
/* New formats must be added to the end of this list */
- AFMT_NUM_CODECS
+ AFMT_NUM_CODECS,
+
+#if CONFIG_CODEC == SWCODEC
+ /* 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
};
+#if CONFIG_CODEC == SWCODEC
+#define AFMT_ENTRY(label, codec_fname, codec_enc_fname, enc_ext) \
+ { label, codec_fname, codec_enc_fname, enc_ext }
+#else
+#define AFMT_ENTRY(label, codec_fname, codec_enc_fname, enc_ext) \
+ { label }
+#endif
+
+/* record describing the audio format */
+struct afmt_entry
+{
+#if CONFIG_CODEC == SWCODEC
+ char label[8]; /* format label */
+ char *codec_fn; /* filename of decoder codec */
+ char *codec_enc_fn; /* filename of encoder codec */
+ char *ext; /* default extension for file (enc only for now) */
+#else
+ char label[4];
+#endif
+};
+
+/* database of labels and codecs. add formats per above enum */
+extern const struct afmt_entry audio_formats[AFMT_NUM_CODECS];
+
+#if CONFIG_CODEC == SWCODEC
+/* recording quality to AFMT_* */
+extern const int rec_quality_info_afmt[9];
+#endif
+
struct mp3entry {
char path[MAX_PATH];
char* title;
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
index 5e2d7b7..b217335 100644
--- a/firmware/export/pcm_record.h
+++ b/firmware/export/pcm_record.h
@@ -20,10 +20,22 @@
#ifndef PCM_RECORD_H
#define PCM_RECORD_H
+void enc_set_parameters(int chunk_size, int num_chunks,
+ int samp_per_chunk, char *head_ptr, int head_size,
+ int enc_id);
+void enc_get_inputs(int *buffer_size, int *channels, int *quality);
+unsigned int* enc_alloc_chunk(void);
+void enc_free_chunk(void);
+int enc_wavbuf_near_empty(void);
+char* enc_get_wav_data(int size);
+extern void (*enc_set_header_callback)(void *head_buffer, int head_size,
+ int num_pcm_samples, bool is_file_header);
+
unsigned long pcm_rec_status(void);
void pcm_rec_init(void);
void pcm_rec_mux(int source);
-
+int pcm_rec_current_bitrate(void);
+int pcm_get_num_unprocessed(void);
void pcm_rec_get_peaks(int *left, int *right);
/* audio.h contains audio recording functions */
diff --git a/firmware/id3.c b/firmware/id3.c
index 35e0517..3f2ba23 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -85,28 +85,59 @@ static const char* const genres[] = {
"Synthpop"
};
-static const char* const codec_labels[] = {
- "???", /* Unknown file format */
-
- "MP1", /* MPEG Audio layer 1 */
- "MP2", /* MPEG Audio layer 2 */
- "MP3", /* MPEG Audio layer 3 */
-
+/* database of audio formats */
+const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
+{
+ /* Unknown file format */
+ AFMT_ENTRY("???", NULL, NULL, NULL ),
+ /* MPEG Audio layer 1 */
+ AFMT_ENTRY("MP1", "mpa.codec", NULL, NULL ),
+ /* MPEG Audio layer 2 */
+ AFMT_ENTRY("MP2", "mpa.codec", NULL, NULL ),
+ /* MPEG Audio layer 3 */
+ AFMT_ENTRY("MP3", "mpa.codec", "mp3_enc.codec", ".mp3"),
#if CONFIG_CODEC == SWCODEC
- "WAV", /* Uncompressed PCM in a WAV file */
- "Ogg", /* Ogg Vorbis */
- "FLAC", /* FLAC */
- "MPC", /* Musepack */
- "AC3", /* A/52 (aka AC3) audio */
- "WV", /* WavPack */
- "ALAC", /* Apple Lossless Audio Codec */
- "AAC", /* Advanced Audio Coding in M4A container */
- "SHN", /* Shorten */
- "AIFF", /* Audio Interchange File Format */
- "SID", /* SID File Format */
+ /* Uncompressed PCM in a WAV file */
+ AFMT_ENTRY("WAV", "wav.codec", "wav_enc.codec", ".wav"),
+ /* Ogg Vorbis */
+ AFMT_ENTRY("Ogg", "vorbis.codec", NULL, NULL ),
+ /* FLAC */
+ AFMT_ENTRY("FLAC", "flac.codec", NULL, NULL ),
+ /* Musepack */
+ AFMT_ENTRY("MPC", "mpc.codec", NULL, NULL ),
+ /* A/52 (aka AC3) audio */
+ AFMT_ENTRY("AC3", "a52.codec", NULL, NULL ),
+ /* WavPack */
+ AFMT_ENTRY("WV", "wavpack.codec", "wavpack_enc.codec", ".wv" ),
+ /* Apple Lossless Audio Codec */
+ AFMT_ENTRY("ALAC", "alac.codec", NULL, NULL ),
+ /* Advanced Audio Coding in M4A container */
+ AFMT_ENTRY("AAC", "aac.codec", NULL, NULL ),
+ /* Shorten */
+ AFMT_ENTRY("SHN", "shorten.codec", NULL, NULL ),
+ /* Audio Interchange File Format */
+ AFMT_ENTRY("AIFF", "aiff.codec", NULL, NULL ),
+ /* SID File Format */
+ AFMT_ENTRY("SID", "sid.codec", NULL, NULL ),
#endif
};
+#if CONFIG_CODEC == SWCODEC
+/* recording quality to AFMT_* */
+const int rec_quality_info_afmt[9] =
+{
+ AFMT_MPA_L3, /* MPEG L3 64 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 96 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 128 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 160 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 192 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 224 kBit/s */
+ AFMT_MPA_L3, /* MPEG L3 320 kBit/s */
+ AFMT_WAVPACK, /* WavPack 909 kBit/s */
+ AFMT_PCM_WAV, /* PCM Wav 1411 kBit/s */
+};
+#endif /* SWCODEC */
+
char* id3_get_genre(const struct mp3entry* id3)
{
if( id3->genre_string )
@@ -119,8 +150,8 @@ char* id3_get_genre(const struct mp3entry* id3)
char* id3_get_codec(const struct mp3entry* id3)
{
- if (id3->codectype < sizeof(codec_labels)/sizeof(char*)) {
- return (char*)codec_labels[id3->codectype];
+ if (id3->codectype < AFMT_NUM_CODECS) {
+ return (char*)audio_formats[id3->codectype].label;
} else {
return NULL;
}
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 7034f38..df0cbba 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -2484,7 +2484,7 @@ void audio_set_recording_options(int frequency, int quality,
DEBUGF("mas_writemem(MAS_BANK_D0, IO_CONTROL_MAIN, %x)\n", shadow_io_control_main);
- if(source == 0) /* Mic */
+ if(source == AUDIO_SRC_MIC)
{
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
index fee2bbd..4e4d5f2 100644
--- a/firmware/pcm_record.c
+++ b/firmware/pcm_record.c
@@ -50,6 +50,8 @@
#include "lcd.h"
#include "lcd-remote.h"
#include "pcm_playback.h"
+#include "sound.h"
+#include "id3.h"
#include "pcm_record.h"
extern int boost_counter; /* used for boost check */
@@ -57,43 +59,26 @@ extern int boost_counter; /* used for boost check */
/***************************************************************************/
static bool is_recording; /* We are recording */
-static bool is_stopping; /* Are we going to stop */
static bool is_paused; /* We have paused */
static bool is_error; /* An error has occured */
static unsigned long num_rec_bytes; /* Num bytes recorded */
static unsigned long num_file_bytes; /* Num bytes written to current file */
static int error_count; /* Number of DMA errors */
+static unsigned long num_pcm_samples; /* Num pcm samples written to current file */
-static long record_start_time; /* Value of current_tick when recording was started */
-static long pause_start_time; /* Value of current_tick when pause was started */
-static volatile int buffered_chunks; /* number of valid chunks in buffer */
-static unsigned int sample_rate; /* Sample rate at time of recording start */
-static int rec_source; /* Current recording source */
+static long record_start_time; /* current_tick when recording was started */
+static long pause_start_time; /* current_tick when pause was started */
+static unsigned int sample_rate; /* Sample rate at time of recording start */
+static int rec_source; /* Current recording source */
static int wav_file;
static char recording_filename[MAX_PATH];
-static bool init_done, close_done, record_done, stop_done, pause_done, resume_done, new_file_done;
+static volatile bool init_done, close_done, record_done;
+static volatile bool stop_done, pause_done, resume_done, new_file_done;
-static short peak_left, peak_right;
-
-/***************************************************************************/
-
-/*
- Some estimates:
- Normal recording rate: 44100 HZ * 4 = 176 KB/s
- Total buffer size: 32 MB / 176 KB/s = 181s before writing to disk
-*/
-
-#define CHUNK_SIZE 8192 /* Multiple of 4 */
-#define WRITE_THRESHOLD 250 /* (2 MB) Write when this many chunks (or less) until buffer full */
-
-#define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)])
-
-static unsigned int rec_buffer_offset;
-static unsigned char *rec_buffer; /* Circular recording buffer */
-static int num_chunks; /* Number of chunks available in rec_buffer */
+static int peak_left, peak_right;
#ifdef IAUDIO_X5
#define SET_IIS_PLAY(x) IIS1CONFIG = (x);
@@ -103,27 +88,71 @@ static int num_chunks; /* Number of chunks available in rec_buffer *
#define SET_IIS_REC(x) IIS1CONFIG = (x);
#endif
-/*
- Overrun occures when DMA needs to write a new chunk and write_index == read_index
- Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk
- more often.
-*/
-
-static int write_index; /* Current chunk the DMA is writing to */
-static int read_index; /* Oldest chunk that is not written to disk */
-static int read2_index; /* Latest chunk that has not been converted to little endian */
-static long pre_record_ticks; /* pre-record time expressed in ticks */
-static int pre_record_chunks; /* pre-record time expressed in chunks */
+/****************************************************************************
+ use 2 circular buffers of same size:
+ rec_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 rec_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:
+ 1.main: codec_load_encoder(); start the encoder
+ 2.encoder: enc_get_inputs(); get encoder buffsize, mono/stereo, quality
+ 3.encoder: enc_set_parameters(); set the encoder parameters (max.chunksize)
+ 4.encoder: enc_get_wav_data(); get n bytes of unprocessed pcm data
+ 5.encoder: enc_wavbuf_near_empty();if true: reduce cpu_boost
+ 6.encoder: enc_alloc_chunk(); get a ptr to next enc chunk
+ 7.encoder: <process enc chunk> compress and store data to enc chunk
+ 8.encoder: enc_free_chunk(); inform main about chunk process finished
+ 9.encoder: repeat 4. to 8.
+ A.main: enc_set_header_callback(); create the current format header (file)
+****************************************************************************/
+#define NUM_CHUNKS 256 /* Power of 2 */
+#define CHUNK_SIZE 8192 /* Power of 2 */
+#define MAX_FEED_SIZE 20000 /* max pcm size passed to encoder */
+#define CHUNK_MASK (NUM_CHUNKS * CHUNK_SIZE - 1)
+#define WRITE_THRESHOLD (44100 * 5 / enc_samp_per_chunk) /* 5sec */
+#define GET_CHUNK(x) (long*)(&rec_buffer[x])
+#define GET_ENC_CHUNK(x) (long*)(&enc_buffer[enc_chunk_size*(x)])
+
+static int audio_enc_id; /* current encoder id */
+static unsigned char *rec_buffer; /* Circular recording buffer */
+static unsigned char *enc_buffer; /* Circular encoding buffer */
+static unsigned char *enc_head_buffer; /* encoder header buffer */
+static int enc_head_size; /* used size in header buffer */
+static int write_pos; /* Current chunk pos for DMA writing */
+static int read_pos; /* Current chunk pos for encoding */
+static long pre_record_ticks;/* pre-record time expressed in ticks */
+static int enc_wr_index; /* Current encoding chunk write index */
+static int enc_rd_index; /* Current encoding chunk read index */
+static int enc_chunk_size; /* maximum encoder chunk size */
+static int enc_num_chunks; /* number of chunks in ringbuffer */
+static int enc_buffer_size; /* encode buffer size */
+static int enc_channels; /* 1=mono 2=stereo */
+static int enc_quality; /* mp3: 64,96,128,160,192,320 kBit */
+static int enc_samp_per_chunk;/* pcm samples per encoder chunk */
+static bool wav_queue_empty; /* all wav chunks processed? */
+static unsigned long avrg_bit_rate; /* average bit rates from chunks */
+static unsigned long curr_bit_rate; /* cumulated bit rates from chunks */
+static unsigned long curr_chunk_cnt; /* number of processed chunks */
+
+void (*enc_set_header_callback)(void *head_buffer, int head_size,
+ int num_pcm_samples, bool is_file_header);
/***************************************************************************/
static struct event_queue pcmrec_queue;
-static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
+static long pcmrec_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
static const char pcmrec_thread_name[] = "pcmrec";
static void pcmrec_thread(void);
static void pcmrec_dma_start(void);
static void pcmrec_dma_stop(void);
+static void close_wave(void);
/* Event IDs */
#define PCMREC_INIT 1 /* Enable recording */
@@ -144,10 +173,16 @@ static void pcmrec_dma_stop(void);
void pcm_rec_init(void)
{
queue_init(&pcmrec_queue);
- create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name);
+ create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
+ pcmrec_thread_name);
}
+int audio_get_encoder_id(void)
+{
+ return audio_enc_id;
+}
+
/* Initializes recording:
* - Set up the UDA1380/TLV320 for recording
* - Prepare for DMA transfers
@@ -155,13 +190,14 @@ void pcm_rec_init(void)
void audio_init_recording(unsigned int buffer_offset)
{
- rec_buffer_offset = buffer_offset;
+ (void)buffer_offset;
+
init_done = false;
queue_post(&pcmrec_queue, PCMREC_INIT, 0);
while(!init_done)
sleep_thread();
- wake_up_thread();
+ wake_up_thread();
}
void audio_close_recording(void)
@@ -171,7 +207,9 @@ void audio_close_recording(void)
while(!close_done)
sleep_thread();
- wake_up_thread();
+ wake_up_thread();
+
+ audio_remove_encoder();
}
unsigned long pcm_rec_status(void)
@@ -184,10 +222,17 @@ unsigned long pcm_rec_status(void)
ret |= AUDIO_STATUS_PAUSE;
if (is_error)
ret |= AUDIO_STATUS_ERROR;
+ if (!is_recording && pre_record_ticks && init_done && !close_done)
+ ret |= AUDIO_STATUS_PRERECORD;
return ret;
}
+int pcm_rec_current_bitrate(void)
+{
+ return avrg_bit_rate;
+}
+
unsigned long audio_recorded_time(void)
{
if (is_recording)
@@ -248,6 +293,8 @@ unsigned long audio_get_spdif_sample_rate(void)
}
#endif
+#if 0
+/* not needed atm */
#ifdef HAVE_SPDIF_POWER
static bool spdif_power_setting;
@@ -256,21 +303,18 @@ void audio_set_spdif_power_setting(bool on)
spdif_power_setting = on;
}
#endif
+#endif
/**
- * Sets the audio source
+ * Sets recording parameters
*
* This functions starts feeding the CPU with audio data over the I2S bus
- *
- * @param source 0=mic, 1=line-in, 2=spdif
*/
void audio_set_recording_options(int frequency, int quality,
int source, int channel_mode,
bool editable, int prerecord_time)
{
/* TODO: */
- (void)quality;
- (void)channel_mode;
(void)editable;
/* NOTE: Coldfire UDA based recording does not yet support anything other
@@ -278,69 +322,30 @@ void audio_set_recording_options(int frequency, int quality,
* based recording will overwrite this value with the proper sample rate in
* audio_record(), and will not be affected by this.
*/
- frequency = 44100;
+ frequency = 44100;
+ enc_quality = quality;
+ rec_source = source;
+ enc_channels = channel_mode == CHN_MODE_MONO ? 1 : 2;
pre_record_ticks = prerecord_time * HZ;
- pre_record_chunks = ((frequency * prerecord_time * 4)/CHUNK_SIZE)+1;
- if(pre_record_chunks >= (num_chunks-250))
- {
- /* we can't prerecord more than our buffersize minus treshold to write to disk! */
- pre_record_chunks = num_chunks-250;
- /* don't forget to recalculate that time! */
- pre_record_ticks = ((pre_record_chunks * CHUNK_SIZE)/(4*frequency)) * HZ;
- }
-
- //logf("pcmrec: src=%d", source);
- rec_source = source;
-#ifdef HAVE_SPDIF_POWER
- /* Check if S/PDIF output power should be switched off or on. NOTE: assumes
- both optical in and out is controlled by the same power source, which is
- the case on H1x0. */
- spdif_power_enable((source == 2) || spdif_power_setting);
-#endif
switch (source)
{
- /* mic */
- case 0:
- /* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */
- DATAINCONTROL = 0xc020;
-
-#ifdef HAVE_UDA1380
- uda1380_enable_recording(true);
+ case AUDIO_SRC_MIC:
+ case AUDIO_SRC_LINEIN:
+#ifdef HAVE_FMRADIO_IN
+ case AUDIO_SRC_FMRADIO:
#endif
-#ifdef HAVE_TLV320
- tlv320_enable_recording(true);
-#endif
- break;
-
- /* line-in */
- case 1:
/* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */
DATAINCONTROL = 0xc020;
-
-#ifdef HAVE_UDA1380
- uda1380_enable_recording(false);
-#endif
-#ifdef HAVE_TLV320
- tlv320_enable_recording(false);
-#endif
break;
-#ifdef HAVE_SPDIF_IN
- /* SPDIF */
- case 2:
+
+#ifdef HAVE_SPDIF_IN
+ case AUDIO_SRC_SPDIF:
/* Int. when 6 samples in FIFO. PDIR2 source = ebu1RcvData */
DATAINCONTROL = 0xc038;
-#ifdef HAVE_SPDIF_POWER
- EBU1CONFIG = spdif_power_setting ? (1 << 2) : 0;
- /* Input source is EBUin1, Feed-through monitoring if desired */
-#else
- EBU1CONFIG = (1 << 2);
- /* Input source is EBUin1, Feed-through monitoring */
-#endif
- uda1380_disable_recording();
break;
-#endif
- }
+#endif /* HAVE_SPDIF_IN */
+ }
sample_rate = frequency;
@@ -349,7 +354,8 @@ void audio_set_recording_options(int frequency, int quality,
SET_IIS_PLAY(0x800); /* Reset before reprogram */
#ifdef HAVE_SPDIF_IN
- if (source == 2) {
+ if (source == AUDIO_SRC_SPDIF)
+ {
/* SCLK2 = Audioclk/4 (can't use EBUin clock), TXSRC = EBU1rcv, 64 bclk/wclk */
IIS2CONFIG = (6 << 12) | (7 << 8) | (4 << 2);
/* S/PDIF feed-through already configured */
@@ -367,6 +373,8 @@ void audio_set_recording_options(int frequency, int quality,
/* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */
SET_IIS_PLAY( (8 << 12) | (4 << 8) | (4 << 2) );
#endif
+
+ audio_load_encoder(rec_quality_info_afmt[quality]);
}
@@ -380,10 +388,9 @@ void audio_set_recording_options(int frequency, int quality,
void audio_set_recording_gain(int left, int right, int type)
{
//logf("rcmrec: t=%d l=%d r=%d", type, left, right);
-#ifdef HAVE_UDA1380
+#if defined(HAVE_UDA1380)
uda1380_set_recvol(left, right, type);
-#endif
-#ifdef HAVE_TLV320
+#elif defined (HAVE_TLV320)
tlv320_set_recvol(left, right, type);
#endif
}
@@ -406,7 +413,7 @@ void audio_record(const char *filename)
recording_filename[MAX_PATH - 1] = 0;
#ifdef HAVE_SPDIF_IN
- if (rec_source == 2)
+ if (rec_source == AUDIO_SRC_SPDIF)
sample_rate = audio_get_spdif_sample_rate();
#endif
@@ -447,6 +454,7 @@ void audio_stop_recording(void)
logf("pcm_stop");
+ is_paused = true; /* fix pcm write ptr at current position */
stop_done = false;
queue_post(&pcmrec_queue, PCMREC_STOP, 0);
@@ -499,9 +507,9 @@ void audio_resume_recording(void)
void pcm_rec_get_peaks(int *left, int *right)
{
if (left)
- *left = (int)peak_left;
+ *left = peak_left;
if (right)
- *right = (int)peak_right;
+ *right = peak_right;
peak_left = 0;
peak_right = 0;
}
@@ -511,7 +519,7 @@ void pcm_rec_get_peaks(int *left, int *right)
/***************************************************************************/
/**
- * Process the chunks using read_index and write_index.
+ * Process the chunks
*
* This function is called when queue_get_w_tmo times out.
*
@@ -519,70 +527,24 @@ void pcm_rec_get_peaks(int *left, int *right)
* they want to save everything in the buffers to disk.
*
*/
-
-static void pcmrec_callback(bool flush) ICODE_ATTR;
static void pcmrec_callback(bool flush)
{
- int num_ready, num_free, num_new;
- short *ptr;
- short value;
- int i, j, w;
-
- w = write_index;
-
- num_new = w - read2_index;
- if (num_new < 0)
- num_new += num_chunks;
-
- for (i=0; i<num_new; i++)
- {
- /* Convert the samples to little-endian so we only have to write later
- (Less hd-spinning time), also do peak detection while we're at it
- */
- ptr = GET_CHUNK(read2_index);
- for (j=0; j<CHUNK_SIZE/4; j++)
- {
- value = *ptr;
- if(value > peak_left)
- peak_left = value;
- else if (-value > peak_left)
- peak_left = -value;
-
- *ptr = htole16(value);
- ptr++;
-
- value = *ptr;
- if(value > peak_right)
- peak_right = value;
- else if (-value > peak_right)
- peak_right = -value;
-
- *ptr = htole16(value);
- ptr++;
- }
+ int i, num_ready, size_yield;
+ long *enc_chunk, chunk_size;
- if(is_recording && !is_paused)
- num_rec_bytes += CHUNK_SIZE;
-
- read2_index++;
- if (read2_index >= num_chunks)
- read2_index = 0;
- }
-
- if ((!is_recording || is_paused) && !flush)
- {
- /* not recording = no saving to disk, fake buffer clearing */
- read_index = write_index;
+ if (!is_recording && !flush)
return;
- }
- num_ready = w - read_index;
+ num_ready = enc_wr_index - enc_rd_index;
if (num_ready < 0)
- num_ready += num_chunks;
+ num_ready += enc_num_chunks;
- num_free = num_chunks - num_ready;
-
- if (num_free <= WRITE_THRESHOLD || flush)
+ /* calculate an estimate of recorded bytes */
+ num_rec_bytes = num_file_bytes + num_ready * /* enc_chunk_size */
+ ((avrg_bit_rate * 1000 / 8 * enc_samp_per_chunk + 22050) / 44100);
+
+ /* near full state reached: less than 5sec remaining space */
+ if (enc_num_chunks - num_ready < WRITE_THRESHOLD || flush)
{
bool must_boost = (boost_counter ? false : true);
@@ -591,23 +553,41 @@ static void pcmrec_callback(bool flush)
if(must_boost)
cpu_boost(true);
+ size_yield = 0;
for (i=0; i<num_ready; i++)
{
- if (write(wav_file, GET_CHUNK(read_index), CHUNK_SIZE) != CHUNK_SIZE)
+ enc_chunk = GET_ENC_CHUNK(enc_rd_index);
+ chunk_size = *enc_chunk++;
+
+ /* safety net: if size entry got corrupted => limit */
+ if (chunk_size > (long)(enc_chunk_size - sizeof(long)))
+ chunk_size = enc_chunk_size - sizeof(long);
+
+ if (enc_set_header_callback != NULL)
+ enc_set_header_callback(enc_chunk, enc_chunk_size,
+ num_pcm_samples, false);
+
+ if (write(wav_file, enc_chunk, chunk_size) != chunk_size)
{
+ close_wave();
if(must_boost)
cpu_boost(false);
logf("pcmrec: write err");
- pcmrec_dma_stop();
- return;
+ is_error = true;
+ break;
}
-
- num_file_bytes += CHUNK_SIZE;
-
- read_index++;
- if (read_index >= num_chunks)
- read_index = 0;
- yield();
+
+ num_file_bytes += chunk_size;
+ num_pcm_samples += enc_samp_per_chunk;
+ size_yield += chunk_size;
+
+ if (size_yield >= 32768)
+ { /* yield when 32kB written */
+ size_yield = 0;
+ yield();
+ }
+
+ enc_rd_index = (enc_rd_index + 1) % enc_num_chunks;
}
if(must_boost)
@@ -623,36 +603,33 @@ static void pcmrec_callback(bool flush)
/* Abort dma transfer */
static void pcmrec_dma_stop(void)
{
- DCR1 = 0;
-
- is_error = true;
- is_recording = false;
-
+ DCR1 = 0;
+
error_count++;
-
+
+ DSR1 = 1; /* Clear interrupt */
+ IPR |= (1<<15); /* Clear pending interrupt request */
+
logf("dma1 stopped");
}
static void pcmrec_dma_start(void)
{
- DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
- SAR1 = (unsigned long)&PDIR2; /* Source address */
- BCR1 = CHUNK_SIZE; /* Bytes to transfer */
+ DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
+ SAR1 = (unsigned long)&PDIR2; /* Source address */
+ BCR1 = CHUNK_SIZE; /* Bytes to transfer */
/* Start the DMA transfer.. */
- DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
-
#ifdef HAVE_SPDIF_IN
INTERRUPTCLEAR = 0x03c00000;
#endif
- /* pre-recording: buffer count */
- buffered_chunks = 0;
+ /* 16Byte transfers prevents from sporadic errors during cpu_boost() */
+ DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_DSIZE(3) | DMA_START;
logf("dma1 started");
}
-
/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */
void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
void DMA1(void)
@@ -668,62 +645,64 @@ void DMA1(void)
logf("dma1 err: 0x%x", res);
- DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
+ DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
BCR1 = CHUNK_SIZE;
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
+
+ /* Flush recorded data to disk and stop recording */
+ queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
}
#ifdef HAVE_SPDIF_IN
- else if ((rec_source == 2) && (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */
+ else if ((rec_source == AUDIO_SRC_SPDIF) &&
+ (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */
{
INTERRUPTCLEAR = 0x03c00000;
error_count++;
logf("spdif err");
- if (is_stopping)
- {
- DCR1 = 0; /* Stop DMA transfer */
- is_stopping = false;
-
- logf("dma1 stopping");
- }
- else
- {
- DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
- BCR1 = CHUNK_SIZE;
- }
+ DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
+ BCR1 = CHUNK_SIZE;
}
#endif
else
{
- write_index++;
- if (write_index >= num_chunks)
- write_index = 0;
+ long peak_l, peak_r;
+ long *ptr, j;
- /* update number of valid chunks for pre-recording */
- if(buffered_chunks < num_chunks)
- buffered_chunks++;
+ ptr = GET_CHUNK(write_pos);
- if (is_stopping)
- {
- DCR1 = 0; /* Stop DMA transfer */
- is_stopping = false;
+ if (!is_paused) /* advance write position */
+ write_pos = (write_pos + CHUNK_SIZE) & CHUNK_MASK;
- logf("dma1 stopping");
- }
- else if (write_index == read_index)
- {
- DCR1 = 0; /* Stop DMA transfer */
- is_recording = false;
+ DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
+ BCR1 = CHUNK_SIZE;
- logf("dma1 overrun");
+ peak_l = peak_r = 0;
- }
- else
+ /* only peak every 4th sample */
+ for (j=0; j<CHUNK_SIZE/4; j+=4)
{
- DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
- BCR1 = CHUNK_SIZE;
+ long value = ptr[j];
+#ifdef ROCKBOX_BIG_ENDIAN
+ if (value > peak_l) peak_l = value;
+ else if (-value > peak_l) peak_l = -value;
+
+ value <<= 16;
+ if (value > peak_r) peak_r = value;
+ else if (-value > peak_r) peak_r = -value;
+#else
+ if (value > peak_r) peak_r = value;
+ else if (-value > peak_r) peak_r = -value;
+
+ value <<= 16;
+ if (value > peak_l) peak_l = value;
+ else if (-value > peak_l) peak_l = -value;
+#endif
}
+
+ peak_left = (int)(peak_l >> 16);
+ peak_right = (int)(peak_r >> 16);
}
IPR |= (1<<15); /* Clear pending interrupt request */
@@ -733,15 +712,8 @@ void DMA1(void)
/* Sets returns 0 if success, -1 on failure */
static int start_wave(void)
{
- unsigned char header[44] =
- {
- 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
- 0x10,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,
- 4,0,0x10,0,'d','a','t','a',0,0,0,0
- };
- unsigned long avg_bytes_per_sec;
-
wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC);
+
if (wav_file < 0)
{
wav_file = -1;
@@ -749,19 +721,9 @@ static int start_wave(void)
is_error = true;
return -1;
}
- /* Now set the sample rate field of the WAV header to what it should be */
- header[24] = (unsigned char)(sample_rate & 0xff);
- header[25] = (unsigned char)(sample_rate >> 8);
- header[26] = (unsigned char)(sample_rate >> 16);
- header[27] = (unsigned char)(sample_rate >> 24);
- /* And then the average bytes per second field */
- avg_bytes_per_sec = sample_rate*4; /* Hard coded to 16 bit stereo */
- header[28] = (unsigned char)(avg_bytes_per_sec & 0xff);
- header[29] = (unsigned char)(avg_bytes_per_sec >> 8);
- header[30] = (unsigned char)(avg_bytes_per_sec >> 16);
- header[31] = (unsigned char)(avg_bytes_per_sec >> 24);
- if (sizeof(header) != write(wav_file, header, sizeof(header)))
+ /* add main file header (enc_head_size=0 for encoders without) */
+ if (enc_head_size != write(wav_file, enc_head_buffer, enc_head_size))
{
close(wav_file);
wav_file = -1;
@@ -776,18 +738,22 @@ static int start_wave(void)
/* Update header and set correct length values */
static void close_wave(void)
{
- long l;
+ unsigned char head[100]; /* assume maximum 100 bytes for file header */
+ int size_read;
if (wav_file != -1)
{
- l = htole32(num_file_bytes + 36);
- lseek(wav_file, 4, SEEK_SET);
- write(wav_file, &l, 4);
-
- l = htole32(num_file_bytes);
- lseek(wav_file, 40, SEEK_SET);
- write(wav_file, &l, 4);
-
+ /* update header before closing the file (wav+wv encoder will do) */
+ if (enc_set_header_callback != NULL)
+ {
+ lseek(wav_file, 0, SEEK_SET);
+ /* try to read the head size (but we'll accept less) */
+ size_read = read(wav_file, head, sizeof(head));
+
+ enc_set_header_callback(head, size_read, num_pcm_samples, true);
+ lseek(wav_file, 0, SEEK_SET);
+ write(wav_file, head, size_read);
+ }
close(wav_file);
wav_file = -1;
}
@@ -795,8 +761,7 @@ static void close_wave(void)
static void pcmrec_start(void)
{
- int pre_chunks = pre_record_chunks; /* recalculate every time! */
- long pre_ticks = pre_record_ticks; /* recalculate every time! */
+ long max_pre_chunks, pre_ticks, max_pre_ticks;
logf("pcmrec_start");
@@ -808,7 +773,7 @@ static void pcmrec_start(void)
}
if (wav_file != -1)
- close(wav_file);
+ close_wave();
if (start_wave() != 0)
{
@@ -817,32 +782,29 @@ static void pcmrec_start(void)
return;
}
- /* pre-recording calculation */
- if(buffered_chunks < pre_chunks)
- {
- /* not enough good chunks available - limit pre-record time */
- pre_chunks = buffered_chunks;
- pre_ticks = ((buffered_chunks * CHUNK_SIZE)/(4*sample_rate)) * HZ;
- }
- record_start_time = current_tick - pre_ticks;
+ /* calculate maximum available chunks & resulting ticks */
+ max_pre_chunks = (enc_wr_index - enc_rd_index +
+ enc_num_chunks) % enc_num_chunks;
+ if (max_pre_chunks > enc_num_chunks - WRITE_THRESHOLD)
+ max_pre_chunks = enc_num_chunks - WRITE_THRESHOLD;
+ max_pre_ticks = max_pre_chunks * HZ * enc_samp_per_chunk / 44100;
- read_index = write_index - pre_chunks;
- if(read_index < 0)
- {
- read_index += num_chunks;
- }
+ /* limit prerecord if not enough data available */
+ pre_ticks = pre_record_ticks > max_pre_ticks ?
+ max_pre_ticks : pre_record_ticks;
+ max_pre_chunks = 44100 * pre_ticks / HZ / enc_samp_per_chunk;
+ enc_rd_index = (enc_wr_index - max_pre_chunks +
+ enc_num_chunks) % enc_num_chunks;
- peak_left = 0;
- peak_right = 0;
-
- num_rec_bytes = pre_chunks * CHUNK_SIZE;
+ record_start_time = current_tick - pre_ticks;
+
+ num_rec_bytes = enc_num_chunks * CHUNK_SIZE;
num_file_bytes = 0;
+ num_pcm_samples = 0;
pause_start_time = 0;
- is_stopping = false;
is_paused = false;
is_recording = true;
-
record_done = true;
}
@@ -850,36 +812,24 @@ static void pcmrec_stop(void)
{
logf("pcmrec_stop");
- if (!is_recording)
+ if (is_recording)
{
- stop_done = true;
- return;
- }
-
- if (!is_paused)
- {
- /* wait for recording to finish */
- is_stopping = true;
-
- while (is_stopping && is_recording)
+ /* wait for encoding finish */
+ is_paused = true;
+ while(!wav_queue_empty)
sleep_thread();
- wake_up_thread();
-
- is_stopping = false;
- }
-
- is_recording = false;
-
- /* Flush buffers to file */
- pcmrec_callback(true);
- close_wave();
+ wake_up_thread();
+ is_recording = false;
+ /* Flush buffers to file */
+ pcmrec_callback(true);
+ close_wave();
+ }
+
+ is_paused = false;
stop_done = true;
-
- /* Finally start dma again for peakmeters and pre-recoding to work. */
- pcmrec_dma_start();
-
+
logf("pcmrec_stop done");
}
@@ -898,7 +848,6 @@ static void pcmrec_new_file(void)
here is a good approximation when recording to the new file starts
*/
record_start_time = current_tick;
- num_rec_bytes = 0;
if (is_paused)
pause_start_time = record_start_time;
@@ -908,7 +857,9 @@ static void pcmrec_new_file(void)
close_wave();
+ num_rec_bytes = 0;
num_file_bytes = 0;
+ num_pcm_samples = 0;
/* start the new file */
if (start_wave() != 0)
@@ -932,20 +883,8 @@ static void pcmrec_pause(void)
return;
}
- /* Abort DMA transfer and flush to file? */
-
- is_stopping = true;
-
- while (is_stopping && is_recording)
- sleep_thread();
- wake_up_thread();
-
pause_start_time = current_tick;
- is_paused = true;
-
- /* Flush what we got in buffers to file */
- pcmrec_callback(true);
-
+ is_paused = true;
pause_done = true;
logf("pcmrec_pause done");
@@ -973,10 +912,7 @@ static void pcmrec_resume(void)
pause_start_time = 0;
}
- pcmrec_dma_start();
-
resume_done = true;
-
logf("pcmrec_resume done");
}
@@ -986,50 +922,47 @@ static void pcmrec_resume(void)
*/
static void pcmrec_init(void)
{
- unsigned long buffer_size;
-
wav_file = -1;
- read_index = 0;
- read2_index = 0;
- write_index = 0;
- pre_record_chunks = 0;
- pre_record_ticks = 0;
+ read_pos = 0;
+ write_pos = 0;
+ enc_wr_index = 0;
+ enc_rd_index = 0;
+
+ avrg_bit_rate = 0;
+ curr_bit_rate = 0;
+ curr_chunk_cnt = 0;
peak_left = 0;
peak_right = 0;
num_rec_bytes = 0;
num_file_bytes = 0;
+ num_pcm_samples = 0;
record_start_time = 0;
pause_start_time = 0;
- buffered_chunks = 0;
-
+
+ close_done = false;
is_recording = false;
- is_stopping = false;
is_paused = false;
is_error = false;
- rec_buffer = (unsigned char*)(((unsigned long)audiobuf + rec_buffer_offset) & ~3);
- buffer_size = (long)audiobufend - (long)audiobuf - rec_buffer_offset - 16;
-
- logf("buf size: %d kb", buffer_size/1024);
-
- num_chunks = buffer_size / CHUNK_SIZE;
-
- logf("num_chunks: %d", num_chunks);
+ rec_buffer = (unsigned char*)(((long)audiobuf + 15) & ~15);
+ enc_buffer = rec_buffer + NUM_CHUNKS * CHUNK_SIZE + MAX_FEED_SIZE;
+ /* 8000Bytes at audiobufend */
+ enc_buffer_size = audiobufend - enc_buffer - 8000;
- SET_IIS_PLAY(0x800); /* Stop any playback */
- AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
- DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */
+ SET_IIS_PLAY(0x800); /* Stop any playback */
+ AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
+ DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */
- DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
- DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
+ DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
+ DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2;
- ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */
- IMR &= ~(1<<15); /* bit 15 is DMA1 */
+ ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */
+ IMR &= ~(1<<15); /* bit 15 is DMA1 */
#ifdef HAVE_SPDIF_IN
- PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */
+ PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */
#endif
pcmrec_dma_start();
@@ -1038,23 +971,16 @@ static void pcmrec_init(void)
static void pcmrec_close(void)
{
-#ifdef HAVE_UDA1380
- uda1380_disable_recording();
-#endif
-#ifdef HAVE_TLV320
- tlv320_disable_recording();
-#endif
-
-#ifdef HAVE_SPDIF_POWER
- spdif_power_enable(spdif_power_setting);
-#endif
DMAROUTE = (DMAROUTE & 0xffff00ff);
ICR7 = 0x00; /* Disable interrupt */
IMR |= (1<<15); /* bit 15 is DMA1 */
+ pcmrec_dma_stop();
+
/* Reset PDIR2 data flow */
DATAINCONTROL = 0x200;
close_done = true;
+ init_done = false;
}
static void pcmrec_thread(void)
@@ -1064,10 +990,10 @@ static void pcmrec_thread(void)
logf("thread pcmrec start");
error_count = 0;
-
- while (1)
+
+ while(1)
{
- queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 40);
+ queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 4);
switch (ev.id)
{
@@ -1104,8 +1030,9 @@ static void pcmrec_thread(void)
break;
case SYS_USB_CONNECTED:
- if (!is_recording && !is_stopping)
+ if (!is_recording)
{
+ pcmrec_close();
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_wait_for_disconnect(&pcmrec_queue);
}
@@ -1148,3 +1075,102 @@ void pcm_rec_mux(int source)
/* iAudio x5 */
#endif
}
+
+
+/****************************************************************************/
+/* */
+/* following functions will be called by the encoder codec */
+/* */
+/****************************************************************************/
+
+/* pass the encoder buffer pointer/size, mono/stereo, quality to the encoder */
+void enc_get_inputs(int *buffer_size, int *channels, int *quality)
+{
+ *buffer_size = enc_buffer_size;
+ *channels = enc_channels;
+ *quality = enc_quality;
+}
+
+/* set the encoder dimensions (called by encoder codec at initialization) */
+void enc_set_parameters(int chunk_size, int num_chunks, int samp_per_chunk,
+ char *head_ptr, int head_size, int enc_id)
+{
+ /* set read_pos just in front of current write_pos */
+ read_pos = (write_pos - CHUNK_SIZE) & CHUNK_MASK;
+
+ enc_rd_index = 0; /* reset */
+ enc_wr_index = 0; /* reset */
+ enc_chunk_size = chunk_size; /* max chunk size */
+ enc_num_chunks = num_chunks; /* total number of chunks */
+ enc_samp_per_chunk = samp_per_chunk; /* pcm samples / encoderchunk */
+ enc_head_buffer = head_ptr; /* optional file header data (wav) */
+ enc_head_size = head_size; /* optional file header data (wav) */
+ audio_enc_id = enc_id; /* AFMT_* id */
+}
+
+/* allocate encoder chunk */
+unsigned int *enc_alloc_chunk(void)
+{
+ return (unsigned int*)(enc_buffer + enc_wr_index * enc_chunk_size);
+}
+
+/* free previously allocated encoder chunk */
+void enc_free_chunk(void)
+{
+ unsigned long *enc_chunk;
+
+ enc_chunk = GET_ENC_CHUNK(enc_wr_index);
+ curr_chunk_cnt++;
+/* curr_bit_rate += *enc_chunk * 44100 * 8 / (enc_samp_per_chunk * 1000); */
+ curr_bit_rate += *enc_chunk * 441 * 8 / (enc_samp_per_chunk * 10 );
+ avrg_bit_rate = (curr_bit_rate + curr_chunk_cnt / 2) / curr_chunk_cnt;
+
+ /* advance enc_wr_index to the next chunk */
+ enc_wr_index = (enc_wr_index + 1) % enc_num_chunks;
+
+ /* buffer full: advance enc_rd_index (for prerecording purpose) */
+ if (enc_rd_index == enc_wr_index)
+ {
+ enc_rd_index = (enc_rd_index + 1) % enc_num_chunks;
+ }
+}
+
+/* checks near empty state on wav input buffer */
+int enc_wavbuf_near_empty(void)
+{
+ /* less than 1sec raw data? => unboost encoder */
+ if (((write_pos - read_pos) & CHUNK_MASK) < 44100*4)
+ return 1;
+ else
+ return 0;
+}
+
+/* passes a pointer to next chunk of unprocessed wav data */
+char *enc_get_wav_data(int size)
+{
+ char *ptr;
+ int avail;
+
+ /* limit the requested pcm data size */
+ if(size > MAX_FEED_SIZE)
+ size = MAX_FEED_SIZE;
+
+ avail = (write_pos - read_pos) & CHUNK_MASK;
+
+ if (avail >= size)
+ {
+ ptr = rec_buffer + read_pos;
+ read_pos = (read_pos + size) & CHUNK_MASK;
+
+ /* ptr must point to continous data at wraparound position */
+ if (read_pos < size)
+ memcpy(rec_buffer + NUM_CHUNKS * CHUNK_SIZE,
+ rec_buffer, read_pos);
+
+ wav_queue_empty = false;
+ return ptr;
+ }
+
+ wav_queue_empty = true;
+ return NULL;
+}
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index 3dd4c60..377c34a 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -424,6 +424,7 @@ static void battery_status_update(void)
* 1) The USB is connected
* 2) The charger is connected
* 3) We are recording, or recording with pause
+ * 4) The radio is playing
*/
static void handle_auto_poweroff(void)
{
@@ -442,7 +443,7 @@ static void handle_auto_poweroff(void)
if(timeout &&
#ifdef CONFIG_TUNER
- (!radio_powered()) &&
+ (!(get_radio_status() & FMRADIO_PLAYING)) &&
#endif
!usb_inserted() &&
((audio_stat == 0) ||