Raw File
audio.c.500d_under_construction.c
/** \file
 * Onscreen audio meters
 */
/*
 * Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */
// include files
#include "dryos.h"
#include "bmp.h"
#include "config.h"
#include "property.h"
#include "menu.h"
#include "gui.h"


// ML config variables - stored in magic.cfg on the card.
CONFIG_INT( "audio.mgain",	mgain,		5 );
CONFIG_INT( "audio.alc-enable",	alc_enable,	0 );
CONFIG_INT("audio.draw-meters", cfg_draw_meters, 2);
CONFIG_INT( "audio.lovl",	lovl,		0 );
CONFIG_INT("audio.monitoring", audio_monitoring, 1);
CONFIG_INT( "audio.mic-power",	mic_power,	1 );
int loopback = 1;

// Declare some stuff.
static void audio_configure(int force);
void sounddev_task();
static int audio_cmd_to_gain_x1000(int cmd);
static void audio_monitoring_display_headphones_connected_or_not();
static void audio_menus_init();
int do_draw_meters = 0;
char label[10] = "RIGHT ";
PROP_INT(PROP_USBRCA_MONITOR, rca_monitor);

// declare audio_thresholds here since it doesn't exist in the 500d ROM (thanks Alex).
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 };

/****************************
 *	Structure declerations
 ****************************/
struct gain_struct
{
	struct semaphore *	sem;
	unsigned		alc1;
	unsigned		sig1;
	unsigned		sig2;
};

static struct gain_struct gain = {
	.sem			= (void*) 1,
};

struct audio_level audio_levels[2];

struct audio_level *get_audio_levels(void)
{
	return audio_levels;
}	
/*--------------------------------*/


/*****************************************************************************
 * A function that isn't used, but I want to include it because it could be
 * useful to someone else. Taken from original Audio.c file.
 * -From Morgan Look
 *****************************************************************************/
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;                       // variable to store current register value
    old = audio_ic_read(reg);  // read current register value
    old &= ~mask;                     // bitwise AND old value against inverted mask
    bits &= mask;                      // limit scope of new bits with mask
    _audio_ic_write(reg | bits | old);    // bitwise OR everything together and call _audio_ic_write function
}
 
/*----------------------------------------------------------------------------*/ 

///////////////////////////////////////////
// Some functions that will be used later.
///////////////////////////////////////////
int ext_cfg_draw_meters(void)
{
    return cfg_draw_meters;
}

/** Returns a dB translated from the raw level
 *
 * Range is -40 to 0 dB
 */
static 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;
}

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)
{
	const uint32_t width = 560; // bmp_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 int db_peak_fast = audio_level_to_db( level->peak_fast );
	const int db_peak = audio_level_to_db( level->peak );
    
	// levels go from -40 to 0, so -40 * 14 == 560 (=width)
	const uint32_t x_db_peak_fast = (width + db_peak_fast * 14) / 4;
	const uint32_t x_db_peak = (width + db_peak * 14) / 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
	int y;
	for( y=0 ; y<meter_height ; y++, row += pitch/4 )
	{
		uint32_t x;
		for( x=0 ; x<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;
		}
	}
    
	// 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)
{
	const uint32_t width = 560 + 8; // bmp_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 * 14;
			row[x_db/4] = white_word;
		}
	}
}

/* Normal VU meter */
static void draw_meters(void)
{
	int screen_layout = get_screen_layout();
	// The db values are multiplied by 8 to make them
	// smoother.
	int erase = 0;
	int hs = get_halfshutter_pressed();
	static int prev_hs = 0;
	if (hs != prev_hs) erase = 1;
	prev_hs = hs;
	int x0 = 0;
	int y0 = 0;
	int small = 0;
	
	if (gui_menu_shown())
	{
		x0 = os.x0 + os.x_ex/2 - 360;
		y0 = os.y0 + os.y_ex/2 - 240;
		y0 += 350;
		x0 += 10;
	}
	else
	{
		small = hs;
		x0 = os.x0 + os.x_ex/2 - 360;
		if (screen_layout == SCREENLAYOUT_3_2) y0 = os.y0; // just above the 16:9 frame
		else if (screen_layout == SCREENLAYOUT_16_9) { small = 1; y0 = os.y0 + os.off_169; } // meters just below 16:9 border
		else if (screen_layout == SCREENLAYOUT_16_10) {small = 1; y0 = os.y0 + os.off_1610; } // meters just below 16:10 border
		else if (screen_layout == SCREENLAYOUT_UNDER_3_2) y0 = MIN(os.y_max, vram_bm.height - 54);
		else if (screen_layout == SCREENLAYOUT_UNDER_16_9) y0 = MIN(os.y_max - os.off_169, vram_bm.height - 54);
		if (hdmi_code) small = 1;
	}
    
	if (erase)
	{
		bmp_fill(
                 screen_layout >= SCREENLAYOUT_UNDER_3_2 ? BOTTOMBAR_BGCOLOR : TOPBAR_BGCOLOR,
                 x0, y0, 720, small ? 20 : 34
                 );
	}
	else if (hs) return; // will draw top bar instead
	else if (!small)
	{
		// mono mic on 500d :(
		draw_ticks( x0, y0 + 10, 3 );
		draw_meter( x0, y0 + 12, 10, &audio_levels[0], label);
		draw_ticks( x0, y0 + 20, 3 );
	}
	else
	{
		draw_ticks( x0, y0 + 7, 2 );
		draw_meter( x0, y0 + 8, 7, &audio_levels[0], label);
		draw_ticks( x0, y0 + 17, 2 );
	}
	if (gui_menu_shown() && alc_enable)
	{
		int dgain_x1000 = audio_cmd_to_gain_x1000(audio_ic_read(AUDIO_IC_ALCVOL));
		bmp_printf(FONT_MED, 10, 340, "AGC:%s%d.%03d dB", dgain_x1000 < 0 ? "-" : " ", ABS(dgain_x1000) / 1000, ABS(dgain_x1000) % 1000);
	}
}

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;
}

void audio_monitoring_display_headphones_connected_or_not()
{
	NotifyBoxHide();
	NotifyBox(2000,
              "Headphones %s", 
              AUDIO_MONITORING_HEADPHONES_CONNECTED ? 
              "connected" :
              "disconnected");
}

static void audio_monitoring_force_display(int x)
{
	prop_deliver(*(int*)(HOTPLUG_VIDEO_OUT_PROP_DELIVER_ADDR), &x, 4, 0x0);
}

static void audio_monitoring_update()
{
	// kill video connect/disconnect event... or not
	*(int*)HOTPLUG_VIDEO_OUT_STATUS_ADDR = audio_monitoring ? 2 : 0;
	
	if (audio_monitoring && rca_monitor)
	{
		audio_monitoring_force_display(0);
		msleep(1000);
		audio_monitoring_display_headphones_connected_or_not();
	}
}

int audio_meters_are_drawn()
{
	return 
    (
     is_movie_mode() && cfg_draw_meters && do_draw_meters && get_global_draw() && !gui_menu_shown() && lv_dispsize == 1
     )
    ||
    (
     gui_menu_shown() && is_menu_active("Audio") && cfg_draw_meters
     );
}

static inline unsigned mgain_index2gain(int index) // sorted mgain values
{
    static uint8_t gains[] = { 0, 3, 6, 10, 17, 20, 23, 26, 29, 32 };
    return gains[COERCE(index, 0, 10)];
}

static int
audio_cmd_to_gain_x1000(
                        int			cmd
                        )
{
	int gain_x1000 = (cmd - 145) * 375;
	return gain_x1000;
}

static void
enable_meters(int mode)
{
	loopback = do_draw_meters = !mode;
	audio_configure( 1 );
}

/*-------------------------------------------*/


/******************************
 *	Dummy functions for now.
 ******************************/
void volume_up()
{

}

void volume_down()
{

}
/*--------------------------------*/


/**************************************
 *   Property stuff goes here.
 **************************************/

PROP_HANDLER( PROP_LV_ACTION )
{
	const unsigned mode = buf[0];
	enable_meters( mode );
	return prop_cleanup( token, property );
}
/*---------------------------------------*/


/*********************************************************************
 *				Analog gain control function.
 *
 *		This is messy I know, but it's the only way I know how to do it.
 *		The problem is that the 500d has to change 3 bits in AUDIO_IC_SIG1,
 *		but the other cameras only have to change 2, so I can't use Trammell's
 *		routine here, I had to write my own. Also, this way we get to use all
 *		of the extra gain settings the 500d has :).
 *
 *		This basically writes the required bits one at a time (in seperate statements),
 *		which is easier to read and understand, but probably not very efficient.
 *
 *		I used the PDF on the AK4346 audio chip from here:
 *		http://www.asahi-kasei.co.jp/akm/en/product/ak4636/ak4636_f01e.pdf
 *
 *		-Coutts
 *********************************************************************/
static inline void
audio_ic_set_mgain(
				   unsigned		mgain
				   )
{
	unsigned sig1 = audio_ic_read( AUDIO_IC_SIG1 ); // Read the value of register 'Signal Select 1', store it in sig1.
													// We will use this later when we set individual bits on/off for
													// different gain values.
    unsigned sig2 = audio_ic_read( AUDIO_IC_SIG2 ); // Read the value of register 'Signal Select 2', store it in sig2.
    
	
    // Setting the bits for each possible gain setting in the 500d, individually so it's easy to understand.
	//		- 24 hours ago I didn't even understand how to configure the audio chip, so I think this is pretty good
	//		  for my first implemenation :)
	//
	// Basically, different gain settings use different combinations of the MGAIN0, MGAIN1, MGAIN2, and MGAIN3 bits being set/cleared.
	// Here's a reference table for the settings, taken from the pdf linked from line 201 above:
	//
	//---------------------------------------------------------
	// MGAIN3  |  MGAIN2  |  MGAIN1  |  MGAIN0 ||  Gain Value
	//---------------------------------------------------------
	//    0    |     0    |    0     |    0    ||     0 dB
	//    0    |     0    |    0     |    1    ||   +20 dB (default setting)
	//    0    |     0    |    1     |    0    ||   +26 dB 
	//    0    |     0    |    1     |    1    ||   +32 dB
	//    0    |     1    |    0     |    0    ||   +10 dB
	//    0    |     1    |    0     |    1    ||   +17 dB
	//    0    |     1    |    1     |    0    ||   +23 dB
	//    0    |     1    |    1     |    1    ||   +29 dB
	//    1    |     0    |    0     |    0    ||    +3 dB
	//    1    |     0    |    0     |    1    ||    +6 dB
	//---------------------------------------------------------
	//
	// So my switch statement below looks at the value of the mgain variable (which is changed by the gain setting in the ML menu),
	// and sets the correct combination of bits accordingly.
	//
	// &= ~(1 << x) means clear bit x
	// |= 1 << x means set bit x
	//
	// That should be enough to bring anybody up to speed on things.
	// -Coutts
    switch (mgain)
    {
        case 0: // 0 dB
            sig1 &= ~(1 << 0); //clear bit1 [MGAIN0] in register 'Signal Select 1'
            sig1 &= ~(1 << 1); //clear bit2 [MGAIN2]	"
            sig1 &= ~(1 << 3); //clear bit3 [MGAIN3]	"
            sig2 &= ~(1 << 5); //clear bit4 [MGAIN1] in register 'Signal Select 2'
            break;
            
        case 1: // 3 dB
            sig1 &= ~(1 << 0); //clear MGAIN0
            sig1 &= ~(1 << 1); //clear MGAIN2
            sig1 |= 1 << 3;	   //set MGAIN3
            sig2 &= ~(1 << 5); //clear MGAIN1
            break;
            
        case 2: // 6 dB
            sig1 &= ~(1 << 1);	//	[etc, etc, etc]
            sig1 |= 1 << 0;		//	  |         |
            sig1 |= 1 << 3;		//	  |         |
            sig2 &= ~(1 << 5);	//	  V         V
            break;
            
        case 3: // 10 dB
            sig1 &= ~(1 << 0);
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 1;
            sig2 &= ~(1 << 5);
            break;
            
        case 4: // 17 dB
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 0;
            sig1 |= 1 << 1;
            sig2 &= ~(1 << 5);
            break;
            
        case 5: // 20 dB
            sig1 &= ~(1 << 1);
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 0;            
            sig2 &= ~(1 << 5);
            break;
            
        case 6: // 23 dB
            sig1 &= ~(1 << 0);
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 1;
            sig2 |= 1 << 5;
            break;
            
        case 7: // 26 dB
            sig1 &= ~(1 << 0);
            sig1 &= ~(1 << 1);
            sig1 &= ~(1 << 3);
            sig2 |= 1 << 5;
            break;
            
        case 8: // 29 dB
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 0;
            sig1 |= 1 << 1;
            sig2 |= 1 << 5;
            break;
            
        case 9: // 32 dB
            sig1 &= ~(1 << 1);
            sig1 &= ~(1 << 3);
            sig1 |= 1 << 0;
            sig2 |= 1 << 5;
            break;
    }
    
	audio_ic_write( AUDIO_IC_SIG1 | sig1 ); // Now that the correct bits in sig1 and sig2 have been set/cleared for whatever
	gain.sig1 = sig1;										// gain setting we want, now it's time to write our new values for 'Signal Select 1'
											// and 'Signal Select 2' to their respective registers in the audio chip.
    audio_ic_write( AUDIO_IC_SIG2 | sig2 );
	gain.sig2 = sig2;
}
/*-------------------------------------------------------*/


/************************************************************************
 *		Reconfigure the audio chip registers to toggle things like AGC.
 ************************************************************************/
static void
audio_configure( int force )
{
	// No need to worry about input source, we only have one option on the 500d.
	snprintf(label,  sizeof(label),  " INT ");
	
	bmp_printf(FONT_MED, 0, 200, "gain.alc: %x", gain.alc1);
	bmp_printf(FONT_MED, 0, 220, "gain.sig1: %x", gain.sig1);
	bmp_printf(FONT_MED, 0, 240, "gain.sig2: %x", gain.sig2);
	
	if( !force )
	{
		//////////////////////////////////////////////////////////////////////////
		// Check if AGC or analog gain settings have changed, if they are still
		// the same, then don't do anything - no need to set things that are
		// already set :)
		// -Coutts
		//////////////////////////////////////////////////////////////////////////
		if( audio_ic_read( AUDIO_IC_ALC1 ) == gain.alc1
           &&  audio_ic_read( AUDIO_IC_SIG1 ) == gain.sig1
           &&  audio_ic_read( AUDIO_IC_SIG2 ) == gain.sig2
           )
			return;
	}
	
	///////////////////////////////////////////////////////////////////
	// Power up some modules used for line out and stuff - not sure
	// about much behind this, but this register seems to be identical
	// between the AK4346 and AK4646 chips, so I guess it's okay.
	// -Coutts
	///////////////////////////////////////////////////////////////////
	//audio_ic_write( AUDIO_IC_PM1 | 0x6D );
	/*------------------------------------------------------------------*/
	
	audio_ic_write( AUDIO_IC_SIG1 | 0x14 ); // enables headphone loopback in play mode.
	
	////////////////////////////////////////////////////////////////////////////////////
	////////////// Set the bits in 'Signal Select 2' for line out *only* ///////////////
	////////////////////////////////////////////////////////////////////////////////////
	// Only set the third bit, in binary it's '100', this is just to set the BEEPA bit.
	// 
	// In the AK4646 chip it's called BEEPL, but they appear to be the same thing.
	// The purpose of this is to switch the BEEP signal to the mono-amp, which I guess
	// means mono line out?
	//
	// Also, we don't need to set bits 1 and 2 like in the AK4646 chip. These bits are for
	// LOVL0 and LOVL1, which control stereo line out gain, with either +0db, +2db, +4db, or +6db.
	//
	// The 500d only uses one bit for this, which means we only have 2 options. +0db or +3db.
	// Our bit is located in register 'Signal Select 4'. In the AK4646 chip, the register in this location 
	// is 'Power Management 3'. This register is named simply 'LOVL'.
	//
	// So, instead of setting bits 1 and 2 in AUDIO_IC_SIG2, we actually need to set bit 7 in 'Signal Select 4'.
	//
	// -Coutts
	////////////////////////////////////////////////////////////////////////////////////
	audio_ic_write( AUDIO_IC_SIG2 | 0x4 ); // 0x4 == 00000100.
	
	// Set the mono line gain to +3db (max).
	audio_ic_write( AUDIO_IC_SIG4 | 0x40 ); // 0x40 == 01000000.
	/*-----------------------------------*/
	
	
	gain.alc1 = alc_enable ? (1<<5) : 0;
	audio_ic_write( AUDIO_IC_ALC1 | gain.alc1 ); // disable all ALC
	
	// Update the analog gain settings.
	audio_ic_set_mgain( mgain );
}
/*------------------------------------------------------------------------*/


/**********************************************
 *		Toggling functions for menu settings.	
 **********************************************/
static void
audio_mgain_toggle( void * priv ) //toggle forward for analog gain.
{
	unsigned * ptr = priv;
	*ptr = MOD((*ptr + 1), 10);
	audio_configure( 1 );
}

static void
audio_mgain_toggle_reverse( void * priv ) //toggle reverse for analog gain.
{
	unsigned * ptr = priv;
	*ptr = MOD((*ptr - 1), 10);
	audio_configure( 1 );
}

static void
audio_monitoring_toggle( void * priv)
{
	audio_monitoring = !audio_monitoring;
	audio_monitoring_update();
}
/*----------------------------------------*/


/******************************************
 *		Display functions for menus.	
 ******************************************/
static void
audio_alc_display( void * priv, int x, int y, int selected )
{
	unsigned fnt = selected ? MENU_FONT_SEL : MENU_FONT;
	bmp_printf(
               FONT(fnt, alc_enable ? COLOR_RED : FONT_FG(fnt), FONT_BG(fnt)),
               x, y,
               "AGC           : %s",
               alc_enable ? "ON " : "OFF"
               );
}

static void
audio_mgain_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
               selected ? MENU_FONT_SEL : MENU_FONT,
               x, y,
               "Analog Gain   : %d dB",
               mgain_index2gain(mgain)
               );
	menu_draw_icon(x, y, MNI_PERCENT, mgain_index2gain(mgain) * 100 / 32);
}

static void
audio_meter_display( void * priv, int x, int y, int selected )
{
	unsigned v = *(unsigned*) priv;
	bmp_printf(
               selected ? MENU_FONT_SEL : MENU_FONT,
               x, y,
               "Audio Meters  : %s",
               v ? "ON" : "OFF"
               );
	menu_draw_icon(x, y, MNI_BOOL_GDR(v));
}

static void
audio_monitoring_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
               selected ? MENU_FONT_SEL : MENU_FONT,
               x, y,
               "Monitoring-USB: %s",
               audio_monitoring ? "ON" : "OFF"
               );
}
/*----------------------------------------*/


/******************************************
 *		Audio menu order / layout	
 ******************************************/
static struct menu_entry audio_menus[] = {
	{
		.name = "Audio Meters",
		.priv		= &cfg_draw_meters,
		.select		= menu_binary_toggle,
		.display	= audio_meter_display,
		.help = "Bar peak decay, from -40 dB to 0 dB, yellow at -12dB."
	},
	{
		.name = "AGC",
		.priv		= &alc_enable,
		.select		= menu_binary_toggle,
		.display	= audio_alc_display,
		.help = "Automatic Gain Control - turn it off :)"
	},
	{
		.name = "Analog Gain (dB)",
		.priv		= &mgain,
		.select		= audio_mgain_toggle,
		.select_reverse	= audio_mgain_toggle_reverse,
		.display	= audio_mgain_display,
		.help = "Gain applied to both inputs in analog domain (preferred)."
	},
	{
		.name = "Monitoring-USB",
		.priv = &audio_monitoring,
		.select		= audio_monitoring_toggle,
		.display	= audio_monitoring_display,
		.help = "Audio monitoring via USB. Disable if you use a SD display."
	},
};
/*----------------------------------------*/


/******************************************
 *		Display the Audio menu section.	
 ******************************************/
static void audio_menus_init()
{
	menu_add( "Audio", audio_menus, COUNT(audio_menus) );
}
/*----------------------------------------*/









	/*********************************************************
	 *--------------------------------------------------------
	 *					ALL TASKS AFTER THIS
	 *--------------------------------------------------------
	 *********************************************************/




/******************************************************************************
 *		Our version of canon's sounddev task. We override canon's task
 *		so that we can change things like AGC and analog/digital gain.
 *
 *		If this is a magic_off boot, then just call canon's built-in
 *		task and don't do anything.
 ******************************************************************************/
static void
my_sounddev_task()
{
	msleep( 2000 );
	
	// If this is a magic_off boot, don't start our task, just call canon's task.
	if (magic_is_off()) { sounddev_task(); return; }
    
	// Wait until ML has started up.
	hold_your_horses(1);
	
	// Semaphores
	gain.sem = create_named_semaphore( "audio_gain", 1);
	sounddev.sem_alc = CreateBinarySemaphore( 0, 0 );
	
	audio_configure( 1 ); // force it to run this first time to re-configure the sound device.
	msleep(500);
	audio_monitoring_update(); // Update audio monitoring stuff.
	
	while(1) //time to loop!!
	{
		int rc = take_semaphore( gain.sem, 1000 );
		if(gui_state != GUISTATE_PLAYMENU || (audio_monitoring && AUDIO_MONITORING_HEADPHONES_CONNECTED)) {
			audio_configure( rc == 0 ); // force it if we got the semaphore
		}
	}
}
// override canon's sounddev task with our own.
TASK_OVERRIDE( sounddev_task, my_sounddev_task );
/*--------------------------------------------------------------------------------*/


/******************************************************************************
 * 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 )
{
	audio_menus_init();
	
	while(1)
	{
		msleep( 50 );
        
		if (is_menu_help_active()) continue;
		
		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( "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;
    
	while(1)
	{
		msleep( 10 );
		compute_audio_levels( 0 );
		compute_audio_levels( 1 );
	}
}

TASK_CREATE( "audio_level_task", compute_audio_level_task, 0, 0x18, 0x1000 );
back to top