def test_get_rms_dB_all(self): dB = self._leveller.get_rms_dB() self.assertEquals(len(dB), 1) time, value_dB = dB[0] self.assertEquals(time, level.SECOND) value = common.decibelToRaw(value_dB) # ms is ((1/2) ** 2 + (1/4) ** 2) / 2 = 5/32 # to avoid float rounding errors, compare against 5/32 by multiplying # and inting self.assertEquals(int(value ** 2 * 32), 5)
def do(self, args): import pygst pygst.require('0.10') import gobject gobject.threads_init() import gst from dadgst.task import level runner = task.SyncRunner() for path in filterFiles(self, args): t = level.LevellerTask(path) runner.run(t) if t.done: self.stdout.write('Successfully analyzed file %s.\n' % path.encode('utf-8')) mixes = t.get_track_mixes() self.stdout.write('%d fragment(s)\n' % len(mixes)) for i, m in enumerate(mixes): self.stdout.write('- fragment %d: %s - %s\n' % ( i, gst.TIME_ARGS(m.start), gst.TIME_ARGS(m.end))) self.stdout.write(' - peak %02.3f dB (%03.3f %%)\n' % ( m.peak, common.decibelToRaw(m.peak) * 100.0)) self.stdout.write(' - rms %r dB\n' % m.rms) self.stdout.write(' - peak rms %r dB\n' % m.rmsPeak) self.stdout.write(' - 95 percentile rms %r dB\n' % m.rmsPercentile) self.stdout.write(' - weighted rms %r dB\n' % m.rmsWeighted) start = m.attack.get(m.rmsPeak - 9) end = m.decay.get(m.rmsPeak - 9) self.stdout.write(' - weighted from %s to %s\n' % ( gst.TIME_ARGS(start), gst.TIME_ARGS(end))) else: self.stderr.write('Could not level %s\n' % path.encode('utf-8')) t.clean()
def _scheduled_cb(self, scheduler, scheduled): self.info('jukebox: scheduled %r' % scheduled) self._scheduling = False self.debug('scheduled %r' % scheduled) audiosource, gnlsource = self._makeGnlSource(scheduled.path, scheduled.path, volume=common.decibelToRaw(scheduled.volume)) self._setGnlSourceProps(gnlsource, scheduled.start, scheduled.mediaStart, scheduled.duration) self.debug('adding %r' % gnlsource) self._composition.add(gnlsource) self._playing.append((scheduled, audiosource, gnlsource)) # add a buffer probe so we can signal started pad = audiosource.get_pad('src') self._probes[pad] = pad.add_buffer_probe(self._buffer_probe) # if this track gets scheduled before lastend, we need an adder if scheduled.start < self._lastend: start = scheduled.start duration = self._lastend - scheduled.start self.debug('schedule adder at %r for %r' % ( gst.TIME_ARGS(start), gst.TIME_ARGS(duration))) operation = gst.element_factory_make("gnloperation") adder = gst.element_factory_make("adder") operation.add(adder) #operation.props.sinks = 2 operation.props.sinks = 2 operation.props.start = start operation.props.duration = duration operation.props.priority = 0 self._composition.add(operation) # update lastend self._lastend = scheduled.start + scheduled.duration self.debug('jukebox: lastend updated to %r' % gst.TIME_ARGS(self._lastend))
def setup(self): EXTRA = 5 * gst.SECOND # how much of tracks to play outside of mix # set up the mix track1 = self._tracks[self._path1][0] if not track1.name: track1.name = self._path1 track2 = self._tracks[self._path2][0] if not track2.name: track2.name = self._path2 mix = mixing.Mix(track1, track2) print 'Track 1: %s' % self._path1 print '- from %s to %s' % ( gst.TIME_ARGS(track1.start), gst.TIME_ARGS(track1.end)) print '- leadout at %s for %s' % ( gst.TIME_ARGS(track1.end - mix.leadout), gst.TIME_ARGS(mix.leadout)) print 'Track 2: %s' % self._path2 print '- from %s to %s' % ( gst.TIME_ARGS(track2.start), gst.TIME_ARGS(track2.end)) print '- leadin until %s for %s' % ( gst.TIME_ARGS(track2.start + mix.leadin), gst.TIME_ARGS(mix.leadin)) print 'mix start: %s' % gst.TIME_ARGS(EXTRA) print 'mix duration: %s' % gst.TIME_ARGS(mix.duration) raw = common.decibelToRaw(mix.volume1) print 'Adjusting track 1 by %.3f dB' % mix.volume1 asource1, source1 = self._makeGnlSource( 'source1', self._path1, volume=raw) source1.props.start = 0 * gst.SECOND source1.props.duration = EXTRA + mix.duration source1.props.media_start = track1.end - (EXTRA + mix.duration) source1.props.media_duration = EXTRA + mix.duration source1.props.priority = 1 self._composition.add(source1) raw = common.decibelToRaw(mix.volume2) print 'Adjusting track 2 by %.3f dB' % mix.volume2 asource2, source2 = self._makeGnlSource( 'source2', self._path2, volume=raw) source2.props.start = EXTRA source2.props.duration = EXTRA + mix.duration source2.props.media_start = track2.start source2.props.media_duration = EXTRA + mix.duration source2.props.priority = 2 self._composition.add(source2) self._source1 = source1 self._asource1 = asource1 self._source2 = source2 self._asource2 = asource2 # add the mixer effect operation = gst.element_factory_make("gnloperation") adder = gst.element_factory_make("adder") operation.add(adder) operation.props.sinks = 2 operation.props.start = EXTRA operation.props.duration = mix.duration operation.props.priority = 0 self._composition.add(operation) # schedule a stop # FIXME: this should be done against the pipeline's clock instead; # imagine using a filesink gobject.timeout_add(((2 * EXTRA) + mix.duration) / 1000000, self.stop) source2.props.duration = EXTRA + mix.duration
# message, if set, holds a ref to leveller, so we delete it here # to assure cleanup of leveller when we del it del message utils.gc_collect('deleted message') l.stop() if success: print 'Successfully analyzed file %r' % path trackMixes = l.get_track_mixes() print '%d slice(s) found.' % len(trackMixes) for i, m in enumerate(trackMixes): print '- slice %d: %s - %s' % ( i, gst.TIME_ARGS(m.start), gst.TIME_ARGS(m.end)) print ' - peak %02.3f dB (%03.3f %%)' % ( m.peak, common.decibelToRaw(m.peak) * 100.0) print ' - rms %r dB' % m.rms print ' - peak rms %r dB' % m.rmsPeak print ' - 95 percentile rms %r dB' % m.rmsPercentile print ' - weighted rms %r dB' % m.rmsWeighted start = m.attack.get(m.rmsPeak - 9) end = m.decay.get(m.rmsPeak - 9) print ' - weighted from %s to %s' % ( gst.TIME_ARGS(start), gst.TIME_ARGS(end)) tracks[path] = trackMixes handle = open(args[0], 'wb') pickle.dump(tracks, handle, 2) handle.close() print
def slice(self, delta=5 * SECOND, threshold=None): """ Find all slices of the track that have their value over the given threshold for at least the given delta. If start or end point of a slice is within delta of beginning or end of the track, then it gets reset to beginning or end of track. @param threshold: the threshold for slice detection; should be in the same scale as the object. If not given, the equivalent of -90 dB is used. @rtype: list of L{Level} @returns: list of slices with their start and end point. """ ret = [] if threshold is None: threshold = -90 if self._scale == SCALE_RAW: threshold = common.decibelToRaw(threshold) previous = 0L # end of previous section length = 0L # length of current section start = 0L # start of slice end = 0L # end of slice # implement a state machine with 4 states BELOW, CLIMBING, ABOVE, FALLING = range(4) state = BELOW for endtime, value in self: length = endtime - previous previous = endtime if state is BELOW: if value < threshold: continue self._log('at %r value %r is above threshold %r' % ( endtime, value, threshold)) state = CLIMBING # start of interval is start of previous block ... start = endtime - length # ... but if it's close to 0, then pick 0 instead if start < delta: self._log('resetting start to 0') start = 0L self._log('set start to %r' % start) elif state is CLIMBING: if value < threshold: # fell again, reset self._log('at %r value %r is below threshold %r' % ( endtime, value, threshold)) self._log('fell again') state = BELOW continue if endtime - start > delta: # long enough, so approve state = ABOVE elif state is ABOVE: if value > threshold: continue self._log('at %r value %r is below threshold %r' % ( endtime, value, threshold)) state = FALLING # end time of slice is end time of this section ... end = endtime # ... but if we're close to the end, then pick the end last = self.end() if last - end < delta: self._log('resetting end to last %r' % last) end = last elif state is FALLING: if value > threshold: # rose again, reset state = ABOVE continue if endtime - end > delta: # long enough, so approve state = BELOW self._log('appending section from %r to %r' % (start, end)) ret.append(self.trim(start, end)) # make sure we finish even if there's not enough data left if state in (ABOVE, FALLING): self._log('appending section from %r to %r' % (start, endtime)) ret.append(self.trim(start, endtime)) return ret