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 | from aubio import source, tempo | 3 | from aubio import source, tempo |
| 4 | from numpy import median, diff | 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 | """ Calculate the beats per minute (bpm) of a given file. | 7 | """ Calculate the beats per minute (bpm) of a given file. |
| 8 | path: path to the file | 8 | path: path to the file |
| 9 | param: dictionary of parameters | 9 | param: dictionary of parameters |
| 10 | """ | 10 | """ |
| 11 | if params is None: | 11 | if params is None: |
| 12 | params = {} | 12 | params = {} |
| 13 | try: | 13 | # default: |
| 14 | win_s = params['win_s'] | 14 | samplerate, win_s, hop_s = 44100, 1024, 512 |
| 15 | samplerate = params['samplerate'] | 15 | if 'mode' in params: |
| 16 | hop_s = params['hop_s'] | 16 | if params.mode in ['super-fast']: |
| 17 | except KeyError: | 17 | # super fast |
| 18 | """ | 18 | samplerate, win_s, hop_s = 4000, 128, 64 |
| 19 | # super fast | 19 | elif params.mode in ['fast']: |
| 20 | samplerate, win_s, hop_s = 4000, 128, 64 | 20 | # fast |
| 21 | # fast | 21 | samplerate, win_s, hop_s = 8000, 512, 128 |
| 22 | samplerate, win_s, hop_s = 8000, 512, 128 | 22 | elif params.mode in ['default']: |
| 23 | """ | 23 | pass |
| 24 | # default: | 24 | else: |
| 25 | samplerate, win_s, hop_s = 44100, 1024, 512 | 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 | s = source(path, samplerate, hop_s) | 34 | s = source(path, samplerate, hop_s) |
| 28 | samplerate = s.samplerate | 35 | samplerate = s.samplerate |
| @@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines | |||
| 44 | if read < hop_s: | 51 | if read < hop_s: |
| 45 | break | 52 | break |
| 46 | 53 | ||
| 47 | # Convert to periods and to bpm | 54 | def beats_to_bpm(beats, path): |
| 48 | if len(beats) > 1: | 55 | # if enough beats are found, convert to periods then to bpm |
| 49 | if len(beats) < 4: | 56 | if len(beats) > 1: |
| 50 | print("few beats found in {:s}".format(path)) | 57 | if len(beats) < 4: |
| 51 | bpms = 60./diff(beats) | 58 | print("few beats found in {:s}".format(path)) |
| 52 | b = median(bpms) | 59 | bpms = 60./diff(beats) |
| 53 | else: | 60 | return median(bpms) |
| 54 | b = 0 | 61 | else: |
| 55 | print("not enough beats found in {:s}".format(path)) | 62 | print("not enough beats found in {:s}".format(path)) |
| 56 | return b | 63 | return 0 |
| 64 | |||
| 65 | return beats_to_bpm(beats, path) | ||
| 57 | 66 | ||
| 58 | if __name__ == '__main__': | 67 | if __name__ == '__main__': |
| 59 | import sys | 68 | import argparse |
| 60 | for f in sys.argv[1:]: | 69 | parser = argparse.ArgumentParser() |
| 61 | bpm = get_file_bpm(f) | 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 | print("{:6s} {:s}".format("{:2f}".format(bpm), f)) | 79 | print("{:6s} {:s}".format("{:2f}".format(bpm), f)) |
| @@ -1,36 +1,53 @@ | |||
| 1 | #! /usr/bin/env python | 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 | # open input file, get its samplerate | 9 | # open input file, get its samplerate |
| 9 | s = source(path) | 10 | s = aubio.source(path) |
| 10 | samplerate = s.samplerate | 11 | samplerate = s.samplerate |
| 11 | 12 | ||
| 12 | # create an A-weighting filter | 13 | # create an A-weighting filter |
| 13 | f = digital_filter(7) | 14 | f = aubio.digital_filter(7) |
| 14 | f.set_a_weighting(samplerate) | 15 | f.set_a_weighting(samplerate) |
| 15 | # alternatively, apply another filter | ||
| 16 | 16 | ||
| 17 | # create output file | 17 | # create output file |
| 18 | o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate) | 18 | o = aubio.sink(target, samplerate) |
| 19 | 19 | ||
| 20 | total_frames = 0 | 20 | total_frames = 0 |
| 21 | while True: | 21 | while True: |
| 22 | # read from source | ||
| 22 | samples, read = s() | 23 | samples, read = s() |
| 24 | # filter samples | ||
| 23 | filtered_samples = f(samples) | 25 | filtered_samples = f(samples) |
| 26 | # write to sink | ||
| 24 | o(filtered_samples, read) | 27 | o(filtered_samples, read) |
| 28 | # count frames read | ||
| 25 | total_frames += read | 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 | duration = total_frames / float(samplerate) | 35 | duration = total_frames / float(samplerate) |
| 29 | print ("read {:s}".format(s.uri)) | 36 | input_str = "input: {:s} ({:.2f} s, {:d} Hz)" |
| 30 | print ("applied A-weighting filtered ({:d} Hz)".format(samplerate)) | 37 | output_str = "output: {:s}, A-weighting filtered ({:d} frames total)" |
| 31 | print ("wrote {:s} ({:.2f} s)".format(o.uri, duration)) | 38 | print(input_str.format(s.uri, duration, samplerate)) |
| 39 | print(output_str.format(o.uri, total_frames)) | ||
| 32 | 40 | ||
| 33 | if __name__ == '__main__': | 41 | if __name__ == '__main__': |
| 34 | import sys | 42 | usage = "{:s} <input_file> [output_file]".format(sys.argv[0]) |
| 35 | for f in sys.argv[1:]: | 43 | if not 1 < len(sys.argv) < 4: |
| 36 | apply_filter(f) | 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 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
| 2 | 2 | ||
| 3 | from aubio import filterbank, fvec | 3 | """Create a filterbank from a list of frequencies. |
| 4 | from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title | ||
| 5 | from numpy import vstack, arange | ||
| 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 | samplerate = 48000 | 15 | samplerate = 48000 |
| 16 | win_s = 2048 | ||
| 9 | 17 | ||
| 18 | # define a list of custom frequency | ||
| 10 | freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000] | 19 | freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000] |
| 20 | # number of filters to create | ||
| 11 | n_filters = len(freq_list) - 2 | 21 | n_filters = len(freq_list) - 2 |
| 12 | 22 | ||
| 13 | f = filterbank(n_filters, win_s) | 23 | # create a new filterbank |
| 14 | freqs = fvec(freq_list) | 24 | f = aubio.filterbank(n_filters, win_s) |
| 25 | freqs = aubio.fvec(freq_list) | ||
| 15 | f.set_triangle_bands(freqs, samplerate) | 26 | f.set_triangle_bands(freqs, samplerate) |
| 16 | 27 | ||
| 28 | # get the coefficients from the filterbank | ||
| 17 | coeffs = f.get_coeffs() | 29 | coeffs = f.get_coeffs() |
| 18 | coeffs[4] *= 5. | 30 | # apply a gain to fifth band |
| 19 | 31 | coeffs[4] *= 6. | |
| 32 | # load the modified coeffs into the filterbank | ||
| 20 | f.set_coeffs(coeffs) | 33 | f.set_coeffs(coeffs) |
| 21 | 34 | ||
| 22 | times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) | 35 | # display the band gains in a loglog plot |
| 23 | title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.') | 36 | freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters) |
| 24 | loglog(times.T, f.get_coeffs().T, '.-') | 37 | plt.title('filterbank built from a list of frequencies\n' |
| 25 | xlim([50, samplerate/2]) | 38 | 'The 5th band has been amplified by a factor 6.') |
| 26 | ylim([1.0e-6, 2.0e-2]) | 39 | plt.loglog(freqs.T, f.get_coeffs().T, '.-') |
| 27 | xlabel('log frequency (Hz)') | 40 | plt.xlim([50, samplerate/2]) |
| 28 | ylabel('log amplitude') | 41 | plt.ylim([1.0e-6, 2.0e-2]) |
| 29 | 42 | plt.xlabel('log frequency (Hz)') | |
| 30 | show() | 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 | pointer += partition | 38 | pointer += partition |
| 39 | pointer += partition | 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 | a = build_sinusoid(sin_length, freqs, samplerate) | 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 | #! /usr/bin/env python | 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 | samplerate = 0 # use original source samplerate | 8 | samplerate = 0 # use original source samplerate |
| 5 | hop_size = 256 # number of frames to read in one block | 9 | hop_size = 256 # number of frames to read in one block |
| 6 | s = aubio.source(sys.argv[1], samplerate, hop_size) | 10 | src = aubio.source(sys.argv[1], samplerate, hop_size) |
| 7 | total_frames = 0 | 11 | total_frames = 0 |
| 8 | 12 | ||
| 9 | while True: # reading loop | 13 | while True: |
| 10 | samples, read = s() | 14 | samples, read = src() # read hop_size new samples from source |
| 11 | total_frames += read | 15 | total_frames += read # increment total number of frames |
| 12 | if read < hop_size: break # end of file reached | 16 | if read < hop_size: # end of file reached |
| 17 | break | ||
| 13 | 18 | ||
| 14 | fmt_string = "read {:d} frames at {:d}Hz from {:s}" | 19 | fmt_string = "read {:d} frames at {:d}Hz from {:s}" |
| 15 | print (fmt_string.format(total_frames, s.samplerate, sys.argv[1])) | 20 | print(fmt_string.format(total_frames, src.samplerate, src.uri)) |
| 16 | |||
| @@ -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 | import numpy as np | 12 | import numpy as np |
| 13 | 13 | ||
| 14 | win_s = 1024 | 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 | warmup = win_s // hop_s - 1 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | ||
| @@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines | |||
| 11 | from aubio import unwrap2pi, float_type | 11 | from aubio import unwrap2pi, float_type |
| 12 | import numpy as np | 12 | import numpy as np |
| 13 | 13 | ||
| 14 | win_s = 1024 | 14 | win_s = 512 |
| 15 | hop_s = win_s / 8 # 87.5 % overlap | 15 | hop_s = win_s // 8 # 87.5 % overlap |
| 16 | 16 | ||
| 17 | warmup = win_s // hop_s - 1 | 17 | warmup = win_s // hop_s - 1 |
| 18 | 18 | ||
| @@ -92,8 +92,10 @@ while True: · 73 unmodified lines | |||
| 92 | old_grain.norm = np.copy(cur_grain.norm) | 92 | old_grain.norm = np.copy(cur_grain.norm) |
| 93 | old_grain.phas = np.copy(cur_grain.phas) | 93 | old_grain.phas = np.copy(cur_grain.phas) |
| 94 | 94 | ||
| 95 | block_read += 1 | 95 | # until end of file |
| 96 | if read < hop_s: break | 96 | if read < hop_s: break |
| 97 | # increment block counter | ||
| 98 | block_read += 1 | ||
| 97 | 99 | ||
| 98 | for t in range(warmup + 2): # purge the last frames from the phase vocoder | 100 | for t in range(warmup + 2): # purge the last frames from the phase vocoder |
| 99 | new_grain.norm[:] = 0 | 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() | ||