def terminate(dur_intro, middle, dur_outro, duration, lgh): # merge intro if isinstance(middle[0], Playback): middle[0].start = 0 middle[0].duration += dur_intro start = [] else: start = [Playback(middle[0].track, 0, dur_intro)] # merge outro if isinstance(middle[-1], Playback): middle[-1].duration += dur_outro end = [] else: end = [ Playback(middle[-1].track, middle[-1].start + middle[-1].duration, dur_outro) ] # combine actions = start + middle + end if lgh == False: return actions excess = sum(inst.duration for inst in actions) - duration if excess == 0: return actions # trim the end with fadeout if actions[-1].duration <= FADE_OUT + excess: start = actions[-1].start dur = FADE_OUT actions.remove(actions[-1]) else: actions[-1].duration -= FADE_OUT + excess start = actions[-1].start + actions[-1].duration dur = FADE_OUT actions.append(Fadeout(middle[0].track, start, dur)) return actions
def do_work(track, options): verbose = bool(options.verbose) # This gets the swing factor swing = float(options.swing) if swing < -0.9: swing = -0.9 if swing > +0.9: swing = +0.9 # If there's no swing, return the original tune if swing == 0: return Playback(track, 0, track.analysis.duration) # This gets the beat and the where the beats strt beats = track.analysis.beats offset = int(beats[0].start * track.sampleRate) # compute rates rates = [] # This is where the magic happens: # For each beat, compute how much to stretch / compress each half of each beat for beat in beats[:-1]: # This adds swing: if 0 < swing: rate1 = 1+swing dur = beat.duration/2.0 stretch = dur * rate1 rate2 = (beat.duration-stretch)/dur # This removes swing else: rate1 = 1 / (1+abs(swing)) dur = (beat.duration/2.0) / rate1 stretch = dur * rate1 rate2 = (beat.duration-stretch)/(beat.duration-dur) # This builds the list of swing rates for each beat start1 = int(beat.start * track.sampleRate) start2 = int((beat.start+dur) * track.sampleRate) rates.append((start1-offset, rate1)) rates.append((start2-offset, rate2)) if verbose: args = (beats.index(beat), dur, beat.duration-dur, stretch, beat.duration-stretch) print "Beat %d — split [%.3f|%.3f] — stretch [%.3f|%.3f] seconds" % args # This gets all the audio, from the vecin = track.data[offset:int(beats[-1].start * track.sampleRate),:] # This block does the time stretching if verbose: print "\nTime stretching..." # Dirac is a timestretching tool that comes with remix. vecout = dirac.timeScale(vecin, rates, track.sampleRate, 0) # This builds the timestretched AudioData object ts = AudioData(ndarray=vecout, shape=vecout.shape, sampleRate=track.sampleRate, numChannels=vecout.shape[1], verbose=verbose) # Create playback objects (just a collection of audio) for the first and last beat pb1 = Playback(track, 0, beats[0].start) pb2 = Playback(track, beats[-1].start, track.analysis.duration-beats[-1].start) # Return the first beat, the timestreched beats, and the last beat return [pb1, ts, pb2]
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 make_crossfade(track1, track2, inter): markers1 = getattr(track1.analysis, track1.resampled['rate']) if len(markers1) < MIN_SEARCH: start1 = track1.resampled['cursor'] else: start1 = markers1[track1.resampled['index'] + track1.resampled['cursor']].start start2 = max((track2.analysis.duration - (inter + 2 * X_FADE)) / 2, 0) markers2 = getattr(track2.analysis, track2.resampled['rate']) if len(markers2) < MIN_SEARCH: track2.resampled['cursor'] = start2 + X_FADE + inter dur = min(track2.analysis.duration - 2 * X_FADE, inter) else: duration, track2.resampled['cursor'] = move_cursor( track2, start2 + X_FADE + inter, 0) dur = markers2[track2.resampled['index'] + track2.resampled['cursor']].start - X_FADE - start2 xf = Crossfade((track1, track2), (start1, start2), X_FADE) pb = Playback(track2, start2 + X_FADE, dur) return [xf, pb]
def make_jumps(path, track): actions = [] source = path[0][0] #pb = Playback(track, 0, 10) for p in path: try: if p[2]['target'] - p[2]['source'] == 1: raise target = p[0] if 0 < target - source: actions.append(Playback(track, source, target - source)) actions.append(Jump(track, p[0], p[1], p[2]['duration'])) source = p[1] except: target = p[1] if 0 < target - source: actions.append(Playback(track, source, target - source)) return actions
def one_loop(graph, track, mode='shortest'): jumps = get_jumps(graph, mode='backward') if len(jumps) == 0: return [] loop = None if mode == 'longest': loop = jumps[0] else: jumps.reverse() for jump in jumps: if jump[1] < jump[0]: loop = jump break if loop == None: return [] # Let's capture a bit of the attack OFFSET = 0.025 # 25 ms pb = Playback(track, loop[1] - OFFSET, loop[0] - loop[1]) jp = Jump(track, loop[0] - OFFSET, loop[1] - OFFSET, loop[2]['duration']) return [pb, jp]
def make_transition(track1, track2, inter, transition): # the minimal transition is 2 markers # the minimal inter is 0 sec markers1 = getattr(track1.analysis, track1.resampled['rate']) markers2 = getattr(track2.analysis, track2.resampled['rate']) if len(markers1) < MIN_SEARCH or len(markers2) < MIN_SEARCH: return make_crossfade(track1, track2, inter) # though the minimal transition is 2 markers, the alignment is on at least 3 seconds mat1 = get_mat_out(track1, max(transition, MIN_ALIGN_DURATION)) mat2 = get_mat_in(track2, max(transition, MIN_ALIGN_DURATION), inter) try: loc, n, rate1, rate2 = align(track1, track2, mat1, mat2) except: return make_crossfade(track1, track2, inter) if transition < MIN_ALIGN_DURATION: duration, cursor = move_cursor(track2, transition, loc) n = max(cursor - loc, MIN_MARKERS) xm = make_crossmatch(track1, track2, rate1, rate2, loc, n) # loc and n are both in terms of potentially upsampled data. # Divide by rate here to get end_crossmatch in terms of the original data. end_crossmatch = (loc + n) / rate2 if markers2[-1].start < markers2[end_crossmatch].start + inter + transition: inter = max(markers2[-1].start - transition, 0) # move_cursor sets the cursor properly for subsequent operations, and gives us duration. dur, track2.resampled['cursor'] = move_cursor(track2, inter, end_crossmatch) pb = Playback(track2, sum(xm.l2[-1]), dur) return [xm, pb]
def do_work(track, options): # This manages the various input options verbose = bool(options.verbose) low_tempo = float(options.low) high_tempo = float(options.high) rate_tempo = float(options.rate) rubato = float(options.rubato) tempo = float(options.tempo) # This set the tempo and applies acceleration or not if rate_tempo == 0: if tempo == 0: low_tempo = track.analysis.tempo['value'] high_tempo = low_tempo else: low_tempo = tempo high_tempo = tempo rates = [] count = min(max(0, int(options.offset)), 1) beats = track.analysis.beats offset = int(beats[0].start * track.sampleRate) # For every beat, we get a tempo, and apply a time stretch for beat in beats[:-1]: # Get a tempo for the beat target_tempo = select_tempo(beats.index(beat), len(beats), low_tempo, high_tempo, rate_tempo) # Calculate rates for time stretching each beat. # if count == 0: dur = beat.duration / 2.0 rate1 = 60.0 / (target_tempo * dur) stretch = dur * rate1 rate2 = rate1 + rubato elif count == 1: rate1 = 60.0 / (target_tempo * beat.duration) # Add a change of rate at a given time start1 = int(beat.start * track.sampleRate) rates.append((start1 - offset, rate1)) if count == 0: start2 = int((beat.start + dur) * track.sampleRate) rates.append((start2 - offset, rate2)) # This prints what's happening, if verbose mode is on. if verbose: if count == 0: args = (beats.index(beat), count, beat.duration, dur * rate1, dur * rate2, 60.0 / (dur * rate1), 60.0 / (dur * rate2)) print "Beat %d (%d) | stretch %.3f sec into [%.3f|%.3f] sec | tempo = [%d|%d] bpm" % args elif count == 1: args = (beats.index(beat), count, beat.duration, beat.duration * rate1, 60.0 / (beat.duration * rate1)) print "Beat %d (%d) | stretch %.3f sec into %.3f sec | tempo = %d bpm" % args count = (count + 1) % 2 # This gets the audio vecin = track.data[offset:int(beats[-1].start * track.sampleRate), :] # This does the time stretch if verbose: print "\nTime stretching..." # Dirac is a timestretching tool that comes with remix. vecout = dirac.timeScale(vecin, rates, track.sampleRate, 0) # This builds the timestretched AudioData object ts = AudioData(ndarray=vecout, shape=vecout.shape, sampleRate=track.sampleRate, numChannels=vecout.shape[1], verbose=verbose) # Create playback objects (just a collection of audio) for the first and last beat pb1 = Playback(track, 0, beats[0].start) pb2 = Playback(track, beats[-1].start, track.analysis.duration - beats[-1].start) # Return the first beat, the timestreched beats, and the last beat return [pb1, ts, pb2]