def pianofreqs(start='A0', stop='C8') -> np.ndarray: """ Generate an array of the frequencies representing all the piano keys """ n0 = int(n2m(start)) n1 = int(n2m(stop)) + 1 return m2f_np(np.arange(n0, n1, 1))
def __init__(self, name='IV', written='3G', sounding='3C'): self._name = name self._written = written self._sounding = sounding self._written_midi = n2m(written) self._sounding_midi = n2m(sounding) self._flageolet_string = InstrumentString(self._sounding)
def asmidi(x) -> float: if isinstance(x, float): assert 0<=x<=128 return x elif isinstance(x, str): return n2m(x) elif isinstance(x, int): assert 0 <= x < 128 return float(x) elif hasattr(x, "pitch"): return x.pitch raise TypeError(f"Cannot interpret {x} as a midinote")
def asmidi(x: pitch_t) -> float: """ Converts a notename to a midinote. """ if isinstance(x, (int, float)): if x > 127: raise ValueError( "A midinote expected (< 128), but got a value of {x}!") return x elif isinstance(x, str): return n2m(x) try: return float(x) except TypeError: raise TypeError(f"could not convert {x} to a midi note")
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
def sound2note(self, note, strings=None): if strings is None: strings = (self.i, self.ii, self.iii, self.iv) notes = note.split() if len(notes) > 1: midinotes = [n2m(n) for n in notes] midinotes.sort(reverse=True) notes = [m2n(m) for m in midinotes] lines = [] for strings in _window((self.i, self.ii, self.iii, self.iv), len(notes)): for string, note in zip(strings, notes): lines.append(self.sound2note(note, [string])) lines.append('\n') return ''.join(lines) else: lines = [] for string in strings: out = string.sound2note(note) if out: lines.append("%s --> %s\n" % (string._name, out)) return ''.join(lines)
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)
def regions_from_files(files, key='auto', offset='auto', fillkeys=True, relpath=True, minkey=36, skipbadestimations=True, printregions=True, offset_thresh=-55): """ files: a list of soundfiles key: 'auto': read the file and determine the main pitch a function: it is called with the path, it returns a midinote an integer or string: the first note of the region, all other samples are assigned chormatically a list of integers or strings: each file is assigned a specific key """ if key == 'auto' and offset == 'auto': freqs_and_offsets = [ estimate_freq_and_offset(f, minamp=offset_thresh) for f in files ] keys = [int(f2m(freq) + 0.5) for freq, _ in freqs_and_offsets] offsets = [o for f, o in freqs_and_offsets] elif key == 'auto': keys = [int(f2m(estimate_freq(f)) + 0.5) for f in files] offsets = [offset] * len(keys) elif offset == 'auto': keys = list(map(key, files)) offsets = [estimate_offset(f) for f in files] else: if isinstance(key, int): keys = list(range(key, key + len(files))) elif isinstance(key, str): key = n2m(key) keys = list(range(key, key + len(files))) elif isinstance(key, collections.Callable): keys = list(map(key, files)) else: keys = key assert len(keys) == len(files) offsets = [offset] * len(keys) if relpath: samples = [os.path.split(f)[1] for f in files] else: samples = files i = 0 for f, key in zip(files, keys): if key < minkey: print("error in the frequency estimation: key of %s out of range" "Trying another strategy" % f) key2 = int(estimate_freq(f, strategy='fft') + 0.5) if key2 < minkey: print(" no luck...") if not skipbadestimations: raise RuntimeError( "could not estimate the frequency of %s, aborting" % f) else: print("estimation failed, skipping...") key2 = -1 if key2 >= minkey: keys[i] = key2 i += 1 rows = sorted(zip(samples, keys, offsets), key=lambda sample_key_offset: sample_key_offset[1]) regions = [] if not fillkeys: raise ValueError("XXX fix this") # for sample, key, offset in samples_and_keys: # regions.append("<region> sample=%s key=%d offset=%d" % # (sample, key, offset)) else: samples, keys, offsets = list(zip(*rows)) from .iterlib import pairwise avgs = [int((key1 + key0) / 2. + 0.5) for key0, key1 in pairwise(keys)] lokeys = [keys[0] - (avgs[0] - keys[0])] + avgs centers = keys hikeys = avgs + [keys[-1] + (keys[-1] - avgs[-1])] hikeys = [max(center, k - 1) for k, center in zip(hikeys, centers)] for sample, offset, center, lokey, hikey in zip( samples, offsets, centers, lokeys, hikeys): assert lokey <= center <= hikey if offset is None: offset = 0 region = ("<region> sample=%s offset=%d pitch_keycenter=%d " "lokey=%d hikey=%d" % (sample, offset, center, lokey, hikey)) regions.append(region) if printregions: print("\n".join(regions)) return regions
def sound2note(self, note): midinote = n2m(note) dx = midinote - self._sounding_midi if dx >= 0: written = self._written_midi + dx return m2n(written)
def note2sound(self, note): midinote = n2m(note) dx = midinote - self._written_midi if dx >= 0: sounding = self._sounding_midi + dx return m2n(sounding)
def normalize_note(note): return m2n(n2m(note))