|
Post by mcbman on Mar 21, 2024 21:35:33 GMT
Hello, I picked up MIDI Fire a few days ago. I like how it does not alter my incoming midi data (both AUM and AudioBus seem to alter and break the midi data stream). It looks like you have excellent support, so I have to give a big-ups for that!
Sorry about the length of Novella I have written to describe my use-case and reasoning for creating the script, which will follow as the next post in thread.
What I would like to ask is….
Is there a way that I can use a “subroutine” in my code, and use “wildcards” for parts of the code which are specific to each MID channel? I wasn’t able to figure it out from the documentation.
|
|
|
Post by mcbman on Mar 21, 2024 21:38:21 GMT
Reasoning behind design of script;
I am a sound designer who uses a Linnstrument as an MPE controller, and I use the ASM Hydrasynth as a versatile tool for MPE sound creation. When designing sounds that are of a percussive or plucked nature, I have found that the MPE (X) pitch parameter's continuous control data (from one’s finger on the X axis) often tends to make these types of sounds feel unnatural (or worse if one adds FM into the mix). For example, if one were to hit a bell, the pitch would not change after it has been struck with a mallet, or if one were to pluck a stringed instrument the pitch would not bend unless one moves his or her fingers along the bridge (it is much harder to hold one’s finger in one place with an MPE controller than on the bridge of an instrument). Unfortunately, due to the sensitivity and responsive nature of these modern MPE controllers it is very hard to keep the pitch from changing after hitting a note (before the release stage of the envelope). Fortunately, due to the sensitivity and responsive of these modern MPE controllers, they are very expressive and cool! One of course, could always turn off the X-axis and avoid this problem, but then one would be hitting all of the notes at perfect pitch every time... I believe that the ability to be able to play notes off key adds to the beauty and character of sound, and a level of expressiveness not attainable from a standard MIDI Keyboard.
Solution to this Conundrum;
Once a note is played, the initial channel pitchbend value is held, and any further stream of values sent from the controller is ignored.
Logic and implementation of script;
After much trial and error, and with the assistance of AI for some of the coding, I have come to a working solution as follows;
Initially, unique variables are set for each channel independently (L0*-LF* depending on which channels are to be affected)
For each channel independently, the following occurs;
1 – The channel’s first pitchbend message value sent from the controller is passed through unaltered. This is a channel pitchbend value of 0 on whichever note is being played (eg E#4), and this is sent by the Linnstrument to reset the pitchbend value of that channel as the new note is played. This occurs before the note on message.
2 – After a note-on message is received for that specific channel, the next channel pitchbend value is allowed to pass through, is then stored, and is repeated 5 times, at a 1ms interval. It is repeated to ensure that the sound generator receives and reacts to the value.
3 – All further pitchbend messages and values on that specific channel are ignored, until a note-off message specific to that channel occurs.
4 – Once the channel specific note-off message is received, all counters for the variables which pertain to that specific channel are reset to their initial state.
After a few days of trial and error, employing chat GPT4 to help me write the code, I have arrived at the script which will be the next post in this thread. This is an abridged version that includes only channel 3 and 4. In my current working script, I have repeated the code for channels 2-16, changing the necessary variables. Channel 1 is the manager channel used for global messages, and thus not affected by the script.
As one might imagine, it is quite laborious to duplicate the script for each channel, then to surgically alter the pertinent parts of the code specific to each channel up to 15 times when tweaking the code (unfortunately AI is a lazy stubborn Ox, and I have to do this manually). I would like to achieve a script that is more concise, efficient (or at least shorter), and that takes “Design Intent” into account, so that is can be modified more easily in the future. I will likely change which channels are affected (2-8 for MPE Lower Row, 9-15 for MPE Upper Row etc..). As of now, my working script affecting channels 2-16 contains 24,968 characters (It does work though, which is an amazing feat to me, due to the fact that person with 0 programming knowledge was able to achieve this). I am also struggling with sending the code from my Windows PC to my Ipad, as I am unable to figure out how to avoid having to manually select all of the text on the Ipad by scrolling the selection bracket down the pages of 20,000+ characters (the double tap “select all” prompt does not exist for me in this instance, perhaps because I am emailing the files in .rtf format, which maybe Apple does not like or something). Aside from the design intent factor, I would also like the code to be optimally designed/written as I am a bit of a perfectionist who doe’s not like redundant convoluted things, and because I also intend to share the script with others.
|
|
|
Post by mcbman on Mar 21, 2024 21:39:26 GMT
IF LOAD
# CHANNEL 2 VARIABLES
ASS L10 = 0 # Flag to indicate the second Pitch Bend has been captured and is ready to be sent
ASS L11 = 0 # Store the second Pitch Bend LSB value
ASS L12 = 0 # Store the second Pitch Bend MSB value
ASS L13 = 0 # Flag to block further Pitch Bend messages until a note-off message is received
ASS L14 = 5 # Counter for sending the stored Pitch Bend value 5 times
ASS L15 = 0 # Counter for the number of Pitch Bend messages received after a note-on
# CHANNEL 3 VARIABLES
ASS L20 = 0 # Flag to indicate the second Pitch Bend has been captured and is ready to be sent
ASS L21 = 0 # Store the second Pitch Bend LSB value
ASS L22 = 0 # Store the second Pitch Bend MSB value
ASS L23 = 0 # Flag to block further Pitch Bend messages until a note-off message is received
ASS L24 = 5 # Counter for sending the stored Pitch Bend value 5 times
ASS L25 = 0 # Counter for the number of Pitch Bend messages received after a note-on
END
# CHANNEL 2
# Handling Pitch Bend messages for Channel 2
IF M0 == E1
IF L13 == 0
MAT L15 = L15 + 1 # Increment the Pitch Bend message counter
IF L15 == 1
# First pitch bend message passes through
# No action needed here to let it pass
END
IF L15 == 2
ASS L11 = M1 # Store the second Pitch Bend LSB value
ASS L12 = M2 # Store the second Pitch Bend MSB value
ASS L10 = 1 # Indicate the second Pitch Bend is ready to be sent 5 times
BLOCK # Block the second Pitch Bend message from being sent directly
END
END
# Block further Pitch Bend messages after capturing the second one
IF L15 > 2
BLOCK
END
END
# Sending the stored second Pitch Bend message 5 times at 1ms intervals for Channel 2
IF L10 == 1
IF L14 > 0
IF T00 >= 1
SEND E1 L11 L12
MAT L14 = L14 - 1 # Decrement the counter
IF L14 == 0
ASS L10 = 0 # Reset the flag after sending it 5 times
ASS L15 = 0 # Reset the Pitch Bend message counter
END
END
END
END
# Handling Note-off messages for Channel 2
IF M0 == 81
ASS L10 = 0 # Reset all flags and counters to their initial state upon note-off
ASS L11 = 0
ASS L12 = 0
ASS L13 = 0
ASS L14 = 5
ASS L15 = 0
END
# CHANNEL 3
# Handling Pitch Bend messages for Channel 3
IF M0 == E2
IF L23 == 0
MAT L25 = L25 + 1 # Increment the Pitch Bend message counter
IF L25 == 1
# First pitch bend message passes through
# No action needed here to let it pass
END
IF L25 == 2
ASS L21 = M1 # Store the second Pitch Bend LSB value
ASS L22 = M2 # Store the second Pitch Bend MSB value
ASS L20 = 1 # Indicate the second Pitch Bend is ready to be sent 5 times
BLOCK # Block the second Pitch Bend message from being sent directly
END
END
# Block further Pitch Bend messages after capturing the second one
IF L25 > 2
BLOCK
END
END
# Sending the stored second Pitch Bend message 5 times at 1ms intervals for Channel 3
IF L20 == 1
IF L24 > 0
IF T00 >= 1
SEND E2 L21 L22
MAT L24 = L24 - 1 # Decrement the counter
IF L24 == 0
ASS L20 = 0 # Reset the flag after sending it 5 times
ASS L25 = 0 # Reset the Pitch Bend message counter
END
END
END
END
# Handling Note-off messages for Channel 3
IF M0 == 82
ASS L20 = 0 # Reset all flags and counters to their initial state upon note-off
ASS L21 = 0
ASS L22 = 0
ASS L23 = 0
ASS L24 = 5
ASS L25 = 0
END
|
|
|
Post by mcbman on Mar 21, 2024 21:44:00 GMT
Thank You very much in advance for your help.
|
|
|
Post by redheronmusic on Mar 22, 2024 19:51:10 GMT
Got this plugged into my brain to work on. Think we can get a simple soluton that doesn't require repeating and editing code for 16 channels.
To verify, since you are in a MPE environment, we don't have to worry about couting notes on a channel, we expect one note on, bend, then off. Then next note. No multiple notes per channel while the bend is on.
|
|
|
Post by redheronmusic on Mar 22, 2024 23:46:25 GMT
Give this a try.
1 ms is pretty fast in MIDI time, so I increased to 10 ms. Play around and see what is needed. (I am surprised at a board that needs a message repeated a few times.)
# MPE Pitch Bend Limiter R2 # Red Heron Music # March 22, 2024
# MPE environment, so presume each channel 2-16 will only see a unique note on - note off event # so we don’t have to keep track of active notes per channel
# Before note, let only zero value pitch bend through # After note, let only a single non-zero pitch bend value through, repeat four times, block any additional PB
# no aliases, since we are relying on indirect references, where alias will not work # i1 Channel 2 status # 0 = nothing happened # 1 = note happened # 2 = PB sent # i2 through iF status for channels 3 - 16
# j0, j1 loop values
If Load # set all flag values to zero Ass J0 = 1 While J0 < 10 Ass IJ0 = 0 Mat J0 = J0 + 1 End End
If MC != 0 # ignore channel 1, process all others If MT == 90 # note on type Ass IMC = 1 # note played End If MT == 80 # note off type, clear status Ass IMC = 0 End If MT == E0 # bend type If M1 == 00 00 # zero value pitch bend If IMC == 0 # no note played # pass zero value PB prior to note on Else Block End Else If IMC == 1 # note but no PB Ass IMC = 2 Ass J0 = 0 # set up loop Ass J1 = $10 # second message delay time While J0 < 4 # only loop four times, original message is the fifth Snd M0 M1 M2 +DJ1 Mat J0 = J0 + 1 Mat J1 = J1 + $10 increment delay TIME End Else Block # block extra bend messages End End End End
|
|