|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- # pvoc.rb -- pvoc.scm -> pvoc.rb
-
- # Translator: Michael Scholz <mi-scholz@users.sourceforge.net>
- # Created: 04/03/27 00:19:51
- # Changed: 14/11/14 08:58:16
-
- # versions of the Moore-Klingbeil-Trevisani-Edwards phase-vocoder
- #
- # class Pvocoder
- # initialize(fftsize, overlap, interp, analyze, edit, synthesize)
- # inspect
- # pvocoder(input)
- #
- # make_pvocoder(fftsize, overlap, interp, analyze, edit, synthesize)
- # pvocoder(pv, input)
- #
- # test_pv_1
- # test_pv_2(freq)
- # test_pv_3(time)
- # test_pv_4(gate)
- #
- # pvoc(*rest)
-
- require "ws"
-
- class Pvocoder
- def initialize(fftsize, overlap, interp, analyze, edit, synthesize)
- @output = interp
- @interp = interp
- @hop = overlap
- @filptr = 0
- @N = fftsize
- @window = make_fft_window(Hamming_window, fftsize)
- @window.scale!(2.0 / (0.54 * fftsize))
- @D = fftsize / overlap
- @in_data = nil
- @ampinc = make_vct(fftsize)
- @freqs = make_vct(fftsize)
- @amps = make_vct(fftsize / 2)
- @phaseinc = make_vct(fftsize / 2)
- @phases = make_vct(fftsize / 2)
- @lastphase = make_vct(fftsize / 2)
- @analyze = analyze
- @edit = edit
- @synthesize = synthesize
- end
-
- def inspect
- format("#<%s outctr: %d, interp: %d, \
- filptr: %d, N: %d, D: %d, in_data: %p>",
- self.class, @output, @interp, @filptr, @N, @D, @in_data)
- end
-
- def pvocoder(input)
- if @output >= @interp
- if @analyze
- @analyze.call(self, input)
- else
- vct_fill!(@freqs, 0.0)
- @output = 0
- if (not vct?(@in_data))
- @in_data = make_vct!(@N) do
- input.call
- end
- else
- vct_move!(@in_data, 0, @D)
- ((@N - @D)...@N).each do |i|
- @in_data[i] = input.call
- end
- end
- buf = @filptr % @N
- if buf.zero?
- vct_fill!(@ampinc, 0.0)
- vct_add!(@ampinc, @in_data)
- vct_multiply!(@ampinc, @window)
- else
- @N.times do |k|
- @ampinc[buf] = @window[k] * @in_data[k]
- buf += 1
- if buf >= @N
- buf = 0
- end
- end
- end
- @filptr += @D
- mus_fft(@ampinc, @freqs, @N, 1)
- rectangular2polar(@ampinc, @freqs)
- end
- if @edit
- @edit.call(self)
- else
- pscl = 1.0 / @D
- kscl = TWO_PI / @N
- (@N / 2).times do |k|
- phasediff = @freqs[k] - @lastphase[k]
- @lastphase[k] = @freqs[k]
- while phasediff > PI
- phasediff -= TWO_PI
- end
- while phasediff < -TWO_PI
- phasediff += TWO_PI
- end
- @freqs[k] = pscl * phasediff + k * kscl
- end
- end
- scl = 1.0 / @interp
- vct_subtract!(@ampinc, @amps)
- vct_subtract!(@freqs, @phaseinc)
- vct_scale!(@ampinc, scl)
- vct_scale!(@freqs, scl)
- end
- @output += 1
- if @synthesize
- @synthesize.call
- else
- vct_add!(@amps, @ampinc)
- vct_add!(@phaseinc, @freqs)
- vct_add!(@phases, @phaseinc)
- sine_bank(@amps, @phases)
- end
- end
- end
-
- add_help(:make_pvocoder,
- "make_pvocoder(fftsize, overlap, interp, analyze=false, \
- edit=false, synthesize=false) \
- Makes a new (Ruby-based, not CLM) phase-vocoder generator.")
- def make_pvocoder(fftsize = 512,
- overlap = 4,
- interp = 128,
- analyze = false,
- edit = false,
- synthesize = false)
- Pvocoder.new(fftsize, overlap, interp, analyze, edit, synthesize)
- end
-
- add_help(:pvocoder,
- "pvocoder(pv, input) \
- Is the phase-vocoder generator associated with make_pvocoder.")
- def pvocoder(pv, input)
- pv.pvocoder(input)
- end
-
- =begin
- let(open_sound("oboe.snd"),
- make_pvocoder(256, 4, 64),
- make_sampler(0)) do |ind, pv, rd|
- map_channel(lambda do |y| pvocoder(pv, rd) end)
- play(ind, :wait, true)
- save_sound_as("pvoc.snd", ind)
- revert_sound(ind)
- close_sound(ind)
- open_sound("pvoc.snd")
- end
- =end
-
- def test_pv_1
- pv = make_phase_vocoder(false, 512, 4, 128, 1.0, false, false, false)
- rd = make_sampler(0)
- map_channel(lambda do |y|
- phase_vocoder(pv,
- lambda do |dir|
- next_sample(rd)
- end)
- end)
- free_sampler(rd)
- end
-
- def test_pv_2(freq)
- pv = make_phase_vocoder(false, 512, 4, 128, freq, false, false, false)
- rd = make_sampler(0)
- map_channel(lambda do |y|
- phase_vocoder(pv,
- lambda do |dir|
- next_sample(rd)
- end)
- end)
- free_sampler(rd)
- end
-
- def test_pv_3(time)
- pv = make_phase_vocoder(false, 512, 4, (time * 128.0).floor,
- 1.0, false, false, false)
- rd = make_sampler(0)
- len = (time * framples()).floor
- data = make_vct!(len) do
- phase_vocoder(pv,
- lambda do |dir|
- next_sample(rd)
- end)
- end
- free_sampler(rd)
- vct2channel(data, 0, len)
- end
-
- def test_pv_4(gate)
- pv = make_phase_vocoder(false,
- 512, 4, 128, 1.0,
- false,
- lambda do |v|
- phase_vocoder_amp_increments(v).map! do |val|
- if val < gate
- 0.0
- else
- val
- end
- true
- end
- end, false)
- rd = make_sampler(0)
- map_channel(lambda do |y|
- phase_vocoder(pv,
- lambda do |dir|
- next_sample(rd)
- end)
- end)
- free_sampler(rd)
- end
-
- # another version of the phase vocoder
-
- add_help(:pvoc,
- "pvoc(*rest)
- :fftsize = 512
- :overlap = 4
- :time = 1.0
- :pitch = 1.0
- :gate = 0.0
- :hoffset = 0.0
- :snd = false
- :chn = false
- Applies the phase vocoder algorithm to the current sound (i.e. fft analysis, \
- oscil bank resynthesis). \
- TIME specifies the time dilation ratio, \
- PITCH specifies the pitch transposition ratio, \
- GATE specifies a resynthesis gate in dB (partials with \
- amplitudes lower than the gate value will not be synthesized), \
- HOFFSET is a pitch offset in Hz.")
- def pvoc(*rest)
- fftsize, overlap, time, pitch, gate, hoffset, snd, chn = nil
- optkey(rest, binding,
- [:fftsize, 512],
- [:overlap, 4],
- [:time, 1.0],
- [:pitch, 1.0],
- [:gate, 0.0],
- [:hoffset, 0.0],
- [:snd, false],
- [:chn, false])
- len = framples(snd, chn)
- filptr = 0
- sr = srate(snd)
- fftsize2 = (fftsize / 2.0).floor
- d = fftsize / overlap
- interp = d * time
- syngate = gate.zero? ? 0.0 : (10 ** (-gate.abs / 20.0))
- poffset = hz2radians(hoffset)
- window = make_fft_window(Hamming_window, fftsize)
- fdr = make_vct(fftsize)
- fdi = make_vct(fftsize)
- lastphase = make_vct(fftsize2)
- lastamp = make_vct(fftsize2)
- lastfreq = make_vct(fftsize2)
- ampinc = make_vct(fftsize2)
- freqinc = make_vct(fftsize2)
- fundamental = TWO_PI / fftsize
- output = interp
- # resynth_oscils = make_array(fftsize2) do
- # make_oscil(:frequency, 0)
- # end
- outlen = (time * len).floor
- in_data = channel2vct(0, fftsize * 2, snd, chn)
- in_data_beg = 0
- vct_scale!(window, 2.0 / (0.54 * fftsize))
- obank = make_oscil_bank(lastfreq, make_vct(fftsize2, 0.0), lastamp)
- out_data = make_vct([len, outlen].max)
- out_data.length.times do |i|
- if output >= interp
- output = 0
- buffix = filptr % fftsize
- vct_fill!(lastamp, 0.0)
- vct_fill!(lastfreq, 0.0)
- vct_add!(lastamp, fdr)
- vct_add!(lastfreq, fdi)
- fftsize.times do |k|
- fdr[buffix] = window[k] * in_data[filptr - in_data_beg]
- filptr += 1
- buffix += 1
- if buffix >= fftsize
- buffix = 0
- end
- end
- filptr -= fftsize - d
- if filptr > in_data_beg + fftsize
- in_data_beg = filptr
- in_data = channel2vct(in_data_beg, fftsize * 2, snd, chn)
- end
- vct_fill!(fdi, 0.0)
- mus_fft(fdr, fdi, fftsize, 1)
- fftsize2.times do |k|
- a = fdr[k]
- b = fdi[k]
- mag = sqrt(a * a + b * b)
- phase = 0
- phasediff = 0
- fdr[k] = mag
- if mag > 0
- phase = -atan2(b, a)
- phasediff = phase - lastphase[k]
- lastphase[k] = phase
- while phasediff > PI
- phasediff -= TWO_PI
- end
- while phasediff < -PI
- phasediff += TWO_PI
- end
- end
- fdi[k] = pitch *
- ((phasediff * sr) / (d * sr) + k * fundamental + poffset)
- if fdr[k] < syngate
- fdr[k] = 0.0
- end
- ampinc[k] = (fdr[k] - lastamp[k]) / interp
- freqinc[k] = (fdi[k] - lastfreq[k]) / interp
- end
- end
- output += 1
- vct_add!(lastamp, ampinc)
- vct_add!(lastfreq, freqinc)
- # old_oscil_bank from extensions.rb
- # out_data[i] = old_oscil_bank(lastamp, resynth_oscils, lastfreq)
- out_data[i] = oscil_bank(obank)
- end
- vct2channel(out_data, 0, out_data.length)
- end
-
- # pvoc.rb ends here
|