Add decay to synthetiser
This commit is contained in:
@@ -10,6 +10,7 @@ from smnp.type.signature.matcher.type import ofTypes, ofType
|
|||||||
|
|
||||||
DEFAULT_BPM = 120
|
DEFAULT_BPM = 120
|
||||||
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
|
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
|
||||||
|
DEFAULT_DECAY = 4
|
||||||
|
|
||||||
|
|
||||||
def getBpm(config):
|
def getBpm(config):
|
||||||
@@ -46,16 +47,38 @@ def getOvertones(config):
|
|||||||
return DEFAULT_OVERTONES
|
return DEFAULT_OVERTONES
|
||||||
|
|
||||||
|
|
||||||
|
def getDecay(config):
|
||||||
|
key = Type.string("decay")
|
||||||
|
if key in config.value:
|
||||||
|
decay = config.value[key]
|
||||||
|
if not decay.type in [Type.INTEGER, Type.FLOAT] or decay.value < 0:
|
||||||
|
raise RuntimeException("The 'decay' property must be non-negative integer or float", None)
|
||||||
|
|
||||||
|
return decay.value
|
||||||
|
|
||||||
|
return DEFAULT_DECAY
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
def __init__(self, bpm, overtones, decay):
|
||||||
|
self.bpm = bpm
|
||||||
|
self.overtones = overtones
|
||||||
|
self.decay = decay
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default():
|
||||||
|
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY)
|
||||||
|
|
||||||
|
|
||||||
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||||
def _function1(env, notes):
|
def _function1(env, notes):
|
||||||
rawNotes = [note.value for note in notes]
|
rawNotes = [note.value for note in notes]
|
||||||
play(rawNotes, DEFAULT_BPM, DEFAULT_OVERTONES)
|
play(rawNotes, Config.default())
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||||
def _function2(env, notes):
|
def _function2(env, notes):
|
||||||
play([ notes ], DEFAULT_BPM, DEFAULT_OVERTONES)
|
play([ notes ], Config.default())
|
||||||
|
|
||||||
|
|
||||||
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
@@ -63,15 +86,18 @@ def _function3(env, config, notes):
|
|||||||
rawNotes = [note.value for note in notes]
|
rawNotes = [note.value for note in notes]
|
||||||
bpm = getBpm(config)
|
bpm = getBpm(config)
|
||||||
overtones = getOvertones(config)
|
overtones = getOvertones(config)
|
||||||
|
decay = getDecay(config)
|
||||||
|
|
||||||
play(rawNotes, bpm, overtones)
|
play(rawNotes, Config(bpm, overtones, decay))
|
||||||
|
|
||||||
|
|
||||||
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
def _function4(env, config, notes):
|
def _function4(env, config, notes):
|
||||||
bpm = getBpm(config)
|
bpm = getBpm(config)
|
||||||
overtones = getOvertones(config)
|
overtones = getOvertones(config)
|
||||||
play([ notes ], bpm, overtones)
|
decay = getDecay(config)
|
||||||
|
|
||||||
|
play([ notes ], Config(bpm, overtones, decay))
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
function = CombinedFunction(
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ def pause(value, bpm):
|
|||||||
time.sleep(60 * 4 / value / bpm)
|
time.sleep(60 * 4 / value / bpm)
|
||||||
|
|
||||||
|
|
||||||
def play(notes, bpm, overtones):
|
def play(notes, config):
|
||||||
compiled = compilePolyphony(notes, bpm, overtones)
|
compiled = compilePolyphony(notes, config)
|
||||||
sd.play(compiled)
|
sd.play(compiled)
|
||||||
time.sleep(len(compiled) / FS)
|
time.sleep(len(compiled) / FS)
|
||||||
|
|
||||||
|
|
||||||
def compilePolyphony(notes, bpm, overtones):
|
def compilePolyphony(notes, config):
|
||||||
compiledLines = [1 / len(notes) * compileNotes(line, bpm, overtones) for line in notes]
|
compiledLines = [1 / len(notes) * compileNotes(line, config) for line in notes]
|
||||||
return sum(adjustSize(compiledLines))
|
return sum(adjustSize(compiledLines))
|
||||||
|
|
||||||
|
|
||||||
@@ -29,30 +29,36 @@ def adjustSize(compiledLines):
|
|||||||
return [np.concatenate([line, np.zeros(maxSize - len(line))]) for line in compiledLines]
|
return [np.concatenate([line, np.zeros(maxSize - len(line))]) for line in compiledLines]
|
||||||
|
|
||||||
|
|
||||||
def compileNotes(notes, bpm, overtones):
|
def compileNotes(notes, config):
|
||||||
dispatcher = {
|
dispatcher = {
|
||||||
Type.NOTE: lambda note, overtones: sineForNote(note.value, bpm, overtones),
|
Type.NOTE: lambda note, overtones: sineForNote(note.value, config),
|
||||||
Type.INTEGER: lambda note, overtones: silenceForPause(note.value, bpm)
|
Type.INTEGER: lambda note, overtones: silenceForPause(note.value, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
return np.concatenate([dispatcher[note.type](note, overtones) for note in notes])
|
return np.concatenate([dispatcher[note.type](note, config) for note in notes])
|
||||||
|
|
||||||
|
|
||||||
def sineForNote(note, bpm, overtones):
|
def sineForNote(note, config):
|
||||||
frequency = note.toFrequency()
|
frequency = note.toFrequency()
|
||||||
duration = 60 * 4 / note.duration / bpm
|
duration = 60 * 4 / note.duration / config.bpm
|
||||||
duration *= 1.5 if note.dot else 1
|
duration *= 1.5 if note.dot else 1
|
||||||
return sound(frequency, duration, overtones)
|
return sound(frequency, duration, config)
|
||||||
|
|
||||||
|
|
||||||
def sound(frequency, duration, overtones):
|
def sound(frequency, duration, config):
|
||||||
return sum(a * sine((i+1) * frequency, duration) for i, a in enumerate(overtones))
|
return decay(sum(a * sine((i+1) * frequency, duration) for i, a in enumerate(config.overtones)), config)
|
||||||
|
|
||||||
|
|
||||||
|
def decay(wave, config):
|
||||||
|
magnitude = np.exp(-config.decay/len(wave) * np.arange(len(wave)))
|
||||||
|
|
||||||
|
return magnitude * wave
|
||||||
|
|
||||||
|
|
||||||
def sine(frequency, duration):
|
def sine(frequency, duration):
|
||||||
return (np.sin(2 * np.pi * np.arange(FS * duration) * frequency / FS)).astype(np.float32)
|
return (np.sin(2 * np.pi * np.arange(FS * duration) * frequency / FS)).astype(np.float32)
|
||||||
|
|
||||||
|
|
||||||
def silenceForPause(value, bpm):
|
def silenceForPause(value, config):
|
||||||
duration = 60 * 4 / value / bpm
|
duration = 60 * 4 / value / config.bpm
|
||||||
return np.zeros(int(FS * duration))
|
return np.zeros(int(FS * duration))
|
||||||
|
|||||||
Reference in New Issue
Block a user