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 2 Input chords ["C", "Dm", "G", "CM7onE"] and Play

Input chord progressions by chord names e.g. C => F => G => Am and and play them help creating music. 

Here, chord progression is a list of chord names as ["C", "F", "G", "Am"].
6th "C6", 7th "C7", 9th "C9", sus4 "Csus4" and on "ConE", "ConG" are OK. But for more complicated ones, need program update.

See a couple of functions in the program:

 - chNtoPitches(ch): convert chord name to list of pitches (e.g. "C" => [60,64,67]

 - addChord: add chord notes to MIDI file


Python examples are as below.

 
# blog 2

import mido
import time
from mido import Message, MidiFile, MidiTrack, MetaMessage

# port open
ports = mido.get_output_names()
port = mido.open_output(ports[0])

# input chord list
chord_list = ["C", "Em", "G", "CM7onE"]

# chord name converts to pitches  e,g, "C" => [60, 64, 67]
toneDict = {"C":60, "Cs":61, "Df":61, "D":62, "Ds":63, "Ef":63, "E":64, "F":65, "Fs":66, "Gf":66, "G":67, "Gs":68, "Af":68, "A":69, "As":70, "Bf":70, "B":71}
def chNtoPitches(ch):
    chord = []
    adj = 0
    ch1 = "major"
    # "dim" to "m5f7f" "
    if ch.find("dim7") > 0:
        ch = ch.replace("dim7","m5f7f")
    elif ch.find("dim") > 0:
        ch = ch.replace("dim","m5f7f")
    # f: flat, s:sharp
    if len(ch) > 1 and (if ch[1] == "f" or ch[1] == "s"):
        adj = 1
    # chord bass pitch
    bass = toneDict[ch[0:1+adj]]
    chord.append(bass)

    # major or minor
    if len(ch) > 1+adj and ch[1+adj] == "m":
        ch1 = "minor"


    # 3rd
    if ch.find("sus4") > 0:
        chord.append(bass+5)
    elif ch1 == "major":
        chord.append(bass+4)
    else:
        chord.append(bass+3)

    # 5th
    if ch.find("5f") > 0:
        chord.append(bass+6)
    elif ch.find("5s") > 0 or ch.find("aug") > 0:
        chord.append(bass+8)
    else:
        chord.append(bass+7)

    # 6th (M6 only)
    if ch.find("6") > 0:
        chord.append(bass+9)

    # 7th (M7 and 7, 7f for dim)
    if ch.find("M7") > 0:
        chord.append(bass+11)
    elif ch.find("7f") > 0:
        chord.append(bass+9)
    elif  ch.find("7") > 0:
        chord.append(bass+10)

    # 9th (M9 only)
    if ch.find("9") > 0:
        chord.append(bass+14)

    # on (e.g. G)    chord starting note to G.
    if ch.find("on") > 0:
        bass2 = toneDict[ch[ch.find("on")+2:len(ch)]]
        # make bass2 as lowest chord tone
        if bass2 in chord:
            chord = [(tn + 12) if tn < bass2 else tn for tn in chord]
    return chord

#------------------------------------------------------------------------------------------
# conver to list of pitch lists
pitch_lists = [chNtoPitches(ch) for ch in chord_list]
print(pitch_lists)

def addChord(track,pitch_list, duration, velocity):
    for i in range(len(pitch_list)):
        track.append(Message('note_on', channel = 0, note=pitch_list[i], velocity=velocity, time=0))
    track.append(Message('note_off', channel = 0, note=pitch_list[0], velocity=0, time=beat_to_tick(duration)))
    for i in range(1,len(pitch_list)):
        track.append(Message('note_off', channel = 0, note=pitch_list[i], velocity=0, time=0))

# convert to beat to MIDI time scale (1 beat to 480 ticks)
def beat_to_tick(dur):
    return int(dur * 480 +0.5)

# create new MIDI file
mid = MidiFile()
track0 = MidiTrack() # tempo
track1 = MidiTrack() # instrument 1
mid.tracks.append(track0)
mid.tracks.append(track1)

# set tempo
bpm = 120 # beat per minutes
track0.append(MetaMessage('set_tempo', tempo=int(1000000*60/bpm), time=0))

# add chords
for i in range(len(pitch_lists)):
    addChord(track1, pitch_lists[i], 4, 70)

# play MIDI file
for msg in mid:
    time.sleep(msg.time)
    if not msg.is_meta:
        port.send(msg)

 

Comments