class DetuningExercise: def __init__(self, sampling_frequency: int): # exercise settings self._sampling_frequency = sampling_frequency self._volume = None self._play_type = None self._scale = None self._max_detuning = None self._lowest_pitch = None self._highest_pitch = None self._smallest_interval = None self._largest_interval = None self._synthesizer = Synthesizer(sampling_frequency) self._possible_error = None self._chord_size = None self._voice_length = None # other variables self._actual_example = None self._player = Player(sampling_frequency) def set_sampling_frequency(self, sampling_frequency: int): self._sampling_frequency = sampling_frequency self._synthesizer.set_sampling_frequency(sampling_frequency) self._player.set_sampling_frequency(sampling_frequency) def set_volume(self, volume: float): self._volume = volume self._synthesizer.set_volume(volume) def set_play_type(self, play_type: str): self._play_type = play_type def set_scale(self, scale: Scale): self._scale = scale def set_max_detuning(self, max_detuning: float): self._max_detuning = max_detuning def set_lowest_pitch(self, lowest_pitch: Pitch): self._lowest_pitch = lowest_pitch def set_highest_pitch(self, highest_pitch: Pitch): self._highest_pitch = highest_pitch def set_smallest_interval(self, smallest_interval: Interval): self._smallest_interval = smallest_interval def set_largest_interval(self, largest_interval: Interval): self._largest_interval = largest_interval def set_synthesizer(self, synthesizer: type): self._synthesizer = Synthesizer(self._sampling_frequency, synthesizer) def get_possible_error(self) -> float: return self._possible_error def set_possible_error(self, possible_error: float): self._possible_error = possible_error def get_chord_size(self) -> int: return self._chord_size def set_chord_size(self, chord_size: int): self._chord_size = chord_size def get_voice_length(self) -> int: return self._voice_length def set_voice_length(self, voice_length: int): self._voice_length = voice_length def generate_new_example(self): # Choose detuning actual_detuning = random.uniform( 0, min(self._max_detuning, 1200 / 2 / len(self._scale._pitches))) # Generate melody chord_generator = ChordGenerator( scale=self._scale, lowest_pitch=self._lowest_pitch, highest_pitch=self._highest_pitch, possible_detune=0, smallest_interval=self._smallest_interval, largest_interval=self._largest_interval, chord_size=self._chord_size) tuned_chords = list() for i in range(self._voice_length): tuned_chords.append(chord_generator.generate_chord()) # Create even detuning number_of_sounds = len(tuned_chords) * tuned_chords[0].get_size() if number_of_sounds < 2: raise ValueError( '[DetuningExercise::generate_new_example()] '\ + 'Cannot properly apply detuning to less than 2 sounds!' ) number_of_lowered_sounds = int(np.ceil(number_of_sounds / 3)) lowered_or_unchanged_indices = np.random.choice( np.arange(number_of_sounds), 2 * number_of_lowered_sounds, replace=False) lowered_indices = lowered_or_unchanged_indices[: number_of_lowered_sounds] unchanged_indices = lowered_or_unchanged_indices[ number_of_lowered_sounds:] highened_indices = np.setdiff1d(np.setdiff1d( np.arange(number_of_sounds), lowered_indices, assume_unique=True), unchanged_indices, assume_unique=True) # Detune chords for i in range(len(tuned_chords)): for j in range(tuned_chords[i].get_size()): if i * tuned_chords[i].get_size() + j in lowered_indices: tuned_chords[i]._pitches[j] = tuned_chords[i]._pitches[ j].copy(detune=-actual_detuning) elif i * tuned_chords[i].get_size() + j in highened_indices: tuned_chords[i]._pitches[j] = tuned_chords[i]._pitches[ j].copy(detune=actual_detuning) # Add chords to example self._actual_example = DetuningExample(actual_detuning) for i in range(len(tuned_chords)): self._actual_example.add_chord(tuned_chords[i]) def play_example(self): # Check exercise state if self._actual_example is None: raise RuntimeError( '[VoicesExercise::play_example()] No example to play!') if self._play_type is None: raise RuntimeError( '[VoicesExercise::play_example()] No play type chosen!') # Play signal = np.zeros(0) if self._play_type == 'Upwards': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_up( self._actual_example.chords) ])) elif self._play_type == 'Downwards': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_down( self._actual_example.chords) ])) elif self._play_type == 'Upwards with hold': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_up_hold( self._actual_example.chords) ])) elif self._play_type == 'Downwards with hold': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_down_hold( self._actual_example.chords) ])) elif self._play_type == 'Together': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_together( self._actual_example.chords) ])) else: raise RuntimeError( '[VoicesExercise::play_example()] Unknown play type!') def answer_example(self, answer: float) -> (bool, float): if self._actual_example.detuning\ + self._possible_error\ >= answer\ >= self._actual_example.detuning\ - self._possible_error: return True, self._actual_example.detuning else: return False, self._actual_example.detuning
class VoicesExercise: def __init__(self, sampling_frequency: int): # exercise settings self._sampling_frequency = sampling_frequency self._volume = None self._play_type = None self._scale = None self._lowest_pitch = None self._highest_pitch = None self._smallest_interval = None self._largest_interval = None self._possible_detune = None self._synthesizer = Synthesizer(sampling_frequency) self._possible_error = None self._chord_size = None self._voice_length = None self._if_first_note_provided = None # other variables self._actual_example = None self._player = Player(sampling_frequency) def set_sampling_frequency(self, sampling_frequency: int): self._sampling_frequency = sampling_frequency self._synthesizer.set_sampling_frequency(sampling_frequency) self._player.set_sampling_frequency(sampling_frequency) def set_volume(self, volume: float): self._volume = volume self._synthesizer.set_volume(volume) def set_play_type(self, play_type: str): self._play_type = play_type def set_scale(self, scale: Scale): self._scale = scale def set_lowest_pitch(self, lowest_pitch: Pitch): self._lowest_pitch = lowest_pitch def set_highest_pitch(self, highest_pitch: Pitch): self._highest_pitch = highest_pitch def set_smallest_interval(self, smallest_interval: Interval): self._smallest_interval = smallest_interval def set_largest_interval(self, largest_interval: Interval): self._largest_interval = largest_interval def set_possible_detune(self, possible_detune: float): self._possible_detune = possible_detune def set_synthesizer(self, synthesizer: type): self._synthesizer = Synthesizer(self._sampling_frequency, synthesizer) def get_possible_error(self) -> float: return self._possible_error def set_possible_error(self, possible_error: float): self._possible_error = possible_error def get_chord_size(self) -> int: return self._chord_size def set_chord_size(self, chord_size: int): self._chord_size = chord_size def get_voice_length(self) -> int: return self._voice_length def set_voice_length(self, voice_length: int): self._voice_length = voice_length def set_if_first_note_provided(self, if_first_note_provided: bool): self._if_first_note_provided = if_first_note_provided def generate_new_example(self): chord_generator = ChordGenerator( scale=self._scale, lowest_pitch=self._lowest_pitch, highest_pitch=self._highest_pitch, possible_detune=self._possible_detune, smallest_interval=self._smallest_interval, largest_interval=self._largest_interval, chord_size=self._chord_size) self._actual_example = VoicesExample() for i in range(self._voice_length): self._actual_example.add_chord(chord_generator.generate_chord()) def get_first_note(self): if self._if_first_note_provided: return self._actual_example.get_pitch(0, 0) def play_example(self): # Check exercise state if self._actual_example is None: raise RuntimeError( '[VoicesExercise::play_example()] No example to play!') if self._play_type is None: raise RuntimeError( '[VoicesExercise::play_example()] No play type chosen!') # Play signal = np.zeros(0) if self._play_type == 'Upwards': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_up( self._actual_example.chords) ])) elif self._play_type == 'Downwards': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_down( self._actual_example.chords) ])) elif self._play_type == 'Upwards with hold': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_up_hold( self._actual_example.chords) ])) elif self._play_type == 'Downwards with hold': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_down_hold( self._actual_example.chords) ])) elif self._play_type == 'Together': self._player.play( np.concatenate([ signal, self._synthesizer.generate_chords_together( self._actual_example.chords) ])) else: raise RuntimeError( '[VoicesExercise::play_example()] Unknown play type!') def answer_example(self, user_answers: list) -> VoicesAnswer: # Prepare correct answers matrix correct_answers = list() for chord_id in range(self._actual_example.get_voice_length()): correct_answers.append(list()) for voice_id in range(self._actual_example.get_chord_size()): correct_answers[-1].append(False) # Add first note to user answers if self._if_first_note_provided: user_answers[0].append(self.get_first_note().get_cents_from_a()) # Set hit answers to true for chord_id in range(self._actual_example.get_voice_length()): for voice_id in range(self._actual_example.get_chord_size()): for user_answer in user_answers[chord_id]: if self._actual_example.get_pitch( chord_id, voice_id ).get_cents_from_a() + self._possible_error\ >= user_answer\ >= self._actual_example.get_pitch( chord_id, voice_id ).get_cents_from_a() - self._possible_error: correct_answers[chord_id][voice_id] = True break # Return answer object return VoicesAnswer(self._actual_example, correct_answers)