Sunday, March 13, 2016

20. Midi numbers and frequencies

Similar to mus11.py, the frequencies will be found for midi numbers between 0 and 127.


mus11.py used music21 to find the frequencies. It is more efficient to use mathematical formulas, rather than creating objects of various classes and then using their methods.


For every 12 midi numbers, the frequency increases by a factor of 2. Thus, for each midi number, the frequency increases by 12th power of 2, or 2**(1/12), with ** being the Python exponential operation. The frequency between two nearest midi numbers will be in variable g.


Further, the midi of 69 is 440 Hz, in music21.


The above can be used to create a lambda function.


# mus20.py
# Midi numbers and frequencies

g = 2**(1/12)

f = lambda midi: 440*g**(midi-69)

for midi in range(128):
    freq = f(midi)
    print('midi = {}, freq = {:.1f} Hz'.format(midi,freq))

This will generate this printout:


midi = 0, freq = 8.2 Hz
midi = 1, freq = 8.7 Hz
midi = 2, freq = 9.2 Hz
midi = 3, freq = 9.7 Hz
midi = 4, freq = 10.3 Hz
midi = 5, freq = 10.9 Hz
midi = 6, freq = 11.6 Hz
midi = 7, freq = 12.2 Hz
midi = 8, freq = 13.0 Hz
midi = 9, freq = 13.7 Hz
midi = 10, freq = 14.6 Hz
midi = 11, freq = 15.4 Hz
midi = 12, freq = 16.4 Hz
midi = 13, freq = 17.3 Hz
midi = 14, freq = 18.4 Hz
midi = 15, freq = 19.4 Hz
midi = 16, freq = 20.6 Hz
midi = 17, freq = 21.8 Hz
midi = 18, freq = 23.1 Hz
midi = 19, freq = 24.5 Hz
midi = 20, freq = 26.0 Hz
midi = 21, freq = 27.5 Hz
midi = 22, freq = 29.1 Hz
midi = 23, freq = 30.9 Hz
midi = 24, freq = 32.7 Hz
midi = 25, freq = 34.6 Hz
midi = 26, freq = 36.7 Hz
midi = 27, freq = 38.9 Hz
midi = 28, freq = 41.2 Hz
midi = 29, freq = 43.7 Hz
midi = 30, freq = 46.2 Hz
midi = 31, freq = 49.0 Hz
midi = 32, freq = 51.9 Hz
midi = 33, freq = 55.0 Hz
midi = 34, freq = 58.3 Hz
midi = 35, freq = 61.7 Hz
midi = 36, freq = 65.4 Hz
midi = 37, freq = 69.3 Hz
midi = 38, freq = 73.4 Hz
midi = 39, freq = 77.8 Hz
midi = 40, freq = 82.4 Hz
midi = 41, freq = 87.3 Hz
midi = 42, freq = 92.5 Hz
midi = 43, freq = 98.0 Hz
midi = 44, freq = 103.8 Hz
midi = 45, freq = 110.0 Hz
midi = 46, freq = 116.5 Hz
midi = 47, freq = 123.5 Hz
midi = 48, freq = 130.8 Hz
midi = 49, freq = 138.6 Hz
midi = 50, freq = 146.8 Hz
midi = 51, freq = 155.6 Hz
midi = 52, freq = 164.8 Hz
midi = 53, freq = 174.6 Hz
midi = 54, freq = 185.0 Hz
midi = 55, freq = 196.0 Hz
midi = 56, freq = 207.7 Hz
midi = 57, freq = 220.0 Hz
midi = 58, freq = 233.1 Hz
midi = 59, freq = 246.9 Hz
midi = 60, freq = 261.6 Hz
midi = 61, freq = 277.2 Hz
midi = 62, freq = 293.7 Hz
midi = 63, freq = 311.1 Hz
midi = 64, freq = 329.6 Hz
midi = 65, freq = 349.2 Hz
midi = 66, freq = 370.0 Hz
midi = 67, freq = 392.0 Hz
midi = 68, freq = 415.3 Hz
midi = 69, freq = 440.0 Hz
midi = 70, freq = 466.2 Hz
midi = 71, freq = 493.9 Hz
midi = 72, freq = 523.3 Hz
midi = 73, freq = 554.4 Hz
midi = 74, freq = 587.3 Hz
midi = 75, freq = 622.3 Hz
midi = 76, freq = 659.3 Hz
midi = 77, freq = 698.5 Hz
midi = 78, freq = 740.0 Hz
midi = 79, freq = 784.0 Hz
midi = 80, freq = 830.6 Hz
midi = 81, freq = 880.0 Hz
midi = 82, freq = 932.3 Hz
midi = 83, freq = 987.8 Hz
midi = 84, freq = 1046.5 Hz
midi = 85, freq = 1108.7 Hz
midi = 86, freq = 1174.7 Hz
midi = 87, freq = 1244.5 Hz
midi = 88, freq = 1318.5 Hz
midi = 89, freq = 1396.9 Hz
midi = 90, freq = 1480.0 Hz
midi = 91, freq = 1568.0 Hz
midi = 92, freq = 1661.2 Hz
midi = 93, freq = 1760.0 Hz
midi = 94, freq = 1864.7 Hz
midi = 95, freq = 1975.5 Hz
midi = 96, freq = 2093.0 Hz
midi = 97, freq = 2217.5 Hz
midi = 98, freq = 2349.3 Hz
midi = 99, freq = 2489.0 Hz
midi = 100, freq = 2637.0 Hz
midi = 101, freq = 2793.8 Hz
midi = 102, freq = 2960.0 Hz
midi = 103, freq = 3136.0 Hz
midi = 104, freq = 3322.4 Hz
midi = 105, freq = 3520.0 Hz
midi = 106, freq = 3729.3 Hz
midi = 107, freq = 3951.1 Hz
midi = 108, freq = 4186.0 Hz
midi = 109, freq = 4434.9 Hz
midi = 110, freq = 4698.6 Hz
midi = 111, freq = 4978.0 Hz
midi = 112, freq = 5274.0 Hz
midi = 113, freq = 5587.7 Hz
midi = 114, freq = 5919.9 Hz
midi = 115, freq = 6271.9 Hz
midi = 116, freq = 6644.9 Hz
midi = 117, freq = 7040.0 Hz
midi = 118, freq = 7458.6 Hz
midi = 119, freq = 7902.1 Hz
midi = 120, freq = 8372.0 Hz
midi = 121, freq = 8869.8 Hz
midi = 122, freq = 9397.3 Hz
midi = 123, freq = 9956.1 Hz
midi = 124, freq = 10548.1 Hz
midi = 125, freq = 11175.3 Hz
midi = 126, freq = 11839.8 Hz
midi = 127, freq = 12543.9 Hz

19. Triads of C major scale

The pitches lists holds the notes in C-major scale (octaves 4 and 5).


In the 7-length chord list, we put the root note as one of the first seven, next 2 above, and last, 4 above.


Then, in the for loop, we go over the chord list. n1, n2, n3 will get the midi values for the notes. By n2 - n1, we find semitones from 1st to 2nd note. By n3 - n1, we find semitone interval from 1st to 3rd note.


Using if and elif, we find the name of interval of n2 - n1 and n3 - n1.


The chords, with major 3 interval, for n2 - n1, chord 1 (I, Tonic), chord 4 (IV, Subdominant), chord 5 (V, Dominant).


# mus19.py
# Triads of C major

import music21 as m21
from writeMIDI import writeMIDI

notes = []

pitches = ['C4','D4','E4','F4','G4','A4','B4',
           'C5','D5','E5','F5','G5','A5','B5']

chord = 7*[None]
for i in range(7):
    chord[i] = pitches[i],pitches[i+2],pitches[i+4]

for i,c in enumerate(chord):
    print('\nChord {}: {}'.format(i+1,c))
    n1 = m21.note.Note(c[0]).pitch.midi
    n2 = m21.note.Note(c[1]).pitch.midi
    n3 = m21.note.Note(c[2]).pitch.midi
    print('n2-n1 = ',n2-n1, end = ' ')
    if n2 - n1 == 3: print('\tminor third')
    elif n2 - n1 == 4: print('\tmajor third')
    print('n3-n1 = ',n3-n1, end = '')
    if n3 - n1 == 7: print('\tperfect fifth')
    elif n3 - n1 == 6: print('\tdiminished fifth')
    notes.append((c[0],2*i,1,120))
    notes.append((c[1],2*i,1,120))
    notes.append((c[2],2*i,1,120))
    
writeMIDI('C','piano',120,notes,'mus19')

This is the printout:

Chord 1: ('C4', 'E4', 'G4')
n2-n1 =  4  major third
n3-n1 =  7 perfect fifth

Chord 2: ('D4', 'F4', 'A4')
n2-n1 =  3  minor third
n3-n1 =  7 perfect fifth

Chord 3: ('E4', 'G4', 'B4')
n2-n1 =  3  minor third
n3-n1 =  7 perfect fifth

Chord 4: ('F4', 'A4', 'C5')
n2-n1 =  4  major third
n3-n1 =  7 perfect fifth

Chord 5: ('G4', 'B4', 'D5')
n2-n1 =  4  major third
n3-n1 =  7 perfect fifth

Chord 6: ('A4', 'C5', 'E5')
n2-n1 =  3  minor third
n3-n1 =  7 perfect fifth

Chord 7: ('B4', 'D5', 'F5')
n2-n1 =  3  minor third
n3-n1 =  6 diminished fifth

This will generate this:


Saturday, March 12, 2016

18. Motifs

Here motif1 and motif2 are series of notes, with timing and volume information. The offset time will correspond to first note, which is at time 0. Here they are strings, but they could also be separate text files with minor changes in program.


notes1 and notes2 are lists which will be populated by getNotes().


In addNotes(), we add either notes1 or notes2 to the main list, notes. We have to adjust the offset time. Tuples can not be modified, and thus we create a temporary list, modify it, and then convert it back to a tuple to append to the notes list.


Finally, we call addNotes() 4 times, adding notes1 or notes2 at appropriate times.


It is also possible to randomly changes notes, etc. to add variation.


# mus18.py
# Motifs

from writeMIDI import writeMIDI

motif1 = '''
C#5 0.0 0.375 106
B4 0.5 0.375 67
G#4 1.0 0.375 80
F#4 1.5 0.375 99
E4 2.0 0.375 84
E4 2.5 0.625 91
E4 3.5 0.375 93
E4 4.0 0.375 78
E4 4.5 0.375 74
E4 5.0 0.375 80
E-4 5.5 0.375 80
E-4 6.0 0.375 80
E4 6.5 0.375 70
E4 7.0 0.625 80
'''

motif2 = '''
C#5 0.0 0.375 81
B4 0.5 0.375 97
G#4 1.0 0.375 77
F#4 1.5 0.375 81
E4 2.0 0.375 97
E4 2.5 0.75 84
E4 3.5 0.375 97
E4 4.0 0.375 91
E4 4.5 0.375 84
E4 5.0 0.375 91
C#4 5.5 0.375 84
C#4 6.0 0.375 80
B3 6.5 0.375 87
B3 7.0 0.667 95
'''

notes = []

notes1 = []
notes2 = []

def getNotes(n,s):
    mot = s.split('\n')
    for m in mot:
        if m == '': continue
        t = m.split()
        n.append((t[0],float(t[1]),
                  float(t[2]),int(t[3])))

def addNotes(n,t):
    for note in n:
        tmp = list(note)
        tmp[1] += t
        notes.append(tuple(tmp))
    
getNotes(notes1,motif1)
getNotes(notes2,motif2)

addNotes(notes1,0)
addNotes(notes2,10)
addNotes(notes2,20)
addNotes(notes1,30)
writeMIDI('C','piano',80,notes,'mus18')

This will generate this:


Thursday, March 3, 2016

17. Modes

A Python dictionary is unordered. It is possible to use an OrderedDict from the standard collections module.


For the 7 modes (given in the order), with roots of C through B, we only get white keys. For 'C' root, we have major (Ionian), and for 'A' we have minor (Dorian).


In the printouts, we can see that the scale is shifted left, for the next mode, and a value an octave above is the new value, replacing the rightmost note (a left rotate operation for the semitone intervals).


The MIDI has 7 scales, with each scale taking 2 measures, with a total length of 14 measures or 14 bars.


# mus17.py
# Modes

import music21 as m21
from writeMIDI import writeMIDI
from collections import OrderedDict

SCALE = OrderedDict()
SCALE['Ionian'] = [2,2,1,2,2,2,1] # major
SCALE['Dorian'] = [2,1,2,2,2,1,2]
SCALE['Phrygian'] = [1,2,2,2,1,2,2]
SCALE['Lydian'] = [2,2,2,1,2,2,1]
SCALE['Mixolydian'] = [2,2,1,2,2,1,2]
SCALE['Aeolian'] = [2,1,2,2,1,2,2] # minor
SCALE['Locrian'] = [1,2,2,1,2,2,2]

start = 0
notes = []

def scale(root, n, kind):
    global start
    start += 8
    print('\nroot = {} \tkind = {}'.format(root,kind))
    p = m21.pitch.Pitch(root)
    midi = p.midi
    for i in range(8):
        if i<7: print('{},'.format(midi),end=' ')
        else: print('{}'.format(midi))
        n.append((midi,start+i,1,120))
        midi += SCALE[kind][i%7]
        
keys = 'CDEFGAB'
lett = 0

for kind in SCALE:
    scale(keys[lett],notes,kind)
    lett += 1

writeMIDI('C','piano',130,notes,'mus17')

This is the printout:

root = C  kind = Ionian
60, 62, 64, 65, 67, 69, 71, 72

root = D  kind = Dorian
62, 64, 65, 67, 69, 71, 72, 74

root = E  kind = Phrygian
64, 65, 67, 69, 71, 72, 74, 76

root = F  kind = Lydian
65, 67, 69, 71, 72, 74, 76, 77

root = G  kind = Mixolydian
67, 69, 71, 72, 74, 76, 77, 79

root = A  kind = Aeolian
69, 71, 72, 74, 76, 77, 79, 81

root = B  kind = Locrian
71, 72, 74, 76, 77, 79, 81, 83

This will generate this MIDI:


Wednesday, March 2, 2016

16. MIDI of Major and Minor Scales

The midi of a scale in a random root note is generated. We use the scales of major and different minors, which are stored in the SCALE dictionary.


All notes are quarter notes, no each takes 2 measures or 8 quarter lengths, assuming time signature of 4/4.


The total duration is 8 measures for the 4 scales.


Also we print a new line only if i is 7. The default end, for a print statement, is new line, unless we override it.


# mus16.py
# scales

import music21 as m21
from writeMIDI import writeMIDI
from random import choice

SCALE = {}
SCALE['major'] = [2,2,1,2,2,2,1]
SCALE['natural minor'] = [2,1,2,2,1,2,2]
SCALE['melodic minor'] = [2,1,2,2,2,2,1]
SCALE['harmonic minor'] = [2,1,2,2,1,3,1]

start = 0
notes = []

def scale(root, n, kind):
    global start
    start += 8
    print('\nroot = {} \tkind = {}'.format(root,kind))
    p = m21.pitch.Pitch(root)
    midi = p.midi
    for i in range(8):
        if i<7: print('{},'.format(midi),end=' ')
        else: print('{}'.format(midi))
        n.append((midi,start+i,1,120))
        midi += SCALE[kind][i%7]
        
keys = ['C','C#','D','D#',
        'E','F','F#','G',
        'G#','A','A#','B']

for kind in SCALE:
    scale(choice(keys),notes,kind)

writeMIDI('C','piano',130,notes,'mus16')

This is one possible output:


root = A  kind = harmonic minor
69, 71, 72, 74, 76, 77, 80, 81

root = D#  kind = major
63, 65, 67, 68, 70, 72, 74, 75

root = G  kind = melodic minor
67, 69, 70, 72, 74, 76, 78, 79

root = A  kind = natural minor
69, 71, 72, 74, 76, 77, 79, 81

The output is:

15. Melodic minor scales

The 12 notes within each octave is represented in the Python list called keys, as in the last example.


We list the semitone intervals of a minor scale, inside the minor list. We copy it to melodic_minor list and then change two of the values.


Inside the main loop, iterated over all keys, we use i%7 (i modulo 7) as index into melodic_minor so we only access indexes 0 through 6.


Also we print a new line only if i is 7. The default end, for a print statement, is new line, unless we override it.


# mus15.py
# melodic minor scales

import music21 as m21

minor = [2,1,2,2,1,2,2]
melodic_minor = minor[:]
melodic_minor[-3] += 1
melodic_minor[-1] -= 1
print('melodic minor = ',melodic_minor)

keys = ['C','C#','D','D#',
        'E','F','F#','G',
        'G#','A','A#','B']

print('The 12 melodic minor scales:')
for key in keys:
    n = m21.note.Note(key)
    midi = n.pitch.midi
    for i in range(8):
        n = m21.note.Note(midi = midi)
        if i<7: print(n.name, end= ' - ')
        else: print(n.name)
        midi = n.pitch.midi
        midi += melodic_minor[i%7]

This will generate this output:


melodic minor =  [2, 1, 2, 2, 2, 2, 1]
The 12 melodic minor scales:
C - D - E- - F - G - A - B - C
C# - E- - E - F# - G# - B- - C - C#
D - E - F - G - A - B - C# - D
E- - F - F# - G# - B- - C - D - E-
E - F# - G - A - B - C# - E- - E
F - G - G# - B- - C - D - E - F
F# - G# - A - B - C# - E- - F - F#
G - A - B- - C - D - E - F# - G
G# - B- - B - C# - E- - F - G - G#
A - B - C - D - E - F# - G# - A
B- - C - C# - E- - F - G - A - B-
B - C# - D - E - F# - G# - B- - B