Idea: Duplex Step Sequencer

Would it be possible with Duplex or Lua scripting to create a xox stepsequencer for something like launchpad or similar pad-based controller?

What I’m thinking is the following:

  • you selected the instrument;
  • you select the resolution;
  • you press the pads where you want the sound;
  • you select another instrumenr, rinse, repeat;
    Ideally you would be able to audition the loop while programming.
    Pnce you’re happy, you dump it to the pattern, in different tracks if possible…

Well, there you are. You pretty much outlined how a roland-style sequencer could work!
And yes, you should be able to build such a beast with all the features you have mentioned.

Actually, the very first thing I did with Lua scripting was a performance sequencer. The code is kind of messy (as it was my first Lua script), so I definitely would want to rewrite that one at some point. But here’s what I learned while making that script:

First of all, it’s based on turning dials, so there is a range of 128 per step. From my controller, I can switch between adjusting the note, the duration (in ticks), the offset (09xx command), and the volume. Everything is output in realtime, so you can press play in Renoise, and then simply start to spew notes all over the place. To keep things organized, the sequencer will output each step in a separate note column, which means that it has a maximum of 12 steps. So far so good…

Now, this is not that easily translated to a grid controller, because you don’t have a range of 128 per step. Actually, you have a very limited range (usually, 8). So the first thing to think about is probably scaling of note values. Of course, it’s possible to have the full range of notes represented in the grid with some kind of paged/scrolling mechanism, but it’s not very ergonomic that you are forced to scroll up and down, just to plot in a basic sequence? But say that you’re working with some custom scale, like the pentatonic scale (the ‘black keys’ scale), then you’ll suddenly be able to represent 1 1/2 octave of notes on a Launchpad. Much more useful than the 2/3 octave possible with a normal scale.

I can share the code with you, if you’re interested?

not to hijack

automageddon thats sounds like a interesting idea you got

and danoise having something like that for the launchpad would be awesome(im a fanboy of the launchpad :D )

This is the suggestion part, I don’t even know where to start coding peeps!
But glad to know that something like that could be done, I tried last night the Drum Processor (http://tools.renoise.com/tools/drum-processor ) and it blew my mind away, it made drum programming in renoise so much faster!

Your performance sequencer sounds amazingly useful! Seems like it would work out better on the iPad. Like how chipPAD can have different grid resolutions per track:

I for one would love to see that code, danoise. Amazing work on Duplex!

Just started looking at the Renoise scripting tonight, but here’s what I’m thinking for a Launchpad step sequencer:

  • 8x8 grid represents 8 steps on 8 channels (probably tracks are columns for consistency with Renoise)
  • press a button to toggle that step on/off (writes directly to pattern editor using selected instrument)
  • “trigger button” column on the right could allow you to select one of 8 pre-defined notes for your step (not sure if Duplex will let me do this by holding down a grid step and changing the note with the trigger buttons - if it can’t do holding then I guess just the last-pressed grid step?)
  • left/right and up/down control buttons could “scroll” the 8x8 grid to see more channels or more steps
  • the other 4 control buttons could either be transport control or maybe instrument selection and something else

From what I’ve gathered I’ll need to create an Application for the pattern editor (this is where I’m kind of stumped right now - looking at the Matrix application, but it’s complicated) and then a new configuration in the Launchpad.lua that maps that application to the buttons… Is that it?

Is there any friendly documentation on Duplex? Basic walkthrough of how Applications and Controllers communicate, required methods, etc? I’m starting to piece it together by looking at the existing code but being completely new to lua doesn’t help :)

i would also love to see that code

i was thinking on something similar for the launchpad,but havent had the time to check up on things yet

Friendly documentation? That would be me At least, I try to answer every Duplex-related question, so keep 'em coming…

But to answer your question, I have not written anything except the basic API docs, no. And since Duplex is a real framework in the sense that it extend the functionality of the Renoise API, a good understanding of Renoise scripting is of course needed before you go write a new application.

But, also notice that something like the Matrix app has a lot of observable values, checking for tracks being changed in realtime etc. It needs to keep track of all those things, which is basically why it has to be so complex. When you remove all these checks, the app is in fact very simple…

OK so per request I have dug out the step-sequencer script !! It’s really messy, but hopefully someone will have some fun with it. Turning a dial, and watching notes crawl up and down the screen is kind of a ‘wow, is this really still a tracker’ feeling, so I hope you enjoy that :slight_smile:

Installation

  1. Take the following code and add it to your GlobalMIDIActions.lua file (remember to take a backup first)

[luabox]------------------------------------------------------------------------------
– Step Sequencer

– length valid range 1-12 (limited by the number of note columns)
– param selected parameter: 1=note, 2=gate, 3=offset, 4=volume
– note valid range 0-121, values 120 & 121 are note-off and empty note
– also, the special value -1 is “strong empty” (skip transposing note)
– gate valid range 0-255 (ticks), 0 is instantly turned off, 255 is always on.
– note that a higher tick value (in lines) than the sequence is ignored
– offset valid range 0-255, 0 is not written to the pattern
– volume valid range 0-127, 127 being full volume and not written to pattern
– adjust affect note/gate/offset/volume
– extend extend output to match track length (boolean)
– global output all voices simultaneously (boolean)
– value# control each step individually

step = {}
step.global = false
step.extend = true
step.modes = {}
step.modes.NOTE = 1
step.modes.GATE = 2
step.modes.OFFSET = 3
step.modes.VOLUME = 4
step.mode = step.modes.NOTE
step.sequence = {}
step.sequence.length = 8
step.sequence.DEFAULT_NOTE_VALUE = -1
step.sequence.DEFAULT_GATE_VALUE = -1
step.sequence.DEFAULT_OFFSET_VALUE = 0
step.sequence.DEFAULT_VOLUME_VALUE = 127

– adjusts note/instrument: transpose an octave down by setting this to -12
step.basenote = 0
step.sequence.note = {}
step.sequence._note = {}
step.sequence.set_note = function(idx,val)
step.sequence.note[idx] = clamp_value(val,-1,121)
step.sequence._note[idx] = step.sequence.note[idx]
end

– adjusts duration: will extend each gate by the selected number of ticks
step.gate = 0
step.sequence.gate = {}
step.sequence._gate = {}
step.sequence.cached_gate = {}
step.sequence.set_gate = function(idx,val)
step.sequence.gate[idx] = clamp_value(val,-1,127)
step.sequence._gate[idx] = step.sequence.gate[idx]
end

– sample offset
step.offset = step.sequence.DEFAULT_OFFSET_VALUE
step.sequence.offset = {}
step.sequence._offset = {}
step.sequence.set_offset = function(idx,val)
step.sequence.offset[idx] = clamp_value(val,0,127)
step.sequence._offset[idx] = step.sequence.offset[idx]
end

– volume: 0 for no sound, 1 for unaltered level
step.volume = 1
step.sequence.volume = {}
step.sequence._volume = {}
step.sequence.set_volume = function(idx,val)
step.sequence.volume[idx] = clamp_value(val,0,127)
step.sequence._volume[idx] = step.sequence.volume[idx]
end

– produce “Fx” hex value
step.ticks_to_notecut = function(val)
return val+240
end

– interpret notecut+line(s)
@param lines - the number of lines
@param val - the effect value
@return number of ticks
step.notecut_to_ticks = function(lines,val)
return (lines*song().transport.tpl)+(val-240)
end

– produce “rhytmic” offset values
step.to_discrete_steps = function(val)
return math.floor(((val+1)*2)/16)*16
end

– get the currently focused column
step.get_selected_column = function()
return clamp_value(song().selected_note_column_index,1,step.sequence.length)
end

– display useful information
step.show_status = function(str)
app():show_status(str)
end

– sequence.clear()
step.sequence.clear = function()
for idx = 1,step.sequence.length do
step.sequence.set_note(idx,step.sequence.DEFAULT_NOTE_VALUE)
step.sequence.set_gate(idx,step.sequence.DEFAULT_GATE_VALUE)
step.sequence.set_offset(idx,step.sequence.DEFAULT_OFFSET_VALUE)
step.sequence.set_volume(idx,step.sequence.DEFAULT_VOLUME_VALUE)
end
end

– sequence.set_length()
– supply default values for undefined indices, skip existing values
step.sequence.set_length = function(new_length)
for idx = 1,new_length do
if(step.sequence.note[idx]==nil)then
step.sequence.set_note(idx,step.sequence.DEFAULT_NOTE_VALUE)
end
if(step.sequence.gate[idx]==nil)then
step.sequence.set_gate(idx,step.sequence.DEFAULT_GATE_VALUE)
end
if(step.sequence.offset[idx]==nil)then
step.sequence.set_offset(idx,step.sequence.DEFAULT_OFFSET_VALUE)
end
if(step.sequence.volume[idx]==nil)then
step.sequence.set_volume(idx,step.sequence.DEFAULT_VOLUME_VALUE)
end
end
step.sequence.length = new_length
end

– show_note_column
– called when data is written to the pattern to ensure that columns are visible
– todo: make optional (advanced settings)
step.show_note_column = function(idx)
if idx > renoise.song().selected_track.visible_note_columns then
renoise.song().selected_track.visible_note_columns = idx
end
end

– output_sequence
@mask_col - optional, column == sequencer index
@mask_type - optional, int or nil
step.output_sequence = function(mask_col,mask_type)
if not song().transport.edit_mode then
return
end
if step.global == true then
mask_col = nil
end
local tpl = song().transport.tpl
local instr_index = song().selected_instrument_index-1
local playback_line = song().transport.edit_pos.line
local curr_line = 1
local offset = nil
local show_column = nil
local col_index = nil
local note_set,gate_set,offset_set,volume_set
local set_note,set_gate,set_offset,set_volume
local writeahead_length = step.sequence.length-1
if step.extend then
writeahead_length = 512
end
local iter = renoise.song().pattern_iterator:lines_in_pattern_track(
song().selected_pattern_index,
song().selected_track_index);
if mask_type then
set_note = (mask_type == step.modes.NOTE)
set_gate = (mask_type == step.modes.GATE)
set_offset = (mask_type == step.modes.OFFSET)
set_volume = (mask_type == step.modes.VOLUME)
else
set_note = true
set_gate = true
set_offset = true
set_volume = true
end
for pos,line in iter do
if(curr_line>=playback_line) then
if(writeahead_length<0) then
break
else
offset = curr_line%step.sequence.length
if(offset==0) then – fix
offset = step.sequence.length
end
– process note columns
col_index = 1
for i,note_column in ipairs(line.note_columns) do
– using masked mode? (global will always continue)
local continue = true
if not step.global then
if mask_col and mask_col ~= col_index then
continue = false
end
end
if continue then
show_column = false
note_set = false
gate_set = false
volume_set = false
– do stuff on the trigger point
if(offset==col_index ) then
if set_note then
local val = step.sequence.note[offset]
if(val==-1) then
– skip undefined notes
elseif(val>120) then
– don’t adjust when set to note-off or empty
note_column.note_value = val
note_column.instrument_value = 255
else
note_column.note_value = clamp_value(val+step.basenote,0,121)
note_column.instrument_value = instr_index
show_column = true
end
note_set = true
end
if set_volume then
local val = math.floor(step.sequence.volume[offset]*step.volume)
if val==127 or val==255 then
note_column.volume_value = 255
else
note_column.volume_value = clamp_value(val,0,127)
show_column = true
end
volume_set = true
end
if set_gate then
step.sequence.cached_gate[col_index] = {}
local val = step.sequence.gate[offset]+step.gate
if val==255 or val==-1 then
– don’t bother
elseif(val<tpl) then
– insert the notecut right away
note_column.panning_value = step.ticks_to_notecut(val)
step.sequence.cached_gate[col_index][col_index] = val
gate_set = true
show_column = true
else
– insert the notecut later
local tick_value = val%tpl
local row_value = math.floor(val/tpl)+1%step.sequence.length
if(row_value<=step.sequence.length) then
local target_row = ((row_value+(col_index-1))%step.sequence.length)
if(target_row==0) then --fix
target_row = step.sequence.length
end
step.sequence.cached_gate[col_index][target_row] = tick_value
end
end
end
else
if set_gate then
– look for scheduled notecut
if(step.sequence.cached_gate[col_index]) then
local tmp_value = step.sequence.cached_gate[col_index][offset]
if(tmp_value) then
note_column.panning_value = step.ticks_to_notecut(tmp_value)
gate_set = true
show_column = true
end
end
end
end
– clear existing?
if set_gate and not gate_set then
note_column.panning_value = 255
end
if set_note and not note_set then
note_column.note_value = 121
note_column.instrument_value = 255
end
if set_volume and not volume_set then
note_column.volume_value = 255
end
if show_column then
step.show_note_column(col_index)
end
if gate_set then
renoise.song().selected_track.panning_column_visible = true
end
end
col_index = col_index+1
end
– process effect columns
col_index = 1
for i,fx_column in ipairs(line.effect_columns) do
if set_offset and col_index == 1 then
local continue = true
if not step.global then
local val = curr_line%step.sequence.length
if(val==0) then – fix
val = step.sequence.length
end
if(mask_col and mask_col~=val) then
continue = false
end
end
if(continue) then
local val = step.to_discrete_steps(step.sequence.offset[offset])
– fix : 256 becoming 0 when wrapped
if val==256 then
val=255
end
val = wrap_value(val+step.offset,0,255)
if val ~= 0 then
fx_column.number_value = 9
fx_column.amount_value = val
else
fx_column.number_value = 0
fx_column.amount_value = 0
end
end
end
col_index = col_index+1
end
end
writeahead_length = writeahead_length-1
end
curr_line = curr_line+1
end
end

– learn_sequence() : import a sequence from actual pattern data,
– tries to establish the actual values (including adjustments)
– but some loss of precision might occur (for example, a low master volume
– might reduce the precision of the resulting volume levels)
– Note also that global mode will make this method “greedy” (analyze all),
– while the non-global mode will only attempt to the current parameter

step.learn_sequence = function()
local playback_line = song().transport.edit_pos.line
local curr_line = 1
local val = nil
local offset = nil
local col_index = nil
local get_note,get_gate,get_offset,get_volume
local readahead_length = step.sequence.length-1
local iter = renoise.song().pattern_iterator:lines_in_pattern_track(
song().selected_pattern_index,
song().selected_track_index);
if step.global then
get_note = true
get_gate = true
get_offset = true
get_volume = true
step.sequence.clear()
else
get_note = (step.mode == step.modes.NOTE)
get_gate = (step.mode == step.modes.GATE)
get_offset = (step.mode == step.modes.OFFSET)
get_volume = (step.mode == step.modes.VOLUME)
end
for pos,line in iter do
if(curr_line>=playback_line) then
if(readahead_length<0) then
break
else
offset = curr_line%step.sequence.length
if(offset==0) then – fix
offset = step.sequence.length
end
– process note columns
col_index = 1
for i,note_column in ipairs(line.note_columns) do
if(offset==col_index ) then
– do stuff on the trigger point
if get_note then
if step.basenote > 0 then
val = note_column.note_value-step.basenote
else
val = note_column.note_value+math.abs(step.basenote)
end
step.sequence.set_note(offset,val)
end
if get_volume then
val = note_column.volume_value
– empty (255) is the same as full volume (127&128)
if val==255 or val==128 then
val = 127
else
val = val*step.volume
end
step.sequence.volume[offset] = note_column.volume_value
end
if get_gate then
val = note_column.panning_value
if val ~= 255 and val > 239 then
step.sequence.gate[offset] = val-240
end
end
else
– non-trigger point position
if get_gate then
val = note_column.panning_value
– proceed only if not special ‘blank’ value
if val ~= 255 and val > 239 then
– consider the master-duration
val=val-step.gate
if curr_line>=col_index then
– after note
step.sequence.set_gate(col_index,step.notecut_to_ticks(curr_line-col_index,val))
else
– before note
local lines = step.sequence.length-(col_index-curr_line)
step.sequence.set_gate(col_index,step.notecut_to_ticks(lines,val))
end
end
end
end
col_index = col_index+1
end
– process effect columns
col_index = 1
for i,fx_column in ipairs(line.effect_columns) do
if get_offset and col_index == 1 then
– note that loss of precision might occur if the offset is stepped
local val = math.floor((fx_column.amount_value/2)-(step.offset/2))
step.sequence.offset[offset] = wrap_value(val,0,255)
end
col_index = col_index+1
end
end
readahead_length = readahead_length-1
end
curr_line = curr_line+1
end
end

add_action(“Step sequencer:Length [Set]”,
function(message)
if message:is_abs_value() then
if(message.int_value == clamp_value(message.int_value,1,12))then
step.sequence.set_length(message.int_value)
step.show_status("Step sequencer: sequence length set to "…val)
end
end
end)

add_action(“Step sequencer:Param (1=Note, 2=Gate, 3=Offset, 4=Vol)”,
function(message)
if message:is_abs_value() then
if(message.int_value == clamp_value(message.int_value,1,4))then
step.mode = message.int_value
if step.mode == 1 then
step.show_status(“Step sequencer: active parameter = pitch”)
elseif step.mode == 2 then
step.show_status(“Step sequencer: active parameter = duration”)
elseif step.mode == 3 then
step.show_status(“Step sequencer: active parameter = sample-offset”)
elseif step.mode == 4 then
step.show_status(“Step sequencer: active parameter = volume”)
end
end
end
end)

add_action(“Step sequencer:Adjust value [Set]”,
function(message)
if message:is_abs_value() then

local idx = step.get_selected_column()
if(step.mode==step.modes.NOTE)then
– adjust +/- 12 semitones
local val = math.floor((message.int_value-64)/5.333)+1
if(step.global)then
step.basenote = val
step.show_status("Step sequencer: master-pitch changed to “…val)
else
if step.sequence.note[idx] == -1 then
return
end
step.sequence.note[idx] = clamp_value(step.sequence._note[idx]+val,0,121)
step.show_status(“Step sequencer: pitch #”…idx…” was set to "…step.sequence.note[idx])
end
end
if step.mode == step.modes.GATE then
local val = message.int_value
if step.global then
step.gate = val
step.show_status(“Step sequencer: master-duration changed to “…val)
else
step.sequence.gate[idx] = clamp_value(step.sequence._gate[idx]+val,0,255)
step.show_status(“Step sequencer: duration #”…idx…” was set to “…step.sequence.gate[idx]…” ticks”)
end
end
if step.mode == step.modes.OFFSET then
local val = message.int_value
if step.global then
step.offset = step.to_discrete_steps(val)
step.show_status("Step sequencer: master sample-offset changed to “…val)
else
step.sequence.offset[idx] = wrap_value(step.sequence._offset[idx]+val,0,255)
step.show_status(“Step sequencer: sample-offset #”…idx…” was set to "…step.sequence.offset[idx])
end
end
if step.mode == step.modes.VOLUME then
– adjust : assume absolute 7 bit value
local val = message.int_value/127
if step.global then
step.volume = val
step.show_status("Step sequencer: master-volume changed to “…val)
else
step.sequence.volume[idx] = clamp_value(math.floor(step.sequence._volume[idx]*val),0,127)
step.show_status(“Step sequencer: volume #”…idx…” was set to "…step.sequence.volume[idx])
end
end
step.output_sequence(idx,step.mode)
end
end)

add_action(“Step sequencer:Output [Trigger]”,
function(message)
if message:is_trigger() then
step.output_sequence(step.get_selected_column(song().selected_note_column_index))
step.show_status(“Step sequencer: Output()”)
end
end)

add_action(“Step sequencer:Learn [Trigger]”,
function(message)
if message:is_trigger() then
step.learn_sequence()
step.show_status(“Step sequencer: Learn()”)
end
end)

add_action(“Step sequencer:Global mode [Set]”,
function(message)
step.global = boolean_message_value(message)
if(step.global) then
step.show_status(“Step sequencer: Global mode enabled”)
else
step.show_status(“Step sequencer: Global mode disabled”)
end
if step.global then
– apply the manually adjusted values
if(step.mode==step.modes.NOTE)then
for idx = 1,step.sequence.length do
step.sequence._note[idx] = step.sequence.note[idx]
end
end
if(step.mode==step.modes.GATE)then
for idx = 1,step.sequence.length do
step.sequence._gate[idx] = step.sequence.gate[idx]
end
end
if(step.mode==step.modes.OFFSET)then
for idx = 1,step.sequence.length do
step.sequence._offset[idx] = step.sequence.offset[idx]
end
end
if(step.mode==step.modes.VOLUME)then
for idx = 1,step.sequence.length do
step.sequence._volume[idx] = step.sequence.volume[idx]
end
end
end
end)

add_action(“Step sequencer:Extend mode [Set]”,
function(message)
step.extend = boolean_message_value(message)
if(step.extend) then
step.show_status(“Step sequencer: Extended mode enabled”)
else
step.show_status(“Step sequencer: Extended mode disabled”)
end

end)

for seq_index = 1,12 do
add_action(string.format(
“Step sequencer:Step #%02d [Set]”, seq_index),
function(message)
if message:is_abs_value() then
if(step.mode==step.modes.NOTE)then
step.sequence.set_note(seq_index,message.int_value)
step.show_status(“Step sequencer: pitch #”…seq_index…" was set to “…message.int_value)
elseif(step.mode==step.modes.GATE)then
step.sequence.set_gate(seq_index,message.int_value)
step.show_status(“Step sequencer: duration #”…seq_index…” was set to “…message.int_value…” ticks")
elseif(step.mode==step.modes.OFFSET)then
step.sequence.set_offset(seq_index,message.int_value)
step.show_status(“Step sequencer: sample-offset #”…seq_index…" was set to “…message.int_value)
elseif(step.mode==step.modes.VOLUME)then
step.sequence.set_volume(seq_index,message.int_value)
step.show_status(“Step sequencer: volume #”…seq_index…” was set to "…message.int_value)
end
step.output_sequence(seq_index,step.mode)
end
end)
end

– now create the default sequence
step.sequence.set_length(step.sequence.length)
[/luabox]

  1. Restart Renoise, or reload mappings from the MIDI mappings dialog.
  2. Assign the mappings to a MIDI controller using the MIDI mappings dialog

(As an example, this my Nocturn mapped with the sequencer)

Quick tutorial:

Basic requirements for the step sequencer to work, is to have a track containing several note columns (12),
a visible panning column (if you want to be able to see the note duration), and a single effect column for
holding the offset commands.

Whenever a parameter is changed, the script will output notes to the pattern. The length of the output is
determined by two things: the length of the sequence itself, and the “extend mode”, which will cause the
output to occupy the entire track from the editing position.

Oh, and you need to turn on edit-mode before anything is written to the pattern :slight_smile:

This is the basic operation of the step sequencer - you can turn some dials to get sound, and it’s recorded
into the pattern exactly like it’s playing (well, almost). The remaining features like global mode and adjust
are much more ninja in nature, but they are really useful once you’ve gotten used to the basic concept.

Global mode : each time a parameter is changed, the sequence is output. This is useful, if you are recording
into several empty patterns and want to capture the entire output. It also affects how “adjust” works

Adjust : change the current value in a number of ways, depending on the type (note/volume/etc). The
active note-column in Renoise determine which value is targeted (since each column corresponds to a step
in the sequence, this is quite easy). In global mode, the current note-column isn’t important, as all columns
are affected.

Adjustments vary slightly for the different parameter types :

  • Note: transposed +/- 12 semitones - Gate: note duration extended with #ticks - Offset: the value is rotated (for rhytmic purposes) - Volume: each step is multiplied with this value

Learn : attempt to import a sequence, so whatever data is present at the cursor position in the current pattern/track/line. Therefore, “learning” a blank pattern will clear the script memory

Output: will output the sequence (this also happens when changing each step)

Distance: the interval between notes (also when importing)

Extend: extend the output, to fill the entire pattern

Adjust value: a global adjust, like offset on all Steps

awesome

do we need to add the code any specific places in the GlobalMIDIActions.lua file,or doesent that matter?

Not really - just add it at the end of the file, and the mappings should appear in the MIDI mappings dialog as “Step sequencer”.

But I have a suggestion for how the Nocturn could be mapped (you happen to own a Nocturn, right?). You might want to make a small modification to the automap layout to make it easy to switch between the different modes (“note”,“gate”,“offset”,“volume”), with only the selected mode lighting up when you press it (like ‘radio buttons’ in a web browser)

First of all, the nocturn should be set to “user mode”, which is simply the normal MIDI mode for the device.
Then, assign each of the four buttons you want to use for switching to the same CC number, but with a different value.

So, button one should be “normal”, “CC#16” (or whatever CC number that doesn’t conflict with other values), with a value of “1”
Button two should also be “normal”, same CC as before, but with a value of “2”. Repeat this for button three and four.

And when you got it working (you can switch between the buttons), remember to save the automap layout, or you will have to remake it the next time!

awesome,yes i got both the nocturn and the launchpad,will check it out,thanks for the tip :yeah:

i finally got some time to mess with this,but im having some problems getting it to work,first of i managed to add the code above to the GlobalMidiActions.lua but what is it excactly you mean by this part

“with a value of “1”
Button two should also be “normal”, same CC as before, but with a value of “2”. Repeat this for button three and four.”

the value part.??

i think i got it to work,kind of anyway,i have to mess with this some more,this was really fun actually
thanks for sharing danoise

EDIT:but the value part i still dont understand

where can i find GlobalMIDIActions.lua on mac os X?

just found it :)

just experimented alittle,i had some quite fun(but not really musical usebla)moments,when i tried NOT to set the controls to the same CC value

this is indeed fun stuff to play around with :yeah:

OK, first stab at a Launchpad step sequencer attached … For this round I found it easier to write something from scratch rather than figure out Duplex. I’ll try to Duplexify it at some point.

This is completely not finished and not tested very thoroughly, but just wanted to throw something out there in case people can take the code and run with it, or have some brilliant suggestions.

There is no UI other than a message when it starts telling you that it found a Launchpad (or that it didn’t), so here are some tips:

The main grid of the Launchpad is the first 8 rows on the first 8 tracks (just one column per track). Press some of the buttons to write notes to your pattern using the current instrument.

The up/down buttons at the top will move down to the next 8 rows, and the left/right should move over to the next 8 tracks (but I haven’t tested that out yet)

The “session” and “user 1” buttons are like transpose down and up respectively. Hold some notes down and press them to transpose those notes. Or press them without holding any notes down to move the base note up & down.

There are no volume or panning controls yet… I’m thinking about using the “trigger” buttons on the right for volume (bottom button would make a note off, next one up maybe volume 16, 32, etc.

But I was also thinking it might be fun to make those buttons jump the playhead to the corresponding line … so you could do some crazy beat juggling or offbeat looping.

Anyway, enjoy?

PS: Cool sequencer, danoise - I’ve definitely never seen anything like it!

this one is cool daxton

just one question though,it seems that when i “inputs” samples using the grid it inputs 2 notes every time i press the grid
instead of only 1??

EDIT:firefox crashes during writing this so it turned up as a double post

this one is cool daxton

just one question though,it seems that when i “inputs” samples using the grid it inputs 2 notes every time i press the grid
instead of only 1??

also i got this error message it happened after i pressed the "pads"on the launchpad right after each thother

for some reason i cant attach a image of the message,will post here later when i can

Hrm, where does the second note end up? On the next line? The next track? The next column? Or some random place?
Oh! Do you have the Launchpad set up as a MIDI Input device in your Renoise preferences? You should turn that off if you do, so the step sequencer can use it exclusively.

Let me know if you can get that error message. Thanks for the feedback!

New version attached with a minimal GUI and volume selection using the right side trigger buttons.

EDIT: I see you got the error message - do me a favor, try this new version. That looks like an array out-of-bounds error to me, but my line numbers have all changed since I put that last version up so I can’t track it down easily. Version control? What’s that?

ahh yes i had launchpad in the midi