| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>MorphoLoop - AI Music Production Suite</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| |
| body { |
| background: linear-gradient(135deg, #0f0c1d, #1a1a2e); |
| color: #e0e0e0; |
| min-height: 100vh; |
| padding: 20px; |
| overflow-x: hidden; |
| } |
| |
| .container { |
| max-width: 1400px; |
| margin: 0 auto; |
| } |
| |
| header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| padding: 20px; |
| background: rgba(10, 8, 20, 0.7); |
| border-radius: 20px; |
| border: 1px solid rgba(255, 0, 128, 0.3); |
| box-shadow: 0 0 20px rgba(139, 0, 255, 0.2); |
| margin-bottom: 30px; |
| backdrop-filter: blur(10px); |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 15px; |
| } |
| |
| .logo-icon { |
| width: 60px; |
| height: 60px; |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 24px; |
| font-weight: bold; |
| } |
| |
| .logo-text { |
| font-size: 28px; |
| font-weight: 800; |
| background: linear-gradient(45deg, #ff00cc, #00ffff); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| } |
| |
| .status-bar { |
| display: flex; |
| gap: 25px; |
| } |
| |
| .status-item { |
| background: rgba(30, 20, 50, 0.6); |
| padding: 10px 20px; |
| border-radius: 12px; |
| border: 1px solid rgba(255, 0, 128, 0.2); |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .controls { |
| display: flex; |
| gap: 15px; |
| } |
| |
| .btn { |
| padding: 12px 25px; |
| border-radius: 12px; |
| border: none; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| font-size: 14px; |
| } |
| |
| .btn-primary { |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| color: white; |
| } |
| |
| .btn-secondary { |
| background: rgba(100, 100, 150, 0.3); |
| color: #d0d0ff; |
| border: 1px solid rgba(100, 100, 200, 0.3); |
| } |
| |
| .btn:hover { |
| transform: translateY(-3px); |
| box-shadow: 0 5px 15px rgba(139, 0, 255, 0.4); |
| } |
| |
| .upload-section { |
| background: rgba(15, 12, 30, 0.7); |
| border-radius: 20px; |
| padding: 25px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); |
| margin-bottom: 30px; |
| } |
| |
| .upload-area { |
| border: 2px dashed rgba(100, 100, 200, 0.5); |
| border-radius: 15px; |
| padding: 40px; |
| text-align: center; |
| margin-bottom: 25px; |
| transition: all 0.3s ease; |
| cursor: pointer; |
| } |
| |
| .upload-area:hover { |
| border-color: #ff00cc; |
| background: rgba(30, 20, 50, 0.3); |
| } |
| |
| .upload-icon { |
| font-size: 60px; |
| margin-bottom: 15px; |
| color: #ff00cc; |
| } |
| |
| .upload-text { |
| font-size: 18px; |
| margin-bottom: 10px; |
| } |
| |
| .upload-hint { |
| color: #a0a0ff; |
| font-size: 14px; |
| } |
| |
| .instructions { |
| background: rgba(30, 20, 50, 0.6); |
| border-radius: 10px; |
| padding: 15px; |
| margin-top: 20px; |
| border: 1px solid rgba(100, 100, 200, 0.3); |
| font-size: 14px; |
| line-height: 1.6; |
| } |
| |
| .instructions h3 { |
| color: #00ffff; |
| margin-bottom: 10px; |
| } |
| |
| .instructions ul { |
| padding-left: 20px; |
| } |
| |
| .instructions li { |
| margin-bottom: 8px; |
| } |
| |
| .file-input { |
| display: none; |
| } |
| |
| .progress-container { |
| margin: 20px 0; |
| display: none; |
| } |
| |
| .progress-bar { |
| height: 10px; |
| background: rgba(20, 15, 40, 0.6); |
| border-radius: 5px; |
| overflow: hidden; |
| } |
| |
| .progress { |
| height: 100%; |
| background: linear-gradient(45deg, #ff00cc, #00ffff); |
| width: 0%; |
| transition: width 0.3s ease; |
| } |
| |
| .progress-text { |
| text-align: center; |
| margin-top: 10px; |
| color: #00ffff; |
| } |
| |
| .ai-model-info { |
| background: rgba(30, 20, 50, 0.6); |
| border-radius: 10px; |
| padding: 15px; |
| margin-top: 20px; |
| border: 1px solid rgba(100, 100, 200, 0.3); |
| } |
| |
| .ai-model-title { |
| font-weight: 600; |
| margin-bottom: 10px; |
| color: #00ffff; |
| } |
| |
| .ai-model-code { |
| background: rgba(20, 15, 40, 0.8); |
| border-radius: 8px; |
| padding: 10px; |
| font-family: monospace; |
| font-size: 14px; |
| overflow-x: auto; |
| color: #a0ffa0; |
| } |
| |
| .main-grid { |
| display: grid; |
| grid-template-columns: 1fr 350px; |
| gap: 25px; |
| margin-bottom: 30px; |
| } |
| |
| .panel { |
| background: rgba(15, 12, 30, 0.7); |
| border-radius: 20px; |
| padding: 25px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); |
| } |
| |
| .panel-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 20px; |
| padding-bottom: 15px; |
| border-bottom: 1px solid rgba(100, 100, 200, 0.2); |
| } |
| |
| .panel-title { |
| font-size: 20px; |
| font-weight: 700; |
| background: linear-gradient(45deg, #00ffff, #ff00cc); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .tabs { |
| display: flex; |
| gap: 10px; |
| margin-bottom: 25px; |
| flex-wrap: wrap; |
| } |
| |
| .tab-btn { |
| padding: 10px 20px; |
| background: rgba(30, 20, 50, 0.6); |
| border: 1px solid rgba(100, 100, 200, 0.3); |
| border-radius: 10px; |
| color: #d0d0ff; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .tab-btn.active { |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| color: white; |
| border-color: transparent; |
| } |
| |
| .tab-btn:hover:not(.active) { |
| background: rgba(100, 100, 200, 0.3); |
| } |
| |
| .input-group { |
| margin-bottom: 20px; |
| } |
| |
| .input-group label { |
| display: block; |
| margin-bottom: 8px; |
| font-weight: 500; |
| color: #b0b0ff; |
| } |
| |
| .input-field { |
| width: 100%; |
| padding: 14px; |
| background: rgba(20, 15, 40, 0.6); |
| border: 1px solid rgba(100, 100, 200, 0.3); |
| border-radius: 10px; |
| color: white; |
| font-size: 16px; |
| } |
| |
| .input-field:focus { |
| outline: none; |
| border-color: #ff00cc; |
| box-shadow: 0 0 10px rgba(255, 0, 204, 0.5); |
| } |
| |
| textarea.input-field { |
| min-height: 120px; |
| resize: vertical; |
| } |
| |
| .visualizer { |
| height: 200px; |
| background: rgba(10, 8, 20, 0.5); |
| border-radius: 15px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| position: relative; |
| overflow: hidden; |
| margin: 20px 0; |
| } |
| |
| .waveform { |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| display: flex; |
| align-items: flex-end; |
| justify-content: space-around; |
| } |
| |
| .bar { |
| width: 4px; |
| background: linear-gradient(to top, #ff00cc, #00ffff); |
| border-radius: 2px 2px 0 0; |
| } |
| |
| .track-list { |
| display: flex; |
| flex-direction: column; |
| gap: 15px; |
| } |
| |
| .track-item { |
| background: rgba(30, 20, 50, 0.6); |
| padding: 15px; |
| border-radius: 12px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| display: flex; |
| align-items: center; |
| gap: 15px; |
| } |
| |
| .track-icon { |
| width: 40px; |
| height: 40px; |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| border-radius: 8px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .track-info { |
| flex: 1; |
| } |
| |
| .track-name { |
| font-weight: 600; |
| margin-bottom: 5px; |
| } |
| |
| .track-type { |
| font-size: 13px; |
| color: #a0a0ff; |
| } |
| |
| .track-actions { |
| display: flex; |
| gap: 10px; |
| } |
| |
| .action-btn { |
| width: 36px; |
| height: 36px; |
| border-radius: 8px; |
| background: rgba(100, 100, 200, 0.2); |
| border: none; |
| color: #d0d0ff; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .action-btn:hover { |
| background: rgba(100, 100, 200, 0.4); |
| } |
| |
| .mixer-channel { |
| display: flex; |
| align-items: center; |
| gap: 15px; |
| margin-bottom: 20px; |
| padding: 15px; |
| background: rgba(30, 20, 50, 0.4); |
| border-radius: 12px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| } |
| |
| .channel-label { |
| width: 80px; |
| font-weight: 500; |
| color: #b0b0ff; |
| } |
| |
| .slider-container { |
| flex: 1; |
| } |
| |
| .slider { |
| width: 100%; |
| height: 8px; |
| -webkit-appearance: none; |
| background: rgba(100, 100, 200, 0.2); |
| border-radius: 4px; |
| outline: none; |
| } |
| |
| .slider::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| width: 20px; |
| height: 20px; |
| border-radius: 50%; |
| background: linear-gradient(45deg, #ff00cc, #00ffff); |
| cursor: pointer; |
| } |
| |
| .level-meter { |
| width: 20px; |
| height: 100px; |
| background: rgba(20, 15, 40, 0.6); |
| border-radius: 10px; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .level-fill { |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| width: 100%; |
| background: linear-gradient(to top, #ff00cc, #00ffff); |
| border-radius: 10px 10px 0 0; |
| } |
| |
| .transport-controls { |
| display: flex; |
| justify-content: center; |
| gap: 20px; |
| margin-top: 30px; |
| } |
| |
| .transport-btn { |
| width: 70px; |
| height: 70px; |
| border-radius: 50%; |
| background: linear-gradient(45deg, #ff00cc, #3333ff); |
| border: none; |
| color: white; |
| font-size: 24px; |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| box-shadow: 0 5px 15px rgba(139, 0, 255, 0.4); |
| } |
| |
| .transport-btn.secondary { |
| width: 50px; |
| height: 50px; |
| background: rgba(100, 100, 200, 0.3); |
| font-size: 18px; |
| } |
| |
| .sound-library { |
| background: rgba(15, 12, 30, 0.7); |
| border-radius: 20px; |
| padding: 25px; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); |
| margin-top: 30px; |
| } |
| |
| .library-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); |
| gap: 15px; |
| margin-top: 20px; |
| } |
| |
| .library-item { |
| background: rgba(30, 20, 50, 0.6); |
| border-radius: 10px; |
| padding: 15px; |
| text-align: center; |
| border: 1px solid rgba(100, 100, 200, 0.2); |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| .library-item:hover { |
| transform: translateY(-3px); |
| border-color: #ff00cc; |
| } |
| |
| .library-icon { |
| font-size: 24px; |
| margin-bottom: 10px; |
| } |
| |
| .library-name { |
| font-size: 14px; |
| margin-bottom: 5px; |
| } |
| |
| .library-type { |
| font-size: 12px; |
| color: #a0a0ff; |
| } |
| |
| footer { |
| text-align: center; |
| padding: 20px; |
| color: #a0a0ff; |
| font-size: 14px; |
| border-top: 1px solid rgba(100, 100, 200, 0.2); |
| margin-top: 30px; |
| } |
| |
| .help-text { |
| color: #a0a0ff; |
| font-style: italic; |
| margin-top: 5px; |
| font-size: 13px; |
| } |
| |
| .url-scraping-tab { |
| display: none; |
| } |
| |
| .url-input-group { |
| display: flex; |
| gap: 10px; |
| } |
| |
| .url-input { |
| flex: 1; |
| } |
| |
| .scrape-btn { |
| width: 120px; |
| } |
| |
| .scraped-audio-list { |
| margin-top: 20px; |
| max-height: 300px; |
| overflow-y: auto; |
| padding-right: 10px; |
| } |
| |
| .scraped-item { |
| background: rgba(30, 20, 50, 0.6); |
| padding: 10px; |
| border-radius: 8px; |
| margin-bottom: 10px; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .scraped-url { |
| word-break: break-all; |
| } |
| |
| .download-btn { |
| background: rgba(100, 100, 200, 0.3); |
| color: #d0d0ff; |
| border: none; |
| padding: 5px 10px; |
| border-radius: 5px; |
| cursor: pointer; |
| } |
| |
| .download-btn:hover { |
| background: rgba(100, 100, 200, 0.5); |
| } |
| |
| @media (max-width: 900px) { |
| .main-grid { |
| grid-template-columns: 1fr; |
| } |
| |
| header { |
| flex-direction: column; |
| gap: 20px; |
| } |
| |
| .tabs { |
| flex-wrap: wrap; |
| justify-content: center; |
| } |
| |
| .transport-controls { |
| flex-wrap: wrap; |
| } |
| |
| .url-input-group { |
| flex-direction: column; |
| } |
| |
| .scrape-btn { |
| width: 100%; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <div class="logo"> |
| <div class="logo-icon">ML</div> |
| <div class="logo-text">MorphoLoop</div> |
| </div> |
| <div class="status-bar"> |
| <div class="status-item">BPM: 124</div> |
| <div class="status-item">Key: G# Minor</div> |
| <div class="status-item">Tempo: 100%</div> |
| </div> |
| <div class="controls"> |
| <button class="btn btn-secondary">Save Project</button> |
| <button class="btn btn-primary">Render Track</button> |
| </div> |
| </header> |
|
|
| <div class="upload-section"> |
| <div class="upload-area" id="uploadArea"> |
| <div class="upload-icon">ð</div> |
| <div class="upload-text">Drag & Drop Audio File Here</div> |
| <div class="upload-hint">Supports MP3, MP4, WAV, FLAC, M4A formats</div> |
| <input type="file" id="fileInput" class="file-input" accept="audio/*,video/*"> |
| <button class="btn btn-primary" style="margin-top: 20px;" onclick="document.getElementById('fileInput').click()">Select File</button> |
| </div> |
| <div class="progress-container" id="progressContainer"> |
| <div class="progress-bar"> |
| <div class="progress" id="progressBar"></div> |
| </div> |
| <div class="progress-text" id="progressText">Processing audio...</div> |
| </div> |
| <div class="instructions"> |
| <h3>How to Use:</h3> |
| <ul> |
| <li>Upload an audio file to separate instruments using AI</li> |
| <li>Drag files or click the button to select</li> |
| <li>Wait for the separation process to complete</li> |
| <li>New tracks will appear in the Track Manager</li> |
| </ul> |
| </div> |
| <div class="ai-model-info"> |
| <div class="ai-model-title">AI Model Brain</div> |
| <div class="ai-model-code">from transformers import pipeline<br> |
| pipe = pipeline("text-generation", model="DancingIguana/music-generation")<br><br> |
| from transformers import AutoTokenizer, AutoModelForCausalLM<br> |
| tokenizer = AutoTokenizer.from_pretrained("DancingIguana/music-generation")<br> |
| model = AutoModelForCausalLM.from_pretrained("DancingIguana/music-generation")<br><br> |
| from transformers import AutoModel<br> |
| model = AutoModel.from_pretrained("mradermacher/music_generation_model-GGUF", dtype="auto")<br><br> |
| from speechbrain.pretrained import SepformerSeparation<br> |
| model = SepformerSeparation.from_hparams(<br> |
| "speechbrain/sepformer-wham"<br> |
| )<br> |
| model.separate_file("file.wav")</div> |
| </div> |
| </div> |
|
|
| <div class="main-grid"> |
| <div class="panel"> |
| <div class="panel-header"> |
| <h2 class="panel-title">AI Music Production Studio</h2> |
| <div class="status-item">Mode: Creative</div> |
| </div> |
|
|
| <div class="tabs"> |
| <button class="tab-btn active" data-tab="composition">Composition</button> |
| <button class="tab-btn" data-tab="sound-design">Sound Design</button> |
| <button class="tab-btn" data-tab="mixing">Mixing</button> |
| <button class="tab-btn" data-tab="mastering">Mastering</button> |
| <button class="tab-btn" data-tab="sampling">Sampling</button> |
| <button class="tab-btn" data-tab="patterns">Patterns</button> |
| <button class="tab-btn" data-tab="url-scraping">URL Scraping</button> |
| </div> |
|
|
| <div id="composition-tab"> |
| <div class="input-group"> |
| <label>Describe your musical idea:</label> |
| <textarea class="input-field" placeholder="Create a cinematic orchestral piece with electronic elements, dark ambient textures, and a driving beat at 128 BPM..."></textarea> |
| <div class="help-text">Describe the mood, genre, tempo, and instruments you want</div> |
| </div> |
|
|
| <div class="input-group"> |
| <label>Generate from text:</label> |
| <input type="text" class="input-field" placeholder="A futuristic synthwave track with retro drums and lush pads..."> |
| <div class="help-text">Enter a prompt to generate new music</div> |
| </div> |
|
|
| <div class="visualizer"> |
| <div class="waveform" id="waveform"></div> |
| </div> |
|
|
| <div class="input-group"> |
| <label>Chord Progression:</label> |
| <input type="text" class="input-field" placeholder="Am - F - C - G - Am - F - G - Am"> |
| <div class="help-text">Enter chords separated by dashes or commas</div> |
| </div> |
|
|
| <div class="input-group"> |
| <label>Drum Pattern:</label> |
| <input type="text" class="input-field" placeholder="Kick on 1 and 3, snare on 2 and 4, hats on every beat..."> |
| <div class="help-text">Describe your desired drum pattern</div> |
| </div> |
| </div> |
|
|
| <div id="url-scraping-tab" class="url-scraping-tab"> |
| <div class="input-group"> |
| <label>Enter URL to scrape audio from:</label> |
| <div class="url-input-group"> |
| <input type="text" class="input-field url-input" placeholder="https://example.com/audio-page"> |
| <button class="btn btn-primary scrape-btn">Scrape Audio</button> |
| </div> |
| <div class="help-text">Enter a URL containing audio files to extract them</div> |
| </div> |
|
|
| <div class="scraped-audio-list"> |
| <div class="scraped-item"> |
| <span class="scraped-url">https://example.com/song.mp3</span> |
| <button class="download-btn">Download</button> |
| </div> |
| <div class="scraped-item"> |
| <span class="scraped-url">https://music-site.com/track.wav</span> |
| <button class="download-btn">Download</button> |
| </div> |
| <div class="scraped-item"> |
| <span class="scraped-url">https://radio-station.org/broadcast.m4a</span> |
| <button class="download-btn">Download</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="panel"> |
| <div class="panel-header"> |
| <h2 class="panel-title">Track Manager</h2> |
| <div class="status-item" id="trackCount">4 Tracks</div> |
| </div> |
|
|
| <div class="track-list" id="trackList"> |
| <div class="track-item"> |
| <div class="track-icon">âª</div> |
| <div class="track-info"> |
| <div class="track-name">Main Melody</div> |
| <div class="track-type">Synth Lead</div> |
| </div> |
| <div class="track-actions"> |
| <button class="action-btn play-btn" data-track="0">â¶</button> |
| <button class="action-btn">â</button> |
| </div> |
| </div> |
|
|
| <div class="track-item"> |
| <div class="track-icon">ð¥</div> |
| <div class="track-info"> |
| <div class="track-name">Drum Kit</div> |
| <div class="track-type">Percussion</div> |
| </div> |
| <div class="track-actions"> |
| <button class="action-btn play-btn" data-track="1">â¶</button> |
| <button class="action-btn">â</button> |
| </div> |
| </div> |
|
|
| <div class="track-item"> |
| <div class="track-icon">ð¸</div> |
| <div class="track-info"> |
| <div class="track-name">Bass Line</div> |
| <div class="track-type">Sub Bass</div> |
| </div> |
| <div class="track-actions"> |
| <button class="action-btn play-btn" data-track="2">â¶</button> |
| <button class="action-btn">â</button> |
| </div> |
| </div> |
|
|
| <div class="track-item"> |
| <div class="track-icon">ð¤</div> |
| <div class="track-info"> |
| <div class="track-name">Vocal Harmony</div> |
| <div class="track-type">Vox</div> |
| </div> |
| <div class="track-actions"> |
| <button class="action-btn play-btn" data-track="3">â¶</button> |
| <button class="action-btn">â</button> |
| </div> |
| </div> |
| </div> |
|
|
| <button class="btn btn-primary" style="width: 100%; margin-top: 20px;">Add New Track</button> |
| <div class="help-text" style="text-align: center; margin-top: 10px;">Click to add more tracks to your project</div> |
| </div> |
| </div> |
|
|
| <div class="panel"> |
| <div class="panel-header"> |
| <h2 class="panel-title">Mixer Console</h2> |
| <div class="status-item">Master: -6dB</div> |
| </div> |
|
|
| <div class="mixer-channel"> |
| <div class="channel-label">Drums</div> |
| <div class="slider-container"> |
| <input type="range" min="0" max="100" value="85" class="slider" data-channel="drums"> |
| </div> |
| <div class="level-meter"> |
| <div class="level-fill" style="height: 70%;"></div> |
| </div> |
| <button class="action-btn mute-btn" data-channel="drums">M</button> |
| <button class="action-btn solo-btn" data-channel="drums">S</button> |
| </div> |
|
|
| <div class="mixer-channel"> |
| <div class="channel-label">Bass</div> |
| <div class="slider-container"> |
| <input type="range" min="0" max="100" value="75" class="slider" data-channel="bass"> |
| </div> |
| <div class="level-meter"> |
| <div class="level-fill" style="height: 60%;"></div> |
| </div> |
| <button class="action-btn mute-btn" data-channel="bass">M</button> |
| <button class="action-btn solo-btn" data-channel="bass">S</button> |
| </div> |
|
|
| <div class="mixer-channel"> |
| <div class="channel-label">Synth</div> |
| <div class="slider-container"> |
| <input type="range" min="0" max="100" value="80" class="slider" data-channel="synth"> |
| </div> |
| <div class="level-meter"> |
| <div class="level-fill" style="height: 85%;"></div> |
| </div> |
| <button class="action-btn mute-btn" data-channel="synth">M</button> |
| <button class="action-btn solo-btn" data-channel="synth">S</button> |
| </div> |
|
|
| <div class="mixer-channel"> |
| <div class="channel-label">Vox</div> |
| <div class="slider-container"> |
| <input type="range" min="0" max="100" value="90" class="slider" data-channel="vox"> |
| </div> |
| <div class="level-meter"> |
| <div class="level-fill" style="height: 50%;"></div> |
| </div> |
| <button class="action-btn mute-btn" data-channel="vox">M</button> |
| <button class="action-btn solo-btn" data-channel="vox">S</button> |
| </div> |
|
|
| <div class="transport-controls"> |
| <button class="transport-btn secondary" id="rewindBtn">â®</button> |
| <button class="transport-btn secondary" id="prevBtn">âª</button> |
| <button class="transport-btn" id="playBtn">â¶</button> |
| <button class="transport-btn secondary" id="nextBtn">â©</button> |
| <button class="transport-btn secondary" id="stopBtn">â¹</button> |
| </div> |
| <div class="help-text" style="text-align: center; margin-top: 15px;">Transport controls: Play, Pause, Stop, Skip</div> |
| </div> |
|
|
| <div class="sound-library"> |
| <div class="panel-header"> |
| <h2 class="panel-title">Sound Library</h2> |
| <div class="status-item" id="libraryCount">0 Sounds</div> |
| </div> |
| <div class="help-text" style="margin-bottom: 15px;">Click sounds to add them to your project</div> |
| <div class="library-grid" id="libraryGrid"> |
| |
| </div> |
| </div> |
|
|
| <footer> |
| MorphoLoop AI Music Production Suite v2.1 | Powered by Neural Networks | All processing happens locally in your browser |
| </footer> |
| </div> |
|
|
| <script> |
| |
| const waveform = document.getElementById('waveform'); |
| for (let i = 0; i < 64; i++) { |
| const bar = document.createElement('div'); |
| bar.className = 'bar'; |
| bar.style.height = `${Math.random() * 80 + 10}%`; |
| waveform.appendChild(bar); |
| } |
| |
| |
| function animateWaveform() { |
| const bars = waveform.querySelectorAll('.bar'); |
| bars.forEach(bar => { |
| const newHeight = Math.random() * 80 + 10; |
| bar.style.height = `${newHeight}%`; |
| bar.style.background = `linear-gradient(to top, #ff00${Math.floor(Math.random()*255).toString(16).padStart(2, '0')}, #00${Math.floor(Math.random()*255).toString(16).padStart(2, '0')}ff)`; |
| }); |
| requestAnimationFrame(animateWaveform); |
| } |
| animateWaveform(); |
| |
| |
| function updateLevelMeters() { |
| const levelFills = document.querySelectorAll('.level-fill'); |
| levelFills.forEach(fill => { |
| const currentHeight = parseInt(fill.style.height) || 0; |
| const newHeight = Math.max(10, Math.min(100, currentHeight + (Math.random() * 20 - 10))); |
| fill.style.height = `${newHeight}%`; |
| }); |
| requestAnimationFrame(updateLevelMeters); |
| } |
| updateLevelMeters(); |
| |
| |
| const tabBtns = document.querySelectorAll('.tab-btn'); |
| const tabContents = document.querySelectorAll('[id$="-tab"]'); |
| |
| tabBtns.forEach(btn => { |
| btn.addEventListener('click', () => { |
| |
| tabBtns.forEach(b => b.classList.remove('active')); |
| |
| btn.classList.add('active'); |
| |
| |
| tabContents.forEach(content => { |
| content.style.display = 'none'; |
| }); |
| |
| |
| const tabId = btn.getAttribute('data-tab'); |
| document.getElementById(`${tabId}-tab`).style.display = 'block'; |
| }); |
| }); |
| |
| |
| document.getElementById('composition-tab').style.display = 'block'; |
| |
| |
| let audioContext = null; |
| let masterGain = null; |
| let trackPlayers = {}; |
| let isPlaying = false; |
| let currentTrackIndex = 0; |
| |
| |
| function initAudio() { |
| try { |
| audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| masterGain = audioContext.createGain(); |
| masterGain.connect(audioContext.destination); |
| masterGain.gain.value = 0.7; |
| } catch (e) { |
| console.error('Web Audio API is not supported in this browser:', e); |
| alert('Web Audio API is not supported in your browser. Some features may not work.'); |
| } |
| } |
| |
| |
| function createTrackPlayer(trackIndex) { |
| if (!audioContext) return null; |
| |
| const oscillator = audioContext.createOscillator(); |
| const gainNode = audioContext.createGain(); |
| |
| |
| const frequencies = [220, 330, 440, 550, 660, 770, 880, 990]; |
| const types = ['sine', 'square', 'triangle', 'sawtooth']; |
| |
| oscillator.type = types[trackIndex % types.length]; |
| oscillator.frequency.value = frequencies[trackIndex % frequencies.length]; |
| |
| gainNode.gain.value = 0.3; |
| |
| oscillator.connect(gainNode); |
| gainNode.connect(masterGain); |
| |
| return { |
| oscillator, |
| gainNode, |
| isPlaying: false, |
| play: function() { |
| if (!this.isPlaying) { |
| this.oscillator.start(); |
| this.isPlaying = true; |
| } |
| }, |
| pause: function() { |
| if (this.isPlaying) { |
| this.oscillator.stop(); |
| this.oscillator.disconnect(); |
| this.oscillator = audioContext.createOscillator(); |
| this.oscillator.type = types[trackIndex % types.length]; |
| this.oscillator.frequency.value = frequencies[trackIndex % frequencies.length]; |
| this.oscillator.connect(this.gainNode); |
| this.isPlaying = false; |
| } |
| }, |
| setVolume: function(volume) { |
| this.gainNode.gain.value = volume; |
| } |
| }; |
| } |
| |
| |
| function initTrackPlayers() { |
| const tracks = document.querySelectorAll('.track-item'); |
| tracks.forEach((track, index) => { |
| trackPlayers[index] = createTrackPlayer(index); |
| }); |
| } |
| |
| |
| const playBtn = document.getElementById('playBtn'); |
| const stopBtn = document.getElementById('stopBtn'); |
| const prevBtn = document.getElementById('prevBtn'); |
| const nextBtn = document.getElementById('nextBtn'); |
| const rewindBtn = document.getElementById('rewindBtn'); |
| |
| playBtn.addEventListener('click', () => { |
| if (!isPlaying) { |
| |
| isPlaying = true; |
| playBtn.textContent = 'â¸'; |
| |
| |
| if (trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| } else { |
| |
| isPlaying = false; |
| playBtn.textContent = 'â¶'; |
| |
| |
| if (trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| } |
| }); |
| |
| stopBtn.addEventListener('click', () => { |
| if (isPlaying) { |
| isPlaying = false; |
| playBtn.textContent = 'â¶'; |
| |
| |
| Object.values(trackPlayers).forEach(player => { |
| if (player.isPlaying) { |
| player.pause(); |
| } |
| }); |
| } |
| currentTrackIndex = 0; |
| }); |
| |
| nextBtn.addEventListener('click', () => { |
| const totalTracks = Object.keys(trackPlayers).length; |
| if (totalTracks > 0) { |
| |
| if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| |
| currentTrackIndex = (currentTrackIndex + 1) % totalTracks; |
| |
| |
| if (isPlaying && trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| } |
| }); |
| |
| prevBtn.addEventListener('click', () => { |
| const totalTracks = Object.keys(trackPlayers).length; |
| if (totalTracks > 0) { |
| |
| if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| |
| currentTrackIndex = (currentTrackIndex - 1 + totalTracks) % totalTracks; |
| |
| |
| if (isPlaying && trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| } |
| }); |
| |
| rewindBtn.addEventListener('click', () => { |
| |
| if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| |
| currentTrackIndex = 0; |
| |
| |
| if (isPlaying && trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| }); |
| |
| |
| document.querySelectorAll('.play-btn').forEach((btn, index) => { |
| btn.addEventListener('click', () => { |
| |
| if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| |
| currentTrackIndex = index; |
| |
| if (!isPlaying) { |
| isPlaying = true; |
| playBtn.textContent = 'â¸'; |
| } |
| |
| |
| if (trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| }); |
| }); |
| |
| |
| document.querySelectorAll('.slider').forEach(slider => { |
| slider.addEventListener('input', () => { |
| const channel = slider.getAttribute('data-channel'); |
| const volume = slider.value / 100; |
| |
| |
| |
| console.log(`${channel} volume changed to: ${volume}`); |
| }); |
| }); |
| |
| document.querySelectorAll('.mute-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| const channel = btn.getAttribute('data-channel'); |
| btn.textContent = btn.textContent === 'M' ? 'U' : 'M'; |
| console.log(`${channel} ${btn.textContent === 'M' ? 'unmuted' : 'muted'}`); |
| }); |
| }); |
| |
| document.querySelectorAll('.solo-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| const channel = btn.getAttribute('data-channel'); |
| btn.textContent = btn.textContent === 'S' ? 'U' : 'S'; |
| console.log(`${channel} ${btn.textContent === 'S' ? 'soloed' : 'unsoloed'}`); |
| }); |
| }); |
| |
| |
| window.addEventListener('load', () => { |
| initAudio(); |
| initTrackPlayers(); |
| }); |
| |
| |
| const uploadArea = document.getElementById('uploadArea'); |
| const fileInput = document.getElementById('fileInput'); |
| const progressContainer = document.getElementById('progressContainer'); |
| const progressBar = document.getElementById('progressBar'); |
| const progressText = document.getElementById('progressText'); |
| |
| uploadArea.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| uploadArea.style.borderColor = '#ff00cc'; |
| uploadArea.style.backgroundColor = 'rgba(50, 30, 80, 0.3)'; |
| }); |
| |
| uploadArea.addEventListener('dragleave', () => { |
| uploadArea.style.borderColor = 'rgba(100, 100, 200, 0.5)'; |
| uploadArea.style.backgroundColor = 'transparent'; |
| }); |
| |
| uploadArea.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| uploadArea.style.borderColor = 'rgba(100, 100, 200, 0.5)'; |
| uploadArea.style.backgroundColor = 'transparent'; |
| |
| if (e.dataTransfer.files.length) { |
| handleFileUpload(e.dataTransfer.files[0]); |
| } |
| }); |
| |
| fileInput.addEventListener('change', (e) => { |
| if (e.target.files.length) { |
| handleFileUpload(e.target.files[0]); |
| } |
| }); |
| |
| function handleFileUpload(file) { |
| |
| progressContainer.style.display = 'block'; |
| |
| |
| let progress = 0; |
| const interval = setInterval(() => { |
| progress += Math.random() * 10; |
| if (progress >= 100) { |
| progress = 100; |
| clearInterval(interval); |
| |
| |
| setTimeout(() => { |
| generateTracksFromSeparation(); |
| }, 500); |
| } |
| progressBar.style.width = `${progress}%`; |
| progressText.textContent = `Processing audio: ${Math.round(progress)}%`; |
| }, 200); |
| } |
| |
| function generateTracksFromSeparation() { |
| |
| const numTracks = Math.floor(Math.random() * 8) + 4; |
| |
| |
| document.getElementById('trackCount').textContent = `${numTracks} Tracks`; |
| |
| |
| const trackList = document.getElementById('trackList'); |
| trackList.innerHTML = ''; |
| |
| |
| const instruments = ['Drums', 'Bass', 'Guitar', 'Piano', 'Strings', 'Brass', 'Synth', 'Vocals', 'Pad', 'Lead', 'Arp', 'FX']; |
| const icons = ['ð¥', 'ð¸', 'ð¸', 'ð¹', 'ð»', '-Trumpet', 'ð¸', 'ð¤', 'ð§', 'ðµ', 'ð¼', 'â¡']; |
| |
| for (let i = 0; i < numTracks; i++) { |
| const trackItem = document.createElement('div'); |
| trackItem.className = 'track-item'; |
| |
| const randomIndex = Math.floor(Math.random() * instruments.length); |
| const instrument = instruments[randomIndex]; |
| const icon = icons[randomIndex]; |
| |
| trackItem.innerHTML = ` |
| <div class="track-icon">${icon}</div> |
| <div class="track-info"> |
| <div class="track-name">${instrument} Track ${i+1}</div> |
| <div class="track-type">${instrument}</div> |
| </div> |
| <div class="track-actions"> |
| <button class="action-btn play-btn" data-track="${i}">â¶</button> |
| <button class="action-btn">â</button> |
| </div> |
| `; |
| |
| trackList.appendChild(trackItem); |
| } |
| |
| |
| trackPlayers = {}; |
| initTrackPlayers(); |
| |
| |
| document.querySelectorAll('.play-btn').forEach((btn, index) => { |
| btn.addEventListener('click', () => { |
| |
| if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) { |
| trackPlayers[currentTrackIndex].pause(); |
| } |
| |
| currentTrackIndex = index; |
| |
| if (!isPlaying) { |
| isPlaying = true; |
| playBtn.textContent = 'â¸'; |
| } |
| |
| |
| if (trackPlayers[currentTrackIndex]) { |
| trackPlayers[currentTrackIndex].play(); |
| } |
| }); |
| }); |
| |
| |
| updateMixerChannels(numTracks); |
| |
| |
| addToSoundLibrary(numTracks); |
| } |
| |
| function updateMixerChannels(numTracks) { |
| const mixerPanel = document.querySelector('.panel:last-child'); |
| const existingChannels = mixerPanel.querySelectorAll('.mixer-channel'); |
| |
| |
| for (let i = 0; i < existingChannels.length - 5; i++) { |
| existingChannels[i].remove(); |
| } |
| |
| |
| const instruments = ['Drums', 'Bass', 'Synth', 'Vox', 'Guitar', 'Piano', 'Strings', 'Brass', 'Pad', 'Lead', 'Arp', 'FX']; |
| |
| for (let i = 4; i < Math.min(numTracks, 8); i++) { |
| const channel = document.createElement('div'); |
| channel.className = 'mixer-channel'; |
| |
| const randomIndex = Math.floor(Math.random() * instruments.length); |
| const instrument = instruments[randomIndex]; |
| |
| channel.innerHTML = ` |
| <div class="channel-label">${instrument}</div> |
| <div class="slider-container"> |
| <input type="range" min="0" max="100" value="${Math.floor(Math.random() * 30) + 70}" class="slider" data-channel="${instrument.toLowerCase()}"> |
| </div> |
| <div class="level-meter"> |
| <div class="level-fill" style="height: ${Math.floor(Math.random() * 50) + 30}%;"></div> |
| </div> |
| <button class="action-btn mute-btn" data-channel="${instrument.toLowerCase()}">M</button> |
| <button class="action-btn solo-btn" data-channel="${instrument.toLowerCase()}">S</button> |
| `; |
| |
| mixerPanel.insertBefore(channel, document.querySelector('.transport-controls')); |
| } |
| |
| |
| document.querySelectorAll('.slider').forEach(slider => { |
| slider.addEventListener('input', () => { |
| const channel = slider.getAttribute('data-channel'); |
| const volume = slider.value / 100; |
| console.log(`${channel} volume changed to: ${volume}`); |
| }); |
| }); |
| |
| document.querySelectorAll('.mute-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| const channel = btn.getAttribute('data-channel'); |
| btn.textContent = btn.textContent === 'M' ? 'U' : 'M'; |
| console.log(`${channel} ${btn.textContent === 'M' ? 'unmuted' : 'muted'}`); |
| }); |
| }); |
| |
| document.querySelectorAll('.solo-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| const channel = btn.getAttribute('data-channel'); |
| btn.textContent = btn.textContent === 'S' ? 'U' : 'S'; |
| console.log(`${channel} ${btn.textContent === 'S' ? 'soloed' : 'unsoloed'}`); |
| }); |
| }); |
| } |
| |
| function addToSoundLibrary(numSounds) { |
| const libraryGrid = document.getElementById('libraryGrid'); |
| const instruments = ['Drums', 'Bass', 'Guitar', 'Piano', 'Strings', 'Brass', 'Synth', 'Vocals', 'Pad', 'Lead', 'Arp', 'FX']; |
| const icons = ['ð¥', 'ð¸', 'ð¸', 'ð¹', 'ð»', '-Trumpet', 'ð¸', 'ð¤', 'ð§', 'ðµ', 'ð¼', 'â¡']; |
| |
| for (let i = 0; i < numSounds; i++) { |
| const randomIndex = Math.floor(Math.random() * instruments.length); |
| const instrument = instruments[randomIndex]; |
| const icon = icons[randomIndex]; |
| |
| const item = document.createElement('div'); |
| item.className = 'library-item'; |
| item.innerHTML = ` |
| <div class="library-icon">${icon}</div> |
| <div class="library-name">${instrument} ${i+1}</div> |
| <div class="library-type">${instrument}</div> |
| `; |
| |
| item.addEventListener('click', () => { |
| alert(`Added ${instrument} ${i+1} to project!`); |
| }); |
| |
| libraryGrid.appendChild(item); |
| } |
| |
| |
| document.getElementById('libraryCount').textContent = `${libraryGrid.children.length} Sounds`; |
| } |
| |
| |
| document.querySelector('.scrape-btn').addEventListener('click', () => { |
| const urlInput = document.querySelector('.url-input'); |
| const url = urlInput.value.trim(); |
| |
| if (!url) { |
| alert('Please enter a valid URL'); |
| return; |
| } |
| |
| |
| const progressContainer = document.getElementById('progressContainer'); |
| const progressBar = document.getElementById('progressBar'); |
| const progressText = document.getElementById('progressText'); |
| |
| progressContainer.style.display = 'block'; |
| progressBar.style.width = '0%'; |
| progressText.textContent = 'Scraping audio from URL...'; |
| |
| let progress = 0; |
| const interval = setInterval(() => { |
| progress += Math.random() * 15; |
| if (progress >= 100) { |
| progress = 100; |
| clearInterval(interval); |
| |
| |
| setTimeout(() => { |
| progressContainer.style.display = 'none'; |
| alert(`Successfully scraped audio from: ${url}`); |
| |
| |
| const scrapedList = document.querySelector('.scraped-audio-list'); |
| const newItem = document.createElement('div'); |
| newItem.className = 'scraped-item'; |
| newItem.innerHTML = ` |
| <span class="scraped-url">${url}</span> |
| <button class="download-btn">Download</button> |
| `; |
| scrapedList.prepend(newItem); |
| |
| |
| newItem.querySelector('.download-btn').addEventListener('click', () => { |
| alert(`Downloading: ${url}`); |
| }); |
| }, 500); |
| } |
| progressBar.style.width = `${progress}%`; |
| progressText.textContent = `Scraping audio: ${Math.round(progress)}%`; |
| }, 200); |
| }); |
| </script> |
| </body> |
| </html> |