Just a new Launchpad Pro Mk3 user with some JACK2 feedback

For starters, it’s awesome how much works out of the box…but I saw some older threads mentioning to use ALSA instead of JACK2…but with the latest 6.8 Linux kernel the simple ALSA method doesn’t seem to be working for me. However, I was readily able to get everything going in JACK2 with the addition of the a2jmidi package in Ubuntu. This made JACK2 pick up the ALSA connection names so Ardour could automatically make the connections it needed.

Here are the relevant Ubuntu software versions in use here:

  • Kernel: 6.8.0-54-generic
  • ALSA (alsa-base): 1.0.25+dfsg-0ubuntu7
  • Pipewire (pipewire-alsa): 1.0.5-1ubuntu2
  • JACK2: 1.9.21~dfsg-3ubuntu3
  • a2jmidid: 9-3ubuntu3
  • Ardour (8.4): 1:8.4.0+ds1-2ubuntu8

The device startup process was somewhat important too…

  • Start JACK
  • Connect Launchpad

If you connect your Launchpad before starting JACK, the port connection names are just “system #”, but with a2jmidi after JACK has started, then the port names appear correctly because they are duplicated/mapped with the ALSA device names. This part is likely the biggest “gotcha” of this particular setup.

So far so good…this immediately made session mode function (mostly, more on this shortly) and I was very easily able to assign note/chord/sequencer MIDI channels. Basically, all of the basic functionality seems to be here, and that’s awesome.

There are, however, a few things broken. I’ll try to outline them here as best I can, and I’ll add to the thread as I discover more issues. (I’m trying to also put these in order of importance too.)

  • Track volume faders don’t appear and don’t seem to function either. (Both MIDI and Audio tracks.)
  • Track panning controls also don’t function in seemingly the same way. (Both MIDI and Audio tracks.)
  • Moving tracks left/right seems to work okay, but up/down doesn’t seem to function as I’d expect in that tracks and busses not visible in the Cue view still seem to occupy space in my session launch buttons. With the master bus at the top of my session, all of my launch buttons become off by one, but if I move the master bus to the end of the project and restart, the session buttons align again.
  • Selecting a drum track does not change to the drum pad input in note mode. I suspect this will possibly need a specialized plugin that can go in front of the MIDI instrument plugin or something similar given the raw flexibility here… (Maybe the volume/panning/sends are similar?.. Maybe plugins are needed to hook-in correctly?..)
  • Tempo tapping (shift+Send) doesn’t adjust the project tempo like I would expect…but maybe it isn’t supposed to?.. This feature should probably be made to match the current Novation documentation.
  • With a formal send on an Audio track, the device Sends should theoretically show volume faders for each of them, but similarly to the other volume/panning problems, these just don’t seem to appear at all either.

Most of my experience with Ardour comes from versions 2-5, so a lot of this MIDI functionality is completely new to me…but the in-DAW features (like quantize) don’t seem to do anything from the controller even though they seem to work fine in the DAW. Before I start listing these things though, I plan to do more testing first to make sure it’s not just a me thing.

Similarly, if there’s anything the dev team or community would like tested, I’m all for assisting here. I’m a programmer professionally too, so while I typically just use a prepackaged Ardour version, I am more than happy pulling dev builds and/or code to compile debug versions…I just felt it more important at this moment to outline the out-of-the-box issues.

I’d love to pay attention to this, but (a) am swamped in other development goals right now and (b) Novation have still not addressed the issues with using their new LaunchKey devices on Linux kernel 6.x, which makes me reluctant to put more time into their hardware (for now).

Also, the ALSA audio/MIDI backend really is a better choice :slight_smile:

I think the issue with ALSA on my machine comes from the overbearing nature of PulseAudio/Pipewire, sadly. That audio engine is largely garbage for anything beyond basic playback, but at least with Pipewire it does make room for JACK2 when it starts. I do have to pipe all output to “playback 3/4” instead of “playback 1/2”, but otherwise it works flawlessly. I’m also routing channels to/from other JACK software and TuxGuitar…and these features don’t seem to work at all without JACK.

I did update my Launchpad to the latest, but also wasn’t aware of the Linux 6.x issues… Perhaps I’ll bark up their support tree and see what they have to say about a possible timeline…

No, the ALSA backend doesn’t use PulseAudio/Pipewire (in fact, it politely asks it to get out of the way, and unless totally borked by your distro (unlikely) it does).

Yes, you cannot do inter-application routing with the ALSA backend.

AFAIK, the LaunchPads are fine with kernel 6.x, but the newer LaunchKey devices fail to be recognized as USB devices at all. They’ve known about this for at least 6 months, maybe more.

Ah, gotcha. I’ve got the Launchpad for sure.

I’ll put some more effort into the ALSA side of things too, just to see how functionality differs.

Okay…ALSA backend does provide the volume/panning/sends. Very clear functionality differences here. Looks like most of those issues are indeed related to JACK vs. ALSA. I’ll poke around more in each with this in mind.

Another follow-up while I’m thinking about it too… My top-right corner keeps flashing at me, and it isn’t always the same color (sometimes blue, sometimes green, sometimes purple, sometimes orange, etc.). I thought these might be some indicator going wild (like a solo notification or something?) but I can’t seem to find any actual underlying cause or reason. Does this actually mean anything? Is it perhaps just the internal or session sequencer BPM or something? It’s another something that doesn’t seem to be readily found in the official documentation either.

No, there is no difference in backend functionality for volume/panning/sends at all.

The rop right corner is flashing in time with the tempo. It’s a bit silly :slight_smile:

Right on. Thanks for the prompt feedback!

I can’t remember if I automatically set up the connection to Ardour’s MIDI Clock out port; that enables it to flash not just at the same tempo, but on the beat.

In the case of the Launchpad Pro, it appears to be the internal sequencer. I suspect Ardour’s rules would apply on the Launchpad X though. This is probably ideal in this case anyway though.

If the connection is made (it might be that I left it as a manual task for now), it will sync to Ardour.

I have all 3 models of the Launchpad.

I think it would be great to work on the JACK2 issues eventually, but realistically even though those are issues, they aren’t super important really, and they may also be easy fixes.

I was having more issues with mapping drums (mostly GUI millions-of-clicks issues, and also mostly at Novation…), so I think it’s time to dive into some formal MIDI mapping via xml. I saw the quick vid on YouTube and am now thinking about a few possible maps for some of the Novation default custom maps…and this may ultimately satisfy my current woes (for now, anyway).

When doing that, I also took a quick peek at the source on GitHub and I’ll also see about getting a custom build going, and maybe eventually a pull request for some related quality of life updates (like JACK-specific fixes and better finger drumming variant or two over the Novation customs). No promises, but I would indeed personally like these fixes locally at least, so I’ll see what I can do for contribution too.

Thanks again for the quick responses and the awesome DAW (to everyone that participates!). It has treated my musical projects well over the years, and I love the continued support. Very much appreciated! :smile:

My solution is to configure the launchpad in the browser to always use the finger-drumming preset in the Custom mode. Takes about a minute.

Is there a fast way to do this? I wasn’t able to find any noteworthy editing features like cutting and pasting buttons…it was getting tedious to drag and setup each individual MIDI note. This does work, it just so, so much clicking…drag the note, set the note, set the octave, set the channel, repeat x64… It is certainly nice to get the color coding though. Once the initial setup is done, the regular re-upload to the device is not that big of a deal…but it’s this initial setup specifically that has turned me off from that particular method for now.

I’m currently looking at the possibility of a Lua plugin (based off of the other MIDI map plugins) to make it relatively simple to quickly remap the “Default Custom 3” profile freely inside of Ardour. This seems like a fairly reasonable way to achieve this too, with far less tedious GUI in the way. It also seems like it shouldn’t take long to produce either. I’m thinking this should make it easier to copy that layout and just change colors instead of doing the note/octave/channel adjustments there too.

I am, however, brand new to all of this, so it may very well be that I’m just missing something…if this is the case, I am all ears on improving mapping “workflow”.

Another quick update.

Well, it seemed easy enough… I’m a C/C++ dev, and I’ve used Lua in the past, but I had to relearn a lot of Lua quirks and dive through a bit of the source too…but I was able to put together a plugin to give me this default mapping here (the interface at least):

This portion seems just fine. Only valid drum “notes” are presented in any given drop-down, and on the way out of the dsp_params() function, everything looks good… However, when I get into dsp_run() and try to iterate a simple for loop (1,64), I don’t seem to actually have that many values inside of the CtrlPorts:array()…and the simple loop doesn’t always complete either.

I’m currently thinking this many parameters on a single plugin is probably just bogging it down enough that it can’t truly complete before dsp_run() is killed/started over for another round of processing. But, even when it does translate values, it seems to not really map them correctly either (confirmed with an ACE MIDI Monitor immediately before and after this new plugin)…but I think I have to first ensure that I’m actually receiving good data, and that I can also faithfully work with that data, before I can truly finish debugging this particular mis-mapping bug.

I’m thinking tomorrow I’ll give a shot towards reducing this down into 4x4 quadrants instead, each plugin instance with a new “quadrant” setting (1-4, or something simple) or “starting note” setting (probably this, for greater compatibility with other 4x4 drum pads), just so I can shift table translation ranges to match the physical location. I seems like I can then probably default to allowing unaffected notes to pass through, and then stack 4x of these plugins (each assigned to their own quadrant), that I still may be able to get something usable here.

I may still be going about this the wrong way entirely, but it seems like it’s really close. If anyone has feedback in general that may be useful, I’d love to hear it too.

Actually, went ahead and just powered through to make those basic changes…the reduction from 64 to 16 has indeed stabilized my parameter data…but I’ll leave the rest of the debugging until tomorrow.

It has been simplified down to this now:

1 Like

I’d like to try that. Can you share it?

I guess we can just yank this comment if this is “too big” or something… Here goes…

(See the latest post in this thread for a working version.)

The translation portion is still not quite right, but it’s also not far off either…now I think I’m back in undocumented territory with the need to learn more in order to make sure I’m not making any further bad assumptions (I most certainly am still, I’m pretty sure).

I simply called this launchpad_drum_midimap.lua and placed it in /usr/share/ardour8/scripts…but after rereading the Ardour’s Lua documentation (in the “Managing Scripts” section), I see now there are formal userspace locations for plugins like these to be added without stomping on the system install. This is probably the best way to add to your own Ardour.

Ah, the power of fresh eyes…

I was sorting the parameters after loading them into the table (forgot I had that there still), and that was absolutely mangling the rest of the processing down the chain. My note translations look good now, and I’ve updated it to allow non-translated notes to just pass-through as well. Moving the starting note works beautifully as well.

I’m going to clean this up one more time so the parameters present more cleanly to the user again and I’ll post a new version then. I should probably have a relatively finished product shortly.

Okay, it’s now fully functional and ready for prime time. It’s stackable so all 64 pads can be used simultaneously as well (instructions on this are now baked into the plugin description).

Here’s the latest screenshot:

And here’s the latest code that goes with it:

ardour {
    ["type"]    = "dsp",
    name        = "Launchpad (4x4) Drum Map",
    category    = "Utility",
    license     = "MIT",
    author      = "Ardour Community",
    description = [[    A Novation Launchpad X/Pro Mk3 to Standard GDM MIDI Drum Mapper.  This plugin expects to operate on top of any 4x4 drum pad layout of similar implementation.  Each plugin instance represents a 4x4 drum map with 1x1 as the top left of the grid and 4x4 as the bottom right (the numbers in parenthesis are the respective MIDI note values).  Generally, these grids start at some MIDI note value like (C3, 48) and increment up 15 more notes from left-to-right, bottom-to-top...making the bottom-left pad the proper MIDI note value to use for the "Starting Note" parameter.

    For the Launchpad rhythm pads in note/sequencer mode, the default starting note is C2 (36) and as such, this is the plugin default.

    However, this plugin can be stacked multiple times (for example, with the Launchpad put into 'Factory Custom Mode 3')...  --BUT-- take care as one instance can remap to a note in range that may unintentionally be remapped again further down the chain!  Generally, stacking them in order from lowest to highest starting note should allow this to work okay.  For this custom mode specifically, this means: first bottom-left (with starting note 48), then top-left (64), then bottom-right (80) and finally top-right (96).]]
}


-- Global Constants
MIDI_MAX			= 127
MIDI_INVALID			= -1
OFF_NOTE			= MIDI_INVALID

PAD_COUNT			= 16		-- 4x4 Grid
NON_PAD_PARAM_COUNT		= 2		-- Should be equal to the number of named parameters defined immediately below


-- Named Parameter Indexes
PASSTHROUGH			= 1
STARTING_NOTE			= 2


function dsp_ioconfig ()
    return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, }
end


function dsp_params ()
	-- Constants
	local DEFAULT_STARTING_NOTE = 36	-- C2

	-- Default Launchpad Grid/Note Map
	--
	-- Note range (default) is C3 (bottom left) to D#4 (top right), left half first, bottom
	--   to top, then right side bottom to top.
	--
	-- The '#x#' here represents physical grid locations: 1x1 is the top left,
	--   1x4 is the top right, 4x4 is the bottom right, etc.
	--
	-- With that in mind, this is the Launchpad order, from lowest note to highest.
	local LAUNCHPAD_MAP = {
		[1]  = "4x1",
		[2]  = "4x2",
		[3]  = "4x3",
		[4]  = "4x4",
		[5]  = "3x1",
		[6]  = "3x2",
		[7]  = "3x3",
		[8]  = "3x4",
		[9]  = "2x1",
		[10] = "2x2",
		[11] = "2x3",
		[12] = "2x4",
		[13] = "1x1",
		[14] = "1x2",
		[15] = "1x3",
		[16] = "1x4"
	}

	-- Default GDM1 MIDI Drum Map
	-- Constants
	local GDM_DRUM_START	= 35
	local GDM_DRUM_END	= 81

	local GDM_DRUM_MAP = {
		[35]	= "Acoustic Bass Drum",
		[36]	= "Bass Drum 1",
		[37]	= "Side Stick",
		[38]	= "Acoustic Snare",
		[39]	= "Hand Clap",
		[40]	= "Electric Snare",
		[41]	= "Low Floor Tom",
		[42]	= "Closed Hi-Hat",
		[43]	= "High Floor Tom",
		[44]	= "Pedal Hi-Hat",
		[45]	= "Low Tom",
		[46]	= "Open Hi-Hat",
		[47]	= "Low-Mid Tom",
		[48]	= "High-Mid Tom",
		[49]	= "Crash Cymbal 1",
		[50]	= "High Tom",
		[51]	= "Ride Cymbal 1",
		[52]	= "Chinese Cymbal",
		[53]	= "Ride Bell",
		[54]	= "Tambourine",
		[55]	= "Splash Cymbal",
		[56]	= "Cowbell",
		[57]	= "Crash Cymbal 2",
		[58]	= "Vibraslap",
		[59]	= "Ride Cymbal 2",
		[60]	= "High Bongo",
		[61]	= "Low Bongo",
		[62]	= "Mute High Conga",
		[63]	= "Open High Conga",
		[64]	= "Low Conga",
		[65]	= "High Timbale",
		[66]	= "Low Timbale",
		[67]	= "High Agogo",
		[68]	= "Low Agogo",
		[69]	= "Cabasa",
		[70]	= "Maracas",
		[71]	= "Short Whistle",
		[72]	= "Long Whistle",
		[73]	= "Short Guiro",
		[74]	= "Long Guiro",
		[75]	= "Claves",
		[76]	= "High Wood Block",
		[77]	= "Low Wood Block",
		[78]	= "Mute Cuica",
		[79]	= "Open Cuica",
		[80]	= "Mute Triangle",
		[81]	= "Open Triangle"
	}

	-- Default "Finger Drumming" Remap
	--
	-- Launchpad 'Default Custom 3' Notes -> Standard GDM Drums
	--
	-- A big thanks to Robert Mathijs and 'The Quest For Groove' finger-drumming
	--   guidelines for providing a robust starting point and further inspiration
	--   to create this plugin.
	local DEFAULT_REMAP = {
		[13] = 41,		--1x1, Low Floor Tom
		[14] = 47,		--1x2, Low-Mid Tom
		[15] = 48,		--1x3, High-Mid Tom
		[16] = 49,		--1x4, Crash Cymbal 1
		[9]  = 42,		--2x1, Closed Hi-Hat
		[10] = 46,		--2x2, Open Hi-Hat
		[11] = 42,		--2x3, Closed Hi-Hat
		[12] = 51,		--2x4, Ride Cymbal 1
		[5]  = 37,		--3x1, Side Stick
		[6]  = 38,		--3x2, Acoustic Snare
		[7]  = 38,		--3x3, Acoustic Snare
		[8]  = 37,		--3x4, Side Stick
		[1]  = 57,		--4x1, Crash Cymbal 2
		[2]  = 35,		--4x2, Acoustic Bass Drum
		[3]  = 35,		--4x3, Acoustic Bass Drum
		[4]  = 53,		--4x4, Ride Bell
	}

	local SCALEPOINTS_MAP = {}
	for note=0,(MIDI_MAX - PAD_COUNT) do
		local name = ARDOUR.ParameterDescriptor.midi_note_name(note)
		SCALEPOINTS_MAP[string.format("(%03d) %s", note, name)] = note
	end

	local DRUM_SCALEPOINTS_MAP = {}
	DRUM_SCALEPOINTS_MAP["None"] = OFF_NOTE

	for note=GDM_DRUM_START,GDM_DRUM_END do
		local name = string.format("(%03d) %s", note, GDM_DRUM_MAP[note])
		DRUM_SCALEPOINTS_MAP[name] = note
	end

	-- Pad parameters to be sorted first, then added to map_params
	local pad_params = {}

	local i = 1
	for pad, name in pairs(LAUNCHPAD_MAP) do
		pad_params[i] = {
			["type"] = "input",
			name = string.format("Pad %s", name),
			min = MIDI_INVALID,
			max = MIDI_MAX,
			default = DEFAULT_REMAP[pad],
			integer = true,
			enum = true,
			scalepoints = DRUM_SCALEPOINTS_MAP,
			doc = "Set this pad's drum output."
		}
		i = i + 1
	end

	table.sort(pad_params, function (a, b)
		return string.upper(a.name) < string.upper(b.name)
	end)

	-- The actual, returned parameter data
	local map_params = {}

	-- Named Parameter Definitions
	map_params[PASSTHROUGH] = {
		["type"] = "input",
		name = "'None' pass-through",
		min = 0,
		max = 1,
		default = 0,
		toggled = true,
		doc = "Allow notes mapped to 'None' to pass through instead of being supressed."
	}

	map_params[STARTING_NOTE] = {
		["type"] = "input",
		name = "Starting Note",
		min = MIDI_INVALID,
		max = MIDI_MAX - PAD_COUNT,
		default = DEFAULT_STARTING_NOTE,
		integer = true,
		enum = true,
		scalepoints = SCALEPOINTS_MAP,
		doc = "Sets the starting note of a 4x4 drum pad.  This should be set to the lowest note, usually triggered by the bottom-left pad."
	}

	-- Pad Parameter Definitions
	i = NON_PAD_PARAM_COUNT + 1
	for _, cur_pad in pairs(pad_params) do
		map_params[i] = cur_pad
		i = i + 1
	end

	return map_params
end


function dsp_run (_, _, n_samples)
	assert (type(midiin) == "table")
	assert (type(midiout) == "table")
	local cnt = 1;

	function tx_midi (time, data)
		midiout[cnt] = {}
		midiout[cnt]["time"] = time;
		midiout[cnt]["data"] = data;
		cnt = cnt + 1;
	end

	-- build translation table
	local translation_table = {}
	local ctrl = CtrlPorts:array()

	local passthrough	= ctrl[PASSTHROUGH]
	local starting_note	= ctrl[STARTING_NOTE]

	-- Due to the nature of the pad layout and the decidedly finite contents it's just
	--   a little more straightforward and cleaner to do this manually.
	translation_table[starting_note + 0]	= ctrl[NON_PAD_PARAM_COUNT + 13]	-- 4x1
	translation_table[starting_note + 1]	= ctrl[NON_PAD_PARAM_COUNT + 14]	-- 4x2
	translation_table[starting_note + 2]	= ctrl[NON_PAD_PARAM_COUNT + 15]	-- 4x3
	translation_table[starting_note + 3]	= ctrl[NON_PAD_PARAM_COUNT + 16]	-- 4x4
	translation_table[starting_note + 4]	= ctrl[NON_PAD_PARAM_COUNT + 9]		-- 3x1
	translation_table[starting_note + 5]	= ctrl[NON_PAD_PARAM_COUNT + 10]	-- 3x2
	translation_table[starting_note + 6]	= ctrl[NON_PAD_PARAM_COUNT + 11]	-- 3x3
	translation_table[starting_note + 7]	= ctrl[NON_PAD_PARAM_COUNT + 12]	-- 3x4
	translation_table[starting_note + 8]	= ctrl[NON_PAD_PARAM_COUNT + 5]		-- 2x1
	translation_table[starting_note + 9]	= ctrl[NON_PAD_PARAM_COUNT + 6]		-- 2x1
	translation_table[starting_note + 10]	= ctrl[NON_PAD_PARAM_COUNT + 7]		-- 2x3
	translation_table[starting_note + 11]	= ctrl[NON_PAD_PARAM_COUNT + 8]		-- 2x4
	translation_table[starting_note + 12]	= ctrl[NON_PAD_PARAM_COUNT + 1]		-- 1x1
	translation_table[starting_note + 13]	= ctrl[NON_PAD_PARAM_COUNT + 2]		-- 1x2
	translation_table[starting_note + 14]	= ctrl[NON_PAD_PARAM_COUNT + 3]		-- 1x3
	translation_table[starting_note + 15]	= ctrl[NON_PAD_PARAM_COUNT + 4]		-- 1x4

	-- for each incoming midi event
	for _,b in pairs (midiin) do
		local t = b["time"] -- t = [ 1 .. n_samples ]
		local d = b["data"] -- midi-event data
		local event_type
		if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end

		if (#d == 3) and (event_type == 9 or event_type == 8 or event_type == 10) then
			-- Do the mapping - 2 is note byte for these types
			if passthrough > 0 then
				-- 'None' lets the notes pass though
				local new_note = translation_table[d[2]]

				if new_note ~= nil and new_note ~= OFF_NOTE then
					d[2] = new_note
				end

				tx_midi (t, d)
			else
				-- 'None' effectively turns this note off
				d[2] = translation_table[d[2]] or OFF_NOTE
				if not (d[2] == OFF_NOTE) then
					tx_midi (t, d)
				end
			end
		else
			tx_midi (t, d)
		end
	end
end

Let me know if you run into any issues or generate any feature requests. To get my full 64 pads going I’m simply stacking these as described in the instructions and then saving the individual presets per quadrant to get a fully mappable drum pad.

And to the dev team: Is something like this worthy of a pull request? I can generate one, or feel free to just copy and include if that’s easier for something this small. Just let me know!

Edit: After playing with it a little bit, it may be worth changing the behavior back to muting “None” selections rather than allowing pass-through. The recent changes have made the pass-through decision considerably less important.

Edit 2: The code has been updated to turn the pass-through behavior into a proper setting. It’s now a toggle. Some tool tip help has also been added for a little easier time understanding, even without reading the description. The screenshot has also been updated to reflect the current changes too.

Edit 3: And in what I hope will be the final edit of this cycle, after testing with the sequencer it starts at C2 instead of C3 like the custom mode…so this is the new default note. Instructions remain for the custom 3 layout, but this should now provide a decent drum layout out of the box for most any place you happen to be drumming in the Launchpad modes. Some of the internal documentation has been updated to reflect these changes too, along with a few errors being corrected along the way.

Edit 4: Bringing this back into a real project and attaching it to DrumGizmo works perfectly as well. I believe I am personally satisfied now, but as always, if there are any comments/suggestions/etc. I’m more than happy to flesh it out further.