Skip to content

Commit

Permalink
Ver(1.3): Fixes for Windows, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
duangsuse committed Apr 26, 2020
1 parent c64f09d commit b9c6457
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 39 deletions.
52 changes: 29 additions & 23 deletions hachiko/funutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,13 @@
from ctypes.util import find_library

from sys import platform as sys_platform
from os import name as os_name

def findLibrary(name, lib_names) -> str:
try:
return next(filter(lambda it: it != None, map(find_library, lib_names))) or ""
except StopIteration:
raise ImportError(f"Couldn't find the {name} library")

def createLibrary(path, mode = 1):
lib = CDLL(path)
def cfunc(name, t_result, *args):
t_args = tuple(arg[1] for arg in args)
extras = tuple((mode, arg[0]) for arg in args)
return CFUNCTYPE(t_result, *t_args)((name, lib), extras)
return cfunc

def platform(opts = {"linux": "linux", "windows": "windows", "macos": "macos"}):
for (k, v) in opts.items():
if sys_platform.lower().startswith(k): return v
return os_name

from os.path import isfile
from itertools import chain

def require(value, p, message):
if not p(value): raise ValueError(f"{message}: {value}")

isNotNone = lambda it: it != None
isNonnegative = lambda it: it >= 0
def isInbounds(start, stop):
return lambda it: it in range(start, stop)
Expand All @@ -41,6 +23,30 @@ def flatMap(f, xs):
for ys in map(f, xs):
for y in ys: yield y

def findLibrary(name, lib_names, solver=lambda name: [f"./{name}.dll"]) -> str:
paths = filter(isNotNone, map(find_library, lib_names))
for dlls in map(solver, lib_names):
paths = chain(paths, filter(isfile, dlls))
try:
return str(next(paths)) #< appended dll scan
except StopIteration:
raise ImportError(f"couldn't find the {name} library")

def createLibrary(path, mode = 1):
lib = CDLL(path)
def cfunc(name, t_result, *args):
t_args = tuple(arg[1] for arg in args)
extras = tuple((mode, arg[0]) for arg in args)
return CFUNCTYPE(t_result, *t_args)((name, lib), extras)
return cfunc

def platform(opts = {"linux": ["linux"], "windows": ["win", "cygwin"], "macos": ["darwin"]}):
for (v, ks) in opts.items():
for k in ks:
if sys_platform.lower().startswith(k): return v
raise ValueError(f"unsupported platform: {sys_platform}")


global cdecls #v define a subset of C Header
if __name__ == "__main__":
import pyparsing as pas
Expand Down Expand Up @@ -101,11 +107,11 @@ def cdefs(decls):
cdefs(decls)
output.close()

def main(argv):
def main(argv = argv):
if len(argv) < 4:
print(f"Usage: {argv[0]} header name lib_names")
return
header, name = argv[1:3]
codegen(f"{name}.py", header, name, argv[3:])

if __name__ == '__main__': main(argv)
if __name__ == '__main__': main()
2 changes: 1 addition & 1 deletion hachiko/hachi.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def onEvent(event):
if exc.value == "proceed": break
return reducer.finish()

class CallFlagTimed(CallFlag):
class CallFlagTimed(CallFlag): #< time record (op -- op1)*
def __init__(self, op, op1):
super().__init__(op, op1)
self.t0 = time()
Expand Down
31 changes: 17 additions & 14 deletions hachiko/synthesize.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
try: import numpy
except ImportError: pass

drivers = ["alsa", "oss", "jack", "portaudio", "sndmgr", "coreaudio", "Direct Sound", "pulseaudio"]
drivers = ["alsa", "oss", "jack", "pulseaudio", "portaudio", "sndmgr", "coreaudio", "dsound", "waveout"]
platform_drivers = {"linux": "alsa", "windows": "dsound", "macos": "coreaudio"}

from .funutils import isNonnegative, isInbounds
from .FluidSynth import *

def fluid_synth_write_s16_stereo(synth, n: int, n_channel = 2): # -> numpy.ndarray
def fluid_synth_write_s16_stereo(synth, n: int, n_channel=2) -> List[int]:
"""Return generated samples in stereo 16-bit format"""
buf = create_string_buffer(n*n_channel*sizeof(c_int16))
fluid_synth_write_s16(synth, n, buf, 0, n_channel, buf, 1, n_channel)
Expand Down Expand Up @@ -44,29 +46,29 @@ def setting(self, key, value):
fluid_settings_setstr(sets, bk, value.encode())

def sfload(self, filename, update_midi_preset=0) -> int:
return fluid_synth_sfload(self.synth, filename.encode(), update_midi_preset)
return fluid_synth_sfload(self.synth, filename.encode(), update_midi_preset)
def sfunload(self, sfid, update_midi_preset=0):
return fluid_synth_sfunload(self.synth, sfid, update_midi_preset)
fluid_synth_sfunload(self.synth, sfid, update_midi_preset)
def program_select(self, chan, sfid, bank, preset):
return fluid_synth_program_select(self.synth, chan, sfid, bank, preset)
fluid_synth_program_select(self.synth, chan, sfid, bank, preset)

def noteon(self, chan, key, vel=127):
require(chan, isNonnegative, "bad channel")
require(key, isInbounds(0, 128), "bad key")
require(vel, isInbounds(0, 128), "bad velocity")
return fluid_synth_noteon(self.synth, chan, key, vel)
fluid_synth_noteon(self.synth, chan, key, vel)
def noteoff(self, chan, key):
require(chan, isNonnegative, "bad channel")
require(key, isInbounds(0, 128), "bad key")
return fluid_synth_noteoff(self.synth, chan, key)
fluid_synth_noteoff(self.synth, chan, key)

def start(self, driver=platform({"linux": "alsa", "windows": "sndmgr", "macos": "coreaudio"}), device=None):
def start(self, driver=platform_drivers[platform()], device=None):
"""driver could be any str in drivers"""
require(driver, drivers.__contains__, "unsupported driver")
self.setting(b"audio.driver", driver)
if device is not None: self.setting(f"audio.{driver}.device", device)
self.audio_driver = new_fluid_audio_driver(self.settings, self.synth)
def get_samples(self, n=1024):
def get_samples(self, n=1024) -> List[int]:
"""Generate audio samples
Returns ndarray containing n audio samples.
If synth is set to stereo output(default) the array will be size 2*n.
Expand All @@ -81,24 +83,25 @@ def __init__(self, sample_rate):
@staticmethod
def getFontPresets(path_sfont) -> List[Tuple[int, int, str]]:
with open(path_sfont, "rb") as fbuf:
sf = Sf2File(fbuf)
sf = Sf2File(fbuf) #< parse sf2
return [(p.bank, p.preset, p.name) for p in sf.build_presets() if len(p.bags) != 0]

def setFont(self, path_sfont, idx_preset = 0):
def setFont(self, path_sfont, idx_preset=0):
presets = NoteSynth.getFontPresets(path_sfont)
require(presets, hasIndex(idx_preset), "preset outbounds")
preset = presets[idx_preset]
(bank, patch, _) = preset
self.program_select(0, self.sfload(path_sfont), bank, patch)

def noteon(self, pitch): return super().noteon(0, pitch)
def noteoff(self, pitch = None):
return super().noteoff(0, pitch) if pitch != None else super().noteoff(0, self.last_pitch)
def noteoff(self, pitch=None):
if pitch != None: super().noteoff(0, pitch)
else: super().noteoff(0, self.last_pitch)
def noteSwitch(self, pitch):
if self.last_pitch != (-1):
self.noteoff()
self.noteon(pitch)
self.last_pitch = pitch

def sampleNote(self, n_sec):
def sampleNote(self, n_sec) -> List[int]:
return self.get_samples(self.sample_rate*n_sec)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def parse_requirements(requirements):
return list(filter(lambda s: s.strip() != "", items))

setup(
name="hachiko-bapu", version="0.1.2",
name="hachiko-bapu", version="0.1.3",
python_requires=">=3.5",
author="duangsuse", author_email="fedora-opensuse@outlook.com",
url="https://github.com/duangsuse-valid-projects/Hachiko",
Expand Down

0 comments on commit b9c6457

Please sign in to comment.