Skip to content

Implement Unison Mode using multiple additional TGs #892

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

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft

Conversation

probonopd
Copy link
Owner

@probonopd probonopd commented Apr 27, 2025

Closes #315

This needs intensive review and testing, since it heavily modifies the sound path.

MiniDexed
├── TGn
│   ├── Unison
│   │   ├── Voices
│   │   ├── Detune
│   │   └── Spread

In performances, there is for each TGn

UnisonVoicesn=3
UnisonDetunen=10
UnisonSpreadn=25

Note that the meaning of the numbers in UnisonDetunen and UnisonSpreadn is still subject to change.


Theory of operation

Each logical TG can play multiple, slightly detuned and panned copies of the same sound using auxiliary TGs. All parameter changes are kept in sync across the unison group, and detune/spread are applied per voice for a rich, wide sound.

Unison Voices:
"Unison voices" refers to the number of simultaneous, slightly detuned and panned copies of a sound (voice) that are played together for a richer, thicker effect. In MiniDexed, you can set the number of unison voices per Tone Generator (TG).

Tone Generators (TGs) and Auxiliary TGs:
A TG (Tone Generator) is an independent instance of the synthesizer engine. The main TG is the logical TG you interact with directly. "Auxiliary TGs" are additional physical TGs assigned to the same logical TG to realize unison. For example, if you set 3 unison voices for TG1, then TG1 uses itself plus 2 auxiliary TGs to play three slightly different versions of the same note.

Unison Group:
The "unison group" is the set of all physical TGs (main + auxiliaries) that together implement unison for a given logical TG.

Parameter Propagation:
When you change a parameter (such as voice/patch, mono mode, filter, etc.) on the main TG, the same change is immediately propagated to all auxiliary TGs in the unison group. This ensures all unison voices sound identical in terms of patch and settings.

Unison Detune and Spread:
Detune: Each unison voice is given a slightly different tuning (pitch offset) based on its position in the group, controlled by the Unison Detune parameter. This creates a chorus-like effect.
Spread: Each unison voice is also given a different stereo pan position, controlled by the Unison Spread parameter, for a wider stereo image.

How It Works on Note Events and Voice Changes:
On every note-on (keydown) and note-off (keyup), the system calculates and applies the correct detune and pan for each TG in the unison group, so each voice is offset appropriately.
When you change the voice/patch or other relevant parameters, the new settings are immediately copied to all auxiliary TGs, and their detune/pan offsets are also updated.

--

To be tested:

  • Reverb and other FX

Note that this doesn't increase the total number of TGs, the other ones will just stay silent.
Menu integration still to be improved.
@probonopd probonopd marked this pull request as draft April 27, 2025 14:30
@probonopd probonopd changed the title Implement Unison Mode Implement Unison Mode using multiple additional TGs Apr 30, 2025
@probonopd probonopd mentioned this pull request Apr 30, 2025
@probonopd probonopd marked this pull request as ready for review May 1, 2025 20:19
Repository owner deleted a comment from github-actions bot May 1, 2025
@probonopd
Copy link
Owner Author

Can't wait to see what @Banana71 thinks about this. (Note: The meaning of the numbers in UnisonDetuneN and UnisonSpreadN is still subject to change, so don't create performances using this feature yet.)

Repository owner deleted a comment from github-actions bot May 1, 2025
Repository owner deleted a comment from github-actions bot May 3, 2025
Copy link

github-actions bot commented May 3, 2025

Build for testing:
MiniDexed_1165_2025-05-03-e3d68db_32bit
MiniDexed_1165_2025-05-03-e3d68db_64bit
Use at your own risk.

Repository owner deleted a comment from github-actions bot May 3, 2025
Repository owner deleted a comment from github-actions bot May 3, 2025
Repository owner deleted a comment from github-actions bot May 3, 2025
Repository owner deleted a comment from github-actions bot May 3, 2025
@probonopd
Copy link
Owner Author

Something is still wrong, we are not really getting 32 physical tone generators. So for one TG (e.g., if there is only TG1 in the performance) it seems to work well, but if there are also TG2...TG8 in use then things get mixed up. I am looking into it.

Copy link

github-actions bot commented May 3, 2025

Build for testing:
MiniDexed_1168_2025-05-03-47ce749_32bit
MiniDexed_1168_2025-05-03-47ce749_64bit
Use at your own risk.

Copy link

github-actions bot commented May 3, 2025

Build for testing:
MiniDexed_1169_2025-05-03-310298d_32bit
MiniDexed_1169_2025-05-03-310298d_64bit
Use at your own risk.

Copy link

github-actions bot commented May 3, 2025

Build for testing:
MiniDexed_1171_2025-05-03-3a6ba6b_32bit
MiniDexed_1171_2025-05-03-3a6ba6b_64bit
Use at your own risk.

@probonopd
Copy link
Owner Author

probonopd commented May 3, 2025

So for now it works but only of you use only, e.g., TG1 from the menu and not use all others..

The issue is that not enough TGs get created at boot time (e.g., for 8 logical TGs, 32 physical ones need to get created, because we can have up to 4 voices per logical TG).

@soyersoyer maybe you could have a look how we can increase the number of TGs that get initialized by a factor of 4 (because each logical TG can now have up to 4 voices)? I can't seem to get this right and run into assertion errors related of the number of TGs when I try to initialize more.

(It is possible that we may start to run out of processing power, but at that point in time we could reduce the amount of polyphony per voice.)

@Banana71
Copy link

Banana71 commented May 3, 2025

Unison would be a great feature.
I don't quite understand how you're trying to solve it.
Is this how it's supposed to work?:
If TG1 Unison Voices is set to 4, TG1 and 3 new sub TGs are set to the same value (VoiceData, Volume, ReverbSend, etc.), while all other TGs remain TG2 to TG8. Detune and PAN are set according to the Detune and Spread parameters.
Is the number of polyphony of 16 then divided by the number of set unison/voices?

What else I noticed: In Main and this version, the Performance Bank name is no longer transferred from the folder name. (001_default - 001-Seed)

@Banana71
Copy link

Banana71 commented May 3, 2025

Shouldn't the voice data for the sub-TGs be removed from the performance? It seems to me that the sub-TGs get their data from the SD card (voice number and bank number)?

@Banana71
Copy link

Banana71 commented May 3, 2025

Detune seems far too strong; the gradations should be much finer.

@probonopd
Copy link
Owner Author

probonopd commented May 3, 2025

This is how it is supposed to work:

For each TG visible to the user ("logical TGs"), 4 physical TGs get created. Depending on how many voices the user chooses to use, one to four are used when the logical TG plays notes.

When the user changes any aspect of TG1, TG2... TG8 shall be unchanged. (The code currently doesn't do this properly because we run out of TGs.)

the number of polyphony of 16 then divided by the number of set unison/voices?

We may need to do this in order not to overload the CPU, but so far it is not done yet because we are not even creating enough (32 physical TGs for 8 logical TGs) to begin with. Once that works, we may need to start reducing the polyphony.

In the performance, for each logical TG, the voice data should be saved (like it always was). But also the union related parameters (number of voices, unison detune, and unison spread) shall get saved.

Shouldn't the voice data for the sub-TGs be removed from the performance?

In the performance, anything should only be saved for the logical TGs, not for the additional physical TGs. (The data from the logical/main TG is always copied to the other three, and then detune and panning is applied to them.)

@Banana71
Copy link

Banana71 commented May 3, 2025

The sub-TGs are visible; I can see 32 TGs.
My thoughts:
My Raspberry Pi 3 Model A is overloaded if I activate Unison twice. To avoid overloading the Raspberry Pi, perhaps you should limit the polyphony and/or the maximum number of sub-TGs. For example, for drum sounds, Unison-Voices=2 with a maximum polyphony of 4 voices would be sufficient.

@probonopd
Copy link
Owner Author

probonopd commented May 4, 2025

The sub-TGs are visible; I can see 32 TGs.

Ah, in the saved performances. That is a bug. The saved performances should only show the logical TGs. The auxiliary ones get their config from the logical ones.

I don't think all 32 TGs are really being created, and this is why we are seeing some strange effects going on as soon as one enables more than just TG1. In other words, it seems to behave like I envisioned it if I set MIDI channel OFF for TG2-TG8 for now. I think we need to create the correct number (32) of physical TGs for TG2-TG8 to also work. Once that is done, we can start to think about limiting CPU load.

@Banana71
Copy link

Banana71 commented May 4, 2025

I can actually see the 32 TGs in the miniDexed UI; I took a photo of it yesterday.
32TGs

RPi 3 Model A

@probonopd
Copy link
Owner Author

probonopd commented May 4, 2025

Argh. I think the whole codebase mixes up logical TGs (what the user sees), auxiliary TGs (used if there is more than one voice in unsion), and physical TGs (meaning both logical and auxiliary TGs). For example, currently when saving performances, all physical TGs get saved whereas only the logical ones should. I will look into it.

This is how I envision it:

  • Logical TG: User-facing TG, corresponds to a part/layer visible to the user and saved/loaded in performances.
  • Auxiliary TG: Used internally for unison/voice stacking, not user-facing, not saved/loaded in performances.
  • Physical TG: Any TG instance (logical or auxiliary); all voice allocation and synthesis uses physical TGs.

Only logical TGs should be exposed to the user and persisted in performance files.

There shall be a static mapping: physicalTG = logicalTG * maxUnisonVoices + unisonVoice, with maxUnisonVoices defined as a constant (currently 4).

The Logical TGs, when shown to the user (or saved in the performances), should be numbered 1-8 (if we have 8 logical TGs). Even though they are physical TG 0, 4, 8, 12, 16, 20, 24, 32.

@probonopd probonopd marked this pull request as draft May 4, 2025 09:32
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

Successfully merging this pull request may close these issues.

Unison mode
2 participants