Page 1 of 1

[Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 2:47 am
by tulamide
I thought, maybe this could be a good idea to have some kind of developer blog. Loose ideas and concepts, babbling while programming, that sort of stuff. However, my health isn't the best, and I can't promise, this series will last until the end, nor how often I'll post. But maybe there's something helpful along the way already, for you guys!

1. Preperation
Before we start with the real thing, we should save us some time with convenience functionality. I already know that I will go crazy when always having to type conditions that will never change. So let's pack them into class methods. The first candidates are "Note On" and "Note Off" messages. Let us define what counts as either of those.

Note On
    - status byte: the status byte must be of value 144
    - velocity: this value has to be greater than 0, because many controllers send a 144/0 as a note off equivalent
Note Off
    - the status byte must be 128, if note off velocity is to be used (does anybody know of a synth that makes use of this information? please tell me!)
    - the status byte must be 144 and the velocity 0 to identify as a note off without note off velocity (note that this is not complying to the midi standard that only knows status 128 for note off)

Let's make use of Ruby's ability to extend a classes content at any time.
Code: Select all
class Midi
  ## class definition, will write to an existing class of the same name, if exists

  def note_on?
    ## style guide recommends to end a method's name with
    ## a question mark, if it returns only true or false

    return status == 144 && data2 > 0 ? true : false
    ## just the code representation of what I wrote in the blog.
    ## status, channel, data1 and data 2 are already existing methods of the Midi class
  end
 
  def note_off?
    return status == 144 && data2 == 0 || status == 128 ? true: false
  end
end


This code is written as the style guide recommends. That's why we see no "if...else" here. && means logical and, || means logical or.

We shouldn't think that one method excludes the other! We need both, since a Note On, as defined above will return "false" for anything that is not a note on (sysex, cc, pitch bend, etc.). The same is true for Note Off.

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 8:00 am
by Spogg
This is going to be very interesting and I appreciate you doing it this way. Fascinating!

I personally have never heard of a keyboard with Note OFF velocity either.

tulamide wrote:The status byte must be 144 and the velocity 0 to identify as a note off without note off velocity (note that this is not complying to the midi standard that only knows status 128 for note off)


I wonder if you could comment on MIDI Running Status which I’ve read is part of the MIDI spec:
http://midi.teragonaudio.com/tech/midispec/run.htm
This allows for Note ON velocity 0 to represent note OFF I think. Or maybe I’ve misunderstood it.

Thanks again

Spogg

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 10:34 am
by tulamide
Spogg wrote:I wonder if you could comment on MIDI Running Status which I’ve read is part of the MIDI spec:
http://midi.teragonaudio.com/tech/midispec/run.htm
This allows for Note ON velocity 0 to represent note OFF I think. Or maybe I’ve misunderstood it.

Unfortunately, Running Status is part of the MIDI 1.0 specification, and can lead to confusion.

Back in the mid 80s, when MIDI was specified, it was a purely hardware protocol. Technology wasn't as advanced as it is nowadays, and so the transmitting via cable was run at some kHz. Effectively, the sending of a complete Note On message took just under 1 ms. But studies revealed that trained musicians have a timing of 1.5 ms, for example when playing chords. Which means, although the notes of a chord aren't exactly simultanously, they are closer together than MIDI can send them. As a workaround, they implemented Running Status, in the hopes that the ommitting of a status byte per message would make up for the timing issue. The running status is only allowed for specific types of messages and only if they refer to the same midi channel (which excludes the newest addition, MPE, and shows how insignificant Running Status is nowadays). So, instead of

[144, 0, 60, 127] <- Note On, channel 0, middle C, velocity 127
[144, 0, 60, 96] <- Note On, channel 0, middle C, velocity 96
[144, 0, 60, 90] <- Note On, channel 0, middle C, velocity 90

Running status would send
[144, 0, 60, 127] <- Note On, channel 0, middle C, velocity 127
[60, 96] <- middle C, velocity 96
[60, 90] <- middle C, velocity 90

(note: message type and channel number are actually coded into a single byte, like 144 for Note On channel 0, 145 for Note On channel 1, etc., it is a convenience function from Flowstone, that allows us the way more comfortable use of 4 bytes)

Now they had another issue: When sending the defined meassage 128 for Note Off, it would break the running status, and the next message would again need to send the whole structure. As another workaround they implemented 144 at velocity 0 to be interpreted as Note Off while in Running Status, so that the chain doesn't need to be cut.

[144, 0, 60, 127] <- Note On, channel 0, middle C, velocity 127
[60, 96] <- middle C, velocity 96
[60, 90] <- middle C, velocity 90
[60, 0] <- middle C, velocity 0, meaning: please ignore this being a Note On message and treat it like a Note Off
[62, 127] <- middle D, velocity 127
etc.

To summarize, it is allowed to use 144 at velocity 0 as a Note Off substitue within a Running Status. It is not allowed as a single full midi message. That is [128, channel, key, velocity]

However, it doesn't affect us at all. Flowstone will always present us with full midi messages, and we already set our MIDI class up for interpreting both as Note Off (which the MIDI Association recommends).

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 2:50 pm
by Spogg
Than you for that Sir! :ugeek:

Cheers

Spogg

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 5:03 pm
by trogluddite
It's overkill for the immediate need, and my Ruby coding was somewhat sloppy back when I created this (posted before, but a very long time ago), but I think I could re-factor it into something a bit more comprehensible. It can handle any number of voices, lowest/highest/most-recent note priority, note stealing, and grouping (mono4) voices for unison.

MIDI Manager B v006 (FS303).fsm
(1.59 MiB) Downloaded 242 times


There's also a custom envelope that I wrote which works nicely for mono-synths - it continues the envelope from the current level at the start of a new note instead of resetting to zero, so that you don't get the snapping to zero volume at note onsets.

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Fri Oct 18, 2019 11:30 pm
by tulamide
trogluddite wrote:It's overkill for the immediate need, and my Ruby coding was somewhat sloppy back when I created this (posted before, but a very long time ago), but I think I could re-factor it into something a bit more comprehensible. It can handle any number of voices, lowest/highest/most-recent note priority, note stealing, and grouping (mono4) voices for unison.

MIDI Manager B v006 (FS303).fsm


There's also a custom envelope that I wrote which works nicely for mono-synths - it continues the envelope from the current level at the start of a new note instead of resetting to zero, so that you don't get the snapping to zero volume at note onsets.

Well, that renders this thread useless! A very good work, with everything you need to use MIDI as versatile as I can think of! And there's also a lot of interesting stuff in the stream section as well!

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Sat Oct 19, 2019 4:01 am
by trogluddite
Thanks, tula! :D

I feel a bit embarrassed really that I didn't catch onto what you guys were doing and remember that schematic a bit sooner. I didn't even remember having got so far along with it! :oops:

If I find some time over the weekend, I'll have a go at tidying up the code a bit. I've got to know Ruby a lot better since I made it, and at first glance, there's quite a lot that isn't really necessary and a couple of things I've noticed already that could be made a bit more efficient (I've never done any MIDI/audio timing tests or benchmarking as far as I know). I rather like your idea of adding some features to the Midi class directly, too - a little drop-in "Midi extension" module might be quite a useful thing for all sorts of MIDI development.

Something else that just occurred to me is that passing the voice data to the audio streams might be possible using a Frame (by sharing its buffer address as an "array pointer"), to eliminate the intermediate green/bus section and all those duplicate Ruby output connectors.

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Sat Oct 19, 2019 9:25 am
by tulamide
trogluddite wrote:I rather like your idea of adding some features to the Midi class directly, too - a little drop-in "Midi extension" module might be quite a useful thing for all sorts of MIDI development.
I think so, too! Every convenient method directly related to a midi message could be done this way.

trogluddite wrote:Something else that just occurred to me is that passing the voice data to the audio streams might be possible using a Frame (by sharing its buffer address as an "array pointer"), to eliminate the intermediate green/bus section and all those duplicate Ruby output connectors.
Another wonderful idea! I know that it works, because a few years ago I used your "pack address"-code to realize something similar for a project by Martin. It worked well, ASM had access to memory blocks created and filled by Ruby.

Re: [Ruby] The road to Midi Mono (and maybe more)

PostPosted: Wed Apr 08, 2020 11:49 am
by tulamide
tulamide wrote:To summarize, it is allowed to use 144 at velocity 0 as a Note Off substitue within a Running Status. It is not allowed as a single full midi message. That is [128, channel, key, velocity]

It still doesn'T affect us, but the official midi specification has slightly changed. 144 w vel 0 is now an official alternative to 128:

"NOTE-OFF MIDI provides two roughly equivalent means of turning off a note (voice). A note may be turned off either by sending a Note-Off message for the same note number and channel, or by sending a Note-On message for that note and channel with a velocity value of zero. The advantage to using "Note-On at zero velocity" is that it can avoid sending additional status bytes when Running Status is employed. Due to this efficiency, sending Note-On messages with velocity values of zero is the most commonly used method. However, some keyboard instruments implement release velocity where a Note-Off code (8nH) accompanied by a "velocity off" byte is used. A receiver must be capable of recognizing either method of turning off a note, and should treat them identically. The three methods of using Note-On (9nH) or Note-Off (8nH) are as follows: 1. For a keyboard which does not implement Velocity, the note will be turned on using 9n, kkkkkkk, 64 (40H) and may be turned off using 9n, 0kkkkkkk, 00000000 or 8n, 0kkkkkkk, 0xxxxxxx (a value of 64 [40H] is used for x)."