Exemplo n.º 1
0
def first_sound(samples,
                threshold=-120.0,
                periodsamps=256,
                hopratio=0.5,
                skip=0) -> int:
    """
    Find the first sample in samples whith a rms
    exceeding the given threshold

    Returns: time of the first sample holding sound or -1 if
             no sound found
    """
    threshold_amp = db2amp(threshold)
    hopsamples = int(periodsamps * hopratio)
    i = 0
    while True:
        i0 = skip + i * hopsamples
        i1 = i0 + periodsamps
        if i1 > len(samples):
            break
        chunk = samples[i0:i1]
        rms_now = rms(chunk)
        if rms_now > threshold_amp:
            return i0
    return -1
Exemplo n.º 2
0
 def atx(self, time: float, freqs: _S[float]) -> _S[float]:
     s = self.sp.partials_at(time)
     data = [(p.freq(time), p.amp(time)) for p in s]
     data.sort()
     out = []
     d = self.decay
     mindb = self.mindb
     for freq in freqs:
         idx1 = bisect.bisect(data, (freq, 0))
         if idx1 >= len(data):
             idx1 = idx0 = idx1 - 1
         idx0 = max(0, idx1 - 1)
         f0, a0 = data[idx0]
         f1, a1 = data[idx1]
         a0db = amp2db(a0)
         a1db = amp2db(a1)
         df0 = (a0db - mindb) / d
         df1 = (a1db - mindb) / d
         if f1 - df1 <= freq <= f0 + df0:
             db0 = _linlin(freq, f0, f0 + df0, a0db, -120)
             db1 = _linlin(freq, f1 - df1, f1, -120, a1db)
             db = max(db0, db1)
         elif freq <= f0 + df0:
             db = _linlin(freq, f0, f0 + df0, a0db, -120)
         elif f1 - df1 <= freq:
             db = _linlin(freq, f1 - df1, f1, -120, a1db)
         else:
             db = mindb
         out.append(db2amp(db))
     return out
     """
Exemplo n.º 3
0
    def nearestx(self,
                 time: float,
                 freqs: _S[float],
                 timemargin=0.01) -> _S[float]:
        """
        Return a list of (freq, amp), representing the 
        freq and amp of the nearest Partial for each given freq.

        Example:

        freqs = [100, 200]
        out = surface.nearest(0.5, freqs)
        for freq, row in zip(freq, out):
            print(f"Nearest partial from {freq}Hz @ 0.5s has a freq. of {row[0]}")

        """
        s = self.sp.partials_between(time - timemargin, time + timemargin)
        if not s:
            return [(0, 0)] * len(freqs)
        data = [(p.freq(time), p.amp(time)) for p in s]
        mindb = self.mindb
        decay = self.decay
        out = []
        for freq in freqs:
            nearest = min(data, key=lambda row: abs(row[0] - freq))
            f, a = nearest
            db = amp2db(a)
            diff = abs(f - freq)
            db2 = max(db - diff * decay, mindb)
            out.append((f, db2amp(db2)))
        return out
Exemplo n.º 4
0
        def loudest_task(self=self, minamp=db2amp(minamp_db)):
            pl = self._plot
            mousepos = self.ui.mousepos()
            time = mousepos.x()
            lasttime = self._state.get('synth_loudest.lasttime', -1)
            if abs(time - lasttime) < 0.001 or time <= 0:
                return
            best = loudest(time, maxpartials, minamp, self._filter_active,
                           *self._filter_range)
            if not best:
                return

            freqs = [row[1] for row in best]
            synth = self._chordsynth
            lastdraw = self._state.get('synth_loudest.lastdraw', 0)
            if abs(time - lastdraw) > 0.005:
                self._chordcursor.setData([time] * len(freqs), freqs)
                self._state['synth_loudest.lastdraw'] = time
            for i, row in enumerate(best):
                synth.setOsc(i, row[1], row[0], row[2])
            self._state['synth_loudest.lasttime'] = time

            self._chordcursortxt.setPos(time, freqs[-1])
            txt = "%.3f" % time
            self._chordcursortxt.setText(txt)
Exemplo n.º 5
0
    def normalize(self, headroom=0.) -> Sample:
        """Normalize in place, returns self

        headroom: maximum peak in dB
        """
        max_peak_possible = db2amp(headroom)
        peak = np.abs(self.samples).max()
        ratio = max_peak_possible / peak
        self.samples *= ratio
        return self
Exemplo n.º 6
0
def create_shape(shape='expon(3)',
                 mindb: U[int, float] = -90,
                 maxdb: U[int, float] = 0) -> _bpf.BpfInterface:
    """
    Return a bpf mapping 0-1 to amplitudes, as needed to be passed
    to DynamicsCurve

    Args:
        shape: a descriptor of the curve to use to map amplitude to dynamics
        mindb: the min. representable amplitude (in dB)
        maxdb: the max. representable amplitude (in dB)

    If X is dynamic and Y is amplitude, an exponential curve with exp > 1
    will allocate more dynamics to the soft amplitude range, resulting in more
    resolution for small amplitudes.
    A curve with exp < 1 will result in more resolution for high dynamics
    """
    minamp, maxamp = db2amp(mindb), db2amp(maxdb)
    return _bpf.util.makebpf(shape, [0, 1], [minamp, maxamp])
Exemplo n.º 7
0
def create_shape(shape='expon(3)', mindb=-90, maxdb=0) -> BpfInterface:
    """
    Return a bpf mapping 0-1 to amplitudes, as needed to be passed
    to DynamicsCurve

    * descr: a descriptor of the curve to use to map amplitude to dynamics
    * mindb, maxdb: the maximum and minimum representable amplitudes (in dB)
    * dynamics: - a list of dynamic-strings,
                - None to use the default (from pppp to ffff)
    
    If X is dynamic and Y is amplitude, an exponential curve with exp > 1
    will allocate more dynamics to the soft amplitude range, resulting in more
    resolution for small amplitudes.
    A curve with exp < 1 will result in more resolution for high dynamics

    import bpf4 as bpf
    shape = bpf.util.makebpf("expon(3)", [0, 1], [-90, 0]).db2amp()
    """
    minamp, maxamp = db2amp(mindb), db2amp(maxdb)
    return _bpf.util.makebpf(shape, [0, 1], [minamp, maxamp])
Exemplo n.º 8
0
    def _synth_nearest_partial(self,
                               state: bool,
                               margin_hz=50,
                               minamp_db=-60,
                               gain=2.0):
        currstate = self._state.get('synth_nearest', False)
        if state == currstate:
            return

        if not state:
            self._synthcursor.hide()
            self._synthcursortxt.hide()
            self._removetask("nearest")
            fadetime = 0.2
            self._sinesynth.fadeout(fadetime)

            def stop(synth=self._sinesynth, state=self._state):
                synth.stop()
                state['synth_nearest'] = False

            QtCore.QTimer.singleShot(fadetime * 1000 + 50, stop)
            return

        minamp = db2amp(minamp_db)

        def update(self=self):
            synth = self._sinesynth
            pl = self._plot
            mousepos = self.ui.mousepos()
            time = mousepos.x()
            mousefreq = mousepos.y()
            freq, amp = self._surface.nearest(time, mousefreq)
            if amp < minamp:
                return
            if abs(freq - mousefreq) > margin_hz:
                amp = 0
            if freq <= 0:
                return
            synth.setOsc(0, freq, amp)
            self._synthcursor.setData([time], [freq])
            self._synthcursortxt.setPos(time, freq)
            txt = "%d Hz  %s  %.3f" % (int(freq), f2n(freq), time)
            self._synthcursortxt.setText(txt)

        self._synthcursor.show()
        self._synthcursortxt.show()
        self._surface = SpectralSurface(self.spectrum, decay=0.01)
        self._sinesynth = SineSynth(freq=self.ui.mousepos().y(),
                                    amp=0,
                                    freqport=0.2,
                                    gain=gain)
        self._addtask("nearest", update)
        self._state['synth_nearest'] = True
Exemplo n.º 9
0
def first_silence(samples: np.ndarray,
                  threshold=-100,
                  period=256,
                  hopratio=0.5,
                  soundthreshold=-60,
                  startidx=0) -> int:
    """
    Return the sample where rms decays below threshold

    Args:
        samples: the samples data
        threshold: the threshold in dBs (rms)
        period: how many samples to use for rms calculation
        hopratio: how many samples to skip before taking the next
            measurement
        soundthreshold: the threshold to considere that the sound
            started (rms)
        startidx: the sample to start looking for silence (0 to start
            from beginning)

    Returns:
        the index where the first silence is found (-1 if no silence found)

    """
    soundstarted = False
    hopsamples = int(period * hopratio)
    thresholdamp = db2amp(threshold)
    soundthreshamp = db2amp(soundthreshold)
    lastrms = rms(samples[startidx:startidx + period])
    idx = hopsamples + startidx
    while idx < len(samples) - period:
        win = samples[idx:idx + period]
        rmsnow = rms(win)
        if rmsnow >= soundthreshamp:
            soundstarted = True
        elif rmsnow <= thresholdamp and lastrms > thresholdamp and soundstarted:
            return idx
        lastrms = rmsnow
        idx += hopsamples
    return -1
Exemplo n.º 10
0
 def nearest(self, time: float, freq: float, timemargin=0.01) -> float:
     s = self.sp.partials_between(time - timemargin, time + timemargin)
     if not s:
         return (0, 0)
     mindist = float("inf")
     for p in s:
         p_freq = p.freq(time)
         dist = abs(p_freq - freq)
         if dist < mindist:
             mindist = dist
             best_freq = p_freq
             best_partial = p
     best_amp = best_partial.amp(time)
     db = max(amp2db(best_amp) - mindist * self.decay, self.mindb)
     return best_freq, db2amp(db)
Exemplo n.º 11
0
    def _synth_nearest_partial(self, state:bool, margin_hz=50, minamp_db=-60, gain=2.0):
        currstate = self._state.get('synth_nearest', False)
        if state == currstate:
            return
        
        if not state:
            self._synthcursor.hide()
            self._synthcursortxt.hide()
            self._removetask("nearest")
            fadetime = 0.2
            self._sinesynth.fadeout(fadetime)

            def stop(synth=self._sinesynth, state=self._state):
                synth.stop()
                state['synth_nearest'] = False
            
            QtCore.QTimer.singleShot(fadetime * 1000 + 50, stop)
            return 

        minamp = db2amp(minamp_db)

        def update(self=self):
            synth = self._sinesynth
            pl = self._plot
            time = pl._mousex
            mousefreq = pl._mousey
            freq, amp = self._surface.nearest(time, mousefreq)
            if amp < minamp:
                return
            if abs(freq - mousefreq) > margin_hz:
                amp = 0
            if freq <= 0:
                return
            synth.setOsc(0, freq, amp)
            self._synthcursor.setData([time], [freq])
            self._synthcursortxt.setPos(time, freq)
            txt = "%d Hz  %s  %.3f" % (int(freq), f2n(freq), time)
            self._synthcursortxt.setText(txt)
            
        self._synthcursor.show()
        self._synthcursortxt.show()
        self._surface = SpectralSurface(self.spectrum, decay=0.01)
        self._sinesynth = SineSynth(freq=self._plot._mousey, amp=0, freqport=0.2, 
                                    gain=gain)
        self._addtask("nearest", update)   
        self._state['synth_nearest'] = True
Exemplo n.º 12
0
def make_weighter(mode='speech'):
    if mode == 'speech':
        freqcurve = bpf.linear(0, 0, 50, 0, 80, 1, 200, 1, 250, 2, 4500, 2, 6000, 1, 8000, 1, 10000, 0)
        durcurve = bpf.linear(0, 0, 0.01, 0, 0.02, 1)
        ampcurve = bpf.linear(0, 0, db2amp(-75), 0, db2amp(-65), 1)
    elif mode == 'transcription':
        freqcurve = bpf.linear(0, 0, 30, 0, 50, 1, 4500, 1, 5000, 0)
        durcurve = bpf.linear(0, 0, 0.01, 0, 0.02, 1)
        ampcurve = bpf.linear(0, 0, db2amp(-70), 0, db2amp(-60), 1)
    elif mode == 'default':
        freqcurve = bpf.linear(0, 0, 30, 0, 50, 1, 8000, 1, 12000, 0.5, 16000, 0)
        durcurve = bpf.linear(0, 0, 0.01, 0, 0.02, 1)
        ampcurve = bpf.linear(0, 0, db2amp(-80), 0, db2amp(-60), 1)
    elif mode == 'speech-transcription':
        freqcurve = bpf.linear(0, 0, 50, 0, 80, 1, 200, 1, 250, 2, 4500, 2, 5000, 0)
        durcurve = bpf.linear(0, 0, 0.01, 0, 0.02, 1)
        ampcurve = bpf.linear(0, 0, db2amp(-75), 0, db2amp(-65), 1)
    else:
        raise KeyError("mode unknown, must be one of 'speech', 'transcription'")
    return PartialWeighter(freqcurve=freqcurve, durcurve=durcurve, ampcurve=ampcurve, 
                           freqweight=1, durweight=1, ampweight=2)
Exemplo n.º 13
0
def contrast(spectrum: sp.Spectrum, mid:float, exp=1.0) -> sp.Spectrum:
    """
    Change the contrast of spectrum

    mid: a dB amplitude
    exp: 0 - no effect
         1 - full effect 
         > 1: possible, needs rescaling

    formula: B = A * (A/mid)**exp
    """
    assert mid <= 0
    mid = db2amp(mid)
    newpartials = []
    for partial in spectrum.partials:
        A = partial.amp.points()[1]
        B = A * (A/mid)**exp
        newpartial = partial.clone(amps=B)
        newpartials.append(newpartial)
    return spectrum.__class__(newpartials)
Exemplo n.º 14
0
def contrast(spectrum: sp.Spectrum, mid: float, exp=1.0) -> sp.Spectrum:
    """
    Change the contrast of spectrum

    mid: a dB amplitude
    exp: 0 - no effect
         1 - full effect 
         > 1: possible, needs rescaling

    formula: newamps = amps * (amps/mid)**exp
    """
    assert mid <= 0
    mid = db2amp(mid)
    newpartials = []
    for partial in spectrum.partials:
        A = partial.amps
        B = A * (A / mid)**exp
        newpartial = partial.clone(amps=B)
        newpartials.append(newpartial)
    return spectrum.__class__(newpartials)
Exemplo n.º 15
0
        def loudest_task(self=self, minamp=db2amp(minamp_db)):
            pl = self._plot
            time = pl._mousex
            lasttime = self._state.get('synth_loudest.lasttime', -1)
            if abs(time - lasttime) < 0.001 or time <= 0:
                return
            best = loudest(time, maxpartials, minamp, self._filter_active, *self._filter_range)
            if not best:
                return 
            
            freqs = [row[1] for row in best]
            synth = self._chordsynth
            lastdraw = self._state.get('synth_loudest.lastdraw', 0)
            if abs(time - lastdraw) > 0.005:
                self._chordcursor.setData([time]*len(freqs), freqs)
                self._state['synth_loudest.lastdraw'] = time
            for i, row in enumerate(best):
                synth.setOsc(i, row[1], row[0], row[2])
            self._state['synth_loudest.lasttime'] = time

            self._chordcursortxt.setPos(time, freqs[-1])
            txt = "%.3f" % time
            self._chordcursortxt.setText(txt)
Exemplo n.º 16
0
 def db2dyn(self, db: float) -> str:
     return self.amp2dyn(db2amp(db))
Exemplo n.º 17
0
def db2dyn(db: float, nearest=True) -> str:
    amp = db2amp(db)
    return _default.amp2dyn(amp, nearest)
Exemplo n.º 18
0
def scaleAmps(n, maxdb=-3):
    return 1 / sqrt(n) * db2amp(maxdb)