Tempo keeps resetting after setting with transport.bpm

Hey guys,

I’m stuck on a script I wrote that renders a song into individual patterns across a selection of tempos. Everything works perfectly except um actually getting the tempo to set.

Basically it creates a folder with the name of the project file, and then makes a bunch of sub folders according to your tempo range and increment, and then renders out the song as a set of patterns for one tempo, does the same for the next tempo etc. But I can’t get the tempo to actually set, ie everything just renders out at the same tempo.

What’s confusing is that I’ll set it via rns.transport.bpm and if you read it back it shows the tempo definitely was changed. But immediately afterward it flips back to either the lowest value of the tempo range or the global bpm of when you executed the script.

I feel like something really simple is probably going on but I dont work with Lua alot and Im just not seeing it.

I’ve tried 2 different approaches and both have the same problem. The first one uses a coroutine:

 
--render a song as individual patterns in a selection of tempos.
--moves from tempBpm to targetBpm in increments of tempoInc 
--Creates a folder with project name, and then makes tempo sub folders like '60' '65' etc to hold renders

rns = renoise.song()

targetBpm = 80 
tempBpm = 60
tempoInc = 5

local function rendering_done_callback()
  print("done a render. in callback rns.transport.bpm is returning: " .. rns.transport.bpm)--this is reading the initial value of tempBpm (60) and on some occasions it reads the originalBpm of when we ran the script...in either case, it never correctly reads what we set it to down in the coroutine.  I tried setting tempo up here in the callback, but that didnt work either
  
  coroutine.resume(renderCoroutine)
  if tempBpm == targetBpm then
   rns.transport.bpm = originalBpm --reset to originalBpm
    renoise.app():show_message("done")
  end
end


-----------------this block just isolates project name so we can make a main folder to keep our tempo folders...is there a variable for project name that u can just read or is this neccessary-----
local fullDir = renoise.song().file_name --complete directory

local startEx ,endEx = string.find(fullDir,".xrns") --get start/end of extension

local i = startEx-1
local nameStart 

local flag = true

while flag do --work back from extension to find a slash so we can get the project file name

  if string.sub(fullDir,i,i) == "\\" then
    flag = false
    nameStart= i+1
  else
    i = i - 1
  end

end

 nom = string.sub(fullDir,nameStart,startEx-1) --now we have the project name 
 
 ourDir = string.sub(fullDir,1,nameStart-1) --now we have the project files's directory
 
 newSubdir = ourDir .. nom --now we can make a folder with same name as our renoise project
 
os.mkdir(newSubdir) --make the folder
-----------------------------------------end of the main folder making block



originalBpm = rns.transport.bpm --save original bpm before rendering

rns.transport.bpm = tempBpm --set tempo to starting tempo
                            --**there are times when all our renders turn out to be at the originalBpm,
                            --meaning that somewhow even this line didnt take??


----------------------------------------------------------------render coroutine
renderCoroutine = coroutine.create(function()

while tempBpm < targetBpm do --for each tempo, make a folder with tempo name ie '65'. In that folder render each pattern of the sequence for that tempo.

local seqNum = 1

while seqNum <= #rns.sequencer.pattern_sequence do

local newSubSubDir = newSubdir .."\\" .. tempBpm --folder name is just the current tempo value
os.mkdir(newSubSubDir)--make the folder

local filename = newSubSubDir .. "\\" .. seqNum .. "_" .. tempBpm .. "_" .. nom .. ".wav" --filename is made up of the seqNum so we know the order plus tempo and song name, even though the name and tempo are already implied by the folder system it's inside.

local options = {
  start_pos = renoise.SongPos(seqNum,1),    
  end_pos = renoise.SongPos(seqNum,rns.patterns[1].number_of_lines),       
  sample_rate = 44100,                       
  bit_depth = 16 ,    
  interpolation = 'default', 
  priority = 'high',       
}

rns:render(options, filename, rendering_done_callback) --render


seqNum = seqNum + 1

coroutine.yield()


end --end of looping thru all patterns in the sequence

tempBpm = tempBpm + tempoInc
rns.transport.bpm = tempBpm

 print("we set rns.transport.bpm in our coroutine and now it returns " .. rns.transport.bpm)

end --end looping thru all tempos defined by range and increment
end)-----------------------------------------------------------------end coroutine

--call coroutine once to get it running
print("start coroutine")
coroutine.resume(renderCoroutine)

And then I also tried doing tempo incrementing inside the render call back like this:

 
--render a song as individual patterns in a selection of tempos.
--moves from tempBpm to targetBpm in increments of tempoInc 
--Creates a folder with project name, and then makes tempo sub folders like '60' '65' etc to hold renders

rns = renoise.song()

targetBpm = 80 
tempBpm = 60
tempoInc = 5
seqNum = 1

local function rendering_done_callback()

print("done a render. in callback rns.transport.bpm is returning: " .. rns.transport.bpm)--this is reading the initial 

local renderAgain = false

seqNum = seqNum + 1

if seqNum <= #rns.sequencer.pattern_sequence then --if more patterns in sequence, render again
  renderAgain = true
else --if no more patterns in sequence, increment tempo, and mabe render again
  seqNum = 1
  tempBpm = tempBpm + tempoInc
   rns.transport.bpm = tempBpm
   print("just set bpm within callback and it reads: " ..  rns.transport.bpm )
   if tempBpm < targetBpm then
     renderAgain = true
   else
   
   end
end

if renderAgain == true then
newSubSubDir = newSubdir .."\\" .. tempBpm --folder name is just the current tempo value
os.mkdir(newSubSubDir)--make the folder

filename = newSubSubDir .. "\\" .. seqNum .. "_" .. tempBpm .. "_" .. nom .. ".wav" --filename is made up of the seqNum so  we know the order plus tempo and song name, even though the name and tempo are already implied by the folder system it's inside.
local options = {
  start_pos = renoise.SongPos(seqNum,1),    
  end_pos = renoise.SongPos(seqNum,rns.patterns[1].number_of_lines),       
  sample_rate = 44100,                       
  bit_depth = 16 ,    
  interpolation = 'default', 
  priority = 'high',       
}

print("call render from callback")
rns:render(options, filename, rendering_done_callback) --render
end

if renderAgain ~= true then
   rns.transport.bpm = originalBpm --reset to originalBpm
    renoise.app():show_message("done")
end
end


-----------------this block just isolates project name so we can make a main folder to keep our tempo folders...is there a variable for project name that u can just read or is this neccessary-----
local fullDir = renoise.song().file_name --complete directory

local startEx ,endEx = string.find(fullDir,".xrns") --get start/end of extension

local i = startEx-1
local nameStart 

local flag = true

while flag do --work back from extension to find a slash so we can get the project file name

  if string.sub(fullDir,i,i) == "\\" then
    flag = false
    nameStart= i+1
  else
    i = i - 1
  end

end

 nom = string.sub(fullDir,nameStart,startEx-1) --now we have the project name 
 
 ourDir = string.sub(fullDir,1,nameStart-1) --now we have the project files's directory
 
 newSubdir = ourDir .. nom --now we can make a folder with same name as our renoise project
 
os.mkdir(newSubdir) --make the folder
-----------------------------------------end of the main folder making block



originalBpm = rns.transport.bpm --save original bpm before rendering

rns.transport.bpm = tempBpm --set tempo to starting tempo
                            --**there are times when all our renders turn out to be at the originalBpm,
                            --meaning that somewhow even this line didnt take??


--------------------------------------------------------------------------------------------
newSubSubDir = newSubdir .."\\" .. tempBpm --folder name is just the current tempo value
os.mkdir(newSubSubDir)--make the folder

filename = newSubSubDir .. "\\" .. seqNum .. "_" .. tempBpm .. "_" .. nom .. ".wav" --filename is made up of the seqNum so we know the order plus tempo and song name, even though the name and tempo are already implied by the folder system it's inside.

local options = {
  start_pos = renoise.SongPos(seqNum,1),    
  end_pos = renoise.SongPos(seqNum,rns.patterns[1].number_of_lines),       
  sample_rate = 44100,                       
  bit_depth = 16 ,    
  interpolation = 'default', 
  priority = 'high',       
}

print("--------------------------call render to enter into our callback queqe")
rns:render(options, filename, rendering_done_callback) --render
----------------------------------------------------------------------------------------------

neither of these work. At the moment as I test these, bpm is staying exclusively at whatever bpm I have in the box in the song tool bar. Right now its set to 200, and no matter what I do it flips back to 200 every time after being set via the script. Even if I comment out every reference to bpm besides rns.transport.bpm = tempBpm, it still flips back. And if I remove that variable and say rns.transport.bpm = 150, it STILL flips back to 200 when I read it up in the callback.

It would be awesome if someone could run this on their computer from the tool dev window and confirm in the console that they are getting the same thing as me. This is what I get from running the first code example on a song with 3 patterns in the sequence:
start coroutine
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
we set rns.transport.bpm in our coroutine and now it returns 65
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
we set rns.transport.bpm in our coroutine and now it returns 70
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
we set rns.transport.bpm in our coroutine and now it returns 75
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
done a render. in callback rns.transport.bpm is returning: 200
we set rns.transport.bpm in our coroutine and now it returns 80

The ‘200’ is the number I have in the bpm box in the song toolbar, and shouldnt factor in to this script at all, it gets read and saved but then is only referenced at the very end to restore tempo to whatever it was before running the script, and like I said above, if you remove all references to this, ie dont save it to a variable called originalBpm and dont restore it up in the callback routine it does the exact same thing. Also, sometimes instead of 200 I’m getting ‘60’ which is the intial value for tempBpm. It fluctuates between these randomly.

Anybody have any idea of what this could be??

Okay so here’s whats wierd and why this is driving me nuts: I did what you mentioned and ran it, and was like holy $hit I think that worked, because this was my console output:

--------------------------call render to enter into our callback queqe
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
just set bpm within callback and it reads: 65
call render from callback
done a render. in callback rns.transport.bpm is returning: 65
call render from callback
done a render. in callback rns.transport.bpm is returning: 65
call render from callback
done a render. in callback rns.transport.bpm is returning: 65
just set bpm within callback and it reads: 70
call render from callback
done a render. in callback rns.transport.bpm is returning: 70
call render from callback
done a render. in callback rns.transport.bpm is returning: 70
call render from callback
done a render. in callback rns.transport.bpm is returning: 70
just set bpm within callback and it reads: 75
call render from callback
done a render. in callback rns.transport.bpm is returning: 75
call render from callback
done a render. in callback rns.transport.bpm is returning: 75
call render from callback
done a render. in callback rns.transport.bpm is returning: 75
just set bpm within callback and it reads: 80

That’s the way it should look!. But then guess what, I was incredulous and ran it a second time immediately after and got this:

--------------------------call render to enter into our callback queqe
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
just set bpm within callback and it reads: 65
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
just set bpm within callback and it reads: 70
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
just set bpm within callback and it reads: 75
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
call render from callback
done a render. in callback rns.transport.bpm is returning: 60
just set bpm within callback and it reads: 80

which is what it was doing before, resetting back to the value that I initialized for rns.transport.bpm immediately after being set . And then I ran it a third time, and got a third different result!:

--------------------------call render to enter into our callback queqe
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
just set bpm within callback and it reads: 65
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
just set bpm within callback and it reads: 70
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
just set bpm within callback and it reads: 75
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
call render from callback
done a render. in callback rns.transport.bpm is returning: 209
just set bpm within callback and it reads: 80

That third time it was flipping back to the bpm value I have in the tempo box in the song toolbar…I am truly boggled rn

Nice, did not even know that you can run coroutines in Renoise lua!

**UPDATE and I’m going even deeper into the weeds with this. If I leave my computer alone for a few hours (exactly how many idk) and then execute the script, it works 100% perfectly: it creates the folders and renders everything at the correct tempos.
But only ONCE, if I then click on ‘execute’ a second time, it doesn’t work. It renders everything at the same tempo, randomly either the current global bpm of the song, or 60bpm, the initial value of tempBpm. Could this be some kind of memory issue? And when left alone, the system is somehow resolving it after a few hours? In C++ these types of logic defying incomprehensible problems often come from something like writing past the boundary of an array and messing with the memory but I don’t see where I would be doing that in my script…

So like basically I’ve run this script and confirmed that it definitely works, but it’s only worked twice now, separated by lets say 6 or more hours between runnings. It never works twice in a row. I also closed and reopened Renoise and that did not make it work. So as far as I can tell right now, I literally have to leave the computer alone and then come back later. I’ll try it at the end of the day and see if it works correctly one time only, like it did just now.

@EatMe I left that line out just to show that it was still resetting to the original bpm even though there was never a statement to reset it, although about half the time it resets to 60, which is the initial value of tempBpm…I’ll try to post a more stripped down version of it to highlight the problem,

Did you check the coroutine status? Maybe it’s running endlessly or something?

@ffx I’ve gotten the same result from the second code block in the original post which doesn’t use coroutines unfortunately. I just ran the first example to check and printed out the coroutine status at the end of the program and it returns ‘dead’ so doesnt appear to be the problem :sob:

Alright, this is a mixed bag but I solved the major problem. And true to the spirit of my experience so far I have no idea why this is the solution. you have to change the bpm AFTER calling coroutine.resume(). So in the first one you need to put

coroutine.resume(renderCoroutine)
  rns.transport.bpm = tempBpm

in the 2 spots where coroutine.resume() occurs

Even the very first attempt to set bpm sometimes fails if you don’t do it after the coroutine.resume, and the tempo just stays at whatever the current bpm is in the song toolbar.

In the second example where there’s no coroutine, you put the tempo change after calling rns.render:

rns:render(options, filename, rendering_done_callback) --render
rns.transport.bpm = tempBpm

unfortunately although the coroutine version is more clear than the second version it has an issue. If u launch renoise and paste it into the terminal it refuses to run UNLESS you first run the version with no coroutine. If you paste THAT in there, hit execute, and then paste the coroutine example BACK in, it works just fine now. How can that be? idk and am going to just walk off into the sunset on this one

Also the first renders of each tempo go long, and include a line from the second pattern. It doesnt matter for what I’m doing so I didn’t try to figure out why

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.