diff --git "a/index.html" "b/index.html"
--- "a/index.html"
+++ "b/index.html"
@@ -1,56 +1,64 @@
-
-
サービスワーカー設定
-
-
下記のボタンを押すと、サービスワーカーを使用してページをオフラインで使用できるようにします。
- これはテスト的な機能で、ページの更新などができなくなるバグが発生する可能性があります。
(更新機能は実装していますが安定して動作するとは限りません。)
- ページが更新されない場合、動画の更新ができなかったり既存のバグが残り続けたりする可能性があります。
-
-
-
-
-
+
+
+
+
+
+
文化発表会動画プレイヤー
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
文化発表会音声動画プレイヤー
+
+
+
+
音声コントロール
+
各パートの音の大きさを設定できます、自分のパートの音量を1、それ以外のパートの音量を0.4くらいにすると他のパートのタイミングを確認しながら練習できます。
+ ※「ピアノ」と「全体」は他のパートと音がずれるため、組み合わせて使用しないでください。
+
+
+
+ 1.00
+
+
+
+
+ 1.00
+
+
+
+
+ 1.00
+
+
+
+
+ 0.00
+
+
+
+
+ 0.00
+
+
+
+
+
+
+
+
+
+
+
プレビュー
+
合成された音声をプレビューできます。再生ボタンをクリックして確認してください。
+
+
00:00 / 00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
音声の合成が完了していません
+
上の音声コントロールで各パートの音量を調整し、「合成」ボタンを押してください
+
+
+
+
+
+
+
+
BPM
+
+
+
+
+
+
+
+
+
+
00:00.00 / 00:00.00
+
+
+
+
+
+ 1.00x
+
+
+
+
+
+
+
+
+
+
+
+ このボックスをドラッグして「再生開始秒数」や「再生終了秒数」の入力ボックスの上で離すと秒数を簡単に変更できます。各秒数は楽譜に合わせています。
+ 時の旅人はAからE、地球星歌は①から⑤で区切っていて、地球星歌はリピート記号があるので①と②は1番と2番があります。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
設定
+
+
+
+
+
+
+
+
+
+
+ 1.00x
+
+
↑練習したいテンポを入力するとそのテンポに合わせて再生速度スライダーを変更できます。
+
楽譜に記載された「♪=」の横のテンポ(地球星歌は約92、地球星歌は約66)を基準に計算します。
+
+
+
+
+
+
- // 新しいaudio要素を作成
- window.combinedAudioElement = new Audio(url);
- window.combinedAudioElement.preservesPitch = true;
- window.combinedAudioElement.mozPreservesPitch = true;
- window.combinedAudioElement.webkitPreservesPitch = true;
- window.combinedAudioElement.playbackRate = currentPlaybackRate;
-
- // 合成完了メッセージ
- combineStatus.textContent = "音声の合成が完了しました";
- isAudioCombined = true;
- enablePlayerControls();
-
- // メトロノームの状態を復元
- if (wasMetronomeActive) {
- setTimeout(() => {
- setMetronomeState(true, metronomeState.bpm);
- }, 500);
- }
- } catch (error) {
- console.error('音声合成エラー:', error);
- combineStatus.textContent = `音声の合成に失敗しました: ${error.message}`;
-
- // エラー時もメトロノームの状態を復元
- if (wasMetronomeActive) {
- setMetronomeState(true, metronomeState.bpm);
- }
- } finally {
- combineButton.disabled = false;
- }
-}
-
-// AudioBufferをWAV Blobに変換
-function bufferToWave(abuffer, audioContext) {
- const numOfChan = abuffer.numberOfChannels;
- const length = abuffer.length * numOfChan * 2 + 44;
- const buffer = new ArrayBuffer(length);
- const view = new DataView(buffer);
- let pos = 0;
-
- // WAVヘッダー書き込み
- setUint32(0x46464952); // "RIFF"
- setUint32(length - 8); // ファイル長 - 8
- setUint32(0x45564157); // "WAVE"
- setUint32(0x20746d66); // "fmt "チャンク
- setUint32(16); // 長さ = 16
- setUint16(1); // PCM (非圧縮)
- setUint16(numOfChan);
- setUint32(audioContext.sampleRate);
- setUint32(audioContext.sampleRate * 2 * numOfChan);
- setUint16(numOfChan * 2);
- setUint16(16);
- setUint32(0x61746164); // "data"チャンク
- setUint32(length - pos - 4);
-
- // インターリーブデータ書き込み
- for (let i = 0; i < abuffer.length; i++) {
- for (let channel = 0; channel < numOfChan; channel++) {
- let sample = abuffer.getChannelData(channel)[i] * 0x7fff;
- sample = Math.max(-32768, Math.min(32767, sample));
- view.setInt16(pos, sample, true);
- pos += 2;
- }
- }
-
- function setUint16(data) {
- view.setUint16(pos, data, true);
- pos += 2;
- }
-
- function setUint32(data) {
- view.setUint32(pos, data, true);
- pos += 4;
- }
-
- return new Blob([buffer], { type: 'audio/wav' });
-}
-
-// ドラッグ可能化
-const app = document.getElementById('app');
-new Draggabilly(app, { containment: 'body' });
-
-// 開閉ボタン
-const header = document.getElementById('header');
-const content = document.getElementById('content');
-let isOpen = false;
-
-// 初期コンパクト状態へアニメーション
-gsap.set(app, { width: 60, height: 40, padding: 0 });
-content.style.display = 'none';
-header.textContent = '▼';
-
-header.addEventListener('click', () => {
- if (isOpen) {
- // 閉じる
- gsap.to(app, {
- duration: 0.4,
- width: 60,
- height: 40,
- padding: 0,
- ease: 'power3.inOut',
- onComplete: () => {
- content.style.display = 'none';
- header.textContent = '▼';
- }
- });
- } else {
- // 開く
- content.style.display = 'flex';
- gsap.to(app, {
- duration: 0.4,
- width: 300,
- height: 400,
- padding: 10,
- ease: 'power3.inOut',
- onComplete: () => {
- header.textContent = '▲';
- }
- });
- }
- isOpen = !isOpen;
-});
-});
-
-
-