UNPKG

@ekx/auph

Version:

[![Build](https://github.com/eliasku/auph/actions/workflows/build.yml/badge.svg)](https://github.com/eliasku/auph/actions/workflows/build.yml) [![Version](https://img.shields.io/npm/v/auph)](https://www.npmjs.com/package/auph) [![Downloads](https://img.sh

756 lines (755 loc) 21.9 kB
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