Skip to content

Commit

Permalink
Fading Convenience Functions (#10)
Browse files Browse the repository at this point in the history
Fading Play Options
  • Loading branch information
bigtimebuddy authored Feb 27, 2017
1 parent 376ab2d commit a76470a
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 86 deletions.
7 changes: 5 additions & 2 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,11 @@ <h3 id="section-utils"><small><a href="#section-utils"><span class="glyphicon gl
</button>

<p>Create a simple sine-wave tone.</p>
<pre><code id="sine-tone" class="javascript">const sound = PIXI.sound.utils.sineTone(523.251);
sound.play();</code></pre>
<pre><code id="sine-tone" class="javascript">const sound = PIXI.sound.utils.sineTone(523.251, 3);
sound.play({
fadeIn: 1,
fadeOut: 1
});</code></pre>
<button class="btn btn-primary" data-code="#sine-tone" data-beforeCode="#render-clear">
<span class="glyphicon glyphicon-play"></span> Play
</button>
Expand Down
173 changes: 105 additions & 68 deletions src/Sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export interface PlayOptions {
end?: number;
speed?: number;
loop?: boolean;
fadeIn?: number;
fadeOut?: number;
sprite?: string;
complete?: CompleteCallback;
loaded?: LoadedCallback;
}
Expand All @@ -39,8 +42,9 @@ export interface PlayOptions {
* @callback PIXI.sound.Sound~loadedCallback
* @param {Error} err The callback error.
* @param {PIXI.sound.Sound} sound The instance of new sound.
* @param {PIXI.sound.SoundInstance} instance The instance of auto-played sound.
*/
export declare type LoadedCallback = (err: Error, sound?: Sound) => void;
export declare type LoadedCallback = (err: Error, sound?: Sound, instance?: SoundInstance) => void;

/**
* Callback when sound is completed.
Expand Down Expand Up @@ -90,22 +94,6 @@ export default class Sound
*/
public autoPlay: boolean;

/**
* Callback when finished playing.
* @name PIXI.sound.Sound#complete
* @type {PIXI.sound.Sound~completeCallback}
* @default false
*/
public complete: CompleteCallback;

/**
* Callback when load is finished.
* @type {PIXI.sound.Sound~loadedCallback}
* @name PIXI.sound.Sound#loaded
* @readOnly
*/
public loaded: LoadedCallback;

/**
* `true` to disallow playing multiple layered instances at once.
* @name PIXI.sound.Sound#singleInstance
Expand Down Expand Up @@ -148,6 +136,22 @@ export default class Sound
*/
public useXHR: boolean;

/**
* The options when auto-playing.
* @name PIXI.sound.Sound#_autoPlayOptions
* @type {PlayOptions}
* @private
*/
private _autoPlayOptions: PlayOptions;

/**
* The internal volume.
* @name PIXI.sound.Sound#_volume
* @type {Number}
* @private
*/
private _volume: number;

/**
* Reference to the sound context.
* @name PIXI.sound.Sound#_context
Expand Down Expand Up @@ -253,13 +257,14 @@ export default class Sound
this._instances = [];
this._sprites = {};

const complete = options.complete;
this._autoPlayOptions = complete ? { complete } : null;

this.isLoaded = false;
this.isPlaying = false;
this.autoPlay = options.autoPlay;
this.singleInstance = options.singleInstance;
this.preload = options.preload || this.autoPlay;
this.complete = options.complete;
this.loaded = options.loaded;
this.src = options.src;
this.srcBuffer = options.srcBuffer;
this.useXHR = options.useXHR;
Expand All @@ -274,7 +279,7 @@ export default class Sound

if (this.preload)
{
this._beginPreload();
this._beginPreload(options.loaded);
}
}

Expand All @@ -294,8 +299,6 @@ export default class Sound
this.removeSprites();
this._sprites = null;

this.complete = null;
this.loaded = null;
this.srcBuffer = null;

this._removeInstances();
Expand Down Expand Up @@ -331,11 +334,11 @@ export default class Sound
*/
public get volume(): number
{
return this._nodes.gain.gain.value;
return this._volume;
}
public set volume(volume: number)
{
this._nodes.gain.gain.value = volume;
this._volume = this._nodes.gain.gain.value = volume;
}

/**
Expand Down Expand Up @@ -528,43 +531,44 @@ export default class Sound
* @param {Number} data.end Time to end playing in seconds.
* @param {Number} [data.speed] Override default speed, default to the Sound's speed setting.
* @param {PIXI.sound.Sound~completeCallback} [callback] Callback when completed.
* @return {PIXI.sound.SoundInstance} Current playing instance.
* @return {PIXI.sound.SoundInstance|Promise<PIXI.sound.SoundInstance>} The sound instance,
* this cannot be reused after it is done playing. Returns a Promise if the sound
* has not yet loaded.
*/
public play(alias: string, callback?: CompleteCallback): SoundInstance;
public play(alias: string, callback?: CompleteCallback): SoundInstance|Promise<SoundInstance>;

/**
* Plays the sound.
* @method PIXI.sound.Sound#play
* @param {PIXI.sound.Sound~completeCallback|object} options Either completed function or play options.
* @param {Number} [options.start=0] Time when to play the sound in seconds.
* @param {Number} [options.end] Time to end playing in seconds.
* @param {String} [options.sprite] Play a named sprite. Will override end, start and speed options.
* @param {Number} [options.fadeIn] Amount of time to fade in volume. If less than 10,
* considered seconds or else milliseconds.
* @param {Number} [options.fadeOut] Amount of time to fade out volume. If less than 10,
* considered seconds or else milliseconds.
* @param {Number} [options.speed] Override default speed, default to the Sound's speed setting.
* @param {Boolean} [options.loop] Override default loop, default to the Sound's loop setting.
* @param {PIXI.sound.Sound~completeCallback} [options.complete] Callback when complete.
* @param {PIXI.sound.Sound~loadedCallback} [options.loaded] If the sound isn't already preloaded, callback when
* the audio has completely finished loading and decoded.
* @return {PIXI.sound.SoundInstance} Current playing instance.
* @return {PIXI.sound.SoundInstance|Promise<PIXI.sound.SoundInstance>} The sound instance,
* this cannot be reused after it is done playing. Returns a Promise if the sound
* has not yet loaded.
*/
public play(source?: PlayOptions|CompleteCallback, callback?: CompleteCallback): SoundInstance;
public play(source?: PlayOptions|CompleteCallback,
callback?: CompleteCallback): SoundInstance|Promise<SoundInstance>;

// Overloaded function
public play(source?: any, callback?: CompleteCallback): SoundInstance
public play(source?: any, complete?: CompleteCallback): SoundInstance|Promise<SoundInstance>
{
let options: PlayOptions;

if (typeof source === "string")
{
const alias: string = source as string;
// @if DEBUG
console.assert(!!this._sprites[alias], `Alias ${alias} is not available`);
// @endif
const sprite: SoundSprite = this._sprites[alias];
options = {
start: sprite.start,
end: sprite.end,
speed: sprite.speed,
complete: callback,
};
const sprite: string = source as string;
options = { sprite, complete };
}
else if (typeof source === "function")
{
Expand All @@ -579,29 +583,55 @@ export default class Sound
options = Object.assign({
complete: null,
loaded: null,
sprite: null,
start: 0,
fadeIn: 0,
fadeOut: 0,
}, options || {});

// A sprite is specified, add the options
if (options.sprite)
{
const alias: string = options.sprite;
// @if DEBUG
console.assert(!!this._sprites[alias], `Alias ${alias} is not available`);
// @endif
const sprite: SoundSprite = this._sprites[alias];
options.start = sprite.start;
options.end = sprite.end;
options.speed = sprite.speed;
delete options.sprite;
}

// @deprecated offset option
if ((options as any).offset) {
options.start = (options as any).offset as number;
}

// if not yet playable, ignore
// - usefull when the sound download isnt yet completed
if (!this.isPlayable)
if (!this.isLoaded)
{
this.autoPlay = true;
if (!this.isLoaded)
return new Promise<SoundInstance>((resolve, reject) =>
{
const loaded = options.loaded;
if (loaded)
this.autoPlay = true;
this._autoPlayOptions = options;
this._beginPreload((err: Error, sound: Sound, instance: SoundInstance) =>
{
this.loaded = loaded;
}
this._beginPreload();
}
return;
if (err)
{
reject(err);
}
else
{
if (options.loaded)
{
options.loaded(err, sound, instance);
}
resolve(instance);
}
});
});
}

// Stop all sounds
Expand All @@ -624,11 +654,14 @@ export default class Sound
instance.once("stop", () => {
this._onComplete(instance);
});

instance.play(
options.start,
options.end,
options.speed,
options.loop,
options.fadeIn,
options.fadeOut,
);
return instance;
}
Expand All @@ -643,6 +676,7 @@ export default class Sound
if (!this.isPlayable)
{
this.autoPlay = false;
this._autoPlayOptions = null;
return this;
}
this.isPlaying = false;
Expand Down Expand Up @@ -690,21 +724,21 @@ export default class Sound
* @method PIXI.sound.Sound#_beginPreload
* @private
*/
private _beginPreload(): void
private _beginPreload(callback?: LoadedCallback): void
{
// Load from the file path
if (this.src)
{
this.useXHR ? this._loadUrl() : this._loadPath();
this.useXHR ? this._loadUrl(callback) : this._loadPath(callback);
}
// Load from the arraybuffer, incase it was loaded outside
else if (this.srcBuffer)
{
this._decode(this.srcBuffer);
this._decode(this.srcBuffer, callback);
}
else if (this.loaded)
else if (callback)
{
this.loaded(new Error("sound.src or sound.srcBuffer must be set"));
callback(new Error("sound.src or sound.srcBuffer must be set"));
}
else
{
Expand Down Expand Up @@ -752,7 +786,7 @@ export default class Sound
* @method PIXI.sound.Sound#_loadUrl
* @private
*/
private _loadUrl(): void
private _loadUrl(callback?: LoadedCallback): void
{
const request = new XMLHttpRequest();
const src: string = this.src;
Expand All @@ -761,9 +795,8 @@ export default class Sound

// Decode asynchronously
request.onload = () => {
this.isLoaded = true;
this.srcBuffer = request.response as ArrayBuffer;
this._decode(request.response);
this._decode(request.response, callback);
};

// actually start the request
Expand All @@ -775,7 +808,7 @@ export default class Sound
* @method PIXI.sound.Sound#_loadPath
* @private
*/
private _loadPath()
private _loadPath(callback?: LoadedCallback)
{
const fs = require("fs");
const src: string = this.src;
Expand All @@ -785,9 +818,9 @@ export default class Sound
// @if DEBUG
console.error(err);
// @endif
if (this.loaded)
if (callback)
{
this.loaded(new Error(`File not found ${this.src}`));
callback(new Error(`File not found ${this.src}`));
}
return;
}
Expand All @@ -798,7 +831,7 @@ export default class Sound
view[i] = data[i];
}
this.srcBuffer = arrayBuffer;
this._decode(arrayBuffer);
this._decode(arrayBuffer, callback);
});
}

Expand All @@ -808,25 +841,29 @@ export default class Sound
* @param {ArrayBuffer} arrayBuffer From load.
* @private
*/
private _decode(arrayBuffer: ArrayBuffer): void
private _decode(arrayBuffer: ArrayBuffer, callback?: LoadedCallback): void
{
this._context.decode(arrayBuffer, (err: Error, buffer: AudioBuffer) =>
{
if (err)
{
this.loaded(err);
if (callback)
{
callback(err);
}
}
else
{
this.isLoaded = true;
this.buffer = buffer;
if (this.loaded)
let instance: SoundInstance;
if (this.autoPlay)
{
this.loaded(null, this);
instance = this.play(this._autoPlayOptions) as SoundInstance;
}
if (this.autoPlay)
if (callback)
{
this.play(this.complete);
callback(null, this, instance);
}
}
},
Expand Down
Loading

0 comments on commit a76470a

Please sign in to comment.