mirror of
https://github.com/wassname/phaser.git
synced 2026-07-30 12:30:07 +08:00
You can now load any binary file via the Loader: game.load.binary(key, url, callback) - the optional callback allows for post-load processing before entering the Cache.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
|
||||
var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });
|
||||
|
||||
function preload() {
|
||||
|
||||
game.load.script('pt', 'wip/pt.js');
|
||||
game.load.binary('impulse', 'assets/audio/protracker/act_of_impulse.mod', modLoaded, this);
|
||||
game.load.binary('macrocosm', 'assets/audio/protracker/macrocosm.mod', modLoaded, this);
|
||||
game.load.binary('enigma', 'assets/audio/protracker/enigma.mod', modLoaded, this);
|
||||
game.load.binary('elysium', 'assets/audio/protracker/elysium.mod', modLoaded, this);
|
||||
|
||||
}
|
||||
|
||||
function modLoaded(key, data) {
|
||||
|
||||
console.log('module loaded:', key);
|
||||
|
||||
var buffer = new Uint8Array(data);
|
||||
|
||||
return buffer;
|
||||
|
||||
}
|
||||
|
||||
var sprite;
|
||||
var modsample = new Array();
|
||||
var module;
|
||||
|
||||
function create() {
|
||||
|
||||
module = new Protracker();
|
||||
module.buffer = game.cache.getBinary('elysium');
|
||||
module.parse();
|
||||
module.play();
|
||||
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
||||
if (module.channel[0].noteon) {sam1=module.channel[0].sample; modsamples=1;}else{modsamples=0;}
|
||||
if (module.channel[1].noteon) {sam2=module.channel[1].sample; modsamples=1;}else{modsamples=0;}
|
||||
if (module.channel[2].noteon) {sam3=module.channel[2].sample; modsamples=1;}else{modsamples=0;}
|
||||
if (module.channel[3].noteon) {sam4=module.channel[3].sample; modsamples=1;}else{modsamples=0;}
|
||||
|
||||
var posi=module.position;
|
||||
var patt=module.row;
|
||||
var bpm=module.bpm;
|
||||
var spd=module.speed;
|
||||
var modname=module.title;
|
||||
var modauth=module.signature;
|
||||
|
||||
}
|
||||
|
||||
function render() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,913 @@
|
||||
/*
|
||||
amiga protracker module player for web audio api
|
||||
(c) 2012-2013 firehawk/tda (firehawk@haxor.fi)
|
||||
|
||||
originally hacked together in a weekend, so please excuse
|
||||
me for the spaghetti code. :)
|
||||
|
||||
AMIGAAAAAAAAH!!
|
||||
|
||||
kinda sorta changelog:
|
||||
(apr 2013)
|
||||
- changed the logic for pattern break/jump. mod.pattern_skank now
|
||||
plays correctly.
|
||||
(feb 2013)
|
||||
- fixed NaN samples with mod.fractured and mod.multicolour (thanks Aegis!)
|
||||
(jan 2013)
|
||||
- fixed vibrato amplitude (was half of what it should be, apparently)
|
||||
- fixed to work on safari again (thanks Matt Diamond @ stackoverflow.com)
|
||||
(dec 2012)
|
||||
- replaced effect switch-statement with jumptables
|
||||
- fixed clicks (bad loops, empty samples)
|
||||
- fixed playback bug with sample-only rows
|
||||
- added amiga 500 lowpass filters (not 100% authentic, though)
|
||||
- added compressor to output
|
||||
- latest safari has broken web audio so chrome-only for now
|
||||
(aug 2012)
|
||||
- first version written from scratch
|
||||
|
||||
todo:
|
||||
- safari on ipad is broken again, it seems
|
||||
- fix more playback bugs
|
||||
* mod.black_queen (pattern loop has glitches)
|
||||
- properly test EEx delay pattern
|
||||
- implement the rest of the effects
|
||||
- optimize for more speed!! SPEEEED!!
|
||||
* switch to fixed point sample pointers, Math.floor() is _slow_ on iOS
|
||||
*/
|
||||
|
||||
// constructor for protracker player object
|
||||
function Protracker()
|
||||
{
|
||||
var i, t;
|
||||
|
||||
this.initialize();
|
||||
this.clearsong();
|
||||
|
||||
this.url="";
|
||||
this.loading=false;
|
||||
this.ready=false;
|
||||
this.playing=false;
|
||||
this.buffer=0;
|
||||
this.mixerNode=0;
|
||||
this.paused=false;
|
||||
this.repeat=false;
|
||||
this.separation=true;
|
||||
|
||||
this.palclock=true;
|
||||
|
||||
this.autostart=false;
|
||||
|
||||
this.onReady=function(){};
|
||||
this.onPlay=function(){};
|
||||
this.onStop=function(){};
|
||||
|
||||
// paula period values
|
||||
this.baseperiodtable=new Array(
|
||||
856,808,762,720,678,640,604,570,538,508,480,453,
|
||||
428,404,381,360,339,320,302,285,269,254,240,226,
|
||||
214,202,190,180,170,160,151,143,135,127,120,113);
|
||||
|
||||
// finetune multipliers
|
||||
this.finetunetable=new Array();
|
||||
for(t=0;t<16;t++) this.finetunetable[t]=Math.pow(2, (t-8)/12/8);
|
||||
|
||||
// calc tables for vibrato waveforms
|
||||
this.vibratotable=new Array();
|
||||
for(t=0;t<4;t++) {
|
||||
this.vibratotable[t]=new Array();
|
||||
for(i=0;i<64;i++) {
|
||||
switch(t) {
|
||||
case 0:
|
||||
this.vibratotable[t][i]=127*Math.sin(Math.PI*2*(i/64));
|
||||
break;
|
||||
case 1:
|
||||
this.vibratotable[t][i]=127-4*i;
|
||||
break;
|
||||
case 2:
|
||||
this.vibratotable[t][i]=(i<32)?127:-127;
|
||||
break;
|
||||
case 3:
|
||||
this.vibratotable[t][i]=(1-2*Math.random())*127;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effect jumptables
|
||||
this.effects_t0 = new Array(
|
||||
this.effect_t0_0, this.effect_t0_1, this.effect_t0_2, this.effect_t0_3, this.effect_t0_4, this.effect_t0_5, this.effect_t0_6, this.effect_t0_7,
|
||||
this.effect_t0_8, this.effect_t0_9, this.effect_t0_a, this.effect_t0_b, this.effect_t0_c, this.effect_t0_d, this.effect_t0_e, this.effect_t0_f);
|
||||
this.effects_t0_e = new Array(
|
||||
this.effect_t0_e0, this.effect_t0_e1, this.effect_t0_e2, this.effect_t0_e3, this.effect_t0_e4, this.effect_t0_e5, this.effect_t0_e6, this.effect_t0_e7,
|
||||
this.effect_t0_e8, this.effect_t0_e9, this.effect_t0_ea, this.effect_t0_eb, this.effect_t0_ec, this.effect_t0_ed, this.effect_t0_ee, this.effect_t0_ef);
|
||||
this.effects_t1 = new Array(
|
||||
this.effect_t1_0, this.effect_t1_1, this.effect_t1_2, this.effect_t1_3, this.effect_t1_4, this.effect_t1_5, this.effect_t1_6, this.effect_t1_7,
|
||||
this.effect_t1_8, this.effect_t1_9, this.effect_t1_a, this.effect_t1_b, this.effect_t1_c, this.effect_t1_d, this.effect_t1_e, this.effect_t1_f);
|
||||
this.effects_t1_e = new Array(
|
||||
this.effect_t1_e0, this.effect_t1_e1, this.effect_t1_e2, this.effect_t1_e3, this.effect_t1_e4, this.effect_t1_e5, this.effect_t1_e6, this.effect_t1_e7,
|
||||
this.effect_t1_e8, this.effect_t1_e9, this.effect_t1_ea, this.effect_t1_eb, this.effect_t1_ec, this.effect_t1_ed, this.effect_t1_ee, this.effect_t1_ef);
|
||||
|
||||
this.context = null;
|
||||
this.samplerate=44100;
|
||||
this.bufferlen=2048;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// create the web audio context
|
||||
Protracker.prototype.createContext = function()
|
||||
{
|
||||
this.context = new webkitAudioContext();
|
||||
this.samplerate=this.context.sampleRate;
|
||||
this.bufferlen=(this.samplerate > 44100) ? 4096 : 2048;
|
||||
|
||||
// fixed filter at 6kHz
|
||||
this.filterNode=this.context.createBiquadFilter();
|
||||
this.filterNode.frequency.value=6000
|
||||
|
||||
// "LED filter" at 3.5kHz - off by default
|
||||
this.lowpassNode=this.context.createBiquadFilter();
|
||||
this.lowpassNode.frequency.value=28867;
|
||||
|
||||
// mixer
|
||||
this.mixerNode=this.context.createJavaScriptNode(this.bufferlen, 1, 2);
|
||||
this.mixerNode.module=this;
|
||||
this.mixerNode.onaudioprocess=Protracker.prototype.mix;
|
||||
|
||||
// compressor for a bit of volume boost
|
||||
this.compressorNode=this.context.createDynamicsCompressor();
|
||||
|
||||
// patch up some cables :)
|
||||
this.mixerNode.connect(this.filterNode);
|
||||
this.filterNode.connect(this.lowpassNode);
|
||||
this.lowpassNode.connect(this.compressorNode);
|
||||
this.compressorNode.connect(this.context.destination);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// play loaded and parsed module with webaudio context
|
||||
Protracker.prototype.play = function()
|
||||
{
|
||||
if (this.context==null) this.createContext();
|
||||
|
||||
if (!this.ready) return false;
|
||||
if (this.paused) {
|
||||
this.paused=false;
|
||||
return true;
|
||||
}
|
||||
this.paused=false;
|
||||
this.initialize();
|
||||
this.flags=1+2;
|
||||
this.playing=true;
|
||||
this.onPlay();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// pause playback
|
||||
Protracker.prototype.pause = function()
|
||||
{
|
||||
if (!this.paused) {
|
||||
this.paused=true;
|
||||
} else {
|
||||
this.paused=false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// stop playback and release webaudio node
|
||||
Protracker.prototype.stop = function()
|
||||
{
|
||||
this.playing=false;
|
||||
this.onStop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// jump positions forward/back
|
||||
Protracker.prototype.jump = function(step)
|
||||
{
|
||||
this.tick=0;
|
||||
this.row=0;
|
||||
this.position+=step;
|
||||
this.flags=1+2;
|
||||
if (this.position<0) this.position=0;
|
||||
if (this.position >= this.songlen) this.stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// set whether module repeats after songlen
|
||||
Protracker.prototype.setrepeat = function(rep)
|
||||
{
|
||||
this.repeat=rep;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// set stereo separation mode (false=paula, true=betterpaula)
|
||||
Protracker.prototype.setseparation = function(sep)
|
||||
{
|
||||
this.separation=sep;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// set amiga video standard (false=NTSC, true=PAL)
|
||||
Protracker.prototype.setamigatype = function(clock)
|
||||
{
|
||||
this.palclock=clock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// set autostart to play immediately after loading
|
||||
Protracker.prototype.setautostart = function(st)
|
||||
{
|
||||
this.autostart=st;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// clear song data
|
||||
Protracker.prototype.clearsong = function()
|
||||
{
|
||||
this.title="";
|
||||
this.signature="";
|
||||
this.songlen=1;
|
||||
this.repeatpos=0;
|
||||
this.patterntable=new ArrayBuffer(128);
|
||||
for(i=0;i<128;i++) this.patterntable[i]=0;
|
||||
|
||||
this.channels=4;
|
||||
|
||||
this.sample=new Array();
|
||||
this.samples=31;
|
||||
for(i=0;i<31;i++) {
|
||||
this.sample[i]=new Object();
|
||||
this.sample[i].name="";
|
||||
this.sample[i].length=0;
|
||||
this.sample[i].finetune=0;
|
||||
this.sample[i].volume=64;
|
||||
this.sample[i].loopstart=0;
|
||||
this.sample[i].looplength=0;
|
||||
this.sample[i].data=0;
|
||||
}
|
||||
|
||||
this.patterns=0;
|
||||
this.pattern=new Array();
|
||||
this.note=new Array();
|
||||
|
||||
this.looprow=0;
|
||||
this.loopstart=0;
|
||||
this.loopcount=0;
|
||||
|
||||
this.patterndelay=0;
|
||||
this.patternwait=0;
|
||||
}
|
||||
|
||||
|
||||
// initialize all player variables
|
||||
Protracker.prototype.initialize = function()
|
||||
{
|
||||
this.tick=0;
|
||||
this.position=0;
|
||||
this.row=0;
|
||||
this.offset=0;
|
||||
this.flags=0;
|
||||
|
||||
this.speed=6;
|
||||
this.bpm=125;
|
||||
this.breakrow=0;
|
||||
this.patternjump=0;
|
||||
this.patterndelay=0;
|
||||
this.patternwait=0;
|
||||
|
||||
this.channel=new Array();
|
||||
for(i=0;i<this.channels;i++) {
|
||||
this.channel[i]=new Object();
|
||||
this.channel[i].sample=0;
|
||||
this.channel[i].period=214;
|
||||
this.channel[i].voiceperiod=214;
|
||||
this.channel[i].note=24;
|
||||
this.channel[i].volume=64;
|
||||
this.channel[i].command=0;
|
||||
this.channel[i].data=0;
|
||||
this.channel[i].samplepos=0;
|
||||
this.channel[i].samplespeed=0;
|
||||
this.channel[i].flags=0;
|
||||
this.channel[i].noteon=0;
|
||||
this.channel[i].slidespeed=0;
|
||||
this.channel[i].slideto=214;
|
||||
this.channel[i].slidetospeed=0;
|
||||
this.channel[i].arpeggio=0;
|
||||
|
||||
this.channel[i].semitone=12;
|
||||
this.channel[i].vibratospeed=0
|
||||
this.channel[i].vibratodepth=0
|
||||
this.channel[i].vibratopos=0;
|
||||
this.channel[i].vibratowave=0;
|
||||
}
|
||||
this.vu=new Array();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// load module from url into local buffer
|
||||
Protracker.prototype.load = function(url)
|
||||
{
|
||||
this.url=url;
|
||||
this.clearsong();
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("GET", this.url, true);
|
||||
request.responseType = "arraybuffer";
|
||||
this.request = request;
|
||||
this.loading=true;
|
||||
var asset = this;
|
||||
request.onload = function() {
|
||||
asset.buffer=new Uint8Array(request.response);
|
||||
asset.parse();
|
||||
if (asset.autostart) asset.play();
|
||||
}
|
||||
request.send();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// parse the module from local buffer
|
||||
Protracker.prototype.parse = function()
|
||||
{
|
||||
var i,j,c;
|
||||
|
||||
if (!this.buffer) return false;
|
||||
|
||||
for(i=0;i<4;i++) this.signature+=String.fromCharCode(this.buffer[1080+i]);
|
||||
switch (this.signature) {
|
||||
case "M.K.":
|
||||
case "M!K!":
|
||||
case "4CHN":
|
||||
case "FLT4":
|
||||
break;
|
||||
|
||||
case "6CHN":
|
||||
this.channels=6;
|
||||
break;
|
||||
|
||||
case "8CHN":
|
||||
case "FLT8":
|
||||
this.channels=8;
|
||||
break;
|
||||
|
||||
case "28CH":
|
||||
this.channels=28;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
this.vu=new Array();
|
||||
for(i=0;i<this.channels;i++) this.vu[i]=0.0;
|
||||
|
||||
i=0;
|
||||
while(this.buffer[i] && i<20)
|
||||
this.title=this.title+String.fromCharCode(this.buffer[i++]);
|
||||
|
||||
for(i=0;i<this.samples;i++) {
|
||||
var st=20+i*30;
|
||||
j=0;
|
||||
while(this.buffer[st+j] && j<22) {
|
||||
this.sample[i].name+=
|
||||
((this.buffer[st+j]>0x1f) && (this.buffer[st+j]<0x7f)) ?
|
||||
(String.fromCharCode(this.buffer[st+j])) :
|
||||
(" ");
|
||||
j++;
|
||||
}
|
||||
this.sample[i].length=2*(this.buffer[st+22]*256 + this.buffer[st+23]);
|
||||
this.sample[i].finetune=this.buffer[st+24];
|
||||
if (this.sample[i].finetune > 7) this.sample[i].finetune=this.sample[i].finetune-16;
|
||||
this.sample[i].volume=this.buffer[st+25];
|
||||
this.sample[i].loopstart=2*(this.buffer[st+26]*256 + this.buffer[st+27]);
|
||||
this.sample[i].looplength=2*(this.buffer[st+28]*256 + this.buffer[st+29]);
|
||||
if (this.sample[i].looplength==2) this.sample[i].looplength=0;
|
||||
if (this.sample[i].loopstart>this.sample[i].length) {
|
||||
this.sample[i].loopstart=0;
|
||||
this.sample[i].looplength=0;
|
||||
}
|
||||
}
|
||||
|
||||
this.songlen=this.buffer[950];
|
||||
if (this.buffer[951] != 127) this.repeatpos=this.buffer[951];
|
||||
for(i=0;i<128;i++) {
|
||||
this.patterntable[i]=this.buffer[952+i];
|
||||
if (this.patterntable[i] > this.patterns) this.patterns=this.patterntable[i];
|
||||
}
|
||||
this.patterns+=1;
|
||||
var patlen=4*64*this.channels;
|
||||
|
||||
this.pattern=new Array();
|
||||
this.note=new Array();
|
||||
for(i=0;i<this.patterns;i++) {
|
||||
this.pattern[i]=new Uint8Array(patlen);
|
||||
this.note[i]=new Uint8Array(this.channels*64);
|
||||
for(j=0;j<patlen;j++) this.pattern[i][j]=this.buffer[1084+i*patlen+j];
|
||||
for(j=0;j<64;j++) for(c=0;c<this.channels;c++) {
|
||||
this.note[i][j*this.channels+c]=0;
|
||||
var n=(this.pattern[i][j*4*this.channels+c*4]&0x0f)<<8 | this.pattern[i][j*4*this.channels+c*4+1];
|
||||
for(var np=0; np<this.baseperiodtable.length; np++)
|
||||
if (n==this.baseperiodtable[np]) this.note[i][j*this.channels+c]=np;
|
||||
}
|
||||
}
|
||||
|
||||
var sst=1084+this.patterns*patlen;
|
||||
for(i=0;i<this.samples;i++) {
|
||||
this.sample[i].data=new Float32Array(this.sample[i].length);
|
||||
for(j=0;j<this.sample[i].length;j++) {
|
||||
var q=this.buffer[sst+j];
|
||||
if (q<128) {
|
||||
q=q/128.0;
|
||||
} else {
|
||||
q=((q-128)/128.0)-1.0;
|
||||
}
|
||||
|
||||
this.sample[i].data[j]=q;
|
||||
}
|
||||
sst+=this.sample[i].length;
|
||||
}
|
||||
|
||||
this.ready=true;
|
||||
this.loading=false;
|
||||
this.buffer=0;
|
||||
|
||||
if (this.context) this.lowpassNode.frequency.value=28867;
|
||||
|
||||
this.onReady();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// advance player
|
||||
Protracker.prototype.advance=function(mod) {
|
||||
var spd=(((mod.samplerate*60)/mod.bpm)/4)/6;
|
||||
// advance player
|
||||
if (mod.offset>spd) { mod.tick++; mod.offset=0; mod.flags|=1; }
|
||||
if (mod.tick>=mod.speed) {
|
||||
|
||||
if (mod.patterndelay) { // delay pattern
|
||||
if (mod.tick < ((mod.patternwait+1)*mod.speed)) {
|
||||
mod.patternwait++;
|
||||
} else {
|
||||
mod.row++; mod.tick=0; mod.flags|=2; mod.patterndelay=0;
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (mod.flags&(16+32+64)) {
|
||||
if (mod.flags&64) { // loop pattern?
|
||||
mod.row=mod.looprow;
|
||||
mod.flags&=0xa1;
|
||||
mod.flags|=2;
|
||||
}
|
||||
else {
|
||||
if (mod.flags&16) { // pattern jump/break?
|
||||
//console.log("break to pattern " + mod.patternjump + " row "+mod.breakrow);
|
||||
mod.position=mod.patternjump;
|
||||
mod.row=mod.breakrow;
|
||||
mod.patternjump=0;
|
||||
mod.breakrow=0;
|
||||
mod.flags&=0xe1;
|
||||
mod.flags|=2;
|
||||
}
|
||||
}
|
||||
mod.tick=0;
|
||||
} else {
|
||||
mod.row++; mod.tick=0; mod.flags|=2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.row>=64) { mod.position++; mod.row=0; mod.flags|=4; }
|
||||
if (mod.position>=mod.songlen) {
|
||||
if (mod.repeat) {
|
||||
mod.position=0;
|
||||
} else {
|
||||
mod.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mix an audio buffer with data
|
||||
Protracker.prototype.mix = function(ape) {
|
||||
var f;
|
||||
var p, pp, n, nn;
|
||||
var mod=ape.srcElement.module;
|
||||
outp=new Array();
|
||||
|
||||
var bufs=new Array(ape.outputBuffer.getChannelData(0), ape.outputBuffer.getChannelData(1));
|
||||
var buflen=ape.outputBuffer.length;
|
||||
for(var s=0;s<buflen;s++)
|
||||
{
|
||||
outp[0]=0.0;
|
||||
outp[1]=0.0;
|
||||
|
||||
if (!mod.paused && mod.playing)
|
||||
{
|
||||
mod.advance(mod);
|
||||
|
||||
var och=0;
|
||||
for(var ch=0;ch<mod.channels;ch++)
|
||||
{
|
||||
// calculate playback position
|
||||
p=mod.patterntable[mod.position];
|
||||
pp=mod.row*4*mod.channels + ch*4;
|
||||
if (mod.flags&2) { // new row
|
||||
modrow = mod.row; // here
|
||||
mod.channel[ch].command=mod.pattern[p][pp+2]&0x0f;
|
||||
mod.channel[ch].data=mod.pattern[p][pp+3];
|
||||
|
||||
if (!(mod.channel[ch].command==0x0e && (mod.channel[ch].data&0xf0)==0xd0)) {
|
||||
n=(mod.pattern[p][pp]&0x0f)<<8 | mod.pattern[p][pp+1];
|
||||
if (n) {
|
||||
// noteon, except if command=3 (porta to note)
|
||||
|
||||
if ((mod.channel[ch].command != 0x03) && (mod.channel[ch].command != 0x05)) {
|
||||
mod.channel[ch].period=n;
|
||||
mod.channel[ch].samplepos=0;
|
||||
if (mod.channel[ch].vibratowave>3) mod.channel[ch].vibratopos=0;
|
||||
mod.channel[ch].flags|=3; // recalc speed
|
||||
mod.channel[ch].noteon=1;
|
||||
} else {
|
||||
mod.channel[ch].slideto=n;
|
||||
|
||||
}
|
||||
}
|
||||
nn=mod.pattern[p][pp+0]&0xf0 | mod.pattern[p][pp+2]>>4;
|
||||
if (nn) {
|
||||
mod.channel[ch].sample=nn-1;
|
||||
|
||||
mod.channel[ch].volume=mod.sample[nn-1].volume;
|
||||
if (!n && (mod.channel[ch].samplepos > mod.sample[nn-1].length)) mod.channel[ch].samplepos=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
mod.channel[ch].voiceperiod=mod.channel[ch].period;
|
||||
if (mod.channel[ch].samplepos==0) modsample[ch]=mod.channel[ch].sample;
|
||||
|
||||
|
||||
// kill empty samples
|
||||
if (!mod.sample[mod.channel[ch].sample].length) mod.channel[ch].noteon=0;
|
||||
|
||||
// effects
|
||||
if (mod.flags&1) {
|
||||
if (!mod.tick) {
|
||||
// process only on tick 0
|
||||
mod.effects_t0[mod.channel[ch].command](mod, ch);
|
||||
} else {
|
||||
mod.effects_t1[mod.channel[ch].command](mod, ch);
|
||||
}
|
||||
}
|
||||
|
||||
// recalc note number from period
|
||||
if (mod.channel[ch].flags&2) {
|
||||
for(var np=0; np<mod.baseperiodtable.length; np++)
|
||||
if (mod.baseperiodtable[np]>=mod.channel[ch].period) mod.channel[ch].note=np;
|
||||
mod.channel[ch].semitone=7;
|
||||
if (mod.channel[ch].period>=120)
|
||||
mod.channel[ch].semitone=mod.baseperiodtable[mod.channel[ch].note]-mod.baseperiodtable[mod.channel[ch].note+1];
|
||||
}
|
||||
|
||||
// recalc sample speed and apply finetune
|
||||
if ((mod.channel[ch].flags&1 || mod.flags&2) && mod.channel[ch].voiceperiod)
|
||||
mod.channel[ch].samplespeed=
|
||||
(mod.palclock ? 7093789.2 : 7159090.5)/(mod.channel[ch].voiceperiod*2) * mod.finetunetable[mod.sample[mod.channel[ch].sample].finetune+8] / mod.samplerate;
|
||||
|
||||
// advance vibrato on each new tick
|
||||
if (mod.flags&1) {
|
||||
mod.channel[ch].vibratopos+=mod.channel[ch].vibratospeed;
|
||||
mod.channel[ch].vibratopos&=0x3f;
|
||||
}
|
||||
|
||||
// mix channel to output
|
||||
och=och^(ch&1);
|
||||
f=0.0;
|
||||
if (mod.channel[ch].noteon) {
|
||||
if (mod.sample[mod.channel[ch].sample].length > mod.channel[ch].samplepos)
|
||||
f=(1.0/mod.channels) *
|
||||
(mod.sample[mod.channel[ch].sample].data[Math.floor(mod.channel[ch].samplepos)]*mod.channel[ch].volume)/64.0;
|
||||
outp[och]+=f;
|
||||
mod.channel[ch].samplepos+=mod.channel[ch].samplespeed;
|
||||
}
|
||||
if (s==0) mod.vu[ch]=Math.abs(f);
|
||||
|
||||
// loop or end samples
|
||||
if (mod.channel[ch].noteon) {
|
||||
if (mod.sample[mod.channel[ch].sample].loopstart || mod.sample[mod.channel[ch].sample].looplength) {
|
||||
if (mod.channel[ch].samplepos >= (mod.sample[mod.channel[ch].sample].loopstart+mod.sample[mod.channel[ch].sample].looplength)) {
|
||||
mod.channel[ch].samplepos=mod.sample[mod.channel[ch].sample].loopstart;
|
||||
}
|
||||
} else {
|
||||
if (mod.channel[ch].samplepos >= mod.sample[mod.channel[ch].sample].length) {
|
||||
mod.channel[ch].noteon=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear channel flags
|
||||
mod.channel[ch].flags=0;
|
||||
}
|
||||
mod.offset++;
|
||||
mod.flags&=0x70;
|
||||
}
|
||||
|
||||
// a more headphone-friendly stereo separation (aka. betterpaula)
|
||||
if (mod.separation) {
|
||||
t=outp[0];
|
||||
outp[0]=outp[0]*0.6 + outp[1]*0.4;
|
||||
outp[1]=outp[1]*0.6 + t*0.4;
|
||||
}
|
||||
bufs[0][s]=outp[0];
|
||||
bufs[1][s]=outp[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// tick 0 effect functions
|
||||
//
|
||||
Protracker.prototype.effect_t0_0=function(mod, ch) { // 0 arpeggio
|
||||
mod.channel[ch].arpeggio=mod.channel[ch].data;
|
||||
}
|
||||
Protracker.prototype.effect_t0_1=function(mod, ch) { // 1 slide up
|
||||
if (mod.channel[ch].data) mod.channel[ch].slidespeed=mod.channel[ch].data;
|
||||
}
|
||||
Protracker.prototype.effect_t0_2=function(mod, ch) { // 2 slide down
|
||||
if (mod.channel[ch].data) mod.channel[ch].slidespeed=mod.channel[ch].data;
|
||||
}
|
||||
Protracker.prototype.effect_t0_3=function(mod, ch) { // 3 slide to note
|
||||
if (mod.channel[ch].data) mod.channel[ch].slidetospeed=mod.channel[ch].data;
|
||||
}
|
||||
Protracker.prototype.effect_t0_4=function(mod, ch) { // 4 vibrato
|
||||
if (mod.channel[ch].data&0x0f && mod.channel[ch].data&0xf0) {
|
||||
mod.channel[ch].vibratodepth=(mod.channel[ch].data&0x0f);
|
||||
mod.channel[ch].vibratospeed=(mod.channel[ch].data&0xf0)>>4;
|
||||
}
|
||||
mod.channel[ch].voiceperiod+=
|
||||
(mod.channel[ch].vibratodepth/32)*mod.channel[ch].semitone*(mod.vibratotable[mod.channel[ch].vibratowave&3][mod.channel[ch].vibratopos]/127);
|
||||
mod.channel[ch].flags|=1;
|
||||
}
|
||||
Protracker.prototype.effect_t0_5=function(mod, ch) { // 5
|
||||
}
|
||||
Protracker.prototype.effect_t0_6=function(mod, ch) { // 6
|
||||
}
|
||||
Protracker.prototype.effect_t0_7=function(mod, ch) { // 7
|
||||
}
|
||||
Protracker.prototype.effect_t0_8=function(mod, ch) { // 8
|
||||
}
|
||||
Protracker.prototype.effect_t0_9=function(mod, ch) { // 9 set sample offset
|
||||
mod.channel[ch].samplepos=mod.channel[ch].data*256;
|
||||
}
|
||||
Protracker.prototype.effect_t0_a=function(mod, ch) { // a
|
||||
}
|
||||
Protracker.prototype.effect_t0_b=function(mod, ch) { // b pattern jump
|
||||
mod.breakrow=0;
|
||||
mod.patternjump=mod.channel[ch].data;
|
||||
mod.flags|=16;
|
||||
}
|
||||
Protracker.prototype.effect_t0_c=function(mod, ch) { // c set volume
|
||||
mod.channel[ch].volume=mod.channel[ch].data;
|
||||
}
|
||||
Protracker.prototype.effect_t0_d=function(mod, ch) { // d pattern break
|
||||
mod.breakrow=((mod.channel[ch].data&0xf0)>>4)*10 + (mod.channel[ch].data&0x0f);
|
||||
if (!(mod.flags&16)) mod.patternjump=mod.position+1;
|
||||
mod.flags|=16;
|
||||
}
|
||||
Protracker.prototype.effect_t0_e=function(mod, ch) { // e
|
||||
var i=(mod.channel[ch].data&0xf0)>>4;
|
||||
mod.effects_t0_e[i](mod, ch);
|
||||
}
|
||||
Protracker.prototype.effect_t0_f=function(mod, ch) { // f set speed
|
||||
if (mod.channel[ch].data > 32) {
|
||||
mod.bpm=mod.channel[ch].data;
|
||||
} else {
|
||||
if (mod.channel[ch].data) mod.speed=mod.channel[ch].data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// tick 0 effect e functions
|
||||
//
|
||||
Protracker.prototype.effect_t0_e0=function(mod, ch) { // e0 filter on/off
|
||||
if (mod.channel[ch].data&0x0f) {
|
||||
mod.lowpassNode.frequency.value=4280; //3500;
|
||||
} else {
|
||||
mod.lowpassNode.frequency.value=28867;
|
||||
}
|
||||
}
|
||||
Protracker.prototype.effect_t0_e1=function(mod, ch) { // e1 fine slide up
|
||||
mod.channel[ch].period-=mod.channel[ch].data&0x0f;
|
||||
if (mod.channel[ch].period < 113) mod.channel[ch].period=113;
|
||||
}
|
||||
Protracker.prototype.effect_t0_e2=function(mod, ch) { // e2 fine slide down
|
||||
mod.channel[ch].period+=mod.channel[ch].data&0x0f;
|
||||
if (mod.channel[ch].period > 856) mod.channel[ch].period=856;
|
||||
mod.channel[ch].flags|=1;
|
||||
}
|
||||
Protracker.prototype.effect_t0_e3=function(mod, ch) { // e3 set glissando
|
||||
}
|
||||
Protracker.prototype.effect_t0_e4=function(mod, ch) { // e4 set vibrato waveform
|
||||
mod.channel[ch].vibratowave=mod.channel[ch].data&0x07;
|
||||
}
|
||||
Protracker.prototype.effect_t0_e5=function(mod, ch) { // e5 set finetune
|
||||
}
|
||||
Protracker.prototype.effect_t0_e6=function(mod, ch) { // e6 loop pattern
|
||||
if (mod.channel[ch].data&0x0f) {
|
||||
if (mod.loopcount) {
|
||||
mod.loopcount--;
|
||||
} else {
|
||||
mod.loopcount=mod.channel[ch].data&0x0f;
|
||||
}
|
||||
if (mod.loopcount) mod.flags|=64;
|
||||
} else {
|
||||
mod.looprow=mod.row;
|
||||
}
|
||||
}
|
||||
Protracker.prototype.effect_t0_e7=function(mod, ch) { // e7
|
||||
}
|
||||
Protracker.prototype.effect_t0_e8=function(mod, ch) { // e8
|
||||
}
|
||||
Protracker.prototype.effect_t0_e9=function(mod, ch) { // e9
|
||||
}
|
||||
Protracker.prototype.effect_t0_ea=function(mod, ch) { // ea fine volslide up
|
||||
mod.channel[ch].volume+=mod.channel[ch].data&0x0f;
|
||||
if (mod.channel[ch].volume > 64) mod.channel[ch].volume=64;
|
||||
}
|
||||
Protracker.prototype.effect_t0_eb=function(mod, ch) { // eb fine volslide down
|
||||
mod.channel[ch].volume-=mod.channel[ch].data&0x0f;
|
||||
if (mod.channel[ch].volume < 0) mod.channel[ch].volume=0;
|
||||
}
|
||||
Protracker.prototype.effect_t0_ec=function(mod, ch) { // ec
|
||||
}
|
||||
Protracker.prototype.effect_t0_ed=function(mod, ch) { // ed delay sample
|
||||
if (mod.tick==(mod.channel[ch].data&0x0f)) {
|
||||
// start note
|
||||
var p=mod.patterntable[mod.position];
|
||||
var pp=mod.row*4*mod.channels + ch*4;
|
||||
n=(mod.pattern[p][pp]&0x0f)<<8 | mod.pattern[p][pp+1];
|
||||
if (n) {
|
||||
mod.channel[ch].period=n;
|
||||
mod.channel[ch].voiceperiod=mod.channel[ch].period;
|
||||
mod.channel[ch].samplepos=0;
|
||||
if (mod.channel[ch].vibratowave>3) mod.channel[ch].vibratopos=0;
|
||||
mod.channel[ch].flags|=3; // recalc speed
|
||||
mod.channel[ch].noteon=1;
|
||||
}
|
||||
n=mod.pattern[p][pp+0]&0xf0 | mod.pattern[p][pp+2]>>4;
|
||||
if (n) {
|
||||
mod.channel[ch].sample=n-1;
|
||||
mod.channel[ch].volume=mod.sample[n-1].volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
Protracker.prototype.effect_t0_ee=function(mod, ch) { // ee delay pattern
|
||||
mod.patterndelay=mod.channel[ch].data&0x0f;
|
||||
mod.patternwait=0;
|
||||
}
|
||||
Protracker.prototype.effect_t0_ef=function(mod, ch) { // ef
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// tick 1+ effect functions
|
||||
//
|
||||
Protracker.prototype.effect_t1_0=function(mod, ch) { // 0 arpeggio
|
||||
if (mod.channel[ch].data) {
|
||||
var apn=mod.channel[ch].note;
|
||||
if ((mod.tick%3)==1) apn+=mod.channel[ch].arpeggio>>4;
|
||||
if ((mod.tick%3)==2) apn+=mod.channel[ch].arpeggio&0x0f;
|
||||
if (apn>=0 && apn <= mod.baseperiodtable.length)
|
||||
mod.channel[ch].voiceperiod = mod.baseperiodtable[apn];
|
||||
mod.channel[ch].flags|=1;
|
||||
}
|
||||
}
|
||||
Protracker.prototype.effect_t1_1=function(mod, ch) { // 1 slide up
|
||||
mod.channel[ch].period-=mod.channel[ch].slidespeed;
|
||||
if (mod.channel[ch].period<113) mod.channel[ch].period=113;
|
||||
mod.channel[ch].flags|=3; // recalc speed
|
||||
}
|
||||
Protracker.prototype.effect_t1_2=function(mod, ch) { // 2 slide down
|
||||
mod.channel[ch].period+=mod.channel[ch].slidespeed;
|
||||
if (mod.channel[ch].period>856) mod.channel[ch].period=856;
|
||||
mod.channel[ch].flags|=3; // recalc speed
|
||||
}
|
||||
Protracker.prototype.effect_t1_3=function(mod, ch) { // 3 slide to note
|
||||
if (mod.channel[ch].period < mod.channel[ch].slideto) {
|
||||
mod.channel[ch].period+=mod.channel[ch].slidetospeed;
|
||||
if (mod.channel[ch].period > mod.channel[ch].slideto)
|
||||
mod.channel[ch].period=mod.channel[ch].slideto;
|
||||
}
|
||||
if (mod.channel[ch].period > mod.channel[ch].slideto) {
|
||||
mod.channel[ch].period-=mod.channel[ch].slidetospeed;
|
||||
if (mod.channel[ch].period<mod.channel[ch].slideto)
|
||||
mod.channel[ch].period=mod.channel[ch].slideto;
|
||||
}
|
||||
mod.channel[ch].flags|=3; // recalc speed
|
||||
}
|
||||
Protracker.prototype.effect_t1_4=function(mod, ch) { // 4 vibrato
|
||||
mod.channel[ch].voiceperiod+=
|
||||
(mod.channel[ch].vibratodepth/16)*mod.channel[ch].semitone*(mod.vibratotable[mod.channel[ch].vibratowave&3][mod.channel[ch].vibratopos]/127);
|
||||
mod.channel[ch].flags|=1;
|
||||
}
|
||||
Protracker.prototype.effect_t1_5=function(mod, ch) { // 5 volslide + slide to note
|
||||
mod.effect_t1_3(mod, ch); // slide to note
|
||||
mod.effect_t1_a(mod, ch); // volslide
|
||||
}
|
||||
Protracker.prototype.effect_t1_6=function(mod, ch) { // 6 volslide + vibrato
|
||||
mod.effect_t1_4(mod, ch); // vibrato
|
||||
mod.effect_t1_a(mod, ch); // volslide
|
||||
}
|
||||
Protracker.prototype.effect_t1_7=function(mod, ch) { // 7
|
||||
}
|
||||
Protracker.prototype.effect_t1_8=function(mod, ch) { // 8
|
||||
}
|
||||
Protracker.prototype.effect_t1_9=function(mod, ch) { // 9 set sample offset
|
||||
}
|
||||
Protracker.prototype.effect_t1_a=function(mod, ch) { // a volume slide
|
||||
if (!(mod.channel[ch].data&0x0f)) {
|
||||
// y is zero, slide up
|
||||
mod.channel[ch].volume+=(mod.channel[ch].data>>4);
|
||||
if (mod.channel[ch].volume>64) mod.channel[ch].volume=64;
|
||||
}
|
||||
if (!(mod.channel[ch].data&0xf0)) {
|
||||
// x is zero, slide down
|
||||
mod.channel[ch].volume-=(mod.channel[ch].data&0x0f);
|
||||
if (mod.channel[ch].volume<0) mod.channel[ch].volume=0;
|
||||
}
|
||||
}
|
||||
Protracker.prototype.effect_t1_b=function(mod, ch) { // b pattern jump
|
||||
}
|
||||
Protracker.prototype.effect_t1_c=function(mod, ch) { // c set volume
|
||||
}
|
||||
Protracker.prototype.effect_t1_d=function(mod, ch) { // d pattern break
|
||||
}
|
||||
Protracker.prototype.effect_t1_e=function(mod, ch) { // e
|
||||
var i=(mod.channel[ch].data&0xf0)>>4;
|
||||
mod.effects_t1_e[i](mod, ch);
|
||||
}
|
||||
Protracker.prototype.effect_t1_f=function(mod, ch) { // f
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// tick 1+ effect e functions
|
||||
//
|
||||
Protracker.prototype.effect_t1_e0=function(mod, ch) { // e0
|
||||
}
|
||||
Protracker.prototype.effect_t1_e1=function(mod, ch) { // e1
|
||||
}
|
||||
Protracker.prototype.effect_t1_e2=function(mod, ch) { // e2
|
||||
}
|
||||
Protracker.prototype.effect_t1_e3=function(mod, ch) { // e3
|
||||
}
|
||||
Protracker.prototype.effect_t1_e4=function(mod, ch) { // e4
|
||||
}
|
||||
Protracker.prototype.effect_t1_e5=function(mod, ch) { // e5
|
||||
}
|
||||
Protracker.prototype.effect_t1_e6=function(mod, ch) { // e6
|
||||
}
|
||||
Protracker.prototype.effect_t1_e7=function(mod, ch) { // e7
|
||||
}
|
||||
Protracker.prototype.effect_t1_e8=function(mod, ch) { // e8
|
||||
}
|
||||
Protracker.prototype.effect_t1_e9=function(mod, ch) { // e9 retrig sample
|
||||
if (mod.tick%(mod.channel[ch].data&0x0f)==0)
|
||||
mod.channel[ch].samplepos=0;
|
||||
}
|
||||
Protracker.prototype.effect_t1_ea=function(mod, ch) { // ea
|
||||
}
|
||||
Protracker.prototype.effect_t1_eb=function(mod, ch) { // eb
|
||||
}
|
||||
Protracker.prototype.effect_t1_ec=function(mod, ch) { // ec cut sample
|
||||
if (mod.tick==(mod.channel[ch].data&0x0f))
|
||||
mod.channel[ch].volume=0;
|
||||
}
|
||||
Protracker.prototype.effect_t1_ed=function(mod, ch) { // ed delay sample
|
||||
mod.effect_t0_ed(mod, ch);
|
||||
}
|
||||
Protracker.prototype.effect_t1_ee=function(mod, ch) { // ee
|
||||
}
|
||||
Protracker.prototype.effect_t1_ef=function(mod, ch) { // ef
|
||||
}
|
||||
Reference in New Issue
Block a user