Create tools for compiling waves
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
from smnp.module.synth.function import synth, pause, plot
|
from smnp.module.synth.function import synth, pause, plot, compile
|
||||||
|
|
||||||
functions = [ synth.function, pause.function, plot.function ]
|
functions = [ synth.function, pause.function, plot.function, compile.function ]
|
||||||
methods = []
|
methods = []
|
||||||
141
smnp/module/synth/function/compile.py
Normal file
141
smnp/module/synth/function/compile.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
from smnp.error.runtime import RuntimeException
|
||||||
|
from smnp.function.model import Function, CombinedFunction
|
||||||
|
from smnp.function.signature import varargSignature
|
||||||
|
from smnp.module.synth.lib.wave import compilePolyphony
|
||||||
|
|
||||||
|
from smnp.type.model import Type
|
||||||
|
from smnp.type.signature.matcher.list import listOf
|
||||||
|
from smnp.type.signature.matcher.type import ofTypes, ofType
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_BPM = 120
|
||||||
|
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
|
||||||
|
DEFAULT_DECAY = 4
|
||||||
|
DEFAULT_ATTACK = 100
|
||||||
|
|
||||||
|
|
||||||
|
def getBpm(config):
|
||||||
|
key = Type.string("bpm")
|
||||||
|
if key in config.value:
|
||||||
|
bpm = config.value[key]
|
||||||
|
if bpm.type != Type.INTEGER or bpm.value <= 0:
|
||||||
|
raise RuntimeException("The 'bpm' property must be positive integer", None)
|
||||||
|
|
||||||
|
return bpm.value
|
||||||
|
|
||||||
|
return DEFAULT_BPM
|
||||||
|
|
||||||
|
|
||||||
|
def getOvertones(config):
|
||||||
|
key = Type.string("overtones")
|
||||||
|
if key in config.value:
|
||||||
|
overtones = config.value[key]
|
||||||
|
rawOvertones = [overtone.value for overtone in overtones.value]
|
||||||
|
if overtones.type != Type.LIST or not all(overtone.type in [Type.FLOAT, Type.INTEGER] for overtone in overtones.value):
|
||||||
|
raise RuntimeException("The 'overtones' property must be list of floats", None)
|
||||||
|
|
||||||
|
if len(rawOvertones) < 1:
|
||||||
|
raise RuntimeException("The 'overtones' property must contain one overtone at least", None)
|
||||||
|
|
||||||
|
if any(overtone < 0 for overtone in rawOvertones):
|
||||||
|
raise RuntimeException("The 'overtones' property mustn't contain negative values", None)
|
||||||
|
|
||||||
|
if sum(rawOvertones) > 1.0:
|
||||||
|
raise RuntimeException("The 'overtones' property must contain overtones which sum is not greater than 1.0", None)
|
||||||
|
|
||||||
|
return rawOvertones
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def getAttack(config):
|
||||||
|
key = Type.string("attack")
|
||||||
|
if key in config.value:
|
||||||
|
attack = config.value[key]
|
||||||
|
if not attack.type in [Type.INTEGER, Type.FLOAT] or attack.value < 0:
|
||||||
|
raise RuntimeException("The 'attack' property must be non-negative integer or float", None)
|
||||||
|
|
||||||
|
return attack.value
|
||||||
|
|
||||||
|
return DEFAULT_ATTACK
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
def __init__(self, bpm, overtones, decay, attack):
|
||||||
|
self.bpm = bpm
|
||||||
|
self.overtones = overtones
|
||||||
|
self.decay = decay
|
||||||
|
self.attack = attack
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default():
|
||||||
|
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK)
|
||||||
|
|
||||||
|
|
||||||
|
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||||
|
def _function1(env, notes):
|
||||||
|
return Type.list([Type.float(float(m)) for m in __function1(notes)])
|
||||||
|
|
||||||
|
|
||||||
|
def __function1(notes):
|
||||||
|
return compilePolyphony([note.value for note in notes], Config.default())
|
||||||
|
|
||||||
|
|
||||||
|
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||||
|
def _function2(env, notes):
|
||||||
|
return Type.list([Type.float(float(m)) for m in __function2(notes)])
|
||||||
|
|
||||||
|
|
||||||
|
def __function2(notes):
|
||||||
|
return compilePolyphony([ notes ], Config.default())
|
||||||
|
|
||||||
|
|
||||||
|
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
|
def _function3(env, config, notes):
|
||||||
|
return Type.list([ Type.float(float(m)) for m in __function3(config, notes) ])
|
||||||
|
|
||||||
|
|
||||||
|
def __function3(config, notes):
|
||||||
|
rawNotes = [note.value for note in notes]
|
||||||
|
|
||||||
|
bpm = getBpm(config)
|
||||||
|
overtones = getOvertones(config)
|
||||||
|
decay = getDecay(config)
|
||||||
|
attack = getAttack(config)
|
||||||
|
|
||||||
|
return compilePolyphony(rawNotes, Config(bpm, overtones, decay, attack))
|
||||||
|
|
||||||
|
|
||||||
|
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
|
def _function4(env, config, notes):
|
||||||
|
return Type.list([ Type.float(float(m)) for m in __function4(config, notes) ])
|
||||||
|
|
||||||
|
|
||||||
|
def __function4(config, notes):
|
||||||
|
bpm = getBpm(config)
|
||||||
|
overtones = getOvertones(config)
|
||||||
|
decay = getDecay(config)
|
||||||
|
attack = getAttack(config)
|
||||||
|
|
||||||
|
return compilePolyphony([ notes ], Config(bpm, overtones, decay, attack))
|
||||||
|
|
||||||
|
|
||||||
|
function = CombinedFunction(
|
||||||
|
'wave',
|
||||||
|
Function(_signature1, _function1),
|
||||||
|
Function(_signature2, _function2),
|
||||||
|
Function(_signature3, _function3),
|
||||||
|
Function(_signature4, _function4),
|
||||||
|
)
|
||||||
@@ -1,106 +1,13 @@
|
|||||||
from smnp.error.runtime import RuntimeException
|
from smnp.function.model import Function
|
||||||
from smnp.function.model import CombinedFunction, Function
|
|
||||||
from smnp.function.signature import signature
|
from smnp.function.signature import signature
|
||||||
from smnp.module.synth.lib.wave import plot
|
from smnp.module.synth.lib.wave import plot
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
from smnp.type.signature.matcher.type import ofType
|
from smnp.type.signature.matcher.list import listOf
|
||||||
|
|
||||||
DEFAULT_BPM = 120
|
_signature = signature(listOf(Type.FLOAT))
|
||||||
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
|
def _function(env, wave):
|
||||||
DEFAULT_DECAY = 4
|
rawWave = [ m.value for m in wave.value ]
|
||||||
DEFAULT_ATTACK = 100
|
plot(rawWave)
|
||||||
|
|
||||||
# TODO: this code is shared with synth.py module, remove repetition
|
|
||||||
def getBpm(config):
|
|
||||||
key = Type.string("bpm")
|
|
||||||
if key in config.value:
|
|
||||||
bpm = config.value[key]
|
|
||||||
if bpm.type != Type.INTEGER or bpm.value <= 0:
|
|
||||||
raise RuntimeException("The 'bpm' property must be positive integer", None)
|
|
||||||
|
|
||||||
return bpm.value
|
|
||||||
|
|
||||||
return DEFAULT_BPM
|
|
||||||
|
|
||||||
|
|
||||||
def getOvertones(config):
|
function = Function(_signature, _function, 'plotWave')
|
||||||
key = Type.string("overtones")
|
|
||||||
if key in config.value:
|
|
||||||
overtones = config.value[key]
|
|
||||||
rawOvertones = [overtone.value for overtone in overtones.value]
|
|
||||||
if overtones.type != Type.LIST or not all(overtone.type in [Type.FLOAT, Type.INTEGER] for overtone in overtones.value):
|
|
||||||
raise RuntimeException("The 'overtones' property must be list of floats", None)
|
|
||||||
|
|
||||||
if len(rawOvertones) < 1:
|
|
||||||
raise RuntimeException("The 'overtones' property must contain one overtone at least", None)
|
|
||||||
|
|
||||||
if any(overtone < 0 for overtone in rawOvertones):
|
|
||||||
raise RuntimeException("The 'overtones' property mustn't contain negative values", None)
|
|
||||||
|
|
||||||
if sum(rawOvertones) > 1.0:
|
|
||||||
raise RuntimeException("The 'overtones' property must contain overtones which sum is not greater than 1.0", None)
|
|
||||||
|
|
||||||
return rawOvertones
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def getAttack(config):
|
|
||||||
key = Type.string("attack")
|
|
||||||
if key in config.value:
|
|
||||||
attack = config.value[key]
|
|
||||||
if not attack.type in [Type.INTEGER, Type.FLOAT] or attack.value < 0:
|
|
||||||
raise RuntimeException("The 'attack' property must be non-negative integer or float", None)
|
|
||||||
|
|
||||||
return attack.value
|
|
||||||
|
|
||||||
return DEFAULT_ATTACK
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
def __init__(self, bpm, overtones, decay, attack):
|
|
||||||
self.bpm = bpm
|
|
||||||
self.overtones = overtones
|
|
||||||
self.decay = decay
|
|
||||||
self.attack = attack
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default():
|
|
||||||
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK)
|
|
||||||
|
|
||||||
|
|
||||||
_signature1 = signature(ofType(Type.NOTE))
|
|
||||||
def _function1(env, note):
|
|
||||||
config = Config.default()
|
|
||||||
|
|
||||||
plot(note.value, config)
|
|
||||||
|
|
||||||
|
|
||||||
_signature2 = signature(ofType(Type.MAP), ofType(Type.NOTE))
|
|
||||||
def _function2(env, config, note):
|
|
||||||
bpm = getBpm(config)
|
|
||||||
overtones = getOvertones(config)
|
|
||||||
decay = getDecay(config)
|
|
||||||
attack = getAttack(config)
|
|
||||||
|
|
||||||
plot(note.value, Config(bpm, overtones, decay, attack))
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
'plot',
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,119 +1,40 @@
|
|||||||
from smnp.error.runtime import RuntimeException
|
|
||||||
from smnp.function.model import Function, CombinedFunction
|
from smnp.function.model import Function, CombinedFunction
|
||||||
from smnp.function.signature import varargSignature
|
from smnp.function.signature import varargSignature
|
||||||
|
from smnp.module.synth.function import compile
|
||||||
from smnp.module.synth.lib.wave import play
|
from smnp.module.synth.lib.wave import play
|
||||||
|
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
from smnp.type.signature.matcher.list import listOf
|
from smnp.type.signature.matcher.list import listOf
|
||||||
from smnp.type.signature.matcher.type import ofTypes, ofType
|
from smnp.type.signature.matcher.type import ofTypes, ofType
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BPM = 120
|
|
||||||
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
|
|
||||||
DEFAULT_DECAY = 4
|
|
||||||
DEFAULT_ATTACK = 100
|
|
||||||
|
|
||||||
|
|
||||||
def getBpm(config):
|
|
||||||
key = Type.string("bpm")
|
|
||||||
if key in config.value:
|
|
||||||
bpm = config.value[key]
|
|
||||||
if bpm.type != Type.INTEGER or bpm.value <= 0:
|
|
||||||
raise RuntimeException("The 'bpm' property must be positive integer", None)
|
|
||||||
|
|
||||||
return bpm.value
|
|
||||||
|
|
||||||
return DEFAULT_BPM
|
|
||||||
|
|
||||||
|
|
||||||
def getOvertones(config):
|
|
||||||
key = Type.string("overtones")
|
|
||||||
if key in config.value:
|
|
||||||
overtones = config.value[key]
|
|
||||||
rawOvertones = [overtone.value for overtone in overtones.value]
|
|
||||||
if overtones.type != Type.LIST or not all(overtone.type in [Type.FLOAT, Type.INTEGER] for overtone in overtones.value):
|
|
||||||
raise RuntimeException("The 'overtones' property must be list of floats", None)
|
|
||||||
|
|
||||||
if len(rawOvertones) < 1:
|
|
||||||
raise RuntimeException("The 'overtones' property must contain one overtone at least", None)
|
|
||||||
|
|
||||||
if any(overtone < 0 for overtone in rawOvertones):
|
|
||||||
raise RuntimeException("The 'overtones' property mustn't contain negative values", None)
|
|
||||||
|
|
||||||
if sum(rawOvertones) > 1.0:
|
|
||||||
raise RuntimeException("The 'overtones' property must contain overtones which sum is not greater than 1.0", None)
|
|
||||||
|
|
||||||
return rawOvertones
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def getAttack(config):
|
|
||||||
key = Type.string("attack")
|
|
||||||
if key in config.value:
|
|
||||||
attack = config.value[key]
|
|
||||||
if not attack.type in [Type.INTEGER, Type.FLOAT] or attack.value < 0:
|
|
||||||
raise RuntimeException("The 'attack' property must be non-negative integer or float", None)
|
|
||||||
|
|
||||||
return attack.value
|
|
||||||
|
|
||||||
return DEFAULT_ATTACK
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
def __init__(self, bpm, overtones, decay, attack):
|
|
||||||
self.bpm = bpm
|
|
||||||
self.overtones = overtones
|
|
||||||
self.decay = decay
|
|
||||||
self.attack = attack
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default():
|
|
||||||
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK)
|
|
||||||
|
|
||||||
|
|
||||||
_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]
|
wave = compile.__function1(notes)
|
||||||
play(rawNotes, Config.default())
|
play(wave)
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||||
def _function2(env, notes):
|
def _function2(env, notes):
|
||||||
play([ notes ], Config.default())
|
wave = compile.__function2(notes)
|
||||||
|
play(wave)
|
||||||
|
|
||||||
|
|
||||||
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
def _function3(env, config, notes):
|
def _function3(env, config, notes):
|
||||||
rawNotes = [note.value for note in notes]
|
wave = compile.__function3(config, notes)
|
||||||
bpm = getBpm(config)
|
play(wave)
|
||||||
overtones = getOvertones(config)
|
|
||||||
decay = getDecay(config)
|
|
||||||
attack = getAttack(config)
|
|
||||||
|
|
||||||
play(rawNotes, Config(bpm, overtones, decay, attack))
|
|
||||||
|
|
||||||
|
|
||||||
_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)
|
wave = compile.__function4(config, notes)
|
||||||
overtones = getOvertones(config)
|
play(wave)
|
||||||
decay = getDecay(config)
|
|
||||||
attack = getAttack(config)
|
|
||||||
|
|
||||||
play([ notes ], Config(bpm, overtones, decay, attack))
|
|
||||||
|
_signature5 = varargSignature(listOf(Type.FLOAT))
|
||||||
|
def _function5(env, waves):
|
||||||
|
for wave in waves:
|
||||||
|
rawWave = [m.value for m in wave.value]
|
||||||
|
play(rawWave)
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
function = CombinedFunction(
|
||||||
@@ -122,4 +43,5 @@ function = CombinedFunction(
|
|||||||
Function(_signature2, _function2),
|
Function(_signature2, _function2),
|
||||||
Function(_signature3, _function3),
|
Function(_signature3, _function3),
|
||||||
Function(_signature4, _function4),
|
Function(_signature4, _function4),
|
||||||
|
Function(_signature5, _function5)
|
||||||
)
|
)
|
||||||
@@ -13,17 +13,15 @@ def pause(value, bpm):
|
|||||||
time.sleep(60 * 4 / value / bpm)
|
time.sleep(60 * 4 / value / bpm)
|
||||||
|
|
||||||
|
|
||||||
def plot(note, config):
|
def plot(wave):
|
||||||
Y = sineForNote(note, config)
|
X = np.arange(len(wave))
|
||||||
X = np.arange(len(Y))
|
plt.plot(X, wave)
|
||||||
plt.plot(X, Y)
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
def play(notes, config):
|
def play(wave):
|
||||||
compiled = compilePolyphony(notes, config)
|
sd.play(wave)
|
||||||
sd.play(compiled)
|
time.sleep(len(wave) / FS)
|
||||||
time.sleep(len(compiled) / FS)
|
|
||||||
|
|
||||||
|
|
||||||
def compilePolyphony(notes, config):
|
def compilePolyphony(notes, config):
|
||||||
|
|||||||
Reference in New Issue
Block a user