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

Interaction of voices and oscillators #191

Open
dpwe opened this issue Aug 27, 2024 · 8 comments
Open

Interaction of voices and oscillators #191

dpwe opened this issue Aug 27, 2024 · 8 comments

Comments

@dpwe
Copy link
Collaborator

dpwe commented Aug 27, 2024

Oscillators are the fundamental resource in Amy, each able to produce a waveform with modulation etc.

Voices are a higher-level construct grouping multiple oscillators. Musical notes often use multiple oscillators in co-ordination (FM voices are a clear example), and the AMY voices/patch_num mechanism makes this convenient.

However, each voice allocated grabs a number of oscs, and the user can't tell which ones (for one thing, the wire protocol is write-only). So it's very hard to mix the use of voices and oscs, because the oscs you're referencing might be in use by one of your voices.

For scenarios that might want to mix using voices with raw oscs, we could provide a mechanism that protected some subset of the oscs from being used by the voices mechanism. For instance, you could reserve the first 20 oscs for direct use, so that the voices allocator only uses oscs 20 and above.

We could introduce a new command to reserve oscs. Or, we could figure out some mechanism within the existing wire protocol. For instance, a negative voice number, as the first reference to voices after a reset, could reserve the oscillators:

amy.send(voices=-1, osc=20)  # Reserve oscs 0..19 for use outside of the voices system.
@dpwe
Copy link
Collaborator Author

dpwe commented Sep 12, 2024

Brian's idea is to use the voices system to allocate a voice that holds down the needed oscs. For instance, to reserve 4 voices you can do something like amy.send(voices=NEW_NUMBER, store_patch="v0Zv1Zv2Zv3Z"). AMY will detect that the "patch" requires 4 oscs, and will allocate and hold those oscillators (somewhere). Then, to use those oscs, subsequent calls to amy.send() simply need to include voices=NEW_NUMBER.

The user is still required to manage the allocation of voices numbers. This is what midi.Synth.allocated_amy_voices does, but currently that's specifically oriented to the patch-based synths. We might want to separate out that manager so it can be used by SingleOscSynthBase and direct use of AMY oscs.

Inspecting debug output, this doesn't actually work at the moment (no oscs show up as allocated), but that's just bugs:

amy.reset()
amy.send(store_patch="1024,v0Zv1Zv2Zv3Z")
amy.send(voices="0", patch=1024)
amy.send(debug=99)

which gives the output that ends with:

osc_to_voice:
0: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
16: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
32: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
48: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
64: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
80: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
96: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
112: 255 255 255 255 255 255 255 255 
memory_patch 0 oscs 2 patch v0Zv1Zv2Zv3Z

We notice:

  • No oscs in the osc_to_voice table are allocated to any voices.
  • The stored memory_patch 0 (i.e., patch 1024) is detected as constituting only 2 voices.

@bwhitman
Copy link
Collaborator

Try it with load_patch, not patch?

bwhitman added a commit that referenced this issue Sep 12, 2024
bwhitman added a commit that referenced this issue Sep 12, 2024
fixing store patch bug found in #191
@bwhitman
Copy link
Collaborator

I fixed up the store_patch bug in #210 .

I now get the correct behavior, using load_patch in Tulip:

amy.reset()
amy.send(store_patch="1024,v0Zv1Zv2Zv3Z")
amy.send(voices=0, load_patch=1024)
amy.send(debug=99)

...

voice 0 base osc 60
osc_to_voice:
0: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
16: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
32: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
48: 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 
64: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
80: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
96: 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
112: 255 255 255 255 255 255 255 255 
memory_patch 0 oscs 4 patch v0Zv1Zv2Zv3Z

@Visuelle-Musik
Copy link

Visuelle-Musik commented Sep 12, 2024

Hi Brian, hi Dan, this concept of setting up oscillators for voices via patches looks like a great concept to me.
I am about to develop a kind of OSCar-inpired synth using AMY, so I guess this should work to set up let's say 32 partials per "macro-oscillator" for maybe 4-6 voices, correct?

Also I am assuming it could it also be possible to add a filter, lfos, envelopes and maybe even an FM-Algorithm this way too, to be associated with a certain voice? Or is it only meant for "real" oscillators and other building-blocks of voices still would have to be added differently? All the best, Mathias

@dpwe
Copy link
Collaborator Author

dpwe commented Sep 12, 2024

The wave=amy.PARTIALS functionality is related to this. However, to some extent PARTIALS is a throwback to an earlier time when oscs were grabbed without care .. the patches simply grab the next N oscs after the 'parent', where N is specified in the (compiled-in) partials table.

PARTIALS oscillators will respect pitch (including modulation) and filtering. However, it ignores amplitude envelope (although it does respect vel).

If we were to get serious about additive synthesis, this part of the engine would need some serious attention!

@Visuelle-Musik
Copy link

Visuelle-Musik commented Sep 12, 2024

Ah ok, could you maybe roughly point out how the "best practice" with the new voices/oscillators logic as proposed here would be to allocate lets say 4 voices with 32 partials each, two LFOs and a filter via a wire-protocol command and/or via native Amy events? TBH I understand the intentions, but did not grasp the whole thing entirely. Not having a Volume EG of course is a bit of an issue to build a real synth with these options... (I guess the Breakpoint-EG already is consumed by the partials?) But maybe to only use a Filter EG still could work!

@dpwe
Copy link
Collaborator Author

dpwe commented Sep 13, 2024

The current operation is that there is a parent osc with wave=amy.PARTIALS that retrieves a preset partials set, which specifies the total number of partials required. It then grabs the following N oscs and makes them type PARTIAL (no "S") to act as the modulated sinusoids. The parent osc updates the FM and AM env gen params of each partial as the sound evolves. Filter and pitch modulation of the parent osc end up applying to the sum of the partials; parent amplitude envelope does not, but that's an oversight we can fix.

However I'm unclear what you hope to do in terms of providing the partial coefficients. Right now they are analyzed from audio external to AMY with Loris, then compiled in to the AMY binary. This pretty limited - you probably don't want to have to recompile each time you change a voice. I could imagine code updating the tables at run time, but that's quite a lot of modification.

@Visuelle-Musik
Copy link

Visuelle-Musik commented Sep 13, 2024

Thanks for explaining those details about amy.PARTIALS! And yes, I think to have a parent amplitute enverlope would be great, especially if that would work without issues with the output from Loris. Or is the parent oscillator an additional one to what Loris generates from a sample anyways?

Concerning what my intention with additive synthesis would be: I was planning to have lets say four voices with 32 sine oscillators each (pitched according to the harmonic series) , not directly dependant on amy.PARTIALS / Loris. So it would be more kind of inspired by the AT&T Hal Alles Machine (Crumar GDS / DK Synergy II+), or the Kawai K5000 or the OSCar. For instance with the K5000 you can use "formant filters", which are actually macros applied on the (in that case 64) partial-tones. Or you can increase/decrease the volume of only odd or even partial-tones etc. As a first step I imagined to build one or two very simple oscillators similar to those that the OSCar supplies: only changing the volume of the overtones would be needed, no breakpoint-EGs as with the K5000 for a start. But then to add a Master-Filter and a Master Volume EG (in contrast to the aforementioned purely additive designs) would be needed to obtain a working synth I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants