87 lines
2.1 KiB
Svelte
87 lines
2.1 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { i18n } from '$lib/i18n';
|
|
|
|
let frequency = 60;
|
|
let playing = false;
|
|
|
|
let audioCtx: AudioContext | undefined;
|
|
let oscillatorL: OscillatorNode | undefined;
|
|
let oscillatorR: OscillatorNode | undefined;
|
|
|
|
onMount(() => {
|
|
audioCtx = new window.AudioContext();
|
|
});
|
|
|
|
function start(mode: 'inPhase' | 'outOfPhase') {
|
|
if (!audioCtx) return;
|
|
oscillatorL?.stop();
|
|
oscillatorR?.stop();
|
|
|
|
oscillatorL = audioCtx.createOscillator();
|
|
oscillatorR = audioCtx.createOscillator();
|
|
const gainNode = audioCtx.createGain();
|
|
|
|
const stereoPannerL = audioCtx.createStereoPanner();
|
|
const stereoPannerR = audioCtx.createStereoPanner();
|
|
|
|
oscillatorL.frequency.setValueAtTime(frequency, audioCtx.currentTime);
|
|
oscillatorR.frequency.setValueAtTime(frequency, audioCtx.currentTime);
|
|
|
|
stereoPannerL.pan.setValueAtTime(-1, audioCtx.currentTime);
|
|
stereoPannerR.pan.setValueAtTime(1, audioCtx.currentTime);
|
|
|
|
oscillatorL.connect(stereoPannerL).connect(audioCtx.destination);
|
|
oscillatorR.connect(gainNode).connect(stereoPannerR).connect(audioCtx.destination);
|
|
|
|
if (mode === 'inPhase') {
|
|
gainNode?.gain.setValueAtTime(1, audioCtx.currentTime); // Normal phase
|
|
} else {
|
|
gainNode?.gain.setValueAtTime(-1, audioCtx.currentTime); // Invert phase
|
|
}
|
|
|
|
oscillatorL?.start();
|
|
oscillatorR?.start();
|
|
playing = true;
|
|
}
|
|
|
|
async function stop() {
|
|
oscillatorL?.stop();
|
|
oscillatorR?.stop();
|
|
playing = false;
|
|
}
|
|
</script>
|
|
|
|
<div class="test">
|
|
<label>
|
|
{$i18n.t('Frequency')}
|
|
<input type="number" bind:value={frequency} min="20" max="20000" disabled={playing} />Hz
|
|
</label>
|
|
<div class="controls">
|
|
<button on:click={() => start('inPhase')}>{$i18n.t('In Phase')}</button>
|
|
<button on:click={() => start('outOfPhase')}>{$i18n.t('Out of Phase')}</button>
|
|
<button class="stop" on:click={stop} disabled={!playing}>{$i18n.t('Stop')}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.test {
|
|
margin: 1em 0;
|
|
}
|
|
|
|
.controls {
|
|
margin-top: 0.5em;
|
|
}
|
|
|
|
.stop {
|
|
margin-left: 1em;
|
|
|
|
&:not(:disabled) {
|
|
background: darkred;
|
|
}
|
|
}
|
|
|
|
input {
|
|
width: 5em;
|
|
}
|
|
</style>
|