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