Spaces:
Running
Running
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.00" /> | |
<meta name="robots" content="noindex,nofollow,noarchive" /> | |
<script | |
src="https://code.jquery.com/jquery-3.6.1.min.js" | |
integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" | |
crossorigin="anonymous" | |
></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js"></script> | |
<link | |
rel="stylesheet" | |
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" | |
/> | |
<link | |
href="https://cdn.jsdelivr.net/gh/daydreamer-json/SomeFontRepo@main/somefontrepo.css" | |
rel="stylesheet" | |
type="text/css" | |
media="all" | |
/> | |
<style> | |
html, | |
body { | |
color: #fff; | |
background: #000; | |
margin: 0; | |
line-height: 1; | |
width: 100vw; | |
height: 100vh; | |
font-family: "A-OTF UD Shin Go Pro", system-ui; | |
} | |
textarea, | |
button { | |
color: #fff; | |
background: #000; | |
width: 100vw; | |
font-family: system-ui; | |
resize: none; | |
} | |
.inputbox { | |
color: #fff; | |
background: #000; | |
width: 20vw; | |
font-family: system-ui; | |
} | |
</style> | |
<script> | |
function logger(text) { | |
document.getElementById("textbox").value += "\r\n" + text; // 新しい行に文字列を追加する | |
} | |
// 以下のスクリプトはBing(ChatGPT)が生成したものに追記したものです。 | |
// プロンプト: | |
// ある曲のカラオケバージョンの音源と、3人分のボーカルの音源があります。つまり、4つの音声ファイルがあります。これらのファイルを完全に同期して同時再生するコードを、Howler.jsを使用した上でHTMLとJavaScriptで書いてください。 | |
var howls = []; | |
// 同期再生用の関数を定義 | |
function playSync() { | |
stopAll(); | |
var liveID = document.getElementById("liveIDBox").value; | |
var charaID = [ | |
document.getElementById("charaIDBox1").value, | |
document.getElementById("charaIDBox2").value, | |
document.getElementById("charaIDBox3").value, | |
]; | |
var okeVol = document.getElementById("okeVolBox").value; | |
var charaVol1 = document.getElementById("charaVolBox1").value; | |
var charaVol2 = document.getElementById("charaVolBox2").value; | |
var charaPan = document.getElementById("charaPanBox").value; | |
// 音声ファイルのパスを配列に格納 | |
var audioFiles = [ | |
`https://cdn.jsdelivr.net/gh/daydreamer-json/umm_assets@main/sound/l/${liveID}/snd_bgm_live_${liveID}_oke_02/snd_bgm_live_${liveID}_oke_02_1.m4a`, | |
`https://cdn.jsdelivr.net/gh/daydreamer-json/umm_assets@main/sound/l/${liveID}/snd_bgm_live_${liveID}_chara_${charaID[0]}_01/snd_bgm_live_${liveID}_chara_${charaID[0]}_01_1.m4a`, | |
`https://cdn.jsdelivr.net/gh/daydreamer-json/umm_assets@main/sound/l/${liveID}/snd_bgm_live_${liveID}_chara_${charaID[1]}_01/snd_bgm_live_${liveID}_chara_${charaID[1]}_01_1.m4a`, | |
`https://cdn.jsdelivr.net/gh/daydreamer-json/umm_assets@main/sound/l/${liveID}/snd_bgm_live_${liveID}_chara_${charaID[2]}_01/snd_bgm_live_${liveID}_chara_${charaID[2]}_01_1.m4a`, | |
]; | |
logger("playSync関数が呼び出されました"); | |
logger(`liveIDは${liveID}です`); | |
// logger(`liveIDのtext_dataは${master_db_text_data_search(16, liveID).text}です`); | |
for (let i = 0; i < charaID.length; i++) { | |
logger(`charaIDは${charaID[i]}です: id=${i + 1}`); | |
} | |
document.getElementById("urllistbox").value = ""; | |
remote_file_head_test(audioFiles).then((statusList) => { | |
if (statusList.every((status) => status === 200)) { | |
for (let i = 0; i < statusList.length; i++) { | |
logger(`リモートファイルへHEADしました: id=${i}, status=${statusList[i]}`); | |
}; | |
logger(`リモートファイルは正常です`); | |
// 配列の要素数だけ繰り返す | |
for (let i = 0; i < audioFiles.length; i++) { | |
// Howlオブジェクトを作成して配列に追加 | |
howls[i] = new Howl({ | |
src: audioFiles[i], // 音声ファイルのパス | |
preload: true, // 読み込み完了まで待つかどうか | |
autoplay: false, // 自動再生するかどうか | |
loop: true, // ループ再生するかどうか | |
}); | |
logger(`Howlオブジェクトを作成しました: id=${i}`); | |
} | |
// 全ての音声ファイルが読み込まれたら実行する関数を設定 | |
var loadedCount = 0; // 読み込まれた音声ファイルの数 | |
var onLoad = function () { | |
loadedCount++; // 読み込まれた数を増やす | |
logger( | |
`音声ファイルがプリロードされました: id=${loadedCount - 1}` | |
); | |
document.getElementById("urllistbox").value += | |
audioFiles[loadedCount - 1].match(/([^\/?#]+)(?=[^\/]*$)/)[0] + | |
"\r\n"; | |
if (loadedCount == audioFiles.length) { | |
// 全て読み込まれたら | |
howls[0].volume(okeVol); | |
logger( | |
`Howlオブジェクトのvolumeを変更しました: id=0, value=${okeVol}` | |
); | |
for (let j = 2; j <= 3; j++) { | |
howls[j].volume(charaVol2); // ボーカルの音源3つ(インデックス1から3)の音量を設定(追加したコード) | |
logger( | |
`Howlオブジェクトのvolumeを変更しました: id=${j}, value=${charaVol2}` | |
); | |
} | |
howls[1].volume(charaVol1); | |
logger( | |
`Howlオブジェクトのvolumeを変更しました: id=1, value=${charaVol1}` | |
); | |
howls[2].stereo(0 - charaPan); // 特定のボーカルだけ左チャンネルに寄せる(追加したコード) | |
logger( | |
`Howlオブジェクトのstereoを変更しました: id=2, value=${ | |
0 - charaPan | |
}` | |
); | |
howls[3].stereo(charaPan); // 特定のボーカルだけ右チャンネルに寄せる(追加したコード) | |
logger( | |
`Howlオブジェクトのstereoを変更しました: id=3, value=${charaPan}` | |
); | |
for (let j = 0; j < howls.length; j++) { | |
// 配列の要素数だけ繰り返す | |
howls[j].play(); // 再生する(Howlオブジェクトは自動的に同期される) | |
logger(`Howlオブジェクトを再生しました: id=${j}`); | |
} | |
} | |
}; | |
for (let k = 0; k < howls.length; k++) { | |
// 配列の要素数だけ繰り返す | |
howls[k].once("load", onLoad); // 読み込み完了時にonLoad関数を実行するように設定 | |
} | |
} else { | |
for (let i = 0; i < statusList.length; i++) { | |
logger(`リモートファイルへHEADしました: id=${i}, status=${statusList[i]}`); | |
} | |
logger(`リモートファイルの一部に問題があります`); | |
} | |
}); | |
} | |
// 再生停止用の関数を定義(追加したコード) | |
function stopAll() { | |
for (var n = 0; n < howls.length; n++) { | |
// 配列の要素数だけ繰り返す | |
howls[n].stop(); // 停止する | |
howls[n].unload(); | |
} | |
document.getElementById("textbox").value = | |
"Logger出力はここに表示されます"; | |
logger(`stopAll関数が呼び出されました`); | |
} | |
function master_db_text_data_search(categoryNumber, idNumber) { | |
fetch( | |
"https://cdn.jsdelivr.net/gh/daydreamer-json/umm_master_db@main/json/text_data.json" | |
) | |
.then((response) => response.json()) | |
.then((data) => { | |
var master_db_text_data = data; | |
logger(`マスターデータベースを読み込みました`); | |
let categoryResult = master_db_text_data.filter(function (obj) { | |
return obj.category === categoryNumber; | |
}); | |
let integratedResult = categoryResult.filter(function (obj) { | |
return obj.id === idNumber; | |
}); | |
return integratedResult[0]; | |
}) | |
.catch((error) => { | |
console.error(error); | |
return null; | |
}); | |
} | |
async function remote_file_head_test(audioFilesList) { | |
const promises = audioFilesList.map((url) => | |
fetch(url, { method: "HEAD" }) | |
); | |
const responses = await Promise.all(promises); | |
return responses.map((response) => response.status); | |
} | |
</script> | |
</head> | |
<body> | |
<button onclick="playSync()" style="font-size: 24px">同期再生</button> | |
<button onclick="stopAll()" style="display: none">停止</button> | |
<br /> | |
<input | |
class="inputbox" | |
id="liveIDBox" | |
type="number" | |
inputmode="numeric" | |
required | |
min="1001" | |
max="9999" | |
value="1036" | |
/> | |
<input | |
class="inputbox" | |
id="charaIDBox1" | |
type="number" | |
inputmode="numeric" | |
required | |
min="1001" | |
max="9999" | |
value="1001" | |
/> | |
<input | |
class="inputbox" | |
id="charaIDBox2" | |
type="number" | |
inputmode="numeric" | |
required | |
min="1001" | |
max="9999" | |
value="1002" | |
/> | |
<input | |
class="inputbox" | |
id="charaIDBox3" | |
type="number" | |
inputmode="numeric" | |
required | |
min="1001" | |
max="9999" | |
value="1003" | |
/> | |
<br /> | |
<input | |
class="inputbox" | |
id="okeVolBox" | |
type="number" | |
inputmode="decimal" | |
required | |
min="0.0" | |
max="1.0" | |
value="1" | |
/> | |
<input | |
class="inputbox" | |
id="charaVolBox1" | |
type="number" | |
inputmode="decimal" | |
required | |
min="0.0" | |
max="1.0" | |
value="0.8" | |
/> | |
<input | |
class="inputbox" | |
id="charaVolBox2" | |
type="number" | |
inputmode="decimal" | |
required | |
min="0.0" | |
max="1.0" | |
value="0.6" | |
/> | |
<input | |
class="inputbox" | |
id="charaPanBox" | |
type="number" | |
inputmode="decimal" | |
required | |
min="0.0" | |
max="1.0" | |
value="0.35" | |
/> | |
<br /> | |
<textarea id="textbox" style="height: 400px" readonly> | |
Logger出力はここに表示されます</textarea | |
> | |
<br /> | |
<textarea id="urllistbox" style="height: 8em" readonly></textarea> | |
</body> | |
</html> | |