def test_add(): os = OrderedSet() for value in range(100): os.add(value) assert len(os) == 100 for value in range(100): assert value in os
def test_index(): values = list(range(100)) random.shuffle(values) os = OrderedSet(values) assert len(os) == len(values) for value in values: assert values.index(value) == os.index(value)
def test_contains(): os = OrderedSet(range(100)) assert len(os) == 100 for value in range(100): assert value in os assert os.count(value) == 1 assert -1 not in os assert 100 not in os
def create_tags_csv(location: str = ""): """ extract tags from the file name write a csv file with those tags :param location: optional content of directory column This csv can be modified to be used with :func:`write_exif_using_csv` or :func:`placeinfo.write_infos` If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text. """ inpath = os.getcwd() tag_set = OrderedSet() tag_set_names = OrderedSet() out_filename = get_info_dir("tags_places.csv") tags_places_file, writer = fileop.create_csv_writer( out_filename, ["directory", "name_part"]) filenameAccessors = [ FilenameAccessor(filename) for filename in get_plain_filenames_of_type(image_types, inpath) ] for fileNameAccessor in filenameAccessors: for tag in fileNameAccessor.tags(): tag_set.add(tag) writeToFile(get_info_dir("tags.txt"), location + "\n\t" + "\n\t".join(tag_set) + "\n") for tag in tag_set: tag_set_names.add((location, tag)) writer.writerows(tag_set_names) tags_places_file.close()
def create_counters_csv_per_dir(): """ extract counter from the file name write a csv file with those counters for each directory This csv can be modified to be used with :func:`write_exif_using_csv` If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text. """ log_function_call(create_tags_csv_per_dir.__name__) inpath = os.getcwd() tag_set_names = OrderedSet() out_filename = get_info_dir("tags_counters.csv") csvfile, writer = fileop.create_csv_writer(out_filename, [ "directory", "name_main", "name_part", "first", "last", "tags3", "description" ]) for (dirpath, dirnames, filenames) in os.walk(inpath): if not inpath == dirpath: continue for dirname in dirnames: filenameAccessors = [ FilenameAccessor(filename) for filename in get_plain_filenames_of_type( image_types, dirpath, dirname) ] if len(filenameAccessors) == 0: continue _add_counter_csv_entries(dirname, filenameAccessors, tag_set_names) writer.writerows(tag_set_names) csvfile.close()
def test_getitem(): values = list(range(100)) random.shuffle(values) os = OrderedSet(values) assert len(os) == len(values) for index in range(len(os)): assert os[index] == values[index]
def _add_counter_csv_entries(dirname: str, filenameAccessors: List[FilenameAccessor], tag_set_names: OrderedSet): fileNameAccessorFirst = filenameAccessors[0] fileNameAccessorLast = filenameAccessors[0] for filenameAccessor in filenameAccessors[1:-1]: if not filenameAccessor.is_direct_successor_of(fileNameAccessorLast): tag_set_names.add((dirname, fileNameAccessorFirst.pre, fileNameAccessorFirst.first_posttag(), fileNameAccessorFirst.counter_main(), fileNameAccessorLast.counter_main())) fileNameAccessorFirst = filenameAccessor fileNameAccessorLast = filenameAccessor tag_set_names.add((dirname, fileNameAccessorFirst.pre, fileNameAccessorFirst.first_posttag(), fileNameAccessorFirst.counter_main(), fileNameAccessorLast.counter_main()))
def __init__(self, distance_measure: MonophonicRhythmDistanceMeasure = HammingDistanceMeasure, rhythm_player: tp.Union[BSRhythmPlayer, tp.Type[BSRhythmPlayer], None] = None): self._config = BSConfig() self._corpus = None # type: MidiRhythmCorpus self._corpus_resolution = -1 self._distances_to_target = np.empty(0) self._distances_to_target_rhythm_are_stale = False self._rhythm_measure = SummedMonophonicRhythmDistance( ) # type: SummedMonophonicRhythmDistance self._rhythm_selection = OrderedSet() self._target_rhythm = None # type: tp.Union[MidiRhythm, None] self._target_rhythm_prev_update = None self._lock = threading.Lock() self._rhythm_player = None self._callbacks = dict((action, OrderedSet()) for action in [ BSController.RHYTHM_SELECTION, BSController.CORPUS_LOADED, BSController.DISTANCES_TO_TARGET_UPDATED, BSController. RHYTHM_PLAYBACK_START, BSController.RHYTHM_PLAYBACK_STOP, BSController.TARGET_RHYTHM_SET, BSController.DISTANCE_MEASURE_SET, BSController.RHYTHM_LOADER_REGISTERED ]) self._rhythm_loaders = OrderedDict( ) # rhythm loaders by loader source name self.set_rhythm_player(rhythm_player) self.set_distance_measure(distance_measure) # automatically register a loader for the currently selected rhythm self.register_rhythm_loader(BSSelectedMidiRhythmLoader(self)) # setup config change handlers self._setup_config() # if a midi root directory is set, load the corpus if self.get_config().midi_root_directory.get(): self.load_corpus()
def create_names_csv_per_dir(start_after_dir=''): """ extract names from the file path write a csv file with those names for each directory This csv can be modified to be used with :func:`write_exif_using_csv` If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text. """ log_function_call(create_names_csv_per_dir.__name__) inpath = os.getcwd() tag_set_names = OrderedSet() out_filename = get_info_dir("tags_names.csv") csvfile, writer = fileop.create_csv_writer( out_filename, ["directory", "name_main", "tags"]) for (dirpath, dirnames, filenames) in os.walk(inpath): if is_invalid_path(dirpath): continue filenameAccessors = [ FilenameAccessor(filename) for filename in filterFiles(filenames, image_types) ] if len(filenameAccessors) == 0: continue tags = [] found = False for part in dirpath.split(os.sep): if found: tags += part.split(', ') else: found = part == start_after_dir filenameAccessorLast = filenameAccessors[0] tag_set_names.add( (", ".join(tags), filenameAccessorLast.pre, ', '.join(OrderedSet(tags + [filenameAccessorLast.pre])))) for filenameAccessor in filenameAccessors[1:]: if not filenameAccessor.pre == filenameAccessorLast.pre: tag_set_names.add( (", ".join(tags), filenameAccessor.pre, ', '.join(OrderedSet(tags + [filenameAccessor.pre])))) filenameAccessorLast = filenameAccessor writer.writerows(tag_set_names) csvfile.close()
def create_tags_csv_per_dir(): """ extract tags from the file name write a csv file with those tags and group them by toplevel directory This csv can be modified to be used with :func:`write_exif_using_csv` or :func:`placeinfo.write_infos` If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text. """ log_function_call(create_tags_csv_per_dir.__name__) inpath = os.getcwd() tag_set_names = OrderedSet() out_filename = get_info_dir("tags_per_dir.csv") tags_places_file, writer = fileop.create_csv_writer( out_filename, ["directory", "name_part"]) for (dirpath, dirnames, filenames) in os.walk(inpath): if not inpath == dirpath: continue for dirname in dirnames: tag_set = OrderedSet() filenameAccessors = [ FilenameAccessor(filename) for filename in get_plain_filenames_of_type( image_types, dirpath, dirname) ] if len(filenameAccessors) == 0: continue for fileNameAccessor in filenameAccessors: for tag in fileNameAccessor.tags(): tag_set.add(tag) writeToFile(get_info_dir("tags.txt"), dirname + "\n\t" + "\n\t".join(tag_set) + "\n") dirname_split = dirname.split("_") subnames = [ subname for subname in dirname_split if not subname.isnumeric() ] dirname = "_".join(subnames) for tag in tag_set: tag_set_names.add((dirname, tag)) writer.writerows(tag_set_names) tags_places_file.close()
def set_description_from_relevant(self): claims = self._db.get_claims() topic_judgments = self._read_judgments(self._judgment_path) posts = self._db.get_posts() post_dict = {p.post_id: p for p in posts} for claim in claims: topic_id = int(claim.claim_id) # tweets = self._twitter_api.get_tweets_by_ids(topic_judgments[topic_id][:self._num_of_relevant_tweets]) # posts, authors = self._db.convert_tweets_to_posts_and_authors(tweets, self._domain) posts = list( map(post_dict.get, topic_judgments[topic_id][:self._num_of_relevant_tweets])) claim_content = OrderedSet(claim.keywords.lower().split()) for post in posts: list(map(claim_content.add, clean_tweet(post.content).split())) # if len(claim_content) > 25: # break claim.description = clean_claim_description( ' '.join(claim_content), True) self._db.addPosts(claims)
def test_reversed(): os = OrderedSet(range(100)) assert list(reversed(os)) == list(reversed(range(100))) names = ['eve', 'carol', 'alice', 'dave', 'bob'] os = OrderedSet(names) assert list(reversed(os)) == list(reversed(names))
def test_iter(): os = OrderedSet(range(100)) assert list(os) == list(range(100)) names = ['eve', 'carol', 'alice', 'dave', 'bob'] os = OrderedSet(names) assert list(os) == names
def test_index_error(): os = OrderedSet(range(10)) os.index(10)
def _greedy_search(self, generate_next_keywords, search_type): claims = self._db.get_claims() min_tweets = self._min_tweet_count # word_tf_idf_dict = self._keywords_generator.get_word_tf_idf_of_claims() for i, claim in enumerate(claims): keywords_tweet_num = defaultdict(int) if i < self._start_from_claim: continue walked_keywords = Counter() print('{} Claim {}/{}'.format(search_type, i, len(claims))) start = timeit.default_timer() claim_description_words = self.get_claim_words_from_description( claim) ordered_words = OrderedSet(claim_description_words) base_keywords = ordered_words if search_type == 'top_down' else set( ) # num_of_potential_words = len(ordered_words) num_of_potential_words = self._max_keywords_size keywords_list = [] for size in range(1, num_of_potential_words + 1): same_keywords_size = Counter() best_word_rank_tuple = ['', 1000] for iter, word in enumerate(ordered_words): keywords_str = ' '.join( generate_next_keywords(base_keywords, word)) keywords_size = len(keywords_str.split(' ')) type_name = '{}_iter_{}_keywords_size_{}'.format( search_type, iter, keywords_size) evaluation = self.eval_keywords_for_claim( claim, keywords_str, type_name) if best_word_rank_tuple[1] > evaluation[ 'distance'] and evaluation[ 'tweet_num'] > min_tweets: best_word_rank_tuple = [word, evaluation['distance']] print('\r{} Distance: {}'.format(type_name, evaluation['distance']), end='') keywords_tweet_num[keywords_str] = evaluation['tweet_num'] if evaluation['tweet_num'] > min_tweets: walked_keywords[ keywords_str] = -1.0 * evaluation['distance'] same_keywords_size[ keywords_str] = -1.0 * evaluation['distance'] keywords_list.append( [keywords_str, evaluation['distance'], type_name]) if ordered_words: if best_word_rank_tuple[0] == '': best_word_rank_tuple[0] = ordered_words.pop() else: ordered_words.discard(best_word_rank_tuple[0]) base_keywords = generate_next_keywords(base_keywords, best_word_rank_tuple[0]) curr_distance = best_word_rank_tuple[1] if len(same_keywords_size) > 0: keywords, best_distances = same_keywords_size.most_common( 1)[0] self._add_new_keywords( claim, keywords, '{}_keywords_size_{}'.format(search_type, size), -1.0 * best_distances, keywords_tweet_num[keywords]) else: self._add_new_keywords( claim, ' '.join(base_keywords), '{}_keywords_size_{}'.format(search_type, size), curr_distance, keywords_tweet_num[' '.join(base_keywords)]) for keywords, keywords_distance, type_name in keywords_list: self._add_new_keywords(claim, keywords, type_name, keywords_distance, keywords_tweet_num[keywords]) if len(walked_keywords) > 0: keywords, best_distances = list( zip(*(walked_keywords.most_common( self._output_keywords_count)))) else: sorted_by_second = sorted(keywords_list, key=lambda tup: tup[1], reverse=True) keywords, best_distances, type_name = list( zip(*sorted_by_second[:self._output_keywords_count])) self._add_new_keywords( claim, '||'.join(keywords), '{}_final'.format(search_type), -1.0 * np.mean(best_distances), sum(list(keywords_tweet_num[k] for k in keywords))) with self._db.session.no_autoflush: self._db.addPosts(self._keywords_connections) self._keywords_connections = [] end = timeit.default_timer() print('run time: {}'.format((end - start)))
def test_discard(): os = OrderedSet(range(100)) for value in range(200): os.discard(value) assert len(os) == 0
def find_bad_exif(do_move=True, check_date_additional=False, folder: str = r""): """ find files with missing exif data """ log_function_call(find_bad_exif.__name__, do_move) clock = Clock() inpath = os.getcwd() lines_no_tags = OrderedSet() lines_bad_date_additional = OrderedSet() lines_date_missing = OrderedSet() out_filename_no_tags = get_info_dir("no_tags.csv") file_no_tags, writer_no_tags = fileop.create_csv_writer( out_filename_no_tags, ["directory", "name_part"]) out_filename_bad_date_additional = get_info_dir("bad_date_additional.csv") file_bad_date_additional, writer_bad_date_additional = fileop.create_csv_writer( out_filename_bad_date_additional, ["directory", "name_part"]) out_filename_date_missing = get_info_dir("date_missing.csv") file_date_missing, writer_date_missing = fileop.create_csv_writer( out_filename_date_missing, ["directory", "name_part"]) for (dirpath, dirnames, filenames) in os.walk(inpath): if is_invalid_path(dirpath, regex=folder): continue if fileop.count_files(filenames, settings.image_types) == 0: continue Tagdict = read_exiftags(dirpath, settings.image_types, ask=False) if len(list(Tagdict.values())) == 0: continue leng = len(list(Tagdict.values())[0]) for i in range(leng): if (not "Keywords" in Tagdict or not Tagdict["Keywords"][i]) or \ (not "Subject" in Tagdict or not Tagdict["Subject"][i]) or \ (not "Description" in Tagdict or not Tagdict["Description"][i]) or \ (not "User Comment" in Tagdict or not Tagdict["User Comment"][i]): lines_no_tags.add((os.path.basename(dirpath), _remove_counter(Tagdict["File Name"][i]))) if do_move and not "bad_exif" in dirpath: move( Tagdict["File Name"][i], dirpath, dirpath.replace( inpath, os.path.join(inpath, "bad_exif_keywords"))) if not "Date/Time Original" in Tagdict or not Tagdict[ "Date/Time Original"][i]: lines_date_missing.add( (os.path.basename(dirpath), _remove_counter(Tagdict["File Name"][i]))) if do_move and not "bad_exif" in dirpath: move( Tagdict["File Name"][i], dirpath, dirpath.replace( inpath, os.path.join(inpath, "bad_exif_date_missing"))) if check_date_additional and \ (("Date Created" in Tagdict and Tagdict["Date Created"][i]) or ("Time Created" in Tagdict and Tagdict["Time Created"][i]) or ("Create Date" in Tagdict and Tagdict["Create Date"][i]) or ("Modify Date" in Tagdict and Tagdict["Modify Date"][i]) or ("Digital Creation Date" in Tagdict and Tagdict["Digital Creation Date"][i])): lines_bad_date_additional.add( (os.path.basename(dirpath), _remove_counter(Tagdict["File Name"][i]))) if do_move and not "bad_exif" in dirpath: move( Tagdict["File Name"][i], dirpath, dirpath.replace( inpath, os.path.join(inpath, "bad_exif_date_additional"))) writer_no_tags.writerows(lines_no_tags) writer_bad_date_additional.writerows(lines_bad_date_additional) writer_date_missing.writerows(lines_date_missing) file_no_tags.close() file_bad_date_additional.close() file_date_missing.close() clock.finish()
def test_index_error(): os = OrderedSet(range(10)) with pytest.raises(ValueError): os.index(10)
class BSController(object): # actions RHYTHM_SELECTION = "<Rhythm-Selection>" RHYTHM_PLAYBACK_START = "<RhythmPlayback-Start>" RHYTHM_PLAYBACK_STOP = "<RhythmPlayback-Stop>" CORPUS_LOADED = "<Corpus-Loaded>" DISTANCES_TO_TARGET_UPDATED = "<TargetDistances-Updated>" TARGET_RHYTHM_SET = "<TargetRhythm-Set>" DISTANCE_MEASURE_SET = "<DistanceMeasure-Set>" RHYTHM_LOADER_REGISTERED = "<RhythmLoader-Registered>" # description of data returned by get_rhythm_data RHYTHM_DATA_TYPES = OrderedDict( (("I", int), ("Distance to target", float), ("Name", str), ("BPM", float), ("Timesignature", str), ("Track count", int), ("Measure count", int))) @staticmethod def get_rhythm_data_attr_names(): structure = BSController.RHYTHM_DATA_TYPES return tuple(structure.keys()) # TODO Avoid re-allocation @staticmethod def get_rhythm_data_attr_types(): structure = BSController.RHYTHM_DATA_TYPES return tuple(structure.values()) # TODO Avoid re-allocation def __init__(self, distance_measure: MonophonicRhythmDistanceMeasure = HammingDistanceMeasure, rhythm_player: tp.Union[BSRhythmPlayer, tp.Type[BSRhythmPlayer], None] = None): self._config = BSConfig() self._corpus = None # type: MidiRhythmCorpus self._corpus_resolution = -1 self._distances_to_target = np.empty(0) self._distances_to_target_rhythm_are_stale = False self._rhythm_measure = SummedMonophonicRhythmDistance( ) # type: SummedMonophonicRhythmDistance self._rhythm_selection = OrderedSet() self._target_rhythm = None # type: tp.Union[MidiRhythm, None] self._target_rhythm_prev_update = None self._lock = threading.Lock() self._rhythm_player = None self._callbacks = dict((action, OrderedSet()) for action in [ BSController.RHYTHM_SELECTION, BSController.CORPUS_LOADED, BSController.DISTANCES_TO_TARGET_UPDATED, BSController. RHYTHM_PLAYBACK_START, BSController.RHYTHM_PLAYBACK_STOP, BSController.TARGET_RHYTHM_SET, BSController.DISTANCE_MEASURE_SET, BSController.RHYTHM_LOADER_REGISTERED ]) self._rhythm_loaders = OrderedDict( ) # rhythm loaders by loader source name self.set_rhythm_player(rhythm_player) self.set_distance_measure(distance_measure) # automatically register a loader for the currently selected rhythm self.register_rhythm_loader(BSSelectedMidiRhythmLoader(self)) # setup config change handlers self._setup_config() # if a midi root directory is set, load the corpus if self.get_config().midi_root_directory.get(): self.load_corpus() def load_corpus(self): """Loads the rhythm corpus based on the settings in the current configuration Loads the rhythm corpus based on the current state of the BSConfig object. This method may be called multiple times throughout the lifespan of the app, e.g. after changing the rhythm resolution or the midi root directory in the BSConfig object. :return: True if load was successful; otherwise False """ prev_corpus = self._corpus prev_corpus_id = prev_corpus.id if prev_corpus else None corpus = get_rhythm_corpus(self._config) if not corpus: return False with self._lock: self._corpus = corpus if corpus.id != prev_corpus_id: self._reset_distances_to_target_rhythm() if corpus.id != prev_corpus_id: self.clear_rhythm_selection() self._dispatch(self.CORPUS_LOADED) return True def is_corpus_loaded(self): """Returns whether the rhythm corpus has loaded :return: True if the rhythm corpus has loaded; False otherwise """ return self._corpus is not None def get_corpus_id(self): """Returns the id of the current rhythm corpus or None if no corpus :return: id of current rhythm corpus """ return self._corpus.id if self.is_corpus_loaded() else None def get_corpus_rootdir(self): """Returns the root directory of the rhythm corpus Returns the path to the root directory of the current rhythm corpus. Returns an empty string if no corpus has been set. :return: root directory of current rhythm corpus or empty string """ return self._config.midi_root_directory.get() def get_corpus_rootdir_name(self): """Returns the name of the current rhythm corpus' root directory Returns the directory name of the current rhythm corpus' root directory. Returns an empty string if no corpus has been set. :return: root directory name of current rhythm corpus or empty string """ rootdir = self.get_corpus_rootdir() try: return os.path.split(rootdir)[-1] except IndexError: return "" def get_rhythm_count(self): """ Returns the number of rhythms in the current rhythm corpus. :return: number of rhythms in current corpus or 0 if no corpus set """ try: return len(self._corpus) except TypeError: return 0 def get_rhythm_by_index(self, rhythm_ix): """ Returns a rhythm by its index in the current corpus. :param rhythm_ix: rhythm index in current corpus :return: the Rhythm object on the given index in the corpus """ self._precondition_check_corpus_set() self._precondition_check_rhythm_index(rhythm_ix) return self._corpus[rhythm_ix] def set_target_rhythm( self, target_rhythm): # type: (tp.Union[RhythmLoop, None]) -> None """ Sets the target rhythm. :param target_rhythm: target rhythm or None """ if target_rhythm == self._target_rhythm or target_rhythm is None: self._target_rhythm = target_rhythm return if not isinstance(target_rhythm, RhythmLoop): raise TypeError("Expected a RhythmLoop but got \"%s\"" % str(target_rhythm)) self._target_rhythm = target_rhythm self._distances_to_target_rhythm_are_stale = True self._dispatch(self.TARGET_RHYTHM_SET) def get_target_rhythm(self): """ Returns the current target rhythm or None if there is no target rhythm set. :return: target rhythm """ return self._target_rhythm def is_target_rhythm_set(self): """ Returns whether or not a target rhythm has been set. :return: True if target rhythm has been set; False otherwise """ return self._target_rhythm is not None def get_rhythm_data(self): """ Returns an iterator yielding rhythm information on each iteration, such as the distance to the target rhythm, the name of the rhythm or the tempo. This data is yielded as a tuple. The type and names of the attributes in these tuples can be found in the ordered dictionary BSController.RHYTHM_DATA_TYPES, which gives name and type information for the elements in the tuple. Warning: This method locks this class. The lock will be released only when iteration is done or when the generator some time when the generator is destroyed. :return: iteration yielding rhythm info """ if not self._corpus: return with self._lock: for i, rhythm in enumerate(self._corpus): d_to_target = self._distances_to_target[i] yield (i, "NaN" if d_to_target == float("inf") else d_to_target, rhythm.name, rhythm.bpm, str(rhythm.time_signature), rhythm.get_track_count(), int(rhythm.get_duration_in_measures())) def set_rhythm_player( self, rhythm_player): # type: (tp.Union[BSRhythmPlayer, None]) -> None """ Sets the rhythm player. :param rhythm_player: rhythm player or None :return: """ if self.is_rhythm_player_set(): self.stop_rhythm_playback() self._rhythm_player.on_playback_ended = no_callback if rhythm_player is None: self._rhythm_player = None return rhythm_player = type_check_and_instantiate_if_necessary( rhythm_player, BSRhythmPlayer, allow_none=True) rhythm_player.on_playback_ended = lambda *args: self._dispatch( self.RHYTHM_PLAYBACK_STOP) self._rhythm_player = rhythm_player def is_rhythm_player_set(self): # type: () -> bool """ Returns whether or not a rhythm player has been set. :return: True if a rhythm player has been set; False otherwise. """ return self._rhythm_player is not None def is_rhythm_player_playing(self): """ Returns whether or not the rhythm player is currently playing. Returns False if no rhythm player has been set. :return: True if rhythm player is playing; False otherwise """ try: return self._rhythm_player.is_playing() except AttributeError: return False def playback_selected_rhythms(self): """ Starts playing the currently selected rhythms. The rhythm player will be used for rhythm playback. :return: None """ self._precondition_check_corpus_set() self._precondition_check_rhythm_player_set() selected_rhythm_indices = self.get_rhythm_selection() rhythms = [ self.get_rhythm_by_index(i) for i in selected_rhythm_indices ] if rhythms: self._rhythm_player.playback_rhythms(rhythms) self._dispatch(BSController.RHYTHM_PLAYBACK_START) def stop_rhythm_playback(self): """ Stops rhythm playback. :return: None """ self._precondition_check_rhythm_player_set() self._rhythm_player.stop_playback() self._dispatch(BSController.RHYTHM_PLAYBACK_STOP) def set_distance_measure(self, track_distance_measure): """ Sets the measure use to get the distance between individual rhythm tracks. :param track_distance_measure: the distance measure, one of: MonophonicRhythmDistanceMeasure - the track distance measure itself Type[MonophonicRhythmDistanceMeasure] - the track distance class, must be a subclass of MonophonicRhythmDistanceMeasure str - the name of the MonophonicRhythmDistanceMeasure class (cls.__friendly_name__) """ # type: (tp.Union[str, MonophonicRhythmDistanceMeasure, tp.Type[MonophonicRhythmDistanceMeasure]]) -> None if isinstance(track_distance_measure, str): try: track_distance_measure = MonophonicRhythmDistanceMeasure.get_measure_by_name( track_distance_measure) except KeyError: raise ValueError("Unknown distance measure: \"%s\"" % str(track_distance_measure)) self._rhythm_measure.monophonic_measure = track_distance_measure self._distances_to_target_rhythm_are_stale = True self._dispatch(self.DISTANCE_MEASURE_SET) def get_config(self) -> BSConfig: return self._config def is_current_distance_measure_quantizable(self): return isinstance(self._rhythm_measure.monophonic_measure, QuantizableMixin) def set_measure_quantization_unit(self, unit): """ Sets the quantization for the distance measures. :param unit: quantization unit :return: None """ if not self.is_current_distance_measure_quantizable(): return False track_distance_measure = self._rhythm_measure.monophonic_measure track_distance_measure.unit = unit track_distance_measure.quantize_enabled = True self._distances_to_target_rhythm_are_stale = True return True def set_tracks_to_compare(self, tracks): """ Sets the tracks to compare. :param tracks: tracks to compare as a list or one of the wildcards ['*', 'a*', 'b*']. See rhythm_pair_track_iterator for further info. """ self._rhythm_measure.tracks = tracks self._distances_to_target_rhythm_are_stale = True def calculate_distances_to_target_rhythm(self): """ Calculates and updates the distances between the rhythms in the corpus and the current target rhythm. This function call will be ignored if the distances are already up to date. The next call to get_rhythm_data will yield the updated distances. """ self._precondition_check_corpus_set() self._precondition_check_target_rhythm_set() measure = self._rhythm_measure target_rhythm = self._target_rhythm if target_rhythm == self._target_rhythm_prev_update: self._distances_to_target_rhythm_are_stale = True # nothing to update if not self._distances_to_target_rhythm_are_stale: return if target_rhythm is None: self._reset_distances_to_target_rhythm() # set distance to NaN self._target_rhythm_prev_update = None return with self._lock: for i, scanned_rhythm in enumerate(self._corpus): distance = measure.get_distance(target_rhythm, scanned_rhythm) self._distances_to_target[i] = distance self._distances_to_target_rhythm_are_stale = False self._target_rhythm_prev_update = target_rhythm self._dispatch(self.DISTANCES_TO_TARGET_UPDATED) def set_rhythm_selection(self, selected_rhythms): """ Sets the rhythm selection. :param selected_rhythms: the indices in the current corpus of the rhythms to select """ if self.is_rhythm_player_set() and self.is_rhythm_player_playing(): self.stop_rhythm_playback() self._rhythm_selection.clear() for rhythm_ix in selected_rhythms: self._precondition_check_rhythm_index(rhythm_ix) self._rhythm_selection.add(rhythm_ix) self._dispatch(BSController.RHYTHM_SELECTION) def clear_rhythm_selection(self): """ Clears the rhythm selection. """ self.set_rhythm_selection([]) def get_rhythm_selection(self): """ Returns the corpus indices of the currently selected rhythms. :return: tuple containing the corpus indices of the currently selected rhythms """ return tuple(self._rhythm_selection) def register_rhythm_loader(self, loader: BSMidiRhythmLoader) -> None: """ Register a new rhythm loader. :param loader: rhythm loader :return: None """ loader_class = loader.__class__ if loader_class in self._rhythm_loaders: raise ValueError( "Already registered a rhythm loader for loader type: \"%s\"" % loader_class) config = self._config loader.bind_to_config(config) self._rhythm_loaders[loader.__class__] = loader self._dispatch(self.RHYTHM_LOADER_REGISTERED, loader) def get_rhythm_loader_source_names(self): """ Returns a tuple with the rhythm source names of the currently registered rhythm loaders. See the BSMidiRhythmLoader.source_name property. :return: tuple containing the source names of the currently registered rhythm loaders """ return tuple(loader.get_source_name() for loader in self._rhythm_loaders.values()) def get_rhythm_loader_count(self): """ Returns the number of currently registered rhythm loaders. :return: number of currently registered rhythm loaders """ return len(self._rhythm_loaders) def get_rhythm_loader_iterator( self ) -> (tp.Iterator[tp.Tuple[tp.Type[BSMidiRhythmLoader], BSMidiRhythmLoader]]): """ Returns an iterator over the currently registered rhythm loaders. :return: iterator over the currently registered rhythm loaders """ return iter(self._rhythm_loaders.items()) def get_rhythm_loader(self, loader_type: tp.Type[BSMidiRhythmLoader] ): # type: (str) -> BSMidiRhythmLoader """ Returns the rhythm loader, given the loader type. :param loader_type: source name :return: loader """ try: loader = self._rhythm_loaders[loader_type] except KeyError: raise ValueError("No rhythm loader registered of type \"%s\"" % loader_type) return loader def export_single_rhythm_as_midi(self, fpath: str, rhythm_ix: int): """ Exports a single rhythm as a MIDI file, given its rhythm index. :param fpath: path to export the MIDI file to (including the .mid extension) :param rhythm_ix: rhythm index :return: None """ rhythm = self.get_rhythm_by_index(rhythm_ix) midi_pattern = rhythm.as_midi_pattern() LOGGER.info("Saving rhythm to: %s" % fpath) midi.write_midifile(fpath, midi_pattern) def export_rhythms_as_midi(self, export_directory: str, rhythms: tp.Union[tp.Sequence[int], str]): """ Exports rhythms as MIDI files to the given export directory. The rhythms can be given in two ways: * as a sequence of rhythm indices * as either "all" for all rhythms in the corpus or "selection" to export the currently selected rhythms :param export_directory: the midi files will be saved in this directory :param rhythms: either a sequence of rhythm indices or "all" or "selection" :return: None """ if isinstance(rhythms, str): if rhythms == "all": n_rhythms = self.get_rhythm_count() rhythm_indices = range(n_rhythms) elif rhythms == "selection": rhythm_indices = self.get_rhythm_selection() else: raise ValueError( "Unknown rhythms wildcard '%s', please choose between ['all', 'selection'] " "or provide a sequence with rhythm indices" % rhythms) else: rhythm_indices = rhythms for rhythm_ix in rhythm_indices: rhythm = self.get_rhythm_by_index(rhythm_ix) fpath = os.path.join(export_directory, "%s.mid" % rhythm.get_name()) self.export_single_rhythm_as_midi(fpath, rhythm_ix) def bind(self, action, callback): """ Adds a callback to the given action. Callback order is preserved. When the given callback is already bound to the given action, the callback will be moved to the end of the callback chain. :param action: action :param callback: callable that will be called when the given action occurs """ if not callable(callback): raise TypeError("Expected a callable") self._precondition_check_action(action) action_callbacks = self._callbacks[action] if callback in action_callbacks: action_callbacks.remove(callback) self._callbacks[action].add(callback) def unbind(self, action, callback): """ Removes a callback from the given action. :param action: action :param callback: callable :return: True if the action was removed successfully or False if the callback was never added to the given action """ self._precondition_check_action(action) action_callbacks = self._callbacks[action] if callback not in action_callbacks: return False self._callbacks[action].remove(callback) return True def _reset_distances_to_target_rhythm( self): # Note: the caller should acquire the lock n_rhythms = self.get_rhythm_count() if len(self._distances_to_target) == n_rhythms: self._distances_to_target.fill(np.inf) else: self._distances_to_target = np.full(n_rhythms, np.inf) def _dispatch(self, action, *args, **kwargs): self._precondition_check_action(action) for clb in self._callbacks[action]: clb(*args, **kwargs) def _on_rhythm_playback_ended(self): self._dispatch(BSController.RHYTHM_PLAYBACK_STOP) def _precondition_check_rhythm_index(self, rhythm_ix): n_rhythms = self.get_rhythm_count() if not (0 <= rhythm_ix < n_rhythms): raise IndexError("Expected rhythm index in range [0, %i]" % (n_rhythms - 1)) def _precondition_check_rhythm_player_set(self): if not self.is_rhythm_player_set(): raise Exception("Rhythm player not set") def _precondition_check_action(self, action): if action not in self._callbacks: raise ValueError("Unknown action \"%s\"" % action) def _precondition_check_corpus_set(self): if not self.is_corpus_loaded(): raise Exception("Corpus not loaded") def _precondition_check_target_rhythm_set(self): if not self.is_target_rhythm_set(): raise Exception("Target rhythm not set") def _setup_config(self): config = self._config config.mapping_reducer.add_binding(self._on_mapping_reducer_changed) config.rhythm_resolution.add_binding( self._on_rhythm_resolution_changed) def _on_mapping_reducer_changed(self, reducer: tp.Type[MidiDrumMappingReducer]): target_rhythm = self._target_rhythm if not target_rhythm: return target_rhythm.set_midi_drum_mapping_reducer(reducer) def _on_rhythm_resolution_changed(self, resolution: int): target_rhythm = self._target_rhythm if not target_rhythm: return target_rhythm.set_resolution(resolution)
def test_init(): os = OrderedSet() assert len(os) == 0