├── .gitignore ├── Makefile ├── docs ├── plan_midi_msgs.txt ├── pedal.txt ├── showframes.pl └── input_frame_format.txt ├── LICENSE.txt ├── my_atexit.h ├── myusb_utils.h ├── myusb_atexit.h ├── my_atexit.c ├── myusb_atexit.c ├── myusb_utils.c ├── README.md └── rb3_driver.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.o 3 | /rb3_driver 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LDLIBS += -lusb-1.0 -lportmidi 2 | 3 | all: rb3_driver 4 | 5 | rb3_driver: rb3_driver.o myusb_utils.o myusb_atexit.o my_atexit.o 6 | $(CC) -o $@ $(CCFLAGS) $(LDFLAGS) $^ $(LDLIBS) 7 | -------------------------------------------------------------------------------- /docs/plan_midi_msgs.txt: -------------------------------------------------------------------------------- 1 | Setup 2 | Set initial program 3 | 4 | Key bitmap/velocity levels for note messages 5 | Note On 6 | Note Off 7 | 8 | 1,2,A,B buttons for patch/octave 9 | Program Change 10 | (internal state for octave) 11 | 12 | Touch strip/button for expression/pitch bend 13 | Control Change 14 | Pitch Bend Change 15 | 16 | Up button for drum mapping 17 | (internal state) 18 | 19 | Active Sensing? 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Martin Sidaway 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /my_atexit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MY_ATEXIT_H 25 | #define MY_ATEXIT_H 1 26 | 27 | extern void my_atexit(void (*func)(void *), void *data); 28 | 29 | #endif /* MY_ATEXIT_H */ 30 | -------------------------------------------------------------------------------- /myusb_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MYUSB_UTILS_H 25 | #define MYUSB_UTILS_H 1 26 | 27 | #include 28 | 29 | extern libusb_device *myusb_get_device_by_prod_name_prefix(const char *prefix, int index); 30 | extern const struct libusb_endpoint_descriptor * 31 | myusb_get_endpoint(libusb_device *dev, uint8_t direction, 32 | uint8_t attrs_mask, uint8_t attrs, int index, 33 | uint8_t *interface_number); 34 | 35 | #endif /* MYUSB_UTILS_H */ 36 | -------------------------------------------------------------------------------- /myusb_atexit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MYUSB_ATEXIT_H 25 | #define MYUSB_ATEXIT_H 1 26 | 27 | #include 28 | 29 | #include "my_atexit.h" 30 | 31 | extern void myusb_unref_device(void *data); 32 | extern void myusb_exit(void *ignored); 33 | extern void myusb_close(void *data); 34 | extern void myusb_free_config_descriptor(void *data); 35 | extern void myusb_atexit_release_interface(libusb_device_handle *dev, int interface_number); 36 | 37 | #endif /* MYUSB_ATEXIT_H */ 38 | -------------------------------------------------------------------------------- /docs/pedal.txt: -------------------------------------------------------------------------------- 1 | Resistance values for RB3 pedal jack 2 | 3 | infinite 7F 4 | 7.5k 7F 5 | 6.8k 7D 6 | 5.6k 79 7 | 3k 67 8 | 1k 39 9 | 470 18 10 | 330 0A 11 | 270 04 12 | 220 00 (Expression) 13 | 150 00 (Exp) 14 | 56 00 (Exp) 15 | 27 00 (Exp) 16 | 7 00 (Exp) 17 | 4.7 00 (Exp + Pedal (Sustain)) 18 | 2.2 00 (Exp + Ped) 19 | 1 00 (Exp + Ped) 20 | 21 | Left: 1k: Expression [CC 11] (default) 22 | Down: 1k: Volume [CC 7] 23 | Right: 1k: Foot controller [CC 4] 24 | 25 | Left: 1: Exp + Pedal [CC 64] (default) 26 | Down: 1: Vol + Ped 27 | Right: 1: Foot + Ped 28 | 29 | As soon as it realises it is dealing with a digital stomp pedal, it resets the 30 | analog value to its rest state (so only Pedal (Sustain) is asserted). 31 | For analog, the rest state (in MIDI) is 7F (in all modes) 32 | For digital, the rest state (in MIDI) is 00, and active state is 7F 33 | 34 | Analog range: 220 - 7500/inf 35 | Digital range: 0 - 4.7 (it's basically looking for a short) 36 | 37 | When switching to a new type of analog pedal, it does _not_ reset the value of 38 | the old analog pedal. 39 | 40 | Byte 14 is the pedal! 41 | 00 - 7F analog (7F is rest state) 42 | FF digital stomp (or high bit is stomp, low bits indicate 43 | analog rest state) 44 | 45 | Byte 20 seems to be 46 | 00 if byte 14 == 7F 47 | 01 if byte 14 == FF (digital stomp) 48 | 02 if 00 < byte 14 < 7F 49 | 03 if byte 14 == 00 50 | 51 | Except that if the keyboard is disconnected, bytes 14 and 20 are both 00 (as is 52 | byte 25 - although this can become 00 anyway). 53 | 54 | -------------------------------------------------------------------------------- /my_atexit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "my_atexit.h" 29 | 30 | typedef struct my_atexit_data { 31 | void (*func)(void *); 32 | void *data; 33 | struct my_atexit_data *next; 34 | } my_atexit_data; 35 | 36 | static my_atexit_data *my_atexit_handlers = NULL; 37 | 38 | static void my_atexit_callback() { 39 | my_atexit_data *handler = NULL; 40 | while (my_atexit_handlers != NULL) { 41 | handler = my_atexit_handlers; 42 | my_atexit_handlers = my_atexit_handlers->next; 43 | handler->func(handler->data); 44 | free(handler); 45 | } 46 | } 47 | 48 | static int my_atexit_initialized = 0; 49 | 50 | void my_atexit(void (*func)(void *), void *data) { 51 | if (my_atexit_initialized == 0) { 52 | my_atexit_initialized = 1; 53 | atexit(my_atexit_callback); 54 | } 55 | my_atexit_data *new_handler = 56 | (my_atexit_data *) malloc(sizeof(my_atexit_data)); 57 | if (new_handler == NULL) { 58 | return; 59 | } 60 | new_handler->func = func; 61 | new_handler->data = data; 62 | new_handler->next = my_atexit_handlers; 63 | my_atexit_handlers = new_handler; 64 | } 65 | -------------------------------------------------------------------------------- /docs/showframes.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Copyright (c) 2015 Martin Sidaway 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | # this software and associated documentation files (the "Software"), to deal in 7 | # the Software without restriction, including without limitation the rights to 8 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is furnished to do 10 | # so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | # 23 | 24 | # This script is provided for reference purposes. It reads successive 27-byte 25 | # frames from a device file and displays the information in a user-readable 26 | # format. It has only been tested on OpenBSD. It takes the device filename as 27 | # an optional argument. 28 | 29 | use warnings; 30 | use strict; 31 | 32 | my $continue = 1; 33 | $SIG{INT} = sub { 34 | $continue = 0; 35 | }; 36 | 37 | my $filename = scalar(@ARGV) > 0 ? $ARGV[0] : '/dev/uhid0'; 38 | 39 | open(my $fh, '<', $filename); 40 | my $lastframe = ""; 41 | 42 | while ($continue) { 43 | read($fh, my $frame, 27, 0); 44 | next if $frame eq $lastframe; 45 | $lastframe = $frame; 46 | 47 | my ($btns, $dpad, $keystr, $keys, $velstr, $vels, $pbb, $on, $mod, $seq); 48 | ($btns, $dpad, $_, $keystr, $velstr, $pbb, $on, $mod, $_, $seq, $_) = 49 | unpack('nCa2a3a5CCCa9CC', $frame); 50 | 51 | $keys = unpack('N', substr($frame, 5, 4)); 52 | $keys = $keys >> 7; 53 | 54 | if (length($velstr) > 0) { 55 | vec($velstr, 0, 8) = vec($velstr, 0, 8) & 0x7f; 56 | } 57 | $vels = []; 58 | @{$vels} = unpack('C*', $velstr); 59 | 60 | printf("btns: %04x, dpad: %d, keys: %07x, vels: [%s], pbb: %02x, on: %02x, mod: %02x, seq: %02x\n", $btns, $dpad, $keys, join(', ', map(sprintf('%02x', $_), @{$vels})), $pbb, $on, $mod, $seq); 61 | } 62 | 63 | close($fh); 64 | -------------------------------------------------------------------------------- /myusb_atexit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "myusb_atexit.h" 29 | 30 | void myusb_unref_device(void *data) { 31 | libusb_device *dev = (libusb_device *)data; 32 | libusb_unref_device(dev); 33 | } 34 | 35 | void myusb_exit(void *ignored) { 36 | libusb_exit(NULL); 37 | } 38 | 39 | void myusb_close(void *data) { 40 | libusb_device_handle *h = (libusb_device_handle *)data; 41 | libusb_close(h); 42 | } 43 | 44 | void myusb_free_config_descriptor(void *data) { 45 | struct libusb_config_descriptor *cfgDesc = 46 | (struct libusb_config_descriptor *)data; 47 | libusb_free_config_descriptor(cfgDesc); 48 | } 49 | 50 | typedef struct myusb_release_interface_data { 51 | libusb_device_handle *dev; 52 | int interface_number; 53 | } myusb_release_interface_data; 54 | 55 | static void myusb_release_interface(void *data) { 56 | myusb_release_interface_data *d = (myusb_release_interface_data *)data; 57 | libusb_release_interface(d->dev, d->interface_number); 58 | free(d); 59 | } 60 | 61 | void myusb_atexit_release_interface(libusb_device_handle *dev, int interface_number) { 62 | myusb_release_interface_data *d = 63 | (myusb_release_interface_data *)malloc(sizeof(myusb_release_interface_data)); 64 | if (d == NULL) { 65 | return; 66 | } 67 | d->dev = dev; 68 | d->interface_number = interface_number; 69 | my_atexit(myusb_release_interface, d); 70 | } 71 | -------------------------------------------------------------------------------- /docs/input_frame_format.txt: -------------------------------------------------------------------------------- 1 | Reverse engineered input frame format 2 | 3 | Btns ?? Bmp Vels PBB Ped Mod ?? PedSt ?? ? Seq ? 4 | 0-2 3-4 5-7/8 8-12 13 14 15 16-19 20 21-23 24 25 26 5 | 6 | Each frame is 27 bytes. 7 | The exact same set of 27 bytes is repeated continually until the state of the 8 | keyboard changes. 9 | 10 | Byte 0 represents the state of the 1, 2, A, and B buttons (each is 1 bit). 11 | Byte 1 represents the state of the -, Power, and + buttons (each is 1 bit). 12 | Byte 2 represents the state of the directional pad (0-7: 8 compass points, 8: centre [pad inactive]) 13 | Bytes 3-4 are always 80. 14 | Byte 5-7/8 is a bitmap of currently depressed keys. 15 | Each byte represents 8 contiguous keys on the keyboard, starting with the 16 | leftmost 8 keys and continuing to the right with each subsequent byte. 17 | Within a byte, the highest bit represents the leftmost key, and so on. 18 | The final, 25th key (high C) is represented by the leftmost bit of byte 8 - 19 | presumably, the velocities only use 7 bits each. 20 | Bytes 8-12 consist of between 1 and 5 bytes each representing a velocity value 21 | for one of the keys. 22 | If there are fewer than 5 keys depressed, this array is padded on the 23 | right with zeros. 24 | If there are more than 5 keys depressed, only the (chronologically) 25 | first 5 keys to be pressed have their velocities represented. 26 | The velocities are sorted in the order that the keys appear on the 27 | keyboard (_not_ in chronological order!). 28 | If 6 or more keys are depressed, and then one or more of the first keys 29 | (chronologically) to be depressed is released, leaving the keyboard with 5 30 | or more keys still depressed (some of which were not in the initial 5), the 31 | velocities of one or more of the "extra" keys (which were previously hidden) 32 | are shown as 40 (even if this is not the true velocity value). 33 | This is the case even if exactly 5 keys then remain in the depressed state. 34 | However, if the number of depressed keys reduces to 4 or less, all keys' 35 | true velocity values are shown, even those previously shown as 40 or 36 | hidden altogether. 37 | Byte 13's high bit represents the state of the pitch bend button. 38 | Byte 14 represents the analog pedal value in the low 7 bits (rest state is 7F), 39 | and the digital stomp pedal value in the high bit (rest state is 0). 40 | Byte 15 represents the modulation slider value as a 7-bit value. 41 | However, it does not differentiate between modulation mode and pitch bend 42 | mode. 43 | Also, it resets to the value 00 as soon as you remove your finger. 44 | N.B. the value 00 does not seem to be otherwise reached. 45 | Bytes 16-19 are always 00. 46 | Byte 20 represents the pedal state abstractly. 47 | Bytes 21-23 are always 00. 48 | Byte 24 is always e0. 49 | Byte 25 seems to be a sequence number. 50 | Whenever the state of the keyboard changes, the value increases by 1. 51 | After it reaches ff, it wraps around to 00 with no other effect. 52 | When the keyboard switches off, this resets to 00. 53 | If the keyboard comes back on, the value reverts to the last value 54 | transmitted when it was last on. 55 | Byte 26 is always 0b. 56 | -------------------------------------------------------------------------------- /myusb_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "myusb_atexit.h" 29 | #include "myusb_utils.h" 30 | 31 | #define MAX_PRODUCT_LEN 1024 32 | 33 | libusb_device *myusb_get_device_by_prod_name_prefix(const char *prefix, int index) { 34 | 35 | libusb_device **devs; 36 | ssize_t cnt = libusb_get_device_list(NULL, &devs); 37 | if (cnt < 0) { 38 | fprintf(stderr, "Failed to get device list\n"); 39 | return NULL; 40 | } 41 | 42 | ssize_t i; 43 | struct libusb_device_descriptor desc; 44 | libusb_device_handle *h; 45 | libusb_device *result = NULL; 46 | char prodName[MAX_PRODUCT_LEN]; 47 | int r; 48 | size_t prefixLen = strlen(prefix); 49 | for (i = 0; i < cnt && !result; i++) { 50 | r = libusb_get_device_descriptor(devs[i], &desc); 51 | if (r < 0) { 52 | continue; 53 | } 54 | r = libusb_open(devs[i], &h); 55 | if (r < 0) { 56 | continue; 57 | } 58 | r = libusb_get_string_descriptor_ascii(h, desc.iProduct, 59 | prodName, MAX_PRODUCT_LEN); 60 | if (r >= 0 && strncmp(prefix, prodName, prefixLen) == 0) { 61 | if (index == 0) { 62 | result = devs[i]; 63 | } else { 64 | --index; 65 | } 66 | } 67 | libusb_close(h); 68 | } 69 | 70 | if (result != NULL) { 71 | // Increase reference count of device before freeing list. 72 | libusb_ref_device(result); 73 | my_atexit(myusb_unref_device, result); 74 | } 75 | 76 | libusb_free_device_list(devs, 1); 77 | 78 | return result; 79 | } 80 | 81 | const struct libusb_endpoint_descriptor * 82 | myusb_get_endpoint(libusb_device *dev, uint8_t direction, 83 | uint8_t attrs_mask, uint8_t attrs, int index, 84 | uint8_t *interface_number) { 85 | 86 | struct libusb_config_descriptor *cfgDesc; 87 | int r = libusb_get_active_config_descriptor(dev, &cfgDesc); 88 | if (r < 0) { 89 | fprintf(stderr, "Failed to get active config\n"); 90 | return NULL; 91 | } 92 | my_atexit(myusb_free_config_descriptor, cfgDesc); 93 | 94 | const struct libusb_endpoint_descriptor *endpoint = NULL; 95 | int i, j, k; 96 | for (i = 0; i < cfgDesc->bNumInterfaces; i++) { 97 | for (j = 0; j < cfgDesc->interface[i].num_altsetting; j++) { 98 | 99 | uint8_t numEndpoints = cfgDesc->interface[i].altsetting[j].bNumEndpoints; 100 | const struct libusb_endpoint_descriptor *endpoints = &cfgDesc->interface[i].altsetting[j].endpoint[0]; 101 | 102 | for (k = 0; k < numEndpoints; k++) { 103 | if ((endpoints[k].bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == direction && 104 | (endpoints[k].bmAttributes & attrs_mask) == attrs) { 105 | if (index == 0) { 106 | endpoint = &endpoints[k]; 107 | *interface_number = cfgDesc->interface[i].altsetting[j].bInterfaceNumber; 108 | goto break_outer; 109 | } else { 110 | --index; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | break_outer: 117 | 118 | return endpoint; 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RB3 Wireless MIDI Keyboard Driver 2 | ================================= 3 | 4 | This is a user-mode "driver" for the Harmonix Rock Band 3 wireless MIDI 5 | keyboard ("keytar") in wireless mode. 6 | 7 | So far, it has only been tested with the Wii version of the keytar, although I 8 | see no reason in principle why most functionality should not work with the 9 | other versions. 10 | 11 | Please see the bottom of this document for licensing and disclaimers. 12 | 13 | 14 | What is the keytar? 15 | ------------------- 16 | 17 | The keytar is a 2-octave (25-key) keyboard with full-size spring-loaded 18 | velocity-sensitive keys, octave shift and program change buttons, a 19 | footswitch/modulation pedal jack, and a dual-function modulation/pitch-bend 20 | touch panel. It comes with a USB dongle that communicates wirelessly with the 21 | keytar. It also has a standard MIDI out (DIN) jack. 22 | 23 | 24 | What is this driver supposed to do? 25 | ----------------------------------- 26 | 27 | Using this driver (although I make no representation that it will work for 28 | you), you can use the keytar to send MIDI messages wirelessly to your computer 29 | using the dongle that comes with the device. 30 | 31 | The dongle does not support standard USB-MIDI, so this driver converts the 32 | dongle's USB packets into MIDI and sends the messages to a MIDI output 33 | interface of your choosing. 34 | 35 | To use the driver's output as input to another program, you need some way of 36 | patching the MIDI "Out" of this program to the MIDI "In" of the other program. 37 | On Windows, this can be done using MIDI Yoke (http://www.midiox.com). On Linux 38 | or Mac OS X, you can probably use Jack (and I believe Jack 2 supports Windows 39 | as well). However, so far I haven't tried it with anything apart from MIDI 40 | Yoke. 41 | 42 | If you do use MIDI Yoke (on Windows), bear in mind that you do *not* need to 43 | manually create a patch using MIDI-OX. In fact, if you do this, it will not 44 | work properly. [I found this out the hard way. :-)] Simply use the MIDI Yoke 45 | "Out" and "In" devices with corresponding numbers, and it should work. 46 | 47 | 48 | Do I need to install it / is it complicated to use? 49 | --------------------------------------------------- 50 | 51 | No, it's just a normal program. The simplest way to use it (on Windows) is 52 | just to expand the zip file to a folder, then double-click "rb3_driver" in the 53 | folder. You will be presented with a numbered list of MIDI output devices. 54 | Just type the number of your chosen output device, and press the Enter key on 55 | your keyboard. If something goes wrong, the program will display an error 56 | message, then exit automatically after a few seconds. To close the program at 57 | any time, just close the window. While in use, you can minimise the program in 58 | the normal way if you want to avoid cluttering up your screen. 59 | 60 | It should automatically detect the correct USB (input) device, provided that 61 | the keytar dongle is plugged in. 62 | 63 | Most systems will have at least one built-in MIDI output device that 64 | corresponds to your sound card's or operating system's built-in MIDI 65 | synthesizer. However, this will usually be of low quality, and you will 66 | probably want to connect it to something else. If you install MIDI Yoke on 67 | Windows, it provides a set of 8 virtual MIDI output devices that send all their 68 | data to the corresponding virtual MIDI input device (which has the same number 69 | but "In" instead of "Out"). This works exactly like a digital patch cable. 70 | You can set up your synthesizer or sequencer program to use this as its input. 71 | 72 | 73 | What are the dependencies / how do I compile it? 74 | ------------------------------------------------ 75 | 76 | If you are using Windows, the good news is that you don't have to compile it 77 | yourself. A ready-compiled version of the program (with dependencies included) 78 | is available from https://github.com/martinjos/rb3_driver/releases 79 | 80 | So far, I have only compiled it under MinGW32/MSYS. However, it should in 81 | principle work just as well on Linux or even (possibly) Mac OS X. 82 | 83 | The main dependencies are libusb (http://libusb.org/) and PortMidi 84 | (http://portmedia.sourceforge.net/portmidi/). 85 | 86 | Once you have those in place (and your C compiler/build system obviously!), 87 | just type "make". (I think this may require GNU make - if it doesn't work, it 88 | should be simple enough to adapt the Makefile.) 89 | 90 | 91 | How much of the keytar's functionality is implemented? 92 | ------------------------------------------------------ 93 | 94 | At the moment, the keys themselves (including velocity), octave & program 95 | change buttons, the modulation/pitch-bend touch panel, drum split, all foot 96 | pedal features (including stomp and mode change), and the sequencer control 97 | buttons, are fully implemented. (Let me know if you find any bugs or missing 98 | features.) 99 | 100 | Note that drum split requires a General MIDI-compliant synthesizer program. If 101 | your synthesizer program is not General MIDI compliant, it may output either 102 | nothing at all, or a confusing array of jumbled up notes, when drum split is 103 | active and the drum keys (i.e. the lower 12) are pressed. 104 | 105 | Also note that the velocity-sensitivity is limited to at most 5 simultaneous 106 | keys - if 5 keys are held down and you strike another, it will register as 107 | having a velocity of exactly 64 (50% of the maximum value), regardless of the 108 | actual velocity. This is an unavoidable limitation of the keytar's USB packet 109 | format. 110 | 111 | LED output is so far not implemented. 112 | 113 | 114 | License 115 | ------- 116 | 117 | Copyright (c) 2015 Martin Sidaway 118 | 119 | Permission is hereby granted, free of charge, to any person obtaining a copy of 120 | this software and associated documentation files (the "Software"), to deal in 121 | the Software without restriction, including without limitation the rights to 122 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 123 | of the Software, and to permit persons to whom the Software is furnished to do 124 | so, subject to the following conditions: 125 | 126 | The above copyright notice and this permission notice shall be included in all 127 | copies or substantial portions of the Software. 128 | 129 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 130 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 131 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 132 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 133 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 134 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 135 | SOFTWARE. 136 | 137 | 138 | Additional legal disclaimers 139 | ---------------------------- 140 | 141 | The author can not be held responsible for the content, safety, security, 142 | freeness from bugs or viruses, or fitness-for-purpose of websites 143 | mentioned/linked to from this file/this repository, nor of the third-party 144 | programs and libraries mentioned here or anywhere in this repository (even 145 | those mentioned as dependencies). If you download, install, or otherwise use 146 | any of these programs and libraries, or visit any of the websites, you do so 147 | entirely at your own risk. 148 | 149 | The author of this software is not associated or affiliated with Harmonix Music 150 | Systems, Inc., or Nintendo Co., Ltd. and this software is not in any way 151 | endorsed or approved by those companies or their subsidiaries/associates. 152 | 153 | The author takes no responsibility for any attempt to use this software with 154 | Harmonix and/or Nintendo products, and provides no guarantee or affirmation 155 | that it is legal to do so in your jurisdiction. 156 | 157 | Harmonix and Rock Band are trademarks of Harmonix Music Systems, Inc. Nintendo 158 | and Wii are trademarks of Nintendo Co., Ltd. The author does not claim any 159 | rights over those trademarks. 160 | 161 | -------------------------------------------------------------------------------- /rb3_driver.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Martin Sidaway 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | * of the Software, and to permit persons to whom the Software is furnished to do 9 | * so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef _POSIX_SOURCE 31 | #include 32 | #endif 33 | 34 | #ifdef _WIN32 35 | #include 36 | #define sleep(x) Sleep(1000*(x)) 37 | #endif 38 | 39 | #include "myusb_atexit.h" 40 | #include "myusb_utils.h" 41 | 42 | #define DATA_BUFFER_LEN 27 43 | #define TRANSFER_TIMEOUT 500 44 | #define BITMAP_OFFSET 5 45 | #define BITMAP_LEN 4 46 | #define VELS_OFFSET 8 47 | #define VELS_LEN 5 48 | #define NUM_KEYS (BITMAP_LEN * 8 - 7) 49 | #define NORMAL_CHAN 0 50 | #define DEFAULT_VEL 0x40 51 | #define DEFAULT_1STNOTE 48 52 | #define DEFAULT_PATCH 0 53 | #define DRUMMAP_CHAN 9 54 | #define DRUMMAP_NKEYS 12 55 | 56 | #define OCTAVE_OFFSET 0 57 | #define OCTAVE_DOWN 1 // Key "1" 58 | #define OCTAVE_UP 4 // Key "B" 59 | #define PATCH_OFFSET 0 60 | #define PATCH_DOWN 2 // Key "A" 61 | #define PATCH_UP 8 // Key "2" 62 | #define PBBTN_OFFSET 13 63 | #define PBBTN_VALUE 0x80 64 | #define PED_OFFSET 14 65 | #define MOD_OFFSET 15 66 | #define PEDST_OFFSET 20 67 | #define PEDST_REST 0 68 | #define PEDST_STOMP 1 69 | #define PEDST_PARTIAL 2 70 | #define PEDST_FULL 3 71 | #define SEQ_OFFSET 1 72 | #define SEQ_STOP 0x01 73 | #define SEQ_CONT 0x10 74 | #define SEQ_START 0x02 75 | 76 | #define DPAD_OFFSET 2 77 | #define DPAD_MASK 0xf 78 | #define DPAD_UP 0 79 | #define DPAD_RIGHT 2 80 | #define DPAD_DOWN 4 81 | #define DPAD_LEFT 6 82 | #define DPAD_CENTER 8 83 | 84 | #define MIDI_NOTEON 0x90 85 | #define MIDI_NOTEOFF 0x80 86 | #define MIDI_PROGCH 0xC0 87 | #define MIDI_CTRLCH 0xB0 88 | #define MIDI_PBEND 0xE0 89 | #define MIDI_SEQSTART 0xFA 90 | #define MIDI_SEQCONT 0xFB 91 | #define MIDI_SEQSTOP 0xFC 92 | 93 | #define MIDI_CC_MOD 1 94 | #define MIDI_CC_FOOT 4 95 | #define MIDI_CC_VOL 7 96 | #define MIDI_CC_EXP 11 97 | #define MIDI_CC_DAMP 64 98 | #define MIDI_CM_ALLOFF 0x7B 99 | 100 | #define MIDI_CHANMASK 0xf 101 | #define MIDI_NUMPATCHES 0x80 102 | 103 | #define SLEEP_IF_CHOOSEDEVICE() do { if (chooseDevice) sleep(3); } while (0) 104 | 105 | uint8_t drumMapNotes[] = { 106 | 35, 36, 38, 40, 41, 47, 107 | 50, 42, 46, 49, 51, 53, 108 | }; 109 | 110 | void mypm_terminate(void *ignored) { 111 | Pm_Terminate(); 112 | } 113 | 114 | void mypm_close(void *data) { 115 | PortMidiStream *str = (PortMidiStream *)data; 116 | Pm_Close(str); 117 | } 118 | 119 | static inline int keyDown(const char *buffer, int index, int mask) { 120 | return (buffer[index] & mask) == mask; 121 | } 122 | 123 | static inline int keyPressed(const char *curBuffer, const char *lastBuffer, 124 | int index, int mask) { 125 | return (curBuffer[index] & mask) == mask && (lastBuffer[index] & mask) == 0; 126 | } 127 | 128 | int main(int argc, char **argv) { 129 | int r; 130 | int i, j, k; 131 | int chooseDevice = 0; 132 | 133 | if (argc < 2) { 134 | fprintf(stderr, "\nAvailable output devices:\n\n"); 135 | chooseDevice = 1; 136 | //return 4; 137 | } 138 | 139 | /* initialize portmidi */ 140 | r = Pm_Initialize(); 141 | if (r < 0) { 142 | fprintf(stderr, "Failed to initialize portmidi\n"); 143 | SLEEP_IF_CHOOSEDEVICE(); 144 | return r; 145 | }; 146 | my_atexit(mypm_terminate, NULL); 147 | 148 | int pmdCount = Pm_CountDevices(); 149 | //fprintf(stderr, "Got %d portmidi devices\n", pmdCount); 150 | PmDeviceID pmDev = -1; 151 | for (i = 0, j = 1; i < pmdCount; ++i) { 152 | const PmDeviceInfo *pmdInfo = Pm_GetDeviceInfo(i); 153 | if (pmdInfo != NULL && pmdInfo->output != 0) { 154 | if (chooseDevice) { 155 | fprintf(stderr, " %d: \"%s\"\n", j, pmdInfo->name); 156 | } else if (strcmp(argv[1], pmdInfo->name) == 0) { 157 | pmDev = i; 158 | } 159 | ++j; 160 | } 161 | } 162 | if (chooseDevice) { 163 | fprintf(stderr, "\nPlease type the number of your chosen output MIDI device and press the Enter key.\n\n"); 164 | scanf("%d", &k); 165 | fprintf(stderr, "\n"); 166 | for (i = 0, j = 1; i < pmdCount; ++i) { 167 | const PmDeviceInfo *pmdInfo = Pm_GetDeviceInfo(i); 168 | if (pmdInfo != NULL && pmdInfo->output != 0) { 169 | if (j == k) { 170 | pmDev = i; 171 | break; 172 | } 173 | ++j; 174 | } 175 | } 176 | } 177 | if (pmDev == -1) { 178 | fprintf(stderr, "Unable to find MIDI output device\n"); 179 | SLEEP_IF_CHOOSEDEVICE(); 180 | return 3; 181 | } 182 | 183 | PortMidiStream *outStream; 184 | r = Pm_OpenOutput(&outStream, pmDev, NULL, 0, NULL, NULL, 0); 185 | if (r < 0) { 186 | fprintf(stderr, "Failed to open MIDI output device\n"); 187 | SLEEP_IF_CHOOSEDEVICE(); 188 | return r; 189 | } 190 | my_atexit(mypm_close, outStream); 191 | 192 | fprintf(stderr, "Got MIDI output device!\n"); 193 | 194 | /* Initialize libusb */ 195 | r = libusb_init(NULL); 196 | if (r < 0) { 197 | fprintf(stderr, "Failed to initialize libusb\n"); 198 | SLEEP_IF_CHOOSEDEVICE(); 199 | return r; 200 | } 201 | my_atexit(myusb_exit, NULL); 202 | 203 | libusb_device *dev = 204 | myusb_get_device_by_prod_name_prefix("Harmonix RB3 Keyboard", 0); 205 | 206 | if (dev == NULL) { 207 | fprintf(stderr, "Failed to find input device\n"); 208 | SLEEP_IF_CHOOSEDEVICE(); 209 | return 1; 210 | } 211 | 212 | fprintf(stderr, "Brilliant news! Found input device!\n"); 213 | 214 | uint8_t interface_number = 0; 215 | const struct libusb_endpoint_descriptor *endpoint = 216 | myusb_get_endpoint(dev, LIBUSB_ENDPOINT_IN, 217 | LIBUSB_TRANSFER_TYPE_MASK, LIBUSB_TRANSFER_TYPE_INTERRUPT, 0, 218 | &interface_number); 219 | 220 | if (endpoint == NULL) { 221 | fprintf(stderr, "No suitable endpoint on input device\n"); 222 | SLEEP_IF_CHOOSEDEVICE(); 223 | return 2; 224 | } 225 | 226 | fprintf(stderr, "Got input device endpoint!\n"); 227 | 228 | libusb_device_handle *h = NULL; 229 | r = libusb_open(dev, &h); 230 | if (r < 0) { 231 | fprintf(stderr, "Failed to open input device\n"); 232 | SLEEP_IF_CHOOSEDEVICE(); 233 | return r; 234 | } 235 | my_atexit(myusb_close, h); 236 | 237 | r = libusb_claim_interface(h, interface_number); 238 | if (r < 0) { 239 | fprintf(stderr, "Failed to claim input device interface\n"); 240 | SLEEP_IF_CHOOSEDEVICE(); 241 | return r; 242 | } 243 | myusb_atexit_release_interface(h, interface_number); 244 | 245 | uint8_t buffer1[DATA_BUFFER_LEN]; 246 | uint8_t buffer2[DATA_BUFFER_LEN]; 247 | memset(buffer1, 0, sizeof(buffer1)); 248 | memset(buffer2, 0, sizeof(buffer2)); 249 | uint8_t *curBuffer = buffer1; 250 | uint8_t *lastBuffer = buffer2; 251 | int transferred_len = 0; 252 | 253 | // MIDI state 254 | int numKeysDown = 0; 255 | uint8_t notesDown[NUM_KEYS]; // What note was last activated for a given key? 256 | uint8_t chansDown[DRUMMAP_NKEYS]; // What channel was last activated for a given key? 257 | uint8_t firstNote = DEFAULT_1STNOTE; 258 | int8_t curPatch = DEFAULT_PATCH; 259 | int8_t drumMapOn = 0; 260 | uint8_t pedalCC = MIDI_CC_EXP; 261 | 262 | //struct libusb_transfer transfer; 263 | //libusb_fill_interrupt_transfer(&transfer, h, endpoint->bEndpointAddress, buffer, DATA_BUFFER_LEN, got_data, NULL, TRANSFER_TIMEOUT); 264 | 265 | uint8_t bitmask; 266 | int cv, lv; 267 | while (1) { 268 | 269 | r = libusb_interrupt_transfer(h, endpoint->bEndpointAddress, curBuffer, DATA_BUFFER_LEN, &transferred_len, TRANSFER_TIMEOUT); 270 | 271 | if (r == LIBUSB_ERROR_TIMEOUT) { 272 | fprintf(stderr, "Data transfer timed out (input)\n"); 273 | continue; 274 | } 275 | 276 | if (r < 0 || transferred_len == 0) { 277 | // N.B. this happens when the USB dongle is removed. 278 | fprintf(stderr, "Data transfer failed (input)\n"); 279 | break; 280 | } 281 | 282 | if (transferred_len < DATA_BUFFER_LEN) { 283 | fprintf(stderr, "Wrong packet size (input)\n"); 284 | continue; 285 | } 286 | 287 | if (memcmp(curBuffer, lastBuffer, DATA_BUFFER_LEN) != 0) { 288 | 289 | // DEBUG: dump input USB packet 290 | //for (i = 0; i < DATA_BUFFER_LEN; i++) { 291 | // fprintf(stderr, " %02x", curBuffer[i]); 292 | //} 293 | //fprintf(stderr, "\n"); 294 | 295 | // Octave and program change 296 | 297 | if (curBuffer[OCTAVE_OFFSET] != lastBuffer[OCTAVE_OFFSET]) { 298 | 299 | uint8_t upOctPressed = keyPressed(curBuffer, lastBuffer, OCTAVE_OFFSET, OCTAVE_UP); 300 | uint8_t downOctPressed = keyPressed(curBuffer, lastBuffer, OCTAVE_OFFSET, OCTAVE_DOWN); 301 | 302 | if ((upOctPressed || downOctPressed) && 303 | keyDown(curBuffer, OCTAVE_OFFSET, OCTAVE_UP) && 304 | keyDown(curBuffer, OCTAVE_OFFSET, OCTAVE_DOWN)) { 305 | 306 | firstNote = DEFAULT_1STNOTE; 307 | 308 | } else if (upOctPressed && firstNote <= 84) { 309 | firstNote += 12; 310 | } else if (downOctPressed && firstNote >= 12) { 311 | firstNote -= 12; 312 | } 313 | 314 | // N.B. assuming OCTAVE_OFFSET == PATCH_OFFSET (to save time) 315 | 316 | uint8_t upPatchPressed = keyPressed(curBuffer, lastBuffer, PATCH_OFFSET, PATCH_UP); 317 | uint8_t downPatchPressed = keyPressed(curBuffer, lastBuffer, PATCH_OFFSET, PATCH_DOWN); 318 | 319 | if ((upPatchPressed || downPatchPressed) && 320 | keyDown(curBuffer, PATCH_OFFSET, PATCH_UP) && 321 | keyDown(curBuffer, PATCH_OFFSET, PATCH_DOWN)) { 322 | 323 | curPatch = DEFAULT_PATCH; 324 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_PROGCH, curPatch, 0)); 325 | 326 | } else if (upPatchPressed) { 327 | curPatch = (curPatch + 1) % MIDI_NUMPATCHES; 328 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_PROGCH, curPatch, 0)); 329 | 330 | } else if (downPatchPressed) { 331 | curPatch -= 1; 332 | if (curPatch < 0) { 333 | curPatch = MIDI_NUMPATCHES - 1; 334 | } 335 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_PROGCH, curPatch, 0)); 336 | } 337 | 338 | } 339 | 340 | // Sequencer controls 341 | 342 | if (curBuffer[SEQ_OFFSET] != lastBuffer[SEQ_OFFSET]) { 343 | uint8_t start = keyPressed(curBuffer, lastBuffer, SEQ_OFFSET, SEQ_START); 344 | uint8_t cont = keyPressed(curBuffer, lastBuffer, SEQ_OFFSET, SEQ_CONT); 345 | uint8_t stop = keyPressed(curBuffer, lastBuffer, SEQ_OFFSET, SEQ_STOP); 346 | 347 | if ((start || cont || stop) && 348 | keyDown(curBuffer, SEQ_OFFSET, SEQ_START) && 349 | keyDown(curBuffer, SEQ_OFFSET, SEQ_CONT) && 350 | keyDown(curBuffer, SEQ_OFFSET, SEQ_STOP)) { 351 | 352 | // Turn off all notes on all channels. 353 | 354 | // First, cancel any sequence commands that may have been 355 | // started by mistake. 356 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_SEQSTOP, 0, 0)); 357 | 358 | Pm_WriteShort(outStream, 0, 359 | Pm_Message(MIDI_CTRLCH | NORMAL_CHAN, MIDI_CM_ALLOFF, 0)); 360 | Pm_WriteShort(outStream, 0, 361 | Pm_Message(MIDI_CTRLCH | DRUMMAP_CHAN, MIDI_CM_ALLOFF, 0)); 362 | 363 | } else if (start) { 364 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_SEQSTART, 0, 0)); 365 | } else if (cont) { 366 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_SEQCONT, 0, 0)); 367 | } else if (stop) { 368 | Pm_WriteShort(outStream, 0, Pm_Message(MIDI_SEQSTOP, 0, 0)); 369 | } 370 | } 371 | 372 | // Modulation and pitch bend 373 | 374 | if (curBuffer[MOD_OFFSET] != lastBuffer[MOD_OFFSET] && 375 | curBuffer[PBBTN_OFFSET] == lastBuffer[PBBTN_OFFSET]) { 376 | 377 | if ((curBuffer[PBBTN_OFFSET] & PBBTN_VALUE) == PBBTN_VALUE) { 378 | // Pitch bend 379 | uint8_t value = 0x40; // Reset value 380 | if (curBuffer[MOD_OFFSET] != 0) { 381 | value = curBuffer[MOD_OFFSET] - 1; 382 | Pm_WriteShort(outStream, 0, 383 | Pm_Message(MIDI_PBEND, 0, value)); 384 | } 385 | } else { 386 | // Modulation 387 | if (curBuffer[MOD_OFFSET] != 0) { 388 | Pm_WriteShort(outStream, 0, 389 | Pm_Message(MIDI_CTRLCH, MIDI_CC_MOD, 390 | curBuffer[MOD_OFFSET] - 1)); 391 | } 392 | } 393 | } 394 | 395 | // Drum split & pedal mode 396 | 397 | int curDpad = curBuffer[DPAD_OFFSET] & DPAD_MASK; 398 | int lastDpad = lastBuffer[DPAD_OFFSET] & DPAD_MASK; 399 | if (curDpad != DPAD_CENTER && curDpad != lastDpad) { 400 | uint8_t newPedalCC = pedalCC; 401 | switch (curDpad) { 402 | case DPAD_UP: 403 | drumMapOn = !drumMapOn; 404 | break; 405 | case DPAD_LEFT: 406 | newPedalCC = MIDI_CC_EXP; 407 | break; 408 | case DPAD_DOWN: 409 | newPedalCC = MIDI_CC_VOL; 410 | break; 411 | case DPAD_RIGHT: 412 | newPedalCC = MIDI_CC_FOOT; 413 | break; 414 | } 415 | if (newPedalCC != pedalCC) { 416 | // Reset old pedal to rest state 417 | // Don't do this - the keyboard doesn't. 418 | // It may be useful to "hold" a value of one pedal while 419 | // changing another. 420 | //Pm_WriteShort(outStream, 0, 421 | // Pm_Message(MIDI_CTRLCH, pedalCC, 0x7F)); 422 | pedalCC = newPedalCC; 423 | } 424 | } 425 | 426 | // Pedal value 427 | 428 | uint8_t curPedal = curBuffer[PED_OFFSET]; 429 | uint8_t lastPedal = lastBuffer[PED_OFFSET]; 430 | if (curPedal != lastPedal) { 431 | uint8_t curAnalog = curPedal & 0x7F; 432 | uint8_t lastAnalog = lastPedal & 0x7F; 433 | if (curAnalog != lastAnalog) { 434 | Pm_WriteShort(outStream, 0, 435 | Pm_Message(MIDI_CTRLCH, pedalCC, curAnalog)); 436 | } 437 | uint8_t curDigital = curPedal & 0x80; 438 | uint8_t lastDigital = lastPedal & 0x80; 439 | if (curDigital != lastDigital) { 440 | Pm_WriteShort(outStream, 0, 441 | Pm_Message(MIDI_CTRLCH, MIDI_CC_DAMP, 442 | curDigital ? 0x7F : 0)); 443 | } 444 | } 445 | 446 | // Keys 447 | 448 | uint8_t t = 0; 449 | uint8_t keyIndex = 0; 450 | int velsKeyIndex = 0; 451 | uint8_t vel; 452 | uint8_t chan; 453 | uint8_t note; 454 | 455 | // Each byte in bitmap 456 | for (i = BITMAP_OFFSET; i < BITMAP_OFFSET + BITMAP_LEN; i++) { 457 | 458 | // Interferes with velocity array index calculations. 459 | //if (curBuffer[i] == lastBuffer[i]) { 460 | // keyIndex += 8; 461 | // continue; 462 | //} 463 | 464 | if (i == BITMAP_OFFSET + BITMAP_LEN - 1) { 465 | // Last byte only has one bit to process. 466 | t = 0x40; 467 | } 468 | 469 | // Each bit 470 | for (bitmask = 0x80; bitmask != t; bitmask >>= 1) { 471 | cv = curBuffer[i] & bitmask; 472 | lv = lastBuffer[i] & bitmask; 473 | if (cv != lv) { 474 | if (cv) { 475 | // Note On 476 | chan = NORMAL_CHAN; 477 | note = firstNote + keyIndex; 478 | //printf("\x90%c\x40", note); 479 | //fprintf(stderr, "Sending NoteOn(%d)\n", note); 480 | if (keyIndex < DRUMMAP_NKEYS) { 481 | if (drumMapOn) { 482 | chan = DRUMMAP_CHAN; 483 | note = drumMapNotes[keyIndex]; 484 | } 485 | chansDown[keyIndex] = chan; 486 | } 487 | vel = DEFAULT_VEL; 488 | if (numKeysDown < VELS_LEN && velsKeyIndex < VELS_LEN) { 489 | // N.B. accepting a few strange but harmless 490 | // (and unlikely) edge cases here in the 491 | // interests of efficiency. 492 | vel = (0x7f & curBuffer[VELS_OFFSET + velsKeyIndex]); 493 | } 494 | Pm_WriteShort(outStream, 0, 495 | Pm_Message(MIDI_NOTEON | (MIDI_CHANMASK & chan), 496 | note, vel)); 497 | notesDown[keyIndex] = note; 498 | ++numKeysDown; 499 | } else { 500 | // Note Off 501 | //printf("\x80%c\x40", notesDown[keyIndex]); 502 | //fprintf(stderr, "Sending NoteOff(%d)\n", notesDown[keyIndex]); 503 | chan = NORMAL_CHAN; 504 | if (keyIndex < DRUMMAP_NKEYS) { 505 | chan = chansDown[keyIndex]; 506 | } 507 | Pm_WriteShort(outStream, 0, 508 | Pm_Message(MIDI_NOTEOFF | (MIDI_CHANMASK & chan), 509 | notesDown[keyIndex], 0x40)); 510 | --numKeysDown; 511 | } 512 | //fflush(stdout); 513 | } 514 | if (cv != 0) { 515 | ++velsKeyIndex; 516 | } 517 | ++keyIndex; 518 | } 519 | } 520 | 521 | // Swap buffers 522 | uint8_t *tmp = curBuffer; 523 | curBuffer = lastBuffer; 524 | lastBuffer = tmp; 525 | 526 | } 527 | 528 | transferred_len = 0; 529 | 530 | } 531 | 532 | return 0; 533 | } 534 | --------------------------------------------------------------------------------