from collections import OrderedDict modes = OrderedDict() for mode_name, alterations in zip(['lydian', 'ionian', 'mixolydian', 'dorian', 'aeolian', 'phrygian', 'locrian'], [[0, 0, 0, -1, 0, 0, 0], [0, 0, 0, 0, 0, 0, -1], [0, 0, -1, 0, 0, 0, 0], [0, 0, 0, 0, 0, -1, 0], [0, -1, 0, 0, 0, 0, 0], [0, 0, 0, 0, -1, 0, 0], [0, 1, 1, 1, 1, 1, 1]]): modes[mode_name] = alterations tetris = "4e6,8b5,8c6,8d6,16e6,16d6,8c6,8b5,4a5,8a5,8c6,4e6,8d6,8c6,4b5,8b5,8c6,4d6,4e6,4c6,4a5,2a5,8p,4d6,8f6,4a6,8g6,8f6,4e6,8e6,8c6,4e6,8d6,8c6,4b5,8b5,8c6,4d6,4e6,4c6,4a5,4a5" from pylab import * from scipy.signal import square key = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] starting_mode = 'aeolian' ending_mode = 'dorian' mode_names = modes.keys() start_index = mode_names.index(starting_mode) end_index = mode_names.index(ending_mode) if end_index < start_index: end_index += 7 print start_index, end_index transposition = array([0, 0, 0, 0, 0, 0, 0]) for i in range(start_index, end_index): transposition += array(modes[mode_names[i % 7]]) print transposition note_scale = ["a", "a#", "b", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#"] transposed_melody = [] for note in tetris.split(','): if note[-1] == 'p': transposed_melody.append(note) else: for target_note in note_scale: if note.find(target_note) != -1: duration, octave = note.split(target_note) break transposed_melody.append( duration + note_scale[(note_scale.index(target_note) + transposition[key.index(target_note)]) % 12] + octave) ",".join(transposed_melody) def transpose(melody, key, starting_mode, ending_mode): mode_names = modes.keys() start_index = mode_names.index(starting_mode) end_index = mode_names.index(ending_mode) if end_index < start_index: end_index += 7 transposition = array([0, 0, 0, 0, 0, 0, 0]) for i in range(start_index, end_index): transposition += array(modes[mode_names[i % 7]]) #print transposition note_scale = ["a", "a#", "b", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#"] transposed_melody = [] for note in melody.split(','): if note[-1] == 'p': transposed_melody.append(note) else: for target_note in note_scale: if note.find(target_note) != -1: duration, octave = note.split(target_note) break transposed_melody.append( duration + note_scale[(note_scale.index(target_note) + transposition[key.index(target_note)]) % 12] + octave) return ",".join(transposed_melody) tetris transpose(tetris, ['a', 'b', 'c', 'd', 'e', 'f', 'g'], 'aeolian', 'dorian') import re from IPython.display import Audio, display def play_melody(melody, sample_freq=10.e3, bpm=50): duration = re.compile("^[0-9]+") pitch = re.compile("[\D]+[\d]*") measure_duration = 4 * 60. / bpm #usually it's 4/4 measures output = zeros((0,)) for note in melody.split(','): # regexp matching duration_match = duration.findall(note) pitch_match = pitch.findall(note) # duration if len(duration_match) == 0: t_max = 1/4. else: t_max = 1/float(duration_match[0]) if "." in pitch_match[0]: t_max *= 1.5 pitch_match[0] = "".join(pitch_match[0].split(".")) t_max = t_max * measure_duration # pitch if pitch_match[0] == 'p': freq = 0 else: if pitch_match[0][-1] in ["4", "5", "6", "7"]: # octave is known octave = ["4", "5", "6", "7"].index(pitch_match[0][-1]) + 4 height = pitch_match[0][:-1] else: # octave is not known octave = 5 height = pitch_match[0] freq = 261.626 * 2 ** ((["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"].index(height) / 12. + octave - 4)) # generate sound t = arange(0, t_max, 1/sample_freq) wave = square(2 * pi * freq * t) # append to output output = hstack((output, wave)) display(Audio(output, rate=sample_freq)) from IPython.html.widgets import interact, fixed def play_transposed_melody(mode): transposed_melody = transpose(tetris, ['a', 'b', 'c', 'd', 'e', 'f', 'g'], 'aeolian', mode) #print transposed_melody play_melody(transposed_melody, bpm=130) interact(play_transposed_melody, mode=modes.keys()) for mode in modes.keys(): print mode play_transposed_melody(mode) import numpy as np def speedx(snd_array, factor): """ Multiplies the sound's speed by some `factor` """ indices = np.round( np.arange(0, len(snd_array), factor) ) indices = indices[indices < len(snd_array)].astype(int) return snd_array[ indices.astype(int) ] def stretch(sound_array, f, window_size, h): """ Stretches the sound by a factor `f` """ phase = np.zeros(window_size) hanning_window = np.hanning(window_size) result = np.zeros( len(sound_array) /f + window_size) for i in np.arange(0, len(sound_array)-(window_size+h), h*f): # two potentially overlapping subarrays a1 = sound_array[i: i + window_size] a2 = sound_array[i + h: i + window_size + h] # resynchronize the second array on the first s1 = np.fft.fft(hanning_window * a1) s2 = np.fft.fft(hanning_window * a2) phase = (phase + np.angle(s2/s1)) % 2*np.pi a2_rephased = np.fft.ifft(np.abs(s2)*np.exp(1j*phase)) # add to result i2 = int(i/f) result[i2 : i2 + window_size] += hanning_window*a2_rephased result = ((2**(16-4)) * result/result.max()) # normalize (16bit) return result.astype('int16') def pitchshift(snd_array, n, window_size=2**13, h=2**11): """ Changes the pitch of a sound by ``n`` semitones. """ factor = 2**(1.0 * n / 12.0) stretched = stretch(snd_array, 1.0/factor, window_size, h) return speedx(stretched[window_size:], factor) from scipy.io import wavfile fps, bowl_sound = wavfile.read("../../../Pianoputer/bowl.wav") tones = range(-25,25) transposed = [pitchshift(bowl_sound, n) for n in tones] print fps def play_melody_with_bowl(melody, sample_freq=10.e3, bpm=50): duration = re.compile("^[0-9]+") pitch = re.compile("[\D]+[\d]*") measure_duration = 4 * 60. / bpm #usually it's 4/4 measures output = zeros((0,)) for note in melody.split(','): # regexp matching duration_match = duration.findall(note) pitch_match = pitch.findall(note) # duration if len(duration_match) == 0: t_max = 1/4. else: t_max = 1/float(duration_match[0]) if "." in pitch_match[0]: t_max *= 1.5 pitch_match[0] = "".join(pitch_match[0].split(".")) t_max = t_max * measure_duration # pitch if pitch_match[0] == 'p': freq = 0 else: if pitch_match[0][-1] in ["4", "5", "6", "7"]: # octave is known octave = ["4", "5", "6", "7"].index(pitch_match[0][-1]) + 4 height = pitch_match[0][:-1] else: # octave is not known octave = 5 height = pitch_match[0] sound_index = (["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"].index(height) + (octave - 5) * 12) # generate sound t = arange(0, t_max, 1./sample_freq) wave = transposed[sound_index] wave = wave[:t.size] # append to output output = hstack((output, wave)) display(Audio(output, rate=sample_freq)) play_melody_with_bowl(transpose(tetris, ['a', 'b', 'c', 'd', 'e', 'f', 'g'], 'aeolian', 'aeolian'), sample_freq=fps, bpm=120.) for mode in modes.keys(): print mode play_melody_with_bowl(transpose(tetris, ['a', 'b', 'c', 'd', 'e', 'f', 'g'], 'aeolian', mode), sample_freq=fps, bpm=120.)