35:c413694d4564
Anton Shestakov <av6@dwimlabs.net>, Tue, 26 Jun 2018 00:50:58 +0800
docs: explain why editing index.html is required, add license

previous change 23:7c9d98964a57

player.js

Permissions: -rw-r--r--

Other formats: Feeds:
/* Based on mozplayer.js by Daniel Svensson, dsvensson@gmail.com */
function parseMetadata(ref) {
var offset = 0;
var read_int32 = function() {
var value = Module.getValue(ref + offset, 'i32');
offset += 4;
return value;
};
var read_string = function() {
var value = Module.Pointer_stringify(Module.getValue(ref + offset, 'i8*'));
offset += 4;
return value;
};
var res = {};
res.length = read_int32();
res.intro_length = read_int32();
res.loop_length = read_int32();
res.play_length = read_int32();
offset += 4 * 12; // skip reserved fields
res.system = read_string();
res.game = read_string();
res.song = read_string();
res.author = read_string();
res.copyright = read_string();
res.comment = read_string();
res.dumper = read_string();
return res;
}
function loadArrayBuffer(url, done, fail) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (this.status !== 200) {
fail(this);
} else {
done(this);
}
};
xhr.send();
}
var Player = Backbone.Model.extend({
// libgme output is in stereo only
channels: 2,
// largest signed short integer value (16 bits), for scaling signal
i16Max: Math.pow(2, 16 - 1) - 1,
tracks: new Tracks(),
defaults: {
autoplay: false,
repeat: false,
random: false,
track: new Track()
},
initialize: function() {
window.AudioContext = (window.AudioContext || window.webkitAudioContext);
if (window.AudioContext) {
this.ctx = new window.AudioContext();
this.ctxSize = 1024 * 4;
this.bufferSize = this.ctxSize * this.channels;
this.ref = Module._malloc(4);
this.buffer = Module._malloc(this.bufferSize * 2);
console.debug('Using Web Audio API');
} else {
console.error('Web Audio API is not supported, try another browser');
}
},
load: function(data) {
this.unload();
var payload = new Uint8Array(data);
var error = Module.ccall('gme_open_data', 'string', ['array', 'number', 'number', 'number'], [payload, payload.length, this.ref, this.ctx.sampleRate]);
if (error !== '') {
console.error('gme_open_data failed: ' + error);
return;
}
this.emu = Module.getValue(this.ref, 'i32');
var trackCount = Module.ccall('gme_track_count', 'number', ['number'], [this.emu]);
var tis = [];
for (var i = 0; i < trackCount; i++) {
var trackInfo = this.getTrackInfo(i);
trackInfo.id = i;
tis.push(trackInfo);
}
this.tracks.reset(tis);
},
unload: function() {
if (this.emu !== null) {
Module.ccall('gme_delete', 'number', ['number'], [this.emu]);
delete this.emu;
}
this.get('track').clear();
},
getTrackInfo: function(track) {
var error = Module.ccall('gme_track_info', 'string', ['number', 'number', 'number'], [this.emu, this.ref, track]);
if (error !== '') {
console.error('gme_track_info failed: ' + error);
return;
}
var memory = Module.getValue(this.ref, '*');
var result = parseMetadata(memory);
Module.ccall('gme_free_info', 'number', ['number'], [memory]);
return result;
},
startTrack: function(track) {
this.stop();
var error = Module.ccall('gme_start_track', 'string', ['number', 'number'], [this.emu, track]);
if (error !== '') {
console.error('gme_start_track failed: ' + error);
return;
}
this.set('track', this.tracks.get(track));
this.get('track').set('status', 'stopped');
},
isTrackEnded: function() {
return Module.ccall('gme_track_ended', 'number', ['number'], [this.emu]) === 1;
},
seek: function(msec) {
var error = Module.ccall('gme_seek', 'string', ['number', 'number'], [this.emu, msec]);
if (error !== '') {
console.error('gme_seek failed: ' + error);
}
},
tell: function() {
return Module.ccall('gme_tell', 'number', ['number'], [this.emu]);
},
voiceCount: function() {
return Module.ccall('gme_voice_count', 'number', ['number'], [this.emu]);
},
stop: function() {
if (this.node) {
this.node.disconnect();
delete this.node;
}
this.get('track').set({'status': 'stopped', 'position': this.tell()});
},
play: function() {
if (this.node) {
return;
}
var limit = this.i16Max;
var player = this;
this.node = this.ctx.createScriptProcessor(this.ctxSize, 0, this.channels);
this.node.onaudioprocess = function(e) {
if (player.isTrackEnded()) {
player.stop();
player.trigger('ended');
return;
}
var channels = [e.outputBuffer.getChannelData(0), e.outputBuffer.getChannelData(1)];
var error = Module.ccall('gme_play', 'string', ['number', 'number', 'number'], [player.emu, player.bufferSize, player.buffer]);
if (error !== '') {
console.error('gme_play failed: ' + error);
return;
}
for (var i = 0; i < player.bufferSize; i++) {
for (var n = 0; n < e.outputBuffer.numberOfChannels; n++) {
channels[n][i] = Module.getValue(player.buffer + i * e.outputBuffer.numberOfChannels * 2 + n * 2, 'i16') / limit;
}
}
player.get('track').set('position', player.tell());
};
this.node.connect(this.ctx.destination);
this.ctx.resume();
this.get('track').set('status', 'playing');
}
});