Update index.html
Browse files- index.html +82 -51
index.html
CHANGED
|
@@ -444,7 +444,6 @@
|
|
| 444 |
const audioBuffers = {};
|
| 445 |
const audioSources = {};
|
| 446 |
const gainNodes = {};
|
| 447 |
-
const pitchShiftNodes = {};
|
| 448 |
|
| 449 |
// 音声ファイル名の配列
|
| 450 |
const audioFiles = ['p', 'a', 't', 's', 'k'];
|
|
@@ -454,7 +453,6 @@
|
|
| 454 |
let isPlaying = false;
|
| 455 |
let isVideoPlaying = false;
|
| 456 |
let lastVolume = 1;
|
| 457 |
-
let shouldPlayFromStartTime = false;
|
| 458 |
|
| 459 |
// 動画のメタデータが読み込まれたら
|
| 460 |
video.addEventListener('loadedmetadata', function() {
|
|
@@ -485,15 +483,25 @@
|
|
| 485 |
}
|
| 486 |
|
| 487 |
// 動画の時間更新イベント
|
| 488 |
-
video.addEventListener('timeupdate',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
|
| 490 |
// 動画終了時の処理
|
| 491 |
video.addEventListener('ended', function() {
|
| 492 |
-
const startTime = parseFloat(startTimeInput.value) || 0;
|
| 493 |
if (!loopCheckbox.checked) {
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
}
|
| 498 |
});
|
| 499 |
|
|
@@ -552,21 +560,23 @@
|
|
| 552 |
|
| 553 |
// 動画クリックで再生/一時停止
|
| 554 |
video.addEventListener('click', function() {
|
| 555 |
-
|
| 556 |
-
pauseMedia();
|
| 557 |
-
} else {
|
| 558 |
-
playMedia();
|
| 559 |
-
}
|
| 560 |
});
|
| 561 |
|
| 562 |
// 再生/一時停止ボタン
|
| 563 |
-
playPauseBtn.addEventListener('click', function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
if (isVideoPlaying) {
|
| 565 |
pauseMedia();
|
| 566 |
} else {
|
| 567 |
playMedia();
|
| 568 |
}
|
| 569 |
-
}
|
| 570 |
|
| 571 |
// 音量コントロール
|
| 572 |
volumeSlider.addEventListener('input', function() {
|
|
@@ -617,13 +627,14 @@
|
|
| 617 |
updatePlaybackRate(speed);
|
| 618 |
});
|
| 619 |
|
| 620 |
-
// 再生速度を更新 (
|
| 621 |
function updatePlaybackRate(speed) {
|
| 622 |
video.playbackRate = speed;
|
| 623 |
|
| 624 |
-
// 音声の再生速度も更新
|
| 625 |
audioFiles.forEach(file => {
|
| 626 |
if (audioSources[file]) {
|
|
|
|
| 627 |
audioSources[file].playbackRate.value = speed;
|
| 628 |
}
|
| 629 |
});
|
|
@@ -675,6 +686,15 @@
|
|
| 675 |
function playAudio(file, startTime) {
|
| 676 |
if (!audioBuffers[file]) return;
|
| 677 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
const source = audioContext.createBufferSource();
|
| 679 |
source.buffer = audioBuffers[file];
|
| 680 |
|
|
@@ -690,26 +710,28 @@
|
|
| 690 |
gainNodes[file].connect(audioContext.destination);
|
| 691 |
|
| 692 |
// 再生
|
| 693 |
-
const currentTime = video.currentTime;
|
| 694 |
const duration = video.duration || videoDuration;
|
| 695 |
const endTime = parseFloat(endTimeInput.value) || duration;
|
| 696 |
const loop = loopCheckbox.checked;
|
| 697 |
|
| 698 |
// 再生範囲を計算
|
| 699 |
const playStartTime = Math.max(startTime, parseFloat(startTimeInput.value) || 0);
|
| 700 |
-
const
|
|
|
|
| 701 |
|
| 702 |
-
|
|
|
|
|
|
|
|
|
|
| 703 |
|
| 704 |
// ループ設定
|
| 705 |
-
source.loop = loop;
|
| 706 |
if (loop) {
|
| 707 |
-
|
| 708 |
-
source.loopStart =
|
| 709 |
source.loopEnd = endTime;
|
| 710 |
}
|
| 711 |
|
| 712 |
-
// 再生速度を同期
|
| 713 |
source.playbackRate.value = video.playbackRate;
|
| 714 |
|
| 715 |
audioSources[file] = source;
|
|
@@ -717,33 +739,29 @@
|
|
| 717 |
|
| 718 |
// 再生関数
|
| 719 |
function playMedia() {
|
| 720 |
-
const startTime =
|
| 721 |
-
|
| 722 |
-
shouldPlayFromStartTime = false;
|
| 723 |
|
| 724 |
// 終了時間が動画の長さを超えないように
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
// 動画を設定
|
| 728 |
-
video.currentTime = startTime;
|
| 729 |
-
video.muted = true;
|
| 730 |
|
| 731 |
-
//
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
if (audioSources[file]) {
|
| 736 |
-
audioSources[file].stop();
|
| 737 |
-
}
|
| 738 |
-
playAudio(file, startTime);
|
| 739 |
-
}
|
| 740 |
-
});
|
| 741 |
|
| 742 |
// 動画を再生
|
| 743 |
video.play();
|
| 744 |
isPlaying = true;
|
| 745 |
isVideoPlaying = true;
|
| 746 |
playPauseBtn.textContent = '⏸';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 747 |
}
|
| 748 |
|
| 749 |
// 一時停止関数
|
|
@@ -753,7 +771,11 @@
|
|
| 753 |
video.pause();
|
| 754 |
audioFiles.forEach(file => {
|
| 755 |
if (audioSources[file]) {
|
| 756 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 757 |
audioSources[file] = null;
|
| 758 |
}
|
| 759 |
});
|
|
@@ -761,15 +783,6 @@
|
|
| 761 |
isPlaying = false;
|
| 762 |
isVideoPlaying = false;
|
| 763 |
playPauseBtn.textContent = '▶';
|
| 764 |
-
shouldPlayFromStartTime = true;
|
| 765 |
-
}
|
| 766 |
-
|
| 767 |
-
// 停止関数
|
| 768 |
-
function stopMedia() {
|
| 769 |
-
pauseMedia();
|
| 770 |
-
const startTime = parseFloat(startTimeInput.value) || 0;
|
| 771 |
-
video.currentTime = startTime;
|
| 772 |
-
updateTimeDisplay();
|
| 773 |
}
|
| 774 |
|
| 775 |
// ボリュームスライダーのイベント
|
|
@@ -801,6 +814,24 @@
|
|
| 801 |
}
|
| 802 |
});
|
| 803 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 804 |
// 初期化
|
| 805 |
loadAudioFiles();
|
| 806 |
updateVolumeIcon();
|
|
|
|
| 444 |
const audioBuffers = {};
|
| 445 |
const audioSources = {};
|
| 446 |
const gainNodes = {};
|
|
|
|
| 447 |
|
| 448 |
// 音声ファイル名の配列
|
| 449 |
const audioFiles = ['p', 'a', 't', 's', 'k'];
|
|
|
|
| 453 |
let isPlaying = false;
|
| 454 |
let isVideoPlaying = false;
|
| 455 |
let lastVolume = 1;
|
|
|
|
| 456 |
|
| 457 |
// 動画のメタデータが読み込まれたら
|
| 458 |
video.addEventListener('loadedmetadata', function() {
|
|
|
|
| 483 |
}
|
| 484 |
|
| 485 |
// 動画の時間更新イベント
|
| 486 |
+
video.addEventListener('timeupdate', function() {
|
| 487 |
+
updateTimeDisplay();
|
| 488 |
+
|
| 489 |
+
// ループ再生の場合、終了時間をチェック
|
| 490 |
+
if (loopCheckbox.checked) {
|
| 491 |
+
const endTime = parseFloat(endTimeInput.value) || videoDuration;
|
| 492 |
+
if (video.currentTime >= endTime) {
|
| 493 |
+
const startTime = parseFloat(startTimeInput.value) || 0;
|
| 494 |
+
seekMedia(startTime);
|
| 495 |
+
}
|
| 496 |
+
}
|
| 497 |
+
});
|
| 498 |
|
| 499 |
// 動画終了時の処理
|
| 500 |
video.addEventListener('ended', function() {
|
|
|
|
| 501 |
if (!loopCheckbox.checked) {
|
| 502 |
+
const startTime = parseFloat(startTimeInput.value) || 0;
|
| 503 |
+
seekMedia(startTime);
|
| 504 |
+
pauseMedia();
|
| 505 |
}
|
| 506 |
});
|
| 507 |
|
|
|
|
| 560 |
|
| 561 |
// 動画クリックで再生/一時停止
|
| 562 |
video.addEventListener('click', function() {
|
| 563 |
+
togglePlayPause();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
});
|
| 565 |
|
| 566 |
// 再生/一時停止ボタン
|
| 567 |
+
playPauseBtn.addEventListener('click', function(e) {
|
| 568 |
+
e.stopPropagation(); // 動画クリックとのバブリングを防止
|
| 569 |
+
togglePlayPause();
|
| 570 |
+
});
|
| 571 |
+
|
| 572 |
+
// 再生/一時停止をトグル
|
| 573 |
+
function togglePlayPause() {
|
| 574 |
if (isVideoPlaying) {
|
| 575 |
pauseMedia();
|
| 576 |
} else {
|
| 577 |
playMedia();
|
| 578 |
}
|
| 579 |
+
}
|
| 580 |
|
| 581 |
// 音量コントロール
|
| 582 |
volumeSlider.addEventListener('input', function() {
|
|
|
|
| 627 |
updatePlaybackRate(speed);
|
| 628 |
});
|
| 629 |
|
| 630 |
+
// 再生速度を更新 (音の高さは変更しない)
|
| 631 |
function updatePlaybackRate(speed) {
|
| 632 |
video.playbackRate = speed;
|
| 633 |
|
| 634 |
+
// 音声の再生速度も更新
|
| 635 |
audioFiles.forEach(file => {
|
| 636 |
if (audioSources[file]) {
|
| 637 |
+
// 音声の再生速度を変更しても音の高さは変わらない
|
| 638 |
audioSources[file].playbackRate.value = speed;
|
| 639 |
}
|
| 640 |
});
|
|
|
|
| 686 |
function playAudio(file, startTime) {
|
| 687 |
if (!audioBuffers[file]) return;
|
| 688 |
|
| 689 |
+
// 既存のソースがあれば停止
|
| 690 |
+
if (audioSources[file]) {
|
| 691 |
+
try {
|
| 692 |
+
audioSources[file].stop();
|
| 693 |
+
} catch(e) {
|
| 694 |
+
console.log("Audio source already stopped");
|
| 695 |
+
}
|
| 696 |
+
}
|
| 697 |
+
|
| 698 |
const source = audioContext.createBufferSource();
|
| 699 |
source.buffer = audioBuffers[file];
|
| 700 |
|
|
|
|
| 710 |
gainNodes[file].connect(audioContext.destination);
|
| 711 |
|
| 712 |
// 再生
|
|
|
|
| 713 |
const duration = video.duration || videoDuration;
|
| 714 |
const endTime = parseFloat(endTimeInput.value) || duration;
|
| 715 |
const loop = loopCheckbox.checked;
|
| 716 |
|
| 717 |
// 再生範囲を計算
|
| 718 |
const playStartTime = Math.max(startTime, parseFloat(startTimeInput.value) || 0);
|
| 719 |
+
const playEndTime = Math.min(endTime, duration);
|
| 720 |
+
const playDuration = playEndTime - playStartTime;
|
| 721 |
|
| 722 |
+
// 再生時間が正の場合のみ再生
|
| 723 |
+
if (playDuration > 0) {
|
| 724 |
+
source.start(0, playStartTime, playDuration);
|
| 725 |
+
}
|
| 726 |
|
| 727 |
// ループ設定
|
|
|
|
| 728 |
if (loop) {
|
| 729 |
+
source.loop = true;
|
| 730 |
+
source.loopStart = parseFloat(startTimeInput.value) || 0;
|
| 731 |
source.loopEnd = endTime;
|
| 732 |
}
|
| 733 |
|
| 734 |
+
// 再生速度を同期
|
| 735 |
source.playbackRate.value = video.playbackRate;
|
| 736 |
|
| 737 |
audioSources[file] = source;
|
|
|
|
| 739 |
|
| 740 |
// 再生関数
|
| 741 |
function playMedia() {
|
| 742 |
+
const startTime = video.currentTime;
|
| 743 |
+
const endTime = parseFloat(endTimeInput.value) || videoDuration;
|
|
|
|
| 744 |
|
| 745 |
// 終了時間が動画の長さを超えないように
|
| 746 |
+
const actualEndTime = Math.min(endTime, videoDuration);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 747 |
|
| 748 |
+
// 現在位置が終了時間を超えている場合、開始位置に戻る
|
| 749 |
+
if (startTime >= actualEndTime) {
|
| 750 |
+
video.currentTime = parseFloat(startTimeInput.value) || 0;
|
| 751 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
|
| 753 |
// 動画を再生
|
| 754 |
video.play();
|
| 755 |
isPlaying = true;
|
| 756 |
isVideoPlaying = true;
|
| 757 |
playPauseBtn.textContent = '⏸';
|
| 758 |
+
|
| 759 |
+
// 音声を再生
|
| 760 |
+
audioFiles.forEach(file => {
|
| 761 |
+
if (audioBuffers[file]) {
|
| 762 |
+
playAudio(file, video.currentTime);
|
| 763 |
+
}
|
| 764 |
+
});
|
| 765 |
}
|
| 766 |
|
| 767 |
// 一時停止関数
|
|
|
|
| 771 |
video.pause();
|
| 772 |
audioFiles.forEach(file => {
|
| 773 |
if (audioSources[file]) {
|
| 774 |
+
try {
|
| 775 |
+
audioSources[file].stop();
|
| 776 |
+
} catch(e) {
|
| 777 |
+
console.log("Audio source already stopped");
|
| 778 |
+
}
|
| 779 |
audioSources[file] = null;
|
| 780 |
}
|
| 781 |
});
|
|
|
|
| 783 |
isPlaying = false;
|
| 784 |
isVideoPlaying = false;
|
| 785 |
playPauseBtn.textContent = '▶';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
}
|
| 787 |
|
| 788 |
// ボリュームスライダーのイベント
|
|
|
|
| 814 |
}
|
| 815 |
});
|
| 816 |
|
| 817 |
+
// ループ設定変更時
|
| 818 |
+
loopCheckbox.addEventListener('change', function() {
|
| 819 |
+
if (isPlaying) {
|
| 820 |
+
// 再生中の場合は音声を再起動
|
| 821 |
+
const currentTime = video.currentTime;
|
| 822 |
+
audioFiles.forEach(file => {
|
| 823 |
+
if (audioSources[file]) {
|
| 824 |
+
try {
|
| 825 |
+
audioSources[file].stop();
|
| 826 |
+
} catch(e) {
|
| 827 |
+
console.log("Audio source already stopped");
|
| 828 |
+
}
|
| 829 |
+
playAudio(file, currentTime);
|
| 830 |
+
}
|
| 831 |
+
});
|
| 832 |
+
}
|
| 833 |
+
});
|
| 834 |
+
|
| 835 |
// 初期化
|
| 836 |
loadAudioFiles();
|
| 837 |
updateVolumeIcon();
|