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_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;
}
}

View file

@ -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;

View file

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

View file

@ -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;
}

View file

@ -2,6 +2,7 @@
#define _MIDI_INTERFACE_H_
#include <stdint.h>
#include <pthread.h>
#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);