diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64e78ba..a9ff100 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - feat-loopoffset env: NODE_VERSION: 18 diff --git a/CHANGELOG.md b/CHANGELOG.md index d94e62e..3ae3e07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.9.0-beta.0 +* @akashic/pdi-types@1.13.0-beta.0 に追従 + * `AudioAsset#loopOffset` に対応 + ## 2.8.3 * `MouseTouchEventHandler` において `button` の値が一部 `PointerEventHandler` と異なってしまう問題を修正 diff --git a/e2e/tests/audio/HTMLAudio.spec.ts b/e2e/tests/audio/HTMLAudio.spec.ts index e0f25be..fd83fea 100644 --- a/e2e/tests/audio/HTMLAudio.spec.ts +++ b/e2e/tests/audio/HTMLAudio.spec.ts @@ -39,7 +39,7 @@ describe("HTMLAudio", () => { const { HTMLAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new HTMLAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 10); + const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 10, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -65,7 +65,7 @@ describe("HTMLAudio", () => { const { HTMLAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new HTMLAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath + "?" + query, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAssetPath + "?" + query, 100, system, false, {}, 0, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -87,7 +87,7 @@ describe("HTMLAudio", () => { const { HTMLAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new HTMLAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", "not_found_audio", 100, system, false, {}, 0); + const asset = plugin.createAsset("id", "not_found_audio", 100, system, false, {}, 0, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (_asset: AudioAsset) => { @@ -112,7 +112,7 @@ describe("HTMLAudio", () => { const plugin = new HTMLAudioPlugin(); plugin.supportedFormats = [format]; const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 0, undefined); return new Promise((resolve) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -139,7 +139,7 @@ describe("HTMLAudio", () => { const plugin = new HTMLAudioPlugin(); plugin.supportedFormats = ["aac", "mp4"]; const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAsset2Path + "?" + query, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAsset2Path + "?" + query, 100, system, false, {}, 0, undefined); return new Promise((resolve) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -168,7 +168,7 @@ describe("HTMLAudio", () => { const plugin = new HTMLAudioPlugin(); plugin.supportedFormats = ["aac", "mp4"]; const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", seAssetPath, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", seAssetPath, 100, system, false, {}, 0, undefined); const player = asset.play(); player.changeVolume(0.1); return new Promise<[AudioAsset, AudioPlayer]>((resolve, reject) => { diff --git a/e2e/tests/audio/WebAudio.spec.ts b/e2e/tests/audio/WebAudio.spec.ts index 85edd72..7f9feec 100644 --- a/e2e/tests/audio/WebAudio.spec.ts +++ b/e2e/tests/audio/WebAudio.spec.ts @@ -39,7 +39,7 @@ describe("WebAudio", () => { const { WebAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new WebAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 10); + const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 10, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -65,7 +65,7 @@ describe("WebAudio", () => { const { WebAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new WebAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath + "?" + query, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAssetPath + "?" + query, 100, system, false, {}, 0, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -87,7 +87,7 @@ describe("WebAudio", () => { const { WebAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new WebAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", "not_found_audio", 100, system, false, {}, 0); + const asset = plugin.createAsset("id", "not_found_audio", 100, system, false, {}, 0, undefined); return new Promise((resolve, reject) => { const loader: AssetLoadHandler = { _onAssetLoad: (_asset: AudioAsset) => { @@ -113,7 +113,7 @@ describe("WebAudio", () => { const plugin = new WebAudioPlugin(); plugin.supportedFormats = [format]; const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAssetPath, 100, system, false, {}, 0, undefined); return new Promise((resolve) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -140,7 +140,7 @@ describe("WebAudio", () => { const plugin = new WebAudioPlugin(); plugin.supportedFormats = ["aac"]; const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", audioAsset2Path + "?" + query, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", audioAsset2Path + "?" + query, 100, system, false, {}, 0, undefined); return new Promise((resolve) => { const loader: AssetLoadHandler = { _onAssetLoad: (asset: AudioAsset) => { @@ -168,7 +168,7 @@ describe("WebAudio", () => { const { WebAudioPlugin } = require("@akashic/pdi-browser") as typeof index; const plugin = new WebAudioPlugin(); const system = new window.__mock__.MockAudioSystem({id: "voice"}); - const asset = plugin.createAsset("id", seAssetPath, 100, system, false, {}, 0); + const asset = plugin.createAsset("id", seAssetPath, 100, system, false, {}, 0, undefined); const player = asset.play(); player.changeVolume(0.1); return new Promise<[AudioAsset, AudioPlayer]>((resolve, reject) => { diff --git a/package-lock.json b/package-lock.json index fb8af64..da32d8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@akashic/pdi-browser", - "version": "2.8.3", + "version": "2.9.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@akashic/pdi-browser", - "version": "2.8.3", + "version": "2.9.0-beta.0", "license": "MIT", "dependencies": { "@akashic/trigger": "^2.0.1" @@ -14,7 +14,7 @@ "devDependencies": { "@akashic/amflow": "^3.3.0", "@akashic/eslint-config": "^1.1.1", - "@akashic/pdi-types": "^1.12.0", + "@akashic/pdi-types": "^1.13.0-beta.0", "@akashic/playlog": "^3.3.0", "@types/jest": "^29.2.0", "@types/node": "^18.0.0", @@ -91,9 +91,9 @@ } }, "node_modules/@akashic/pdi-types": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@akashic/pdi-types/-/pdi-types-1.12.0.tgz", - "integrity": "sha512-kgjUvhdDBFX0oDRy2imdST49T13cSBnRKy09sryWOxMicmq6JvVI3hMuqyyQoc18csr46N8DonkHFSE49skCXA==", + "version": "1.13.0-beta.0", + "resolved": "https://registry.npmjs.org/@akashic/pdi-types/-/pdi-types-1.13.0-beta.0.tgz", + "integrity": "sha512-0sp5sBye5/EcYlAhWfGxV2qkFHPheluI1rYionzS9JojNi24j+aBBrr7oJoynyV5Lf0rSIPcCIlATv4qC/CeVw==", "dev": true, "dependencies": { "@akashic/amflow": "~3.3.0", diff --git a/package.json b/package.json index 0632ac6..afe5595 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@akashic/pdi-browser", - "version": "2.8.3", + "version": "2.9.0-beta.0", "description": "An akashic-pdi implementation for Web browsers", "main": "index.js", "typings": "lib/full/index.d.ts", @@ -43,7 +43,7 @@ "devDependencies": { "@akashic/amflow": "^3.3.0", "@akashic/eslint-config": "^1.1.1", - "@akashic/pdi-types": "^1.12.0", + "@akashic/pdi-types": "^1.13.0-beta.0", "@akashic/playlog": "^3.3.0", "@types/jest": "^29.2.0", "@types/node": "^18.0.0", @@ -74,6 +74,7 @@ "@akashic/trigger": "^2.0.1" }, "publishConfig": { - "@akashic:registry": "https://registry.npmjs.org/" + "@akashic:registry": "https://registry.npmjs.org/", + "tag": "next" } } diff --git a/src/ResourceFactory.ts b/src/ResourceFactory.ts index a5e0eec..47f9e4d 100644 --- a/src/ResourceFactory.ts +++ b/src/ResourceFactory.ts @@ -41,13 +41,14 @@ export class ResourceFactory implements pdi.ResourceFactory { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number ): AudioAsset { const activePlugin = this._audioPluginManager.getActivePlugin(); if (!activePlugin) { throw new Error("ResourceFactory#createAudioAsset(): could not initialize ActivePlugin"); } - const audioAsset = activePlugin.createAsset(id, assetPath, duration, system, loop, hint, offset); + const audioAsset = activePlugin.createAsset(id, assetPath, duration, system, loop, hint, offset, loopOffset); this._audioManager.registerAudioAsset(audioAsset); audioAsset.onDestroyed.addOnce(this._onAudioAssetDestroyed, this); return audioAsset; diff --git a/src/asset/AudioAsset.ts b/src/asset/AudioAsset.ts index ad61cde..f140b6b 100644 --- a/src/asset/AudioAsset.ts +++ b/src/asset/AudioAsset.ts @@ -8,6 +8,7 @@ export abstract class AudioAsset extends Asset implements pdi.AudioAsset { loop: boolean; hint: pdi.AudioAssetHint; offset: number; + loopOffset: number | undefined; _system: pdi.AudioSystem; _lastPlayedPlayer: pdi.AudioPlayer | undefined; @@ -18,7 +19,8 @@ export abstract class AudioAsset extends Asset implements pdi.AudioAsset { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number | undefined, ) { super(id, path); this.duration = duration; @@ -26,6 +28,7 @@ export abstract class AudioAsset extends Asset implements pdi.AudioAsset { this.hint = hint; this._system = system; this.offset = offset; + this.loopOffset = loopOffset; this.path = this._modifyPath(this.path); } diff --git a/src/plugin/AudioPlugin.ts b/src/plugin/AudioPlugin.ts index 7339186..850645b 100644 --- a/src/plugin/AudioPlugin.ts +++ b/src/plugin/AudioPlugin.ts @@ -19,7 +19,8 @@ export interface AudioPlugin { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number, ) => AudioAsset; createPlayer: (system: pdi.AudioSystem, manager: AudioManager) => AudioPlayer; diff --git a/src/plugin/HTMLAudioPlugin/HTMLAudioPlayer.ts b/src/plugin/HTMLAudioPlugin/HTMLAudioPlayer.ts index 6c42a80..15a9d98 100644 --- a/src/plugin/HTMLAudioPlugin/HTMLAudioPlayer.ts +++ b/src/plugin/HTMLAudioPlugin/HTMLAudioPlayer.ts @@ -32,34 +32,41 @@ export class HTMLAudioPlayer extends AudioPlayer { const audio = asset.cloneElement(); if (audio) { - if (asset.offset === undefined) { - // offsetが指定されていない場合、durationを無視して全体再生する + // NOTE: 後方互換のため、offset の指定がない場合は duration を無視 (終端まで再生) + const duration = (asset.duration != null && asset.offset != null) ? asset.duration / 1000 : null; + const offset = (asset.offset ?? 0) / 1000; + const loopStart = (asset.loop && asset.loopOffset != null) ? asset.loopOffset / 1000 : offset; + const end = (duration != null) ? offset + duration : null; + + audio.currentTime = offset; + if (loopStart === 0 && end == null) { audio.loop = asset.loop; + audio.addEventListener("ended", this._endedEventHandler); } else { - const offsetSec = (asset.offset ?? 0) / 1000; - const durationEndSec = asset.duration / 1000 + offsetSec; - audio.currentTime = offsetSec; - audio.ontimeupdate = () => { - if (durationEndSec <= audio.currentTime) { - if (asset.loop) { - audio.currentTime = offsetSec; - } else { - audio.pause(); - } - } - }; - audio.onended = () => { - if (asset.loop) { - audio.currentTime = offsetSec; + if (!asset.loop) { + audio.addEventListener("ended", this._endedEventHandler); + } else { + audio.addEventListener("ended", () => { + audio.currentTime = loopStart; audio.play(); - } - }; + }); + } + if (end != null) { + audio.addEventListener("timeupdate", () => { + if (end <= audio.currentTime) { + if (asset.loop) { + audio.currentTime = loopStart; + } else { + audio.pause(); + } + } + }); + } } setupChromeMEIWorkaround(audio); audio.volume = this._calculateVolume(); audio.play().catch((_err) => { /* user interactの前にplay()を呼ぶとエラーになる。これはHTMLAudioAutoplayHelperで吸収する */}); - audio.addEventListener("ended", this._endedEventHandler, false); audio.addEventListener("play", this._onPlayEventHandler, false); this._isWaitingPlayEvent = true; this._audioInstance = audio; diff --git a/src/plugin/HTMLAudioPlugin/HTMLAudioPlugin.ts b/src/plugin/HTMLAudioPlugin/HTMLAudioPlugin.ts index 6693bdb..5d40d76 100644 --- a/src/plugin/HTMLAudioPlugin/HTMLAudioPlugin.ts +++ b/src/plugin/HTMLAudioPlugin/HTMLAudioPlugin.ts @@ -46,9 +46,10 @@ export class HTMLAudioPlugin implements AudioPlugin { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number | undefined ): AudioAsset { - return new HTMLAudioAsset(id, path, duration, system, loop, hint, offset); + return new HTMLAudioAsset(id, path, duration, system, loop, hint, offset, loopOffset); } createPlayer(system: pdi.AudioSystem, manager: AudioManager): AudioPlayer { diff --git a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioAsset.spec.ts b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioAsset.spec.ts index 560ed9c..5e65f9c 100644 --- a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioAsset.spec.ts +++ b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioAsset.spec.ts @@ -8,10 +8,11 @@ describe("HTMLAudioAsset", () => { it("can instantiate", () => { const system = new MockAudioSystem({ id: "audio-system" }); - const asset = new HTMLAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = new HTMLAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, undefined); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.ogg"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBeUndefined(); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false); @@ -27,7 +28,8 @@ describe("HTMLAudioAsset", () => { system, false, { streaming: false, extensions: [".m4a", ".aac"] }, - 10 + 10, + undefined ); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.m4a"); @@ -42,7 +44,8 @@ describe("HTMLAudioAsset", () => { system, false, { streaming: false, extensions: [".mp4", ".aac"] }, - 10 + 10, + undefined ); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.aac"); diff --git a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlayer.spec.ts b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlayer.spec.ts index 2509831..1ae4039 100644 --- a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlayer.spec.ts +++ b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlayer.spec.ts @@ -22,7 +22,7 @@ describe("HTMLAudioPlayer", () => { const system = new MockAudioSystem({ id: "audio-system" }); const manager = new AudioManager(); const player = new HTMLAudioPlayer(system, manager); - const asset = new HTMLAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new HTMLAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, undefined); expect(player.currentAudio).toBeUndefined(); diff --git a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlugin.spec.ts b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlugin.spec.ts index 3ff65fb..6d335cf 100644 --- a/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlugin.spec.ts +++ b/src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlugin.spec.ts @@ -13,11 +13,12 @@ describe("HTMLAudioPlugin", () => { const plugin = new HTMLAudioPlugin(); plugin.supportedFormats = ["ogg", "aac"]; const system = new MockAudioSystem({ id: "audio-system" }); - const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.ogg"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBe(20); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false); diff --git a/src/plugin/ProxyAudioPlugin/ProxyAudioAsset.ts b/src/plugin/ProxyAudioPlugin/ProxyAudioAsset.ts index 2479883..617383c 100644 --- a/src/plugin/ProxyAudioPlugin/ProxyAudioAsset.ts +++ b/src/plugin/ProxyAudioPlugin/ProxyAudioAsset.ts @@ -14,9 +14,10 @@ export class ProxyAudioAsset extends AudioAsset { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number | undefined, ) { - super(id, assetPath, duration, system, loop, hint, offset); + super(id, assetPath, duration, system, loop, hint, offset, loopOffset); this._handlerSet = handlerSet; } @@ -32,7 +33,8 @@ export class ProxyAudioAsset extends AudioAsset { duration: this.duration, loop: this.loop, hint: this.hint, - offset: this.offset + offset: this.offset, + loopOffset: this.loopOffset }, (err?: any) => { if (err) { loader._onAssetError(this, ExceptionFactory.createAssetLoadError(err)); diff --git a/src/plugin/ProxyAudioPlugin/ProxyAudioHandlerSet.ts b/src/plugin/ProxyAudioPlugin/ProxyAudioHandlerSet.ts index 5edf14a..8039c4a 100644 --- a/src/plugin/ProxyAudioPlugin/ProxyAudioHandlerSet.ts +++ b/src/plugin/ProxyAudioPlugin/ProxyAudioHandlerSet.ts @@ -9,6 +9,7 @@ export interface LoadAudioAssetParameterObject { loop: boolean; hint: AudioAssetHintParameterObject; offset: number; + loopOffset: number | undefined; } export interface CreateAudioPlayerParameterObject { diff --git a/src/plugin/ProxyAudioPlugin/ProxyAudioPlugin.ts b/src/plugin/ProxyAudioPlugin/ProxyAudioPlugin.ts index bd1a9b8..1eb07a6 100644 --- a/src/plugin/ProxyAudioPlugin/ProxyAudioPlugin.ts +++ b/src/plugin/ProxyAudioPlugin/ProxyAudioPlugin.ts @@ -27,9 +27,10 @@ export class ProxyAudioPlugin implements AudioPlugin { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number | undefined ): AudioAsset { - return new ProxyAudioAsset(this._handlerSet, id, assetPath, duration, system, loop, hint, offset); + return new ProxyAudioAsset(this._handlerSet, id, assetPath, duration, system, loop, hint, offset, loopOffset); } createPlayer(system: pdi.AudioSystem, manager: AudioManager): AudioPlayer { diff --git a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioAsset.spec.ts b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioAsset.spec.ts index 5f9b9f3..58243a1 100644 --- a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioAsset.spec.ts +++ b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioAsset.spec.ts @@ -6,11 +6,22 @@ describe("ProxyAudioAsset", () => { it("can instantiate", () => { const system = new MockAudioSystem({ id: "audio-system" }); const handlerSet = new MockProxyAudioHandlerSet(); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = new ProxyAudioAsset( + handlerSet, + "audio-asset", + "/path/to/audio", + 100, + system, + false, + { streaming: false }, + 10, + undefined + ); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBeUndefined(); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false); @@ -20,7 +31,7 @@ describe("ProxyAudioAsset", () => { it("can load", done => { const system = new MockAudioSystem({ id: "audio-system" }); const handlerSet = new MockProxyAudioHandlerSet(); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); asset._load({ _onAssetLoad: (a) => { @@ -37,7 +48,7 @@ describe("ProxyAudioAsset", () => { it("can unload", done => { const system = new MockAudioSystem({ id: "audio-system" }); const handlerSet = new MockProxyAudioHandlerSet(); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); asset._load({ _onAssetLoad: (a) => { diff --git a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlayer.spec.ts b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlayer.spec.ts index 7190df1..f3957e5 100644 --- a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlayer.spec.ts +++ b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlayer.spec.ts @@ -25,7 +25,7 @@ describe("ProxyAudioPlayer", () => { const manager = new AudioManager(); const handlerSet = new MockProxyAudioHandlerSet(); const player = new ProxyAudioPlayer(handlerSet, system, manager); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, 20); expect(player.currentAudio).toBeUndefined(); expect(handlerSet.playAudioPlayer).not.toBeCalled(); @@ -44,7 +44,7 @@ describe("ProxyAudioPlayer", () => { const manager = new AudioManager(); const handlerSet = new MockProxyAudioHandlerSet(); const player = new ProxyAudioPlayer(handlerSet, system, manager); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, 20); player.play(asset); player.play(asset); @@ -57,7 +57,7 @@ describe("ProxyAudioPlayer", () => { const manager = new AudioManager(); const handlerSet = new MockProxyAudioHandlerSet(); const player = new ProxyAudioPlayer(handlerSet, system, manager); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, 20); expect(handlerSet.createAudioPlayer).not.toBeCalled(); @@ -76,7 +76,7 @@ describe("ProxyAudioPlayer", () => { const manager = new AudioManager(); const handlerSet = new MockProxyAudioHandlerSet(); const player = new ProxyAudioPlayer(handlerSet, system, manager); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, 20); expect(handlerSet.destroyAudioPlayer).not.toBeCalled(); @@ -90,7 +90,7 @@ describe("ProxyAudioPlayer", () => { const manager = new AudioManager(); const handlerSet = new MockProxyAudioHandlerSet(); const player = new ProxyAudioPlayer(handlerSet, system, manager); - const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new ProxyAudioAsset(handlerSet, "audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, 20); expect(handlerSet.changeAudioVolume).not.toBeCalled(); diff --git a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlugin.spec.ts b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlugin.spec.ts index d103136..8759532 100644 --- a/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlugin.spec.ts +++ b/src/plugin/ProxyAudioPlugin/__tests__/ProxyAudioPlugin.spec.ts @@ -13,11 +13,12 @@ describe("ProxyAudioPlugin", () => { const handlerSet = new MockProxyAudioHandlerSet(); const plugin = new ProxyAudioPlugin(handlerSet); const system = new MockAudioSystem({ id: "audio-system" }); - const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBe(20); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false); diff --git a/src/plugin/WebAudioPlugin/WebAudioPlayer.ts b/src/plugin/WebAudioPlugin/WebAudioPlayer.ts index d18de98..171bdc5 100644 --- a/src/plugin/WebAudioPlugin/WebAudioPlayer.ts +++ b/src/plugin/WebAudioPlugin/WebAudioPlayer.ts @@ -38,6 +38,7 @@ export class WebAudioPlayer extends AudioPlayer { if (this.currentAudio) { this.stop(); } + if (asset.data) { const bufferNode = helper.createBufferNode(this._audioContext); bufferNode.buffer = asset.data; @@ -47,18 +48,20 @@ export class WebAudioPlayer extends AudioPlayer { // Chromeだとevent listerで指定した場合に動かないことがある // https://github.com/mozilla-appmaker/appmaker/issues/1984 this._sourceNode.onended = this._endedEventHandler; - // loop時にoffsetを指定すると正しく動作しないことがあるため、暫定対応としてloopが真の場合はoffsetを指定しない + + bufferNode.loop = asset.loop; + + const offset = (asset.offset ?? 0) / 1000; + const duration = (asset.duration != null) ? asset.duration / 1000 : undefined; if (asset.loop) { - bufferNode.loop = asset.loop; - this._sourceNode.start(0); + bufferNode.loopStart = asset.loopOffset ? (asset.loopOffset / 1000) : offset; + if (duration != null) + bufferNode.loopEnd = offset + duration; + this._sourceNode.start(0, offset); } else { - const offset = (asset.offset ?? 0) / 1000; - if (asset.duration > 0) { - this._sourceNode.start(0, offset, asset.duration / 1000); - } else { - this._sourceNode.start(0, offset); - } + this._sourceNode.start(0, offset, duration); } + } else { // 再生できるオーディオがない場合。duration後に停止処理だけ行う(処理のみ進め音は鳴らさない) this._dummyDurationWaitTimer = setTimeout(this._endedEventHandler, asset.duration); diff --git a/src/plugin/WebAudioPlugin/WebAudioPlugin.ts b/src/plugin/WebAudioPlugin/WebAudioPlugin.ts index b3e8953..5a54425 100644 --- a/src/plugin/WebAudioPlugin/WebAudioPlugin.ts +++ b/src/plugin/WebAudioPlugin/WebAudioPlugin.ts @@ -46,9 +46,10 @@ export class WebAudioPlugin implements AudioPlugin { system: pdi.AudioSystem, loop: boolean, hint: pdi.AudioAssetHint, - offset: number + offset: number, + loopOffset: number | undefined, ): AudioAsset { - return new WebAudioAsset(id, assetPath, duration, system, loop, hint, offset); + return new WebAudioAsset(id, assetPath, duration, system, loop, hint, offset, loopOffset); } createPlayer(system: pdi.AudioSystem, manager: AudioManager): AudioPlayer { diff --git a/src/plugin/WebAudioPlugin/__tests__/WebAudioAsset.spec.ts b/src/plugin/WebAudioPlugin/__tests__/WebAudioAsset.spec.ts index 0e67141..5586edb 100644 --- a/src/plugin/WebAudioPlugin/__tests__/WebAudioAsset.spec.ts +++ b/src/plugin/WebAudioPlugin/__tests__/WebAudioAsset.spec.ts @@ -10,10 +10,11 @@ describe("HTMLAudioAsset", () => { it("can instantiate", () => { const system = new MockAudioSystem({ id: "audio-system" }); - const asset = new WebAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = new WebAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.ogg"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBe(20); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false); @@ -29,7 +30,8 @@ describe("HTMLAudioAsset", () => { system, false, { streaming: false, extensions: [".m4a", ".aac"] }, - 10 + 10, + undefined ); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.m4a"); @@ -44,7 +46,8 @@ describe("HTMLAudioAsset", () => { system, false, { streaming: false, extensions: [".mp4", ".aac"] }, - 10 + 10, + undefined ); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.aac"); diff --git a/src/plugin/WebAudioPlugin/__tests__/WebAudioPlayer.spec.ts b/src/plugin/WebAudioPlugin/__tests__/WebAudioPlayer.spec.ts index 5d1cc66..8b45da8 100644 --- a/src/plugin/WebAudioPlugin/__tests__/WebAudioPlayer.spec.ts +++ b/src/plugin/WebAudioPlugin/__tests__/WebAudioPlayer.spec.ts @@ -24,7 +24,7 @@ describe("WebAudioPlayer", () => { const system = new MockAudioSystem({ id: "audio-system" }); const manager = new AudioManager(); const player = new WebAudioPlayer(system, manager); - const asset = new WebAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0); + const asset = new WebAudioAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 0, undefined); expect(player.currentAudio).toBeUndefined(); diff --git a/src/plugin/WebAudioPlugin/__tests__/WebAudioPlugin.spec.ts b/src/plugin/WebAudioPlugin/__tests__/WebAudioPlugin.spec.ts index b36b6a1..a27dcb6 100644 --- a/src/plugin/WebAudioPlugin/__tests__/WebAudioPlugin.spec.ts +++ b/src/plugin/WebAudioPlugin/__tests__/WebAudioPlugin.spec.ts @@ -15,11 +15,12 @@ describe("WebAudioPlugin", () => { const plugin = new WebAudioPlugin(); plugin.supportedFormats = ["ogg", "aac"]; const system = new MockAudioSystem({ id: "audio-system" }); - const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10); + const asset = plugin.createAsset("audio-asset", "/path/to/audio", 100, system, false, { streaming: false }, 10, 20); expect(asset.id).toBe("audio-asset"); expect(asset.path).toBe("/path/to/audio.ogg"); expect(asset.offset).toBe(10); + expect(asset.loopOffset).toBe(20); expect(asset.duration).toBe(100); expect(asset.hint).toEqual({ streaming: false }); expect(asset.loop).toBe(false);