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();
|