Search Topic
WAD Audio API JavaScript Library
Web Audio DAW, abbreviates as WAD. It is an audio manipulator library based on WA API. It greatly simplifies the process of creating, playing, and manipulating audio. It also has support for sprites, various custom-defined FX, microphone input. This library works in any browser that supports the Web Audio API. It audio input and attempt to determine the frequency and note name of the source.
Installation
To use WadJS in your project, simply include the script in your HTML file.
<script src="https://unpkg.com/web-audio-daw"></script>
WadJS is also available as an npm module.
npm install web-audio-daw
import Wad from 'web-audio-daw';
Panning
Wad supports two types of panning: stereo-panning, and 3d-panning. Stereo-panning works simply by specifying the left/right balance of the sound using a number between 1 and -1. Here a value of 1 means the sound is panned hard-right, and a value of -1 means the sound is panned hard-left.
In 3d-panning, directly setting the left/right stereo balance is not possible. It shows the distance of the sound source from the audio listener. Any time you would pass in a panning parameter (either to the constructor, the play()
method, or the setPanning()
method), you can pass it in as a three element array to specify the X, Y, and Z location of the sound. You can set the panning to arbitrarily high or low values, but it will make the sound very quiet, since it’s very far away. Here two different panning models are available using 3d-panning. The HRTF panning model is higher quality, but the equal power panning model is more performant. Mostly equal power panning model used in practice.
var saw = new Wad({
source : ‘sawtooth’,
panning : [0, 1, 10],
panningModel : ‘HRTF’,
rolloffFactor : 1 // other properties of the panner node can be specified in the constructor, or on play()
})
Filters
The filter constructor argument can be passed an object or an array of objects. If an array is passed, the filters are applied in that order. Whichever form is passed to the constructor should also be passed to the play argument.
filter: [
{type : ‘lowpass’, frequency : 600, q : 1, env : {frequency : 800, attack : 0.5}},
{type : ‘highpass’, frequency : 1000, q : 5}
]
{type : ‘lowpass’, frequency : 600, q : 1, env : {frequency : 800, attack : 0.5}},
{type : ‘highpass’, frequency : 1000, q : 5}
]
Microphone InputMicrophone input acts as a source for a Wad. Reverb or filters can be applied on this but envelop or filter envelop is not possible to apply on microphone input. it will constantly stream the mic input through all applied effects (filters, reverb, etc) and out through your speakers or headphones as soon as you call the
tripleOscillator.play({ pitch : ‘Eb3’}) // This note is filtered and panned.
play()
method on that Wad. To terminate the method thestop()
method on a microphone Wad to disconnect your microphone from that Wad. Microphone input needs headphones otherwise problem will occur.
var voice = new Wad({ source : 'mic', reverb : { wet : .4 },
filter : { type : 'highpass', frequency : 500 },
panning : -.2 })
// You must give your browser permission to use your microphone before calling play().
voice.play()
Configuring Reverb
Server needs to send impulse response to use reverb. This type of response is a small audio file like a wav or mp3, that shows the acoustic characteristics along with physical space. Online response can be self created though it is created in online.
Audio Sprites
When many short audio clips are included loading them as a single, longer audio clip, and play sections from that longer clip as needed. It will enhance the level of performance.
var helloWorld = new Wad({
source: ‘https://www.myserver.com/audio/hello-world.wav’,// add a key for each sprite
sprite: {
hello : [0, .4], // the start and end time, in seconds
world : [.4,1]
}
});// for each key on the sprite object in the constructor above, the wad that is created will have a key of the same name, with a play() method.
helloWorld.hello.play();
helloWorld.world.play();// you can still play the entire clip normally, if you want.
helloWorld.play();// if you hear clicks or pops from starting and stopping playback in the middle of the clip, you can try adding some attack and release to the envelope.
helloWorld.hello.play({env:{attack: .1, release:.02}})
source: ‘https://www.myserver.com/audio/hello-world.wav’,// add a key for each sprite
sprite: {
hello : [0, .4], // the start and end time, in seconds
world : [.4,1]
}
});// for each key on the sprite object in the constructor above, the wad that is created will have a key of the same name, with a play() method.
helloWorld.hello.play();
helloWorld.world.play();// you can still play the entire clip normally, if you want.
helloWorld.play();// if you hear clicks or pops from starting and stopping playback in the middle of the clip, you can try adding some attack and release to the envelope.
helloWorld.hello.play({env:{attack: .1, release:.02}})
Logging
WadJS can log various warnings and notices to the console, but these are totally optional. To view these messages in the console, you can increase Wad’s verbosity.
Wad.logs.verbosity = 0 // WadJS will print nothing to your console. This is the default setting. Wad.logs.verbosity = 1 // View some notices and warnings, e.g. audio context started, midi devices connected, //etc. These logs should not print more than once. Wad.logs.verbosity = 2 // View all notices and warnings, including those from play() and stop(). //These logs might print many times.
Sound Iterator
The SoundIterator
object is used for playing sounds in a random order or repeatedly through a loop. It is good for footstep sounds, for example.
var iterator = new Wad.SoundIterator({
files: [new Wad({source:’square’}), new Wad({source:’triangle’})],
files: [new Wad({source:’square’}), new Wad({source:’triangle’})],
// Takes Wad objects, or files that would be passed to source. If it is passed a file that is not a Wad object,
then it will create a generic Wad object with the passed file as the source.
random: false, // either play a random order (true), or play in the order of the list (false)
randomPlaysBeforeRepeat: 0, // This value says the amount of plays that need to happen before
random: false, // either play a random order (true), or play in the order of the list (false)
randomPlaysBeforeRepeat: 0, // This value says the amount of plays that need to happen before
//a sound can be repeated. This only works if the length of the iterator is 3 or more, and this value is max 1 less than the length of the sound list.
})
})
The methods are:
iterator.play(args) // Plays the next sound in the list, or next random sound following the random rules. //The passed args are the normal args that can be passed to Wad.play(). The function returns a Promise. iterator.add(sound) // Pass in either a Wad object or an object that would be passed as a source in a new Wad. //It returns the SoundIterator object to be chained. iterator.remove(sound) // pass in the Wad instance you want to have removed from the iterator. //Only Wad objects that were added as Wad objects can be removed.
Tuna Effects
Tuna, everyone’s favorite Web Audio effects library, is included in WadJS. This makes it super easy to add effects from Tuna to any Wad or PolyWad.
let itBeTuna = new Wad({
source : ‘sine’,
tuna : {
Overdrive : {
outputGain: 0.5, //0 to 1+
drive: 0.7, //0 to 1
curveAmount: 1, //0 to 1
algorithmIndex: 0, //0 to 5, selects one of our drive algorithms
bypass: 0
},
Chorus : {
intensity: 0.3, //0 to 1
rate: 4, //0.001 to 8
stereoPhase: 0, //0 to 180
bypass: 0
}
}
})
source : ‘sine’,
tuna : {
Overdrive : {
outputGain: 0.5, //0 to 1+
drive: 0.7, //0 to 1
curveAmount: 1, //0 to 1
algorithmIndex: 0, //0 to 5, selects one of our drive algorithms
bypass: 0
},
Chorus : {
intensity: 0.3, //0 to 1
rate: 4, //0.001 to 8
stereoPhase: 0, //0 to 180
bypass: 0
}
}
})
Audio Listener
WadJS wraps the AudioListener to provide uniformity across browsers. The AudioListener is only useful when using 3D panning.Though both can be used to influence the listeners. The default position and orientation is: positionX=0, positionY=0, positionZ=0, forwardX=0, forwardY=0, forwardZ=-1, upX=0, upY=1, upZ=0.
- Wad.listener.setPosition(x,y,z) -> setPosition moves the listener to the specified coordinates. Take note that the web audio API has X move left and right, y move up and down, and z move forward and back. So if one is moving around a flat environment, then x and z will want to be used, and not X and Y.
- Wad.listener. setOrientation(forwardX, forwardY, forwardZ, upX, upY, upZ) -> This takes two direction vectors. Neither vector’s coordinates have units. The first vector is the direction the user’s nose is facing. The second vector is the direction of the top of the listener’s head.
- Wad.listener.getPosition() -> returns a 3 element list of the user’s positionX.value, positionY.value, and positionZ.value.
- Wad.listener.getOrientation() -> returns a six element array of: forwardX.value, forwardY.value, forwardZ.value, upX.value, upY.value, and upZ.value.
- To set or get a value directly, do:
listener.positionX.value
.
Wad.listener.setPosition(1,0,0)
console.log(Wad.listener.positionX.value)
Wad.listener.forwardZ.value += 1
console.log(Wad.listener.getPosition()[0])
console.log(Wad.listener.positionX.value)
Wad.listener.forwardZ.value += 1
console.log(Wad.listener.getPosition()[0])
Play Labels
When you call stop()
on a Wad, it will only stop the most recently triggered note. If you want to retain control over multiple notes that played from the same Wad, you can label those notes when play()
is called. When stop()
is called, you can pass in a label argument to stop all currently sustained notes with that label.
saw.play({pitch : 'A4', label : 'A4'}) // The label can be any string, //but using the same name as the note is often sensible. saw.play({pitch : 'G4', label : 'G4'}) saw.stop('A4') // The first note will stop, but the second note will continue playing.
External FX
Sometimes you might want to incorporate external libraries into Wad, for example FX or visualizers. You can override the constructExternalFx and setUpExternalFxOnPlay methods to add those nodes to the wad chain. In the following example the values are hardcoded, but they could easily have been passed as arguments to play.
//For example to add a Tuna chorus you would put this somewhere in your own code, and also include the Tuna library:var tuna;
Wad.prototype.constructExternalFx = function(arg, context){
this.tuna = new Tuna(context);
this.chorus = arg.chorus;
};Wad.prototype.setUpExternalFxOnPlay = function(arg, context){
var chorus = new tuna.Chorus({
rate : arg.chorus.rate || this.chorus.rate,
feedback : arg.chorus.feedback || this.chorus.feedback,
delay : arg.chorus.delay || this.chorus.delay,
bypass : arg.chorus.bypass || this.chorus.bypass
});
chorus.input.connect = chorus.connect.bind(chorus) // we do this dance because tuna exposes its input differently.
this.nodes.push(chorus.input) // you would generally want to do this at the end unless you are working with something that does not modulate the sound (i.e, a visualizer)
};
Wad.prototype.constructExternalFx = function(arg, context){
this.tuna = new Tuna(context);
this.chorus = arg.chorus;
};Wad.prototype.setUpExternalFxOnPlay = function(arg, context){
var chorus = new tuna.Chorus({
rate : arg.chorus.rate || this.chorus.rate,
feedback : arg.chorus.feedback || this.chorus.feedback,
delay : arg.chorus.delay || this.chorus.delay,
bypass : arg.chorus.bypass || this.chorus.bypass
});
chorus.input.connect = chorus.connect.bind(chorus) // we do this dance because tuna exposes its input differently.
this.nodes.push(chorus.input) // you would generally want to do this at the end unless you are working with something that does not modulate the sound (i.e, a visualizer)
};
Presets
If you’d like to use a pre-configured Wad, check out the presets. They should give you a better idea of the sorts of sounds that you can create with WadJS. For example, you can create a Wad using the preset ‘hiHatClosed’ like this:
var hat = new Wad(Wad.presets.hiHatClosed);
PolyWads
In many cases, it is useful to group multiple Wads together. This can be accomplished with a PolyWad, a multi-purpose object that can store other Wads and PolyWads. There are two main cases where you might want to group several Wads together. One case is when you want to make a complex instrument that uses multiple oscillators. Other audio synthesis programs often have instruments that combine multiple oscillators, with names like ‘TripleOscillator’ or ‘3xOSC’.
var sine = new Wad({ source : 'sine' }) var square = new Wad({ source : 'square' }) var triangle = new Wad({ source : 'triangle' }) var tripleOscillator = new Wad.Poly() tripleOscillator.add(sine).add(square).add(triangle) // Many methods are chainable for convenience. tripleOscillator.play({ pitch : 'G#2'}) tripleOscillator.setVolume(.5) tripleOscillator.stop() // play(), stop(), and various setter methods can be called on a PolyWad just as they would be called on a regular Wad. tripleOscillator.remove(triangle) // It's really just a double-oscillator at this point.
The second main case in which you would want to group several Wads together is to make a mixer track, where several Wads share a set of effects and filters.
var mixerTrack = new Wad.Poly({
filter : {
type : 'lowpass',
frequency : 700,
q : 3
},
panning : 1
})
mixerTrack.add(tripleOscillator).add(triangle)tripleOscillator.play({ pitch : ‘Eb3’}) // This note is filtered and panned.
Compression
Rich or modern sound needs to compress the range of a song. A compressor can modify the song according to choices.
var compressor = new Wad.Poly({
compressor : {
attack : .003 // The amount of time, in seconds, to reduce the gain by 10dB.
//This parameter ranges from 0 to 1.
knee : 30 // A decibel value representing the range above the threshold where the curve smoothly transitions to the "ratio" portion. This parameter ranges from 0 to 40.
ratio : 12 // The amount of dB change in input for a 1 dB change in output. This parameter ranges from 1 to 20.
release : .25 // The amount of time (in seconds) to increase the gain by 10dB. This parameter ranges from 0 to 1.
threshold : -24 // The decibel value above which the compression will start taking effect. This parameter ranges from -100 to 0.
}
})
Recording
PolyWads can be created with a recorder, which can save the output of the PolyWad to an audio file.
let voice = new Wad({source: ‘mic’})
let polywad = new Wad.Poly({
recorder: {
options: { mimeType : ‘audio/webm’ },
onstop: function(event) {
let blob = new Blob(this.recorder.chunks, { ‘type’ : ‘audio/webm;codecs=opus’ });
window.open(URL.createObjectURL(blob));
};
}
})
polywad.add(voice)
voice.play()
polywad.recorder.start()
// make some noise
polywad.recorder.stop() // a new window should open, where you can download the file you just created
let polywad = new Wad.Poly({
recorder: {
options: { mimeType : ‘audio/webm’ },
onstop: function(event) {
let blob = new Blob(this.recorder.chunks, { ‘type’ : ‘audio/webm;codecs=opus’ });
window.open(URL.createObjectURL(blob));
};
}
})
polywad.add(voice)
voice.play()
polywad.recorder.start()
// make some noise
polywad.recorder.stop() // a new window should open, where you can download the file you just created
Audio Meter
When voice is clipping an audio meter can make you aware of it, helps the volume level of Poly WAD’s output. The creation of PolyWADs automatically create audio meter.
var sawtooth = new Wad({source:’sawtooth’, env:{hold:1, release:.2}})
var triangle = new Wad({source:’triangle’, env:{hold:1, release:.2}})
var polywad = new Wad.Poly({
audioMeter: {
clipLevel: .98, // the level (0 to 1) that you would consider “clipping”.
averaging: .95, // how “smoothed” you would like the meter to be over time. Should be between 0 and less than 1.
clipLag: 750, // how long you would like the “clipping” indicator to show after clipping has occured, in milliseconds.
},
})
polywad.add(sawtooth).add(triangle)setInterval(function(){
console.log(“Volume: “, Math.round(polywad.audioMeter.volume * 1000))
console.log(“Clipping: “, polywad.audioMeter.checkClipping())
}, 50)
polywad.play()
var triangle = new Wad({source:’triangle’, env:{hold:1, release:.2}})
var polywad = new Wad.Poly({
audioMeter: {
clipLevel: .98, // the level (0 to 1) that you would consider “clipping”.
averaging: .95, // how “smoothed” you would like the meter to be over time. Should be between 0 and less than 1.
clipLag: 750, // how long you would like the “clipping” indicator to show after clipping has occured, in milliseconds.
},
})
polywad.add(sawtooth).add(triangle)setInterval(function(){
console.log(“Volume: “, Math.round(polywad.audioMeter.volume * 1000))
console.log(“Clipping: “, polywad.audioMeter.checkClipping())
}, 50)
polywad.play()
Pitch Detection
PolyWads can detect the frequency of their input.
var voice = new Wad({source : ‘mic’ }); // At this point, your browser will ask for permission to access your microphone.
var tuner = new Wad.Poly();
tuner.setVolume(0); // If you’re not using headphones, you can eliminate microphone feedback by muting the output from the tuner.
tuner.add(voice);voice.play(); // You must give your browser permission to access your microphone before calling play().tuner.updatePitch() // The tuner is now calculating the pitch and note name of its input 60 times per second. These values are stored in <code>tuner.pitch</code> and <code>tuner.noteName</code>.var logPitch = function(){
console.log(tuner.pitch, tuner.noteName)
requestAnimationFrame(logPitch)
};
logPitch();
// If you sing into your microphone, your pitch will be logged to the console in real time.tuner.stopUpdatingPitch(); // Stop calculating the pitch if you don’t need to know it anymore.
var tuner = new Wad.Poly();
tuner.setVolume(0); // If you’re not using headphones, you can eliminate microphone feedback by muting the output from the tuner.
tuner.add(voice);voice.play(); // You must give your browser permission to access your microphone before calling play().tuner.updatePitch() // The tuner is now calculating the pitch and note name of its input 60 times per second. These values are stored in <code>tuner.pitch</code> and <code>tuner.noteName</code>.var logPitch = function(){
console.log(tuner.pitch, tuner.noteName)
requestAnimationFrame(logPitch)
};
logPitch();
// If you sing into your microphone, your pitch will be logged to the console in real time.tuner.stopUpdatingPitch(); // Stop calculating the pitch if you don’t need to know it anymore.
MIDI Input
WadJS can read MIDI data from MIDI instruments and controllers, and you can set handlers to respond to that data. When WadJS initializes, it tries to automatically detect any connected MIDI devices, and creates a reference to it in the array Wad.midiInputs
. To handle MIDI data, assign a MIDI handler function to a MIDI device’s onmidimessage
property. By default, Wad is configured to log MIDI messages to the console, which should be sufficient if you are quickly testing your devices. If you want to quickly set up a MIDI keyboard to play a Wad, assign a Wad of your choice (or any object with play()
and stop()
methods) to Wad.midiInstrument
.
Wad.midiInstrument = new Wad({source : 'sine'});
If you want to get creative with how WadJS handles MIDI data, I strongly encourage you to write your own MIDI handler functions. For example, note-on velocity (how hard you press a key when playing a note) usually modulates the volume of a note, but it might sound interesting if you configure note-on velocity to modulate the attack or filter frequency instead.
var midiMap = function(event){
console.log(event.receivedTime, event.data);
}Wad.assignMidiMap(midiMap)
console.log(event.receivedTime, event.data);
}Wad.assignMidiMap(midiMap)
If you have multiple MIDI devices that you would like to use simultaneously, you will need multiple MIDI handler functions. The second argument to Wad.assignMidiMap
is used to specify the index of the MIDI device you would like to assign to.
Wad.assignMidiMap(anotherMidiHandlerFunction, 1)
Wad.midiInputs[1].onmidimessage = anotherMidiHandlerFunction
Wad.midiInputs[1].onmidimessage = anotherMidiHandlerFunction
Wad.assignMidiMap
can also accept success and failure callbacks as its third and fourth arguments, to handle cases where the MIDI device you are trying to assign to cannot be found.
Access to the Audio Context
WAD at the time of creation an automatic Audio Context creates. It should not use obviously as it is exposed at Wad.audioContext
. When A-Frame is using in application and WadJS detects an <a-scene>
element on the page, WadJS will use A-Frame’s Audio Context and Audio Listener, instead of creating its own.
Cross-Browser Compatibility
As a JavaScript library Wad renowned in today’s market because of it awesome compatibility with Chrome, decently in Safari for iOS. It works nicely in any browser that supports Web Audio, especially mobile browsers.