File: ionic-audio-web-track.ts
import {IAudioProvider, ITrackConstraint, IAudioTrack} from './ionic-audio-interfaces';
import {Injectable, Inject, Optional} from 'angular2/core';
declare let webkitAudioContext;
/**
* Creates an HTML5 audio track
*
* @export
* @class WebAudioTrack
* @constructor
* @implements {IAudioTrack}
*/
@Injectable()
export class WebAudioTrack implements IAudioTrack {
private audio: HTMLAudioElement;
public isPlaying: boolean = false;
public isFinished: boolean = false;
private _progress: number;
private _completed: number;
private _duration: number;
private _id: number;
private _isLoading: boolean;
private _hasLoaded: boolean;
constructor(public src: string, @Optional() public preload: string = 'none', @Optional() private ctx: AudioContext = new (AudioContext || webkitAudioContext)()) {
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._duration = e.target.duration;
}, 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);
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}
*/
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}
*/
public get completed() : number {
return this._completed;
}
/**
* Gets any errors logged by HTML5 audio
*
* @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}
*/
public get canPlay() : boolean {
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}
*/
public get isLoading() : boolean {
return this._isLoading;
}
/**
* Gets a boolean value indicating whether the track has finished loading
*
* @property hadLoaded
* @readonly
* @type {boolean}
*/
public get hasLoaded() : boolean {
return this._hasLoaded;
}
/**
* Plays current track
*
* @method play
*/
play() {
if (!this.audio) {
this.createAudio();
}
if (!this._hasLoaded) {
console.log(`Loading track ${this.src}`);
this._isLoading = true;
}
//var source = this.ctx.createMediaElementSource(this.audio);
//source.connect(this.ctx.destination);
this.audio.play();
}
/**
* Pauses current track
*
* @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
*/
stop() {
if (!this.audio) return;
this.pause();
this.audio.removeEventListener("timeupdate", (e) => { this.onTimeUpdate(e); });
this.isFinished = true;
this.destroy();
}
/**
* Seeks to a new position within the track
*
* @method seekTo
* @param {number} time the new position to seek to
*/
seekTo(time: number) {
this.audio.currentTime = time;
}
/**
* Releases audio resources
*
* @method destroy
*/
destroy() {
this.audio = undefined;
console.log(`Released track ${this.src}`);
}
}