├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── example.midizaprc
├── examples
├── APCmini.midizaprc
├── MP100.midizaprc
├── MPKmini2.midizaprc
├── Maschine.midizaprc
├── README.md
├── XTouchMini+.midizaprc
├── XTouchMini.midizaprc
├── XTouchONE.midizaprc
├── nanoKONTROL2.midizaprc
└── x-touch-one.device
├── jackdriver.c
├── jackdriver.h
├── keys.sed
├── keywords.sed
├── midizap-mode.el.in
├── midizap-mode
├── midizaprc
└── section
├── midizap.1
├── midizap.c
├── midizap.h
└── readconfig.c
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | keys.h
3 | midizap
4 |
5 | *.o
6 |
7 | *~
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | #CFLAGS=-g -W -Wall
3 | CFLAGS=-O3 -W -Wall
4 |
5 | prefix=/usr/local
6 | bindir=$(DESTDIR)$(prefix)/bin
7 | mandir=$(DESTDIR)$(prefix)/share/man/man1
8 | datadir=$(DESTDIR)/etc
9 |
10 | # See whether emacs is installed and try to guess its installation prefix.
11 | emacs_prefix = $(patsubst %/bin/emacs,%,$(shell which emacs 2>/dev/null))
12 | ifneq ($(strip $(emacs_prefix)),)
13 | elispdir = $(emacs_prefix)/share/emacs/site-lisp
14 | endif
15 |
16 | # Check to see whether we have Jack installed. Needs pkg-config.
17 | JACK := $(shell pkg-config --libs jack 2>/dev/null)
18 |
19 | OBJ = readconfig.o midizap.o jackdriver.o
20 |
21 | # Only try to install the manual page if it's actually there, to prevent
22 | # errors if pandoc isn't installed.
23 | INSTALL_TARGETS = midizap $(wildcard midizap.1)
24 |
25 | .PHONY: all world install uninstall man pdf clean realclean
26 |
27 | all: midizap midizap-mode.el
28 |
29 | # This also creates the manual page (see below).
30 | world: all man
31 |
32 | install: all
33 | install -d $(bindir) $(datadir) $(mandir)
34 | install midizap $(bindir)
35 | install -m 0644 example.midizaprc $(datadir)/midizaprc
36 | ifneq ($(elispdir),)
37 | # If emacs was found, or elispdir was specified manually, install
38 | # midizap-mode.el into the elispdir directory.
39 | install -d $(DESTDIR)$(elispdir)
40 | install -m 0644 midizap-mode.el $(DESTDIR)$(elispdir)
41 | endif
42 | # If present, the manual page will be installed along with the program.
43 | ifneq ($(findstring midizap.1, $(INSTALL_TARGETS)),)
44 | install -m 0644 midizap.1 $(mandir)
45 | else
46 | @echo "Manual page not found, create it with 'make man'."
47 | endif
48 |
49 | uninstall:
50 | rm -f $(bindir)/midizap $(mandir)/midizap.1 $(datadir)/midizaprc
51 |
52 | midizap: $(OBJ)
53 | gcc $(CFLAGS) $(OBJ) -o midizap -L /usr/X11R6/lib -lX11 -lXtst $(JACK)
54 |
55 | # This creates the manual page from the README. Requires pandoc
56 | # (http://pandoc.org/).
57 | man: midizap.1
58 |
59 | # Manual page in pdf format. This also needs groff.
60 | pdf: midizap.pdf
61 |
62 | midizap.1: README.md
63 | pandoc -s -tman $< > $@
64 |
65 | midizap.pdf: midizap.1
66 | # This assumes that man does the right thing when given a file instead of a
67 | # program name, and that it understands groff's -T option.
68 | man -Tpdf ./midizap.1 > $@
69 |
70 | clean:
71 | rm -f midizap keys.h keys.el midizap-mode.el $(OBJ)
72 |
73 | realclean:
74 | rm -f midizap midizap.1 midizap.pdf keys.h $(OBJ)
75 |
76 | keys.h: keys.sed /usr/include/X11/keysymdef.h
77 | sed -f keys.sed < /usr/include/X11/keysymdef.h > keys.h
78 |
79 | keys.el: keywords.sed /usr/include/X11/keysymdef.h
80 | sed -f keywords.sed < /usr/include/X11/keysymdef.h | tr '\n' ' ' > keys.el
81 |
82 | midizap-mode.el: midizap-mode.el.in keys.el
83 | sed '/;; keysyms/r keys.el' < midizap-mode.el.in > midizap-mode.el
84 |
85 | readconfig.o: midizap.h keys.h
86 | midizap.o: midizap.h jackdriver.h
87 | jackdriver.o: jackdriver.h
88 |
--------------------------------------------------------------------------------
/example.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
3 | # Copyright 2018 Albert Graef
4 | #
5 | # Lines in this file starting with # are comments.
6 |
7 | # This program works pretty much like Eric Messick's shuttlepro program,
8 | # but it translates MIDI input rather than input events from the Contour
9 | # Design Shuttle devices. By default, the program creates a Jack MIDI
10 | # client named "midizap" with a single input port, which you'll have to
11 | # connect to the MIDI controller that you want to use (e.g., using a
12 | # patchbay program like qjackctl; non-Jack ALSA MIDI inputs can be
13 | # accommodated using a2jmidid).
14 |
15 | # Both the Jack client name and the number of (input and output) ports
16 | # can be adjusted, either from the command line, using the -j and -o
17 | # options (these always take priority), or by employing the following
18 | # midizaprc directives. NOTE: These options only take effect
19 | # immediately after program start, when the Jack client is initialized.
20 | # If you edit them later, you need to restart the program, so that a new
21 | # Jack client is created.
22 |
23 | # The JACK_NAME directive is used to change the client name.
24 | # (Uncomment the line and edit the name as needed.) This is useful,
25 | # e.g., if you're running multiple instances of midizap using different
26 | # configurations for different controllers, and you want to have them
27 | # named appropriately so that they can be wired up more easily using the
28 | # qjackctl patchbay.
29 |
30 | #JACK_NAME "midizap"
31 |
32 | # The number of ports given with the JACK_PORTS directive must be
33 | # 1 or 2. It causes the given number of both input and output ports to
34 | # be created. This option is useful if you want to translate MIDI
35 | # messages, see the [MIDI] section below for details. Two input and
36 | # output ports can be employed, e.g., if you also need to provide
37 | # backward translations for controller feedback, see the [MIDI2] section
38 | # below for an example.
39 |
40 | #JACK_PORTS 2
41 |
42 | # Other than the input being MIDI instead of the Shuttle's key and wheel
43 | # events, the program works like Eric Messick's original. Each section
44 | # in the file (starting with a name in brackets and a regex to be
45 | # matched against the window class and name) specifies the bindings for
46 | # one application. A section at the end without regex provides default
47 | # bindings if none of the other sections are matched. Within each
48 | # section, bindings are introduced with the name of the MIDI message
49 | # being assigned, followed by a sequence of X KeySyms and/or MIDI
50 | # messages to be output when the MIDI message is received.
51 |
52 | # Here is a brief rundown of the supported notation for MIDI messages
53 | # (please check the documentation for details).
54 |
55 | # CC<0..127>: control change message for the given controller
56 | # PC<0..127>: program change message
57 | # PB: pitch bend message
58 | # CP: channel pressure
59 | # KP:: key pressure (aftertouch)
60 | # <#b>: MIDI note (on or off)
61 |
62 | # Note messages are specified using the customary notation (note name
63 | # A..G, optionally followed by an accidental, # or b, followed by a MIDI
64 | # octave number). The same notation is also used with aftertouch (KP)
65 | # messages, which always apply to a specific note (in contrast, channel
66 | # pressure (CP) always applies to all notes on a single MIDI channel).
67 | # Enharmonic spellings are equivalent, so, e.g., D#5 and Eb5 denote
68 | # exactly the same MIDI note. All MIDI octaves start at the note C, so
69 | # B0 comes before C1. By default, octave numbers are zero-based, so C0
70 | # is MIDI note 0, C5 denotes middle C, A5 is the chamber pitch, etc.
71 | # However, you can adjust this to your liking by specifying the offset
72 | # of the lowest MIDI octave. Two of the most common alternatives are
73 | # listed below (uncomment one of the following lines to use these):
74 |
75 | #MIDI_OCTAVE -1 # ASA (Acoustical Society of America; middle C is C4)
76 | #MIDI_OCTAVE -2 # alternate MIDI (various manufacturers; middle C is C3)
77 |
78 | # The program distinguishes between messages on different MIDI channels.
79 | # By default, messages are assumed to be on MIDI channel 1, but the MIDI
80 | # channel can be specified explicitly following a dash at the end of the
81 | # message token. E.g., a message on MIDI channel 10 would be denoted
82 | # CC7-10 or C#3-10.
83 |
84 | # Each of these messages can be either "on" or "off", and so they can
85 | # have different "press" and "release" keystrokes associated with them.
86 | # In addition, all messages except PC (which doesn't have a data value)
87 | # can also have their value changes translated, in which case they have
88 | # associated key bindings which are executed each time the value
89 | # increases or decreases, respectively. Such bindings are indicated
90 | # with the suffixes "+" and "-". You can also use the "=" suffix to
91 | # indicate that the same translation should be applied to both increases
92 | # and decreases of the controller or pitch bend value. Thus, e.g., CC7=
93 | # indicates that the same translation applies for both CC7+ and CC7-.
94 | # This is most commonly used with pure MIDI -> MIDI translations.
95 |
96 | # There is also another special mode for these incremental bindings,
97 | # incremental "bit-sign" mode. The suffixes "<", ">" and "~" can be
98 | # used in lieu of "+", "-" and "=" with the CC token to properly
99 | # interpret the control values of endless rotary encoders and jog wheels
100 | # on Mackie-like devices. These encoders send values < 64 for
101 | # increases, and > 64 for decreases, where the first 6 bits of the value
102 | # denote the actual amount of change relative to the current value.
103 |
104 | # Debugging options: You want to run the program in a terminal window to
105 | # see its output when using these. The following line, when
106 | # uncommented, prints the section recognized for the window in focus:
107 |
108 | #DEBUG_REGEX
109 |
110 | # This option prints the contents of the entire configuration file, as
111 | # parsed by the program, in a human-readable format:
112 |
113 | #DEBUG_STROKES
114 |
115 | # You can also use the following option to have the recognized
116 | # translations printed out as the program executes them, in the same
117 | # format as DEBUG_STROKES:
118 |
119 | #DEBUG_KEYS
120 |
121 | # Finally, the following option prints all MIDI input (with the input
122 | # port number in the first, and the actual data value in the last
123 | # column). This is useful as a simple MIDI monitor, especially if you
124 | # want to figure out which tokens to use in your translations.
125 |
126 | #DEBUG_MIDI
127 |
128 | # NOTE: The debugging options can also be specified on the command line
129 | # using -d in conjunction with any of the letters r, s, k and m (or the
130 | # letter j if you also want debugging output from Jack). Just -d
131 | # without any option letter turns on all debugging options.
132 |
133 |
134 | # Sample bindings for video editing. These assume a Mackie-compatible
135 | # device, which are available from various manufacturers. They are more
136 | # or less standardized, and offer an abundance of useful controls,
137 | # making it easier to provide bindings which just work. If you don't
138 | # have one of these lying around, there are inexpensive emulations in
139 | # software (such as the TouchDAW app on Android), or you can just edit
140 | # the rules below to make them work with your controller.
141 |
142 | # On most Mackie-like devices there are some playback controls and
143 | # cursor keys which generate various note events, and a jog wheel which
144 | # generates CC60 messages. We put all of these to good use here. Note
145 | # that the CC60 control requires use of the aforementioned special
146 | # incremental mode for endless rotary encoders.
147 |
148 |
149 | # Bindings for the Kdenlive and Shotcut video editors (matched by their
150 | # WM_CLASS). These have very similar key bindings, see e.g.:
151 | # https://www.shotcut.org/howtos/keyboard-shortcuts/
152 |
153 | [Kdenlive/Shotcut] ^(shotcut|kdenlive)$
154 |
155 | # Both Kdenlive and Shotcut use the J-K-L shortcuts, where each
156 | # successive J or L key decrements or increments the playback speed. We
157 | # assign these to the MCU Rewind and Forward controls.
158 |
159 | # playback controls
160 | A#7 XK_space # Play/Pause
161 | A7 "K" # Stop
162 | G7 "J" # Rewind
163 | G#7 "L" # Forward
164 |
165 | # punch in/out (sets in and out points)
166 | # Note that these are labeled drop/replace on some devices. We also
167 | # provide an alternative binding below.
168 | D#7 "I" # Set In
169 | E7 "O" # Set Out
170 |
171 | # up/down cursor movement (alternate binding for set in/out)
172 | C8 "I" # Set In
173 | C#8 "O" # Set Out
174 |
175 | # left/right cursor movement
176 | D8 XK_Home # Beginning
177 | D#8 XK_End # End
178 |
179 | # the jog wheel moves left/right by single frames
180 | CC60< XK_Left # Frame reverse
181 | CC60> XK_Right # Frame forward
182 |
183 |
184 | [MIDI]
185 |
186 | # The special "MIDI" default section is only active when MIDI output is
187 | # enabled (midizap -o). This allows you to use midizap as a MIDI mapper
188 | # translating MIDI input to MIDI output. Here's a simple example for
189 | # illustration purposes, which shows how to map both the Mackie master
190 | # fader and the jog wheel to CC7, so that they can be used as volume
191 | # controls.
192 |
193 | # Note that the master fader is PB (on MIDI channel 9), which has 128
194 | # times the range of a MIDI controller, so we scale it down accordingly
195 | # by specifying a step size of 128.
196 |
197 | PB[128]-9= CC7
198 | CC60~ CC7
199 |
200 | # Drumkit example. The following translations should work on most MIDI
201 | # keyboards. We assume that the keyboard is set to MIDI channel 1 (the
202 | # usual default). The first four white keys (C, D, E and F) in the
203 | # fourth MIDI octave are mapped to the notes of a little drumkit on MIDI
204 | # channel 10, and the volume controller (CC7) is bound to the volume
205 | # controller on the same channel, so that you can change the output
206 | # volume as you play the drumkit. Note that you need a GM-compatible
207 | # software synthesizer such as Fluidsynth/Qsynth to make this work.
208 |
209 | C4 C3-10
210 | D4 C#3-10
211 | E4 D3-10
212 | F4 D#3-10
213 |
214 | CC7= CC7-10
215 |
216 |
217 | [MIDI2]
218 |
219 | # Auxiliary MIDI translations. This is only used when midizap is
220 | # invoked with the -o2 option, so that it creates a second pair of MIDI
221 | # input and output ports. Input for this section only comes from the
222 | # second input port, and output goes to the second output port. This is
223 | # typically used for feedback to controllers featuring motor faders,
224 | # LEDs and the like, in which case the translations are often the
225 | # inverse of what's in the [MIDI] section.
226 |
227 | # Here we only map CC7 back to PB-9 (the MCU master fader). Please
228 | # check examples/APCmini.midizaprc for a more comprehensive example.
229 |
230 | CC7= PB[128]-9
231 |
232 |
233 | # Default section (cursor and mouse emulation)
234 |
235 | [Default]
236 |
237 | # First, some Mackie-compatible bindings.
238 |
239 | # cursor movement
240 | D8 XK_Left
241 | D#8 XK_Right
242 | C8 XK_Up
243 | C#8 XK_Down
244 |
245 | # stop/play/rec are assigned to the left/middle/right mouse buttons
246 | A7 XK_Button_1
247 | A#7 XK_Button_2
248 | B7 XK_Button_3
249 |
250 | # the jog wheel emulates the scroll wheel of the mouse
251 | CC60< XK_Scroll_Up
252 | CC60> XK_Scroll_Down
253 |
254 | # The following bindings should work on any MIDI keyboard. The C, D and
255 | # E keys in the middle octave are bound to the three mouse buttons, and
256 | # the modulation wheel (CC1) emulates the mouse wheel. The F, G, A and
257 | # B keys in the middle octave are mapped to the cursor keys (Left, Up,
258 | # Down, Right).
259 |
260 | C5 XK_Button_1
261 | D5 XK_Button_2
262 | E5 XK_Button_3
263 |
264 | F5 XK_Left
265 | G5 XK_Up
266 | A5 XK_Down
267 | B5 XK_Right
268 |
269 | CC1+ XK_Scroll_Up
270 | CC1- XK_Scroll_Down
271 |
--------------------------------------------------------------------------------
/examples/APCmini.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # Mackie emulation for the AKAI APCmini
3 |
4 | # This turns the APCmini into a Mackie-compatible controller, so that it can
5 | # be used with Linux DAW programs like Ardour. The emulation is complicated
6 | # by the APCmini having no encoders, no motorized faders, and not nearly as
7 | # many dedicated buttons as a Mackie device. But it offers enough controls to
8 | # be usable as a basic DAW controller. Tested with Ardour.
9 |
10 | # Copyright (c) 2018 Albert Graef
11 |
12 | # Copying and distribution of this file, with or without modification, are
13 | # permitted in any medium without royalty provided the copyright notice and
14 | # this notice are preserved. This file is offered as-is, without any
15 | # warranty.
16 |
17 | JACK_NAME "midizap-APCmini"
18 | JACK_PORTS 2
19 |
20 | # SETUP: The following lines will take care of setting up all the connections
21 | # automatically, but you still need to enable the Mackie control surface in
22 | # Ardour, so that Ardour exposes the Mackie control ports.
23 |
24 | JACK_IN1 APC MINI MIDI 1
25 | JACK_OUT1 ardour:mackie control in
26 | JACK_IN2 ardour:mackie control out
27 | JACK_OUT2 APC MINI MIDI 1
28 |
29 | # PROTOCOL DOCUMENTATION: The Mackie Control (MC) protocol is fairly
30 | # ubiquitous, but since manufacturers can't be bothered to properly document
31 | # their stuff these days, we have to rely on volunteers who do their work
32 | # using some reverse engineering. Here are the links that I found most
33 | # useful:
34 |
35 | # This is fairly comprehensive, but lacks the feedback messages:
36 | # http://www.jjlee.com/qlab/Mackie Control MIDI Map.pdf
37 |
38 | # This chart really is a piece of art. It's actually about the Behringer
39 | # X-Touch and its Xctl protocol, but there's useful information about MC in
40 | # there as well:
41 | # http://www.budgetfeatures.com/XctlDOC/Xctl Protocol for X-Touch V1.0.pdf
42 |
43 | # Information about the APCmini can be found in the Akai forums here:
44 | # http://community.akaipro.com/akai_professional/topics/midi-information-for-apc-mini
45 |
46 | [MIDI]
47 |
48 | # The APCmini's dedicated shift key is used to provide alternative functions
49 | # to some of the buttons and the faders.
50 | D8 SHIFT RELEASE SHIFT
51 |
52 | # transport (assigned to the topmost 5 "scene launch" buttons on the right)
53 | A#6 A7 # Stop
54 | B6 A#7 # Play
55 | C7 B7 # Rec
56 | #C7 D7 # Cycle
57 | C#7 G7 # Rew
58 | D7 G#7 # FFwd
59 |
60 | # the next three buttons below are used for the MC shift keys
61 | # NOTE: The MC actually has four shift keys, so one has to go. You may want
62 | # to rearrange these as needed.
63 | D#7 A#5 # Shift
64 | E7 B5 # Control
65 | F7 C6 # Option
66 | #F7 C#6 # Alt/Cmd
67 |
68 | # shifted "scene launch" buttons
69 | # We assign these to the function keys F1..F8 here, but of course you can
70 | # remap these as needed.
71 | ^A#6 F#4
72 | ^B6 G4
73 | ^C7 G#4
74 | ^C#7 A4
75 | ^D7 A#4
76 | ^D#7 B4
77 | ^E7 C5
78 | ^F7 C#5
79 |
80 | # bottom 3x8 grid: mute/solo/rec
81 | # NOTE: Incidentally, these happen to be identical to corresponding MC input.
82 |
83 | # rec (bottom row of the grid)
84 | C0 C0
85 | C#0 C#0
86 | D0 D0
87 | D#0 D#0
88 | E0 E0
89 | F0 F0
90 | F#0 F#0
91 | G0 G0
92 |
93 | # solo (next row above)
94 | G#0 G#0
95 | A0 A0
96 | A#0 A#0
97 | B0 B0
98 | C1 C1
99 | C#1 C#1
100 | D1 D1
101 | D#1 D#1
102 |
103 | # mute (next row above)
104 | E1 E1
105 | F1 F1
106 | F#1 F#1
107 | G1 G1
108 | G#1 G#1
109 | A1 A1
110 | A#1 A#1
111 | B1 B1
112 |
113 | # track select (bottom row right above the faders)
114 | E5 C2
115 | F5 C#2
116 | F#5 D2
117 | G5 D#2
118 | G#5 E2
119 | A5 F2
120 | A#5 F#2
121 | B5 G2
122 |
123 | # shifted bottom row
124 | # We have these assigned to the bank/channel and track/pan/send/plugin
125 | # controls, but you may want to remap some or all of these as needed.
126 | ^E5 A#3 # Bank Left
127 | ^F5 B3 # Bank Right
128 | ^F#5 C4 # Channel Left
129 | ^G5 C#4 # Channel Right
130 | # NOTE: Plugin appears to be unsupported in Ardour.
131 | ^G#5 E3 # Track (Volume)
132 | ^A5 F#3 # Pan
133 | ^A#5 F3 # Send
134 | ^B5 G3 # Plugin (Device)
135 |
136 | # faders (MC uses pitch bends here, use 129 as step size to get full range)
137 | CC48[] PB[129]-1
138 | CC49[] PB[129]-2
139 | CC50[] PB[129]-3
140 | CC51[] PB[129]-4
141 | CC52[] PB[129]-5
142 | CC53[] PB[129]-6
143 | CC54[] PB[129]-7
144 | CC55[] PB[129]-8
145 | # master fader
146 | CC56[] PB[129]-9
147 |
148 | # faders become encoders when shifted (CC16..CC23, incremental mode)
149 | ^CC48= CC16~
150 | ^CC49= CC17~
151 | ^CC50= CC18~
152 | ^CC51= CC19~
153 | ^CC52= CC20~
154 | ^CC53= CC21~
155 | ^CC54= CC22~
156 | ^CC55= CC23~
157 |
158 | # feedback section ########################################################
159 |
160 | [MIDI2]
161 |
162 | # transport (will light up in green)
163 | A7 A#6 $M0 # reset all meters, see "meter feedback" below
164 | A#7 B6
165 | B7 C7[2] # Rec, blinks when engaged
166 | #D7 C7 # Cycle
167 | G7 C#7
168 | G#7 D7
169 |
170 | # Feedback for the MC shift keys. We've disabled this by default since it
171 | # doesn't add much value and clobbers some of the blinkenlights below.
172 |
173 | #A#5 D#7
174 | #B5 E7
175 | #C6 F7
176 |
177 | # Blinkenlights galore! The Mackie protocol provides us with both time and
178 | # meter data for which we provide some translations here which make various
179 | # LEDs light up in different colors.
180 |
181 | # Meter feedback: Each meter value is sent as a channel pressure message (on
182 | # the first MIDI channel) with the mixer channel index 0..7 in the hi- and the
183 | # meter value in the lo-nibble of the velocity value. On a real MCP device
184 | # like the X-Touch, the meters have 7 segments (4 green, 3 orange, and 1 red),
185 | # but the actual range of the meter values seems to be more like 0..13 (at
186 | # least that's what Ardour outputs, YMMV). Note that we only use the upper
187 | # 5x8 part of the grid here, in order not to clobber the three rows at the
188 | # bottom with the rec/solo/mute buttons, so we have to squash the meter values
189 | # into those 5 LEDs per strip in some way. The following setup with 3 green,
190 | # 1 yellow and 1 red LED seems to work reasonably well, but you might want to
191 | # adjust the colors and the mapping of the meter values to your liking.
192 |
193 | CP[16] C2{0,1} G#2{0:3,1} E3{0:6,1} C4{0:9,5} G#4{0:12,3}
194 |
195 | # NOTE: We only report the values as we receive them here, there's no
196 | # automatic decay of the meters like with real Mackie hardware. Thus we
197 | # explicitly reset all meters when transport stops below. (Ardour at least
198 | # does *not* do that automatically.)
199 |
200 | M0[1] $CP{0} $CP{16} $CP{32} $CP{48} $CP{64} $CP{80} $CP{96} $CP{112}
201 |
202 | # This decodes the least significant digit in the time display (CC69) to count
203 | # off time on the 4 bottommost scene launch buttons. Note that the digits are
204 | # encoded in ASCII here, therefore the copious amount of zeros in the value
205 | # lists below to skip over all the non-digit characters at the beginning of
206 | # the ASCII table.
207 |
208 | CC69[] F7{0:49,1,0} E7{0:50,1,0} Eb7{0:51,1,0} D7{0:52,1,0}
209 |
210 | # no feedback for faders (faders aren't motorized)
211 |
212 | # feedback for rec/solo/mute/select
213 |
214 | # rec: color = red (vel. 3)
215 | C0 C0[3]
216 | C#0 C#0[3]
217 | D0 D0[3]
218 | D#0 D#0[3]
219 | E0 E0[3]
220 | F0 F0[3]
221 | F#0 F#0[3]
222 | G0 G0[3]
223 |
224 | # solo: color = green (vel. 1)
225 | G#0 G#0[1]
226 | A0 A0[1]
227 | A#0 A#0[1]
228 | B0 B0[1]
229 | C1 C1[1]
230 | C#1 C#1[1]
231 | D1 D1[1]
232 | D#1 D#1[1]
233 |
234 | # mute: color = yellow (vel. 5)
235 | E1 E1[5]
236 | F1 F1[5]
237 | F#1 F#1[5]
238 | G1 G1[5]
239 | G#1 G#1[5]
240 | A1 A1[5]
241 | A#1 A#1[5]
242 | B1 B1[5]
243 |
244 | # select (will light up in red)
245 | # NOTE: Ardour apparently doesn't update these when changing banks.
246 | C2 E5
247 | C#2 F5
248 | D2 F#5
249 | D#2 G5
250 | E2 G#5
251 | F2 A5
252 | F#2 A#5
253 | G2 B5
254 |
--------------------------------------------------------------------------------
/examples/MP100.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # Minimal Mackie emulation for the Harley Benton MP-100 a.k.a. MeloAudio MIDI
3 | # Commander foot controller (https://meloaudio.com)
4 |
5 | # This device is rather limited in what it can do as a Mackie controller (no
6 | # feedback, just 4 switches, 4 toggles, and 2 continuous controllers), but it
7 | # may still come in handy to guitarists for basic hands-free DAW control. Note
8 | # that we can't make good use of the bank switches on the device (the two
9 | # extra switches on the right), since these always emit a PC message, which
10 | # interferes with our use of the PC messages for the transport controls. It
11 | # may be possible to do something better by configuring a custom mode on the
12 | # device, but here we stick to what's available in the factory settings.
13 |
14 | # Copyright (c) 2019 Albert Graef
15 |
16 | # Copying and distribution of this file, with or without modification, are
17 | # permitted in any medium without royalty provided the copyright notice and
18 | # this notice are preserved. This file is offered as-is, without any
19 | # warranty.
20 |
21 | JACK_NAME "midizap-MP100"
22 | JACK_PORTS 1
23 |
24 | # Auto-connect to the MP-100 on the input, and Ardour's Mackie control input
25 | # on the output side.
26 |
27 | JACK_IN TSMIDI.* MIDI 1
28 | JACK_OUT ardour:mackie control in
29 |
30 | # The following configuration assumes that the MP-100 is set to mode 1 (JAMP),
31 | # which is the default. Note that the controller numbers for the top row and
32 | # the expression pedal inputs are specific to JAMP mode, so you will have to
33 | # adjust these if you run the device in a different host mode. As implemented
34 | # below, the controls are laid out as follows:
35 |
36 | # top row: [mute] [solo] [rec] [select] EXP1: volume (current channel)
37 | # bottom row: [stop] [play] [chan<] [chan>] EXP2: volume (master)
38 |
39 | [MIDI]
40 |
41 | # Note that MCP expects a note-on/off pair for each activation of the
42 | # mure/solo/rec/select switches, while the MP-100 top switches act as toggles,
43 | # so we have to use suitable mod translations for the top row.
44 |
45 | # top row (mute/solo/rec/select for current channel)
46 | CC22[] E1{127} E1{0} # Mute
47 | CC25[] G#0{127} G#0{0} # Solo
48 | CC24[] C0{127} C0{0} # Rec
49 | CC26[] C2{127} C2{0} # Select
50 |
51 | # bottom row (basic transport and bank controls)
52 | PC0 A7 # Stop
53 | PC1 A#7 # Play
54 | PC2 C4 # Channel Left
55 | PC3 C#4 # Channel Right
56 |
57 | # EXP-1 and EXP-2 (current channel and master volume)
58 | CC4[] PB[129]-1 # Volume
59 | CC7[] PB[129]-9 # Master
60 |
--------------------------------------------------------------------------------
/examples/MPKmini2.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # Minimal Mackie emulation for the AKAI MPKmini mkII
3 |
4 | # Copyright (c) 2018 Albert Graef
5 |
6 | # Copying and distribution of this file, with or without modification, are
7 | # permitted in any medium without royalty provided the copyright notice and
8 | # this notice are preserved. This file is offered as-is, without any
9 | # warranty.
10 |
11 | JACK_NAME "midizap-MPKmini2"
12 | JACK_PORTS 1
13 |
14 | # Auto-connect to the MPKMini2 on the input, and Ardour's Mackie control input
15 | # on the output side.
16 |
17 | JACK_IN MPKmini2 MIDI 1
18 | JACK_OUT ardour:mackie control in
19 |
20 | # This configuration assumes that the MPKmini2 is set to factory defaults.
21 | # The device doesn't provide much feedback possibilities, so we don't even
22 | # try. Controls: The joystick can be used as a shuttle control (push to the
23 | # left for rewind, to the right for fast forward). The eight knobs are mapped
24 | # to the channel volume controls. The drum pads are assigned as follows, with
25 | # the transport section on bank A and the cursor and bank controls on bank B:
26 |
27 | # Bank A: Pad 1-4: Stop Play Rec Cycle; Pad 5-8: Rew FFwd Click Marker
28 | # Bank B: Pad 1-4: Up Down Left Right; Pad 5-8: Bank< Bank> Channel< Channel>
29 |
30 | # TODO: No assignments for the pads in CC and PROG CHANGE mode at this time,
31 | # they may be added later when we figure out what to do with them. Also, no
32 | # encoders on the MPKmini2, so no jog wheel. :(
33 |
34 | [MIDI]
35 |
36 | # Pads, Bank A
37 | G#3 A7 # Stop
38 | A3 A#7 # Play
39 | A#3 B7 # Rec
40 | B3 D7 # Cycle
41 | C4 G7 # Rew
42 | C#4 G#7 # FFwd
43 | D4 F7 # Click
44 | D#4 C7 # Marker
45 |
46 | # Pads, Bank B
47 | G#2 C8 # Up
48 | A2 C#8 # Down
49 | A#2 D8 # Left
50 | B2 D#8 # Right
51 | C3 A#3 # Bank Left
52 | C#3 B3 # Bank Right
53 | D3 C4 # Channel Left
54 | D#3 C#4 # Channel Right
55 |
56 | # Joystick (push left/right for Rewind/Fast Forward)
57 | PB[] $M0{0:8192,1,2}?
58 | M0[] $M1{1,-1} $M2{-1:2,1,-1}
59 | M1[] G7[127] # Rew
60 | M2[] G#7[127] # FFwd
61 |
62 | # knobs (MC pitch bends, use 129 as step size to get full range)
63 | CC1[] PB[129]-1
64 | CC2[] PB[129]-2
65 | CC3[] PB[129]-3
66 | CC4[] PB[129]-4
67 | CC5[] PB[129]-5
68 | CC6[] PB[129]-6
69 | CC7[] PB[129]-7
70 | CC8[] PB[129]-8
71 |
--------------------------------------------------------------------------------
/examples/Maschine.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # Mackie emulation for the NI Maschine Mk3
3 |
4 | # Copyright (c) 2018 Albert Graef
5 |
6 | # Copying and distribution of this file, with or without modification, are
7 | # permitted in any medium without royalty provided the copyright notice and
8 | # this notice are preserved. This file is offered as-is, without any
9 | # warranty.
10 |
11 | JACK_NAME "midizap-Maschine"
12 | JACK_PORTS 2
13 | SYSTEM_PASSTHROUGH # pass through MCP feedback
14 |
15 | # automatic connections
16 | JACK_IN1 Ctlra Maschine Mk3
17 | JACK_OUT1 ardour:mackie control in
18 | JACK_IN2 ardour:mackie control out
19 | JACK_OUT2 Ctlra Maschine Mk3
20 |
21 | # NOTE: At present, this controller isn't properly supported by ALSA, but it
22 | # can be made to work in Linux with Harry van Haaren's Ctlra software.
23 | # Specifically, you'll need the ctlra_daemonx program from the
24 | # mapping_v1-daemonx branch which for the time being can be found here:
25 | # https://github.com/agraef/openAV-Ctlra/tree/mapping_v1-daemonx
26 |
27 | # After cloning the repository and switching to the mapping_v1-daemonx
28 | # branch, you can run the following to install the libctlra library along with
29 | # ctlra_daemonx: meson build && cd build && ninja && sudo ninja install
30 |
31 | # Then run ctlra_daemonx -fnm alongside with midizap, and check that the ports
32 | # are connected as follows (the JACK_IN/OUT directives above should take care
33 | # of this automagically): Ctlra Maschine Mk3 -> midizap midi_in / midi_out ->
34 | # Ardour mackie control in / mackie control out -> midizap midi_in2 /
35 | # midi_out2 -> Ctlra Maschine Mk3.
36 |
37 | # Usage (executive summary):
38 |
39 | # - SHIFT, SELECT, SOLO and MUTE are used as shift buttons which change the
40 | # functions of the encoders and some keys
41 |
42 | # - The transport section (bottom/left) mostly does what you'd expect it to.
43 | # PLAY, REC, STOP and RESTART/Loop all work as advertized. The two keys
44 | # above REC and STOP are used as REWIND and FAST FWD.
45 |
46 | # - The eight buttons at the top select a channel, or engage Channel
47 | # Rec/Solo/Mute when combined with SELECT, SOLO and MUTE, respectively.
48 |
49 | # - The eight small encoders function as the volume faders, or as encoders
50 | # (usually pan) when combined with SHIFT. Touching the encoders means
51 | # touching the faders, or pushing the encoders when combined with SELECT.
52 |
53 | # - The big encoder emulates the jog wheel. Pushing this encoder left, right,
54 | # up and down emulates the cursor keys, pressing it engages the Zoom
55 | # function (which also lights up all four LEDs around the encoder).
56 |
57 | # - The touchstrip is used as the master fader. The four buttons above it
58 | # emulate the MCP shift keys (SHIFT, CTRL, OPTION, ALT/CMD).
59 |
60 | # - The left and right arrow keys in the top/left button group are used to
61 | # change banks, or move by single channels when combined with SHIFT.
62 |
63 | # These are just the most important mappings; there's a bunch of other
64 | # bindings, but I'll leave you to discover them on your own (see below for all
65 | # the gory details). There are so many keys on this device, many of them are
66 | # still unused right now, so you can easily add more bindings if you want. :)
67 |
68 | [MIDI]
69 |
70 | # We use the Mk3's dedicated SHIFT button as our primary shift key, to provide
71 | # alternative functions to some of the buttons and the faders.
72 | F2 SHIFT ^F2 RELEASE SHIFT ^F2
73 |
74 | # transport (assigned to the transport section on the bottom left)
75 | F#5 A7 # Stop
76 | E5 A#7 # Play
77 | F5 B7 # Rec
78 | C5 D7 # Cycle (RESTART/Loop key)
79 | C#5 G7 # Rew (ERASE/Replace key)
80 | D5 G#7 # FFwd (TAP/Metro key)
81 | D#5 C#7 # Nudge (FOLLOW/Grid key)
82 |
83 | # additional functions on shifted keys
84 | ^C#5 D#7 # In (SHIFT ERASE/Replace key)
85 | ^F5 E7 # Out (SHIFT Rec key)
86 | ^D5 F7 # Click (SHIFT TAP/Metro key)
87 | ^D#5 C7 # Mark (SHIFT FOLLOW/Grid key)
88 |
89 | # switch between SMPTE and BBT timecode
90 | C#6 F4 # (MIDI/Channel key)
91 |
92 | # Bank/channel left/right (arrow keys in the top left section)
93 | E6 A#3 # Bank Left
94 | A5 B3 # Bank Right
95 | ^E6 C4 # Channel Left
96 | ^A5 C#4 # Channel Right
97 |
98 | # the four buttons below the arrow keys are assigned to the utility functions
99 | F6 G#6 # Save (SAVE/File key)
100 | G#5 A6 # Undo (SETTINGS key)
101 | F#6 A#6 # Cancel (AUTO key)
102 | G5 B6 # Enter (MACRO/Set key)
103 |
104 | # Track/Pan/Send/Plugin (4 buttons right above the grid)
105 | # NOTE: Plugin appears to be unsupported in Ardour.
106 | G#3 E3 # Track (PAD MODE key)
107 | A3 F#3 # Pan (KEYBOARD key)
108 | A#3 F3 # Send (CHORDS key)
109 | B3 G3 # Plugin (STEPS key)
110 |
111 | # the four buttons right above the touchstrip are used for the MC shift keys
112 | A4 A#5 # Shift (PITCH key)
113 | A#4 B5 # Control (MOD key)
114 | B4 C6 # Option (PERFORM key)
115 | D3 C#6 # Alt/Cmd (NOTES key)
116 |
117 | # big encoder press/left/right/up/down is assigned to the zoom/cursor keys
118 | D2 C8 # Up
119 | E2 C#8 # Down
120 | D#2 D8 # Left
121 | C#2 D#8 # Right
122 | C2 E8 # Zoom
123 |
124 | # These can also be shifted, so that you can bind them in the DAW if you
125 | # want. E.g., I have the previous/next marker functions on the shifted cursor
126 | # left/right keys in Ardour.
127 |
128 | ^D2 A#5 C8 # Up
129 | ^E2 A#5 C#8 # Down
130 | ^D#2 A#5 D8 # Left
131 | ^C#2 A#5 D#8 # Right
132 | ^C2 A#5 E8 # Zoom
133 |
134 | # The Mk3 has one row of dedicated "channel buttons" at the top, right above
135 | # the display. Since we'd also like to use these for the channel-based
136 | # rec/solo/mute functions, we combine them with the SELECT, SOLO and MUTE
137 | # buttons as additional shift keys.
138 |
139 | F#4 SHIFT2 ^F#4 RELEASE SHIFT2 ^F#4
140 | G4 SHIFT3 ^G4 RELEASE SHIFT3 ^G4
141 | G#4 SHIFT4 ^G#4 RELEASE SHIFT4 ^G#4
142 |
143 | # top row, unshifted: track select
144 | G6 C2
145 | G#6 C#2
146 | A6 D2
147 | A#6 D#2
148 | B6 E2
149 | C7 F2
150 | C#7 F#2
151 | D7 G2
152 |
153 | # SELECT: rec
154 | 2^G6 C0
155 | 2^G#6 C#0
156 | 2^A6 D0
157 | 2^A#6 D#0
158 | 2^B6 E0
159 | 2^C7 F0
160 | 2^C#7 F#0
161 | 2^D7 G0
162 |
163 | # SOLO: solo
164 | 3^G6 G#0
165 | 3^G#6 A0
166 | 3^A6 A#0
167 | 3^A#6 B0
168 | 3^B6 C1
169 | 3^C7 C#1
170 | 3^C#7 D1
171 | 3^D7 D#1
172 |
173 | # MUTE: mute
174 | 4^G6 E1
175 | 4^G#6 F1
176 | 4^A6 F#1
177 | 4^A#6 G1
178 | 4^B6 G#1
179 | 4^C7 A1
180 | 4^C#7 A#1
181 | 4^D7 B1
182 |
183 | # We also assign these to the function keys F1..F8 when shifted. You may want
184 | # to remap these as needed.
185 | ^G6 F#4
186 | ^G#6 G4
187 | ^A6 G#4
188 | ^A#6 A4
189 | ^B6 A#4
190 | ^C7 B4
191 | ^C#7 C5
192 | ^D7 C#5
193 |
194 | # The grid buttons are passed through unchanged, so that you can still use
195 | # them as drum pads (provided that you filter out all MIDI channels except
196 | # channel 10). Note that we use mod translations here, in order to preserve
197 | # the velocities.
198 |
199 | C3[]-10 C3-10
200 | C#3[]-10 C#3-10
201 | D3[]-10 D3-10
202 | D#3[]-10 D#3-10
203 | E3[]-10 E3-10
204 | F3[]-10 F3-10
205 | F#3[]-10 F#3-10
206 | G3[]-10 G3-10
207 | G#3[]-10 G#3-10
208 | A3[]-10 A3-10
209 | A#3[]-10 A#3-10
210 | B3[]-10 B3-10
211 | C4[]-10 C4-10
212 | C#4[]-10 C#4-10
213 | D4[]-10 D4-10
214 | D#4[]-10 D#4-10
215 |
216 | # Map the A..H buttons to program changes on channel 10 so that you can
217 | # quickly switch the sounds of your drumkit, drum patterns etc. (This is just
218 | # an example, you might want to disable these or remap them as you see fit.)
219 |
220 | F#2 PC0-10
221 | G2 PC1-10
222 | G#2 PC2-10
223 | A2 PC3-10
224 | A#2 PC4-10
225 | B2 PC5-10
226 | C3 PC6-10
227 | C#3 PC7-10
228 |
229 | # big encoder assigned to MCP jog wheel
230 | CC0~ CC60~
231 |
232 | # encoders are mapped to the MCP channel faders
233 | # (MC uses pitch bends here, use 129 as step size to get full range)
234 | CC1~ PB[129]-1
235 | CC2~ PB[129]-2
236 | CC3~ PB[129]-3
237 | CC4~ PB[129]-4
238 | CC5~ PB[129]-5
239 | CC6~ PB[129]-6
240 | CC7~ PB[129]-7
241 | CC8~ PB[129]-8
242 | # master fader (touchstrip)
243 | CC9[] PB[129]-9
244 |
245 | # encoders become the MCP encoders when shifted (CC16..CC23, incremental mode)
246 | ^CC1~ CC16~
247 | ^CC2~ CC17~
248 | ^CC3~ CC18~
249 | ^CC4~ CC19~
250 | ^CC5~ CC20~
251 | ^CC6~ CC21~
252 | ^CC7~ CC22~
253 | ^CC8~ CC23~
254 |
255 | # encoder touches = fader touches
256 | E7 G#8
257 | F7 A8
258 | F#7 A#8
259 | G7 B8
260 | G#7 C9
261 | A7 C#9
262 | A#7 D9
263 | B7 D#9
264 | D#7 E9
265 |
266 | # SELECT + encoder touches = push encoders -- we can't use SHIFT here since
267 | # touches might easily get triggered when the encoders are moved
268 | 2^E7 G#2
269 | 2^F7 A2
270 | 2^F#7 A#2
271 | 2^G7 B2
272 | 2^G#7 C3
273 | 2^A7 C#3
274 | 2^A#7 D3
275 | 2^B7 D#3
276 | 2^D#7 E3
277 |
278 | # feedback section ########################################################
279 |
280 | [MIDI2]
281 |
282 | # transport
283 | A7 F#5 $M0 # reset all meters, see "meter" in the MCP feedback section below
284 | A#7 E5
285 | B7 F5 # Rec
286 | D7 C5 # Cycle
287 | G7 C#5
288 | G#7 D5
289 | C#7 D#5
290 |
291 | ^F7 D5
292 | ^C7 D#5
293 |
294 | # SMPTE/BBT
295 | F4 C#6
296 |
297 | # channel left/right keys
298 | C4 E6
299 | C#4 A5
300 |
301 | # row above grid (track/pan/send/plugin)
302 | E3 G#3
303 | F#3 A3
304 | F3 A#3
305 | G3 B3
306 |
307 | # feedback for the MC shift keys
308 | A#5 A4 # Shift
309 | B5 A#4 # Control
310 | C6 B4 # Option
311 | C#6 D3 # Alt/Cmd
312 |
313 | # zoom (as the big encoder itself has no led, we light up the 4 leds around it
314 | # instead; color: 4 = blue)
315 | E8 C#2[4] D2[4] D#2[4] E2[4]
316 |
317 | # select
318 | # NOTE: Ardour apparently doesn't update these when changing banks.
319 | C2 G6
320 | C#2 G#6
321 | D2 A6
322 | D#2 A#6
323 | E2 B6
324 | F2 C7
325 | F#2 C#7
326 | G2 D7
327 |
328 | # no feedback for encoders, only touchstrip
329 | PB[128]{0}-9 CC9'
330 |
331 | # MCP feedback (simply passed through, ctlra_daemonx handles these
332 | # automagically).
333 |
334 | # rec/solo/mute
335 |
336 | C0[] C0
337 | C#0[] C#0
338 | D0[] D0
339 | D#0[] D#0
340 | E0[] E0
341 | F0[] F0
342 | F#0[] F#0
343 | G0[] G0
344 |
345 | G#0[] G#0
346 | A0[] A0
347 | A#0[] A#0
348 | B0[] B0
349 | C1[] C1
350 | C#1[] C#1
351 | D1[] D1
352 | D#1[] D#1
353 |
354 | E1[] E1
355 | F1[] F1
356 | F#1[] F#1
357 | G1[] G1
358 | G#1[] G#1
359 | A1[] A1
360 | A#1[] A#1
361 | B1[] B1
362 |
363 | # meter values
364 | CP[] CP
365 |
366 | # NOTE: We only report the values as we receive them here, there's no
367 | # automatic decay of the meters like with real Mackie hardware. Thus we
368 | # explicitly reset all meters when transport stops below. (Ardour at least
369 | # does *not* do that automatically.)
370 |
371 | M0[1] CP{0} CP{16} CP{32} CP{48} CP{64} CP{80} CP{96} CP{112}
372 |
373 | # timecode
374 | CC64[] CC64
375 | CC65[] CC65
376 | CC66[] CC66
377 | CC67[] CC67
378 | CC68[] CC68
379 | CC69[] CC69
380 | CC70[] CC70
381 | CC71[] CC71
382 | CC72[] CC72
383 | CC73[] CC73
384 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Examples
3 |
4 | This folder contains a couple of sample midizap configurations for different controllers:
5 |
6 | - [APCmini.midizaprc](APCmini.midizaprc): Mackie emulation for the AKAI APCmini
7 |
8 | - [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)
9 |
10 | - [MPKmini2.midizaprc](MPKmini2.midizaprc): minimal Mackie emulation for the AKAI MPKmini Mk2 keyboard
11 |
12 | - [nanoKONTROL2.midizaprc](nanoKONTROL2.midizaprc): fix up the marker keys of the Korg nanoKONTROL2 in Cubase mode for use in Ardour
13 |
14 | - [XTouchMini.midizaprc](XTouchMini.midizaprc): adds a bunch of Mackie control functions to make the device more useful in MC mode
15 |
16 | - [XTouchONE.midizaprc](XTouchONE.midizaprc): adds a bunch of Mackie control functions to make the device more useful in MC mode
17 |
18 | Other interesting items:
19 |
20 | - [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)
21 |
22 | 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.
23 |
24 | 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.
25 |
--------------------------------------------------------------------------------
/examples/XTouchMini+.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # This is an addon to XTouchMini.midizaprc which adds some extra MIDI CC
3 | # assignments for the encoders and the master fader to the ALT layer.
4 |
5 | # NOTE: This is to be used along with XTouchMini.midizaprc (which see).
6 |
7 | # Copyright (c) 2018 Albert Graef
8 |
9 | # Copying and distribution of this file, with or without modification, are
10 | # permitted in any medium without royalty provided the copyright notice and
11 | # this notice are preserved. This file is offered as-is, without any
12 | # warranty.
13 |
14 | JACK_NAME "midizap-XTouchMini+"
15 | JACK_PORTS 2
16 |
17 | JACK_IN1 X-TOUCH MINI MIDI 1
18 | JACK_OUT1 ardour:MIDI control in
19 | JACK_IN2 ardour:MIDI control out
20 | JACK_OUT2 X-TOUCH MINI MIDI 1
21 |
22 | [MIDI]
23 |
24 | # ALT button (no feedback here, as this is already handled in the
25 | # XTouchMini.midizaprc "mother" configuration)
26 | E7 SHIFT2 RELEASE SHIFT2
27 |
28 | # The encoders and the master fader are assigned to standard MIDI controls
29 | # CC1..CC9 on the ALT layer, so you can assign them freely with Ardour's MIDI
30 | # learn facility, and use the unit as an MC device at the same time.
31 |
32 | # ALT encoders and fader = CC1..CC9, with direct feedback
33 | 2^CC16~ CC1 $M1
34 | 2^CC17~ CC2 $M2
35 | 2^CC18~ CC3 $M3
36 | 2^CC19~ CC4 $M4
37 | 2^CC20~ CC5 $M5
38 | 2^CC21~ CC6 $M6
39 | 2^CC22~ CC7 $M7
40 | 2^CC23~ CC8 $M8
41 |
42 | 2^PB[128]{0}-9 CC9'
43 |
44 | # macros handling direct feedback for CC1..CC8
45 | M1[12]{0} !CC48{33-43}'
46 | M2[12]{0} !CC49{33-43}'
47 | M3[12]{0} !CC50{33-43}'
48 | M4[12]{0} !CC51{33-43}'
49 | M5[12]{0} !CC52{33-43}'
50 | M6[12]{0} !CC53{33-43}'
51 | M7[12]{0} !CC54{33-43}'
52 | M8[12]{0} !CC55{33-43}'
53 |
54 | [MIDI2]
55 |
56 | # feedback for the encoders on the ALT layer (CC1..CC8)
57 | 2^CC1[12]{0} CC48{33-43}'
58 | 2^CC2[12]{0} CC49{33-43}'
59 | 2^CC3[12]{0} CC50{33-43}'
60 | 2^CC4[12]{0} CC51{33-43}'
61 | 2^CC5[12]{0} CC52{33-43}'
62 | 2^CC6[12]{0} CC53{33-43}'
63 | 2^CC7[12]{0} CC54{33-43}'
64 | 2^CC8[12]{0} CC55{33-43}'
65 |
--------------------------------------------------------------------------------
/examples/XTouchMini.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # While the X-Touch Mini is very popular as a Lightroom controller, musicians
3 | # often complain that its Mackie control mode is just too basic. But midizap
4 | # makes it easy to add most of the essential MC functions that are missing, so
5 | # that the unit becomes really usable as a control surface.
6 |
7 | # NOTE: This configuration assumes that the X-Touch Mini is in MC mode, which
8 | # is the case if the MC MODE LED on the right side is lit. If necessary, you
9 | # can switch the device to MC mode by holding the MC key while powering it up.
10 |
11 | # NOTE: There's an addon to this configuration with some MIDI CC bindings on
12 | # the ALT layer, see XTouchMini+.midizaprc.
13 |
14 | # Copyright (c) 2018 Albert Graef
15 |
16 | # Copying and distribution of this file, with or without modification, are
17 | # permitted in any medium without royalty provided the copyright notice and
18 | # this notice are preserved. This file is offered as-is, without any
19 | # warranty.
20 |
21 | JACK_NAME "midizap-XTouchMini"
22 | JACK_PORTS 2
23 |
24 | # Automatic connections for the device and Ardour.
25 | JACK_IN1 X-TOUCH MINI MIDI 1
26 | JACK_OUT1 ardour:mackie control in
27 | JACK_IN2 ardour:mackie control out
28 | JACK_OUT2 X-TOUCH MINI MIDI 1
29 |
30 | # Pass everything through, except for the translations below.
31 |
32 | PASSTHROUGH
33 | SYSTEM_PASSTHROUGH
34 |
35 | # The idea of this mapping is to leave the original bindings mostly untouched.
36 | # In order to accommodate the missing MC functions, we add two shifted layers.
37 | # To access these layers, we reassign the first two keys in the bottom row as
38 | # internal shift keys, denoted SHIFT and ALT in the following.
39 |
40 | # Note that the device, besides the encoders and the master fader, has two
41 | # rows of 8 buttons and 2 buttons on the right which are laid out in MC mode
42 | # as follows:
43 |
44 | # [CLICK] [SOLO] [TRACK] [SEND] [PAN] [PLUGIN] [EQ] [INST] [MARKER/A]
45 | # [DROP/MC] [REPLACE] [REW] [FFWD] [LOOP] [STOP] [PLAY] [REC] [NUDGE/B]
46 |
47 | # This midizap configuration remaps them as follows:
48 |
49 | # [CLICK] [SOLO] [TRACK] [SEND] [PAN] [PLUGIN] [EQ] [INST] [BANK LEFT]
50 | # [SHIFT] [ALT] [REW] [FFWD] [LOOP] [STOP] [PLAY] [REC] [BANK RIGHT]
51 |
52 | # Note the two shift keys (SHIFT and ALT) in the lower button row, and the
53 | # bank switch keys on the right. Other than that, the unshifted layer is
54 | # unchanged. On the SHIFT layer, the bindings are as follows:
55 |
56 | # [SELECT] ... [CHAN LEFT]
57 | # [DROP] [REPLACE] [FLIP] [MARKER] [NUDGE] [CHAN RIGHT]
58 |
59 | # Note that the remapped functions (DROP, REPLACE, MARKER, NUDGE) are now
60 | # available as shifted keys in the bottom row. We also added the FLIP key
61 | # there. The shifted keys in the top row are bound to the channel SELECT
62 | # functions. Moreover, our translations for the SHIFT layer also assign the
63 | # master fader to the first channel, like on the X-Touch ONE, and the encoders
64 | # become the eight channel faders.
65 |
66 | # Finally, the ALT layer. Since Ardour requires some of the keys to be
67 | # combined with the MC SHIFT key to get at some functions (in particular,
68 | # SHIFT + SELECT/MARKER/NUDGE), we provide those shifted bindings on this
69 | # layer. We also throw in some more MC keys (cursor and zoom):
70 |
71 | # [^SELECT] ... [UP]
72 | # [LEFT] [RIGHT] [ZOOM] [^MARKER] [^NUDGE] [DOWN]
73 |
74 | # Note that the way I've configured this layer is somewhat tailored to Ardour,
75 | # so you may want to adjust it to your liking. The SHIFT layer, in contrast,
76 | # should be pretty generic, and work fine as is with most DAWs.
77 |
78 | [MIDI]
79 |
80 | # Note that, as explained in the midizap manual, you can change the SHIFT and
81 | # ALT keys to CapsLock-style keys by removing the RELEASE part of the
82 | # sequence. They will then work as toggles. With the RELEASE sequence, they
83 | # work as momentary switches, i.e., you need to hold them down to invoke a
84 | # shifted function.
85 |
86 | # MC = SHIFT button, with direct feedback
87 | D#7 SHIFT ^D#7 RELEASE SHIFT ^D#7
88 |
89 | # ALT button
90 | E7 SHIFT2 ^E7 RELEASE SHIFT2 ^E7
91 |
92 | # A/B buttons on the right = bank left/right
93 | C7 A#3 # bank left
94 | C#7 B3 # bank right
95 |
96 | # shifted A/B buttons = channel left/right
97 | ^C7 C4 # channel left
98 | ^C#7 C#4 # channel right
99 |
100 | # other shifted buttons
101 | ^G7 D#7 # REW = DROP
102 | ^G#7 E7 # FFWD = REPLACE
103 | ^D7 D4 # LOOP = FLIP
104 | ^A7 C7 # STOP = MARKER
105 | ^A#7 C#7 # PLAY = NUDGE
106 | # ^B7 # REC, currently unassigned
107 |
108 | # shifted top row = SELECT
109 | ^F7 C2
110 | ^F#7 C#2
111 | ^E3 D2
112 | ^F3 D#2
113 | ^F#3 E2
114 | ^G3 F2
115 | ^G#3 F#2
116 | ^A3 G2
117 |
118 | # shifted encoders = channel faders
119 | ^CC16~ PB[129]-1
120 | ^CC17~ PB[129]-2
121 | ^CC18~ PB[129]-3
122 | ^CC19~ PB[129]-4
123 | ^CC20~ PB[129]-5
124 | ^CC21~ PB[129]-6
125 | ^CC22~ PB[129]-7
126 | ^CC23~ PB[129]-8
127 |
128 | # fader goes to first channel in bank when shifted
129 | ^PB[]-9 PB-1
130 |
131 | # ALT layer. This uses the same trick (the explicit RELEASE sequence) as in
132 | # XTouchONE.midizaprc to transmit the MC SHIFT key (A#5) in the right order.
133 |
134 | # ALT top row = SHIFT+SELECT
135 | 2^F7 A#5 C2 RELEASE C2 A#5
136 | 2^F#7 A#5 C#2 RELEASE C#2 A#5
137 | 2^E3 A#5 D2 RELEASE D2 A#5
138 | 2^F3 A#5 D#2 RELEASE D#2 A#5
139 | 2^F#3 A#5 E2 RELEASE E2 A#5
140 | 2^G3 A#5 F2 RELEASE F2 A#5
141 | 2^G#3 A#5 F#2 RELEASE F#2 A#5
142 | 2^A3 A#5 G2 RELEASE G2 A#5
143 |
144 | 2^A7 A#5 C7 RELEASE C7 A#5 # ALT+STOP = SHIFT+MARKER
145 | 2^A#7 A#5 C#7 RELEASE C#7 A#5 # ALT+PLAY = SHIFT+NUDGE
146 |
147 | 2^G7 D8 # ALT+REW = LEFT
148 | 2^G#7 D#8 # ALT+FFWD = RIGHT
149 | 2^D7 E8 # ALT+LOOP = ZOOM
150 | 2^C7 C8 # ALT+A = UP
151 | 2^C#7 C#8 # ALT+B = DOWN
152 |
153 | # Encoders and fader are disabled on the ALT layer, to accommodate the MIDI CC
154 | # bindings in XTouchMini+.midizaprc.
155 |
156 | 2^CC16~ NOP
157 | 2^CC17~ NOP
158 | 2^CC18~ NOP
159 | 2^CC19~ NOP
160 | 2^CC20~ NOP
161 | 2^CC21~ NOP
162 | 2^CC22~ NOP
163 | 2^CC23~ NOP
164 |
165 | 2^PB[]-9 NOP
166 |
167 | [MIDI2]
168 |
169 | # feedback for the BANK LEFT/RIGHT buttons
170 | A#3 C7
171 | B3 C#7
172 |
173 | # We also provide feedback for the *faders* here, so that the current values
174 | # are shown on the LED rings of the encoders while the faders are being
175 | # operated. Note that the fader values are shown in "fan" mode (arc from zero
176 | # to the current value) so that they're distinguishable from pan values which
177 | # will be shown in "pan" mode (single tick indicating left/right position).
178 |
179 | # NOTE: This is still experimental. Overloading the encoders in this manner is
180 | # convenient and seems to work reasonably well in Ardour at least, but having
181 | # some encoders display pan and others volume may be confusing at times.
182 |
183 | PB[1536]{0}-1 CC48{33-43}'
184 | PB[1536]{0}-2 CC49{33-43}'
185 | PB[1536]{0}-3 CC50{33-43}'
186 | PB[1536]{0}-4 CC51{33-43}'
187 | PB[1536]{0}-5 CC52{33-43}'
188 | PB[1536]{0}-6 CC53{33-43}'
189 | PB[1536]{0}-7 CC54{33-43}'
190 | PB[1536]{0}-8 CC55{33-43}'
191 |
--------------------------------------------------------------------------------
/examples/XTouchONE.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # The X-Touch ONE is a very capable Mackie device as it is, but it can still
3 | # be improved a bit with some midizap magic. Most notably, the device lacks a
4 | # SHIFT key, so we remap one of the lesser-used keys to provide an extra
5 | # shifted layer which can be assigned freely to additional MC functions which
6 | # aren't readily available on the device.
7 |
8 | # NOTE: This config assumes that the X-Touch ONE is in its default Mackie
9 | # mode. Otherwise you will have to adjust the mapping accordingly.
10 |
11 | # Copyright (c) 2018 Albert Graef
12 |
13 | # Copying and distribution of this file, with or without modification, are
14 | # permitted in any medium without royalty provided the copyright notice and
15 | # this notice are preserved. This file is offered as-is, without any
16 | # warranty.
17 |
18 | JACK_NAME "midizap-XTouchONE"
19 | JACK_PORTS 2
20 |
21 | # Automatic connections for the device and Ardour.
22 | JACK_IN1 X-Touch One MIDI 1
23 | JACK_OUT1 ardour:mackie control in
24 | JACK_IN2 ardour:mackie control out
25 | JACK_OUT2 X-Touch One MIDI 1
26 |
27 | # Pass everything through, except for the mappings below.
28 | PASSTHROUGH
29 | SYSTEM_PASSTHROUGH
30 |
31 | [MIDI]
32 |
33 | # I use the SCRUB key (F8 in MC mode) as the SHIFT key, because Ardour doesn't
34 | # need it. YMMV, though, so if you need that key, you'll have to substitute
35 | # another one that you can spare in the rule below.
36 |
37 | F8 SHIFT ^F8 RELEASE SHIFT ^F8
38 |
39 | # Remap the shifted F1 .. F4 keys to do something useful. I have them bound to
40 | # the most important encoder assignment keys here, but of course you can
41 | # change them to anything you want.
42 |
43 | ^F#4 E3 # Track (Trim in Ardour)
44 | ^G4 F#3 # Pan
45 | ^G#4 F3 # Send
46 | ^A4 G3 # Plugin (not supported by Ardour)
47 |
48 | # Remap the shifted F5 and F6 keys to F7 and F8 which are missing on the ONE.
49 |
50 | ^A#4 C5
51 | ^B4 C#5
52 |
53 | # If you're using the encoder assignment keys, then most likely you also want
54 | # to have a FLIP button; we (rather arbitrarily) assign it to the shifted SOLO
55 | # button in the TRANSPORT section.
56 |
57 | ^F#7 D4 # Flip
58 |
59 | # Since the X-Touch ONE is a single-channel controller, I like to have the
60 | # bank and channel keys linked up with channel SELECT, so that the current
61 | # channel is also selected in the DAW when switching banks. Therefore I have
62 | # bound these functions to the shifted bank and channel keys here, but of
63 | # course you can change them to anything you want.
64 |
65 | ^A#3 A#3 C2 # BANK< SELECT
66 | ^B3 B3 C2 # BANK> SELECT
67 | ^C4 C4 C2 # CHAN< SELECT
68 | ^C#4 C#4 C2 # CHAN> SELECT
69 |
70 | # With the translations below, the other shifted buttons are simply passed
71 | # through along with the MC SHIFT key (A#5). You can either assign these
72 | # combinations in the DAW (e.g., I have the shifted left/right keys bound to
73 | # the previous/next marker functions in Ardour), or just directly edit the
74 | # bindings below as you see fit.
75 |
76 | # NOTE: We use an explicit RELEASE sequence here, to prevent the MC SHIFT key
77 | # from being released too early. (Note that by default, midizap will release
78 | # MIDI key events in the same order in which they are pressed, which may not
79 | # work in some key combinations. At least I found that this confuses Ardour in
80 | # some cases.)
81 |
82 | ^C8 A#5 C8 RELEASE C8 A#5 # Up
83 | ^C#8 A#5 C#8 RELEASE C#8 A#5 # Down
84 | ^D8 A#5 D8 RELEASE D8 A#5 # Left
85 | ^D#8 A#5 D#8 RELEASE D#8 A#5 # Right
86 | ^E8 A#5 E8 RELEASE E8 A#5 # Zoom
87 |
88 | ^C0 A#5 C0 RELEASE C0 A#5 # SHIFT REC
89 | ^G#0 A#5 G#0 RELEASE G#0 A#5 # SHIFT SOLO
90 | ^E1 A#5 E1 RELEASE E1 A#5 # SHIFT MUTE
91 | ^C2 A#5 C2 RELEASE C2 A#5 # SHIFT SELECT
92 |
93 | ^C7 A#5 C7 RELEASE C7 A#5 # SHIFT MARKER
94 | ^C#7 A#5 C#7 RELEASE C#7 A#5 # SHIFT NUDGE
95 | ^D7 A#5 D7 RELEASE D7 A#5 # SHIFT CYCLE
96 | ^D#7 A#5 D#7 RELEASE D#7 A#5 # SHIFT DROP
97 | ^E7 A#5 E7 RELEASE E7 A#5 # SHIFT REPLACE
98 | ^F7 A#5 F7 RELEASE F7 A#5 # SHIFT CLICK
99 |
100 | ^G7 A#5 G7 RELEASE G7 A#5 # SHIFT REW
101 | ^G#7 A#5 G#7 RELEASE G#7 A#5 # SHIFT FFWD
102 | ^A7 A#5 A7 RELEASE A7 A#5 # SHIFT STOP
103 | ^A#7 A#5 A#7 RELEASE A#7 A#5 # SHIFT PLAY
104 | ^B7 A#5 B7 RELEASE B7 A#5 # SHIFT REC
105 |
106 | [MIDI2]
107 |
108 | # feedback for the (shifted) F1 .. F6 bindings and the remapped SOLO key
109 | E3 F#4
110 | F#3 G4
111 | F3 G#4
112 | A3 A4
113 | C5 A#4
114 | C#5 B4
115 | D4 F#7 # FLIP -> SOLO
116 |
--------------------------------------------------------------------------------
/examples/nanoKONTROL2.midizaprc:
--------------------------------------------------------------------------------
1 |
2 | # The nanoKONTROL2 has no plain Mackie emulation. Its Cubase mode comes close,
3 | # but has the MARKER keys set up in a Cubase-specific way. This config patches
4 | # them up a bit so that they do something useful on Linux, at least in Ardour.
5 |
6 | # Copyright (c) 2018 Albert Graef
7 |
8 | # Copying and distribution of this file, with or without modification, are
9 | # permitted in any medium without royalty provided the copyright notice and
10 | # this notice are preserved. This file is offered as-is, without any
11 | # warranty.
12 |
13 | JACK_NAME "midizap-nanoKONTROL2"
14 | JACK_PORTS 2
15 |
16 | # automatic connections
17 | JACK_IN1 nanoKONTROL2 MIDI 1
18 | JACK_OUT1 ardour:mackie control in
19 | JACK_IN2 ardour:mackie control out
20 | JACK_OUT2 nanoKONTROL2 MIDI 1
21 |
22 | # Pass everything through (including feedback), except for the mappings below.
23 | PASSTHROUGH
24 |
25 | # NOTE: This assumes that you run the nanoKONTROL2 in *Cubase* mode. Use
26 | # Korg's editor application to enter that mode, or hold the SET and << keys
27 | # when plugging in the device. (Note that Ardour also has support for the
28 | # nanoKONTROL2 as a generic MIDI device, in which case you can leave the
29 | # device in its default CC mode and midizap isn't required.)
30 |
31 | [Ardour] ^ardour_ardour$
32 |
33 | # AFAICT, the "Prev/Next Marker" functions have no dedicated keys on the MCU.
34 | # In Ardour, they can be accessed with the Q and W keys.
35 |
36 | E7 "q" # MARKER <
37 | F#7 "w" # MARKER >
38 |
39 | [MIDI]
40 |
41 | # Here's another way to access the "Prev/Next Marker" functions which doesn't
42 | # rely on keyboard shortcuts of the application. Instead, it assumes that you
43 | # assign an MCU key combination to these functions in your DAW. I have Ardour
44 | # set up so that SHIFT + the left and right cursor keys on the MCU jumps to
45 | # the previous and next marker, respectively. Of course, you can change this
46 | # to whatever is convenient for you.
47 |
48 | E7 A#5 D8 # MCU SHIFT <-
49 | F#7 A#5 D#8 # MCU SHIFT ->
50 |
51 | # Ardour uses the MCU's MARKER key to set a marker, so that's what we assign
52 | # the SET button to.
53 |
54 | F7 C7 # SET -> MARKER
55 |
56 | # Of course, you can set the key to whatever you want. E.g., to map it to the
57 | # SHIFT key:
58 |
59 | #F7 A#5
60 |
--------------------------------------------------------------------------------
/examples/x-touch-one.device:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/jackdriver.c:
--------------------------------------------------------------------------------
1 | /* AG: This is a trimmed-down version of the Jack MIDI driver pilfered from
2 | Spencer Jackson's osc2midi program, cf. https://github.com/ssj71/OSC2MIDI. */
3 |
4 | /*-
5 | * Copyright (c) 2014 Spencer Jackson
6 | * All rights reserved.
7 | *
8 | * Redistribution and use in source and binary forms, with or without
9 | * modification, are permitted provided that the following conditions
10 | * are met:
11 | * 1. Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * 2. Redistributions in binary form must reproduce the above copyright
14 | * notice, this list of conditions and the following disclaimer in the
15 | * documentation and/or other materials provided with the distribution.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 | * SUCH DAMAGE.
28 | */
29 |
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include "jackdriver.h"
45 |
46 |
47 | typedef struct _MidiMessage
48 | {
49 | jack_nframes_t time;
50 | int len; /* Length of MIDI message, in bytes. */
51 | uint8_t data[3];
52 | } MidiMessage;
53 |
54 | #define RINGBUFFER_SIZE 16384*sizeof(MidiMessage)
55 |
56 | /* Will emit a warning if time between jack callbacks is longer than this. */
57 | #define MAX_TIME_BETWEEN_CALLBACKS 0.1
58 |
59 | /* Will emit a warning if execution of jack callback takes longer than this. */
60 | #define MAX_PROCESSING_TIME 0.01
61 |
62 |
63 | ///////////////////////////////////////////////
64 | //These functions operate in the JACK RT Thread
65 | ///////////////////////////////////////////////
66 |
67 | double
68 | get_time(void)
69 | {
70 | double seconds;
71 | int ret;
72 | struct timeval tv;
73 |
74 | ret = gettimeofday(&tv, NULL);
75 |
76 | if (ret)
77 | {
78 | perror("gettimeofday");
79 | exit(EX_OSERR);
80 | }
81 |
82 | seconds = tv.tv_sec + tv.tv_usec / 1000000.0;
83 |
84 | return (seconds);
85 | }
86 |
87 | double
88 | get_delta_time(void)
89 | {
90 | static double previously = -1.0;
91 | double now;
92 | double delta;
93 |
94 | now = get_time();
95 |
96 | if (previously == -1.0)
97 | {
98 | previously = now;
99 |
100 | return (0);
101 | }
102 |
103 | delta = now - previously;
104 | previously = now;
105 |
106 | assert(delta >= 0.0);
107 |
108 | return (delta);
109 | }
110 |
111 |
112 | double
113 | nframes_to_ms(jack_client_t* jack_client,jack_nframes_t nframes)
114 | {
115 | jack_nframes_t sr;
116 |
117 | sr = jack_get_sample_rate(jack_client);
118 |
119 | assert(sr > 0);
120 |
121 | return ((nframes * 1000.0) / (double)sr);
122 | }
123 |
124 | void
125 | queue_message(jack_ringbuffer_t* ringbuffer, MidiMessage *ev)
126 | {
127 | int written;
128 |
129 | if (jack_ringbuffer_write_space(ringbuffer) < sizeof(*ev))
130 | {
131 | fprintf(stderr, "Not enough space in the ringbuffer, MIDI LOST.\n");
132 | return;
133 | }
134 |
135 | written = jack_ringbuffer_write(ringbuffer, (char *)ev, sizeof(*ev));
136 |
137 | if (written != sizeof(*ev))
138 | fprintf(stderr, "jack_ringbuffer_write failed, MIDI LOST.\n");
139 | }
140 |
141 | void
142 | process_midi_input(JACK_SEQ* seq,jack_nframes_t nframes)
143 | {
144 | int k;
145 |
146 | for (k = 0; k < seq->n_in; k++) {
147 |
148 | int read, events, i;
149 | MidiMessage rev;
150 | jack_midi_event_t event;
151 |
152 | void *port_buffer = jack_port_get_buffer(seq->input_port[k], nframes);
153 | // this is used for direct pass-through of system messages
154 | void *out_buffer = seq->passthrough[k] && k < seq->n_out?
155 | jack_port_get_buffer(seq->output_port[k], nframes):0;
156 | if (port_buffer == NULL)
157 | {
158 | fprintf(stderr, "jack_port_get_buffer failed, cannot receive anything.\n");
159 | return;
160 | }
161 |
162 | if (out_buffer)
163 | {
164 | #ifdef JACK_MIDI_NEEDS_NFRAMES
165 | jack_midi_clear_buffer(out_buffer, nframes);
166 | #else
167 | jack_midi_clear_buffer(out_buffer);
168 | #endif
169 | }
170 |
171 | #ifdef JACK_MIDI_NEEDS_NFRAMES
172 | events = jack_midi_get_event_count(port_buffer, nframes);
173 | #else
174 | events = jack_midi_get_event_count(port_buffer);
175 | #endif
176 |
177 | for (i = 0; i < events; i++)
178 | {
179 |
180 | #ifdef JACK_MIDI_NEEDS_NFRAMES
181 | read = jack_midi_event_get(&event, port_buffer, i, nframes);
182 | #else
183 | read = jack_midi_event_get(&event, port_buffer, i);
184 | #endif
185 | if (!read)
186 | {
187 | //successful event get
188 |
189 | if (event.size <= 3 && event.size >= 1 && event.buffer[0] < 0xf0)
190 | {
191 | //not sysex or something
192 |
193 | //PUSH ONTO CIRCULAR BUFFER
194 | //not sure if its a true copy onto buffer, if not this won't work
195 | rev.len = event.size;
196 | rev.time = event.time;
197 | memcpy(rev.data, event.buffer, rev.len);
198 | queue_message(seq->ringbuffer_in[k],&rev);
199 | }
200 | else if (out_buffer && event.size >= 1 && event.buffer[0] >= 0xf0)
201 | {
202 | // direct pass-through of system messages
203 | #ifdef JACK_MIDI_NEEDS_NFRAMES
204 | uint8_t *buffer = jack_midi_event_reserve(out_buffer, event.time, event.size, nframes);
205 | #else
206 | uint8_t *buffer = jack_midi_event_reserve(out_buffer, event.time, event.size);
207 | #endif
208 | if (buffer) memcpy(buffer, event.buffer, event.size);
209 | }
210 | }
211 |
212 | }
213 | }
214 | }
215 |
216 | void
217 | process_midi_output(JACK_SEQ* seq,jack_nframes_t nframes)
218 | {
219 | jack_nframes_t last_frame_time = jack_last_frame_time(seq->jack_client);
220 | int k;
221 |
222 | for (k = 0; k < seq->n_out; k++) {
223 |
224 | int read, t;
225 | uint8_t *buffer;
226 | void *port_buffer;
227 | MidiMessage ev;
228 |
229 | port_buffer = jack_port_get_buffer(seq->output_port[k], nframes);
230 | if (port_buffer == NULL)
231 | {
232 | fprintf(stderr, "jack_port_get_buffer failed, cannot send anything.\n");
233 | return;
234 | }
235 |
236 | if (!seq->passthrough[k])
237 | {
238 | #ifdef JACK_MIDI_NEEDS_NFRAMES
239 | jack_midi_clear_buffer(port_buffer, nframes);
240 | #else
241 | jack_midi_clear_buffer(port_buffer);
242 | #endif
243 | }
244 |
245 | while (jack_ringbuffer_read_space(seq->ringbuffer_out[k]))
246 | {
247 | read = jack_ringbuffer_peek(seq->ringbuffer_out[k], (char *)&ev, sizeof(ev));
248 |
249 | if (read != sizeof(ev))
250 | {
251 | //warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
252 | jack_ringbuffer_read_advance(seq->ringbuffer_out[k], read);
253 | continue;
254 | }
255 |
256 | t = ev.time + nframes - last_frame_time;
257 |
258 | /* If computed time is too much into the future, we'll need
259 | to send it later. */
260 | if (t >= (int)nframes)
261 | break;
262 |
263 | /* If computed time is < 0, we missed a cycle because of xrun. */
264 | if (t < 0)
265 | t = 0;
266 |
267 | jack_ringbuffer_read_advance(seq->ringbuffer_out[k], sizeof(ev));
268 |
269 | #ifdef JACK_MIDI_NEEDS_NFRAMES
270 | buffer = jack_midi_event_reserve(port_buffer, t, ev.len, nframes);
271 | #else
272 | buffer = jack_midi_event_reserve(port_buffer, t, ev.len);
273 | #endif
274 |
275 | if (buffer == NULL)
276 | {
277 | //warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST.");
278 | break;
279 | }
280 |
281 | memcpy(buffer, ev.data, ev.len);
282 | }
283 | }
284 | }
285 |
286 | int
287 | process_callback(jack_nframes_t nframes, void *seqq)
288 | {
289 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
290 | #ifdef MEASURE_TIME
291 | if (get_delta_time() > MAX_TIME_BETWEEN_CALLBACKS)
292 | fprintf(stderr, "Had to wait too long for JACK callback; scheduling problem?\n");
293 | #endif
294 |
295 | if(seq->n_in)
296 | process_midi_input( seq,nframes );
297 | if(seq->n_out)
298 | process_midi_output( seq,nframes );
299 |
300 | #ifdef MEASURE_TIME
301 | if (get_delta_time() > MAX_PROCESSING_TIME)
302 | fprintf(stderr, "Processing took too long; scheduling problem?\n");
303 | #endif
304 |
305 | return (0);
306 | }
307 |
308 | ///////////////////////////////////////////////
309 | //these functions are executed in other threads
310 | ///////////////////////////////////////////////
311 | void queue_midi(void* seqq, uint8_t msg[], uint8_t port_no)
312 | {
313 | MidiMessage ev;
314 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
315 | ev.len = 3;
316 |
317 | // At least with JackOSX, Jack will transmit the bytes verbatim, so make
318 | // sure that we look at the status byte and trim the message accordingly,
319 | // in order not to transmit any invalid MIDI data.
320 | switch (msg[0] & 0xf0)
321 | {
322 | case 0x80:
323 | case 0x90:
324 | case 0xa0:
325 | case 0xb0:
326 | case 0xe0:
327 | break; // 2 data bytes
328 | case 0xc0:
329 | case 0xd0:
330 | ev.len = 2; // 1 data byte
331 | break;
332 | case 0xf0: // system message
333 | switch (msg[0])
334 | {
335 | case 0xf2:
336 | break; // 2 data bytes
337 | case 0xf1:
338 | case 0xf3:
339 | ev.len = 2; // 1 data byte
340 | break;
341 | case 0xf6:
342 | case 0xf8:
343 | case 0xf9:
344 | case 0xfa:
345 | case 0xfb:
346 | case 0xfc:
347 | case 0xfe:
348 | case 0xff:
349 | ev.len = 1; // no data byte
350 | break;
351 | default:
352 | // ignore unknown (most likely sysex)
353 | return;
354 | }
355 | break;
356 | default:
357 | return; // not a valid MIDI message, bail out
358 | }
359 |
360 | ev.data[0] = msg[0];
361 | ev.data[1] = msg[1];
362 | ev.data[2] = msg[2];
363 |
364 | ev.time = jack_frame_time(seq->jack_client);
365 | queue_message(seq->ringbuffer_out[port_no],&ev);
366 | }
367 |
368 | int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no)
369 | {
370 | int read, k;
371 | MidiMessage ev;
372 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
373 |
374 | for (k = 0; k < seq->n_in; k++) {
375 |
376 | if (jack_ringbuffer_read_space(seq->ringbuffer_in[k]))
377 | {
378 | read = jack_ringbuffer_peek(seq->ringbuffer_in[k], (char *)&ev, sizeof(ev));
379 |
380 | if (read != sizeof(ev))
381 | {
382 | //warn_from_jack_thread_context("Short read from the ringbuffer, possible note loss.");
383 | jack_ringbuffer_read_advance(seq->ringbuffer_in[k], read);
384 | return -1;
385 | }
386 |
387 | jack_ringbuffer_read_advance(seq->ringbuffer_in[k], sizeof(ev));
388 |
389 | memcpy(msg,ev.data,ev.len);
390 | *port_no = k;
391 |
392 | return ev.len;
393 | }
394 | }
395 | return 0;
396 | }
397 |
398 | int jack_quit;
399 |
400 | void
401 | shutdown_callback()
402 | {
403 | // we can't do anything fancy here, just ping the main thread
404 | jack_quit = -1;
405 | }
406 |
407 | char *jack_command_line = "midizap";
408 |
409 | void
410 | session_callback(jack_session_event_t *event, void *seqq)
411 | {
412 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
413 | // XXXTODO: In order to better support Jack session management in the future
414 | // we may want to copy over the loaded midizaprc file and store it in the
415 | // session dir, so that we can reload it from there later. For the time
416 | // being, we simply record the command line here.
417 | //printf("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit");
418 |
419 | event->command_line = strdup(jack_command_line);
420 | jack_session_reply(seq->jack_client, event);
421 |
422 | if (event->type == JackSessionSaveAndQuit) {
423 | jack_quit = 1;
424 | }
425 |
426 | jack_session_event_free (event);
427 | }
428 |
429 | void
430 | connect_callback(jack_port_id_t a, jack_port_id_t b, int yn, void *seqq)
431 | {
432 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
433 | jack_port_t *ap = jack_port_by_id(seq->jack_client, a);
434 | jack_port_t *bp = jack_port_by_id(seq->jack_client, b);
435 | const char *aname = jack_port_name(ap);
436 | const char *bname = jack_port_name(bp);
437 | size_t l = strlen(seq->client_name);
438 | if (jack_port_is_mine(seq->jack_client, ap))
439 | printf("%-*s %s: %s\n", (int)l+10, aname,
440 | (yn ? "connected to" : "disconnected from"), bname);
441 | else if (jack_port_is_mine(seq->jack_client, bp))
442 | printf("%-*s %s: %s\n", (int)l+10, bname,
443 | (yn ? "connected to" : "disconnected from"), aname);
444 | }
445 |
446 | // queue for pending connections, to be processed in the main thread
447 | #define CONN_SIZE 256
448 | static int n_inconn, n_outconn;
449 |
450 | static struct {
451 | int portno;
452 | char *name;
453 | } inconn[CONN_SIZE], outconn[CONN_SIZE];
454 |
455 | static void add_inconn(int portno, const char *name)
456 | {
457 | if (n_inconn < CONN_SIZE) {
458 | inconn[n_inconn].portno = portno;
459 | inconn[n_inconn].name = strdup(name);
460 | n_inconn++;
461 | }
462 | }
463 |
464 | static void add_outconn(int portno, const char *name)
465 | {
466 | if (n_outconn < CONN_SIZE) {
467 | outconn[n_outconn].portno = portno;
468 | outconn[n_outconn].name = strdup(name);
469 | n_outconn++;
470 | }
471 | }
472 |
473 | static void match_connections(JACK_SEQ* seq, jack_port_t *port)
474 | {
475 | if (jack_port_is_mine(seq->jack_client, port)) return;
476 | int flags = jack_port_flags(port);
477 | const char *name = jack_port_name(port);
478 | if (flags & JackPortIsInput) {
479 | // Try to match the port name to one of our out regexes.
480 | for (int i = 0; i < 2 && i < seq->n_out; i++) {
481 | if (seq->out[i] && regexec(&seq->outre[i], name, 0, 0, 0) == 0 &&
482 | // check that port types are compatible
483 | jack_port_type(seq->output_port[i]) == jack_port_type(port) &&
484 | // check that we're not connected yet
485 | !jack_port_connected_to(seq->output_port[i], name)) {
486 | // we can't connect right here, that has to be done in the main
487 | // thread, so we simply store the request for later
488 | add_outconn(i, name);
489 | }
490 | }
491 | } else if (flags & JackPortIsOutput) {
492 | // Try to match the port name to one of our in regexes.
493 | for (int i = 0; i < 2 && i < seq->n_in; i++) {
494 | if (seq->in[i] && regexec(&seq->inre[i], name, 0, 0, 0) == 0 &&
495 | // check that port types are compatible
496 | jack_port_type(seq->input_port[i]) == jack_port_type(port) &&
497 | // check that we're not connected yet
498 | !jack_port_connected_to(seq->input_port[i], name)) {
499 | add_inconn(i, name);
500 | }
501 | }
502 | }
503 | }
504 |
505 | void
506 | registration_callback(jack_port_id_t id, int reg, void *seqq)
507 | {
508 | if (!reg) return;
509 | JACK_SEQ* seq = (JACK_SEQ*)seqq;
510 | jack_port_t *port = jack_port_by_id(seq->jack_client, id);
511 | match_connections(seq, port);
512 | }
513 |
514 | ////////////////////////////////
515 | //this is run in the main thread
516 | ////////////////////////////////
517 |
518 | void process_connections(JACK_SEQ* seq)
519 | {
520 | int i;
521 | for (i = 0; i < n_inconn; i++)
522 | if (inconn[i].name) {
523 | if (jack_connect(seq->jack_client,
524 | inconn[i].name,
525 | jack_port_name(seq->input_port[inconn[i].portno])))
526 | fprintf(stderr, "error trying to connect in%d to %s\n",
527 | inconn[i].portno, inconn[i].name);
528 | free(inconn[i].name);
529 | }
530 | n_inconn = 0;
531 | for (i = 0; i < n_outconn; i++)
532 | if (outconn[i].name) {
533 | if (jack_connect(seq->jack_client,
534 | jack_port_name(seq->output_port[outconn[i].portno]),
535 | outconn[i].name))
536 | fprintf(stderr, "error trying to connect out%d to %s\n",
537 | outconn[i].portno, outconn[i].name);
538 | free(outconn[i].name);
539 | }
540 | n_outconn = 0;
541 | }
542 |
543 | int
544 | init_jack(JACK_SEQ* seq, uint8_t verbose)
545 | {
546 | int err, k;
547 | char portname[100],
548 | *client_name = seq->client_name?seq->client_name:"midizap";
549 | jack_status_t status;
550 |
551 | // compile the in/out connection regexes
552 | for (int i = 0; i < 2; i++) {
553 | if (seq->in[i] && *seq->in[i]) {
554 | int err = regcomp(&seq->inre[i], seq->in[i], REG_EXTENDED|REG_NOSUB);
555 | if (err) {
556 | char buf[1024];
557 | regerror(err, &seq->inre[i], buf, sizeof(buf));
558 | fprintf(stderr, "error compiling in%d regex: %s\n%s\n",
559 | i, seq->in[i], buf);
560 | regfree(&seq->inre[i]);
561 | seq->in[i] = 0;
562 | }
563 | } else {
564 | seq->in[i] = 0;
565 | }
566 | if (seq->out[i] && *seq->out[i]) {
567 | int err = regcomp(&seq->outre[i], seq->out[i], REG_EXTENDED|REG_NOSUB);
568 | if (err) {
569 | char buf[1024];
570 | regerror(err, &seq->outre[i], buf, sizeof(buf));
571 | fprintf(stderr, "error compiling out%d regex: %s\n%s\n",
572 | i, seq->out[i], buf);
573 | regfree(&seq->outre[i]);
574 | seq->out[i] = 0;
575 | }
576 | } else {
577 | seq->out[i] = 0;
578 | }
579 | }
580 |
581 | if(verbose)printf("opening client...\n");
582 | seq->jack_client = jack_client_open(client_name, JackNullOption, &status);
583 |
584 | if (seq->jack_client == NULL)
585 | {
586 | fprintf(stderr, "Could not connect to the JACK server; run jackd first?\n");
587 | return 0;
588 | }
589 |
590 | if (verbose && (status & JackServerStarted)) {
591 | printf("JACK server started\n");
592 | }
593 | if (verbose && (status & JackNameNotUnique)) {
594 | client_name = jack_get_client_name(seq->jack_client);
595 | printf("JACK client name changed to: %s\n", client_name);
596 | }
597 |
598 | jack_on_shutdown(seq->jack_client, shutdown_callback, (void*)seq);
599 | jack_set_session_callback(seq->jack_client, session_callback, (void*)seq);
600 | jack_set_port_registration_callback(seq->jack_client, registration_callback, (void*)seq);
601 | if (verbose) jack_set_port_connect_callback(seq->jack_client, connect_callback, (void*)seq);
602 |
603 | //if(verbose)printf("assigning process callback...\n");
604 | err = jack_set_process_callback(seq->jack_client, process_callback, (void*)seq);
605 | if (err)
606 | {
607 | fprintf(stderr, "Could not register JACK process callback.\n");
608 | return 0;
609 | }
610 |
611 |
612 | seq->ringbuffer_in = NULL;
613 | seq->input_port = NULL;
614 | if(seq->n_in)
615 | {
616 |
617 | //if(verbose)printf("initializing JACK input: \ncreating ringbuffer...\n");
618 | seq->ringbuffer_in = calloc(seq->n_in, sizeof(jack_ringbuffer_t*));
619 | seq->input_port = calloc(seq->n_in, sizeof(jack_port_t*));
620 | if (!seq->ringbuffer_in || !seq->input_port)
621 | {
622 | fprintf(stderr, "Cannot allocate memory for ports and ringbuffers.\n");
623 | return 0;
624 | }
625 |
626 | for (k = 0; k < seq->n_in; k++) {
627 | seq->ringbuffer_in[k] = jack_ringbuffer_create(RINGBUFFER_SIZE);
628 |
629 | if (seq->ringbuffer_in[k] == NULL)
630 | {
631 | fprintf(stderr, "Cannot create JACK ringbuffer.\n");
632 | return 0;
633 | }
634 |
635 | jack_ringbuffer_mlock(seq->ringbuffer_in[k]);
636 |
637 | if (k)
638 | sprintf(portname, "midi_in%d", k+1);
639 | else
640 | strcpy(portname, "midi_in");
641 | seq->input_port[k] = jack_port_register(seq->jack_client, portname,
642 | JACK_DEFAULT_MIDI_TYPE,
643 | JackPortIsInput, 0);
644 |
645 | if (seq->input_port[k] == NULL)
646 | {
647 | fprintf(stderr, "Could not register JACK port.\n");
648 | return 0;
649 | }
650 | }
651 | }
652 |
653 | seq->ringbuffer_out = NULL;
654 | seq->output_port = NULL;
655 | if(seq->n_out)
656 | {
657 |
658 | //if(verbose)printf("initializing JACK output: \ncreating ringbuffer...\n");
659 | seq->ringbuffer_out = calloc(seq->n_out, sizeof(jack_ringbuffer_t*));
660 | seq->output_port = calloc(seq->n_out, sizeof(jack_port_t*));
661 | if (!seq->ringbuffer_out || !seq->output_port)
662 | {
663 | fprintf(stderr, "Cannot allocate memory for ports and ringbuffers.\n");
664 | return 0;
665 | }
666 |
667 | for (k = 0; k < seq->n_out; k++) {
668 | seq->ringbuffer_out[k] = jack_ringbuffer_create(RINGBUFFER_SIZE);
669 |
670 | if (seq->ringbuffer_out[k] == NULL)
671 | {
672 | fprintf(stderr, "Cannot create JACK ringbuffer.\n");
673 | return 0;
674 | }
675 |
676 | jack_ringbuffer_mlock(seq->ringbuffer_out[k]);
677 |
678 | if (k)
679 | sprintf(portname, "midi_out%d", k+1);
680 | else
681 | strcpy(portname, "midi_out");
682 | seq->output_port[k] = jack_port_register(seq->jack_client, portname,
683 | JACK_DEFAULT_MIDI_TYPE,
684 | JackPortIsOutput, 0);
685 |
686 | if (seq->output_port[k] == NULL)
687 | {
688 | fprintf(stderr, "Could not register JACK port.\n");
689 | return 0;
690 | }
691 | }
692 | }
693 |
694 | if (jack_activate(seq->jack_client))
695 | {
696 | fprintf(stderr, "Cannot activate JACK client.\n");
697 | return 0;
698 | }
699 |
700 | // All done, set up the initial connections.
701 | const char **ports = jack_get_ports(seq->jack_client, 0, 0, 0);
702 | for (const char **name = ports; *name; ++name) {
703 | jack_port_t *port = jack_port_by_name(seq->jack_client, *name);
704 | match_connections(seq, port);
705 | }
706 | free(ports);
707 | process_connections(seq);
708 |
709 | return 1;
710 | }
711 |
712 | void close_jack(JACK_SEQ* seq)
713 | {
714 | int k;
715 | if(seq->n_out) {
716 | for (k = 0; k < seq->n_out; k++)
717 | jack_ringbuffer_free(seq->ringbuffer_out[k]);
718 | }
719 | if(seq->n_in) {
720 | for (k = 0; k < seq->n_in; k++)
721 | jack_ringbuffer_free(seq->ringbuffer_in[k]);
722 | }
723 | jack_client_close(seq->jack_client);
724 | }
725 |
--------------------------------------------------------------------------------
/jackdriver.h:
--------------------------------------------------------------------------------
1 | #ifndef JACKDRIVER_H
2 | #define JACKDRIVER_H
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | typedef struct _jseq
10 | {
11 | char *client_name;
12 | jack_ringbuffer_t **ringbuffer_out;
13 | jack_ringbuffer_t **ringbuffer_in;
14 | jack_client_t *jack_client;
15 | jack_port_t **output_port;
16 | jack_port_t **input_port;
17 | uint8_t n_in, n_out, passthrough[2];
18 | char *in[2], *out[2];
19 | regex_t inre[2], outre[2];
20 | } JACK_SEQ;
21 |
22 | extern int jack_quit;
23 | // This is supposed to be set properly by main().
24 | extern char *jack_command_line;
25 |
26 | int init_jack(JACK_SEQ* seq, uint8_t verbose);
27 | void process_connections(JACK_SEQ* seq);
28 | void close_jack(JACK_SEQ* seq);
29 | void queue_midi(void* seqq, uint8_t msg[], uint8_t port_no);
30 | int pop_midi(void* seqq, uint8_t msg[], uint8_t *port_no);
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/keys.sed:
--------------------------------------------------------------------------------
1 | /^\#ifdef/p
2 | /^\#endif/p
3 | /^\#define/!d
4 | s/^\#define //
5 | s/^\([^[:space:]]*\).*$/{ "\1", \1 }, /
6 |
--------------------------------------------------------------------------------
/keywords.sed:
--------------------------------------------------------------------------------
1 | /^\#define/!d
2 | s/^\#define //
3 | s/^\([^[:space:]]*\).*$/"\1"/
4 |
--------------------------------------------------------------------------------
/midizap-mode.el.in:
--------------------------------------------------------------------------------
1 | ;;; midizap-mode.el --- midizap syntax highlighting for Emacs.
2 |
3 | ;;; Commentary:
4 |
5 | ;;; This is a simple mode for editing midizaprc files which provides basic
6 | ;;; syntax highlighting, and a command midizap-mode-run, bound to C-c C-c,
7 | ;;; which lets you quickly launch a midizap session (with options) on the
8 | ;;; edited midizaprc file in an Emacs buffer.
9 |
10 | ;;; Install this anywhere where Emacs finds it (e.g., in the Emacs site-lisp
11 | ;;; directory -- usually under /usr/share/emacs/site-lisp on Un*x systems, or
12 | ;;; in any directory on the Emacs load-path) and load it in your .emacs as
13 | ;;; follows:
14 |
15 | ;;; (require 'midizap-mode)
16 |
17 | ;;; The mode also supports auto-completion of midizaprc keywords, to make this
18 | ;;; work you'll need the auto-complete package available from MELPA, please
19 | ;;; check: https://github.com/auto-complete/auto-complete.
20 |
21 | ;;; In the midizap-mode subdirectory you'll find some snippets to be used with
22 | ;;; yasnippet (https://github.com/joaotavora/yasnippet); to use these, copy
23 | ;;; the entire folder to your ~/.emacs.d/snippets directory.
24 |
25 | ;;; Code:
26 |
27 | (require 'comint)
28 |
29 | (defconst midizap-keywords
30 | (list
31 | "DEBUG_REGEX" "DEBUG_STROKES" "DEBUG_KEYS" "DEBUG_MIDI"
32 | "MIDI_OCTAVE" "JACK_NAME" "JACK_PORTS"
33 | "JACK_IN" "JACK_IN1" "JACK_IN2"
34 | "JACK_OUT" "JACK_OUT1" "JACK_OUT2"
35 | "PASSTHROUGH" "SYSTEM_PASSTHROUGH"
36 | "RELEASE" "SHIFT" "SHIFT1" "SHIFT2" "SHIFT3" "SHIFT4"
37 | "CLASS" "TITLE"
38 | ;; keysyms
39 |
40 | ))
41 |
42 | ;;;###autoload
43 | (define-generic-mode 'midizap-mode
44 | nil
45 | midizap-keywords
46 | '(("^[[:blank:]]*\\(#.*\\)" 1 'font-lock-comment-face t)
47 | ("[[:blank:]]+\\(#.*\\)" 1 'font-lock-comment-face t)
48 | ("^[[:blank:]]*\\[\\([^\n[]+\\)\\]\\(.*\\)"
49 | 1 'font-lock-variable-name-face)
50 | ("\\<\\(\\([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)
51 | ("\\<\\([0-9]+\\)\\>" 1 'font-lock-constant-face))
52 | (list "\\.midizaprc\\'")
53 | (list 'midizap-mode-setup-function)
54 | "Generic mode for midizap configuration files.")
55 |
56 | (defvar midizap-mode-keymap (make-sparse-keymap)
57 | "Keymap for midizap-mode.")
58 |
59 | (defvar midizap-command "midizap -drk ")
60 |
61 | (defvar ac-sources)
62 |
63 | (defvar midizap-mode-ac-source
64 | '((candidates . midizap-keywords)))
65 |
66 | (defun midizap-mode-run (command)
67 | "Run the current midizaprc file with COMMAND in a comint buffer."
68 | (interactive
69 | (list
70 | (let ((command (eval midizap-command)))
71 | (read-string "Run midizap with: " command))))
72 | (unless (equal command (eval midizap-command))
73 | (setq midizap-command command))
74 | (save-some-buffers)
75 | (let* ((file (buffer-file-name))
76 | (buf-name (concat "*" file "*")))
77 | (with-current-buffer (get-buffer-create buf-name)
78 | (erase-buffer)
79 | (comint-mode)
80 | (comint-exec
81 | buf-name
82 | file
83 | shell-file-name
84 | nil
85 | (list "-c" (concat command " " file)))
86 | (display-buffer buf-name))))
87 |
88 | (define-key midizap-mode-keymap "\C-c\C-c" 'midizap-mode-run)
89 |
90 | (defun midizap-mode-setup-function ()
91 | "Custom setup function for midizap-mode."
92 | (make-local-variable 'parse-sexp-ignore-comments)
93 | (make-local-variable 'comment-start)
94 | (make-local-variable 'comment-start-skip)
95 | (make-local-variable 'comment-end)
96 | (setq parse-sexp-ignore-comments t
97 | comment-end ""
98 | comment-start "# "
99 | comment-start-skip "# *"
100 | )
101 | (if (boundp 'ac-sources)
102 | (progn
103 | (add-to-list 'ac-modes 'midizap-mode)
104 | (add-to-list 'ac-sources 'midizap-mode-ac-source))
105 | (message "You may want to install and use auto-complete"))
106 | (use-local-map midizap-mode-keymap)
107 | )
108 |
109 | (provide 'midizap-mode)
110 |
111 | ;; End:
112 | ;;; midizap-mode.el ends here
113 |
--------------------------------------------------------------------------------
/midizap-mode/midizaprc:
--------------------------------------------------------------------------------
1 | # -*- mode: snippet -*-
2 | # name: midizaprc template
3 | # key: #
4 | # --
5 |
6 | # midizap configuration template for Emacs (requires yasnippet)
7 |
8 | # Jack client name and ports/connections
9 |
10 | # NOTE: Remove the JACK_PORTS and JACK_OUT lines if you don't need MIDI
11 | # output. For bidirectional setups, use JACK_PORTS 2 and add JACK_IN2,
12 | # JACK_OUT2 and a [MIDI2] section for the feedback connection.
13 |
14 | JACK_NAME "${1:client-name}"
15 | JACK_PORTS 1
16 | JACK_IN ${2:controller-regex}
17 | JACK_OUT ${3:application-regex}
18 |
19 | # common debugging options (uncomment as needed)
20 |
21 | #DEBUG_REGEX
22 | #DEBUG_KEYS
23 | #DEBUG_MIDI
24 |
25 | # translations go here
26 |
27 | [MIDI]
28 |
--------------------------------------------------------------------------------
/midizap-mode/section:
--------------------------------------------------------------------------------
1 | # -*- mode: snippet -*-
2 | # name: section header
3 | # key: [
4 | # --
5 | [${1:name}] ${2:regex}
6 |
--------------------------------------------------------------------------------
/midizap.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 |
4 | Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
5 | Copyright 2018 Albert Graef
6 |
7 | Based on a version (c) 2006 Trammell Hudson
8 |
9 | which was in turn
10 |
11 | Based heavily on code by Arendt David
12 |
13 | */
14 |
15 | #include "midizap.h"
16 | #include "jackdriver.h"
17 |
18 | typedef struct input_event EV;
19 |
20 | Display *display;
21 |
22 | JACK_SEQ seq;
23 | int jack_num_outputs = 0, debug_jack = 0;
24 | int auto_feedback = 1;
25 | int passthrough[2] = {-1, -1}, system_passthrough[2] = {-1, -1};
26 | int shift = 0;
27 |
28 | void
29 | initdisplay(void)
30 | {
31 | int event, error, major, minor;
32 |
33 | display = XOpenDisplay(0);
34 | if (!display) {
35 | fprintf(stderr, "unable to open X display\n");
36 | exit(1);
37 | }
38 | if (!XTestQueryExtension(display, &event, &error, &major, &minor)) {
39 | fprintf(stderr, "Xtest extensions not supported\n");
40 | XCloseDisplay(display);
41 | exit(1);
42 | }
43 | }
44 |
45 | void
46 | send_button(unsigned int button, int press)
47 | {
48 | XTestFakeButtonEvent(display, button, press ? True : False, DELAY);
49 | }
50 |
51 | void
52 | send_key(KeySym key, int press)
53 | {
54 | KeyCode keycode;
55 |
56 | if (key >= XK_Button_1 && key <= XK_Scroll_Down) {
57 | send_button((unsigned int)key - XK_Button_0, press);
58 | return;
59 | }
60 | keycode = XKeysymToKeycode(display, key);
61 | XTestFakeKeyEvent(display, keycode, press ? True : False, DELAY);
62 | }
63 |
64 | // cached controller and pitch bend values
65 | static int16_t notevalue[2][16][128];
66 | static int16_t ccvalue[2][16][128];
67 | static int16_t kpvalue[2][16][128];
68 | static int16_t cpvalue[2][16];
69 | static int16_t pbvalue[2][16] =
70 | {{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
71 | 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192},
72 | {8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
73 | 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}};
74 |
75 | static int dataval(int val, int min, int max)
76 | {
77 | if (!val || val > max)
78 | return max;
79 | else if (val < min)
80 | return min;
81 | else
82 | return val;
83 | }
84 |
85 | static int datavals(int val, int step, int *steps, int n_steps)
86 | {
87 | if (val < 0)
88 | return -datavals(-val, step, steps, n_steps);
89 | else if (val < n_steps)
90 | return steps[val];
91 | else if (n_steps)
92 | return steps[n_steps-1];
93 | else if (step)
94 | return step*val;
95 | else
96 | return val;
97 | }
98 |
99 | void
100 | handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive);
101 |
102 | void
103 | send_midi(uint8_t portno, stroke *s, int index, int dir,
104 | int mod, int mod_step, int mod_n_steps, int *mod_steps,
105 | int val, int depth, uint8_t ret_msg[3])
106 | {
107 | int status = s->status, data = s->data, swap = s->swap,
108 | recursive = s->recursive;
109 | int step = s->step, n_steps = s->n_steps, *steps = s->steps;
110 | if (!recursive && !jack_num_outputs) return; // MIDI output not enabled
111 | uint8_t msg[3];
112 | int chan = status & 0x0f;
113 | msg[0] = status;
114 | msg[1] = data;
115 | switch (status & 0xf0) {
116 | case 0x90:
117 | if (dir) {
118 | // increment (dir==1) or decrement (dir==-1) the current value,
119 | // clamping it to the 0..127 data byte range
120 | if (!step) step = 1;
121 | dir *= step;
122 | if (dir > 0) {
123 | if (notevalue[portno][chan][data] >= 127) return;
124 | notevalue[portno][chan][data] += dir;
125 | if (notevalue[portno][chan][data] > 127) notevalue[portno][chan][data] = 127;
126 | } else {
127 | if (notevalue[portno][chan][data] == 0) return;
128 | notevalue[portno][chan][data] += dir;
129 | if (notevalue[portno][chan][data] < 0) notevalue[portno][chan][data] = 0;
130 | }
131 | msg[2] = notevalue[portno][chan][data];
132 | } else if (mod) {
133 | int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
134 | int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
135 | int v = datavals(r, step, steps, n_steps);
136 | if (d > 127 || d < 0) return;
137 | if (v > 127 || v < 0) return;
138 | if (s->change) {
139 | if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
140 | s->d = d; s->v = v; s->change = 2; // >1 => initialized
141 | }
142 | msg[1] = d;
143 | msg[2] = v;
144 | } else if (!index) {
145 | msg[2] = dataval(step, 0, 127);
146 | } else {
147 | msg[2] = 0;
148 | }
149 | break;
150 | case 0xb0:
151 | if (dir) {
152 | if (s->incr) {
153 | // incremental controller, simply spit out a relative sign bit value
154 | if (!step) step = 1;
155 | dir *= step;
156 | if (dir < -63) dir = -63;
157 | if (dir > 63) dir = 63;
158 | msg[2] = dir>0?dir:dir<0?64-dir:0;
159 | } else {
160 | // increment (dir==1) or decrement (dir==-1) the current value,
161 | // clamping it to the 0..127 data byte range
162 | if (!step) step = 1;
163 | dir *= step;
164 | if (dir > 0) {
165 | if (ccvalue[portno][chan][data] >= 127) return;
166 | ccvalue[portno][chan][data] += dir;
167 | if (ccvalue[portno][chan][data] > 127) ccvalue[portno][chan][data] = 127;
168 | } else {
169 | if (ccvalue[portno][chan][data] == 0) return;
170 | ccvalue[portno][chan][data] += dir;
171 | if (ccvalue[portno][chan][data] < 0) ccvalue[portno][chan][data] = 0;
172 | }
173 | msg[2] = ccvalue[portno][chan][data];
174 | }
175 | } else if (mod) {
176 | int m = (data>=128)*128;
177 | int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
178 | int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
179 | int v = datavals(r, step, steps, n_steps);
180 | if (d-m > 127 || d-m < 0) return;
181 | if (v > 127 || v < 0) return;
182 | if (s->change) {
183 | if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
184 | s->d = d; s->v = v; s->change = 2; // >1 => initialized
185 | }
186 | msg[1] = d;
187 | msg[2] = v;
188 | } else if (!index) {
189 | msg[2] = dataval(step, 0, 127);
190 | } else {
191 | msg[2] = 0;
192 | }
193 | break;
194 | case 0xa0:
195 | if (dir) {
196 | // increment (dir==1) or decrement (dir==-1) the current value,
197 | // clamping it to the 0..127 data byte range
198 | if (!step) step = 1;
199 | dir *= step;
200 | if (dir > 0) {
201 | if (kpvalue[portno][chan][data] >= 127) return;
202 | kpvalue[portno][chan][data] += dir;
203 | if (kpvalue[portno][chan][data] > 127) kpvalue[portno][chan][data] = 127;
204 | } else {
205 | if (kpvalue[portno][chan][data] == 0) return;
206 | kpvalue[portno][chan][data] += dir;
207 | if (kpvalue[portno][chan][data] < 0) kpvalue[portno][chan][data] = 0;
208 | }
209 | msg[2] = kpvalue[portno][chan][data];
210 | } else if (mod) {
211 | int q = swap?val%mod:val/mod, r = swap?val/mod:val%mod;
212 | int d = msg[1] + datavals(q, mod_step, mod_steps, mod_n_steps);
213 | int v = datavals(r, step, steps, n_steps);
214 | if (d > 127 || d < 0) return;
215 | if (v > 127 || v < 0) return;
216 | if (s->change) {
217 | if (s->change > 1 && s->d == d && s->v == v) return; // unchanged value
218 | s->d = d; s->v = v; s->change = 2; // >1 => initialized
219 | }
220 | msg[1] = d;
221 | msg[2] = v;
222 | } else if (!index) {
223 | msg[2] = dataval(step, 0, 127);
224 | } else {
225 | msg[2] = 0;
226 | }
227 | break;
228 | case 0xd0:
229 | if (dir) {
230 | // increment (dir==1) or decrement (dir==-1) the current value,
231 | // clamping it to the 0..127 data byte range
232 | if (!step) step = 1;
233 | dir *= step;
234 | if (dir > 0) {
235 | if (cpvalue[portno][chan] >= 127) return;
236 | cpvalue[portno][chan] += dir;
237 | if (cpvalue[portno][chan] > 127) cpvalue[portno][chan] = 127;
238 | } else {
239 | if (cpvalue[portno][chan] == 0) return;
240 | cpvalue[portno][chan] += dir;
241 | if (cpvalue[portno][chan] < 0) cpvalue[portno][chan] = 0;
242 | }
243 | msg[1] = cpvalue[portno][chan];
244 | } else if (mod) {
245 | int v = datavals(swap?val/mod:val%mod, step, steps, n_steps);
246 | if (v > 127 || v < 0) return;
247 | if (s->change) {
248 | if (s->change > 1 && s->v == v) return; // unchanged value
249 | s->v = v; s->change = 2; // >1 => initialized
250 | }
251 | msg[1] = v;
252 | } else if (!index) {
253 | msg[1] = dataval(step, 0, 127);
254 | } else {
255 | msg[1] = 0;
256 | }
257 | break;
258 | case 0xe0: {
259 | // pitch bends are treated similarly to a controller, but with a 14 bit
260 | // range (0..16383, with 8192 being the center value)
261 | int pbval = 0;
262 | if (dir) {
263 | if (!step) step = 1;
264 | dir *= step;
265 | if (dir > 0) {
266 | if (pbvalue[portno][chan] >= 16383) return;
267 | pbvalue[portno][chan] += dir;
268 | if (pbvalue[portno][chan] > 16383) pbvalue[portno][chan] = 16383;
269 | } else {
270 | if (pbvalue[portno][chan] == 0) return;
271 | pbvalue[portno][chan] += dir;
272 | if (pbvalue[portno][chan] < 0) pbvalue[portno][chan] = 0;
273 | }
274 | pbval = pbvalue[portno][chan];
275 | } else if (mod) {
276 | int v = datavals(swap?val/mod:val%mod, step, steps, n_steps);
277 | if (v > 16383 || v < 0) return;
278 | if (s->change) {
279 | if (s->change > 1 && s->v == v) return; // unchanged value
280 | s->v = v; s->change = 2; // >1 => initialized
281 | }
282 | pbval = v;
283 | } else if (!index) {
284 | pbval = 8192+dataval(step, -8192, 8191);
285 | } else {
286 | // we use 8192 (center) as the "home" (a.k.a. "off") value, so the pitch
287 | // will only bend up, never down below the center value
288 | pbval = 8192;
289 | }
290 | // the result is a 14 bit value which gets encoded as a combination of two
291 | // 7 bit values which become the data bytes of the message
292 | msg[1] = pbval & 0x7f; // LSB (lower 7 bits)
293 | msg[2] = pbval >> 7; // MSB (upper 7 bits)
294 | break;
295 | }
296 | case 0xc0:
297 | if (mod) {
298 | int d = msg[1] + datavals(swap?val%mod:val/mod, mod_step, mod_steps, mod_n_steps);
299 | if (d > 127 || d < 0) return;
300 | if (s->change) {
301 | if (s->change > 1 && s->d == d) return; // unchanged value
302 | s->d = d; s->change = 2; // >1 => initialized
303 | }
304 | msg[1] = d;
305 | }
306 | // just send the message
307 | break;
308 | default:
309 | return;
310 | }
311 | if (ret_msg) memcpy(ret_msg, msg, 3);
312 | if (recursive) {
313 | // As these values may be mutated, we need to save and restore them, in
314 | // case a macro calls itself recursively.
315 | uint8_t change = s->change;
316 | int d = s->d, v = s->v;
317 | s->change = change>0;
318 | handle_event(msg, portno, depth+1, recursive);
319 | s->change = change;
320 | s->d = d; s->v = v;
321 | } else
322 | queue_midi(&seq, msg, portno);
323 | }
324 |
325 | static int stroke_data_cmp(const void *a, const void *b)
326 | {
327 | const stroke_data *ad = (const stroke_data*)a;
328 | const stroke_data *bd = (const stroke_data*)b;
329 | if (ad->chan == bd->chan)
330 | return ad->data - bd->data;
331 | else
332 | return ad->chan - bd->chan;
333 | }
334 |
335 | static stroke *find_stroke_data(stroke_data *sd,
336 | int chan, int data, int index,
337 | int *step, int *n_steps, int **steps,
338 | int *incr, int *mod,
339 | uint16_t n)
340 | {
341 | if (n < 16) {
342 | // Linear search is presumably faster for small arrays, and we also avoid
343 | // function calls for doing the comparisons here. Not sure where it breaks
344 | // even with glibc's bsearch(), though (TODO: measure).
345 | uint16_t i;
346 | for (i = 0; i < n; i++) {
347 | if (sd[i].chan == chan && sd[i].data == data) {
348 | if (step) *step = sd[i].step[index];
349 | if (n_steps) *n_steps = sd[i].n_steps[index];
350 | if (steps) *steps = sd[i].steps[index];
351 | if (incr) *incr = sd[i].is_incr;
352 | if (mod) *mod = sd[i].mod;
353 | return sd[i].s[index];
354 | } else if (sd[i].chan > chan ||
355 | (sd[i].chan == chan && sd[i].data > data)) {
356 | return NULL;
357 | }
358 | }
359 | return NULL;
360 | } else {
361 | // binary search from libc
362 | stroke_data *ret, key;
363 | key.chan = chan; key.data = data;
364 | ret = bsearch(&key, sd, n, sizeof(stroke_data), stroke_data_cmp);
365 | if (ret) {
366 | if (step) *step = ret->step[index];
367 | if (n_steps) *n_steps = ret->n_steps[index];
368 | if (steps) *steps = ret->steps[index];
369 | if (incr) *incr = ret->is_incr;
370 | if (mod) *mod = ret->mod;
371 | return ret->s[index];
372 | } else
373 | return NULL;
374 | }
375 | }
376 |
377 | static stroke *find_note(translation *tr, int shift,
378 | int chan, int data, int index, int *mod,
379 | int *step, int *n_steps, int **steps)
380 | {
381 | return find_stroke_data(tr->note[shift], chan, data, index,
382 | step, n_steps, steps, 0, mod,
383 | tr->n_note[shift]);
384 | }
385 |
386 | static stroke *find_notes(translation *tr, int shift,
387 | int chan, int data, int index, int *step)
388 | {
389 | return find_stroke_data(tr->notes[shift], chan, data, index, step,
390 | 0, 0, 0, 0,
391 | tr->n_notes[shift]);
392 | }
393 |
394 | static stroke *find_pc(translation *tr, int shift,
395 | int chan, int data, int index)
396 | {
397 | return find_stroke_data(tr->pc[shift], chan, data, index, 0, 0, 0, 0, 0,
398 | tr->n_pc[shift]);
399 | }
400 |
401 | static stroke *find_cc(translation *tr, int shift,
402 | int chan, int data, int index, int *mod,
403 | int *step, int *n_steps, int **steps)
404 | {
405 | return find_stroke_data(tr->cc[shift], chan, data, index,
406 | step, n_steps, steps, 0, mod,
407 | tr->n_cc[shift]);
408 | }
409 |
410 | static stroke *find_ccs(translation *tr, int shift,
411 | int chan, int data, int index, int *step, int *incr)
412 | {
413 | return find_stroke_data(tr->ccs[shift], chan, data, index, step, 0, 0,
414 | incr, 0,
415 | tr->n_ccs[shift]);
416 | }
417 |
418 | static stroke *find_kp(translation *tr, int shift,
419 | int chan, int data, int index, int *mod,
420 | int *step, int *n_steps, int **steps)
421 | {
422 | return find_stroke_data(tr->kp[shift], chan, data, index,
423 | step, n_steps, steps, 0, mod,
424 | tr->n_kp[shift]);
425 | }
426 |
427 | static stroke *find_kps(translation *tr, int shift,
428 | int chan, int data, int index, int *step)
429 | {
430 | return find_stroke_data(tr->kps[shift], chan, data, index, step,
431 | 0, 0, 0, 0,
432 | tr->n_kps[shift]);
433 | }
434 |
435 | static stroke *find_cp(translation *tr, int shift,
436 | int chan, int index, int *mod,
437 | int *step, int *n_steps, int **steps)
438 | {
439 | return find_stroke_data(tr->cp[shift], chan, 0, index,
440 | step, n_steps, steps, 0, mod,
441 | tr->n_cp[shift]);
442 | }
443 |
444 | static stroke *find_cps(translation *tr, int shift,
445 | int chan, int index, int *step)
446 | {
447 | return find_stroke_data(tr->cps[shift], chan, 0, index, step,
448 | 0, 0, 0, 0,
449 | tr->n_cps[shift]);
450 | }
451 |
452 | static stroke *find_pb(translation *tr, int shift,
453 | int chan, int index, int *mod,
454 | int *step, int *n_steps, int **steps)
455 | {
456 | return find_stroke_data(tr->pb[shift], chan, 0, index,
457 | step, n_steps, steps, 0, mod,
458 | tr->n_pb[shift]);
459 | }
460 |
461 | static stroke *find_pbs(translation *tr, int shift,
462 | int chan, int index, int *step)
463 | {
464 | return find_stroke_data(tr->pbs[shift], chan, 0, index, step, 0, 0, 0, 0,
465 | tr->n_pbs[shift]);
466 | }
467 |
468 | stroke *
469 | fetch_stroke(translation *tr, uint8_t portno, int status, int chan, int data,
470 | int index, int dir, int *step, int *n_steps, int **steps,
471 | int *incr, int *mod)
472 | {
473 | if (tr && tr->portno == portno) {
474 | switch (status) {
475 | case 0x90:
476 | if (dir)
477 | return find_notes(tr, shift, chan, data, dir>0, step);
478 | else
479 | return find_note(tr, shift, chan, data, index, mod, step, n_steps, steps);
480 | case 0xc0:
481 | return find_pc(tr, shift, chan, data, index);
482 | case 0xb0:
483 | if (dir)
484 | return find_ccs(tr, shift, chan, data, dir>0, step, incr);
485 | else
486 | return find_cc(tr, shift, chan, data, index, mod, step, n_steps, steps);
487 | case 0xa0:
488 | if (dir)
489 | return find_kps(tr, shift, chan, data, dir>0, step);
490 | else
491 | return find_kp(tr, shift, chan, data, index, mod, step, n_steps, steps);
492 | case 0xd0:
493 | if (dir)
494 | return find_cps(tr, shift, chan, dir>0, step);
495 | else
496 | return find_cp(tr, shift, chan, index, mod, step, n_steps, steps);
497 | case 0xe0:
498 | if (dir)
499 | return find_pbs(tr, shift, chan, dir>0, step);
500 | else
501 | return find_pb(tr, shift, chan, index, mod, step, n_steps, steps);
502 | default:
503 | return NULL;
504 | }
505 | } else
506 | return NULL;
507 | }
508 |
509 | #define MAX_WINNAME_SIZE 1024
510 | static char last_window_name[MAX_WINNAME_SIZE];
511 | static char last_window_class[MAX_WINNAME_SIZE];
512 | static Window last_focused_window = 0;
513 | static translation *last_window_translation = NULL, *last_translation = NULL;
514 | static int last_window = 0;
515 |
516 | void reload_callback(void)
517 | {
518 | last_focused_window = 0;
519 | last_window_translation = last_translation = NULL;
520 | last_window = 0;
521 | }
522 |
523 | static void debug_section(translation *tr)
524 | {
525 | // we do some caching of the last printed translation here, so that we don't
526 | // print the same message twice
527 | if (debug_regex && (!last_window || tr != last_translation)) {
528 | last_translation = tr;
529 | last_window = 1;
530 | if (tr) {
531 | printf("translation: %s for %s (class %s)\n",
532 | tr->name, last_window_name, last_window_class);
533 | } else {
534 | printf("no translation found for %s (class %s)\n",
535 | last_window_name, last_window_class);
536 | }
537 | }
538 | }
539 |
540 | static char *note_name(int n)
541 | {
542 | static char *note_names[] = { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" };
543 | if (n < 0 && n%12)
544 | return note_names[12+n%12];
545 | else
546 | return note_names[n%12];
547 | }
548 |
549 | static int note_octave(int n)
550 | {
551 | if (n < 0 && n%12)
552 | return n/12-1 + midi_octave;
553 | else
554 | return n/12 + midi_octave;
555 | }
556 |
557 | static char *debug_key(translation *tr, char *name,
558 | int status, int chan, int data, int dir)
559 | {
560 | char prefix[10] = "";
561 | if (shift) sprintf(prefix, "%d^", shift);
562 | char *suffix = "";
563 | strcpy(name, "??");
564 | switch (status) {
565 | case 0x90: {
566 | int mod = 0, step, n_steps, *steps;
567 | if (tr) {
568 | if (dir) {
569 | step = 1;
570 | (void)find_notes(tr, shift, chan, data, dir>0, &step);
571 | } else
572 | (void)find_note(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps);
573 | }
574 | if (dir)
575 | suffix = (dir<0)?"-":"+";
576 | else
577 | suffix = "";
578 | if (dir && step != 1)
579 | sprintf(name, "%s%s%d[%d]-%d%s", prefix, note_name(data),
580 | note_octave(data), step, chan+1, suffix);
581 | else if (!dir && mod)
582 | if (step != 1)
583 | sprintf(name, "%s%s%d[%d][%d]-%d%s", prefix, note_name(data),
584 | note_octave(data), mod, step, chan+1, suffix);
585 | else if (n_steps) {
586 | sprintf(name, "%s%s%d[%d]{", prefix, note_name(data),
587 | note_octave(data), mod);
588 | int l = strlen(name);
589 | for (int i = 0; i < n_steps; i++, (l = strlen(name)))
590 | sprintf(name+l, "%s%d", i?",":"", steps[i]);
591 | sprintf(name+l, "}-%d%s", chan+1, suffix);
592 | } else
593 | sprintf(name, "%s%s%d[%d]-%d%s", prefix, note_name(data),
594 | note_octave(data), mod, chan+1, suffix);
595 | else
596 | sprintf(name, "%s%s%d-%d%s", prefix, note_name(data),
597 | note_octave(data), chan+1, suffix);
598 | break;
599 | }
600 | case 0xa0: {
601 | int step = 0, n_steps = 0, *steps = 0, mod = 0;
602 | if (tr) {
603 | if (dir) {
604 | step = 1;
605 | (void)find_kps(tr, shift, chan, data, dir>0, &step);
606 | } else
607 | (void)find_kp(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps);
608 | }
609 | if (dir)
610 | suffix = (dir<0)?"-":"+";
611 | else
612 | suffix = "";
613 | if (dir && step != 1)
614 | sprintf(name, "%sKP:%s%d[%d]-%d%s", prefix, note_name(data),
615 | note_octave(data), step, chan+1, suffix);
616 | else if (!dir && mod)
617 | if (step != 1)
618 | sprintf(name, "%sKP:%s%d[%d][%d]-%d%s", prefix, note_name(data),
619 | note_octave(data), mod, step, chan+1, suffix);
620 | else if (n_steps) {
621 | sprintf(name, "%sKP:%s%d[%d]{", prefix, note_name(data),
622 | note_octave(data), mod);
623 | int l = strlen(name);
624 | for (int i = 0; i < n_steps; i++, (l = strlen(name)))
625 | sprintf(name+l, "%s%d", i?",":"", steps[i]);
626 | sprintf(name+l, "}-%d%s", chan+1, suffix);
627 | } else
628 | sprintf(name, "%sKP:%s%d[%d]-%d%s", prefix, note_name(data),
629 | note_octave(data), mod, chan+1, suffix);
630 | else
631 | sprintf(name, "%sKP:%s%d-%d%s", prefix, note_name(data),
632 | note_octave(data), chan+1, suffix);
633 | break;
634 | }
635 | case 0xb0: {
636 | int step = 0, n_steps = 0, *steps = 0, mod = 0, is_incr = 0;
637 | if (tr) {
638 | if (dir) {
639 | step = 1;
640 | (void)find_ccs(tr, shift, chan, data, dir>0, &step, &is_incr);
641 | } else
642 | (void)find_cc(tr, shift, chan, data, 0, &mod, &step, &n_steps, &steps);
643 | }
644 | if (is_incr)
645 | suffix = (dir<0)?"<":">";
646 | else if (dir)
647 | suffix = (dir<0)?"-":"+";
648 | else
649 | suffix = "";
650 | // check for pseudo CC messages denoting a macro
651 | char *tok = data>=128?"M":"CC";
652 | data %= 128;
653 | if (dir && step != 1)
654 | sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, step, chan+1, suffix);
655 | else if (!dir && mod)
656 | if (step != 1)
657 | sprintf(name, "%s%s%d[%d][%d]-%d%s", prefix, tok, data, mod, step, chan+1, suffix);
658 | else if (n_steps) {
659 | sprintf(name, "%s%s%d[%d]{", prefix, tok, data, mod);
660 | int l = strlen(name);
661 | for (int i = 0; i < n_steps; i++, (l = strlen(name)))
662 | sprintf(name+l, "%s%d", i?",":"", steps[i]);
663 | sprintf(name+l, "}-%d%s", chan+1, suffix);
664 | } else
665 | sprintf(name, "%s%s%d[%d]-%d%s", prefix, tok, data, mod, chan+1, suffix);
666 | else
667 | sprintf(name, "%s%s%d-%d%s", prefix, tok, data, chan+1, suffix);
668 | break;
669 | }
670 | case 0xc0:
671 | sprintf(name, "%sPC%d-%d", prefix, data, chan+1);
672 | break;
673 | case 0xd0: {
674 | int step = 0, n_steps = 0, *steps = 0, mod = 0;
675 | if (tr) {
676 | if (dir) {
677 | step = 1;
678 | (void)find_cps(tr, shift, chan, dir>0, &step);
679 | } else
680 | (void)find_cp(tr, shift, chan, 0, &mod, &step, &n_steps, &steps);
681 | }
682 | if (!dir)
683 | suffix = "";
684 | else
685 | suffix = (dir<0)?"-":"+";
686 | if (dir && step != 1)
687 | sprintf(name, "%sCP[%d]-%d%s", prefix, step, chan+1, suffix);
688 | else if (!dir && mod)
689 | if (step != 1)
690 | sprintf(name, "%sCP[%d][%d]-%d", prefix, mod, step, chan+1);
691 | else if (n_steps) {
692 | sprintf(name, "%sCP[%d]{", prefix, mod);
693 | int l = strlen(name);
694 | for (int i = 0; i < n_steps; i++, (l = strlen(name)))
695 | sprintf(name+l, "%s%d", i?",":"", steps[i]);
696 | sprintf(name+l, "}-%d", chan);
697 | } else
698 | sprintf(name, "%sCP[%d]-%d", prefix, mod, chan+1);
699 | else
700 | sprintf(name, "%sCP-%d%s", prefix, chan+1, suffix);
701 | break;
702 | }
703 | case 0xe0: {
704 | int step = 1;
705 | if (tr) (void)find_pbs(tr, shift, chan, dir>0, &step);
706 | if (!dir)
707 | suffix = "";
708 | else
709 | suffix = (dir<0)?"-":"+";
710 | if (dir && step != 1)
711 | sprintf(name, "%sPB[%d]-%d%s", prefix, step, chan+1, suffix);
712 | else
713 | sprintf(name, "%sPB-%d%s", prefix, chan+1, suffix);
714 | break;
715 | }
716 | default: // this can't happen
717 | break;
718 | }
719 | return name;
720 | }
721 |
722 | static void debug_input(int portno,
723 | int status, int chan, int data, int data2)
724 | {
725 | char name[100];
726 | if (status == 0xe0)
727 | // translate LSB,MSB to a pitch bend value in the range -8192..8191
728 | data2 = ((data2 << 7) | data) - 8192;
729 | else if (status == 0xd0)
730 | data2 = data;
731 | if (status == 0xc0)
732 | printf("[%d] %s\n", portno,
733 | debug_key(0, name, status, chan, data, 0));
734 | else if (status != 0xf0) // system messages ignored for now
735 | printf("[%d] %s value = %d\n", portno,
736 | debug_key(0, name, status, chan, data, 0), data2);
737 | }
738 |
739 | // Some machinery to handle the debugging of section matches. This is
740 | // necessary since some inputs may generate a lot of calls to send_strokes()
741 | // without ever actually matching any output sequence at all. In such cases we
742 | // want to prevent a cascade of useless debugging messages by handling the
743 | // message printing in a lazy manner.
744 |
745 | static int debug_state = 0, debug_count = 0;
746 | static translation *debug_tr = NULL;
747 |
748 | static void start_debug()
749 | {
750 | // start a debugging section
751 | debug_state = debug_regex;
752 | debug_tr = NULL;
753 | debug_count = 0;
754 | }
755 |
756 | static void end_debug()
757 | {
758 | // end a debugging section; if we still haven't matched an output sequence,
759 | // but processed any input at all, we print the last matched translation
760 | // section now anyway
761 | if (debug_state && debug_count) debug_section(debug_tr);
762 | debug_state = 0;
763 | }
764 |
765 | // maximum recursion depth
766 | #define MAX_DEPTH 32
767 |
768 | // shift feedback
769 | uint8_t shift_fb[N_SHIFTS][3];
770 |
771 | static int toggle_msg(uint8_t msg[3])
772 | {
773 | if (msg[0] < 0x80) return 0;
774 | switch (msg[0]&0xf0) {
775 | case 0xc0:
776 | return 0;
777 | case 0xd0:
778 | msg[1] = 0;
779 | break;
780 | case 0xe0:
781 | msg[1] = 0x40;
782 | msg[2] = 0;
783 | break;
784 | default:
785 | msg[2] = 0;
786 | break;
787 | }
788 | return 1;
789 | }
790 |
791 |
792 | int
793 | check_strokes(translation *tr, uint8_t portno, int status, int chan, int data)
794 | {
795 | for (int i = 0; i < 2; i++)
796 | if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
797 | fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
798 | return 1;
799 |
800 | if (jack_num_outputs) {
801 | // fall back to default MIDI translation
802 | tr = default_midi_translation[portno];
803 | for (int i = 0; i < 2; i++)
804 | if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
805 | fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
806 | return 1;
807 | // Ignore all MIDI input on the second port if no translation was found in
808 | // the [MIDI2] section (or the section is missing altogether).
809 | if (portno) return 0;
810 | }
811 |
812 | // fall back to the default translation
813 | tr = default_translation;
814 | for (int i = 0; i < 2; i++)
815 | if (fetch_stroke(tr, portno, status, chan, data, i, 0, 0,0,0,0,0) ||
816 | fetch_stroke(tr, portno, status, chan, data, 0, i?1:-1, 0,0,0,0,0))
817 | return 1;
818 | return 0;
819 | }
820 |
821 | void
822 | send_strokes(translation *tr, uint8_t portno, int status, int chan,
823 | int data, int data2, int index, int dir, int depth)
824 | {
825 | int nkeys = 0, step = 0, n_steps = 0, *steps = 0, is_incr = 0, mod = 0;
826 | stroke *s = fetch_stroke(tr, portno, status, chan, data, index, dir,
827 | &step, &n_steps, &steps, &is_incr, &mod);
828 | // If there's no press/release translation, check whether we have got at
829 | // least the corresponding release/press translation, in order to prevent
830 | // spurious error messages if either the press or release translation just
831 | // happens to be empty.
832 | int chk = s ||
833 | (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0));
834 |
835 | if (!s && jack_num_outputs) {
836 | // fall back to default MIDI translation
837 | tr = default_midi_translation[portno];
838 | s = fetch_stroke(tr, portno, status, chan, data, index, dir,
839 | &step, &n_steps, &steps, &is_incr, &mod);
840 | chk = chk || s ||
841 | (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0));
842 | // Ignore all MIDI input on the second port if no translation was found in
843 | // the [MIDI2] section (or the section is missing altogether).
844 | if (portno && !s) return;
845 | }
846 |
847 | if (!s) {
848 | // fall back to the default translation
849 | tr = default_translation;
850 | s = fetch_stroke(tr, portno, status, chan, data, index, dir,
851 | &step, &n_steps, &steps, &is_incr, &mod);
852 | chk = chk || s ||
853 | (!dir && fetch_stroke(tr, portno, status, chan, data, !index, dir, 0, 0, 0, 0, 0));
854 | }
855 |
856 | if (debug_regex) {
857 | if (s) {
858 | // found a sequence, print the matching section now
859 | debug_section(tr);
860 | debug_state = 0;
861 | } else if (!chk) {
862 | // No matches yet. To prevent a cascade of spurious messages, we defer
863 | // printing the matched section for now and just record it instead; it
864 | // may then be printed later.
865 | debug_tr = tr;
866 | // record that we actually tried to process some input
867 | debug_count = 1;
868 | }
869 | }
870 |
871 | if (s && debug_keys) {
872 | char name[100];
873 | print_stroke_sequence(debug_key(tr, name, status, chan, data, dir),
874 | (dir||mod)?"":index?"U":"D", s,
875 | mod, step, n_steps, steps, data2);
876 | }
877 | while (s) {
878 | if (s->keysym) {
879 | send_key(s->keysym, s->press);
880 | nkeys++;
881 | } else if (s->shift) {
882 | // toggle shift status
883 | if (shift != s->shift) {
884 | if (shift) {
885 | // reset current shift feedback
886 | if (toggle_msg(shift_fb[shift-1]))
887 | queue_midi(&seq, shift_fb[shift-1], 1);
888 | memset(shift_fb[shift-1], 0, 3);
889 | }
890 | shift = s->shift;
891 | } else
892 | shift = 0;
893 | } else if (!s->status) {
894 | // do nothing (NOP)
895 | ;
896 | } else {
897 | if (s->recursive && depth >= MAX_DEPTH) {
898 | char name[100];
899 | if (tr && tr->name)
900 | fprintf(stderr, "Error: [%s]$%s: recursion too deep\n",
901 | tr->name, debug_key(tr, name, status, chan, data, dir));
902 | else
903 | fprintf(stderr, "Error: $%s: recursion too deep\n",
904 | debug_key(tr, name, status, chan, data, dir));
905 | } else if (s->feedback) {
906 | if (!s->recursive && jack_num_outputs > 1) {
907 | if (s->feedback == 1)
908 | // direct feedback, simply flip the port number
909 | send_midi(!portno, s, index, dir, mod,
910 | step, n_steps, steps, data2, depth, 0);
911 | else if (portno == 0 && !mod && !dir)
912 | // shift feedback, this only works with key translations right
913 | // now, and portno *must* be zero
914 | send_midi(1, s, !shift, 0, 0,
915 | step, n_steps, steps, data2, depth,
916 | shift?shift_fb[shift-1]:0);
917 | }
918 | } else {
919 | send_midi(portno, s, index, dir, mod,
920 | step, n_steps, steps, data2, depth, 0);
921 | }
922 | }
923 | s = s->next;
924 | }
925 | // no need to flush the display if we didn't send any keys
926 | if (nkeys) {
927 | XFlush(display);
928 | }
929 | }
930 |
931 | char *
932 | get_window_name(Window win)
933 | {
934 | Atom prop = XInternAtom(display, "WM_NAME", False);
935 | Atom type;
936 | int form;
937 | unsigned long remain, len;
938 | unsigned char *list;
939 |
940 | if (XGetWindowProperty(display, win, prop, 0, 1024, False,
941 | AnyPropertyType, &type, &form, &len, &remain,
942 | &list) != Success) {
943 | fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n", (int)win);
944 | return NULL;
945 | }
946 |
947 | return (char*)list;
948 | }
949 |
950 | char *
951 | get_window_class(Window win)
952 | {
953 | Atom prop = XInternAtom(display, "WM_CLASS", False);
954 | Atom type;
955 | int form;
956 | unsigned long remain, len;
957 | unsigned char *list;
958 |
959 | if (XGetWindowProperty(display, win, prop, 0, 1024, False,
960 | AnyPropertyType, &type, &form, &len, &remain,
961 | &list) != Success) {
962 | fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n", (int)win);
963 | return NULL;
964 | }
965 |
966 | return (char*)list;
967 | }
968 |
969 | char *
970 | walk_window_tree(Window win, char **window_class)
971 | {
972 | char *window_name;
973 | Window root = 0;
974 | Window parent;
975 | Window *children;
976 | unsigned int nchildren;
977 |
978 | while (win != root) {
979 | window_name = get_window_name(win);
980 | if (window_name != NULL) {
981 | *window_class = get_window_class(win);
982 | return window_name;
983 | }
984 | if (XQueryTree(display, win, &root, &parent, &children, &nchildren)) {
985 | win = parent;
986 | XFree(children);
987 | } else {
988 | fprintf(stderr, "XQueryTree failed for window 0x%x\n", (int)win);
989 | return NULL;
990 | }
991 | }
992 | return NULL;
993 | }
994 |
995 | translation *
996 | get_focused_window_translation()
997 | {
998 | Window focus;
999 | int revert_to;
1000 | char *window_name = NULL, *window_class = NULL;
1001 |
1002 | XGetInputFocus(display, &focus, &revert_to);
1003 | if (focus != last_focused_window) {
1004 | last_window = 0;
1005 | last_focused_window = focus;
1006 | window_name = walk_window_tree(focus, &window_class);
1007 | last_window_translation = get_translation(window_name, window_class);
1008 | if (window_name && *window_name) {
1009 | strncpy(last_window_name, window_name, MAX_WINNAME_SIZE);
1010 | last_window_name[MAX_WINNAME_SIZE-1] = 0;
1011 | } else {
1012 | strcpy(last_window_name, "Unnamed");;
1013 | }
1014 | if (window_class && *window_class) {
1015 | strncpy(last_window_class, window_class, MAX_WINNAME_SIZE);
1016 | last_window_class[MAX_WINNAME_SIZE-1] = 0;
1017 | } else {
1018 | strcpy(last_window_name, "Unnamed");;
1019 | }
1020 | if (window_name != NULL) {
1021 | XFree(window_name);
1022 | }
1023 | if (window_class != NULL) {
1024 | XFree(window_class);
1025 | }
1026 | }
1027 | return last_window_translation;
1028 | }
1029 |
1030 | static int8_t innotevalue[2][16][128];
1031 | static int8_t inccvalue[2][16][128];
1032 | static int8_t inkpvalue[2][16][128];
1033 | static int8_t incpvalue[2][16];
1034 | static int16_t inpbvalue[2][16] =
1035 | {{8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
1036 | 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192},
1037 | {8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192,
1038 | 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192}};
1039 |
1040 | // If this option is enabled (-k on the command line), we make sure that each
1041 | // "key" (note, cc, pb) is "off" before we allow it to go "on" again. This is
1042 | // useful to eliminate double note-ons and the like, but interferes with the
1043 | // way some controllers work, so it is disabled by default.
1044 |
1045 | static int keydown_tracker = 0;
1046 |
1047 | static uint8_t innotedown[2][16][128];
1048 | static uint8_t inccdown[2][16][128];
1049 | static uint8_t inpbdown[2][16];
1050 | static uint8_t inkpdown[2][16][128];
1051 | static uint8_t incpdown[2][16];
1052 |
1053 | int
1054 | check_notes(translation *tr, uint8_t portno, int chan, int data)
1055 | {
1056 | if (tr && tr->portno == portno &&
1057 | (find_notes(tr, shift, chan, data, 0, 0) ||
1058 | find_notes(tr, shift, chan, data, 1, 0)))
1059 | return 1;
1060 | tr = default_midi_translation[portno];
1061 | if (tr && tr->portno == portno &&
1062 | (find_notes(tr, shift, chan, data, 0, 0) ||
1063 | find_notes(tr, shift, chan, data, 1, 0)))
1064 | return 1;
1065 | tr = default_translation;
1066 | if (tr && tr->portno == portno &&
1067 | (find_notes(tr, shift, chan, data, 0, 0) ||
1068 | find_notes(tr, shift, chan, data, 1, 0)))
1069 | return 1;
1070 | return 0;
1071 | }
1072 |
1073 | int
1074 | get_note_step(translation *tr, uint8_t portno, int chan, int data, int dir)
1075 | {
1076 | int step;
1077 | if (tr && tr->portno == portno &&
1078 | find_notes(tr, shift, chan, data, dir>0, &step))
1079 | return step;
1080 | tr = default_midi_translation[portno];
1081 | if (tr && tr->portno == portno &&
1082 | find_notes(tr, shift, chan, data, dir>0, &step))
1083 | return step;
1084 | tr = default_translation;
1085 | if (tr && tr->portno == portno &&
1086 | find_notes(tr, shift, chan, data, dir>0, &step))
1087 | return step;
1088 | return 1;
1089 | }
1090 |
1091 | int
1092 | get_note_mod(translation *tr, uint8_t portno, int chan, int data)
1093 | {
1094 | int mod;
1095 | if (tr && tr->portno == portno &&
1096 | find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1097 | return mod;
1098 | tr = default_midi_translation[portno];
1099 | if (tr && tr->portno == portno &&
1100 | find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1101 | return mod;
1102 | tr = default_translation;
1103 | if (tr && tr->portno == portno &&
1104 | find_note(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1105 | return mod;
1106 | return 0;
1107 | }
1108 |
1109 | int
1110 | check_incr(translation *tr, uint8_t portno, int chan, int data)
1111 | {
1112 | int is_incr;
1113 | if (tr && tr->portno == portno &&
1114 | (find_ccs(tr, shift, chan, data, 0, 0, &is_incr) ||
1115 | find_ccs(tr, shift, chan, data, 1, 0, &is_incr)))
1116 | return is_incr;
1117 | tr = default_midi_translation[portno];
1118 | if (tr && tr->portno == portno &&
1119 | (find_ccs(tr, shift, chan, data, 0, 0, &is_incr) ||
1120 | find_ccs(tr, shift, chan, data, 1, 0, &is_incr)))
1121 | return is_incr;
1122 | tr = default_translation;
1123 | if (tr && tr->portno == portno &&
1124 | (find_ccs(tr, shift, chan, data, 0, 0, &is_incr) ||
1125 | find_ccs(tr, shift, chan, data, 1, 0, &is_incr)))
1126 | return is_incr;
1127 | return 0;
1128 | }
1129 |
1130 | int
1131 | check_ccs(translation *tr, uint8_t portno, int chan, int data)
1132 | {
1133 | if (tr && tr->portno == portno &&
1134 | (find_ccs(tr, shift, chan, data, 0, 0, 0) ||
1135 | find_ccs(tr, shift, chan, data, 1, 0, 0)))
1136 | return 1;
1137 | tr = default_midi_translation[portno];
1138 | if (tr && tr->portno == portno &&
1139 | (find_ccs(tr, shift, chan, data, 0, 0, 0) ||
1140 | find_ccs(tr, shift, chan, data, 1, 0, 0)))
1141 | return 1;
1142 | tr = default_translation;
1143 | if (tr && tr->portno == portno &&
1144 | (find_ccs(tr, shift, chan, data, 0, 0, 0) ||
1145 | find_ccs(tr, shift, chan, data, 1, 0, 0)))
1146 | return 1;
1147 | return 0;
1148 | }
1149 |
1150 | int
1151 | get_cc_step(translation *tr, uint8_t portno, int chan, int data, int dir)
1152 | {
1153 | int step;
1154 | if (tr && tr->portno == portno &&
1155 | find_ccs(tr, shift, chan, data, dir>0, &step, 0))
1156 | return step;
1157 | tr = default_midi_translation[portno];
1158 | if (tr && tr->portno == portno &&
1159 | find_ccs(tr, shift, chan, data, dir>0, &step, 0))
1160 | return step;
1161 | tr = default_translation;
1162 | if (tr && tr->portno == portno &&
1163 | find_ccs(tr, shift, chan, data, dir>0, &step, 0))
1164 | return step;
1165 | return 1;
1166 | }
1167 |
1168 | int
1169 | get_cc_mod(translation *tr, uint8_t portno, int chan, int data)
1170 | {
1171 | int mod;
1172 | if (tr && tr->portno == portno &&
1173 | find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1174 | return mod;
1175 | tr = default_midi_translation[portno];
1176 | if (tr && tr->portno == portno &&
1177 | find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1178 | return mod;
1179 | tr = default_translation;
1180 | if (tr && tr->portno == portno &&
1181 | find_cc(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1182 | return mod;
1183 | return 0;
1184 | }
1185 |
1186 | int
1187 | check_kps(translation *tr, uint8_t portno, int chan, int data)
1188 | {
1189 | if (tr && tr->portno == portno &&
1190 | (find_kps(tr, shift, chan, data, 0, 0) ||
1191 | find_kps(tr, shift, chan, data, 1, 0)))
1192 | return 1;
1193 | tr = default_midi_translation[portno];
1194 | if (tr && tr->portno == portno &&
1195 | (find_kps(tr, shift, chan, data, 0, 0) ||
1196 | find_kps(tr, shift, chan, data, 1, 0)))
1197 | return 1;
1198 | tr = default_translation;
1199 | if (tr && tr->portno == portno &&
1200 | (find_kps(tr, shift, chan, data, 0, 0) ||
1201 | find_kps(tr, shift, chan, data, 1, 0)))
1202 | return 1;
1203 | return 0;
1204 | }
1205 |
1206 | int
1207 | get_kp_step(translation *tr, uint8_t portno, int chan, int data, int dir)
1208 | {
1209 | int step;
1210 | if (tr && tr->portno == portno &&
1211 | find_kps(tr, shift, chan, data, dir>0, &step))
1212 | return step;
1213 | tr = default_midi_translation[portno];
1214 | if (tr && tr->portno == portno &&
1215 | find_kps(tr, shift, chan, data, dir>0, &step))
1216 | return step;
1217 | tr = default_translation;
1218 | if (tr && tr->portno == portno &&
1219 | find_kps(tr, shift, chan, data, dir>0, &step))
1220 | return step;
1221 | return 1;
1222 | }
1223 |
1224 | int
1225 | get_kp_mod(translation *tr, uint8_t portno, int chan, int data)
1226 | {
1227 | int mod;
1228 | if (tr && tr->portno == portno &&
1229 | find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1230 | return mod;
1231 | tr = default_midi_translation[portno];
1232 | if (tr && tr->portno == portno &&
1233 | find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1234 | return mod;
1235 | tr = default_translation;
1236 | if (tr && tr->portno == portno &&
1237 | find_kp(tr, shift, chan, data, 0, &mod, 0, 0, 0))
1238 | return mod;
1239 | return 0;
1240 | }
1241 |
1242 | int
1243 | check_cps(translation *tr, uint8_t portno, int chan)
1244 | {
1245 | if (tr && tr->portno == portno &&
1246 | (find_cps(tr, shift, chan, 0, 0) ||
1247 | find_cps(tr, shift, chan, 1, 0)))
1248 | return 1;
1249 | tr = default_midi_translation[portno];
1250 | if (tr && tr->portno == portno &&
1251 | (find_cps(tr, shift, chan, 0, 0) ||
1252 | find_cps(tr, shift, chan, 1, 0)))
1253 | return 1;
1254 | tr = default_translation;
1255 | if (tr && tr->portno == portno &&
1256 | (find_cps(tr, shift, chan, 0, 0) ||
1257 | find_cps(tr, shift, chan, 1, 0)))
1258 | return 1;
1259 | return 0;
1260 | }
1261 |
1262 | int
1263 | get_cp_step(translation *tr, uint8_t portno, int chan, int dir)
1264 | {
1265 | int step;
1266 | if (tr && tr->portno == portno &&
1267 | find_cps(tr, shift, chan, dir>0, &step))
1268 | return step;
1269 | tr = default_midi_translation[portno];
1270 | if (tr && tr->portno == portno &&
1271 | find_cps(tr, shift, chan, dir>0, &step))
1272 | return step;
1273 | tr = default_translation;
1274 | if (tr && tr->portno == portno &&
1275 | find_cps(tr, shift, chan, dir>0, &step))
1276 | return step;
1277 | return 1;
1278 | }
1279 |
1280 | int
1281 | get_cp_mod(translation *tr, uint8_t portno, int chan)
1282 | {
1283 | int mod;
1284 | if (tr && tr->portno == portno &&
1285 | find_cp(tr, shift, chan, 0, &mod, 0, 0, 0))
1286 | return mod;
1287 | tr = default_midi_translation[portno];
1288 | if (tr && tr->portno == portno &&
1289 | find_cp(tr, shift, chan, 0, &mod, 0, 0, 0))
1290 | return mod;
1291 | tr = default_translation;
1292 | if (tr && tr->portno == portno &&
1293 | find_cp(tr, shift, chan, 0, &mod, 0, 0, 0))
1294 | return mod;
1295 | return 0;
1296 | }
1297 |
1298 | int
1299 | check_pbs(translation *tr, uint8_t portno, int chan)
1300 | {
1301 | if (tr && tr->portno == portno &&
1302 | (find_pbs(tr, shift, chan, 0, 0) ||
1303 | find_pbs(tr, shift, chan, 1, 0)))
1304 | return 1;
1305 | tr = default_midi_translation[portno];
1306 | if (tr && tr->portno == portno &&
1307 | (find_pbs(tr, shift, chan, 0, 0) ||
1308 | find_pbs(tr, shift, chan, 1, 0)))
1309 | return 1;
1310 | tr = default_translation;
1311 | if (tr && tr->portno == portno &&
1312 | (find_pbs(tr, shift, chan, 0, 0) ||
1313 | find_pbs(tr, shift, chan, 1, 0)))
1314 | return 1;
1315 | return 0;
1316 | }
1317 |
1318 | int
1319 | get_pb_step(translation *tr, uint8_t portno, int chan, int dir)
1320 | {
1321 | int step;
1322 | if (tr && tr->portno == portno &&
1323 | find_pbs(tr, shift, chan, dir>0, &step))
1324 | return step;
1325 | tr = default_midi_translation[portno];
1326 | if (tr && tr->portno == portno &&
1327 | find_pbs(tr, shift, chan, dir>0, &step))
1328 | return step;
1329 | tr = default_translation;
1330 | if (tr && tr->portno == portno &&
1331 | find_pbs(tr, shift, chan, dir>0, &step))
1332 | return step;
1333 | return 1;
1334 | }
1335 |
1336 | int
1337 | get_pb_mod(translation *tr, uint8_t portno, int chan)
1338 | {
1339 | int mod;
1340 | if (tr && tr->portno == portno &&
1341 | find_pb(tr, shift, chan, 0, &mod, 0, 0, 0))
1342 | return mod;
1343 | tr = default_midi_translation[portno];
1344 | if (tr && tr->portno == portno &&
1345 | find_pb(tr, shift, chan, 0, &mod, 0, 0, 0))
1346 | return mod;
1347 | tr = default_translation;
1348 | if (tr && tr->portno == portno &&
1349 | find_pb(tr, shift, chan, 0, &mod, 0, 0, 0))
1350 | return mod;
1351 | return 0;
1352 | }
1353 |
1354 | static int
1355 | check_recursive(int status, int chan, int data, int recursive)
1356 | {
1357 | // only mod translations can be used in recursive calls
1358 | if (recursive) {
1359 | char name[100];
1360 | fprintf(stderr, "Warning: $%s: undefined macro\n",
1361 | debug_key(0, name, status, chan, data, 0));
1362 | }
1363 | return recursive;
1364 | }
1365 |
1366 | void
1367 | handle_event(uint8_t *msg, uint8_t portno, int depth, int recursive)
1368 | {
1369 | translation *tr = get_focused_window_translation();
1370 |
1371 | //fprintf(stderr, "midi [%d]: %0x %0x %0x\n", portno, msg[0], msg[1], msg[2]);
1372 | int status = msg[0] & 0xf0, chan = msg[0] & 0x0f;
1373 | if (status == 0x80) {
1374 | // convert proper note-off to note-on with vel. 0
1375 | status = 0x90;
1376 | msg[0] = status | chan;
1377 | msg[2] = 0;
1378 | }
1379 | if (debug_midi && depth == 0)
1380 | debug_input(portno, status, chan, msg[1], msg[2]);
1381 | if (passthrough[portno] &&
1382 | !check_strokes(tr, portno, status, chan, status>=0xd0?0:msg[1])) {
1383 | queue_midi(&seq, msg, portno);
1384 | return;
1385 | }
1386 | switch (status) {
1387 | case 0xc0:
1388 | start_debug();
1389 | if (check_recursive(status, chan, msg[1], recursive)) break;
1390 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, 0, depth);
1391 | send_strokes(tr, portno, status, chan, msg[1], 0, 1, 0, depth);
1392 | end_debug();
1393 | break;
1394 | case 0xb0:
1395 | if (auto_feedback) ccvalue[!portno][chan][msg[1]] = msg[2];
1396 | start_debug();
1397 | if (get_cc_mod(tr, portno, chan, msg[1])) {
1398 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1399 | end_debug();
1400 | break;
1401 | }
1402 | if (check_recursive(status, chan, msg[1], recursive)) break;
1403 | if (msg[2]) {
1404 | if (!keydown_tracker || !inccdown[portno][chan][msg[1]]) {
1405 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1406 | inccdown[portno][chan][msg[1]] = 1;
1407 | }
1408 | } else {
1409 | if (!keydown_tracker || inccdown[portno][chan][msg[1]]) {
1410 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
1411 | inccdown[portno][chan][msg[1]] = 0;
1412 | }
1413 | }
1414 | if (check_incr(tr, portno, chan, msg[1])) {
1415 | debug_count = 0;
1416 | // Incremental controller a la MCU. NB: This assumes a signed bit
1417 | // representation (values above 0x40 indicate counter-clockwise
1418 | // rotation), which seems to be what most DAWs expect nowadays.
1419 | if (msg[2] < 64) {
1420 | int step = get_cc_step(tr, portno, chan, msg[1], -1);
1421 | if (step) {
1422 | int d = msg[2]/step;
1423 | while (d) {
1424 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, 1, depth);
1425 | d--;
1426 | }
1427 | }
1428 | } else if (msg[2] > 64) {
1429 | int step = get_cc_step(tr, portno, chan, msg[1], -1);
1430 | if (step) {
1431 | int d = (msg[2]-64)/step;
1432 | while (d) {
1433 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, -1, depth);
1434 | d--;
1435 | }
1436 | }
1437 | }
1438 | } else if (check_ccs(tr, portno, chan, msg[1]) &&
1439 | inccvalue[portno][chan][msg[1]] != msg[2]) {
1440 | debug_count = 0;
1441 | int dir = inccvalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
1442 | int step = get_cc_step(tr, portno, chan, msg[1], dir);
1443 | if (step) {
1444 | while (inccvalue[portno][chan][msg[1]] != msg[2]) {
1445 | int d = abs(inccvalue[portno][chan][msg[1]] - msg[2]);
1446 | if (d > step) d = step;
1447 | if (d < step) break;
1448 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
1449 | inccvalue[portno][chan][msg[1]] += dir*d;
1450 | }
1451 | }
1452 | }
1453 | end_debug();
1454 | break;
1455 | case 0x90:
1456 | if (auto_feedback) notevalue[!portno][chan][msg[1]] = msg[2];
1457 | start_debug();
1458 | if (get_note_mod(tr, portno, chan, msg[1])) {
1459 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1460 | end_debug();
1461 | break;
1462 | }
1463 | if (check_recursive(status, chan, msg[1], recursive)) break;
1464 | if (msg[2]) {
1465 | if (!keydown_tracker || !innotedown[portno][chan][msg[1]]) {
1466 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1467 | innotedown[portno][chan][msg[1]] = 1;
1468 | }
1469 | } else {
1470 | if (!keydown_tracker || innotedown[portno][chan][msg[1]]) {
1471 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
1472 | innotedown[portno][chan][msg[1]] = 0;
1473 | }
1474 | }
1475 | if (check_notes(tr, portno, chan, msg[1]) &&
1476 | innotevalue[portno][chan][msg[1]] != msg[2]) {
1477 | debug_count = 0;
1478 | int dir = innotevalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
1479 | int step = get_note_step(tr, portno, chan, msg[1], dir);
1480 | if (step) {
1481 | while (innotevalue[portno][chan][msg[1]] != msg[2]) {
1482 | int d = abs(innotevalue[portno][chan][msg[1]] - msg[2]);
1483 | if (d > step) d = step;
1484 | if (d < step) break;
1485 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
1486 | innotevalue[portno][chan][msg[1]] += dir*d;
1487 | }
1488 | }
1489 | }
1490 | end_debug();
1491 | break;
1492 | case 0xa0:
1493 | if (auto_feedback) kpvalue[!portno][chan][msg[1]] = msg[2];
1494 | start_debug();
1495 | if (get_kp_mod(tr, portno, chan, msg[1])) {
1496 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1497 | end_debug();
1498 | break;
1499 | }
1500 | if (check_recursive(status, chan, msg[1], recursive)) break;
1501 | if (msg[2]) {
1502 | if (!keydown_tracker || !inkpdown[portno][chan][msg[1]]) {
1503 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 0, 0, depth);
1504 | inkpdown[portno][chan][msg[1]] = 1;
1505 | }
1506 | } else {
1507 | if (!keydown_tracker || inkpdown[portno][chan][msg[1]]) {
1508 | send_strokes(tr, portno, status, chan, msg[1], msg[2], 1, 0, depth);
1509 | inkpdown[portno][chan][msg[1]] = 0;
1510 | }
1511 | }
1512 | if (check_kps(tr, portno, chan, msg[1]) &&
1513 | inkpvalue[portno][chan][msg[1]] != msg[2]) {
1514 | debug_count = 0;
1515 | int dir = inkpvalue[portno][chan][msg[1]] > msg[2] ? -1 : 1;
1516 | int step = get_kp_step(tr, portno, chan, msg[1], dir);
1517 | if (step) {
1518 | while (inkpvalue[portno][chan][msg[1]] != msg[2]) {
1519 | int d = abs(inkpvalue[portno][chan][msg[1]] - msg[2]);
1520 | if (d > step) d = step;
1521 | if (d < step) break;
1522 | send_strokes(tr, portno, status, chan, msg[1], 0, 0, dir, depth);
1523 | inkpvalue[portno][chan][msg[1]] += dir*d;
1524 | }
1525 | }
1526 | }
1527 | end_debug();
1528 | break;
1529 | case 0xd0:
1530 | if (auto_feedback) cpvalue[!portno][chan] = msg[1];
1531 | start_debug();
1532 | if (get_cp_mod(tr, portno, chan)) {
1533 | send_strokes(tr, portno, status, chan, 0, msg[1], 0, 0, depth);
1534 | end_debug();
1535 | break;
1536 | }
1537 | if (check_recursive(status, chan, msg[1], recursive)) break;
1538 | if (msg[1]) {
1539 | if (!keydown_tracker || !incpdown[portno][chan]) {
1540 | send_strokes(tr, portno, status, chan, 0, 0, 0, 0, depth);
1541 | incpdown[portno][chan] = 1;
1542 | }
1543 | } else {
1544 | if (!keydown_tracker || incpdown[portno][chan]) {
1545 | send_strokes(tr, portno, status, chan, 0, 0, 1, 0, depth);
1546 | incpdown[portno][chan] = 0;
1547 | }
1548 | }
1549 | if (check_cps(tr, portno, chan) &&
1550 | incpvalue[portno][chan] != msg[1]) {
1551 | debug_count = 0;
1552 | int dir = incpvalue[portno][chan] > msg[1] ? -1 : 1;
1553 | int step = get_cp_step(tr, portno, chan, dir);
1554 | if (step) {
1555 | while (incpvalue[portno][chan] != msg[1]) {
1556 | int d = abs(incpvalue[portno][chan] - msg[1]);
1557 | if (d > step) d = step;
1558 | if (d < step) break;
1559 | send_strokes(tr, portno, status, chan, 0, 0, 0, dir, depth);
1560 | incpvalue[portno][chan] += dir*d;
1561 | }
1562 | }
1563 | }
1564 | end_debug();
1565 | break;
1566 | case 0xe0: {
1567 | int bend = ((msg[2] << 7) | msg[1]) - 8192;
1568 | if (auto_feedback) pbvalue[!portno][chan] = bend+8192;
1569 | start_debug();
1570 | if (get_pb_mod(tr, portno, chan)) {
1571 | send_strokes(tr, portno, status, chan, 0, bend+8192, 0, 0, depth);
1572 | end_debug();
1573 | break;
1574 | }
1575 | if (check_recursive(status, chan, msg[1], recursive)) break;
1576 | if (bend) {
1577 | if (!keydown_tracker || !inpbdown[portno][chan]) {
1578 | send_strokes(tr, portno, status, chan, 0, 0, 0, 0, depth);
1579 | inpbdown[portno][chan] = 1;
1580 | }
1581 | } else {
1582 | if (!keydown_tracker || inpbdown[portno][chan]) {
1583 | send_strokes(tr, portno, status, chan, 0, 0, 1, 0, depth);
1584 | inpbdown[portno][chan] = 0;
1585 | }
1586 | }
1587 | if (check_pbs(tr, portno, chan) && inpbvalue[portno][chan] - 8192 != bend) {
1588 | debug_count = 0;
1589 | int dir = inpbvalue[portno][chan] - 8192 > bend ? -1 : 1;
1590 | int step = get_pb_step(tr, portno, chan, dir);
1591 | if (step) {
1592 | while (inpbvalue[portno][chan] - 8192 != bend) {
1593 | int d = abs(inpbvalue[portno][chan] - 8192 - bend);
1594 | if (d > step) d = step;
1595 | if (d < step) break;
1596 | send_strokes(tr, portno, status, chan, 0, 0, 0, dir, depth);
1597 | inpbvalue[portno][chan] += dir*d;
1598 | }
1599 | }
1600 | }
1601 | end_debug();
1602 | break;
1603 | }
1604 | default:
1605 | // ignore everything else for now, specifically system messages
1606 | break;
1607 | }
1608 | }
1609 |
1610 | void help(char *progname)
1611 | {
1612 | fprintf(stderr, "Usage: %s [-hkn] [-d[rskmj]] [-ost[n]] [-j name] [-P[prio]] [[-r] rcfile]\n", progname);
1613 | fprintf(stderr, "-h print this message\n");
1614 | fprintf(stderr, "-d debug (r = regex, s = strokes, k = keys, m = midi, j = jack; default: all)\n");
1615 | fprintf(stderr, "-j jack client name (default: midizap)\n");
1616 | fprintf(stderr, "-k keep track of key status (ignore double on/off messages)\n");
1617 | fprintf(stderr, "-n no automatic feedback from the second port (-o2)\n");
1618 | fprintf(stderr, "-o set number of MIDI output ports (n = 0-2, default: 1)\n");
1619 | fprintf(stderr, "-P set real-time priority (default: 90)\n");
1620 | fprintf(stderr, "-r config file name (default: MIDIZAP_CONFIG_FILE variable or ~/.midizaprc)\n");
1621 | fprintf(stderr, "-s pass-through of system messages (n = 0-2; default: all ports)\n");
1622 | fprintf(stderr, "-t pass-through of untranslated messages (n = 0-2; default: all ports)\n");
1623 | }
1624 |
1625 | uint8_t quit = 0;
1626 |
1627 | void quitter()
1628 | {
1629 | quit = 1;
1630 | }
1631 |
1632 | // Helper functions to process the command line, so that we can pass it to
1633 | // Jack session management.
1634 |
1635 | static char *command_line;
1636 | static size_t len;
1637 |
1638 | static void add_command(char *arg, int sep)
1639 | {
1640 | char *a = arg;
1641 | // Do some simplistic quoting if the argument contains blanks. This won't do
1642 | // the right thing if the argument also contains quotes. Oh well.
1643 | if ((strchr(a, ' ') || strchr(a, '\t')) && !strchr(a, '"')) {
1644 | a = malloc(strlen(arg)+3);
1645 | sprintf(a, "\"%s\"", arg);
1646 | }
1647 | if (!command_line) {
1648 | len = strlen(a);
1649 | command_line = malloc(len+1);
1650 | strcpy(command_line, a);
1651 | } else {
1652 | size_t l = strlen(a)+sep;
1653 | command_line = realloc(command_line, len+l+1);
1654 | if (sep) command_line[len] = ' ';
1655 | strcpy(command_line+len+sep, a);
1656 | len += l;
1657 | }
1658 | if (a != arg) free(a);
1659 | }
1660 |
1661 | static char *absolute_path(char *name)
1662 | {
1663 | if (*name == '/') {
1664 | return name;
1665 | } else {
1666 | // This is a relative pathname, we turn it into a canonicalized absolute
1667 | // path. NOTE: This requires glibc. We should probably rewrite this code
1668 | // to be more portable.
1669 | char *pwd = getcwd(NULL, 0);
1670 | if (!pwd) {
1671 | perror("getcwd");
1672 | return name;
1673 | } else {
1674 | char *path = malloc(strlen(pwd)+strlen(name)+2);
1675 | static char abspath[PATH_MAX];
1676 | sprintf(path, "%s/%s", pwd, name);
1677 | if (!realpath(path, abspath)) strcpy(abspath, path);
1678 | free(path); free(pwd);
1679 | return abspath;
1680 | }
1681 | }
1682 | }
1683 |
1684 | // poll interval in microsec (this shouldn't be too large to avoid jitter)
1685 | #define POLL_INTERVAL 1000
1686 |
1687 | #include
1688 | #include
1689 |
1690 | int
1691 | main(int argc, char **argv)
1692 | {
1693 | uint8_t msg[3];
1694 | int opt, prio = 0;
1695 |
1696 | // Start recording the command line to be passed to Jack session management.
1697 | add_command(argv[0], 0);
1698 |
1699 | while ((opt = getopt(argc, argv, "hkno::d::j:r:P::s::t::")) != -1) {
1700 | switch (opt) {
1701 | case 'h':
1702 | help(argv[0]);
1703 | exit(0);
1704 | case 'k':
1705 | keydown_tracker = 1;
1706 | add_command("-k", 1);
1707 | break;
1708 | case 'n':
1709 | auto_feedback = 0;
1710 | add_command("-n", 1);
1711 | break;
1712 | case 'o':
1713 | jack_num_outputs = 1;
1714 | if (optarg && *optarg) {
1715 | const char *a = optarg;
1716 | if (!strcmp(a, "2")) {
1717 | jack_num_outputs = 2;
1718 | add_command("-o2", 1);
1719 | } else if (!strcmp(a, "1")) {
1720 | add_command("-o1", 1);
1721 | } else if (!strcmp(a, "0")) {
1722 | jack_num_outputs = -1; // override config setting
1723 | add_command("-o0", 1);
1724 | } else {
1725 | fprintf(stderr, "%s: wrong port number (-o), must be 0, 1 or 2\n", argv[0]);
1726 | fprintf(stderr, "Try -h for help.\n");
1727 | exit(1);
1728 | }
1729 | } else
1730 | add_command("-o", 1);
1731 | break;
1732 | case 'd':
1733 | if (optarg && *optarg) {
1734 | const char *a = optarg;
1735 | add_command("-d", 1);
1736 | add_command(optarg, 0);
1737 | while (*a) {
1738 | switch (*a) {
1739 | case 'r':
1740 | default_debug_regex = 1;
1741 | break;
1742 | case 's':
1743 | default_debug_strokes = 1;
1744 | break;
1745 | case 'k':
1746 | default_debug_keys = 1;
1747 | break;
1748 | case 'm':
1749 | default_debug_midi = 1;
1750 | break;
1751 | case 'j':
1752 | debug_jack = 1;
1753 | break;
1754 | default:
1755 | fprintf(stderr, "%s: unknown debugging option (-d), must be r, s, k or j\n", argv[0]);
1756 | fprintf(stderr, "Try -h for help.\n");
1757 | exit(1);
1758 | }
1759 | ++a;
1760 | }
1761 | } else {
1762 | default_debug_regex = default_debug_strokes = default_debug_keys =
1763 | default_debug_midi = 1;
1764 | debug_jack = 1;
1765 | add_command("-d", 1);
1766 | }
1767 | break;
1768 | case 'j':
1769 | jack_client_name = optarg;
1770 | add_command("-j", 1);
1771 | add_command(optarg, 1);
1772 | break;
1773 | case 'r':
1774 | config_file_name = optarg;
1775 | add_command("-r", 1);
1776 | // We need to convert this to an absolute pathname for Jack session
1777 | // management.
1778 | add_command(absolute_path(optarg), 1);
1779 | break;
1780 | case 'P':
1781 | prio = (optarg&&*optarg)?atoi(optarg):90;
1782 | if (prio > 0) {
1783 | add_command("-P", 1);
1784 | if (optarg&&*optarg) add_command(optarg, 0);
1785 | } else {
1786 | fprintf(stderr, "%s: invalid real-time priority (-P), must be a positive integer\n", argv[0]);
1787 | fprintf(stderr, "Try -h for help.\n");
1788 | exit(1);
1789 | }
1790 | break;
1791 | case 's':
1792 | if (optarg && *optarg) {
1793 | const char *a = optarg;
1794 | if (!strcmp(a, "2")) {
1795 | system_passthrough[0] = 0;
1796 | system_passthrough[1] = 1;
1797 | add_command("-s2", 1);
1798 | } else if (!strcmp(a, "1")) {
1799 | system_passthrough[0] = 1;
1800 | system_passthrough[1] = 0;
1801 | add_command("-s1", 1);
1802 | } else if (!strcmp(a, "0")) {
1803 | system_passthrough[0] = system_passthrough[1] = 0;
1804 | add_command("-s0", 1);
1805 | } else {
1806 | fprintf(stderr, "%s: wrong port number (-s), must be 0, 1 or 2\n", argv[0]);
1807 | fprintf(stderr, "Try -h for help.\n");
1808 | exit(1);
1809 | }
1810 | } else {
1811 | system_passthrough[0] = system_passthrough[1] = 1;
1812 | add_command("-s", 1);
1813 | }
1814 | break;
1815 | case 't':
1816 | if (optarg && *optarg) {
1817 | const char *a = optarg;
1818 | if (!strcmp(a, "2")) {
1819 | passthrough[0] = 0;
1820 | passthrough[1] = 1;
1821 | add_command("-t2", 1);
1822 | } else if (!strcmp(a, "1")) {
1823 | passthrough[0] = 1;
1824 | passthrough[1] = 0;
1825 | add_command("-t1", 1);
1826 | } else if (!strcmp(a, "0")) {
1827 | passthrough[0] = passthrough[1] = 0;
1828 | add_command("-t0", 1);
1829 | } else {
1830 | fprintf(stderr, "%s: wrong port number (-t), must be 0, 1 or 2\n", argv[0]);
1831 | fprintf(stderr, "Try -h for help.\n");
1832 | exit(1);
1833 | }
1834 | } else {
1835 | passthrough[0] = passthrough[1] = 1;
1836 | add_command("-t", 1);
1837 | }
1838 | break;
1839 | default:
1840 | fprintf(stderr, "Try -h for help.\n");
1841 | exit(1);
1842 | }
1843 | }
1844 |
1845 | if (optind+1 < argc) {
1846 | help(argv[0]);
1847 | exit(1);
1848 | }
1849 |
1850 | if (optind < argc) {
1851 | config_file_name = argv[optind];
1852 | add_command(absolute_path(argv[optind]), 1);
1853 | }
1854 |
1855 | if (command_line) jack_command_line = command_line;
1856 |
1857 | initdisplay();
1858 |
1859 | // Force the config file to be loaded initially, so that we pick up the Jack
1860 | // client name and number of output ports (if not set from the command
1861 | // line). This cannot be changed later, so if you want to make changes to
1862 | // the client name or number of ports take effect, you need to restart the
1863 | // program.
1864 | read_config_file();
1865 |
1866 | seq.client_name = jack_client_name;
1867 | seq.n_in = jack_num_outputs>1?jack_num_outputs:1;
1868 | seq.n_out = jack_num_outputs>0?jack_num_outputs:0;
1869 | seq.passthrough[0] = jack_num_outputs>0?system_passthrough[0]>0:0;
1870 | seq.passthrough[1] = jack_num_outputs>1?system_passthrough[1]>0:0;
1871 | seq.in[0] = jack_in_regex[0];
1872 | seq.in[1] = jack_num_outputs>1?jack_in_regex[1]:0;
1873 | seq.out[0] = jack_num_outputs>0?jack_out_regex[0]:0;
1874 | seq.out[1] = jack_num_outputs>1?jack_out_regex[1]:0;
1875 | if (!init_jack(&seq, debug_jack)) {
1876 | exit(1);
1877 | }
1878 |
1879 | passthrough[0] = jack_num_outputs>0?passthrough[0]>0:0;
1880 | passthrough[1] = jack_num_outputs>1?passthrough[1]>0:0;
1881 |
1882 | // set real-time scheduling priority if requested
1883 | if (prio) {
1884 | int pol = SCHED_RR; // other options: SCHED_FIFO, SCHED_OTHER
1885 | struct sched_param param;
1886 | memset(¶m, 0, sizeof(param));
1887 | param.sched_priority = prio;
1888 | if (pthread_setschedparam(pthread_self(), pol, ¶m))
1889 | perror("pthread_setschedparam");
1890 | }
1891 |
1892 | int do_flush = debug_regex || debug_strokes || debug_keys || debug_midi ||
1893 | debug_jack;
1894 | signal(SIGINT, quitter);
1895 | time_t t0 = time(0);
1896 | while (!quit) {
1897 | uint8_t portno;
1898 | if (jack_quit) {
1899 | printf("[jack %s, exiting]\n",
1900 | (jack_quit>0)?"asked us to quit":"shutting down");
1901 | close_jack(&seq);
1902 | exit(0);
1903 | }
1904 | process_connections(&seq);
1905 | while (pop_midi(&seq, msg, &portno)) {
1906 | handle_event(msg, portno, 0, 0);
1907 | time_t t = time(0);
1908 | if (t > t0) {
1909 | // Check whether to reload the config file every sec.
1910 | if (read_config_file()) last_focused_window = 0;
1911 | t0 = t;
1912 | }
1913 | }
1914 | usleep(POLL_INTERVAL);
1915 | time_t t = time(0);
1916 | if (t > t0) {
1917 | // Check again when polling.
1918 | if (read_config_file()) last_focused_window = 0;
1919 | t0 = t;
1920 | }
1921 | // Make sure that debugging output gets flushed every once in a while (may
1922 | // be buffered when midizap is running inside a QjackCtl session).
1923 | if (do_flush) fflush(NULL);
1924 | }
1925 | printf(" [exiting]\n");
1926 | close_jack(&seq);
1927 | }
1928 |
--------------------------------------------------------------------------------
/midizap.h:
--------------------------------------------------------------------------------
1 |
2 | // Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
3 | // Copyright 2018 Albert Graef
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 |
30 | // delay in ms before processing each XTest event
31 | // CurrentTime means no delay
32 | #define DELAY CurrentTime
33 |
34 | // we define these as extra KeySyms to represent mouse events
35 | #define XK_Button_0 0x2000000 // just an offset, not a real button
36 | #define XK_Button_1 0x2000001
37 | #define XK_Button_2 0x2000002
38 | #define XK_Button_3 0x2000003
39 | #define XK_Scroll_Up 0x2000004
40 | #define XK_Scroll_Down 0x2000005
41 |
42 | #define PRESS 1
43 | #define RELEASE 2
44 | #define PRESS_RELEASE 3
45 | #define HOLD 4
46 |
47 | typedef struct _stroke {
48 | struct _stroke *next;
49 | // nonzero keysym indicates a key event
50 | KeySym keysym;
51 | int8_t press; // zero -> release, non-zero -> press
52 | // nonzero value indicates a shift event
53 | int8_t shift;
54 | // keysym == shift == 0 => MIDI event
55 | int status, data; // status and, if applicable, first data byte
56 | int step; // step size (1, 127 or 8191 by default, depending on status)
57 | // discrete steps (for special "modulus" translations only)
58 | int n_steps, *steps;
59 | // the incremental bit indicates an incremental control change (typically
60 | // used with endless rotary encoders) to be represented as a sign bit value
61 | uint8_t incr;
62 | // the swap bit indicates that 1st and 2nd data byte are to be swapped in
63 | // mod translations
64 | uint8_t swap;
65 | // the change bit indicates that the message should only be output if its
66 | // value has changed since the last time
67 | uint8_t change;
68 | // cached values for the change bit
69 | int d, v;
70 | // the recursive bit indicates a MIDI message which is to be translated
71 | // recursively
72 | uint8_t recursive;
73 | // the feedback bit indicates a MIDI message which is to be sent back to
74 | // device or application (flipping the output port)
75 | uint8_t feedback;
76 | // the dirty bit indicates a MIDI event for which a release event still
77 | // needs to be generated in key events
78 | uint8_t dirty;
79 | } stroke;
80 |
81 | typedef struct _stroke_data {
82 | // key (MIDI channel and, for note/CC/PB, data byte)
83 | uint8_t chan, data;
84 | // stroke data, indexed by press/release or up/down index
85 | stroke *s[2];
86 | // step size
87 | int step[2], n_steps[2], *steps[2];
88 | // incr flag (CC only)
89 | uint8_t is_incr;
90 | // modulus
91 | uint16_t mod;
92 | // anyshift flag (default rule)
93 | uint8_t anyshift;
94 | } stroke_data;
95 |
96 | #define N_SHIFTS 4 // number of distinct shift states
97 | #define N_ST (N_SHIFTS+1)
98 |
99 | typedef struct _translation {
100 | struct _translation *next;
101 | char *name;
102 | int mode, is_default;
103 | regex_t regex;
104 | uint8_t portno;
105 | // these are indexed by shift status
106 | stroke_data *note[N_ST];
107 | stroke_data *notes[N_ST];
108 | stroke_data *pc[N_ST];
109 | stroke_data *cc[N_ST];
110 | stroke_data *ccs[N_ST];
111 | stroke_data *pb[N_ST];
112 | stroke_data *pbs[N_ST];
113 | stroke_data *kp[N_ST];
114 | stroke_data *kps[N_ST];
115 | stroke_data *cp[N_ST];
116 | stroke_data *cps[N_ST];
117 | // actual and allocated sizes (can be at most 16*128)
118 | uint16_t n_note[N_ST], n_notes[N_ST], n_pc[N_ST],
119 | n_cc[N_ST], n_ccs[N_ST], n_pb[N_ST], n_pbs[N_ST],
120 | n_kp[N_ST], n_kps[N_ST], n_cp[N_ST], n_cps[N_ST];
121 | uint16_t a_note[N_ST], a_notes[N_ST], a_pc[N_ST],
122 | a_cc[N_ST], a_ccs[N_ST], a_pb[N_ST], a_pbs[N_ST],
123 | a_kp[N_ST], a_kps[N_ST], a_cp[N_ST], a_cps[N_ST];
124 | } translation;
125 |
126 | extern void reload_callback(void);
127 | extern int read_config_file(void);
128 | extern translation *get_translation(char *win_title, char *win_class);
129 | extern void print_stroke_sequence(char *name, char *up_or_down, stroke *s,
130 | int mod, int step, int n_steps, int *steps,
131 | int val);
132 | extern translation *default_translation, *default_midi_translation[2];
133 | extern int debug_regex, debug_strokes, debug_keys, debug_midi;
134 | extern int default_debug_regex, default_debug_strokes, default_debug_keys,
135 | default_debug_midi;
136 | extern char *config_file_name;
137 | extern int jack_num_outputs, auto_feedback;
138 | extern int passthrough[2], system_passthrough[2];
139 | extern int midi_octave, shift;
140 | extern char *jack_client_name, *jack_in_regex[2], *jack_out_regex[2];
141 |
--------------------------------------------------------------------------------