andy-audio

![andy](static/andy.png)


Keywords
audio, mp3, mid, midi, m4a, sound, player, games, music, game-development, music-library, synth, synthesizer, typescript, webaudio
License
ICU
Install
npm install andy-audio@0.3.4

Documentation

Andy

andy

Andy is a simple library to play sounds (.wav, .m4a) and midi files (.mid) on the web.

(Andy Logo by Peter Chen)

Examples:

Andy is the audio system from within my online game engine. It is the main part of the system, but the 3d audio, and some other bits have been stripped out.

Adding Andy to Your Project

You can use standard NPM.

  npm i andy-audio
  yarn add andy-audio

When you do your build into your own project, you will also have to copy a few workers from the node_modules folder and store them wherever you load your javascript files from:

  cp node_modules/andy-audio/dist/types/metronome.worker.js dist/metronome.worker.js
  cp node_modules/andy-audio/dist/types/noise.processor.js dist/noise.processor.js

Have a look through the example project code.

Roadmap

See the Project Board for the latest.

Usage

Loading and Playing a WAV, M4A, etc

Andy is a "low level" library in that it just provides an easier way to load and play audio files. You'll have to write your own manager / context / come up with your own way of managing the files (see example below). Here is a "raw" Typescript example of Andy's basic usage:

import { SoundSystem, Sound, loadSound } from "andy-audio";

const system = new SoundSystem();
const sounds = {
  s0: loadSound("http://envelope.robrohan.com/andy/coin4.wav", system),
  s1: loadSound("http://envelope.robrohan.com/andy/coin3.m4a", system),
};

Promise.all([sounds.s0, sounds.s1]).then((s) => {
  sounds.s0 = s[0];
  sounds.s1 = s[1];

  window.sound = system;
  window.sounds = sounds;
});

Then later you could play the sound (say from the HTML page itself):

function uiSound() {
  window.sound.play(window.sounds.s0);
}

Of course, it's better to use Andy within your chosen framework. For example in a React Context, or what have you. Additionally, you can provide your own loadSound method if you wish (have a look at that function).

Loading and Playing a MIDI file

Andy also can play MIDI files. It uses it currently uses it's own hand rolled synth (see Andy's Synth below), and tries to support all the General Midi Standard set of instruments (but it currently does not).

The following is an example of loading a midi file, and playing it using the built in synth.

import {
  SoundSystem,
  loadMidi,
  createRun,
  insertRun,
  playHeadToggle,
  playHeadRewind,
  rewindRun,
  silenceRun,
} from "andy-audio";

const system = new SoundSystem();

loadMidi("static/TestMidiTuneMarkers.mid").then((song) => {
  // Create a run from the midi song data we loaded
  songRun = createRun(system, song);
  // Insert the run into the player
  insertRun(system, songRun);
  playHeadToggle();

  // Example of how to stop it
  setTimeout(() => {
    playHeadToggle();
    silenceRun(songRun);
    // Rewind example
    playHeadRewind();
    rewindRun(songRun);
  }, 2000);
});

Exported midi files exported from Reaper and MuseScore 3 are what we currently test with. However, any midi file from any program should theoretically work...theoretically.

React Context Example

Here is a simple example of using andy within a React Context (in Typescript).

Create a file called SoundContext.tsx with the following content:

import React from "react";
import { Sound, SoundSystem, loadSound } from "andy-audio";

export interface ISounds {
  load: Function;
  play: Function;
}

export interface SoundRepository {
  [key: string]: Sound;
}

const sounds = {} as SoundRepository;
const system = new SoundSystem();

const createKey = (url: string): string => {
  const parts = url.split("/");
  return parts[parts.length - 1];
};

export const SoundContext = React.createContext<ISounds>({
  load: async (soundUrl: string, key?: string) => {
    const sound = await loadSound(soundUrl, system);
    sounds[key || createKey(soundUrl)] = sound;
  },
  play: (name: string, loop: boolean = false) => {
    system.play(sounds[name], loop);
  },
});

You can then use this context within your app. Here is an example usage with the create react app:

    ...
    import { SoundContext } from './SoundContext';
    ...

    const App = () => {
      const ctx = useContext(SoundContext);
      // Note: browsers require a user interaction to play a sound
      ctx.load("http://localhost:3000/coin4.wav", "coin");

      return (
        <div className="App">
            <div
              className="App-link"
              onClick={() => ctx.play("coin4")}
            >Learn React</div>
        </div>
      );
    }

Andy's Synth

Andy's Synth is currently very basic. While we are planning on future version supporting more robust synths, Andy currently has a very, very simple Additive Synthesis.

We also do not have many instruments created. See this file for currently supported instruments - patches welcome!

Andy will use the Organ sound if it doesn't have a supported instrument.

The current implementation consists of a chain of:

  • Max 8 oscillators. Each independently can be a:

    • sine
    • triangle
    • square
    • white noise (use "custom")
  • One filter

    • highpass
    • lowpass
    • bandpass
    • highshelf
    • lowshelf
    • peaking
    • notch
  • One ADSR

  • One Compressor

They are applied in this order.

Each oscillators can have it's own volume, and it's frequency can be set using one of the following:

  • frequency - a specific frequency. "1300" for example

  • freqOffset - a frequency off set from the base frequency. "200" for example. This means that if they pressed the key that did "440" it would generate a wave at "640". If they did "620" it would generate "820".

  • freqMult - multiply to the base frequency. "2" for example. If the base frequency was "440" this wave would generate "880".

Checkout the Patch Editor, use keys z-, and q-i on your keyboard as piano keys, and mess with the JSON in the textbox to get a feel for what you can do with it.

Tip: open the browser console to check if you've typed something that made the json invalid - like not starting a value with a leading zero, or leaving out a comma.