summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES26
-rw-r--r--firmware/drivers/tlv320.c45
-rw-r--r--firmware/drivers/uda1380.c84
-rw-r--r--firmware/export/audio.h106
-rw-r--r--firmware/export/config-h100.h6
-rw-r--r--firmware/export/config-h120.h6
-rw-r--r--firmware/export/config-h300.h7
-rw-r--r--firmware/export/config-iaudiox5.h6
-rw-r--r--firmware/export/id3.h91
-rw-r--r--firmware/export/pcm_playback.h16
-rw-r--r--firmware/export/pcm_record.h46
-rw-r--r--firmware/export/system.h81
-rw-r--r--firmware/export/thread.h3
-rw-r--r--firmware/export/tlv320.h10
-rw-r--r--firmware/export/uda1380.h13
-rw-r--r--firmware/id3.c138
-rw-r--r--firmware/mpeg.c20
-rw-r--r--firmware/pcm_playback.c700
-rw-r--r--firmware/pcm_record.c2002
-rw-r--r--firmware/system.c3
-rw-r--r--firmware/target/coldfire/iaudio/x5/system-x5.c7
-rw-r--r--firmware/target/coldfire/iriver/system-iriver.c7
-rw-r--r--firmware/target/coldfire/system-coldfire.c7
-rw-r--r--firmware/target/coldfire/system-target.h29
-rw-r--r--firmware/thread.c8
25 files changed, 2053 insertions, 1414 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 1ec3c82..df38169 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -4,6 +4,7 @@ logf.c
#endif
backlight.c
buffer.c
+general.c
common/atoi.c
common/crc32.c
common/ctype.c
@@ -45,7 +46,12 @@ target/coldfire/memcpy-coldfire.S
target/coldfire/memmove-coldfire.S
target/coldfire/memset-coldfire.S
target/coldfire/memset16-coldfire.S
+#ifndef SIMULATOR
+#ifndef BOOTLOADER
+target/coldfire/pcm-coldfire.c
+#endif
target/coldfire/system-coldfire.c
+#endif
#elif (CONFIG_CPU == SH7034)
target/sh/memcpy-sh.S
target/sh/memmove-sh.S
@@ -207,15 +213,21 @@ drivers/wm8731l.c
#elif defined(HAVE_TLV320) && !defined(SIMULATOR)
drivers/tlv320.c
#endif
-#if (CONFIG_CODEC == SWCODEC) && !defined(SIMULATOR)
-pcm_playback.c
-#endif
-#if CONFIG_CODEC == SWCODEC
+#if (CONFIG_CODEC == SWCODEC) && !defined(BOOTLOADER)
+pcm_sampr.c
replaygain.c
-#endif
-#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
+#ifndef SIMULATOR
+pcm_playback.c
+#endif /* SIMULATOR */
+#ifdef HAVE_RECORDING
+enc_base.c
+#if defined(CPU_COLDFIRE)
+#ifndef SIMULATOR
pcm_record.c
-#endif
+#endif /* SIMULATOR */
+#endif /* CPU_COLDFIRE */
+#endif /* HAVE_RECORDING */
+#endif /* SWCODEC && !BOOTLOADER */
sound.c
#if defined(IRIVER_IFP7XX_SERIES) && defined(STUB)
common/sscanf.c
diff --git a/firmware/drivers/tlv320.c b/firmware/drivers/tlv320.c
index abce31e..7c4bbbd 100644
--- a/firmware/drivers/tlv320.c
+++ b/firmware/drivers/tlv320.c
@@ -82,7 +82,7 @@ void tlv320_init(void)
tlv320_write_reg(REG_DAP, 0x00); /* No deemphasis */
tlv320_write_reg(REG_DAIF, DAIF_IWL_16 | DAIF_FOR_I2S);
tlv320_write_reg(REG_DIA, DIA_ACT);
- tlv320_write_reg(REG_SRC, (1 << 5)); /* 44.1kHz */
+ tlv320_set_frequency(-1); /* default */
/* All ON except ADC, MIC and LINE */
tlv320_write_reg(REG_PC, PC_ADC | PC_MIC | PC_LINE);
}
@@ -96,6 +96,32 @@ void tlv320_reset(void)
}
/**
+ * Sets internal sample rate for DAC and ADC relative to MCLK
+ * Selection for frequency:
+ * Fs: tlv: with:
+ * 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16
+ * 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8
+ * 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default)
+ * 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2
+ */
+void tlv320_set_frequency(unsigned fsel)
+{
+ /* All rates available for 11.2896MHz besides 8.021 */
+ unsigned char values_src[3] =
+ {
+ /* Fs: */
+ (0x8 << 2) | SRC_CLKIN, /* 11025, 22050 */
+ (0x8 << 2), /* 44100 */
+ (0xf << 2), /* 88200 */
+ };
+
+ if (fsel >= ARRAYLEN(values_src))
+ fsel = 1;
+
+ tlv320_write_reg(REG_SRC, values_src[fsel]);
+}
+
+/**
* Sets left and right headphone volume
*
* Left & Right: 48 .. 121 .. 127 => Volume -73dB (mute) .. +0 dB .. +6 dB
@@ -142,7 +168,6 @@ void tlv320_set_recvol(int left, int right, int type)
value_aap &= ~AAP_MICB;
tlv320_write_reg(REG_AAP, value_aap);
-
}
else if (type == AUDIO_GAIN_LINEIN)
{
@@ -180,15 +205,17 @@ void tlv320_mute(bool mute)
}
/* Nice shutdown of TLV320 codec */
-void tlv320_close()
+void tlv320_close(void)
{
+ tlv320_mute(true);
+ sleep(HZ/8);
+
tlv320_write_reg(REG_PC, PC_OFF | PC_CLK | PC_OSC | PC_OUT |
PC_DAC | PC_ADC | PC_MIC | PC_LINE); /* All OFF */
}
void tlv320_enable_recording(bool source_mic)
{
- unsigned value_daif = tlv320_regs[REG_DAIF];
unsigned value_aap, value_pc;
if (source_mic)
@@ -205,20 +232,12 @@ void tlv320_enable_recording(bool source_mic)
tlv320_write_reg(REG_PC, value_pc);
tlv320_write_reg(REG_AAP, value_aap);
-
- /* Enable MASTER mode (start sending I2S data to the CPU) */
- value_daif |= DAIF_MS;
- tlv320_write_reg(REG_DAIF, value_daif);
}
-void tlv320_disable_recording()
+void tlv320_disable_recording(void)
{
unsigned value_pc = tlv320_regs[REG_PC];
unsigned value_aap = tlv320_regs[REG_AAP];
- unsigned value_daif = tlv320_regs[REG_DAIF];
-
- value_daif &= ~DAIF_MS; /* disable MASTER mode */
- tlv320_write_reg(REG_DAIF, value_daif);
value_aap |= AAP_MICM; /* mute MIC */
tlv320_write_reg(REG_PC, value_aap);
diff --git a/firmware/drivers/uda1380.c b/firmware/drivers/uda1380.c
index 241a117..d6dfe66 100644
--- a/firmware/drivers/uda1380.c
+++ b/firmware/drivers/uda1380.c
@@ -49,9 +49,10 @@ short recgain_line;
#define NUM_DEFAULT_REGS 13
unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] =
{
- REG_0, EN_DAC | EN_INT | EN_DEC | SYSCLK_256FS | WSPLL_25_50,
+ REG_0, EN_DAC | EN_INT | EN_DEC | ADC_CLK | DAC_CLK |
+ SYSCLK_256FS | WSPLL_25_50,
REG_I2S, I2S_IFMT_IIS,
- REG_PWR, PON_BIAS,
+ REG_PWR, PON_PLL | PON_BIAS,
/* PON_HP & PON_DAC is enabled later */
REG_AMIX, AMIX_RIGHT(0x3f) | AMIX_LEFT(0x3f),
/* 00=max, 3f=mute */
@@ -60,7 +61,7 @@ unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] =
REG_MIX_VOL, MIX_VOL_CH_1(0) | MIX_VOL_CH_2(0xff),
/* 00=max, ff=mute */
REG_EQ, EQ_MODE_MAX,
- /* Bass and tremble = 0 dB */
+ /* Bass and treble = 0 dB */
REG_MUTE, MUTE_MASTER | MUTE_CH2,
/* Mute everything to start with */
REG_MIX_CTL, MIX_CTL_MIX,
@@ -192,6 +193,43 @@ void uda1380_reset(void)
#endif
}
+/**
+ * Sets frequency settings for DAC and ADC relative to MCLK
+ *
+ * Selection for frequency ranges:
+ * Fs: range: with:
+ * 11025: 0 = 6.25 to 12.5 MCLK/2 SCLK, LRCK: Audio Clk / 16
+ * 22050: 1 = 12.5 to 25 MCLK/2 SCLK, LRCK: Audio Clk / 8
+ * 44100: 2 = 25 to 50 MCLK SCLK, LRCK: Audio Clk / 4 (default)
+ * 88200: 3 = 50 to 100 MCLK SCLK, LRCK: Audio Clk / 2 <= TODO: Needs WSPLL
+ */
+void uda1380_set_frequency(unsigned fsel)
+{
+ static const unsigned short values_reg[4][2] =
+ {
+ /* Fs: */
+ { 0, WSPLL_625_125 | SYSCLK_512FS }, /* 11025 */
+ { 0, WSPLL_125_25 | SYSCLK_256FS }, /* 22050 */
+ { MIX_CTL_SEL_NS, WSPLL_25_50 | SYSCLK_256FS }, /* 44100 */
+ { MIX_CTL_SEL_NS, WSPLL_50_100 | SYSCLK_256FS }, /* 88200 */
+ };
+
+ const unsigned short *ent;
+
+ if (fsel >= ARRAYLEN(values_reg))
+ fsel = 2;
+
+ ent = values_reg[fsel];
+
+ /* Set WSPLL input frequency range or SYSCLK divider */
+ uda1380_regs[REG_0] &= ~0xf;
+ uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ent[1]);
+
+ /* Choose 3rd order or 5th order noise shaper */
+ uda1380_regs[REG_MIX_CTL] &= ~MIX_CTL_SEL_NS;
+ uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] | ent[0]);
+}
+
/* Initialize UDA1380 codec with default register values (uda1380_defaults) */
int uda1380_init(void)
{
@@ -227,30 +265,34 @@ void uda1380_close(void)
*/
void uda1380_enable_recording(bool source_mic)
{
+ uda1380_regs[REG_0] &= ~(ADC_CLK | DAC_CLK);
uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC);
if (source_mic)
{
/* VGA_GAIN: 0=0 dB, F=30dB */
+ /* Output of left ADC is fed into right bitstream */
+ uda1380_regs[REG_PWR] &= ~(PON_PLL | PON_PGAR | PON_ADCR);
uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_LNA | PON_ADCL);
+ uda1380_regs[REG_ADC] &= ~SKIP_DCFIL;
uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & VGA_GAIN_MASK)
| SEL_LNA | SEL_MIC | EN_DCFIL);
uda1380_write_reg(REG_PGA, 0);
- } else
+ }
+ else
{
/* PGA_GAIN: 0=0 dB, F=24dB */
+ uda1380_regs[REG_PWR] &= ~(PON_PLL | PON_LNA);
uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PGAL | PON_ADCL
| PON_PGAR | PON_ADCR);
uda1380_write_reg(REG_ADC, EN_DCFIL);
- uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & PGA_GAIN_MASK)
- | PGA_GAINL(0) | PGA_GAINR(0));
+ uda1380_write_reg(REG_PGA, uda1380_regs[REG_PGA] & PGA_GAIN_MASK);
}
sleep(HZ/8);
uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER);
uda1380_write_reg(REG_MIX_CTL, MIX_MODE(1));
-
}
/**
@@ -262,10 +304,13 @@ void uda1380_disable_recording(void)
sleep(HZ/8);
uda1380_write_reg(REG_I2S, I2S_IFMT_IIS);
- uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] & ~(PON_LNA | PON_ADCL
- | PON_ADCR | PON_PGAL
- | PON_PGAR));
- uda1380_write_reg(REG_0, uda1380_regs[REG_0] & ~EN_ADC);
+
+ uda1380_regs[REG_PWR] &= ~(PON_LNA | PON_ADCL | PON_ADCR | PON_PGAL | PON_PGAR);
+ uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PLL);
+
+ uda1380_regs[REG_0] &= ~EN_ADC;
+ uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ADC_CLK | DAC_CLK);
+
uda1380_write_reg(REG_ADC, SKIP_DCFIL);
}
@@ -373,20 +418,3 @@ void uda1380_set_monitor(int enable)
else /* mute channel 2 */
uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] | MUTE_CH2);
}
-
-/* Change the order of the noise chaper,
- 5th order is recommended above 32kHz */
-void uda1380_set_nsorder(int order)
-{
- switch(order)
- {
- case 5:
- uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL]
- | MIX_CTL_SEL_NS);
- break;
- case 3:
- default:
- uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL]
- & ~MIX_CTL_SEL_NS);
- }
-}
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 9099cb3..d3f544d 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -20,6 +20,20 @@
#define AUDIO_H
#include <stdbool.h>
+#include <sys/types.h>
+/* These must always be included with audio.h for this to compile under
+ cetain conditions. Do it here or else spread the complication around to
+ many files. */
+#if CONFIG_CODEC == SWCODEC
+#include "pcm_sampr.h"
+#include "pcm_playback.h"
+#ifdef HAVE_RECORDING
+#include "pcm_record.h"
+#include "id3.h"
+#include "enc_base.h"
+#endif /* HAVE_RECORDING */
+#endif /* CONFIG_CODEC == SWCODEC */
+
#ifdef SIMULATOR
#define audio_play(x) sim_audio_play(x)
@@ -31,9 +45,6 @@
#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
@@ -72,10 +83,11 @@ void audio_resume(void);
void audio_next(void);
void audio_prev(void);
int audio_status(void);
-bool audio_query_poweroff(void);
+#if CONFIG_CODEC == SWCODEC
int audio_track_count(void); /* SWCODEC only */
long audio_filebufused(void); /* SWCODEC only */
void audio_pre_ff_rewind(void); /* SWCODEC only */
+#endif /* CONFIG_CODEC == SWCODEC */
void audio_ff_rewind(long newtime);
void audio_flush_and_reload_tracks(void);
struct mp3entry* audio_current_track(void);
@@ -89,18 +101,28 @@ void audio_error_clear(void);
int audio_get_file_pos(void);
void audio_beep(int duration);
void audio_init_playback(void);
+unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
-/* audio recording functions */
-void audio_init_recording(unsigned int buffer_offset);
-void audio_close_recording(void);
-void audio_record(const char *filename);
-void audio_stop_recording(void);
-void audio_pause_recording(void);
-void audio_resume_recording(void);
-void audio_new_file(const char *filename);
+/* channel modes */
+enum rec_channel_modes
+{
+ __CHN_MODE_START_INDEX = -1,
+
+ CHN_MODE_STEREO,
+ CHN_MODE_MONO,
+
+ CHN_NUM_MODES
+};
+
+#if CONFIG_CODEC == SWCODEC
+/* channel mode capability bits */
+#define CHN_CAP_STEREO (1 << CHN_MODE_STEREO)
+#define CHN_CAP_MONO (1 << CHN_MODE_MONO)
+#define CHN_CAP_ALL (CHN_CAP_STEREO | CHN_CAP_MONO)
+#endif /* CONFIG_CODEC == SWCODEC */
/* audio sources */
-enum
+enum audio_sources
{
AUDIO_SRC_PLAYBACK = -1, /* for audio playback (default) */
AUDIO_SRC_MIC, /* monitor mic */
@@ -123,33 +145,57 @@ enum
AUDIO_SRC_MAX = AUDIO_NUM_SOURCES-1
};
-/* channel modes */
-enum
+#ifdef HAVE_RECORDING
+/* parameters for audio_set_recording_options */
+struct audio_recording_options
{
- CHN_MODE_MONO = 1,
- CHN_MODE_STEREO,
+ int rec_source;
+ int rec_frequency;
+ int rec_channels;
+ int rec_prerecord_time;
+#if CONFIG_CODEC == SWCODEC
+ int rec_source_flags; /* for rec_set_source */
+ struct encoder_config enc_config;
+#else
+ int rec_quality;
+ bool rec_editable;
+#endif
};
-void audio_set_recording_options(int frequency, int quality,
- int source, int channel_mode,
- bool editable, int prerecord_time);
+
+/* audio recording functions */
+void audio_init_recording(unsigned int buffer_offset);
+void audio_close_recording(void);
+void audio_record(const char *filename);
+void audio_stop_recording(void);
+void audio_pause_recording(void);
+void audio_resume_recording(void);
+void audio_new_file(const char *filename);
+void audio_set_recording_options(struct audio_recording_options *options);
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);
-unsigned long audio_prev_elapsed(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);
+/* SWCODEC recoring functions */
+/* playback.c */
+bool audio_load_encoder(int afmt);
void audio_remove_encoder(void);
+unsigned char *audio_get_recording_buffer(size_t *buffer_size);
#endif /* CONFIG_CODEC == SWCODEC */
+#endif /* HAVE_RECORDING */
+#ifdef HAVE_SPDIF_IN
+#ifdef HAVE_SPDIF_POWER
+void audio_set_spdif_power_setting(bool on);
+bool audio_get_spdif_power_setting(void);
+#endif
+/* returns index into rec_master_sampr_list */
+int audio_get_spdif_sample_rate(void);
+/* > 0: monitor EBUin, 0: Monitor IISrecv, <0: reset only */
+void audio_spdif_set_monitor(int monitor_spdif);
+#endif /* HAVE_SPDIF_IN */
+unsigned long audio_prev_elapsed(void);
/***********************************************************************/
diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h
index 6f74078..285ab88 100644
--- a/firmware/export/config-h100.h
+++ b/firmware/export/config-h100.h
@@ -84,6 +84,12 @@
/* define this if you have recording possibility */
#define HAVE_RECORDING 1
+/* define hardware samples rate caps mask */
+#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
#define HAVE_AGC
#ifndef SIMULATOR
diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h
index 1476102..b22ff0e 100644
--- a/firmware/export/config-h120.h
+++ b/firmware/export/config-h120.h
@@ -77,6 +77,12 @@
/* define this if you have recording possibility */
#define HAVE_RECORDING 1
+/* define hardware samples rate caps mask */
+#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
#define HAVE_AGC
#define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */
diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h
index 31f0f67..748635d 100644
--- a/firmware/export/config-h300.h
+++ b/firmware/export/config-h300.h
@@ -72,6 +72,12 @@
/* define this if you have recording possibility */
#define HAVE_RECORDING 1
+/* define hardware samples rate caps mask */
+#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
#define HAVE_AGC
#define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */
@@ -157,4 +163,3 @@
/* Define this for FM radio input available */
#define HAVE_FMRADIO_IN
-
diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h
index 80b010a..d4c904e 100644
--- a/firmware/export/config-iaudiox5.h
+++ b/firmware/export/config-iaudiox5.h
@@ -9,6 +9,12 @@
/* define this if you have recording possibility */
#define HAVE_RECORDING 1
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11)
+
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index 1d07aff..dd099e0 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -24,13 +24,19 @@
#include "file.h"
/* Audio file types. */
-enum {
+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 */
+ AFMT_AIFF, /* Audio Interchange File Format */
+
+#if CONFIG_CODEC == SWCODEC
AFMT_PCM_WAV, /* Uncompressed PCM in a WAV file */
AFMT_OGG_VORBIS, /* Ogg Vorbis */
AFMT_FLAC, /* FLAC */
@@ -40,54 +46,91 @@ enum {
AFMT_ALAC, /* Apple Lossless Audio Codec */
AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */
AFMT_SHN, /* Shorten */
- AFMT_AIFF, /* Audio Interchange File Format */
AFMT_SID, /* SID File Format */
- AFMT_ADX, /* ADX */
+ AFMT_ADX, /* ADX File Format */
+#endif
- /* New formats must be added to the end of this list */
+ /* 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
+#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
+ CODEC_TYPE_ENCODER = (1 << 12),
+#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */
};
#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
+#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_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,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
{
-#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];
+#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];
-#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_playback.h b/firmware/export/pcm_playback.h
index a4cd939..9c3e96b 100644
--- a/firmware/export/pcm_playback.h
+++ b/firmware/export/pcm_playback.h
@@ -19,11 +19,23 @@
#ifndef PCM_PLAYBACK_H
#define PCM_PLAYBACK_H
+#include <sys/types.h>
+
+/* Typedef for registered callback (play and record) */
+typedef void (*pcm_more_callback_type)(unsigned char **start,
+ size_t *size);
+
void pcm_init(void);
+
+/* set the pcm frequency - use values in hw_sampr_list
+ * use -1 for the default frequency
+ */
void pcm_set_frequency(unsigned int frequency);
+/* apply settings to hardware immediately */
+void pcm_apply_settings(bool reset);
/* This is for playing "raw" PCM data */
-void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
+void pcm_play_data(pcm_more_callback_type get_more,
unsigned char* start, size_t size);
void pcm_calculate_peaks(int *left, int *right);
@@ -35,4 +47,4 @@ void pcm_play_pause(bool play);
bool pcm_is_paused(void);
bool pcm_is_playing(void);
-#endif
+#endif /* PCM_PLAYBACK_H */
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
index b217335..c1187a4 100644
--- a/firmware/export/pcm_record.h
+++ b/firmware/export/pcm_record.h
@@ -20,24 +20,44 @@
#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);
+#define DMA_REC_ERROR_DMA ((size_t)-1)
+#ifdef HAVE_SPDIF_IN
+#define DMA_REC_ERROR_SPDIF ((size_t)-2)
+#endif
+/* Use AUDIO_SRC_* enumeration values */
+void pcm_set_monitor(int monitor);
+void pcm_set_rec_source(int source);
+
+/**
+ * RAW pcm data recording
+ * These calls are nescessary only when using the raw pcm apis directly.
+ */
+
+/* Initialize pcm recording interface */
+void pcm_init_recording(void);
+/* Uninitialze pcm recording interface */
+void pcm_close_recording(void);
+
+/* Start recording "raw" PCM data */
+void pcm_record_data(pcm_more_callback_type more_ready,
+ unsigned char *start, size_t size);
+/* Stop tranferring data into supplied buffer */
+void pcm_stop_recording(void);
+
+void pcm_calculate_rec_peaks(int *left, int *right);
+
+/** General functions for high level codec recording **/
+void pcm_rec_error_clear(void);
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_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);
-void pcm_rec_get_peaks(int *left, int *right);
-/* audio.h contains audio recording functions */
+/* audio.h contains audio_* recording functions */
-#endif
+#endif /* PCM_RECORD_H */
diff --git a/firmware/export/system.h b/firmware/export/system.h
index 4a33d80..9b90a6e 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -21,7 +21,6 @@
#define __SYSTEM_H__
#include "cpu.h"
-#include "config.h"
#include "stdbool.h"
extern void system_reboot (void);
@@ -111,6 +110,23 @@ const char *get_cpu_boost_tracker(void);
#define MAX(a, b) (((a)>(b))?(a):(b))
#endif
+/* return number of elements in array a */
+#define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0]))
+
+/* return p incremented by specified number of bytes */
+#define SKIPBYTES(p, count) ((typeof (p))((char *)(p) + (count)))
+
+#define P2_M1(p2) ((1 << (p2))-1)
+
+/* align up or down to nearest 2^p2 */
+#define ALIGN_DOWN_P2(n, p2) ((n) & ~P2_M1(p2))
+#define ALIGN_UP_P2(n, p2) ALIGN_DOWN_P2((n) + P2_M1(p2),p2)
+
+/* align up or down to nearest integer multiple of a */
+#define ALIGN_DOWN(n, a) ((n)/(a)*(a))
+#define ALIGN_UP(n, a) ALIGN_DOWN((n)+((a)-1),a)
+
+/* live endianness conversion */
#ifdef ROCKBOX_LITTLE_ENDIAN
#define letoh16(x) (x)
#define letoh32(x) (x)
@@ -120,6 +136,8 @@ const char *get_cpu_boost_tracker(void);
#define betoh32(x) swap32(x)
#define htobe16(x) swap16(x)
#define htobe32(x) swap32(x)
+#define swap_odd_even_be32(x) (x)
+#define swap_odd_even_le32(x) swap_odd_even32(x)
#else
#define letoh16(x) swap16(x)
#define letoh32(x) swap32(x)
@@ -129,6 +147,37 @@ const char *get_cpu_boost_tracker(void);
#define betoh32(x) (x)
#define htobe16(x) (x)
#define htobe32(x) (x)
+#define swap_odd_even_be32(x) swap_odd_even32(x)
+#define swap_odd_even_le32(x) (x)
+#endif
+
+/* static endianness conversion */
+#define SWAP_16(x) ((typeof(x))(unsigned short)(((unsigned short)(x) >> 8) | \
+ ((unsigned short)(x) << 8)))
+
+#define SWAP_32(x) ((typeof(x))(unsigned long)( ((unsigned long)(x) >> 24) | \
+ (((unsigned long)(x) & 0xff0000ul) >> 8) | \
+ (((unsigned long)(x) & 0xff00ul) << 8) | \
+ ((unsigned long)(x) << 24)))
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+#define LE_TO_H16(x) (x)
+#define LE_TO_H32(x) (x)
+#define H_TO_LE16(x) (x)
+#define H_TO_LE32(x) (x)
+#define BE_TO_H16(x) SWAP_16(x)
+#define BE_TO_H32(x) SWAP_32(x)
+#define H_TO_BE16(x) SWAP_16(x)
+#define H_TO_BE32(x) SWAP_32(x)
+#else
+#define LE_TO_H16(x) SWAP_16(x)
+#define LE_TO_H32(x) SWAP_32(x)
+#define H_TO_LE16(x) SWAP_16(x)
+#define H_TO_LE32(x) SWAP_32(x)
+#define BE_TO_H16(x) (x)
+#define BE_TO_H32(x) (x)
+#define H_TO_BE16(x) (x)
+#define H_TO_BE32(x) (x)
#endif
@@ -181,6 +230,7 @@ enum {
: /* %0 */ I_CONSTRAINT((char)(mask)), \
/* %1 */ "z"(address-GBR))
+
#endif /* CONFIG_CPU == SH7034 */
#ifndef SIMULATOR
@@ -388,7 +438,20 @@ static inline unsigned long swap32(unsigned long value)
#define invalidate_icache()
#endif
-#else
+
+#ifndef CPU_COLDFIRE
+static inline unsigned long swap_odd_even32(unsigned long value)
+{
+ /*
+ result[31..24],[15.. 8] = value[23..16],[ 7.. 0]
+ result[23..16],[ 7.. 0] = value[31..24],[15.. 8]
+ */
+ unsigned long t = value & 0xff00ff00;
+ return (t >> 8) | ((t ^ value) << 8);
+}
+#endif
+
+#else /* SIMULATOR */
static inline unsigned short swap16(unsigned short value)
/*
@@ -412,8 +475,18 @@ static inline unsigned long swap32(unsigned long value)
return (lo << 16) | hi;
}
+static inline unsigned long swap_odd_even32(unsigned long value)
+{
+ /*
+ result[31..24],[15.. 8] = value[23..16],[ 7.. 0]
+ result[23..16],[ 7.. 0] = value[31..24],[15.. 8]
+ */
+ unsigned long t = value & 0xff00ff00;
+ return (t >> 8) | ((t ^ value) << 8);
+}
+
#define invalidate_icache()
-#endif
+#endif /* !SIMULATOR */
-#endif
+#endif /* __SYSTEM_H__ */
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index 17e6e3a..72c692e 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -142,7 +142,10 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list);
void sleep_thread(int ticks);
void block_thread(struct thread_entry **thread, int timeout);
void wakeup_thread(struct thread_entry **thread);
+#ifdef HAVE_PRIORITY_SCHEDULING
int thread_set_priority(struct thread_entry *thread, int priority);
+int thread_get_priority(struct thread_entry *thread);
+#endif
void init_threads(void);
int thread_stack_usage(const struct thread_entry *thread);
int thread_get_status(const struct thread_entry *thread);
diff --git a/firmware/export/tlv320.h b/firmware/export/tlv320.h
index dfcbec4..023ec9d 100644
--- a/firmware/export/tlv320.h
+++ b/firmware/export/tlv320.h
@@ -24,6 +24,16 @@
extern void tlv320_init(void);
extern void tlv320_reset(void);
+/**
+ * Sets internal sample rate for DAC and ADC relative to MCLK
+ * Selection for frequency:
+ * Fs: tlv: with:
+ * 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16
+ * 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8
+ * 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default)
+ * 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2
+ */
+extern void tlv320_set_frequency(unsigned fsel);
extern void tlv320_enable_output(bool enable);
extern void tlv320_set_headphone_vol(int vol_l, int vol_r);
extern void tlv320_set_recvol(int left, int right, int type);
diff --git a/firmware/export/uda1380.h b/firmware/export/uda1380.h
index 9c761c6..639ca8a 100644
--- a/firmware/export/uda1380.h
+++ b/firmware/export/uda1380.h
@@ -28,8 +28,17 @@ extern void uda1380_set_bass(int value);
extern void uda1380_set_treble(int value);
extern int uda1380_mute(int mute);
extern void uda1380_close(void);
-extern void uda1380_set_nsorder(int order);
-
+/**
+ * Sets frequency settings for DAC and ADC relative to MCLK
+ *
+ * Selection for frequency ranges:
+ * Fs: range: with:
+ * 11025: 0 = 6.25 to 12.5 SCLK, LRCK: Audio Clk / 16
+ * 22050: 1 = 12.5 to 25 SCLK, LRCK: Audio Clk / 8
+ * 44100: 2 = 25 to 50 SCLK, LRCK: Audio Clk / 4 (default)
+ * 88200: 3 = 50 to 100 SCLK, LRCK: Audio Clk / 2
+ */
+extern void uda1380_set_frequency(unsigned fsel);
extern void uda1380_enable_recording(bool source_mic);
extern void uda1380_disable_recording(void);
extern void uda1380_set_recvol(int left, int right, int type);
diff --git a/firmware/id3.c b/firmware/id3.c
index 92f60a2..7d03c75 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -44,6 +44,89 @@
#include "replaygain.h"
#include "rbunicode.h"
+/** Database of audio formats **/
+const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
+{
+ /* Unknown file format */
+ [AFMT_UNKNOWN] =
+ AFMT_ENTRY("???", NULL, NULL, NULL ),
+
+ /* MPEG Audio layer 1 */
+ [AFMT_MPA_L1] =
+ AFMT_ENTRY("MP1", "mpa", NULL, "mp1\0" ),
+ /* MPEG Audio layer 2 */
+ [AFMT_MPA_L2] =
+ AFMT_ENTRY("MP2", "mpa", NULL, "mpa\0mp2\0" ),
+ /* MPEG Audio layer 3 */
+ [AFMT_MPA_L3] =
+ AFMT_ENTRY("MP3", "mpa", "mp3_enc", "mp3\0" ),
+
+ /* Audio Interchange File Format */
+ [AFMT_AIFF] =
+ AFMT_ENTRY("AIFF", "aiff", NULL, "aiff\0aif\0"),
+
+#if CONFIG_CODEC == SWCODEC
+ /* Uncompressed PCM in a WAV file */
+ [AFMT_PCM_WAV] =
+ AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ),
+ /* Ogg Vorbis */
+ [AFMT_OGG_VORBIS] =
+ AFMT_ENTRY("Ogg", "vorbis", NULL, "ogg\0" ),
+ /* FLAC */
+ [AFMT_FLAC] =
+ AFMT_ENTRY("FLAC", "flac", NULL, "flac\0" ),
+ /* Musepack */
+ [AFMT_MPC] =
+ AFMT_ENTRY("MPC", "mpc", NULL, "mpc\0" ),
+ /* A/52 (aka AC3) audio */
+ [AFMT_A52] =
+ AFMT_ENTRY("AC3", "a52", NULL, "a52\0ac3\0" ),
+ /* WavPack */
+ [AFMT_WAVPACK] =
+ AFMT_ENTRY("WV", "wavpack", "wavpack_enc", "wv\0" ),
+ /* Apple Lossless Audio Codec */
+ [AFMT_ALAC] =
+ AFMT_ENTRY("ALAC", "alac", NULL, "m4a\0" ),
+ /* Advanced Audio Coding in M4A container */
+ [AFMT_AAC] =
+ AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ),
+ /* Shorten */
+ [AFMT_SHN] =
+ AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ),
+ /* SID File Format */
+ [AFMT_SID] =
+ AFMT_ENTRY("SID", "sid", NULL, "sid\0" ),
+ /* ADX File Format */
+ [AFMT_ADX] =
+ AFMT_ENTRY("ADX", "adx", NULL, "adx\0" ),
+#endif
+};
+
+#if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING)
+/* get REC_FORMAT_* corresponding AFMT_* */
+const int rec_format_afmt[REC_NUM_FORMATS] =
+{
+ /* give AFMT_UNKNOWN by default */
+ [0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN,
+ /* add new entries below this line */
+ [REC_FORMAT_MPA_L3] = AFMT_MPA_L3,
+ [REC_FORMAT_WAVPACK] = AFMT_WAVPACK,
+ [REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV,
+};
+
+/* get AFMT_* corresponding REC_FORMAT_* */
+const int afmt_rec_format[AFMT_NUM_CODECS] =
+{
+ /* give -1 by default */
+ [0 ... AFMT_NUM_CODECS-1] = -1,
+ /* add new entries below this line */
+ [AFMT_MPA_L3] = REC_FORMAT_MPA_L3,
+ [AFMT_WAVPACK] = REC_FORMAT_WAVPACK,
+ [AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV,
+};
+#endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */
+/****/
+
#define UNSYNC(b0,b1,b2,b3) (((long)(b0 & 0x7F) << (3*7)) | \
((long)(b1 & 0x7F) << (2*7)) | \
((long)(b2 & 0x7F) << (1*7)) | \
@@ -85,61 +168,6 @@ static const char* const genres[] = {
"Synthpop"
};
-/* 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
- /* 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 ),
- /* ADX File Format */
- AFMT_ENTRY("ADX", "adx.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 )
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index ce1d995..bb438a3 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -2453,34 +2453,32 @@ static void stop_recording(void)
resume_recording();
}
-void audio_set_recording_options(int frequency, int quality,
- int source, int channel_mode,
- bool editable, int prerecord_time)
+void audio_set_recording_options(struct audio_recording_options *options)
{
bool is_mpeg1;
- is_mpeg1 = (frequency < 3)?true:false;
+ is_mpeg1 = (options->rec_frequency < 3)?true:false;
rec_version_index = is_mpeg1?3:2;
- rec_frequency_index = frequency % 3;
+ rec_frequency_index = options->rec_frequency % 3;
- shadow_encoder_control = (quality << 17) |
+ shadow_encoder_control = (options->rec_quality << 17) |
(rec_frequency_index << 10) |
((is_mpeg1?1:0) << 9) |
- (((channel_mode * 2 + 1) & 3) << 6) |
+ (((options->rec_channels * 2 + 1) & 3) << 6) |
(1 << 5) /* MS-stereo */ |
(1 << 2) /* Is an original */;
mas_writemem(MAS_BANK_D0, MAS_D0_ENCODER_CONTROL, &shadow_encoder_control,1);
DEBUGF("mas_writemem(MAS_BANK_D0, ENCODER_CONTROL, %x)\n", shadow_encoder_control);
- shadow_soft_mute = editable?4:0;
+ shadow_soft_mute = options->rec_editable?4:0;
mas_writemem(MAS_BANK_D0, MAS_D0_SOFT_MUTE, &shadow_soft_mute,1);
DEBUGF("mas_writemem(MAS_BANK_D0, SOFT_MUTE, %x)\n", shadow_soft_mute);
shadow_io_control_main = ((1 << 10) | /* Monitoring ON */
- ((source < 2)?1:2) << 8) | /* Input select */
+ ((options->rec_source < 2)?1:2) << 8) | /* Input select */
(1 << 5) | /* SDO strobe invert */
((is_mpeg1?0:1) << 3) |
(1 << 2) | /* Inverted SIBC clock signal */
@@ -2489,7 +2487,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 == AUDIO_SRC_MIC)
+ if(options->rec_source == AUDIO_SRC_MIC)
{
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
@@ -2500,7 +2498,7 @@ void audio_set_recording_options(int frequency, int quality,
mas_codec_writereg(8, 0);
}
- prerecording_max_seconds = prerecord_time;
+ prerecording_max_seconds = options->rec_prerecord_time;
if(prerecording_max_seconds)
{
prerecording = true;
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index cd14f12..b7ea96f 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -16,259 +16,86 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include <stdbool.h>
-#include "config.h"
-#include "debug.h"
-#include "panic.h"
-#include <kernel.h>
-#include "cpu.h"
-#include "i2c.h"
-#if defined(HAVE_UDA1380)
-#include "uda1380.h"
-#elif defined(HAVE_WM8975)
+#include "system.h"
+#include "kernel.h"
+#include "logf.h"
+#include "audio.h"
+#if defined(HAVE_WM8975)
#include "wm8975.h"
#elif defined(HAVE_WM8758)
#include "wm8758.h"
-#elif defined(HAVE_TLV320)
-#include "tlv320.h"
#elif defined(HAVE_WM8731) || defined(HAVE_WM8721)
#include "wm8731l.h"
#elif CONFIG_CPU == PNX0101
+#include "string.h"
#include "pnx0101.h"
#endif
-#include "system.h"
-#include "logf.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include "pcm_playback.h"
-#include "lcd.h"
-#include "button.h"
-#include "file.h"
-#include "buffer.h"
-#include "sprintf.h"
-#include "button.h"
-#include <string.h>
-
-static bool pcm_playing;
-static bool pcm_paused;
+/**
+ * APIs implemented in the target-specific portion:
+ * Public -
+ * pcm_init
+ * pcm_get_bytes_waiting
+ * pcm_calculate_peaks
+ * Semi-private -
+ * pcm_play_dma_start
+ * pcm_play_dma_stop
+ * pcm_play_pause_pause
+ * pcm_play_pause_unpause
+ */
+
+/** These items may be implemented target specifically or need to
+ be shared semi-privately **/
/* the registered callback function to ask for more mp3 data */
-static void (*callback_for_more)(unsigned char**, size_t*) IDATA_ATTR = NULL;
+pcm_more_callback_type pcm_callback_for_more = NULL;
+bool pcm_playing = false;
+bool pcm_paused = false;
+
+void pcm_play_dma_start(const void *addr, size_t size);
+void pcm_play_dma_stop(void);
+void pcm_play_pause_pause(void);
+void pcm_play_pause_unpause(void);
+
+/** Functions that require targeted implementation **/
+
+#ifndef CPU_COLDFIRE
#if (CONFIG_CPU == S3C2440)
/* TODO: Implement for Gigabeat
For now, just implement some dummy functions.
*/
-
void pcm_init(void)
{
-
}
-static void dma_start(const void *addr, size_t size)
+void pcm_play_dma_start(const void *addr, size_t size)
{
(void)addr;
(void)size;
}
-void pcm_set_frequency(unsigned int frequency)
-{
- (void)frequency;
-}
-
-void pcm_play_stop(void)
+void pcm_play_dma_stop(void)
{
}
-size_t pcm_get_bytes_waiting(void)
+void pcm_play_pause_pause(void)
{
- return 0;
}
-#else
-#ifdef CPU_COLDFIRE
-#ifdef HAVE_SPDIF_OUT
-#define EBU_DEFPARM ((7 << 12) | (3 << 8) | (1 << 5) | (5 << 2))
-#endif
-#define IIS_DEFPARM(freq) ((freq << 12) | 0x300 | 4 << 2)
-#define IIS_RESET 0x800
-
-#ifdef IAUDIO_X5
-#define SET_IIS_CONFIG(x) IIS1CONFIG = (x);
-#else
-#define SET_IIS_CONFIG(x) IIS2CONFIG = (x);
-#endif
-
-static int pcm_freq = 0x6; /* 44.1 is default */
-
-int peak_left = 0, peak_right = 0;
-
-/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
-static void dma_start(const void *addr, size_t size)
+void pcm_play_pause_unpause(void)
{
- pcm_playing = true;
-
- addr = (void *)((unsigned long)addr & ~3); /* Align data */
- size &= ~3; /* Size must be multiple of 4 */
-
- /* Reset the audio FIFO */
-#ifdef HAVE_SPDIF_OUT
- EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
-#endif
-
- /* Set up DMA transfer */
- SAR0 = (unsigned long)addr; /* Source address */
- DAR0 = (unsigned long)&PDOR3; /* Destination address */
- BCR0 = size; /* Bytes to transfer */
-
- /* Enable the FIFO and force one write to it */
- SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq));
- /* Also send the audio to S/PDIF */
-#ifdef HAVE_SPDIF_OUT
- EBU1CONFIG = EBU_DEFPARM;
-#endif
- DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_SINC | (3 << 20) | DMA_START;
}
-/* Stops the DMA transfer and interrupt */
-static void dma_stop(void)
-{
- pcm_playing = false;
-
- DCR0 = 0;
- DSR0 = 1;
- /* Reset the FIFO */
- SET_IIS_CONFIG(IIS_RESET | IIS_DEFPARM(pcm_freq));
-#ifdef HAVE_SPDIF_OUT
- EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
-#endif
-}
-
-/* sets frequency of input to DAC */
void pcm_set_frequency(unsigned int frequency)
{
- switch(frequency)
- {
- case 11025:
- pcm_freq = 0x2;
-#ifdef HAVE_UDA1380
- uda1380_set_nsorder(3);
-#endif
- break;
- case 22050:
- pcm_freq = 0x4;
-#ifdef HAVE_UDA1380
- uda1380_set_nsorder(3);
-#endif
- break;
- case 44100:
- default:
- pcm_freq = 0x6;
-#ifdef HAVE_UDA1380
- uda1380_set_nsorder(5);
-#endif
- break;
- }
+ (void)frequency;
}
size_t pcm_get_bytes_waiting(void)
{
- return (BCR0 & 0xffffff);
-}
-
-/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */
-void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
-void DMA0(void)
-{
- int res = DSR0;
-
- DSR0 = 1; /* Clear interrupt */
- DCR0 &= ~DMA_EEXT;
-
- /* Stop on error */
- if(res & 0x70)
- {
- dma_stop();
- logf("DMA Error:0x%04x", res);
- }
- else
- {
- size_t next_size;
- unsigned char *next_start;
- {
- void (*get_more)(unsigned char**, size_t*) = callback_for_more;
- if (get_more)
- get_more(&next_start, &next_size);
- else
- {
- next_size = 0;
- next_start = NULL;
- }
- }
- if(next_size)
- {
- SAR0 = (unsigned long)next_start; /* Source address */
- BCR0 = next_size; /* Bytes to transfer */
- DCR0 |= DMA_EEXT;
- }
- else
- {
- /* Finished playing */
- dma_stop();
- logf("DMA No Data:0x%04x", res);
- }
- }
-
- IPR |= (1<<14); /* Clear pending interrupt request */
-}
-
-void pcm_init(void)
-{
- pcm_playing = false;
- pcm_paused = false;
-
- MPARK = 0x81; /* PARK[1,0]=10 + BCR24BIT */
- DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
- DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1;
- DMACONFIG = 1; /* DMA0Req = PDOR3 */
-
- /* Reset the audio FIFO */
- SET_IIS_CONFIG(IIS_RESET);
-
- /* Enable interrupt at level 7, priority 0 */
- ICR6 = 0x1c;
- IMR &= ~(1<<14); /* bit 14 is DMA0 */
-
- pcm_set_frequency(44100);
-
- /* Prevent pops (resets DAC to zero point) */
- SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq) | IIS_RESET);
-
-#if defined(HAVE_UDA1380)
- /* Initialize default register values. */
- uda1380_init();
-
- /* Sleep a while so the power can stabilize (especially a long
- delay is needed for the line out connector). */
- sleep(HZ);
-
- /* Power on FSDAC and HP amp. */
- uda1380_enable_output(true);
-
- /* Unmute the master channel (DAC should be at zero point now). */
- uda1380_mute(false);
-
-#elif defined(HAVE_TLV320)
- tlv320_init();
- sleep(HZ/4);
- tlv320_mute(false);
-#endif
-
- /* Call dma_stop to initialize everything. */
- dma_stop();
+ return 0;
}
#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
@@ -286,14 +113,14 @@ void pcm_init(void)
#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
#endif
-static int pcm_freq = 44100; /* 44.1 is default */
+static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
/* NOTE: The order of these two variables is important if you use the iPod
assembler optimised fiq handler, so don't change it. */
unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
-static void dma_start(const void *addr, size_t size)
+void pcm_play_dma_start(const void *addr, size_t size)
{
p=(unsigned short*)addr;
p_size=size;
@@ -341,7 +168,7 @@ static void dma_start(const void *addr, size_t size)
}
/* Stops the DMA transfer and interrupt */
-static void dma_stop(void)
+void pcm_play_dma_stop(void)
{
pcm_playing = false;
@@ -365,9 +192,58 @@ static void dma_stop(void)
disable_fiq();
}
+void pcm_play_pause_pause(void)
+{
+#if CONFIG_CPU == PP5020
+ /* Disable the interrupt */
+ IISCONFIG &= ~0x2;
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x20000000;
+#elif CONFIG_CPU == PP5002
+ /* Disable the interrupt */
+ IISFIFO_CFG &= ~(1<<9);
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x4;
+#endif
+ disable_fiq();
+}
+
+void pcm_play_pause_unpause(void)
+{
+ /* Enable the FIFO and fill it */
+
+ enable_fiq();
+
+ /* Enable playback FIFO */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x20000000;
+#elif CONFIG_CPU == PP5002
+ IISCONFIG |= 0x4;
+#endif
+
+ /* Fill the FIFO - we assume there are enough bytes in the
+ pcm buffer to fill the 32-byte FIFO. */
+ while (p_size > 0) {
+ if (FIFO_FREE_COUNT < 2) {
+ /* Enable interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x2;
+#elif CONFIG_CPU == PP5002
+ IISFIFO_CFG |= (1<<9);
+#endif
+ return;
+ }
+
+ IISFIFO_WR = (*(p++))<<16;
+ IISFIFO_WR = (*(p++))<<16;
+ p_size-=4;
+ }
+}
+
void pcm_set_frequency(unsigned int frequency)
{
- pcm_freq=frequency;
+ (void)frequency;
+ pcm_freq = HW_SAMPR_DEFAULT;
}
size_t pcm_get_bytes_waiting(void)
@@ -378,8 +254,8 @@ size_t pcm_get_bytes_waiting(void)
/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
has registers r8-r14 banked, and so does not need to be saved. This routine
uses only these registers, and so will never touch the stack unless it
- actually needs to do so when calling callback_for_more. C version is still
- included below for reference.
+ actually needs to do so when calling pcm_callback_for_more. C version is
+ still included below for reference.
*/
#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
void fiq(void) ICODE_ATTR __attribute__((naked));
@@ -433,10 +309,10 @@ void fiq(void)
"add r1, r11, #4 \n\t" /* r1 = &p_size */
"str r9, [r0] \n\t" /* save internal copies of variables back */
"str r8, [r1] \n\t"
- "ldr r2, =callback_for_more\n\t"
+ "ldr r2, =pcm_callback_for_more\n\t"
"ldr r2, [r2] \n\t" /* get callback address */
"cmp r2, #0 \n\t" /* check for null pointer */
- "movne lr, pc \n\t" /* call callback_for_more */
+ "movne lr, pc \n\t" /* call pcm_callback_for_more */
"bxne r2 \n\t"
"ldmia sp!, { r0-r3, r12, lr}\n\t"
"ldr r8, [r11, #4] \n\t" /* reload p_size and p */
@@ -477,7 +353,7 @@ void fiq(void)
"b .exit \n\t"
);
}
-#else
+#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
void fiq(void)
{
@@ -507,20 +383,21 @@ void fiq(void)
}
/* p is empty, get some more data */
- if (callback_for_more) {
- callback_for_more((unsigned char**)&p,&p_size);
+ if (pcm_callback_for_more) {
+ pcm_callback_for_more((unsigned char**)&p,&p_size);
}
} while (p_size);
/* No more data, so disable the FIFO/FIQ */
- dma_stop();
+ pcm_play_dma_stop();
}
-#endif
+#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
void pcm_init(void)
{
pcm_playing = false;
pcm_paused = false;
+ pcm_callback_for_more = NULL;
/* Initialize default register values. */
wmcodec_init();
@@ -531,8 +408,8 @@ void pcm_init(void)
/* Unmute the master channel (DAC should be at zero point now). */
wmcodec_mute(false);
- /* Call dma_stop to initialize everything. */
- dma_stop();
+ /* Call pcm_play_dma_stop to initialize everything. */
+ pcm_play_dma_stop();
}
#elif (CONFIG_CPU == PNX0101)
@@ -542,12 +419,16 @@ void pcm_init(void)
short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
-static int pcm_freq = 44100; /* 44.1 is default */
+static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
-static void dma_start(const void *addr, size_t size)
+void pcm_init(void)
+{
+}
+
+void pcm_play_dma_start(const void *addr, size_t size)
{
p = (unsigned short*)addr;
p_size = size;
@@ -555,11 +436,19 @@ static void dma_start(const void *addr, size_t size)
pcm_playing = true;
}
-static void dma_stop(void)
+void pcm_play_dma_stop(void)
{
pcm_playing = false;
}
+void pcm_play_pause_pause(void)
+{
+}
+
+void pcm_play_pause_unpause(void)
+{
+}
+
static inline void fill_dma_buf(int offset)
{
short *l, *r, *lend;
@@ -611,8 +500,8 @@ static inline void fill_dma_buf(int offset)
p = tmp_p;
if (l >= lend)
return;
- else if (callback_for_more)
- callback_for_more((unsigned char**)&p,
+ else if (pcm_callback_for_more)
+ pcm_callback_for_more((unsigned char**)&p,
&p_size);
}
while (p_size);
@@ -647,9 +536,10 @@ unsigned long physical_address(void *p)
void pcm_init(void)
{
int i;
- callback_for_more = NULL;
+
pcm_playing = false;
pcm_paused = false;
+ pcm_callback_for_more = NULL;
memset(dma_buf_left, 0, sizeof(dma_buf_left));
memset(dma_buf_right, 0, sizeof(dma_buf_right));
@@ -691,271 +581,37 @@ void pcm_init(void)
void pcm_set_frequency(unsigned int frequency)
{
- pcm_freq=frequency;
+ (void)frequency;
+ pcm_freq = HW_SAMPR_DEFAULT;
}
size_t pcm_get_bytes_waiting(void)
{
return p_size;
}
-#endif
+#endif /* CONFIG_CPU == */
-void pcm_play_stop(void)
+/* dummy functions for those not actually supporting all this yet */
+void pcm_apply_settings(bool reset)
{
- if (pcm_playing) {
- dma_stop();
- }
+ (void)reset;
}
-#endif
-
-void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
- unsigned char* start, size_t size)
+void pcm_set_monitor(int monitor)
{
- callback_for_more = get_more;
-
- if (!(start && size))
- {
- if (get_more)
- get_more(&start, &size);
- else
- return;
- }
- if (start && size)
- {
- dma_start(start, size);
- if (pcm_paused) {
- pcm_paused = false;
- pcm_play_pause(false);
- }
- }
+ (void)monitor;
}
+/** **/
void pcm_mute(bool mute)
{
-#ifdef HAVE_UDA1380
- uda1380_mute(mute);
-#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
+#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
|| defined(HAVE_WM8731) || defined(HAVE_WM8721)
wmcodec_mute(mute);
-#elif defined(HAVE_TLV320)
- tlv320_mute(mute);
#endif
if (mute)
sleep(HZ/16);
}
-void pcm_play_pause(bool play)
-{
- bool needs_change = pcm_paused == play;
-
- /* This needs to be done ahead of the rest to prevent infinite
- * recursion from dma_start */
- pcm_paused = !play;
- if (pcm_playing && needs_change) {
- if(play) {
- if (pcm_get_bytes_waiting()) {
- logf("unpause");
-
-#ifdef CPU_COLDFIRE
- /* Enable the FIFO and force one write to it */
- SET_IIS_CONFIG(IIS_DEFPARM(pcm_freq));
-#ifdef HAVE_SPDIF_OUT
- EBU1CONFIG = EBU_DEFPARM;
-#endif
- DCR0 |= DMA_EEXT | DMA_START;
-#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
- || defined(HAVE_WM8731) || defined(HAVE_WM8721)
- /* Enable the FIFO and fill it */
-
- enable_fiq();
-
- /* Enable playback FIFO */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x20000000;
-#elif CONFIG_CPU == PP5002
- IISCONFIG |= 0x4;
-#endif
-
- /* Fill the FIFO - we assume there are enough bytes in the
- pcm buffer to fill the 32-byte FIFO. */
- while (p_size > 0) {
- if (FIFO_FREE_COUNT < 2) {
- /* Enable interrupt */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x2;
-#elif CONFIG_CPU == PP5002
- IISFIFO_CFG |= (1<<9);
-#endif
- return;
- }
-
- IISFIFO_WR = (*(p++))<<16;
- IISFIFO_WR = (*(p++))<<16;
- p_size-=4;
- }
-#elif (CONFIG_CPU == PNX0101 || CONFIG_CPU == S3C2440) /* End wmcodecs */
- /* nothing yet */
-#endif
- } else {
-#if (CONFIG_CPU != PNX0101 && CONFIG_CPU != S3C2440)
- size_t next_size;
- unsigned char *next_start;
- void (*get_more)(unsigned char**, size_t*) = callback_for_more;
- logf("unpause, no data waiting");
- if (get_more)
- get_more(&next_start, &next_size);
- if (next_start && next_size)
- dma_start(next_start, next_size);
- else
- {
- dma_stop();
- logf("unpause attempted, no data");
- }
-#endif
- }
- } else {
- logf("pause");
-
-#ifdef CPU_COLDFIRE
- /* Disable DMA peripheral request. */
- DCR0 &= ~DMA_EEXT;
- SET_IIS_CONFIG(IIS_RESET | IIS_DEFPARM(pcm_freq));
-#ifdef HAVE_SPDIF_OUT
- EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
-#endif
-#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
- || defined(HAVE_WM8731) || defined(HAVE_WM8721)
-#if CONFIG_CPU == PP5020
- /* Disable the interrupt */
- IISCONFIG &= ~0x2;
- /* Disable playback FIFO */
- IISCONFIG &= ~0x20000000;
-#elif CONFIG_CPU == PP5002
- /* Disable the interrupt */
- IISFIFO_CFG &= ~(1<<9);
- /* Disable playback FIFO */
- IISCONFIG &= ~0x4;
-#endif
-
- disable_fiq();
-#elif (CONFIG_CPU == PNX0101 || CONFIG_CPU == S3C2440) /* End wmcodecs */
- /* nothing yet */
-#endif
- }
- } /* pcm_playing && needs_change */
-}
-
-bool pcm_is_playing(void) {
- return pcm_playing;
-}
-
-bool pcm_is_paused(void) {
- return pcm_paused;
-}
-
-
-#if defined(CPU_COLDFIRE)
-/* Peaks ahead in the DMA buffer based upon the calling period to
- attempt to compensate for the delay. Keeps a moving average of
- length four. */
-void pcm_calculate_peaks(int *left, int *right)
-{
- unsigned long samples;
- unsigned long *addr, *end;
- long peak_p, peak_n;
-
- static unsigned long last_peak_tick = 0;
- static unsigned long frame_period = 0;
-
- /* Throttled peak ahead based on calling period */
- unsigned long period = current_tick - last_peak_tick;
-
- /* Keep reasonable limits on period */
- if (period < 1)
- period = 1;
- else if (period > HZ/5)
- period = HZ/5;
-
- frame_period = (3*frame_period + period) >> 2;
-
- last_peak_tick = current_tick;
-
- if (!pcm_playing || pcm_paused)
- {
- peak_left = peak_right = 0;
- goto peak_done;
- }
-
- samples = (BCR0 & 0xffffff) >> 2;
- addr = (long *)(SAR0 & ~3);
- samples = MIN(frame_period*44100/HZ, samples);
- end = addr + samples;
- peak_p = peak_n = 0;
-
- if (left && right)
- {
- if (samples > 0)
- {
- long peak_rp = 0, peak_rn = 0;
-
- do
- {
- long value = *addr;
- long ch;
-
- ch = value >> 16;
- if (ch > peak_p) peak_p = ch;
- else if (ch < peak_n) peak_n = ch;
-
- ch = (short)value;
- if (ch > peak_rp) peak_rp = ch;
- else if (ch < peak_rn) peak_rn = ch;
-
- addr += 4;
- }
- while (addr < end);
-
- peak_left = MAX(peak_p, -peak_n);
- peak_right = MAX(peak_rp, -peak_rn);
- }
- }
- else if (left || right)
- {
- if (samples > 0)
- {
- if (left)
- {
- /* Put left channel in low word */
- addr = (long *)((short *)addr - 1);
- end = (long *)((short *)end - 1);
- }
-
- do
- {
- long value = *(short *)addr;
-
- if (value > peak_p) peak_p = value;
- else if (value < peak_n) peak_n = value;
-
- addr += 4;
- }
- while (addr < end);
-
- if (left)
- peak_left = MAX(peak_p, -peak_n);
- else
- peak_right = MAX(peak_p, -peak_n);
- }
- }
-
-peak_done:
- if (left)
- *left = peak_left;
-
- if (right)
- *right = peak_right;
-}
-#else
/*
* This function goes directly into the DMA buffer to calculate the left and
* right peak values. To avoid missing peaks it tries to look forward two full
@@ -1037,4 +693,94 @@ void pcm_calculate_peaks(int *left, int *right)
}
#endif
}
+
#endif /* CPU_COLDFIRE */
+
+/****************************************************************************
+ * Functions that do not require targeted implementation but only a targeted
+ * interface
+ */
+
+/* Common code to pcm_play_data and pcm_play_pause
+ Returns true if DMA playback was started, else false. */
+bool pcm_play_data_start(pcm_more_callback_type get_more,
+ unsigned char *start, size_t size)
+{
+ if (!(start && size))
+ {
+ size = 0;
+ if (get_more)
+ get_more(&start, &size);
+ }
+
+ if (start && size)
+ {
+ pcm_play_dma_start(start, size);
+ return true;
+ }
+
+ return false;
+}
+
+void pcm_play_data(pcm_more_callback_type get_more,
+ unsigned char *start, size_t size)
+{
+ pcm_callback_for_more = get_more;
+
+ if (pcm_play_data_start(get_more, start, size) && pcm_paused)
+ {
+ pcm_paused = false;
+ pcm_play_pause(false);
+ }
+}
+
+void pcm_play_pause(bool play)
+{
+ bool needs_change = pcm_paused == play;
+
+ /* This needs to be done ahead of the rest to prevent infinite
+ recursion from pcm_play_data */
+ pcm_paused = !play;
+
+ if (pcm_playing && needs_change)
+ {
+ if (play)
+ {
+ if (pcm_get_bytes_waiting())
+ {
+ logf("unpause");
+ pcm_play_pause_unpause();
+ }
+ else
+ {
+ logf("unpause, no data waiting");
+ if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0))
+ {
+ pcm_play_dma_stop();
+ logf("unpause attempted, no data");
+ }
+ }
+ }
+ else
+ {
+ logf("pause");
+ pcm_play_pause_pause();
+ }
+ } /* pcm_playing && needs_change */
+}
+
+void pcm_play_stop(void)
+{
+ if (pcm_playing)
+ pcm_play_dma_stop();
+}
+
+bool pcm_is_playing(void)
+{
+ return pcm_playing;
+}
+
+bool pcm_is_paused(void)
+{
+ return pcm_paused;
+}
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
index 2785d4b..25f1f1e 100644
--- a/firmware/pcm_record.c
+++ b/firmware/pcm_record.c
@@ -16,199 +16,238 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-#include "config.h"
-#include "debug.h"
+#include "system.h"
+#include "kernel.h"
+#include "logf.h"
#include "panic.h"
#include "thread.h"
-
-#include <kernel.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
#include <string.h>
-
-#include "cpu.h"
-#include "i2c.h"
-#include "power.h"
-#ifdef HAVE_UDA1380
+#include "ata.h"
+#include "usb.h"
+#if defined(HAVE_UDA1380)
#include "uda1380.h"
-#endif
-#ifdef HAVE_TLV320
+#include "general.h"
+#elif defined(HAVE_TLV320)
#include "tlv320.h"
#endif
-#include "system.h"
-#include "usb.h"
-
#include "buffer.h"
#include "audio.h"
-#include "button.h"
-#include "file.h"
-#include "sprintf.h"
-#include "logf.h"
-#include "button.h"
-#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 */
/***************************************************************************/
+/**
+ * APIs implemented in the target tree portion:
+ * Public -
+ * pcm_init_recording
+ * pcm_close_recording
+ * pcm_rec_mux
+ * Semi-private -
+ * pcm_rec_dma_start
+ * pcm_rec_dma_stop
+ */
+
+/** These items may be implemented target specifically or need to
+ be shared semi-privately **/
+
+/* the registered callback function for when more data is available */
+pcm_more_callback_type pcm_callback_more_ready = NULL;
+/* DMA transfer in is currently active */
+bool pcm_recording = false;
+
+/* APIs implemented in the target-specific portion */
+void pcm_rec_dma_start(const void *addr, size_t size);
+void pcm_rec_dma_stop(void);
+
+/** General recording state **/
static bool is_recording; /* We are recording */
static bool is_paused; /* We have paused */
+static bool is_stopping; /* We are currently stopping */
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; /* 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 */
+/** 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 */
-static int wav_file;
-static char recording_filename[MAX_PATH];
+/** Stats on encoded data for all files from start to stop **/
+static unsigned long long accum_rec_bytes; /* total size written to chunks */
+static unsigned long long accum_pcm_samples; /* total pcm count processed */
-static volatile bool init_done, close_done, record_done;
-static volatile bool stop_done, pause_done, resume_done, new_file_done;
-
-static int peak_left, peak_right;
+/* 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
+};
-#ifdef IAUDIO_X5
-#define SET_IIS_PLAY(x) IIS1CONFIG = (x);
-#define SET_IIS_REC(x) IIS1CONFIG = (x);
-#else
-#define SET_IIS_PLAY(x) IIS2CONFIG = (x);
-#define SET_IIS_REC(x) IIS1CONFIG = (x);
-#endif
+/** 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 struct encoder_config enc_config; /* Current encoder configuration */
/****************************************************************************
- use 2 circular buffers of same size:
- rec_buffer=DMA output buffer: chunks (8192 Bytes) of raw pcm audio data
+ 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 rec_buffer
+ 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:
- 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
+ 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_pcm_buf_near_empty(); if 1: 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)
+ A.pcmrec: enc_events_callback(); called for certain events
****************************************************************************/
-#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 */
+
+/** 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 */
+static volatile int dma_wr_pos; /* current DMA write pos */
+static int pcm_rd_pos; /* current PCM read pos */
+static volatile bool dma_lock; /* lock DMA write position */
+static unsigned long pre_record_ticks;/* pre-record time in ticks */
+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 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 size_t enc_chunk_size; /* maximum encoder chunk size */
+static size_t enc_data_size; /* maximum data size for encoder */
+static unsigned long enc_sample_rate; /* sample rate used by encoder */
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);
+/** file flushing **/
+static int write_threshold; /* max chunk limit for data flush */
+static int panic_threshold; /* boost thread prio when here */
+static int spinup_time = -1;/* last ata_spinup_time */
+
+/** 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 */
+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 */
/***************************************************************************/
static struct event_queue pcmrec_queue;
-static long pcmrec_stack[2*DEFAULT_STACK_SIZE/sizeof(long)];
+static long pcmrec_stack[3*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 */
-#define PCMREC_CLOSE 2
+/* Event values which are also single-bit flags */
+#define PCMREC_INIT 0x00000001 /* enable recording */
+#define PCMREC_CLOSE 0x00000002
-#define PCMREC_START 3 /* Start a new recording */
-#define PCMREC_STOP 4 /* Stop the current recording */
-#define PCMREC_PAUSE 10
-#define PCMREC_RESUME 11
-#define PCMREC_NEW_FILE 12
-#define PCMREC_SET_GAIN 13
+#define PCMREC_START 0x00000004 /* start recording (when stopped) */
+#define PCMREC_STOP 0x00000008 /* stop the current recording */
+#define PCMREC_PAUSE 0x00000010 /* pause the current recording */
+#define PCMREC_RESUME 0x00000020 /* resume the current recording */
+#define PCMREC_NEW_FILE 0x00000040 /* start new file (when recording) */
+#define PCMREC_SET_GAIN 0x00000080
+#define PCMREC_FLUSH_NUM 0x00000100 /* flush a number of files out */
+#define PCMREC_FINISH_STOP 0x00000200 /* finish the stopping recording */
-/*******************************************************************/
-/* Functions that are not executing in the pcmrec_thread first */
-/*******************************************************************/
+/* mask for signaling events */
+static volatile long pcm_thread_event_mask;
-/* Creates pcmrec_thread */
-void pcm_rec_init(void)
+static void pcm_thread_sync_post(long event, void *data)
{
- queue_init(&pcmrec_queue, true);
- create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
- pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING));
-}
+ pcm_thread_event_mask &= ~event;
+ queue_post(&pcmrec_queue, event, data);
+ while(!(event & pcm_thread_event_mask))
+ yield();
+} /* pcm_thread_sync_post */
+static inline void pcm_thread_signal_event(long event)
+{
+ pcm_thread_event_mask |= event;
+} /* pcm_thread_signal_event */
-int audio_get_encoder_id(void)
+static inline void pcm_thread_unsignal_event(long event)
{
- return audio_enc_id;
-}
+ pcm_thread_event_mask &= ~event;
+} /* pcm_thread_unsignal_event */
-/* Initializes recording:
- * - Set up the UDA1380/TLV320 for recording
- * - Prepare for DMA transfers
- */
+static inline bool pcm_thread_event_state(long signaled, long unsignaled)
+{
+ return ((signaled | unsignaled) & pcm_thread_event_mask) == signaled;
+} /* pcm_thread_event_state */
-void audio_init_recording(unsigned int buffer_offset)
+static void pcm_thread_wait_for_stop(void)
{
- (void)buffer_offset;
+ if (is_stopping)
+ {
+ logf("waiting for stop to finish");
+ while (is_stopping)
+ yield();
+ }
+} /* pcm_thread_wait_for_stop */
- init_done = false;
- queue_post(&pcmrec_queue, PCMREC_INIT, 0);
+/*******************************************************************/
+/* Functions that are not executing in the pcmrec_thread first */
+/*******************************************************************/
- while(!init_done)
- sleep_thread(1);
-}
-
-void audio_close_recording(void)
+/* Callback for when more data is ready */
+static void pcm_rec_have_more(unsigned char **data, size_t *size)
{
- close_done = false;
- queue_post(&pcmrec_queue, PCMREC_CLOSE, 0);
+ if (*size != 0)
+ {
+ /* some error condition */
+ if (*size == DMA_REC_ERROR_DMA)
+ {
+ /* Flush recorded data to disk and stop recording */
+ queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
+ return;
+ }
+ /* else try again next transmission */
+ }
+ else if (!dma_lock)
+ {
+ /* advance write position */
+ dma_wr_pos = (dma_wr_pos + PCM_CHUNK_SIZE) & PCM_CHUNK_MASK;
+ }
- while(!close_done)
- sleep_thread(1);
+ *data = (unsigned char *)GET_PCM_CHUNK(dma_wr_pos);
+ *size = PCM_CHUNK_SIZE;
+} /* pcm_rec_have_more */
- audio_remove_encoder();
-}
+/** pcm_rec_* group **/
+void pcm_rec_error_clear(void)
+{
+ is_error = false;
+} /* pcm_rec_error_clear */
unsigned long pcm_rec_status(void)
{
@@ -216,165 +255,223 @@ unsigned long pcm_rec_status(void)
if (is_recording)
ret |= AUDIO_STATUS_RECORD;
+
if (is_paused)
ret |= AUDIO_STATUS_PAUSE;
+
if (is_error)
ret |= AUDIO_STATUS_ERROR;
- if (!is_recording && pre_record_ticks && init_done && !close_done)
+
+ if (!is_recording && pre_record_ticks &&
+ pcm_thread_event_state(PCMREC_INIT, PCMREC_CLOSE))
ret |= AUDIO_STATUS_PRERECORD;
return ret;
-}
+} /* pcm_rec_status */
int pcm_rec_current_bitrate(void)
{
- return avrg_bit_rate;
-}
+ if (accum_pcm_samples == 0)
+ return 0;
-unsigned long audio_recorded_time(void)
+ return (int)(8*accum_rec_bytes*enc_sample_rate / (1000*accum_pcm_samples));
+} /* pcm_rec_current_bitrate */
+
+int pcm_rec_encoder_afmt(void)
{
- if (is_recording)
+ return enc_config.afmt;
+} /* pcm_rec_encoder_afmt */
+
+int pcm_rec_rec_format(void)
{
- if (is_paused)
- return pause_start_time - record_start_time;
- else
- return current_tick - record_start_time;
- }
+ return afmt_rec_format[enc_config.afmt];
+} /* pcm_rec_rec_format */
- return 0;
-}
+unsigned long pcm_rec_sample_rate(void)
+{
+ /* Which is better ?? */
+#if 0
+ return enc_sample_rate;
+#endif
+ return sample_rate;
+} /* audio_get_sample_rate */
-unsigned long audio_num_recorded_bytes(void)
+/**
+ * Creates pcmrec_thread
+ */
+void pcm_rec_init(void)
{
- if (is_recording)
- return num_rec_bytes;
+ queue_init(&pcmrec_queue, true);
+ create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
+ pcmrec_thread_name, PRIORITY_RECORDING);
+} /* pcm_rec_init */
+/** audio_* group **/
+
+void audio_init_recording(unsigned int buffer_offset)
+{
+ (void)buffer_offset;
+ pcm_thread_wait_for_stop();
+ pcm_thread_sync_post(PCMREC_INIT, NULL);
+} /* audio_init_recording */
+
+void audio_close_recording(void)
+{
+ pcm_thread_wait_for_stop();
+ pcm_thread_sync_post(PCMREC_CLOSE, NULL);
+ /* reset pcm to defaults (playback only) */
+ pcm_set_frequency(-1);
+ pcm_set_monitor(-1);
+ pcm_set_rec_source(-1);
+#ifdef HAVE_TLV320
+ /* tlv320 screeches if left at 88.2 with no inputs */
+ pcm_apply_settings(true);
+#endif
+ audio_remove_encoder();
+} /* audio_close_recording */
+
+unsigned long audio_recorded_time(void)
+{
+ if (!is_recording || enc_sample_rate == 0)
return 0;
-}
-#ifdef HAVE_SPDIF_IN
-/* Only the last six of these are standard rates, but all sample rates are
- * possible, so we support some other common ones as well.
- */
-static unsigned long spdif_sample_rates[] = {
- 8000, 11025, 12000, 16000, 22050, 24000,
- 32000, 44100, 48000, 64000, 88200, 96000
-};
+ /* 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 SPDIF sample rate. Since we base our reading on the actual SPDIF
- * sample rate (which might be a bit inaccurate), we round off to the closest
- * sample rate that is supported by SPDIF.
- */
-unsigned long audio_get_spdif_sample_rate(void)
+unsigned long audio_num_recorded_bytes(void)
{
- int i = 0;
- unsigned long measured_rate;
- const int upper_bound = sizeof(spdif_sample_rates)/sizeof(long) - 1;
+ if (!is_recording)
+ return 0;
+
+ return num_rec_bytes;
+} /* audio_num_recorded_bytes */
+#ifdef HAVE_SPDIF_IN
+/* Return current SPDIF sample rate */
+static unsigned long measure_spdif_sample_rate(void)
+{
/* The following formula is specified in MCF5249 user's manual section
- * 17.6.1. The 3*(1 << 13) part will need changing if the setup of the
- * PHASECONFIG register is ever changed. The 128 divide is because of the
- * fact that the SPDIF clock is the sample rate times 128.
+ * 17.6.1. The 128 divide is because of the fact that the SPDIF clock is
+ * the sample rate times 128. Keep "3*(1 << 13)" part in sync with
+ * PHASECONFIG setup in pcm_init_recording in pcm-coldfire.c.
*/
- measured_rate = (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ/
+ return (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ /
((1 << 15)*3*(1 << 13))/128);
- /* Find which SPDIF sample rate we're closest to. */
- while (spdif_sample_rates[i] < measured_rate && i < upper_bound) ++i;
- if (i > 0 && i < upper_bound)
- {
- long diff1 = measured_rate - spdif_sample_rates[i - 1];
- long diff2 = spdif_sample_rates[i] - measured_rate;
+} /* measure_spdif_sample_rate */
- if (diff2 > diff1) --i;
- }
- return i;
-}
-#endif
+/**
+ * Return SPDIF sample rate index in audio_master_sampr_list. Since we base
+ * our reading on the actual SPDIF sample rate (which might be a bit
+ * inaccurate), we round off to the closest sample rate that is supported by
+ * SPDIF.
+ */
+int audio_get_spdif_sample_rate(void)
+{
+ unsigned long measured_rate = measure_spdif_sample_rate();
+ /* Find which SPDIF sample rate we're closest to. */
+ return round_value_to_list32(measured_rate, audio_master_sampr_list,
+ SAMPR_NUM_FREQ, false);
+} /* audio_get_spdif_sample_rate */
-#if 0
-/* not needed atm */
#ifdef HAVE_SPDIF_POWER
static bool spdif_power_setting;
void audio_set_spdif_power_setting(bool on)
{
spdif_power_setting = on;
-}
+} /* audio_set_spdif_power_setting */
+
+bool audio_get_spdif_power_setting(void)
+{
+ return spdif_power_setting;
+} /* audio_get_spdif_power_setting */
#endif
+
+void audio_spdif_set_monitor(int monitor_spdif)
+{
+ EBU1CONFIG = 0x800; /* Reset before reprogram */
+
+ if (monitor_spdif > 0)
+ {
+#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
+ }
+ else if (monitor_spdif == 0)
+ {
+ /* SCLK2, TXSRC = IIS1recv, validity, normal operation */
+ EBU1CONFIG = (7 << 12) | (4 << 8) | (1 << 5) | (5 << 2);
+ }
+} /* audio_spdif_set_monitor */
+
+#endif /* HAVE_SPDIF_IN */
/**
* Sets recording parameters
- *
- * This functions starts feeding the CPU with audio data over the I2S bus
*/
-void audio_set_recording_options(int frequency, int quality,
- int source, int channel_mode,
- bool editable, int prerecord_time)
+void audio_set_recording_options(struct audio_recording_options *options)
{
- /* TODO: */
- (void)editable;
+ pcm_thread_wait_for_stop();
- /* NOTE: Coldfire UDA based recording does not yet support anything other
- * than 44.1kHz sampling rate, so we limit it to that case here now. SPDIF
- * based recording will overwrite this value with the proper sample rate in
- * audio_record(), and will not be affected by this.
- */
- frequency = 44100;
- enc_quality = quality;
- rec_source = source;
- enc_channels = channel_mode == CHN_MODE_MONO ? 1 : 2;
- pre_record_ticks = prerecord_time * HZ;
+ /* stop DMA transfer */
+ dma_lock = true;
+ pcm_stop_recording();
- switch (source)
- {
- case AUDIO_SRC_MIC:
- case AUDIO_SRC_LINEIN:
-#ifdef HAVE_FMRADIO_IN
- case AUDIO_SRC_FMRADIO:
-#endif
- /* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */
- DATAINCONTROL = 0xc020;
- break;
+ rec_frequency = options->rec_frequency;
+ rec_source = options->rec_source;
+ num_channels = options->rec_channels == 1 ? 1 : 2;
+ 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
- case AUDIO_SRC_SPDIF:
- /* Int. when 6 samples in FIFO. PDIR2 source = ebu1RcvData */
- DATAINCONTROL = 0xc038;
- break;
-#endif /* HAVE_SPDIF_IN */
+ if (rec_source == AUDIO_SRC_SPDIF)
+ {
+ /* must measure SPDIF sample rate before configuring codecs */
+ unsigned long sr = measure_spdif_sample_rate();
+ /* 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);
}
- sample_rate = frequency;
-
- /* Monitoring: route the signals through the coldfire audio interface. */
+ pcm_set_monitor(rec_source);
+ pcm_set_rec_source(rec_source);
- SET_IIS_PLAY(0x800); /* Reset before reprogram */
+ /* apply pcm settings to hardware */
+ pcm_apply_settings(true);
-#ifdef HAVE_SPDIF_IN
- if (source == AUDIO_SRC_SPDIF)
+ if (audio_load_encoder(enc_config.afmt))
{
- /* 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 */
+ /* start DMA transfer */
+ pcm_record_data(pcm_rec_have_more, NULL, 0);
+ /* do unlock after starting to prevent preincrement of dma_wr_pos */
+ dma_lock = pre_record_ticks == 0;
}
else
{
- /* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */
- IIS2CONFIG = (8 << 12) | (4 << 8) | (4 << 2);
-
- EBU1CONFIG = 0x800; /* Reset before reprogram */
- /* SCLK2, TXSRC = IIS1recv, validity, normal operation */
- EBU1CONFIG = (7 << 12) | (4 << 8) | (1 << 5) | (5 << 2);
- }
-#else
- /* 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]);
+ logf("set rec opt: enc load failed");
+ is_error = true;
}
-
+} /* audio_set_recording_options */
/**
* Note that microphone is mono, only left value is used
@@ -391,8 +488,7 @@ void audio_set_recording_gain(int left, int right, int type)
#elif defined (HAVE_TLV320)
tlv320_set_recvol(left, right, type);
#endif
-}
-
+} /* audio_set_recording_gain */
/**
* Start recording
@@ -401,585 +497,882 @@ void audio_set_recording_gain(int left, int right, int type)
*/
void audio_record(const char *filename)
{
+ logf("audio_record: %s", filename);
+
+ pcm_thread_wait_for_stop();
+ pcm_thread_sync_post(PCMREC_START, (void *)filename);
+
+ logf("audio_record_done");
+} /* audio_record */
+
+void audio_new_file(const char *filename)
+{
+ logf("audio_new_file: %s", filename);
+
+ pcm_thread_wait_for_stop();
+ pcm_thread_sync_post(PCMREC_NEW_FILE, (void *)filename);
+
+ logf("audio_new_file done");
+} /* audio_new_file */
+
+void audio_stop_recording(void)
+{
+ logf("audio_stop_recording");
+
+ pcm_thread_wait_for_stop();
+
if (is_recording)
- {
- logf("record while recording");
- return;
- }
+ dma_lock = true; /* fix DMA write ptr at current position */
+
+ pcm_thread_sync_post(PCMREC_STOP, NULL);
+
+ logf("audio_stop_recording done");
+} /* audio_stop_recording */
+
+void audio_pause_recording(void)
+{
+ logf("audio_pause_recording");
- strncpy(recording_filename, filename, MAX_PATH - 1);
- recording_filename[MAX_PATH - 1] = 0;
+ pcm_thread_wait_for_stop();
-#ifdef HAVE_SPDIF_IN
- if (rec_source == AUDIO_SRC_SPDIF)
- sample_rate = audio_get_spdif_sample_rate();
-#endif
+ if (is_recording)
+ dma_lock = true; /* fix DMA write ptr at current position */
- record_done = false;
- queue_post(&pcmrec_queue, PCMREC_START, 0);
+ pcm_thread_sync_post(PCMREC_PAUSE, NULL);
+ logf("audio_pause_recording done");
+} /* audio_pause_recording */
- while(!record_done)
- sleep_thread(1);
-}
+void audio_resume_recording(void)
+{
+ logf("audio_resume_recording");
+ pcm_thread_wait_for_stop();
+ pcm_thread_sync_post(PCMREC_RESUME, NULL);
-void audio_new_file(const char *filename)
+ logf("audio_resume_recording done");
+} /* audio_resume_recording */
+
+/***************************************************************************/
+/* */
+/* 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)
{
- logf("pcm_new_file");
+ 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 */
- new_file_done = false;
+/* 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;
- strncpy(recording_filename, filename, MAX_PATH - 1);
- recording_filename[MAX_PATH - 1] = 0;
+ return size >= fnq_size - MAX_PATH;
+} /* pcmrec_fnq_is_full */
- queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0);
+/* 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);
- while(!new_file_done)
- sleep_thread(1);
+ if ((fnq_wr_pos += MAX_PATH) >= fnq_size)
+ fnq_wr_pos = 0;
- logf("pcm_new_file done");
-}
+ if (fnq_rd_pos != fnq_wr_pos)
+ return true;
-/**
- *
- */
-void audio_stop_recording(void)
+ /* queue full */
+ if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
+ fnq_rd_pos = 0;
+
+ return true;
+} /* pcmrec_fnq_add_filename */
+
+/* replace the last filename added */
+static bool pcmrec_fnq_replace_tail(const char *filename)
{
- if (!is_recording)
- return;
+ int pos;
+
+ if (pcmrec_fnq_is_empty())
+ return false;
+
+ pos = fnq_wr_pos - MAX_PATH;
+ if (pos < 0)
+ pos = fnq_size - MAX_PATH;
+
+ strncpy(fn_queue + pos, filename, MAX_PATH);
+
+ return true;
+} /* pcmrec_fnq_replace_tail */
- logf("pcm_stop");
+/* 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);
- is_paused = true; /* fix pcm write ptr at current position */
- stop_done = false;
- queue_post(&pcmrec_queue, PCMREC_STOP, 0);
+ if ((fnq_rd_pos += MAX_PATH) >= fnq_size)
+ fnq_rd_pos = 0;
- while(!stop_done)
- sleep_thread(1);
+ return true;
+} /* pcmrec_fnq_get_filename */
- logf("pcm_stop done");
-}
+/* close the file number pointed to by fd_p */
+static void pcmrec_close_file(int *fd_p)
+{
+ if (*fd_p < 0)
+ return; /* preserve error */
-void audio_pause_recording(void)
+ close(*fd_p);
+ *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 (!is_recording)
+ if (rec_fdata.new_enc_size != prev_enc_size)
{
- logf("pause when not recording");
- return;
+ ssize_t size_diff = rec_fdata.new_enc_size - prev_enc_size;
+ num_rec_bytes += size_diff;
+ accum_rec_bytes += size_diff;
}
- if (is_paused)
+
+ if (rec_fdata.new_num_pcm != prev_num_pcm)
{
- logf("pause when paused");
- return;
+ unsigned long pcm_diff = rec_fdata.new_num_pcm - prev_num_pcm;
+ num_rec_samples += pcm_diff;
+ accum_pcm_samples += pcm_diff;
}
-
- pause_done = false;
- queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
+} /* pcmrec_update_sizes_inl */
- while(!pause_done)
- sleep_thread(1);
-}
+/* 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 */
-void audio_resume_recording(void)
+static void pcmrec_start_file(void)
{
- if (!is_paused)
+ 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("resume when not paused");
- return;
+ logf("start file: fnq empty");
+ *filename = '\0';
+ is_error = true;
+ }
+ else if (is_error)
+ {
+ logf("start file: is_error already");
+ }
+ else if (curr_rec_file >= 0)
+ {
+ /* Any previous file should have been closed */
+ logf("start file: file already open");
+ is_error = true;
}
- resume_done = false;
- queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
+ if (is_error)
+ rec_fdata.chunk->flags |= CHUNKF_ERROR;
- while(!resume_done)
- sleep_thread(1);
-}
+ /* 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 (!is_error && (rec_fdata.chunk->flags & CHUNKF_ERROR))
+ {
+ logf("start file: enc error");
+ is_error = true;
+ }
-/* return peaks as int, so convert from short first
- note that peak values are always positive */
-void pcm_rec_get_peaks(int *left, int *right)
+ if (is_error)
+ {
+ 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)
{
- if (left)
- *left = peak_left;
- if (right)
- *right = peak_right;
- peak_left = 0;
- peak_right = 0;
-}
+ size_t enc_size = rec_fdata.new_enc_size;
+ unsigned long num_pcm = rec_fdata.new_num_pcm;
-/***************************************************************************/
-/* Functions that executes in the context of pcmrec_thread */
-/***************************************************************************/
+ if (is_error)
+ 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 (!is_error)
+ {
+ logf("wr chk enc error %d %d",
+ rec_fdata.chunk->enc_size, rec_fdata.chunk->num_pcm);
+ is_error = true;
+ }
+} /* 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 (!is_error)
+ {
+ if (rec_fdata.chunk->flags & CHUNKF_ERROR)
+ {
+ logf("end file: enc error");
+ is_error = true;
+ }
+ else
+ {
+ pcmrec_update_sizes(enc_size, num_pcm);
+ }
+ }
+
+ /* Force file close if error */
+ if (is_error)
+ pcmrec_close_file(&rec_fdata.rec_file);
+
+ rec_fdata.chunk->flags &= ~CHUNKF_END_FILE;
+} /* pcmrec_end_file */
/**
* Process the chunks
*
* This function is called when queue_get_w_tmo times out.
*
- * Other functions can also call this function with flush = true when
- * they want to save everything in the buffers to disk.
+ * Set flush_num to the number of files to flush to disk.
+ * flush_num = -1 to flush all available chunks to disk.
+ * flush_num = 0 normal write thresholding
+ * flush_num = 1 or greater - all available chunks of current file plus
+ * flush_num file starts if first chunk has been processed.
*
*/
-static void pcmrec_callback(bool flush)
+static void pcmrec_flush(unsigned flush_num)
{
- int i, num_ready, size_yield;
- long *enc_chunk, chunk_size;
-
- if (!is_recording && !flush)
- return;
+ static unsigned long last_flush_tick = 0;
+ unsigned long start_tick;
+ int num_ready, num;
+ int prio;
+ int i;
num_ready = enc_wr_index - enc_rd_index;
if (num_ready < 0)
num_ready += enc_num_chunks;
- /* 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);
+ num = num_ready;
- /* near full state reached: less than 5sec remaining space */
- if (enc_num_chunks - num_ready < WRITE_THRESHOLD || flush)
+ if (flush_num == 0)
{
- logf("writing: %d (%d)", num_ready, flush);
-
- cpu_boost_id(true, CPUBOOSTID_PCMRECORD);
+ if (!is_recording)
+ return;
- size_yield = 0;
- for (i=0; i<num_ready; i++)
+ if (ata_spinup_time != spinup_time)
{
- 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);
+ /* spinup time has changed, calculate new write threshold */
+ logf("new t spinup : %d", ata_spinup_time);
+ unsigned long st = spinup_time = ata_spinup_time;
+
+ /* write at 5s + st remaining in enc_buffer */
+ if (st < 2*HZ)
+ st = 2*HZ; /* my drive is usually < 250 ticks :) */
+ else if (st > 10*HZ)
+ st = 10*HZ;
+
+ write_threshold = enc_num_chunks -
+ (int)(((5ull*HZ + st)*4ull*sample_rate + (enc_chunk_size-1)) /
+ (enc_chunk_size*HZ));
+
+ if (write_threshold < 0)
+ write_threshold = 0;
+ else if (write_threshold > panic_threshold)
+ write_threshold = panic_threshold;
+
+ logf("new wr thresh: %d", write_threshold);
+ }
- if (enc_set_header_callback != NULL)
- enc_set_header_callback(enc_chunk, enc_chunk_size,
- num_pcm_samples, false);
+ if (num_ready < write_threshold)
+ return;
- if (write(wav_file, enc_chunk, chunk_size) != chunk_size)
- {
- close_wave();
- logf("pcmrec: write err");
- is_error = true;
- break;
- }
+ /* if we're getting called too much and this isn't forced,
+ boost stat */
+ if (current_tick - last_flush_tick < HZ/2)
+ num = panic_threshold;
+ }
- num_file_bytes += chunk_size;
- num_pcm_samples += enc_samp_per_chunk;
- size_yield += chunk_size;
+ start_tick = current_tick;
+ prio = -1;
- if (size_yield >= 32768)
- { /* yield when 32kB written */
- size_yield = 0;
- yield();
- }
+ logf("writing: %d (%d)", num_ready, flush_num);
+
+ cpu_boost_id(true, CPUBOOSTID_PCMRECORD);
- enc_rd_index = (enc_rd_index + 1) % enc_num_chunks;
+ for (i=0; i<num_ready; i++)
+ {
+ if (prio == -1 && (num >= panic_threshold ||
+ current_tick - start_tick > 10*HZ))
+ {
+ /* losing ground - boost priority until finished */
+ logf("pcmrec: boost priority");
+ prio = thread_set_priority(NULL, thread_get_priority(NULL)-1);
}
- /* sync file */
- fsync(wav_file);
+ 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;
- cpu_boost_id(false, CPUBOOSTID_PCMRECORD);
+ if (rec_fdata.chunk->flags & CHUNKF_START_FILE)
+ {
+ pcmrec_start_file();
+ if (--flush_num == 0)
+ i = num_ready; /* stop on next loop - must write this
+ chunk if it has data */
+ }
- logf("done");
- }
-}
+ pcmrec_write_chunk();
-/* Abort dma transfer */
-static void pcmrec_dma_stop(void)
-{
- DCR1 = 0;
+ if (rec_fdata.chunk->flags & CHUNKF_END_FILE)
+ pcmrec_end_file();
- error_count++;
+ INC_ENC_INDEX(enc_rd_index);
- DSR1 = 1; /* Clear interrupt */
- IPR |= (1<<15); /* Clear pending interrupt request */
+ if (is_error)
+ break;
- logf("dma1 stopped");
-}
+ if (prio == -1)
+ {
+ num = enc_wr_index - enc_rd_index;
+ if (num < 0)
+ num += enc_num_chunks;
+ }
-static void pcmrec_dma_start(void)
-{
- DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
- SAR1 = (unsigned long)&PDIR2; /* Source address */
- BCR1 = CHUNK_SIZE; /* Bytes to transfer */
+ /* no yielding, the file apis called in the codecs do that */
+ } /* end for */
- /* Start the DMA transfer.. */
-#ifdef HAVE_SPDIF_IN
- INTERRUPTCLEAR = 0x03c00000;
-#endif
+ /* sync file */
+ if (rec_fdata.rec_file >= 0)
+ fsync(rec_fdata.rec_file);
+
+ cpu_boost_id(false, CPUBOOSTID_PCMRECORD);
- /* 16Byte transfers prevents from sporadic errors during cpu_boost() */
- DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_DSIZE(3) | DMA_START;
+ if (prio != -1)
+ {
+ /* return to original priority */
+ logf("pcmrec: unboost priority");
+ thread_set_priority(NULL, prio);
+ }
- logf("dma1 started");
-}
+ last_flush_tick = current_tick; /* save tick when we left */
+ logf("done");
+} /* pcmrec_flush */
-/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */
-void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
-void DMA1(void)
+/**
+ * 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 void pcmrec_new_stream(const char *filename, /* next file name */
+ unsigned long flags, /* CHUNKF_* flags */
+ int pre_index) /* index for prerecorded data */
{
- int res = DSR1;
+ logf("pcmrec_new_stream");
- DSR1 = 1; /* Clear interrupt */
+ struct enc_buffer_event_data data;
+ bool (*fnq_add_fn)(const char *) = NULL;
+ struct enc_chunk_hdr *start = NULL;
- if (res & 0x70)
+ int get_chunk_index(struct enc_chunk_hdr *chunk)
{
- DCR1 = 0; /* Stop DMA transfer */
- error_count++;
-
- logf("dma1 err: 0x%x", res);
-
- DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
- BCR1 = CHUNK_SIZE;
- DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
+ return ((char *)chunk - (char *)enc_buffer) / enc_chunk_size;
+ }
- /* Flush recorded data to disk and stop recording */
- queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
- }
-#ifdef HAVE_SPDIF_IN
- else if ((rec_source == AUDIO_SRC_SPDIF) &&
- (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */
+ struct enc_chunk_hdr * get_prev_chunk(int index)
{
- INTERRUPTCLEAR = 0x03c00000;
- error_count++;
+ DEC_ENC_INDEX(index);
+ return GET_ENC_CHUNK(index);
+ }
- logf("spdif err");
+ data.pre_chunk = NULL;
+ data.chunk = GET_ENC_CHUNK(enc_wr_index);
- DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
- BCR1 = CHUNK_SIZE;
- }
-#endif
- else
+ /* end chunk */
+ if (flags & CHUNKF_END_FILE)
{
- long peak_l, peak_r;
- long *ptr, j;
-
- ptr = GET_CHUNK(write_pos);
+ data.chunk->flags &= CHUNKF_START_FILE | CHUNKF_END_FILE;
- if (!is_paused) /* advance write position */
- write_pos = (write_pos + CHUNK_SIZE) & CHUNK_MASK;
+ 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 = get_prev_chunk(enc_wr_index);
- DAR1 = (unsigned long)GET_CHUNK(write_pos); /* Destination address */
- BCR1 = CHUNK_SIZE;
+ if (last->flags & CHUNKF_END_FILE)
+ {
+ /* end already processed and marked - can't end twice */
+ logf("file end again");
+ flags &= ~CHUNKF_END_FILE;
+ }
+ }
+ }
- peak_l = peak_r = 0;
+ /* start chunk */
+ if (flags & CHUNKF_START_FILE)
+ {
+ bool pre = flags & CHUNKF_PRERECORD;
- /* only peak every 4th sample */
- for (j=0; j<CHUNK_SIZE/4; j+=4)
+ if (pre)
{
- 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
+ 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;
}
- peak_left = (int)(peak_l >> 16);
- peak_right = (int)(peak_r >> 16);
+ /* 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;
+ }
}
- IPR |= (1<<15); /* Clear pending interrupt request */
-}
-
-/* Create WAVE file and write header */
-/* Sets returns 0 if success, -1 on failure */
-static int start_wave(void)
-{
- wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC);
+ data.flags = flags;
+ enc_events_callback(ENC_REC_NEW_STREAM, &data);
- if (wav_file < 0)
+ if (flags & CHUNKF_END_FILE)
{
- wav_file = -1;
- logf("rec: create failed: %d", wav_file);
- is_error = true;
- return -1;
+ int i = get_chunk_index(data.chunk);
+ get_prev_chunk(i)->flags |= CHUNKF_END_FILE;
}
-
- /* add main file header (enc_head_size=0 for encoders without) */
- if (enc_head_size != write(wav_file, enc_head_buffer, enc_head_size))
+
+ if (start)
{
- close(wav_file);
- wav_file = -1;
- logf("rec: write failed");
- is_error = true;
- return -1;
- }
+ if (!(flags & CHUNKF_PRERECORD))
+ {
+ /* get stats on data added to start - sort of a prerecord operation */
+ int i = get_chunk_index(data.chunk);
+ struct enc_chunk_hdr *chunk = data.chunk;
- return 0;
-}
+ logf("start data: %d %d", i, enc_wr_index);
-/* Update header and set correct length values */
-static void close_wave(void)
-{
- unsigned char head[100]; /* assume maximum 100 bytes for file header */
- int size_read;
+ num_rec_bytes = 0;
+ num_rec_samples = 0;
- if (wav_file != -1)
- {
- /* 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));
+ 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;
- enc_set_header_callback(head, size_read, num_pcm_samples, true);
- lseek(wav_file, 0, SEEK_SET);
- write(wav_file, head, size_read);
+ /* flush one file out if full and adding */
+ if (fnq_add_fn == pcmrec_fnq_add_filename && pcmrec_fnq_is_full())
+ {
+ logf("fnq full: flushing 1");
+ pcmrec_flush(1);
}
- close(wav_file);
- wav_file = -1;
+
+ fnq_add_fn(filename);
}
-}
+} /* pcmrec_new_stream */
-static void pcmrec_start(void)
+/** event handlers for pcmrec thread */
+
+/* PCMREC_INIT */
+static void pcmrec_init(void)
{
- long max_pre_chunks, pre_ticks, max_pre_ticks;
+ rec_fdata.rec_file = -1;
+
+ /* pcm FIFO */
+ dma_lock = true;
+ pcm_rd_pos = 0;
+ dma_wr_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;
+ accum_rec_bytes = 0;
+ accum_pcm_samples = 0;
+
+ pcm_thread_unsignal_event(PCMREC_CLOSE);
+ is_recording = false;
+ is_paused = false;
+ is_stopping = false;
+ is_error = false;
+
+ pcm_buffer = audio_get_recording_buffer(&rec_buffer_size);
+ /* Line align pcm_buffer 2^4=16 bytes */
+ pcm_buffer = (unsigned char *)ALIGN_UP_P2((unsigned)pcm_buffer, 4);
+ enc_buffer = pcm_buffer + ALIGN_UP_P2(PCM_NUM_CHUNKS*PCM_CHUNK_SIZE +
+ PCM_MAX_FEED_SIZE, 2);
+
+ pcm_init_recording();
+ pcm_thread_signal_event(PCMREC_INIT);
+} /* pcmrec_init */
+
+/* PCMREC_CLOSE */
+static void pcmrec_close(void)
+{
+ dma_lock = true;
+ pcm_close_recording();
+ pcm_thread_unsignal_event(PCMREC_INIT);
+ pcm_thread_signal_event(PCMREC_CLOSE);
+} /* pcmrec_close */
+
+/* PCMREC_START */
+static void pcmrec_start(const char *filename)
+{
+ unsigned long pre_sample_ticks;
+ int rd_start;
- logf("pcmrec_start");
+ logf("pcmrec_start: %s", filename);
if (is_recording)
{
logf("already recording");
- record_done = true;
- return;
+ goto already_recording;
}
- if (wav_file != -1)
- close_wave();
+ /* reset stats */
+ num_rec_bytes = 0;
+ num_rec_samples = 0;
+ accum_rec_bytes = 0;
+ accum_pcm_samples = 0;
+ spinup_time = -1;
+
+ rd_start = enc_wr_index;
+ pre_sample_ticks = 0;
- if (start_wave() != 0)
+ if (pre_record_ticks)
{
- /* failed to create the file */
- record_done = true;
- return;
- }
+ int i;
- /* 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;
-
- /* 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 +
+ /* 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;
+
+ /* Get exact measure of recorded data as number of samples aren't
+ nescessarily going to be the max for each chunk */
+ for (i = rd_start; 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;
- record_start_time = current_tick - pre_ticks;
+ chunk_sample_ticks = chunk->num_pcm*HZ;
- num_rec_bytes = enc_num_chunks * CHUNK_SIZE;
- num_file_bytes = 0;
- num_pcm_samples = 0;
- pause_start_time = 0;
+ rd_start = i;
+ pre_sample_ticks += chunk_sample_ticks;
+ num_rec_bytes += chunk->enc_size;
+ num_rec_samples += chunk->num_pcm;
+
+ /* stop here if enough already */
+ if (pre_sample_ticks >= pre_record_sample_ticks)
+ break;
+ }
+
+ accum_rec_bytes = num_rec_bytes;
+ accum_pcm_samples = num_rec_samples;
+ }
+
+ enc_rd_index = rd_start;
+
+ /* filename queue should be empty */
+ if (!pcmrec_fnq_is_empty())
+ {
+ logf("fnq: not empty!");
+ pcmrec_fnq_set_empty();
+ }
+ dma_lock = false;
is_paused = false;
is_recording = true;
- record_done = true;
-}
+ pcmrec_new_stream(filename,
+ CHUNKF_START_FILE |
+ (pre_sample_ticks > 0 ? CHUNKF_PRERECORD : 0),
+ enc_rd_index);
+
+already_recording:
+ pcm_thread_signal_event(PCMREC_START);
+ logf("pcmrec_start done");
+} /* pcmrec_start */
+
+/* PCMREC_STOP */
static void pcmrec_stop(void)
{
logf("pcmrec_stop");
- if (is_recording)
+ if (!is_recording)
{
- /* wait for encoding finish */
- is_paused = true;
- while(!wav_queue_empty)
- sleep_thread(1);
-
- is_recording = false;
-
- /* Flush buffers to file */
- pcmrec_callback(true);
- close_wave();
+ logf("not recording");
+ goto not_recording_or_stopping;
+ }
+ else if (is_stopping)
+ {
+ logf("already stopping");
+ goto not_recording_or_stopping;
}
- is_paused = false;
- stop_done = true;
+ is_stopping = true;
+ dma_lock = true; /* lock dma write position */
+ queue_post(&pcmrec_queue, PCMREC_FINISH_STOP, NULL);
+not_recording_or_stopping:
+ pcm_thread_signal_event(PCMREC_STOP);
logf("pcmrec_stop done");
-}
+} /* pcmrec_stop */
-static void pcmrec_new_file(void)
+/* PCMREC_FINISH_STOP */
+static void pcmrec_finish_stop(void)
{
- logf("pcmrec_new_file");
+ logf("pcmrec_finish_stop");
- if (!is_recording)
+ if (!is_stopping)
{
- logf("not recording");
- new_file_done = true;
- return;
+ logf("not stopping");
+ goto not_stopping;
}
- /* Since pcmrec_callback() blocks until the data has been written,
- here is a good approximation when recording to the new file starts
- */
- record_start_time = current_tick;
+ /* flush all available data first to avoid overflow while waiting
+ for encoding to finish */
+ pcmrec_flush(-1);
- if (is_paused)
- pause_start_time = record_start_time;
+ /* wait for encoder to finish remaining data */
+ if (!is_error)
+ {
+ while (!wav_queue_empty)
+ yield();
+ }
- /* Flush what we got in buffers to file */
- pcmrec_callback(true);
+ /* end stream at last data */
+ pcmrec_new_stream(NULL, CHUNKF_END_FILE, 0);
- close_wave();
-
- num_rec_bytes = 0;
- num_file_bytes = 0;
- num_pcm_samples = 0;
+ /* flush anything else encoder added */
+ pcmrec_flush(-1);
+
+ /* 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;
- /* start the new file */
- if (start_wave() != 0)
+ /* filename queue should be empty */
+ if (!pcmrec_fnq_is_empty())
{
- logf("new_file failed");
- pcmrec_stop();
+ logf("fnq: not empty!");
+ pcmrec_fnq_set_empty();
}
- new_file_done = true;
- logf("pcmrec_new_file done");
-}
+ /* be absolutely sure the file is closed */
+ if (is_error)
+ pcmrec_close_file(&rec_fdata.rec_file);
+ rec_fdata.rec_file = -1;
+
+ is_recording = false;
+ is_paused = false;
+ is_stopping = false;
+ dma_lock = pre_record_ticks == 0;
+
+not_stopping:
+ logf("pcmrec_finish_stop done");
+} /* pcmrec_finish_stop */
+/* PCMREC_PAUSE */
static void pcmrec_pause(void)
{
logf("pcmrec_pause");
if (!is_recording)
{
- logf("pause: not recording");
- pause_done = true;
- return;
+ logf("not recording");
+ goto not_recording_or_paused;
+ }
+ else if (is_paused)
+ {
+ logf("already paused");
+ goto not_recording_or_paused;
}
- pause_start_time = current_tick;
+ dma_lock = true; /* fix DMA write pointer at current position */
is_paused = true;
- pause_done = true;
+not_recording_or_paused:
+ pcm_thread_signal_event(PCMREC_PAUSE);
logf("pcmrec_pause done");
-}
-
+} /* pcmrec_pause */
+/* PCMREC_RESUME */
static void pcmrec_resume(void)
{
logf("pcmrec_resume");
- if (!is_paused)
+ if (!is_recording)
{
- logf("resume: not paused");
- resume_done = true;
- return;
+ logf("not recording");
+ goto not_recording_or_not_paused;
+ }
+ else if (!is_paused)
+ {
+ logf("not paused");
+ goto not_recording_or_not_paused;
}
is_paused = false;
is_recording = true;
+ dma_lock = false;
- /* Compensate for the time we have been paused */
- if (pause_start_time)
- {
- record_start_time += current_tick - pause_start_time;
- pause_start_time = 0;
- }
-
- resume_done = true;
+not_recording_or_not_paused:
+ pcm_thread_signal_event(PCMREC_RESUME);
logf("pcmrec_resume done");
-}
+} /* pcmrec_resume */
-/**
- * audio_init_recording calls this function using PCMREC_INIT
- *
- */
-static void pcmrec_init(void)
+/* PCMREC_NEW_FILE */
+static void pcmrec_new_file(const char *filename)
{
- wav_file = -1;
- read_pos = 0;
- write_pos = 0;
- enc_wr_index = 0;
- enc_rd_index = 0;
+ logf("pcmrec_new_file: %s", filename);
- avrg_bit_rate = 0;
- curr_bit_rate = 0;
- curr_chunk_cnt = 0;
-
- peak_left = 0;
- peak_right = 0;
+ if (!is_recording)
+ {
+ logf("not recording");
+ goto not_recording;
+ }
num_rec_bytes = 0;
- num_file_bytes = 0;
- num_pcm_samples = 0;
- record_start_time = 0;
- pause_start_time = 0;
-
- close_done = false;
- is_recording = false;
- is_paused = false;
- is_error = false;
-
- 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 */
-
- 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 */
-
-#ifdef HAVE_SPDIF_IN
- PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */
-#endif
- pcmrec_dma_start();
-
- init_done = 1;
-}
+ num_rec_samples = 0;
-static void pcmrec_close(void)
-{
- DMAROUTE = (DMAROUTE & 0xffff00ff);
- ICR7 = 0x00; /* Disable interrupt */
- IMR |= (1<<15); /* bit 15 is DMA1 */
+ pcmrec_new_stream(filename,
+ CHUNKF_START_FILE | CHUNKF_END_FILE,
+ 0);
- pcmrec_dma_stop();
-
- /* Reset PDIR2 data flow */
- DATAINCONTROL = 0x200;
- close_done = true;
- init_done = false;
-}
+not_recording:
+ pcm_thread_signal_event(PCMREC_NEW_FILE);
+ logf("pcmrec_new_file done");
+} /* pcmrec_new_file */
+static void pcmrec_thread(void) __attribute__((noreturn));
static void pcmrec_thread(void)
{
struct event ev;
logf("thread pcmrec start");
- error_count = 0;
-
while(1)
{
- queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 4);
+ if (is_recording)
+ {
+ /* Poll periodically to flush data */
+ queue_wait_w_tmo(&pcmrec_queue, &ev, HZ/5);
+
+ if (ev.id == SYS_TIMEOUT)
+ {
+ pcmrec_flush(0); /* flush if getting full */
+ continue;
+ }
+ }
+ else
+ {
+ /* Not doing anything - sit and wait for commands */
+ queue_wait(&pcmrec_queue, &ev);
+ }
switch (ev.id)
{
@@ -992,13 +1385,17 @@ static void pcmrec_thread(void)
break;
case PCMREC_START:
- pcmrec_start();
+ pcmrec_start((const char *)ev.data);
break;
case PCMREC_STOP:
pcmrec_stop();
break;
+ case PCMREC_FINISH_STOP:
+ pcmrec_finish_stop();
+ break;
+
case PCMREC_PAUSE:
pcmrec_pause();
break;
@@ -1008,11 +1405,11 @@ static void pcmrec_thread(void)
break;
case PCMREC_NEW_FILE:
- pcmrec_new_file();
+ pcmrec_new_file((const char *)ev.data);
break;
- case SYS_TIMEOUT:
- pcmrec_callback(false);
+ case PCMREC_FLUSH_NUM:
+ pcmrec_flush((unsigned)ev.data);
break;
case SYS_USB_CONNECTED:
@@ -1023,140 +1420,267 @@ static void pcmrec_thread(void)
usb_wait_for_disconnect(&pcmrec_queue);
}
break;
- }
- }
+ } /* end switch */
+ } /* end while */
+} /* pcmrec_thread */
- logf("thread pcmrec done");
-}
+/****************************************************************************/
+/* */
+/* following functions will be called by the encoder codec */
+/* */
+/****************************************************************************/
-/* Select VINL & VINR source: 0=Line-in, 1=FM Radio */
-void pcm_rec_mux(int source)
+/* pass the encoder settings to the encoder */
+void enc_get_inputs(struct enc_inputs *inputs)
{
-#ifdef IRIVER_H300_SERIES
- if(source == 0)
- and_l(~0x40000000, &GPIO_OUT); /* Line In */
- else
- or_l(0x40000000, &GPIO_OUT); /* FM radio */
+ inputs->sample_rate = sample_rate;
+ inputs->num_channels = num_channels;
+ inputs->config = &enc_config;
+} /* enc_get_inputs */
- or_l(0x40000000, &GPIO_ENABLE);
- or_l(0x40000000, &GPIO_FUNCTION);
-#elif defined(IRIVER_H100_SERIES)
- if(source == 0)
- and_l(~0x00800000, &GPIO_OUT); /* Line In */
- else
- or_l(0x00800000, &GPIO_OUT); /* FM radio */
+/* set the encoder dimensions (called by encoder codec at initialization and
+ termination) */
+void enc_set_parameters(struct enc_parameters *params)
+{
+ size_t bufsize, resbytes;
- or_l(0x00800000, &GPIO_ENABLE);
- or_l(0x00800000, &GPIO_FUNCTION);
+ logf("enc_set_parameters");
-#elif defined(IAUDIO_X5)
- if(source == 0)
- or_l((1<<29), &GPIO_OUT); /* Line In */
- else
- and_l(~(1<<29), &GPIO_OUT); /* FM radio */
+ if (!params)
+ {
+ logf("reset");
+ /* Encoder is terminating */
+ memset(&enc_config, 0, sizeof (enc_config));
+ enc_sample_rate = 0;
+ return;
+ }
+
+ enc_sample_rate = params->enc_sample_rate;
+ logf("enc sampr:%d", enc_sample_rate);
+
+ pcm_rd_pos = dma_wr_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_data_size = enc_chunk_size - ENC_CHUNK_HDR_SIZE;
+ enc_events_callback = params->events_callback;
+
+ logf("chunk size:%d", 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:%d", resbytes);
+
+ bufsize = rec_buffer_size - (enc_buffer - pcm_buffer) -
+ resbytes - FNQ_MIN_NUM_PATHS*MAX_PATH;
+
+ enc_num_chunks = bufsize / enc_chunk_size;
+ logf("num chunks:%d", enc_num_chunks);
- or_l((1<<29), &GPIO_ENABLE);
- or_l((1<<29), &GPIO_FUNCTION);
+ /* get real amount used by encoder chunks */
+ bufsize = enc_num_chunks*enc_chunk_size;
+ logf("enc size:%d", bufsize);
+
+ /* panic boost thread priority at 1 second remaining */
+ panic_threshold = enc_num_chunks -
+ (4*sample_rate + (enc_chunk_size-1)) / enc_chunk_size;
+ if (panic_threshold < 0)
+ panic_threshold = 0;
+
+ logf("panic thr:%d", panic_threshold);
+
+ /** 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;
+ }
- /* iAudio x5 */
+ /* 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 = ALIGN_DOWN(fnq_size, MAX_PATH);
+ logf("fnq files: %d", fnq_size / MAX_PATH);
+
+#if 0
+ logf("ab :%08X", (unsigned long)audiobuf);
+ logf("pcm:%08X", (unsigned long)pcm_buffer);
+ logf("enc:%08X", (unsigned long)enc_buffer);
+ logf("res:%08X", (unsigned long)params->reserve_buffer);
+ logf("fnq:%08X", (unsigned long)fn_queue);
+ logf("end:%08X", (unsigned long)fn_queue + fnq_size);
+ logf("abe:%08X", (unsigned long)audiobufend);
#endif
-}
+ /* init all chunk headers and reset indexes */
+ enc_rd_index = 0;
+ for (enc_wr_index = enc_num_chunks; enc_wr_index > 0; )
+ GET_ENC_CHUNK(--enc_wr_index)->flags = 0;
-/****************************************************************************/
-/* */
-/* following functions will be called by the encoder codec */
-/* */
-/****************************************************************************/
+ logf("enc_set_parameters done");
+} /* enc_set_parameters */
-/* pass the encoder buffer pointer/size, mono/stereo, quality to the encoder */
-void enc_get_inputs(int *buffer_size, int *channels, int *quality)
+/* return encoder chunk at current write position */
+struct enc_chunk_hdr * enc_get_chunk(void)
{
- *buffer_size = enc_buffer_size;
- *channels = enc_channels;
- *quality = enc_quality;
-}
+ struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
+ chunk->flags &= CHUNKF_START_FILE;
-/* 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 */
-}
+ if (!is_recording)
+ chunk->flags |= CHUNKF_PRERECORD;
-/* allocate encoder chunk */
-unsigned int *enc_alloc_chunk(void)
-{
- return (unsigned int*)(enc_buffer + enc_wr_index * enc_chunk_size);
-}
+ return chunk;
+} /* enc_get_chunk */
-/* free previously allocated encoder chunk */
-void enc_free_chunk(void)
+/* releases the current chunk into the available chunks */
+void enc_finish_chunk(void)
{
- unsigned long *enc_chunk;
+ struct enc_chunk_hdr *chunk = GET_ENC_CHUNK(enc_wr_index);
- 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;
+ /* encoder may have set error flag or written too much data */
+ if ((long)chunk->flags < 0 || chunk->enc_size > enc_data_size)
+ {
+ is_error = true;
- /* advance enc_wr_index to the next chunk */
- enc_wr_index = (enc_wr_index + 1) % enc_num_chunks;
+#ifdef ROCKBOX_HAS_LOGF
+ if (chunk->enc_size > enc_data_size)
+ {
+ /* illegal to scribble over next chunk */
+ logf("finish chk ovf: %d>%d", chunk->enc_size, enc_data_size);
+ }
+ else
+ {
+ /* encoder set error flag */
+ logf("finish chk enc error");
+ }
+#endif
+ }
+
+ /* advance enc_wr_index to the next encoder chunk */
+ INC_ENC_INDEX(enc_wr_index);
- /* buffer full: advance enc_rd_index (for prerecording purpose) */
- if (enc_rd_index == enc_wr_index)
+ if (enc_rd_index != enc_wr_index)
{
- enc_rd_index = (enc_rd_index + 1) % enc_num_chunks;
+ num_rec_bytes += chunk->enc_size;
+ accum_rec_bytes += chunk->enc_size;
+ num_rec_samples += chunk->num_pcm;
+ accum_pcm_samples += chunk->num_pcm;
}
-}
+ else if (is_recording) /* buffer full */
+ {
+ /* keep current position */
+ logf("enc_buffer ovf");
+ DEC_ENC_INDEX(enc_wr_index);
+ }
+ else
+ {
+ /* advance enc_rd_index for prerecording */
+ INC_ENC_INDEX(enc_rd_index);
+ }
+} /* enc_finish_chunk */
-/* checks near empty state on wav input buffer */
-int enc_wavbuf_near_empty(void)
+/* checks near empty state on pcm input buffer */
+int enc_pcm_buf_near_empty(void)
{
/* less than 1sec raw data? => unboost encoder */
- if (((write_pos - read_pos) & CHUNK_MASK) < 44100*4)
- return 1;
- else
- return 0;
-}
+ size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK;
+ return avail < (sample_rate << 2) ? 1 : 0;
+} /* enc_pcm_buf_near_empty */
/* passes a pointer to next chunk of unprocessed wav data */
-char *enc_get_wav_data(int size)
+/* TODO: this really should give the actual size returned */
+unsigned char * enc_get_pcm_data(size_t size)
{
- char *ptr;
- int avail;
+ size_t avail = (dma_wr_pos - pcm_rd_pos) & PCM_CHUNK_MASK;
/* limit the requested pcm data size */
- if(size > MAX_FEED_SIZE)
- size = MAX_FEED_SIZE;
-
- avail = (write_pos - read_pos) & CHUNK_MASK;
+ if (size > PCM_MAX_FEED_SIZE)
+ size = PCM_MAX_FEED_SIZE;
if (avail >= size)
{
- ptr = rec_buffer + read_pos;
- read_pos = (read_pos + size) & CHUNK_MASK;
+ unsigned char *ptr = pcm_buffer + pcm_rd_pos;
+ pcm_rd_pos = (pcm_rd_pos + size) & PCM_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);
+ if ((size_t)pcm_rd_pos < size)
+ memcpy(pcm_buffer + PCM_NUM_CHUNKS*PCM_CHUNK_SIZE,
+ pcm_buffer, pcm_rd_pos);
wav_queue_empty = false;
return ptr;
}
+ /* not enough data available - encoder should idle */
wav_queue_empty = true;
return NULL;
-}
+} /* enc_get_pcm_data */
+
+/* puts some pcm data back in the queue */
+size_t enc_unget_pcm_data(size_t size)
+{
+ /* can't let DMA advance write position when doing this */
+ int level = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+ if (pcm_rd_pos != dma_wr_pos)
+ {
+ /* disallow backing up into current DMA write chunk */
+ size_t old_avail = (pcm_rd_pos - dma_wr_pos - PCM_CHUNK_SIZE)
+ & PCM_CHUNK_MASK;
+
+ /* limit size to amount of old data remaining */
+ if (size > old_avail)
+ size = old_avail;
+
+ pcm_rd_pos = (pcm_rd_pos - size) & PCM_CHUNK_MASK;
+ }
+
+ set_irq_level(level);
+
+ return size;
+} /* enc_unget_pcm_data */
+
+/** Low level pcm recording apis **/
+
+/****************************************************************************
+ * Functions that do not require targeted implementation but only a targeted
+ * interface
+ */
+void pcm_record_data(pcm_more_callback_type more_ready,
+ unsigned char *start, size_t size)
+{
+ pcm_callback_more_ready = more_ready;
+
+ if (!(start && size))
+ {
+ size = 0;
+ if (more_ready)
+ more_ready(&start, &size);
+ }
+
+ if (start && size)
+ pcm_rec_dma_start(start, size);
+} /* pcm_record_data */
+
+void pcm_stop_recording(void)
+{
+ if (pcm_recording)
+ pcm_rec_dma_stop();
+} /* pcm_stop_recording */
diff --git a/firmware/system.c b/firmware/system.c
index 242d84d..96d5f96 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -390,8 +390,7 @@ int system_memory_guard(int newmode)
(void)newmode;
return 0;
}
-#elif defined(CPU_COLDFIRE)
-/* system code is in target tree for all coldfire targets */
+
#elif CONFIG_CPU == SH7034
#include "led.h"
#include "system.h"
diff --git a/firmware/target/coldfire/iaudio/x5/system-x5.c b/firmware/target/coldfire/iaudio/x5/system-x5.c
index 6be6d25..30a4f6e 100644
--- a/firmware/target/coldfire/iaudio/x5/system-x5.c
+++ b/firmware/target/coldfire/iaudio/x5/system-x5.c
@@ -42,7 +42,7 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
RECALC_DELAYS(CPUFREQ_MAX);
- PLLCR = 0x13442045;
+ PLLCR = 0x03042045 | (PLLCR & 0x70C00000);
CSCR0 = 0x00001180; /* Flash: 4 wait states */
CSCR1 = 0x00000980; /* LCD: 2 wait states */
while(!(PLLCR & 0x80000000)) {}; /* Wait until the PLL has locked.
@@ -60,7 +60,7 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
RECALC_DELAYS(CPUFREQ_NORMAL);
- PLLCR = 0x16430045;
+ PLLCR = 0x06030045 | (PLLCR & 0x70C00000);
CSCR0 = 0x00000580; /* Flash: 1 wait state */
CSCR1 = 0x00000180; /* LCD: 0 wait states */
while(!(PLLCR & 0x80000000)) {}; /* Wait until the PLL has locked.
@@ -77,7 +77,8 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true);
RECALC_DELAYS(CPUFREQ_DEFAULT);
- PLLCR = 0x10400200; /* Power down PLL, but keep CLSEL and CRSEL */
+ /* Power down PLL, but keep CLSEL and CRSEL */
+ PLLCR = 0x00000200 | (PLLCR & 0x70C00000);
CSCR0 = 0x00000180; /* Flash: 0 wait states */
CSCR1 = 0x00000180; /* LCD: 0 wait states */
DCR = (0x8000 | DEFAULT_REFRESH_TIMER); /* Refresh timer */
diff --git a/firmware/target/coldfire/iriver/system-iriver.c b/firmware/target/coldfire/iriver/system-iriver.c
index 3517788..43ba4ee 100644
--- a/firmware/target/coldfire/iriver/system-iriver.c
+++ b/firmware/target/coldfire/iriver/system-iriver.c
@@ -81,7 +81,7 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
RECALC_DELAYS(CPUFREQ_MAX);
- PLLCR = 0x11c56005;
+ PLLCR = 0x01056005 | (PLLCR & 0x70c00000);
CSCR0 = 0x00001180; /* Flash: 4 wait states */
CSCR1 = 0x00001580; /* LCD: 5 wait states */
#if CONFIG_USBOTG == USBOTG_ISP1362
@@ -108,7 +108,7 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
RECALC_DELAYS(CPUFREQ_NORMAL);
- PLLCR = 0x13c5e005;
+ PLLCR = 0x0305e005 | (PLLCR & 0x70c00000);
CSCR0 = 0x00000580; /* Flash: 1 wait state */
CSCR1 = 0x00000180; /* LCD: 0 wait states */
#if CONFIG_USBOTG == USBOTG_ISP1362
@@ -134,7 +134,8 @@ void set_cpu_frequency(long frequency)
PLLCR &= ~1; /* Bypass mode */
timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true);
RECALC_DELAYS(CPUFREQ_DEFAULT);
- PLLCR = 0x10c00200; /* Power down PLL, but keep CLSEL and CRSEL */
+ /* Power down PLL, but keep CLSEL and CRSEL */
+ PLLCR = 0x00000200 | (PLLCR & 0x70c00000);
CSCR0 = 0x00000180; /* Flash: 0 wait states */
CSCR1 = 0x00000180; /* LCD: 0 wait states */
#if CONFIG_USBOTG == USBOTG_ISP1362
diff --git a/firmware/target/coldfire/system-coldfire.c b/firmware/target/coldfire/system-coldfire.c
index 66e4feb..2fc8149 100644
--- a/firmware/target/coldfire/system-coldfire.c
+++ b/firmware/target/coldfire/system-coldfire.c
@@ -310,3 +310,10 @@ int system_memory_guard(int newmode)
return oldmode;
}
+
+/* allow setting of audio clock related bits */
+void coldfire_set_pllcr_audio_bits(long bits)
+{
+ PLLCR = (PLLCR & ~0x70c00000) | (bits & 0x70c00000);
+}
+
diff --git a/firmware/target/coldfire/system-target.h b/firmware/target/coldfire/system-target.h
index 0385211..24e3fb87 100644
--- a/firmware/target/coldfire/system-target.h
+++ b/firmware/target/coldfire/system-target.h
@@ -110,6 +110,28 @@ static inline unsigned long swap32(unsigned long value)
return value;
}
+static inline unsigned long swap_odd_even32(unsigned long value)
+{
+ /*
+ result[31..24],[15.. 8] = value[23..16],[ 7.. 0]
+ result[23..16],[ 7.. 0] = value[31..24],[15.. 8]
+ */
+ unsigned long mask = 0x00FF00FF;
+
+ asm ( /* val = ABCD */
+ "and.l %[val],%[mask] \n" /* mask = .B.D */
+ "eor.l %[mask],%[val] \n" /* val = A.C. */
+ "lsl.l #8,%[mask] \n" /* mask = B.D. */
+ "lsr.l #8,%[val] \n" /* val = .A.C */
+ "or.l %[mask],%[val] \n" /* val = BADC */
+ : /* outputs */
+ [val] "+d"(value),
+ [mask]"+d"(mask)
+ );
+
+ return value;
+}
+
static inline void invalidate_icache(void)
{
asm volatile ("move.l #0x01000000,%d0\n"
@@ -118,6 +140,13 @@ static inline void invalidate_icache(void)
"movec.l %d0,%cacr");
}
+#ifdef IAUDIO_X5
+#define DEFAULT_PLLCR_AUDIO_BITS 0x10400000
+#else
+#define DEFAULT_PLLCR_AUDIO_BITS 0x10c00000
+#endif
+void coldfire_set_pllcr_audio_bits(long bits);
+
/* 11.2896 MHz */
#define CPUFREQ_DEFAULT_MULT 1
#define CPUFREQ_DEFAULT (CPUFREQ_DEFAULT_MULT * CPU_FREQ)
diff --git a/firmware/thread.c b/firmware/thread.c
index 6a94a52..4094877 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -711,6 +711,14 @@ int thread_set_priority(struct thread_entry *thread, int priority)
return old_priority;
}
+
+int thread_get_priority(struct thread_entry *thread)
+{
+ if (thread == NULL)
+ thread = cores[CURRENT_CORE].running;
+
+ return thread->priority;
+}
#endif
void init_threads(void)