Added 60dB integration by manishEMS47 · Pull Request #32 · ganatan/angular-node-java · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions frontend-angular-ai/voice-generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ DEEPSEEK_API_KEY=deepseek-your-key
ELEVENLABS_API_KEY=eleven-your-key
ELEVENLABS_VOICE_ID=eleven-voice-id-xxxxxxxx

# 60dB – Realistic voice synthesis & cloning (multi-language)
# VOICE_ID is optional: leave blank to use the 60dB system default voice
SIXTYDB_API_KEY=sixtydb-your-key
SIXTYDB_VOICE_ID=

# --------------------------------------------------
# AVATARS / VIDEO AI – Face & Speech Animation
# --------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const aiServices = {

tts: [
{ type: 'elevenlabs', label: 'ElevenLabs', purpose: 'High-quality voice synthesis from text, multilingual' },
{ type: '60db', label: '60dB', purpose: 'Voice synthesis and cloning from text, multilingual' },
// autres services TTS...
],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const aiServices = {

tts: [
{ type: 'elevenlabs', label: 'ElevenLabs', purpose: 'High-quality voice synthesis from text, multilingual' },
{ type: '60db', label: '60dB', purpose: 'Voice synthesis and cloning from text, multilingual' },
],

avatar: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const aiServices = {

tts: [
{ type: 'elevenlabs', label: 'ElevenLabs', purpose: 'High-quality voice synthesis from text, multilingual' },
{ type: '60db', label: '60dB', purpose: 'Voice synthesis and cloning from text, multilingual' },
],

avatar: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import path from 'path';
import dotenv from 'dotenv';

import testElevenLabs from '../services/voice/test-elevenlabs.js';
import generateVoice from '../services/voice/voice.service.js';
import generateVoiceElevenLabs from '../services/voice/voice.service.js';
import generateVoiceSixtyDb from '../services/voice/sixtydb.service.js';
import generateVoiceMock from '../mocks/voice/voice.mock.js';

dotenv.config();
Expand All @@ -16,11 +17,28 @@ function safeFilename(name, llm) {
return `${name.toLowerCase().replace(/\s+/g, '-')}-${llm}`;
}

function getTtsProvider(tts) {
const providers = {
elevenlabs: {
real: generateVoiceElevenLabs,
voiceId: () => process.env.ELEVENLABS_VOICE_ID || '21m00Tcm4TlvDq8ikWAM',
},
'60db': {
real: generateVoiceSixtyDb,
voiceId: () => process.env.SIXTYDB_VOICE_ID || '',
},
};

return providers[tts] || providers.elevenlabs;
}

router.post('/:llm', async (req, res) => {
const { llm } = req.params;
const { name } = req.body;

const voiceId = process.env.ELEVENLABS_VOICE_ID || '21m00Tcm4TlvDq8ikWAM';
const tts = (req.query.tts || 'elevenlabs').toLowerCase();
const provider = getTtsProvider(tts);
const voiceId = provider.voiceId();
const fileName = safeFilename(name, llm);

const audioPath = path.join(process.cwd(), 'storage', 'voices', `${fileName}.mp3`);
Expand All @@ -47,8 +65,8 @@ router.post('/:llm', async (req, res) => {
await generateVoiceMock(text, voiceId, audioPath);
console.log('🟡 TTS MOCK -', audioPath);
} else {
await generateVoice(text, voiceId, audioPath);
console.log('✅ TTS réel -', audioPath);
await provider.real(text, voiceId, audioPath);
console.log(`✅ TTS réel (${tts}) -`, audioPath);
}

const publicPath = `/storage/voices/${fileName}.mp3`;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

import axios from 'axios';
import fs from 'fs';

async function generateVoice(text, voiceId, outputPath) {
const url = 'https://api.60db.ai/tts-synthesize';

try {
const body = {
text: text,
output_format: 'mp3',
};

if (voiceId) {
body.voice_id = voiceId;
}

const response = await axios.post(
url,
body,
{
headers: {
Authorization: `Bearer ${process.env.SIXTYDB_API_KEY}`,
'Content-Type': 'application/json',
},
},
);

const { success, message, audio_base64 } = response.data || {};

if (!success || !audio_base64) {
throw new Error(message || 'Réponse 60db invalide (audio_base64 manquant)');
}

fs.writeFileSync(outputPath, Buffer.from(audio_base64, 'base64'));
console.log('✅ Audio enregistré :', outputPath);

return outputPath;

} catch (error) {
const status = error.response?.status;

if (status) {
console.error(`❌ Erreur 60db ${status}`);
} else {
console.error('❌ Erreur inconnue :', error.message);
}

throw error;
}
}

export default generateVoice;
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class AiService {
);
}

generateVoice(llm: string, name: string): Observable<VoiceGenerationResponse> {
generateVoice(llm: string, name: string, tts = 'elevenlabs'): Observable<VoiceGenerationResponse> {
if (environment.useMock) {
const safeName = name.toLowerCase().replace(/\s+/g, '-');
const voiceMockPath = `assets/voices/${safeName}-${llm}.mp3`;
Expand All @@ -57,7 +57,7 @@ export class AiService {
}).pipe(delay(1000));
}

const url = `${this.baseUrl}/voice/${llm}`;
const url = `${this.baseUrl}/voice/${llm}?tts=${encodeURIComponent(tts)}`;
const body = { name };

return this.http.post<VoiceGenerationResponse>(url, body).pipe(
Expand Down
Loading