import '@johnlindquist/kit';
const speechStream = new (class SpeechStream
{
private textQueue: string[] = [];
private isSpeaking: boolean = false;
private feed: string = '';
private finalizeFeedDebounced: () => void;
constructor(private readonly config: { waitForDelimiter: number, estimatedWordsPerMinute: number })
{
this.finalizeFeedDebounced = _.debounce(this.finalizeFeed.bind(this), config.waitForDelimiter);
onExit(() =>
{
this.textQueue = [];
this.feed = '';
sayIt('');
});
}
public addText(text: string): void
{
this.feed += text;
this.processAccumulatedText();
this.finalizeFeedDebounced();
}
private processAccumulatedText(): void
{
const delimiters = /([.,;:!?\n])/;
const delimiterMatch = this.feed.match(delimiters);
if (delimiterMatch)
{
const delimiterIndex = delimiterMatch.index;
const textUntilDelimiter = this.feed.slice(0, delimiterIndex + 1);
this.textQueue.push(textUntilDelimiter.trim());
this.feed = this.feed.slice(delimiterIndex + 1);
}
this.processQueue();
}
private finalizeFeed(): void
{
if (this.feed)
{
this.textQueue.push(this.feed.trim());
this.feed = '';
this.processQueue();
}
}
private processQueue(): void
{
if (this.isSpeaking || this.textQueue.length === 0) return;
this.isSpeaking = true;
const textToSpeak = this.textQueue.shift();
this.waitForSpeechEnd(textToSpeak);
sayIt(textToSpeak);
}
private waitForSpeechEnd(text: string): void
{
const estimatedSpeechDuration = this.estimateSpeechDuration(text);
setTimeout(() =>
{
this.isSpeaking = false;
this.processQueue();
}, estimatedSpeechDuration);
}
private estimateSpeechDuration(text: string): number
{
const wordsPerMinute = this.config.estimatedWordsPerMinute;
const words = text.trim().split(/\s+/).length;
const minutes = words / wordsPerMinute;
return minutes * 60 * 1000;
}
})({
waitForDelimiter: 4000,
estimatedWordsPerMinute: 200
});
export function sayIt(text: string): ReturnType<typeof say>
{
return say(text, { name: 'Microsoft Zira - English (United States)', rate: 1.3 });
}
export function queueSpeech(text: string)
{
speechStream.addText(text);
}