New Tool (3.0): Akaizer


#1

akaizer_big.png

This tool is the wrapper for Akaizer, freeware timestretching and pitchshifting tool for Windows, Linux and OS X (http://akaizer.blogspot.ru/).
How to use: Sample Editor -> Process -> Akaizer.

Download Akaizer


#2

R3 - can’t install this tool - seams the file is corrupted

btw. looks like a handy tool thx for making it


#3

R3 - can’t install this tool - seams the file is corrupted

btw. looks like a handy tool thx for making it

Reuploaded the files, should work now. Thanks for the report!


#4

Hello lwpss. Got a report from a user that undo processing the Akaizer tool manages to crash down Renoise.

This happens because of the “prepare/finalize_sample_data_changes” calls, which are only expected when changing the sample buffer manually via “sample_buffer:sample_data”.
This is not expected (and thus necessary) when loading a complete new sample from Lua via “sample_buffer:load_from”. Of course this never should crash down Renoise, so we’ll fix that and will throw an error when this happens.

To fix the crash in your tool, remove the “sample_buffer:prepare_sample_data_changes” and “sample_buffer:finalize_sample_data_changes” calls from your “process_sample” function please. Then undo will work as expected…


#5

I experienced this crash as well, sorry for not getting around to reporting it.


#6

Hello lwpss. Got a report from a user that undo processing the Akaizer tool manages to crash down Renoise.

This happens because of the “prepare/finalize_sample_data_changes” calls, which are only expected when changing the sample buffer manually via “sample_buffer:sample_data”.
This is not expected (and thus necessary) when loading a complete new sample from Lua via “sample_buffer:load_from”. Of course this never should crash down Renoise, so we’ll fix that and will throw an error when this happens.

To fix the crash in your tool, remove the “sample_buffer:prepare_sample_data_changes” and “sample_buffer:finalize_sample_data_changes” calls from your “process_sample” function please. Then undo will work as expected…

Thank you taktik! I updated the tool, should’ve read API documentation better.


#7

<3


#8

Any plans on updating this baby for latest version of Renoise:-)


#9

Any plans on updating this baby for latest version of Renoise:-)

Done!


#10

Done!

the blog isnt working anymore, can you upload akaizer osx version for me somewhere please?


#11

the blog isnt working anymore, can you upload akaizer osx version for me somewhere please?

Binaries are included within the tool itself, there is no need to download them.


#12

Akaizer doesn’t work for me. Whenever I try to process a sample with it, I get the following error:

std::logic_error: 'trying to load sample data from a non existing file: 'C:\Users\SNATCH~1\AppData\Local\Temp\Renoise-0-3996\Renoise_TmpFile-0-3-131%_1000_0_R.wav''
stack traceback:
 [C]: in function 'load_from'
 main.lua:106: in function 'process_sample'
 main.lua:215: in function 'show_dialog'
 main.lua:20: in function <main.lua:14>

I’m on Renoise 3.1, Windows 7 x64.

Did I miss something in setting up the tool?


#13

Hi, I’m the developer of Akaizer. I took some time to go through the Lua code for the wrapper tool and fix some of the problems and clean it up slightly. This new code should fix the “trying to load sample data from a non existing file” error and it also fixes compatibility with Windows XP, as it wasn’t working properly on my XP setup. This has only been tested on XP 32-bit but should work OK for all other operating systems.

6569 main.lua

--------------------------------
-- Akaizer wrapper tool v1.02 --
--------------------------------

-- constants --

MINIMAL_LENGTH = 0.04
BIN = {
  WINDOWS = "bin/win/Akaizer.exe",
  LINUX = "bin/linux/Akaizer",
  MACINTOSH = "bin/osx/Akaizer"
}

-- init --

if(os.platform() == "LINUX" or os.platform() == "MACINTOSH") then
  io.chmod(string.format("%q", renoise.tool().bundle_path..BIN[os.platform()]), 755)
end

renoise.tool():add_menu_entry {
  name = "Sample Editor:Process:Akaizer...",
  invoke = function()
     show_dialog()
  end
}

renoise.tool():add_keybinding {
  name = "Sample Editor:Process:Akaizer",
  invoke = function()
     show_dialog()
  end
}

-- helpers --

function round(num, idp)
  return tonumber(string.format("%."..(idp or 0).."f", num))
end

function remove_file_extension(filename)
  local found, len, remainder = filename:find("^(.*)%.[^%.]*$")

  if found then
    return remainder
  else
    return filename
  end
end

function parse_path(path)
  local dir, name, ext = path:match("(.-)([^\\/]-%.?([^%.\\/]*))$")
  name = remove_file_extension(name)
  
  return dir, name
end

function get_sample_length()
  local sample = renoise.song().selected_sample.sample_buffer

  local sample_rate = sample.sample_rate
  local frames = sample.number_of_frames

  return frames / sample_rate
end

-- processing --

function process_sample(time_factor, cycle_length, transpose, is_classic)
  if is_classic then
    time_factor = math.floor(time_factor)
  end
  if time_factor == 100 and transpose == 0 then
    renoise.app():show_message("Sample wasn't processed because you didn't change Time Factor or Transpose.")
    show_dialog()
  else
    local tmp_path = os.tmpname("wav")
    local tmp_dir, tmp_name = parse_path(tmp_path)
    
    local output = string.format("%s-%s%%_%u_%s%d_%s.wav",
      tmp_name,
      time_factor,
      cycle_length,
      transpose > 0 and "+" or "",
      transpose,
      is_classic and "C" or "R"
    )
    
    local exe = string.format("%q %s %s %u %d%s",
      renoise.tool().bundle_path..BIN[os.platform()],
      tmp_path,
      time_factor,
      cycle_length,
      transpose,
      is_classic and " -c" or ""
    )
    
    renoise.song().selected_sample.sample_buffer:save_as(tmp_path, "wav")
    os.execute(exe)
    renoise.song().selected_sample.sample_buffer:load_from(tmp_dir..output)
    
    os.remove(tmp_path)
    os.remove(tmp_dir..output)
    
    renoise.app():show_status("Successfully processed!")
  end
end

-- dialogs --

function show_dialog()
  local length = get_sample_length()
  
  if length < MINIMAL_LENGTH then
    renoise.app():show_error("The sample is too short! Minimal length is 40 ms.")
    return
  end
  
  local classic = false
  
  local vb = renoise.ViewBuilder()

  local DIALOG_MARGIN = renoise.ViewBuilder.DEFAULT_DIALOG_MARGIN
  local CONTENT_SPACING = renoise.ViewBuilder.DEFAULT_CONTROL_SPACING  

  -- view elements --

  local time_box = vb:valuebox {
    tooltip = "[Min: 25] [Max: 2000] [Default: 100]",
    width = 120,
    min = 25,
    max = 2000,
    value = 100,
    tostring = function(value)
      if classic then
        return string.format("%u", value)
      else
        return string.format("%.2f", value)
      end
    end,
    tonumber = function(value)
      if tonumber(value) == nil then
        return
      else
        if not classic then
          return tonumber(round(value, 2))
        else
          return tonumber(value)
        end
      end
    end
  }

  local cycle_box = vb:valuebox {
    tooltip = "[Min: 20] [Max: 2000] [Default: 1000]",
    width = 120,
    min = 20,
    max = 2000,
    value = 1000
  }

  local transpose_box = vb:valuebox {
    tooltip= "[Min: -24] [Max: +24] [Default: 0]",
    width = 120,
    min = -24,
    max = 24,
    value = 0,
    tostring = function(value)
      if value > 0 then
        return string.format("%+d", value)
      else
        return string.format("%d", value)
      end
    end,
    tonumber = function(value)
      if tonumber(value) == nil then
        return
      else
        return tonumber(value)
      end
    end
  }

  local is_classic_box = vb:checkbox {
    value = false,
    notifier = function()
      if classic then
        classic = false
        time_box.value = time_box.value + 0.000001
      else
        classic = true
        time_box.value = time_box.value + 0.000001
      end
    end
  }

  -- rows --

  local time_row = vb:row {
    vb:text {
      text = "Time Factor",
      width = 80
    },
    time_box,
    vb:text {
      text = "%",
      width = 80
    }
  }

  local cycle_row = vb:row {
    vb:text {
      text = "Cycle Length",
      width = 80
    },
    cycle_box,
    vb:text {
      text = "samples",
      width = 80
    }
  }

  local transpose_row = vb:row {
    vb:text {
      text = "Transpose",
      width = 80
    },
    transpose_box,
    vb:text {
      text = "semitones",
      width = 80
    }
  }

  local is_classic_row = vb:row {
    is_classic_box,
    vb:text {
      text = 'Enable "classic" cyclic algorithm'
    }
  }

  -- prompt --

  local prompt = renoise.app():show_custom_prompt(
    "Akaizer",

    vb:column {
      margin = DIALOG_MARGIN,
      spacing = CONTENT_SPACING,

      time_row,
      cycle_row,
      transpose_row,
      is_classic_row
    },
    
    { "Process", "Cancel" }
  )
  
  if prompt == "Process" then
    process_sample(
      time_box.value,
      cycle_box.value, 
      transpose_box.value, 
      is_classic_box.value
    )
  end
end

#14

Brilliant, that did the trick. Thanks a lot, Ben!


#15

I found some more bugs in the previous script (v1.02) that I posted. This updated version fixes those bugs and improves the GUI slightly. The code should be rock solid now. Works perfectly on Windows and should also be fine for all other operating systems.

6575 main.lua

--------------------------------
-- Akaizer wrapper tool v1.03 --
--------------------------------

-- constants --

local MIN_TIME_FACTOR = 25
local DEF_TIME_FACTOR = 100
local MAX_TIME_FACTOR = 2000
local MIN_CYCLE_LENGTH = 20
local DEF_CYCLE_LENGTH = 1000
local MAX_CYCLE_LENGTH = 2000
local MIN_TRANSPOSE = -24
local DEF_TRANSPOSE = 0
local MAX_TRANSPOSE = 24
local DEF_ALGORITHM = 1 -- 1=Revised 2=Classic
local MINIMAL_LENGTH = 40 -- milliseconds
local BIN = {
  WINDOWS = "bin/win/Akaizer.exe",
  LINUX = "bin/linux/Akaizer",
  MACINTOSH = "bin/osx/Akaizer"
}

-- variables --

local last_time_factor = DEF_TIME_FACTOR
local last_cycle_length = DEF_CYCLE_LENGTH
local last_transpose = DEF_TRANSPOSE
local last_algorithm = DEF_ALGORITHM
local show_dialog = nil

-- init --

if(os.platform() == "LINUX" or os.platform() == "MACINTOSH") then
  io.chmod(string.format("%q", renoise.tool().bundle_path..BIN[os.platform()]), 755)
end

-- menu entries --

renoise.tool():add_menu_entry {
  name = "Sample Editor:Process:Akaizer...",
  invoke = function()
    show_dialog()
  end
}

-- key binding --

renoise.tool():add_keybinding {
  name = "Sample Editor:Process:Akaizer",
  invoke = function()
    show_dialog()
  end
}

-- helpers --

local function remove_file_extension(filename)
  local found, len, remainder = filename:find("^(.*)%.[^%.]*$")
  
  if found then
    return remainder
  else
    return filename
  end
end

local function parse_path(path)
  local dir, name, ext = path:match("(.-)([^\\/]-%.?([^%.\\/]*))$")
  name = remove_file_extension(name)
  
  return dir, name
end

local function get_sample_length() -- in seconds
  local sample = renoise.song().selected_sample.sample_buffer
  
  local sample_rate = sample.sample_rate
  local frames = sample.number_of_frames
  
  return frames / sample_rate
end

-- processing --

local function process_sample(time_factor, cycle_length, transpose, is_classic)
  if is_classic then
    time_factor = math.floor(time_factor)
  end
  if time_factor == DEF_TIME_FACTOR and transpose == DEF_TRANSPOSE then
    renoise.app():show_message("Sample wasn't processed because you didn't change Time Factor or Transpose.")
    show_dialog()
  else
    local tmp_path = os.tmpname("wav")
    local tmp_dir, tmp_name = parse_path(tmp_path)
    
    local output = string.format("%s-%s%%_%u_%s%d_%s.wav",
      tmp_name,
      time_factor,
      cycle_length,
      transpose > 0 and "+" or "",
      transpose,
      is_classic and "C" or "R"
    )
    
    local exe = string.format("%q %s %s %u %d%s",
      renoise.tool().bundle_path..BIN[os.platform()],
      tmp_path,
      time_factor,
      cycle_length,
      transpose,
      is_classic and " -c" or ""
    )
    
    renoise.song().selected_sample.sample_buffer:save_as(tmp_path, "wav")
    
    if os.execute(exe) ~= 0 then
      renoise.app():show_error("Process did not complete successfully...")
      os.remove(tmp_path)
      return
    end
    
    renoise.song().selected_sample.sample_buffer:load_from(tmp_dir..output)
    
    os.remove(tmp_path)
    os.remove(tmp_dir..output)
    
    renoise.app():show_status("Successfully processed!")
  end
end

-- dialogs --

show_dialog = function()
  if renoise.song().selected_sample then
    if get_sample_length() * 1000 < MINIMAL_LENGTH then
      renoise.app():show_error("The sample is too short! Minimal length is "..MINIMAL_LENGTH.." ms.")
      return
    end
  else
    return
  end
  
  local DIALOG_MARGIN = renoise.ViewBuilder.DEFAULT_DIALOG_MARGIN
  local CONTENT_SPACING = renoise.ViewBuilder.DEFAULT_CONTROL_SPACING
  local vb = renoise.ViewBuilder()
  local algorithm_switch = nil
  
  -- view elements --
  
  local time_factor_box = vb:valuebox {
    tooltip = "TIME FACTOR [Min: "..MIN_TIME_FACTOR.."] [Max: "..MAX_TIME_FACTOR.."] [Default: "..DEF_TIME_FACTOR.."]",
    min = MIN_TIME_FACTOR,
    max = MAX_TIME_FACTOR,
    value = last_time_factor,
    width = 80,
    tostring = function(value)
      if algorithm_switch.value == 1 then
        return string.format("%.2f", value)
      else
        return string.format("%u", value)
      end
    end,
    tonumber = function(value)
      if not tonumber(value) then
        return nil
      end
      if algorithm_switch.value == 1 then
        return tonumber(string.format("%.2f", value))
      else
        return tonumber(string.format("%u", value))
      end
    end
  }
  
  local time_factor_button = vb:button {
    tooltip = "Reset TIME FACTOR to default value ["..DEF_TIME_FACTOR.."]",
    text = "^",
    notifier = function()
      time_factor_box.value = DEF_TIME_FACTOR
    end
  }
  
  local cycle_length_box = vb:valuebox {
    tooltip = "CYCLE LENGTH [Min: "..MIN_CYCLE_LENGTH.."] [Max: "..MAX_CYCLE_LENGTH.."] [Default: "..DEF_CYCLE_LENGTH.."]",
    min = MIN_CYCLE_LENGTH,
    max = MAX_CYCLE_LENGTH,
    value = last_cycle_length,
    width = 80
  }
  
  local cycle_length_button = vb:button {
    tooltip = "Reset CYCLE LENGTH to default value ["..DEF_CYCLE_LENGTH.."]",
    text = "^",
    notifier = function()
      cycle_length_box.value = DEF_CYCLE_LENGTH
    end
  }
  
  local transpose_box = vb:valuebox {
    tooltip= "TRANSPOSE [Min: "..MIN_TRANSPOSE.."] [Max: +"..MAX_TRANSPOSE.."] [Default: "..DEF_TRANSPOSE.."]",
    min = MIN_TRANSPOSE,
    max = MAX_TRANSPOSE,
    value = last_transpose,
    width = 80,
    tostring = function(value)
      if value > 0 then
        return string.format("%+d", value)
      else
        return string.format("%d", value)
      end
    end,
    tonumber = function(value)
      if not tonumber(value) then
        return nil
      end
      return tonumber(value)
    end
  }
  
  local transpose_button = vb:button {
    tooltip = "Reset TRANSPOSE to default value ["..DEF_TRANSPOSE.."]",
    text = "^",
    notifier = function()
      transpose_box.value = DEF_TRANSPOSE
    end
  }
  
  algorithm_switch = vb:switch {
    tooltip = "(R)EVISED or (C)LASSIC cyclic algorithm",
    items = {"R", "C"},
    value = last_algorithm,
    width = 80,
    notifier = function()
      -- refresh time factor valuebox by changing its value
      if time_factor_box.value >= MAX_TIME_FACTOR - 1 then
        time_factor_box.value = time_factor_box.value - 1
        time_factor_box.value = time_factor_box.value + 1
      else
        time_factor_box.value = time_factor_box.value + 1
        time_factor_box.value = time_factor_box.value - 1
      end
    end
  }
  
  -- rows --
  
  local time_factor_row = vb:row {
    vb:text {
      text = "Time Factor",
      width = 80
    },
    time_factor_box,
    vb:text {
      text = "%",
      width = 70
    },
    time_factor_button
  }
  
  local cycle_length_row = vb:row {
    vb:text {
      text = "Cycle Length",
      width = 80
    },
    cycle_length_box,
    vb:text {
      text = "samples",
      width = 70
    },
    cycle_length_button
  }
  
  local transpose_row = vb:row {
    vb:text {
      text = "Transpose",
      width = 80
    },
    transpose_box,
    vb:text {
      text = "semitones",
      width = 70
    },
    transpose_button
  }
  
  local algorithm_row = vb:row {
    vb:text {
      text = "Algorithm",
      width = 80
    },
    algorithm_switch
  }
  
  -- prompt --
  
  local prompt = renoise.app():show_custom_prompt(
    "Akaizer",
    
    vb:column {
      margin = DIALOG_MARGIN,
      spacing = CONTENT_SPACING,
      
      time_factor_row,
      cycle_length_row,
      transpose_row,
      algorithm_row
    },
    
    { "Process", "Cancel" }
  )
  
  -- remember current values
  last_time_factor = time_factor_box.value
  last_cycle_length = cycle_length_box.value
  last_transpose = transpose_box.value
  last_algorithm = algorithm_switch.value
  
  if prompt == "Process" then
    process_sample(
      time_factor_box.value,
      cycle_length_box.value, 
      transpose_box.value, 
      algorithm_switch.value == 2 and true or false
    )
  end
end

#16

Akaizer doesn’t work for me. Whenever I try to process a sample with it, I get the following error:

std::logic_error: 'trying to load sample data from a non existing file: 'C:\Users\SNATCH~1\AppData\Local\Temp\Renoise-0-3996\Renoise_TmpFile-0-3-131%_1000_0_R.wav''
stack traceback:
[C]: in function 'load_from'
main.lua:106: in function 'process_sample'
main.lua:215: in function 'show_dialog'
main.lua:20: in function <main.lua:14>

I’m on Renoise 3.1, Windows 7 x64.

Did I miss something in setting up the tool?

Hi, I’ve got the same problem, but I have no idea what to do with the lua file that Ben B has provided. Can anyone help?


#17

It’s OK, I’ve accidently sorted it lol. Everythings working fine now. :slight_smile:


#18
'/Users/floriankrause/Library/Preferences/Renoise/V3.1.0/Scripts/Tools/com.lwpss.Akaizer.xrnx/' failed to execute in one of its menu entry functions.

Please contact the author (lwpss [lw.pss.mail@gmail.com]) for assistance...

std::logic_error: 'do NOT call 'prepare/finalize_sample_data_changes' before/after loading or creating new sample buffers. prepare/finalize must only be called when modifying existing sample data via sample_buffer:sample_data().'
stack traceback:
 [C]: in function 'finalize_sample_data_changes'
 main.lua:115: in function 'process_sample'
 main.lua:267: in function 'show_dialog'
 main.lua:20: in function <main.lua:14>

#19
'/Users/floriankrause/Library/Preferences/Renoise/V3.1.0/Scripts/Tools/com.lwpss.Akaizer.xrnx/' failed to execute in one of its menu entry functions.

Please contact the author (lwpss [lw.pss.mail@gmail.com]) for assistance...

std::logic_error: 'do NOT call 'prepare/finalize_sample_data_changes' before/after loading or creating new sample buffers. prepare/finalize must only be called when modifying existing sample data via sample_buffer:sample_data().'
stack traceback:
[C]: in function 'finalize_sample_data_changes'
main.lua:115: in function 'process_sample'
main.lua:267: in function 'show_dialog'
main.lua:20: in function <main.lua:14>

What exactly did you do to get that error? Can you replicate it?

According to that traceback info it is line 115 of the code that is causing the problem:

renoise.song().selected_sample.sample_buffer:save_as(tmp_path, "wav")

I don’t normally code in Lua, so I was just improving on what had already been done, and that line was already in the code to begin with. I’ll have to investigate as to why it is causing a problem, though it seems to work fine on my PC.


#20

I load a sample, go to process --> Akaizer, change the time factor (e.g. to 80%), click “process”, and it crashes.