コード例 #1
0
ファイル: sequencer.py プロジェクト: alex-berman/cob
class Sequencer:
    def __init__(self, logger=None):
        if logger is None:
            logger = logging.getLogger("Sequencer")
        self.logger = logger
        self._params = {"tracks": {},
                        "buses": {},
                        "reference_colour": None,
                        "max_colour_distance": 1.0}
        self._sounds = {}
        self._tracks = collections.OrderedDict()
        self._buses = collections.OrderedDict()
        self._groups = []
        self._scheduler = Scheduler()
        self.control_panels = set()
        self._setup_colour_receiver()
        SynthController.kill_potential_engine_from_previous_process()
        self._synth = SynthController()
        self._synth.launch_engine()
        self._synth.connect(self._synth.lang_port)
        self._setup_websocket_server()

    def _setup_colour_receiver(self):
        self._current_colour = None
        self._colour_receiver = ColourReceiver()
        self._colour_receiver.received_colour = self.set_current_colour        
        self._colour_receiver.start()

    def set_current_colour(self, rgb):
        self._current_colour = rgb
        if self._params["reference_colour"] is not None:
            self._estimate_age()
            self._set_age_dependent_rates()
            for control_panel in self.control_panels:
                control_panel.send_params()

    def _estimate_age(self):
        distance_to_reference = self._colour_distance(
            self._current_colour, self._params["reference_colour"])
        age = distance_to_reference / self._params["max_colour_distance"]
        age = min(age, 1.0)
        self._estimated_age = age
        # self.log("estimated age: %.2f" % self._estimated_age)

    def _set_age_dependent_rates(self):
        for track in self._tracks.values():
            params = self._params["tracks"][track["name"]]
            if params["age_type"] is not None:
                params["rate"] = self._age_dependent_rate(params["age_type"])
                # self.log("rate %.1f for age_type=%s, track %s" % (
                #         params["rate"], params["age_type"], track["name"]))
                self._on_track_params_changed(track)

    def _age_dependent_rate(self, age_type):
        if age_type == "decay":
            return RATE_MIN + (RATE_MAX - RATE_MIN) * (1 - self._estimated_age)
        else:
            return RATE_MIN + (RATE_MAX - RATE_MIN) * self._estimated_age

    def _colour_distance(self, colour1, colour2):
        diffs = [colour1[n] - colour2[n]
                 for n in range(3)]
        return math.sqrt(sum([diff*diff for diff in diffs]))

    def calibrate_colour(self):
        self.log("calibrate_colour %s" % self._current_colour)
        if self._current_colour is not None:
            self._params["reference_colour"] = self._current_colour

    def get_tracks(self):
        return self._tracks

    def get_buses(self):
        return self._buses

    def get_params(self):
        return self._params

    def play(self, sound, looped=0):
        track_name = self._sounds[sound]["track_name"]
        track = self._tracks[track_name]
        params = self._params["tracks"][track_name]
        self._synth.play(
            sound,
            params["pan"],
            params["fade"],
            params["gain"] + params["gain_adjustment"],
            params["rate"],
            looped,
            params["send"],
            params["send_gain"] + params["gain_adjustment"],
            params["comp_threshold"])

    def schedule(self, action, delay):
        self._scheduler.schedule(action, delay)

    def is_playing(self, sound):
        return self._synth.is_playing(sound)

    def load_sounds(self, pattern):
        for sound in glob.glob(pattern):
            self.load_sound(sound)

    def load_sound(self, sound):
        self._synth.load_sound(sound)
        self._sounds[sound] = {}

    def add_track(self, name, pattern, params_overrides):
        params = copy.copy(DEFAULT_SOUND_PARAMS)
        params.update(params_overrides)
        sounds = glob.glob(pattern)
        track = {"name": name,
                 "sounds": sounds}
        self._params["tracks"][name] = params
        for sound in sounds:
            self._sounds[sound]["track_name"] = name
        self._tracks[name] = track

    def add_group(self, pattern, params):
        group = Group(self, params)
        for sound in glob.glob(pattern):
            group.add(sound)
        self._groups.append(group)

    def add_bus(self, name):
        self._synth.add_bus(name)
        self._buses[name] = {"name": name}
        self._params["buses"][name] = DEFAULT_BUS_PARAMS

    def set_bus_params(self, bus, new_params):
        params = self._params["buses"][bus]
        params.update(new_params)
        self._synth.set_bus_params(
            bus,
            params["reverb_mix"],
            params["reverb_room"],
            params["reverb_damp"])

    def try_to_load_params(self):
        if os.path.exists(PARAMS_FILENAME):
            self.load_params()

    def run_main_loop(self):
        while True:
            self._process()
            time.sleep(.1)

    def _process(self):
        self._synth.process()
        self._scheduler.run_scheduled_events()
        self._colour_receiver.serve()
        for group in self._groups:
            group.process()

    def _setup_websocket_server(self):
        self._server = WebsocketServer(ControlPanelHandler, {"sequencer": self})
        server_thread = threading.Thread(target=self._server.start)
        server_thread.daemon = True
        server_thread.start()

    def set_global_param(self, param, value):
        self._params[param] = value

    def set_track_param(self, track_name, param, value):
        track = self._tracks[track_name]
        params = self._params["tracks"][track_name]
        params[param] = value
        self._on_track_params_changed(track)

    def _on_tracks_params_changed(self):
        for track in self._tracks.values():
            self._on_track_params_changed(track)

    def _on_track_params_changed(self, track):
        params = self._params["tracks"][track["name"]]
        for sound in track["sounds"]:
            if self.is_playing(sound):
                self._synth.set_param(sound, "gain",
                                      params["gain"] + params["gain_adjustment"])
                self._synth.set_param(sound, "send_gain",
                                      params["send_gain"] + params["gain_adjustment"])
                self._synth.set_param(sound, "rate", params["rate"])

    def save_params(self):
        f = open(PARAMS_FILENAME, "w")
        cPickle.dump(self._params, f)
        f.close()

    def load_params(self):
        f = open(PARAMS_FILENAME, "r")
        self._params = cPickle.load(f)
        self._on_tracks_params_changed()
        f.close()
        
    def log(self, string):
        print string
        self.logger.debug(string)