feat: Estudio Musical para criacao de cantigas personalizadas
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user