My program (a plugin, VST) is a standard plugin, it does no audio processing just MIDI. It has embedded lua for processing, the basic stuff it does is:
- when the user moves a knob on the GUI a LUA function is called (that function can update some other GUI elements on the plugin)
- the processBlock method in the plugin calls the corresponding processBlock function in LUA (implemented by the user, this also can change the GUI, same principal)
it’s all fine if i don’t change GUI elements in LUA in the processBlock, but when i start doing that, i get assetions about MessageManager locking. I tried locking the MessageManager like it says in the manual immediatly when the setValue method is called from lua, but that didn’t help (the setValue method of my class that catches LUA calls, modifies the GUI by calling the gui’s setValue() method). I put the MessageManager lock in the setValue() method of the GUI, that helped, cause i was able to open the editor and see the GUI changed from the processBlock (some text in the TextEditor called on each processBlock run). But when i touched a slider after a few moves i get a host+plugin freeze (a deadlock i imagine).
I’m stuck with how to do this locking, this plugin has 3 “layers”, the GUI layer, the VST layer and the LUA layer, all interact with each other somehow.
some code
int ModifierContainer::setValue (LuaVirtualMachine& vm)
{
/* controllers are indexed with leading 1 not zero
we could keep zero indexed in the global arrays but
for consistency we just do a -1 for controller index here */
lua_State *state = (lua_State *) vm;
const int valueType = lua_type (state, -1);
if (valueType == LUA_TNUMBER)
{
const uint32 newValue = (uint32) lua_tonumber (state, -1);
lua_remove (state, -1);
const uint32 index = (uint32) lua_tonumber (state, -1);
setEditorValue (index-1, newValue, true);
return (true);
}
else if (valueType == LUA_TSTRING)
{
const String newValue (lua_tostring (state, -1));
lua_remove (state, -1);
const uint32 index = (uint32) lua_tonumber (state, -1);
setEditorValue (index-1, newValue, true);
return (true);
}
else
{
return (false);
}
}
the setEditorValue method just checks if the editor is open (pointer is valid) and calls the editor method setControllerValueText
void DeviceEditor::setControllerValueText (const uint32 controllerId, const String controllerValue, const bool update)
{
Component *c = getComponentById (controllerId);
if (c)
{
TextEditor *editor = dynamic_cast <TextEditor*> (c);
if (editor != 0)
{
const MessageManagerLock mmLock;
editor->setText (controllerValue, update);
}
}
}
in the VST processBlock method i call
modifierContainer->luaProcessBlock(pos);
witch is defined as
void ModifierContainer::luaProcessBlock (const AudioPlayHead::CurrentPositionInfo lastPosInfo)
{
lua_State *L = (lua_State *) m_vm;
lua_pushstring (L, "position");
lua_newtable (L);
luaSetArrayElement (T("bpm"), (uint32)lastPosInfo.bpm);
luaSetArrayElement (T("ppqPosition"), (uint32)lastPosInfo.ppqPosition);
luaSetArrayElement (T("ppqPositionOfLastBarStart"), (uint32)lastPosInfo.ppqPositionOfLastBarStart);
luaSetArrayElement (T("timeSigDenominator"), lastPosInfo.timeSigDenominator);
luaSetArrayElement (T("timeSigNumerator"), lastPosInfo.timeSigNumerator);
lua_settable (L, LUA_GLOBALSINDEX);
if (scriptHasFunction ("processBlock"))
{
selectScriptFunction ("processBlock");
go (0,0);
}
}
when the gui is changed a lua method is called
/* internal setValue used just by this class */
void ModifierContainer::intSetValue (const uint32 index, const uint32 newValue)
{
Modifier *m = modifierArray[index];
if (m)
{
parent->getCallbackLock().enter();
const String lua = m->getLuaCode();
if (scriptHasFunction (lua.toUTF8()))
{
selectScriptFunction (lua.toUTF8());
addParam (midiChannel);
addParam (newValue);
if (m->isController())
{
const uint32 n = m->getControllerNumber();
addParam (n);
}
go (1, m);
if (!luaDbg->error().isEmpty())
{
Logger::writeToLog (T("ModifierContainer::intSetValue Funcion call errors"));
Logger::writeToLog (luaDbg->error());
}
}
m->setValue (newValue);
m = 0;
parent->getCallbackLock().exit();
}
}
in LUA the processBlock just does
function this.processBlock (this)
this:setValue (parameters["parameterName"], "processBlock");
end
or the same this:setValue can be called on any part of the gui when the user touches it.
Could you perhaps give me some pointers on how to do locking the right way here. I spawn no additional Threads of my own (i don’t know about LUA internal stuff).
the code (working) is here http://code.google.com/p/ctrlr/source/browse/#svn/trunk/src