# rubber.rb -- Translation of rubber.scm
# Translator/Author: Michael Scholz <scholz-micha@gmx.de>
# Last: Tue Mar 18 02:35:11 CET 2003
# Version: $Revision: 1.2 $
# module Rubber (see rubber.scm)
# add_named_mark(samp, name, snd, chn)
# derumble_sound(*args)
# sample_sound(*args)
# unsample_sound(*args)
# crossings()
# env_add(s0, s1, samps)
# rubber_sound(*args)
require "examp"
module Rubber
doc "#{self.class} #{self.name} is the translation of rubber.scm.\n"
$zeros_checked = 8
$extension = 10.0
$show_details = false
def add_named_mark(samp, name, snd = false, chn = false)
m = add_mark(samp.round, snd, chn)
set_mark_name(m, name)
m
end
def derumble_sound(*args)
snd = (args[0] or false)
chn = (args[1] or false)
old_length = frames(snd, chn)
pow2 = (log([old_length, srate(snd)].min) / log(2)).ceil
fftlen = (2 ** pow2).round
flt_env = [0.0, 0.0, 32.0 / srate(snd), 0.0, 40.0 / srate(snd), 1.0, 1.0, 1.0]
filter_sound(flt_env, fftlen, snd, chn)
set_frames(old_length, snd, chn)
end
def sample_sound(*args)
snd = (args[0] or false)
chn = (args[1] or false)
src_sound(1.0 / $extension, 1.0, snd, chn) unless $extension == 1.0
end
def unsample_sound(*args)
snd = (args[0] or false)
chn = (args[1] or false)
src_sound($extension, 1.0, snd, chn) unless $extension == 1.0
end
def crossings
crosses = 0
sr0 = make_sample_reader(0)
samp0 = next_sample(sr0)
len = frames()
sum = 0.0
last_cross = 0
silence = $extension * 0.001
(0...len).each do |i|
samp1 = next_sample(sr0)
if samp0 <= 0.0 and samp1 > 0.0 and (i - last_cross) > 4 and sum > silence
crosses += 1
last_cross = i
sum = 0.0
end
sum += samp0.abs
samp0 = samp1
end
crosses
end
def env_add(s0, s1, samps)
data = make_vct(samps.round)
x = 1.0
xinc = 1.0 / samps
sr0 = make_sample_reader(s0.round)
sr1 = make_sample_reader(s1.round)
(0...samps).each do |i|
data[i] = x * next_sample(sr0) + (1.0 - x) * next_sample(sr1)
x += xinc
end
data
end
def rubber_sound(*args)
stretch = args[0]
snd = (args[1] or false)
chn = (args[2] or false)
as_one_edit(lambda do | |
derumble_sound(snd, chn)
sample_sound(snd, chn)
crosses = crossings()
cross_samples = make_vct(crosses)
cross_weights = make_vct(crosses)
cross_marks = make_vct(crosses)
cross_periods = make_vct(crosses)
sr0 = make_sample_reader(0, snd, chn)
samp0 = next_sample(sr0)
len = frames()
sum = 0.0
last_cross = 0
cross = 0
silences = 0
silence = $extension * 0.001
(0...len).each do |i|
samp1 = next_sample(sr0)
if samp0 <= 0.0 and samp1 > 0.0 and (i - last_cross) > 4 and sum > silence
last_cross = i
sum = 0.0
cross_samples[cross] = i
cross += 1
end
sum += samp0.abs
samp0 = samp1
end
(0...(crosses - 1)).each do |i|
start = cross_samples[i]
autolen = 0
s0 = start
pow2 = (log($extension * (srate() / 40.0)) / log(2)).ceil
fftlen = (2 ** pow2).round
len4 = fftlen / 4
data = make_vct(fftlen)
reader = make_sample_reader(s0.round)
(0...fftlen).each do |j| data[j] = next_sample(reader) end
autocorrelate(data)
autolen = 0
(1...len4).each do |j|
if data[j] < data[j + 1] and data[j + 1] > data[j + 2]
autolen = j * 2
break
end
end
next_start = start + autolen
min_i = i + 1
min_samps = (cross_samples[min_i] - next_start).abs
((i + 2)...[crosses, i + $zeros_checked].min).each do |k|
dist = (cross_samples[k] - next_start).abs
if dist < min_samps
min_samps = dist
min_i = k
end
end
current_mark = min_i
current_min = 0.0
s0 = start
s1 = cross_samples[current_mark]
len = autolen
sr0 = make_sample_reader(s0.round)
sr1 = make_sample_reader(s1.round)
ampsum = 0.0
diffsum = 0.0
(0...len).each do |dummy|
samp0 = next_sample(sr0)
samp1 = next_sample(sr1)
ampsum += samp0.abs
diffsum += (samp1 - samp0).abs
end
if diffsum == 0.0
current_min = 0.0
else
current_min = diffsum / ampsum
end
min_samps = 0.5 * current_min
top = [crosses - 1, current_mark, i + $zeros_checked].min
((i + 1)...top).each do |k|
wgt = 0.0
s0 = start
s1 = cross_samples[k]
len = autolen
sr0 = make_sample_reader(s0.round)
sr1 = make_sample_reader(s1.round)
ampsum = 0.0
diffsum = 0.0
(0...len).each do |dummy|
samp0 = next_sample(sr0)
samp1 = next_sample(sr1)
ampsum += samp0.abs
diffsum += (samp1 - samp0).abs
end
if diffsum == 0.0
wgt = 0.0
else
wgt = diffsum / ampsum
end
if wgt < min_samps
min_samps = wgt
min_i = k
end
end
unless current_mark == min_i
cross_weights[i] = 1000.0
else
cross_weights[i] = current_min
cross_marks[i] = current_mark
cross_periods[i] = cross_samples[current_mark] - cross_samples[i]
end
end
len = frames(snd, chn)
adding = (stretch > 1.0)
samps = ((stretch - 1.0).abs * len).round
needed_samps = (adding ? samps : [len, samps * 2].min)
handled = 0
mult = 1
curs = 0
weigths = cross_weights.length
edits = make_vct(weigths)
until curs == weigths or handled >= needed_samps
best_mark = -1
old_handled = handled
cur = 0
curmin = cross_weights[0]
cross_weights.each_with_index do |cross_w, i|
if cross_w < curmin
cur = i
curmin = cross_w
end
end
best_mark = cur
handled += cross_periods[best_mark].round
if (handled < needed_samps) or
((handled - needed_samps) < (needed_samps - old_handled))
edits[curs] = best_mark
curs += 1
end
cross_weights[best_mark] = 1000.0
end
mult = (needed_samps / handled).ceil if curs >= weigths
changed_len = 0
weights = cross_weights.length
(0...curs).each do |i|
break if changed_len > samps
best_mark = edits[i].round
beg = cross_samples[best_mark]
next_beg = cross_samples[cross_marks[best_mark].round]
len = cross_periods[best_mark]
if len > 0
if adding
new_samps = env_add(beg, next_beg, len)
if $show_details
add_named_mark(beg, format("%d:%d", i, (len / $extension).round))
end
insert_samples(beg, len, new_samps)
if mult > 1
(1...mult).each do |k| insert_samples(beg + k * len, len, new_samps) end
end
changed_len += mult * len
(0...weights).each do |j|
curbeg = cross_samples[j]
if curbeg > beg
cross_samples[j] = curbeg + len
end
end
else
message("trouble at %d: %d of %d", i, beg, frames()) if beg >= frames()
if $show_details
add_named_mark(beg - 1, format("%d:%d", i, (len / $extension).round))
end
delete_samples(beg, len)
changed_len += len
fin = beg + len
(0...weights).each do |j|
curbeg = cross_samples[j]
if curbeg > beg
if curbeg < fin
cross_periods[j] = 0
else
cross_samples[j] = curbeg - len
end
end
end
end
end
end
message("wanted: %d, got %d", samps.round, changed_len.round) if $show_details
unsample_sound(snd, chn)
if $show_details
message("%f -> %f (%f)",
frames(snd, chn, 0), frames(snd,chn),
(stretch * frames(snd, chn, 0)).round)
end
end, "(rubber_sound(#{args.join(', ')})")
end
end
# rubber.rb ends here