Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Commit

Permalink
add loadMusic() and streamed audio support
Browse files Browse the repository at this point in the history
  • Loading branch information
slmjkdbtl committed Dec 17, 2023
1 parent 2e62e4d commit 0eb9125
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 24 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## v3000.2
- added `loadMusic()` to load streaming audio (doesn't block in loading screen)

### v3000.1.17
- exposed `vel` property on `BodyComp`

Expand Down
2 changes: 1 addition & 1 deletion examples/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ kaboom({
})

loadSound("bell", "/examples/sounds/bell.mp3")
loadSound("OtherworldlyFoe", "/examples/sounds/OtherworldlyFoe.mp3")
loadMusic("OtherworldlyFoe", "/examples/sounds/OtherworldlyFoe.mp3")

// play() to play audio
// (This might not play until user input due to browser policy)
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const run = async () => {
let failed = false
console.log("launching browser")
const browser = await puppeteer.launch({
headless: "new"
headless: "new",
})
console.log("getting examples list")
const examples = (await fs.readdir("examples"))
Expand Down
1 change: 1 addition & 0 deletions src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class Asset<D> {
private onFinishEvents: Event<[]> = new Event()
constructor(loader: Promise<D>) {
loader.then((data) => {
this.loaded = true
this.data = data
this.onLoadEvents.trigger(data)
}).catch((err) => {
Expand Down
140 changes: 133 additions & 7 deletions src/kaboom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ import type {
Outline,
PolygonComp,
PolygonCompOpt,
MusicData,
} from "./types"

import beanSpriteSrc from "./assets/bean.png"
Expand Down Expand Up @@ -633,6 +634,7 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
fonts: new AssetBucket<FontData>(),
bitmapFonts: new AssetBucket<BitmapFontData>(),
sounds: new AssetBucket<SoundData>(),
music: new AssetBucket<MusicData>(),
shaders: new AssetBucket<ShaderData>(),
custom: new AssetBucket<any>(),
packer: new TexPacker(ggl, SPRITE_ATLAS_WIDTH, SPRITE_ATLAS_HEIGHT),
Expand Down Expand Up @@ -1016,7 +1018,6 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
return assets.shaders.add(name, load)
}

// TODO: allow stream big audio
// load a sound to asset manager
function loadSound(
name: string | null,
Expand All @@ -1031,6 +1032,13 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
)
}

function loadMusic(
name: string | null,
url: string,
) {
return assets.music.addLoaded(name, new Audio(fixURL(url)))
}

function loadBean(name: string = "bean"): Asset<SpriteData> {
return loadSprite(name, beanSpriteSrc)
}
Expand All @@ -1043,6 +1051,10 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
return assets.sounds.get(name)
}

function getMusic(name: string): Asset<MusicData> | void {
return assets.music.get(name)
}

function getFont(name: string): Asset<FontData> | void {
return assets.fonts.get(name)
}
Expand Down Expand Up @@ -1084,7 +1096,7 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
}

function resolveSound(
src: Parameters<typeof play>[0],
src: string | SoundData | Asset<SoundData>,
): Asset<SoundData> | null {
if (typeof src === "string") {
const snd = getSound(src)
Expand All @@ -1104,6 +1116,25 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
}
}

function resolveMusic(
src: string | MusicData | Asset<MusicData>,
): MusicData | null {
if (typeof src === "string") {
const music = getMusic(src)
if (music) {
return music.data
} else {
throw new Error(`Music not found: ${src}`)
}
} else if (src instanceof HTMLAudioElement) {
return src
} else if (src instanceof Asset) {
return src.data
} else {
throw new Error(`Invalid music: ${src}`)
}
}

function resolveShader(
src: RenderProps["shader"],
): ShaderData | Asset<ShaderData> | null {
Expand Down Expand Up @@ -1170,15 +1201,108 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
return audio.masterNode.gain.value
}

// TODO: method to completely destory audio?
// TODO: time() not correct when looped over or ended
// TODO: onEnd() not working
// plays a sound, returns a control handle
// TODO: create new Audio() on every call?
function playMusic(music: MusicData, opt: AudioPlayOpt = {}): AudioPlay {

const onEndEvents = new Event()

if (!opt.paused) {
music.play()
}

music.onended = () => onEndEvents.trigger()

return {

play(time: number) {
this.seek(time)
},

seek() {
music.play()
},

stop() {
music.play
},

set loop(l: boolean) {
music.loop = l
},

get loop() {
return music.loop
},

set paused(p: boolean) {
if (p) {
music.pause()
} else {
music.play()
}
},

get paused() {
return music.paused
},

time() {
return music.currentTime
},

duration() {
return music.duration
},

set volume(val: number) {
music.volume = val
},

get volume() {
return music.volume
},

set speed(s) {
music.playbackRate = Math.max(s, 0)
},

get speed() {
return music.playbackRate
},

set detune(d) {
// TODO
},

get detune() {
// TODO
return 0
},

onEnd(action: () => void) {
return onEndEvents.add(action)
},

then(action: () => void) {
return this.onEnd(action)
},

}

}

function play(
src: string | SoundData | Asset<SoundData>,
src: string | SoundData | Asset<SoundData> | MusicData | Asset<MusicData>,
opt: AudioPlayOpt = {},
): AudioPlay {

// @ts-ignore
const music = resolveMusic(src)

if (music) {
return playMusic(music)
}

const ctx = audio.ctx
let paused = opt.paused ?? false
let srcNode = ctx.createBufferSource()
Expand Down Expand Up @@ -1210,6 +1334,7 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
}
}

// @ts-ignore
const snd = resolveSound(src)

if (snd instanceof Asset) {
Expand Down Expand Up @@ -6289,6 +6414,7 @@ export default (gopt: KaboomOpt = {}): KaboomCtx => {
loadSprite,
loadSpriteAtlas,
loadSound,
loadMusic,
loadBitmapFont,
loadFont,
loadShader,
Expand Down
31 changes: 21 additions & 10 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1184,14 +1184,27 @@ export interface KaboomCtx {
*
* @example
* ```js
* loadSound("shoot", "horse.ogg")
* loadSound("shoot", "https://kaboomjs.com/sounds/scream6.mp3")
* loadSound("shoot", "/sounds/horse.ogg")
* loadSound("shoot", "/sounds/squeeze.mp3")
* loadSound("shoot", "/sounds/shoot.wav")
* ```
*/
loadSound(
name: string | null,
src: string | ArrayBuffer,
): Asset<SoundData>,
/**
* Like loadSound(), but the audio is streamed and won't block loading. Use this for big audio files like background music.
*
* @example
* ```js
* loadMusic("shoot", "/music/bossfight.mp3")
* ```
*/
loadMusic(
name: string | null,
url: string,
): Asset<MusicData>,
/**
* Load a font (any format supported by the browser, e.g. ttf, otf, woff).
*
Expand Down Expand Up @@ -1642,7 +1655,10 @@ export interface KaboomCtx {
* music.speed = 1.2
* ```
*/
play(src: string | SoundData | Asset<SoundData>, options?: AudioPlayOpt): AudioPlay,
play(
src: string | SoundData | Asset<SoundData> | MusicData | Asset<MusicData>,
options?: AudioPlayOpt,
): AudioPlay,
/**
* Yep.
*/
Expand Down Expand Up @@ -2914,13 +2930,6 @@ export interface LoadSpriteOpt {
anims?: SpriteAnims,
}

export interface LoadSoundOpt {
/**
* If stream audio instead of loading the entire audio upfront, use this for large audio files like background music.
*/
stream?: boolean,
}

export type NineSlice = {
/**
* The width of the 9-slice's left column.
Expand Down Expand Up @@ -3027,6 +3036,8 @@ export declare class SoundData {
static fromURL(url: string): Promise<SoundData>
}

export type MusicData = HTMLAudioElement

export interface LoadFontOpt {
filter?: TexFilter,
outline?: number | Outline,
Expand Down
6 changes: 1 addition & 5 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
export class Registry<T> extends Map<number, T> {
private lastID: number
constructor(...args) {
super(...args)
this.lastID = 0
}
private lastID: number = 0
push(v: T): number {
const id = this.lastID
this.set(id, v)
Expand Down

0 comments on commit 0eb9125

Please sign in to comment.