Hi @mars and all–
As I find time, I’m working on the first part of my Wizard C++ patch programming tutorial (which, as I’ve suggested elsewhere, focuses on low-level patch programming and control interactions for realtime performance more than on DSP; I’m hoping to help explain the framework into which users can put whatever DSP they want).
Assumptions
If I understand it, there is currently no way for a patch running on a Wizard (or Alchemist, or other hypothetical #USE_MODE_BUTTON
and #USE_RGB_LED
device) to access the LED. This seems to be for two reasons:
- The Patch API simply doesn’t have any route to the
setLed(0, rgb)
function defined inOwl.cpp
(at least not that I can see); - Even if it did,
loop()
, when inRUN_MODE
, is callingupdateLed()
once per cycle (in order to do the generic LED parameter updates), so even if a patch could change the LED timings they would be immediately overwritten by a new value derived from theadc_values
.
(If I’m wrong about the above, what follows will not make much sense so please stop reading here and let me know!)
Feature Suggestion and Use Cases
The updateLed()
stuff is very sensible default behavior, but it doesn’t really use the LED to output any new information (except, I guess, the value of the CV inputs combined with the knobs). I also observe that the main limitation I’m running into with the Wizard as a performance instrument is the lack of visual feedback. I can think of three immediate use cases (all of which are getting invoked in the patch I’m writing for the tutorial):
- Parameter pages–I’m working on approaches to step through pages of parameters (for example, the starting page controls oscillator frequency, pan, and volume, but there’s a second page that changes the oscillator itself) and having visual feedback (“red page”, “purple page”, “blue page”) would be EXTREMELY helpful here to minimize confusion.
- Parameter take-up–as a corollary to point #2, if there are parameter pages, there needs to be a system for parameter take-up (since the parameter values are absolute and would jump otherwise). Visual feedback (probably a blink or two from the LED) when the take-up position is approached or reached is not strictly necessary but makes it a lot nicer.
- LFO visualization–pretty obvious, and very useful.
(By the way, I know that #1 and #2 break the MIDI model, but I’m thinking of this as a close-to-the-metal exercise that’s specific to the Wizard, and I think there would be other uses of a patch-controllable LED in the orthodox MIDI setting).
Draft Implementation Suggestion
Rather than passing a function reference down into Patch, I think it would be simpler to add functions to Patch which modify two variables, one bool
and one uint32_t
, which loop()
could access, by whatever means (my first thought would be using public
variables in ProgramManager program
rather than supplementing ProgramVector
, but there might well be better ways to do it). Other than on patch changes (described below), loop()
would only read these variables, never modify them. I would suggest structuring the API such that a Patch can only write them into, but never read them from, its enclosing ProgramManager (with the promise that once written they would never be written to externally during the lifetime of the patch).
There’s a concurrency question here, but I think it’s minor since being slightly off in the LED timing doesn’t matter.
With this in place, only two things (that I can think of) would need to be changed:
updateLed()
in its current form would get renamed to updateLedFromADCs()
, and the RUN_MODE
branch would end with something like:
case RUN_MODE:
... // mode button?
... // error?
}else{
if(program.ledautoupdate){
updateLedFromADCs();
}else{
setLed(0, program.ledrgb);
}
}
break;
On patch change, which only happens in CONFIGURE_MODE
(MIDI aside), ledautoupdate
would be set back to true
(right after setLed(0, value & 0x01 ? BLUE_COLOUR : GREEN_COLOUR);
) so that the new patch would get the default behavior unless it chose otherwise. If the variables are in the ProgramManager
, this would probably best be put in ProgramManager::resetProgram
inside #ifdef USE_RGB_LED
.
I don’t think the gain selection call would need to change (since updateLed()
isn’t getting called in CONFIGURE_MODE
; as soon as the mode button was released, the updating would follow its previous behavior.
Conclusion
I think this would (probably) be pretty easy to do, and I am certain that for at least some uses of the Wizard (and Alchemist) it would be very, very useful and really expand the utility of the device. Not as nice as the screen on the Magus, but a lot more than nothing!
If it were just a matter of changing loop()
I might have tried hacking it into the firmware myself, but since it implicates Patch
as well I thought I had best leave it as per the above.
The use cases may seem unconvincing, but I think the tutorial will help clarify them (for now, I’m implementing the page, take-up and LFO mechanics, but just pointing out that the lack of visual feedback makes them harder to use consistently under pressure).