├── README.md ├── ym2612.min.js └── ym2612.js /README.md: -------------------------------------------------------------------------------- 1 | **ym2612.js**, *the* definitive Yamaha YM2612 emulation in JavaScript! 2 | 3 | # Preamble 4 | 5 | This is as complete an emulation as possible of the Yamaha YM2612 synthesis chip written entirely in JavaScript. This core is ported entirely from the open-source C-based YM2612 core found in the Wii port of the Genesis Plus emulator, Genesis Plus GX. The original intention was primarily to mockup interaction of a YM2612 core with an implementation of Steinberg's *VST* audio toolkit, but now serves as the YM2612 core of my web-based VGM player. 6 | 7 | * Genesis Plus GX can be found at [http://code.google.com/p/genplus-gx](http://code.google.com/p/genplus-gx) 8 | * VST can be found at [http://www.steinberg.net/en/company/developer.html](http://www.steinberg.net/en/company/developer.html) 9 | * Source distribution of the web VGM player soon to follow! 10 | 11 | # Installation 12 | 13 | 1. Download ym2612.js to a directory that's accessible by your intended webpage or other JavaScript-capable development environment. 14 | 2. Reference the file in your project. 15 | * In HTML, a normal script element on the page itself will do. 16 | * In Sphere, you'll need to RequireScript it. 17 | 3. Follow the usage instructions below! 18 | 19 | # Usage 20 | 21 | First, you'll need to declare a `YM2612` object (a simple `var ym = new YM2612();` or some such will do). Once the object is loaded, you'll need to initialize it using `ym.init(int_clock, int_samplerate)` , where `int_clock` is the chip's native frequency (7670448 is the ideal value here for NTSC) and `int_samplerate` is your output audio's sample-frame rate (44100 is a relatively normal value). Make sure that if you update the frame rate in your app that you call `ym.init` again. You'll then need to `ym.config(9);` 9 being the bit length of PCM data to send to the DAC internally, followed by a `ym.reset();`, then `ym.write(0x28,0x00);` to make sure all the channels are keyed off. 22 | 23 | Assuming your computer did not blow up, we should be ready to feed the chip some data! From this point forward, if you are going to feed data manually, you must know what the chip's registers do. Manually or programmatically, write to registers using `ym.write(byte_addr, byte_data)` , where `byte_reg` is the register to which to write and `byte_data` is the data to write to it. First, turn all your channels' notes off, as well as disable the DAC on channel 6 temporarily, then set the channels' panning and modulation states, then finally set the channels' FM parameters. Now it should be safe to send musical data to the chip. If you are feeding data manually, you may or may not need to write to registers such as timer registers. 24 | 25 | Once you've written your data to the chip and your app wants to advance, get the generated audio using `buffer = ym.update(int_length)` , where `int_length` is the number of sample-frames to write to the `buffer` variable. The results should be an array[2][`int_length`] in size of 16-bit audio samples; you'll likely need to loop through `buffer` to add the samples to existing audio. 26 | 27 | So! Your setup code should look similar to the following… 28 | 29 | ``` 30 | var ym = new YM2612(); 31 | ym.init(7670448, 44100); // call this if the clock and/or output sample rate ever need to change 32 | ym.config(9); 33 | ym.reset(); 34 | ym.write(0x28,0x00); 35 | ``` 36 | 37 | …and your update loop might look like the following… 38 | 39 | ``` 40 | do { 41 | cmd = getByte(); 42 | if (cmd!==UPDATE) { // some control value you define specifying "time to update sound" 43 | data = getByte(); 44 | ym.write(cmd, data); 45 | } 46 | else { 47 | len = getNumberOfSamples(); // some function you define to get a particular length 48 | buffer = ym.update(len); 49 | sendBufferToAudioSink(buffer); // some function you define to send a stereo buffer to Web Audio 50 | } 51 | } while (canFeed); // canFeed is some boolean saying you can still emulate 52 | ``` 53 | 54 | # API 55 |

How to Initialize

56 |
57 |
new YM2612();
58 |
If successful, a new YM2612 object is returned.
59 |
60 |
61 |
void YM2612.init(int_clock, int_samplerate);
62 |
Initialize the chip to a given frequency and sample rate.
63 |
Arguments: `int_clock` is the YM2612's frequency native to the app (7670445 is the ideal value here); `int_samplerate` is the app's sample-frame rate (aka, "sample rate" or "frame rate", where 44100 is a normal value to use here).
64 |
Return: void (none)
65 |
void YM2612.reset()
66 |
Reset the chip; silence all channels and zero all timers.
67 |
Arguments: none
68 |
Return: void (none)
69 |
void YM2612.config(dac_bits)
70 |
Configure the chip's DAC precision for PCM data.
71 |
Arguments: `dac_bits` is the number of bits of DAC precision to use.
72 |
Return: void (none)
73 |
void YM2612.write(byte_addr, byte_data)
74 |
Write data to a chip register.
75 |
Arguments: `byte_addr` is the address of the register to which to write; `byte_data` is the data to write to that register.
76 |
Return: void (none)
77 |
int YM2612.read()
78 |
Read the chip's current status; likely more useful in a more full-featured emulator.
79 |
Arguments: none
80 |
Return: int status_byte
81 |
Array YM2612.update(int_length)
82 |
Generate audio data from the chip.
83 |
Arguments: `int_length` is the amount of frames of stereo audio data to generate.
84 |
Return: Array[2][`int_length`] audio_buffer
85 |
Object YM2612.getContext()
86 |
TODO
87 |
Arguments: TODO
88 |
Return: TODO
89 |
int YM2612.getContextSize()
90 |
TODO
91 |
Arguments: TODO
92 |
Return: void (none)
93 |
void YM2612.restore(bytearray_buffer)
94 |
TODO
95 |
Arguments: TODO
96 |
Return: void (none)
97 |
void YM2612.load(bytearray_buffer)
98 |
TODO
99 |
Arguments: TODO
100 |
Return: void (none)
101 |
void YM2612.save(obj_state)
102 |
TODO
103 |
Arguments: TODO
104 |
Return: void (none)
105 |
106 | 107 | # Disclaimer 108 | 109 | [Genesis Plus](http://code.google.com/p/genplus-gx) is an open-source multi-platform emulator of the Sega Genesis/Mega Drive. I very much enjoy Genesis games and am a big fan of the audio output by the console, as well as FM synthesis in general, hence this project. The two goals of this project are to create a stable YM2612 library in JavaScript to use in web-based players and synthesizers, and to design a usable YM2612 VST instrument for DAWs that support the VST format, as well as an Audio Unit for those OS X-based DAWs that support CoreAudio. 110 | 111 | # License 112 | 113 | ym2612.js is provided under an MIT license. 114 | -------------------------------------------------------------------------------- /ym2612.min.js: -------------------------------------------------------------------------------- 1 | function YM2612(){if(!this instanceof YM2612)return new YM2612;this.version=257;this.start=0;this.count=0;this.chip=null}(function(e){"use strict";function c(){function e(){this.ar=0;this.d1r=0;this.d2r=0;this.rr=0;this.ksr=0;this.mul=1;this.init=function(){this.ar=0;this.d1r=0;this.d2r=0;this.rr=0;this.ksr=0;this.mul=1};this.toString=function(){return["MUL="+this.mul,"KS="+this.ksr,"AR="+this.ar,"D1R="+this.d1r,"D2R="+this.d2r,"RR="+this.rr].join(",")}}function t(){this.ar=0;this.d1r=0;this.d2r=0;this.rr=0;this.init=function(){this.ar=0;this.d1r=0;this.d2r=0;this.rr=0}}this.DT=-1;this.KSR=0;this.rate=new e;this.phase=0;this.Incr=0;this.state=0;this.tl=0;this.volume=0;this.sl=0;this.vol_out=0;this.eg={sh:new t,sel:new t,init:function(){this.sh.init();this.sel.init()}};this.ssg=0;this.ssgn=0;this.key=0;this.AMmask=0;this.reset=function(){this.Incr=-1;this.key=0;this.phase=0;this.ssgn=0;this.state=i.OFF;this.volume=r.MAX_ATT_INDEX;this.vol_out=r.MAX_ATT_INDEX;this.out[0]=0,this.out[1]=0};this.debug={dt1mul:0,dt1:0,mul:0,tl:0,ksar:0,ks:0,ar:0,amd1r:0,am:0,d1r:0,d2r:0,slrr:0,sl:0,rr:0};this.debug.toString=function(){return["TL:"+this.tl,"DT1:"+this.dt1,"MUL:"+this.mul,"KS:"+this.ks,"AR:"+this.ar,"AM:"+this.am,"D1R:"+this.d1r,"D2R:"+this.d2r,"SL:"+this.sl,"RR:"+this.rr].join(",")};this.out=[0,0];this.toString=function(){return"OP{"+this.debug.toString()+"}"}}function h(){this.SLOT=[new c,new c,new c,new c];this.ALGO=0;this.FB=0;this.op1_out=[0,0];this.connect=["x","x","x","x"];this.mem={connect:"mem",value:0};this.pms=0;this.ams=0;this.fc=0;this.kcode=0;this.block_fnum=0;this.fn_h=0;this.outputs={m1:0,m2:0,c1:0,c2:0,mem:0,x:0,out:0};this.canCSM=0;this.canDAC=0;this.muted=0;this.pan=[0,0];this.reset=function(){this.mem.value=0,this.op1_out[0]=0,this.op1_out[1]=0;var e=this.SLOT.length;while(--e>-1)this.SLOT[e].reset()};this.toString=function(){return"CH{"+["ALGO:"+this.ALGO,"FB:"+this.FB,"PMS:"+this.pms,"AMS:"+this.ams,"SLOTS["+this.SLOT.join(",")+"]"].join(",")+"}"}}function p(e,t){this.address=0;this.status=0;this.mode=0;this.timer_base=1;this.TA=0;this.TAL=0;this.TAC=0;this.TB=0;this.TBL=0;this.TBC=0;this.dt_tab=[new Array(32),new Array(32),new Array(32),new Array(32),new Array(32),new Array(32),new Array(32),new Array(32)];this.clock=e||7670448;this.rate=t||44100}function d(){this.fc=[0,0,0];this.fn_h=0;this.kcode=[0,0,0];this.block_fnum=[0,0,0];this.key_csm=0}function v(e,t){function n(){this.cnt=0;this.timer=0;this.timer_add=0;this.timer_overflow=0;this.init=function(){this.cnt=0;this.timer=0;this.timer_add=0;this.timer_overflow=0}}this.ST=new p(e,t);this.SL3=new d;this.eg=new n;this.lfo=new n;this.lfo.AM=0;this.lfo.PM=0;this.fn={table:new Array(4096),max:0}}function m(e,t){this.CH=[new h,new h,new h,new h,new h,new h];this.CH[2].canCSM=1;this.CH[5].canDAC=1;this.dacen=0;this.dacout=0;this.OPN=new v(e,t);this.toString=function(){return"YM[\n"+this.CH.join(",\n")+"\n]"}}function g(e){if((e.OPN.ST.mode&1)>0){if(t.mode)e.OPN.ST.TAC-=e.OPN.ST.timer_base;else--e.OPN.ST.TAC;if(e.OPN.ST.TAC<=0){if((e.OPN.ST.mode&4)>0)e.OPN.ST.status|=1;if(t.mode&&e.OPN.ST.TAL)e.OPN.ST.TAC+=e.OPN.ST.TAL;else e.OPN.ST.TAC=e.OPN.ST.TAL;if((e.OPN.ST.mode&192)===128)e.CH[2].keyControlCSM()}}}function y(e,n){if((e.OPN.ST.mode&2)>0){if(t.mode)e.OPN.ST.TBC-=e.OPN.ST.timer_base*n;else e.OPN.ST.TBC-=n;if(e.OPN.ST.TBC<=0){if((e.OPN.ST.mode&8)>0)e.OPN.ST.status|=2;if(e.OPN.ST.TBL)e.OPN.ST.TBC+=e.OPN.ST.TBL;else e.OPN.ST.TBC=e.OPN.ST.TBL}}}function b(e,t){if(((e.OPN.ST.mode^t)&192)>0){e.CH[2].SLOT[l[0]].Incr=-1;if((t&192)!==128&&e.OPN.SL3.key_csm){e.CH[2].keyOffCSM(l[0]);e.CH[2].keyOffCSM(l[1]);e.CH[2].keyOffCSM(l[2]);e.CH[2].keyOffCSM(l[3]);e.OPN.SL3.key_csm=0}}if(t&1&&!(e.OPN.ST.mode&1))e.OPN.ST.TAC=e.OPN.ST.TAL;if(t&2&&!(e.OPN.ST.mode&2))e.OPN.ST.TBC=e.OPN.ST.TBL;e.OPN.ST.status&=~t>>4;e.OPN.ST.mode=t}function w(e){var n;if(t.mode)n=function(e){while(e.lfo.timer>=e.lfo.timer_overflow){e.lfo.timer-=e.lfo.timer_overflow;e.lfo.cnt=e.lfo.cnt+1&127;if(e.lfo.cnt<64)e.lfo.AM=(e.lfo.cnt^63)<<1;else e.lfo.AM=(e.lfo.cnt&63)<<1;e.lfo.PM=e.lfo.cnt>>2}};else n=function(e){if(e.lfo.timer>e.lfo.timer_overflow){e.lfo.timer=0;e.lfo.cnt=e.lfo.cnt+1&127;if(e.lfo.cnt<64)e.lfo.AM=(e.lfo.cnt^63)<<1;else e.lfo.AM=(e.lfo.cnt&63)<<1;e.lfo.PM=e.lfo.cnt>>2}};if(e.OPN.lfo.timer_overflow){if(t.mode)e.OPN.lfo.timer+=e.OPN.lfo.timer_add;else++e.OPN.lfo.timer;n(e.OPN)}}function E(e,t){var n=e.CH.length;while(--n>-1)e.CH[n].advance_eg(t)}function S(e){var t=e.CH.length;while(--t>-1)e.CH[t].update_ssg_eg()}function T(e,t){if(t>e.CH.length)t=e.CH.length;while(--t>-1)e.CH[t].reset()}function N(e){if(t.debug)console.log("init_tables",e.CH.length);var i,s,a;var l;var c,h;var p,d;var v=Math.PI,m=Math.log,g=Math.pow,y=Math.sin;var b=r.STEP/32,w=1<<16,E=u.RES_LEN<<1;for(a=0;a>4;if(l&1)l=(l>>1)+1;else l=l>>1;l<<=2;d=a<<1;u.tab[d+0]=l;u.tab[d+1]=-l;for(s=1;s<13;++s){p=d+0+s*E|0;u.tab[p]=u.tab[d]>>s;u.tab[p+1]=-u.tab[p]}}p=v/o.LEN,d=8/m(2),b=2*4/r.STEP;for(s=0;s0)c=m(1/h)*d;else c=m(-1/h)*d;l=c*b|0;if(l&1)l=(l>>1)+1;else l=l>>1;n.sin[s]=(l<<1)+(h>=0?0:1)}for(s=0;s<8;++s){for(l=0;l<128;++l){for(a=0;a<8;++a){d=0;for(c=0;c<7;++c){if((l&1<0){d+=f.pm_output[(c<<3)+s][a]}}i=(l<<8)+(s<<5);f.pm_table[i+a+0]=d;f.pm_table[i+(a^7)+8]=d;f.pm_table[i+a+16]=-d;f.pm_table[i+(a^7)+24]=-d}}}}var t={hq_fm:0,dac_bits:8,maxcalc:0,debug:0,debugLocal:0,debugArr:[],mode:0,strict:0};var n={FREQ_SH:16,EG_SH:16,LFO_SH:24,TIMER_SH:16};n.FREQ_MASK=(1<>3;n.sin=new Array(o.LEN);n.sl=function(){var e=function(e){return e*4/r.STEP|0};return[e(0),e(1),e(2),e(3),e(4),e(5),e(6),e(7),e(8),e(9),e(10),e(11),e(12),e(13),e(14),e(31)]}();i.RATE_STEPS=8;i.inc=[0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,2,1,2,1,2,1,2,1,2,1,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,4,2,4,2,4,2,4,2,4,2,4,4,4,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,8,4,4,4,8,4,8,4,8,4,8,4,8,4,8,8,8,4,8,8,8,8,8,8,8,8,8,8,8,16,16,16,16,16,16,16,16,0,0,0,0,0,0,0,0];i.rate_select=function(){var e=function(e){return e*i.RATE_STEPS|0};return[e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(18),e(0),e(0),e(0),e(0),e(2),e(2),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(0),e(1),e(2),e(3),e(4),e(5),e(6),e(7),e(8),e(9),e(10),e(11),e(12),e(13),e(14),e(15),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16),e(16)]}();i.rate_shift=function(){var e=function(e){return e|0};return[e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(11),e(10),e(10),e(10),e(10),e(9),e(9),e(9),e(9),e(8),e(8),e(8),e(8),e(7),e(7),e(7),e(7),e(6),e(6),e(6),e(6),e(5),e(5),e(5),e(5),e(4),e(4),e(4),e(4),e(3),e(3),e(3),e(3),e(2),e(2),e(2),e(2),e(1),e(1),e(1),e(1),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0),e(0)]}();s.tab=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,8,8,1,1,1,1,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,16,16,16,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,16,17,19,20,22,22,22,22];var a={fktable:[0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3]};var f={samples_per_step:[108,77,71,67,62,44,8,5],ams_depth_shift:[8,3,1,0],pm_output:[[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,1,1,1,1],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,1,1,1,1],[0,0,1,1,2,2,2,3],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,1,1,1,1],[0,0,1,1,2,2,2,3],[0,0,2,3,4,4,5,6],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,1,1],[0,0,0,0,1,1,1,1],[0,0,0,1,1,1,1,2],[0,0,1,1,2,2,2,3],[0,0,2,3,4,4,5,6],[0,0,4,6,8,8,10,12],[0,0,0,0,0,0,0,0],[0,0,0,0,1,1,1,1],[0,0,0,1,1,1,2,2],[0,0,1,1,2,2,3,3],[0,0,1,2,2,2,3,4],[0,0,2,3,4,4,5,6],[0,0,4,6,8,8,10,12],[0,0,8,12,16,16,20,24],[0,0,0,0,0,0,0,0],[0,0,0,0,2,2,2,2],[0,0,0,2,2,2,4,4],[0,0,2,2,4,4,6,6],[0,0,2,4,4,4,6,8],[0,0,4,6,8,8,10,12],[0,0,8,12,16,16,20,24],[0,0,16,24,32,32,40,48],[0,0,0,0,0,0,0,0],[0,0,0,0,4,4,4,4],[0,0,0,4,4,4,8,8],[0,0,4,4,8,8,12,12],[0,0,4,8,8,8,12,16],[0,0,8,12,16,16,20,24],[0,0,16,24,32,32,40,48],[0,0,32,48,64,64,80,96]],pm_table:new Array(128*8*32)};a.CHAN=function(e){return e&3};a.SLOT=function(e){return e>>2&3};var l=[0,2,1,3];n.bitmask=0;c.prototype.keyOn=function(e,t){if(!this.key&&!e.OPN.SL3.key_csm){this.phase=0;this.ssgn=0;if(this.rate.ar+this.rate.ksr<94)this.state=this.volume<=r.MIN_ATT_INDEX?this.sl===r.MIN_ATT_INDEX?i.SUS:i.DEC:i.ATT;else{this.volume=r.MIN_ATT_INDEX;this.state=this.sl===r.MIN_ATT_INDEX?i.SUS:i.DEC}if((this.ssg&8)>0&&(this.ssgn^this.ssg&4)>0)this.vol_out=this.tl+(512-this.volume&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)}if(!t)this.key=1};h.prototype.keyOn=function(e,t){this.SLOT[t].keyOn(e,0)};c.prototype.keyOff=function(e,t){if(t&&!this.key||!t&&this.key&&!e.OPN.SL3.key_csm){if(this.state>i.REL){this.state=i.REL;if((this.ssg&8)>0){if((this.ssgn^this.ssg&4)>0)this.volume=512-this.volume|0;if(this.volume>=512){this.volume=r.MAX_ATT_INDEX;this.state=i.OFF}this.vol_out=this.tl+(this.volume|0)}}}if(!t)this.key=0};h.prototype.keyOff=function(e,t){this.SLOT[t].keyOff(e,0)};h.prototype.keyOnCSM=function(e,t){this.SLOT[t].keyOn(e,1)};h.prototype.keyOffCSM=function(e,t){this.SLOT[t].keyOff(e,1)};h.prototype.keyControlCSM=function(e){this.keyOnCSM(l[0]);this.keyOnCSM(l[1]);this.keyOnCSM(l[2]);this.keyOnCSM(l[3]);e.OPN.SL3.key_csm=1};h.prototype.setupConnection=function(){var e="out";var n={m1:0,m2:2,c1:1,c2:3},r=this.mem.connect;switch(this.ALGO){case 0:this.connect[n.m1]="c1";this.connect[n.c1]="mem";this.connect[n.m2]="c2";this.mem.connect="m2";break;case 1:this.connect[n.m1]="mem";this.connect[n.c1]="mem";this.connect[n.m2]="c2";this.mem.connect="m2";break;case 2:this.connect[n.m1]="c2";this.connect[n.c1]="mem";this.connect[n.m2]="c2";this.mem.connect="m2";break;case 3:this.connect[n.m1]="c1";this.connect[n.c1]="mem";this.connect[n.m2]="c2";this.mem.connect="c2";break;case 4:this.connect[n.m1]="c1";this.connect[n.c1]=e;this.connect[n.m2]="c2";this.mem.connect="mem";break;case 5:this.connect[n.m1]="x";this.connect[n.c1]=e;this.connect[n.m2]=e;this.mem.connect="m2";break;case 6:this.connect[n.m1]="c1";this.connect[n.c1]=e;this.connect[n.m2]=e;this.mem.connect="mem";break;case 7:this.connect[n.m1]=e;this.connect[n.c1]=e;this.connect[n.m2]=e;this.mem.connect="mem";break;default:if(t.strict)throw new Error("CH::setup_connection - unsupported algorithm ("+this.ALGO+")");else break}this.connect[3]=e};c.prototype.set_det_mul=function(e,t){this.rate.mul=(t&15)>0?(t&15)<<1:1;this.DT=t>>4&7;this.debug.dt1mul=t&255;this.debug.dt1=this.DT;this.debug.mul=t&15};h.prototype.set_det_mul=function(e,t,n){this.SLOT[t].set_det_mul(e,n);this.SLOT[l[0]].Incr=-1};c.prototype.set_tl=function(e){this.debug.tl=e&127;this.tl=this.debug.tl<0&&(this.ssgn^this.ssg&4)>0&&this.state>i.REL)this.vol_out=this.tl+((512-this.volume|0)&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)};h.prototype.set_tl=function(e,t){this.SLOT[e].set_tl(t)};c.prototype.set_ar_ksr=function(e){this.debug.ksar=e&255;this.debug.ks=e>>6;this.debug.ar=e&31;var t=this.KSR|0;this.rate.ar=this.debug.ar>0?32+(this.debug.ar<<1):0;this.KSR=3-this.debug.ks;if(this.rate.ar+this.rate.ksr<94){var n=this.rate.ar+this.rate.ksr|0;this.eg.sh.ar=i.rate_shift[n];this.eg.sel.ar=i.rate_select[n]}else{this.eg.sh.ar=0;this.eg.sel.ar=18*i.RATE_STEPS}return this.KSR!==t};h.prototype.set_ar_ksr=function(e,t){if(this.SLOT[e].set_ar_ksr(t))this.SLOT[l[0]].Incr=-1};c.prototype.set_dr=function(e){this.debug.amd1r=e&255;this.debug.am=e&128;this.debug.d1r=e&31;this.rate.d1r=this.debug.d1r>0?32+(this.debug.d1r<<1):0;var t=this.rate.d1r+this.rate.ksr|0;this.eg.sh.d1r=i.rate_shift[t];this.eg.sel.d1r=i.rate_select[t]};h.prototype.set_dr=function(e,t){this.SLOT[e].set_dr(t)};c.prototype.set_sr=function(e){this.debug.d2r=e&31;this.rate.d2r=this.debug.d2r>0?32+(this.debug.d2r<<1):0;var t=this.rate.d2r+this.rate.ksr|0;this.eg.sh.d2r=i.rate_shift[t];this.eg.sel.d2r=i.rate_select[t]};h.prototype.set_sr=function(e,t){this.SLOT[e].set_sr(t)};c.prototype.set_sl_rr=function(e){this.debug.slrr=e&255;this.debug.sl=e>>4&15;this.debug.rr=e&15;this.sl=n.sl[this.debug.sl];if(this.state===i.DEC&&this.volume>=(this.sl|0))this.state=i.SUS;this.rate.rr=34+(this.debug.rr<<2);var t=this.rate.rr+this.rate.ksr|0;this.eg.sh.rr=i.rate_shift[t];this.eg.sel.rr=i.rate_select[t]};h.prototype.set_sl_rr=function(e,t){this.SLOT[e].set_sl_rr(t)};c.prototype.advance_eg=function(e){switch(this.state){case i.ATT:if(!(e&(1<>this.eg.sh.ar&7)]>>4;if(this.volume<=r.MIN_ATT_INDEX){this.volume=r.MIN_ATT_INDEX;this.state=this.sl===r.MIN_ATT_INDEX?i.SUS:i.DEC}if((this.ssg&8)>0&&(this.ssgn^this.ssg&4)>0)this.vol_out=this.tl+((512-this.volume|0)&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)}break;case i.DEC:if(!(e&(1<0){if(this.volume<512){this.volume+=i.inc[this.eg.sel.d1r+(e>>this.eg.sh.d1r&7)]<<2;if((this.ssgn^this.ssg&4)>0)this.vol_out=this.tl+((512-this.volume|0)&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)}}else{this.volume+=i.inc[this.eg.sel.d1r+(e>>this.eg.sh.d1r&7)];this.vol_out=this.tl+(this.volume|0)}if(this.volume>=(this.sl|0))this.state=i.SUS}break;case i.SUS:if(!(e&(1<0){if(this.volume<512){this.volume+=i.inc[this.eg.sel.d2r+(e>>this.eg.sh.d2r&7)]<<2;if((this.ssgn^this.ssg&4)>0)this.vol_out=this.tl+((512-this.volume|0)&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)}}else{this.volume+=i.inc[this.eg.sel.d2r+(e>>this.eg.sh.d2r&7)];if(this.volume>=r.MAX_ATT_INDEX)this.volume=r.MAX_ATT_INDEX;this.vol_out=this.tl+(this.volume|0)}}break;case i.REL:if(!(e&(1<0){if(this.volume<512){this.volume+=i.inc[this.eg.sel.rr+(e>>this.eg.sh.rr&7)]<<2;if(this.volume>=512){this.volume=r.MAX_ATT_INDEX;this.state=i.OFF}}}else{this.volume+=i.inc[this.eg.sel.rr+(e>>this.eg.sh.rr&7)];if(this.volume>=r.MAX_ATT_INDEX){this.volume=r.MAX_ATT_INDEX;this.state=i.OFF}}this.vol_out=this.tl+(this.volume|0)}break;default:if(t.strict)throw new Error("FM_SLOT::advance_eg - unsupported state ("+this.state+")");else break}};h.prototype.advance_eg=function(e){var t=this.SLOT.length;while(--t>-1)this.SLOT[t].advance_eg(e)};c.prototype.update_ssg_eg=function(){if((this.ssg&8)>0&&this.volume>=512&&this.state>i.REL){if((this.ssg&1)>0){if((this.ssg&2)>0)this.ssgn=4;if(this.state!==i.ATT&&!(this.ssgn^this.ssg&4))this.volume=r.MAX_ATT_INDEX}else{if((this.ssg&2)>0)this.ssgn^=4;else this.phase=0;if(this.state!==i.ATT){if(this.rate.ar+this.rate.ksr<94)this.state=this.volume<=r.MIN_ATT_INDEX?this.sl===r.MIN_ATT_INDEX?i.SUS:i.DEC:i.ATT;else{this.volume=r.MIN_ATT_INDEX;this.state=this.sl===r.MIN_ATT_INDEX?i.SUS:i.DEC}}}if((this.ssgn^this.ssg&4)>0)this.vol_out=this.tl+((512-this.volume|0)&r.MAX_ATT_INDEX);else this.vol_out=this.tl+(this.volume|0)}};h.prototype.update_ssg_eg=function(){var e=this.SLOT.length;while(--e>-1)this.SLOT[e].update_ssg_eg()};c.prototype.update_phase_lfo=function(e,n,r){var i=f.pm_table[((r&2032)>>4<<8)+n+e.OPN.lfo.PM];if(i){if(this.DT<0){console.log("FM_SLOT::update_phase_lfo - invalid DT",this.DT);if(t.strict)throw new Error("FM_SLOT::update_phase_lfo - invalid DT="+this.DT);else return}var o,u,l;r=i+(r<<1);o=(r&28672)>>12;r=r&4095;u=o<<2|a.fktable[r>>8];if(t.mode){l=(e.OPN.fn.table[r]>>7-o)+e.OPN.ST.dt_tab[this.DT][u];if(l<0)l+=e.OPN.fn.max}else l=(r<<5>>7-o)+e.OPN.ST.dt_tab[this.DT][u]&s.MASK;this.phase+=l*this.rate.mul>>1}else this.phase+=this.Incr};c.prototype.update_phase_lfo_precalc=function(e,n,r){if(n!==-1){var i;if(t.mode){i=n+e.OPN.ST.dt_tab[this.DT][r];if(i<0)i+=e.OPN.fn.max}else i=n+e.OPN.ST.dt_tab[this.DT][r]&s.MASK;this.phase+=i*this.rate.mul>>1}else this.phase+=this.Incr};h.prototype.update_phase_lfo=function(e){var n=this.pms,r=this.block_fnum;var i,s,o;var u=f.pm_table[((r&2032)>>4<<8)+n+e.OPN.lfo.PM];if(u){r=u+(r<<1);i=(r&28672)>>12;r=r&4095;s=i<<2|a.fktable[r>>8];if(t.mode){o=e.OPN.fn.table[r]>>7-i}else o=r<<5>>7-i}else{o=-1}var c=this.SLOT.length;while(--c>-1)this.SLOT[l[c]].update_phase_lfo_precalc(e,o,s)};c.prototype.refresh_fc_eg=function(e,n,r){if(this.DT<0){console.log("FM_SLOT::refresh_fc_eg - invalid DT",this.DT);if(t.strict)throw new Error("FM_SLOT::refresh_fc_eg - invalid DT="+this.DT);else return}if(t.debug>1)console.log("OPN.ST.dt_tab["+this.DT+"]["+r+"]",e.OPN.ST.dt_tab[this.DT][r]);n+=e.OPN.ST.dt_tab[this.DT][r];if(t.mode){if(n<0)n+=e.OPN.fn.max}else n&=s.MASK;this.Incr=n*this.rate.mul>>1;r=r>>this.KSR;if(this.rate.ksr!==r){this.rate.ksr=r;var o=this.rate.ar+r|0;if(o<94){this.eg.sh.ar=i.rate_shift[o];this.eg.sel.ar=i.rate_select[o]}else{this.eg.sh.ar=0;this.eg.sel.ar=18*i.RATE_STEPS}o=this.rate.d1r+r|0;this.eg.sh.d1r=i.rate_shift[o];this.eg.sel.d1r=i.rate_select[o];o=this.rate.d2r+r|0;this.eg.sh.d2r=i.rate_shift[o];this.eg.sel.d2r=i.rate_select[o];o=this.rate.rr+r|0;this.eg.sh.rr=i.rate_shift[o];this.eg.sel.rr=i.rate_select[o]}};h.prototype.refresh_fc_eg=function(e){if(this.SLOT[l[0]].Incr===-1){var n=this.fc|0,r=this.kcode|0;if(t.debug>1)console.log("FM_CH::refresh_fc_eg",n,r);var i=this.SLOT.length;while(--i>-1)this.SLOT[l[i]].refresh_fc_eg(e,n,r)}};c.prototype.calcVol=function(e){return this.vol_out+(e&this.AMmask)|0};var x;if(t.mode)x=function(e,t,r,i){var s=(t<<3)+n.sin[(e&~n.FREQ_MASK)+(i?r:r<<15)>>n.FREQ_SH&o.MASK];if(s>=u.TAB_LEN)return 0;return u.tab[s]};else x=function(e,t,r,i){var s=(t<<3)+n.sin[(i?e+r>>o.BITS:(e>>o.BITS)+(r>>1))&o.MASK];if(s>=u.TAB_LEN)return 0;return u.tab[s]};c.prototype.calculate=function(e,t,n){var i=this.vol_out+(t&this.AMmask)|0,s=0;if(n){var o=this.out[0]+this.out[1]|0;this.out[0]=this.out[1]|0;s=this.out[0]|0;if(i1&&t.maxcalc>0;var i=e.OPN.lfo.AM>>this.ams;var s,o;var u,a=["x","c1","m2","c2"];this.outputs.m2=0,this.outputs.c1=0,this.outputs.c2=0,this.outputs.mem=0;this.outputs.x=0;this.outputs[this.mem.connect]=this.mem.value;u=0;o=this.SLOT[l[u]].calculate(this.FB,i,1);if(o!==0){if(this.connect[u]==="x")this.outputs.x=o,this.outputs.mem=o,this.outputs.c1=o,this.outputs.c2=o;else this.outputs[this.connect[u]]+=o}u=2;o=this.SLOT[l[u]].calculate(this.outputs[a[u]],i,0);if(o!==0){if(this.connect[u]==="x"){}else this.outputs[this.connect[u]]+=o}u=1;o=this.SLOT[l[u]].calculate(this.outputs[a[u]],i,0);if(o!==0){if(this.connect[u]==="x"){}else this.outputs[this.connect[u]]+=o}u=3;o=this.SLOT[l[u]].calculate(this.outputs[a[u]],i,0);if(o!==0){if(this.connect[u]==="x"){}else this.outputs[this.connect[u]]+=o}this.mem.value=this.outputs.mem|0;if(this.pms){if((e.OPN.ST.mode&192)>0&&this.canCSM){this.SLOT[l[0]].update_phase_lfo(e,this.pms,e.OPN.SL3.block_fnum[1]);this.SLOT[l[1]].update_phase_lfo(e,this.pms,e.OPN.SL3.block_fnum[2]);this.SLOT[l[2]].update_phase_lfo(e,this.pms,e.OPN.SL3.block_fnum[0]);this.SLOT[l[3]].update_phase_lfo(e,this.pms,this.block_fnum)}else this.update_phase_lfo(e)}else{this.SLOT[l[0]].phase+=this.SLOT[l[0]].Incr;this.SLOT[l[1]].phase+=this.SLOT[l[1]].Incr;this.SLOT[l[2]].phase+=this.SLOT[l[2]].Incr;this.SLOT[l[3]].phase+=this.SLOT[l[3]].Incr}if(r)n+="; m2="+this.outputs.m2+";c1="+this.outputs.c1+";c2="+this.outputs.c2+";out="+this.outputs.out,console.log("FM_CH::calc",this.ALGO,n),--t.maxcalc};a.WriteMode=function(e,r,i){i=i&255;switch(r){case 33:break;case 34:if(i&8){if(t.mode)e.OPN.lfo.timer_overflow=f.samples_per_step[i&7]<=3){if(t.strict)throw new Error("OPN_Write - unsupported channel "+u+" or slot "+c+" from {$"+s.toString(16)+",$"+o.toString(16)+"}");else return}if(s>=256)u+=3;var h=l[c];switch(s&240){case 48:e.CH[u].set_det_mul(e,c,o);break;case 64:e.CH[u].set_tl(c,o);break;case 80:e.CH[u].set_ar_ksr(c,o);break;case 96:e.CH[u].set_dr(c,o);e.CH[u].SLOT[c].AMmask=o&128?~0:0;break;case 112:e.CH[u].set_sr(c,o);break;case 128:e.CH[u].set_sl_rr(c,o);break;case 144:(function(e){e.ssg=o&15;if(e.state>i.REL){if((e.ssg&8)>0&&(e.ssgn^e.ssg&4)>0)e.vol_out=e.tl+((512-e.volume|0)&r.MAX_ATT_INDEX);else e.vol_out=e.tl+(e.volume|0)}})(e.CH[u].SLOT[c]);break;case 160:var p,d;switch(c){case 0:p=((e.CH[u].fn_h&7)<<8)+o;d=e.CH[u].fn_h>>3&255;e.CH[u].kcode=d<<2|a.fktable[p>>7];if(t.mode)e.CH[u].fc=e.OPN.fn.table[p<<1]>>7-d;else e.CH[u].fc=p<<6>>7-d;e.CH[u].block_fnum=d<<11|p;e.CH[u].SLOT[l[0]].Incr=-1;if(t.debug>2)console.log("block_fnum=x",e.CH[u].block_fnum.toString(16)," kcode=",e.CH[u].kcode.toString(16)," fc=",e.CH[u].fc.toString(16));break;case 1:e.CH[u].fn_h=o&63|0;break;case 2:if(s<256){p=((e.OPN.SL3.fn_h&7)<<8)+o;d=e.OPN.SL3.fn_h>>3;e.OPN.SL3.kcode[u]=d<<2|a.fktable[p>>7];if(t.mode)e.OPN.SL3.fc[u]=e.OPN.fn.table[p<<1]>>7-d;else e.OPN.SL3.fc[u]=p<<6>>7-d;e.OPN.SL3.block_fnum[u]=d<<11|p;e.CH[2].SLOT[l[0]].Incr=-1}break;case 3:if(s<256)e.OPN.SL3.fn_h=o&63;break}break;case 176:switch(c){case 0:var v=o>>3&7;e.CH[u].ALGO=o&7;if(t.mode)e.CH[u].FB=v?v+6:0;else e.CH[u].FB=v;e.CH[u].setupConnection();break;case 1:e.CH[u].pms=(o&7)<<5;e.CH[u].ams=f.ams_depth_shift[o>>4&3];e.CH[u].pan[0]=o&128?n.bitmask:0;e.CH[u].pan[1]=o&64?n.bitmask:0;break}break}};a.SetPrescaler=function(e,r){e.ratio=r||144;e.OPN.ST.scale=e.OPN.ST.clock/e.OPN.ST.rate/e.ratio;if(t.debug)console.log("init_timetables",e.OPN.ST.clock,e.OPN.ST.rate,e.ratio,e.OPN.ST.scale);var i,o,u;var a=e.OPN.ST.scale*(1<2)console.log("init_timetables dt_tab",ym.OPN.ST.dt_tab);u=32*a,o=4096;while(--o>-1){e.OPN.fn.table[o]=o*u|0}e.OPN.fn.max=131072*a|0;e.OPN.eg.timer_add=e.OPN.ST.scale*(1<=180){if((r&3)!==3)a.WriteReg(e,r,192),a.WriteReg(e,r|256,192);--r}r=178;while(r>=30){if((r&3)!==3)a.WriteReg(e,r,0),a.WriteReg(e,r|256,0);--r}})(this.chip);this.start=0;this.count=0};e.prototype.write=function(e,n){if(t.debug>1)console.log("OPN::write",e.toString(16),n.toString(16));n&=255;this.chip.OPN.ST.address=e&511;var r=this.chip.OPN.ST.address;switch(r&496){case 32:switch(r){case 42:this.chip.dacout=(n-128|0)<<6;break;case 43:this.chip.dacen=!!(n&128);break;default:a.WriteMode(this.chip,r,n);break}break;default:a.WriteReg(this.chip,r,n);break}};e.prototype.read=function(e){return this.chip.OPN.ST.status&255};e.prototype.update=function(e){var n=e*this.ratio;if(t.debug)console.log("==== YM::update","samples="+e,"cycles="+n);var r=[[],[]],i,s,o;var u=!!(this.chip.OPN.ST.mode&192),a;var f=-1;while(++f8192)this.chip.CH[i].outputs.out=8192;else if(this.chip.CH[i].outputs.out<-8192)this.chip.CH[i].outputs.out=-8192;if(!this.chip.CH[i].muted)s+=this.chip.CH[i].outputs.out&this.chip.CH[i].pan[0]|0,o+=this.chip.CH[i].outputs.out&this.chip.CH[i].pan[1]|0;if(a&&this.chip.CH[i].canCSM){this.chip.CH[i].keyOffCSM(this.chip,l[0]);this.chip.CH[i].keyOffCSM(this.chip,l[1]);this.chip.CH[i].keyOffCSM(this.chip,l[2]);this.chip.CH[i].keyOffCSM(this.chip,l[3])}}w(this.chip);if(t.mode){this.chip.OPN.eg.timer+=this.chip.OPN.eg.timer_add;while(this.chip.OPN.eg.timer>=this.chip.OPN.eg.timer_overflow){this.chip.OPN.eg.timer-=this.chip.OPN.eg.timer_overflow;++this.chip.OPN.eg.cnt;E(this.chip,this.chip.OPN.eg.cnt)}}else{++this.chip.OPN.eg.timer;if(this.chip.OPN.eg.timer>=3){this.chip.OPN.eg.timer=0;++this.chip.OPN.eg.cnt;E(this.chip,this.chip.OPN.eg.cnt)}}r[0][f]=s;r[1][f]=o;this.chip.OPN.SL3.key_csm<<=1;g(this.chip);if(a){this.chip.OPN.SL3.key_csm=0}}y(this.chip,e);if(t.debugArr.length>0)console.log(t.debugArr.join(", "));return r};e.prototype.config=function(e){n.bitmask=~((1<-1){if(this.chip.CH[t].pan[0])this.chip.CH[t].pan[0]=n.bitmask;if(this.chip.CH[t].pan[1])this.chip.CH[t].pan[1]=n.bitmask}};e.prototype.toggle=function(e,t){if(e<6)this.chip.CH[e].muted=!t};e.prototype.toString=function(){return this.chip.toString()};e.prototype.load=function(e){};e.prototype.save=function(e){}})(YM2612); 2 | -------------------------------------------------------------------------------- /ym2612.js: -------------------------------------------------------------------------------- 1 | function YM2612() { 2 | if (!this instanceof YM2612) return new YM2612(); 3 | this.version = 0x101; 4 | this.start = 0; 5 | this.count = 0; 6 | this.chip = null; 7 | } 8 | 9 | (function(Y){ 10 | "use strict"; 11 | 12 | /**** CONFIG ****/ 13 | var cfg = { 14 | hq_fm:0, // force 53kHz sampling rate 15 | dac_bits:8, // DAC width 16 | maxcalc:0, // for logging, # total chan_calc ops to log 17 | debug:0, // for logging 18 | debugLocal:0, 19 | debugArr:[], 20 | mode:0, // 0=gpgx, 1=vb/scale 21 | strict:0 // abort on bad input if true 22 | }; 23 | 24 | /**** GLOBALS ****/ 25 | var _YM = { //// used if cfg.mode = 1 (i.e. scale the tables to a ratio of clock) +neo 26 | "FREQ_SH":16, // 16.16 fixed point (freq calcs) 27 | "EG_SH":16, // 16.16 fixed point (env gen timing) 28 | "LFO_SH":24, // 8.24 fixed point (lfo calcs) 29 | "TIMER_SH":16 // 16.16 fixed point (timers calcs) 30 | }; 31 | _YM.FREQ_MASK = (1<<_YM.FREQ_SH)-1; 32 | 33 | /**** ENVELOPE GENERATOR ****/ 34 | var _ENV = { 35 | "BITS":10, 36 | "MIN_ATT_INDEX":0 37 | }; 38 | _ENV.LEN = 1<<_ENV.BITS; 39 | _ENV.STEP = 128.0/_ENV.LEN; 40 | _ENV.MAX_ATT_INDEX = _ENV.LEN-1; 41 | 42 | var _EG = { 43 | 'ATT':4, 44 | 'DEC':3, 45 | 'SUS':2, 46 | 'REL':1, 47 | 'OFF':0 48 | }; 49 | 50 | /**** PHASE GENERATOR (detune mask) ****/ 51 | var _DT = { 52 | "BITS":17 53 | }; 54 | _DT.LEN = 1<<_DT.BITS; 55 | _DT.MASK = _DT.LEN-1; 56 | 57 | /**** OPERATOR UNIT ****/ 58 | var _SIN = { 59 | "BITS":10 60 | }; 61 | _SIN.LEN = 1<<_SIN.BITS; 62 | _SIN.MASK = _SIN.LEN-1; 63 | 64 | var _TL = { 65 | "BITS":14 66 | }; 67 | _TL.RES_LEN = 256; // sinus resolution 68 | _TL.TAB_LEN = 13*2*_TL.RES_LEN; // 13 = sinus amplitude bits, 2 = sinus sign bit 69 | _TL.tab = new Array(_TL.TAB_LEN); 70 | 71 | _ENV.QUIET = _TL.TAB_LEN>>3; 72 | 73 | /* sin waveform table in 'decibel' scale */ 74 | _YM.sin = new Array(_SIN.LEN); 75 | 76 | /* sustain level table (3dB per step) */ 77 | /* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ 78 | /* 1, 2, 4, 8, 16, 32, 64 (value)*/ 79 | /* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ 80 | /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ 81 | /* attenuation value (10 bits) = (SL << 2) << 3 */ 82 | _YM.sl = (function(){ 83 | var SC = function(db){return (db*4.0/_ENV.STEP)|0;}; 84 | return [ 85 | SC(0), SC(1), SC(2), SC(3), SC(4), SC(5), SC(6), SC(7), 86 | SC(8), SC(9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31) 87 | ]; 88 | })(); 89 | 90 | _EG.RATE_STEPS = 8; 91 | _EG.inc = [ // 19*_EG.RATE_STEPS 92 | /*cycle:0 1 2 3 4 5 6 7*/ 93 | 94 | /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ 95 | /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ 96 | /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ 97 | /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ 98 | 99 | /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ 100 | /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ 101 | /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ 102 | /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ 103 | 104 | /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ 105 | /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ 106 | /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ 107 | /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ 108 | 109 | /*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ 110 | /*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ 111 | /*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ 112 | /*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ 113 | 114 | /*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ 115 | /*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ 116 | /*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ 117 | ]; 118 | 119 | _EG.rate_select = (function(){ 120 | var O = function(a){return (a*_EG.RATE_STEPS)|0;}; 121 | return [ // env gen rates - 32+64 rates+32 RKS 122 | /* 32 infinite time rates (same as Rate 0) */ 123 | O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), 124 | O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), 125 | O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), 126 | O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), 127 | 128 | /* rates 00-11 */ 129 | /* 130 | O( 0),O( 1),O( 2),O( 3), 131 | O( 0),O( 1),O( 2),O( 3), 132 | */ 133 | O(18),O(18),O( 0),O( 0), 134 | O( 0),O( 0),O( 2),O( 2), // Nemesis's tests 135 | 136 | O( 0),O( 1),O( 2),O( 3), 137 | O( 0),O( 1),O( 2),O( 3), 138 | O( 0),O( 1),O( 2),O( 3), 139 | O( 0),O( 1),O( 2),O( 3), 140 | O( 0),O( 1),O( 2),O( 3), 141 | O( 0),O( 1),O( 2),O( 3), 142 | O( 0),O( 1),O( 2),O( 3), 143 | O( 0),O( 1),O( 2),O( 3), 144 | O( 0),O( 1),O( 2),O( 3), 145 | O( 0),O( 1),O( 2),O( 3), 146 | 147 | /* rate 12 */ 148 | O( 4),O( 5),O( 6),O( 7), 149 | 150 | /* rate 13 */ 151 | O( 8),O( 9),O(10),O(11), 152 | 153 | /* rate 14 */ 154 | O(12),O(13),O(14),O(15), 155 | 156 | /* rate 15 */ 157 | O(16),O(16),O(16),O(16), 158 | 159 | /* 32 dummy rates (same as 15 3) */ 160 | O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), 161 | O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), 162 | O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), 163 | O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) 164 | ]; 165 | })(); 166 | 167 | /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ 168 | /*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ 169 | /*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ 170 | _EG.rate_shift = (function(){ 171 | var O = function(a){return (a)|0;}; 172 | return [ // env gen counter shifts - 32+64 rates+32 RKS 173 | /* 32 infinite time rates */ 174 | /* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), 175 | O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), 176 | O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), 177 | O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ 178 | 179 | /* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ 180 | O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), 181 | O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), 182 | O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), 183 | O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), 184 | 185 | /* rates 00-11 */ 186 | O(11),O(11),O(11),O(11), 187 | O(10),O(10),O(10),O(10), 188 | O( 9),O( 9),O( 9),O( 9), 189 | O( 8),O( 8),O( 8),O( 8), 190 | O( 7),O( 7),O( 7),O( 7), 191 | O( 6),O( 6),O( 6),O( 6), 192 | O( 5),O( 5),O( 5),O( 5), 193 | O( 4),O( 4),O( 4),O( 4), 194 | O( 3),O( 3),O( 3),O( 3), 195 | O( 2),O( 2),O( 2),O( 2), 196 | O( 1),O( 1),O( 1),O( 1), 197 | O( 0),O( 0),O( 0),O( 0), 198 | 199 | /* rate 12 */ 200 | O( 0),O( 0),O( 0),O( 0), 201 | 202 | /* rate 13 */ 203 | O( 0),O( 0),O( 0),O( 0), 204 | 205 | /* rate 14 */ 206 | O( 0),O( 0),O( 0),O( 0), 207 | 208 | /* rate 15 */ 209 | O( 0),O( 0),O( 0),O( 0), 210 | 211 | /* 32 dummy rates (same as 15 3) */ 212 | O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), 213 | O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), 214 | O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), 215 | O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) 216 | ]; 217 | })(); 218 | 219 | _DT.tab = [ // 4*32 220 | /* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ 221 | /* FD=0 */ 222 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224 | /* FD=1 */ 225 | 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 226 | 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, 227 | /* FD=2 */ 228 | 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 229 | 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, 230 | /* FD=3 */ 231 | 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 232 | 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 233 | ]; 234 | 235 | 236 | /* OPN key frequency number -> key code follow table */ 237 | /* fnum higher 4bit -> keycode lower 2bit */ 238 | var OPN = { 239 | "fktable":[0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3], 240 | }; 241 | 242 | var LFO = { 243 | /* 8 LFO speed parameters */ 244 | /* each value represents number of samples that one LFO level will last for */ 245 | "samples_per_step":[108, 77, 71, 67, 62, 44, 8, 5], 246 | /*There are 4 different LFO AM depths available, they are: 247 | 0 dB, 1.4 dB, 5.9 dB, 11.8 dB 248 | Here is how it is generated (in EG steps): 249 | 250 | 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 251 | 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 252 | 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 253 | 254 | (1.4 dB is loosing precision as you can see) 255 | 256 | It's implemented as generator from 0..126 with step 2 then a shift 257 | right N times, where N is: 258 | 8 for 0 dB 259 | 3 for 1.4 dB 260 | 1 for 5.9 dB 261 | 0 for 11.8 dB 262 | */ 263 | "ams_depth_shift":[8,3,1,0], 264 | /*There are 8 different LFO PM depths available, they are: 265 | 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) 266 | 267 | Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 268 | (bits 8,9,10 = FNUM MSB from OCT/FNUM register) 269 | 270 | Here we store only first quarter (positive one) of full waveform. 271 | Full table (lfo_pm_table) containing all 128 waveforms is build 272 | at run (init) time. 273 | 274 | One value in table below represents 4 (four) basic LFO steps 275 | (1 PM step = 4 AM steps). 276 | 277 | For example: 278 | at LFO SPEED=0 (which is 108 samples per basic LFO step) 279 | one value from "lfo_pm_output" table lasts for 432 consecutive 280 | samples (4*108=432) and one full LFO waveform cycle lasts for 13824 281 | samples (32*432=13824; 32 because we store only a quarter of whole 282 | waveform in the table below) 283 | */ 284 | "pm_output":[ // [7*8][8] 285 | /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ 286 | /* FNUM BIT 4: 000 0001xxxx */ 287 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 288 | /* DEPTH 1 */ [0, 0, 0, 0, 0, 0, 0, 0], 289 | /* DEPTH 2 */ [0, 0, 0, 0, 0, 0, 0, 0], 290 | /* DEPTH 3 */ [0, 0, 0, 0, 0, 0, 0, 0], 291 | /* DEPTH 4 */ [0, 0, 0, 0, 0, 0, 0, 0], 292 | /* DEPTH 5 */ [0, 0, 0, 0, 0, 0, 0, 0], 293 | /* DEPTH 6 */ [0, 0, 0, 0, 0, 0, 0, 0], 294 | /* DEPTH 7 */ [0, 0, 0, 0, 1, 1, 1, 1], 295 | 296 | /* FNUM BIT 5: 000 0010xxxx */ 297 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 298 | /* DEPTH 1 */ [0, 0, 0, 0, 0, 0, 0, 0], 299 | /* DEPTH 2 */ [0, 0, 0, 0, 0, 0, 0, 0], 300 | /* DEPTH 3 */ [0, 0, 0, 0, 0, 0, 0, 0], 301 | /* DEPTH 4 */ [0, 0, 0, 0, 0, 0, 0, 0], 302 | /* DEPTH 5 */ [0, 0, 0, 0, 0, 0, 0, 0], 303 | /* DEPTH 6 */ [0, 0, 0, 0, 1, 1, 1, 1], 304 | /* DEPTH 7 */ [0, 0, 1, 1, 2, 2, 2, 3], 305 | 306 | /* FNUM BIT 6: 000 0100xxxx */ 307 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 308 | /* DEPTH 1 */ [0, 0, 0, 0, 0, 0, 0, 0], 309 | /* DEPTH 2 */ [0, 0, 0, 0, 0, 0, 0, 0], 310 | /* DEPTH 3 */ [0, 0, 0, 0, 0, 0, 0, 0], 311 | /* DEPTH 4 */ [0, 0, 0, 0, 0, 0, 0, 1], 312 | /* DEPTH 5 */ [0, 0, 0, 0, 1, 1, 1, 1], 313 | /* DEPTH 6 */ [0, 0, 1, 1, 2, 2, 2, 3], 314 | /* DEPTH 7 */ [0, 0, 2, 3, 4, 4, 5, 6], 315 | 316 | /* FNUM BIT 7: 000 1000xxxx */ 317 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 318 | /* DEPTH 1 */ [0, 0, 0, 0, 0, 0, 0, 0], 319 | /* DEPTH 2 */ [0, 0, 0, 0, 0, 0, 1, 1], 320 | /* DEPTH 3 */ [0, 0, 0, 0, 1, 1, 1, 1], 321 | /* DEPTH 4 */ [0, 0, 0, 1, 1, 1, 1, 2], 322 | /* DEPTH 5 */ [0, 0, 1, 1, 2, 2, 2, 3], 323 | /* DEPTH 6 */ [0, 0, 2, 3, 4, 4, 5, 6], 324 | /* DEPTH 7 */ [0, 0, 4, 6, 8, 8, 0xa, 0xc], 325 | 326 | /* FNUM BIT 8: 001 0000xxxx */ 327 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 328 | /* DEPTH 1 */ [0, 0, 0, 0, 1, 1, 1, 1], 329 | /* DEPTH 2 */ [0, 0, 0, 1, 1, 1, 2, 2], 330 | /* DEPTH 3 */ [0, 0, 1, 1, 2, 2, 3, 3], 331 | /* DEPTH 4 */ [0, 0, 1, 2, 2, 2, 3, 4], 332 | /* DEPTH 5 */ [0, 0, 2, 3, 4, 4, 5, 6], 333 | /* DEPTH 6 */ [0, 0, 4, 6, 8, 8, 0xa, 0xc], 334 | /* DEPTH 7 */ [0, 0, 8, 0xc,0x10,0x10,0x14,0x18], 335 | 336 | /* FNUM BIT 9: 010 0000xxxx */ 337 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 338 | /* DEPTH 1 */ [0, 0, 0, 0, 2, 2, 2, 2], 339 | /* DEPTH 2 */ [0, 0, 0, 2, 2, 2, 4, 4], 340 | /* DEPTH 3 */ [0, 0, 2, 2, 4, 4, 6, 6], 341 | /* DEPTH 4 */ [0, 0, 2, 4, 4, 4, 6, 8], 342 | /* DEPTH 5 */ [0, 0, 4, 6, 8, 8, 0xa, 0xc], 343 | /* DEPTH 6 */ [0, 0, 8, 0xc,0x10,0x10,0x14,0x18], 344 | /* DEPTH 7 */ [0, 0,0x10,0x18,0x20,0x20,0x28,0x30], 345 | 346 | /* FNUM BIT10: 100 0000xxxx */ 347 | /* DEPTH 0 */ [0, 0, 0, 0, 0, 0, 0, 0], 348 | /* DEPTH 1 */ [0, 0, 0, 0, 4, 4, 4, 4], 349 | /* DEPTH 2 */ [0, 0, 0, 4, 4, 4, 8, 8], 350 | /* DEPTH 3 */ [0, 0, 4, 4, 8, 8, 0xc, 0xc], 351 | /* DEPTH 4 */ [0, 0, 4, 8, 8, 8, 0xc,0x10], 352 | /* DEPTH 5 */ [0, 0, 8, 0xc,0x10,0x10,0x14,0x18], 353 | /* DEPTH 6 */ [0, 0,0x10,0x18,0x20,0x20,0x28,0x30], 354 | /* DEPTH 7 */ [0, 0,0x20,0x30,0x40,0x40,0x50,0x60] 355 | ], 356 | /* all 128 LFO PM waveforms */ 357 | /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ 358 | "pm_table":new Array(128*8*32) 359 | }; 360 | 361 | OPN.CHAN = function(N){return N&0x3;}; 362 | OPN.SLOT = function(N){return (N>>2)&0x3;}; 363 | 364 | /* slot number */ 365 | var _SLOT = [0,2,1,3]; 366 | 367 | /**** END GLOBALS ****/ 368 | 369 | /**** FM STRUCTS based on genplus-gx ****/ 370 | function FM_SLOT() { 371 | this.DT = -1; // index into ym2612.OPN.ST.dt_tab, formerly INT32* detune: dt_tab[DT] 372 | this.KSR = 0; // UINT8 key scale rate: 3-KSR 373 | function _rate() { 374 | this.ar = 0; // UINT32 attack rate 375 | this.d1r = 0; // UINT32 decay rate 376 | this.d2r = 0; // UINT32 sustain rate 377 | this.rr = 0; // UINT32 release rate 378 | this.ksr = 0; // UINT8 key scale rate: kcode>>(3-KSR) 379 | this.mul = 1; // UINT32 multiple: ML_TABLE[ML] 380 | this.init = function() { 381 | this.ar = 0; 382 | this.d1r = 0; 383 | this.d2r = 0; 384 | this.rr = 0; 385 | this.ksr = 0; 386 | this.mul = 1; 387 | }; 388 | this.toString = function(){return ["MUL="+this.mul,"KS="+this.ksr,"AR="+this.ar,"D1R="+this.d1r,"D2R="+this.d2r,"RR="+this.rr].join(',');}; 389 | } 390 | this.rate = new _rate; 391 | // phase generator 392 | this.phase = 0; // UINT32 phase counter 393 | this.Incr = 0; // INT32 phase step 394 | // envelope generator 395 | this.state = 0; // UINT8 phase type 396 | this.tl = 0; // UINT32 total level: TL<<3 397 | this.volume = 0; // UINT32 envelope counter 398 | this.sl = 0; // UINT32 sustain level: sl_table[SL] 399 | this.vol_out = 0; // UINT32 current output from EG (without AM from LFO) 400 | function _eg() { 401 | this.ar=0; // UINT8 402 | this.d1r=0; // UINT8 403 | this.d2r=0; // UINT8 404 | this.rr=0; // UINT8 405 | this.init = function(){ 406 | this.ar=0; // UINT8 407 | this.d1r=0; // UINT8 408 | this.d2r=0; // UINT8 409 | this.rr=0; // UINT8 410 | }; 411 | } 412 | this.eg = { 413 | sh:new _eg, // state 414 | sel:new _eg, 415 | init:function(){this.sh.init();this.sel.init();} 416 | }; 417 | this.ssg = 0; // UINT8 ssg-eg waveform 418 | this.ssgn = 0; // UINT8 ssg-eg negated output 419 | this.key = 0; // UINT8 0 = last key was KEY_OFF, 1 = KEY_ON 420 | // lfo 421 | this.AMmask = 0; // UINT32 AM enable flag 422 | this.reset = function() { 423 | this.Incr = -1; 424 | this.key = 0; 425 | this.phase = 0; 426 | this.ssgn = 0; 427 | this.state = _EG.OFF; 428 | this.volume = _ENV.MAX_ATT_INDEX; 429 | this.vol_out = _ENV.MAX_ATT_INDEX; 430 | this.out[0] = 0, this.out[1] = 0; 431 | }; 432 | this.debug = { 433 | "dt1mul":0, "dt1":0, "mul":0, 434 | "tl":0, 435 | "ksar":0, "ks":0, "ar":0, 436 | "amd1r":0, "am":0, "d1r":0, 437 | "d2r":0, 438 | "slrr":0, "sl":0, "rr":0 439 | }; 440 | this.debug.toString = function(){return [ 441 | "TL:"+(this.tl), 442 | "DT1:"+this.dt1, 443 | "MUL:"+this.mul, 444 | "KS:"+this.ks,"AR:"+this.ar, 445 | "AM:"+this.am,"D1R:"+this.d1r, 446 | "D2R:"+this.d2r, 447 | "SL:"+(this.sl),"RR:"+this.rr 448 | ].join(',');}; 449 | this.out = [0,0]; // replace FM_CH.op1_out +neo 450 | this.toString = function(){return "OP{"+this.debug.toString()+"}";}; 451 | } 452 | function FM_CH() { 453 | this.SLOT = [ // four slots/ops 454 | new FM_SLOT(), 455 | new FM_SLOT(), 456 | new FM_SLOT(), 457 | new FM_SLOT() 458 | ]; 459 | this.ALGO = 0; // UINT8 algorithm 460 | this.FB = 0; // UINT8 feedback shift 461 | this.op1_out = [0,0]; // INT32 op1 output for feedback (stereo) 462 | this.connect = ['x','x','x','x']; // SLOT output pointers, formerly INT32*[4] 463 | this.mem = { 464 | connect:'mem', // INT32* where to put the delayed sample (MEM) 465 | value:0 // INT32 delated sample (MEM) value 466 | }; 467 | this.pms = 0; // INT32 channel PMS 468 | this.ams = 0; // UINT8 channel AMS 469 | this.fc = 0; // UINT32 fnum,blk adjusted to sample rate 470 | this.kcode = 0; // UINT8 key code 471 | this.block_fnum = 0; // UINT32 current blk/fnum value for this slot 472 | this.fn_h = 0; // replaces FM_ST.fn_h 473 | this.outputs = { 474 | "m1":0, 475 | "m2":0, 476 | "c1":0, 477 | "c2":0, 478 | "mem":0, 479 | "x":0, 480 | "out":0 // replaces out_fm[ch] 481 | }; 482 | this.canCSM = 0; // replaces hardcoded check against CH3 483 | this.canDAC = 0; // replaces hardcoded check against CH6 484 | this.muted = 0; 485 | this.pan = [0,0]; // replaces FM_OPN.pan 486 | this.reset = function() { 487 | this.mem.value = 0, this.op1_out[0] = 0, this.op1_out[1] = 0; 488 | var s = this.SLOT.length; while (--s>-1) this.SLOT[s].reset(); 489 | }; 490 | this.toString = function(){return "CH{"+[ 491 | "ALGO:"+this.ALGO,"FB:"+this.FB, 492 | "PMS:"+this.pms,"AMS:"+this.ams, 493 | "SLOTS["+this.SLOT.join(',')+"]" 494 | ].join(',')+"}";}; 495 | } 496 | function FM_ST(c, r) { 497 | this.address = 0; // UINT16 address register 498 | this.status = 0; // UINT8 status flag 499 | this.mode = 0; // UINT32 CSM/3SLOT mode 500 | //this.fn_h = 0; // UINT8 freq latch 501 | this.timer_base = 1; 502 | this.TA = 0; // INT32 timer a value 503 | this.TAL = 0; // INT32 timer a base 504 | this.TAC = 0; // INT32 timer a counter 505 | this.TB = 0; // INT32 timer b value 506 | this.TBL = 0; // INT32 timer b base 507 | this.TBC = 0; // INT32 timer b counter 508 | this.dt_tab = [ // INT32[8][32] detune table 509 | new Array(32), 510 | new Array(32), 511 | new Array(32), 512 | new Array(32), 513 | new Array(32), 514 | new Array(32), 515 | new Array(32), 516 | new Array(32) 517 | ]; 518 | this.clock = c||7670448; 519 | this.rate = r||44100; 520 | } 521 | 522 | function FM_3SLOT() { 523 | this.fc = [0,0,0]; // UINT32[3] fnum3,blk3 calculated 524 | this.fn_h = 0; // UINT8 freq3 latch 525 | this.kcode = [0,0,0]; // UINT8[3] key code 526 | this.block_fnum = [0,0,0]; // UINT32[3] current fnum value for this slot 527 | this.key_csm = 0; // UINT8 CSM mode KEY_ON flag 528 | } 529 | 530 | function FM_OPN(c, r) { 531 | this.ST = new FM_ST(c,r); 532 | this.SL3 = new FM_3SLOT; 533 | //this.pan = new Array(6*2); // UINT[6*2] fm channels output masks (0xffffffff = enable) 534 | function _timer() { 535 | this.cnt = 0; 536 | this.timer = 0; 537 | this.timer_add = 0; // vb 538 | this.timer_overflow = 0; // vb unused for eg 539 | this.init = function() { 540 | this.cnt = 0; // current phase counter (UINT32 for eg, UINT8 for lfo) 541 | this.timer = 0; // UINT32 542 | this.timer_add = 0; // UINT32 step of timer 543 | this.timer_overflow = 0; // UINT32 timer overflows every N samples 544 | }; 545 | } 546 | this.eg = new _timer(); 547 | this.lfo = new _timer(); 548 | this.lfo.AM = 0; // UINT32 current lfo AM step 549 | this.lfo.PM = 0; // UINT32 current lfo PM step 550 | this.fn = {"table":new Array(4096), "max":0}; 551 | } 552 | 553 | function YMX(c,r) { 554 | this.CH = [new FM_CH, new FM_CH, new FM_CH, new FM_CH, new FM_CH, new FM_CH]; 555 | this.CH[2].canCSM = 1; 556 | this.CH[5].canDAC = 1; 557 | this.dacen = 0; // UINT8 558 | this.dacout = 0; // INT32 559 | this.OPN = new FM_OPN(c,r); 560 | this.toString = function(){return "YM[\n"+this.CH.join(',\n')+"\n]";}; 561 | } 562 | /**** END FM STRUCTS ****/ 563 | 564 | /**** FM DEFS based on genplus-gx ****/ 565 | /* current chip state */ 566 | //_YM.m2 = 0; _YM.c1 = 0; _YM.c2 = 0; // INT32 phase modulation input for ops 2,3,4 567 | //_YM.mem = 0; // INT32 one sample delay memory 568 | //_YM.out_fm = [0,0,0,0,0,0,0,0]; // INT32[8] outputs of working channels // REPLACED BY FM_CH.out 569 | _YM.bitmask = 0; //UINT32 working channels output bitmasking (DAC quantization) 570 | 571 | FM_SLOT.prototype.keyOn = function(x,csm) { 572 | if (!this.key&&!x.OPN.SL3.key_csm) { 573 | this.phase = 0; /* restart Phase Generator */ 574 | this.ssgn = 0; /* reset SSG-EG inversion flag */ 575 | if ((this.rate.ar+this.rate.ksr)<94) /*32+62*/ 576 | this.state = (this.volume<=_ENV.MIN_ATT_INDEX)?(this.sl===_ENV.MIN_ATT_INDEX?_EG.SUS:_EG.DEC):_EG.ATT; 577 | else { 578 | this.volume = _ENV.MIN_ATT_INDEX; /* force attenuation level to 0 */ 579 | this.state = (this.sl===_ENV.MIN_ATT_INDEX)?_EG.SUS:_EG.DEC; 580 | } 581 | /* recalculate EG output */ 582 | if ((this.ssg&0x08)>0&&(this.ssgn^(this.ssg&0x04))>0) this.vol_out = this.tl+((0x200-this.volume)&_ENV.MAX_ATT_INDEX); 583 | else this.vol_out = this.tl+(this.volume|0); 584 | } 585 | if (!csm) this.key = 1; 586 | }; 587 | FM_CH.prototype.keyOn = function(x,s) {this.SLOT[s].keyOn(x,0);}; 588 | 589 | FM_SLOT.prototype.keyOff = function(x,csm) { 590 | if ((csm&&!this.key)||(!csm&&this.key&&!x.OPN.SL3.key_csm)) { 591 | if (this.state>_EG.REL) { 592 | this.state = _EG.REL; /* phase -> Release */ 593 | /* SSG-EG specific update */ 594 | if ((this.ssg&0x08)>0) { 595 | /* convert EG attenuation level */ 596 | if ((this.ssgn^(this.ssg&0x04))>0) this.volume = (0x200-this.volume)|0; 597 | /* force EG attenuation level */ 598 | if (this.volume>=0x200) { 599 | this.volume = _ENV.MAX_ATT_INDEX; 600 | this.state = _EG.OFF; 601 | } 602 | this.vol_out = this.tl+(this.volume|0); /* recalculate EG output */ 603 | } 604 | } 605 | } 606 | if (!csm) this.key = 0; 607 | }; 608 | FM_CH.prototype.keyOff = function(x,s) {this.SLOT[s].keyOff(x,0);}; 609 | 610 | FM_CH.prototype.keyOnCSM = function(x,s) {this.SLOT[s].keyOn(x,1);}; 611 | FM_CH.prototype.keyOffCSM = function(x,s) {this.SLOT[s].keyOff(x,1);}; 612 | 613 | FM_CH.prototype.keyControlCSM = function(x) { 614 | this.keyOnCSM(_SLOT[0]); 615 | this.keyOnCSM(_SLOT[1]); 616 | this.keyOnCSM(_SLOT[2]); 617 | this.keyOnCSM(_SLOT[3]); 618 | x.OPN.SL3.key_csm = 1; 619 | }; 620 | 621 | function INTERNAL_TIMER_A(x) { 622 | if ((x.OPN.ST.mode&0x01)>0) { 623 | if (cfg.mode) x.OPN.ST.TAC -= x.OPN.ST.timer_base; // vb 624 | else --x.OPN.ST.TAC; // gpgx 625 | if (x.OPN.ST.TAC<=0) { 626 | /* set status (if enabled) */ 627 | if ((x.OPN.ST.mode&0x04)>0) x.OPN.ST.status |= 0x01; 628 | /* reload the counter */ 629 | if (cfg.mode&&x.OPN.ST.TAL) x.OPN.ST.TAC += x.OPN.ST.TAL; // vb 630 | else x.OPN.ST.TAC = x.OPN.ST.TAL; // gpgx 631 | /* CSM mode auto key on */ 632 | if ((x.OPN.ST.mode & 0xC0)===0x80) x.CH[2].keyControlCSM(); 633 | } 634 | } 635 | } 636 | function INTERNAL_TIMER_B(x, step) { 637 | if ((x.OPN.ST.mode & 0x02)>0) { 638 | if (cfg.mode) x.OPN.ST.TBC -= x.OPN.ST.timer_base*step; // vb 639 | else x.OPN.ST.TBC -= step; // gpgx 640 | if (x.OPN.ST.TBC <= 0) { 641 | /* set status (if enabled) */ 642 | if ((x.OPN.ST.mode & 0x08)>0) x.OPN.ST.status |= 0x02; 643 | /* reload the counter */ 644 | if (x.OPN.ST.TBL) x.OPN.ST.TBC += x.OPN.ST.TBL; 645 | else x.OPN.ST.TBC = x.OPN.ST.TBL; 646 | } 647 | } 648 | } 649 | 650 | /* OPN Mode Register Write */ 651 | function set_timers(x,v) { 652 | /* b7 = CSM MODE */ 653 | /* b6 = 3 slot mode */ 654 | /* b5 = reset b */ 655 | /* b4 = reset a */ 656 | /* b3 = timer enable b */ 657 | /* b2 = timer enable a */ 658 | /* b1 = load b */ 659 | /* b0 = load a */ 660 | if (((x.OPN.ST.mode^v)&0xc0)>0) { 661 | x.CH[2].SLOT[_SLOT[0]].Incr = -1; // phase increment need to be recalculated 662 | // csm mode disabled and csm keyon active 663 | if (((v&0xc0)!==0x80)&&x.OPN.SL3.key_csm) { 664 | // csm mode keyoff 665 | x.CH[2].keyOffCSM(_SLOT[0]); 666 | x.CH[2].keyOffCSM(_SLOT[1]); 667 | x.CH[2].keyOffCSM(_SLOT[2]); 668 | x.CH[2].keyOffCSM(_SLOT[3]); 669 | x.OPN.SL3.key_csm = 0; 670 | } 671 | } 672 | // reload timers 673 | if ((v&1)&&!(x.OPN.ST.mode&1)) x.OPN.ST.TAC = x.OPN.ST.TAL; 674 | if ((v&2)&&!(x.OPN.ST.mode&2)) x.OPN.ST.TBC = x.OPN.ST.TBL; 675 | // reset timers flags 676 | x.OPN.ST.status &= ~v>>4; 677 | x.OPN.ST.mode = v; 678 | } 679 | 680 | /* set algorithm connection */ 681 | FM_CH.prototype.setupConnection = function() { 682 | var carrier = 'out'; 683 | var o = {m1:0, m2:2, c1:1, c2:3}, mem = this.mem.connect; 684 | switch (this.ALGO) { 685 | case 0: 686 | /* M1---C1---MEM---M2---C2---OUT */ 687 | this.connect[o.m1] = 'c1'; 688 | this.connect[o.c1] = 'mem'; 689 | this.connect[o.m2] = 'c2'; 690 | //if (mem!=='mem') this.connect[o[mem]] = 'm2'; 691 | this.mem.connect = 'm2'; 692 | break; 693 | case 1: 694 | /* M1------+-MEM---M2---C2---OUT */ 695 | /* C1-+ */ 696 | this.connect[o.m1] = 'mem'; 697 | this.connect[o.c1] = 'mem'; 698 | this.connect[o.m2] = 'c2'; 699 | //if (mem!=='mem') this.connect[o[mem]] = 'm2'; 700 | this.mem.connect = 'm2'; 701 | break; 702 | case 2: 703 | /* M1-----------------+-C2---OUT */ 704 | /* C1---MEM---M2-+ */ 705 | this.connect[o.m1] = 'c2'; 706 | this.connect[o.c1] = 'mem'; 707 | this.connect[o.m2] = 'c2'; 708 | //if (mem!=='mem') this.connect[o[mem]] = 'm2'; 709 | this.mem.connect = 'm2'; 710 | break; 711 | case 3: 712 | /* M1---C1---MEM------+-C2---OUT */ 713 | /* M2-+ */ 714 | this.connect[o.m1] = 'c1'; 715 | this.connect[o.c1] = 'mem'; 716 | this.connect[o.m2] = 'c2'; 717 | //if (mem!=='mem') this.connect[o[mem]] = 'c2'; 718 | this.mem.connect = 'c2'; 719 | break; 720 | case 4: 721 | /* M1---C1-+-OUT */ 722 | /* M2---C2-+ */ 723 | /* MEM: not used */ 724 | this.connect[o.m1] = 'c1'; 725 | this.connect[o.c1] = carrier; 726 | this.connect[o.m2] = 'c2'; 727 | //if (mem!=='mem') this.connect[o[mem]] = 'mem'; 728 | this.mem.connect = 'mem'; 729 | break; 730 | case 5: 731 | /* +----C1----+ */ 732 | /* M1-+-MEM---M2-+-OUT */ 733 | /* +----C2----+ */ 734 | this.connect[o.m1] = 'x'; 735 | this.connect[o.c1] = carrier; 736 | this.connect[o.m2] = carrier; 737 | //if (mem!=='mem') this.connect[o[mem]] = 'm2'; 738 | this.mem.connect = 'm2'; 739 | break; 740 | case 6: 741 | /* M1---C1-+ */ 742 | /* M2-+-OUT */ 743 | /* C2-+ */ 744 | /* MEM: not used */ 745 | this.connect[o.m1] = 'c1'; 746 | this.connect[o.c1] = carrier; 747 | this.connect[o.m2] = carrier; 748 | //if (mem!=='mem') this.connect[o[mem]] = 'mem'; 749 | this.mem.connect = 'mem'; 750 | break; 751 | case 7: 752 | /* M1-+ */ 753 | /* C1-+-OUT */ 754 | /* M2-+ */ 755 | /* C2-+ */ 756 | /* MEM: not used*/ 757 | this.connect[o.m1] = carrier; 758 | this.connect[o.c1] = carrier; 759 | this.connect[o.m2] = carrier; 760 | //if (mem!=='mem') this.connect[o[mem]] = 'mem'; 761 | this.mem.connect = 'mem'; 762 | break; 763 | default: 764 | if (cfg.strict) throw new Error("CH::setup_connection - unsupported algorithm ("+this.ALGO+")"); 765 | else break; 766 | } 767 | this.connect[3] = carrier; 768 | }; 769 | 770 | /* set detune & multiple */ 771 | FM_SLOT.prototype.set_det_mul = function(x,v) { 772 | this.rate.mul = ((v&0x0f)>0)?((v&0x0f)<<1):1; 773 | this.DT = (v>>4)&7;//x.OPN.ST.dt_tab[(v>>4)&7]; 774 | this.debug.dt1mul = v&0xff; 775 | this.debug.dt1 = this.DT; 776 | this.debug.mul = v&0x0f; 777 | }; 778 | FM_CH.prototype.set_det_mul = function(x,s,v) { 779 | this.SLOT[s].set_det_mul(x,v); 780 | this.SLOT[_SLOT[0]].Incr = -1; 781 | }; 782 | 783 | /* set total level */ 784 | FM_SLOT.prototype.set_tl = function(v) { 785 | this.debug.tl = (v&0x7f); 786 | this.tl = (this.debug.tl)<<(_ENV.BITS-7); // 7-bit tl 787 | // recalculate eg output 788 | if ((this.ssg&0x08)>0&&((this.ssgn^(this.ssg&0x04))>0)&&this.state>_EG.REL) 789 | this.vol_out = this.tl+(((0x200-this.volume)|0)&_ENV.MAX_ATT_INDEX); 790 | else 791 | this.vol_out = this.tl+((this.volume)|0); 792 | }; 793 | FM_CH.prototype.set_tl = function(s,v) {this.SLOT[s].set_tl(v);}; 794 | 795 | /* set attack rate & key scale */ 796 | FM_SLOT.prototype.set_ar_ksr = function(v) { 797 | this.debug.ksar = v&0xff; 798 | this.debug.ks = v>>6; 799 | this.debug.ar = v&0x1f; 800 | var old_ksr = this.KSR|0; 801 | this.rate.ar = ((this.debug.ar)>0)?32+((this.debug.ar)<<1):0; 802 | this.KSR = 3-(this.debug.ks); 803 | /* Even if it seems unnecessary to do it here, it could happen that KSR and KC */ 804 | /* are modified but the resulted SLOT->ksr value (kc >> SLOT->KSR) remains unchanged. */ 805 | /* In such case, Attack Rate would not be recalculated by "refresh_fc_eg_slot". */ 806 | /* This fixes the intro of "The Adventures of Batman & Robin" (Eke-Eke) */ 807 | if ((this.rate.ar+this.rate.ksr)<94) { /*32+62*/ 808 | var q = (this.rate.ar+this.rate.ksr)|0; 809 | this.eg.sh.ar = _EG.rate_shift[q]; 810 | this.eg.sel.ar = _EG.rate_select[q]; 811 | } 812 | else { /* verified by Nemesis on real hardware (Attack phase is blocked) */ 813 | this.eg.sh.ar = 0; 814 | this.eg.sel.ar = 18*_EG.RATE_STEPS; 815 | } 816 | return this.KSR!==old_ksr; 817 | }; 818 | FM_CH.prototype.set_ar_ksr = function(s,v) {if (this.SLOT[s].set_ar_ksr(v)) this.SLOT[_SLOT[0]].Incr = -1;}; 819 | 820 | /* set decay rate */ 821 | FM_SLOT.prototype.set_dr = function(v) { 822 | this.debug.amd1r = v&0xff; 823 | this.debug.am = v&0x80; 824 | this.debug.d1r = v&0x1f; 825 | this.rate.d1r = ((this.debug.d1r)>0)?32+((this.debug.d1r)<<1):0; 826 | var q = (this.rate.d1r+this.rate.ksr)|0; 827 | this.eg.sh.d1r = _EG.rate_shift[q]; 828 | this.eg.sel.d1r = _EG.rate_select[q]; 829 | }; 830 | FM_CH.prototype.set_dr = function(s,v) {this.SLOT[s].set_dr(v);}; 831 | 832 | /* set sustain rate */ 833 | FM_SLOT.prototype.set_sr = function(v) { 834 | this.debug.d2r = v&0x1f; 835 | this.rate.d2r = ((this.debug.d2r)>0)?32+((this.debug.d2r)<<1):0; 836 | var q = (this.rate.d2r+this.rate.ksr)|0; 837 | this.eg.sh.d2r = _EG.rate_shift[q]; 838 | this.eg.sel.d2r = _EG.rate_select[q]; 839 | }; 840 | FM_CH.prototype.set_sr = function(s,v) {this.SLOT[s].set_sr(v);}; 841 | 842 | /* set release rate */ 843 | FM_SLOT.prototype.set_sl_rr = function(v) { 844 | this.debug.slrr = v&0xff; 845 | this.debug.sl = (v>>4)&0x0f; 846 | this.debug.rr = v&0x0f; 847 | this.sl = _YM.sl[this.debug.sl]; 848 | // check eg state changes 849 | if (this.state===_EG.DEC&&this.volume>=(this.sl|0)) this.state = _EG.SUS; 850 | this.rate.rr = 34+((this.debug.rr)<<2); 851 | var q = (this.rate.rr+this.rate.ksr)|0; 852 | this.eg.sh.rr = _EG.rate_shift[q]; 853 | this.eg.sel.rr = _EG.rate_select[q]; 854 | }; 855 | FM_CH.prototype.set_sl_rr = function(s,v) {this.SLOT[s].set_sl_rr(v);}; 856 | 857 | /* advance LFO to next sample */ 858 | function advance_lfo(x) { 859 | var _upd; 860 | if (cfg.mode) _upd = function(o) { // vb 861 | while (o.lfo.timer>=o.lfo.timer_overflow) { 862 | o.lfo.timer -= o.lfo.timer_overflow; 863 | o.lfo.cnt = (o.lfo.cnt+1)&127; /* There are 128 LFO steps */ 864 | /* triangle (inverted) */ 865 | /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ 866 | if (o.lfo.cnt<64) o.lfo.AM = (o.lfo.cnt^63)<<1; 867 | else o.lfo.AM = (o.lfo.cnt&63)<<1; 868 | o.lfo.PM = o.lfo.cnt>>2; /* PM works with 4 times slower clock */ 869 | } 870 | }; 871 | else _upd = function(o) { // gpgx 872 | if (o.lfo.timer>o.lfo.timer_overflow) { 873 | o.lfo.timer = 0; 874 | o.lfo.cnt = (o.lfo.cnt+1)&127; /* There are 128 LFO steps */ 875 | /* triangle (inverted) */ 876 | /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ 877 | if (o.lfo.cnt<64) o.lfo.AM = (o.lfo.cnt^63)<<1; 878 | else o.lfo.AM = (o.lfo.cnt&63)<<1; 879 | o.lfo.PM = o.lfo.cnt>>2; /* PM works with 4 times slower clock */ 880 | } 881 | }; 882 | if (x.OPN.lfo.timer_overflow) { /* LFO enabled ? */ 883 | /* increment LFO timer (every samples) */ 884 | if (cfg.mode) x.OPN.lfo.timer += x.OPN.lfo.timer_add; // vb 885 | else ++x.OPN.lfo.timer; // gpgx 886 | /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ 887 | _upd(x.OPN); 888 | } 889 | } 890 | 891 | FM_SLOT.prototype.advance_eg = function(eg_cnt) { 892 | switch (this.state) { 893 | case _EG.ATT: /* attack phase */ 894 | if (!(eg_cnt&((1<>this.eg.sh.ar)&7)]))>>4; /* update attenuation level */ 896 | /* check phase transition*/ 897 | if (this.volume<=_ENV.MIN_ATT_INDEX) { 898 | this.volume = _ENV.MIN_ATT_INDEX; 899 | this.state = (this.sl===_ENV.MIN_ATT_INDEX)?_EG.SUS:_EG.DEC; /* special case where SL=0 */ 900 | } 901 | /* recalculate EG output */ 902 | if ((this.ssg&0x08)>0&&(this.ssgn^(this.ssg&0x04))>0) this.vol_out = this.tl+(((0x200-this.volume)|0)&_ENV.MAX_ATT_INDEX); /* SSG-EG Output Inversion */ 903 | else this.vol_out = this.tl+(this.volume|0); 904 | } 905 | break; 906 | case _EG.DEC: /* decay phase */ 907 | if (!(eg_cnt&((1<0) { /* SSG EG type */ 909 | /* update attenuation level */ 910 | if (this.volume<0x200) { 911 | this.volume += _EG.inc[this.eg.sel.d1r+((eg_cnt>>this.eg.sh.d1r)&7)]<<2; 912 | /* recalculate EG output */ 913 | if ((this.ssgn^(this.ssg&0x04))>0) this.vol_out = this.tl+(((0x200-this.volume)|0)&_ENV.MAX_ATT_INDEX); /* SSG-EG Output Inversion */ 914 | else this.vol_out = this.tl+(this.volume|0); 915 | } 916 | } 917 | else { 918 | this.volume += (_EG.inc[this.eg.sel.d1r+((eg_cnt>>this.eg.sh.d1r)&7)]); 919 | this.vol_out = this.tl+(this.volume|0); /* recalculate EG output */ 920 | } 921 | /* check phase transition*/ 922 | if (this.volume>=(this.sl|0)) this.state = _EG.SUS; 923 | } 924 | break; 925 | case _EG.SUS: /* sustain phase */ 926 | if (!(eg_cnt&((1<0) { 929 | /* update attenuation level */ 930 | if (this.volume<0x200) { 931 | this.volume += _EG.inc[this.eg.sel.d2r+((eg_cnt>>this.eg.sh.d2r)&7)]<<2; 932 | /* recalculate EG output */ 933 | if ((this.ssgn^(this.ssg&0x04))>0) this.vol_out = this.tl+(((0x200-this.volume)|0)&_ENV.MAX_ATT_INDEX); /* SSG-EG Output Inversion */ 934 | else this.vol_out = this.tl+(this.volume|0); 935 | } 936 | } 937 | else { 938 | /* update attenuation level */ 939 | this.volume += (_EG.inc[this.eg.sel.d2r+((eg_cnt>>this.eg.sh.d2r)&7)]); 940 | /* check phase transition*/ 941 | if (this.volume>=_ENV.MAX_ATT_INDEX) this.volume = _ENV.MAX_ATT_INDEX; /* do not change SLOT->state (verified on real chip) */ 942 | this.vol_out = this.tl+(this.volume|0); /* recalculate EG output */ 943 | } 944 | } 945 | break; 946 | case _EG.REL: /* release phase */ 947 | if (!(eg_cnt&((1<0) { 950 | /* update attenuation level */ 951 | if (this.volume<0x200) { 952 | this.volume += _EG.inc[this.eg.sel.rr+((eg_cnt>>this.eg.sh.rr)&7)]<<2; 953 | /* check phase transition*/ 954 | if (this.volume>=0x200) { 955 | this.volume = _ENV.MAX_ATT_INDEX; 956 | this.state = _EG.OFF; 957 | } 958 | } 959 | } 960 | else { 961 | /* update attenuation level */ 962 | this.volume += (_EG.inc[this.eg.sel.rr+((eg_cnt>>this.eg.sh.rr)&7)]); 963 | /* check phase transition*/ 964 | if (this.volume>=_ENV.MAX_ATT_INDEX) { 965 | this.volume = _ENV.MAX_ATT_INDEX; 966 | this.state = _EG.OFF; 967 | } 968 | } 969 | this.vol_out = this.tl+(this.volume|0); /* recalculate EG output */ 970 | } 971 | break; 972 | default: 973 | if (cfg.strict) throw new Error("FM_SLOT::advance_eg - unsupported state ("+this.state+")"); 974 | else break; 975 | } 976 | }; 977 | FM_CH.prototype.advance_eg = function(eg_cnt) {var j = this.SLOT.length; while (--j>-1) this.SLOT[j].advance_eg(eg_cnt);}; 978 | function advance_eg_channels(x, eg_cnt) {var i = x.CH.length; while (--i>-1) x.CH[i].advance_eg(eg_cnt);} 979 | 980 | /* SSG-EG update process */ 981 | /* The behavior is based upon Nemesis tests on real hardware */ 982 | /* This is actually executed before each samples */ 983 | FM_SLOT.prototype.update_ssg_eg = function() { 984 | /* detect SSG-EG transition */ 985 | /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ 986 | /* if an Attack Phase is programmed, inversion can occur on each sample */ 987 | if ((this.ssg&0x08)>0&&this.volume>=0x200&&this.state>_EG.REL) { 988 | if ((this.ssg&0x01)>0) { /* bit 0 = hold SSG-EG */ 989 | if ((this.ssg&0x02)>0) this.ssgn = 4; /* set inversion flag */ 990 | if (this.state!==_EG.ATT&&!(this.ssgn^(this.ssg&0x04))) this.volume = _ENV.MAX_ATT_INDEX; /* force attenuation level during decay phases */ 991 | } 992 | else { /* loop SSG-EG */ 993 | /* toggle output inversion flag or reset Phase Generator */ 994 | if ((this.ssg&0x02)>0) this.ssgn ^= 4; 995 | else this.phase = 0; 996 | /* same as Key ON */ 997 | if (this.state!==_EG.ATT) { 998 | if ((this.rate.ar+this.rate.ksr)<94) /*32+62*/ 999 | this.state = (this.volume<=_ENV.MIN_ATT_INDEX)? 1000 | (this.sl===_ENV.MIN_ATT_INDEX?_EG.SUS:_EG.DEC): 1001 | _EG.ATT; 1002 | else { /* Attack Rate is maximal: directly switch to Decay or Sustain */ 1003 | this.volume = _ENV.MIN_ATT_INDEX; 1004 | this.state = (this.sl===_ENV.MIN_ATT_INDEX)?_EG.SUS:_EG.DEC; 1005 | } 1006 | } 1007 | } 1008 | /* recalculate EG output */ 1009 | if ((this.ssgn^(this.ssg&0x04))>0) this.vol_out = this.tl+(((0x200-this.volume)|0)&_ENV.MAX_ATT_INDEX); 1010 | else this.vol_out = this.tl+(this.volume|0); 1011 | } 1012 | }; 1013 | FM_CH.prototype.update_ssg_eg = function() {var j = this.SLOT.length; while (--j>-1) this.SLOT[j].update_ssg_eg();}; 1014 | function update_ssg_eg_channels(x) {var i = x.CH.length; while (--i>-1) x.CH[i].update_ssg_eg();} 1015 | 1016 | FM_SLOT.prototype.update_phase_lfo = function(x, pms, block_fnum) { 1017 | var off = LFO.pm_table[(((block_fnum&0x7f0)>>4)<<8)+pms+x.OPN.lfo.PM]; 1018 | if (off) { /* LFO phase modulation active */ 1019 | if (this.DT<0) { 1020 | console.log("FM_SLOT::update_phase_lfo - invalid DT",this.DT); 1021 | if (cfg.strict) throw new Error("FM_SLOT::update_phase_lfo - invalid DT="+this.DT); 1022 | else return; 1023 | } 1024 | var blk, kc, fc; 1025 | /* there are 2048 FNUMs that can be generated using FNUM/BLK registers 1026 | but LFO works with one more bit of a precision so we really need 4096 elements */ 1027 | block_fnum = off+(block_fnum<<1); 1028 | blk = (block_fnum&0x7000)>>12; 1029 | block_fnum = block_fnum&0xfff; 1030 | kc = (blk<<2)|OPN.fktable[block_fnum>>8]; /* keyscale code */ 1031 | /* (frequency) phase increment counter */ 1032 | if (cfg.mode) { // vb 1033 | fc = ((x.OPN.fn.table[block_fnum]>>(7-blk))+x.OPN.ST.dt_tab[this.DT][kc]); 1034 | if (fc<0) fc += x.OPN.fn.max; /* (frequency) phase overflow (credits to Nemesis) */ 1035 | } 1036 | else fc = (((block_fnum<<5)>>(7-blk))+x.OPN.ST.dt_tab[this.DT][kc])&_DT.MASK; // gpgx 1037 | this.phase +=(fc*this.rate.mul)>>1; /* update phase */ 1038 | } 1039 | else this.phase += this.Incr; /* LFO phase modulation = zero */ 1040 | }; 1041 | FM_SLOT.prototype.update_phase_lfo_precalc = function(x, fc, kc) { 1042 | if (fc!==-1) { 1043 | var finc; 1044 | if (cfg.mode) { // vb 1045 | finc = (fc+x.OPN.ST.dt_tab[this.DT][kc]); 1046 | if (finc<0) finc += x.OPN.fn.max; /* (frequency) phase overflow (credits to Nemesis) */ 1047 | } 1048 | else finc = (fc+x.OPN.ST.dt_tab[this.DT][kc])&_DT.MASK; // gpgx 1049 | this.phase += (finc*this.rate.mul)>>1; /* update phase */ 1050 | } 1051 | else this.phase += this.Incr; 1052 | }; 1053 | FM_CH.prototype.update_phase_lfo = function(x) { 1054 | var pms = this.pms, block_fnum = this.block_fnum; 1055 | //var i = this.SLOT.length; while (--i>-1) this.SLOT[i].update_phase_lfo(x, pms, block_fnum); 1056 | var blk, kc, fc; 1057 | var off = LFO.pm_table[(((block_fnum&0x7f0)>>4)<<8)+pms+x.OPN.lfo.PM]; 1058 | if (off) { 1059 | block_fnum = off+(block_fnum<<1); 1060 | blk = (block_fnum&0x7000)>>12; 1061 | block_fnum = block_fnum&0xfff; 1062 | kc = (blk<<2)|OPN.fktable[block_fnum>>8]; /* keyscale code */ 1063 | if (cfg.mode) { // vb 1064 | fc = (x.OPN.fn.table[block_fnum]>>(7-blk)); 1065 | } 1066 | else fc = ((block_fnum<<5)>>(7-blk)); 1067 | } 1068 | else { 1069 | fc = -1; 1070 | } 1071 | var i = this.SLOT.length; while (--i>-1) this.SLOT[_SLOT[i]].update_phase_lfo_precalc(x, fc, kc); 1072 | } 1073 | 1074 | /* update phase increment and envelope generator */ 1075 | FM_SLOT.prototype.refresh_fc_eg = function(x, fc, kc) { 1076 | if (this.DT<0) { 1077 | console.log("FM_SLOT::refresh_fc_eg - invalid DT",this.DT); 1078 | if (cfg.strict) throw new Error("FM_SLOT::refresh_fc_eg - invalid DT="+this.DT); 1079 | else return; 1080 | } 1081 | if (cfg.debug>1) console.log("OPN.ST.dt_tab["+this.DT+"]["+kc+"]",x.OPN.ST.dt_tab[this.DT][kc]); 1082 | fc += x.OPN.ST.dt_tab[this.DT][kc]; /* add detune value */ 1083 | /* (frequency) phase overflow (credits to Nemesis) */ 1084 | if (cfg.mode) {if (fc<0) fc += x.OPN.fn.max;} // vb 1085 | else fc &= _DT.MASK; // gpgx 1086 | this.Incr = (fc*this.rate.mul)>>1; /* (frequency) phase increment counter */ 1087 | kc = kc>>this.KSR; /* ksr */ 1088 | if (this.rate.ksr!==kc) { 1089 | this.rate.ksr = kc; 1090 | var q = (this.rate.ar+kc)|0; 1091 | if ((q)<94) { /*32+62*/ /* recalculate envelope generator rates */ 1092 | this.eg.sh.ar = _EG.rate_shift[q]; 1093 | this.eg.sel.ar = _EG.rate_select[q]; 1094 | } 1095 | else { /* verified by Nemesis on real hardware (Attack phase is blocked) */ 1096 | this.eg.sh.ar = 0; 1097 | this.eg.sel.ar = 18*_EG.RATE_STEPS; 1098 | } 1099 | q = (this.rate.d1r+kc)|0; 1100 | this.eg.sh.d1r = _EG.rate_shift[q]; 1101 | this.eg.sel.d1r = _EG.rate_select[q]; 1102 | q = (this.rate.d2r+kc)|0; 1103 | this.eg.sh.d2r = _EG.rate_shift[q]; 1104 | this.eg.sel.d2r = _EG.rate_select[q]; 1105 | q = (this.rate.rr+kc)|0; 1106 | this.eg.sh.rr = _EG.rate_shift[q]; 1107 | this.eg.sel.rr = _EG.rate_select[q]; 1108 | } 1109 | }; 1110 | /* update phase increment counters */ 1111 | FM_CH.prototype.refresh_fc_eg = function(x) { 1112 | if (this.SLOT[_SLOT[0]].Incr===-1) { 1113 | var fc = this.fc|0, kc = this.kcode|0; 1114 | if (cfg.debug>1) console.log("FM_CH::refresh_fc_eg",fc,kc); 1115 | var i = this.SLOT.length; while (--i>-1) this.SLOT[_SLOT[i]].refresh_fc_eg(x, fc, kc); 1116 | } 1117 | }; 1118 | 1119 | FM_SLOT.prototype.calcVol = function(AM){return (this.vol_out+(AM&this.AMmask))|0;}; 1120 | 1121 | var op_calc; 1122 | if (cfg.mode) op_calc = function(phase, env, pm, fb) { // vb 1123 | var p = (env<<3)+_YM.sin[ 1124 | (((phase&~_YM.FREQ_MASK)+(fb?pm:pm<<15))>>_YM.FREQ_SH)&_SIN.MASK 1125 | ]; 1126 | if (p>=_TL.TAB_LEN) return 0; 1127 | return _TL.tab[p]; 1128 | }; 1129 | else op_calc = function(phase, env, pm, fb) { // gpgx 1130 | var p = (env<<3)+_YM.sin[(fb?(phase+pm)>>_SIN.BITS:(phase>>_SIN.BITS)+(pm>>1))&_SIN.MASK]; 1131 | if (p>=_TL.TAB_LEN) return 0; 1132 | return _TL.tab[p]; 1133 | }; 1134 | 1135 | FM_SLOT.prototype.calculate = function(inp, am, asFB) { 1136 | var eg_out = (this.vol_out+(am&this.AMmask))|0,//this.calcVol(am), // inline FM_SLOT.calcVol(am) for speed 1137 | val = 0; 1138 | if (asFB) { 1139 | var o = (this.out[0]+this.out[1])|0; 1140 | this.out[0] = this.out[1]|0; 1141 | val = this.out[0]|0; 1142 | if (eg_out<_ENV.QUIET) { 1143 | if (!inp) o = 0; 1144 | this.out[1] = op_calc(this.phase, eg_out, (o<1&&cfg.maxcalc>0); 1157 | var AM = x.OPN.lfo.AM>>this.ams; 1158 | //if (this.muted) return; 1159 | var eg_out, val; 1160 | var i, outs = ['x','c1','m2','c2']; 1161 | this.outputs.m2 = 0, this.outputs.c1 = 0, this.outputs.c2 = 0, this.outputs.mem = 0; this.outputs.x = 0; 1162 | this.outputs[this.mem.connect] = this.mem.value; /* restore delayed sample (MEM) value to m2 or c2 */ 1163 | //console.log("CH::calculate",this.connect,this.mem); 1164 | /* SLOT 1 */ 1165 | i = 0; val = this.SLOT[_SLOT[i]].calculate(this.FB, AM, 1); 1166 | if (val!==0) { 1167 | if (this.connect[i]==='x') this.outputs.x = val, this.outputs.mem = val, this.outputs.c1 = val, this.outputs.c2 = val; // algorithm 5 1168 | else this.outputs[this.connect[i]] += val; 1169 | } 1170 | /* SLOT 3 */ 1171 | i = 2; val = this.SLOT[_SLOT[i]].calculate(this.outputs[outs[i]], AM, 0); 1172 | if (val!==0) { 1173 | if (this.connect[i]==='x') {} 1174 | else this.outputs[this.connect[i]] += val; 1175 | } 1176 | /* SLOT 2 */ 1177 | i = 1; val = this.SLOT[_SLOT[i]].calculate(this.outputs[outs[i]], AM, 0); 1178 | if (val!==0) { 1179 | if (this.connect[i]==='x') {} 1180 | else this.outputs[this.connect[i]] += val; 1181 | } 1182 | /* SLOT 4 */ 1183 | i = 3; val = this.SLOT[_SLOT[i]].calculate(this.outputs[outs[i]], AM, 0); 1184 | if (val!==0) { 1185 | if (this.connect[i]==='x') {} 1186 | else this.outputs[this.connect[i]] += val; 1187 | } 1188 | this.mem.value = this.outputs.mem|0; /* store current MEM */ 1189 | if (this.pms) { /* update phase counters AFTER output calculations */ 1190 | if ((x.OPN.ST.mode&0xC0)>0&&this.canCSM) { /* add support for 3 slot mode */ 1191 | this.SLOT[_SLOT[0]].update_phase_lfo(x, this.pms, x.OPN.SL3.block_fnum[1]); 1192 | this.SLOT[_SLOT[1]].update_phase_lfo(x, this.pms, x.OPN.SL3.block_fnum[2]); 1193 | this.SLOT[_SLOT[2]].update_phase_lfo(x, this.pms, x.OPN.SL3.block_fnum[0]); 1194 | this.SLOT[_SLOT[3]].update_phase_lfo(x, this.pms, this.block_fnum); 1195 | } 1196 | else this.update_phase_lfo(x); 1197 | } 1198 | else { /* no LFO phase modulation */ 1199 | this.SLOT[_SLOT[0]].phase += this.SLOT[_SLOT[0]].Incr; 1200 | this.SLOT[_SLOT[1]].phase += this.SLOT[_SLOT[1]].Incr; 1201 | this.SLOT[_SLOT[2]].phase += this.SLOT[_SLOT[2]].Incr; 1202 | this.SLOT[_SLOT[3]].phase += this.SLOT[_SLOT[3]].Incr; 1203 | } 1204 | if (msg_out) 1205 | msg += "; m2="+this.outputs.m2+";c1="+this.outputs.c1+";c2="+this.outputs.c2+";out="+this.outputs.out, 1206 | console.log("FM_CH::calc",this.ALGO,msg), 1207 | --cfg.maxcalc; 1208 | }; 1209 | 1210 | /* write a OPN mode register 0x20-0x2f */ 1211 | OPN.WriteMode = function(x,r,v) { 1212 | v = v&0xff; 1213 | switch (r) { 1214 | case 0x21: break; // test mode 1215 | case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/ym2612) */ 1216 | if (v&8) { /* LFO enabled ? */ 1217 | if (cfg.mode) x.OPN.lfo.timer_overflow = LFO.samples_per_step[v&7]<<_YM.LFO_SH; // vb 1218 | else x.OPN.lfo.timer_overflow = LFO.samples_per_step[v&7]; // gpgx 1219 | } 1220 | else { /* hold LFO waveform in reset state */ 1221 | x.OPN.lfo.timer_overflow = 0; 1222 | x.OPN.lfo.timer = 0; 1223 | x.OPN.lfo.cnt = 0; 1224 | x.OPN.lfo.AM = 126; 1225 | x.OPN.lfo.PM = 0; 1226 | } 1227 | break; 1228 | case 0x24: /* timer A High 8*/ 1229 | x.OPN.ST.TA = (x.OPN.ST.TA&0x03)|(((v)|0)<<2); 1230 | x.OPN.ST.TAL = (1024-x.OPN.ST.TA); 1231 | if (cfg.mode) x.OPN.ST.TAL <<= _YM.TIMER_SH; // vb 1232 | break; 1233 | case 0x25: /* timer A Low 2*/ 1234 | x.OPN.ST.TA = (x.OPN.ST.TA&0x3fc)|(v&3); 1235 | x.OPN.ST.TAL = (1024-x.OPN.ST.TA); 1236 | if (cfg.mode) x.OPN.ST.TAL <<= _YM.TIMER_SH; // vb 1237 | break; 1238 | case 0x26: /* timer B */ 1239 | x.OPN.ST.TB = v; 1240 | if (cfg.mode) x.OPN.ST.TBL = (256-v)<<(_YM.TIMER_SH+4); // vb 1241 | else x.OPN.ST.TBL = (256-v)<<4; // gpgx 1242 | break; 1243 | case 0x27: /* mode, timer control */ 1244 | set_timers(x,v); 1245 | break; 1246 | case 0x28: /* key on / off */ 1247 | var c = v&0x03; if (c===3) break; 1248 | if (v&0x04) c += 3; /* CH 4-6 */ 1249 | (function(ch){ 1250 | if (v&0x10) ch.keyOn(x,_SLOT[0]); else ch.keyOff(x,_SLOT[0]); 1251 | if (v&0x20) ch.keyOn(x,_SLOT[1]); else ch.keyOff(x,_SLOT[1]); 1252 | if (v&0x40) ch.keyOn(x,_SLOT[2]); else ch.keyOff(x,_SLOT[2]); 1253 | if (v&0x80) ch.keyOn(x,_SLOT[3]); else ch.keyOff(x,_SLOT[3]); 1254 | })(x.CH[c]); 1255 | break; 1256 | } 1257 | }; 1258 | 1259 | /* write a OPN register (0x30-0xff) */ 1260 | OPN.WriteReg = function(x,r,v) { 1261 | v = v&0xff; 1262 | var c = OPN.CHAN(r), 1263 | sl = OPN.SLOT(r); 1264 | if (c>=3) { /* 0xX3,0xX7,0xXB,0xXF */ 1265 | if (cfg.strict) throw new Error("OPN_Write - unsupported channel "+c+' or slot '+sl+' from {$'+r.toString(16)+',$'+v.toString(16)+'}'); // 0x?3, 0x?7, 0x?B, 0x?F 1266 | else return; 1267 | } 1268 | if (r>=0x100) c += 3; 1269 | var s = _SLOT[sl]; 1270 | switch (r&0xf0) { 1271 | case 0x30: /* DET , MUL */ 1272 | x.CH[c].set_det_mul(x, sl, v); 1273 | break; 1274 | case 0x40: /* TL */ 1275 | x.CH[c].set_tl(sl, v); 1276 | break; 1277 | case 0x50: /* KS, AR */ 1278 | x.CH[c].set_ar_ksr(sl, v); 1279 | break; 1280 | case 0x60: /* bit7 = AM ENABLE, DR */ 1281 | x.CH[c].set_dr(sl, v); 1282 | x.CH[c].SLOT[sl].AMmask = (v&0x80)?~0:0; 1283 | break; 1284 | case 0x70: /* SR */ 1285 | x.CH[c].set_sr(sl, v); 1286 | break; 1287 | case 0x80: /* SL, RR */ 1288 | x.CH[c].set_sl_rr(sl, v); 1289 | break; 1290 | case 0x90: /* SSG-EG */ 1291 | (function(S){ 1292 | S.ssg = v&0x0f; 1293 | /* recalculate EG output */ 1294 | if (S.state>_EG.REL) { 1295 | if ((S.ssg&0x08)>0&&(S.ssgn^(S.ssg&0x04))>0) 1296 | S.vol_out = S.tl+(((0x200-S.volume)|0)&_ENV.MAX_ATT_INDEX); 1297 | else 1298 | S.vol_out = S.tl+(S.volume|0); 1299 | } 1300 | })(x.CH[c].SLOT[sl]); 1301 | break; 1302 | case 0xa0: 1303 | var fn, blk; 1304 | switch (sl) { 1305 | case 0: /* 0xa0-0xa2 : FNUM1 */ 1306 | //fn = ((x.OPN.ST.fn_h&7)<<8)+v; // old 1307 | //blk = (x.OPN.ST.fn_h>>3)&0xff; // old 1308 | fn = ((x.CH[c].fn_h&7)<<8)+v; 1309 | blk = (x.CH[c].fn_h>>3)&0xff; 1310 | x.CH[c].kcode = (blk<<2)|OPN.fktable[fn>>7]; /* keyscale code */ 1311 | /* phase increment counter */ 1312 | if (cfg.mode) x.CH[c].fc = x.OPN.fn.table[fn<<1]>>(7-blk); // vb 1313 | else x.CH[c].fc = (fn<<6)>>(7-blk); // gpgx 1314 | x.CH[c].block_fnum = (blk<<11)|fn; /* store fnum in clear form for LFO PM calculations */ 1315 | x.CH[c].SLOT[_SLOT[0]].Incr = -1; 1316 | if (cfg.debug>2) console.log('block_fnum=x',x.CH[c].block_fnum.toString(16),' kcode=',x.CH[c].kcode.toString(16),' fc=',x.CH[c].fc.toString(16)); 1317 | break; 1318 | case 1: /* 0xa4-0xa6 : FNUM2,BLK */ 1319 | //x.OPN.ST.fn_h = (v&0x3f)|0; // old 1320 | x.CH[c].fn_h = (v&0x3f)|0; 1321 | break; 1322 | case 2: /* 0xa8-0xaa : 3CH FNUM1 */ 1323 | if (r<0x100) { 1324 | fn = ((x.OPN.SL3.fn_h&7)<<8)+v; 1325 | blk = x.OPN.SL3.fn_h>>3; 1326 | x.OPN.SL3.kcode[c] = (blk<<2)|OPN.fktable[fn>>7]; /* keyscale code */ 1327 | /* phase increment counter */ 1328 | if (cfg.mode) x.OPN.SL3.fc[c] = x.OPN.fn.table[fn<<1]>>(7-blk); // vb 1329 | else x.OPN.SL3.fc[c] = (fn<<6)>>(7-blk); // gpgx 1330 | x.OPN.SL3.block_fnum[c] = (blk<<11)|fn; 1331 | x.CH[2].SLOT[_SLOT[0]].Incr = -1; 1332 | } 1333 | break; 1334 | case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ 1335 | if (r<0x100) x.OPN.SL3.fn_h = v&0x3f; 1336 | break; 1337 | } 1338 | break; 1339 | case 0xb0: 1340 | switch (sl) { 1341 | case 0: /* 0xb0-0xb2 : FB,ALGO */ 1342 | var fb = (v>>3)&7; 1343 | x.CH[c].ALGO = v&7; 1344 | if (cfg.mode) x.CH[c].FB = fb?fb+6:0; // vb 1345 | else x.CH[c].FB = fb; // gpgx 1346 | //console.log("C[",c,']=',x.CH[c].ALGO,','+x.CH[c].FB); 1347 | x.CH[c].setupConnection(); 1348 | break; 1349 | case 1: /* 0xb4-0xb6 : L , R , AMS , PMS */ 1350 | x.CH[c].pms = (v&7)<<5; /* b0-2 PMS */ /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ 1351 | x.CH[c].ams = LFO.ams_depth_shift[(v>>4)&0x03]; /* b4-5 AMS */ 1352 | /* PAN : b7 = L, b6 = R */ 1353 | // TODO: merge pan[] into FM_CH 1354 | x.CH[c].pan[0] = v&0x80?_YM.bitmask:0; // new method 1355 | x.CH[c].pan[1] = v&0x40?_YM.bitmask:0; // new method 1356 | //x.OPN.pan[(c<<1)+0] = v&0x80?_YM.bitmask:0; // old method 1357 | //x.OPN.pan[(c<<1)+1] = v&0x40?_YM.bitmask:0; // old method 1358 | break; 1359 | } 1360 | break; 1361 | } 1362 | }; 1363 | 1364 | 1365 | function reset_channels(x, num) {if (num>x.CH.length) num = x.CH.length; while (--num>-1) x.CH[num].reset();} 1366 | 1367 | /* prescaler set (and make time tables) */ 1368 | OPN.SetPrescaler = function(x, r) { 1369 | x.ratio = r||144; 1370 | x.OPN.ST.scale = (x.OPN.ST.clock/x.OPN.ST.rate)/x.ratio; 1371 | if (cfg.debug) console.log("init_timetables",x.OPN.ST.clock,x.OPN.ST.rate,x.ratio,x.OPN.ST.scale); 1372 | // init_timetables 1373 | var d, i, q; 1374 | var z = x.OPN.ST.scale*(1<<(_YM.FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ // vb 1375 | /* build DETUNE table */ 1376 | for (d=0; d<4; ++d) { 1377 | for (i=0; i<32; ++i) { 1378 | q = _DT.tab[(d<<5)+i]; // gpgx 1379 | if (cfg.mode) q *= z; // vb 1380 | x.OPN.ST.dt_tab[d][i] = q|0; 1381 | x.OPN.ST.dt_tab[d+4][i] = -x.OPN.ST.dt_tab[d][i]; 1382 | } 1383 | } 1384 | if (cfg.debug>2) console.log("init_timetables dt_tab",ym.OPN.ST.dt_tab); 1385 | /* there are 2048 FNUMs that can be generated using FNUM/BLK registers 1386 | but LFO works with one more bit of a precision so we really need 4096 elements */ 1387 | /* calculate fnumber -> increment counter table */ 1388 | /* freq table for octave 7 */ 1389 | /* OPN phase increment counter = 20bit */ 1390 | /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ 1391 | /* where sample clock is M/144 */ 1392 | /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ 1393 | /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ 1394 | q = 32.0*z, i = 4096; while (--i>-1) {x.OPN.fn.table[i] = (i*q)|0;} // vb 1395 | x.OPN.fn.max = (0x20000*z)|0; /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ // vb 1396 | x.OPN.eg.timer_add = (x.OPN.ST.scale*(1<<_YM.EG_SH))|0; // vb 1397 | x.OPN.eg.timer_overflow = (3)*(1<<_YM.EG_SH); /* EG is updated every 3 samples */ // vb 1398 | x.OPN.lfo.timer_add = (x.OPN.ST.scale*(1<<_YM.LFO_SH))|0; /* LFO timer increment (every samples) */ // vb 1399 | x.OPN.ST.timer_base = (x.OPN.ST.scale*(1<<_YM.TIMER_SH))|0; /* Timers increment (every samples) */ // vb 1400 | }; 1401 | 1402 | /* initialize generic tables */ 1403 | function init_tables(ym) { 1404 | if (cfg.debug) console.log("init_tables",ym.CH.length); 1405 | var d, i, x; // signed int 1406 | var n; // signed int 1407 | var o, m; // double 1408 | var q, z; 1409 | var PI = Math.PI, LOG = Math.log, POW = Math.pow, SIN = Math.sin; 1410 | /* build Linear Power Table */ 1411 | var tmp = (_ENV.STEP/32.0), sh = (1<<16), rl2 = _TL.RES_LEN<<1; 1412 | for (x=0; x<_TL.RES_LEN; ++x) { 1413 | m = sh/POW(2, (x+1)*tmp); 1414 | //m = m|0; // m = Math.floor(m); // extraneous, folded into next calculation +neo 1415 | /* we never reach (1<<16) here due to the (x+1) */ 1416 | /* result fits within 16 bits at maximum */ 1417 | //n = m|0; /* 16 bits here */ 1418 | //n >>= 4; /* 12 bits here */ 1419 | n = (m|0)>>4; 1420 | if (n&1) n = (n>>1)+1; /* round to nearest */ 1421 | else n = n>>1; 1422 | /* 11 bits here (rounded) */ 1423 | n <<= 2; /* 13 bits here (as in real chip) */ 1424 | z = x<<1; /* 14 bits (with sign bit) */ 1425 | _TL.tab[z+0] = n; 1426 | _TL.tab[z+1] = -n; 1427 | /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ 1428 | /* s = sign bit */ 1429 | /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ 1430 | /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ 1431 | /* any value above 13 (included) would be discarded. */ 1432 | for (i=1; i<13; ++i) { 1433 | q = (z+0+i*rl2)|0; 1434 | _TL.tab[q] = _TL.tab[z]>>i; 1435 | _TL.tab[q+1] = -_TL.tab[q]; 1436 | } 1437 | } 1438 | //console.log("TL_TABLE",_TL.tab.join(", ")); 1439 | /* build Logarithmic Sinus table */ 1440 | q = PI/_SIN.LEN, z = 8.0/LOG(2.0), tmp = 2.0*4/_ENV.STEP; for (i=0; i<_SIN.LEN; ++i) { /* non-standard sinus */ 1441 | m = SIN(((i<<1)+1.0)*q); /* checked against the real chip */ 1442 | /* we never reach zero here due to ((i*2)+1) */ 1443 | /* convert to 'decibels' */ 1444 | if (m>0.0) o = LOG(1.0/m)*z; 1445 | else o = LOG(-1.0/m)*z; 1446 | //o = o/(_ENV.STEP/4); // folded into next calculation +neo 1447 | n = (o*tmp)|0; //n = (2.0*o)|0; 1448 | if (n&1) n = (n>>1)+1; /* round to nearest */ 1449 | else n = n>>1; 1450 | _YM.sin[i] = (n<<1)+(m>=0.0?0:1); /* 13-bits (8.5) value is formatted for above 'Power' table */ 1451 | } 1452 | //console.log("SIN_TAB",_YM.sin.join(", ")); 1453 | /* build LFO PM modulation table */ 1454 | for (i=0; i<8; ++i) { /* 8 PM depths */ 1455 | for (n=0; n<128; ++n) { /* 7 bits meaningful of F-NUMBER */ 1456 | for (x=0; x<8; ++x) { 1457 | z = 0; 1458 | for (o=0; o<7; ++o) { /* 7 bits */ 1459 | if ((n&(1<0) { /* only if bit "bit_tmp" is set */ 1460 | z += LFO.pm_output[(o<<3)+i][x]; 1461 | } 1462 | } 1463 | /* 32 steps for LFO PM (sinus) */ 1464 | d = (n<<8)+(i<<5); // fnum*32*8 + i*32 1465 | LFO.pm_table[d+x+0] = z; 1466 | LFO.pm_table[d+(x^7)+8] = z; 1467 | LFO.pm_table[d+x+16] = -z; 1468 | LFO.pm_table[d+(x^7)+24] = -z; 1469 | } 1470 | } 1471 | } 1472 | } 1473 | 1474 | /**** END FM DEFS ****/ 1475 | 1476 | /**** YM2612 API based on genplus-gx ****/ 1477 | 1478 | /* initialize ym2612 emulator */ 1479 | Y.prototype.init = function(clock,rate) { 1480 | if (cfg.debug) console.log("OPN::init("+clock+','+rate+")"); 1481 | if (!this.chip) this.chip = new YMX(clock, rate); 1482 | else this.chip.ST.clock = clock||7670448, this.chip.ST.rate = rate||44100; 1483 | this.ratio = 144; /* chip is running a VCLK / 144 = MCLK / 7 / 144 */ 1484 | this.interval = cfg.mode; 1485 | this.start = 0; 1486 | this.count = 0; 1487 | init_tables(this.chip); 1488 | }; 1489 | /* reset OPN registers */ 1490 | Y.prototype.reset = function() { 1491 | if (cfg.debug) console.log("OPN::reset"); 1492 | (function(x){ 1493 | var i; 1494 | OPN.SetPrescaler(x, 144); /* YM2612 prescaler is fixed to 1/6, one sample (6 mixed channels) is output for each 24 FM clocks */ 1495 | x.OPN.eg.timer = 0; 1496 | x.OPN.eg.cnt = 0; 1497 | x.OPN.lfo.timer_overflow = 0; 1498 | x.OPN.lfo.timer = 0; 1499 | x.OPN.lfo.cnt = 0; 1500 | x.OPN.lfo.AM = 126; 1501 | x.OPN.lfo.PM = 0; 1502 | x.OPN.ST.TAC = 0; 1503 | x.OPN.ST.TBC = 0; 1504 | x.OPN.SL3.key_csm = 0; 1505 | x.dacen = 0; 1506 | x.dacout = 0; 1507 | set_timers(x, 0x30); 1508 | x.OPN.ST.TB = 0; 1509 | x.OPN.ST.TA = 0; 1510 | if (cfg.mode) { // vb 1511 | x.OPN.ST.TBL = 256<<(_YM.TIMER_SH+4); 1512 | x.OPN.ST.TAL = 1024<<(_YM.TIMER_SH); 1513 | } 1514 | else { // gpgx 1515 | x.OPN.ST.TBL = 256<<4; 1516 | x.OPN.ST.TAL = 1024; 1517 | } 1518 | reset_channels(x, 6); 1519 | //for (i=0; i<6; ++i) {if (i!=0) x.CH[i].muted = 1;} 1520 | i = 0xb6; while (i>=0xb4) { 1521 | if ((i&3)!==3) 1522 | OPN.WriteReg(x, i, 0xc0), 1523 | OPN.WriteReg(x, i|0x100, 0xc0); 1524 | --i; 1525 | } 1526 | i = 0xb2; while (i>=30) { 1527 | if ((i&3)!==3) 1528 | OPN.WriteReg(x, i, 0), 1529 | OPN.WriteReg(x, i|0x100, 0); 1530 | --i; 1531 | } 1532 | })(this.chip); 1533 | this.start = 0; 1534 | this.count = 0; 1535 | }; 1536 | /* ym2612 write */ 1537 | Y.prototype.write = function(a,v) { 1538 | if (cfg.debug>1) console.log("OPN::write",a.toString(16),v.toString(16)); 1539 | v &= 0xff; /* adjust to 8 bit bus */ 1540 | this.chip.OPN.ST.address = a&0x1ff; 1541 | //switch (a) { 1542 | // case 0: /* address port 0 */ 1543 | // this.chip.OPN.ST.address = v; 1544 | // break; 1545 | // case 2: /* address port 1 */ 1546 | // this.chip.OPN.ST.address = v|0x100; 1547 | // break; 1548 | // default: /* data port */ 1549 | var addr = this.chip.OPN.ST.address; /* verified by Nemesis on real YM2612 */ 1550 | switch (addr&0x1f0) { 1551 | case 0x20: /* 0x20-0x2f Mode */ 1552 | switch (addr) { 1553 | case 0x2a: /* DAC data (ym2612) */ 1554 | this.chip.dacout = ((v-0x80)|0)<<6; /* convert to 14-bit output */ 1555 | break; 1556 | case 0x2b: /* DAC Sel (ym2612) */ 1557 | this.chip.dacen = !!(v&0x80); /* b7 = dac enable */ 1558 | break; 1559 | default: /* OPN section */ 1560 | OPN.WriteMode(this.chip, addr, v); /* write register */ 1561 | break; 1562 | } 1563 | break; 1564 | default: /* 0x30-0xff OPN section */ 1565 | OPN.WriteReg(this.chip, addr, v); /* write register */ 1566 | break; 1567 | } 1568 | //break; 1569 | //} 1570 | }; 1571 | Y.prototype.read = function(x){return this.chip.OPN.ST.status&0xff;}; 1572 | 1573 | /* Generate samples for ym2612 */ 1574 | Y.prototype.update = function(len) { 1575 | //// update length is given in samples 1576 | //// but needs to calculate in chip cycles, 1577 | //// adjust afterwards +neo 1578 | var num = len*this.ratio; // num cycles 1579 | if(cfg.debug) console.log("==== YM::update","samples="+len,"cycles="+num); 1580 | var buf = [[],[]], j, lt, rt; 1581 | var is_csm = !!(this.chip.OPN.ST.mode&0xc0), dis_csm; 1582 | var i = -1; while (++i0)) cfg.debugArr[cfg.debugArr.length] = this.chip.CH[j].outputs.out, --cfg.debugLocal; 1610 | /* 14-bit accumulator channels outputs (range is -8192;+8192) */ 1611 | if (this.chip.CH[j].outputs.out>8192) this.chip.CH[j].outputs.out = 8192; 1612 | else if (this.chip.CH[j].outputs.out<-8192) this.chip.CH[j].outputs.out = -8192; 1613 | /* stereo DAC channels outputs mixing */ 1614 | //if (j===0) msg[i] = (this.chip.CH[j].out&this.chip.OPN.pan[(j<<1)+0]); 1615 | //lt += this.chip.CH[j].out&this.chip.OPN.pan[(j<<1)+0]; // old method 1616 | //rt += this.chip.CH[j].out&this.chip.OPN.pan[(j<<1)+1]; // old method 1617 | if (!this.chip.CH[j].muted) // new method 1618 | lt += (this.chip.CH[j].outputs.out&this.chip.CH[j].pan[0])|0, 1619 | rt += (this.chip.CH[j].outputs.out&this.chip.CH[j].pan[1])|0; 1620 | if (dis_csm&&this.chip.CH[j].canCSM) { /* CSM Mode Key ON still disabled */ 1621 | /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ 1622 | this.chip.CH[j].keyOffCSM(this.chip, _SLOT[0]); 1623 | this.chip.CH[j].keyOffCSM(this.chip, _SLOT[1]); 1624 | this.chip.CH[j].keyOffCSM(this.chip, _SLOT[2]); 1625 | this.chip.CH[j].keyOffCSM(this.chip, _SLOT[3]); 1626 | } 1627 | } 1628 | /* advance LFO */ 1629 | advance_lfo(this.chip); 1630 | /* EG is updated every 3 samples */ 1631 | if (cfg.mode) { // vb 1632 | this.chip.OPN.eg.timer += this.chip.OPN.eg.timer_add; /* advance envelope generator */ 1633 | while (this.chip.OPN.eg.timer>=this.chip.OPN.eg.timer_overflow) { 1634 | this.chip.OPN.eg.timer -= this.chip.OPN.eg.timer_overflow; 1635 | ++this.chip.OPN.eg.cnt; 1636 | advance_eg_channels(this.chip, this.chip.OPN.eg.cnt); 1637 | } 1638 | } 1639 | else { // gpgx 1640 | ++this.chip.OPN.eg.timer; /* advance envelope generator */ 1641 | if (this.chip.OPN.eg.timer>=3) { 1642 | this.chip.OPN.eg.timer = 0; 1643 | ++this.chip.OPN.eg.cnt; 1644 | advance_eg_channels(this.chip, this.chip.OPN.eg.cnt); 1645 | } 1646 | } 1647 | /* buffering */ 1648 | buf[0][i] = lt; 1649 | buf[1][i] = rt; 1650 | /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ 1651 | /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ 1652 | this.chip.OPN.SL3.key_csm <<= 1; 1653 | INTERNAL_TIMER_A(this.chip); /* timer A control */ 1654 | if (dis_csm) { /* CSM Mode Key ON still disabled */ 1655 | this.chip.OPN.SL3.key_csm = 0; /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ 1656 | } 1657 | } 1658 | /* timer B control */ 1659 | INTERNAL_TIMER_B(this.chip, len); 1660 | //if (cfg.debug>1) console.log("YM::update",msg); 1661 | //// post-update adjustments +neo 1662 | //this.count += num; 1663 | //var time = this.start; /* FM frame initial timestamp */ 1664 | //var out = [[],[]]; 1665 | //i = 0; j = 0; 1666 | //do { 1667 | // out[0][i] = buf[0][j|0]; /* left channel */ 1668 | // out[1][i] = buf[1][j|0]; /* right channel */ 1669 | // j += (z), ++i; 1670 | //time += this.ratio, ++i; /* increment time counter */ 1671 | //} while (i0) console.log(cfg.debugArr.join(", ")); 1675 | return buf; 1676 | }; 1677 | 1678 | /* DAC precision (normally 9-bit on real hardware, implemented through simple 14-bit channel output bitmasking) */ 1679 | Y.prototype.config = function(bits) { 1680 | _YM.bitmask = ~((1<<(_TL.BITS-bits))-1); 1681 | /* update L/R panning bitmasks */ 1682 | /*var i = -1; while (++i<12) { // 2out*6ch, ORIGINAL METHOD 1683 | if (this.chip.OPN.pan[i]) this.chip.OPN.pan[i] = _YM.bitmask; 1684 | }*/ 1685 | var i = this.chip.CH.length; while (--i>-1) { // 6ch*2out, NEW PER-CHANNEL PAN 1686 | if (this.chip.CH[i].pan[0]) this.chip.CH[i].pan[0] = _YM.bitmask; 1687 | if (this.chip.CH[i].pan[1]) this.chip.CH[i].pan[1] = _YM.bitmask; 1688 | } 1689 | }; 1690 | 1691 | /* Toggle channel muting +neo */ 1692 | Y.prototype.toggle = function(ch,m) { 1693 | if (ch<6) this.chip.CH[ch].muted = !m; 1694 | } 1695 | 1696 | /* debug output +neo */ 1697 | Y.prototype.toString = function(){return this.chip.toString();}; 1698 | 1699 | Y.prototype.load = function(state){}; 1700 | Y.prototype.save = function(state){}; 1701 | })(YM2612); 1702 | --------------------------------------------------------------------------------