Library: Adsrenvelope

All,

Please find below version 1.0 of the ADSREnvelope wrapper class.

This class is designed to emulate a simple ADSR envelope and write it to the instrument envelopes which are now available to Lua with Renoise 2.8. It works with Renoise 2.8 beta 2 and will be maintained through the beta and release candidate period.

The code is self contained and can just be placed within it’s own file and 'require’d from your main tool code. It has error checking built in and will stop script execution and deliver a helpful message explaining any errors during use.

It’s free to use and well documented so you can fully understand how it works.

Enjoy!

Click to view contents
  
--------------------------------------------------------------------------------  
-- ADSR Envelope Class  
--  
-- Copyright 2011 Martin Bealby  
--  
-- Free to use in any project  
--------------------------------------------------------------------------------  
  
  
--------------------------------------------------------------------------------  
-- Documentation  
--------------------------------------------------------------------------------  
  
--[[  
The ADSR envelope class (ADSREnvelope()) is designed to present a simple ADSR  
interface to the envelope objects available in Renoise 2.8.  
  
The ADSREnvelope class presents the following API:  
  
  
 ADSREnvelope() - Create a new blank class instance  
 ADSREnvelope:set_attack(ticks) - Set the attack time (in ticks)  
 ADSREnvelope:set_decay(ticks) - Set the decay time (in ticks)  
 ADSREnvelope:set_sustain(level) - Set the sustain level (0 to 1)  
 ADSREnvelope:set_release(ticks) - Set the release time (in ticks)  
 ADSREnvelope:get_settings_string() - Retrieves all settings as a parameter  
 string  
 ADSREnvelope:load_settings_string(settings_string)  
 - Loads settings from a well-formed  
 parameter string  
 ADSREnvelope:copy_to(envelope) - Copies the ADSR envelope parameters into  
 the specified InstrumentMixerEnvelope or  
 InstrumentFilterEnvelope  
  
The ADSREnvelope class takes care of automatically enabling / disabling the  
sustain point depending upon the sustain value.  
]]--  
  
  
--------------------------------------------------------------------------------  
-- Changelog  
--------------------------------------------------------------------------------  
  
-- Version 1.0 - Initial release based upon Renoise 2.8 Beta 2  
  
  
--------------------------------------------------------------------------------  
-- Class Preamble  
--------------------------------------------------------------------------------  
class "ADSREnvelope"  
  
  
--------------------------------------------------------------------------------  
-- Class Functions  
--------------------------------------------------------------------------------  
function ADSREnvelope:__init()  
 -- Initialise the class to defaults  
 self.attack_ticks = 6  
 self.decay_ticks = 6  
 self.sustain_level = 0.5  
 self.release_ticks = 6  
end  
  
  
function ADSREnvelope:set_attack(ticks)  
 -- Sets a new attack value  
 assert(type(ticks) == "number", "ADSREnvelope:set_attack() - ticks must be"..  
 " specified as a number")  
 assert(math.floor(ticks) >= 0 , "ADSREnvelope:set_attack() - ticks must be"..  
 " a positive number")  
 assert(math.floor(ticks) <= 127, "ADSREnvelope:set_attack() - ticks must be"..  
 " a number less than 128")  
  
 self.attack_ticks = math.floor(ticks)  
end  
  
  
function ADSREnvelope:set_decay(ticks)  
 -- Sets a new decay value  
 assert(type(ticks) == "number", "ADSREnvelope:set_decay() - ticks must be"..  
 " specified as a number")  
 assert(math.floor(ticks) >= 0 , "ADSREnvelope:set_decay() - ticks must be"..  
 " a positive number")  
 assert(math.floor(ticks) <= 127, "ADSREnvelope:set_decay() - ticks must be"..  
 " a number less than 128")  
  
 self.decay_ticks = math.floor(ticks)  
end  
  
  
function ADSREnvelope:set_sustain(level)  
 -- Sets a new sustain level  
 assert(type(ticks) == "number", "ADSREnvelope:set_sustain() - level must be"..  
 " specified as a number")  
 assert(level >= 0, "ADSREnvelope:set_sustain() - level must be between 0"..  
 " and 1")  
 assert(level <= 1, "ADSREnvelope:set_sustain() - level must be between 0"..  
 " and 1")  
  
 self.sustain_level = level  
end  
  
  
function ADSREnvelope:set_release(ticks)  
 -- Sets a new decay value  
 assert(type(ticks) == "number", "ADSREnvelope:set_release() - ticks must be"..  
 " specified as a number")  
 assert(math.floor(ticks) >= 0 , "ADSREnvelope:set_release() - ticks must be"..  
 " a positive number")  
 assert(math.floor(ticks) <= 127, "ADSREnvelope:set_release() - ticks must"..  
 " be a number less than 128")  
  
 self.release_ticks = math.floor(ticks)  
end  
  
  
function ADSREnvelope:get_settings_string()  
 -- Returns all ADSR settings as a string with header  
 return string.format("ADSR1:%d:%d:%f:%d",  
 self.attack_ticks,  
 self.decay_ticks,  
 self.sustain_level,  
 self.release_ticks)  
end  
  
  
function ADSREnvelope:load_settings_string(settings_string)  
 -- Verifies a settings string and loads all parameters from within  
 assert(type(settings_string) == "string", "ADSREnvelope:load_settings_string"..  
 " - settings string must be a"..  
 " string")  
  
 -- Split string into parameters  
 local param_table = {}  
 local from = 1  
 local delim_from, delim_to = string.find(settings_string, ":", from)  
 while delim_from do  
 table.insert(param_table, string.sub(settings_string, from, delim_from-1))  
 from = delim_to + 1  
 delim_from, delim_to = string.find(settings_string, ":", from)  
 end  
 table.insert(param_table, string.sub(settings_string, from))  
  
 assert(#param_table == 5, "ADSREnvelope:load_settings_string - settings"..  
 " string has incorrect number of parameters")  
 assert(param_table[1] == "ADSR1", "ADSREnvelope:load_settings_string -"..  
 " settings string header check failed")  
  
 -- Seems ok, set parameters  
 self.attack_ticks = tonumber(param_table[2])  
 self.decay_ticks = tonumber(param_table[3])  
 self.sustain_level = tonumber(param_table[4])  
 self.release_ticks = tonumber(param_table[5])  
end  
  
  
function ADSREnvelope:copy_to(envelope)  
 -- Copies the ADSR curve to the specified envelope object  
  
 if (type(envelope) == "InstrumentMixerEnvelope") or  
 (type(envelope) == "InstrumentFilterEnvelope") then  
 -- supported envelope type  
  
 -- clear existing envelope  
 envelope:clear_points()  
  
 -- add attack points  
 if self.attack_ticks > 0 then  
 envelope:add_point_at(1, 0)  
 envelope:add_point_at(1 + self.attack_ticks, 1)  
 else  
 envelope:add_point_at(1, 1)  
 end  
  
 -- add decay points  
 envelope:add_point_at(1 + self.attack_ticks + self.decay_ticks,  
 self.sustain_level)  
  
 -- add sustain / release points  
 if self.sustain_level ~= 0 then  
 if self.release_ticks > 0 then  
 envelope:add_point_at(1 + self.attack_ticks + self.decay_ticks  
 + self.release_ticks, 0)  
 else  
 envelope:add_point_at(1 + self.attack_ticks + self.decay_ticks  
 + 1, 0)  
 end  
 envelope.sustain_position = self.attack_ticks + self.decay_ticks + 1  
 envelope.sustain_enabled = true  
 else  
 envelope.sustain_enabled = false  
 end  
  
 envelope.enabled = true  
  
 else  
 -- unsupported envelope type  
 assert(false, "ADSREnvelope:copy_to(envelope) - envelope must be of type"..  
 " InstrumentMixerEnvelope or InstrumentFilterEnvelope")  
 end  
end  
  

thanks for this. it definitely opens news ideas, as it was for my experiment to move sample loop points and morphing samples data through DSP sliders.

this could also work in almost-realtime, I think?

I would really appreciate some use-cases.
I’ve done the require ADSREnvelopeClass
I’ve done the

function adsra()
ADSREnvelope:set_attack(3)
end

and I’m not sure where to go from here. Nothing seemed to happen to selected_instrument.

Awesome, this could be useful for a lot of things