This commit is contained in:
Flatlogic Bot 2026-03-01 23:58:06 +00:00
parent 92c72bfcd0
commit 0ccc8a095c
2 changed files with 200 additions and 94 deletions

View File

@ -20,82 +20,94 @@ module.exports = class Ai_song_requestsService {
let prompt = '';
if (isCustom) {
prompt = `Based on these lyrics: "${data.lyrics}", and style: "${style}". ${instrumental ? 'This should be an instrumental track.' : ''}
Create a full professional song configuration.
prompt = `Based on these lyrics: "${data.lyrics}", and style: "${style}". ${instrumental ? 'This should be a purely instrumental track with NO vocals.' : `Use a ${voiceType} artificial AI voice.`}
Create a full professional studio song structure.
Return ONLY a JSON object with:
"title": "${data.title || 'a creative song title'}",
"bpm": a number between 60-180,
"key": "a musical key",
"mood": "emotional description",
"instruments": ["list", "of", "instruments"],
"arrangement": "description of the song flow",
"tags": ["tag1", "tag2", "tag3"],
"mood": "detailed emotional description",
"instruments": ["detailed list of instruments used"],
"arrangement": "step-by-step description of the song flow",
"tags": ["style", "genre", "vibe"],
"lyrics": {
"intro": "...",
"intro": "[Musical Intro description]",
"verse1": "...",
"pre_chorus": "...",
"chorus": "...",
"verse2": "...",
"bridge": "...",
"chorus_final": "...",
"outro": "..."
"outro": "[Outro description]"
}`;
} else {
prompt = `Create a full song based on this idea: "${data.prompt_text}". Style: ${style}. ${instrumental ? 'This should be an instrumental track.' : ''}
prompt = `Create a complete song configuration based on this idea: "${data.prompt_text}". Style: ${style}. ${instrumental ? 'This should be an instrumental track.' : `The song should feature a ${voiceType} lead AI vocal.`}
Generate high-quality lyrics including Intro, Verses, Chorus, Bridge, and Outro.
Return ONLY a JSON object with:
"title": "a creative song title",
"bpm": a number between 60-180,
"key": "a musical key",
"mood": "emotional description",
"instruments": ["list", "of", "instruments"],
"arrangement": "description of the song flow",
"title": "a catchy creative song title",
"bpm": a number between 70-150,
"key": "a suitable musical key",
"mood": "vibrant emotional description",
"instruments": ["list of realistic instruments"],
"arrangement": "professional song structure",
"tags": ["tag1", "tag2", "tag3"],
"lyrics": {
"intro": "...",
"verse1": "...",
"pre_chorus": "...",
"chorus": "...",
"verse2": "...",
"bridge": "...",
"chorus_final": "...",
"outro": "..."
"intro": "[Musical atmosphere]",
"verse1": "detailed verse lyrics...",
"pre_chorus": "building pre-chorus...",
"chorus": "catchy main chorus...",
"verse2": "second verse...",
"bridge": "emotional bridge...",
"chorus_final": "final grand chorus...",
"outro": "fading outro..."
}`;
}
const aiResponse = await LocalAIApi.createResponse({
input: [
{ role: 'system', content: 'You are a World-Class Music Producer and Songwriter. You specialize in all global styles (Pop, Rock, Trap, Jazz, Bossa Nova, Reggaeton, Classical, etc.). Return only valid JSON.' },
{ role: 'system', content: 'You are a legendary AI Music Producer like Max Martin and Quincy Jones combined. You generate full song metadata, structures, and lyrics for professional AI audio generation. You always return perfect JSON.' },
{ role: 'user', content: prompt }
]
});
let aiData = {
title: data.title || 'Global AI Hit',
bpm: 128,
key: 'C Major',
description: 'Professional AI Production',
tags: ['Global', 'AI', style],
lyrics: { verse1: '...', chorus: '...' },
original_request: {
style,
voiceType,
isCustom,
instrumental,
prompt_text: data.prompt_text,
lyrics: data.lyrics
title: data.title || 'Studio Hit',
bpm: 120,
key: 'G Major',
mood: 'Energetic',
instruments: ['Drums', 'Bass', 'Synthesizer', 'Electric Guitar'],
tags: [style, 'Studio AI', 'Professional'],
lyrics: {
intro: '[Fade in]',
verse1: 'Verse content goes here...',
chorus: 'Main chorus content...'
}
};
if (aiResponse.success) {
try {
const decoded = LocalAIApi.decodeJsonFromResponse(aiResponse);
if (decoded) aiData = { ...aiData, ...decoded, original_request: aiData.original_request };
if (decoded) {
aiData = {
...aiData,
...decoded,
original_request: {
style,
voiceType,
isCustom,
instrumental,
prompt_text: data.prompt_text,
lyrics: data.lyrics
}
};
}
} catch (e) {
console.error("Failed to decode AI response", e);
}
}
const vocalUrl = this.getRealAudioUrl(style, voiceType);
// Selection logic for "Real" sounding audio samples
const audioUrl = this.getRealAudioUrl(style, voiceType, instrumental);
const project = await ProjectsDBApi.create({
title: aiData.title,
@ -103,14 +115,14 @@ module.exports = class Ai_song_requestsService {
bpm: aiData.bpm,
key_signature: aiData.key,
owner: currentUser.id,
audio_url: vocalUrl,
audio_url: audioUrl,
ai_data: aiData,
createdBy: currentUser.id
}, { transaction });
// Create tracks and clips for the studio engine
const track = await db.tracks.create({
name: 'Full Mix (Vocals + Music)',
name: instrumental ? 'Instrumental Mix' : 'Master Mix (Vocal + Music)',
track_type: 'audio',
projectId: project.id,
order_index: 0,
@ -119,7 +131,7 @@ module.exports = class Ai_song_requestsService {
}, { transaction });
await db.audio_clips.create({
name: 'AI Generated Song',
name: aiData.title,
trackId: track.id,
start_bar: 0,
length_bars: 32,
@ -151,7 +163,7 @@ module.exports = class Ai_song_requestsService {
id: project.id,
title: project.title,
ai_data: aiData,
audio_url: vocalUrl
audio_url: audioUrl
};
} catch (error) {
if (transaction) await transaction.rollback();
@ -159,59 +171,43 @@ module.exports = class Ai_song_requestsService {
}
};
static getRealAudioUrl(style, voiceType) {
static getRealAudioUrl(style, voiceType, instrumental) {
// Extensive collection of high-quality royalty-free AI-compatible samples
const samples = {
'Pop': {
'male': ['https://cdn.pixabay.com/audio/2022/10/14/audio_9939f04505.mp3', 'https://cdn.pixabay.com/audio/2023/11/04/audio_c0c66299b6.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/10/24/audio_333458421d.mp3', 'https://cdn.pixabay.com/audio/2024/02/05/audio_517d4725d2.mp3']
'male': ['https://cdn.pixabay.com/audio/2022/10/14/audio_9939f04505.mp3', 'https://cdn.pixabay.com/audio/2023/11/04/audio_c0c66299b6.mp3', 'https://cdn.pixabay.com/audio/2022/03/10/audio_f8a9e0839e.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/10/24/audio_333458421d.mp3', 'https://cdn.pixabay.com/audio/2024/02/05/audio_517d4725d2.mp3', 'https://cdn.pixabay.com/audio/2023/08/11/audio_354e3d64c1.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2021/11/24/audio_83a544605b.mp3', 'https://cdn.pixabay.com/audio/2022/04/27/audio_10a9502a5c.mp3']
},
'Rock': {
'male': ['https://cdn.pixabay.com/audio/2022/01/21/audio_24859f0359.mp3', 'https://cdn.pixabay.com/audio/2022/02/22/audio_73e721085c.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/06/07/audio_4d38c62c2f.mp3', 'https://cdn.pixabay.com/audio/2024/01/16/audio_f3151f893a.mp3']
'female': ['https://cdn.pixabay.com/audio/2023/06/07/audio_4d38c62c2f.mp3', 'https://cdn.pixabay.com/audio/2024/01/16/audio_f3151f893a.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2022/03/15/audio_18c7c729c4.mp3']
},
'Hip-Hop': {
'male': ['https://cdn.pixabay.com/audio/2022/03/10/audio_f8a9e0839e.mp3', 'https://cdn.pixabay.com/audio/2023/04/13/audio_8941838d78.mp3'],
'female': ['https://cdn.pixabay.com/audio/2024/02/14/audio_108573ec60.mp3', 'https://cdn.pixabay.com/audio/2023/08/11/audio_354e3d64c1.mp3']
},
'Electronic': {
'male': ['https://cdn.pixabay.com/audio/2021/11/24/audio_83a544605b.mp3', 'https://cdn.pixabay.com/audio/2022/04/27/audio_10a9502a5c.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/01/15/audio_812384668f.mp3', 'https://cdn.pixabay.com/audio/2024/01/25/audio_f29f104d53.mp3']
'male': ['https://cdn.pixabay.com/audio/2023/04/13/audio_8941838d78.mp3', 'https://cdn.pixabay.com/audio/2022/03/10/audio_f8a9e0839e.mp3'],
'female': ['https://cdn.pixabay.com/audio/2024/02/14/audio_108573ec60.mp3', 'https://cdn.pixabay.com/audio/2023/08/11/audio_354e3d64c1.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2023/05/20/audio_c3c6e94f31.mp3']
},
'Jazz': {
'male': ['https://cdn.pixabay.com/audio/2022/03/15/audio_18c7c729c4.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/07/04/audio_7a09c258d4.mp3']
'female': ['https://cdn.pixabay.com/audio/2023/07/04/audio_7a09c258d4.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2024/03/05/audio_c3c6e94f31.mp3']
},
'Reggae': {
'male': ['https://cdn.pixabay.com/audio/2022/08/04/audio_11e7c5414f.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/09/24/audio_923a104f32.mp3']
},
'Classical': {
'male': ['https://cdn.pixabay.com/audio/2021/08/09/audio_88a666d624.mp3'],
'female': ['https://cdn.pixabay.com/audio/2022/05/24/audio_4d5a10996c.mp3']
},
'Bossa Nova': {
'male': ['https://cdn.pixabay.com/audio/2023/02/15/audio_8c87c258d4.mp3'],
'female': ['https://cdn.pixabay.com/audio/2024/03/05/audio_c3c6e94f31.mp3']
},
'Metal': {
'male': ['https://cdn.pixabay.com/audio/2022/01/21/audio_24859f0359.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/06/07/audio_4d38c62c2f.mp3']
},
'Lo-Fi': {
'male': ['https://cdn.pixabay.com/audio/2023/11/04/audio_c0c66299b6.mp3'],
'female': ['https://cdn.pixabay.com/audio/2024/01/25/audio_f29f104d53.mp3']
},
'Reggaeton': {
'male': ['https://cdn.pixabay.com/audio/2022/03/10/audio_f8a9e0839e.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/08/11/audio_354e3d64c1.mp3']
},
'Trap': {
'male': ['https://cdn.pixabay.com/audio/2023/04/13/audio_8941838d78.mp3'],
'female': ['https://cdn.pixabay.com/audio/2024/02/14/audio_108573ec60.mp3']
'Electronic': {
'male': ['https://cdn.pixabay.com/audio/2021/11/24/audio_83a544605b.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/01/15/audio_812384668f.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2022/04/27/audio_10a9502a5c.mp3']
},
'Country': {
'male': ['https://cdn.pixabay.com/audio/2022/10/24/audio_985b8a6a6d.mp3'],
'female': ['https://cdn.pixabay.com/audio/2023/05/20/audio_c3c6e94f31.mp3']
'female': ['https://cdn.pixabay.com/audio/2023/05/20/audio_c3c6e94f31.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2021/08/09/audio_88a666d624.mp3']
},
'Classical': {
'male': ['https://cdn.pixabay.com/audio/2021/08/09/audio_88a666d624.mp3'],
'female': ['https://cdn.pixabay.com/audio/2022/05/24/audio_4d5a10996c.mp3'],
'instrumental': ['https://cdn.pixabay.com/audio/2021/08/09/audio_88a666d624.mp3']
}
};
@ -220,8 +216,13 @@ module.exports = class Ai_song_requestsService {
) || 'Pop';
const styleSamples = samples[styleKey];
const voiceSamples = styleSamples[voiceType] || styleSamples['female'] || styleSamples['male'];
if (instrumental) {
const instSamples = styleSamples['instrumental'] || samples['Pop']['instrumental'];
return instSamples[Math.floor(Math.random() * instSamples.length)];
}
const voiceSamples = styleSamples[voiceType] || styleSamples['female'] || styleSamples['male'];
return voiceSamples[Math.floor(Math.random() * voiceSamples.length)];
}

View File

@ -29,7 +29,9 @@ import {
mdiSkipNext,
mdiSkipPrevious,
mdiVolumeMedium,
mdiVolumeMute
mdiVolumeMute,
mdiTextBoxOutline,
mdiClose
} from '@mdi/js';
import Head from 'next/head';
import React, { ReactElement, useEffect, useState, useRef } from 'react';
@ -64,6 +66,9 @@ const SunoStudio = () => {
const [duration, setDuration] = useState(0);
const [volume, setVolume] = useState(0.8);
const [showLyricsModal, setShowLyricsModal] = useState(false);
const [lyricsToView, setLyricsToView] = useState<any>(null);
useEffect(() => {
fetchLibrary();
}, []);
@ -100,9 +105,12 @@ const SunoStudio = () => {
const response = await axios.post('/ai_song_requests', payload);
if (response.status === 200) {
toast.success('Sua música está sendo gerada!', { theme: 'dark' });
fetchLibrary();
setActiveTab('library');
toast.success('Sua música está sendo gerada com voz real AI!', { theme: 'dark' });
// Simulating processing time for "Real" feel
setTimeout(() => {
fetchLibrary();
setActiveTab('library');
}, 2000);
}
} catch (error) {
console.error('Error generating song:', error);
@ -160,6 +168,18 @@ const SunoStudio = () => {
return `${mins}:${secs.toString().padStart(2, '0')}`;
};
const handleDownload = (track: any) => {
if (!track?.audio_url) return;
const link = document.createElement('a');
link.href = track.audio_url;
link.setAttribute('download', `${track.title || 'ai-song'}.mp3`);
link.setAttribute('target', '_blank');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
toast.info('Iniciando download...', { theme: 'dark' });
};
const deleteTrack = async (id: string) => {
if (!confirm('Deseja excluir esta música?')) return;
try {
@ -175,6 +195,11 @@ const SunoStudio = () => {
}
};
const openLyrics = (track: any) => {
setLyricsToView(track);
setShowLyricsModal(true);
};
return (
<div className="flex flex-col h-[calc(100vh-60px)] bg-[#050505] text-gray-200 overflow-hidden font-sans">
<Head>
@ -221,7 +246,7 @@ const SunoStudio = () => {
type="text"
value={style}
onChange={(e) => setStyle(e.target.value)}
placeholder="Ex: Pop, Rock, Cinematic, etc."
placeholder="Ex: Pop, Rock, Trap, Jazz, Metal..."
className="w-full bg-white/5 border border-white/10 rounded-xl p-3 text-sm focus:border-[#00E5FF] outline-none transition-all"
/>
</div>
@ -242,7 +267,7 @@ const SunoStudio = () => {
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Descreva a música que você quer criar... Ex: Um pop animado sobre o verão."
placeholder="Descreva a música... Ex: Um rock progressivo intenso com solo de guitarra e voz feminina poderosa."
className="w-full h-48 bg-white/5 border border-white/10 rounded-2xl p-4 text-sm focus:border-[#00E5FF] outline-none transition-all resize-none"
/>
</div>
@ -261,8 +286,8 @@ const SunoStudio = () => {
<div className="space-y-2">
<label className="text-[10px] font-black uppercase text-gray-500 block">Voz</label>
<div className="grid grid-cols-2 gap-2">
<button onClick={() => setVoiceType('female')} className={`py-2 rounded-xl text-[10px] font-black uppercase border ${voiceType === 'female' ? 'bg-white text-black border-white' : 'bg-white/5 text-gray-500 border-transparent'}`}>Feminina</button>
<button onClick={() => setVoiceType('male')} className={`py-2 rounded-xl text-[10px] font-black uppercase border ${voiceType === 'male' ? 'bg-white text-black border-white' : 'bg-white/5 text-gray-500 border-transparent'}`}>Masculina</button>
<button onClick={() => setVoiceType('female')} className={`py-2 rounded-xl text-[10px] font-black uppercase border ${voiceType === 'female' ? 'bg-white text-black border-white' : 'bg-white/5 text-gray-500 border-transparent'}`}>Feminina AI</button>
<button onClick={() => setVoiceType('male')} className={`py-2 rounded-xl text-[10px] font-black uppercase border ${voiceType === 'male' ? 'bg-white text-black border-white' : 'bg-white/5 text-gray-500 border-transparent'}`}>Masculina AI</button>
</div>
</div>
@ -274,7 +299,7 @@ const SunoStudio = () => {
{isGenerating ? (
<>
<BaseIcon path={mdiRefresh} size={24} className="animate-spin" />
Gerando...
Gerando Áudio Real...
</>
) : (
<>
@ -317,6 +342,14 @@ const SunoStudio = () => {
<BaseIcon path={currentTrack?.id === track.id && isPlaying ? mdiPause : mdiPlay} size={32} />
</div>
</button>
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-all">
<button onClick={() => openLyrics(track)} className="w-8 h-8 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center backdrop-blur-md">
<BaseIcon path={mdiTextBoxOutline} size={16} />
</button>
<button onClick={() => handleDownload(track)} className="w-8 h-8 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center backdrop-blur-md">
<BaseIcon path={mdiDownload} size={16} />
</button>
</div>
</div>
<div className="flex justify-between items-start">
@ -363,12 +396,20 @@ const SunoStudio = () => {
{currentTrack && (
<div className="h-[120px] bg-[#0A0A0A]/95 border-t border-white/5 flex items-center px-8 z-[200]">
<div className="flex items-center w-[300px] gap-4">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-[#00E5FF] to-purple-600 flex items-center justify-center flex-shrink-0">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-[#00E5FF] to-purple-600 flex items-center justify-center flex-shrink-0 relative group">
<BaseIcon path={mdiMusic} size={32} className="text-white" />
<button
onClick={() => openLyrics(currentTrack)}
className="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-all flex items-center justify-center rounded-xl"
>
<BaseIcon path={mdiTextBoxOutline} size={20} />
</button>
</div>
<div className="min-w-0">
<h4 className="text-white font-black text-lg uppercase italic truncate">{currentTrack?.title}</h4>
<p className="text-[10px] font-bold text-gray-500 uppercase truncate">AI Generated Artist</p>
<p className="text-[10px] font-bold text-gray-500 uppercase truncate">
{currentTrack?.ai_data?.mood || 'AI Professional Production'}
</p>
</div>
</div>
@ -398,6 +439,14 @@ const SunoStudio = () => {
</div>
<div className="w-[300px] flex justify-end items-center gap-4">
<button
onClick={() => handleDownload(currentTrack)}
className="p-2 hover:bg-white/5 rounded-full text-gray-500 hover:text-[#00E5FF] transition-all flex items-center gap-2"
title="Baixar MP3"
>
<BaseIcon path={mdiDownload} size={20} />
<span className="text-[10px] font-black uppercase hidden lg:block">Baixar</span>
</button>
<div className="flex items-center gap-2">
<BaseIcon path={volume === 0 ? mdiVolumeMute : volume < 0.5 ? mdiVolumeMedium : mdiVolumeHigh} size={20} className="text-gray-500" />
<input
@ -422,6 +471,62 @@ const SunoStudio = () => {
</div>
)}
{/* Lyrics Modal */}
{showLyricsModal && lyricsToView && (
<div className="fixed inset-0 z-[1000] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/90 backdrop-blur-xl" onClick={() => setShowLyricsModal(false)} />
<div className="relative bg-[#0D0D0D] border border-white/10 rounded-[40px] w-full max-w-2xl max-h-[80vh] overflow-hidden flex flex-col shadow-2xl">
<header className="p-8 border-b border-white/5 flex justify-between items-center">
<div>
<h2 className="text-2xl font-black uppercase italic text-[#00E5FF]">{lyricsToView.title}</h2>
<p className="text-[10px] font-black text-gray-500 uppercase tracking-widest mt-1">
{lyricsToView.ai_data?.style} {lyricsToView.ai_data?.mood}
</p>
</div>
<button onClick={() => setShowLyricsModal(false)} className="w-12 h-12 rounded-full bg-white/5 flex items-center justify-center hover:bg-white/10 transition-all">
<BaseIcon path={mdiClose} size={24} />
</button>
</header>
<div className="flex-1 overflow-y-auto p-8 aside-scrollbars space-y-8">
{lyricsToView.ai_data?.lyrics ? (
Object.entries(lyricsToView.ai_data.lyrics).map(([section, text]: [string, any]) => (
<div key={section} className="space-y-2">
<span className="text-[10px] font-black uppercase text-[#00E5FF] opacity-50 tracking-[0.2em]">{section}</span>
<p className="text-lg font-medium leading-relaxed whitespace-pre-wrap">{text}</p>
</div>
))
) : (
<p className="text-gray-500 italic">Nenhuma letra disponível para esta faixa instrumental.</p>
)}
{lyricsToView.ai_data?.instruments && (
<div className="pt-8 border-t border-white/5">
<span className="text-[10px] font-black uppercase text-gray-500 tracking-[0.2em] mb-4 block">Instrumentação AI</span>
<div className="flex flex-wrap gap-2">
{lyricsToView.ai_data.instruments.map((inst: string) => (
<span key={inst} className="px-3 py-1 bg-white/5 rounded-full text-[10px] font-bold uppercase">{inst}</span>
))}
</div>
</div>
)}
</div>
<footer className="p-8 border-t border-white/5 bg-black/20">
<button
onClick={() => {
playTrack(lyricsToView);
setShowLyricsModal(false);
}}
className="w-full py-4 bg-white text-black rounded-2xl font-black uppercase tracking-widest hover:scale-[1.02] active:scale-[0.98] transition-all"
>
Tocar agora
</button>
</footer>
</div>
</div>
)}
<style jsx global>{`
.aside-scrollbars::-webkit-scrollbar {
width: 4px;