Archived
1
0
Fork 0
This repository has been archived on 2025-09-02. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
skcblitz/bank.h

404 lines
12 KiB
C
Raw Permalink Normal View History

2025-09-02 14:43:36 +02:00
void bank_reset(opl3_chip *chip, bank_handle *bank) {
ushort voices = chip->n_voices;
byte flags = bank->flags;
char velo_func = bank->velo_func;
bank_instr *instr = bank->bdata;
byte *bankdata = (byte*)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, byte keep, byte useunkn, char velofunc) {
byte *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;
}
byte bank_getnote(bank_channel *ch, byte key, byte id, byte drum, bank_instr *instr) {
short note = (short)key;
if(instr->fixed /* || drum */) {
note = ((short)instr->percnum) + instr->channels[id].offset;
}
else {
note += instr->channels[id].offset;
}
note = note < 0 ? 0 : note;
note = note > 127 ? 127 : note;
return (byte)note;
}
void OPL3_ChannelFreqHz(opl3_channel *channel, uint freq) {
uint 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, (uint)f_num);
}
uint bank_getfreq(byte key, short pitch, short 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 (uint)freq;
}
void OPL3_ChannelNote(opl3_channel *channel, byte key, short pitch, short detune) {
OPL3_ChannelFreqHz(channel, bank_getfreq(key, pitch, detune));
}
uint bank_getlevel(char function, byte velocity, byte volume, char 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 (uint)lvl;
}
void OPL3_ChannelLevelPan(opl3_channel *channel, char function, byte velocity, byte volume, char 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, byte 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, byte 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, byte note, byte 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;
}
}
byte 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)) {
short offset = ((short)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, byte note, byte velocity) {
if(ch->notes[note] != 0xff) {
bank_release_key(ch, note);
}
if(ch->active == BANK_MAX) {
return NULL;
}
byte 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);
ushort 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) {
byte 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) {
byte 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, byte channel, byte key, byte 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, byte channel, byte key, byte 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, byte channel, byte program) {
bank_channel *ch = &bank->channel[channel];
ch->program = program;
bank_progupdate(ch, chip);
}
void bank_pitchbend(bank_handle *bank, opl3_chip *chip, byte channel, short pitch) {
bank_channel *ch = &bank->channel[channel];
ch->pitch = pitch;
bank_frequpdate(ch, chip);
}
void bank_control(bank_handle *bank, opl3_chip *chip, byte channel, byte control, byte 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 |= (((ushort)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 |= ((ushort)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 = ((char)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]);
}
}