diff --git a/src/tools/midifile.c b/src/tools/midifile.c index feff133c7..0975a120d 100644 --- a/src/tools/midifile.c +++ b/src/tools/midifile.c @@ -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]; } -static int read_mthd(struct midi_file *mf) +static inline int avail(struct midi_file *mf) { - uint8_t buffer[14]; - int res; - - 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; + if (mf->p < mf->data + mf->size) + return mf->size + mf->data - mf->p; return 0; } -int midi_file_open(struct midi_file *mf, int mode, - const struct midi_events *events, void *data) +static int read_mthd(struct midi_file *mf) +{ + 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; spa_zero(*mf); - - mf->events = events; mf->data = data; + mf->size = size; + mf->p = data; if ((res = read_mthd(mf)) < 0) return res; spa_list_init(&mf->tracks); - mf->tick = 0; + mf->tempo = DEFAULT_TEMPO; + mf->tick = 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) { - uint8_t buffer[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)) + if (avail(mf) < 8) return -EIO; - track->start = mf->offset+8; - track->offset = 0; - track->size = parse_be32(buffer + 4); - mf->offset += track->size + 8; + if (memcmp(mf->p, "MTrk", 4)) + return -EIO; + + track->p = track->data = mf->p + 8; + track->size = parse_be32(mf->p + 4); + mf->p += track->size + 8; return 0; } static int parse_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t *result) { - uint32_t value; - uint8_t buffer[1]; - int i, res; + uint32_t i, value = 0; + uint8_t b; value = 0; for (i = 0; i < 4; i++) { - if (tr->offset >= tr->size) { + if (tr->p >= tr->data + tr->size) { tr->eof = true; break; } - - if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1) - return res < 0 ? res : -EIO; - - tr->offset++; - - value = (value << 7) | ((buffer[0]) & 0x7f); - if ((buffer[0] & 0x80) == 0) + b = *tr->p++; + value = (value << 7) | (b & 0x7f); + if ((b & 0x80) == 0) break; } *result = value; return 0; } - static int peek_event(struct midi_file *mf, struct midi_track *tr, struct midi_event *event) { - uint8_t buffer[4], status; - uint32_t size = 0, start; + uint8_t *save, status; + uint32_t size = 0; int res; - if (tr->eof || tr->offset > tr->size) + if (tr->eof || tr->p >= tr->data + tr->size) return -EIO; - if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1) - return res < 0 ? res : -EIO; + save = tr->p; + status = *tr->p; event->track = tr; 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) { status = tr->running_status; + event->data = tr->p; } else { tr->running_status = status; - event->offset++; + event->data = ++tr->p; } - event->status = status; - tr->offset++; - if (status < 0xf0) { size++; if (status < 0xc0 || status >= 0xe0) size++; } else { if (status == 0xff) { - if ((res = mf->events->read(mf->data, tr->start + tr->offset, buffer, 1)) != 1) - return res < 0 ? res : -EIO; - - tr->offset++; + event->meta = *tr->p++; if ((res = parse_varlen(mf, tr, &size)) < 0) return res; - event->meta = buffer[0]; - event->offset = tr->offset; + event->data = tr->p; switch (event->meta) { case 0x2f: @@ -179,27 +160,22 @@ static int peek_event(struct midi_file *mf, struct midi_track *tr, struct midi_e case 0x51: if (size < 3) 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_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; } } else if (status == 0xf0 || status == 0xf7) { if ((res = parse_varlen(mf, tr, &size)) < 0) return res; - event->offset = tr->offset; + event->data = tr->p; } else { return -EIO; } } - tr->offset = start; + tr->p = save; - event->offset += tr->start; event->size = size; return 0; @@ -245,7 +221,7 @@ int midi_file_consume_event(struct midi_file *mf, struct midi_event *event) uint32_t delta_time; 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) return res; tr->tick += delta_time; diff --git a/src/tools/midifile.h b/src/tools/midifile.h index 782332507..a04458e17 100644 --- a/src/tools/midifile.h +++ b/src/tools/midifile.h @@ -35,17 +35,18 @@ struct midi_event { double sec; uint8_t status; uint8_t meta; - uint32_t offset; - size_t size; + + uint8_t *data; + uint32_t size; }; struct midi_track { struct spa_list link; - uint32_t start; + uint8_t *data; uint32_t size; - uint32_t offset; + uint8_t *p; int64_t tick; uint8_t running_status; unsigned int eof:1; @@ -53,13 +54,11 @@ struct midi_track { 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 { - uint32_t size; + uint8_t *data; + size_t size; + + uint32_t length; uint16_t format; uint16_t ntracks; uint16_t division; @@ -67,18 +66,14 @@ struct midi_file { struct spa_list tracks; - uint32_t offset; + uint8_t *p; int64_t tick; double tick_sec; double tick_start; - - const struct midi_events *events; - void *data; }; -int midi_file_open(struct midi_file *mf, int mode, - const struct midi_events *events, void *data); -int midi_file_close(struct midi_file *mf); +int midi_file_init(struct midi_file *mf, const char *mode, + void *data, size_t size); int midi_file_add_track(struct midi_file *mf, struct midi_track *track); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 46107d887..2f92baec1 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -105,7 +107,7 @@ struct data { enum mode mode; bool verbose; - bool midi; + bool is_midi; const char *remote_name; const char *media_type; const char *media_category; @@ -145,10 +147,12 @@ struct data { uint64_t clock_start; struct { - FILE *f; - struct midi_file file; + int fd; + void *mem; + struct stat st; + struct midi_file mf; struct midi_track track[64]; - } md; + } midi; }; static inline int @@ -951,24 +955,12 @@ static void show_usage(const char *name, bool is_error) "\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) { int res; struct midi_event ev; struct spa_pod_builder b; struct spa_pod_frame f; - uint8_t buf[4096]; uint32_t first_frame, last_frame; 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; 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) return res; @@ -1002,12 +995,13 @@ static int midi_play(struct data *d, void *src, unsigned int n_frames) break; 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; - midi_read(d, ev.offset, &buf[1], ev.size); - spa_pod_builder_bytes(&b, buf, ev.size + 1); - + memcpy(&buf[1], ev.data, ev.size); next: - midi_file_consume_event(&d->md.file, &ev); + midi_file_consume_event(&d->midi.mf, &ev); } spa_pod_builder_pop(&b, &f); @@ -1019,28 +1013,35 @@ static int setup_midifile(struct data *data) int res; uint16_t i; - data->md.f = fopen(data->filename, - data->mode == mode_playback ? "r" : "w"); - if (data->md.f == NULL) { - fprintf(stderr, "error: failed to open midi file \"%s\": %m\n", - data->filename); + if (stat(data->filename, &data->midi.st)) { + fprintf(stderr,"error: can't find file '%s': %m\n", data->filename); 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; if (data->verbose) printf("opened file \"%s\" format %08x ntracks:%d div:%d\n", data->filename, - data->md.file.format, data->md.file.ntracks, - data->md.file.division); + data->midi.mf.format, data->midi.mf.ntracks, + data->midi.mf.division); - for (i = 0; i < data->md.file.ntracks; i++) { - midi_file_add_track(&data->md.file, &data->md.track[i]); + for (i = 0; i < data->midi.mf.ntracks; i++) { + midi_file_add_track(&data->midi.mf, &data->midi.track[i]); } - data->fill = data->mode == mode_playback ? - midi_play : midi_play; + data->fill = data->mode == mode_playback ? midi_play : midi_play; data->stride = 1; return 0; @@ -1257,7 +1258,7 @@ int main(int argc, char *argv[]) break; case 'm': - data.midi = true; + data.is_midi = true; break; case 'R': @@ -1437,7 +1438,7 @@ int main(int argc, char *argv[]) if (!data.list_targets) { struct spa_audio_info_raw info; - if (data.midi) + if (data.is_midi) ret = setup_midifile(&data); else 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( .flags = data.channelmap.n_channels ? 0 : SPA_AUDIO_FLAG_UNPOSITIONED, .format = data.spa_format,