( params: Partial<GoogleTtsParams> )
| 588 | } |
| 589 | |
| 590 | async function synthesizeWithGoogle( |
| 591 | params: Partial<GoogleTtsParams> |
| 592 | ): Promise<{ audioBuffer: Buffer; format: string; mimeType: string }> { |
| 593 | const { |
| 594 | text, |
| 595 | apiKey, |
| 596 | voiceId, |
| 597 | languageCode, |
| 598 | gender, |
| 599 | audioEncoding = 'MP3', |
| 600 | speakingRate = 1.0, |
| 601 | pitch = 0.0, |
| 602 | volumeGainDb, |
| 603 | sampleRateHertz, |
| 604 | effectsProfileId, |
| 605 | } = params |
| 606 | |
| 607 | if (!text || !apiKey || !languageCode) { |
| 608 | throw new Error('text, apiKey, and languageCode are required for Google Cloud TTS') |
| 609 | } |
| 610 | |
| 611 | const clampedSpeakingRate = Math.max(0.25, Math.min(2.0, speakingRate)) |
| 612 | |
| 613 | const audioConfig: Record<string, unknown> = { |
| 614 | audioEncoding, |
| 615 | speakingRate: clampedSpeakingRate, |
| 616 | pitch, |
| 617 | } |
| 618 | |
| 619 | if (volumeGainDb !== undefined) { |
| 620 | audioConfig.volumeGainDb = volumeGainDb |
| 621 | } |
| 622 | if (sampleRateHertz) { |
| 623 | audioConfig.sampleRateHertz = sampleRateHertz |
| 624 | } |
| 625 | if (effectsProfileId && effectsProfileId.length > 0) { |
| 626 | audioConfig.effectsProfileId = effectsProfileId |
| 627 | } |
| 628 | |
| 629 | // Build voice config based on what's provided |
| 630 | const voice: Record<string, unknown> = { |
| 631 | languageCode, |
| 632 | } |
| 633 | |
| 634 | // If voiceId is provided, use it (it takes precedence over gender) |
| 635 | if (voiceId) { |
| 636 | voice.name = voiceId |
| 637 | } |
| 638 | |
| 639 | // Only include gender if specified (don't default to NEUTRAL as it's not supported) |
| 640 | if (gender) { |
| 641 | voice.ssmlGender = gender |
| 642 | } |
| 643 | |
| 644 | // If neither voiceId nor gender is provided, default to a specific voice |
| 645 | if (!voiceId && !gender) { |
| 646 | voice.name = 'en-US-Neural2-C' |
| 647 | } |
no test coverage detected