Beispiel #1
0
 def add_partial(self, partial: sndtrck.Partial):
     """
     Raises TrackFull if the partial can't be added
     """
     for p in self.partials:
         if p.t0 < partial.t0:
             if p.t1 + self.mingap > partial.t0:
                 if p.t1 > partial.t0:
                     raise TrackFullError(
                         f"Partial doesn't fit. Own partial {p}, t1+mingap={p.t1+self.mingap} > new partial.t0 {partial.t0}"
                     )
         else:
             if partial.t1 + self.mingap > p.t0:
                 raise TrackFullError("partial does not fit")
     self.partials.append(partial)
     self.partials.sort(key=lambda p: p.t0)
     assert not self.has_overlap()
     minnote = f2m(partial.minfreq)
     maxnote = f2m(partial.maxfreq)
     if len(self.partials) == 1:
         # track was empty
         self.minnote = minnote
         self.maxnote = maxnote
     else:
         assert self.minnote is not None and self.maxnote is not None
         self.minnote = min(self.minnote, minnote)
         self.maxnote = max(self.maxnote, maxnote)
     self._changed()
     return True
Beispiel #2
0
    def rate_partial(self,
                     partial: sndtrck.Partial,
                     maxrange: int,
                     minmargin: float = None) -> float:
        """
        Rates how good this partial fits in this track. Returns > 0 if partial fits,
        the value returned indicates how good it fits
        """
        if minmargin is None:
            minmargin = self.mingap
        assert minmargin is not None

        isempty = self.isempty()
        if isempty:
            margin = partial.t0
        elif not self.partial_fits(partial.t0, partial.t1):
            return -1
        else:
            partial_left_idx = self.partial_left_to(partial.t0)
            if partial_left_idx is None:
                assert all(p.t0 > partial.t1 for p in self.partials)
                margin = partial.t0

            else:
                # the found partial is either the last partial, or the partial next
                # to it shoudl start AFTER the partial being rated
                assert partial_left_idx == len(
                    self.partials) - 1 or self.partials[partial_left_idx +
                                                        1].t0 > partial.t1
                p0 = self.partials[partial_left_idx]
                # Partials should be left indented
                margin = partial.t0 - p0.t1
                assert margin >= minmargin

        # Try to pack as tight as possible
        margin_rating = bpf.halfcos(minmargin, 1, 1, 0.01, exp=0.6)(margin)

        margin_weight, range_weight, wrange_weight = 3, 1, 1

        if isempty:
            return euclidian_distance(
                [margin_rating, 1, 1],
                [margin_weight, range_weight, wrange_weight])

        trackminnote, trackmaxnote = self.track_range()
        minnote = f2m(partial.minfreq)
        maxnote = f2m(partial.maxfreq)
        range_with_note = max(trackmaxnote, maxnote) - min(
            trackminnote, minnote)
        if range_with_note > maxrange:
            return -1
        range_rating = bpf.expon(0, 1, maxrange, 0.0001,
                                 exp=1)(range_with_note)
        avgpitch = self.avgpitch()
        avgdiff = abs(avgpitch - f2m(partial.meanfreq_weighted))
        wrange_rating = bpf.halfcos(0, 1, maxrange, 0.0001, exp=0.5)(avgdiff)
        total = euclidian_distance(
            [margin_rating, range_rating, wrange_rating],
            [margin_weight, range_weight, wrange_weight])
        return total
Beispiel #3
0
def ringmod_sources2(sideband1: pitch_t,
                     sideband2: pitch_t,
                     minnote: pitch_t = "A0",
                     maxnote: pitch_t = "C8",
                     maxdiff=0.5) -> List[Tuple[Note, Note]]:
    """
    Find all pairs of two frequencies which produce the given sidebands when ringmodulated
    Returns a list of Notes

    minnote, maxnote: the range for possible answers
    maxdiff: the max. difference between the given sidebands and the resulting sidebands,
             in midi (1=semitone)
    """
    difm = asmidi(sideband1)
    summ = asmidi(sideband2)
    midimin = n2m(minnote) if isinstance(minnote, str) else minnote
    midimax = n2m(maxnote) if isinstance(maxnote, str) else maxnote
    results = []
    for midi1, midi2 in combinations(range(int(midimin), int(ceil(midimax))),
                                     2):
        note0, note1 = ringmod_exactsource(m2f(midi1), m2f(midi2))
        f0 = note0.freq
        f1 = note1.freq
        if abs(f2m(f0 + f1) -
               summ) <= maxdiff and abs(f2m(abs(f1 - f0)) - difm) <= maxdiff:
            results.append((note0, note1))
    results.sort()
    return results
Beispiel #4
0
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
Beispiel #5
0
def ringmod_exactsource(sideband1: pitch_t,
                        sideband2: pitch_t) -> Tuple[Note, Note]:
    """
    Find a pair of frequencies which, when ringmodulated, result in the
    given sidebands

    sideband1, sideband2: the sidebands produced, as notename or midinote

    Returns: the original pitches, as midinotes
    """
    diffFreq = m2f(asmidi(sideband1))
    sumFreq = m2f(asmidi(sideband2))
    f1 = (diffFreq + sumFreq) / 2.0
    f0 = sumFreq - f1
    return Note(f2m(f0)), Note(f2m(f1))
Beispiel #6
0
def readSpectrumAsChords(path,
                         numsteps=8,
                         maxNotesPerChord=inf) -> List[chord_t]:
    """
    Reads the spectrum saved in `path`, splits it into at most `numsteps` chords, depending
    on their amplitude.

    The information saved by audacity represents the spectrum of the selected audio

    Args:
        path: the path of the saved spectrum (a .txt file)
        numsteps: the number of steps to split the spectral information into, according
            to their amplitude. Each step can be seen as a "layer"
        maxNotesPerChord: the max. number of bins for each "layer". Normally the loudest
            layers will have fewer components
    """
    data = readSpectrum(path)
    notes = []
    for bin_ in data:
        note = Note(note=f2n(bin_.freq),
                    midi=f2m(bin_.freq),
                    freq=bin_.freq,
                    level=bin_.level,
                    step=dbToStep(bin_.level, numsteps))
        notes.append(note)
    chords = [[] for _ in range(numsteps)]
    notes2 = sorted(notes, key=lambda n: n.level, reverse=True)
    for note in notes2:
        chord = chords[note.step]
        if len(chord) <= maxNotesPerChord:
            chord.append(note)
    for chord in chords:
        chord.sort(key=lambda n: n.level, reverse=True)
    return chords
Beispiel #7
0
def _sumtone_find_source(pitch,
                         maxdist=0.5,
                         intervals: List[U[int, float]] = None,
                         minnote: pitch_t = 'A0',
                         maxnote: pitch_t = 'C8',
                         difftonegap=0.):
    """
    pitch: a note str or a midinote
    maxdist: the max. distance between the given note and #
                 the produced sumtone
    intervals: allowed intervals for the source pitches
    minnote, maxnote: the range to look for sumtones

    Returns: a list of pairs (note1:str, note2:str) representing
             notes which produce
             the given pitch as a sumtone
             (or an empty list if no pairs are found)
    """
    intervals = intervals or [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    m0 = asmidi(pitch)
    midimin = int(n2m(minnote)) if isinstance(minnote, str) else int(minnote)
    midimax = int(n2m(maxnote)) if isinstance(maxnote, str) else int(maxnote)
    minValidFreq = 1
    results = []
    for interval in intervals:
        for midi1 in range(midimin, midimax):
            midi2 = midi1 + interval
            if midi2 > midimax:
                continue
            sumFreq = m2f(midi1) + m2f(midi2)
            if sumFreq <= minValidFreq:
                continue
            diffFreq = abs(m2f(midi1) - m2f(midi2))
            if diffFreq < 20 or (abs(f2m(diffFreq) - f2m(sumFreq)) <
                                 difftonegap):
                continue
            midiError = abs(f2m(sumFreq) - m0)
            if midiError <= maxdist:
                results.append((midiError, (midi1, midi2)))
    if not results:
        return []
    results.sort()  # secondary sort by minimal difference
    pairs = list(set(midipair for diff, midipair in results))
    pairs.sort()  # primary sort by pitch
    out = [(m2n(m1), m2n(m2)) for m1, m2 in pairs]
    assert all(isinstance(n0, str) and isinstance(n1, str) for n0, n1 in out)
    return out
Beispiel #8
0
def sumtones(*pitches: pitch_t) -> List[Note]:
    """
    Calculate the summation tones generated by all the pair combinations of notes
    
    pitches: a seq. of Notes
    notes: a seq of notes as strings "C4", "C5+", etc. or frequencies.
    """
    midis = _parsePitches(pitches)
    freqs = list(map(m2f, midis))
    return [Note(f2m(f1 + f2)) for f1, f2 in combinations(freqs, 2)]
Beispiel #9
0
def difftones(*pitches: pitch_t) -> List[Note]:
    """
    Calculate the difference tones generated by all the pair
    combinations of notes
    
    SEE ALSO: difftones_sources, difftone_sources_in_range
    """
    midis = _parsePitches(*pitches)
    freqs = [m2f(m) for m in midis]
    return [Note(f2m(abs(f2 - f1))) for f1, f2 in combinations(freqs, 2)]
Beispiel #10
0
def difftones_beatings(
    pitch1: pitch_t,
    pitch2: pitch_t,
    maxbeatings=20,
    minbeatings=0.3,
    wave="saw",
) -> List[DifftoneBeating]:
    """
    estimate the beatings generated by the difference tone(s) between note1 and note2
    
    for each pair of notes other than sinus-tones, many difference tones are generated:
       * between the fundamentals
       * between the overtones
    
    The calculation is based on the relative amplitude of the overtones and presuposes
    simple waveforms. Any other routine would have to work with the actual sample data
    to calculate the beatings.
    
    Using saw waves gives a sort of upper limit to the beatings which can be generated
    between two notes. In reality, the amplitude of the beatings will always be less than
    what is reported here.
    """
    defaultwave = wave
    freq1 = m2f(asmidi(pitch1))
    freq2 = m2f(asmidi(pitch2))
    overtones1 = [freq1 * i for i in range(1, 7)]
    overtones2 = [freq2 * i for i in range(1, 7)]
    pairs = []
    for i, o1 in enumerate(overtones1):
        for j, o2 in enumerate(overtones2):
            if minbeatings <= abs(o1 - o2) <= maxbeatings:
                amp1 = wave_overtone_relative_amplitude(defaultwave, i)
                amp2 = wave_overtone_relative_amplitude(defaultwave, j)
                amp = amp1 * amp2
                if amp > 0:
                    pairs.append((amp, o1, o2))
    pairs.sort(reverse=True)
    pairs2 = [
        DifftoneBeating(abs(o1 - o2), Note(f2m(o1)), Note(f2m(o2)), freq1,
                        freq2, amp) for amp, o1, o2 in pairs
    ]
    return pairs2
Beispiel #11
0
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
Beispiel #12
0
def difftone_source(difftone: pitch_t,
                    interval: float,
                    resolution=0.0) -> Difftone:
    """
    difftone: the resulting difftone
    interval: the interval between the two notes which should produce a 
        difference tone close to `difftone`
    resolution: the resolution of the pitch grid for the source notes. This
        also defines the acceptable error of the resulting difftone

    Returns: a Difftone
    """
    diffFreq = m2f(asmidi(difftone))
    ratio = interval2ratio(interval)
    f0 = diffFreq / (ratio - 1)
    f1 = f0 * ratio
    midi0, midi1 = f2m(f0), f2m(f1)
    if resolution > 0:
        midi0 = misc.snap_to_grid(midi0, resolution)
        midi1 = misc.snap_to_grid(midi1, resolution)
    return Difftone(midi0, midi1, difftone)
Beispiel #13
0
 def avgpitch(self) -> float:
     """
     Returns:
         This track's average pitch (midinote)
     """
     if self.isempty():
         raise IndexError("This Track is empty")
     if self._avgpitch < 0:
         self._avgpitch = sum(
             f2m(partial.meanfreq_weighted)
             for partial in self.partials) / len(self.partials)
     # assert self.minnote <= self._avgpitch <= self.maxnote, f"minnote={self.minnote}, avg={self._avgpitch}, maxnote={self.maxnote}"
     return self._avgpitch
Beispiel #14
0
 def __init__(self,
              note0: pitch_t,
              note1: pitch_t,
              desired: pitch_t = None):
     self.note0: Note = asNote(note0)
     self.note1: Note = asNote(note1)
     self.freq0: float = round(self.note0.freq)
     self.freq1: float = round(self.note1.freq)
     self.diff: Note = Note(f2m(abs(self.note0.freq - self.note1.freq)))
     self.desired: Note = asNote(
         desired) if desired is not None else self.diff
     self.beatings: int = round(abs(self.desired.freq - self.diff.freq))
     self._notes = None
Beispiel #15
0
def fm_chord(carrierfreq: float,
             modfreq: float,
             index: float,
             minamp=0.01,
             minpitch=None,
             maxpitch=None) -> Chord:
    maxfreq = 24000 if maxpitch is None else asNote(maxpitch).freq
    minfreq = 0 if minpitch is None else asNote(minpitch).freq
    bands = fm_sidebands(carrierfreq=carrierfreq,
                         modfreq=modfreq,
                         index=index,
                         minamp=minamp,
                         minfreq=minfreq,
                         maxfreq=maxfreq)
    notes = [Note(f2m(freq), amp=amp) for freq, amp in bands]
    return Chord(notes)
Beispiel #16
0
def difftones_cubic(*notes: pitch_t) -> List[Note]:
    """
    Return the cubic difference tones
    
    if f1 and f2 are pure tones and f1 < f2, the cubic diff-tone is
    
    2*f1 - f2
    """
    freqs = [m2f(asmidi(note)) for note in notes]
    out = []
    for f1, f2 in combinations(freqs, 2):
        if f2 < f1:
            f1, f2 = f2, f1
        f0 = f1 * 2 - f2
        out.append(Note(f2m(f0)))
    return out
Beispiel #17
0
    def sound2harmonic(self, note, kind='all', tolerance=0.5):
        """
        find the harmonics in this string which can produce the given sound as result.

        note: the note to produce (a string note)
        kind: kind of harmonic. One of [4, 3M, 3m, natural, all]
        tolerance: the acceptable difference between the desired note and the result
                   (in semitones)
        """
        midinote = n2m(note)
        if kind == '4' or kind == 4:
            f0 = midinote - 24
            out = self.sound2note(m2n(f0))
        elif kind == '3M' or kind == 3:
            f0 = midinote - 28
            out = self.sound2note(m2n(f0))
        elif kind == '3m':
            f0 = midinote - 31
            out = self.sound2note(m2n(f0))
        elif kind in ('n', 'natural'):
            fundamental = m2f(self._sounding_midi)
            harmonics = [f2m(fundamental * harmonic) for harmonic in range(12)]
            acceptable_harmonics = []
            for harmonic in harmonics:
                if abs(harmonic - midinote) <= tolerance:
                    acceptable_harmonics.append(harmonic)
            if len(acceptable_harmonics) > 0:
                # now find the position of the node in the string
                results = []
                nodes = []
                for harmonic in acceptable_harmonics:
                    fret = self._flageolet_string.ratio2fret(
                        m2f(harmonic) / fundamental)
                    nodes.append(fret)
                for node in nodes:
                    for fret_pos in node.frets_pos:
                        results.append(fret_pos[1])  # we only append the pitch
                out = [self.sound2note(result) for result in results]
            else:
                out = None
        elif kind == 'all':
            out = []
            for kind in ('4 3M 3m n'.split()):
                out.append(
                    self.sound2harmonic(note, kind=kind, tolerance=tolerance))
            return out
        return kind, out
Beispiel #18
0
def ringmod(*pitches: pitch_t) -> List[Note]:
    """
    Calculate the ring-modulation between the given notes

    pitches:
        a midinote, a notename or a Note (no frequencies!)
        Many notenames can be given as one string, separated by spaces

    Returns the notes of the sidebands, as Notes
    """
    for p in pitches:
        _checkpitch(p)
    midis = _parsePitches(*pitches)
    freqs = list(map(m2f, midis))
    sidebands = [_ringmod2f(f1, f2) for f1, f2 in combinations(freqs, 2)]
    all_sidebands: List[float] = list(set(flatten(sidebands)))
    all_sidebands.sort()
    return [Note(f2m(sideband)) for sideband in all_sidebands]
Beispiel #19
0
def difftone_evaluate_inharmonicity(pitch1: pitch_t, pitch2: pitch_t) -> float:
    m1 = asmidi(pitch1)
    m2 = asmidi(pitch2)
    if m1 > m2:
        m1, m2 = m2, m1
    md = f2m(abs(m2f(m1) - m2f(m2)))
    f1, f2, fd = map(m2f, (m1, m2, md))
    rat_2_1 = f2 / f1
    rat_2_d = f2 / fd
    rat_1_d = f1 / fd
    ratq_2_1 = misc.snap_to_grid(rat_2_1, 0.5)
    ratq_2_d = misc.snap_to_grid(rat_2_d, 0.5)
    ratq_1_d = misc.snap_to_grid(rat_1_d, 0.5)
    delta_2_1 = abs(rat_2_1 - ratq_2_1) / rat_2_1
    delta_2_d = abs(rat_2_d - ratq_2_d) / rat_2_d
    delta_1_d = abs(rat_1_d - ratq_1_d) / rat_1_d
    print(delta_2_1, delta_2_d, delta_1_d)
    delta = sqrt(delta_2_1**2 + delta_2_d**2 + delta_1_d**2)
    return delta
Beispiel #20
0
 def gradient(t, f):
     semitones = curve(t)
     f2 = m2f(f2m(f) + semitones)
     return f2
Beispiel #21
0
def transpose(spectrum: sp.Spectrum, semitones:float) -> sp.Spectrum:
    """
    Transpose spectrum by a fixed number of semitones
    """
    curve = bpf.asbpf(lambda f: m2f((f2m(f)+semitones)))
    return spectrum.freqwarp(curve)
Beispiel #22
0
def ringmod_sources(sidebands: pitch_t,
                    minnote: pitch_t = "A0",
                    maxnote: pitch_t = "C8",
                    maxdiff=0.5,
                    matchall=True,
                    constraints=None,
                    numsources=None) -> List[Note]:
    """
    Given a seq. of sidebands, find notes that, when ringmodulated together,
    include these sidebands as the result.

    sidebands: a seq. of sidebands, as notenames or frequencies
    minnote, maxnote: limit the possible range of the sourcefreqs
    matchall: if True, all sidebands should be matched
    constraints: if given, a seq. of functions. Each of these constraints is of
                 the form (midisources, sidebands) -> bool
                 where: midisources: the sources being modulated (as midinotes)
                        sidebands: the sidebands generated (as midinote)
    Example:

    find a seq. of frequencies which generate the folowing sidebands, given that
    all sidebands should lie within the interval C2-C6

    sidebands = ["C4", "E4", "B5"]
    constraints = [lambda freqs: all(n2f("C2") <= freq <= n2f("C6") for freq in freqs)]
    sourcefreqs = ringmod_sources(sidebands, matchall=True, constraints=constraints)
    print(map(f2n, sourcefreqs))
    --> ['2C#', '4E', '5E']
    newsidebands = ringmod(*sourcefreqs)
    print(map(f2n, newsidebands))
    --> ['4C-09', '4E', '4G+30', '5D+08', '5Gb-27', '5B+02']
    """
    assert isinstance(sidebands, (list, tuple))
    sidemidis = [asmidi(sb) for sb in sidebands]
    midi0, midi1 = n2m(minnote), n2m(maxnote)
    if len(sidebands) == 2 and (numsources == 2 or numsources is None):
        note1, note2 = ringmod_exactsource(sidebands[0], sidebands[1])
        return [note1, note2]
    elif len(sidebands) > 6 or numsources is not None and numsources > 3:
        raise NotImplementedError("too many sidebands...")
    bestmatch = []
    sourcefreqs = None
    for m0, m1, m2 in combinations(range(int(midi0), int(ceil(midi1))), 3):
        newbands = ringmod(m0, m1, m2)
        newmidis = [band.midi for band in newbands]
        matching = _matchone(sidemidis, newmidis, maxdiff)
        if not matching:
            continue
        elif matchall and len(matching) < len(sidebands):
            continue
        elif constraints and not all(
                constr([m0, m1, m2], newmidis) for constr in constraints):
            continue
        if len(matching) > len(bestmatch):
            bestmatch = matching
            sourcefreqs = [m2f(m) for m in newmidis]
        elif len(matching) == len(bestmatch):
            newdiff = sum(abs(orig - new) for orig, new in matching)
            lastdiff = sum(abs(orig - last) for orig, last in bestmatch)
            if newdiff < lastdiff:
                sourcefreqs = [m2f(m) for m in newmidis]
                bestmatch = matching
    return [Note(f2m(freq)) for freq in sourcefreqs]
Beispiel #23
0
 def gradient(t, f):
     semitones = curve(t)
     f2 = m2f(f2m(f) + semitones)
     return f2
Beispiel #24
0
def transpose(spectrum: sp.Spectrum, semitones: float) -> sp.Spectrum:
    """
    Transpose spectrum by a fixed number of semitones
    """
    curve = bpf.asbpf(lambda f: m2f((f2m(f) + semitones)))
    return spectrum.freqwarp(curve)
Beispiel #25
0
    def addpartial(self, partial: sndtrck.Partial) -> bool:
        """
        Returns True if partial could be added

        """
        assert not self.isrendered()
        # max_overlap = R(1, 16)
        max_overlap = 0

        if not self.isempty():
            if partial.t0 < self.end:
                raise ValueError(
                    f"Overlap detected: partials starts at {partial.t0}, voice ends at {self.end}"
                )

        # partialdata: 2D numpy with columns [time, freq, amp, phase, bw]
        partialdata: np.ndarray = partial.toarray()
        amps = partialdata[:, 2]
        assert np.all(amps[1:-1] > 0)
        freqs = partialdata[:, 1]
        assert np.all(freqs[1:-1] > 0)

        if len(partialdata) < 2:
            logger.error("Trying to add an empty partial, skipping")
            return False
        # TODO: hacer minamp configurable
        minamp = db2amp(-90)
        # The 1st and last bp can have amp=0, used to avoid clicks. Should we include them?
        if len(partialdata) > 2 and partialdata[0, 2] == 0 and partialdata[
                0, 1] == partialdata[1, 1]:
            partialdata = partialdata[1:]
        notes: List[Note] = []
        for i in range(len(partialdata) - 1):
            t0, freq0, amp0, phase0, bw0 = partialdata[i, 0:5]
            t1, freq1, amp1 = partialdata[i + 1, 0:3]
            dur = t1 - t0
            if dur < 1e-12:
                logger.error("small note: " + str((t0, t1, freq0, amp0)))
            pitch = f2m(freq0)
            amp = amp0
            note = Note(pitch,
                        t0,
                        dur,
                        max(amp, minamp),
                        bw0,
                        tied=False,
                        color="@addpartial")
            notes.append(note)
        # The last breakpoint was not added: add it if it would make a
        # difference in pitch and is not just a closing bp (with amp=0)
        t0, f0, a0 = partialdata[-2, 0:3]  # butlast
        t1, f1, a1 = partialdata[-1, 0:3]  # last
        if a1 > 0 and abs(f2m(f1) - f2m(f0)) > 0.5:
            lastnote_dur = min(self.lastnote_duration, notes[-1].dur)
            notes.append(
                Note(f2m(f1),
                     start=t1,
                     dur=lastnote_dur,
                     amp=a1,
                     color="@lastnote"))
        notes.sort(key=lambda n: n.start)
        mindur = R(1, 128)
        if has_short_notes(notes, mindur):
            logger.error(">>>>> short notes detected")
            logger.error("\n        ".join(
                str(n) for n in notes if n.dur < mindur))
            raise ValueError("short notes detected")
        if any(n.amp == 0 for n in notes):
            logger.error("Notes with amp=0 detected: ")
            logger.error("\n        ".join(
                str(n) for n in notes if n.amp == 0))
            raise ValueError("notes with amp=0 detected")
        self.addnotes(notes)
        self.added_partials += 1
        return True
Beispiel #26
0
 def _calculate_range(self):
     self.minnote = f2m(min(p.minfreq for p in self.partials))
     self.maxnote = f2m(max(p.maxfreq for p in self.partials))