Skip to main content

Featured

Python programming 8 Music with mouse-drawn lines by PyGame

I revised my previous program "Small interactive music maker" and now melody is made from mouse-drawn lines. Actually, change is not big. Instead of my Fourier series function, two key functions are included as - draw lines with mouse by pygame - from line to fx Drawing lines from below.   https://www.dreamincode.net/forums/topic/401541-buttons-and-sliders-in-pygame/ This must be some pretty fun for kids!   ---------------------------------------------------------------------------------------------- blog 8 Some versions of pygame may have some bug. Need " pip install pygame==2.0.0.dev6 " import pygame, math, sys, time import pygame.midi # use pygame.midi (not Mido) from random import random, choice pygame.init() # initialize Pygame pygame.midi.init() # initialize Pygame MIDI port = pygame.midi.Output(0) # port open port.set_instrument(53, channel = 0) # Voice port.set_instrument(0, channel = 1) # piano port.set_instrument(33, channel = 2) # bass X = 900 ...

Python Programming 4 Piano Roll Animation of MIDI file by Matplotlib

Piano Roll Animation made by Matplotlib Artist blit.

You can read your MIDI file (hopefully any file), play and draw Piano Roll over time (notes shown when played over vertical piano keys).
Remark) Tempo change is not well shown now. I'll update to handle tempo change later.

You can see the example in my Youtube https://www.youtube.com/watch?v=yZ8H1D5n4KI

 

Matplotlib was very slow before. But now, with Artist blit, it is barely fast enough to draw Piano Roll animations. But still need some sleep time adjustments.

--------------------------------------------------------------------------------------------- 
 
# blog 4 program

import mido
import time
from mido import Message, MidiFile, MidiTrack, MetaMessage
from mido_util import *
import matplotlib.pyplot as plt

# open MIDI port to play music
ports = mido.get_output_names()
port = mido.open_output(ports[0])

# import MIDI file
score_name = 'blog03'
score = MidiFile(score_name+'.mid', clip=True)

# MIDI play and show Piano Roll
def play_plot_MIDI(port, score):
    #########################################################################
    # lists for plot
    pitch_list = []
    reverse_pitch_list = [] # for note duration
    on_time_list = []
    off_time_list = []
    draw_timing = []
    play_time = 0
    # note_on time and note_off time
    score_tempo = 100 # initial tempo BPM
    for msg in score:
        if msg.type == "set_tempo":
            score_tempo = msg.tempo/10000 # no tempo change assumed

    for msg in score:
        if not msg.is_meta:
            play_time += msg.time / score_tempo * 100
            if msg.type == "note_on":
                pitch_list.append(msg.channel*1000+msg.note)
                reverse_pitch_list = [msg.channel*1000+msg.note]+reverse_pitch_list
                # note_on time
                on_time_list.append(play_time)
                off_time_list.append(0) # dummy
            if msg.type == "note_off":
                # find latest note_on
                on_idx = len(pitch_list) - reverse_pitch_list.index(msg.channel*1000+msg.note) -1
                # note_off time
                off_time_list[on_idx] = play_time

    # draw timing to show same timing notes together
    for i in range(len(on_time_list)-1):
        draw_timing.append(on_time_list[i] < on_time_list[i+1])
    draw_timing.append(1)

    #########################################################################
    # chart
    fig = plt.figure(figsize=(10,6))
    ax = fig.add_subplot(111)
    # piano roll background
    x = [0,off_time_list[-1]]
    color = "#bbbbbb" # grey
    for i in range(50,88):
        y = [i,i]
        if i % 12 in [1,3,6,8,10]: # piano black keys
            ax.plot(x,y,color,linewidth = 12, alpha=0.2)
        else:
            ax.plot(x,y,"w",linewidth = 12, alpha=1)
    for i in [52.5, 59.5, 64.5, 71.5, 76.5, 83.5]: # line between piano white keys
        ax.plot(x,[i,i],color = "#bbbbbb", linewidth = 1)
    fig.canvas.draw()
    fig.show()
    fig.canvas.blit(ax.bbox)
    fig.canvas.flush_events()

    #########################################################################
    # play and draw MIDI notes
    i = 0
    time1 = time.time() # to adjutst sleep time because matplotlib takes time
    for msg in score:
        if not msg.is_meta:
            # sleep msg.time with adustment
            if msg.time > 0:
                time.sleep(max(0,msg.time - (time.time()-time1)))
                time1 = time.time()
            # send MIDI msg
            port.send(msg)
            # draw note on chart
            if msg.type == "note_on":
                if time1 == 0:
                    time1 = time.time()
                # chart limited
                if msg.channel < 5 and msg.note > 50:
                    x = [on_time_list[i], off_time_list[i]]
                    y = [msg.note, pitch_list[i]%1000]
                    color = ["r", "g", "b", "c", "m", "y", "k", "w"][msg.channel]
                    line, = ax.plot(x,y,color = color, linewidth = 4, alpha=0.5)
                    ax.draw_artist(line)
                # draw bars
                if draw_timing[i] == 1:
                    fig.canvas.blit(ax.bbox)
                    fig.canvas.flush_events()
                i += 1
    plt.show()

play_plot_MIDI(port, score)

Comments

Popular Posts