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

audiolooper

v0.0.10

Published

AudioLooper - Loop your tracks with automatic synchronization.

Downloads

5

Readme

audiolooper - Looping made easy

What is an audiolooper?

An audiolooper let's you loop your audiotracks in a very simple and intuitive way. The looping algorithm keeps the tracks automatically in sync.

This package is a part of the sountility collection!

How does it work?

The audiolooper tries to loope the tracks you add in a synced manner. It simply loops the tracks according to the first track you added to the looper. This makes looping easier in most cases (not always, don't expect any miracles). It was a long way until I arrived here. Before I created this package, I tried many different things in order to get a consistent looping (initially, I was using webworkers with setTimeout, but this led to very inconsistent timing and glichtes). This solution is very solid and offers consistent looping by using the webaudio BufferSourceNode objects.

Looping, explained

Here I try to explain you, how audiolooper loops your tracks in a synced manner.

When the first track is added, it is looped over as is. All the following tracks will be synced with the first track you added. The looping algorithm makes the duration of the track you added after the first one a multiple of the first tracks duration. It will always be rounded up until it is a multiple of the first tracks duration.

Example #1

  • First track duration: 1s
  • The tracks duration you add: 0,7s

:arrow_forward: The duration of the track you added becomes 1s. The track will be looped over now every 1s.

Example #2

  • First track duration: 1s
  • The tracks duration you add: 1,00001s

:arrow_forward: The duration of the track you added becomes 2s. The track will be looped over now every 2s.

Example #3

  • First track duration: 5s
  • The tracks duration you add: 7s

:arrow_forward: The duration of the track you added becomes 10s. The track will be looped over now every 10s.

Example #4

  • First track duration: 5s
  • The tracks duration you add: 4,9s

:arrow_forward: The duration of the track you added becomes 5s. The track will be looped over now every 5s.

I hope I was able to clarify how the audiolooper works! If you still have probblems understanding how it works, feel free to open an issue!

Methods

Constructor

new AudioLooper(audioCtx)

To create a new audiolooper instance, you have to pass on parameter to the constructor: A valid AudioContext object.

Adding a track

.addTrack({ id, audioBuffer, trackAdded })

To add a track to the looper, use this method. The method expects one parameter, which has to be an object of the following structure:

{
  id: ANY,
  audioBuffer: BufferSourceNode,
  trackAdded: Function
}
  • id: The id can be anything, but you'll need it refer to the tracks for later. I recommend using a library like uuid to generate ids.

  • audioBuffer: This needs to be a webaudio BufferSourceNode. This one will then be added to the looper and will be looped over.

  • trackAdded: This field must be a function. It is a callback function which will be executed when the track was added to the looper. This function can accept one parameter, which will be the final BufferSourceNode used by the looper (ATTENTION! The BufferSourceNode actually used by the looper is a different one, not the one you passed into the function).

Removing a track

.removeTrack({ id })

To remove a track, simply use this function. It requires an object ad its only parameter which has a field id, the id of the track you want to delete (you specified the id before, when you added the track).

ATTENTION! Removing the first track will lead to unexpected behaviour and will be disabled in the near future!

Pausing a track

.pauseTrack({ id })

To pause a track, simply use this method. It requires an object as its only parameter which has a field id, the id of the track. (you specified the id before, when you added the track).

Playing a paused track

.playTrack({ id })

To play a track you paused before, simply use this method. It requires an object as its only parameter which has a field id, the id of the track. (you specified the id before, when you added the track).

Get a tracks time

.getCurrentTime({ id })

To get the time(seconds) which passed since the track was added, use this method. It requires an object as its only parameter which has a field id, the id of the track. (you specified the id before, when you added the track).

Look, if a specific track exists

.exists({ id })

To check if a specific track was already added to the audiolooper, use this method. It requires an object as its only parameter which has a field id, the id of the track. (you specified the id before, when you added the track).

Code example

This is a small example which shows how to create a very simple loopstation.

import Recordy from 'recordy';
import AudioLooper from 'audiolooper';

const audioCtx = new AudioContext();

const recordy = new Recordy(audioCtx);

recordy.getInput()
  .then((hasInput) => {
    if (hasInput)
      console.log(`Got mic input!`);
    else
      console.error(`Could not get mic input.`);
  });

function render() {
  const looper = new AudioLooper(audioCtx);


  const mainDiv = document.createElement(`div`);
  mainDiv.class = `main`;

  const recordBtn = document.createElement(`button`);
  const stopRecordBtn = document.createElement(`button`);

  recordBtn.textContent = `Start recording`;
  stopRecordBtn.textContent = `Stop recording`;

  recordBtn.addEventListener(`click`, () => {
    recordy.startRecording();
  });

  stopRecordBtn.addEventListener(`click`, () => {
    recordy.stopRecording() // TRUE == Create audio object, FALSE = return blob
      .then((blob) => {
        // create arraybuffer from blob
        const fileReader = new FileReader();
        fileReader.addEventListener(`loadend`, () => {
          audioCtx.decodeAudioData(fileReader.result)
            .then((audioBuffer) => {
              const id = Math.random() * 1000;

              looper.addTrack({
                id,
                audioBuffer,
                trackAdded: (bufferNode) => {
                  bufferNode.connect(audioCtx.destination);
                }
              });
            });
        });
        fileReader.readAsArrayBuffer(blob);
      });
  });

  mainDiv.appendChild(recordBtn);
  mainDiv.appendChild(stopRecordBtn);

  document.querySelector(`body`).appendChild(mainDiv);
}

render(recordy, audioCtx);