MorphoLoop888 / index.html
Solomon7890's picture
Update 1
7597537 verified
Raw
History Blame Contribute Delete
44.8 kB
<!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">
<!-- Sound library items will be added here -->
</div>
</div>
<footer>
MorphoLoop AI Music Production Suite v2.1 | Powered by Neural Networks | All processing happens locally in your browser
</footer>
</div>
<script>
// Create waveform visualization
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);
}
// Animate waveform
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();
// Simulate level meters
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();
// Tab switching functionality
const tabBtns = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('[id$="-tab"]');
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
// Remove active class from all buttons
tabBtns.forEach(b => b.classList.remove('active'));
// Add active class to clicked button
btn.classList.add('active');
// Hide all tab contents
tabContents.forEach(content => {
content.style.display = 'none';
});
// Show the corresponding tab content
const tabId = btn.getAttribute('data-tab');
document.getElementById(`${tabId}-tab`).style.display = 'block';
});
});
// Initialize the first tab as active
document.getElementById('composition-tab').style.display = 'block';
// Audio context and playback variables
let audioContext = null;
let masterGain = null;
let trackPlayers = {};
let isPlaying = false;
let currentTrackIndex = 0;
// Initialize Web Audio API
function initAudio() {
try {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
masterGain = audioContext.createGain();
masterGain.connect(audioContext.destination);
masterGain.gain.value = 0.7; // Default master volume
} 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.');
}
}
// Create a simple oscillator-based player for demonstration
function createTrackPlayer(trackIndex) {
if (!audioContext) return null;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
// Different frequencies for different instruments
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; // Default track volume
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;
}
};
}
// Initialize players for default tracks
function initTrackPlayers() {
const tracks = document.querySelectorAll('.track-item');
tracks.forEach((track, index) => {
trackPlayers[index] = createTrackPlayer(index);
});
}
// Transport controls
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) {
// Start playback
isPlaying = true;
playBtn.textContent = '⏸';
// Play the current track
if (trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
} else {
// Pause playback
isPlaying = false;
playBtn.textContent = '▶';
// Pause the current track
if (trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].pause();
}
}
});
stopBtn.addEventListener('click', () => {
if (isPlaying) {
isPlaying = false;
playBtn.textContent = '▶';
// Stop all playing tracks
Object.values(trackPlayers).forEach(player => {
if (player.isPlaying) {
player.pause();
}
});
}
currentTrackIndex = 0;
});
nextBtn.addEventListener('click', () => {
const totalTracks = Object.keys(trackPlayers).length;
if (totalTracks > 0) {
// Stop current track if playing
if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) {
trackPlayers[currentTrackIndex].pause();
}
currentTrackIndex = (currentTrackIndex + 1) % totalTracks;
// Play the next track if we're in play mode
if (isPlaying && trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
}
});
prevBtn.addEventListener('click', () => {
const totalTracks = Object.keys(trackPlayers).length;
if (totalTracks > 0) {
// Stop current track if playing
if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) {
trackPlayers[currentTrackIndex].pause();
}
currentTrackIndex = (currentTrackIndex - 1 + totalTracks) % totalTracks;
// Play the previous track if we're in play mode
if (isPlaying && trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
}
});
rewindBtn.addEventListener('click', () => {
// Stop current track if playing
if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) {
trackPlayers[currentTrackIndex].pause();
}
currentTrackIndex = 0;
// Play the first track if we're in play mode
if (isPlaying && trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
});
// Individual track play buttons
document.querySelectorAll('.play-btn').forEach((btn, index) => {
btn.addEventListener('click', () => {
// Stop current track if playing
if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) {
trackPlayers[currentTrackIndex].pause();
}
currentTrackIndex = index;
if (!isPlaying) {
isPlaying = true;
playBtn.textContent = '⏸';
}
// Play the selected track
if (trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
});
});
// Mixer controls
document.querySelectorAll('.slider').forEach(slider => {
slider.addEventListener('input', () => {
const channel = slider.getAttribute('data-channel');
const volume = slider.value / 100;
// In a real implementation, this would control the actual audio
// For now, just log the change
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'}`);
});
});
// Initialize audio when page loads
window.addEventListener('load', () => {
initAudio();
initTrackPlayers();
});
// File upload handling
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) {
// Show progress container
progressContainer.style.display = 'block';
// Simulate processing progress
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
// After processing, generate tracks
setTimeout(() => {
generateTracksFromSeparation();
}, 500);
}
progressBar.style.width = `${progress}%`;
progressText.textContent = `Processing audio: ${Math.round(progress)}%`;
}, 200);
}
function generateTracksFromSeparation() {
// Simulate different number of separated tracks
const numTracks = Math.floor(Math.random() * 8) + 4; // Between 4-12 tracks
// Update track count
document.getElementById('trackCount').textContent = `${numTracks} Tracks`;
// Clear existing tracks
const trackList = document.getElementById('trackList');
trackList.innerHTML = '';
// Create new tracks
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);
}
// Reinitialize track players
trackPlayers = {};
initTrackPlayers();
// Reattach event listeners to new play buttons
document.querySelectorAll('.play-btn').forEach((btn, index) => {
btn.addEventListener('click', () => {
// Stop current track if playing
if (trackPlayers[currentTrackIndex] && trackPlayers[currentTrackIndex].isPlaying) {
trackPlayers[currentTrackIndex].pause();
}
currentTrackIndex = index;
if (!isPlaying) {
isPlaying = true;
playBtn.textContent = '⏸';
}
// Play the selected track
if (trackPlayers[currentTrackIndex]) {
trackPlayers[currentTrackIndex].play();
}
});
});
// Update mixer channels to match new track count
updateMixerChannels(numTracks);
// Add to sound library
addToSoundLibrary(numTracks);
}
function updateMixerChannels(numTracks) {
const mixerPanel = document.querySelector('.panel:last-child');
const existingChannels = mixerPanel.querySelectorAll('.mixer-channel');
// Remove existing channels except the last one (transport controls)
for (let i = 0; i < existingChannels.length - 5; i++) { // Keep 4 original channels
existingChannels[i].remove();
}
// Add new channels based on number of tracks
const instruments = ['Drums', 'Bass', 'Synth', 'Vox', 'Guitar', 'Piano', 'Strings', 'Brass', 'Pad', 'Lead', 'Arp', 'FX'];
for (let i = 4; i < Math.min(numTracks, 8); i++) { // Start from index 4 to add after original channels
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'));
}
// Reattach event listeners to new mixer 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);
}
// Update library count
document.getElementById('libraryCount').textContent = `${libraryGrid.children.length} Sounds`;
}
// URL scraping functionality
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;
}
// Simulate scraping process
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);
// After scraping, show results
setTimeout(() => {
progressContainer.style.display = 'none';
alert(`Successfully scraped audio from: ${url}`);
// Add a new scraped item to the list
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);
// Add event listener to the new download button
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>