Convert range of numbers in name of notes (tostring, tonumber)

[code Trick]

I share this fairly fast way of converting the typical range of numbers from 0 to 119 to strings from C-0 to B-9 , using tostring and tonumber for a valuebox.

--tostring to valuebox
function note_tostring ( number ) --return a string, range number: 0 to 119)
  local note_name = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }
  return ("%s%s"):format( note_name[number % 12 + 1], math.floor( number/12 ) )

--tonumber to valuebox
function note_tonumber( string ) --return a number
  local note_name_1 = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }
  local note_name_2 = { "c-", "c#", "d-", "d#", "e-", "f-", "f#", "g-", "g#", "a-", "a#", "b-" }
  local note_name_3 = { "C", "C#", "D", "D#", "E", "F" , "F#", "G", "G#", "A", "A#", "B" }
  local note_name_4 = { "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" }
  for i = 1, 12 do
    for octave = 0, 9 do
      if ( string == ("%s%s"):format( note_name_1[i], octave ) ) or
         ( string == ("%s%s"):format( note_name_2[i], octave ) ) or
         ( string == ("%s%s"):format( note_name_3[i], octave ) ) or
         ( string == ("%s%s"):format( note_name_4[i], octave ) ) then
        return i + ( octave * 12 ) - 1

--the valuebox
vb:valuebox {
  height = 25,
  width = 59,
  min = 0,
  max = 119,
  value = 36,
  tostring = function( number ) return note_tostring ( number ) end,
  tonumber = function( string ) return note_tonumber ( string ) end,
  tooltip = "Tooltip text\n[Range = C-0 to B-9]"

This case is equal from the first function (but little optimized):

function note_tostring_( value ) --return a string, Range number: 0 to 119)
  if ( value < 12 ) then
    return ("%s0"):format( pht_table_notes[value + 1] )
  elseif ( value < 24 ) then
    return ("%s1"):format( pht_table_notes[value - 11] )
  elseif ( value < 36 ) then
    return ("%s2"):format( pht_table_notes[value - 23] )
  elseif ( value < 48 ) then
    return ("%s3"):format( pht_table_notes[value - 35] )
  elseif ( value < 60 ) then
    return ("%s4"):format( pht_table_notes[value - 47] )
  elseif ( value < 72 ) then
    return ("%s5"):format( pht_table_notes[value - 59] )
  elseif ( value < 84 ) then
    return ("%s6"):format( pht_table_notes[value - 71] )
  elseif ( value < 96 ) then
    return ("%s7"):format( pht_table_notes[value - 83] )
  elseif ( value < 108 ) then
    return ("%s8"):format( pht_table_notes[value - 95] )
    return ("%s9"):format( pht_table_notes[value - 107] )

The second function will allow you to manually write the strings (C-0, C#0 … ) in the valuebox and the function will convert it to its corresponding number.It also allows you to write the letters in miniscule and forget the " - ".

Please, someone who has code of this kind, could you share it?It would be interesting to have a compilation of “advanced code” for this kind of details, value conversions and so on …

Yes I also have these functions in a loose collection of musical calculation tools. I plan to one day make a nice graphical interface and lots of hooks into renoise, so the functions could be used in efficient ways. Right now I keep using the code from the scripting terminal, and many functions are really worth using, expanding what is possible with renoise considerably (close to perfect tuning tricks, or time/rhythm matching tasks, lots of that kind of stuff wherever some math can make renoise do things that seem sound to me).

Here are my number to note string and string to note number functions. I think they are based around a4 = 440Hz = note number 57, with the lowest note that is “c-0” being number zero. This could be arbitrarily offset if a task requires it.

The functions do no bounds checking yet, they were designed quite some time ago to be used by someone who feeds only reasonable values or code that does checking before calling the functions.

The number to note string function will also output cents, btw, in two flavours, the 100 cent per semitone standard, and 127 “cent” per semitone that renoise seems to use. So you can put a fractional value in it as note number, and the result string will have both “cent” measures appended, I needed that for certain tuning use cases. Also there is a second parameter “flat”, if it is not nil, it will display the notes in flats instead of sharps, don’t know why I did that, cannot remember, maybe I was drunk. Shall I clean it up as a version, so it willl just spit out the 3 char note string?

Anyways, here the 2 functions straight from my messy .lua file…I’d be proud to do some adjustments to them, like checking for legal input or optimizing, if it makes them more useful for something offered in public, that people will get for free:

function notetonumber(note)
 local NoteVal = { ["c"]=0, ["d"]=2, ["e"]=4, ["f"]=5, ["g"]=7, ["a"]=9, ["b"]=11 }
 local SignVal = { ["b"]=-1, ["-"]=0, ["#"]=1 }
 local res = -1
 if note ~= nil then
 local MyNote = string.lower(string.sub(note,1,1))
 if NoteVal[MyNote] ~= nil then
 res = NoteVal[MyNote]
 local MyNote = string.lower(string.sub(note,2,2))
 if SignVal[MyNote] ~= nil then res = res + SignVal[MyNote]; end
 if tonumber(string.sub(note,3,3)) ~= nil then res = res + 12.0 * tonumber(string.sub(note,3,3)) else res = -1; end;
 return res

function numbertonote(nnote, flat) ---jhere
 -- TODO: check note in range... - allow c-0 minus 100 cents
 local NoteVals = { "C-","C#","D-","D#","E-","F-","F#","G-","G#","A-","A#","B-" } 
 local NoteValsf = { "C-","Db","D-","Eb","E-","F-","Gb","G-","Ab","A-","Bb","B-" }
 if flat then NoteVals = NoteValsf end
 local Cents = (nnote - math.floor(nnote))
 if Cents > 0.5 then Cents = Cents - 1.0; nnote = nnote + 1.0; end
 local Octave = math.floor(nnote / 12.0)
 local SNote = math.floor(nnote - Octave*12.0)
 local sign = " + "
 if Cents < 0.0 then sign = " - "; Cents = math.abs(Cents); end
 local RCents = Cents*127
 return ((((((NoteVals[SNote+1] .. tostring(Octave)) .. sign) .. tonumber(Cents*100)) .. "c /") .. sign) .. tonumber(RCents)) .. "Rc"

Keep in mind I’m no pro lua coder, but just googled together what I needed to make my functions work while writing them, and as soon as they seemed to do what I wanted I just left them behind without further polishing. And I am completely self taught programmer, my ways of implementing stuff might sometimes be different (and more confusing) than what is normally taught.

I think optimizing the routines to work faster makes sense, when it comes to crunching pattern data that renoise will pass to the script as string? Didn’t have that task in mind when writing them, but I could try to consider that…

Oh, you wanted other functions.

Well, now let’s have some real fun converting between note number and frequency in Hertz.

As well shitty functions sloppily written, but still. I always though, maybe there are rounding error pitfals in the calculations, as results sometimes seem to differ a bit from tables. Dunno. Anyone could check this? At keast the functions spit out data that works as expected when I use it for tuning instruments.

local trt = 1.0594630943592952645
local ltrt = math.log(trt)

function notenumber2frq(note,a4f)
  note = note or 57.0
  a4f = a4f or 440.0
  local a4 = 57.0  
  return a4f * math.pow(trt, note-a4)

function freq2notenumber(freq,a4f)
  freq = freq or 440.0
  if freq < 0.1 then freq = 16.351597831287; end -- "c-0"
  a4f = a4f or 440.0
  if a4f <= 0.0 then a4f = 440.0; end
  local a4 = 57.0
  return (a4*ltrt + math.log(freq/a4f))/ltrt

they are thought to be compatible with the number values used in the functions above.

Seems we all have slightly different ways of approaching this

These are the xLib converter functions for note-column value/string representations, including note-off and “empty”:

Same, but for Effect Columns:

And some note/hz converters in cLib


If you want note_string_to_value and note_value_to_string to be really fast, I think you should generate hash tables instead of using these functions.

It’s quite easy and super fast, with the only drawback being the “ugliness” of having to fire the generator-function once upon script init (or first access).

1 Like