From 36ca6059c18e9c11b390d63d8872ffd682150205 Mon Sep 17 00:00:00 2001 From: Sen Date: Thu, 20 Mar 2025 11:02:57 +0100 Subject: [PATCH] initial commit --- .gitignore | 5 + Makefile | 11 + alsasnd.h | 296 ++++++++++ argparse.h | 271 +++++++++ audio.h | 97 ++++ bank.h | 487 ++++++++++++++++ bankconv.c | 40 ++ bankdump.c | 147 +++++ banks/2x2.op3 | Bin 0 -> 4384 bytes banks/cyberpuck.tmb | Bin 0 -> 3328 bytes banks/d3dtimbr.tmb | Bin 0 -> 3328 bytes banks/d3dtimbrmod.tmb | Bin 0 -> 3328 bytes banks/default.tmb | Bin 0 -> 3328 bytes banks/dmx_dmx.op2 | Bin 0 -> 11908 bytes banks/dmx_doom1.op2 | Bin 0 -> 11908 bytes banks/dmx_doom2.op2 | Bin 0 -> 11908 bytes banks/dmx_raptor.op2 | Bin 0 -> 11908 bytes banks/dmx_strife.op2 | Bin 0 -> 11908 bytes banks/drumopl.ibk | Bin 0 -> 3204 bytes banks/drumopl.tmb | Bin 0 -> 3328 bytes banks/earthsieg.ad | Bin 0 -> 5122 bytes banks/fat2.op3 | Bin 0 -> 4384 bytes banks/fat4.op3 | Bin 0 -> 4384 bytes banks/file49.opl | Bin 0 -> 5019 bytes banks/file53.opl | Bin 0 -> 3662 bytes banks/gmoconel.tmb | Bin 0 -> 3328 bytes banks/gmopl.ibk | Bin 0 -> 3204 bytes banks/gmoplmod.tmb | Bin 0 -> 3328 bytes banks/hmi-144.skb | Bin 0 -> 5636 bytes banks/insmaker_standard.bnk | Bin 0 -> 6748 bytes banks/jv_2op.op3 | Bin 0 -> 6184 bytes banks/mt32.ibk | Bin 0 -> 3204 bytes banks/nemesis.opl | Bin 0 -> 3702 bytes banks/std-o3.skb | Bin 0 -> 8101 bytes banks/std-sb.skb | Bin 0 -> 7023 bytes banks/swtimbr.tmb | Bin 0 -> 3328 bytes banks/tim.tim | Bin 0 -> 2021 bytes banks/wallace.op3 | Bin 0 -> 3112 bytes banks/wallence.tmb | Bin 0 -> 3328 bytes banks/warcraft.ad | Bin 0 -> 3622 bytes banks/wolfinstein.op2 | Bin 0 -> 11908 bytes banktest.c | 166 ++++++ dmx.h | 925 +++++++++++++++++++++++++++++++ midi.h | 574 +++++++++++++++++++ mididump.c | 55 ++ opl3.h | 1042 +++++++++++++++++++++++++++++++++++ oplplayer.c | 966 ++++++++++++++++++++++++++++++++ strings.h | 119 ++++ wavfile.h | 89 +++ 49 files changed, 5290 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 alsasnd.h create mode 100644 argparse.h create mode 100644 audio.h create mode 100644 bank.h create mode 100644 bankconv.c create mode 100644 bankdump.c create mode 100644 banks/2x2.op3 create mode 100644 banks/cyberpuck.tmb create mode 100644 banks/d3dtimbr.tmb create mode 100644 banks/d3dtimbrmod.tmb create mode 100644 banks/default.tmb create mode 100644 banks/dmx_dmx.op2 create mode 100644 banks/dmx_doom1.op2 create mode 100644 banks/dmx_doom2.op2 create mode 100644 banks/dmx_raptor.op2 create mode 100644 banks/dmx_strife.op2 create mode 100644 banks/drumopl.ibk create mode 100644 banks/drumopl.tmb create mode 100644 banks/earthsieg.ad create mode 100644 banks/fat2.op3 create mode 100644 banks/fat4.op3 create mode 100644 banks/file49.opl create mode 100644 banks/file53.opl create mode 100644 banks/gmoconel.tmb create mode 100644 banks/gmopl.ibk create mode 100644 banks/gmoplmod.tmb create mode 100644 banks/hmi-144.skb create mode 100644 banks/insmaker_standard.bnk create mode 100644 banks/jv_2op.op3 create mode 100644 banks/mt32.ibk create mode 100644 banks/nemesis.opl create mode 100644 banks/std-o3.skb create mode 100644 banks/std-sb.skb create mode 100644 banks/swtimbr.tmb create mode 100644 banks/tim.tim create mode 100644 banks/wallace.op3 create mode 100644 banks/wallence.tmb create mode 100644 banks/warcraft.ad create mode 100644 banks/wolfinstein.op2 create mode 100644 banktest.c create mode 100644 dmx.h create mode 100644 midi.h create mode 100644 mididump.c create mode 100644 opl3.h create mode 100644 oplplayer.c create mode 100644 strings.h create mode 100644 wavfile.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7b0c4c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/bankconv +/bankdump +/banktest +/mididump +/oplplayer diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0524e34 --- /dev/null +++ b/Makefile @@ -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 diff --git a/alsasnd.h b/alsasnd.h new file mode 100644 index 0000000..d12e227 --- /dev/null +++ b/alsasnd.h @@ -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 diff --git a/argparse.h b/argparse.h new file mode 100644 index 0000000..b66b078 --- /dev/null +++ b/argparse.h @@ -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 +#include + +#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 +#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 diff --git a/audio.h b/audio.h new file mode 100644 index 0000000..3ddf12f --- /dev/null +++ b/audio.h @@ -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); + } +} diff --git a/bank.h b/bank.h new file mode 100644 index 0000000..aa7d81e --- /dev/null +++ b/bank.h @@ -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]); + } +} \ No newline at end of file diff --git a/bankconv.c b/bankconv.c new file mode 100644 index 0000000..240ee29 --- /dev/null +++ b/bankconv.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/bankdump.c b/bankdump.c new file mode 100644 index 0000000..3bbbcd5 --- /dev/null +++ b/bankdump.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/banks/2x2.op3 b/banks/2x2.op3 new file mode 100644 index 0000000000000000000000000000000000000000..7d5c2e5674c9221df685bb0a34b9240e47ff7303 GIT binary patch literal 4384 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 BO-ld( literal 0 HcmV?d00001 diff --git a/banks/cyberpuck.tmb b/banks/cyberpuck.tmb new file mode 100644 index 0000000000000000000000000000000000000000..4ebadab218d23b1dbe7a321fa279007057f654ec GIT binary patch literal 3328 zcmeH}=}%Nw6u{4YZ{9F01H7R^aOJU7yF_`2jY}J60FlP%Ff3BF#ThEnCe}JELb~Wh zJGBkfY6TTPsGzoqX>4&zmk)(T)bx|u8k@#8z=zhPi}N1yW(KWH&%HyH`4dcgGau%c zv)*&hx%Vlm6ZGp9O#o&9LRN_S_42J8(&S5$t{DaaT&lWB=mXoC)NRcESRk(fwW9Hn;( zbrp#XJcHrkt6&5^O97GukR~5M*NVs`vb-CCAmED0S~1&wT0kw~5<+Cqjalz;8?~Se zFzeaCsuKfOF51MVz&A(v<^&*Lx$t_Ke4rrKMo#FA*?v(w|YG_^lXSgjr3I_=)NO6)g0xtJDnIGm}P z-qcLk$qfvoz`i=OLGHJ2zgLwdGqbNsd z<*Bt(ISh|XN$|FxJ~AQML%-hp1ct|o4iBeKNRWp{a==SZAK_g^Y)5HK?gEweq0a@EoXVD11p| z+t#$hG|7GWB(R|sWT*5XYZI|Z;T>_&Id!{5Q*f&j2Lp3M2PSl24tfkM!_&s`9`dJ)eM7Dm0mFcch?Yo1 zz{bapiJ3V~E2Y`0JeRP7NTqgqR{uVi=SE~J)yg9g9tUI_P33?YoHKUO6U&4mF3~iH zL&n8z&V?pPJnky7trwQ_yDG+pjo11}3TBB*QC7qD1rCK*)T@GB(+G||CQ2-Jgvv^; zrl#+rJ#Wj!ygDS)$p$@oJrT>lR{%DWK)R_Bte&vRE|#zlk_an;S~ndY%mVFN32nZj zDjqb>Qbx7E8!;lT1#$_5jYtcZ2+$87MzH-^S1~77t{gnbuZ@DJ+IFr6uyiSa-_ISy zNC=^H{d&Cl2zqxFfwXa>WM(G1-!KUoU9Uhz-=HfNHS%I*nN42+gjU-PbR-iiJZ z=%0zTCIol7Z8Jb*DZ7=?#{3Szu|a<{>zH)@2UhJ#=ko}G6>gur!v-)9x4__bn1c<> z=0EN(#Ai0y4)d@B{CABwy#42}!AEHQ6D9fV{sw=@AL4-j#b7ANx_2&=VrTisp#k58 z*HOSFmrBU=ZIQQV7LNa^G(vLb7|Z@kPnazmixc%3gRcqnkX0?svu8<{r*kk zFJKK6$dcu>*0xX>1nVlu$uT9gz8eaPLegRm8>3JNuPXh}c5`eDztU6=4L3tkNGtFp zg^g>Nfw>ye3N~57!D~T;lF9HK5Rz!Ct-W=Nkc8UFuok4pTvQLZ-@vzV1Nb@-bT@t) QB0NH~@Muo(e=_TT04zBIV*mgE literal 0 HcmV?d00001 diff --git a/banks/d3dtimbr.tmb b/banks/d3dtimbr.tmb new file mode 100644 index 0000000000000000000000000000000000000000..bc65895a2289a420c5c969e821d8196293841e0d GIT binary patch literal 3328 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 JHMckb{tZ+5(rf?# literal 0 HcmV?d00001 diff --git a/banks/default.tmb b/banks/default.tmb new file mode 100644 index 0000000000000000000000000000000000000000..748a3ddfa8e6dee1ed7005dc26476ddd20c43148 GIT binary patch literal 3328 zcmeH}{cls(8OOipUSHb;+bFAxTd&PhS2(tgNZy8X5dCP7uH?_H~^6^oWYrB*7o&w0*sp69vdyxW~l*R}#)z~GM~osP6jK_fxbQHD)F(wWsW0XM+-7Nk;- zErwW(_k9n1x#b^Bu@9vK#(Ht(%H^#<9^h+#3U(&)7rDoy4_J1#QU?^EjB&KsE|-Kr z`wnE%H*IMa;33=@i+(Jjg!RC-FWm&lvbKGj_5RSo|B_!oZEv*p{VA$3p~Q?{*r{TV z^cJru#1K4!^TX@$+VKDZnT3Y?Fc1wD%RT0Bku?x?xtKU;!Egi4)Ge9Kglm%* zp~rN4n79H@79EQb$Q&p&=dDbp|GG3&^$0Slv$rIK_%leQSLI~XDnDx3nP7&Ays5k% zckRe3sf2g~P91*eAtn+Z;u~=O zW;EhmbB<2}4{fqy@j_B4qp6WFQhRx+1clAEvK@&knM8%nwX&JSxP$_|2HCWBRhsd^ zb8I-i(97TllKcz~tWiBo*d@(A{IjW0llS3ffFMXfuVBQt9~wViLLkjfVqa|KZV9pY z7DQgs9J7-(K;+^tYMs3CUe?iFHbXk#ykAS)1G68k2TN8suK7A#*irT{V{{+R&%r#f zhn||i3?6qU_Lb*LZ%KY+Ry-CB$op`<36V?HbEIOJ-RO%eGo8GdFB7OUrl1FX2L+Hr zLn`_CP9ag7z1Cft=a})6_;pvAk1?tcqp}?dQ`7;iN>a|nd6_KOk&$wA?0jany!OaQ z9Xh{*BtMTtqTGqv;9WSpbL*5@awB%_Y*{4t>`1_TtPk|i?gLGwcR#$L^7 z!1+I)jjy`tX2Kok-BxP+$Mht$tu1*Gn}6R=FDG(6Ty9-nsH;*j%`)^vmzB!*iN&8r zWDg@nI2arNU%Req>PhAxX|&bL*tmI2)z>avC>(-U z&5g*kzYvl$MDcW}@1-}}5S#QOpEWe7k4Xo*?uMPNxycloJAFMp_}~kgd`!$C)`W>G zEl&0FU?cYLo$rIJ1KvU1xRg4JQZ=8u+CS;mW-8)PP2FRqu8xh#2s}y&zRWG6RFRp+ zeuv#Rs>mC;4iuioL~b#eky3yo>g5TK6SZDYqvkU8sv^c9D|i{Hj9XDeH~Rkpl$T=? zDuG+II&&=x4nq;jJJ52=XoFCMnO#ol6!76Er@|{*hNzinRTV$w{o}Yvg3%taGWP8^r0W8GJ#v{gw*qen58(eI2jYRhnb69ps+LiWDKtYoRFkL> z!gq@E6|`+16}77xzCtROuf6q9@*X846*+rJEtu;k(-q^s%F_EP%cnb7Io+&bx>79) zHZS{11&s;+M)*B>Pmg9t$5iHyK+)8^KM%O>kg@$S;dO@u>msz1v^+ueE zca+Vr3NVZ2Xt4?(DIRZgZhY-1^+I6!Ih#asFme6w1AwO*j5T9o;^<$g`)WvuPzRs* z8^LOVZ3LaK6D*Yz=I%tETZR7-x{pQwDULlyP!H8-d2@E>>nifA2|6f=5>`=rhrO+B zO#$k;D)`kfuAggsmLQCeu6>F>oX|AsK)~frmy6)1u;~Apz1v-;st(M^)@h}R>aT#i zWDRgimeffj3ASxJ&)5lG)HqM=)a|B fJ-Mf-ovNw;*L4j2Yh6&mtf~jo<-vsg->=NKHxJ|2 literal 0 HcmV?d00001 diff --git a/banks/dmx_dmx.op2 b/banks/dmx_dmx.op2 new file mode 100644 index 0000000000000000000000000000000000000000..111091d74eaca6e2b20d7c7bb4dda994d8a98aa7 GIT binary patch literal 11908 zcmd5=U2q%Mbw0ZQ0T6^h@JC|hH1?$|%QQ2Z0rZXP%?2Gclts)6DN~)I+Q()Gkpo2Hch5amYTXVojy?hkv(l^0#Fnw zTG6oQ>;j;Hy%*w4U%C$f4$gPa|2^m4eSB>E**|>dna2Svh?ee1@$O)>ye5J3lIX@F0Z4aLY0}kOJ`M2$ zzSzx=Xx-WtK)pS-uJUb&clegdA3>Z@ovW82?rDvb|H`3ln^z^-^82FiH1TV0aZ0kn zp~(FA#lH8gPNU6%*y@L*$!!nm>YDEW2)A~B?T8rj=K26YJIvkuCSmBQ_lx9|%69-{ zX|)%(C$MxF1qU?y_$mw4H7QkbH$awm9U~!bU0(PGyuaE5)}xTz@{^pkt(8?2_$VxF z`K!9I#-f=0&eqsux1$Sx$Vf*YoY=;I)dTTX-_@kR9xr?i*z3C?9tg$;4mf#dFCrZb z#!gmf%EeL=lNk!UIf(9&)%A1;yl@p*sYS9n1F?w~Cl?M4HJaqG@?EfY;nzAzp~oG= z=&cVW*cFVfe&8T@7$`p`LvIk)UKV2_|8+>R{Bq>x$Ku+EuB>4UA-alYIg;G=uoBir zD7f!~C$}*+xKyRluZ1Th%jaWn9ux1K$l`b8xCc6JeJr+_{S525FVuVU4KXHK+5&4& zFjl@N#;}!cu-XIBTklnEbo&)af&`BQB3A{uuzO1hgVzbM@(Af#+lt)%7<02LvR;cR z^~{gJiUeYmTE`-{uISob34+1s9igsE5WRL%g5AN`%^pWeBTe;_93PgT$G4~;RHUnv z#0}6cUmT#;U_Wkqmfr1_q1~rU+55u12T51ydWUQUeQaSje=mFCFQD{~*jHP9%C#2i zwQF0sw`@B3triJsdN4lsHf5_P%r4z_{^8Ygtb7z;Kd?J*i81iQUjTT6k(&aH7hZtm zvR8_Cc&|U_b!=XkL%P!!tI}S03D_JO*Wq0}irba6EfC%5>#fpW_#;@SM{dNr>-(WX?K)EJvLE zMHtJvEm^xlbEnt&wQBdw+>ZhFg_-iIvz!tA_%`i@YrvBAHb-+ zZ^-doA$IjUl^%|?6$-KW3Qd^kRm4O>QKj;qP2--d(q!{OvIYI}U7y&)w^=4k&L?5b zFHH$Bww%}6Xof81+xT*9%v_sfwFH+^pA%zpIUG0<-|N30aN@(u7yE@D&rQKt-Y=FD zo!bjmM=+**PAn(-J)F6_0!u0UqEzy9m9l?{%y8~#zk}dm7RFx&t1FOvu3s#Nso3O# zZ3~4b#d4PRKPg$>)}`S}KZKqVZpJV?Q7Ku9;jOb+`3J+vv&Y49%4d&wpf7wY{Rf2M z_qumu?Xc``%N=SF%UL?zLYU~_Q+y1aY4@G}J?teAO+Mk|V&6Q{yKqEQXfOORuy_7i zihBaFW!1qXum6n{mvDa6bX!n5dzfrqN&CgRR?d1oxK~!vryR`6hC}~Zg{Jr{zR)Jc zWju#}fnOV5xB^M*Ie^~4%BtNrs7KlK!s`{GH)6c-b+~nU znDV?ZUzy8`75ZvzPGhZhpqx-(#me*LP`;Qsg1)w0ySLY|(5(v(d@sq;a%b?J;ZE{> zZEN4K2ax{Y=uwKhlMC;9frf|t-Tfrz6VAQq>oB)`6Ra>qZ`>pdz}&kIU9QkxD8RdO zcW@xV@=x)Lo*KQn9mAEb2|PzZ`EZwPiT2mZ@IT;8fzqLQejSqUk#n*HzY&xNO%9Ad7!&#a{{XZ>HWON66rd0tCI7C3$qWa&?@&(p ze%<|V+?U<{_2CT3;bThwaS=B};9r({q-7okGu2Ugw1Y73_jVA*y_XR)@T&yKmM{FP zi0nZOJ#N23C3}8)*KZTwZ4b&h0Gns+BE5f-F!a>RLCjwGbOitR!KTj!@GuGaK^iN~ z=h+a{2FhXW8+Zq43B%?He_tTx>02+~$3(Snz3~G+22X4IrC%3qL+(}10MbSg0~LZ_ zAEO-i1IlTa@9Oy5@7eewzyBcSTJCO66JHR~c{V5hjBn+Pn34Lcfk>h#L zDO(VfAGF|?{kZBU2_Mo$C%pXcM}(MB@B6p$+#8mDd_oxKG5OLgzSC=$UP}vY9`?SH zlPxLi|L5-tF(ZJriuEk5zvfuXsriS-hx)3t`S)02Z9ng(5b3gmQ*bG}RT|0p^tSfRY(#mW$CHV9tde zvIi+&@cer+go8iX+7jj~2Ae;Zz#p{!pJ1NBnTuyw@yx}ZaAF6T-+36uuII1(rvz>P z=lDGTv(+TtPXoVPyFx*y$F(n>e`z&I@6}&feVdONNEyX^K})d_Q^`)VaZSk@jx6VM z(A3hI0;|Cq0mHhQDwyK+qek|^<%0$Mbjv0U6B%P#&9iYutJhn#VJKsm#k|U~%t4GQ zW-hO#5H($0QujTiE0?saYM&zO7!_hj)zy4K;j!%h`q_wXq%Nr0d`?qo)HOW)>{Jo; zvq7Uakn0dMg~MN(>oJyO&Pipl!281QWEMFi@v?vDyTUr<$j zo4;NE75-P1D_6GJm>(A+rK?I!gNr*;EU42@K~==qCDoiZG5lHPSm=bc_IX0f7Ywt2 z0pjzqejUTAfy&pG+COUk4J!Etmd&r(M|{?IUdyIcvuYe78u&Rab>TByfRm_*?;JG(*=!#f9pKFRCrF2K$Fz*DxDQ z95S*6Q<>ri7qOG3I%DVtOJEbtrrjAB*REW_>Mn>7W4ODpftiZVs1-J7$5T3+)Mj!@ zR&#NjKODVmIGa~zrgYVngGPR15S{o|taq)3O8l$*#?=_VjG>uqK*_jWw+%dFXen$x zhKTRzw^3XVYw=;D8gB?pn#Gx%csyW!jF~B6_s0Acn>25<=oD>dCf0)cBa8iU)G+x) zIB3$oOW=^@_rOYh+bHh-+1?-S;G=yBBZq?@t4ra}uK}G(Pr$_WlWNv5n*auJzTxy& zMUb&6LogYgbPi|Jx|SCQh}w!FoDNuDI2xPH|8WgRLz5kRQpW^3rf@$eRGKWJ=?XBe zWLYyAgGK@SBOBC>S+0ESj~V4cF{7k1+%0@Ig9AVpTR4^?|HU2bSyh>4G1iyASX4~4 zu65h?*!)AtS7r-_QOGnRZ2n=^mr`_HGjc8oHvb6nGurv{b(6z!*XAE3zJhg_ZWIqT z{}}7LM18=;1&4o}^&Q12z_Za_|3%hE9heh% z&d@LCX{$AYh;O@(ZLX0=GKi1vPZ_h=Cek`uS&t${px3cif#bT3+QhZxr|?qtI+Qd`Z!pp8p4xG+vx?!VP{>o1Sj6 z{Q>REDjLr-=f6oUdx827_ws;{fwYls=0Jju!|Vi3QT_lwYZ%j0bb}I@FsW!440}Bv za7xvUX}n)e8Z%sgGyh0Gmkkpt9-#9o8@i0QEYAI?ayq7CD*Lj5nQRaLnKz_>b1M{7dymILYMYj*4(%i+yU zyqwk_Jf&fY24idK|^4 g3ERp4P*M1T%FcfiKHVSj`iQS()7%8x>znZZ7q;R^BLDyZ literal 0 HcmV?d00001 diff --git a/banks/dmx_doom1.op2 b/banks/dmx_doom1.op2 new file mode 100644 index 0000000000000000000000000000000000000000..f4182961d24ed3524f9f0e8d8f37194b80151458 GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/banks/dmx_strife.op2 b/banks/dmx_strife.op2 new file mode 100644 index 0000000000000000000000000000000000000000..4c13b7fd59282b9d374f08b4c83fac517d4208ae GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/banks/drumopl.ibk b/banks/drumopl.ibk new file mode 100644 index 0000000000000000000000000000000000000000..fb2d7ade4d6c5020413ea4b27fc19ba73a0da14a GIT binary patch literal 3204 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 literal 0 HcmV?d00001 diff --git a/banks/fat2.op3 b/banks/fat2.op3 new file mode 100644 index 0000000000000000000000000000000000000000..729f6fc630b1a581a17853b0ebf2085459ac3833 GIT binary patch literal 4384 zcmai%ZERE58OQ(UT>JWByi1&r7Z`1FV?d0x8V;nWEE9GhuZ5-=P5{xWyJ;Z$NJs>dG7OeUfdpkruXpvzJt#mJn~GWq<_IlHN0sPJ#QaRFcS1X+$#M3>h)2c%1J^i-;0PNpV#La)L12k@8bMBDjme zd0IT0_WnK%U&6YSE~bFe7SA$Q!g^QoGdh}E4jx3(L-n!c*Y<#Kz%`BXA8dc83a%-f zl6YWjBESUiT2A$5*@WgrVlQQRT zq{o>rY^ASL`${DJI`jFFyhC~5$>bKmKuW5k@^Y@0sfLx7dCPRf(dWdh;t^;c2{X-b zA74>SMNhY%TOl~cCBDS2>40|J`dG4W+&`{@PvYv^)K0K8xei`Ymnw;$PX2Y4|4`!l zI@iHhhiEJ1Y{-5cnh{EUPPs8OSs}P)OFTG~S=jz!{*-@K=EU2*mf8l%%)M z9(@=v5$&aRqPrt*_+ofY(w{}-DQIC_ZlZc)bf6MGgAFf{_;}pm?)kj0-0}0J%~T)U zcCnIi13eGWc#R9@Aqf7Wm+H+^@oH$b>d|GC+b3dOB5%i{Qr{XoBMqY8@b)UI4-SuU zrkRK>mH3%4WtK~E1xExI`F}~~XU3K}JSO>LJn~M3)1SkV|G~3Asub%TLp~aBFcDwD zPzO_5zjbFX7;qVgRZ>51Ru4Q0+{Ay+wz=mwn+F%<_8TH^BA;>DpP}P>oPB1VogD|d z0UP_J_0{uHU$euz9-{vAytdner8c^y{j+DRf0p-2ymoIv;o`joId6JlnbZHyXYr_Q zKG7xow4Rph+jOAR@#j@3E@NnPJwq)<))!j=CNF;O1~)@TB!6mF{zJU*dl*(ZnP3bj zzX7jdWu&_IU*0tiZsW(&dNHf7_~D(h)+odb%*v5XPXBjF`$_bPhPNrV6{E~h16@L(_ZQUp3;U_!N z&oY;ZM1a1R5BzQy;89XJng_q{lAoc>n!HW*el-_nF0l{)g$6t}@`pNTBlw$`RXkiB zGu;qt^h2q>M%xQDP-}5SUcUpMZFKGr-BNvPT%WqRY9xlGea3w#c^j%?U6uUwl>dD# zyc~JSj{D8tdU0Qc=w33aUY!x=bt&?aostt*JW!#`Ncr@K^DW#}qNF9x&LmZ?mmp2k z10B_X{wq`$=mgcuuZFG+b|0q`bmWEphI^BX>UHGi>~P9IIk))|@M)-9Iv466|C!0X z%Qfc|D;{%pZlEJe?W{$CLBEC43R*`X{iO&3xnC1M10~z@{ao`SP0#(s_$Ka|E zg<@~80r;1j3;Q#4f8GLK5G#(MK+pGvMfBei9{M2esyXVKo?D&t1^n-if=UE|EuP`|Tnme|Gts@|(FMkpU~m-7JY~yTcWf%6#jbejaaUdwTBx9C3O+HiVnkxe+`YQh}(bq4$?*0uSkRDn9 literal 0 HcmV?d00001 diff --git a/banks/fat4.op3 b/banks/fat4.op3 new file mode 100644 index 0000000000000000000000000000000000000000..daf307e6a6cd24d1ae84e2e797b608bb61723fb8 GIT binary patch literal 4384 zcmaKvdr;KZ701ur7Yj(2-{OMe1AYs>Fm3npSU2ltU5J3%bc_lZ$2htwqM0;Af;crv zIlq0dXkv(>qKU>V_-fLQ#k~7Z6rv$bTc^=FPNo@S1f5Ruhr6)LO04wUpTsn6tbd&O z?q?3?cka38o_k%i<*B+SYnwJbv#I{6+(*5Q>o(?AY9D1%3x2zt#h;ECwyC0>;$=D?5en025ner0BDfl7a((m$x)?^^N9kbq0CGu429upVH_*m#koctTn945`=P%#n=qNrM zorC$42bTmWPP8MyAbm~rQ7q=GuHHmj=P3`x$H+f}3^ddJdY$6H&~;1-$F3tUg~0)e zlT{VHM)@0hzQT#KqrJ#?B}G3IT&~e!A%nOMvu7OBA%_%_45qze{53j*E3aKg%i2MT zqW~?P^6VO}Mtg2r@G?%wBgthL5P7C7yLAbj0UW-BmR<2<$SI`oFWqtREiA^2p0}}l z#RVV5!LDn>S>VPnKBQgPSgkO8OHcmTG)(;UU}pi&vA%Y+06YC;_{_cG0+sI~E~oZ6 zF;_X~pzmUQIq_>X#|jjO*MBkvukIql*9uAbul|DuT{h3t6uRQHhV~2B=!whieHL_a zs3)Fzo=jn=S@68yHDrfuvcI7K^Q>#L13V0C#doKLM-AxA`}wFI!#Sh+fX#~a$-;l) z+HA!oqwt?9{H@|oss0BC8_`)R_EB^6LZgrSJQu|N7O#xPeJ=i5bQtsABt!ccxk22z zY;c6)Z-|bde7t7_Z8G$TI%pmlv7&=@3=X31P|u(u`bTgn^zljJ5KADN(7PYG26{BG(egz$}Cwc{Sskc5C`#yX{@a@Fy|I6jc@ji62oCqax znGD5`ihKlD#<;nv>>lTqFESK|3^miyymzNo;o28!TSPut_u+fL$Ozu`&rtpKFK!V2 zPw)Qo!#?-x+QuxOy!XQ-(dYF^3IlIPk0odii~d=c@lG4^qkG6wosS0t=r$kmHKJw) z8LCgPQ%N$g?e}WMch_h6yt`|&6#8y$mdZ=>eC{=D zewHu1znRu0!#t6XoPEZOE-x&m{%j{noFwrHXRKq~no5#eC#&3`eLaTm{=BXd3@>S> zb$Nj-^_xDhoOtH(Mhw5)_@LmM>xie+9;iY`(wdHS7+$ejr`WtuZy*j(yU{ln;A}<< zK%b$712iW2>ASmwS?H2kTQbU)Ko-SGJ!usA>`}SC`5sSGdG4MjI*(rP&B5*q-s#9U zBU#!n%jOx1%j@5@5WCZ#pF!&~Zkr|I9A1}#ZW)?WG2B^8=biFT6+E+H8huyaoPzR{ zZ6)NNyv?cdo)UC>lh3DMct>Nos86%VC)}|#TYR@|63XE%*@Ba#@9t|i`5a+rFrmCJ zNcG_mm_c>sFSTnG!ILpReAXcNdcnJ2YBwm(&Cp;#c~#Jeyc+^DMV$6_lj3R+ajf1T z`4i71?(O<*0Xp)Qp0Hzh&l{O^-mxu(xGZm-g@r}c+omhw_HD$iux+B?v!6{;oYv*d zfPBf8iD+v8ui%9*zbex?f`%+qC6_Zu(!7GtC3IbzeQYCM%?YkXeidr7|BF|&e$S6| zelm;dhV@45eB)0RQ3sOW#mSpWapV_U9}`@S6HaS7hx4Yd2(_TKW_8~7RtXONpa?jI!uQv3|;-Rn;dApyw@)BY!ZDa zmyDXQSbmSJ`~exN�+|xSf7Dd= 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 literal 0 HcmV?d00001 diff --git a/banks/file53.opl b/banks/file53.opl new file mode 100644 index 0000000000000000000000000000000000000000..f0c24fab0744461c0594d673a7662aa36913870a GIT binary patch literal 3662 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 GbAJH?H~iE9 literal 0 HcmV?d00001 diff --git a/banks/gmopl.ibk b/banks/gmopl.ibk new file mode 100644 index 0000000000000000000000000000000000000000..e177514773399b4b04a300309175735a0725d0b5 GIT binary patch literal 3204 zcmZ8jZERE58GepUY_uW3goJ>$B{$)#`?KB@M%PU($M&^-$+fS!_xd9#Rd>UnQh$s& z5UN$v>J-9S*cw9kn&_%IlnVMUQ~SRKsr^xDTiJ)ENmXTStdllvwVjv@O=9mk$Jg3+ zq}cC!-tXsq-xHhhv2BdaMVif(Hn0}rFgAw&lpo z|HI5AS~4G{Kfp%N{;W;$AC(tpfc51;Mw-&;!J?X&&FEB%qB1zaiY=nd@srQYh5!kRTqdIPpEWpFk1 zNI&bu)BIk{`;iFi)T`BdU#$=y;1}YrS6i(s9|Zd0gC1IqtM>waM0=L-e6ox0i1wpu zyS3{+>);>O()d@~d#V#2hqVJe^QY5WSf`(d{R6mkaiGgT@@B0%^l^s#seWsVU%|yY zk+*{S^-3=q_h0A<`ctX=1dYaL(*dtfJq^FIt1rNl`RDf6nwNhc`17^1YZq(%U0&DE zVYzXyYhM}OfM&fE4eHmmK73N#yfesGulM7VqrHTe%CDlE_}K67^0mX+YGrD359{=+ zQQwcovF&{UA7b08E3+r}2K^XjU#>2HJ>j5!%^$$~_71{F%@g5qYWZw0$Dg0{wnNw4h67^plP0PZ2YhQmL*ZnyxOhuys-n#$V|MBx!cv;BR zzAE{K4Gd36hrTw3w<-Uwy>=cyJl7v(j8dYbkIrD)-@27`_H&fv*QRz41p8F44`TMn zp)FFGKI-)pUimM+roE2o=|RFn)u(IcaQaAjTUWnk;pma#&S3v1YaiicAw_tp_h7R2 zQN%577ef1k~f48%H+RHno)o^CzXkJ!>F&N&rk;WKe5gBb{^AvA@x{b8$+K;QTXLQO}@o)MTS~JH-Sm%8XMQ6izn{FFS#nT^K zSFJ5Jy8dFZ2-;2l7Zy_Q@51~%e*3r47?_&xHO$6?+eO0fTe2W+*u)Qx?TR9pKW&!Y zmp$|zE4+uaH8X#li$+78e0svqR~vWk{VB*V8tTK}S9ems75+W!XL#<@z2yIK>3?9; zWjt8jeJ_x2#F_BT2T`hzMvhKwBM+~((&132KDO~SG+L2R2v17+`uBjrK|DlQ@sV)# zmDS?*(+G9szedmM9{Oj~kqiCyAT_7M|L(!1{rig7_ep2;@$Nf^*8XDIzY*%x_m9s> z-)1)uR`1!)KLZapv3@Y+U)Z(UpMQXmlJi75Z}OZCo>1SzhSj;r*ts!}{$N;AqzA+( zGTh0#iA)j+ix*8v7?$CB97tPsVoXHI7F;tCx63G+al^$>ekjWwGaDzJ^0;N^$vwP6 z+=v^N1sQ-S#JOWTX`VygNhrc0e?-xZnKvZi7)DYOJV83i97I0ZoSES8crI;==s5~O z0#IFc!S#qZz$Yfso&%~PT`)cF2qIhX48SfLPSP=p$P~x3hQ}=k4dGOp$GPj0vXPKP z-b{>1BIOt!745>ccwXL=oTU7QJDwAS4)^=tjr+%sj}i#BWr~CZK@b25s#~U-j9b#` zmg_lYE=^W>b7DfMNSwuH7d=pdBz2LaM!j4 z5y9P}T_&lQf$bzR8PgNig>2sNba;-Pjq4PPdchwPL0vl~rARp$+sTnE?ocrzn5aw8 z>W1`4Iq!(^xy3Edc6pin7&+S!6t&WnH+I}6km(q?v?W}U$aQVeADSPk+3=`j>ZQOV zWyzKRjv<nW)15#Shl$Ib9Do+;o~DPy|g3MJc4$`F>plcE|3LjH>vgse*t z@<*W{=)okb!o-AS=!R65D&^aw7=oMi6TIiBOwK zgnBPhWgS~-eZQ6}s~;Rlm6aPJ)K63*lp7+H8zMRjo+>|%Av$p7#8S?2o;x7NhCs-g zu93r-?a2#_aZBx2oYM;DDf^U}ObU2lcp2IcA~HOelpDv>w%QceGYl(9qb@Lv)ArI5 znMoQCT45m&gcz+HZ45P~m>AE-sc{)v0}4))U~n2N(lNM8%{5@=#H_fqR%UL@7#Bee U%S_~HXcLZIl6@jgv6H}m0PjGC&;S4c literal 0 HcmV?d00001 diff --git a/banks/gmoplmod.tmb b/banks/gmoplmod.tmb new file mode 100644 index 0000000000000000000000000000000000000000..d23ee4e7a60fb72c13aaa41b5f5a517b6e414496 GIT binary patch literal 3328 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 literal 0 HcmV?d00001 diff --git a/banks/insmaker_standard.bnk b/banks/insmaker_standard.bnk new file mode 100644 index 0000000000000000000000000000000000000000..c0ec9d123ef088cb299a7363fef0b43ba8224068 GIT binary patch literal 6748 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 Mx#a)f|7?MO0kZ?L$p8QV literal 0 HcmV?d00001 diff --git a/banks/jv_2op.op3 b/banks/jv_2op.op3 new file mode 100644 index 0000000000000000000000000000000000000000..b94b3a12ec3b9482379ef0d274d787ce61cfcbaf GIT binary patch literal 6184 zcmeH}UrbwN7{7C6nX75|P_oBUMdVYEG zd~cucJ@5A&86E6D)q7@Oc;KzUs$=TN@sm}r44mrS$u>S3(>8uVv(XMdNiaq-K-uK) zo7|qoj~dF90MQZExwKQ){2#KXmSh5HOxH)#u@}X!ZP6-bprLT zw}T7(#q1p`_9<&m>EH)PavAe44yV$fTxY2?=CW8t94j+|) zUv(Pgm>VXq6Dfz?$v^nIPGbz~@6SFi1b@P@M)Hx}QI)TocUi@HS$c3D0y10r8gtm_ z|K(cDrIyhTL4mv!$;UHsZwEAnHFX!(heNBi;17#N9CJ@aE8kyL=&%u9x;3A>-6nyO zh0Gqi59#$Ny!#;^hWYXk9#Z(ZlzHy)NS<#hBc`M=4j6U|IfZPO$-XbHDN zpjy0{kNsa}eQkWdSo{p;>h!y1EGV-J4$Ql!2iLi4kZ{=jLoWp7{AIGPdeQU|sE_=4 z`|HpsN59#a-JA3Ieo6QS1Rscz0=#a-J#+&CHIBK@2ru87yQhbqK(JUiM&@CL7uhJ6 zOns4sa+k>^=hpnce55QN<&N3^QQH6Bovpi|QFUG?@1yD-c@|#k(S}LR-h+?3m_MQQ zR^l8Iv2o7lV*P}BO`fg4ZXNFeUxHDep3J`I)BT#C&HoYh-_*Ll!t2Vh{w~-D?{$)# z4Yoe7WWo3y>({x}FWV@O@H)Y#X}x4VfHt!NfjZ$4$>D&>YlKJSocgVARD`Bq*>R$; zyD(A)`>)1QWw@Se6GkcTLksm8&R;kPFATAogIJ%ODqerT#I`=n6E<@`XU*Zr?LnSH zwKz%U!(jPq418X#4zN$r&)*At?m1iSn9CP*J1EtmDe`}Z?%b0g5RTSKSU-Mr>;MFA zS>j~f#H2Y-7@d0`*ROQglxt#=oJY{i=Z4Sg3=^Mk?O$5Jx55rt@VfK%V$M@6PWatV z%y|_%OL#ZC!slLpE;}DrLPHHpHDUg*eu8i*$?s7jOOg5|^Zw%s z(fg00^8ND4`IBT$X3nFYybiK2uC}2kmSFZAa2!qBpz$0_lXV^2H#qUBHO~>S(Q~9z zONXqAo27|<%H%GI-XC0?v!LsPEWR%tvMH_t%3o#t#SMg57GCh#X;s2Jy EKLL;8lmGw# literal 0 HcmV?d00001 diff --git a/banks/mt32.ibk b/banks/mt32.ibk new file mode 100644 index 0000000000000000000000000000000000000000..c5254b9dddbebff60501a0fd09580f0689d3586d GIT binary patch literal 3204 zcmZ`)U2GiH6+UCfHsmhBlZ_n*(z4?iwX{L<&YG%ys%Lk{yOW)nJDxl1pP(wF8;Iy&`|A z|5?)M4Db1WoXz^AbrpWB&ik_QcXIRMM;(!jUmNRhalQ}W7GXNtc#nL1v6J|}Y6ySA z?=jvc%@5CEy*?2?VO&A3CH#nSc?UmP`x|+++4JW{PQ{It;Unt2Ea$7kxrC2C9yi{Y z{GQ_deP`n4=3|c~Tsu?W&W|8(Z}Xe`>#?@dt7jfSzP-LNN>=MhsEvJoWB&ZrY^PW?xeCOpm0-+HLK1bw6ECk)3=m`K-O?Ps%^K?xj9y>8*sf zrc-~I(6fry)?bQOg0pw1`Ho?{Ih+2c@*m7zjQ6K4jF@{`_GUvCvs zne50-m9PJ+-+iL_d|UB>1JmT4S+=a|wG53s`uM>yb$)AojF>I1H~CpzE|XS^;o(v* z%eAK}%Pabb%76bVCGA%2x5}?+I%zgLc)rwYwiXj>oY#~0=e$!iFn^ohJxae{ig{1?TsKKb2w_gAVO=jTrz?(xQG{OHotUs3%SjYG))obbj* zI=;5_YvgkNFKm1v{^`<0!p+)+cy{T~Hvm%NelKq9k2jYl6TgA)>469K;p3A2tl)cz z;)Zc2`g=UHi<`B>@e@m9Ln_`}AB$(dec)?~XU8v*)A2#9-_^(XrQ6SL;}4HNMIQQY z&p(kxJ%)q0Jo(`PE3pzxK>A^7=a_{ys?mo;LtKyV3l;;scsr|3`Bqt@HtX({Ss5*?HxE z_5C;F7pMF3V!8PBVeLQg(090AWWCAz?!)t%KU4GHx^bbNTKelR0B#d03{Jr}PYe!e z-S?8&xW5j;X!5>?^}FlMD+4HQSx?gdKKS4{oX1{~kBcIkH`Zp9U)Rpy`_W%h{Q=3ZV_I_^*Y9!?HF1Dl%;?nmx>A{#+9`1D>C zZxv`Y@M#vhz*HaywrdxJs|!qJNnBGTieaMupFkntr0aJk-WD2e|hn_$JOqL+W0bY?s_+O|COkj(CK4dJbKw`-$%aj0? zia{V_5Xcw=Q!r!sk;N9pH9Q(vJ`~C{v|Jl9B^M)X1{p@HelZFwU_~BfK{%vUc_ZR2r0@#=)EKb1=EeAWLd1{-JlQ=PS@xqxREij6!&9Fk*En3HI;2F&EOTcF5 ztvMSzWl_lNLWF%ufOnPv$KV`4?i3;la}ISKA7>~5&Jf4q;*Q0jTq;IK{V25Z4EVq? zG!T==&>&N)I*}EkO#-xW0hA&%bpamALwGQkO&-VG1>Im_;lX(vYKFO1AS@D~WhS&E zmuE@9Ep9ck`3(i&Hw+xqgt)Z?CWJN@i5&t?i%WTFKIUI072n#9>0m>$H ze4JPpn7}75T()1rW#Z)XxJgQ-{5u*mCGtehJ?L4GU$v j>_otaSqwd#suLEY`G}9#KPKvUl$Nk|%0uu~ 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 a5_$X;y}uP3aEw**;qZFKIo+y~-TFUyiogK? literal 0 HcmV?d00001 diff --git a/banks/std-o3.skb b/banks/std-o3.skb new file mode 100644 index 0000000000000000000000000000000000000000..1daaa2c11ac0f3543f4fb3b648a255448e7e00b3 GIT binary patch literal 8101 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 literal 0 HcmV?d00001 diff --git a/banks/swtimbr.tmb b/banks/swtimbr.tmb new file mode 100644 index 0000000000000000000000000000000000000000..6c3e72cb7d6d75372ca0cc9eb8081afb32d1db55 GIT binary patch literal 3328 zcmeHITWnNS6kX@ud9}2i-nKIx1PZ5B8F3n!k?=5$&J-&(K1yjVM5Fd1fwc3=WnVkuCy@bcKnaVgxi9be zoPic&y@)5jKPA3V$GXF3zw-ai;1JA9`1s>}-^v8@wIRbwhEp;@x3(MU)Llma1@JPC zj*M-d1imdNkWBna=g{&IlYQU%P696*KrUnDnM!O^&z}!9N}+7aty`Z-**}qTR(9SC zb7-WD5(6tckHb7}xn+X6(dF20x!n+Pf#_W>bLnl<A%;L-v^2^6*p>0~OW2;Wq;9Ld9DSti0%_6^LWnUnXaW81>n)GT6zqkIUX zsl{Uu?x6fu#N%!Ks#=0jbVD=ZiMH8LicwWQFN}2jev1h`jQfnQ4<}y8*C)7>_FT8E z0Jcv6&OCT;>+RJkYK>bD@io_m=nqo&!B*4g={YydLax!y${xAxTIAmEqi|+buhBYrGVnE_abITAVjw^{$E{seljez~F8^M_z6v-;eQZre*qd0kH$~MZ8}x%Sjo)W$s$_KTg`N)N>l5A3i8DGQhSvPx5u2HPK;LWRKQHux9&; z&nkD}4=X_oP@|PIBbQvF%039AA8Q|5`=D&GYv@|$Z1YDj%H~jvo?OS3!%zrlTJiH{ z$0nLP1g4M=W`$)*W_n2tM((|bMjA1JSz(+oi4g!NfJ_T4MI>0a4q>Z;$(Mx^N|4N0 z`E!J({a+sYfPU($E`$Ht1FmU!#QJK#MXw=vP zp^^#$XI)F^zcJva*X}xcbYOs(>q2D+yEZ=2;-gL!jOzju3MqEA_`N+g8l4w2^Fx!u z7KO^32GNZJND5Yz)AO^sWbu97L1Mce3)k#fn3k~ zc|cZ;jukspxC4HL_GFKp*#mV-y#V0?;CVS2lB3ubP6pTeFEX*rHty&G|8%DpBb(&g z4ivy&G>%BMp#j!SP$(dGj15?Hv|;kk(srNLenR@q)u$fzC*@P%(qiDZnAw$Ii~|*BvDC zC_j^3<9zu`M0E3vP5XZEzwwr;Z0gqllMnS?efD)%dO9EL(9|B#u691q^41*sa%4fE z?_90Ux}kCvI&wr_L%TclpzW&QOPcl{zOSf5AHEG@9xs2oL#vSH6IrzEz3VhFq^u-q zLmxVYO83_t0ro-32>Q{!xCU5NSB}B8Ql+tEO)PZpPL&c#WFdtt;g(2-r!Cso*v=vJ zxQ3TX3Z7`ZLiG#mqZ&0CZ-wU)Z(EELR|c!u3n)e0G)E-bBQJ;I%}Fn6Ssnr;LxdE0 z+~G}Ym_4HTj(t7m0IOi!u!#${2)Zjbd_+^zP7 zdD=2n8PrzFU%+RapvB7-SpwM(ks|-vIV8|psA__){!mqBV}02S{k+vMtj+4bIwbqH85JC^JPUg2kSO93gpgQQau}%6%1O*HTQ_jvA*;B zVyy4;v@<;B$y|Br+0ivlwatmRt*s*YP&aK6;ruS=eV-@qJI^)m+B8qzEbI1Kf1MOQ zJ-+4{p*!RmdAy}_YlC=CC&11Ic^jSkdQ$z#NjGyma(JKFr^V#{O__5%PQdb9T~r_A z7;kwc@BeuBP>fTytf${N_n7C6!C#(p^-Xzk4pUu<{LABSSB9GGpNDrYlLy$yioY+s Mk3{%?WjoXS1TO1QMF0Q* literal 0 HcmV?d00001 diff --git a/banks/wallace.op3 b/banks/wallace.op3 new file mode 100644 index 0000000000000000000000000000000000000000..9642a8dfdb9a3c4b0305d84ee583955c9c5bd11c GIT binary patch literal 3112 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 K$N7hn^?w1_2nO;1 literal 0 HcmV?d00001 diff --git a/banks/warcraft.ad b/banks/warcraft.ad new file mode 100644 index 0000000000000000000000000000000000000000..b373a06bfa29b12b95ea3bde4cb4dcec686d2bb1 GIT binary patch literal 3622 zcmY+{dvH|M9S895y?6I!v#giwhCFx(xk#K!U>!@RChtY~m5TX?4dSvq6>`DDKP zJNKM(&$)Z{2Jkxo4GXD`71Y2w>cD2|#4c*0hbEzqy6_53#@p15kLW0zryg9PHz7tX zIGn&}_-G2Us29_y52Z8}b#x4t(KM`~>1d-Fc!FkPKOKvsbR1r$woTC%b zPbVQvv*h$(!=l-kKy#2!b1|DvMirfc0KEmxG!Gl-t=LBM@eI8UN9a_XqPOF9It?Gt zJMbATzy(@}?`RRer_3t}nD^NpMqKV#* z)pQkF=>rJSAokH_JV$?mm*{G|Ngu?Y=o)-Z*Wx052v_OD_<^p2#|``x0@}Bw&7XYju+`h{ElwIdvr5Cp+Cnz=@xuTAIA{g3fCy$ z7f7eukV^%MXpZpHT(OW&7Axo!v5wv%Hq$(@i{2`FXujy9w~1HiRPi>wU3^5RiSzUh zafKF$7%dbI51$s{qtiteogt>tVo^$GiaI(=ETgl<8ahX`(Gu|ly;JO`rQ#^POT0|) z7H`qH;w<%xbF@tK({d4}^8~+is1O#d6ccEb$fwm}Hmwm=v{nRYooJ@_hz)eU*hUwK zXXrw4gw~5wbdh+SHi!@CV(}SWA}-K?_>MM;?`e~8TYOqXCLJCoT4y*4mu7~q@tY6r z9+hw-)!s-kS=;X8r)JszYfA^N$)o2+%kCei)X-3o=JLA3*EQMaf%oEYYoJV?6|pG$ zLS#^;0m#MjJ1XZ7QYKsZ8_o+n6dHzJ3=4lCq)!iC_ z$Pvxs#lU5GUCye35_#O+sO^qrYo;BFBwAKq56D`qM76BzD$`uHbH~_3IyjaIJt`&l zX4S*BBlM}d&Ysv~Lhr--FUppMzJWZrdAFG=?H#zH(q@(JYsiDA2rV@-X&t>s;IjSU zuVk`2`qG(Py($%UM1OqU$Uf7)OqFwKnxgtlPoy=bOf9vaPS9M12zpeX3tD`do)cr~H#-QAYXgwDiH_pHv4BRnxn_EI?SXojY9W>e*L z!JZ{!;kF$@wG4K)Wx|~;k8(NBU9F88>?d2)uGk9#rCe*gI6?K&*{y4?AX-PMw6EJx zDOsg1CO5}RqV1McMjajl@CSLDs4Ks zqz8I0UPye4C!O-beL6JRki7&mx@O4zYfP7^Wo3Z9v|^pA*@x|UaP^_QSk`(c^lp;# zEUcFEw4d_wy&-6nPgcF`^Hp-?cdT;ATFJ-p81x40(q+v~vvcHiWvMcm_q83AXV%G9 znX(U@-pc$K)d)#x-%f#BM_jdZ zKEJk@za8M-aD>c5Lp*zYh^v+2C*T9S6?)o7A_lc*Uzh-95$? zO_O~kT7H81LD_N&hBa9I@ToHdh+LIzVt?icIVt`(V&G|WRo=tMRA?p*j*@p9li%Wk z5wk9_RG(DVN=MZ0fEh{F`6?Zgee$g##`DRC|5kH6?(2_gHLP)ce9jLmSerhg7GO=2 z>-k|hYcogG=8Q-+7OwJ^{WvC9sdPkbzU7LS!%C`)_Oq62VXy$!BF8bg54o1Rzuz#E zRt{d`6SC6MdmOqO?%nbiwq~JP>xnpKaP%eK%J?kZmEwxXI|?LLcM*=seaPjD1T;?y zhR3kBV8nQ(=BPmrOdW%AmC7^>{Q*fj&K_pEQ)?L74U?a}!8Bb94DFRI<8LzEt}P$h zqtfe4)f%q5rcrPThCNtgyMUQ(H>V!wk$Swssxq%mVaYCKQ%UG@*b)! z|JhnTS@_k%On3eLi{Cg6>kRhFJ7PM;wL617@@btK;*J@X7i%*(PF@IuPTle*SEVym znsX6_mWFHcdt0I7`>1C6oY&+V%iKiGm#Ecg%hq0&|CY7OGpn?$GG4>iK3BUOZ;{)7 z;i}=9xNuk|zk91ni-+YIQe*kI)bX$D@$sBz?TtP1H_qW6RXLs4iww)*KX;NXD)hOC c7_J+0DbVVeLVa>P(FMA1Hrq(c>n+a literal 0 HcmV?d00001 diff --git a/banks/wolfinstein.op2 b/banks/wolfinstein.op2 new file mode 100644 index 0000000000000000000000000000000000000000..ddf4cde84d5322c1203cf821ca43a17aa3258f12 GIT binary patch literal 11908 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 jHdh +#include +#include +#include +#include +#include +#include +#include +#define ALSA_PCM_NEW_HW_PARAMS_API +#include + +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; +} diff --git a/dmx.h b/dmx.h new file mode 100644 index 0000000..b1533ed --- /dev/null +++ b/dmx.h @@ -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; +} diff --git a/midi.h b/midi.h new file mode 100644 index 0000000..9f1c880 --- /dev/null +++ b/midi.h @@ -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; +} diff --git a/mididump.c b/mididump.c new file mode 100644 index 0000000..f0c1c8a --- /dev/null +++ b/mididump.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/opl3.h b/opl3.h new file mode 100644 index 0000000..885c5ba --- /dev/null +++ b/opl3.h @@ -0,0 +1,1042 @@ + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + int16_t out; + int16_t fbmod; + int16_t *mod; + int16_t prout; + uint16_t eg_rout; + uint16_t eg_out; + // uint8_t eg_inc; + uint8_t eg_gen; + // uint8_t eg_rate; + uint8_t eg_ksl; + uint8_t *trem; + uint8_t reg_vib; + uint8_t reg_type; + uint8_t reg_ksr; + uint8_t reg_mult; + uint8_t reg_ksl; + uint8_t reg_tl; + uint8_t reg_ar; + uint8_t reg_dr; + uint8_t reg_sl; + uint8_t reg_rr; + uint8_t reg_wf; + uint8_t key; + uint8_t detrigger; + uint8_t retrigger; + uint32_t pg_reset; + uint32_t pg_phase; + uint16_t pg_phase_out; + uint8_t slot_num; +}; + +struct _opl3_channel { + opl3_slot *slots[2]; + opl3_channel *pair; + opl3_chip *chip; + int16_t *out[4]; + + int32_t level[2]; + + uint8_t chtype; + uint16_t f_num; + uint8_t block; + uint8_t fb; + uint8_t con; + uint8_t alg; + uint8_t ksv; + uint8_t ch_num; +}; + +struct _opl3_chip { + opl3_channel *channel; + opl3_slot *slot; + uint16_t timer; + uint64_t eg_timer; + uint8_t eg_timerrem; + uint8_t eg_state; + uint8_t eg_add; + uint8_t nts; + uint8_t vibpos; + uint8_t vibshift; + uint8_t tremolo; + uint8_t tremolopos; + uint8_t tremoloshift; + uint8_t n_voices; + uint32_t noise; + int16_t zeromod; + int32_t mixbuff[4]; + + int32_t rateratio; + int32_t samplecnt; + int16_t oldsamples[2]; + int16_t samples[2]; +}; + +/* input: [0, 256), output: [0, 65536] */ +#define OPL_SIN(x) ((int32_t)(sin((x) * M_PI / 512.0) * 65536.0)) + +#define RSM_FRAC 10 + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2 +}; + +// Tables + +static const uint16_t logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +static const uint16_t exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +static const uint8_t mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +static const uint8_t kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const uint8_t kslshift[4] = { + 8, 1, 2, 0 +}; + +static const uint8_t eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +// Envelope generator + +typedef int16_t(*envelope_sinfunc)(uint16_t phase, uint16_t envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +int16_t OPL3_EnvelopeCalcExp(uint32_t level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +int16_t OPL3_EnvelopeCalcSin0(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +int16_t OPL3_EnvelopeCalcSin1(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +int16_t OPL3_EnvelopeCalcSin2(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +int16_t OPL3_EnvelopeCalcSin3(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +int16_t OPL3_EnvelopeCalcSin4(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +int16_t OPL3_EnvelopeCalcSin5(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +int16_t OPL3_EnvelopeCalcSin6(uint16_t phase, uint16_t envelope) +{ + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +int16_t OPL3_EnvelopeCalcSin7(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + uint8_t nonzero; + uint8_t rate; + uint8_t rate_hi; + uint8_t rate_lo; + uint8_t reg_rate = 0; + uint8_t ks; + uint8_t eg_shift, shift; + uint16_t eg_rout; + int16_t eg_inc; + uint8_t eg_off; + uint8_t reset = 0; + if(slot->retrigger) { + slot->eg_rout = 0x1ff; + } + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!(slot->eg_rout)) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->eg_rout >> (4 - shift); + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!(slot->key)) + { + slot->eg_gen = envelope_gen_num_release; + } + slot->detrigger = 0; + slot->retrigger = 0; +} + +void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + int16_t ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (uint8_t)ksl; +} + +void OPL3_EnvelopeKeyOn(opl3_slot *slot) +{ + slot->key = 0x01; + if(slot->detrigger) { + slot->eg_gen = envelope_gen_num_release; + // slot->eg_rout = 0x1ff; + // slot->eg_out = slot->eg_rout = 0x1ff; + slot->detrigger = 0; + } + slot->retrigger = 1; +} + +void OPL3_EnvelopeKeyOff(opl3_slot *slot) +{ + slot->key = 0x00; + slot->detrigger = 1; + slot->retrigger = 0; +} + +void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + uint16_t f_num; + uint32_t basefreq; + uint16_t phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + int8_t range; + uint8_t vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (uint16_t)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + slot->pg_phase_out = phase; +} + +void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +void OPL3_ProcessSlot(opl3_slot *slot) +{ + OPL3_SlotCalcFB(slot); + OPL3_EnvelopeCalc(slot); + OPL3_PhaseGenerate(slot); + OPL3_SlotGenerate(slot); +} + +static int16_t OPL3_ClipSampleOld(int32_t sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (int16_t)sample; +} + +int16_t OPL3_ClipSample(int32_t sample) +{ + int32_t sign = (sample < 0) ? -1 : 1; + sample = (sample < 0) ? -sample : sample; + sample *= 5; + sample /= 8; + sample = sample > 32767 ? 32767 : sample; + sample *= sign; + return (int16_t)sample; +} + +void OPL3_Generate(opl3_chip *chip, int16_t *buf) +{ + opl3_channel *channel; + int16_t **out; + int32_t mix[2]; + uint8_t ii; + int16_t accm; + uint8_t shift = 0; + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < (chip->n_voices * 2); ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } + + mix[0] = mix[1] = 0; + for (ii = 0; ii < chip->n_voices; ii++) + { + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; + mix[0] += (int16_t)((accm * channel->level[0]) >> 16); + mix[1] += (int16_t)((accm * channel->level[1]) >> 16); + } + chip->mixbuff[0] = mix[0]; + chip->mixbuff[1] = mix[1]; + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == 0xfffffffff) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; +} + +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (int16_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (int16_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +// Operator + +void OPL3_SlotFlags(opl3_slot *slot, uint8_t tremolo, uint8_t vibrato, uint8_t sustaining, uint8_t ksr) { + if (tremolo) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (uint8_t*)&slot->chip->zeromod; + } + slot->reg_vib = vibrato & 0x01; + slot->reg_type = sustaining & 0x01; + slot->reg_ksr = ksr & 0x01; +} + +void OPL3_SlotMult(opl3_slot *slot, uint8_t mult) { + slot->reg_mult = mult & 0x0f; +} + +void OPL3_SlotKSL(opl3_slot *slot, uint8_t ksl) { + slot->reg_ksl = ksl & 0x03; + OPL3_EnvelopeUpdateKSL(slot); +} + +void OPL3_SlotLevel(opl3_slot *slot, uint8_t level) { + slot->reg_tl = level & 0x3f; +} + +void OPL3_SlotADSR(opl3_slot *slot, uint8_t attack, uint8_t decay, uint8_t sustain, uint8_t release) { + slot->reg_ar = attack & 0x0f; + slot->reg_dr = decay & 0x0f; + slot->reg_sl = sustain & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = release & 0x0f; + if (slot->reg_rr == 0x00) + { + slot->reg_rr = 0x01; + } +} + +void OPL3_SlotWaveform(opl3_slot *slot, uint8_t waveform) { + slot->reg_wf = waveform & 0x07; +} + +// Channel + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelUpdateAlg(opl3_channel *channel) +{ + channel->alg = channel->con; + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } +} + +void OPL3_ChannelFreq(opl3_channel *channel, uint8_t block, uint16_t f_num) { + if (channel->chtype == ch_4op2) + { + return; + } + channel->f_num = f_num & 0x3ff; + channel->block = block & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +void OPL3_ChannelOutput(opl3_channel *channel, uint8_t output, uint32_t level) { + channel->level[output & 1] = level; +} + +void OPL3_Channel4Op(opl3_channel *channel, uint8_t op4) { + if (op4) + { + channel->chtype = ch_4op; + channel->pair->chtype = ch_4op2; + OPL3_ChannelUpdateAlg(channel); + } + else + { + channel->chtype = ch_2op; + channel->pair->chtype = ch_2op; + OPL3_ChannelUpdateAlg(channel); + OPL3_ChannelUpdateAlg(channel->pair); + } +} + +void OPL3_ChannelFeedback(opl3_channel *channel, uint8_t feedback) { + channel->fb = feedback & 0x07; +} + +void OPL3_ChannelAM(opl3_channel *channel, uint8_t am) { + channel->con = am & 0x01; + OPL3_ChannelUpdateAlg(channel); +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slots[0]); + OPL3_EnvelopeKeyOn(channel->slots[1]); + OPL3_EnvelopeKeyOn(channel->pair->slots[0]); + OPL3_EnvelopeKeyOn(channel->pair->slots[1]); + } + else if (channel->chtype == ch_2op) + { + OPL3_EnvelopeKeyOn(channel->slots[0]); + OPL3_EnvelopeKeyOn(channel->slots[1]); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slots[0]); + OPL3_EnvelopeKeyOff(channel->slots[1]); + OPL3_EnvelopeKeyOff(channel->pair->slots[0]); + OPL3_EnvelopeKeyOff(channel->pair->slots[1]); + } + else if (channel->chtype == ch_2op) + { + OPL3_EnvelopeKeyOff(channel->slots[0]); + OPL3_EnvelopeKeyOff(channel->slots[1]); + } +} + +void OPL3_Reset(opl3_chip *chip) +{ + uint8_t slotnum; + uint8_t channum; + + int32_t rateratio = chip->rateratio; + uint8_t n_voices = chip->n_voices; + opl3_slot *slot = chip->slot; + opl3_channel *channel = chip->channel; + + memset(chip, 0, sizeof(opl3_chip) + (n_voices * (sizeof(opl3_slot) * 2 + sizeof(opl3_channel)))); + + chip->rateratio = rateratio; + chip->n_voices = n_voices; + chip->slot = slot; + chip->channel = channel; + + for (slotnum = 0; slotnum < (chip->n_voices * 2); slotnum++) + { + slot = &chip->slot[slotnum]; + slot->chip = chip; + slot->mod = &chip->zeromod; + slot->eg_rout = 0x1ff; + slot->eg_out = 0x1ff; + slot->eg_gen = envelope_gen_num_release; + slot->trem = (uint8_t*)&chip->zeromod; + slot->slot_num = slotnum; + } + for (channum = 0; channum < chip->n_voices; channum++) + { + channel = &chip->channel[channum]; + channel->slots[0] = &chip->slot[channum * 2]; + channel->slots[1] = &chip->slot[channum * 2 + 1]; + chip->slot[channum * 2].channel = channel; + chip->slot[channum * 2 + 1].channel = channel; + channel->pair = &chip->channel[(channum & 1) ? (channum - 1) : (channum + 1)]; + channel->chip = chip; + channel->out[0] = &chip->zeromod; + channel->out[1] = &chip->zeromod; + channel->out[2] = &chip->zeromod; + channel->out[3] = &chip->zeromod; + channel->chtype = ch_2op; + channel->level[0] = 0x10000; + channel->level[1] = 0x10000; + channel->ch_num = channum; + OPL3_ChannelSetupAlg(channel); + } + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +void OPL3_Rate(opl3_chip *chip, uint32_t samplerate) { + chip->rateratio = (samplerate << RSM_FRAC) / 49716; +} + +void OPL3_ChipFlags(opl3_chip *chip, uint8_t nts, uint8_t vibrato, uint8_t deeptremolo, uint8_t deepvibrato) { + chip->nts = nts; + chip->tremoloshift = ((deeptremolo ^ 1) << 1) + 2; + chip->vibshift = deepvibrato ^ 1; +} + +opl3_chip *OPL3_Alloc(uint32_t samplerate, uint8_t voices) { + opl3_chip *chip = malloc(sizeof(opl3_chip) + (voices * (sizeof(opl3_slot) * 2 + sizeof(opl3_channel)))); + chip->channel = (opl3_channel*)(((uint8_t*)chip) + sizeof(opl3_chip)); + chip->slot = (opl3_slot*)(((uint8_t*)chip->channel) + (voices * sizeof(opl3_channel))); + chip->n_voices = voices; + OPL3_Rate(chip, samplerate); + OPL3_Reset(chip); + return chip; +} + +uint8_t OPL3_Playing(opl3_chip *chip) { + opl3_slot *slot; + for(int z = 0; z < (chip->n_voices * 2); z++) { + slot = &chip->slot[z]; + if(slot->eg_out <= 0x100) { + return 1; + } + } + return 0; +} diff --git a/oplplayer.c b/oplplayer.c new file mode 100644 index 0000000..f7b9ea4 --- /dev/null +++ b/oplplayer.c @@ -0,0 +1,966 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define ALSA_PCM_NEW_HW_PARAMS_API +#include + +#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; +} \ No newline at end of file diff --git a/strings.h b/strings.h new file mode 100644 index 0000000..d31176e --- /dev/null +++ b/strings.h @@ -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" diff --git a/wavfile.h b/wavfile.h new file mode 100644 index 0000000..0e73b9d --- /dev/null +++ b/wavfile.h @@ -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; +}