Featured
- Get link
- X
- Other Apps
Python Programming 3 Create melody with Fourier Series
Since music is a movement of sounds with some patterns, I simply use Fourier Series to create melody patterns.
Blue line is a sample Fourier series and red bars are the notes. Notes are the nearest pitches in specified scale (e.g. [C, D, E, F, G, A, B, C] in C major).
My Fourier series is pretty simple as below. Coefficients of Fourier series are random. So you can hear different sounds every time.
In a song, usually some phrases are repeats and some phrases are
different. Use same coefficients for repeats and use new coefficients
for new phrases.
You can hear a full composition based on this method in my Youtube "Etude 4". https://www.youtube.com/watch?v=DfIxoH5QTnQ
Within the actual composition, I make pitches more aligned to chord progressions. e.g. select pitches from chord notes with higher priority.
Remark) At the bottom of below program, score is saved as a new file. If you don't want, please comment out.
---------------------------------------------------------------------------------------------
# blog 3 import mido import time from mido import Message, MidiFile, MidiTrack, MetaMessage from random import random import numpy as np import matplotlib.pyplot as plt # port open ports = mido.get_output_names() port = mido.open_output(ports[0]) # Fourier Series function for melody pattern def fx1(x, a, cycle): x1 = 2*np.pi/cycle*x y = a[0] * np.sin(x1) + a[1] * np.sin(2*x1) + a[2] * np.sin(4*x1) + a[3] * np.sin(8*x1) + a[4] * np.sin(16*x1) return y # find nearlest pitch on scale def nearest_on_scale(p,scale): if abs(p-scale[sum([p>x for x in scale])-1]) <= abs(p-scale[sum([p>x for x in scale])]): pOnS = scale[sum([p>x for x in scale])-1] else: pOnS = scale[sum([p>x for x in scale])] return pOnS # add chord notes to track def addChord(track,channel,pitch_list, duration, velocity): for i in range(len(pitch_list)): track.append(Message('note_on', channel = channel, note=pitch_list[i], velocity=velocity, time=0)) track.append(Message('note_off', channel = channel, 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 = channel, note=pitch_list[i], velocity=0, time=0)) # convert beat to MIDI time scale (1 beat to 480 ticks) def beat_to_tick(dur): return int(dur * 480 +0.5) # Fourier series # coefficient random creation a = [1/((i+1))*(random()-0.5) for i in range(5) ] # song length = 32 x = np.linspace(0, 32, 481) # Fourier series cycle = 32 beats cycle = 32 y = [72 + 12 * fx1(xi, a, cycle) for xi in x] # pitch center = C5, pitch range [1 octave x max/min(fx1)] # C major scale pitches C_major_scale = [p + octave * 12 for octave in [0,1,2,3,4,5,6,7,8,9] for p in [0, 2, 4, 5, 7, 9, 11]] # Melody as nearest scale pitches pitch_list = [] duration_list = [] x1 = 0 while sum(duration_list) < x[-1]: # pitch on y at time x1 p0 = 72 + 12 * fx1(x1,a,cycle) # find nearest pitch on scale pitch = nearest_on_scale(p0, C_major_scale) # initial duration of each note is 1/4 duration = 0.25 x1 += duration # if pitch is same as before, make duration longer if len(pitch_list) > 1 and pitch_list[-1] == pitch: duration_list[-1] += duration # otherwise add new note else: pitch_list.append(pitch) duration_list.append(duration) # create new MIDI file mid = MidiFile() track0 = MidiTrack() # tempo track1 = MidiTrack() # melody track2 = MidiTrack() # chord mid.tracks.append(track0) # tempo mid.tracks.append(track1) # melody mid.tracks.append(track2) # chord track1.append(Message('program_change', channel = 0, program=40,time=0)) # melody program:40 = Violin (General MIDI) track2.append(Message('program_change', channel = 1, program=0,time=0)) # chord program:0 = Piano # set tempo bpm = 120 # beat per minutes track0.append(MetaMessage('set_tempo', tempo=int(1000000*60/bpm), time=0)) # add melody velocity = 70 for i in range(len(pitch_list)): track1.append(Message('note_on', channel = 0, note=pitch_list[i], velocity=velocity, time=0)) track1.append(Message('note_off', channel = 0, note=pitch_list[i], velocity=0, time=beat_to_tick(duration_list[i]))) chord_list = ["C", "Em", "G", "CM7onE","C", "Em", "G", "CM7onE"] chord_pitch_list = [[60, 64, 67], [64, 67, 71], [67, 71, 74], [72, 64, 67, 71],[60, 64, 67], [64, 67, 71], [67, 71, 74], [72, 64, 67, 71]] # from blog 2 chord progression # add chords for i in range(len(chord_pitch_list)): channel = 1 addChord(track2, channel, chord_pitch_list[i], 4, 70) # play MIDI file for msg in mid: time.sleep(msg.time) if not msg.is_meta: port.send(msg) # MIDI score saved score_name = "blog03" mid.save(score_name+'.mid')
- Get link
- X
- Other Apps
Popular Posts
Python programming 1 Basics of Mido for MIDI handling
- Get link
- X
- Other Apps
Python Programming 4 Piano Roll Animation of MIDI file by Matplotlib
- Get link
- X
- Other Apps
Comments
Post a Comment