1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Andy 2005-11-12 04:00:56 +00:00
parent 663fba1399
commit e6e5496535
19 changed files with 778 additions and 386 deletions

View file

@ -65,7 +65,4 @@ playback.c
metadata.c metadata.c
codecs.c codecs.c
dsp.c dsp.c
#ifndef SIMULATOR
pcm_recording.c
#endif
#endif #endif

View file

@ -58,9 +58,6 @@
#include "ata_mmc.h" #include "ata_mmc.h"
#endif #endif
#include "logfdisp.h" #include "logfdisp.h"
#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
extern bool pcm_rec_screen(void);
#endif
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcmbuf.h" #include "pcmbuf.h"
#include "pcm_playback.h" #include "pcm_playback.h"
@ -1781,7 +1778,6 @@ bool debug_menu(void)
{ "CPU frequency", dbg_cpufreq }, { "CPU frequency", dbg_cpufreq },
#endif #endif
#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
{ "PCM recording", pcm_rec_screen },
{ "S/PDIF analyzer", dbg_spdif }, { "S/PDIF analyzer", dbg_spdif },
#endif #endif
#if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE) #if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE)

View file

@ -939,13 +939,13 @@ new:
id: LANG_RECORDING_LEFT id: LANG_RECORDING_LEFT
desc: in the recording screen desc: in the recording screen
eng: "Left" eng: "Gain Left"
voice: "" voice: ""
new: new:
id: LANG_RECORDING_RIGHT id: LANG_RECORDING_RIGHT
desc: in the recording screen desc: in the recording screen
eng: "Right" eng: "Gain Right"
voice: "" voice: ""
new: new:
@ -3352,3 +3352,21 @@ desc: in crossfade settings menu
eng: "Fade out mode" eng: "Fade out mode"
voice: "Fade out mode" voice: "Fade out mode"
new: 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:

View file

@ -323,7 +323,7 @@ void init(void)
sound_settings_apply(); sound_settings_apply();
#endif #endif
#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
pcm_init_recording(); pcm_rec_init();
#endif #endif
talk_init(); talk_init();
/* runtime database has to be initialized after audio_init() */ /* runtime database has to be initialized after audio_init() */

View file

@ -226,7 +226,7 @@ bool radio_screen(void)
audio_stop(); audio_stop();
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC
mpeg_init_recording(); audio_init_recording();
sound_settings_apply(); sound_settings_apply();
@ -238,23 +238,24 @@ bool radio_screen(void)
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
1, /* Line In */ 1, /* Line In */
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, 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), audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
sound_default(SOUND_RIGHT_GAIN), false); sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
#else #else
uda1380_enable_recording(false); uda1380_enable_recording(false);
uda1380_set_recvol(0, 0, 10); uda1380_set_recvol(10, 10, AUDIO_GAIN_LINEIN);
uda1380_set_monitor(true); uda1380_set_monitor(true);
/* Set the input multiplexer to FM */ /* Set the input multiplexer to FM */
pcmrec_set_mux(1); pcm_rec_mux(1);
#endif #endif
#endif #endif
@ -345,14 +346,14 @@ bool radio_screen(void)
#ifndef SIMULATOR #ifndef SIMULATOR
if(audio_status() == AUDIO_STATUS_RECORD) if(audio_status() == AUDIO_STATUS_RECORD)
{ {
mpeg_new_file(rec_create_filename(buf)); audio_new_file(rec_create_filename(buf));
update_screen = true; update_screen = true;
} }
else else
{ {
have_recorded = true; have_recorded = true;
talk_buffer_steal(); /* we use the mp3 buffer */ talk_buffer_steal(); /* we use the mp3 buffer */
mpeg_record(rec_create_filename(buf)); audio_record(rec_create_filename(buf));
update_screen = true; update_screen = true;
} }
#endif #endif
@ -517,7 +518,7 @@ bool radio_screen(void)
#ifndef SIMULATOR #ifndef SIMULATOR
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC
seconds = mpeg_recorded_time() / HZ; seconds = audio_recorded_time() / HZ;
#endif #endif
#endif #endif
if(update_screen || seconds > last_seconds) if(update_screen || seconds > last_seconds)
@ -609,8 +610,8 @@ bool radio_screen(void)
{ {
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC
/* Enable the Left and right A/D Converter */ /* Enable the Left and right A/D Converter */
mpeg_set_recording_gain(sound_default(SOUND_LEFT_GAIN), audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
sound_default(SOUND_RIGHT_GAIN), false); sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
mas_codec_writereg(6, 0x4000); mas_codec_writereg(6, 0x4000);
#endif #endif
radio_set_status(FMRADIO_POWERED); /* leave it powered */ radio_set_status(FMRADIO_POWERED); /* leave it powered */
@ -619,7 +620,7 @@ bool radio_screen(void)
{ {
radio_stop(); radio_stop();
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
pcmrec_set_mux(0); /* Line In */ pcm_rec_mux(0); /* Line In */
#endif #endif
} }
@ -913,12 +914,13 @@ static bool fm_recording_settings(void)
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
1, /* Line In */ 1, /* Line In */
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, global_settings.rec_editable,
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time,
global_settings.rec_monitor);
} }
return ret; return ret;
} }

View file

@ -28,6 +28,13 @@
#include "led.h" #include "led.h"
#include "mpeg.h" #include "mpeg.h"
#include "audio.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 "mp3_playback.h"
#include "mas.h" #include "mas.h"
#include "button.h" #include "button.h"
@ -79,9 +86,12 @@
#elif CONFIG_KEYPAD == IRIVER_H100_PAD #elif CONFIG_KEYPAD == IRIVER_H100_PAD
#define REC_STOPEXIT BUTTON_OFF #define REC_STOPEXIT BUTTON_OFF
#define REC_RECPAUSE BUTTON_ON #define REC_RECPAUSE BUTTON_REC
#define REC_INC BUTTON_RIGHT #define REC_INC BUTTON_RIGHT
#define REC_DEC BUTTON_LEFT #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 #elif CONFIG_KEYPAD == GMINI100_PAD
#define REC_STOPEXIT BUTTON_OFF #define REC_STOPEXIT BUTTON_OFF
@ -102,6 +112,12 @@ bool f3_rec_screen(void);
#define MAX_SOURCE SOURCE_LINE #define MAX_SOURCE SOURCE_LINE
#endif #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 */ #define MAX_FILE_SIZE 0x7FF00000 /* 2 GB - 1 MB */
const char* const freq_str[6] = const char* const freq_str[6] =
@ -118,13 +134,18 @@ static void set_gain(void)
{ {
if(global_settings.rec_source == SOURCE_MIC) 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 else
{ {
mpeg_set_recording_gain(global_settings.rec_left_gain, audio_set_recording_gain(global_settings.rec_left_gain,
global_settings.rec_right_gain, false); 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[] = static const char* const fmtstr[] =
@ -176,9 +197,9 @@ char *rec_create_filename(char *buffer)
strncpy(buffer, rec_base_directory, MAX_PATH); strncpy(buffer, rec_base_directory, MAX_PATH);
#ifdef HAVE_RTC #ifdef HAVE_RTC
create_datetime_filename(buffer, buffer, "R", ".mp3"); create_datetime_filename(buffer, buffer, "R", REC_FILE_ENDING);
#else #else
create_numbered_filename(buffer, buffer, "rec_", ".mp3", 4); create_numbered_filename(buffer, buffer, "rec_", REC_FILE_ENDING, 4);
#endif #endif
return buffer; return buffer;
} }
@ -227,7 +248,7 @@ static void trigger_listener(int trigger_status)
if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD) if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD)
{ {
talk_buffer_steal(); /* we use the mp3 buffer */ 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 */ /* give control to mpeg thread so that it can start recording */
yield(); yield(); yield(); yield(); yield(); yield();
@ -236,7 +257,7 @@ static void trigger_listener(int trigger_status)
/* if we're already recording this is a retrigger */ /* if we're already recording this is a retrigger */
else else
{ {
mpeg_new_file(rec_create_filename(path_buffer)); audio_new_file(rec_create_filename(path_buffer));
/* tell recording_screen to reset the time */ /* tell recording_screen to reset the time */
last_seconds = 0; last_seconds = 0;
} }
@ -273,6 +294,7 @@ bool recording_screen(void)
char path_buffer[MAX_PATH]; char path_buffer[MAX_PATH];
bool been_in_usb_mode = false; bool been_in_usb_mode = false;
int last_audio_stat = -1; int last_audio_stat = -1;
int audio_stat;
#if CONFIG_LED == LED_REAL #if CONFIG_LED == LED_REAL
bool led_state = false; bool led_state = false;
int led_countdown = 2; int led_countdown = 2;
@ -289,7 +311,7 @@ bool recording_screen(void)
#if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
ata_set_led_enabled(false); ata_set_led_enabled(false);
#endif #endif
mpeg_init_recording(); audio_init_recording();
sound_set_volume(global_settings.volume); sound_set_volume(global_settings.volume);
@ -298,15 +320,20 @@ bool recording_screen(void)
peak_meter_enabled = true; peak_meter_enabled = true;
#if CONFIG_CODEC == SWCODEC
audio_stop();
#endif
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, global_settings.rec_editable,
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time,
global_settings.rec_monitor);
set_gain(); set_gain();
@ -321,7 +348,12 @@ bool recording_screen(void)
while(!done) 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 #if CONFIG_LED == LED_REAL
/* /*
@ -390,7 +422,7 @@ bool recording_screen(void)
if(audio_stat & AUDIO_STATUS_RECORD) if(audio_stat & AUDIO_STATUS_RECORD)
{ {
audio_stop(); audio_stop_recording();
} }
else else
{ {
@ -416,7 +448,7 @@ bool recording_screen(void)
/* manual recording */ /* manual recording */
have_recorded = true; have_recorded = true;
talk_buffer_steal(); /* we use the mp3 buffer */ 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; last_seconds = 0;
if (global_settings.talk_menu) if (global_settings.talk_menu)
{ /* no voice possible here, but a beep */ { /* no voice possible here, but a beep */
@ -438,7 +470,7 @@ bool recording_screen(void)
{ {
if(audio_stat & AUDIO_STATUS_PAUSE) if(audio_stat & AUDIO_STATUS_PAUSE)
{ {
mpeg_resume_recording(); audio_resume_recording();
if (global_settings.talk_menu) if (global_settings.talk_menu)
{ /* no voice possible here, but a beep */ { /* no voice possible here, but a beep */
audio_beep(HZ/4); /* short beep on resume */ audio_beep(HZ/4); /* short beep on resume */
@ -446,7 +478,7 @@ bool recording_screen(void)
} }
else else
{ {
mpeg_pause_recording(); audio_pause_recording();
} }
} }
update_countdown = 1; /* Update immediately */ update_countdown = 1; /* Update immediately */
@ -557,12 +589,13 @@ bool recording_screen(void)
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, global_settings.rec_editable,
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time,
global_settings.rec_monitor);
set_gain(); set_gain();
update_countdown = 1; /* Update immediately */ update_countdown = 1; /* Update immediately */
@ -596,7 +629,7 @@ bool recording_screen(void)
case REC_F3: case REC_F3:
if(audio_stat & AUDIO_STATUS_RECORD) 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; last_seconds = 0;
} }
else else
@ -638,7 +671,7 @@ bool recording_screen(void)
lcd_setfont(FONT_SYSFIXED); lcd_setfont(FONT_SYSFIXED);
seconds = mpeg_recorded_time() / HZ; seconds = audio_recorded_time() / HZ;
update_countdown--; update_countdown--;
if(update_countdown == 0 || seconds > last_seconds) if(update_countdown == 0 || seconds > last_seconds)
@ -660,7 +693,7 @@ bool recording_screen(void)
lcd_puts(0, 0, buf); lcd_puts(0, 0, buf);
dseconds = rec_timesplit_seconds(); dseconds = rec_timesplit_seconds();
num_recorded_bytes = mpeg_num_recorded_bytes(); num_recorded_bytes = audio_num_recorded_bytes();
if(audio_stat & AUDIO_STATUS_PRERECORD) if(audio_stat & AUDIO_STATUS_PRERECORD)
{ {
@ -699,7 +732,7 @@ bool recording_screen(void)
((global_settings.rec_timesplit && (seconds >= dseconds)) ((global_settings.rec_timesplit && (seconds >= dseconds))
|| (num_recorded_bytes >= MAX_FILE_SIZE))) || (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; update_countdown = 1;
last_seconds = 0; 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)); splash(0, true, str(LANG_DISK_FULL));
status_draw(true); 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(); audio_init_playback();
#endif
/* make sure the trigger is really turned off */ /* make sure the trigger is really turned off */
peak_meter_trigger(false); peak_meter_trigger(false);
@ -924,12 +968,13 @@ bool f2_rec_screen(void)
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, global_settings.rec_editable,
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time,
global_settings.rec_monitor);
set_gain(); set_gain();
@ -1018,12 +1063,14 @@ bool f3_rec_screen(void)
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */ 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_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable, global_settings.rec_editable,
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time,
global_settings.rec_monitor);
set_gain(); set_gain();
@ -1034,4 +1081,13 @@ bool f3_rec_screen(void)
} }
#endif /* #ifdef REC_F3 */ #endif /* #ifdef REC_F3 */
#if CONFIG_CODEC == SWCODEC
void audio_beep(int duration)
{
/* dummy */
(void)duration;
}
#endif
#endif /* HAVE_RECORDING */ #endif /* HAVE_RECORDING */

View file

@ -85,7 +85,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
#include "dsp.h" #include "dsp.h"
#endif #endif
#define CONFIG_BLOCK_VERSION 29 #define CONFIG_BLOCK_VERSION 30
#define CONFIG_BLOCK_SIZE 512 #define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44 #define RTC_BLOCK_SIZE 44
@ -407,6 +407,7 @@ static const struct bit_entry hd_bits[] =
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
{1, S_O(rec_startup), false, "rec screen on startup", off_on }, {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 */ /* values for the trigger */
{8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL}, {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 }, {22, S_O(dircache_size), 0, NULL, NULL },
#endif #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. */ /* If values are just added to the end, no need to bump the version. */
/* new stuff to be added at the end */ /* new stuff to be added at the end */

View file

@ -176,7 +176,12 @@ struct user_settings
int rec_mic_gain; /* 0-15 */ int rec_mic_gain; /* 0-15 */
int rec_left_gain; /* 0-15 */ int rec_left_gain; /* 0-15 */
int rec_right_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_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 */ /* note: timesplit setting is not saved */
int rec_timesplit; /* 0 = off, int rec_timesplit; /* 0 = off,

View file

@ -228,6 +228,28 @@ static bool receditable(void)
&global_settings.rec_editable); &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 bool rectimesplit(void)
{ {
static const struct opt_items names[] = { static const struct opt_items names[] = {
@ -740,7 +762,7 @@ bool recording_menu(bool no_source)
{ {
int m; int m;
int i = 0; int i = 0;
struct menu_item items[10]; struct menu_item items[13];
bool result; bool result;
items[i].desc = ID2P(LANG_RECORDING_QUALITY); 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].desc = ID2P(LANG_RECORDING_CHANNELS);
items[i++].function = recchannels; 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].desc = ID2P(LANG_RECORDING_EDITABLE);
items[i++].function = receditable; items[i++].function = receditable;
items[i].desc = ID2P(LANG_RECORD_TIMESPLIT); items[i].desc = ID2P(LANG_RECORD_TIMESPLIT);

View file

@ -28,6 +28,7 @@
#include "string.h" #include "string.h"
#include "file.h" #include "file.h"
#include "buffer.h" #include "buffer.h"
#include "audio.h"
#include "i2c-coldfire.h" #include "i2c-coldfire.h"
#include "uda1380.h" #include "uda1380.h"
@ -204,7 +205,7 @@ void uda1380_close(void)
* sound samples over the I2S bus, which is connected * sound samples over the I2S bus, which is connected
* to the processor's IIS1 interface. * 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) void uda1380_enable_recording(bool source_mic)
{ {
@ -246,17 +247,29 @@ void uda1380_disable_recording(void)
/** /**
* Set recording gain and volume * Set recording gain and volume
* *
* mic_gain : range 0 .. 15 -> 0 .. 30 dB gain * type: params: ranges:
* linein_gain : range 0 .. 15 -> 0 .. 24 dB gain * 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
* *
* adc_volume : range -127 .. 48 -> -63 .. 24 dB gain * Note: For all types the value 0 gives 0 dB gain.
* note that 0 -> 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)); switch (type)
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)); 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;
}
} }

View file

@ -33,6 +33,11 @@
#define AUDIOERR_DISK_FULL 1 #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 struct audio_debug
{ {
int audiobuflen; int audiobuflen;
@ -79,6 +84,24 @@ int audio_get_file_pos(void);
void audio_beep(int duration); void audio_beep(int duration);
void audio_init_playback(void); 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 */ /* audio event handling */

View file

@ -7,7 +7,7 @@
#define MODEL_NUMBER 0 #define MODEL_NUMBER 0
/* define this if you have recording possibility */ /* 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 this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1 #define HAVE_LCD_BITMAP 1

View file

@ -44,20 +44,11 @@
#define MPEG_RESERVED_HEADER_SPACE (4096 + 576) #define MPEG_RESERVED_HEADER_SPACE (4096 + 576)
#if (CONFIG_CODEC == MAS3587F) || defined(SIMULATOR) #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 #if CONFIG_TUNER & S1A0903X01
int mpeg_get_mas_pllfreq(void); int mpeg_get_mas_pllfreq(void);
#endif #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 #endif
unsigned long mpeg_get_last_header(void); unsigned long mpeg_get_last_header(void);

View file

@ -17,37 +17,13 @@
* *
****************************************************************************/ ****************************************************************************/
/*
* Function names are taken from apps/recorder/recording.c to
* make the integration later easier..
*
*/
#ifndef PCM_RECORD_H #ifndef PCM_RECORD_H
#define PCM_RECORD_H #define PCM_RECORD_H
unsigned long pcm_status(void); unsigned long pcm_rec_status(void);
void pcm_rec_init(void);
void pcm_rec_mux(int source);
void pcm_init_recording(void); /* audio.h contains audio recording functions */
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);
void pcmrec_set_mux(int source);
#endif #endif

View file

@ -36,11 +36,15 @@ enum {
SOUND_MDB_ENABLE, SOUND_MDB_ENABLE,
SOUND_SUPERBASS, SOUND_SUPERBASS,
#endif #endif
#if CONFIG_CODEC == MAS3587F #if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380)
SOUND_LEFT_GAIN, SOUND_LEFT_GAIN,
SOUND_RIGHT_GAIN, SOUND_RIGHT_GAIN,
SOUND_MIC_GAIN, SOUND_MIC_GAIN,
#endif #endif
#if defined(HAVE_UDA1380)
SOUND_ADC_LEFT_GAIN,
SOUND_ADC_RIGHT_GAIN,
#endif
}; };
enum { enum {

View file

@ -32,7 +32,7 @@ extern void uda1380_set_nsorder(int order);
extern void uda1380_enable_recording(bool source_mic); extern void uda1380_enable_recording(bool source_mic);
extern void uda1380_disable_recording(void); 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); extern void uda1380_set_monitor(int enable);
#define UDA1380_ADDR 0x30 #define UDA1380_ADDR 0x30

View file

@ -2115,7 +2115,7 @@ void audio_init_playback(void)
/**************************************************************************** /****************************************************************************
* Recording functions * Recording functions
***************************************************************************/ ***************************************************************************/
void mpeg_init_recording(void) void audio_init_recording(void)
{ {
init_recording_done = false; init_recording_done = false;
queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL); queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL);
@ -2224,7 +2224,7 @@ static void init_recording(void)
call mpeg_set_recording_options(). */ call mpeg_set_recording_options(). */
} }
void mpeg_record(const char *filename) void audio_record(const char *filename)
{ {
mpeg_errno = 0; mpeg_errno = 0;
@ -2234,12 +2234,12 @@ void mpeg_record(const char *filename)
queue_post(&mpeg_queue, MPEG_RECORD, NULL); 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); 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); queue_post(&mpeg_queue, MPEG_RESUME_RECORDING, NULL);
} }
@ -2435,9 +2435,10 @@ static void stop_recording(void)
resume_recording(); 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, int source, int channel_mode,
bool editable, int prerecord_time) bool editable, int prerecord_time,
bool monitor)
{ {
bool is_mpeg1; 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); 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 */ ((source < 2)?1:2) << 8) | /* Input select */
(1 << 5) | /* SDO strobe invert */ (1 << 5) | /* SDO strobe invert */
((is_mpeg1?0:1) << 3) | ((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 */ /* 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 */ /* Enable both left and right A/D */
shadow_codec_reg0 = (left << 12) | shadow_codec_reg0 = (left << 12) |
(right << 8) | (right << 8) |
(left << 4) | (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; 0x0007;
mas_codec_writereg(0x0, shadow_codec_reg0); mas_codec_writereg(0x0, shadow_codec_reg0);
} }
@ -2539,7 +2540,7 @@ void audio_beep(int duration)
while (current_tick - starttick < duration); while (current_tick - starttick < duration);
} }
void mpeg_new_file(const char *filename) void audio_new_file(const char *filename)
{ {
mpeg_errno = 0; mpeg_errno = 0;
@ -2549,7 +2550,7 @@ void mpeg_new_file(const char *filename)
queue_post(&mpeg_queue, MPEG_NEW_FILE, NULL); queue_post(&mpeg_queue, MPEG_NEW_FILE, NULL);
} }
unsigned long mpeg_recorded_time(void) unsigned long audio_recorded_time(void)
{ {
if(is_prerecording) if(is_prerecording)
return prerecord_count * HZ; return prerecord_count * HZ;
@ -2565,7 +2566,7 @@ unsigned long mpeg_recorded_time(void)
return 0; return 0;
} }
unsigned long mpeg_num_recorded_bytes(void) unsigned long audio_num_recorded_bytes(void)
{ {
int num_bytes; int num_bytes;
int index; int index;
@ -2599,7 +2600,7 @@ void audio_init_playback(void)
{ {
/* a dummy */ /* a dummy */
} }
unsigned long mpeg_recorded_time(void) unsigned long audio_recorded_time(void)
{ {
/* a dummy */ /* a dummy */
return 0; return 0;
@ -2609,42 +2610,42 @@ void audio_beep(int duration)
/* a dummy */ /* a dummy */
(void)duration; (void)duration;
} }
void mpeg_pause_recording(void) void audio_pause_recording(void)
{ {
/* a dummy */ /* a dummy */
} }
void mpeg_resume_recording(void) void audio_resume_recording(void)
{ {
/* a dummy */ /* a dummy */
} }
unsigned long mpeg_num_recorded_bytes(void) unsigned long audio_num_recorded_bytes(void)
{ {
/* a dummy */ /* a dummy */
return 0; return 0;
} }
void mpeg_record(const char *filename) void audio_record(const char *filename)
{ {
/* a dummy */ /* a dummy */
(void)filename; (void)filename;
} }
void mpeg_new_file(const char *filename) void audio_new_file(const char *filename)
{ {
/* a dummy */ /* a dummy */
(void)filename; (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 */ /* a dummy */
(void)left; (void)left;
(void)right; (void)right;
(void)use_mic; (void)type;
} }
void mpeg_init_recording(void) void audio_init_recording(void)
{ {
/* a dummy */ /* 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, int source, int channel_mode,
bool editable, int prerecord_time) bool editable, int prerecord_time)
{ {
@ -2710,6 +2711,12 @@ void audio_stop(void)
#endif /* SIMULATOR */ #endif /* SIMULATOR */
} }
/* dummy */
void audio_stop_recording(void)
{
audio_stop();
}
void audio_pause(void) void audio_pause(void)
{ {
#ifndef SIMULATOR #ifndef SIMULATOR

View file

@ -30,11 +30,7 @@
#include "cpu.h" #include "cpu.h"
#include "i2c.h" #include "i2c.h"
#if defined(HAVE_UDA1380)
#include "uda1380.h" #include "uda1380.h"
#elif defined(HAVE_TLV320)
#include "tlv320.h"
#endif
#include "system.h" #include "system.h"
#include "usb.h" #include "usb.h"
@ -56,47 +52,56 @@
static volatile bool is_recording; /* We are recording */ static volatile bool is_recording; /* We are recording */
static volatile bool is_stopping; /* Are we going to stop */ static volatile bool is_stopping; /* Are we going to stop */
static volatile bool is_paused; /* We have paused */ 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 int_count; /* Number of DMA completed interrupts */
static volatile int error_count; /* Number of DMA errors */ 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 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 unsigned long pause_start_time; /* Value of current_tick when pause was started */
static int rec_gain, rec_volume;
static bool show_waveform; static bool show_waveform;
static int init_done = 0;
static int wav_file; static int wav_file;
static char recording_filename[MAX_PATH]; 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: Some estimates:
44100 HZ * 4 = 176400 bytes/s Normal recording rate: 44100 HZ * 4 = 176 KB/s
Refresh LCD 10 HZ = 176400 / 10 = 17640 bytes ~=~ 1024*16 bytes 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 CHUNK_SIZE 65536 /* Multiple of 4 */
#define EACH_BUFFER_SIZE (1024*16) /* Multiple of 4. Use small value to get responsive waveform */ #define WRITE_THRESHOLD 30 /* Write when this many chunks (or less) until buffer full */
#define ALL_BUFFERS_SIZE (NUM_BUFFERS * EACH_BUFFER_SIZE)
#define WRITE_THRESHOLD 40 /* Minimum number of buffers before write to file */ #define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)])
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 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 somewhere or increase Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk
the total buffer size (or WRITE_THRESHOLD) more often.
*/ */
static int write_index; /* Which buffer the DMA is currently recording */ static volatile int write_index; /* Current chunk the DMA is writing to */
static int read_index; /* The oldest buffer that the pcmrec_callback has not read */ 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 const char pcmrec_thread_name[] = "pcmrec";
static void pcmrec_thread(void); static void pcmrec_thread(void);
static void pcmrec_dma_start(void);
static void pcmrec_dma_stop(void);
/* Event IDs */ /* Event IDs */
#define PCMREC_OPEN 1 /* Enable recording */ #define PCMREC_INIT 1 /* Enable recording */
#define PCMREC_CLOSE 2 /* Disable recording */ #define PCMREC_CLOSE 2
#define PCMREC_START 3 /* Start a new recording */ #define PCMREC_START 3 /* Start a new recording */
#define PCMREC_STOP 4 /* Stop the current recording */ #define PCMREC_STOP 4 /* Stop the current recording */
#define PCMREC_PAUSE 10 #define PCMREC_PAUSE 10
@ -122,71 +130,58 @@ static void pcmrec_thread(void);
/* Functions that are not executing in the pcmrec_thread first */ /* 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); queue_init(&pcmrec_queue);
create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name); create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name);
} }
void pcm_open_recording(void)
/* Initializes recording:
* - Set up the UDA1380 for recording
* - Prepare for DMA transfers
*/
void audio_init_recording(void)
{ {
init_done = 0; init_done = false;
queue_post(&pcmrec_queue, PCMREC_INIT, 0);
logf("pcm_open_rec"); while(!init_done)
sleep_thread();
queue_post(&pcmrec_queue, PCMREC_OPEN, 0); wake_up_thread();
while (init_done)
{
sleep(HZ >> 8);
}
logf("pcm_open_rec done");
} }
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); queue_post(&pcmrec_queue, PCMREC_CLOSE, 0);
while(!close_done)
sleep_thread();
wake_up_thread();
} }
unsigned long pcm_rec_status(void)
unsigned long pcm_status(void)
{ {
unsigned long ret = 0; unsigned long ret = 0;
if (is_recording) if (is_recording)
ret |= AUDIO_STATUS_RECORD; ret |= AUDIO_STATUS_RECORD;
if (is_paused)
ret |= AUDIO_STATUS_PAUSE;
if (is_error)
ret |= AUDIO_STATUS_ERROR;
return ret; return ret;
} }
unsigned long audio_recorded_time(void)
void pcm_new_file(const char *filename)
{
/* todo */
filename = filename;
}
unsigned long pcm_recorded_time(void)
{ {
if (is_recording) if (is_recording)
{ {
if(is_paused) if (is_paused)
return pause_start_time - record_start_time; return pause_start_time - record_start_time;
else else
return current_tick - record_start_time; return current_tick - record_start_time;
@ -195,92 +190,168 @@ unsigned long pcm_recorded_time(void)
return 0; return 0;
} }
unsigned long pcm_num_recorded_bytes(void) unsigned long audio_num_recorded_bytes(void)
{ {
if (is_recording) if (is_recording)
{
return num_rec_bytes; return num_rec_bytes;
}
else
return 0; return 0;
} }
void pcm_pause_recording(void)
{
/* todo */
}
void pcm_resume_recording(void)
{
/* todo */
}
/** /**
* Sets the audio source * 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) /* TODO: */
uda1380_enable_recording(source); (void)frequency;
#elif defined(HAVE_TLV320) (void)quality;
tlv320_enable_recording(source); (void)channel_mode;
#endif (void)editable;
show_waveform = enable_waveform; (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 type 0=line-in (radio), 1=mic, 2=ADC
* *
* @param gain line-in and microphone gain (0-15)
* @param volume ADC volume (0-255)
*/ */
void pcm_set_recording_gain(int gain, int volume) void audio_set_recording_gain(int left, int right, int type)
{ {
rec_gain = gain; //logf("rcmrec: t=%d l=%d r=%d", type, left, right);
rec_volume = volume; uda1380_set_recvol(left, right, type);
queue_post(&pcmrec_queue, PCMREC_SET_GAIN, 0);
} }
/** /**
* Start recording * 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); strncpy(recording_filename, filename, MAX_PATH - 1);
recording_filename[MAX_PATH - 1] = 0; recording_filename[MAX_PATH - 1] = 0;
record_done = false;
queue_post(&pcmrec_queue, PCMREC_START, 0); 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) if (!is_recording)
is_stopping = 1; return;
logf("pcm_stop");
stop_done = false;
queue_post(&pcmrec_queue, PCMREC_STOP, 0); queue_post(&pcmrec_queue, PCMREC_STOP, 0);
logf("pcm_stop_recording"); while(!stop_done)
sleep_thread();
wake_up_thread();
while (is_stopping) logf("pcm_stop done");
}
void audio_pause_recording(void)
{
if (!is_recording)
{ {
sleep(HZ >> 4); logf("pause when not recording");
return;
}
if (is_paused)
{
logf("pause when paused");
return;
} }
logf("pcm_stop_recording done"); pause_done = false;
queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
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 */ /* 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 * DMA1 handler posts to pcmrec_queue and pcmrec_thread calls this
* function. Also pcmrec_stop will call this function when the recording * function.
* is stopping, and that call will have flush = true. *
* 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"))); static void pcmrec_callback(bool flush) __attribute__ ((section (".icode")));
void pcmrec_callback(bool flush) static void pcmrec_callback(bool flush)
{ {
int num_ready; int num_ready, num_free, num_new;
unsigned short *ptr;
int i, j, w;
num_ready = write_index - read_index; w = write_index;
if (num_ready < 0)
num_ready += NUM_BUFFERS;
/* we can consume up to num_ready buffers */ num_new = w - read2_index;
if (num_new < 0)
num_new += num_chunks;
#ifdef HAVE_REMOTE_LCD for (i=0; i<num_new; i++)
/* Draw waveform on remote LCD */
if (show_waveform && num_ready>0)
{ {
short *buf; /* Convert the samples to little-endian so we only have to write later
long x,y,offset; (Less hd-spinning time)
int show_index; */
ptr = GET_CHUNK(read2_index);
/* Just display the last buffer (most recent one) */ for (j=0; j<CHUNK_SIZE/2; j++)
show_index = read_index + num_ready - 1;
buf = (short*)rec_buffers[show_index];
lcd_remote_clear_display();
offset = 0;
for (x=0; x<LCD_REMOTE_WIDTH-1; x++)
{ {
y = buf[offset] * (LCD_REMOTE_HEIGHT / 2) *5; /* The 5 is just 'zooming' */ /* TODO: might be a good place to add the peak-meter.. */
y = y >> 15; /* Divide with SHRT_MAX */
y += LCD_REMOTE_HEIGHT/2;
if (y < 2) y=2;
if (y >= LCD_REMOTE_HEIGHT-2) y = LCD_REMOTE_HEIGHT-2;
lcd_remote_drawpixel(x,y);
offset += (EACH_BUFFER_SIZE/2) / LCD_REMOTE_WIDTH;
}
lcd_remote_update();
}
#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_ready >= WRITE_THRESHOLD || flush)
{
unsigned short *ptr = (unsigned short*)rec_buffers[read_index];
int i;
for (i=0; i<EACH_BUFFER_SIZE * num_ready / 2; i++)
{
*ptr = htole16(*ptr); *ptr = htole16(*ptr);
ptr++; ptr++;
} }
write(wav_file, rec_buffers[read_index], EACH_BUFFER_SIZE * num_ready); num_rec_bytes += CHUNK_SIZE;
read_index+=num_ready; read2_index++;
if (read_index >= NUM_BUFFERS) if (read2_index >= num_chunks)
read_index -= NUM_BUFFERS; read2_index = 0;
} }
} else num_ready = w - read_index;
if (num_ready < 0)
num_ready += num_chunks;
if (num_ready >= num_chunks)
{ {
/* In this case we must consume the buffers otherwise we will */ logf("num_ready overflow?");
/* get 'dma1 overrun' pretty fast */ num_ready = num_chunks-1;
}
num_free = num_chunks - num_ready;
if (wav_file == -1 || (!is_recording && !flush))
{
/* In this case we should consume the buffers to avoid */
/* getting 'dma1 overrun' */
read_index+=num_ready; read_index+=num_ready;
if (read_index >= NUM_BUFFERS) if (read_index >= num_chunks)
read_index -= NUM_BUFFERS; read_index -= num_chunks;
return;
}
if (num_free <= WRITE_THRESHOLD || flush)
{
logf("writing: %d (%d)", num_ready, flush);
for (i=0; i<num_ready; i++)
{
if (write(wav_file, GET_CHUNK(read_index), CHUNK_SIZE) != CHUNK_SIZE)
{
logf("pcmrec: write err");
pcmrec_dma_stop();
return;
}
num_file_bytes += CHUNK_SIZE;
read_index++;
if (read_index >= num_chunks)
read_index = 0;
}
logf("done");
} }
} }
/* Abort dma transfer */
void pcmrec_dma_start(void) static void pcmrec_dma_stop(void)
{ {
DAR1 = (unsigned long)rec_buffers[write_index++]; /* Destination address */ DCR1 = 0;
is_error = true;
is_recording = false;
error_count++;
logf("dma1 stopped");
}
static void pcmrec_dma_start(void)
{
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
SAR1 = (unsigned long)&PDIR2; /* Source address */ SAR1 = (unsigned long)&PDIR2; /* Source address */
BCR1 = EACH_BUFFER_SIZE; /* Bytes to transfer */ BCR1 = CHUNK_SIZE; /* Bytes to transfer */
/* Start the DMA transfer.. */ /* Start the DMA transfer.. */
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START; DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
@ -404,36 +491,37 @@ void DMA1(void)
{ {
DCR1 = 0; /* Stop DMA transfer */ DCR1 = 0; /* Stop DMA transfer */
error_count++; error_count++;
is_recording = 0; is_recording = false;
logf("dma1 err 0x%x", res); logf("dma1 err 0x%x", res);
/* Flush recorded data to disk and stop recording */
queue_post(&pcmrec_queue, PCMREC_STOP, NULL);
} else } else
{ {
num_rec_bytes += EACH_BUFFER_SIZE;
write_index++; write_index++;
if (write_index >= NUM_BUFFERS) if (write_index >= num_chunks)
write_index = 0; write_index = 0;
if (is_stopping || !is_recording) if (is_stopping || !is_recording)
{ {
DCR1 = 0; /* Stop DMA transfer */ DCR1 = 0; /* Stop DMA transfer */
is_recording = 0; is_stopping = false;
logf("dma1 stopping"); logf("dma1 stopping");
} else if (write_index == read_index) } else if (write_index == read_index)
{ {
DCR1 = 0; /* Stop DMA transfer */ DCR1 = 0; /* Stop DMA transfer */
is_recording = 0; is_recording = false;
logf("dma1 overrun"); logf("dma1 overrun");
} else } else
{ {
DAR1 = (unsigned long)rec_buffers[write_index]; /* Destination address */ DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
BCR1 = EACH_BUFFER_SIZE; BCR1 = CHUNK_SIZE;
queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL); queue_post(&pcmrec_queue, PCMREC_GOT_DATA, NULL);
@ -443,6 +531,8 @@ void DMA1(void)
IPR |= (1<<15); /* Clear pending interrupt request */ 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) static int start_wave(void)
{ {
unsigned char header[44] = unsigned char header[44] =
@ -456,7 +546,8 @@ static int start_wave(void)
if (wav_file < 0) if (wav_file < 0)
{ {
wav_file = -1; wav_file = -1;
logf("create failed: %d", wav_file); logf("rec: create failed: %d", wav_file);
is_error = true;
return -1; return -1;
} }
@ -464,8 +555,9 @@ static int start_wave(void)
{ {
close(wav_file); close(wav_file);
wav_file = -1; wav_file = -1;
logf("write failed"); logf("rec: write failed");
return -2; is_error = true;
return -1;
} }
return 0; return 0;
@ -476,16 +568,19 @@ static void close_wave(void)
{ {
long l; long l;
l = htole32(num_rec_bytes + 36); if (wav_file != -1)
{
l = htole32(num_file_bytes + 36);
lseek(wav_file, 4, SEEK_SET); lseek(wav_file, 4, SEEK_SET);
write(wav_file, &l, 4); write(wav_file, &l, 4);
l = htole32(num_rec_bytes); l = htole32(num_file_bytes);
lseek(wav_file, 40, SEEK_SET); lseek(wav_file, 40, SEEK_SET);
write(wav_file, &l, 4); write(wav_file, &l, 4);
close(wav_file); close(wav_file);
wav_file = -1; wav_file = -1;
}
} }
static void pcmrec_start(void) static void pcmrec_start(void)
@ -493,74 +588,206 @@ static void pcmrec_start(void)
logf("pcmrec_start"); logf("pcmrec_start");
if (is_recording) if (is_recording)
{
logf("already recording");
record_done = true;
return; return;
}
if (wav_file != -1) if (wav_file != -1)
close(wav_file); close(wav_file);
logf("rec: %s", recording_filename); if (start_wave() != 0)
{
start_wave(); /* todo: send signal to pcm_record if we have failed */ /* failed to create the file */
record_done = true;
return;
}
num_rec_bytes = 0; num_rec_bytes = 0;
num_file_bytes = 0;
/* Store the current time */
record_start_time = current_tick; record_start_time = current_tick;
pause_start_time = 0;
write_index = 0; write_index = 0;
read_index = 0; read_index = 0;
read2_index = 0;
is_stopping = 0; is_stopping = false;
is_paused = 0; is_paused = false;
is_recording = 1; is_recording = true;
pcmrec_dma_start(); pcmrec_dma_start();
record_done = true;
} }
static void pcmrec_stop(void) static void pcmrec_stop(void)
{ {
/* wait for recording to finish */
/* todo: Abort current DMA transfer using DCR1.. */
logf("pcmrec_stop"); logf("pcmrec_stop");
while (is_recording) if (!is_recording)
{ {
sleep(HZ >> 4); stop_done = true;
return;
} }
logf("pcmrec_stop done"); if (!is_paused)
{
/* wait for recording to finish */
is_stopping = true;
/* Write unfinished buffers to file */ while (is_stopping && is_recording)
sleep_thread();
wake_up_thread();
is_stopping = false;
}
is_recording = false;
/* Flush buffers to file */
pcmrec_callback(true); pcmrec_callback(true);
close_wave(); close_wave();
is_stopping = 0; stop_done = true;
logf("pcmrec_stop done");
} }
static void pcmrec_open(void) static void pcmrec_new_file(void)
{ {
unsigned long buffer_start; logf("pcmrec_new_file");
int i;
if (!is_recording)
{
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();
num_file_bytes = 0;
/* start the new file */
if (start_wave() != 0)
{
logf("new_file failed");
pcmrec_stop();
}
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");
}
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");
}
/**
* audio_init_recording calls this function using PCMREC_INIT
*
*/
static void pcmrec_init(void)
{
unsigned long buffer_size;
show_waveform = 0; show_waveform = 0;
is_recording = 0;
is_stopping = 0;
num_rec_bytes = 0;
wav_file = -1; wav_file = -1;
read_index = 0; read_index = 0;
read2_index = 0;
write_index = 0; write_index = 0;
buffer_start = (unsigned long)(&audiobuf[(audiobufend - audiobuf) - (ALL_BUFFERS_SIZE + 16)]); num_rec_bytes = 0;
buffer_start &= ~3; num_file_bytes = 0;
record_start_time = 0;
pause_start_time = 0;
for (i=0; i<NUM_BUFFERS; i++) is_recording = false;
{ is_stopping = false;
rec_buffers[i] = (unsigned char*)(buffer_start + EACH_BUFFER_SIZE * i); is_paused = false;
} is_error = false;
rec_buffer = (unsigned char*)(((unsigned long)audiobuf) & ~3);
buffer_size = (long)audiobufend - (long)audiobuf - 16;
//buffer_size = 1024*1024*5;
logf("buf size: %d kb", buffer_size/1024);
num_chunks = buffer_size / CHUNK_SIZE;
logf("num_chunks: %d", num_chunks);
IIS1CONFIG = 0x800; /* Stop any playback */ IIS1CONFIG = 0x800; /* Stop any playback */
AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */ AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
@ -578,16 +805,13 @@ static void pcmrec_open(void)
static void pcmrec_close(void) static void pcmrec_close(void)
{ {
#if defined(HAVE_UDA1380)
uda1380_disable_recording(); uda1380_disable_recording();
#elif defined(HAVE_TLV320)
tlv320_disable_recording();
#endif
DMAROUTE = (DMAROUTE & 0xffff00ff); DMAROUTE = (DMAROUTE & 0xffff00ff);
ICR7 = 0x00; /* Disable interrupt */ ICR7 = 0x00; /* Disable interrupt */
IMR |= (1<<15); /* bit 15 is DMA1 */ IMR |= (1<<15); /* bit 15 is DMA1 */
close_done = true;
} }
static void pcmrec_thread(void) static void pcmrec_thread(void)
@ -596,14 +820,17 @@ static void pcmrec_thread(void)
logf("thread pcmrec start"); logf("thread pcmrec start");
int_count = 0;
error_count = 0;
while (1) while (1)
{ {
queue_wait(&pcmrec_queue, &ev); queue_wait(&pcmrec_queue, &ev);
switch (ev.id) switch (ev.id)
{ {
case PCMREC_OPEN: case PCMREC_INIT:
pcmrec_open(); pcmrec_init();
break; break;
case PCMREC_CLOSE: case PCMREC_CLOSE:
@ -619,25 +846,18 @@ static void pcmrec_thread(void)
break; break;
case PCMREC_PAUSE: case PCMREC_PAUSE:
/* todo */ pcmrec_pause();
break; break;
case PCMREC_RESUME: case PCMREC_RESUME:
/* todo */ pcmrec_resume();
break; break;
case PCMREC_NEW_FILE: case PCMREC_NEW_FILE:
/* todo */ pcmrec_new_file();
break;
case PCMREC_SET_GAIN:
#if defined(HAVE_UDA1380)
uda1380_set_recvol(rec_gain, rec_gain, rec_volume);
#elif defined(HAVE_TLV320)
/* ToDo */
#endif
break; break;
/* Notification by DMA interrupt */
case PCMREC_GOT_DATA: case PCMREC_GOT_DATA:
pcmrec_callback(false); pcmrec_callback(false);
break; break;
@ -655,7 +875,8 @@ static void pcmrec_thread(void)
logf("thread pcmrec done"); logf("thread pcmrec done");
} }
void pcmrec_set_mux(int source) /* Select VINL & VINR source: 0=Line-in, 1=FM Radio */
void pcm_rec_mux(int source)
{ {
if(source == 0) if(source == 0)
and_l(~0x00800000, &GPIO_OUT); /* Line In */ and_l(~0x00800000, &GPIO_OUT); /* Line In */

View file

@ -84,6 +84,12 @@ static const struct sound_settings_info sound_settings_table[] = {
[SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 15, 8, NULL}, [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 15, 8, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 15, 8, NULL}, [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 15, 8, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 15, 2, NULL}, [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 15, 2, NULL},
#elif defined(HAVE_UDA1380)
[SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 8, 8, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 8, 8, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 15, 2, NULL},
[SOUND_ADC_LEFT_GAIN] = {"dB", 1, 1,-128, 48, 0, NULL},
[SOUND_ADC_RIGHT_GAIN]= {"dB", 1, 1,-128, 48, 0, NULL},
#endif #endif
}; };
@ -668,6 +674,30 @@ int sound_val2phys(int setting, int value)
break; break;
} }
return result; return result;
#elif defined(HAVE_UDA1380)
int result = 0;
switch(setting)
{
case SOUND_LEFT_GAIN:
case SOUND_RIGHT_GAIN:
result = value * 30; /* (24/8) *10 */
break;
case SOUND_MIC_GAIN:
result = value * 20; /* (30/15) *10 */
break;
case SOUND_ADC_LEFT_GAIN:
case SOUND_ADC_RIGHT_GAIN:
result = value * 5; /* (1/2) *10 */
break;
default:
result = value;
break;
}
return result;
#else #else
(void)setting; (void)setting;
return value; return value;