stoermelder's Idea und Development Thread

the controller has two buttons to activate layer a and layer b and with each layer the 8 knobs, one fader and 16 buttons send midi data on a different cc or a different note - example: first knob sends on cc 1 if layer a is enabled and on cc 11 if layer b is enabled and it is sending them on channel 11 always for both layers … the controller remembers its state for both layers properly if switching between them … by experimenting i found out that i can enable the lights of the buttons by just sending the button midi note at velocity 127 simply on channel 11 back to the controller and the same for the state of the knob and the led ring around it - the cool thing is that sending a velocity to the controller to a certain cc it really sets that knob on the controller to that value - so if i first move the knob on the controller, then the midi-cat mapped knob in vcvrack moves … now i can move the knob in vcvrack and due to the sending back the knob now is also on the new position on the controller too … the not so cool thing is that the controller seems to only evaluate the midi sent back to it for the currently active layer (i.e. if i would send a cc back to the controller for a cc knob in the inactive layer it will not have recognized that change when switching back to its layer) and for this it would be good if midi-cat could optionally kind of refresh-send the midi states back to the controller regularly (i think 2-5 times a second should be fine) so that that gets updated after a layer switch to reflect all the changes in the formerly inactive layer too … layer switching is completely inside of the controller and does not create any midi events btw.

I had a vision for a collection of modules that would help with adding structure to patches. They are designed to capture the features a DAW provides, while keeping the spirit of modularity.

Using them together, you have a way to record entire songs in Rack; there is an ability to scrub to particular points of the song, and adjust and/or add automation, audio, or midi data.

The most essential one is the one I called FRAME. There is also AUTOMATION and PLAY, which are similar to FRAME, and TIMELINE.

There are also MACRO and MACRO FRAME, as well as OSC and OSC FRAME.


FRAME

I drafted an svg a while back, but never found the motivation to implement it myself. Here it is.

I realized it is a bit cluttered after making it, but I really wanted it to be compact horizontally because I imagine using a lot of them in a song.

Basically the FRAME module ‘frames’ or captures a signal.

To record with FRAME, twist the SCENE knob to select which scene is written to, plug the signal into the IN port, patch a clock signal into the RATE port, and either press the WRITE_BUTTON or patch a signal into the WRITE port to enable recording.

When WRITE is active and only the RATE port is patched, FRAME will continuously record the signal into a buffer. When WRITE is pressed again, it will snap the length of the buffer to the nearest clock step as indicated by RATE and stop recording.

Alternatively, if you press the ADD button instead of WRITE, it will enter into ‘add’ mode. The behaviour of this mode can be configured to either be ‘Latch Replace’ (where when the input changes on a channel, the buffer is replaced for that channel (useful for adding more cv on other channels)), or ‘Sum’ (where input is continuously summed with the buffer for all channels (useful for audio overdubbing)).

The WRITE and ADD ports normally take toggle values, but also respond to continuous values, in which their effects are scaled by the ports value. For example, with .5 being on the WRITE port, the current audio in the buffer is not replaced entirely, but multiplied by .5. If .5 were on the ADD port, the input signal is multiplied by .5 before being added. I currently use a live looper called wayback and it works a similar way and it’s great for fluidity in live looping.

One could use the SCENE knob to record multiple takes to filter later, or to sequence recordings, or to crossfade between recordings in interesting ways if modulated.

The two buttons by the scene knob are called CLEAR and EDIT. The CLEAR knob erases a scene on press, and erases all scenes on long press. The EDIT knob pulls up a window where you can edit the signal recorded! Something that looks like this:

Each lane contains the buffer for the particular scene and channel. One could click and add points to the lanes, or decrease all points, or possibly copy and paste points… just look at a DAW for some feature ideas.

The red knob in FRAME is an amplifier for the signals read from the buffer. To mute FRAME for a bit, just turn it all the way down. You can set the levels for audio recorded using this knob. I also envisioned a circular LED strip indicating the current value of each channel going around the knob (sort of like VIZ, just in a circle) to be able to quickly see what a FRAME is outputting.

Screenshot_20200922_175735

When FRAME has a buffer already recorded, changing the rate of the clock on the RATE port will increase or decrease the speed.

I went over the behavior when just the RATE port is patched with a clock signal. If you patch the POS port as well, the position will still progress at the speed according to RATE, but the position can be modulated with POS. Patch an LFO for example, to get a wiggly effect on playback.

When only POS is patched, FRAME does not progress at all, and records and reads signals at the point of the buffer indicated by POS. You could use FRAME as a granular engine this way, or patch other interesting signals into POS to read audio back in creative ways.

When both POS and RATE are unpatched, FRAME defaults to the RATE and POS of the FRAME it is touching on the left. When there is no FRAME on the left, FRAME defaults to the RATE and POS of the TIMELINE module.

When IN is not patched, FRAME defaults to the OUT of the previous FRAME on the left unless the PASSTHROUGH switch is active on that module - in that case, FRAME receives the IN of that module.

If OUT is not patched, the FRAME’s output is summed with the FRAME OUT on the left.

This unpatch and passthrough behaviour gives way to an easier interface for recording multiple signals. For example, to add a sound element that is 8 beats long in a 4 beat loop, simply duplicate the FRAME, activate PASSTHROUGH on the previous FRAME, and enter write mode on the FRAME and record your sound for 8 beats. Nothing else.


PLAY

PLAY is similar to FRAME, it is just for MIDI data.

The differences between FRAME are:

  • Instead of a red AMP knob, it has a VEL knob that controls the velocity of notes.
  • Instead of a circular LED strip to visualize the output, it has a piano roll that displays the notes being played (currently it’s a snip of rewin’s quantizer). The velocity is represented by the opacity of the led, or possibly the color.
  • Write mode becomes a ‘Midi Replace’ mode, and add mode becomes a ‘Midi Overdub’ mode.
  • Instead of an automation lanes edit interface when you press the EDIT button, it opens up a midi editing interface, similar to DAWs.

AUTOMATION

The AUTOMATION module is similar to FRAME, except it does not have an IN and OUT port, and is meant for recording parameter automation. When AUTOMATION is in write mode, twisting knobs of other modules records automation into the next available channel in the AUTOMATION module. When AUTOMATION is in add mode, you can add more parameters or latch replace existing ones.

Using AUTOMATION in conjunction with TIMELINE (by leaving POS and RATE unpatched) as well as FRAME, you can create songs in VCV Rack just like in the DAW! Simply record audio loops and possibly CV with multiple FRAME’s, and automate their AMP and SCENE params onto a TIMELINE position. Using this, you could play a verse for this many bars, then a chorus, etc…

AUTOMATION modules are also aware of one another. Multiple AUTOMATION modules can affect one parameter, their output is simply summed. This is similar to the feature of automation items in DAWs.


TIMELINE

This module serves as the global rate and pos for modules that have no RATE or POS connected and no neighboring FRAME (or PLAY & AUTOMATION) modules.

It has params and inputs for controlling the RATE and POS similar to the FRAME module.

When the TIMELINE position moves, the modules connected to it move as well. FRAME’s and AUTOMATION’s and PLAY’s that are looping also move in their position so to preserve deterministic playback.

I’m still thinking about the details of this module.


MACRO

MACRO is somewhat similar in functionality to this request https://github.com/VCVRack/Rack/issues/655 , except it integrates with an expansion module ‘MACRO FRAME’ to enable ‘freezing’ of macros.

MACRO is a column of inputs and outputs which define the I/O for a strip of modules. They introduce a higher level of abstraction - a collection of modules, a ‘macro’.

Ideally, this module could collapse the modules in the macro into itself so all you see is the relevant I/O, as well as possibly textual labels about what they are. However, even if this would not be possible with current API, implementing MACRO would not be pointless. You could still quickly save and load macros, and audition different macros of similar type (e.g. ‘modulator’, ‘sound source’, ‘audio fx’) without needing to repatch everything.

I also envisioned the ability to easily route between macros. Instead of patching macros together, one could press a TOGGLE button on a specific output port, and twist an input port of another macro to send more and more signal to it. This would enable easy automation of patching, without involving huge routing matrices with many redundant knobs. It would also enable an ability to control patching via OSC. Unfortunately in the current model, this may require infinitely rotating knobs, which is something I am not sure VCV supports.


MACRO FRAME

FRAME for macros!

Basically, it is like FRAME, except it is placed next to a MACRO module. The core function of this module is to record the output of a MACRO, so that when the MACRO FRAME enters read mode, the modules inside the MACRO are automatically disabled, thus saving resources.

You could think of this as freezing a track with an fx chain in a DAW, except the track has many outputs which may be audio or cv.

This module would theoretically allow one to create huge patches.


OSC

OSC stands for OpenSoundControl. It is an internet protocol that sends values to an IP at some address (like /slider/2). I have started using it to control VCV Rack via my Surface and an interface I made in OpenStageControl. The interface makes the process of creating sounds I hear in my head so much more comfortable. There is no clicking involved, you can adjust multiple parameters at once, and can do so very rapidly.

Here’s a picture:

Currently, I do this by sending OSC data to REAPER, and then sending MIDI CC data to control parameters in Rack.

The vision for the OSC module, is to convert osc messages to parameter automation. It is similar to trowaSoft’s cvOSCcv, except instead of converting received osc values to cv, it uses them to modulate parameters.

Having this restriction has the bonus that

  • Mapped parameter names can be sent back to osc for display.
  • Values are automatically sent back to osc for display when the parameter changes.

The OSC module would be able to handle much more mappings than trowaSoft’s cvOSCcv. I image an interface similar to CV-MAP, except there is a place to input the address.

The OSC module integrates with the OSC FRAME module, an expander for it.


OSC FRAME

OSC FRAME is similar to MACRO FRAME, it also records and is able to playback, add, edit. and rewrite the output of the module it expands ( in this case, the OSC module).

OSC FRAME integrates with OSC and the device sending the OSC messages. OSC data that was captured by OSC FRAME and is being played back, is sent to the osc device for display.

This would mean that the knobs in the image above would move according to the actual values of the parameter they are mapped to. In theory, one could play back a song and watch as the knobs in the osc interface morph in time, and optionally latch replace or clear certain movements.


In conclusion, I think these modules would do wonders for structure and usability in VCV Rack. I hope someone else thinks this vision is as cool as I think it is. I thought quite a bit about how to go about bringing in some of my favorite features of the DAW, as well as OSC, into VCV Rack and this is the best solution I could think of. I think this collection of modules would form such an amazing music interface, it could even make the most artistically inept into sound wizards.

1 Like

At the risk of diverging too much from the stoermelder thread, have you checked out the Entrian Sequencers? Different interface but you’ll find a lot of the DAW-like features from your first three modules there.

1 Like

I’ve seen them, but haven’t used them. You’re right though, they do have a lot of the features. I prefer my interface more though, it feels more compact and modular.

Thanks for the explanation. As I said before I don’t understand how this controller, or the led rings, could be useable in any other software.
Did you check the editor software for the controller? Maybe there are some settings which could help.

Interesting post - but would this not be more suited to the “Module ideas” thread rather than this Stoermelder thread?

1 Like

Ah I didn’t know, I’ll post it to there as well.

the controller itself is working fine, consistent and as expected as long as one uses it in a normal way and does not want to extend its functionality by trying to set its position without turning a knob on itself :slight_smile: … even that actually works well as long as one does not switch the layers …

In my opinion this would be the only reason I would buy this controller, normal MIDI feedback operation.

another little idea: would it be possible to make the velocity value send back from midi-cat in button toggle mode configurable like @Curlymorphic did it in his iverson module: Studio 6 + 1, Trigger sequencer for midi controllers, prerelease ? this would make it possibe to structure larger fields of toggle buttons on a supported controller a bit by color …

best wishes and thanks for all your good stuff - hexdump

update: in the midi-cat case it would then only be one slider for the value instead the five in iverson for the different states

There is an open feature request for that. I haven’t thought about how to provide such settings in an elegent and useable way, maybe I can come up with something good.

I’ve been reasoning about some RPC for modules since I started to use rack but never got back to have a look at the source code. Today I sat down and implemented the basic functionality of my python midi codec with a patch, where I showcase how I script 2 modules … (Plateau and ADDR-Seq) by entering python commands in a Jupyter Notebook. On mac and linux the setup and code is a little bit easier, as you can create virtual ports by code, on windows I had to use loopMIDI which is easy enough to setup. So for this I created a loopback device called VCVScript.

The idea is: I created an automatic mapping with midi-cat, created a list of the same order with python compatible names.

plateau_controls = [
    "dry_level",
    "wet_level",
    "pre_delay",
    "input_low_cut",
    "input_high_cut",
    "size",
    "diffusion",
    "decay",
    "reverb_high_cut",
    "reverb_low_cut",
    "modulation_rate",
    "modulation_shape",
    "modulation_depth",
    "freeze",
    "clear",
    "freeze_toggle",
    "nr_17",
    "dry_cv_depth",
    "wet_cv_depth",
    "input_low_cut_cv",
    "input_high_cut_cv",
    "size_cv",
    "diffusion_cv",
    "decay_cv",
    "reverb_high_cut_cv",
    "reverb_low_cut_cv",
    "mod_speed_cv",
    "mod_shape_cv",
    "mod_depth_cv",
    "tuned_mode",
    "diffuse_input",
]
addr_seq_controls = [
    "stesp",
    "direction",
    "select_step",
    "step_1",
    "step_2",
    "step_3",
    "step_4",
    "step_5",
    "step_6",
    "step_7",
    "step_8",
]

Could also have been using the label function of midi_cat and reading the labels from the json.

Next step then saved this as preset and loaded the json in the python script, automatically generated midi cc’s and been writing it back. Then loading the preset in rack again. With this setup I have a proper mapping.

No the idea … I want to gey some mechanism, where I can address every module and its items. midi is always possible, but requires manual mapping all the time. My idea is more like how can we automate this.

One idea which I also posted in another thread is to have something like OSC … an publish subscribe like lightweight thing lik zeromq, or nanomsg nng. I also wouldn’t mind using mqtt for this and running a broker on my machine. But I’d rather prefere a highspeed low latency solution.

And now the question to @stoermelder or @Vortico

Is it possible to enumerate all existing modules in rack and get their parameter names? Like it already is done in midi-map or cat.

This way I would have a server module in rack that enumerates all the modules and I’d implement the client side in my python script (or other app like touch osc) where I then could address a module by its canonical name like

/plateau/<moduleId or instance number>/pre_delay

I had a reason remote code implemented with a sysex codec to be able to adress all modules in parallel within a script. But I don’t think we need that in rack, also it might would be nice. But for this solution I’d rather have some conventional setup, where I can discover the modules automatically (get a response from the module server with the available controller item names and then could autogenerate class in python. Then I get autocompletion and can easily script stuff like custom randomization, which I did in this example and assigned a midi send on the big button. The script receives the midi message and then executes the randomization over midi.

Please open a feature request on Rack for that (there will be an API for enumeration of all module in Rack v2).
It sounds like a really specialized request for your personal workflow. Will there anybody else using something like that?

My guess is: basically every workflow where you don’t map one small controller to an interface, but have a controller with a lot of controlls and want to control as many parameters as possible. Or put it simple: Less work setting up mappings: It basically this example with cvOSCcv from trowasoft but for every module you wan’t to control.

I could totally do it like this or with the manuall midi way and would have to have a lot of setup every time I create new patches, which is fine for me, but very tedious :slight_smile:

I also had a look at how midi-cat and mem are saving the stuff into the preset or song json. So it’s almost there.

This is how midi-cat json looks like:

 {
      "id": 91,
      "plugin": "Stoermelder-P1",
      "version": "1.7.1",
      "model": "MidiCat",
      "params": [],
      "rightModuleId": 97,
      "data": {
        "panelTheme": 1,
        "textScrolling": true,
        "mappingIndicatorHidden": true,
        "locked": false,
        "maps": [
          {
            "cc": 31,
            "ccMode": 0,
            "note": -1,
            "noteMode": 0,
            "moduleId": 60,
            "paramId": 0,
            "label": "",
            "midiOptions": 0
          },
          {
            "cc": 32,
            "ccMode": 0,
            "note": -1,
            "noteMode": 0,
            "moduleId": 60,
            "paramId": 1,
            "label": "",
            "midiOptions": 0
          },

And the mem with plateau stored:

  {
      "id": 97,
      "plugin": "Stoermelder-P1",
      "version": "1.7.1",
      "model": "MidiCatEx",
      "params": [
        {
          "id": 0,
          "value": 0.0
        }
      ],
      "leftModuleId": 91,
      "data": {
        "panelTheme": 1,
        "midiMap": [
          {
            "pluginSlug": "Bogaudio",
            "moduleSlug": "Bogaudio-AddrSeq",
            "pluginName": "Bogaudio",
            "moduleName": "ADDR-SEQ",
            "paramMap": [
              {
                "paramId": 0,
                "cc": 31,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 1,
                "cc": 32,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 2,
                "cc": 33,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 3,
                "cc": 34,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 4,
                "cc": 35,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 5,
                "cc": 36,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 6,
                "cc": 37,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 7,
                "cc": 38,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 8,
                "cc": 39,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 10,
                "cc": 41,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              }
            ]
          },
          {
            "pluginSlug": "Valley",
            "moduleSlug": "Plateau",
            "pluginName": "Valley",
            "moduleName": "Plateau",
            "paramMap": [
              {
                "paramId": 0,
                "cc": 0,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },
              {
                "paramId": 1,
                "cc": 1,
                "ccMode": 0,
                "note": -1,
                "noteMode": 0,
                "label": "",
                "midiOptions": 0
              },

So almost everything is there. Could you additionally store the default label which is shown as mapping text.

re midi-cat feedback value: i mentioned that and the link to the iversion approach as i thought that it was solved there quite nicely in the context menu and doing it in a simlar way would maybe make both approaches kind of consistent to each other - but of course if you find or like another approach better this would be fine too

What is the reason why the midi-cat is limited to 128 inputs. Midi related? Or performance? Could there be an option to extend? like 256, 512, 1024, 2048. This would help me tremendously with my workflow. I could just create a custom build and test it, but if it makes sense this might be an option for others as well.

Another Idea I got for strip from the python scripting I’m doing. Let the user select the paramters he wants to randomize or he want’s to exclude from randomization. I’m doing this in my scripting stuff with 2 regex expressions.

Or another Idea for randomization. You have code in MIDI-CAT to select a module and map all of it’s parameters. How about a new module (possible Name: RAND) which has the paramter selection/mapping features from MIDI-CAT but doesn’t map to midi, but just creates a list of parameters (randomize and exclude from randomization lists).

I really used the strip randomize feature a lot, but more often I just wanted to exclude some parameters, or just have a bunch from different modules randomized which are spread across the path. (That’s why I creted the pthon script_

One midi channel is limited to 128 cc parameters. If you wanna more just add another midi cat!

I guess this can be relaxed or expanded with V2 when sysex arrives.

Never the less. I made good progress today. Maybe this is something ben likes to integrate in one of his modules.

With the setup of midi-cat and T7-Ctrl I can now auto generate mappings for defined modules and than randomize and random patch outputs to inputs. There is still work left … but with this setup one could define a ruleset of which inputs are alllowed to be pathed to outputs (for restrictions and getting useable results).

I’m pretty sure this will yield some nice experiements and sounds. As soon as it’s poished I’l create a separate thread and release the code for the people interested in this kind of stuff. In the meantime, maybe there is some idea from this setup you like to pickup

this is already possible with the in/ex button and the right click menue, or do you think of something different?

1 Like

Totally did not see that. My bad. Would be nice to toggle the button somehow for excluding more than one in a row or be able to include one again after excluding

1 Like