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
3import alsaaudio
4import numpy as np
5import aubio
6
7# constants
8samplerate = 44100
9win_s = 2048
10hop_s = win_s // 2
11framesize = hop_s
12
13# set up audio input
14recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
15recorder.setperiodsize(framesize)
16recorder.setrate(samplerate)
17recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE)
18recorder.setchannels(1)
19
20# create aubio pitch detection (first argument is method, "default" is
21# "yinfft", can also be "yin", "mcomb", fcomb", "schmitt").
22pitcher = aubio.pitch("default", win_s, hop_s, samplerate)
23# set output unit (can be 'midi', 'cent', 'Hz', ...)
24pitcher.set_unit("Hz")
25# ignore frames under this level (dB)
26pitcher.set_silence(-40)
27
28print("Starting to listen, press Ctrl+C to stop")
29
30# main loop
31while 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
3import numpy as np
4from aubio import pitch
5import pylab as plt
6
7buf_size = 2048 * 1
8hop_size = buf_size // 4
9
10samplerate = 44100
11minfreq = 40
12maxfreq = 6000
13
14def 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
21def 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
41if __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
3from aubio import source, tempo3from aubio import source, tempo
4from numpy import median, diff4from numpy import median, diff
5 5
6def get_file_bpm(path, params = None):6def 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 file8 path: path to the file
9 param: dictionary of parameters9 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 fast19 elif params.mode in ['fast']:
20 samplerate, win_s, hop_s = 4000, 128, 64 20 # fast
21 # fast21 samplerate, win_s, hop_s = 8000, 512, 128
22 samplerate, win_s, hop_s = 8000, 512, 12822 elif params.mode in ['default']:
23 """23 pass
24 # default:24 else:
25 samplerate, win_s, hop_s = 44100, 1024, 51225 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.samplerate35 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 break52 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 = 061 else:
55 print("not enough beats found in {:s}".format(path))62 print("not enough beats found in {:s}".format(path))
56 return b63 return 0
64
65 return beats_to_bpm(beats, path)
57 66
58if __name__ == '__main__':67if __name__ == '__main__':
59 import sys68 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))
python/demos/demo_filter.py+31−14
@@ -1,36 +1,53 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3import sys
4import os.path
5import aubio
3 6
4def apply_filter(path):
5 from aubio import source, sink, digital_filter
6 from os.path import basename, splitext
7 7
8def apply_filter(path, target):
8 # open input file, get its samplerate9 # open input file, get its samplerate
9 s = source(path)10 s = aubio.source(path)
10 samplerate = s.samplerate11 samplerate = s.samplerate
11 12
12 # create an A-weighting filter13 # 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 file17 # create output file
18 o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate)18 o = aubio.sink(target, samplerate)
19 19
20 total_frames = 020 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 += read29 total_frames += read
26 if read < s.hop_size: break30 # 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
33if __name__ == '__main__':41if __name__ == '__main__':
34 import sys42 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)
python/demos/demo_filterbank.py+31−17
@@ -1,30 +1,44 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3from aubio import filterbank, fvec3"""Create a filterbank from a list of frequencies.
4from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
5from numpy import vstack, arange
6 4
7win_s = 20485This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
6triangular filters from a list of frequencies.
7
8The filterbank coefficients are then modified before being displayed."""
9
10import aubio
11import numpy as np
12import matplotlib.pyplot as plt
13
14# sampling rate and size of the fft
8samplerate = 4800015samplerate = 48000
16win_s = 2048
9 17
18# define a list of custom frequency
10freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]19freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
20# number of filters to create
11n_filters = len(freq_list) - 221n_filters = len(freq_list) - 2
12 22
13f = filterbank(n_filters, win_s)23# create a new filterbank
14freqs = fvec(freq_list)24f = aubio.filterbank(n_filters, win_s)
25freqs = aubio.fvec(freq_list)
15f.set_triangle_bands(freqs, samplerate)26f.set_triangle_bands(freqs, samplerate)
16 27
28# get the coefficients from the filterbank
17coeffs = f.get_coeffs()29coeffs = f.get_coeffs()
18coeffs[4] *= 5.30# apply a gain to fifth band
19 31coeffs[4] *= 6.
32# load the modified coeffs into the filterbank
20f.set_coeffs(coeffs)33f.set_coeffs(coeffs)
21 34
22times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)35# display the band gains in a loglog plot
23title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')36freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
24loglog(times.T, f.get_coeffs().T, '.-')37plt.title('filterbank built from a list of frequencies\n'
25xlim([50, samplerate/2])38 'The 5th band has been amplified by a factor 6.')
26ylim([1.0e-6, 2.0e-2])39plt.loglog(freqs.T, f.get_coeffs().T, '.-')
27xlabel('log frequency (Hz)')40plt.xlim([50, samplerate/2])
28ylabel('log amplitude')41plt.ylim([1.0e-6, 2.0e-2])
29 42plt.xlabel('log frequency (Hz)')
30show()43plt.ylabel('log amplitude')
44plt.show()
python/demos/demo_notes.py+37−0
@@ -0,0 +1,37 @@
1#! /usr/bin/env python
2
3import sys
4from aubio import source, notes
5
6if len(sys.argv) < 2:
7 print("Usage: %s <filename> [samplerate]" % sys.argv[0])
8 sys.exit(1)
9
10filename = sys.argv[1]
11
12downsample = 1
13samplerate = 44100 // downsample
14if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
15
16win_s = 512 // downsample # fft size
17hop_s = 256 // downsample # hop size
18
19s = source(filename, samplerate, hop_s)
20samplerate = s.samplerate
21
22tolerance = 0.8
23
24notes_o = notes("default", win_s, hop_s, samplerate)
25
26print("%8s" % "time","[ start","vel","last ]")
27
28# total number of frames read
29total_frames = 0
30while 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
37 37
38pointer += partition38pointer += partition
39pointer += partition39pointer += partition
40freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8)40freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8)
41 41
42a = build_sinusoid(sin_length, freqs, samplerate)42a = build_sinusoid(sin_length, freqs, samplerate)
43 43
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
12import pyaudio
13import sys
14import numpy as np
15import aubio
16
17# initialise pyaudio
18p = pyaudio.PyAudio()
19
20# open stream
21buffer_size = 1024
22pyaudio_format = pyaudio.paFloat32
23n_channels = 1
24samplerate = 44100
25stream = p.open(format=pyaudio_format,
26 channels=n_channels,
27 rate=samplerate,
28 input=True,
29 frames_per_buffer=buffer_size)
30
31if 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
37else:
38 # run forever
39 outputsink = None
40 record_duration = None
41
42# setup pitch
43tolerance = 0.8
44win_s = 4096 # fft size
45hop_s = buffer_size # hop size
46pitch_o = aubio.pitch("default", win_s, hop_s, samplerate)
47pitch_o.set_unit("midi")
48pitch_o.set_tolerance(tolerance)
49
50print("*** starting recording")
51while 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
72print("*** done recording")
73stream.stop_stream()
74stream.close()
75p.terminate()
python/demos/demo_source_simple.py+13−9
@@ -1,16 +1,20 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2import sys, aubio2
3"""A simple example using aubio.source."""
4
5import sys
6import aubio
3 7
4samplerate = 0 # use original source samplerate8samplerate = 0 # use original source samplerate
5hop_size = 256 # number of frames to read in one block9hop_size = 256 # number of frames to read in one block
6s = aubio.source(sys.argv[1], samplerate, hop_size)10src = aubio.source(sys.argv[1], samplerate, hop_size)
7total_frames = 011total_frames = 0
8 12
9while True: # reading loop13while True:
10 samples, read = s()14 samples, read = src() # read hop_size new samples from source
11 total_frames += read15 total_frames += read # increment total number of frames
12 if read < hop_size: break # end of file reached16 if read < hop_size: # end of file reached
17 break
13 18
14fmt_string = "read {:d} frames at {:d}Hz from {:s}"19fmt_string = "read {:d} frames at {:d}Hz from {:s}"
15print (fmt_string.format(total_frames, s.samplerate, sys.argv[1]))20print(fmt_string.format(total_frames, src.samplerate, src.uri))
16
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
5Note you will need to have pyaudio installed: `pip install pyaudio`.
6
7Examples:
8 ./demo_tapthebeat.py ~/Music/track1.ogg
9
10When compiled with ffmpeg/libav, you should be able to open remote streams. For
11instance using youtube-dl (`pip install youtube-dl`):
12
13 ./demo_tapthebeat.py `youtube-dl -xg https://youtu.be/zZbM9n9j3_g`
14
15"""
16
17import sys
18import time
19import pyaudio
20import aubio
21import numpy as np
22
23win_s = 1024 # fft size
24hop_s = win_s // 2 # hop size
25
26# parse command line arguments
27if len(sys.argv) < 2:
28 print("Usage: %s <filename> [samplerate]" % sys.argv[0])
29 sys.exit(1)
30
31filename = sys.argv[1]
32
33samplerate = 0
34if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
35
36# create aubio source
37a_source = aubio.source(filename, samplerate, hop_s)
38samplerate = a_source.samplerate
39
40# create aubio tempo detection
41a_tempo = aubio.tempo("default", win_s, hop_s, samplerate)
42
43# create a simple click sound
44click = 0.7 * np.sin(2. * np.pi * np.arange(hop_s) / hop_s * samplerate / 3000.)
45
46# pyaudio callback
47def 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
59p = pyaudio.PyAudio()
60pyaudio_format = pyaudio.paFloat32
61frames_per_buffer = hop_s
62n_channels = 1
63stream = 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
68stream.start_stream()
69
70# wait for stream to finish
71while stream.is_active():
72 time.sleep(0.1)
73
74# stop pyaudio stream
75stream.stop_stream()
76stream.close()
77# close pyaudio
78p.terminate()
python/demos/demo_timestretch.py+1−1
@@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines
12import numpy as np12import numpy as np
13 13
14win_s = 102414win_s = 1024
15hop_s = win_s / 8 # 87.5 % overlap15hop_s = win_s // 8 # 87.5 % overlap
16 16
17warmup = win_s // hop_s - 117warmup = win_s // hop_s - 1
18 18
python/demos/demo_timestretch_online.py+5−3
@@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines
11from aubio import unwrap2pi, float_type11from aubio import unwrap2pi, float_type
12import numpy as np12import numpy as np
13 13
14win_s = 102414win_s = 512
15hop_s = win_s / 8 # 87.5 % overlap15hop_s = win_s // 8 # 87.5 % overlap
16 16
17warmup = win_s // hop_s - 117warmup = 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 += 195 # until end of file
96 if read < hop_s: break96 if read < hop_s: break
97 # increment block counter
98 block_read += 1
97 99
98for t in range(warmup + 2): # purge the last frames from the phase vocoder100for t in range(warmup + 2): # purge the last frames from the phase vocoder
99 new_grain.norm[:] = 0101 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
10import sys
11from aubio import source, notes
12from mido import Message, MetaMessage, MidiFile, MidiTrack, second2tick, bpm2tempo
13
14if len(sys.argv) < 3:
15 print("Usage: %s <filename> <output> [samplerate]" % sys.argv[0])
16 sys.exit(1)
17
18filename = sys.argv[1]
19midioutput = sys.argv[2]
20
21downsample = 1
22samplerate = 44100 // downsample
23if len( sys.argv ) > 3: samplerate = int(sys.argv[3])
24
25win_s = 512 // downsample # fft size
26hop_s = 256 // downsample # hop size
27
28s = source(filename, samplerate, hop_s)
29samplerate = s.samplerate
30
31tolerance = 0.8
32
33notes_o = notes("default", win_s, hop_s, samplerate)
34
35print("%8s" % "time","[ start","vel","last ]")
36
37# create a midi file
38mid = MidiFile()
39track = MidiTrack()
40mid.tracks.append(track)
41
42ticks_per_beat = mid.ticks_per_beat # default: 480
43bpm = 120 # default midi tempo
44
45tempo = bpm2tempo(bpm)
46track.append(MetaMessage('set_tempo', tempo=tempo))
47track.append(MetaMessage('time_signature', numerator=4, denominator=4))
48
49def frames2tick(frames, samplerate=samplerate):
50 sec = frames / float(samplerate)
51 return int(second2tick(sec, ticks_per_beat, tempo))
52
53last_time = 0
54
55# total number of frames read
56total_frames = 0
57while 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
77mid.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
16import sys
17import numpy as np
18import matplotlib.pyplot as plt
19
20def 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
32def 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
57def 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
69def 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
95def 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
108def 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
130def 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
151testfreqs = [441., 800., 10000., 40.]
152
153if len(sys.argv) > 1:
154 testfreqs = map(float,sys.argv[1:])
155
156for 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)
175plt.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