How is the hexadecimal system incorporated in Renoise?

I wanted to inquire by those who are more experienced about how the hexadecimal numbering system is incorporated in Renoise. I had always heard that Renoise used it and its party of what piqued my interest in this software in the first place. I was trying to understand how its implemented so that I could try to use it with the Lua scripting capabilities. I figured it could be quite useful in terms of developing some novel granular synthesis types of retriggered sequenced sounds.

Anyone have any general thoughts on how the hexadecimal system functions in Renoise? how it can be leveraged with Lua scripting? And how it may help in terms of generating granular types of sounds?

I recommend having a look at Wikipedia about how it’s laid out.

It’s just a way to represent values (a way to type numbers). The main practical difference here is that it’s a slightly more compact way to represent values, compared to the decimal system.

To clarify, I know how hexadecimal works. I guess my question is: what numbers in Renoise are implemented in hexadecimal? The time signatures? BPM? Etc.

No. The pattern editor.

What aspect(s) of the pattern editor are in hexadecimal?

Check out the online manual about pattern commands and their min / max values. For example the patterncommand for pitching downwards is Dxx, where the xx part references the hexidecimal values, the min value is 00 and the max ff. I think there is a youtube tutorial on this on the official renoise channel.

If you go into scripting, these are the functions to convert between hex and dec:

dec = tonumber(hex_string, 16)
hex = string.format("%02X", dec) – formats dec 0-255 to hex “00”-“FF”, as used widely in the pattern editor.

It’s usually not often that you’d have to use this, as the note column class provides decimal getters/setters.

Renoise has a somewhat special way of dealing with this issue. As a general rule, those values that must be shown on screen whose range exceeds 3 digits in decimal is converted to hexadecimal, simply to occupy less space: 2 digits.
Here there are various groups, with ranges that include:

  • 00-7F, 128 values (MIDI I / O uses 128 values), as a number.
  • 00-FF, 256 values, as the index of the delay, as a number, etc.
  • 00-FE, 255 values, as the index of the instruments (?), as a number. Why FF does not exist?
  • “00” - “ZZ”, which can be used as a string in many effect parameters.
  • 0-119 for notes, 120 for Note-OFF, 121, empty note.
  • BPM, LPB, TPL, keyboard velocity… in decimal…

…the list is a little long.

In addition, there are special cases where another type of numbering is used:

  • As the volume of the instrument: 0-math.db2lin (6).

There are also cases where decimal values ​​are used but can be represented in hexadecimal, such as the index of the lines.
The index of the pattern or sequence is in decimal, ranging from 0 to 999. Similar for tracks, whose range is not defined (0 to >1000).

I think you do not have to get sick with all this. Simply look in the API documentation (Renoise.Song.API.lua) for the data you need for your script and see what numerical notation it is in.

Here is a concrete case for build a personalized valuebox (decimal to hexadecimal):

    local name=""
    if (value<=#song.instruments) then
    return ("%.2X| %s"):format(value-1, name)
  tonumber=function(value) if (tonumber(value,16)~=nil) then return tonumber(value,16)+1 end end,
  midi_mapping=("Tools:%s:Chord Builder:Advanced Controls:Instr. Distribution %.2d [Set]"):format(pre_main_title,idx),
  tooltip=("Instrument distribution %s.\nRoute this instrument into scile %s.\n(00 to FE)"):format(idx,idx)

“idx” is an index to be able to iterate with it. This case is shielded against unwanted cases, such as entering an invalid value.

Since the “valuebox.value” depends on “idx” (which is a number that can grow), it must be checked that it does not exceed 255, in this case for the range of instruments (max 255).
“tonumber” should be checked to not return nil in case the user enters an invalid value within the valuebox.

That is, when you build your script, you should always be clear about the range, minimum and maximum value to exclude everything else and not return an invalid value error.

You will always have to compare decimal values, and then convert them to hexadecimal if necessary.

To sample modulation, you can see these values:

-- Play mode (interpolation mode).[].sample_modulation_sets[].devices[].play_mode, _observable
  -> [enum = PLAYMODE]

-- Envelope length.[].sample_modulation_sets[].devices[].length, _observable
  -> [number, 6-1000]

-- Loop.[].sample_modulation_sets[].devices[].loop_mode, _observable
  -> [enum = LOOP_MODE][].sample_modulation_sets[].devices[].loop_start, _observable
  -> [number, 1-envelope.length][].sample_modulation_sets[].devices[].loop_end, _observable
  -> [number, 1-envelope.length]

-- Sustain.[].sample_modulation_sets[].devices[].sustain_enabled, _observable
  -> [boolean][].sample_modulation_sets[].devices[].sustain_position, _observable
  -> [number, 1-envelope.length]

-- Fade amount. (Only applies to volume envelopes)[].sample_modulation_sets[].devices[].fade_amount, _observable
  -> [number, 0-4095]

-- Get all points of the envelope. When setting a new list of points,
-- items may be unsorted by time, but there may not be multiple points
-- for the same time. Returns a copy of the list, so changing
-- `points[1].value` will not do anything. Instead, change them via
-- `points = { something }` instead.[].sample_modulation_sets[].devices[].points[], _observable
  -> [array of {time, value} tables]

-- An envelope point's time.[].sample_modulation_sets[].devices[].points[].time
  -> [number, 1-envelope.length]
-- An envelope point's value.[].sample_modulation_sets[].devices[].points[].value
  -> [number, 0.0-1.0]