def values(self, p_map, v_note): if v_note != self.actor_note: raise Exception('v_note {0} not in ScalarConstraint actors.'.format(v_note.note)) policy_context = p_map[self.actor_note].policy_context tones = list(policy_context.harmonic_context.tonality.annotation) tones = tones[:-1] # remove final note (same as first) if len(self.scalar_roles) != 0: tones = [tones[i] for i in self.scalar_roles] if p_map[v_note].note is not None: tone = p_map[v_note].note.diatonic_pitch.diatonic_tone return OrderedSet([self.actor_note]) if tone in tones else None pitch_range = policy_context.pitch_range start_partition = max(ChromaticScale.index_to_location(pitch_range.start_index)[0] - 1, 0) end_partition = min(ChromaticScale.index_to_location(pitch_range.end_index)[0] + 1, ChromaticScale.CHROMATIC_END[0]) valid_set = OrderedSet() for tone in tones: for j in range(start_partition, end_partition + 1): pitch = DiatonicPitch(j, tone) if pitch_range.is_pitch_inbounds(pitch): note = Note(pitch, self.actor_note.base_duration, self.actor_note.num_dots) valid_set.add(note) return valid_set
def _build_p_map_dict(self, partial_pitch_results=None): actors = OrderedSet() for p in self.pitch_constraints: actors = actors.union(p.actors) d = OrderedDict() for note in actors: hc = self.hct[note.get_absolute_position().position] if hc is None: raise Exception( 'Cannot locate harmonic context for note \'{0}\''.format( note)) contextual = ContextualNote(PolicyContext(hc, self.pitch_range)) d[note] = contextual if partial_pitch_results is not None: for k, v in partial_pitch_results.items(): if not isinstance(k, Note): raise Exception( 'key of partial_pitch_results must be a Note.') if not isinstance(v, DiatonicPitch): raise Exception( 'value of partial_pitch_results must be a DiatonicPitch.' ) if k not in d: raise Exception( 'Note \'{0}\' of partial result is not a constraint actor.' .format(k)) d[k].note = Note(v, k.base_duration, k.num_dots) return d
def values(self, p_map, v_note): if v_note != self.actor_note: raise Exception("Illegal v_note {0} for constraint".format(v_note)) if p_map[v_note].note is not None: if p_map[v_note].note.diatonic_pitch.chromatic_distance in (p.chromatic_distance for p in self.pitches): return OrderedSet[p_map[v_note].note] raise Exception('Fixed Pitch Select Set Constraint Violated has {0} not in pitch set'.format( p_map[v_note].note.diatonic_pitch)) # Return all pitches (self.pitches) except though, for each # select a representation closest to the target tonality, if it exists. tonality = p_map[self.actor_note].policy_context.harmonic_context.tonality result_pitches = [] for pitch in self.pitches: found_pitch = pitch for p in pitch.enharmonics(): if p.diatonic_tone in tonality.annotation: found_pitch = p break result_pitches.append(found_pitch) result = OrderedSet() for p in result_pitches: result.add(Note(p, self.actor_note.base_duration, self.actor_note.num_dots)) return result
def values(self, p_map, v_note): if v_note != self.actor_note: raise Exception("Illegal v_note {0} for constraints".format(v_note)) if p_map[v_note].note is not None: if p_map[v_note].note.diatonic_pitch.diatonic_tone == self.tone: return {p_map[v_note].note} raise Exception('Fixed Tone Policy Violated has {0} should be {1}'.format( p_map[v_note].note.diatonic_pitch.diatonic_tone, self.tone)) contextual_note = p_map[self.actor_note] policy_context = contextual_note.policy_context pitch_range = contextual_note.policy_context.pitch_range start_partition = max(ChromaticScale.index_to_location(pitch_range.start_index)[0] - 1, 0) end_partition = min(ChromaticScale.index_to_location(pitch_range.end_index)[0] + 1, ChromaticScale.CHROMATIC_END[0]) # Try to find that tone in target's tonality/scale. tone = self.tone for t_str in self.tone.enharmonics(): t = DiatonicToneCache.get_tone(t_str) for scale_tone in PitchScale(policy_context.harmonic_context.tonality, policy_context.pitch_range).tone_scale: if scale_tone == t: tone = t break valid_set = OrderedSet() for i in range(start_partition, end_partition + 1): pitch = DiatonicPitch(i, tone) if pitch_range.is_pitch_inbounds(pitch): note = Note(pitch, self.actor_note.base_duration, self.actor_note.num_dots) valid_set.add(note) return valid_set
def values(self, p_map, v_target_note): if v_target_note == self.note_two: up_down = self.up_down v_source_note = self.note_one elif v_target_note == self.note_one: up_down = not self.up_down v_source_note = self.note_two else: raise Exception( 'v_note specification does not match any v_note in constraints.' ) if p_map[v_target_note].note is not None: return OrderedSet([p_map[v_target_note].note]) arg_contextual_note = p_map[v_source_note] target_contextual_note = p_map[v_target_note] if arg_contextual_note.note is None: pitches = p_map.all_tonal_pitches(v_target_note) result = OrderedSet() for p in pitches: result.add( Note(p, v_target_note.base_duration, v_target_note.num_dots)) return result return self.compute_result(arg_contextual_note, target_contextual_note, up_down)
def test_copy(self): s = OrderedSet() for i in range(1, 10): to = TestObject(i) s.add(to) s1 = s.copy() assert id(s1) != id(s) objects = [t.ord_value() for t in s1] assert objects == [1, 2, 3, 4, 5, 6, 7, 8, 9]
def compute_full_result(p_map, v_note, e_set): pitches = p_map.all_tonal_pitches(v_note) e_chrm_list = [n.diatonic_pitch.chromatic_distance for n in e_set] del_set = OrderedSet() for p in pitches: if p.chromatic_distance in e_chrm_list: del_set.add(p) pitches = [p for p in pitches if p not in del_set] return OrderedSet([Note(p, v_note.base_duration, v_note.num_dots) for p in pitches])
def _candidate_closure(self, p_map, v_note): """ Find all policies that have v_note as a parameter, and collect their unassigned actors into a set without replication. :param p_map: PMap :param v_note: ContextualNote :return: """ policies = self.v_policy_map[v_note] candidates = OrderedSet() for p in policies: candidates = candidates.union(p_map.unassigned_actors(p)) return candidates
def compute_result(self, arg_contextual_note, target_contextual_note, down_steps, up_steps): arg_pitch = arg_contextual_note.note.diatonic_pitch pitches = PitchScale.compute_tonal_pitch_range( target_contextual_note.policy_context.harmonic_context.tonality, arg_pitch, down_steps, up_steps) result = OrderedSet() for pitch in pitches: result.add( Note(pitch, self.note_two.base_duration, self.note_two.num_dots)) return result
def values(self, p_map, v_note): """ Compute possible values for v_note's target. :param p_map: :param v_note: :return: Note: Here is why the intervals are reversed for solving for note_one: Suppose x --> [x-a, x + b]. Then for some value y, for t with y-b<=t<=y+a, we have t -->[t-a, t+b], but from the inequalities, t-a<=y<t+b - so the reverse map is [y-b, y+a] <-- y, which is exactly what happens below. """ if v_note == self.note_two: source = self.note_one target = self.note_two up_intvl = self.up_interval down_intvl = self.down_interval elif v_note == self.note_one: source = self.note_two target = self.note_one up_intvl = self.down_interval down_intvl = self.up_interval else: raise Exception( 'v_note specification does not match any v_note in constraints.' ) if p_map[target].note is not None: return OrderedSet([p_map[target].note]) arg_contextual_note = p_map[source] target_contextual_note = p_map[target] if arg_contextual_note.note is None: pitches = p_map.all_tonal_pitches(v_note) result = OrderedSet() for p in pitches: result.add(Note(p, v_note.base_duration, v_note.num_dots)) return result # return {Note(p, v_note.base_duration, v_note.num_dots) for p in pitches} return self.compute_result(arg_contextual_note, target_contextual_note, up_intvl, down_intvl)
def values(self, p_map, v_note): assigned = p_map.assigned_actors(self) unassigned = p_map.unassigned_actors(self) if v_note in unassigned: tonality = p_map[v_note].policy_context.harmonic_context.tonality pitches = PitchScale.compute_tonal_pitches(tonality, self.pitch_range) answer = OrderedSet() for p in pitches: answer.add(Note(p, v_note.base_duration, v_note.num_dots)) return answer # return {Note(p, v_note.base_duration, v_note.num_dots) for p in pitches} if v_note in assigned: return OrderedSet([p_map[v_note].note]) raise Exception( '{0} is not in actor list for pitch range constraints.'.format( v_note.note))
def test_union(self): s1 = OrderedSet() for i in range(1, 10): s1.add(TestObject(i)) s2 = OrderedSet() for i in range(100, 110): to = TestObject(i) s2.add(to) s = s1.union(s2) objects = [t.ord_value() for t in s] assert objects == [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 ]
def values(self, pdi, note): position = pdi.correct_position( note.get_absolute_position()) # position should be adjusted ts = pdi.ts_event_sequence.floor_event(position).object beat_position = TimeConversion(pdi.tempo_event_sequence, pdi.ts_event_sequence, Position(pdi.line_duration())).\ position_to_bp(position) num_beats = ts.beats_per_measure if beat_position.beat_fraction > 0 else ts.beats_per_measure - 1 beat_index = (beat_position.beat + 1) % ts.beats_per_measure delta_t = ts.beat_duration if beat_position.beat_fraction == 0 else \ ts.beat_duration * (1 - beat_position.beat_fraction) deltas = OrderedSet() for i in range(0, num_beats): if (self.beat_ids is not None and beat_index in self.beat_ids) or \ (self.beat_type is not None and self.beat_type == ts.beat_type(beat_index)): deltas.add(delta_t) delta_t += ts.beat_duration beat_index = (beat_index + 1) % ts.beats_per_measure return deltas
def test_remove(self): s = OrderedSet() fone = None fmid = None flast = None for i in range(1, 10): item = TestObject(i) if i == 1: fone = item elif i == 6: fmid = item elif i == 9: flast = item s.add(item) s.remove(fmid) s.remove(flast) s.remove(fone) objects = [t.ord_value() for t in s] assert objects == [2, 3, 4, 5, 7, 8]
def values(self, p_map, v_note): """ Return a set of possible pitches that v_note can take that must be in p_map target's chord. :param p_map: :param v_note: :return: """ if v_note != self.actor_note: raise Exception( 'v_note {0} not in ChordalToneConstraint actors.'.format( v_note.note)) policy_context = p_map[self.actor_note].policy_context tones = policy_context.harmonic_context.chord.tones if p_map[v_note].note is not None: note = p_map[v_note].note if note.diatonic_pitch.diatonic_tone in tones: return {note} raise Exception( 'Chordal Pitch Policy Violated has {0} should be member of chord {1}' .format(p_map[v_note].note.diatonic_pitch, policy_context.harmonic_context.chord)) pitch_range = policy_context.pitch_range start_partition = max( ChromaticScale.index_to_location(pitch_range.start_index)[0] - 1, 0) end_partition = min( ChromaticScale.index_to_location(pitch_range.end_index)[0] + 1, ChromaticScale.CHROMATIC_END[0]) valid_set = OrderedSet() for tone in tones: for i in range(start_partition, end_partition + 1): pitch = DiatonicPitch(i, tone[0]) if pitch_range.is_pitch_inbounds(str(pitch)): note = Note(pitch, self.actor_note.base_duration, self.actor_note.num_dots) valid_set.add(note) return valid_set
def values(self, p_map, v_note): if v_note != self.actor_note: raise Exception("Illegal v_note {0} for constraint".format(v_note)) if p_map[v_note].note is not None: if p_map[ v_note].note.diatonic_pitch.chromatic_distance == self.pitch.chromatic_distance: return OrderedSet([p_map[v_note].note]) raise Exception( 'Fixed Pitch Constraint Violated has {0} should be {1}'.format( p_map[v_note].note.diatonic_pitch, self.pitch)) # select a pitch representation closest to the target tonality, if it exists. policy_context = p_map[self.actor_note].policy_context pitch = self.pitch for p in self.pitch.enharmonics(): if p.diatonic_tone in policy_context.harmonic_context.tonality.annotation: pitch = p break actual_note = Note(pitch, self.actor_note.base_duration, self.actor_note.num_dots) return OrderedSet([actual_note])
def _policy_values(self, p_map, v_note): """ For v_note, find all note values for its target that satisfy all policies in which v_note is involved. :param p_map: PMap :param v_note: ContextualNote :return: A set of notes with same duration as v_note, varying in pitch. """ pitches = None for p in self.v_policy_map[v_note]: p_values = p.values(p_map, v_note) if p_values is None: returned_pitches = OrderedSet() # None means p cannot be satisfied! else: returned_pitches = OrderedSet() for n in p_values: returned_pitches.add(n.diatonic_pitch) pitches = returned_pitches if pitches is None else pitches.intersection(returned_pitches) retset = OrderedSet() for p in pitches: retset.add(Note(p, v_note.base_duration, v_note.num_dots)) return retset
def compute_result(self, arg_contextual_note, target_contextual_note, up_intvl, down_intvl): """ :param arg_contextual_note: :param target_contextual_note: :param up_intvl: :param down_intvl: :return: """ starting_pitch = arg_contextual_note.note.diatonic_pitch chromatic_distance_start = starting_pitch.chromatic_distance - down_intvl.chromatic_distance chromatic_distance_end = starting_pitch.chromatic_distance + up_intvl.chromatic_distance r_start = max( chromatic_distance_start, target_contextual_note.policy_context.pitch_range.start_index) r_end = min( chromatic_distance_end, target_contextual_note.policy_context.pitch_range.end_index) if r_start > r_end: return OrderedSet() pitch_range = PitchRange(r_start, r_end) pitch_scale = PitchScale( target_contextual_note.policy_context.harmonic_context.tonality, pitch_range) result = OrderedSet() for pitch in pitch_scale.pitch_scale: result.add( Note(pitch, self.note_two.base_duration, self.note_two.num_dots)) # v_result = {Note(pitch, self.note_two.base_duration, self.note_two.num_dots) # for pitch in pitch_scale.pitch_scale} return result
def values(self, p_map, v_note): """ Find value candidates for v_note target, given p_map. In this constraints, we are more interested in the set of values that v_note target cannot take. :param p_map: :param v_note: :return: """ assigned = p_map.assigned_actors(self) unassigned = p_map.unassigned_actors(self) if len(assigned) == 0: pitches = p_map.all_tonal_pitches(v_note) return {Note(p, v_note.note.base_duration, v_note.note.num_dots) for p in pitches} if v_note in assigned: return {p_map[v_note].note} if v_note not in unassigned: raise Exception('{0} is not in actor list of not equal pitch constraints.'.format(v_note.note)) e_set = OrderedSet() for v in assigned: e_set.add(p_map[v].note) return NotEqualPitchConstraint.compute_full_result(p_map, v_note, e_set)
def __init__(self, policies): """ Constructor. :param policies: non-null set of policies """ if policies is None or (not isinstance(policies, set) and not isinstance(policies, list) and not isinstance(policies, OrderedSet)): raise Exception('Policies must be non-null and a Set') self._policies = OrderedSet(policies) self.v_policy_map = dict() self._build_v_policy_map() self.__instance_limit = 0 self.__num_instances = 0 self.__full_results = list()
def compute_note(p_map, assigned_note, unassigned_note): """ For an assigned note and an unassigned note, return for unassigned, a note the same as assigned, but with pitch enharmonic to its tonality. :param p_map: :param assigned_note: :param unassigned_note: :return: """ # select a pitch representation closest to the tonality, if it exists. policy_context = p_map[unassigned_note].policy_context pitch = p_map[assigned_note].note.diatonic_pitch for p in pitch.enharmonics(): for t in PitchScale(policy_context.harmonic_context.tonality, policy_context.pitch_range).tone_scale: if p.diatonic_tone == t: pitch = p break actual_note = Note(pitch, unassigned_note.base_duration, unassigned_note.num_dots) return OrderedSet([actual_note])
def compute_result(self, arg_contextual_note, target_contextual_note, up_down): """ Compute the target note from the arg note basecd on up_down and self.n_steps. :param arg_contextual_note: Given note :param target_contextual_note: Note to find based on constraints. :param up_down: :return: """ if str(arg_contextual_note.policy_context.harmonic_context.tonality) != \ str(target_contextual_note.policy_context.harmonic_context.tonality): raise Exception( 'Note one and two of tonal step constraints must match on tonality' ) starting_pitch = arg_contextual_note.note.diatonic_pitch pitch_scale = PitchScale( target_contextual_note.policy_context.harmonic_context.tonality, target_contextual_note.policy_context.pitch_range) scale = pitch_scale.pitch_scale pitch_index = scale.index( starting_pitch) if starting_pitch in scale else None if pitch_index is None: return None end_index = pitch_index + ( self.n_steps * (1 if up_down == PitchStepConstraint.UP else -1)) if end_index not in range(0, len(scale)): return None end_pitch = scale[end_index] return OrderedSet([ Note(end_pitch, self.note_two.base_duration, self.note_two.num_dots) ])
def test_for_book_example_2(self): print('----- test for book example 2 -----') source_instance_expression = '{<A-Major:i> [sA:4 A A A] qA:4 [iA:4 A] <:iv> qA:4 [sA:4 A A A] qA:4}' target_instance_expression = '{<G-Major:i> wA:4 <:iv> wA:4}' lge = LineGrammarExecutor() source_instance_line, source_instance_hct = lge.parse( source_instance_expression) actors = source_instance_line.get_all_notes() for a in actors: print("{0}".format(a)) target_instance_line, target_instance_hct = lge.parse( target_instance_expression) target_hcs = target_instance_hct.hc_list() for hc in target_hcs: print("{0}".format(hc)) pitch_range = PitchRange( DiatonicPitch.parse('C:4').chromatic_distance, DiatonicPitch.parse('C:6').chromatic_distance) p_map = PMap.create(source_instance_expression, pitch_range, [('G-Major:I', Duration(3, 4)), ('G-Major:IV', Duration(3, 4))]) actors = p_map.actors policies = OrderedSet() policies.add( StepSequenceConstraint( [actors[0], actors[1], actors[2], actors[3]], [1, 1, 1])) policies.add(ChordalPitchConstraint(actors[0])) policies.add(ChordalPitchConstraint(actors[4])) policies.add(ChordalPitchConstraint(actors[8])) policies.add( StepSequenceConstraint( [actors[8], actors[9], actors[10], actors[11]], [1, -1, -1])) policies.add(EqualPitchConstraint([actors[0], actors[12]])) policies.add(EqualPitchConstraint([actors[4], actors[7]])) policies.add( RelativeDiatonicConstraint(actors[4], actors[5], Interval(3, IntervalType.Major), Interval(1, IntervalType.Perfect))) policies.add(StepSequenceConstraint([actors[5], actors[6]], [-1])) # policies.add(ChordalPitchConstraint(actors[7])) solver = PitchConstraintSolver(policies) full_results, partial_results = solver.solve(p_map) print('Results has {0} results.'.format(len(full_results))) for pm in full_results: if str(pm[actors[7]].note.diatonic_pitch) == 'D:4' and str( pm[actors[0]].note.diatonic_pitch) == 'G:4': print("{0}".format(pm))
def _visit(self, p_map, v_note): """ Recursive method used to derive sets of solution to the constraints constraint problem. :param p_map: PMap :param v_note: ContextualNote, source key of PMap :return: A set of pmaps """ if p_map[v_note].note is not None: # setting means visited return {} results = OrderedSet() # list of tuples (v_note, {solution to v_note's policies}) result_values = self._build_potential_values(p_map, {v_note}) if len(result_values) == 0: return results for value in result_values[0][1]: # [0] is for v_note; [1] is the set of values. p_map[v_note].note = value value_results = OrderedSet() # results for this value for v_note + solutions for ALL peers to v_note # Advantage in the following, setting above partially solves each policy v_note is involved in. # For this 'value' for v_note, dive depth first through all v_note peers (all policies v_note is in) # which collectively we call a branch. # peer_candidates are all unassigned actors in v_note's policies. peer_candidates = self._candidate_closure(p_map, v_note) if len(peer_candidates) == 0: # We reached the end of a branch, save the result. if self._full_check_and_validate(p_map): if self.instance_limit != -1 and self.__num_instances >= self.instance_limit: return results self.full_results.append(p_map.replicate()) self.__num_instances = self.__num_instances + 1 else: value_results.add(p_map.replicate()) # We only need a shallow copy else: for c_note in peer_candidates: if self.instance_limit != -1 and self.__num_instances >= self.instance_limit: results = results.union(value_results) return results if len(value_results) == 0: # first time through this loop per value, visit with p_map! value_results = self._visit(p_map, c_note) if len(value_results) == 0: # Indicates failure to assign c_note, move to next value. break # If one peer fails, they all will, for this 'value'! else: value_results_copy = value_results.copy() # for peer c_note, if all r below fails (len(cand_results) == 0) should we also move on to # next value? Add 'found' flag, set after union, after loop, check if False, to break found = False for r in value_results_copy: if r[c_note].note is None: cand_results = self._visit(r, c_note) if len(cand_results) != 0: value_results = value_results.union(cand_results) found = True value_results.remove(r) # r has no c_note assigned, what was returned did! # If not, r's peers cannot be assigned! if found is False: # Same as if part, if c_note produces no results, it cannot be assigned. break # If one peer fails, they all will! results = results.union(value_results) p_map[v_note].note = None return results
def test_ordering(self): s1 = OrderedSet() for i in range(1, 10): s1.add(TestObject(i)) objects = [t.ord_value() for t in s1] assert objects == [1, 2, 3, 4, 5, 6, 7, 8, 9]
def test_constructor(self): s = OrderedSet([1, 2, 3, 4, 5]) items = [item for item in s] assert items == [1, 2, 3, 4, 5]
def _compute_values(self, v_note, hc): """ The core pitch computation used in the values() method. :param v_note: :param hc: :return: Notes: The key for the new constraint is the call to 'eval_as_pitch', which returns one or two pitches nearest to the reshape curve. Case 1: Exactly one pitch. If pitch is scalar, use If pitch is non-scalar - if on-beat and pitch is 1/2 step from any chordal tone, do not use, else use Case 2: Two notes If both are scalar, use the closest to the curve. If both are non-scalar, use the nearest to curve, and apply as in case 1 (example, A, Bb in C-) If one is scalar and the other not: If the scalar is on-beat or is within the scalar bias weight, use it Otherwise use the non-scalar """ self.candidate_pitches = list() for p in self.pitch_function.eval_as_pitch(self.note_position.position): e = FitPitchToFunctionConstraint._get_tonal_equivalent(p, hc) self.candidate_pitches.append((e, True) if e is not None else (p, False)) if len(self.candidate_pitches) == 0: return OrderedSet() v_note_beat_position = self._get_beat_position(v_note.get_absolute_position()) # If one solution pitch if len(self.candidate_pitches) == 1: # if is scalar, use it if self.candidate_pitches[0][1]: return OrderedSet([Note(self.candidate_pitches[0][0], v_note.base_duration, v_note.num_dots)]) # non-scalar # if on beat and half step off from chord tone, do not use if v_note_beat_position is not None and v_note_beat_position == BeatType.Strong: if FitPitchToFunctionConstraint._pitch_is_halfstep_off_chord(self.candidate_pitches[0][0], hc): return set() # otherwise use return OrderedSet([Note(self.candidate_pitches[0][0], v_note.base_duration, v_note.num_dots)]) # if 2 pitch solution if len(self.candidate_pitches) == 2: # If both scalar if self.candidate_pitches[0][1] and self.candidate_pitches[1][1]: interp = self.pitch_function.pitch_range_interpreter index = min(enumerate(self.candidate_pitches), key=lambda x: abs(interp.value_for(x[1][0]) - self.function_value))[0] # return pitch closest to curve value return OrderedSet([Note(self.candidate_pitches[index][0], v_note.base_duration, v_note.num_dots)]) elif not self.candidate_pitches[0][1] and not self.candidate_pitches[1][1]: index = min(enumerate(self.candidate_pitches), key=lambda x: abs(interp.value_for(x[1][0]) - self.function_value))[0] pitch = self.candidate_pitches[index] # if on beat and half step off from chord tone, do not use if v_note_beat_position is not None: if FitPitchToFunctionConstraint._pitch_is_halfstep_off_chord(pitch[0], hc): return OrderedSet() # otherwise use return OrderedSet([Note(pitch[0], v_note.base_duration, v_note.num_dots)]) else: scalar_pitch = self.candidate_pitches[0][0] if self.candidate_pitches[0][1] \ else self.candidate_pitches[1][0] nonscalar_pitch = self.candidate_pitches[0][0] if self.candidate_pitches[1][1] \ else self.candidate_pitches[1][0] scalar_bias = (scalar_pitch.chromatic_distance - self.function_value) /\ abs(scalar_pitch.chromatic_distance - nonscalar_pitch.chromatic_distance) if v_note_beat_position is not None or scalar_bias <= FitPitchToFunctionConstraint.SCALAR_BIAS_WEIGHT: return OrderedSet([Note(scalar_pitch, v_note.base_duration, v_note.num_dots)]) else: return OrderedSet([Note(nonscalar_pitch, v_note.base_duration, v_note.num_dots)]) else: raise Exception('Internal error - more than 2 candidate pitches.')
def values(self, p_map, v_note): """ :param p_map: :param v_note: :return: """ index = self.actors.index(v_note) if v_note in self.actors else None if index is None: raise Exception('Cannot find v_note in constraints actors') if p_map[v_note].note is not None: return OrderedSet([p_map[v_note].note]) # find the first assigned note assigned_index = None for i in range(0, len(self.actors)): if p_map[self.actors[i]].note is not None: assigned_index = i break if assigned_index is None: pitches = p_map.all_tonal_pitches(v_note) return OrderedSet([Note(p, v_note.base_duration, v_note.num_dots) for p in pitches]) known_note = p_map[self.actors[assigned_index]].note if assigned_index < index: for i in range(assigned_index + 1, index + 1): unknown_contextual_note = p_map[self.actors[i]] unknown_note = unknown_contextual_note.note if unknown_note is not None: known_note = unknown_note continue lower_index = self.variance_list[i - 1] if self.variance_list[i - 1] < 0 else 0 upper_index = self.variance_list[i - 1] if self.variance_list[i - 1] > 0 else 0 pitches = PitchScale.compute_tonal_pitch_range( unknown_contextual_note.policy_context.harmonic_context.tonality, known_note.diatonic_pitch, lower_index, upper_index) pitch_index = self.variance_list[i - 1] + (len(pitches) - 1 if self.variance_list[i - 1] < 0 else 0) if pitch_index < 0 or pitch_index >= len(pitches): if pitches is None or len(pitches) == 0: return OrderedSet() pitch = pitches[0] if pitch_index < 0 else pitches[len(pitches) - 1] else: pitch = pitches[pitch_index] known_note = Note(pitch, self.actors[i].base_duration, self.actors[i].num_dots) return OrderedSet([known_note]) for i in range(assigned_index - 1, index - 1, -1): unknown_contextual_note = p_map[self.actors[i]] unknown_note = unknown_contextual_note.note if unknown_note is not None: known_note = unknown_note continue upper_index = -self.variance_list[i] if self.variance_list[i] < 0 else 0 lower_index = -self.variance_list[i] if self.variance_list[i] > 0 else 0 pitches = PitchScale.compute_tonal_pitch_range( unknown_contextual_note.policy_context.harmonic_context.tonality, known_note.diatonic_pitch, lower_index, upper_index) pitch_index = -self.variance_list[i] + (len(pitches) - 1 if self.variance_list[i] > 0 else 0) if pitch_index < 0: pitch_index = 0 elif pitch_index >= len(pitches): pitch_index = len(pitches) - 1 pitch = pitches[pitch_index] known_note = Note(pitch, self.actors[i].base_duration, self.actors[i].num_dots) return OrderedSet([known_note])
def values(self, p_map, v_note): """ Compute possible values for v_note's target. :param p_map: note-->contextual_note :param v_note: Note :return: Candidate Notes. Note: Here is why the intervals are reversed for solving for note_one: Suppose x --> [x-a, x + b]. Then for some value y, for t with y-b<=t<=y+a, we have t -->[t-a, t+b], but from the inequalities, t-a<=y<t+b - so the reverse map is [y-b, y+a] <-- y, which is exactly what happens below. """ if v_note == self.note_two: source = self.note_one target = self.note_two comparative = self.comparative elif v_note == self.note_one: source = self.note_two target = self.note_one comparative = 4 - self.comparative else: raise Exception( 'v_note specification does not match any v_note in constraints.' ) if p_map[target].note is not None: return {p_map[target].note} if p_map[source].note is None: answer_range = p_map[target].policy_context.pitch_range source_pitch = None else: # Establish a pitch range commensurate with comparative. qrange = p_map[target].policy_context.pitch_range source_pitch = p_map[source].note.diatonic_pitch if comparative > 2: answer_range = PitchRange(qrange.start_index, source_pitch.chromatic_distance) elif comparative < 2: answer_range = PitchRange(source_pitch.chromatic_distance, qrange.end_index) else: answer_range = PitchRange(source_pitch.chromatic_distance, source_pitch.chromatic_distance) pitches = PitchScale.compute_tonal_pitches( p_map[target].policy_context.harmonic_context.tonality, answer_range) if comparative == 4 and len(pitches) > 0 and source_pitch is not None and \ source_pitch.chromatic_distance == pitches[-1].chromatic_distance: pitches.pop(-1) if comparative == 0 and len(pitches) > 0 and source_pitch is not None and \ source_pitch.chromatic_distance == pitches[0].chromatic_distance: del pitches[0] answer = OrderedSet() for pitch in pitches: answer.add(Note(pitch, target.base_duration, target.num_dots)) return answer