Choose MIDI Chord

Hi, everyone.

Why does the following not work?

chords = [(chord :C, :minor7), (chord :Ab, :major7)].ring

thisChord1 = chords.choose

live_loop :midich do
  midi thisChord1, release: 0.6
  sleep 0.5
end

I was hoping to swap the “play” in my existing code with “midi”, but no dice.

I can get midi chords to play, but only if I choose those chords ahead of time. How to I play randomly selected midi chords the way I am able to with the built in synths?

Thank you so much,
Jack

Hi @jackdire,

before answering, would you please tell us if you only want to play this chords inside sonic pi or if you really want to send chords to another midi device, software ?

For play them into sonic pi, just try this :

chords = [(chord :C, :minor7), (chord :Ab, :major7)].ring



live_loop :without_midi do
  play chords.choose, release: 0.6
  sleep 0.5
end

After some research

Sonic Pi does not support chords to be played via midi natively. But we can create a function to achieve that.

@robin.newman proposed a solution in 2017 https://github.com/samaaron/sonic-pi/issues/1691

so you can do that

 use_midi_defaults port: "midi_through_port-0", channel: 1


define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

live_loop :play_chords_via_midi do
  midi_chord chords.choose, release: 0.6
  sleep 0.5
end

If you are on windows, you have to create a midi loop via https://www.tobias-erichsen.de/software/loopmidi.html

au plaisir !

Thanks @nlb I was just about to dig out that routine and post it!

1 Like

you’re welcome “rendre à César ce qui appartient à César”.

but why this feature is not natively supported @samaaron ?

Thank you so much for the responses. I am using this to fire off some Native Instruments in Ableton as well as a hardware Minitaur, Sirin, SH-01A, and Erebus through Ableton. I hope.

I couldn’t get your code to send midi as written, so I messed around and made these changes, which work now:

chords = [(chord :C, :minor7), (chord :Ab, :major7)].ring

define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

live_loop :play_chords_via_midi do
  midi_chord chords.choose, release: 0.6, channel: 16
  sleep 0.5
end

What will I beak by nuking the use_midi_defaults line? I’m not assigning a port, and the IAC is picking it all up. Will this break something later?

And where can I find information on what is happening in this block:

define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

A few terms and principles in there are new to me. I finally got my head around the non-midi stuff somewhat yesterday, but the *args and |note| stuff is new to me now.

Thanks again. I really appreciate it.

To further illustrate my total lack of understanding in that mystery block, I can’t see what is causing the following to play consistently random chords with each loop when the chord is chosen before the loop (a technique that worked for me with non-midi code yesterday).

chords = [(chord :C, :minor7), (chord :Ab, :major7), (chord :Eb, :major7), (chord :Bb, "7")].ring

thisChord1 = chords.choose

define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

live_loop :channel16 do
  midi_chord thisChord1, release: 0.6, channel: 16
  sleep 0.5
end

What in that *arg |note| sorcery block is changing all of this?

Thanks!

To make all of this even weirder, I deleted the *args block and it is still firing off midi in Ableton:

chords = [(chord :C, :minor7), (chord :Ab, :major7), (chord :Eb, :major7), (chord :Bb, "7")].ring

thisChord1 = chords.choose

live_loop :channel16 do
  midi_chord thisChord1, release: 0.6, channel: 16
  sleep 0.5
end

So strange.

chords = [(chord :C, :minor7), (chord :Ab, :major7), (chord :Eb, :major7), (chord :Bb, “7”)].ring

  • the chosen is chord is memorised into a variable
thisChord1 = chords.choose 

there the chord is chosen and is memorized into the variable thisChord1

  • a personal function midi_chord
define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

then we create a “personal” function or method call it as you want.
we name it midi_chord pretty name no ?
and this function needs arguments, parameters if you prefer.

so the notes included into the chord chosen
and then optional parameters, indeed we can give zero or more parameters so we use the *

  • each : to loop every note of the chord
notes.each do |note|
    midi note, *args
  end

But how can i guess the parameters the midi instruction can receive ?
I encourage you to to read some docs :-). The sonic pi Help is a “merveille”

To do that place your cursor in the word midi and type F1 then you will reach the good page given you an explanation about the midi instruction used into midi_chord function.

  • the midi_chord function is called
live_loop :channel16 do
  midi_chord thisChord1, release: 0.6, channel: 16 
 sleep 0.5
end

there the midi_chord function is called and the variable thisChord1 is given as first argument, then release: 0.6, channel:16 also as following arguments.

image

Hope this can help you !

By the way, do you manage to get Ableton play something or not at all. Which os do you use ? Have you made a midi loop ?

Thank you for all of this! That f1 trick for help was a big missing link for me.

But why is the chord randomly changing with every loop when the thisChord1 variable memorizes a chord outside of the live loop? That does not happen with the internal synths.

I have gotten Ableton on OSX to receive all of the midi being generated, thanks to some digging I did on here last night. The software synth in Ableton is working great. I just need the live loop to not change the chord played by the thisChord1 variable each time.

Thanks again!

euh ? to be sure you don’t press the run button once your script is running ?
is it your whole code ? Give us the code causing issue ?

Sorry, it’s this code from a few posts back. I think I sent it while you were replying, so you may not have seen it:

chords = [(chord :C, :minor7), (chord :Ab, :major7), (chord :Eb, :major7), (chord :Bb, "7")].ring

thisChord1 = chords.choose

define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

live_loop :channel16 do
  midi_chord thisChord1, release: 0.6, channel: 16
  sleep 0.5
end

When I run this, the chord changes with each loop. I can’t see what would cause that since thisChord1 is locked in before the loop.

as you say it’s strange :slight_smile: . I test your code and the same chord is played with sp3.2.2 on ubuntu 18.04.04.
So what sonic pi version do you use ?
Is there somebody on mac, a mac u$er :slight_smile: ?

UPDATE: I dropped a print thisChord1 inside the loop, and discovered the chord isn’t actually changing. Which is good. It just sounds like it is.

So I threw a print count into the loop as well to see what was happening in each loop. Check it out:

{run: 1, time: 0.0, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.0, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.0, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.0, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.5, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.5, thread: :live_loop_channel16}
 ├─ (ring <SonicPi::Chord :Bb :7 [70, 74, 77, 80])
 └─ 2
 
{run: 1, time: 0.5, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.5, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 0.5, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.0, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.0, thread: :live_loop_channel16}
 ├─ (ring <SonicPi::Chord :Bb :7 [70, 74, 77, 80])
 └─ 3
 
{run: 1, time: 1.0, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.0, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.0, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.5, thread: :live_loop_channel16}
 ├─ (ring <SonicPi::Chord :Bb :7 [70, 74, 77, 80])
 └─ 4
 
{run: 1, time: 1.5, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.5, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.5, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 1.5, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.0, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.0, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.0, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.0, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.0, thread: :live_loop_channel16}
 ├─ (ring <SonicPi::Chord :Bb :7 [70, 74, 77, 80])
 └─ 5
 
=> Stopping all runs...

=> Stopping run 1

{run: 1, time: 2.5, thread: :live_loop_channel16}
 └─ midi 80, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.5, thread: :live_loop_channel16}
 └─ midi 74, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.5, thread: :live_loop_channel16}
 └─ midi 70, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.5, thread: :live_loop_channel16}
 └─ midi 77, 127, sustain: 1.0, port: "*", channel: 16
 
{run: 1, time: 2.5, thread: :live_loop_channel16}
 ├─ (ring <SonicPi::Chord :Bb :7 [70, 74, 77, 80])
 └─ 6

The chord sounds different each time because the midi_chord function is handling/playing that chord’s notes so differently each time. It there something I can do to make it more consistent?

Here is the full code:

chords = [(chord :C, :minor7), (chord :Ab, :major7), (chord :Eb, :major7), (chord :Bb, "7")].ring

thisChord1 = chords.choose
theCount = 1

define :midi_chord do |notes, *args|
  notes.each do |note|
    midi note, *args
  end
end

live_loop :channel16 do
  midi_chord thisChord1, release: 0.6, channel: 16
  sleep 0.5
  print thisChord1
  theCount = theCount+1
  print theCount
end

not sure you can rely on the order of output with print but i may be mistaken.

what if you change the instrument midi into Ableton ? and please give us a screenshot of midi note into ableton by record the track ?

Ah, I had not considered that the output sequence may be inaccurate. Good idea with recording the midi notes. Here is what came in:

I then thought maybe the timing of the notes was causing the chord sound to change each time, so I quantized the recorded midi and aligned all of the notes. Unfortunately, that didn’t help. The sound still changes.

I am testing with random built-in software synths in Ableton. I have tested it with about 5 or 6 now, and they all behave the same. The chord keeps changing sound.

why other pink notes than the notes you sent via sonic pi ?
otherwise i don’t see why and let it the question to others people.
Cheers

1 Like

I just loaded up Hybrid Keys by Native Instruments, and it sounds great. The chord sounds the same each time. I will test with other plugins, but I have run it multiple times with multiple random seeds just now on Hybrid Keys, and it works beautifully. So it looks like this technique very much depends on the software synth being used.

Fingers crossed that the SH-01A, Reface DX, and Digitone hardware synths behave as well as Hybrid Keys. In typing this I realized this won’t be a problem on the monophonic Minitaur and Sirin, so at least I’m good there.

Thanks again for all of your help with this. It means a lot to me.

1 Like

And yeah I don’t know where those other droney notes are coming from. I’m about to test all of that out. :slight_smile:

I just recorded in Ableton without Sonic Pi running, and it is capturing those extra notes. So hey future people looking at this, the code is not making those extra notes happen. I have some kind of midi leak somewhere on my ship. I’ll start poking around to find it.