On Sun, 26 May 2013 16:52:59 +0200
Juan Pedro Bolívar Puente <raskolnikov-mXXj517/z...@public.gmane.org> wrote:
> I disagree.

I didn't mean to start a language pissing contest... but it looks like I did :)

> First, there is nothing intrinsically "more performant" in Lua than in 
> JavaScript -- they are both dynamically typed languages with objects and 
> first-class functions.  Also, there is a wide choice of VMs in in 
> Javascript.  While QtScript uses the webkit JS engine, which is fairly 
> good and nothing prevents us to moving to V8 or something like that.

Javascript is performant in so far as it's JITed; there's LuaJIT too but I 
don't recommend any type of JIT because it allocates a gigantic block of RAM 
that's tagged as executable so it's harder to sandbox. JITs are largely 
unnecessary if you identify functionality hotspots and can easily rewrite those 
in C++, then expose those to the script layer.

> Lua does not meet the realtime constraints neither anyway.

What makes you say that? I see you have experience in just about every 
interpreter expect Lua (btw I live next door in Valencia if you want to take it 
outside:).

Lua's used by Reason and ReNoise and in game engines since the PS2 all the way 
to Crytek's Far Cry/Crysis which have similar frame-lock/tick constraints. 
There's a laundry list of realtime apps on www.lua.org/uses.html. Most 
tellingly, you'll find it in many embedded systems like Cisco, Barracuda, 
MicroTik RouterOS, OpenWrt, Wavecom GSM firmware. It hardly gets more realtime 
than that.

> In my 
> experience, the scripting language should not be the fastest, but the 
> most expressive, because its goal is to allow very fast development and 
> hackability.  If you look around, other music software are also using a 
> wide variety of similar approaches, like Ableton Live, which uses Python 
> (which is slower but more expressive than JS)

You would know so correct me if I'm wrong, aren't large parts of Live itself 
written in Python? Lua is much nimbler and doesn't offer Python's richer object 
orientation. In Mixxx's case, I understand the idea is extensibility - not to 
rewrite large parts of the application in script.

> What its missing in Mixxx is, however, an API in the scripting engine to 
> bind a MIDI signal to a control.

That's where Lua shines; writing a native Lua 'module' takes very few lines of 
code. Lua is meant to be an embeddable language, i.e. it plugs into an existing 
application vs the other way around, it's unobtrusive because it is so tiny.

> This way, the script can handle mode 
> selection, but for every mode, the important MIDI messages (cue button, 
> knobs and faders) still go through the engine directly.  Something like:
> 
>     engine.connectMidiToControl(status, midino, group, control, options);
>     engine.connectControlToMidi(group, control, status, midino, options);

I wrote something like that and used it on all but my very first gigs. I 
attached the glue code and script here; initialization is at the bottom. Please 
don't judge Lua's merits (or mine for that matter:) based on this, it was never 
meant to be used/seen by anyone but myself, is/was a work in progress and I 
frequently tweaked scripts just before a gig, knowing full well it was pretty 
risky/stupid.

> If I were to critisize JavaScript, on the other hand, it would be 
> because it is not always as concise and expressive as it could be. 

I'm fairly sure Lua is one of the most concise languages there is. Its 
reference manual is 103 pages long, including the C API. Also Lua 5.2 has a 
rich set of binary operators (for example bit range extract, ROL/ROR) that'd 
help HID/MIDI processing.

cheers,

-- p
-- MIDIbots main lua scriptey

-- require "Traktor313"

-- MIDI NOTES START AT CONTROL 9 ?! (no fucking idea why)

-- device aliases (CACA -- MOVE TO SEPARATE INI -- different on each computer!)
dm2alias = "SB Audigy MIDI Port II [A800]"
traktoralias = "MIDI Yoke NT:  1"

-- some physical dj1 buttons are defined twice; once per selected deck

gDeckLUT = {[1] = "deck_a", [2] = "deck_b"}

---- Variables ----------------------------------------------------

cueMode = "auto"
Shift = false
gSnapFlag = false
RebootCnt = 4
LastJoyLeftClock = 0
LastJoyRightClock = 0
LastJoyYTime = 0
ScanFlag = false
Wheel2for1 = false
WheelHadMod = false
LastDeckLoaded = -1
LockedDeck = -1
gDeck = {[1] = {TempoEditFlag = false, TempoPerc = 0}, [2] = {TempoEditFlag = false, TempoPerc = 0}}
gMaxTempoDelta = 40		-- not is up AND down

min, perc = mb.GetBatteryLevel()
print ("battery = "..min.." minutes, "..perc.."%%")

print("RECOMPILED NAKED SCRIPT!!!")

---- printf ---------------------------------------------------------------

function printf(fmt, ...)
	
	local	argtab = {...}
	
	-- replace %S with "%s"
	local	expanded_fmt = string.gsub(fmt, "%%S", "\"%%s\"")
	
	local	s = string.format(expanded_fmt, unpack(argtab))
	
	print(s)
end

---- Get Editd Deck ---------------------------------------------

local
function GetEditedDeck()

	local	edited_deck
	
	if (gDeck[1].TempoEditFlag) then
		return 1
	elseif (gDeck[2].TempoEditFlag) then
		return 2
	else
		return nil
	end
end

---- Set Text ---------------------------------------------------

local
function SetText(txt, timeout_secs)

	osd.Clear()
	osd.SetTextColor(32, 255, 32)
	osd.DrawText(txt, 8, 0)
	osd.SetPen(2, 0, 0, 0)
	osd.SetBrush(255, 0, 0)
	osd.Refresh()
	
	if (timeout_secs ~= nil) then
		osd.SetClearTimer(timeout_secs)
	end
end

---- Show Tempo Slider ------------------------------------------

function ShowTempoSlider(deck, timeout_secs)

	local	perc = gDeck[deck].TempoPerc
	
	osd.Clear()
	
	local client_w, client_h = osd.GetSize()
	
	local h_border, v_border
	
	h_border = client_w * 0.10
	v_border = client_h * 0.20
	
	-- slider size in pixels
	local	slider_width = client_w * 0.02
	local	slider_height = client_h - (2 * v_border)
	
	local	x_pos
	
	if (deck == 1) then
		x_pos = h_border
	elseif (deck == 2) then
		x_pos = client_w - h_border
	else
		return			-- should be error
	end

-- draw slider
	
	osd.SetPen(2, 0, 0, 0)
	osd.SetBrush(128, 128, 128)
	
	osd.DrawRect(x_pos - slider_width/2, v_border, slider_width, slider_height, 5)

-- draw zero tempo % ref notch
	local	zero_notch_h = slider_width / 2
	local	zero_notch_w = slider_width * 5
	
	osd.DrawRect(x_pos - zero_notch_w/2 , v_border + slider_height/2 - zero_notch_h/2, zero_notch_w, zero_notch_h)
	
-- draw notch
	
	-- notch size in pixels
	local	notch_width = slider_width * 4
	local	notch_height = slider_width
	
	osd.SetPen(2, 0, 0, 0)
	osd.SetBrush(200, 200, 200)
	
	local	notch_y = v_border + (slider_height / 2) + ((slider_height / 2) * perc / (gMaxTempoDelta / 2))
	
	osd.DrawRect(x_pos - notch_width/2, notch_y - notch_height/2, notch_width, notch_height, 2)
	
-- draw slder value
	local	s = string.format("Tempo %+.1f", perc)
	
	osd.DrawText(s, 8, 0)
	
	osd.Refresh()
	osd.SetClearTimer(timeout_secs)
end

---- Stop Pitch Bend --------------------------------------------

function StopPitchBend()

	mb.SendMIDI(traktor.deck_a.pitch_bend, 64)
	mb.SendMIDI(traktor.deck_b.pitch_bend, 64)
end

---- Stop Deck Grid Adjust ----------------------------------------

function StopDeckGridAdjust(DeckPort)
	
	-- printf("StopDeckGridAdjust()")
	
	-- send note offs to all
	mb.SendMIDI(DeckPort.grid_bpm_coarse.inc, 0)
	mb.SendMIDI(DeckPort.grid_bpm_coarse.dec, 0)
	mb.SendMIDI(DeckPort.grid_bpm_fine.inc, 0)
	mb.SendMIDI(DeckPort.grid_bpm_fine.dec, 0)
end

---- Stop Grid Adjust ------------------------------------------

function StopGridAdjust()

	StopDeckGridAdjust(traktor[gDeckLUT[1]])
	StopDeckGridAdjust(traktor[gDeckLUT[2]])
end

---- Stop Wheels -----------------------------------------------

function StopWheels()

	StopPitchBend()
	StopGridAdjust()
end
	
---- Only Note Down -----------------------------------------------

function OnlyDown(Val)

	if (Val ~= 127) then
		abort()
	end

end

---- Only Note Up -----------------------------------------------

function OnlyUp(Val)

	if (Val ~= 0) then
		abort()
	end

end

---- Check Deck Lock ----------------------------------------------

function CheckDeckLock(deck)

	-- printf("locked deck = "..LockedDeck.." this deck = "..deck)
	
	if (deck == LockedDeck) or (LockedDeck == "both") then
		SetText("Deck "..deck.." is LOCKED!", 1)
		abort("deck "..deck.." was locked")
	end
end

---- Reset Loop Button States --------------------------------------

function ResetLoopButtons()

	gLoopButtonEndFlag = false
end

---- Unlock Decks --------------------------------------------------

function UnlockDecks(Val)

	OnlyDown(Val)
	
	-- reset pitch bend, grid adjust & loop
	StopPitchBend()
	StopGridAdjust()
	ResetLoopButtons()
	
	if (ScanFlag) then
		-- lock BOTH decks
		LockedDeck = "both"
		SetText("locked BOTH decks", 3)
		return
	elseif (Shift) then
		ResetTempoEdit()
		SetText(" RESET TEMPO EDIT FOR BOTH DECKS", 1)
	else
		-- unlock decks		
		SetText("unlocked decks", 2)
		LockedDeck = -1
	end
end

---- Set Deck Tempo -----------------------------------------------

function SetDeckTempo(deck, tempo)

	-- clamp tempo
	local	half_tempo_delta = gMaxTempoDelta / 2
	
	if (tempo < -half_tempo_delta) then
		tempo = -half_tempo_delta
	elseif (tempo > half_tempo_delta) then
		tempo = half_tempo_delta
	end
	
	gDeck[deck].TempoPerc = tempo
	
	-- send 14-bit pitch bend msg
	local	deck_name = gDeckLUT[deck]
		
	-- sends 14-bit msg as 0-normalized float
	mb.SendMIDI(traktor[deck_name].tempo_wide, tempo / gMaxTempoDelta)
	
	-- printf("%s tempo set to %f", deck_name, tempo)
end

---- Move Back -1/+1 ----------------------------------------------

function MoveBack1(Val)

	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	if not (Shift) then
		-- back 1
		mb.SendMIDI(traktor[deck_name].beat_jump_back1, 127, 0)
		SetText(" << 1", 1)
	else
		-- forward 1
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd1, 127, 0)
		SetText(" >> 1", 1)
	end
end

---- Move Back -4/-8 ----------------------------------------------

function MoveBack4(Val)

	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	if not (Shift) then
		mb.SendMIDI(traktor[deck_name].beat_jump_back2, 127, 0)
		SetText(" << 4", 1)
	else
		-- when shift move back 8
		mb.SendMIDI(traktor[deck_name].beat_jump_back2, 127, 0)
		mb.SendMIDI(traktor[deck_name].beat_jump_back2, 127, 0)
		SetText(" << 8", 1)
	end
end

---- Move Fwd 4 ----------------------------------------------

function MoveFwd4(Val)

	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	if not (Shift) then
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd2, 127, 0)
		SetText(" >> 4", 1)
	else
		-- when shift move back 8
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd2, 127, 0)
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd2, 127, 0)
		SetText(" >> 8", 1)
	end
end

---- Move Back 16 ---------------------------------------------

function MoveBack16(Val)

	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	if not (Shift) then
		mb.SendMIDI(traktor[deck_name].beat_jump_back3, 127, 0)
		SetText(" << 16", 1)
	else
		-- when shift move back 32
		mb.SendMIDI(traktor[deck_name].beat_jump_back3, 127, 0)
		mb.SendMIDI(traktor[deck_name].beat_jump_back3, 127, 0)
		SetText(" >> 32", 1)
	end
end

---- Move Fwd 16 ---------------------------------------------

function MoveFwd16(Val)

	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	printf("MoveFwd16(%d) deck(%d)", Val, deck)
	
	if not (Shift) then
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd3, 127, 0)
		SetText(" >> 16", 1)
	else
		-- when shift move forward 32
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd3, 127, 0)
		mb.SendMIDI(traktor[deck_name].beat_jump_fwd3, 127, 0)
		SetText(" >> 32", 1)
	end
end

---- Move to Previous Cue -----------------------------------------------

function MovePrevCue(Val)
	
	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	mb.SendMIDI(traktor[deck_name].cue_prev, 127, 0)
	
end

---- Move to Next Cue  --------------------------------------------------

function MoveNextCue(Val)
	
	OnlyDown(Val)
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	mb.SendMIDI(traktor[deck_name].cue_next, 127, 0)
	
end

---- Cue Button ---------------------------------------------------

function CueButton(Val)

	OnlyDown(Val)
	-- no deck lock check here!
	
	local	deck_name = gDeckLUT[deck]
	
	if not (Shift) then
		-- set cue (on the fly)
		mb.SendMIDI(traktor[deck_name].cue_set, 127, 0)
		
		-- CAN'T LOCK HERE CAUSE MAY UNLOCK PREVIOUS CUE!!!
		-- mb.SendMIDI(traktor[deck_name].lock_cue_point, 127, 0)
		-- need to lock MANUALLY a bit later (better like that anyway)
		if (gSnapFlag) then
			SetText("Set (snapped) Cue Marker", 2)
		else
			SetText("Set Cue Marker", 2)
		end
	else
		-- lock/unlock cue marker
		mb.SendMIDI(traktor[deck_name].lock_cue_point, 127, 0)
		
		SetText("Cue Marker lock toggle", 2)		
	end
end

---- Reset Snap State -----------------------------------------------

function ResetSnapState()
	
	mb.SendMIDI(traktor.deck_a.snap_to_grid, gSnapFlag)
	mb.SendMIDI(traktor.deck_b.snap_to_grid, gSnapFlag)
end

---- ALT on/off -----------------------------------------------------

function SnapOnOff(Val)
	
	if (Val == 127) then
		gSnapFlag = true
		SetText(" SNAP", 2)
	else
		gSnapFlag = false
		SetText("", 2)
	end
	
	ResetSnapState()
end

---- Play/Pause --------------------------------------------------------

function PlayPause(Val)
	
	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	if (Shift) then
		-- cue/pause
		mb.SendMIDI(traktor[deck_name].cue_pause, Val)
		
		-- what happens if Shift state changes and then press up?
		-- ... will just continue playing
	else
		-- play/pause
		OnlyDown(Val)
		
		-- toggle
		mb.SendMIDI(traktor[deck_name].play_pause, 127, 0)
	end
end

---- Sync Deck -------------------------------------------------------

function Sync(Val)

	CheckDeckLock(deck)
	
	local	deck_name = gDeckLUT[deck]
	
	local	edited_deck = GetEditedDeck()
	
	if (edited_deck ~= nil) then
		
		-- editing tempo
		printf("RESETTING DECK %d TEMPO", edited_deck)
		
		SetDeckTempo(edited_deck, 0)
		
		-- show slider
		ShowTempoSlider(deck, 3)
	
		-- stop any repeat
		LastJoyYTime = 0
		
	elseif (gSnapFlag) then
		-- snap floating cuepoint to grid
		
		-- WARNING: if the time between the cue/pause down/up is too short
		--	    then Traktor may not register it
		
		-- send cue/pause to snap
		mb.SendMIDI(traktor[deck_name].cue_pause, Val)
			
		if (Val == 0) then
			printf("snapped deck %S", deck_name)
		end

	else
		-- sync
		mb.SendMIDI(traktor[deck_name].sync, Val)
	end
end

---- BeatGrid Adjust -----------------------------------------------

function BeatGridAdjust(DeckPort, dwheel)

	if (dwheel == 0) then
		StopDeckGridAdjust(DeckPort)
		return
	end
	
	-- amplify?
	dwheel = dwheel * 3
	
	local	dir = nil
	
	if (dwheel < 0) then
		dir = "inc"			-- INCREASE bpm
	else
		dir = "dec"			-- DECREASE bpm
	end
	
	local	absd = math.abs(dwheel)
	if (absd > 15) then
		-- coarse wheel
		mb.SendMIDI(DeckPort.grid_bpm_coarse[dir], 127, 0)
		printf("Adjust BPM coarse %d", absd)
	elseif (absd > 5) then
		-- fine wheel ?
		mb.SendMIDI(DeckPort.grid_bpm_fine[dir], 127, 0)
		printf("Adjust BPM fine %d", 0)
	else
		-- none, reset
		StopDeckGridAdjust(DeckPort)
		printf("Adjust BPM fine %d", 127)
	end

end

---- Track Scan -----------------------------------------------

function TrackScan(dwheel, deck)

	if (dwheel == 0) then
		return
	end
	
	printf("TrackScan(%d)", dwheel)
	
	CheckDeckLock(deck)
	
	WheelHadMod = true
	
	local	absd = math.abs(dwheel)
	
	if (absd > 15) then
		local	deck_name = gDeckLUT[deck]
		
		if (dwheel > 0) then
			mb.SendMIDI(traktor[deck_name].beat_jump_fwd3, 127, 0)
		else
			mb.SendMIDI(traktor[deck_name].beat_jump_back3, 127, 0)
		end
	end

end

---- DM2 Deck Wheel ----------------------------------------------

function DM2DeckWheel(Val)

	if (Val == 64) then
		WheelHadMod = false
	end
	
	if (Wheel2for1 and (deck == 2)) then
		-- force wheel2 to be for deck 1
		deck = 1
	end
	
	if (ScanFlag) then
		TrackScan(Val - 64, deck)
		return
	end
	
	local	deck_name = gDeckLUT[deck]
	
	if (gSnapFlag) then
		-- adjust beat grid
		BeatGridAdjust(traktor[deck_name], Val - 64)
		
		WheelHadMod = true
	else
		-- send pitch bend
		if (Val ~= 64) then
			CheckDeckLock(deck)
		end
		
		if (WheelHadMod) then
			SetText("(wheel skidding ignored)", 1)
			return
		end
		
		mb.SendMIDI(traktor[deck_name].pitch_bend, Val)
	end
end

---- DM2 Joystick X axis -------------------------------------------

function DM2JoyX(Val)
	
	-- NOP!
	
	--[[
	local	edited_deck = GetEditedDeck()
	if (edit_deck ~= nil) then
		-- BLOCK deck loads while joystick is used for TEMPO
		return
	end
	
	local	dt, port
	
	if (Val == 0) then
	
		dt = os.clock() - LastJoyLeftClock
		LastJoyLeftClock = os.clock()
		deck = 1		
	elseif (Val == 127) then
	
		dt = os.clock() - LastJoyRightClock
		LastJoyRightClock = os.clock()
		deck = 2
	else
		-- nop
		return
	end
	
	local	deck_name = gDeckLUT[deck]
	
	port = traktor[deck_name].list_load_track		-- load into deck A or B
	
	-- if double-selected fast enough will send deck load
	if (dt < 0.4) then
		-- CheckDeckLock(deck)
		
		print("LastDeckLoaded = "..LastDeckLoaded.." deck = "..deck)
		
		-- check not doing double deck load
		if (ScanFlag or (LastDeckLoaded ~= deck)) then
			
			-- load into deck
			LastDeckLoaded = deck
			mb.SendMIDI(port, 127, 0)
			ResetDeckTempo(deck)
			
			-- lock other deck
			LockedDeck = (2 - deck) + 1
		else
			SetText("Double Load ! (use scan-mod)", 2)
		end
	end

	collectgarbage()
]]
end

---- Load Deck -----------------------------------------------------

function LoadDeck(Val)

	printf("hslider %d", Val)
	
	local	dt		-- unused
	
	if (Val == 0) then
	
		dt = os.clock() - LastJoyLeftClock
		LastJoyLeftClock = os.clock()
		deck = 1
			
	elseif (Val == 127) then
	
		dt = os.clock() - LastJoyRightClock
		LastJoyRightClock = os.clock()
		deck = 2
	else
		-- nop
		return
	end
	
	if (ScanFlag or (LastDeckLoaded ~= deck)) then
		
		-- load into deck
		local	deck_name = gDeckLUT[deck]
	
		LastDeckLoaded = deck
		mb.SendMIDI(traktor[deck_name].list_load_track, 127, 0)
		ResetDeckTempo(deck)
		
		-- lock other deck
		LockedDeck = (2 - deck) + 1
	else
		SetText("Double Load ! (use scan-mod)", 1)
	end
	
	collectgarbage()
end

---- DM2 Joystick Y axis -------------------------------------------

function DM2JoyY(Val)

	local	edited_deck = GetEditedDeck()
	
	-- printf("DM2JoyY(%d)", Val)
	
	local	dir
	
	if (Val == 127) then
		dir = "up"
	elseif (Val == 0) then
		dir = "down"
	else
		LastJoyYTime = 0
		return				-- nop in deadzone
	end
	
	if (edited_deck == nil) then
		-- scroll up/down playlist
		local	port_lut = {up = traktor.list_select_up, down = traktor.list_select_down}
		
		port = port_lut[dir]
		assert(port, "illegal port look-up")	
		
		mb.SendMIDI(port, 127, 0)
	else
		-- adjust tempo
		assert(edited_deck, "DM2JoyY() - missing deck!!!")
		
		-- dermine if is in repeat or step mode
		local	perc_incr
		
		dt = os.clock() - LastJoyYTime
		
		-- printf("y dt = %f", dt)
		
		if (dt < 0.4) then
			-- in repeat mode
			perc_incr = 0.5
		else
			-- in single-step mode
			perc_incr = 0.1
		end
		
		LastJoyYTime = os.clock()
		
		incr_lut = {up = -perc_incr, down = perc_incr}		-- (tempo slider is inverted, duh)
		
		tempo_perc = gDeck[edited_deck].TempoPerc + incr_lut[dir]
		
		-- printf("deck %d: setting tempo %f", edited_deck, tempo_perc)
		
		SetDeckTempo(edited_deck, tempo_perc)
		
		ShowTempoSlider(edited_deck, 3)
	end	
end

---- Reboot Step ----------------------------------------------------

function RebootStep()
	
	RebootCnt = RebootCnt - 1
	if (RebootCnt <= 0) then
		reboot()
	else
		SetText("REBOOT COUNTDOWN: "..RebootCnt, 2)
	end
end

---- Scan On --------------------------------------------------------

function ScanOn(Val)

	if (Val == 127) then
		ScanFlag = true
		SetText("Scan ON", 20)
	else
		ScanFlag = false
		SetText("", 0.1)
	end
	
	StopWheels()		-- stop any remanent wheel functions
end

---- Replace Wheel 2 for Wheel 1 -------------------------------------

function Wheel2For1(Val)

	if (Val == 127) then
		Wheel2for1 = true
		SetText("Wheel2for1 ON", 20)
	else
		Wheel2for1 = false
		SetText("", 0.1)
	end
end

---- Stop Tempo Edit ------------------------------------------------

function StopTempoEdit()

	gDeck[1].TempoEditFlag = false
	gDeck[2].TempoEditFlag = false
	LastJoyYTime = 0
	gSnapFlag = false
	ResetSnapState()
end

---- Reset Deck Tempo -------------------------------------------------

function ResetDeckTempo(deck)

	SetDeckTempo(deck, 0)
	LastJoyYTime = 0
	gDeck[deck].TempoEditFlag = false
end

---- Reset Tempoes ----------------------------------------------------

function ResetTempoEdit()

	ResetDeckTempo(1)
	ResetDeckTempo(2)
end

---- Tempo Edit Switch ----------------------------------------------

function TempoEditSwitch(Val)
	
	local	lut = {[0] = false, [127] = true}
	
	local	f = lut[Val]
	if (f == nil) then
		f = false			-- should be warning!!
	end
	
	gDeck[deck].TempoEditFlag = f
	
	if (Shift) then
	
		local	deck_name = gDeckLUT[deck]
		
		-- set beat marker
		mb.SendMIDI(traktor[deck_name].set_beat_marker, 127, 0)
		return
	end
	
	if (f) then
		ShowTempoSlider(deck, 2)
	else
		osd.Clear()
	end
end

---- Traktor Teach --------------------------------------------------

function TEACH(Val)

	SetText(" TEACH", 2)
	mb.SendMIDI(traktor.deck_b.seek_start, 127)
	mb.SendMIDI(traktor.deck_b.seek_start, 0)
	
end

---- Shift On/Off ---------------------------------------------------

function ShiftOnOff(Val)

	if (Val == 127) then
		Shift = true
		
		if ((deck ~= nil) and gDeck[deck].TempoEditFlag) then
			local	deck_name = gDeckLUT[deck]
			
			-- set beat marker
			mb.SendMIDI(traktor[deck_name].set_beat_marker, 127, 0)
			
			SetText(" BEAT MARKER SET")
		else
			SetText(" SHIFT")
		end
	else
		Shift = false
		SetText("")
	end
	
	StopWheels()		-- stop any remanent wheel functions
end

---- Create Var Sub-Tables ------------------------------------------

function CreateVarSubTables(var)

	local part
	
	for w in string.gfind(var, "([%w_]+)%.") do
		if part then
			part = part.."."..w
		else
			part = w		-- first time
		end
		
		-- print("concat "..part)
		
		-- if table is empty
		local	cmd = string.format("return (%s == nil)", part)
		
		if (loadstring(cmd)()) then
			-- init table
			ok = loadstring(string.format("%s = {}", part))()
		end
	end

end

---- Define Port ----------------------------------------------------

function DefinePort(port, device, type, note, channel)

	CreateVarSubTables(port)
	
	local	cmd = string.format("%s = Port(\"%s\", %s, \"%s\", %d, %d)", port, port, device, type, note, channel)
	
	-- print("command: "..cmd)
	
	loadstring(cmd)()
end

---- Load INI -------------------------------------------------------

function LoadINI(filename)

	fn = assert(io.open(filename))
	
	local	port, device, type, note, channel
	
	local	i = 0
	
	while 1 do
		local ln = fn:read("*l")
		if ln then
			local _, _, port, dev, type, note, chan = string.find(ln, "([%w%._]+)%s+([%w%._]+)%s+(%u+)%s+(%d+)%s+(%d+)")
			
			if port then
				DefinePort(port, dev, type, note, chan)
			end
			i = i + 1
		else
			break		-- last line reached
		end
	end
	
	fn:close()
end

---- Loop On/Off -------------------------------------------------------

function LoopOnOff(Val)

	OnlyDown(Val)
	
	-- if CTRL-ALT-DEL start reboot countdown
	if (Shift and ScanFlag and (deck == 2)) then
		RebootStep()
		return
	end
	
	local	deck_name = gDeckLUT[deck]
	
	if (gLoopButtonEndFlag) then
		mb.SendMIDI(traktor[deck_name].loop_end, 127, 0)
		SetText("Loop OUT", 2)
	else
		mb.SendMIDI(traktor[deck_name].loop_start, 127, 0)
		SetText("Loop SET/IN", 2)
	end
end

---- Loop Kill ------------------------------------------------------

function LoopKill(val)

	if (val == 127) then
		gLoopButtonEndFlag = true
		SetText("Loop CANCEL shift", 2)
	else
		gLoopButtonEndFlag = false
		SetText("", 2)
	end
end

---- START ----------------------------------------------------------

function Start()

	LoadINI("ports.INI")

-- dm2
	mb.SetMIDIHandler(dm2.xfader, "LoadDeck")
	mb.SetMIDIHandler(dm2.deck_a.wheel, "DM2DeckWheel", "deck=1")
	mb.SetMIDIHandler(dm2.deck_b.wheel, "DM2DeckWheel", "deck=2")
	
	-- play/stop/rec buttons
	mb.SetMIDIHandler(dm2.solo, "UnlockDecks")

	-- macro buttons
	mb.SetMIDIHandler(dm2.macro1, "ScanOn")
	mb.SetMIDIHandler(dm2.macro2, "Wheel2For1")
	--mb.SetMIDIHandler(dm2.macro3, "TEACH")
	mb.SetMIDIHandler(dm2.macro4, "ShiftOnOff")
	
	-- 1/2/3 buttons
	mb.SetMIDIHandler(dm2.button1, "CueButton", "deck=1")
	mb.SetMIDIHandler(dm2.button2, "SnapOnOff")
	mb.SetMIDIHandler(dm2.button3, "CueButton", "deck=2")
	
	-- a/b buttons
	mb.SetMIDIHandler(dm2.a, "PlayPause", "deck=1")
	mb.SetMIDIHandler(dm2.b, "PlayPause", "deck=2")
	
	-- stop/play/rec buttons
	mb.SetMIDIHandler(dm2.stop, "LoopOnOff", "deck=1")
	mb.SetMIDIHandler(dm2.play, "LoopKill")
	mb.SetMIDIHandler(dm2.rec, "LoopOnOff", "deck=2")
	ResetLoopButtons()
	
	-- Deck1 ring buttons
	pf = "deck=1"
	mb.SetMIDIHandler(dm2.deck_a.n, "MoveBack1", pf)
	mb.SetMIDIHandler(dm2.deck_a.nw, "MoveBack4", pf)
	mb.SetMIDIHandler(dm2.deck_a.ne, "MoveFwd4", pf)
	mb.SetMIDIHandler(dm2.deck_a.w, "MoveBack16", pf)
	mb.SetMIDIHandler(dm2.deck_a.e, "MoveFwd16", pf)
	mb.SetMIDIHandler(dm2.deck_a.sw, "MovePrevCue", pf)
	mb.SetMIDIHandler(dm2.deck_a.se, "MoveNextCue", pf)
	mb.SetMIDIHandler(dm2.deck_a.s, "TempoEditSwitch", pf)
	mb.SetMIDIHandler(dm2.deck_a.lock, "Sync", pf)

	-- Deck2 ring buttons
	pf = "deck=2"
	mb.SetMIDIHandler(dm2.deck_b.n, "MoveBack1", pf)
	mb.SetMIDIHandler(dm2.deck_b.nw, "MoveBack4", pf)
	mb.SetMIDIHandler(dm2.deck_b.ne, "MoveFwd4", pf)
	mb.SetMIDIHandler(dm2.deck_b.w, "MoveBack16", pf)
	mb.SetMIDIHandler(dm2.deck_b.e, "MoveFwd16", pf)
	mb.SetMIDIHandler(dm2.deck_b.sw, "MovePrevCue", pf)
	mb.SetMIDIHandler(dm2.deck_b.se, "MoveNextCue", pf)
	mb.SetMIDIHandler(dm2.deck_b.s, "TempoEditSwitch", pf)
	mb.SetMIDIHandler(dm2.deck_b.lock, "Sync", pf)
	
	-- joy
	mb.SetMIDIHandler(dm2.joy_x, "DM2JoyX")
	mb.SetMIDIHandler(dm2.joy_y, "DM2JoyY")
	mb.SetMidiHandlerRepeat(dm2.joy_y, 0.5, 0.05, 64-10, 64+10)
	mb.SetMIDIHandlerDuplicates(dm2.joy_y, false)

	ResetTempoEdit()
	
	osd.Show()
	
	SetText("Lua started", 4)
	
	printf("PRINTING FROM SCRIPT!")
	
end

---- STOP -------------------------------------------------------------

function Stop()

	print("Stop() called")
	
	SetText("Lua stopped", 2)

end
#include "mb/LuaGlue.h"

#include "mb/midibots.h"
#include "mb/EventEngine.h"
#include "mb/LuaGlue.h"
#include "mb/Ports.h"

using namespace MIDIbots;

Frame		*SFrame = nil;
ScriptInterface	*SInterface = nil;

static
lua_State	*L = nil;
static
wxString	gLUAScriptFileName = "";
static
int		ExceptionCnt = 0;

#define	LG_CUSTOM_ABORT_CODE				0x1234
#define	LG_MAX_EXCEPTION_CNT_BEFORE_RESTART		3

//---- Exception Filter ----------------------------------

static
int	ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
	wxLogMessage("ERROR - ExceptionFilter(), code 0x%08X", code);
	
	switch (code)
	{	case EXCEPTION_ACCESS_VIOLATION:
		case EXCEPTION_INT_DIVIDE_BY_ZERO:		// CAN'T CONTINUE CAUSE WILL RE-EXECUTE / 0
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		case EXCEPTION_BREAKPOINT:
		
			return EXCEPTION_EXECUTE_HANDLER;
			break;
		
		case LG_CUSTOM_EXCEPT_CODE:
			
			wxLogMessage("caught custom exception");
			return EXCEPTION_EXECUTE_HANDLER;
			break;
		
		default:

			return EXCEPTION_EXECUTE_HANDLER;
			break;
	}
}

//---- At Panic function (called by Lua VM) -------------------------------

static
int	lg_atpanic(lua_State *L)
{
	wxLogMessage("ERROR - lg_atpanic() called, raising custom exception");
	
	// request to restart Lua when possible (posts event)
	SInterface->RestartLuaVM();
	
	SInterface->SetOSDText("EXCEPTION RAISED", 1);
	
	// raise an exception to get out of VM
	SInterface->RaiseException();
	
	return 0;	// (will never be called)
}

//---- print OVERRIDE ----------------------------------------------------

static
int	lg_print(lua_State *L)
{
	const int	nargs = lua_gettop(L);
	
	for (int i = 0; i < nargs; i++)
	{	
		const char	*s = lua_tostring(L, i + 1);
		wxASSERT(s);
		
		wxLogMessage(">%s", s);
	}
	
	return 0;
}

//---- Abort Lua Handler function -----------------------------------------

static
int	lg_Abort(lua_State *L)
{
	if (lua_isstring(L, -1))
	{
		// dump any info message to log
		const char	*abortStr = luaL_checkstring(L, -1);
		
		wxLogMessage("Abort: \"%s\" ", abortStr);
	}
	
	lua_pushnumber(L, LG_CUSTOM_ABORT_CODE);
	
	// push error message on the stack
	lua_error(L);
	
	return 0;	// (will never be called)
}

//---- Reboot Lua Handler function ---------------------------------------

static
int	lg_Reboot(lua_State *L)
{
	SInterface->SetOSDText("REBOOTING LUA", 2);
	
	// request to restart Lua when possible (posts event)
	SInterface->RestartLuaVM();
	
	return 0;
}

//---- Set MIDI Handler --------------------------------------------------------

static
int	lg_SetMIDIHandler(lua_State *L)
{
	int	nargs = lua_gettop(L);
	
	if ((nargs < 2) || (nargs > 3))
	{
		wxLogMessage("ERROR - lg_SetMIDIHandler() bad # of arguments");
		return 0;
	}
	
	LuaPort	*luaport = LuaPort::get(L, 1);
	if (luaport == nil)
	{	luaL_error(L, "error: NIL lua port");
		
		return 0;
	}
	
	Port	*port = luaport->m_Port;
	if (port == nil)
	{
		luaL_error(L, "error: NIL port->m_Port");
		return 0;
	}
		
	MSG_TYPE	EventType;
	int		Note, Channel;
	
	EventType = port->m_MIDIMsgType;
	Note = port->m_Note;
	Channel = port->m_Channel;
	
	// get function name	
	const char	*fnname = luaL_checkstring(L, 2);
	
	// push Lua function name
	lua_pushstring(L, fnname);
	// get actual function on stack
	lua_gettable(L, LUA_GLOBALSINDEX);
	// check is function on the stack
	bool	IsLuaFunction = lua_isfunction(L, -1);
	
	// pop pushed function
	lua_pop(L, 1);
	
	if (!IsLuaFunction)
	{
		luaL_error(L, "error: Lua function \"%s\" doesn't exist", fnname);
		
		// don't register this handler
		return 0 ;
	}
	
	// instantiate handler
	LuaEventHandler	*eh = new LuaEventHandler(L, fnname);
	
	// set handler
	SInterface->SetHandler(EventType, Note, Channel, eh);
	
	wxLogMessage(" Registered ('%s', %3d, %2d)   ->   %s", EventTypeToStr(EventType), Note, Channel, fnname);
	
	// set any function prefix
	if (nargs == 3)
	{	const char	*FnPrefix = luaL_checkstring(L, 3);
	
		eh->SetFunctionPrefix(FnPrefix);
		
		wxLogMessage("   Registered function prefix \"%s\" ", FnPrefix);
	}
	
	return 0;		// no result
}

//---- Set MIDI Handler Repeat -------------------------------------------------

static
int	lg_SetMIDIHandlerRepeat(lua_State *L)
{
	int	nargs = lua_gettop(L);
	
	if ((nargs != 3) && (nargs != 5))
	{
		luaL_error(L, "error: bad # of args");
		
		return 0;
	}
	
	LuaPort	*luaport = LuaPort::get(L, 1);
	if (luaport == nil)
	{	luaL_error(L, "error: NIL lua port");
		
		return 0;
	}
	
	Port	*port = luaport->m_Port;
	if (port == nil)
	{
		luaL_error(L, "error: NIL port->m_Port");
		return 0;
	}
		
	MSG_TYPE	EventType;
	int		Note, Channel;
	
	EventType = port->m_MIDIMsgType;
	Note = port->m_Note;
	Channel = port->m_Channel;
	
	double	delay, frequ;
	
	delay = luaL_checknumber(L, 2);
	frequ = luaL_checknumber(L, 3);
	
	int	dzStart, dzEnd;
	
	// get any deadzone
	if (nargs == 5)
	{	dzStart = luaL_checkint(L, 4);
		dzEnd = luaL_checkint(L, 5);
	}
	else
	{	dzStart = -1;
		dzEnd = -1;
	}

	// force deadzones on note on/off
	switch (EventType)
	{
		case MSG_NOTE_ON:
			
			dzStart = 0;
			dzEnd = 126;
			break;
		
		case MSG_NOTE_OFF:
			
			dzStart = 0;
			dzEnd = 126;
			break;
	}
	
	// get handler
	LuaEventHandler	*eh = static_cast<LuaEventHandler*> (SInterface->GetHandler(EventType, Note, Channel));
	if (eh == nil)
	{
		luaL_error(L, "error: handler missing");
		return 0;
	}
	
	// convert to miliseconds
	int	DelayMS, FrequMS;
	
	DelayMS = delay * 1000.0;
	FrequMS = frequ * 1000.0;
	
	// set repeat params
	eh->SetRepeat(DelayMS, FrequMS, dzStart, dzEnd);
	
	return 0;		// no result
}

//---- Set MIDI Handler Duplicates ----------------------------------------------

static
int	lg_SetMIDIHandlerDuplicates(lua_State *L)
{
	int	nargs = lua_gettop(L);
	
	if (nargs != 2)
	{
		luaL_error(L, "error: bad # of args");
		
		return 0;
	}
	
	LuaPort	*luaport = LuaPort::get(L, 1);
	if (luaport == nil)
	{	luaL_error(L, "error: NIL lua port");
		
		return 0;
	}
	
	Port	*port = luaport->m_Port;
	if (port == nil)
	{
		luaL_error(L, "error: NIL port->m_Port");
		return 0;
	}
	
	MSG_TYPE	EventType;
	int		Note, Channel;
	
	EventType = port->m_MIDIMsgType;
	Note = port->m_Note;
	Channel = port->m_Channel;
		
	// get handler
	LuaEventHandler	*eh = static_cast<LuaEventHandler*> (SInterface->GetHandler(EventType, Note, Channel));
	if (eh == nil)
	{
		luaL_error(L, "error: handler missing");
		return 0;
	}
	
	if (!lua_isboolean(L, 2))
	{
		luaL_error(L, "error: boolean expected");
		return 0;
	}
	
	bool	Flag = (lua_toboolean(L, 2) == 1);
	
	eh->SetDuplicates(Flag);
	
	return 0;		// no result
}



//---- Receive MIDI msg implementation ------------------------------

void	LuaEventHandler::Process(MidiMsg *msg)
{
	__try
	{	
		// if is NOTE ON with value of zero, turn into NOTE OFF ? caca
		if ((msg->GetType() == MSG_NOTE_ON) && (msg->GetVal() == 0))
		{
			msg->SetType(MSG_NOTE_OFF);
		}
	
		lua_settop(L, 0);
		
		// push Lua function name
		lua_pushstring(L, m_FunctionName);
		// get actual function on stack
		lua_gettable(L, LUA_GLOBALSINDEX);
		// check is function on the stack
		if (!lua_isfunction(L, -1))
		{	// Lua function not found
			wxLogMessage("ERROR: function \"%s\" missing ", m_FunctionName);
			
			lua_settop(L, 0);
			return;
		}
		
		int	err;
		
		// call any prefix
		if (!m_FunctionPrefix.empty())
		{	err = luaL_dostring(L, m_FunctionPrefix);
			if (err)
			{
				wxLogMessage("Error while calling function prefix: \"%s\", err %s", m_FunctionPrefix, lua_tostring(L, -1));
				
				// remove error from stack
				lua_remove(L, -1);
				
				return;
			}
		}
		
		// push argument(s)
		lua_pushnumber(L, msg->GetHighLevelVal());
		
		// call function (protected)
		err = lua_pcall(L, 1/*args*/, 0/*res*/, 0/*err function*/);
		if (err)
		{
			// check if is abort code or actual error
			if (lua_isnumber(L, -1))
			{
				// check if is custom abort code (should be!)
				int	code = luaL_checkint(L, -1);
				if (code == LG_CUSTOM_ABORT_CODE)
				{
					// wxLogMessage("Abort: custom code caught");
					
					// reset stack to zero
					lua_settop(L, 0);
					
					return;
				}
				else
				{
					wxLogMessage("Error, unrecognized error code %s", lua_tostring(L, -1));
				}
			}
			
			wxLogMessage("Error %s", lua_tostring(L, -1));
			
			// remove error from stack
			lua_remove(L, -1);
			
			wxLogMessage("Lua Stack top %d", lua_gettop(L));
		}
	}
	__except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
	{
		ExceptionCnt++;
		
		// error beep
		::MessageBeep(MB_ICONEXCLAMATION);
		
		wxLogMessage("ERROR - caught exception! (cnt %d)", ExceptionCnt);
		
		if (ExceptionCnt > LG_MAX_EXCEPTION_CNT_BEFORE_RESTART)
		{	
			wxLogMessage("EXCEEDED MAX # EXCEPTIONS, REQUESTING RESTART");
			
			// request to restart Lua when possible (posts event)
			SInterface->RestartLuaVM();
		}
		
		// reset stack to zero
		lua_settop(L, 0);
	}
}

//---- Send Midi --------------------------------------------------

static
int	lg_SendMidi(lua_State *L)
{
	int	numargs = lua_gettop(L);
	
	if (numargs < 2)
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	LuaPort	*luaport = LuaPort::get(L, 1);
	if (luaport == nil)	luaL_error(L, "error: NIL lua port");
	
	Port	*port = luaport->m_Port;
	if (port == nil)	luaL_error(L, "error: NIL port->m_Port");
	
	MSG_TYPE	EventType;
	int		Note, Channel;
	
	EventType = port->m_MIDIMsgType;
	Note = port->m_Note;
	Channel = port->m_Channel;
	
	for (int i = 2; i <= numargs; i++)
	{
		double	v;
		
		if (lua_isboolean(L, i))
		{	// automatically convert bool to 0 or 127
			bool	f = (lua_toboolean(L, i) != 0);
			
			v = f ? 127 : 0;
		}
		else	v = luaL_checknumber(L, i);
		
		SInterface->SendMidi(EventType, Note, Channel, v);
	}
	
	return 0;
}

//---- Send Mouse Click ------------------------------------------------

static
int	lg_SendMouseClick(lua_State *L)
{
	wxLogMessage("lg_SendMouseClick");
	
	int	numargs = lua_gettop(L);
	
	if ((numargs < 2) || (numargs > 4))
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	const char	*windowname;
	int		argind = 1;
	
	if (lua_type(L, 1) == LUA_TSTRING)
	{
		windowname = luaL_checkstring(L, 1);
		argind++;
	}
	else
	{
		windowname = "";
	}
	
	int	x, y;
	
	x = luaL_checkint(L, argind++);
	y = luaL_checkint(L, argind++);
	
	const char	*FlagStr;
	
	if (argind <= numargs)
		FlagStr = luaL_checkstring(L, argind++);
	else	FlagStr = "du";				// default is down, up
	
	SInterface->SendMouseClick(windowname, x, y, FlagStr);
	
	return 0;
}

//---- Get Pixel ------------------------------------------------------

static
int	lg_GetPixel(lua_State *L)
{
	int	numargs = lua_gettop(L);
	int	argind = 1;
	
	if ((numargs != 2) && (numargs != 3))
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	const char	*windowname;
	
	if (lua_type(L, 1) == LUA_TSTRING)
	{
		windowname = luaL_checkstring(L, 1);
		argind++;
	}
	else
	{
		windowname = "";		// desktop
	}
	
	int	x, y;
	
	x = luaL_checkint(L, argind++);
	y = luaL_checkint(L, argind);
	
	int	r, g, b;
	
	bool	OK = SInterface->GetPixel(windowname, x, y, r/*&*/, g/*&*/, b/*&*/);
	if (!OK)	return 0;
	
	lua_pushnumber(L, r);
	lua_pushnumber(L, g);
	lua_pushnumber(L, b);
	
	return 3;
}

//---- Set Pixel ------------------------------------------------------

static
int	lg_SetPixel(lua_State *L)
{
	int	numargs = lua_gettop(L);
	
	if ((numargs != 5) && (numargs != 6))
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	const char	*windowname;
	int		argind = 1;
	
	if (lua_type(L, 1) == LUA_TSTRING)
	{
		windowname = luaL_checkstring(L, 1);
		argind++;
	}
	else
	{
		windowname = "";
	}
	
	int	x, y, r, g, b;
	
	x = luaL_checkint(L, argind++);
	y = luaL_checkint(L, argind++);
	r = luaL_checkint(L, argind++);
	g = luaL_checkint(L, argind++);
	b = luaL_checkint(L, argind++);
	
	SInterface->SetPixel(windowname, x, y, r, g, b);
	
	return 0;
}

//---- Play Warning Sound -----------------------------------------------

static
int	lg_PlayWarningSound(lua_State *L)
{
	int	numargs = lua_gettop(L);
	
	if (numargs != 0)
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	// error beep
	::MessageBeep(MB_ICONEXCLAMATION);
	
	return 0;
}

//---- Get Window Rect -----------------------------------------------------

static
int	lg_GetWindowRect(lua_State *L)
{
	int	numargs = lua_gettop(L);
	int	argind = 1;
	
	if ((numargs != 0) && (numargs != 1))
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	const char	*windowname;
	
	if (lua_type(L, 1) == LUA_TSTRING)
	{
		windowname = luaL_checkstring(L, 1);
		argind++;
	}
	else
	{
		windowname = "";		// desktop
	}
	
	int	left, right, top, bottom;
	
	bool	OK = SInterface->GetWindowRect(windowname, left/*&*/, top/*&*/, right/*&*/, bottom/*&*/);
	if (!OK)
	{
		luaL_error(L, "SInterface::GetWindowRect() error");
	}
	
	lua_pushnumber(L, left);
	lua_pushnumber(L, top);
	lua_pushnumber(L, right);
	lua_pushnumber(L, bottom);
	
	return 4;
}

//---- Set Window Rect -----------------------------------------------------

static
int	lg_SetWindowRect(lua_State *L)
{
	int	numargs = lua_gettop(L);
	int	argind = 1;
	
	if ((numargs != 4) && (numargs != 5))
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	const char	*windowname;
	
	if (lua_type(L, 1) == LUA_TSTRING)
	{
		windowname = luaL_checkstring(L, 1);
		argind++;
	}
	else
	{
		windowname = "";		// desktop
	}
	
	int	left, right, top, bottom;
	
	left = luaL_checkint(L, argind++);
	top = luaL_checkint(L, argind++);
	right = luaL_checkint(L, argind++);
	bottom = luaL_checkint(L, argind++);
	
	bool	OK = SInterface->SetWindowRect(windowname, left, top, right, bottom);
	if (!OK)
	{
		luaL_error(L, "SInterface::SetWindowRect() error");
	}
	
	return 4;
}

//---- Get Battery Level ----------------------------------------------

static
int	lg_GetBatteryLevel(lua_State *L)
{
	int	numargs = lua_gettop(L);
	if (numargs != 0)
	{
		luaL_error(L, "error: bad # of args");
		return 0;
	}
	
	int	mins, level;
	
	bool	OK = SInterface->GetBatteryLevel(mins/*&*/, level/*&*/);
	
	lua_pushnumber(L, mins);
	lua_pushnumber(L, level);
	
	return 2;
}

//---- Function Table -------------------------------------

static const
struct luaL_reg	FunctionTab[] = 
{
	{"SetMIDIHandler",		lg_SetMIDIHandler},
	{"SendMIDI",			lg_SendMidi},
	{"SendMouseClick",		lg_SendMouseClick},
	{"GetPixel",			lg_GetPixel},
	{"SetPixel",			lg_SetPixel},
	{"SetMidiHandlerRepeat",	lg_SetMIDIHandlerRepeat},
	{"SetMIDIHandlerDuplicates",	lg_SetMIDIHandlerDuplicates},
	{"GetWindowRect",		lg_GetWindowRect},
	{"SetWindowRect",		lg_SetWindowRect},
	{"WarningSound",		lg_PlayWarningSound},
	{"GetBatteryLevel",		lg_GetBatteryLevel},
	
	{nil,				nil}
};

//---- Init Lua -------------------------------------------

void	MIDIbots::InitLua(Frame *f, ScriptInterface *si, const char *filename)
{
	if (L != nil)		return;		// already running
	
	wxLogMessage("Starting Lua...");
	
	if (si != nil)
	{	// start
		SFrame = f;
		SInterface = si;
	}
	// else is a restart
	
	if (filename != nil)
		gLUAScriptFileName = filename;
	// else is a restart
	
	si->Init();
	
	ExceptionCnt = 0;
	
	wxASSERT(si);
	
	L = lua_open();
	
	// register at panic FIRST
	lua_atpanic(L, lg_atpanic);
	
	luaL_openlibs(L);
	
	// register my abort & (lua) reboot
	lua_register(L, "abort", lg_Abort);
	lua_register(L, "print", lg_print);
	lua_register(L, "reboot", lg_Reboot);

	// register MIDIbots functions
	luaL_openlib(L, "mb", FunctionTab, 0);
	
	// register on-screen display library
	RegisterOSDLib(L);
	
	// register Lua Ports
	RegisterLuaPorts(L);
		
// load & parse Lua script file
	int	res = luaL_loadfile(L, gLUAScriptFileName);
	if (res != 0)
	{	// error
		wxLogMessage("ERROR(%d)  %s", res, lua_tostring(L,0));
		
		// error beep
		::MessageBeep(MB_ICONEXCLAMATION);
		
		return;
	}
	
	// call main function (protected) -- WHY DO I NEED TO CALL THIS FUNCTION FIRST???
	// NOT REALLY SURE WHAT MAIN FUNCTION IS SITTING ON THE STACK ANYWAY
	res = lua_pcall(L, 0/*args*/, 0/*res*/, 0/*err function*/);
	if (res != 0)
	{	// error
		wxLogMessage("ERROR(%d)  %s while calling main() function", res, lua_tostring(L, -1));
		
		// 'error' beep
		::MessageBeep(MB_ICONEXCLAMATION);
		return;
	}
	
// call Start() function
	
	// push Lua function name
	lua_pushstring(L, "Start");
	// get actual function on stack
	lua_gettable(L, LUA_GLOBALSINDEX);
	// check is function on the stack
	if (!lua_isfunction(L, -1))
	{	// lua function not found
		wxLogMessage("ERROR - function Start() missing");
		
		// error beep
		::MessageBeep(MB_ICONEXCLAMATION);
		return;
	}
	
	res = lua_pcall(L, 0/*args*/, 0/*res*/, 0/*err function*/);
	if (res != 0)
	{	// error - didn't run OK
		wxLogMessage("ERROR(%d)  %s while calling Start()", res, lua_tostring(L, -1));
		
		// error beep
		::MessageBeep(MB_ICONEXCLAMATION);
		return;
	}
	else
	{	// 'OK' beep
		SInterface->PlaySoundwave(MD_SOUND_OK);
	}
	
	wxLogMessage("Lua Register done");
}

//----- Exit Lua ------------------------------------------

void	MIDIbots::ExitLua(void)
{
	if (L == nil)		return;		// wasn't running
	
	// put exception handler around here???
	
// call Stop() function
	
	// push Lua function name
	lua_pushstring(L, "Stop");
	// get actual function on stack
	lua_gettable(L, LUA_GLOBALSINDEX);
	// call if Stop() function exists
	if (lua_isfunction(L, -1))
	{
		int	res = lua_pcall(L, 0/*args*/, 0/*res*/, 0/*err function*/);
		if (res != 0)
		{	// error - didn't run OK
			wxLogMessage("ERROR(%d)  %s while calling Stop()", res, lua_tostring(L,0));
			
			// error beep
			::MessageBeep(MB_ICONEXCLAMATION);
			return;
		}
	}
	
	//lua_setgcthreshold(L, 0);	// collect garbage
	
	lua_close(L);
	
	L = nil;
}


// nada mas
------------------------------------------------------------------------------
Try New Relic Now & We'll Send You this Cool Shirt
New Relic is the only SaaS-based application performance monitoring service 
that delivers powerful full stack analytics. Optimize and monitor your
browser, app, & servers with just a few lines of code. Try New Relic
and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may
_______________________________________________
Get Mixxx, the #1 Free MP3 DJ Mixing software Today
http://mixxx.org


Mixxx-devel mailing list
Mixxx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mixxx-devel

Reply via email to