1
0
Fork 0

opl arb update

This commit is contained in:
Sen 2025-08-25 13:17:58 +02:00
parent 16b05b2e1e
commit c0919b0eed
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
2 changed files with 479 additions and 647 deletions

View file

@ -16,6 +16,8 @@ import common.util.Identifyable;
import javax.sound.sampled.SourceDataLine;
import client.audio.AudioInterface.BankInstr;
import client.audio.AudioInterface.BankHandle.BankChannel;
import client.audio.OPLChip.OPLChannel;
import client.audio.OPLChip.OPLSlot;
import client.util.FileUtils;
@ -7865,8 +7867,8 @@ public class AudioInterface implements Runnable {
this.tpqn = tpqn;
this.nowait = nowait;
this.setTempo(500000);
this.chip = new OPLChip(48000, voices, 0, false, false);
this.bank = new BankHandle(this.chip, bank.getData(), keep, useunkn, (byte)velofunc);
this.chip = new OPLChip(48000, voices, velofunc, 0, false, false);
this.bank = new BankHandle(this.chip, bank.getData(), keep, useunkn);
this.playing = true;
}
@ -7874,8 +7876,8 @@ public class AudioInterface implements Runnable {
this.tracks = null;
this.tpqn = 0;
this.nowait = false;
this.chip = new OPLChip(48000, voices, 0, false, false);
this.bank = new BankHandle(this.chip, bank.getData(), keep, useunkn, (byte)velofunc);
this.chip = new OPLChip(48000, voices, velofunc, 0, false, false);
this.bank = new BankHandle(this.chip, bank.getData(), keep, useunkn);
this.ticktime = start;
}
@ -7989,7 +7991,7 @@ public class AudioInterface implements Runnable {
}
}
private void process(MidiTrack trk, BankHandle bank, OPLChip chip) {
private void process(MidiTrack trk, BankHandle bank) {
if(trk.pos >= trk.size) {
return;
}
@ -8008,7 +8010,7 @@ public class AudioInterface implements Runnable {
case midev_noteoff: {
byte o_key = trk.readByte();
byte o_velocity = trk.readByte();
bank.noteoff(chip, channel, o_key, o_velocity);
bank.noteoff(channel, o_key, o_velocity);
this.debug("MIDI Note-Aus - C%02d N%03d V%03d", channel+1, o_key, o_velocity);
break;
}
@ -8016,9 +8018,9 @@ public class AudioInterface implements Runnable {
byte key = trk.readByte();
byte velocity = trk.readByte();
if(velocity == 0)
bank.noteoff(chip, channel, key, velocity);
bank.noteoff(channel, key, velocity);
else
bank.noteon(chip, channel, key, velocity);
bank.noteon(channel, key, velocity);
this.debug("MIDI Note-An - C%02d N%03d V%03d", channel+1, key, velocity);
break;
}
@ -8030,13 +8032,13 @@ public class AudioInterface implements Runnable {
case midev_control: {
byte control = trk.readByte();
byte value = trk.readByte();
bank.control(chip, channel, control, value);
bank.control(channel, control, value);
this.debug("MIDI Controller - C%02d N%03d V%03d", channel+1, control, value);
break;
}
case midev_progchg: {
byte program = trk.readByte();
bank.progchange(chip, channel, program);
bank.progchange(channel, program);
this.debug("MIDI Programm - C%02d P%03d", channel+1, program);
break;
}
@ -8048,7 +8050,7 @@ public class AudioInterface implements Runnable {
case midev_pitchbend: {
short pb = (short)(((short)trk.readByte()) | (((short)trk.readByte()) << 7));
short pitch = (short)(pb - 0x2000);
bank.pitchbend(chip, channel, pitch);
bank.pitchbend(channel, pitch);
this.debug("MIDI Pitch-Bend - C%02d P%03d", channel+1, pitch);
break;
}
@ -8094,7 +8096,7 @@ public class AudioInterface implements Runnable {
}
}
private boolean tick(BankHandle bank, OPLChip chip) {
private boolean tick(BankHandle bank) {
boolean end = true;
for(int trk = 0; trk < this.tracks.length; trk++) {
MidiTrack track = this.tracks[trk];
@ -8109,7 +8111,7 @@ public class AudioInterface implements Runnable {
}
while(true) {
if(track.pos > 0) {
this.process(track, bank, chip);
this.process(track, bank);
if(!track.ending && track.pos >= track.size) {
this.log("MIDI Spur #%d endete zu früh", track.trknum);
track.ending = true;
@ -8132,10 +8134,10 @@ public class AudioInterface implements Runnable {
public long process() {
if(this.tracks == null) {
this.bank.progchange(this.chip, (byte)0, (byte)(this.ticktime < 128 ? this.ticktime : 0));
this.bank.progchange(this.chip, (byte)9, (byte)0);
this.bank.progchange(0, this.ticktime < 128 ? this.ticktime : 0);
this.bank.progchange(9, 0);
Log.SOUND.user("MIDI-Test %s%d", this.ticktime < 128 ? "#" : "P", this.ticktime < 128 ? this.ticktime : this.ticktime - 128);
this.bank.noteon(this.chip, this.ticktime < 128 ? (byte)0 : (byte)9, (byte)(this.ticktime < 128 ? (byte)36 : (this.ticktime - 128)), (byte)127);
this.bank.noteon(this.ticktime < 128 ? 0 : 9, this.ticktime < 128 ? 36 : this.ticktime - 128, 127);
this.ticktime++;
if(this.ticktime == 128)
this.ticktime = 128 + 35;
@ -8147,7 +8149,7 @@ public class AudioInterface implements Runnable {
if(!this.playing && (this.nowait || !this.chip.isPlaying())) {
return -1L;
}
else if(this.playing && this.tick(this.bank, this.chip)) {
else if(this.playing && this.tick(this.bank)) {
return ((long)this.ticktime) * 1000L;
}
else if(this.playing) {
@ -8165,198 +8167,87 @@ public class AudioInterface implements Runnable {
public static class BankHandle {
public class BankVoice {
final OPLChannel opl;
BankChannel channel;
OPLChannel opl;
byte note;
BankOp op;
int detune;
BankVoice pair;
int note = -1;
BankVoice(OPLChannel opl) {
this.opl = opl;
}
}
public class BankKey {
byte note;
byte velocity;
int velocity;
BankVoice voice;
}
public class BankChannel {
BankKey[] keys = new BankKey[BANK_MAX];
byte[] notes = new byte[128];
byte keyindex;
byte active;
final BankKey[] keys = new BankKey[BANK_MAX];
final int[] notes = new int[128];
int keyindex;
int active;
int pbank;
byte pan;
byte volume;
short pitch;
byte program;
int pan;
int volume = 127;
int pitch;
int program;
BankInstr instr;
int ch_num;
BankChannel(boolean drum) {
Arrays.fill(this.notes, -1);
this.volume = 127;
this.pbank = drum ? 128 : 0;
this.instr = BankHandle.this.instruments[drum ? 128 : 0];
for(int z = 0; z < this.keys.length; z++) {
this.keys[z] = new BankKey();
}
}
}
private static final int BANK_MAX = 64;
private static final double BANK_PBRANGE = 2.0;
private static final int[] 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
};
private static final int[] opl3_maxfreq = {
48503,
97006,
194013,
388026,
776053,
1552107,
3104215,
6208431
};
public final BankChannel[] channels = new BankChannel[16];
public final BankVoice[] voices;
public final BankInstr[] instruments;
public final boolean keep;
public final boolean useunkn;
public final byte velo_func;
public int index;
public int used;
public BankHandle(OPLChip chip, BankInstr[] instr, boolean keep, boolean useunkn, byte velofunc) {
public BankHandle(OPLChip chip, BankInstr[] instr, boolean keep, boolean useunkn) {
this.keep = keep;
this.useunkn = useunkn;
this.velo_func = velofunc;
this.instruments = instr;
this.voices = new BankVoice[chip.channel.length];
this.reset(chip);
for(int z = 0; z < this.channels.length; z++) {
this.channels[z] = new BankChannel(z == 9);
}
public void reset(OPLChip chip) {
this.index = 0;
this.used = 0;
for(int h = 0; h < this.channels.length; h++) {
BankChannel channel = this.channels[h] = new BankChannel();
Arrays.fill(channel.notes, (byte)0xff);
channel.volume = 127;
channel.pbank = (h == 9) ? 128 : 0;
channel.instr = this.instruments[(h == 9) ? 128 : 0];
channel.ch_num = h;
for(int z = 0; z < channel.keys.length; z++) {
channel.keys[z] = new BankKey();
for(int z = 0; z < this.voices.length; z++) {
this.voices[z] = new BankVoice(chip.channel[z]);
}
}
for(int h = 0; h < this.voices.length; h++) {
BankVoice voice = this.voices[h] = new BankVoice();
voice.opl = chip.channel[h];
voice.note = (byte)0xff;
voice.op = BankOp.b_op2;
voice.detune = 0;
}
for(int h = 0; h < this.voices.length; h++) {
this.voices[h].pair = this.voices[h+(((h & 1) == 0) ? 1 : -1)];
}
}
private static byte getnote(BankChannel ch, byte key, int id, boolean drum, BankInstr instr) {
int note = (int)key;
if(instr.fixed) { // || drum
note = 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;
}
private static void OPL3_ChannelFreqHz(OPLChannel channel, int freq) {
int block = 0;
while(opl3_maxfreq[block] < freq) {
block++;
if(block == 8) {
break;
}
}
if(block == 8) {
channel.setFrequency(7, 1023);
return;
}
double f_num = ((double)freq) / 1000.0 * Math.pow(2.0, (20.0-((double)block))) / 49716.0;
channel.setFrequency(block, (int)f_num);
}
private static int getfreq(byte key, int pitch, int detune) {
double pfrq = Math.pow(2.0, ((((double)pitch) / 8191.0 * BANK_PBRANGE) + (((double)detune) / 100.0)) / 12.0);
double freq = ((double)bank_notes[key]) * pfrq;
return (int)freq;
}
private static void OPL3_ChannelNote(OPLChannel channel, byte key, int pitch, int detune) {
OPL3_ChannelFreqHz(channel, getfreq(key, pitch, detune));
}
private static double velofunc(double x, double n) {
return 1.0-Math.log(1.0+((1.0-Math.pow(x,n))*(Math.E-1.0)));
}
private static int getlevel(byte function, byte velocity, byte volume, byte pan) {
double lvl = ((function == -128) ? 1.0 :
(function != 0 ? 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 (int)lvl;
}
private static void OPL3_ChannelLevelPan(OPLChannel channel, byte function, byte velocity, byte volume, byte pan) {
channel.setLevel(0, getlevel(function, velocity, volume, pan));
channel.setLevel(1, getlevel(function, velocity, volume, (byte)-pan));
}
private void releaseVoice(BankChannel ch, BankVoice voice) {
if(voice.note == (byte)0xff) {
if(voice.note == -1)
return;
}
voice.opl.keyOff();
voice.note = (byte)0xff;
if(voice.op == BankOp.b_op22) {
voice.pair.note = (byte)0xff;
voice.pair.opl.keyOff();
this.used -= 2;
}
else if(voice.op == BankOp.b_op4) {
voice.pair.note = (byte)0xff;
this.used -= 2;
}
else {
voice.opl.key(false);
voice.note = -1;
this.used -= 1;
}
}
private BankVoice releaseKey(BankChannel ch, byte note) {
private BankVoice releaseKey(BankChannel ch, int note) {
if(/* ch == null || */ ch.active == 0) {
return null;
}
if(ch.notes[note] == (byte)0xff) {
if(ch.notes[note] == -1) {
return null;
}
BankKey key = ch.keys[ch.notes[note]];
ch.notes[note] = (byte)0xff;
ch.notes[note] = -1;
ch.active -= 1;
if(key.voice != null) {
this.releaseVoice(ch, key.voice);
@ -8365,130 +8256,67 @@ public class AudioInterface implements Runnable {
return key.voice;
}
private void initVoice(OPLChannel channel, BankInstr instr, int id) {
channel.set4Op(instr.op == BankOp.b_op4);
for(int o = 0; o < ((instr.op == BankOp.b_op4) ? 4 : 2); o++) {
BankPair pair = instr.channels[(instr.op == BankOp.b_op4) ? (o >> 1) : id];
BankOperator op = pair.ops[o & 1];
OPLSlot slot = (o >= 2) ? (channel.pair.slots[o & 1]) : (channel.slots[o & 1]);
private BankVoice getVoice(BankChannel ch, int note, int velocity) {
BankInstr instr = ch.instr;
if(ch.pbank == 128)
instr = this.instruments[128 + note];
else if(ch.pbank != 0 && !this.useunkn)
return null;
if(instr.ops.length == 0)
return null;
if(this.used == this.voices.length) {
if(this.keep)
return null;
this.releaseKey(this.voices[this.index].channel, this.voices[this.index].note);
}
int vi = this.index;
while(this.voices[this.index].note != -1) {
this.index += 1;
if(this.index >= this.voices.length)
this.index = 0;
if(vi == this.index) {
if(this.keep)
return null;
this.releaseKey(this.voices[this.index].channel, this.voices[this.index].note);
break;
}
}
BankVoice voice = this.voices[this.index];
this.index += 1;
if(this.index >= this.voices.length)
this.index = 0;
voice.channel = ch;
voice.note = note;
voice.opl.setAlgo(instr.algo, instr.feedback1, instr.feedback2);
voice.opl.setLevel(velocity, ch.volume, ch.pan);
for(int o = 0; o < instr.ops.length; o++) {
BankOperator op = instr.ops[o];
OPLSlot slot = voice.opl.slots[o];
slot.setFlags(op.tremolo, op.vibrato, op.sustaining, op.ksr);
slot.setMultiplier(op.mult);
slot.setKSL(op.ksl);
slot.setLevel(op.level);
slot.setEnvelope(op.attack, op.decay, op.sustain, op.release);
slot.setWaveform(op.waveform);
if((o & 1) == 1) {
OPLChannel chn = (o >= 2) ? channel.pair : channel;
chn.setFeedback(pair.feedback);
chn.setAM(pair.am);
slot.setOffset(op.offset);
slot.setDetune(op.detune);
slot.setFixed(instr.fixed ? instr.percnum : -1);
slot.setNote(note);
slot.setPitch(ch.pitch);
}
}
}
private BankVoice getVoice(BankChannel ch, byte note, byte velocity) {
BankInstr instr = ch.instr;
if(ch.pbank == 128) {
instr = this.instruments[128 + note];
}
else if(ch.pbank != 0 && !this.useunkn) {
return null;
}
if(instr.op == BankOp.b_op0) {
return null;
}
if(this.used == this.voices.length) {
if(this.keep) {
return null;
}
if((instr.op != BankOp.b_op2) && ((this.index & 1) != 0)) {
this.index += 1;
if(this.index >= this.voices.length) {
this.index = 0;
}
}
this.releaseKey(this.voices[this.index].channel, this.voices[this.index].note);
}
else if((instr.op != BankOp.b_op2) && ((this.index & 1) != 0)) {
this.index += 1;
if(this.index >= this.voices.length) {
this.index = 0;
}
}
int vi = this.index;
while((this.voices[this.index].note != (byte)0xff) || ((instr.op != BankOp.b_op2) && (this.voices[this.index+1].note != (byte)0xff))) {
this.index += (instr.op != BankOp.b_op2) ? 2 : 1;
if(this.index >= this.voices.length) {
this.index = 0;
}
if(vi == this.index) {
if(this.keep) {
return null;
}
this.releaseKey(this.voices[this.index].channel, this.voices[this.index].note);
if((instr.op != BankOp.b_op2) && (this.voices[this.index+1].note != (byte)0xff)) {
this.releaseKey(this.voices[this.index+1].channel, this.voices[this.index+1].note);
}
break;
}
}
BankVoice voice = this.voices[this.index];
if((instr.op == BankOp.b_op2) && (voice.op == BankOp.b_op4)) {
int offset = this.index + ((this.index & 1) == 0 ? 1 : -1);
BankVoice voice2 = this.voices[offset];
if(voice2.note == (byte)0xff) {
voice2.opl.setLevel(0, 0);
voice2.opl.setLevel(1, 0);
}
}
this.index += (instr.op != BankOp.b_op2) ? 2 : 1;
if(this.index >= this.voices.length) {
this.index = 0;
}
voice.channel = ch;
voice.note = note;
voice.op = instr.op;
voice.detune = instr.channels[0].detune;
this.initVoice(voice.opl, instr, 0);
if(voice.op == BankOp.b_op22) {
voice.pair.channel = ch;
voice.pair.note = note;
voice.pair.op = BankOp.b_op22;
voice.pair.detune = instr.channels[1].detune;
this.initVoice(voice.pair.opl, instr, 1);
OPL3_ChannelNote(voice.opl, getnote(ch, note, 0, ch.pbank == 128, instr), ch.pitch, voice.detune);
OPL3_ChannelNote(voice.pair.opl, getnote(ch, note, 1, ch.pbank == 128, instr), ch.pitch, voice.pair.detune);
OPL3_ChannelLevelPan(voice.opl, this.velo_func, velocity, ch.volume, ch.pan);
OPL3_ChannelLevelPan(voice.pair.opl, this.velo_func, velocity, ch.volume, ch.pan);
voice.opl.keyOn();
voice.pair.opl.keyOn();
this.used += 2;
}
else if(voice.op == BankOp.b_op4) {
voice.pair.channel = ch;
voice.pair.note = note;
voice.pair.op = BankOp.b_op4;
OPL3_ChannelNote(voice.opl, getnote(ch, note, 0, ch.pbank == 128, instr), ch.pitch, voice.detune);
OPL3_ChannelLevelPan(voice.opl, this.velo_func, velocity, ch.volume, ch.pan);
voice.opl.keyOn();
this.used += 2;
}
else {
OPL3_ChannelNote(voice.opl, getnote(ch, note, 0, ch.pbank == 128, instr), ch.pitch, voice.detune);
OPL3_ChannelLevelPan(voice.opl, this.velo_func, velocity, ch.volume, ch.pan);
voice.opl.keyOn();
voice.opl.key(true);
this.used += 1;
}
return voice;
}
private BankVoice pressKey(BankChannel ch, byte note, byte velocity) {
if(ch.notes[note] != (byte)0xff) {
private BankVoice pressKey(BankChannel ch, int note, int velocity) {
if(ch.notes[note] != -1) {
this.releaseKey(ch, note);
}
if(ch.active == BANK_MAX) {
return null;
}
byte ki = ch.keyindex;
int ki = ch.keyindex;
while(ch.keys[ch.keyindex].velocity != 0) {
ch.keyindex += 1;
if(ch.keyindex == BANK_MAX) {
@ -8502,21 +8330,20 @@ public class AudioInterface implements Runnable {
ch.notes[note] = ch.keyindex;
ch.active += 1;
BankKey key = ch.keys[ch.keyindex];
key.note = note;
key.velocity = velocity;
key.voice = voice;
return voice;
}
private void notesoff(BankChannel ch) {
for(int h = 0; (h < BANK_MAX) && (ch.active > 0); h++) {
this.releaseKey(ch, (byte)h);
for(int h = 0; h < ch.notes.length && ch.active > 0; h++) {
this.releaseKey(ch, h);
}
}
private void progupdate(BankChannel ch, OPLChip chip) {
private void progupdate(BankChannel ch) {
this.notesoff(ch);
short id = ch.program;
int id = ch.program;
if(ch.pbank == 128) {
id = 128;
}
@ -8526,37 +8353,33 @@ public class AudioInterface implements Runnable {
ch.instr = this.instruments[id];
}
private void levelupdate(BankChannel ch, OPLChip chip) {
byte done = 0;
private void levelupdate(BankChannel ch) {
int done = 0;
for(int h = 0; (h < BANK_MAX) && (done < ch.active); h++) {
BankKey key = ch.keys[h];
if((key.velocity == 0) || (key.voice == null)) {
continue;
}
OPL3_ChannelLevelPan(key.voice.opl, this.velo_func, key.velocity, ch.volume, ch.pan);
if(key.voice.op == BankOp.b_op22) {
OPL3_ChannelLevelPan(key.voice.pair.opl, this.velo_func, key.velocity, ch.volume, ch.pan);
}
key.voice.opl.setLevel(key.velocity, ch.volume, ch.pan);
done++;
}
}
private void frequpdate(BankChannel ch, OPLChip chip) {
byte done = 0;
private void frequpdate(BankChannel ch) {
int done = 0;
for(int h = 0; (h < BANK_MAX) && (done < ch.active); h++) {
BankKey key = ch.keys[h];
if((key.velocity == 0) || (key.voice == null)) {
continue;
}
OPL3_ChannelNote(key.voice.opl, getnote(ch, key.note, 0, ch.pbank == 128, ch.instr), ch.pitch, key.voice.detune);
if(key.voice.op == BankOp.b_op22) {
OPL3_ChannelNote(key.voice.pair.opl, getnote(ch, key.note, 1, ch.pbank == 128, ch.instr), ch.pitch, key.voice.pair.detune);
for(OPLSlot slot : key.voice.opl.slots) {
slot.setPitch(ch.pitch);
}
done++;
}
}
public void noteon(OPLChip chip, byte channel, byte key, byte velocity) {
public void noteon(int channel, int key, int velocity) {
BankChannel ch = this.channels[channel];
// if((ch.pbank == 128) && ((key < 35) || (key > 81))) {
// return;
@ -8564,7 +8387,7 @@ public class AudioInterface implements Runnable {
this.pressKey(ch, key, velocity);
}
public void noteoff(OPLChip chip, byte channel, byte key, byte velocity) {
public void noteoff(int channel, int key, int velocity) {
BankChannel ch = this.channels[channel];
// if((ch.pbank == 128) && ((key < 35) || (key > 81))) {
// return;
@ -8572,19 +8395,19 @@ public class AudioInterface implements Runnable {
this.releaseKey(ch, key);
}
public void progchange(OPLChip chip, byte channel, byte program) {
public void progchange(int channel, int program) {
BankChannel ch = this.channels[channel];
ch.program = program;
this.progupdate(ch, chip);
this.progupdate(ch);
}
public void pitchbend(OPLChip chip, byte channel, short pitch) {
public void pitchbend(int channel, int pitch) {
BankChannel ch = this.channels[channel];
ch.pitch = pitch;
this.frequpdate(ch, chip);
ch.pitch = (int)((((double)pitch) / 8191.0 * BANK_PBRANGE) * 10000.0);
this.frequpdate(ch);
}
public void control(OPLChip chip, byte channel, byte control, byte value) {
public void control(int channel, int control, int value) {
BankChannel ch = this.channels[channel];
switch(control) {
case 0x00: // bank MSB
@ -8594,9 +8417,9 @@ public class AudioInterface implements Runnable {
// else {
if(channel != 9) {
ch.pbank &= 0x007f;
ch.pbank |= (((short)value) << 7) & 0x3f80;
ch.pbank |= (value << 7) & 0x3f80;
}
this.progupdate(ch, chip);
this.progupdate(ch);
break;
case 0x20: // bank LSB
// if((channel == 9) && (value == 0)) {
@ -8605,24 +8428,24 @@ public class AudioInterface implements Runnable {
// else {
if(channel != 9) {
ch.pbank &= 0x3f80;
ch.pbank |= ((short)value) & 0x007f;
ch.pbank |= value & 0x007f;
}
this.progupdate(ch, chip);
this.progupdate(ch);
break;
case 0x07: // volume MSB
ch.volume = value;
this.levelupdate(ch, chip);
this.levelupdate(ch);
break;
case 0x0a: // pan MSB
ch.pan = (byte)(value - 64);
this.levelupdate(ch, chip);
ch.pan = value - 64;
this.levelupdate(ch);
break;
case 0x79: // reset
ch.pbank = (channel == 9) ? 128 : 0;
ch.volume = 127;
ch.pan = 0;
this.progupdate(ch, chip);
this.levelupdate(ch, chip);
this.progupdate(ch);
this.levelupdate(ch);
break;
case 0x7b: // all off
this.notesoff(ch);
@ -8637,13 +8460,6 @@ public class AudioInterface implements Runnable {
}
}
public static enum BankOp {
b_op2,
b_op4,
b_op22,
b_op0
};
public static class BankOperator {
public final int mult;
public final int level;
@ -8658,6 +8474,8 @@ public class AudioInterface implements Runnable {
boolean sustaining;
boolean ksr;
int ksl;
int offset;
int detune;
public BankOperator(int mult, int level, int waveform, int a, int d, int s, int r) {
this.mult = mult;
@ -8693,50 +8511,58 @@ public class AudioInterface implements Runnable {
this.ksl = ksl;
return this;
}
public BankOperator offset(int offset) {
this.offset = offset;
return this;
}
public static class BankPair {
public final BankOperator[] ops;
public final int detune;
public final int offset;
public final int feedback;
public final boolean am;
public BankPair(BankOperator op1, BankOperator op2, int detune, int offset, int feedback, boolean am) {
this.ops = new BankOperator[] {op1, op2};
public BankOperator detune(int detune) {
this.detune = detune;
this.offset = offset;
this.feedback = feedback;
this.am = am;
return this;
}
}
public static class BankInstr {
public final BankPair[] channels;
public final BankOp op;
public final BankOperator[] ops;
public final int feedback1;
public final int feedback2;
public final int algo;
public boolean fixed;
public int percnum;
private BankInstr(BankPair ch1, BankPair ch2, BankOp op) {
this.channels = new BankPair[] {ch1, ch2};
this.op = op;
public BankInstr(int feedback1, int feedback2, int algo, BankOperator ... ops) {
this.ops = ops;
this.feedback1 = feedback1;
this.feedback2 = feedback2;
this.algo = algo;
}
public BankInstr() {
this(null, null, BankOp.b_op0);
this(0, 0, 0);
}
public BankInstr(BankOperator op1, BankOperator op2, boolean am, int detune, int offset, int feedback) {
this(new BankPair(op1, op2, detune, offset, feedback, am), null, BankOp.b_op2);
this(feedback, 0, am ? 1 : 0, op1, op2);
op1.offset(offset).detune(detune);
op2.offset(offset).detune(detune);
}
public BankInstr(BankOperator op1A, BankOperator op2A, boolean amA, int detuneA, int offsetA, int feedbackA,
BankOperator op1B, BankOperator op2B, boolean amB, int detuneB, int offsetB, int feedbackB) {
this(new BankPair(op1A, op2A, detuneA, offsetA, feedbackA, amA), new BankPair(op1B, op2B, detuneB, offsetB, feedbackB, amB), BankOp.b_op22);
this(feedbackA, feedbackB, 0x08 | (amA ? 1 : 0) | (amB ? 2 : 0), op1A, op2A, op1B, op2B);
op1A.offset(offsetA).detune(detuneA);
op2A.offset(offsetA).detune(detuneA);
op1B.offset(offsetB).detune(detuneB);
op2B.offset(offsetB).detune(detuneB);
}
public BankInstr(BankOperator op1, BankOperator op2, BankOperator op3, BankOperator op4, int algo, int detune, int offset, int feedback) {
this(new BankPair(op1, op2, detune, offset, feedback, (algo & 2) != 0), new BankPair(op3, op4, detune, offset, feedback, (algo & 1) != 0), BankOp.b_op4);
this(feedback, 0, 0x04 | (algo & 3), op1, op2, op3, op4);
op1.offset(offset).detune(detune);
op2.offset(offset).detune(detune);
op3.offset(offset).detune(detune);
op4.offset(offset).detune(detune);
}
public BankInstr setFixed() {

View file

@ -1,12 +1,6 @@
package client.audio;
public class OPLChip {
private static enum ChType {
CH2OP,
CH4OP1,
CH4OP2
};
private static enum EnvState {
ATTACK,
DECAY,
@ -23,39 +17,54 @@ public class OPLChip {
}
public class OPLSlot implements ModGen {
OPLChannel channel;
short out;
short fbmodv;
final ModGen fbmod = new ModGen() {
public short getModulation() {
return OPLSlot.this.fbmodv;
private final OPLChannel channel;
public short out;
public short fbmodv;
public final ModGen fbmod = () -> OPLSlot.this.fbmodv;
public ModGen mod;
public int fb;
public short prout;
public short eg_rout;
public short eg_out;
public EnvState eg_gen;
public byte eg_ksl;
public ModGen trem;
public boolean reg_vib;
public boolean reg_type;
public boolean reg_ksr;
public byte reg_mult;
public byte reg_ksl;
public byte reg_tl;
public byte reg_ar;
public byte reg_dr;
public byte reg_sl;
public byte reg_rr;
public int reg_wf;
public boolean key;
public boolean detrigger;
public boolean retrigger;
public boolean pg_reset;
public int pg_phase;
public int pg_phase_out;
public int f_num;
public int block;
public int ksv;
public int frequency = -1;
public int offset;
public int fixed;
public int note;
public int detune;
public int pitch;
public OPLSlot(OPLChannel channel) {
this.channel = channel;
this.mod = OPLChip.this.zeromod;
this.eg_rout = 0x1ff;
this.eg_out = 0x1ff;
this.eg_gen = EnvState.RELEASE;
this.trem = OPLChip.this.zeromod;
}
};
ModGen mod;
short prout;
short eg_rout;
short eg_out;
EnvState eg_gen;
byte eg_ksl;
ModGen trem;
byte reg_vib;
byte reg_type;
byte reg_ksr;
byte reg_mult;
byte reg_ksl;
byte reg_tl;
byte reg_ar;
byte reg_dr;
byte reg_sl;
byte reg_rr;
byte reg_wf;
boolean key;
boolean detrigger;
boolean retrigger;
boolean pg_reset;
int pg_phase;
short pg_phase_out;
int slot_num;
public short getModulation() {
return this.out;
@ -64,9 +73,9 @@ public class OPLChip {
private void process()
{
// feedback
if (this.channel.fb != 0x00)
if (this.fb != 0x00)
{
this.fbmodv = (short)((this.prout + this.out) >> (0x09 - this.channel.fb));
this.fbmodv = (short)((this.prout + this.out) >> (0x09 - this.fb));
}
else
{
@ -106,7 +115,7 @@ public class OPLChip {
reg_rate = this.reg_dr;
break;
case SUSTAIN:
if (this.reg_type == 0)
if (!this.reg_type)
{
reg_rate = this.reg_rr;
}
@ -117,7 +126,7 @@ public class OPLChip {
}
}
this.pg_reset = reset;
ks = (byte)(this.channel.ksv >> ((this.reg_ksr ^ 1) << 1));
ks = (byte)(this.ksv >> (this.reg_ksr ? 0 : 2));
nonzero = (reg_rate != 0);
rate = (byte)(ks + (reg_rate << 2));
rate_hi = (byte)(rate >> 2);
@ -132,7 +141,7 @@ public class OPLChip {
{
if (rate_hi < 12)
{
if (OPLChip.this.eg_state != 0)
if (OPLChip.this.eg_state)
{
switch (eg_shift)
{
@ -159,7 +168,7 @@ public class OPLChip {
}
if (shift == 0)
{
shift = OPLChip.this.eg_state;
shift = (byte)(OPLChip.this.eg_state ? 1 : 0);
}
}
}
@ -223,15 +232,15 @@ public class OPLChip {
this.detrigger = false;
this.retrigger = false;
// phase
short f_num;
int f_num;
int basefreq;
short phase;
f_num = this.channel.f_num;
if (this.reg_vib != 0)
int phase;
f_num = this.f_num;
if (this.reg_vib)
{
byte range;
byte vibpos;
range = (byte)((f_num >> 7) & 7);
int range;
int vibpos;
range = (f_num >> 7) & 7;
vibpos = OPLChip.this.vibpos;
if ((vibpos & 3) == 0)
{
@ -248,12 +257,10 @@ public class OPLChip {
}
f_num += range;
}
basefreq = (f_num << this.channel.block) >> 1;
phase = (short)(this.pg_phase >> 9);
basefreq = (f_num << this.block) >> 1;
phase = this.pg_phase >> 9;
if (this.pg_reset)
{
this.pg_phase = 0;
}
this.pg_phase += (basefreq * MULT[this.reg_mult]) >> 1;
this.pg_phase_out = phase;
// output
@ -262,8 +269,8 @@ public class OPLChip {
private void updateKSL()
{
short ksl = (short)((KSL[this.channel.f_num >> 6] << 2)
- ((0x08 - this.channel.block) << 5));
short ksl = (short)((KSL[this.f_num >> 6] << 2)
- ((0x08 - this.block) << 5));
if (ksl < 0)
{
ksl = 0;
@ -290,18 +297,15 @@ public class OPLChip {
this.retrigger = false;
}
private void setFeedback(int feedback) {
this.fb = feedback;
}
public void setFlags(boolean tremolo, boolean vibrato, boolean sustaining, boolean ksr) {
if (tremolo)
{
this.trem = OPLChip.this.tremolo;
}
else
{
this.trem = OPLChip.this.zeromod;
}
this.reg_vib = (byte)(vibrato ? 1 : 0);
this.reg_type = (byte)(sustaining ? 1 : 0);
this.reg_ksr = (byte)(ksr ? 1 : 0);
this.trem = tremolo ? OPLChip.this.tremolo : OPLChip.this.zeromod;
this.reg_vib = vibrato;
this.reg_type = sustaining;
this.reg_ksr = ksr;
}
public void setMultiplier(int mult) {
@ -335,42 +339,100 @@ public class OPLChip {
public void setWaveform(int waveform) {
this.reg_wf = (byte)(waveform & 0x07);
}
private void updateFrequency() {
int freq = this.frequency;
if(freq < 0) {
int key = this.note;
if(this.fixed >= 0)
key = this.fixed + this.offset;
else
key += this.offset;
key = key < 0 ? 0 : key;
key = key > 127 ? 127 : key;
double pfrq = Math.pow(2.0, ((((double)this.pitch) / 10000.0) + (((double)this.detune) / 100.0)) / 12.0);
double dfreq = ((double)NOTES[key]) * pfrq;
freq = (int)dfreq;
}
int block = 0;
while(MAX_FREQ[block] < freq) {
block++;
if(block == 8) {
break;
}
}
if(block == 8) {
this.block = 7;
this.f_num = 1023;
}
else {
double f_num = ((double)freq) / 1000.0 * Math.pow(2.0, (20.0-((double)block))) / 49716.0;
this.block = block;
this.f_num = (int)f_num;
}
this.ksv = (this.block << 1) | ((this.f_num >> (0x09 - OPLChip.this.nts)) & 0x01);
this.updateKSL();
}
public void setNote(int note) {
this.note = note;
this.frequency = -1;
this.updateFrequency();
}
public void setOffset(int offset) {
this.offset = offset;
this.frequency = -1;
this.updateFrequency();
}
public void setFixed(int fixed) {
this.fixed = fixed;
this.frequency = -1;
this.updateFrequency();
}
public void setDetune(int detune) {
this.detune = detune;
this.frequency = -1;
this.updateFrequency();
}
public void setPitch(int pitch) {
this.pitch = pitch;
this.frequency = -1;
this.updateFrequency();
}
public void setFrequency(int freq) {
this.frequency = freq;
this.updateFrequency();
}
};
public class OPLChannel {
OPLSlot[] slots = new OPLSlot[2];
OPLChannel pair;
ModGen[] out = new ModGen[4];
public final OPLSlot[] slots = new OPLSlot[4];
public final ModGen[] out = new ModGen[this.slots.length];
public final int[] level = new int[2];
int[] level = new int[2];
ChType chtype;
short f_num;
byte block;
byte fb;
byte con;
byte alg;
byte ksv;
int ch_num;
private void setupAlgo()
{
if ((this.alg & 0x08) != 0)
{
return;
public OPLChannel() {
for(int z = 0; z < this.slots.length; z++) {
this.slots[z] = new OPLSlot(this);
}
if ((this.alg & 0x04) != 0)
this.setAlgo(0, 0, 0);
this.setLevel(0, 0, 0);
}
public void setAlgo(int algo, int feedback1, int feedback2) {
if ((algo & 0x04) != 0)
{
this.pair.out[0] = OPLChip.this.zeromod;
this.pair.out[1] = OPLChip.this.zeromod;
this.pair.out[2] = OPLChip.this.zeromod;
this.pair.out[3] = OPLChip.this.zeromod;
switch (this.alg & 0x03)
switch (algo & 0x03)
{
case 0x00:
this.pair.slots[0].mod = this.pair.slots[0].fbmod;
this.pair.slots[1].mod = this.pair.slots[0];
this.slots[0].mod = this.pair.slots[1];
this.slots[2].setFeedback(feedback1);
this.slots[2].mod = this.slots[2].fbmod;
this.slots[3].mod = this.slots[2];
this.slots[0].mod = this.slots[3];
this.slots[1].mod = this.slots[0];
this.out[0] = this.slots[1];
this.out[1] = OPLChip.this.zeromod;
@ -378,31 +440,34 @@ public class OPLChip {
this.out[3] = OPLChip.this.zeromod;
break;
case 0x01:
this.pair.slots[0].mod = this.pair.slots[0].fbmod;
this.pair.slots[1].mod = this.pair.slots[0];
this.slots[2].setFeedback(feedback1);
this.slots[2].mod = this.slots[2].fbmod;
this.slots[3].mod = this.slots[2];
this.slots[0].mod = OPLChip.this.zeromod;
this.slots[1].mod = this.slots[0];
this.out[0] = this.pair.slots[1];
this.out[0] = this.slots[3];
this.out[1] = this.slots[1];
this.out[2] = OPLChip.this.zeromod;
this.out[3] = OPLChip.this.zeromod;
break;
case 0x02:
this.pair.slots[0].mod = this.pair.slots[0].fbmod;
this.pair.slots[1].mod = OPLChip.this.zeromod;
this.slots[0].mod = this.pair.slots[1];
this.slots[2].setFeedback(feedback1);
this.slots[2].mod = this.slots[2].fbmod;
this.slots[3].mod = OPLChip.this.zeromod;
this.slots[0].mod = this.slots[3];
this.slots[1].mod = this.slots[0];
this.out[0] = this.pair.slots[0];
this.out[0] = this.slots[2];
this.out[1] = this.slots[1];
this.out[2] = OPLChip.this.zeromod;
this.out[3] = OPLChip.this.zeromod;
break;
case 0x03:
this.pair.slots[0].mod = this.pair.slots[0].fbmod;
this.pair.slots[1].mod = OPLChip.this.zeromod;
this.slots[0].mod = this.pair.slots[1];
this.slots[2].setFeedback(feedback1);
this.slots[2].mod = this.slots[2].fbmod;
this.slots[3].mod = OPLChip.this.zeromod;
this.slots[0].mod = this.slots[3];
this.slots[1].mod = OPLChip.this.zeromod;
this.out[0] = this.pair.slots[0];
this.out[0] = this.slots[2];
this.out[1] = this.slots[0];
this.out[2] = this.slots[1];
this.out[3] = OPLChip.this.zeromod;
@ -411,128 +476,74 @@ public class OPLChip {
}
else
{
switch (this.alg & 0x01)
this.slots[0].setFeedback(feedback1);
this.slots[0].mod = this.slots[0].fbmod;
switch (algo & 0x01)
{
case 0x00:
this.slots[0].mod = this.slots[0].fbmod;
this.slots[1].mod = this.slots[0];
this.out[0] = this.slots[1];
this.out[1] = OPLChip.this.zeromod;
this.out[2] = OPLChip.this.zeromod;
this.out[3] = OPLChip.this.zeromod;
break;
case 0x01:
this.slots[0].mod = this.slots[0].fbmod;
this.slots[1].mod = OPLChip.this.zeromod;
this.out[0] = this.slots[0];
this.out[1] = this.slots[1];
this.out[2] = OPLChip.this.zeromod;
break;
}
if((algo & 0x08) != 0) {
this.slots[2].setFeedback(feedback2);
this.slots[2].mod = this.slots[2].fbmod;
switch (algo & 0x02)
{
case 0x00:
this.slots[3].mod = this.slots[2];
this.out[2] = this.slots[3];
this.out[3] = OPLChip.this.zeromod;
break;
case 0x02:
this.slots[3].mod = OPLChip.this.zeromod;
this.out[2] = this.slots[2];
this.out[3] = this.slots[3];
break;
}
}
else {
this.slots[2].mod = OPLChip.this.zeromod;
this.slots[3].mod = OPLChip.this.zeromod;
this.out[2] = OPLChip.this.zeromod;
this.out[3] = OPLChip.this.zeromod;
}
}
}
private void updateAlgo()
{
this.alg = this.con;
if (this.chtype == ChType.CH4OP1)
{
this.pair.alg = (byte)(0x04 | (this.con << 1) | (this.pair.con));
this.alg = 0x08;
this.pair.setupAlgo();
private static double velofunc(double x, double n) {
return 1.0-Math.log(1.0+((1.0-Math.pow(x,n))*(Math.E-1.0)));
}
else if (this.chtype == ChType.CH4OP2)
{
this.alg = (byte)(0x04 | (this.pair.con << 1) | (this.con));
this.pair.alg = 0x08;
this.setupAlgo();
private void updateLevel(boolean right, int velocity, int volume, int pan) {
int function = (int)OPLChip.this.velo_func;
double lvl = ((function == -128) ? 1.0 :
(function != 0 ? 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)(right ? -pan : pan))/63.0));
// fprintf(stderr, "%d===%d-.%.2f\n", function, velocity, lvl);
lvl *= 65536.0;
this.level[right ? 1 : 0] = (int)lvl;
}
public void setLevel(int velocity, int volume, int pan) {
this.updateLevel(false, velocity, volume, pan);
this.updateLevel(true, velocity, volume, pan);
}
public void key(boolean on) {
if(on)
for(OPLSlot slot : this.slots) {
slot.keyOn();
}
else
{
this.setupAlgo();
}
}
public void setFrequency(int block, int f_num) {
if (this.chtype == ChType.CH4OP2)
{
return;
}
this.f_num = (short)(f_num & 0x3ff);
this.block = (byte)(block & 0x07);
this.ksv = (byte)((this.block << 1)
| ((this.f_num >> (0x09 - OPLChip.this.nts)) & 0x01));
this.slots[0].updateKSL();
this.slots[1].updateKSL();
if (this.chtype == ChType.CH4OP1)
{
this.pair.f_num = this.f_num;
this.pair.block = this.block;
this.pair.ksv = this.ksv;
this.pair.slots[0].updateKSL();
this.pair.slots[1].updateKSL();
}
}
public void setLevel(int output, int level) {
this.level[output & 1] = level;
}
public void set4Op(boolean op4) {
if (op4)
{
this.chtype = ChType.CH4OP1;
this.pair.chtype = ChType.CH4OP2;
this.updateAlgo();
}
else
{
this.chtype = ChType.CH2OP;
this.pair.chtype = ChType.CH2OP;
this.updateAlgo();
this.pair.updateAlgo();
}
}
public void setFeedback(int feedback) {
this.fb = (byte)(feedback & 0x07);
}
public void setAM(boolean am) {
this.con = (byte)(am ? 1 : 0);
this.updateAlgo();
}
public void keyOn()
{
if (this.chtype == ChType.CH4OP1)
{
this.slots[0].keyOn();
this.slots[1].keyOn();
this.pair.slots[0].keyOn();
this.pair.slots[1].keyOn();
}
else if (this.chtype == ChType.CH2OP)
{
this.slots[0].keyOn();
this.slots[1].keyOn();
}
}
public void keyOff()
{
if (this.chtype == ChType.CH4OP1)
{
this.slots[0].keyOff();
this.slots[1].keyOff();
this.pair.slots[0].keyOff();
this.pair.slots[1].keyOff();
}
else if (this.chtype == ChType.CH2OP)
{
this.slots[0].keyOff();
this.slots[1].keyOff();
for(OPLSlot slot : this.slots) {
slot.keyOff();
}
}
}
@ -611,7 +622,7 @@ public class OPLChip {
0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400
};
private static final byte[] MULT = {
private static final int[] MULT = {
1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
};
@ -630,6 +641,36 @@ public class OPLChip {
{ 1, 1, 1, 0 }
};
private static final int[] 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
};
private static final int[] MAX_FREQ = {
48503,
97006,
194013,
388026,
776053,
1552107,
3104215,
6208431
};
private static final WaveFunc[] WAVE_FUNCS = {
OPLChip::wave0,
OPLChip::wave1,
@ -641,18 +682,9 @@ public class OPLChip {
OPLChip::wave7
};
final OPLChannel[] channel;
final OPLSlot[] slot;
final ModGen tremolo = new ModGen() {
public short getModulation() {
return OPLChip.this.tremolov;
}
};
final ModGen zeromod = new ModGen() {
public short getModulation() {
return 0;
}
};
public final OPLChannel[] channel;
public final ModGen tremolo = () -> OPLChip.this.tremolov;
public final ModGen zeromod = () -> 0;
private final int[] mixbuff = new int[2];
private final int[] oldsamples = new int[2];
private final int[] samples = new int[2];
@ -662,11 +694,12 @@ public class OPLChip {
private final byte nts;
private final byte vibshift;
private final byte tremoloshift;
private final byte velo_func;
private short timer;
private long eg_timer;
private byte eg_timerrem;
private byte eg_state;
private boolean eg_state;
private byte eg_add;
private byte vibpos;
private short tremolov;
@ -829,17 +862,19 @@ public class OPLChip {
this.samples[0] = this.mixbuff[0];
this.samples[1] = this.mixbuff[1];
for (int ii = 0; ii < this.slot.length; ii++)
{
this.slot[ii].process();
}
mix[0] = mix[1] = 0;
for (int ii = 0; ii < this.channel.length; ii++)
{
OPLChannel channel = this.channel[ii];
for (OPLSlot slot : channel.slots)
{
slot.process();
}
ModGen[] out = channel.out;
int accm = out[0].getModulation() + out[1].getModulation() + out[2].getModulation() + out[3].getModulation();
int accm = 0;
for(int z = 0; z < out.length; z++) {
accm += out[z].getModulation();
}
mix[0] += (accm * channel.level[0]) >> 16;
mix[1] += (accm * channel.level[1]) >> 16;
}
@ -884,7 +919,7 @@ public class OPLChip {
}
}
if (this.eg_timerrem != 0 || this.eg_state != 0)
if (this.eg_timerrem != 0 || this.eg_state)
{
if (this.eg_timer == 0xfffffffffL)
{
@ -898,7 +933,7 @@ public class OPLChip {
}
}
this.eg_state ^= 1;
this.eg_state ^= true;
}
public int[] generate()
@ -918,54 +953,25 @@ public class OPLChip {
return this.output;
}
public OPLChip(int samplerate, int voices, int nts, boolean deeptremolo, boolean deepvibrato) {
public OPLChip(int samplerate, int voices, int velo_func, int nts, boolean deeptremolo, boolean deepvibrato) {
this.channel = new OPLChannel[voices];
this.slot = new OPLSlot[voices * 2];
this.rateratio = (samplerate << RSM_FRAC) / 49716;
this.velo_func = (byte)velo_func;
this.nts = (byte)nts;
this.tremoloshift = (byte)(deeptremolo ? 2 : 4);
this.vibshift = (byte)(deepvibrato ? 0 : 1);
for (int slotnum = 0; slotnum < this.slot.length; slotnum++)
{
OPLSlot slot = this.slot[slotnum] = new OPLSlot();
slot.mod = this.zeromod;
slot.eg_rout = 0x1ff;
slot.eg_out = 0x1ff;
slot.eg_gen = EnvState.RELEASE;
slot.trem = this.zeromod;
slot.slot_num = slotnum;
}
for (int channum = 0; channum < this.channel.length; channum++)
{
OPLChannel channel = this.channel[channum] = new OPLChannel();
channel.slots[0] = this.slot[channum * 2];
channel.slots[1] = this.slot[channum * 2 + 1];
this.slot[channum * 2].channel = channel;
this.slot[channum * 2 + 1].channel = channel;
channel.out[0] = this.zeromod;
channel.out[1] = this.zeromod;
channel.out[2] = this.zeromod;
channel.out[3] = this.zeromod;
channel.chtype = ChType.CH2OP;
channel.level[0] = 0x10000;
channel.level[1] = 0x10000;
channel.ch_num = channum;
}
for (int channum = 0; channum < this.channel.length; channum++)
{
this.channel[channum].pair = this.channel[(channum & 1) != 0 ? (channum - 1) : (channum + 1)];
}
for (int channum = 0; channum < this.channel.length; channum++)
{
this.channel[channum].setupAlgo();
for(int z = 0; z < this.channel.length; z++) {
this.channel[z] = new OPLChannel();
}
}
public boolean isPlaying() {
for(OPLSlot slot : this.slot) {
for(OPLChannel chn : this.channel) {
for(OPLSlot slot : chn.slots) {
if(slot.eg_out <= 0x100)
return true;
}
}
return false;
}