Rework the interface a bit
This commit is contained in:
398
src/App.svelte
398
src/App.svelte
@@ -5,8 +5,8 @@
|
|||||||
import WelcomeModal from "./lib/components/WelcomeModal.svelte";
|
import WelcomeModal from "./lib/components/WelcomeModal.svelte";
|
||||||
import ProcessorPopup from "./lib/components/ProcessorPopup.svelte";
|
import ProcessorPopup from "./lib/components/ProcessorPopup.svelte";
|
||||||
import { engines } from "./lib/audio/engines/registry";
|
import { engines } from "./lib/audio/engines/registry";
|
||||||
import type { SynthEngine, PitchLock } from "./lib/audio/engines/SynthEngine";
|
import type { SynthEngine, PitchLock } from "./lib/audio/engines/base/SynthEngine";
|
||||||
import type { EngineType } from "./lib/audio/engines/SynthEngine";
|
import type { EngineType } from "./lib/audio/engines/base/SynthEngine";
|
||||||
import { AudioService } from "./lib/audio/services/AudioService";
|
import { AudioService } from "./lib/audio/services/AudioService";
|
||||||
import { downloadWAV } from "./lib/audio/utils/WAVEncoder";
|
import { downloadWAV } from "./lib/audio/utils/WAVEncoder";
|
||||||
import { loadVolume, saveVolume, loadDuration, saveDuration, loadPitchLockEnabled, savePitchLockEnabled, loadPitchLockFrequency, savePitchLockFrequency } from "./lib/utils/settings";
|
import { loadVolume, saveVolume, loadDuration, saveDuration, loadPitchLockEnabled, savePitchLockEnabled, loadPitchLockFrequency, savePitchLockFrequency } from "./lib/utils/settings";
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
let selectionStart = $state<number | null>(null);
|
let selectionStart = $state<number | null>(null);
|
||||||
let selectionEnd = $state<number | null>(null);
|
let selectionEnd = $state<number | null>(null);
|
||||||
let canUndo = $state(false);
|
let canUndo = $state(false);
|
||||||
|
let sidebarOpen = $state(false);
|
||||||
|
|
||||||
const showDuration = $derived(engineType !== 'sample');
|
const showDuration = $derived(engineType !== 'sample');
|
||||||
const showRandomButton = $derived(engineType === 'generative');
|
const showRandomButton = $derived(engineType === 'generative');
|
||||||
@@ -256,6 +257,7 @@
|
|||||||
currentParams = null;
|
currentParams = null;
|
||||||
isProcessed = false;
|
isProcessed = false;
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
sidebarOpen = false;
|
||||||
|
|
||||||
if (engineType === 'generative') {
|
if (engineType === 'generative') {
|
||||||
generateRandom();
|
generateRandom();
|
||||||
@@ -393,25 +395,28 @@
|
|||||||
showModal = false;
|
showModal = false;
|
||||||
await audioService.initialize();
|
await audioService.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSidebar() {
|
||||||
|
sidebarOpen = !sidebarOpen;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window onkeydown={keyboardHandler} />
|
<svelte:window onkeydown={keyboardHandler} />
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
{#if sidebarOpen}
|
||||||
|
<div class="sidebar-overlay" onclick={toggleSidebar} onkeydown={(e) => e.key === 'Escape' && toggleSidebar()} role="button" tabindex="-1" aria-label="Close sidebar"></div>
|
||||||
|
{/if}
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<div class="mode-buttons">
|
<button class="hamburger" onclick={toggleSidebar} aria-label="Toggle engine menu">
|
||||||
{#each engines as currentEngine, index}
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<button
|
<line x1="3" y1="6" x2="21" y2="6"/>
|
||||||
class="engine-button"
|
<line x1="3" y1="12" x2="21" y2="12"/>
|
||||||
class:active={currentEngineIndex === index}
|
<line x1="3" y1="18" x2="21" y2="18"/>
|
||||||
data-description={currentEngine.getDescription()}
|
</svg>
|
||||||
onclick={() => switchEngine(index)}
|
</button>
|
||||||
>
|
<h1 class="app-title">Poof: a sample generator</h1>
|
||||||
{currentEngine.getName()}
|
<div class="controls-group">
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<div class="controls-group">
|
|
||||||
{#if showPitchLock}
|
{#if showPitchLock}
|
||||||
<div class="control-item pitch-lock-control">
|
<div class="control-item pitch-lock-control">
|
||||||
<div class="control-header">
|
<div class="control-header">
|
||||||
@@ -477,7 +482,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-area">
|
<div class="content-wrapper">
|
||||||
|
<div class="sidebar" class:open={sidebarOpen}>
|
||||||
|
<div class="sidebar-content">
|
||||||
|
{#each engines as currentEngine, index}
|
||||||
|
<button
|
||||||
|
class="engine-button"
|
||||||
|
class:active={currentEngineIndex === index}
|
||||||
|
data-description={currentEngine.getDescription()}
|
||||||
|
onclick={() => switchEngine(index)}
|
||||||
|
>
|
||||||
|
{currentEngine.getName()}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-area">
|
||||||
<div class="waveform-container">
|
<div class="waveform-container">
|
||||||
{#if showFileDropZone}
|
{#if showFileDropZone}
|
||||||
<div
|
<div
|
||||||
@@ -552,6 +573,7 @@
|
|||||||
<VUMeter buffer={currentBuffer} {playbackPosition} />
|
<VUMeter buffer={currentBuffer} {playbackPosition} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if showModal}
|
{#if showModal}
|
||||||
<WelcomeModal onclose={closeModal} />
|
<WelcomeModal onclose={closeModal} />
|
||||||
@@ -568,33 +590,105 @@
|
|||||||
|
|
||||||
.top-bar {
|
.top-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 0.75rem;
|
align-items: center;
|
||||||
padding: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
border-bottom: 1px solid #333;
|
border-bottom: 1px solid #333;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons {
|
.content-wrapper {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
flex-direction: row;
|
||||||
overflow-x: auto;
|
overflow: hidden;
|
||||||
overflow-y: hidden;
|
}
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
|
.sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
left: -100%;
|
||||||
|
top: 0;
|
||||||
|
width: 70%;
|
||||||
|
max-width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border-right: 1px solid #333;
|
||||||
|
z-index: 2000;
|
||||||
|
transition: left 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.open {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid #3a3a3a;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s, background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
background-color: #0f0f0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #444 transparent;
|
scrollbar-color: #444 transparent;
|
||||||
padding-bottom: 0.25rem;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons::-webkit-scrollbar {
|
.sidebar-content::-webkit-scrollbar {
|
||||||
height: 4px;
|
width: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons::-webkit-scrollbar-track {
|
.sidebar-content::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons::-webkit-scrollbar-thumb {
|
.sidebar-content::-webkit-scrollbar-thumb {
|
||||||
background: #444;
|
background: #444;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
@@ -603,10 +697,14 @@
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 0.85rem;
|
font-size: 0.75rem;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.5rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
min-width: calc(25vw - 1rem);
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: left;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #2a2a2a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-button:hover {
|
.engine-button:hover {
|
||||||
@@ -621,15 +719,15 @@
|
|||||||
.engine-button::after {
|
.engine-button::after {
|
||||||
content: attr(data-description);
|
content: attr(data-description);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 0;
|
||||||
left: 0;
|
left: 100%;
|
||||||
|
margin-left: 0.5rem;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
background-color: #0a0a0a;
|
background-color: #0a0a0a;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
font-size: 0.85rem;
|
font-size: 0.75rem;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
max-width: 90vw;
|
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -641,19 +739,25 @@
|
|||||||
|
|
||||||
.controls-group {
|
.controls-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 0.75rem;
|
gap: 0.25rem;
|
||||||
width: 100%;
|
align-items: center;
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-item {
|
.control-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 0.35rem;
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
background-color: #0f0f0f;
|
background-color: #0f0f0f;
|
||||||
padding: 0.5rem 0.65rem;
|
padding: 0.25rem 0.5rem;
|
||||||
border: 1px solid #2a2a2a;
|
border: 1px solid #2a2a2a;
|
||||||
transition: border-color 0.2s;
|
transition: border-color 0.2s;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-item:hover {
|
.control-item:hover {
|
||||||
@@ -662,38 +766,39 @@
|
|||||||
|
|
||||||
.control-header {
|
.control-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-header label {
|
.control-header label {
|
||||||
font-size: 0.75rem;
|
font-size: 0.65rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.03em;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-value-display {
|
.control-value-display {
|
||||||
font-size: 0.8rem;
|
font-size: 0.65rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
min-width: 3.5rem;
|
min-width: 2rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-item input[type="range"] {
|
.control-item input[type="range"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 120px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-checkbox {
|
.custom-checkbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.35rem;
|
gap: 0.25rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
@@ -709,8 +814,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
border: 1px solid #3a3a3a;
|
border: 1px solid #3a3a3a;
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
@@ -733,25 +836,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-text {
|
.checkbox-text {
|
||||||
font-size: 0.7rem;
|
font-size: 0.6rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.03em;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-box {
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-box svg {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-checkbox:hover .checkbox-text {
|
.custom-checkbox:hover .checkbox-text {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pitch-input {
|
.pitch-input {
|
||||||
width: 100%;
|
width: 50px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 0.35rem 0.5rem;
|
padding: 0.2rem 0.3rem;
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
border: 1px solid #3a3a3a;
|
border: 1px solid #3a3a3a;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 0.85rem;
|
font-size: 0.7rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
@@ -784,163 +897,44 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
.controls-group {
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item {
|
|
||||||
min-width: 140px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-lock-control {
|
|
||||||
min-width: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item input[type="range"] {
|
|
||||||
min-width: 80px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.top-bar {
|
.sidebar {
|
||||||
flex-direction: row;
|
position: static;
|
||||||
justify-content: space-between;
|
left: 0;
|
||||||
align-items: center;
|
width: 12%;
|
||||||
gap: 1rem;
|
min-width: 120px;
|
||||||
|
max-width: 200px;
|
||||||
|
transition: none;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mode-buttons {
|
.sidebar-overlay {
|
||||||
flex: 1;
|
display: none;
|
||||||
overflow-x: auto;
|
|
||||||
padding-bottom: 0;
|
|
||||||
max-width: 60%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-button {
|
.hamburger {
|
||||||
font-size: 0.9rem;
|
display: none;
|
||||||
padding: 0.6rem 1rem;
|
}
|
||||||
min-width: auto;
|
|
||||||
|
.app-title {
|
||||||
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-button::after {
|
.engine-button::after {
|
||||||
display: block;
|
display: block;
|
||||||
width: 250px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.engine-button:hover::after {
|
.engine-button:hover::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls-group {
|
|
||||||
width: auto;
|
|
||||||
flex-shrink: 0;
|
|
||||||
gap: 0.5rem;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item {
|
|
||||||
min-width: 120px;
|
|
||||||
padding: 0.45rem 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-lock-control {
|
|
||||||
min-width: 140px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-header label {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-value-display {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
min-width: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-text {
|
|
||||||
font-size: 0.65rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-box {
|
|
||||||
width: 13px;
|
|
||||||
height: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-box svg {
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-input {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
padding: 0.3rem 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item input[type="range"] {
|
|
||||||
min-width: 70px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.mode-buttons {
|
.sidebar {
|
||||||
max-width: 65%;
|
width: 10%;
|
||||||
}
|
|
||||||
|
|
||||||
.control-item {
|
|
||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
padding: 0.5rem 0.65rem;
|
max-width: 180px;
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-lock-control {
|
|
||||||
min-width: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-header label {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-value-display {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
min-width: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-text {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-box {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-box svg {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-input {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
padding: 0.35rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item input[type="range"] {
|
|
||||||
min-width: 90px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
|
||||||
.control-item {
|
|
||||||
min-width: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pitch-lock-control {
|
|
||||||
min-width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item input[type="range"] {
|
|
||||||
min-width: 110px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1037,13 +1031,13 @@
|
|||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
height: 20px;
|
height: 1.5rem;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="range"]::-webkit-slider-track {
|
input[type="range"]::-webkit-slider-track {
|
||||||
background: #333;
|
background: #333;
|
||||||
height: 4px;
|
height: 0.375rem;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
@@ -1051,13 +1045,13 @@
|
|||||||
input[type="range"]::-webkit-slider-thumb {
|
input[type="range"]::-webkit-slider-thumb {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 16px;
|
width: 1rem;
|
||||||
height: 16px;
|
height: 1rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-top: -6px;
|
margin-top: -0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="range"]::-webkit-slider-thumb:hover {
|
input[type="range"]::-webkit-slider-thumb:hover {
|
||||||
@@ -1066,14 +1060,14 @@
|
|||||||
|
|
||||||
input[type="range"]::-moz-range-track {
|
input[type="range"]::-moz-range-track {
|
||||||
background: #333;
|
background: #333;
|
||||||
height: 4px;
|
height: 0.375rem;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="range"]::-moz-range-thumb {
|
input[type="range"]::-moz-range-thumb {
|
||||||
width: 16px;
|
width: 1rem;
|
||||||
height: 16px;
|
height: 1rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum EnvCurve {
|
enum EnvCurve {
|
||||||
Linear,
|
Linear,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PitchLock, SynthEngine } from './SynthEngine';
|
import type { PitchLock, SynthEngine } from './base/SynthEngine';
|
||||||
|
|
||||||
interface BassDrumParams {
|
interface BassDrumParams {
|
||||||
// Core frequency (base pitch of the kick)
|
// Core frequency (base pitch of the kick)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface BenjolinParams {
|
interface BenjolinParams {
|
||||||
// Core oscillators
|
// Core oscillators
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum OscillatorWaveform {
|
enum OscillatorWaveform {
|
||||||
Sine,
|
Sine,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface DustNoiseParams {
|
interface DustNoiseParams {
|
||||||
// Dust density and character
|
// Dust density and character
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum EnvCurve {
|
enum EnvCurve {
|
||||||
Linear,
|
Linear,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PitchLock, SynthEngine } from './SynthEngine';
|
import type { PitchLock, SynthEngine } from './base/SynthEngine';
|
||||||
|
|
||||||
interface HiHatParams {
|
interface HiHatParams {
|
||||||
// Decay time (0 = closed/tight, 1 = open/long)
|
// Decay time (0 = closed/tight, 1 = open/long)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface InputParams {
|
interface InputParams {
|
||||||
recorded: boolean;
|
recorded: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
type HarmonicMode =
|
type HarmonicMode =
|
||||||
| 'single' // Just fundamental
|
| 'single' // Just fundamental
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine } from './SynthEngine';
|
import type { SynthEngine } from './base/SynthEngine';
|
||||||
|
|
||||||
interface NoiseDrumParams {
|
interface NoiseDrumParams {
|
||||||
// Noise characteristics
|
// Noise characteristics
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface ParticleNoiseParams {
|
interface ParticleNoiseParams {
|
||||||
// Particle characteristics
|
// Particle characteristics
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum PDWaveform {
|
enum PDWaveform {
|
||||||
Sine,
|
Sine,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum LFOWaveform {
|
enum LFOWaveform {
|
||||||
Sine,
|
Sine,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface SampleParams {
|
interface SampleParams {
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PitchLock, SynthEngine } from './SynthEngine';
|
import type { PitchLock, SynthEngine } from './base/SynthEngine';
|
||||||
|
|
||||||
interface SnareParams {
|
interface SnareParams {
|
||||||
// Core frequency (base pitch of the snare)
|
// Core frequency (base pitch of the snare)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CsoundEngine, type CsoundParameter } from './CsoundEngine';
|
import { CsoundEngine, type CsoundParameter } from './base/CsoundEngine';
|
||||||
import type { PitchLock } from './SynthEngine';
|
import type { PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum Waveform {
|
enum Waveform {
|
||||||
Sine = 0,
|
Sine = 0,
|
||||||
@@ -39,7 +39,7 @@ export interface SubtractiveThreeOscParams {
|
|||||||
|
|
||||||
export class SubtractiveThreeOsc extends CsoundEngine<SubtractiveThreeOscParams> {
|
export class SubtractiveThreeOsc extends CsoundEngine<SubtractiveThreeOscParams> {
|
||||||
getName(): string {
|
getName(): string {
|
||||||
return 'Subtractive 3-OSC';
|
return '3OSC';
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription(): string {
|
getDescription(): string {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
enum EnvCurve {
|
enum EnvCurve {
|
||||||
Linear,
|
Linear,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
|
|
||||||
interface WavetableParams {
|
interface WavetableParams {
|
||||||
bankIndex: number;
|
bankIndex: number;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine, PitchLock } from './SynthEngine';
|
import type { SynthEngine, PitchLock } from './base/SynthEngine';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { ZZFX } from 'zzfx';
|
import { ZZFX } from 'zzfx';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SynthEngine } from './SynthEngine';
|
import type { SynthEngine } from './base/SynthEngine';
|
||||||
import { FourOpFM } from './FourOpFM';
|
import { FourOpFM } from './FourOpFM';
|
||||||
import { TwoOpFM } from './TwoOpFM';
|
import { TwoOpFM } from './TwoOpFM';
|
||||||
import { PhaseDistortionFM } from './PhaseDistortionFM';
|
import { PhaseDistortionFM } from './PhaseDistortionFM';
|
||||||
|
|||||||
Reference in New Issue
Block a user