Build a perfect Virtual Piano using buttons only

You could probably get more colors out of strings if you were to run Renoise under a color terminal and output (debugging) strings directly to stdout using ANSI escape sequences:

io.stdout:write("\27[33;1mThis is yellow!\n")
io.stdout:write("\27[31;1mThis is red!\n")
io.stdout:write("\27[35;1mThis is magenta!\n")
io.stdout:write("\27[34;1mThis is blue!\n")

Thinking more Linux here though, don’t know about Windows :slight_smile:

Good little piano ‘button’ keyboard btw Raul :slight_smile:

Thanks 4Tey!I am using windows at all times and linux for boot with USB.So I do not have a proper machine to try Renoise under Linux or Mac.

I keep thinking things with colors.I have not seen the specific documentation, but there should be some way to detect that the selected instrument uses samples (not VSTi).Considering the keyzones.There should be a simple way to colorize gray (or active = false) the keys that do not have any associated samples.Disable non-serving keys…With this, in addition to adjusting the size of the piano, and being able to control the keys through MIDI (especially), USB keyboard and mouse, would be a frankly useful and attractive tool.

MIDI pressed and MIDI released to control the “note buttons”???I mean all the notes of the 10 octaves, the 120 notes.The virtual piano would control the 120 notes, but visually only the necessary octaves would appear. I think 3 octaves would be enough.

I have used to call a function:

renoise.tool():add_midi_mapping {
  name = THE_NAME,
  invoke = function(message)
    if (message:is_trigger()) then
      name_func()
    end
  end
}

MIDI is_trigger() is equal to button released… InRenoise.ScriptingTool.API.lua:

class "renoise.ScriptingTool.MidiMessage"
    
      -- returns if action should be invoked
      function is_trigger() -> boolean

      -- check which properties are valid
      function: is_switch() -> boolean
      function: is_rel_value() -> boolean
      function: is_abs_value() -> boolean

      -- [0 - 127] for abs values, [-63 - 63] for relative values
      -- valid when is_rel_value() or is_abs_value() returns true, else undefined
      property: int_value

      -- valid [true OR false] when :is_switch() returns true, else undefined
      property: boolean_value

is_swith() act like a button, with pressed, and released?What I would like to build is a way to control the virtual keyboard with the MIDI keyboard, all notes.Since there are 120 notes, you really only need to deal with one note.The rest is repeat.My final idea is to build a virtual piano that can control notes with the midi keyboard, but also to be able to edit in the pattern editor the notes directly with the mouse on the virtual keyboard.

  • With the mouse the sound can use the osc server.
  • With the midi keyboard I understand that the sound should “go alone”.

If you want to do a virtual-midi keyboard alike thing with MIDI input, you’ll need for the tool to configure it’s own MIDI input.

There is an example on how to do this here:

https://github.com/renoise/xrnx/blob/master/Snippets/Midi.lua

It lets you receive raw MIDI data, which you can then do whatever with you want.

All the basic MIDI messages (so, not sysex and such stuff) is really just three bytes of data - so quite simple to process.

(check the MIDI input panel to investigate incoming messages)

  • With the mouse the sound can use the osc server.
  • With the midi keyboard I understand that the sound should “go alone”.

If you want to trigger notes in realtime, this is done through the OSC server, no matter what

Remember, you tool defines it’s own MIDI input port - independently of Renoise.

You can send messages using the OSC server in two ways:

  1. “Raw” midi message which is received by whichever instrument is selected

  2. “Extended” message which targets a specific instrument and/or track

The “extended” message is dealing with triggering notes only, so if you want to pass CC data from the keyboard (mod wheel?) to Renoise (or other tools), then you need to send as “raw” midi

Raw MIDI works like incoming midi message and can be MIDI mapped to any element that light up when pressing CTRL+M (MIDI mapping mode).

Hope this helps!

@Raul,
Here is the way to build your widget programatically, done for the fun of it :slight_smile: This will also let you decide how many octaves to build.

(A version that would render from any key to any key would be a bit more complex/different than this, but this is a first step)

Click to view contents
function pw_build_octave(octave)
  local intervals = { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 }
  local black_spaces = { [2] = 8, [4] = 22, [7] = 7, [9] = 7 }
    
  local white_keys = vb:row { spacing = -3 }
  local black_keys = vb:row { }
  local octave_content = vb:row {
    spacing = -131,
    white_keys,
    black_keys }
  
  for key = 1, 12 do
    if intervals[key] == 1 then
      white_keys:add_child(
        vb:button {
          height = 66, width = 23,
          color = { 255, 255, 255 },
          id = "KEY_" .. (key - 1) + (12 * octave),
        })
    else
      black_keys:add_child(
        vb:button {
          height = 40, width = 15,
          color = { 1, 1, 1 },
          id = "KEY_" .. (key - 1) + (12 * octave),
        })   
      if key <= 9 then
        black_keys:add_child(vb:space { width = black_spaces[key] })
      end      
    end
  end
  return octave_content
end

function pw_build(octaves)
  local piano_widget = vb:row { margin = 5, spacing = -2 }
  for octave = 0, octaves-1 do
    piano_widget:add_child( pw_build_octave(octave) )
  end
  return piano_widget
end
local my_pianowidget_content = pw_build(4)

@Joule, thank you very much! :slight_smile:

I added a bit of layout:

Click to view contents
function white_tooltip (key, octave)
  if key == 1 then return "C-"..octave.."" end
  if key == 3 then return "D-"..octave.."" end
  if key == 5 then return "E-"..octave.."" end
  if key == 6 then return "F-"..octave.."" end
  if key == 8 then return "G-"..octave.."" end
  if key ==10 then return "A-"..octave.."" end
  if key ==12 then return "B-"..octave.."" end
end
---
function black_tooltip (key, octave)
  if key == 2 then return "C#"..octave.."" end
  if key == 4 then return "D#"..octave.."" end
  if key == 7 then return "F#"..octave.."" end
  if key == 9 then return "G#"..octave.."" end
  if key ==11 then return "A#"..octave.."" end
end
---
function white_bitmap (key, octave)
  if key == 1 then
    for i = 0, 9 do
      if octave == i then return "icons/c-"..i..".png" end
    end
  end
  --if key == 1 then return "icons/c.png" end
  if key == 3 then return "icons/d-.png" end
  if key == 5 then return "icons/e-.png" end
  if key == 6 then return "icons/f-.png" end
  if key == 8 then return "icons/g-.png" end
  if key ==10 then return "icons/a-.png" end
  if key ==12 then return "icons/b-.png" end
end

  function pw_build_octave(octave)
  
    local intervals = { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 }
    local black_spaces = { [2] = 8, [4] = 22, [7] = 7, [9] = 7 }
      
    local white_keys = vb:row { spacing = -3 }
    local black_keys = vb:row { }
    local octave_content = vb:row {
      spacing = -131,
      white_keys,
      black_keys
    }
    
    for key = 1, 12 do
      if intervals[key] == 1 then
        white_keys:add_child(
          vb:button {
            height = 66, width = 23,
            color = { 255, 255, 255 },
            id = "KEY_" .. (key - 1) + (12 * octave),
            tooltip = white_tooltip(key, octave), --<<
            bitmap = white_bitmap(key, octave), --<<
            pressed = function(key, octave) end, --<<
            released = function(key, octave) end --<<
          })
      else
        black_keys:add_child(
          vb:button {
            height = 40, width = 15,
            color = { 1, 1, 1 },
            id = "KEY_" .. (key - 1) + (12 * octave),
            tooltip = black_tooltip(key, octave), --<<
            pressed = function(key, octave) end, --<<
            released = function(key, octave) end --<<
          })   
        if key <= 9 then
          black_keys:add_child(vb:space { width = black_spaces[key] })
        end      
      end
    end
  
    return octave_content
  end
  
  
  function pw_build(octaves)
    local piano_widget = vb:row { margin = 5, spacing = -2 }
  
    for octave = 0, octaves-1 do
      piano_widget:add_child( pw_build_octave(octave) )
    end
  
    return piano_widget
  end
  
  my_pianowidget_content = pw_build(4)
tooltip = white_tooltip(key, octave), --<<
bitmap = white_bitmap(key, octave), --<<
pressed = function(key, octave) end, --<<
released = function(key, octave) end --<<

...

tooltip = black_tooltip(key, octave), --<<
pressed = function(key, octave) end, --<<
released = function(key, octave) end --<<

7449 vb-perfect-piano-2.png

I think it would be all set to have a virtual piano as a base.From here, on the 10 octave piano, it is possible to add 2 side scroll buttons and display only 3 or 4 octaves to make a more compact tool. Another button on the left to deploy a bottom line with more buttons to trigger functions.

For now, I think I would be able to include 3 things:

  1. OSC Server for sound.
  2. Control writing in the pattern editor using the mouse with the virtual piano.
  3. Control writing in the pattern editor using the USB keyboard with the virtual piano, according the “Oct” value of Renoise.

I find it an unknown world yet for include control via MIDI input.This involves connecting a MIDI keyboard and pressing the keyboard notes that the virtual piano only can illuminate the keys (pressed and released).

Yes. By making a class you can include smart properties (“first_octave”, “last_octave” et c) and make sure that these will refresh the whole piano view whenever they are changed.

… allowing a syntax that would look something like this:

local piano_widget = PianoWidget { first_octave = 2, last_octave = 7 } -- creates an object by the PianoWidget class
piano_widget.first_octave = 3 -- set a new value for first_octave. in the class, a property setter function is used to refresh the piano view

A class can be used as an interface that makes dynamic behavior like this a breeze. You can imagine how simple it would be to scroll, increase or decrease number of octaves by just pressing a button, and with this kind of automatic rebuilding.

PS. I noticed that you linked a tutorial on LUA classes previously. However, Renoise allows a nicer syntax than the one found in conventional lua tutorials.

If you want to do a virtual-midi keyboard alike thing with MIDI input, you’ll need for the tool to configure it’s own MIDI input.

There is an example on how to do this here:

https://github.com/renoise/xrnx/blob/master/Snippets/Midi.lua

It lets you receive raw MIDI data, which you can then do whatever with you want.

All the basic MIDI messages (so, not sysex and such stuff) is really just three bytes of data - so quite simple to process.

(check the MIDI input panel to investigate incoming messages)

If you want to trigger notes in realtime, this is done through the OSC server, no matter what

Remember, you tool defines it’s own MIDI input port - independently of Renoise.

You can send messages using the OSC server in two ways:

  1. “Raw” midi message which is received by whichever instrument is selected

  2. “Extended” message which targets a specific instrument and/or track

The “extended” message is dealing with triggering notes only, so if you want to pass CC data from the keyboard (mod wheel?) to Renoise (or other tools), then you need to send as “raw” midi

Raw MIDI works like incoming midi message and can be MIDI mapped to any element that light up when pressing CTRL+M (MIDI mapping mode).

Hope this helps!

I have not gone into depth to control a specific tool with MIDI input.Thinking about a virtual piano, the only thing that would need to be controlled is the reaction of a button according to the note pressed with the MIDI keyboard.That’s 120 buttons on the tool related keys 120 notes of the MIDI keyboard.In the end, the code is summarized in configuring a single button with the MIDI input code. Then add the remaining 119.

The MIDI code that is needed to run the tool, will serve for any model MIDI keyboard?

Yes. By making a class you can include smart properties (“first_octave”, “last_octave” et c) and make sure that these will refresh the whole piano view whenever they are changed.

… allowing a syntax that would look something like this:

local piano_widget = PianoWidget { first_octave = 2, last_octave = 7 } -- creates an object by the PianoWidget class
piano_widget.first_octave = 3 -- set a new value for first_octave. in the class, a property setter function is used to refresh the piano view

A class can be used as an interface that makes dynamic behavior like this a breeze. You can imagine how simple it would be to scroll, increase or decrease number of octaves by just pressing a button, and with this kind of automatic rebuilding.

PS. I noticed that you linked a tutorial on LUA classes previously. However, Renoise allows a nicer syntax than the one found in conventional lua tutorials.

I’ve been looking at this.It is also possible to do it as follows:

function pw_build(first_octave, last_octave) --range 0 to 9 (10 octaves)
    local piano_widget = vb:row { margin = 5, spacing = -2 }
  
    for octave = first_octave, last_octave do
      piano_widget:add_child( pw_build_octave(octave) )
    end
  
    return piano_widget
  end
  
  my_pianowidget_content = pw_build(3,6)

Withpw_build(3,6) octaves 3, 4, 5, 6 appear.It is thus possible to easily change or move it.Move one octave to the left would bepw_build(2,5).Maximize:pw_build(0,9).Only one octave (octave 4): pw_build(4,4)

Yes, of course you can use function oriented programming for anything if you want to, but it’s not a very good (flexible and future friendly) way of designing the code.

So I take it you won’t be touching Clojure then Joule? :wink:

C
    You shoot yourself in the foot. 

Assembly Language
    You crash the OS and overwrite the root disk. The system administrator arrives and shoots you in the foot.
    After a moment of contemplation, the administrator shoots himself in the foot and then hops around the room rabidly shooting at everyone in sight. 

C++
    You accidentally create a dozen instances of yourself and shoot them all in the foot. 
    Providing emergency medical care is impossible since you can't tell which are bitwise copies and which are just 
    pointing at others and saying "that's me, over there."

I like good ol’ assembly language :slight_smile:

Lately I’ve been busy with another tool. I leave here a screenshot that shows the appearance of a tool with two virtual pianos, one horizontal and one vertical.

7549 VPDpro_v1.0.png

Both pianos have been constructed using the methodology of this topic.The vertical virtual piano could be used in any tool similar to a piano roll.

It looks so complicated. You must have used a lot of selfmade bitmaps.

This tool is for users with midicontrollers, right?

It looks so complicated. You must have used a lot of selfmade bitmaps.

No, actually I’ve only used a couple of templates to create the images. As it is a repetitive process it is very simple.On the other hand, I have a hobby to create new icons. I like it.

This tool is for users with midicontrollers, right?

Yes. With a Midi controller keyboard you can compose with live recording, but not edit.This tool allows editing with the mouse and with the midi Input.In fact, you could compose a song without using an alphanumeric USB keyboard.Also, I added the ChordPad with the same capacity. You can add chords with the mouse or with any Midi pad.The tool is designed to be compact.You can select the columns you want to display.

It may seem a bit complex. But the compressed tool does not occupy more than 100KB.

The tool is designed to be compact

Did you try it on tablets?

Did you try it on tablets?

No.I’m working using Windows 10 and a normal monitor.But I guess on a touch screen could be controlled. That is, composing a song through the tool from the touch screen.It is designed to take advantage of the maximum height in a monitor of 1024 x 768, at least.For the touch screen it would be necessary to add a crosshead or joystick(up down right left).It could work…

The advantage of this tool is that it offers a large area to view and edit all the parameters that you can use within the pattern editor, except the parameters derived from the automation of the effects chains.Actually, the tool “shows a zoom” of one line of the selected note column.It allows better understanding of the parameters used and available…

Yes, of course you can use function oriented programming for anything if you want to, but it’s not a very good (flexible and future friendly) way of designing the code.

Why? I am asking as a clojure developer :slight_smile:

It’s more practical when making something that is more complex. This means that it will likely take less time to add new features in the future (it’s not uncommon to realize that a lot of stuff must be restructured when adding some new feature to function oriented code). Also, it’s a more true representation of the actual behavior of a software, I think. In addition, the classes can be more reusable as they are likely more simple to implement in other sotware.

Hi Raul. Do you still have the .xrnx file for the basic virtual piano you had a screenshot of? Would you mind making that available please if you do? I just want to enter notes with both the track window and piano keys visible at the same time. Normally I’d use my Midi keyboard but this is for an academic exercise on my laptop while away from home.

Hi Raul. Do you still have the .xrnx file for the basic virtual piano you had a screenshot of? Would you mind making that available please if you do? I just want to enter notes with both the track window and piano keys visible at the same time. Normally I’d use my Midi keyboard but this is for an academic exercise on my laptop while away from home.

Hi Sparky! Currently the closest thing is the KangarooX120 tool.Among other things, it has a horizontal virtual piano with all the octaves that allows to introduce any note with the mouse from each key of the piano.You can also map it for your MIDI Input.

If you are not interested in the pads panel,you can minimize it, to show only this virtual piano:

[sharedmedia=core:attachments:8135]

Hi Sparky! Currently the closest thing is the KangarooX120 tool.Among other things, it has a horizontal virtual piano with all the octaves that allows to introduce any note with the mouse from each key of the piano.You can also map it for your MIDI Input.

If you are not interested in the pads panel,you can minimize it, to show only this virtual piano:

Excellent! Thanks for that. Although I am interested in playing with Lua another time, I was just wanting a music keyboard for today. That’s great - just what I needed, thank you.

Excellent! Thanks for that. Although I am interested in playing with Lua another time, I was just wanting a music keyboard for today. That’s great - just what I needed, thank you.

I’m glad it serves you. If you intend to play with the LUA code, maybe KangarooX120 is not an easy tool to start with. Use custom buttons (many) and several classes to define specific objects. But if you want to learn how to make a virtual piano for Renoise, it is possible to make a very basic tool so that the code is easy to understand and simpler (fewer letters to read).