Warp Core, a phase distortion oscillator, now available in the Library

Afraid so… I’m running into this myself with a module I’m working on. Here is the per-sample code that Rack executes. It has four steps; I’ve included the cable step and expander exchange steps below.

Conceptually, expanders are like an invisible “behind-the-panel” cable that allows arbitrary messages (not just poly voltages) to flow back and forth, but they have the same delay (which makes things easier for all sorts of Rack-engine-related reasons). It’d be really hard to come up with a generalizable way to let expanders merge with their parent modules and process simultaneously.

It just occurred to me, though, that since a parent module knows exactly when an expander is connected, it could sense that and start delaying its own outputs by one sample, meaning that the expander outputs and the parent outputs would stay sync’d. There would be a one-sample audio glitch to cover, but that’s probably fine. I’m going to experiment with this and will report back (in some other thread). I wonder if any of the expandable modules are doing this already?

static void Engine_stepFrame(Engine* that) {
	Engine::Internal* internal = that->internal;

	// Param smoothing
	// ...

	// Step cables
	for (Cable* cable : that->internal->cables) {
		Cable_step(cable);
	}

	// Flip messages for each module
	for (Module* module : that->internal->modules) {
		if (module->leftExpander.messageFlipRequested) {
			std::swap(module->leftExpander.producerMessage, module->leftExpander.consumerMessage);
			module->leftExpander.messageFlipRequested = false;
		}
		if (module->rightExpander.messageFlipRequested) {
			std::swap(module->rightExpander.producerMessage, module->rightExpander.consumerMessage);
			module->rightExpander.messageFlipRequested = false;
		}
	}

	// Step modules along with workers
	// ...

	internal->frame++;
}
1 Like

Oh wow! Good to know.

So I suppose when you have a chain of expanders, that each one introduces an additional 1 sample delay. Or do all the expanders logically link back directly to the root (non-expander) module, such that there is always only 1 sample delay? (I suppose this belongs in the development arena - sorry for the hijack)

(ducking under the OT caution tape)

Without some fairly sophisticated (and probably ill-advised) workarounds it would work like your first case: if the “expander expander” passes messages back to the (grand)parent in a bucket brigade using the standard .messageFlipRequested protocol, it would be 2 away from the main module, the “expander expander expander” would be 3, etc. You could do a handshake where you passed mutual weak references across the bucket brigade, stored them on both sides, and managed the exchange yourself to create a kind of spooky non-local coupling, but–let’s just say it would need serious bug-testing.

Sorry, @InfrasonicAudio, your announcement thread got a little… expanded :dark_sunglasses: :sunglasses:

2 Likes

This is in fact exactly what the (unreleased) Substation poly sequencer expander module does: on connection, a weak ref to the main module is negotiated, which allows each chained expander to access shared memory (and skip the cascading delay). You’re right that it took a lot of work to test all the edge cases!

5 Likes

Also, fantastic job on this module! Great sounds, and the details and diagrams in the manual are especially appreciated.

Really interesting stuff on expanders. Is there a developer reference for the APIs used for expander modules?

@slimechildaudio – that is awesome. Even in a sadly (though understandably) unreleased module, I am glad at least someone has pulled this off, and I am not surprised that it was you!

@InfrasonicAudio – here’s a (very!) recent code snippet and link to the API–

and here’s an older thread (some parts may be out of date, but a lot should still be valid):

AFAIK there’s no current developer reference, but there are 77 open-source expanders at time of writing, ranging from extremely simple to extremely not. I don’t know if any use a version of the weakref technique Coriander got working.

Some day I will publish it.

Until then, if you want an alternative, here’s a base class I made for expandable modules: dep/slime4rack/include/slime/engine/Expanders.hpp · main · Slime Child Audio / Substation for VCV Rack - Open Source · GitLab

6 Likes

Wonderful news.

Wonderful code! Thank you so much!

1 Like

First little jam with Warp Core …

5 Likes

The current version (2.0.0) has a pretty major bug in the external PM processing that I have fixed and will be including in an update this week. It was not processing full audio rate blocks of the EXT PM input signal, rather only one sample per block (and the rest zeros or bad data from the ring buffer). In addition to fixing this I’m also increasing the EXT PM amount range, i.e. the internal amplitude scaling of the signal to a phase offset, since it was very limited before.

I verified it sounds like I expect it to by (almost) recreating the Prodigy “Funky Shit” sound. Couldn’t get it to sound exactly the same since unlike the Korg Prophecy, Warp Core doesn’t have a true pulse wave carrier oscillator, only sine. At least for now :thinking:

10 Likes

Version 2.0.1 Available

Version 2.0.1 of Warp Core has been released to the VCV Rack library

Changes

  • [FIXED] Default tuning is C4 per VCV Rack voltage spec
  • [FIXED] Full module initialization properly resets everything to its initial state
  • [FIXED] EXT PM input was not being processed correctly at audio rate
  • [FIXED] EXT PM input was not being processed correctly in polyphonic patches
  • [CHANGED] EXT PM input scaling is now 10x what it was before, i.e. 10V at the EXT PM input results in a modulation index of 10 instead of 1

Full Changelog

Notes

Due to the EXT PM scaling change, patches that were previously using EXT PM will not sound the same. The tenfold increase in how the voltage at that input maps to phase modulation index means that you will need to attenuate voltages going into that input by 10x to achieve the same result. The benefit is that now you can use EXT PM to apply a much greater phase modulation index to the sound, resulting in richer and more varied timbres.

12 Likes

I have been working on a hardware version of Warp Core to hopefully start shipping later this year. It builds upon (and improves, I think) the VCV design.

Once this is done and shipping I am likely going to come back and update Warp Core to be more of a true hardware clone, in a backward-compatible way.

Any opinions from the community on that? I’m not sure too many developers have gone this direction - VCV first, then a hardware module. Would it make more sense to release the back-ported hardware clone VCV version as a new module, “Warp Core 1.1” or something? Or just update it in-place and be sure to preserve backward compatibility for loading patch settings?

10 Likes

This is very cool! My vision of the future (maybe a year or two down the road) also includes developing hardware modules at some point. I would love to ask you more questions about how you approach this, what kind of microcontroller you use, how do you do the board layout, etc.

But first, to address your question, I think one consideration would be to avoid disrupting existing patches that people make using your current version of Warp Core. Existing patches should continue to work exactly the same even after they update Warp Core. Maybe that’s what you mean when you mention preserving backward compatibility.

It may be worth keeping Warp Core as-is (“classic”) and have a separate module with the new behaviors. Or have one module with different modes. Or best of all, keeping a single module, but with newer behaviors that default to be identical to the existing software module.

I’m not sure I’m adding anything particularly useful to the conversation, but I hope it helps.

Thanks! Yes that’s exactly what I mean by “backward compatible”. I don’t want anyone’s patches to ever “break” due to an update. I would consider that rule #1 for updates. I also like the idea of keeping a “classic” version and creating a more faithful hardware clone down the line.

As far as hardware details… currently I am just using the Daisy Patch Submodule which takes care of all the actual “difficult” hardware stuff and has a reliable supply chain. It was not hard at all to design the control board around it. Considering the capabilities and cost ($40/each, minus volume discounts for larger quantities) it’s pretty hard to beat as the backbone of a digital module with stereo I/O, in my opinion. Considering labor and opportunity costs for the extra electronics design overhead, parts sourcing, bootstrapping, etc that a completely custom digital module design would entail I can’t imagine I could replicate what it offers for much cheaper than that, at least not at my current skill level with electronics design.

1 Like

The Daisy stuff looks very interesting. I wonder if it could handle the floating point calculation nightmare of my Elastika module, LOL. I figured I might have to resort to using an FPGA to make that work on a small board with low power. Which means I would have to learn FPGA design!

1 Like

That would be the route I’d go I reckon. Totally appreciate not wanting to break backwards compatibility, but equally you don’t want to add ‘restrictions’ to the new hardware clone by doing that - so having 2 versions seems like it might be the best option. The Vult Freak filter takes a similar approach - there’s an original VCV version and a newer version that more closely mimics the actual hardware.

I’m sure I’m not alone in dreaming of a hardware rack with Warp Core and Elastika in it. Thanks for the vid and wishing you guys all the best in porting your modules from software to hardware. It’s exciting to think about more people forging the path in that direction.

4 Likes