Exemplo n.º 1
0
def difftone_sources_from_set(difftone: pitch_t,
                              sources: List[T],
                              tomidi: Callable[[T], float] = asmidi,
                              maxdeviation=0.5,
                              exclude_intervals=None,
                              exclude_intervals_maxdeviation=0.5,
                              gap=0) -> List[Tuple[T, T]]:
    difftone_pitch = asmidi(difftone)
    f0 = m2f(difftone_pitch - maxdeviation)
    f1 = m2f(difftone_pitch + maxdeviation)
    possible_sources = [
        source for source in sources if tomidi(source) >= difftone_pitch + gap
    ]
    results = []
    for s1, s2 in combinations(possible_sources, 2):
        p1 = tomidi(s1)
        p2 = tomidi(s2)
        if p1 > p2:
            p1, p2 = p2, p1
            s1, s2, = s2, s1
        ok = True
        if exclude_intervals:
            interval_now = abs(p1 - p2)
            for excluded in exclude_intervals:
                if abs(interval_now -
                       excluded) < exclude_intervals_maxdeviation:
                    ok = False
                    break
        if not ok:
            continue
        diff_freq = abs(m2f(p1) - m2f(p2))
        if f0 <= diff_freq <= f1:
            results.append((s1, s2))
    results.sort(key=lambda pair: tomidi(pair[0]))
    return results
Exemplo n.º 2
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
Exemplo n.º 3
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))
Exemplo n.º 4
0
def sumtones_sources(result,
                     maxdist=0.5,
                     maxnote="C8",
                     intervals=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                     show=False,
                     minnote="A0",
                     difftonegap=0):
    """
    find two notes which produce a sumation tone near the given note

    Args:
        result: the resulting difference tone, as note
        maxdist: the maximum distance between the expected result and the generated tone,
            in semitones (0.5 == 50 cents)
        maxnote: the highest note to considere
        intervals: accepted intervals between the notes. Only 'tempered' notes are used,
            but of course microtones produce also difference tones!
        difftonegap : minimum gap (absolute value) between the sumtone and the difftone
            produced by the two notes
        show: either False or one of:
            'report': produce a text report
            'musicxml': convert to musicxml, show as Finale file
            'lily': convert to lilypond, show as PDF
    """
    result = asmidi(result)
    maxnote = asmidi(maxnote)
    pairs = _sumtone_find_source(result,
                                 maxdist,
                                 minnote=minnote,
                                 maxnote=maxnote,
                                 intervals=intervals,
                                 difftonegap=difftonegap)
    if not pairs:
        if show == 'report':
            warnings.warn(
                "No source pair of notes found that satisfy the given conditions"
            )
        return []
    out = []
    resultfreq = m2f(result)
    for note0, note1 in pairs:
        sumfreq = n2f(note0) + n2f(note1)
        sumtone = SumTone((note0, note1), f2n(sumfreq), sumfreq,
                          abs(sumfreq - resultfreq))
        out.append(sumtone)
    if show == 'report':
        raise NotImplementedError()
        # _sumtones_sources_report(result, out)
    elif show:
        from maelzel import scoring
        events = []
        for pair in out:
            notes = []
            notes.extend(pair.notes)
            notes.append(pair.sumnote)
            chord = scoring.Notation(pitches=notes)
            chord.addArticulation('sum: %.2f' % pair.sumfreq)
            events.append(chord)
        scoring.render.show(events)
    return RecordList(out)
Exemplo n.º 5
0
def pushState(a4: float = None,
              tempo: time_t = None,
              renderer=None,
              config: configdict.CheckedDict = None,
              scorestruct: ScoreStructure = None) -> _State:
    """
    Push a new state to the global state stack. A new state inherits values
    not set from the earlier state

    Args:
        a4: the reference frequency
        tempo: a tempo reference
        renderer: a play.OfflineRenderer
        config: a configuration dict, as created by config.clone()
        scorestruct: the current ScoreStructure.
    """
    assert len(_statestack) >= 1
    assert tempo is None or scorestruct is None
    currState = _statestack[-1]
    if a4 is None:
        a4 = currState.a4
    tempo = F(tempo) if tempo is not None else currState.tempo
    if renderer is None:
        renderer = currState.renderer
    if config is None:
        config = currState.config.copy()
    else:
        assert config is not currentConfig() and \
               not isinstance(config, configdict.ConfigDict)
    state = _State(a4=a4, tempo=tempo, renderer=renderer, config=config)

    _statestack.append(state)
    if a4 != m2f(69):
        set_reference_freq(a4)
    return state
Exemplo n.º 6
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
Exemplo n.º 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
Exemplo n.º 8
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
Exemplo n.º 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)]
Exemplo n.º 10
0
def _difftone_find_source(pitch,
                          maxdeviation=0.5,
                          intervals=None,
                          minnote: pitch_t = 'A0',
                          maxnote: pitch_t = 'C8',
                          resolution=1.) -> List[Tuple[float, float]]:
    """

    Args:
        pitch: the resulting difftone
        maxdeviation: the max. deviation between the generated difftone
            and the desired one
        intervals: a list of possible intervals (None to allow any interval)
        minnote: the lowest note to search
        maxnote: the highest note to search
        resolution: the pitch resolution of the search space (1=semitone)

    Returns:
        a list of (lowest note, highest note))
    """
    pitch = asmidi(pitch)
    pitchfreq = m2f(pitch)
    intervals = intervals or list(frange(resolution, 8, resolution))
    f0 = m2f(pitch - maxdeviation)
    f1 = m2f(pitch + maxdeviation)
    minmidi = asmidi(minnote)
    maxmidi = asmidi(maxnote)
    out = []
    for interval in intervals:
        for m0 in frange(minmidi, maxmidi + resolution, resolution):
            m1 = m0 + interval
            if m1 >= maxmidi + resolution:
                continue
            diffFreq = abs(m2f(m0) - m2f(m1))
            if diffFreq > f1:
                break
            elif f0 <= diffFreq <= f1:
                out.append((abs(diffFreq - pitchfreq), (m0, m1)))
    if out:
        out.sort()
        out = [pair for error, pair in out]
        out.sort()
    return out
Exemplo n.º 11
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
Exemplo n.º 12
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
Exemplo n.º 13
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)
Exemplo n.º 14
0
        if self is currentConfig():
            set_reference_freq(value)

    @property
    def tempo(self) -> float:
        return float(self.scorestruct.getMeasureDef(0).quarterTempo)

    @tempo.setter
    def tempo(self, quarterTempo: float):
        pass

    def activate(self) -> None:
        set_reference_freq(self.a4)


_statestack = [_State(a4=config.get('A4', m2f(69)), config=config)]


def pushState(a4: float = None,
              tempo: time_t = None,
              renderer=None,
              config: configdict.CheckedDict = None,
              scorestruct: ScoreStructure = None) -> _State:
    """
    Push a new state to the global state stack. A new state inherits values
    not set from the earlier state

    Args:
        a4: the reference frequency
        tempo: a tempo reference
        renderer: a play.OfflineRenderer
Exemplo n.º 15
0
 def gradient(t, f):
     semitones = curve(t)
     f2 = m2f(f2m(f) + semitones)
     return f2
Exemplo n.º 16
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)
Exemplo n.º 17
0
def piano_freqs(start='A0', stop='C8') -> numpy.ndarray:
    """
    generate an array of the frequencies representing all the piano keys
    """
    keys = range(int(n2m(start)), int(n2m(stop))+1)
    return numpy.fromiter((m2f(key) for key in keys), dtype=numpy.float64)
Exemplo n.º 18
0
def logfreqs(delta_midi=0.5) -> numpy.ndarray:
    """
    return an array of frequencies corresponding to the pitches of all notes of the audible spectrum
    delta_midi indicates the definition of the pitch matrix: 1=semitones, 0.5, quartertones, etc.
    """
    return numpy.fromiter((m2f(x) for x in numpy.arange(0, 139, delta_midi)), dtype=numpy.float64)
Exemplo n.º 19
0
 def gradient(t, f):
     semitones = curve(t)
     f2 = m2f(f2m(f) + semitones)
     return f2
Exemplo n.º 20
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)
Exemplo n.º 21
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]