155 lines
4.0 KiB
C
155 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// SPDX-FileCopyrightText: 2020 Jan Engelhardt
|
|
/*
|
|
* pcmmix - trivial wave mixer
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <libHX/init.h>
|
|
#include <libHX/option.h>
|
|
|
|
static char *blocklen_str;
|
|
static unsigned int mixing_mode, sample_rate = 48000;
|
|
|
|
enum {
|
|
MIX_ARPEGGIO = 0,
|
|
MIX_POLYPHONY,
|
|
};
|
|
|
|
static ssize_t fullread(int fd, void *buf, size_t rem)
|
|
{
|
|
size_t pos = 0;
|
|
while (rem > 0) {
|
|
ssize_t ret = read(fd, (char *)buf + pos, rem);
|
|
if (ret == 0)
|
|
return pos;
|
|
if (ret < 0)
|
|
return ret;
|
|
rem -= ret;
|
|
pos += ret;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
int main(int argc, const char **argv)
|
|
{
|
|
static const struct HXoption options_table[] = {
|
|
{.sh = 'a', .type = HXTYPE_NONE | HXTYPE_VAL, .ptr = &mixing_mode,
|
|
.val = MIX_ARPEGGIO, .help = "Arpeggio mix (see -t too)"},
|
|
{.sh = 'p', .type = HXTYPE_NONE | HXTYPE_VAL, .ptr = &mixing_mode,
|
|
.val = MIX_POLYPHONY, .help = "Polyphonic mix"},
|
|
{.sh = 'r', .type = HXTYPE_UINT, .ptr = &sample_rate,
|
|
.help = "Input sample rate (default: 48000)", .htyp = "RATE"},
|
|
{.sh = 't', .type = HXTYPE_STRING, .ptr = &blocklen_str,
|
|
.help = "Block length (default: 40/1086)", .htyp = "p/q"},
|
|
HXOPT_AUTOHELP,
|
|
HXOPT_TABLEEND,
|
|
};
|
|
int ret;
|
|
if ((ret = HX_init()) <= 0) {
|
|
fprintf(stderr, "HX_init: %s\n", strerror(-ret));
|
|
abort();
|
|
}
|
|
if (HX_getopt(options_table, &argc, &argv, HXOPT_USAGEONERR) !=
|
|
HXOPT_ERR_SUCCESS) {
|
|
HX_exit();
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (blocklen_str == NULL)
|
|
blocklen_str = "120/1086";
|
|
|
|
char *e = NULL;
|
|
unsigned int blocklen_p = strtoul(blocklen_str, &e, 0);
|
|
unsigned int blocklen_q = 1;
|
|
if (e != NULL && *e == '/')
|
|
blocklen_q = strtoul(e + 1, NULL, 0);
|
|
fprintf(stderr, "Block length: %u/%us", blocklen_p, blocklen_q);
|
|
unsigned int blocksamples = sample_rate * blocklen_p / blocklen_q;
|
|
unsigned int blockbytes = sizeof(int16_t) * blocksamples;
|
|
fprintf(stderr, ", %u samples, %u bytes\n", blocksamples, blockbytes);
|
|
|
|
--argc;
|
|
++argv;
|
|
|
|
int *inputfd = malloc(sizeof(*inputfd) * argc);
|
|
int inputcount = 0, eofs = 0;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
inputfd[i] = open(argv[i], O_RDONLY);
|
|
if (inputfd[i] < 0) {
|
|
fprintf(stderr, "open %s: %s\n", argv[i], strerror(errno));
|
|
continue;
|
|
}
|
|
++inputcount;
|
|
}
|
|
|
|
if (inputfd == NULL) {
|
|
perror("malloc");
|
|
HX_exit();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (mixing_mode == MIX_ARPEGGIO) {
|
|
void *inputbuf = malloc(blockbytes);
|
|
void *dumbbuf = malloc(blockbytes);
|
|
if (inputbuf == NULL || dumbbuf == NULL) {
|
|
perror("malloc");
|
|
HX_exit();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
unsigned int fragsamples = blocksamples / inputcount;
|
|
unsigned int fragbytes = sizeof(int16_t) * fragsamples;
|
|
fprintf(stderr, "Slot sample count: %u\n", fragsamples);
|
|
|
|
while (eofs < inputcount) {
|
|
memset(inputbuf, 0, sizeof(int16_t) * blocksamples);
|
|
for (int i = 0; i < inputcount; ++i) {
|
|
for (int j = 0; j < inputcount; ++j) {
|
|
void *atp = i != j ? dumbbuf : inputbuf + i * fragbytes;
|
|
ssize_t rdret = fullread(inputfd[i], atp, fragbytes);
|
|
if (i == j && rdret != fragbytes)
|
|
++eofs;
|
|
}
|
|
}
|
|
write(STDOUT_FILENO, inputbuf, inputcount * fragbytes);
|
|
}
|
|
|
|
free(dumbbuf);
|
|
free(inputbuf);
|
|
} else if (mixing_mode == MIX_POLYPHONY) {
|
|
int16_t **inputbuf = malloc(sizeof(int16_t *) * inputcount);
|
|
for (int i = 0; i < inputcount; ++i)
|
|
inputbuf[i] = malloc(blockbytes);
|
|
int16_t *outputbuf = malloc(blockbytes);
|
|
|
|
while (eofs < inputcount) {
|
|
for (int i = 0; i < inputcount; ++i) {
|
|
memset(inputbuf[i], 0, blockbytes);
|
|
ssize_t rdret = fullread(inputfd[i], inputbuf[i], blockbytes);
|
|
if (rdret != blockbytes)
|
|
++eofs;
|
|
}
|
|
for (int s = 0; s < blocksamples; ++s) {
|
|
int32_t v = 0;
|
|
for (int i = 0; i < inputcount; ++i)
|
|
v += inputbuf[i][s];
|
|
outputbuf[s] = v / inputcount;
|
|
}
|
|
write(STDOUT_FILENO, outputbuf, blockbytes);
|
|
}
|
|
free(outputbuf);
|
|
for (int i = 0; i < inputcount; ++i)
|
|
free(inputbuf[i]);
|
|
free(inputbuf);
|
|
}
|
|
|
|
free(inputfd);
|
|
return EXIT_SUCCESS;
|
|
}
|