fixed desync issue on tempo change

This commit is contained in:
Lauchmelder 2022-04-15 15:21:50 +02:00
parent 1735fe98e7
commit 5e7ab246fe
5 changed files with 66 additions and 21 deletions

View file

@ -12,13 +12,14 @@
#include "midi_interface.h" #include "midi_interface.h"
#include "midi_parser.h" #include "midi_parser.h"
uint32_t time_per_quarter;
uint32_t ticks_per_quarter;
MidiInterface* interface; MidiInterface* interface;
static int _Atomic playing = ATOMIC_VAR_INIT(0); 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* play_track(void* data);
void* advance_counter(void* data);
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
@ -28,8 +29,7 @@ int main(int argc, char** argv)
exit(-1); exit(-1);
} }
printf("Loading MIDI file...\n");
MidiParser* parser; MidiParser* parser;
parser = parseMidi(argv[1], false, true); parser = parseMidi(argv[1], false, true);
if(!parser) if(!parser)
@ -37,14 +37,17 @@ int main(int argc, char** argv)
fprintf(stderr, "Failed to read MIDI file\n"); fprintf(stderr, "Failed to read MIDI file\n");
exit(-1); exit(-1);
} }
printf("Done. Press enter to continue.\n");
time_per_quarter = ((uint32_t*)(parser->tracks[0].events[2].infos))[0]; getchar();
ticks_per_quarter = parser->ticks;
int result = open_midi_device(&interface, NULL); int result = open_midi_device(&interface, NULL);
if(result < 0) if(result < 0)
exit(result); 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)); pthread_t* threads = (pthread_t*)malloc(parser->nbOfTracks * sizeof(pthread_t));
for(int i = 0; i < parser->nbOfTracks; i++) for(int i = 0; i < parser->nbOfTracks; i++)
{ {
@ -60,39 +63,61 @@ int main(int argc, char** argv)
free(threads); free(threads);
close_midi_device(interface); close_midi_device(interface);
return 0; 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) void* play_track(void* data)
{ {
Track* track = (Track*)data; Track* track = (Track*)data;
Message* message; Message* message;
create_message(&message); create_message(&message);
message->channel = 0;
message->length = 2; message->length = 2;
uint64_t tick_of_last_event = 0;
int current_event = 0; int current_event = 0;
struct timeval begin, end;
while(!playing); while(!playing);
gettimeofday(&begin, NULL);
while(current_event < track->nbOfEvents) while(current_event < track->nbOfEvents)
{ {
Event event = track->events[current_event]; Event event = track->events[current_event];
while(tick_of_last_event + event.timeToAppear > current_tick);
gettimeofday(&end, NULL); if(event.type == MidiTempoChanged)
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)
{ {
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; 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]); // printf("Sending %d %d\n", message->data[0], message->data[1]);
int result = write_midi_device(interface, message); int result = write_midi_device(interface, message);
if(result < 0) if(result < 0)
@ -100,5 +125,6 @@ void* play_track(void* data)
} }
current_event++; current_event++;
tick_of_last_event += event.timeToAppear;
} }
} }

View file

@ -22,6 +22,7 @@ int data_length(MessageType type)
{ {
case NOTE_ON: return 2; case NOTE_ON: return 2;
case NOTE_OFF: return 2; case NOTE_OFF: return 2;
case CONTROLLER_CHANGE: return 2;
case SYSTEM_EXCLUSIVE: return 0; case SYSTEM_EXCLUSIVE: return 0;
} }
@ -43,6 +44,10 @@ int decode_status_byte(Message* message, uint8_t status)
message->type = NOTE_OFF; message->type = NOTE_OFF;
break; break;
case 0xB0:
message->type = CONTROLLER_CHANGE;
break;
case 0xF0: case 0xF0:
message->type = SYSTEM_EXCLUSIVE; message->type = SYSTEM_EXCLUSIVE;
break; break;
@ -69,6 +74,10 @@ int encode_status_byte(const Message* message, uint8_t* status)
*status |= 0x80; *status |= 0x80;
break; break;
case CONTROLLER_CHANGE:
*status |= 0xB0;
break;
case SYSTEM_EXCLUSIVE: case SYSTEM_EXCLUSIVE:
*status |= 0xF0; *status |= 0xF0;
break; break;

View file

@ -5,7 +5,7 @@
typedef enum MessageType typedef enum MessageType
{ {
NOTE_OFF, NOTE_ON, NOTE_OFF, NOTE_ON, CONTROLLER_CHANGE,
SYSTEM_EXCLUSIVE SYSTEM_EXCLUSIVE
} MessageType; } MessageType;

View file

@ -39,12 +39,15 @@ int open_midi_device(MidiInterface** interface, const char* device)
(*interface)->fd = fd; (*interface)->fd = fd;
(*interface)->out_buf = (uint8_t*)malloc(8); (*interface)->out_buf = (uint8_t*)malloc(8);
pthread_mutex_init(&((*interface)->write_mutex), NULL);
return fd; return fd;
} }
int close_midi_device(MidiInterface* interface) int close_midi_device(MidiInterface* interface)
{ {
close(interface->fd); close(interface->fd);
pthread_mutex_destroy(&(interface->write_mutex));
free(interface->out_buf); free(interface->out_buf);
free(interface); free(interface);
@ -86,12 +89,17 @@ int write_midi_device(MidiInterface* interface, const Message* buffer)
if(result < 0) if(result < 0)
return result; return result;
pthread_mutex_lock(&(interface->write_mutex));
interface->out_buf[0] = status; interface->out_buf[0] = status;
memcpy(interface->out_buf + 1, buffer->data, buffer->length); memcpy(interface->out_buf + 1, buffer->data, buffer->length);
result = write(interface->fd, interface->out_buf, 1 + buffer->length); result = write(interface->fd, interface->out_buf, 1 + buffer->length);
pthread_mutex_unlock(&(interface->write_mutex));
if(result < 0) if(result < 0)
return MIDI_WRITE_ERROR; return MIDI_WRITE_ERROR;
return 0;
} }
int write_midi_device_raw(MidiInterface* interface, const char* buffer) 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 try_open_device(const char* device)
{ {
int fd = open(device, O_RDWR, 0); int fd = open(device, O_RDWR | O_ASYNC, 0);
return fd; return fd;
} }

View file

@ -2,6 +2,7 @@
#define _MIDI_INTERFACE_H_ #define _MIDI_INTERFACE_H_
#include <stdint.h> #include <stdint.h>
#include <pthread.h>
#include "message.h" #include "message.h"
#include "midi_error.h" #include "midi_error.h"
@ -10,6 +11,7 @@ typedef struct MidiInterface
{ {
int fd; int fd;
uint8_t* out_buf; uint8_t* out_buf;
pthread_mutex_t write_mutex;
} MidiInterface; } MidiInterface;
int open_midi_device(MidiInterface** interface, const char* device); int open_midi_device(MidiInterface** interface, const char* device);