Update index.html
Browse files- index.html +19 -8
index.html
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>高度な音声動画プレイヤー</title>
|
|
|
|
| 7 |
<style>
|
| 8 |
body {
|
| 9 |
font-family: 'Arial', sans-serif;
|
|
@@ -444,6 +445,7 @@
|
|
| 444 |
const audioBuffers = {};
|
| 445 |
const audioSources = {};
|
| 446 |
const gainNodes = {};
|
|
|
|
| 447 |
|
| 448 |
// 音声ファイル名の配列
|
| 449 |
const audioFiles = ['p', 'a', 't', 's', 'k'];
|
|
@@ -453,6 +455,7 @@
|
|
| 453 |
let isPlaying = false;
|
| 454 |
let isVideoPlaying = false;
|
| 455 |
let lastVolume = 1;
|
|
|
|
| 456 |
|
| 457 |
// 動画のメタデータが読み込まれたら
|
| 458 |
video.addEventListener('loadedmetadata', function() {
|
|
@@ -627,15 +630,15 @@
|
|
| 627 |
updatePlaybackRate(speed);
|
| 628 |
});
|
| 629 |
|
| 630 |
-
// 再生速度を更新 (
|
| 631 |
function updatePlaybackRate(speed) {
|
|
|
|
| 632 |
video.playbackRate = speed;
|
| 633 |
|
| 634 |
-
//
|
| 635 |
audioFiles.forEach(file => {
|
| 636 |
-
if (
|
| 637 |
-
|
| 638 |
-
audioSources[file].playbackRate.value = speed;
|
| 639 |
}
|
| 640 |
});
|
| 641 |
}
|
|
@@ -677,6 +680,11 @@
|
|
| 677 |
audioBuffers[file] = audioBuffer;
|
| 678 |
gainNodes[file] = audioContext.createGain();
|
| 679 |
gainNodes[file].gain.value = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
})
|
| 681 |
.catch(error => console.error(`Error loading ${file}.mp3:`, error));
|
| 682 |
});
|
|
@@ -695,9 +703,12 @@
|
|
| 695 |
}
|
| 696 |
}
|
| 697 |
|
|
|
|
| 698 |
const source = audioContext.createBufferSource();
|
| 699 |
source.buffer = audioBuffers[file];
|
| 700 |
|
|
|
|
|
|
|
| 701 |
// ボリュームスライダーの値を取得
|
| 702 |
const volumeSlider = document.querySelector(`.audio-slider[data-audio="${file}"]`);
|
| 703 |
const volume = parseFloat(volumeSlider.value) * parseFloat(globalVolumeSlider.value);
|
|
@@ -706,7 +717,7 @@
|
|
| 706 |
gainNodes[file].gain.value = volume;
|
| 707 |
|
| 708 |
// 接続
|
| 709 |
-
|
| 710 |
gainNodes[file].connect(audioContext.destination);
|
| 711 |
|
| 712 |
// 再生
|
|
@@ -732,7 +743,7 @@
|
|
| 732 |
}
|
| 733 |
|
| 734 |
// 再生速度を同期
|
| 735 |
-
|
| 736 |
|
| 737 |
audioSources[file] = source;
|
| 738 |
}
|
|
@@ -791,7 +802,7 @@
|
|
| 791 |
const value = parseFloat(this.value);
|
| 792 |
volumeValues[index].textContent = value.toFixed(2);
|
| 793 |
|
| 794 |
-
if (isPlaying &&
|
| 795 |
const globalVolume = parseFloat(globalVolumeSlider.value);
|
| 796 |
gainNodes[this.dataset.audio].gain.value = value * globalVolume;
|
| 797 |
}
|
|
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>高度な音声動画プレイヤー</title>
|
| 7 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/soundtouch.min.js"></script>
|
| 8 |
<style>
|
| 9 |
body {
|
| 10 |
font-family: 'Arial', sans-serif;
|
|
|
|
| 445 |
const audioBuffers = {};
|
| 446 |
const audioSources = {};
|
| 447 |
const gainNodes = {};
|
| 448 |
+
const soundTouchNodes = {};
|
| 449 |
|
| 450 |
// 音声ファイル名の配列
|
| 451 |
const audioFiles = ['p', 'a', 't', 's', 'k'];
|
|
|
|
| 455 |
let isPlaying = false;
|
| 456 |
let isVideoPlaying = false;
|
| 457 |
let lastVolume = 1;
|
| 458 |
+
let currentPlaybackRate = 1;
|
| 459 |
|
| 460 |
// 動画のメタデータが読み込まれたら
|
| 461 |
video.addEventListener('loadedmetadata', function() {
|
|
|
|
| 630 |
updatePlaybackRate(speed);
|
| 631 |
});
|
| 632 |
|
| 633 |
+
// 再生速度を更新 (音の高さを保持)
|
| 634 |
function updatePlaybackRate(speed) {
|
| 635 |
+
currentPlaybackRate = speed;
|
| 636 |
video.playbackRate = speed;
|
| 637 |
|
| 638 |
+
// 音声の再生速度を変更 (SoundTouchJSが音の高さを保持)
|
| 639 |
audioFiles.forEach(file => {
|
| 640 |
+
if (soundTouchNodes[file]) {
|
| 641 |
+
soundTouchNodes[file].tempo = speed;
|
|
|
|
| 642 |
}
|
| 643 |
});
|
| 644 |
}
|
|
|
|
| 680 |
audioBuffers[file] = audioBuffer;
|
| 681 |
gainNodes[file] = audioContext.createGain();
|
| 682 |
gainNodes[file].gain.value = 1;
|
| 683 |
+
|
| 684 |
+
// SoundTouchノードを作成
|
| 685 |
+
soundTouchNodes[file] = new soundtouch.SoundTouch(audioBuffer.sampleRate);
|
| 686 |
+
soundTouchNodes[file].tempo = currentPlaybackRate;
|
| 687 |
+
soundTouchNodes[file].pitch = 1; // ピッチ変更なし
|
| 688 |
})
|
| 689 |
.catch(error => console.error(`Error loading ${file}.mp3:`, error));
|
| 690 |
});
|
|
|
|
| 703 |
}
|
| 704 |
}
|
| 705 |
|
| 706 |
+
// SoundTouchプロセッサーを作成
|
| 707 |
const source = audioContext.createBufferSource();
|
| 708 |
source.buffer = audioBuffers[file];
|
| 709 |
|
| 710 |
+
const soundTouchProcessor = new soundtouch.SimpleFilter(audioContext, source, soundTouchNodes[file]);
|
| 711 |
+
|
| 712 |
// ボリュームスライダーの値を取得
|
| 713 |
const volumeSlider = document.querySelector(`.audio-slider[data-audio="${file}"]`);
|
| 714 |
const volume = parseFloat(volumeSlider.value) * parseFloat(globalVolumeSlider.value);
|
|
|
|
| 717 |
gainNodes[file].gain.value = volume;
|
| 718 |
|
| 719 |
// 接続
|
| 720 |
+
soundTouchProcessor.connect(gainNodes[file]);
|
| 721 |
gainNodes[file].connect(audioContext.destination);
|
| 722 |
|
| 723 |
// 再生
|
|
|
|
| 743 |
}
|
| 744 |
|
| 745 |
// 再生速度を同期
|
| 746 |
+
soundTouchNodes[file].tempo = currentPlaybackRate;
|
| 747 |
|
| 748 |
audioSources[file] = source;
|
| 749 |
}
|
|
|
|
| 802 |
const value = parseFloat(this.value);
|
| 803 |
volumeValues[index].textContent = value.toFixed(2);
|
| 804 |
|
| 805 |
+
if (isPlaying && gainNodes[this.dataset.audio]) {
|
| 806 |
const globalVolume = parseFloat(globalVolumeSlider.value);
|
| 807 |
gainNodes[this.dataset.audio].gain.value = value * globalVolume;
|
| 808 |
}
|