diff --git a/src/main.c b/src/main.c index 8730e37..851a3a8 100644 --- a/src/main.c +++ b/src/main.c @@ -8,7 +8,7 @@ #include #include -static const char* devices[] = {"/dev/midi", "/dev/midi2", NULL}; +#include "midi_interface.h" typedef struct Note { @@ -20,7 +20,7 @@ typedef struct Note typedef struct Offset { signed char pitch; - signed char velocity + signed char velocity; } Offset; static const Offset pattern[] = { @@ -58,80 +58,78 @@ void *arpeggio_loop(void* data); int main(int argc, char** argv) { - unsigned char data[3] = {0, 0, 0}; + MidiInterface interface; + int result = open_midi_device(&interface, NULL); + if(result < 0) + exit(result); + int key_down = 0; Note current_note; - const char* device; - int fd; - int i = 0; - while((device = devices[i++]) != NULL) - { - printf("Trying %s...", device); - - fd = open(device, O_RDWR, 0); - if(fd < 0) - { - printf("Failed: %s\n", strerror(errno)); - } - else - { - printf("Success!\n"); - break; - } - } - - if(fd < 0) - { - fprintf(stderr, "No MIDI devices detected.\n"); - return -1; - } - - printf("Using MIDI device %s\n", device); pthread_t arpeggio_thread; - pthread_create(&arpeggio_thread, NULL, arpeggio_loop, (void*)fd); + pthread_create(&arpeggio_thread, NULL, arpeggio_loop, (void*)&interface); + + Message* message; + create_message(&message); for(;;) { - read(fd, (void*)data, sizeof(data)); - - if(data[0] != 0xfd) + result = read_midi_device(interface, message); + if(result < 0) { - if(data[2] > 0) + fprintf(stderr, "Failed to read message: %s\n", midi_strerror(result)); + if(result == MIDI_UNKNOWN_STATUS_BYTE) { - key_down = 1; - note = ¤t_note; + fprintf(stderr, "%x\n", message->type); + exit(result); + } + } - current_note.channel = 0x90; - current_note.pitch = data[1]; - current_note.velocity = data[2]; - } - else if(data[1] == current_note.pitch) - { - key_down = 0; - note = NULL; - step = 0; - } + // if(message->type != SYSTEM_EXCLUSIVE) + // printf("ch: %x, s: %x -- %02x %02x\n", message->channel, message->type, message->data[0], message->data[1]); + + if(message->type == NOTE_ON) + { + key_down = 1; + note = ¤t_note; + + current_note.channel = 0x90; + current_note.pitch = message->data[0]; + current_note.velocity = message->data[1]; + } + else if(message->type == NOTE_OFF && message->data[0] == note->pitch) + { + key_down = 0; + note = NULL; + step = 0; } } pthread_join(arpeggio_thread, NULL); - close(fd); + free_message(message); + close(interface.fd); return 0; } void *arpeggio_loop(void* data) { - int fd = (int)data; + MidiInterface* interface = (MidiInterface*)data; + Message* message; + create_message(&message); + message->channel = 0; + message->length = 2; + message->type = NOTE_ON; while(stop == 0) { if(note != NULL) { Note out = *note; - out.pitch += pattern[step].pitch; - out.velocity += pattern[step].velocity; - write(fd, &out, sizeof(Note)); + message->data[0] = out.pitch + pattern[step].pitch; + message->data[1] = out.velocity + pattern[step].velocity; + int result = write_midi_device(*interface, message); + if(result < 0) + fprintf(stderr, "Failed to send message: %s\n", midi_strerror(result)); step++; if(step >= (sizeof(pattern) / sizeof(Offset))) @@ -140,4 +138,6 @@ void *arpeggio_loop(void* data) usleep(166666); } } + + free_message(message); } \ No newline at end of file diff --git a/src/message.c b/src/message.c new file mode 100644 index 0000000..9996c3b --- /dev/null +++ b/src/message.c @@ -0,0 +1,81 @@ +#include "message.h" +#include +#include + +#include "midi_error.h" + +void create_message(Message** message) +{ + *message = (Message*)malloc(sizeof(Message)); + (*message)->data = (uint8_t*)malloc(8); +} + +void free_message(Message* message) +{ + free(message->data); + free(message); +} + +int data_length(MessageType type) +{ + switch(type) + { + case NOTE_ON: return 2; + case NOTE_OFF: return 2; + + case SYSTEM_EXCLUSIVE: return 0; + } + + return MIDI_UNKNOWN_STATUS_BYTE; +} + +int decode_status_byte(Message* message, uint8_t status) +{ + message->channel = (status & 0xF); + message->type = (status & 0xF0); + switch(status & 0xF0) + { + case 0x90: + message->type = NOTE_ON; + break; + + case 0x80: + message->type = NOTE_OFF; + break; + + case 0xF0: + message->type = SYSTEM_EXCLUSIVE; + break; + + default: + return MIDI_UNKNOWN_STATUS_BYTE; + } + + return 0; +} + +int encode_status_byte(const Message* message, uint8_t* status) +{ + *status = 0; + *status |= (message->channel & 0xF); + + switch(message->type) + { + case NOTE_ON: + *status |= 0x90; + break; + + case NOTE_OFF: + *status |= 0x80; + break; + + case SYSTEM_EXCLUSIVE: + *status |= 0xF0; + break; + + default: + return MIDI_UNKNOWN_STATUS_BYTE; + } + + return 0; +} \ No newline at end of file diff --git a/src/message.h b/src/message.h new file mode 100644 index 0000000..9fac323 --- /dev/null +++ b/src/message.h @@ -0,0 +1,28 @@ +#ifndef _MESSAGE_H_ +#define _MESSAGE_H_ + +#include + +typedef enum MessageType +{ + NOTE_OFF, NOTE_ON, + SYSTEM_EXCLUSIVE +} MessageType; + +typedef struct Message +{ + uint8_t channel; + MessageType type; + + int length; + uint8_t* data; +} Message; + +void create_message(Message** message); +void free_message(Message* message); + +int data_length(MessageType type); +int decode_status_byte(Message* message, uint8_t status); +int encode_status_byte(const Message* message, uint8_t* status); + +#endif \ No newline at end of file diff --git a/src/midi_error.c b/src/midi_error.c new file mode 100644 index 0000000..3f5a389 --- /dev/null +++ b/src/midi_error.c @@ -0,0 +1,15 @@ +#include "midi_error.h" + +#include + +static const char* error_messages[] = { + NULL, NULL, + "Failed to read from MIDI device.", + "Failed to write to MIDI device.", + "Unknown MIDI status byte" +}; + +const char* midi_strerror(MidiError error) +{ + return error_messages[-error]; +} \ No newline at end of file diff --git a/src/midi_error.h b/src/midi_error.h new file mode 100644 index 0000000..4debe2d --- /dev/null +++ b/src/midi_error.h @@ -0,0 +1,13 @@ +#ifndef _MIDI_ERROR_H_ +#define _MIDI_ERROR_H_ + +typedef enum MidiError +{ + MIDI_READ_ERROR = -2, + MIDI_WRITE_ERROR = -3, + MIDI_UNKNOWN_STATUS_BYTE = -4 +} MidiError; + +const char* midi_strerror(MidiError error); + +#endif \ No newline at end of file diff --git a/src/midi_interface.c b/src/midi_interface.c new file mode 100644 index 0000000..4c1f968 --- /dev/null +++ b/src/midi_interface.c @@ -0,0 +1,90 @@ +#include "midi_interface.h" +#include +#include +#include +#include +#include + +static const char* devices[] = {"/dev/midi", "/dev/midi2", NULL}; + +int try_open_device(const char* device); + +int open_midi_device(MidiInterface* interface, const char* device) +{ + int fd; + + if(device != NULL) + { + fd = try_open_device(device); + } + else + { + int i = 0; + while((device = devices[i++]) != NULL) + { + fd = try_open_device(device); + if(fd >= 0) + break; + } + } + + if(device == NULL) + fprintf(stderr, "Failed to locate MIDI device.\n"); + else if(fd < 0) + fprintf(stderr, "Failed to open MIDI device %s\n", device); + else + printf("Using device %s\n", device); + + interface->fd = fd; + return fd; +} + +int read_midi_device(MidiInterface interface, Message* buffer) +{ + uint8_t status; + int result = read(interface.fd, &status, 1); + if(result < 0) + return MIDI_READ_ERROR; + + result = decode_status_byte(buffer, status); + if(result < 0) + return result; + + buffer->length = data_length(buffer->type); + if(buffer->length < 0) + return buffer->length; + + result = read(interface.fd, buffer->data, buffer->length); + if(result < 0) + return MIDI_READ_ERROR; + + if(buffer->type == NOTE_ON && buffer->data[1] == 0) + { + buffer->type = NOTE_OFF; + } + + return 0; +} + +int write_midi_device(MidiInterface interface, const Message* buffer) +{ + uint8_t status; + int result = encode_status_byte(buffer, &status); + if(result < 0) + return result; + + uint8_t* buffer_out = (uint8_t*)malloc(1 + buffer->length); + buffer_out[0] = status; + memcpy(buffer_out + 1, buffer->data, buffer->length); + + result = write(interface.fd, buffer_out, 1 + buffer->length); + if(result < 0) + return MIDI_WRITE_ERROR; +} + +int try_open_device(const char* device) +{ + int fd = open(device, O_RDWR, 0); + + return fd; +} \ No newline at end of file diff --git a/src/midi_interface.h b/src/midi_interface.h new file mode 100644 index 0000000..a9c0c32 --- /dev/null +++ b/src/midi_interface.h @@ -0,0 +1,20 @@ +#ifndef _MIDI_INTERFACE_H_ +#define _MIDI_INTERFACE_H_ + +#include + +#include "message.h" +#include "midi_error.h" + +typedef struct MidiInterface +{ + int fd; +} MidiInterface; + +int open_midi_device(MidiInterface* interface, const char* device); + +int read_midi_device(MidiInterface interface, Message* buffer); +int write_midi_device(MidiInterface interface, const Message* buffer); + + +#endif \ No newline at end of file