greatest-ape / OctaSine

Frequency modulation synthesizer plugin (VST2, CLAP). Runs on macOS, Windows and Linux.
https://www.octasine.com/
GNU Affero General Public License v3.0
649 stars 18 forks source link

Microtuning? #12

Open unremarkablegarden opened 3 years ago

unremarkablegarden commented 3 years ago

Would it be possible to implement support for microtuning?

alcomposer commented 3 years ago

May I suggest to go straight to scala, possibly using a library like: https://github.com/surge-synthesizer/tuning-library

greatest-ape commented 3 years ago

Yes, that's an interesting idea. I think going for scl/kbm support immediately is a great idea.

A few thoughts:

Some links that could be useful:

Woyten commented 3 years ago

I wrote a library with full SCL and KBM support. Please try it out: https://github.com/Woyten/tune 😃

Woyten commented 3 years ago

@alcomposer If you are interested in Rust microtunings in general, have a look at my list: https://github.com/Woyten/microtonal-rust

alcomposer commented 3 years ago

It's best to target all use case at once with scala.

Which means:

And possibly link both tuning variables, (the one in settings & in main interface).

Alternatively, have all three values on main interface? However I think for 99.9% of the time, having all tuning in a settings dialog would suffice.

We also need to think about publishing the Pitch Bend value (up/down). With the Surge scala lib pitch bend is integrated at a very low level.

Andreya-Autumn commented 1 year ago

Hello! :)

Yes, you'll need an import option, that's true. But I don't know if you need to save tunings with patches. By far the most common use case is to have patches tuning-agnostic and load tunings specifically per project. So I'd say best practice is to make tuning independent from patch loading. Imagine trying out different patches, and having to load the scl for the current project over and over? Not good. :)

You could make it optional to save tunings with patches, for the occassional patch that's been designed around a particular tuning. That is a thing, so it's a nice thing to do. Surge does that. But it's not first order of business.

A master pitch param can certainly be combined with the Surge tuning library. Binding to frequencyForMidiNoteScaledBy0 and making your master pitch create offsets from there seems like the right idea. Keep in mind that Scl/kbm files can change both the reference frequency and which MIDI note the reference frequency refers to. So you can't assume MIDI note 69 = 440 as your fixed point of reference. Looking at master_frequency.rs, it seems like you do, so I believe some refactoring might be necessary to make it work.

You could look at how it's implemented in Surge, but there's some additional complexity there since we allow the user to choose between two pitch modulation behaviors, and also all our pitch sliders have an absolute mode (for linear Hz offsets instead of log2 hz offsets), which makes things a lot more involved: https://github.com/surge-synthesizer/surge/blob/19dec72044b6858db52d8c838b547a05c20d4e7f/src/common/dsp/SurgeVoice.h#L111

You're very welcome to come by the Surge Synth Team Discord server, to bounce questions like these around. The folks who authored the tuning library (not me btw!) are around and are usually always happy to help with implementation.

The library should compile/link just fine cross-platform, that's important to us. And there's at least one resident Rust afficionado aboard who could maybe advice on that front.

Andreya-Autumn commented 1 year ago

Another comment, on the pitch bend thing. One reason that's implemented so deeply in the tuning lib is that when you use alternate tunings, there's two different ways you can calculate pitch modulation, which have very different user-facing results.

Basically either you do (ref freqkey) + modulation, or you do ref freq(key + modulation). In the first case, pitch bend of 2 always bends a standard equal-tempered whole tone. In the second, pitch bend of 2 always bends two steps up in the current scale.

In the standard 12-equal case that makes no difference. But if you load an unequal scale, you get different pitch bend amounts depending on which note you played. And if you load, for example, 34 equal divisions of an octave, a pitch bend of two is much smaller than in 12-equal.

Both behaviors are good for different things. Pitch bends always landing on an in-scale note is sweet, but if you have two oscillators tuned a fifth apart, you'll want that relationship to be the same for every note.

And it can affect parameters too, not just modulation. In Surge, if you choose the in-scale behavior, setting oscillator pitch to +1 always sets it to one step up in the selected scale no matter how big or small that step is.

If you want to settle for one of the two, I think it's fair to say (ref freq*key) + modulation is the safer bet. Especially if you UI lets the user set pitch bend amounts in cents or some other fine value.

Andreya-Autumn commented 1 year ago

I also have to mention, there is a newer approach to microtuning software which is found here: https://github.com/ODDSound/MTS-ESP

Implementation works similarly to the Surge Tuning Library. It's minimal and cross-platform and liberally licenced.

The difference is, the older scl/kbm workflow is per-instance and fixed. This means you have to load tuning files into each instrument in a project, and the tuning effectively can't change during playback.

MTS-ESP is global and dynamic, all compatible instruments connect to a single source behind the scenes and automatically retune themselves as specified by the source. This saves us the repetitive work of loading scale files over and over. And also, the tuning can change during playback, which is enormously useful for many microtuning scenarios.

It is my opinion that MTS-ESP is the superior solution. Its main weakness is in the (relatively niche) use case of loading different tunings at the same time in different instruments. However, many people used scl/kbm for years and built up their workflow around that, and so still prefer it.

Devs who implements both of these get the biggest kudos from the community generally.

Again, feel free to drop by the Surge server if you like. :)

unremarkablegarden commented 1 year ago

I wrote this Python script to convert Scala to MIDI sysex... If that is at all partially useful. Has functions for parsing Scala and converting to frequencies etc. https://github.com/unremarkablegarden/scala2mts

greatest-ape commented 1 year ago

Thanks, that’s a lot of useful info :-)