From 5840b1fa803b1a73b3365223322dbc062a98bdfe Mon Sep 17 00:00:00 2001 From: Alex Iadicicco Date: Mon, 5 Dec 2022 23:42:00 -0800 Subject: [PATCH] Almost fully figured out the synth format decoder --- src/main/java/funorb/audio/FmtSynth.java | 14 +- src/main/java/funorb/audio/Oscillator.java | 317 ----------------- .../java/funorb/audio/OscillatorState.java | 65 ---- src/main/java/funorb/audio/Synth.java | 324 ++++++++++++++++++ src/main/java/funorb/audio/SynthEnvelope.java | 66 ++++ .../audio/{fh_.java => SynthMystery.java} | 59 ++-- 6 files changed, 428 insertions(+), 417 deletions(-) delete mode 100644 src/main/java/funorb/audio/Oscillator.java delete mode 100644 src/main/java/funorb/audio/OscillatorState.java create mode 100644 src/main/java/funorb/audio/Synth.java create mode 100644 src/main/java/funorb/audio/SynthEnvelope.java rename src/main/java/funorb/audio/{fh_.java => SynthMystery.java} (73%) diff --git a/src/main/java/funorb/audio/FmtSynth.java b/src/main/java/funorb/audio/FmtSynth.java index 5eea1fa..a34de4f 100644 --- a/src/main/java/funorb/audio/FmtSynth.java +++ b/src/main/java/funorb/audio/FmtSynth.java @@ -4,7 +4,7 @@ import funorb.cache.ResourceLoader; import funorb.io.Buffer; public final class FmtSynth { - private final Oscillator[] oscs = new Oscillator[10]; + private final Synth[] oscs = new Synth[10]; private final int loopStartMs; private final int loopEndMs; @@ -13,7 +13,7 @@ public final class FmtSynth { final int peekByte = data.readUByte(); if (peekByte != 0) { --data.pos; - this.oscs[i] = new Oscillator(); + this.oscs[i] = new Synth(); this.oscs[i].initialize(data); } } @@ -31,8 +31,8 @@ public final class FmtSynth { int totalDurMs = 0; for (int i = 0; i < 10; ++i) { - if (this.oscs[i] != null && totalDurMs < this.oscs[i].durMs + this.oscs[i].delayMs) { - totalDurMs = this.oscs[i].durMs + this.oscs[i].delayMs; + if (this.oscs[i] != null && totalDurMs < this.oscs[i].lengthMs + this.oscs[i].posMs) { + totalDurMs = this.oscs[i].lengthMs + this.oscs[i].posMs; } } @@ -44,9 +44,9 @@ public final class FmtSynth { for (int i = 0; i < 10; ++i) { if (this.oscs[i] != null) { - final int durSamples = this.oscs[i].durMs * SampledAudioChannel.SAMPLES_PER_SECOND / 1000; - final int delaySamples = this.oscs[i].delayMs * SampledAudioChannel.SAMPLES_PER_SECOND / 1000; - final int[] s16buf = this.oscs[i].generateS16(durSamples, this.oscs[i].durMs); + final int durSamples = this.oscs[i].lengthMs * SampledAudioChannel.SAMPLES_PER_SECOND / 1000; + final int delaySamples = this.oscs[i].posMs * SampledAudioChannel.SAMPLES_PER_SECOND / 1000; + final int[] s16buf = this.oscs[i].generateS16(durSamples, this.oscs[i].lengthMs); for (int j = 0; j < durSamples; ++j) { int sample = dataS8[j + delaySamples] + (s16buf[j] >> 8); diff --git a/src/main/java/funorb/audio/Oscillator.java b/src/main/java/funorb/audio/Oscillator.java deleted file mode 100644 index ef159ca..0000000 --- a/src/main/java/funorb/audio/Oscillator.java +++ /dev/null @@ -1,317 +0,0 @@ -package funorb.audio; - -import funorb.io.Buffer; - -import java.util.Arrays; -import java.util.Random; - -public final class Oscillator { - private static final int[] NOISE = new int[0x8000]; - private static final int[] SINE = new int[0x8000]; - private static final int[] buf; - private static final int[] _p; - private static final int[] _q; - private static final int[] _t; - private static final int[] _c; - private static final int[] _w; - - static { - final Random var0 = new Random(0L); - for (int i = 0; i < 0x8000; ++i) { - NOISE[i] = (var0.nextInt() & 2) - 1; - } - for (int i = 0; i < 0x8000; ++i) { - SINE[i] = (int) (Math.sin((double) i * Math.PI / 0x4000) * 0x4000); - } - - buf = new int[220500]; - _q = new int[5]; - _t = new int[5]; - _p = new int[5]; - _w = new int[5]; - _c = new int[5]; - } - - private final int[] _y = new int[]{0, 0, 0, 0, 0}; - private final int[] _x = new int[]{0, 0, 0, 0, 0}; - private final int[] _h = new int[]{0, 0, 0, 0, 0}; - public int delayMs = 0; - public int durMs = 500; - private fh_ _k; - private OscillatorState osc5_; - private int _f = 0; - private OscillatorState osc7_; - private OscillatorState osc4_; - private OscillatorState osc3_; - private OscillatorState osc0_; - private int _b = 100; - private OscillatorState osc8_; - private OscillatorState osc6_; - private OscillatorState osc2_; - private OscillatorState osc1_; - - public int[] generateS16(final int len, final int lenMs) { - Arrays.fill(buf, 0, len, 0); - - if (lenMs < 10) { - return buf; - } - - final double samplesPerMs = (double) len / ((double) lenMs + 0.0D); - - this.osc0_.reset(); - this.osc1_.reset(); - int var5 = 0; - int var6 = 0; - int var7 = 0; - if (this.osc2_ != null) { - this.osc2_.reset(); - this.osc3_.reset(); - var5 = (int) ((double) (this.osc2_._i - this.osc2_._d) * 32.768D / samplesPerMs); - var6 = (int) ((double) this.osc2_._d * 32.768D / samplesPerMs); - } - - int var8 = 0; - int var9 = 0; - int var10 = 0; - if (this.osc4_ != null) { - this.osc4_.reset(); - this.osc5_.reset(); - var8 = (int) ((double) (this.osc4_._i - this.osc4_._d) * 32.768D / samplesPerMs); - var9 = (int) ((double) this.osc4_._d * 32.768D / samplesPerMs); - } - - for (int i = 0; i < 5; ++i) { - if (this._y[i] != 0) { - _c[i] = 0; - _q[i] = (int) ((double) this._h[i] * samplesPerMs); - _p[i] = (this._y[i] << 14) / 100; - _t[i] = (int) ((double) (this.osc0_._i - this.osc0_._d) * 32.768D * Math.pow(1.0057929410678534D, this._x[i]) / samplesPerMs); - _w[i] = (int) ((double) this.osc0_._d * 32.768D / samplesPerMs); - } - } - - for (int i = 0; i < len; ++i) { - int var12 = this.osc0_.next(len); - int var13 = this.osc1_.next(len); - int var14; - int var15; - if (this.osc2_ != null) { - var14 = this.osc2_.next(len); - var15 = this.osc3_.next(len); - var12 += this.sample(this.osc2_.waveform, var7, var15) >> 1; - var7 += (var14 * var5 >> 16) + var6; - } - - if (this.osc4_ != null) { - var14 = this.osc4_.next(len); - var15 = this.osc5_.next(len); - var13 = var13 * ((this.sample(this.osc4_.waveform, var10, var15) >> 1) + 0x8000) >> 15; - var10 += (var14 * var8 >> 16) + var9; - } - - for (var14 = 0; var14 < 5; ++var14) { - if (this._y[var14] != 0) { - var15 = i + _q[var14]; - if (var15 < len) { - buf[var15] += this.sample(this.osc0_.waveform, _c[var14], var13 * _p[var14] >> 15); - _c[var14] += (var12 * _t[var14] >> 16) + _w[var14]; - } - } - } - } - - if (this.osc6_ != null) { - this.osc6_.reset(); - this.osc7_.reset(); - int var11 = 0; - boolean var19 = true; - - for (int i = 0; i < len; ++i) { - final int var15 = this.osc6_.next(len); - final int var16 = this.osc7_.next(len); - final int var12; - if (var19) { - var12 = this.osc6_._d + ((this.osc6_._i - this.osc6_._d) * var15 >> 8); - } else { - var12 = this.osc6_._d + ((this.osc6_._i - this.osc6_._d) * var16 >> 8); - } - - var11 += 256; - if (var11 >= var12) { - var11 = 0; - var19 = !var19; - } - - if (var19) { - buf[i] = 0; - } - } - } - - if (this._f > 0 && this._b > 0) { - final int var11 = (int) ((double) this._f * samplesPerMs); - for (int i = var11; i < len; ++i) { - buf[i] += buf[i - var11] * this._b / 100; - } - } - - if (this._k._d[0] > 0 || this._k._d[1] > 0) { - this.osc8_.reset(); - int var11 = this.osc8_.next(len + 1); - int var12 = this._k.a197(0, (float) var11 / 65536.0F); - int var13 = this._k.a197(1, (float) var11 / 65536.0F); - if (len >= var12 + var13) { - int var14 = 0; - final int var15 = Math.min(var13, len - var12); - - while (var14 < var15) { - int var16 = (int) ((long) buf[var14 + var12] * (long) fh_._g >> 16); - - for (int var17 = 0; var17 < var12; ++var17) { - var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) fh_._e[0][var17] >> 16); - } - - for (int var17 = 0; var17 < var14; ++var17) { - var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) fh_._e[1][var17] >> 16); - } - - buf[var14] = var16; - var11 = this.osc8_.next(len + 1); - ++var14; - } - - int var15a = 128; - while (true) { - if (var15a > len - var12) { - var15a = len - var12; - } - - while (var14 < var15a) { - int var16 = (int) ((long) buf[var14 + var12] * (long) fh_._g >> 16); - - for (int var17 = 0; var17 < var12; ++var17) { - var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) fh_._e[0][var17] >> 16); - } - - for (int var17 = 0; var17 < var13; ++var17) { - var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) fh_._e[1][var17] >> 16); - } - - buf[var14] = var16; - var11 = this.osc8_.next(len + 1); - ++var14; - } - - if (var14 >= len - var12) { - while (var14 < len) { - int var16 = 0; - - for (int var17 = var14 + var12 - len; var17 < var12; ++var17) { - var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) fh_._e[0][var17] >> 16); - } - - for (int var17 = 0; var17 < var13; ++var17) { - var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) fh_._e[1][var17] >> 16); - } - - buf[var14] = var16; - this.osc8_.next(len + 1); - ++var14; - } - break; - } - - var12 = this._k.a197(0, (float) var11 / 65536.0F); - var13 = this._k.a197(1, (float) var11 / 65536.0F); - var15a += 128; - } - } - } - - for (int i = 0; i < len; ++i) { - if (buf[i] < -32768) { - buf[i] = -32768; - } - if (buf[i] > 32767) { - buf[i] = 32767; - } - } - return buf; - } - - public void initialize(final Buffer buf) { - this.osc0_ = new OscillatorState(); - this.osc0_.initialize(buf); - this.osc1_ = new OscillatorState(); - this.osc1_.initialize(buf); - - final int peek1 = buf.readUByte(); - if (peek1 != 0) { - --buf.pos; - this.osc2_ = new OscillatorState(); - this.osc2_.initialize(buf); - this.osc3_ = new OscillatorState(); - this.osc3_.initialize(buf); - } - - final int peek2 = buf.readUByte(); - if (peek2 != 0) { - --buf.pos; - this.osc4_ = new OscillatorState(); - this.osc4_.initialize(buf); - this.osc5_ = new OscillatorState(); - this.osc5_.initialize(buf); - } - - final int peek3 = buf.readUByte(); - if (peek3 != 0) { - --buf.pos; - this.osc6_ = new OscillatorState(); - this.osc6_.initialize(buf); - this.osc7_ = new OscillatorState(); - this.osc7_.initialize(buf); - } - - for (int i = 0; i < 10; ++i) { - final int peek4 = buf.readVariable8_16(); - if (peek4 == 0) { - break; - } - - this._y[i] = peek4; - this._x[i] = buf.readBiasedVariable8_16(); - this._h[i] = buf.readVariable8_16(); - } - - this._f = buf.readVariable8_16(); - this._b = buf.readVariable8_16(); - this.durMs = buf.readUShort(); - this.delayMs = buf.readUShort(); - this._k = new fh_(); - this.osc8_ = new OscillatorState(); - this._k.a086(buf, this.osc8_); - } - - private int sample(final int type, final int phase, final int amplitude) { - if (type == Waveform.SQUARE) { - return ((phase & 0x7fff) < 0x4000) ? amplitude : -amplitude; - } else if (type == Waveform.SINE) { - return (SINE[phase & 0x7fff] * amplitude) >> 14; - } else if (type == Waveform.SAWTOOTH) { - return (((phase & 0x7fff) * amplitude) >> 14) - amplitude; - } else if (type == Waveform.NOISE) { - return NOISE[(phase / 2607) & 0x7fff] * amplitude; - } else { - return 0; - } - } - - @SuppressWarnings("WeakerAccess") - private static final class Waveform { - public static final int SQUARE = 1; - public static final int SINE = 2; - public static final int SAWTOOTH = 3; - public static final int NOISE = 4; - } -} diff --git a/src/main/java/funorb/audio/OscillatorState.java b/src/main/java/funorb/audio/OscillatorState.java deleted file mode 100644 index 1c7087e..0000000 --- a/src/main/java/funorb/audio/OscillatorState.java +++ /dev/null @@ -1,65 +0,0 @@ -package funorb.audio; - -import funorb.io.Buffer; - -public final class OscillatorState { - public int _d; - public int _i; - public int waveform; - private int count = 2; - private int[] values1 = new int[2]; - private int[] values2 = new int[2]; - private int _g; - private int _e; - private int _b; - private int _j; - private int pos; - - public OscillatorState() { - this.values1[1] = 65535; - this.values2[1] = 65535; - } - - public void initialize(final Buffer buffer) { - this.waveform = buffer.readUByte(); - this._d = buffer.readInt(); - this._i = buffer.readInt(); - this.read(buffer); - } - - public void read(final Buffer buffer) { - this.count = buffer.readUByte(); - this.values1 = new int[this.count]; - this.values2 = new int[this.count]; - for (int i = 0; i < this.count; ++i) { - this.values1[i] = buffer.readUShort(); - this.values2[i] = buffer.readUShort(); - } - } - - public void reset() { - this._e = 0; - this.pos = 0; - this._b = 0; - this._j = 0; - this._g = 0; - } - - public int next(final int len) { - if (this._g >= this._e) { - this._j = this.values2[this.pos++] << 15; - if (this.pos >= this.count) { - this.pos = this.count - 1; - } - - this._e = (int) (((double) this.values1[this.pos] / 65536.0D) * (double) len); - if (this._e > this._g) { - this._b = ((this.values2[this.pos] << 15) - this._j) / (this._e - this._g); - } - } - - this._j += this._b; - ++this._g; - return this._j - this._b >> 15; - } -} diff --git a/src/main/java/funorb/audio/Synth.java b/src/main/java/funorb/audio/Synth.java new file mode 100644 index 0000000..b86dd8d --- /dev/null +++ b/src/main/java/funorb/audio/Synth.java @@ -0,0 +1,324 @@ +package funorb.audio; + +import funorb.io.Buffer; + +import java.util.Arrays; +import java.util.Random; + +public final class Synth { + private static final double _120TH_ROOT_OF_2 = 1.0057929410678534D; + + private static final int[] NOISE = new int[0x8000]; + private static final int[] SINE = new int[0x8000]; + private static final int[] buf; + private static final int[] harmVolScaled_idk; + private static final int[] harmDelScaled_idk; + private static final int[] harmFreq_idk; + private static final int[] harmPhase_idk; + private static final int[] harmFreqMin_idk; + + static { + final Random var0 = new Random(0L); + for (int i = 0; i < 0x8000; ++i) { + NOISE[i] = (var0.nextInt() & 2) - 1; + } + for (int i = 0; i < 0x8000; ++i) { + SINE[i] = (int) (Math.sin((double) i * Math.PI / 0x4000) * 0x4000); + } + + buf = new int[220500]; + harmDelScaled_idk = new int[5]; + harmFreq_idk = new int[5]; + harmVolScaled_idk = new int[5]; + harmFreqMin_idk = new int[5]; + harmPhase_idk = new int[5]; + } + + private final int[] harmVol_idk = new int[]{0, 0, 0, 0, 0}; + private final int[] harmSemis_idk = new int[]{0, 0, 0, 0, 0}; + private final int[] harmDel_idk = new int[]{0, 0, 0, 0, 0}; + public int posMs = 0; + public int lengthMs = 500; + private SynthMystery _k; + private int echoTime = 0; + private SynthEnvelope envGapOn; + private SynthEnvelope envBaseFreq; + private SynthEnvelope envBaseAmp; + private SynthEnvelope envFmRate; + private SynthEnvelope envFmRange; + private SynthEnvelope envAmRate; + private SynthEnvelope envAmRange; + private int echoAmount = 100; + private SynthEnvelope osc8_; + private SynthEnvelope envGapOff; + + public int[] generateS16(final int len, final int lenMs) { + Arrays.fill(buf, 0, len, 0); + + if (lenMs < 10) { + return buf; + } + + final double samplesPerMs = (double) len / ((double) lenMs + 0.0D); + + this.envBaseFreq.reset(); + this.envBaseAmp.reset(); + int osc2RangeScaled = 0; + int osc2MinScaled = 0; + int oscFmPhase = 0; + if (this.envFmRate != null) { + this.envFmRate.reset(); + this.envFmRange.reset(); + osc2RangeScaled = (int) ((double) (this.envFmRate.max - this.envFmRate.min) * 32.768D / samplesPerMs); + osc2MinScaled = (int) ((double) this.envFmRate.min * 32.768D / samplesPerMs); + } + + int osc4RangeScaled = 0; + int osc4MinScaled = 0; + int oscAmpPhase = 0; + if (this.envAmRate != null) { + this.envAmRate.reset(); + this.envAmRange.reset(); + osc4RangeScaled = (int) ((double) (this.envAmRate.max - this.envAmRate.min) * 32.768D / samplesPerMs); + osc4MinScaled = (int) ((double) this.envAmRate.min * 32.768D / samplesPerMs); + } + + for (int harm_idk = 0; harm_idk < 5; ++harm_idk) { + if (this.harmVol_idk[harm_idk] != 0) { + harmPhase_idk[harm_idk] = 0; + harmDelScaled_idk[harm_idk] = (int) ((double) this.harmDel_idk[harm_idk] * samplesPerMs); + harmVolScaled_idk[harm_idk] = (this.harmVol_idk[harm_idk] << 14) / 100; + harmFreq_idk[harm_idk] = (int) ((double) (this.envBaseFreq.max - this.envBaseFreq.min) * 32.768D * + Math.pow(_120TH_ROOT_OF_2, this.harmSemis_idk[harm_idk]) / samplesPerMs); + harmFreqMin_idk[harm_idk] = (int) ((double) this.envBaseFreq.min * 32.768D / samplesPerMs); + } + } + + for (int i = 0; i < len; ++i) { + int baseFreq = this.envBaseFreq.next(len); + int baseAmp = this.envBaseAmp.next(len); + int rate; + int range; + if (this.envFmRate != null) { + rate = this.envFmRate.next(len); + range = this.envFmRange.next(len); + baseFreq += this.sample(this.envFmRate.waveform, oscFmPhase, range) >> 1; + oscFmPhase += (rate * osc2RangeScaled >> 16) + osc2MinScaled; + } + + if (this.envAmRate != null) { + rate = this.envAmRate.next(len); + range = this.envAmRange.next(len); + baseAmp = baseAmp * ((this.sample(this.envAmRate.waveform, oscAmpPhase, range) >> 1) + 0x8000) >> 15; + oscAmpPhase += (rate * osc4RangeScaled >> 16) + osc4MinScaled; + } + + for (int harm_idk = 0; harm_idk < 5; ++harm_idk) { + if (this.harmVol_idk[harm_idk] != 0) { + int index = harm_idk + harmDelScaled_idk[harm_idk]; + if (index < len) { + buf[index] += this.sample( + this.envBaseFreq.waveform, + harmPhase_idk[harm_idk], + baseAmp * harmVolScaled_idk[harm_idk] >> 15 + ); + harmPhase_idk[harm_idk] += (baseFreq * harmFreq_idk[harm_idk] >> 16) + harmFreqMin_idk[harm_idk]; + } + } + } + } + + if (this.envGapOff != null) { + this.envGapOff.reset(); + this.envGapOn.reset(); + int gapAccum = 0; + boolean gapOn = true; + + for (int i = 0; i < len; ++i) { + final int gapOffThresh = this.envGapOff.next(len); + final int gapOnThresh = this.envGapOn.next(len); + final int gapThresh; + if (gapOn) { + gapThresh = this.envGapOff.min + ((this.envGapOff.max - this.envGapOff.min) * gapOffThresh >> 8); + } else { + gapThresh = this.envGapOff.min + ((this.envGapOff.max - this.envGapOff.min) * gapOnThresh >> 8); + } + + gapAccum += 256; + if (gapAccum >= gapThresh) { + gapAccum = 0; + gapOn = !gapOn; + } + + if (gapOn) { + buf[i] = 0; + } + } + } + + if (this.echoTime > 0 && this.echoAmount > 0) { + final int delay = (int) ((double) this.echoTime * samplesPerMs); + for (int i = delay; i < len; ++i) { + buf[i] += buf[i - delay] * this.echoAmount / 100; + } + } + + if (this._k._d[0] > 0 || this._k._d[1] > 0) { + this.osc8_.reset(); + int var11 = this.osc8_.next(len + 1); + int var12 = this._k.a197(0, (float) var11 / 65536.0F); + int var13 = this._k.a197(1, (float) var11 / 65536.0F); + if (len >= var12 + var13) { + int var14 = 0; + final int var15 = Math.min(var13, len - var12); + + while (var14 < var15) { + int var16 = (int) ((long) buf[var14 + var12] * (long) SynthMystery._g >> 16); + + for (int var17 = 0; var17 < var12; ++var17) { + var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) SynthMystery._e[0][var17] >> 16); + } + + for (int var17 = 0; var17 < var14; ++var17) { + var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) SynthMystery._e[1][var17] >> 16); + } + + buf[var14] = var16; + var11 = this.osc8_.next(len + 1); + ++var14; + } + + int var15a = 128; + while (true) { + if (var15a > len - var12) { + var15a = len - var12; + } + + while (var14 < var15a) { + int var16 = (int) ((long) buf[var14 + var12] * (long) SynthMystery._g >> 16); + + for (int var17 = 0; var17 < var12; ++var17) { + var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) SynthMystery._e[0][var17] >> 16); + } + + for (int var17 = 0; var17 < var13; ++var17) { + var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) SynthMystery._e[1][var17] >> 16); + } + + buf[var14] = var16; + var11 = this.osc8_.next(len + 1); + ++var14; + } + + if (var14 >= len - var12) { + while (var14 < len) { + int var16 = 0; + + for (int var17 = var14 + var12 - len; var17 < var12; ++var17) { + var16 += (int) ((long) buf[var14 + var12 - 1 - var17] * (long) SynthMystery._e[0][var17] >> 16); + } + + for (int var17 = 0; var17 < var13; ++var17) { + var16 -= (int) ((long) buf[var14 - 1 - var17] * (long) SynthMystery._e[1][var17] >> 16); + } + + buf[var14] = var16; + this.osc8_.next(len + 1); + ++var14; + } + break; + } + + var12 = this._k.a197(0, (float) var11 / 65536.0F); + var13 = this._k.a197(1, (float) var11 / 65536.0F); + var15a += 128; + } + } + } + + for (int i = 0; i < len; ++i) { + if (buf[i] < -32768) { + buf[i] = -32768; + } + if (buf[i] > 32767) { + buf[i] = 32767; + } + } + return buf; + } + + public void initialize(final Buffer buf) { + this.envBaseFreq = new SynthEnvelope(); + this.envBaseFreq.load(buf); + this.envBaseAmp = new SynthEnvelope(); + this.envBaseAmp.load(buf); + + final int peek1 = buf.readUByte(); + if (peek1 != 0) { + --buf.pos; + this.envFmRate = new SynthEnvelope(); + this.envFmRate.load(buf); + this.envFmRange = new SynthEnvelope(); + this.envFmRange.load(buf); + } + + final int peek2 = buf.readUByte(); + if (peek2 != 0) { + --buf.pos; + this.envAmRate = new SynthEnvelope(); + this.envAmRate.load(buf); + this.envAmRange = new SynthEnvelope(); + this.envAmRange.load(buf); + } + + final int peek3 = buf.readUByte(); + if (peek3 != 0) { + --buf.pos; + this.envGapOff = new SynthEnvelope(); + this.envGapOff.load(buf); + this.envGapOn = new SynthEnvelope(); + this.envGapOn.load(buf); + } + + for (int i = 0; i < 10; ++i) { + final int peek4 = buf.readVariable8_16(); + if (peek4 == 0) { + break; + } + + this.harmVol_idk[i] = peek4; + this.harmSemis_idk[i] = buf.readBiasedVariable8_16(); + this.harmDel_idk[i] = buf.readVariable8_16(); + } + + this.echoTime = buf.readVariable8_16(); + this.echoAmount = buf.readVariable8_16(); + this.lengthMs = buf.readUShort(); + this.posMs = buf.readUShort(); + this._k = new SynthMystery(); + this.osc8_ = new SynthEnvelope(); + this._k.load(buf, this.osc8_); + } + + private int sample(final int type, final int phase, final int amplitude) { + if (type == Waveform.SQUARE) { + return ((phase & 0x7fff) < 0x4000) ? amplitude : -amplitude; + } else if (type == Waveform.SINE) { + return (SINE[phase & 0x7fff] * amplitude) >> 14; + } else if (type == Waveform.SAWTOOTH) { + return (((phase & 0x7fff) * amplitude) >> 14) - amplitude; + } else if (type == Waveform.NOISE) { + return NOISE[(phase / 2607) & 0x7fff] * amplitude; + } else { + return 0; + } + } + + @SuppressWarnings("WeakerAccess") + private static final class Waveform { + public static final int SQUARE = 1; + public static final int SINE = 2; + public static final int SAWTOOTH = 3; + public static final int NOISE = 4; + } +} diff --git a/src/main/java/funorb/audio/SynthEnvelope.java b/src/main/java/funorb/audio/SynthEnvelope.java new file mode 100644 index 0000000..0068c9a --- /dev/null +++ b/src/main/java/funorb/audio/SynthEnvelope.java @@ -0,0 +1,66 @@ +package funorb.audio; + +import funorb.io.Buffer; + +public final class SynthEnvelope { + public int min; + public int max; + public int waveform; + private int numSegs; + private int[] envPtsX; + private int[] envPtsY; + private int curSamples; + private int envSegEnd; + private int curSlope; + private int curY; + private int curSegIdx; + + public SynthEnvelope() { + this.numSegs = 2; + this.envPtsX = new int[]{0, 65535}; + this.envPtsY = new int[]{0, 65535}; + } + + public void load(final Buffer buffer) { + this.waveform = buffer.readUByte(); + this.min = buffer.readInt(); + this.max = buffer.readInt(); + this.loadPoints(buffer); + } + + public void loadPoints(final Buffer buffer) { + this.numSegs = buffer.readUByte(); + this.envPtsX = new int[this.numSegs]; + this.envPtsY = new int[this.numSegs]; + for (int i = 0; i < this.numSegs; ++i) { + this.envPtsX[i] = buffer.readUShort(); + this.envPtsY[i] = buffer.readUShort(); + } + } + + public void reset() { + this.envSegEnd = 0; + this.curSegIdx = 0; + this.curSlope = 0; + this.curY = 0; + this.curSamples = 0; + } + + public int next(final int len) { + if (this.curSamples >= this.envSegEnd) { + this.curY = this.envPtsY[this.curSegIdx++] << 15; + if (this.curSegIdx >= this.numSegs) { + this.curSegIdx = this.numSegs - 1; + } + + this.envSegEnd = (int) (((double) this.envPtsX[this.curSegIdx] / 65536.0D) * (double) len); + if (this.envSegEnd > this.curSamples) { + this.curSlope = ((this.envPtsY[this.curSegIdx] << 15) - this.curY) / (this.envSegEnd - this.curSamples); + } + } + + this.curY += this.curSlope; + ++this.curSamples; + return this.curY - this.curSlope >> 15; + } +} diff --git a/src/main/java/funorb/audio/fh_.java b/src/main/java/funorb/audio/SynthMystery.java similarity index 73% rename from src/main/java/funorb/audio/fh_.java rename to src/main/java/funorb/audio/SynthMystery.java index 9744b8a..1b1d85c 100644 --- a/src/main/java/funorb/audio/fh_.java +++ b/src/main/java/funorb/audio/SynthMystery.java @@ -2,7 +2,7 @@ package funorb.audio; import funorb.io.Buffer; -public final class fh_ { +public final class SynthMystery { public static final int[][] _e = new int[2][8]; private static final float[][] _f = new float[2][8]; public static int _g; @@ -17,42 +17,45 @@ public final class fh_ { return var1 * 3.1415927F / 11025.0F; } - public void a086(final Buffer var1, final OscillatorState var2) { - final int var3 = var1.readUByte(); + public void load(final Buffer buf, final SynthEnvelope env) { + final int var3 = buf.readUByte(); + this._d[0] = var3 >> 4; this._d[1] = var3 & 15; + if (var3 == 0) { this._b[1] = 0; this._b[0] = 0; - } else { - this._b[0] = var1.readUShort(); - this._b[1] = var1.readUShort(); - final int var4 = var1.readUByte(); + return; + } - int var5; - int var6; - for (var5 = 0; var5 < 2; ++var5) { - for (var6 = 0; var6 < this._d[var5]; ++var6) { - this._a[var5][0][var6] = var1.readUShort(); - this._c[var5][0][var6] = var1.readUShort(); + this._b[0] = buf.readUShort(); + this._b[1] = buf.readUShort(); + final int var4 = buf.readUByte(); + + int var5; + int var6; + for (var5 = 0; var5 < 2; ++var5) { + for (var6 = 0; var6 < this._d[var5]; ++var6) { + this._a[var5][0][var6] = buf.readUShort(); + this._c[var5][0][var6] = buf.readUShort(); + } + } + + for (var5 = 0; var5 < 2; ++var5) { + for (var6 = 0; var6 < this._d[var5]; ++var6) { + if ((var4 & 1 << var5 * 4 << var6) == 0) { + this._a[var5][1][var6] = this._a[var5][0][var6]; + this._c[var5][1][var6] = this._c[var5][0][var6]; + } else { + this._a[var5][1][var6] = buf.readUShort(); + this._c[var5][1][var6] = buf.readUShort(); } } + } - for (var5 = 0; var5 < 2; ++var5) { - for (var6 = 0; var6 < this._d[var5]; ++var6) { - if ((var4 & 1 << var5 * 4 << var6) == 0) { - this._a[var5][1][var6] = this._a[var5][0][var6]; - this._c[var5][1][var6] = this._c[var5][0][var6]; - } else { - this._a[var5][1][var6] = var1.readUShort(); - this._c[var5][1][var6] = var1.readUShort(); - } - } - } - - if (var4 != 0 || this._b[1] != this._b[0]) { - var2.read(var1); - } + if (var4 != 0 || this._b[1] != this._b[0]) { + env.loadPoints(buf); } }