Add test models page and synth presets.

This commit is contained in:
b1rdmania
2025-12-20 12:09:20 +00:00
parent 98e95c1001
commit b7a79e93c9
8 changed files with 900 additions and 75 deletions
+258
View File
@@ -0,0 +1,258 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>MOTIF — Test models</title>
<style>
:root {
--color-bg: #0b0b0f;
--color-surface: rgba(255, 255, 255, 0.06);
--color-surface-2: rgba(255, 255, 255, 0.09);
--color-border: rgba(255, 255, 255, 0.12);
--color-text: rgba(255, 255, 255, 0.9);
--color-text-muted: rgba(255, 255, 255, 0.7);
--color-accent-primary: #00ff88;
--color-accent-secondary: #ff3cf5;
--radius: 12px;
--shadow-sm: 0 12px 34px rgba(0,0,0,0.45);
--glow-primary: 0 0 18px rgba(0, 255, 136, 0.18);
--spacing-unit: 8px;
}
body {
background: radial-gradient(1000px 600px at 20% 10%, rgba(255,60,245,0.10), transparent 55%),
radial-gradient(900px 500px at 80% 20%, rgba(0,255,136,0.09), transparent 50%),
linear-gradient(180deg, #07070a, #0b0b0f 60%, #060609);
color: var(--color-text);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
margin: 0;
padding: calc(var(--spacing-unit) * 6) calc(var(--spacing-unit) * 3);
}
.container { max-width: 960px; margin: 0 auto; }
h1 {
color: var(--color-accent-primary);
text-align: center;
margin: 0 0 calc(var(--spacing-unit) * 4) 0;
font-size: 2em;
font-weight: 700;
letter-spacing: 0.05em;
text-shadow: var(--glow-primary);
}
.topbar {
display: flex;
align-items: center;
justify-content: center;
gap: calc(var(--spacing-unit) * 2);
margin-bottom: calc(var(--spacing-unit) * 2);
}
.topbar h1 { margin: 0; }
.topbar-actions {
margin-left: auto;
display: inline-flex;
align-items: center;
gap: 8px;
}
.nav-link {
background: transparent;
border: 1px solid rgba(255,255,255,0.14);
color: rgba(255,255,255,0.85);
padding: 8px 10px;
border-radius: var(--radius);
box-shadow: none;
font-size: 13px;
text-decoration: none;
}
.nav-link:hover { box-shadow: var(--shadow-sm); border-color: rgba(255,255,255,0.22); }
.controls { margin: calc(var(--spacing-unit) * 3) 0; text-align: center; }
input[type="text"] {
width: min(600px, 100%);
padding: 12px 14px;
background: rgba(0,0,0,0.28);
border: 1px solid rgba(255,255,255,0.12);
border-radius: 10px;
color: var(--color-text);
outline: none;
}
button {
background: var(--color-surface);
border: 1px solid rgba(255,255,255,0.12);
color: rgba(255,255,255,0.86);
padding: 10px 12px;
border-radius: 10px;
cursor: pointer;
}
button.primary {
background: linear-gradient(90deg, rgba(0,255,136,0.18), rgba(255,60,245,0.10));
border-color: rgba(0,255,136,0.28);
}
button:disabled { opacity: 0.5; cursor: not-allowed; }
#status {
margin-top: 10px;
padding: 10px 12px;
border: 1px solid rgba(255,255,255,0.10);
border-radius: 12px;
background: rgba(0,0,0,0.20);
color: rgba(255,255,255,0.75);
}
.section {
border: 1px solid rgba(255,255,255,0.12);
border-radius: 18px;
background: rgba(0,0,0,0.18);
padding: 14px;
box-shadow: 0 26px 90px rgba(0,0,0,0.35);
margin-top: 16px;
}
.section-header { display:flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.section-title { margin: 0; font-size: 18px; }
.section-subtitle { margin: 0; color: rgba(255,255,255,0.65); font-size: 13px; }
.divider { height: 1px; background: rgba(255,255,255,0.08); margin: 12px 0; }
.results-section { display: none; }
.results-section.visible { display: block; }
.results-table { width: 100%; border-collapse: collapse; }
.results-table th, .results-table td { padding: 10px; border-bottom: 1px solid rgba(255,255,255,0.08); }
.results-table tr.selected { background: rgba(0,255,136,0.06); }
.player-section { display: none; }
.player-section.visible { display: block; }
.preview-controls, .motif-controls { display:flex; gap: 10px; align-items:center; flex-wrap: wrap; }
.volume-control { display:flex; align-items:center; gap: 8px; }
.volume-control label { color: rgba(255,255,255,0.65); font-size: 12px; }
input[type="range"] { width: 160px; }
.model-row { display:flex; align-items:center; gap: 10px; flex-wrap: wrap; }
.model-row label { color: rgba(255,255,255,0.7); font-size: 12px; }
select {
background: rgba(0,0,0,0.28);
border: 1px solid rgba(255,255,255,0.12);
color: rgba(255,255,255,0.9);
padding: 10px 12px;
border-radius: 10px;
font-family: inherit;
}
.ios-audio-banner {
margin-top: 10px;
border: 1px solid rgba(255,255,255,0.10);
border-radius: 12px;
padding: 10px 12px;
background: rgba(0,0,0,0.18);
display:none;
}
.ios-audio-banner .row { display:flex; align-items:center; justify-content:space-between; gap: 10px; }
.ios-audio-banner .state { margin-top: 6px; font-size: 12px; color: rgba(255,255,255,0.65); }
</style>
</head>
<body>
<div class="container">
<div class="topbar">
<h1>🎵 MOTIF</h1>
<div class="topbar-actions">
<a class="nav-link" href="/" aria-label="Back to main app">Home</a>
</div>
</div>
<p style="text-align:center; color: rgba(255,255,255,0.7); margin-top: 0;">Test models: Pre8bit → NES/GB → SNESish</p>
<div class="controls">
<input type="text" id="songInput" placeholder="Enter song name..." />
<button id="searchBtn">Search</button>
</div>
<div id="status">Ready. Enter a song name to search for MIDI files.</div>
<div id="resultsSection" class="section results-section">
<div class="section-header">
<h3 class="section-title">Search Results</h3>
<p class="section-subtitle">Pick a MIDI, then compare models.</p>
</div>
<div class="divider"></div>
<table id="resultsTable" class="results-table">
<thead>
<tr>
<th>Title</th>
<th>Source</th>
<th>Duration</th>
</tr>
</thead>
<tbody id="resultsBody"></tbody>
</table>
</div>
<div id="playerSection" class="player-section">
<div id="selectedInfo" class="section">
<div style="display:flex; flex-direction:column; gap: 6px;">
<h4 id="selectedTitle" style="margin:0;">No MIDI selected</h4>
<p id="selectedMeta" style="margin:0; color: rgba(255,255,255,0.7); font-size: 13px;">Select a MIDI file from search results to enable playback</p>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Preview</h3>
<p class="section-subtitle">Soundfont MIDI preview (baseline).</p>
</div>
<div class="divider"></div>
<div class="preview-controls">
<button id="soundfontPlayBtn" class="primary" disabled>Play Preview</button>
<button id="soundfontStopBtn" disabled>Stop</button>
<div class="volume-control">
<label for="soundfontVolume">Volume</label>
<input type="range" id="soundfontVolume" min="0" max="1" step="0.01" value="0.8" />
</div>
<button id="nextResultBtn" disabled>Try Next Result</button>
</div>
</div>
<div class="section">
<div class="section-header">
<h3 class="section-title">Motif generation</h3>
<p class="section-subtitle">Same MIDI → different synth models.</p>
</div>
<div class="divider"></div>
<div class="model-row">
<label for="modelSelect">Model</label>
<select id="modelSelect">
<option value="pre8bit">Pre8bit (beep / sparse)</option>
<option value="nes_gb" selected>NES / Game Boy (chip)</option>
<option value="snes_ish">SNESish (warm + echo)</option>
</select>
<span id="modelHint" style="color: rgba(255,255,255,0.65); font-size: 12px;">Square/triangle/noise vibe, tight envelope.</span>
</div>
<div class="divider"></div>
<div class="motif-controls">
<button id="motifBtn" class="primary" disabled>Generate & Play</button>
<button id="motifStopBtn" disabled>Stop</button>
<div class="volume-control">
<label for="motifVolume">Volume</label>
<input type="range" id="motifVolume" min="0" max="1" step="0.01" value="0.3" />
</div>
</div>
<div id="iosAudioBanner" class="ios-audio-banner" aria-live="polite">
<div class="row">
<div class="copy" style="color: rgba(255,255,255,0.8); font-size: 13px;">
<strong>iOS Safari:</strong> tap once to enable audio, then press <strong>Generate &amp; Play</strong>.
</div>
<button id="enableAudioBtn" type="button">Enable Audio</button>
</div>
<div id="iosAudioState" class="state">Audio: …</div>
</div>
</div>
</div>
</div>
<script type="module" src="/src/models.ts"></script>
</body>
</html>