From 5f69b91b1ebc466a667fd15180f293755f08887a Mon Sep 17 00:00:00 2001 From: codinronan Date: Thu, 9 Mar 2017 14:59:55 -0600 Subject: [PATCH] Exposes the volume controls for WebAudioTrack and CordovaAudioTrack --- dist/ionic-audio-cordova-track.ts | 116 ++++++++++++++------------ dist/ionic-audio-interfaces.ts | 19 ++--- dist/ionic-audio-web-track.ts | 130 ++++++++++++++++-------------- 3 files changed, 145 insertions(+), 120 deletions(-) diff --git a/dist/ionic-audio-cordova-track.ts b/dist/ionic-audio-cordova-track.ts index 6c22d57..8c5fffe 100644 --- a/dist/ionic-audio-cordova-track.ts +++ b/dist/ionic-audio-cordova-track.ts @@ -1,11 +1,11 @@ -import {IAudioTrack} from './ionic-audio-interfaces'; +import {IAudioTrack} from './ionic-audio-interfaces'; import {Injectable} from '@angular/core'; declare let Media: any; /** * Cordova Media audio track - * + * * @export * @class CordovaAudioTrack * @constructor @@ -23,24 +23,24 @@ export class CordovaAudioTrack implements IAudioTrack { private _isLoading: boolean; private _hasLoaded: boolean; private _timer: any; - + constructor(public src: string) { if (window['cordova'] === undefined || window['Media'] === undefined) { console.log('Cordova Media is not available'); return; }; - - this.createAudio(); + + this.createAudio(); } - + private createAudio() { this.audio = new Media(this.src, () => { console.log('Finished playback'); this.stopTimer(); - this.isFinished = true; + this.isFinished = true; this.destroy(); // TODO add parameter to control whether to release audio on stop or finished }, (err) => { - console.log(`Audio error => track ${this.src}`, err); + console.log(`Audio error => track ${this.src}`, err); }, (status) => { switch (status) { case Media.MEDIA_STARTING: @@ -50,8 +50,8 @@ export class CordovaAudioTrack implements IAudioTrack { case Media.MEDIA_RUNNING: console.log(`Playing track ${this.src}`); this.isPlaying = true; - this._isLoading = false; - break; + this._isLoading = false; + break; case Media.MEDIA_PAUSED: this.isPlaying = false; break @@ -59,55 +59,55 @@ export class CordovaAudioTrack implements IAudioTrack { this.isPlaying = false; break; } - }); + }); } - + private startTimer() { - this._timer = setInterval(() => { + this._timer = setInterval(() => { if (this._duration===undefined || this._duration < 0) { this._duration = Math.round(this.audio.getDuration()*100)/100; - } - + } + this.audio.getCurrentPosition((position) => { if (position > -1) { this._progress = Math.round(position*100)/100; - this._completed = this._duration > 0 ? Math.round(this._progress / this._duration * 100)/100 : 0; + this._completed = this._duration > 0 ? Math.round(this._progress / this._duration * 100)/100 : 0; } }, (e) => { console.log("Error getting position", e); } ); - }, 1000); + }, 1000); } - + private stopTimer() { clearInterval(this._timer); } - + /** public members */ /** * Gets the track id - * + * * @property id * @type {number} */ public get id() : number { return this._id; } - + /** * Sets the track id - * + * * @property id */ public set id(v : number) { this._id = v; } - + /** * Gets the track duration, or -1 if it cannot be determined - * + * * @property duration * @readonly * @type {number} @@ -115,21 +115,21 @@ export class CordovaAudioTrack implements IAudioTrack { public get duration() : number { return this._duration; } - + /** * Gets current track time (progress) - * + * * @property progress * @readonly * @type {number} */ public get progress() : number { return this._progress; - } - + } + /** * Gets current track progress as a percentage - * + * * @property completed * @readonly * @type {number} @@ -141,17 +141,17 @@ export class CordovaAudioTrack implements IAudioTrack { /** * Gets any errors logged by HTML5 audio * - * @property error + * @property error * @readonly * @type {MediaError} */ public get error() : MediaError { return this.audio.error; } - + /** * Gets a boolean value indicating whether the current source can be played - * + * * @property canPlay * @readonly * @type {boolean} @@ -159,10 +159,10 @@ export class CordovaAudioTrack implements IAudioTrack { public get canPlay() : boolean { return true; } - + /** * Gets a boolean value indicating whether the track is in loading state - * + * * @property isLoading * @readonly * @type {boolean} @@ -170,76 +170,88 @@ export class CordovaAudioTrack implements IAudioTrack { public get isLoading() : boolean { return this._isLoading; } - + /** * Gets a boolean value indicating whether the track has finished loading * - * @property hadLoaded + * @property hadLoaded * @readonly * @type {boolean} */ public get hasLoaded() : boolean { return this._hasLoaded; } - + /** * Plays current track - * + * * @method play */ play() { if (!this.audio) { - this.createAudio(); + this.createAudio(); } - + if (!this._hasLoaded) { console.log(`Loading track ${this.src}`); this._isLoading = true; } - + this.audio.play(); this.startTimer(); } - + /** * Pauses current track * - * @method pause + * @method pause */ pause() { if (!this.isPlaying) return; console.log(`Pausing track ${this.src}`); this.audio.pause(); - this.stopTimer(); + this.stopTimer(); } - + /** * Stops current track and releases audio * - * @method stop + * @method stop */ stop() { this.audio.stop(); // calls Media onSuccess callback } - + /** * Seeks to a new position within the track * - * @method seekTo + * @method seekTo * @param {number} time the new position (milliseconds) to seek to */ seekTo(time: number) { // Cordova Media reports duration and progress as seconds, so we need to multiply by 1000 this.audio.seekTo(time*1000); } - + + /** + * Sets the volume of the current track. Valid values are (0, 1) inclusive. + * + * @method setVolume + * @param v {number} the new volume to set + */ + setVolume(v: number) { + // Valid values are (0,1) inclusive. + const volume = Math.min(Math.max(0, v), 1); + this.audio.setVolume(volume); + } + /** * Releases audio resources - * + * * @method destroy */ destroy() { - this.audio.release(); + this.audio.release(); console.log(`Released track ${this.src}`); } -} \ No newline at end of file +} diff --git a/dist/ionic-audio-interfaces.ts b/dist/ionic-audio-interfaces.ts index 2077251..8272317 100644 --- a/dist/ionic-audio-interfaces.ts +++ b/dist/ionic-audio-interfaces.ts @@ -1,24 +1,24 @@ /** * Defines the audio provider contract - * + * * @export * @interface IAudioProvider */ export interface IAudioProvider { current: number; tracks: IAudioTrack[]; - + create(track: ITrackConstraint): IAudioTrack; add(track: IAudioTrack); play(index: number); pause(index?: number); stop(index?: number); -} +} /** * Defines the properties for JSON objects representing tracks to be played - * + * * @export * @interface ITrackConstraint */ @@ -27,13 +27,13 @@ export interface ITrackConstraint { src: string; title?: string; artist?: string; - art?: string; + art?: string; preload?: string; } /** - * Defines the audio track contract - * + * Defines the audio track contract + * * @export * @interface IAudioTrack * @extends {ITrackConstraint} @@ -41,7 +41,7 @@ export interface ITrackConstraint { export interface IAudioTrack extends ITrackConstraint { src: string; id: number; - isPlaying: boolean; + isPlaying: boolean; isLoading: boolean; isFinished: boolean; duration: number; @@ -49,11 +49,12 @@ export interface IAudioTrack extends ITrackConstraint { completed: number; canPlay: boolean; error: MediaError; - + play(); pause(); stop(); seekTo(time: number); + setVolume(volume: number); destroy(); } diff --git a/dist/ionic-audio-web-track.ts b/dist/ionic-audio-web-track.ts index 4e90feb..bc628ed 100644 --- a/dist/ionic-audio-web-track.ts +++ b/dist/ionic-audio-web-track.ts @@ -1,4 +1,4 @@ -import {IAudioTrack} from './ionic-audio-interfaces'; +import {IAudioTrack} from './ionic-audio-interfaces'; import {Injectable, Optional} from '@angular/core'; declare let window; @@ -6,7 +6,7 @@ window.AudioContext = window['AudioContext'] || window['webkitAudioContext']; /** * Creates an HTML5 audio track - * + * * @export * @class WebAudioTrack * @constructor @@ -26,84 +26,84 @@ export class WebAudioTrack implements IAudioTrack { constructor(public src: string, @Optional() public preload: string = 'none', @Optional() private ctx: AudioContext = undefined) { // audio context not needed for now // this.ctx = this.ctx || new AudioContext(); - - this.createAudio(); + + this.createAudio(); } - + private createAudio() { this.audio = new Audio(); this.audio.src = this.src; this.audio.preload = this.preload; //this.audio.controls = true; //this.audio.autoplay = false; - + this.audio.addEventListener("timeupdate", (e) => { this.onTimeUpdate(e); }, false); - + this.audio.addEventListener("error", (err) => { console.log(`Audio error => track ${this.src}`, err); this.isPlaying = false; }, false); - + this.audio.addEventListener("canplay", () => { console.log(`Loaded track ${this.src}`); this._isLoading = false; this._hasLoaded = true; }, false); - + this.audio.addEventListener("playing", () => { console.log(`Playing track ${this.src}`); this.isFinished = false; this.isPlaying = true; }, false); - + this.audio.addEventListener("ended", () => { this.isPlaying = false; this.isFinished = true; console.log('Finished playback'); }, false); - - this.audio.addEventListener("durationchange", (e:any) => { + + this.audio.addEventListener("durationchange", (e:any) => { this._duration = e.target.duration; - }, false); + }, false); } - + private onTimeUpdate(e: Event) { if (this.isPlaying && this.audio.currentTime > 0) { this._progress = this.audio.currentTime; this._completed = this.audio.duration > 0 ? Math.trunc (this.audio.currentTime / this.audio.duration * 100)/100 : 0; - } + } } - + static formatTime(value:number) { let s = Math.trunc(value % 60); let m = Math.trunc((value / 60) % 60); - let h = Math.trunc(((value / 60) / 60) % 60); + let h = Math.trunc(((value / 60) / 60) % 60); return h > 0 ? `${h<10?'0'+h:h}:${m<10?'0'+m:m}:${s<10?'0'+s:s}` : `${m<10?'0'+m:m}:${s<10?'0'+s:s}`; - } - - + } + + /** * Gets the track id - * + * * @property id * @type {number} */ public get id() : number { return this._id; } - + /** * Sets the track id - * + * * @property id */ public set id(v : number) { this._id = v; } - + /** * Gets the track duration, or -1 if it cannot be determined - * + * * @property duration * @readonly * @type {number} @@ -111,10 +111,10 @@ export class WebAudioTrack implements IAudioTrack { public get duration() : number { return this._duration; } - + /** * Gets current track time (progress) - * + * * @property progress * @readonly * @type {number} @@ -122,10 +122,10 @@ export class WebAudioTrack implements IAudioTrack { public get progress() : number { return this._progress; } - + /** * Gets current track progress as a percentage - * + * * @property completed * @readonly * @type {number} @@ -137,17 +137,17 @@ export class WebAudioTrack implements IAudioTrack { /** * Gets any errors logged by HTML5 audio * - * @property error + * @property error * @readonly * @type {MediaError} */ public get error() : MediaError { return this.audio.error; } - + /** * Gets a boolean value indicating whether the current source can be played - * + * * @property canPlay * @readonly * @type {boolean} @@ -156,11 +156,11 @@ export class WebAudioTrack implements IAudioTrack { let format = `audio/${this.audio.src.substr(this.audio.src.lastIndexOf('.')+1)}`; return this.audio && this.audio.canPlayType(format) != ''; } - - + + /** * Gets a boolean value indicating whether the track is in loading state - * + * * @property isLoading * @readonly * @type {boolean} @@ -168,56 +168,56 @@ export class WebAudioTrack implements IAudioTrack { public get isLoading() : boolean { return this._isLoading; } - - + + /** * Gets a boolean value indicating whether the track has finished loading * - * @property hadLoaded + * @property hadLoaded * @readonly * @type {boolean} */ public get hasLoaded() : boolean { return this._hasLoaded; } - - + + /** * Plays current track - * + * * @method play */ play() { if (!this.audio) { - this.createAudio(); + this.createAudio(); } - + if (!this._hasLoaded) { console.log(`Loading track ${this.src}`); this._isLoading = true; } - - //var source = this.ctx.createMediaElementSource(this.audio); + + //var source = this.ctx.createMediaElementSource(this.audio); //source.connect(this.ctx.destination); this.audio.play(); - } - + } + /** * Pauses current track * - * @method pause + * @method pause */ pause() { if (!this.isPlaying) return; console.log(`Pausing track ${this.src}`); this.audio.pause(); this.isPlaying = false; - } - + } + /** * Stops current track and releases audio * - * @method stop + * @method stop */ stop() { if (!this.audio) return; @@ -226,25 +226,37 @@ export class WebAudioTrack implements IAudioTrack { this.isFinished = true; this.destroy(); } - - + + /** * Seeks to a new position within the track * - * @method seekTo + * @method seekTo * @param {number} time the new position to seek to */ seekTo(time: number) { - this.audio.currentTime = time; + this.audio.currentTime = time; + } + + /** + * Sets the volume of the current track. Valid values are (0, 1) inclusive. + * + * @method setVolume + * @param v {number} the new volume to set + */ + setVolume(v: number) { + // Valid values are (0,1) inclusive. + const volume = Math.min(Math.max(0, v), 1); + this.audio.volume = volume; } - + /** * Releases audio resources - * + * * @method destroy */ destroy() { - this.audio = undefined; + this.audio = undefined; console.log(`Released track ${this.src}`); } -} \ No newline at end of file +}