Compare commits
111 Commits
Author | SHA1 | Date |
---|---|---|
nixo | 6d5a3b1ef4 | |
nixo | e00ab5e45b | |
Albert Graef | b6068a73c0 | |
Albert Graef | dc626710e6 | |
Albert Graef | 8c622bd085 | |
Albert Graef | e53e3b3140 | |
Albert Graef | 8fef722919 | |
Albert Graef | 24e346c733 | |
Albert Graef | 240d30da7d | |
Albert Graef | 951fa8fb17 | |
Albert Graef | 7954230468 | |
Albert Graef | 357836a383 | |
Albert Graef | c2160b9893 | |
Albert Graef | 7ce7f3c30b | |
Albert Graef | 15b8b574b3 | |
Albert Graef | b39484c0ac | |
Albert Graef | 4947aca50f | |
Albert Graef | 70287a7e9f | |
Albert Graef | 1898fe74cf | |
Albert Graef | 9d55ce9651 | |
Albert Graef | d0477d27d2 | |
Albert Graef | 56ea96686a | |
Albert Graef | 483c2d3105 | |
Albert Graef | 714e9d1a35 | |
Albert Graef | c9e545ff51 | |
Albert Graef | 17d0947051 | |
Albert Graef | cfb56c945f | |
Albert Graef | aee118bb2a | |
Albert Graef | 825f8cf54f | |
Albert Graef | cb90678434 | |
Albert Graef | a90a0c0361 | |
Albert Graef | b8622829c1 | |
Albert Graef | 55746bcd94 | |
Albert Graef | d70be88ad5 | |
Albert Graef | 28a9d2c515 | |
Albert Graef | 9f69dd19a0 | |
Albert Graef | 7397aced4d | |
Albert Graef | 21491e32ba | |
Albert Graef | 0d265f5ebc | |
Albert Graef | df6083ea7a | |
Albert Graef | af1dbd10dc | |
Albert Graef | 6b99bc35d4 | |
Albert Graef | 5c04c47f70 | |
Albert Graef | 0f1cd1c17f | |
Albert Graef | 7c0403d913 | |
Albert Graef | d9c72d6ff1 | |
Albert Graef | 49166bd1fa | |
Albert Graef | f865d731aa | |
Albert Graef | fb411c505a | |
Albert Graef | f30ab39b65 | |
Albert Graef | 5c54198f07 | |
Albert Graef | 14167f763a | |
Albert Graef | 4756432b1b | |
Albert Graef | 32a7c51ab2 | |
Albert Graef | 591680e655 | |
Albert Graef | 66e2bc777b | |
Albert Graef | 3404fd39a4 | |
Albert Graef | 0bccf128d8 | |
Albert Graef | fe3378be4f | |
Albert Graef | 8ce561692e | |
Albert Graef | 5a87c30f66 | |
Albert Graef | 5e284565df | |
Albert Graef | d33034cc58 | |
Albert Graef | 3c1fade294 | |
Albert Graef | 5cd99ff33c | |
Albert Graef | 738c59088e | |
Albert Graef | 1c388b471f | |
Albert Graef | 60329c4fa4 | |
Albert Graef | 888758c87b | |
Albert Graef | 36bbdd037f | |
Albert Graef | b54f5ba2f9 | |
Albert Graef | 1698aded9a | |
Albert Graef | a81a0f87da | |
Albert Graef | f3efca83a3 | |
Albert Graef | 2e53dc693a | |
Albert Graef | b7701e496b | |
Albert Graef | 4ceef9cbd9 | |
Albert Graef | 83c983e3e4 | |
Albert Graef | f2aedd6267 | |
Albert Graef | 7c9147b4d4 | |
Albert Graef | 1459e91cf5 | |
Albert Graef | fd883c1391 | |
Albert Graef | 5baa93cb91 | |
Albert Graef | 14ba4da728 | |
Albert Graef | 3e47533617 | |
Albert Graef | 58c13233b4 | |
Albert Graef | 81da80c3a5 | |
Albert Graef | 77aaa0de68 | |
Albert Graef | 6c4dbb03cf | |
Albert Graef | be5094bda7 | |
Albert Graef | 156f2db614 | |
Albert Graef | 8f9d1a4ae8 | |
Albert Graef | b56cd35b80 | |
Albert Graef | 803dd48b6e | |
Albert Graef | 5d8dae58dd | |
Albert Graef | a11da5c9ec | |
Albert Graef | 94436fc266 | |
Albert Graef | 7fbae6adb8 | |
Albert Graef | fbbfabb41d | |
Albert Graef | 7110181ab6 | |
Albert Graef | 3d33784dd0 | |
Albert Graef | 689c12defe | |
Albert Graef | 4905e00b97 | |
Albert Graef | 22ff7683d2 | |
Albert Graef | 029590bdac | |
Albert Graef | b3b74f7579 | |
Albert Graef | 3ae0854f88 | |
Albert Graef | 2bb1113fac | |
Albert Graef | ab8b31c9aa | |
Albert Graef | 84a9109e19 | |
Albert Graef | 18cf14b888 |
22
Makefile
22
Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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}'
|
|
@ -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}'
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
193
jackdriver.c
193
jackdriver.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
13
jackdriver.h
13
jackdriver.h
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/^\#define/!d
|
||||
s/^\#define //
|
||||
s/^\([^[:space:]]*\).*$/"\1"/
|
|
@ -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
|
|
@ -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]
|
|
@ -0,0 +1,5 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: section header
|
||||
# key: [
|
||||
# --
|
||||
[${1:name}] ${2:regex}
|
403
midizap.c
403
midizap.c
|
@ -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);
|
||||
|
|
54
midizap.h
54
midizap.h
|
@ -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];
|
||||
|
|
|
@ -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
|
685
readconfig.c
685
readconfig.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue