Hello,
I think someone, maybe Nathan, has developed a class for the controller Midi Fighter Twister.
Would someone share it or know how I could find it ?
Many thanks,
Christophe
Hello,
I think someone, maybe Nathan, has developed a class for the controller Midi Fighter Twister.
Would someone share it or know how I could find it ?
Many thanks,
Christophe
I just posted my Twister controller class as a quark. You can install via Quarks.install("https://github.com/scztt/Twister.quark")
Itâs not very documented (just the Twister class itself), and isnât really prepared for general public usage, so there may be issues especially when used outside of workflows that look like mine. However, Iâve used this for many performances and itâs basically my core control class, so itâs still quite stable and powerful. You may need to familiarize yourself with the Connection quark, since it makes heavy use of those patterns. Iâm totally happy to help with configuration or answer questions about it if needed.
One additional caveat - youâll need to configure your twister knobs to send relative updates rather than absolute values. The expectation is that the knobs are ccâs 0âŚ15, but think this is the default anyway.
Thank you so much,
Iâve just bought it and will test it soon.
By installing your quark, Iâve got in the post window :
WARNING: MIDIWatcher not found
Is it necessary ?
Many thanks,
Christophe
It probably is neccesary. I may not have published that one, Iâll post it and fix the issue in the next day or so.
Twister is a fantastic and super flexible device, especially for the price Iâve got two and use them all the time.
Okay - you should now be able to install https://github.com/scztt/MIDIWatcher.quark
or update and re-install Twister
to get things working.
Hello Scott,
Iâm eventually testing the Twister class and Iâve got some questions, even though I still have to read the documentation about the Connection Quark.
ERROR: Message âminvalâ not understood.
RECEIVER:
Instance of Array { (0x120e85ba8, gc=00, fmt=01, flg=00, set=02)
indexed slots [2]
0 : Integer 100
1 : Integer 5000
}
ARGS:
PATH: /Users/xon/Documents/Projets/SC_LFL/Live4Life/_0 Live Q2.scd
PROTECTED CALL STACK:
Meta_MethodError:new 0x11ca99480
arg this = DoesNotUnderstandError
arg what = nil
arg receiver = [ 100, 5000 ]
Meta_DoesNotUnderstandError:new 0x11ca9b440
arg this = DoesNotUnderstandError
arg receiver = [ 100, 5000 ]
arg selector = minval
arg args = [ ]
Object:doesNotUnderstand 0x11d0dbc80
arg this = [ 100, 5000 ]
arg selector = minval
arg args = nil
ControlSpec:setFrom 0x1204761c0
arg this = a ControlSpec(0, 1, âlinearâ, 0.0, 0, ââ)
arg otherSpec = [ 100, 5000 ]
AbstractControlValue:spec_ 0x11ecf7180
arg this = a BusControlValue
arg inSpec = [ 100, 5000 ]
a FunctionDef 0x113dccb38
sourceCode = â{
~amp.spec = \db.asSpec;
~freq.spec = \freq.asSpec;
~formant.spec = [100, 5000];
~bw.spec = [10, 200];
~speak = OnOffControlValue();
}â
a FunctionDef 0x11d35f440
sourceCode = ââ
Function:prTry 0x11d62f5c0
arg this = a Function
var result = nil
var thread = a Routine
var next = nil
var wasInProtectedFunc = true
CALL STACK:
DoesNotUnderstandError:reportError
arg this =
< closed FunctionDef >
arg error =
Integer:forBy
arg this = 0
arg endval = 6
arg stepval = 2
arg function =
var i = 6
var j = 3
SequenceableCollection:pairsDo
arg this = [*8]
arg function =
Scheduler:seconds_
arg this =
arg newSeconds = 19.388749358
Meta_AppClock:tick
arg this =
var saveClock =
Process:tick
arg this =
^^ The preceding error dump is for ERROR: Message âminvalâ not understood.
RECEIVER: [ 100, 5000 ]
probably due ro this :
~c.use {
~amp.spec = \db.asSpec;
~freq.spec = \freq.asSpec;
~formant.spec = [100, 5000];
~bw.spec = [10, 200];
~speak = OnOffControlValue();
};
~t = Twister(\default);
~t.rows(0).do(.ledColor(Color.rand));
Many thanks for your help,
Christophe
There was a formatting issue with the second question :
By evaluating this :
~t = Twister(\default);
~t.rows(0).do(.ledColor(Color.rand));
The post window returns this :
Connecting TwisterDevice(-1862386936) to Twister(-825954363)
â a Twister
â [ a TwisterKnob, a TwisterKnob, a TwisterKnob, a TwisterKnob ]
But there is no update of Led colors on the Twister.
Many thanks,
Christophe
Hey @Xon â
One thing I notice - if youâre JUST executing the code you specified - LED lights are ONLY shown if the knob is actually mapped to a CV. So you would first need to do e.g.
t.rows(0, 0).cv = NumericControlValue(spec:[0, 100]);
t.rows(0, 0).ledColor = Color.red;
If this does not work, a few things you should try for debugging:
Connecting TwisterDevice(-1862386936) to Twister(-825954363)
message codes should correspond to the device IDâs if you list MIDIOut sources.You MIGHT need to have the knobs of the twister mapped a particular way using the MIDI Fighter config app. I can check in a few days when I have my equipment set up a little better to see what my local config is (I just moved, so Iâm mostly a pile of boxes).
Hi Scott,
I think I 've got issues understanding the way connecting the Twister.
When I do :
MIDIClient.sources
Iâve got : MIDIEndPoint(âMidi Fighter Twister 1â, âMidi Fighter Twisterâ, -1404845322)
since I renamed The Midi Fighter Twister in the MIDI Monitor â Midi Fighter Twister 1
When I do :
t = Twister(\default);
Iâve got : Connecting TwisterDevice(407605236) to Twister(1091711609),
But nothing seems connected, since Iâve got nothing with MIDIFunc.trace.
Iâve got to do :
MIDIIn.connectAll;
to get info with MIDIFunc.trace.
That is why, by evaluating this :
t.rows(0, 0).cv = NumericControlValue(spec:[0, 100]);
Iâve got this error :
ERROR: Message âdefaultâ not understood.
RECEIVER:
Instance of Array { (0x10c725b78, gc=78, fmt=01, flg=00, set=02)
indexed slots [2]
0 : Integer 0
1 : Integer 100
}
ARGS:
PATH: /Users/xon/Documents/Projets/SC_LFL/Live4Life/_0 Live Q3.scd
CALL STACK:
DoesNotUnderstandError:reportError
arg this =
Nil:handleError
arg this = nil
arg error =
Thread:handleError
arg this =
arg error =
Object:throw
arg this =
Object:doesNotUnderstand
arg this = [*2]
arg selector = âdefaultâ
arg args = [*0]
AbstractControlValue:constrain
arg this =
arg notify = true
AbstractControlValue:init
arg this =
arg initialValue = nil
arg inSpec = [*2]
< closed FunctionDef > (no arguments or variables)
Interpreter:interpretPrintCmdLine
arg this =
var res = nil
var func =
var code = ât.rows(0, 0).cv = NumericConâŚâ
var doc = nil
var ideClass =
Process:interpretPrintCmdLine
arg this =
^^ The preceding error dump is for ERROR: Message âdefaultâ not understood.
RECEIVER: [ 0, 100 ]
Many thanks for your help,
Christophe
Hello,
Today Iâm trying to fight again with the MIDI Fighter Twister class.
I try to connect it to SC on Mac, with no success.
Has someone succeeded on Mac and can help me ?
The example of the Twister class posts an error for me, as previously posted.
If I do :
MIDIClient.sources;
// posts â [ MIDIEndPoint(âGestionnaire IACâ, âBus 1â, -63353900), MIDIEndPoint(âMidi Fighter Twister 1â, âMidi Fighter Twisterâ, -1404845322) ]
MIDIClient.destinations;
// posts â [ MIDIEndPoint(âGestionnaire IACâ, âBus 1â, 1886033969), MIDIEndPoint(âMidi Fighter Twister 1â, âMidi Fighter Twisterâ, -1777583588) ]
So I try :
TwisterDevice.registerDevice(\myTwister, âMidi Fighter Twister 1â, âMidi Fighter Twisterâ);
// posts â TwisterDevice
t = Twister(\myTwister);
// posts something I do not understand
â Connecting TwisterDevice(-737782931) to Twister(707069498)
â a Twister
t.rows(0, 0).cv = NumericControlValue(spec:[0, 100]);
// posts an error :
ERROR: Message âdefaultâ not understood. ⌠RECEIVER: [ 0, 100 ]
Many thanks,
Christophe
First: Try spec: [0, 100].asSpec
â probably the method ought to be changed to do that automatically, but the quark author may have been unaware of that loose convention.
Second: Please enclose your code snippets in triple-backticks.
```
code here
```
hjh
Many thanks, James!
I am doing something wrong by registering it⌠(as explained in my previous mail).
For example, the code below does nothing :
~t.rows(0).do(_.ledColor_(Color.rand));
// posts -> [ a TwisterKnob, a TwisterKnob, a TwisterKnob, a TwisterKnob ]
Second, Iâve got got a new error by evaluating the example and adding .asSpec
to the 2 arrays of the example.
So below the example code and the error :
(
s.waitForBoot {
~t = Twister(\default);
// Set up some control values we want to connect the twister to.
// See help for Connection library or BusControlValue for more details.
~c = ControlValueEnvir(BusControlValue);
~c.use {
~amp.spec = \db.asSpec;
~freq.spec = \freq.asSpec;
~formant.spec = [100, 5000].asSpec;
~bw.spec = [10, 200].asSpec;
~speak = OnOffControlValue();
};
// Map our control values to knobs and buttons
~t.rows(0, 0).knobCV = ~c.amp;
~t.rows(0, 1).knobCV = ~c.freq;
~t.rows(0, 2).knobCV = ~c.formant;
~t.rows(0, 3).knobCV = ~c.bw;
~t.buttons[0].cv = ~c.speak;
// Set some random colors
~t.rows(0).do(_.ledColor_(Color.rand));
// Play a simple synth
Ndef(\tone, {
|freq = 100, amp = 0, formant = 100, bw = 100, t_trigger|
var sig;
sig = Formant.ar(freq, formant, bw, amp.dbamp);
sig = sig + (WhiteNoise.ar(0.1) * Env.perc().kr(gate:t_trigger));
}).play;
// Map our control values to synth params.
Ndef(\tone).map(
\amp, ~c.amp.asMap,
\freq, ~c.freq.asMap,
\formant, ~c.formant.asMap,
\bw, ~c.bw
);
// Map our button to something arbitrary
~c.speak.signal(\on).connectToUnique({
"Pushing a button".postln;
Ndef(\tone).set(\t_trigger, 1);
});
// Set some initial values.
// Note that this should update the display on the Twister device if it is connected.
~c.setValues((
freq: 370,
amp: -10,
formant: 1000,
bw: 300
));
};
)
ERROR: Message âconnectToUniqueâ not understood.
RECEIVER:
Instance of UpdateDispatcherItem { (0x1693f06b8, gc=98, fmt=00, flg=00, set=02)
instance variables [3]
dependants : instance of IdentitySet (0x1693f0648, size=2, set=2)
object : instance of OnOffControlValue (0x129b85498, size=11, set=4)
key : Symbol âonâ
}
ARGS:
Instance of Function { (0x1693f9ba8, gc=98, fmt=00, flg=00, set=02)
instance variables [2]
def : instance of FunctionDef - closed
context : Frame (0x123965658) of Interpreter:functionCompileContext
}
PATH: /Users/xon/Documents/Projets/SC_LFL/Live4Life/_Init Midi Twister.scd
Many thanks,
Christophe
And if it can help to help me to connect this controller :
MIDIFunc.trace
posts this as ID :
MIDI Message Received:
type: control
src: -1404845322
chan: 0
num: 12
val: 110
Christophe
I donât know these classes at all.
The spec-related error is pretty common, so I made a reasonable guess about it.
That guess got you past that error into something much more specific to this class framework. Iâve got no idea about that.
Anyone else?
hjh
It looks like you have an out of date version of theConnection
quark - both the .asSpec
conversion and connectToUnique
should be present in the current latest. You should be able to update from the quarks UI Or using something like Quark("Connection").update
.
Many thanks, Scott!
By updating it specifically, the example works now without error (strange, because I am sure I had updated all the Quarks via the the Quarks windowâŚ).
Anyway, the controller still does not reactâŚ
Do you have an ideas of what I am doing wrong for registering it as written in my previous mails ?
Thanks
Christophe
By evaluating the example, it posts :
Connecting TwisterDevice(-425842367) to Twister(-1519646368)
The controller does not react. By evaluating it again :
Connecting TwisterDevice(-425842367) to Twister(1820904612)
What is weird is that :
MIDIFunc.trace;
gives apparently a different iD. But I do not know how to change it in your classâŚ
MIDI Message Received:
type: control
src: -1404845322
chan: 0
num: 12
val: 28
The quarks system is not reliable about updating to specific versions, and I release versions of Connection semi-often, so itâs probably not your fault - just a glitch in the space-time fold.
Two things to check:
Twister
class expects that the device is configured to send relative messages - you can set these in the MIDIFighter Utility by setting Encoder MIDI Type
to Enc 3FH/41H
. It looks from your trace log that itâs probably not configured this way (the values should be 63/65), so this is probably the issue.\default
and \secondary
, which are linked to MIDI devices named Midi Fighter Twister 1
and Midi Fighter Twister 2
. If your device is for some reason not showing up with these names, you can rename it in the MacOS MIDI/Audio application, or you can point to another name by doing:~twister = Twister(TwisterDevice(\mydevice, MIDIClient.sources[0]))
~t = Twister(TwisterDevice(\mine, MIDIClient.sources[1]));
~t.knobs[0].cv = NumericControlValue(spec:[0, 1]);
~t.knobs[0].ledColor = Color.red;
Finally, the device should be totally blacked out and unresponsive until youâve set at least one CV (only knobs with CVâs attached will light up and behave).
Many thanks, Scott!
I can now make it work, your quark is so cool!
Just a few remarks and questions :
Midi Fighter Twister 1
in the MacOS MIDI/Audio application,~t = Twister(\default);
does nothing.
But by specifying the source, like you indicate me, it works well :
~t = Twister(TwisterDevice(\mine, MIDIClient.sources[1]))
My sources are :
[ MIDIEndPoint(âGestionnaire IACâ, âBus 1â, -63353900), MIDIEndPoint(âMidi Fighter Twister 1â, âMidi Fighter Twisterâ, -1404845322) ]
You seem not to use the 4 banks, or the super knob ?
Would you recommend specific configurations in the Midi Fighter Utility concerning global settings, sensitivity, excluding Encoder Midi Type ?
In the Twister example, you use ControlValueEnvir(BusControlValue)
, which seem not documented in the Connection Quark.
I would like to know more about the types of ControlValue, e.g. the difference and advantages between a NumericalControlValue and a BusControlValue.
I have now to dig into the Connection Quark and how to best use it for MVC.
In the Twister example, .asMaps
is missing in the Ndef \bw, ~c.bw
.
Many thanks,
Christophe
Glad itâs working, and thanks for being so patient! This was pretty untested outside of my own usage.
The problem youâre seeing is one I see occasionally also - it seems to be possibly related to some kind of general MIDI initialization problem in SuperCollider, but I canât reliably reproduce it? The best thing to do is just add a line for your TwisterDevice
setup in your startup file - after youâve run it once, you can refer to that device by name (e.g. \mine
) when setting up Twister
instances.
A TwisterDevice
is a Singleton
that corresponds to one actual physical device. You can have as many Twister
instances as youâd like, and you can connect and disconnect them from devices as needed (only the connected Twister
will update the controller, and receive control changes) - this is a replacement for the Twister bank behavior:
~bank1 = Twister(\mine);
~bank2 = Twister(\mine);
~bank3 = Twister(\mine); // this one is now connected, because Twister objects are connected when they are constructed
~bank1.connect(\mine); // connect ~bank1 - now these cv's will display and respond - the other banks will not
The super-knob doesnât change the physical accuracy of the knob, only multiplies each tick so it increments/decrements the value faster. Since the Twister
class is using relative ccâs, this has no effect. You can change the increment scale via e.g. Twister(\mine).knobs[0].knobScale = 0.1
(the default is 0.05).
ControlValueEnvir
is a subclass of EnvironmentRedirect
(which in turn behaves like an environment) where values are implicitly created when referred to. The class of the objects that are implicitly created is passed as an argument to the constructor, so ControlValueEnvir(BusControlValue)
will create BusControlValues
. If youâve ever used ProxySpace
, this behaves almost identical, but instead of creating NodeProxy
âs it creates a control value of the type you specify. Thereâs one specific use-case that makes this class very good:
~c = ~c ?? { ControlValueEnvir(BusControlValue) };
~c.use {
~freq.spec = [20, 20000];
~amp.spec = [-90, 0];
};
~t.knobs[0].cv = ~c[\freq];
In this case, ~freq and ~amp are implicitly created as BCVâs when they are referred to, and their specs are set. Thereâs a very good syntactic reason for this: you can change your specs or even add new CVâs and re-execute the above statement and the cvâs will be updated, but are not re-created - and will remain connected to any UI / controllers.
CVE has a few convenience methods to e.g. set all values from a Dictionary
, ~c.setValues((freq:100, amp:-10))
, convert them to a Pbind
of the values (.asPbind
), or turn them into an array of Synth arguments (~c.asSynthArgs
).
BusControlValue
is just a NumericControlValue
that has an associated Bus
that gets updated with the value. They can be used interchangeably, but BCV is more convenient and efficient if youâre going to map it to a synth parameter.
Note also that both Ndef(\foo).set(\freq, ~c.freq)
and Ndef(\foo).set(\freq, ~c.freq.asMap)
are valid. The first sets the value ONCE, the second actually maps the value to the Synth input.