mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
pw-cat: add support for streaming
sndfile actually supports reading and writing from/to stdin/out with the - filename, so allow that. Add support for streaming in dsf, dff and midifile as well. Add a -a option to pw-cat use a pipe with raw bytes, otherwise try to use the parsers and sndfile to read/write from/to stdin/stdout. You can then do things like: sox 2L-053_04_stereo-DSD64.dff -t dsf - | pw-cat -pdv - pw-cat 07.Joe.Satriani.Clouds.race.across.the.sky.wav | pw-cat -pv - pw-cat -rmv --target=0 - | pw-mididump - Fixes: #4204
This commit is contained in:
parent
d9bd2628d9
commit
19f4fac1e1
4 changed files with 343 additions and 233 deletions
|
|
@ -14,22 +14,25 @@
|
||||||
|
|
||||||
#include "dfffile.h"
|
#include "dfffile.h"
|
||||||
|
|
||||||
|
#define BLOCKSIZE 8192
|
||||||
|
|
||||||
struct dff_file {
|
struct dff_file {
|
||||||
uint8_t *data;
|
uint8_t *buffer;
|
||||||
size_t size;
|
size_t blocksize;
|
||||||
|
size_t offset;
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
int fd;
|
bool close;
|
||||||
|
FILE *file;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
struct dff_file_info info;
|
struct dff_file_info info;
|
||||||
|
|
||||||
uint8_t *p;
|
|
||||||
size_t offset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dff_chunk {
|
struct dff_chunk {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
uint64_t pos;
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,28 +60,44 @@ static inline uint64_t parse_be64(const uint8_t *in)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int f_avail(struct dff_file *f)
|
static inline int f_read(struct dff_file *f, void *data, size_t size)
|
||||||
{
|
{
|
||||||
if (f->p < f->data + f->size)
|
size_t s = fread(data, 1, size, f->file);
|
||||||
return f->size + f->data - f->p;
|
f->pos += s;
|
||||||
|
if (s < size)
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_chunk(struct dff_file *f, struct dff_chunk *c)
|
static int read_chunk(struct dff_file *f, struct dff_chunk *c)
|
||||||
{
|
{
|
||||||
if (f_avail(f) < 12)
|
uint8_t data[12];
|
||||||
|
|
||||||
|
if (f_read(f, data, sizeof(data)) < 0)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
|
||||||
c->id = parse_be32(f->p); /* id of this chunk */
|
c->id = parse_be32(data); /* id of this chunk */
|
||||||
c->size = parse_be64(f->p + 4); /* size of this chunk */
|
c->size = parse_be64(data + 4); /* size of this chunk */
|
||||||
f->p += 12;
|
c->pos = f->pos;
|
||||||
c->data = f->p;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int skip_chunk(struct dff_file *f, const struct dff_chunk *c)
|
static int skip_chunk(struct dff_file *f, const struct dff_chunk *c)
|
||||||
{
|
{
|
||||||
f->p = SPA_PTROFF(c->data, c->size, uint8_t);
|
size_t bytes;
|
||||||
|
uint8_t data[256];
|
||||||
|
|
||||||
|
if (c->pos + c->size <= f->pos)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bytes = c->size + c->pos - f->pos;
|
||||||
|
while (bytes > 0) {
|
||||||
|
size_t s = fread(data, 1, SPA_MIN(bytes, sizeof(data)), f->file);
|
||||||
|
if (s == 0)
|
||||||
|
break;
|
||||||
|
f->pos += s;
|
||||||
|
bytes -= s;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,22 +105,26 @@ static int read_PROP(struct dff_file *f, struct dff_chunk *prop)
|
||||||
{
|
{
|
||||||
struct dff_chunk c[1];
|
struct dff_chunk c[1];
|
||||||
int res;
|
int res;
|
||||||
|
uint8_t data[4];
|
||||||
|
|
||||||
if (f_avail(f) < 4 ||
|
if (f_read(f, data, sizeof(data)) < 0 ||
|
||||||
memcmp(prop->data, "SND ", 4) != 0)
|
memcmp(data, "SND ", 4) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
f->p += 4;
|
|
||||||
|
|
||||||
while (f->p < SPA_PTROFF(prop->data, prop->size, uint8_t)) {
|
while (f->pos < prop->pos + prop->size) {
|
||||||
if ((res = read_chunk(f, &c[0])) < 0)
|
if ((res = read_chunk(f, &c[0])) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
switch (c[0].id) {
|
switch (c[0].id) {
|
||||||
case FOURCC('F', 'S', ' ', ' '):
|
case FOURCC('F', 'S', ' ', ' '):
|
||||||
f->info.rate = parse_be32(f->p);
|
if (f_read(f, data, 4) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
f->info.rate = parse_be32(data);
|
||||||
break;
|
break;
|
||||||
case FOURCC('C', 'H', 'N', 'L'):
|
case FOURCC('C', 'H', 'N', 'L'):
|
||||||
f->info.channels = parse_be16(f->p);
|
if (f_read(f, data, 2) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
f->info.channels = parse_be16(data);
|
||||||
switch (f->info.channels) {
|
switch (f->info.channels) {
|
||||||
case 2:
|
case 2:
|
||||||
f->info.channel_type = 2;
|
f->info.channel_type = 2;
|
||||||
|
|
@ -116,7 +139,9 @@ static int read_PROP(struct dff_file *f, struct dff_chunk *prop)
|
||||||
break;
|
break;
|
||||||
case FOURCC('C', 'M', 'P', 'R'):
|
case FOURCC('C', 'M', 'P', 'R'):
|
||||||
{
|
{
|
||||||
uint32_t cmpr = parse_be32(f->p);
|
if (f_read(f, data, 4) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
uint32_t cmpr = parse_be32(data);
|
||||||
if (cmpr != FOURCC('D', 'S', 'D', ' '))
|
if (cmpr != FOURCC('D', 'S', 'D', ' '))
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
break;
|
break;
|
||||||
|
|
@ -138,15 +163,16 @@ static int read_FRM8(struct dff_file *f)
|
||||||
struct dff_chunk c[2];
|
struct dff_chunk c[2];
|
||||||
int res;
|
int res;
|
||||||
bool found_dsd = false;
|
bool found_dsd = false;
|
||||||
|
uint8_t data[4];
|
||||||
|
|
||||||
if ((res = read_chunk(f, &c[0])) < 0)
|
if ((res = read_chunk(f, &c[0])) < 0)
|
||||||
return res;
|
return res;
|
||||||
if (c[0].id != FOURCC('F','R','M','8'))
|
if (c[0].id != FOURCC('F','R','M','8'))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (f_avail(f) < 4 ||
|
|
||||||
memcmp(c[0].data, "DSD ", 4) != 0)
|
if (f_read(f, data, sizeof(data)) < 0 ||
|
||||||
|
memcmp(data, "DSD ", 4) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
f->p += 4;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if ((res = read_chunk(f, &c[1])) < 0)
|
if ((res = read_chunk(f, &c[1])) < 0)
|
||||||
|
|
@ -181,37 +207,33 @@ static int read_FRM8(struct dff_file *f)
|
||||||
static int open_read(struct dff_file *f, const char *filename, struct dff_file_info *info)
|
static int open_read(struct dff_file *f, const char *filename, struct dff_file_info *info)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if ((f->fd = open(filename, O_RDONLY)) < 0) {
|
if (strcmp(filename, "-") != 0) {
|
||||||
res = -errno;
|
if ((f->file = fopen(filename, "r")) == NULL) {
|
||||||
goto exit;
|
res = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
f->close = true;
|
||||||
|
} else {
|
||||||
|
f->close = false;
|
||||||
|
f->file = stdin;
|
||||||
}
|
}
|
||||||
if (fstat(f->fd, &st) < 0) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
f->size = st.st_size;
|
|
||||||
|
|
||||||
f->data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->fd, 0);
|
|
||||||
if (f->data == MAP_FAILED) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
f->p = f->data;
|
|
||||||
|
|
||||||
if ((res = read_FRM8(f)) < 0)
|
if ((res = read_FRM8(f)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
|
|
||||||
|
f->blocksize = BLOCKSIZE * f->info.channels;
|
||||||
|
f->buffer = calloc(1, f->blocksize);
|
||||||
|
if (f->buffer == NULL) {
|
||||||
|
res = -errno;
|
||||||
|
goto exit_close;
|
||||||
|
}
|
||||||
f->mode = 1;
|
f->mode = 1;
|
||||||
*info = f->info;
|
*info = f->info;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_unmap:
|
|
||||||
munmap(f->data, f->size);
|
|
||||||
exit_close:
|
exit_close:
|
||||||
close(f->fd);
|
if (f->close)
|
||||||
|
fclose(f->file);
|
||||||
exit:
|
exit:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -267,18 +289,26 @@ dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_l
|
||||||
int32_t step = SPA_ABS(layout->interleave);
|
int32_t step = SPA_ABS(layout->interleave);
|
||||||
uint32_t channels = f->info.channels;
|
uint32_t channels = f->info.channels;
|
||||||
bool rev = layout->lsb != f->info.lsb;
|
bool rev = layout->lsb != f->info.lsb;
|
||||||
size_t total, offset, scale;
|
size_t total, offset, scale, pos;
|
||||||
|
|
||||||
offset = f->offset;
|
offset = f->offset;
|
||||||
|
pos = offset % f->blocksize;
|
||||||
scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
|
scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
|
||||||
|
|
||||||
samples *= step;
|
samples *= step;
|
||||||
samples *= scale;
|
samples *= scale;
|
||||||
|
|
||||||
for (total = 0; total < samples && offset < f->info.length; total++) {
|
for (total = 0; total < samples; total++) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
int32_t j;
|
int32_t j;
|
||||||
const uint8_t *s = f->p + offset;
|
const uint8_t *s = f->buffer + pos;
|
||||||
|
|
||||||
|
if (pos == 0) {
|
||||||
|
if (fread(f->buffer, 1, f->blocksize, f->file) != f->blocksize)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (f->info.length > 0 && offset >= f->info.length)
|
||||||
|
break;
|
||||||
|
|
||||||
for (i = 0; i < layout->channels; i++) {
|
for (i = 0; i < layout->channels; i++) {
|
||||||
if (layout->interleave > 0) {
|
if (layout->interleave > 0) {
|
||||||
|
|
@ -294,6 +324,9 @@ dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offset += step * channels;
|
offset += step * channels;
|
||||||
|
pos += step * channels;
|
||||||
|
if (pos == f->blocksize)
|
||||||
|
pos = 0;
|
||||||
}
|
}
|
||||||
f->offset = offset;
|
f->offset = offset;
|
||||||
|
|
||||||
|
|
@ -302,12 +335,9 @@ dff_file_read(struct dff_file *f, void *data, size_t samples, const struct dff_l
|
||||||
|
|
||||||
int dff_file_close(struct dff_file *f)
|
int dff_file_close(struct dff_file *f)
|
||||||
{
|
{
|
||||||
if (f->mode == 1) {
|
if (f->close)
|
||||||
munmap(f->data, f->size);
|
fclose(f->file);
|
||||||
} else
|
free(f->buffer);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
close(f->fd);
|
|
||||||
free(f);
|
free(f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,14 @@
|
||||||
#include "dsffile.h"
|
#include "dsffile.h"
|
||||||
|
|
||||||
struct dsf_file {
|
struct dsf_file {
|
||||||
uint8_t *data;
|
uint8_t *buffer;
|
||||||
size_t size;
|
size_t offset;
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
int fd;
|
bool close;
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
struct dsf_file_info info;
|
struct dsf_file_info info;
|
||||||
|
|
||||||
uint8_t *p;
|
|
||||||
size_t offset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline uint32_t parse_le32(const uint8_t *in)
|
static inline uint32_t parse_le32(const uint8_t *in)
|
||||||
|
|
@ -44,104 +42,110 @@ static inline uint64_t parse_le64(const uint8_t *in)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int f_avail(struct dsf_file *f)
|
static inline int f_skip(struct dsf_file *f, size_t bytes)
|
||||||
{
|
{
|
||||||
if (f->p < f->data + f->size)
|
uint8_t data[256];
|
||||||
return f->size + f->data - f->p;
|
while (bytes > 0) {
|
||||||
|
size_t s = fread(data, 1, SPA_MIN(bytes, sizeof(data)), f->file);
|
||||||
|
bytes -= s;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_DSD(struct dsf_file *f)
|
static int read_DSD(struct dsf_file *f)
|
||||||
{
|
{
|
||||||
|
size_t s;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
uint8_t data[28];
|
||||||
|
|
||||||
if (f_avail(f) < 28 ||
|
s = fread(data, 1, 28, f->file);
|
||||||
memcmp(f->p, "DSD ", 4) != 0)
|
if (s < 28 || memcmp(data, "DSD ", 4) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
size = parse_le64(f->p + 4); /* size of this chunk */
|
size = parse_le64(data + 4); /* size of this chunk */
|
||||||
parse_le64(f->p + 12); /* total size */
|
parse_le64(data + 12); /* total size */
|
||||||
parse_le64(f->p + 20); /* metadata */
|
parse_le64(data + 20); /* metadata */
|
||||||
f->p += size;
|
if (size > s)
|
||||||
|
f_skip(f, size - s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_fmt(struct dsf_file *f)
|
static int read_fmt(struct dsf_file *f)
|
||||||
{
|
{
|
||||||
|
size_t s;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
uint8_t data[52];
|
||||||
|
|
||||||
if (f_avail(f) < 52 ||
|
s = fread(data, 1, 52, f->file);
|
||||||
memcmp(f->p, "fmt ", 4) != 0)
|
if (s < 52 || memcmp(data, "fmt ", 4) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
size = parse_le64(f->p + 4); /* size of this chunk */
|
size = parse_le64(data + 4); /* size of this chunk */
|
||||||
if (parse_le32(f->p + 12) != 1) /* version */
|
if (parse_le32(data + 12) != 1) /* version */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (parse_le32(f->p + 16) != 0) /* format id */
|
if (parse_le32(data + 16) != 0) /* format id */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
f->info.channel_type = parse_le32(f->p + 20);
|
f->info.channel_type = parse_le32(data + 20);
|
||||||
f->info.channels = parse_le32(f->p + 24);
|
f->info.channels = parse_le32(data + 24);
|
||||||
f->info.rate = parse_le32(f->p + 28);
|
f->info.rate = parse_le32(data + 28);
|
||||||
f->info.lsb = parse_le32(f->p + 32) == 1;
|
f->info.lsb = parse_le32(data + 32) == 1;
|
||||||
f->info.samples = parse_le64(f->p + 36);
|
f->info.samples = parse_le64(data + 36);
|
||||||
f->info.blocksize = parse_le32(f->p + 44);
|
f->info.blocksize = parse_le32(data + 44);
|
||||||
f->p += size;
|
if (size > s)
|
||||||
|
f_skip(f, size - s);
|
||||||
|
|
||||||
|
f->buffer = calloc(1, f->info.blocksize * f->info.channels);
|
||||||
|
if (f->buffer == NULL)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_data(struct dsf_file *f)
|
static int read_data(struct dsf_file *f)
|
||||||
{
|
{
|
||||||
|
size_t s;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
uint8_t data[12];
|
||||||
|
|
||||||
if (f_avail(f) < 12 ||
|
s = fread(data, 1, 12, f->file);
|
||||||
memcmp(f->p, "data", 4) != 0)
|
if (s < 12 || memcmp(data, "data", 4) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
size = parse_le64(f->p + 4); /* size of this chunk */
|
size = parse_le64(data + 4); /* size of this chunk */
|
||||||
f->info.length = size - 12;
|
f->info.length = size - 12;
|
||||||
f->p += 12;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int open_read(struct dsf_file *f, const char *filename, struct dsf_file_info *info)
|
static int open_read(struct dsf_file *f, const char *filename, struct dsf_file_info *info)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if ((f->fd = open(filename, O_RDONLY)) < 0) {
|
if (strcmp(filename, "-") != 0) {
|
||||||
res = -errno;
|
if ((f->file = fopen(filename, "r")) == NULL) {
|
||||||
goto exit;
|
res = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
f->close = true;
|
||||||
|
} else {
|
||||||
|
f->close = false;
|
||||||
|
f->file = stdin;
|
||||||
}
|
}
|
||||||
if (fstat(f->fd, &st) < 0) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
f->size = st.st_size;
|
|
||||||
|
|
||||||
f->data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->fd, 0);
|
|
||||||
if (f->data == MAP_FAILED) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
f->p = f->data;
|
|
||||||
|
|
||||||
if ((res = read_DSD(f)) < 0)
|
if ((res = read_DSD(f)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
if ((res = read_fmt(f)) < 0)
|
if ((res = read_fmt(f)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
if ((res = read_data(f)) < 0)
|
if ((res = read_data(f)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
|
|
||||||
f->mode = 1;
|
f->mode = 1;
|
||||||
*info = f->info;
|
*info = f->info;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_unmap:
|
|
||||||
munmap(f->data, f->size);
|
|
||||||
exit_close:
|
exit_close:
|
||||||
close(f->fd);
|
if (f->close)
|
||||||
|
fclose(f->file);
|
||||||
exit:
|
exit:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -197,21 +201,28 @@ dsf_file_read(struct dsf_file *f, void *data, size_t samples, const struct dsf_l
|
||||||
int step = SPA_ABS(layout->interleave);
|
int step = SPA_ABS(layout->interleave);
|
||||||
bool rev = layout->lsb != f->info.lsb;
|
bool rev = layout->lsb != f->info.lsb;
|
||||||
size_t total, block, offset, pos, scale;
|
size_t total, block, offset, pos, scale;
|
||||||
|
size_t blocksize = f->info.blocksize * f->info.channels;
|
||||||
|
|
||||||
block = f->offset / f->info.blocksize;
|
block = f->offset / f->info.blocksize;
|
||||||
offset = block * f->info.blocksize * f->info.channels;
|
offset = block * blocksize;
|
||||||
pos = f->offset % f->info.blocksize;
|
pos = f->offset % f->info.blocksize;
|
||||||
scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
|
scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
|
||||||
|
|
||||||
samples *= step;
|
samples *= step;
|
||||||
samples *= scale;
|
samples *= scale;
|
||||||
|
|
||||||
for (total = 0; total < samples && offset + pos < f->info.length; total++) {
|
for (total = 0; total < samples; total++) {
|
||||||
const uint8_t *s = f->p + offset + pos;
|
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (pos == 0) {
|
||||||
|
if (fread(f->buffer, 1, blocksize, f->file) != blocksize)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (f->info.length > 0 && offset + pos >= f->info.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
for (i = 0; i < layout->channels; i++) {
|
for (i = 0; i < layout->channels; i++) {
|
||||||
const uint8_t *c = &s[f->info.blocksize * i];
|
const uint8_t *c = &f->buffer[f->info.blocksize * i + pos];
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
if (layout->interleave > 0) {
|
if (layout->interleave > 0) {
|
||||||
|
|
@ -225,7 +236,7 @@ dsf_file_read(struct dsf_file *f, void *data, size_t samples, const struct dsf_l
|
||||||
pos += step;
|
pos += step;
|
||||||
if (pos == f->info.blocksize) {
|
if (pos == f->info.blocksize) {
|
||||||
pos = 0;
|
pos = 0;
|
||||||
offset += f->info.blocksize * f->info.channels;
|
offset += blocksize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f->offset += total * step;
|
f->offset += total * step;
|
||||||
|
|
@ -235,12 +246,9 @@ dsf_file_read(struct dsf_file *f, void *data, size_t samples, const struct dsf_l
|
||||||
|
|
||||||
int dsf_file_close(struct dsf_file *f)
|
int dsf_file_close(struct dsf_file *f)
|
||||||
{
|
{
|
||||||
if (f->mode == 1) {
|
if (f->close)
|
||||||
munmap(f->data, f->size);
|
fclose(f->file);
|
||||||
} else
|
free(f->buffer);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
close(f->fd);
|
|
||||||
free(f);
|
free(f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,27 +19,28 @@
|
||||||
struct midi_track {
|
struct midi_track {
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
|
|
||||||
uint8_t *data;
|
long start;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
long pos;
|
||||||
|
|
||||||
uint8_t *p;
|
|
||||||
int64_t tick;
|
int64_t tick;
|
||||||
unsigned int eof:1;
|
unsigned int eof:1;
|
||||||
uint8_t event[4];
|
uint8_t event[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct midi_file {
|
struct midi_file {
|
||||||
uint8_t *data;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
int fd;
|
FILE *file;
|
||||||
|
bool close;
|
||||||
|
long pos;
|
||||||
|
|
||||||
|
uint8_t *buffer;
|
||||||
|
size_t buffer_size;
|
||||||
|
|
||||||
struct midi_file_info info;
|
struct midi_file_info info;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
uint32_t tempo;
|
uint32_t tempo;
|
||||||
|
|
||||||
uint8_t *p;
|
|
||||||
int64_t tick;
|
int64_t tick;
|
||||||
double tick_sec;
|
double tick_sec;
|
||||||
double tick_start;
|
double tick_start;
|
||||||
|
|
@ -57,138 +58,157 @@ 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 inline int mf_avail(struct midi_file *mf)
|
static inline int mf_read(struct midi_file *mf, void *data, size_t size)
|
||||||
{
|
{
|
||||||
if (mf->p < mf->data + mf->size)
|
if (fread(data, size, 1, mf->file) != 1)
|
||||||
return mf->size + mf->data - mf->p;
|
return 0;
|
||||||
return 0;
|
mf->pos += size;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int tr_avail(struct midi_track *tr)
|
static inline int tr_avail(struct midi_track *tr)
|
||||||
{
|
{
|
||||||
if (tr->eof)
|
if (tr->eof)
|
||||||
return 0;
|
return 0;
|
||||||
if (tr->p < tr->data + tr->size)
|
if (tr->size == 0)
|
||||||
return tr->size + tr->data - tr->p;
|
return 1;
|
||||||
|
if (tr->pos < tr->start + tr->size)
|
||||||
|
return tr->size + tr->start - tr->pos;
|
||||||
tr->eof = true;
|
tr->eof = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_mthd(struct midi_file *mf)
|
static int read_mthd(struct midi_file *mf)
|
||||||
{
|
{
|
||||||
if (mf_avail(mf) < 14 ||
|
uint8_t data[14];
|
||||||
memcmp(mf->p, "MThd", 4) != 0)
|
|
||||||
return -EINVAL;
|
if (mf_read(mf, data, sizeof(data)) != 1 ||
|
||||||
|
memcmp(data, "MThd", 4) != 0)
|
||||||
mf->length = parse_be32(mf->p + 4);
|
|
||||||
mf->info.format = parse_be16(mf->p + 8);
|
|
||||||
mf->info.ntracks = parse_be16(mf->p + 10);
|
|
||||||
mf->info.division = parse_be16(mf->p + 12);
|
|
||||||
|
|
||||||
mf->p += 14;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_mtrk(struct midi_file *mf, struct midi_track *track)
|
|
||||||
{
|
|
||||||
if (mf_avail(mf) < 8 ||
|
|
||||||
memcmp(mf->p, "MTrk", 4) != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
track->data = track->p = mf->p + 8;
|
|
||||||
track->size = parse_be32(mf->p + 4);
|
|
||||||
|
|
||||||
mf->p = track->data + track->size;
|
|
||||||
if (mf->p > mf->data + mf->size)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
mf->length = parse_be32(data + 4);
|
||||||
|
mf->info.format = parse_be16(data + 8);
|
||||||
|
mf->info.ntracks = parse_be16(data + 10);
|
||||||
|
mf->info.division = parse_be16(data + 12);
|
||||||
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 = 0;
|
uint32_t value = 0;
|
||||||
|
uint8_t data[1];
|
||||||
|
|
||||||
while (tr_avail(tr) > 0) {
|
while (mf_read(mf, data, 1) == 1) {
|
||||||
uint8_t b = *tr->p++;
|
value = (value << 7) | (data[0] & 0x7f);
|
||||||
value = (value << 7) | (b & 0x7f);
|
if ((data[0] & 0x80) == 0)
|
||||||
if ((b & 0x80) == 0)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*result = value;
|
*result = value;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int read_delta_time(struct midi_file *mf, struct midi_track *tr)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
uint32_t delta_time;
|
||||||
|
|
||||||
|
if ((res = parse_varlen(mf, tr, &delta_time)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
tr->tick += delta_time;
|
||||||
|
tr->pos = mf->pos;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_mtrk(struct midi_file *mf, struct midi_track *track)
|
||||||
|
{
|
||||||
|
uint8_t data[8];
|
||||||
|
|
||||||
|
if (mf_read(mf, data, sizeof(data)) != 1 ||
|
||||||
|
memcmp(data, "MTrk", 4) != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
track->start = track->pos = mf->pos;
|
||||||
|
track->size = parse_be32(data + 4);
|
||||||
|
|
||||||
|
return read_delta_time(mf, track);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *ensure_buffer(struct midi_file *mf, struct midi_track *tr, size_t size)
|
||||||
|
{
|
||||||
|
if (size <= 4)
|
||||||
|
return tr->event;
|
||||||
|
|
||||||
|
if (size > mf->buffer_size) {
|
||||||
|
mf->buffer = realloc(mf->buffer, size);
|
||||||
|
mf->buffer_size = size;
|
||||||
|
}
|
||||||
|
return mf->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
static int open_read(struct midi_file *mf, const char *filename, struct midi_file_info *info)
|
static int open_read(struct midi_file *mf, const char *filename, struct midi_file_info *info)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if ((mf->fd = open(filename, O_RDONLY)) < 0) {
|
if (strcmp(filename, "-") != 0) {
|
||||||
res = -errno;
|
if ((mf->file = fopen(filename, "r")) == NULL) {
|
||||||
goto exit;
|
res = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
mf->close = true;
|
||||||
|
} else {
|
||||||
|
mf->file = stdin;
|
||||||
|
mf->close = false;
|
||||||
}
|
}
|
||||||
if (fstat(mf->fd, &st) < 0) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
mf->size = st.st_size;
|
|
||||||
|
|
||||||
mf->data = mmap(NULL, mf->size, PROT_READ, MAP_SHARED, mf->fd, 0);
|
|
||||||
if (mf->data == MAP_FAILED) {
|
|
||||||
res = -errno;
|
|
||||||
goto exit_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
mf->p = mf->data;
|
|
||||||
|
|
||||||
if ((res = read_mthd(mf)) < 0)
|
if ((res = read_mthd(mf)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
|
|
||||||
mf->tempo = DEFAULT_TEMPO;
|
mf->tempo = DEFAULT_TEMPO;
|
||||||
mf->tick = 0;
|
mf->tick = 0;
|
||||||
|
|
||||||
for (i = 0; i < mf->info.ntracks; i++) {
|
for (i = 0; i < mf->info.ntracks; i++) {
|
||||||
struct midi_track *tr = &mf->tracks[i];
|
struct midi_track *tr = &mf->tracks[i];
|
||||||
uint32_t delta_time;
|
|
||||||
|
|
||||||
if ((res = read_mtrk(mf, tr)) < 0)
|
if ((res = read_mtrk(mf, tr)) < 0)
|
||||||
goto exit_unmap;
|
goto exit_close;
|
||||||
|
|
||||||
if ((res = parse_varlen(mf, tr, &delta_time)) < 0)
|
|
||||||
goto exit_unmap;
|
|
||||||
|
|
||||||
tr->tick = delta_time;
|
|
||||||
tr->id = i;
|
tr->id = i;
|
||||||
|
|
||||||
|
if (i + 1 < mf->info.ntracks &&
|
||||||
|
fseek(mf->file, tr->start + tr->size, SEEK_SET) != 0) {
|
||||||
|
res = -errno;
|
||||||
|
goto exit_close;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mf->mode = 1;
|
mf->mode = 1;
|
||||||
*info = mf->info;
|
*info = mf->info;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_unmap:
|
|
||||||
munmap(mf->data, mf->size);
|
|
||||||
exit_close:
|
exit_close:
|
||||||
close(mf->fd);
|
if (mf->close)
|
||||||
|
fclose(mf->file);
|
||||||
exit:
|
exit:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int write_n(int fd, const void *buf, int count)
|
static inline int write_n(FILE *file, const void *buf, int count)
|
||||||
{
|
{
|
||||||
return write(fd, buf, count) == (ssize_t)count ? count : -errno;
|
return fwrite(buf, 1, count, file) == (size_t)count ? count : -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int write_be16(int fd, uint16_t val)
|
static inline int write_be16(FILE *file, uint16_t val)
|
||||||
{
|
{
|
||||||
uint8_t buf[2] = { val >> 8, val };
|
uint8_t buf[2] = { val >> 8, val };
|
||||||
return write_n(fd, buf, 2);
|
return write_n(file, buf, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int write_be32(int fd, uint32_t val)
|
static inline int write_be32(FILE *file, uint32_t val)
|
||||||
{
|
{
|
||||||
uint8_t buf[4] = { val >> 24, val >> 16, val >> 8, val };
|
uint8_t buf[4] = { val >> 24, val >> 16, val >> 8, val };
|
||||||
return write_n(fd, buf, 4);
|
return write_n(file, buf, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_RES(expr) if ((res = (expr)) < 0) return res
|
#define CHECK_RES(expr) if ((res = (expr)) < 0) return res
|
||||||
|
|
@ -198,17 +218,17 @@ static int write_headers(struct midi_file *mf)
|
||||||
struct midi_track *tr = &mf->tracks[0];
|
struct midi_track *tr = &mf->tracks[0];
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
lseek(mf->fd, 0, SEEK_SET);
|
fseek(mf->file, 0, SEEK_SET);
|
||||||
|
|
||||||
mf->length = 6;
|
mf->length = 6;
|
||||||
CHECK_RES(write_n(mf->fd, "MThd", 4));
|
CHECK_RES(write_n(mf->file, "MThd", 4));
|
||||||
CHECK_RES(write_be32(mf->fd, mf->length));
|
CHECK_RES(write_be32(mf->file, mf->length));
|
||||||
CHECK_RES(write_be16(mf->fd, mf->info.format));
|
CHECK_RES(write_be16(mf->file, mf->info.format));
|
||||||
CHECK_RES(write_be16(mf->fd, mf->info.ntracks));
|
CHECK_RES(write_be16(mf->file, mf->info.ntracks));
|
||||||
CHECK_RES(write_be16(mf->fd, mf->info.division));
|
CHECK_RES(write_be16(mf->file, mf->info.division));
|
||||||
|
|
||||||
CHECK_RES(write_n(mf->fd, "MTrk", 4));
|
CHECK_RES(write_n(mf->file, "MTrk", 4));
|
||||||
CHECK_RES(write_be32(mf->fd, tr->size));
|
CHECK_RES(write_be32(mf->file, tr->size));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -226,9 +246,15 @@ static int open_write(struct midi_file *mf, const char *filename, struct midi_fi
|
||||||
if (info->division == 0)
|
if (info->division == 0)
|
||||||
info->division = 96;
|
info->division = 96;
|
||||||
|
|
||||||
if ((mf->fd = open(filename, O_WRONLY | O_CREAT, 0660)) < 0) {
|
if (strcmp(filename, "-") != 0) {
|
||||||
res = -errno;
|
if ((mf->file = fopen(filename, "w")) == NULL) {
|
||||||
goto exit;
|
res = -errno;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
mf->close = true;
|
||||||
|
} else {
|
||||||
|
mf->file = stdout;
|
||||||
|
mf->close = false;
|
||||||
}
|
}
|
||||||
mf->mode = 2;
|
mf->mode = 2;
|
||||||
mf->tempo = DEFAULT_TEMPO;
|
mf->tempo = DEFAULT_TEMPO;
|
||||||
|
|
@ -271,17 +297,17 @@ int midi_file_close(struct midi_file *mf)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (mf->mode == 1) {
|
if (mf->mode == 2) {
|
||||||
munmap(mf->data, mf->size);
|
|
||||||
} else if (mf->mode == 2) {
|
|
||||||
uint8_t buf[4] = { 0x00, 0xff, 0x2f, 0x00 };
|
uint8_t buf[4] = { 0x00, 0xff, 0x2f, 0x00 };
|
||||||
CHECK_RES(write_n(mf->fd, buf, 4));
|
CHECK_RES(write_n(mf->file, buf, 4));
|
||||||
mf->tracks[0].size += 4;
|
mf->tracks[0].size += 4;
|
||||||
CHECK_RES(write_headers(mf));
|
CHECK_RES(write_headers(mf));
|
||||||
} else
|
} else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
close(mf->fd);
|
if (mf->close)
|
||||||
|
fclose(mf->file);
|
||||||
|
free(mf->buffer);
|
||||||
free(mf);
|
free(mf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -322,22 +348,31 @@ int midi_file_next_time(struct midi_file *mf, double *sec)
|
||||||
int midi_file_read_event(struct midi_file *mf, struct midi_event *event)
|
int midi_file_read_event(struct midi_file *mf, struct midi_event *event)
|
||||||
{
|
{
|
||||||
struct midi_track *tr;
|
struct midi_track *tr;
|
||||||
uint32_t delta_time, size;
|
uint32_t size;
|
||||||
uint8_t status, meta;
|
uint8_t status, meta;
|
||||||
int res, running;
|
int res, running;
|
||||||
|
long offs;
|
||||||
|
|
||||||
|
event->data = NULL;
|
||||||
|
|
||||||
if ((res = peek_next(mf, event)) <= 0)
|
if ((res = peek_next(mf, event)) <= 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
tr = &mf->tracks[event->track];
|
tr = &mf->tracks[event->track];
|
||||||
status = *tr->p;
|
|
||||||
|
offs = tr->pos;
|
||||||
|
if (offs != mf->pos) {
|
||||||
|
if (fseek(mf->file, offs, SEEK_SET) != 0)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
mf_read(mf, &status, 1);
|
||||||
|
|
||||||
running = (status & 0x80) == 0;
|
running = (status & 0x80) == 0;
|
||||||
if (running) {
|
if (running) {
|
||||||
|
tr->event[1] = status;
|
||||||
status = tr->event[0];
|
status = tr->event[0];
|
||||||
event->data = tr->event;
|
|
||||||
} else {
|
} else {
|
||||||
event->data = tr->p++;
|
|
||||||
tr->event[0] = status;
|
tr->event[0] = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,52 +387,82 @@ int midi_file_read_event(struct midi_file *mf, struct midi_event *event)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xff:
|
case 0xff:
|
||||||
meta = *tr->p++;
|
if (running)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mf_read(mf, &meta, 1);
|
||||||
|
|
||||||
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
event->meta.offset = tr->p - event->data;
|
event->meta.offset = 2;
|
||||||
event->meta.size = size;
|
event->meta.size = size;
|
||||||
|
|
||||||
|
if ((event->data = ensure_buffer(mf, tr, size + event->meta.offset)) == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
event->data[0] = status;
|
||||||
|
event->data[1] = meta;
|
||||||
|
if (size > 0 && mf_read(mf, &event->data[2], size) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
switch (meta) {
|
switch (meta) {
|
||||||
case 0x2f:
|
case 0x2f:
|
||||||
tr->eof = true;
|
tr->eof = true;
|
||||||
break;
|
break;
|
||||||
case 0x51:
|
case 0x51:
|
||||||
|
{
|
||||||
if (size < 3)
|
if (size < 3)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
mf->tick_sec = event->sec;
|
mf->tick_sec = event->sec;
|
||||||
mf->tick_start = tr->tick;
|
mf->tick_start = tr->tick;
|
||||||
event->meta.parsed.tempo.uspqn = mf->tempo = (tr->p[0]<<16) | (tr->p[1]<<8) | tr->p[2];
|
event->meta.parsed.tempo.uspqn = mf->tempo =
|
||||||
|
(event->data[2]<<16) | (event->data[3]<<8) | event->data[4];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
size += tr->p - event->data;
|
}
|
||||||
|
size += event->meta.offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf0:
|
case 0xf0:
|
||||||
case 0xf7:
|
case 0xf7:
|
||||||
|
if (running)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
if ((res = parse_varlen(mf, tr, &size)) < 0)
|
||||||
return res;
|
return res;
|
||||||
size += tr->p - event->data;
|
|
||||||
|
if ((event->data = ensure_buffer(mf, tr, size + 1)) == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
event->data[0] = status;
|
||||||
|
if (mf_read(mf, &event->data[1], size) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
size += 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
event->size = size;
|
event->size = size;
|
||||||
|
if (event->data == NULL) {
|
||||||
if (running) {
|
if ((event->data = ensure_buffer(mf, tr, size)) == NULL)
|
||||||
memcpy(&event->data[1], tr->p, size - 1);
|
return -ENOMEM;
|
||||||
tr->p += size - 1;
|
event->data[0] = tr->event[0];
|
||||||
} else {
|
if (running) {
|
||||||
tr->p = event->data + event->size;
|
event->data[1] = tr->event[1];
|
||||||
|
if (size > 2 && mf_read(mf, &event->data[2], size - 2) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
if (size > 1 && mf_read(mf, &event->data[1], size - 1) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = parse_varlen(mf, tr, &delta_time)) < 0)
|
if ((res = read_delta_time(mf, tr)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
tr->tick += delta_time;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,7 +479,7 @@ static int write_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t va
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
b = buffer & 0xff;
|
b = buffer & 0xff;
|
||||||
CHECK_RES(write_n(mf->fd, &b, 1));
|
CHECK_RES(write_n(mf->file, &b, 1));
|
||||||
tr->size++;
|
tr->size++;
|
||||||
buffer >>= 8;
|
buffer >>= 8;
|
||||||
} while (b & 0x80);
|
} while (b & 0x80);
|
||||||
|
|
@ -458,7 +523,7 @@ int midi_file_write_event(struct midi_file *mf, const struct midi_event *event)
|
||||||
CHECK_RES(write_varlen(mf, tr, tick - tr->tick));
|
CHECK_RES(write_varlen(mf, tr, tick - tr->tick));
|
||||||
tr->tick = tick;
|
tr->tick = tick;
|
||||||
|
|
||||||
CHECK_RES(write_n(mf->fd, data, size));
|
CHECK_RES(write_n(mf->file, data, size));
|
||||||
tr->size += size;
|
tr->size += size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ struct data {
|
||||||
#define TYPE_ENCODED 3
|
#define TYPE_ENCODED 3
|
||||||
#endif
|
#endif
|
||||||
int data_type;
|
int data_type;
|
||||||
|
bool raw;
|
||||||
const char *remote_name;
|
const char *remote_name;
|
||||||
const char *media_type;
|
const char *media_type;
|
||||||
const char *media_category;
|
const char *media_category;
|
||||||
|
|
@ -980,6 +981,7 @@ static const struct option long_options[] = {
|
||||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||||
{ "volume", required_argument, NULL, OPT_VOLUME },
|
{ "volume", required_argument, NULL, OPT_VOLUME },
|
||||||
{ "quality", required_argument, NULL, 'q' },
|
{ "quality", required_argument, NULL, 'q' },
|
||||||
|
{ "raw", no_argument, NULL, 'a' },
|
||||||
|
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
@ -1024,6 +1026,7 @@ static void show_usage(const char *name, bool is_error)
|
||||||
" --format Sample format %s (req. for rec) (default %s)\n"
|
" --format Sample format %s (req. for rec) (default %s)\n"
|
||||||
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
" --volume Stream volume 0-1.0 (default %.3f)\n"
|
||||||
" -q --quality Resampler quality (0 - 15) (default %d)\n"
|
" -q --quality Resampler quality (0 - 15) (default %d)\n"
|
||||||
|
" -a, --raw RAW mode\n"
|
||||||
"\n"),
|
"\n"),
|
||||||
DEFAULT_RATE,
|
DEFAULT_RATE,
|
||||||
DEFAULT_CHANNELS,
|
DEFAULT_CHANNELS,
|
||||||
|
|
@ -1691,9 +1694,9 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
#ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION
|
||||||
while ((c = getopt_long(argc, argv, "hvprmdoR:q:P:", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "hvprmdoR:q:P:a", long_options, NULL)) != -1) {
|
||||||
#else
|
#else
|
||||||
while ((c = getopt_long(argc, argv, "hvprmdR:q:P:", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "hvprmdR:q:P:a", long_options, NULL)) != -1) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
@ -1745,6 +1748,10 @@ int main(int argc, char *argv[])
|
||||||
data.quality = atoi(optarg);
|
data.quality = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
data.raw = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case OPT_MEDIA_TYPE:
|
case OPT_MEDIA_TYPE:
|
||||||
data.media_type = optarg;
|
data.media_type = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1893,7 +1900,7 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
pw_core_add_listener(data.core, &data.core_listener, &core_events, &data);
|
pw_core_add_listener(data.core, &data.core_listener, &core_events, &data);
|
||||||
|
|
||||||
if (spa_streq(data.filename, "-")) {
|
if (data.raw) {
|
||||||
ret = setup_pipe(&data);
|
ret = setup_pipe(&data);
|
||||||
} else {
|
} else {
|
||||||
switch (data.data_type) {
|
switch (data.data_type) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue