Compare commits

...

111 Commits
0.1 ... master

Author SHA1 Message Date
nixo 6d5a3b1ef4 add guix build file 2020-07-25 21:44:33 +02:00
nixo e00ab5e45b Fix jog back on djcontrol instinct 2020-07-25 21:30:17 +02:00
Albert Graef b6068a73c0 Minimal Mackie emulation for the Harley Benton MP-100 a.k.a. MeloAudio MIDI Commander. 2019-12-18 10:56:19 +01:00
Albert Graef dc626710e6 Install midizap-mode automatically if Emacs is found during installation. Update the documentation. 2018-11-16 21:18:54 +01:00
Albert Graef 8c622bd085 midizap-mode: Add keysyms to auto-complete keywords table. 2018-11-16 20:37:58 +01:00
Albert Graef e53e3b3140 Add some snippets for Emacs yasnippets. 2018-11-13 08:31:41 +01:00
Albert Graef 8fef722919 Remove unneeded changes to midizap mode syntax table. 2018-11-11 18:59:24 +01:00
Albert Graef 24e346c733 Comment changes. 2018-11-11 10:12:21 +01:00
Albert Graef 240d30da7d Comment changes. 2018-11-11 00:31:31 +01:00
Albert Graef 951fa8fb17 Update the manual. 2018-11-10 23:46:20 +01:00
Albert Graef 7954230468 Add an Emacs mode for midizaprc files. 2018-11-10 23:45:56 +01:00
Albert Graef 357836a383 Fix glitches in regexes. 2018-11-06 07:31:56 +01:00
Albert Graef c2160b9893 Typo in the manual. 2018-10-16 08:33:48 +02:00
Albert Graef 7ce7f3c30b Fix up the detection of our own ports in the connection callback. 2018-10-16 08:29:22 +02:00
Albert Graef 15b8b574b3 Update the manual. 2018-10-15 06:23:23 +02:00
Albert Graef b39484c0ac Make the -r optional, so that just 'midizap rcfile' will work. 2018-10-15 05:56:47 +02:00
Albert Graef 4947aca50f Fix an obscure bug in copying anyshift mod translations which might errorneously override the modulus in an existing translation. 2018-10-15 03:26:31 +02:00
Albert Graef 70287a7e9f X-Touch Mini example: Move the MIDI CC assignments on the ALT layer to a separate config, so that we don't mix up Mackie and MIDI control messages. 2018-10-14 21:05:19 +02:00
Albert Graef 1898fe74cf X-Touch Mini example: Add some more bindings for the encoders on the ALT layer (experimental). 2018-10-14 10:47:01 +02:00
Albert Graef 9d55ce9651 Cosmetic changes to the other examples. 2018-10-14 10:47:01 +02:00
Albert Graef d0477d27d2 Rework the X-Touch Mini and ONE examples. 2018-10-14 10:47:01 +02:00
Albert Graef 56ea96686a Add a built-in patchbay facility (JACK_IN/JACK_OUT directives). 2018-10-14 10:47:01 +02:00
Albert Graef 483c2d3105 Comment changes. 2018-10-12 14:48:49 +02:00
Albert Graef 714e9d1a35 Rearrange some of the shifted functions of the XTouchMini2 bindings. 2018-10-12 14:48:28 +02:00
Albert Graef c9e545ff51 Show fader feedback on encoders (experimental). 2018-10-12 09:13:09 +02:00
Albert Graef 17d0947051 Add another variant of the X-Touch Mini configuration. 2018-10-11 21:37:46 +02:00
Albert Graef cfb56c945f Cosmetic changes. 2018-10-11 21:36:41 +02:00
Albert Graef aee118bb2a Text changes. 2018-10-11 20:03:10 +02:00
Albert Graef 825f8cf54f Add different variants of the midizap patchbay for use with or without a2jmidid. The former requires that a2jmidid is running, the latter will work without, but only when Jack1 is used with seq as MIDI driver, as described in the manual. 2018-10-11 19:50:01 +02:00
Albert Graef cb90678434 Add an example config for the X-Touch Mini. 2018-10-11 19:03:51 +02:00
Albert Graef a90a0c0361 Typo. 2018-10-10 23:54:41 +02:00
Albert Graef b8622829c1 Text and comment changes. 2018-09-25 04:09:05 +02:00
Albert Graef 55746bcd94 Fix some blunders (last encoder assign button should actually be PLUGIN, not INST). 2018-09-24 18:01:06 +02:00
Albert Graef d70be88ad5 X-Touch ONE: Add a FLIP button (shifted SOLO in the transport section). 2018-09-24 13:09:53 +02:00
Albert Graef 28a9d2c515 X-Touch ONE: Add some more bindings for the encoder assignment section. 2018-09-24 01:57:56 +02:00
Albert Graef 9f69dd19a0 Comment changes. 2018-09-24 01:57:56 +02:00
Albert Graef 7397aced4d X-Touch ONE: Add some more shifted bindings. 2018-09-23 23:03:08 +02:00
Albert Graef 21491e32ba Update the documentation. 2018-09-23 13:34:48 +02:00
Albert Graef 0d265f5ebc Bugfix: Suppress the automatic release sequence for MIDI output if there's an explicit release sequence. 2018-09-23 12:27:23 +02:00
Albert Graef df6083ea7a Add some more shifted bindings. 2018-09-23 11:31:10 +02:00
Albert Graef af1dbd10dc Add a README file with short descriptions of the included examples. 2018-09-23 11:31:10 +02:00
Albert Graef 6b99bc35d4 Add an Ardour device description for the X-Touch ONE. 2018-09-23 11:31:10 +02:00
Albert Graef 5c04c47f70 X-TouchONE: Add some convenient bindings for the shifted bank/channel switches. 2018-09-23 11:31:10 +02:00
Albert Graef 0f1cd1c17f Update the documentation. 2018-09-23 11:31:10 +02:00
Albert Graef 7c0403d913 Allow CLASS and TITLE prefixes on a regex, to make it possible to match translation sections only by class or title. 2018-09-23 11:31:10 +02:00
Albert Graef d9c72d6ff1 Cosmetic changes in debugging output. 2018-09-23 11:31:10 +02:00
Albert Graef 49166bd1fa Comment changes. 2018-09-21 09:30:34 +02:00
Albert Graef f865d731aa Some improvements to the Maschine bindings.
- emulate shifted cursor keys and push encoders
- SELECT, SOLO, MUTE are ordinary shift buttons now (no lock)
2018-09-21 08:47:51 +02:00
Albert Graef fb411c505a Comment changes. 2018-09-21 07:55:08 +02:00
Albert Graef f30ab39b65 Text changes. 2018-09-20 19:15:50 +02:00
Albert Graef 5c54198f07 Add a qjackctl patchbay for the included examples. 2018-09-20 19:09:00 +02:00
Albert Graef 14167f763a Text changes. 2018-09-20 14:26:31 +02:00
Albert Graef 4756432b1b Clean up Jack debug mode. Also add a callback to print (dis)connections from/to other Jack MIDI clients in Jack debug mode. 2018-09-20 13:48:59 +02:00
Albert Graef 32a7c51ab2 Add SHIFT+SELECT to the ONE bindings. 2018-09-20 12:45:21 +02:00
Albert Graef 591680e655 Improved nanoKONTROL2 and X-Touch ONE examples. 2018-09-20 10:13:04 +02:00
Albert Graef 66e2bc777b Text changes. 2018-09-20 02:02:48 +02:00
Albert Graef 3404fd39a4 Update the documentation. 2018-09-20 01:25:18 +02:00
Albert Graef 0bccf128d8 Add two simple examples for the new pass-through option. 2018-09-20 01:25:18 +02:00
Albert Graef fe3378be4f Add an option and directive for pass-through of non-system messages. 2018-09-20 01:25:18 +02:00
Albert Graef 8ce561692e Update the documentation. 2018-09-18 22:38:58 +02:00
Albert Graef 5a87c30f66 Make it possible to specify exactly which ports MIDI pass-through should be applied to. 2018-09-18 22:31:12 +02:00
Albert Graef 5e284565df Basic regexes are too limited. Switch to extended regexes in matching translation sections instead. 2018-09-18 11:53:43 +02:00
Albert Graef d33034cc58 Text and comment changes. 2018-09-18 09:24:22 +02:00
Albert Graef 3c1fade294 Add a complete grammar for the configuration language to the manual. 2018-09-18 00:15:52 +02:00
Albert Graef 5cd99ff33c Add AKAI MPKmini2 Mackie emulation example. 2018-09-17 16:14:52 +02:00
Albert Graef 738c59088e Comment changes. 2018-09-17 16:14:52 +02:00
Albert Graef 1c388b471f Text changes. 2018-09-17 16:14:52 +02:00
Albert Graef 60329c4fa4 Comment changes. 2018-09-17 16:14:52 +02:00
Albert Graef 888758c87b Use new synthetic macro events in the Mackie emulation examples. 2018-09-16 09:52:04 +02:00
Albert Graef 36bbdd037f Bugfix: ensure that we don't override an incremental data translation with a default key/mod translation and vice versa. 2018-09-16 09:52:04 +02:00
Albert Graef b54f5ba2f9 Text changes. 2018-09-16 09:52:04 +02:00
Albert Graef 1698aded9a Update the documentation. 2018-09-15 23:53:25 +02:00
Albert Graef a81a0f87da Add synthetic macro events, change detection, and some refactoring bugfixes. 2018-09-15 23:52:56 +02:00
Albert Graef f3efca83a3 Text changes. 2018-09-14 17:33:01 +02:00
Albert Graef 2e53dc693a Update the documentation. 2018-09-14 09:19:37 +02:00
Albert Graef b7701e496b Rework the processing of shift status, part 2. Removed the ? prefix, rules are now anyshift by default. The prefix 0^ now denotes a rule only active in unshifted state. 2018-09-14 06:51:40 +02:00
Albert Graef 4ceef9cbd9 Rework the processing of shift status, bugfixes. Anyshift rules are now considered default rules which may be overridden. 2018-09-14 06:28:13 +02:00
Albert Graef 83c983e3e4 Update the documentation. 2018-09-13 19:38:32 +02:00
Albert Graef f2aedd6267 Add support for direct feedback. 2018-09-13 19:38:32 +02:00
Albert Graef 7c9147b4d4 Cosmetic changes to the help message. 2018-09-13 05:29:37 +02:00
Albert Graef 1459e91cf5 Update the documentation. 2018-09-13 05:25:11 +02:00
Albert Graef fd883c1391 Allow up to four different internal shift keys. 2018-09-12 19:57:03 +02:00
Albert Graef 5baa93cb91 Update the documentation. 2018-09-12 10:52:47 +02:00
Albert Graef 14ba4da728 Update the Maschine Mk3 example. 2018-09-12 10:52:23 +02:00
Albert Graef 3e47533617 Add an automatic feedback option (enabled by default) to keep track of incoming feedback, so that incremental data translations use the right values. Also add a command line option and config file directive to disable it if needed, and a directive for system pass-through which corresponds to the -s command line option. 2018-09-12 10:51:27 +02:00
Albert Graef 58c13233b4 Update of Maschine Mk3 config, to accommodate latest ctlra_daemon changes. 2018-09-12 00:03:44 +02:00
Albert Graef 81da80c3a5 Add an example config for the NI Maschine Mk3, using Ctlra. 2018-09-10 01:25:30 +02:00
Albert Graef 77aaa0de68 Update the manual. 2018-09-08 22:32:27 +02:00
Albert Graef 6c4dbb03cf Add an option for direct pass-through of system messages. 2018-09-08 17:55:12 +02:00
Albert Graef be5094bda7 Update the manual. 2018-09-05 04:58:32 +02:00
Albert Graef 156f2db614 Use mod translations for the faders where possible. 2018-09-05 04:58:32 +02:00
Albert Graef 8f9d1a4ae8 Bugfixes in mod translations. 2018-09-05 04:58:32 +02:00
Albert Graef b56cd35b80 Text changes. 2018-09-05 02:57:25 +02:00
Albert Graef 803dd48b6e Text changes. 2018-09-04 07:53:18 +02:00
Albert Graef 5d8dae58dd Typo. 2018-09-03 23:55:00 +02:00
Albert Graef a11da5c9ec Text changes. 2018-09-03 23:33:37 +02:00
Albert Graef 94436fc266 Update the manual. 2018-09-03 17:04:43 +02:00
Albert Graef 7fbae6adb8 Allow empty output sequences, and add a special NOP token which doesn't produce any output. This makes it possible to have translations which don't produce any output, suppressing any default translations. 2018-09-03 17:04:37 +02:00
Albert Graef fbbfabb41d Text changes. 2018-09-03 01:54:50 +02:00
Albert Graef 7110181ab6 Fix some typos and bad wording. 2018-09-02 13:06:55 +02:00
Albert Graef 3d33784dd0 Text changes. 2018-09-02 11:47:08 +02:00
Albert Graef 689c12defe Refactoring. 2018-09-02 00:44:04 +02:00
Albert Graef 4905e00b97 Update the manual. 2018-09-02 00:44:04 +02:00
Albert Graef 22ff7683d2 Fix up -o option. 2018-09-02 00:12:36 +02:00
Albert Graef 029590bdac Update the manual. 2018-09-01 17:14:12 +02:00
Albert Graef b3b74f7579 Add an empty modulus bracket as syntactic sugar to denote a basic mod translation with zero offset. 2018-09-01 17:14:12 +02:00
Albert Graef 3ae0854f88 Cosmetic changes to meter feedback, also update comments and manual accordingly. 2018-09-01 13:21:18 +02:00
Albert Graef 2bb1113fac Bugfix in MIDI parser. 2018-09-01 07:42:39 +02:00
Albert Graef ab8b31c9aa Text changes. 2018-08-31 13:28:00 +02:00
Albert Graef 84a9109e19 Typo. 2018-08-31 13:18:26 +02:00
Albert Graef 18cf14b888 To be on the safe side, check the result of realpath(). 2018-08-31 12:56:33 +02:00
24 changed files with 4566 additions and 1738 deletions

View File

@ -7,6 +7,12 @@ bindir=$(DESTDIR)$(prefix)/bin
mandir=$(DESTDIR)$(prefix)/share/man/man1
datadir=$(DESTDIR)/etc
# See whether emacs is installed and try to guess its installation prefix.
emacs_prefix = $(patsubst %/bin/emacs,%,$(shell which emacs 2>/dev/null))
ifneq ($(strip $(emacs_prefix)),)
elispdir = $(emacs_prefix)/share/emacs/site-lisp
endif
# Check to see whether we have Jack installed. Needs pkg-config.
JACK := $(shell pkg-config --libs jack 2>/dev/null)
@ -18,7 +24,7 @@ INSTALL_TARGETS = midizap $(wildcard midizap.1)
.PHONY: all world install uninstall man pdf clean realclean
all: midizap
all: midizap midizap-mode.el
# This also creates the manual page (see below).
world: all man
@ -27,6 +33,12 @@ install: all
install -d $(bindir) $(datadir) $(mandir)
install midizap $(bindir)
install -m 0644 example.midizaprc $(datadir)/midizaprc
ifneq ($(elispdir),)
# If emacs was found, or elispdir was specified manually, install
# midizap-mode.el into the elispdir directory.
install -d $(DESTDIR)$(elispdir)
install -m 0644 midizap-mode.el $(DESTDIR)$(elispdir)
endif
# If present, the manual page will be installed along with the program.
ifneq ($(findstring midizap.1, $(INSTALL_TARGETS)),)
install -m 0644 midizap.1 $(mandir)
@ -56,7 +68,7 @@ midizap.pdf: midizap.1
man -Tpdf ./midizap.1 > $@
clean:
rm -f midizap keys.h $(OBJ)
rm -f midizap keys.h keys.el midizap-mode.el $(OBJ)
realclean:
rm -f midizap midizap.1 midizap.pdf keys.h $(OBJ)
@ -64,6 +76,12 @@ realclean:
keys.h: keys.sed /usr/include/X11/keysymdef.h
sed -f keys.sed < /usr/include/X11/keysymdef.h > keys.h
keys.el: keywords.sed /usr/include/X11/keysymdef.h
sed -f keywords.sed < /usr/include/X11/keysymdef.h | tr '\n' ' ' > keys.el
midizap-mode.el: midizap-mode.el.in keys.el
sed '/;; keysyms/r keys.el' < midizap-mode.el.in > midizap-mode.el
readconfig.o: midizap.h keys.h
midizap.o: midizap.h jackdriver.h
jackdriver.o: jackdriver.h

644
README.md

File diff suppressed because it is too large Load Diff

View File

@ -40,17 +40,17 @@
#JACK_PORTS 2
# Other than the input being MIDI instead of the Shuttle's key and wheel
# events, the program works exactly the same. Each section in the file
# (starting with a name in brackets and a regex to be matched against
# the window class and name) specifies the bindings for one application.
# A section at the end without regex provides default bindings if none
# of the other sections are matched. Within each section, bindings are
# introduced with the name of the MIDI message being assigned, followed
# by a sequence of X KeySyms and/or MIDI messages to be output when the
# MIDI message is received.
# events, the program works like Eric Messick's original. Each section
# in the file (starting with a name in brackets and a regex to be
# matched against the window class and name) specifies the bindings for
# one application. A section at the end without regex provides default
# bindings if none of the other sections are matched. Within each
# section, bindings are introduced with the name of the MIDI message
# being assigned, followed by a sequence of X KeySyms and/or MIDI
# messages to be output when the MIDI message is received.
# Here is a brief rundown of the supported notation for MIDI messages
# (please check the documentation for more details).
# (please check the documentation for details).
# CC<0..127>: control change message for the given controller
# PC<0..127>: program change message
@ -61,10 +61,10 @@
# Note messages are specified using the customary notation (note name
# A..G, optionally followed by an accidental, # or b, followed by a MIDI
# octave number. The same notation is also used with aftertouch (KP)
# octave number). The same notation is also used with aftertouch (KP)
# messages, which always apply to a specific note (in contrast, channel
# pressure (CP) always applies to all notes on a single MIDI channel).
# Enharmonic spellings are equivalent, so, e.g., D# and Eb denote
# Enharmonic spellings are equivalent, so, e.g., D#5 and Eb5 denote
# exactly the same MIDI note. All MIDI octaves start at the note C, so
# B0 comes before C1. By default, octave numbers are zero-based, so C0
# is MIDI note 0, C5 denotes middle C, A5 is the chamber pitch, etc.
@ -78,36 +78,20 @@
# The program distinguishes between messages on different MIDI channels.
# By default, messages are assumed to be on MIDI channel 1, but the MIDI
# channel can be specified explicitly following a dash at the end of the
# message token. E.g., a message on MIDI channel 10 would be denoted,
# e.g., CC7-10 or C#3-10.
# message token. E.g., a message on MIDI channel 10 would be denoted
# CC7-10 or C#3-10.
# Each of these messages can be either "on" or "off", and so they can
# have different "press" and "release" keystrokes associated with them.
# E.g., a "note on" message with non-zero velocity emulates a button
# press, while the corresponding "note off" emulates a button release,
# just as if the MIDI keys were just ordinary keys on a computer
# keyboard. The same holds true for control change, channel and key
# pressure messages (here any non-zero value means "on", zero "off"),
# and pitch bends (here the center value of the pitch wheel means "off",
# any other value means "on"). The program change messages play a
# somewhat special role in that they don't actually have any "off"
# messages associated with them, so to keep in line with the other kinds
# of MIDI messages we consider them as being "pressed" and then
# "released" immediately afterwards.
# In addition, all messages except PC (which doesn't have a data value)
# can also have their value changes translated, in which case they have
# associated key bindings which are executed each time the value
# increases or decreases, respectively. Such bindings are indicated
# with the suffixes "+" and "-". Thus, e.g., a key sequence bound to
# CC7+ will be executed each time the value of controller 7 increases,
# and CC7- will be executed each time it decreases. The same applies to
# channel and key pressure as well as pitch bend and even note messages.
# You can also use the "=" suffix to indicate that the same translation
# should be applied to both increases and decreases of the controller or
# pitch bend value. Thus, e.g., CC7= indicates that the same
# translation applies for both CC7+ and CC7-. This is most commonly
# used with pure MIDI -> MIDI translations.
# with the suffixes "+" and "-". You can also use the "=" suffix to
# indicate that the same translation should be applied to both increases
# and decreases of the controller or pitch bend value. Thus, e.g., CC7=
# indicates that the same translation applies for both CC7+ and CC7-.
# This is most commonly used with pure MIDI -> MIDI translations.
# There is also another special mode for these incremental bindings,
# incremental "bit-sign" mode. The suffixes "<", ">" and "~" can be
@ -117,14 +101,6 @@
# increases, and > 64 for decreases, where the first 6 bits of the value
# denote the actual amount of change relative to the current value.
# As already mentioned, translations can also contain other MIDI
# messages, in order to translate MIDI input to MIDI output to be passed
# on to to other MIDI devices and applications. In fact, X KeySyms and
# MIDI messages can be mixed freely in the output. To enable this,
# invoke the program with the '-o' option. This creates a MIDI output
# port, which can then be hooked up to other Jack MIDI applications.
# (Otherwise, MIDI messages in the translations will just be ignored.)
# Debugging options: You want to run the program in a terminal window to
# see its output when using these. The following line, when
# uncommented, prints the section recognized for the window in focus:
@ -155,29 +131,30 @@
# without any option letter turns on all debugging options.
# Sample bindings for video editing. These assume a Mackie MCU-like
# Sample bindings for video editing. These assume a Mackie-compatible
# device, which are available from various manufacturers. They are more
# or less standardized, and offer an abundance of useful controls,
# making it easier to provide bindings which just work. If you don't
# have one of these lying around, there are inexpensive emulations in
# software (such as the TouchDAW app on Android), or you can just edit the
# rules below to make them work with your controller.
# software (such as the TouchDAW app on Android), or you can just edit
# the rules below to make them work with your controller.
# On most MCU-style devices there are some playback controls and cursor
# keys which generate various note events, and a jog wheel which
# On most Mackie-like devices there are some playback controls and
# cursor keys which generate various note events, and a jog wheel which
# generates CC60 messages. We put all of these to good use here. Note
# that the CC60 control requires use of the aforementioned special
# incremental mode for endless rotary encoders.
# Shotcut (WM_CLASS is "shotcut")
# see https://www.shotcut.org/howtos/keyboard-shortcuts/
# Bindings for the Kdenlive and Shotcut video editors (matched by their
# WM_CLASS). These have very similar key bindings, see e.g.:
# https://www.shotcut.org/howtos/keyboard-shortcuts/
[Shotcut] ^shotcut$
[Kdenlive/Shotcut] ^(shotcut|kdenlive)$
# Shotcut uses the customary J-K-L shortcuts, each successive J or L key
# decrements or increments the playback speed. We assign these to the
# MCU Rewind and Forward controls.
# Both Kdenlive and Shotcut use the J-K-L shortcuts, where each
# successive J or L key decrements or increments the playback speed. We
# assign these to the MCU Rewind and Forward controls.
# playback controls
A#7 XK_space # Play/Pause
@ -186,8 +163,8 @@
G#7 "L" # Forward
# punch in/out (sets in and out points)
# Note that your device may not have these, or they may be labeled
# differently, so we provide an alternative binding below.
# Note that these are labeled drop/replace on some devices. We also
# provide an alternative binding below.
D#7 "I" # Set In
E7 "O" # Set Out
@ -199,34 +176,7 @@
D8 XK_Home # Beginning
D#8 XK_End # End
# the jog wheel moves single frames to the left or the right
CC60< XK_Left # Frame reverse
CC60> XK_Right # Frame forward
# Kdenlive (same bindings as above)
[Kdenlive] ^kdenlive$
# playback controls
A#7 XK_space # Play/Pause
A7 "K" # Stop
G7 "J" # Rewind
G#7 "L" # Forward
# punch in/out (sets in and out points)
D#7 "I" # Set In
E7 "O" # Set Out
# alternate binding for set in/out (cursor up/down)
C8 "I" # Set In
C#8 "O" # Set Out
# cursor left/right
D8 XK_Home # Beginning
D#8 XK_End # End
# jog wheel
# the jog wheel moves left/right by single frames
CC60< XK_Left # Frame reverse
CC60> XK_Right # Frame forward
@ -236,13 +186,13 @@
# The special "MIDI" default section is only active when MIDI output is
# enabled (midizap -o). This allows you to use midizap as a MIDI mapper
# translating MIDI input to MIDI output. Here's a simple example for
# illustration purposes, which shows how to map both the MCU master
# illustration purposes, which shows how to map both the Mackie master
# fader and the jog wheel to CC7, so that they can be used as volume
# controls.
# Note that the MCU master fader is PB (on MIDI channel 9), which has
# 128 times the range of a MIDI controller, so we scale it down
# accordingly by specifying a step size of 128.
# Note that the master fader is PB (on MIDI channel 9), which has 128
# times the range of a MIDI controller, so we scale it down accordingly
# by specifying a step size of 128.
PB[128]-9= CC7
CC60~ CC7
@ -284,7 +234,7 @@
[Default]
# First, some MCU-compatible bindings.
# First, some Mackie-compatible bindings.
# cursor movement
D8 XK_Left

View File

@ -1,6 +1,12 @@
# Mackie emulation for the AKAI APCmini
# This turns the APCmini into a Mackie-compatible controller, so that it can
# be used with Linux DAW programs like Ardour. The emulation is complicated
# by the APCmini having no encoders, no motorized faders, and not nearly as
# many dedicated buttons as a Mackie device. But it offers enough controls to
# be usable as a basic DAW controller. Tested with Ardour.
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
@ -11,31 +17,29 @@
JACK_NAME "midizap-APCmini"
JACK_PORTS 2
# This turns the APCmini into a Mackie-compatible controller, so that it can
# be used with Linux DAW programs like Ardour. The emulation is complicated by
# the APCmini having no encoders, no motorized faders, and not nearly as many
# dedicated buttons as a Mackie device. But it offers enough controls to be
# usable as a basic DAW controller. I tested it with Ardour.
# SETUP: The following lines will take care of setting up all the connections
# automatically, but you still need to enable the Mackie control surface in
# Ardour, so that Ardour exposes the Mackie control ports.
# SETUP: In Ardour, enable the Mackie control surface, then connect Ardour's
# "mackie control" ports to midizap's midi_out and midi_in2 ports, and the
# APCmini to midizap's midi_in and midi_out2 ports.
JACK_IN1 APC MINI MIDI 1
JACK_OUT1 ardour:mackie control in
JACK_IN2 ardour:mackie control out
JACK_OUT2 APC MINI MIDI 1
# PROTOCOL DOCUMENTATION: The Mackie protocol is fairly ubiquitous, but since
# the manufacturers can't be bothered to properly *document* their stuff these
# days, we have to rely on volunteers who do their work using some reverse
# engineering. Here are the links that I found most useful:
# http://www.budgetfeatures.com/XctlDOC/Xctl Protocol for X-Touch V1.0.pdf
# (This chart really is a piece of art. It's actually about the Behringer
# X-Touch, but since that device is MCU-compatible, there's a wealth of useful
# information in there; just ignore the bits which pertain to Xctl.)
# There's another fairly comprehensive one here:
# PROTOCOL DOCUMENTATION: The Mackie Control (MC) protocol is fairly
# ubiquitous, but since manufacturers can't be bothered to properly document
# their stuff these days, we have to rely on volunteers who do their work
# using some reverse engineering. Here are the links that I found most
# useful:
# This is fairly comprehensive, but lacks the feedback messages:
# http://www.jjlee.com/qlab/Mackie Control MIDI Map.pdf
# This chart really is a piece of art. It's actually about the Behringer
# X-Touch and its Xctl protocol, but there's useful information about MC in
# there as well:
# http://www.budgetfeatures.com/XctlDOC/Xctl Protocol for X-Touch V1.0.pdf
# Information about the APCmini can be found in the Akai forums here:
# http://community.akaipro.com/akai_professional/topics/midi-information-for-apc-mini
@ -43,18 +47,18 @@ JACK_PORTS 2
# The APCmini's dedicated shift key is used to provide alternative functions
# to some of the buttons and the faders.
?D8 SHIFT RELEASE SHIFT
D8 SHIFT RELEASE SHIFT
# transport (assigned to the topmost 5 "scene launch" buttons on the right)
A#6 A7 # Stop
B6 A#7 # Play
C7 B7 # Rec
#C7 C#7 # Cycle
#C7 D7 # Cycle
C#7 G7 # Rew
D7 G#7 # FFwd
# the next three buttons below are used for the MCU shift keys
# NOTE: The MCU actually has four shift keys, so one has to go. You may want
# the next three buttons below are used for the MC shift keys
# NOTE: The MC actually has four shift keys, so one has to go. You may want
# to rearrange these as needed.
D#7 A#5 # Shift
E7 B5 # Control
@ -74,7 +78,7 @@ F7 C6 # Option
^F7 C#5
# bottom 3x8 grid: mute/solo/rec
# NOTE: Incidentally, these happen to be identical to corresponding MCU input.
# NOTE: Incidentally, these happen to be identical to corresponding MC input.
# rec (bottom row of the grid)
C0 C0
@ -117,29 +121,29 @@ A#5 F#2
B5 G2
# shifted bottom row
# We have these assigned to the bank/channel and track/pan/send/instr controls,
# but you may want to remap some or all of these as needed.
# We have these assigned to the bank/channel and track/pan/send/plugin
# controls, but you may want to remap some or all of these as needed.
^E5 A#3 # Bank Left
^F5 B3 # Bank Right
^F#5 C4 # Channel Left
^G5 C#4 # Channel Right
# NOTE: Only Pan and Send appear to be supported in Ardour.
# NOTE: Plugin appears to be unsupported in Ardour.
^G#5 E3 # Track (Volume)
^A5 F#3 # Pan
^A#5 F3 # Send
^B5 A3 # Instr (Device)
^B5 G3 # Plugin (Device)
# faders (MCU uses pitch bends here, use 129 as step size to get full range)
CC48= PB[129]-1
CC49= PB[129]-2
CC50= PB[129]-3
CC51= PB[129]-4
CC52= PB[129]-5
CC53= PB[129]-6
CC54= PB[129]-7
CC55= PB[129]-8
# faders (MC uses pitch bends here, use 129 as step size to get full range)
CC48[] PB[129]-1
CC49[] PB[129]-2
CC50[] PB[129]-3
CC51[] PB[129]-4
CC52[] PB[129]-5
CC53[] PB[129]-6
CC54[] PB[129]-7
CC55[] PB[129]-8
# master fader
?CC56= PB[129]-9
CC56[] PB[129]-9
# faders become encoders when shifted (CC16..CC23, incremental mode)
^CC48= CC16~
@ -156,91 +160,94 @@ CC55= PB[129]-8
[MIDI2]
# transport (will light up in green)
A7 A#6
A7 A#6 $M0 # reset all meters, see "meter feedback" below
A#7 B6
B7 C7[2] # Rec, blinks when engaged
#C#7 C7 # Cycle
#D7 C7 # Cycle
G7 C#7
G#7 D7
# Feedback for the MCU shift keys. We've disabled this by default since
# it doesn't add much value and clobbers the time display below.
# Feedback for the MC shift keys. We've disabled this by default since it
# doesn't add much value and clobbers some of the blinkenlights below.
#A#5 D#7
#B5 E7
#C6 F7
# Blinkenlights galore! The Mackie protocol provides us with both time
# and meter data for which we provide some translations here which make
# various LEDs light up in different colors.
# Blinkenlights galore! The Mackie protocol provides us with both time and
# meter data for which we provide some translations here which make various
# LEDs light up in different colors.
# Meter feedback: The Mackie protocol packs all 8 channel meters into a
# single channel pressure message which we need to transform into the
# corresponding note messages for the LEDs. Note that we only use the
# upper 5x8 part of the grid here, in order not to clobber the 3 rows at
# the bottom with the rec/solo/mute buttons. This means that the
# meters, which actually have 8 different values, have to be squashed
# into 5 LEDs per channel.
# Meter feedback: Each meter value is sent as a channel pressure message (on
# the first MIDI channel) with the mixer channel index 0..7 in the hi- and the
# meter value in the lo-nibble of the velocity value. On a real MCP device
# like the X-Touch, the meters have 7 segments (4 green, 3 orange, and 1 red),
# but the actual range of the meter values seems to be more like 0..13 (at
# least that's what Ardour outputs, YMMV). Note that we only use the upper
# 5x8 part of the grid here, in order not to clobber the three rows at the
# bottom with the rec/solo/mute buttons, so we have to squash the meter values
# into those 5 LEDs per strip in some way. The following setup with 3 green,
# 1 yellow and 1 red LED seems to work reasonably well, but you might want to
# adjust the colors and the mapping of the meter values to your liking.
?CP[16] C2{0,1} G#2{0:3,1} E3{0:5,5} C4{0:7,5} G#4{0:8,3}
CP[16] C2{0,1} G#2{0:3,1} E3{0:6,1} C4{0:9,5} G#4{0:12,3}
# Here's an alternative decoding, which creates horizontal meters
# covering the full range, with the first channel on top. This is just
# for illustration purposes -- as it ranges across the entire 8x8 grid,
# it will clobber the rec/solo/mute controls.
# NOTE: We only report the values as we receive them here, there's no
# automatic decay of the meters like with real Mackie hardware. Thus we
# explicitly reset all meters when transport stops below. (Ardour at least
# does *not* do that automatically.)
#?CP[16][-8] G#4{0,1} A4{0,0,1} A#4{0:3,1} B4{0:4,1} C5{0:5,5} C#5{0:6,5} D5{0:7,5} D#5{0:8,3}
M0[1] $CP{0} $CP{16} $CP{32} $CP{48} $CP{64} $CP{80} $CP{96} $CP{112}
# This decodes the least significant digit in the time display (CC69) to
# count off time on the 4 bottommost scene launch buttons. Note that
# the digits are encoded in ASCII here, therefore the copious amount of
# zeros in the value lists below to skip over all the non-digit
# characters at the beginning of the ASCII table.
# This decodes the least significant digit in the time display (CC69) to count
# off time on the 4 bottommost scene launch buttons. Note that the digits are
# encoded in ASCII here, therefore the copious amount of zeros in the value
# lists below to skip over all the non-digit characters at the beginning of
# the ASCII table.
?CC69[128] F7{0:49,1,0} E7{0:50,1,0} Eb7{0:51,1,0} D7{0:52,1,0}
CC69[] F7{0:49,1,0} E7{0:50,1,0} Eb7{0:51,1,0} D7{0:52,1,0}
# no feedback for faders (faders aren't motorized)
# NOTE: Feedback for rec/solo/mute/select also needs to be recognized in shift
# mode, so that the (shifted) bank/channel left/right keys work as expected.
# feedback for rec/solo/mute/select
# rec: color = red (vel. 3)
?C0 C0[3]
?C#0 C#0[3]
?D0 D0[3]
?D#0 D#0[3]
?E0 E0[3]
?F0 F0[3]
?F#0 F#0[3]
?G0 G0[3]
C0 C0[3]
C#0 C#0[3]
D0 D0[3]
D#0 D#0[3]
E0 E0[3]
F0 F0[3]
F#0 F#0[3]
G0 G0[3]
# solo: color = green (vel. 1)
?G#0 G#0[1]
?A0 A0[1]
?A#0 A#0[1]
?B0 B0[1]
?C1 C1[1]
?C#1 C#1[1]
?D1 D1[1]
?D#1 D#1[1]
G#0 G#0[1]
A0 A0[1]
A#0 A#0[1]
B0 B0[1]
C1 C1[1]
C#1 C#1[1]
D1 D1[1]
D#1 D#1[1]
# mute: color = yellow (vel. 5)
?E1 E1[5]
?F1 F1[5]
?F#1 F#1[5]
?G1 G1[5]
?G#1 G#1[5]
?A1 A1[5]
?A#1 A#1[5]
?B1 B1[5]
E1 E1[5]
F1 F1[5]
F#1 F#1[5]
G1 G1[5]
G#1 G#1[5]
A1 A1[5]
A#1 A#1[5]
B1 B1[5]
# select (will light up in red)
# NOTE: Ardour apparently doesn't update these when changing banks.
?C2 E5
?C#2 F5
?D2 F#5
?D#2 G5
?E2 G#5
?F2 A5
?F#2 A#5
?G2 B5
C2 E5
C#2 F5
D2 F#5
D#2 G5
E2 G#5
F2 A5
F#2 A#5
G2 B5

59
examples/MP100.midizaprc Normal file
View File

@ -0,0 +1,59 @@
# Minimal Mackie emulation for the Harley Benton MP-100 a.k.a. MeloAudio MIDI
# Commander foot controller (https://meloaudio.com)
# This device is rather limited in what it can do as a Mackie controller (no
# feedback, just 4 switches, 4 toggles, and 2 continuous controllers), but it
# may still come in handy to guitarists for basic hands-free DAW control. Note
# that we can't make good use of the bank switches on the device (the two
# extra switches on the right), since these always emit a PC message, which
# interferes with our use of the PC messages for the transport controls. It
# may be possible to do something better by configuring a custom mode on the
# device, but here we stick to what's available in the factory settings.
# Copyright (c) 2019 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-MP100"
JACK_PORTS 1
# Auto-connect to the MP-100 on the input, and Ardour's Mackie control input
# on the output side.
JACK_IN TSMIDI.* MIDI 1
JACK_OUT ardour:mackie control in
# The following configuration assumes that the MP-100 is set to mode 1 (JAMP),
# which is the default. Note that the controller numbers for the top row and
# the expression pedal inputs are specific to JAMP mode, so you will have to
# adjust these if you run the device in a different host mode. As implemented
# below, the controls are laid out as follows:
# top row: [mute] [solo] [rec] [select] EXP1: volume (current channel)
# bottom row: [stop] [play] [chan<] [chan>] EXP2: volume (master)
[MIDI]
# Note that MCP expects a note-on/off pair for each activation of the
# mure/solo/rec/select switches, while the MP-100 top switches act as toggles,
# so we have to use suitable mod translations for the top row.
# top row (mute/solo/rec/select for current channel)
CC22[] E1{127} E1{0} # Mute
CC25[] G#0{127} G#0{0} # Solo
CC24[] C0{127} C0{0} # Rec
CC26[] C2{127} C2{0} # Select
# bottom row (basic transport and bank controls)
PC0 A7 # Stop
PC1 A#7 # Play
PC2 C4 # Channel Left
PC3 C#4 # Channel Right
# EXP-1 and EXP-2 (current channel and master volume)
CC4[] PB[129]-1 # Volume
CC7[] PB[129]-9 # Master

View File

@ -0,0 +1,70 @@
# Minimal Mackie emulation for the AKAI MPKmini mkII
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-MPKmini2"
JACK_PORTS 1
# Auto-connect to the MPKMini2 on the input, and Ardour's Mackie control input
# on the output side.
JACK_IN MPKmini2 MIDI 1
JACK_OUT ardour:mackie control in
# This configuration assumes that the MPKmini2 is set to factory defaults.
# The device doesn't provide much feedback possibilities, so we don't even
# try. Controls: The joystick can be used as a shuttle control (push to the
# left for rewind, to the right for fast forward). The eight knobs are mapped
# to the channel volume controls. The drum pads are assigned as follows, with
# the transport section on bank A and the cursor and bank controls on bank B:
# Bank A: Pad 1-4: Stop Play Rec Cycle; Pad 5-8: Rew FFwd Click Marker
# Bank B: Pad 1-4: Up Down Left Right; Pad 5-8: Bank< Bank> Channel< Channel>
# TODO: No assignments for the pads in CC and PROG CHANGE mode at this time,
# they may be added later when we figure out what to do with them. Also, no
# encoders on the MPKmini2, so no jog wheel. :(
[MIDI]
# Pads, Bank A
G#3 A7 # Stop
A3 A#7 # Play
A#3 B7 # Rec
B3 D7 # Cycle
C4 G7 # Rew
C#4 G#7 # FFwd
D4 F7 # Click
D#4 C7 # Marker
# Pads, Bank B
G#2 C8 # Up
A2 C#8 # Down
A#2 D8 # Left
B2 D#8 # Right
C3 A#3 # Bank Left
C#3 B3 # Bank Right
D3 C4 # Channel Left
D#3 C#4 # Channel Right
# Joystick (push left/right for Rewind/Fast Forward)
PB[] $M0{0:8192,1,2}?
M0[] $M1{1,-1} $M2{-1:2,1,-1}
M1[] G7[127] # Rew
M2[] G#7[127] # FFwd
# knobs (MC pitch bends, use 129 as step size to get full range)
CC1[] PB[129]-1
CC2[] PB[129]-2
CC3[] PB[129]-3
CC4[] PB[129]-4
CC5[] PB[129]-5
CC6[] PB[129]-6
CC7[] PB[129]-7
CC8[] PB[129]-8

383
examples/Maschine.midizaprc Normal file
View File

@ -0,0 +1,383 @@
# Mackie emulation for the NI Maschine Mk3
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-Maschine"
JACK_PORTS 2
SYSTEM_PASSTHROUGH # pass through MCP feedback
# automatic connections
JACK_IN1 Ctlra Maschine Mk3
JACK_OUT1 ardour:mackie control in
JACK_IN2 ardour:mackie control out
JACK_OUT2 Ctlra Maschine Mk3
# NOTE: At present, this controller isn't properly supported by ALSA, but it
# can be made to work in Linux with Harry van Haaren's Ctlra software.
# Specifically, you'll need the ctlra_daemonx program from the
# mapping_v1-daemonx branch which for the time being can be found here:
# https://github.com/agraef/openAV-Ctlra/tree/mapping_v1-daemonx
# After cloning the repository and switching to the mapping_v1-daemonx
# branch, you can run the following to install the libctlra library along with
# ctlra_daemonx: meson build && cd build && ninja && sudo ninja install
# Then run ctlra_daemonx -fnm alongside with midizap, and check that the ports
# are connected as follows (the JACK_IN/OUT directives above should take care
# of this automagically): Ctlra Maschine Mk3 -> midizap midi_in / midi_out ->
# Ardour mackie control in / mackie control out -> midizap midi_in2 /
# midi_out2 -> Ctlra Maschine Mk3.
# Usage (executive summary):
# - SHIFT, SELECT, SOLO and MUTE are used as shift buttons which change the
# functions of the encoders and some keys
# - The transport section (bottom/left) mostly does what you'd expect it to.
# PLAY, REC, STOP and RESTART/Loop all work as advertized. The two keys
# above REC and STOP are used as REWIND and FAST FWD.
# - The eight buttons at the top select a channel, or engage Channel
# Rec/Solo/Mute when combined with SELECT, SOLO and MUTE, respectively.
# - The eight small encoders function as the volume faders, or as encoders
# (usually pan) when combined with SHIFT. Touching the encoders means
# touching the faders, or pushing the encoders when combined with SELECT.
# - The big encoder emulates the jog wheel. Pushing this encoder left, right,
# up and down emulates the cursor keys, pressing it engages the Zoom
# function (which also lights up all four LEDs around the encoder).
# - The touchstrip is used as the master fader. The four buttons above it
# emulate the MCP shift keys (SHIFT, CTRL, OPTION, ALT/CMD).
# - The left and right arrow keys in the top/left button group are used to
# change banks, or move by single channels when combined with SHIFT.
# These are just the most important mappings; there's a bunch of other
# bindings, but I'll leave you to discover them on your own (see below for all
# the gory details). There are so many keys on this device, many of them are
# still unused right now, so you can easily add more bindings if you want. :)
[MIDI]
# We use the Mk3's dedicated SHIFT button as our primary shift key, to provide
# alternative functions to some of the buttons and the faders.
F2 SHIFT ^F2 RELEASE SHIFT ^F2
# transport (assigned to the transport section on the bottom left)
F#5 A7 # Stop
E5 A#7 # Play
F5 B7 # Rec
C5 D7 # Cycle (RESTART/Loop key)
C#5 G7 # Rew (ERASE/Replace key)
D5 G#7 # FFwd (TAP/Metro key)
D#5 C#7 # Nudge (FOLLOW/Grid key)
# additional functions on shifted keys
^C#5 D#7 # In (SHIFT ERASE/Replace key)
^F5 E7 # Out (SHIFT Rec key)
^D5 F7 # Click (SHIFT TAP/Metro key)
^D#5 C7 # Mark (SHIFT FOLLOW/Grid key)
# switch between SMPTE and BBT timecode
C#6 F4 # (MIDI/Channel key)
# Bank/channel left/right (arrow keys in the top left section)
E6 A#3 # Bank Left
A5 B3 # Bank Right
^E6 C4 # Channel Left
^A5 C#4 # Channel Right
# the four buttons below the arrow keys are assigned to the utility functions
F6 G#6 # Save (SAVE/File key)
G#5 A6 # Undo (SETTINGS key)
F#6 A#6 # Cancel (AUTO key)
G5 B6 # Enter (MACRO/Set key)
# Track/Pan/Send/Plugin (4 buttons right above the grid)
# NOTE: Plugin appears to be unsupported in Ardour.
G#3 E3 # Track (PAD MODE key)
A3 F#3 # Pan (KEYBOARD key)
A#3 F3 # Send (CHORDS key)
B3 G3 # Plugin (STEPS key)
# the four buttons right above the touchstrip are used for the MC shift keys
A4 A#5 # Shift (PITCH key)
A#4 B5 # Control (MOD key)
B4 C6 # Option (PERFORM key)
D3 C#6 # Alt/Cmd (NOTES key)
# big encoder press/left/right/up/down is assigned to the zoom/cursor keys
D2 C8 # Up
E2 C#8 # Down
D#2 D8 # Left
C#2 D#8 # Right
C2 E8 # Zoom
# These can also be shifted, so that you can bind them in the DAW if you
# want. E.g., I have the previous/next marker functions on the shifted cursor
# left/right keys in Ardour.
^D2 A#5 C8 # Up
^E2 A#5 C#8 # Down
^D#2 A#5 D8 # Left
^C#2 A#5 D#8 # Right
^C2 A#5 E8 # Zoom
# The Mk3 has one row of dedicated "channel buttons" at the top, right above
# the display. Since we'd also like to use these for the channel-based
# rec/solo/mute functions, we combine them with the SELECT, SOLO and MUTE
# buttons as additional shift keys.
F#4 SHIFT2 ^F#4 RELEASE SHIFT2 ^F#4
G4 SHIFT3 ^G4 RELEASE SHIFT3 ^G4
G#4 SHIFT4 ^G#4 RELEASE SHIFT4 ^G#4
# top row, unshifted: track select
G6 C2
G#6 C#2
A6 D2
A#6 D#2
B6 E2
C7 F2
C#7 F#2
D7 G2
# SELECT: rec
2^G6 C0
2^G#6 C#0
2^A6 D0
2^A#6 D#0
2^B6 E0
2^C7 F0
2^C#7 F#0
2^D7 G0
# SOLO: solo
3^G6 G#0
3^G#6 A0
3^A6 A#0
3^A#6 B0
3^B6 C1
3^C7 C#1
3^C#7 D1
3^D7 D#1
# MUTE: mute
4^G6 E1
4^G#6 F1
4^A6 F#1
4^A#6 G1
4^B6 G#1
4^C7 A1
4^C#7 A#1
4^D7 B1
# We also assign these to the function keys F1..F8 when shifted. You may want
# to remap these as needed.
^G6 F#4
^G#6 G4
^A6 G#4
^A#6 A4
^B6 A#4
^C7 B4
^C#7 C5
^D7 C#5
# The grid buttons are passed through unchanged, so that you can still use
# them as drum pads (provided that you filter out all MIDI channels except
# channel 10). Note that we use mod translations here, in order to preserve
# the velocities.
C3[]-10 C3-10
C#3[]-10 C#3-10
D3[]-10 D3-10
D#3[]-10 D#3-10
E3[]-10 E3-10
F3[]-10 F3-10
F#3[]-10 F#3-10
G3[]-10 G3-10
G#3[]-10 G#3-10
A3[]-10 A3-10
A#3[]-10 A#3-10
B3[]-10 B3-10
C4[]-10 C4-10
C#4[]-10 C#4-10
D4[]-10 D4-10
D#4[]-10 D#4-10
# Map the A..H buttons to program changes on channel 10 so that you can
# quickly switch the sounds of your drumkit, drum patterns etc. (This is just
# an example, you might want to disable these or remap them as you see fit.)
F#2 PC0-10
G2 PC1-10
G#2 PC2-10
A2 PC3-10
A#2 PC4-10
B2 PC5-10
C3 PC6-10
C#3 PC7-10
# big encoder assigned to MCP jog wheel
CC0~ CC60~
# encoders are mapped to the MCP channel faders
# (MC uses pitch bends here, use 129 as step size to get full range)
CC1~ PB[129]-1
CC2~ PB[129]-2
CC3~ PB[129]-3
CC4~ PB[129]-4
CC5~ PB[129]-5
CC6~ PB[129]-6
CC7~ PB[129]-7
CC8~ PB[129]-8
# master fader (touchstrip)
CC9[] PB[129]-9
# encoders become the MCP encoders when shifted (CC16..CC23, incremental mode)
^CC1~ CC16~
^CC2~ CC17~
^CC3~ CC18~
^CC4~ CC19~
^CC5~ CC20~
^CC6~ CC21~
^CC7~ CC22~
^CC8~ CC23~
# encoder touches = fader touches
E7 G#8
F7 A8
F#7 A#8
G7 B8
G#7 C9
A7 C#9
A#7 D9
B7 D#9
D#7 E9
# SELECT + encoder touches = push encoders -- we can't use SHIFT here since
# touches might easily get triggered when the encoders are moved
2^E7 G#2
2^F7 A2
2^F#7 A#2
2^G7 B2
2^G#7 C3
2^A7 C#3
2^A#7 D3
2^B7 D#3
2^D#7 E3
# feedback section ########################################################
[MIDI2]
# transport
A7 F#5 $M0 # reset all meters, see "meter" in the MCP feedback section below
A#7 E5
B7 F5 # Rec
D7 C5 # Cycle
G7 C#5
G#7 D5
C#7 D#5
^F7 D5
^C7 D#5
# SMPTE/BBT
F4 C#6
# channel left/right keys
C4 E6
C#4 A5
# row above grid (track/pan/send/plugin)
E3 G#3
F#3 A3
F3 A#3
G3 B3
# feedback for the MC shift keys
A#5 A4 # Shift
B5 A#4 # Control
C6 B4 # Option
C#6 D3 # Alt/Cmd
# zoom (as the big encoder itself has no led, we light up the 4 leds around it
# instead; color: 4 = blue)
E8 C#2[4] D2[4] D#2[4] E2[4]
# select
# NOTE: Ardour apparently doesn't update these when changing banks.
C2 G6
C#2 G#6
D2 A6
D#2 A#6
E2 B6
F2 C7
F#2 C#7
G2 D7
# no feedback for encoders, only touchstrip
PB[128]{0}-9 CC9'
# MCP feedback (simply passed through, ctlra_daemonx handles these
# automagically).
# rec/solo/mute
C0[] C0
C#0[] C#0
D0[] D0
D#0[] D#0
E0[] E0
F0[] F0
F#0[] F#0
G0[] G0
G#0[] G#0
A0[] A0
A#0[] A#0
B0[] B0
C1[] C1
C#1[] C#1
D1[] D1
D#1[] D#1
E1[] E1
F1[] F1
F#1[] F#1
G1[] G1
G#1[] G#1
A1[] A1
A#1[] A#1
B1[] B1
# meter values
CP[] CP
# NOTE: We only report the values as we receive them here, there's no
# automatic decay of the meters like with real Mackie hardware. Thus we
# explicitly reset all meters when transport stops below. (Ardour at least
# does *not* do that automatically.)
M0[1] CP{0} CP{16} CP{32} CP{48} CP{64} CP{80} CP{96} CP{112}
# timecode
CC64[] CC64
CC65[] CC65
CC66[] CC66
CC67[] CC67
CC68[] CC68
CC69[] CC69
CC70[] CC70
CC71[] CC71
CC72[] CC72
CC73[] CC73

24
examples/README.md Normal file
View File

@ -0,0 +1,24 @@
# Examples
This folder contains a couple of sample midizap configurations for different controllers:
- [APCmini.midizaprc](APCmini.midizaprc): Mackie emulation for the AKAI APCmini
- [Maschine.midizaprc](Maschine.midizaprc): Mackie emulation for the NI Maschine Mk3 (this requires Harry v. Haaren's Ctlra software to make the device work in Linux, please check the config for detailed instructions)
- [MPKmini2.midizaprc](MPKmini2.midizaprc): minimal Mackie emulation for the AKAI MPKmini Mk2 keyboard
- [nanoKONTROL2.midizaprc](nanoKONTROL2.midizaprc): fix up the marker keys of the Korg nanoKONTROL2 in Cubase mode for use in Ardour
- [XTouchMini.midizaprc](XTouchMini.midizaprc): adds a bunch of Mackie control functions to make the device more useful in MC mode
- [XTouchONE.midizaprc](XTouchONE.midizaprc): adds a bunch of Mackie control functions to make the device more useful in MC mode
Other interesting items:
- [x-touch-one.device](x-touch-one.device): X-Touch ONE device description for Ardour 5.12 (this is basically the X-Touch description with a bank size of 1, so that all tracks become accessible)
Installation: Copy the x-touch-one.device file to your ~/.config/ardour5/mcp/ directory and select "Behringer X-Touch ONE" as your Mackie control surface in Ardour's preferences dialog.
Note: You can also use my patched-up version of Ardour 5.12 instead, which fixes up the single-channel bank changes so that the X-Touch ONE will work nicely with Ardour *without* having to set the bank size to 1 (which will be problematic if you use the ONE in combination with other Mackie controllers which require a larger bank size). You can find the sources here: https://github.com/agraef/ardour/tree/5.12-ag-mcpfixes. Make sure to check out the 5.12-ag-mcpfixes branch. This also has a few other bugfixes in the MCP code which will become available with Ardour 6.0, but aren't in the upstream 5.12 tree.

View File

@ -0,0 +1,64 @@
# This is an addon to XTouchMini.midizaprc which adds some extra MIDI CC
# assignments for the encoders and the master fader to the ALT layer.
# NOTE: This is to be used along with XTouchMini.midizaprc (which see).
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-XTouchMini+"
JACK_PORTS 2
JACK_IN1 X-TOUCH MINI MIDI 1
JACK_OUT1 ardour:MIDI control in
JACK_IN2 ardour:MIDI control out
JACK_OUT2 X-TOUCH MINI MIDI 1
[MIDI]
# ALT button (no feedback here, as this is already handled in the
# XTouchMini.midizaprc "mother" configuration)
E7 SHIFT2 RELEASE SHIFT2
# The encoders and the master fader are assigned to standard MIDI controls
# CC1..CC9 on the ALT layer, so you can assign them freely with Ardour's MIDI
# learn facility, and use the unit as an MC device at the same time.
# ALT encoders and fader = CC1..CC9, with direct feedback
2^CC16~ CC1 $M1
2^CC17~ CC2 $M2
2^CC18~ CC3 $M3
2^CC19~ CC4 $M4
2^CC20~ CC5 $M5
2^CC21~ CC6 $M6
2^CC22~ CC7 $M7
2^CC23~ CC8 $M8
2^PB[128]{0}-9 CC9'
# macros handling direct feedback for CC1..CC8
M1[12]{0} !CC48{33-43}'
M2[12]{0} !CC49{33-43}'
M3[12]{0} !CC50{33-43}'
M4[12]{0} !CC51{33-43}'
M5[12]{0} !CC52{33-43}'
M6[12]{0} !CC53{33-43}'
M7[12]{0} !CC54{33-43}'
M8[12]{0} !CC55{33-43}'
[MIDI2]
# feedback for the encoders on the ALT layer (CC1..CC8)
2^CC1[12]{0} CC48{33-43}'
2^CC2[12]{0} CC49{33-43}'
2^CC3[12]{0} CC50{33-43}'
2^CC4[12]{0} CC51{33-43}'
2^CC5[12]{0} CC52{33-43}'
2^CC6[12]{0} CC53{33-43}'
2^CC7[12]{0} CC54{33-43}'
2^CC8[12]{0} CC55{33-43}'

View File

@ -0,0 +1,190 @@
# While the X-Touch Mini is very popular as a Lightroom controller, musicians
# often complain that its Mackie control mode is just too basic. But midizap
# makes it easy to add most of the essential MC functions that are missing, so
# that the unit becomes really usable as a control surface.
# NOTE: This configuration assumes that the X-Touch Mini is in MC mode, which
# is the case if the MC MODE LED on the right side is lit. If necessary, you
# can switch the device to MC mode by holding the MC key while powering it up.
# NOTE: There's an addon to this configuration with some MIDI CC bindings on
# the ALT layer, see XTouchMini+.midizaprc.
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-XTouchMini"
JACK_PORTS 2
# Automatic connections for the device and Ardour.
JACK_IN1 X-TOUCH MINI MIDI 1
JACK_OUT1 ardour:mackie control in
JACK_IN2 ardour:mackie control out
JACK_OUT2 X-TOUCH MINI MIDI 1
# Pass everything through, except for the translations below.
PASSTHROUGH
SYSTEM_PASSTHROUGH
# The idea of this mapping is to leave the original bindings mostly untouched.
# In order to accommodate the missing MC functions, we add two shifted layers.
# To access these layers, we reassign the first two keys in the bottom row as
# internal shift keys, denoted SHIFT and ALT in the following.
# Note that the device, besides the encoders and the master fader, has two
# rows of 8 buttons and 2 buttons on the right which are laid out in MC mode
# as follows:
# [CLICK] [SOLO] [TRACK] [SEND] [PAN] [PLUGIN] [EQ] [INST] [MARKER/A]
# [DROP/MC] [REPLACE] [REW] [FFWD] [LOOP] [STOP] [PLAY] [REC] [NUDGE/B]
# This midizap configuration remaps them as follows:
# [CLICK] [SOLO] [TRACK] [SEND] [PAN] [PLUGIN] [EQ] [INST] [BANK LEFT]
# [SHIFT] [ALT] [REW] [FFWD] [LOOP] [STOP] [PLAY] [REC] [BANK RIGHT]
# Note the two shift keys (SHIFT and ALT) in the lower button row, and the
# bank switch keys on the right. Other than that, the unshifted layer is
# unchanged. On the SHIFT layer, the bindings are as follows:
# [SELECT] ... [CHAN LEFT]
# [DROP] [REPLACE] [FLIP] [MARKER] [NUDGE] [CHAN RIGHT]
# Note that the remapped functions (DROP, REPLACE, MARKER, NUDGE) are now
# available as shifted keys in the bottom row. We also added the FLIP key
# there. The shifted keys in the top row are bound to the channel SELECT
# functions. Moreover, our translations for the SHIFT layer also assign the
# master fader to the first channel, like on the X-Touch ONE, and the encoders
# become the eight channel faders.
# Finally, the ALT layer. Since Ardour requires some of the keys to be
# combined with the MC SHIFT key to get at some functions (in particular,
# SHIFT + SELECT/MARKER/NUDGE), we provide those shifted bindings on this
# layer. We also throw in some more MC keys (cursor and zoom):
# [^SELECT] ... [UP]
# [LEFT] [RIGHT] [ZOOM] [^MARKER] [^NUDGE] [DOWN]
# Note that the way I've configured this layer is somewhat tailored to Ardour,
# so you may want to adjust it to your liking. The SHIFT layer, in contrast,
# should be pretty generic, and work fine as is with most DAWs.
[MIDI]
# Note that, as explained in the midizap manual, you can change the SHIFT and
# ALT keys to CapsLock-style keys by removing the RELEASE part of the
# sequence. They will then work as toggles. With the RELEASE sequence, they
# work as momentary switches, i.e., you need to hold them down to invoke a
# shifted function.
# MC = SHIFT button, with direct feedback
D#7 SHIFT ^D#7 RELEASE SHIFT ^D#7
# ALT button
E7 SHIFT2 ^E7 RELEASE SHIFT2 ^E7
# A/B buttons on the right = bank left/right
C7 A#3 # bank left
C#7 B3 # bank right
# shifted A/B buttons = channel left/right
^C7 C4 # channel left
^C#7 C#4 # channel right
# other shifted buttons
^G7 D#7 # REW = DROP
^G#7 E7 # FFWD = REPLACE
^D7 D4 # LOOP = FLIP
^A7 C7 # STOP = MARKER
^A#7 C#7 # PLAY = NUDGE
# ^B7 # REC, currently unassigned
# shifted top row = SELECT
^F7 C2
^F#7 C#2
^E3 D2
^F3 D#2
^F#3 E2
^G3 F2
^G#3 F#2
^A3 G2
# shifted encoders = channel faders
^CC16~ PB[129]-1
^CC17~ PB[129]-2
^CC18~ PB[129]-3
^CC19~ PB[129]-4
^CC20~ PB[129]-5
^CC21~ PB[129]-6
^CC22~ PB[129]-7
^CC23~ PB[129]-8
# fader goes to first channel in bank when shifted
^PB[]-9 PB-1
# ALT layer. This uses the same trick (the explicit RELEASE sequence) as in
# XTouchONE.midizaprc to transmit the MC SHIFT key (A#5) in the right order.
# ALT top row = SHIFT+SELECT
2^F7 A#5 C2 RELEASE C2 A#5
2^F#7 A#5 C#2 RELEASE C#2 A#5
2^E3 A#5 D2 RELEASE D2 A#5
2^F3 A#5 D#2 RELEASE D#2 A#5
2^F#3 A#5 E2 RELEASE E2 A#5
2^G3 A#5 F2 RELEASE F2 A#5
2^G#3 A#5 F#2 RELEASE F#2 A#5
2^A3 A#5 G2 RELEASE G2 A#5
2^A7 A#5 C7 RELEASE C7 A#5 # ALT+STOP = SHIFT+MARKER
2^A#7 A#5 C#7 RELEASE C#7 A#5 # ALT+PLAY = SHIFT+NUDGE
2^G7 D8 # ALT+REW = LEFT
2^G#7 D#8 # ALT+FFWD = RIGHT
2^D7 E8 # ALT+LOOP = ZOOM
2^C7 C8 # ALT+A = UP
2^C#7 C#8 # ALT+B = DOWN
# Encoders and fader are disabled on the ALT layer, to accommodate the MIDI CC
# bindings in XTouchMini+.midizaprc.
2^CC16~ NOP
2^CC17~ NOP
2^CC18~ NOP
2^CC19~ NOP
2^CC20~ NOP
2^CC21~ NOP
2^CC22~ NOP
2^CC23~ NOP
2^PB[]-9 NOP
[MIDI2]
# feedback for the BANK LEFT/RIGHT buttons
A#3 C7
B3 C#7
# We also provide feedback for the *faders* here, so that the current values
# are shown on the LED rings of the encoders while the faders are being
# operated. Note that the fader values are shown in "fan" mode (arc from zero
# to the current value) so that they're distinguishable from pan values which
# will be shown in "pan" mode (single tick indicating left/right position).
# NOTE: This is still experimental. Overloading the encoders in this manner is
# convenient and seems to work reasonably well in Ardour at least, but having
# some encoders display pan and others volume may be confusing at times.
PB[1536]{0}-1 CC48{33-43}'
PB[1536]{0}-2 CC49{33-43}'
PB[1536]{0}-3 CC50{33-43}'
PB[1536]{0}-4 CC51{33-43}'
PB[1536]{0}-5 CC52{33-43}'
PB[1536]{0}-6 CC53{33-43}'
PB[1536]{0}-7 CC54{33-43}'
PB[1536]{0}-8 CC55{33-43}'

View File

@ -0,0 +1,115 @@
# The X-Touch ONE is a very capable Mackie device as it is, but it can still
# be improved a bit with some midizap magic. Most notably, the device lacks a
# SHIFT key, so we remap one of the lesser-used keys to provide an extra
# shifted layer which can be assigned freely to additional MC functions which
# aren't readily available on the device.
# NOTE: This config assumes that the X-Touch ONE is in its default Mackie
# mode. Otherwise you will have to adjust the mapping accordingly.
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-XTouchONE"
JACK_PORTS 2
# Automatic connections for the device and Ardour.
JACK_IN1 X-Touch One MIDI 1
JACK_OUT1 ardour:mackie control in
JACK_IN2 ardour:mackie control out
JACK_OUT2 X-Touch One MIDI 1
# Pass everything through, except for the mappings below.
PASSTHROUGH
SYSTEM_PASSTHROUGH
[MIDI]
# I use the SCRUB key (F8 in MC mode) as the SHIFT key, because Ardour doesn't
# need it. YMMV, though, so if you need that key, you'll have to substitute
# another one that you can spare in the rule below.
F8 SHIFT ^F8 RELEASE SHIFT ^F8
# Remap the shifted F1 .. F4 keys to do something useful. I have them bound to
# the most important encoder assignment keys here, but of course you can
# change them to anything you want.
^F#4 E3 # Track (Trim in Ardour)
^G4 F#3 # Pan
^G#4 F3 # Send
^A4 G3 # Plugin (not supported by Ardour)
# Remap the shifted F5 and F6 keys to F7 and F8 which are missing on the ONE.
^A#4 C5
^B4 C#5
# If you're using the encoder assignment keys, then most likely you also want
# to have a FLIP button; we (rather arbitrarily) assign it to the shifted SOLO
# button in the TRANSPORT section.
^F#7 D4 # Flip
# Since the X-Touch ONE is a single-channel controller, I like to have the
# bank and channel keys linked up with channel SELECT, so that the current
# channel is also selected in the DAW when switching banks. Therefore I have
# bound these functions to the shifted bank and channel keys here, but of
# course you can change them to anything you want.
^A#3 A#3 C2 # BANK< SELECT
^B3 B3 C2 # BANK> SELECT
^C4 C4 C2 # CHAN< SELECT
^C#4 C#4 C2 # CHAN> SELECT
# With the translations below, the other shifted buttons are simply passed
# through along with the MC SHIFT key (A#5). You can either assign these
# combinations in the DAW (e.g., I have the shifted left/right keys bound to
# the previous/next marker functions in Ardour), or just directly edit the
# bindings below as you see fit.
# NOTE: We use an explicit RELEASE sequence here, to prevent the MC SHIFT key
# from being released too early. (Note that by default, midizap will release
# MIDI key events in the same order in which they are pressed, which may not
# work in some key combinations. At least I found that this confuses Ardour in
# some cases.)
^C8 A#5 C8 RELEASE C8 A#5 # Up
^C#8 A#5 C#8 RELEASE C#8 A#5 # Down
^D8 A#5 D8 RELEASE D8 A#5 # Left
^D#8 A#5 D#8 RELEASE D#8 A#5 # Right
^E8 A#5 E8 RELEASE E8 A#5 # Zoom
^C0 A#5 C0 RELEASE C0 A#5 # SHIFT REC
^G#0 A#5 G#0 RELEASE G#0 A#5 # SHIFT SOLO
^E1 A#5 E1 RELEASE E1 A#5 # SHIFT MUTE
^C2 A#5 C2 RELEASE C2 A#5 # SHIFT SELECT
^C7 A#5 C7 RELEASE C7 A#5 # SHIFT MARKER
^C#7 A#5 C#7 RELEASE C#7 A#5 # SHIFT NUDGE
^D7 A#5 D7 RELEASE D7 A#5 # SHIFT CYCLE
^D#7 A#5 D#7 RELEASE D#7 A#5 # SHIFT DROP
^E7 A#5 E7 RELEASE E7 A#5 # SHIFT REPLACE
^F7 A#5 F7 RELEASE F7 A#5 # SHIFT CLICK
^G7 A#5 G7 RELEASE G7 A#5 # SHIFT REW
^G#7 A#5 G#7 RELEASE G#7 A#5 # SHIFT FFWD
^A7 A#5 A7 RELEASE A7 A#5 # SHIFT STOP
^A#7 A#5 A#7 RELEASE A#7 A#5 # SHIFT PLAY
^B7 A#5 B7 RELEASE B7 A#5 # SHIFT REC
[MIDI2]
# feedback for the (shifted) F1 .. F6 bindings and the remapped SOLO key
E3 F#4
F#3 G4
F3 G#4
A3 A4
C5 A#4
C#5 B4
D4 F#7 # FLIP -> SOLO

View File

@ -0,0 +1,59 @@
# The nanoKONTROL2 has no plain Mackie emulation. Its Cubase mode comes close,
# but has the MARKER keys set up in a Cubase-specific way. This config patches
# them up a bit so that they do something useful on Linux, at least in Ardour.
# Copyright (c) 2018 Albert Graef <aggraef@gmail.com>
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any
# warranty.
JACK_NAME "midizap-nanoKONTROL2"
JACK_PORTS 2
# automatic connections
JACK_IN1 nanoKONTROL2 MIDI 1
JACK_OUT1 ardour:mackie control in
JACK_IN2 ardour:mackie control out
JACK_OUT2 nanoKONTROL2 MIDI 1
# Pass everything through (including feedback), except for the mappings below.
PASSTHROUGH
# NOTE: This assumes that you run the nanoKONTROL2 in *Cubase* mode. Use
# Korg's editor application to enter that mode, or hold the SET and << keys
# when plugging in the device. (Note that Ardour also has support for the
# nanoKONTROL2 as a generic MIDI device, in which case you can leave the
# device in its default CC mode and midizap isn't required.)
[Ardour] ^ardour_ardour$
# AFAICT, the "Prev/Next Marker" functions have no dedicated keys on the MCU.
# In Ardour, they can be accessed with the Q and W keys.
E7 "q" # MARKER <
F#7 "w" # MARKER >
[MIDI]
# Here's another way to access the "Prev/Next Marker" functions which doesn't
# rely on keyboard shortcuts of the application. Instead, it assumes that you
# assign an MCU key combination to these functions in your DAW. I have Ardour
# set up so that SHIFT + the left and right cursor keys on the MCU jumps to
# the previous and next marker, respectively. Of course, you can change this
# to whatever is convenient for you.
E7 A#5 D8 # MCU SHIFT <-
F#7 A#5 D#8 # MCU SHIFT ->
# Ardour uses the MCU's MARKER key to set a marker, so that's what we assign
# the SET button to.
F7 C7 # SET -> MARKER
# Of course, you can set the key to whatever you want. E.g., to map it to the
# SHIFT key:
#F7 A#5

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<MackieProtocolDevice>
<Name value="Behringer X-Touch ONE"/>
<DeviceType value="MCU"/>
<Strips value="1"/>
<MasterFader value="yes"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="yes"/>
<Extenders value="0"/>
<MasterPosition value="0"/>
<GlobalControls value="yes"/>
<JogWheel value="yes"/>
<TouchSenseFaders value="yes"/>
<HasSeparateMeters value="yes"/>
</MackieProtocolDevice>

View File

@ -150,12 +150,24 @@ process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
jack_midi_event_t event;
void *port_buffer = jack_port_get_buffer(seq->input_port[k], nframes);
// this is used for direct pass-through of system messages
void *out_buffer = seq->passthrough[k] && k < seq->n_out?
jack_port_get_buffer(seq->output_port[k], nframes):0;
if (port_buffer == NULL)
{
fprintf(stderr, "jack_port_get_buffer failed, cannot receive anything.\n");
return;
}
if (out_buffer)
{
#ifdef JACK_MIDI_NEEDS_NFRAMES
jack_midi_clear_buffer(out_buffer, nframes);
#else
jack_midi_clear_buffer(out_buffer);
#endif
}
#ifdef JACK_MIDI_NEEDS_NFRAMES
events = jack_midi_get_event_count(port_buffer, nframes);
#else
@ -174,7 +186,7 @@ process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
{
//successful event get
if (event.size <= 3 && event.size >= 1)
if (event.size <= 3 && event.size >= 1 && event.buffer[0] < 0xf0)
{
//not sysex or something
@ -185,6 +197,16 @@ process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
memcpy(rev.data, event.buffer, rev.len);
queue_message(seq->ringbuffer_in[k],&rev);
}
else if (out_buffer && event.size >= 1 && event.buffer[0] >= 0xf0)
{
// direct pass-through of system messages
#ifdef JACK_MIDI_NEEDS_NFRAMES
uint8_t *buffer = jack_midi_event_reserve(out_buffer, event.time, event.size, nframes);
#else
uint8_t *buffer = jack_midi_event_reserve(out_buffer, event.time, event.size);
#endif
if (buffer) memcpy(buffer, event.buffer, event.size);
}
}
}
@ -211,11 +233,14 @@ process_midi_output(JACK_SEQ* seq,jack_nframes_t nframes)
return;
}
if (!seq->passthrough[k])
{
#ifdef JACK_MIDI_NEEDS_NFRAMES
jack_midi_clear_buffer(port_buffer, nframes);
jack_midi_clear_buffer(port_buffer, nframes);
#else
jack_midi_clear_buffer(port_buffer);
jack_midi_clear_buffer(port_buffer);
#endif
}
while (jack_ringbuffer_read_space(seq->ringbuffer_out[k]))
{
@ -401,9 +426,120 @@ session_callback(jack_session_event_t *event, void *seqq)
jack_session_event_free (event);
}
void
connect_callback(jack_port_id_t a, jack_port_id_t b, int yn, void *seqq)
{
JACK_SEQ* seq = (JACK_SEQ*)seqq;
jack_port_t *ap = jack_port_by_id(seq->jack_client, a);
jack_port_t *bp = jack_port_by_id(seq->jack_client, b);
const char *aname = jack_port_name(ap);
const char *bname = jack_port_name(bp);
size_t l = strlen(seq->client_name);
if (jack_port_is_mine(seq->jack_client, ap))
printf("%-*s %s: %s\n", (int)l+10, aname,
(yn ? "connected to" : "disconnected from"), bname);
else if (jack_port_is_mine(seq->jack_client, bp))
printf("%-*s %s: %s\n", (int)l+10, bname,
(yn ? "connected to" : "disconnected from"), aname);
}
// queue for pending connections, to be processed in the main thread
#define CONN_SIZE 256
static int n_inconn, n_outconn;
static struct {
int portno;
char *name;
} inconn[CONN_SIZE], outconn[CONN_SIZE];
static void add_inconn(int portno, const char *name)
{
if (n_inconn < CONN_SIZE) {
inconn[n_inconn].portno = portno;
inconn[n_inconn].name = strdup(name);
n_inconn++;
}
}
static void add_outconn(int portno, const char *name)
{
if (n_outconn < CONN_SIZE) {
outconn[n_outconn].portno = portno;
outconn[n_outconn].name = strdup(name);
n_outconn++;
}
}
static void match_connections(JACK_SEQ* seq, jack_port_t *port)
{
if (jack_port_is_mine(seq->jack_client, port)) return;
int flags = jack_port_flags(port);
const char *name = jack_port_name(port);
if (flags & JackPortIsInput) {
// Try to match the port name to one of our out regexes.
for (int i = 0; i < 2 && i < seq->n_out; i++) {
if (seq->out[i] && regexec(&seq->outre[i], name, 0, 0, 0) == 0 &&
// check that port types are compatible
jack_port_type(seq->output_port[i]) == jack_port_type(port) &&
// check that we're not connected yet
!jack_port_connected_to(seq->output_port[i], name)) {
// we can't connect right here, that has to be done in the main
// thread, so we simply store the request for later
add_outconn(i, name);
}
}
} else if (flags & JackPortIsOutput) {
// Try to match the port name to one of our in regexes.
for (int i = 0; i < 2 && i < seq->n_in; i++) {
if (seq->in[i] && regexec(&seq->inre[i], name, 0, 0, 0) == 0 &&
// check that port types are compatible
jack_port_type(seq->input_port[i]) == jack_port_type(port) &&
// check that we're not connected yet
!jack_port_connected_to(seq->input_port[i], name)) {
add_inconn(i, name);
}
}
}
}
void
registration_callback(jack_port_id_t id, int reg, void *seqq)
{
if (!reg) return;
JACK_SEQ* seq = (JACK_SEQ*)seqq;
jack_port_t *port = jack_port_by_id(seq->jack_client, id);
match_connections(seq, port);
}
////////////////////////////////
//this is run in the main thread
////////////////////////////////
void process_connections(JACK_SEQ* seq)
{
int i;
for (i = 0; i < n_inconn; i++)
if (inconn[i].name) {
if (jack_connect(seq->jack_client,
inconn[i].name,
jack_port_name(seq->input_port[inconn[i].portno])))
fprintf(stderr, "error trying to connect in%d to %s\n",
inconn[i].portno, inconn[i].name);
free(inconn[i].name);
}
n_inconn = 0;
for (i = 0; i < n_outconn; i++)
if (outconn[i].name) {
if (jack_connect(seq->jack_client,
jack_port_name(seq->output_port[outconn[i].portno]),
outconn[i].name))
fprintf(stderr, "error trying to connect out%d to %s\n",
outconn[i].portno, outconn[i].name);
free(outconn[i].name);
}
n_outconn = 0;
}
int
init_jack(JACK_SEQ* seq, uint8_t verbose)
{
@ -412,6 +548,36 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
*client_name = seq->client_name?seq->client_name:"midizap";
jack_status_t status;
// compile the in/out connection regexes
for (int i = 0; i < 2; i++) {
if (seq->in[i] && *seq->in[i]) {
int err = regcomp(&seq->inre[i], seq->in[i], REG_EXTENDED|REG_NOSUB);
if (err) {
char buf[1024];
regerror(err, &seq->inre[i], buf, sizeof(buf));
fprintf(stderr, "error compiling in%d regex: %s\n%s\n",
i, seq->in[i], buf);
regfree(&seq->inre[i]);
seq->in[i] = 0;
}
} else {
seq->in[i] = 0;
}
if (seq->out[i] && *seq->out[i]) {
int err = regcomp(&seq->outre[i], seq->out[i], REG_EXTENDED|REG_NOSUB);
if (err) {
char buf[1024];
regerror(err, &seq->outre[i], buf, sizeof(buf));
fprintf(stderr, "error compiling out%d regex: %s\n%s\n",
i, seq->out[i], buf);
regfree(&seq->outre[i]);
seq->out[i] = 0;
}
} else {
seq->out[i] = 0;
}
}
if(verbose)printf("opening client...\n");
seq->jack_client = jack_client_open(client_name, JackNullOption, &status);
@ -429,13 +595,12 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
printf("JACK client name changed to: %s\n", client_name);
}
if(verbose)printf("assigning shutdown callback...\n");
jack_on_shutdown(seq->jack_client, shutdown_callback, (void*)seq);
if(verbose)printf("assigning session callback...\n");
jack_set_session_callback(seq->jack_client, session_callback, (void*)seq);
jack_set_port_registration_callback(seq->jack_client, registration_callback, (void*)seq);
if (verbose) jack_set_port_connect_callback(seq->jack_client, connect_callback, (void*)seq);
if(verbose)printf("assigning process callback...\n");
//if(verbose)printf("assigning process callback...\n");
err = jack_set_process_callback(seq->jack_client, process_callback, (void*)seq);
if (err)
{
@ -449,7 +614,7 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
if(seq->n_in)
{
if(verbose)printf("initializing JACK input: \ncreating ringbuffer...\n");
//if(verbose)printf("initializing JACK input: \ncreating ringbuffer...\n");
seq->ringbuffer_in = calloc(seq->n_in, sizeof(jack_ringbuffer_t*));
seq->input_port = calloc(seq->n_in, sizeof(jack_port_t*));
if (!seq->ringbuffer_in || !seq->input_port)
@ -490,7 +655,7 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
if(seq->n_out)
{
if(verbose)printf("initializing JACK output: \ncreating ringbuffer...\n");
//if(verbose)printf("initializing JACK output: \ncreating ringbuffer...\n");
seq->ringbuffer_out = calloc(seq->n_out, sizeof(jack_ringbuffer_t*));
seq->output_port = calloc(seq->n_out, sizeof(jack_port_t*));
if (!seq->ringbuffer_out || !seq->output_port)
@ -531,6 +696,16 @@ init_jack(JACK_SEQ* seq, uint8_t verbose)
fprintf(stderr, "Cannot activate JACK client.\n");
return 0;
}
// All done, set up the initial connections.
const char **ports = jack_get_ports(seq->jack_client, 0, 0, 0);
for (const char **name = ports; *name; ++name) {
jack_port_t *port = jack_port_by_name(seq->jack_client, *name);
match_connections(seq, port);
}
free(ports);
process_connections(seq);
return 1;
}

View File

@ -1,7 +1,10 @@
#ifndef JACKDRIVER_H
#define JACKDRIVER_H
#include<jack/jack.h>
#include<jack/ringbuffer.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <regex.h>
typedef struct _jseq
{
@ -11,8 +14,9 @@ typedef struct _jseq
jack_client_t *jack_client;
jack_port_t **output_port;
jack_port_t **input_port;
uint8_t n_in;
uint8_t n_out;
uint8_t n_in, n_out, passthrough[2];
char *in[2], *out[2];
regex_t inre[2], outre[2];
} JACK_SEQ;
extern int jack_quit;
@ -20,6 +24,7 @@ extern int jack_quit;
extern char *jack_command_line;
int init_jack(JACK_SEQ* seq, uint8_t verbose);
void process_connections(JACK_SEQ* seq);
void close_jack(JACK_SEQ* seq);
void queue_midi(void* seqq, uint8_t msg[], uint8_t port_no);
int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no);

3
keywords.sed Normal file
View File

@ -0,0 +1,3 @@
/^\#define/!d
s/^\#define //
s/^\([^[:space:]]*\).*$/"\1"/

112
midizap-mode.el.in Normal file
View File

@ -0,0 +1,112 @@
;;; midizap-mode.el --- midizap syntax highlighting for Emacs.
;;; Commentary:
;;; This is a simple mode for editing midizaprc files which provides basic
;;; syntax highlighting, and a command midizap-mode-run, bound to C-c C-c,
;;; which lets you quickly launch a midizap session (with options) on the
;;; edited midizaprc file in an Emacs buffer.
;;; Install this anywhere where Emacs finds it (e.g., in the Emacs site-lisp
;;; directory -- usually under /usr/share/emacs/site-lisp on Un*x systems, or
;;; in any directory on the Emacs load-path) and load it in your .emacs as
;;; follows:
;;; (require 'midizap-mode)
;;; The mode also supports auto-completion of midizaprc keywords, to make this
;;; work you'll need the auto-complete package available from MELPA, please
;;; check: https://github.com/auto-complete/auto-complete.
;;; In the midizap-mode subdirectory you'll find some snippets to be used with
;;; yasnippet (https://github.com/joaotavora/yasnippet); to use these, copy
;;; the entire folder to your ~/.emacs.d/snippets directory.
;;; Code:
(require 'comint)
(defconst midizap-keywords
(list
"DEBUG_REGEX" "DEBUG_STROKES" "DEBUG_KEYS" "DEBUG_MIDI"
"MIDI_OCTAVE" "JACK_NAME" "JACK_PORTS"
"JACK_IN" "JACK_IN1" "JACK_IN2"
"JACK_OUT" "JACK_OUT1" "JACK_OUT2"
"PASSTHROUGH" "SYSTEM_PASSTHROUGH"
"RELEASE" "SHIFT" "SHIFT1" "SHIFT2" "SHIFT3" "SHIFT4"
"CLASS" "TITLE"
;; keysyms
))
;;;###autoload
(define-generic-mode 'midizap-mode
nil
midizap-keywords
'(("^[[:blank:]]*\\(#.*\\)" 1 'font-lock-comment-face t)
("[[:blank:]]+\\(#.*\\)" 1 'font-lock-comment-face t)
("^[[:blank:]]*\\[\\([^\n[]+\\)\\]\\(.*\\)"
1 'font-lock-variable-name-face)
("\\<\\(\\([Kk][Pp]:\\)?[A-Ga-g][#Bb]?-?[0-9]+\\|\\([Mm]\\|[Cc][Hh]\\|[Pp][Bb]\\|[Pp][Cc]\\|[Cc][Cc]\\|[Cc][Pp]\\)[0-9]*\\|XK_[A-Za-z_0-9]+\\(/[UDH]\\)?\\)\\>" 1 'default)
("\\<\\([0-9]+\\)\\>" 1 'font-lock-constant-face))
(list "\\.midizaprc\\'")
(list 'midizap-mode-setup-function)
"Generic mode for midizap configuration files.")
(defvar midizap-mode-keymap (make-sparse-keymap)
"Keymap for midizap-mode.")
(defvar midizap-command "midizap -drk ")
(defvar ac-sources)
(defvar midizap-mode-ac-source
'((candidates . midizap-keywords)))
(defun midizap-mode-run (command)
"Run the current midizaprc file with COMMAND in a comint buffer."
(interactive
(list
(let ((command (eval midizap-command)))
(read-string "Run midizap with: " command))))
(unless (equal command (eval midizap-command))
(setq midizap-command command))
(save-some-buffers)
(let* ((file (buffer-file-name))
(buf-name (concat "*" file "*")))
(with-current-buffer (get-buffer-create buf-name)
(erase-buffer)
(comint-mode)
(comint-exec
buf-name
file
shell-file-name
nil
(list "-c" (concat command " " file)))
(display-buffer buf-name))))
(define-key midizap-mode-keymap "\C-c\C-c" 'midizap-mode-run)
(defun midizap-mode-setup-function ()
"Custom setup function for midizap-mode."
(make-local-variable 'parse-sexp-ignore-comments)
(make-local-variable 'comment-start)
(make-local-variable 'comment-start-skip)
(make-local-variable 'comment-end)
(setq parse-sexp-ignore-comments t
comment-end ""
comment-start "# "
comment-start-skip "# *"
)
(if (boundp 'ac-sources)
(progn
(add-to-list 'ac-modes 'midizap-mode)
(add-to-list 'ac-sources 'midizap-mode-ac-source))
(message "You may want to install and use auto-complete"))
(use-local-map midizap-mode-keymap)
)
(provide 'midizap-mode)
;; End:
;;; midizap-mode.el ends here

27
midizap-mode/midizaprc Normal file
View File

@ -0,0 +1,27 @@
# -*- mode: snippet -*-
# name: midizaprc template
# key: #
# --
# midizap configuration template for Emacs (requires yasnippet)
# Jack client name and ports/connections
# NOTE: Remove the JACK_PORTS and JACK_OUT lines if you don't need MIDI
# output. For bidirectional setups, use JACK_PORTS 2 and add JACK_IN2,
# JACK_OUT2 and a [MIDI2] section for the feedback connection.
JACK_NAME "${1:client-name}"
JACK_PORTS 1
JACK_IN ${2:controller-regex}
JACK_OUT ${3:application-regex}
# common debugging options (uncomment as needed)
#DEBUG_REGEX
#DEBUG_KEYS
#DEBUG_MIDI
# translations go here
[MIDI]

5
midizap-mode/section Normal file
View File

@ -0,0 +1,5 @@
# -*- mode: snippet -*-
# name: section header
# key: [
# --
[${1:name}] ${2:regex}

2782
midizap.1

File diff suppressed because it is too large Load Diff

403
midizap.c
View File

@ -21,6 +21,8 @@ Display *display;
JACK_SEQ seq;
int jack_num_outputs = 0, debug_jack = 0;
int auto_feedback = 1;
int passthrough[2] = {-1, -1}, system_passthrough[2] = {-1, -1};
int shift = 0;
void
@ -60,13 +62,15 @@ send_key(KeySym key, int press)
}
// cached controller and pitch bend values
static int16_t notevalue[16][128];
static int16_t ccvalue[16][128];
static int16_t kpvalue[16][128];
static int16_t cpvalue[16];
static int16_t pbvalue[16] =
{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
static int16_t notevalue[2][16][128];
static int16_t ccvalue[2][16][128];
static int16_t kpvalue[2][16][128];
static int16_t cpvalue[2][16];
static int16_t pbvalue[2][16] =
{{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192},
{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}};
static int dataval(int val, int min, int max)
{
@ -96,14 +100,14 @@ void
handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive);
void
send_midi(uint8_t portno, int status, int data,
int step, int n_steps, int *steps,
int incr, int index, int dir,
send_midi(uint8_t portno, stroke *s, int index, int dir,
int mod, int mod_step, int mod_n_steps, int *mod_steps,
int val, int swap,
int recursive, int depth)
int val, int depth, uint8_t ret_msg[3])
{
if (!jack_num_outputs) return; // MIDI output not enabled
int status = s->status, data = s->data, swap = s->swap,
recursive = s->recursive;
int step = s->step, n_steps = s->n_steps, *steps = s->steps;
if (!recursive && !jack_num_outputs) return; // MIDI output not enabled
uint8_t msg[3];
int chan = status & 0x0f;
msg[0] = status;
@ -116,21 +120,25 @@ send_midi(uint8_t portno, int status, int data,
if (!step) step = 1;
dir *= step;
if (dir > 0) {
if (notevalue[chan][data] >= 127) return;
notevalue[chan][data] += dir;
if (notevalue[chan][data] > 127) notevalue[chan][data] = 127;
if (notevalue[portno][chan][data] >= 127) return;
notevalue[portno][chan][data] += dir;
if (notevalue[portno][chan][data] > 127) notevalue[portno][chan][data] = 127;
} else {
if (notevalue[chan][data] == 0) return;
notevalue[chan][data] += dir;
if (notevalue[chan][data] < 0) notevalue[chan][data] = 0;
if (notevalue[portno][chan][data] == 0) return;
notevalue[portno][chan][data] += dir;
if (notevalue[portno][chan][data] < 0) notevalue[portno][chan][data] = 0;
}
msg[2] = notevalue[chan][data];
msg[2] = notevalue[portno][chan][data];
} else if (mod) {
int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
int v = datavals(r, step, steps, n_steps);
if (d > 127 || d < 0) return;
if (v > 127 || v < 0) return;
if (s->change) {
if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
s->d = d; s->v = v; s->change = 2; // >1 => initialized
}
msg[1] = d;
msg[2] = v;
} else if (!index) {
@ -141,7 +149,7 @@ send_midi(uint8_t portno, int status, int data,
break;
case 0xb0:
if (dir) {
if (incr) {
if (s->incr) {
// incremental controller, simply spit out a relative sign bit value
if (!step) step = 1;
dir *= step;
@ -154,22 +162,27 @@ send_midi(uint8_t portno, int status, int data,
if (!step) step = 1;
dir *= step;
if (dir > 0) {
if (ccvalue[chan][data] >= 127) return;
ccvalue[chan][data] += dir;
if (ccvalue[chan][data] > 127) ccvalue[chan][data] = 127;
if (ccvalue[portno][chan][data] >= 127) return;
ccvalue[portno][chan][data] += dir;
if (ccvalue[portno][chan][data] > 127) ccvalue[portno][chan][data] = 127;
} else {
if (ccvalue[chan][data] == 0) return;
ccvalue[chan][data] += dir;
if (ccvalue[chan][data] < 0) ccvalue[chan][data] = 0;
if (ccvalue[portno][chan][data] == 0) return;
ccvalue[portno][chan][data] += dir;
if (ccvalue[portno][chan][data] < 0) ccvalue[portno][chan][data] = 0;
}
msg[2] = ccvalue[chan][data];
msg[2] = ccvalue[portno][chan][data];
}
} else if (mod) {
int m = (data>=128)*128;
int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
int v = datavals(r, step, steps, n_steps);
if (d > 127 || d < 0) return;
if (d-m > 127 || d-m < 0) return;
if (v > 127 || v < 0) return;
if (s->change) {
if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
s->d = d; s->v = v; s->change = 2; // >1 => initialized
}
msg[1] = d;
msg[2] = v;
} else if (!index) {
@ -185,21 +198,25 @@ send_midi(uint8_t portno, int status, int data,
if (!step) step = 1;
dir *= step;
if (dir > 0) {
if (kpvalue[chan][data] >= 127) return;
kpvalue[chan][data] += dir;
if (kpvalue[chan][data] > 127) kpvalue[chan][data] = 127;
if (kpvalue[portno][chan][data] >= 127) return;
kpvalue[portno][chan][data] += dir;
if (kpvalue[portno][chan][data] > 127) kpvalue[portno][chan][data] = 127;
} else {
if (kpvalue[chan][data] == 0) return;
kpvalue[chan][data] += dir;
if (kpvalue[chan][data] < 0) kpvalue[chan][data] = 0;
if (kpvalue[portno][chan][data] == 0) return;
kpvalue[portno][chan][data] += dir;
if (kpvalue[portno][chan][data] < 0) kpvalue[portno][chan][data] = 0;
}
msg[2] = kpvalue[chan][data];
msg[2] = kpvalue[portno][chan][data];
} else if (mod) {
int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
int v = datavals(r, step, steps, n_steps);
if (d > 127 || d < 0) return;
if (v > 127 || v < 0) return;
if (s->change) {
if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
s->d = d; s->v = v; s->change = 2; // >1 => initialized
}
msg[1] = d;
msg[2] = v;
} else if (!index) {
@ -215,18 +232,22 @@ send_midi(uint8_t portno, int status, int data,
if (!step) step = 1;
dir *= step;
if (dir > 0) {
if (cpvalue[chan] >= 127) return;
cpvalue[chan] += dir;
if (cpvalue[chan] > 127) cpvalue[chan] = 127;
if (cpvalue[portno][chan] >= 127) return;
cpvalue[portno][chan] += dir;
if (cpvalue[portno][chan] > 127) cpvalue[portno][chan] = 127;
} else {
if (cpvalue[chan] == 0) return;
cpvalue[chan] += dir;
if (cpvalue[chan] < 0) cpvalue[chan] = 0;
if (cpvalue[portno][chan] == 0) return;
cpvalue[portno][chan] += dir;
if (cpvalue[portno][chan] < 0) cpvalue[portno][chan] = 0;
}
msg[1] = cpvalue[chan];
msg[1] = cpvalue[portno][chan];
} else if (mod) {
int v = datavals(swap?val%mod:val/mod, step, steps, n_steps);
int v = datavals(swap?val/mod:val%mod, step, steps, n_steps);
if (v > 127 || v < 0) return;
if (s->change) {
if (s->change > 1 && s->v == v) return; // unchanged value
s->v = v; s->change = 2; // >1 => initialized
}
msg[1] = v;
} else if (!index) {
msg[1] = dataval(step, 0, 127);
@ -242,19 +263,23 @@ send_midi(uint8_t portno, int status, int data,
if (!step) step = 1;
dir *= step;
if (dir > 0) {
if (pbvalue[chan] >= 16383) return;
pbvalue[chan] += dir;
if (pbvalue[chan] > 16383) pbvalue[chan] = 16383;
if (pbvalue[portno][chan] >= 16383) return;
pbvalue[portno][chan] += dir;
if (pbvalue[portno][chan] > 16383) pbvalue[portno][chan] = 16383;
} else {
if (pbvalue[chan] == 0) return;
pbvalue[chan] += dir;
if (pbvalue[chan] < 0) pbvalue[chan] = 0;
if (pbvalue[portno][chan] == 0) return;
pbvalue[portno][chan] += dir;
if (pbvalue[portno][chan] < 0) pbvalue[portno][chan] = 0;
}
pbval = pbvalue[chan];
pbval = pbvalue[portno][chan];
} else if (mod) {
int v = datavals(swap?val%mod:val/mod, step, steps, n_steps);
if (v > 8191 || v < -8192) return;
pbval = 8192+v;
int v = datavals(swap?val/mod:val%mod, step, steps, n_steps);
if (v > 16383 || v < 0) return;
if (s->change) {
if (s->change > 1 && s->v == v) return; // unchanged value
s->v = v; s->change = 2; // >1 => initialized
}
pbval = v;
} else if (!index) {
pbval = 8192+dataval(step, -8192, 8191);
} else {
@ -270,8 +295,12 @@ send_midi(uint8_t portno, int status, int data,
}
case 0xc0:
if (mod) {
int d = msg[1] + datavals(swap?val/mod:val%mod, mod_step, mod_steps, mod_n_steps);
int d = msg[1] + datavals(swap?val%mod:val/mod, mod_step, mod_steps, mod_n_steps);
if (d > 127 || d < 0) return;
if (s->change) {
if (s->change > 1 && s->d == d) return; // unchanged value
s->d = d; s->change = 2; // >1 => initialized
}
msg[1] = d;
}
// just send the message
@ -279,10 +308,18 @@ send_midi(uint8_t portno, int status, int data,
default:
return;
}
if (!recursive)
queue_midi(&seq, msg, portno);
else
if (ret_msg) memcpy(ret_msg, msg, 3);
if (recursive) {
// As these values may be mutated, we need to save and restore them, in
// case a macro calls itself recursively.
uint8_t change = s->change;
int d = s->d, v = s->v;
s->change = change>0;
handle_event(msg, portno, depth+1, recursive);
s->change = change;
s->d = d; s->v = v;
} else
queue_midi(&seq, msg, portno);
}
static int stroke_data_cmp(const void *a, const void *b)
@ -315,8 +352,9 @@ static stroke *find_stroke_data(stroke_data *sd,
if (mod) *mod = sd[i].mod;
return sd[i].s[index];
} else if (sd[i].chan > chan ||
(sd[i].chan == chan && sd[i].data > data))
(sd[i].chan == chan && sd[i].data > data)) {
return NULL;
}
}
return NULL;
} else {
@ -519,7 +557,9 @@ static int note_octave(int n)
static char *debug_key(translation *tr, char *name,
int status, int chan, int data, int dir)
{
char *prefix = shift?"^":"", *suffix = "";
char prefix[10] = "";
if (shift) sprintf(prefix, "%d^", shift);
char *suffix = "";
strcpy(name, "??");
switch (status) {
case 0x90: {
@ -607,21 +647,24 @@ static char *debug_key(translation *tr, char *name,
suffix = (dir<0)?"-":"+";
else
suffix = "";
// check for pseudo CC messages denoting a macro
char *tok = data>=128?"M":"CC";
data %= 128;
if (dir && step != 1)
sprintf(name, "%sCC%d[%d]-%d%s", prefix, data, step, chan+1, suffix);
sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, step, chan+1, suffix);
else if (!dir && mod)
if (step != 1)
sprintf(name, "%sCC%d[%d][%d]-%d%s", prefix, data, mod, step, chan+1, suffix);
sprintf(name, "%s%s%d[%d][%d]-%d%s", prefix, tok, data, mod, step, chan+1, suffix);
else if (n_steps) {
sprintf(name, "%sCC%d[%d]{", prefix, data, mod);
sprintf(name, "%s%s%d[%d]{", prefix, tok, data, mod);
int l = strlen(name);
for (int i = 0; i < n_steps; i++, (l = strlen(name)))
sprintf(name+l, "%s%d", i?",":"", steps[i]);
sprintf(name+l, "}-%d%s", chan+1, suffix);
} else
sprintf(name, "%sCC%d[%d]-%d%s", prefix, data, mod, chan+1, suffix);
sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, mod, chan+1, suffix);
else
sprintf(name, "%sCC%d-%d%s", prefix, data, chan+1, suffix);
sprintf(name, "%s%s%d-%d%s", prefix, tok, data, chan+1, suffix);
break;
}
case 0xc0:
@ -722,6 +765,59 @@ static void end_debug()
// maximum recursion depth
#define MAX_DEPTH 32
// shift feedback
uint8_t shift_fb[N_SHIFTS][3];
static int toggle_msg(uint8_t msg[3])
{
if (msg[0] < 0x80) return 0;
switch (msg[0]&0xf0) {
case 0xc0:
return 0;
case 0xd0:
msg[1] = 0;
break;
case 0xe0:
msg[1] = 0x40;
msg[2] = 0;
break;
default:
msg[2] = 0;
break;
}
return 1;
}
int
check_strokes(translation *tr, uint8_t portno, int status, int chan, int data)
{
for (int i = 0; i < 2; i++)
if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
return 1;
if (jack_num_outputs) {
// fall back to default MIDI translation
tr = default_midi_translation[portno];
for (int i = 0; i < 2; i++)
if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
return 1;
// Ignore all MIDI input on the second port if no translation was found in
// the [MIDI2] section (or the section is missing altogether).
if (portno) return 0;
}
// fall back to the default translation
tr = default_translation;
for (int i = 0; i < 2; i++)
if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
return 1;
return 0;
}
void
send_strokes(translation *tr, uint8_t portno, int status, int chan,
int data, int data2, int index, int dir, int depth)
@ -784,7 +880,19 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan,
nkeys++;
} else if (s->shift) {
// toggle shift status
shift = !shift;
if (shift != s->shift) {
if (shift) {
// reset current shift feedback
if (toggle_msg(shift_fb[shift-1]))
queue_midi(&seq, shift_fb[shift-1], 1);
memset(shift_fb[shift-1], 0, 3);
}
shift = s->shift;
} else
shift = 0;
} else if (!s->status) {
// do nothing (NOP)
;
} else {
if (s->recursive && depth >= MAX_DEPTH) {
char name[100];
@ -794,10 +902,22 @@ send_strokes(translation *tr, uint8_t portno, int status, int chan,
else
fprintf(stderr, "Error: $%s: recursion too deep\n",
debug_key(tr, name, status, chan, data, dir));
} else if (s->feedback) {
if (!s->recursive && jack_num_outputs > 1) {
if (s->feedback == 1)
// direct feedback, simply flip the port number
send_midi(!portno, s, index, dir, mod,
step, n_steps, steps, data2, depth, 0);
else if (portno == 0 && !mod && !dir)
// shift feedback, this only works with key translations right
// now, and portno *must* be zero
send_midi(1, s, !shift, 0, 0,
step, n_steps, steps, data2, depth,
shift?shift_fb[shift-1]:0);
}
} else {
send_midi(portno, s->status, s->data, s->step, s->n_steps, s->steps,
s->incr, index, dir, mod,
step, n_steps, steps, data2, s->swap, s->recursive, depth);
send_midi(portno, s, index, dir, mod,
step, n_steps, steps, data2, depth, 0);
}
}
s = s->next;
@ -1256,7 +1376,13 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
msg[0] = status | chan;
msg[2] = 0;
}
if (debug_midi) debug_input(portno, status, chan, msg[1], msg[2]);
if (debug_midi && depth == 0)
debug_input(portno, status, chan, msg[1], msg[2]);
if (passthrough[portno] &&
!check_strokes(tr, portno, status, chan, status>=0xd0?0:msg[1])) {
queue_midi(&seq, msg, portno);
return;
}
switch (status) {
case 0xc0:
start_debug();
@ -1266,6 +1392,7 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
end_debug();
break;
case 0xb0:
if (auto_feedback) ccvalue[!portno][chan][msg[1]] = msg[2];
start_debug();
if (get_cc_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
@ -1284,16 +1411,8 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
inccdown[portno][chan][msg[1]] = 0;
}
}
// This is a bit of a kludge, since controllers can be used in two
// different ways. It may happen that we haven't got any translations for
// the pressed/released state, and that because of a step size >1 the
// incremental controllers also fail to generate a single stroke. In such
// a case, we want to pretend that we haven't actually seen any input at
// all, to prevent spurios section matches when regex debugging is in
// effect. So we reset the debug counter here, which keeps track of the
// number of generated strokes.
debug_count = 0;
if (check_incr(tr, portno, chan, msg[1])) {
debug_count = 0;
// Incremental controller a la MCU. NB: This assumes a signed bit
// representation (values above 0x40 indicate counter-clockwise
// rotation), which seems to be what most DAWs expect nowadays.
@ -1309,15 +1428,16 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
} else if (msg[2] > 64) {
int step = get_cc_step(tr, portno, chan, msg[1], -1);
if (step) {
int d = (msg[2]-64)/step;
uint8_t d = (msg[2]-128)/step;
while (d) {
send_strokes(tr, portno, status, chan, msg[1], 0, 0, -1, depth);
d--;
d++;
}
}
}
} else if (check_ccs(tr, portno, chan, msg[1]) &&
inccvalue[portno][chan][msg[1]] != msg[2]) {
debug_count = 0;
int dir = inccvalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
int step = get_cc_step(tr, portno, chan, msg[1], dir);
if (step) {
@ -1333,6 +1453,7 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
end_debug();
break;
case 0x90:
if (auto_feedback) notevalue[!portno][chan][msg[1]] = msg[2];
start_debug();
if (get_note_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
@ -1351,9 +1472,9 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
innotedown[portno][chan][msg[1]] = 0;
}
}
debug_count = 0;
if (check_notes(tr, portno, chan, msg[1]) &&
innotevalue[portno][chan][msg[1]] != msg[2]) {
debug_count = 0;
int dir = innotevalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
int step = get_note_step(tr, portno, chan, msg[1], dir);
if (step) {
@ -1369,6 +1490,7 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
end_debug();
break;
case 0xa0:
if (auto_feedback) kpvalue[!portno][chan][msg[1]] = msg[2];
start_debug();
if (get_kp_mod(tr, portno, chan, msg[1])) {
send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
@ -1387,9 +1509,9 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
inkpdown[portno][chan][msg[1]] = 0;
}
}
debug_count = 0;
if (check_kps(tr, portno, chan, msg[1]) &&
inkpvalue[portno][chan][msg[1]] != msg[2]) {
debug_count = 0;
int dir = inkpvalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
int step = get_kp_step(tr, portno, chan, msg[1], dir);
if (step) {
@ -1405,6 +1527,7 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
end_debug();
break;
case 0xd0:
if (auto_feedback) cpvalue[!portno][chan] = msg[1];
start_debug();
if (get_cp_mod(tr, portno, chan)) {
send_strokes(tr, portno, status, chan, 0, msg[1], 0, 0, depth);
@ -1423,9 +1546,9 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
incpdown[portno][chan] = 0;
}
}
debug_count = 0;
if (check_cps(tr, portno, chan) &&
incpvalue[portno][chan] != msg[1]) {
debug_count = 0;
int dir = incpvalue[portno][chan] > msg[1] ? -1 : 1;
int step = get_cp_step(tr, portno, chan, dir);
if (step) {
@ -1442,10 +1565,10 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
break;
case 0xe0: {
int bend = ((msg[2] << 7) | msg[1]) - 8192;
if (auto_feedback) pbvalue[!portno][chan] = bend+8192;
start_debug();
//fprintf(stderr, "pb %d\n", bend);
if (get_pb_mod(tr, portno, chan)) {
send_strokes(tr, portno, status, chan, 0, bend, 0, 0, depth);
send_strokes(tr, portno, status, chan, 0, bend+8192, 0, 0, depth);
end_debug();
break;
}
@ -1461,8 +1584,8 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
inpbdown[portno][chan] = 0;
}
}
debug_count = 0;
if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) {
debug_count = 0;
int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1;
int step = get_pb_step(tr, portno, chan, dir);
if (step) {
@ -1486,14 +1609,17 @@ handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
void help(char *progname)
{
fprintf(stderr, "Usage: %s [-h] [-k] [-o[2]] [-j name] [-P[prio]] [-r rcfile] [-d[rskmj]]\n", progname);
fprintf(stderr, "Usage: %s [-hkn] [-d[rskmj]] [-ost[n]] [-j name] [-P[prio]] [[-r] rcfile]\n", progname);
fprintf(stderr, "-h print this message\n");
fprintf(stderr, "-k keep track of key status (ignore double notes)\n");
fprintf(stderr, "-o enable MIDI output (add 2 for a second pair of ports)\n");
fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, m = midi, j = jack; default: all)\n");
fprintf(stderr, "-j jack client name (default: midizap)\n");
fprintf(stderr, "-k keep track of key status (ignore double on/off messages)\n");
fprintf(stderr, "-n no automatic feedback from the second port (-o2)\n");
fprintf(stderr, "-o set number of MIDI output ports (n = 0-2, default: 1)\n");
fprintf(stderr, "-P set real-time priority (default: 90)\n");
fprintf(stderr, "-r config file name (default: MIDIZAP_CONFIG_FILE variable or ~/.midizaprc)\n");
fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, m = midi, j = jack; default: all)\n");
fprintf(stderr, "-s pass-through of system messages (n = 0-2; default: all ports)\n");
fprintf(stderr, "-t pass-through of untranslated messages (n = 0-2; default: all ports)\n");
}
uint8_t quit = 0;
@ -1548,7 +1674,7 @@ static char *absolute_path(char *name)
char *path = malloc(strlen(pwd)+strlen(name)+2);
static char abspath[PATH_MAX];
sprintf(path, "%s/%s", pwd, name);
realpath(path, abspath);
if (!realpath(path, abspath)) strcpy(abspath, path);
free(path); free(pwd);
return abspath;
}
@ -1570,16 +1696,19 @@ main(int argc, char **argv)
// Start recording the command line to be passed to Jack session management.
add_command(argv[0], 0);
while ((opt = getopt(argc, argv, "hko::d::j:r:P::")) != -1) {
while ((opt = getopt(argc, argv, "hkno::d::j:r:P::s::t::")) != -1) {
switch (opt) {
case 'h':
help(argv[0]);
exit(0);
case 'k':
// see comment on -k and keydown_tracker above
keydown_tracker = 1;
add_command("-k", 1);
break;
case 'n':
auto_feedback = 0;
add_command("-n", 1);
break;
case 'o':
jack_num_outputs = 1;
if (optarg && *optarg) {
@ -1587,12 +1716,16 @@ main(int argc, char **argv)
if (!strcmp(a, "2")) {
jack_num_outputs = 2;
add_command("-o2", 1);
} else if (strcmp(a, "1")) {
fprintf(stderr, "%s: wrong port number (-o), must be 1 or 2\n", argv[0]);
} else if (!strcmp(a, "1")) {
add_command("-o1", 1);
} else if (!strcmp(a, "0")) {
jack_num_outputs = -1; // override config setting
add_command("-o0", 1);
} else {
fprintf(stderr, "%s: wrong port number (-o), must be 0, 1 or 2\n", argv[0]);
fprintf(stderr, "Try -h for help.\n");
exit(1);
} else
add_command("-o1", 1);
}
} else
add_command("-o", 1);
break;
@ -1655,34 +1788,97 @@ main(int argc, char **argv)
exit(1);
}
break;
case 's':
if (optarg && *optarg) {
const char *a = optarg;
if (!strcmp(a, "2")) {
system_passthrough[0] = 0;
system_passthrough[1] = 1;
add_command("-s2", 1);
} else if (!strcmp(a, "1")) {
system_passthrough[0] = 1;
system_passthrough[1] = 0;
add_command("-s1", 1);
} else if (!strcmp(a, "0")) {
system_passthrough[0] = system_passthrough[1] = 0;
add_command("-s0", 1);
} else {
fprintf(stderr, "%s: wrong port number (-s), must be 0, 1 or 2\n", argv[0]);
fprintf(stderr, "Try -h for help.\n");
exit(1);
}
} else {
system_passthrough[0] = system_passthrough[1] = 1;
add_command("-s", 1);
}
break;
case 't':
if (optarg && *optarg) {
const char *a = optarg;
if (!strcmp(a, "2")) {
passthrough[0] = 0;
passthrough[1] = 1;
add_command("-t2", 1);
} else if (!strcmp(a, "1")) {
passthrough[0] = 1;
passthrough[1] = 0;
add_command("-t1", 1);
} else if (!strcmp(a, "0")) {
passthrough[0] = passthrough[1] = 0;
add_command("-t0", 1);
} else {
fprintf(stderr, "%s: wrong port number (-t), must be 0, 1 or 2\n", argv[0]);
fprintf(stderr, "Try -h for help.\n");
exit(1);
}
} else {
passthrough[0] = passthrough[1] = 1;
add_command("-t", 1);
}
break;
default:
fprintf(stderr, "Try -h for help.\n");
exit(1);
}
}
if (optind < argc) {
if (optind+1 < argc) {
help(argv[0]);
exit(1);
}
if (optind < argc) {
config_file_name = argv[optind];
add_command(absolute_path(argv[optind]), 1);
}
if (command_line) jack_command_line = command_line;
initdisplay();
// Force the config file to be loaded initially, so that we pick up the Jack
// client name to be used (if not set from the command line). This cannot be
// changed later, so if you want to make changes to the client name in the
// config file take effect, you need to restart the program.
// client name and number of output ports (if not set from the command
// line). This cannot be changed later, so if you want to make changes to
// the client name or number of ports take effect, you need to restart the
// program.
read_config_file();
seq.client_name = jack_client_name;
seq.n_in = jack_num_outputs>1?jack_num_outputs:1;
seq.n_out = jack_num_outputs;
seq.n_out = jack_num_outputs>0?jack_num_outputs:0;
seq.passthrough[0] = jack_num_outputs>0?system_passthrough[0]>0:0;
seq.passthrough[1] = jack_num_outputs>1?system_passthrough[1]>0:0;
seq.in[0] = jack_in_regex[0];
seq.in[1] = jack_num_outputs>1?jack_in_regex[1]:0;
seq.out[0] = jack_num_outputs>0?jack_out_regex[0]:0;
seq.out[1] = jack_num_outputs>1?jack_out_regex[1]:0;
if (!init_jack(&seq, debug_jack)) {
exit(1);
}
passthrough[0] = jack_num_outputs>0?passthrough[0]>0:0;
passthrough[1] = jack_num_outputs>1?passthrough[1]>0:0;
// set real-time scheduling priority if requested
if (prio) {
int pol = SCHED_RR; // other options: SCHED_FIFO, SCHED_OTHER
@ -1705,6 +1901,7 @@ main(int argc, char **argv)
close_jack(&seq);
exit(0);
}
process_connections(&seq);
while (pop_midi(&seq, msg, &portno)) {
handle_event(msg, portno, 0, 0);
time_t t = time(0);

View File

@ -62,9 +62,17 @@ typedef struct _stroke {
// the swap bit indicates that 1st and 2nd data byte are to be swapped in
// mod translations
uint8_t swap;
// the change bit indicates that the message should only be output if its
// value has changed since the last time
uint8_t change;
// cached values for the change bit
int d, v;
// the recursive bit indicates a MIDI message which is to be translated
// recursively
uint8_t recursive;
// the feedback bit indicates a MIDI message which is to be sent back to
// device or application (flipping the output port)
uint8_t feedback;
// the dirty bit indicates a MIDI event for which a release event still
// needs to be generated in key events
uint8_t dirty;
@ -80,32 +88,39 @@ typedef struct _stroke_data {
// incr flag (CC only)
uint8_t is_incr;
// modulus
uint8_t mod;
uint16_t mod;
// anyshift flag (default rule)
uint8_t anyshift;
} stroke_data;
#define N_SHIFTS 4 // number of distinct shift states
#define N_ST (N_SHIFTS+1)
typedef struct _translation {
struct _translation *next;
char *name;
int is_default;
int mode, is_default;
regex_t regex;
uint8_t portno;
// these are indexed by shift status
stroke_data *note[2];
stroke_data *notes[2];
stroke_data *pc[2];
stroke_data *cc[2];
stroke_data *ccs[2];
stroke_data *pb[2];
stroke_data *pbs[2];
stroke_data *kp[2];
stroke_data *kps[2];
stroke_data *cp[2];
stroke_data *cps[2];
stroke_data *note[N_ST];
stroke_data *notes[N_ST];
stroke_data *pc[N_ST];
stroke_data *cc[N_ST];
stroke_data *ccs[N_ST];
stroke_data *pb[N_ST];
stroke_data *pbs[N_ST];
stroke_data *kp[N_ST];
stroke_data *kps[N_ST];
stroke_data *cp[N_ST];
stroke_data *cps[N_ST];
// actual and allocated sizes (can be at most 16*128)
uint16_t n_note[2], n_notes[2], n_pc[2], n_cc[2], n_ccs[2],
n_pb[2], n_pbs[2], n_kp[2], n_kps[2], n_cp[2], n_cps[2];
uint16_t a_note[2], a_notes[2], a_pc[2], a_cc[2], a_ccs[2],
a_pb[2], a_pbs[2], a_kp[2], a_kps[2], a_cp[2], a_cps[2];
uint16_t n_note[N_ST], n_notes[N_ST], n_pc[N_ST],
n_cc[N_ST], n_ccs[N_ST], n_pb[N_ST], n_pbs[N_ST],
n_kp[N_ST], n_kps[N_ST], n_cp[N_ST], n_cps[N_ST];
uint16_t a_note[N_ST], a_notes[N_ST], a_pc[N_ST],
a_cc[N_ST], a_ccs[N_ST], a_pb[N_ST], a_pbs[N_ST],
a_kp[N_ST], a_kps[N_ST], a_cp[N_ST], a_cps[N_ST];
} translation;
extern void reload_callback(void);
@ -119,6 +134,7 @@ extern int debug_regex, debug_strokes, debug_keys, debug_midi;
extern int default_debug_regex, default_debug_strokes, default_debug_keys,
default_debug_midi;
extern char *config_file_name;
extern int jack_num_outputs;
extern int jack_num_outputs, auto_feedback;
extern int passthrough[2], system_passthrough[2];
extern int midi_octave, shift;
extern char *jack_client_name;
extern char *jack_client_name, *jack_in_regex[2], *jack_out_regex[2];

51
midizap.scm Normal file
View File

@ -0,0 +1,51 @@
(define-module (guixpkgs midizap)
#:use-module ((guix licenses) #:prefix license:)
#:use-module (guix build-system gnu)
#:use-module (guix packages)
#:use-module (guix git-download)
#:use-module (gnu packages xorg)
#:use-module (gnu packages audio)
#:use-module (guix gexp))
(define-public midizap
(package
(name "midizap")
(version "0.8")
(source
(local-file "." "midizap" #:recursive? #t)
;; (origin
;; (method git-fetch)
;; (uri (git-reference
;; (url "https://github.com/agraef/midizap.git")
;; (commit version)))
;; (sha256
;; (base32
;; "0y31fnffl31n9lpkiw1dc3q4rnpfnras30n9y2h5j6586dkmgni4")))
)
(arguments
`(#:tests? #f ; no check target
#:make-flags
(list "CC=gcc"
(string-append "DESTDIR=" %output)
"prefix=")
#:phases
(modify-phases %standard-phases
(replace 'configure
(lambda* (#:key inputs outputs #:allow-other-keys)
(substitute* "Makefile"
(("/usr/include/X11/")
(string-append (assoc-ref inputs "xorgproto") "/include/X11/"))
(("-lXtst") "-lXtst -ljack"))
#t)))))
;; (native-inputs `(("pkg-config" ,pkg-config)))
(inputs `(("jack" ,jack-1)
("libx11" ,libx11)
("libxtst" ,libxtst)
("xorgproto" ,xorgproto)))
(build-system gnu-build-system)
(home-page "")
(synopsis "")
(description "")
(license license:gpl2+)))
midizap

File diff suppressed because it is too large Load Diff