Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> | |
| <title>NanoMaestro Realtime - Real-time AI Music</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="./styles.css?v=2.2" /> | |
| </head> | |
| <body> | |
| <!-- Full-screen Loading Overlay --> | |
| <div id="loadingOverlay" class="loading-overlay"> | |
| <div class="loader-card"> | |
| <h1 class="loader-title">NanoMaestro Realtime</h1> | |
| <p class="loader-subtitle">Real-time Lightweight Music Model</p> | |
| <div class="progress-container"> | |
| <div class="progress-bar"> | |
| <span id="loadProgress"></span> | |
| </div> | |
| <div class="progress-details"> | |
| <span id="loadStatus">Preparing application...</span> | |
| <span id="loadPercent">0%</span> | |
| </div> | |
| </div> | |
| <div class="loader-cta"> | |
| <a href="https://nanomaestro.app" target="_blank" rel="noopener" class="cta-link-badge"> | |
| <span>Try the newest web app <span class="cta-extra-text">for the best experience and latest features</span>!</span> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> | |
| </a> | |
| </div> | |
| <div class="loader-heart-notice"> | |
| <a href="https://huggingface.co/utkucoban/NanoMaestro-Realtime" target="_blank" rel="noopener"> | |
| Don't forget to heart the model if you like it! ❤️ | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Application Shell --> | |
| <main class="dashboard-shell"> | |
| <!-- Topbar Header --> | |
| <header class="app-header"> | |
| <div class="brand"> | |
| <div> | |
| <h2>NanoMaestro Realtime</h2> | |
| <p id="appStatus">Ready to perform</p> | |
| </div> | |
| </div> | |
| <!-- Top Model Switcher --> | |
| <div class="top-model-selector"> | |
| <label for="topModelSelect" class="top-model-label">Model Selector</label> | |
| <div class="custom-select-container top-select-container"> | |
| <select id="topModelSelect"> | |
| <option value="pro" selected>NanoMaestro Pro (54M - INT8 CPU)</option> | |
| <option value="light">NanoMaestro Light (13M - INT8 CPU)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <a href="https://nanomaestro.app" target="_blank" rel="noopener" class="cta-link-badge"> | |
| <span>Try the newest web app <span class="cta-extra-text">for the best experience and latest features</span>!</span> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> | |
| </a> | |
| </header> | |
| <div class="dashboard-content"> | |
| <!-- Left Sidebar / Info Panel --> | |
| <aside class="sidebar-spacer info-sidebar"> | |
| <div class="info-card"> | |
| <h5>About NanoMaestro</h5> | |
| <p id="modelDesc">NanoMaestro Pro is a 50 MB music-generation model with 54 million parameters, designed to run continuously in real time on almost any consumer CPU.</p> | |
| <p>Inference runs entirely on your device through WebAssembly (WASM), allowing NanoMaestro to generate music directly on your device locally.</p> | |
| <div class="heart-notice"> | |
| <a href="https://huggingface.co/utkucoban/NanoMaestro-Realtime" target="_blank" rel="noopener"> | |
| Don't forget to heart the model if you like it! ❤️ | |
| </a> | |
| </div> | |
| </div> | |
| <div class="info-card"> | |
| <h5>Bored with the song?</h5> | |
| <p>Click the <strong>New Song</strong> button to generate a completely new unique song. NanoMaestro will keep generating forever!</p> | |
| </div> | |
| </aside> | |
| <!-- Left Column: Visualizer and bottom controls --> | |
| <div class="main-column"> | |
| <!-- Visualizer Area (Main Panel) --> | |
| <section class="visualizer-panel"> | |
| <div class="panel-header"> | |
| <h3>Performance Visualizer</h3> | |
| <div class="hud-stats"> | |
| <div class="hud-item"> | |
| <span class="hud-label">BPM</span> | |
| <span class="hud-value" id="tempo">120</span> | |
| </div> | |
| <div class="hud-item"> | |
| <span class="hud-label">Notes</span> | |
| <span class="hud-value" id="eventCount">0</span> | |
| </div> | |
| <div class="hud-item"> | |
| <span class="hud-label">Last Token</span> | |
| <span class="hud-value" id="lastToken">-</span> | |
| </div> | |
| <div class="hud-item queue-hud-item"> | |
| <span class="hud-label">Queue</span> | |
| <div class="queue-hud-row"> | |
| <div class="readout-progress"> | |
| <span id="queueFill"></span> | |
| </div> | |
| <span class="hud-value" id="queueCount">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="visualizer-container"> | |
| <canvas id="noteCanvas"></canvas> | |
| </div> | |
| </section> | |
| <!-- Control Section Below Visualizer --> | |
| <section class="controls-panel-bottom"> | |
| <!-- Playback Actions & Seeding (Main Row) --> | |
| <div class="controls-main-row"> | |
| <!-- Playback Actions --> | |
| <div class="control-group playback-actions"> | |
| <button id="startBtn" type="button" class="btn-primary" disabled> | |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M8 5v14l11-7z"/></svg> | |
| Start Performance | |
| </button> | |
| <button id="stopBtn" type="button" class="btn-secondary" disabled> | |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg> | |
| Stop | |
| </button> | |
| <button id="newSongBtn" type="button" class="btn-secondary" disabled> | |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg> | |
| New Song | |
| </button> | |
| <button id="recordBtn" type="button" class="btn-secondary" disabled> | |
| <svg id="recordIcon" viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> | |
| <circle cx="12" cy="12" r="8" fill="#d9534f"/> | |
| </svg> | |
| <span id="recordBtnText">Record</span> | |
| </button> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| <!-- Right Column: Settings Dropdown --> | |
| <aside class="sidebar-column"> | |
| <!-- Settings Dropdown (Collapsible details element covering all sliders) --> | |
| <details class="advanced-details settings-dropdown"> | |
| <summary>Generation & Acoustic Settings</summary> | |
| <div class="advanced-content settings-vertical-stack"> | |
| <!-- Generation Engine Settings --> | |
| <div class="settings-column"> | |
| <h5>Generation Engine</h5> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="temperature">Creativity (Temp)</label> | |
| <span id="temperatureValue">0.85</span> | |
| </div> | |
| <input id="temperature" type="range" min="0.45" max="1.35" step="0.05" value="0.85" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="topK">Vocabulary Focus (Top K)</label> | |
| <span id="topKValue">40</span> | |
| </div> | |
| <input id="topK" type="range" min="5" max="120" step="1" value="40" /> | |
| </div> | |
| </div> | |
| <!-- Acoustic Settings --> | |
| <div class="settings-column"> | |
| <h5>Acoustic Parameters</h5> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="masterVolume">Volume</label> | |
| <span id="volumeValue">85%</span> | |
| </div> | |
| <input id="masterVolume" type="range" min="0" max="100" step="1" value="85" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="reverbWet">Room Size (Reverb)</label> | |
| <span id="reverbValue">22%</span> | |
| </div> | |
| <input id="reverbWet" type="range" min="0" max="100" step="1" value="22" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="delayWet">Echo (Delay)</label> | |
| <span id="delayValue">8%</span> | |
| </div> | |
| <input id="delayWet" type="range" min="0" max="100" step="1" value="8" /> | |
| </div> | |
| <div class="slider-group"> | |
| <div class="slider-header"> | |
| <label for="synthRelease">Sustain (Release)</label> | |
| <span id="releaseValue">1.2s</span> | |
| </div> | |
| <input id="synthRelease" type="range" min="0.2" max="3.0" step="0.1" value="1.2" /> | |
| </div> | |
| </div> | |
| </div> | |
| </details> | |
| <!-- Playing Style Box --> | |
| <div class="playing-style-panel settings-column"> | |
| <h5>Playing Style</h5> | |
| <div class="toggle-group" id="seedSourceGroup"> | |
| <button type="button" class="toggle-btn" data-value="default">Start from Blank</button> | |
| <button type="button" class="toggle-btn" data-value="midi">Start from MIDI</button> | |
| <button type="button" class="toggle-btn" data-value="bach">Bach</button> | |
| <button type="button" class="toggle-btn" data-value="mozart"> | |
| <span>Mozart</span> | |
| <span class="rec-tag">Recommended</span> | |
| </button> | |
| <button type="button" class="toggle-btn" data-value="chopin">Chopin</button> | |
| <button type="button" class="toggle-btn" data-value="debussy">Debussy</button> | |
| <button type="button" class="toggle-btn active" data-value="beethoven"> | |
| <span>Beethoven</span> | |
| <span class="rec-tag">Recommended</span> | |
| </button> | |
| <button type="button" class="toggle-btn" data-value="satie">Satie</button> | |
| </div> | |
| <!-- MIDI Seeding Controls (Expandable if active) --> | |
| <div id="midiSeedControls" style="display: none;" class="midi-seed-controls"> | |
| <div class="input-file-group"> | |
| <label for="midiFileInput">Select MIDI File</label> | |
| <input id="midiFileInput" type="file" accept=".mid,.midi" /> | |
| </div> | |
| <div id="midiFileInfo" class="midi-file-info" style="display: none; margin-top: 10px;"> | |
| <p id="midiFileName" class="midi-info-text" style="font-weight: 500;"></p> | |
| <p id="midiFileStats" class="midi-info-text" style="margin-top: 2px;"></p> | |
| <div class="timeline-container" style="margin-top: 14px;"> | |
| <span class="slider-header" style="font-size: 0.72rem; margin-bottom: 6px; color: var(--color-text-muted);">Visual Song Map & Warm-up Context (256 Tokens)</span> | |
| <div id="midiTimeline" class="midi-timeline"></div> | |
| </div> | |
| <div class="slider-group" style="margin-top: 12px;"> | |
| <div class="slider-header"> | |
| <label for="midiStartBar">Start Generation from Bar</label> | |
| <span id="midiStartBarValue">0</span> | |
| </div> | |
| <input id="midiStartBar" type="range" min="0" max="0" step="1" value="0" /> | |
| </div> | |
| <p id="midiStartBarTime" class="midi-info-subtext"></p> | |
| <div class="toggle-group-container" style="margin-top: 14px;"> | |
| <span class="toggle-group-label" style="font-size: 0.72rem; margin-bottom: 6px; display: block; color: var(--color-text-muted);">MIDI Playback Tempo Mode</span> | |
| <div class="toggle-group" id="midiBpmModeGroup"> | |
| <button type="button" class="toggle-btn active" data-value="lock">Lock MIDI BPM</button> | |
| <button type="button" class="toggle-btn" data-value="model">Follow Model BPM</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Playback Instrument Box --> | |
| <div class="playing-style-panel settings-column" style="margin-top: 15px;"> | |
| <h5>Playback Instrument</h5> | |
| <div class="custom-select-container"> | |
| <select id="instrumentSelect"> | |
| <option value="piano" selected>Piano (Default)</option> | |
| <option value="xylophone">Xylophone</option> | |
| <option value="violin">Violin (Recommended)</option> | |
| <option value="trumpet">Trumpet (Recommended)</option> | |
| <option value="saxophone">Saxophone</option> | |
| <option value="organ">Organ (Recommended)</option> | |
| <option value="harp">Harp</option> | |
| <option value="guitar-acoustic">Acoustic Guitar</option> | |
| <option value="guitar-electric">Electric Guitar</option> | |
| <option value="bass-electric">Electric Bass</option> | |
| </select> | |
| </div> | |
| </div> | |
| </aside> | |
| </div> | |
| </main> | |
| <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/tone@15.1.22/build/Tone.js"></script> | |
| <script type="module" src="./app.js?v=2.2"></script> | |
| </body> | |
| </html> | |