summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md12
-rw-r--r--emu8910.js574
-rw-r--r--fym.js66
-rw-r--r--index.html7
-rw-r--r--music/01_scalesmannmisfire.fymbin0 -> 2092 bytes
-rw-r--r--pako_inflate.min.js1
-rw-r--r--parser.js79
-rw-r--r--src/emu8910.ts (renamed from emu8910.ts)379
8 files changed, 855 insertions, 263 deletions
diff --git a/README.md b/README.md
index adee73c..ff89f40 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,16 @@ It implements most of the PSG's original registers. A datasheet can be found [he
Sound output is achieved in the browser through an AudioContext() hook. <br>
This emulator also adds interrupt support (with variable frequency) for updating the PSG's registers.
-This repository currently lacks a working example of the emulator which I plan to add in the future.
+FIR filter data generated using:
+[https://www.arc.id.au/FilterDesign.html](https://www.arc.id.au/FilterDesign.html)
Files:
-* emu8910.ts - Core emulator implementation
+* src/emu8910.ts - Core emulator implementation
+* fym.js - FYM (Fast YM) format parser
+* update.js - Register parser
+* index.html - HTML boilerplate
+
+To run demo start web server: `python -m http.server 8000` and navigate to `index.html`.
+Then click anywhere on the page to start audio output.
+
diff --git a/emu8910.js b/emu8910.js
new file mode 100644
index 0000000..d05fb78
--- /dev/null
+++ b/emu8910.js
@@ -0,0 +1,574 @@
+// Copyright (C)
+// Author: Dylan Muller
+var YM_CLOCK_ZX = 1750000;
+var FIR = [-0.011368,
+ 0.004512,
+ 0.008657,
+ -0.011763,
+ -0.000000,
+ 0.012786,
+ -0.010231,
+ -0.005801,
+ 0.015915,
+ -0.006411,
+ -0.012504,
+ 0.017299,
+ -0.000000,
+ -0.019605,
+ 0.016077,
+ 0.009370,
+ -0.026526,
+ 0.011074,
+ 0.022508,
+ -0.032676,
+ 0.000000,
+ 0.042011,
+ -0.037513,
+ -0.024362,
+ 0.079577,
+ -0.040604,
+ -0.112540,
+ 0.294080,
+ 0.625000,
+ 0.294080,
+ -0.112540,
+ -0.040604,
+ 0.079577,
+ -0.024362,
+ -0.037513,
+ 0.042011,
+ 0.000000,
+ -0.032676,
+ 0.022508,
+ 0.011074,
+ -0.026526,
+ 0.009370,
+ 0.016077,
+ -0.019605,
+ -0.000000,
+ 0.017299,
+ -0.012504,
+ -0.006411,
+ 0.015915,
+ -0.005801,
+ -0.010231,
+ 0.012786,
+ -0.000000,
+ -0.011763,
+ 0.008657,
+ 0.004512,
+ -0.011368];
+var Interpolator = /** @class */ (function () {
+ function Interpolator() {
+ this.buffer = [];
+ for (var i = 0; i < 4; i++) {
+ this.buffer[i] = 0x0;
+ }
+ }
+ Interpolator.prototype.step = function (x) {
+ var b = this.buffer;
+ b[0] = b[1];
+ b[1] = b[2];
+ b[2] = b[3];
+ b[3] = x;
+ };
+ Interpolator.prototype.cubic = function (mu) {
+ var b = this.buffer;
+ var a0, a1, a2, a3, mu2 = 0;
+ mu2 = mu * mu2;
+ a0 = b[3] - b[2] - b[0] + b[1];
+ a1 = b[0] - b[1] - a0;
+ a2 = b[2] - b[0];
+ a3 = b[1];
+ return (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3);
+ };
+ return Interpolator;
+}());
+// DC filter
+var BiasFilter = /** @class */ (function () {
+ function BiasFilter(length, attenuate) {
+ this.samples = [];
+ this.index = 0x0;
+ this.length = 0x0;
+ this.sum = 0x0;
+ this.attenuate = 0x0;
+ this.length = length;
+ this.sum = 0x0;
+ for (var i = 0; i < this.length; i++) {
+ this.samples[i] = 0x0;
+ }
+ this.attenuate = attenuate;
+ }
+ BiasFilter.prototype.step = function (x) {
+ var index = this.index;
+ var delta = x - this.samples[index];
+ var attenuate = this.attenuate;
+ var avg = 0x0;
+ this.sum += delta;
+ this.samples[index] = x;
+ if (++this.index > (this.length - 1)) {
+ this.index = 0x0;
+ }
+ avg = this.sum / this.length;
+ return (x - avg) * (1 / attenuate);
+ };
+ return BiasFilter;
+}());
+var FirFilter = /** @class */ (function () {
+ function FirFilter(h, m) {
+ this.buffer = [];
+ this.index = 0x0;
+ this.offset = 0x0;
+ this.length = 0x0;
+ this.m = 0x0;
+ this.h = [];
+ this.length = h.length * m;
+ this.index = 0;
+ this.m = m;
+ this.h = h;
+ var buffer = this.buffer;
+ for (var i = 0; i < this.length * 2; i++) {
+ buffer[i] = 0x0;
+ }
+ }
+ FirFilter.prototype.step = function (samples) {
+ var index = this.index;
+ var buffer = this.buffer;
+ var length = this.length;
+ var m = this.m;
+ var h = this.h;
+ var y = 0x0;
+ var i = 0x0;
+ this.offset = length - (index * m);
+ var sub = buffer.slice(this.offset);
+ this.index = (index + 1) % (length / m - 1);
+ for (i = m - 1; i >= 0; i--) {
+ buffer[this.offset + i] = samples[i];
+ }
+ for (i = 0; i < h.length; i++) {
+ y += h[i] * sub[i];
+ }
+ for (i = 0; i < m; i++) {
+ buffer[this.offset + length - m + i] = buffer[this.offset + i];
+ }
+ return y;
+ };
+ return FirFilter;
+}());
+var AudioDriver = /** @class */ (function () {
+ function AudioDriver(host) {
+ this.frequency = 0x0;
+ this.update = function (ev) {
+ var ch0 = ev.outputBuffer.getChannelData(0);
+ var ch1 = ev.outputBuffer.getChannelData(1);
+ var host = this.host;
+ var filter = this.filter;
+ var bias = this.bias;
+ var output = [0, 0];
+ var port = [0, 0];
+ for (var i = 0; i < ch0.length; i++) {
+ output = host.step();
+ port[0] = filter[0].step(output[0]);
+ port[1] = filter[1].step(output[1]);
+ ch0[i] = bias + port[0];
+ ch1[i] = bias + port[1];
+ }
+ }.bind(this);
+ this.device = new AudioContext();
+ var device = this.device;
+ this.filter = [
+ new BiasFilter(1024, 1.25),
+ new BiasFilter(1024, 1.25),
+ device.createBiquadFilter(),
+ device.createBiquadFilter()
+ ];
+ var filter = this.filter;
+ filter[2].type = "lowshelf";
+ filter[2].frequency.value = 10000;
+ filter[2].gain.value = 2;
+ filter[3].type = "lowpass";
+ filter[3].frequency.value = 10000;
+ filter[3].Q.value = 1;
+ this.frequency = device.sampleRate;
+ this.context = device.createScriptProcessor(4096, 0, 2);
+ this.context.onaudioprocess = this.update;
+ this.context.connect(filter[2]);
+ filter[2].connect(filter[3]);
+ filter[3].connect(device.destination);
+ this.host = host;
+ this.bias = 1 / 100;
+ }
+ return AudioDriver;
+}());
+var PSG49_LUT;
+(function (PSG49_LUT) {
+ PSG49_LUT[PSG49_LUT["A_FINE"] = 0] = "A_FINE";
+ PSG49_LUT[PSG49_LUT["A_COARSE"] = 1] = "A_COARSE";
+ PSG49_LUT[PSG49_LUT["B_FINE"] = 2] = "B_FINE";
+ PSG49_LUT[PSG49_LUT["B_COARSE"] = 3] = "B_COARSE";
+ PSG49_LUT[PSG49_LUT["C_FINE"] = 4] = "C_FINE";
+ PSG49_LUT[PSG49_LUT["C_COARSE"] = 5] = "C_COARSE";
+ PSG49_LUT[PSG49_LUT["NOISE_PERIOD"] = 6] = "NOISE_PERIOD";
+ PSG49_LUT[PSG49_LUT["MIXER"] = 7] = "MIXER";
+ PSG49_LUT[PSG49_LUT["A_VOL"] = 8] = "A_VOL";
+ PSG49_LUT[PSG49_LUT["B_VOL"] = 9] = "B_VOL";
+ PSG49_LUT[PSG49_LUT["C_VOL"] = 10] = "C_VOL";
+ PSG49_LUT[PSG49_LUT["ENV_FINE"] = 11] = "ENV_FINE";
+ PSG49_LUT[PSG49_LUT["ENV_COARSE"] = 12] = "ENV_COARSE";
+ PSG49_LUT[PSG49_LUT["ENV_SHAPE"] = 13] = "ENV_SHAPE";
+})(PSG49_LUT || (PSG49_LUT = {}));
+var PSG49 = /** @class */ (function () {
+ function PSG49(clockRate, intRate) {
+ // main register file
+ this.register = {
+ A_FINE: 0x0, A_COARSE: 0x0,
+ B_FINE: 0x0, B_COARSE: 0x0,
+ C_FINE: 0x0, C_COARSE: 0x0,
+ NOISE_PERIOD: 0x0,
+ // bit position
+ // 5 4 3 2 1 0
+ // NC NB NA TC TB TA
+ // T = Tone, N = Noise
+ MIXER: 0x0,
+ A_VOL: 0x0,
+ B_VOL: 0x0,
+ C_VOL: 0x0,
+ ENV_FINE: 0x0, ENV_COARSE: 0x0,
+ ENV_SHAPE: 0x0,
+ PORT_A: 0x0,
+ PORT_B: 0x0
+ };
+ this.driver = new AudioDriver(this);
+ this.interpolate = [
+ new Interpolator(),
+ new Interpolator()
+ ];
+ var m = 8;
+ this.fir = [
+ new FirFilter(FIR, m),
+ new FirFilter(FIR, m)
+ ];
+ this.oversample = m;
+ this.clock = {
+ frequency: clockRate,
+ scale: 1 / 16 * 2,
+ cycle: 0,
+ step: 0
+ };
+ this.interrupt = {
+ frequency: intRate,
+ cycle: 0,
+ routine: function () { }
+ };
+ this.envelope = {
+ strobe: 0,
+ transient: 0,
+ step: 0,
+ shape: 0,
+ offset: 0,
+ stub: []
+ };
+ this.channels = [
+ {
+ counter: 0x0,
+ pan: 0.5,
+ },
+ {
+ counter: 0x0,
+ pan: 0.5
+ },
+ {
+ counter: 0x0,
+ pan: 0.5
+ },
+ { counter: 0x0 }
+ ];
+ // seed noise generator
+ this.channels[3].port = 0x1;
+ this.dac = [];
+ this.build_dac(1.3, 40);
+ this.build_adsr();
+ }
+ PSG49.prototype.build_dac = function (decay, shift) {
+ var dac = this.dac;
+ var y = Math.sqrt(decay);
+ var z = shift / 31;
+ dac[0] = 0;
+ dac[1] = 0;
+ for (var i = 2; i <= 31; i++) {
+ dac[i] = 1.0 / Math.pow(y, shift - (z * i));
+ }
+ };
+ PSG49.prototype.init_test = function () {
+ var r = this.register;
+ r.MIXER = 56;
+ r.A_VOL = 15;
+ //r.A_VOL |= 0x10;
+ r.A_FINE = 200;
+ //r.ENV_COARSE = 200;
+ };
+ PSG49.prototype.build_adsr = function () {
+ var envelope = this.envelope;
+ var stub = envelope.stub;
+ stub.reset = function (ev) {
+ var strobe = ev.strobe;
+ var transient = ev.transient;
+ switch (ev.offset) {
+ case 0x4:
+ transient = 0;
+ case 0x0:
+ ev.step = strobe ? transient : 31;
+ break;
+ case 0x5:
+ transient = 31;
+ case 0x1:
+ ev.step = strobe ? transient : 0;
+ break;
+ case 0x2:
+ ev.step = 31;
+ break;
+ case 0x3:
+ ev.step = 0;
+ break;
+ }
+ };
+ stub.grow = function (ev) {
+ if (++ev.step > 31) {
+ ev.strobe ^= 1;
+ ev.stub.reset(ev);
+ }
+ };
+ stub.decay = function (ev) {
+ if (--ev.step < 0) {
+ ev.strobe ^= 1;
+ ev.stub.reset(ev);
+ }
+ };
+ stub.hold = function (ev) { };
+ envelope.matrix = [
+ [stub.decay, stub.hold],
+ [stub.grow, stub.hold],
+ [stub.decay, stub.decay],
+ [stub.grow, stub.grow],
+ [stub.decay, stub.grow],
+ [stub.grow, stub.decay],
+ ];
+ };
+ PSG49.prototype.clamp = function () {
+ var r = this.register;
+ r.A_FINE &= 0xff;
+ r.B_FINE &= 0xff;
+ r.C_FINE &= 0xff;
+ r.ENV_FINE &= 0xff;
+ r.A_COARSE &= 0xf;
+ r.B_COARSE &= 0xf;
+ r.C_COARSE &= 0xf;
+ r.ENV_COARSE &= 0xff;
+ r.A_VOL &= 0x1f;
+ r.B_VOL &= 0x1f;
+ r.C_VOL &= 0x1f;
+ r.NOISE_PERIOD &= 0x1f;
+ r.MIXER &= 0x3f;
+ r.ENV_SHAPE &= 0xff;
+ };
+ PSG49.prototype.map = function () {
+ var r = this.register;
+ var channel = this.channels;
+ var ev = this.envelope;
+ var toneMask = [0x1, 0x2, 0x4];
+ var noiseMask = [0x8, 0x10, 0x20];
+ this.clamp();
+ // update tone channel period
+ channel[0].period = r.A_FINE | r.A_COARSE << 8;
+ channel[1].period = r.B_FINE | r.B_COARSE << 8;
+ channel[2].period = r.C_FINE | r.C_COARSE << 8;
+ channel[0].volume = r.A_VOL & 0xf;
+ channel[1].volume = r.B_VOL & 0xf;
+ channel[2].volume = r.C_VOL & 0xf;
+ for (var i = 0; i < 3; i++) {
+ var bit = r.MIXER & toneMask[i];
+ channel[i].tone = bit ? 1 : 0;
+ }
+ for (var i = 0; i < 3; i++) {
+ var bit = r.MIXER & noiseMask[i];
+ channel[i].noise = bit ? 1 : 0;
+ }
+ channel[0].envelope = (r.A_VOL & 0x10) ? 0 : 1;
+ channel[1].envelope = (r.B_VOL & 0x10) ? 0 : 1;
+ channel[2].envelope = (r.C_VOL & 0x10) ? 0 : 1;
+ // update channel noise period
+ channel[3].period = r.NOISE_PERIOD << 1;
+ ev.period = r.ENV_FINE | r.ENV_COARSE << 8;
+ ev.shape = r.ENV_SHAPE;
+ switch (ev.shape) {
+ case 0x0:
+ case 0x1:
+ case 0x2:
+ case 0x3:
+ case 0x9:
+ ev.transient = 0;
+ ev.offset = 0;
+ r.ENV_SHAPE = 0xff;
+ break;
+ case 0xb:
+ ev.transient = 31;
+ ev.offset = 0;
+ r.ENV_SHAPE = 0xff;
+ break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0xf:
+ ev.transient = 0;
+ ev.offset = 1;
+ r.ENV_SHAPE = 0xff;
+ case 0xd:
+ ev.transient = 31;
+ ev.offset = 1;
+ r.ENV_SHAPE = 0xff;
+ break;
+ case 0x8:
+ ev.offset = 2;
+ break;
+ case 0xc:
+ ev.offset = 3;
+ break;
+ case 0xa:
+ ev.offset = 4;
+ break;
+ case 0xe:
+ ev.offset = 5;
+ break;
+ }
+ if (ev.shape != ev.store) {
+ ev.strobe = 0x0;
+ ev.counter = 0x0;
+ ev.stub.reset(ev);
+ }
+ ev.store = r.ENV_SHAPE;
+ };
+ PSG49.prototype.step_tone = function (index) {
+ var ch = this.channels[index % 3];
+ var step = this.clock.step;
+ var port = ch.port;
+ var period = (ch.period == 0x0) ? 0x1 : ch.period;
+ ch.counter += step;
+ if (ch.counter >= period) {
+ // 50% duty cycle
+ port ^= 0x1;
+ ch.port = port;
+ ch.counter = 0x0;
+ }
+ return ch.port;
+ };
+ PSG49.prototype.step_envelope = function () {
+ var step = this.clock.step;
+ var ev = this.envelope;
+ ev.counter += step;
+ if (ev.counter >= ev.period) {
+ ev.matrix[ev.offset][ev.strobe](ev);
+ ev.counter = 0x0;
+ }
+ return (ev.step);
+ };
+ PSG49.prototype.step_noise = function () {
+ var ch = this.channels[3];
+ var step = this.clock.step;
+ var port = ch.port;
+ var period = (ch.period == 0) ? 1 : ch.period;
+ ch.counter += step;
+ if (ch.counter >= period) {
+ port ^= (((port & 1) ^ ((port >> 3) & 1)) << 17);
+ port >>= 1;
+ ch.port = port;
+ ch.counter = 0x0;
+ }
+ return ch.port & 1;
+ };
+ PSG49.prototype.step_mixer = function () {
+ var port = 0x0;
+ var output = [0.0, 0.0];
+ var index = 0x0;
+ var ch = this.channels;
+ var noise = this.step_noise();
+ var step = this.step_envelope();
+ for (var i = 0; i < 3; i++) {
+ var volume = ch[i].volume;
+ var pan = ch[i].pan;
+ port = this.step_tone(i) | ch[i].tone;
+ port &= noise | ch[i].noise;
+ // todo: add dac volume table
+ //bit*=toneChannel[i].volume;
+ // mix each channel
+ if (!ch[i].envelope) {
+ index = step;
+ }
+ else {
+ index = volume * 2 + 1;
+ }
+ port *= this.dac[index];
+ // clamp pan levels
+ // distortion over +1 ?
+ if (pan > 0.9) {
+ pan = 0.9;
+ }
+ else if (pan < 0.1) {
+ pan = 0.1;
+ }
+ output[0] += port * (1 - pan);
+ output[1] += port * (pan);
+ }
+ return output;
+ };
+ PSG49.prototype.step = function () {
+ var output = [];
+ var clockStep = 0;
+ var intStep = 0;
+ var i = 0x0;
+ var clock = this.clock;
+ var driver = this.driver;
+ var fir = this.fir;
+ var oversample = this.oversample;
+ var interpolate = this.interpolate;
+ var interrupt = this.interrupt;
+ var x = clock.scale;
+ var fc = clock.frequency;
+ var fd = driver.frequency;
+ var fi = interrupt.frequency;
+ clockStep = (fc * x) / fd;
+ clock.step = clockStep / oversample;
+ intStep = fi / fd;
+ // add number of clock cycle
+ interrupt.cycle += intStep;
+ // do we have clock cycles to process?
+ // if so process single clock cycle
+ var sample_left = [];
+ var sample_right = [];
+ for (i = 0; i < oversample; i++) {
+ sample_left[i] = 0x0;
+ sample_right[i] = 0x0;
+ }
+ if (interrupt.cycle > 1) {
+ interrupt.cycle--;
+ interrupt.routine();
+ interrupt.cycle = 0;
+ }
+ for (var i_1 = 0; i_1 < oversample; i_1++) {
+ clock.cycle += clockStep;
+ if (clock.cycle > 1) {
+ clock.cycle--;
+ this.map();
+ output = this.step_mixer();
+ interpolate[0].step(output[0]);
+ interpolate[1].step(output[1]);
+ }
+ sample_left[i_1] = interpolate[0].cubic(0.5);
+ sample_right[i_1] = interpolate[1].cubic(0.5);
+ }
+ output[0] = fir[0].step(sample_left);
+ output[1] = fir[1].step(sample_right);
+ return output;
+ };
+ return PSG49;
+}());
diff --git a/fym.js b/fym.js
new file mode 100644
index 0000000..8ee4cc3
--- /dev/null
+++ b/fym.js
@@ -0,0 +1,66 @@
+FYMReader = function(buffer) {
+ var psgDump = pako.inflate(new Uint8Array(buffer));
+ var ptr = 0;
+ var frame = 0;
+
+ function getInt() {
+ var r = 0;
+ for(var i = 0; i < 4; i++) r += psgDump[ptr++] << (8*i);
+ return r;
+ }
+
+ function getStr() {
+ var c, r = '';
+ while(c = psgDump[ptr++]) r += String.fromCharCode(c);
+ return r;
+ }
+
+ var offset = getInt();
+
+ var frameCount = getInt();
+ this.getFrameCount = function() {
+ return frameCount;
+ }
+
+ var loopFrame = getInt();
+ this.getLoopFrame = function() {
+ return loopFrame;
+ }
+
+ var clockRate = getInt();
+ this.getClockRate = function() {
+ return clockRate;
+ }
+
+ var frameRate = getInt();
+ this.getFrameRate = function() {
+ return frameRate;
+ }
+
+ var trackName = getStr();
+ this.getTrackName = function() {
+ return trackName;
+ }
+
+ var authorName = getStr();
+ this.getAuthorName = function() {
+ return authorName;
+ }
+
+ var loopCount = 0;
+ this.getLoopCount = function() {
+ return loopCount;
+ }
+
+ this.getNextFrame = function() {
+ var regs = [];
+ for(var r = 0; r < 14; r++) {
+ regs[r] = psgDump[r * frameCount + frame + offset];
+ }
+ if(++frame >= frameCount) {
+ loopCount++;
+ frame = loopFrame;
+ }
+ return regs;
+ }
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..3b469da
--- /dev/null
+++ b/index.html
@@ -0,0 +1,7 @@
+<html>
+Click anywhere to play.
+<script type="text/javascript" src="./pako_inflate.min.js"></script>
+<script type="text/javascript" src="./fym.js"></script>
+<script type="text/javascript" src="./emu8910.js"></script>
+<script type="text/javascript" src="./parser.js"></script>
+</html> \ No newline at end of file
diff --git a/music/01_scalesmannmisfire.fym b/music/01_scalesmannmisfire.fym
new file mode 100644
index 0000000..497e8a1
--- /dev/null
+++ b/music/01_scalesmannmisfire.fym
Binary files differ
diff --git a/pako_inflate.min.js b/pako_inflate.min.js
new file mode 100644
index 0000000..0bc5de0
--- /dev/null
+++ b/pako_inflate.min.js
@@ -0,0 +1 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).pako=e()}}(function(){return function r(o,s,f){function l(t,e){if(!s[t]){if(!o[t]){var i="function"==typeof require&&require;if(!e&&i)return i(t,!0);if(d)return d(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var a=s[t]={exports:{}};o[t][0].call(a.exports,function(e){return l(o[t][1][e]||e)},a,a.exports,r,o,s,f)}return s[t].exports}for(var d="function"==typeof require&&require,e=0;e<f.length;e++)l(f[e]);return l}({1:[function(e,t,i){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;i.assign=function(e){for(var t,i,n=Array.prototype.slice.call(arguments,1);n.length;){var a=n.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(var r in a)t=a,i=r,Object.prototype.hasOwnProperty.call(t,i)&&(e[r]=a[r])}}return e},i.shrinkBuf=function(e,t){return e.length===t?e:e.subarray?e.subarray(0,t):(e.length=t,e)};var a={arraySet:function(e,t,i,n,a){if(t.subarray&&e.subarray)e.set(t.subarray(i,i+n),a);else for(var r=0;r<n;r++)e[a+r]=t[i+r]},flattenChunks:function(e){var t,i,n,a,r,o;for(t=n=0,i=e.length;t<i;t++)n+=e[t].length;for(o=new Uint8Array(n),t=a=0,i=e.length;t<i;t++)r=e[t],o.set(r,a),a+=r.length;return o}},r={arraySet:function(e,t,i,n,a){for(var r=0;r<n;r++)e[a+r]=t[i+r]},flattenChunks:function(e){return[].concat.apply([],e)}};i.setTyped=function(e){e?(i.Buf8=Uint8Array,i.Buf16=Uint16Array,i.Buf32=Int32Array,i.assign(i,a)):(i.Buf8=Array,i.Buf16=Array,i.Buf32=Array,i.assign(i,r))},i.setTyped(n)},{}],2:[function(e,t,i){"use strict";var f=e("./common"),a=!0,r=!0;try{String.fromCharCode.apply(null,[0])}catch(e){a=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(e){r=!1}for(var l=new f.Buf8(256),n=0;n<256;n++)l[n]=252<=n?6:248<=n?5:240<=n?4:224<=n?3:192<=n?2:1;function d(e,t){if(t<65534&&(e.subarray&&r||!e.subarray&&a))return String.fromCharCode.apply(null,f.shrinkBuf(e,t));for(var i="",n=0;n<t;n++)i+=String.fromCharCode(e[n]);return i}l[254]=l[254]=1,i.string2buf=function(e){var t,i,n,a,r,o=e.length,s=0;for(a=0;a<o;a++)55296==(64512&(i=e.charCodeAt(a)))&&a+1<o&&56320==(64512&(n=e.charCodeAt(a+1)))&&(i=65536+(i-55296<<10)+(n-56320),a++),s+=i<128?1:i<2048?2:i<65536?3:4;for(t=new f.Buf8(s),a=r=0;r<s;a++)55296==(64512&(i=e.charCodeAt(a)))&&a+1<o&&56320==(64512&(n=e.charCodeAt(a+1)))&&(i=65536+(i-55296<<10)+(n-56320),a++),i<128?t[r++]=i:(i<2048?t[r++]=192|i>>>6:(i<65536?t[r++]=224|i>>>12:(t[r++]=240|i>>>18,t[r++]=128|i>>>12&63),t[r++]=128|i>>>6&63),t[r++]=128|63&i);return t},i.buf2binstring=function(e){return d(e,e.length)},i.binstring2buf=function(e){for(var t=new f.Buf8(e.length),i=0,n=t.length;i<n;i++)t[i]=e.charCodeAt(i);return t},i.buf2string=function(e,t){var i,n,a,r,o=t||e.length,s=new Array(2*o);for(i=n=0;i<o;)if((a=e[i++])<128)s[n++]=a;else if(4<(r=l[a]))s[n++]=65533,i+=r-1;else{for(a&=2===r?31:3===r?15:7;1<r&&i<o;)a=a<<6|63&e[i++],r--;1<r?s[n++]=65533:a<65536?s[n++]=a:(a-=65536,s[n++]=55296|a>>10&1023,s[n++]=56320|1023&a)}return d(s,n)},i.utf8border=function(e,t){var i;for((t=t||e.length)>e.length&&(t=e.length),i=t-1;0<=i&&128==(192&e[i]);)i--;return i<0?t:0===i?t:i+l[e[i]]>t?i:t}},{"./common":1}],3:[function(e,t,i){"use strict";t.exports=function(e,t,i,n){for(var a=65535&e|0,r=e>>>16&65535|0,o=0;0!==i;){for(i-=o=2e3<i?2e3:i;r=r+(a=a+t[n++]|0)|0,--o;);a%=65521,r%=65521}return a|r<<16|0}},{}],4:[function(e,t,i){"use strict";t.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],5:[function(e,t,i){"use strict";var s=function(){for(var e,t=[],i=0;i<256;i++){e=i;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[i]=e}return t}();t.exports=function(e,t,i,n){var a=s,r=n+i;e^=-1;for(var o=n;o<r;o++)e=e>>>8^a[255&(e^t[o])];return-1^e}},{}],6:[function(e,t,i){"use strict";t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},{}],7:[function(e,t,i){"use strict";t.exports=function(e,t){var i,n,a,r,o,s,f,l,d,c,u,h,b,m,w,k,_,g,v,p,x,y,S,E,Z;i=e.state,n=e.next_in,E=e.input,a=n+(e.avail_in-5),r=e.next_out,Z=e.output,o=r-(t-e.avail_out),s=r+(e.avail_out-257),f=i.dmax,l=i.wsize,d=i.whave,c=i.wnext,u=i.window,h=i.hold,b=i.bits,m=i.lencode,w=i.distcode,k=(1<<i.lenbits)-1,_=(1<<i.distbits)-1;e:do{b<15&&(h+=E[n++]<<b,b+=8,h+=E[n++]<<b,b+=8),g=m[h&k];t:for(;;){if(h>>>=v=g>>>24,b-=v,0===(v=g>>>16&255))Z[r++]=65535&g;else{if(!(16&v)){if(0==(64&v)){g=m[(65535&g)+(h&(1<<v)-1)];continue t}if(32&v){i.mode=12;break e}e.msg="invalid literal/length code",i.mode=30;break e}p=65535&g,(v&=15)&&(b<v&&(h+=E[n++]<<b,b+=8),p+=h&(1<<v)-1,h>>>=v,b-=v),b<15&&(h+=E[n++]<<b,b+=8,h+=E[n++]<<b,b+=8),g=w[h&_];i:for(;;){if(h>>>=v=g>>>24,b-=v,!(16&(v=g>>>16&255))){if(0==(64&v)){g=w[(65535&g)+(h&(1<<v)-1)];continue i}e.msg="invalid distance code",i.mode=30;break e}if(x=65535&g,b<(v&=15)&&(h+=E[n++]<<b,(b+=8)<v&&(h+=E[n++]<<b,b+=8)),f<(x+=h&(1<<v)-1)){e.msg="invalid distance too far back",i.mode=30;break e}if(h>>>=v,b-=v,(v=r-o)<x){if(d<(v=x-v)&&i.sane){e.msg="invalid distance too far back",i.mode=30;break e}if(S=u,(y=0)===c){if(y+=l-v,v<p){for(p-=v;Z[r++]=u[y++],--v;);y=r-x,S=Z}}else if(c<v){if(y+=l+c-v,(v-=c)<p){for(p-=v;Z[r++]=u[y++],--v;);if(y=0,c<p){for(p-=v=c;Z[r++]=u[y++],--v;);y=r-x,S=Z}}}else if(y+=c-v,v<p){for(p-=v;Z[r++]=u[y++],--v;);y=r-x,S=Z}for(;2<p;)Z[r++]=S[y++],Z[r++]=S[y++],Z[r++]=S[y++],p-=3;p&&(Z[r++]=S[y++],1<p&&(Z[r++]=S[y++]))}else{for(y=r-x;Z[r++]=Z[y++],Z[r++]=Z[y++],Z[r++]=Z[y++],2<(p-=3););p&&(Z[r++]=Z[y++],1<p&&(Z[r++]=Z[y++]))}break}}break}}while(n<a&&r<s);n-=p=b>>3,h&=(1<<(b-=p<<3))-1,e.next_in=n,e.next_out=r,e.avail_in=n<a?a-n+5:5-(n-a),e.avail_out=r<s?s-r+257:257-(r-s),i.hold=h,i.bits=b}},{}],8:[function(e,t,i){"use strict";var z=e("../utils/common"),R=e("./adler32"),N=e("./crc32"),O=e("./inffast"),C=e("./inftrees"),I=1,D=2,T=0,U=-2,F=1,n=852,a=592;function L(e){return(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function r(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new z.Buf16(320),this.work=new z.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function o(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=F,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new z.Buf32(n),t.distcode=t.distdyn=new z.Buf32(a),t.sane=1,t.back=-1,T):U}function s(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,o(e)):U}function f(e,t){var i,n;return e&&e.state?(n=e.state,t<0?(i=0,t=-t):(i=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15<t)?U:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=i,n.wbits=t,s(e))):U}function l(e,t){var i,n;return e?(n=new r,(e.state=n).window=null,(i=f(e,t))!==T&&(e.state=null),i):U}var d,c,u=!0;function H(e){if(u){var t;for(d=new z.Buf32(512),c=new z.Buf32(32),t=0;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(C(I,e.lens,0,288,d,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;C(D,e.lens,0,32,c,0,e.work,{bits:5}),u=!1}e.lencode=d,e.lenbits=9,e.distcode=c,e.distbits=5}function j(e,t,i,n){var a,r=e.state;return null===r.window&&(r.wsize=1<<r.wbits,r.wnext=0,r.whave=0,r.window=new z.Buf8(r.wsize)),n>=r.wsize?(z.arraySet(r.window,t,i-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(n<(a=r.wsize-r.wnext)&&(a=n),z.arraySet(r.window,t,i-n,a,r.wnext),(n-=a)?(z.arraySet(r.window,t,i-n,n,0),r.wnext=n,r.whave=r.wsize):(r.wnext+=a,r.wnext===r.wsize&&(r.wnext=0),r.whave<r.wsize&&(r.whave+=a))),0}i.inflateReset=s,i.inflateReset2=f,i.inflateResetKeep=o,i.inflateInit=function(e){return l(e,15)},i.inflateInit2=l,i.inflate=function(e,t){var i,n,a,r,o,s,f,l,d,c,u,h,b,m,w,k,_,g,v,p,x,y,S,E,Z=0,B=new z.Buf8(4),A=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!e||!e.state||!e.output||!e.input&&0!==e.avail_in)return U;12===(i=e.state).mode&&(i.mode=13),o=e.next_out,a=e.output,f=e.avail_out,r=e.next_in,n=e.input,s=e.avail_in,l=i.hold,d=i.bits,c=s,u=f,y=T;e:for(;;)switch(i.mode){case F:if(0===i.wrap){i.mode=13;break}for(;d<16;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(2&i.wrap&&35615===l){B[i.check=0]=255&l,B[1]=l>>>8&255,i.check=N(i.check,B,2,0),d=l=0,i.mode=2;break}if(i.flags=0,i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&l)<<8)+(l>>8))%31){e.msg="incorrect header check",i.mode=30;break}if(8!=(15&l)){e.msg="unknown compression method",i.mode=30;break}if(d-=4,x=8+(15&(l>>>=4)),0===i.wbits)i.wbits=x;else if(x>i.wbits){e.msg="invalid window size",i.mode=30;break}i.dmax=1<<x,e.adler=i.check=1,i.mode=512&l?10:12,d=l=0;break;case 2:for(;d<16;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(i.flags=l,8!=(255&i.flags)){e.msg="unknown compression method",i.mode=30;break}if(57344&i.flags){e.msg="unknown header flags set",i.mode=30;break}i.head&&(i.head.text=l>>8&1),512&i.flags&&(B[0]=255&l,B[1]=l>>>8&255,i.check=N(i.check,B,2,0)),d=l=0,i.mode=3;case 3:for(;d<32;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.head&&(i.head.time=l),512&i.flags&&(B[0]=255&l,B[1]=l>>>8&255,B[2]=l>>>16&255,B[3]=l>>>24&255,i.check=N(i.check,B,4,0)),d=l=0,i.mode=4;case 4:for(;d<16;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.head&&(i.head.xflags=255&l,i.head.os=l>>8),512&i.flags&&(B[0]=255&l,B[1]=l>>>8&255,i.check=N(i.check,B,2,0)),d=l=0,i.mode=5;case 5:if(1024&i.flags){for(;d<16;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.length=l,i.head&&(i.head.extra_len=l),512&i.flags&&(B[0]=255&l,B[1]=l>>>8&255,i.check=N(i.check,B,2,0)),d=l=0}else i.head&&(i.head.extra=null);i.mode=6;case 6:if(1024&i.flags&&(s<(h=i.length)&&(h=s),h&&(i.head&&(x=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Array(i.head.extra_len)),z.arraySet(i.head.extra,n,r,h,x)),512&i.flags&&(i.check=N(i.check,n,h,r)),s-=h,r+=h,i.length-=h),i.length))break e;i.length=0,i.mode=7;case 7:if(2048&i.flags){if(0===s)break e;for(h=0;x=n[r+h++],i.head&&x&&i.length<65536&&(i.head.name+=String.fromCharCode(x)),x&&h<s;);if(512&i.flags&&(i.check=N(i.check,n,h,r)),s-=h,r+=h,x)break e}else i.head&&(i.head.name=null);i.length=0,i.mode=8;case 8:if(4096&i.flags){if(0===s)break e;for(h=0;x=n[r+h++],i.head&&x&&i.length<65536&&(i.head.comment+=String.fromCharCode(x)),x&&h<s;);if(512&i.flags&&(i.check=N(i.check,n,h,r)),s-=h,r+=h,x)break e}else i.head&&(i.head.comment=null);i.mode=9;case 9:if(512&i.flags){for(;d<16;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(l!==(65535&i.check)){e.msg="header crc mismatch",i.mode=30;break}d=l=0}i.head&&(i.head.hcrc=i.flags>>9&1,i.head.done=!0),e.adler=i.check=0,i.mode=12;break;case 10:for(;d<32;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}e.adler=i.check=L(l),d=l=0,i.mode=11;case 11:if(0===i.havedict)return e.next_out=o,e.avail_out=f,e.next_in=r,e.avail_in=s,i.hold=l,i.bits=d,2;e.adler=i.check=1,i.mode=12;case 12:if(5===t||6===t)break e;case 13:if(i.last){l>>>=7&d,d-=7&d,i.mode=27;break}for(;d<3;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}switch(i.last=1&l,d-=1,3&(l>>>=1)){case 0:i.mode=14;break;case 1:if(H(i),i.mode=20,6!==t)break;l>>>=2,d-=2;break e;case 2:i.mode=17;break;case 3:e.msg="invalid block type",i.mode=30}l>>>=2,d-=2;break;case 14:for(l>>>=7&d,d-=7&d;d<32;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if((65535&l)!=(l>>>16^65535)){e.msg="invalid stored block lengths",i.mode=30;break}if(i.length=65535&l,d=l=0,i.mode=15,6===t)break e;case 15:i.mode=16;case 16:if(h=i.length){if(s<h&&(h=s),f<h&&(h=f),0===h)break e;z.arraySet(a,n,r,h,o),s-=h,r+=h,f-=h,o+=h,i.length-=h;break}i.mode=12;break;case 17:for(;d<14;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(i.nlen=257+(31&l),l>>>=5,d-=5,i.ndist=1+(31&l),l>>>=5,d-=5,i.ncode=4+(15&l),l>>>=4,d-=4,286<i.nlen||30<i.ndist){e.msg="too many length or distance symbols",i.mode=30;break}i.have=0,i.mode=18;case 18:for(;i.have<i.ncode;){for(;d<3;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.lens[A[i.have++]]=7&l,l>>>=3,d-=3}for(;i.have<19;)i.lens[A[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,S={bits:i.lenbits},y=C(0,i.lens,0,19,i.lencode,0,i.work,S),i.lenbits=S.bits,y){e.msg="invalid code lengths set",i.mode=30;break}i.have=0,i.mode=19;case 19:for(;i.have<i.nlen+i.ndist;){for(;k=(Z=i.lencode[l&(1<<i.lenbits)-1])>>>16&255,_=65535&Z,!((w=Z>>>24)<=d);){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(_<16)l>>>=w,d-=w,i.lens[i.have++]=_;else{if(16===_){for(E=w+2;d<E;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(l>>>=w,d-=w,0===i.have){e.msg="invalid bit length repeat",i.mode=30;break}x=i.lens[i.have-1],h=3+(3&l),l>>>=2,d-=2}else if(17===_){for(E=w+3;d<E;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}d-=w,x=0,h=3+(7&(l>>>=w)),l>>>=3,d-=3}else{for(E=w+7;d<E;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}d-=w,x=0,h=11+(127&(l>>>=w)),l>>>=7,d-=7}if(i.have+h>i.nlen+i.ndist){e.msg="invalid bit length repeat",i.mode=30;break}for(;h--;)i.lens[i.have++]=x}}if(30===i.mode)break;if(0===i.lens[256]){e.msg="invalid code -- missing end-of-block",i.mode=30;break}if(i.lenbits=9,S={bits:i.lenbits},y=C(I,i.lens,0,i.nlen,i.lencode,0,i.work,S),i.lenbits=S.bits,y){e.msg="invalid literal/lengths set",i.mode=30;break}if(i.distbits=6,i.distcode=i.distdyn,S={bits:i.distbits},y=C(D,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,S),i.distbits=S.bits,y){e.msg="invalid distances set",i.mode=30;break}if(i.mode=20,6===t)break e;case 20:i.mode=21;case 21:if(6<=s&&258<=f){e.next_out=o,e.avail_out=f,e.next_in=r,e.avail_in=s,i.hold=l,i.bits=d,O(e,u),o=e.next_out,a=e.output,f=e.avail_out,r=e.next_in,n=e.input,s=e.avail_in,l=i.hold,d=i.bits,12===i.mode&&(i.back=-1);break}for(i.back=0;k=(Z=i.lencode[l&(1<<i.lenbits)-1])>>>16&255,_=65535&Z,!((w=Z>>>24)<=d);){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(k&&0==(240&k)){for(g=w,v=k,p=_;k=(Z=i.lencode[p+((l&(1<<g+v)-1)>>g)])>>>16&255,_=65535&Z,!(g+(w=Z>>>24)<=d);){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}l>>>=g,d-=g,i.back+=g}if(l>>>=w,d-=w,i.back+=w,i.length=_,0===k){i.mode=26;break}if(32&k){i.back=-1,i.mode=12;break}if(64&k){e.msg="invalid literal/length code",i.mode=30;break}i.extra=15&k,i.mode=22;case 22:if(i.extra){for(E=i.extra;d<E;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.length+=l&(1<<i.extra)-1,l>>>=i.extra,d-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=23;case 23:for(;k=(Z=i.distcode[l&(1<<i.distbits)-1])>>>16&255,_=65535&Z,!((w=Z>>>24)<=d);){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(0==(240&k)){for(g=w,v=k,p=_;k=(Z=i.distcode[p+((l&(1<<g+v)-1)>>g)])>>>16&255,_=65535&Z,!(g+(w=Z>>>24)<=d);){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}l>>>=g,d-=g,i.back+=g}if(l>>>=w,d-=w,i.back+=w,64&k){e.msg="invalid distance code",i.mode=30;break}i.offset=_,i.extra=15&k,i.mode=24;case 24:if(i.extra){for(E=i.extra;d<E;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}i.offset+=l&(1<<i.extra)-1,l>>>=i.extra,d-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){e.msg="invalid distance too far back",i.mode=30;break}i.mode=25;case 25:if(0===f)break e;if(h=u-f,i.offset>h){if((h=i.offset-h)>i.whave&&i.sane){e.msg="invalid distance too far back",i.mode=30;break}h>i.wnext?(h-=i.wnext,b=i.wsize-h):b=i.wnext-h,h>i.length&&(h=i.length),m=i.window}else m=a,b=o-i.offset,h=i.length;for(f<h&&(h=f),f-=h,i.length-=h;a[o++]=m[b++],--h;);0===i.length&&(i.mode=21);break;case 26:if(0===f)break e;a[o++]=i.length,f--,i.mode=21;break;case 27:if(i.wrap){for(;d<32;){if(0===s)break e;s--,l|=n[r++]<<d,d+=8}if(u-=f,e.total_out+=u,i.total+=u,u&&(e.adler=i.check=i.flags?N(i.check,a,u,o-u):R(i.check,a,u,o-u)),u=f,(i.flags?l:L(l))!==i.check){e.msg="incorrect data check",i.mode=30;break}d=l=0}i.mode=28;case 28:if(i.wrap&&i.flags){for(;d<32;){if(0===s)break e;s--,l+=n[r++]<<d,d+=8}if(l!==(4294967295&i.total)){e.msg="incorrect length check",i.mode=30;break}d=l=0}i.mode=29;case 29:y=1;break e;case 30:y=-3;break e;case 31:return-4;case 32:default:return U}return e.next_out=o,e.avail_out=f,e.next_in=r,e.avail_in=s,i.hold=l,i.bits=d,(i.wsize||u!==e.avail_out&&i.mode<30&&(i.mode<27||4!==t))&&j(e,e.output,e.next_out,u-e.avail_out)?(i.mode=31,-4):(c-=e.avail_in,u-=e.avail_out,e.total_in+=c,e.total_out+=u,i.total+=u,i.wrap&&u&&(e.adler=i.check=i.flags?N(i.check,a,u,e.next_out-u):R(i.check,a,u,e.next_out-u)),e.data_type=i.bits+(i.last?64:0)+(12===i.mode?128:0)+(20===i.mode||15===i.mode?256:0),(0===c&&0===u||4===t)&&y===T&&(y=-5),y)},i.inflateEnd=function(e){if(!e||!e.state)return U;var t=e.state;return t.window&&(t.window=null),e.state=null,T},i.inflateGetHeader=function(e,t){var i;return e&&e.state?0==(2&(i=e.state).wrap)?U:((i.head=t).done=!1,T):U},i.inflateSetDictionary=function(e,t){var i,n=t.length;return e&&e.state?0!==(i=e.state).wrap&&11!==i.mode?U:11===i.mode&&R(1,t,n,0)!==i.check?-3:j(e,t,n,n)?(i.mode=31,-4):(i.havedict=1,T):U},i.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":1,"./adler32":3,"./crc32":5,"./inffast":7,"./inftrees":9}],9:[function(e,t,i){"use strict";var I=e("../utils/common"),D=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],T=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],U=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],F=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];t.exports=function(e,t,i,n,a,r,o,s){var f,l,d,c,u,h,b,m,w,k=s.bits,_=0,g=0,v=0,p=0,x=0,y=0,S=0,E=0,Z=0,B=0,A=null,z=0,R=new I.Buf16(16),N=new I.Buf16(16),O=null,C=0;for(_=0;_<=15;_++)R[_]=0;for(g=0;g<n;g++)R[t[i+g]]++;for(x=k,p=15;1<=p&&0===R[p];p--);if(p<x&&(x=p),0===p)return a[r++]=20971520,a[r++]=20971520,s.bits=1,0;for(v=1;v<p&&0===R[v];v++);for(x<v&&(x=v),_=E=1;_<=15;_++)if(E<<=1,(E-=R[_])<0)return-1;if(0<E&&(0===e||1!==p))return-1;for(N[1]=0,_=1;_<15;_++)N[_+1]=N[_]+R[_];for(g=0;g<n;g++)0!==t[i+g]&&(o[N[t[i+g]]++]=g);if(0===e?(A=O=o,h=19):1===e?(A=D,z-=257,O=T,C-=257,h=256):(A=U,O=F,h=-1),_=v,u=r,S=g=B=0,d=-1,c=(Z=1<<(y=x))-1,1===e&&852<Z||2===e&&592<Z)return 1;for(;;){for(b=_-S,o[g]<h?(m=0,w=o[g]):o[g]>h?(m=O[C+o[g]],w=A[z+o[g]]):(m=96,w=0),f=1<<_-S,v=l=1<<y;a[u+(B>>S)+(l-=f)]=b<<24|m<<16|w|0,0!==l;);for(f=1<<_-1;B&f;)f>>=1;if(0!==f?(B&=f-1,B+=f):B=0,g++,0==--R[_]){if(_===p)break;_=t[i+o[g]]}if(x<_&&(B&c)!==d){for(0===S&&(S=x),u+=v,E=1<<(y=_-S);y+S<p&&!((E-=R[y+S])<=0);)y++,E<<=1;if(Z+=1<<y,1===e&&852<Z||2===e&&592<Z)return 1;a[d=B&c]=x<<24|y<<16|u-r|0}}return 0!==B&&(a[u+B]=_-S<<24|64<<16|0),s.bits=x,0}},{"../utils/common":1}],10:[function(e,t,i){"use strict";t.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],11:[function(e,t,i){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],"/lib/inflate.js":[function(e,t,i){"use strict";var c=e("./zlib/inflate"),u=e("./utils/common"),h=e("./utils/strings"),b=e("./zlib/constants"),n=e("./zlib/messages"),a=e("./zlib/zstream"),r=e("./zlib/gzheader"),m=Object.prototype.toString;function o(e){if(!(this instanceof o))return new o(e);this.options=u.assign({chunkSize:16384,windowBits:0,to:""},e||{});var t=this.options;t.raw&&0<=t.windowBits&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(0<=t.windowBits&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),15<t.windowBits&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new a,this.strm.avail_out=0;var i=c.inflateInit2(this.strm,t.windowBits);if(i!==b.Z_OK)throw new Error(n[i]);if(this.header=new r,c.inflateGetHeader(this.strm,this.header),t.dictionary&&("string"==typeof t.dictionary?t.dictionary=h.string2buf(t.dictionary):"[object ArrayBuffer]"===m.call(t.dictionary)&&(t.dictionary=new Uint8Array(t.dictionary)),t.raw&&(i=c.inflateSetDictionary(this.strm,t.dictionary))!==b.Z_OK))throw new Error(n[i])}function s(e,t){var i=new o(t);if(i.push(e,!0),i.err)throw i.msg||n[i.err];return i.result}o.prototype.push=function(e,t){var i,n,a,r,o,s=this.strm,f=this.options.chunkSize,l=this.options.dictionary,d=!1;if(this.ended)return!1;n=t===~~t?t:!0===t?b.Z_FINISH:b.Z_NO_FLUSH,"string"==typeof e?s.input=h.binstring2buf(e):"[object ArrayBuffer]"===m.call(e)?s.input=new Uint8Array(e):s.input=e,s.next_in=0,s.avail_in=s.input.length;do{if(0===s.avail_out&&(s.output=new u.Buf8(f),s.next_out=0,s.avail_out=f),(i=c.inflate(s,b.Z_NO_FLUSH))===b.Z_NEED_DICT&&l&&(i=c.inflateSetDictionary(this.strm,l)),i===b.Z_BUF_ERROR&&!0===d&&(i=b.Z_OK,d=!1),i!==b.Z_STREAM_END&&i!==b.Z_OK)return this.onEnd(i),!(this.ended=!0);s.next_out&&(0!==s.avail_out&&i!==b.Z_STREAM_END&&(0!==s.avail_in||n!==b.Z_FINISH&&n!==b.Z_SYNC_FLUSH)||("string"===this.options.to?(a=h.utf8border(s.output,s.next_out),r=s.next_out-a,o=h.buf2string(s.output,a),s.next_out=r,s.avail_out=f-r,r&&u.arraySet(s.output,s.output,a,r,0),this.onData(o)):this.onData(u.shrinkBuf(s.output,s.next_out)))),0===s.avail_in&&0===s.avail_out&&(d=!0)}while((0<s.avail_in||0===s.avail_out)&&i!==b.Z_STREAM_END);return i===b.Z_STREAM_END&&(n=b.Z_FINISH),n===b.Z_FINISH?(i=c.inflateEnd(this.strm),this.onEnd(i),this.ended=!0,i===b.Z_OK):n!==b.Z_SYNC_FLUSH||(this.onEnd(b.Z_OK),!(s.avail_out=0))},o.prototype.onData=function(e){this.chunks.push(e)},o.prototype.onEnd=function(e){e===b.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=u.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},i.Inflate=o,i.inflate=s,i.inflateRaw=function(e,t){return(t=t||{}).raw=!0,s(e,t)},i.ungzip=s},{"./utils/common":1,"./utils/strings":2,"./zlib/constants":4,"./zlib/gzheader":6,"./zlib/inflate":8,"./zlib/messages":10,"./zlib/zstream":11}]},{},[])("/lib/inflate.js")});
diff --git a/parser.js b/parser.js
new file mode 100644
index 0000000..e7e8f15
--- /dev/null
+++ b/parser.js
@@ -0,0 +1,79 @@
+
+ var song;
+ var emu8910 = new PSG49(YM_CLOCK_ZX, 50);
+
+ Array.prototype.shuffle = function() {
+ var m = this.length, t, i;
+ while(m) {
+ i = Math.floor(Math.random() * m--);
+ t = this[m];
+ this[m] = this[i];
+ this[i] = t;
+ }
+ return this;
+ }
+
+ var songs = [
+ "01_scalesmannmisfire.fym"
+ ].shuffle();
+
+ var currentSong = 0;
+
+ loadAndPlay = function() {
+ emu8910.driver.device.resume()
+ var oReq = new XMLHttpRequest();
+ oReq.open('GET', 'music/' + songs[currentSong], true);
+ oReq.responseType = 'arraybuffer';
+ oReq.onload = function(e) {
+ if(oReq.response) play(oReq.response);
+ }
+ oReq.send();
+ }
+
+ updateState = function() {
+ var r = song.getNextFrame();
+
+ emu8910.register.A_FINE = r[0];
+ emu8910.register.A_COARSE = r[1];
+
+ emu8910.register.B_FINE = r[2];
+ emu8910.register.B_COARSE = r[3];
+
+ emu8910.register.C_FINE = r[4];
+ emu8910.register.C_COARSE = r[5];
+ emu8910.register.NOISE_PERIOD = r[6];
+
+ var mix_tone_A = (r[7] & 1) << 0;
+ var mix_tone_B = ((r[7] >> 1) & 1) << 1;
+ var mix_tone_C = ((r[7] >> 2) & 1) << 2;
+
+ var mix_noise_A = ((r[7] >> 3) & 1) << 3;
+ var mix_noise_B = ((r[7] >> 4) & 1) << 4;
+ var mix_noise_C = ((r[7] >> 5) & 1) << 5;
+
+ var MIXER = (mix_tone_A | mix_tone_B | mix_tone_C
+ | mix_noise_A | mix_noise_B | mix_noise_C);
+
+ emu8910.register.MIXER = MIXER;
+
+ emu8910.register.A_VOL = r[8];
+ emu8910.register.B_VOL = r[9];
+ emu8910.register.C_VOL = r[10];
+
+ emu8910.register.ENV_FINE = r[11];
+ emu8910.register.ENV_COURSE = r[12];
+
+ if (r[13] != 0xff) {
+ emu8910.register.ENV_SHAPE = r[13];
+ }
+ }
+
+ play = function(fym) {
+ song = new FYMReader(fym);
+ emu8910.interrupt.routine = updateState;
+ emu8910.clock.frequency = song.getClockRate()
+ emu8910.interrupt.frequency = song.getFrameRate()
+ }
+
+ document.addEventListener('click', loadAndPlay, false);
+ document.addEventListener('touchend', loadAndPlay, false);
diff --git a/emu8910.ts b/src/emu8910.ts
index a057c2d..180399b 100644
--- a/emu8910.ts
+++ b/src/emu8910.ts
@@ -1,212 +1,65 @@
-// Copyright (C)
-// Author: Dylan Muller
+// Copyright (C)
+// AY8910 emulator.
+// Author: Dylan Muller.
const YM_CLOCK_ZX = 1750000;
-const FIR =
-
-[-0.000058,
- 0.000024,
- 0.000088,
- -0.000003,
- -0.000116,
- -0.000035,
- 0.000137,
- 0.000089,
- -0.000143,
- -0.000156,
- 0.000126,
- 0.000231,
- -0.000079,
- -0.000304,
- -0.000002,
- 0.000362,
- 0.000117,
- -0.000390,
- -0.000261,
- 0.000375,
- 0.000423,
- -0.000304,
- -0.000585,
- 0.000168,
- 0.000726,
- 0.000033,
- -0.000821,
- -0.000294,
- 0.000845,
- 0.000596,
- -0.000775,
- -0.000915,
- 0.000593,
- 0.001215,
- -0.000292,
- -0.001456,
- -0.000121,
- 0.001594,
- 0.000626,
- -0.001589,
- -0.001186,
- 0.001405,
- 0.001751,
- -0.001025,
- -0.002258,
- 0.000444,
- 0.002638,
- 0.000316,
- -0.002820,
- -0.001211,
- 0.002743,
- 0.002175,
- -0.002360,
- -0.003117,
- 0.001647,
- 0.003933,
- -0.000611,
- -0.004512,
- -0.000705,
- 0.004744,
- 0.002222,
- -0.004535,
- -0.003825,
- 0.003817,
- 0.005369,
- -0.002558,
- -0.006686,
- 0.000773,
- 0.007598,
- 0.001470,
- -0.007929,
- -0.004048,
- 0.007523,
- 0.006783,
- -0.006258,
- -0.009449,
- 0.004059,
- 0.011782,
- -0.000908,
- -0.013485,
- -0.003144,
- 0.014246,
- 0.007982,
- -0.013741,
- -0.013425,
- 0.011632,
- 0.019237,
- -0.007546,
- -0.025141,
- 0.000999,
- 0.030835,
- 0.008787,
- -0.036013,
- -0.023415,
- 0.040387,
- 0.047128,
- -0.043707,
- -0.095861,
- 0.045780,
- 0.314841,
- 0.453515,
- 0.314841,
- 0.045780,
- -0.095861,
- -0.043707,
- 0.047128,
- 0.040387,
- -0.023415,
- -0.036013,
- 0.008787,
- 0.030835,
- 0.000999,
- -0.025141,
- -0.007546,
- 0.019237,
- 0.011632,
- -0.013425,
- -0.013741,
- 0.007982,
- 0.014246,
- -0.003144,
- -0.013485,
- -0.000908,
- 0.011782,
- 0.004059,
- -0.009449,
- -0.006258,
- 0.006783,
- 0.007523,
- -0.004048,
- -0.007929,
- 0.001470,
- 0.007598,
- 0.000773,
- -0.006686,
- -0.002558,
- 0.005369,
- 0.003817,
- -0.003825,
- -0.004535,
- 0.002222,
- 0.004744,
- -0.000705,
- -0.004512,
- -0.000611,
- 0.003933,
- 0.001647,
- -0.003117,
- -0.002360,
- 0.002175,
- 0.002743,
- -0.001211,
- -0.002820,
- 0.000316,
- 0.002638,
- 0.000444,
- -0.002258,
- -0.001025,
- 0.001751,
- 0.001405,
- -0.001186,
- -0.001589,
- 0.000626,
- 0.001594,
- -0.000121,
- -0.001456,
- -0.000292,
- 0.001215,
- 0.000593,
- -0.000915,
- -0.000775,
- 0.000596,
- 0.000845,
- -0.000294,
- -0.000821,
- 0.000033,
- 0.000726,
- 0.000168,
- -0.000585,
- -0.000304,
- 0.000423,
- 0.000375,
- -0.000261,
- -0.000390,
- 0.000117,
- 0.000362,
- -0.000002,
- -0.000304,
- -0.000079,
- 0.000231,
- 0.000126,
- -0.000156,
- -0.000143,
- 0.000089,
- 0.000137,
- -0.000035,
- -0.000116,
- -0.000003,
- 0.000088,
- 0.000024,
- -0.000058] ;
-
-
+const FIR = [-0.011368,
+ 0.004512,
+ 0.008657,
+ -0.011763,
+ -0.000000,
+ 0.012786,
+ -0.010231,
+ -0.005801,
+ 0.015915,
+ -0.006411,
+ -0.012504,
+ 0.017299,
+ -0.000000,
+ -0.019605,
+ 0.016077,
+ 0.009370,
+ -0.026526,
+ 0.011074,
+ 0.022508,
+ -0.032676,
+ 0.000000,
+ 0.042011,
+ -0.037513,
+ -0.024362,
+ 0.079577,
+ -0.040604,
+ -0.112540,
+ 0.294080,
+ 0.625000,
+ 0.294080,
+ -0.112540,
+ -0.040604,
+ 0.079577,
+ -0.024362,
+ -0.037513,
+ 0.042011,
+ 0.000000,
+ -0.032676,
+ 0.022508,
+ 0.011074,
+ -0.026526,
+ 0.009370,
+ 0.016077,
+ -0.019605,
+ -0.000000,
+ 0.017299,
+ -0.012504,
+ -0.006411,
+ 0.015915,
+ -0.005801,
+ -0.010231,
+ 0.012786,
+ -0.000000,
+ -0.011763,
+ 0.008657,
+ 0.004512,
+ -0.011368]
interface Channel{
@@ -271,7 +124,7 @@ class Interpolator{
cubic(mu : number){
- let b = this.buffer;
+ let b = this.buffer;
let a0,a1,a2,a3,mu2 = 0;
mu2 = mu * mu2;
a0 = b[3] - b[2] - b[0] + b[1];
@@ -292,7 +145,7 @@ class BiasFilter {
length : number = 0x0;
sum: number = 0x0;
attenuate : number = 0x0;
-
+
constructor(length : number, attenuate : number){
this.length = length;
@@ -309,7 +162,7 @@ class BiasFilter {
let delta = x - this.samples[index];
let attenuate = this.attenuate;
let avg = 0x0;
-
+
this.sum += delta;
this.samples[index] = x;
@@ -367,7 +220,7 @@ class FirFilter {
buffer[this.offset + length - m + i] = buffer[this.offset + i];
}
-
+
return y;
}
@@ -387,9 +240,9 @@ class AudioDriver {
this.device = new AudioContext();
let device = this.device;
-
+
this.filter = [
-
+
new BiasFilter(1024, 1.25),
new BiasFilter(1024, 1.25),
@@ -400,13 +253,13 @@ class AudioDriver {
let filter = this.filter;
filter[2].type = "lowshelf";
- filter[2].frequency.value = 1500;
- filter[2].gain.value = 3.35;
+ filter[2].frequency.value = 10000;
+ filter[2].gain.value = 2;
filter[3].type = "lowpass";
filter[3].frequency.value = 10000;
filter[3].Q.value = 1;
-
+
this.frequency = device.sampleRate;
this.context = device.createScriptProcessor(4096,0,2);
this.context.onaudioprocess = this.update;
@@ -421,10 +274,10 @@ class AudioDriver {
}
update = function(ev : AudioProcessingEvent){
-
+
let ch0 = ev.outputBuffer.getChannelData(0);
let ch1 = ev.outputBuffer.getChannelData(1);
-
+
let host = this.host;
let filter = this.filter;
let bias = this.bias;
@@ -432,9 +285,9 @@ class AudioDriver {
let port = [0, 0];
for(let i = 0; i < ch0.length; i++){
-
+
output = host.step();
-
+
port[0] = filter[0].step(output[0]);
port[1] = filter[1].step(output[1]);
@@ -458,7 +311,7 @@ enum PSG49_LUT{
ENV_FINE,
ENV_COARSE,
ENV_SHAPE
-
+
}
class PSG49 {
@@ -481,6 +334,10 @@ class PSG49 {
NOISE_PERIOD: 0x0,
+ // bit position
+ // 5 4 3 2 1 0
+ // NC NB NA TC TB TA
+ // T = Tone, N = Noise
MIXER: 0x0,
A_VOL: 0x0,
@@ -497,7 +354,7 @@ class PSG49 {
constructor(clockRate : number, intRate : number){
this.driver = new AudioDriver(this);
- this.interpolate = [
+ this.interpolate = [
new Interpolator(),
new Interpolator()
];
@@ -508,7 +365,7 @@ class PSG49 {
new FirFilter(FIR, m)
];
this.oversample = m;
-
+
this.clock = {
frequency : clockRate,
scale : 1/16 * 2,
@@ -531,12 +388,12 @@ class PSG49 {
stub : []
} as Envelope;
-
- this.channels = [
+
+ this.channels = [
{
counter : 0x0,
pan : 0.5,
- } as Channel,
+ } as Channel,
{
counter : 0x0,
pan : 0.5
@@ -545,7 +402,7 @@ class PSG49 {
counter : 0x0,
pan : 0.5
} as Channel,
-
+
{counter : 0x0} as Channel
]
@@ -590,23 +447,23 @@ class PSG49 {
stub.reset = (ev : Envelope)=>{
let strobe = ev.strobe;
let transient = ev.transient;
-
+
switch(ev.offset){
- case 0x4:
+ case 0x4:
transient = 0;
- case 0x0:
+ case 0x0:
ev.step = strobe ? transient : 31;
break;
- case 0x5:
+ case 0x5:
transient = 31;
- case 0x1:
+ case 0x1:
ev.step = strobe ? transient : 0;
break;
case 0x2: ev.step = 31;
break;
case 0x3: ev.step = 0;
- break;
+ break;
}
}
@@ -637,10 +494,10 @@ class PSG49 {
[stub.grow, stub.grow],
[stub.decay, stub.grow],
[stub.grow, stub.decay],
-
+
];
}
-
+
clamp(){
let r = this.register;
@@ -649,13 +506,13 @@ class PSG49 {
r.A_COARSE &= 0xf; r.B_COARSE &=0xf;
r.C_COARSE &= 0xf; r.ENV_COARSE &= 0xff;
-
+
r.A_VOL &= 0x1f; r.B_VOL &= 0x1f;
r.C_VOL &= 0x1f;
r.NOISE_PERIOD &= 0x1f; r.MIXER &= 0x3f;
- r.ENV_SHAPE &= 0xff;
-
+ r.ENV_SHAPE &= 0xff;
+
}
map(){
@@ -690,19 +547,19 @@ class PSG49 {
channel[0].envelope = (r.A_VOL & 0x10) ? 0 : 1;
channel[1].envelope = (r.B_VOL & 0x10) ? 0 : 1;
channel[2].envelope = (r.C_VOL & 0x10) ? 0 : 1;
-
+
// update channel noise period
channel[3].period = r.NOISE_PERIOD << 1;
ev.period = r.ENV_FINE | r.ENV_COARSE << 8;
ev.shape = r.ENV_SHAPE;
-
+
switch(ev.shape){
-
+
case 0x0: case 0x1:
case 0x2: case 0x3:
- case 0x9:
+ case 0x9:
ev.transient = 0;
ev.offset = 0;
r.ENV_SHAPE = 0xff;
@@ -723,22 +580,22 @@ class PSG49 {
ev.offset = 1;
r.ENV_SHAPE = 0xff;
break;
- case 0x8:
+ case 0x8:
ev.offset = 2;
break;
- case 0xc:
+ case 0xc:
ev.offset = 3;
break;
- case 0xa:
+ case 0xa:
ev.offset = 4;
break;
- case 0xe:
+ case 0xe:
ev.offset = 5;
- break;
+ break;
}
if(ev.shape != ev.store){
- ev.strobe = 0x0;
+ ev.strobe = 0x0;
ev.counter = 0x0;
ev.stub.reset(ev);
@@ -754,7 +611,7 @@ class PSG49 {
let period = (ch.period == 0x0) ? 0x1 : ch.period;
ch.counter += step;
-
+
if(ch.counter >= period){
// 50% duty cycle
port ^= 0x1;
@@ -770,7 +627,7 @@ class PSG49 {
let step = this.clock.step;
let ev = this.envelope;
-
+
ev.counter += step;
if(ev.counter >= ev.period){
@@ -789,7 +646,7 @@ class PSG49 {
let period = (ch.period == 0) ? 1 : ch.period;
ch.counter += step;
-
+
if(ch.counter >= period){
port ^= (((port & 1) ^ ((port >> 3) & 1)) << 17);
port >>= 1;
@@ -798,7 +655,7 @@ class PSG49 {
}
return ch.port & 1;
}
-
+
step_mixer(){
let port = 0x0;
@@ -809,17 +666,17 @@ class PSG49 {
let step = this.step_envelope();
for(let i = 0; i < 3; i++){
-
+
let volume = ch[i].volume;
let pan = ch[i].pan;
port = this.step_tone(i) | ch[i].tone;
port &= noise | ch[i].noise;
-
+
// todo: add dac volume table
//bit*=toneChannel[i].volume;
// mix each channel
-
+
if(!ch[i].envelope){
index = step;
}else{
@@ -829,7 +686,7 @@ class PSG49 {
port *= this.dac[index];
- // clamp pan levels
+ // clamp pan levels
// distortion over +1 ?
if(pan > 0.9){
@@ -843,12 +700,12 @@ class PSG49 {
output[1] += port * (pan) ;
}
-
+
return output;
}
step(){
-
+
let output = [];
let clockStep = 0;
let intStep = 0;
@@ -901,16 +758,16 @@ class PSG49 {
interpolate[0].step(output[0]);
interpolate[1].step(output[1]);
-
+
}
sample_left[i] = interpolate[0].cubic(0.5);
sample_right[i] = interpolate[1].cubic(0.5);
-
+
}
output[0] = fir[0].step(sample_left);
output[1] = fir[1].step(sample_right);
-
+
return output;
}