// Example of a one button standard looper. Second loop length will be the length of all other loops  		
//				Second loop length will be a multiple of first loop length
//
// Author: LoopToGo
//
// Arguments or user arguments : 
// 		1 : (optional) gFirstTrack, track number to start the looping, default is 1, -1 = selected
// 		2 : (optional) gNbBars, number of bars For the first recording, default is 10
//		3 : (optional) if 1, loops length are all equal (same behavior as dtOne.ltgs), -1 : use permanent variable $pUniqueLoopLengthChecked
//		4 : (optional) if 1, will use pDontChangeTrackMonitoring variable to determine if Track monitoring is unchanged or not
//
//  Warning when using with midi events, make sure "Use MIDI events as arg" is uncheck!	

loadScript ..\aliases.txt					// To use aliases instead of shortcut commands

/////////////////////////////////////////////////////////
///////////// For debugging

// Note see LoopToGo\libs\debugUtilities.ltgl For details on how to use debugging

loadScript LoopToGo\libs\debugUtilities.ltgl

// Uncomment the following line to remove all debug prints. When uncommented, there is no impact on perfromance
alias debug doNothing

// Set tag Info, Info2 and Info3 to 1 to print or 0 to not print
debugTag Info 0
debugTag Info2 0
debugTag Info3 0
debugTag Info4 0

//alias debug print
alias print debug Info [ $scriptLineNumber ] 
alias print2 debug Info2 [ $scriptLineNumber ] 
alias print3 debug Info3 [ $scriptLineNumber ] 
alias print4 debug Info4 [ $scriptLineNumber ] 


// Uses { and } to increment and decrement debug message  
alias {		debugIncreaseIndentation
alias }		debugDecreaseIndentation

print " "  // new line!
print "Start of script: "

///////////// End of debugging
/////////////////////////////////////////////////////////

// Do not call this script when there is no track!
if $nbTracks < 1
	setUserArgs  // reset the users args just in case!
	endScriptDynamic
endIf

// Refrain from calling this script twice in a short time (debounce of midi events below)

variable deltaTime $sysTime - $gLastExecutionTime
globalVariable gLastExecutionTime $sysTime
variable isSynced 1
variable quantization 1 // in bar

//print2 $deltaTime $sysTime $gLastExecutionTime

/////////////////////////////////////////////////////////
///////////// Script configuration

alias aDefaultFirstTrack					1			// The first track used for looping
alias aDefaultNbBars						10			// Lenght of first record loop if not specified by $arg1 or $userArg1
alias aGridLineColor						#224422		// Color for the grid lines
alias aBeforeLoopEnd						0.02		// Increase to make the extend before the end of the loop! Must not be greater than 0.02!!!

///////////// End of configuration
/////////////////////////////////////////////////////////

alias true 1
alias false 0

alias UnLockedState 0
alias LockedState 3

alias OverdubMode 6
alias RepeatRecord 2

// Looper States
alias LooperStoppedAndEmpty 0
alias LooperStopped 1
alias LooperRecord 2
alias LooperPlay 3
alias LooperOverdubFirst 4
alias LooperOverdubSecond 5

// Track monitoring mode
alias InLoopsOrWhileTrackSelected 3

// Debouncing : midi buttons can sen multiple events in a short period of time
if $deltaTime < 200
	print3 "Debouncing: " 	$deltaTime 
	endScriptDynamic
endIf	

print deltaTime : $deltaTime

// Code For pausing or resetting the looper with a double click. 
if 0 // this section is disabled
	if  $deltaTime < 500
		print "Double click: " 	$deltaTime	$gLooperState $isRecording
			
		if $gLooperState = LooperStopped
			loadScript LoopToGo\LooperEmulations\resetSong.ltgs			// The user ask For stop twice to reset the looper	
			print Reset song 
			endScriptDynamic		
		endIf 
		
		if $isRecording
			pause
			print Pause 
			endScriptDynamic
		endIf

	endIf
endIf

callFunction fctComputeLooperState	// The looper state must be compute in case the user used Ctr-Z (undo) or Ctr-Y (Redo)

// If we are not starting from scratch, just make sure we have a jam marker
if $nbMarkers < 1
	if $gSecondRecordLoopID		
		print2 startJamMode $gSecondRecordLoopID
		startJamMode $gSecondRecordLoopID
	else
		print2 startJamMode $gFirstRecordLoopID
		startJamMode $gFirstRecordLoopID		
	endIf
endIf 

if $arg1 = 0
	variable arg1 aDefaultFirstTrack
endIf

if $arg2 = 0
	variable arg2 aDefaultNbBars
endIf

callFunction looperStateManager $arg1 $arg2 $arg3

// Choose action depending on the actual looper state 
function looperStateManager
{
	if $gLooperState = LooperStoppedAndEmpty
	
		// default values
		globalVariable gNbBars aDefaultNbBars
		globalVariable gFirstTrack	aDefaultFirstTrack
		persistentVariable pUniqueLoopLength $pUniqueLoopLengthChecked
		
		if $pNbMaxBars
			globalVariable gNbBars $pNbMaxBars
		endIf
		
		if $arg4 // If not provided, it will be 0!
			variable dontChangeMonitoring  $pDontChangeTrackMonitoring
		else
			variable dontChangeMonitoring  0			
		endIf
		
		if $nbUserArgs
			callFunction setParameter 1 $nbUserArgs $userArg1 gFirstTrack 			g  			// g = global
			callFunction setParameter 2 $nbUserArgs $userArg2 gNbBars 				g				// g = global
			callFunction setParameter 3 $nbUserArgs $userArg3 pUniqueLoopLength 	p		// p = persistent
		
			setUserArgs // Make sure user args a resetted
		else
			callFunction setParameter 1 $nbArgs $arg1 	gFirstTrack 		g  			// g = global
			callFunction setParameter 2 $nbArgs $arg2 	gNbBars 			g				// g = global			
			callFunction setParameter 3 $nbArgs $arg3 	pUniqueLoopLength 	p		// p = persistent		
		endIf
						
		callFunction fctInitAndStartRecording	

		setAutoTrackBar false  // To zoom to almost 100% of the view
		// Adjust the view size 
		setUserArgs 0 // argument for next shortcut 1 = zoom
		shortcutByID  116  // horizontal zoom		
		
		exitFunction

	endIf
	setUserArgs  // reset the users args just in case!

		
	if $gLooperState = LooperRecord		
		callFunction fctStopFirstRecording		
		
		setAutoTrackBar false  // To zoom to almost 100% of the view
		// Adjust the view size 
		setUserArgs 0 // argument for next shortcut 1 = zoom
		shortcutByID  116  // horizontal zoom
		
		exitFunction
	endIf
																						
	if $gLooperState = LooperPlay	
		callFunction fctNewOverdubFirst		
		
		exitFunction
	endIf
					 			
	if $gLooperState = LooperOverdubFirst		
		callFunction fctStopExtendingSecondOverdub
	
		exitFunction
	endIf							
		
	if $gLooperState = LooperOverdubSecond		
		callFunction fctStopOverdubSecond
		
		exitFunction
	endIf
			
	if $isRecording	
		callFunction fctStopOverdubSecond
		
		exitFunction
	endIf
				
	setBar $gFirstRecordLoopStart
	
	record // Start recording
		
}
endFunction

// First stop of recording
function fctStopFirstRecording
{
	
	print  "fctStopFirstRecording: " 
	
	// We must compute the next start of the record loop which must be the start of the first record loop plus a integer number of the first record loop length
						
	globalVariable gFirstRecordLoopEnd $songBar + .5 $quantization
	globalVariable gFirstRecordLoopLength $gFirstRecordLoopEnd - $gFirstRecordLoopStart
	globalVariable gLastEnd $gFirstRecordLoopEnd
	
	if $gFirstRecordLoopEnd < $gFirstRecordLoopStart + $quantization
		globalVariable gFirstRecordLoopEnd $gFirstRecordLoopStart + $quantization $quantization
	endIf
	
	print  $lastCreatedRecordLoopID $gFirstRecordLoopStart $gFirstRecordLoopEnd
			
	setLoopStartAndEnd $lastCreatedRecordLoopID $gFirstRecordLoopStart $gFirstRecordLoopEnd
	
	print4 setLoopStartAndEnd $lastCreatedRecordLoopID $gFirstRecordLoopStart $gFirstRecordLoopEnd
	
	removeCustomGridLine $gFirstRecordLoopStart + $gNbBars
	addCustomGridLine $gFirstRecordLoopEnd		3 	aGridLineColor

	setLoopColor $lastCreatedRecordLoopID "Green"  // Green because we are playing back what was recorded		
	
	startJamMode $lastCreatedRecordLoopID  true // true = start in overdub. This is needed only for when the user call the script before the end of the loop so the data are not erased when going back to the start of the loop
	

	if $songBar < $gFirstRecordLoopEnd	// Before the end of the loop, we must jump to the beginning + delta equivalent to the extra length	
		print4 "Pressed before end of loop"

		setLoopLockState $lastCreatedRecordLoopID LockedState true // true = do a ramp down of the inputs the lock
				
	else
		print4 "Pressed equal or after end of loop"
		
		setLoopLockState $lastCreatedRecordLoopID LockedState
		
		jumpTo $gFirstRecordLoopStart + $songBar - $gFirstRecordLoopEnd // More accurate than setBar!!!
		waitDelta $tempo/5000  // Wait For the jump! Must not be too small because will causes issue with high tempo
	endIf	
		
}
endFunction


// First initialization of variables and start of recording
function fctInitAndStartRecording
{	
	print  "fctInitAndStartRecording: " 
	
	globalVariable gFirstTime true
	globalVariable gFirstRecordLoopID 0
	globalVariable gInRecordLoop true
	globalVariable gLooperState LooperRecord
	globalVariable gFirstRecordLoopStart $songBar
	globalVariable gSecondRecordLoopLength 0
	globalVariable gSecondRecordLoopID 0
	
	setUserArgs 1		// Arg1 to for next shortcut 1: vertically zoomed
	shortcutByID 9		// Set vertical zomm mode
	
	selectTrack $gFirstTrack
			
	if $selectedTrack = 0
		selectTrack aDefaultFirstTrack
	endIf
	
	ifNot $dontChangeMonitoring
		callFunction setTracksMonitoringMode
	endIf
	// First record loop	
	
	globalVariable gFirstRecordLoopEnd $gFirstRecordLoopStart + $gNbBars
	
	
	addRecordLoop $selectedTrack FirstLoop $gFirstRecordLoopStart $gFirstRecordLoopEnd
	
	print3 addRecordLoop $selectedTrack FirstLoop $gFirstRecordLoopStart $gFirstRecordLoopEnd
	
	removeAllCustomGridLines

	
	if $isSynced
		
		setGridLinesVisibility 7 // Normal mode for Sinced looping
		
	else // Synced
		setGridLinesVisibility 0 // No standard grid line
				
		addCustomGridLine $gFirstRecordLoopStart				3	aGridLineColor
		addCustomGridLine $gFirstRecordLoopStart + $gNbBars		3 	aGridLineColor
	
	endIf
	
	globalVariable gFirstRecordLoopID	$lastCreatedRecordLoopID
	globalVariable gFirstLoopTrack $selectedTrack
	
	
	
	record // Start recording
	
	startJamMode $lastCreatedRecordLoopID
		
	setLoopColor $lastCreatedRecordLoopID "Red"  // Green because we are playing back what was recorded		
	
	// Adjust the view size 
	setUserArgs 0 // argument for next shortcut 1 = zoom
	shortcutByID  116  // horizontal zoom
			
}
endFunction

// Creation of the first overdubing loop
function fctNewOverdubFirst
{	
	print "fctNewOverdubFirst: " 
	
	setLoopLockState $lastCreatedRecordLoopID LockedState
	
	setLoopColor $lastCreatedRecordLoopID "Green"  // Green because we are playing back what was recorded		
	
	if $gSecondRecordLoopLength = 0
				
		addRecordLoop $selectedTrack "Overdub" $gFirstRecordLoopStart $gFirstRecordLoopEnd
		
		print3 addRecordLoop $selectedTrack "Overdub" $gFirstRecordLoopStart $gFirstRecordLoopStart + $gFirstRecordLoopLength
		
		// The next line is to make sure the start of the recording in the middle of the loop will be smooth even if the input already as sound
		armRampUpRecording $lastCreatedRecordLoopID true  // true = ramp immediatly, false (or not provided) = will be 0 until end of the loop then will ramp up
				
		if $pUniqueLoopLength
			setLoopJamState $lastCreatedRecordLoopID OverdubMode 
			setLoopColor $lastCreatedRecordLoopID "Yellow"		
		else
			setLoopJamState $lastCreatedRecordLoopID RepeatRecord 
			
			setLoopColor $lastCreatedRecordLoopID "Red"		

		endIf
		
		getLoopEnd	loopEnd $lastCreatedRecordLoopID
		
		globalVariable gSecondRecordLoopLength $gFirstRecordLoopLength  // First time, will be the same length as the first record loop!
		globalVariable gSecondRecordLoopID	 $lastCreatedRecordLoopID  
				
				
		if $pUniqueLoopLength
			exitFunction // We don't create the extent function marker, all loops will be same length
		endIf
		
		variable arguments $lastCreatedRecordLoopID  & " " & $gFirstRecordLoopLength

		print gFirstRecordLoopLength : $gFirstRecordLoopLength gSecondRecordLoopLength : $gSecondRecordLoopLength [ $arguments ]
		
		addFunctionMarker $gFirstRecordLoopEnd - aBeforeLoopEnd "Overdub" fctExtendLoopRange $arguments
		
		globalVariable gLooperFunctionMarkerID $lastCreatedMarkerID
		
		setMarkerColor $gLooperFunctionMarkerID #775544


		print2 : gLooperFunctionMarkerID: $gLooperFunctionMarkerID
	else
	
		addRecordLoop $selectedTrack "Overdub" $gFirstRecordLoopStart $gFirstRecordLoopStart + $gSecondRecordLoopLength
		
		print3 addRecordLoop $selectedTrack "Overdub" $gFirstRecordLoopStart $gFirstRecordLoopStart + $gSecondRecordLoopLength
		
		// The next line is to make sure the start of the recording in the middle of the loop will be smooth even if the input already as sound
		armRampUpRecording $lastCreatedRecordLoopID true  // true = ramp immediatly, false (or not provided) = will be 0 until end of the loop then will ramp up
		
		setLoopJamState $lastCreatedRecordLoopID OverdubMode 
		
		setLoopColor $lastCreatedRecordLoopID "Yellow"		

		
		// We only select the last record loop to be able to use the E shortcut (erase)
		unselectAllLoops
		selectLoop $lastCreatedRecordLoopID  


	endIf
				
}
endFunction

// Creation of the second overdubing loop and other subsequent overdub loops
function fctNewOverdubSecond
{	
	print3 "fctNewOverdubSecond: "  $gLooperState  LooperOverdubFirst LooperOverdubSecond

	//globalVariable gLooperState LooperOverdubFirst
	
	if $nbMarkers > 1
	
		// First time in the second overDub.Since there is a marker that extend the looght length we, continue overbubing but we stop the loop extent!
			
		print3 "fctNewOverdubSecond:   nbMarkers > 1 "

				
	else
	
		// We don't use script markers, it means that it is not the first time we use a second overdub, we change to play!!! 
		
		callFunction fctStopOverdubSecond		 
				
	endIf
				
}
endFunction



// Stopping the extension of loop by deleting the Function marker responsible For extension
function fctStopExtendingSecondOverdub
{
	print  "fctStopExtendingSecondOverdub: nbMarkers"  $nbMarkers
	
	deleteAllMarkers  2049 // delete Function marker 
		
	// Make sure we are overdubbing
	
	setLoopJamState $lastCreatedRecordLoopID OverdubMode
		

	setLoopColor $lastCreatedRecordLoopID "Yellow"	

	if $gFirstRecordLoopLength*1.1 < $gSecondRecordLoopLength
		// Second loop is longer thean the first loop, it means that the end of the 
	endIf

}
endFunction

// Stop second overdubbing and subsequent
function fctStopOverdubSecond
{
	print  "fctStopOverdubSecond: " 
	
		
	setLoopLockState $lastCreatedRecordLoopID LockedState true
	
	setLoopColor $lastCreatedRecordLoopID "Green"  // Green because we are playing back what was recorded		
	
	deleteAllMarkers  2049 // delete Function marker Extent loop, just in case!!!!
	print Deleted all marker 2049
}
endFunction

// Determine the looper state based on recording state and loops state
function fctComputeLooperState
{

	print fctComputeLooperState Before : $gLooperState $gSecondRecordLoopID $gSecondRecordLoopLength
	
	variable previousLooperState $gLooperState
	
	globalVariable gLooperState LooperPlay
	
	ifNot $isRecording
		if $nbLoops = 0
			globalVariable gLooperState LooperStoppedAndEmpty
			exitFunction
		endIf
			
		globalVariable gLooperState LooperStopped
				
		exitFunction
	endIf	
	
	
	for loop 1 $nbLoops					// We check all the loops in case the user deleted a loop or did Ctr-Z!
		variable loopID loopID & $loop
		
		getLoopNextJamState loopJamMode $$loopID
		getLoopLockState loopLockState $$loopID
		getLoopType loopType $$loopID

		if $loopType = Record
		
		print2 $gLooperState $loopJamMode $loopLockState $nbMarkers
		
			if $loopLockState = UnLockedState
				if $loopJamMode = OverdubMode		
					if $previousLooperState = LooperOverdubFirst						
						globalVariable gLooperState LooperOverdubSecond																		
						exitFunction //Found, no need to continue searching!
					endIf
					
					if $previousLooperState = LooperOverdubSecond
						globalVariable gLooperState LooperOverdubSecond
						exitFunction //Found, no need to continue searching!
					endIf
					
					if $nbMarkers < 2		// No Function markers, we are in last overdub mode
						globalVariable gLooperState LooperOverdubSecond
						exitFunction //Found, no need to continue searching!
					endIf
					
					globalVariable gLooperState LooperOverdubFirst
					
					exitFunction //Found, no need to continue searching!
					
				endIf
				
				ifNot  $gFirstRecordLoopID = $lastCreatedRecordLoopID
					globalVariable gLooperState LooperOverdubFirst
					exitFunction //Found, no need to continue searching!
				endIf
				
				globalVariable gLooperState LooperRecord
				exitFunction //Found, no need to continue searching!
				
								
			endIf
		endIf	
					
	endFor
		
}
endFunction


// Set all tracks Play Record and Pause monitoring mode to "In loops or when track is selected"
function setTracksMonitoringMode
{
	print  "setTracksMonitoringMode: " 

	for track 1 $nbTracks
		setChainRecordPlayMonitorMode $track InLoopsOrWhileTrackSelected 				
	endFor
}
endFunction


//////////////////////////////////////////////////////////

alias print print	// We must reset the print aliases because it refers to a functions in the debug library which will noy be accessible from the function 

// This Function is designed to be called by a FunctionMarker
// It does have access to the global variables but not to the othe local variables in this file!!!!

function fctExtendLoopRange // recordLoopID incrementWidth
{
	//print function fctExtendLoopRange
	
	waitDelta 1/$tempo   // This line allows this functions to run outside the audio engine to avoid glitches 

	//ifNot $nbArgs = 2
	//	print fctExtendLoopRange requires 2 arguments [ $nbArgs : recordLoopID "=" $arg1 incrementWidth "=" $arg2 ]
	//	exitFunction
	//endIf
			
	getLoopJamState loopJamMode $arg1
	
	// The first time, the loop will be in record mode, we just set it in overdub mud
	// The second time, the loop will be in overdub, we move the marker and extend the loop!

	if $loopJamMode = OverdubMode
			
		variable newEnd $gFirstRecordLoopStart + $gSecondRecordLoopLength + $arg2
		variable newStart $gLastEnd
		
			
		addPlayLoop $gFirstLoopTrack $gFirstRecordLoopID  $newStart $newEnd 	//Always on first track
		
		globalVariable gLastEnd $newEnd
		

		setLoopStartAndEnd $arg1 $gFirstRecordLoopStart $newEnd
		
		getLoopEnd playLoopEnd $lastCreatedPlayLoopID 
		
		
												
		// Move script markers just before the end 			
		setMarkerBar $gLooperFunctionMarkerID $newEnd - aBeforeLoopEnd
		
	
		globalVariable gSecondRecordLoopLength $newEnd - $gFirstRecordLoopStart
		
		addCustomGridLine $newEnd		3 	aGridLineColor
	
		
	endIf

	setLoopJamState $arg1 OverdubMode
	
	setLoopColor $arg1 "Yellow"	
			
	setMarkerName $gLooperFunctionMarkerID "Extend"
	setMarkerColor $gLooperFunctionMarkerID #335577
	
	// Adjust the view size 
	setUserArgs 0 // argument for next shortcut 1 = zoom
	shortcutByID  116  // horizontal zoom
}		
endFunction


function setParameter // nb nbArgs arg variable g||p
{
	print4 function setParameter $arg1 $arg2 $arg3 $arg4 $arg5

	if $arg3 > 0-1
		if $arg2 > $arg1 - 1
					
			if $arg5 = "g"
				globalVariable $arg4 $arg3
			else
				if $arg5 = "p"
					persistentVariable $arg4 $arg3
				else
					parentVariable $arg4 $arg3
				endIf
			endIf	
			
		endIf
		
		print4 "   " $arg4 set to: $$arg4 
	else
		print4 "   " $arg4 keep default value: $$arg4 
	endIf

}
endFunction