1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
class:: PV_ExtractRepeat
summary:: extract a repeating loop out from audio
categories:: UGens>FFT, UGens>Analysis
Description::
If you have a signal that consists of some fixed looping audio (e.g. a beat) plus some varying content (e.g. singing), this UGen uses a simple binary-masking technique to try and separate the looping bit from the non-looping bit. The quality of the separation is quite rough, but useful in some circumstances.
Note that this unit estimates the loop characteristics online in real time, meaning it takes a few times through the loop before the separation really kicks in. This means that at any point, any novel element is included in the nonrepeating part, even if it's really a loop that's starting.
You must know the loop duration - this unit will not estimate it for you.
classmethods::
method::new
argument::array
The array of input signals.
argument::chain
an fft chain
argument::loopbuf
a buffer where data about the loop is calculated/stored. num frames should be enough to hold the loop (you'll get a warning if not), num channels should be (fftsize/2 + 1).
argument::loopdur
duration in seconds of the bit you want extracted. (You can change the loop duration on-the-fly but you'll get some unhelpful results while the unit settles in to the new loop duration.)
argument::memorytime
how quickly (in seconds) the recursive estimation converges
argument::which
set to 0 to extract the loop, set to 1 to extract everything else
argument::ffthop
this should match the hop size used in the FFT. typically 0.5.
argument::thresh
threshold for allocating bins to repeating/nonrepeating. Default is 1, and raising it means more gets allocated to the repeating part.
Examples::
First a toy example with synthetic material; then an example reading a WAV file from disk (you'll have to find one, and also find its bpm).
code::
s.boot
// Create audio containing repeating and varying components, merged in the same channel:
(
x = {
var trigs, looplen = 2, loopy, varying;
trigs = Impulse.kr(6/looplen) + Impulse.kr(4/looplen);
loopy = BPF.ar(WhiteNoise.ar * EnvGen.ar(Env.perc(0.01, 0.3), trigs), LFCub.kr(1/looplen).exprange(100, 5000));
varying = Pulse.ar(LFNoise0.kr(6/looplen).exprange(100, 500).cpsmidi.round.midicps) * 0.1;
(loopy + varying).dup
}.play;
)
// Now the thing that will attempt to separate them out again (inc a buffer for it to use):
~loopbuf = Buffer.alloc(s, 200, 513);
(
y = { |which=1, active=1|
var son = In.ar(0);
var chain = FFT(LocalBuf(1024), son);
chain = PV_ExtractRepeat(chain, ~loopbuf, 2, memorytime:30, which:which);
ReplaceOut.ar(0, Select.ar(active, [son, IFFT(chain)]).dup);
}.play(x, addAction: \addAfter);
)
y.set(\which, 0) // focus on the nonrepeating bit
y.set(\which, 1) // focus on the repeating bit
y.set(\active, 0) // back to normal
y.set(\active, 1) // filter it again
y.free;
x.free;
//////////////////////////////////////
// With music:
~track = "~/tmpwavs/Postal Service - Such Great Heights.mp3.2.wav".standardizePath; ~bpm=116.6;
~track = "~/tmpwavs/Amy Winehouse - Rehab.mp3.wav".standardizePath; ~bpm=145.1;
~looplen = 480/~bpm;
b = Buffer.cueSoundFile(s, ~track, 0, 2);
x = { DiskIn.ar(2, b) }.play;
// Now the thing that will attempt to separate them out again (inc a buffer for it to use):
~loopbuf = Buffer.alloc(s, 1000, 513);
(
y = { |which=1, active=1, thresh=1|
var son = In.ar(0);
var chain = FFT(LocalBuf(1024), son);
chain = PV_ExtractRepeat(chain, ~loopbuf, ~looplen, memorytime:30, which:which, thresh:thresh);
ReplaceOut.ar(0, Select.ar(active, [son, IFFT(chain)]).dup);
}.play(x, addAction: \addAfter);
)
y.set(\which, 0) // focus on the nonrepeating bit
y.set(\which, 1) // focus on the repeating bit
y.set(\active, 0) // back to normal
y.set(\active, 1) // filter it again
y.set(\which, 0, \thresh, 1.5) // trying a higher threshold
y.free;
x.free;
b.close
::
This unit is (c) Dan Stowell, based on the technique presented in this paper (but different - adapted for online estimation and with some other little differences):
Zafar Rafii and Bryan Pardo. "A Simple Music/Voice Separation Method based on the Extraction of the Repeating Musical Structure," 36th International Conference on Acoustics, Speech and Signal Processing (ICASSP 2011), Prague, Czech Republic, May 22-27, 2011.
http://www.cs.northwestern.edu/~zra446/research.html
|