Skip to content

Commit

Permalink
Merge pull request #3 from cstoquer/master
Browse files Browse the repository at this point in the history
AudioManager 0.1.1
  • Loading branch information
Cedric Stoquer committed Jun 26, 2015
2 parents e5f6846 + bb55ed8 commit b25bd67
Show file tree
Hide file tree
Showing 8 changed files with 436 additions and 177 deletions.
92 changes: 92 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//Examples: https://github.com/jshint/jshint/blob/master/examples/.jshintrc
//Documentation: http://www.jshint.com/docs/
//In Sublime: https://github.com/victorporof/Sublime-JSHint#using-your-own-jshintrc-options

{
// Custom globals.
"globals" : {
"console" : false,

// Added the below for browser for component(1) compatibility
"require" : false,
"module" : false,
"exports" : false
},

// Settings
"passfail" : false, // Stop on first error.
"maxerr" : 100, // Maximum error before stopping.

// Predefined globals whom JSHint will ignore.
"browser" : true, // Standard browser globals e.g. `window`, `document`.

"node" : false,
"rhino" : false,
"couch" : false,
"wsh" : false, // Windows Scripting Host.

"jquery" : false,
"prototypejs" : false,
"mootools" : false,
"dojo" : false,

// Development.
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
"devel" : false, // Allow developments statements e.g. `console.log();`.


// ECMAScript 5.
//NB: For client code we enable strict mode once at the top of the built concatenated script.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').


// The Good Parts.
"asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
"laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : false, // Require {} for every new block or scope.
"eqeqeq" : true, // Require triple equals i.e. `===`.
"eqnull" : false, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : true, // Prohipit variable use before definition.
"loopfunc" : false, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"regexp" : false, // Prohibit `.` and `[^...]` in regular expressions.
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
"scripturl" : false, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.
"camelcase" : true, // require camelCase or ALL_CAPS
"unused" : true, // prohibit unused variables
"-W100" : true, // if unsafe characters should not be checked

//
"maxparams" : 7,
"maxdepth" : 5,
"maxstatements" : 80,
"maxcomplexity" : 30,

// Personal styling preferences.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : true, // Prohibit use of empty blocks.
"nonew" : true, // Prohibit use of constructors for side-effects.
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"white" : true, // Check against strict whitespace and indentation rules.
"indent" : 4, // Specify indentation spacing (also shouts on case X: return n, and {x:1, y:2})
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"smarttabs" : true // true: Tolerate mixed tabs/spaces when used for alignment
}
36 changes: 22 additions & 14 deletions ISound.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// jscs:disable requireCurlyBraces


//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Sound Abstract class.
* Implement dynamic loading / unloading mechanism.
Expand All @@ -10,16 +7,18 @@
*/
function ISound() {
// public properties
this.playing = false;
this.fade = 0;
this.usedMemory = 0;
this.poolRef = null;

// the following properties are public but should NOT be assigned directly.
// instead, use the setter functions: setId, setVolume, setPan, setLoop.
// instead, use the setter functions: setId, setVolume, setPan, setLoop, setPitch.
this.id = 0;
this.volume = 1.0;
this.pan = 0.0;
this.loop = false;
this.pitch = 0.0;

// private properties
this._src = '';
Expand Down Expand Up @@ -56,6 +55,11 @@ ISound.prototype.setLoop = function (value) {
this.loop = value;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
ISound.prototype.setPitch = function (pitch) {
this.pitch = pitch;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Load sound. Abstract method to be overwritten
* @private
Expand All @@ -73,11 +77,11 @@ ISound.prototype._load = function (filePath) {
* @param {Function} [cd] - optional callback function
*/
ISound.prototype.load = function (cb) {
if (!this.id) { return console.error('Can not load a sound without id.'); }
if (this._loaded) { return cb && cb(null, this); }
if (!this.id) return cb && cb('noId');
if (this._loaded) return cb && cb(null, this);

if (cb) { this._queuedCallback.push(cb); }
if (this._loading) { return; }
if (this._loading) return;
this._loading = true;

return this._load(this._src, this);
Expand Down Expand Up @@ -116,7 +120,8 @@ ISound.prototype._finalizeLoad = function (error) {
ISound.prototype.unload = function () {
this._playTriggered = 0;
this.setLoop(false);
this.fade = 0;
this.fade = 0;
this.pitch = 0;
this.stop();

if (this._loading) {
Expand All @@ -139,25 +144,27 @@ ISound.prototype.unload = function () {
/** Play sound. If sound is not yet loaded, it is loaded in memory and flagged to be played
* once loading has finished. If loading take too much time, playback may be cancelled.
*
* @param {number} vol - volume
* @param {number} pan - panoramic
* @param {number} [vol] - optional volume
* @param {number} [pan] - optional panoramic
* @param {number} [pitch] - optional pitch value in semi-tone (available only if using webAudio)
*/
ISound.prototype.play = function (vol, pan) {
if (vol !== undefined) { this.setVolume(vol); }
if (pan !== undefined) { this.setPan(pan); }
ISound.prototype.play = function (vol, pan, pitch) {
if (vol !== undefined && vol !== null) { this.setVolume(vol); }
if (pan !== undefined && pan !== null) { this.setPan(pan); }

if (!this._loaded) {
this._playTriggered = Date.now();
this.load();
return;
}

this._play();
this._play(pitch);
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Play sound. Abstract method to be overwritten */
ISound.prototype._play = function () {
this.playing = true;
console.log('ISound play call: "' + this._src + '"');
};

Expand All @@ -167,5 +174,6 @@ ISound.prototype._play = function () {
* @param {Function} [cb] - optional callback function (use it when sound has a fade out)
*/
ISound.prototype.stop = function (cb) {
this.playing = false;
return cb && cb();
};
111 changes: 91 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,126 @@
# AudioManager
play sounds using Web Audio, fallback on HTML5 Audio
Play sounds using Web Audio, fallback on HTML5 Audio.
`AudioManager` is specifically designed to works for games that have a big
quantity of audio assets. Loading and unloading is made easy and transparent.
If available, WizAsset is used for downloading files to disc.

## API
# API

Create audioManager object and define sound channels.
```javascript
// initialisation
var audioManager = new AudioManager(channelIds);
audioManager.init();
audioManager.setVolume(channelId, volume);

// play simple sound
audioManager.playSound(channelId, soundId, volume, panoramic, pitch);

// sound group
audioManager.createSoundGroups(soundGroupDefs, channelId);
audioManager.playSoundGroup(channelId, groupId, volume, panoramic, pitch);

// loop
audioManager.playLoopSound(channelId, soundId, volume);
audioManager.stopLoopSound(channelId);
audioManager.stopAllLoopSounds();

// release memory
audioManager.release();
```

# Documentation

### Create audioManager object and channels
Pass the list of channels to the constructor as an array of strings.
```javascript
var channels = ['music', 'sfx', 'ui'];
var audioManager = new AudioManager(channels);
```

Setup audioManager path to sound assets folder
### Setup audioManager path to sound assets folder
```javascript
audioManager.settings.audioPath = 'assets/audio/';
```

Start audio engine.
To work correctly on iOS, this must be called on an user interaction (e.g. user pressing a button)
### Start audio engine.
To work correctly on iOS, this must be called on an user interaction
(e.g. user pressing a button)
```javascript
gameStartButton.on('tap', function () {
audioManager.init();
});
```

Set channel volume
### Set channel volume
By default, channel volume is set to 0 and channel is muted.
No sounds will play until channel volume is set.
```javascript
var volume = 1.0; // volume is a float in range ]0..1]
audioManager.setVolume('ui', volume);
```

### Create and play a simple sound.
Create a sound and playing it in a channel.
Sound is created and loaded automatically.
```javascript
var fileName = 'laser1';
audioManager.playSound('sfx', fileName);

// volume and panoramic can be set optionally
var volume = 0.7; // volume is a float in range ]0..1]
var panoramic = 0.9; // panoramic is a float in range [-1..1], 0 is the center
audioManager.playSound('sfx', fileName, volume, panoramic);
```

Sounds creation and preloading can be forced.
```javascript
audioManager.createSound(fileName).load();
```

Alternatively, sounds can be played outside channels.
```javascript
var sound = audioManager.createSound(fileName);
sound.play(volume, panoramic, pitch); // all parameters are optional.
```
### Change pitch
This feature is only available with WebAudio.

```javascript
var pitch = -7.0; // in semi-tone
sound.setPitch(pitch);
```

The pitch can be set at play.
```javascript
var volume = 1.0; // volume is a float in range ]0..1]
audioManager.setVolume('sfx', volume);
audioManager.playSound('ui', fileName, volume, panoramic, pitch);
```

Load and play a simple sound.
While a sound is playing, the pitch can be changed dynamically
```javascript
var fileName = 'sound1'; // fileName is sound path without '.mp3' extension
audioManager.createSound(fileName);
audioManager.playSound('ui', fileName);
var portamento = 3.0;
sound.setPitch(pitch, portamento);
```

Create and play sound groups.
### Create and play sound groups.
A sound group is a collection of sounds that will play alternatively in a
round-robin pattern on each `play` call.
```javascript
var soundGroupDefs = {
groupId1: { id: ['sound1', 'sound2'], vol: [1.0, 0.8] },
groupId1: { id: ['sound1', 'sound2'], vol: [1.0, 0.8], pitch: [0.0] },
groupId2: { ... },
...
};
audioManager.createSoundGroups(soundGroupDefs, 'sfx');

var panoramic = 0.3; // panoramic is a float in range [-1..1].
// set to 0, the sound will play at center.
audioManager.playSoundGroup('sfx', 'groupId1', panoramic);
var volume = 0.8; // volume is a float in range ]0..1]
var panoramic = 0.3; // panoramic is a float in range [-1..1], 0 is the center
var pitch = 3.0; // in semi-tone
audioManager.playSoundGroup('sfx', 'groupId1', volume, panoramic, pitch);
```

Play and stop looped sounds (e.g. background music). Only one loop can play per channel.
### Play and stop looped sounds
Only one loop can play per channel. Playing a new looped sound in the same
channel will stop current playing sound before starting new one.
```javascript
var volume = 1.0; // volume is a float in range ]0..1]
var fileName = 'bgm1';
Expand All @@ -58,7 +129,7 @@ audioManager.stopLoopSound('music'); // stop looped sound in channel 'music'
audioManager.stopAllLoopSounds(); // stop all looped sounds in all channel
```

Release memory
### Release memory
```javascript
audioManager.release();
```
3 changes: 2 additions & 1 deletion Sound.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// jscs:disable requireCurlyBraces
var inherits = require('util').inherits;
var ISound = require('./ISound.js');
var PLAY_OPTIONS = { playAudioWhenScreenIsLocked: false };
Expand Down Expand Up @@ -101,6 +100,7 @@ Sound.prototype._play = function () {
this._audio.pause();
this._audio.currentTime = 0;
this._audio.play(PLAY_OPTIONS);
this.playing = true;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Expand All @@ -112,5 +112,6 @@ Sound.prototype.stop = function (cb) {
this._audio.pause();
this._audio.currentTime = 0;
this._playTriggered = 0;
this.playing = false;
return cb && cb(); // TODO: fade-out
};
Loading

0 comments on commit b25bd67

Please sign in to comment.