1# rubber.rb -- Translation of rubber.scm 2 3# Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net> 4# Created: 03/02/28 03:04:03 5# Changed: 14/11/23 06:14:52 6 7# module Rubber (see rubber.scm) 8# add_named_mark(samp, name, snd, chn) 9# derumble_sound(snd, chn) 10# sample_sound(snd, chn) 11# unsample_sound(snd, chn) 12# crossings() 13# env_add(s0, s1, samps) 14# rubber_sound(stretch, snd, chn) 15 16require "clm" 17 18module Rubber 19 doc "#{self.class} #{self.name} is the translation of rubber.scm.\n" 20 21 $zeros_checked = 8 22 $extension = 10.0 23 $show_details = false 24 25 def add_named_mark(samp, name, snd = false, chn = false) 26 m = add_mark(samp.round, snd, chn) 27 set_mark_name(m, name) 28 m 29 end 30 31 def derumble_sound(snd = false, chn = false) 32 old_length = framples(snd, chn) 33 pow2 = (log([old_length, srate(snd)].min) / log(2)).ceil 34 fftlen = (2 ** pow2).round 35 flt_env = [0.0, 0.0, 32.0 / srate(snd), 0.0, 36 40.0 / srate(snd), 1.0, 1.0, 1.0] 37 filter_sound(flt_env, fftlen, snd, chn) 38 set_framples(old_length, snd, chn) 39 end 40 41 def sample_sound(snd = false, chn = false) 42 if $extension != 1.0 43 src_sound(1.0 / $extension, 1.0, snd, chn) 44 end 45 end 46 47 def unsample_sound(snd = false, chn = false) 48 if $extension != 1.0 49 src_sound($extension, 1.0, snd, chn) 50 end 51 end 52 53 def crossings 54 crosses = 0 55 sr0 = make_sampler(0) 56 samp0 = next_sample(sr0) 57 len = framples() 58 sum = 0.0 59 last_cross = 0 60 silence = $extension * 0.001 61 (0...len).each do |i| 62 samp1 = next_sample(sr0) 63 if samp0 <= 0.0 and 64 samp1 > 0.0 and 65 (i - last_cross) > 4 and 66 sum > silence 67 crosses += 1 68 last_cross = i 69 sum = 0.0 70 end 71 sum += samp0.abs 72 samp0 = samp1 73 end 74 crosses 75 end 76 77 def env_add(s0, s1, samps) 78 data = make_vct(samps.round) 79 x = 1.0 80 xinc = 1.0 / samps 81 sr0 = make_sampler(s0.round) 82 sr1 = make_sampler(s1.round) 83 (0...samps).each do |i| 84 data[i] = x * next_sample(sr0) + (1.0 - x) * next_sample(sr1) 85 x += xinc 86 end 87 data 88 end 89 90 def rubber_sound(stretch, snd = false, chn = false) 91 as_one_edit(lambda do | | 92 derumble_sound(snd, chn) 93 sample_sound(snd, chn) 94 crosses = crossings() 95 cross_samples = make_vct(crosses) 96 cross_weights = make_vct(crosses) 97 cross_marks = make_vct(crosses) 98 cross_periods = make_vct(crosses) 99 sr0 = make_sampler(0, snd, chn) 100 samp0 = next_sample(sr0) 101 len = framples() 102 sum = 0.0 103 last_cross = 0 104 cross = 0 105 silence = $extension * 0.001 106 (0...len).each do |i| 107 samp1 = next_sample(sr0) 108 if samp0 <= 0.0 and 109 samp1 > 0.0 and 110 (i - last_cross) > 4 and 111 sum > silence 112 last_cross = i 113 sum = 0.0 114 cross_samples[cross] = i 115 cross += 1 116 end 117 sum += samp0.abs 118 samp0 = samp1 119 end 120 (0...(crosses - 1)).each do |i| 121 start = cross_samples[i] 122 autolen = 0 123 s0 = start 124 pow2 = (log($extension * (srate() / 40.0)) / log(2)).ceil 125 fftlen = (2 ** pow2).round 126 len4 = fftlen / 4 127 data = make_vct(fftlen) 128 reader = make_sampler(s0.round) 129 (0...fftlen).each do |j| 130 data[j] = next_sample(reader) 131 end 132 autocorrelate(data) 133 autolen = 0 134 (1...len4).detect do |j| 135 if data[j] < data[j + 1] and data[j + 1] > data[j + 2] 136 autolen = j * 2 137 true 138 end 139 end 140 next_start = start + autolen 141 min_i = i + 1 142 min_samps = (cross_samples[min_i] - next_start).abs 143 ((i + 2)...[crosses, i + $zeros_checked].min).each do |k| 144 dist = (cross_samples[k] - next_start).abs 145 if dist < min_samps 146 min_samps = dist 147 min_i = k 148 end 149 end 150 current_mark = min_i 151 current_min = 0.0 152 s0 = start 153 s1 = cross_samples[current_mark] 154 len = autolen 155 sr0 = make_sampler(s0.round) 156 sr1 = make_sampler(s1.round) 157 ampsum = 0.0 158 diffsum = 0.0 159 (0...len).each do |dummy| 160 samp0 = next_sample(sr0) 161 samp1 = next_sample(sr1) 162 ampsum += samp0.abs 163 diffsum += (samp1 - samp0).abs 164 end 165 if diffsum == 0.0 166 current_min = 0.0 167 else 168 current_min = diffsum / ampsum 169 end 170 min_samps = 0.5 * current_min 171 top = [crosses - 1, current_mark, i + $zeros_checked].min 172 ((i + 1)...top).each do |k| 173 wgt = 0.0 174 s0 = start 175 s1 = cross_samples[k] 176 len = autolen 177 sr0 = make_sampler(s0.round) 178 sr1 = make_sampler(s1.round) 179 ampsum = 0.0 180 diffsum = 0.0 181 (0...len).each do |dummy| 182 samp0 = next_sample(sr0) 183 samp1 = next_sample(sr1) 184 ampsum += samp0.abs 185 diffsum += (samp1 - samp0).abs 186 end 187 if diffsum == 0.0 188 wgt = 0.0 189 else 190 wgt = diffsum / ampsum 191 end 192 if wgt < min_samps 193 min_samps = wgt 194 min_i = k 195 end 196 end 197 unless current_mark == min_i 198 cross_weights[i] = 1000.0 199 else 200 cross_weights[i] = current_min 201 cross_marks[i] = current_mark 202 cross_periods[i] = cross_samples[current_mark] - 203 cross_samples[i] 204 end 205 end 206 len = framples(snd, chn) 207 adding = (stretch > 1.0) 208 samps = ((stretch - 1.0).abs * len).round 209 needed_samps = (adding ? samps : [len, samps * 2].min) 210 handled = 0 211 mult = 1 212 curs = 0 213 weigths = cross_weights.length 214 edits = make_vct(weigths) 215 until curs == weigths or handled >= needed_samps 216 best_mark = -1 217 old_handled = handled 218 cur = 0 219 curmin = cross_weights[0] 220 cross_weights.each_with_index do |cross_w, i| 221 if cross_w < curmin 222 cur = i 223 curmin = cross_w 224 end 225 end 226 best_mark = cur 227 handled += cross_periods[best_mark].round 228 if (handled < needed_samps) or 229 ((handled - needed_samps) < (needed_samps - old_handled)) 230 edits[curs] = best_mark 231 curs += 1 232 end 233 cross_weights[best_mark] = 1000.0 234 end 235 mult = (needed_samps / handled).ceil if curs >= weigths 236 changed_len = 0 237 weights = cross_weights.length 238 (0...curs).detect do |i| 239 best_mark = edits[i].round 240 beg = cross_samples[best_mark].to_i 241 next_beg = cross_samples[cross_marks[best_mark].round] 242 len = cross_periods[best_mark].to_i 243 if len > 0 244 if adding 245 new_samps = env_add(beg, next_beg, len) 246 if $show_details 247 add_named_mark(beg, 248 format("%d:%d", 249 i, (len / $extension).round)) 250 end 251 insert_samples(beg, len, new_samps) 252 if mult > 1 253 (1...mult).each do |k| 254 insert_samples(beg + k * len, len, new_samps) 255 end 256 end 257 changed_len = changed_len + mult * len 258 (0...weights).each do |j| 259 curbeg = cross_samples[j] 260 if curbeg > beg 261 cross_samples[j] = curbeg + len 262 end 263 end 264 else 265 frms = framples() 266 if beg >= frms 267 Snd.display("trouble at %d: %d of %d", i, beg, frms) 268 end 269 if $show_details 270 add_named_mark(beg - 1, 271 format("%d:%d", 272 i, (len / $extension).round)) 273 end 274 delete_samples(beg, len) 275 changed_len += len 276 fin = beg + len 277 (0...weights).each do |j| 278 curbeg = cross_samples[j] 279 if curbeg > beg 280 if curbeg < fin 281 cross_periods[j] = 0 282 else 283 cross_samples[j] = curbeg - len 284 end 285 end 286 end 287 end 288 end 289 changed_len > samps 290 end 291 if $show_details 292 Snd.display("wanted: %d, got %d", 293 samps.round, changed_len.round) 294 end 295 unsample_sound(snd, chn) 296 if $show_details 297 frms0 = framples(snd, chn, 0) 298 Snd.display("%f -> %f (%f)", 299 frms0, 300 framples(snd,chn), 301 (stretch * frms0).round) 302 end 303 end, 304 format("rubber_sound(%f, %p, %p,", stretch, snd, chn)) 305 end 306end 307 308include Rubber 309 310# rubber.rb ends here 311