mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -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"
 | 
			
		||||
 | 
			
		||||
#define BLOCKSIZE	8192
 | 
			
		||||
 | 
			
		||||
struct dff_file {
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	size_t size;
 | 
			
		||||
	uint8_t *buffer;
 | 
			
		||||
	size_t blocksize;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
 | 
			
		||||
	int mode;
 | 
			
		||||
	int fd;
 | 
			
		||||
	bool close;
 | 
			
		||||
	FILE *file;
 | 
			
		||||
	size_t pos;
 | 
			
		||||
 | 
			
		||||
	struct dff_file_info info;
 | 
			
		||||
 | 
			
		||||
	uint8_t *p;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct dff_chunk {
 | 
			
		||||
	uint32_t id;
 | 
			
		||||
	uint64_t size;
 | 
			
		||||
	uint64_t pos;
 | 
			
		||||
	void *data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,28 +60,44 @@ static inline uint64_t parse_be64(const uint8_t *in)
 | 
			
		|||
	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)
 | 
			
		||||
		return f->size + f->data - f->p;
 | 
			
		||||
	size_t s = fread(data, 1, size, f->file);
 | 
			
		||||
	f->pos += s;
 | 
			
		||||
	if (s < size)
 | 
			
		||||
		return -1;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	c->id = parse_be32(f->p);	/* id of this chunk */
 | 
			
		||||
	c->size = parse_be64(f->p + 4);	/* size of this chunk */
 | 
			
		||||
	f->p += 12;
 | 
			
		||||
	c->data = f->p;
 | 
			
		||||
	c->id = parse_be32(data);	/* id of this chunk */
 | 
			
		||||
	c->size = parse_be64(data + 4);	/* size of this chunk */
 | 
			
		||||
	c->pos = f->pos;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,22 +105,26 @@ static int read_PROP(struct dff_file *f, struct dff_chunk *prop)
 | 
			
		|||
{
 | 
			
		||||
	struct dff_chunk c[1];
 | 
			
		||||
	int res;
 | 
			
		||||
	uint8_t data[4];
 | 
			
		||||
 | 
			
		||||
	if (f_avail(f) < 4 ||
 | 
			
		||||
	    memcmp(prop->data, "SND ", 4) != 0)
 | 
			
		||||
	if (f_read(f, data, sizeof(data)) < 0 ||
 | 
			
		||||
	    memcmp(data, "SND ", 4) != 0)
 | 
			
		||||
		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)
 | 
			
		||||
			return res;
 | 
			
		||||
 | 
			
		||||
		switch (c[0].id) {
 | 
			
		||||
		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;
 | 
			
		||||
		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) {
 | 
			
		||||
			case 2:
 | 
			
		||||
				f->info.channel_type = 2;
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +139,9 @@ static int read_PROP(struct dff_file *f, struct dff_chunk *prop)
 | 
			
		|||
			break;
 | 
			
		||||
		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', ' '))
 | 
			
		||||
				return -ENOTSUP;
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -138,15 +163,16 @@ static int read_FRM8(struct dff_file *f)
 | 
			
		|||
	struct dff_chunk c[2];
 | 
			
		||||
	int res;
 | 
			
		||||
	bool found_dsd = false;
 | 
			
		||||
	uint8_t data[4];
 | 
			
		||||
 | 
			
		||||
	if ((res = read_chunk(f, &c[0])) < 0)
 | 
			
		||||
		return res;
 | 
			
		||||
	if (c[0].id != FOURCC('F','R','M','8'))
 | 
			
		||||
		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;
 | 
			
		||||
	f->p += 4;
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		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)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	struct stat st;
 | 
			
		||||
 | 
			
		||||
	if ((f->fd = open(filename, O_RDONLY)) < 0) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	if (strcmp(filename, "-") != 0) {
 | 
			
		||||
		if ((f->file = fopen(filename, "r")) == NULL) {
 | 
			
		||||
			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)
 | 
			
		||||
		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;
 | 
			
		||||
	*info = f->info;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
exit_unmap:
 | 
			
		||||
	munmap(f->data, f->size);
 | 
			
		||||
exit_close:
 | 
			
		||||
	close(f->fd);
 | 
			
		||||
	if (f->close)
 | 
			
		||||
		fclose(f->file);
 | 
			
		||||
exit:
 | 
			
		||||
	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);
 | 
			
		||||
	uint32_t channels = f->info.channels;
 | 
			
		||||
	bool rev = layout->lsb != f->info.lsb;
 | 
			
		||||
	size_t total, offset, scale;
 | 
			
		||||
	size_t total, offset, scale, pos;
 | 
			
		||||
 | 
			
		||||
	offset = f->offset;
 | 
			
		||||
	pos = offset % f->blocksize;
 | 
			
		||||
	scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
 | 
			
		||||
 | 
			
		||||
	samples *= step;
 | 
			
		||||
	samples *= scale;
 | 
			
		||||
 | 
			
		||||
	for (total = 0; total < samples && offset < f->info.length; total++) {
 | 
			
		||||
	for (total = 0; total < samples; total++) {
 | 
			
		||||
		uint32_t i;
 | 
			
		||||
		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++) {
 | 
			
		||||
			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;
 | 
			
		||||
		pos += step * channels;
 | 
			
		||||
		if (pos == f->blocksize)
 | 
			
		||||
			pos = 0;
 | 
			
		||||
	}
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	if (f->mode == 1) {
 | 
			
		||||
		munmap(f->data, f->size);
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	close(f->fd);
 | 
			
		||||
	if (f->close)
 | 
			
		||||
		fclose(f->file);
 | 
			
		||||
	free(f->buffer);
 | 
			
		||||
	free(f);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,16 +14,14 @@
 | 
			
		|||
#include "dsffile.h"
 | 
			
		||||
 | 
			
		||||
struct dsf_file {
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	size_t size;
 | 
			
		||||
	uint8_t *buffer;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
 | 
			
		||||
	int mode;
 | 
			
		||||
	int fd;
 | 
			
		||||
	bool close;
 | 
			
		||||
	FILE *file;
 | 
			
		||||
 | 
			
		||||
	struct dsf_file_info info;
 | 
			
		||||
 | 
			
		||||
	uint8_t *p;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
		return f->size + f->data - f->p;
 | 
			
		||||
	uint8_t data[256];
 | 
			
		||||
	while (bytes > 0) {
 | 
			
		||||
		size_t s = fread(data, 1, SPA_MIN(bytes, sizeof(data)), f->file);
 | 
			
		||||
		bytes -= s;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_DSD(struct dsf_file *f)
 | 
			
		||||
{
 | 
			
		||||
	size_t s;
 | 
			
		||||
	uint64_t size;
 | 
			
		||||
	uint8_t data[28];
 | 
			
		||||
 | 
			
		||||
	if (f_avail(f) < 28 ||
 | 
			
		||||
	    memcmp(f->p, "DSD ", 4) != 0)
 | 
			
		||||
	s = fread(data, 1, 28, f->file);
 | 
			
		||||
	if (s < 28 || memcmp(data, "DSD ", 4) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	size = parse_le64(f->p + 4);	/* size of this chunk */
 | 
			
		||||
	parse_le64(f->p + 12);		/* total size */
 | 
			
		||||
	parse_le64(f->p + 20);		/* metadata */
 | 
			
		||||
	f->p += size;
 | 
			
		||||
	size = parse_le64(data + 4);	/* size of this chunk */
 | 
			
		||||
	parse_le64(data + 12);		/* total size */
 | 
			
		||||
	parse_le64(data + 20);		/* metadata */
 | 
			
		||||
	if (size > s)
 | 
			
		||||
		f_skip(f, size - s);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_fmt(struct dsf_file *f)
 | 
			
		||||
{
 | 
			
		||||
	size_t s;
 | 
			
		||||
	uint64_t size;
 | 
			
		||||
	uint8_t data[52];
 | 
			
		||||
 | 
			
		||||
	if (f_avail(f) < 52 ||
 | 
			
		||||
	    memcmp(f->p, "fmt ", 4) != 0)
 | 
			
		||||
	s = fread(data, 1, 52, f->file);
 | 
			
		||||
	if (s < 52 || memcmp(data, "fmt ", 4) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	size = parse_le64(f->p + 4);	/* size of this chunk */
 | 
			
		||||
	if (parse_le32(f->p + 12) != 1)	/* version */
 | 
			
		||||
	size = parse_le64(data + 4);	/* size of this chunk */
 | 
			
		||||
	if (parse_le32(data + 12) != 1)	/* version */
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (parse_le32(f->p + 16) != 0)	/* format id */
 | 
			
		||||
	if (parse_le32(data + 16) != 0)	/* format id */
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	f->info.channel_type = parse_le32(f->p + 20);
 | 
			
		||||
	f->info.channels = parse_le32(f->p + 24);
 | 
			
		||||
	f->info.rate = parse_le32(f->p + 28);
 | 
			
		||||
	f->info.lsb = parse_le32(f->p + 32) == 1;
 | 
			
		||||
	f->info.samples = parse_le64(f->p + 36);
 | 
			
		||||
	f->info.blocksize = parse_le32(f->p + 44);
 | 
			
		||||
	f->p += size;
 | 
			
		||||
	f->info.channel_type = parse_le32(data + 20);
 | 
			
		||||
	f->info.channels = parse_le32(data + 24);
 | 
			
		||||
	f->info.rate = parse_le32(data + 28);
 | 
			
		||||
	f->info.lsb = parse_le32(data + 32) == 1;
 | 
			
		||||
	f->info.samples = parse_le64(data + 36);
 | 
			
		||||
	f->info.blocksize = parse_le32(data + 44);
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_data(struct dsf_file *f)
 | 
			
		||||
{
 | 
			
		||||
	size_t s;
 | 
			
		||||
	uint64_t size;
 | 
			
		||||
	uint8_t data[12];
 | 
			
		||||
 | 
			
		||||
	if (f_avail(f) < 12 ||
 | 
			
		||||
	    memcmp(f->p, "data", 4) != 0)
 | 
			
		||||
	s = fread(data, 1, 12, f->file);
 | 
			
		||||
	if (s < 12 || memcmp(data, "data", 4) != 0)
 | 
			
		||||
		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->p += 12;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int open_read(struct dsf_file *f, const char *filename, struct dsf_file_info *info)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	struct stat st;
 | 
			
		||||
 | 
			
		||||
	if ((f->fd = open(filename, O_RDONLY)) < 0) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	if (strcmp(filename, "-") != 0) {
 | 
			
		||||
		if ((f->file = fopen(filename, "r")) == NULL) {
 | 
			
		||||
			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)
 | 
			
		||||
		goto exit_unmap;
 | 
			
		||||
		goto exit_close;
 | 
			
		||||
	if ((res = read_fmt(f)) < 0)
 | 
			
		||||
		goto exit_unmap;
 | 
			
		||||
		goto exit_close;
 | 
			
		||||
	if ((res = read_data(f)) < 0)
 | 
			
		||||
		goto exit_unmap;
 | 
			
		||||
		goto exit_close;
 | 
			
		||||
 | 
			
		||||
	f->mode = 1;
 | 
			
		||||
	*info = f->info;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
exit_unmap:
 | 
			
		||||
	munmap(f->data, f->size);
 | 
			
		||||
exit_close:
 | 
			
		||||
	close(f->fd);
 | 
			
		||||
	if (f->close)
 | 
			
		||||
		fclose(f->file);
 | 
			
		||||
exit:
 | 
			
		||||
	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);
 | 
			
		||||
	bool rev = layout->lsb != f->info.lsb;
 | 
			
		||||
	size_t total, block, offset, pos, scale;
 | 
			
		||||
	size_t blocksize = f->info.blocksize * f->info.channels;
 | 
			
		||||
 | 
			
		||||
	block = f->offset / f->info.blocksize;
 | 
			
		||||
	offset = block * f->info.blocksize * f->info.channels;
 | 
			
		||||
	offset = block * blocksize;
 | 
			
		||||
	pos = f->offset % f->info.blocksize;
 | 
			
		||||
	scale = SPA_CLAMP(f->info.rate / (44100u * 64u), 1u, 4u);
 | 
			
		||||
 | 
			
		||||
	samples *= step;
 | 
			
		||||
	samples *= scale;
 | 
			
		||||
 | 
			
		||||
	for (total = 0; total < samples && offset + pos < f->info.length; total++) {
 | 
			
		||||
		const uint8_t *s = f->p + offset + pos;
 | 
			
		||||
	for (total = 0; total < samples; total++) {
 | 
			
		||||
		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++) {
 | 
			
		||||
			const uint8_t *c = &s[f->info.blocksize * i];
 | 
			
		||||
			const uint8_t *c = &f->buffer[f->info.blocksize * i + pos];
 | 
			
		||||
			int j;
 | 
			
		||||
 | 
			
		||||
			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;
 | 
			
		||||
		if (pos == f->info.blocksize) {
 | 
			
		||||
			pos = 0;
 | 
			
		||||
			offset += f->info.blocksize * f->info.channels;
 | 
			
		||||
			offset += blocksize;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	if (f->mode == 1) {
 | 
			
		||||
		munmap(f->data, f->size);
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	close(f->fd);
 | 
			
		||||
	if (f->close)
 | 
			
		||||
		fclose(f->file);
 | 
			
		||||
	free(f->buffer);
 | 
			
		||||
	free(f);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,27 +19,28 @@
 | 
			
		|||
struct midi_track {
 | 
			
		||||
	uint16_t id;
 | 
			
		||||
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	long start;
 | 
			
		||||
	uint32_t size;
 | 
			
		||||
	long pos;
 | 
			
		||||
 | 
			
		||||
	uint8_t *p;
 | 
			
		||||
	int64_t tick;
 | 
			
		||||
	unsigned int eof:1;
 | 
			
		||||
	uint8_t event[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct midi_file {
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	int mode;
 | 
			
		||||
	int fd;
 | 
			
		||||
	FILE *file;
 | 
			
		||||
	bool close;
 | 
			
		||||
	long pos;
 | 
			
		||||
 | 
			
		||||
	uint8_t *buffer;
 | 
			
		||||
	size_t buffer_size;
 | 
			
		||||
 | 
			
		||||
	struct midi_file_info info;
 | 
			
		||||
	uint32_t length;
 | 
			
		||||
	uint32_t tempo;
 | 
			
		||||
 | 
			
		||||
	uint8_t *p;
 | 
			
		||||
	int64_t tick;
 | 
			
		||||
	double tick_sec;
 | 
			
		||||
	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];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
		return mf->size + mf->data - mf->p;
 | 
			
		||||
	return 0;
 | 
			
		||||
	if (fread(data, size, 1, mf->file) != 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
	mf->pos += size;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int tr_avail(struct midi_track *tr)
 | 
			
		||||
{
 | 
			
		||||
	if (tr->eof)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (tr->p < tr->data + tr->size)
 | 
			
		||||
		return tr->size + tr->data - tr->p;
 | 
			
		||||
	if (tr->size == 0)
 | 
			
		||||
		return 1;
 | 
			
		||||
	if (tr->pos < tr->start + tr->size)
 | 
			
		||||
		return tr->size + tr->start - tr->pos;
 | 
			
		||||
	tr->eof = true;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_mthd(struct midi_file *mf)
 | 
			
		||||
{
 | 
			
		||||
	if (mf_avail(mf) < 14 ||
 | 
			
		||||
	    memcmp(mf->p, "MThd", 4) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	uint8_t data[14];
 | 
			
		||||
 | 
			
		||||
	if (mf_read(mf, data, sizeof(data)) != 1 ||
 | 
			
		||||
	    memcmp(data, "MThd", 4) != 0)
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t *result)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t value = 0;
 | 
			
		||||
	uint8_t data[1];
 | 
			
		||||
 | 
			
		||||
	while (tr_avail(tr) > 0) {
 | 
			
		||||
		uint8_t b = *tr->p++;
 | 
			
		||||
		value = (value << 7) | (b & 0x7f);
 | 
			
		||||
		if ((b & 0x80) == 0)
 | 
			
		||||
	while (mf_read(mf, data, 1) == 1) {
 | 
			
		||||
		value = (value << 7) | (data[0] & 0x7f);
 | 
			
		||||
		if ((data[0] & 0x80) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	*result = value;
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	uint16_t i;
 | 
			
		||||
	struct stat st;
 | 
			
		||||
 | 
			
		||||
	if ((mf->fd = open(filename, O_RDONLY)) < 0) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	if (strcmp(filename, "-") != 0) {
 | 
			
		||||
		if ((mf->file = fopen(filename, "r")) == NULL) {
 | 
			
		||||
			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)
 | 
			
		||||
		goto exit_unmap;
 | 
			
		||||
		goto exit_close;
 | 
			
		||||
 | 
			
		||||
	mf->tempo = DEFAULT_TEMPO;
 | 
			
		||||
	mf->tick = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < mf->info.ntracks; i++) {
 | 
			
		||||
		struct midi_track *tr = &mf->tracks[i];
 | 
			
		||||
		uint32_t delta_time;
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		if (i + 1 < mf->info.ntracks &&
 | 
			
		||||
		    fseek(mf->file, tr->start + tr->size, SEEK_SET) != 0) {
 | 
			
		||||
			res = -errno;
 | 
			
		||||
			goto exit_close;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	mf->mode = 1;
 | 
			
		||||
	*info = mf->info;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
exit_unmap:
 | 
			
		||||
	munmap(mf->data, mf->size);
 | 
			
		||||
exit_close:
 | 
			
		||||
	close(mf->fd);
 | 
			
		||||
	if (mf->close)
 | 
			
		||||
		fclose(mf->file);
 | 
			
		||||
exit:
 | 
			
		||||
	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 };
 | 
			
		||||
	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 };
 | 
			
		||||
	return write_n(fd, buf, 4);
 | 
			
		||||
	return write_n(file, buf, 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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];
 | 
			
		||||
	int res;
 | 
			
		||||
 | 
			
		||||
	lseek(mf->fd, 0, SEEK_SET);
 | 
			
		||||
	fseek(mf->file, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	mf->length = 6;
 | 
			
		||||
	CHECK_RES(write_n(mf->fd, "MThd", 4));
 | 
			
		||||
	CHECK_RES(write_be32(mf->fd, mf->length));
 | 
			
		||||
	CHECK_RES(write_be16(mf->fd, mf->info.format));
 | 
			
		||||
	CHECK_RES(write_be16(mf->fd, mf->info.ntracks));
 | 
			
		||||
	CHECK_RES(write_be16(mf->fd, mf->info.division));
 | 
			
		||||
	CHECK_RES(write_n(mf->file, "MThd", 4));
 | 
			
		||||
	CHECK_RES(write_be32(mf->file, mf->length));
 | 
			
		||||
	CHECK_RES(write_be16(mf->file, mf->info.format));
 | 
			
		||||
	CHECK_RES(write_be16(mf->file, mf->info.ntracks));
 | 
			
		||||
	CHECK_RES(write_be16(mf->file, mf->info.division));
 | 
			
		||||
 | 
			
		||||
	CHECK_RES(write_n(mf->fd, "MTrk", 4));
 | 
			
		||||
	CHECK_RES(write_be32(mf->fd, tr->size));
 | 
			
		||||
	CHECK_RES(write_n(mf->file, "MTrk", 4));
 | 
			
		||||
	CHECK_RES(write_be32(mf->file, tr->size));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -226,9 +246,15 @@ static int open_write(struct midi_file *mf, const char *filename, struct midi_fi
 | 
			
		|||
	if (info->division == 0)
 | 
			
		||||
		info->division = 96;
 | 
			
		||||
 | 
			
		||||
	if ((mf->fd = open(filename, O_WRONLY | O_CREAT, 0660)) < 0) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	if (strcmp(filename, "-") != 0) {
 | 
			
		||||
		if ((mf->file = fopen(filename, "w")) == NULL) {
 | 
			
		||||
			res = -errno;
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		mf->close = true;
 | 
			
		||||
	} else {
 | 
			
		||||
		mf->file = stdout;
 | 
			
		||||
		mf->close = false;
 | 
			
		||||
	}
 | 
			
		||||
	mf->mode = 2;
 | 
			
		||||
	mf->tempo = DEFAULT_TEMPO;
 | 
			
		||||
| 
						 | 
				
			
			@ -271,17 +297,17 @@ int midi_file_close(struct midi_file *mf)
 | 
			
		|||
{
 | 
			
		||||
	int res;
 | 
			
		||||
 | 
			
		||||
	if (mf->mode == 1) {
 | 
			
		||||
		munmap(mf->data, mf->size);
 | 
			
		||||
	} else if (mf->mode == 2) {
 | 
			
		||||
	if (mf->mode == 2) {
 | 
			
		||||
		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;
 | 
			
		||||
		CHECK_RES(write_headers(mf));
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	close(mf->fd);
 | 
			
		||||
	if (mf->close)
 | 
			
		||||
		fclose(mf->file);
 | 
			
		||||
	free(mf->buffer);
 | 
			
		||||
	free(mf);
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	struct midi_track *tr;
 | 
			
		||||
	uint32_t delta_time, size;
 | 
			
		||||
	uint32_t size;
 | 
			
		||||
	uint8_t status, meta;
 | 
			
		||||
	int res, running;
 | 
			
		||||
	long offs;
 | 
			
		||||
 | 
			
		||||
	event->data = NULL;
 | 
			
		||||
 | 
			
		||||
	if ((res = peek_next(mf, event)) <= 0)
 | 
			
		||||
		return res;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	if (running) {
 | 
			
		||||
		tr->event[1] = status;
 | 
			
		||||
		status = tr->event[0];
 | 
			
		||||
		event->data = tr->event;
 | 
			
		||||
	} else {
 | 
			
		||||
		event->data = tr->p++;
 | 
			
		||||
		tr->event[0] = status;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -352,52 +387,82 @@ int midi_file_read_event(struct midi_file *mf, struct midi_event *event)
 | 
			
		|||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0xff:
 | 
			
		||||
		meta = *tr->p++;
 | 
			
		||||
		if (running)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		mf_read(mf, &meta, 1);
 | 
			
		||||
 | 
			
		||||
		if ((res = parse_varlen(mf, tr, &size)) < 0)
 | 
			
		||||
			return res;
 | 
			
		||||
 | 
			
		||||
		event->meta.offset = tr->p - event->data;
 | 
			
		||||
		event->meta.offset = 2;
 | 
			
		||||
		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) {
 | 
			
		||||
		case 0x2f:
 | 
			
		||||
			tr->eof = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x51:
 | 
			
		||||
		{
 | 
			
		||||
			if (size < 3)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			mf->tick_sec = event->sec;
 | 
			
		||||
			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;
 | 
			
		||||
		}
 | 
			
		||||
		size += tr->p - event->data;
 | 
			
		||||
		}
 | 
			
		||||
		size += event->meta.offset;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0xf0:
 | 
			
		||||
	case 0xf7:
 | 
			
		||||
		if (running)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		if ((res = parse_varlen(mf, tr, &size)) < 0)
 | 
			
		||||
			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;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	event->size = size;
 | 
			
		||||
 | 
			
		||||
	if (running) {
 | 
			
		||||
		memcpy(&event->data[1], tr->p, size - 1);
 | 
			
		||||
		tr->p += size - 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		tr->p = event->data + event->size;
 | 
			
		||||
	if (event->data == NULL) {
 | 
			
		||||
		if ((event->data = ensure_buffer(mf, tr, size)) == NULL)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		event->data[0] = tr->event[0];
 | 
			
		||||
		if (running) {
 | 
			
		||||
			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;
 | 
			
		||||
 | 
			
		||||
	tr->tick += delta_time;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -414,7 +479,7 @@ static int write_varlen(struct midi_file *mf, struct midi_track *tr, uint32_t va
 | 
			
		|||
	}
 | 
			
		||||
        do  {
 | 
			
		||||
		b = buffer & 0xff;
 | 
			
		||||
		CHECK_RES(write_n(mf->fd, &b, 1));
 | 
			
		||||
		CHECK_RES(write_n(mf->file, &b, 1));
 | 
			
		||||
		tr->size++;
 | 
			
		||||
		buffer >>= 8;
 | 
			
		||||
	} 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));
 | 
			
		||||
	tr->tick = tick;
 | 
			
		||||
 | 
			
		||||
	CHECK_RES(write_n(mf->fd, data, size));
 | 
			
		||||
	CHECK_RES(write_n(mf->file, data, size));
 | 
			
		||||
	tr->size += size;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,6 +103,7 @@ struct data {
 | 
			
		|||
#define TYPE_ENCODED    3
 | 
			
		||||
#endif
 | 
			
		||||
	int data_type;
 | 
			
		||||
	bool raw;
 | 
			
		||||
	const char *remote_name;
 | 
			
		||||
	const char *media_type;
 | 
			
		||||
	const char *media_category;
 | 
			
		||||
| 
						 | 
				
			
			@ -980,6 +981,7 @@ static const struct option long_options[] = {
 | 
			
		|||
	{ "format",		required_argument, NULL, OPT_FORMAT },
 | 
			
		||||
	{ "volume",		required_argument, NULL, OPT_VOLUME },
 | 
			
		||||
	{ "quality",		required_argument, NULL, 'q' },
 | 
			
		||||
	{ "raw",		no_argument, NULL, 'a' },
 | 
			
		||||
 | 
			
		||||
	{ 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"
 | 
			
		||||
	     "      --volume                          Stream volume 0-1.0 (default %.3f)\n"
 | 
			
		||||
	     "  -q  --quality                         Resampler quality (0 - 15) (default %d)\n"
 | 
			
		||||
	     "  -a, --raw                             RAW mode\n"
 | 
			
		||||
	     "\n"),
 | 
			
		||||
	     DEFAULT_RATE,
 | 
			
		||||
	     DEFAULT_CHANNELS,
 | 
			
		||||
| 
						 | 
				
			
			@ -1691,9 +1694,9 @@ int main(int argc, char *argv[])
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
		switch (c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1745,6 +1748,10 @@ int main(int argc, char *argv[])
 | 
			
		|||
			data.quality = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'a':
 | 
			
		||||
			data.raw = true;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case OPT_MEDIA_TYPE:
 | 
			
		||||
			data.media_type = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1893,7 +1900,7 @@ int main(int argc, char *argv[])
 | 
			
		|||
	}
 | 
			
		||||
	pw_core_add_listener(data.core, &data.core_listener, &core_events, &data);
 | 
			
		||||
 | 
			
		||||
	if (spa_streq(data.filename, "-")) {
 | 
			
		||||
	if (data.raw) {
 | 
			
		||||
		ret = setup_pipe(&data);
 | 
			
		||||
	} else {
 | 
			
		||||
		switch (data.data_type) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue