// ═══════════════════════════════════════════════════════════ // FILE: server.js - Complete Backend with Smart Instruments // ═══════════════════════════════════════════════════════════ const express = require('express'); const multer = require('multer'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); const { exec } = require('child_process'); const util = require('util'); const execPromise = util.promisify(exec); const app = express(); const PORT = process.env.PORT || 7860; app.use(cors()); app.use(express.json()); const uploadsDir = path.join(__dirname, 'uploads'); const outputsDir = path.join(__dirname, 'outputs'); if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true }); if (!fs.existsSync(outputsDir)) fs.mkdirSync(outputsDir, { recursive: true }); const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, uploadsDir), filename: (req, file, cb) => cb(null, `${Date.now()}-${file.originalname}`) }); const upload = multer({ storage: storage, limits: { fileSize: 50 * 1024 * 1024 } }); // 🎹 ═══════════════════════════════════════════════════════════ // INSTRUMENT LIBRARY - Categorized by Mood // ═══════════════════════════════════════════════════════════ const INSTRUMENTS = { // 💔 SAD INSTRUMENTS sad_piano: { name: "Sad Piano", mood: "sad", command: "aevalsrc='0.13*sin(2*PI*(220+5*sin(0.08*t))*t)*exp(-0.15*mod(t,4))':d=12,aecho=0.7:0.85:600:0.35,lowpass=f=3000" }, melancholic_violin: { name: "Melancholic Violin", mood: "sad", command: "aevalsrc='0.12*sin(2*PI*330*t)*sin(2*PI*0.3*t)*exp(-0.1*mod(t,5))':d=12,vibrato=f=0.15:d=0.5,aecho=0.6:0.8:500:0.3" }, lonely_cello: { name: "Lonely Cello", mood: "sad", command: "aevalsrc='0.14*sin(2*PI*165*t)*sin(2*PI*0.2*t)':d=12,chorus=0.4:0.7:40:0.3:0.2:2,lowpass=f=2500" }, tearful_flute: { name: "Tearful Flute", mood: "sad", command: "aevalsrc='0.11*sin(2*PI*440*t)*sin(2*PI*1.5*t)*exp(-0.2*mod(t,3))':d=12,aecho=0.5:0.75:400:0.3,vibrato=f=0.2:d=0.4" }, // ❤️ ROMANTIC INSTRUMENTS romantic_piano: { name: "Romantic Piano", mood: "romantic", command: "aevalsrc='0.12*(sin(2*PI*330*t)+sin(2*PI*415*t))*exp(-0.12*mod(t,3.5))':d=12,aecho=0.6:0.8:450:0.35,chorus=0.3:0.6:35:0.25:0.15:2" }, love_strings: { name: "Love Strings", mood: "romantic", command: "aevalsrc='0.13*(sin(2*PI*440*t)+sin(2*PI*554*t))*sin(2*PI*0.4*t)':d=12,vibrato=f=0.18:d=0.45,aecho=0.5:0.7:350:0.3" }, gentle_harp: { name: "Gentle Harp", mood: "romantic", command: "aevalsrc='0.11*sin(2*PI*(660+8*sin(0.15*t))*t)*exp(-0.25*mod(t,2.5))':d=12,chorus=0.4:0.7:45:0.3:0.2:2,aecho=0.6:0.8:300:0.35" }, sweet_bells: { name: "Sweet Bells", mood: "romantic", command: "aevalsrc='0.09*sin(2*PI*880*t)*exp(-0.4*mod(t,2))':d=12,aecho=0.5:0.75:250:0.4,vibrato=f=0.12:d=0.3" }, // 💔❤️ SAD ROMANTIC MIX bittersweet_piano: { name: "Bittersweet Piano", mood: "sad_romantic", command: "aevalsrc='0.13*(sin(2*PI*247*t)+sin(2*PI*330*t))*exp(-0.15*mod(t,4))*(1+0.3*sin(0.5*t))':d=12,aecho=0.7:0.85:500:0.35,vibrato=f=0.1:d=0.4" }, nostalgic_violin: { name: "Nostalgic Violin", mood: "sad_romantic", command: "aevalsrc='0.12*sin(2*PI*370*t)*sin(2*PI*0.35*t)*(1+0.2*sin(0.3*t))':d=12,chorus=0.5:0.75:40:0.3:0.2:2,aecho=0.6:0.8:450:0.3" }, heartbreak_strings: { name: "Heartbreak Strings", mood: "sad_romantic", command: "aevalsrc='0.13*(sin(2*PI*220*t)+sin(2*PI*330*t))*exp(-0.1*mod(t,5))':d=12,vibrato=f=0.15:d=0.5,lowpass=f=3500" }, // 🌙 AMBIENT/DREAMY INSTRUMENTS dreamy_pad: { name: "Dreamy Pad", mood: "ambient", command: "aevalsrc='0.11*(sin(2*PI*165*t)+sin(2*PI*220*t)+sin(2*PI*330*t))':d=12,aphaser=in_gain=0.4:out_gain=0.7:delay=3:decay=0.4:speed=0.25,aecho=0.5:0.7:400:0.3" }, ethereal_synth: { name: "Ethereal Synth", mood: "ambient", command: "aevalsrc='0.1*(sin(2*PI*220*t)+sin(2*PI*277*t))*sin(2*PI*0.5*t)':d=12,chorus=0.6:0.85:50:0.4:0.25:2,aecho=0.6:0.8:500:0.3" }, celestial_bells: { name: "Celestial Bells", mood: "ambient", command: "aevalsrc='0.09*(sin(2*PI*880*t)+sin(2*PI*1108*t))*exp(-0.3*t)':d=12,aecho=0.7:0.9:600:0.4,aphaser=in_gain=0.3:out_gain=0.6:delay=2:decay=0.3:speed=0.2" }, // 🎸 CHILL/LOFI INSTRUMENTS lofi_guitar: { name: "LoFi Guitar", mood: "chill", command: "aevalsrc='0.12*sin(2*PI*196*t)*exp(-0.3*mod(t,3))*(1+0.15*random(0))':d=12,chorus=0.5:0.7:40:0.25:0.15:2,lowpass=f=4000" }, jazzy_piano: { name: "Jazzy Piano", mood: "chill", command: "aevalsrc='0.11*(sin(2*PI*262*t)+sin(2*PI*330*t))*exp(-0.2*mod(t,2.5))':d=12,vibrato=f=0.18:d=0.4,aecho=0.4:0.6:300:0.25" }, smooth_sax: { name: "Smooth Saxophone", mood: "chill", command: "aevalsrc='0.12*sin(2*PI*294*t)*sin(2*PI*2*t)':d=12,vibrato=f=0.25:d=0.5,chorus=0.4:0.7:45:0.3:0.2:2" }, // ⚡ UPBEAT/ENERGETIC INSTRUMENTS bright_synth: { name: "Bright Synth", mood: "upbeat", command: "aevalsrc='0.11*(sin(2*PI*440*t)+sin(2*PI*554*t)+sin(2*PI*659*t))':d=12,chorus=0.6:0.8:50:0.35:0.25:2,highpass=f=200" }, sparkle_bells: { name: "Sparkle Bells", mood: "upbeat", command: "aevalsrc='0.1*(sin(2*PI*1320*t)+sin(2*PI*1760*t))*exp(-0.5*mod(t,1.5))':d=12,aecho=0.4:0.6:150:0.3,highpass=f=500" }, happy_piano: { name: "Happy Piano", mood: "upbeat", command: "aevalsrc='0.11*(sin(2*PI*523*t)+sin(2*PI*659*t))*exp(-0.25*mod(t,2))':d=12,chorus=0.5:0.7:40:0.3:0.2:2" } }; // 🎯 ═══════════════════════════════════════════════════════════ // SMART EFFECT CONFIGURATIONS - Each effect has mood-matched instruments // ═══════════════════════════════════════════════════════════ const EFFECTS = { lofi: { name: "LoFi Hip Hop", description: "Vintage warmth with jazzy chill vibes", baseCommand: `-af "asetrate=44100*0.90,aresample=44100,bass=g=5:f=85:width_type=h:width=100,treble=g=-3:f=8000,equalizer=f=200:t=q:width=1.5:g=-2,equalizer=f=1000:t=q:width=2:g=2,acompressor=threshold=-20dB:ratio=3:attack=10:release=120:makeup=3,vibrato=f=0.2:d=0.4,highpass=f=70,lowpass=f=4500,volume=1.3"`, instrumentVolume: 0.14, // LoFi uses chill + some sad romantic vibes instrumentMoods: ["chill", "sad_romantic", "ambient"] }, slowed_reverb: { name: "Slowed + Reverb", description: "Dreamy slow with sad romantic atmosphere", baseCommand: `-af "atempo=0.90,asetrate=44100*0.95,aresample=44100,bass=g=4:f=80:width_type=h:width=120,aecho=0.8:0.88:250:0.5,aecho=0.7:0.78:400:0.4,aecho=0.6:0.68:600:0.3,equalizer=f=100:t=q:width=2:g=3.5,equalizer=f=4000:t=q:width=4:g=-4,acompressor=threshold=-20dB:ratio=2:attack=15:release=200:makeup=2,highpass=f=50,lowpass=f=7000,volume=1.4"`, instrumentVolume: 0.13, // Slowed+Reverb is emotional - sad, romantic, sad_romantic mix instrumentMoods: ["sad", "romantic", "sad_romantic", "ambient"] }, nightcore: { name: "Nightcore", description: "High energy with bright sparkling sounds", baseCommand: `-af "atempo=1.30,asetrate=44100*1.18,aresample=44100,treble=g=6:f=5000:width_type=h:width=3000,bass=g=-3:f=100,equalizer=f=2000:t=q:width=2:g=2.5,equalizer=f=6000:t=q:width=3:g=4,equalizer=f=10000:t=q:width=4:g=5,acompressor=threshold=-18dB:ratio=3:attack=3:release=30:makeup=4,highpass=f=120,volume=1.4"`, instrumentVolume: 0.11, // Nightcore is upbeat and energetic instrumentMoods: ["upbeat", "romantic"] }, vaporwave: { name: "Vaporwave", description: "Retro nostalgic with dreamy sad vibes", baseCommand: `-af "asetrate=44100*0.82,aresample=44100,equalizer=f=300:t=q:width=2:g=4,equalizer=f=1500:t=q:width=3:g=-2,equalizer=f=6000:t=q:width=4:g=-4,aphaser=in_gain=0.5:out_gain=0.75:delay=4:decay=0.5:speed=0.2,chorus=0.6:0.85:55:0.4:0.25:2,vibrato=f=0.12:d=0.5,aecho=0.5:0.65:180:0.35,acompressor=threshold=-20dB:ratio=2.5:attack=12:release=150:makeup=2,highpass=f=70,lowpass=f=5500,volume=1.2"`, instrumentVolume: 0.13, // Vaporwave is nostalgic - sad_romantic + ambient instrumentMoods: ["sad_romantic", "ambient", "sad"] }, "8d_audio": { name: "8D Audio", description: "Immersive 360° with ambient dreamy layers", baseCommand: `-af "apulsator=hz=0.08:mode=sine:width=0.9,extrastereo=m=3:c=1,aecho=0.7:0.85:100:0.35,haas=level_in=1:level_out=1.1:side_gain=0.9:middle_source=mid,bass=g=3:f=85,acompressor=threshold=-18dB:ratio=2:attack=10:release=100:makeup=2,highpass=f=60,volume=1.2"`, instrumentVolume: 0.12, // 8D is immersive - ambient + romantic instrumentMoods: ["ambient", "romantic", "sad_romantic"] }, bass_boosted: { name: "Bass Boosted", description: "Heavy bass with deep atmospheric tones", baseCommand: `-af "bass=g=18:f=55:width_type=o:width=1.5,bass=g=14:f=95:width_type=h:width=90,equalizer=f=35:t=q:width=1.2:g=12,equalizer=f=75:t=q:width=1.5:g=10,equalizer=f=140:t=q:width=2:g=8,equalizer=f=500:t=q:width=2:g=-3,equalizer=f=4000:t=q:width=4:g=-6,volume='mod(t,31):if(lt(MOD,15),0.4,if(lt(MOD,18),0.05,if(lt(MOD,26),0.5+0.0625*(MOD-18),1.5)))':eval=frame,acompressor=threshold=-22dB:ratio=8:attack=3:release=40:makeup=8,tremolo=f=0.15:d=0.3,highpass=f=28,alimiter=limit=0.95:attack=5:release=50"`, instrumentVolume: 0.09, // Bass boosted - deep ambient + chill instrumentMoods: ["ambient", "chill"] }, ambient: { name: "Ambient Chill", description: "Ethereal meditation with sad peaceful vibes", baseCommand: `-af "asetrate=44100*0.94,aresample=44100,aecho=0.6:0.8:200:0.4,aecho=0.5:0.7:400:0.3,chorus=0.6:0.85:50:0.4:0.25:2,aphaser=in_gain=0.5:out_gain=0.8:delay=3:decay=0.4:speed=0.2,equalizer=f=1500:t=q:width=3:g=-2,equalizer=f=6000:t=q:width=4:g=-4,acompressor=threshold=-18dB:ratio=2:attack=15:release=200:makeup=3,highpass=f=50,lowpass=f=9000,volume=1.5"`, instrumentVolume: 0.15, // Ambient - all ambient + sad moods instrumentMoods: ["ambient", "sad", "sad_romantic"] }, tiktok_lofi: { name: "TikTok LoFi", description: "Viral style with modern romantic chill", baseCommand: `-af "asetrate=44100*0.88,aresample=44100,bass=g=6:f=90:width_type=h:width=100,treble=g=-4:f=7500,equalizer=f=200:t=q:width=1.8:g=-1.5,equalizer=f=800:t=q:width=2:g=2,equalizer=f=3000:t=q:width=3:g=-2,acompressor=threshold=-20dB:ratio=4:attack=5:release=100:makeup=4,vibrato=f=0.2:d=0.5,chorus=0.5:0.8:45:0.3:0.2:2,highpass=f=70,lowpass=f=5000,volume=1.35"`, instrumentVolume: 0.13, // TikTok LoFi - chill + romantic instrumentMoods: ["chill", "romantic", "sad_romantic"] }, chill_lofi: { name: "Chill LoFi", description: "Study vibes with jazzy peaceful atmosphere", baseCommand: `-af "asetrate=44100*0.92,aresample=44100,bass=g=5:f=88:width_type=h:width=90,treble=g=-4:f=7000,equalizer=f=250:t=q:width=2:g=-1.5,equalizer=f=1000:t=q:width=2.5:g=1.2,equalizer=f=3500:t=q:width=3:g=-2.5,acompressor=threshold=-21dB:ratio=3:attack=10:release=140:makeup=2.5,vibrato=f=0.15:d=0.45,chorus=0.4:0.8:45:0.28:0.18:2,aecho=0.4:0.6:150:0.25,highpass=f=70,lowpass=f=5000,volume=1.2"`, instrumentVolume: 0.12, // Chill LoFi - pure chill + ambient instrumentMoods: ["chill", "ambient"] } }; // 🎲 Smart Random Instrument Selector function getSmartInstruments(effectType, count = 2) { const effect = EFFECTS[effectType]; if (!effect || !effect.instrumentMoods) { return getRandomInstruments(count); } // Get instruments that match the effect's moods const matchingInstruments = Object.keys(INSTRUMENTS).filter(key => effect.instrumentMoods.includes(INSTRUMENTS[key].mood) ); // Shuffle and select const shuffled = matchingInstruments.sort(() => Math.random() - 0.5); const selected = []; for (let i = 0; i < Math.min(count, shuffled.length); i++) { selected.push(shuffled[i]); } return selected; } // Fallback random selector function getRandomInstruments(count = 2) { const instrumentKeys = Object.keys(INSTRUMENTS); const shuffled = instrumentKeys.sort(() => Math.random() - 0.5); return shuffled.slice(0, count); } // 🏥 Health Check app.get('/', (req, res) => { res.json({ status: 'online', message: '🎵 LoFi Remix API v3.1 - Smart Mood-Based Instruments', version: '3.1', features: [ '9 unique effects', 'Smart mood-based instrument selection', '20+ instruments across 5 moods', 'Sad, Romantic, Sad-Romantic, Ambient, Chill, Upbeat' ], total_effects: Object.keys(EFFECTS).length, total_instruments: Object.keys(INSTRUMENTS).length, instrument_moods: ['sad', 'romantic', 'sad_romantic', 'ambient', 'chill', 'upbeat'] }); }); // 🎵 Process Audio app.post('/process', upload.single('audio'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ success: false, error: 'No audio file uploaded' }); } const effectType = req.body.effect || 'lofi'; const addInstruments = req.body.instruments !== 'false'; if (!EFFECTS[effectType]) { fs.unlinkSync(req.file.path); return res.status(400).json({ success: false, error: `Unknown effect: ${effectType}`, available_effects: Object.keys(EFFECTS) }); } const inputPath = req.file.path; const outputFilename = `${effectType}-${Date.now()}.mp3`; const outputPath = path.join(outputsDir, outputFilename); const effect = EFFECTS[effectType]; console.log(`🎵 Processing: ${effect.name}`); console.log(`📝 ${effect.description}`); console.log(`🎭 Moods: ${effect.instrumentMoods?.join(', ') || 'default'}`); let ffmpegCommand; let usedInstruments = []; if (addInstruments) { // 🎲 Smart selection - 2 instruments matching effect mood const selectedKeys = getSmartInstruments(effectType, 2); usedInstruments = selectedKeys.map(key => ({ id: key, name: INSTRUMENTS[key].name, mood: INSTRUMENTS[key].mood })); console.log(`🎹 Instruments: ${usedInstruments.map(i => `${i.name} (${i.mood})`).join(', ')}`); // Generate instruments const inst1Path = path.join(outputsDir, `inst1-${Date.now()}.wav`); const inst2Path = path.join(outputsDir, `inst2-${Date.now()}.wav`); await execPromise(`ffmpeg -f lavfi -i "${INSTRUMENTS[selectedKeys[0]].command}" -t 12 -y "${inst1Path}"`); await execPromise(`ffmpeg -f lavfi -i "${INSTRUMENTS[selectedKeys[1]].command}" -t 12 -y "${inst2Path}"`); // Mix with smart volume const vol = effect.instrumentVolume; ffmpegCommand = `ffmpeg -i "${inputPath}" -stream_loop -1 -i "${inst1Path}" -stream_loop -1 -i "${inst2Path}" -filter_complex "[1]volume=${vol}[i1];[2]volume=${vol}[i2];[0][i1][i2]amix=inputs=3:duration=first:dropout_transition=2,${effect.baseCommand.replace('-af "', '').replace('"', '')}" -ar 44100 -b:a 192k -y "${outputPath}"`; // Cleanup setTimeout(() => { [inst1Path, inst2Path].forEach(p => { if (fs.existsSync(p)) fs.unlinkSync(p); }); }, 5000); } else { ffmpegCommand = `ffmpeg -i "${inputPath}" -threads 0 ${effect.baseCommand} -ar 44100 -b:a 192k -y "${outputPath}"`; } console.log(`⚙️ Processing...`); const startTime = Date.now(); await execPromise(ffmpegCommand, { maxBuffer: 50 * 1024 * 1024 }); const processingTime = ((Date.now() - startTime) / 1000).toFixed(2); fs.unlinkSync(inputPath); const stats = fs.statSync(outputPath); const fileSizeKB = (stats.size / 1024).toFixed(2); console.log(`✅ Done: ${fileSizeKB}KB in ${processingTime}s`); res.json({ success: true, message: `Processed with ${effect.name}`, downloadUrl: `/download/${outputFilename}`, filename: outputFilename, effect: { id: effectType, name: effect.name, description: effect.description, moods: effect.instrumentMoods || [] }, instruments: addInstruments ? usedInstruments : [], output: { size_kb: fileSizeKB }, processing_time_seconds: processingTime }); } catch (error) { console.error('❌ Error:', error); if (req.file && fs.existsSync(req.file.path)) { fs.unlinkSync(req.file.path); } res.status(500).json({ success: false, error: 'Processing failed', details: error.message }); } }); // 📥 Download app.get('/download/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(outputsDir, filename); if (!fs.existsSync(filePath)) { return res.status(404).json({ success: false, error: 'File not found' }); } console.log(`📥 Download: ${filename}`); res.download(filePath, filename, (err) => { if (!err) { setTimeout(() => { if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); console.log(`🗑️ Cleaned: ${filename}`); } }, 10000); } }); }); // 🎛️ Get Effects Info app.get('/effects', (req, res) => { res.json({ total: Object.keys(EFFECTS).length, effects: Object.keys(EFFECTS).map(key => ({ id: key, name: EFFECTS[key].name, description: EFFECTS[key].description, moods: EFFECTS[key].instrumentMoods || [] })) }); }); // 🎹 Get Instruments Info app.get('/instruments', (req, res) => { const byMood = {}; Object.keys(INSTRUMENTS).forEach(key => { const inst = INSTRUMENTS[key]; if (!byMood[inst.mood]) { byMood[inst.mood] = []; } byMood[inst.mood].push({ id: key, name: inst.name }); }); res.json({ total: Object.keys(INSTRUMENTS).length, by_mood: byMood, all: Object.keys(INSTRUMENTS).map(key => ({ id: key, name: INSTRUMENTS[key].name, mood: INSTRUMENTS[key].mood })) }); }); // 🚀 Start Server app.listen(PORT, '0.0.0.0', () => { console.log('═══════════════════════════════════════════════'); console.log('🚀 LoFi Remix API v3.1 - SMART INSTRUMENTS'); console.log('═══════════════════════════════════════════════'); console.log(`📍 Server: http://0.0.0.0:${PORT}`); console.log(`⚡ Effects: ${Object.keys(EFFECTS).length}`); console.log(`🎹 Instruments: ${Object.keys(INSTRUMENTS).length}`); console.log(`🎭 Moods: Sad, Romantic, Sad-Romantic, Ambient, Chill, Upbeat`); console.log(`🎲 Smart mood-based selection!`); console.log('═══════════════════════════════════════════════'); });