Skip to content

Commit

Permalink
feat: follow [email protected] (AudioAsset#loopOffset)
Browse files Browse the repository at this point in the history
  • Loading branch information
xnv committed Nov 13, 2023
1 parent 5fcb3fb commit c3413ba
Show file tree
Hide file tree
Showing 25 changed files with 129 additions and 82 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- feat-loopoffset

env:
NODE_VERSION: 18
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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` と異なってしまう問題を修正

Expand Down
12 changes: 6 additions & 6 deletions e2e/tests/audio/HTMLAudio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioAsset>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AssetLoadError>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (_asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand Down Expand Up @@ -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) => {
Expand Down
12 changes: 6 additions & 6 deletions e2e/tests/audio/WebAudio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioAsset>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AssetLoadError>((resolve, reject) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (_asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand All @@ -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<AudioAsset>((resolve) => {
const loader: AssetLoadHandler = {
_onAssetLoad: (asset: AudioAsset) => {
Expand Down Expand Up @@ -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) => {
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -74,6 +74,7 @@
"@akashic/trigger": "^2.0.1"
},
"publishConfig": {
"@akashic:registry": "https://registry.npmjs.org/"
"@akashic:registry": "https://registry.npmjs.org/",
"tag": "next"
}
}
5 changes: 3 additions & 2 deletions src/ResourceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/asset/AudioAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -18,14 +19,16 @@ 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;
this.loop = loop;
this.hint = hint;
this._system = system;
this.offset = offset;
this.loopOffset = loopOffset;
this.path = this._modifyPath(this.path);
}

Expand Down
3 changes: 2 additions & 1 deletion src/plugin/AudioPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
47 changes: 27 additions & 20 deletions src/plugin/HTMLAudioPlugin/HTMLAudioPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/plugin/HTMLAudioPlugin/HTMLAudioPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioAsset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");
Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
3 changes: 2 additions & 1 deletion src/plugin/HTMLAudioPlugin/__tests__/HTMLAudioPlugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions src/plugin/ProxyAudioPlugin/ProxyAudioAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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));
Expand Down
Loading

0 comments on commit c3413ba

Please sign in to comment.