Keyboard mapping script

Thanks, Marc, for your reply and efforts to illustrate.

I copied this example with the part of the Moonlight midi inputs and session in Thonny.
I changed input and output into “GO:KEYS” (ROLAND).

Nothings happen.
I used also Pianoteq6 (it’s a brilliant piano program). It’s not clear for me where I have to handle for triggering MIDI notes.
Do I have to start on a specific way I don’t know on Mac and on the keyboard?

Basically I hav ethe follow question:
I wonder how you can put all many notes (part of 1e mouvement Mondschein sonate) automatically and not by hand.

My dreamscript would be:
I put personal consonant and dissonant chords per MIDI note in Scamp.
How I have to program these chords if possible via MIDI-keyboard without writing down all this numbers one by one?

Can you deliver me some script example: for instance two different chords for triggering two different MIDI-notes. How and where to put in the script?)

If I can start and let work (sounding) thanks to your example I can figure out other chords.

I hope you can help me further.

Thanks in advance.

Paul

After you plug in your keyboard, run the following script:

from scamp import *
print_available_midi_input_devices()
print_available_midi_output_devices()

What does it print out?

Hi Marc

What does it print out?

MIDI Input Devices Available:
[Port 0]: GO:KEYS
MIDI Output Devices Available:
[Port 0]: GO:KEYS

Running the SCAMP script “Moonlight Example.py” there is no sound from my keyboard. No MIDI interaction.

Paul

Yeah, Marc, playing some keys on my keyboard I now hear the parts of the Moonlight sonata.

So what I have to do if I like to make a script for special chord dedicated to determinated keys? (See my dream on earlier email)

Thanks in advance.

Paul

Hi Marc

  1. A.
    You make a link to
    #Instrument=Veena
    C C# D D#
    #InstrumenFlute
    C C# D D#
    #Instrument=Veena
    C C# D D#
    #InstrumenFlute
    C C# D D#
    Where I have to put in this script of notes? I don’t see.
    1.B.
    What has it to do with my aim to put different chords of simultaneous notes linked at different individual keys on tmy keyboard?

Initially I just experimented randomly with
“for pitch in range”
and
“gesture -rate by pitch”.
There are funny changes but I don’t understand to master the effect of the numbers in this script. What is essentially the punch line?

What and where in the script I have to put the commands so that not only my keyboard is sounding but also the Pianoteq-pianosound (software) is active?

Many thanks in advance.

Paul

Hi Paul,

The truth is, it’s not the simplest script to understand if you’re not experienced with Python, and it’s really just designed to do what it does. You might find it most interesting to experiment with changing the notes list starting on line 54. That list is a list of pitches played by the inner voice in the first movement of the moonlight sonata, and if you change it to some other sequence of pitches (making sure to at least include one of every pitch class!), it will produce a different kind of result.

To be honest though, I think I don’t understand what your dream script is. You want every note you play to cause a chord to play along with it? Should the chord happen at the same time, or after you play the note?

In order to make use of SCAMP, you really need to understand Python programming more generally. I really like this tutorial series:

I really like this tutorial series:

This tutorial is pretty good too, and is a little slower and easier, if the other one goes to fast:

Marc

Dear Marc
I appreciate your effort to help me. You are wright: I’m not really interested in programming via Python but the language of Python has to be utilitary to cover my dream script. I appology: In terms of receptology.
Yes, you know from our DVD-project: I compose and improvise more intuitively as ‘on the traditional way’. Technology has to be a tool to make my dreams true.

To be honest though, I think I don’t understand what your dream script is. You want every note you play to cause a chord to play along with it? Should the chord happen at the same time, or after you play the note?
It’s exactly what I like to realize. I play f.e. C4 on the Piano MIDI keyboard and this key triggers a specific staple of notes that is fixed via Scamp on before. The triggered script sounds at the same time. So, for more keys available for about 40 other Piano MIDI keys of the keyboard.

Of course I still understand Python basically and generally via the youtube links you indicated. But I’m not so motivated as a normal computer programmer. On my Korg Kronos there are many, many algorithms that vary my play on the keyboard. But these algorithms are fixed, not changeable.

That was my start of thinking to use Python dynamically.
My dream script would be:
When I can trigger several Midi keys (connect to specific vertical stapled notes I programmed in Python). With this script I can experiment to select, change or exclude some special chords I trigger, just till I can play my “jazz”.
In addition, finally it would be great to trigger some keys in keyzones flute, other piano, other bass, other drum or the trigger depends of the pressure (piano touch) flute or piano.

I know you have to do some more other relevant things than guiding me on my way I proposed but I would appreciate very much if you can write down one or two different commands that I can copy, transpose or vary to finally a personal set of chords for determinated midi-keys.

If you like that I appreciate your efforts with a small donation, I 'm open to do.

Thanks in advance.
Best regards

Paul

Hi Paul,

So, I think I understand, and I thought I’d give it a shot!

Here’s the script:

https://git.sr.ht/~marcevanstein/scamp/tree/master/examples/Demos/KeyboardMapper/KeyboardMapper.py

The key line you will want to customize is this one:

pitch_to_instrument_and_pitches = {
    # this line says that if you play midi pitch 55, it will come out as pitch 77, played by the clarinet
    55: [clarinet, 77],
    # this line says that if you play midi pitch 43, it will come out as the cluster [60, 61, 62, 63] played by clarinet
    43: [clarinet, [60, 61, 62, 63]],
    # this line says that if you play a pitch between 65-72, it will come out as a chord played by the flute
    # consisting of the note two half steps higher and two half-steps lower. "p" stands for the particular
    # pitch that you play, since this is operating on a range of pitches
    # Note the quotes! The strings used here aren't really a standard python thing; it was just the easiest way
    # of making it work.
    "65-72": [flute, "[p-2, p+2]"],
    # this line says what to do if you play a pitch that isn't covered by the lines above. In this case, we play
    # back with a piano, up an octave (since "p" is the pitch you played, "p+12" is that pitch up an octave.
    "default": [piano, "p+12"]
}

…as well as the lines before where you create the instruments and the MIDI input device setting afterward.

I’m hoping the comments are enough to get you going, but I can clarify if there’s something you don’t understand. For getting the playback to work with pianoteq, you’ll probably have to use IAC, which is like a virtual MIDI cable:

Thanks for your offer of a donation; it would actually be really appreciated, if you’re up for it (I could use it to offset the cost of website hosting). Maybe I can send you an e-mail with my venmo or something?

Many, many thanks, Marc. I’m excited to try out in the next days, and I send your later my experience. I donate with pleasure via Paypal, if you agree. You send me the link.

Best

Paul

Hello Marc

I understand very well your clear comment about the “clarinet” en flute "commands. Thanks.

I’m trying to run Scamp-script that you constructed for me very well. No problem to run, but I don’ hear anything. No flute, no clarinet etc.

I changed next lines to resolve.
piano = s.new_midi_part(“GO:KEYS”, note_on_and_off_only=True)
s.register_midi_listener(“Go:KEYS”, midi_callback)

Without success.

Running your Moonlight sonate script, however, my external keyboard “GO:KEYS” is sounding on as well.
Rests one difference - I detect - with your Moonlight script:
rom scamp import
print_available_midi_input_devices()
print_available_midi_output_devices()

MIDI_INPUT_DEVICE = “Go:KEYS”
MIDI_OUTPUT_DEVICE = “Go:KEYS”

I once integrate above lines. But the sounding problem isn’t solved.

First, the midi notes I play on my keyboard GO:KEYS" arrive on my soft keyboard of Pianoteq on iMac.
I added in the Audio MIDI setup a virtual IAC setup in the meaning that the softsynth Pianoteq would produce the clarinet or flute clusters etc. Doesn’t yet work.

What I once prefer: the “GO:KEYS” external keyboard AND the soft program Pianoteq are sounding at the same time. In that case: I use the piano sound of Pianoteq.
Of course, I understand the programmed Clarinet and flute sounds cannot sound on a piano software.

If you can advice me to change the command lines on the right way, I’m greatfully thank you in advance.

Paul

Are you hearing flute sounds if you play in the middle register above middle C?

To get it to play back through your keyboard, you will want:

piano = s.new_midi_part("gokeys", “GO:KEYS”, note_on_and_off_only=True)

The first argument there ("gokeys") is just the name of the part in the score if you created one — it’s not the midi output device. Its the second argument that determines the device.

What do you want to be doing the playback? Your keyboard? Pianoteq?

Hello Marc

New try.
The good news: today - why not yesterday? - I launced the script you made for me. Now the midi keyboard sounds (flute clarinet ) are present.
Question: Where I have to check in the script to control all programmed sounds over the keyboard?

The bad news: all try out - f.e. clarinet triggered by the midi-key, programmed to produce a chord - the triggering sequences (different notes ) are not present.
Can you help?
Thanks in advance.

Paul
PS Normally, my donation would be arrived to you.

Hi Paul,

Where you go in the script is lines 40 - 54, which you can modify by adding new lines. For instance, if you want middle C to cause the flute to play a chord of notes 67, 68, and 73, you would add this line within the curly braces.:

43: [clarinet, [67, 68, 73]],

Also, I’m not sure I understand what you’re saying by this:

Do you mean that the original notes that you’re pressing aren’t playing? Just the flute and clarinet chord?

Hi Marc

Thanks.

I mean: it is only the pressed midi note (flute or clarinet) I hear. The programmed scamp-notes related to the played Midi note (the flute / clarinet chord) are absent (i don’t hear)
What to do?
thanks in advance.

Paul

Unfortunately I have not yet received an answer to my Scamp file playback problem. Today on my new Yamaha PSR-SX900 double checked. What’s wrong with my MIDI-notes into chords.py? Your Moonlight.py works flawlessly? Even without replacing the name <Go: Keys> by in the script. Can you help me soon, Marc? see below.
In the Shell there appairs with my play:
144,75,0
144,82,74
etc
This seems to be normal.

Thanks in advance.

Paul

SCRIPT MIDI-NOTES INTO CHORDS.

from scamp import *

s = Session()

# This is where you define all of the instrument that you want to play back notes with.
# The `note_on_and_off_only=True` flag is helpful in keeping the midi messages simple if
# your not doing any pitch bending, glissandi, microtonal stuff, or dynamic envelopes.
# The `new_midi_part` call will open an outgoing midi stream, in this case to IAC, which
# is a virtual midi cable that can be used (on macs) to route playback between applications
piano = s.new_midi_part("sx900","SX900", note_on_and_off_only=True)
# The `new_part` calls create parts that play back using the default soundfont
flute = s.new_part("flute", note_on_and_off_only=True)
clarinet = s.new_part("clarinet", note_on_and_off_only=True)

# This is where the magic happens! Each entry in this dictionary determines what happens
# when you play a pitch in a certain key or range of keys.
pitch_to_instrument_and_pitches = {
    # this line says that if you play midi pitch 55, it will come out as pitch 77, played by the clarinet
    55: [clarinet, 77],
    # this line says that if you play midi pitch 43, it will come out as the cluster [60, 61, 62, 63] played by clarinet
    43: [clarinet, [60, 61, 62, 63]],
    # this line says that if you play a pitch between 65-72, it will come out as a chord played by the flute
    # consisting of the note two half steps higher and two half-steps lower. "p" stands for the particular
    # pitch that you play, since this is operating on a range of pitches
    # Note the quotes! The strings used here aren't really a standard python thing; it was just the easiest way
    # of making it work.
    "65-72": [flute, "[p-2, p+2]"],
    # this line says what to do if you play a pitch that isn't covered by the lines above. In this case, we play
    # back with a piano, up an octave (since "p" is the pitch you played, "p+12" is that pitch up an octave.
    "default": [piano, "p+12"]
}

# Put here the name of the device MIDI messages are coming in from
from scamp import *
print_available_midi_input_devices()
print_available_midi_output_devices()

# the input and output device can be the same or different
# for instance, you might want to take midi in from the keyboard,
# but send any note playback messages to a softsynth like pianoteq
MIDI_INPUT_DEVICE = "SX900"
MIDI_OUTPUT_DEVICE = "SX900"


# # Uncomment these lines if you want to see which midi input and output devices are available
# print_available_midi_input_devices()
# print_available_midi_output_devices()

# --------------------------------------------- IMPLEMENTATION -----------------------------------------------

# ----- process pitch_to_instrument_and_pitches -----
processed_pitch_to_instrument_and_pitches = {}
for key, value in pitch_to_instrument_and_pitches.items():
    if isinstance(key, str) and "-" in key:
        start_pitch, end_pitch = key.split("-")
        for p in range(int(start_pitch), int(end_pitch) + 1):
            processed_pitch_to_instrument_and_pitches[p] = value
for key, value in pitch_to_instrument_and_pitches.items():
    if not isinstance(key, str):
        processed_pitch_to_instrument_and_pitches[key] = value
            
for key, value in processed_pitch_to_instrument_and_pitches.items():
    instrument, pitches = value
    if isinstance(pitches, str) and key != "default":
        processed_pitch_to_instrument_and_pitches[key] = [instrument, eval(pitches, {}, {"p": key})]

processed_pitch_to_instrument_and_pitches["default"] = pitch_to_instrument_and_pitches["default"]
notes_down = [[] for _ in range(128)]
pedal_held_notes = [[] for _ in range(128)]
pedal_down = False


def midi_callback(midi_message):
    global notes_down, pedal_held_notes, pedal_down
    # function that handles any incoming midi messages from the keyboard
    print(midi_message)
    code, pitch, volume = midi_message
    if volume > 0 and code == 144:
        # note on message causes us to fork a new moonlight sonata gesture at the given pitch and volume
        if pitch in processed_pitch_to_instrument_and_pitches:
            instrument, pitches = processed_pitch_to_instrument_and_pitches[pitch]
        else:
            instrument, pitches = processed_pitch_to_instrument_and_pitches["default"]
            if isinstance(pitches, str):
                pitches = eval(pitches, {}, {"p": pitch})
        if hasattr(pitches, '__len__'):
            notes_down[pitch].append(instrument.start_chord(pitches, volume / 127))
        else:
            notes_down[pitch].append(instrument.start_note(pitches, volume / 127))
    elif volume == 0 and code == 144 or code == 143 or code == 128:
        # note off message (or note on message with 0 velocity, which is sometimes how it's implemented)
        # we use it to kill the gesture running for the note at the given pitch
        if pedal_down:
            pedal_held_notes[pitch].extend(notes_down[pitch])
            notes_down[pitch].clear()
        else:
            for note in notes_down[pitch]:
                note.end()
            notes_down[pitch].clear()
    elif code == 176 and pitch == 64:
        # pedal change messages just get passed straight along to the MIDI_OUTPUT_DEVICE
        # (with some warping of the values)
        pedal_down = volume > 0
        if volume == 0:
            for pitch_notes in pedal_held_notes:
                for note in pitch_notes:
                    note.end()
                pitch_notes.clear()


s.register_midi_listener("SX900", midi_callback)
s.wait_forever()

Hi Paul,

My wife and I had our first child last month, so as you can see, there
will probably be some delays in my responses!

I’m still not totally sure I understand what you’re saying the problem
is. But maybe you could try this: Replace the
pitch_to_instrument_and_pitches lines with a few different things and
tell me what happens in each of these cases:

pitch_to_instrument_and_pitches = {
“default”: [piano, “p”]
}

pitch_to_instrument_and_pitches = {
“default”: [clarinet, “p”]
}

pitch_to_instrument_and_pitches = {
“default”: [flute, “[p-4, p, p+4, p+8]”]
}

  1. This should be different for notes below and above middle C.

pitch_to_instrument_and_pitches = {
“1-60”: [piano, [40, 48, 56, 64, 72]]
“61-127”: [flute, [70, 71, 72, 73, 74]]
}

If you can tell me what happens in each of these cases (maybe even
record the result?), it would help me to understand the issue.

Best,

Marc

Hello Marc

First of all: full congratulations for the important happening in your life: the birth of your child. Before this message and without news from you for a long time, I was worried about your healthy (maybe corona I was afraid) etc.

I tried to replace your alternatives without any sound or result.
I don’t know what’s happen. see below.
Would it be important to adjust every time I change from SX900 into Roland Go: Keys"? I would think: no.

I replaced all four alternative pitch to instrument and pitches.
For example about option 3:
Traceback (most recent call last):
File “/Users/PaulTimmermans/Desktop/Midinotes> chords. try new.py”, line 26
“default”: [flute, “[p-4, p, p+4, p+8]”]
^
SyntaxError: invalid character in identifier4:

option 4:
pitch_to_instrument_and_pitches = {
“1-60”: [piano, [40, 48, 56, 64, 72]]
“61-127”: [flute, [70, 71, 72, 73, 74]]
}
Each time, there is a message in the shell:
Traceback (most recent call last):
File “/Users/PaulTimmermans/Desktop/Midinotes> chords. try new.py”, line 26
“1-60”: [piano, [40, 48, 56, 64, 72]]
^
SyntaxError: invalid character in identifier

I suggest you send me a new script (with integration GO:Keys and / or SX900)
with your four options integration. I don’t understand why the script of the moonlight sonata seems to be sounding without problems.

Hopely fast reply.

Best thank you in advance.

Paul

Hi Paul — I’ll try to look into this soon, but for now I can say that the SyntaxError was because I forgot to include a comma at the end of the “1-60” line. It should have been:

pitch_to_instrument_and_pitches = {
    “1-60”: [piano, [40, 48, 56, 64, 72]],
    “61-127”: [flute, [70, 71, 72, 73, 74]]
}

Dear Marc

The comma isn’t yet the solution:

Traceback (most recent call last):
File “/Users/PaulTimmermans/Desktop/Midinotes> chords. try new.py”, line 26
“1-60”: [piano, [40, 48, 56, 64, 72]],
^
SyntaxError: invalid character in identifier

Thanks in advance for your help.

Paul

PS I wouldn’t possible to REPLY within the scamsters page. Why? I don’t know.
Clicking on REPLY there wasn’t opening the REPLY window.

Ah, in that case I think it’s the quote character: it should be a simple double quote like this:

pitch_to_instrument_and_pitches = {
    "1-60": [piano, [40, 48, 56, 64, 72]],
    "61-127": [flute, [70, 71, 72, 73, 74]]
}

Sometimes word processors sneakily change simple double-quotes to fancy ones and it breaks the computer program.

Also, am I understanding correctly that you want to be using two different keyboards, “SX900” and “Roland Go: Keys”? If so, can you tell me what prints when you run

from scamp import *
print_available_midi_input_devices()

with each of them plugged in?