Skip to content

Instantly share code, notes, and snippets.

@MT--
Created February 23, 2016 16:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MT--/ece6e388a693416aa7e7 to your computer and use it in GitHub Desktop.
Save MT--/ece6e388a693416aa7e7 to your computer and use it in GitHub Desktop.
Angular(1) factory to create metronome based on Web Audio API with help from https://github.com/cwilso/metronome/
'use strict';
/**
* make a new Metronome based on the provided temp
* ideas from:
* https://github.com/cwilso/metronome/
*/
angular.module('main')
.factory('Metronome', function ( $window ) {
// build the metronome for injection
var Metronome = function ( bpm ) {
// boot the audio platform
var AudioContext = $window.AudioContext || $window.webkitAudioContext;
var audioContext = new AudioContext();
audioContext.suspend();
// decrease the volume and connect to output
var gainNode = audioContext.createGain();
gainNode.connect(audioContext.destination);
gainNode.gain.value = 0.1;
// specify the state
var nextNoteTime = 0,
currentSixteenth = 0,
lookahead = 250,
noteRes = 2, // 0 == 16th, 1 == 8th, 2 == 4th
noteLen = 0.05,
scheduleAheadTime = 0.25,
timerID = 0;
var isPlaying = false;
// calculate actual length of 1/4 note
// bpm is pulled from the song info, it won't change
var secPerBeat = 60.0 / bpm;
/**
* advance note and playhead by a 16th note
*/
var nextNote = function () {
// add length of 16th note
nextNoteTime += 0.25 * secPerBeat;
// advance the 16th note marker
currentSixteenth++;
// wrap back to zero
if ( currentSixteenth === 16 ) {
currentSixteenth = 0;
} // end if
};
/**
* do the next note/pitch
* @param beatNumber
* @param time
*/
var scheduleNote = function ( beatNumber, time ) {
// we are skipping non-eighth sixteen notes
if ( (noteRes === 1) && (beatNumber % 2) ) {
return;
} // end if
// we are skipping non-quarter eighth notes
if ( (noteRes === 2) && (beatNumber % 4) ) {
return;
} // end if
// create osc
var osc = audioContext.createOscillator();
osc.connect(gainNode);
// set some pitches to differentiate notes
if ( !(beatNumber % 16) ) {
osc.frequency.value = 1320.0; // Downbeat
} else if ( beatNumber % 4 ) {
osc.frequency.value = 440.0; // Sixteenth Notes
} else {
osc.frequency.value = 880.0; // Quarter Notes
} // end if
osc.start(time);
osc.stop(time + noteLen);
};
/**
* notes that need to play before the next interval
* schedule these and advance playhead
*/
var scheduler = function () {
while ( nextNoteTime < (audioContext.currentTime + scheduleAheadTime) ) {
scheduleNote(currentSixteenth, nextNoteTime);
nextNote();
} // end while
timerID = setTimeout(scheduler, lookahead);
};
/**
* start the metronome
*/
this.play = function () {
isPlaying = !isPlaying;
if ( isPlaying ) {
audioContext.resume();
currentSixteenth = 0;
nextNoteTime = audioContext.currentTime;
scheduler();
} else {
clearTimeout(timerID);
} // end if
};
/**
* tell the controller if the metronome is playing
* @returns {boolean}
*/
this.getIsPlaying = function () {
return isPlaying;
};
/**
* tell the controller the current playhead time
* @returns {*|number|Number}
*/
this.getCurrentTime = function () {
return audioContext.currentTime;
};
};
return Metronome;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment