Hello, I don’t know what I’m doing. Trying to grasp duplex application programming and started ‘a simple project’ to begin with.
My humble goal is this: I’ve tinkered together a 3-fader touchosc layout for an iPad. Each fader has a set of 5 buttons below the fader. Now, what I want to do is to control currently selected note’s volume, panning and delay column values with these sliders. Don’t know how reasonable that is in the end, but that’s what my ‘simple project’ aims at. I’ve been at it for a couple of nights. Yesterday I got the touchosc layout done, and delved into duplex world. Tonight I’ve gotten the very basics of duplex application working, but I’m still quite lost.
Here’s what I’ve got so far in terms of files.
Touchosc layout: https://www.dropbox.com/s/gh4ryef1ux5uqqm/3faders.touchosc
Duplex controlmap: 3743 3faders.xml
Click to view contents
[xml]
TouchOSC 3faders
KMaki
3 faders with 5 buttons
[/xml]
Duplex configuration file: 3744 3faders.lua
Click to view contents
(Never mind the comments… this was a copy-paste operation… )
--[[----------------------------------------------------------------------------
-- Duplex.TouchOSC
----------------------------------------------------------------------------]]--
-- setup "Mixer + Recorder + Matrix" for this configuration
duplex_configurations:insert {
-- configuration properties
name = "3faders VolPanDel",
pinned = true,
-- device properties
device = {
class_name = "TouchOSC",
display_name = "TouchOSC",
device_prefix = nil,
device_address = "10.0.0.2",
device_port_in = "8001",
device_port_out = "8081",
control_map = "Controllers/TouchOSC/Controlmaps/3faders.xml",
thumbnail = "Controllers/TouchOSC/TouchOSC.bmp",
protocol = DEVICE_OSC_PROTOCOL,
},
applications = {
VolPanDel = {
mappings = {
pan_fader = {
group_name = "top_fader",
index = 1,
},
pan_button1 = {
group_name = "top_buttons",
index = 1,
},
pan_button2 = {
group_name = "top_buttons",
index = 2,
},
pan_button3 = {
group_name = "top_buttons",
index = 3,
},
pan_button4 = {
group_name = "top_buttons",
index = 4,
},
pan_button5 = {
group_name = "top_buttons",
index = 5,
},
--
del_fader = {
group_name = "mid_fader",
index = 1,
},
del_button1 = {
group_name = "mid_buttons",
index = 1,
},
del_button2 = {
group_name = "mid_buttons",
index = 2,
},
del_button3 = {
group_name = "mid_buttons",
index = 3,
},
del_button4 = {
group_name = "mid_buttons",
index = 4,
},
del_button5 = {
group_name = "mid_buttons",
index = 5,
},
--
vol_fader = {
group_name = "bot_fader",
index = 1,
},
vol_button1 = {
group_name = "bot_buttons",
index = 1,
},
vol_button2 = {
group_name = "bot_buttons",
index = 2,
},
vol_button3 = {
group_name = "bot_buttons",
index = 3,
},
vol_button4 = {
group_name = "bot_buttons",
index = 4,
},
vol_button5 = {
group_name = "bot_buttons",
index = 5,
},
--
},
options = {},
palette = {},
}
}
}
Duplex application file: 3745 VolPanDel.lua
Click to view contents
class 'VolPanDel' (Application)
VolPanDel.default_options = {}
-----------
function VolPanDel:__init(process,mappings,options,cfg_name,palette)
self.mappings = {
pan_fader = {
description = "Note panning 0-127",
},
pan_button1 = {
description = "Pan 25%"
},
pan_button2 = {
description = "Pan 25%"
},
pan_button3 = {
description = "Pan 50%"
},
pan_button4 = {
description = "Pan 75%"
},
pan_button5 = {
description = "Pan 100%"
},
--
del_fader = {
description = "Note delay 0-255",
},
del_button1 = {
description = "Delay 0%"
},
del_button2 = {
description = "Delay 25%"
},
del_button3 = {
description = "Delay 50%"
},
del_button4 = {
description = "Delay 75%"
},
del_button5 = {
description = "Delay 100%"
},
--
vol_fader = {
description = "Note volume 0-127",
},
vol_button1 = {
description = "Volume 0%"
},
vol_button2 = {
description = "Volume 25%"
},
vol_button3 = {
description = "Volume 50%"
},
vol_button4 = {
description = "Volume 75%"
},
vol_button5 = {
description = "Volume 100%"
},
--
}
Application.__init(self,process,mappings,options,cfg_name,palette)
end
function VolPanDel:start_app()
if not Application.start_app(self) then
return
end
end
function VolPanDel:_build_app()
do --create faders
local panf = UISlider(self.display)
panf.group_name = self.mappings.pan_fader.group_name
panf:set_pos(self.mappings.pan_fader.index)
panf.on_change = function(obj)
if not self.active then
return false
end
renoise.app():show_status("Pan change")
end
self:_add_component(panf)
self._panf = panf
local delf = UISlider(self.display)
delf.group_name = self.mappings.del_fader.group_name
delf:set_pos(self.mappings.del_fader.index)
delf.on_change = function(obj)
if not self.active then
return false
end
renoise.app():show_status("Delay change")
end
self:_add_component(delf)
self._delf = delf
local volf = UISlider(self.display)
volf.group_name = self.mappings.vol_fader.group_name
volf:set_pos(self.mappings.vol_fader.index)
volf.on_change = function(obj)
if not self.active then
return false
end
renoise.app():show_status("Vol change")
end
self:_add_component(volf)
self._volf = volf
end
--create buttons
--PAN
local panb1 = UIButton(self.display)
panb1.group_name = self.mappings.pan_button1.group_name
panb1.on_press = function(msg)
if (not self.active) then
return false
end
print("Pan button 1")
end
self:_add_component(panb1)
self._pan_button1 = panb1
--
local panb2 = UIButton(self.display)
panb2.group_name = self.mappings.pan_button2.group_name
panb2.on_press = function(msg)
if (not self.active) then
return false
end
print("Pan button2")
end
self:_add_component(panb2)
self._pan_button2 = panb2
--
local panb3 = UIButton(self.display)
panb3.group_name = self.mappings.pan_button3.group_name
panb3.on_press = function(msg)
if (not self.active) then
return false
end
print("Pan button3")
end
self:_add_component(panb3)
self._pan_button3 = panb3
--
local panb4 = UIButton(self.display)
panb4.group_name = self.mappings.pan_button4.group_name
panb4.on_press = function(msg)
if (not self.active) then
return false
end
print("Pan button4")
end
self:_add_component(panb4)
self._pan_button4 = panb4
--
local panb5 = UIButton(self.display)
panb5.group_name = self.mappings.pan_button5.group_name
panb5.on_press = function(msg)
if (not self.active) then
return false
end
print("Pan button5")
end
self:_add_component(panb5)
self._pan_button5 = panb5
--
--VOL
local volb1 = UIButton(self.display)
volb1.group_name = self.mappings.vol_button1.group_name
volb1.on_press = function(msg)
if (not self.active) then
return false
end
print("vol button1")
end
self:_add_component(volb1)
self._vol_button1 = volb1
--
local volb2 = UIButton(self.display)
volb2.group_name = self.mappings.vol_button2.group_name
volb2.on_press = function(msg)
if (not self.active) then
return false
end
print("vol button2")
end
self:_add_component(volb2)
self._vol_button2 = volb2
--
local volb3 = UIButton(self.display)
volb3.group_name = self.mappings.vol_button3.group_name
volb3.on_press = function(msg)
if (not self.active) then
return false
end
print("vol button3")
end
self:_add_component(volb3)
self._vol_button3 = volb3
--
local volb4 = UIButton(self.display)
volb4.group_name = self.mappings.vol_button4.group_name
volb4.on_press = function(msg)
if (not self.active) then
return false
end
print("vol button4")
end
self:_add_component(volb4)
self._vol_button4 = volb4
--
local volb5 = UIButton(self.display)
volb5.group_name = self.mappings.vol_button5.group_name
volb5.on_press = function(msg)
if (not self.active) then
return false
end
print("vol button5")
end
self:_add_component(volb5)
self._vol_button5 = volb5
--
return true
end
--[[
--this gets either 'vol' 'pan' or 'del' value, in 0-1 scale
function VolPanDel:get_value(val_type)
local min
local max
local value
if renoise.song().selected_note_column == nil then
return nil
elseif val_type == "vol" then
min = 0
max = 127
value = renoise.song().selected_note_column.volume_value
if value == 255 then value = 64 end
elseif val_type == "pan" then
min = 0
max = 127
value = renoise.song().selected_note_column.panning_value
if value == 255 then value = 64 end
elseif val_type == "del" then
local min = 0
local max = 255
local value = renoise.song().selected_note_column.delay_value
end
return value / max --not perfect yet.
end
--this gets either 'vol' 'pan' or 'del' value, in 0-1 scale
function VolPanDel:set_value(val_type, value)
local min
local max
if renoise.song().selected_note_column == nil then
return nil
elseif val_type == "vol" then
min = 0
max = 127
if value == 255 then value = 64 end
renoise.song().selected_note_column.volume_value = max(min(value*max,max),min)
elseif val_type == "pan" then
min = 0
max = 127
renoise.song().selected_note_column.panning_value = max(min(value*max,max),min)
elseif val_type == "del" then
local min = 0
local max = 255
renoise.song().selected_note_column.delay_value = max(min(value*max,max),min)
end
return value / max --not perfect yet.
end
--]]
At the moment I’m just familiarizing myself with the whole Duplex concept, and working to get a connection with A.) touchosc & duplex, and B.) duplex & renoise. Part A.) I think I’ve overcome, because I’ve got the faders kind of working. I was happy when I accomplished that. The duplex virtual control surface gets created all right (ugly at the moment), and the faders are in sync between my controller and renoise. I’ve even got an .on_change function for the faders. Just a placeholder that throws a status message, but at least they’re doing something.
It’s Part B.) I’m having difficulties with. Or rather, the entrails of Duplex are just too vast for me to comprehend.
I’m running into a serious brain melt with the buttons. I’m not quite sure what I’m supposed to do (or what I’m doing right now for that matter), but the problem is that when I press the first button in the top row, I get in response all .on_press functions from all the buttons in the group. This I’m deducing with printouts to terminal. The other buttons are not responding at all. The duplex layout states they are “(unassigned)”. Huh?
What I’d eventually like the buttons to do is to just quickly set the volume/panning/delay to 0%, 25%, 50%, 75% or 100%.
Is there some other on-hands documentation than this post: Creating a simple duplex application, and the duplex docs? I admit I’ve maybe strayed off to areas of tool development reserved to pure genius, but I’d like to give it a shot anyway. I’ve used the post behind the link as a reference with my endeavor. I’ve also skimmed through some existing duplex files.
And. If someone could maybe give a hint on what’s going on with the buttons, I’d appreciate it very much.