def align(track1, track2, mat1, mat2): """ Constrained search between a settled section and a new section. Outputs location in mat2 and the number of rows used in the transition. """ # Get the average marker duration. marker1 = average_duration(getattr(track1.analysis, track1.resampled['rate'])[track1.resampled['index']:track1.resampled['index']+rows(mat1)]) marker2 = average_duration(getattr(track2.analysis, track2.resampled['rate'])[track2.resampled['index']:track2.resampled['index']+rows(mat2)]) def get_adjustment(tr1, tr2): """Update tatum rate if necessary""" dist = np.log2(tr1 / tr2) if dist < -0.5: return (1, 2) elif dist > 0.5: return (2, 1) else: return (1, 1) rate1, rate2 = get_adjustment(marker1, marker2) if rate1 == 2: mat1 = upsample_matrix(mat1) if rate2 == 2: mat2 = upsample_matrix(mat2) # Update sizes. rows2 = rows(mat2) rows1 = min( rows(mat1), max(rows2 - MIN_SEARCH, MIN_MARKERS)) # at least the best of MIN_SEARCH choices # Search for minimum. def dist(i): return evaluate_distance(mat1[0:rows1,:], mat2[i:i+rows1,:]) min_loc = min(xrange(rows2 - rows1), key=dist) min_val = dist(min_loc) # Let's make sure track2 ends its transition on a regular tatum. if rate2 == 2 and (min_loc + rows1) & 1: rows1 -= 1 return min_loc, rows1, rate1, rate2
def timbre_whiten(mat): if rows(mat) < 2: return mat m = np.zeros((rows(mat), 12), dtype=np.float32) m[:, 0] = mat[:, 0] - np.mean(mat[:, 0], 0) m[:, 0] = m[:, 0] / np.std(m[:, 0], 0) m[:, 1:] = mat[:, 1:] - np.mean(mat[:, 1:].flatten(), 0) m[:, 1:] = m[:, 1:] / np.std(m[:, 1:].flatten(), 0) # use this! return m
def timbre_whiten(mat): if rows(mat) < 2: return mat m = np.zeros((rows(mat), 12), dtype=np.float32) m[:,0] = mat[:,0] - np.mean(mat[:,0],0) m[:,0] = m[:,0] / np.std(m[:,0],0) m[:,1:] = mat[:,1:] - np.mean(mat[:,1:].flatten(),0) m[:,1:] = m[:,1:] / np.std(m[:,1:].flatten(),0) # use this! return m
def get_paths_slow(matrix, size=MIN_RANGE): paths = [] for i in xrange(rows(matrix)-MIN_ALIGN+1): vector = np.zeros((rows(matrix)-MIN_ALIGN+1,), dtype=np.float32) for j in xrange(rows(matrix)-MIN_ALIGN+1): vector[j] = evaluate_distance(matrix[i:i+MIN_ALIGN,:], matrix[j:j+MIN_ALIGN,:]) paths.append(get_loop_points(vector, size)) return paths
def initialize(track, inter, transition): """find initial cursor location""" mat = track.resampled['matrix'] markers = getattr(track.analysis, track.resampled['rate']) try: # compute duration of matrix mat_dur = markers[track.resampled['index'] + rows( mat)].start - markers[track.resampled['index']].start start = (mat_dur - inter - transition - FADE_IN) / 2 dur = start + FADE_IN + inter # move cursor to transition marker duration, track.resampled['cursor'] = move_cursor(track, dur, 0) # work backwards to find the exact locations of initial fade in and playback sections fi = Fadein( track, markers[track.resampled['index'] + track.resampled['cursor']].start - inter - FADE_IN, FADE_IN) pb = Playback( track, markers[track.resampled['index'] + track.resampled['cursor']].start - inter, inter) except: track.resampled['cursor'] = FADE_IN + inter fi = Fadein(track, 0, FADE_IN) pb = Playback(track, FADE_IN, inter) return [fi, pb]
def get_mat_in(track, transition, inter): """ Find and output the search matrix to use in the next alignment. Assumes that track.resampled exists. """ # search from the start cursor = 0 track.resampled['cursor'] = cursor mat = track.resampled['matrix'] # compute search zone by anticipating what's playing after the transition marker_end = getattr(track.analysis, track.resampled['rate'])[track.resampled['index'] + rows(mat)].start marker_start = getattr( track.analysis, track.resampled['rate'])[track.resampled['index']].start search_dur = (marker_end - marker_start) - inter - 2 * transition if search_dur < 0: return mat[:MIN_MARKERS, :] # find what the location is in rows duration, cursor = move_cursor(track, search_dur, cursor) return mat[:cursor, :]
def move_cursor(track, duration, cursor, buf=MIN_MARKERS): dur = 0 while dur < duration and cursor < rows(track.resampled['matrix']) - buf: markers = getattr(track.analysis, track.resampled['rate']) dur += markers[track.resampled['index'] + cursor].duration cursor += 1 return dur, cursor
def analyze(track): timbre = resample_features(track, rate=RATE, feature='timbre') timbre['matrix'] = timbre_whiten(timbre['matrix']) pitch = resample_features(track, rate=RATE, feature='pitches') #NOTE why not whiten pitch matrix? # pick a tradeoff between speed and memory size if rows(timbre['matrix']) < MAX_SIZE: # faster but memory hungry. For euclidean distances only. t_paths = get_paths(timbre['matrix']) p_paths = get_paths(pitch['matrix']) else: # slower but memory efficient. Any distance possible. t_paths = get_paths_slow(timbre['matrix']) p_paths = get_paths_slow(pitch['matrix']) # intersection of top timbre and pitch results paths = path_intersect(t_paths, p_paths) markers = getattr(track.analysis, timbre['rate'])[timbre['index']:timbre['index']+len(paths)] graph = make_graph(paths, markers, timbre['matrix'], pitch['matrix']) #NOTE remove last node because empty? size = graph.number_of_nodes() graph.remove_node(size-1) return graph
def initialize(track, inter, transition): """find initial cursor location""" mat = track.resampled['matrix'] markers = getattr(track.analysis, track.resampled['rate']) try: # compute duration of matrix mat_dur = markers[track.resampled['index'] + rows(mat)].start - markers[track.resampled['index']].start start = (mat_dur - inter - transition - FADE_IN) / 2 dur = start + FADE_IN + inter # move cursor to transition marker duration, track.resampled['cursor'] = move_cursor(track, dur, 0) # work backwards to find the exact locations of initial fade in and playback sections fi = Fadein(track, markers[track.resampled['index'] + track.resampled['cursor']].start - inter - FADE_IN, FADE_IN) pb = Playback(track, markers[track.resampled['index'] + track.resampled['cursor']].start - inter, inter) except: track.resampled['cursor'] = FADE_IN + inter fi = Fadein(track, 0, FADE_IN) pb = Playback(track, FADE_IN, inter) return [fi, pb]
def get_mat_in(track, transition, inter): """ Find and output the search matrix to use in the next alignment. Assumes that track.resampled exists. """ # search from the start cursor = 0 track.resampled['cursor'] = cursor mat = track.resampled['matrix'] # compute search zone by anticipating what's playing after the transition marker_end = getattr(track.analysis, track.resampled['rate'])[track.resampled['index'] + rows(mat)].start marker_start = getattr(track.analysis, track.resampled['rate'])[track.resampled['index']].start search_dur = (marker_end - marker_start) - inter - 2 * transition if search_dur < 0: return mat[:MIN_MARKERS,:] # find what the location is in rows duration, cursor = move_cursor(track, search_dur, cursor) return mat[:cursor,:]
def get_paths(matrix, size=MIN_RANGE): mat = make_similarity_matrix(matrix, size=MIN_ALIGN) paths = [] for i in xrange(rows(mat)): paths.append(get_loop_points(mat[i,:], size)) return paths
def do_work(track, options): dur = float(options.duration) mlp = int(options.minimum) lgh = bool(options.length) inf = bool(options.infinite) pkl = bool(options.pickle) gml = bool(options.graph) plt = bool(options.plot) fce = bool(options.force) sho = bool(options.shortest) lon = bool(options.longest) vbs = bool(options.verbose) mp3 = track.filename try: if fce == True: raise graph = read_graph(mp3+".graph.gpkl") except: # compute resampled and normalized matrix timbre = resample_features(track, rate=RATE, feature='timbre') timbre['matrix'] = timbre_whiten(timbre['matrix']) pitch = resample_features(track, rate=RATE, feature='pitches') # pick a tradeoff between speed and memory size if rows(timbre['matrix']) < MAX_SIZE: # faster but memory hungry. For euclidean distances only. t_paths = get_paths(timbre['matrix']) p_paths = get_paths(pitch['matrix']) else: # slower but memory efficient. Any distance possible. t_paths = get_paths_slow(timbre['matrix']) p_paths = get_paths_slow(pitch['matrix']) # intersection of top timbre and pitch results paths = path_intersect(t_paths, p_paths) # TEMPORARY -- check that the data looks good if vbs == True: print_screen(paths) # make graph markers = getattr(track.analysis, timbre['rate'])[timbre['index']:timbre['index']+len(paths)] graph = make_graph(paths, markers) # remove smaller loops for quality results if 0 < mlp: remove_short_loops(graph, mlp) # plot graph if plt == True: save_plot(graph, mp3+".graph.png") # save graph if pkl == True: save_graph(graph, mp3+".graph.gpkl") if gml == True: save_graph(graph, mp3+".graph.gml") # single loops if sho == True: return one_loop(graph, track, mode='shortest') if lon == True: return one_loop(graph, track, mode='longest') # other infinite loops if inf == True: if vbs == True: print "\nInput Duration:", track.analysis.duration # get the optimal path for a given duration return infinite(graph, track, dur) dur_intro = min(graph.nodes()) dur_outro = track.analysis.duration - max(graph.nodes()) if vbs == True: print "Input Duration:", track.analysis.duration # get the optimal path for a given duration path = compute_path(graph, max(dur-dur_intro-dur_outro, 0)) # build actions middle = make_jumps(path, track) # complete list of actions actions = terminate(dur_intro, middle, dur_outro, dur, lgh) return actions