wam-community
Version:
A collection of prebuilt Web Audio Modules ready for use
502 lines (484 loc) • 18 kB
JavaScript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "../shared/util.ts":
/*!*************************!*\
!*** ../shared/util.ts ***!
\*************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ constantSource: () => (/* binding */ constantSource),
/* harmony export */ noiseSource: () => (/* binding */ noiseSource),
/* harmony export */ token: () => (/* binding */ token)
/* harmony export */ });
function token() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 16);
}
function constantSource(audioContext) {
if (audioContext.createConstantSource) {
let source = audioContext.createConstantSource();
source.start();
return source;
}
else {
let length = audioContext.sampleRate;
var buffer = audioContext.createBuffer(1, length, audioContext.sampleRate);
var noise = buffer.getChannelData(0);
for (var i = 0; i < length; i++) {
noise[i] = 1.0;
}
var source = audioContext.createBufferSource();
source.buffer = buffer;
source.loop = true;
source.loopStart = 0.0;
source.loopEnd = 0.9;
source.start();
return source;
}
}
function noiseSource(audioContext) {
let length = audioContext.sampleRate;
var buffer = audioContext.createBuffer(1, length, audioContext.sampleRate);
var noise = buffer.getChannelData(0);
for (var i = 0; i < length; i++) {
noise[i] = (Math.random() * 2) - 1;
}
var source = audioContext.createBufferSource();
source.buffer = buffer;
source.loop = true;
source.loopStart = 0.0;
source.loopEnd = 0.9;
source.start();
return source;
}
/***/ }),
/***/ "./src/Clip.ts":
/*!*********************!*\
!*** ./src/Clip.ts ***!
\*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Clip: () => (/* binding */ Clip),
/* harmony export */ PP16: () => (/* binding */ PP16),
/* harmony export */ PPQN: () => (/* binding */ PPQN)
/* harmony export */ });
/* harmony import */ var _shared_util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../shared/util */ "../shared/util.ts");
const PPQN = 24;
const PP16 = (PPQN / 4);
class Clip {
constructor(id, state) {
if (state) {
this.state = {
id: state.id,
speed: state.speed,
steps: [...state.steps]
};
}
else {
this.state = {
id: id || (0,_shared_util__WEBPACK_IMPORTED_MODULE_0__.token)(),
steps: [0, 0, 0, 0, 0, 0, 0, 0],
speed: 24,
};
}
this.dirty = true;
}
getState() {
return {
id: this.state.id,
steps: [...this.state.steps],
speed: this.state.speed
};
}
async setState(state) {
this.state.id = state.id;
this.state.steps = [...state.steps];
this.state.speed = state.speed;
this.dirty = true;
if (this.updateProcessor)
this.updateProcessor(this);
}
length() {
return this.state.steps.length;
}
setRenderFlag(dirty) {
this.dirty = dirty;
}
needsRender() {
return this.dirty;
}
}
/***/ }),
/***/ "./src/StepModulatorKernel.ts":
/*!************************************!*\
!*** ./src/StepModulatorKernel.ts ***!
\************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ StepModulatorKernel: () => (/* binding */ StepModulatorKernel)
/* harmony export */ });
/* harmony import */ var _Clip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Clip */ "./src/Clip.ts");
const audioWorkletGlobalScope = globalThis;
const moduleId = 'com.sequencerParty.stepmod';
const ModuleScope = audioWorkletGlobalScope.webAudioModules.getModuleScope(moduleId);
const WamProcessor = ModuleScope.WamProcessor;
const WamParameterInfo = ModuleScope.WamParameterInfo;
class StepModulatorKernel {
constructor(id, row, processor) {
this.processor = processor;
this.id = id;
this.paramIds = {};
this.rowConfigured = false;
this.activeStep = 0;
this.clips = new Map();
this.lastValue = 0;
this.setRow(row);
}
setRow(row) {
this.row = row;
}
wamParameters() {
if (this.row === undefined) {
throw new Error("calling wamParameters without row set!");
}
const prefix = `row${this.row + 1}-`;
let parameters = {};
this.paramIds["slew"] = `${prefix}slew`;
this.paramIds["gain"] = `${prefix}gain`;
parameters[this.paramIds["slew"]] = new WamParameterInfo(this.paramIds["slew"], {
type: "float",
defaultValue: 1.0,
minValue: 0,
maxValue: 1.0,
});
parameters[this.paramIds["gain"]] = new WamParameterInfo(this.paramIds["gain"], {
type: "float",
defaultValue: 1.0,
minValue: 0,
maxValue: 1.0,
});
for (let i = 1; i < 9; i++) {
const rowedId = `${prefix}step${i}`;
this.paramIds[`step${i}`] = rowedId;
parameters[rowedId] = new WamParameterInfo(rowedId, {
type: "float",
defaultValue: 0,
minValue: 0,
maxValue: 1,
});
}
this.rowConfigured = true;
return parameters;
}
process(currentClipId, tickPosition, params) {
let clip = this.clips.get(currentClipId);
if (!this.rowConfigured) {
this.wamParameters();
}
if (!clip)
return;
if (!this.targetParam)
return;
let clipPosition = tickPosition % (clip.length() * clip.state.speed);
if (this.ticks != clipPosition) {
this.ticks = clipPosition;
}
this.update(clip, params);
}
update(clip, params) {
if (!this.rowConfigured) {
this.wamParameters();
}
if (!this.targetParam) {
return;
}
let step;
if (clip.state.speed == 0) {
step = this.activeStep;
}
else {
step = Math.floor(this.ticks / clip.state.speed);
this.activeStep = step;
}
var result = 0;
var i = 0;
switch (step) {
default:
result = 0;
break;
case 0:
result = params[this.paramIds["step1"]].value;
break;
case 1:
result = params[this.paramIds["step2"]].value;
break;
case 2:
result = params[this.paramIds["step3"]].value;
break;
case 3:
result = params[this.paramIds["step4"]].value;
break;
case 4:
result = params[this.paramIds["step5"]].value;
break;
case 5:
result = params[this.paramIds["step6"]].value;
break;
case 6:
result = params[this.paramIds["step7"]].value;
break;
case 7:
result = params[this.paramIds["step8"]].value;
break;
}
let target = (step < clip.state.steps.length) ? clip.state.steps[step] + result : result;
let slew = params[this.paramIds["slew"]].value;
let gain = params[this.paramIds["gain"]].value;
let value = this.lastValue + ((target - this.lastValue) * (slew) * slew * slew);
if (value != this.lastValue) {
const { currentTime } = audioWorkletGlobalScope;
const min = (this.targetParam.minValue === undefined) ? 0 : this.targetParam.minValue;
const max = (this.targetParam.maxValue === undefined) ? 1 : this.targetParam.maxValue;
var output = min + (value * (max - min) * gain);
if (this.targetParam.type == 'int' || this.targetParam.type == 'choice' || this.targetParam.type == 'boolean') {
output = Math.round(output);
}
this.processor.emitEvents({
type: "wam-automation",
data: {
id: this.targetParam.id,
normalized: false,
value: output
},
time: currentTime
});
}
this.lastValue = value;
}
async onMessage(message) {
if (message.data && message.data.action == "clip") {
let clip = new _Clip__WEBPACK_IMPORTED_MODULE_0__.Clip(message.data.id, message.data.state);
this.clips.set(message.data.id, clip);
}
else if (message.data && message.data.action == "target") {
this.targetParam = message.data.param;
}
}
}
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!***************************************!*\
!*** ./src/StepModulatorProcessor.ts ***!
\***************************************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _StepModulatorKernel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./StepModulatorKernel */ "./src/StepModulatorKernel.ts");
const moduleId = 'com.sequencerParty.stepmod';
const PPQN = 24;
const audioWorkletGlobalScope = globalThis;
const ModuleScope = audioWorkletGlobalScope.webAudioModules.getModuleScope(moduleId);
const { WamProcessor } = ModuleScope;
let quantizeValues = [
1,
3,
6,
12,
24,
96
];
class StepModulatorProcessor extends WamProcessor {
constructor(options) {
super(options);
this.count = 0;
const { moduleId, instanceId, } = options.processorOptions;
this.lastTime = null;
super.port.start();
this.lastTime = null;
this.ticks = 0;
this.sequencers = {};
this.sequencerOrder = [];
this.currentClipId = "";
}
_generateWamParameterInfo() {
let allParams = {};
const seqs = this.allSequencers();
for (let seq of seqs) {
allParams = {
...allParams,
...seq.wamParameters()
};
}
return allParams;
}
_process(startSample, endSample, inputs, outputs) {
const { currentTime } = audioWorkletGlobalScope;
if (this.pendingClipChange && this.pendingClipChange.timestamp <= currentTime) {
this.currentClipId = this.pendingClipChange.id;
this.pendingClipChange = undefined;
}
if (!this.transportData) {
return;
}
if (this.transportData.playing && currentTime > this.transportData.currentBarStarted) {
var timeElapsed = currentTime - this.transportData.currentBarStarted;
var beatPosition = (this.transportData.currentBar * this.transportData.timeSigNumerator) + ((this.transportData.tempo / 60.0) * timeElapsed);
var tickPosition = Math.floor(beatPosition * PPQN);
const sequencers = this.allSequencers();
sequencers.forEach((sequencer, index) => {
sequencer.process(this.currentClipId, tickPosition, this._parameterState);
if (this.activeSteps) {
this.activeSteps[index] = sequencer.activeStep;
}
});
}
return;
}
async _onMessage(message) {
var _a, _b, _c, _d, _e;
if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.source) == "stepBuffer") {
const sharedBuffer = message.data.buffer;
this.activeSteps = new Float32Array(sharedBuffer);
}
else if (((_b = message.data) === null || _b === void 0 ? void 0 : _b.source) == "add") {
const seq = new _StepModulatorKernel__WEBPACK_IMPORTED_MODULE_0__.StepModulatorKernel(message.data.id, this.sequencerOrder.length, this);
seq.setRow(this.sequencerOrder.length);
this.sequencers[message.data.id] = seq;
this.sequencerOrder.push(message.data.id);
this.updateParameters();
}
else if (((_c = message.data) === null || _c === void 0 ? void 0 : _c.source) == "delete") {
if (this.sequencers[message.data.id]) {
delete this.sequencers[message.data.id];
}
this.sequencerOrder = this.sequencerOrder.filter(id => id != message.data.id);
this.sequencerOrder.forEach((id, index) => this.sequencers[id].setRow(index));
this.updateParameters();
}
else if (((_d = message.data) === null || _d === void 0 ? void 0 : _d.source) == "order") {
this.sequencerOrder = message.data.sequencerOrder;
this.sequencerOrder.forEach((id, index) => this.sequencers[id].setRow(index));
this.updateParameters();
}
else if (((_e = message.data) === null || _e === void 0 ? void 0 : _e.source) == "sequencer") {
await this.sequencers[message.data.sequencerId].onMessage(message);
}
else if (message.data && message.data.action == "play") {
this.pendingClipChange = {
id: message.data.id,
timestamp: 0,
};
}
else {
await super._onMessage(message);
}
}
_onTransport(transportData) {
this.transportData = transportData;
}
_onMidi(midiData) {
const { currentTime } = audioWorkletGlobalScope;
if ((midiData.bytes[0] & 0xf0) == 0x90) {
const seqs = this.allSequencers();
for (let s of seqs) {
const clip = s.clips.get(this.currentClipId);
if (clip && clip.state.speed == 0) {
s.activeStep = (s.activeStep + 1) % clip.length();
s.update(clip, this._parameterState);
}
}
}
this.emitEvents({
type: "wam-midi",
data: midiData,
time: currentTime
});
}
allSequencers() {
return this.sequencerOrder.map(id => this.sequencers[id]);
}
updateParameters() {
const parameters = this._generateWamParameterInfo();
let oldState = this._parameterState;
this._initialize();
for (let paramID of Object.keys(oldState)) {
if (!!this._parameterState[paramID]) {
let update = {
id: oldState[paramID].id,
value: oldState[paramID].value,
normalized: false,
};
this._setParameterValue(update, false);
this._parameterState[paramID].value = oldState[paramID].value;
}
}
}
}
try {
audioWorkletGlobalScope.registerProcessor(moduleId, StepModulatorProcessor);
}
catch (error) {
console.warn(error);
}
})();
/******/ })()
;