npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

reactive-synth-envelope-generator

v0.1.1

Published

WASM implementation of an envelope generator audio processing node compatible with the web audio API. Created for reactive-synth.

Downloads

11

Readme

Reactive-Synth envelope generator

A WASM binary and a AudioWorkletProcessor implementation for an envelope generator created for Reactive-Synth.

It generates a linear 5-step envelope output with values between 0 and 1.

Initialization

Copy both files (envelope-generator.js and reactive_synth_envelope_generator.wasm) to your static assets folder (preferably at build-time) to use in a web project. Assuming they are both in a worklets folder ready to be served by your web server, the follow steps are needed to initialize and use an evelope generator instance in your audio graph.

  1. Fetch the WASM binary and read it as an ArrayBuffer.

    const envelopeGeneratorBinaryResponse = await fetch(
      "/worklets/reactive_synth_envelope_generator.wasm"
    );
    const envelopeGeneratorBinary =
      await envelopeGeneratorBinaryResponse.arrayBuffer();
  2. Add the js module to your audio worklet context.

    const audioContext = new AudioContext();
    // ...
    await audioContext.audioWorklet.addModule("/worklets/envelope-generator.js");
  3. Create an AudioWorkletNode instance from the registered module. It's registered under the name: reactive-synth-envelope-generator.

    const envelopeGeneratorNode = new AudioWorkletNode(
      audioContext,
      "reactive-synth-envelope-generator",
      {
        numberOfInputs: 0,
        numberOfOutputs: 1,
        channelCount: 1,
        channelCountMode: "explicit",
        outputChannelCount: [1],
        processorOptions: { sampleRate: audioContext.sampleRate },
      }
    );
  4. Send the WASM binary to the envelope generator node through its message port. It will respond through the same port when it's ready to run.

    envelopeGeneratorNode.port.postMessage({
      type: "wasm",
      wasmModule: envelopeGeneratorBinary,
    });
    envelopeGeneratorNode.port.start();
  5. Wait for the envelope generator node to tell you it's ready through the message port.

    await new Promise((resolve) =>
      envelopeGeneratorNode.port.addEventListener("message", function moduleReady(event) {
        if (
          event.data &&
          event.data.type === "module-ready" &&
          event.data.value
        ) {
          resolve();
          envelopeGeneratorNode.port.removeEventListener("message", moduleReady);
        }
      })
    );

The envelope generator is now ready to connect to your audio graph.

Parameters

The trigger parameter is effectively the input for the module. It's a parameter rather than an actual input because it can be convenient to set the value to manually start and stop an envelope, or add a constant to the output of whatever other modules are connected. Any value above zero starts an envelope, and falling back to 0 or below starts the release phase then ends the envelope.

The envelope shape is controlled by the rest of the parameters: attackValue, attackTime, holdTime, decayTime, sustainValue, and releaseTime. Times are in seconds, and the values for attack and sustain must be between 0 and 1.

Output

The envelope generator node only has one output value, which is between 0 and 1. Usually, you will want to plug it into a GainNode to scale it for whatever purpose you need. This can be duplicated over multiple channels if that somehow helps you by changing outputChannelCount on construction of the AudioWorkletNode.

Message port events

Besides being used to send the WASM binary to the processor, the message port is also used to make visualizations and manual triggering possible.

The envelope generator can be manually triggered by sending messages with type "manual-trigger" and a value of true or false.

The port will send a message of type "trigger-change" with a value of true or false whenever the trigger state changes for any reason, be it an automated input or manual trigger. The value changes to false when the trigger is released (value falls to 0 or below), normally before the release step of the envelope is done. In Reactive-Synth, this is used to blink a light on the trigger button.

Since the state changes at audio rates (usually 44.1kHz or 48kHz) and we want to visualize at video rates (usually 60Hz), the node doesn't just spam out state updates, you have to ask for each one (usually from a requestAnimationFrame call). Post a message with type "get-state", and it will post a message back with type "state" and a state field with this structure:

{
  stage: "rest" | "attack" | "hold" | "decay" | "sustain" | "release";
  stageProgress: number;
  outputValue: number;
  parameters: {
    attackValue: number;
    attackTime: number;
    holdTime: number;
    decayTime: number;
    sustainValue: number;
    releaseTime: number;
  }
}