Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polyphonic cables #1118

Closed
AndrewBelt opened this Issue Oct 9, 2018 · 31 comments

Comments

Projects
None yet
@AndrewBelt
Copy link
Member

AndrewBelt commented Oct 9, 2018

Could allow up to 16 or so channels of voltage through a single cable. Is this a good maximum?

Could be implemented without API breakage with this pseudocode.

struct Input {
	union {
		float value;
		float values[MAX_CHANNELS];
	};
	int numChannels = 1;
	...
};

Modules that don't support poly will just use the first value (value and values[0] are the same thing). Modules that do can read/write to inputs/outputs. numChannels is writable for Outputs and can be 0 to MAX_CHANNELS. However, a sane value (e.g. 0.f) should be written to values[0] if there are zero channels, for modules that do not support poly cables.

Port lights could appear in a unique color (blue) to specify polyphonic activity. Something like sum(value[i]**2, i) would be its value.

Users might be confused which modules support it. It could simply be added as a tag (POLY_TAG) which will appear in the Module Browser.

@Rcomian

This comment has been minimized.

Copy link

Rcomian commented Oct 9, 2018

does numInputs refer to the number of inputs actually connected or to the number of inputs supported over that wire by the module?

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Oct 9, 2018

@Rcomian The maximum number of channels in a cable is MAX_CHANNELS (in my example) which is equal to 16. numChannels is the number of values that are defined by the Output. This can change every frame.

@VCVRack VCVRack deleted a comment from gbrandt1 Oct 9, 2018

@VCVRack VCVRack deleted a comment from WiggyWire Oct 9, 2018

@Rcomian

This comment has been minimized.

Copy link

Rcomian commented Oct 9, 2018

As a module writing outputs, how do I inform the downstream module that only 6 values are relevant? As a module writing outputs, do I assume that any downstream module will accept all 16 values if I want to send them? Or can I tell how many voices the downstream module can accept?

For example, If I'm using 16 generators to create outputs, but the downstream module is only 8 voice poly, do I write all 16 values anyway? If I knew limitations of the downstream module I could close some generators.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Oct 9, 2018

As a module writing outputs, how do I inform the downstream module that only 6 values are relevant?

Set outputs[...].numChannels = 6;

As a module writing outputs, do I assume that any downstream module will accept all 16 values if I want to send them?

The input module might not support poly, so it would only use the first value. But generally if a module claims to support poly cables, it should support up to 16. I can't think of a good reason for it to support less.

Most plugin developers would do something like this.

int n = inputs[PITCH_INPUT].numChannels;

outputs[SINE_OUTPUT].numChannels = n;
for (int i = 0; i < n; i++) {
	// Process voice `i`
	outputs[SINE_OUTPUT].values[i] = oscillators[i].process(inputs[PITCH_INPUT].values[i]);
}

The oscillators array would simply be length MAX_CHANNELS.

@cmountvpr

This comment has been minimized.

Copy link

cmountvpr commented Oct 9, 2018

I think the Poly capability should be indicated visually on the module itself, to help keep track of what's what while working on the patch, particularly if not all modules are Poly capable. Perhaps a coloured box with the letter "P" in a corner of the module?

@caillou

This comment has been minimized.

Copy link

caillou commented Oct 9, 2018

I love the idea from a technical standpoint. One cable is better than 4, obviously.

But I can't help wondering how a simple and intuitive UI would inform the user about that situation.

Additionally, we would need multiplexing and de-multiplexing modules, to go from n-patch-cables to mono-patch-cables.

@AndrewBelt What was the exact use-case you had in mind? If the problem you are trying to solve is too many cables overlapping modules, this could also be solved by a core module that functions as a CV Bus. You might remember the idea from a recent facebook conversation.

42821470_10154897995222465_426001665603141632_o

@MazeOfEncryption

This comment has been minimized.

Copy link

MazeOfEncryption commented Oct 16, 2018

I think the issue with a CV bus is that you're going to need some sort of multi-channel system unless you only want one bus. I think a polyphonic cable could be implemented nicely if it were a different style - perhaps thicker, or a different color, or maybe a texture of some sort. This would clearly distinguish it as being polyphonic, and would be much more intuitive, IMO. This also opens up the possibility for modules accepting polyphonic cables as inputs or outputting them. Imagine this - I polyphonic VCO, so it can play up to 8 pitches at once - take that output, put it through a polyphonic VCF, VCA, ADSR, etc.

Now, to achieve this, you can cycle through each channel, and run that channel as a cv through the module. I suggest adding an official "polyphonic" api in the plugin api, which would allow modules to store variables which change each step as an object, perhaps Rack::PolyFloat - This way, the engine can handle polyphony, so that if there are ever updates to it (i.e. more channels), modules won't break if they just used arrays or similar methods of computing polyphony. I also suggest adding Rack::POLY_CHANNELS as a constant for the number of channels, if plugins do decide to implement polyphony themselves.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Oct 16, 2018

I opted against two different types of cables because it would be confusing which input and output ports can be used. The best way is certainly to extend normal cables to support an array of values.

@MazeOfEncryption

This comment has been minimized.

Copy link

MazeOfEncryption commented Oct 16, 2018

I personally disagree - If you were to implement a different port style, perhaps larger and with a different shape, it's pretty easy to distinguish between the 2. I think that one cable supporting polyphony is more confusing, as you then have to handle users attempting to plug polyphonic cables into non-polyphonic inputs. If you were to just take the first channel, there would be a lot of confusion as to what happened to the other channels. One potential solution to this is manually forcing polyphony among plugins - this works great for a VCF connected to nothing but the polyphonic cable... However, as soon as you get into any modules which store data between steps, you would need to run a separate instance of the plugin per every channel per every polyphonic cable. This overloads the engine pretty quickly. If you do just go with taking the single channel, I still do recommend some sort of visual distinction so that users know it is a polyphonic cable. The ability for rack to handle polyphony itself with Rack::PolyFloat etc. would be nice for new plugin devs - however, if you are planning on having devs manually implement it with arrays, or if a dev just wants to use arrays for a more advanced polyphonic plugin, I would still recommend some basic api just to let rack know that the module is, indeed, polyphonic.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Oct 16, 2018

If you were to just take the first channel, there would be a lot of confusion as to what happened to the other channels

I don't think so. If you plug a cable into a VCF and the input plug has a blue LED and the output plug has a red/green/orange LED, it's pretty clear that the VCF doesn't support polyphony.

manually forcing polyphony

Not doing that, out of the question.

I think I'll pass on these suggestions, but thanks.

@MazeOfEncryption

This comment has been minimized.

Copy link

MazeOfEncryption commented Oct 16, 2018

Fair enough - Thank you for taking my suggestions into consideration! I still think polyphony will be awesome.

@cmountvpr

This comment has been minimized.

Copy link

cmountvpr commented Oct 16, 2018

Does polyphony create a linear proportional increase in DSP requirements? Two voices = 2x DSP of One Voice?

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Oct 16, 2018

That depends on what the plugin step() method does.

@graehu

This comment has been minimized.

Copy link

graehu commented Nov 12, 2018

If you are planning to extend the normal cable functionality, does that mean you would allow multiple input cables?

An example being a module that supports poly on an input allowing connections from several different VCOs?

I think the feature would be Cool, given it's not abused by module makers. :p

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Nov 13, 2018

@graehu Are you asking if multiple outputs can be merged into a single cable? You'd need a module to do that, which accepts for example, two inputs and copies the first 8 channels to output channels 1-8 and the second input's first 8 channels to 9-16.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Nov 13, 2018

The status of this issue is to wait on performance results. If real-world monophonic patches use less than 10% more CPU consumption, it will be approved.

@graehu

This comment has been minimized.

Copy link

graehu commented Nov 13, 2018

@AndrewBelt I think you've answered my question but I'll restate it for verbosity. Right now Rack allows you to have multiple cables attached to any output. So I could have one LFO send CV to as many other modules as I want.

I'm talking about the opposite of that. Many different outputs sending to one input. To be clear I meant without an intermediate module routing all the channels into a single cable. But rather lots of cables into one pot.

Based on your reply, that's not something Rack would support out of the box. Unless I've misunderstood. :)

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Nov 13, 2018

Right, one cable per input still.

@korfuri

This comment has been minimized.

Copy link

korfuri commented Nov 16, 2018

Port lights aren't really standard, many modules don't use them, so this may not be enough to inform users. Could we still distinguish the cables visually at render time? Either thickness, color scheme, or possibly something like striped cables (poly) vs solid-colored cables (mono). I.e. if a cable links a poly-compatible module to another, it's rendered as a poly cable (event if it turns out that num_values will be 1, the cable can't know that until it transmits data). If either module only supports monophony, the cable is rendered as a mono cable.

That's still only really 1 type of cable - I click on any input, drag to any output, the same data structures are created and I don't have to pick as a user, but there's a visual cue on what's happening in that cable.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Nov 16, 2018

@korfuri Port lights are owned by the cables. They're not part of the module. You're thinking about LEDs some developers put on the panel to copy the port value.

Thick cables is the best option, and I will likely do that, but note that num_values can change per-sample, so the GUI might appear too weirdly dynamic if the size is rapidly changing.

@korfuri

This comment has been minimized.

Copy link

korfuri commented Nov 16, 2018

Ah, right, I was thinking of the LEDs that sometimes go near the ports. The cable lights are a great solution. 👍

@NothanUmber

This comment has been minimized.

Copy link

NothanUmber commented Dec 1, 2018

Great idea!
It might make sense to think about going to 48 (or 64) for MAX_CHANNELS. Rationale: Most multi-expression-per-finger instruments have three control dimensions per finger nowadays. For 16 midi channels this would make 3*16=48 channels. (Additionally some support one or two expression pedals, breath and one or two ribbons. So let's say about five more mono channels. 53 rounded up to the next 2^x member would be 64.)
That way we could build group and ungroup modules that e.g. combine and split xyz modulation on/from a single cable and modules that just have an "xyz" input/output.
It's a trade-off - presumably for the 90%+ case 16 voice cables are sufficient, so we would in most cases allocate more memory than necessary.

In any case: Allowing 16(+) channels per cable would be a big step into allowing to write poly modules for VCV!

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Dec 1, 2018

@NothanUmber MAX_CHANNELS is the number of voices, not total number of parameters. If your controller generates X, Y, and Z, you'd use three polyphonic cables for that.

@NothanUmber

This comment has been minimized.

Copy link

NothanUmber commented Dec 1, 2018

@AndrewBelt yepp, understood. Was thinking along the lines of "group/event busses" like you have e.g. in Max or Reaktor where you can further tidy up stuff that is often used together.
But you are right, some of the immediacy might get lost, having separate cables for poly x, y, z and mono pedal1, pedal2, ribbon1, ribbon2 and breath (as we have it e.g. for Eigenharps) is by leaps and bounds better than having to fiddle around with half-a-hundred cables per module :)

So yeah, 16 voices per cable should do the trick, thanks in advance for considering that!

@NRG70

This comment has been minimized.

Copy link

NRG70 commented Dec 5, 2018

i am for polyphonic connections, il'd also be happy with 8 voices.

@andrewbest

This comment has been minimized.

Copy link

andrewbest commented Dec 8, 2018

Voices as in polyphony of a synth. Filter voices going into amp voices. Envelope voices going into filter and amp. But there also could be grouped control voltage busses for things other than polyphony. So it serves both purposes. Just wanted to clear up the fact that there are two separate conversations happening here.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Dec 31, 2018

Added polyphony to engine with f40d334

@NikolaiVChr

This comment has been minimized.

Copy link

NikolaiVChr commented Jan 4, 2019

When you hover over a port with a polyphonic cable attached and the cable highlights, I suggest a small number in the middle of the cable should appear that shows the number of channels.

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Jan 4, 2019

Performance testing has been positive. I am confident that the overhead of having 16 channels per port is less than a 1% reduction for modules that don't take advantage of it (monophonic modules).

@NothanUmber

This comment has been minimized.

Copy link

NothanUmber commented Jan 4, 2019

Awesome! Thanks for the early new year's gift!
Now hopefully a lot of devs will want to adapt their modules to poly. Fortunately quite a number of stuff is open source, so poly enthusiastic fans of modules coming from dedicated mono purists often have a second option.... :)

@AndrewBelt AndrewBelt referenced this issue Jan 7, 2019

Closed

Poly Mode #1084

@AndrewBelt

This comment has been minimized.

Copy link
Member Author

AndrewBelt commented Jan 25, 2019

Finished polyphony in 6d86a82. I ask anyone interested in adding polyphony to their plugins to take a look at the new Port methods in https://github.com/VCVRack/Rack/blob/v1/include/engine/Port.hpp#L32-L68.

@AndrewBelt AndrewBelt closed this Jan 25, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.