
-- return the window size
function getWindowSize()
	return 797, 491
end

-- return the main font
-- the return is a list of font filename and size options
-- the first one found will be used
function getFont()
	if isMac then
		return { { name="GillSans.dfont", size=14 }, { name="Monaco.dfont", size=12 } }
	else
		return { { name="tahoma.ttf", size=14 }, { name="arial.ttf", size=12 } }
	end
end
-- return the label font
function getLabelFont()
	if isMac then
		return { { name="GillSans.dfont", size=11 }, { name="Monaco.dfont", size=9 } }
	else
		return { { name="tahoma.ttf", size=11 }, { name="arial.ttf", size=9 } }
	end
end
-- return the text field font
function getTextFieldFont()
	if isMac then
		return { { name="GillSans.dfont", size=12 }, { name="Monaco.dfont", size=10 } }
	else
		return { { name="tahoma.ttf", size=12 }, { name="arial.ttf", size=10 } }
	end
end

-- create and lay out all the controls
function createUI()

	-- load textures
	atlas = texture( "buttons.png" )
	background = texture( "bgtile.png" )
	setBackgroundTexture( background )

	local windowW = getWindowSize()
	
	-- set location of the banner
	setBannerPosition( 471.0 )
	
	-- set outline colours for groups
	setGroupActiveColour( 0.95, 0.9, 0.9, 1.0 )
	setGroupInactiveColour( 0.8, 0.4, 0.01, 1.0 )
	
	-- constants
	local pad = 10.0
	local kMeterW = 5
	local kMeterH = 69
	local kTriggerLvlPadH = 4.0
	local kMeterDrop = 14.0
	local kMeterPadH = 2.0
	local kPitchButtonW = 16
	local kPitchButtonH = 14
	local kTextFieldH = 12
	local kKnobSize = 42
	local kKnobSpace = 54
	local kEditTextH = 11
	local kSmallKnobSize = 16
	local kButtonW = 48
	local kButtonH = 32
	local kButtonSpaceH = 58
	local kButtonSpaceV = 42
	local kSmallIndicatorSpace = 20
	local kFadeTextW = 36
	local kFadeTextSpace = 42
		
	-- make renderers
	theKnobRenderer = knobRenderer( atlas, 6*kButtonW+1, 2*kButtonH+1, kKnobSize )
	theButtonRenderer = buttonRenderer( atlas )
	theSliderRenderer = sliderRenderer( atlas )

	-- buttons
	
	loopButtonsGroup = group( "Loop" )
	loopButtonsGroup:add( paramButton( "Tap Tempo",		0*kButtonSpaceH, 0*kButtonSpaceV, kButtonW, kButtonH, 3*kButtonW, 2*kButtonH ) )
	loopButtonsGroup:add( paramButton( "Tap Tempo", 	1*kButtonSpaceH, 0*kButtonSpaceV, kButtonW, kButtonH, 5*kButtonW, 0*kButtonH, true ) );
	loopButtonsGroup:add( paramButton( "Tap Record",	0*kButtonSpaceH, 1*kButtonSpaceV, kButtonW, kButtonH, 4*kButtonW, 2*kButtonH ) )
	loopButtonsGroup:add( paramButton( "Tap Record",	1*kButtonSpaceH, 1*kButtonSpaceV, kButtonW, kButtonH, 6*kButtonW, 0*kButtonH, true ) );
	loopButtonsGroup:add( paramButton( "Freeze Loop",	2*kButtonSpaceH, 1*kButtonSpaceV, kButtonW, kButtonH, 7*kButtonW, 0*kButtonH ) )
	loopButtonsGroup:add( paramButton( "Clear Loop",	2*kButtonSpaceH, 0*kButtonSpaceV, kButtonW, kButtonH, 3*kButtonW, 0*kButtonH ) )
	
	miscButtonsGroup = group( "Misc" )
	miscButtonsGroup:add( paramButton( "Pitch-Independent Delay",	0*kButtonSpaceH, 1*kButtonSpaceV, kButtonW, kButtonH, 8*kButtonW, 0*kButtonH ) )
	miscButtonsGroup:add( paramButton( "Trigger",					0*kButtonSpaceH, 0*kButtonSpaceV, kButtonW, kButtonH, 4*kButtonW, 0*kButtonH ) )
	miscButtonsGroup:add( button( "Prefs", "Prefs",					1*kButtonSpaceH, 0*kButtonSpaceV, kButtonW, kButtonH, 0*kButtonW, 0*kButtonH ) )
	miscButtonsGroup:add( paramButton( "Digital Mode",				1*kButtonSpaceH, 1*kButtonSpaceV, kButtonW, kButtonH, 1*kButtonW, 0*kButtonH ) )
	
	-- knobs
	
	local function knobAndTextField( group, param, label, x, y, size, textH, textW )
		textW = textW or size
		local knobx = x + 0.5 * ( textW - size )
		group:add( knob( param, "", knobx, y+textH+1, size ) )
		group:add( textField( param, label, x, y, textW, textH ) )
	end
	
	filterGroup = group( "Filter" )
	knobAndTextField( filterGroup, "Filter Cutoff", 	"Cutoff", 		0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Filter Q", 			"Q", 			1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Filter Type", 		"Type", 		2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Filter LFO Speed", 	"LFO Speed", 	3*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Filter LFO Depth", 	"LFO Depth", 	4*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Saturation", 		"", 			5*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( filterGroup, "Saturation Shape", 	"Shape", 		6*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	tapeSimGroup = group( "Sim" )
	tapeSimGroup:setVisible( false )
	knobAndTextField( tapeSimGroup, "Drive",			"Drive",		0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "Crunch",			"Crunch",		1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "Post Gain",		"Gain",			2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "Low Cut Freq", 	"Lo Freq", 		3*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "Low Cut Amount", 	"Lo Amnt", 		4*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "High Cut Freq", 	"Hi Freq", 		5*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( tapeSimGroup, "High Cut Amount", 	"Hi Amnt", 		6*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	inertiaGroup = group( "Inertia" )
	knobAndTextField( inertiaGroup, "PitchInertia", 			"Inertia", 		0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( inertiaGroup, "Delay Inertia Fade Time",	"Inertia Fade", 1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	inertiaGroup:add( paramButton( "Delay Inertia Mode",						2*kKnobSpace, 0, kButtonW, kButtonH, 2*kButtonW, 0*kButtonH ) )
	
	pingPongGroup = group( "Ping-Pong/Read Mode" )
	knobAndTextField( pingPongGroup, "Monoize Input",	"Mono-ize", 0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( pingPongGroup, "Initial Pan",		"Init Pan", 1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	pingPongGroup:add( paramButton( "Tape Read Mode", 2*kKnobSpace, 0, kButtonW, kButtonH, 8*kButtonW, 2*kButtonH ) )
	
	levelsGroup = group( "Levels" )
	levelsGroup:add( slider( "Trigger Level", 					0, -kMeterDrop+kTriggerLvlPadH, 12, kMeterH-2*kTriggerLvlPadH, "vertical", 
																2*kPitchButtonW+16+1, 4*kButtonH+2*12+1, 12, 8 ) )
	knobAndTextField( levelsGroup, "Dry Level", 	"Dry", 		12+2*kMeterW+kMeterPadH, 						0, kKnobSize, kTextFieldH )
	knobAndTextField( levelsGroup, "Effect Level", 	"Effect", 	12+2*kMeterW+kMeterPadH+kKnobSpace, 			0, kKnobSize, kTextFieldH )
	knobAndTextField( levelsGroup, "Input Level", 	"Input", 	12+4*kMeterW+3*kMeterPadH+kKnobSpace+kKnobSize, 0, kKnobSize, kTextFieldH )
	levelsGroup:add( paramButton( "Punch In Set Input Level",	12+4*kMeterW+3*kMeterPadH+kKnobSpace+2*kKnobSize+4, 14+kSmallIndicatorSpace, 36, 12, 4*kButtonW, 4*kButtonH+2*12 ) )
	levelsGroup:add( paramButton( "Punch Out Set Input Level",	12+4*kMeterW+3*kMeterPadH+kKnobSpace+2*kKnobSize+4, 14, 36, 12, 3*kButtonW, 4*kButtonH+2*12 ) )
	
	delayTimeGroup = group( "Delay Time" )
	knobAndTextField( delayTimeGroup, "L->L time", 	"L-L Time", 0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayTimeGroup, "L->R time", 	"L-R Time", 1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayTimeGroup, "R->L time", 	"R-L Time", 2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayTimeGroup, "R->R time", 	"R-R Time", 3*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	delayLevelGroup = group( "Delay Level" )
	knobAndTextField( delayLevelGroup, "L->L feedback", 	"L-L Level", 0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayLevelGroup, "L->R feedback", 	"L-R Level", 1*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayLevelGroup, "R->L feedback", 	"R-L Level", 2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	knobAndTextField( delayLevelGroup, "R->R feedback", 	"R-R Level", 3*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	masterLevelGroup = group( "Master" )
	knobAndTextField( masterLevelGroup, "Master feedback", "Feedback", 0*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	-- pitch controls
	
	pitchSliderGroup = group( "Pitch" )
	local kPixPerSemiTone = 20
	local kPitchSliderH = 20
	local kPitchSliderPadW = 10
	local pitchSliderW = 24*kPixPerSemiTone + 1
	local px = kPitchSliderPadW
	
	pitchSliderGroup:add( slider( "Pitch", 			px, kPitchButtonH+5, pitchSliderW, kPitchSliderH, "horizontal", 
													2*kPitchButtonW+1, 4*kButtonH+2*12+1, 14, 20 ) )
	px = px + pitchSliderW + kPitchSliderPadW + pad
	pitchSliderGroup:add( textField( "Pitch", "", 	px, kButtonH+15, kButtonW, kTextFieldH ) )
	pitchSliderGroup:add( paramButton( "Reverse",	px, 0, kButtonW, kButtonH, 2*kButtonW, 2*kButtonH ) )
	px = px + kButtonW + pad;
	pitchSliderGroup:add( paramButton( "Stop",		px, 0, kButtonW, kButtonH, 9*kButtonW, 2*kButtonH ) )
	px = px + kButtonW + pad;
	
	local blackNotes = { [1]=true, [3]=true, [6]=true, [8]=true, [10]=true }
	paramID_Pitch = getParameterID( "Pitch" )
	paramID_Reverse = getParameterID( "Reverse" )
	for i=-12,12,1 do
		local s = (i>=0) and i or (i+12)
		local u = blackNotes[s] and kPitchButtonW or 0
		local xx = kPitchSliderPadW + (i+12)*kPixPerSemiTone - 0.5 * kPitchButtonW;

		pitchSliderGroup:add( luaButton( function() setPitch( i, false ) end,
			"Pitch", "Set Pitch", "push", 
			xx, kPitchButtonH+kPitchSliderH+10, kPitchButtonW, kPitchButtonH, u, 4*kButtonH+2*12 ) )
		pitchSliderGroup:add( luaButton( function() setPitch( i, true ) end,
			"Pitch", "Set Pitch", "push", 
			xx, 0, kPitchButtonW, kPitchButtonH, u, 4*kButtonH+2*12 ) )

	end
	
	knobAndTextField( pitchSliderGroup, "Pitch LFO Speed", 	"LFO Speed", px+0*kKnobSpace, 10, kKnobSize, kTextFieldH )
	knobAndTextField( pitchSliderGroup, "Pitch LFO Depth", 	"LFO Depth", px+1*kKnobSpace, 10, kKnobSize, kTextFieldH)
	
	
	-- delay group
	
	delayGroup = group( "Delay Time" )
	
	-- delay slider controls
	local kDelaySliderW = 185
	delayGroup:add( textField( "Delay Slider Min", "Min", 0, 0, kKnobSize, kTextFieldH, "%.2f" ) )
	delayGroup:add( delaySlider( kPitchSliderPadW, 22, kDelaySliderW, kPitchSliderH, "horizontal", 2*kPitchButtonW+1, 4*kButtonH+2*12+1, 14, 20 ) )
	delayGroup:add( textField( "Delay Slider Max", "Max", kDelaySliderW+2*kPitchSliderPadW-kKnobSize, 0, kKnobSize, kTextFieldH, "%.2f" ) )
	
	-- master delay controls
	local ddx = kDelaySliderW + 2*kPitchSliderPadW + pad
	delayGroup:add( textField( "Max Delay Time", "Max Delay", 	ddx, 0, kKnobSize, kTextFieldH, "%.2f" ) )
	delayGroup:add( textField( "Delay Time", "Delay Time", 		ddx, 32, math.floor(kKnobSize*1.25), kTextFieldH, "%.3f" ) )
	
	-- sync controls
	
	syncGroup = group( "Sync" )
	syncGroup:add( textField( "Beats", "", 		0, 44, kKnobSize, kTextFieldH ) )
	syncGroup:add( dropDown( "Beat Divisor",	0, 26, 48, 12 ) )
	syncGroup:add( paramButton( "Triplet",		0, 8, 48, 12, 0, 4*kButtonH ) )
	syncGroup:add( paramButton( "Host Sync",	kButtonSpaceH, 26, kButtonW, kButtonH, 5*kButtonW, 2*kButtonH ) )

	syncGroup:add( dropDown( "Sync Group Mode",	0, -10, 48, 12 ) )
	knobAndTextField( syncGroup, "Sync Group Number", "", kButtonSpaceH, -10, kSmallKnobSize, kTextFieldH, kFadeTextW )
	
	-- MIDI clock controls
	
	clockGroup = group( "MIDI Clock Out" )
	local kClockMenuW = 48
	local kClockMenuH = 12
	clockGroup:add( dropDown( "Tap Rec Start Clock Msg",	0, 0, kClockMenuW, kClockMenuH ) )
	clockGroup:add( dropDown( "Tap Rec End Clock Msg", 		1*58, 0, kClockMenuW, kClockMenuH ) )
	clockGroup:add( dropDown( "Clock Msg", 					1*58, kSmallIndicatorSpace, kClockMenuW, kClockMenuH ) )
	clockGroup:add( paramButton( "Pause Clock",				2*58, 0, kButtonW, kButtonH, 1*kButtonW, 2*kButtonH ) )
	
	-- punch in/out controls
	
	punchGroup = group( "Punch In/Out" )
	punchGroup:add( dropDown( "Punch In Quantize",				0, 0, kClockMenuW, kClockMenuH ) )
	punchGroup:add( dropDown( "Punch Out Quantize",				1*58, 0, kClockMenuW, kClockMenuH ) )
	punchGroup:add( indicator( "PendingPunch", 					0*58, kSmallIndicatorSpace, 48, 12, 3*kButtonW, 4*kButtonH ) )
	punchGroup:add( dropDown( "Punch Out Type",					1*58, kSmallIndicatorSpace, kClockMenuW, kClockMenuH ) )
	
	-- envelope controls
	
	fadeGroup = group( "Fade" )
	knobAndTextField( fadeGroup, "Env Attack", 	"", 	0, 0, kSmallKnobSize, kTextFieldH, kFadeTextW )
	knobAndTextField( fadeGroup, "Env Release",	"", 	kFadeTextSpace, 0, kSmallKnobSize, kTextFieldH, kFadeTextW )
	fadeGroup:add( dropDown( "Auto Clear After Fade",	2*kFadeTextSpace, 0, 48, 12 ) )
	fadeGroup:add( paramButton( "Env Gate",				2*kFadeTextSpace+kButtonSpaceH, 0, kButtonW, kButtonH, 0*kButtonW, 2*kButtonH ) )
	fadeGroup:add( paramButton( "Env Override",			2*kFadeTextSpace+2*kButtonSpaceH, 0, kButtonW, kButtonH, 9*kButtonW, 0*kButtonH ) )
	
	-- record offset controls
	
	recordOffsetGroup = group( "Record Offset" )
	recordOffsetGroup:setVisible( false )
	recordOffsetGroup:add( paramButton( "Record Offset Enable",					0, 0, kButtonW, kButtonH, 7*kButtonW, 2*kButtonH ) )
	recordOffsetGroup:add( dropDown( "Record Offset Type", 						kKnobSpace, 0, 48, 12 ) )
	knobAndTextField( recordOffsetGroup, "Record Offset Frames",	"Offset",	2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	
	-- fx loop controls
	
	if isFxLoopVersion then
		preWriteFxLoopGroup = group( "Pre-Write Fx Loop" )
		preWriteFxLoopGroup:setVisible( false )
		preWriteFxLoopGroup:add( paramButton( "Pre-Write FX Loop Enable",				0, 0, kButtonW, kButtonH, 7*kButtonW, 2*kButtonH ) )
		preWriteFxLoopGroup:add( dropDown( "Pre-Write Latency Compensation", 			kKnobSpace, 0, 48, 12 ) )
		knobAndTextField( preWriteFxLoopGroup, "Pre-Write Manual Latency",	"Latency",	2*kKnobSpace, 0, kKnobSize, kTextFieldH )

		preSatFxLoopGroup = group( "Pre-Sat Fx Loop" )
		preSatFxLoopGroup:setVisible( false )
		preSatFxLoopGroup:add( paramButton( "Pre-Sat FX Loop Enable",				0, 0, kButtonW, kButtonH, 7*kButtonW, 2*kButtonH ) )
		preSatFxLoopGroup:add( dropDown( "Pre-Sat Latency Compensation", 			kKnobSpace, 0, 48, 12 ) )
		knobAndTextField( preSatFxLoopGroup, "Pre-Sat Manual Latency",	"Latency",	2*kKnobSpace, 0, kKnobSize, kTextFieldH )
	end

	OSCGroup = group( "OSC" )
	knobAndTextField( OSCGroup, "OSC Port Offset", 	"", 	0, 0, kSmallKnobSize, kTextFieldH, kFadeTextW )

	-- positions!
	
	local left = 20.0
	local ight = windowW - 20.0
	
	local x = left;
	local y = left;
	
	local theValueDisplay = valueDisplay()
	theValueDisplay:setColour( 1, 1, 1, 1 )
	
	clockGroup:setPosition( x, y )
	theValueDisplay:setPosition( "Tempo", x, y+kSmallIndicatorSpace, 48, 12 )
	
	x = x + clockGroup:width() + pad
	punchGroup:setPosition( x, y )
	
	x = x + punchGroup:width() + pad
	fadeGroup:setPosition( x, y )
	theValueDisplay:setPosition( "EnvLevel", x+2*kFadeTextSpace, y+kSmallIndicatorSpace, 48, 12 )
	x = x + fadeGroup:width() + pad
	OSCGroup:setPosition( x, y )
	
	x = left
	y = y + clockGroup:height() + pad
	loopButtonsGroup:setPosition( x, y )
	add( paramButton( "Freeze Mode", x+2*kButtonSpaceH, y+2*kButtonSpaceV-6, 48, 12, 7*kButtonW, 4*kButtonH ) )
	x = x + loopButtonsGroup:width() + pad
	y = y + 10.0
	delayGroup:setPosition( x, y )
	add( luaButton( toggleFxLoops, "More", "Show/Hide More Controls", "toggle", 
					x, y + loopButtonsGroup:height() - 10 - 12 - pad, 48, 12, 6*kButtonW, 4*kButtonH ) )
	toggleWaveformButton = luaButton( toggleWaveform, "Tape", "Show/Hide Tape", "toggle", 
					x + 48 + pad, y + loopButtonsGroup:height() - 10 - 12 - pad, 48, 12, 8*kButtonW, 4*kButtonH )
	add( toggleWaveformButton )
	theValueDisplay:setPosition( "DelayTime", x+77, y-10, kKnobSize+10, 12 )
	theValueDisplay:setPosition( "DelayBeats", x+77, y+6, kKnobSize+10, 12 )
	x = x + delayGroup:width() + pad
	syncGroup:setPosition( x, y )
	x = x + syncGroup:width() + pad
	y = y - 10.0
	miscButtonsGroup:setPosition( x, y )
	add( indicator( "DigitalModeActive", x+1*kButtonSpaceH, y+2*kButtonSpaceV-6, 48, 12, 5*kButtonW, 4*kButtonH ) )
	x = left
	y = y + loopButtonsGroup:height() + pad + 10
	delayTimeGroup:setPosition( x, y )
	recordOffsetGroup:setPosition( x, y )
	if isFxLoopVersion then
		local xx = x + recordOffsetGroup:width() + pad
		preWriteFxLoopGroup:setPosition( xx, y )
		xx = xx + preWriteFxLoopGroup:width() + pad
		preSatFxLoopGroup:setPosition( xx, y )
	end
	x = x + delayTimeGroup:width() + pad
	delayLevelGroup:setPosition( x, y )
	x = x + delayLevelGroup:width() + pad
	masterLevelGroup:setPosition( x, y )
	
	x = x + masterLevelGroup:width() + pad
	levelsX = x
	levelsY = y - kMeterDrop
	levelsGroup:setPosition( x, y )
	
	x = left
	local minx, miny, maxx, maxy = delayTimeGroup:bounds()
	y = maxy + pad + 10
	pitchSliderGroup:setPosition( x, y )
	local pitchSliderY = y
	
	x = left
	y = y + pitchSliderGroup:height() + pad + 10
	filterGroup:setPosition( x, y )
	tapeSimGroup:setPosition( x, y )
	saturationButton = paramButton( "Saturation On/Off", x+5*kKnobSpace+0.5*(kKnobSize-48), y-16, 48, 12, 4*kButtonW, 4*kButtonH )
	add( saturationButton )
	toggleFilterButton = luaButton( toggleFilter, "Tape Sim", "Show/Hide Tape Sim Controls", "toggle", 
					x, y + kKnobSize + kTextFieldH + 6, 54, 12, 9*kButtonW, 4*kButtonH )
	add( toggleFilterButton )
	
	x = x + filterGroup:width() + pad
	inertiaGroup:setPosition( x, y )
	
	x = x + inertiaGroup:width() + pad
	pingPongGroup:setPosition( x, y )
		
	-- positions done
		
	-- waveform display
	
	theWaveformDisplay = waveformDisplay( left, pitchSliderY, windowW - 2*left, pitchSliderGroup:height() + filterGroup:height() )
	add( theWaveformDisplay )
	theWaveformDisplay:setVisible( false )
	theWaveformDisplay:setMoving( true )
	theWaveformDisplayMovingButton = luaButton( toggleWaveformMoving, "Moving", "Toggle moving waveform", "toggle", 
					left, pitchSliderY-12, 48, 12, 5*kButtonW, 4*kButtonH+2*12 )
	add( theWaveformDisplayMovingButton )
	theWaveformDisplayMovingButton:setVisible( false )

	-- meters
	
	x = levelsX
	y = levelsY
	x = x + 12
	
	meters = levelMeters( atlas, 512-5, 0, kMeterW, kMeterH )
	meters:add( "InputRmsLeft", x, y )
	x = x + kMeterW
	meters:add( "InputRmsRight", x, y )
	x = x + kMeterW
	add( paramButton( "Input Level Sensitivity", x, y, 12, 6, 0, 4*kButtonH+2*12+2*kPitchButtonH ) )
	x = x + 2*kMeterPadH+kKnobSize+kKnobSpace
	meters:add( "FxRmsLeft", x, y )
	x = x + kMeterW
	meters:add( "FxRmsRight", x, y )

end		-- of createUI()

function toggleFxLoops()
	local v = not delayTimeGroup:isVisible()
	delayTimeGroup:setVisible( v )
	delayLevelGroup:setVisible( v )
	masterLevelGroup:setVisible( v )
	v = not v
	if isFxLoopVersion then
		preWriteFxLoopGroup:setVisible( v )
		preSatFxLoopGroup:setVisible( v )
	end
	recordOffsetGroup:setVisible( v )
	redrawLabels()
end

function toggleWaveform()
	local v = toggleWaveformButton:getState()
	theWaveformDisplay:setVisible( v )
	theWaveformDisplayMovingButton:setVisible( v )
	v = not v
	inertiaGroup:setVisible( v )
	pingPongGroup:setVisible( v )
	pitchSliderGroup:setVisible( v )
	toggleFilterButton:setVisible( v )
	if not v then
		filterGroup:setVisible( v )
		saturationButton:setVisible( v )
		tapeSimGroup:setVisible( v )
		redrawLabels()
	else
		toggleFilter()
	end
end

function toggleWaveformMoving()
	theWaveformDisplay:setMoving( not theWaveformDisplay:isMoving() )
end

function toggleFilter()
	if toggleWaveformButton:getState() then
		return
	end
	local v = not toggleFilterButton:getState()
	filterGroup:setVisible( v )
	saturationButton:setVisible( v )
	v = not v
	tapeSimGroup:setVisible( v )
	redrawLabels()
end

function setPitch( pitch, reverse )
	setParameter( paramID_Pitch, pitch )
	if reverse then
		setParameter( paramID_Reverse, ( getParameter( paramID_Reverse ) > 0 ) and 0 or 1 )
	end
end
