def _note_deviates(b0, b1, b2, pitchdelta: float, dbdelta: float): """ True if b1 deviates from the interpolation of b0 and b2 dbdelta and pitchdelta can be negative, in which case they are not taken into account bx: a tuplet of (time, freq, amp, ...) """ t0 = b0[0] t = b1[0] t1 = b2[0] dt = (t - t0) / (t1 - t0) if dbdelta >= 0: a0 = b0[2] a = b1[2] a1 = b2[2] a_t = a0 + dt * (a1 - a0) if abs(amp2db(a) - amp2db(a_t)) >= dbdelta: return True if pitchdelta >= 0: f0 = b0[1] f = b1[1] f1 = b2[1] f_t = f0 + dt * (f1 - f0) if abs(f2m(f_t) - f2m(f)) >= pitchdelta: return True return False
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 """
def amp2dyn(self, amp: float, nearest=True) -> str: """ Convert amplitude to a string representation of its corresponding musical dynamic as defined in DYNAMIC_TABLE amp: an amplitude 0-1 nearest: if True, find the nearest dynamic (can round up), otherwise, the next lower dynamic """ amps = self._amps dyns = self.dynamics if amp < amps[0]: return dyns[0] if amp > amps[-1]: return dyns[-1] insert_point = _bisect(amps, amp) if not nearest: floor = max(0, amps[insert_point - 1]) return dyns[floor] if insert_point == 0: return dyns[0] if insert_point >= len(amps): return dyns[-1] assert 1 <= insert_point < len(amps) amp0, dyn0 = amps[insert_point - 1], dyns[insert_point - 1] amp1, dyn1 = amps[insert_point], dyns[insert_point] db = amp2db(amp) dyn = dyn0 if abs(db - amp2db(amp0)) < abs(db - amp2db(amp1)) else dyn1 assert isinstance(dyn, str) return dyn
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
def linearamp(amp:float, dbfloor:float=-100.0) -> float: """ Converts an amplitude to a value between 0-1 following the dB scale dbfloor: min. amplitude as db """ dbrange = abs(dbfloor) posdb = max(dbfloor, amp2db(amp)) + dbrange return posdb / dbrange
def _breakpoint_deviates(b0, b1, b2, dbdelta, pitchdelta, bwdelta): """ True if b1 deviates from the interpolation of b0 and b2 bx: a tuplet of (time, freq, amp, bw) """ t0, f0, a0, bw0 = b0 t, f, a, bw = b1 t1, f1, a1, bw1 = b2 a_t = interpol_linear(t, t0, a0, t1, a1) varamp = abs(amp2db(a) - amp2db(a_t)) if varamp >= dbdelta: return True f_t = interpol_linear(t, t0, f0, t1, f1) varpitch = abs(f2m(f_t) - f2m(f)) if varpitch >= pitchdelta: return True bw_t = interpol_linear(t, t0, bw0, t1, bw1) if abs(bw_t - bw) >= bwdelta: return True return False
def amp2dyn(self, amp: float, nearest=True) -> str: """ convert amplitude to a string representation of its corresponding musical dynamic as defined in DYNAMIC_TABLE nearest: if True, it searches for the nearest dynamic. Otherwise it gives the dynamic exactly inferior """ curve = self._amps2dyns if amp < curve[0][0]: return curve[0][1] if amp > curve[-1][0]: return curve[-1][1] insert_point = _bisect(curve, (amp, None)) if not nearest: idx = max(0, insert_point - 1) return curve[idx][1] amp0, dyn0 = curve[insert_point - 1] amp1, dyn1 = curve[insert_point] db = amp2db(amp) return dyn0 if abs(db - amp2db(amp0)) < abs(db - amp2db(amp1)) else dyn1
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)
def dyn2db(self, dyn: str) -> float: return amp2db(self.dyn2amp(dyn))
def peak(self) -> float: """return the highest sample value (dB)""" return amp2db(np.abs(self.samples).max())