Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

glicol

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

glicol - npm Package Compare versions

Comparing version 0.2.23 to 0.3.0

detect.js

95

glicol-engine.js
export default (t, r) => {
const TextParameterReader = t;
const RingBuffer = r;
class GlicolEngine extends AudioWorkletProcessor {

@@ -12,8 +13,13 @@ static get parameterDescriptors() {

this._paramArray = new Uint8Array(2048);
const { codeQueue, paramQueue } = options.processorOptions;
const isLiveCoding = options.isLiveCoding;
const isLiveCoding = options.processorOptions.isLiveCoding;
// console.log("options.isLiveCoding", options.processorOptions.isLiveCoding);
this.useSAB = options.processorOptions.useSAB;
if (this.useSAB) {
// console.log(this.useSAB)
this._code_reader = new TextParameterReader(
new RingBuffer(options.processorOptions.codeQueue, Uint8Array));
this._param_reader = new TextParameterReader(
new RingBuffer(options.processorOptions.paramQueue, Uint8Array));
}
this._code_reader = new TextParameterReader(new RingBuffer(codeQueue, Uint8Array));
this._param_reader = new TextParameterReader(new RingBuffer(paramQueue, Uint8Array));
this.port.onmessage = async e => {

@@ -95,2 +101,10 @@ if (e.data.type === "load") {

this._wasm.exports.update(codeUint8ArrayPtr, size)
} else if (e.data.type === "msg") {
let msg = e.data.value
let size = msg.byteLength
let msgUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size);
let msgUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, msgUint8ArrayPtr, size);
msgUint8Array.set(msg.slice(0, size));
this._wasm.exports.send_msg(msgUint8ArrayPtr, size)
} else if (e.data.type === "bpm") {

@@ -102,6 +116,2 @@ this._wasm.exports.set_bpm(e.data.value);

this._wasm.exports.set_track_amp(e.data.value);
// } else if (e.data.type === "sab") {
// } else if (e.data.type === "result") {
// this._result_reader = new TextParameterReader(new RingBuffer(e.data.data, Uint8Array));
} else {

@@ -118,29 +128,19 @@ throw "unexpected.";

let size = this._code_reader.dequeue(this._codeArray)
if (size) {
let codeUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size);
// console.log("codeUint8ArrayPtr", codeUint8ArrayPtr)
let codeUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, codeUint8ArrayPtr, size);
// console.log(this._codeArray.slice(0, size))
codeUint8Array.set(this._codeArray.slice(0, size), "this._codeArray.slice(0, size)");
this._wasm.exports.update(codeUint8ArrayPtr, size)
if (this.useSAB) {
let size = this._code_reader.dequeue(this._codeArray)
if (size) {
let codeUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size);
let codeUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, codeUint8ArrayPtr, size);
codeUint8Array.set(this._codeArray.slice(0, size), "this._codeArray.slice(0, size)");
this._wasm.exports.update(codeUint8ArrayPtr, size)
}
let size2 = this._param_reader.dequeue(this._paramArray)
if (size2) {
let paramUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size2);
let paramUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, paramUint8ArrayPtr, size2);
paramUint8Array.set(this._paramArray.slice(0, size2));
this._wasm.exports.send_msg(paramUint8ArrayPtr, size2)
}
}
let size2 = this._param_reader.dequeue(this._paramArray)
if (size2) {
let paramUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size2);
// console.log("paramUint8ArrayPtr", paramUint8ArrayPtr)
let paramUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, paramUint8ArrayPtr, size2);
// console.log(this._paramArray.slice(0, size2), "this._paramArray.slice(0, size2)")
paramUint8Array.set(this._paramArray.slice(0, size2));
// console.log("paramUint8Array",paramUint8Array)
this._wasm.exports.send_msg(paramUint8ArrayPtr, size2)
}
// if (midiSize) {
// let codeUint8ArrayPtr = this._wasm.exports.alloc_uint8array(size);
// let codeUint8Array = new Uint8Array(this._wasm.exports.memory.buffer, codeUint8ArrayPtr, size);
// codeUint8Array.set(this._codeArray.slice(0, size));
if (inputs[0][0]) { // TODO: support stereo or multi-chan

@@ -183,27 +183,2 @@ this._inPtr = this._wasm.exports.alloc(128)

registerProcessor('glicol-engine', GlicolEngine)
}
// https://gist.github.com/littledan/f7c1d1abf0e51ad4b526a8eadb2da43b
// register processor in AudioWorkletGlobalScope
// function registerProcessor(name, processorCtor) {
// return `${processorCtor};\nregisterProcessor('${name}', ${processorCtor.name});`;
// }
// const worklet = (URL.createObjectURL(
// new Blob(
// [
// registerProcessor(
// 'glicol-engine',
// GlicolEngine
// ),
// ],
// { type: 'text/javascript' }
// )
// ));
// export {worklet}
// export default whateverWorker.a.b
// registerProcessor('glicol-engine', GlicolEngine)
// `
}
import worklet from './glicol-engine'
import wasm from './glicol_wasm.wasm'
import nosab from './nosab'
import { detectBrowser } from './detect'
import {TextParameterReader, TextParameterWriter, RingBuffer} from './ringbuf'
let text = `( ${String(worklet)} )(${TextParameterReader}, ${RingBuffer})`;
// console.log(text)
var isSharedArrayBufferSupported = false;
try {
var sab = new SharedArrayBuffer(1);
var {name, _} = detectBrowser();
if (sab && !name.includes('Safari') ) {
isSharedArrayBufferSupported = true
}
} catch(e){
console.warn(nosab)
}
class Engine {
constructor(isLiveCoding) {
constructor({
audioContext = new AudioContext(),
isLiveCoding = false,
connectTo,
onLoaded = () => {}
}={}) {
// console.log("audioContext", audioContext);
// console.log("connectTo", connectTo, "!connectTo", !connectTo);
(async () => {
// isLiveCoding = true
this.encoder = new TextEncoder('utf-8');
this.decoder = new TextDecoder('utf-8');
this.audioContext = new AudioContext()
this.audioContext = audioContext;
this.audioContext.suspend()

@@ -20,18 +41,29 @@

let sab = RingBuffer.getStorageForCapacity(2048, Uint8Array);
let rb = new RingBuffer(sab, Uint8Array);
this.codeWriter = new TextParameterWriter(rb);
let sab2 = RingBuffer.getStorageForCapacity(2048, Uint8Array);
let rb2 = new RingBuffer(sab2, Uint8Array);
this.paramWriter = new TextParameterWriter(rb2);
this.node = new AudioWorkletNode(this.audioContext, 'glicol-engine', {
if (isSharedArrayBufferSupported) {
let sab = RingBuffer.getStorageForCapacity(2048, Uint8Array);
let rb = new RingBuffer(sab, Uint8Array);
this.codeWriter = new TextParameterWriter(rb);
let sab2 = RingBuffer.getStorageForCapacity(2048, Uint8Array);
let rb2 = new RingBuffer(sab2, Uint8Array);
this.paramWriter = new TextParameterWriter(rb2);
this.node = new AudioWorkletNode(this.audioContext, 'glicol-engine', {
outputChannelCount: [2],
processorOptions: {
codeQueue: sab,
paramQueue: sab2,
useSAB: true,
isLiveCoding: isLiveCoding,
},
})
} else {
this.node = new AudioWorkletNode(this.audioContext, 'glicol-engine', {
outputChannelCount: [2],
processorOptions: {
codeQueue: sab,
paramQueue: sab2
},
isLiveCoding: isLiveCoding === true ? true: false
})
useSAB: false,
isLiveCoding: isLiveCoding,
}
})
}

@@ -41,4 +73,5 @@ this.sampleBuffers = {}

this.node.port.onmessage = async e => {
this.log("%c GLICOL loaded. ", "background:#3b82f6; color:white; font-weight: bold; font-family: Courier")
if (e.data.type === 'ready') {
if (Object.keys(this.sampleBuffers).length !== 0) {

@@ -69,2 +102,3 @@ for (let key in this.sampleBuffers) {

}
onLoaded()
} else if (e.data.type === 'e') {

@@ -99,25 +133,42 @@ // let decoder = new TextDecoder("utf-8")

}
this.node.connect(this.audioContext.destination)
if (!connectTo) {
this.node.connect(this.audioContext.destination)
} else {
this.node.connect(connectTo)
}
// wasm({env:{now:Date.now}}).then(res=>window._wasm=res);
// console.log("wasm func; we don't call it, just want the url",wasm)
// this.log("the imported wasm:", wasm)
// this.log("the imported wasm as str:", String(wasm))
let url = String(wasm).replaceAll(' ', '')
// console.log("wasm url remove all spaces:",url)
url = url.split(",\"/")[1];
// console.log("wasm url trim prefix:",url)
url = url.split("\")")[0]
// console.log("wasm url trim end:",url)
// console.log("url",url)
fetch(url)
.then(response => response.arrayBuffer())
let urlSplit = url.split("/");
urlSplit.shift()
let urlNoHead = "/"+urlSplit.join("/")
let finalUrl = urlNoHead.split(".wasm")[0] + ".wasm"
// console.log(finalUrl)
fetch(finalUrl).then(response => response.arrayBuffer())
.then(arrayBuffer => {
this.node.port.postMessage({
type: "load", obj: arrayBuffer
type: "load",
obj: arrayBuffer
})
})
.catch(e=>{
console.log(e)
console.error("fail to load the wasm module. please report it here: https://github.com/chaosprint/glicol")
})
})();
}
run(code) {
this.audioContext.resume()
if (this.codeWriter.available_write()) {
this.codeWriter.enqueue(this.encoder.encode(code))
if (isSharedArrayBufferSupported) {
// console.log("isSharedArrayBufferSupported", isSharedArrayBufferSupported);
if (this.codeWriter.available_write()) {
this.codeWriter.enqueue(this.encoder.encode(code))
}
} else {
this.node.port.postMessage({
type: "run",
value: this.encoder.encode(code)
})
}

@@ -128,5 +179,13 @@ }

let str;
str = msg.slice(-1) === ";"? msg : msg+";"
if (this.paramWriter.available_write()) {
this.paramWriter.enqueue(this.encoder.encode(str))
str = msg.slice(-1) === ";"? msg : msg+";" // todo: not robust
if (isSharedArrayBufferSupported) {
if (this.paramWriter.available_write()) {
this.paramWriter.enqueue(this.encoder.encode(str))
}
} else {
this.node.port.postMessage({
type: "msg",
value: this.encoder.encode(str)
})
}

@@ -147,2 +206,6 @@ }

connect(target) {
this.node.connect(target)
}
reset() {

@@ -164,5 +227,83 @@ // this.node

showAllSamples() {
window.table(Object.keys(this.sampleBuffers))
console.table(Object.keys(this.sampleBuffers))
return ``
}
async addSampleFiles(name, url) {
if (url === undefined) {
var input = document.createElement('input');
input.type = 'file';
input.multiple = true
input.onchange = e => {
var files = e.target.files;
// log(files)
for (var i = 0; i < files.length; i++) {
((file) => {
var reader = new FileReader();
reader.onload = async (e) => {
let name = file.name.toLowerCase().replace(".wav", "").replace(".mp3", "").replaceAll("-","_").replaceAll(" ","_").replaceAll("#","_sharp_")
await this.audioContext.decodeAudioData(e.target.result, buffer => {
this.sampleBuffers[name] = buffer
var sample;
if (buffer.numberOfChannels === 1) {
sample = buffer.getChannelData(0);
} else if (buffer.numberOfChannels === 2) {
sample = new Float32Array( buffer.length * 2);
sample.set(buffer.getChannelData(0), 0);
sample.set(buffer.getChannelData(1), buffer.length);
} else {
throw(Error("Only support mono or stereo samples."))
}
console.log("loading sample: ", name)
this.node.port.postMessage({
type: "loadsample",
sample: sample,
channels: buffer.numberOfChannels,
length: buffer.length,
name: this.encoder.encode("\\"+ name),
sr: buffer.sampleRate
})
})
// log(`Sample %c${key.replace(".wav", "")} %cloaded`, "color: green; font-weight: bold", "")
};
reader.readAsArrayBuffer(file);
})(files[i]);
}
}
input.click();
} else {
this.audioContext.suspend()
let myRequest = new Request(url);
await fetch(myRequest).then(response => response.arrayBuffer())
.then(arrayBuffer => {
this.audioContext.decodeAudioData(arrayBuffer, buffer => {
// log(new Int16Array(buffer.getChannelData(0).buffer))
// let name = file.name.toLowerCase().replace(".wav", "").replace(".mp3", "").replace("-","_").replace(" ","_")
this.sampleBuffers[name] = buffer
var sample;
if (buffer.numberOfChannels === 1) {
sample = buffer.getChannelData(0);
} else if (buffer.numberOfChannels === 2) {
sample = new Float32Array( buffer.length * 2);
sample.set(buffer.getChannelData(0), 0);
sample.set(buffer.getChannelData(1), buffer.length);
} else {
throw(Error("Only support mono or stereo samples."))
}
this.node.port.postMessage({
type: "loadsample",
sample: sample,
channels: buffer.numberOfChannels,
length: buffer.length,
name: this.encoder.encode("\\"+ name),
sr: buffer.sampleRate
})
}, function(e){ console.log("Error with decoding audio data" + e.err); })
});
this.audioContext.resume()
}
}
async loadSamples() {

@@ -203,7 +344,11 @@

})
// log(window.showAllSamples())
// log(this.showAllSamples())
})
// window.actx.suspend()
// this.audioContext.suspend()
// ['bd0000', 'clav', "pandrum", "panfx", "cb"]
}
log(...params) {
setTimeout(console.log.bind(console, ...params));
}
}

@@ -210,0 +355,0 @@

{
"name": "glicol",
"version": "0.2.23",
"version": "0.3.0",
"description": "An light-weight, garbage-collection-free, and easy-to-use audio engine for browser-based music apps.",

@@ -5,0 +5,0 @@ "main": "index.js",

## What's this?
This is a light-weight, garbage-collection free, memory-safe and easy-to-use audio library for browsers. It's written in Rust and ported to JS via WebAssembly and runs in AudioWorklet. The communication is realised with SharedArrayBuffer.
This is a light-weight, garbage-collection free, memory-safe and easy-to-use audio library for browsers. It's written in Rust and ported to JS via WebAssembly and runs in AudioWorklet. The communication is realised with SharedArrayBuffer*.
> Note that you need to have `cross-origin isolation` enabled on the web server (both the dev server and the one you deploy your web app) to use this package. For `vite` dev server, you can use my plugin [here](https://github.com/chaosprint/vite-plugin-cross-origin-isolation). For deployment on `Netlify` or `Firebase`, check their docs for editing the header files. If you use a customised server, you have to figure it out yourself.
> *Without SAB, you can still use Glicol. However, to get the best audio performance, you need to have `cross-origin isolation` enabled on the web server (both the dev server and the one you deploy your web app) to use this package. For `vite` dev server, you can use my plugin [here](https://github.com/chaosprint/vite-plugin-cross-origin-isolation). For deployment on `Netlify` or `Firebase`, check their docs for editing the header files. If you use a customised server, you have to figure it out yourself.

@@ -26,16 +26,8 @@ ## Why `glicol.js`?

Rust is also famous for its error handling. `glicol.js` has taken advantage of that and offers a robust error handling mechanism.
Rust is also famous for its error handling. `glicol.js` has taken advantage of that and offers a robust error handling mechanism. The principle is "Musique Non-Stop", i.e. when there is an error, it will be reported in the console while the music will continue as before.
> WIP: the error report is coming soon.
### Easy to use
With the top-level audio performance in the browser, Glicol is yet easy to use. The balance between minimalism and readability/ergonomics is consistent in the API designing.
With the top-level audio performance in the browser, Glicol is yet easy to use. The balance between minimalism and readability/ergonomics is consistent in the API designing. After you `npm i glicol`, you can just write:
> As this is not a stable version yet, the APIs may significantly change in the future. If you wish to test or use it in a project, please contact me.
## Usage
After you `npm i glicol`, you can just write:
```js

@@ -46,3 +38,3 @@ import Glicol from "glicol"

You can also write the graph in this way:
Then write the graph in this way:

@@ -56,4 +48,6 @@ ```js

Simple as that. No need to create a node, and then connect it everywhere.
Simple as that.
No need to create a node, and then connect it everywhere.
Note that there are two `chains` here, one is called `o` and the other is `~am`.

@@ -83,11 +77,10 @@

```js
glicol.send_msg(`o, 0, 0, 110`)
// chain "o", node_index 0, param 0, set to 110
glicol.sendMsg(`o, 0, 0, 110`)
```
This will send message to set:
- chain: `o`
- node_index: 0,
- para_index: 0,
- set_to_number: 110
## API reference (coming soon...)
> As this is not a stable version yet, the APIs may significantly change in the future. If you wish to test or use it in a project, please contact me.
## Alternative usage - Glicol DSL

@@ -116,4 +109,4 @@

```js
// track "o", node_index 0, param 0, set to 110
glicol.send_msg(`o, 0, 0, 110`);
// chain "o", node_index 0, param 0, set to 110
glicol.sendMsg(`o, 0, 0, 110`);
```

@@ -128,5 +121,23 @@

```js
glicol.send_msg(`o, 0, 0, 110; o, 1, 0, 500; o, 1, 1, 0.8`);
glicol.sendMsg(`o, 0, 0, 110; o, 1, 0, 500; o, 1, 1, 0.8`);
```
## Extension
You can provide an `audioContext` to glicol and use the output of glicol to another node from that `audioContext`:
```js
import Glicol from 'glicol'
const myAudioContext = new AudioContext()
const gainNode = myAudioContext.createGain();
gainNode.gain.value = 0.1
gainNode.connect(myAudioContext.destination)
const glicol = new Glicol({
audioContext: myAudioContext,
connectTo: gainNode
})
```
## Feedback

@@ -133,0 +144,0 @@

@@ -64,8 +64,8 @@ // from: https://github.com/padenot/ringbuf.js

static getStorageForCapacity(capacity, type) {
if (!type.BYTES_PER_ELEMENT) {
throw "Pass in a ArrayBuffer subclass";
if (!type.BYTES_PER_ELEMENT) {
throw "Pass in a ArrayBuffer subclass";
}
var bytes = 8 + (capacity + 1) * type.BYTES_PER_ELEMENT;
return new SharedArrayBuffer(bytes);
}
var bytes = 8 + (capacity + 1) * type.BYTES_PER_ELEMENT;
return new SharedArrayBuffer(bytes);
}
constructor(sab, type) {

@@ -72,0 +72,0 @@ if (!ArrayBuffer.__proto__.isPrototypeOf(type) &&

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc