Add speech as string prototype
This commit is contained in:
17
src/API.ts
17
src/API.ts
@ -358,7 +358,7 @@ export class UserAPI {
|
||||
input: string,
|
||||
options: InputOptions = {},
|
||||
id: number | string = ""
|
||||
) => {
|
||||
): Player => {
|
||||
const zid = "z" + id.toString();
|
||||
const key = id === "" ? this.generateCacheKey(input, options) : zid;
|
||||
|
||||
@ -1292,24 +1292,23 @@ export class UserAPI {
|
||||
// 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.
|
||||
* @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;
|
||||
synth.cancel();
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.voice = speechSynthesis.getVoices()[index];
|
||||
utterance.voice = speechSynthesis.getVoices()[voice];
|
||||
utterance.rate = rate;
|
||||
utterance.pitch = pitch;
|
||||
synth.speak(utterance);
|
||||
}
|
||||
|
||||
say = this.speak;
|
||||
|
||||
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Trivial functions
|
||||
|
||||
@ -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)!
|
||||
|
||||
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(
|
||||
"Hello world!",
|
||||
`
|
||||
@ -1590,13 +1600,36 @@ mod(4) :: speak("Hello world!")
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Speak with a different voice",
|
||||
"Different voices",
|
||||
`
|
||||
mod(2) :: speak("Topos!",irand(0,25))
|
||||
`,
|
||||
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
86
src/StringExtensions.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ import { gzipSync, decompressSync, strFromU8 } from "fflate";
|
||||
import showdown from "showdown";
|
||||
showdown.setFlavor("github");
|
||||
import showdownHighlight from "showdown-highlight";
|
||||
import { makeStringExtensions } from "./StringExtensions";
|
||||
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",
|
||||
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);
|
||||
makeArrayExtensions(this.api);
|
||||
makeStringExtensions(this.api);
|
||||
|
||||
// ================================================================================
|
||||
// CodeMirror Management
|
||||
|
||||
Reference in New Issue
Block a user