File size: 16,136 Bytes
eb1a122
 
 
 
 
f026127
eb1a122
 
 
e33cc90
 
f026127
 
e33cc90
 
 
ce84147
 
 
 
 
 
f026127
ce84147
 
f026127
f08496f
f026127
f08496f
f026127
 
e33cc90
 
5a90820
 
 
 
f026127
5a90820
e33cc90
f026127
f08496f
 
 
eb1a122
f026127
 
 
eb1a122
 
 
f026127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e33cc90
 
f026127
e33cc90
 
 
 
0b5f0f0
 
 
 
 
e33cc90
f026127
e33cc90
 
 
 
 
 
 
 
 
f026127
 
 
 
e33cc90
 
 
f08496f
eb1a122
f026127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb1a122
f026127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f08496f
f026127
 
 
fa35534
 
f026127
 
 
 
 
 
 
 
 
 
 
 
 
0b5f0f0
f026127
 
 
 
 
5a90820
f026127
 
 
 
 
f08496f
e33cc90
eb1a122
f026127
f08496f
e33cc90
f08496f
f026127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e9170f
f026127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e33cc90
f026127
 
 
 
 
 
 
e33cc90
3e9170f
f026127
 
 
 
 
e33cc90
f08496f
f026127
eb1a122
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Sample Extractor</title>
    <link rel="stylesheet" href="/web/styles.css" />
  </head>
  <body>
    <div id="globalDropOverlay" class="global-drop-overlay" aria-hidden="true">
      <div>
        <strong>Drop audio to extract samples</strong>
        <span>The waveform appears immediately and processing starts automatically.</span>
      </div>
    </div>

    <div id="errorBanner" class="error-banner" role="alert" hidden>
      <div class="error-copy">
        <strong id="errorTitle">Request failed</strong>
        <span id="errorMessage">Something went wrong.</span>
        <small id="errorDetail"></small>
      </div>
      <button id="dismissErrorButton" class="icon-button" type="button" aria-label="Dismiss error">×</button>
    </div>

    <div class="app-shell">
      <header class="topbar">
        <div class="brand">
          <span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span>
          <strong>Sample Extractor</strong>
          <span class="beta-pill">BETA</span>
        </div>

        <div id="dropzone" class="file-picker" title="Choose audio, or drop audio anywhere" role="button" tabindex="0">
          <input id="fileInput" type="file" accept="audio/*,.wav,.mp3,.flac,.aiff,.ogg,.m4a,.aac" aria-label="Choose audio file" />
          <button id="chooseAudioButton" class="file-picker-button" type="button">Choose audio</button>
          <span class="file-picker-copy"><span id="dropTitle">Drop audio anywhere</span><small id="dropMeta">Processing starts automatically</small></span>
          <span aria-hidden="true"></span>
        </div>

        <div class="top-actions">
          <div class="backend-pill" aria-live="polite">
            <span class="status-dot" id="healthDot"></span>
            <span><strong id="healthText">Connecting</strong><small id="healthSubtext">FastAPI backend</small></span>
          </div>
          <button id="runButton" class="secondary-action" type="button" disabled>↻ Reprocess</button>
          <button id="exportAllButton" class="secondary-action" type="button" disabled>⇧ Export All</button>
          <button id="exportSelectedButton" class="primary-action" type="button" disabled>⇄ Export Selected <span id="selectedCountTop">(0)</span></button>
        </div>
      </header>

      <main class="main-grid">
        <section class="canvas-pane" aria-label="Waveform and samples">
          <section class="wave-panel" aria-label="Waveform overview">
            <div class="wave-toolbar">
              <div class="zoom-tools" aria-label="Waveform zoom controls">
                <button id="zoomInspectButton" class="tool-button" type="button" title="Zoom with pinch or wheel"></button>
                <button id="zoomOutButton" class="tool-button" type="button" title="Zoom out"></button>
                <button id="zoomInButton" class="tool-button" type="button" title="Zoom in">+</button>
                <button id="zoomFitButton" class="tool-button fit" type="button" title="Fit waveform">Fit</button>
              </div>
              <div class="wave-status">
                <strong id="waveTitle">Load audio</strong>
                <span id="waveProgressText">Drop a file anywhere. Extraction starts automatically.</span>
              </div>
              <time id="transportTime" class="top-time">0:00 / 0:00</time>
            </div>

            <canvas id="waveform" class="waveform" height="360"></canvas>

            <div class="transport-row" aria-label="Preview transport">
              <button id="transportPlayButton" class="round-play" type="button" aria-label="Play preview"></button>
              <input id="transportSeek" class="transport-seek" type="range" min="0" max="1000" value="0" step="1" aria-label="Seek preview" />
              <div class="preview-tabs" aria-label="Preview source">
                <button id="previewSourceButton" class="preview-tab active" type="button" data-preview-mode="source">Source</button>
                <button id="previewStemButton" class="preview-tab" type="button" data-preview-mode="stem">Stem</button>
                <button id="previewReproductionButton" class="preview-tab" type="button" data-preview-mode="reproduction">Reproduced</button>
              </div>
            </div>

            <div class="hidden-audio-bank" aria-hidden="true">
              <audio id="sourcePreview"></audio>
              <audio id="stemAudio"></audio>
              <audio id="reconAudio"></audio>
              <audio id="hitAudio"></audio>
              <audio id="sampleAudio"></audio>
            </div>
          </section>

          <section class="sample-board" aria-label="Extracted samples">
            <div class="board-header">
              <h2>Extracted Samples <span id="sampleCountLabel">(0)</span></h2>
              <span id="resultSummary">Drop audio. The app uploads, separates stems, tunes parameters, and deals sample cards.</span>
            </div>
            <div id="samplesGrid" class="sample-grid"></div>
          </section>
        </section>

        <aside class="settings-pane" aria-label="Settings">
          <header class="settings-head">
            <strong>Advanced Settings</strong>
            <button class="icon-button" type="button" aria-label="Hide settings">×</button>
          </header>

          <section class="settings-section simple-section">
            <h3>Detection</h3>
            <label class="control-line">Stem
              <select id="stem"></select>
            </label>
            <label class="range-line">Sensitivity
              <span class="range-wrap"><input id="onset_delta" type="range" min="0.01" max="0.35" step="0.005" /><output id="sensitivityOutput">Auto</output></span>
            </label>
            <label class="range-line">Minimum interval
              <span class="range-wrap"><input id="min_gap" type="range" min="0.001" max="1" step="0.005" /><output id="minGapOutput">Auto</output></span>
            </label>
            <label class="range-line">Minimum amplitude
              <span class="range-wrap"><input id="energy_threshold_db" type="range" min="-100" max="0" step="1" /><output id="energyOutput">Auto</output></span>
            </label>
          </section>

          <section class="settings-section simple-section">
            <h3>Grouping</h3>
            <label class="toggle-line"><span>Group Similar Hits</span><input id="groupSimilarToggle" type="checkbox" checked /></label>
            <label class="range-line">Max groups
              <span class="range-wrap"><input id="target_max" type="range" min="1" max="64" step="1" /><output id="targetMaxOutput">Auto</output></span>
            </label>
            <label class="range-line">Max group distance
              <span class="range-wrap"><input id="ncc_threshold" type="range" min="0" max="1" step="0.01" /><output id="nccOutput">Auto</output></span>
            </label>
          </section>

          <section class="settings-section simple-section">
            <h3>Waveform</h3>
            <label class="control-line">Resolution
              <select id="waveformResolution"><option>High</option><option>Medium</option><option>Low</option></select>
            </label>
            <label class="control-line">Channels
              <select id="waveformChannels"><option>Left & Right</option><option>Mono</option></select>
            </label>
          </section>

          <details class="settings-section advanced-fold">
            <summary>Expert pipeline controls</summary>
            <div class="expert-grid">
              <label>Separation engine<select id="separation_backend"><option value="spleeter">Spleeter (default)</option><option value="demucs">Demucs</option><option value="none">No separation / full mix</option></select></label>
              <label>Spleeter model<select id="spleeter_model"></select></label>
              <label>Demucs model<select id="demucs_model"></select></label>
              <label>Clustering mode<select id="clustering_mode"><option value="batch_quality">batch quality</option><option value="online_preview">online preview</option></select></label>
              <label>Shifts<input id="demucs_shifts" type="number" min="0" max="8" step="1" /></label>
              <label>Overlap<input id="demucs_overlap" type="number" min="0" max="0.9" step="0.05" /></label>
              <label>Onset mode<select id="onset_mode"><option value="auto">auto / multiband</option><option value="percussive">percussive</option><option value="harmonic">harmonic</option><option value="broadband">broadband</option></select></label>
              <label>Pre-pad<input id="pre_pad" type="number" min="0" max="0.25" step="0.001" /></label>
              <label>Min duration<input id="min_dur" type="number" min="0.001" max="10" step="0.005" /></label>
              <label>Max duration<input id="max_dur" type="number" min="0.01" max="10" step="0.1" /></label>
              <label>Target min<input id="target_min" type="number" min="0" max="256" step="1" /></label>
              <label>Attack ms<input id="attack_ms" type="number" min="1" max="250" step="1" /></label>
              <label>Mel prefilter<input id="mel_threshold" type="number" min="0" max="1" step="0.01" /></label>
              <label>Linkage<select id="linkage"><option value="average">average</option><option value="complete">complete</option><option value="single">single</option></select></label>
              <label>MIDI grid<select id="subdivision"><option value="4">4th</option><option value="8">8th</option><option value="16">16th</option><option value="32">32nd</option><option value="64">64th</option></select></label>
            </div>
            <div class="toggles">
              <label><input id="synthesize" type="checkbox" /> synthesize alternates</label>
              <label><input id="quantize_midi" type="checkbox" /> quantize MIDI</label>
              <label><input id="auto_tune" type="checkbox" checked /> automatic parameter tuning</label>
              <label><input id="use_disk_cache" type="checkbox" /> disk cache stems/source loads</label>
              <label><input id="allow_backend_fallback" type="checkbox" /> fallback to full mix if separation is unavailable</label>
            </div>
            <div class="preset-row">
              <button id="usePreviewButton" class="secondary-action" type="button">Fast preview</button>
              <button id="useQualityButton" class="secondary-action" type="button">Best quality</button>
              <button id="clearCacheButton" class="secondary-action" type="button">Clear cache</button>
            </div>
          </details>

          <button id="resetUiButton" class="reset-button" type="button">Reset to Defaults</button>
        </aside>
      </main>

      <footer class="bottom-bar" aria-label="Selection and tools">
        <div class="selection-count"><span class="checked-box"></span><strong id="selectedCountBottom">0 Selected</strong></div>
        <div class="bottom-zoom"><button class="tool-button" type="button" data-zoom-command="out">⌕ −</button><input class="mini-slider" type="range" min="1" max="64" value="1" /><button class="tool-button" type="button" data-zoom-command="in">+</button></div>
        <div class="bottom-actions"><button id="selectAllSamplesButton" class="secondary-action" type="button">☑ Select All</button><button id="clearSelectionButton" class="secondary-action" type="button">🗑 Clear</button><button id="openToolsButton" class="help-button" type="button">?</button></div>
      </footer>

      <section id="toolsDrawer" class="tools-drawer" aria-label="Review tools" hidden>
        <div class="drawer-grid">
          <article>
            <h3>Pipeline</h3>
            <span id="jobPill" class="job-pill">idle</span>
            <div id="stageList" class="stage-list"></div>
            <pre id="logs" class="logs" aria-live="polite"></pre>
          </article>
          <article>
            <h3>Exports</h3>
            <div id="downloads" class="downloads compact-downloads"></div>
            <div id="editedDownloads" class="downloads edited-downloads"></div>
            <button id="exportStateButton" class="secondary-action" type="button" disabled>Export edited pack</button>
          </article>
          <article>
            <h3>Selection</h3>
            <p id="selectedHitMeta">Click an onset marker or hit row to audition the detected slice.</p>
            <p id="selectedSampleMeta">Click a sample card to hear the representative sample.</p>
            <div id="supervisionSummary" class="state-summary">No interactive state loaded.</div>
          </article>
          <article>
            <h3>Run history</h3>
            <button id="refreshHistoryButton" class="secondary-action" type="button">Refresh history</button>
            <div id="historyList" class="history-list"></div>
          </article>
        </div>

        <details class="drawer-advanced">
          <summary>Review & edit state</summary>
          <div class="supervision-actions">
            <button id="refreshStateButton" class="secondary-action" type="button">Refresh state</button>
            <button id="forceOnsetButton" class="secondary-action" type="button" disabled>Add-onset mode off</button>
            <button id="undoButton" class="secondary-action" type="button" disabled>Undo edit</button>
          </div>
          <div class="supervision-tools">
            <label>Target cluster<select id="targetClusterSelect"></select></label>
            <button id="moveHitButton" class="secondary-action" type="button" disabled>Move selected hit</button>
            <button id="pullHitButton" class="secondary-action" type="button" disabled>Pull into new cluster</button>
            <button id="acceptHitButton" class="secondary-action" type="button" disabled>Accept hit</button>
            <button id="favoriteHitButton" class="secondary-action" type="button" disabled>Favorite</button>
            <button id="suppressHitButton" class="secondary-action danger" type="button" disabled>Suppress</button>
            <button id="restoreHitButton" class="secondary-action" type="button" disabled>Restore</button>
            <button id="lockClusterButton" class="secondary-action" type="button" disabled>Lock cluster</button>
            <button id="explainClusterButton" class="secondary-action" type="button" disabled>Explain</button>
          </div>
          <div class="supervision-grid">
            <article><h4>Outlier-first review queue</h4><div id="reviewQueue" class="compact-list"></div></article>
            <article><h4>Cluster board</h4><div id="clusterBoard" class="compact-list"></div></article>
            <article><h4>Suggestion inbox</h4><div id="suggestionInbox" class="compact-list"></div></article>
            <article><h4>Constraint / event log</h4><div id="stateLog" class="compact-list"></div></article>
          </div>
          <pre id="clusterExplanation" class="explanation empty">Select a cluster and click Explain.</pre>
        </details>

        <details class="drawer-advanced">
          <summary>Raw tables</summary>
          <div class="result-columns">
            <section><h3>Representative samples</h3><div class="table-wrap"><table id="samplesTable"><thead><tr><th>Audition</th><th>Sample</th><th>Class</th><th>Hits</th><th>Score</th><th>Duration</th><th>First hit</th><th>File</th></tr></thead><tbody></tbody></table></div></section>
            <section><h3>Detected hit review</h3><div class="table-wrap hit-table-wrap"><table id="hitsTable"><thead><tr><th>Audition</th><th>#</th><th>Label</th><th>Cluster</th><th>Confidence</th><th>Flags</th><th>Onset</th><th>Duration</th><th>Energy</th><th>File</th></tr></thead><tbody></tbody></table></div></section>
          </div>
        </details>
      </section>
    </div>
    <script type="module" src="/web/app.js"></script>
  </body>
</html>