78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
import * as sdk from "microsoft-cognitiveservices-speech-sdk";
|
|
import { config } from "../config.js";
|
|
import { BaseEngine, type VoiceParams } from "./BaseEngine.js";
|
|
|
|
interface AzureVoiceMeta {
|
|
DisplayName: string;
|
|
ShortName: string;
|
|
Name: string;
|
|
}
|
|
|
|
export class AzureEngine extends BaseEngine {
|
|
protected override voices: Record<string, string> = {};
|
|
|
|
constructor() {
|
|
super("azure", "Microsoft Azure TTS", "wav");
|
|
void this.populateVoiceList();
|
|
}
|
|
|
|
override getDefaultVoice(): string {
|
|
return "aria";
|
|
}
|
|
|
|
override getSpeechFile(
|
|
text: string,
|
|
filepath: string,
|
|
voice: string = this.getDefaultVoice(),
|
|
_params: VoiceParams = {},
|
|
): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
if (!config.AZURE_API_KEY || !config.AZURE_REGION) {
|
|
reject(new Error("AZURE_API_KEY and AZURE_REGION must be set"));
|
|
return;
|
|
}
|
|
const speechConfig = sdk.SpeechConfig.fromSubscription(
|
|
config.AZURE_API_KEY,
|
|
config.AZURE_REGION,
|
|
);
|
|
speechConfig.speechSynthesisOutputFormat = sdk.SpeechSynthesisOutputFormat.Riff24Khz16BitMonoPcm;
|
|
const internal = this.voices[voice] ?? this.voices[this.getDefaultVoice()];
|
|
if (internal) speechConfig.speechSynthesisVoiceName = internal;
|
|
const audioConfig = sdk.AudioConfig.fromAudioFileOutput(filepath);
|
|
const synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig);
|
|
synthesizer.speakTextAsync(
|
|
text,
|
|
(result) => {
|
|
synthesizer.close();
|
|
if (result) resolve(filepath);
|
|
else reject(new Error("Azure TTS returned no result"));
|
|
},
|
|
(error: unknown) => {
|
|
synthesizer.close();
|
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
private async populateVoiceList(): Promise<void> {
|
|
if (!config.AZURE_LIST_ENDPOINT || !config.AZURE_API_KEY) return;
|
|
try {
|
|
const res = await fetch(config.AZURE_LIST_ENDPOINT, {
|
|
headers: { "Ocp-Apim-Subscription-Key": config.AZURE_API_KEY },
|
|
});
|
|
const json = (await res.json()) as AzureVoiceMeta[];
|
|
for (const voice of json) {
|
|
const key = voice.DisplayName.toLowerCase();
|
|
if (this.voices[key]) {
|
|
if (voice.Name.includes("Neural")) this.voices[key] = voice.ShortName;
|
|
} else {
|
|
this.voices[key] = voice.ShortName;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("Azure: failed to populate voice list:", err);
|
|
}
|
|
}
|
|
}
|