Add speech as string prototype

This commit is contained in:
2023-08-30 22:08:23 +03:00
parent 4098ea6e50
commit db2434d4dc
4 changed files with 130 additions and 10 deletions

View File

@ -358,7 +358,7 @@ export class UserAPI {
input: string, input: string,
options: InputOptions = {}, options: InputOptions = {},
id: number | string = "" id: number | string = ""
) => { ): Player => {
const zid = "z" + id.toString(); const zid = "z" + id.toString();
const key = id === "" ? this.generateCacheKey(input, options) : zid; const key = id === "" ? this.generateCacheKey(input, options) : zid;
@ -1292,24 +1292,23 @@ export class UserAPI {
// Speech synthesis // Speech synthesis
// ============================================================= // =============================================================
speak = (text: string, index: number, rate: number = 1, pitch: number = 1): void => { speak = (text: string, voice: number, rate: number = 1, pitch: number = 1): void => {
/* /*
* Speaks the given text using the browser's speech synthesis API. * Speaks the given text using the browser's speech synthesis API.
* @param text - The text to speak * @param text - The text to speak
* @param index - The index of the voice to use * @param voice - The index of the voice to use
* @param rate - The rate at which to speak the text
* @param pitch - The pitch at which to speak the text
*
*/ */
const synth = window.speechSynthesis; const synth = window.speechSynthesis;
synth.cancel(); synth.cancel();
const utterance = new SpeechSynthesisUtterance(text); const utterance = new SpeechSynthesisUtterance(text);
utterance.voice = speechSynthesis.getVoices()[index]; utterance.voice = speechSynthesis.getVoices()[voice];
utterance.rate = rate; utterance.rate = rate;
utterance.pitch = pitch; utterance.pitch = pitch;
synth.speak(utterance); synth.speak(utterance);
} };
say = this.speak;
// ============================================================= // =============================================================
// Trivial functions // Trivial functions

View File

@ -1581,6 +1581,16 @@ mod(0.25) :: sound('sine')
Topos can also speak using [Web Speec API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API)! Topos can also speak using [Web Speec API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API)!
Speech synthesis can be used in two ways:
- <icode>speak(text: string, voice: number, rate: number, pitch: number)</icode>: speak the given text.
Or by using string and chaining:
- <icode>"Hello".rate(1.5).pitch(0.5).speak()</icode>.
Examples:
${makeExample( ${makeExample(
"Hello world!", "Hello world!",
` `
@ -1590,13 +1600,36 @@ mod(4) :: speak("Hello world!")
)} )}
${makeExample( ${makeExample(
"Speak with a different voice", "Different voices",
` `
mod(2) :: speak("Topos!",irand(0,25)) mod(2) :: speak("Topos!",irand(0,25))
`, `,
false false
)} )}
${makeExample(
"Chaining string",
`
onbeat(1,3) :: "Foobaba".voice(irand(0,10)).speak()
`,
false
)}
${makeExample(
"Building string and chaining",
`
const subject = ["coder","user","loser"].pick()
const verb = ["is", "was", "isnt"].pick()
const object = ["happy","sad","tired"].pick()
const sentence = subject+" "+verb+" "+" "+object
mod(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).say()
`,
false
)}
`; `;

86
src/StringExtensions.ts Normal file
View File

@ -0,0 +1,86 @@
import { type UserAPI } from "./API";
import { Player } from "./classes/ZPlayer";
export {};
// Extend String prototype
declare global {
interface String {
z(): Player;
speak(): void;
rate(speed: number): string;
pitch(pitch: number): string;
volume(volume: number): string;
voice(voice: number): string;
options(): SpeechOptions;
}
}
const speechOptionsMap = new Map<string, SpeechOptions>();
export const makeStringExtensions = (api: UserAPI) => {
String.prototype.speak = function () {
const options = speechOptionsMap.get(this.valueOf()) || {};
new Speech({ ...options, text: this.valueOf() }).say();
};
String.prototype.rate = function (speed: number) {
const options = speechOptionsMap.get(this.valueOf()) || {};
speechOptionsMap.set(this.valueOf(), { ...options, rate: speed });
return this.valueOf();
};
String.prototype.pitch = function (pitch: number) {
const options = speechOptionsMap.get(this.valueOf()) || {};
speechOptionsMap.set(this.valueOf(), { ...options, pitch: pitch });
return this.valueOf();
};
String.prototype.volume = function (volume: number) {
const options = speechOptionsMap.get(this.valueOf()) || {};
speechOptionsMap.set(this.valueOf(), { ...options, volume: volume });
return this.valueOf();
};
String.prototype.voice = function (voice: number) {
const options = speechOptionsMap.get(this.valueOf()) || {};
speechOptionsMap.set(this.valueOf(), { ...options, voice: voice });
return this.valueOf();
};
String.prototype.options = function (): SpeechOptions {
return speechOptionsMap.get(this.valueOf()) || {};
};
String.prototype.z = function () {
return api.z(this.valueOf());
};
}
type SpeechOptions = {
text?: string;
rate?: number;
pitch?: number;
volume?: number;
voice?: number;
}
class Speech {
constructor(
public options: SpeechOptions
) {}
say = () => {
if (this.options.text) {
const synth = window.speechSynthesis;
synth.cancel();
const utterance = new SpeechSynthesisUtterance(this.options.text);
utterance.rate = this.options.rate || 1;
utterance.pitch = this.options.pitch || 1;
utterance.volume = this.options.volume || 1;
if (this.options.voice) {
utterance.voice = synth.getVoices()[this.options.voice];
}
synth.speak(utterance);
}
}
}

View File

@ -30,6 +30,7 @@ import { gzipSync, decompressSync, strFromU8 } from "fflate";
import showdown from "showdown"; import showdown from "showdown";
showdown.setFlavor("github"); showdown.setFlavor("github");
import showdownHighlight from "showdown-highlight"; import showdownHighlight from "showdown-highlight";
import { makeStringExtensions } from "./StringExtensions";
const classMap = { const classMap = {
h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2", h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2", h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
@ -219,6 +220,7 @@ export class Editor {
this.api = new UserAPI(this); this.api = new UserAPI(this);
makeArrayExtensions(this.api); makeArrayExtensions(this.api);
makeStringExtensions(this.api);
// ================================================================================ // ================================================================================
// CodeMirror Management // CodeMirror Management