487 lines
No EOL
15 KiB
C
487 lines
No EOL
15 KiB
C
#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]);
|
|
}
|
|
} |