audio.c 27.8 KB
Newer Older
1
2
3
/** \file
 * Onscreen audio meters
 */
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * 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.
 */

24
#include "dryos.h"
25
#include "bmp.h"
26
#include "config.h"
27
#include "property.h"
hudson@kremvax's avatar
hudson@kremvax committed
28
#include "menu.h"
a1ex's avatar
a1ex committed
29
#include "gui.h"
hudson@kremvax's avatar
hudson@kremvax committed
30

a1ex's avatar
a1ex committed
31
32
33
34
void audio_configure(int force);
void volume_display();
void volume_display_clear();

35
36
37
// Dump the audio registers to a file if defined
#undef CONFIG_AUDIO_REG_LOG

38
39
40
41
42
43
44
45
46
47
48
49
50
struct gain_struct
{
	struct semaphore *	sem;
	unsigned		alc1;
	unsigned		sig1;
	unsigned		sig2;
};

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


51
52
// Set defaults
CONFIG_INT( "audio.mgain",	mgain,		4 );
53
54
CONFIG_INT( "audio.dgain.l",	dgain_l,	0 );
CONFIG_INT( "audio.dgain.r",	dgain_r,	0 );
55
CONFIG_INT( "audio.mic-power",	mic_power,	1 );
56
CONFIG_INT( "audio.lovl",	lovl,		0 );
57
58
CONFIG_INT( "audio.o2gain",	o2gain,		0 );
CONFIG_INT( "audio.alc-enable",	alc_enable,	0 );
a1ex's avatar
a1ex committed
59
//CONFIG_INT( "audio.mic-in",	mic_in,		0 ); // not used any more?
60
int loopback = 1;
a1ex's avatar
a1ex committed
61
//CONFIG_INT( "audio.input-source",	input_source,		0 ); //0=internal; 1=L int, R ext; 2 = stereo ext; 3 = L int, R ext balanced
a1ex's avatar
a1ex committed
62
CONFIG_INT( "audio.input-choice",	input_choice,		0 ); //0=internal; 1=L int, R ext; 2 = stereo ext; 3 = L int, R ext balanced, 4 = auto (0 or 1)
a1ex's avatar
a1ex committed
63
CONFIG_INT( "audio.disable-filters",	disable_filters,	1 ); //disable the HPF, LPF and pre-emphasis filters
64
CONFIG_INT("audio.draw-meters", cfg_draw_meters, 2);
65
CONFIG_INT("audio.monitoring", audio_monitoring, 1);
a1ex's avatar
a1ex committed
66
int do_draw_meters = 0;
67

a1ex's avatar
a1ex committed
68
69
70
71
int ext_cfg_draw_meters(void)
{
    return cfg_draw_meters;
}
72

73
struct audio_level audio_levels[2];
hudson@kremvax's avatar
hudson@kremvax committed
74

a1ex's avatar
a1ex committed
75
76
77
78
79
struct audio_level *get_audio_levels(void)
{
	return audio_levels;
}

a1ex's avatar
a1ex committed
80
81
// from Morgan Look
// THIS FUNCTION IS NOT TESTED!!! (it may work, or it may not)
a1ex's avatar
a1ex committed
82
83
84
85
86
87
88
89
90
91
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
a1ex's avatar
a1ex committed
92
   _audio_ic_write(reg | bits | old);    // bitwise OR everything together and call _audio_ic_write function
a1ex's avatar
a1ex committed
93
}
hudson@kremvax's avatar
hudson@kremvax committed
94

95
96
97
98
99

/** Returns a dB translated from the raw level
 *
 * Range is -40 to 0 dB
 */
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
100
101
static int
audio_level_to_db(
hudson@kremvax's avatar
hudson@kremvax committed
102
	int			raw_level
103
104
105
106
107
108
)
{
	int db;

	for( db = 40 ; db ; db-- )
	{
109
		if( audio_thresholds[db] > raw_level )
110
111
112
113
114
115
116
			return -db;
	}

	return 0;
}


Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
117
118


119
120
121
122
#ifdef OSCOPE_METERS
void draw_meters(void)
{
#define MAX_SAMPLES 720
hudson@kremvax's avatar
hudson@kremvax committed
123
124
	static int16_t levels[ MAX_SAMPLES ];
	static uint32_t index;
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
	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
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
150

151
152
char left_label[10];
char right_label[10];
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
153
154
155
156
157
158

static uint8_t
db_to_color(
	int			db
)
{
hudson@kremvax's avatar
hudson@kremvax committed
159
	if( db < -35 )
160
		return 0x2F; // white
hudson@kremvax's avatar
hudson@kremvax committed
161
	if( db < -20 )
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
162
		return 0x06; // dark green
hudson@kremvax's avatar
hudson@kremvax committed
163
	if( db < -15 )
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
164
165
166
167
168
169
170
171
172
		return 0x0F; // yellow
	return 0x0c; // dull red
}

static uint8_t
db_peak_to_color(
	int			db
)
{
hudson@kremvax's avatar
hudson@kremvax committed
173
	if( db < -35 )
174
		return 11; // dark blue
hudson@kremvax's avatar
hudson@kremvax committed
175
	if( db < -20 )
176
		return 11; // dark blue
hudson@kremvax's avatar
hudson@kremvax committed
177
	if( db < -15 )
178
		return 15; // bright yellow
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
179
180
181
182
	return 0x08; // bright red
}


hudson@kremvax's avatar
hudson@kremvax committed
183
184
static void
draw_meter(
185
186
187
	int		y_origin,
	struct	audio_level *	level,
    char *	label
hudson@kremvax's avatar
hudson@kremvax committed
188
189
)
{
a1ex's avatar
a1ex committed
190
	const uint32_t width = 560; // bmp_width();
191
	const uint32_t pitch = BMPPITCH;
hudson@kremvax's avatar
hudson@kremvax committed
192
	uint32_t * row = (uint32_t*) bmp_vram();
193
194
	if( !row )
		return;
195
196
197

	// Skip to the desired y coord and over the
	// space for the numerical levels
198
199
	// .. and the space for showing the channel and source.
	row += (pitch/4) * y_origin + AUDIO_METER_OFFSET;
200

hudson@kremvax's avatar
hudson@kremvax committed
201
202
	const int db_avg = audio_level_to_db( level->avg );
	const int db_peak = audio_level_to_db( level->peak );
203

a1ex's avatar
a1ex committed
204
205
206
	// levels go from -40 to 0, so -40 * 14 == 560 (=width)
	const uint32_t x_db_avg = (width + db_avg * 14) / 4;
	const uint32_t x_db_peak = (width + db_peak * 14) / 4;
Trammell Hudson's avatar
Zebras!    
Trammell Hudson committed
207
208
209

	const uint8_t bar_color = db_to_color( db_avg );
	const uint8_t peak_color = db_peak_to_color( db_peak );
hudson@kremvax's avatar
hudson@kremvax committed
210
211
	const int meter_height = 12;

212
213
	const uint32_t bar_color_word = color_word( bar_color );
	const uint32_t peak_color_word = color_word( peak_color );
a1ex's avatar
a1ex committed
214
	const uint32_t bg_color_word = color_word(COLOR_BLACK);
hudson@kremvax's avatar
hudson@kremvax committed
215
216

	// Write the meter an entire scan line at a time
217
	int y;
hudson@kremvax's avatar
hudson@kremvax committed
218
219
220
221
222
223
224
225
226
227
228
	for( y=0 ; y<meter_height ; y++, row += pitch/4 )
	{
		uint32_t x;
		for( x=0 ; x<width/4 ; x++ )
		{
			if( x < x_db_avg )
				row[x] = bar_color_word;
			else
			if( x < x_db_peak )
				row[x] = bg_color_word;
			else
229
			if( x < x_db_peak + 4 )
hudson@kremvax's avatar
hudson@kremvax committed
230
231
232
233
234
				row[x] = peak_color_word;
			else
				row[x] = bg_color_word;
		}
	}
235
236

	// Write the current level
a1ex's avatar
a1ex committed
237
	bmp_printf( FONT(FONT_SMALL, COLOR_WHITE, COLOR_BLACK), 0, y_origin, "%s %2d", label, db_avg );
hudson@kremvax's avatar
hudson@kremvax committed
238
}
239

240

hudson@kremvax's avatar
hudson@kremvax committed
241
242
243
244
245
246
static void
draw_ticks(
	int		y,
	int		tick_height
)
{
a1ex's avatar
a1ex committed
247
	const uint32_t width = 560 + 8; // bmp_width();
248
	const uint32_t pitch = BMPPITCH;
hudson@kremvax's avatar
hudson@kremvax committed
249
	uint32_t * row = (uint32_t*) bmp_vram();
250
251
	if( !row )
		return;
252
	row += (pitch/4) * y + AUDIO_METER_OFFSET - 2 ;//seems to need less of an offset
253

254
	const uint32_t white_word = 0
255
256
257
258
		| ( COLOR_WHITE << 24 )
		| ( COLOR_WHITE << 16 )
		| ( COLOR_WHITE <<  8 )
		| ( COLOR_WHITE <<  0 );
259

hudson@kremvax's avatar
hudson@kremvax committed
260
	for( ; tick_height > 0 ; tick_height--, row += pitch/4 )
261
262
	{
		int db;
263
		for( db=-40; db<= 0 ; db+=5 )
264
		{
a1ex's avatar
a1ex committed
265
			const uint32_t x_db = width + db * 14;
266
			row[x_db/4] = white_word;
267
268
		}
	}
hudson@kremvax's avatar
hudson@kremvax committed
269
}
270

a1ex's avatar
a1ex committed
271
static int audio_cmd_to_gain_x1000(int cmd);
272

hudson@kremvax's avatar
hudson@kremvax committed
273
274
275
276
277
/* Normal VU meter */
static void draw_meters(void)
{
	// The db values are multiplied by 8 to make them
	// smoother.
278
	draw_meter( 0, &audio_levels[0], left_label);
hudson@kremvax's avatar
hudson@kremvax committed
279
	draw_ticks( 12, 4 );
280
	draw_meter( 16, &audio_levels[1], right_label);
a1ex's avatar
a1ex committed
281
282
283
284
285
	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, 420, "AGC:%s%d.%03d dB", dgain_x1000 < 0 ? "-" : " ", ABS(dgain_x1000) / 1000, ABS(dgain_x1000) % 1000);
	}
286
}
hudson@kremvax's avatar
hudson@kremvax committed
287

288
289
290
#endif


291
292


hudson@kremvax's avatar
hudson@kremvax committed
293
294
295
296
297
298
299
300
301
302
303
304
305

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;
306
	level->avg	= (level->avg * 15 + raw) / 16;
hudson@kremvax's avatar
hudson@kremvax committed
307
308
309
310
	if( raw > level->peak )
		level->peak = raw;

	// Decay the peak to the average
hudson@kremvax's avatar
hudson@kremvax committed
311
	level->peak = ( level->peak * 31 + level->avg ) / 32;
312
313
}

314
int show_volume = 0;
315

316
317
int audio_meters_are_drawn()
{
a1ex's avatar
a1ex committed
318
319
	return 
		(
320
			shooting_mode == SHOOTMODE_MOVIE && cfg_draw_meters && do_draw_meters && zebra_should_run()
a1ex's avatar
a1ex committed
321
		)
322
		||
a1ex's avatar
a1ex committed
323
		(
324
			gui_menu_shown() && is_menu_active("Audio") && cfg_draw_meters
a1ex's avatar
a1ex committed
325
		);
326
}
327
328
329
330
/** Task to monitor the audio levels.
 *
 * Compute the average and peak level, periodically calling
 * the draw_meters() function to display the results on screen.
331
332
 * \todo Check that we have live-view enabled and the TFT is on
 * before drawing.
333
334
 */
static void
a1ex's avatar
a1ex committed
335
meter_task( void* unused )
336
337
338
{
	while(1)
	{
339
		msleep( 50 );
340

341
		if (audio_meters_are_drawn())
342
343
344
345
		{
			if (!is_mvr_buffer_almost_full())
				BMP_SEM( draw_meters(); )
		}
346
		else
347
		{
348
			msleep( 500 );
349
		}
350
351
352
353
354
355
356
		
		if (show_volume)
		{
			volume_display();
			show_volume--;
			if (show_volume == 0) volume_display_clear();
		}
357
358
359
	}
}

360

361
362
363
364
365
TASK_CREATE( "meter_task", meter_task, 0, 0x18, 0x1000 );


/** Monitor the audio levels very quickly */
static void
a1ex's avatar
a1ex committed
366
compute_audio_level_task( void* unused )
367
{
368
	audio_levels[0].peak = audio_levels[1].peak = 0;
a1ex's avatar
a1ex committed
369
	audio_levels[0].avg = audio_levels[1].avg = 0;
370
371
372

	while(1)
	{
373
		msleep( 10 );
374
375
376
377
378
		compute_audio_levels( 0 );
		compute_audio_levels( 1 );
	}
}

379
TASK_CREATE( "audio_level_task", compute_audio_level_task, 0, 0x18, 0x1000 );
380

381
382

/** Write the MGAIN2-0 bits.
383
 * Table 19 for the gain values (variable "bits"):
384
 *
a1ex's avatar
a1ex committed
385
386
387
388
389
390
391
392
 *   0 == +0 dB
 *   1 == +20 dB
 *   2 == +26 dB
 *   3 == +32 dB
 *   4 == +10 dB
 *   5 == +17 dB
 *   6 == +23 dB
 *   7 == +29 dB
393
 *
394
395
 * Why is it split between two registers?  I don't know.
 */
396
397
398
399
400
401
402
403
404
405
406
407
408
409

static inline unsigned mgain_index2gain(int index) // sorted mgain values
{
	static uint8_t gains[] = { 0, 10, 17, 20, 23, 26, 29, 32 };
	return gains[index & 0x7];
}
static inline unsigned mgain_index2bits(int index) // sorted mgain values
{
	static uint8_t bitsv[] = { 0, 0x4, 0x5, 0x01, 0x06, 0x02, 0x07, 0x03 };
	return bitsv[index & 0x7];
}



410
411
static inline void
audio_ic_set_mgain(
412
	unsigned		index
413
414
)
{
415
	unsigned bits = mgain_index2bits(index);
416
417
418
419
420
421
	bits &= 0x7;
	unsigned sig1 = audio_ic_read( AUDIO_IC_SIG1 );
	sig1 &= ~0x3;
	sig1 |= (bits & 1);
	sig1 |= (bits & 4) >> 1;
	audio_ic_write( AUDIO_IC_SIG1 | sig1 );
422
	gain.sig1 = sig1;
423
424
425
426
427

	unsigned sig2 = audio_ic_read( AUDIO_IC_SIG2 );
	sig2 &= ~(1<<5);
	sig2 |= (bits & 2) << 4;
	audio_ic_write( AUDIO_IC_SIG2 | sig2 );
428
	gain.sig2 = sig2;
429
430
431
}


432
433
static inline uint8_t
audio_gain_to_cmd(
434
435
436
437
438
439
	int			gain
)
{
	unsigned cmd = ( gain * 1000 ) / 375 + 145;
	cmd &= 0xFF;

440
441
442
	return cmd;
}

a1ex's avatar
a1ex committed
443
444
445
446
447
448
449
450
static int
audio_cmd_to_gain_x1000(
	int			cmd
)
{
	int gain_x1000 = (cmd - 145) * 375;
	return gain_x1000;
}
451
452
453
454
455
456
457
458

static inline void
audio_ic_set_input_volume(
	int			channel,
	int			gain
)
{
	unsigned cmd = audio_gain_to_cmd( gain );
459
460
461
462
463
464
	if( channel )
		cmd |= AUDIO_IC_IVL;
	else
		cmd |= AUDIO_IC_IVR;

	audio_ic_write( cmd );
465
466
467
}


468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
#ifdef CONFIG_AUDIO_REG_LOG

// 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 )
		{
			fprintf(
				reg_file,
				"%s %02x\n",
				audio_reg_names[i],
				reg
			);
			output = 1;
		}

		last_regs[i] = reg;
	}

	if( output )
		fprintf( reg_file, "%s\n", "" );
}


static void
audio_reg_close( void )
{
	if( reg_file )
		FIO_CloseFile( reg_file );
	reg_file = NULL;
}

#endif

568
569
570
571
572
573
574
575
int mic_inserted = 0;
PROP_HANDLER( PROP_MIC_INSERTED )
{
   mic_inserted = buf[0];
   audio_configure( 1 );
   return prop_cleanup( token, property );
}

a1ex's avatar
a1ex committed
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
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;
}

int get_mic_power(int input_source)
{
	return (input_source >= 2) ? mic_power : 1;
}
592

593
void
594
audio_configure( int force )
595
{
596
597
598
599
#ifdef CONFIG_AUDIO_REG_LOG
	audio_reg_dump( force );
	return;
#endif
a1ex's avatar
a1ex committed
600
	
a1ex's avatar
a1ex committed
601
	int pm3[] = { 0x00, 0x05, 0x07, 0x11 }; //should this be in a header file?
a1ex's avatar
a1ex committed
602
	int input_source = get_input_source();
a1ex's avatar
a1ex committed
603
604
	

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
	//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:
			snprintf(left_label,  sizeof(left_label),  "L INT");
			snprintf(right_label, sizeof(right_label), "R INT");
			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;
	}
627

628
629
630
631
632
633
634
635
636
	if( !force )
	{
		// Check for ALC configuration; do nothing if it is
		// already disabled
		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;
637
		DebugMsg( DM_AUDIO, 3, "%s: Reseting user settings", __func__ );
638
	}
639
640
641
	
	//~ static int iter=0;
	//~ bmp_printf(FONT_MED, 0, 70, "audio configure(%d)", iter++);
642

643
	audio_ic_write( AUDIO_IC_PM1 | 0x6D ); // power up ADC and DAC
a1ex's avatar
a1ex committed
644
	
a1ex's avatar
a1ex committed
645
646
	//mic_power is forced on if input source is 0 or 1
	int mic_pow = get_mic_power(input_source);
a1ex's avatar
a1ex committed
647
				 
hudson@kremvax's avatar
hudson@kremvax committed
648
649
	audio_ic_write( AUDIO_IC_SIG1
		| 0x10
a1ex's avatar
a1ex committed
650
		| ( mic_pow ? 0x4 : 0x0 )
hudson@kremvax's avatar
hudson@kremvax committed
651
	); // power up, no gain
652
653
654

	audio_ic_write( AUDIO_IC_SIG2
		| 0x04 // external, no gain
655
		| ( lovl & 0x3) << 0 // line output level
656
	);
657

a1ex's avatar
a1ex committed
658
659
	//PM3 is set according to the input choice
	audio_ic_write( AUDIO_IC_PM3 | pm3[input_source] );
660

661
	gain.alc1 = alc_enable ? (1<<5) : 0;
662
	audio_ic_write( AUDIO_IC_ALC1 | gain.alc1 ); // disable all ALC
663

664
665
666
	// Control left/right gain independently
	audio_ic_write( AUDIO_IC_MODE4 | 0x00 );

667
668
	audio_ic_set_input_volume( 0, dgain_r );
	audio_ic_set_input_volume( 1, dgain_l );
a1ex's avatar
a1ex committed
669
	audio_ic_set_mgain( mgain );
670

a1ex's avatar
a1ex committed
671
672
673
	if (disable_filters) {
		audio_ic_write( AUDIO_IC_FIL1 | 0x00 ); //no need to set them all to 0Hz, just turn em off, in one easy register
	}
674

675
	// Enable loop mode and output digital volume2
676
	uint32_t mode3 = audio_ic_read( AUDIO_IC_MODE3 );
677
678
679
	mode3 &= ~0x5C; // disable loop, olvc, datt0/1
	audio_ic_write( AUDIO_IC_MODE3
		| mode3				// old value
680
681
		| loopback << 6		// loop mode
		| (o2gain & 0x3) << 2	// output volume
682
	);
683
684

	//draw_audio_regs();
685
/*	bmp_printf( FONT_SMALL, 500, 450,
a1ex's avatar
a1ex committed
686
		"Gain %d/%d Mgain %d Src %d",
687
688
		dgain_l,
		dgain_r,
689
		mgain_index2gain(mgain),
a1ex's avatar
a1ex committed
690
		input_source
691
	);*/
692

693
	DebugMsg( DM_AUDIO, 3,
694
		"Gain mgain=%d dgain=%d/%d",
695
		mgain_index2gain(mgain),
696
697
		dgain_l,
		dgain_r
698
699
700
701
	);
}


hudson@kremvax's avatar
hudson@kremvax committed
702
703
/** Menu handlers */

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
static void
audio_binary_toggle( void * priv )
{
	unsigned * ptr = priv;
	*ptr = !*ptr;
	audio_configure( 1 );
}


static void
audio_3bit_toggle( void * priv )
{
	unsigned * ptr = priv;
	*ptr = (*ptr + 0x1) & 0x3;
	audio_configure( 1 );
}

721
722
723
724
725
726
727
static void
audio_3bit_toggle_reverse( void * priv )
{
	unsigned * ptr = priv;
	*ptr = (*ptr - 0x1) & 0x3;
	audio_configure( 1 );
}
728
729
730

static void
audio_mgain_toggle( void * priv )
hudson@kremvax's avatar
hudson@kremvax committed
731
732
733
{
	unsigned * ptr = priv;
	*ptr = (*ptr + 0x1) & 0x7;
734
	audio_configure( 1 );
hudson@kremvax's avatar
hudson@kremvax committed
735
736
}

737
738
739
740
741
742
743
744
745
746
static void
audio_mgain_toggle_reverse( void * priv )
{
	unsigned * ptr = priv;
	*ptr = (*ptr - 0x1) & 0x7;
	audio_configure( 1 );
}



747
748
749

static void
audio_mgain_display( void * priv, int x, int y, int selected )
hudson@kremvax's avatar
hudson@kremvax committed
750
{
751
752
	unsigned gain_index = *(unsigned*) priv;
	gain_index &= 0x7;
hudson@kremvax's avatar
hudson@kremvax committed
753

754
755
756
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
a1ex's avatar
a1ex committed
757
		"Analog Gain   : %d dB",
758
		mgain_index2gain(gain_index)
hudson@kremvax's avatar
hudson@kremvax committed
759
	);
a1ex's avatar
a1ex committed
760
	menu_draw_icon(x, y, MNI_PERCENT, mgain_index2gain(gain_index) * 100 / 32);
hudson@kremvax's avatar
hudson@kremvax committed
761
762
}

763
764
765

static void
audio_dgain_toggle( void * priv )
hudson@kremvax's avatar
hudson@kremvax committed
766
767
768
769
770
771
{
	unsigned dgain = *(unsigned*) priv;
	dgain += 6;
	if( dgain > 40 )
		dgain = 0;
	*(unsigned*) priv = dgain;
772
	audio_configure( 1 );
hudson@kremvax's avatar
hudson@kremvax committed
773
774
}

775
776
777
778
779
780
781
782
783
784
785
786
static void
audio_dgain_toggle_reverse( void * priv )
{
	unsigned dgain = *(unsigned*) priv;
	if( dgain <= 0 ) {
		dgain = 36;
	} else {
		dgain -= 6;
	}
	*(unsigned*) priv = dgain;
	audio_configure( 1 );
}
787
788
789

static void
audio_dgain_display( void * priv, int x, int y, int selected )
hudson@kremvax's avatar
hudson@kremvax committed
790
{
791
792
793
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
794
		// 23456789012
a1ex's avatar
a1ex committed
795
		"%s-DigitalGain : %d dB",
796
		priv == &dgain_l ? "L" : "R",
hudson@kremvax's avatar
hudson@kremvax committed
797
798
		*(unsigned*) priv
	);
799
	menu_draw_icon(x, y, alc_enable ? MNI_WARNING : MNI_PERCENT, (*(unsigned*) priv) * 100 / 36);
hudson@kremvax's avatar
hudson@kremvax committed
800
801
802
}


803
804
static void
audio_lovl_display( void * priv, int x, int y, int selected )
805
806
807
808
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
809
		//23456789012
a1ex's avatar
a1ex committed
810
		"Output volume : %d dB",
811
		2 * *(unsigned*) priv
812
	);
813
	menu_draw_icon(x, y, !audio_monitoring ? MNI_WARNING : MNI_PERCENT, (2 * *(unsigned*) priv) * 100 / 6);
814
815
}

a1ex's avatar
a1ex committed
816
817
818
static void
audio_meter_display( void * priv, int x, int y, int selected )
{
819
	unsigned v = *(unsigned*) priv;
a1ex's avatar
a1ex committed
820
821
822
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
a1ex's avatar
a1ex committed
823
		"Audio Meters  : %s",
824
		v ? "ON" : "OFF"
a1ex's avatar
a1ex committed
825
	);
826
	menu_draw_icon(x, y, v && !get_global_draw() ? MNI_WARNING : MNI_BOOL(v), 0);
a1ex's avatar
a1ex committed
827
828
}

829

830
831
832
#if 0
static void
audio_o2gain_display( void * priv, int x, int y, int selected )
833
834
835
836
837
838
839
840
{
	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,
841
842
		//23456789
		"o2gain:  -%2d dB",
843
844
845
		gains[ gain_reg ]
	);
}
846
847
848
849
850
851
852
853
854
#endif


static void
audio_alc_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
855
		//23456789012
a1ex's avatar
a1ex committed
856
		"AGC           : %s",
857
		alc_enable ? "ON " : "OFF"
858
859
	);
}
860
861


862
static void
a1ex's avatar
a1ex committed
863
audio_input_display( void * priv, int x, int y, int selected )
864
865
866
867
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
868
		//23456789012
a1ex's avatar
a1ex committed
869
870
871
872
873
874
		"Input Source  : %s",
		(input_choice == 0 ? "internal mic" : 
		(input_choice == 1 ? "L:int R:ext" :
		(input_choice == 2 ? "external stereo" : 
		(input_choice == 3 ? "L:int R:balanced" : 
		(input_choice == 4 ? (mic_inserted ? "Auto int/EXT " : "Auto INT/ext") : 
a1ex's avatar
a1ex committed
875
		"error")))))
876
	);
a1ex's avatar
a1ex committed
877
	menu_draw_icon(x, y, input_choice == 4 ? MNI_AUTO : MNI_ON, 0);
878
}
a1ex's avatar
a1ex committed
879
880
881
882
static void
audio_input_toggle( void * priv )
{
	unsigned * ptr = priv;
a1ex's avatar
a1ex committed
883
	*ptr = (*ptr + 1) % 5;
a1ex's avatar
a1ex committed
884
885
886
	audio_configure( 1 );
}

887
/*
888
889
890
891
892
893
static void
audio_loopback_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
894
		"Loopback      : %s",
895
		loopback ? "ON " : "OFF"
896
	);
897
}*/
898

899
/*audio_filters_display( void * priv, int x, int y, int selected )
a1ex's avatar
a1ex committed
900
901
902
903
904
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		//23456789012
905
		"DigitalFilters: %s",
a1ex's avatar
a1ex committed
906
907
		disable_filters ? "OFF" : "ON "
	);
908
}*/
a1ex's avatar
a1ex committed
909

910
/*
a1ex's avatar
a1ex committed
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
PROP_INT(PROP_WINDCUT_MODE, windcut_mode);

void windcut_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		"Wind Cut Mode : %d",
		windcut_mode
	);
}

void set_windcut(int value)
{
	prop_request_change(PROP_WINDCUT_MODE, &value, 4);
}

void windcut_toggle(void* priv)
{
	windcut_mode = !windcut_mode;
	set_windcut(windcut_mode);
932
}*/
a1ex's avatar
a1ex committed
933

934
935
936
937
938
939
940
941
942
943
944
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"
	);
}

a1ex's avatar
a1ex committed
945
946
947
static void
audio_micpower_display( void * priv, int x, int y, int selected )
{
a1ex's avatar
a1ex committed
948
	unsigned int mic_pow = get_mic_power(get_input_source());
a1ex's avatar
a1ex committed
949
950
951
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
a1ex's avatar
a1ex committed
952
		"Mic Power     : %s",
a1ex's avatar
a1ex committed
953
		mic_pow ? "ON (Low Z)" : "OFF (High Z)"
a1ex's avatar
a1ex committed
954
	);
a1ex's avatar
a1ex committed
955
	if (mic_pow != mic_power) menu_draw_icon(x,y, MNI_WARNING, 0);
a1ex's avatar
a1ex committed
956
957
958
}


959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
PROP_INT(PROP_USBRCA_MONITOR, rca_monitor);

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

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

hudson@kremvax's avatar
hudson@kremvax committed
982
static struct menu_entry audio_menus[] = {
a1ex's avatar
a1ex committed
983
984
	{
		.priv		= &cfg_draw_meters,
a1ex's avatar
a1ex committed
985
		.select		= menu_binary_toggle,
a1ex's avatar
a1ex committed
986
		.select_reverse		= menu_ternary_toggle_reverse,
a1ex's avatar
a1ex committed
987
		.display	= audio_meter_display,
988
		.help = "Meters show average value and peaks, from -40 dB to 0 dB."
a1ex's avatar
a1ex committed
989
	},
990
#if 0
991
	{
992
		.priv		= &o2gain,
993
994
995
		.select		= audio_o2gain_toggle,
		.display	= audio_o2gain_display,
	},
996
#endif
hudson@kremvax's avatar
hudson@kremvax committed
997
	{
998
		.priv		= &mgain,
hudson@kremvax's avatar
hudson@kremvax committed
999
		.select		= audio_mgain_toggle,
1000
		.select_reverse	= audio_mgain_toggle_reverse,
hudson@kremvax's avatar
hudson@kremvax committed
1001
		.display	= audio_mgain_display,
1002
		.help = "Gain applied to both inputs in analog domain (preferred)."
hudson@kremvax's avatar
hudson@kremvax committed
1003
1004
	},
	{
1005
		.priv		= &dgain_l,
1006
		.select		= audio_dgain_toggle,
1007
		.select_reverse = audio_dgain_toggle_reverse,
1008
		.display	= audio_dgain_display,
1009
		.help = "Digital gain applied only to the LEFT channel."
1010
1011
	},
	{
1012
		.priv		= &dgain_r,
hudson@kremvax's avatar
hudson@kremvax committed
1013
		.select		= audio_dgain_toggle,
1014
		.select_reverse = audio_dgain_toggle_reverse,
hudson@kremvax's avatar
hudson@kremvax committed
1015
		.display	= audio_dgain_display,
1016
		.help = "Digital gain applied only to the RIGHT channel."
hudson@kremvax's avatar
hudson@kremvax committed
1017
	},
1018
	{
1019
		.priv		= &alc_enable,
1020
1021
		.select		= audio_binary_toggle,
		.display	= audio_alc_display,
1022
		.help = "Automatic Gain Control - turn it off :)"
1023
	},
1024
	/*{
a1ex's avatar
a1ex committed
1025
1026
1027
		.priv		= &windcut_mode,
		.select		= windcut_toggle,
		.display	= windcut_display,
1028
	},*/
1029
	/*{
a1ex's avatar
a1ex committed
1030
		.priv		= &disable_filters,
1031
		.select		= audio_binary_toggle,
a1ex's avatar
a1ex committed
1032
		.display	= audio_filters_display,
1033
	},*/
1034
1035
1036
1037
1038
1039
1040
#ifdef CONFIG_AUDIO_REG_LOG
	{
		.priv		= "Close register log",
		.select		= audio_reg_close,
		.display	= menu_print,
	},
#endif
a1ex's avatar
a1ex committed
1041
	{
a1ex's avatar
a1ex committed
1042
		.priv		= &input_choice,
a1ex's avatar
a1ex committed
1043
1044
		.select		= audio_input_toggle,
		.display	= audio_input_display,
1045
		.help = "Audio input: internal / external / both / balanced / auto."
a1ex's avatar
a1ex committed
1046
	},
1047
	/*{
1048
1049
1050
		.priv		= &loopback,
		.select		= audio_binary_toggle,
		.display	= audio_loopback_display,
1051
	},*/
a1ex's avatar
a1ex committed
1052
1053
1054
1055
	{
		.priv		= &mic_power,
		.select		= audio_binary_toggle,
		.display	= audio_micpower_display,
a1ex's avatar
a1ex committed
1056
		.help = "Needed for int. and some other mics, but lowers impedance."
a1ex's avatar
a1ex committed
1057
	},
1058
1059
1060
1061
1062
	{
		.priv		= &lovl,
		.select		= audio_3bit_toggle,
		.select_reverse		= audio_3bit_toggle_reverse,
		.display	= audio_lovl_display,
1063
		.help = "Output volume for audio monitoring (headphones only)."
1064
	},
1065
	{
a1ex's avatar
a1ex committed
1066
		.priv = &audio_monitoring,
1067
		.select		= audio_monitoring_toggle,
1068
		.display	= audio_monitoring_display,
1069
		.help = "Audio monitoring via USB. Disable if you use a SD display."
1070
	},
hudson@kremvax's avatar
hudson@kremvax committed
1071
1072
};

1073

1074
1075

static void
1076
1077
enable_recording(
	int			mode
1078
1079
)
{
1080
	switch( mode )
1081
1082
	{
	case 0:
1083
1084
1085
1086
		// Movie recording stopped;  (fallthrough)
	case 2:
		// Movie recording started
		give_semaphore( gain.sem );
1087
1088
1089
1090
1091
1092
1093
1094
		break;
	case 1:
		// Movie recording about to start?
		break;
	default:
		// Uh?
		break;
	}
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
}

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



1108
1109
PROP_HANDLER( PROP_LV_ACTION )
{
1110
	const unsigned mode = buf[0];
1111
1112
1113
	enable_meters( mode );
	return prop_cleanup( token, property );
}
1114

1115
1116
PROP_HANDLER( PROP_MVR_REC_START )
{
1117
	const unsigned mode = buf[0];
1118
1119
	enable_recording( mode );
	return prop_cleanup( token, property );
1120
1121
}

a1ex's avatar
a1ex committed
1122
1123
void sounddev_task();

1124

1125
1126
1127
1128
1129
/** Replace the sound dev task with our own to disable AGC.
 *
 * This task disables the AGC when the sound device is activated.
 */
void
a1ex's avatar
a1ex committed
1130
my_sounddev_task()
1131
{
a1ex's avatar
a1ex committed
1132
	msleep( 2000 );
1133