Skip to content

Instantly share code, notes, and snippets.

@arnaldorusso
Forked from jiaaro/Generate WAV from MIDI.md
Last active January 22, 2016 17:02
Show Gist options
  • Save arnaldorusso/48b47a23675606cd16d9 to your computer and use it in GitHub Desktop.
Save arnaldorusso/48b47a23675606cd16d9 to your computer and use it in GitHub Desktop.
Generate a wave file from a MIDI file with Pydub

Simple example of rendering a midi file with Pydub

Basically, this takes a MIDI input file (I just googled and grabbed one of Maroon 5's "Animal" – I know, I know) and generates a WAV file.

NOTE: This is the slowest midi rendering program I have ever seen!

Dependencies:

In this example I an rendering a MIDI file of Maroon 5's "Animals" from http://www.free-midi.org

ok, ok, ok - Here's the output:

Here is the output my program generated (converted to mp3):

output.mp3

Obvious issues:

  • Tempo is hard-coded
  • All instruments are rendered using a Sine wave, including drums. Would be better to use different signal generators depending which instrument is being rendered (Sine is fine for flute, square is closer to saxaphone, etc)
  • This is REALLLY SLOWWW. I haven't looked at performance at all, but it took almost an hour (2014, 2.8 GHz i7, Macbook Pro) to render the wav file with the code above. LOL
from mido import MidiFile
from pydub import AudioSegment
from pydub.generators import Sine
from collections import defaultdict
def note_to_freq(note, concert_A=440.0):
'''
from wikipedia: http://en.wikipedia.org/wiki/MIDI_Tuning_Standard#Frequency_values
'''
return (2.0 ** ((note - 69) / 12.0)) * concert_A
mid = MidiFile("./maroon_5-animals.mid")
output = AudioSegment.silent(mid.length * 1000.0)
tempo = 100 # bpm
def ticks_to_ms(ticks):
tick_ms = (60000.0 / tempo) / mid.ticks_per_beat
return ticks * tick_ms
for track in mid.tracks:
# position of rendering in ms
current_pos = 0.0
current_notes = defaultdict(dict)
# current_notes = {
# channel: {
# note: (start_time, message)
# }
# }
for msg in track:
current_pos += ticks_to_ms(msg.time)
if msg.type == 'note_on':
current_notes[msg.channel][msg.note] = (current_pos, msg)
if msg.type == 'note_off':
start_pos, start_msg = current_notes[msg.channel].pop(msg.note)
duration = current_pos - start_pos
signal_generator = Sine(note_to_freq(msg.note))
rendered = signal_generator.to_audio_segment(duration=duration-50, volume=-20).fade_out(100).fade_in(30)
output = output.overlay(rendered, start_pos)
output.export("animal.wav", format="wav")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment