-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2198d60
Showing
15 changed files
with
737 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from musical.theory import Note, Scale, Chord | ||
from musical.audio import playback | ||
|
||
from timeline import Hit, Timeline | ||
|
||
# Define key and scale | ||
key = Note('D3') | ||
scale = Scale(key, 'minor') | ||
|
||
# Grab progression chords from scale starting at the octave of our key | ||
progression = Chord.progression(scale, base_octave=key.octave) | ||
|
||
time = 0.0 # Keep track of currect note placement time in seconds | ||
|
||
timeline = Timeline() | ||
|
||
# Add progression to timeline by arpeggiating chords from the progression | ||
for index in [0, 2, 3, 1, 0, 2, 3, 4, 5, 4, 0]: | ||
chord = progression[index] | ||
root, third, fifth = chord.notes | ||
arpeggio = [root, third, fifth, third, root, third, fifth, third] | ||
for i, interval in enumerate(arpeggio): | ||
ts = float(i * 2) / len(arpeggio) | ||
timeline.add(time + ts, Hit(interval, 1.0)) | ||
time += 2.0 | ||
|
||
# Strum out root chord to finish | ||
chord = progression[0] | ||
timeline.add(time + 0.0, Hit(chord.notes[0], 4.0)) | ||
timeline.add(time + 0.1, Hit(chord.notes[1], 4.0)) | ||
timeline.add(time + 0.2, Hit(chord.notes[2], 4.0)) | ||
timeline.add(time + 0.3, Hit(chord.notes[1].transpose(12), 4.0)) | ||
timeline.add(time + 0.4, Hit(chord.notes[2].transpose(12), 4.0)) | ||
timeline.add(time + 0.5, Hit(chord.notes[0].transpose(12), 4.0)) | ||
|
||
print "Rendering audio..." | ||
|
||
data = timeline.render() | ||
|
||
# Reduce volume to 25% | ||
data = data * 0.25 | ||
|
||
print "Playing audio..." | ||
|
||
playback.play(data) | ||
|
||
print "Done!" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import random | ||
|
||
from musical.theory import Note, Scale | ||
from musical.audio import effect, playback | ||
|
||
from timeline import Hit, Timeline | ||
|
||
|
||
# Define key and scale | ||
key = Note('E3') | ||
scale = Scale(key, 'harmonic minor') | ||
|
||
time = 0.0 # Keep track of currect note placement time in seconds | ||
|
||
timeline = Timeline() | ||
|
||
note = key | ||
|
||
# Semi-randomly queue notes from the scale | ||
for i in xrange(64): | ||
if note.index > 50 or note.index < 24: | ||
# If note goes out of comfort zone, randomly place back at base octave | ||
note = scale.get(random.randrange(4) * 2) | ||
note = note.at_octave(key.octave) | ||
else: | ||
# Transpose the note by some small random interval | ||
note = scale.transpose(note, random.choice((-2, -1, 1, 2))) | ||
length = random.choice((0.125, 0.125, 0.25)) | ||
timeline.add(time, Hit(note, length + 0.125)) | ||
time += length | ||
|
||
# Resolve | ||
note = scale.transpose(key, random.choice((-1, 1, 4))) | ||
timeline.add(time, Hit(note, 0.75)) # Tension | ||
timeline.add(time + 0.5, Hit(key, 4.0)) # Resolution | ||
|
||
print "Rendering audio..." | ||
|
||
data = timeline.render() | ||
|
||
print "Applying Chorus effect..." | ||
|
||
data = effect.chorus(data, freq=3.14159) | ||
|
||
# Reduce volume to 50% | ||
data = data * 0.5 | ||
|
||
print "Playing audio..." | ||
|
||
playback.play(data) | ||
|
||
print "Done!" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import numpy | ||
|
||
from musical.theory import Note, Scale | ||
from musical.audio import source, playback | ||
|
||
# Define key and scale | ||
key = Note('C4') | ||
scale = Scale(key, 'major') | ||
|
||
note = key | ||
chunks = [] | ||
for i in xrange(len(scale)): | ||
third = scale.transpose(note, 2) | ||
chunks.append(source.sine(note, 0.5) + source.square(third, 0.5)) | ||
note = scale.transpose(note, 1) | ||
fifth = scale.transpose(key, 4) | ||
chunks.append(source.sine(key, 1.5) + source.square(fifth, 1.5)) | ||
|
||
print "Rendering audio..." | ||
|
||
data = numpy.concatenate(chunks) | ||
|
||
# Reduce volume to 50% | ||
data = data * 0.5 | ||
|
||
print "Playing audio..." | ||
|
||
playback.play(data) | ||
|
||
print "Done!" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import theory, audio |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import source, effect, encode, playback, save |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import source | ||
|
||
# TODO: More effects. Distortion, echo, delay, reverb, phaser, pitch shift? | ||
# TODO: Better generalize chorus/flanger (they share a lot of code) | ||
|
||
def modulated_delay(data, modwave, dry, wet): | ||
''' Use LFO "modwave" as a delay modulator (no feedback) | ||
''' | ||
out = data.copy() | ||
for i in xrange(len(data)): | ||
index = int(i - modwave[i]) | ||
if index >= 0 and index < len(data): | ||
out[i] = data[i] * dry + data[index] * wet | ||
return out | ||
|
||
|
||
def feedback_modulated_delay(data, modwave, dry, wet): | ||
''' Use LFO "modwave" as a delay modulator (with feedback) | ||
''' | ||
out = data.copy() | ||
for i in xrange(len(data)): | ||
index = int(i - modwave[i]) | ||
if index >= 0 and index < len(data): | ||
out[i] = out[i] * dry + out[index] * wet | ||
return out | ||
|
||
|
||
def chorus(data, freq, dry=0.5, wet=0.5, depth=1.0, delay=25.0, rate=44100): | ||
''' Chorus effect | ||
http://en.wikipedia.org/wiki/Chorus_effect | ||
''' | ||
length = float(len(data)) / rate | ||
mil = float(rate) / 1000 | ||
delay *= mil | ||
depth *= mil | ||
modwave = (source.sine(freq, length) / 2 + 0.5) * depth + delay | ||
return modulated_delay(data, modwave, dry, wet) | ||
|
||
|
||
def flanger(data, freq, dry=0.5, wet=0.5, depth=20.0, delay=1.0, rate=44100): | ||
''' Flanger effect | ||
http://en.wikipedia.org/wiki/Flanging | ||
''' | ||
length = float(len(data)) / rate | ||
mil = float(rate) / 1000 | ||
delay *= mil | ||
depth *= mil | ||
modwave = (source.sine(freq, length) / 2 + 0.5) * depth + delay | ||
return feedback_modulated_delay(data, modwave, dry, wet) | ||
|
||
|
||
def tremolo(data, freq, dry=0.5, wet=0.5, rate=44100): | ||
''' Tremolo effect | ||
http://en.wikipedia.org/wiki/Tremolo | ||
''' | ||
length = float(len(data)) / rate | ||
modwave = (source.sine(freq, length) / 2 + 0.5) | ||
return (data * dry) + ((data * modwave) * wet) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import numpy | ||
|
||
|
||
def as_uint8(data): | ||
''' Return data encoded as unsigned 8 bit integer | ||
''' | ||
data = (data / 2 + 0.5).clip(0, 1) | ||
return (data * 255).astype(numpy.uint8) | ||
|
||
|
||
def as_int8(data): | ||
''' Return data encoded as signed 8 bit integer | ||
''' | ||
data = data.clip(-1, 1) | ||
return (data * 127).astype(numpy.int8) | ||
|
||
|
||
def as_uint16(data): | ||
''' Return data encoded as unsigned 16 bit integer | ||
''' | ||
data = (data / 2 + 0.5).clip(0, 1) | ||
return (data * 65535).astype(numpy.uint16) | ||
|
||
|
||
def as_int16(data): | ||
''' Return data encoded as signed 16 bit integer | ||
''' | ||
data = data.clip(-1, 1) | ||
return (data * 32767).astype(numpy.int16) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import numpy | ||
import encode | ||
|
||
|
||
def pygame_play(data, rate=44100): | ||
''' Send audio array to pygame for playback | ||
''' | ||
import pygame | ||
pygame.mixer.init(rate, -16, 1, 1024) | ||
sound = pygame.sndarray.numpysnd.make_sound(encode.as_int16(data)) | ||
length = sound.get_length() | ||
sound.play() | ||
pygame.time.wait(int(length * 1000)) | ||
pygame.quit() | ||
|
||
|
||
def pygame_supported(): | ||
''' Return True is pygame playback is supported | ||
''' | ||
try: | ||
import pygame | ||
except: | ||
return False | ||
return True | ||
|
||
|
||
def oss_play(data, rate=44100): | ||
''' Send audio array to oss for playback | ||
''' | ||
import ossaudiodev | ||
audio = ossaudiodev.open('/dev/audio','w') | ||
formats = audio.getfmts() | ||
if ossaudiodev.AFMT_S16_LE in formats: | ||
# Use 16 bit if available | ||
audio.setfmt(ossaudiodev.AFMT_S16_LE) | ||
data = encode.as_int16(data) | ||
elif ossaudiodev.AFMT_U8 in formats: | ||
# Otherwise use 8 bit | ||
audio.setfmt(ossaudiodev.AFMT_U8) | ||
data = encode.as_uint8(data) | ||
audio.speed(rate) | ||
while len(data): | ||
audio.write(data[:1024]) | ||
data = data[1024:] | ||
audio.flush() | ||
audio.sync() | ||
audio.close() | ||
|
||
|
||
def oss_supported(): | ||
''' Return True is oss playback is supported | ||
''' | ||
try: | ||
import ossaudiodev | ||
except: | ||
return False | ||
return True | ||
|
||
|
||
def pyaudio_play(data, rate=44100): | ||
''' Send audio array to pyaudio for playback | ||
''' | ||
import pyaudio | ||
p = pyaudio.PyAudio() | ||
stream = p.open(format=pyaudio.paFloat32, channels=1, rate=rate, output=1) | ||
stream.write(data.astype(numpy.float32).tostring()) | ||
stream.close() | ||
p.terminate() | ||
|
||
|
||
def pyaudio_supported(): | ||
''' Return True is pyaudio playback is supported | ||
''' | ||
try: | ||
import pyaudio | ||
except: | ||
return False | ||
return True | ||
|
||
|
||
def play(data, rate=44100): | ||
''' Send audio to first available playback method | ||
''' | ||
if pygame_supported(): | ||
return pygame_play(data) | ||
elif oss_supported(): | ||
return oss_play(data) | ||
elif pyaudio_supported(): | ||
return pyaudio_play(data) | ||
else: | ||
raise Exception("No supported playback method found") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import encode | ||
|
||
# TODO: Support other formats and settings | ||
|
||
|
||
def save_wave(data, path, rate=44100): | ||
''' Save audio data to wave file, currently only 16bit | ||
''' | ||
import wave | ||
fp = wave.open(path, 'w') | ||
fp.setnchannels(1) | ||
fp.setframerate(rate) | ||
fp.setsampwidth(2) | ||
fp.setnframes(len(data)) | ||
data = encode.as_int16(data) | ||
fp.writeframes(data.tostring()) | ||
fp.close() | ||
|
Oops, something went wrong.