18

I am writing a script to convert a picture into MIDI notes based on the RGBA values of the individual pixels. However, I cannot seem to get the last step working, which is to actually output the notes to a file.

I have tried using the MIDIUtil library, however its documentation is not the greatest and I can't seem to figure it out.

If anyone could tell me how to sequence the notes (so that they don't all begin at the beginning) it would be greatly appreciated.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
pstap92
  • 191
  • 1
  • 1
  • 4

3 Answers3

32

Looking at the sample, something like

from midiutil.MidiFile import MIDIFile

# create your MIDI object
mf = MIDIFile(1)     # only 1 track
track = 0   # the only track

time = 0    # start at the beginning
mf.addTrackName(track, time, "Sample Track")
mf.addTempo(track, time, 120)

# add some notes
channel = 0
volume = 100

pitch = 60           # C4 (middle C)
time = 0             # start on beat 0
duration = 1         # 1 beat long
mf.addNote(track, channel, pitch, time, duration, volume)

pitch = 64           # E4
time = 2             # start on beat 2
duration = 1         # 1 beat long
mf.addNote(track, channel, pitch, time, duration, volume)

pitch = 67           # G4
time = 4             # start on beat 4
duration = 1         # 1 beat long
mf.addNote(track, channel, pitch, time, duration, volume)

# write it to disk
with open("output.mid", 'wb') as outf:
    mf.writeFile(outf)
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
26

I know this is an old post, but I'm the author of the library, and I wanted to mention that python 2 and 3 support have now been unified and with the demise of Google Code the code is now hosted on GitHub and can be installed via pip, ie:

pip install MIDIUtil

Documentation is available at Read The Docs.

(Tried to comment but I lacked the experience points.)

The end-of-track message is created automatically when the file is written to disk.

Mark Wirt
  • 261
  • 3
  • 2
  • 3
    Hi Mark. I have a file of consecutive time-stamps , and for each time-stamp in the file, I want to play a note, say C64 Maj. There is no tempo, like an improvisation, the stamps are random...how do I get them "as is" onto a midi file? – ajsp Aug 03 '17 at 09:23
  • MIDI is able to play notes surprisingly quickly and surprisingly accurately. I had a similar situation, and what I ended up doing was picking the tempo to be extremely quick and then just rounding the timestamps to that fast tempo. The faster the tempo, the more negligible the rounding errors are. – Pro Q Jul 17 '19 at 22:35
0

For any one looking at this in 2023, also take a look at Computil package which makes generating midi files very easy:

# import the module
>>> import cu

# initialize the module
>>> cu.rt.init()

# create a single note
>>> c4 = cu.note(knum=60)

# and put it in a list and send it to the processor to play it
>>> cu.proc([c4])

# the full signature of the note function has the parameters:
# knum: the midi key number
# onset: is the ongoing time expressed in seconds, with respect to the process start time 0 (default is 0, which means now)
# dur: the duration of the note
# chnl: the midi channel to play the note on
# vel: the dynamic of the note

# now create a A minor chord with a duration of 2 seconds, 
# and play it 1 second after the processor starts
>>> a_min = cu.chord(knums=(69, 72, 76), onset=1, dur=2)

# and send it to the processor to play it
>>> cu.proc([a_min])

# now let us give the processor a list of notes/chords to play:
# the chromatic scale starting from the middle c upwards.
>>> voice = [cu.note(knum=60 + i, onset=i, dur=0.1) for i in range(12)]
>>> cu.proc([voice])

# playing polyphony is as easy as passing multiple lists of note/chords
# to the processor:
>>> voice1 = [cu.note(knum=60 + i, onset=i, dur=0.5) for i in range(12)]
>>> voice2 = [cu.note(knum=48 + i, onset=i + 0.5, dur=0.5) for i in range(12)]
>>> voice3 = [cu.chord(knums=[60+i, 60+i+4, 60+i+7], onset=i + 0.2, dur=0.5) for i in range(12)]

>>> cu.proc([voice1, voice2, voice3])

# If you want to write a midi file to the disk instead of playing it back
# pass a path string as the second argument to the processor
>>> cu.proc([voice1, voice2, voice3], "/tmp/my-polyphony.mid")
Student
  • 708
  • 4
  • 11