class SynthController: PORT = 57120 def __init__(self): self.target = liblo.Address(self.PORT) self._lock = threading.Lock() self._load_results = {} self._sc_listener = OscReceiver(proto=liblo.UDP) self._sc_listener.add_method("/loaded", "ii", self._handle_loaded) self._sc_listener.start() self._send("/info_subscribe", self._sc_listener.port) def load_sound(self, sound_id, filename): self._send("/load", sound_id, filename) num_frames_loaded = self._get_load_result(sound_id) if num_frames_loaded <= 0: raise Exception("error when loading sound %s: SC reports numFrames=%s" % ( filename, num_frames_loaded)) return num_frames_loaded def _get_load_result(self, sound_id): while True: if sound_id in self._load_results: result = self._load_results[sound_id] del self._load_results[sound_id] return result self._sc_listener.serve() time.sleep(0.01) def _handle_loaded(self,path, args, types, src, data): sound_id, result = args self._load_results[sound_id] = result def free_sound(self, sound_id): self._send("/free", sound_id) def play_segment(self, segment_id, sound_id, begin, end, period_duration, looped_duration, channel, pan): if looped_duration: self._send("/loop", segment_id, sound_id, begin, end, period_duration, looped_duration, channel, pan) else: self._send("/play", segment_id, sound_id, begin, end, period_duration, channel, pan) def pan(self, segment_id, pan): self._send("/pan", segment_id, pan) def stop_all(self): self._send("/stop_all") def sync_beep(self): self._send("/sync_beep") def subscribe_to_amp(self, port): self._send("/amp_subscribe", port) def subscribe_to_waveform(self, port): self._send("/waveform_subscribe", port) def _send(self, command, *args): with self._lock: liblo.send(self.target, command, *args)
class SynthController: DEFAULT_PORT = 57120 @staticmethod def kill_potential_engine_from_previous_process(): subprocess.call("killall --quiet sclang", shell=True) subprocess.call("killall --quiet scsynth", shell=True) def __init__(self, logger=None): if logger is None: logger = logging.getLogger("SynthController") self.logger = logger self._sc_process = None self._sc_listener = None self._listening_to_engine = False def launch_engine(self, mode): self.stop_engine() f = open("sc/engine.sc") engine = f.read() f.close() f = open("sc/%s.sc" % mode) engine += f.read() f.close() out = open("sc/_compiled.sc", "w") f = open("sc/boot.sc") for line in f: line = line.replace("//$ENGINE", engine) print >>out, line, f.close() out.close() self._sc_process = subprocess.Popen("sclang sc/_compiled.sc", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) self._listening_to_engine = True self.lang_port = None initialized = False while self.lang_port is None or not initialized: line = self._sc_process.stdout.readline().strip() print "SC: %s" % line m = re.search('langPort=(\d+)', line) if m: self.lang_port = int(m.group(1)) elif line == "Receiving notification messages from server localhost": initialized = True self._sc_output_thread = threading.Thread(name="SynthController._sc_output_thread", target=self._read_sc_output) self._sc_output_thread.daemon = True self._sc_output_thread.start() def _read_sc_output(self): while self._listening_to_engine: line = self._sc_process.stdout.readline() if line: print "SC: %s" % line, def connect(self, port): self._lock = threading.Lock() self.target = liblo.Address(port) def subscribe_to_info(self): self._load_results = {} if not self._sc_listener: self._sc_listener = OscReceiver(proto=liblo.TCP, name="SynthController") self._sc_listener.add_method("/loaded", "ii", self._handle_loaded) self._sc_listener.start() self._send("/info_subscribe", self._sc_listener.port) def stop_engine(self): if self._sc_listener: self._sc_listener.stop() if self._sc_process: self._sc_process.stdin.write("thisProcess.shutdown;\n") self._sc_process.stdin.write("0.exit;\n") if self._listening_to_engine: self._listening_to_engine = False self._send("/shutdown") self.logger.info("waiting for SC output thread to finish") self._sc_output_thread.join() self.logger.info("closing SC pipe") self._sc_process.stdin.close() self._sc_process.stdout.close() self.logger.info("waiting for SC process to exit") self._sc_process.wait() self.logger.info("SC process exited") self.target = None def load_sound(self, sound_id, filename): self._send("/load", sound_id, filename) num_frames_loaded = self._get_load_result(sound_id) return num_frames_loaded def _get_load_result(self, sound_id, timeout=10.0): t = time.time() while True: if sound_id in self._load_results: result = self._load_results[sound_id] del self._load_results[sound_id] return result elif (time.time() - t) > timeout: return None else: self._sc_listener.serve() time.sleep(0.01) def _handle_loaded(self,path, args, types, src, data): sound_id, result = args print "got /loaded %s" % args #TEMP self._load_results[sound_id] = result def free_sound(self, sound_id): self._send("/free", sound_id) def free_sounds(self): self._send("/free_all") def play_segment(self, segment_id, sound_id, begin, end, period_duration, looped_duration, channel, pan): if looped_duration: self._send("/loop", segment_id, sound_id, begin, end, period_duration, looped_duration, channel, pan) else: self._send("/play", segment_id, sound_id, begin, end, period_duration, channel, pan) def pan(self, segment_id, pan): self._send("/pan", segment_id, pan) def stop_all(self): self._send("/stop_all") def sync_beep(self): self._send("/sync_beep") def subscribe_to_amp(self, port): self._send("/amp_subscribe", port) def subscribe_to_waveform(self, port): self._send("/waveform_subscribe", port) def _send(self, command, *args): with self._lock: liblo.send(self.target, command, *args)
class SynthController: @staticmethod def kill_potential_engine_from_previous_process(): subprocess.call("killall --quiet sclang", shell=True) subprocess.call("killall --quiet scsynth", shell=True) def __init__(self, logger=None): if logger is None: logger = logging.getLogger("SynthController") self.logger = logger self._sc_process = None self._sc_listener = None self._listening_to_engine = False def launch_engine(self): self.stop_engine() self._sc_process = subprocess.Popen("sclang sc/engine.sc", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) self._listening_to_engine = True self.lang_port = None initialized = False while self.lang_port is None or not initialized: line = self._sc_process.stdout.readline().strip() self._log("SC: %s" % line) m = re.search('langPort=(\d+)', line) if m: self.lang_port = int(m.group(1)) elif line == "Receiving notification messages from server localhost": initialized = True self._sc_output_thread = threading.Thread(name="SynthController._sc_output_thread", target=self._read_sc_output) self._sc_output_thread.daemon = True self._sc_output_thread.start() self._await_synths_to_be_loaded() def _await_synths_to_be_loaded(self): time.sleep(1) def _read_sc_output(self): while self._listening_to_engine: line = self._sc_process.stdout.readline().strip("\r\n") if line: self._log("SC: %s" % line) def connect(self, port): self._lock = threading.Lock() self.target = liblo.Address(port) self._subscribe_to_info() def _subscribe_to_info(self): self._sounds_info = {} if not self._sc_listener: self._sc_listener = OscReceiver(proto=liblo.TCP, name="SynthController") self._sc_listener.add_method( "/loaded", "sii", self._handle_loaded) self._sc_listener.add_method( "/stopped_playing", "s", self._handle_stopped_playing) self._sc_listener.start() self._send("/info_subscribe", self._sc_listener.port) def stop_engine(self): if self._sc_listener: self._sc_listener.stop() if self._sc_process: self._sc_process.stdin.write("thisProcess.shutdown;\n") self._sc_process.stdin.write("0.exit;\n") if self._listening_to_engine: self._listening_to_engine = False self._send("/shutdown") self._log("waiting for SC output thread to finish") self._sc_output_thread.join() self._log("closing SC pipe") self._sc_process.stdin.close() self._sc_process.stdout.close() self._log("waiting for SC process to exit") self._sc_process.wait() self._log("SC process exited") self.target = None def load_sound(self, filename): self._send("/load", filename) num_frames_loaded = self._get_load_result(filename) return num_frames_loaded def _get_load_result(self, filename, timeout=10.0): t = time.time() while True: if filename in self._sounds_info: result = self._sounds_info[filename] return result elif (time.time() - t) > timeout: return None else: self._sc_listener.serve() time.sleep(0.01) def _handle_loaded(self, path, args, types, src, data): filename, num_frames, sample_rate = args print "got /loaded %s" % args duration = float(num_frames) / sample_rate print "duration=%s" % duration self._sounds_info[filename] = { "duration": duration, "is_playing": False } def _handle_stopped_playing(self, path, args, types, src, data): sound = args[0] self._sounds_info[sound]["is_playing"] = False print "stopped playing %s" % sound def play(self, sound, pan, fade, gain, rate, looped, send, send_gain, comp_threshold): if fade is None: if looped: fade = .1 else: fade = 0 self._send( "/play", sound, pan, fade, gain, rate, looped, send, send_gain, comp_threshold) self._sounds_info[sound]["is_playing"] = True def get_duration(self, sound): return self._sounds_info[sound]["duration"] def is_playing(self, sound): return self._sounds_info[sound]["is_playing"] def add_bus(self, name): self._send("/add_bus", name) def set_bus_params(self, name, mix, room, damp): self._send("/set_bus_params", name, mix, room, damp) def _send(self, command, *args): with self._lock: liblo.send(self.target, command, *args) def _log(self, string): print string self.logger.debug(string) def set_param(self, sound, param, value): self._send("/set_%s" % param, sound, value) def process(self): self._sc_listener.serve()