Midier icon indicating copy to clipboard operation
Midier copied to clipboard

MIDI input

Open tmeysson opened this issue 6 months ago • 3 comments

Hello, great piece of software, I'll be sure to use it for backing chords or bass line while playing the synthetizer. But, I may be missing an interesting feature: is there any way MIDI patterns can be recorded from an external source for loop playback ? For instance, I may wish to start a 'metronome' loop at 90 BPM, record a layer on top of that from an external MIDI keyboard, then play that record in a loop while I move on to something else... Is that in any way possible ? Maybe, using the serial input to trigger sequencer.play calls, with some kind of quantization maybe ?

tmeysson avatar Dec 11 '23 19:12 tmeysson

Hey @tmeysson and thanks for reaching out to me! I really like the idea of recording an external MIDI source and then playing it from Midier. Unfortunately at the moment there's no mechanism in Midier to receive external MIDI notes and the only way to play is with Midier originated layers.

Do you have any idea how your suggestion can be implemented easily?

razrotenberg avatar Dec 11 '23 21:12 razrotenberg

Thanks for your quick response ! I've been thinking about this, this evening, and it's really two separate things that have to be worked together.

First, the raw MIDI input part (which may be left to noteon and noteoff messages, maybe). I haven't looked into the serial code, but, as it seems nothing is supposed to come in from the serial line as it now stands, either there is just nothing that reads Serial anywhere, or, just for sanity's sake, it's periodically flushed and discarded. So, probably, we may just read bytes from Serial the usual way, and the MIDI protocol isn't really hard to interpret, especially in this case where the first byte is between two possible, we only really need the second one and the third one gets discarded... aah, maybe there are two-byte messages also, so we need to identify those so as not to fall out of sync waiting for a non-existent third byte. And; it requires running the sequencer in async mode.

The tricky bit, I think, is finding a consistent way of handling incoming noteon/noteoff events, transforming those into arpeggios defined by a root note, a mode and degree. Given a specific chord, if it can be matched against a known quality (there's only 9 as I can see), that can associated through a static (and small) table with a list of (mode, degree) pairs which yield that quality, then we can infer for each of those the adequate root note that corresponds to this specific arpeggio (in this mode and degree combination), so, it can be represented in the sequencer, if we also identify the corresponding permutation and rhythm (and duration, supposing possible repeats). No matter what, this can only be done once all the notes from the arpeggio have been played, so we may opt for an alternate play mode where the notes are just played together in the correct order and held approximately for the required period, before all notes are released, and without any more requirement for time precision. Then, as we usually have several (root note, mode, degree) candidate tuples for each arpeggio, we can also match each successive arpeggio's possible definitions against that list and try to keep a consistent root note and mode, hopefully ending up with a succession of arpeggios corresponding to different degrees of the same musical structure. Or either, something a bit more complex, but maybe not combining more than a few tonalities and modes (though, sometimes, strange things work surprisingly well...). So, at the same time, we're doing real-time musical analysis, yahoo ! -- though, we'd expect it's possible, supposing the musician is trying to do anything even remotely conventional. Oh, and, if the chord corresponds to nothing we know of, for instance, it contains two successive seconds, then, it's legitimate to drop it because we have no way to represent it. Or either, we have to treat it as two separate objects, but, quite possibly it can be an error from the operator.

And then, that doesn't cover the problem of erasing recordings, totally or partially, so, that's only part of the job.

Maybe an appropriate interface would do away with the keyboard concept, and rather work with dials and switches like with old-style sequencers, but that is completely off the recording topic, then. Possibly much simpler, though, if it's possible to have a separate interface for the arpeggiator, like in your other project built on top of this one.

If you can make any sense of this, feel free to do so, but, personally I think I'll start by deciding whether I want an arpeggiator or a real MIDI looper. Probably just not the same thing, in fact.

On Mon, 11 Dec 2023 at 22:18, Raz Rotenberg @.***> wrote:

Hey @tmeysson https://github.com/tmeysson and thanks for reaching out to me! I really like the idea of recording an external MIDI source and then playing it from Midier. Unfortunately at the moment there's no mechanism in Midier to receive external MIDI notes and the only way to play is with Midier originated layers.

Do you have any idea how your suggestion can be implemented easily?

— Reply to this email directly, view it on GitHub https://github.com/razrotenberg/Midier/issues/16#issuecomment-1850902778, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4K44I2FC3XDQLDUSNJ37TYI52ATAVCNFSM6AAAAABAQIVOQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNJQHEYDENZXHA . You are receiving this because you were mentioned.Message ID: @.***>

tmeysson avatar Dec 12 '23 22:12 tmeysson

Looked a bit further into the code, and it seems it has to do with Arpeggino and Controlino, rather than Midier, so, you may have to move this thread where appropriate.

We'll need objects similar to controlino::Key, but attached to a given MIDI note instead of a pin; and, the check() method will read the current status from an array instead of calling the digitalRead() method. So, this isn't a subclass of controlino::Control. The status array is a class variable, and it's updated by periodic calls to a class method that acts as a frontend to Serial.available() and Serial.read(), as in the attached example. It filters out anything not NoteOn or NoteOff, and changes the current status of the corresponding notes accordingly. The two-stage update process may seem a bit awkward, but it's the best I could come up with while staying in compatibility with the existing control types and their use, and at the same time handling the problem of updating values on demand, within an array, and not by individual polling as controlino::Control expects. Then, it can be integrated in Arpeggino's handle::keys section, by replacing the Key objects it uses, possibly through a compile-time option.

After that, it may be possible to replace the other controls with the same type of object, extended to handle CC events also, possibly with different types (pushbuttons, two-state toggles, encoders/faders, ...), which would have to be aggregated to notes because of the reading mechanism. That may save users quite a lot of hassle with the hardware setup part, supposing they have any kind of MIDI interface available.

Better, isn't it ?

On Tue, 12 Dec 2023 at 23:12, Thomas Meyssonnier @.***> wrote:

Thanks for your quick response ! I've been thinking about this, this evening, and it's really two separate things that have to be worked together.

First, the raw MIDI input part (which may be left to noteon and noteoff messages, maybe). I haven't looked into the serial code, but, as it seems nothing is supposed to come in from the serial line as it now stands, either there is just nothing that reads Serial anywhere, or, just for sanity's sake, it's periodically flushed and discarded. So, probably, we may just read bytes from Serial the usual way, and the MIDI protocol isn't really hard to interpret, especially in this case where the first byte is between two possible, we only really need the second one and the third one gets discarded... aah, maybe there are two-byte messages also, so we need to identify those so as not to fall out of sync waiting for a non-existent third byte. And; it requires running the sequencer in async mode.

The tricky bit, I think, is finding a consistent way of handling incoming noteon/noteoff events, transforming those into arpeggios defined by a root note, a mode and degree. Given a specific chord, if it can be matched against a known quality (there's only 9 as I can see), that can associated through a static (and small) table with a list of (mode, degree) pairs which yield that quality, then we can infer for each of those the adequate root note that corresponds to this specific arpeggio (in this mode and degree combination), so, it can be represented in the sequencer, if we also identify the corresponding permutation and rhythm (and duration, supposing possible repeats). No matter what, this can only be done once all the notes from the arpeggio have been played, so we may opt for an alternate play mode where the notes are just played together in the correct order and held approximately for the required period, before all notes are released, and without any more requirement for time precision. Then, as we usually have several (root note, mode, degree) candidate tuples for each arpeggio, we can also match each successive arpeggio's possible definitions against that list and try to keep a consistent root note and mode, hopefully ending up with a succession of arpeggios corresponding to different degrees of the same musical structure. Or either, something a bit more complex, but maybe not combining more than a few tonalities and modes (though, sometimes, strange things work surprisingly well...). So, at the same time, we're doing real-time musical analysis, yahoo ! -- though, we'd expect it's possible, supposing the musician is trying to do anything even remotely conventional. Oh, and, if the chord corresponds to nothing we know of, for instance, it contains two successive seconds, then, it's legitimate to drop it because we have no way to represent it. Or either, we have to treat it as two separate objects, but, quite possibly it can be an error from the operator.

And then, that doesn't cover the problem of erasing recordings, totally or partially, so, that's only part of the job.

Maybe an appropriate interface would do away with the keyboard concept, and rather work with dials and switches like with old-style sequencers, but that is completely off the recording topic, then. Possibly much simpler, though, if it's possible to have a separate interface for the arpeggiator, like in your other project built on top of this one.

If you can make any sense of this, feel free to do so, but, personally I think I'll start by deciding whether I want an arpeggiator or a real MIDI looper. Probably just not the same thing, in fact.

On Mon, 11 Dec 2023 at 22:18, Raz Rotenberg @.***> wrote:

Hey @tmeysson https://github.com/tmeysson and thanks for reaching out to me! I really like the idea of recording an external MIDI source and then playing it from Midier. Unfortunately at the moment there's no mechanism in Midier to receive external MIDI notes and the only way to play is with Midier originated layers.

Do you have any idea how your suggestion can be implemented easily?

— Reply to this email directly, view it on GitHub https://github.com/razrotenberg/Midier/issues/16#issuecomment-1850902778, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4K44I2FC3XDQLDUSNJ37TYI52ATAVCNFSM6AAAAABAQIVOQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNJQHEYDENZXHA . You are receiving this because you were mentioned.Message ID: @.***>

tmeysson avatar Dec 13 '23 15:12 tmeysson