initial commit
This commit is contained in:
commit
36ca6059c1
49 changed files with 5290 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/bankconv
|
||||
/bankdump
|
||||
/banktest
|
||||
/mididump
|
||||
/oplplayer
|
11
Makefile
Normal file
11
Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
all: oplplayer bankdump bankconv banktest mididump
|
||||
oplplayer: oplplayer.c alsasnd.h ../argparse.h audio.h bank.h dmx.h midi.h strings.h wavfile.h opl3.h
|
||||
gcc -g -o oplplayer oplplayer.c -lm -lasound -lncurses
|
||||
bankdump: bankdump.c ../argparse.h dmx.h strings.h
|
||||
gcc -g -o bankdump bankdump.c
|
||||
bankconv: bankconv.c ../argparse.h dmx.h strings.h
|
||||
gcc -g -o bankconv bankconv.c
|
||||
banktest: banktest.c alsasnd.h ../argparse.h audio.h bank.h dmx.h strings.h wavfile.h opl3.h
|
||||
gcc -g -o banktest banktest.c -lm -lasound
|
||||
mididump: mididump.c ../argparse.h bank.h midi.h strings.h
|
||||
gcc -g -o mididump mididump.c
|
296
alsasnd.h
Normal file
296
alsasnd.h
Normal file
|
@ -0,0 +1,296 @@
|
|||
#ifdef SND_S32
|
||||
#define pcms_t int32_t
|
||||
#define aud_ihandle aud_handle32
|
||||
#define SND_FMT SND_PCM_FORMAT_S32_LE
|
||||
#else
|
||||
#define pcms_t int16_t
|
||||
#define aud_ihandle aud_handle16
|
||||
#define SND_FMT SND_PCM_FORMAT_S16_LE
|
||||
#endif
|
||||
|
||||
#define SND_SMPSIZE (sizeof(pcms_t))
|
||||
|
||||
typedef struct {
|
||||
snd_pcm_t *handle;
|
||||
uint32_t samplerate;
|
||||
uint32_t blocksize;
|
||||
uint32_t channels;
|
||||
uint32_t smppos;
|
||||
pcms_t *buffer;
|
||||
uint32_t devbufsize;
|
||||
wav_handle wav;
|
||||
#ifdef SND_MSGUNDER
|
||||
uint32_t underruns;
|
||||
uint32_t lastunder;
|
||||
uint32_t pushed;
|
||||
#endif
|
||||
} aud_ihandle;
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
wav_write32
|
||||
#else
|
||||
wav_write16
|
||||
#endif
|
||||
(wav_handle *wav, pcms_t *samples, uint32_t count) {
|
||||
if(wav->fd == NULL) {
|
||||
return;
|
||||
}
|
||||
for(int c = 0; c < (count * ((uint32_t)wav->channels)); c++) {
|
||||
#ifdef SND_S32
|
||||
wav_write_uint32((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
|
||||
#else
|
||||
wav_write_uint16((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
|
||||
#endif
|
||||
}
|
||||
if(fwrite(wav->buffer, 1, SND_SMPSIZE * ((uint32_t)wav->channels) * count, wav->fd) != (SND_SMPSIZE * ((uint32_t)wav->channels) * count)) {
|
||||
fprintf(stderr, STR_WAV_EIO"\n", wav->filename);
|
||||
fclose(wav->fd);
|
||||
wav->fd = NULL;
|
||||
return;
|
||||
}
|
||||
wav->samples += count;
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
aud_prepare32
|
||||
#else
|
||||
aud_prepare16
|
||||
#endif
|
||||
(aud_ihandle *dev) {
|
||||
if(dev->handle == NULL) {
|
||||
return;
|
||||
}
|
||||
memset(dev->buffer, 0, dev->blocksize * SND_SMPSIZE * dev->channels);
|
||||
dev->smppos = 0;
|
||||
int rc = 0;
|
||||
uint32_t tries = 0;
|
||||
while(rc != dev->blocksize) {
|
||||
if(tries++ >= 256) {
|
||||
mid_log(STR_AUD_PREPERR);
|
||||
return;
|
||||
}
|
||||
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
|
||||
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < dev->blocksize)) {
|
||||
snd_pcm_wait(dev->handle, 100);
|
||||
}
|
||||
else if(rc == -EPIPE) {
|
||||
snd_pcm_prepare(dev->handle);
|
||||
}
|
||||
}
|
||||
snd_pcm_nonblock(dev->handle, 1);
|
||||
for(int z = 0; z < 16; z++) {
|
||||
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
|
||||
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < dev->blocksize)) {
|
||||
snd_pcm_wait(dev->handle, 100);
|
||||
}
|
||||
else if(rc == -EPIPE) {
|
||||
snd_pcm_prepare(dev->handle);
|
||||
}
|
||||
}
|
||||
snd_pcm_nonblock(dev->handle, 0);
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
aud_write32
|
||||
#else
|
||||
aud_write16
|
||||
#endif
|
||||
(aud_ihandle *dev, pcms_t *data, uint32_t samples) {
|
||||
int rc;
|
||||
uint32_t blocksize = dev->blocksize;
|
||||
while(samples > 0) {
|
||||
blocksize = samples < blocksize ? samples : blocksize;
|
||||
if(dev->handle) {
|
||||
rc = snd_pcm_writei(dev->handle, data, blocksize);
|
||||
}
|
||||
else if(dev->wav.buffer) {
|
||||
#ifdef SND_S32
|
||||
wav_write32(&dev->wav, data, blocksize);
|
||||
#else
|
||||
wav_write16(&dev->wav, data, blocksize);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
fwrite(data, 1, blocksize * dev->channels * SND_SMPSIZE, stdout);
|
||||
}
|
||||
data += blocksize * dev->channels;
|
||||
samples -= blocksize;
|
||||
if(dev->handle) {
|
||||
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < blocksize)) {
|
||||
snd_pcm_wait(dev->handle, 100);
|
||||
}
|
||||
else if(rc == -EPIPE) {
|
||||
#ifdef SND_MSGUNDER
|
||||
if(dev->underruns == 0) {
|
||||
dev->lastunder = dev->pushed;
|
||||
}
|
||||
dev->underruns += 1;
|
||||
#endif
|
||||
snd_pcm_prepare(dev->handle);
|
||||
}
|
||||
else if(rc < 0) {
|
||||
mid_log(STR_AUD_WRITERR, snd_strerror(rc), rc);
|
||||
}
|
||||
#ifdef SND_MSGUNDER
|
||||
else {
|
||||
dev->pushed += 1;
|
||||
}
|
||||
if((dev->underruns > 0) && ((dev->pushed - dev->lastunder) >= (dev->samplerate / dev->blocksize))) {
|
||||
mid_log(STR_AUD_UNDERR, dev->underruns);
|
||||
dev->underruns = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
#ifdef SND_S32
|
||||
aud_open_dev32
|
||||
#else
|
||||
aud_open_dev16
|
||||
#endif
|
||||
(aud_ihandle *dev, const char *devname, uint32_t samplerate, uint8_t channels, uint32_t frames, uint32_t bufsize) {
|
||||
dev->channels = channels;
|
||||
dev->samplerate = samplerate;
|
||||
dev->smppos = 0;
|
||||
#ifdef SND_MSGUNDER
|
||||
dev->underruns = 0;
|
||||
dev->lastunder = 0;
|
||||
dev->pushed = 0;
|
||||
#endif
|
||||
dev->handle = NULL;
|
||||
dev->buffer = NULL;
|
||||
dev->wav.buffer = NULL;
|
||||
if((devname[0] == '-') && (devname[1] == 0)) {
|
||||
dev->blocksize = frames;
|
||||
dev->devbufsize = 0;
|
||||
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
|
||||
return 0;
|
||||
}
|
||||
else if((strlen(devname) >= 4) && (strcmp(devname+(strlen(devname)-4), ".wav") == 0)) {
|
||||
if(wav_open(&dev->wav, devname, samplerate, channels, SND_SMPSIZE) ^ 1) {
|
||||
return false;
|
||||
}
|
||||
dev->blocksize = frames;
|
||||
dev->devbufsize = 0;
|
||||
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
|
||||
dev->wav.buffer = malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
|
||||
return 0;
|
||||
}
|
||||
int rc;
|
||||
int subdir = 0;
|
||||
snd_pcm_hw_params_t *params = NULL;
|
||||
rc = snd_pcm_open(&dev->handle, devname, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if(rc < 0) {
|
||||
fprintf(stderr, STR_AUD_OPNERR"\n", devname, snd_strerror(rc), rc);
|
||||
return 1;
|
||||
}
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
snd_pcm_hw_params_any(dev->handle, params);
|
||||
snd_pcm_hw_params_set_access(dev->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if(snd_pcm_hw_params_set_format(dev->handle, params, SND_FMT) != 0) {
|
||||
snd_pcm_close(dev->handle);
|
||||
return 2;
|
||||
}
|
||||
snd_pcm_hw_params_set_channels(dev->handle, params, dev->channels);
|
||||
snd_pcm_hw_params_set_rate_near(dev->handle, params, &dev->samplerate, &subdir);
|
||||
snd_pcm_uframes_t framesize = frames;
|
||||
snd_pcm_hw_params_set_period_size_near(dev->handle, params, &framesize, &subdir);
|
||||
snd_pcm_uframes_t buffersize = bufsize;
|
||||
if(bufsize > 0) {
|
||||
snd_pcm_hw_params_set_buffer_size_near(dev->handle, params, &buffersize);
|
||||
}
|
||||
rc = snd_pcm_hw_params(dev->handle, params);
|
||||
if(rc < 0) {
|
||||
fprintf(stderr, STR_AUD_HWERR"\n", devname, snd_strerror(rc), rc);
|
||||
snd_pcm_close(dev->handle);
|
||||
return 1;
|
||||
}
|
||||
snd_pcm_hw_params_get_period_size(params, &framesize, &subdir);
|
||||
snd_pcm_hw_params_get_buffer_size(params, &buffersize);
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_current(dev->handle, swparams);
|
||||
// snd_pcm_sw_params_set_avail_min(dev->handle, swparams, framesize);
|
||||
// snd_pcm_sw_params_set_start_threshold(dev->handle, swparams, 0);
|
||||
// snd_pcm_sw_params_set_stop_threshold(dev->handle, swparams, buffersize);
|
||||
rc = snd_pcm_sw_params(dev->handle, swparams);
|
||||
if(rc < 0) {
|
||||
fprintf(stderr, STR_AUD_SWERR"\n", devname, snd_strerror(rc), rc);
|
||||
snd_pcm_close(dev->handle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dev->blocksize = framesize;
|
||||
dev->devbufsize = buffersize;
|
||||
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
|
||||
snd_pcm_wait(dev->handle, -1);
|
||||
#ifdef SND_S32
|
||||
aud_prepare32(dev);
|
||||
#else
|
||||
aud_prepare16(dev);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
aud_push32
|
||||
#else
|
||||
aud_push16
|
||||
#endif
|
||||
(aud_ihandle *dev, pcms_t *data) {
|
||||
memcpy(&dev->buffer[dev->smppos*dev->channels], data, SND_SMPSIZE * dev->channels);
|
||||
dev->smppos++;
|
||||
if(dev->smppos == dev->blocksize) {
|
||||
#ifdef SND_S32
|
||||
aud_write32(dev, dev->buffer, dev->blocksize);
|
||||
#else
|
||||
aud_write16(dev, dev->buffer, dev->blocksize);
|
||||
#endif
|
||||
dev->smppos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
aud_flush32
|
||||
#else
|
||||
aud_flush16
|
||||
#endif
|
||||
(aud_ihandle *dev) {
|
||||
if(dev->smppos != 0) {
|
||||
#ifdef SND_S32
|
||||
aud_write32(dev, dev->buffer, dev->smppos);
|
||||
#else
|
||||
aud_write16(dev, dev->buffer, dev->smppos);
|
||||
#endif
|
||||
dev->smppos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef SND_S32
|
||||
aud_close32
|
||||
#else
|
||||
aud_close16
|
||||
#endif
|
||||
(aud_ihandle *dev) {
|
||||
if(dev->handle) {
|
||||
snd_pcm_drain(dev->handle);
|
||||
snd_pcm_close(dev->handle);
|
||||
}
|
||||
if(dev->wav.buffer) {
|
||||
wav_close(&dev->wav);
|
||||
free(dev->wav.buffer);
|
||||
}
|
||||
free(dev->buffer);
|
||||
}
|
||||
|
||||
#undef pcms_t
|
||||
#undef aud_ihandle
|
||||
#undef SND_FMT
|
271
argparse.h
Normal file
271
argparse.h
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
Argument parser 0.3.2 (Sen)
|
||||
#define ARGPARSE_DE -> Deutsche Ausgabe
|
||||
#define ARGPARSE_OUT -> Ausgabe-Funktion
|
||||
*/
|
||||
|
||||
#ifndef ARGPARSE_H
|
||||
#define ARGPARSE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef ARGPARSE_DE
|
||||
#define STR_ARG_USAGE "Usage"
|
||||
#define STR_ARG_HELP "output this help"
|
||||
#define STR_ARG_OPVEC "Option %s (-%c)"
|
||||
#define STR_ARG_OPPOS "Option %s (#%d)"
|
||||
#define STR_ARG_EINT "must be a full number"
|
||||
#define STR_ARG_ERANGE "has to be in range %d .. %d"
|
||||
#define STR_ARG_UNKNOWN "Unknown option -%c"
|
||||
#define STR_ARG_EMORE "Too many arguments, maximum %d"
|
||||
#define STR_ARG_ELESS "Not enough arguments, minimum %d"
|
||||
#define STR_ARG_EREQARG STR_ARG_OPVEC " needs an argument"
|
||||
#else
|
||||
#define STR_ARG_USAGE "Verwendung"
|
||||
#define STR_ARG_HELP "Diese Hilfe anzeigen"
|
||||
#define STR_ARG_OPVEC "Option %s (-%c)"
|
||||
#define STR_ARG_OPPOS "Option %s (#%d)"
|
||||
#define STR_ARG_EINT "muss ein ganzzahliger Wert sein"
|
||||
#define STR_ARG_ERANGE "muss sich im Bereich %d .. %d befinden"
|
||||
#define STR_ARG_UNKNOWN "Unbekannte Option -%c"
|
||||
#define STR_ARG_EMORE "Zu viele Argumente, Maximum %d"
|
||||
#define STR_ARG_ELESS "Nicht genug Argumente, Minimum %d"
|
||||
#define STR_ARG_EREQARG STR_ARG_OPVEC " benoetigt ein Argument"
|
||||
#endif
|
||||
|
||||
#define STR_ARG_EINT_V STR_ARG_OPVEC " " STR_ARG_EINT
|
||||
#define STR_ARG_EINT_P STR_ARG_OPPOS " " STR_ARG_EINT
|
||||
#define STR_ARG_ERANGE_V STR_ARG_OPVEC " " STR_ARG_ERANGE
|
||||
#define STR_ARG_ERANGE_P STR_ARG_OPPOS " " STR_ARG_ERANGE
|
||||
|
||||
#ifndef ARGPARSE_OUT
|
||||
#include <stdio.h>
|
||||
#define ARGPARSE_OUT(s ...) fprintf(stderr, s)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char spec;
|
||||
char arg;
|
||||
int min;
|
||||
int max;
|
||||
int *value;
|
||||
char **str;
|
||||
const char *name;
|
||||
} arg_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int arg_min;
|
||||
unsigned int arg_max;
|
||||
unsigned int arg_count;
|
||||
arg_t *arg_vec;
|
||||
} argp_t;
|
||||
|
||||
void arg_help(argp_t *argspec, char *prog) {
|
||||
arg_t *arg_vec = argspec->arg_vec;
|
||||
ARGPARSE_OUT(STR_ARG_USAGE": %s\n [-h ("STR_ARG_HELP")]\n", prog);
|
||||
for(int h = 0; h < argspec->arg_count; h++) {
|
||||
arg_t *arg = &arg_vec[h];
|
||||
if(arg->spec) {
|
||||
if(arg->arg) {
|
||||
if((arg->min == 0) && (arg->max == 0)) {
|
||||
ARGPARSE_OUT(" [-%c <%s>]\n", arg->spec, arg->name);
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(" [-%c <%s (%d-%d)>]\n", arg->spec, arg->name, arg->min, arg->max);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(" [-%c (%s)]\n", arg->spec, arg->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int h = 0; h < argspec->arg_max; h++) {
|
||||
arg_t *arg = &arg_vec[h];
|
||||
char nb = (h < argspec->arg_min) ? '<' : '[';
|
||||
char eb = (h < argspec->arg_min) ? '>' : ']';
|
||||
if((arg->min == 0) && (arg->max == 0)) {
|
||||
ARGPARSE_OUT(" %c%s%s%c\n", nb, arg->name, (arg->value == NULL) ? "" : ", ...", eb);
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(" %c%s (%d-%d)%c\n", nb, arg->name, arg->min, arg->max, eb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char arg_process(arg_t *arg_vec, char *arg, int off) {
|
||||
if((arg_vec[off].min == 0) && (arg_vec[off].max == 0)) {
|
||||
if(arg_vec[off].value != NULL) {
|
||||
arg_vec[off].str[*(arg_vec[off].value)] = arg;
|
||||
*(arg_vec[off].value) += 1;
|
||||
}
|
||||
else {
|
||||
*(arg_vec[off].str) = arg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errno = 0;
|
||||
char *endptr;
|
||||
*(arg_vec[off].value) = strtol(arg, &endptr, 10);
|
||||
if((errno != 0) || (endptr[0] != 0)) {
|
||||
if(arg_vec[off].spec) {
|
||||
ARGPARSE_OUT(STR_ARG_EINT_V"\n", arg_vec[off].name, arg_vec[off].spec);
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(STR_ARG_EINT_P"\n", arg_vec[off].name, off + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if((*(arg_vec[off].value) < arg_vec[off].min) || (*(arg_vec[off].value) > arg_vec[off].max)) {
|
||||
if(arg_vec[off].spec) {
|
||||
ARGPARSE_OUT(STR_ARG_ERANGE_V"\n", arg_vec[off].name, arg_vec[off].spec, arg_vec[off].min, arg_vec[off].max);
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(STR_ARG_ERANGE_P"\n", arg_vec[off].name, off + 1, arg_vec[off].min, arg_vec[off].max);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char arg_parse(argp_t *argspec, int argc, char **argv) {
|
||||
unsigned int arg_min = argspec->arg_min;
|
||||
unsigned int arg_max = argspec->arg_max;
|
||||
unsigned int arg_count = argspec->arg_count;
|
||||
arg_t *arg_vec = argspec->arg_vec;
|
||||
unsigned int idx = 0;
|
||||
unsigned int sp = 0;
|
||||
char sa = 0;
|
||||
for(int h = 1; h < argc; h++) {
|
||||
char *arg = argv[h];
|
||||
if((sa == 0) && (arg[0] == '-') && (arg[1] != 0)) {
|
||||
arg += 1;
|
||||
if(arg[0] == 'h') {
|
||||
arg_help(argspec, argv[0]);
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
while((arg[0] != 0) && (sa == 0)) {
|
||||
int f = 0;
|
||||
for(int h = 0; h < arg_count; h++) {
|
||||
if((arg_vec[h].arg ^ 1) && (arg[0] == arg_vec[h].spec)) {
|
||||
*(arg_vec[h].value) = 1;
|
||||
arg += 1;
|
||||
f++;
|
||||
break;
|
||||
}
|
||||
else if(arg[0] == arg_vec[h].spec) {
|
||||
sa = arg[0];
|
||||
sp = h;
|
||||
arg += 1;
|
||||
f++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(f == 0) {
|
||||
ARGPARSE_OUT(STR_ARG_UNKNOWN"\n", arg[0]);
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(arg[0] == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(sa == 0) {
|
||||
if((arg[0] == '\\') && (arg[1] == '-')) {
|
||||
arg += 1;
|
||||
}
|
||||
if((idx < arg_max) || ((arg_max > 0) && (arg_vec[arg_max-1].min == 0) && (arg_vec[arg_max-1].max == 0) && (arg_vec[arg_max-1].value != NULL))) {
|
||||
if(!arg_process(arg_vec, arg, (idx >= arg_max) ? (arg_max-1) : idx)) {
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
ARGPARSE_OUT(STR_ARG_EMORE"\n", arg_max);
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(!arg_process(arg_vec, arg, sp)) {
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
sa = 0;
|
||||
}
|
||||
if(sa != 0) {
|
||||
ARGPARSE_OUT(STR_ARG_EREQARG"\n", arg_vec[sp].name, sa);
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
if(idx < arg_min) {
|
||||
ARGPARSE_OUT(STR_ARG_ELESS"\n", arg_min);
|
||||
free(argspec);
|
||||
return 0;
|
||||
}
|
||||
free(argspec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void arg_add_gen(argp_t *argspec, char spec, const char *name, int min, int max, char argreq, int *value, char **str) {
|
||||
arg_t *arg = &argspec->arg_vec[(argspec->arg_count)++];
|
||||
arg->spec = spec;
|
||||
arg->arg = argreq;
|
||||
arg->min = min;
|
||||
arg->max = max;
|
||||
arg->value = value;
|
||||
arg->str = str;
|
||||
arg->name = name;
|
||||
}
|
||||
|
||||
void arg_add_int(argp_t *argspec, char spec, const char *name, int min, int max, int *value) {
|
||||
arg_add_gen(argspec, spec, name, min, max, 1, value, NULL);
|
||||
}
|
||||
|
||||
void arg_add_str(argp_t *argspec, char spec, const char *name, char **value) {
|
||||
arg_add_gen(argspec, spec, name, 0, 0, 1, NULL, value);
|
||||
}
|
||||
|
||||
void arg_add_bool(argp_t *argspec, char spec, const char *name, int *value) {
|
||||
arg_add_gen(argspec, spec, name, 0, 0, 0, value, NULL);
|
||||
}
|
||||
|
||||
void arg_add_nint(argp_t *argspec, const char *name, char req, int min, int max, int *value) {
|
||||
arg_add_gen(argspec, 0, name, min, max, 0, value, NULL);
|
||||
argspec->arg_max += 1;
|
||||
argspec->arg_min += req ? 1 : 0;
|
||||
}
|
||||
|
||||
void arg_add_nstr(argp_t *argspec, const char *name, char req, char **value) {
|
||||
arg_add_gen(argspec, 0, name, 0, 0, 0, NULL, value);
|
||||
argspec->arg_max += 1;
|
||||
argspec->arg_min += req ? 1 : 0;
|
||||
}
|
||||
|
||||
void arg_add_nbool(argp_t *argspec, const char *name, char req, int *value) {
|
||||
arg_add_gen(argspec, 0, name, 0, 1, 0, value, NULL);
|
||||
argspec->arg_max += 1;
|
||||
argspec->arg_min += req ? 1 : 0;
|
||||
}
|
||||
|
||||
void arg_add_vstr(argp_t *argspec, const char *name, char req, char **svalue, int *ivalue) {
|
||||
arg_add_gen(argspec, 0, name, 0, 0, 0, ivalue, svalue);
|
||||
argspec->arg_max = 1;
|
||||
argspec->arg_min += req ? 1 : 0;
|
||||
}
|
||||
|
||||
argp_t *arg_alloc(unsigned int count) {
|
||||
char *data = malloc(sizeof(argp_t) + (count * sizeof(arg_t)));
|
||||
argp_t *argspec = (argp_t*)data;
|
||||
data += sizeof(argp_t);
|
||||
argspec->arg_vec = (arg_t*)data;
|
||||
argspec->arg_count = 0;
|
||||
argspec->arg_min = 0;
|
||||
argspec->arg_max = 0;
|
||||
}
|
||||
|
||||
#endif
|
97
audio.h
Normal file
97
audio.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "wavfile.h"
|
||||
|
||||
#define SND_MSGUNDER
|
||||
|
||||
#define SND_S32
|
||||
#include "alsasnd.h"
|
||||
|
||||
#undef SND_S32
|
||||
#include "alsasnd.h"
|
||||
|
||||
typedef struct {
|
||||
aud_handle16 ihandle;
|
||||
aud_handle32 lhandle;
|
||||
uint32_t samplerate;
|
||||
bool iswav;
|
||||
bool iswide;
|
||||
} aud_handle;
|
||||
|
||||
bool aud_open_dev(aud_handle *dev, const char *devname, uint32_t samplerate, uint8_t channels, uint32_t frames, uint32_t bufsize, bool wide) {
|
||||
if(wide) {
|
||||
int state = aud_open_dev32(&dev->lhandle, devname, samplerate, channels, frames, bufsize);
|
||||
if(state == 2) {
|
||||
wide = false;
|
||||
mid_log(STR_AUD_FMTCH"\n", devname, 32, 16);
|
||||
state = aud_open_dev16(&dev->ihandle, devname, samplerate, channels, frames, bufsize);
|
||||
if(state == 2) {
|
||||
mid_log(STR_AUD_FMTERR"\n", devname);
|
||||
return false;
|
||||
}
|
||||
else if(state != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(state != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int state = aud_open_dev16(&dev->ihandle, devname, samplerate, channels, frames, bufsize);
|
||||
if(state == 2) {
|
||||
wide = true;
|
||||
mid_log(STR_AUD_FMTCH"\n", devname, 16, 32);
|
||||
state = aud_open_dev32(&dev->lhandle, devname, samplerate, channels, frames, bufsize);
|
||||
if(state == 2) {
|
||||
mid_log(STR_AUD_FMTERR"\n", devname);
|
||||
return false;
|
||||
}
|
||||
else if(state != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(state != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(wide) {
|
||||
dev->samplerate = dev->lhandle.samplerate;
|
||||
dev->iswav = dev->lhandle.wav.buffer != NULL;
|
||||
}
|
||||
else {
|
||||
dev->samplerate = dev->ihandle.samplerate;
|
||||
dev->iswav = dev->ihandle.wav.buffer != NULL;
|
||||
}
|
||||
dev->iswide = wide;
|
||||
return true;
|
||||
}
|
||||
|
||||
void aud_push(aud_handle *dev, int16_t *data) {
|
||||
if(dev->iswide) {
|
||||
int32_t conv[dev->lhandle.channels];
|
||||
for(int h = 0; h < dev->lhandle.channels; h++) {
|
||||
conv[h] = ((int32_t)data[h]) << 16;
|
||||
}
|
||||
aud_push32(&dev->lhandle, conv);
|
||||
}
|
||||
else {
|
||||
aud_push16(&dev->ihandle, data);
|
||||
}
|
||||
}
|
||||
|
||||
void aud_flush(aud_handle *dev) {
|
||||
if(dev->iswide) {
|
||||
aud_flush32(&dev->lhandle);
|
||||
}
|
||||
else {
|
||||
aud_flush16(&dev->ihandle);
|
||||
}
|
||||
}
|
||||
|
||||
void aud_close(aud_handle *dev) {
|
||||
if(dev->iswide) {
|
||||
aud_close32(&dev->lhandle);
|
||||
}
|
||||
else {
|
||||
aud_close16(&dev->ihandle);
|
||||
}
|
||||
}
|
487
bank.h
Normal file
487
bank.h
Normal file
|
@ -0,0 +1,487 @@
|
|||
#define BANK_MAX 64
|
||||
#define BANK_KEEP 0x01
|
||||
#define BANK_UNKN 0x02
|
||||
|
||||
typedef struct _bank_voice bank_voice;
|
||||
typedef struct _bank_key bank_key;
|
||||
typedef struct _bank_channel bank_channel;
|
||||
typedef struct _bank_handle bank_handle;
|
||||
|
||||
struct _bank_voice {
|
||||
bank_channel *channel;
|
||||
opl3_channel *opl;
|
||||
uint8_t note;
|
||||
uint8_t op;
|
||||
int16_t detune;
|
||||
bank_voice *pair;
|
||||
};
|
||||
|
||||
struct _bank_key {
|
||||
uint8_t note;
|
||||
uint8_t velocity;
|
||||
bank_voice *voice;
|
||||
};
|
||||
|
||||
struct _bank_channel {
|
||||
bank_handle *bank;
|
||||
bank_key keys[BANK_MAX];
|
||||
uint8_t notes[128];
|
||||
uint8_t keyindex;
|
||||
uint8_t active;
|
||||
uint16_t pbank;
|
||||
int8_t pan;
|
||||
uint8_t volume;
|
||||
int16_t pitch;
|
||||
uint8_t program;
|
||||
bank_instr *instr;
|
||||
uint8_t ch_num;
|
||||
};
|
||||
|
||||
struct _bank_handle {
|
||||
bank_channel channel[16];
|
||||
bank_voice *voices;
|
||||
bank_instr *bdata;
|
||||
uint16_t voiceindex;
|
||||
uint16_t v_avail;
|
||||
uint16_t v_used;
|
||||
uint8_t flags;
|
||||
int8_t velo_func;
|
||||
};
|
||||
|
||||
void bank_reset(opl3_chip *chip, bank_handle *bank) {
|
||||
uint16_t voices = chip->n_voices;
|
||||
uint8_t flags = bank->flags;
|
||||
int8_t velo_func = bank->velo_func;
|
||||
bank_instr *instr = bank->bdata;
|
||||
uint8_t *bankdata = (uint8_t*)bank;
|
||||
bankdata += sizeof(bank_handle);
|
||||
memset(bank, 0, sizeof(bank_handle));
|
||||
bank->voices = (bank_voice*)bankdata;
|
||||
bank->v_avail = voices;
|
||||
bank->flags = flags;
|
||||
bank->velo_func = velo_func;
|
||||
bank->bdata = instr;
|
||||
for(int h = 0; h < 16; h++) {
|
||||
bank_channel *channel = &bank->channel[h];
|
||||
channel->bank = bank;
|
||||
memset(channel->notes, 0xff, 128);
|
||||
channel->volume = 127;
|
||||
channel->pbank = (h == 9) ? 128 : 0;
|
||||
channel->instr = &bank->bdata[(h == 9) ? 128 : 0];
|
||||
channel->ch_num = h;
|
||||
}
|
||||
for(int h = 0; h < voices; h++) {
|
||||
bank_voice *voice = &bank->voices[h];
|
||||
voice->channel = NULL;
|
||||
voice->opl = &chip->channel[h];
|
||||
voice->note = 0xff;
|
||||
voice->op = b_op2;
|
||||
voice->detune = 0;
|
||||
voice->pair = &bank->voices[h+(((h & 1) == 0) ? 1 : -1)];
|
||||
}
|
||||
}
|
||||
|
||||
bank_handle *bank_alloc(opl3_chip *chip, bank_instr *instr, uint8_t keep, uint8_t useunkn, int8_t velofunc) {
|
||||
uint8_t *bankdata = malloc(sizeof(bank_handle) + (sizeof(bank_voice) * chip->n_voices));
|
||||
bank_handle *bank = (bank_handle*)bankdata;
|
||||
bank->flags = (keep ? BANK_KEEP : 0) | (useunkn ? BANK_UNKN : 0);
|
||||
bank->velo_func = velofunc;
|
||||
bank->bdata = instr;
|
||||
bank_reset(chip, bank);
|
||||
return bank;
|
||||
}
|
||||
|
||||
static const uint32_t bank_notes[] = {
|
||||
8175, 8661, 9177, 9722, 10300, 10913, 11562, 12249,
|
||||
12978, 13750, 14567, 15433, 16351, 17323, 18354, 19445,
|
||||
20601, 21826, 23124, 24499, 25956, 27500, 29135, 30867,
|
||||
32703, 34647, 36708, 38890, 41203, 43653, 46249, 48999,
|
||||
51913, 55000, 58270, 61735, 65406, 69295, 73416, 77781,
|
||||
82406, 87307, 92498, 97998, 103826, 110000, 116540, 123470,
|
||||
130812, 138591, 146832, 155563, 164813, 174614, 184997, 195997,
|
||||
207652, 220000, 233081, 246941, 261625, 277182, 293664, 311126,
|
||||
329627, 349228, 369994, 391995, 415304, 440000, 466163, 493883,
|
||||
523251, 554365, 587329, 622253, 659255, 698456, 739988, 783990,
|
||||
830609, 880000, 932327, 987766, 1046502, 1108730, 1174659, 1244507,
|
||||
1318510, 1396912, 1479977, 1567981, 1661218, 1760000, 1864655, 1975533,
|
||||
2093004, 2217461, 2349318, 2489015, 2637020, 2793825, 2959955, 3135963,
|
||||
3322437, 3520000, 3729310, 3951066, 4186009, 4434922, 4698636, 4978031,
|
||||
5274040, 5587651, 5919910, 6271926, 6644875, 7040000, 7458620, 7902132,
|
||||
8372018, 8869844, 9397272, 9956063, 10548081, 11175303, 11839821, 12543853
|
||||
};
|
||||
|
||||
static const uint32_t opl3_maxfreq[] = {
|
||||
48503,
|
||||
97006,
|
||||
194013,
|
||||
388026,
|
||||
776053,
|
||||
1552107,
|
||||
3104215,
|
||||
6208431
|
||||
};
|
||||
|
||||
uint8_t bank_getnote(bank_channel *ch, uint8_t key, uint8_t id, uint8_t drum, bank_instr *instr) {
|
||||
int16_t note = (int16_t)key;
|
||||
if(instr->fixed /* || drum */) {
|
||||
note = ((int16_t)instr->percnum) + instr->channels[id].offset;
|
||||
}
|
||||
else {
|
||||
note += instr->channels[id].offset;
|
||||
}
|
||||
note = note < 0 ? 0 : note;
|
||||
note = note > 127 ? 127 : note;
|
||||
return (uint8_t)note;
|
||||
}
|
||||
|
||||
static void OPL3_ChannelFreqHz(opl3_channel *channel, uint32_t freq) {
|
||||
uint32_t block = 0;
|
||||
while(opl3_maxfreq[block] < freq) {
|
||||
block++;
|
||||
if(block == 8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(block == 8) {
|
||||
OPL3_ChannelFreq(channel, 7, 1023);
|
||||
return;
|
||||
}
|
||||
double f_num = ((double)freq) / 1000.0 * pow(2.0, (20.0-((double)block))) / 49716.0;
|
||||
OPL3_ChannelFreq(channel, block, (uint32_t)f_num);
|
||||
}
|
||||
|
||||
#define BANK_PBRANGE 2.0
|
||||
|
||||
uint32_t bank_getfreq(uint8_t key, int16_t pitch, int16_t detune) {
|
||||
double pfrq = pow(2.0, ((((double)pitch) / 8191.0 * BANK_PBRANGE) + (((double)detune) / 100.0)) / 12.0);
|
||||
double freq = ((double)bank_notes[key]) * pfrq;
|
||||
return (uint32_t)freq;
|
||||
}
|
||||
|
||||
static void OPL3_ChannelNote(opl3_channel *channel, uint8_t key, int16_t pitch, int16_t detune) {
|
||||
OPL3_ChannelFreqHz(channel, bank_getfreq(key, pitch, detune));
|
||||
}
|
||||
|
||||
#define BANK_VELOFUNC(x, n) (1.0-log(1.0+((1.0-pow(x,n))*(M_E-1.0))))
|
||||
|
||||
uint32_t bank_getlevel(int8_t function, uint8_t velocity, uint8_t volume, int8_t pan) {
|
||||
double lvl = ((function == -128) ? 1.0 :
|
||||
(function ? BANK_VELOFUNC(((double)velocity)/127.0, function < 0 ? (0.1+(1.0-((((double)(-function))-1.0)/126.0))*9.9) : (1.0+((((double)function)-1.0)/126.0)*9.0)) :
|
||||
(((double)velocity)/127.0))) * (((double)volume)/127.0) * (1.0-(0.9*((double)pan)/63.0));
|
||||
// fprintf(stderr, "%d===%d-->%.2f\n", function, velocity, lvl);
|
||||
lvl *= 65536.0;
|
||||
return (uint32_t)lvl;
|
||||
}
|
||||
|
||||
static void OPL3_ChannelLevelPan(opl3_channel *channel, int8_t function, uint8_t velocity, uint8_t volume, int8_t pan) {
|
||||
OPL3_ChannelOutput(channel, 0, bank_getlevel(function, velocity, volume, pan));
|
||||
OPL3_ChannelOutput(channel, 1, bank_getlevel(function, velocity, volume, -pan));
|
||||
}
|
||||
|
||||
void bank_release_voice(bank_handle *bank, bank_channel *ch, bank_voice *voice) {
|
||||
if(voice->note == 0xff) {
|
||||
return;
|
||||
}
|
||||
OPL3_ChannelKeyOff(voice->opl);
|
||||
voice->note = 0xff;
|
||||
if(voice->op == b_op22) {
|
||||
voice->pair->note = 0xff;
|
||||
OPL3_ChannelKeyOff(voice->pair->opl);
|
||||
bank->v_used -= 2;
|
||||
}
|
||||
else if(voice->op == b_op4) {
|
||||
voice->pair->note = 0xff;
|
||||
bank->v_used -= 2;
|
||||
}
|
||||
else {
|
||||
bank->v_used -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
bank_voice *bank_release_key(bank_channel *ch, uint8_t note) {
|
||||
if(ch->active == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if(ch->notes[note] == 0xff) {
|
||||
return NULL;
|
||||
}
|
||||
bank_key *key = &ch->keys[ch->notes[note]];
|
||||
ch->notes[note] = 0xff;
|
||||
ch->active -= 1;
|
||||
if(key->voice) {
|
||||
bank_release_voice(ch->bank, ch, key->voice);
|
||||
}
|
||||
key->velocity = 0;
|
||||
return key->voice;
|
||||
}
|
||||
|
||||
void bank_init_voice(opl3_channel *channel, bank_instr *instr, uint8_t id) {
|
||||
OPL3_Channel4Op(channel, instr->op == b_op4);
|
||||
for(int o = 0; o < ((instr->op == b_op4) ? 4 : 2); o++) {
|
||||
bank_pair *pair = &instr->channels[(instr->op == b_op4) ? (o >> 1) : id];
|
||||
bank_operator *op = &pair->ops[o & 1];
|
||||
opl3_slot *slot = (o >= 2) ? (channel->pair->slots[o & 1]) : (channel->slots[o & 1]);
|
||||
OPL3_SlotFlags(slot, op->tremolo, op->vibrato, op->sustaining, op->ksr);
|
||||
OPL3_SlotMult(slot, op->mult);
|
||||
OPL3_SlotKSL(slot, op->ksl);
|
||||
OPL3_SlotLevel(slot, op->level);
|
||||
OPL3_SlotADSR(slot, op->attack, op->decay, op->sustain, op->release);
|
||||
OPL3_SlotWaveform(slot, op->waveform);
|
||||
if((o & 1) == 1) {
|
||||
opl3_channel *chn = (o >= 2) ? channel->pair : channel;
|
||||
OPL3_ChannelFeedback(chn, pair->feedback);
|
||||
OPL3_ChannelAM(chn, pair->am);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bank_voice *bank_get_voice(bank_handle *bank, bank_channel *ch, uint8_t note, uint8_t velocity) {
|
||||
bank_instr *instr = ch->instr;
|
||||
if(ch->pbank == 128) {
|
||||
instr += note;
|
||||
}
|
||||
else if((ch->pbank != 0) && (!(bank->flags & BANK_UNKN))) {
|
||||
return NULL;
|
||||
}
|
||||
if(instr->op == b_op0) {
|
||||
return NULL;
|
||||
}
|
||||
if(bank->v_used == bank->v_avail) {
|
||||
if(bank->flags & BANK_KEEP) {
|
||||
return NULL;
|
||||
}
|
||||
if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) {
|
||||
bank->voiceindex += 1;
|
||||
if(bank->voiceindex >= bank->v_avail) {
|
||||
bank->voiceindex = 0;
|
||||
}
|
||||
}
|
||||
bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note);
|
||||
}
|
||||
else if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) {
|
||||
bank->voiceindex += 1;
|
||||
if(bank->voiceindex >= bank->v_avail) {
|
||||
bank->voiceindex = 0;
|
||||
}
|
||||
}
|
||||
uint8_t vi = bank->voiceindex;
|
||||
while((bank->voices[bank->voiceindex].note != 0xff) || ((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff))) {
|
||||
bank->voiceindex += (instr->op != b_op2) ? 2 : 1;
|
||||
if(bank->voiceindex >= bank->v_avail) {
|
||||
bank->voiceindex = 0;
|
||||
}
|
||||
if(vi == bank->voiceindex) {
|
||||
if(bank->flags & BANK_KEEP) {
|
||||
return NULL;
|
||||
}
|
||||
bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note);
|
||||
if((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff)) {
|
||||
bank_release_key(bank->voices[bank->voiceindex+1].channel, bank->voices[bank->voiceindex+1].note);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
bank_voice *voice = &bank->voices[bank->voiceindex];
|
||||
if((instr->op == b_op2) && (voice->op == b_op4)) {
|
||||
int16_t offset = ((int16_t)bank->voiceindex) + (((bank->voiceindex & 1) == 0) ? 1 : -1);
|
||||
bank_voice *voice2 = &bank->voices[offset];
|
||||
if(voice2->note == 0xff) {
|
||||
OPL3_ChannelOutput(voice2->opl, 0, 0);
|
||||
OPL3_ChannelOutput(voice2->opl, 1, 0);
|
||||
}
|
||||
}
|
||||
bank->voiceindex += (instr->op != b_op2) ? 2 : 1;
|
||||
if(bank->voiceindex >= bank->v_avail) {
|
||||
bank->voiceindex = 0;
|
||||
}
|
||||
voice->channel = ch;
|
||||
voice->note = note;
|
||||
voice->op = instr->op;
|
||||
voice->detune = instr->channels[0].detune;
|
||||
bank_init_voice(voice->opl, instr, 0);
|
||||
if(voice->op == b_op22) {
|
||||
voice->pair->channel = ch;
|
||||
voice->pair->note = note;
|
||||
voice->pair->op = b_op22;
|
||||
voice->pair->detune = instr->channels[1].detune;
|
||||
bank_init_voice(voice->pair->opl, instr, 1);
|
||||
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
|
||||
OPL3_ChannelNote(voice->pair->opl, bank_getnote(ch, note, 1, ch->pbank == 128, instr), ch->pitch, voice->pair->detune);
|
||||
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
|
||||
OPL3_ChannelLevelPan(voice->pair->opl, bank->velo_func, velocity, ch->volume, ch->pan);
|
||||
OPL3_ChannelKeyOn(voice->opl);
|
||||
OPL3_ChannelKeyOn(voice->pair->opl);
|
||||
bank->v_used += 2;
|
||||
}
|
||||
else if(voice->op == b_op4) {
|
||||
voice->pair->channel = ch;
|
||||
voice->pair->note = note;
|
||||
voice->pair->op = b_op4;
|
||||
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
|
||||
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
|
||||
OPL3_ChannelKeyOn(voice->opl);
|
||||
bank->v_used += 2;
|
||||
}
|
||||
else {
|
||||
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
|
||||
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
|
||||
OPL3_ChannelKeyOn(voice->opl);
|
||||
bank->v_used += 1;
|
||||
}
|
||||
return voice;
|
||||
}
|
||||
|
||||
bank_voice *bank_press_key(bank_channel *ch, uint8_t note, uint8_t velocity) {
|
||||
if(ch->notes[note] != 0xff) {
|
||||
bank_release_key(ch, note);
|
||||
}
|
||||
if(ch->active == BANK_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t ki = ch->keyindex;
|
||||
while(ch->keys[ch->keyindex].velocity != 0) {
|
||||
ch->keyindex += 1;
|
||||
if(ch->keyindex == BANK_MAX) {
|
||||
ch->keyindex = 0;
|
||||
}
|
||||
if(ki == ch->keyindex) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bank_voice *voice = bank_get_voice(ch->bank, ch, note, velocity);
|
||||
ch->notes[note] = ch->keyindex;
|
||||
ch->active += 1;
|
||||
bank_key *key = &ch->keys[ch->keyindex];
|
||||
key->note = note;
|
||||
key->velocity = velocity;
|
||||
key->voice = voice;
|
||||
return voice;
|
||||
}
|
||||
|
||||
void bank_notesoff(bank_channel *ch) {
|
||||
for(int h = 0; (h < BANK_MAX) && (ch->active > 0); h++) {
|
||||
bank_release_key(ch, h);
|
||||
}
|
||||
}
|
||||
|
||||
void bank_progupdate(bank_channel *ch, opl3_chip *chip) {
|
||||
bank_notesoff(ch);
|
||||
uint16_t id = ch->program;
|
||||
if(ch->pbank == 128) {
|
||||
id = 128;
|
||||
}
|
||||
else if((ch->pbank != 0) && (!(ch->bank->flags & BANK_UNKN))) {
|
||||
id = 0;
|
||||
}
|
||||
ch->instr = (ch->bank->bdata)+id;
|
||||
}
|
||||
|
||||
void bank_levelupdate(bank_channel *ch, opl3_chip *chip) {
|
||||
uint8_t done = 0;
|
||||
for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) {
|
||||
bank_key *key = (ch->keys)+h;
|
||||
if((key->velocity == 0) || (key->voice == NULL)) {
|
||||
continue;
|
||||
}
|
||||
OPL3_ChannelLevelPan(key->voice->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan);
|
||||
if(key->voice->op == b_op22) {
|
||||
OPL3_ChannelLevelPan(key->voice->pair->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan);
|
||||
}
|
||||
done++;
|
||||
}
|
||||
}
|
||||
|
||||
void bank_frequpdate(bank_channel *ch, opl3_chip *chip) {
|
||||
uint8_t done = 0;
|
||||
for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) {
|
||||
bank_key *key = (ch->keys)+h;
|
||||
if((key->velocity == 0) || (key->voice == NULL)) {
|
||||
continue;
|
||||
}
|
||||
OPL3_ChannelNote(key->voice->opl, bank_getnote(ch, key->note, 0, ch->pbank == 128, ch->instr), ch->pitch, key->voice->detune);
|
||||
if(key->voice->op == b_op22) {
|
||||
OPL3_ChannelNote(key->voice->pair->opl, bank_getnote(ch, key->note, 1, ch->pbank == 128, ch->instr), ch->pitch, key->voice->pair->detune);
|
||||
}
|
||||
done++;
|
||||
}
|
||||
}
|
||||
|
||||
void bank_noteon(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t key, uint8_t velocity) {
|
||||
bank_channel *ch = &bank->channel[channel];
|
||||
// if((ch->pbank == 128) && ((key < 35) || (key > 81))) {
|
||||
// return;
|
||||
// }
|
||||
bank_press_key(ch, key, velocity);
|
||||
}
|
||||
|
||||
void bank_noteoff(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t key, uint8_t velocity) {
|
||||
bank_channel *ch = &bank->channel[channel];
|
||||
// if((ch->pbank == 128) && ((key < 35) || (key > 81))) {
|
||||
// return;
|
||||
// }
|
||||
bank_release_key(ch, key);
|
||||
}
|
||||
|
||||
void bank_progchange(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t program) {
|
||||
bank_channel *ch = &bank->channel[channel];
|
||||
ch->program = program;
|
||||
bank_progupdate(ch, chip);
|
||||
}
|
||||
|
||||
void bank_pitchbend(bank_handle *bank, opl3_chip *chip, uint8_t channel, int16_t pitch) {
|
||||
bank_channel *ch = &bank->channel[channel];
|
||||
ch->pitch = pitch;
|
||||
bank_frequpdate(ch, chip);
|
||||
}
|
||||
|
||||
void bank_control(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t control, uint8_t value) {
|
||||
bank_channel *ch = &bank->channel[channel];
|
||||
switch(control) {
|
||||
case 0x00: // bank MSB
|
||||
// if((channel == 9) && (value == 0)) {
|
||||
// ch->pbank = 128;
|
||||
// }
|
||||
// else {
|
||||
if(channel != 9) {
|
||||
ch->pbank &= 0x007f;
|
||||
ch->pbank |= (((uint16_t)value) << 7) & 0x3f80;
|
||||
}
|
||||
bank_progupdate(ch, chip);
|
||||
break;
|
||||
case 0x20: // bank LSB
|
||||
// if((channel == 9) && (value == 0)) {
|
||||
// ch->pbank = 128;
|
||||
// }
|
||||
// else {
|
||||
if(channel != 9) {
|
||||
ch->pbank &= 0x3f80;
|
||||
ch->pbank |= ((uint16_t)value) & 0x007f;
|
||||
}
|
||||
bank_progupdate(ch, chip);
|
||||
break;
|
||||
case 0x07: // volume MSB
|
||||
ch->volume = value;
|
||||
bank_levelupdate(ch, chip);
|
||||
break;
|
||||
case 0x0a: // pan MSB
|
||||
ch->pan = ((int8_t)value) - 64;
|
||||
bank_levelupdate(ch, chip);
|
||||
break;
|
||||
case 0x79: // reset
|
||||
ch->pbank = (channel == 9) ? 128 : 0;
|
||||
ch->volume = 127;
|
||||
ch->pan = 0;
|
||||
bank_progupdate(ch, chip);
|
||||
bank_levelupdate(ch, chip);
|
||||
break;
|
||||
case 0x7b: // all off
|
||||
bank_notesoff(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bank_alloff(bank_handle *bank) {
|
||||
for(int h = 0; h < 16; h++) {
|
||||
bank_notesoff(&bank->channel[h]);
|
||||
}
|
||||
}
|
40
bankconv.c
Normal file
40
bankconv.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "strings.h"
|
||||
#include "dmx.h"
|
||||
#define ARGPARSE_DE
|
||||
#include "../argparse.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *dmxname = NULL;
|
||||
char *drumname = NULL;
|
||||
char *skbname = NULL;
|
||||
int32_t usedrum = 0;
|
||||
|
||||
argp_t *argspec = arg_alloc(4);
|
||||
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
|
||||
arg_add_nstr(argspec, STR_ARGS_SKB, true, &skbname);
|
||||
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
|
||||
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
|
||||
if(arg_parse(argspec, argc, argv) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
|
||||
if(instr == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if(skb_write_bank(instr, skbname) ^ 1) {
|
||||
free(instr);
|
||||
return 1;
|
||||
}
|
||||
free(instr);
|
||||
return 0;
|
||||
}
|
147
bankdump.c
Normal file
147
bankdump.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "strings.h"
|
||||
#include "dmx.h"
|
||||
#define ARGPARSE_DE
|
||||
#include "../argparse.h"
|
||||
|
||||
void dump_bank(bank_instr *instr, uint8_t prog, uint8_t name) {
|
||||
bank_instr *ins = &instr[prog];
|
||||
if(ins->op == b_op0) {
|
||||
return;
|
||||
}
|
||||
printf("[] #%d - MIDI %s #%d\n", prog, (prog < 128) ? "program" : "drum", (prog < 128) ? prog : (prog - 128));
|
||||
if(name) {
|
||||
printf(" [] name = '%s'\n", ins->name);
|
||||
}
|
||||
printf(" [] mode = %s\n", (ins->op == b_op22) ? "2x2OP" : ((ins->op == b_op4) ? "4OP" : "2OP"));
|
||||
printf(" [] notes = %s\n", ins->fixed ? "fixed" : "variable");
|
||||
printf(" [] percussion number = %d\n", ins->percnum);
|
||||
for(int ch = 0; ch < ((ins->op == b_op2) ? 1 : 2); ch++) {
|
||||
bank_pair *chn = &ins->channels[ch];
|
||||
printf(" [] voice %d\n", ch + 1);
|
||||
printf(" [] detune = %d\n", chn->detune);
|
||||
printf(" [] note offset = %d\n", chn->offset);
|
||||
printf(" [] feedback = %d\n", chn->feedback);
|
||||
printf(" [] algorithm = %s\n", chn->am ? "AM" : "FM");
|
||||
for(int op = 0; op < 2; op++) {
|
||||
bank_operator *opr = &chn->ops[op];
|
||||
printf(" [] %s\n", op ? "carrier" : "modulator");
|
||||
printf(" [] freq. multiplier = %d\n", opr->mult);
|
||||
printf(" [] output level = %d\n", 63-opr->level);
|
||||
printf(" [] waveform = %d\n", opr->waveform);
|
||||
printf(" [] key scale level = %d\n", opr->ksl);
|
||||
printf(" [] attack rate = %d\n", opr->attack);
|
||||
printf(" [] decay rate = %d\n", opr->decay);
|
||||
printf(" [] sustain level = %d\n", 15-opr->sustain);
|
||||
printf(" [] release rate = %d\n", opr->release);
|
||||
printf(" [] tremolo = %s\n", opr->tremolo ? "ON" : "OFF");
|
||||
printf(" [] vibrato = %s\n", opr->vibrato ? "ON" : "OFF");
|
||||
printf(" [] envelope sustain = %s\n", opr->sustaining ? "ON" : "OFF");
|
||||
printf(" [] key rate scale = %s\n", opr->ksr ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_bank_header(uint8_t name) {
|
||||
printf(name ? "| |GLOBAL " : "| |GLOBAL ");
|
||||
for(int ch = 0; ch < 2; ch++) {
|
||||
printf("|VOICE %d |MODULATOR %d |CARRIER %d ", ch + 1, ch + 1, ch + 1);
|
||||
}
|
||||
printf("|\n");
|
||||
printf(name ? "|ID#|MIDI|NAME |TP|FN|PCN" : "|ID#|MIDI|TP|FN|PCN");
|
||||
for(int ch = 0; ch < 2; ch++) {
|
||||
printf("|DTNE|OFFS|FB|AL");
|
||||
for(int op = 0; op < 2; op++) {
|
||||
printf("|ML|LV|WV|KL|AR|DR|SL|RR|TR|VB|ST|KS");
|
||||
}
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
void dump_bank_line(bank_instr *instr, uint8_t prog, uint8_t name) {
|
||||
bank_instr *ins = &instr[prog];
|
||||
if(ins->op == b_op0) {
|
||||
return;
|
||||
}
|
||||
printf(name ? "|%03d|%c%03d|%-32s|%s|%s|%03d" : "|%03d|%c%03d|%s%s|%s|%03d", prog, (prog < 128) ? 'P' : 'D', (prog < 128) ? prog : (prog - 128), name ? ins->name : "", (ins->op == b_op22) ? "P4" : ((ins->op == b_op4) ? "4O" : "2O"), ins->fixed ? "+F" : "--", ins->percnum);
|
||||
for(int ch = 0; ch < ((ins->op == b_op2) ? 1 : 2); ch++) {
|
||||
bank_pair *chn = &ins->channels[ch];
|
||||
printf("|%+04d|%+04d|%02d|%s", chn->detune, chn->offset, chn->feedback, chn->am ? "AM" : "FM");
|
||||
for(int op = 0; op < 2; op++) {
|
||||
bank_operator *opr = &chn->ops[op];
|
||||
printf("|%02d|%02d|%02d|%02d|%02d|%02d|%02d|%02d|%s|%s|%s|%s", opr->mult, 63-opr->level, opr->waveform, opr->ksl, opr->attack, opr->decay, 15-opr->sustain, opr->release,
|
||||
opr->tremolo ? "+T" : "--", opr->vibrato ? "+V" : "--", opr->sustaining ? "+S" : "--", opr->ksr ? "+K" : "--");
|
||||
}
|
||||
}
|
||||
if(ins->op == b_op2) {
|
||||
printf("|....|....|..|..");
|
||||
for(int op = 0; op < 2; op++) {
|
||||
printf("|..|..|..|..|..|..|..|..|..|..|..|..");
|
||||
}
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *dmxname = NULL;
|
||||
char *drumname = NULL;
|
||||
int32_t usedrum = 0;
|
||||
int32_t prog = 256;
|
||||
int32_t table = 0;
|
||||
int32_t name = 0;
|
||||
|
||||
argp_t *argspec = arg_alloc(6);
|
||||
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
|
||||
arg_add_nint(argspec, STR_ARGS_BANKN, false, 0, 255, &prog);
|
||||
arg_add_bool(argspec, 'l', STR_ARGS_NTABLE, &table);
|
||||
arg_add_bool(argspec, 'n', STR_ARGS_NONAME, &name);
|
||||
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
|
||||
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
|
||||
if(arg_parse(argspec, argc, argv) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
table ^= 1;
|
||||
name ^= 1;
|
||||
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
|
||||
if(instr == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if(prog == 256) {
|
||||
if(table) {
|
||||
dump_bank_header(name);
|
||||
}
|
||||
for(int i = 0; i < 256; i++) {
|
||||
if(table) {
|
||||
dump_bank_line(instr, i, name);
|
||||
}
|
||||
else {
|
||||
dump_bank(instr, i, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(instr[prog].op == b_op0) {
|
||||
fprintf(stderr, STR_BNK_UNDEF"\n", prog);
|
||||
free(instr);
|
||||
return 1;
|
||||
}
|
||||
if(table) {
|
||||
dump_bank_header(name);
|
||||
dump_bank_line(instr, prog, name);
|
||||
}
|
||||
else {
|
||||
dump_bank(instr, prog, name);
|
||||
}
|
||||
}
|
||||
free(instr);
|
||||
return 0;
|
||||
}
|
BIN
banks/2x2.op3
Normal file
BIN
banks/2x2.op3
Normal file
Binary file not shown.
BIN
banks/cyberpuck.tmb
Normal file
BIN
banks/cyberpuck.tmb
Normal file
Binary file not shown.
BIN
banks/d3dtimbr.tmb
Normal file
BIN
banks/d3dtimbr.tmb
Normal file
Binary file not shown.
BIN
banks/d3dtimbrmod.tmb
Normal file
BIN
banks/d3dtimbrmod.tmb
Normal file
Binary file not shown.
BIN
banks/default.tmb
Normal file
BIN
banks/default.tmb
Normal file
Binary file not shown.
BIN
banks/dmx_dmx.op2
Normal file
BIN
banks/dmx_dmx.op2
Normal file
Binary file not shown.
BIN
banks/dmx_doom1.op2
Normal file
BIN
banks/dmx_doom1.op2
Normal file
Binary file not shown.
BIN
banks/dmx_doom2.op2
Normal file
BIN
banks/dmx_doom2.op2
Normal file
Binary file not shown.
BIN
banks/dmx_raptor.op2
Normal file
BIN
banks/dmx_raptor.op2
Normal file
Binary file not shown.
BIN
banks/dmx_strife.op2
Normal file
BIN
banks/dmx_strife.op2
Normal file
Binary file not shown.
BIN
banks/drumopl.ibk
Normal file
BIN
banks/drumopl.ibk
Normal file
Binary file not shown.
BIN
banks/drumopl.tmb
Normal file
BIN
banks/drumopl.tmb
Normal file
Binary file not shown.
BIN
banks/earthsieg.ad
Normal file
BIN
banks/earthsieg.ad
Normal file
Binary file not shown.
BIN
banks/fat2.op3
Normal file
BIN
banks/fat2.op3
Normal file
Binary file not shown.
BIN
banks/fat4.op3
Normal file
BIN
banks/fat4.op3
Normal file
Binary file not shown.
BIN
banks/file49.opl
Normal file
BIN
banks/file49.opl
Normal file
Binary file not shown.
BIN
banks/file53.opl
Normal file
BIN
banks/file53.opl
Normal file
Binary file not shown.
BIN
banks/gmoconel.tmb
Normal file
BIN
banks/gmoconel.tmb
Normal file
Binary file not shown.
BIN
banks/gmopl.ibk
Normal file
BIN
banks/gmopl.ibk
Normal file
Binary file not shown.
BIN
banks/gmoplmod.tmb
Normal file
BIN
banks/gmoplmod.tmb
Normal file
Binary file not shown.
BIN
banks/hmi-144.skb
Normal file
BIN
banks/hmi-144.skb
Normal file
Binary file not shown.
BIN
banks/insmaker_standard.bnk
Normal file
BIN
banks/insmaker_standard.bnk
Normal file
Binary file not shown.
BIN
banks/jv_2op.op3
Normal file
BIN
banks/jv_2op.op3
Normal file
Binary file not shown.
BIN
banks/mt32.ibk
Normal file
BIN
banks/mt32.ibk
Normal file
Binary file not shown.
BIN
banks/nemesis.opl
Normal file
BIN
banks/nemesis.opl
Normal file
Binary file not shown.
BIN
banks/std-o3.skb
Normal file
BIN
banks/std-o3.skb
Normal file
Binary file not shown.
BIN
banks/std-sb.skb
Normal file
BIN
banks/std-sb.skb
Normal file
Binary file not shown.
BIN
banks/swtimbr.tmb
Normal file
BIN
banks/swtimbr.tmb
Normal file
Binary file not shown.
BIN
banks/tim.tim
Normal file
BIN
banks/tim.tim
Normal file
Binary file not shown.
BIN
banks/wallace.op3
Normal file
BIN
banks/wallace.op3
Normal file
Binary file not shown.
BIN
banks/wallence.tmb
Normal file
BIN
banks/wallence.tmb
Normal file
Binary file not shown.
BIN
banks/warcraft.ad
Normal file
BIN
banks/warcraft.ad
Normal file
Binary file not shown.
BIN
banks/wolfinstein.op2
Normal file
BIN
banks/wolfinstein.op2
Normal file
Binary file not shown.
166
banktest.c
Normal file
166
banktest.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
void mid_log(char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
char msg[1024];
|
||||
msg[0] = 0;
|
||||
vsprintf(msg, format, ap);
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#include "strings.h"
|
||||
#include "opl3.h"
|
||||
#include "dmx.h"
|
||||
#include "bank.h"
|
||||
#include "audio.h"
|
||||
#define ARGPARSE_DE
|
||||
#include "../argparse.h"
|
||||
|
||||
bool tst_note(aud_handle *dev, bank_instr *instr, opl3_chip *chip, bank_handle *bank, uint32_t samplerate, uint8_t prog, uint8_t note, uint32_t len, uint32_t pause) {
|
||||
if(instr[prog].op == b_op0) {
|
||||
return false;
|
||||
}
|
||||
uint8_t drum = prog >= 128;
|
||||
if(drum) {
|
||||
note = prog - 128;
|
||||
}
|
||||
int16_t buf[2];
|
||||
if(drum) {
|
||||
mid_log(STR_TST_DRUM, note, instr[128+note].name);
|
||||
bank_noteon(bank, chip, 9, note, 127);
|
||||
}
|
||||
else {
|
||||
mid_log(STR_TST_INSTR, note, prog, instr[prog].name);
|
||||
bank_progchange(bank, chip, 0, prog);
|
||||
bank_noteon(bank, chip, 0, note, 127);
|
||||
}
|
||||
for(int i = 0; i < ((samplerate*len)/1000); i++) {
|
||||
OPL3_GenerateResampled(chip, buf);
|
||||
aud_push(dev, buf);
|
||||
}
|
||||
bank_noteoff(bank, chip, drum ? 9 : 0, note, 0);
|
||||
for(int i = 0; i < ((samplerate*pause)/1000); i++) {
|
||||
OPL3_GenerateResampled(chip, buf);
|
||||
aud_push(dev, buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tst_play(uint32_t samplerate, const char *dmxname, const char *drumname, uint8_t usedrum, const char *device, uint32_t fsize, uint32_t bufsize, uint8_t widefmt, uint8_t prog, uint8_t note, uint16_t offset, uint32_t len, uint32_t pause) {
|
||||
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
|
||||
if(instr == NULL) {
|
||||
return false;
|
||||
}
|
||||
if((offset == 256) && (instr[prog].op == b_op0)) {
|
||||
fprintf(stderr, STR_BNK_UNDEF"\n", prog);
|
||||
free(instr);
|
||||
return false;
|
||||
}
|
||||
opl3_chip *chip = OPL3_Alloc(samplerate, 2);
|
||||
bank_handle *bank = bank_alloc(chip, instr, 0, 0, 0);
|
||||
aud_handle dev;
|
||||
if(aud_open_dev(&dev, device, samplerate, 2, fsize, bufsize, widefmt) ^ 1) {
|
||||
free(instr);
|
||||
free(chip);
|
||||
free(bank);
|
||||
return false;
|
||||
}
|
||||
samplerate = dev.samplerate;
|
||||
OPL3_Rate(chip, samplerate);
|
||||
bool played = false;
|
||||
if(offset != 256) {
|
||||
for(int z = offset; z < 256; z++) {
|
||||
played |= tst_note(&dev, instr, chip, bank, samplerate, z, note, len, pause);
|
||||
}
|
||||
}
|
||||
else {
|
||||
played = tst_note(&dev, instr, chip, bank, samplerate, prog, note, len, pause);
|
||||
}
|
||||
if(played ^ 1) {
|
||||
fprintf(stderr, STR_BNK_NONE"\n");
|
||||
}
|
||||
aud_flush(&dev);
|
||||
aud_close(&dev);
|
||||
free(instr);
|
||||
free(chip);
|
||||
free(bank);
|
||||
return played;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int32_t samplerate = 48000;
|
||||
char *device = "default";
|
||||
int32_t bufsize = 0;
|
||||
int32_t fsize = 32;
|
||||
int32_t widefmt = 0;
|
||||
char *dmxname = NULL;
|
||||
char *drumname = NULL;
|
||||
int32_t usedrum = 0;
|
||||
int32_t prog = 256;
|
||||
int32_t dprog = 256;
|
||||
int32_t mprog = 256;
|
||||
int32_t note = 36;
|
||||
int32_t len = 750;
|
||||
int32_t pause = 250;
|
||||
int32_t offset = 256;
|
||||
|
||||
argp_t *argspec = arg_alloc(15);
|
||||
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
|
||||
arg_add_nint(argspec, STR_ARGS_OFFSET, false, 0, 255, &offset);
|
||||
arg_add_int(argspec, 'r', STR_ARGS_SRATE, 8000, 384000, &samplerate);
|
||||
arg_add_str(argspec, 'd', STR_ARGS_DEVICE, &device);
|
||||
arg_add_int(argspec, 'b', STR_ARGS_BUFFER, 0, 1048576, &bufsize);
|
||||
arg_add_int(argspec, 'f', STR_ARGS_FRAMES, 2, 8192, &fsize);
|
||||
arg_add_bool(argspec, 'w', STR_ARGS_WIDEFT, &widefmt);
|
||||
arg_add_int(argspec, 'n', STR_ARGS_NOTE, 0, 127, ¬e);
|
||||
arg_add_int(argspec, 'l', STR_ARGS_LENGTH, 10, 60000, &len);
|
||||
arg_add_int(argspec, 'p', STR_ARGS_PAUSE, 10, 60000, &pause);
|
||||
arg_add_int(argspec, 'i', STR_ARGS_INSTR, 0, 255, &prog);
|
||||
arg_add_int(argspec, 'm', STR_ARGS_PERC, 0, 127, &mprog);
|
||||
arg_add_int(argspec, 'g', STR_ARGS_DPERC, 0, 46, &dprog);
|
||||
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
|
||||
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
|
||||
if(arg_parse(argspec, argc, argv) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(dprog != 256) {
|
||||
if((prog != 256) || (mprog != 256)) {
|
||||
fprintf(stderr, STR_TST_NDDRUM"\n");
|
||||
return 1;
|
||||
}
|
||||
prog = dprog + 128 + 35;
|
||||
}
|
||||
else if(mprog != 256) {
|
||||
if((prog != 256) || (dprog != 256)) {
|
||||
fprintf(stderr, STR_TST_NMDRUM"\n");
|
||||
return 1;
|
||||
}
|
||||
prog = mprog + 128;
|
||||
}
|
||||
|
||||
if((offset != 256) && (prog != 256)) {
|
||||
fprintf(stderr, STR_TST_NOFFS"\n");
|
||||
return 1;
|
||||
}
|
||||
else if((offset == 256) && (prog == 256)) {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if(tst_play(samplerate, dmxname, drumname, usedrum, device, fsize, bufsize, widefmt, prog, note, offset, len, pause) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
925
dmx.h
Normal file
925
dmx.h
Normal file
|
@ -0,0 +1,925 @@
|
|||
|
||||
enum bank_op {
|
||||
b_op2,
|
||||
b_op4,
|
||||
b_op22,
|
||||
b_op0
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t tremolo;
|
||||
uint8_t vibrato;
|
||||
uint8_t sustaining;
|
||||
uint8_t ksr;
|
||||
uint8_t mult;
|
||||
uint8_t ksl;
|
||||
uint8_t level;
|
||||
uint8_t attack;
|
||||
uint8_t decay;
|
||||
uint8_t sustain;
|
||||
uint8_t release;
|
||||
uint8_t waveform;
|
||||
} bank_operator;
|
||||
|
||||
typedef struct {
|
||||
bank_operator ops[2];
|
||||
int16_t detune;
|
||||
int16_t offset;
|
||||
uint8_t feedback;
|
||||
uint8_t am;
|
||||
} bank_pair;
|
||||
|
||||
typedef struct {
|
||||
bank_pair channels[2];
|
||||
uint8_t op;
|
||||
uint8_t fixed;
|
||||
uint8_t percnum;
|
||||
char name[32];
|
||||
} bank_instr;
|
||||
|
||||
uint32_t bnk_read_mhbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t minsize, uint32_t maxsize, const char *hdr, uint32_t hdrsize) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
fprintf(stderr, STR_BNK_OPNERR"\n", filename, strerror(err), err);
|
||||
return 0;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_END)) {
|
||||
err = errno;
|
||||
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
long size = ftell(fd);
|
||||
if(size < 0L) {
|
||||
err = errno;
|
||||
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_SET)) {
|
||||
err = errno;
|
||||
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(size < (hdrsize+minsize)) {
|
||||
fprintf(stderr, STR_BNK_ESHORT"\n", filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(hdrsize > 0) {
|
||||
uint8_t header[hdrsize];
|
||||
if(fread(header, 1, hdrsize, fd) != hdrsize) {
|
||||
err = feof(fd);
|
||||
fprintf(stderr, err == 0 ? STR_BNK_EIO"\n" : STR_BNK_EEOF"\n", filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(memcmp(header, hdr, hdrsize) != 0) {
|
||||
fprintf(stderr, STR_BNK_EMFHDR"\n", filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
size -= hdrsize;
|
||||
}
|
||||
size = ((maxsize > 0) && (size > maxsize)) ? maxsize : size;
|
||||
*data = malloc(size);
|
||||
if(fread(*data, 1, size, fd) != size) {
|
||||
err = feof(fd);
|
||||
fprintf(stderr, err == 0 ? STR_BNK_EIO"\n" : STR_BNK_EEOF"\n", filename);
|
||||
fclose(fd);
|
||||
free(*data);
|
||||
return 0;
|
||||
}
|
||||
fclose(fd);
|
||||
*instr = malloc(sizeof(bank_instr)*256);
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t bnk_read_mbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t minsize, uint32_t maxsize) {
|
||||
return bnk_read_mhbank(data, instr, filename, minsize, maxsize, NULL, 0);
|
||||
}
|
||||
|
||||
uint32_t bnk_read_hbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t size, const char *hdr, uint32_t hdrsize) {
|
||||
return bnk_read_mhbank(data, instr, filename, size, size, hdr, hdrsize);
|
||||
}
|
||||
|
||||
uint32_t bnk_read_sbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t size) {
|
||||
return bnk_read_mhbank(data, instr, filename, size, size, NULL, 0);
|
||||
}
|
||||
|
||||
uint16_t dmx_read_uint16(uint8_t *data) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
value |= ((uint16_t)data[1-h]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t dmx_read_uint16be(uint8_t *data) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
value |= ((uint16_t)data[h]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void dmx_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
instr->fixed = ((data[0] & 0x01) > 0) | drum;
|
||||
instr->op = ((data[0] & 0x04) > 0) ? b_op22 : b_op2;
|
||||
instr->channels[0].detune = 0;
|
||||
instr->channels[1].detune = ((int16_t)data[2]) - 128;
|
||||
instr->percnum = data[3] & 0x7f;
|
||||
data += 4;
|
||||
for(int ch = 0; ch < 2; ch++) {
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[op].tremolo = (data[op*7+0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[op].vibrato = (data[op*7+0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[op].sustaining = (data[op*7+0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[op].ksr = (data[op*7+0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[op].mult = data[op*7+0] & 0x0f;
|
||||
instr->channels[ch].ops[op].attack = (data[op*7+1] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].decay = data[op*7+1] & 0x0f;
|
||||
instr->channels[ch].ops[op].sustain = (data[op*7+2] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].release = data[op*7+2] & 0x0f;
|
||||
instr->channels[ch].ops[op].waveform = data[op*7+3] & 0x07;
|
||||
instr->channels[ch].ops[op].ksl = (data[op*7+4] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[op].level = data[op*7+5] & 0x3f;
|
||||
}
|
||||
instr->channels[ch].feedback = (data[6] >> 1) & 0x07;
|
||||
instr->channels[ch].am = data[6] & 0x01;
|
||||
instr->channels[ch].offset = ((int16_t)dmx_read_uint16(data+14)) + 12;
|
||||
data += 16;
|
||||
}
|
||||
}
|
||||
|
||||
bank_instr *dmx_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_hbank(&data, &instr, filename, 175*68, "#OPL_II#", 8);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
instr[i].op = b_op0;
|
||||
instr[i].name[0] = 0;
|
||||
}
|
||||
for(int i = 0; i < 175; i++) {
|
||||
dmx_read_instr(&instr[(i < 128) ? i : (i + 35)], data, i >= 128);
|
||||
data += 36;
|
||||
}
|
||||
for(int i = 0; i < 175; i++) {
|
||||
bank_instr *ins = &instr[(i < 128) ? i : (i + 35)];
|
||||
memcpy(ins->name, data, 32);
|
||||
ins->name[31] = 0;
|
||||
data += 32;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void tmb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
instr->name[0] = 0;
|
||||
instr->fixed = drum;
|
||||
instr->op = b_op2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
|
||||
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
|
||||
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
|
||||
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
|
||||
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
|
||||
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
|
||||
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
|
||||
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
|
||||
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
|
||||
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
|
||||
}
|
||||
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
|
||||
instr->channels[0].am = data[10] & 0x01;
|
||||
instr->percnum = drum ? (data[11] & 0x7f) : 0;
|
||||
instr->channels[0].offset = drum ? 0 : ((int8_t)data[11]);
|
||||
instr->channels[0].detune = 0;
|
||||
}
|
||||
|
||||
bank_instr *tmb_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_sbank(&data, &instr, filename, 256*13);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
tmb_read_instr(&instr[i], data, i >= 128);
|
||||
data += 13;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void ibk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
instr->fixed = drum;
|
||||
instr->op = b_op2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
|
||||
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
|
||||
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
|
||||
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
|
||||
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
|
||||
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
|
||||
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
|
||||
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
|
||||
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
|
||||
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
|
||||
}
|
||||
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
|
||||
instr->channels[0].am = data[10] & 0x01;
|
||||
instr->percnum = drum ? (data[13] & 0x7f) : 0;
|
||||
instr->channels[0].offset = drum ? 0 : ((int8_t)data[13]);
|
||||
instr->channels[0].detune = 0;
|
||||
}
|
||||
|
||||
bank_instr *ibk_read_bank(const char *filename, uint8_t drum) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_hbank(&data, &instr, filename, 128*25, "IBK\x1a", 4);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
instr[i + (drum ? 0 : 128)].op = b_op0;
|
||||
instr[i + (drum ? 0 : 128)].name[0] = 0;
|
||||
}
|
||||
for(int i = 0; i < 128; i++) {
|
||||
ibk_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
|
||||
data += 16;
|
||||
}
|
||||
for(int i = 0; i < 128; i++) {
|
||||
bank_instr *ins = &instr[i + (drum ? 128 : 0)];
|
||||
memcpy(ins->name, data, 8);
|
||||
ins->name[8] = 0;
|
||||
data += 9;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void sb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
for(int h = 0; h < 28; h++) {
|
||||
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
|
||||
}
|
||||
instr->name[28] = 0;
|
||||
instr->percnum = data[35] & 0x7f;
|
||||
instr->fixed = drum;
|
||||
instr->op = b_op2;
|
||||
data += 36;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
|
||||
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
|
||||
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
|
||||
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
|
||||
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
|
||||
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
|
||||
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
|
||||
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
|
||||
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
|
||||
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
|
||||
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
|
||||
}
|
||||
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
|
||||
instr->channels[0].am = data[10] & 0x01;
|
||||
instr->channels[0].offset = 0;
|
||||
instr->channels[0].detune = 0;
|
||||
}
|
||||
|
||||
bank_instr *sb_read_bank(const char *filename, uint8_t drum) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*52);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
instr[i + (drum ? 0 : 128)].op = b_op0;
|
||||
instr[i + (drum ? 0 : 128)].name[0] = 0;
|
||||
}
|
||||
for(int i = 0; i < 128; i++) {
|
||||
sb_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
|
||||
data += 52;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void sb3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
for(int h = 0; h < 28; h++) {
|
||||
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
|
||||
}
|
||||
instr->name[28] = 0;
|
||||
instr->percnum = data[35] & 0x7f;
|
||||
instr->fixed = drum;
|
||||
instr->op = (data[0] == '4') ? b_op4 : b_op2;
|
||||
data += 36;
|
||||
for(int ch = 0; ch < 2; ch++) {
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[op].tremolo = (data[op+0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[op].vibrato = (data[op+0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[op].sustaining = (data[op+0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[op].ksr = (data[op+0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[op].mult = data[op+0] & 0x0f;
|
||||
instr->channels[ch].ops[op].attack = (data[op+4] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].decay = data[op+4] & 0x0f;
|
||||
instr->channels[ch].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].release = data[op+6] & 0x0f;
|
||||
instr->channels[ch].ops[op].waveform = data[op+8] & 0x07;
|
||||
instr->channels[ch].ops[op].ksl = (data[op+2] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[op].level = data[op+2] & 0x3f;
|
||||
}
|
||||
instr->channels[ch].feedback = (data[10] >> 1) & 0x07;
|
||||
instr->channels[ch].am = data[10] & 0x01;
|
||||
instr->channels[ch].offset = 0;
|
||||
instr->channels[ch].detune = 0;
|
||||
data += 11;
|
||||
}
|
||||
}
|
||||
|
||||
bank_instr *sb3_read_bank(const char *filename, uint8_t drum) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*60);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
instr[i + (drum ? 0 : 128)].op = b_op0;
|
||||
instr[i + (drum ? 0 : 128)].name[0] = 0;
|
||||
}
|
||||
for(int i = 0; i < 128; i++) {
|
||||
sb3_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
|
||||
data += 60;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void op3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
instr->percnum = data[1] & 0x7f;
|
||||
instr->fixed = drum;
|
||||
instr->op = ((data[0] & 0x01) > 0) ? b_op4 : b_op2;
|
||||
data += 2;
|
||||
for(int ch = 0; ch < 2; ch++) {
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f;
|
||||
instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f;
|
||||
instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f;
|
||||
instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07;
|
||||
instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f;
|
||||
}
|
||||
instr->channels[ch].feedback = (data[5] >> 1) & 0x07;
|
||||
instr->channels[ch].am = data[5] & 0x01;
|
||||
instr->channels[ch].offset = 0;
|
||||
instr->channels[ch].detune = 0;
|
||||
data += 11;
|
||||
}
|
||||
}
|
||||
|
||||
bank_instr *op3_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 16, 256*24+16, "Junglevision Patch File\x1a", 24);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
instr[i].op = b_op0;
|
||||
instr[i].name[0] = 0;
|
||||
}
|
||||
data += 8;
|
||||
uint16_t nmelo = dmx_read_uint16(&data[0]);
|
||||
uint16_t ndrum = dmx_read_uint16(&data[2]);
|
||||
uint16_t omelo = dmx_read_uint16(&data[4]);
|
||||
uint16_t odrum = dmx_read_uint16(&data[6]);
|
||||
data += 8;
|
||||
if((((nmelo+ndrum)*24+16) > size) || ((omelo+nmelo) > 128) || ((odrum+ndrum) > 128)) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
for(int i = 0; i < nmelo; i++) {
|
||||
op3_read_instr(&instr[i + omelo], data, 0);
|
||||
data += 24;
|
||||
}
|
||||
for(int i = 0; i < ndrum; i++) {
|
||||
op3_read_instr(&instr[i + odrum + 128], data, 1);
|
||||
data += 24;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void ad_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum, uint16_t isize) {
|
||||
if(isize < 12) {
|
||||
return;
|
||||
}
|
||||
instr->percnum = data[0] & 0x7f;
|
||||
instr->fixed = drum;
|
||||
instr->op = (isize >= 23) ? b_op4 : b_op2;
|
||||
data += 1;
|
||||
instr->channels[0].feedback = instr->channels[1].feedback = (data[5] >> 1) & 0x07;
|
||||
instr->channels[0].am = data[5] & 0x01;
|
||||
instr->channels[1].am = (data[5] & 0x80) > 0;
|
||||
for(int ch = 0; ch < ((isize >= 23) ? 2 : 1); ch++) {
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f;
|
||||
instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f;
|
||||
instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f;
|
||||
instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07;
|
||||
instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f;
|
||||
}
|
||||
instr->channels[ch].offset = 0;
|
||||
instr->channels[ch].detune = 0;
|
||||
data += 11;
|
||||
}
|
||||
}
|
||||
|
||||
bank_instr *ad_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mbank(&data, &instr, filename, 2, 256*20+2);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
instr[i].op = b_op0;
|
||||
instr[i].name[0] = 0;
|
||||
}
|
||||
for(int i = 0;; i++, data += 6) {
|
||||
if((i*6) >= size) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
uint8_t prog = data[0];
|
||||
if(prog == 0xff) {
|
||||
break;
|
||||
}
|
||||
if(prog >= 128) {
|
||||
continue;
|
||||
}
|
||||
uint8_t bank = data[1];
|
||||
if((bank != 0) && (bank != 127)) {
|
||||
continue;
|
||||
}
|
||||
uint16_t offs = dmx_read_uint16(data+2);
|
||||
uint16_t isize;
|
||||
if(((offs+2) > size) || ((offs+(isize = dmx_read_uint16(odata+offs))) > size) || (isize < 2)) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
ad_read_instr(&instr[prog + ((bank == 127) ? 128 : 0)], odata+(offs+2), bank == 127, isize-2);
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void bk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
instr->fixed = drum;
|
||||
instr->op = b_op2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[0].ops[op].tremolo = data[op*13+9] & 0x01;
|
||||
instr->channels[0].ops[op].vibrato = data[op*13+10] & 0x01;
|
||||
instr->channels[0].ops[op].sustaining = data[op*13+5] & 0x01;
|
||||
instr->channels[0].ops[op].ksr = data[op*13+11] & 0x01;
|
||||
instr->channels[0].ops[op].mult = data[op*13+1] & 0x0f;
|
||||
instr->channels[0].ops[op].attack = data[op*13+3] & 0x0f;
|
||||
instr->channels[0].ops[op].decay = data[op*13+6] & 0x0f;
|
||||
instr->channels[0].ops[op].sustain = data[op*13+4] & 0x0f;
|
||||
instr->channels[0].ops[op].release = data[op*13+7] & 0x0f;
|
||||
instr->channels[0].ops[op].waveform = data[26+op] & 0x07;
|
||||
instr->channels[0].ops[op].ksl = data[op*13+0] & 0x03;
|
||||
instr->channels[0].ops[op].level = data[op*13+8] & 0x3f;
|
||||
}
|
||||
instr->channels[0].feedback = data[2] & 0x07;
|
||||
instr->channels[0].am = (data[25] & 0x01) ^ 1;
|
||||
instr->channels[0].offset = 0;
|
||||
instr->channels[0].detune = 0;
|
||||
}
|
||||
|
||||
bank_instr *bk_read_bank(const char *filename, uint8_t drum) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mbank(&data, &instr, filename, 28, 256*42+28);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
if(memcmp(data+2, "ADLIB-", 6) != 0) {
|
||||
fprintf(stderr, STR_BNK_EMFHDR"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
data += 8;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
instr[i].op = b_op0;
|
||||
instr[i].name[0] = 0;
|
||||
}
|
||||
// uint16_t nnorm = data[0];
|
||||
uint32_t ninst = dmx_read_uint16(&data[2]);
|
||||
data += 20;
|
||||
if((ninst*42+28) > size) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
data += ninst*12;
|
||||
int pos = 0;
|
||||
for(int i = 0; i < ninst; i++, data += 30) {
|
||||
if(data[0] != 0) {
|
||||
continue;
|
||||
}
|
||||
bk_read_instr(&instr[pos + (drum ? 128 : 0)], data+2, drum);
|
||||
uint8_t *ndata = odata+(i*12+28);
|
||||
instr[pos + (drum ? 128 : 0)].percnum = ndata[2] & 0x7f;
|
||||
memcpy(instr[pos + (drum ? 128 : 0)].name, ndata+3, 8);
|
||||
instr[pos + (drum ? 128 : 0)].name[8] = 0;
|
||||
if(++pos == 128) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void tim_read_instr(bank_instr *instr, uint8_t *data) {
|
||||
instr->percnum = 0;
|
||||
instr->fixed = 0;
|
||||
instr->op = b_op2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[0].ops[op].tremolo = dmx_read_uint16(&data[op*26+18]) & 0x01;
|
||||
instr->channels[0].ops[op].vibrato = dmx_read_uint16(&data[op*26+20]) & 0x01;
|
||||
instr->channels[0].ops[op].sustaining = dmx_read_uint16(&data[op*26+10]) & 0x01;
|
||||
instr->channels[0].ops[op].ksr = dmx_read_uint16(&data[op*26+22]) & 0x01;
|
||||
instr->channels[0].ops[op].mult = dmx_read_uint16(&data[op*26+2]) & 0x0f;
|
||||
instr->channels[0].ops[op].attack = dmx_read_uint16(&data[op*26+6]) & 0x0f;
|
||||
instr->channels[0].ops[op].decay = dmx_read_uint16(&data[op*26+12]) & 0x0f;
|
||||
instr->channels[0].ops[op].sustain = dmx_read_uint16(&data[op*26+8]) & 0x0f;
|
||||
instr->channels[0].ops[op].release = dmx_read_uint16(&data[op*26+14]) & 0x0f;
|
||||
instr->channels[0].ops[op].waveform = dmx_read_uint16(&data[52+op*2]) & 0x07;
|
||||
instr->channels[0].ops[op].ksl = dmx_read_uint16(&data[op*26+0]) & 0x03;
|
||||
instr->channels[0].ops[op].level = dmx_read_uint16(&data[op*26+16]) & 0x3f;
|
||||
}
|
||||
instr->channels[0].feedback = dmx_read_uint16(&data[4]) & 0x07;
|
||||
instr->channels[0].am = (dmx_read_uint16(&data[50]) & 0x01) ^ 1;
|
||||
instr->channels[0].offset = 0;
|
||||
instr->channels[0].detune = 0;
|
||||
}
|
||||
|
||||
bank_instr *tim_read_bank(const char *filename, uint8_t drum) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mbank(&data, &instr, filename, 6, 256*65+6);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
instr[i].op = b_op0;
|
||||
instr[i].name[0] = 0;
|
||||
}
|
||||
uint32_t ninst = dmx_read_uint16(&data[2]);
|
||||
data += 6;
|
||||
if((ninst*65+6) > size) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
data += ninst*9;
|
||||
for(int i = 0; (i < ninst) && (i < 128); i++, data += 56) {
|
||||
tim_read_instr(&instr[i + (drum ? 128 : 0)], data);
|
||||
uint8_t *ndata = odata+(i*9+6);
|
||||
memcpy(instr[i + (drum ? 128 : 0)].name, ndata, 8);
|
||||
instr[i + (drum ? 128 : 0)].name[8] = 0;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
void wopl_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
|
||||
memcpy(instr->name, data, 32);
|
||||
instr->name[31] = 0;
|
||||
data += 32;
|
||||
instr->percnum = data[6] & 0x7f;
|
||||
instr->fixed = drum;
|
||||
instr->op = (data[7] & 0x01) ? (((data[7] & 0x02) > 0) ? b_op22 : b_op4) : b_op2;
|
||||
instr->channels[0].offset = (int16_t)dmx_read_uint16be(&data[0]);
|
||||
instr->channels[0].detune = 0;
|
||||
if(instr->op != b_op2) {
|
||||
instr->channels[1].offset = (int16_t)dmx_read_uint16be(&data[2]);
|
||||
instr->channels[0].detune = (int8_t)data[5];
|
||||
}
|
||||
instr->channels[0].am = data[8] & 0x01;
|
||||
instr->channels[0].feedback = (data[8] >> 1) & 0x07;
|
||||
if(instr->op != b_op2) {
|
||||
instr->channels[1].am = data[9] & 0x01;
|
||||
instr->channels[1].feedback = (data[9] >> 1) & 0x07;
|
||||
}
|
||||
data += 10;
|
||||
for(int ch = 0; ch < ((instr->op != b_op2) ? 2 : 1); ch++) {
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[1-op].tremolo = (data[op*5+0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[1-op].vibrato = (data[op*5+0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[1-op].sustaining = (data[op*5+0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[1-op].ksr = (data[op*5+0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[1-op].mult = data[op*5+0] & 0x0f;
|
||||
instr->channels[ch].ops[1-op].attack = (data[op*5+2] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[1-op].decay = data[op*5+2] & 0x0f;
|
||||
instr->channels[ch].ops[1-op].sustain = (data[op*5+3] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[1-op].release = data[op*5+3] & 0x0f;
|
||||
instr->channels[ch].ops[1-op].waveform = data[op*5+4] & 0x07;
|
||||
instr->channels[ch].ops[1-op].ksl = (data[op*5+1] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[1-op].level = data[op*5+1] & 0x3f;
|
||||
}
|
||||
data += 10;
|
||||
}
|
||||
}
|
||||
|
||||
bank_instr *wopl_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256*62+9, 512*66+77, "WOPL3-BANK", 10);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint8_t *odata = data;
|
||||
int stride = (data[7] != 0) ? 66 : 62;
|
||||
int off = (data[7] != 0) ? 77 : 9 + ((data[4] == 2) ? 102 : 0);
|
||||
int off2 = (data[4] == 2) ? (128*stride) : 0;
|
||||
if((256*stride+off+off2) > size) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(odata);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
data += off;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
wopl_read_instr(&instr[i], data, 0);
|
||||
data += stride;
|
||||
}
|
||||
data += off2;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
wopl_read_instr(&instr[i + 128], data, 1);
|
||||
data += stride;
|
||||
}
|
||||
free(odata);
|
||||
return instr;
|
||||
}
|
||||
|
||||
// SKB!
|
||||
// OO-NNNNN NAME[N] FPPPPPPP AFFFAFFF
|
||||
// DDDDDDDD OOOOOOOO TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR -WWW-WWW
|
||||
|
||||
uint32_t skb_read_instr(bank_instr *instr, uint8_t *data, uint32_t pos, uint32_t size) {
|
||||
if((pos + 1) > size) {
|
||||
return 0;
|
||||
}
|
||||
data += pos;
|
||||
instr->op = (data[0] >> 6) & 0x03;
|
||||
uint8_t nlen = data[0] & 0x1f;
|
||||
if((pos + 1 + nlen) > size) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(instr->name, data+1, nlen);
|
||||
instr->name[nlen] = 0;
|
||||
pos += 1 + nlen;
|
||||
if(instr->op == b_op0) {
|
||||
return pos;
|
||||
}
|
||||
int numch = (instr->op == b_op2) ? 1 : 2;
|
||||
if((pos + 1 + (12 * numch)) > size) {
|
||||
return 0;
|
||||
}
|
||||
data += 1 + nlen;
|
||||
instr->fixed = (data[0] & 0x80) > 0;
|
||||
instr->percnum = data[0] & 0x7f;
|
||||
instr->channels[0].am = (data[1] & 0x08) > 0;
|
||||
instr->channels[0].feedback = data[1] & 0x07;
|
||||
if(numch == 2) {
|
||||
instr->channels[1].am = (data[1] & 0x80) > 0;
|
||||
instr->channels[1].feedback = (data[1] >> 4) & 0x07;
|
||||
}
|
||||
data += 2;
|
||||
for(int ch = 0; ch < numch; ch++) {
|
||||
instr->channels[ch].detune = ((int16_t)data[0]) - 128;
|
||||
instr->channels[ch].offset = ((int16_t)data[1]) - 128;
|
||||
data += 2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
instr->channels[ch].ops[op].tremolo = (data[0] & 0x80) > 0;
|
||||
instr->channels[ch].ops[op].vibrato = (data[0] & 0x40) > 0;
|
||||
instr->channels[ch].ops[op].sustaining = (data[0] & 0x20) > 0;
|
||||
instr->channels[ch].ops[op].ksr = (data[0] & 0x10) > 0;
|
||||
instr->channels[ch].ops[op].mult = data[0] & 0x0f;
|
||||
instr->channels[ch].ops[op].ksl = (data[1] >> 6) & 0x03;
|
||||
instr->channels[ch].ops[op].level = data[1] & 0x3f;
|
||||
instr->channels[ch].ops[op].attack = (data[2] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].decay = data[2] & 0x0f;
|
||||
instr->channels[ch].ops[op].sustain = (data[3] >> 4) & 0x0f;
|
||||
instr->channels[ch].ops[op].release = data[3] & 0x0f;
|
||||
data += 4;
|
||||
}
|
||||
instr->channels[ch].ops[0].waveform = (data[0] >> 4) & 0x07;
|
||||
instr->channels[ch].ops[1].waveform = data[0] & 0x07;
|
||||
data += 1;
|
||||
}
|
||||
return pos + 2 + (11 * numch);
|
||||
}
|
||||
|
||||
bank_instr *skb_read_bank(const char *filename) {
|
||||
uint8_t *data;
|
||||
bank_instr *instr;
|
||||
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256, 256*56, "SKB!", 4);
|
||||
if(size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
uint32_t pos = 0;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
pos = skb_read_instr(&instr[i], data, pos, size);
|
||||
if(pos == 0) {
|
||||
fprintf(stderr, STR_BNK_EEOD"\n", filename);
|
||||
free(data);
|
||||
free(instr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
return instr;
|
||||
}
|
||||
|
||||
bank_instr *bnk_read_bank(const char *filename, uint8_t drum) {
|
||||
int len = 0;
|
||||
int dp = 0;
|
||||
char ch;
|
||||
while(ch = filename[len++]) {
|
||||
if(ch == '.') {
|
||||
dp = len;
|
||||
}
|
||||
}
|
||||
if(dp == 0) {
|
||||
fprintf(stderr, STR_BNK_UNKN"\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
char ext[len -= dp];
|
||||
for(int c = 0; c < len; c++) {
|
||||
ext[c] = filename[dp+c];
|
||||
if((ext[c] >= 'A') && (ext[c] <= 'Z')) {
|
||||
ext[c] = ext[c] - 'A' + 'a';
|
||||
}
|
||||
}
|
||||
if(strcmp(ext, "skb") == 0) {
|
||||
return skb_read_bank(filename);
|
||||
}
|
||||
else if((strcmp(ext, "dmx") == 0) || (strcmp(ext, "op2") == 0) || (strcmp(ext, "lmp") == 0)) {
|
||||
return dmx_read_bank(filename);
|
||||
}
|
||||
else if(strcmp(ext, "tmb") == 0) {
|
||||
return tmb_read_bank(filename);
|
||||
}
|
||||
else if(strcmp(ext, "ibk") == 0) {
|
||||
return ibk_read_bank(filename, drum);
|
||||
}
|
||||
else if(strcmp(ext, "sb") == 0) {
|
||||
return sb_read_bank(filename, drum);
|
||||
}
|
||||
else if(strcmp(ext, "o3") == 0) {
|
||||
return sb3_read_bank(filename, drum);
|
||||
}
|
||||
else if(strcmp(ext, "op3") == 0) {
|
||||
return op3_read_bank(filename);
|
||||
}
|
||||
else if((strcmp(ext, "ad") == 0) || (strcmp(ext, "opl") == 0)) {
|
||||
return ad_read_bank(filename);
|
||||
}
|
||||
else if(strcmp(ext, "bnk") == 0) {
|
||||
return bk_read_bank(filename, drum);
|
||||
}
|
||||
else if((strcmp(ext, "tim") == 0) || (strcmp(ext, "snd") == 0)) {
|
||||
return tim_read_bank(filename, drum);
|
||||
}
|
||||
// else if(strcmp(ext, "wopl") == 0) {
|
||||
// return wopl_read_bank(filename);
|
||||
// }
|
||||
fprintf(stderr, STR_BNK_UNKN"\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bank_instr *bnk_read_banks(const char *filename, const char *drumname, uint8_t usedrum) {
|
||||
bank_instr *ins1 = bnk_read_bank(filename, usedrum);
|
||||
if((ins1 != NULL) && (drumname != NULL)) {
|
||||
bank_instr *ins2 = bnk_read_bank(drumname, usedrum ^ 1);
|
||||
if(ins2 != NULL) {
|
||||
memcpy(ins1+128, ins2+128, sizeof(bank_instr)*128);
|
||||
free(ins2);
|
||||
}
|
||||
else {
|
||||
free(ins1);
|
||||
ins1 = NULL;
|
||||
}
|
||||
}
|
||||
return ins1;
|
||||
}
|
||||
|
||||
uint32_t skb_write_instr(bank_instr *instr, uint8_t *data, uint32_t pos) {
|
||||
data += pos;
|
||||
uint8_t nlen = strlen(instr->name);
|
||||
data[0] = (instr->op << 6) | nlen;
|
||||
memcpy(data+1, instr->name, nlen);
|
||||
pos += 1 + nlen;
|
||||
if(instr->op == b_op0) {
|
||||
return pos;
|
||||
}
|
||||
int numch = (instr->op == b_op2) ? 1 : 2;
|
||||
data += 1 + nlen;
|
||||
data[0] = (instr->fixed << 7) | instr->percnum;
|
||||
data[1] = ((numch == 2) ? ((instr->channels[1].am << 7) | (instr->channels[1].feedback << 4)) : 0) | (instr->channels[0].am << 3) | instr->channels[0].feedback;
|
||||
data += 2;
|
||||
for(int ch = 0; ch < numch; ch++) {
|
||||
data[0] = (uint8_t)((int16_t)(instr->channels[ch].detune + 128));
|
||||
data[1] = (uint8_t)((int16_t)(instr->channels[ch].offset + 128));
|
||||
data += 2;
|
||||
for(int op = 0; op < 2; op++) {
|
||||
data[0] = (instr->channels[ch].ops[op].tremolo << 7) | (instr->channels[ch].ops[op].vibrato << 6) | (instr->channels[ch].ops[op].sustaining << 5) |
|
||||
(instr->channels[ch].ops[op].ksr << 4) | instr->channels[ch].ops[op].mult;
|
||||
data[1] = (instr->channels[ch].ops[op].ksl << 6) | instr->channels[ch].ops[op].level;
|
||||
data[2] = (instr->channels[ch].ops[op].attack << 4) | instr->channels[ch].ops[op].decay;
|
||||
data[3] = (instr->channels[ch].ops[op].sustain << 4) | instr->channels[ch].ops[op].release;
|
||||
data += 4;
|
||||
}
|
||||
data[0] = (instr->channels[ch].ops[0].waveform << 4) | instr->channels[ch].ops[1].waveform;
|
||||
data += 1;
|
||||
}
|
||||
return pos + 2 + (11 * numch);
|
||||
}
|
||||
|
||||
bool skb_write_bank(bank_instr *instr, const char *filename) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "wb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
fprintf(stderr, STR_BNK_OPNERR"\n", filename, strerror(err), err);
|
||||
return false;
|
||||
}
|
||||
if(fwrite("SKB!", 1, 4, fd) != 4) {
|
||||
fprintf(stderr, STR_BNK_EWIO"\n", filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint8_t *data = malloc(256*56);
|
||||
uint32_t size = 0;
|
||||
for(int i = 0; i < 256; i++) {
|
||||
size = skb_write_instr(&instr[i], data, size);
|
||||
}
|
||||
if(fwrite(data, 1, size, fd) != size) {
|
||||
fprintf(stderr, STR_BNK_EWIO"\n", filename);
|
||||
fclose(fd);
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
fclose(fd);
|
||||
free(data);
|
||||
return true;
|
||||
}
|
574
midi.h
Normal file
574
midi.h
Normal file
|
@ -0,0 +1,574 @@
|
|||
enum mid_event {
|
||||
midev_noteoff = 0x80,
|
||||
midev_noteon = 0x90,
|
||||
midev_aftertouch = 0xa0,
|
||||
midev_control = 0xb0,
|
||||
midev_progchg = 0xc0,
|
||||
midev_chnpressure = 0xd0,
|
||||
midev_pitchbend = 0xe0,
|
||||
midev_sysex = 0xf0,
|
||||
midev_songpos = 0xf2,
|
||||
midev_songsel = 0xf3,
|
||||
midev_tunereq = 0xf6,
|
||||
midev_endsysex = 0xf7,
|
||||
midev_clock = 0xf8,
|
||||
midev_start = 0xfa,
|
||||
midev_continue = 0xfb,
|
||||
midev_stop = 0xfc,
|
||||
midev_actsense = 0xfe,
|
||||
midev_meta = 0xff
|
||||
};
|
||||
|
||||
enum mid_meta {
|
||||
midmt_seqnum = 0x00, // nn nn
|
||||
midmt_text = 0x01, // text...
|
||||
midmt_copyright = 0x02, // text...
|
||||
midmt_trackname = 0x03, // text...
|
||||
midmt_instrname = 0x04, // text...
|
||||
midmt_lyric = 0x05, // text...
|
||||
midmt_marker = 0x06, // text...
|
||||
midmt_cuepoint = 0x07, // text...
|
||||
midmt_chnprefix = 0x20, // cc
|
||||
midmt_endtrack = 0x2f, //
|
||||
midmt_tempo = 0x51, // tt tt tt
|
||||
midmt_smpte = 0x54, // hr mn se fr ff
|
||||
midmt_timesig = 0x58, // nn dd cc bb
|
||||
midmt_keysig = 0x59, // sf mi
|
||||
midmt_seqspec = 0x7f // data...
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer;
|
||||
uint32_t size;
|
||||
uint32_t pos;
|
||||
uint8_t status;
|
||||
uint8_t ending;
|
||||
uint32_t wait;
|
||||
uint16_t trknum;
|
||||
} mid_track;
|
||||
|
||||
typedef struct {
|
||||
mid_track *track;
|
||||
uint16_t tracks;
|
||||
uint32_t tpqn;
|
||||
uint32_t uspb;
|
||||
uint32_t ticktime;
|
||||
} mid_handle;
|
||||
|
||||
const char *mid_hdr = "MThd";
|
||||
const char *mid_trk = "MTrk";
|
||||
|
||||
#define MID_DEFTEMPO 500000
|
||||
|
||||
void mid_settempo(mid_handle *mid, uint32_t tempo) {
|
||||
mid->uspb = tempo;
|
||||
mid->ticktime = mid->uspb / mid->tpqn;
|
||||
}
|
||||
|
||||
uint32_t mid_read_uint32(uint8_t *data) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
value |= ((uint32_t)data[h]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t mid_read_uint16(uint8_t *data) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
value |= ((uint16_t)data[h]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t midt_read_uint32(mid_track *trk) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint32_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t midt_read_uint24(mid_track *trk) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 3; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint32_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 2 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t midt_read_uint16(mid_track *trk) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint16_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t midt_read_uint8(mid_track *trk) {
|
||||
uint8_t value = 0;
|
||||
if(trk->pos < trk->size) {
|
||||
value = trk->buffer[trk->pos++];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void midt_read_var(mid_track *trk, uint32_t len, uint8_t *chars) {
|
||||
memset(chars, 0, len);
|
||||
for(int h = 0; h < len; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
chars[h] = trk->buffer[trk->pos++];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mid_read_vlen(mid_track *trk)
|
||||
{
|
||||
uint32_t value;
|
||||
uint8_t bt;
|
||||
|
||||
if(trk->pos >= trk->size) {
|
||||
return 0;
|
||||
}
|
||||
if((value = trk->buffer[trk->pos++]) & 0x80)
|
||||
{
|
||||
value &= 0x7f;
|
||||
do {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value = (value << 7) + ((bt = trk->buffer[trk->pos++]) & 0x7f);
|
||||
}
|
||||
while(bt & 0x80);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool mid_read(mid_handle *mid, const char *filename) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_OPNERR, filename, strerror(err), err);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_END)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
long size = ftell(fd);
|
||||
if(size < 0L) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_SET)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(size < 14) {
|
||||
mid_log(STR_MID_ESHORT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint8_t header[14];
|
||||
if(fread(header, 1, 14, fd) != 14) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(memcmp(header, mid_hdr, 4) != 0) {
|
||||
mid_log(STR_MID_EMFHDR, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(mid_read_uint32(header+4) != 6) {
|
||||
mid_log(STR_MID_EHSIZE, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
bool mtrack;
|
||||
uint16_t type = mid_read_uint16(header+8);
|
||||
if(type == 0) {
|
||||
mtrack = false;
|
||||
}
|
||||
else if((type == 1) || (type == 2)) {
|
||||
mtrack = true;
|
||||
}
|
||||
else {
|
||||
mid_log(STR_MID_ETKFMT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
mid->tracks = mid_read_uint16(header+10);
|
||||
if(mid->tracks == 0) {
|
||||
mid_log(STR_MID_ENOTRK, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
else if((mtrack ^ 1) && (mid->tracks > 1)) {
|
||||
mid_log(STR_MID_ESMULT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
mid->tpqn = mid_read_uint16(header+12);
|
||||
if((mid->tpqn & 0x8000) > 0) {
|
||||
mid_log(STR_MID_ESMPTE, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(mid->tpqn == 0) {
|
||||
mid_log(STR_MID_EDZERO, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint32_t esize = 0;
|
||||
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(memcmp(header, mid_trk, 4) != 0) {
|
||||
mid_log(STR_MID_ETKHDR, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint32_t trks = mid_read_uint32(header+4);
|
||||
if((14 + esize + 8 + trks) > size) {
|
||||
mid_log(STR_MID_EOUTOF, filename, trk+1);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, trks, SEEK_CUR)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
esize += trks + 8;
|
||||
}
|
||||
if(fseek(fd, 14L, SEEK_SET)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
esize -= (mid->tracks * 8);
|
||||
uint8_t *buf = malloc((sizeof(mid_track) * mid->tracks) + esize);
|
||||
mid->track = (mid_track*)buf;
|
||||
buf += (sizeof(mid_track) * mid->tracks);
|
||||
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
|
||||
memset((mid->track)+trk, 0, sizeof(mid_track));
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
free(mid->track);
|
||||
return false;
|
||||
}
|
||||
mid->track[trk].size = mid_read_uint32(header+4);
|
||||
if(fread(buf, 1, mid->track[trk].size, fd) != mid->track[trk].size) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
free(mid->track);
|
||||
return false;
|
||||
}
|
||||
mid->track[trk].buffer = buf;
|
||||
mid->track[trk].pos = 0;
|
||||
mid->track[trk].wait = 0;
|
||||
mid->track[trk].trknum = trk;
|
||||
buf += mid->track[trk].size;
|
||||
}
|
||||
fclose(fd);
|
||||
mid_settempo(mid, MID_DEFTEMPO);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mid_free(mid_handle *mid) {
|
||||
free(mid->track);
|
||||
}
|
||||
|
||||
void mid_process_meta(mid_handle *mid, mid_track *trk) {
|
||||
uint8_t meta = midt_read_uint8(trk);
|
||||
uint32_t size = mid_read_vlen(trk);
|
||||
switch(meta) {
|
||||
case midmt_seqnum:
|
||||
if(size == 0) {
|
||||
mid_dlog(STR_MID_SEQNUMO, trk->trknum, trk->trknum);
|
||||
return;
|
||||
}
|
||||
else if(size != 2) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
|
||||
return;
|
||||
}
|
||||
mid_dlog(STR_MID_SEQNUM, trk->trknum, midt_read_uint16(trk));
|
||||
return;
|
||||
case midmt_text:
|
||||
case midmt_copyright:
|
||||
case midmt_trackname:
|
||||
case midmt_instrname:
|
||||
case midmt_lyric:
|
||||
case midmt_marker:
|
||||
case midmt_cuepoint: {
|
||||
uint8_t dt[size+1];
|
||||
dt[size] = 0;
|
||||
midt_read_var(trk, size, dt);
|
||||
mid_tlog(meta, dt);
|
||||
return;
|
||||
}
|
||||
case midmt_chnprefix:
|
||||
if(size != 1) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 1, size);
|
||||
return;
|
||||
}
|
||||
mid_dlog(STR_MID_CHNPFX, trk->trknum, midt_read_uint8(trk));
|
||||
return;
|
||||
case midmt_endtrack:
|
||||
trk->pos += size;
|
||||
trk->ending = 1;
|
||||
mid_dlog(STR_MID_END, trk->trknum);
|
||||
return;
|
||||
case midmt_tempo:
|
||||
if(size != 3) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 3, size);
|
||||
return;
|
||||
}
|
||||
mid_settempo(mid, midt_read_uint24(trk));
|
||||
mid_dlog(STR_MID_TEMPO, 60000000 / mid->uspb);
|
||||
return;
|
||||
case midmt_smpte:
|
||||
if(size != 5) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 5, size);
|
||||
return;
|
||||
}
|
||||
trk->pos += 5;
|
||||
mid_dlog(STR_MID_SMPTE);
|
||||
return;
|
||||
case midmt_timesig: {
|
||||
if(size != 4) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 4, size);
|
||||
return;
|
||||
}
|
||||
uint8_t n = midt_read_uint8(trk);
|
||||
uint32_t d = 1 << ((uint32_t)midt_read_uint8(trk));
|
||||
uint8_t c = midt_read_uint8(trk);
|
||||
uint8_t b = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_TIMESIG, n, d, c, b);
|
||||
return;
|
||||
}
|
||||
case midmt_keysig: {
|
||||
if(size != 2) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
|
||||
return;
|
||||
}
|
||||
int8_t s = midt_read_uint8(trk);
|
||||
uint8_t m = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_KEYSIG, s, (m == 0) ? "MAJOR" : ((m == 1) ? "MINOR" : "- ? -"));
|
||||
return;
|
||||
}
|
||||
case midmt_seqspec: {
|
||||
trk->pos += size;
|
||||
mid_dlog(STR_MID_SEQDATA, trk->trknum, size);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
trk->pos += size;
|
||||
mid_dlog(STR_MID_UNKMETA, trk->trknum, meta, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MID_NOEVT
|
||||
void mid_process(mid_handle *mid, mid_track *trk, bank_handle *bank, opl3_chip *chip) {
|
||||
#else
|
||||
void mid_process(mid_handle *mid, mid_track *trk) {
|
||||
#endif
|
||||
if(trk->pos >= trk->size) {
|
||||
return;
|
||||
}
|
||||
uint8_t status = trk->buffer[trk->pos++];
|
||||
if((status & 0x80) == 0) {
|
||||
status = trk->status;
|
||||
trk->pos -= 1;
|
||||
}
|
||||
else {
|
||||
trk->status = status;
|
||||
}
|
||||
if((status & 0xf0) != 0xf0) {
|
||||
uint8_t channel = status & 0x0f;
|
||||
status &= 0xf0;
|
||||
switch(status) {
|
||||
case midev_noteoff: {
|
||||
uint8_t o_key = midt_read_uint8(trk);
|
||||
uint8_t o_velocity = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_noteoff(bank, chip, channel, o_key, o_velocity);
|
||||
#endif
|
||||
mid_dlog(STR_MID_NOTEOFF, channel+1, o_key, o_velocity);
|
||||
break;
|
||||
}
|
||||
case midev_noteon: {
|
||||
uint8_t key = midt_read_uint8(trk);
|
||||
uint8_t velocity = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
if(velocity == 0) {
|
||||
bank_noteoff(bank, chip, channel, key, velocity);
|
||||
}
|
||||
else {
|
||||
bank_noteon(bank, chip, channel, key, velocity);
|
||||
}
|
||||
#endif
|
||||
mid_dlog(STR_MID_NOTEON, channel+1, key, velocity);
|
||||
break;
|
||||
}
|
||||
case midev_aftertouch: {
|
||||
uint8_t pressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_PRESS, channel+1, pressure);
|
||||
break;
|
||||
}
|
||||
case midev_control: {
|
||||
uint8_t control = midt_read_uint8(trk);
|
||||
uint8_t value = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_control(bank, chip, channel, control, value);
|
||||
#endif
|
||||
mid_dlog(STR_MID_CC, channel+1, control, value);
|
||||
break;
|
||||
}
|
||||
case midev_progchg: {
|
||||
uint8_t program = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_progchange(bank, chip, channel, program);
|
||||
#endif
|
||||
mid_dlog(STR_MID_PROG, channel+1, program);
|
||||
break;
|
||||
}
|
||||
case midev_chnpressure: {
|
||||
uint8_t cpressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_CPRESS, channel+1, cpressure);
|
||||
break;
|
||||
}
|
||||
case midev_pitchbend: {
|
||||
uint16_t pb = ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7);
|
||||
int16_t pitch = ((int16_t)pb) - 0x2000;
|
||||
#ifndef MID_NOEVT
|
||||
bank_pitchbend(bank, chip, channel, pitch);
|
||||
#endif
|
||||
mid_dlog(STR_MID_PITCH, channel+1, pitch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(status) {
|
||||
case midev_sysex: {
|
||||
uint32_t slen = mid_read_vlen(trk);
|
||||
trk->pos += slen;
|
||||
mid_dlog(STR_MID_SYSEX, slen);
|
||||
break;
|
||||
}
|
||||
case midev_songpos:
|
||||
mid_dlog(STR_MID_SONGPOS, ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7));
|
||||
break;
|
||||
case midev_songsel:
|
||||
mid_dlog(STR_MID_SONGSEL, midt_read_uint8(trk));
|
||||
break;
|
||||
case midev_tunereq:
|
||||
mid_dlog(STR_MID_TUNE);
|
||||
break;
|
||||
case midev_endsysex: {
|
||||
uint32_t elen = mid_read_vlen(trk);
|
||||
trk->pos += elen;
|
||||
mid_dlog(STR_MID_ESYSEX, elen);
|
||||
break;
|
||||
}
|
||||
case midev_clock:
|
||||
case midev_start:
|
||||
case midev_continue:
|
||||
case midev_stop:
|
||||
case midev_actsense:
|
||||
mid_dlog(STR_MID_GENSTAT, status);
|
||||
break;
|
||||
case midev_meta:
|
||||
mid_process_meta(mid, trk);
|
||||
break;
|
||||
default:
|
||||
mid_log(STR_MID_UNKSTAT, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MID_NOEVT
|
||||
uint8_t mid_tick(mid_handle *mid, bank_handle *bank, opl3_chip *chip) {
|
||||
#else
|
||||
uint8_t mid_tick(mid_handle *mid) {
|
||||
#endif
|
||||
uint8_t end = 1;
|
||||
for(int trk = 0; trk < mid->tracks; trk++) {
|
||||
mid_track *track = (mid->track)+trk;
|
||||
if(track->ending) {
|
||||
continue;
|
||||
}
|
||||
if(track->wait > 0) {
|
||||
track->wait -= 1;
|
||||
if(track->wait > 0) {
|
||||
end = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while(true) {
|
||||
if(track->pos > 0) {
|
||||
#ifndef MID_NOEVT
|
||||
mid_process(mid, track, bank, chip);
|
||||
#else
|
||||
mid_process(mid, track);
|
||||
#endif
|
||||
if((track->ending ^ 1) && (track->pos >= track->size)) {
|
||||
mid_log(STR_MID_EEND, track->trknum);
|
||||
track->ending = 1;
|
||||
}
|
||||
if(track->ending) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
track->wait = mid_read_vlen(track);
|
||||
if(track->wait > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
end &= track->ending;
|
||||
}
|
||||
return end ^ 1;
|
||||
}
|
55
mididump.c
Normal file
55
mididump.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
uint32_t tick = 0;
|
||||
|
||||
void mid_log(char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
char msg[1024];
|
||||
msg[0] = 0;
|
||||
vsprintf(msg, format, ap);
|
||||
printf("[%08d] %s\n", tick, msg);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void mid_dlog(char *format, ...) {
|
||||
mid_log(format);
|
||||
}
|
||||
|
||||
void mid_tlog(int type, char *text) {
|
||||
mid_log(STR_MID_TEXT, type, text);
|
||||
}
|
||||
|
||||
#define MID_NOEVT
|
||||
|
||||
#include "midi.h"
|
||||
#define ARGPARSE_DE
|
||||
#include "../argparse.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *midname = NULL;
|
||||
|
||||
argp_t *argspec = arg_alloc(1);
|
||||
arg_add_nstr(argspec, STR_ARGS_MIDI, true, &midname);
|
||||
if(arg_parse(argspec, argc, argv) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
mid_handle mid;
|
||||
if(mid_read(&mid, midname) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
while(mid_tick(&mid)) {
|
||||
tick++;
|
||||
}
|
||||
mid_free(&mid);
|
||||
return 0;
|
||||
}
|
966
oplplayer.c
Normal file
966
oplplayer.c
Normal file
|
@ -0,0 +1,966 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <ncurses.h>
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
static const char plr_wheel[] = { '/', '-', '\\', '|'};
|
||||
static const char plr_notes[] = { 'C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B'};
|
||||
static const char plr_hexn[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
static const char plr_waves[] = { 'S', 'H', 'A', 'P', 's', 'a', 'R', 'D'};
|
||||
static const char *plr_descs[] = { STR_IMID_TITLE, STR_IMID_INFO, STR_IMID_COPY, STR_IMID_WARN, STR_IMID_LANG, STR_IMID_VER };
|
||||
static const char plr_phases[] = { 'A', 'D', 'S', 'R'};
|
||||
|
||||
int32_t log_debug = 0;
|
||||
bool karaoke = false;
|
||||
uint32_t log_pos = 0;
|
||||
uint32_t mlog_pos;
|
||||
uint32_t klog_pos;
|
||||
uint32_t klog_offs;
|
||||
uint32_t midinf_offs[6];
|
||||
char *logger[4];
|
||||
char *mlogger[4];
|
||||
char *klogger[5];
|
||||
char *midinfo[6];
|
||||
#define MINFO_DESC 10
|
||||
int16_t vu_sum[2];
|
||||
char *tbuffer;
|
||||
int last_w = 0;
|
||||
int last_h = 0;
|
||||
char bankname[36];
|
||||
|
||||
void plr_setbnk(const char *dmxname, const char *drmname) {
|
||||
int pos = 0;
|
||||
for(int h = 0; h < (drmname ? 2 : 1); h++) {
|
||||
const char *filename = h ? drmname : dmxname;
|
||||
int len = 0;
|
||||
int dp = 0;
|
||||
char ch;
|
||||
while(ch = filename[len++]) {
|
||||
if((ch == '/') || (ch == '\\')) {
|
||||
dp = len;
|
||||
}
|
||||
}
|
||||
len -= dp+1;
|
||||
len = len > (drmname ? 17 : 35) ? (drmname ? 17 : 35) : len;
|
||||
memcpy(bankname+pos, filename+dp, len);
|
||||
bankname[pos += len] = (h || (drmname == NULL)) ? 0 : '+';
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
void mid_log(char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
logger[log_pos][0] = 0;
|
||||
vsnprintf(logger[log_pos++], 116, format, ap);
|
||||
if(log_pos == 4) {
|
||||
log_pos = 0;
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void mid_mlog(char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
mlogger[mlog_pos][0] = 0;
|
||||
vsnprintf(mlogger[mlog_pos++], 82, format, ap);
|
||||
if(mlog_pos == 4) {
|
||||
mlog_pos = 0;
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void mid_clog() {
|
||||
klog_pos = 0;
|
||||
klog_offs = 0;
|
||||
if(karaoke) {
|
||||
for(int z = 0; z < 5; z++) {
|
||||
klogger[z][0] = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int z = 0; z < 5; z++) {
|
||||
memset(klogger[z], '-', 115);
|
||||
klogger[z][115] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mid_cmlog() {
|
||||
karaoke = false;
|
||||
mlog_pos = 0;
|
||||
for(int z = 0; z < 4; z++) {
|
||||
mlogger[z][0] = 0;
|
||||
}
|
||||
for(int z = 0; z < 6; z++) {
|
||||
int len = strlen(plr_descs[z]);
|
||||
memcpy(midinfo[z], plr_descs[z], len);
|
||||
memset((midinfo[z])+len, ' ', MINFO_DESC-2-len);
|
||||
midinfo[z][MINFO_DESC-2] = ':';
|
||||
midinfo[z][MINFO_DESC-1] = ' ';
|
||||
midinfo[z][MINFO_DESC] = 0;
|
||||
midinf_offs[z] = MINFO_DESC;
|
||||
}
|
||||
mid_clog();
|
||||
}
|
||||
|
||||
void vu_reset() {
|
||||
vu_sum[0] = vu_sum[1] = 0;
|
||||
}
|
||||
|
||||
void vu_push(int16_t *buf) {
|
||||
for(int j = 0; j < 2; j++) {
|
||||
vu_sum[j] = ((((int32_t)(buf[j] < 0 ? (-buf[j]) : buf[j])) * 2) + ((int32_t)vu_sum[j])) / 3;
|
||||
}
|
||||
}
|
||||
|
||||
void mid_kar() {
|
||||
if(karaoke) {
|
||||
return;
|
||||
}
|
||||
karaoke = true;
|
||||
mid_clog();
|
||||
}
|
||||
|
||||
void mid_info(int type, char *text) {
|
||||
char *info = midinfo[type];
|
||||
int32_t offs = midinf_offs[type];
|
||||
int len = strlen(text);
|
||||
int max = ((type >= 4) ? 57 : 115);
|
||||
max -= (offs != MINFO_DESC) ? 2 : 0;
|
||||
len = (len > (max-offs)) ? (max-offs) : len;
|
||||
if(len <= 0) {
|
||||
return;
|
||||
}
|
||||
if(offs != MINFO_DESC) {
|
||||
info[offs++] = ';';
|
||||
info[offs++] = ' ';
|
||||
}
|
||||
memcpy(info+offs, text, len);
|
||||
offs += len;
|
||||
info[offs] = 0;
|
||||
midinf_offs[type] = offs;
|
||||
}
|
||||
|
||||
void mid_tlog(int type, char *text) {
|
||||
int slen = strlen(text);
|
||||
for(int h = 0; h < slen; h++) {
|
||||
text[h] = ((text[h] < 32) || (text[h] > 126)) ? ((h == (slen-1)) ? 0 : '?') : text[h];
|
||||
}
|
||||
if((type == 1) && (text[0] == '@')) {
|
||||
// text += 1;
|
||||
switch(text[1]) {
|
||||
case 'T':
|
||||
mid_info(0, text+2);
|
||||
return;
|
||||
case 'I':
|
||||
mid_info(1, text+2);
|
||||
return;
|
||||
case 'K':
|
||||
mid_info(2, text+2);
|
||||
return;
|
||||
case 'W':
|
||||
mid_info(3, text+2);
|
||||
return;
|
||||
case 'L':
|
||||
mid_info(4, text+2);
|
||||
return;
|
||||
case 'V':
|
||||
mid_info(5, text+2);
|
||||
return;
|
||||
}
|
||||
// text += (text[0] == 0) ? 0 : 1;
|
||||
}
|
||||
else if((type == 1) && (text[0] == '%') && (text[1] == '-')) {
|
||||
mid_kar();
|
||||
return;
|
||||
}
|
||||
else if(type == 1 /* || type == 5 */) {
|
||||
switch(text[0]) {
|
||||
case '\\':
|
||||
mid_kar();
|
||||
mid_clog();
|
||||
text += 1;
|
||||
slen -= 1;
|
||||
break;
|
||||
case '/':
|
||||
// mid_kar();
|
||||
if(karaoke) {
|
||||
klog_pos += 1;
|
||||
klog_offs = 0;
|
||||
if(klog_pos == 5) {
|
||||
mid_clog();
|
||||
}
|
||||
text += 1;
|
||||
slen -= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(karaoke) {
|
||||
int len;
|
||||
while(slen > 0) {
|
||||
len = (slen > (115-klog_offs)) ? (115-klog_offs) : slen;
|
||||
if(len <= 0) {
|
||||
break;
|
||||
}
|
||||
memcpy((klogger[klog_pos])+klog_offs, text, len);
|
||||
klog_offs += len;
|
||||
klogger[klog_pos][klog_offs] = 0;
|
||||
slen -= len;
|
||||
if(slen > 0) {
|
||||
klog_pos += 1;
|
||||
klog_offs = 0;
|
||||
if(klog_pos == 5) {
|
||||
mid_clog();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(log_debug || (type <= 2) || (type >= 5)) {
|
||||
mid_mlog(STR_MID_TEXT, type, text);
|
||||
}
|
||||
}
|
||||
|
||||
void mid_dlog(char *format, ...) {
|
||||
if(log_debug) {
|
||||
mid_mlog(format);
|
||||
}
|
||||
}
|
||||
|
||||
#include "opl3.h"
|
||||
#include "dmx.h"
|
||||
#include "bank.h"
|
||||
#include "midi.h"
|
||||
#include "audio.h"
|
||||
#define ARGPARSE_DE
|
||||
#include "../argparse.h"
|
||||
|
||||
#define PLR_REFRESH 16
|
||||
|
||||
#define PLR_BLACK 1
|
||||
#define PLR_DRED 2
|
||||
#define PLR_DGREEN 3
|
||||
#define PLR_DYELLOW 4
|
||||
#define PLR_DBLUE 5
|
||||
#define PLR_DMAGENTA 6
|
||||
#define PLR_DCYAN 7
|
||||
#define PLR_GRAY 8
|
||||
#define PLR_DARKGRAY 9
|
||||
#define PLR_RED 10
|
||||
#define PLR_GREEN 11
|
||||
#define PLR_YELLOW 12
|
||||
#define PLR_BLUE 13
|
||||
#define PLR_MAGENTA 14
|
||||
#define PLR_CYAN 15
|
||||
#define PLR_WHITE 16
|
||||
|
||||
enum plr_state {
|
||||
PL_NORMAL,
|
||||
PL_PREV,
|
||||
PL_NEXT,
|
||||
PL_QUIT,
|
||||
PL_INTERRUPT
|
||||
};
|
||||
|
||||
int8_t plr_int = PL_NORMAL;
|
||||
|
||||
void plr_sig(int signum) {
|
||||
plr_int = PL_INTERRUPT;
|
||||
}
|
||||
|
||||
void plr_skip() {
|
||||
if(plr_int == PL_INTERRUPT) {
|
||||
return;
|
||||
}
|
||||
int c = wgetch(stdscr);
|
||||
switch(c) {
|
||||
case 'n':
|
||||
plr_int = PL_NEXT;
|
||||
break;
|
||||
case 'b':
|
||||
plr_int = PL_PREV;
|
||||
break;
|
||||
case 'q':
|
||||
plr_int = PL_QUIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void str_time(char *str, uint64_t time) {
|
||||
uint64_t msecs = time % 1000ULL;
|
||||
uint64_t secs = (time /= 1000ULL) % 60ULL;
|
||||
uint64_t mins = (time /= 60ULL) % 60ULL;
|
||||
time /= 60ULL;
|
||||
sprintf(str, "%02d:%02d:%02d.%03d", time, mins, secs, msecs);
|
||||
}
|
||||
|
||||
void plr_sep() {
|
||||
color_set(PLR_DARKGRAY, NULL);
|
||||
addch('|');
|
||||
}
|
||||
|
||||
void plr_vbdr() {
|
||||
color_set(PLR_DARKGRAY, NULL);
|
||||
addstr("||");
|
||||
}
|
||||
|
||||
void plr_newl(int *line) {
|
||||
plr_vbdr();
|
||||
move((*line)++, 0);
|
||||
}
|
||||
|
||||
void plr_hbdr(int width) {
|
||||
color_set(PLR_DARKGRAY, NULL);
|
||||
memset(tbuffer, '#', width);
|
||||
tbuffer[width] = 0;
|
||||
addstr(tbuffer);
|
||||
}
|
||||
|
||||
void plr_pad(int width) {
|
||||
memset(tbuffer, ' ', width);
|
||||
tbuffer[width] = 0;
|
||||
addstr(tbuffer);
|
||||
}
|
||||
|
||||
void plr_hnbdr(int width, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
color_set(PLR_GRAY, NULL);
|
||||
int len = vsprintf(tbuffer, fmt, ap);
|
||||
addstr(tbuffer);
|
||||
addch(' ');
|
||||
plr_hbdr(width - (len+1));
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void plr_cpfx(char desc, int id, int cl) {
|
||||
color_set(cl, NULL);
|
||||
sprintf(tbuffer, "%c-%02d", desc, id);
|
||||
addstr(tbuffer);
|
||||
}
|
||||
|
||||
void plr_pfx(const char *desc) {
|
||||
color_set(PLR_DYELLOW, NULL);
|
||||
addstr(desc);
|
||||
}
|
||||
|
||||
void plr_bv(char ch, int cl, bool on) {
|
||||
color_set(on ? cl : PLR_DBLUE, NULL);
|
||||
addch(on ? ch : '-');
|
||||
}
|
||||
|
||||
void plr_dv(const char *s1, int cl1, const char *s2, int cl2, bool sec) {
|
||||
color_set(sec ? cl2 : cl1, NULL);
|
||||
addstr(sec ? s2 : s1);
|
||||
}
|
||||
|
||||
void plr_fcv(int cl, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
color_set(cl, NULL);
|
||||
vsprintf(tbuffer, fmt, ap);
|
||||
addstr(tbuffer);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void plr_fv(const char *desc, bool dark, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
color_set(PLR_GRAY, NULL);
|
||||
addstr(desc);
|
||||
color_set(dark ? PLR_DCYAN : PLR_CYAN, NULL);
|
||||
vsprintf(tbuffer, fmt, ap);
|
||||
addstr(tbuffer);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void plr_fc(const char *desc, char value) {
|
||||
plr_fv(desc, 0, "%c", value);
|
||||
}
|
||||
|
||||
void plr_fi(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%d", value);
|
||||
}
|
||||
|
||||
void plr_fi2(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%02d", value);
|
||||
}
|
||||
|
||||
void plr_fi2d(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 1, "%02d", value);
|
||||
}
|
||||
|
||||
void plr_fi3(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%03d", value);
|
||||
}
|
||||
|
||||
void plr_fi4(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%04d", value);
|
||||
}
|
||||
|
||||
void plr_fi5p(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%+05d", value);
|
||||
}
|
||||
|
||||
void plr_fi8(const char *desc, int64_t value) {
|
||||
plr_fv(desc, 0, "%08lld", value);
|
||||
}
|
||||
|
||||
void plr_fi4n(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%4d", value);
|
||||
}
|
||||
|
||||
void plr_fhz(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%6d Hz", value);
|
||||
}
|
||||
|
||||
void plr_fbit(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%2d-Bit", value);
|
||||
}
|
||||
|
||||
void plr_fbpm(const char *desc, int32_t value) {
|
||||
plr_fv(desc, 0, "%4d BPM", value);
|
||||
}
|
||||
|
||||
void plr_fstr(const char *desc, const char *value) {
|
||||
plr_fv(desc, 0, "%s", value);
|
||||
}
|
||||
|
||||
void plr_str(const char *str, int cl) {
|
||||
color_set(cl, NULL);
|
||||
addstr(str);
|
||||
}
|
||||
|
||||
void plr_ncstr(const char *str, int cl, int width) {
|
||||
color_set(cl, NULL);
|
||||
sprintf(tbuffer, "%-*2$.*2$s", str, width);
|
||||
addstr(tbuffer);
|
||||
}
|
||||
|
||||
void plr_nstr(const char *str, int width) {
|
||||
plr_ncstr(str, PLR_GRAY, width);
|
||||
}
|
||||
|
||||
void plr_dstr(const char *str, int width) {
|
||||
color_set(PLR_CYAN, NULL);
|
||||
sprintf(tbuffer, "%*2$.*2$s", str, width);
|
||||
addstr(tbuffer);
|
||||
}
|
||||
|
||||
void plr_printchip(opl3_chip *chip, bank_handle *bank, mid_handle *mid, aud_handle *dev, uint8_t nowait, uint32_t tick, uint64_t mtime, const char *midname, int16_t *aud) {
|
||||
char gfx[33];
|
||||
move(1, 0);
|
||||
if(dev->iswav) {
|
||||
char *logp = logger[(log_pos+4-1) % 4];
|
||||
str_time(gfx, mtime);
|
||||
sprintf(tbuffer, "[%s][%08d] %-115s\n", gfx, tick, logp);
|
||||
addstr(tbuffer);
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
int line = 1;
|
||||
int sw;
|
||||
int sh;
|
||||
getmaxyx(stdscr, sh, sw);
|
||||
sw = (sw < 176) ? 176 : sw;
|
||||
sh = (sh < 34) ? 34 : sh;
|
||||
if((sw != last_w) || (sh != last_h)) {
|
||||
erase();
|
||||
}
|
||||
last_w = sw;
|
||||
last_h = sh;
|
||||
int PLR_WIDTH = (sw-2)/39;
|
||||
int PLR_HEIGHT = (sh-34)/3;
|
||||
int pad = (sw-2) - (PLR_WIDTH * 39);
|
||||
int cpad = sw-176;
|
||||
int lpad = sw-121;
|
||||
int nch = chip->n_voices;
|
||||
PLR_HEIGHT = (PLR_HEIGHT > ((nch+PLR_WIDTH-1)/PLR_WIDTH)) ? ((nch+PLR_WIDTH-1)/PLR_WIDTH) : PLR_HEIGHT;
|
||||
int addl = sh - 34 - (PLR_HEIGHT * 3);
|
||||
gfx[16] = 0;
|
||||
plr_vbdr();
|
||||
plr_hnbdr(sw-4, "%s", STR_PLR_TITLE);
|
||||
plr_newl(&line);
|
||||
for(int p = 0; p < PLR_HEIGHT; p++) {
|
||||
for(int c = 0; c < PLR_WIDTH; c++) {
|
||||
if((p*PLR_WIDTH+c) >= nch) {
|
||||
plr_vbdr();
|
||||
plr_pad(39*(PLR_WIDTH-c)-2);
|
||||
break;
|
||||
}
|
||||
opl3_channel *channel = &chip->channel[p*PLR_WIDTH+c];
|
||||
int16_t **out = channel->out;
|
||||
int16_t accm = *out[0] + *out[1] + *out[2] + *out[3];
|
||||
int32_t chnlvl = 0;
|
||||
for(int n = 0; n < 2; n++) {
|
||||
chnlvl += (int16_t)((accm * channel->level[n]) >> 16);
|
||||
}
|
||||
chnlvl = chnlvl < 0 ? (-chnlvl) : chnlvl;
|
||||
chnlvl = (chnlvl * 16) / 8192;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
gfx[i] = i < chnlvl ? '#' : '-';
|
||||
}
|
||||
int vid = (p*PLR_WIDTH+c) + 1;
|
||||
uint8_t active = (channel->slots[0]->eg_out <= 0x100) || (channel->slots[1]->eg_out <= 0x100);
|
||||
plr_vbdr();
|
||||
if(vid < 100) {
|
||||
plr_fcv(active ? PLR_MAGENTA : PLR_DMAGENTA, "%02d", vid);
|
||||
}
|
||||
else {
|
||||
plr_fcv(active ? PLR_MAGENTA : PLR_DMAGENTA, "++");
|
||||
}
|
||||
plr_sep();
|
||||
plr_dv("2O", PLR_BLUE, "4O", PLR_GREEN, channel->chtype != ch_2op);
|
||||
plr_sep();
|
||||
plr_dv("FM", PLR_RED, "AM", PLR_YELLOW, channel->con);
|
||||
plr_sep();
|
||||
plr_fi("FB", channel->fb);
|
||||
plr_sep();
|
||||
plr_fi("F", channel->block);
|
||||
plr_fi4("$", channel->f_num);
|
||||
plr_sep();
|
||||
plr_str(gfx, PLR_GREEN);
|
||||
}
|
||||
if(pad >= 2) {
|
||||
plr_vbdr();
|
||||
plr_pad(pad-2);
|
||||
}
|
||||
else if(pad == 1) {
|
||||
plr_sep();
|
||||
}
|
||||
plr_newl(&line);
|
||||
for(int o = 0; o < 2; o++) {
|
||||
for(int c = 0; c < PLR_WIDTH; c++) {
|
||||
if((p*PLR_WIDTH+c) >= nch) {
|
||||
plr_vbdr();
|
||||
plr_pad(39*(PLR_WIDTH-c)-2);
|
||||
break;
|
||||
}
|
||||
opl3_channel *channel = &chip->channel[p*PLR_WIDTH+c];
|
||||
opl3_slot *slot = channel->slots[o];
|
||||
uint8_t trem = slot->trem != (uint8_t*)&slot->chip->zeromod;
|
||||
plr_vbdr();
|
||||
// plr_fcv(PLR_DMAGENTA, "O%d", o + 1);
|
||||
// plr_sep();
|
||||
plr_dv(".", PLR_DGREEN, "*", PLR_GREEN, slot->key);
|
||||
plr_sep();
|
||||
plr_bv('T', PLR_GREEN, trem);
|
||||
plr_bv('V', PLR_YELLOW, slot->reg_vib);
|
||||
plr_bv('S', PLR_RED, slot->reg_type);
|
||||
plr_bv('K', PLR_MAGENTA, slot->reg_ksr);
|
||||
plr_sep();
|
||||
plr_fi2("M", slot->reg_mult);
|
||||
// plr_sep();
|
||||
plr_fi2("L", 63-slot->reg_tl);
|
||||
// plr_sep();
|
||||
plr_fi("K", slot->reg_ksl);
|
||||
// plr_fi3("/", slot->eg_ksl);
|
||||
// plr_sep();
|
||||
plr_fc("W", plr_waves[slot->reg_wf]);
|
||||
plr_sep();
|
||||
plr_fi2("E", slot->reg_ar);
|
||||
plr_fi2d("", slot->reg_dr);
|
||||
plr_fi2("", (slot->reg_sl > 15) ? 0 : (15-slot->reg_sl));
|
||||
plr_fi2d("", slot->reg_rr);
|
||||
plr_sep();
|
||||
plr_fc("P", plr_phases[slot->eg_gen]);
|
||||
plr_fi3("", slot->eg_rout);
|
||||
plr_fi3("/", slot->eg_out);
|
||||
}
|
||||
if(pad >= 2) {
|
||||
plr_vbdr();
|
||||
plr_pad(pad-2);
|
||||
}
|
||||
else if(pad == 1) {
|
||||
plr_sep();
|
||||
}
|
||||
plr_newl(&line);
|
||||
}
|
||||
}
|
||||
for(int l = 0; l < addl; l++) {
|
||||
plr_vbdr();
|
||||
plr_pad(39*PLR_WIDTH-2);
|
||||
if(pad >= 2) {
|
||||
plr_vbdr();
|
||||
plr_pad(pad-2);
|
||||
}
|
||||
else if(pad == 1) {
|
||||
plr_sep();
|
||||
}
|
||||
plr_newl(&line);
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_hbdr(sw-4);
|
||||
plr_newl(&line);
|
||||
char nid[128+1];
|
||||
nid[128] = 0;
|
||||
char chid[cpad+1];
|
||||
chid[cpad] = 0;
|
||||
for(int d = chip->n_voices; d < cpad; d++) {
|
||||
chid[d] = 'x';
|
||||
}
|
||||
for(int c = 0; c < 16; c++) {
|
||||
bank_channel *channel = &bank->channel[c];
|
||||
for(int d = 0; d < 128; d++) {
|
||||
nid[d] = (channel->notes[d] == 0xff) ? '-' : '_';
|
||||
}
|
||||
for(int d = 0; (d < cpad) && (d < chip->n_voices); d++) {
|
||||
bank_voice *voice = &bank->voices[d];
|
||||
chid[d] = ((voice->note != 0xff) && (voice->channel->ch_num == c)) ? '*' : '.';
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_cpfx('M', c + 1, ((channel->pbank != 0) && (channel->pbank != 128)) ? ((bank->flags & BANK_UNKN) ? PLR_YELLOW : PLR_RED) : PLR_DYELLOW);
|
||||
plr_sep();
|
||||
if((channel->pbank >= 0) && (channel->pbank < 1000)) {
|
||||
plr_fi3("PR", channel->pbank);
|
||||
}
|
||||
else {
|
||||
plr_fstr("PR", "???");
|
||||
}
|
||||
plr_fi3("/", channel->program);
|
||||
plr_sep();
|
||||
plr_fi3("VL", channel->volume);
|
||||
plr_sep();
|
||||
plr_fi3("PN", channel->pan);
|
||||
plr_sep();
|
||||
plr_fi5p("PT", channel->pitch);
|
||||
plr_sep();
|
||||
plr_fi3("AC", channel->active);
|
||||
plr_vbdr();
|
||||
plr_str(nid, PLR_GREEN);
|
||||
plr_vbdr();
|
||||
plr_str(chid, PLR_GREEN);
|
||||
plr_newl(&line);
|
||||
}
|
||||
for(int d = 0; d < 128; d++) {
|
||||
nid[d] = ' ';
|
||||
}
|
||||
for(int d = 0; d < 11; d++) {
|
||||
nid[d*12] = plr_hexn[d];
|
||||
}
|
||||
for(int d = 0; d < cpad; d++) {
|
||||
chid[d] = (d == bank->voiceindex) ? '^' : ' ';
|
||||
}
|
||||
str_time(gfx, mtime);
|
||||
plr_vbdr();
|
||||
plr_pfx("MIDI");
|
||||
plr_sep();
|
||||
plr_fi8("TICK ", tick);
|
||||
plr_sep();
|
||||
plr_fstr("TIME ", gfx);
|
||||
plr_vbdr();
|
||||
plr_str(nid, PLR_GRAY);
|
||||
plr_vbdr();
|
||||
plr_str(chid, PLR_GREEN);
|
||||
plr_newl(&line);
|
||||
for(int d = 0; d < 128; d++) {
|
||||
nid[d] = plr_notes[d % 12];
|
||||
}
|
||||
if(bank->voiceindex < cpad) {
|
||||
chid[bank->voiceindex] = '*';
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_bv('K', PLR_YELLOW, bank->flags & BANK_KEEP);
|
||||
plr_bv('U', PLR_RED, bank->flags & BANK_UNKN);
|
||||
plr_bv('W', PLR_MAGENTA, !nowait);
|
||||
plr_bv('?', PLR_GREEN, false);
|
||||
plr_sep();
|
||||
plr_fi3("USED ", bank->v_used);
|
||||
plr_fi3("/", bank->v_avail);
|
||||
plr_sep();
|
||||
plr_fbpm("TEMPO ", 60000000 / mid->uspb);
|
||||
plr_fi4n("/", mid->tpqn);
|
||||
plr_vbdr();
|
||||
plr_str(nid, PLR_GRAY);
|
||||
plr_vbdr();
|
||||
plr_str(chid, PLR_GREEN);
|
||||
plr_newl(&line);
|
||||
for(int d = 0; d < cpad; d++) {
|
||||
chid[d] = '0' + (((d+1)/10) % 10);
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_pfx("CARD");
|
||||
plr_sep();
|
||||
plr_fbit("FORMAT ", dev->iswide ? 32 : 16);
|
||||
plr_sep();
|
||||
plr_fhz("RATE ", dev->samplerate);
|
||||
plr_vbdr();
|
||||
plr_hbdr(128);
|
||||
plr_vbdr();
|
||||
plr_str(chid, PLR_GRAY);
|
||||
plr_newl(&line);
|
||||
for(int d = 0; d < cpad; d++) {
|
||||
chid[d] = '0' + ((d+1) % 10);
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_pfx("BANK");
|
||||
plr_sep();
|
||||
plr_dstr(bankname, 35);
|
||||
plr_vbdr();
|
||||
plr_pfx("FILE");
|
||||
plr_sep();
|
||||
plr_dstr(midname, 123);
|
||||
plr_vbdr();
|
||||
plr_str(chid, PLR_GRAY);
|
||||
plr_newl(&line);
|
||||
|
||||
plr_vbdr();
|
||||
plr_hbdr(sw-4);
|
||||
plr_newl(&line);
|
||||
for(int l = 0; l < 4; l++) {
|
||||
plr_vbdr();
|
||||
plr_nstr(midinfo[l], lpad);
|
||||
plr_vbdr();
|
||||
plr_nstr(klogger[l], 115);
|
||||
plr_newl(&line);
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_nstr(midinfo[4], lpad/2);
|
||||
plr_sep();
|
||||
plr_nstr(midinfo[5], (lpad-1)/2);
|
||||
plr_vbdr();
|
||||
plr_nstr(klogger[4], 115);
|
||||
plr_newl(&line);
|
||||
|
||||
plr_vbdr();
|
||||
plr_hbdr(sw-4);
|
||||
plr_newl(&line);
|
||||
gfx[32] = 0;
|
||||
vu_push(aud);
|
||||
for(int l = 0; l < 4; l++) {
|
||||
if((l & 1) == 0) {
|
||||
int32_t chnlvl = vu_sum[l >> 1];
|
||||
chnlvl = (chnlvl * 32) / 8192;
|
||||
for(int i = 0; i < 32; i++) {
|
||||
gfx[i] = i < chnlvl ? '#' : '-';
|
||||
}
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_nstr(logger[(log_pos+l) % 4], lpad);
|
||||
plr_vbdr();
|
||||
plr_nstr(mlogger[(mlog_pos+l) % 4], 81);
|
||||
plr_vbdr();
|
||||
plr_ncstr(gfx, PLR_GREEN, 26);
|
||||
plr_ncstr(gfx+26, PLR_YELLOW, 4);
|
||||
plr_ncstr(gfx+30, PLR_RED, 2);
|
||||
plr_newl(&line);
|
||||
}
|
||||
plr_vbdr();
|
||||
plr_hnbdr(sw-4, "%c %s", plr_wheel[(tick >> 5) & 3], STR_PLR_INFO);
|
||||
plr_newl(&line);
|
||||
|
||||
plr_skip();
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
bool plr_play(opl3_chip *chip, bank_handle *bank, aud_handle *dev, const char *midname, const char *dmxname, uint8_t nowait, uint32_t midis, uint32_t num) {
|
||||
mid_handle mid;
|
||||
if(mid_read(&mid, midname) ^ 1) {
|
||||
return false;
|
||||
}
|
||||
mid_log(STR_PLR_PLAY, midname, num+1, midis);
|
||||
uint64_t sampletime = 1000000000ULL / ((uint64_t)dev->samplerate);
|
||||
uint64_t elapsed = 0;
|
||||
uint64_t samples = 0;
|
||||
uint64_t pushed = 0;
|
||||
uint32_t tick = 0;
|
||||
int16_t buf[2];
|
||||
while((plr_int == PL_NORMAL) && mid_tick(&mid, bank, chip)) {
|
||||
elapsed += ((uint64_t)mid.ticktime) * 1000ULL;
|
||||
while(elapsed >= sampletime) {
|
||||
OPL3_GenerateResampled(chip, buf);
|
||||
aud_push(dev, buf);
|
||||
samples++;
|
||||
if(samples >= (dev->samplerate/PLR_REFRESH)) {
|
||||
pushed += samples;
|
||||
samples = 0;
|
||||
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
|
||||
}
|
||||
elapsed -= sampletime;
|
||||
}
|
||||
if(samples >= (dev->samplerate/PLR_REFRESH)) {
|
||||
pushed += samples;
|
||||
samples = 0;
|
||||
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
|
||||
}
|
||||
tick++;
|
||||
}
|
||||
bank_alloff(bank);
|
||||
while((plr_int == PL_NORMAL) && (nowait ^ 1) && OPL3_Playing(chip)) {
|
||||
OPL3_GenerateResampled(chip, buf);
|
||||
aud_push(dev, buf);
|
||||
samples++;
|
||||
if(samples >= (dev->samplerate/PLR_REFRESH)) {
|
||||
pushed += samples;
|
||||
samples = 0;
|
||||
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
|
||||
}
|
||||
}
|
||||
plr_printchip(chip, bank, &mid, dev, nowait, tick, (pushed+samples)*1000ULL/dev->samplerate, midname, buf);
|
||||
aud_flush(dev);
|
||||
mid_free(&mid);
|
||||
OPL3_Reset(chip);
|
||||
bank_reset(chip, bank);
|
||||
mid_cmlog();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plr_playloop(uint32_t samplerate, uint32_t voices, char **midnames, uint32_t midis, const char *dmxname, const char *drumname, const char *device, uint32_t fsize, uint32_t bufsize, uint8_t widefmt,
|
||||
uint8_t keep, uint8_t useunkn, int8_t velofunc, uint8_t nowait) {
|
||||
bank_instr *instr = bnk_read_banks(dmxname, drumname, 0);
|
||||
if(instr == NULL) {
|
||||
return false;
|
||||
}
|
||||
opl3_chip *chip = OPL3_Alloc(samplerate, voices);
|
||||
bank_handle *bank = bank_alloc(chip, instr, keep, useunkn, velofunc);
|
||||
char *log = malloc(256*19);
|
||||
for(int h = 0; h < 4; h++) {
|
||||
logger[h] = &log[h*256];
|
||||
logger[h][0] = 0;
|
||||
}
|
||||
for(int h = 0; h < 4; h++) {
|
||||
mlogger[h] = &log[(h+4)*256];
|
||||
}
|
||||
for(int h = 0; h < 5; h++) {
|
||||
klogger[h] = &log[(h+8)*256];
|
||||
}
|
||||
for(int h = 0; h < 6; h++) {
|
||||
midinfo[h] = &log[(h+13)*256];
|
||||
}
|
||||
plr_setbnk(dmxname, drumname);
|
||||
vu_reset();
|
||||
mid_cmlog();
|
||||
aud_handle dev;
|
||||
if(aud_open_dev(&dev, device, samplerate, 2, fsize, bufsize, widefmt) ^ 1) {
|
||||
free(instr);
|
||||
free(chip);
|
||||
free(bank);
|
||||
free(log);
|
||||
return false;
|
||||
}
|
||||
samplerate = dev.samplerate;
|
||||
OPL3_Rate(chip, samplerate);
|
||||
signal(SIGINT, plr_sig);
|
||||
initscr();
|
||||
if(!(dev.iswav)) {
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(PLR_BLACK, 0, -1);
|
||||
init_pair(PLR_DRED, 1, -1);
|
||||
init_pair(PLR_DGREEN, 2, -1);
|
||||
init_pair(PLR_DYELLOW, 3, -1);
|
||||
init_pair(PLR_DBLUE, 4, -1);
|
||||
init_pair(PLR_DMAGENTA, 5, -1);
|
||||
init_pair(PLR_DCYAN, 6, -1);
|
||||
init_pair(PLR_GRAY, 7, -1);
|
||||
init_pair(PLR_DARKGRAY, 8, -1);
|
||||
init_pair(PLR_RED, 9, -1);
|
||||
init_pair(PLR_GREEN, 10, -1);
|
||||
init_pair(PLR_YELLOW, 11, -1);
|
||||
init_pair(PLR_BLUE, 12, -1);
|
||||
init_pair(PLR_MAGENTA, 13, -1);
|
||||
init_pair(PLR_CYAN, 14, -1);
|
||||
init_pair(PLR_WHITE, 15, -1);
|
||||
}
|
||||
cbreak();
|
||||
noecho();
|
||||
nodelay(stdscr, true);
|
||||
nonl();
|
||||
intrflush(stdscr, false);
|
||||
keypad(stdscr, true);
|
||||
curs_set(0);
|
||||
tbuffer = malloc(65536);
|
||||
|
||||
bool state = false;
|
||||
bool skip = false;
|
||||
for(int h = 0; (h < midis) && (plr_int == PL_NORMAL); h++) {
|
||||
if(plr_play(chip, bank, &dev, midnames[h], dmxname, nowait, midis, h)) {
|
||||
skip = false;
|
||||
state = true;
|
||||
}
|
||||
else if(skip) {
|
||||
plr_int = PL_PREV;
|
||||
}
|
||||
if(plr_int == PL_NEXT) {
|
||||
plr_int = PL_NORMAL;
|
||||
}
|
||||
else if(plr_int == PL_PREV) {
|
||||
plr_int = PL_NORMAL;
|
||||
h -= ((h == 0) ? 1 : 2);
|
||||
skip = h >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// vu_reset();
|
||||
// plr_printchip(chip, bank, &dev, nowait, 0, 0ULL, 0, dev.iswav, "", "", NULL);
|
||||
aud_close(&dev);
|
||||
free(instr);
|
||||
free(chip);
|
||||
free(bank);
|
||||
free(log);
|
||||
free(tbuffer);
|
||||
endwin();
|
||||
fprintf(stderr, (plr_int == PL_INTERRUPT) ? STR_PLR_INT"\n" : STR_PLR_END"\n");
|
||||
return state;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int32_t samplerate = 48000;
|
||||
int32_t voices = 36;
|
||||
char *device = "default";
|
||||
int32_t bufsize = 0;
|
||||
int32_t fsize = 32;
|
||||
int32_t widefmt = 0;
|
||||
int32_t keep = 0;
|
||||
int32_t useunkn = 0;
|
||||
int32_t velo = 1;
|
||||
int32_t nowait = 0;
|
||||
char *dmxname = "bank.op2";
|
||||
char *drumname = NULL;
|
||||
int32_t midis = 0;
|
||||
char **midnames = malloc(sizeof(char*) * argc);
|
||||
|
||||
argp_t *argspec = arg_alloc(14);
|
||||
arg_add_vstr(argspec, STR_ARGS_MIDI, true, midnames, &midis);
|
||||
arg_add_int(argspec, 'r', STR_ARGS_SRATE, 8000, 384000, &samplerate);
|
||||
arg_add_int(argspec, 'v', STR_ARGS_VOICES, 2, 192, &voices);
|
||||
arg_add_str(argspec, 'd', STR_ARGS_DEVICE, &device);
|
||||
arg_add_int(argspec, 'b', STR_ARGS_BUFFER, 0, 1048576, &bufsize);
|
||||
arg_add_int(argspec, 'f', STR_ARGS_FRAMES, 2, 8192, &fsize);
|
||||
arg_add_int(argspec, 'e', STR_ARGS_VELO, -128, 127, &velo);
|
||||
arg_add_bool(argspec, 'w', STR_ARGS_WIDEFT, &widefmt);
|
||||
arg_add_bool(argspec, 'k', STR_ARGS_KEEPV, &keep);
|
||||
arg_add_bool(argspec, 'u', STR_ARGS_USEUNK, &useunkn);
|
||||
arg_add_bool(argspec, 'n', STR_ARGS_DONTWT, &nowait);
|
||||
arg_add_str(argspec, 'p', STR_ARGS_BANK, &dmxname);
|
||||
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
|
||||
arg_add_bool(argspec, 'D', STR_ARGS_MDEBUG, &log_debug);
|
||||
if(arg_parse(argspec, argc, argv) ^ 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((voices & 1) != 0) {
|
||||
fprintf(stderr, STR_PLR_UNEVEN"\n");
|
||||
free(midnames);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool state = plr_playloop(samplerate, voices, midnames, midis, dmxname, drumname, device, fsize, bufsize, widefmt, keep, useunkn ^ 1, velo, nowait);
|
||||
free(midnames);
|
||||
return state ? 0 : 2;
|
||||
}
|
119
strings.h
Normal file
119
strings.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
#define STR_ARGS_MIDI "MIDI-Datei"
|
||||
#define STR_ARGS_BANK "Bank-Datei"
|
||||
#define STR_ARGS_DBANK "Perk.-Datei"
|
||||
#define STR_ARGS_USEDRM "Als Perk.-Bank"
|
||||
#define STR_ARGS_SRATE "Abtastrate"
|
||||
#define STR_ARGS_VOICES "OPL-Stimmen"
|
||||
#define STR_ARGS_DEVICE "ALSA-Geraet/WAV-Datei"
|
||||
#define STR_ARGS_BUFFER "Puffergroesse"
|
||||
#define STR_ARGS_FRAMES "PCM-Intervall"
|
||||
#define STR_ARGS_WIDEFT "32-Bit Samples"
|
||||
#define STR_ARGS_KEEPV "Stimmen behalten"
|
||||
#define STR_ARGS_USEUNK "Keine unbek. Bank"
|
||||
#define STR_ARGS_VELO "Anschlag-Funktion"
|
||||
#define STR_ARGS_DONTWT "Nicht ausklingen"
|
||||
#define STR_ARGS_OFFSET "Start"
|
||||
#define STR_ARGS_NOTE "Note"
|
||||
#define STR_ARGS_LENGTH "Laenge"
|
||||
#define STR_ARGS_PAUSE "Pause"
|
||||
#define STR_ARGS_INSTR "Instrument-N."
|
||||
#define STR_ARGS_PERC "Perk.-Nummer"
|
||||
#define STR_ARGS_DPERC "DMX-Perk.-N."
|
||||
#define STR_ARGS_BANKN "Bank-Nummer"
|
||||
#define STR_ARGS_NTABLE "Liste [k. Tabelle]"
|
||||
#define STR_ARGS_NONAME "Keine Instr.-Namen"
|
||||
#define STR_ARGS_SKB "SKB-Datei"
|
||||
#define STR_ARGS_MDEBUG "MIDI-Debug"
|
||||
|
||||
#define STR_WAV_OPNERR "Fehler beim Oeffnen von WAV-Datei '%s': %s (%d)"
|
||||
#define STR_WAV_WRITERR "Fehler beim Schreiben nach WAV-Datei '%s': "
|
||||
#define STR_WAV_EIO STR_WAV_WRITERR "E/A-Fehler"
|
||||
#define STR_WAV_EGEN STR_WAV_WRITERR "%s (%d)"
|
||||
|
||||
#define STR_MID_OPNERR "Fehler beim Oeffnen von MIDI-Datei '%s': %s (%d)"
|
||||
#define STR_MID_READERR "Fehler beim Lesen von MIDI-Datei '%s': "
|
||||
#define STR_MID_EIO STR_MID_READERR "E/A-Fehler"
|
||||
#define STR_MID_EEOF STR_MID_READERR "Ende der Datei erreicht"
|
||||
#define STR_MID_EGEN STR_MID_READERR "%s (%d)"
|
||||
#define STR_MID_ESHORT STR_MID_READERR "Datei zu klein"
|
||||
#define STR_MID_EMFHDR STR_MID_READERR "Fehlerhafter Dateiheader oder falscher Dateityp"
|
||||
#define STR_MID_EHSIZE STR_MID_READERR "Dateiheader mit falscher Laenge"
|
||||
#define STR_MID_ETKFMT STR_MID_READERR "Fehlerhaftes Spurformat"
|
||||
#define STR_MID_ENOTRK STR_MID_READERR "Keine Spuren definiert"
|
||||
#define STR_MID_ESMULT STR_MID_READERR "Einzelspur-Format mit mehreren Spuren"
|
||||
#define STR_MID_ESMPTE STR_MID_READERR "SMPTE-Zeitformat nicht unterstuetzt"
|
||||
#define STR_MID_EDZERO STR_MID_READERR "Zeitformat teilt durch null"
|
||||
#define STR_MID_ETKHDR STR_MID_READERR "Fehlerhafter Spurheader"
|
||||
#define STR_MID_EOUTOF STR_MID_READERR "Spur #%d ausserhalb der Datei"
|
||||
#define STR_MID_SEQNUMO "MIDI Spur #%d Sequenz-Nummer = %d (eigene)"
|
||||
#define STR_MID_SEQNUM "MIDI Spur #%d Sequenz-Nummer = %d"
|
||||
#define STR_MID_CHNPFX "MIDI Spur #%d Kanal-Praefix = %d"
|
||||
#define STR_MID_END "MIDI Spur #%d Ende erreicht"
|
||||
#define STR_MID_TEMPO "MIDI Tempo = %d"
|
||||
#define STR_MID_SMPTE "MIDI SMPTE-Event (nicht unterstuetzt)"
|
||||
#define STR_MID_TIMESIG "MIDI Takt-Signatur = %d/%d - %d CPC - %d 32PQ"
|
||||
#define STR_MID_KEYSIG "MIDI Noten-Signatur = %d - %s"
|
||||
#define STR_MID_TEXT "MIDI Text (%d): %s"
|
||||
#define STR_MID_SEQDATA "MIDI Spur #%d Sequenzer-Daten empfangen - %d Bytes"
|
||||
#define STR_MID_UNKMETA "MIDI Spur #%d Meta-Event 0x%02x (%d)"
|
||||
#define STR_MID_METALEN "MIDI Spur #%d Meta-Event 0x%02x - erwartet %d, hat %d"
|
||||
#define STR_MID_NOTEOFF "MIDI Note-Aus - C%02d N%03d V%03d"
|
||||
#define STR_MID_NOTEON "MIDI Note-An - C%02d N%03d V%03d"
|
||||
#define STR_MID_PRESS "MIDI Aftertouch - C%02d P%03d"
|
||||
#define STR_MID_CC "MIDI Controller - C%02d N%03d V%03d"
|
||||
#define STR_MID_PROG "MIDI Programm - C%02d P%03d"
|
||||
#define STR_MID_CPRESS "MIDI Kanal-Druck - C%02d P%03d"
|
||||
#define STR_MID_PITCH "MIDI Pitch-Bend - C%02d P%03d"
|
||||
#define STR_MID_SYSEX "MIDI Sysex (Normal) mit Laenge = %d"
|
||||
#define STR_MID_SONGPOS "MIDI Song-Position = %d"
|
||||
#define STR_MID_SONGSEL "MIDI Song-Auswahl = %d"
|
||||
#define STR_MID_TUNE "MIDI Stimmung angefordert, nichts zu tun?!"
|
||||
#define STR_MID_ESYSEX "MIDI Sysex (Escape) mit Laenge = %d"
|
||||
#define STR_MID_GENSTAT "MIDI Status %d"
|
||||
#define STR_MID_UNKSTAT "MIDI Status unbekannt: 0x%02x"
|
||||
#define STR_MID_EEND "MIDI Spur #%d endete zu frueh"
|
||||
|
||||
#define STR_IMID_TITLE "Titel"
|
||||
#define STR_IMID_INFO "Info"
|
||||
#define STR_IMID_COPY "Hinweis"
|
||||
#define STR_IMID_WARN "Lizenz"
|
||||
#define STR_IMID_LANG "Sprache"
|
||||
#define STR_IMID_VER "Version"
|
||||
|
||||
#define STR_BNK_UNKN "Format von Bank-Datei '%s' unbekannt"
|
||||
#define STR_BNK_OPNERR "Fehler beim Oeffnen von Bank-Datei '%s': %s (%d)"
|
||||
#define STR_BNK_READERR "Fehler beim Lesen von Bank-Datei '%s': "
|
||||
#define STR_BNK_WRITERR "Fehler beim Schreiben nach Bank-Datei '%s': "
|
||||
#define STR_BNK_PARSERR "Fehler beim Verarbeiten von Bank-Datei '%s': "
|
||||
#define STR_BNK_EIO STR_BNK_READERR "E/A-Fehler"
|
||||
#define STR_BNK_EEOF STR_BNK_READERR "Ende der Datei erreicht"
|
||||
#define STR_BNK_EGEN STR_BNK_READERR "%s (%d)"
|
||||
#define STR_BNK_ESHORT STR_BNK_READERR "Datei zu klein"
|
||||
#define STR_BNK_EMFHDR STR_BNK_READERR "Fehlerhafter Dateiheader oder falscher Dateityp"
|
||||
#define STR_BNK_EWIO STR_BNK_WRITERR "E/A-Fehler"
|
||||
#define STR_BNK_EEOD STR_BNK_PARSERR "Ende der Daten erreicht"
|
||||
|
||||
#define STR_AUD_UNDERR "ALSA Puffer-Unterlauf! (%d)"
|
||||
#define STR_AUD_WRITERR "Fehler beim Schreiben von PCM: %s (%d)"
|
||||
#define STR_AUD_OPNERR "Fehler beim Oeffnen von ALSA PCM-Geraet '%s': %s (%d)"
|
||||
#define STR_AUD_HWERR "Fehler beim Setzen von Hardware-Parametern von ALSA PCM-Geraet '%s': %s (%d)"
|
||||
#define STR_AUD_SWERR "Fehler beim Setzen von Software-Parametern von ALSA PCM-Geraet '%s': %s (%d)"
|
||||
#define STR_AUD_FMTERR "Fehler beim Setzen von Sample-Format von ALSA PCM-Geraet '%s': Kein Format verfuegbar"
|
||||
#define STR_AUD_FMTCH "ALSA PCM-Geraet '%s': Sample-Format S%dLE nicht verfuegbar, setze auf S%dLE"
|
||||
#define STR_AUD_PREPERR "Fehler beim Vorbereiten vom ALSA PCM-Geraet"
|
||||
|
||||
#define STR_BNK_UNDEF "Instrument %d ist in der Bank nicht definiert!"
|
||||
#define STR_BNK_NONE "Keine Instrumente zum abspielen!"
|
||||
|
||||
#define STR_TST_NDDRUM "DMX-Perkussions- und Instrument-/MIDI-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
|
||||
#define STR_TST_NMDRUM "MIDI-Perkussions- und Instrument-/DMX-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
|
||||
#define STR_TST_NOFFS "Offset und Instrument-/MIDI-Perkussions-/DMX-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
|
||||
#define STR_TST_DRUM "Spiele Perkussion %d ('%s') .."
|
||||
#define STR_TST_INSTR "Spiele Note #%d auf Instrument %d ('%s') .."
|
||||
|
||||
#define STR_PLR_UNEVEN "Die Anzahl der OPL-Stimmen muss gerade sein"
|
||||
#define STR_PLR_PLAY "Spiele MIDI '%s' (%d/%d) .."
|
||||
#define STR_PLR_END "Fertig."
|
||||
#define STR_PLR_INT "Unterbrochen."
|
||||
#define STR_PLR_TITLE "SKC OPL MIDI Player v 0.0.1 (~ Sen)"
|
||||
#define STR_PLR_INFO "+++ Q - Beenden +++ N - Naechster Titel +++ B - Vorheriger Titel"
|
89
wavfile.h
Normal file
89
wavfile.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
static const char *wav_hdr = "RIFF";
|
||||
static const char *wav_hdr_fmt = "fmt ";
|
||||
static const char *wav_hdr_data = "data";
|
||||
static const char *wav_format = "WAVE";
|
||||
#define WAV_HDRSIZE 44
|
||||
|
||||
typedef struct {
|
||||
uint32_t samplerate;
|
||||
uint16_t channels;
|
||||
uint16_t bytes;
|
||||
uint32_t samples;
|
||||
FILE *fd;
|
||||
const char *filename;
|
||||
uint8_t *buffer;
|
||||
} wav_handle;
|
||||
|
||||
void wav_write_uint32(uint8_t *data, uint32_t value) {
|
||||
for(int h = 0; h < 4; h++) {
|
||||
data[h] = ((uint32_t)((value >> (8*h) & 0xff)));
|
||||
}
|
||||
}
|
||||
|
||||
void wav_write_uint16(uint8_t *data, uint16_t value) {
|
||||
for(int h = 0; h < 2; h++) {
|
||||
data[h] = ((uint16_t)((value >> (8*h) & 0xff)));
|
||||
}
|
||||
}
|
||||
|
||||
void wav_write_header(uint8_t *data, uint32_t samplerate, uint16_t channels, uint32_t samples, uint16_t bytes) {
|
||||
memcpy(data+0, wav_hdr, 4);
|
||||
wav_write_uint32(data+4, 36 + samples * ((uint32_t)channels) * ((uint32_t)bytes));
|
||||
memcpy(data+8, wav_format, 4);
|
||||
memcpy(data+12, wav_hdr_fmt, 4);
|
||||
wav_write_uint32(data+16, 16);
|
||||
wav_write_uint16(data+20, 1);
|
||||
wav_write_uint16(data+22, channels);
|
||||
wav_write_uint32(data+24, samplerate);
|
||||
wav_write_uint32(data+28, samplerate * ((uint32_t)channels) * ((uint32_t)bytes));
|
||||
wav_write_uint16(data+32, channels * bytes);
|
||||
wav_write_uint16(data+34, 8 * bytes);
|
||||
memcpy(data+36, wav_hdr_data, 4);
|
||||
wav_write_uint32(data+40, samples * ((uint32_t)channels) * ((uint32_t)bytes));
|
||||
}
|
||||
|
||||
bool wav_open(wav_handle *wav, const char *filename, uint32_t samplerate, uint16_t channels, uint16_t bytes) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "wb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
mid_log(STR_WAV_OPNERR, filename, strerror(err), err);
|
||||
return false;
|
||||
}
|
||||
uint8_t pad[WAV_HDRSIZE];
|
||||
memset(pad, 0, WAV_HDRSIZE);
|
||||
if(fwrite(pad, 1, WAV_HDRSIZE, fd) != WAV_HDRSIZE) {
|
||||
mid_log(STR_WAV_EIO, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
wav->fd = fd;
|
||||
wav->samplerate = samplerate;
|
||||
wav->channels = channels;
|
||||
wav->bytes = bytes;
|
||||
wav->samples = 0;
|
||||
wav->filename = filename;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wav_close(wav_handle *wav) {
|
||||
if(wav->fd == NULL) {
|
||||
return false;
|
||||
}
|
||||
int err;
|
||||
if(fseek(wav->fd, 0L, SEEK_SET)) {
|
||||
err = errno;
|
||||
mid_log(STR_WAV_EGEN, wav->filename, strerror(err), err);
|
||||
fclose(wav->fd);
|
||||
return false;
|
||||
}
|
||||
uint8_t header[WAV_HDRSIZE];
|
||||
wav_write_header(header, wav->samplerate, wav->channels, wav->samples, wav->bytes);
|
||||
if(fwrite(header, 1, WAV_HDRSIZE, wav->fd) != WAV_HDRSIZE) {
|
||||
mid_log(STR_WAV_EIO, wav->filename);
|
||||
fclose(wav->fd);
|
||||
return false;
|
||||
}
|
||||
fclose(wav->fd);
|
||||
return true;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue