From c0919b0eeda4299b2b67fa95d83f796ee77d8b6a Mon Sep 17 00:00:00 2001 From: Sen Date: Mon, 25 Aug 2025 13:17:58 +0200 Subject: [PATCH] opl arb update --- .../java/client/audio/AudioInterface.java | 502 +++++--------- .../src/main/java/client/audio/OPLChip.java | 624 +++++++++--------- 2 files changed, 479 insertions(+), 647 deletions(-) diff --git a/client/src/main/java/client/audio/AudioInterface.java b/client/src/main/java/client/audio/AudioInterface.java index b496c218..77807fbc 100644 --- a/client/src/main/java/client/audio/AudioInterface.java +++ b/client/src/main/java/client/audio/AudioInterface.java @@ -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); - } - - 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.channels.length; z++) { + this.channels[z] = new BankChannel(z == 9); } - 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 z = 0; z < this.voices.length; z++) { + this.voices[z] = new BankVoice(chip.channel[z]); } - 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 { - this.used -= 1; - } + 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); - } - } - } - - 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(); - this.used += 1; + slot.setOffset(op.offset); + slot.setDetune(op.detune); + slot.setFixed(instr.fixed ? instr.percnum : -1); + slot.setNote(note); + slot.setPitch(ch.pitch); } + 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 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}; - this.detune = detune; + public BankOperator offset(int offset) { this.offset = offset; - this.feedback = feedback; - this.am = am; + return this; + } + + public BankOperator detune(int detune) { + this.detune = detune; + 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() { diff --git a/client/src/main/java/client/audio/OPLChip.java b/client/src/main/java/client/audio/OPLChip.java index 93e08d04..02d05c1f 100644 --- a/client/src/main/java/client/audio/OPLChip.java +++ b/client/src/main/java/client/audio/OPLChip.java @@ -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; - } - }; - 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; + 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; + } 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,205 +339,212 @@ 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; + public OPLChannel() { + for(int z = 0; z < this.slots.length; z++) { + this.slots[z] = new OPLSlot(this); + } + this.setAlgo(0, 0, 0); + this.setLevel(0, 0, 0); + } - private void setupAlgo() - { - if ((this.alg & 0x08) != 0) + public void setAlgo(int algo, int feedback1, int feedback2) { + if ((algo & 0x04) != 0) { - return; - } - if ((this.alg & 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[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.pair.slots[0].mod = this.pair.slots[0].fbmod; - this.pair.slots[1].mod = this.pair.slots[0]; - this.slots[0].mod = OPLChip.this.zeromod; - this.slots[1].mod = this.slots[0]; - this.out[0] = this.pair.slots[1]; - 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[1].mod = this.slots[0]; - this.out[0] = this.pair.slots[0]; - 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[1].mod = OPLChip.this.zeromod; - this.out[0] = this.pair.slots[0]; - this.out[1] = this.slots[0]; - this.out[2] = this.slots[1]; - this.out[3] = OPLChip.this.zeromod; - break; + case 0x00: + 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; + this.out[2] = OPLChip.this.zeromod; + this.out[3] = OPLChip.this.zeromod; + break; + case 0x01: + 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.slots[3]; + this.out[1] = this.slots[1]; + this.out[2] = OPLChip.this.zeromod; + this.out[3] = OPLChip.this.zeromod; + break; + case 0x02: + 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.slots[2]; + this.out[1] = this.slots[1]; + this.out[2] = OPLChip.this.zeromod; + this.out[3] = OPLChip.this.zeromod; + break; + case 0x03: + 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.slots[2]; + this.out[1] = this.slots[0]; + this.out[2] = this.slots[1]; + this.out[3] = OPLChip.this.zeromod; + break; } } 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; + case 0x00: + this.slots[1].mod = this.slots[0]; + this.out[0] = this.slots[1]; + this.out[1] = OPLChip.this.zeromod; + break; + case 0x01: + this.slots[1].mod = OPLChip.this.zeromod; + this.out[0] = this.slots[0]; + this.out[1] = this.slots[1]; + 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; - 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; - this.out[3] = OPLChip.this.zeromod; - break; } } } - 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(); - } - else if (this.chtype == ChType.CH4OP2) - { - this.alg = (byte)(0x04 | (this.pair.con << 1) | (this.con)); - this.pair.alg = 0x08; - this.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))); + } + + 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,53 +953,24 @@ 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) { - if(slot.eg_out <= 0x100) - return true; + for(OPLChannel chn : this.channel) { + for(OPLSlot slot : chn.slots) { + if(slot.eg_out <= 0x100) + return true; + } } return false; }