Merge branch 'add-possibility-to-tune'
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import sys
|
||||
import time
|
||||
|
||||
from smnp.cli.parser import CliParser
|
||||
from smnp.error.base import SmnpException
|
||||
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.program.interpreter import Interpreter
|
||||
|
||||
|
||||
def interpretFile(args, file):
|
||||
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_DECAY = 4
|
||||
DEFAULT_ATTACK = 100
|
||||
DEFAULT_TUNING = 440
|
||||
|
||||
|
||||
def getBpm(config):
|
||||
@@ -72,16 +73,29 @@ def getAttack(config):
|
||||
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:
|
||||
def __init__(self, bpm, overtones, decay, attack):
|
||||
def __init__(self, bpm, overtones, decay, attack, tuning):
|
||||
self.bpm = bpm
|
||||
self.overtones = overtones
|
||||
self.decay = decay
|
||||
self.attack = attack
|
||||
self.tuning = tuning
|
||||
|
||||
@staticmethod
|
||||
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))
|
||||
@@ -114,8 +128,9 @@ def __function3(config, notes):
|
||||
overtones = getOvertones(config)
|
||||
decay = getDecay(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))
|
||||
@@ -128,8 +143,9 @@ def __function4(config, notes):
|
||||
overtones = getOvertones(config)
|
||||
decay = getDecay(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(
|
||||
|
||||
@@ -4,6 +4,7 @@ import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
|
||||
from smnp.note.pitch import Tuning
|
||||
from smnp.type.model import Type
|
||||
|
||||
FS = 44100
|
||||
@@ -25,7 +26,8 @@ def play(wave):
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
||||
@@ -35,17 +37,17 @@ def adjustSize(compiledLines):
|
||||
return [np.concatenate([line, np.zeros(maxSize - len(line))]) for line in compiledLines]
|
||||
|
||||
|
||||
def compileNotes(notes, config):
|
||||
def compileNotes(notes, config, tuning):
|
||||
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)
|
||||
}
|
||||
|
||||
return np.concatenate([dispatcher[note.type](note, config) for note in notes])
|
||||
|
||||
|
||||
def sineForNote(note, config):
|
||||
frequency = note.toFrequency()
|
||||
def sineForNote(note, config, tuning):
|
||||
frequency = note.toFrequency(tuning)
|
||||
duration = 60 * 4 / note.duration / config.bpm
|
||||
duration *= 1.5 if note.dot else 1
|
||||
return sound(frequency, duration, config)
|
||||
|
||||
@@ -16,8 +16,8 @@ class Note:
|
||||
self.duration = duration
|
||||
self.dot = dot
|
||||
|
||||
def toFrequency(self):
|
||||
return self.note.toFrequency() * 2 ** self.octave
|
||||
def toFrequency(self, tuning):
|
||||
return tuning[self.note] * 2 ** self.octave
|
||||
|
||||
def transpose(self, interval):
|
||||
origIntRepr = self._intRepr()
|
||||
|
||||
@@ -2,6 +2,7 @@ from enum import Enum
|
||||
|
||||
from smnp.error.note import NoteException
|
||||
|
||||
_semitone = 2**(1/12)
|
||||
|
||||
class NotePitch(Enum):
|
||||
C = 0
|
||||
@@ -17,21 +18,21 @@ class NotePitch(Enum):
|
||||
AIS = 10
|
||||
H = 11
|
||||
|
||||
def toFrequency(self):
|
||||
return {
|
||||
NotePitch.C: 16.35,
|
||||
NotePitch.CIS: 17.32,
|
||||
NotePitch.D: 18.35,
|
||||
NotePitch.DIS: 19.45,
|
||||
NotePitch.E: 20.60,
|
||||
NotePitch.F: 21.83,
|
||||
NotePitch.FIS: 23.12,
|
||||
NotePitch.G: 24.50,
|
||||
NotePitch.GIS: 25.96,
|
||||
NotePitch.A: 27.50,
|
||||
NotePitch.AIS: 29.17,
|
||||
NotePitch.H: 30.87
|
||||
}[self]
|
||||
# def toFrequency(self):
|
||||
# return {
|
||||
# NotePitch.C: 16.35,
|
||||
# NotePitch.CIS: 17.32,
|
||||
# NotePitch.D: 18.35,
|
||||
# NotePitch.DIS: 19.45,
|
||||
# NotePitch.E: 20.60,
|
||||
# NotePitch.F: 21.83,
|
||||
# NotePitch.FIS: 23.12,
|
||||
# NotePitch.G: 24.50,
|
||||
# NotePitch.GIS: 25.96,
|
||||
# NotePitch.A: 27.50,
|
||||
# NotePitch.AIS: 29.17,
|
||||
# NotePitch.H: 30.87
|
||||
# }[self]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -50,6 +51,14 @@ class NotePitch(Enum):
|
||||
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 = {
|
||||
'cb': NotePitch.H,
|
||||
'c': NotePitch.C,
|
||||
|
||||
Reference in New Issue
Block a user