def gimme_loop_handler(cls, addr, tags, data, source): # time in seconds; key between 0 (for C) and 11 (for B) pd_looper_id, required_tempo, required_key = data[0], data[1], data[2] # If the track hasn't been registered yet, we do that pd_looper_infos = cls.pd_loopers.setdefault(pd_looper_id, { 'current_loop_id': None, 'track_ids': [], 'tempo': None, }) current_loop_id = pd_looper_infos['current_loop_id'] # Picking the new loop in the pool with cls.pool_lock: # prefilter available loops (those whose track haven't been picked already) available_loops = cls.pool.values() forbidden_tracks = cls.forbidden_tracks(pd_looper_id) available_loops = filter(lambda l: l['track_id'] not in forbidden_tracks, available_loops) # Select the most suitable next loop according to the current loop. if current_loop_id is not None: def sort_key(l): timbre_dist = loop_distance(cls.old_loops[current_loop_id], l) tempo_dist = abs(1 - l['tempo'] / float(required_tempo)) * 40 return timbre_dist + tempo_dist loop_infos = sorted(available_loops, key=lambda l: sort_key)[0] else: loop_infos = available_loops[0] # Remove the loop from the pool, reserving the loop's track for this looper cls.pool.pop(loop_infos['loop_id']) pd_looper_infos['track_ids'].append(loop_infos['track_id']) pd_looper_infos['current_loop_id'] = loop_infos['loop_id'] # Adding the picked looped to `old_loops`, so that we remember it # but it cannot be used again. with cls.old_loops_lock: cls.old_loops[loop_infos['loop_id']] = loop_infos if current_loop_id is not None: cls.old_loops.pop(current_loop_id) # Preparing the loop logger.info('sending new loop %s to looper %s, left : %s' % (loop_infos['path'], pd_looper_id, len(cls.pool))) loop = Sound.from_file(loop_infos['path']) required_length = loop.length * float(required_tempo) / loop_infos['tempo'] beat_length = 60.0 / required_tempo required_length = round(required_length / beat_length) * beat_length loop = loop.time_stretch(required_length).fade(in_dur=0.002, out_dur=0.002) loop.to_file(loop_infos['path']) # Sending loop, and fill-up the pool if necessary. send_msg('/new_loop', pd_looper_id, loop_infos['path'], int(round(loop.length * 1000)), loop_infos['loop_id']) if len(LoopScraper.pool) < LoopScraper.pool_min_size: LoopScraper.wake_up_scrapers()
def scrape(self): id_counter = 0 track_id, filename, sound_length = get_sound() # Check if the sound is long enough, and if yes we extract some loops from it. # TODO: make this less restrictive to waste a bit less if sound_length > 2 * self.sample_length: offset = 0 upper_limit = sound_length - 2 * self.sample_length while (offset + 2 * self.sample_length < upper_limit): # Calculate a random offset where the loop will start offset = random.randint(offset, int(min(offset + sound_length * 0.2, upper_limit))) # Extracting a loop and saving it to 'loop<n>.wav' sample = Sound.from_file(filename, start=offset, end=offset+self.sample_length) loops = sample.extract_loops() for loop in loops: if id_counter > 10: offset = upper_limit break id_counter += 1 loop_id = '%s_%s' % (track_id, id_counter) loop_path = self._get_free_path() logger.info('loop extracted to %s' % loop_path) loop.to_file(loop_path) #key, key_confidence, length = loop.echonest.key, loop.echonest.key_confidence, loop.length with self.pool_lock: self.pool[loop_id] = dict(loop.loop_infos, **{ 'path': loop_path, 'length': loop.length, 'loop_id': loop_id, 'track_id': track_id, 'timbre_start': loop.loop_infos['timbre_start'], 'timbre_end': loop.loop_infos['timbre_end'] #'key': (key, key_confidence) }) # Increment values for next loop offset += self.sample_length # Delete the sounds to save memory # We also have to collect manually because of a "bug" in pandas: # https://github.com/pydata/pandas/issues/2659 if 'loop' in locals(): del loop del sample gc.collect()
def scrape(self): track_id, pad_path, pad_length = get_sound() new_pad_path = self._get_free_path() pad_id = '%s' % (track_id) sound = Sound.from_file(pad_path, end=min(30, pad_length)) sound = sound.remove_beats() sound.to_file(new_pad_path) logger.info('pad extracted to %s' % new_pad_path) '''pad.to_file(pad_path) # Delete the sound to save memory # We also have to collect manually because of a "bug" in pandas: # https://github.com/pydata/pandas/issues/2659 del pad gc.collect()''' with self.pool_lock: self.pool[pad_id] = { 'path': new_pad_path, 'pad_id': pad_id }