Last active
April 25, 2020 18:30
-
-
Save salt-die/8e4d7120803080af055c3f7707fc9a3c to your computer and use it in GitHub Desktop.
various songs from numpy arrays
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from itertools import chain, product, repeat, starmap | |
from more_itertools import interleave | |
from functools import lru_cache | |
import numpy as np | |
import sounddevice as sd | |
SAMPLE_RATE = 44100 | |
sd.default.samplerate = SAMPLE_RATE | |
NOTES = 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B' | |
# ---- Build note to pitch dict ---- | |
a = 2**(1/12) | |
note_to_pitch = {f'{note}{octave}': 440 * a**(12 * (int(octave) - 4) + i) | |
for (i, note), octave in product(enumerate(NOTES, start=-9), range(9))} | |
for note, pitch in list(note_to_pitch.items()): | |
if '#' in note: | |
enharmonic = f'{NOTES[NOTES.index(note[0]) + 2]}b{note[-1]}' | |
note_to_pitch[enharmonic] = pitch | |
# -------------------------------------- | |
@lru_cache | |
def sound_array(frequency, duration, rest=0, frequency_by_name=True): | |
if frequency_by_name: frequency = note_to_pitch[frequency] | |
samples = np.arange(SAMPLE_RATE * duration, dtype=np.float) / SAMPLE_RATE | |
sound = (10000 * np.sin(2 * np.pi * frequency * samples)).astype(np.int16) | |
if rest: sound[-int(SAMPLE_RATE * rest):] = 0 | |
return sound | |
def compile_song(*note_lists): | |
return sum(np.concatenate(tuple(starmap(sound_array, note_list))) for note_list in note_lists) | |
def prelude_constructor(clef): | |
def measure_builder(notes, note1, note2=None): | |
interleaved = tuple(interleave(notes, repeat(note1))) | |
if note2 is not None: return measure_builder(interleaved, note2) | |
return repeat(interleaved, 2) | |
measures = chain.from_iterable(measure_builder(notes, note1, note2) for notes, note1, note2 in clef) | |
return zip(chain.from_iterable(measures), repeat(.2)) # Add duration to all notes | |
treble = ((( 'C5', 'C4'), 'D4', 'Eb4'), (('Ab4', 'C4'), 'E4', 'F4'), | |
(( 'B4', 'D4'), 'Eb4', 'F4'), (( 'C5', 'Eb4'), 'F4', 'G4'), | |
(('Eb5', 'Eb4'), 'G4', 'Ab4'), (( 'D5', 'D4'), 'E4', 'F#4'), | |
(( 'D5', 'D4'), 'F#4', 'G4'), (( 'C5', 'C4'), 'D4', 'E4'), | |
(( 'C5', 'C4'), 'E4', 'F4'), (('Bb4', 'D4'), 'Eb4', 'F4'), | |
(('Bb4', 'Eb4'), 'F4', 'G4'), (('Ab4', 'Eb4'), 'F4', 'G4'), | |
(('Ab4', 'Bb3'), 'C4', 'D4'), (('G4', 'Eb4'), 'Ab3', 'Bb3'), | |
(( 'F4', 'A3'), 'Bb3', 'C4'), (('F4', 'B3'), 'C4', 'D4'), | |
(('F4', 'B3'), 'C4', 'D4')) | |
bass = ((( 'C3', 'Eb3'), 'F3', 'G3'), (( 'C3', 'F3'), 'G3', 'Ab3'), | |
(( 'C3', 'F3'), 'G3', 'Ab3'), (( 'C3', 'G3'), 'D3', 'Eb3'), | |
(( 'C3', 'Ab3'), 'Bb3', 'C4'), (( 'C3', 'F#3'), 'G3', 'A3'), | |
(('Bb2', 'G3'), 'A3', 'Bb3'), (('Bb2', 'E3'), 'F3', 'G3'), | |
(('Ab2', 'F3'), 'G3', 'Ab3'), (('Ab2', 'F3'), 'C3', 'D3'), | |
(( 'G2', 'G3'), 'D3', 'Eb3'), (( 'C3', 'Ab3'), 'D3', 'Eb3'), | |
(( 'D3', 'Ab3'), 'Eb3', 'F3'), (('Eb3', 'Ab3'), 'F3', 'G3'), | |
(('Eb3', 'F3'), 'G3', 'A3'), (( 'D3', 'Ab3'), 'Eb3', 'F3'), | |
(( 'C3', 'Ab3'), 'E3', 'F3')) | |
sound = compile_song(*map(prelude_constructor, (treble, bass))) | |
sd.play(sound, blocking=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from itertools import starmap, product | |
from functools import lru_cache | |
import numpy as np | |
import sounddevice as sd | |
SAMPLE_RATE = 44100 | |
sd.default.samplerate = SAMPLE_RATE | |
NOTES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | |
a = 2**(1/12) | |
note_to_pitch = {f'{note}{octave}': 440 * a**(12 * (int(octave) - 4) + i) | |
for (i, note), octave in product(enumerate(NOTES, start=-9), range(9))} | |
@lru_cache | |
def sound_array(frequency, duration, rest=.05, frequency_by_name=True): | |
if frequency_by_name: | |
frequency = note_to_pitch[frequency] | |
samples = np.arange(SAMPLE_RATE * duration, dtype=np.float) / SAMPLE_RATE | |
sound = (10000 * np.sin(2 * np.pi * frequency * samples)).astype(np.int16) | |
if rest: | |
sound[-int(SAMPLE_RATE * rest):] = 0 | |
return sound | |
twinkle = [('C4', .5), ('C4', .5), | |
('G4', .5), ('G4', .5), | |
('A4', .5), ('A4', .5), | |
('G4', 1), | |
('F4', .5), ('F4', .5), | |
('E4', .5), ('E4', .5), | |
('D4', .5), ('D4', .5), | |
('C4', 1)] | |
sound = np.concatenate(tuple(starmap(sound_array, twinkle))) | |
sd.play(sound, blocking=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment