Change Synth backend
This commit is contained in:
5
Audio.py
5
Audio.py
@@ -4,10 +4,7 @@ import time
|
|||||||
|
|
||||||
BREAK = 0
|
BREAK = 0
|
||||||
|
|
||||||
def playNote(note, bpm):
|
|
||||||
frequency = note.toFrequency()
|
|
||||||
duration = 60 * 4 / note.duration / bpm
|
|
||||||
pysine.sine(frequency, duration)
|
|
||||||
#TODO: duration powinno byc w prawdziwych nutach: 4 = cwierc, 2 = pol etc.
|
#TODO: duration powinno byc w prawdziwych nutach: 4 = cwierc, 2 = pol etc.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
205
Environment.py
Normal file
205
Environment.py
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import sys
|
||||||
|
from Evaluator import RuntimeException, objectString
|
||||||
|
from Note import *
|
||||||
|
import random
|
||||||
|
import Synth
|
||||||
|
import time
|
||||||
|
|
||||||
|
types = {
|
||||||
|
int: 'integer',
|
||||||
|
str: 'string',
|
||||||
|
list: 'list',
|
||||||
|
float: 'percent',
|
||||||
|
Note: 'note',
|
||||||
|
type(None): 'void'
|
||||||
|
}
|
||||||
|
|
||||||
|
class Environment():
|
||||||
|
def __init__(self, scopes, functions):
|
||||||
|
self.scopes = scopes
|
||||||
|
self.functions = functions
|
||||||
|
|
||||||
|
def findVariable(self, name, type=None):
|
||||||
|
for scope in reversed(self.scopes):
|
||||||
|
if name in scope:
|
||||||
|
value = scope[name]
|
||||||
|
if type is not None:
|
||||||
|
if isinstance(value, type):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
raise RuntimeException(f"Variable '{name}' is not declared" + ("" if type is None else f" (expected type: {types[type]})"))
|
||||||
|
|
||||||
|
def findVariableScope(self, name, type=None):
|
||||||
|
for scope in reversed(self.scopes):
|
||||||
|
if name in scope:
|
||||||
|
if type is not None:
|
||||||
|
if isinstance(scope[name], type):
|
||||||
|
return scope
|
||||||
|
else:
|
||||||
|
return scope
|
||||||
|
|
||||||
|
def sample(args, env):
|
||||||
|
if len(args) == 1 and isinstance(args[0], list):
|
||||||
|
return _sample(args[0])
|
||||||
|
elif len(args) == 0:
|
||||||
|
return _sample(Note.range(Note(NotePitch.C), Note(NotePitch.H)))
|
||||||
|
elif all(isinstance(x, Note) for x in args):
|
||||||
|
return _sample(args)
|
||||||
|
else:
|
||||||
|
pass # not valid signature
|
||||||
|
|
||||||
|
def _sample(list):
|
||||||
|
return list[int(random.uniform(0, len(list)))]
|
||||||
|
|
||||||
|
def doPrint(args, env):
|
||||||
|
print("".join([objectString(arg) for arg in args]))
|
||||||
|
|
||||||
|
def semitonesList(list):
|
||||||
|
for x in list:
|
||||||
|
if not isinstance(x, Note) and not isistance(x, int):
|
||||||
|
pass # invalid arguments
|
||||||
|
withoutPauses = tuple(filter(lambda x: isinstance(x, Note), list))
|
||||||
|
r = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i, _ in enumerate(withoutPauses) if i != 0]
|
||||||
|
return returnElementOrList(r)
|
||||||
|
|
||||||
|
def returnElementOrList(list):
|
||||||
|
return list[0] if len(list) == 1 else list
|
||||||
|
|
||||||
|
def semitones(args, env):
|
||||||
|
if len(args) > 0 and isinstance(args[0], list):
|
||||||
|
return semitonesList(args[0])
|
||||||
|
return semitonesList(args)
|
||||||
|
|
||||||
|
def intervalList(list):
|
||||||
|
r = [intervalToString(x) for x in list]
|
||||||
|
return returnElementOrList(r)
|
||||||
|
|
||||||
|
def interval(args, env):
|
||||||
|
if len(args) > 0 and isinstance(args[0], list):
|
||||||
|
return intervalList(args[0])
|
||||||
|
return intervalList(args)
|
||||||
|
|
||||||
|
def transposeTo(args, env):
|
||||||
|
if len(args) > 1 and isinstance(args[0], Note) and all(isinstance(x, list) for i, x in enumerate(args) if i != 0):
|
||||||
|
target = args[0]
|
||||||
|
result = []
|
||||||
|
for i, notes in enumerate(args):
|
||||||
|
if i == 0:
|
||||||
|
continue
|
||||||
|
if len(notes) > 0:
|
||||||
|
first = notes[0]
|
||||||
|
semitones = semitonesList([target, first])
|
||||||
|
result.append([note.transpose(semitones) for note in notes if isinstance(note, Note)])
|
||||||
|
else:
|
||||||
|
result.append([])
|
||||||
|
return returnElementOrList(result)
|
||||||
|
else:
|
||||||
|
pass # not valid signature
|
||||||
|
|
||||||
|
|
||||||
|
def transpose(args, env):
|
||||||
|
if len(args) > 1 and isinstance(args[0], int):
|
||||||
|
value = args[0]
|
||||||
|
transposed = []
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if i == 0:
|
||||||
|
continue
|
||||||
|
if not isinstance(arg, list):
|
||||||
|
return # is not list
|
||||||
|
transposed.append([note.transpose(value) for note in arg if isinstance(note, Note)])
|
||||||
|
return transposed
|
||||||
|
else:
|
||||||
|
return # not valid signature
|
||||||
|
|
||||||
|
def objectType(args, env):
|
||||||
|
if len(args) == 1:
|
||||||
|
return types[type(args[0])]
|
||||||
|
else:
|
||||||
|
pass # not valid signature
|
||||||
|
|
||||||
|
def exit(args, env):
|
||||||
|
if len(args) == 1 and isinstance(args[0], int):
|
||||||
|
sys.exit(args[0])
|
||||||
|
else:
|
||||||
|
pass # not valid signature
|
||||||
|
|
||||||
|
def upper(args, env):
|
||||||
|
value = []
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, Note):
|
||||||
|
value.append(arg.getUpperNeighbour())
|
||||||
|
elif isinstance(arg, list):
|
||||||
|
value.append(upperList(arg))
|
||||||
|
else:
|
||||||
|
pass # invalid argument
|
||||||
|
return returnElementOrList(value)
|
||||||
|
|
||||||
|
def upperList(list):
|
||||||
|
if all(isinstance(x, Note) for x in list):
|
||||||
|
value = [note.getUpperNeighbour() for note in list]
|
||||||
|
return returnElementOrList(value)
|
||||||
|
else:
|
||||||
|
pass #not valid signature
|
||||||
|
|
||||||
|
def lower(args, env):
|
||||||
|
value = []
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, Note):
|
||||||
|
value.append(arg.getLowerNeighbour())
|
||||||
|
elif isinstance(arg, list):
|
||||||
|
value.append(lowerList(arg))
|
||||||
|
else:
|
||||||
|
pass # invalid argument
|
||||||
|
return returnElementOrList(value)
|
||||||
|
|
||||||
|
def lowerList(list):
|
||||||
|
if all(isinstance(x, Note) for x in list):
|
||||||
|
value = [note.getLowerNeighbour() for note in list]
|
||||||
|
return returnElementOrList(value)
|
||||||
|
else:
|
||||||
|
pass #not valid signature
|
||||||
|
|
||||||
|
def sleep(args, env):
|
||||||
|
if len(args) == 1 and isinstance(args[0], int):
|
||||||
|
time.sleep(args[0])
|
||||||
|
else:
|
||||||
|
pass # not valid signature
|
||||||
|
|
||||||
|
def rand(args, env):
|
||||||
|
if not all(isinstance(x, list) and len(x) == 2 and isinstance(x[0], float) for x in args):
|
||||||
|
return # not valid signature
|
||||||
|
if sum([x[0] for x in args]) != 1.0:
|
||||||
|
return # not sums to 100%
|
||||||
|
choice = random.random()
|
||||||
|
acc = 0
|
||||||
|
for e in args:
|
||||||
|
acc += e[0]
|
||||||
|
if choice <= acc:
|
||||||
|
return e[1]
|
||||||
|
|
||||||
|
def createEnvironment():
|
||||||
|
functions = {
|
||||||
|
'print': doPrint,
|
||||||
|
'synth': Synth.play,
|
||||||
|
'pause': Synth.pause,
|
||||||
|
'type': objectType,
|
||||||
|
'sample': sample,
|
||||||
|
'semitones': semitones,
|
||||||
|
'interval': interval,
|
||||||
|
'transpose': transpose,
|
||||||
|
'transposeTo': transposeTo,
|
||||||
|
'upper': upper,
|
||||||
|
'lower': lower,
|
||||||
|
'sleep': sleep,
|
||||||
|
'random': rand,
|
||||||
|
'exit': exit
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"bpm": 120
|
||||||
|
}
|
||||||
|
|
||||||
|
return Environment([ variables ], functions)
|
||||||
|
|
||||||
@@ -1,20 +1,11 @@
|
|||||||
import os
|
|
||||||
from Tokenizer import tokenize, TokenType
|
from Tokenizer import tokenize, TokenType
|
||||||
from Parser import parse
|
from Parser import parse
|
||||||
from AST import *
|
from AST import *
|
||||||
import sys
|
from Note import Note
|
||||||
from Note import *
|
|
||||||
import random
|
|
||||||
import Midi
|
|
||||||
|
|
||||||
class RuntimeException(Exception):
|
class RuntimeException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Environment():
|
|
||||||
def __init__(self, scopes, functions):
|
|
||||||
self.scopes = scopes
|
|
||||||
self.functions = functions
|
|
||||||
|
|
||||||
def evaluateProgram(program, environment):
|
def evaluateProgram(program, environment):
|
||||||
for node in program.children:
|
for node in program.children:
|
||||||
evaluate(node, environment)
|
evaluate(node, environment)
|
||||||
@@ -23,18 +14,12 @@ def evaluateInteger(integer, environment):
|
|||||||
return integer.value
|
return integer.value
|
||||||
|
|
||||||
def evaluatePercent(percent, environment):
|
def evaluatePercent(percent, environment):
|
||||||
pass
|
return percent.value.value * 0.01
|
||||||
|
|
||||||
def evaluateIdentifier(identifier, environment):
|
def evaluateIdentifier(identifier, environment):
|
||||||
value = findVariable(identifier.identifier, environment)
|
value = environment.findVariable(identifier.identifier)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def findVariable(name, environment):
|
|
||||||
for scope in reversed(environment.scopes):
|
|
||||||
if name in scope:
|
|
||||||
return scope[name]
|
|
||||||
raise RuntimeException(f"Variable '{name}' is not declared")
|
|
||||||
|
|
||||||
def evaluateString(string, environment):
|
def evaluateString(string, environment):
|
||||||
value = string.value
|
value = string.value
|
||||||
for scope in reversed(environment.scopes):
|
for scope in reversed(environment.scopes):
|
||||||
@@ -42,6 +27,21 @@ def evaluateString(string, environment):
|
|||||||
value = value.replace('{' + k + '}', objectString(v)) #TODO: poprawic
|
value = value.replace('{' + k + '}', objectString(v)) #TODO: poprawic
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def objectString(obj):
|
||||||
|
if isinstance(obj, str):
|
||||||
|
return obj
|
||||||
|
if isinstance(obj, int):
|
||||||
|
return str(obj)
|
||||||
|
if isinstance(obj, Note):
|
||||||
|
return obj.note.name
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return "(" + ", ".join([objectString(v) for v in obj]) + ")"
|
||||||
|
if isinstance(obj, float):
|
||||||
|
return f"{int(obj*100)}%"
|
||||||
|
if obj is None:
|
||||||
|
raise RuntimeException(f"Trying to interpret void")
|
||||||
|
raise RuntimeException(f"Don't know how to interpret {str(obj)}")
|
||||||
|
|
||||||
def evaluateNote(note, environment):
|
def evaluateNote(note, environment):
|
||||||
return note.value
|
return note.value
|
||||||
|
|
||||||
@@ -69,7 +69,11 @@ def evaluateList(list, environment):
|
|||||||
def evaluateAssignment(assignment, environment):
|
def evaluateAssignment(assignment, environment):
|
||||||
target = assignment.target.identifier
|
target = assignment.target.identifier
|
||||||
value = evaluate(assignment.value, environment)
|
value = evaluate(assignment.value, environment)
|
||||||
environment.scopes[-1][target] = value
|
scopeOfExistingVariable = environment.findVariableScope(target)
|
||||||
|
if scopeOfExistingVariable is not None:
|
||||||
|
scopeOfExistingVariable[target] = value
|
||||||
|
else:
|
||||||
|
environment.scopes[-1][target] = value
|
||||||
|
|
||||||
def evaluateAsterisk(asterisk, environment):
|
def evaluateAsterisk(asterisk, environment):
|
||||||
count = evaluate(asterisk.iterator, environment)
|
count = evaluate(asterisk.iterator, environment)
|
||||||
@@ -130,77 +134,3 @@ def evaluate(input, environment):
|
|||||||
return evaluateColon(input, environment)
|
return evaluateColon(input, environment)
|
||||||
if isinstance(input, IdentifierNode):
|
if isinstance(input, IdentifierNode):
|
||||||
return evaluateIdentifier(input, environment)
|
return evaluateIdentifier(input, environment)
|
||||||
|
|
||||||
def rand(args, env):
|
|
||||||
if len(args) == 1 and isinstance(args[0], list):
|
|
||||||
return args[0][int(random.uniform(0, len(args[0])))]
|
|
||||||
|
|
||||||
def objectString(obj):
|
|
||||||
if isinstance(obj, str):
|
|
||||||
return obj
|
|
||||||
if isinstance(obj, int):
|
|
||||||
return str(obj)
|
|
||||||
if isinstance(obj, Note):
|
|
||||||
return obj.note.name
|
|
||||||
if isinstance(obj, list):
|
|
||||||
return "(" + ", ".join([objectString(v) for v in obj]) + ")"
|
|
||||||
raise RuntimeException(f"Don't know how to interpret {str(obj)}")
|
|
||||||
|
|
||||||
def prt(args, env):
|
|
||||||
print("".join([objectString(arg) for arg in args]))
|
|
||||||
|
|
||||||
def semitonesList(list):
|
|
||||||
withoutPauses = tuple(filter(lambda x: isinstance(x, Note), list))
|
|
||||||
r = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i, _ in enumerate(withoutPauses) if i != 0]
|
|
||||||
return r[0] if len(r) == 1 else r
|
|
||||||
|
|
||||||
def semitones(args, env):
|
|
||||||
if len(args) > 0 and isinstance(args[0], list):
|
|
||||||
return semitonesList(args[0])
|
|
||||||
return semitonesList(args)
|
|
||||||
|
|
||||||
def intervalList(list):
|
|
||||||
r = [intervalToString(x) for x in list]
|
|
||||||
return r[0] if len(r) == 1 else r
|
|
||||||
|
|
||||||
def interval(args, env):
|
|
||||||
if len(args) > 0 and isinstance(args[0], list):
|
|
||||||
return intervalList(args[0])
|
|
||||||
return intervalList(args)
|
|
||||||
|
|
||||||
def transpose(args, env):
|
|
||||||
if len(args) > 1 and isinstance(args[0], int):
|
|
||||||
value = args[0]
|
|
||||||
transposed = []
|
|
||||||
for i, arg in enumerate(args):
|
|
||||||
if i == 0:
|
|
||||||
continue
|
|
||||||
if not isinstance(arg, list):
|
|
||||||
return # is not list
|
|
||||||
transposed.append([note.transpose(value) for note in arg if isinstance(note, Note)])
|
|
||||||
return transposed
|
|
||||||
else:
|
|
||||||
return # not valid signature
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
functions = {
|
|
||||||
'print': prt,
|
|
||||||
'midi': Midi.play,
|
|
||||||
'pause': Midi.pause,
|
|
||||||
'type': lambda args, env: print(type(args[0])),
|
|
||||||
'random': rand,
|
|
||||||
'semitones': semitones,
|
|
||||||
'interval': interval,
|
|
||||||
'transpose': transpose
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
with open(sys.argv[1], 'r') as source:
|
|
||||||
lines = [line.rstrip('\n') for line in source.readlines()]
|
|
||||||
|
|
||||||
tokens = [token for token in tokenize(lines) if token.type != TokenType.COMMENT]
|
|
||||||
|
|
||||||
ast = parse(tokens)
|
|
||||||
environment = Environment([{ "bpm": 120 }], functions)
|
|
||||||
evaluate(ast, environment)
|
|
||||||
|
|
||||||
44
Midi.py
44
Midi.py
@@ -1,44 +0,0 @@
|
|||||||
from Note import *
|
|
||||||
import Audio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
def parseNotes(notes):
|
|
||||||
map = { NotePitch.C: 'c', NotePitch.CIS: 'c#', NotePitch.D: 'd', NotePitch.DIS: 'd#',
|
|
||||||
NotePitch.E: 'e', NotePitch.F: 'f', NotePitch.FIS: 'f#', NotePitch.G: 'g',
|
|
||||||
NotePitch.GIS: 'g#', NotePitch.A: 'a', NotePitch.AIS: 'a#', NotePitch.H: 'b' }
|
|
||||||
parsed = [(f"{map[note.note]}{note.octave}", note.duration) for note in notes]
|
|
||||||
print(parsed)
|
|
||||||
|
|
||||||
def play(args, env):
|
|
||||||
if len(args) > 0 and isinstance(args[0], list):
|
|
||||||
playList(args[0], env)
|
|
||||||
return
|
|
||||||
playList(args, env)
|
|
||||||
|
|
||||||
def playList(notes, env):
|
|
||||||
bpm = findVariable("bpm", env)
|
|
||||||
if all(isinstance(x, Note) or isinstance(x, int) for x in notes):
|
|
||||||
for x in notes:
|
|
||||||
if isinstance(x, Note):
|
|
||||||
Audio.playNote(x, bpm)
|
|
||||||
if isinstance(x, int):
|
|
||||||
doPause(x, bpm)
|
|
||||||
#sys.stdout = open(os.devnull, 'w')
|
|
||||||
#sys.stderr = open(os.devnull, 'w')
|
|
||||||
#sys.stdout = sys.__stdout__
|
|
||||||
#sys.stderr = sys.__stderr__
|
|
||||||
|
|
||||||
def findVariable(name, environment):
|
|
||||||
for scope in reversed(environment.scopes):
|
|
||||||
if name in scope:
|
|
||||||
return scope[name]
|
|
||||||
|
|
||||||
def pause(args, env):
|
|
||||||
bpm = findVariable("bpm", env)
|
|
||||||
value = args[0]
|
|
||||||
doPause(value, bpm)
|
|
||||||
|
|
||||||
def doPause(value, bpm):
|
|
||||||
time.sleep(60 * 4 / value / bpm)
|
|
||||||
52
Note.py
52
Note.py
@@ -3,18 +3,18 @@ from Error import ParseError
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
class NotePitch(Enum):
|
class NotePitch(Enum):
|
||||||
C = 1
|
C = 0
|
||||||
CIS = 2
|
CIS = 1
|
||||||
D = 3
|
D = 2
|
||||||
DIS = 4
|
DIS = 3
|
||||||
E = 5
|
E = 4
|
||||||
F = 6
|
F = 5
|
||||||
FIS = 7
|
FIS = 6
|
||||||
G = 8
|
G = 7
|
||||||
GIS = 9
|
GIS = 8
|
||||||
A = 10
|
A = 9
|
||||||
AIS = 11
|
AIS = 10
|
||||||
H = 12
|
H = 11
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -53,13 +53,6 @@ class NotePitch(Enum):
|
|||||||
return map[string.lower()]
|
return map[string.lower()]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise ParseError(f"Note '{string}' does not exist")
|
raise ParseError(f"Note '{string}' does not exist")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def range(a, b):
|
|
||||||
aValue = a.value
|
|
||||||
bValue = b.value
|
|
||||||
|
|
||||||
return [note[1] for note in NotePitch.__members__.items() if note[1].value >= aValue and note[1].value <= bValue]
|
|
||||||
|
|
||||||
class Note:
|
class Note:
|
||||||
def __init__(self, note, octave = 4, duration = 4):
|
def __init__(self, note, octave = 4, duration = 4):
|
||||||
@@ -79,10 +72,7 @@ class Note:
|
|||||||
def transpose(self, interval):
|
def transpose(self, interval):
|
||||||
origIntRepr = self._intRepr()
|
origIntRepr = self._intRepr()
|
||||||
transposedIntRepr = origIntRepr + interval
|
transposedIntRepr = origIntRepr + interval
|
||||||
pitch = transposedIntRepr % len(NotePitch)
|
return Note._fromIntRepr(transposedIntRepr, self.duration)
|
||||||
note = NotePitch(pitch if pitch != 0 else self.note.value)
|
|
||||||
octave = int(transposedIntRepr / len(NotePitch))
|
|
||||||
return Note(note, octave, self.duration)
|
|
||||||
|
|
||||||
def _intRepr(self):
|
def _intRepr(self):
|
||||||
return self.octave * len(NotePitch) + self.note.value
|
return self.octave * len(NotePitch) + self.note.value
|
||||||
@@ -93,13 +83,27 @@ class Note:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
def getLowerNeighbour(self):
|
||||||
|
return Note._fromIntRepr(max((self._intRepr()-1), 0))
|
||||||
|
|
||||||
|
def getUpperNeighbour(self):
|
||||||
|
maxIntRepr = Note(NotePitch.H, 9)._intRepr() # max value for one-digit octave number
|
||||||
|
return Note._fromIntRepr(min((self._intRepr()+1), maxIntRepr))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _fromIntRepr(intRepr, duration=4):
|
||||||
|
note = NotePitch(intRepr % len(NotePitch))
|
||||||
|
octave = int(intRepr / len(NotePitch))
|
||||||
|
return Note(note, octave, duration)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def checkInterval(a, b):
|
def checkInterval(a, b):
|
||||||
return a._intRepr() - b._intRepr()
|
return a._intRepr() - b._intRepr()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def range(a, b):
|
def range(a, b):
|
||||||
return [Note(note, 1, 1) for note in NotePitch.range(a.note, b.note)]
|
return [Note._fromIntRepr(x, 4) for x in range(a._intRepr(), b._intRepr()+1)]
|
||||||
|
|
||||||
def intervalToString(interval):
|
def intervalToString(interval):
|
||||||
octaveInterval = int(abs(interval) / len(NotePitch))
|
octaveInterval = int(abs(interval) / len(NotePitch))
|
||||||
|
|||||||
42
Synth.py
Normal file
42
Synth.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import numpy as np
|
||||||
|
import sounddevice as sd
|
||||||
|
from Note import Note
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FS = 44100
|
||||||
|
|
||||||
|
|
||||||
|
def play(args, env):
|
||||||
|
if len(args) > 0 and isinstance(args[0], list):
|
||||||
|
playList(args[0], env)
|
||||||
|
return
|
||||||
|
playList(args, env)
|
||||||
|
|
||||||
|
def playList(notes, env):
|
||||||
|
bpm = env.findVariable("bpm", int)
|
||||||
|
if all(isinstance(x, Note) or isinstance(x, int) for x in notes):
|
||||||
|
for x in notes:
|
||||||
|
if isinstance(x, Note):
|
||||||
|
playNote(x, bpm)
|
||||||
|
if isinstance(x, int):
|
||||||
|
doPause(x, bpm)
|
||||||
|
|
||||||
|
def playNote(note, bpm):
|
||||||
|
frequency = note.toFrequency()
|
||||||
|
duration = 60 * 4 / note.duration / bpm
|
||||||
|
sine(frequency, duration)
|
||||||
|
|
||||||
|
def sine(frequency, duration):
|
||||||
|
samples = (np.sin(2*np.pi*np.arange(FS*duration)*frequency/FS)).astype(np.float32)
|
||||||
|
sd.play(samples, FS)
|
||||||
|
time.sleep(duration)
|
||||||
|
|
||||||
|
def doPause(value, bpm):
|
||||||
|
time.sleep(60 * 4 / value / bpm)
|
||||||
|
|
||||||
|
def pause(args, env):
|
||||||
|
bpm = findVariable("bpm", env)
|
||||||
|
value = args[0]
|
||||||
|
doPause(value, bpm)
|
||||||
17
Tokenizer.py
17
Tokenizer.py
@@ -166,7 +166,7 @@ tokenizers = (
|
|||||||
tokenizeWhitespaces
|
tokenizeWhitespaces
|
||||||
)
|
)
|
||||||
|
|
||||||
def tokenize(lines):
|
def doTokenize(lines):
|
||||||
tokens = []
|
tokens = []
|
||||||
for lineNumber, line in enumerate(lines):
|
for lineNumber, line in enumerate(lines):
|
||||||
current = 0
|
current = 0
|
||||||
@@ -185,15 +185,6 @@ def tokenize(lines):
|
|||||||
|
|
||||||
return [token for token in tokens if token.type is not None]
|
return [token for token in tokens if token.type is not None]
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def tokenize(lines):
|
||||||
try:
|
tokens = doTokenize(lines)
|
||||||
with open(sys.argv[1], 'r') as source:
|
return list(filter(lambda x: x.type != TokenType.COMMENT, tokens))
|
||||||
lines = [line.rstrip('\n') for line in source.readlines()]
|
|
||||||
|
|
||||||
tokens = tokenize(lines)
|
|
||||||
|
|
||||||
for token in tokens:
|
|
||||||
print(token)
|
|
||||||
except TokenizerError as e:
|
|
||||||
print(str(e))
|
|
||||||
|
|
||||||
|
|||||||
17
main.py
Normal file
17
main.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from Tokenizer import tokenize
|
||||||
|
from Parser import parse
|
||||||
|
from Evaluator import evaluate
|
||||||
|
from Environment import createEnvironment
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open(sys.argv[1], 'r') as source:
|
||||||
|
lines = [line.rstrip('\n') for line in source.readlines()]
|
||||||
|
|
||||||
|
env = createEnvironment()
|
||||||
|
|
||||||
|
tokens = tokenize(lines)
|
||||||
|
|
||||||
|
ast = parse(tokens)
|
||||||
|
|
||||||
|
evaluate(ast, env)
|
||||||
Reference in New Issue
Block a user