def mix(self, verbose=True): """ Mix all the patterns into a single result sample. """ if not self.patterns: if verbose: print("No patterns to mix, output is empty.") return Sample() total_seconds = 0.0 for p in self.patterns: bar = next(iter(p.values())) total_seconds += len(bar) * 60.0 / self.bpm / self.ticks if verbose: print("Mixing {:d} patterns...".format(len(self.patterns))) mixed = Sample().make_32bit() for index, timestamp, sample in self.mixed_samples(tracker=False): if verbose: print("\r{:3.0f} % ".format(timestamp / total_seconds * 100), end="") mixed.mix_at(timestamp, sample) # chop/extend to get to the precise total duration (in case of silence in the last bars etc) missing = total_seconds - mixed.duration if missing > 0: mixed.add_silence(missing) elif missing < 0: mixed.clip(0, total_seconds) if verbose: print("\rMix done.") return mixed
def mix_generator(self): """ Returns a generator that produces samples that are the chronological chunks of the final output mix. This avoids having to mix it into one big output mix sample. """ if not self.patterns: yield Sample() return total_seconds = 0.0 for p in self.patterns: bar = next(iter(p.values())) total_seconds += len(bar) * 60.0 / self.bpm / self.ticks mixed_duration = 0.0 samples = self.mixed_samples() # get the first sample index, previous_timestamp, sample = next(samples) mixed = Sample().make_32bit() mixed.mix_at(previous_timestamp, sample) # continue mixing the following samples for index, timestamp, sample in samples: trigger_duration = timestamp - previous_timestamp overflow = None if mixed.duration < trigger_duration: # fill with some silence to reach the next sample position mixed.add_silence(trigger_duration - mixed.duration) elif mixed.duration > trigger_duration: # chop off the sound that extends into the next sample position # keep this overflow and mix it later! overflow = mixed.split(trigger_duration) mixed_duration += mixed.duration yield mixed mixed = overflow if overflow else Sample().make_32bit() mixed.mix(sample) previous_timestamp = timestamp # output the last remaining sample and extend it to the end of the duration if needed timestamp = total_seconds trigger_duration = timestamp - previous_timestamp if mixed.duration < trigger_duration: mixed.add_silence(trigger_duration - mixed.duration) elif mixed.duration > trigger_duration: mixed.clip(0, trigger_duration) mixed_duration += mixed.duration yield mixed