Showing the changes to python/demos (14 of 230 files). Show all
Unified · Split
| @@ -0,0 +1,45 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +import alsaaudio | |
| 4 | +import numpy as np | |
| 5 | +import aubio | |
| 6 | + | |
| 7 | +# constants | |
| 8 | +samplerate = 44100 | |
| 9 | +win_s = 2048 | |
| 10 | +hop_s = win_s // 2 | |
| 11 | +framesize = hop_s | |
| 12 | + | |
| 13 | +# set up audio input | |
| 14 | +recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE) | |
| 15 | +recorder.setperiodsize(framesize) | |
| 16 | +recorder.setrate(samplerate) | |
| 17 | +recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE) | |
| 18 | +recorder.setchannels(1) | |
| 19 | + | |
| 20 | +# create aubio pitch detection (first argument is method, "default" is | |
| 21 | +# "yinfft", can also be "yin", "mcomb", fcomb", "schmitt"). | |
| 22 | +pitcher = aubio.pitch("default", win_s, hop_s, samplerate) | |
| 23 | +# set output unit (can be 'midi', 'cent', 'Hz', ...) | |
| 24 | +pitcher.set_unit("Hz") | |
| 25 | +# ignore frames under this level (dB) | |
| 26 | +pitcher.set_silence(-40) | |
| 27 | + | |
| 28 | +print("Starting to listen, press Ctrl+C to stop") | |
| 29 | + | |
| 30 | +# main loop | |
| 31 | +while True: | |
| 32 | + try: | |
| 33 | + # read data from audio input | |
| 34 | + _, data = recorder.read() | |
| 35 | + # convert data to aubio float samples | |
| 36 | + samples = np.fromstring(data, dtype=aubio.float_type) | |
| 37 | + # pitch of current frame | |
| 38 | + freq = pitcher(samples)[0] | |
| 39 | + # compute energy of current block | |
| 40 | + energy = np.sum(samples**2)/len(samples) | |
| 41 | + # do something with the results | |
| 42 | + print("{:10.4f} {:10.4f}".format(freq,energy)) | |
| 43 | + except KeyboardInterrupt: | |
| 44 | + print("Ctrl+C pressed, exiting") | |
| 45 | + break | |
| @@ -0,0 +1,50 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +import numpy as np | |
| 4 | +from aubio import pitch | |
| 5 | +import pylab as plt | |
| 6 | + | |
| 7 | +buf_size = 2048 * 1 | |
| 8 | +hop_size = buf_size // 4 | |
| 9 | + | |
| 10 | +samplerate = 44100 | |
| 11 | +minfreq = 40 | |
| 12 | +maxfreq = 6000 | |
| 13 | + | |
| 14 | +def sinewave(freq, duration, samplerate = samplerate): | |
| 15 | + """ generate a sinewave """ | |
| 16 | + length = hop_size | |
| 17 | + while length < duration * samplerate: | |
| 18 | + length += hop_size | |
| 19 | + return np.sin( 2. * np.pi * np.arange(length) * freq / samplerate ).astype("float32") | |
| 20 | + | |
| 21 | +def get_stats_for_pitch_method(method, freqs, samplerate = samplerate): | |
| 22 | + """ for a given pitch method and a list of frequency, generate a sinewave | |
| 23 | + and get mean deviation """ | |
| 24 | + means = np.zeros(len(freqs)) | |
| 25 | + medians = np.zeros(len(freqs)) | |
| 26 | + for freq, fn in zip(freqs, range(len(freqs))): | |
| 27 | + s = sinewave(freq, .50).reshape(-1, hop_size) | |
| 28 | + #s = (sinewave(freq, .50) + .0*sinewave(freq/2., .50)).reshape(-1, hop_size) | |
| 29 | + p = pitch(method, buf_size, hop_size, samplerate = samplerate) | |
| 30 | + candidates = np.zeros(len(s)) | |
| 31 | + #samples = np.zeros(buf_size) | |
| 32 | + for frame, i in zip(s, range(len(s))): | |
| 33 | + candidates[i] = p(frame)[0] | |
| 34 | + # skip first few candidates | |
| 35 | + candidates = candidates[4:] | |
| 36 | + means[fn] = np.mean(candidates[candidates != 0] - freq) | |
| 37 | + medians[fn] = np.median(candidates[candidates != 0] - freq) | |
| 38 | + print (freq, means[fn], medians[fn]) | |
| 39 | + return means, medians | |
| 40 | + | |
| 41 | +if __name__ == '__main__': | |
| 42 | + freqs = np.arange(minfreq, maxfreq, 1.) | |
| 43 | + modes = ["yin", "yinfft"] | |
| 44 | + for mode in modes: | |
| 45 | + means, medians = get_stats_for_pitch_method(mode, freqs) | |
| 46 | + plt.figure() | |
| 47 | + plt.plot(freqs, means, 'g-') | |
| 48 | + plt.plot(freqs, medians, 'r--') | |
| 49 | + #plt.savefig(mode + '_deviations_test.png', dpi=300) | |
| 50 | + plt.show() | |
| @@ -3,26 +3,33 @@ · 2 unmodified lines | ||
| 3 | 3 | from aubio import source, tempo |
| 4 | 4 | from numpy import median, diff |
| 5 | 5 | |
| 6 | −def get_file_bpm(path, params = None): | |
| 6 | +def get_file_bpm(path, params=None): | |
| 7 | 7 | """ Calculate the beats per minute (bpm) of a given file. |
| 8 | 8 | path: path to the file |
| 9 | 9 | param: dictionary of parameters |
| 10 | 10 | """ |
| 11 | 11 | if params is None: |
| 12 | 12 | params = {} |
| 13 | − try: | |
| 14 | − win_s = params['win_s'] | |
| 15 | − samplerate = params['samplerate'] | |
| 16 | − hop_s = params['hop_s'] | |
| 17 | − except KeyError: | |
| 18 | − """ | |
| 19 | − # super fast | |
| 20 | − samplerate, win_s, hop_s = 4000, 128, 64 | |
| 21 | − # fast | |
| 22 | − samplerate, win_s, hop_s = 8000, 512, 128 | |
| 23 | − """ | |
| 24 | − # default: | |
| 25 | − samplerate, win_s, hop_s = 44100, 1024, 512 | |
| 13 | + # default: | |
| 14 | + samplerate, win_s, hop_s = 44100, 1024, 512 | |
| 15 | + if 'mode' in params: | |
| 16 | + if params.mode in ['super-fast']: | |
| 17 | + # super fast | |
| 18 | + samplerate, win_s, hop_s = 4000, 128, 64 | |
| 19 | + elif params.mode in ['fast']: | |
| 20 | + # fast | |
| 21 | + samplerate, win_s, hop_s = 8000, 512, 128 | |
| 22 | + elif params.mode in ['default']: | |
| 23 | + pass | |
| 24 | + else: | |
| 25 | + raise ValueError("unknown mode {:s}".format(params.mode)) | |
| 26 | + # manual settings | |
| 27 | + if 'samplerate' in params: | |
| 28 | + samplerate = params.samplerate | |
| 29 | + if 'win_s' in params: | |
| 30 | + win_s = params.win_s | |
| 31 | + if 'hop_s' in params: | |
| 32 | + hop_s = params.hop_s | |
| 26 | 33 | |
| 27 | 34 | s = source(path, samplerate, hop_s) |
| 28 | 35 | samplerate = s.samplerate |
| @@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines | ||
| 44 | 51 | if read < hop_s: |
| 45 | 52 | break |
| 46 | 53 | |
| 47 | − # Convert to periods and to bpm | |
| 48 | − if len(beats) > 1: | |
| 49 | − if len(beats) < 4: | |
| 50 | − print("few beats found in {:s}".format(path)) | |
| 51 | − bpms = 60./diff(beats) | |
| 52 | − b = median(bpms) | |
| 53 | − else: | |
| 54 | − b = 0 | |
| 55 | − print("not enough beats found in {:s}".format(path)) | |
| 56 | − return b | |
| 54 | + def beats_to_bpm(beats, path): | |
| 55 | + # if enough beats are found, convert to periods then to bpm | |
| 56 | + if len(beats) > 1: | |
| 57 | + if len(beats) < 4: | |
| 58 | + print("few beats found in {:s}".format(path)) | |
| 59 | + bpms = 60./diff(beats) | |
| 60 | + return median(bpms) | |
| 61 | + else: | |
| 62 | + print("not enough beats found in {:s}".format(path)) | |
| 63 | + return 0 | |
| 64 | + | |
| 65 | + return beats_to_bpm(beats, path) | |
| 57 | 66 | |
| 58 | 67 | if __name__ == '__main__': |
| 59 | − import sys | |
| 60 | − for f in sys.argv[1:]: | |
| 61 | − bpm = get_file_bpm(f) | |
| 68 | + import argparse | |
| 69 | + parser = argparse.ArgumentParser() | |
| 70 | + parser.add_argument('-m', '--mode', | |
| 71 | + help="mode [default|fast|super-fast]", | |
| 72 | + dest="mode", default='default') | |
| 73 | + parser.add_argument('sources', | |
| 74 | + nargs='+', | |
| 75 | + help="input_files") | |
| 76 | + args = parser.parse_args() | |
| 77 | + for f in args.sources: | |
| 78 | + bpm = get_file_bpm(f, params = args) | |
| 62 | 79 | print("{:6s} {:s}".format("{:2f}".format(bpm), f)) |
| @@ -1,36 +1,53 @@ | ||
| 1 | 1 | #! /usr/bin/env python |
| 2 | 2 | |
| 3 | +import sys | |
| 4 | +import os.path | |
| 5 | +import aubio | |
| 3 | 6 | |
| 4 | −def apply_filter(path): | |
| 5 | − from aubio import source, sink, digital_filter | |
| 6 | − from os.path import basename, splitext | |
| 7 | 7 | |
| 8 | +def apply_filter(path, target): | |
| 8 | 9 | # open input file, get its samplerate |
| 9 | − s = source(path) | |
| 10 | + s = aubio.source(path) | |
| 10 | 11 | samplerate = s.samplerate |
| 11 | 12 | |
| 12 | 13 | # create an A-weighting filter |
| 13 | − f = digital_filter(7) | |
| 14 | + f = aubio.digital_filter(7) | |
| 14 | 15 | f.set_a_weighting(samplerate) |
| 15 | − # alternatively, apply another filter | |
| 16 | 16 | |
| 17 | 17 | # create output file |
| 18 | − o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate) | |
| 18 | + o = aubio.sink(target, samplerate) | |
| 19 | 19 | |
| 20 | 20 | total_frames = 0 |
| 21 | 21 | while True: |
| 22 | + # read from source | |
| 22 | 23 | samples, read = s() |
| 24 | + # filter samples | |
| 23 | 25 | filtered_samples = f(samples) |
| 26 | + # write to sink | |
| 24 | 27 | o(filtered_samples, read) |
| 28 | + # count frames read | |
| 25 | 29 | total_frames += read |
| 26 | − if read < s.hop_size: break | |
| 30 | + # end of file reached | |
| 31 | + if read < s.hop_size: | |
| 32 | + break | |
| 27 | 33 | |
| 34 | + # print some info | |
| 28 | 35 | duration = total_frames / float(samplerate) |
| 29 | − print ("read {:s}".format(s.uri)) | |
| 30 | − print ("applied A-weighting filtered ({:d} Hz)".format(samplerate)) | |
| 31 | − print ("wrote {:s} ({:.2f} s)".format(o.uri, duration)) | |
| 36 | + input_str = "input: {:s} ({:.2f} s, {:d} Hz)" | |
| 37 | + output_str = "output: {:s}, A-weighting filtered ({:d} frames total)" | |
| 38 | + print(input_str.format(s.uri, duration, samplerate)) | |
| 39 | + print(output_str.format(o.uri, total_frames)) | |
| 32 | 40 | |
| 33 | 41 | if __name__ == '__main__': |
| 34 | − import sys | |
| 35 | − for f in sys.argv[1:]: | |
| 36 | − apply_filter(f) | |
| 42 | + usage = "{:s} <input_file> [output_file]".format(sys.argv[0]) | |
| 43 | + if not 1 < len(sys.argv) < 4: | |
| 44 | + print(usage) | |
| 45 | + sys.exit(1) | |
| 46 | + if len(sys.argv) < 3: | |
| 47 | + input_path = sys.argv[1] | |
| 48 | + basename = os.path.splitext(os.path.basename(input_path))[0] + ".wav" | |
| 49 | + output_path = "filtered_" + basename | |
| 50 | + else: | |
| 51 | + input_path, output_path = sys.argv[1:] | |
| 52 | + # run function | |
| 53 | + apply_filter(input_path, output_path) | |
| @@ -1,30 +1,44 @@ | ||
| 1 | 1 | #! /usr/bin/env python |
| 2 | 2 | |
| 3 | −from aubio import filterbank, fvec | |
| 4 | −from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title | |
| 5 | −from numpy import vstack, arange | |
| 3 | +"""Create a filterbank from a list of frequencies. | |
| 6 | 4 | |
| 7 | −win_s = 2048 | |
| 5 | +This demo uses `aubio.filterbank.set_triangle_bands` to build a set of | |
| 6 | +triangular filters from a list of frequencies. | |
| 7 | + | |
| 8 | +The filterbank coefficients are then modified before being displayed.""" | |
| 9 | + | |
| 10 | +import aubio | |
| 11 | +import numpy as np | |
| 12 | +import matplotlib.pyplot as plt | |
| 13 | + | |
| 14 | +# sampling rate and size of the fft | |
| 8 | 15 | samplerate = 48000 |
| 16 | +win_s = 2048 | |
| 9 | 17 | |
| 18 | +# define a list of custom frequency | |
| 10 | 19 | freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000] |
| 20 | +# number of filters to create | |
| 11 | 21 | n_filters = len(freq_list) - 2 |
| 12 | 22 | |
| 13 | −f = filterbank(n_filters, win_s) | |
| 14 | −freqs = fvec(freq_list) | |
| 23 | +# create a new filterbank | |
| 24 | +f = aubio.filterbank(n_filters, win_s) | |
| 25 | +freqs = aubio.fvec(freq_list) | |
| 15 | 26 | f.set_triangle_bands(freqs, samplerate) |
| 16 | 27 | |
| 28 | +# get the coefficients from the filterbank | |
| 17 | 29 | coeffs = f.get_coeffs() |
| 18 | −coeffs[4] *= 5. | |
| 19 | − | |
| 30 | +# apply a gain to fifth band | |
| 31 | +coeffs[4] *= 6. | |
| 32 | +# load the modified coeffs into the filterbank | |
| 20 | 33 | f.set_coeffs(coeffs) |
| 21 | 34 | |
| 22 | −times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) | |
| 23 | −title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.') | |
| 24 | −loglog(times.T, f.get_coeffs().T, '.-') | |
| 25 | −xlim([50, samplerate/2]) | |
| 26 | −ylim([1.0e-6, 2.0e-2]) | |
| 27 | −xlabel('log frequency (Hz)') | |
| 28 | −ylabel('log amplitude') | |
| 29 | − | |
| 30 | −show() | |
| 35 | +# display the band gains in a loglog plot | |
| 36 | +freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) | |
| 37 | +plt.title('filterbank built from a list of frequencies\n' | |
| 38 | + 'The 5th band has been amplified by a factor 6.') | |
| 39 | +plt.loglog(freqs.T, f.get_coeffs().T, '.-') | |
| 40 | +plt.xlim([50, samplerate/2]) | |
| 41 | +plt.ylim([1.0e-6, 2.0e-2]) | |
| 42 | +plt.xlabel('log frequency (Hz)') | |
| 43 | +plt.ylabel('log amplitude') | |
| 44 | +plt.show() | |
| @@ -0,0 +1,37 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +import sys | |
| 4 | +from aubio import source, notes | |
| 5 | + | |
| 6 | +if len(sys.argv) < 2: | |
| 7 | + print("Usage: %s <filename> [samplerate]" % sys.argv[0]) | |
| 8 | + sys.exit(1) | |
| 9 | + | |
| 10 | +filename = sys.argv[1] | |
| 11 | + | |
| 12 | +downsample = 1 | |
| 13 | +samplerate = 44100 // downsample | |
| 14 | +if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) | |
| 15 | + | |
| 16 | +win_s = 512 // downsample # fft size | |
| 17 | +hop_s = 256 // downsample # hop size | |
| 18 | + | |
| 19 | +s = source(filename, samplerate, hop_s) | |
| 20 | +samplerate = s.samplerate | |
| 21 | + | |
| 22 | +tolerance = 0.8 | |
| 23 | + | |
| 24 | +notes_o = notes("default", win_s, hop_s, samplerate) | |
| 25 | + | |
| 26 | +print("%8s" % "time","[ start","vel","last ]") | |
| 27 | + | |
| 28 | +# total number of frames read | |
| 29 | +total_frames = 0 | |
| 30 | +while True: | |
| 31 | + samples, read = s() | |
| 32 | + new_note = notes_o(samples) | |
| 33 | + if (new_note[0] != 0): | |
| 34 | + note_str = ' '.join(["%.2f" % i for i in new_note]) | |
| 35 | + print("%.6f" % (total_frames/float(samplerate)), new_note) | |
| 36 | + total_frames += read | |
| 37 | + if read < hop_s: break | |
| @@ -37,7 +37,7 @@ freqs[ pointer : pointer + partition ] = 1480 · 36 unmodified lines | ||
| 37 | 37 | |
| 38 | 38 | pointer += partition |
| 39 | 39 | pointer += partition |
| 40 | −freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8) | |
| 40 | +freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8) | |
| 41 | 41 | |
| 42 | 42 | a = build_sinusoid(sin_length, freqs, samplerate) |
| 43 | 43 | |
| @@ -0,0 +1,75 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Use pyaudio to open the microphone and run aubio.pitch on the stream of | |
| 4 | +# incoming samples. If a filename is given as the first argument, it will | |
| 5 | +# record 5 seconds of audio to this location. Otherwise, the script will | |
| 6 | +# run until Ctrl+C is pressed. | |
| 7 | + | |
| 8 | +# Examples: | |
| 9 | +# $ ./python/demos/demo_pyaudio.py | |
| 10 | +# $ ./python/demos/demo_pyaudio.py /tmp/recording.wav | |
| 11 | + | |
| 12 | +import pyaudio | |
| 13 | +import sys | |
| 14 | +import numpy as np | |
| 15 | +import aubio | |
| 16 | + | |
| 17 | +# initialise pyaudio | |
| 18 | +p = pyaudio.PyAudio() | |
| 19 | + | |
| 20 | +# open stream | |
| 21 | +buffer_size = 1024 | |
| 22 | +pyaudio_format = pyaudio.paFloat32 | |
| 23 | +n_channels = 1 | |
| 24 | +samplerate = 44100 | |
| 25 | +stream = p.open(format=pyaudio_format, | |
| 26 | + channels=n_channels, | |
| 27 | + rate=samplerate, | |
| 28 | + input=True, | |
| 29 | + frames_per_buffer=buffer_size) | |
| 30 | + | |
| 31 | +if len(sys.argv) > 1: | |
| 32 | + # record 5 seconds | |
| 33 | + output_filename = sys.argv[1] | |
| 34 | + record_duration = 5 # exit 1 | |
| 35 | + outputsink = aubio.sink(sys.argv[1], samplerate) | |
| 36 | + total_frames = 0 | |
| 37 | +else: | |
| 38 | + # run forever | |
| 39 | + outputsink = None | |
| 40 | + record_duration = None | |
| 41 | + | |
| 42 | +# setup pitch | |
| 43 | +tolerance = 0.8 | |
| 44 | +win_s = 4096 # fft size | |
| 45 | +hop_s = buffer_size # hop size | |
| 46 | +pitch_o = aubio.pitch("default", win_s, hop_s, samplerate) | |
| 47 | +pitch_o.set_unit("midi") | |
| 48 | +pitch_o.set_tolerance(tolerance) | |
| 49 | + | |
| 50 | +print("*** starting recording") | |
| 51 | +while True: | |
| 52 | + try: | |
| 53 | + audiobuffer = stream.read(buffer_size) | |
| 54 | + signal = np.fromstring(audiobuffer, dtype=np.float32) | |
| 55 | + | |
| 56 | + pitch = pitch_o(signal)[0] | |
| 57 | + confidence = pitch_o.get_confidence() | |
| 58 | + | |
| 59 | + print("{} / {}".format(pitch,confidence)) | |
| 60 | + | |
| 61 | + if outputsink: | |
| 62 | + outputsink(signal, len(signal)) | |
| 63 | + | |
| 64 | + if record_duration: | |
| 65 | + total_frames += len(signal) | |
| 66 | + if record_duration * samplerate < total_frames: | |
| 67 | + break | |
| 68 | + except KeyboardInterrupt: | |
| 69 | + print("*** Ctrl+C pressed, exiting") | |
| 70 | + break | |
| 71 | + | |
| 72 | +print("*** done recording") | |
| 73 | +stream.stop_stream() | |
| 74 | +stream.close() | |
| 75 | +p.terminate() | |
| @@ -1,16 +1,20 @@ | ||
| 1 | 1 | #! /usr/bin/env python |
| 2 | −import sys, aubio | |
| 2 | + | |
| 3 | +"""A simple example using aubio.source.""" | |
| 4 | + | |
| 5 | +import sys | |
| 6 | +import aubio | |
| 3 | 7 | |
| 4 | 8 | samplerate = 0 # use original source samplerate |
| 5 | −hop_size = 256 # number of frames to read in one block | |
| 6 | −s = aubio.source(sys.argv[1], samplerate, hop_size) | |
| 9 | +hop_size = 256 # number of frames to read in one block | |
| 10 | +src = aubio.source(sys.argv[1], samplerate, hop_size) | |
| 7 | 11 | total_frames = 0 |
| 8 | 12 | |
| 9 | −while True: # reading loop | |
| 10 | − samples, read = s() | |
| 11 | − total_frames += read | |
| 12 | − if read < hop_size: break # end of file reached | |
| 13 | +while True: | |
| 14 | + samples, read = src() # read hop_size new samples from source | |
| 15 | + total_frames += read # increment total number of frames | |
| 16 | + if read < hop_size: # end of file reached | |
| 17 | + break | |
| 13 | 18 | |
| 14 | 19 | fmt_string = "read {:d} frames at {:d}Hz from {:s}" |
| 15 | −print (fmt_string.format(total_frames, s.samplerate, sys.argv[1])) | |
| 16 | − | |
| 20 | +print(fmt_string.format(total_frames, src.samplerate, src.uri)) | |
| @@ -0,0 +1,78 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +""" A simple demo using aubio and pyaudio to play beats in real time | |
| 4 | + | |
| 5 | +Note you will need to have pyaudio installed: `pip install pyaudio`. | |
| 6 | + | |
| 7 | +Examples: | |
| 8 | + ./demo_tapthebeat.py ~/Music/track1.ogg | |
| 9 | + | |
| 10 | +When compiled with ffmpeg/libav, you should be able to open remote streams. For | |
| 11 | +instance using youtube-dl (`pip install youtube-dl`): | |
| 12 | + | |
| 13 | + ./demo_tapthebeat.py `youtube-dl -xg https://youtu.be/zZbM9n9j3_g` | |
| 14 | + | |
| 15 | +""" | |
| 16 | + | |
| 17 | +import sys | |
| 18 | +import time | |
| 19 | +import pyaudio | |
| 20 | +import aubio | |
| 21 | +import numpy as np | |
| 22 | + | |
| 23 | +win_s = 1024 # fft size | |
| 24 | +hop_s = win_s // 2 # hop size | |
| 25 | + | |
| 26 | +# parse command line arguments | |
| 27 | +if len(sys.argv) < 2: | |
| 28 | + print("Usage: %s <filename> [samplerate]" % sys.argv[0]) | |
| 29 | + sys.exit(1) | |
| 30 | + | |
| 31 | +filename = sys.argv[1] | |
| 32 | + | |
| 33 | +samplerate = 0 | |
| 34 | +if len( sys.argv ) > 2: samplerate = int(sys.argv[2]) | |
| 35 | + | |
| 36 | +# create aubio source | |
| 37 | +a_source = aubio.source(filename, samplerate, hop_s) | |
| 38 | +samplerate = a_source.samplerate | |
| 39 | + | |
| 40 | +# create aubio tempo detection | |
| 41 | +a_tempo = aubio.tempo("default", win_s, hop_s, samplerate) | |
| 42 | + | |
| 43 | +# create a simple click sound | |
| 44 | +click = 0.7 * np.sin(2. * np.pi * np.arange(hop_s) / hop_s * samplerate / 3000.) | |
| 45 | + | |
| 46 | +# pyaudio callback | |
| 47 | +def pyaudio_callback(_in_data, _frame_count, _time_info, _status): | |
| 48 | + samples, read = a_source() | |
| 49 | + is_beat = a_tempo(samples) | |
| 50 | + if is_beat: | |
| 51 | + samples += click | |
| 52 | + #print ('tick') # avoid print in audio callback | |
| 53 | + audiobuf = samples.tobytes() | |
| 54 | + if read < hop_s: | |
| 55 | + return (audiobuf, pyaudio.paComplete) | |
| 56 | + return (audiobuf, pyaudio.paContinue) | |
| 57 | + | |
| 58 | +# create pyaudio stream with frames_per_buffer=hop_s and format=paFloat32 | |
| 59 | +p = pyaudio.PyAudio() | |
| 60 | +pyaudio_format = pyaudio.paFloat32 | |
| 61 | +frames_per_buffer = hop_s | |
| 62 | +n_channels = 1 | |
| 63 | +stream = p.open(format=pyaudio_format, channels=n_channels, rate=samplerate, | |
| 64 | + output=True, frames_per_buffer=frames_per_buffer, | |
| 65 | + stream_callback=pyaudio_callback) | |
| 66 | + | |
| 67 | +# start pyaudio stream | |
| 68 | +stream.start_stream() | |
| 69 | + | |
| 70 | +# wait for stream to finish | |
| 71 | +while stream.is_active(): | |
| 72 | + time.sleep(0.1) | |
| 73 | + | |
| 74 | +# stop pyaudio stream | |
| 75 | +stream.stop_stream() | |
| 76 | +stream.close() | |
| 77 | +# close pyaudio | |
| 78 | +p.terminate() | |
| @@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines | ||
| 12 | 12 | import numpy as np |
| 13 | 13 | |
| 14 | 14 | win_s = 1024 |
| 15 | −hop_s = win_s / 8 # 87.5 % overlap | |
| 15 | +hop_s = win_s // 8 # 87.5 % overlap | |
| 16 | 16 | |
| 17 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | |
| @@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines | ||
| 11 | 11 | from aubio import unwrap2pi, float_type |
| 12 | 12 | import numpy as np |
| 13 | 13 | |
| 14 | −win_s = 1024 | |
| 15 | −hop_s = win_s / 8 # 87.5 % overlap | |
| 14 | +win_s = 512 | |
| 15 | +hop_s = win_s // 8 # 87.5 % overlap | |
| 16 | 16 | |
| 17 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | |
| @@ -92,8 +92,10 @@ while True: · 73 unmodified lines | ||
| 92 | 92 | old_grain.norm = np.copy(cur_grain.norm) |
| 93 | 93 | old_grain.phas = np.copy(cur_grain.phas) |
| 94 | 94 | |
| 95 | − block_read += 1 | |
| 95 | + # until end of file | |
| 96 | 96 | if read < hop_s: break |
| 97 | + # increment block counter | |
| 98 | + block_read += 1 | |
| 97 | 99 | |
| 98 | 100 | for t in range(warmup + 2): # purge the last frames from the phase vocoder |
| 99 | 101 | new_grain.norm[:] = 0 |
| @@ -0,0 +1,77 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | + | |
| 3 | +# Simple demo to extract notes from a sound file, and store them in a midi file | |
| 4 | +# using mido. | |
| 5 | +# | |
| 6 | +# Install mido: `pip instal mido` | |
| 7 | +# | |
| 8 | +# Documentation: https://mido.readthedocs.io/ | |
| 9 | + | |
| 10 | +import sys | |
| 11 | +from aubio import source, notes | |
| 12 | +from mido import Message, MetaMessage, MidiFile, MidiTrack, second2tick, bpm2tempo | |
| 13 | + | |
| 14 | +if len(sys.argv) < 3: | |
| 15 | + print("Usage: %s <filename> <output> [samplerate]" % sys.argv[0]) | |
| 16 | + sys.exit(1) | |
| 17 | + | |
| 18 | +filename = sys.argv[1] | |
| 19 | +midioutput = sys.argv[2] | |
| 20 | + | |
| 21 | +downsample = 1 | |
| 22 | +samplerate = 44100 // downsample | |
| 23 | +if len( sys.argv ) > 3: samplerate = int(sys.argv[3]) | |
| 24 | + | |
| 25 | +win_s = 512 // downsample # fft size | |
| 26 | +hop_s = 256 // downsample # hop size | |
| 27 | + | |
| 28 | +s = source(filename, samplerate, hop_s) | |
| 29 | +samplerate = s.samplerate | |
| 30 | + | |
| 31 | +tolerance = 0.8 | |
| 32 | + | |
| 33 | +notes_o = notes("default", win_s, hop_s, samplerate) | |
| 34 | + | |
| 35 | +print("%8s" % "time","[ start","vel","last ]") | |
| 36 | + | |
| 37 | +# create a midi file | |
| 38 | +mid = MidiFile() | |
| 39 | +track = MidiTrack() | |
| 40 | +mid.tracks.append(track) | |
| 41 | + | |
| 42 | +ticks_per_beat = mid.ticks_per_beat # default: 480 | |
| 43 | +bpm = 120 # default midi tempo | |
| 44 | + | |
| 45 | +tempo = bpm2tempo(bpm) | |
| 46 | +track.append(MetaMessage('set_tempo', tempo=tempo)) | |
| 47 | +track.append(MetaMessage('time_signature', numerator=4, denominator=4)) | |
| 48 | + | |
| 49 | +def frames2tick(frames, samplerate=samplerate): | |
| 50 | + sec = frames / float(samplerate) | |
| 51 | + return int(second2tick(sec, ticks_per_beat, tempo)) | |
| 52 | + | |
| 53 | +last_time = 0 | |
| 54 | + | |
| 55 | +# total number of frames read | |
| 56 | +total_frames = 0 | |
| 57 | +while True: | |
| 58 | + samples, read = s() | |
| 59 | + new_note = notes_o(samples) | |
| 60 | + if (new_note[0] != 0): | |
| 61 | + note_str = ' '.join(["%.2f" % i for i in new_note]) | |
| 62 | + print("%.6f" % (total_frames/float(samplerate)), new_note) | |
| 63 | + delta = frames2tick(total_frames) - last_time | |
| 64 | + if new_note[2] > 0: | |
| 65 | + track.append(Message('note_off', note=int(new_note[2]), | |
| 66 | + velocity=127, time=0) | |
| 67 | + ) | |
| 68 | + track.append(Message('note_on', | |
| 69 | + note=int(new_note[0]), | |
| 70 | + velocity=int(new_note[1]), | |
| 71 | + time=delta) | |
| 72 | + ) | |
| 73 | + last_time = frames2tick(total_frames) | |
| 74 | + total_frames += read | |
| 75 | + if read < hop_s: break | |
| 76 | + | |
| 77 | +mid.save(midioutput) | |
| @@ -0,0 +1,175 @@ | ||
| 1 | +#! /usr/bin/env python | |
| 2 | +# -*- coding: utf8 -*- | |
| 3 | + | |
| 4 | +""" Pure python implementation of the sum of squared difference | |
| 5 | + | |
| 6 | + sqd_yin: original sum of squared difference [0] | |
| 7 | + d_t(tau) = x ⊗ kernel | |
| 8 | + sqd_yinfast: sum of squared diff using complex domain [0] | |
| 9 | + sqd_yinfftslow: tappered squared diff [1] | |
| 10 | + sqd_yinfft: modified squared diff using complex domain [1] | |
| 11 | + | |
| 12 | +[0]:http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf | |
| 13 | +[1]:https://aubio.org/phd/ | |
| 14 | +""" | |
| 15 | + | |
| 16 | +import sys | |
| 17 | +import numpy as np | |
| 18 | +import matplotlib.pyplot as plt | |
| 19 | + | |
| 20 | +def sqd_yin(samples): | |
| 21 | + """ compute original sum of squared difference | |
| 22 | + | |
| 23 | + Brute-force computation (cost o(N**2), slow).""" | |
| 24 | + B = len(samples) | |
| 25 | + W = B//2 | |
| 26 | + yin = np.zeros(W) | |
| 27 | + for j in range(W): | |
| 28 | + for tau in range(1, W): | |
| 29 | + yin[tau] += (samples[j] - samples[j+tau])**2 | |
| 30 | + return yin | |
| 31 | + | |
| 32 | +def sqd_yinfast(samples): | |
| 33 | + """ compute approximate sum of squared difference | |
| 34 | + | |
| 35 | + Using complex convolution (fast, cost o(n*log(n)) )""" | |
| 36 | + # yin_t(tau) = (r_t(0) + r_(t+tau)(0)) - 2r_t(tau) | |
| 37 | + B = len(samples) | |
| 38 | + W = B//2 | |
| 39 | + yin = np.zeros(W) | |
| 40 | + sqdiff = np.zeros(W) | |
| 41 | + kernel = np.zeros(B) | |
| 42 | + # compute r_(t+tau)(0) | |
| 43 | + squares = samples**2 | |
| 44 | + for tau in range(W): | |
| 45 | + sqdiff[tau] = squares[tau:tau+W].sum() | |
| 46 | + # add r_t(0) | |
| 47 | + sqdiff += sqdiff[0] | |
| 48 | + # compute r_t(tau) using kernel convolution in complex domain | |
| 49 | + samples_fft = np.fft.fft(samples) | |
| 50 | + kernel[1:W+1] = samples[W-1::-1] # first half, reversed | |
| 51 | + kernel_fft = np.fft.fft(kernel) | |
| 52 | + r_t_tau = np.fft.ifft(samples_fft * kernel_fft).real[W:] | |
| 53 | + # compute yin_t(tau) | |
| 54 | + yin = sqdiff - 2 * r_t_tau | |
| 55 | + return yin | |
| 56 | + | |
| 57 | +def sqd_yintapered(samples): | |
| 58 | + """ compute tappered sum of squared difference | |
| 59 | + | |
| 60 | + Brute-force computation (cost o(N**2), slow).""" | |
| 61 | + B = len(samples) | |
| 62 | + W = B//2 | |
| 63 | + yin = np.zeros(W) | |
| 64 | + for tau in range(1, W): | |
| 65 | + for j in range(W - tau): | |
| 66 | + yin[tau] += (samples[j] - samples[j+tau])**2 | |
| 67 | + return yin | |
| 68 | + | |
| 69 | +def sqd_yinfft(samples): | |
| 70 | + """ compute yinfft modified sum of squared differences | |
| 71 | + | |
| 72 | + Very fast, improved performance in transients. | |
| 73 | + | |
| 74 | + FIXME: biased.""" | |
| 75 | + B = len(samples) | |
| 76 | + W = B//2 | |
| 77 | + yin = np.zeros(W) | |
| 78 | + def hanningz(W): | |
| 79 | + return .5 * (1. - np.cos(2. * np.pi * np.arange(W) / W)) | |
| 80 | + #win = np.ones(B) | |
| 81 | + win = hanningz(B) | |
| 82 | + sqrmag = np.zeros(B) | |
| 83 | + fftout = np.fft.fft(win*samples) | |
| 84 | + sqrmag[0] = fftout[0].real**2 | |
| 85 | + for l in range(1, W): | |
| 86 | + sqrmag[l] = fftout[l].real**2 + fftout[l].imag**2 | |
| 87 | + sqrmag[B-l] = sqrmag[l] | |
| 88 | + sqrmag[W] = fftout[W].real**2 | |
| 89 | + fftout = np.fft.fft(sqrmag) | |
| 90 | + sqrsum = 2.*sqrmag[:W + 1].sum() | |
| 91 | + yin[0] = 0 | |
| 92 | + yin[1:] = sqrsum - fftout.real[1:W] | |
| 93 | + return yin / B | |
| 94 | + | |
| 95 | +def cumdiff(yin): | |
| 96 | + """ compute the cumulative mean normalized difference """ | |
| 97 | + W = len(yin) | |
| 98 | + yin[0] = 1. | |
| 99 | + cumsum = 0. | |
| 100 | + for tau in range(1, W): | |
| 101 | + cumsum += yin[tau] | |
| 102 | + if cumsum != 0: | |
| 103 | + yin[tau] *= tau/cumsum | |
| 104 | + else: | |
| 105 | + yin[tau] = 1 | |
| 106 | + return yin | |
| 107 | + | |
| 108 | +def compute_all(x): | |
| 109 | + import time | |
| 110 | + now = time.time() | |
| 111 | + | |
| 112 | + yin = sqd_yin(x) | |
| 113 | + t1 = time.time() | |
| 114 | + print ("yin took %.2fms" % ((t1-now) * 1000.)) | |
| 115 | + | |
| 116 | + yinfast = sqd_yinfast(x) | |
| 117 | + t2 = time.time() | |
| 118 | + print ("yinfast took: %.2fms" % ((t2-t1) * 1000.)) | |
| 119 | + | |
| 120 | + yintapered = sqd_yintapered(x) | |
| 121 | + t3 = time.time() | |
| 122 | + print ("yintapered took: %.2fms" % ((t3-t2) * 1000.)) | |
| 123 | + | |
| 124 | + yinfft = sqd_yinfft(x) | |
| 125 | + t4 = time.time() | |
| 126 | + print ("yinfft took: %.2fms" % ((t4-t3) * 1000.)) | |
| 127 | + | |
| 128 | + return yin, yinfast, yintapered, yinfft | |
| 129 | + | |
| 130 | +def plot_all(yin, yinfast, yintapered, yinfft): | |
| 131 | + fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey='col') | |
| 132 | + | |
| 133 | + axes[0, 0].plot(yin, label='yin') | |
| 134 | + axes[0, 0].plot(yintapered, label='yintapered') | |
| 135 | + axes[0, 0].set_ylim(bottom=0) | |
| 136 | + axes[0, 0].legend() | |
| 137 | + axes[1, 0].plot(yinfast, '-', label='yinfast') | |
| 138 | + axes[1, 0].plot(yinfft, label='yinfft') | |
| 139 | + axes[1, 0].legend() | |
| 140 | + | |
| 141 | + axes[0, 1].plot(cumdiff(yin), label='yin') | |
| 142 | + axes[0, 1].plot(cumdiff(yintapered), label='yin tapered') | |
| 143 | + axes[0, 1].set_ylim(bottom=0) | |
| 144 | + axes[0, 1].legend() | |
| 145 | + axes[1, 1].plot(cumdiff(yinfast), '-', label='yinfast') | |
| 146 | + axes[1, 1].plot(cumdiff(yinfft), label='yinfft') | |
| 147 | + axes[1, 1].legend() | |
| 148 | + | |
| 149 | + fig.tight_layout() | |
| 150 | + | |
| 151 | +testfreqs = [441., 800., 10000., 40.] | |
| 152 | + | |
| 153 | +if len(sys.argv) > 1: | |
| 154 | + testfreqs = map(float,sys.argv[1:]) | |
| 155 | + | |
| 156 | +for f in testfreqs: | |
| 157 | + print ("Comparing yin implementations for sine wave at %.fHz" % f) | |
| 158 | + samplerate = 44100. | |
| 159 | + win_s = 4096 | |
| 160 | + | |
| 161 | + x = np.cos(2.*np.pi * np.arange(win_s) * f / samplerate) | |
| 162 | + | |
| 163 | + n_times = 1#00 | |
| 164 | + for n in range(n_times): | |
| 165 | + yin, yinfast, yinfftslow, yinfft = compute_all(x) | |
| 166 | + if 0: # plot difference | |
| 167 | + plt.plot(yin-yinfast) | |
| 168 | + plt.tight_layout() | |
| 169 | + plt.show() | |
| 170 | + if 1: | |
| 171 | + plt.plot(yinfftslow-yinfft) | |
| 172 | + plt.tight_layout() | |
| 173 | + plt.show() | |
| 174 | + plot_all(yin, yinfast, yinfftslow, yinfft) | |
| 175 | +plt.show() | |