def __init__(self, gateway, settings, behaviour, scale="DIATONIC"): self.settings = settings self.behaviour = behaviour self.gateway = gateway self.registers = registers self.offered_scales = SCALES_BY_FREQUENCY self.offered_meters = METERS self.scale = scale self.num_voices = settings['number_of_voices'] self.comp_logger = logging.getLogger("composer") self.note_logger = logging.getLogger("transcriber") self.musical_logger = logging.getLogger("musical") # change INFO to DEBUG for debugging output self.musical_logger.setLevel(logging.INFO) self.meter = self.behaviour['meter'] self.applied_meter = METERS[self.meter]['applied'] self._update_groupings(self.meter) self.use_meter = True self.drummer = Drummer(self) self.comment = None self.voices = {} for voice_idx in range(1, 5): self.gateway.mute_voice(voice_idx, 1) for voice_idx in range(self.num_voices): id_ = voice_idx + 1 self.gateway.mute_voice(id_, 0) self.voices[id_] = Voice( id_, self, note_length_grouping=self.behaviour["meter"][1], register=self.settings["voice_registers"][voice_idx], behaviour=self.settings['voice_behaviours'][voice_idx]) self.gateway.pd.send(["voice", id_, "adsr_enable", int(bool(settings['enable_adsr']))]) [voice.register_other_voices() for voice in list(self.voices.values())] self.set_meter(self.meter) self.notate = settings.get('notate') if self.notate: self.notator = Notator(self.num_voices)
class AbstractComposer(object, metaclass=ABCMeta): def __init__(self, gateway, settings, behaviour, scale="DIATONIC"): self.settings = settings self.behaviour = behaviour self.gateway = gateway self.registers = registers self.offered_scales = SCALES_BY_FREQUENCY self.offered_meters = METERS self.scale = scale self.num_voices = settings['number_of_voices'] self.comp_logger = logging.getLogger("composer") self.note_logger = logging.getLogger("transcriber") self.musical_logger = logging.getLogger("musical") # change INFO to DEBUG for debugging output self.musical_logger.setLevel(logging.INFO) self.meter = self.behaviour['meter'] self.applied_meter = METERS[self.meter]['applied'] self._update_groupings(self.meter) self.use_meter = True self.drummer = Drummer(self) self.comment = None self.voices = {} for voice_idx in range(1, 5): self.gateway.mute_voice(voice_idx, 1) for voice_idx in range(self.num_voices): id_ = voice_idx + 1 self.gateway.mute_voice(id_, 0) self.voices[id_] = Voice( id_, self, note_length_grouping=self.behaviour["meter"][1], register=self.settings["voice_registers"][voice_idx], behaviour=self.settings['voice_behaviours'][voice_idx]) self.gateway.pd.send(["voice", id_, "adsr_enable", int(bool(settings['enable_adsr']))]) [voice.register_other_voices() for voice in list(self.voices.values())] self.set_meter(self.meter) self.notate = settings.get('notate') if self.notate: self.notator = Notator(self.num_voices) def __repr__(self): return "<Composer-Inst ({}) with harmony: {}>".format(self.__class__, self.harm) def _update_groupings(self, meter): self.TERNARY_GROUPINGS = note_length_groupings.get_grouping(meter, "terns") self.HEAVY_GROUPINGS = note_length_groupings.get_grouping(meter, "heavy") self.DEFAULT_GROUPINGS = note_length_groupings.get_grouping(meter, "default") self.FAST_GROUPINGS = note_length_groupings.get_grouping(meter, "first") def report(self): '''utility function that prints info on harmonies and single voices''' sys.stderr.write("harmonies: {0}\n".format(self.harm)) sys.stderr.write("voices: {0}\nnotes:{1}\n".format(self.voices, [x.note for x in list(self.voices.values())])) def set_meter(self, meter): '''modifies composer-attributes for the specified meter. calls reload_register method of the voices and creates and sets the new meter also for the drummer-instance''' self.meter = meter try: self.applied_meter = self.offered_meters[meter]["applied"] except KeyError: self.comp_logger.error("no applied meter registered for: {}".format(meter)) self.applied_meter = note_length_groupings.analyze_grouping(meter[1]) self._update_groupings(meter) for v in list(self.voices.values()): v.set_note_length_groupings() v.reload_register() self.drummer.meter = self.applied_meter self.drummer.create_pattern() @abstractmethod def generate(self): pass @abstractmethod def choose_rhythm(self): pass def set_binaural_diffs(self, val=None, voice=None): '''"de-tunes" the specified voice by the specified interval (in hertz) if no values are given, random values (in the configurated range) are set for each voice. ''' if val and val != 'random': if voice: voice.binaural_diff = val self.gateway.pd.send(["voice", "binaural", str(voice.id), val]) else: self.gateway.pd.send(["voice", "binaural", -1, val]) for voice in list(self.voices.values()): voice.binaural_diff = val else: if self.behaviour['common_binaural_diff']: val = random.random() * self.behaviour.get("max_binaural_diff") for voice in list(self.voices.values()): if not self.behaviour.voice_get(voice.id, "automate_binaural_diffs"): continue new_val = val or random.random() * self.behaviour.voice_get(voice.id, "max_binaural_diff") voice.binaural_diff = new_val self.gateway.pd.send(["voice", "binaural", voice.id, new_val]) def set_scale(self, name, min=0, max=128): '''sets the specified scale and generates a new real scale''' self.scale = name self.generate_real_scale(min, max) @staticmethod def assemble_real_scale(scale, min=0, max=128, tunings=None): '''extends the one-octave scale over the specified range. Tunings should be in (<index>, <delta>) format, e.g. {1: -0.5 2: -1} would create the the start of a greek enharmonic scale for an underlying scale of [1, 1, 1,.....''' real_scale = [] value = 0 for n in range(min, max): index = n % len(scale) if scale[index]: adjustment = 0 if not tunings else tunings.get(index, 0) real_scale.append(value + adjustment) value += 1 return real_scale def generate_real_scale(self, min=0, max=128): '''extends the one-octave scale over the specified range''' scale = SCALES[self.scale] tunings = None if self.scale == 'GREEK_ENHARMONIC': tunings = {1: -0.5, 2: -1, 8:-0.5, 9:-1} elif self.scale == 'DAMONIAN_MIXOLYDIAN': tunings = {1: -0.5, 2: -1, 6:-0.5, 7:-1} # (West - ancient greek music, p. 174) elif self.scale == 'PTOLEMY_TROPOI': tunings = {3: -0.3, 8: -0.2, 9: 0.3} # (West - ancient greek music, p. 171) elif self.scale == 'PERSIAN_SEGAH': tunings = {2: -0.5, 8: 0.25, 10: 0.13} elif self.scale == 'PERSIAN_SHUR': tunings = {3: 0.5, 8: -0.2, 10: -0.2} self.real_scale = self.assemble_real_scale(scale, min, max, tunings=tunings)
class AbstractComposer(object): __metaclass__ = ABCMeta def __init__(self, gateway, settings, behaviour, scale="DIATONIC"): self.settings = settings self.behaviour = behaviour self.gateway = gateway self.registers = registers self.offered_scales = SCALES_BY_FREQUENCY self.offered_meters = METERS self.scale = scale self.num_voices = settings['number_of_voices'] self.comp_logger = logging.getLogger("composer") self.note_logger = logging.getLogger("transcriber") self.musical_logger = logging.getLogger("musical") # change INFO to DEBUG for debugging output self.musical_logger.setLevel(logging.INFO) self.meter = self.behaviour['meter'] self.applied_meter = METERS[self.meter]['applied'] self._update_groupings(self.meter) self.drummer = Drummer(self) self.comment = None self.voices = {} for voice_idx in range(self.num_voices): id_ = voice_idx + 1 self.voices[id_] = Voice( id_, self, note_length_grouping=self.behaviour["meter"][1], register=self.settings["voice_registers"][voice_idx], behaviour=self.settings['voice_behaviours'][voice_idx]) [voice.register_other_voices() for voice in self.voices.values()] self.set_meter(self.meter) self.notate = settings.get('notate') if self.notate: self.notator = Notator(self.num_voices) def __repr__(self): return "<Composer-Inst ({}) with harmony: {}>".format(self.__class__, self.harm) def _update_groupings(self, meter): self.TERNARY_GROUPINGS = note_length_groupings.get_grouping(meter, "terns") self.HEAVY_GROUPINGS = note_length_groupings.get_grouping(meter, "heavy") self.DEFAULT_GROUPINGS = note_length_groupings.get_grouping(meter, "default") self.FAST_GROUPINGS = note_length_groupings.get_grouping(meter, "first") def report(self): '''utility function that prints info on harmonies and single voices''' sys.stderr.write("harmonies: {0}\n".format(self.harm)) sys.stderr.write("voices: {0}\nnotes:{1}\n".format(self.voices, map(lambda x: x.note, self.voices.values()))) def set_meter(self, meter): '''modifies composer-attributes for the specified meter. calls reload_register method of the voices and creates and sets the new meter also for the drummer-instance''' self.meter = meter self.applied_meter = self.offered_meters[meter]["applied"] self._update_groupings(meter) for v in self.voices.values(): v.set_note_length_groupings() v.reload_register() self.drummer.meter = METERS[meter]["applied"] self.drummer.create_pattern() @abstractmethod def generate(self): pass @abstractmethod def choose_rhythm(self): pass def set_binaural_diffs(self, val=None, voice=None): '''"de-tunes" the specified voice by the specified interval (in hertz) if no values are given, random values (in the configurated range) are set for each voice. ''' if val and val != 'random': if voice: voice.binaural_diff = val self.gateway.pd.send(["voice", "binaural", str(voice.id), val]) else: self.gateway.pd.send(["voice", "binaural", -1, val]) for v in self.voices.values(): v.binaural_diff = val else: if self.behaviour['common_binaural_diff']: val = random.random() * self.behaviour.get("max_binaural_diff") for v in self.voices.values(): if not self.behaviour.voice_get(v.id, "automate_binaural_diffs"): continue val = val or random.random() * self.behaviour.voice_get(v.id, "max_binaural_diff") v.binaural_diff = val self.gateway.pd.send(["voice", "binaural", v.id, val]) def set_scale(self, name, min=0, max=128): '''sets the specified scale and generates a new real scale''' self.scale = name self.generate_real_scale(min, max) @staticmethod def assemble_real_scale(scale, min=0, max=128): '''extends the one-octave scale over the specified range''' real_scale = [] value = 0 for n in xrange(min, max): value += 1 index = n % len(scale) if scale[index]: real_scale.append(value) return real_scale def generate_real_scale(self, min=0, max=128): '''extends the one-octave scale over the specified range''' scale = SCALES[self.scale] self.real_scale = self.assemble_real_scale(scale, min, max)