Skip to content

Commit

Permalink
Feature/add on update fn (#13)
Browse files Browse the repository at this point in the history
* Add onUpdate raf

* Update log
  • Loading branch information
Willy Brauner authored Sep 14, 2022
1 parent 2ac5f5c commit b89d0e3
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 21 deletions.
3 changes: 3 additions & 0 deletions examples/basic/src/AudioService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class AudioService {
url: applause,
options: {
volume: this.globalConfig.audioVolume,
onUpdate: (time) => {
// console.log("on update", time)
},
},
},
{
Expand Down
3 changes: 2 additions & 1 deletion examples/basic/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"skipLibCheck": true
"skipLibCheck": true,
"jsx": "react"
}
}
76 changes: 56 additions & 20 deletions src/AudioManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface IAudioManagerOptions {
preload?: boolean
html5?: boolean
delay?: number
onUpdate?: (time: number) => void
}

/**
Expand All @@ -43,9 +44,11 @@ export class AudioManager {
public isPlaying: boolean
public isMuted: boolean
public id: string = null
protected nameSpace: string

public canplayPromise: TDeferredPromise<void>
public endedPromise: TDeferredPromise<void>
protected raf: number

constructor(audioFileUrl: string, options: IAudioManagerOptions = {}) {
this.url = audioFileUrl
Expand All @@ -57,31 +60,36 @@ export class AudioManager {
preload: true,
html5: false,
delay: 0,
onUpdate: null,
}

this.options = {
...defaultOptions,
...options,
}

this.nameSpace = this.getNameSpace(this.id, this.url)
this.isPlaying = false
this.isLoading = true
this.isLoaded = false
this.isMuted = false
this.canplayPromise = deferredPromise()
this.endedPromise = deferredPromise()

this.load()
this.initEvents()
}

protected getNameSpace(id, url): string {
return `${id} - ${url.replace(/^.*[\\\/]/, "")} -`
}

protected load() {
// load howler sound
this.sound = new Howl({
src: [this.url],
...this.options,
onload: () => {
log(this.id, "canplay handler, audio is ready")
log(this.nameSpace, "canplay handler, audio is ready")
this.isLoaded = true
this.canplayPromise.resolve()
this.isLoading = false
Expand All @@ -96,50 +104,60 @@ export class AudioManager {
this.sound.on("end", this.handleEnded)
}

public destroy() {
log(this.id, "destroy")
this.sound.unload()
MUTE_AUDIO_SIGNAL.remove(this.handleMuteAll)
}

// ---------------------–---------------------–---------------------–------------------- EVENTS

protected handleMuteAll = (mute: boolean): void => {
mute ? this.mute() : this.unmute()
}

protected handleEnded = (): void => {
log(this.id, "ended")
log(this.nameSpace, "ended")
this.isPlaying = false
this.endedPromise.resolve()

if (this.raf) {
this.cancelRaf()
}
}
// ---------------------–---------------------–---------------------–------------------- API

public async play(): Promise<void> {
log(this.id, "waiting for canplayPromise...")
log(this.nameSpace, "waiting for canplayPromise...")
await this.canplayPromise.promise
this.endedPromise = deferredPromise()
log(this.id, "play", this.options)
log(this.nameSpace, "play", this.options)
await new Promise((r) => setTimeout(r, this.options.delay))

this.id = this.sound.play()
this.nameSpace = this.getNameSpace(this.id, this.url)
this.isPlaying = true

if (this.options.onUpdate) {
this.raf = this.rafRender()
}

return this.endedPromise.promise
}

public pause() {
if (!this.isPlaying || !this.isLoaded) return
this.sound.pause()
if (this.raf) {
this.cancelRaf()
}
}

public async stop() {
log(this.id, "stop")
log(this.nameSpace, "stop")
this.sound.stop(this.id)
this.isPlaying = false
if (this.raf) {
this.cancelRaf()
}
}

public replay() {
log(this.id, "replay")
log(this.nameSpace, "replay")
this.stop()
this.play()
}
Expand All @@ -150,22 +168,22 @@ export class AudioManager {
}

public mute(): void {
log(this.id, "mute")
log(this.nameSpace, "mute")
if (this.isMuted) return

this.sound.mute(true)
this.isMuted = true
}

public unmute(): void {
log(this.id, "unmute")
log(this.nameSpace, "unmute")
if (!this.isMuted) return
this.sound.mute(false)
this.isMuted = false
}

public async fade(from: number, to: number, duration = 1000): Promise<void> {
log(this.id, "fade >", from, to, this.options)
log(this.nameSpace, "fade >", from, to, this.options)
// play in case is not playing
if (!this.isPlaying) this.play()

Expand All @@ -176,28 +194,46 @@ export class AudioManager {
public async fadeIn(duration: number = 1000): Promise<void> {
// if (!this.isLoaded) await this.canplayPromise.promise
this.id = this.sound.play()
log(this.id, `fadeIn 0 -> ${this.options.volume}`)
log(this.nameSpace, `fadeIn 0 -> ${this.options.volume}`)
this.isPlaying = true

this.sound.fade(0, this.options.volume, duration)
return new Promise((r) =>
setTimeout(() => {
log(this.id, "fadeIn ended")
log(this.nameSpace, "fadeIn ended")
r()
}, duration)
)
}

public async fadeOut(duration: number = 1000): Promise<void> {
// if (!this.isLoaded) await this.canplayPromise.promise
log(this.id, `fadeOut ${this.options.volume} -> 0`)
log(this.nameSpace, `fadeOut ${this.options.volume} -> 0`)
this.sound.fade(this.options.volume, 0, duration)
return new Promise((resolve) =>
setTimeout(() => {
log(this.id, "fadeOut ended")
log(this.nameSpace, "fadeOut ended")
this.stop()
resolve()
}, duration)
)
}

public destroy() {
log(this.nameSpace, "destroy")
this.sound.unload()
MUTE_AUDIO_SIGNAL.remove(this.handleMuteAll)
}

protected rafRender() {
return requestAnimationFrame((time) => {
this.options.onUpdate?.(time)
this.raf = this.rafRender()
})
}

protected cancelRaf() {
cancelAnimationFrame(this.raf)
this.raf = null
}
}

0 comments on commit b89d0e3

Please sign in to comment.