Compare commits
1 Commits
add-readme
...
add-cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24bcb25bee |
@@ -1,11 +1,9 @@
|
|||||||
import sys
|
from smnp.cli.parser import CliParser
|
||||||
import time
|
|
||||||
|
|
||||||
from smnp.error.base import SmnpException
|
from smnp.error.base import SmnpException
|
||||||
from smnp.library.loader import loadStandardLibrary
|
from smnp.library.loader import loadStandardLibrary
|
||||||
from smnp.program.interpreter import Interpreter
|
|
||||||
from smnp.cli.parser import CliParser
|
|
||||||
from smnp.module.mic.lib.detector.noise import NoiseDetector
|
from smnp.module.mic.lib.detector.noise import NoiseDetector
|
||||||
|
from smnp.program.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
def interpretFile(args, file):
|
def interpretFile(args, file):
|
||||||
stdLibraryEnv = loadStandardLibrary() if not args.dry_run else None
|
stdLibraryEnv = loadStandardLibrary() if not args.dry_run else None
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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
|
DEFAULT_DECAY = 4
|
||||||
DEFAULT_ATTACK = 100
|
DEFAULT_ATTACK = 100
|
||||||
|
DEFAULT_TUNING = 440
|
||||||
|
|
||||||
|
|
||||||
def getBpm(config):
|
def getBpm(config):
|
||||||
@@ -72,16 +73,29 @@ def getAttack(config):
|
|||||||
return DEFAULT_ATTACK
|
return DEFAULT_ATTACK
|
||||||
|
|
||||||
|
|
||||||
|
def getTuning(config):
|
||||||
|
key = Type.string("tuning")
|
||||||
|
if key in config.value:
|
||||||
|
tuning = config.value[key]
|
||||||
|
if not tuning.type in [Type.INTEGER, Type.FLOAT] or tuning.value < 0:
|
||||||
|
raise RuntimeException("The 'tuning' property must be non-negative integer or float", None)
|
||||||
|
|
||||||
|
return tuning.value
|
||||||
|
|
||||||
|
return DEFAULT_TUNING
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, bpm, overtones, decay, attack):
|
def __init__(self, bpm, overtones, decay, attack, tuning):
|
||||||
self.bpm = bpm
|
self.bpm = bpm
|
||||||
self.overtones = overtones
|
self.overtones = overtones
|
||||||
self.decay = decay
|
self.decay = decay
|
||||||
self.attack = attack
|
self.attack = attack
|
||||||
|
self.tuning = tuning
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default():
|
def default():
|
||||||
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK)
|
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK, DEFAULT_TUNING)
|
||||||
|
|
||||||
|
|
||||||
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||||
@@ -114,8 +128,9 @@ def __function3(config, notes):
|
|||||||
overtones = getOvertones(config)
|
overtones = getOvertones(config)
|
||||||
decay = getDecay(config)
|
decay = getDecay(config)
|
||||||
attack = getAttack(config)
|
attack = getAttack(config)
|
||||||
|
tuning = getTuning(config)
|
||||||
|
|
||||||
return compilePolyphony(rawNotes, Config(bpm, overtones, decay, attack))
|
return compilePolyphony(rawNotes, Config(bpm, overtones, decay, attack, tuning))
|
||||||
|
|
||||||
|
|
||||||
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
|
||||||
@@ -128,8 +143,9 @@ def __function4(config, notes):
|
|||||||
overtones = getOvertones(config)
|
overtones = getOvertones(config)
|
||||||
decay = getDecay(config)
|
decay = getDecay(config)
|
||||||
attack = getAttack(config)
|
attack = getAttack(config)
|
||||||
|
tuning = getTuning(config)
|
||||||
|
|
||||||
return compilePolyphony([ notes ], Config(bpm, overtones, decay, attack))
|
return compilePolyphony([ notes ], Config(bpm, overtones, decay, attack, tuning))
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
function = CombinedFunction(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import matplotlib.pyplot as plt
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import sounddevice as sd
|
import sounddevice as sd
|
||||||
|
|
||||||
|
from smnp.note.pitch import Tuning
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
|
|
||||||
FS = 44100
|
FS = 44100
|
||||||
@@ -25,7 +26,8 @@ def play(wave):
|
|||||||
|
|
||||||
|
|
||||||
def compilePolyphony(notes, config):
|
def compilePolyphony(notes, config):
|
||||||
compiledLines = [1 / len(notes) * compileNotes(line, config) for line in notes]
|
tuning = Tuning(config.tuning)
|
||||||
|
compiledLines = [1 / len(notes) * compileNotes(line, config, tuning) for line in notes]
|
||||||
return sum(adjustSize(compiledLines))
|
return sum(adjustSize(compiledLines))
|
||||||
|
|
||||||
|
|
||||||
@@ -35,17 +37,17 @@ 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, config):
|
def compileNotes(notes, config, tuning):
|
||||||
dispatcher = {
|
dispatcher = {
|
||||||
Type.NOTE: lambda note, overtones: sineForNote(note.value, config),
|
Type.NOTE: lambda note, overtones: sineForNote(note.value, config, tuning),
|
||||||
Type.INTEGER: lambda note, overtones: silenceForPause(note.value, config)
|
Type.INTEGER: lambda note, overtones: silenceForPause(note.value, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
return np.concatenate([dispatcher[note.type](note, config) for note in notes])
|
return np.concatenate([dispatcher[note.type](note, config) for note in notes])
|
||||||
|
|
||||||
|
|
||||||
def sineForNote(note, config):
|
def sineForNote(note, config, tuning):
|
||||||
frequency = note.toFrequency()
|
frequency = note.toFrequency(tuning)
|
||||||
duration = 60 * 4 / note.duration / config.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, config)
|
return sound(frequency, duration, config)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ class Note:
|
|||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.dot = dot
|
self.dot = dot
|
||||||
|
|
||||||
def toFrequency(self):
|
def toFrequency(self, tuning):
|
||||||
return self.note.toFrequency() * 2 ** self.octave
|
return tuning[self.note] * 2 ** self.octave
|
||||||
|
|
||||||
def transpose(self, interval):
|
def transpose(self, interval):
|
||||||
origIntRepr = self._intRepr()
|
origIntRepr = self._intRepr()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from enum import Enum
|
|||||||
|
|
||||||
from smnp.error.note import NoteException
|
from smnp.error.note import NoteException
|
||||||
|
|
||||||
|
_semitone = 2**(1/12)
|
||||||
|
|
||||||
class NotePitch(Enum):
|
class NotePitch(Enum):
|
||||||
C = 0
|
C = 0
|
||||||
@@ -17,21 +18,21 @@ class NotePitch(Enum):
|
|||||||
AIS = 10
|
AIS = 10
|
||||||
H = 11
|
H = 11
|
||||||
|
|
||||||
def toFrequency(self):
|
# def toFrequency(self):
|
||||||
return {
|
# return {
|
||||||
NotePitch.C: 16.35,
|
# NotePitch.C: 16.35,
|
||||||
NotePitch.CIS: 17.32,
|
# NotePitch.CIS: 17.32,
|
||||||
NotePitch.D: 18.35,
|
# NotePitch.D: 18.35,
|
||||||
NotePitch.DIS: 19.45,
|
# NotePitch.DIS: 19.45,
|
||||||
NotePitch.E: 20.60,
|
# NotePitch.E: 20.60,
|
||||||
NotePitch.F: 21.83,
|
# NotePitch.F: 21.83,
|
||||||
NotePitch.FIS: 23.12,
|
# NotePitch.FIS: 23.12,
|
||||||
NotePitch.G: 24.50,
|
# NotePitch.G: 24.50,
|
||||||
NotePitch.GIS: 25.96,
|
# NotePitch.GIS: 25.96,
|
||||||
NotePitch.A: 27.50,
|
# NotePitch.A: 27.50,
|
||||||
NotePitch.AIS: 29.17,
|
# NotePitch.AIS: 29.17,
|
||||||
NotePitch.H: 30.87
|
# NotePitch.H: 30.87
|
||||||
}[self]
|
# }[self]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -50,6 +51,14 @@ class NotePitch(Enum):
|
|||||||
raise NoteException(f"Note '{string}' does not exist")
|
raise NoteException(f"Note '{string}' does not exist")
|
||||||
|
|
||||||
|
|
||||||
|
class Tuning(object):
|
||||||
|
def __init__(self, a4=440):
|
||||||
|
self.tuning = { value: a4/_semitone**(57-i) for i, value in enumerate(NotePitch.__members__) }
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.tuning[str(item)]
|
||||||
|
|
||||||
|
|
||||||
stringToPitch = {
|
stringToPitch = {
|
||||||
'cb': NotePitch.H,
|
'cb': NotePitch.H,
|
||||||
'c': NotePitch.C,
|
'c': NotePitch.C,
|
||||||
|
|||||||
Reference in New Issue
Block a user