diff options
Diffstat (limited to 'firmware')
| -rw-r--r-- | firmware/SOURCES | 26 | ||||
| -rw-r--r-- | firmware/drivers/tlv320.c | 45 | ||||
| -rw-r--r-- | firmware/drivers/uda1380.c | 84 | ||||
| -rw-r--r-- | firmware/export/audio.h | 106 | ||||
| -rw-r--r-- | firmware/export/config-h100.h | 6 | ||||
| -rw-r--r-- | firmware/export/config-h120.h | 6 | ||||
| -rw-r--r-- | firmware/export/config-h300.h | 7 | ||||
| -rw-r--r-- | firmware/export/config-iaudiox5.h | 6 | ||||
| -rw-r--r-- | firmware/export/id3.h | 91 | ||||
| -rw-r--r-- | firmware/export/pcm_playback.h | 16 | ||||
| -rw-r--r-- | firmware/export/pcm_record.h | 46 | ||||
| -rw-r--r-- | firmware/export/system.h | 81 | ||||
| -rw-r--r-- | firmware/export/thread.h | 3 | ||||
| -rw-r--r-- | firmware/export/tlv320.h | 10 | ||||
| -rw-r--r-- | firmware/export/uda1380.h | 13 | ||||
| -rw-r--r-- | firmware/id3.c | 138 | ||||
| -rw-r--r-- | firmware/mpeg.c | 20 | ||||
| -rw-r--r-- | firmware/pcm_playback.c | 700 | ||||
| -rw-r--r-- | firmware/pcm_record.c | 2002 | ||||
| -rw-r--r-- | firmware/system.c | 3 | ||||
| -rw-r--r-- | firmware/target/coldfire/iaudio/x5/system-x5.c | 7 | ||||
| -rw-r--r-- | firmware/target/coldfire/iriver/system-iriver.c | 7 | ||||
| -rw-r--r-- | firmware/target/coldfire/system-coldfire.c | 7 | ||||
| -rw-r--r-- | firmware/target/coldfire/system-target.h | 29 | ||||
| -rw-r--r-- | firmware/thread.c | 8 |
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) |