# Midilib for Sonic Pi and Generating markov chains from midis

**URL:** <https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646>\
**Category:** Creations & Ideas\
**Created:** [May 27, 2021, 5:18pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646 "2021-05-27T17:18:50Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [May 27, 2021, 5:18pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/1 "2021-05-27T17:18:50Z")

</div>

I created a [fork of minilib](https://github.com/amiika/sonicmidilib) that now runs with ruby 3.x and Sonic Pi.

Also included this [‘forgotten’ feature request](https://github.com/sonic-pi-net/sonic-pi/issues/1029) to my fork which makes it possible to read the midi from the file and create a markov chain from the parsed melody directly in Sonic Pi:

Here is a neverending jazzy version of old Finnish folk tune:

```ruby
# Path to midilib for Sonic Pi
load "~/midilib/sonicpi.rb"

# Path to example midi in the examples folder
test_midi = ENV['HOME']+'/midilib/examples/pinnin_valssi.mid'

use_bpm 180

# Parse to hash
midi_hash = midi_to_hash test_midi

melody = midi_hash[:tracks][0].map {|n| n[:note] }
chords = midi_hash[:tracks][1].map {|n| n[:note] }

sleep = midi_hash[:sleeps][0]
chord_sleep = midi_hash[:sleeps][1]

notes_and_lengths = melody.zip(sleep)
chords_and_lengths = chords.zip(chord_sleep)

# Create markov chains from the melody and the chords
melody_chain = markov(notes_and_lengths, 2)
chord_chain = markov(chords_and_lengths, 1)

with_fx :wobble, phase: 1.0, res: 0.5 do
  in_thread do
    loop do |n|
      a = melody_chain.next || melody_chain.reset
      synth :chiplead, note: a[0], attack: 0.1, release: 0.1, amp: 1
      sleep a[1]
    end
  end
  
  loop do |n|
    b = chord_chain.next || chord_chain.reset
    synth :chipbass, note: b[0], attack: 0.1, release: 0.1, amp: 1
    sleep b[1]
  end
end

```

To run this first “clone [https://github.com/amiika/sonicmidilib.git](https://github.com/amiika/sonicmidilib.git)” in your user folder and run the example in Sonic Pi (Or clone elsewhere and change the paths).

EDIT: Changed repository and cleaning up the fork to create a pull request to the original.

---

<div class="post-metadata">

**Author:** ![Nechoj](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/nechoj/32/1814_2.png) [@Nechoj](https://in-thread.sonic-pi.net/u/Nechoj)\
**Post date:** [May 27, 2021, 7:58pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/2 "2021-05-27T19:58:18Z")

</div>

Thanks for this, looks great! It will take some time to understand all the code behind…

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [May 30, 2021, 7:46pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/3 "2021-05-30T19:46:01Z")

</div>

My pull request to midilib went trough which means you can now use midilib with Sonic Pi also from the original repository:

[jimm/midilib: Pure Ruby MIDI file and event manipulation library (github.com)](https://github.com/jimm/midilib)

---

<div class="post-metadata">

**Author:** ![Bohrbug](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/bohrbug/32/73_2.png) [@Bohrbug](https://in-thread.sonic-pi.net/u/Bohrbug)\
**Post date:** [June 4, 2021, 7:09pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/4 "2021-06-04T19:09:14Z")

</div>

Hi @amiika, this looks awesome and I would like to try this out, so I’ve cloned the original midilib repository, and installed midilib as a gem. Then I had to add your ‘pinnin\_valssi.mid and sonicpi.rb files, that where not included from your fork. I’ve changed the paths successfully, no problems there, but when evaluating the example I get stuck at the ‘#create markov chains’ part. I get this → undefined method `markov’ for Runtime:SonicPiLang.  
Do I have to define the markov function somehow first to run this ? Or something else to get this going ? Cheers!

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [June 4, 2021, 9:17pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/5 "2021-06-04T21:17:13Z")

</div>

My original fork should still be working. I added the markov.rb from the Sonic Pi Feature request to lib folder in [there](https://github.com/amiika/sonicmidilib/tree/main/lib/midilib/sonic_pi).

I also made the pull request to the original, which means it’s working when required from the path. You don’t have to install the gem as those does not work with Sonic Pi.

Meanwhile … made some new changes to the midilib to include sleep and sustain, which would make it easier to use with Sonic Pi. These changes are in my [new fork](https://github.com/amiika/midilib) and don’t know yet if those will be accepted:

```ruby
load "~/midilib/lib/midilib.rb"

use_bpm 180

# Path to midi
TEST_MIDI = ENV['HOME']+'/sonicmidilib/examples/pinnin_valssi.mid'

seq = MIDI::Sequence.new()

File.open(TEST_MIDI, 'rb') do |file|
  seq.read(file)
end

seq.each_with_index do |track,idx|
  track = track.select { |e| e.is_a?(MIDI::NoteOn) }
  in_thread do
    track.each do |e|
      play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2
      sleep e.sleep
    end
  end
end

```

---

<div class="post-metadata">

**Author:** ![Bohrbug](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/bohrbug/32/73_2.png) [@Bohrbug](https://in-thread.sonic-pi.net/u/Bohrbug)\
**Post date:** [June 4, 2021, 10:34pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/6 "2021-06-04T22:34:13Z")

</div>

Ok, thanks! I’ll try to use your forks instead and see if I get things going. Cheers!

---

<div class="post-metadata">

**Author:** ![robin.newman](https://avatars.discourse-cdn.com/v4/letter/r/76d3ee/32.png) [@robin.newman](https://in-thread.sonic-pi.net/u/robin.newman)\
**Post date:** [June 5, 2021, 1:35pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/7 "2021-06-05T13:35:59Z")

</div>

One thing I noticed with your excellent port is that it can’t handle multiple parts if they start playing at different times from the start. EG in a fugue part 2 might come in 4 bars later than part 1. I deal with this with midi files exported from MuscScore by putting in dummy very high or very low notes each of a bars duration instead of each bar rest, then running a conversion script written in procesing to Sonic Pi code, then substituting the dummy notes back with bars rests. A bit tedious but it works.

---

<div class="post-metadata">

**Author:** ![Bohrbug](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/bohrbug/32/73_2.png) [@Bohrbug](https://in-thread.sonic-pi.net/u/Bohrbug)\
**Post date:** [June 5, 2021, 1:36pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/8 "2021-06-05T13:36:43Z")

</div>

@amiika Works from your original fork! Going to investigate further now . . .

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [June 5, 2021, 5:18pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/9 "2021-06-05T17:18:57Z")

</div>

That should not be a problem, at least in my new fork, but I have to test it out. Could you post a link to some example from Musescore? You can get time\_from\_start value from the Events, so I quess you have to put a condition that sleeps in the beginning if time\_from\_start\>0.

---

<div class="post-metadata">

**Author:** ![robin.newman](https://avatars.discourse-cdn.com/v4/letter/r/76d3ee/32.png) [@robin.newman](https://in-thread.sonic-pi.net/u/robin.newman)\
**Post date:** [June 5, 2021, 7:12pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/10 "2021-06-05T19:12:27Z")

</div>

Sent you a message with a link  
EDIT  
also, some files have tempo changes within them. Will it handle those?

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [June 6, 2021, 10:39am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/11 "2021-06-06T10:39:00Z")

</div>

Great piece. I made some changes to [my midilib fork](https://github.com/amiika/midilib) to make it work. Renamed sleep to wait and switched the order around. It makes more sense this way.

Playing multiple parts:

```ruby
load "~/midilib/lib/midilib.rb"

test_bpm = 200

use_bpm test_bpm

# Path to midi
TEST_MIDI = ENV['HOME']+'/midis/Danse_Macabre_Op.40.mid'

seq = MIDI::Sequence.new()

File.open(TEST_MIDI, 'rb') do |file|
  seq.read(file)
end

seq.each_with_index do |track,idx|
  track = track.select { |e| e.is_a?(MIDI::NoteOn) }
  in_thread do
    track.each do |e|
      with_synth :piano do
        sleep e.wait
        play e.note, release: e.sustain*0.8, decay: e.sustain*0.2
      end
    end
  end
end

```

Also added bpm function to tempo, which does the same thing as @theibbster did earlier with his script. Except im using separate threads for different types of events. Example with tempo changes:

```ruby
load "~/midilib/lib/midilib.rb"

test_bpm = 80

use_bpm test_bpm

# Path to midi
TEST_MIDI = ENV['HOME']+'/midis/Tempo_Changes_test.mid'

seq = MIDI::Sequence.new()

File.open(TEST_MIDI, 'rb') do |file|
  seq.read(file)
end

seq.each_with_index do |track,idx|
  tempo = track.select { |e| e.is_a?(MIDI::Tempo) }
  track = track.select { |e| e.is_a?(MIDI::NoteOn) }
  in_thread do
    track.each do |e|
      with_bpm test_bpm do
        sleep e.wait
        play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2
      end
    end
  end
  in_thread do
    tempo.each do |t|
      sleep t.wait
      test_bpm = test_bpm+(t.bpm-test_bpm)
    end
  end
end

```

Potentially you could also have different events like Pitch bend, Program or Control change Events also.

---

<div class="post-metadata">

**Author:** ![robin.newman](https://avatars.discourse-cdn.com/v4/letter/r/76d3ee/32.png) [@robin.newman](https://in-thread.sonic-pi.net/u/robin.newman)\
**Post date:** [June 6, 2021, 1:28pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/12 "2021-06-06T13:28:21Z")

</div>

Danse macabre sounds great! Well done.

---

<div class="post-metadata">

**Author:** ![magnusviri](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/magnusviri/32/2248_2.png) [@magnusviri](https://in-thread.sonic-pi.net/u/magnusviri)\
**Post date:** [March 30, 2022, 4:20am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/13 "2022-03-30T04:20:41Z")

</div>

I just wrote my own version of this. I thought of posting it to the forum and so I searched to see which category to post it to and then I found this post. I wish I knew about this before, but I am glad for the experience of writing my own version, especially because it re-familiarized me with Ruby. But I think yours might be better than mine. Here’s mine.

```ruby
# This depends on https://github.com/jimm/midilib
#
# gem install midilib

midi_file = "/Users/jeng/Dropbox/Music/bach-midi/fuguecm.mid"

$LOAD_PATH.append("/Library/Ruby/Gems/2.6.0/gems/midilib-2.0.5/lib/")
require 'midilib/sequence'
require 'midilib/io/midifile'
seq = MIDI::Sequence.new
File.open(midi_file, 'rb') { | file | seq.read(file) }
seq.each_with_index do |track,ii|
	label = ("midi"+ii.to_s).to_sym
	puts "track name '#{track.name}', instrument name '#{track.instrument}', #{track.events.length} events, label #{label}"
	events = []
	time_divisor = 1000.0
	bpm = -1
	track.each_with_index do |event, jj|
		if MIDI::NoteEvent === event
			new_event = {
				:note => event.note,
				:time_from_start => event.time_from_start/time_divisor,
				:delta_time => event.delta_time/time_divisor,
				:status => event.status,
				:velocity => event.velocity
			}
			if events.length > 0
				events[events.length-1][:sustain] = event.delta_time/time_divisor
			end
			events.append(new_event)			
		elsif MIDI::Tempo === event
			new_event = {
				:bpm => MIDI::Tempo.mpq_to_bpm(event.tempo),
			}
			puts(event)
			events.append(new_event)
		else
			puts(event)
		end
	end
	begin
		thread_ii = 0
		if bpm > 0
			use_bpm bpm
		end
		live_loop label do
			use_synth :piano
			event = events[thread_ii]
			if event != nil and event.has_key? :status
				if event.has_key? :delta_time
					delta_time = event[:delta_time]
					if delta_time > 0
						sleep delta_time
					else
						sleep 0.000001
					end
				else
					puts("Missing delta_time")
					sleep 1
				end
				if event[:status] == 144
					play event[:note], velocity: event[:velocity], sustain: event[:sustain]
					puts("#{label} #{thread_ii} sleep #{delta_time} play #{event[:note]} velocity #{event[:velocity]} sustain #{event[:sustain]}")
				elsif event[:status] != 128
					puts(event[:status], "stop 2")
					stop
				end
			else
				sleep 0.001
				puts ("stop 3")
				stop
			end
			thread_ii+=1
			if thread_ii>=events.length
				puts ("stop 1")
				stop
			end
		end
	rescue
		events.each do |event|
			puts(event)
		end
	end
end

```

I am curious, your version of sonicmidilib says it’s compatible with Sonic Pi, but somehow I got the original project to work by adding the directory where it is installed to $LOAD\_PATH. Is that why it wasn’t compatible? I was worried about compatibility problems between Ruby 2.6 and Ruby 3, but I didn’t notice any problems. I believe you also added some methods to make it easier to use with Sonic Pi.

Is there any chance something like these scripts can be documented so people don’t duplicate work? Or even better, it would be awesome if there was a Sonic Pi package manager that allowed downloading additions like this.

I’m not sure what to do with my script now. I had looked for midi file playback but I only found the midicsv and some other methods that I eventually decided not to do (but was grateful for the ideas). I will probably want to have an intermediate step that I can modify specifically for Sonic Pi, but for now I’d rather have Sonic Pi read and playback the midi file without any intermediate steps (like converting to csv).

---

<div class="post-metadata">

**Author:** ![magnusviri](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/magnusviri/32/2248_2.png) [@magnusviri](https://in-thread.sonic-pi.net/u/magnusviri)\
**Post date:** [March 30, 2022, 5:00am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/14 "2022-03-30T05:00:48Z")

</div>

> [@amiika](#):
>
> Pitch bend, Program or Control change Events also.

I need this. I’m still learning Sonic Pi, but I believe I would have to play a note, save a reference to it, then modify that reference with `control`. It looks like midilib has these events ([source](https://www.rubydoc.info/gems/midilib/2.0.5/MIDI/Realtime)).

MetaEvent

- KeySig
- Marker
- Tempo
- TimeSig

NoteEvent

- NoteOff
- NoteOn
- PolyPressure

ChannelEvent

- ChannelPressure
- Controller
- NoteEvent
- PitchBend
- ProgramChange

It looks like each synth has a different set of opts that can be controlled. So I’d have to have some sort of connecting or binding that would bind a midi control to the synth opt. Maybe the best way would be to use anonymous functions for the actual note playing, with parameters for the midi events.

I don’t see a list of sample opts but it appears there are many.

I’m guessing I’d want to be able to control every effect that is possible with `with_fx`. That’s a lot. I guess I’ve got a lot of fooling around to do to see if I can get this working.

Does anyone have a midi file that manipulates all of these midi controls?

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [March 30, 2022, 6:02am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/15 "2022-03-30T06:02:55Z")

</div>

> [@magnusviri](#):
>
> I am curious, your version of sonicmidilib says it’s compatible with Sonic Pi, but somehow I got the original project to work by adding the directory where it is installed to $LOAD\_PATH. Is that why it wasn’t compatible? I was worried about compatibility problems between Ruby 2.6 and Ruby 3, but I didn’t notice any problems. I believe you also added some methods to make it easier to use with Sonic Pi.

At the time of the post the original project did not work. I made some fixes and did a pull request to the original so it works now. However, it does not include those [helper methods](https://github.com/amiika/sonicmidilib/blob/main/sonicpi.rb) i made for Sonic Pi.

> [@magnusviri](#):
>
> Or even better, it would be awesome if there was a Sonic Pi package manager that allowed downloading additions like this.

For now, there is no such thing, but @samaaron has hinted that maybe later … might be something for elixir only but i don’t really know 🙂

---

<div class="post-metadata">

**Author:** ![magnusviri](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/magnusviri/32/2248_2.png) [@magnusviri](https://in-thread.sonic-pi.net/u/magnusviri)\
**Post date:** [March 31, 2022, 4:21am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/16 "2022-03-31T04:21:04Z")

</div>

I found [this project](https://github.com/jazz-soft/test-midi-files/tree/main/midi) with lots of test midi files. I downloaded them and tried midilib against them all. I’m also testing for these note events: ChannelPressure, Controller, KeySig, Marker, NoteOn, PitchBend, ProgramChange, Tempo, TimeSig. Here’s my code. I run this script in the Terminal, not Sonic-Pi.

```ruby
load "/Users/jeng/midilib/lib/midilib.rb"

def open_midi_file(midi_file)
	puts(midi_file)
	seq = MIDI::Sequence.new()
	File.open(midi_file, 'rb') do |file|
		seq.read(file)
	end
	test_bpm = 80
	seq.each_with_index do |track,idx|
		tempoTrack = track.select { |e| e.is_a?(MIDI::Tempo) }
		noteTrack = track.select { |e| e.is_a?(MIDI::NoteOn) }
		keySigTrack = track.select { |e| e.is_a?(MIDI::KeySig) }
		markerTrack = track.select { |e| e.is_a?(MIDI::Marker) }
		timeSigTrack = track.select { |e| e.is_a?(MIDI::TimeSig) }
		channelPressureTrack = track.select { |e| e.is_a?(MIDI::ChannelPressure) }
		controllerTrack = track.select { |e| e.is_a?(MIDI::Controller) }
		pitchBendTrack = track.select { |e| e.is_a?(MIDI::PitchBend) }
		programChangeTrack = track.select { |e| e.is_a?(MIDI::ProgramChange) }
		puts("------------------------")
		if(tempoTrack.length > 0)
			puts("tempoTrack")
			puts(tempoTrack)
		end
		if(noteTrack.length > 0)
			puts("noteTrack")
			puts(noteTrack.length)
		end
		if(keySigTrack.length > 0)
			puts("keySigTrack")
			puts(keySigTrack)
		end
		if(markerTrack.length > 0)
			puts("markerTrack")
			puts(markerTrack.length)
		end
		if(timeSigTrack.length > 0)
			puts("timeSigTrack")
			puts(timeSigTrack)
		end
		if(channelPressureTrack.length > 0)
			puts("channelPressureTrack")
			puts(channelPressureTrack.length)
		end
		if(controllerTrack.length > 0)
			puts("controllerTrack")
			puts(controllerTrack.length)
		end
		if(pitchBendTrack.length > 0)
			puts("pitchBendTrack")
			puts(pitchBendTrack.length)
		end
		if(programChangeTrack.length > 0)
			puts("programChangeTrack")
			puts(programChangeTrack.length)
		end
	end
end
midi_files = Dir["/Users/jeng/test-midi-files/midi/*"]
midi_files.sort.each { |midi_file|
	open_midi_file(midi_file)
}

```

It fails 19 tests, mainly tests involving corrupted midi files.

test-all-gm-percussion.mid  
test-corrupt-file-missing-byte.mid  
test-illegal-message-all.mid  
test-illegal-message-f1-xx.mid  
test-illegal-message-f2-xx-xx.mid  
test-illegal-message-f3-xx.mid  
test-illegal-message-f4.mid  
test-illegal-message-f5.mid  
test-illegal-message-f6.mid  
test-illegal-message-f8.mid  
test-illegal-message-f9.mid  
test-illegal-message-fa.mid  
test-illegal-message-fb.mid  
test-illegal-message-fc.mid  
test-illegal-message-fd.mid  
test-illegal-message-fe.mid  
test-non-midi-track.mid  
test-sysex-7e-06-01-id-request.mid  
test-syx-7e-06-01-id-request.syx

It passed 43 tests, though. These 2 files include the types of control I want to respond to.

test-rpn-00-00-pitch-bend-range.mid  
test-rpn-00-05-modulation-depth-range.mid

Now I just need to figure out how to get Sonic-Pi to manipulate the signal based on the control data. I think I’d probably want to do something like this.

```ruby
	in_thread do
		polyPressureTrack.each do |t|
			sleep t.wait
			control get(:v), amp: t.value/127.0
		end
	end
	with_fx :level,amp: 1 do |v|
		set :v,v
		in_thread do
			noteTrack.each do |e|
				with_bpm test_bpm do
					sleep e.wait
					play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2, amp: e.velocity/127.0
					# Do something with v
				end
			end
		end
	end

```

---

<div class="post-metadata">

**Author:** ![magnusviri](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/magnusviri/32/2248_2.png) [@magnusviri](https://in-thread.sonic-pi.net/u/magnusviri)\
**Post date:** [April 1, 2022, 5:50am UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/17 "2022-04-01T05:50:59Z")

</div>

Using [this file](https://github.com/jazz-soft/test-midi-files/blob/main/midi/test-rpn-00-00-pitch-bend-range.mid) I was able to get a sort of pitch bend. It’s missing the slide and the values are wrong.

```ruby
mypath="/Users/jeng/Dropbox/Github/sonic-pi/sonic-pi-magnusviri/"
load mypath+"day007 29/amiika/midilib/lib/midilib.rb"

def open_midi_file(midi_file)
  seq = MIDI::Sequence.new()
  File.open(midi_file, 'rb') do |file|
    seq.read(file)
  end
  seq.each do |track|
    noteTrack = track.select { |e| e.is_a?(MIDI::NoteOn) }
    pitchBendTrack = track.select { |e| e.is_a?(MIDI::PitchBend) }
    use_synth :piano
    with_fx :pitch_shift do |bend|
      live_loop :notes do
        noteTrack.each do |e|
          sleep e.wait
          if e.velocity > 0
            play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2, amp: e.velocity/127.0
          else
            play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2
          end
        end
      end
      if(pitchBendTrack.length > 0)
        live_loop :pitch_mod do
          pitchBendTrack.each do |t|
            sleep t.wait
            control bend, pitch: t.value/127.0
          end
        end
      end
    end
  end
  puts("end")
end

midi_file = "/Users/jeng/test-midi-files/midi/test-rpn-00-00-pitch-bend-range.mid"
open_midi_file(midi_file)

```

---

<div class="post-metadata">

**Author:** ![magnusviri](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/magnusviri/32/2248_2.png) [@magnusviri](https://in-thread.sonic-pi.net/u/magnusviri)\
**Post date:** [April 1, 2022, 6:34pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/18 "2022-04-01T18:34:45Z")

</div>

I think I’m done with this for now. I made a [GitHub repo](https://github.com/magnusviri/sonic-pi-magnusviri) and put it there. Here’s the main function.

```ruby
load ENV['HOME']+"/midilib/lib/midilib.rb"

def play_midi_file(midi_file, play_tracks_func)
  seq = MIDI::Sequence.new()
  File.open(midi_file, 'rb') do |file|
    seq.read(file)
  end
  seq.each do |track|
    play_tracks_func[{
      'channelPressureTrack'=>track.select { |e| e.is_a?(MIDI::ChannelPressure) },
      'controllerTrack'=>track.select { |e| e.is_a?(MIDI::Controller) },
      'keySigTrack'=>track.select { |e| e.is_a?(MIDI::KeySig) },
      'markerTrack'=>track.select { |e| e.is_a?(MIDI::Marker) },
      'noteTrack'=>track.select { |e| e.is_a?(MIDI::NoteOn) },
      'pitchBendTrack'=>track.select { |e| e.is_a?(MIDI::PitchBend) },
      'polyPressureTrack'=>track.select { |e| e.is_a?(MIDI::PolyPressure) },
      'programChangeTrack'=>track.select { |e| e.is_a?(MIDI::ProgramChange) },
      'tempoTrack'=>track.select { |e| e.is_a?(MIDI::Tempo) },
      'timeSigTrack'=>track.select { |e| e.is_a?(MIDI::TimeSig) }
    }]
  end
end

```

And this is how you use it.

```ruby
midi_file = ENV['HOME']+"/test-midi-files/midi/test-rpn-00-00-pitch-bend-range.mid"

play_midi_file(midi_file, lambda{ |tracks|
  use_synth :saw
  with_fx :pitch_shift do |fx1|
    live_loop :notes do
      tracks['noteTrack'].each do |e|
        sleep e.wait
        play e.note, sustain: e.sustain*0.8, decay: e.sustain*0.2
      end
    end
    if(tracks['pitchBendTrack'].length > 0)
      live_loop :fx1 do
        tracks['pitchBendTrack'].each do |t|
          sleep t.wait
          control fx1, pitch: t.value/127.0
        end
      end
    end
  end
})

```

By putting the playback code in a lambda, it’s easier to ignore the midi code and focus on the Sonic Pi code. It would be nice to get all of the midi controls to do what they’re supposed to, but I’m still really new to Sonic Pi and I could spend weeks working on it. But I am only interested in a few of the midi parameters and fx so I’m not going to figure it all out. If someone else does, that would be awesome. Otherwise, I think I’m done with this for now. Thank you to everyone who has done the work that led to this.

---

<div class="post-metadata">

**Author:** ![amiika](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/amiika/32/2401_2.png) [@amiika](https://in-thread.sonic-pi.net/u/amiika)\
**Post date:** [April 2, 2022, 2:38pm UTC](https://in-thread.sonic-pi.net/t/midilib-for-sonic-pi-and-generating-markov-chains-from-midis/5646/19 "2022-04-02T14:38:58Z")

</div>

Great work! Thanks. I’m sure it will come useful one day for others as well 🙂

I’v havent had much time to play with midi playback since most of my time has gone to developing Ziffers. I’ll come back to this at some point and one thing I was planning to do earlier was also support for writing midi from Sonic Pi. Ziffers to midi or saving other generated melodies from markov, lindermayer and other generative stuff would be great.
