danoniplus 49.0.0 → 49.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/danoni_main.js CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2026/06/23
7
+ * Revised : 2026/07/04
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 49.0.0`;
12
- const g_revisedDate = `2026/06/23`;
11
+ const g_version = `Ver 49.1.1`;
12
+ const g_revisedDate = `2026/07/04`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -2514,7 +2514,7 @@ const drawTitleResultMotion = _displayName =>
2514
2514
  // WebAudioAPIでAudio要素風に再生するクラス
2515
2515
  class AudioPlayer {
2516
2516
  constructor() {
2517
- this._context = new AudioContext();
2517
+ this._context = getSharedAudioContext();
2518
2518
  this._gain = this._context.createGain();
2519
2519
  this._gain.connect(this._context.destination);
2520
2520
  this._startTime = 0;
@@ -2562,11 +2562,8 @@ class AudioPlayer {
2562
2562
  }
2563
2563
 
2564
2564
  close() {
2565
- if (this._context) {
2566
- this._context.close()?.catch(() => {/* ignore double-close */ });
2567
- this._buffer = null;
2568
- }
2569
2565
  if (this._source) {
2566
+ this._source.stop(0);
2570
2567
  this._source.disconnect(this._gain);
2571
2568
  this._source = null;
2572
2569
  }
@@ -2641,10 +2638,33 @@ class AudioPlayer {
2641
2638
  this._eventListeners[_type] = this._eventListeners[_type].filter(_element => _element !== _listener);
2642
2639
  }
2643
2640
 
2641
+ getBuffer() {
2642
+ return this._buffer;
2643
+ }
2644
+
2645
+ setBuffer(_buffer) {
2646
+ this._buffer = _buffer;
2647
+ this._duration = _buffer.duration;
2648
+ this._eventListeners[`canplaythrough`]?.forEach(_listener => _listener());
2649
+ }
2650
+
2644
2651
  load() { }
2645
2652
  dispatchEvent() { }
2646
2653
  }
2647
2654
 
2655
+ // グローバルで1つだけ保持(遅延生成)
2656
+ let g_sharedAudioContext = null;
2657
+ const getSharedAudioContext = () => {
2658
+ if (!g_sharedAudioContext) {
2659
+ g_sharedAudioContext = new AudioContext();
2660
+ }
2661
+ // タブのバックグラウンド化等でsuspendedになることがあるため念のためresume
2662
+ if (g_sharedAudioContext.state === `suspended`) {
2663
+ g_sharedAudioContext.resume();
2664
+ }
2665
+ return g_sharedAudioContext;
2666
+ };
2667
+
2648
2668
  /**
2649
2669
  * クリップボードコピー関数
2650
2670
  * 入力値をクリップボードへコピーし、メッセージを表示
@@ -6370,23 +6390,22 @@ const playBGM = async (_num, _currentLoopNum = g_settings.musicLoopNum) => {
6370
6390
 
6371
6391
  if (encodeFlg) {
6372
6392
  try {
6373
- // base64エンコードは読込に時間が掛かるため、曲変更時のみ読込
6374
- if (!hasVal(g_musicdata) || Math.abs(_num) % g_headerObj.musicIdxList.length !== 0) {
6375
- closeExistingAudio();
6393
+ closeExistingAudio();
6394
+ if (g_audioBufferCache.has(url)) {
6395
+ const tmpAudio = new AudioPlayer();
6396
+ tmpAudio.setBuffer(g_audioBufferCache.get(url)); // 新設メソッド、decode不要
6397
+ g_audioForMS = tmpAudio;
6398
+ } else {
6376
6399
  await loadScript2(url);
6377
6400
  musicInit();
6378
- if (!isTitle()) {
6379
- g_musicdata = ``;
6380
- return;
6381
- }
6401
+ if (!isTitle()) { g_musicdata = ``; return; }
6402
+
6382
6403
  const tmpAudio = new AudioPlayer();
6383
- const array = Uint8Array.from(atob(g_musicdata), v => v.charCodeAt(0));
6404
+ const array = base64ToUint8Array(g_musicdata);
6384
6405
  await tmpAudio.init(array.buffer);
6385
- if (!isTitle()) {
6386
- g_musicdata = ``;
6387
- tmpAudio.close();
6388
- return;
6389
- }
6406
+ if (!isTitle()) { g_musicdata = ``; tmpAudio.close(); return; }
6407
+
6408
+ cacheAudioBuffer(url, tmpAudio.getBuffer());
6390
6409
  g_audioForMS = tmpAudio;
6391
6410
  }
6392
6411
  g_audioForMS.volume = g_stateObj.bgmVolume / 100;
@@ -11969,7 +11988,7 @@ const loadMusic = () => {
11969
11988
  const blobUrl = URL.createObjectURL(request.response);
11970
11989
  createEmptySprite(divRoot, `loader`, g_windowObj.loader);
11971
11990
  lblLoading.textContent = g_lblNameObj.pleaseWait;
11972
- setAudio(blobUrl);
11991
+ setAudio(blobUrl, url);
11973
11992
  } else {
11974
11993
  makeWarningWindow(`${g_msgInfoObj.E_0041.split('{0}').join(getFullPath(url))}<br>(${request.status} ${request.statusText})`, { backBtnUse: true });
11975
11994
  }
@@ -12001,8 +12020,9 @@ const loadMusic = () => {
12001
12020
  * 音楽データの設定
12002
12021
  * iOSの場合はAudioタグによる再生
12003
12022
  * @param {string} _url
12023
+ * @param {string} _cacheKey
12004
12024
  */
12005
- const setAudio = async (_url) => {
12025
+ const setAudio = async (_url, _cacheKey = _url) => {
12006
12026
 
12007
12027
  const loadMp3 = () => {
12008
12028
  if (g_isFile) {
@@ -12019,6 +12039,7 @@ const setAudio = async (_url) => {
12019
12039
  g_currentPage = `loadingIos`;
12020
12040
  lblLoading.textContent = `Click to Start!`;
12021
12041
  divRoot.appendChild(makePlayButton(evt => {
12042
+ getSharedAudioContext().resume();
12022
12043
  g_currentPage = `loading`;
12023
12044
  resetKeyControl();
12024
12045
  divRoot.removeChild(evt.target);
@@ -12034,7 +12055,7 @@ const setAudio = async (_url) => {
12034
12055
  await loadScript2(_url);
12035
12056
  if (typeof musicInit === C_TYP_FUNCTION) {
12036
12057
  musicInit();
12037
- readyToStart(() => initWebAudioAPIfromBase64(g_musicdata));
12058
+ readyToStart(() => initWebAudioAPIfromBase64(g_musicdata, _cacheKey));
12038
12059
  } else {
12039
12060
  makeWarningWindow(g_msgInfoObj.E_0031);
12040
12061
  musicAfterLoaded();
@@ -12045,11 +12066,19 @@ const setAudio = async (_url) => {
12045
12066
  };
12046
12067
 
12047
12068
  // Base64から音声データに変換してWebAudioAPIで再生する準備
12048
- const initWebAudioAPIfromBase64 = async (_base64) => {
12069
+ const initWebAudioAPIfromBase64 = async (_base64, _cacheKey) => {
12049
12070
  g_audio = new AudioPlayer();
12050
12071
  musicAfterLoaded();
12051
- const array = Uint8Array.from(atob(_base64), v => v.charCodeAt(0))
12072
+
12073
+ if (_cacheKey && g_audioBufferCache.has(_cacheKey)) {
12074
+ g_audio.setBuffer(g_audioBufferCache.get(_cacheKey)); // デコード完全スキップ
12075
+ return;
12076
+ }
12077
+ const array = base64ToUint8Array(_base64);
12052
12078
  await g_audio.init(array.buffer);
12079
+ if (_cacheKey) {
12080
+ cacheAudioBuffer(_cacheKey, g_audio.getBuffer());
12081
+ }
12053
12082
  };
12054
12083
 
12055
12084
  // 音声ファイルを読み込んでWebAudioAPIで再生する準備
@@ -12061,6 +12090,26 @@ const initWebAudioAPIfromURL = async (_url) => {
12061
12090
  await g_audio.init(arrayBuffer);
12062
12091
  };
12063
12092
 
12093
+ const g_audioBufferCache = new Map();
12094
+ const AUDIO_CACHE_MAX = 5;
12095
+
12096
+ const cacheAudioBuffer = (_key, _buffer) => {
12097
+ g_audioBufferCache.set(_key, _buffer);
12098
+ if (g_audioBufferCache.size > AUDIO_CACHE_MAX) {
12099
+ g_audioBufferCache.delete(g_audioBufferCache.keys().next().value); // 古い順に破棄
12100
+ }
12101
+ };
12102
+
12103
+ const base64ToUint8Array = (_base64Str) => {
12104
+ const binaryStr = atob(_base64Str);
12105
+ const len = binaryStr.length;
12106
+ const array = new Uint8Array(len);
12107
+ for (let i = 0; i < len; i++) {
12108
+ array[i] = binaryStr.charCodeAt(i);
12109
+ }
12110
+ return array;
12111
+ };
12112
+
12064
12113
  const musicAfterLoaded = () => {
12065
12114
  g_audio.load();
12066
12115
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/06/23 (v49.0.0)
8
+ * Revised : 2026/07/04 (v49.1.1)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -145,7 +145,8 @@ const g_localeObj = {
145
145
  };
146
146
 
147
147
  const g_userAgent = window.navigator.userAgent.toLowerCase(); // msie, edge, chrome, safari, firefox, opera
148
- const g_isIos = listMatching(g_userAgent, [`iphone`, `ipad`, `ipod`]);
148
+ const g_isIos = listMatching(g_userAgent, [`iphone`, `ipad`, `ipod`]) ||
149
+ (navigator.maxTouchPoints > 1 && /macintosh/i.test(g_userAgent));
149
150
  const g_isMac = listMatching(g_userAgent, [`iphone`, `ipad`, `ipod`, `mac os`]);
150
151
 
151
152
  // 変数型
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "49.0.0",
3
+ "version": "49.1.1",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "jsdelivr": "./js/danoni_main.js",