lnx studio from github works on linux if you use the xplat branch. this comes with the caveat that it provides everything, but you have to manually set a couple of things up, which means mostly to edit some paths. together with pipewire this is fantastic. I also added this line to the executable to scale the ui:
QT_SCALE_FACTOR=1 QT_AUTO_SCREEN_SCALE_FACTOR=0 QT_SCREEN_SCALE_FACTORS=2 QT_QPA_PLATFORM=xcb
if you have VSTPlugin installed, save this as LNX_VSTi.sc in lnx studios own
‘SCClassLibrary/LNX_Studio Library/2. Instruments’ to get started.
Also edit sclang_conf_lnx.yaml to include the VSTPlugin extension.
It works on my machine
LNX_VSTi : LNX_InstrumentTemplate {
var <vsti, <proxy;
*new { arg server=Server.default,studio,instNo,bounds,open=true,id,loadList;
^super.new(server,studio,instNo,bounds,open,id,loadList)
}
*studioName {^"VSTi"}
*thisWidth {^200} // the default width of this instrument
*thisHeight {^100} // the default height of this instrument
*sortOrder{^10}
*isVisible{^true}
isInstrument{^true}
canBeSequenced{^true}
isMixerInstrument{^true}
mixerColor{^Color(1,1,1,0.4)} // colour in mixer
canTurnOnOff{^true}
hasMIDIClock{^false}
clockPriority{^5} // order for clock beats. sequenced controllers 1st. instruments last.
// the lower the number the higher the priority
// this line is important for mixer instruments!
hasLevelsOut{^true}
interface{^nil} // supply an immutable list of methods to make available to the network
header { // define your document header details
instrumentHeaderType="SC Template Doc";
version="v1.1";
}
initModel {
#models,defaults=[
// 0.solo
[0, \switch, (\strings_:"S"), midiControl, 0, "Solo",
{|me,val,latency,send,toggle| this.solo(val,latency,send,toggle) },
\action2_ -> {|me| this.soloAlt(me.value) }],
// 1.onOff
[1, \switch, (\strings_:((this.instNo+1).asString)), midiControl, 1, "On/Off",
{|me,val,latency,send,toggle| this.onOff(val,latency,send,toggle) },
\action2_ -> {|me| this.onOffAlt(me.value) }],
// 2.amp
[ \db6, midiControl, 2, "Amp",
(mouseDownAction_:{hack[\fadeTask].stop}),
{|me,val,latency,send|
this.setPVP(2,val,latency,send);
this.setMixerSynth(\amp,val.dbamp,latency); // set mixer synth
}],
// 3. master pan
[\bipolar, midiControl, 3, "Pan", (\label_:"Pan", zeroValue_:0),
{|me,val,latency,send|
this.setPVPModel(3,val,0,send); // set p & network model via VP
this.setMixerSynth(\pan,val,latency); // set mixer synth
}],
// 4. peak level
[0.7, \unipolar, midiControl, 4, "Peak Level",
{|me,val,latency,send| this.setPVP(4,val,latency,send) }],
// 5. out channels
[0,\audioOut, midiControl, 5, "Output channels",
(\items_:LNX_AudioDevices.outputAndFXMenuList),
{|me,val,latency,send|
var channel = LNX_AudioDevices.getOutChannelIndex(val);
this.setPVPModel(5,val,0,send); // set p & network model via VP
this.setMixerSynth(\outChannel,channel,latency); // set mixer synth
}],
// 6. main send channel
[-1, \audioOut, midiControl, 6, "Main Send Channel",
(\items_:LNX_AudioDevices.outputAndFXMenuList),
{|me,val,latency,send|
var channel = LNX_AudioDevices.getOutChannelIndex(val);
this.setPVPModel(6,val,0,send); // set p & network model via VP
this.setMixerSynth(\sendChannel,channel,latency); // set mixer synth
}],
// 7. main send amp
[-inf, \db2, midiControl, 7, "Main Send Amp", (label_:"Send"),
{|me,val,latency,send|
this.setPVPModel(7,val,0,send); // set p & network model via VP
this.setMixerSynth(\sendAmp,val.dbamp,latency); // set mixer synth
}],
// 8. syncDelay
[\sync, {|me,val,latency,send|
this.setPVPModel(8,val,latency,send);
this.syncDelay_(val);
}]
].generateAllModels;
presetExclusion=[0,1];
randomExclusion=[0,1];
autoExclusion=[];
}
// return your output model, for fadeIn, fadeOut, and Mixer
peakModel {^models[4]}
volumeModel {^models[2]}
outChModel {^models[5]}
soloModel {^models[0]}
onOffModel {^models[1]}
panModel {^models[3]}
sendChModel {^models[6]}
sendAmpModel {^models[7]}
syncModel {^models[8]}
//// GUI ///////////////////////////////////////////
createWindow{|bounds|
this.createTemplateWindow(bounds,Color(0.1,0.1,0.1,1));
}
createWidgets{
gui[\topLevel]=MVC_RoundedCompositeView(window, window.bounds);
MVC_FlatButton2(window, Rect(10, 10, 85, 30))
.strings_(["gui"])
.action_({ vsti.gui });
MVC_FlatButton2(window, Rect(105, 10, 85, 30))
.strings_(["editor"])
.action_({ vsti.editor });
// MIDI Settings
MVC_FlatButton(window,Rect(10, 50, 85, 30),"MIDI")
.rounded_(true)
.canFocus_(false)
.shadow_(true)
.color_(\up,Color(10665/19997,223/375,2/3) )
.color_(\down,Color(10665/19997,223/375,2/3) )
.color_(\string,Color.white)
.resize_(9)
.action_{ this.createMIDIInOutModelWindow(window, //,low:11,high:12,
colors:(border1:Color(0,1/103,9/77,65/77) , border2:Color(59/108,65/103,505/692))
) };
// MIDI Control
MVC_FlatButton(window,Rect(105, 50, 85, 30),"Cntrl")
.rounded_(true)
.canFocus_(false)
.shadow_(true)
.color_(\up,Color(10665/19997,223/375,2/3) )
.color_(\down,Color(10665/19997,223/375,2/3))
.color_(\string,Color.white)
.resize_(9)
.action_{ LNX_MIDIControl.editControls(this); LNX_MIDIControl.window.front };
} // put your GUI code in here
//// uGens & midi players //////////////////////////
// this will be called by the studio after booting
*initUGens{|server|
SynthDef(\vsti, { arg out = 0;
// VST instruments usually don't have inputs
Out.ar(out, VSTPlugin.ar(nil, 2))
}).add.send(server);
}
// startDSP : this is called once to start ugens at instrument creation
// stopDSP : and once to stop ugen when the instrument is deleted
// used to start & stop fx's or mono synths
// updateDSP : and also update synth parameters in a load
startDSP {
synth = Synth.new(\vsti, [\out, this.instGroupChannel], instGroupID);
vsti = VSTPluginController(synth);
proxy = vsti.midi;
vsti.browse;
}
stopDSP { synth.free; vsti = proxy = nil; }
updateDSP{|oldP,latency|
synth.set(\out, this.instGroupChannel);
this.setMixerSynth(\sendChannel,LNX_AudioDevices.getOutChannelIndex(p[6]),latency); // set mixer synth
this.setMixerSynth(\outChannel, LNX_AudioDevices.getOutChannelIndex(p[5]),latency); // set mixer synth
}
replaceDSP{} // to be used by synth's or fx's that need to replace the current
// synth with a new one in order to update values
// midi In methods, latency is supplied
noteOn {|note, vel, latency| proxy.noteOn(0, note, vel)} // noteOn
noteOff {|note, vel, latency| proxy.noteOff(0, note, vel)} // noteOff
control {|num, val, latency| proxy.control(0, num, val)} // control
bend {|bend , latency| proxy.bend(0, bend)} // bend
touch {|pressure , latency|} // pressure
program {|program , latency|} // and program (the selectProgram method is called 1st
sysex {|data , latency|}
midiInternal{|command,arg1,arg2| } // internal comms via midi (not used yet)
// a quick way to get working, to be used by all midi in future
pipeIn{|pipe|
switch (pipe.kind)
{\noteOn} { // noteOn
this.noteOn(pipe.note, pipe.velocity, pipe.latency)
}
{\noteOff} { // noteOff
this.noteOff(pipe.note, pipe.velocity, pipe.latency)
}
{\control} { // control
this.control(pipe.num, pipe.val, pipe.latency)
}
{\bend} { // bend
this.bend(pipe.val, pipe.latency)
}
{\touch} { // touch
this.touch(pipe.pressure, pipe.latency)
}
{\program} {
// to do and confirm
}
}
} // end ////////////////////////////////////