From e6e5496535a6fa45ee5cb63fb80886514ae72231 Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 12 Nov 2005 04:00:56 +0000 Subject: iRiver: Initial support for wav-recording in recording menu. Supports mic/line-in (and radio), monitor mode, time-splitting (and byte-splitting), pause/resume etc. Things todo: Prerecording, peakmeter (should be simple), frequency other than 44.1 kHz, etc.. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7818 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 3 - apps/debug_menu.c | 4 - apps/lang/english.lang | 22 +- apps/main.c | 2 +- apps/recorder/radio.c | 32 +- apps/recorder/recording.c | 114 +++++-- apps/settings.c | 23 +- apps/settings.h | 5 + apps/sound_menu.c | 34 +- firmware/drivers/uda1380.c | 33 +- firmware/export/audio.h | 23 ++ firmware/export/config-h120.h | 2 +- firmware/export/mpeg.h | 13 +- firmware/export/pcm_record.h | 32 +- firmware/export/sound.h | 6 +- firmware/export/uda1380.h | 2 +- firmware/mpeg.c | 51 +-- firmware/pcm_record.c | 717 +++++++++++++++++++++++++++--------------- firmware/sound.c | 30 ++ 19 files changed, 770 insertions(+), 378 deletions(-) diff --git a/apps/SOURCES b/apps/SOURCES index 8b2184baca..6b2b68236b 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -65,7 +65,4 @@ playback.c metadata.c codecs.c dsp.c -#ifndef SIMULATOR -pcm_recording.c -#endif #endif diff --git a/apps/debug_menu.c b/apps/debug_menu.c index cd395cc9f3..dd6a2d76a5 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -58,9 +58,6 @@ #include "ata_mmc.h" #endif #include "logfdisp.h" -#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) -extern bool pcm_rec_screen(void); -#endif #if CONFIG_CODEC == SWCODEC #include "pcmbuf.h" #include "pcm_playback.h" @@ -1781,7 +1778,6 @@ bool debug_menu(void) { "CPU frequency", dbg_cpufreq }, #endif #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) - { "PCM recording", pcm_rec_screen }, { "S/PDIF analyzer", dbg_spdif }, #endif #if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE) diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 9d547173c7..9123066627 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -939,13 +939,13 @@ new: id: LANG_RECORDING_LEFT desc: in the recording screen -eng: "Left" +eng: "Gain Left" voice: "" new: id: LANG_RECORDING_RIGHT desc: in the recording screen -eng: "Right" +eng: "Gain Right" voice: "" new: @@ -3352,3 +3352,21 @@ desc: in crossfade settings menu eng: "Fade out mode" voice: "Fade out mode" new: + +id: LANG_RECORDING_ADC_RIGHT +desc: in the recording settings +eng: "ADC Gain Right" +voice: "ADC Gain Right" +new: + +id: LANG_RECORDING_ADC_LEFT +desc: in the recording settings +eng: "ADC Gain Left" +voice: "ADC Gain Left" +new: + +id: LANG_RECORDING_MONITOR +desc: in the recording settings +eng: "Monitor Mode" +voice: "Monitor Mode" +new: diff --git a/apps/main.c b/apps/main.c index 296b3fc351..ab802d5e24 100644 --- a/apps/main.c +++ b/apps/main.c @@ -323,7 +323,7 @@ void init(void) sound_settings_apply(); #endif #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) - pcm_init_recording(); + pcm_rec_init(); #endif talk_init(); /* runtime database has to be initialized after audio_init() */ diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c index 443bb3192f..6be6d04e72 100644 --- a/apps/recorder/radio.c +++ b/apps/recorder/radio.c @@ -226,7 +226,7 @@ bool radio_screen(void) audio_stop(); #if CONFIG_CODEC != SWCODEC - mpeg_init_recording(); + audio_init_recording(); sound_settings_apply(); @@ -238,23 +238,24 @@ bool radio_screen(void) if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, 1, /* Line In */ global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); - mpeg_set_recording_gain(sound_default(SOUND_LEFT_GAIN), - sound_default(SOUND_RIGHT_GAIN), false); + audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN), + sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN); #else uda1380_enable_recording(false); - uda1380_set_recvol(0, 0, 10); + uda1380_set_recvol(10, 10, AUDIO_GAIN_LINEIN); uda1380_set_monitor(true); /* Set the input multiplexer to FM */ - pcmrec_set_mux(1); + pcm_rec_mux(1); #endif #endif @@ -345,14 +346,14 @@ bool radio_screen(void) #ifndef SIMULATOR if(audio_status() == AUDIO_STATUS_RECORD) { - mpeg_new_file(rec_create_filename(buf)); + audio_new_file(rec_create_filename(buf)); update_screen = true; } else { have_recorded = true; talk_buffer_steal(); /* we use the mp3 buffer */ - mpeg_record(rec_create_filename(buf)); + audio_record(rec_create_filename(buf)); update_screen = true; } #endif @@ -517,7 +518,7 @@ bool radio_screen(void) #ifndef SIMULATOR #if CONFIG_CODEC != SWCODEC - seconds = mpeg_recorded_time() / HZ; + seconds = audio_recorded_time() / HZ; #endif #endif if(update_screen || seconds > last_seconds) @@ -609,8 +610,8 @@ bool radio_screen(void) { #if CONFIG_CODEC != SWCODEC /* Enable the Left and right A/D Converter */ - mpeg_set_recording_gain(sound_default(SOUND_LEFT_GAIN), - sound_default(SOUND_RIGHT_GAIN), false); + audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN), + sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN); mas_codec_writereg(6, 0x4000); #endif radio_set_status(FMRADIO_POWERED); /* leave it powered */ @@ -619,7 +620,7 @@ bool radio_screen(void) { radio_stop(); #if CONFIG_CODEC == SWCODEC - pcmrec_set_mux(0); /* Line In */ + pcm_rec_mux(0); /* Line In */ #endif } @@ -913,12 +914,13 @@ static bool fm_recording_settings(void) if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, 1, /* Line In */ global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); } return ret; } diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index b70e7b64ed..ebd92178c5 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c @@ -28,6 +28,13 @@ #include "led.h" #include "mpeg.h" #include "audio.h" +#if CONFIG_CODEC == SWCODEC +#include "pcm_record.h" +#endif +#ifdef HAVE_UDA1380 +#include "uda1380.h" +#endif + #include "mp3_playback.h" #include "mas.h" #include "button.h" @@ -79,9 +86,12 @@ #elif CONFIG_KEYPAD == IRIVER_H100_PAD #define REC_STOPEXIT BUTTON_OFF -#define REC_RECPAUSE BUTTON_ON +#define REC_RECPAUSE BUTTON_REC #define REC_INC BUTTON_RIGHT #define REC_DEC BUTTON_LEFT +#define REC_NEXT BUTTON_DOWN +#define REC_PREV BUTTON_UP +#define REC_SETTINGS BUTTON_MODE #elif CONFIG_KEYPAD == GMINI100_PAD #define REC_STOPEXIT BUTTON_OFF @@ -102,6 +112,12 @@ bool f3_rec_screen(void); #define MAX_SOURCE SOURCE_LINE #endif +#if CONFIG_CODEC == SWCODEC +#define REC_FILE_ENDING ".wav" +#else +#define REC_FILE_ENDING ".mp3" +#endif + #define MAX_FILE_SIZE 0x7FF00000 /* 2 GB - 1 MB */ const char* const freq_str[6] = @@ -118,13 +134,18 @@ static void set_gain(void) { if(global_settings.rec_source == SOURCE_MIC) { - mpeg_set_recording_gain(global_settings.rec_mic_gain, 0, true); + audio_set_recording_gain(global_settings.rec_mic_gain, 0, AUDIO_GAIN_MIC); } else { - mpeg_set_recording_gain(global_settings.rec_left_gain, - global_settings.rec_right_gain, false); + audio_set_recording_gain(global_settings.rec_left_gain, + global_settings.rec_right_gain, AUDIO_GAIN_LINEIN); } +#ifdef HAVE_UDA1380 + audio_set_recording_gain(global_settings.rec_adc_left_gain, + global_settings.rec_adc_right_gain, + AUDIO_GAIN_ADC); +#endif } static const char* const fmtstr[] = @@ -176,9 +197,9 @@ char *rec_create_filename(char *buffer) strncpy(buffer, rec_base_directory, MAX_PATH); #ifdef HAVE_RTC - create_datetime_filename(buffer, buffer, "R", ".mp3"); + create_datetime_filename(buffer, buffer, "R", REC_FILE_ENDING); #else - create_numbered_filename(buffer, buffer, "rec_", ".mp3", 4); + create_numbered_filename(buffer, buffer, "rec_", REC_FILE_ENDING, 4); #endif return buffer; } @@ -227,7 +248,7 @@ static void trigger_listener(int trigger_status) if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD) { talk_buffer_steal(); /* we use the mp3 buffer */ - mpeg_record(rec_create_filename(path_buffer)); + audio_record(rec_create_filename(path_buffer)); /* give control to mpeg thread so that it can start recording */ yield(); yield(); yield(); @@ -236,7 +257,7 @@ static void trigger_listener(int trigger_status) /* if we're already recording this is a retrigger */ else { - mpeg_new_file(rec_create_filename(path_buffer)); + audio_new_file(rec_create_filename(path_buffer)); /* tell recording_screen to reset the time */ last_seconds = 0; } @@ -273,6 +294,7 @@ bool recording_screen(void) char path_buffer[MAX_PATH]; bool been_in_usb_mode = false; int last_audio_stat = -1; + int audio_stat; #if CONFIG_LED == LED_REAL bool led_state = false; int led_countdown = 2; @@ -289,7 +311,7 @@ bool recording_screen(void) #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) ata_set_led_enabled(false); #endif - mpeg_init_recording(); + audio_init_recording(); sound_set_volume(global_settings.volume); @@ -298,15 +320,20 @@ bool recording_screen(void) peak_meter_enabled = true; +#if CONFIG_CODEC == SWCODEC + audio_stop(); +#endif + if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, global_settings.rec_source, global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); set_gain(); @@ -321,7 +348,12 @@ bool recording_screen(void) while(!done) { - int audio_stat = audio_status(); +#if CONFIG_CODEC == SWCODEC + audio_stat = pcm_rec_status(); +#else + audio_stat = audio_status(); +#endif + #if CONFIG_LED == LED_REAL /* @@ -390,7 +422,7 @@ bool recording_screen(void) if(audio_stat & AUDIO_STATUS_RECORD) { - audio_stop(); + audio_stop_recording(); } else { @@ -416,7 +448,7 @@ bool recording_screen(void) /* manual recording */ have_recorded = true; talk_buffer_steal(); /* we use the mp3 buffer */ - mpeg_record(rec_create_filename(path_buffer)); + audio_record(rec_create_filename(path_buffer)); last_seconds = 0; if (global_settings.talk_menu) { /* no voice possible here, but a beep */ @@ -438,7 +470,7 @@ bool recording_screen(void) { if(audio_stat & AUDIO_STATUS_PAUSE) { - mpeg_resume_recording(); + audio_resume_recording(); if (global_settings.talk_menu) { /* no voice possible here, but a beep */ audio_beep(HZ/4); /* short beep on resume */ @@ -446,7 +478,7 @@ bool recording_screen(void) } else { - mpeg_pause_recording(); + audio_pause_recording(); } } update_countdown = 1; /* Update immediately */ @@ -535,7 +567,7 @@ bool recording_screen(void) sound_min(SOUND_RIGHT_GAIN)) global_settings.rec_right_gain--; break; - } + } set_gain(); update_countdown = 1; /* Update immediately */ break; @@ -557,12 +589,13 @@ bool recording_screen(void) if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, global_settings.rec_source, global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); set_gain(); update_countdown = 1; /* Update immediately */ @@ -596,7 +629,7 @@ bool recording_screen(void) case REC_F3: if(audio_stat & AUDIO_STATUS_RECORD) { - mpeg_new_file(rec_create_filename(path_buffer)); + audio_new_file(rec_create_filename(path_buffer)); last_seconds = 0; } else @@ -638,8 +671,8 @@ bool recording_screen(void) lcd_setfont(FONT_SYSFIXED); - seconds = mpeg_recorded_time() / HZ; - + seconds = audio_recorded_time() / HZ; + update_countdown--; if(update_countdown == 0 || seconds > last_seconds) { @@ -660,7 +693,7 @@ bool recording_screen(void) lcd_puts(0, 0, buf); dseconds = rec_timesplit_seconds(); - num_recorded_bytes = mpeg_num_recorded_bytes(); + num_recorded_bytes = audio_num_recorded_bytes(); if(audio_stat & AUDIO_STATUS_PRERECORD) { @@ -699,7 +732,7 @@ bool recording_screen(void) ((global_settings.rec_timesplit && (seconds >= dseconds)) || (num_recorded_bytes >= MAX_FILE_SIZE))) { - mpeg_new_file(rec_create_filename(path_buffer)); + audio_new_file(rec_create_filename(path_buffer)); update_countdown = 1; last_seconds = 0; } @@ -784,7 +817,13 @@ bool recording_screen(void) } } - if(audio_status() & AUDIO_STATUS_ERROR) + +#if CONFIG_CODEC == SWCODEC + audio_stat = pcm_rec_status(); +#else + audio_stat = audio_status(); +#endif + if (audio_stat & AUDIO_STATUS_ERROR) { splash(0, true, str(LANG_DISK_FULL)); status_draw(true); @@ -799,7 +838,12 @@ bool recording_screen(void) } } +#if CONFIG_CODEC == SWCODEC + audio_stop_recording(); + audio_close_recording(); +#else audio_init_playback(); +#endif /* make sure the trigger is really turned off */ peak_meter_trigger(false); @@ -924,12 +968,13 @@ bool f2_rec_screen(void) if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, global_settings.rec_source, global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); set_gain(); @@ -1018,12 +1063,14 @@ bool f3_rec_screen(void) if (global_settings.rec_prerecord_time) talk_buffer_steal(); /* will use the mp3 buffer */ - mpeg_set_recording_options(global_settings.rec_frequency, + audio_set_recording_options(global_settings.rec_frequency, global_settings.rec_quality, global_settings.rec_source, global_settings.rec_channels, global_settings.rec_editable, - global_settings.rec_prerecord_time); + global_settings.rec_prerecord_time, + global_settings.rec_monitor); + set_gain(); @@ -1034,4 +1081,13 @@ bool f3_rec_screen(void) } #endif /* #ifdef REC_F3 */ +#if CONFIG_CODEC == SWCODEC +void audio_beep(int duration) +{ + /* dummy */ + (void)duration; +} +#endif + + #endif /* HAVE_RECORDING */ diff --git a/apps/settings.c b/apps/settings.c index b573d72c97..d44fec1f0b 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -85,7 +85,7 @@ const char rec_base_directory[] = REC_BASE_DIR; #include "dsp.h" #endif -#define CONFIG_BLOCK_VERSION 29 +#define CONFIG_BLOCK_VERSION 30 #define CONFIG_BLOCK_SIZE 512 #define RTC_BLOCK_SIZE 44 @@ -407,6 +407,7 @@ static const struct bit_entry hd_bits[] = #ifdef HAVE_RECORDING {1, S_O(rec_startup), false, "rec screen on startup", off_on }, + {1, S_O(rec_monitor), true, "monitor recording", off_on }, /* values for the trigger */ {8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL}, @@ -450,6 +451,26 @@ static const struct bit_entry hd_bits[] = {22, S_O(dircache_size), 0, NULL, NULL }, #endif +#if defined(HAVE_UDA1380) + /* recording settings for iriver */ + {4, S_O(rec_timesplit), 0, "rec timesplit", /* 0...15 */ + "off,00:05,00:10,00:15,00:30,01:00,01:14,01:20,02:00,04:00,06:00,08:00,10:00,12:00,18:00,24:00" }, + {1, S_O(rec_channels), 0, "rec channels", "stereo,mono" }, + {4, S_O(rec_mic_gain), 4, "rec mic gain", NULL }, + {1, S_O(rec_source), 0 /* 0=mic */, "rec source", "mic,line" }, + {3, S_O(rec_frequency), 0, /* 0=44.1kHz */ + "rec frequency", "44,48,32,22,24,16" }, + {4, S_O(rec_left_gain), 2, /* 0dB */ + "rec left gain", NULL }, /* 0...15 */ + {4, S_O(rec_right_gain), 2, /* 0dB */ + "rec right gain", NULL }, /* 0...15 */ + {5, S_O(rec_prerecord_time), 0, "prerecording time", NULL }, /* 0...30 */ + {1, S_O(rec_directory), 0, /* rec_base_directory */ + "rec directory", REC_BASE_DIR ",current" }, + {8|SIGNED, S_O(rec_adc_left_gain), 0, /* 0dB */ "adc left gain", NULL }, /* -128...48 */ + {8|SIGNED, S_O(rec_adc_right_gain), 0, /* 0dB */ "adc right gain", NULL }, /* -128...48 */ +#endif + /* If values are just added to the end, no need to bump the version. */ /* new stuff to be added at the end */ diff --git a/apps/settings.h b/apps/settings.h index 63349c646e..18c566b980 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -176,7 +176,12 @@ struct user_settings int rec_mic_gain; /* 0-15 */ int rec_left_gain; /* 0-15 */ int rec_right_gain; /* 0-15 */ +#ifdef HAVE_UDA1380 + int rec_adc_left_gain; /* -128 .. 48 */ + int rec_adc_right_gain; /* -128 .. 48 */ +#endif bool rec_editable; /* true means that the bit reservoir is off */ + bool rec_monitor; /* true means that one can listen to what is being recorded */ /* note: timesplit setting is not saved */ int rec_timesplit; /* 0 = off, diff --git a/apps/sound_menu.c b/apps/sound_menu.c index 766372471e..c429920143 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c @@ -228,6 +228,28 @@ static bool receditable(void) &global_settings.rec_editable); } +static bool recmonitor(void) +{ + return set_bool(str(LANG_RECORDING_MONITOR), + &global_settings.rec_monitor); +} + +#ifdef HAVE_UDA1380 +static bool recadcleft(void) +{ + return set_sound(str(LANG_RECORDING_ADC_LEFT), + &global_settings.rec_adc_left_gain, + SOUND_ADC_LEFT_GAIN); +} + +static bool recadcright(void) +{ + return set_sound(str(LANG_RECORDING_ADC_RIGHT), + &global_settings.rec_adc_right_gain, + SOUND_ADC_RIGHT_GAIN); +} +#endif + static bool rectimesplit(void) { static const struct opt_items names[] = { @@ -740,7 +762,7 @@ bool recording_menu(bool no_source) { int m; int i = 0; - struct menu_item items[10]; + struct menu_item items[13]; bool result; items[i].desc = ID2P(LANG_RECORDING_QUALITY); @@ -753,6 +775,16 @@ bool recording_menu(bool no_source) } items[i].desc = ID2P(LANG_RECORDING_CHANNELS); items[i++].function = recchannels; + +#ifdef HAVE_UDA1380 + items[i].desc = ID2P(LANG_RECORDING_ADC_LEFT); + items[i++].function = recadcleft; + items[i].desc = ID2P(LANG_RECORDING_ADC_RIGHT); + items[i++].function = recadcright; +#endif + + items[i].desc = ID2P(LANG_RECORDING_MONITOR); + items[i++].function = recmonitor; items[i].desc = ID2P(LANG_RECORDING_EDITABLE); items[i++].function = receditable; items[i].desc = ID2P(LANG_RECORD_TIMESPLIT); diff --git a/firmware/drivers/uda1380.c b/firmware/drivers/uda1380.c index e89f9bb684..6a5bd078c0 100644 --- a/firmware/drivers/uda1380.c +++ b/firmware/drivers/uda1380.c @@ -28,6 +28,7 @@ #include "string.h" #include "file.h" #include "buffer.h" +#include "audio.h" #include "i2c-coldfire.h" #include "uda1380.h" @@ -204,7 +205,7 @@ void uda1380_close(void) * sound samples over the I2S bus, which is connected * to the processor's IIS1 interface. * - * source_mic: true=record from microphone, false=record from line-in + * source_mic: true=record from microphone, false=record from line-in (or radio) */ void uda1380_enable_recording(bool source_mic) { @@ -246,17 +247,29 @@ void uda1380_disable_recording(void) /** * Set recording gain and volume * - * mic_gain : range 0 .. 15 -> 0 .. 30 dB gain - * linein_gain : range 0 .. 15 -> 0 .. 24 dB gain - * - * adc_volume : range -127 .. 48 -> -63 .. 24 dB gain - * note that 0 -> 0 dB gain.. + * type: params: ranges: + * AUDIO_GAIN_MIC left 0 .. 15 -> 0 .. 30 dB gain + * AUDIO_GAIN_LINEIN left & right 0 .. 8 -> 0 .. 24 dB gain + * AUDIO_GAIN_ADC left & right -128 .. 48 -> -64 .. 24 dB gain + * + * Note: For all types the value 0 gives 0 dB gain. */ -void uda1380_set_recvol(int mic_gain, int linein_gain, int adc_volume) +void uda1380_set_recvol(int left, int right, int type) { - uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(adc_volume) | DEC_VOLR(adc_volume)); - uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & ~PGA_GAIN_MASK) | PGA_GAINL(linein_gain) | PGA_GAINR(linein_gain)); - uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & ~VGA_GAIN_MASK) | VGA_GAIN(mic_gain)); + switch (type) + { + case AUDIO_GAIN_MIC: + uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & ~VGA_GAIN_MASK) | VGA_GAIN(left)); + break; + + case AUDIO_GAIN_LINEIN: + uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] & ~PGA_GAIN_MASK) | PGA_GAINL(left) | PGA_GAINR(right)); + break; + + case AUDIO_GAIN_ADC: + uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left) | DEC_VOLR(right)); + break; + } } diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 67ed052f2b..ab6f41c81f 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -33,6 +33,11 @@ #define AUDIOERR_DISK_FULL 1 +#define AUDIO_GAIN_LINEIN 0 +#define AUDIO_GAIN_MIC 1 +#define AUDIO_GAIN_ADC 2 /* for UDA1380 */ + + struct audio_debug { int audiobuflen; @@ -79,6 +84,24 @@ int audio_get_file_pos(void); void audio_beep(int duration); void audio_init_playback(void); +/* audio recording functions */ +void audio_init_recording(void); +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(int frequency, int quality, + int source, int channel_mode, + bool editable, int prerecord_time, + bool monitor); +void audio_set_recording_gain(int left, int right, int type); +unsigned long audio_recorded_time(void); +unsigned long audio_num_recorded_bytes(void); + + + /***********************************************************************/ /* audio event handling */ diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h index 72bc770bde..653dbc0bee 100644 --- a/firmware/export/config-h120.h +++ b/firmware/export/config-h120.h @@ -7,7 +7,7 @@ #define MODEL_NUMBER 0 /* define this if you have recording possibility */ -/*#define HAVE_RECORDING 1*/ +#define HAVE_RECORDING 1 /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP 1 diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h index a7c9cac321..d62ac14ade 100644 --- a/firmware/export/mpeg.h +++ b/firmware/export/mpeg.h @@ -44,20 +44,11 @@ #define MPEG_RESERVED_HEADER_SPACE (4096 + 576) #if (CONFIG_CODEC == MAS3587F) || defined(SIMULATOR) -void mpeg_init_recording(void); -void mpeg_record(const char *filename); -void mpeg_new_file(const char *filename); -void mpeg_set_recording_options(int frequency, int quality, - int source, int channel_mode, - bool editable, int prerecord_time); -void mpeg_set_recording_gain(int left, int right, bool use_mic); + #if CONFIG_TUNER & S1A0903X01 int mpeg_get_mas_pllfreq(void); #endif -unsigned long mpeg_recorded_time(void); -unsigned long mpeg_num_recorded_bytes(void); -void mpeg_pause_recording(void); -void mpeg_resume_recording(void); + #endif unsigned long mpeg_get_last_header(void); diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h index af73108d86..304a67f577 100644 --- a/firmware/export/pcm_record.h +++ b/firmware/export/pcm_record.h @@ -17,37 +17,13 @@ * ****************************************************************************/ -/* - * Function names are taken from apps/recorder/recording.c to - * make the integration later easier.. - * - */ - #ifndef PCM_RECORD_H #define PCM_RECORD_H -unsigned long pcm_status(void); - -void pcm_init_recording(void); - -void pcm_open_recording(void); -void pcm_close_recording(void); - - -void pcm_set_recording_options(int source, bool enable_waveform); -void pcm_set_recording_gain(int gain, int volume); - -void pcm_record(const char *filename); -void pcm_stop_recording(void); - -//void pcm_new_file(const char *filename); - - -unsigned long pcm_recorded_time(void); -unsigned long pcm_num_recorded_bytes(void); -void pcm_pause_recording(void); -void pcm_resume_recording(void); +unsigned long pcm_rec_status(void); +void pcm_rec_init(void); +void pcm_rec_mux(int source); -void pcmrec_set_mux(int source); +/* audio.h contains audio recording functions */ #endif diff --git a/firmware/export/sound.h b/firmware/export/sound.h index 9907462dc8..2c002063db 100644 --- a/firmware/export/sound.h +++ b/firmware/export/sound.h @@ -36,11 +36,15 @@ enum { SOUND_MDB_ENABLE, SOUND_SUPERBASS, #endif -#if CONFIG_CODEC == MAS3587F +#if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) SOUND_LEFT_GAIN, SOUND_RIGHT_GAIN, SOUND_MIC_GAIN, #endif +#if defined(HAVE_UDA1380) + SOUND_ADC_LEFT_GAIN, + SOUND_ADC_RIGHT_GAIN, +#endif }; enum { diff --git a/firmware/export/uda1380.h b/firmware/export/uda1380.h index 3bef5439b5..90a6d44e04 100644 --- a/firmware/export/uda1380.h +++ b/firmware/export/uda1380.h @@ -32,7 +32,7 @@ extern void uda1380_set_nsorder(int order); extern void uda1380_enable_recording(bool source_mic); extern void uda1380_disable_recording(void); -extern void uda1380_set_recvol(int mic_gain, int linein_gain, int adc_volume); +extern void uda1380_set_recvol(int left, int right, int type); extern void uda1380_set_monitor(int enable); #define UDA1380_ADDR 0x30 diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 022a321602..caeb3b96b6 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -2115,7 +2115,7 @@ void audio_init_playback(void) /**************************************************************************** * Recording functions ***************************************************************************/ -void mpeg_init_recording(void) +void audio_init_recording(void) { init_recording_done = false; queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL); @@ -2224,7 +2224,7 @@ static void init_recording(void) call mpeg_set_recording_options(). */ } -void mpeg_record(const char *filename) +void audio_record(const char *filename) { mpeg_errno = 0; @@ -2234,12 +2234,12 @@ void mpeg_record(const char *filename) queue_post(&mpeg_queue, MPEG_RECORD, NULL); } -void mpeg_pause_recording(void) +void audio_pause_recording(void) { queue_post(&mpeg_queue, MPEG_PAUSE_RECORDING, NULL); } -void mpeg_resume_recording(void) +void audio_resume_recording(void) { queue_post(&mpeg_queue, MPEG_RESUME_RECORDING, NULL); } @@ -2435,9 +2435,10 @@ static void stop_recording(void) resume_recording(); } -void mpeg_set_recording_options(int frequency, int quality, +void audio_set_recording_options(int frequency, int quality, int source, int channel_mode, - bool editable, int prerecord_time) + bool editable, int prerecord_time, + bool monitor) { bool is_mpeg1; @@ -2461,7 +2462,7 @@ void mpeg_set_recording_options(int frequency, int quality, DEBUGF("mas_writemem(MAS_BANK_D0, SOFT_MUTE, %x)\n", shadow_soft_mute); - shadow_io_control_main = ((1 << 10) | /* Monitoring ON */ + shadow_io_control_main = ((monitor?(1 << 10):0) | /* Monitoring ON */ ((source < 2)?1:2) << 8) | /* Input select */ (1 << 5) | /* SDO strobe invert */ ((is_mpeg1?0:1) << 3) | @@ -2497,13 +2498,13 @@ void mpeg_set_recording_options(int frequency, int quality, } /* If use_mic is true, the left gain is used */ -void mpeg_set_recording_gain(int left, int right, bool use_mic) +void audio_set_recording_gain(int left, int right, int type) { /* Enable both left and right A/D */ shadow_codec_reg0 = (left << 12) | (right << 8) | (left << 4) | - (use_mic?0x0008:0) | /* Connect left A/D to mic */ + (type==AUDIO_GAIN_MIC?0x0008:0) | /* Connect left A/D to mic */ 0x0007; mas_codec_writereg(0x0, shadow_codec_reg0); } @@ -2539,7 +2540,7 @@ void audio_beep(int duration) while (current_tick - starttick < duration); } -void mpeg_new_file(const char *filename) +void audio_new_file(const char *filename) { mpeg_errno = 0; @@ -2549,7 +2550,7 @@ void mpeg_new_file(const char *filename) queue_post(&mpeg_queue, MPEG_NEW_FILE, NULL); } -unsigned long mpeg_recorded_time(void) +unsigned long audio_recorded_time(void) { if(is_prerecording) return prerecord_count * HZ; @@ -2565,7 +2566,7 @@ unsigned long mpeg_recorded_time(void) return 0; } -unsigned long mpeg_num_recorded_bytes(void) +unsigned long audio_num_recorded_bytes(void) { int num_bytes; int index; @@ -2599,7 +2600,7 @@ void audio_init_playback(void) { /* a dummy */ } -unsigned long mpeg_recorded_time(void) +unsigned long audio_recorded_time(void) { /* a dummy */ return 0; @@ -2609,42 +2610,42 @@ void audio_beep(int duration) /* a dummy */ (void)duration; } -void mpeg_pause_recording(void) +void audio_pause_recording(void) { /* a dummy */ } -void mpeg_resume_recording(void) +void audio_resume_recording(void) { /* a dummy */ } -unsigned long mpeg_num_recorded_bytes(void) +unsigned long audio_num_recorded_bytes(void) { /* a dummy */ return 0; } -void mpeg_record(const char *filename) +void audio_record(const char *filename) { /* a dummy */ (void)filename; } -void mpeg_new_file(const char *filename) +void audio_new_file(const char *filename) { /* a dummy */ (void)filename; } -void mpeg_set_recording_gain(int left, int right, bool use_mic) +void audio_set_recording_gain(int left, int right, int type) { /* a dummy */ (void)left; (void)right; - (void)use_mic; + (void)type; } -void mpeg_init_recording(void) +void audio_init_recording(void) { /* a dummy */ } -void mpeg_set_recording_options(int frequency, int quality, +void audio_set_recording_options(int frequency, int quality, int source, int channel_mode, bool editable, int prerecord_time) { @@ -2710,6 +2711,12 @@ void audio_stop(void) #endif /* SIMULATOR */ } +/* dummy */ +void audio_stop_recording(void) +{ + audio_stop(); +} + void audio_pause(void) { #ifndef SIMULATOR diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c index 8b46a09ed3..b167d6a562 100644 --- a/firmware/pcm_record.c +++ b/firmware/pcm_record.c @@ -30,11 +30,7 @@ #include "cpu.h" #include "i2c.h" -#if defined(HAVE_UDA1380) #include "uda1380.h" -#elif defined(HAVE_TLV320) -#include "tlv320.h" -#endif #include "system.h" #include "usb.h" @@ -56,47 +52,56 @@ static volatile bool is_recording; /* We are recording */ static volatile bool is_stopping; /* Are we going to stop */ static volatile bool is_paused; /* We have paused */ +static volatile bool is_error; /* An error has occured */ -static volatile int num_rec_bytes; +static volatile unsigned long num_rec_bytes; /* Num bytes recorded */ +static volatile unsigned long num_file_bytes; /* Num bytes written to current file */ static volatile int int_count; /* Number of DMA completed interrupts */ static volatile int error_count; /* Number of DMA errors */ static unsigned long record_start_time; /* Value of current_tick when recording was started */ static unsigned long pause_start_time; /* Value of current_tick when pause was started */ -static int rec_gain, rec_volume; static bool show_waveform; -static int init_done = 0; + static int wav_file; static char recording_filename[MAX_PATH]; +static bool init_done, close_done, record_done, stop_done, pause_done, resume_done, new_file_done; + /***************************************************************************/ /* - Some estimates: - 44100 HZ * 4 = 176400 bytes/s - Refresh LCD 10 HZ = 176400 / 10 = 17640 bytes ~=~ 1024*16 bytes + Some estimates: + Normal recording rate: 44100 HZ * 4 = 176 KB/s + Total buffer size: 32 MB / 176 KB/s = 181s before writing to disk + CHUNK_SIZE: 65536 + Chunks/s: 176 KB / 65536 = ~3 chunks / s + + WRITE_THRESHOLD: 30 + - Should gives us < 10s to start writing to disk before we run out + of buffer space.. - If NUM_BUFFERS is 80 we can hold ~8 sec of data in memory - ALL_BUFFER_SIZE will be 1024*16 * 80 = 1310720 bytes */ -#define NUM_BUFFERS 80 -#define EACH_BUFFER_SIZE (1024*16) /* Multiple of 4. Use small value to get responsive waveform */ -#define ALL_BUFFERS_SIZE (NUM_BUFFERS * EACH_BUFFER_SIZE) +#define CHUNK_SIZE 65536 /* Multiple of 4 */ +#define WRITE_THRESHOLD 30 /* Write when this many chunks (or less) until buffer full */ + +#define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)]) -#define WRITE_THRESHOLD 40 /* Minimum number of buffers before write to file */ +static unsigned char *rec_buffer; /* Circular recording buffer */ +static int num_chunks; /* Number of chunks available in rec_buffer */ -static unsigned char *rec_buffers[NUM_BUFFERS]; /* - Overrun occures when DMA needs to write a new buffer and write_index == read_index - Solution to this is to optimize pcmrec_callback, use cpu_boost somewhere or increase - the total buffer size (or WRITE_THRESHOLD) + Overrun occures when DMA needs to write a new chunk and write_index == read_index + Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk + more often. */ -static int write_index; /* Which buffer the DMA is currently recording */ -static int read_index; /* The oldest buffer that the pcmrec_callback has not read */ +static volatile int write_index; /* Current chunk the DMA is writing to */ +static volatile int read_index; /* Oldest chunk that is not written to disk */ +static volatile int read2_index; /* Latest chunk that has not been converted to little endian */ /***************************************************************************/ @@ -105,10 +110,13 @@ static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(lon static const char pcmrec_thread_name[] = "pcmrec"; static void pcmrec_thread(void); +static void pcmrec_dma_start(void); +static void pcmrec_dma_stop(void); /* Event IDs */ -#define PCMREC_OPEN 1 /* Enable recording */ -#define PCMREC_CLOSE 2 /* Disable recording */ +#define PCMREC_INIT 1 /* Enable recording */ +#define PCMREC_CLOSE 2 + #define PCMREC_START 3 /* Start a new recording */ #define PCMREC_STOP 4 /* Stop the current recording */ #define PCMREC_PAUSE 10 @@ -122,71 +130,58 @@ static void pcmrec_thread(void); /* Functions that are not executing in the pcmrec_thread first */ /*******************************************************************/ -void pcm_init_recording(void) +/* Creates pcmrec_thread */ +void pcm_rec_init(void) { - int_count = 0; - error_count = 0; - - show_waveform = 0; - is_recording = 0; - is_stopping = 0; - num_rec_bytes = 0; - wav_file = -1; - read_index = 0; - write_index = 0; - queue_init(&pcmrec_queue); create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name); } -void pcm_open_recording(void) -{ - init_done = 0; - - logf("pcm_open_rec"); - - queue_post(&pcmrec_queue, PCMREC_OPEN, 0); - while (init_done) - { - sleep(HZ >> 8); - } - - logf("pcm_open_rec done"); +/* Initializes recording: + * - Set up the UDA1380 for recording + * - Prepare for DMA transfers + */ + +void audio_init_recording(void) +{ + init_done = false; + queue_post(&pcmrec_queue, PCMREC_INIT, 0); + + while(!init_done) + sleep_thread(); + wake_up_thread(); } -void pcm_close_recording(void) +void audio_close_recording(void) { - /* todo: synchronize completion with pcmrec thread */ + close_done = false; queue_post(&pcmrec_queue, PCMREC_CLOSE, 0); + + while(!close_done) + sleep_thread(); + wake_up_thread(); } - - -unsigned long pcm_status(void) +unsigned long pcm_rec_status(void) { unsigned long ret = 0; if (is_recording) ret |= AUDIO_STATUS_RECORD; + if (is_paused) + ret |= AUDIO_STATUS_PAUSE; + if (is_error) + ret |= AUDIO_STATUS_ERROR; return ret; } - - -void pcm_new_file(const char *filename) -{ - /* todo */ - filename = filename; - -} - -unsigned long pcm_recorded_time(void) +unsigned long audio_recorded_time(void) { if (is_recording) { - if(is_paused) + if (is_paused) return pause_start_time - record_start_time; else return current_tick - record_start_time; @@ -195,92 +190,168 @@ unsigned long pcm_recorded_time(void) return 0; } -unsigned long pcm_num_recorded_bytes(void) +unsigned long audio_num_recorded_bytes(void) { - if (is_recording) - { return num_rec_bytes; - } - else - return 0; -} - -void pcm_pause_recording(void) -{ - /* todo */ -} -void pcm_resume_recording(void) -{ - /* todo */ + return 0; } /** * Sets the audio source * - * Side effect: This functions starts feeding the CPU with audio data over the I2S bus + * This functions starts feeding the CPU with audio data over the I2S bus * - * @param source 0=line-in, 1=mic + * @param source 0=mic, 1=line-in, (todo: 2=spdif) */ -void pcm_set_recording_options(int source, bool enable_waveform) +void audio_set_recording_options(int frequency, int quality, + int source, int channel_mode, + bool editable, int prerecord_time, + bool monitor) { -#if defined(HAVE_UDA1380) - uda1380_enable_recording(source); -#elif defined(HAVE_TLV320) - tlv320_enable_recording(source); -#endif - show_waveform = enable_waveform; + /* TODO: */ + (void)frequency; + (void)quality; + (void)channel_mode; + (void)editable; + (void)prerecord_time; + + //logf("pcmrec: src=%d", source); + + switch (source) + { + /* mic */ + case 0: + uda1380_enable_recording(true); + break; + + /* line-in */ + case 1: + uda1380_enable_recording(false); + break; + } + + uda1380_set_monitor(monitor); } /** + * Note that microphone is mono, only left value is used + * See uda1380_set_recvol() for exact ranges. * - * @param gain line-in and microphone gain (0-15) - * @param volume ADC volume (0-255) + * @param type 0=line-in (radio), 1=mic, 2=ADC + * */ -void pcm_set_recording_gain(int gain, int volume) +void audio_set_recording_gain(int left, int right, int type) { - rec_gain = gain; - rec_volume = volume; - - queue_post(&pcmrec_queue, PCMREC_SET_GAIN, 0); + //logf("rcmrec: t=%d l=%d r=%d", type, left, right); + uda1380_set_recvol(left, right, type); } + /** * Start recording * - * Use pcm_set_recording_options before calling record + * Use audio_set_recording_options first to select recording options */ -void pcm_record(const char *filename) +void audio_record(const char *filename) { + if (is_recording) + { + logf("record while recording"); + return; + } + strncpy(recording_filename, filename, MAX_PATH - 1); recording_filename[MAX_PATH - 1] = 0; + record_done = false; queue_post(&pcmrec_queue, PCMREC_START, 0); + + while(!record_done) + sleep_thread(); + wake_up_thread(); +} + + +void audio_new_file(const char *filename) +{ + logf("pcm_new_file"); + + new_file_done = false; + + strncpy(recording_filename, filename, MAX_PATH - 1); + recording_filename[MAX_PATH - 1] = 0; + + queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0); + + while(!new_file_done) + sleep_thread(); + wake_up_thread(); + + logf("pcm_new_file done"); } /** * */ -void pcm_stop_recording(void) +void audio_stop_recording(void) { - if (is_recording) - is_stopping = 1; + if (!is_recording) + return; + logf("pcm_stop"); + + stop_done = false; queue_post(&pcmrec_queue, PCMREC_STOP, 0); - logf("pcm_stop_recording"); + while(!stop_done) + sleep_thread(); + wake_up_thread(); + + logf("pcm_stop done"); +} - while (is_stopping) +void audio_pause_recording(void) +{ + if (!is_recording) + { + logf("pause when not recording"); + return; + } + if (is_paused) { - sleep(HZ >> 4); + logf("pause when paused"); + return; } + + pause_done = false; + queue_post(&pcmrec_queue, PCMREC_PAUSE, 0); - logf("pcm_stop_recording done"); + while(!pause_done) + sleep_thread(); + wake_up_thread(); } +void audio_resume_recording(void) +{ + if (!is_paused) + { + logf("resume when not paused"); + return; + } + + resume_done = false; + queue_post(&pcmrec_queue, PCMREC_RESUME, 0); + + while(!resume_done) + sleep_thread(); + wake_up_thread(); +} + + /***************************************************************************/ /* Functions that executes in the context of pcmrec_thread */ @@ -288,100 +359,116 @@ void pcm_stop_recording(void) /** - * Process the buffers using read_index and write_index. + * Process the chunks using read_index and write_index. * - * DMA1 handler posts to pcmrec_queue so that pcmrec_thread calls this - * function. Also pcmrec_stop will call this function when the recording - * is stopping, and that call will have flush = true. + * DMA1 handler posts to pcmrec_queue and pcmrec_thread calls this + * function. + * + * Other function can also call this function with flush = true when + * they want to save everything recorded sofar to disk. * */ -void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); -void pcmrec_callback(bool flush) +static void pcmrec_callback(bool flush) __attribute__ ((section (".icode"))); +static void pcmrec_callback(bool flush) { - int num_ready; + int num_ready, num_free, num_new; + unsigned short *ptr; + int i, j, w; + + w = write_index; + + num_new = w - read2_index; + if (num_new < 0) + num_new += num_chunks; + + for (i=0; i= num_chunks) + read2_index = 0; + } - num_ready = write_index - read_index; + num_ready = w - read_index; if (num_ready < 0) - num_ready += NUM_BUFFERS; - - /* we can consume up to num_ready buffers */ + num_ready += num_chunks; -#ifdef HAVE_REMOTE_LCD - /* Draw waveform on remote LCD */ - if (show_waveform && num_ready>0) + if (num_ready >= num_chunks) { - short *buf; - long x,y,offset; - int show_index; - - /* Just display the last buffer (most recent one) */ - show_index = read_index + num_ready - 1; - buf = (short*)rec_buffers[show_index]; - - lcd_remote_clear_display(); - - offset = 0; - for (x=0; x> 15; /* Divide with SHRT_MAX */ - y += LCD_REMOTE_HEIGHT/2; + logf("num_ready overflow?"); + num_ready = num_chunks-1; + } - if (y < 2) y=2; - if (y >= LCD_REMOTE_HEIGHT-2) y = LCD_REMOTE_HEIGHT-2; + num_free = num_chunks - num_ready; - lcd_remote_drawpixel(x,y); + if (wav_file == -1 || (!is_recording && !flush)) + { + /* In this case we should consume the buffers to avoid */ + /* getting 'dma1 overrun' */ - offset += (EACH_BUFFER_SIZE/2) / LCD_REMOTE_WIDTH; - } + read_index+=num_ready; + if (read_index >= num_chunks) + read_index -= num_chunks; - lcd_remote_update(); + return; } -#endif - - /* Note: This might be a good place to call the 'codec' later */ - - /* Check that we have the minimum amount of data to save or */ - /* that if it's closing time which mean we have to save.. */ - if (wav_file != -1) + if (num_free <= WRITE_THRESHOLD || flush) { - if (num_ready >= WRITE_THRESHOLD || flush) + logf("writing: %d (%d)", num_ready, flush); + + for (i=0; i= NUM_BUFFERS) - read_index -= NUM_BUFFERS; + + num_file_bytes += CHUNK_SIZE; + + read_index++; + if (read_index >= num_chunks) + read_index = 0; } - - } else - { - /* In this case we must consume the buffers otherwise we will */ - /* get 'dma1 overrun' pretty fast */ - - read_index+=num_ready; - if (read_index >= NUM_BUFFERS) - read_index -= NUM_BUFFERS; + + logf("done"); } } +/* Abort dma transfer */ +static void pcmrec_dma_stop(void) +{ + DCR1 = 0; + + is_error = true; + is_recording = false; + + error_count++; + + logf("dma1 stopped"); +} -void pcmrec_dma_start(void) +static void pcmrec_dma_start(void) { - DAR1 = (unsigned long)rec_buffers[write_index++]; /* Destination address */ - SAR1 = (unsigned long)&PDIR2; /* Source address */ - BCR1 = EACH_BUFFER_SIZE; /* Bytes to transfer */ + DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ + SAR1 = (unsigned long)&PDIR2; /* Source address */ + BCR1 = CHUNK_SIZE; /* Bytes to transfer */ /* Start the DMA transfer.. */ DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; @@ -404,36 +491,37 @@ void DMA1(void) { DCR1 = 0; /* Stop DMA transfer */ error_count++; - is_recording = 0; + is_recording = false; logf("dma1 err 0x%x", res); + + /* Flush recorded data to disk and stop recording */ + queue_post(&pcmrec_queue, PCMREC_STOP, NULL); } else { - num_rec_bytes += EACH_BUFFER_SIZE; - write_index++; - if (write_index >= NUM_BUFFERS) + if (write_index >= num_chunks) write_index = 0; if (is_stopping || !is_recording) { DCR1 = 0; /* Stop DMA transfer */ - is_recording = 0; + is_stopping = false; logf("dma1 stopping"); } else if (write_index == read_index) { DCR1 = 0; /* Stop DMA transfer */ - is_recording = 0; + is_recording = false; logf("dma1 overrun"); } else { - DAR1 = (unsigned long)rec_buffers[write_index]; /* Destination address */ - BCR1 = EACH_BUFFER_SIZE; + DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */ + BCR1 = CHUNK_SIZE; queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL); @@ -443,6 +531,8 @@ void DMA1(void) 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) { unsigned char header[44] = @@ -456,7 +546,8 @@ static int start_wave(void) if (wav_file < 0) { wav_file = -1; - logf("create failed: %d", wav_file); + logf("rec: create failed: %d", wav_file); + is_error = true; return -1; } @@ -464,8 +555,9 @@ static int start_wave(void) { close(wav_file); wav_file = -1; - logf("write failed"); - return -2; + logf("rec: write failed"); + is_error = true; + return -1; } return 0; @@ -476,16 +568,19 @@ static void close_wave(void) { long l; - l = htole32(num_rec_bytes + 36); - lseek(wav_file, 4, SEEK_SET); - write(wav_file, &l, 4); - - l = htole32(num_rec_bytes); - lseek(wav_file, 40, SEEK_SET); - write(wav_file, &l, 4); - - close(wav_file); - wav_file = -1; + if (wav_file != -1) + { + l = htole32(num_file_bytes + 36); + lseek(wav_file, 4, SEEK_SET); + write(wav_file, &l, 4); + + l = htole32(num_file_bytes); + lseek(wav_file, 40, SEEK_SET); + write(wav_file, &l, 4); + + close(wav_file); + wav_file = -1; + } } static void pcmrec_start(void) @@ -493,74 +588,206 @@ static void pcmrec_start(void) logf("pcmrec_start"); if (is_recording) + { + logf("already recording"); + record_done = true; return; - + } + if (wav_file != -1) close(wav_file); - logf("rec: %s", recording_filename); - - start_wave(); /* todo: send signal to pcm_record if we have failed */ - + if (start_wave() != 0) + { + /* failed to create the file */ + record_done = true; + return; + } + num_rec_bytes = 0; - - /* Store the current time */ + num_file_bytes = 0; record_start_time = current_tick; - + pause_start_time = 0; + write_index = 0; read_index = 0; + read2_index = 0; - is_stopping = 0; - is_paused = 0; - is_recording = 1; - + is_stopping = false; + is_paused = false; + is_recording = true; + pcmrec_dma_start(); + record_done = true; } static void pcmrec_stop(void) { - /* wait for recording to finish */ + logf("pcmrec_stop"); + + if (!is_recording) + { + stop_done = true; + return; + } + + if (!is_paused) + { + /* wait for recording to finish */ + is_stopping = true; + + while (is_stopping && is_recording) + sleep_thread(); + wake_up_thread(); + + is_stopping = false; + } + + is_recording = false; + + /* Flush buffers to file */ + pcmrec_callback(true); - /* todo: Abort current DMA transfer using DCR1.. */ + close_wave(); - logf("pcmrec_stop"); + stop_done = true; + + logf("pcmrec_stop done"); +} - while (is_recording) +static void pcmrec_new_file(void) +{ + logf("pcmrec_new_file"); + + if (!is_recording) { - sleep(HZ >> 4); + logf("not recording"); + new_file_done = true; + return; } + + /* 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; + num_rec_bytes = 0; + + if (is_paused) + pause_start_time = record_start_time; + + /* Flush what we got in buffers to file */ + pcmrec_callback(true); + + close_wave(); - logf("pcmrec_stop done"); + num_file_bytes = 0; + + /* start the new file */ + if (start_wave() != 0) + { + logf("new_file failed"); + pcmrec_stop(); + } - /* Write unfinished buffers to file */ + new_file_done = true; + logf("pcmrec_new_file done"); +} + +static void pcmrec_pause(void) +{ + logf("pcmrec_pause"); + + if (!is_recording) + { + logf("pause: not recording"); + pause_done = true; + return; + } + + /* Abort DMA transfer and flush to file? */ + + is_stopping = true; + + while (is_stopping && is_recording) + sleep_thread(); + wake_up_thread(); + + pause_start_time = current_tick; + is_paused = true; + + /* Flush what we got in buffers to file */ pcmrec_callback(true); + + pause_done = true; + + logf("pcmrec_pause done"); +} - close_wave(); - is_stopping = 0; +static void pcmrec_resume(void) +{ + logf("pcmrec_resume"); + + if (!is_paused) + { + logf("resume: not paused"); + resume_done = true; + return; + } + + is_paused = false; + is_recording = true; + + /* Compensate for the time we have been paused */ + if (pause_start_time) + { + record_start_time += current_tick - pause_start_time; + pause_start_time = 0; + } + + pcmrec_dma_start(); + + resume_done = true; + + logf("pcmrec_resume done"); } -static void pcmrec_open(void) + +/** + * audio_init_recording calls this function using PCMREC_INIT + * + */ +static void pcmrec_init(void) { - unsigned long buffer_start; - int i; + unsigned long buffer_size; show_waveform = 0; - is_recording = 0; - is_stopping = 0; - num_rec_bytes = 0; wav_file = -1; read_index = 0; + read2_index = 0; write_index = 0; - - buffer_start = (unsigned long)(&audiobuf[(audiobufend - audiobuf) - (ALL_BUFFERS_SIZE + 16)]); - buffer_start &= ~3; - - for (i=0; i