Create tools for compiling waves

This commit is contained in:
Bartłomiej Pluta
2019-07-30 16:41:49 +02:00
parent a7de7f0279
commit aca6e6bb55
5 changed files with 172 additions and 204 deletions

View File

@@ -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 = []

View 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),
)

View File

@@ -1,106 +1,13 @@
from smnp.error.runtime import RuntimeException
from smnp.function.model import CombinedFunction, Function
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.module.synth.lib.wave import plot
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
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
DEFAULT_DECAY = 4
DEFAULT_ATTACK = 100
# 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
_signature = signature(listOf(Type.FLOAT))
def _function(env, wave):
rawWave = [ m.value for m in wave.value ]
plot(rawWave)
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 = 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)
)
function = Function(_signature, _function, 'plotWave')

View File

@@ -1,119 +1,40 @@
from smnp.error.runtime import RuntimeException
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.module.synth.function import compile
from smnp.module.synth.lib.wave import play
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):
rawNotes = [note.value for note in notes]
play(rawNotes, Config.default())
wave = compile.__function1(notes)
play(wave)
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function2(env, notes):
play([ notes ], Config.default())
wave = compile.__function2(notes)
play(wave)
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function3(env, config, notes):
rawNotes = [note.value for note in notes]
bpm = getBpm(config)
overtones = getOvertones(config)
decay = getDecay(config)
attack = getAttack(config)
play(rawNotes, Config(bpm, overtones, decay, attack))
wave = compile.__function3(config, notes)
play(wave)
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function4(env, config, notes):
bpm = getBpm(config)
overtones = getOvertones(config)
decay = getDecay(config)
attack = getAttack(config)
wave = compile.__function4(config, notes)
play(wave)
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(
@@ -122,4 +43,5 @@ function = CombinedFunction(
Function(_signature2, _function2),
Function(_signature3, _function3),
Function(_signature4, _function4),
Function(_signature5, _function5)
)

View File

@@ -13,17 +13,15 @@ def pause(value, bpm):
time.sleep(60 * 4 / value / bpm)
def plot(note, config):
Y = sineForNote(note, config)
X = np.arange(len(Y))
plt.plot(X, Y)
def plot(wave):
X = np.arange(len(wave))
plt.plot(X, wave)
plt.show()
def play(notes, config):
compiled = compilePolyphony(notes, config)
sd.play(compiled)
time.sleep(len(compiled) / FS)
def play(wave):
sd.play(wave)
time.sleep(len(wave) / FS)
def compilePolyphony(notes, config):