├── .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 | --------------------------------------------------------------------------------