parent
1893577b13
commit
9a04e8723d
@ -1,116 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import os.path
|
||||
import logging
|
||||
|
||||
from . import soundfont
|
||||
|
||||
# TODO:
|
||||
# - removing instruments from collection, hides it
|
||||
# - removing collection removes all instruments
|
||||
# - make UI
|
||||
# - UI actions use commands
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Collection(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class SoundFontCollection(Collection):
|
||||
def __init__(self, name, path):
|
||||
super().__init__(name)
|
||||
self.path = path
|
||||
|
||||
def create_instruments(self):
|
||||
sf = soundfont.SoundFont()
|
||||
sf.parse(self.path)
|
||||
for preset in sf.presets:
|
||||
yield SoundFontInstrument(
|
||||
preset.name, self, self.path, preset.bank, preset.preset)
|
||||
|
||||
|
||||
class Instrument(object):
|
||||
def __init__(self, name, collection, id):
|
||||
self.name = name
|
||||
self.collection = collection
|
||||
self.id = id
|
||||
|
||||
|
||||
class SoundFontInstrument(Instrument):
|
||||
def __init__(self, name, collection, path, bank, preset):
|
||||
super().__init__(
|
||||
name,
|
||||
collection,
|
||||
hashlib.md5(('%s:%d:%d' % (path, bank, preset)).encode('utf-8')).hexdigest())
|
||||
|
||||
self.path = path
|
||||
self.bank = bank
|
||||
self.preset = preset
|
||||
|
||||
def __str__(self):
|
||||
return '<SoundFontInstrument "%s" path="%s" bank=%d preset=%d>' % (
|
||||
self.name, self.path, self.bank, self.preset)
|
||||
|
||||
|
||||
class SampleInstrument(Instrument):
|
||||
def __init__(self, name, path):
|
||||
super().__init__(
|
||||
name,
|
||||
None,
|
||||
hashlib.md5(('%s' % path).encode('utf-8')).hexdigest())
|
||||
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
return '<SampleInstrument "%s" path="%s">' % (
|
||||
self.name, self.path)
|
||||
|
||||
|
||||
class InstrumentLibrary(object):
|
||||
def __init__(self, add_default_instruments=True):
|
||||
self.instruments = []
|
||||
self.collections = []
|
||||
self.default_instrument = None
|
||||
|
||||
if add_default_instruments:
|
||||
for p in sorted(glob.glob('/usr/share/sounds/sf2/*.sf2')):
|
||||
self.add_soundfont(p)
|
||||
|
||||
for d in sorted(glob.glob('/storage/home/share/samples/ST-0?')):
|
||||
if not os.path.isdir(d):
|
||||
continue
|
||||
|
||||
for p in sorted(glob.glob(os.path.join(d, '*.wav'))):
|
||||
self.add_sample(p)
|
||||
|
||||
if self.default_instrument is None:
|
||||
self.default_instrument = self.instruments[0]
|
||||
|
||||
logger.info("Default instrument: %s", self.default_instrument)
|
||||
|
||||
def add_instrument(self, instr):
|
||||
logger.info("Adding instrument %s to library...", instr)
|
||||
self.instruments.append(instr)
|
||||
|
||||
def add_soundfont(self, path):
|
||||
sf = soundfont.SoundFont()
|
||||
sf.parse(path)
|
||||
collection = SoundFontCollection(sf.bank_name, path)
|
||||
self.collections.append(collection)
|
||||
for instr in collection.create_instruments():
|
||||
self.add_instrument(instr)
|
||||
if instr.bank == 0 and instr.preset == 0 and self.default_instrument is None:
|
||||
self.default_instrument = instr
|
||||
|
||||
def add_sample(self, path):
|
||||
instr = SampleInstrument(
|
||||
os.path.splitext(os.path.basename(path))[0], path)
|
||||
self.add_instrument(instr)
|
@ -1,35 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
#from .library import InstrumentLibrary
|
||||
|
||||
|
||||
# class LibraryTest(unittest.TestCase):
|
||||
# def testAddSoundFont(self):
|
||||
# lib = InstrumentLibrary(add_default_instruments=False)
|
||||
# lib.add_soundfont('/usr/share/sounds/sf2/TimGM6mb.sf2')
|
||||
# state = json.loads(json.dumps(lib.serialize()))
|
||||
# lib2 = InstrumentLibrary(state=state)
|
||||
# lib2.init_references()
|
||||
|
||||
# def testAddSample(self):
|
||||
# path = '/storage/home/share/samples/ST-01/MonsterBass.wav'
|
||||
|
||||
# lib = InstrumentLibrary(add_default_instruments=False)
|
||||
# lib.add_sample(path)
|
||||
# self.assertEqual(len(lib.instruments), 1)
|
||||
# self.assertEqual(lib.instruments[0].name, 'MonsterBass')
|
||||
# self.assertEqual(lib.instruments[0].path, path)
|
||||
|
||||
# state = json.loads(json.dumps(lib.serialize()))
|
||||
# lib = InstrumentLibrary(state=state)
|
||||
# lib.init_references()
|
||||
# self.assertEqual(len(lib.instruments), 1)
|
||||
# self.assertEqual(lib.instruments[0].name, 'MonsterBass')
|
||||
# self.assertEqual(lib.instruments[0].path, path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -0,0 +1,9 @@
|
||||
from .client import InstrumentDBClientMixin
|
||||
from .instrument_description import (
|
||||
InstrumentDescription,
|
||||
parse_uri,
|
||||
)
|
||||
from .mutations import (
|
||||
AddInstrumentDescription,
|
||||
RemoveInstrumentDescription,
|
||||
)
|
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import logging
|
||||
|
||||
from noisicaa import core
|
||||
from noisicaa.core import ipc
|
||||
from . import mutations
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InstrumentDBClientMixin(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._stub = None
|
||||
self._session_id = None
|
||||
self._instruments = {}
|
||||
self.listeners = core.CallbackRegistry()
|
||||
|
||||
@property
|
||||
def instruments(self):
|
||||
return sorted(
|
||||
self._instruments.values(), key=lambda i: i.display_name.lower())
|
||||
|
||||
def get_instrument_description(self, uri):
|
||||
return self._instrument[uri]
|
||||
|
||||
async def setup(self):
|
||||
await super().setup()
|
||||
self.server.add_command_handler(
|
||||
'INSTRUMENTDB_MUTATION', self.handle_mutation)
|
||||
|
||||
async def connect(self, address, flags=None):
|
||||
assert self._stub is None
|
||||
self._stub = ipc.Stub(self.event_loop, address)
|
||||
await self._stub.connect()
|
||||
self._session_id = await self._stub.call(
|
||||
'START_SESSION', self.server.address, flags)
|
||||
|
||||
async def disconnect(self, shutdown=False):
|
||||
if self._session_id is not None:
|
||||
await self._stub.call('END_SESSION', self._session_id)
|
||||
self._session_id = None
|
||||
|
||||
if self._stub is not None:
|
||||
if shutdown:
|
||||
await self.shutdown()
|
||||
|
||||
await self._stub.close()
|
||||
self._stub = None
|
||||
|
||||
async def shutdown(self):
|
||||
await self._stub.call('SHUTDOWN')
|
||||
|
||||
async def start_scan(self):
|
||||
return await self._stub.call('START_SCAN', self._session_id)
|
||||
|
||||
def handle_mutation(self, mutation):
|
||||
logger.info("Mutation received: %s" % mutation)
|
||||
if isinstance(mutation, mutations.AddInstrumentDescription):
|
||||
assert mutation.description.uri not in self._instruments
|
||||
self._instruments[mutation.description.uri] = mutation.description
|
||||
else:
|
||||
raise ValueError(mutation)
|
||||
|
||||
self.listeners.call('mutation', mutation)
|
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import asynctest
|
||||
|
||||
from noisicaa import core
|
||||
from noisicaa.core import ipc
|
||||
|
||||
from . import process
|
||||
from . import client
|
||||
|
||||
|
||||
class TestClientImpl(object):
|
||||
def __init__(self, event_loop):
|
||||
super().__init__()
|
||||
self.event_loop = event_loop
|
||||
self.server = ipc.Server(self.event_loop, 'client')
|
||||
|
||||
async def setup(self):
|
||||
await self.server.setup()
|
||||
|
||||
async def cleanup(self):
|
||||
await self.server.cleanup()
|
||||
|
||||
|
||||
class TestClient(client.InstrumentDBClientMixin, TestClientImpl):
|
||||
pass
|
||||
|
||||
|
||||
class TestProcessImpl(object):
|
||||
def __init__(self, event_loop):
|
||||
super().__init__()
|
||||
self.event_loop = event_loop
|
||||
self.server = ipc.Server(self.event_loop, 'audioproc')
|
||||
|
||||
async def setup(self):
|
||||
await self.server.setup()
|
||||
|
||||
async def cleanup(self):
|
||||
await self.server.cleanup()
|
||||
|
||||
|
||||
class TestProcess(process.InstrumentDBProcessMixin, TestProcessImpl):
|
||||
pass
|
||||
|
||||
|
||||
class InstrumentDBClientTest(asynctest.TestCase):
|
||||
async def setUp(self):
|
||||
self.process = TestProcess(self.loop)
|
||||
await self.process.setup()
|
||||
self.process_task = self.loop.create_task(
|
||||
self.process.run())
|
||||
|
||||
self.client = TestClient(self.loop)
|
||||
await self.client.setup()
|
||||
await self.client.connect(self.process.server.address)
|
||||
|
||||
async def tearDown(self):
|
||||
await self.client.disconnect(shutdown=True)
|
||||
await self.client.cleanup()
|
||||
await asyncio.wait_for(self.process_task, None)
|
||||
await self.process.cleanup()
|
||||
|
||||
async def test_start_scan(self):
|
||||
await self.client.start_scan()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import enum
|
||||
import urllib.parse
|
||||
|
||||
class InstrumentDescription(object):
|
||||
def __init__(self, uri, display_name):
|
||||
self.uri = uri
|
||||
self.display_name = display_name
|
||||
|
||||
|
||||
def parse_uri(uri):
|
||||
fmt, _, path, _, args, _ = urllib.parse.urlparse(uri)
|
||||
path = urllib.parse.unquote(path)
|
||||
if args:
|
||||
args = dict(urllib.parse.parse_qsl(args, strict_parsing=True))
|
||||
else:
|
||||
args = {}
|
||||
|
||||
if fmt == 'sf2':
|
||||
return 'fluidsynth', {
|
||||
'soundfont_path': path,
|
||||
'bank': int(args['bank']),
|
||||
'preset': int(args['preset'])
|
||||
}
|
||||
|
||||
elif fmt == 'sample':
|
||||
return 'sample_player', {
|
||||
'sample_path': path,
|
||||
}
|
||||
|
||||
else:
|
||||
raise ValueError(fmt)
|
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
class Mutation(object):
|
||||
pass
|
||||
|
||||
|
||||
class AddInstrumentDescription(Mutation):
|
||||
def __init__(self, description):
|
||||
self.description = description
|
||||
|
||||
def __str__(self):
|
||||
return '<AddInstrumentDescription uri="%s">' % self.description.uri
|
||||
|
||||
|
||||
class RemoveInstrumentDescription(Mutation):
|
||||
def __init__(self, uri):
|
||||
self.uri = uri
|
||||
|
||||
def __str__(self):
|
||||
return '<RemoveInstrumentDescription uri="%s">' % self.uri
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import glob
|
||||
import os
|
||||
import os.path
|
||||
import logging
|
||||
|
||||
from noisicaa import core
|
||||
from noisicaa import instrument_db
|
||||
|
||||
from . import sample_scanner
|
||||
from . import soundfont_scanner
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InstrumentDB(object):
|
||||
def __init__(self):
|
||||
self._instruments = {}
|
||||
self.listeners = core.CallbackRegistry()
|
||||
|
||||
def setup(self):
|
||||
scanners = [
|
||||
sample_scanner.SampleScanner(),
|
||||
soundfont_scanner.SoundFontScanner(),
|
||||
]
|
||||
|
||||
search_paths = [
|
||||
'/usr/share/sounds/sf2/',
|
||||
] + sorted(glob.glob('/storage/home/share/samples/ST-0?'))
|
||||
|
||||
for root_path in search_paths:
|
||||
logger.info("Scanning instruments in %s...", root_path)
|
||||
for dname, dirs, files in os.walk(root_path):
|
||||
for fname in sorted(files):
|
||||
path = os.path.join(dname, fname)
|
||||
logger.info("Scanning file %s...", path)
|
||||
|
||||
for scanner in scanners:
|
||||
for description in scanner.scan(path):
|
||||
assert description.uri not in self._instruments
|
||||
self._instruments[description.uri] = description
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
def initial_mutations(self):
|
||||
for uri, description in sorted(self._instruments.items()):
|
||||
yield instrument_db.AddInstrumentDescription(description)
|
||||
|
||||
def start_scan(self):
|
||||
pass
|
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import unittest
|
||||
|
||||
from . import db
|
||||
|
||||
|
||||
class NodeDBTest(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from noisicaa import constants
|
||||
from noisicaa import instrument_db
|
||||
|
||||
from . import scanner
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SampleScanner(scanner.Scanner):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def scan(self, path):
|
||||
if not path.endswith('.wav'):
|
||||
return
|
||||
|
||||
uri = self.make_uri('sample', path)
|
||||
logger.info("Adding sample instrument %s...", uri)
|
||||
|
||||
description = instrument_db.InstrumentDescription(
|
||||
uri=uri,
|
||||
display_name=os.path.basename(path)[:-4])
|
||||
|
||||
yield description
|
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import urllib.parse
|
||||
|
||||
class Scanner(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def make_uri(self, fmt, path, **kwargs):
|
||||
return urllib.parse.urlunparse((
|
||||
fmt,
|
||||
None,
|
||||
urllib.parse.quote(path),
|
||||
None,
|
||||
urllib.parse.urlencode(sorted((k, str(v)) for k, v in kwargs.items()), True),
|
||||
None))
|
||||
|
||||
def scan(self):
|
||||
raise NotImplementedError
|
||||
|
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import urllib.parse
|
||||
|
||||
from noisicaa import constants
|
||||
from noisicaa import instrument_db
|
||||
from noisicaa.instr import soundfont
|
||||
|
||||
from . import scanner
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SoundFontScanner(scanner.Scanner):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def scan(self, path):
|
||||
if not path.endswith('.sf2'):
|
||||
return
|
||||
|
||||
sf = soundfont.SoundFont()
|
||||
sf.parse(path)
|
||||
for preset in sf.presets:
|
||||
uri = self.make_uri('sf2', path, bank=preset.bank, preset=preset.preset)
|
||||
logger.info("Adding soundfont instrument %s...", uri)
|
||||
|
||||
description = instrument_db.InstrumentDescription(
|
||||
uri=uri,
|
||||
display_name=preset.name)
|
||||
|
||||
yield description
|
@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from noisicaa import core
|
||||
from noisicaa.core import ipc
|
||||
|
||||
from .private import db
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InvalidSessionError(Exception): pass
|
||||
|
||||
|
||||
class Session(object):
|
||||
def __init__(self, event_loop, callback_stub, flags):
|
||||
self.event_loop = event_loop
|
||||
self.callback_stub = callback_stub
|
||||
self.flags = flags or set()
|
||||
self.id = uuid.uuid4().hex
|
||||
self.pending_mutations = []
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
def publish_mutation(self, mutation):
|
||||
if not self.callback_stub.connected:
|
||||
self.pending_mutations.append(mutation)
|
||||
return
|
||||
|
||||
callback_task = self.event_loop.create_task(
|
||||
self.callback_stub.call('INSTRUMENTDB_MUTATION', mutation))
|
||||
callback_task.add_done_callback(self.publish_mutation_done)
|
||||
|
||||
def publish_mutation_done(self, callback_task):
|
||||
assert callback_task.done()
|
||||
exc = callback_task.exception()
|
||||
if exc is not None:
|
||||
logger.error(
|
||||
"INSTRUMENTDB_MUTATION failed with exception: %s", exc)
|
||||
|
||||
def callback_stub_connected(self):
|
||||
assert self.callback_stub.connected
|
||||
while self.pending_mutations:
|
||||
self.publish_mutation(self.pending_mutations.pop(0))
|
||||
|
||||
|
||||
class InstrumentDBProcessMixin(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.sessions = {}
|
||||
self.db = db.InstrumentDB()
|
||||
|
||||
async def setup(self):
|
||||
await super().setup()
|
||||
|
||||
self._shutting_down = asyncio.Event()
|
||||
self._shutdown_complete = asyncio.Event()
|
||||
|
||||
self.server.add_command_handler(
|
||||
'START_SESSION', self.handle_start_session)
|
||||
self.server.add_command_handler(
|
||||
'END_SESSION', self.handle_end_session)
|
||||
self.server.add_command_handler('SHUTDOWN', self.handle_shutdown)
|
||||
self.server.add_command_handler(
|
||||
'START_SCAN', self.handle_start_scan)
|
||||
|
||||
self.db.setup()
|
||||
|
||||
async def cleanup(self):
|
||||
self.db.cleanup()
|
||||
await super().cleanup()
|
||||
|
||||
async def run(self):
|
||||
await self._shutting_down.wait()
|
||||
logger.info("Shutting down...")
|
||||
self._shutdown_complete.set()
|
||||
|
||||
def get_session(self, session_id):
|
||||
try:
|
||||
return self.sessions[session_id]
|
||||
except KeyError:
|
||||
raise InvalidSessionError
|
||||
|
||||
def publish_mutation(self, mutation):
|
||||
for session in self.sessions.values():
|
||||
session.publish_mutation(mutation)
|
||||
|
||||
def handle_start_session(self, client_address, flags):
|
||||
client_stub = ipc.Stub(self.event_loop, client_address)
|
||||
connect_task = self.event_loop.create_task(client_stub.connect())
|
||||
session = Session(self.event_loop, client_stub, flags)
|
||||
connect_task.add_done_callback(
|
||||
functools.partial(self._client_connected, session))
|
||||
self.sessions[session.id] = session
|
||||
|
||||
# Send initial mutations to build up the current pipeline
|
||||
# state.
|
||||
for mutation in self.db.initial_mutations():
|
||||
session.publish_mutation(mutation)
|
||||
|
||||
return session.id
|
||||
|
||||
def _client_connected(self, session, connect_task):
|
||||
assert connect_task.done()
|
||||
exc = connect_task.exception()
|
||||
if exc is not None:
|
||||
logger.error("Failed to connect to callback client: %s", exc)
|
||||
return
|
||||
|
||||
session.callback_stub_connected()
|
||||
|
||||
def handle_end_session(self, session_id):
|
||||
session = self.get_session(session_id)
|
||||
session.cleanup()
|
||||
del self.sessions[session_id]
|
||||
|
||||
async def handle_shutdown(self):
|
||||
logger.info("Shutdown received.")
|
||||
self._shutting_down.set()
|
||||
logger.info("Waiting for shutdown to complete...")
|
||||
await self._shutdown_complete.wait()
|
||||
logger.info("Shutdown complete.")
|
||||
|
||||
async def handle_start_scan(self, session_id):
|
||||
self.get_session(session_id)
|
||||
return self.db.start_scan()
|
||||
|
||||
|
||||
class InstrumentDBProcess(InstrumentDBProcessMixin, core.ProcessImpl):
|
||||
pass
|
@ -1,88 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from . import model
|
||||
from . import state
|
||||
from . import mutations
|
||||
|
||||
|
||||
class Instrument(model.Instrument, state.StateBase):
|
||||
def __init__(self, name=None, library_id=None, state=None):
|
||||
super().__init__(state)
|
||||
if state is None:
|
||||
self.name = name
|
||||
self.library_id = library_id
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def track(self):
|
||||
return self.parent
|
||||
|
||||
@property
|
||||
def sheet(self):
|
||||
return self.track.sheet
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
return self.track.project
|
||||
|
||||
@property
|
||||
def pipeline_node_id(self):
|
||||
return '%s-instr' % self.id
|
||||
|
||||
state.StateBase.register_class(Instrument)
|
||||
|
||||
|
||||
class SoundFontInstrument(model.SoundFontInstrument, Instrument):
|
||||
def __init__(self, path=None, bank=None, preset=None, state=None, **kwargs):
|
||||
super().__init__(state=state, **kwargs)
|
||||
if state is None:
|
||||
self.path = path
|
||||
self.bank = bank
|
||||
self.preset = preset
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s, bank %d, preset %d)' % (
|
||||
self.name, self.path, self.bank, self.preset)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, SoundFontInstrument):
|
||||
return False
|
||||
return (self.path, self.bank, self.preset) == (other.path, other.bank, other.preset)
|
||||
|
||||
def add_to_pipeline(self):
|
||||
self.sheet.handle_pipeline_mutation(
|
||||
mutations.AddNode(
|
||||
'fluidsynth', self.pipeline_node_id, self.name,
|
||||
soundfont_path=self.path,
|
||||
bank=self.bank,
|
||||
preset=self.preset))
|
||||
|
||||
def remove_from_pipeline(self):
|
||||
self.sheet.handle_pipeline_mutation(
|
||||
mutations.RemoveNode(self.pipeline_node_id))
|
||||
|
||||
state.StateBase.register_class(SoundFontInstrument)
|
||||
|
||||
|
||||
class SampleInstrument(model.SampleInstrument, Instrument):
|
||||
def __init__(self, path=None, state=None, **kwargs):
|
||||
super().__init__(state=state, **kwargs)
|
||||
if state is None:
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (self.name, self.path)
|
||||
|
||||
def add_to_pipeline(self):
|
||||
self.sheet.handle_pipeline_mutation(
|
||||
mutations.AddNode(
|
||||
'sample_player', self.pipeline_node_id, self.name,
|
||||
sample_path=self.path))
|
||||
|
||||
def remove_from_pipeline(self):
|
||||
self.sheet.handle_pipeline_mutation(
|
||||
mutations.RemoveNode(self.pipeline_node_id))
|
||||
|
||||
state.StateBase.register_class(SampleInstrument)
|