korginc / logue-sdk

This repository contains all the files and tools needed to build custom oscillators and effects for the prologue synthesizer.
BSD 3-Clause "New" or "Revised" License
833 stars 305 forks source link

nTS1 mkii - Make unit_note_on() behaviour the same for both legato modes. #111

Open AndrewCapon opened 5 months ago

AndrewCapon commented 5 months ago

With firmware 1.2:

Currently in legato mode 1 we only get a single unit_note_on() and multiple unit_note_off() calls.

in legato mode 0 we only get multiple unit_note_on() and multiple unit_note_off() calls.

So if you actually want to make use of note ons and offs the only way is to have the user select legato mode 0, you cannot determine pitch from the note ons if you don't get them.

Wouldn't it be better if legato mode 1 worked the same as legato mode 0?

john-k-walton commented 5 months ago

accurate note on/off is required for correct envelope control. please fix.

dukesrg commented 5 months ago

I suppose that pitch should always be taken from unit_runtime_osc_context, rather than from note of NoteOn event. In Legato mode it is implied that only a single voice is audible with no EG control. So just up to us how to treat NoteOffs, e.g.

AndrewCapon commented 5 months ago

The main issue with the unit_runtime_osc_context is you do not know if it is a new note, or caused by the LFO, or caused by pitchbend. Also the pitchbend is fixed at +/-2 which stops the unit being able to change the PB range.

Any unit that needs to do something on noteon (say reset or change phases) can not rely on the unit_runtime_osc_context data, you can put code in there to recognise a note change but this note change can be caused by PB or LFO, so if you use this change of note you are going to get audible glitches with PB or LFO.

If you want paraphonic oscillators you need NoteOn and NoteOff, having these only work in one of the legato modes to me is madness.

There should be a flag somewhere for legato mode so the unit can adjust itself.

In the Korg para_saw example we have:

/*
 *  File: para_saw.h
 *
 *  Simple paraphonic saw oscillator.
 *  Please turn off Legato in the global settings.
 */

This should not be needed!

AndrewCapon commented 5 months ago

Also I think now in 1.2 there is no way for the unit to know if no notes are being played.

So play and release three notes:

Depending on legato mode:

1 : Note on, Note off, Note off, Note off.

2 : Note on, note on, note on, note off, note off, note off.

So for the second case we can match note ons and offs, for the first case we have no idea.

For case 1, we can never tell.

With firmware 1.1 we could tell when no notes were held down.

dukesrg commented 5 months ago

@AndrewCapon Option 1 - monophonic

  1. keep NoteOn note value
  2. respect only NoteOff with the note number from the p.1 Option 2 - poly/paraphonic
  3. perform voice allocation on NoteOn based on note value with persisting note numbers
  4. process NoteOff based on the note number and the respective kept in the p.1

For the option 2 the runtime pitch does not make much sense as indeed it is not possible to determine which note and how match it is affected. I'd say it is kept for the SDK1.0 conatibility/portability. So for the poly/para mode, the only reliable option to use unit_pitch_bend, such way pitch LFO wil lbe ignored, I suppose. The same was pitchbend sensitivity is up to you how to treat 14-bit PB values as MIDI does not mandate the +/-2 OCT range. I hope PB is not affected by pitch LFO in this case?

AndrewCapon commented 5 months ago

_Option 1 - monophonic

keep NoteOn note value respect only NoteOff with the note number from the p.1_

So if a I play C1, C2, C3 then release C1 but still hold C2 and C3?

Option 2 - poly/paraphonic perform voice allocation on NoteOn based on note value with persisting note numbers process NoteOff based on the note number and the respective kept in the p.1

We only get one note on in legato mode 1

AndrewCapon commented 5 months ago

Maybe I'm not explaining or understanding but as far as I can see:

In legato mode 0 with firmware 1.2 you can handle your pitch and voice from the NoteOn/NoteOff, you can also determine if any notes are currently being played.

In legato mode 1 you cannot handle your pitch using NoteOn, you have to use unit_runtime_osc_context. Also you cannot determine accurately if any notes are currently being played, or if all notes are released. You can determine if the first note played has been released but you do not have any info on any notes that were played (NoteOns) after the first note and whilst it was held.

Also we have no idea what legato mode is set by the user.

Also for the user to change Legato mode it is a bit of a pain requiring a power cycle, and the idea of certain units requiring a certain mode will I guess lead to confusion?

dukesrg commented 5 months ago

In Legato mode ONLY ONE note at a time is possible. Treat it as a single string pluck instrument. You should not get two NoteOn events with no NoteOff between them, just ignore redundant NoteOffs after the first one. With Legato = Off - it's normal poly mode, NoteOff per each NoteOn are expected.

AndrewCapon commented 5 months ago

Maybe I'm thick.

I have some logic that I want to run when no notes are held. How in mode 1 can I do this? I want to run this logic when the notes are released, not on the next noteon.

How can I determine what legato mode I am in?

How can I write generic note on/ note off code that works in both legato modes?

How can I determine pitch/velocity via note ons in mode 1?

dukesrg commented 5 months ago

Maybe I'm thick.

I have some logic that I want to run when no notes are held. How in mode 1 can I do this?

The tricky part is indeed with the pitch control - the one from the context is only meningful for mono/LegatoOn mode.

AndrewCapon commented 5 months ago

In mode 1 we get a single note on and multiple note offs.

So lets say note on 63 and hold.

We save away 63.

User then plays two more notes 64 and 65 and hold.

We then get a note off 63.

You are now saying all notes are off?

dukesrg commented 5 months ago

Correct, that is how both Legato and Portamrnto should work. Even if it is theoreticallly possible write down several parallel legato at on the stave, there is no way to implement it over MIDI. So for legato mode you just need to implement single note at a time, i.e. as soon as you tracking note on- note off parity.

AndrewCapon commented 5 months ago

All notes are not off, every other synth would agree with me on this.

I really don't understand why you are arguing that you are correct here where quite obviously you are not!

boochow commented 2 months ago

This issue is complex, but IMHO, as AndrewCapon pointed out, the legato mode one should behave similarly to legato mode zero. Here are my thoughts:

1) As a MIDI controller, the expected behavior of the NTS-1 is to send MIDI note events corresponding to key ON and OFF actions. 2) The primary purpose of these ON and OFF events is to control the envelope generator. Each note event indicates whether the envelope should retrigger or enter the release phase; however, this does not directly dictate whether the oscillator should produce sound. 3) The legato mode specifies how the envelope generator should respond to simultaneous or overlapping note-ons and note-offs. However, regardless of the legato mode, the note ON and OFF events themselves should be transmitted accurately, as the legato mode only affects the behavior of the envelope generator, not the MIDI controller. 4) Although note events don’t directly indicate whether the oscillator should produce sound, they are still useful for altering the oscillator’s state. For example, Karplus-Strong string synthesis requires a trigger signal, which can be derived from note-on events. 5) The note number in the MIDI events alone cannot fully determine the oscillator’s pitch, as various other factors can influence pitch. Nonetheless, it is still essential for relating note-on and note-off events. 6) Using MIDI note events, one can implement not only monophonic oscillators but also paraphonic ones. While the legato mode might help determine whether the oscillator operates in monophonic or paraphonic mode, this is a separate issue. Perhaps the amp_eg_phase or amp_eg_state in unit_runtime_osc_context can be used to get the legato mode status.