@ekx/auph
Version:
[](https://github.com/eliasku/auph/actions/workflows/build.yml) [](https://www.npmjs.com/package/auph) [ • 21.9 kB
JavaScript
var auph=(function(exports){'use strict';function setError(status, context) {
}/**
* Object Name Identifier Layout: [00tt 0000 | vvvv vvvv | vvvv vvvv | iiii iiii]
*/
var tMask = 0x30000000;
var vMask = 0x00FFFF00;
var vIncr = 0x00000100;
var iMask = 0x000000FF;
var Mixer = 0x00000001;
// used for integer to float params conversion
var Unit = 1024;
var DefaultBus = 1 /* Sound */ | 268435456 /* Bus */;function nextHandle(h) {
return ((h + vIncr) & vMask) | (h & (tMask | iMask));
}
function connectAudioNode(node, dest) {
node.connect(dest);
}
function disconnectAudioNode(node, dest) {
node.disconnect(dest);
}
function setAudioParamValue(param, value) {
param.value = value;
}
function len(a) {
return a.length;
}
function resize(a, length) {
a.length = length;
}
function add(a, e) {
a.push(e);
}function unlock(unlocked) {
// "touchstart", "touchend", "mousedown", "pointerdown"
var events = ["touchstart", "touchend", "mousedown", "click", "keydown"];
var num = len(events);
var doc = document;
var handle = function () {
if (unlocked()) {
for (var i = 0; i < num; ++i) {
doc.removeEventListener(events[i], handle, true);
}
}
};
for (var i = 0; i < num; ++i) {
doc.addEventListener(events[i], handle, true);
}
}var ctx;
var emptyAudioBuffer;
var defaultSampleRate = 22050;
function getContext() {
if (ctx && ctx.state !== "closed") {
return ctx;
}
}
function getAudioContextObject() {
return ctx;
}
function getContextState(ctx) {
var state = 0;
if (ctx.state !== "closed") {
state |= 1 /* Active */;
if (ctx.state === "running") {
state |= 2 /* Running */;
}
}
return state;
}
function audioContextResume(ctx) {
ctx.resume().then(function () {
}).catch(function (reason) {
});
}
function audioContextPause(ctx) {
ctx.suspend().then(function () {
}).catch(function (reason) {
});
}
function newAudioContext(options) {
var scope = window;
var audioContext = scope.AudioContext || scope.webkitAudioContext;
// TODO: set sample rate could lead to wrong playback on safari mobile (maybe it should be recreated after unlock?)
//try {
// return new audioContext(options);
//} catch (err) {
// error(Message.WebAudio_TryDefaultOptions, err);
//}
try {
return new audioContext();
}
catch (err) {
}
}
function initContext() {
if (ctx) {
return ctx;
}
ctx = newAudioContext();
if (ctx) {
emptyAudioBuffer = ctx.createBuffer(1, 1, defaultSampleRate);
unlock(function () {
if (ctx.state === "suspended") {
audioContextResume(ctx);
return false;
}
return true;
});
}
return ctx;
}
function closeContext(context) {
context.close().then(function () {
}).catch(function (reason) {
});
ctx = undefined;
}var VoiceObj = /** @class */ (function () {
function VoiceObj(g, p, index) {
var _this = this;
this.g = g;
this.p = p;
// handle passport
this.h = 0;
// Control Flags
this.s = 0;
// Gain
this.G = Unit;
// Pan
this.P = Unit;
// Rate
this.R = Unit;
// Source auph Buffer
this.bf = 0;
// Destination auph Bus
this.bs = 0;
// is source-node started: static buffer playback
this._s = 0;
// Source-Node
this.sn = null;
// Destination-Node: connected destination audio node
this.dn = null;
this.pr = null;
this._e = function () {
// maybe check is useful
//if (this.buffer === e.target || (this.stream && this.stream.el === e.target)) {
_voiceStop(_this);
//}
};
this.h = 805306368 /* Voice */ | index;
}
return VoiceObj;
}());
function _voiceNew(ctx, index) {
var gain = ctx.createGain();
var pan = ctx.createStereoPanner();
connectAudioNode(pan, gain);
return new VoiceObj(gain, pan, index);
}
function _voiceChangeDestination(v, target) {
if (target !== v.dn) {
var gain = v.g;
if (v.dn) {
disconnectAudioNode(gain, v.dn);
}
v.dn = target;
if (target) {
connectAudioNode(gain, target);
}
}
}
function _voiceResetDestination(v) {
if (v.dn) {
disconnectAudioNode(v.g, v.dn);
v.dn = null;
}
}
function _voiceStop(v) {
// stop buffer
var buffer = v.sn;
if (buffer) {
if ((v.s & 2 /* Running */) !== 0) {
buffer.stop();
}
buffer.onended = null;
disconnectAudioNode(buffer);
try {
buffer.buffer = emptyAudioBuffer;
}
catch (_a) {
}
v.sn = null;
}
if (v.pr) {
v.pr.disconnect();
v.pr.onaudioprocess = null;
v.pr = null;
}
_voiceResetDestination(v);
v.bf = 0;
v.bs = 0;
v.s = 0;
v.h = nextHandle(v.h);
}
function _voiceStartBuffer(v) {
var source = v.sn;
if (source && !v._s) {
//source.addEventListener("ended", v._e, {once: true});
source.onended = v._e;
source.loop = (v.s & 4 /* Loop */) !== 0;
source.start();
v._s = 1;
}
}
function _voicePrepareBuffer(v, ctx, audioBuffer) {
var source = ctx.createBufferSource();
source.buffer = audioBuffer;
connectAudioNode(source, v.p);
v.sn = source;
v._s = 0;
}
function _voiceSetLoop(v, value) {
var current = (v.s & 4 /* Loop */) !== 0;
if (value !== current) {
v.s ^= 4 /* Loop */;
if (v.sn) {
v.sn.loop = value;
}
}
}
function _voiceSetRunning(v, value) {
var current = !!(v.s & 2 /* Running */);
if (value !== current) {
v.s ^= 2 /* Running */;
var playbackRate = value ? (v.R / Unit) : 0.0;
if (v.sn) {
setAudioParamValue(v.sn.playbackRate, playbackRate);
if (value) {
// restart if play called in pause mode
_voiceStartBuffer(v);
}
}
}
}
function _voiceApplyPitch(v, value) {
if (!!(v.s & 2 /* Running */)) {
if (v.sn) {
setAudioParamValue(v.sn.playbackRate, value / Unit);
}
}
}
var voicePool = [null];
var voicesMaxCount = 64;
function _getVoiceObj(handle) {
var obj = voicePool[handle & iMask];
return (obj && obj.h === handle) ? obj : null;
}
function createVoiceObj(ctx) {
var next = len(voicePool);
for (var i = 1; i < next; ++i) {
var v = voicePool[i];
if (v.s === 0) {
return v.h;
}
}
if (next < voicesMaxCount) {
var v = _voiceNew(ctx, next);
v.h = 805306368 /* Voice */ | next;
voicePool.push(v);
return v.h;
}
return 0;
}var BusObj = /** @class */ (function () {
function BusObj(g) {
this.g = g;
this.h = 0;
this.s = 1 /* Active */ | 2 /* Running */;
this.G = Unit;
}
return BusObj;
}());
var busLine = [];
function createBusObj(ctx) {
var next = len(busLine);
var obj = new BusObj(ctx.createGain());
obj.h = next | 268435456 /* Bus */;
add(busLine, obj);
return obj;
}
function initBusPool(ctx) {
var master = createBusObj(ctx).g;
connectAudioNode(master, ctx.destination);
connectAudioNode(createBusObj(ctx).g, master);
connectAudioNode(createBusObj(ctx).g, master);
connectAudioNode(createBusObj(ctx).g, master);
}
function termBusPool() {
for (var i = 0; i < len(busLine); ++i) {
disconnectAudioNode(busLine[i].g);
}
resize(busLine, 0);
}
function _getBus(bus) {
var obj = busLine[bus & iMask];
return (obj && obj.h === bus) ? obj : null;
}
function _getBusGain(handle) {
var obj = _getBus(handle);
return obj ? obj.g : undefined;
}
function _setBusConnected(bus, connected) {
var flag = !!(bus.s & 2 /* Running */);
if (flag !== connected) {
var master = busLine[0];
var dest = bus === master ? getAudioContextObject().destination : master.g;
if (connected) {
connectAudioNode(bus.g, dest);
}
else {
disconnectAudioNode(bus.g, dest);
}
bus.s ^= 2 /* Running */;
}
}var BufferObj = /** @class */ (function () {
function BufferObj(h, s, b,
// HEAP pointer to C-callback
_f,
// HEAP pointer to C-userdata
_u) {
if (_f === void 0) { _f = 0; }
if (_u === void 0) { _u = 0; }
this.h = h;
this.s = s;
this.b = b;
this._f = _f;
this._u = _u;
}
return BufferObj;
}());
var buffers = [null];
var buffersMaxCount = 128;
function getNextBufferObj() {
var next = len(buffers);
for (var i = 1; i < next; ++i) {
var buffer = buffers[i];
if (buffer.s === 0) {
return buffer.h;
}
}
if (next < buffersMaxCount) {
var b = new BufferObj(next | 536870912 /* Buffer */, 0, null);
add(buffers, b);
return b.h;
}
return 0;
}
function _bufferDestroy(obj) {
// TODO:
// if ((obj.s & Flag.Stream) !== 0 && obj.data) {
// URL.revokeObjectURL(obj.b as string);
// }
obj.h = nextHandle(obj.h);
obj.s = 0;
obj.b = null;
}
function _getBufferObj(buffer) {
var obj = buffers[buffer & iMask];
if (obj && obj.h === buffer) {
return obj;
}
return null;
}
function _decodeAudioData(ctx, obj, buffer) {
var success = function (audioBuffer) {
obj.s |= 2 /* Loaded */;
obj.b = audioBuffer;
};
var fail = function (err) {
_bufferDestroy(obj);
};
// TODO: maybe callbacks will be deprecated?
ctx.decodeAudioData(buffer, success, fail);
}
function _bufferMemory(obj, ctx, data, flags) {
obj.s |= 1 /* Active */;
var buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
// TODO: streaming
// if (flags & Flag.Stream) {
// obj.s |= Flag.Stream;
_decodeAudioData(ctx, obj, buffer);
}
function _buffer_set_callback(obj, ctx, f, u) {
obj.s |= 1 /* Active */ | 2 /* Loaded */ | 16 /* Callback */;
obj.b = new AudioBuffer({ length: 8192, sampleRate: 44100, numberOfChannels: 2 });
obj._f = f;
obj._u = u;
}
function _bufferLoad(obj, ctx, filepath, flags) {
obj.s |= 1 /* Active */;
// TODO: streaming
//if (flags & Flag.Stream) {
//obj.s |= Flag.Stream;
fetch(new Request(filepath))
.then(function (response) { return response.arrayBuffer(); })
.then(function (buffer) { return _decodeAudioData(ctx, obj, buffer); })
.catch(function (reason) {
_bufferDestroy(obj);
});
}function setup() {
var ctx = initContext();
if (ctx) {
initBusPool(ctx);
}
}
function shutdown() {
var ctx = getContext();
if (ctx) {
termBusPool();
resize(voicePool, 1);
resize(buffers, 1);
closeContext(ctx);
}
}
function load(filepath, flags) {
var handle = getNextBufferObj();
if (handle) {
var ctx = getAudioContextObject();
if (!ctx) {
return 0;
}
var obj = buffers[handle & iMask];
_bufferLoad(obj, ctx, filepath);
}
return handle;
}
function loadMemory(data, flags) {
var handle = getNextBufferObj();
if (handle) {
var ctx = getAudioContextObject();
if (!ctx) {
return 0;
}
var obj = buffers[handle & iMask];
_bufferMemory(obj, ctx, data);
}
return handle;
}
function load_callback(p_callback, p_userdata) {
var handle = getNextBufferObj();
if (handle) {
var ctx = getAudioContextObject();
if (!ctx) {
return 0;
}
var obj = buffers[handle & iMask];
_buffer_set_callback(obj, ctx, p_callback, p_userdata);
}
return handle;
}
function unload(name) {
var obj = _getBufferObj(name);
if (obj) {
stop(name);
_bufferDestroy(obj);
}
}
/***
*
* @param buffer
* @param gain
* @param pan
* @param rate
* @param flags
* @param bus
*/
function voice(buffer, gain, pan, rate, flags, bus) {
// arguments check debug
if (flags & ~(2 /* Running */ | 4 /* Loop */)) {
return 0;
}
///
var ctx = getAudioContextObject();
if (!ctx || ctx.state !== "running") {
setError(23 /* InvalidMixerState */, ctx === null || ctx === void 0 ? void 0 : ctx.state);
return 0;
}
var bufferObj = _getBufferObj(buffer);
if (!bufferObj) {
return 0;
}
if (!(bufferObj.s & 2 /* Loaded */)) {
return 0;
}
if (!bufferObj.b) {
return 0;
}
var targetNode = _getBusGain(bus ? bus : DefaultBus);
if (!targetNode) {
return 0;
}
var voice = createVoiceObj(ctx);
if (voice === 0) {
return 0;
}
var voiceObj = _getVoiceObj(voice);
voiceObj.s = 1 /* Active */ | flags;
voiceObj.bf = buffer;
voiceObj.G = gain;
voiceObj.P = pan;
voiceObj.R = rate;
setAudioParamValue(voiceObj.g.gain, gain / Unit);
setAudioParamValue(voiceObj.p.pan, pan / Unit - 1);
// TODO: streamed decoding
//if (bufferObj.s & Flag.Stream) {
if (bufferObj.s & 16 /* Callback */) {
// const _play_next_chunk = () => {
// _voicePrepareBuffer(voiceObj, ctx, bufferObj.b as AudioBuffer);
// voiceObj.sn!.loop = false;
// const ptr = _auph_read_to_buffer(bufferObj._u, bufferObj._f);
// (bufferObj.b as AudioBuffer).copyToChannel(HEAPF32.subarray(ptr >>> 2, 8192 + (ptr >>> 2)), 0, 0);
// (bufferObj.b as AudioBuffer).copyToChannel(HEAPF32.subarray(8192 + (ptr >>> 2), (2 * 8192) + (ptr >>> 2)), 1, 0);
// voiceObj.sn!.onended = _play_next_chunk;
// voiceObj.sn!.start();
// };
// _play_next_chunk();
//_voicePrepareBuffer(voiceObj, ctx, bufferObj.b as AudioBuffer);
if (flags & 2 /* Running */) {
// _voiceStartBuffer(voiceObj);
voiceObj._s = 1;
//voiceObj.sn!.loop = true;
var processor = ctx.createScriptProcessor(8192, 0, 2);
processor.onaudioprocess = function (ev) {
// The output buffer contains the samples that will be modified and played
var outputBuffer = ev.outputBuffer;
// Loop through the output channels (in this case there is only one)
var ptr = _auph_read_to_buffer(bufferObj._u, bufferObj._f);
var output0 = outputBuffer.getChannelData(0);
var output1 = outputBuffer.getChannelData(1);
output0.set(HEAPF32.subarray(ptr >>> 2, 8192 + (ptr >>> 2)));
output1.set(HEAPF32.subarray(8192 + (ptr >>> 2), (2 * 8192) + (ptr >>> 2)));
};
//disconnectAudioNode(voiceObj.sn!, voiceObj.p);
// connectAudioNode(voiceObj.sn!, processor);
connectAudioNode(processor, voiceObj.p);
//voiceObj.sn!.start();
voiceObj.pr = processor;
}
}
else {
_voicePrepareBuffer(voiceObj, ctx, bufferObj.b);
if (flags & 2 /* Running */) {
_voiceStartBuffer(voiceObj);
}
}
// maybe we need to set target before `startBuffer()`
_voiceChangeDestination(voiceObj, targetNode);
_voiceApplyPitch(voiceObj, rate);
return voice;
}
function stop(name) {
if (name === 0) {
return;
}
var type = name & tMask;
if (type === 805306368 /* Voice */) {
var obj = _getVoiceObj(name);
if (obj) {
_voiceStop(obj);
}
}
else if (type === 536870912 /* Buffer */) {
var obj = _getBufferObj(name);
if (obj) {
for (var i = 1; i < len(voicePool); ++i) {
var v = voicePool[i];
if (v.bf === name) {
_voiceStop(v);
}
}
}
}
}
function set(name, param, value) {
if (name === 0) {
return;
}
if (name === Mixer && (param & 128 /* Flags */) && (param & 2 /* Running */)) {
var ctx = getContext();
if (ctx) {
if (value && ctx.state === "suspended") {
audioContextResume(ctx);
}
else if (!value && ctx.state === "running") {
audioContextPause(ctx);
}
}
}
var type = name & tMask;
if (type === 805306368 /* Voice */) {
var obj = _getVoiceObj(name);
if (obj) {
if (param & 128 /* Flags */) {
var enabled = value !== 0;
if (param & 2 /* Running */) {
_voiceSetRunning(obj, enabled);
}
else if (param & 4 /* Loop */) {
_voiceSetLoop(obj, enabled);
}
}
else {
if (param === 1 /* Gain */) {
if (obj.G !== value) {
obj.G = value;
setAudioParamValue(obj.g.gain, value / Unit);
}
}
else if (param === 2 /* Pan */) {
if (obj.P !== value) {
obj.P = value;
setAudioParamValue(obj.p.pan, value / Unit - 1);
}
}
else if (param === 3 /* Rate */) {
if (obj.R !== value) {
obj.R = value;
_voiceApplyPitch(obj, value);
}
}
else ;
}
}
}
else if (type === 268435456 /* Bus */) {
var obj = _getBus(name);
if (obj) {
if (param & 128 /* Flags */) {
if (param & 2 /* Running */) {
_setBusConnected(obj, !!value);
}
}
else {
if (param === 1 /* Gain */) {
if (obj.G !== value) {
setAudioParamValue(obj.g.gain, value / Unit);
obj.G = value;
}
}
}
}
}
}
function get(name, param) {
var result = 0;
if (name === Mixer) {
var ctx = getAudioContextObject();
if (ctx) {
if (param === 0 /* State */) {
result = getContextState(ctx);
}
else if (param === 5 /* SampleRate */) {
result = ctx.sampleRate | 0;
}
}
return result;
}
var type = name & tMask;
if ((param & 256 /* Count */) && !(name & iMask)) {
var stateMask = param & 127 /* StateMask */;
if (type === 805306368 /* Voice */) {
result = _countObjectsWithFlags(voicePool, stateMask);
}
else if (type === 268435456 /* Bus */) {
result = _countObjectsWithFlags(busLine, stateMask);
}
else if (type === 536870912 /* Buffer */) {
result = _countObjectsWithFlags(buffers, stateMask);
}
return result;
}
if (type === 805306368 /* Voice */) {
var obj = _getVoiceObj(name);
if (obj) {
if (param === 0 /* State */) {
result = obj.s;
}
else if (param === 1 /* Gain */) {
result = obj.G;
}
else if (param === 2 /* Pan */) {
result = obj.P;
}
else if (param === 3 /* Rate */) {
result = obj.R;
}
else if (param === 6 /* Duration */) {
if (obj.sn && obj.sn.buffer) {
result = (obj.sn.buffer.duration * Unit) | 0;
}
}
else if (param === 4 /* CurrentTime */) {
if (obj.sn && obj.sn.buffer) ;
}
else ;
}
return result;
}
if (type === 268435456 /* Bus */) {
var obj = _getBus(name);
if (obj) {
if (param === 0 /* State */) {
result = obj.s;
}
else if (param === 1 /* Gain */) {
result = obj.G;
}
else ;
}
return result;
}
if (type === 536870912 /* Buffer */) {
var obj = _getBufferObj(name);
if (obj) {
if (param === 0 /* State */) {
result = obj.s;
}
else if (param === 6 /* Duration */) {
if (obj.b) {
result = (obj.b.duration * Unit) | 0;
}
}
else ;
}
}
return result;
}
function vibrate(durationMillis) {
try {
if (navigator.vibrate) {
return navigator.vibrate(durationMillis) ? 0 : 1;
}
}
catch (_a) {
}
return 1;
}
/** private helpers **/
function _countObjectsWithFlags(arr, mask) {
var cnt = 0;
var size = len(arr);
for (var i = 1; i < size; ++i) {
var obj = arr[i];
if (obj && (obj.s & mask) === mask) {
++cnt;
}
}
return cnt;
}exports.get=get;exports.load=load;exports.loadMemory=loadMemory;exports.load_callback=load_callback;exports.set=set;exports.setup=setup;exports.shutdown=shutdown;exports.stop=stop;exports.unload=unload;exports.vibrate=vibrate;exports.voice=voice;Object.defineProperty(exports,'__esModule',{value:true});return exports;})({});//# sourceMappingURL=auph.js.map