Compare commits
5 commits
9ae4b740e1
...
1dfcadc8a3
Author | SHA1 | Date | |
---|---|---|---|
1dfcadc8a3 | |||
1c2e89bb79 | |||
ba6e54d7d5 | |||
7a7dcd3662 | |||
1510c9661d |
34 changed files with 431 additions and 180 deletions
|
@ -43,9 +43,11 @@
|
|||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@tabler/icons-webfont": "^2.47.0",
|
||||
"debug": "^4.3.4",
|
||||
"i18next": "^23.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"normalize.css": "^8.0.1",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-i18next": "^2.2.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3"
|
||||
|
|
|
@ -26,6 +26,9 @@ dependencies:
|
|||
debug:
|
||||
specifier: ^4.3.4
|
||||
version: 4.3.4
|
||||
i18next:
|
||||
specifier: ^23.10.0
|
||||
version: 23.10.0
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
|
@ -35,6 +38,9 @@ dependencies:
|
|||
svelte:
|
||||
specifier: ^4.2.7
|
||||
version: 4.2.9
|
||||
svelte-i18next:
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2(i18next@23.10.0)(svelte@4.2.9)
|
||||
tslib:
|
||||
specifier: ^2.4.1
|
||||
version: 2.6.2
|
||||
|
@ -139,7 +145,6 @@ packages:
|
|||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
dev: true
|
||||
|
||||
/@esbuild/aix-ppc64@0.19.12:
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||
|
@ -1802,6 +1807,12 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/i18next@23.10.0:
|
||||
resolution: {integrity: sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.9
|
||||
dev: false
|
||||
|
||||
/ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: true
|
||||
|
@ -2396,7 +2407,6 @@ packages:
|
|||
|
||||
/regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
|
@ -2683,6 +2693,16 @@ packages:
|
|||
svelte: 4.2.9
|
||||
dev: false
|
||||
|
||||
/svelte-i18next@2.2.2(i18next@23.10.0)(svelte@4.2.9):
|
||||
resolution: {integrity: sha512-IpJDZCH5cCgKfHQHgiLmGT4j9HCdg4fqsP3oP2deLu8PxmNj0Ui6khMiDoxAxedAiYEhr0xendv2xqh3Rq+uQQ==}
|
||||
peerDependencies:
|
||||
i18next: '*'
|
||||
svelte: '*'
|
||||
dependencies:
|
||||
i18next: 23.10.0
|
||||
svelte: 4.2.9
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess@5.1.3(postcss@8.4.33)(svelte@4.2.9)(typescript@5.3.3):
|
||||
resolution: {integrity: sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==}
|
||||
engines: {node: '>= 16.0.0', pnpm: ^8.0.0}
|
||||
|
|
|
@ -38,6 +38,23 @@ button, .button {
|
|||
|
||||
background: black;
|
||||
color: white;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
border-radius: 4px;
|
||||
padding: 0.2em;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { i18n } from '$lib/i18n';
|
||||
|
||||
let screenResolution = '... x ...';
|
||||
let windowResolution = '';
|
||||
|
@ -24,11 +25,11 @@
|
|||
|
||||
<div class="info">
|
||||
<div class="resolution">
|
||||
<div class="title">Screen Resolution</div>
|
||||
<div class="title">{$i18n.t('Screen Resolution')}</div>
|
||||
<div class="value">{screenResolution}</div>
|
||||
{#if windowResolution && windowResolution !== screenResolution}
|
||||
<div class="window" transition:fade>
|
||||
<div class="title">Window Resolution</div>
|
||||
<div class="title">{$i18n.t('Window Resolution')}</div>
|
||||
<div class="value">{windowResolution}</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<script>
|
||||
import { IconSpiral } from '@tabler/icons-svelte';
|
||||
export let size = 32;
|
||||
</script>
|
||||
|
||||
<div class="spinner"><IconSpiral {size} class="spinner-icon" /></div>
|
||||
|
||||
<style>
|
||||
.spinner {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:global(.spinner-icon) {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -7,7 +7,7 @@
|
|||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher<{ focus: void }>();
|
||||
|
||||
export let full = false;
|
||||
export let bg = false;
|
||||
|
||||
let sizes = {
|
||||
blockSize: 64,
|
||||
|
@ -28,7 +28,7 @@
|
|||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="test-card"
|
||||
class:full
|
||||
class:bg
|
||||
style="--block-size: {sizes.blockSize}px;
|
||||
--horizontal-margin: {sizes.horizontalMargin}px;
|
||||
--vertical-margin: {sizes.verticalMargin}px;
|
||||
|
@ -38,7 +38,7 @@
|
|||
--left-column: {leftColumn};"
|
||||
on:dblclick={() => dispatch('focus') && document.body.requestFullscreen()}
|
||||
>
|
||||
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={!full} />
|
||||
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} />
|
||||
|
||||
<div class="axes">
|
||||
<Axes />
|
||||
|
@ -141,7 +141,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.test-card:not(.full) {
|
||||
.test-card.bg {
|
||||
& .info,
|
||||
& .column,
|
||||
& .axes,
|
||||
|
|
8
src/lib/i18n.ts
Normal file
8
src/lib/i18n.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import i18next from 'i18next';
|
||||
import { createI18nStore } from 'svelte-i18next';
|
||||
|
||||
i18next.init({
|
||||
lng: 'en'
|
||||
});
|
||||
|
||||
export const i18n = createI18nStore(i18next);
|
|
@ -1,7 +1,9 @@
|
|||
<script>
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<a href="/" class="hide-idle"><i class="ti ti-arrow-back"></i> Back</a>
|
||||
<a href="/" class="hide-idle"><i class="ti ti-arrow-back"></i> {$i18n.t('Back')}</a>
|
||||
<slot />
|
||||
|
||||
<style>
|
||||
a {
|
||||
|
@ -21,5 +23,7 @@
|
|||
align-items: center;
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
5
src/routes/(fullscreen)/card/+page.svelte
Normal file
5
src/routes/(fullscreen)/card/+page.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import TestCard from '$lib/TestCard.svelte';
|
||||
</script>
|
||||
|
||||
<TestCard />
|
116
src/routes/(fullscreen)/mouse/+page.svelte
Normal file
116
src/routes/(fullscreen)/mouse/+page.svelte
Normal file
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts">
|
||||
import TestCard from '$lib/TestCard.svelte';
|
||||
|
||||
let x = -1;
|
||||
let y = -1;
|
||||
let leftButton = false;
|
||||
let rightButton = false;
|
||||
|
||||
function onMouseMove(ev: MouseEvent) {
|
||||
x = ev.x;
|
||||
y = ev.y;
|
||||
}
|
||||
|
||||
function onMouseDown(ev: MouseEvent) {
|
||||
if (ev.button === 0) {
|
||||
leftButton = true;
|
||||
} else if (ev.button === 2) {
|
||||
rightButton = true;
|
||||
}
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function onMouseUp(ev: MouseEvent) {
|
||||
if (ev.button === 0) {
|
||||
leftButton = false;
|
||||
} else if (ev.button === 2) {
|
||||
rightButton = false;
|
||||
}
|
||||
ev.preventDefault();
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:body
|
||||
on:mousemove={onMouseMove}
|
||||
on:mousedown={onMouseDown}
|
||||
on:mouseup={onMouseUp}
|
||||
on:contextmenu={(ev) => {
|
||||
ev.preventDefault();
|
||||
return false;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="background">
|
||||
<TestCard bg />
|
||||
</div>
|
||||
|
||||
<div class="indicator" style="--x: {x}px; --y: {y}px">
|
||||
<div class="x"></div>
|
||||
<div class="y"></div>
|
||||
<div class="click left" class:pressed={leftButton}></div>
|
||||
<div class="click right" class:pressed={rightButton}></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.background {
|
||||
opacity: 0.33;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
& .x,
|
||||
& .y {
|
||||
position: absolute;
|
||||
background: white;
|
||||
}
|
||||
|
||||
& .x {
|
||||
height: 100vh;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
left: var(--x);
|
||||
}
|
||||
|
||||
& .y {
|
||||
height: 1px;
|
||||
width: 100vw;
|
||||
top: var(--y);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
& .click {
|
||||
position: absolute;
|
||||
top: var(--y);
|
||||
left: var(--x);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
&.left {
|
||||
background: red;
|
||||
}
|
||||
|
||||
&.right {
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
opacity: 0.5;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
}
|
||||
</style>
|
50
src/routes/(pages)/+layout.svelte
Normal file
50
src/routes/(pages)/+layout.svelte
Normal file
|
@ -0,0 +1,50 @@
|
|||
<script lang="ts">
|
||||
import TestCard from '$lib/TestCard.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<TestCard bg on:focus={() => goto('/card')} />
|
||||
<main class:sub={!$page.data.root}>
|
||||
<a href=".." class="button button-back"><i class="ti ti-arrow-back" />{$i18n.t('Back')}</a>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid white;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main.sub {
|
||||
height: 90vh;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
.button-back {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
|
||||
opacity: 0.66;
|
||||
transition: opacity 0.3s;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
main:not(.sub) .button-back {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -1,46 +1,47 @@
|
|||
<script>
|
||||
import { version } from '../../package.json';
|
||||
import { version } from '../../../package.json';
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<h1>Universal Test Card</h1>
|
||||
<h1>Total Tech Test</h1>
|
||||
|
||||
<div class="options">
|
||||
<a href="card">
|
||||
<i class="ti ti-device-desktop"></i>
|
||||
Screen
|
||||
{$i18n.t('Screen')}
|
||||
</a>
|
||||
<a href="audio">
|
||||
<i class="ti ti-volume"></i>
|
||||
Audio
|
||||
{$i18n.t('Audio')}
|
||||
</a>
|
||||
<a href="av-sync">
|
||||
<i class="ti ti-time-duration-off"></i>
|
||||
AV Sync
|
||||
{$i18n.t('AV Sync')}
|
||||
</a>
|
||||
<a href="keyboard">
|
||||
<i class="ti ti-keyboard"></i>
|
||||
Keyboard
|
||||
{$i18n.t('Keyboard')}
|
||||
</a>
|
||||
<a href="mouse" class="disabled">
|
||||
<a href="mouse">
|
||||
<i class="ti ti-mouse"></i>
|
||||
Mouse
|
||||
{$i18n.t('Mouse')}
|
||||
</a>
|
||||
<a href="gamepad">
|
||||
<i class="ti ti-device-gamepad"></i>
|
||||
Gamepad
|
||||
{$i18n.t('Gamepad')}
|
||||
</a>
|
||||
<a href="camera">
|
||||
<i class="ti ti-camera"></i>
|
||||
Camera
|
||||
{$i18n.t('Camera')}
|
||||
</a>
|
||||
<a href="microphone" class="disabled">
|
||||
<i class="ti ti-microphone"></i>
|
||||
Microphone
|
||||
{$i18n.t('Microphone')}
|
||||
</a>
|
||||
<a href="sensors" class="disabled">
|
||||
<i class="ti ti-cpu-2"></i>
|
||||
Sensors
|
||||
{$i18n.t('Sensors')}
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -63,6 +64,7 @@
|
|||
& a {
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
|
@ -6,19 +6,20 @@
|
|||
import rearLeftUrl from '@assets/audio/5.1/Rear_Left.mp3';
|
||||
import rearRightUrl from '@assets/audio/5.1/Rear_Right.mp3';
|
||||
import LfeUrl from '@assets/audio/5.1/LFE_Noise.mp3';
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<Speaker src={frontLeftUrl} left>Front Left</Speaker>
|
||||
<Speaker src={frontLeftUrl} left>{$i18n.t('Front Left')}</Speaker>
|
||||
<div class="center">
|
||||
<Speaker src={frontCenterUrl} center>Front Center</Speaker>
|
||||
<Speaker src={frontCenterUrl} center>{$i18n.t('Front Center')}</Speaker>
|
||||
</div>
|
||||
<Speaker src={frontRightUrl} right>Front Right</Speaker>
|
||||
<Speaker src={frontRightUrl} right>{$i18n.t('Front Right')}</Speaker>
|
||||
</div>
|
||||
<div class="row">
|
||||
<Speaker src={rearLeftUrl} left>Rear Left</Speaker>
|
||||
<Speaker src={rearRightUrl} right>Rear Right</Speaker>
|
||||
<Speaker src={rearLeftUrl} left>{$i18n.t('Rear Left')}</Speaker>
|
||||
<Speaker src={rearRightUrl} right>{$i18n.t('Rear Right')}</Speaker>
|
||||
</div>
|
||||
<Speaker src={LfeUrl} lfe>LFE</Speaker>
|
||||
<Speaker src={LfeUrl} lfe>{$i18n.t('LFE')}</Speaker>
|
||||
|
||||
<div class="label">5.1</div>
|
|
@ -8,24 +8,25 @@
|
|||
import rearLeftUrl from '@assets/audio/7.1/Rear_Left.mp3';
|
||||
import rearRightUrl from '@assets/audio/7.1/Rear_Right.mp3';
|
||||
import LfeUrl from '@assets/audio/7.1/LFE_Noise.mp3';
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<Speaker src={frontLeftUrl} left>Front Left</Speaker>
|
||||
<Speaker src={frontLeftUrl} left>{$i18n.t('Front Left')}</Speaker>
|
||||
<div class="center">
|
||||
<Speaker src={frontCenterUrl} center>Front Center</Speaker>
|
||||
<Speaker src={frontCenterUrl} center>{$i18n.t('Front Center')}</Speaker>
|
||||
</div>
|
||||
<Speaker src={frontRightUrl} right>Front Right</Speaker>
|
||||
<Speaker src={frontRightUrl} right>{$i18n.t('Front Right')}</Speaker>
|
||||
</div>
|
||||
<div class="row">
|
||||
<Speaker src={sideLeftUrl} left>Side Left</Speaker>
|
||||
<Speaker src={sideRightUrl} right>Side Right</Speaker>
|
||||
<Speaker src={sideLeftUrl} left>{$i18n.t('Side Left')}</Speaker>
|
||||
<Speaker src={sideRightUrl} right>{$i18n.t('Side Right')}</Speaker>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<Speaker src={rearLeftUrl} left>Rear Left</Speaker>
|
||||
<Speaker src={rearRightUrl} right>Rear Right</Speaker>
|
||||
<Speaker src={rearLeftUrl} left>{$i18n.t('Rear Left')}</Speaker>
|
||||
<Speaker src={rearRightUrl} right>{$i18n.t('Rear Right')}</Speaker>
|
||||
</div>
|
||||
<Speaker src={LfeUrl} lfe>LFE</Speaker>
|
||||
<Speaker src={LfeUrl} lfe>{$i18n.t('LFE')}</Speaker>
|
||||
|
||||
<div class="label">7.1</div>
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
import { i18n } from '$lib/i18n';
|
||||
|
||||
export let element: HTMLElement;
|
||||
|
||||
|
@ -46,8 +47,8 @@
|
|||
<button on:click={onClick}>
|
||||
<i class="ti ti-refresh"></i>
|
||||
{#if cycling}
|
||||
Stop Cycling
|
||||
{$i18n.t('Stop Cycling')}
|
||||
{:else}
|
||||
Cycle through
|
||||
{$i18n.t('Cycle through')}
|
||||
{/if}
|
||||
</button>
|
|
@ -4,15 +4,16 @@
|
|||
import rightUrl from '@assets/audio/stereo/Right.mp3';
|
||||
import Speaker from './speaker.svelte';
|
||||
import CycleButton from './cycle-button.svelte';
|
||||
import { i18n } from '$lib/i18n';
|
||||
|
||||
let speakersEl: HTMLElement;
|
||||
</script>
|
||||
|
||||
<div class="test">
|
||||
<div class="speakers" bind:this={speakersEl}>
|
||||
<Speaker src={leftUrl} left inline>Left</Speaker>
|
||||
<Speaker src={centerUrl} center inline>Center</Speaker>
|
||||
<Speaker src={rightUrl} right inline>Right</Speaker>
|
||||
<Speaker src={leftUrl} left inline>{$i18n.t('Left')}</Speaker>
|
||||
<Speaker src={centerUrl} center inline>{$i18n.t('Center')}</Speaker>
|
||||
<Speaker src={rightUrl} right inline>{$i18n.t('Right')}</Speaker>
|
||||
</div>
|
||||
<CycleButton element={speakersEl} />
|
||||
</div>
|
6
src/routes/(pages)/audio/+layout.svelte
Normal file
6
src/routes/(pages)/audio/+layout.svelte
Normal file
|
@ -0,0 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-volume"></i> {$i18n.t('Audio test')}</h2>
|
||||
<slot />
|
41
src/routes/(pages)/audio/+page.svelte
Normal file
41
src/routes/(pages)/audio/+page.svelte
Normal file
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import StereoTest from './(channels)/stereo-test.svelte';
|
||||
import PhaseTest from './phase.svelte';
|
||||
import { i18n } from '$lib/i18n';
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<h3>{$i18n.t('Channel tests')}</h3>
|
||||
<h4>{$i18n.t('Stereo')}</h4>
|
||||
<section>
|
||||
<StereoTest />
|
||||
</section>
|
||||
<h4>{$i18n.t('Surround audio')}</h4>
|
||||
<section>
|
||||
<ul>
|
||||
<li><a class="button" href="channels-5.1">{$i18n.t('5.1 Surround')}</a></li>
|
||||
<li><a class="button" href="channels-7.1">{$i18n.t('7.1 Surround')}</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<h3>{$i18n.t('Phase test')}</h3>
|
||||
<PhaseTest />
|
||||
</article>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
display: inline-flex;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
87
src/routes/(pages)/audio/phase.svelte
Normal file
87
src/routes/(pages)/audio/phase.svelte
Normal file
|
@ -0,0 +1,87 @@
|
|||
<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>
|
|
@ -1,9 +1,10 @@
|
|||
<script lang="ts">
|
||||
import videoUrl from '@assets/avsync.webm';
|
||||
import { i18n } from '$lib/i18n';
|
||||
let paused = true;
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-time-duration-off"></i> Audio/Video Synchronization</h2>
|
||||
<h2><i class="ti ti-time-duration-off"></i> {$i18n.t('Audio/Video Synchronization')}</h2>
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video
|
||||
class:playing={!paused}
|
|
@ -2,6 +2,7 @@
|
|||
import { onDestroy, onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import debug from 'debug';
|
||||
import { i18n } from '$lib/i18n';
|
||||
const dbg = debug('app:camera');
|
||||
|
||||
let video: HTMLVideoElement;
|
||||
|
@ -78,26 +79,26 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-camera"></i> Camera test</h2>
|
||||
<h2><i class="ti ti-camera"></i> {$i18n.t('Camera test')}</h2>
|
||||
|
||||
<div class="controls">
|
||||
<label>
|
||||
Device
|
||||
{$i18n.t('Device')}
|
||||
<select bind:value={currentDevice} disabled={!devices.length}>
|
||||
{#each devices as device}
|
||||
<option value={device.deviceId}>{device.label || '???'}</option>
|
||||
{:else}
|
||||
<option>No camera found</option>
|
||||
<option>{$i18n.t('No camera found')}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<button on:click={refreshDevices}>
|
||||
<i class="ti ti-refresh"></i>
|
||||
Refresh
|
||||
{$i18n.t('Refresh')}
|
||||
</button>
|
||||
<div class="separator"></div>
|
||||
<label>
|
||||
Resolution
|
||||
{$i18n.t('Resolution')}
|
||||
<select bind:value={requestResolution}>
|
||||
<option value="auto">Auto</option>
|
||||
<option value={[4096, 2160]}>4096x2160</option>
|
||||
|
@ -109,7 +110,7 @@
|
|||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Frame rate
|
||||
{$i18n.t('Frame rate')}
|
||||
<select bind:value={requestFramerate}>
|
||||
<option value="auto">Auto</option>
|
||||
<option value={120}>120 fps</option>
|
||||
|
@ -135,29 +136,29 @@
|
|||
|
||||
<footer>
|
||||
{#if !currentDevice}
|
||||
<span class="subdued">No camera selected</span>
|
||||
<span class="subdued">{$i18n.t('No camera selected')}</span>
|
||||
{:else}
|
||||
<ul>
|
||||
{#key currentDevice}
|
||||
<li>
|
||||
Resolution: <strong>{deviceInfo.resolution || '???'}</strong>
|
||||
{$i18n.t('Resolution')}: <strong>{deviceInfo.resolution || '???'}</strong>
|
||||
</li>
|
||||
<li>
|
||||
Frame rate: <strong>{deviceInfo.frameRate || '???'}</strong>
|
||||
{$i18n.t('Frame rate')}: <strong>{deviceInfo.frameRate || '???'}</strong>
|
||||
</li>
|
||||
{/key}
|
||||
</ul>
|
||||
<div class="controls">
|
||||
<button on:click={takeSnapshot}>
|
||||
<i class="ti ti-camera"></i>
|
||||
Take picture
|
||||
{$i18n.t('Take picture')}
|
||||
</button>
|
||||
<button on:click={() => (flipped = !flipped)}>
|
||||
<i class="ti ti-flip-vertical"></i>
|
||||
{#if flipped}
|
||||
Unflip image
|
||||
{$i18n.t('Unflip image')}
|
||||
{:else}
|
||||
Flip image
|
||||
{$i18n.t('Flip image')}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
|
@ -2,6 +2,7 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import debug from 'debug';
|
||||
import { i18n } from '$lib/i18n';
|
||||
|
||||
const dbg = debug('app:camera');
|
||||
|
||||
|
@ -92,27 +93,27 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-device-gamepad"></i> Gamepad & Joystick Tests</h2>
|
||||
<h2><i class="ti ti-device-gamepad"></i> {$i18n.t('Gamepad & Joystick Tests')}</h2>
|
||||
<div class="controls">
|
||||
<label>
|
||||
Device
|
||||
{$i18n.t('Device')}
|
||||
<select disabled={!gamepads.length}>
|
||||
{#each gamepads as gamepad}
|
||||
<option value={gamepad.index}>{gamepad.id}</option>
|
||||
{:else}
|
||||
<option>No gamepads detected. (Try pressing a button)</option>
|
||||
<option>{$i18n.t('No gamepads detected. (Try pressing a button)')}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<button on:click={refreshGamepads}>
|
||||
<i class="ti ti-refresh"></i>
|
||||
Refresh
|
||||
{$i18n.t('Refresh')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if currentGamepad}
|
||||
<section>
|
||||
<h3>Buttons</h3>
|
||||
<h3>{$i18n.t('Buttons')}</h3>
|
||||
<ul class="buttons">
|
||||
{#each buttons as button, i}
|
||||
<li class:pressed={button.pressed}>{i}</li>
|
||||
|
@ -120,7 +121,7 @@
|
|||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Axes</h3>
|
||||
<h3>{$i18n.t('Axes')}</h3>
|
||||
<div class="axes">
|
||||
{#each axes as axis, i (i)}
|
||||
<div class="axis">
|
||||
|
@ -130,7 +131,7 @@
|
|||
<span>{axis.toFixed(2)}</span>
|
||||
</div>
|
||||
<details>
|
||||
<summary>History</summary>
|
||||
<summary>{$i18n.t('History')}</summary>
|
||||
<canvas width="512" height="128" data-axis={i}></canvas>
|
||||
</details>
|
||||
</div>
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { i18n } from '$lib/i18n';
|
||||
|
||||
let key: string;
|
||||
let code: string;
|
||||
|
@ -14,8 +15,8 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<h2>Keyboard testing</h2>
|
||||
<p>Press a key on the keyboard to see the event object and the key code.</p>
|
||||
<h2>{$i18n.t('Keyboard testing')}</h2>
|
||||
<p>{$i18n.t('Press a key on the keyboard to see the event object and the key code.')}</p>
|
||||
<div class="current">
|
||||
{#if key}
|
||||
<span>{key}</span>
|
||||
|
@ -25,7 +26,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<p>Pressed keys:</p>
|
||||
<p>{$i18n.t('Pressed keys:')}</p>
|
||||
<ul>
|
||||
{#each pressedKeys as key}
|
||||
<li>{key}</li>
|
|
@ -4,10 +4,7 @@
|
|||
import '@fontsource/b612/700.css';
|
||||
import '@tabler/icons-webfont/tabler-icons.css';
|
||||
import '../index.css';
|
||||
import TestCard from '$lib/TestCard.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let idleTimeout: NodeJS.Timeout | undefined;
|
||||
onMount(() => {
|
||||
|
@ -19,53 +16,11 @@
|
|||
}, 3000);
|
||||
});
|
||||
});
|
||||
|
||||
$: onlyCard = $page.data.card;
|
||||
</script>
|
||||
|
||||
<TestCard full={onlyCard} on:focus={() => goto('/card')} />
|
||||
<main class:content={!onlyCard} class:sub={!$page.data.root && !onlyCard}>
|
||||
<a href=".." class="button button-back"><i class="ti ti-arrow-back" />Back</a>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main.content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid white;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main.sub {
|
||||
height: 90vh;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
.button-back {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
|
||||
opacity: 0.66;
|
||||
transition: opacity 0.3s;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
main:not(.sub) .button-back {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global(.hide-idle) {
|
||||
transition: opacity 1s;
|
||||
opacity: 1;
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-volume"></i> Audio test</h2>
|
||||
<slot />
|
|
@ -1,37 +0,0 @@
|
|||
<script lang="ts">
|
||||
import StereoTest from './(channels)/stereo-test.svelte';
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<h3>Channel tests</h3>
|
||||
<h4>Stereo</h4>
|
||||
<section>
|
||||
<StereoTest />
|
||||
</section>
|
||||
<h4>Surround audio</h4>
|
||||
<section>
|
||||
<ul>
|
||||
<li><a class="button" href="channels-5.1">5.1 Surround</a></li>
|
||||
<li><a class="button" href="channels-7.1">7.1 Surround</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<style>
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
display: inline-flex;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +0,0 @@
|
|||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = () => {
|
||||
return {
|
||||
card: true
|
||||
};
|
||||
};
|
Loading…
Reference in a new issue