initial commit

This commit is contained in:
Sen 2025-03-20 11:02:57 +01:00
commit 36ca6059c1
49 changed files with 5290 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/bankconv
/bankdump
/banktest
/mididump
/oplplayer

11
Makefile Normal file
View file

@ -0,0 +1,11 @@
all: oplplayer bankdump bankconv banktest mididump
oplplayer: oplplayer.c alsasnd.h ../argparse.h audio.h bank.h dmx.h midi.h strings.h wavfile.h opl3.h
gcc -g -o oplplayer oplplayer.c -lm -lasound -lncurses
bankdump: bankdump.c ../argparse.h dmx.h strings.h
gcc -g -o bankdump bankdump.c
bankconv: bankconv.c ../argparse.h dmx.h strings.h
gcc -g -o bankconv bankconv.c
banktest: banktest.c alsasnd.h ../argparse.h audio.h bank.h dmx.h strings.h wavfile.h opl3.h
gcc -g -o banktest banktest.c -lm -lasound
mididump: mididump.c ../argparse.h bank.h midi.h strings.h
gcc -g -o mididump mididump.c

296
alsasnd.h Normal file
View file

@ -0,0 +1,296 @@
#ifdef SND_S32
#define pcms_t int32_t
#define aud_ihandle aud_handle32
#define SND_FMT SND_PCM_FORMAT_S32_LE
#else
#define pcms_t int16_t
#define aud_ihandle aud_handle16
#define SND_FMT SND_PCM_FORMAT_S16_LE
#endif
#define SND_SMPSIZE (sizeof(pcms_t))
typedef struct {
snd_pcm_t *handle;
uint32_t samplerate;
uint32_t blocksize;
uint32_t channels;
uint32_t smppos;
pcms_t *buffer;
uint32_t devbufsize;
wav_handle wav;
#ifdef SND_MSGUNDER
uint32_t underruns;
uint32_t lastunder;
uint32_t pushed;
#endif
} aud_ihandle;
void
#ifdef SND_S32
wav_write32
#else
wav_write16
#endif
(wav_handle *wav, pcms_t *samples, uint32_t count) {
if(wav->fd == NULL) {
return;
}
for(int c = 0; c < (count * ((uint32_t)wav->channels)); c++) {
#ifdef SND_S32
wav_write_uint32((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
#else
wav_write_uint16((wav->buffer)+(c*SND_SMPSIZE), samples[c]);
#endif
}
if(fwrite(wav->buffer, 1, SND_SMPSIZE * ((uint32_t)wav->channels) * count, wav->fd) != (SND_SMPSIZE * ((uint32_t)wav->channels) * count)) {
fprintf(stderr, STR_WAV_EIO"\n", wav->filename);
fclose(wav->fd);
wav->fd = NULL;
return;
}
wav->samples += count;
}
void
#ifdef SND_S32
aud_prepare32
#else
aud_prepare16
#endif
(aud_ihandle *dev) {
if(dev->handle == NULL) {
return;
}
memset(dev->buffer, 0, dev->blocksize * SND_SMPSIZE * dev->channels);
dev->smppos = 0;
int rc = 0;
uint32_t tries = 0;
while(rc != dev->blocksize) {
if(tries++ >= 256) {
mid_log(STR_AUD_PREPERR);
return;
}
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < dev->blocksize)) {
snd_pcm_wait(dev->handle, 100);
}
else if(rc == -EPIPE) {
snd_pcm_prepare(dev->handle);
}
}
snd_pcm_nonblock(dev->handle, 1);
for(int z = 0; z < 16; z++) {
rc = snd_pcm_writei(dev->handle, dev->buffer, dev->blocksize);
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < dev->blocksize)) {
snd_pcm_wait(dev->handle, 100);
}
else if(rc == -EPIPE) {
snd_pcm_prepare(dev->handle);
}
}
snd_pcm_nonblock(dev->handle, 0);
}
void
#ifdef SND_S32
aud_write32
#else
aud_write16
#endif
(aud_ihandle *dev, pcms_t *data, uint32_t samples) {
int rc;
uint32_t blocksize = dev->blocksize;
while(samples > 0) {
blocksize = samples < blocksize ? samples : blocksize;
if(dev->handle) {
rc = snd_pcm_writei(dev->handle, data, blocksize);
}
else if(dev->wav.buffer) {
#ifdef SND_S32
wav_write32(&dev->wav, data, blocksize);
#else
wav_write16(&dev->wav, data, blocksize);
#endif
}
else {
fwrite(data, 1, blocksize * dev->channels * SND_SMPSIZE, stdout);
}
data += blocksize * dev->channels;
samples -= blocksize;
if(dev->handle) {
if(rc == -EAGAIN || (rc >= 0 && (size_t)rc < blocksize)) {
snd_pcm_wait(dev->handle, 100);
}
else if(rc == -EPIPE) {
#ifdef SND_MSGUNDER
if(dev->underruns == 0) {
dev->lastunder = dev->pushed;
}
dev->underruns += 1;
#endif
snd_pcm_prepare(dev->handle);
}
else if(rc < 0) {
mid_log(STR_AUD_WRITERR, snd_strerror(rc), rc);
}
#ifdef SND_MSGUNDER
else {
dev->pushed += 1;
}
if((dev->underruns > 0) && ((dev->pushed - dev->lastunder) >= (dev->samplerate / dev->blocksize))) {
mid_log(STR_AUD_UNDERR, dev->underruns);
dev->underruns = 0;
}
#endif
}
}
}
int
#ifdef SND_S32
aud_open_dev32
#else
aud_open_dev16
#endif
(aud_ihandle *dev, const char *devname, uint32_t samplerate, uint8_t channels, uint32_t frames, uint32_t bufsize) {
dev->channels = channels;
dev->samplerate = samplerate;
dev->smppos = 0;
#ifdef SND_MSGUNDER
dev->underruns = 0;
dev->lastunder = 0;
dev->pushed = 0;
#endif
dev->handle = NULL;
dev->buffer = NULL;
dev->wav.buffer = NULL;
if((devname[0] == '-') && (devname[1] == 0)) {
dev->blocksize = frames;
dev->devbufsize = 0;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
return 0;
}
else if((strlen(devname) >= 4) && (strcmp(devname+(strlen(devname)-4), ".wav") == 0)) {
if(wav_open(&dev->wav, devname, samplerate, channels, SND_SMPSIZE) ^ 1) {
return false;
}
dev->blocksize = frames;
dev->devbufsize = 0;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
dev->wav.buffer = malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
return 0;
}
int rc;
int subdir = 0;
snd_pcm_hw_params_t *params = NULL;
rc = snd_pcm_open(&dev->handle, devname, SND_PCM_STREAM_PLAYBACK, 0);
if(rc < 0) {
fprintf(stderr, STR_AUD_OPNERR"\n", devname, snd_strerror(rc), rc);
return 1;
}
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(dev->handle, params);
snd_pcm_hw_params_set_access(dev->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
if(snd_pcm_hw_params_set_format(dev->handle, params, SND_FMT) != 0) {
snd_pcm_close(dev->handle);
return 2;
}
snd_pcm_hw_params_set_channels(dev->handle, params, dev->channels);
snd_pcm_hw_params_set_rate_near(dev->handle, params, &dev->samplerate, &subdir);
snd_pcm_uframes_t framesize = frames;
snd_pcm_hw_params_set_period_size_near(dev->handle, params, &framesize, &subdir);
snd_pcm_uframes_t buffersize = bufsize;
if(bufsize > 0) {
snd_pcm_hw_params_set_buffer_size_near(dev->handle, params, &buffersize);
}
rc = snd_pcm_hw_params(dev->handle, params);
if(rc < 0) {
fprintf(stderr, STR_AUD_HWERR"\n", devname, snd_strerror(rc), rc);
snd_pcm_close(dev->handle);
return 1;
}
snd_pcm_hw_params_get_period_size(params, &framesize, &subdir);
snd_pcm_hw_params_get_buffer_size(params, &buffersize);
snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
snd_pcm_sw_params_current(dev->handle, swparams);
// snd_pcm_sw_params_set_avail_min(dev->handle, swparams, framesize);
// snd_pcm_sw_params_set_start_threshold(dev->handle, swparams, 0);
// snd_pcm_sw_params_set_stop_threshold(dev->handle, swparams, buffersize);
rc = snd_pcm_sw_params(dev->handle, swparams);
if(rc < 0) {
fprintf(stderr, STR_AUD_SWERR"\n", devname, snd_strerror(rc), rc);
snd_pcm_close(dev->handle);
return 1;
}
dev->blocksize = framesize;
dev->devbufsize = buffersize;
dev->buffer = (pcms_t*)malloc(dev->blocksize * SND_SMPSIZE * dev->channels);
snd_pcm_wait(dev->handle, -1);
#ifdef SND_S32
aud_prepare32(dev);
#else
aud_prepare16(dev);
#endif
return 0;
}
void
#ifdef SND_S32
aud_push32
#else
aud_push16
#endif
(aud_ihandle *dev, pcms_t *data) {
memcpy(&dev->buffer[dev->smppos*dev->channels], data, SND_SMPSIZE * dev->channels);
dev->smppos++;
if(dev->smppos == dev->blocksize) {
#ifdef SND_S32
aud_write32(dev, dev->buffer, dev->blocksize);
#else
aud_write16(dev, dev->buffer, dev->blocksize);
#endif
dev->smppos = 0;
}
}
void
#ifdef SND_S32
aud_flush32
#else
aud_flush16
#endif
(aud_ihandle *dev) {
if(dev->smppos != 0) {
#ifdef SND_S32
aud_write32(dev, dev->buffer, dev->smppos);
#else
aud_write16(dev, dev->buffer, dev->smppos);
#endif
dev->smppos = 0;
}
}
void
#ifdef SND_S32
aud_close32
#else
aud_close16
#endif
(aud_ihandle *dev) {
if(dev->handle) {
snd_pcm_drain(dev->handle);
snd_pcm_close(dev->handle);
}
if(dev->wav.buffer) {
wav_close(&dev->wav);
free(dev->wav.buffer);
}
free(dev->buffer);
}
#undef pcms_t
#undef aud_ihandle
#undef SND_FMT

271
argparse.h Normal file
View file

@ -0,0 +1,271 @@
/*
Argument parser 0.3.2 (Sen)
#define ARGPARSE_DE -> Deutsche Ausgabe
#define ARGPARSE_OUT -> Ausgabe-Funktion
*/
#ifndef ARGPARSE_H
#define ARGPARSE_H
#include <stdlib.h>
#include <errno.h>
#ifndef ARGPARSE_DE
#define STR_ARG_USAGE "Usage"
#define STR_ARG_HELP "output this help"
#define STR_ARG_OPVEC "Option %s (-%c)"
#define STR_ARG_OPPOS "Option %s (#%d)"
#define STR_ARG_EINT "must be a full number"
#define STR_ARG_ERANGE "has to be in range %d .. %d"
#define STR_ARG_UNKNOWN "Unknown option -%c"
#define STR_ARG_EMORE "Too many arguments, maximum %d"
#define STR_ARG_ELESS "Not enough arguments, minimum %d"
#define STR_ARG_EREQARG STR_ARG_OPVEC " needs an argument"
#else
#define STR_ARG_USAGE "Verwendung"
#define STR_ARG_HELP "Diese Hilfe anzeigen"
#define STR_ARG_OPVEC "Option %s (-%c)"
#define STR_ARG_OPPOS "Option %s (#%d)"
#define STR_ARG_EINT "muss ein ganzzahliger Wert sein"
#define STR_ARG_ERANGE "muss sich im Bereich %d .. %d befinden"
#define STR_ARG_UNKNOWN "Unbekannte Option -%c"
#define STR_ARG_EMORE "Zu viele Argumente, Maximum %d"
#define STR_ARG_ELESS "Nicht genug Argumente, Minimum %d"
#define STR_ARG_EREQARG STR_ARG_OPVEC " benoetigt ein Argument"
#endif
#define STR_ARG_EINT_V STR_ARG_OPVEC " " STR_ARG_EINT
#define STR_ARG_EINT_P STR_ARG_OPPOS " " STR_ARG_EINT
#define STR_ARG_ERANGE_V STR_ARG_OPVEC " " STR_ARG_ERANGE
#define STR_ARG_ERANGE_P STR_ARG_OPPOS " " STR_ARG_ERANGE
#ifndef ARGPARSE_OUT
#include <stdio.h>
#define ARGPARSE_OUT(s ...) fprintf(stderr, s)
#endif
typedef struct {
char spec;
char arg;
int min;
int max;
int *value;
char **str;
const char *name;
} arg_t;
typedef struct {
unsigned int arg_min;
unsigned int arg_max;
unsigned int arg_count;
arg_t *arg_vec;
} argp_t;
void arg_help(argp_t *argspec, char *prog) {
arg_t *arg_vec = argspec->arg_vec;
ARGPARSE_OUT(STR_ARG_USAGE": %s\n [-h ("STR_ARG_HELP")]\n", prog);
for(int h = 0; h < argspec->arg_count; h++) {
arg_t *arg = &arg_vec[h];
if(arg->spec) {
if(arg->arg) {
if((arg->min == 0) && (arg->max == 0)) {
ARGPARSE_OUT(" [-%c <%s>]\n", arg->spec, arg->name);
}
else {
ARGPARSE_OUT(" [-%c <%s (%d-%d)>]\n", arg->spec, arg->name, arg->min, arg->max);
}
}
else {
ARGPARSE_OUT(" [-%c (%s)]\n", arg->spec, arg->name);
}
}
}
for(int h = 0; h < argspec->arg_max; h++) {
arg_t *arg = &arg_vec[h];
char nb = (h < argspec->arg_min) ? '<' : '[';
char eb = (h < argspec->arg_min) ? '>' : ']';
if((arg->min == 0) && (arg->max == 0)) {
ARGPARSE_OUT(" %c%s%s%c\n", nb, arg->name, (arg->value == NULL) ? "" : ", ...", eb);
}
else {
ARGPARSE_OUT(" %c%s (%d-%d)%c\n", nb, arg->name, arg->min, arg->max, eb);
}
}
}
char arg_process(arg_t *arg_vec, char *arg, int off) {
if((arg_vec[off].min == 0) && (arg_vec[off].max == 0)) {
if(arg_vec[off].value != NULL) {
arg_vec[off].str[*(arg_vec[off].value)] = arg;
*(arg_vec[off].value) += 1;
}
else {
*(arg_vec[off].str) = arg;
}
}
else {
errno = 0;
char *endptr;
*(arg_vec[off].value) = strtol(arg, &endptr, 10);
if((errno != 0) || (endptr[0] != 0)) {
if(arg_vec[off].spec) {
ARGPARSE_OUT(STR_ARG_EINT_V"\n", arg_vec[off].name, arg_vec[off].spec);
}
else {
ARGPARSE_OUT(STR_ARG_EINT_P"\n", arg_vec[off].name, off + 1);
}
return 0;
}
if((*(arg_vec[off].value) < arg_vec[off].min) || (*(arg_vec[off].value) > arg_vec[off].max)) {
if(arg_vec[off].spec) {
ARGPARSE_OUT(STR_ARG_ERANGE_V"\n", arg_vec[off].name, arg_vec[off].spec, arg_vec[off].min, arg_vec[off].max);
}
else {
ARGPARSE_OUT(STR_ARG_ERANGE_P"\n", arg_vec[off].name, off + 1, arg_vec[off].min, arg_vec[off].max);
}
return 0;
}
}
return 1;
}
char arg_parse(argp_t *argspec, int argc, char **argv) {
unsigned int arg_min = argspec->arg_min;
unsigned int arg_max = argspec->arg_max;
unsigned int arg_count = argspec->arg_count;
arg_t *arg_vec = argspec->arg_vec;
unsigned int idx = 0;
unsigned int sp = 0;
char sa = 0;
for(int h = 1; h < argc; h++) {
char *arg = argv[h];
if((sa == 0) && (arg[0] == '-') && (arg[1] != 0)) {
arg += 1;
if(arg[0] == 'h') {
arg_help(argspec, argv[0]);
free(argspec);
return 0;
}
while((arg[0] != 0) && (sa == 0)) {
int f = 0;
for(int h = 0; h < arg_count; h++) {
if((arg_vec[h].arg ^ 1) && (arg[0] == arg_vec[h].spec)) {
*(arg_vec[h].value) = 1;
arg += 1;
f++;
break;
}
else if(arg[0] == arg_vec[h].spec) {
sa = arg[0];
sp = h;
arg += 1;
f++;
break;
}
}
if(f == 0) {
ARGPARSE_OUT(STR_ARG_UNKNOWN"\n", arg[0]);
free(argspec);
return 0;
}
}
if(arg[0] == 0) {
continue;
}
}
else if(sa == 0) {
if((arg[0] == '\\') && (arg[1] == '-')) {
arg += 1;
}
if((idx < arg_max) || ((arg_max > 0) && (arg_vec[arg_max-1].min == 0) && (arg_vec[arg_max-1].max == 0) && (arg_vec[arg_max-1].value != NULL))) {
if(!arg_process(arg_vec, arg, (idx >= arg_max) ? (arg_max-1) : idx)) {
free(argspec);
return 0;
}
idx += 1;
continue;
}
else {
ARGPARSE_OUT(STR_ARG_EMORE"\n", arg_max);
free(argspec);
return 0;
}
}
if(!arg_process(arg_vec, arg, sp)) {
free(argspec);
return 0;
}
sa = 0;
}
if(sa != 0) {
ARGPARSE_OUT(STR_ARG_EREQARG"\n", arg_vec[sp].name, sa);
free(argspec);
return 0;
}
if(idx < arg_min) {
ARGPARSE_OUT(STR_ARG_ELESS"\n", arg_min);
free(argspec);
return 0;
}
free(argspec);
return 1;
}
void arg_add_gen(argp_t *argspec, char spec, const char *name, int min, int max, char argreq, int *value, char **str) {
arg_t *arg = &argspec->arg_vec[(argspec->arg_count)++];
arg->spec = spec;
arg->arg = argreq;
arg->min = min;
arg->max = max;
arg->value = value;
arg->str = str;
arg->name = name;
}
void arg_add_int(argp_t *argspec, char spec, const char *name, int min, int max, int *value) {
arg_add_gen(argspec, spec, name, min, max, 1, value, NULL);
}
void arg_add_str(argp_t *argspec, char spec, const char *name, char **value) {
arg_add_gen(argspec, spec, name, 0, 0, 1, NULL, value);
}
void arg_add_bool(argp_t *argspec, char spec, const char *name, int *value) {
arg_add_gen(argspec, spec, name, 0, 0, 0, value, NULL);
}
void arg_add_nint(argp_t *argspec, const char *name, char req, int min, int max, int *value) {
arg_add_gen(argspec, 0, name, min, max, 0, value, NULL);
argspec->arg_max += 1;
argspec->arg_min += req ? 1 : 0;
}
void arg_add_nstr(argp_t *argspec, const char *name, char req, char **value) {
arg_add_gen(argspec, 0, name, 0, 0, 0, NULL, value);
argspec->arg_max += 1;
argspec->arg_min += req ? 1 : 0;
}
void arg_add_nbool(argp_t *argspec, const char *name, char req, int *value) {
arg_add_gen(argspec, 0, name, 0, 1, 0, value, NULL);
argspec->arg_max += 1;
argspec->arg_min += req ? 1 : 0;
}
void arg_add_vstr(argp_t *argspec, const char *name, char req, char **svalue, int *ivalue) {
arg_add_gen(argspec, 0, name, 0, 0, 0, ivalue, svalue);
argspec->arg_max = 1;
argspec->arg_min += req ? 1 : 0;
}
argp_t *arg_alloc(unsigned int count) {
char *data = malloc(sizeof(argp_t) + (count * sizeof(arg_t)));
argp_t *argspec = (argp_t*)data;
data += sizeof(argp_t);
argspec->arg_vec = (arg_t*)data;
argspec->arg_count = 0;
argspec->arg_min = 0;
argspec->arg_max = 0;
}
#endif

97
audio.h Normal file
View file

@ -0,0 +1,97 @@
#include "wavfile.h"
#define SND_MSGUNDER
#define SND_S32
#include "alsasnd.h"
#undef SND_S32
#include "alsasnd.h"
typedef struct {
aud_handle16 ihandle;
aud_handle32 lhandle;
uint32_t samplerate;
bool iswav;
bool iswide;
} aud_handle;
bool aud_open_dev(aud_handle *dev, const char *devname, uint32_t samplerate, uint8_t channels, uint32_t frames, uint32_t bufsize, bool wide) {
if(wide) {
int state = aud_open_dev32(&dev->lhandle, devname, samplerate, channels, frames, bufsize);
if(state == 2) {
wide = false;
mid_log(STR_AUD_FMTCH"\n", devname, 32, 16);
state = aud_open_dev16(&dev->ihandle, devname, samplerate, channels, frames, bufsize);
if(state == 2) {
mid_log(STR_AUD_FMTERR"\n", devname);
return false;
}
else if(state != 0) {
return false;
}
}
else if(state != 0) {
return false;
}
}
else {
int state = aud_open_dev16(&dev->ihandle, devname, samplerate, channels, frames, bufsize);
if(state == 2) {
wide = true;
mid_log(STR_AUD_FMTCH"\n", devname, 16, 32);
state = aud_open_dev32(&dev->lhandle, devname, samplerate, channels, frames, bufsize);
if(state == 2) {
mid_log(STR_AUD_FMTERR"\n", devname);
return false;
}
else if(state != 0) {
return false;
}
}
else if(state != 0) {
return false;
}
}
if(wide) {
dev->samplerate = dev->lhandle.samplerate;
dev->iswav = dev->lhandle.wav.buffer != NULL;
}
else {
dev->samplerate = dev->ihandle.samplerate;
dev->iswav = dev->ihandle.wav.buffer != NULL;
}
dev->iswide = wide;
return true;
}
void aud_push(aud_handle *dev, int16_t *data) {
if(dev->iswide) {
int32_t conv[dev->lhandle.channels];
for(int h = 0; h < dev->lhandle.channels; h++) {
conv[h] = ((int32_t)data[h]) << 16;
}
aud_push32(&dev->lhandle, conv);
}
else {
aud_push16(&dev->ihandle, data);
}
}
void aud_flush(aud_handle *dev) {
if(dev->iswide) {
aud_flush32(&dev->lhandle);
}
else {
aud_flush16(&dev->ihandle);
}
}
void aud_close(aud_handle *dev) {
if(dev->iswide) {
aud_close32(&dev->lhandle);
}
else {
aud_close16(&dev->ihandle);
}
}

487
bank.h Normal file
View file

@ -0,0 +1,487 @@
#define BANK_MAX 64
#define BANK_KEEP 0x01
#define BANK_UNKN 0x02
typedef struct _bank_voice bank_voice;
typedef struct _bank_key bank_key;
typedef struct _bank_channel bank_channel;
typedef struct _bank_handle bank_handle;
struct _bank_voice {
bank_channel *channel;
opl3_channel *opl;
uint8_t note;
uint8_t op;
int16_t detune;
bank_voice *pair;
};
struct _bank_key {
uint8_t note;
uint8_t velocity;
bank_voice *voice;
};
struct _bank_channel {
bank_handle *bank;
bank_key keys[BANK_MAX];
uint8_t notes[128];
uint8_t keyindex;
uint8_t active;
uint16_t pbank;
int8_t pan;
uint8_t volume;
int16_t pitch;
uint8_t program;
bank_instr *instr;
uint8_t ch_num;
};
struct _bank_handle {
bank_channel channel[16];
bank_voice *voices;
bank_instr *bdata;
uint16_t voiceindex;
uint16_t v_avail;
uint16_t v_used;
uint8_t flags;
int8_t velo_func;
};
void bank_reset(opl3_chip *chip, bank_handle *bank) {
uint16_t voices = chip->n_voices;
uint8_t flags = bank->flags;
int8_t velo_func = bank->velo_func;
bank_instr *instr = bank->bdata;
uint8_t *bankdata = (uint8_t*)bank;
bankdata += sizeof(bank_handle);
memset(bank, 0, sizeof(bank_handle));
bank->voices = (bank_voice*)bankdata;
bank->v_avail = voices;
bank->flags = flags;
bank->velo_func = velo_func;
bank->bdata = instr;
for(int h = 0; h < 16; h++) {
bank_channel *channel = &bank->channel[h];
channel->bank = bank;
memset(channel->notes, 0xff, 128);
channel->volume = 127;
channel->pbank = (h == 9) ? 128 : 0;
channel->instr = &bank->bdata[(h == 9) ? 128 : 0];
channel->ch_num = h;
}
for(int h = 0; h < voices; h++) {
bank_voice *voice = &bank->voices[h];
voice->channel = NULL;
voice->opl = &chip->channel[h];
voice->note = 0xff;
voice->op = b_op2;
voice->detune = 0;
voice->pair = &bank->voices[h+(((h & 1) == 0) ? 1 : -1)];
}
}
bank_handle *bank_alloc(opl3_chip *chip, bank_instr *instr, uint8_t keep, uint8_t useunkn, int8_t velofunc) {
uint8_t *bankdata = malloc(sizeof(bank_handle) + (sizeof(bank_voice) * chip->n_voices));
bank_handle *bank = (bank_handle*)bankdata;
bank->flags = (keep ? BANK_KEEP : 0) | (useunkn ? BANK_UNKN : 0);
bank->velo_func = velofunc;
bank->bdata = instr;
bank_reset(chip, bank);
return bank;
}
static const uint32_t bank_notes[] = {
8175, 8661, 9177, 9722, 10300, 10913, 11562, 12249,
12978, 13750, 14567, 15433, 16351, 17323, 18354, 19445,
20601, 21826, 23124, 24499, 25956, 27500, 29135, 30867,
32703, 34647, 36708, 38890, 41203, 43653, 46249, 48999,
51913, 55000, 58270, 61735, 65406, 69295, 73416, 77781,
82406, 87307, 92498, 97998, 103826, 110000, 116540, 123470,
130812, 138591, 146832, 155563, 164813, 174614, 184997, 195997,
207652, 220000, 233081, 246941, 261625, 277182, 293664, 311126,
329627, 349228, 369994, 391995, 415304, 440000, 466163, 493883,
523251, 554365, 587329, 622253, 659255, 698456, 739988, 783990,
830609, 880000, 932327, 987766, 1046502, 1108730, 1174659, 1244507,
1318510, 1396912, 1479977, 1567981, 1661218, 1760000, 1864655, 1975533,
2093004, 2217461, 2349318, 2489015, 2637020, 2793825, 2959955, 3135963,
3322437, 3520000, 3729310, 3951066, 4186009, 4434922, 4698636, 4978031,
5274040, 5587651, 5919910, 6271926, 6644875, 7040000, 7458620, 7902132,
8372018, 8869844, 9397272, 9956063, 10548081, 11175303, 11839821, 12543853
};
static const uint32_t opl3_maxfreq[] = {
48503,
97006,
194013,
388026,
776053,
1552107,
3104215,
6208431
};
uint8_t bank_getnote(bank_channel *ch, uint8_t key, uint8_t id, uint8_t drum, bank_instr *instr) {
int16_t note = (int16_t)key;
if(instr->fixed /* || drum */) {
note = ((int16_t)instr->percnum) + instr->channels[id].offset;
}
else {
note += instr->channels[id].offset;
}
note = note < 0 ? 0 : note;
note = note > 127 ? 127 : note;
return (uint8_t)note;
}
static void OPL3_ChannelFreqHz(opl3_channel *channel, uint32_t freq) {
uint32_t block = 0;
while(opl3_maxfreq[block] < freq) {
block++;
if(block == 8) {
break;
}
}
if(block == 8) {
OPL3_ChannelFreq(channel, 7, 1023);
return;
}
double f_num = ((double)freq) / 1000.0 * pow(2.0, (20.0-((double)block))) / 49716.0;
OPL3_ChannelFreq(channel, block, (uint32_t)f_num);
}
#define BANK_PBRANGE 2.0
uint32_t bank_getfreq(uint8_t key, int16_t pitch, int16_t detune) {
double pfrq = pow(2.0, ((((double)pitch) / 8191.0 * BANK_PBRANGE) + (((double)detune) / 100.0)) / 12.0);
double freq = ((double)bank_notes[key]) * pfrq;
return (uint32_t)freq;
}
static void OPL3_ChannelNote(opl3_channel *channel, uint8_t key, int16_t pitch, int16_t detune) {
OPL3_ChannelFreqHz(channel, bank_getfreq(key, pitch, detune));
}
#define BANK_VELOFUNC(x, n) (1.0-log(1.0+((1.0-pow(x,n))*(M_E-1.0))))
uint32_t bank_getlevel(int8_t function, uint8_t velocity, uint8_t volume, int8_t pan) {
double lvl = ((function == -128) ? 1.0 :
(function ? BANK_VELOFUNC(((double)velocity)/127.0, function < 0 ? (0.1+(1.0-((((double)(-function))-1.0)/126.0))*9.9) : (1.0+((((double)function)-1.0)/126.0)*9.0)) :
(((double)velocity)/127.0))) * (((double)volume)/127.0) * (1.0-(0.9*((double)pan)/63.0));
// fprintf(stderr, "%d===%d-->%.2f\n", function, velocity, lvl);
lvl *= 65536.0;
return (uint32_t)lvl;
}
static void OPL3_ChannelLevelPan(opl3_channel *channel, int8_t function, uint8_t velocity, uint8_t volume, int8_t pan) {
OPL3_ChannelOutput(channel, 0, bank_getlevel(function, velocity, volume, pan));
OPL3_ChannelOutput(channel, 1, bank_getlevel(function, velocity, volume, -pan));
}
void bank_release_voice(bank_handle *bank, bank_channel *ch, bank_voice *voice) {
if(voice->note == 0xff) {
return;
}
OPL3_ChannelKeyOff(voice->opl);
voice->note = 0xff;
if(voice->op == b_op22) {
voice->pair->note = 0xff;
OPL3_ChannelKeyOff(voice->pair->opl);
bank->v_used -= 2;
}
else if(voice->op == b_op4) {
voice->pair->note = 0xff;
bank->v_used -= 2;
}
else {
bank->v_used -= 1;
}
}
bank_voice *bank_release_key(bank_channel *ch, uint8_t note) {
if(ch->active == 0) {
return NULL;
}
if(ch->notes[note] == 0xff) {
return NULL;
}
bank_key *key = &ch->keys[ch->notes[note]];
ch->notes[note] = 0xff;
ch->active -= 1;
if(key->voice) {
bank_release_voice(ch->bank, ch, key->voice);
}
key->velocity = 0;
return key->voice;
}
void bank_init_voice(opl3_channel *channel, bank_instr *instr, uint8_t id) {
OPL3_Channel4Op(channel, instr->op == b_op4);
for(int o = 0; o < ((instr->op == b_op4) ? 4 : 2); o++) {
bank_pair *pair = &instr->channels[(instr->op == b_op4) ? (o >> 1) : id];
bank_operator *op = &pair->ops[o & 1];
opl3_slot *slot = (o >= 2) ? (channel->pair->slots[o & 1]) : (channel->slots[o & 1]);
OPL3_SlotFlags(slot, op->tremolo, op->vibrato, op->sustaining, op->ksr);
OPL3_SlotMult(slot, op->mult);
OPL3_SlotKSL(slot, op->ksl);
OPL3_SlotLevel(slot, op->level);
OPL3_SlotADSR(slot, op->attack, op->decay, op->sustain, op->release);
OPL3_SlotWaveform(slot, op->waveform);
if((o & 1) == 1) {
opl3_channel *chn = (o >= 2) ? channel->pair : channel;
OPL3_ChannelFeedback(chn, pair->feedback);
OPL3_ChannelAM(chn, pair->am);
}
}
}
bank_voice *bank_get_voice(bank_handle *bank, bank_channel *ch, uint8_t note, uint8_t velocity) {
bank_instr *instr = ch->instr;
if(ch->pbank == 128) {
instr += note;
}
else if((ch->pbank != 0) && (!(bank->flags & BANK_UNKN))) {
return NULL;
}
if(instr->op == b_op0) {
return NULL;
}
if(bank->v_used == bank->v_avail) {
if(bank->flags & BANK_KEEP) {
return NULL;
}
if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) {
bank->voiceindex += 1;
if(bank->voiceindex >= bank->v_avail) {
bank->voiceindex = 0;
}
}
bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note);
}
else if((instr->op != b_op2) && ((bank->voiceindex & 1) != 0)) {
bank->voiceindex += 1;
if(bank->voiceindex >= bank->v_avail) {
bank->voiceindex = 0;
}
}
uint8_t vi = bank->voiceindex;
while((bank->voices[bank->voiceindex].note != 0xff) || ((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff))) {
bank->voiceindex += (instr->op != b_op2) ? 2 : 1;
if(bank->voiceindex >= bank->v_avail) {
bank->voiceindex = 0;
}
if(vi == bank->voiceindex) {
if(bank->flags & BANK_KEEP) {
return NULL;
}
bank_release_key(bank->voices[bank->voiceindex].channel, bank->voices[bank->voiceindex].note);
if((instr->op != b_op2) && (bank->voices[bank->voiceindex+1].note != 0xff)) {
bank_release_key(bank->voices[bank->voiceindex+1].channel, bank->voices[bank->voiceindex+1].note);
}
break;
}
}
bank_voice *voice = &bank->voices[bank->voiceindex];
if((instr->op == b_op2) && (voice->op == b_op4)) {
int16_t offset = ((int16_t)bank->voiceindex) + (((bank->voiceindex & 1) == 0) ? 1 : -1);
bank_voice *voice2 = &bank->voices[offset];
if(voice2->note == 0xff) {
OPL3_ChannelOutput(voice2->opl, 0, 0);
OPL3_ChannelOutput(voice2->opl, 1, 0);
}
}
bank->voiceindex += (instr->op != b_op2) ? 2 : 1;
if(bank->voiceindex >= bank->v_avail) {
bank->voiceindex = 0;
}
voice->channel = ch;
voice->note = note;
voice->op = instr->op;
voice->detune = instr->channels[0].detune;
bank_init_voice(voice->opl, instr, 0);
if(voice->op == b_op22) {
voice->pair->channel = ch;
voice->pair->note = note;
voice->pair->op = b_op22;
voice->pair->detune = instr->channels[1].detune;
bank_init_voice(voice->pair->opl, instr, 1);
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
OPL3_ChannelNote(voice->pair->opl, bank_getnote(ch, note, 1, ch->pbank == 128, instr), ch->pitch, voice->pair->detune);
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
OPL3_ChannelLevelPan(voice->pair->opl, bank->velo_func, velocity, ch->volume, ch->pan);
OPL3_ChannelKeyOn(voice->opl);
OPL3_ChannelKeyOn(voice->pair->opl);
bank->v_used += 2;
}
else if(voice->op == b_op4) {
voice->pair->channel = ch;
voice->pair->note = note;
voice->pair->op = b_op4;
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
OPL3_ChannelKeyOn(voice->opl);
bank->v_used += 2;
}
else {
OPL3_ChannelNote(voice->opl, bank_getnote(ch, note, 0, ch->pbank == 128, instr), ch->pitch, voice->detune);
OPL3_ChannelLevelPan(voice->opl, bank->velo_func, velocity, ch->volume, ch->pan);
OPL3_ChannelKeyOn(voice->opl);
bank->v_used += 1;
}
return voice;
}
bank_voice *bank_press_key(bank_channel *ch, uint8_t note, uint8_t velocity) {
if(ch->notes[note] != 0xff) {
bank_release_key(ch, note);
}
if(ch->active == BANK_MAX) {
return NULL;
}
uint8_t ki = ch->keyindex;
while(ch->keys[ch->keyindex].velocity != 0) {
ch->keyindex += 1;
if(ch->keyindex == BANK_MAX) {
ch->keyindex = 0;
}
if(ki == ch->keyindex) {
return NULL;
}
}
bank_voice *voice = bank_get_voice(ch->bank, ch, note, velocity);
ch->notes[note] = ch->keyindex;
ch->active += 1;
bank_key *key = &ch->keys[ch->keyindex];
key->note = note;
key->velocity = velocity;
key->voice = voice;
return voice;
}
void bank_notesoff(bank_channel *ch) {
for(int h = 0; (h < BANK_MAX) && (ch->active > 0); h++) {
bank_release_key(ch, h);
}
}
void bank_progupdate(bank_channel *ch, opl3_chip *chip) {
bank_notesoff(ch);
uint16_t id = ch->program;
if(ch->pbank == 128) {
id = 128;
}
else if((ch->pbank != 0) && (!(ch->bank->flags & BANK_UNKN))) {
id = 0;
}
ch->instr = (ch->bank->bdata)+id;
}
void bank_levelupdate(bank_channel *ch, opl3_chip *chip) {
uint8_t done = 0;
for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) {
bank_key *key = (ch->keys)+h;
if((key->velocity == 0) || (key->voice == NULL)) {
continue;
}
OPL3_ChannelLevelPan(key->voice->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan);
if(key->voice->op == b_op22) {
OPL3_ChannelLevelPan(key->voice->pair->opl, ch->bank->velo_func, key->velocity, ch->volume, ch->pan);
}
done++;
}
}
void bank_frequpdate(bank_channel *ch, opl3_chip *chip) {
uint8_t done = 0;
for(int h = 0; (h < BANK_MAX) && (done < ch->active); h++) {
bank_key *key = (ch->keys)+h;
if((key->velocity == 0) || (key->voice == NULL)) {
continue;
}
OPL3_ChannelNote(key->voice->opl, bank_getnote(ch, key->note, 0, ch->pbank == 128, ch->instr), ch->pitch, key->voice->detune);
if(key->voice->op == b_op22) {
OPL3_ChannelNote(key->voice->pair->opl, bank_getnote(ch, key->note, 1, ch->pbank == 128, ch->instr), ch->pitch, key->voice->pair->detune);
}
done++;
}
}
void bank_noteon(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t key, uint8_t velocity) {
bank_channel *ch = &bank->channel[channel];
// if((ch->pbank == 128) && ((key < 35) || (key > 81))) {
// return;
// }
bank_press_key(ch, key, velocity);
}
void bank_noteoff(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t key, uint8_t velocity) {
bank_channel *ch = &bank->channel[channel];
// if((ch->pbank == 128) && ((key < 35) || (key > 81))) {
// return;
// }
bank_release_key(ch, key);
}
void bank_progchange(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t program) {
bank_channel *ch = &bank->channel[channel];
ch->program = program;
bank_progupdate(ch, chip);
}
void bank_pitchbend(bank_handle *bank, opl3_chip *chip, uint8_t channel, int16_t pitch) {
bank_channel *ch = &bank->channel[channel];
ch->pitch = pitch;
bank_frequpdate(ch, chip);
}
void bank_control(bank_handle *bank, opl3_chip *chip, uint8_t channel, uint8_t control, uint8_t value) {
bank_channel *ch = &bank->channel[channel];
switch(control) {
case 0x00: // bank MSB
// if((channel == 9) && (value == 0)) {
// ch->pbank = 128;
// }
// else {
if(channel != 9) {
ch->pbank &= 0x007f;
ch->pbank |= (((uint16_t)value) << 7) & 0x3f80;
}
bank_progupdate(ch, chip);
break;
case 0x20: // bank LSB
// if((channel == 9) && (value == 0)) {
// ch->pbank = 128;
// }
// else {
if(channel != 9) {
ch->pbank &= 0x3f80;
ch->pbank |= ((uint16_t)value) & 0x007f;
}
bank_progupdate(ch, chip);
break;
case 0x07: // volume MSB
ch->volume = value;
bank_levelupdate(ch, chip);
break;
case 0x0a: // pan MSB
ch->pan = ((int8_t)value) - 64;
bank_levelupdate(ch, chip);
break;
case 0x79: // reset
ch->pbank = (channel == 9) ? 128 : 0;
ch->volume = 127;
ch->pan = 0;
bank_progupdate(ch, chip);
bank_levelupdate(ch, chip);
break;
case 0x7b: // all off
bank_notesoff(ch);
break;
}
}
void bank_alloff(bank_handle *bank) {
for(int h = 0; h < 16; h++) {
bank_notesoff(&bank->channel[h]);
}
}

40
bankconv.c Normal file
View file

@ -0,0 +1,40 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include "strings.h"
#include "dmx.h"
#define ARGPARSE_DE
#include "../argparse.h"
int main(int argc, char **argv) {
char *dmxname = NULL;
char *drumname = NULL;
char *skbname = NULL;
int32_t usedrum = 0;
argp_t *argspec = arg_alloc(4);
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
arg_add_nstr(argspec, STR_ARGS_SKB, true, &skbname);
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
if(arg_parse(argspec, argc, argv) ^ 1) {
return 1;
}
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
if(instr == NULL) {
return 1;
}
if(skb_write_bank(instr, skbname) ^ 1) {
free(instr);
return 1;
}
free(instr);
return 0;
}

147
bankdump.c Normal file
View file

@ -0,0 +1,147 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include "strings.h"
#include "dmx.h"
#define ARGPARSE_DE
#include "../argparse.h"
void dump_bank(bank_instr *instr, uint8_t prog, uint8_t name) {
bank_instr *ins = &instr[prog];
if(ins->op == b_op0) {
return;
}
printf("[] #%d - MIDI %s #%d\n", prog, (prog < 128) ? "program" : "drum", (prog < 128) ? prog : (prog - 128));
if(name) {
printf(" [] name = '%s'\n", ins->name);
}
printf(" [] mode = %s\n", (ins->op == b_op22) ? "2x2OP" : ((ins->op == b_op4) ? "4OP" : "2OP"));
printf(" [] notes = %s\n", ins->fixed ? "fixed" : "variable");
printf(" [] percussion number = %d\n", ins->percnum);
for(int ch = 0; ch < ((ins->op == b_op2) ? 1 : 2); ch++) {
bank_pair *chn = &ins->channels[ch];
printf(" [] voice %d\n", ch + 1);
printf(" [] detune = %d\n", chn->detune);
printf(" [] note offset = %d\n", chn->offset);
printf(" [] feedback = %d\n", chn->feedback);
printf(" [] algorithm = %s\n", chn->am ? "AM" : "FM");
for(int op = 0; op < 2; op++) {
bank_operator *opr = &chn->ops[op];
printf(" [] %s\n", op ? "carrier" : "modulator");
printf(" [] freq. multiplier = %d\n", opr->mult);
printf(" [] output level = %d\n", 63-opr->level);
printf(" [] waveform = %d\n", opr->waveform);
printf(" [] key scale level = %d\n", opr->ksl);
printf(" [] attack rate = %d\n", opr->attack);
printf(" [] decay rate = %d\n", opr->decay);
printf(" [] sustain level = %d\n", 15-opr->sustain);
printf(" [] release rate = %d\n", opr->release);
printf(" [] tremolo = %s\n", opr->tremolo ? "ON" : "OFF");
printf(" [] vibrato = %s\n", opr->vibrato ? "ON" : "OFF");
printf(" [] envelope sustain = %s\n", opr->sustaining ? "ON" : "OFF");
printf(" [] key rate scale = %s\n", opr->ksr ? "ON" : "OFF");
}
}
}
void dump_bank_header(uint8_t name) {
printf(name ? "| |GLOBAL " : "| |GLOBAL ");
for(int ch = 0; ch < 2; ch++) {
printf("|VOICE %d |MODULATOR %d |CARRIER %d ", ch + 1, ch + 1, ch + 1);
}
printf("|\n");
printf(name ? "|ID#|MIDI|NAME |TP|FN|PCN" : "|ID#|MIDI|TP|FN|PCN");
for(int ch = 0; ch < 2; ch++) {
printf("|DTNE|OFFS|FB|AL");
for(int op = 0; op < 2; op++) {
printf("|ML|LV|WV|KL|AR|DR|SL|RR|TR|VB|ST|KS");
}
}
printf("|\n");
}
void dump_bank_line(bank_instr *instr, uint8_t prog, uint8_t name) {
bank_instr *ins = &instr[prog];
if(ins->op == b_op0) {
return;
}
printf(name ? "|%03d|%c%03d|%-32s|%s|%s|%03d" : "|%03d|%c%03d|%s%s|%s|%03d", prog, (prog < 128) ? 'P' : 'D', (prog < 128) ? prog : (prog - 128), name ? ins->name : "", (ins->op == b_op22) ? "P4" : ((ins->op == b_op4) ? "4O" : "2O"), ins->fixed ? "+F" : "--", ins->percnum);
for(int ch = 0; ch < ((ins->op == b_op2) ? 1 : 2); ch++) {
bank_pair *chn = &ins->channels[ch];
printf("|%+04d|%+04d|%02d|%s", chn->detune, chn->offset, chn->feedback, chn->am ? "AM" : "FM");
for(int op = 0; op < 2; op++) {
bank_operator *opr = &chn->ops[op];
printf("|%02d|%02d|%02d|%02d|%02d|%02d|%02d|%02d|%s|%s|%s|%s", opr->mult, 63-opr->level, opr->waveform, opr->ksl, opr->attack, opr->decay, 15-opr->sustain, opr->release,
opr->tremolo ? "+T" : "--", opr->vibrato ? "+V" : "--", opr->sustaining ? "+S" : "--", opr->ksr ? "+K" : "--");
}
}
if(ins->op == b_op2) {
printf("|....|....|..|..");
for(int op = 0; op < 2; op++) {
printf("|..|..|..|..|..|..|..|..|..|..|..|..");
}
}
printf("|\n");
}
int main(int argc, char **argv) {
char *dmxname = NULL;
char *drumname = NULL;
int32_t usedrum = 0;
int32_t prog = 256;
int32_t table = 0;
int32_t name = 0;
argp_t *argspec = arg_alloc(6);
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
arg_add_nint(argspec, STR_ARGS_BANKN, false, 0, 255, &prog);
arg_add_bool(argspec, 'l', STR_ARGS_NTABLE, &table);
arg_add_bool(argspec, 'n', STR_ARGS_NONAME, &name);
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
if(arg_parse(argspec, argc, argv) ^ 1) {
return 1;
}
table ^= 1;
name ^= 1;
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
if(instr == NULL) {
return 1;
}
if(prog == 256) {
if(table) {
dump_bank_header(name);
}
for(int i = 0; i < 256; i++) {
if(table) {
dump_bank_line(instr, i, name);
}
else {
dump_bank(instr, i, name);
}
}
}
else {
if(instr[prog].op == b_op0) {
fprintf(stderr, STR_BNK_UNDEF"\n", prog);
free(instr);
return 1;
}
if(table) {
dump_bank_header(name);
dump_bank_line(instr, prog, name);
}
else {
dump_bank(instr, prog, name);
}
}
free(instr);
return 0;
}

BIN
banks/2x2.op3 Normal file

Binary file not shown.

BIN
banks/cyberpuck.tmb Normal file

Binary file not shown.

BIN
banks/d3dtimbr.tmb Normal file

Binary file not shown.

BIN
banks/d3dtimbrmod.tmb Normal file

Binary file not shown.

BIN
banks/default.tmb Normal file

Binary file not shown.

BIN
banks/dmx_dmx.op2 Normal file

Binary file not shown.

BIN
banks/dmx_doom1.op2 Normal file

Binary file not shown.

BIN
banks/dmx_doom2.op2 Normal file

Binary file not shown.

BIN
banks/dmx_raptor.op2 Normal file

Binary file not shown.

BIN
banks/dmx_strife.op2 Normal file

Binary file not shown.

BIN
banks/drumopl.ibk Normal file

Binary file not shown.

BIN
banks/drumopl.tmb Normal file

Binary file not shown.

BIN
banks/earthsieg.ad Normal file

Binary file not shown.

BIN
banks/fat2.op3 Normal file

Binary file not shown.

BIN
banks/fat4.op3 Normal file

Binary file not shown.

BIN
banks/file49.opl Normal file

Binary file not shown.

BIN
banks/file53.opl Normal file

Binary file not shown.

BIN
banks/gmoconel.tmb Normal file

Binary file not shown.

BIN
banks/gmopl.ibk Normal file

Binary file not shown.

BIN
banks/gmoplmod.tmb Normal file

Binary file not shown.

BIN
banks/hmi-144.skb Normal file

Binary file not shown.

BIN
banks/insmaker_standard.bnk Normal file

Binary file not shown.

BIN
banks/jv_2op.op3 Normal file

Binary file not shown.

BIN
banks/mt32.ibk Normal file

Binary file not shown.

BIN
banks/nemesis.opl Normal file

Binary file not shown.

BIN
banks/std-o3.skb Normal file

Binary file not shown.

BIN
banks/std-sb.skb Normal file

Binary file not shown.

BIN
banks/swtimbr.tmb Normal file

Binary file not shown.

BIN
banks/tim.tim Normal file

Binary file not shown.

BIN
banks/wallace.op3 Normal file

Binary file not shown.

BIN
banks/wallence.tmb Normal file

Binary file not shown.

BIN
banks/warcraft.ad Normal file

Binary file not shown.

BIN
banks/wolfinstein.op2 Normal file

Binary file not shown.

166
banktest.c Normal file
View file

@ -0,0 +1,166 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
void mid_log(char *format, ...) {
va_list ap;
va_start(ap, format);
char msg[1024];
msg[0] = 0;
vsprintf(msg, format, ap);
fprintf(stderr, "%s\n", msg);
va_end(ap);
}
#include "strings.h"
#include "opl3.h"
#include "dmx.h"
#include "bank.h"
#include "audio.h"
#define ARGPARSE_DE
#include "../argparse.h"
bool tst_note(aud_handle *dev, bank_instr *instr, opl3_chip *chip, bank_handle *bank, uint32_t samplerate, uint8_t prog, uint8_t note, uint32_t len, uint32_t pause) {
if(instr[prog].op == b_op0) {
return false;
}
uint8_t drum = prog >= 128;
if(drum) {
note = prog - 128;
}
int16_t buf[2];
if(drum) {
mid_log(STR_TST_DRUM, note, instr[128+note].name);
bank_noteon(bank, chip, 9, note, 127);
}
else {
mid_log(STR_TST_INSTR, note, prog, instr[prog].name);
bank_progchange(bank, chip, 0, prog);
bank_noteon(bank, chip, 0, note, 127);
}
for(int i = 0; i < ((samplerate*len)/1000); i++) {
OPL3_GenerateResampled(chip, buf);
aud_push(dev, buf);
}
bank_noteoff(bank, chip, drum ? 9 : 0, note, 0);
for(int i = 0; i < ((samplerate*pause)/1000); i++) {
OPL3_GenerateResampled(chip, buf);
aud_push(dev, buf);
}
return true;
}
bool tst_play(uint32_t samplerate, const char *dmxname, const char *drumname, uint8_t usedrum, const char *device, uint32_t fsize, uint32_t bufsize, uint8_t widefmt, uint8_t prog, uint8_t note, uint16_t offset, uint32_t len, uint32_t pause) {
bank_instr *instr = bnk_read_banks(dmxname, drumname, usedrum);
if(instr == NULL) {
return false;
}
if((offset == 256) && (instr[prog].op == b_op0)) {
fprintf(stderr, STR_BNK_UNDEF"\n", prog);
free(instr);
return false;
}
opl3_chip *chip = OPL3_Alloc(samplerate, 2);
bank_handle *bank = bank_alloc(chip, instr, 0, 0, 0);
aud_handle dev;
if(aud_open_dev(&dev, device, samplerate, 2, fsize, bufsize, widefmt) ^ 1) {
free(instr);
free(chip);
free(bank);
return false;
}
samplerate = dev.samplerate;
OPL3_Rate(chip, samplerate);
bool played = false;
if(offset != 256) {
for(int z = offset; z < 256; z++) {
played |= tst_note(&dev, instr, chip, bank, samplerate, z, note, len, pause);
}
}
else {
played = tst_note(&dev, instr, chip, bank, samplerate, prog, note, len, pause);
}
if(played ^ 1) {
fprintf(stderr, STR_BNK_NONE"\n");
}
aud_flush(&dev);
aud_close(&dev);
free(instr);
free(chip);
free(bank);
return played;
}
int main(int argc, char **argv) {
int32_t samplerate = 48000;
char *device = "default";
int32_t bufsize = 0;
int32_t fsize = 32;
int32_t widefmt = 0;
char *dmxname = NULL;
char *drumname = NULL;
int32_t usedrum = 0;
int32_t prog = 256;
int32_t dprog = 256;
int32_t mprog = 256;
int32_t note = 36;
int32_t len = 750;
int32_t pause = 250;
int32_t offset = 256;
argp_t *argspec = arg_alloc(15);
arg_add_nstr(argspec, STR_ARGS_BANK, true, &dmxname);
arg_add_nint(argspec, STR_ARGS_OFFSET, false, 0, 255, &offset);
arg_add_int(argspec, 'r', STR_ARGS_SRATE, 8000, 384000, &samplerate);
arg_add_str(argspec, 'd', STR_ARGS_DEVICE, &device);
arg_add_int(argspec, 'b', STR_ARGS_BUFFER, 0, 1048576, &bufsize);
arg_add_int(argspec, 'f', STR_ARGS_FRAMES, 2, 8192, &fsize);
arg_add_bool(argspec, 'w', STR_ARGS_WIDEFT, &widefmt);
arg_add_int(argspec, 'n', STR_ARGS_NOTE, 0, 127, &note);
arg_add_int(argspec, 'l', STR_ARGS_LENGTH, 10, 60000, &len);
arg_add_int(argspec, 'p', STR_ARGS_PAUSE, 10, 60000, &pause);
arg_add_int(argspec, 'i', STR_ARGS_INSTR, 0, 255, &prog);
arg_add_int(argspec, 'm', STR_ARGS_PERC, 0, 127, &mprog);
arg_add_int(argspec, 'g', STR_ARGS_DPERC, 0, 46, &dprog);
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
arg_add_bool(argspec, 'Q', STR_ARGS_USEDRM, &usedrum);
if(arg_parse(argspec, argc, argv) ^ 1) {
return 1;
}
if(dprog != 256) {
if((prog != 256) || (mprog != 256)) {
fprintf(stderr, STR_TST_NDDRUM"\n");
return 1;
}
prog = dprog + 128 + 35;
}
else if(mprog != 256) {
if((prog != 256) || (dprog != 256)) {
fprintf(stderr, STR_TST_NMDRUM"\n");
return 1;
}
prog = mprog + 128;
}
if((offset != 256) && (prog != 256)) {
fprintf(stderr, STR_TST_NOFFS"\n");
return 1;
}
else if((offset == 256) && (prog == 256)) {
offset = 0;
}
if(tst_play(samplerate, dmxname, drumname, usedrum, device, fsize, bufsize, widefmt, prog, note, offset, len, pause) ^ 1) {
return 1;
}
return 0;
}

925
dmx.h Normal file
View file

@ -0,0 +1,925 @@
enum bank_op {
b_op2,
b_op4,
b_op22,
b_op0
};
typedef struct {
uint8_t tremolo;
uint8_t vibrato;
uint8_t sustaining;
uint8_t ksr;
uint8_t mult;
uint8_t ksl;
uint8_t level;
uint8_t attack;
uint8_t decay;
uint8_t sustain;
uint8_t release;
uint8_t waveform;
} bank_operator;
typedef struct {
bank_operator ops[2];
int16_t detune;
int16_t offset;
uint8_t feedback;
uint8_t am;
} bank_pair;
typedef struct {
bank_pair channels[2];
uint8_t op;
uint8_t fixed;
uint8_t percnum;
char name[32];
} bank_instr;
uint32_t bnk_read_mhbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t minsize, uint32_t maxsize, const char *hdr, uint32_t hdrsize) {
int err;
FILE *fd = fopen(filename, "rb");
if(fd == NULL) {
err = errno;
fprintf(stderr, STR_BNK_OPNERR"\n", filename, strerror(err), err);
return 0;
}
if(fseek(fd, 0L, SEEK_END)) {
err = errno;
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
fclose(fd);
return 0;
}
long size = ftell(fd);
if(size < 0L) {
err = errno;
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
fclose(fd);
return 0;
}
if(fseek(fd, 0L, SEEK_SET)) {
err = errno;
fprintf(stderr, STR_BNK_EGEN"\n", filename, strerror(err), err);
fclose(fd);
return 0;
}
if(size < (hdrsize+minsize)) {
fprintf(stderr, STR_BNK_ESHORT"\n", filename);
fclose(fd);
return 0;
}
if(hdrsize > 0) {
uint8_t header[hdrsize];
if(fread(header, 1, hdrsize, fd) != hdrsize) {
err = feof(fd);
fprintf(stderr, err == 0 ? STR_BNK_EIO"\n" : STR_BNK_EEOF"\n", filename);
fclose(fd);
return 0;
}
if(memcmp(header, hdr, hdrsize) != 0) {
fprintf(stderr, STR_BNK_EMFHDR"\n", filename);
fclose(fd);
return 0;
}
size -= hdrsize;
}
size = ((maxsize > 0) && (size > maxsize)) ? maxsize : size;
*data = malloc(size);
if(fread(*data, 1, size, fd) != size) {
err = feof(fd);
fprintf(stderr, err == 0 ? STR_BNK_EIO"\n" : STR_BNK_EEOF"\n", filename);
fclose(fd);
free(*data);
return 0;
}
fclose(fd);
*instr = malloc(sizeof(bank_instr)*256);
return size;
}
uint32_t bnk_read_mbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t minsize, uint32_t maxsize) {
return bnk_read_mhbank(data, instr, filename, minsize, maxsize, NULL, 0);
}
uint32_t bnk_read_hbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t size, const char *hdr, uint32_t hdrsize) {
return bnk_read_mhbank(data, instr, filename, size, size, hdr, hdrsize);
}
uint32_t bnk_read_sbank(uint8_t **data, bank_instr **instr, const char *filename, uint32_t size) {
return bnk_read_mhbank(data, instr, filename, size, size, NULL, 0);
}
uint16_t dmx_read_uint16(uint8_t *data) {
uint16_t value = 0;
for(int h = 0; h < 2; h++) {
value |= ((uint16_t)data[1-h]);
value <<= h < 1 ? 8 : 0;
}
return value;
}
uint16_t dmx_read_uint16be(uint8_t *data) {
uint16_t value = 0;
for(int h = 0; h < 2; h++) {
value |= ((uint16_t)data[h]);
value <<= h < 1 ? 8 : 0;
}
return value;
}
void dmx_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->fixed = ((data[0] & 0x01) > 0) | drum;
instr->op = ((data[0] & 0x04) > 0) ? b_op22 : b_op2;
instr->channels[0].detune = 0;
instr->channels[1].detune = ((int16_t)data[2]) - 128;
instr->percnum = data[3] & 0x7f;
data += 4;
for(int ch = 0; ch < 2; ch++) {
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[op].tremolo = (data[op*7+0] & 0x80) > 0;
instr->channels[ch].ops[op].vibrato = (data[op*7+0] & 0x40) > 0;
instr->channels[ch].ops[op].sustaining = (data[op*7+0] & 0x20) > 0;
instr->channels[ch].ops[op].ksr = (data[op*7+0] & 0x10) > 0;
instr->channels[ch].ops[op].mult = data[op*7+0] & 0x0f;
instr->channels[ch].ops[op].attack = (data[op*7+1] >> 4) & 0x0f;
instr->channels[ch].ops[op].decay = data[op*7+1] & 0x0f;
instr->channels[ch].ops[op].sustain = (data[op*7+2] >> 4) & 0x0f;
instr->channels[ch].ops[op].release = data[op*7+2] & 0x0f;
instr->channels[ch].ops[op].waveform = data[op*7+3] & 0x07;
instr->channels[ch].ops[op].ksl = (data[op*7+4] >> 6) & 0x03;
instr->channels[ch].ops[op].level = data[op*7+5] & 0x3f;
}
instr->channels[ch].feedback = (data[6] >> 1) & 0x07;
instr->channels[ch].am = data[6] & 0x01;
instr->channels[ch].offset = ((int16_t)dmx_read_uint16(data+14)) + 12;
data += 16;
}
}
bank_instr *dmx_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_hbank(&data, &instr, filename, 175*68, "#OPL_II#", 8);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
}
for(int i = 0; i < 175; i++) {
dmx_read_instr(&instr[(i < 128) ? i : (i + 35)], data, i >= 128);
data += 36;
}
for(int i = 0; i < 175; i++) {
bank_instr *ins = &instr[(i < 128) ? i : (i + 35)];
memcpy(ins->name, data, 32);
ins->name[31] = 0;
data += 32;
}
free(odata);
return instr;
}
void tmb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->name[0] = 0;
instr->fixed = drum;
instr->op = b_op2;
for(int op = 0; op < 2; op++) {
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
}
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
instr->channels[0].am = data[10] & 0x01;
instr->percnum = drum ? (data[11] & 0x7f) : 0;
instr->channels[0].offset = drum ? 0 : ((int8_t)data[11]);
instr->channels[0].detune = 0;
}
bank_instr *tmb_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_sbank(&data, &instr, filename, 256*13);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 256; i++) {
tmb_read_instr(&instr[i], data, i >= 128);
data += 13;
}
free(odata);
return instr;
}
void ibk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->fixed = drum;
instr->op = b_op2;
for(int op = 0; op < 2; op++) {
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
}
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
instr->channels[0].am = data[10] & 0x01;
instr->percnum = drum ? (data[13] & 0x7f) : 0;
instr->channels[0].offset = drum ? 0 : ((int8_t)data[13]);
instr->channels[0].detune = 0;
}
bank_instr *ibk_read_bank(const char *filename, uint8_t drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_hbank(&data, &instr, filename, 128*25, "IBK\x1a", 4);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 128; i++) {
instr[i + (drum ? 0 : 128)].op = b_op0;
instr[i + (drum ? 0 : 128)].name[0] = 0;
}
for(int i = 0; i < 128; i++) {
ibk_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
data += 16;
}
for(int i = 0; i < 128; i++) {
bank_instr *ins = &instr[i + (drum ? 128 : 0)];
memcpy(ins->name, data, 8);
ins->name[8] = 0;
data += 9;
}
free(odata);
return instr;
}
void sb_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
for(int h = 0; h < 28; h++) {
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
}
instr->name[28] = 0;
instr->percnum = data[35] & 0x7f;
instr->fixed = drum;
instr->op = b_op2;
data += 36;
for(int op = 0; op < 2; op++) {
instr->channels[0].ops[op].tremolo = (data[op+0] & 0x80) > 0;
instr->channels[0].ops[op].vibrato = (data[op+0] & 0x40) > 0;
instr->channels[0].ops[op].sustaining = (data[op+0] & 0x20) > 0;
instr->channels[0].ops[op].ksr = (data[op+0] & 0x10) > 0;
instr->channels[0].ops[op].mult = data[op+0] & 0x0f;
instr->channels[0].ops[op].attack = (data[op+4] >> 4) & 0x0f;
instr->channels[0].ops[op].decay = data[op+4] & 0x0f;
instr->channels[0].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
instr->channels[0].ops[op].release = data[op+6] & 0x0f;
instr->channels[0].ops[op].waveform = data[op+8] & 0x07;
instr->channels[0].ops[op].ksl = (data[op+2] >> 6) & 0x03;
instr->channels[0].ops[op].level = data[op+2] & 0x3f;
}
instr->channels[0].feedback = (data[10] >> 1) & 0x07;
instr->channels[0].am = data[10] & 0x01;
instr->channels[0].offset = 0;
instr->channels[0].detune = 0;
}
bank_instr *sb_read_bank(const char *filename, uint8_t drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*52);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 128; i++) {
instr[i + (drum ? 0 : 128)].op = b_op0;
instr[i + (drum ? 0 : 128)].name[0] = 0;
}
for(int i = 0; i < 128; i++) {
sb_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
data += 52;
}
free(odata);
return instr;
}
void sb3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
for(int h = 0; h < 28; h++) {
instr->name[h] = (data[h] == 0x1a) ? ' ' : data[h];
}
instr->name[28] = 0;
instr->percnum = data[35] & 0x7f;
instr->fixed = drum;
instr->op = (data[0] == '4') ? b_op4 : b_op2;
data += 36;
for(int ch = 0; ch < 2; ch++) {
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[op].tremolo = (data[op+0] & 0x80) > 0;
instr->channels[ch].ops[op].vibrato = (data[op+0] & 0x40) > 0;
instr->channels[ch].ops[op].sustaining = (data[op+0] & 0x20) > 0;
instr->channels[ch].ops[op].ksr = (data[op+0] & 0x10) > 0;
instr->channels[ch].ops[op].mult = data[op+0] & 0x0f;
instr->channels[ch].ops[op].attack = (data[op+4] >> 4) & 0x0f;
instr->channels[ch].ops[op].decay = data[op+4] & 0x0f;
instr->channels[ch].ops[op].sustain = (data[op+6] >> 4) & 0x0f;
instr->channels[ch].ops[op].release = data[op+6] & 0x0f;
instr->channels[ch].ops[op].waveform = data[op+8] & 0x07;
instr->channels[ch].ops[op].ksl = (data[op+2] >> 6) & 0x03;
instr->channels[ch].ops[op].level = data[op+2] & 0x3f;
}
instr->channels[ch].feedback = (data[10] >> 1) & 0x07;
instr->channels[ch].am = data[10] & 0x01;
instr->channels[ch].offset = 0;
instr->channels[ch].detune = 0;
data += 11;
}
}
bank_instr *sb3_read_bank(const char *filename, uint8_t drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_sbank(&data, &instr, filename, 128*60);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 128; i++) {
instr[i + (drum ? 0 : 128)].op = b_op0;
instr[i + (drum ? 0 : 128)].name[0] = 0;
}
for(int i = 0; i < 128; i++) {
sb3_read_instr(&instr[i + (drum ? 128 : 0)], data, drum);
data += 60;
}
free(odata);
return instr;
}
void op3_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->percnum = data[1] & 0x7f;
instr->fixed = drum;
instr->op = ((data[0] & 0x01) > 0) ? b_op4 : b_op2;
data += 2;
for(int ch = 0; ch < 2; ch++) {
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0;
instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0;
instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0;
instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0;
instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f;
instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f;
instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f;
instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f;
instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f;
instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07;
instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03;
instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f;
}
instr->channels[ch].feedback = (data[5] >> 1) & 0x07;
instr->channels[ch].am = data[5] & 0x01;
instr->channels[ch].offset = 0;
instr->channels[ch].detune = 0;
data += 11;
}
}
bank_instr *op3_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 16, 256*24+16, "Junglevision Patch File\x1a", 24);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
}
data += 8;
uint16_t nmelo = dmx_read_uint16(&data[0]);
uint16_t ndrum = dmx_read_uint16(&data[2]);
uint16_t omelo = dmx_read_uint16(&data[4]);
uint16_t odrum = dmx_read_uint16(&data[6]);
data += 8;
if((((nmelo+ndrum)*24+16) > size) || ((omelo+nmelo) > 128) || ((odrum+ndrum) > 128)) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
for(int i = 0; i < nmelo; i++) {
op3_read_instr(&instr[i + omelo], data, 0);
data += 24;
}
for(int i = 0; i < ndrum; i++) {
op3_read_instr(&instr[i + odrum + 128], data, 1);
data += 24;
}
free(odata);
return instr;
}
void ad_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum, uint16_t isize) {
if(isize < 12) {
return;
}
instr->percnum = data[0] & 0x7f;
instr->fixed = drum;
instr->op = (isize >= 23) ? b_op4 : b_op2;
data += 1;
instr->channels[0].feedback = instr->channels[1].feedback = (data[5] >> 1) & 0x07;
instr->channels[0].am = data[5] & 0x01;
instr->channels[1].am = (data[5] & 0x80) > 0;
for(int ch = 0; ch < ((isize >= 23) ? 2 : 1); ch++) {
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[op].tremolo = (data[op*6+0] & 0x80) > 0;
instr->channels[ch].ops[op].vibrato = (data[op*6+0] & 0x40) > 0;
instr->channels[ch].ops[op].sustaining = (data[op*6+0] & 0x20) > 0;
instr->channels[ch].ops[op].ksr = (data[op*6+0] & 0x10) > 0;
instr->channels[ch].ops[op].mult = data[op*6+0] & 0x0f;
instr->channels[ch].ops[op].attack = (data[op*6+2] >> 4) & 0x0f;
instr->channels[ch].ops[op].decay = data[op*6+2] & 0x0f;
instr->channels[ch].ops[op].sustain = (data[op*6+3] >> 4) & 0x0f;
instr->channels[ch].ops[op].release = data[op*6+3] & 0x0f;
instr->channels[ch].ops[op].waveform = data[op*6+4] & 0x07;
instr->channels[ch].ops[op].ksl = (data[op*6+1] >> 6) & 0x03;
instr->channels[ch].ops[op].level = data[op*6+1] & 0x3f;
}
instr->channels[ch].offset = 0;
instr->channels[ch].detune = 0;
data += 11;
}
}
bank_instr *ad_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mbank(&data, &instr, filename, 2, 256*20+2);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
}
for(int i = 0;; i++, data += 6) {
if((i*6) >= size) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
uint8_t prog = data[0];
if(prog == 0xff) {
break;
}
if(prog >= 128) {
continue;
}
uint8_t bank = data[1];
if((bank != 0) && (bank != 127)) {
continue;
}
uint16_t offs = dmx_read_uint16(data+2);
uint16_t isize;
if(((offs+2) > size) || ((offs+(isize = dmx_read_uint16(odata+offs))) > size) || (isize < 2)) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
ad_read_instr(&instr[prog + ((bank == 127) ? 128 : 0)], odata+(offs+2), bank == 127, isize-2);
}
free(odata);
return instr;
}
void bk_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
instr->fixed = drum;
instr->op = b_op2;
for(int op = 0; op < 2; op++) {
instr->channels[0].ops[op].tremolo = data[op*13+9] & 0x01;
instr->channels[0].ops[op].vibrato = data[op*13+10] & 0x01;
instr->channels[0].ops[op].sustaining = data[op*13+5] & 0x01;
instr->channels[0].ops[op].ksr = data[op*13+11] & 0x01;
instr->channels[0].ops[op].mult = data[op*13+1] & 0x0f;
instr->channels[0].ops[op].attack = data[op*13+3] & 0x0f;
instr->channels[0].ops[op].decay = data[op*13+6] & 0x0f;
instr->channels[0].ops[op].sustain = data[op*13+4] & 0x0f;
instr->channels[0].ops[op].release = data[op*13+7] & 0x0f;
instr->channels[0].ops[op].waveform = data[26+op] & 0x07;
instr->channels[0].ops[op].ksl = data[op*13+0] & 0x03;
instr->channels[0].ops[op].level = data[op*13+8] & 0x3f;
}
instr->channels[0].feedback = data[2] & 0x07;
instr->channels[0].am = (data[25] & 0x01) ^ 1;
instr->channels[0].offset = 0;
instr->channels[0].detune = 0;
}
bank_instr *bk_read_bank(const char *filename, uint8_t drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mbank(&data, &instr, filename, 28, 256*42+28);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
if(memcmp(data+2, "ADLIB-", 6) != 0) {
fprintf(stderr, STR_BNK_EMFHDR"\n", filename);
free(odata);
free(instr);
return NULL;
}
data += 8;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
}
// uint16_t nnorm = data[0];
uint32_t ninst = dmx_read_uint16(&data[2]);
data += 20;
if((ninst*42+28) > size) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
data += ninst*12;
int pos = 0;
for(int i = 0; i < ninst; i++, data += 30) {
if(data[0] != 0) {
continue;
}
bk_read_instr(&instr[pos + (drum ? 128 : 0)], data+2, drum);
uint8_t *ndata = odata+(i*12+28);
instr[pos + (drum ? 128 : 0)].percnum = ndata[2] & 0x7f;
memcpy(instr[pos + (drum ? 128 : 0)].name, ndata+3, 8);
instr[pos + (drum ? 128 : 0)].name[8] = 0;
if(++pos == 128) {
break;
}
}
free(odata);
return instr;
}
void tim_read_instr(bank_instr *instr, uint8_t *data) {
instr->percnum = 0;
instr->fixed = 0;
instr->op = b_op2;
for(int op = 0; op < 2; op++) {
instr->channels[0].ops[op].tremolo = dmx_read_uint16(&data[op*26+18]) & 0x01;
instr->channels[0].ops[op].vibrato = dmx_read_uint16(&data[op*26+20]) & 0x01;
instr->channels[0].ops[op].sustaining = dmx_read_uint16(&data[op*26+10]) & 0x01;
instr->channels[0].ops[op].ksr = dmx_read_uint16(&data[op*26+22]) & 0x01;
instr->channels[0].ops[op].mult = dmx_read_uint16(&data[op*26+2]) & 0x0f;
instr->channels[0].ops[op].attack = dmx_read_uint16(&data[op*26+6]) & 0x0f;
instr->channels[0].ops[op].decay = dmx_read_uint16(&data[op*26+12]) & 0x0f;
instr->channels[0].ops[op].sustain = dmx_read_uint16(&data[op*26+8]) & 0x0f;
instr->channels[0].ops[op].release = dmx_read_uint16(&data[op*26+14]) & 0x0f;
instr->channels[0].ops[op].waveform = dmx_read_uint16(&data[52+op*2]) & 0x07;
instr->channels[0].ops[op].ksl = dmx_read_uint16(&data[op*26+0]) & 0x03;
instr->channels[0].ops[op].level = dmx_read_uint16(&data[op*26+16]) & 0x3f;
}
instr->channels[0].feedback = dmx_read_uint16(&data[4]) & 0x07;
instr->channels[0].am = (dmx_read_uint16(&data[50]) & 0x01) ^ 1;
instr->channels[0].offset = 0;
instr->channels[0].detune = 0;
}
bank_instr *tim_read_bank(const char *filename, uint8_t drum) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mbank(&data, &instr, filename, 6, 256*65+6);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
for(int i = 0; i < 256; i++) {
instr[i].op = b_op0;
instr[i].name[0] = 0;
}
uint32_t ninst = dmx_read_uint16(&data[2]);
data += 6;
if((ninst*65+6) > size) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
data += ninst*9;
for(int i = 0; (i < ninst) && (i < 128); i++, data += 56) {
tim_read_instr(&instr[i + (drum ? 128 : 0)], data);
uint8_t *ndata = odata+(i*9+6);
memcpy(instr[i + (drum ? 128 : 0)].name, ndata, 8);
instr[i + (drum ? 128 : 0)].name[8] = 0;
}
free(odata);
return instr;
}
void wopl_read_instr(bank_instr *instr, uint8_t *data, uint8_t drum) {
memcpy(instr->name, data, 32);
instr->name[31] = 0;
data += 32;
instr->percnum = data[6] & 0x7f;
instr->fixed = drum;
instr->op = (data[7] & 0x01) ? (((data[7] & 0x02) > 0) ? b_op22 : b_op4) : b_op2;
instr->channels[0].offset = (int16_t)dmx_read_uint16be(&data[0]);
instr->channels[0].detune = 0;
if(instr->op != b_op2) {
instr->channels[1].offset = (int16_t)dmx_read_uint16be(&data[2]);
instr->channels[0].detune = (int8_t)data[5];
}
instr->channels[0].am = data[8] & 0x01;
instr->channels[0].feedback = (data[8] >> 1) & 0x07;
if(instr->op != b_op2) {
instr->channels[1].am = data[9] & 0x01;
instr->channels[1].feedback = (data[9] >> 1) & 0x07;
}
data += 10;
for(int ch = 0; ch < ((instr->op != b_op2) ? 2 : 1); ch++) {
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[1-op].tremolo = (data[op*5+0] & 0x80) > 0;
instr->channels[ch].ops[1-op].vibrato = (data[op*5+0] & 0x40) > 0;
instr->channels[ch].ops[1-op].sustaining = (data[op*5+0] & 0x20) > 0;
instr->channels[ch].ops[1-op].ksr = (data[op*5+0] & 0x10) > 0;
instr->channels[ch].ops[1-op].mult = data[op*5+0] & 0x0f;
instr->channels[ch].ops[1-op].attack = (data[op*5+2] >> 4) & 0x0f;
instr->channels[ch].ops[1-op].decay = data[op*5+2] & 0x0f;
instr->channels[ch].ops[1-op].sustain = (data[op*5+3] >> 4) & 0x0f;
instr->channels[ch].ops[1-op].release = data[op*5+3] & 0x0f;
instr->channels[ch].ops[1-op].waveform = data[op*5+4] & 0x07;
instr->channels[ch].ops[1-op].ksl = (data[op*5+1] >> 6) & 0x03;
instr->channels[ch].ops[1-op].level = data[op*5+1] & 0x3f;
}
data += 10;
}
}
bank_instr *wopl_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256*62+9, 512*66+77, "WOPL3-BANK", 10);
if(size == 0) {
return NULL;
}
uint8_t *odata = data;
int stride = (data[7] != 0) ? 66 : 62;
int off = (data[7] != 0) ? 77 : 9 + ((data[4] == 2) ? 102 : 0);
int off2 = (data[4] == 2) ? (128*stride) : 0;
if((256*stride+off+off2) > size) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(odata);
free(instr);
return NULL;
}
data += off;
for(int i = 0; i < 128; i++) {
wopl_read_instr(&instr[i], data, 0);
data += stride;
}
data += off2;
for(int i = 0; i < 128; i++) {
wopl_read_instr(&instr[i + 128], data, 1);
data += stride;
}
free(odata);
return instr;
}
// SKB!
// OO-NNNNN NAME[N] FPPPPPPP AFFFAFFF
// DDDDDDDD OOOOOOOO TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR TVSKMMMM KKLLLLLL AAAADDDD SSSSRRRR -WWW-WWW
uint32_t skb_read_instr(bank_instr *instr, uint8_t *data, uint32_t pos, uint32_t size) {
if((pos + 1) > size) {
return 0;
}
data += pos;
instr->op = (data[0] >> 6) & 0x03;
uint8_t nlen = data[0] & 0x1f;
if((pos + 1 + nlen) > size) {
return 0;
}
memcpy(instr->name, data+1, nlen);
instr->name[nlen] = 0;
pos += 1 + nlen;
if(instr->op == b_op0) {
return pos;
}
int numch = (instr->op == b_op2) ? 1 : 2;
if((pos + 1 + (12 * numch)) > size) {
return 0;
}
data += 1 + nlen;
instr->fixed = (data[0] & 0x80) > 0;
instr->percnum = data[0] & 0x7f;
instr->channels[0].am = (data[1] & 0x08) > 0;
instr->channels[0].feedback = data[1] & 0x07;
if(numch == 2) {
instr->channels[1].am = (data[1] & 0x80) > 0;
instr->channels[1].feedback = (data[1] >> 4) & 0x07;
}
data += 2;
for(int ch = 0; ch < numch; ch++) {
instr->channels[ch].detune = ((int16_t)data[0]) - 128;
instr->channels[ch].offset = ((int16_t)data[1]) - 128;
data += 2;
for(int op = 0; op < 2; op++) {
instr->channels[ch].ops[op].tremolo = (data[0] & 0x80) > 0;
instr->channels[ch].ops[op].vibrato = (data[0] & 0x40) > 0;
instr->channels[ch].ops[op].sustaining = (data[0] & 0x20) > 0;
instr->channels[ch].ops[op].ksr = (data[0] & 0x10) > 0;
instr->channels[ch].ops[op].mult = data[0] & 0x0f;
instr->channels[ch].ops[op].ksl = (data[1] >> 6) & 0x03;
instr->channels[ch].ops[op].level = data[1] & 0x3f;
instr->channels[ch].ops[op].attack = (data[2] >> 4) & 0x0f;
instr->channels[ch].ops[op].decay = data[2] & 0x0f;
instr->channels[ch].ops[op].sustain = (data[3] >> 4) & 0x0f;
instr->channels[ch].ops[op].release = data[3] & 0x0f;
data += 4;
}
instr->channels[ch].ops[0].waveform = (data[0] >> 4) & 0x07;
instr->channels[ch].ops[1].waveform = data[0] & 0x07;
data += 1;
}
return pos + 2 + (11 * numch);
}
bank_instr *skb_read_bank(const char *filename) {
uint8_t *data;
bank_instr *instr;
uint32_t size = bnk_read_mhbank(&data, &instr, filename, 256, 256*56, "SKB!", 4);
if(size == 0) {
return NULL;
}
uint32_t pos = 0;
for(int i = 0; i < 256; i++) {
pos = skb_read_instr(&instr[i], data, pos, size);
if(pos == 0) {
fprintf(stderr, STR_BNK_EEOD"\n", filename);
free(data);
free(instr);
return NULL;
}
}
free(data);
return instr;
}
bank_instr *bnk_read_bank(const char *filename, uint8_t drum) {
int len = 0;
int dp = 0;
char ch;
while(ch = filename[len++]) {
if(ch == '.') {
dp = len;
}
}
if(dp == 0) {
fprintf(stderr, STR_BNK_UNKN"\n", filename);
return NULL;
}
char ext[len -= dp];
for(int c = 0; c < len; c++) {
ext[c] = filename[dp+c];
if((ext[c] >= 'A') && (ext[c] <= 'Z')) {
ext[c] = ext[c] - 'A' + 'a';
}
}
if(strcmp(ext, "skb") == 0) {
return skb_read_bank(filename);
}
else if((strcmp(ext, "dmx") == 0) || (strcmp(ext, "op2") == 0) || (strcmp(ext, "lmp") == 0)) {
return dmx_read_bank(filename);
}
else if(strcmp(ext, "tmb") == 0) {
return tmb_read_bank(filename);
}
else if(strcmp(ext, "ibk") == 0) {
return ibk_read_bank(filename, drum);
}
else if(strcmp(ext, "sb") == 0) {
return sb_read_bank(filename, drum);
}
else if(strcmp(ext, "o3") == 0) {
return sb3_read_bank(filename, drum);
}
else if(strcmp(ext, "op3") == 0) {
return op3_read_bank(filename);
}
else if((strcmp(ext, "ad") == 0) || (strcmp(ext, "opl") == 0)) {
return ad_read_bank(filename);
}
else if(strcmp(ext, "bnk") == 0) {
return bk_read_bank(filename, drum);
}
else if((strcmp(ext, "tim") == 0) || (strcmp(ext, "snd") == 0)) {
return tim_read_bank(filename, drum);
}
// else if(strcmp(ext, "wopl") == 0) {
// return wopl_read_bank(filename);
// }
fprintf(stderr, STR_BNK_UNKN"\n", filename);
return NULL;
}
bank_instr *bnk_read_banks(const char *filename, const char *drumname, uint8_t usedrum) {
bank_instr *ins1 = bnk_read_bank(filename, usedrum);
if((ins1 != NULL) && (drumname != NULL)) {
bank_instr *ins2 = bnk_read_bank(drumname, usedrum ^ 1);
if(ins2 != NULL) {
memcpy(ins1+128, ins2+128, sizeof(bank_instr)*128);
free(ins2);
}
else {
free(ins1);
ins1 = NULL;
}
}
return ins1;
}
uint32_t skb_write_instr(bank_instr *instr, uint8_t *data, uint32_t pos) {
data += pos;
uint8_t nlen = strlen(instr->name);
data[0] = (instr->op << 6) | nlen;
memcpy(data+1, instr->name, nlen);
pos += 1 + nlen;
if(instr->op == b_op0) {
return pos;
}
int numch = (instr->op == b_op2) ? 1 : 2;
data += 1 + nlen;
data[0] = (instr->fixed << 7) | instr->percnum;
data[1] = ((numch == 2) ? ((instr->channels[1].am << 7) | (instr->channels[1].feedback << 4)) : 0) | (instr->channels[0].am << 3) | instr->channels[0].feedback;
data += 2;
for(int ch = 0; ch < numch; ch++) {
data[0] = (uint8_t)((int16_t)(instr->channels[ch].detune + 128));
data[1] = (uint8_t)((int16_t)(instr->channels[ch].offset + 128));
data += 2;
for(int op = 0; op < 2; op++) {
data[0] = (instr->channels[ch].ops[op].tremolo << 7) | (instr->channels[ch].ops[op].vibrato << 6) | (instr->channels[ch].ops[op].sustaining << 5) |
(instr->channels[ch].ops[op].ksr << 4) | instr->channels[ch].ops[op].mult;
data[1] = (instr->channels[ch].ops[op].ksl << 6) | instr->channels[ch].ops[op].level;
data[2] = (instr->channels[ch].ops[op].attack << 4) | instr->channels[ch].ops[op].decay;
data[3] = (instr->channels[ch].ops[op].sustain << 4) | instr->channels[ch].ops[op].release;
data += 4;
}
data[0] = (instr->channels[ch].ops[0].waveform << 4) | instr->channels[ch].ops[1].waveform;
data += 1;
}
return pos + 2 + (11 * numch);
}
bool skb_write_bank(bank_instr *instr, const char *filename) {
int err;
FILE *fd = fopen(filename, "wb");
if(fd == NULL) {
err = errno;
fprintf(stderr, STR_BNK_OPNERR"\n", filename, strerror(err), err);
return false;
}
if(fwrite("SKB!", 1, 4, fd) != 4) {
fprintf(stderr, STR_BNK_EWIO"\n", filename);
fclose(fd);
return false;
}
uint8_t *data = malloc(256*56);
uint32_t size = 0;
for(int i = 0; i < 256; i++) {
size = skb_write_instr(&instr[i], data, size);
}
if(fwrite(data, 1, size, fd) != size) {
fprintf(stderr, STR_BNK_EWIO"\n", filename);
fclose(fd);
free(data);
return false;
}
fclose(fd);
free(data);
return true;
}

574
midi.h Normal file
View file

@ -0,0 +1,574 @@
enum mid_event {
midev_noteoff = 0x80,
midev_noteon = 0x90,
midev_aftertouch = 0xa0,
midev_control = 0xb0,
midev_progchg = 0xc0,
midev_chnpressure = 0xd0,
midev_pitchbend = 0xe0,
midev_sysex = 0xf0,
midev_songpos = 0xf2,
midev_songsel = 0xf3,
midev_tunereq = 0xf6,
midev_endsysex = 0xf7,
midev_clock = 0xf8,
midev_start = 0xfa,
midev_continue = 0xfb,
midev_stop = 0xfc,
midev_actsense = 0xfe,
midev_meta = 0xff
};
enum mid_meta {
midmt_seqnum = 0x00, // nn nn
midmt_text = 0x01, // text...
midmt_copyright = 0x02, // text...
midmt_trackname = 0x03, // text...
midmt_instrname = 0x04, // text...
midmt_lyric = 0x05, // text...
midmt_marker = 0x06, // text...
midmt_cuepoint = 0x07, // text...
midmt_chnprefix = 0x20, // cc
midmt_endtrack = 0x2f, //
midmt_tempo = 0x51, // tt tt tt
midmt_smpte = 0x54, // hr mn se fr ff
midmt_timesig = 0x58, // nn dd cc bb
midmt_keysig = 0x59, // sf mi
midmt_seqspec = 0x7f // data...
};
typedef struct {
uint8_t *buffer;
uint32_t size;
uint32_t pos;
uint8_t status;
uint8_t ending;
uint32_t wait;
uint16_t trknum;
} mid_track;
typedef struct {
mid_track *track;
uint16_t tracks;
uint32_t tpqn;
uint32_t uspb;
uint32_t ticktime;
} mid_handle;
const char *mid_hdr = "MThd";
const char *mid_trk = "MTrk";
#define MID_DEFTEMPO 500000
void mid_settempo(mid_handle *mid, uint32_t tempo) {
mid->uspb = tempo;
mid->ticktime = mid->uspb / mid->tpqn;
}
uint32_t mid_read_uint32(uint8_t *data) {
uint32_t value = 0;
for(int h = 0; h < 4; h++) {
value |= ((uint32_t)data[h]);
value <<= h < 3 ? 8 : 0;
}
return value;
}
uint16_t mid_read_uint16(uint8_t *data) {
uint16_t value = 0;
for(int h = 0; h < 2; h++) {
value |= ((uint16_t)data[h]);
value <<= h < 1 ? 8 : 0;
}
return value;
}
uint32_t midt_read_uint32(mid_track *trk) {
uint32_t value = 0;
for(int h = 0; h < 4; h++) {
if(trk->pos >= trk->size) {
break;
}
value |= ((uint32_t)trk->buffer[trk->pos++]);
value <<= h < 3 ? 8 : 0;
}
return value;
}
uint32_t midt_read_uint24(mid_track *trk) {
uint32_t value = 0;
for(int h = 0; h < 3; h++) {
if(trk->pos >= trk->size) {
break;
}
value |= ((uint32_t)trk->buffer[trk->pos++]);
value <<= h < 2 ? 8 : 0;
}
return value;
}
uint16_t midt_read_uint16(mid_track *trk) {
uint16_t value = 0;
for(int h = 0; h < 2; h++) {
if(trk->pos >= trk->size) {
break;
}
value |= ((uint16_t)trk->buffer[trk->pos++]);
value <<= h < 1 ? 8 : 0;
}
return value;
}
uint8_t midt_read_uint8(mid_track *trk) {
uint8_t value = 0;
if(trk->pos < trk->size) {
value = trk->buffer[trk->pos++];
}
return value;
}
void midt_read_var(mid_track *trk, uint32_t len, uint8_t *chars) {
memset(chars, 0, len);
for(int h = 0; h < len; h++) {
if(trk->pos >= trk->size) {
break;
}
chars[h] = trk->buffer[trk->pos++];
}
}
uint32_t mid_read_vlen(mid_track *trk)
{
uint32_t value;
uint8_t bt;
if(trk->pos >= trk->size) {
return 0;
}
if((value = trk->buffer[trk->pos++]) & 0x80)
{
value &= 0x7f;
do {
if(trk->pos >= trk->size) {
break;
}
value = (value << 7) + ((bt = trk->buffer[trk->pos++]) & 0x7f);
}
while(bt & 0x80);
}
return value;
}
bool mid_read(mid_handle *mid, const char *filename) {
int err;
FILE *fd = fopen(filename, "rb");
if(fd == NULL) {
err = errno;
mid_log(STR_MID_OPNERR, filename, strerror(err), err);
return false;
}
if(fseek(fd, 0L, SEEK_END)) {
err = errno;
mid_log(STR_MID_EGEN, filename, strerror(err), err);
fclose(fd);
return false;
}
long size = ftell(fd);
if(size < 0L) {
err = errno;
mid_log(STR_MID_EGEN, filename, strerror(err), err);
fclose(fd);
return false;
}
if(fseek(fd, 0L, SEEK_SET)) {
err = errno;
mid_log(STR_MID_EGEN, filename, strerror(err), err);
fclose(fd);
return false;
}
if(size < 14) {
mid_log(STR_MID_ESHORT, filename);
fclose(fd);
return false;
}
uint8_t header[14];
if(fread(header, 1, 14, fd) != 14) {
err = feof(fd);
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
fclose(fd);
return false;
}
if(memcmp(header, mid_hdr, 4) != 0) {
mid_log(STR_MID_EMFHDR, filename);
fclose(fd);
return false;
}
if(mid_read_uint32(header+4) != 6) {
mid_log(STR_MID_EHSIZE, filename);
fclose(fd);
return false;
}
bool mtrack;
uint16_t type = mid_read_uint16(header+8);
if(type == 0) {
mtrack = false;
}
else if((type == 1) || (type == 2)) {
mtrack = true;
}
else {
mid_log(STR_MID_ETKFMT, filename);
fclose(fd);
return false;
}
mid->tracks = mid_read_uint16(header+10);
if(mid->tracks == 0) {
mid_log(STR_MID_ENOTRK, filename);
fclose(fd);
return false;
}
else if((mtrack ^ 1) && (mid->tracks > 1)) {
mid_log(STR_MID_ESMULT, filename);
fclose(fd);
return false;
}
mid->tpqn = mid_read_uint16(header+12);
if((mid->tpqn & 0x8000) > 0) {
mid_log(STR_MID_ESMPTE, filename);
fclose(fd);
return false;
}
if(mid->tpqn == 0) {
mid_log(STR_MID_EDZERO, filename);
fclose(fd);
return false;
}
uint32_t esize = 0;
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
if(fread(header, 1, 8, fd) != 8) {
err = feof(fd);
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
fclose(fd);
return false;
}
if(memcmp(header, mid_trk, 4) != 0) {
mid_log(STR_MID_ETKHDR, filename);
fclose(fd);
return false;
}
uint32_t trks = mid_read_uint32(header+4);
if((14 + esize + 8 + trks) > size) {
mid_log(STR_MID_EOUTOF, filename, trk+1);
fclose(fd);
return false;
}
if(fseek(fd, trks, SEEK_CUR)) {
err = errno;
mid_log(STR_MID_EGEN, filename, strerror(err), err);
fclose(fd);
return false;
}
esize += trks + 8;
}
if(fseek(fd, 14L, SEEK_SET)) {
err = errno;
mid_log(STR_MID_EGEN, filename, strerror(err), err);
fclose(fd);
return false;
}
esize -= (mid->tracks * 8);
uint8_t *buf = malloc((sizeof(mid_track) * mid->tracks) + esize);
mid->track = (mid_track*)buf;
buf += (sizeof(mid_track) * mid->tracks);
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
memset((mid->track)+trk, 0, sizeof(mid_track));
if(fread(header, 1, 8, fd) != 8) {
err = feof(fd);
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
fclose(fd);
free(mid->track);
return false;
}
mid->track[trk].size = mid_read_uint32(header+4);
if(fread(buf, 1, mid->track[trk].size, fd) != mid->track[trk].size) {
err = feof(fd);
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
fclose(fd);
free(mid->track);
return false;
}
mid->track[trk].buffer = buf;
mid->track[trk].pos = 0;
mid->track[trk].wait = 0;
mid->track[trk].trknum = trk;
buf += mid->track[trk].size;
}
fclose(fd);
mid_settempo(mid, MID_DEFTEMPO);
return true;
}
void mid_free(mid_handle *mid) {
free(mid->track);
}
void mid_process_meta(mid_handle *mid, mid_track *trk) {
uint8_t meta = midt_read_uint8(trk);
uint32_t size = mid_read_vlen(trk);
switch(meta) {
case midmt_seqnum:
if(size == 0) {
mid_dlog(STR_MID_SEQNUMO, trk->trknum, trk->trknum);
return;
}
else if(size != 2) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
return;
}
mid_dlog(STR_MID_SEQNUM, trk->trknum, midt_read_uint16(trk));
return;
case midmt_text:
case midmt_copyright:
case midmt_trackname:
case midmt_instrname:
case midmt_lyric:
case midmt_marker:
case midmt_cuepoint: {
uint8_t dt[size+1];
dt[size] = 0;
midt_read_var(trk, size, dt);
mid_tlog(meta, dt);
return;
}
case midmt_chnprefix:
if(size != 1) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 1, size);
return;
}
mid_dlog(STR_MID_CHNPFX, trk->trknum, midt_read_uint8(trk));
return;
case midmt_endtrack:
trk->pos += size;
trk->ending = 1;
mid_dlog(STR_MID_END, trk->trknum);
return;
case midmt_tempo:
if(size != 3) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 3, size);
return;
}
mid_settempo(mid, midt_read_uint24(trk));
mid_dlog(STR_MID_TEMPO, 60000000 / mid->uspb);
return;
case midmt_smpte:
if(size != 5) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 5, size);
return;
}
trk->pos += 5;
mid_dlog(STR_MID_SMPTE);
return;
case midmt_timesig: {
if(size != 4) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 4, size);
return;
}
uint8_t n = midt_read_uint8(trk);
uint32_t d = 1 << ((uint32_t)midt_read_uint8(trk));
uint8_t c = midt_read_uint8(trk);
uint8_t b = midt_read_uint8(trk);
mid_dlog(STR_MID_TIMESIG, n, d, c, b);
return;
}
case midmt_keysig: {
if(size != 2) {
trk->pos += size;
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
return;
}
int8_t s = midt_read_uint8(trk);
uint8_t m = midt_read_uint8(trk);
mid_dlog(STR_MID_KEYSIG, s, (m == 0) ? "MAJOR" : ((m == 1) ? "MINOR" : "- ? -"));
return;
}
case midmt_seqspec: {
trk->pos += size;
mid_dlog(STR_MID_SEQDATA, trk->trknum, size);
return;
}
default:
trk->pos += size;
mid_dlog(STR_MID_UNKMETA, trk->trknum, meta, size);
return;
}
}
#ifndef MID_NOEVT
void mid_process(mid_handle *mid, mid_track *trk, bank_handle *bank, opl3_chip *chip) {
#else
void mid_process(mid_handle *mid, mid_track *trk) {
#endif
if(trk->pos >= trk->size) {
return;
}
uint8_t status = trk->buffer[trk->pos++];
if((status & 0x80) == 0) {
status = trk->status;
trk->pos -= 1;
}
else {
trk->status = status;
}
if((status & 0xf0) != 0xf0) {
uint8_t channel = status & 0x0f;
status &= 0xf0;
switch(status) {
case midev_noteoff: {
uint8_t o_key = midt_read_uint8(trk);
uint8_t o_velocity = midt_read_uint8(trk);
#ifndef MID_NOEVT
bank_noteoff(bank, chip, channel, o_key, o_velocity);
#endif
mid_dlog(STR_MID_NOTEOFF, channel+1, o_key, o_velocity);
break;
}
case midev_noteon: {
uint8_t key = midt_read_uint8(trk);
uint8_t velocity = midt_read_uint8(trk);
#ifndef MID_NOEVT
if(velocity == 0) {
bank_noteoff(bank, chip, channel, key, velocity);
}
else {
bank_noteon(bank, chip, channel, key, velocity);
}
#endif
mid_dlog(STR_MID_NOTEON, channel+1, key, velocity);
break;
}
case midev_aftertouch: {
uint8_t pressure = midt_read_uint8(trk);
mid_dlog(STR_MID_PRESS, channel+1, pressure);
break;
}
case midev_control: {
uint8_t control = midt_read_uint8(trk);
uint8_t value = midt_read_uint8(trk);
#ifndef MID_NOEVT
bank_control(bank, chip, channel, control, value);
#endif
mid_dlog(STR_MID_CC, channel+1, control, value);
break;
}
case midev_progchg: {
uint8_t program = midt_read_uint8(trk);
#ifndef MID_NOEVT
bank_progchange(bank, chip, channel, program);
#endif
mid_dlog(STR_MID_PROG, channel+1, program);
break;
}
case midev_chnpressure: {
uint8_t cpressure = midt_read_uint8(trk);
mid_dlog(STR_MID_CPRESS, channel+1, cpressure);
break;
}
case midev_pitchbend: {
uint16_t pb = ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7);
int16_t pitch = ((int16_t)pb) - 0x2000;
#ifndef MID_NOEVT
bank_pitchbend(bank, chip, channel, pitch);
#endif
mid_dlog(STR_MID_PITCH, channel+1, pitch);
break;
}
}
}
else {
switch(status) {
case midev_sysex: {
uint32_t slen = mid_read_vlen(trk);
trk->pos += slen;
mid_dlog(STR_MID_SYSEX, slen);
break;
}
case midev_songpos:
mid_dlog(STR_MID_SONGPOS, ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7));
break;
case midev_songsel:
mid_dlog(STR_MID_SONGSEL, midt_read_uint8(trk));
break;
case midev_tunereq:
mid_dlog(STR_MID_TUNE);
break;
case midev_endsysex: {
uint32_t elen = mid_read_vlen(trk);
trk->pos += elen;
mid_dlog(STR_MID_ESYSEX, elen);
break;
}
case midev_clock:
case midev_start:
case midev_continue:
case midev_stop:
case midev_actsense:
mid_dlog(STR_MID_GENSTAT, status);
break;
case midev_meta:
mid_process_meta(mid, trk);
break;
default:
mid_log(STR_MID_UNKSTAT, status);
break;
}
}
}
#ifndef MID_NOEVT
uint8_t mid_tick(mid_handle *mid, bank_handle *bank, opl3_chip *chip) {
#else
uint8_t mid_tick(mid_handle *mid) {
#endif
uint8_t end = 1;
for(int trk = 0; trk < mid->tracks; trk++) {
mid_track *track = (mid->track)+trk;
if(track->ending) {
continue;
}
if(track->wait > 0) {
track->wait -= 1;
if(track->wait > 0) {
end = 0;
continue;
}
}
while(true) {
if(track->pos > 0) {
#ifndef MID_NOEVT
mid_process(mid, track, bank, chip);
#else
mid_process(mid, track);
#endif
if((track->ending ^ 1) && (track->pos >= track->size)) {
mid_log(STR_MID_EEND, track->trknum);
track->ending = 1;
}
if(track->ending) {
break;
}
}
track->wait = mid_read_vlen(track);
if(track->wait > 0) {
break;
}
}
end &= track->ending;
}
return end ^ 1;
}

55
mididump.c Normal file
View file

@ -0,0 +1,55 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include "strings.h"
uint32_t tick = 0;
void mid_log(char *format, ...) {
va_list ap;
va_start(ap, format);
char msg[1024];
msg[0] = 0;
vsprintf(msg, format, ap);
printf("[%08d] %s\n", tick, msg);
va_end(ap);
}
void mid_dlog(char *format, ...) {
mid_log(format);
}
void mid_tlog(int type, char *text) {
mid_log(STR_MID_TEXT, type, text);
}
#define MID_NOEVT
#include "midi.h"
#define ARGPARSE_DE
#include "../argparse.h"
int main(int argc, char **argv) {
char *midname = NULL;
argp_t *argspec = arg_alloc(1);
arg_add_nstr(argspec, STR_ARGS_MIDI, true, &midname);
if(arg_parse(argspec, argc, argv) ^ 1) {
return 1;
}
mid_handle mid;
if(mid_read(&mid, midname) ^ 1) {
return 1;
}
while(mid_tick(&mid)) {
tick++;
}
mid_free(&mid);
return 0;
}

1042
opl3.h Normal file

File diff suppressed because it is too large Load diff

966
oplplayer.c Normal file
View file

@ -0,0 +1,966 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <ncurses.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include "strings.h"
static const char plr_wheel[] = { '/', '-', '\\', '|'};
static const char plr_notes[] = { 'C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B'};
static const char plr_hexn[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static const char plr_waves[] = { 'S', 'H', 'A', 'P', 's', 'a', 'R', 'D'};
static const char *plr_descs[] = { STR_IMID_TITLE, STR_IMID_INFO, STR_IMID_COPY, STR_IMID_WARN, STR_IMID_LANG, STR_IMID_VER };
static const char plr_phases[] = { 'A', 'D', 'S', 'R'};
int32_t log_debug = 0;
bool karaoke = false;
uint32_t log_pos = 0;
uint32_t mlog_pos;
uint32_t klog_pos;
uint32_t klog_offs;
uint32_t midinf_offs[6];
char *logger[4];
char *mlogger[4];
char *klogger[5];
char *midinfo[6];
#define MINFO_DESC 10
int16_t vu_sum[2];
char *tbuffer;
int last_w = 0;
int last_h = 0;
char bankname[36];
void plr_setbnk(const char *dmxname, const char *drmname) {
int pos = 0;
for(int h = 0; h < (drmname ? 2 : 1); h++) {
const char *filename = h ? drmname : dmxname;
int len = 0;
int dp = 0;
char ch;
while(ch = filename[len++]) {
if((ch == '/') || (ch == '\\')) {
dp = len;
}
}
len -= dp+1;
len = len > (drmname ? 17 : 35) ? (drmname ? 17 : 35) : len;
memcpy(bankname+pos, filename+dp, len);
bankname[pos += len] = (h || (drmname == NULL)) ? 0 : '+';
pos++;
}
}
void mid_log(char *format, ...) {
va_list ap;
va_start(ap, format);
logger[log_pos][0] = 0;
vsnprintf(logger[log_pos++], 116, format, ap);
if(log_pos == 4) {
log_pos = 0;
}
va_end(ap);
}
void mid_mlog(char *format, ...) {
va_list ap;
va_start(ap, format);
mlogger[mlog_pos][0] = 0;
vsnprintf(mlogger[mlog_pos++], 82, format, ap);
if(mlog_pos == 4) {
mlog_pos = 0;
}
va_end(ap);
}
void mid_clog() {
klog_pos = 0;
klog_offs = 0;
if(karaoke) {
for(int z = 0; z < 5; z++) {
klogger[z][0] = 0;
}
}
else {
for(int z = 0; z < 5; z++) {
memset(klogger[z], '-', 115);
klogger[z][115] = 0;
}
}
}
void mid_cmlog() {
karaoke = false;
mlog_pos = 0;
for(int z = 0; z < 4; z++) {
mlogger[z][0] = 0;
}
for(int z = 0; z < 6; z++) {
int len = strlen(plr_descs[z]);
memcpy(midinfo[z], plr_descs[z], len);
memset((midinfo[z])+len, ' ', MINFO_DESC-2-len);
midinfo[z][MINFO_DESC-2] = ':';
midinfo[z][MINFO_DESC-1] = ' ';
midinfo[z][MINFO_DESC] = 0;
midinf_offs[z] = MINFO_DESC;
}
mid_clog();
}
void vu_reset() {
vu_sum[0] = vu_sum[1] = 0;
}
void vu_push(int16_t *buf) {
for(int j = 0; j < 2; j++) {
vu_sum[j] = ((((int32_t)(buf[j] < 0 ? (-buf[j]) : buf[j])) * 2) + ((int32_t)vu_sum[j])) / 3;
}
}
void mid_kar() {
if(karaoke) {
return;
}
karaoke = true;
mid_clog();
}
void mid_info(int type, char *text) {
char *info = midinfo[type];
int32_t offs = midinf_offs[type];
int len = strlen(text);
int max = ((type >= 4) ? 57 : 115);
max -= (offs != MINFO_DESC) ? 2 : 0;
len = (len > (max-offs)) ? (max-offs) : len;
if(len <= 0) {
return;
}
if(offs != MINFO_DESC) {
info[offs++] = ';';
info[offs++] = ' ';
}
memcpy(info+offs, text, len);
offs += len;
info[offs] = 0;
midinf_offs[type] = offs;
}
void mid_tlog(int type, char *text) {
int slen = strlen(text);
for(int h = 0; h < slen; h++) {
text[h] = ((text[h] < 32) || (text[h] > 126)) ? ((h == (slen-1)) ? 0 : '?') : text[h];
}
if((type == 1) && (text[0] == '@')) {
// text += 1;
switch(text[1]) {
case 'T':
mid_info(0, text+2);
return;
case 'I':
mid_info(1, text+2);
return;
case 'K':
mid_info(2, text+2);
return;
case 'W':
mid_info(3, text+2);
return;
case 'L':
mid_info(4, text+2);
return;
case 'V':
mid_info(5, text+2);
return;
}
// text += (text[0] == 0) ? 0 : 1;
}
else if((type == 1) && (text[0] == '%') && (text[1] == '-')) {
mid_kar();
return;
}
else if(type == 1 /* || type == 5 */) {
switch(text[0]) {
case '\\':
mid_kar();
mid_clog();
text += 1;
slen -= 1;
break;
case '/':
// mid_kar();
if(karaoke) {
klog_pos += 1;
klog_offs = 0;
if(klog_pos == 5) {
mid_clog();
}
text += 1;
slen -= 1;
}
break;
}
if(karaoke) {
int len;
while(slen > 0) {
len = (slen > (115-klog_offs)) ? (115-klog_offs) : slen;
if(len <= 0) {
break;
}
memcpy((klogger[klog_pos])+klog_offs, text, len);
klog_offs += len;
klogger[klog_pos][klog_offs] = 0;
slen -= len;
if(slen > 0) {
klog_pos += 1;
klog_offs = 0;
if(klog_pos == 5) {
mid_clog();
}
}
}
return;
}
}
if(log_debug || (type <= 2) || (type >= 5)) {
mid_mlog(STR_MID_TEXT, type, text);
}
}
void mid_dlog(char *format, ...) {
if(log_debug) {
mid_mlog(format);
}
}
#include "opl3.h"
#include "dmx.h"
#include "bank.h"
#include "midi.h"
#include "audio.h"
#define ARGPARSE_DE
#include "../argparse.h"
#define PLR_REFRESH 16
#define PLR_BLACK 1
#define PLR_DRED 2
#define PLR_DGREEN 3
#define PLR_DYELLOW 4
#define PLR_DBLUE 5
#define PLR_DMAGENTA 6
#define PLR_DCYAN 7
#define PLR_GRAY 8
#define PLR_DARKGRAY 9
#define PLR_RED 10
#define PLR_GREEN 11
#define PLR_YELLOW 12
#define PLR_BLUE 13
#define PLR_MAGENTA 14
#define PLR_CYAN 15
#define PLR_WHITE 16
enum plr_state {
PL_NORMAL,
PL_PREV,
PL_NEXT,
PL_QUIT,
PL_INTERRUPT
};
int8_t plr_int = PL_NORMAL;
void plr_sig(int signum) {
plr_int = PL_INTERRUPT;
}
void plr_skip() {
if(plr_int == PL_INTERRUPT) {
return;
}
int c = wgetch(stdscr);
switch(c) {
case 'n':
plr_int = PL_NEXT;
break;
case 'b':
plr_int = PL_PREV;
break;
case 'q':
plr_int = PL_QUIT;
break;
}
}
void str_time(char *str, uint64_t time) {
uint64_t msecs = time % 1000ULL;
uint64_t secs = (time /= 1000ULL) % 60ULL;
uint64_t mins = (time /= 60ULL) % 60ULL;
time /= 60ULL;
sprintf(str, "%02d:%02d:%02d.%03d", time, mins, secs, msecs);
}
void plr_sep() {
color_set(PLR_DARKGRAY, NULL);
addch('|');
}
void plr_vbdr() {
color_set(PLR_DARKGRAY, NULL);
addstr("||");
}
void plr_newl(int *line) {
plr_vbdr();
move((*line)++, 0);
}
void plr_hbdr(int width) {
color_set(PLR_DARKGRAY, NULL);
memset(tbuffer, '#', width);
tbuffer[width] = 0;
addstr(tbuffer);
}
void plr_pad(int width) {
memset(tbuffer, ' ', width);
tbuffer[width] = 0;
addstr(tbuffer);
}
void plr_hnbdr(int width, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
color_set(PLR_GRAY, NULL);
int len = vsprintf(tbuffer, fmt, ap);
addstr(tbuffer);
addch(' ');
plr_hbdr(width - (len+1));
va_end(ap);
}
void plr_cpfx(char desc, int id, int cl) {
color_set(cl, NULL);
sprintf(tbuffer, "%c-%02d", desc, id);
addstr(tbuffer);
}
void plr_pfx(const char *desc) {
color_set(PLR_DYELLOW, NULL);
addstr(desc);
}
void plr_bv(char ch, int cl, bool on) {
color_set(on ? cl : PLR_DBLUE, NULL);
addch(on ? ch : '-');
}
void plr_dv(const char *s1, int cl1, const char *s2, int cl2, bool sec) {
color_set(sec ? cl2 : cl1, NULL);
addstr(sec ? s2 : s1);
}
void plr_fcv(int cl, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
color_set(cl, NULL);
vsprintf(tbuffer, fmt, ap);
addstr(tbuffer);
va_end(ap);
}
void plr_fv(const char *desc, bool dark, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
color_set(PLR_GRAY, NULL);
addstr(desc);
color_set(dark ? PLR_DCYAN : PLR_CYAN, NULL);
vsprintf(tbuffer, fmt, ap);
addstr(tbuffer);
va_end(ap);
}
void plr_fc(const char *desc, char value) {
plr_fv(desc, 0, "%c", value);
}
void plr_fi(const char *desc, int32_t value) {
plr_fv(desc, 0, "%d", value);
}
void plr_fi2(const char *desc, int32_t value) {
plr_fv(desc, 0, "%02d", value);
}
void plr_fi2d(const char *desc, int32_t value) {
plr_fv(desc, 1, "%02d", value);
}
void plr_fi3(const char *desc, int32_t value) {
plr_fv(desc, 0, "%03d", value);
}
void plr_fi4(const char *desc, int32_t value) {
plr_fv(desc, 0, "%04d", value);
}
void plr_fi5p(const char *desc, int32_t value) {
plr_fv(desc, 0, "%+05d", value);
}
void plr_fi8(const char *desc, int64_t value) {
plr_fv(desc, 0, "%08lld", value);
}
void plr_fi4n(const char *desc, int32_t value) {
plr_fv(desc, 0, "%4d", value);
}
void plr_fhz(const char *desc, int32_t value) {
plr_fv(desc, 0, "%6d Hz", value);
}
void plr_fbit(const char *desc, int32_t value) {
plr_fv(desc, 0, "%2d-Bit", value);
}
void plr_fbpm(const char *desc, int32_t value) {
plr_fv(desc, 0, "%4d BPM", value);
}
void plr_fstr(const char *desc, const char *value) {
plr_fv(desc, 0, "%s", value);
}
void plr_str(const char *str, int cl) {
color_set(cl, NULL);
addstr(str);
}
void plr_ncstr(const char *str, int cl, int width) {
color_set(cl, NULL);
sprintf(tbuffer, "%-*2$.*2$s", str, width);
addstr(tbuffer);
}
void plr_nstr(const char *str, int width) {
plr_ncstr(str, PLR_GRAY, width);
}
void plr_dstr(const char *str, int width) {
color_set(PLR_CYAN, NULL);
sprintf(tbuffer, "%*2$.*2$s", str, width);
addstr(tbuffer);
}
void plr_printchip(opl3_chip *chip, bank_handle *bank, mid_handle *mid, aud_handle *dev, uint8_t nowait, uint32_t tick, uint64_t mtime, const char *midname, int16_t *aud) {
char gfx[33];
move(1, 0);
if(dev->iswav) {
char *logp = logger[(log_pos+4-1) % 4];
str_time(gfx, mtime);
sprintf(tbuffer, "[%s][%08d] %-115s\n", gfx, tick, logp);
addstr(tbuffer);
refresh();
return;
}
int line = 1;
int sw;
int sh;
getmaxyx(stdscr, sh, sw);
sw = (sw < 176) ? 176 : sw;
sh = (sh < 34) ? 34 : sh;
if((sw != last_w) || (sh != last_h)) {
erase();
}
last_w = sw;
last_h = sh;
int PLR_WIDTH = (sw-2)/39;
int PLR_HEIGHT = (sh-34)/3;
int pad = (sw-2) - (PLR_WIDTH * 39);
int cpad = sw-176;
int lpad = sw-121;
int nch = chip->n_voices;
PLR_HEIGHT = (PLR_HEIGHT > ((nch+PLR_WIDTH-1)/PLR_WIDTH)) ? ((nch+PLR_WIDTH-1)/PLR_WIDTH) : PLR_HEIGHT;
int addl = sh - 34 - (PLR_HEIGHT * 3);
gfx[16] = 0;
plr_vbdr();
plr_hnbdr(sw-4, "%s", STR_PLR_TITLE);
plr_newl(&line);
for(int p = 0; p < PLR_HEIGHT; p++) {
for(int c = 0; c < PLR_WIDTH; c++) {
if((p*PLR_WIDTH+c) >= nch) {
plr_vbdr();
plr_pad(39*(PLR_WIDTH-c)-2);
break;
}
opl3_channel *channel = &chip->channel[p*PLR_WIDTH+c];
int16_t **out = channel->out;
int16_t accm = *out[0] + *out[1] + *out[2] + *out[3];
int32_t chnlvl = 0;
for(int n = 0; n < 2; n++) {
chnlvl += (int16_t)((accm * channel->level[n]) >> 16);
}
chnlvl = chnlvl < 0 ? (-chnlvl) : chnlvl;
chnlvl = (chnlvl * 16) / 8192;
for(int i = 0; i < 16; i++) {
gfx[i] = i < chnlvl ? '#' : '-';
}
int vid = (p*PLR_WIDTH+c) + 1;
uint8_t active = (channel->slots[0]->eg_out <= 0x100) || (channel->slots[1]->eg_out <= 0x100);
plr_vbdr();
if(vid < 100) {
plr_fcv(active ? PLR_MAGENTA : PLR_DMAGENTA, "%02d", vid);
}
else {
plr_fcv(active ? PLR_MAGENTA : PLR_DMAGENTA, "++");
}
plr_sep();
plr_dv("2O", PLR_BLUE, "4O", PLR_GREEN, channel->chtype != ch_2op);
plr_sep();
plr_dv("FM", PLR_RED, "AM", PLR_YELLOW, channel->con);
plr_sep();
plr_fi("FB", channel->fb);
plr_sep();
plr_fi("F", channel->block);
plr_fi4("$", channel->f_num);
plr_sep();
plr_str(gfx, PLR_GREEN);
}
if(pad >= 2) {
plr_vbdr();
plr_pad(pad-2);
}
else if(pad == 1) {
plr_sep();
}
plr_newl(&line);
for(int o = 0; o < 2; o++) {
for(int c = 0; c < PLR_WIDTH; c++) {
if((p*PLR_WIDTH+c) >= nch) {
plr_vbdr();
plr_pad(39*(PLR_WIDTH-c)-2);
break;
}
opl3_channel *channel = &chip->channel[p*PLR_WIDTH+c];
opl3_slot *slot = channel->slots[o];
uint8_t trem = slot->trem != (uint8_t*)&slot->chip->zeromod;
plr_vbdr();
// plr_fcv(PLR_DMAGENTA, "O%d", o + 1);
// plr_sep();
plr_dv(".", PLR_DGREEN, "*", PLR_GREEN, slot->key);
plr_sep();
plr_bv('T', PLR_GREEN, trem);
plr_bv('V', PLR_YELLOW, slot->reg_vib);
plr_bv('S', PLR_RED, slot->reg_type);
plr_bv('K', PLR_MAGENTA, slot->reg_ksr);
plr_sep();
plr_fi2("M", slot->reg_mult);
// plr_sep();
plr_fi2("L", 63-slot->reg_tl);
// plr_sep();
plr_fi("K", slot->reg_ksl);
// plr_fi3("/", slot->eg_ksl);
// plr_sep();
plr_fc("W", plr_waves[slot->reg_wf]);
plr_sep();
plr_fi2("E", slot->reg_ar);
plr_fi2d("", slot->reg_dr);
plr_fi2("", (slot->reg_sl > 15) ? 0 : (15-slot->reg_sl));
plr_fi2d("", slot->reg_rr);
plr_sep();
plr_fc("P", plr_phases[slot->eg_gen]);
plr_fi3("", slot->eg_rout);
plr_fi3("/", slot->eg_out);
}
if(pad >= 2) {
plr_vbdr();
plr_pad(pad-2);
}
else if(pad == 1) {
plr_sep();
}
plr_newl(&line);
}
}
for(int l = 0; l < addl; l++) {
plr_vbdr();
plr_pad(39*PLR_WIDTH-2);
if(pad >= 2) {
plr_vbdr();
plr_pad(pad-2);
}
else if(pad == 1) {
plr_sep();
}
plr_newl(&line);
}
plr_vbdr();
plr_hbdr(sw-4);
plr_newl(&line);
char nid[128+1];
nid[128] = 0;
char chid[cpad+1];
chid[cpad] = 0;
for(int d = chip->n_voices; d < cpad; d++) {
chid[d] = 'x';
}
for(int c = 0; c < 16; c++) {
bank_channel *channel = &bank->channel[c];
for(int d = 0; d < 128; d++) {
nid[d] = (channel->notes[d] == 0xff) ? '-' : '_';
}
for(int d = 0; (d < cpad) && (d < chip->n_voices); d++) {
bank_voice *voice = &bank->voices[d];
chid[d] = ((voice->note != 0xff) && (voice->channel->ch_num == c)) ? '*' : '.';
}
plr_vbdr();
plr_cpfx('M', c + 1, ((channel->pbank != 0) && (channel->pbank != 128)) ? ((bank->flags & BANK_UNKN) ? PLR_YELLOW : PLR_RED) : PLR_DYELLOW);
plr_sep();
if((channel->pbank >= 0) && (channel->pbank < 1000)) {
plr_fi3("PR", channel->pbank);
}
else {
plr_fstr("PR", "???");
}
plr_fi3("/", channel->program);
plr_sep();
plr_fi3("VL", channel->volume);
plr_sep();
plr_fi3("PN", channel->pan);
plr_sep();
plr_fi5p("PT", channel->pitch);
plr_sep();
plr_fi3("AC", channel->active);
plr_vbdr();
plr_str(nid, PLR_GREEN);
plr_vbdr();
plr_str(chid, PLR_GREEN);
plr_newl(&line);
}
for(int d = 0; d < 128; d++) {
nid[d] = ' ';
}
for(int d = 0; d < 11; d++) {
nid[d*12] = plr_hexn[d];
}
for(int d = 0; d < cpad; d++) {
chid[d] = (d == bank->voiceindex) ? '^' : ' ';
}
str_time(gfx, mtime);
plr_vbdr();
plr_pfx("MIDI");
plr_sep();
plr_fi8("TICK ", tick);
plr_sep();
plr_fstr("TIME ", gfx);
plr_vbdr();
plr_str(nid, PLR_GRAY);
plr_vbdr();
plr_str(chid, PLR_GREEN);
plr_newl(&line);
for(int d = 0; d < 128; d++) {
nid[d] = plr_notes[d % 12];
}
if(bank->voiceindex < cpad) {
chid[bank->voiceindex] = '*';
}
plr_vbdr();
plr_bv('K', PLR_YELLOW, bank->flags & BANK_KEEP);
plr_bv('U', PLR_RED, bank->flags & BANK_UNKN);
plr_bv('W', PLR_MAGENTA, !nowait);
plr_bv('?', PLR_GREEN, false);
plr_sep();
plr_fi3("USED ", bank->v_used);
plr_fi3("/", bank->v_avail);
plr_sep();
plr_fbpm("TEMPO ", 60000000 / mid->uspb);
plr_fi4n("/", mid->tpqn);
plr_vbdr();
plr_str(nid, PLR_GRAY);
plr_vbdr();
plr_str(chid, PLR_GREEN);
plr_newl(&line);
for(int d = 0; d < cpad; d++) {
chid[d] = '0' + (((d+1)/10) % 10);
}
plr_vbdr();
plr_pfx("CARD");
plr_sep();
plr_fbit("FORMAT ", dev->iswide ? 32 : 16);
plr_sep();
plr_fhz("RATE ", dev->samplerate);
plr_vbdr();
plr_hbdr(128);
plr_vbdr();
plr_str(chid, PLR_GRAY);
plr_newl(&line);
for(int d = 0; d < cpad; d++) {
chid[d] = '0' + ((d+1) % 10);
}
plr_vbdr();
plr_pfx("BANK");
plr_sep();
plr_dstr(bankname, 35);
plr_vbdr();
plr_pfx("FILE");
plr_sep();
plr_dstr(midname, 123);
plr_vbdr();
plr_str(chid, PLR_GRAY);
plr_newl(&line);
plr_vbdr();
plr_hbdr(sw-4);
plr_newl(&line);
for(int l = 0; l < 4; l++) {
plr_vbdr();
plr_nstr(midinfo[l], lpad);
plr_vbdr();
plr_nstr(klogger[l], 115);
plr_newl(&line);
}
plr_vbdr();
plr_nstr(midinfo[4], lpad/2);
plr_sep();
plr_nstr(midinfo[5], (lpad-1)/2);
plr_vbdr();
plr_nstr(klogger[4], 115);
plr_newl(&line);
plr_vbdr();
plr_hbdr(sw-4);
plr_newl(&line);
gfx[32] = 0;
vu_push(aud);
for(int l = 0; l < 4; l++) {
if((l & 1) == 0) {
int32_t chnlvl = vu_sum[l >> 1];
chnlvl = (chnlvl * 32) / 8192;
for(int i = 0; i < 32; i++) {
gfx[i] = i < chnlvl ? '#' : '-';
}
}
plr_vbdr();
plr_nstr(logger[(log_pos+l) % 4], lpad);
plr_vbdr();
plr_nstr(mlogger[(mlog_pos+l) % 4], 81);
plr_vbdr();
plr_ncstr(gfx, PLR_GREEN, 26);
plr_ncstr(gfx+26, PLR_YELLOW, 4);
plr_ncstr(gfx+30, PLR_RED, 2);
plr_newl(&line);
}
plr_vbdr();
plr_hnbdr(sw-4, "%c %s", plr_wheel[(tick >> 5) & 3], STR_PLR_INFO);
plr_newl(&line);
plr_skip();
refresh();
}
bool plr_play(opl3_chip *chip, bank_handle *bank, aud_handle *dev, const char *midname, const char *dmxname, uint8_t nowait, uint32_t midis, uint32_t num) {
mid_handle mid;
if(mid_read(&mid, midname) ^ 1) {
return false;
}
mid_log(STR_PLR_PLAY, midname, num+1, midis);
uint64_t sampletime = 1000000000ULL / ((uint64_t)dev->samplerate);
uint64_t elapsed = 0;
uint64_t samples = 0;
uint64_t pushed = 0;
uint32_t tick = 0;
int16_t buf[2];
while((plr_int == PL_NORMAL) && mid_tick(&mid, bank, chip)) {
elapsed += ((uint64_t)mid.ticktime) * 1000ULL;
while(elapsed >= sampletime) {
OPL3_GenerateResampled(chip, buf);
aud_push(dev, buf);
samples++;
if(samples >= (dev->samplerate/PLR_REFRESH)) {
pushed += samples;
samples = 0;
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
}
elapsed -= sampletime;
}
if(samples >= (dev->samplerate/PLR_REFRESH)) {
pushed += samples;
samples = 0;
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
}
tick++;
}
bank_alloff(bank);
while((plr_int == PL_NORMAL) && (nowait ^ 1) && OPL3_Playing(chip)) {
OPL3_GenerateResampled(chip, buf);
aud_push(dev, buf);
samples++;
if(samples >= (dev->samplerate/PLR_REFRESH)) {
pushed += samples;
samples = 0;
plr_printchip(chip, bank, &mid, dev, nowait, tick, pushed*1000ULL/dev->samplerate, midname, buf);
}
}
plr_printchip(chip, bank, &mid, dev, nowait, tick, (pushed+samples)*1000ULL/dev->samplerate, midname, buf);
aud_flush(dev);
mid_free(&mid);
OPL3_Reset(chip);
bank_reset(chip, bank);
mid_cmlog();
return true;
}
bool plr_playloop(uint32_t samplerate, uint32_t voices, char **midnames, uint32_t midis, const char *dmxname, const char *drumname, const char *device, uint32_t fsize, uint32_t bufsize, uint8_t widefmt,
uint8_t keep, uint8_t useunkn, int8_t velofunc, uint8_t nowait) {
bank_instr *instr = bnk_read_banks(dmxname, drumname, 0);
if(instr == NULL) {
return false;
}
opl3_chip *chip = OPL3_Alloc(samplerate, voices);
bank_handle *bank = bank_alloc(chip, instr, keep, useunkn, velofunc);
char *log = malloc(256*19);
for(int h = 0; h < 4; h++) {
logger[h] = &log[h*256];
logger[h][0] = 0;
}
for(int h = 0; h < 4; h++) {
mlogger[h] = &log[(h+4)*256];
}
for(int h = 0; h < 5; h++) {
klogger[h] = &log[(h+8)*256];
}
for(int h = 0; h < 6; h++) {
midinfo[h] = &log[(h+13)*256];
}
plr_setbnk(dmxname, drumname);
vu_reset();
mid_cmlog();
aud_handle dev;
if(aud_open_dev(&dev, device, samplerate, 2, fsize, bufsize, widefmt) ^ 1) {
free(instr);
free(chip);
free(bank);
free(log);
return false;
}
samplerate = dev.samplerate;
OPL3_Rate(chip, samplerate);
signal(SIGINT, plr_sig);
initscr();
if(!(dev.iswav)) {
start_color();
use_default_colors();
init_pair(PLR_BLACK, 0, -1);
init_pair(PLR_DRED, 1, -1);
init_pair(PLR_DGREEN, 2, -1);
init_pair(PLR_DYELLOW, 3, -1);
init_pair(PLR_DBLUE, 4, -1);
init_pair(PLR_DMAGENTA, 5, -1);
init_pair(PLR_DCYAN, 6, -1);
init_pair(PLR_GRAY, 7, -1);
init_pair(PLR_DARKGRAY, 8, -1);
init_pair(PLR_RED, 9, -1);
init_pair(PLR_GREEN, 10, -1);
init_pair(PLR_YELLOW, 11, -1);
init_pair(PLR_BLUE, 12, -1);
init_pair(PLR_MAGENTA, 13, -1);
init_pair(PLR_CYAN, 14, -1);
init_pair(PLR_WHITE, 15, -1);
}
cbreak();
noecho();
nodelay(stdscr, true);
nonl();
intrflush(stdscr, false);
keypad(stdscr, true);
curs_set(0);
tbuffer = malloc(65536);
bool state = false;
bool skip = false;
for(int h = 0; (h < midis) && (plr_int == PL_NORMAL); h++) {
if(plr_play(chip, bank, &dev, midnames[h], dmxname, nowait, midis, h)) {
skip = false;
state = true;
}
else if(skip) {
plr_int = PL_PREV;
}
if(plr_int == PL_NEXT) {
plr_int = PL_NORMAL;
}
else if(plr_int == PL_PREV) {
plr_int = PL_NORMAL;
h -= ((h == 0) ? 1 : 2);
skip = h >= 0;
}
}
// vu_reset();
// plr_printchip(chip, bank, &dev, nowait, 0, 0ULL, 0, dev.iswav, "", "", NULL);
aud_close(&dev);
free(instr);
free(chip);
free(bank);
free(log);
free(tbuffer);
endwin();
fprintf(stderr, (plr_int == PL_INTERRUPT) ? STR_PLR_INT"\n" : STR_PLR_END"\n");
return state;
}
int main(int argc, char **argv) {
int32_t samplerate = 48000;
int32_t voices = 36;
char *device = "default";
int32_t bufsize = 0;
int32_t fsize = 32;
int32_t widefmt = 0;
int32_t keep = 0;
int32_t useunkn = 0;
int32_t velo = 1;
int32_t nowait = 0;
char *dmxname = "bank.op2";
char *drumname = NULL;
int32_t midis = 0;
char **midnames = malloc(sizeof(char*) * argc);
argp_t *argspec = arg_alloc(14);
arg_add_vstr(argspec, STR_ARGS_MIDI, true, midnames, &midis);
arg_add_int(argspec, 'r', STR_ARGS_SRATE, 8000, 384000, &samplerate);
arg_add_int(argspec, 'v', STR_ARGS_VOICES, 2, 192, &voices);
arg_add_str(argspec, 'd', STR_ARGS_DEVICE, &device);
arg_add_int(argspec, 'b', STR_ARGS_BUFFER, 0, 1048576, &bufsize);
arg_add_int(argspec, 'f', STR_ARGS_FRAMES, 2, 8192, &fsize);
arg_add_int(argspec, 'e', STR_ARGS_VELO, -128, 127, &velo);
arg_add_bool(argspec, 'w', STR_ARGS_WIDEFT, &widefmt);
arg_add_bool(argspec, 'k', STR_ARGS_KEEPV, &keep);
arg_add_bool(argspec, 'u', STR_ARGS_USEUNK, &useunkn);
arg_add_bool(argspec, 'n', STR_ARGS_DONTWT, &nowait);
arg_add_str(argspec, 'p', STR_ARGS_BANK, &dmxname);
arg_add_str(argspec, 'q', STR_ARGS_DBANK, &drumname);
arg_add_bool(argspec, 'D', STR_ARGS_MDEBUG, &log_debug);
if(arg_parse(argspec, argc, argv) ^ 1) {
return 1;
}
if((voices & 1) != 0) {
fprintf(stderr, STR_PLR_UNEVEN"\n");
free(midnames);
return 1;
}
bool state = plr_playloop(samplerate, voices, midnames, midis, dmxname, drumname, device, fsize, bufsize, widefmt, keep, useunkn ^ 1, velo, nowait);
free(midnames);
return state ? 0 : 2;
}

119
strings.h Normal file
View file

@ -0,0 +1,119 @@
#define STR_ARGS_MIDI "MIDI-Datei"
#define STR_ARGS_BANK "Bank-Datei"
#define STR_ARGS_DBANK "Perk.-Datei"
#define STR_ARGS_USEDRM "Als Perk.-Bank"
#define STR_ARGS_SRATE "Abtastrate"
#define STR_ARGS_VOICES "OPL-Stimmen"
#define STR_ARGS_DEVICE "ALSA-Geraet/WAV-Datei"
#define STR_ARGS_BUFFER "Puffergroesse"
#define STR_ARGS_FRAMES "PCM-Intervall"
#define STR_ARGS_WIDEFT "32-Bit Samples"
#define STR_ARGS_KEEPV "Stimmen behalten"
#define STR_ARGS_USEUNK "Keine unbek. Bank"
#define STR_ARGS_VELO "Anschlag-Funktion"
#define STR_ARGS_DONTWT "Nicht ausklingen"
#define STR_ARGS_OFFSET "Start"
#define STR_ARGS_NOTE "Note"
#define STR_ARGS_LENGTH "Laenge"
#define STR_ARGS_PAUSE "Pause"
#define STR_ARGS_INSTR "Instrument-N."
#define STR_ARGS_PERC "Perk.-Nummer"
#define STR_ARGS_DPERC "DMX-Perk.-N."
#define STR_ARGS_BANKN "Bank-Nummer"
#define STR_ARGS_NTABLE "Liste [k. Tabelle]"
#define STR_ARGS_NONAME "Keine Instr.-Namen"
#define STR_ARGS_SKB "SKB-Datei"
#define STR_ARGS_MDEBUG "MIDI-Debug"
#define STR_WAV_OPNERR "Fehler beim Oeffnen von WAV-Datei '%s': %s (%d)"
#define STR_WAV_WRITERR "Fehler beim Schreiben nach WAV-Datei '%s': "
#define STR_WAV_EIO STR_WAV_WRITERR "E/A-Fehler"
#define STR_WAV_EGEN STR_WAV_WRITERR "%s (%d)"
#define STR_MID_OPNERR "Fehler beim Oeffnen von MIDI-Datei '%s': %s (%d)"
#define STR_MID_READERR "Fehler beim Lesen von MIDI-Datei '%s': "
#define STR_MID_EIO STR_MID_READERR "E/A-Fehler"
#define STR_MID_EEOF STR_MID_READERR "Ende der Datei erreicht"
#define STR_MID_EGEN STR_MID_READERR "%s (%d)"
#define STR_MID_ESHORT STR_MID_READERR "Datei zu klein"
#define STR_MID_EMFHDR STR_MID_READERR "Fehlerhafter Dateiheader oder falscher Dateityp"
#define STR_MID_EHSIZE STR_MID_READERR "Dateiheader mit falscher Laenge"
#define STR_MID_ETKFMT STR_MID_READERR "Fehlerhaftes Spurformat"
#define STR_MID_ENOTRK STR_MID_READERR "Keine Spuren definiert"
#define STR_MID_ESMULT STR_MID_READERR "Einzelspur-Format mit mehreren Spuren"
#define STR_MID_ESMPTE STR_MID_READERR "SMPTE-Zeitformat nicht unterstuetzt"
#define STR_MID_EDZERO STR_MID_READERR "Zeitformat teilt durch null"
#define STR_MID_ETKHDR STR_MID_READERR "Fehlerhafter Spurheader"
#define STR_MID_EOUTOF STR_MID_READERR "Spur #%d ausserhalb der Datei"
#define STR_MID_SEQNUMO "MIDI Spur #%d Sequenz-Nummer = %d (eigene)"
#define STR_MID_SEQNUM "MIDI Spur #%d Sequenz-Nummer = %d"
#define STR_MID_CHNPFX "MIDI Spur #%d Kanal-Praefix = %d"
#define STR_MID_END "MIDI Spur #%d Ende erreicht"
#define STR_MID_TEMPO "MIDI Tempo = %d"
#define STR_MID_SMPTE "MIDI SMPTE-Event (nicht unterstuetzt)"
#define STR_MID_TIMESIG "MIDI Takt-Signatur = %d/%d - %d CPC - %d 32PQ"
#define STR_MID_KEYSIG "MIDI Noten-Signatur = %d - %s"
#define STR_MID_TEXT "MIDI Text (%d): %s"
#define STR_MID_SEQDATA "MIDI Spur #%d Sequenzer-Daten empfangen - %d Bytes"
#define STR_MID_UNKMETA "MIDI Spur #%d Meta-Event 0x%02x (%d)"
#define STR_MID_METALEN "MIDI Spur #%d Meta-Event 0x%02x - erwartet %d, hat %d"
#define STR_MID_NOTEOFF "MIDI Note-Aus - C%02d N%03d V%03d"
#define STR_MID_NOTEON "MIDI Note-An - C%02d N%03d V%03d"
#define STR_MID_PRESS "MIDI Aftertouch - C%02d P%03d"
#define STR_MID_CC "MIDI Controller - C%02d N%03d V%03d"
#define STR_MID_PROG "MIDI Programm - C%02d P%03d"
#define STR_MID_CPRESS "MIDI Kanal-Druck - C%02d P%03d"
#define STR_MID_PITCH "MIDI Pitch-Bend - C%02d P%03d"
#define STR_MID_SYSEX "MIDI Sysex (Normal) mit Laenge = %d"
#define STR_MID_SONGPOS "MIDI Song-Position = %d"
#define STR_MID_SONGSEL "MIDI Song-Auswahl = %d"
#define STR_MID_TUNE "MIDI Stimmung angefordert, nichts zu tun?!"
#define STR_MID_ESYSEX "MIDI Sysex (Escape) mit Laenge = %d"
#define STR_MID_GENSTAT "MIDI Status %d"
#define STR_MID_UNKSTAT "MIDI Status unbekannt: 0x%02x"
#define STR_MID_EEND "MIDI Spur #%d endete zu frueh"
#define STR_IMID_TITLE "Titel"
#define STR_IMID_INFO "Info"
#define STR_IMID_COPY "Hinweis"
#define STR_IMID_WARN "Lizenz"
#define STR_IMID_LANG "Sprache"
#define STR_IMID_VER "Version"
#define STR_BNK_UNKN "Format von Bank-Datei '%s' unbekannt"
#define STR_BNK_OPNERR "Fehler beim Oeffnen von Bank-Datei '%s': %s (%d)"
#define STR_BNK_READERR "Fehler beim Lesen von Bank-Datei '%s': "
#define STR_BNK_WRITERR "Fehler beim Schreiben nach Bank-Datei '%s': "
#define STR_BNK_PARSERR "Fehler beim Verarbeiten von Bank-Datei '%s': "
#define STR_BNK_EIO STR_BNK_READERR "E/A-Fehler"
#define STR_BNK_EEOF STR_BNK_READERR "Ende der Datei erreicht"
#define STR_BNK_EGEN STR_BNK_READERR "%s (%d)"
#define STR_BNK_ESHORT STR_BNK_READERR "Datei zu klein"
#define STR_BNK_EMFHDR STR_BNK_READERR "Fehlerhafter Dateiheader oder falscher Dateityp"
#define STR_BNK_EWIO STR_BNK_WRITERR "E/A-Fehler"
#define STR_BNK_EEOD STR_BNK_PARSERR "Ende der Daten erreicht"
#define STR_AUD_UNDERR "ALSA Puffer-Unterlauf! (%d)"
#define STR_AUD_WRITERR "Fehler beim Schreiben von PCM: %s (%d)"
#define STR_AUD_OPNERR "Fehler beim Oeffnen von ALSA PCM-Geraet '%s': %s (%d)"
#define STR_AUD_HWERR "Fehler beim Setzen von Hardware-Parametern von ALSA PCM-Geraet '%s': %s (%d)"
#define STR_AUD_SWERR "Fehler beim Setzen von Software-Parametern von ALSA PCM-Geraet '%s': %s (%d)"
#define STR_AUD_FMTERR "Fehler beim Setzen von Sample-Format von ALSA PCM-Geraet '%s': Kein Format verfuegbar"
#define STR_AUD_FMTCH "ALSA PCM-Geraet '%s': Sample-Format S%dLE nicht verfuegbar, setze auf S%dLE"
#define STR_AUD_PREPERR "Fehler beim Vorbereiten vom ALSA PCM-Geraet"
#define STR_BNK_UNDEF "Instrument %d ist in der Bank nicht definiert!"
#define STR_BNK_NONE "Keine Instrumente zum abspielen!"
#define STR_TST_NDDRUM "DMX-Perkussions- und Instrument-/MIDI-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
#define STR_TST_NMDRUM "MIDI-Perkussions- und Instrument-/DMX-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
#define STR_TST_NOFFS "Offset und Instrument-/MIDI-Perkussions-/DMX-Perkussions-Nummern koennen nicht gleichzeitig angegeben werden"
#define STR_TST_DRUM "Spiele Perkussion %d ('%s') .."
#define STR_TST_INSTR "Spiele Note #%d auf Instrument %d ('%s') .."
#define STR_PLR_UNEVEN "Die Anzahl der OPL-Stimmen muss gerade sein"
#define STR_PLR_PLAY "Spiele MIDI '%s' (%d/%d) .."
#define STR_PLR_END "Fertig."
#define STR_PLR_INT "Unterbrochen."
#define STR_PLR_TITLE "SKC OPL MIDI Player v 0.0.1 (~ Sen)"
#define STR_PLR_INFO "+++ Q - Beenden +++ N - Naechster Titel +++ B - Vorheriger Titel"

89
wavfile.h Normal file
View file

@ -0,0 +1,89 @@
static const char *wav_hdr = "RIFF";
static const char *wav_hdr_fmt = "fmt ";
static const char *wav_hdr_data = "data";
static const char *wav_format = "WAVE";
#define WAV_HDRSIZE 44
typedef struct {
uint32_t samplerate;
uint16_t channels;
uint16_t bytes;
uint32_t samples;
FILE *fd;
const char *filename;
uint8_t *buffer;
} wav_handle;
void wav_write_uint32(uint8_t *data, uint32_t value) {
for(int h = 0; h < 4; h++) {
data[h] = ((uint32_t)((value >> (8*h) & 0xff)));
}
}
void wav_write_uint16(uint8_t *data, uint16_t value) {
for(int h = 0; h < 2; h++) {
data[h] = ((uint16_t)((value >> (8*h) & 0xff)));
}
}
void wav_write_header(uint8_t *data, uint32_t samplerate, uint16_t channels, uint32_t samples, uint16_t bytes) {
memcpy(data+0, wav_hdr, 4);
wav_write_uint32(data+4, 36 + samples * ((uint32_t)channels) * ((uint32_t)bytes));
memcpy(data+8, wav_format, 4);
memcpy(data+12, wav_hdr_fmt, 4);
wav_write_uint32(data+16, 16);
wav_write_uint16(data+20, 1);
wav_write_uint16(data+22, channels);
wav_write_uint32(data+24, samplerate);
wav_write_uint32(data+28, samplerate * ((uint32_t)channels) * ((uint32_t)bytes));
wav_write_uint16(data+32, channels * bytes);
wav_write_uint16(data+34, 8 * bytes);
memcpy(data+36, wav_hdr_data, 4);
wav_write_uint32(data+40, samples * ((uint32_t)channels) * ((uint32_t)bytes));
}
bool wav_open(wav_handle *wav, const char *filename, uint32_t samplerate, uint16_t channels, uint16_t bytes) {
int err;
FILE *fd = fopen(filename, "wb");
if(fd == NULL) {
err = errno;
mid_log(STR_WAV_OPNERR, filename, strerror(err), err);
return false;
}
uint8_t pad[WAV_HDRSIZE];
memset(pad, 0, WAV_HDRSIZE);
if(fwrite(pad, 1, WAV_HDRSIZE, fd) != WAV_HDRSIZE) {
mid_log(STR_WAV_EIO, filename);
fclose(fd);
return false;
}
wav->fd = fd;
wav->samplerate = samplerate;
wav->channels = channels;
wav->bytes = bytes;
wav->samples = 0;
wav->filename = filename;
return true;
}
bool wav_close(wav_handle *wav) {
if(wav->fd == NULL) {
return false;
}
int err;
if(fseek(wav->fd, 0L, SEEK_SET)) {
err = errno;
mid_log(STR_WAV_EGEN, wav->filename, strerror(err), err);
fclose(wav->fd);
return false;
}
uint8_t header[WAV_HDRSIZE];
wav_write_header(header, wav->samplerate, wav->channels, wav->samples, wav->bytes);
if(fwrite(header, 1, WAV_HDRSIZE, wav->fd) != WAV_HDRSIZE) {
mid_log(STR_WAV_EIO, wav->filename);
fclose(wav->fd);
return false;
}
fclose(wav->fd);
return true;
}