Skip to content

Instantly share code, notes, and snippets.

@wrl
Created April 30, 2013 23:14
Show Gist options
  • Save wrl/5492643 to your computer and use it in GitHub Desktop.
Save wrl/5492643 to your computer and use it in GitHub Desktop.
maschine mikro -> midi translator
/**
* william light <wrl@illest.net> 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;
}
/**
* william light <wrl@illest.net> 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