404 lines
12 KiB
C
404 lines
12 KiB
C
|
|
||
|
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]);
|
||
|
}
|
||
|
}
|