Update index.html
Browse files- index.html +78 -53
index.html
CHANGED
@@ -413,11 +413,8 @@
|
|
413 |
</div>
|
414 |
</div>
|
415 |
</div>
|
416 |
-
<script type="module">
|
417 |
-
import
|
418 |
-
window.soundtouch = soundtouch;
|
419 |
-
</script>
|
420 |
-
<script>
|
421 |
|
422 |
document.addEventListener('DOMContentLoaded', function() {
|
423 |
// 要素を取得
|
@@ -697,71 +694,99 @@
|
|
697 |
});
|
698 |
}
|
699 |
|
700 |
-
function playAudio(file, startTime) {
|
701 |
-
|
702 |
|
703 |
-
|
704 |
-
|
705 |
try {
|
706 |
-
|
707 |
} catch(e) {
|
708 |
-
|
709 |
}
|
710 |
-
|
711 |
-
|
712 |
-
const source = audioContext.createBufferSource();
|
713 |
-
source.buffer = audioBuffers[file];
|
714 |
-
|
715 |
-
// SoundTouch プロセッサーを作成
|
716 |
-
soundTouchNodes[file] = new soundtouch.SoundTouch(audioBuffers[file].sampleRate);
|
717 |
-
soundTouchNodes[file].tempo = currentPlaybackRate;
|
718 |
-
soundTouchNodes[file].pitch = 1; // ピッチ変更なし
|
719 |
-
|
720 |
-
// SimpleFilter を作成し、Web Audio Node を取得
|
721 |
-
const soundTouchProcessor = new soundtouch.SimpleFilter(audioContext, source, soundTouchNodes[file]);
|
722 |
-
|
723 |
-
// soundtouch.getWebAudioNode() を使用してノードを取得
|
724 |
-
const soundTouchNode = soundtouch.getWebAudioNode(
|
725 |
-
audioContext,
|
726 |
-
source,
|
727 |
-
soundTouchNodes[file]
|
728 |
-
);
|
729 |
|
730 |
-
|
731 |
-
|
732 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
733 |
|
734 |
-
|
735 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
736 |
|
737 |
-
|
738 |
-
|
739 |
-
|
|
|
740 |
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
|
|
|
|
750 |
|
751 |
-
|
752 |
-
if (playDuration > 0) {
|
753 |
source.start(0, playStartTime, playDuration);
|
754 |
-
|
755 |
|
756 |
-
|
757 |
-
|
758 |
source.loop = true;
|
759 |
source.loopStart = parseFloat(startTimeInput.value) || 0;
|
760 |
source.loopEnd = endTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
761 |
}
|
762 |
|
763 |
-
|
764 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
765 |
|
766 |
// 再生関数
|
767 |
function playMedia() {
|
|
|
413 |
</div>
|
414 |
</div>
|
415 |
</div>
|
416 |
+
<script type="module">
|
417 |
+
import { SoundTouch, SimpleFilter, getWebAudioNode } from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/soundtouch.min.js';
|
|
|
|
|
|
|
418 |
|
419 |
document.addEventListener('DOMContentLoaded', function() {
|
420 |
// 要素を取得
|
|
|
694 |
});
|
695 |
}
|
696 |
|
697 |
+
function playAudio(file, startTime) {
|
698 |
+
if (!audioBuffers[file]) return;
|
699 |
|
700 |
+
// 既存のソースがあれば停止
|
701 |
+
if (audioSources[file]) {
|
702 |
try {
|
703 |
+
audioSources[file].stop();
|
704 |
} catch(e) {
|
705 |
+
console.log("Audio source already stopped");
|
706 |
}
|
707 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
708 |
|
709 |
+
const source = audioContext.createBufferSource();
|
710 |
+
source.buffer = audioBuffers[file];
|
711 |
+
|
712 |
+
// SoundTouch プロセッサーを作成
|
713 |
+
const soundTouch = new SoundTouch(audioBuffers[file].sampleRate);
|
714 |
+
soundTouch.tempo = currentPlaybackRate;
|
715 |
+
soundTouch.pitch = 1;
|
716 |
+
|
717 |
+
// BufferSourceを作成
|
718 |
+
const bufferSource = new soundtouch.BufferSource(audioBuffers[file].getChannelData(0));
|
719 |
+
|
720 |
+
// SimpleFilterを作成
|
721 |
+
const filter = new SimpleFilter(soundTouch, bufferSource);
|
722 |
|
723 |
+
// ScriptProcessorNodeを作成
|
724 |
+
const processor = audioContext.createScriptProcessor(4096, 1, 1);
|
725 |
+
processor.onaudioprocess = (event) => {
|
726 |
+
const outputBuffer = event.outputBuffer.getChannelData(0);
|
727 |
+
const framesExtracted = filter.extract(outputBuffer, outputBuffer.length);
|
728 |
+
if (framesExtracted === 0) {
|
729 |
+
processor.disconnect();
|
730 |
+
}
|
731 |
+
};
|
732 |
|
733 |
+
// ボリュームコントロール
|
734 |
+
const volumeSlider = document.querySelector(`.audio-slider[data-audio="${file}"]`);
|
735 |
+
const volume = parseFloat(volumeSlider.value) * parseFloat(globalVolumeSlider.value);
|
736 |
+
gainNodes[file].gain.value = volume;
|
737 |
|
738 |
+
// 接続チェーン
|
739 |
+
source.connect(processor);
|
740 |
+
processor.connect(gainNodes[file]);
|
741 |
+
gainNodes[file].connect(audioContext.destination);
|
742 |
|
743 |
+
// 再生時間計算
|
744 |
+
const duration = video.duration || videoDuration;
|
745 |
+
const endTime = parseFloat(endTimeInput.value) || duration;
|
746 |
+
const playStartTime = Math.max(startTime, parseFloat(startTimeInput.value) || 0);
|
747 |
+
const playEndTime = Math.min(endTime, duration);
|
748 |
+
const playDuration = playEndTime - playStartTime;
|
749 |
|
750 |
+
if (playDuration > 0) {
|
|
|
751 |
source.start(0, playStartTime, playDuration);
|
752 |
+
}
|
753 |
|
754 |
+
// ループ設定
|
755 |
+
if (loopCheckbox.checked) {
|
756 |
source.loop = true;
|
757 |
source.loopStart = parseFloat(startTimeInput.value) || 0;
|
758 |
source.loopEnd = endTime;
|
759 |
+
}
|
760 |
+
|
761 |
+
audioSources[file] = {
|
762 |
+
source: source,
|
763 |
+
processor: processor,
|
764 |
+
filter: filter
|
765 |
+
};
|
766 |
+
soundTouchNodes[file] = soundTouch;
|
767 |
}
|
768 |
|
769 |
+
// 一時停止関数も修正
|
770 |
+
function pauseMedia() {
|
771 |
+
if (!isPlaying) return;
|
772 |
+
|
773 |
+
video.pause();
|
774 |
+
audioFiles.forEach(file => {
|
775 |
+
if (audioSources[file]) {
|
776 |
+
try {
|
777 |
+
audioSources[file].source.stop();
|
778 |
+
audioSources[file].processor.disconnect();
|
779 |
+
} catch(e) {
|
780 |
+
console.log("Audio source already stopped");
|
781 |
+
}
|
782 |
+
audioSources[file] = null;
|
783 |
+
}
|
784 |
+
});
|
785 |
+
|
786 |
+
isPlaying = false;
|
787 |
+
isVideoPlaying = false;
|
788 |
+
playPauseBtn.textContent = '▶';
|
789 |
+
}
|
790 |
|
791 |
// 再生関数
|
792 |
function playMedia() {
|