https://bitbucket.org/hudson/magic-lantern
Tip revision: 989de1b205e0455a30ab8e9d9c1277d67c285b3d authored by a1ex on 22 February 2014, 00:05:50 UTC
Close branch ml-cbr-backend.
Close branch ml-cbr-backend.
Tip revision: 989de1b
audio-common.c
#include "dryos.h"
#include "lvinfo.h"
#define SOUND_RECORDING_ENABLED (sound_recording_mode != 1) // not 100% sure
#if defined(CONFIG_500D) || defined(CONFIG_5D3) || defined(CONFIG_7D) || defined(CONFIG_650D) || defined(CONFIG_700D) || defined(CONFIG_100D)
int audio_thresholds[] = { 0x7fff, 0x7213, 0x65ab, 0x5a9d, 0x50c2, 0x47fa, 0x4026, 0x392c, 0x32f4, 0x2d6a, 0x2879, 0x2412, 0x2026, 0x1ca7, 0x1989, 0x16c2, 0x1449, 0x1214, 0x101d, 0xe5c, 0xccc, 0xb68, 0xa2a, 0x90f, 0x813, 0x732, 0x66a, 0x5b7, 0x518, 0x48a, 0x40c, 0x39b, 0x337, 0x2dd, 0x28d, 0x246, 0x207, 0x1ce, 0x19c, 0x16f, 0x147 };
#endif
void audio_configure(int force);
static void volume_display();
static void audio_monitoring_display_headphones_connected_or_not();
static void audio_menus_init();
#ifdef FEATURE_HEADPHONE_MONITORING
static void audio_monitoring_update();
#endif
static void audio_input_toggle( void * priv, int delta );
#ifdef CONFIG_600D
//Prototypes for 600D
static void audio_ic_set_lineout_onoff(int op_mode);
static void audio_ic_set_lineout_vol();
static void audio_ic_set_input(int op_mode);
#else
static inline unsigned mgain_index2gain(int index);
static inline unsigned mgain_index2bits(int index);
static inline uint8_t audio_gain_to_cmd(int gain);
#endif
// Dump the audio registers to a file if defined
#undef CONFIG_AUDIO_REG_LOG
// Or on the scren
#undef CONFIG_AUDIO_REG_BMP
struct gain_struct
{
struct semaphore * sem;
unsigned alc1;
unsigned sig1;
unsigned sig2;
};
static struct gain_struct gain = {
.sem = (void*) 1,
};
static CONFIG_INT( "audio.lovl", lovl, 0 );
static CONFIG_INT( "audio.alc-enable", alc_enable, 0 );
static int loopback = 1;
static CONFIG_INT( "audio.input-choice", input_choice, 4 ); //0=internal; 1=L int, R ext; 2 = stereo ext; 3 = L int, R ext balanced, 4 = auto (0 or 1)
static CONFIG_INT( "audio.filters", enable_filters, 1 ); //disable the HPF, LPF and pre-emphasis filters
#define cfg_draw_meters 1
static CONFIG_INT("audio.monitoring", audio_monitoring, 1);
static int do_draw_meters = 0;
static struct audio_level audio_levels[2];
struct audio_level *get_audio_levels(void)
{
return audio_levels;
}
// from linux snd_soc_update_bits()
static void masked_audio_ic_write(
unsigned reg, // the register we wish to manipulate (eg AUDIO_IC_SIG1)
unsigned mask, // the range of bits we want to manipulate (eg 0x05 or b0000111) to only allow changes to b3,b2,b0
unsigned bits // the bits we wish to set (eg 0x02 or b000010 to set b1, while clearing others within scope of the mask)
)
{
unsigned old,new; // variable to store current register value
old = audio_ic_read(reg-0x100); // read current register value
new = (old &~mask) | (bits & mask);
_audio_ic_write(reg | new); // bitwise OR everything together and call _audio_ic_write function
}
/** Returns a dB translated from the raw level
*
* Range is -40 to 0 dB
*/
int
audio_level_to_db(
int raw_level
)
{
int db;
for( db = 40 ; db ; db-- )
{
if( audio_thresholds[db] > raw_level )
return -db;
}
return 0;
}
#ifdef OSCOPE_METERS
void draw_meters(void)
{
#define MAX_SAMPLES 720
static int16_t levels[ MAX_SAMPLES ];
static uint32_t index;
levels[ index++ ] = audio_read_level();
if( index >= MAX_SAMPLES )
index = 0;
struct vram_info * vram = &vram_info[ vram_get_number(2) ];
//thunk audio_dev_compute_average_level = (void*) 0xFF9725C4;
//audio_dev_compute_average_level();
// The level goes from -40 to 0
uint32_t x;
for( x=0 ; x<MAX_SAMPLES && x<vram->width ; x++ )
{
uint16_t y = 256 + levels[ x ] / 128;
vram->vram[ y * vram->pitch + x ] = 0xFFFF;
}
uint32_t y;
for( y=0 ; y<128 ; y++ )
{
vram->vram[ y * vram->pitch + index ] = 0x888F;
}
}
#else
static char left_label[10] = "LEFT ";
static char right_label[10] = "RIGHT";
static uint8_t
db_to_color(
int db
)
{
if( db < -25 )
return 0x2F; // white
if( db < -12 )
return 0x06; // dark green
if( db < -3 )
return 0x0F; // yellow
return 0x0c; // dull red
}
static uint8_t
db_peak_to_color(
int db
)
{
if( db < -25 )
return 11; // dark blue
if( db < -12 )
return 11; // dark blue
if( db < -3 )
return 15; // bright yellow
return 0x08; // bright red
}
static void
draw_meter(
int x_origin,
int y_origin,
int meter_height,
struct audio_level * level,
char * label,
unsigned int width
)
{
const uint32_t pitch = BMPPITCH;
uint32_t * row = (uint32_t*) bmp_vram();
if( !row )
return;
// Skip to the desired y coord and over the
// space for the numerical levels
// .. and the space for showing the channel and source.
row += (pitch/4) * y_origin + AUDIO_METER_OFFSET + x_origin/4;
const int32_t db_peak_fast = audio_level_to_db( level->peak_fast );
const int32_t db_peak = audio_level_to_db( level->peak );
// levels go from -40 to 0
const int32_t x_db_peak_fast = ((int32_t)width + db_peak_fast * (int32_t)width / 40) / 4;
const int32_t x_db_peak = ((int32_t)width + db_peak * (int32_t)width / 40) / 4;
const uint8_t bar_color = db_to_color( db_peak_fast );
const uint8_t peak_color = db_peak_to_color( db_peak );
const uint32_t bar_color_word = color_word( bar_color );
const uint32_t peak_color_word = color_word( peak_color );
const uint32_t bg_color_word = color_word(COLOR_BLACK);
// Write the meter an entire scan line at a time
int32_t y;
for(y = 0; y < (int32_t)meter_height; y++)
{
int32_t x;
for(x = 0; x < (int32_t)(width / 4); x++)
{
if( x < x_db_peak_fast )
{
row[x] = bar_color_word;
}
else if( x < x_db_peak )
{
row[x] = bg_color_word;
}
else if( x < x_db_peak + 4 )
{
row[x] = peak_color_word;
}
else
{
row[x] = bg_color_word;
}
}
row += pitch / 4;
}
// Write the current level
bmp_printf( FONT(FONT_SMALL, COLOR_WHITE, COLOR_BLACK), x_origin, y_origin, "%s %02d", label, MIN(db_peak, -1) );
}
static void
draw_ticks(
int x,
int y,
int tick_height,
int width
)
{
const uint32_t pitch = BMPPITCH;
uint32_t * row = (uint32_t*) bmp_vram();
if( !row )
return;
row += (pitch/4) * y + AUDIO_METER_OFFSET - 2 + x/4;//seems to need less of an offset
const uint32_t white_word = 0
| ( COLOR_WHITE << 24 )
| ( COLOR_WHITE << 16 )
| ( COLOR_WHITE << 8 )
| ( COLOR_WHITE << 0 );
for( ; tick_height > 0 ; tick_height--, row += pitch/4 )
{
int db;
for( db=-40; db<= 0 ; db+=5 )
{
const uint32_t x_db = width + db * width / 40;
row[x_db/4] = white_word;
}
}
}
static int audio_cmd_to_gain_x1000(int cmd);
/* these will be set by the LV info bar manager */
static int audio_meter_x = INT_MIN;
static int audio_meter_y = INT_MIN;
static int audio_meter_width = INT_MIN;
static int audio_meters_are_drawn_common()
{
#ifdef FEATURE_AUDIO_METERS
if (!SOUND_RECORDING_ENABLED && !fps_should_record_wav())
return 0;
#if defined(CONFIG_7D)
if(!recording)
{
return 0;
}
#endif
if (gui_menu_shown())
{
return is_menu_active("Audio");
}
else
{
return is_movie_mode() && do_draw_meters && zebra_should_run() && !get_halfshutter_pressed();
}
#else
return 0;
#endif
}
/* Normal VU meter */
static void draw_meters(void)
{
// The db values are multiplied by 8 to make them
// smoother.
int x0 = audio_meter_x;
int y0 = audio_meter_y;
int width = audio_meter_width;
if (menu_active_and_not_hidden()) /* not managed by LV info bars; show the meters in the help bar */
{
x0 = 0;
y0 = 457;
width = 640; /* can be full-screen */
}
if (x0 == INT_MIN || y0 == INT_MIN || width == INT_MIN)
{
/* don't know yet where to draw */
return;
}
draw_meter( x0, y0 + 0, 10, &audio_levels[0], left_label, width);
draw_ticks( x0, y0 + 10, 3, width);
#if !(defined(CONFIG_500D) || defined(CONFIG_1100D)) // mono mic on 500d and 1100d
draw_meter( x0, y0 + 12, 10, &audio_levels[1], right_label, width);
#endif
if (gui_menu_shown() && alc_enable)
{
#ifdef CONFIG_600D
int dgain_x1000 = audio_cmd_to_gain_x1000(audio_ic_read(ML_ALC_TARGET_LEV-0x100));
#else
int dgain_x1000 = audio_cmd_to_gain_x1000(audio_ic_read(AUDIO_IC_ALCVOL));
#endif
bmp_printf(FONT_MED, 10, 410, "AGC:%s%d.%03d dB", dgain_x1000 < 0 ? "-" : " ", ABS(dgain_x1000) / 1000, ABS(dgain_x1000) % 1000);
}
}
static LVINFO_UPDATE_FUNC(audio_meter_update)
{
/* this is more like a placeholder; it's used to integrate the meters in the top bar, without conflicting with other elements,
* but for quick refreshes we have a different task that redraws just the meters, not the entire bar */
if (audio_meters_are_drawn())
{
/* if it's alone, draw it big; if not, draw it smaller */
int is_big = fontspec_width(item->fontspec) >= fontspec_width(FONT_MED_LARGE);
int label_width = AUDIO_METER_OFFSET*4;
item->width = MIN((is_big ? 720 : 360) / 40 * 40, 720-item->x);
audio_meter_x = item->x - item->width/2;
audio_meter_y = item->y;
audio_meter_width = item->width - label_width;
item->custom_drawing = 1;
if (can_draw)
{
/* could be a little cleaner, but should do the job as long as draw_meters is thread-safe */
draw_meters();
}
}
}
static struct lvinfo_item info_items[] = {
{
.name = "Audio meters",
.which_bar = LV_TOP_BAR_ONLY,
.update = audio_meter_update,
.preferred_position = -128,
.priority = 2,
},
};
static void audio_meter_init()
{
lvinfo_add_item(info_items);
}
INIT_FUNC("audio.meter.init", audio_meter_init);
#endif
static int
audio_cmd_to_gain_x1000(
int cmd
)
{
int gain_x1000 = (cmd - 145) * 375;
return gain_x1000;
}
static void
compute_audio_levels(
int ch
)
{
struct audio_level * const level = &audio_levels[ch];
int raw = audio_read_level( ch );
if( raw < 0 )
raw = -raw;
level->last = raw;
level->avg = (level->avg * 15 + raw) / 16;
if( raw > level->peak )
level->peak = raw;
if( raw > level->peak_fast )
level->peak_fast = raw;
// Decay the peak to the average
level->peak = ( level->peak * 63 + level->avg ) / 64;
level->peak_fast = ( level->peak_fast * 7 + level->avg ) / 8;
}
/** Task to monitor the audio levels.
*
* Compute the average and peak level, periodically calling
* the draw_meters() function to display the results on screen.
* \todo Check that we have live-view enabled and the TFT is on
* before drawing.
*/
static void
meter_task( void* unused )
{
#ifdef CONFIG_600D
//initialize audio config for 600D
audio_configure(1);
#endif
TASK_LOOP
{
msleep(DISPLAY_IS_ON ? 50 : 500);
if (audio_meters_are_drawn())
{
if (!is_mvr_buffer_almost_full())
BMP_LOCK( draw_meters(); )
}
if (audio_monitoring)
{
static int hp = 0;
int h = AUDIO_MONITORING_HEADPHONES_CONNECTED;
if (h != hp)
{
audio_monitoring_display_headphones_connected_or_not();
}
hp = h;
}
}
}
TASK_CREATE( "audio_meter_task", meter_task, 0, 0x18, 0x1000 );
/** Monitor the audio levels very quickly */
static void
compute_audio_level_task( void* unused )
{
audio_levels[0].peak = audio_levels[1].peak = 0;
audio_levels[0].avg = audio_levels[1].avg = 0;
TASK_LOOP
{
msleep(MIN_MSLEEP);
compute_audio_levels( 0 );
compute_audio_levels( 1 );
}
}
TASK_CREATE( "audio_level_task", compute_audio_level_task, 0, 0x18, 0x1000 );
/** Write the MGAIN2-0 bits.
* Table 19 for the gain values (variable "bits"):
*
* 0 == +0 dB
* 1 == +20 dB
* 2 == +26 dB
* 3 == +32 dB
* 4 == +10 dB
* 5 == +17 dB
* 6 == +23 dB
* 7 == +29 dB
*
* Why is it split between two registers? I don't know.
==================================
* 500d mono chip gain settings - by: coutts
0 == +0 dB
1 == +20 dB
2 == +26 dB
3 == +32 dB
4 == +10 dB
5 == +17 dB
6 == +23 dB
7 == +29 dB
8 == +3 dB
9 == +6 dB
*/
#if defined(CONFIG_AUDIO_REG_LOG) || defined(CONFIG_AUDIO_REG_BMP)
// Do not write the value; just read them and record to a logfile
static uint16_t audio_regs[] = {
AUDIO_IC_PM1,
AUDIO_IC_PM2,
AUDIO_IC_SIG1,
AUDIO_IC_SIG2,
AUDIO_IC_ALC1,
AUDIO_IC_ALC2,
AUDIO_IC_IVL,
AUDIO_IC_IVR,
AUDIO_IC_OVL,
AUDIO_IC_OVR,
AUDIO_IC_ALCVOL,
AUDIO_IC_MODE3,
AUDIO_IC_MODE4,
AUDIO_IC_PM3,
AUDIO_IC_FIL1,
AUDIO_IC_HPF0,
AUDIO_IC_HPF1,
AUDIO_IC_HPF2,
AUDIO_IC_HPF3,
AUDIO_IC_LPF0,
AUDIO_IC_LPF1,
AUDIO_IC_LPF2,
AUDIO_IC_LPF3,
};
static const char * audio_reg_names[] = {
"AUDIO_IC_PM1",
"AUDIO_IC_PM2",
"AUDIO_IC_SIG1",
"AUDIO_IC_SIG2",
"AUDIO_IC_ALC1",
"AUDIO_IC_ALC2",
"AUDIO_IC_IVL",
"AUDIO_IC_IVR",
"AUDIO_IC_OVL",
"AUDIO_IC_OVR",
"AUDIO_IC_ALCVOL",
"AUDIO_IC_MODE3",
"AUDIO_IC_MODE4",
"AUDIO_IC_PM3",
"AUDIO_IC_FIL1",
"AUDIO_IC_HPF0",
"AUDIO_IC_HPF1",
"AUDIO_IC_HPF2",
"AUDIO_IC_HPF3",
"AUDIO_IC_LPF0",
"AUDIO_IC_LPF1",
"AUDIO_IC_LPF2",
"AUDIO_IC_LPF3",
};
static FILE * reg_file;
static void
audio_reg_dump( int force )
{
if( !reg_file )
return;
static uint16_t last_regs[ COUNT(audio_regs) ];
unsigned i;
int output = 0;
for( i=0 ; i<COUNT(audio_regs) ; i++ )
{
const uint16_t reg = audio_ic_read( audio_regs[i] );
if( reg != last_regs[i] || force )
{
my_fprintf(
reg_file,
"%s %02x\n",
audio_reg_names[i],
reg
);
output = 1;
}
last_regs[i] = reg;
}
if( output )
my_fprintf( reg_file, "%s\n", "" );
}
static void
audio_reg_close( void )
{
if( reg_file )
FIO_CloseFile( reg_file );
reg_file = NULL;
}
static void
audio_reg_dump_screen()
{
int i, x, y;
for( i=0 ; i<COUNT(audio_regs) ; i++ )
{
const uint16_t reg = audio_ic_read( audio_regs[i] );
x = 10 + (i / 30) * 200;
y = 50 + (i % 30) * 12;
bmp_printf(FONT_SMALL, x, y,
"%s %02x\n",
audio_reg_names[i],
reg
);
}
}
#endif
#if defined(CONFIG_600D) && defined(CONFIG_AUDIO_600D_DEBUG)
static uint16_t audio_regs_once[] = {
ML_SMPLING_RATE-0x100,
ML_PLLNL-0x100,
ML_PLLNH-0x100,
ML_PLLML-0x100,
ML_PLLMH-0x100,
ML_PLLDIV-0x100,
ML_CLK_EN-0x100,
ML_CLK_CTL-0x100,
ML_SW_RST-0x100,
ML_RECPLAY_STATE-0x100,
ML_MIC_IN_CHARG_TIM-0x100,
ML_PW_REF_PW_MNG-0x100,
ML_PW_IN_PW_MNG-0x100,
ML_PW_DAC_PW_MNG-0x100,
ML_PW_SPAMP_PW_MNG-0x100,
ML_PW_ZCCMP_PW_MNG-0x100,
ML_MICBIAS_VOLT-0x100,
ML_MIC_IN_VOL-0x100,
ML_MIC_BOOST_VOL1-0x100,
ML_MIC_BOOST_VOL2-0x100,
ML_SPK_AMP_VOL-0x100,
ML_HP_AMP_VOL-0x100,
ML_AMP_VOLFUNC_ENA-0x100,
ML_AMP_VOL_FADE-0x100,
ML_SPK_AMP_OUT-0x100,
ML_HP_AMP_OUT_CTL-0x100,
ML_MIC_IF_CTL-0x100,
ML_RCH_MIXER_INPUT-0x100,
ML_LCH_MIXER_INPUT-0x100,
ML_RECORD_PATH-0x100,
ML_SAI_TRANS_CTL-0x100,
ML_SAI_RCV_CTL-0x100,
ML_SAI_MODE_SEL-0x100,
ML_FILTER_EN-0x100,
ML_FILTER_DIS_ALL-0x100,
ML_DVOL_CTL_FUNC_EN-0x100,
ML_MIXER_VOL_CTL-0x100,
ML_REC_DIGI_VOL-0x100,
ML_REC_LR_BAL_VOL-0x100,
ML_PLAY_DIG_VOL-0x100,
ML_EQ_GAIN_BRAND0-0x100,
ML_EQ_GAIN_BRAND1-0x100,
ML_EQ_GAIN_BRAND2-0x100,
ML_EQ_GAIN_BRAND3-0x100,
ML_EQ_GAIN_BRAND4-0x100,
ML_HPF2_CUTOFF-0x100,
ML_EQBRAND0_F0L-0x100,
ML_EQBRAND0_F0H-0x100,
ML_EQBRAND0_F1L-0x100,
ML_EQBRAND0_F1H-0x100,
ML_EQBRAND1_F0L-0x100,
ML_EQBRAND1_F0H-0x100,
ML_EQBRAND1_F1L-0x100,
ML_EQBRAND1_F1H-0x100,
ML_EQBRAND2_F0L-0x100,
ML_EQBRAND2_F0H-0x100,
ML_EQBRAND2_F1L-0x100,
ML_EQBRAND2_F1H-0x100,
ML_EQBRAND3_F0L-0x100,
ML_EQBRAND3_F0H-0x100,
ML_EQBRAND3_F1L-0x100,
ML_EQBRAND3_F1H-0x100,
ML_EQBRAND4_F0L-0x100,
ML_EQBRAND4_F0H-0x100,
ML_EQBRAND4_F1L-0x100,
ML_EQBRAND4_F1H-0x100,
ML_MIC_PARAM10-0x100,
ML_MIC_PARAM11-0x100,
ML_SND_EFFECT_MODE-0x100,
ML_ALC_MODE-0x100,
ML_ALC_ATTACK_TIM-0x100,
ML_ALC_DECAY_TIM-0x100,
ML_ALC_HOLD_TIM-0x100,
ML_ALC_TARGET_LEV-0x100,
ML_ALC_MAXMIN_GAIN-0x100,
ML_NOIS_GATE_THRSH-0x100,
ML_ALC_ZERO_TIMOUT-0x100,
ML_PL_ATTACKTIME-0x100,
ML_PL_DECAYTIME-0x100,
ML_PL_TARGET_LEVEL-0x100,
ML_PL_MAXMIN_GAIN-0x100,
ML_PLYBAK_BOST_VOL-0x100,
ML_PL_0CROSS_TIMEOUT-0x100,
};
static const char * audio_reg_names_once[] = {
"ML_SMPLING_RATE",
"ML_PLLNL",
"ML_PLLNH",
"ML_PLLML",
"ML_PLLMH",
"ML_PLLDIV",
"ML_CLK_EN",
"ML_CLK_CTL",
"ML_SW_RST",
"ML_RECPLAY_STATE",
"ML_MIC_IN_CHARG_TIM",
"ML_PW_REF_PW_MNG",
"ML_PW_IN_PW_MNG",
"ML_PW_DAC_PW_MNG",
"ML_PW_SPAMP_PW_MNG",
"ML_PW_ZCCMP_PW_MNG",
"ML_MICBIAS_VOLT",
"ML_MIC_IN_VOL",
"ML_MIC_BOOST_VOL1",
"ML_MIC_BOOST_VOL2",
"ML_SPK_AMP_VOL",
"ML_HP_AMP_VOL",
"ML_AMP_VOLFUNC_ENA",
"ML_AMP_VOL_FADE",
"ML_SPK_AMP_OUT",
"ML_HP_AMP_OUT_CTL",
"ML_MIC_IF_CTL",
"ML_RCH_MIXER_INPUT",
"ML_LCH_MIXER_INPUT",
"ML_RECORD_PATH",
"ML_SAI_TRANS_CTL",
"ML_SAI_RCV_CTL",
"ML_SAI_MODE_SEL",
"ML_FILTER_EN",
"ML_FILTER_DIS_ALL",
"ML_DVOL_CTL_FUNC_EN",
"ML_MIXER_VOL_CTL",
"ML_REC_DIGI_VOL",
"ML_REC_LR_BAL_VOL",
"ML_PLAY_DIG_VOL",
"ML_EQ_GAIN_BRAND0",
"ML_EQ_GAIN_BRAND1",
"ML_EQ_GAIN_BRAND2",
"ML_EQ_GAIN_BRAND3",
"ML_EQ_GAIN_BRAND4",
"ML_HPF2_CUTOFF",
"ML_EQBRAND0_F0L",
"ML_EQBRAND0_F0H",
"ML_EQBRAND0_F1L",
"ML_EQBRAND0_F1H",
"ML_EQBRAND1_F0L",
"ML_EQBRAND1_F0H",
"ML_EQBRAND1_F1L",
"ML_EQBRAND1_F1H",
"ML_EQBRAND2_F0L",
"ML_EQBRAND2_F0H",
"ML_EQBRAND2_F1L",
"ML_EQBRAND2_F1H",
"ML_EQBRAND3_F0L",
"ML_EQBRAND3_F0H",
"ML_EQBRAND3_F1L",
"ML_EQBRAND3_F1H",
"ML_EQBRAND4_F0L",
"ML_EQBRAND4_F0H",
"ML_EQBRAND4_F1L",
"ML_EQBRAND4_F1H",
"ML_MIC_PARAM10",
"ML_MIC_PARAM11",
"ML_SND_EFFECT_MODE",
"ML_ALC_MODE",
"ML_ALC_ATTACK_TIM",
"ML_ALC_DECAY_TIM",
"ML_ALC_HOLD_TIM",
"ML_ALC_TARGET_LEV",
"ML_ALC_MAXMIN_GAIN",
"ML_NOIS_GATE_THRSH",
"ML_ALC_ZERO_TIMOUT",
"ML_PL_ATTACKTIME",
"ML_PL_DECAYTIME",
"ML_PL_TARGET_LEVEL",
"ML_PL_MAXMIN_GAIN",
"ML_PLYBAK_BOST_VOL",
"ML_PL_0CROSS_TIMEOUT",
};
void
audio_reg_dump_once()
{
char log_filename[100];
int log_number = 0;
for (log_number = 0; log_number < 100; log_number++)
{
snprintf(log_filename, sizeof(log_filename), CARD_DRIVE "ML/audio%02d.LOG", log_number);
unsigned size;
if( FIO_GetFileSize( log_filename, &size ) != 0 ) break;
if (size == 0) break;
}
FILE* f = FIO_CreateFileEx(log_filename);
unsigned i;
for( i=0 ; i<COUNT(audio_regs_once) ; i++ )
{
const uint16_t reg = audio_ic_read( audio_regs_once[i] );
my_fprintf(f, "%s %02x\n", audio_reg_names_once[i], reg);
msleep(10);
}
FIO_CloseFile(f);
NotifyBox(4000, "log audio%02d.log saved", log_number );
}
#endif
int mic_inserted = -1;
PROP_HANDLER( PROP_MIC_INSERTED )
{
if (mic_inserted != -1)
{
NotifyBox(2000,
"Microphone %s",
buf[0] ?
"connected" :
"disconnected");
}
mic_inserted = buf[0];
#ifdef CONFIG_600D
audio_ic_set_input(OP_STANDALONE); //Need faster finish this prop on 600D. audio_configure() is slow.Then get hang
#else
audio_configure( 1 );
#endif
//~ menu_set_dirty();
}
static int get_input_source()
{
int input_source;
//setup input_source based on choice and mic pluggedinedness
if (input_choice == 4) {
input_source = mic_inserted ? 2 : 0;
} else {
input_source = input_choice;
}
return input_source;
}
static void
audio_set_meterlabel(){
#if (defined(CONFIG_500D) || defined(CONFIG_1100D)) //500d and 1100d only have internal mono audio :(
int input_source = 0;
#else
int input_source = get_input_source();
#endif
//those char*'s cause a memory corruption, don't know why
//char * left_labels[] = {"L INT", "L INT", "L EXT", "L INT"}; //these are used by draw_meters()
//char * right_labels[] = {"R INT", "R EXT", "R EXT", "R BAL"}; //but defined and set here, because a change to the pm3 array should be changed in them too.
switch (input_source)
{
case 0:
#if (defined(CONFIG_500D) || defined(CONFIG_1100D))
snprintf(left_label, sizeof(left_label), " MIC ");
snprintf(right_label, sizeof(right_label), " N/C ");
#else
snprintf(left_label, sizeof(left_label), "L INT");
snprintf(right_label, sizeof(right_label), "R INT");
#endif
break;
case 1:
snprintf(left_label, sizeof(left_label), "L INT");
snprintf(right_label, sizeof(right_label), "R EXT");
break;
case 2:
snprintf(left_label, sizeof(left_label), "L EXT");
snprintf(right_label, sizeof(right_label), "R EXT");
break;
case 3:
snprintf(left_label, sizeof(left_label), "L INT");
snprintf(right_label, sizeof(right_label), "R BAL");
break;
}
}
#if 0
static void
audio_o2gain_display( void * priv, int x, int y, int selected )
{
static uint8_t gains[] = { 0, 6, 12, 18 };
unsigned gain_reg= *(unsigned*) priv;
gain_reg &= 0x3;
bmp_printf(
selected ? MENU_FONT_SEL : MENU_FONT,
x, y,
//23456789
"o2gain: -%2d dB",
gains[ gain_reg ]
);
}
#endif
static const char* get_audio_input_string()
{
switch(input_choice) {
case 0:
return "Internal mic";
case 1:
return "L:int R:ext";
case 2:
return "External Stereo";
case 3:
return "L:int R:balanced";
case 4:
return (mic_inserted? "Auto int/EXT" : "Auto INT/ext");
default:
break;
}
return "error";
}
static void audio_monitoring_force_display(int x)
{
#ifdef HOTPLUG_VIDEO_OUT_PROP_DELIVER_ADDR
prop_deliver(*(int*)(HOTPLUG_VIDEO_OUT_PROP_DELIVER_ADDR), &x, 4, 0x0);
#endif
}
void audio_monitoring_display_headphones_connected_or_not()
{
NotifyBox(2000,
"Headphones %s",
AUDIO_MONITORING_HEADPHONES_CONNECTED ?
"connected" :
"disconnected");
#ifdef CONFIG_600D
audio_configure(1);
#endif
}
PROP_INT(PROP_USBRCA_MONITOR, rca_monitor);
static void
audio_monitoring_toggle( void * priv, int delta )
{
audio_monitoring = !audio_monitoring;
audio_monitoring_update(); //call audio_monitoring_force_display()
}
static void
enable_recording(int mode)
{
switch( mode )
{
case 0:
// Movie recording stopped; (fallthrough)
case 2:
// Movie recording started
#ifdef CONFIG_600D
audio_configure(1);
#else
give_semaphore( gain.sem );
#endif
break;
case 1:
// Movie recording about to start? : 600D do not override audio here. Recording start/stop will call case2 and case 2 together. So twice audio_configre() need more cpu/mem overhead. will stop recording.because buffer will full.
break;
default:
// Uh?
break;
}
}
// to be called from some other tasks that may mess with audio
static void audio_force_reconfigure()
{
give_semaphore( gain.sem );
}
static void
enable_meters(int mode)
{
loopback = do_draw_meters = !mode;
#if !defined(CONFIG_600D)
audio_configure( 1 );
#endif
}
PROP_HANDLER( PROP_LV_ACTION )
{
const unsigned mode = buf[0];
enable_meters( mode );
}
PROP_HANDLER( PROP_MVR_REC_START )
{
const unsigned mode = buf[0];
enable_recording( mode );
}
void input_toggle()
{
#ifdef FEATURE_INPUT_SOURCE
audio_input_toggle(&input_choice, 1);
NotifyBox(2000, "Input: %s", get_audio_input_string());
#endif
}