From 5e7ab246fef89fe336333e3eec073a3e5e061fc6 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Fri, 15 Apr 2022 15:21:50 +0200 Subject: [PATCH] fixed desync issue on tempo change --- src/main.c | 64 +++++++++++++++++++++++++++++++------------- src/message.c | 9 +++++++ src/message.h | 2 +- src/midi_interface.c | 10 ++++++- src/midi_interface.h | 2 ++ 5 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/main.c b/src/main.c index 5c73143..0aeeba7 100644 --- a/src/main.c +++ b/src/main.c @@ -12,13 +12,14 @@ #include "midi_interface.h" #include "midi_parser.h" -uint32_t time_per_quarter; -uint32_t ticks_per_quarter; MidiInterface* interface; static int _Atomic playing = ATOMIC_VAR_INIT(0); +static uint32_t _Atomic time_per_quarter = ATOMIC_VAR_INIT(1); +static uint32_t _Atomic current_tick = ATOMIC_VAR_INIT(0); void* play_track(void* data); +void* advance_counter(void* data); int main(int argc, char** argv) { @@ -28,8 +29,7 @@ int main(int argc, char** argv) exit(-1); } - - + printf("Loading MIDI file...\n"); MidiParser* parser; parser = parseMidi(argv[1], false, true); if(!parser) @@ -37,14 +37,17 @@ int main(int argc, char** argv) fprintf(stderr, "Failed to read MIDI file\n"); exit(-1); } - - time_per_quarter = ((uint32_t*)(parser->tracks[0].events[2].infos))[0]; - ticks_per_quarter = parser->ticks; + printf("Done. Press enter to continue.\n"); + getchar(); int result = open_midi_device(&interface, NULL); if(result < 0) exit(result); + pthread_t counter_thread; + pthread_create(&counter_thread, NULL, advance_counter, &(parser->ticks)); + pthread_detach(counter_thread); + pthread_t* threads = (pthread_t*)malloc(parser->nbOfTracks * sizeof(pthread_t)); for(int i = 0; i < parser->nbOfTracks; i++) { @@ -60,39 +63,61 @@ int main(int argc, char** argv) free(threads); close_midi_device(interface); - return 0; } +void* advance_counter(void* data) +{ + uint16_t ticks_per_quarter = *((uint32_t*)data); + while(!playing); + + for(;;) + { + usleep((uint64_t)time_per_quarter / ticks_per_quarter); + current_tick++; + } +} + void* play_track(void* data) { Track* track = (Track*)data; Message* message; create_message(&message); - message->channel = 0; message->length = 2; + uint64_t tick_of_last_event = 0; int current_event = 0; - struct timeval begin, end; while(!playing); - gettimeofday(&begin, NULL); while(current_event < track->nbOfEvents) { Event event = track->events[current_event]; + while(tick_of_last_event + event.timeToAppear > current_tick); - gettimeofday(&end, NULL); - uint64_t deltatime = ((end.tv_sec - begin.tv_sec) * 1000000) + (end.tv_usec - begin.tv_usec); - if(event.timeToAppear > 0) - usleep(event.timeToAppear * time_per_quarter / ticks_per_quarter - deltatime); - gettimeofday(&begin, NULL); - - if(event.type == MidiNotePressed || event.type == MidiNoteReleased) + if(event.type == MidiTempoChanged) { - message->type = (event.type == MidiNotePressed) ? NOTE_ON : NOTE_OFF; + time_per_quarter = ((uint32_t*)(event.infos))[0]; + } + else if(event.type == MidiNotePressed || event.type == MidiNoteReleased || event.type == MidiControllerValueChanged) + { + message->channel = (((uint8_t*)(event.infos))[0] & 0xF); message->data = event.infos + 1; + switch(event.type) + { + case MidiNotePressed: + message->type = NOTE_ON; + break; + + case MidiNoteReleased: + message->type = NOTE_OFF; + break; + + case MidiControllerValueChanged: + message->type = CONTROLLER_CHANGE; + break; + } // printf("Sending %d %d\n", message->data[0], message->data[1]); int result = write_midi_device(interface, message); if(result < 0) @@ -100,5 +125,6 @@ void* play_track(void* data) } current_event++; + tick_of_last_event += event.timeToAppear; } } \ No newline at end of file diff --git a/src/message.c b/src/message.c index 9996c3b..4398e4b 100644 --- a/src/message.c +++ b/src/message.c @@ -22,6 +22,7 @@ int data_length(MessageType type) { case NOTE_ON: return 2; case NOTE_OFF: return 2; + case CONTROLLER_CHANGE: return 2; case SYSTEM_EXCLUSIVE: return 0; } @@ -43,6 +44,10 @@ int decode_status_byte(Message* message, uint8_t status) message->type = NOTE_OFF; break; + case 0xB0: + message->type = CONTROLLER_CHANGE; + break; + case 0xF0: message->type = SYSTEM_EXCLUSIVE; break; @@ -69,6 +74,10 @@ int encode_status_byte(const Message* message, uint8_t* status) *status |= 0x80; break; + case CONTROLLER_CHANGE: + *status |= 0xB0; + break; + case SYSTEM_EXCLUSIVE: *status |= 0xF0; break; diff --git a/src/message.h b/src/message.h index 9fac323..da9112e 100644 --- a/src/message.h +++ b/src/message.h @@ -5,7 +5,7 @@ typedef enum MessageType { - NOTE_OFF, NOTE_ON, + NOTE_OFF, NOTE_ON, CONTROLLER_CHANGE, SYSTEM_EXCLUSIVE } MessageType; diff --git a/src/midi_interface.c b/src/midi_interface.c index d679e7f..905d28d 100644 --- a/src/midi_interface.c +++ b/src/midi_interface.c @@ -39,12 +39,15 @@ int open_midi_device(MidiInterface** interface, const char* device) (*interface)->fd = fd; (*interface)->out_buf = (uint8_t*)malloc(8); + pthread_mutex_init(&((*interface)->write_mutex), NULL); + return fd; } int close_midi_device(MidiInterface* interface) { close(interface->fd); + pthread_mutex_destroy(&(interface->write_mutex)); free(interface->out_buf); free(interface); @@ -86,12 +89,17 @@ int write_midi_device(MidiInterface* interface, const Message* buffer) if(result < 0) return result; + pthread_mutex_lock(&(interface->write_mutex)); interface->out_buf[0] = status; + memcpy(interface->out_buf + 1, buffer->data, buffer->length); result = write(interface->fd, interface->out_buf, 1 + buffer->length); + pthread_mutex_unlock(&(interface->write_mutex)); if(result < 0) return MIDI_WRITE_ERROR; + + return 0; } int write_midi_device_raw(MidiInterface* interface, const char* buffer) @@ -101,7 +109,7 @@ int write_midi_device_raw(MidiInterface* interface, const char* buffer) int try_open_device(const char* device) { - int fd = open(device, O_RDWR, 0); + int fd = open(device, O_RDWR | O_ASYNC, 0); return fd; } \ No newline at end of file diff --git a/src/midi_interface.h b/src/midi_interface.h index dd1ef46..5fe9398 100644 --- a/src/midi_interface.h +++ b/src/midi_interface.h @@ -2,6 +2,7 @@ #define _MIDI_INTERFACE_H_ #include +#include #include "message.h" #include "midi_error.h" @@ -10,6 +11,7 @@ typedef struct MidiInterface { int fd; uint8_t* out_buf; + pthread_mutex_t write_mutex; } MidiInterface; int open_midi_device(MidiInterface** interface, const char* device);