Created
April 30, 2013 23:14
-
-
Save wrl/5492643 to your computer and use it in GitHub Desktop.
maschine mikro -> midi translator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* william light <[email protected]> wrote this in 2012 | |
* | |
* released under creative commons CC0 | |
* http://creativecommons.org/publicdomain/zero/1.0/ | |
*/ | |
#include <sys/stat.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <math.h> | |
#include <string.h> | |
#include <linux/hidraw.h> | |
#include <alsa/asoundlib.h> | |
#include <alsa/seq.h> | |
#include "mikro.h" | |
#define DIE(...) \ | |
do {\ | |
fprintf(stderr, __VA_ARGS__);\ | |
exit(1);\ | |
} while (0); | |
static uint8_t pad_note_map[16] = { | |
60, 61, 62, 63, | |
56, 57, 58, 59, | |
52, 53, 54, 55, | |
48, 49, 50, 51 | |
}; | |
#define MIDI_NOTE_FOR_PAD(X) (pad_note_map[X]) | |
static snd_seq_t *handle; | |
static int seq_port; | |
static snd_seq_event_t seq_ev; | |
static int process_pads(uint8_t *data, uint16_t *pads) | |
{ | |
uint16_t new; | |
int i, changed, vel; | |
changed = 0; | |
for (i = 0; i < 16; i++) { | |
new = ((data[0] << 4) | (data[1] & 0x0F)); | |
data += 2; | |
if (pads[i] == new) | |
continue; | |
vel = lrintf((new / 4095.f) * 127.f); | |
vel = 127; | |
if (!pads[i]) { | |
/* we already know the new value is different, otherwise we'd | |
have hit the above "continue" */ | |
snd_seq_ev_set_noteon(&seq_ev, 1, MIDI_NOTE_FOR_PAD(i), vel); | |
} else { | |
if (new) | |
snd_seq_ev_set_keypress(&seq_ev, 1, MIDI_NOTE_FOR_PAD(i), vel); | |
else | |
snd_seq_ev_set_noteoff(&seq_ev, 1, MIDI_NOTE_FOR_PAD(i), vel); | |
} | |
snd_seq_event_output(handle, &seq_ev); | |
snd_seq_drain_output(handle); | |
pads[i] = new; | |
changed = 1; | |
} | |
return changed; | |
} | |
uint8_t light_buf[61] = { | |
[0] = 0x80, | |
[1 ... 60] = 1, | |
[29 ... 36] = 0 | |
}; | |
static void output_lights(int fd) | |
{ | |
write(fd, light_buf, sizeof(light_buf)); | |
} | |
static int pad_to_light_map[16] = { | |
52, 51, 50, 49, | |
48, 47, 46, 45, | |
28, 27, 26, 25, | |
24, 23, 22, 21 | |
}; | |
uint8_t buttons[5] = {[0 ... 3] = 0, [4] = 16}; | |
static void button_dispatch(uint8_t *data) | |
{ | |
int i, off, code, diff; | |
for (i = 0; i < 4; i++) { | |
diff = data[i] ^ buttons[i]; | |
while (!!diff) { | |
off = ffs(diff); | |
code = mikro_bitfield_button_map[i][8 - off]; | |
if (data[i] & (1 << (off - 1))) | |
printf(" [+] %s\n", maschine_buttons_to_string[code]); | |
else | |
printf(" [ ] %s\n", maschine_buttons_to_string[code]); | |
diff >>= off; | |
} | |
buttons[i] = data[i]; | |
} | |
if (buttons[4] > 15) { | |
buttons[4] = data[4]; | |
return; | |
} | |
if (buttons[4] == data[4]) | |
return; | |
if (((buttons[4] + 1) & 0xF) == data[4]) | |
printf(" [>] encoder\n"); | |
else | |
printf(" [<] encoder\n"); | |
buttons[4] = data[4]; | |
} | |
static void read_from_dev(int fd) | |
{ | |
uint8_t buf[128], src, *data; | |
uint16_t pads[16] = {[0 ... 15] = 4095}; | |
ssize_t nbytes; | |
int i; | |
printf("let's do it\n\n"); | |
do { | |
if ((nbytes = read(fd, &buf, sizeof(buf))) < 0) { | |
perror("read"); | |
break; | |
} | |
src = buf[0]; | |
data = &buf[1]; | |
switch (src) { | |
case 32: | |
/* pads */ | |
if (!process_pads(data, pads)) | |
continue; | |
for (i = 0; i < 16; i++) | |
light_buf[pad_to_light_map[i]] = (33 - ((!!pads[i]) * 2)) & 0x1F; | |
fflush(stdout); | |
break; | |
case 1: | |
button_dispatch(data); | |
break; | |
default: | |
printf(" [?] %3ld bytes from %d\n", nbytes, src); | |
} | |
output_lights(fd); | |
} while (1); | |
} | |
static snd_seq_t *open_seq_client() | |
{ | |
snd_seq_t *handle; | |
if (snd_seq_open(&handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) | |
return NULL; | |
snd_seq_set_client_name(handle, "Maschine Mikro Translator"); | |
return handle; | |
} | |
static int get_seq_port(snd_seq_t *handle, const char *name) | |
{ | |
return snd_seq_create_simple_port( | |
handle, name, SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, | |
SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |
} | |
static void close_seq_client(snd_seq_t *handle) | |
{ | |
snd_seq_close(handle); | |
} | |
int main(int argc, char **argv) | |
{ | |
int fd; | |
if (argc < 2) | |
DIE("need device\n"); | |
if ((fd = open(argv[1], O_RDWR | O_EXCL)) < 0) { | |
perror("open"); | |
return EXIT_FAILURE; | |
} | |
if (!(handle = open_seq_client())) | |
DIE("couldn't open sequencer client\n"); | |
seq_port = get_seq_port(handle, "Maschine MIDI Out"); | |
snd_seq_ev_set_direct(&seq_ev); | |
snd_seq_ev_set_source(&seq_ev, seq_port); | |
snd_seq_ev_set_subs(&seq_ev); | |
output_lights(fd); | |
read_from_dev(fd); | |
close_seq_client(handle); | |
close(fd); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* william light <[email protected]> wrote this in 2012 | |
* | |
* released under creative commons CC0 | |
* http://creativecommons.org/publicdomain/zero/1.0/ | |
*/ | |
#include <stdint.h> | |
#define PACKED __attribute__((packed)) | |
typedef unsigned int uint_t; | |
typedef enum { | |
MASCHINE_BUTTON_DOWN, | |
MASCHINE_BUTTON_UP, | |
MASCHINE_ENCODER_TURN | |
} maschine_event_type_t; | |
typedef enum { | |
MASCHINE_BTN_RESTART, | |
MASCHINE_BTN_STEP_LEFT, | |
MASCHINE_BTN_STEP_RIGHT, | |
MASCHINE_BTN_GRID, | |
MASCHINE_BTN_PLAY, | |
MASCHINE_BTN_REC, | |
MASCHINE_BTN_ERASE, | |
MASCHINE_BTN_SHIFT, | |
MASCHINE_BTN_BROWSE, | |
MASCHINE_BTN_SAMPLING, | |
MASCHINE_BTN_GROUP, | |
MASCHINE_BTN_NOTE_REPEAT, | |
MASCHINE_BTN_ENCODER, | |
MASCHINE_BTN_F1, | |
MASCHINE_BTN_F2, | |
MASCHINE_BTN_F3, | |
MASCHINE_BTN_MAIN, | |
MASCHINE_BTN_NAV, | |
MASCHINE_BTN_NAV_LEFT, | |
MASCHINE_BTN_NAV_RIGHT, | |
MASCHINE_BTN_ENTER, | |
MASCHINE_BTN_SCENE, | |
MASCHINE_BTN_PATTERN, | |
MASCHINE_BTN_PAD_MODE, | |
MASCHINE_BTN_VIEW, | |
MASCHINE_BTN_DUPLICATE, | |
MASCHINE_BTN_SELECT, | |
MASCHINE_BTN_SOLO, | |
MASCHINE_BTN_MUTE | |
} maschine_buttons_t; | |
const char *maschine_buttons_to_string[] = { | |
[MASCHINE_BTN_RESTART] = "MASCHINE_BTN_RESTART", | |
[MASCHINE_BTN_STEP_LEFT] = "MASCHINE_BTN_STEP_LEFT", | |
[MASCHINE_BTN_STEP_RIGHT] = "MASCHINE_BTN_STEP_RIGHT", | |
[MASCHINE_BTN_GRID] = "MASCHINE_BTN_GRID", | |
[MASCHINE_BTN_PLAY] = "MASCHINE_BTN_PLAY", | |
[MASCHINE_BTN_REC] = "MASCHINE_BTN_REC", | |
[MASCHINE_BTN_ERASE] = "MASCHINE_BTN_ERASE", | |
[MASCHINE_BTN_SHIFT] = "MASCHINE_BTN_SHIFT", | |
[MASCHINE_BTN_BROWSE] = "MASCHINE_BTN_BROWSE", | |
[MASCHINE_BTN_SAMPLING] = "MASCHINE_BTN_SAMPLING", | |
[MASCHINE_BTN_GROUP] = "MASCHINE_BTN_GROUP", | |
[MASCHINE_BTN_NOTE_REPEAT] = "MASCHINE_BTN_NOTE_REPEAT", | |
[MASCHINE_BTN_ENCODER] = "MASCHINE_BTN_ENCODER", | |
[MASCHINE_BTN_F1] = "MASCHINE_BTN_F1", | |
[MASCHINE_BTN_F2] = "MASCHINE_BTN_F2", | |
[MASCHINE_BTN_F3] = "MASCHINE_BTN_F3", | |
[MASCHINE_BTN_MAIN] = "MASCHINE_BTN_MAIN", | |
[MASCHINE_BTN_NAV] = "MASCHINE_BTN_NAV", | |
[MASCHINE_BTN_NAV_LEFT] = "MASCHINE_BTN_NAV_LEFT", | |
[MASCHINE_BTN_NAV_RIGHT] = "MASCHINE_BTN_NAV_RIGHT", | |
[MASCHINE_BTN_ENTER] = "MASCHINE_BTN_ENTER", | |
[MASCHINE_BTN_SCENE] = "MASCHINE_BTN_SCENE", | |
[MASCHINE_BTN_PATTERN] = "MASCHINE_BTN_PATTERN", | |
[MASCHINE_BTN_PAD_MODE] = "MASCHINE_BTN_PAD_MODE", | |
[MASCHINE_BTN_VIEW] = "MASCHINE_BTN_VIEW", | |
[MASCHINE_BTN_DUPLICATE] = "MASCHINE_BTN_DUPLICATE", | |
[MASCHINE_BTN_SELECT] = "MASCHINE_BTN_SELECT", | |
[MASCHINE_BTN_SOLO] = "MASCHINE_BTN_SOLO", | |
[MASCHINE_BTN_MUTE] = "MASCHINE_BTN_MUTE" | |
}; | |
struct mikro_button_report { | |
uint_t restart:1; | |
uint_t step_left:1; | |
uint_t step_right:1; | |
uint_t grid:1; | |
uint_t play:1; | |
uint_t rec:1; | |
uint_t erase:1; | |
uint_t shift:1; | |
uint_t browse:1; | |
uint_t sampling:1; | |
uint_t group:1; | |
uint_t note_repeat:1; | |
uint_t encoder_press:1; | |
uint_t unused:3; | |
uint_t f1:1; | |
uint_t f2:1; | |
uint_t f3:1; | |
uint_t main:1; | |
uint_t nav:1; | |
uint_t nav_left:1; | |
uint_t nav_right:1; | |
uint_t enter:1; | |
uint_t scene:1; | |
uint_t pattern:1; | |
uint_t pad_mode:1; | |
uint_t view:1; | |
uint_t duplicate:1; | |
uint_t select:1; | |
uint_t solo:1; | |
uint_t mute:1; | |
uint8_t encoder; | |
} PACKED; | |
int mikro_bitfield_button_map[4][8] = { | |
{ | |
MASCHINE_BTN_RESTART, | |
MASCHINE_BTN_STEP_LEFT, | |
MASCHINE_BTN_STEP_RIGHT, | |
MASCHINE_BTN_GRID, | |
MASCHINE_BTN_PLAY, | |
MASCHINE_BTN_REC, | |
MASCHINE_BTN_ERASE, | |
MASCHINE_BTN_SHIFT | |
}, | |
{ | |
MASCHINE_BTN_BROWSE, | |
MASCHINE_BTN_SAMPLING, | |
MASCHINE_BTN_GROUP, | |
MASCHINE_BTN_NOTE_REPEAT, | |
MASCHINE_BTN_ENCODER, | |
0, | |
0, | |
0 | |
}, | |
{ | |
MASCHINE_BTN_F1, | |
MASCHINE_BTN_F2, | |
MASCHINE_BTN_F3, | |
MASCHINE_BTN_MAIN, | |
MASCHINE_BTN_NAV, | |
MASCHINE_BTN_NAV_LEFT, | |
MASCHINE_BTN_NAV_RIGHT, | |
MASCHINE_BTN_ENTER | |
}, | |
{ | |
MASCHINE_BTN_SCENE, | |
MASCHINE_BTN_PATTERN, | |
MASCHINE_BTN_PAD_MODE, | |
MASCHINE_BTN_VIEW, | |
MASCHINE_BTN_DUPLICATE, | |
MASCHINE_BTN_SELECT, | |
MASCHINE_BTN_SOLO, | |
MASCHINE_BTN_MUTE | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment