History

Merge branch 'master' into feature/pitchshift

Paul Brossier committed 7 years ago

Commit 633400d2 parents 5b46bc3 f19db54230 files · +12450 −4607Browse files

Showing the changes to python/demos (14 of 230 files). Show all

Unified · Split

14 files
  1. python/demos/demo_alsa.py +45 −0
  2. python/demos/demo_bench_yin.py +50 −0
  3. python/demos/demo_bpm_extract.py +44 −27
  4. python/demos/demo_filter.py +31 −14
  5. python/demos/demo_filterbank.py +31 −17
  6. python/demos/demo_notes.py +37 −0
  7. python/demos/demo_pitch_sinusoid.py +1 −1
  8. python/demos/demo_pyaudio.py +75 −0
  9. python/demos/demo_source_simple.py +13 −9
  10. python/demos/demo_tapthebeat.py +78 −0
  11. python/demos/demo_timestretch.py +1 −1
  12. python/demos/demo_timestretch_online.py +5 −3
  13. python/demos/demo_wav2midi.py +77 −0
  14. python/demos/demo_yin_compare.py +175 −0
python/demos/demo_alsa.py+45−0
@@ -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
python/demos/demo_bench_yin.py+50−0
@@ -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()
python/demos/demo_bpm_extract.py+44−27
@@ -3,26 +3,33 @@ · 2 unmodified lines
33 from aubio import source, tempo
44 from numpy import median, diff
55
6−def get_file_bpm(path, params = None):
6+def get_file_bpm(path, params=None):
77 """ Calculate the beats per minute (bpm) of a given file.
88 path: path to the file
99 param: dictionary of parameters
1010 """
1111 if params is None:
1212 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
2633
2734 s = source(path, samplerate, hop_s)
2835 samplerate = s.samplerate
@@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines
4451 if read < hop_s:
4552 break
4653
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)
5766
5867 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)
6279 print("{:6s} {:s}".format("{:2f}".format(bpm), f))
python/demos/demo_filter.py+31−14
@@ -1,36 +1,53 @@
11 #! /usr/bin/env python
22
3+import sys
4+import os.path
5+import aubio
36
4−def apply_filter(path):
5− from aubio import source, sink, digital_filter
6− from os.path import basename, splitext
77
8+def apply_filter(path, target):
89 # open input file, get its samplerate
9− s = source(path)
10+ s = aubio.source(path)
1011 samplerate = s.samplerate
1112
1213 # create an A-weighting filter
13− f = digital_filter(7)
14+ f = aubio.digital_filter(7)
1415 f.set_a_weighting(samplerate)
15− # alternatively, apply another filter
1616
1717 # create output file
18− o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate)
18+ o = aubio.sink(target, samplerate)
1919
2020 total_frames = 0
2121 while True:
22+ # read from source
2223 samples, read = s()
24+ # filter samples
2325 filtered_samples = f(samples)
26+ # write to sink
2427 o(filtered_samples, read)
28+ # count frames read
2529 total_frames += read
26− if read < s.hop_size: break
30+ # end of file reached
31+ if read < s.hop_size:
32+ break
2733
34+ # print some info
2835 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))
3240
3341 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)
python/demos/demo_filterbank.py+31−17
@@ -1,30 +1,44 @@
11 #! /usr/bin/env python
22
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.
64
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
815 samplerate = 48000
16+win_s = 2048
917
18+# define a list of custom frequency
1019 freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
20+# number of filters to create
1121 n_filters = len(freq_list) - 2
1222
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)
1526 f.set_triangle_bands(freqs, samplerate)
1627
28+# get the coefficients from the filterbank
1729 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
2033 f.set_coeffs(coeffs)
2134
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()
python/demos/demo_notes.py+37−0
@@ -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
python/demos/demo_pitch_sinusoid.py+1−1
@@ -37,7 +37,7 @@ freqs[ pointer : pointer + partition ] = 1480 · 36 unmodified lines
3737
3838 pointer += partition
3939 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)
4141
4242 a = build_sinusoid(sin_length, freqs, samplerate)
4343
python/demos/demo_pyaudio.py+75−0
@@ -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()
python/demos/demo_source_simple.py+13−9
@@ -1,16 +1,20 @@
11 #! /usr/bin/env python
2−import sys, aubio
2+
3+"""A simple example using aubio.source."""
4+
5+import sys
6+import aubio
37
48 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)
711 total_frames = 0
812
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
1318
1419 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))
python/demos/demo_tapthebeat.py+78−0
@@ -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()
python/demos/demo_timestretch.py+1−1
@@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines
1212 import numpy as np
1313
1414 win_s = 1024
15−hop_s = win_s / 8 # 87.5 % overlap
15+hop_s = win_s // 8 # 87.5 % overlap
1616
1717 warmup = win_s // hop_s - 1
1818
python/demos/demo_timestretch_online.py+5−3
@@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines
1111 from aubio import unwrap2pi, float_type
1212 import numpy as np
1313
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
1616
1717 warmup = win_s // hop_s - 1
1818
@@ -92,8 +92,10 @@ while True: · 73 unmodified lines
9292 old_grain.norm = np.copy(cur_grain.norm)
9393 old_grain.phas = np.copy(cur_grain.phas)
9494
95− block_read += 1
95+ # until end of file
9696 if read < hop_s: break
97+ # increment block counter
98+ block_read += 1
9799
98100 for t in range(warmup + 2): # purge the last frames from the phase vocoder
99101 new_grain.norm[:] = 0
python/demos/demo_wav2midi.py+77−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)
python/demos/demo_yin_compare.py+175−0
@@ -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()

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t