mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
pw-cat: read midi file from memory
mmap the midi file and read from memory. Simplifies some things and allows us to potentially parse from the RT thread later.
This commit is contained in:
parent
632c8440f8
commit
ddcb08a98a
3 changed files with 101 additions and 129 deletions
|
|
@ -38,139 +38,120 @@ static inline uint32_t parse_be32(const uint8_t *in)
|
||||||
return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3];
|
return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_mthd(struct midi_file *mf)
|
static inline int avail(struct midi_file *mf)
|
||||||
{
|
{
|
||||||
uint8_t buffer[14];
|
if (mf->p < mf->data + mf->size)
|
||||||
int res;
|
return mf->size + mf->data - mf->p;
|
||||||
|
|
||||||
if ((res = mf->events->read(mf->data, mf->offset, buffer, 14)) != 14)
|
|
||||||
return res < 0 ? res : -EIO;
|
|
||||||
|
|
||||||
if (memcmp(buffer, "MThd", 4))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
mf->size = parse_be32(buffer + 4);
|
|
||||||
mf->format = parse_be16(buffer + 8);
|
|
||||||
mf->ntracks = parse_be16(buffer + 10);
|
|
||||||
mf->division = parse_be16(buffer + 12);
|
|
||||||
mf->offset += 14;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int midi_file_open(struct midi_file *mf, int mode,
|
static int read_mthd(struct midi_file *mf)
|
||||||
const struct midi_events *events, void *data)
|
{
|
||||||
|
if (avail(mf) < 14)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (memcmp(mf->p, "MThd", 4))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
mf->length = parse_be32(mf->p + 4);
|
||||||
|
mf->format = parse_be16(mf->p + 8);
|
||||||
|
mf->ntracks = parse_be16(mf->p + 10);
|
||||||
|
mf->division = parse_be16(mf->p + 12);
|
||||||
|
mf->p += 14;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int midi_file_init(struct midi_file *mf, const char *mode,
|
||||||
|
void *data, size_t size)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
spa_zero(*mf);
|
spa_zero(*mf);
|
||||||
|
|
||||||
mf->events = events;
|
|
||||||
mf->data = data;
|
mf->data = data;
|
||||||
|
mf->size = size;
|
||||||
|
mf->p = data;
|
||||||
|
|
||||||
if ((res = read_mthd(mf)) < 0)
|
if ((res = read_mthd(mf)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
spa_list_init(&mf->tracks);
|
spa_list_init(&mf->tracks);
|
||||||
mf->tick = 0;
|
|
||||||
mf->tempo = DEFAULT_TEMPO;
|
mf->tempo = DEFAULT_TEMPO;
|
||||||
|
mf->tick = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int midi_file_close(struct midi_file *mf)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_mtrk(struct midi_file *mf, struct midi_track *track)
|
static int read_mtrk(struct midi_file *mf, struct midi_track *track)
|
||||||
{
|
{
|
||||||
uint8_t buffer[8];
|
if (avail(mf) < 8)
|
||||||
int res;
|
|
||||||
|
|
||||||
if ((res = mf->events->read(mf->data, mf->offset, buffer, 8)) != 8)
|
|
||||||
return res < 0 ? res : -EIO;
|
|
||||||
|
|
||||||
if (memcmp(buffer, "MTrk", 4))
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
track->start = mf->offset+8;
|
if (memcmp(mf->p, "MTrk", 4))
|
||||||
track->offset = 0;
|
return -EIO;
|
||||||
track->size = parse_be32(buffer + 4);
|
|
||||||
mf->offset += track->size + 8;
|
track->p = track->data = mf->p + 8;
|
||||||
|
track->size = parse_be32(mf->p + 4);
|
||||||
|
mf->p += track->size + 8;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t *result)
|
static int parse_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t *result)
|
||||||
{
|
{
|
||||||
uint32_t value;
|
uint32_t i, value = 0;
|
||||||
uint8_t buffer[1];
|
uint8_t b;
|
||||||
int i, res;
|
|
||||||
|
|
||||||
value = 0;
|
value = 0;
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
if (tr->offset >= tr->size) {
|
if (tr->p >= tr->data + tr->size) {
|
||||||
tr->eof = true;
|
tr->eof = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
b = *tr->p++;
|
||||||
if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1)
|
value = (value << 7) | (b & 0x7f);
|
||||||
return res < 0 ? res : -EIO;
|
if ((b & 0x80) == 0)
|
||||||
|
|
||||||
tr->offset++;
|
|
||||||
|
|
||||||
value = (value << 7) | ((buffer[0]) & 0x7f);
|
|
||||||
if ((buffer[0] & 0x80) == 0)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*result = value;
|
*result = value;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int peek_event(struct midi_file *mf, struct midi_track *tr, struct midi_event *event)
|
static int peek_event(struct midi_file *mf, struct midi_track *tr, struct midi_event *event)
|
||||||
{
|
{
|
||||||
uint8_t buffer[4], status;
|
uint8_t *save, status;
|
||||||
uint32_t size = 0, start;
|
uint32_t size = 0;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (tr->eof || tr->offset > tr->size)
|
if (tr->eof || tr->p >= tr->data + tr->size)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1)
|
save = tr->p;
|
||||||
return res < 0 ? res : -EIO;
|
status = *tr->p;
|
||||||
|
|
||||||
event->track = tr;
|
event->track = tr;
|
||||||
event->sec = mf->tick_sec + ((tr->tick - mf->tick_start) * (double)mf->tempo) / (1000000.0 * mf->division);
|
event->sec = mf->tick_sec + ((tr->tick - mf->tick_start) * (double)mf->tempo) / (1000000.0 * mf->division);
|
||||||
start = event->offset = tr->offset;
|
|
||||||
|
|
||||||
status = buffer[0];
|
|
||||||
if ((status & 0x80) == 0) {
|
if ((status & 0x80) == 0) {
|
||||||
status = tr->running_status;
|
status = tr->running_status;
|
||||||
|
event->data = tr->p;
|
||||||
} else {
|
} else {
|
||||||
tr->running_status = status;
|
tr->running_status = status;
|
||||||
event->offset++;
|
event->data = ++tr->p;
|
||||||
}
|
}
|
||||||
|
|
||||||
event->status = status;
|
event->status = status;
|
||||||
|
|
||||||
tr->offset++;
|
|
||||||
|
|
||||||
if (status < 0xf0) {
|
if (status < 0xf0) {
|
||||||
size++;
|
size++;
|
||||||
if (status < 0xc0 || status >= 0xe0)
|
if (status < 0xc0 || status >= 0xe0)
|
||||||
size++;
|
size++;
|
||||||
} else {
|
} else {
|
||||||
if (status == 0xff) {
|
if (status == 0xff) {
|
||||||
if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1)
|
event->meta = *tr->p++;
|
||||||
return res < 0 ? res : -EIO;
|
|
||||||
|
|
||||||
tr->offset++;
|
|
||||||
|
|
||||||
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
event->meta = buffer[0];
|
event->data = tr->p;
|
||||||
event->offset = tr->offset;
|
|
||||||
|
|
||||||
switch (event->meta) {
|
switch (event->meta) {
|
||||||
case 0x2f:
|
case 0x2f:
|
||||||
|
|
@ -179,27 +160,22 @@ static int peek_event(struct midi_file *mf, struct midi_track *tr, struct midi_e
|
||||||
case 0x51:
|
case 0x51:
|
||||||
if (size < 3)
|
if (size < 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 3)) != 3)
|
|
||||||
return res < 0 ? res : -EIO;
|
|
||||||
|
|
||||||
mf->tick_sec = event->sec;
|
mf->tick_sec = event->sec;
|
||||||
mf->tick_start = tr->tick;
|
mf->tick_start = tr->tick;
|
||||||
mf->tempo = (buffer[0]<<16) | (buffer[1]<<8) | buffer[2];
|
mf->tempo = (tr->p[0]<<16) | (tr->p[1]<<8) | tr->p[2];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (status == 0xf0 || status == 0xf7) {
|
} else if (status == 0xf0 || status == 0xf7) {
|
||||||
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
||||||
return res;
|
return res;
|
||||||
event->offset = tr->offset;
|
event->data = tr->p;
|
||||||
} else {
|
} else {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr->offset = start;
|
tr->p = save;
|
||||||
|
|
||||||
event->offset += tr->start;
|
|
||||||
event->size = size;
|
event->size = size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -245,7 +221,7 @@ int midi_file_consume_event(struct midi_file *mf, struct midi_event *event)
|
||||||
uint32_t delta_time;
|
uint32_t delta_time;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
tr->offset = event->offset - tr->start + event->size;
|
tr->p = event->data + event->size;
|
||||||
if ((res = parse_varlen(mf, tr, &delta_time)) < 0)
|
if ((res = parse_varlen(mf, tr, &delta_time)) < 0)
|
||||||
return res;
|
return res;
|
||||||
tr->tick += delta_time;
|
tr->tick += delta_time;
|
||||||
|
|
|
||||||
|
|
@ -35,17 +35,18 @@ struct midi_event {
|
||||||
double sec;
|
double sec;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
uint8_t meta;
|
uint8_t meta;
|
||||||
uint32_t offset;
|
|
||||||
size_t size;
|
uint8_t *data;
|
||||||
|
uint32_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct midi_track {
|
struct midi_track {
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
|
|
||||||
uint32_t start;
|
uint8_t *data;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
uint32_t offset;
|
uint8_t *p;
|
||||||
int64_t tick;
|
int64_t tick;
|
||||||
uint8_t running_status;
|
uint8_t running_status;
|
||||||
unsigned int eof:1;
|
unsigned int eof:1;
|
||||||
|
|
@ -53,13 +54,11 @@ struct midi_track {
|
||||||
struct spa_list events;
|
struct spa_list events;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct midi_events {
|
|
||||||
int (*read) (void *data, size_t offset, void *buf, size_t size);
|
|
||||||
int (*write) (void *data, size_t offset, void *buf, size_t size);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct midi_file {
|
struct midi_file {
|
||||||
uint32_t size;
|
uint8_t *data;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
uint32_t length;
|
||||||
uint16_t format;
|
uint16_t format;
|
||||||
uint16_t ntracks;
|
uint16_t ntracks;
|
||||||
uint16_t division;
|
uint16_t division;
|
||||||
|
|
@ -67,18 +66,14 @@ struct midi_file {
|
||||||
|
|
||||||
struct spa_list tracks;
|
struct spa_list tracks;
|
||||||
|
|
||||||
uint32_t offset;
|
uint8_t *p;
|
||||||
int64_t tick;
|
int64_t tick;
|
||||||
double tick_sec;
|
double tick_sec;
|
||||||
double tick_start;
|
double tick_start;
|
||||||
|
|
||||||
const struct midi_events *events;
|
|
||||||
void *data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int midi_file_open(struct midi_file *mf, int mode,
|
int midi_file_init(struct midi_file *mf, const char *mode,
|
||||||
const struct midi_events *events, void *data);
|
void *data, size_t size);
|
||||||
int midi_file_close(struct midi_file *mf);
|
|
||||||
|
|
||||||
int midi_file_add_track(struct midi_file *mf, struct midi_track *track);
|
int midi_file_add_track(struct midi_file *mf, struct midi_track *track);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -105,7 +107,7 @@ struct data {
|
||||||
|
|
||||||
enum mode mode;
|
enum mode mode;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool midi;
|
bool is_midi;
|
||||||
const char *remote_name;
|
const char *remote_name;
|
||||||
const char *media_type;
|
const char *media_type;
|
||||||
const char *media_category;
|
const char *media_category;
|
||||||
|
|
@ -145,10 +147,12 @@ struct data {
|
||||||
uint64_t clock_start;
|
uint64_t clock_start;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
FILE *f;
|
int fd;
|
||||||
struct midi_file file;
|
void *mem;
|
||||||
|
struct stat st;
|
||||||
|
struct midi_file mf;
|
||||||
struct midi_track track[64];
|
struct midi_track track[64];
|
||||||
} md;
|
} midi;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
|
@ -951,24 +955,12 @@ static void show_usage(const char *name, bool is_error)
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int midi_read(void *data, size_t offset, void *buf, size_t size)
|
|
||||||
{
|
|
||||||
struct data *d = data;
|
|
||||||
fseek(d->md.f, offset, SEEK_SET);
|
|
||||||
return fread(buf, 1, size, d->md.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct midi_events midi_events = {
|
|
||||||
.read = midi_read,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int midi_play(struct data *d, void *src, unsigned int n_frames)
|
static int midi_play(struct data *d, void *src, unsigned int n_frames)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct midi_event ev;
|
struct midi_event ev;
|
||||||
struct spa_pod_builder b;
|
struct spa_pod_builder b;
|
||||||
struct spa_pod_frame f;
|
struct spa_pod_frame f;
|
||||||
uint8_t buf[4096];
|
|
||||||
uint32_t first_frame, last_frame;
|
uint32_t first_frame, last_frame;
|
||||||
|
|
||||||
spa_zero(b);
|
spa_zero(b);
|
||||||
|
|
@ -983,9 +975,10 @@ static int midi_play(struct data *d, void *src, unsigned int n_frames)
|
||||||
last_frame = first_frame + d->position->clock.duration;
|
last_frame = first_frame + d->position->clock.duration;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
uint32_t frame;
|
uint32_t frame, offset;
|
||||||
|
uint8_t *buf;
|
||||||
|
|
||||||
res = midi_file_peek_event(&d->md.file, &ev);
|
res = midi_file_peek_event(&d->midi.mf, &ev);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
|
@ -1002,12 +995,13 @@ static int midi_play(struct data *d, void *src, unsigned int n_frames)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
spa_pod_builder_control(&b, frame, SPA_CONTROL_Midi);
|
spa_pod_builder_control(&b, frame, SPA_CONTROL_Midi);
|
||||||
|
offset = b.state.offset;
|
||||||
|
spa_pod_builder_bytes(&b, NULL, ev.size + 1);
|
||||||
|
buf = SPA_POD_BODY(spa_pod_builder_deref(&b, offset));
|
||||||
buf[0] = ev.status;
|
buf[0] = ev.status;
|
||||||
midi_read(d, ev.offset, &buf[1], ev.size);
|
memcpy(&buf[1], ev.data, ev.size);
|
||||||
spa_pod_builder_bytes(&b, buf, ev.size + 1);
|
|
||||||
|
|
||||||
next:
|
next:
|
||||||
midi_file_consume_event(&d->md.file, &ev);
|
midi_file_consume_event(&d->midi.mf, &ev);
|
||||||
}
|
}
|
||||||
spa_pod_builder_pop(&b, &f);
|
spa_pod_builder_pop(&b, &f);
|
||||||
|
|
||||||
|
|
@ -1019,28 +1013,35 @@ static int setup_midifile(struct data *data)
|
||||||
int res;
|
int res;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
data->md.f = fopen(data->filename,
|
if (stat(data->filename, &data->midi.st)) {
|
||||||
data->mode == mode_playback ? "r" : "w");
|
fprintf(stderr,"error: can't find file '%s': %m\n", data->filename);
|
||||||
if (data->md.f == NULL) {
|
|
||||||
fprintf(stderr, "error: failed to open midi file \"%s\": %m\n",
|
|
||||||
data->filename);
|
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = midi_file_open(&data->md.file, 0, &midi_events, data)) < 0)
|
if ((data->midi.fd = open(data->filename, O_RDONLY)) < 0) {
|
||||||
|
fprintf(stderr, "error: open '%s' failed : %m\n", data->filename);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->midi.mem = mmap(NULL, data->midi.st.st_size, PROT_READ, MAP_SHARED, data->midi.fd, 0);
|
||||||
|
if (data->midi.mem == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "error: mmap '%s' failed : %m\n", data->filename);
|
||||||
|
close(data->midi.fd);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
if ((res = midi_file_init(&data->midi.mf, "r", data->midi.mem, data->midi.st.st_size)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (data->verbose)
|
if (data->verbose)
|
||||||
printf("opened file \"%s\" format %08x ntracks:%d div:%d\n",
|
printf("opened file \"%s\" format %08x ntracks:%d div:%d\n",
|
||||||
data->filename,
|
data->filename,
|
||||||
data->md.file.format, data->md.file.ntracks,
|
data->midi.mf.format, data->midi.mf.ntracks,
|
||||||
data->md.file.division);
|
data->midi.mf.division);
|
||||||
|
|
||||||
for (i = 0; i < data->md.file.ntracks; i++) {
|
for (i = 0; i < data->midi.mf.ntracks; i++) {
|
||||||
midi_file_add_track(&data->md.file, &data->md.track[i]);
|
midi_file_add_track(&data->midi.mf, &data->midi.track[i]);
|
||||||
}
|
}
|
||||||
data->fill = data->mode == mode_playback ?
|
data->fill = data->mode == mode_playback ? midi_play : midi_play;
|
||||||
midi_play : midi_play;
|
|
||||||
data->stride = 1;
|
data->stride = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1257,7 +1258,7 @@ int main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
data.midi = true;
|
data.is_midi = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'R':
|
case 'R':
|
||||||
|
|
@ -1437,7 +1438,7 @@ int main(int argc, char *argv[])
|
||||||
if (!data.list_targets) {
|
if (!data.list_targets) {
|
||||||
struct spa_audio_info_raw info;
|
struct spa_audio_info_raw info;
|
||||||
|
|
||||||
if (data.midi)
|
if (data.is_midi)
|
||||||
ret = setup_midifile(&data);
|
ret = setup_midifile(&data);
|
||||||
else
|
else
|
||||||
ret = setup_sndfile(&data);
|
ret = setup_sndfile(&data);
|
||||||
|
|
@ -1453,7 +1454,7 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.midi) {
|
if (!data.is_midi) {
|
||||||
info = SPA_AUDIO_INFO_RAW_INIT(
|
info = SPA_AUDIO_INFO_RAW_INIT(
|
||||||
.flags = data.channelmap.n_channels ? 0 : SPA_AUDIO_FLAG_UNPOSITIONED,
|
.flags = data.channelmap.n_channels ? 0 : SPA_AUDIO_FLAG_UNPOSITIONED,
|
||||||
.format = data.spa_format,
|
.format = data.spa_format,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue