Hax0Ring The Rubberband

Suva’s inbox seems to be full? “this member can’t receive private messages” or something like that.

so here’s my pm, plus code that should actually work (but surely will contain bugs, the usual precautions apply):


hey there!

I had a quick (and probably sloppy) go to see if I could make your rubberband plugin apply only to the current selection, and it seems to work!

well, the temporary sample creation feels a bit dogdy, and I yet have no idea how to make the new sample have the same properties as the old one. but as a proof of concept it’s okay.

one other thing I really would like to do is to make it non modal, and allow getting the durations for timestretching from the current selection. oh, and remove the loop if the original sample didn’t have one. so I’d like to know if you’re currently working on this, if I should wait until your update etc.

oh, here goes the actual stuff, just replace that function and try it on vocal samples, it’s kinda cool.

[details=“Click to view contents”] ```lua
function process_rubberband(cmd)
local exe

if os.platform() == ‘WINDOWS’ then
exe = ‘"’ … renoise.tool().bundle_path … ‘bin/win32/rubberband.exe"’
elseif os.platform() == ‘MACINTOSH’ then
exe = ‘"’ … renoise.tool().bundle_path … ‘bin/osx/rubberband"’
else
exe = ‘rubberband’
end

local ofile = os.tmpname(‘wav’)
local ifile = os.tmpname(‘wav’)
local xfile = os.tmpname(‘wav’)
local buffer = renoise.song().selected_sample.sample_buffer

– create temporary sample to hold the selection
local instrument = renoise.song().selected_instrument
local temp_sample = renoise.song().selected_instrument:insert_sample_at(renoise.song().selected_sample_index + 1)
local temp_buffer = temp_sample.sample_buffer
local temp_sample2 = renoise.song().selected_instrument:insert_sample_at(renoise.song().selected_sample_index + 1)
local temp_buffer2 = temp_sample2.sample_buffer
if (not temp_buffer:create_sample_data(buffer.sample_rate, buffer.bit_depth, buffer.number_of_channels, (buffer.selection_end - buffer.selection_start) + 1 )) then
renoise.app():show_error(“Couldn’t create sample!”) return
end
local int_frame
local int_chan
local int_new_frame

– copy the selection
for int_chan = 1, buffer.number_of_channels do
int_new_frame = 1
for int_frame = buffer.selection_start, buffer.selection_end do
temp_buffer:set_sample_data(int_chan, int_new_frame, buffer:sample_data(int_chan, int_frame))
int_new_frame = int_new_frame + 1
end
end

– save the temporary sample
temp_buffer:save_as(ofile, ‘wav’)

– process the file
os.execute(exe … " " … cmd … " “…ofile…” "…ifile);

if not io.exists(ifile) then
display_error()
return
end

temp_buffer:load_from(ifile)

– allocate the second sample buffer
– the size is “stuff outside the selection” + “size of processed file”
if not temp_buffer2:create_sample_data(buffer.sample_rate, buffer.bit_depth, buffer.number_of_channels, (buffer.number_of_frames - (buffer.selection_end - buffer.selection_start) + 1) + temp_buffer.number_of_frames)

then
renoise.app():show_error(“Couldn’t create sample!”) return
end

– copy the selection back
int_new_frame = 1

– copy the part before the selection
if buffer.selection_start > 1 then
for int_frame = 1, buffer.selection_start - 1 do
for int_chan = 1, buffer.number_of_channels do
temp_buffer2:set_sample_data(int_chan, int_frame, buffer:sample_data(int_chan, int_frame))
end
int_new_frame = int_new_frame + 1
end
end

– copy the processed part selection
for int_frame = 1, temp_buffer.number_of_frames do
for int_chan = 1, buffer.number_of_channels do
temp_buffer2:set_sample_data(int_chan, int_new_frame, temp_buffer:sample_data(int_chan, int_frame))
end
int_new_frame = int_new_frame + 1
end

– copy the part after the selection
if buffer.selection_end < buffer.number_of_frames then
for int_frame = buffer.selection_end, buffer.number_of_frames do
for int_chan = 1, buffer.number_of_channels do
temp_buffer2:set_sample_data(int_chan, int_new_frame, buffer:sample_data(int_chan, int_frame))
end
int_new_frame = int_new_frame + 1
end
end

temp_buffer2:save_as(xfile, ‘wav’)
buffer:load_from(xfile)

–buffer:finalize_sample_data_changes()

– delete original sample and the one of the temporary samples

– (and make sure this doesn’t delete the wrong samples perhaps… ?!)

instrument:delete_sample_at(renoise.song().selected_sample_index + 1)
instrument:delete_sample_at(renoise.song().selected_sample_index + 1)

os.remove(ofile)
os.remove(ifile)
os.remove(xfile)
end

  
cheers!

well actually it’s very buggy so my code is probably crap, but when it works it clearly demonstrates how desirable this feature is ^^

hi

just a stupid question,how can i test this out?

hmm I think it may be a bit early for that, it was really just intended for suva originally and it’s really broken (I just fiddled a bit, didn’t have enough time for more, PLUS I hardly know what I’m doing as always haha)…

… but if you open main.lua of the rubberband plugin, and look for “function process_rubberband(cmd)” and replace that with the code I posted (the whole function, there’s a bunch of dashes where it ends), well, you can test it (make sure to select “reload all tools” of course).

at least on mono samples with short selections I could get it to work, and it’s kinda fun to treat individual words in a vocal sample. but if you select to much the sample just disappear heh (and stereo is weird anyway, but this seems to be a rubberband issue).

it’s really an utter hack… but hey, it’s free, right? :P

thanks,yeah i know,i have a hard time(not enough time in the day)to get my head around this scripting thing,and im more like a learn while your doing,instead of a learn while you read kinda guy :D

EDIT:just tried your code,and it works,didnt get any errors or anything,tried it on some amen breakbeats and it works quite well actually,did get some quite nice results

cheers! I updated the code above a bit, I think I know why the samples sometimes disappeared. if you’re using it, you might as well use the updated version :) (it’s also nicer in that the sample name gets retained)

when I select part of a smallish sample and stretch that to, say, 60 seconds, that sometimes works, but in big samples that it never, ever does… it gets stretched just a bit, or even shortened… and I wouldn’t even know how to debug that to be honest. “the code should work, it just doesn’t” (says the noob haha, but still, I poured over it a few times and I just can’t spot the mistake…)

but hey, it’s a start at least. call it an appetizer or something ^^

yeah i noticed that sometimes the sample waveform disappeared,but if you zoomed in abit it came back

yes I forgot to “finalize the sample buffer” ahem :D

by the way, it seems to work flawless when pitchshifting, so that can be used for more than just testing purposes I think.

i tried to copy-paste your updated code above in again,but now i get this error message when i try to open the timestretch function

http://stashbox.org/958954/get-a-error-now.jpg

you probably have replaced too much?

oh, and thanks for checking it out btw, kinda forgot to mention that :)

yeah i had replaced to much

it works like a charm,cant tell you if theres something wrong with the code though,i dont quite understand this yet

it was my pleasure so much great stuff getting released,and because i dont know this scripting stuff yet,i might as well help with some testing :D

so far I was only looking at the function that does the processing, not at the one that prepares the parameters for it… and that one still assumed the whole sample, not the current selection. DOH!!!

so search (in main.lua) for

local nframes = sel_sample.sample_buffer.number_of_frames  

and replace it with

local nframes = sel_sample.sample_buffer.selection_end - sel_sample.sample_buffer.selection_start + 1  

yay! I haven’t tested much yet… but that was such a HUGE bug I wouldn’t be surprised if it caused all the weirdness that confused me. on first glance it seems fine now.

corrected it,thanks

null problemo, enjoy!

next up is optionally using a stretch time factor (50%, 200% etc., well actually as a simple floating point number), and allowing fractions for seconds/beats/lines. and not restricting that to 100 (I mean, no offense, but 100 seconds? I need at least over 9000 for a lot of reasons, none of them any good :D)

after that making it a floating window so you don’t have to reopen it all the time. a benefit of that is that the values don’t get reset, so you can easily do the same operation on a bunch of samples etc.

oh, and then you can also change the selection while using it, so that would allow for an option to get the desired stretch time from the current selection, apply that to another selection, then switch samples and select something else… ahhh.

this’ll take a while of course, but I’m not saying this to be a tease, but so Suva knows what I’m up to?! after all I have no idea if he is working on it as well, etc. but I LOVE this plugin - and now that I know how, how could I possibly resist scratching a few itches I get when using it? unpossible…

but I’m not trying to fork or push my changes on you (Suva)! at the very least you will have something you can loot for ideas/code to put back into the official thingy, and I get to learn stuff.

im really looking forward to see what you do with this cool tool,i love it too :D

i actually use your version over the officially now

just make sure you save a lot… please… ^^

but hey, why not replace the show_stretch_dialog() with this:

[details=“Click to view contents”] ```lua

function show_stretch_dialog()
local bpm = renoise.song().transport.bpm
local lpb = renoise.song().transport.lpb
local coef = bpm * lpb / 60.0

local real_percentage = 0

local real_duration = 0

local sel_sample = renoise.song().selected_sample

–local nframes = sel_sample.sample_buffer.number_of_frames

local nframes = sel_sample.sample_buffer.selection_end - sel_sample.sample_buffer.selection_start + 1
local srate = sel_sample.sample_buffer.sample_rate

local slength = nframes / srate
local rows = slength * coef

local bool_precise = false

local vb = renoise.ViewBuilder()

local nlines_selector =
vb:textfield {
id = ‘txtDuration’,
tooltip = “The desired duration”,
align = “right”,
width = 50,
value = tostring(1),
notifier = function(real_value)
real_duration = tonumber(real_value)
end
}

local crisp_selector = vb:popup {
width = 230,
items = {‘1 (Piano)’, ‘2 (Smooth)’, ‘3 (Balanced multitimbral mixture)’, ‘4 (Unpitched percussion with stable notes)’, ‘5 (Crisp monophonic instrumental)’, ‘6 (Unpitched solo percussion)’},
value = 4
}
local type_selector = vb:popup {
items = {‘lines’, ‘beats’, ‘seconds’},
value = 2
}

local view =

vb:column
{
vb:horizontal_aligner
{
margin = 3,
spacing = 10,
mode = “center”,
nlines_selector,
type_selector,
},
vb:horizontal_aligner
{
margin = 3,
spacing = 10,
mode = “center”,
crisp_selector,
},
vb:horizontal_aligner
{
mode = “center”,
margin = 3,
spacing = 10,
vb:textfield {
id = ‘txtStretchFactor’,
tooltip = “The desired stretch factor (set to 0 to use duration instead)”,
align = “right”,
width = 50,
value = tostring(real_percentage),
notifier = function(real_value)
real_percentage = tonumber(real_value)
end
},
vb:text { text = ‘Stretch factor’ },
},
vb:horizontal_aligner
{
margin = 3,
spacing = 10,
mode = “center”,
vb:checkbox {
id = “chkPrecise”,
value = bool_precise,
notifier = function(boolean_value)
bool_precise = boolean_value
end
},
vb:text { text = ‘–precise’ },
}
}

local res = renoise.app():show_custom_prompt (
“Time Stretch”,
view,
{‘Stretch’, ‘Cancel’}
);

– How long we stretch?
local real_stretch_factor

if real_percentage > 0 then
– use supplied percentage
real_stretch_factor = real_percentage
else
– calculate factor from desired time
if type_selector.value == 1 then
real_stretch_factor = real_duration / rows
elseif type_selector.value == 2 then
real_stretch_factor = (real_duration * lpb) / rows
elseif type_selector.value == 3 then
real_stretch_factor = real_duration / slength
end;
end

if real_stretch_factor > 100 then
local conf = renoise.app():show_prompt(‘Too big stretch!’, ‘You want to multiply sample length by ‘… real_stretch_factor…’! Doing this may freeze Renoise for several minutes or even indefinitely. Are you sure you want to continue?’, {‘Sure’, ‘No way!’});
if conf ~= ‘Sure’ then
return
end
end

if res == ‘Stretch’ then
process_stretch(real_stretch_factor, crisp_selector.value, bool_precise)
end
end

  
and process_stretch() with this:  
  
[details="Click to view contents"] ```lua  
function process_stretch(stretch, crisp, bool_precise)  
 local cmd = "--time "..stretch.." --crisp "..crisp;  
 if bool_precise then  
 cmd = cmd .. ' -P'  
 end  
 process_rubberband(cmd);  
end  
``` [/details]  
  
that has a few of the above mentioned changes (stretch factor and allowing non-integer seconds etc.), PLUS I just discovered the "precise" option of rubberband, which seems to preserve timing at the cost of sounding smooth (and CPU of course)...! that's actually important, because on one song I rapped everything in half time, and shortening the time made that sound totally off-rhythm.  
  
Oh, it also got one more crispness factor, just because rubberband has it, and it lists their names (but fuck knows what they mean ![:lol:](https://files.renoise.com/forum/emoticons/default/laugh.gif))  
  
I also wondered about combining time stretch and pitch shift into one dialog.. sure, most of the time you just need one, but when you do need both you can't do that in one operation like rubberband actually can.... ?  
  
and when rubberband has new releases we can just plug them in yay... !  
  
kinda makes me wonder what other command line sound manipulation tools there are? surely a trillion!  
  
ahhh, scripting. gotta love it!  
  
 ![](http://www.wiggler.gr/wp-content/lolcode.jpg)

man where do you come up with that stuff :unsure:

i need to find myself a small project so i can start learning this stuff,just dont really know what it should be??

i was actually thinking on looking into adding pitchshifting to the custom wave generator,but wouldnt really know where to begin or how

I’m not really coming up with anything yet, LUA and most importantly the Renoise (GUI) API are just fucking amazing, it’s like LEGO or something :D

But I wouldn’t think about specific tools too much yet, I’d modify something existing to get a feel for it? What I mean is, instead of picking a point on the map and intending to get there, just walk into one of the available directions. It’s less frustrating, because any change that actually has an effect is an success, and once you collected enough skills and snippets of knowledge, ideas that are possible with these building blocks (and how to accomplish them) will come by themselves. That’s the start I think. Just don’t be discouraged by obstacles, as there will be plenty and they’ll never end :)

It really takes time, trying stuff and failing a lot, and asking questions of course (the more specific the better).

I’ll check your stuff later this evening. You have generated lots of info overnight, can’t absorb all of it in one go :D

Added some of your changes to the trunk. Will study more later.

Some notes:

  • I am trying to keep it as simple and comfortable as possible, so I changed the default back from 1 beat to 16 beats. (I think we should add some sensible autodetection that guesses how long the user is most likely wanting to stretch the sample)
  • Removed the “percentage” entry and integrated it into the main duration selector. Just enter 200 and select “percent”. Makes things more simple.
  • Changed the name of the --precise field.

Needs some more work, but you should also keep the goals. The interface should be as simple as possible, no awkward functions that require some beforehand knowledge to use, everything should be self explanatory, and should run without user having to go into all the details.