feat: Estudio Musical para criacao de cantigas personalizadas

This commit is contained in:
2026-06-03 11:21:23 +00:00
parent 6a1c89bb33
commit 22f34b2e35
5 changed files with 481 additions and 4 deletions
+107
View File
@@ -210,6 +210,113 @@ app.post('/api/tts', requireAuth, async (req, res) => {
}
});
// Rota POST para gerar música customizada (Estúdio Musical)
app.post('/api/music/generate', requireAuth, async (req, res) => {
const { tema, voz, ritmo, duracao } = req.body;
if (!tema) {
return res.status(400).json({ error: 'Tema da música é obrigatório' });
}
try {
// 1. Determinar o prompt de estilo baseado nos inputs
let voiceDesc = 'female vocals, clear and sweet singing voice';
if (voz === 'homem') {
voiceDesc = 'male vocals, warm and gentle singing voice';
} else if (voz === 'crianca') {
voiceDesc = 'children vocals, sweet child singing voice';
}
let rhythmDesc = 'happy acoustic children song, simple melody, acoustic guitar';
if (ritmo === 'roda') {
rhythmDesc = 'traditional Brazilian cantiga de roda, folk children rhythm, acoustic guitar, percussion';
} else if (ritmo === 'pop') {
rhythmDesc = 'upbeat pop for children, modern synth, happy rhythm, claps';
} else if (ritmo === 'ninar') {
rhythmDesc = 'gentle lullaby for children, music box, soft strings, calm, sleeping mood';
} else if (ritmo === 'marcha') {
rhythmDesc = 'marching children rhythm, playful snare drums, acoustic guitar, brass accent';
}
const stylePrompt = `${rhythmDesc}, ${voiceDesc}, educational children song style, clear pronunciation, 100 BPM`;
// 2. Determinar o tamanho das letras baseado na duração (duracao)
let lengthInstructions = 'curta, de apenas 6 a 8 versos';
if (duracao === '1min') {
lengthInstructions = 'média, de 12 a 16 versos (com verso 1, refrão, verso 2, refrão)';
} else if (duracao === '2min') {
lengthInstructions = 'completa e longa, de 24 a 32 versos (com estrutura completa: verso 1, refrão, verso 2, refrão, ponte e refrão final)';
}
// 3. Chamar a IA (MiniMax-M3) para compor a letra
const lyricsPrompt = `Gere uma letra de cantiga escolar infantil sobre o tema: "${tema}".
A letra deve ser escrita para o público infantil (linguagem lúdica, alegre e educativa).
A estrutura de versos deve ser: ${lengthInstructions}.
Retorne APENAS a letra formatada em versos com quebras de linha. Não adicione notas, comentários ou títulos.`;
const aiResponse = await callMinimax({
system: "Você é um compositor pedagógico infantil que escreve letras de músicas educativas em português brasileiro. Retorne estritamente a letra da música, sem introduções ou observações extras.",
messages: [{ role: 'user', content: lyricsPrompt }],
temperature: 0.7,
max_tokens: 1000
});
const lyrics = aiResponse.text ? aiResponse.text.trim() : '';
if (!lyrics) {
throw new Error('Falha ao compor a letra da música.');
}
// 4. Chamar a API da MiniMax para gerar a música (canto + melodia)
const minmBase = (process.env.MINIMAX_API_BASE || 'https://api.minimax.io/v1').replace(/\/v1\/?$/, '');
const musicResp = await fetch(`${minmBase}/v1/music_generation`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'music-2.0',
prompt: stylePrompt,
lyrics: lyrics,
audio_setting: { sample_rate: 32000, bitrate: 128000, format: 'mp3' }
})
});
if (!musicResp.ok) {
const errText = await musicResp.text();
throw new Error(`MiniMax music-2.0 respondeu erro: ${errText}`);
}
const musicData = await musicResp.json();
const audioHex = musicData.data?.audio;
if (!audioHex) {
throw new Error('Nenhum dado de áudio retornado pela MiniMax.');
}
// 5. Salvar o arquivo localmente
const fs = require('fs');
const path = require('path');
const mediaDir = path.join(__dirname, 'public', 'generated-media');
if (!fs.existsSync(mediaDir)) {
fs.mkdirSync(mediaDir, { recursive: true });
}
const fileName = `estudio_musica_${Date.now()}.mp3`;
const filePath = path.join(mediaDir, fileName);
fs.writeFileSync(filePath, Buffer.from(audioHex, 'hex'));
const audioUrl = `/generated-media/${fileName}`;
// 6. Retornar dados para o frontend
res.json({
success: true,
lyrics: lyrics,
audioUrl: audioUrl
});
} catch (error) {
console.error('Erro no Estúdio Musical:', error);
res.status(500).json({ error: error.message || 'Ocorreu um erro ao gerar a música' });
}
});
// Rota GET para obter configurações do agente
app.get('/api/agent-config', requireAuth, (req, res) => {
const config = readAgentConfig();