🚀 Auto-deploy: Camila atualizado em 26/05/2026 13:59:50
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Migushthe2nd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
# MsEdgeTTS
|
||||
[](https://badge.fury.io/js/msedge-tts)
|
||||
|
||||
**[Update Dec 2025]**
|
||||
The Read Aloud API now requires a user agent matching the Microsoft Edge browser, and thus will not work in browsers other than Microsoft Edge.
|
||||
It will of course still work in any server-side runtime.
|
||||
|
||||
A simple Azure Speech Service module that uses the Microsoft Edge Read Aloud API.
|
||||
|
||||
~~Full support for SSML~~ Only supports `speak`, `voice`, and `prosody` element types. The following is the default SSML object:
|
||||
|
||||
```xml
|
||||
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts"
|
||||
xml:lang="${this._voiceLang}">
|
||||
<voice name="${voiceName}">
|
||||
<prosody rate="${rate}" pitch="${pitch}" volume="${volume}">
|
||||
${input}
|
||||
</prosody>
|
||||
</voice>
|
||||
</speak>
|
||||
```
|
||||
|
||||
Documentation on the SSML
|
||||
format [can be found here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup)
|
||||
. All supported audio formats [can be found here](./src/Output.ts).
|
||||
|
||||
## Example usage
|
||||
|
||||
Make sure to **escape/sanitize** your user's input!
|
||||
Use a library like [xml-escape](https://www.npmjs.com/package/xml-escape).
|
||||
|
||||
### Write to stream
|
||||
|
||||
```js
|
||||
import {MsEdgeTTS, OUTPUT_FORMAT} from "msedge-tts";
|
||||
|
||||
const tts = new MsEdgeTTS();
|
||||
await tts.setMetadata("en-IE-ConnorNeural", OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS);
|
||||
const {audioStream} = tts.toStream("Hi, how are you?");
|
||||
|
||||
audioStream.on("data", (data) => {
|
||||
console.log("DATA RECEIVED", data);
|
||||
// raw audio file data
|
||||
});
|
||||
|
||||
audioStream.on("close", () => {
|
||||
console.log("STREAM CLOSED");
|
||||
});
|
||||
```
|
||||
|
||||
### Write to file
|
||||
|
||||
```js
|
||||
import {MsEdgeTTS, OUTPUT_FORMAT} from "msedge-tts";
|
||||
|
||||
(async () => {
|
||||
const tts = new MsEdgeTTS();
|
||||
await tts.setMetadata("en-US-AriaNeural", OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS);
|
||||
const {audioFilePath} = await tts.toFile("./tmpfolder", "Hi, how are you?");
|
||||
})();
|
||||
```
|
||||
|
||||
### Change voice rate, pitch and volume
|
||||
```js
|
||||
import {MsEdgeTTS, OUTPUT_FORMAT} from "msedge-tts";
|
||||
|
||||
(async () => {
|
||||
const tts = new MsEdgeTTS();
|
||||
await tts.setMetadata("en-US-AriaNeural", OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS);
|
||||
const {audioStream} = await tts.toStream("Hi, how are you?", {rate: 0.5, pitch: "+200Hz"});
|
||||
})();
|
||||
```
|
||||
|
||||
### Use an alternative HTTP Agent
|
||||
Use a custom http.Agent implementation like [https-proxy-agent](https://github.com/TooTallNate/proxy-agents) or [socks-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/main/packages/socks-proxy-agent).
|
||||
|
||||
```js
|
||||
import {MsEdgeTTS, OUTPUT_FORMAT} from "msedge-tts";
|
||||
import {SocksProxyAgent} from 'socks-proxy-agent';
|
||||
|
||||
(async () => {
|
||||
const agent = new SocksProxyAgent("socks://your-name%40gmail.com:abcdef12345124@br41.nordvpn.com")
|
||||
const tts = new MsEdgeTTS(agent);
|
||||
await tts.setMetadata("en-US-AriaNeural", OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS);
|
||||
const {audioStream} = await tts.toStream("Hi, how are you?");
|
||||
})();
|
||||
```
|
||||
|
||||
### Get sentence and word boundaries
|
||||
|
||||
```js
|
||||
import {MsEdgeTTS, OUTPUT_FORMAT} from "msedge-tts";
|
||||
|
||||
(async () => {
|
||||
const tts = new MsEdgeTTS();
|
||||
await tts.setMetadata("en-US-AriaNeural", OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS, {
|
||||
wordBoundaryEnabled: true,
|
||||
sentenceBoundaryEnabled: true
|
||||
});
|
||||
// as stream
|
||||
const {metadataStream} = await tts.toStream("Hi, how are you doing today hello hello hello?");
|
||||
/* ->
|
||||
{
|
||||
"Metadata": [
|
||||
{
|
||||
"Type": "SentenceBoundary",
|
||||
"Data": {
|
||||
"Offset": 1000000,
|
||||
"Duration": 35875000,
|
||||
"text": {
|
||||
"Text": "Hi, how are you doing today hello hello hello?",
|
||||
"Length": 46,
|
||||
"BoundaryType": "SentenceBoundary"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
// or as file
|
||||
const {metadataFilePath} = await tts.toFile("Hi, how are you?");
|
||||
/* ->
|
||||
{
|
||||
"Metadata": [
|
||||
<all metadata combined>
|
||||
]
|
||||
}
|
||||
*/
|
||||
})();
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
For the full documentation check out the [API Documentation](https://migushthe2nd.github.io/MsEdgeTTS).
|
||||
|
||||
This library only supports promises.
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import { OUTPUT_FORMAT } from "./Output";
|
||||
import { Readable } from "stream";
|
||||
import { Agent } from "http";
|
||||
import { ProsodyOptions } from "./Prosody";
|
||||
export type Voice = {
|
||||
Name: string;
|
||||
ShortName: string;
|
||||
Gender: string;
|
||||
Locale: string;
|
||||
SuggestedCodec: string;
|
||||
FriendlyName: string;
|
||||
Status: string;
|
||||
};
|
||||
export declare class MetadataOptions {
|
||||
/**
|
||||
* (optional) any voice locale that is supported by the voice. See the list of all voices for compatibility. If not provided, the locale will be inferred from the `voiceName`.
|
||||
* Changing the voiceName will reset the voiceLocale.
|
||||
*/
|
||||
voiceLocale?: string;
|
||||
/**
|
||||
* (optional) whether to enable sentence boundary metadata. Default is `false`
|
||||
*/
|
||||
sentenceBoundaryEnabled?: boolean;
|
||||
/**
|
||||
* (optional) whether to enable word boundary metadata. Default is `false`
|
||||
*/
|
||||
wordBoundaryEnabled?: boolean;
|
||||
}
|
||||
/**
|
||||
* MsEdgeTTS constructor options
|
||||
*/
|
||||
type Options = {
|
||||
/**
|
||||
* (optional, **NOT SUPPORTED IN BROWSER**) Use a custom http.Agent implementation like [https-proxy-agent](https://github.com/TooTallNate/proxy-agents) or [socks-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/main/packages/socks-proxy-agent).
|
||||
* */
|
||||
agent?: Agent;
|
||||
/** whether to enable the built-in logger. This logs connections inits, disconnects, and incoming data to the console */
|
||||
enableLogger?: boolean;
|
||||
};
|
||||
export declare class MsEdgeTTS {
|
||||
private static TRUSTED_CLIENT_TOKEN;
|
||||
private static VOICES_URL;
|
||||
private static WSS_URL;
|
||||
private static JSON_XML_DELIM;
|
||||
private static AUDIO_DELIM;
|
||||
private static VOICE_LANG_REGEX;
|
||||
private readonly _enableLogger;
|
||||
private readonly _isBrowser;
|
||||
private _ws;
|
||||
private _voice;
|
||||
private _outputFormat;
|
||||
private _metadataOptions;
|
||||
private _streams;
|
||||
private _startTime;
|
||||
private readonly _agent;
|
||||
private _log;
|
||||
/**
|
||||
* Create a new `MsEdgeTTS` instance.
|
||||
*
|
||||
* @param options (optional) {@link Options}
|
||||
*/
|
||||
constructor(options?: Options);
|
||||
private static getSynthUrl;
|
||||
private _initClient;
|
||||
private static generateUUID;
|
||||
private static generateSecMsGec;
|
||||
private _send;
|
||||
private _pushAudioData;
|
||||
private _pushMetadata;
|
||||
private _SSMLTemplate;
|
||||
/**
|
||||
* Fetch the list of voices available in Microsoft Edge.
|
||||
* These, however, are not all. The complete list of voices supported by this module [can be found here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support) (neural, standard, and preview).
|
||||
*/
|
||||
getVoices(): Promise<Voice[]>;
|
||||
/**
|
||||
* Sets the required information for the speech to be synthesised and inits a new WebSocket connection.
|
||||
* Must be called at least once before text can be synthesised.
|
||||
* Saved in this instance. Can be called at any time times to update the metadata.
|
||||
* Merges specified values into previously provided values.
|
||||
*
|
||||
* @param voiceName a string with any `ShortName`. A list of all available neural voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#neural-voices). However, it is not limited to neural voices: standard voices can also be used. A list of standard voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#standard-voices). Changing the voiceName will reset the voiceLocale.
|
||||
* @param outputFormat any {@link OUTPUT_FORMAT}
|
||||
* @param metadataOptions (optional) {@link MetadataOptions}
|
||||
*/
|
||||
setMetadata(voiceName: string, outputFormat: OUTPUT_FORMAT, metadataOptions?: MetadataOptions): Promise<void>;
|
||||
private _metadataCheck;
|
||||
/**
|
||||
* Close the WebSocket connection.
|
||||
*/
|
||||
close(): void;
|
||||
/**
|
||||
* Writes raw audio synthesised from text to a file. Uses a basic {@link _SSMLTemplate SML template}.
|
||||
*
|
||||
* @param dirPath a valid output directory path
|
||||
* @param input the input to synthesise
|
||||
* @param options (optional) {@link ProsodyOptions}
|
||||
@returns {Promise<{audioFilePath: string, metadataFilePath: string | null}>} - a `Promise` with the full filepaths
|
||||
*/
|
||||
toFile(dirPath: string, input: string, options?: ProsodyOptions): Promise<{
|
||||
audioFilePath: string;
|
||||
metadataFilePath: string | null;
|
||||
}>;
|
||||
/**
|
||||
* Writes raw audio synthesised from text in real-time to a {@link Readable}. Uses a basic {@link _SSMLTemplate SML template}.
|
||||
*
|
||||
* @param input the text to synthesise. Can include SSML elements.
|
||||
* @param options (optional) {@link ProsodyOptions}
|
||||
@returns {Promise<{audioStream: Readable, metadataStream: Readable | null}>} - a `Promise` with the streams
|
||||
*/
|
||||
toStream(input: string, options?: ProsodyOptions): {
|
||||
audioStream: Readable;
|
||||
metadataStream: Readable | null;
|
||||
};
|
||||
/**
|
||||
* Writes raw audio synthesised from text to a file. Has no SSML template. Basic SSML should be provided in the request.
|
||||
*
|
||||
* @param dirPath a valid output directory path.
|
||||
* @param requestSSML the SSML to send. SSML elements required in order to work.
|
||||
* @returns {Promise<{audioFilePath: string, metadataFilePath: string | null}>} - a `Promise` with the full filepaths
|
||||
*/
|
||||
rawToFile(dirPath: string, requestSSML: string): Promise<{
|
||||
audioFilePath: string;
|
||||
metadataFilePath: string | null;
|
||||
}>;
|
||||
/**
|
||||
* Writes raw audio synthesised from a request in real-time to a {@link Readable}. Has no SSML template. Basic SSML should be provided in the request.
|
||||
*
|
||||
* @param requestSSML the SSML to send. SSML elements required in order to work.
|
||||
@returns {Promise<{audioStream: Readable, metadataStream: Readable | null}>} - a `Promise` with the streams
|
||||
*/
|
||||
rawToStream(requestSSML: string): {
|
||||
audioStream: Readable;
|
||||
metadataStream: Readable | null;
|
||||
};
|
||||
private _hasMetadataBoundaries;
|
||||
private _rawSSMLRequestToFile;
|
||||
private static randomHex;
|
||||
private _rawSSMLRequest;
|
||||
}
|
||||
export {};
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MsEdgeTTS = exports.MetadataOptions = void 0;
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
|
||||
const index_1 = require("buffer/index"); // /index is important for browser compatibility
|
||||
const Output_1 = require("./Output");
|
||||
const stream_1 = require("stream");
|
||||
const fs = __importStar(require("fs"));
|
||||
const Prosody_1 = require("./Prosody");
|
||||
const utils_1 = require("./utils");
|
||||
class MetadataOptions {
|
||||
/**
|
||||
* (optional) any voice locale that is supported by the voice. See the list of all voices for compatibility. If not provided, the locale will be inferred from the `voiceName`.
|
||||
* Changing the voiceName will reset the voiceLocale.
|
||||
*/
|
||||
voiceLocale;
|
||||
/**
|
||||
* (optional) whether to enable sentence boundary metadata. Default is `false`
|
||||
*/
|
||||
sentenceBoundaryEnabled = false;
|
||||
/**
|
||||
* (optional) whether to enable word boundary metadata. Default is `false`
|
||||
*/
|
||||
wordBoundaryEnabled = false;
|
||||
}
|
||||
exports.MetadataOptions = MetadataOptions;
|
||||
var messageTypes;
|
||||
(function (messageTypes) {
|
||||
messageTypes["TURN_START"] = "turn.start";
|
||||
messageTypes["TURN_END"] = "turn.end";
|
||||
messageTypes["RESPONSE"] = "response";
|
||||
messageTypes["SPEECH_CONFIG"] = "speech.config";
|
||||
messageTypes["AUDIO_METADATA"] = "audio.metadata";
|
||||
messageTypes["AUDIO"] = "audio";
|
||||
messageTypes["SSML"] = "ssml";
|
||||
})(messageTypes || (messageTypes = {}));
|
||||
class MsEdgeTTS {
|
||||
static TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
|
||||
static VOICES_URL = `https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=${MsEdgeTTS.TRUSTED_CLIENT_TOKEN}`;
|
||||
static WSS_URL = "wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1";
|
||||
static JSON_XML_DELIM = "\r\n\r\n";
|
||||
static AUDIO_DELIM = "Path:audio\r\n";
|
||||
static VOICE_LANG_REGEX = /\w{2}-\w{2}/;
|
||||
_enableLogger;
|
||||
_isBrowser;
|
||||
_ws;
|
||||
_voice;
|
||||
_outputFormat;
|
||||
_metadataOptions = new MetadataOptions();
|
||||
_streams = {};
|
||||
_startTime = 0;
|
||||
_agent;
|
||||
_log(...o) {
|
||||
if (this._enableLogger) {
|
||||
console.log(...o);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a new `MsEdgeTTS` instance.
|
||||
*
|
||||
* @param options (optional) {@link Options}
|
||||
*/
|
||||
constructor(options) {
|
||||
this._agent = options?.agent || undefined;
|
||||
this._enableLogger = options?.enableLogger || false;
|
||||
this._isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
|
||||
}
|
||||
static async getSynthUrl() {
|
||||
const req_id = MsEdgeTTS.generateUUID(); // Generate the request ID
|
||||
const secMsGEC = await MsEdgeTTS.generateSecMsGec(this.TRUSTED_CLIENT_TOKEN);
|
||||
return `${this.WSS_URL}?TrustedClientToken=${this.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=1-143.0.3650.96&ConnectionId=${req_id}`;
|
||||
}
|
||||
async _initClient() {
|
||||
const synthUrl = await MsEdgeTTS.getSynthUrl();
|
||||
const options = {
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0",
|
||||
"Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold"
|
||||
}
|
||||
};
|
||||
this._ws = this._isBrowser
|
||||
? new isomorphic_ws_1.default(synthUrl, options)
|
||||
: new isomorphic_ws_1.default(synthUrl, { ...options, agent: this._agent });
|
||||
this._ws.binaryType = "arraybuffer";
|
||||
return new Promise((resolve, reject) => {
|
||||
this._ws.onopen = () => {
|
||||
this._log("Connected in", (Date.now() - this._startTime) / 1000, "seconds");
|
||||
this._send(`Content-Type:application/json; charset=utf-8\r\nPath:${messageTypes.SPEECH_CONFIG}${MsEdgeTTS.JSON_XML_DELIM}
|
||||
{
|
||||
"context": {
|
||||
"synthesis": {
|
||||
"audio": {
|
||||
"metadataoptions": {
|
||||
"sentenceBoundaryEnabled": "${this._metadataOptions.sentenceBoundaryEnabled}",
|
||||
"wordBoundaryEnabled": "${this._metadataOptions.wordBoundaryEnabled}"
|
||||
},
|
||||
"outputFormat": "${this._outputFormat}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`).then(resolve);
|
||||
};
|
||||
this._ws.onmessage = (m) => {
|
||||
const buffer = index_1.Buffer.from(m.data);
|
||||
const message = buffer.toString();
|
||||
const requestId = /X-RequestId:(.*?)\r\n/gm.exec(message)[1];
|
||||
if (message.includes(`Path:${messageTypes.TURN_START}`)) {
|
||||
// start of turn, ignore
|
||||
this._log("->", message);
|
||||
}
|
||||
else if (message.includes(`Path:${messageTypes.TURN_END}`)) {
|
||||
// end of turn, close stream
|
||||
this._log("->", message);
|
||||
this._streams[requestId].audio.push(null);
|
||||
}
|
||||
else if (message.includes(`Path:${messageTypes.RESPONSE}`)) {
|
||||
// context response, ignore
|
||||
this._log("->", message);
|
||||
}
|
||||
else if (message.includes(`Path:${messageTypes.AUDIO_METADATA}`)) {
|
||||
// audio metadata, wordboundary/sentenceboundary
|
||||
const dataStartIndex = buffer.indexOf(MsEdgeTTS.JSON_XML_DELIM) + MsEdgeTTS.JSON_XML_DELIM.length;
|
||||
const data = buffer.subarray(dataStartIndex);
|
||||
this._log("->", message);
|
||||
this._pushMetadata(data, requestId);
|
||||
}
|
||||
else if (message.includes(`Path:${messageTypes.AUDIO}`) && m.data instanceof ArrayBuffer) {
|
||||
const dataStartIndex = buffer.indexOf(MsEdgeTTS.AUDIO_DELIM) + MsEdgeTTS.AUDIO_DELIM.length;
|
||||
const headers = buffer.subarray(0, dataStartIndex).toString();
|
||||
const data = buffer.subarray(dataStartIndex);
|
||||
this._log("->", headers);
|
||||
this._pushAudioData(data, requestId);
|
||||
}
|
||||
else {
|
||||
this._log("->", "UNKNOWN MESSAGE", message);
|
||||
}
|
||||
};
|
||||
this._ws.onclose = () => {
|
||||
this._log("disconnected after:", (Date.now() - this._startTime) / 1000, "seconds");
|
||||
for (const requestId in this._streams) {
|
||||
this._streams[requestId].audio.push(null);
|
||||
}
|
||||
};
|
||||
this._ws.onerror = (event) => {
|
||||
// Node's `ws` library fires either a native Error or an ErrorEvent wrapping one.
|
||||
const underlying = event?.error ?? event;
|
||||
const message = underlying?.message ?? String(underlying);
|
||||
const code = underlying?.code ?? event?.code;
|
||||
const wrapped = new Error(`Edge TTS WebSocket error: ${message}${code ? ` (code=${code})` : ""}`);
|
||||
wrapped.cause = underlying;
|
||||
reject(wrapped);
|
||||
};
|
||||
});
|
||||
}
|
||||
static generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
static async generateSecMsGec(trustedClientToken) {
|
||||
const ticks = Math.floor(Date.now() / 1000) + 11644473600;
|
||||
const rounded = ticks - (ticks % 300);
|
||||
const windowsTicks = rounded * 10000000;
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
||||
return Array.from(new Uint8Array(hashBuffer))
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
.toUpperCase();
|
||||
}
|
||||
async _send(message) {
|
||||
for (let i = 1; i <= 3 && this._ws.readyState !== this._ws.OPEN; i++) {
|
||||
if (i == 1) {
|
||||
this._startTime = Date.now();
|
||||
}
|
||||
this._log("connecting: ", i);
|
||||
await this._initClient();
|
||||
}
|
||||
this._ws.send(message, () => {
|
||||
this._log("<-", message);
|
||||
});
|
||||
}
|
||||
_pushAudioData(data, requestId) {
|
||||
this._streams[requestId].audio.push(data);
|
||||
}
|
||||
_pushMetadata(data, requestId) {
|
||||
this._streams[requestId].metadata.push(data);
|
||||
}
|
||||
_SSMLTemplate(input, options = {}) {
|
||||
// in case future updates to the edge API block these elements, we'll be concatenating strings.
|
||||
options = { ...new Prosody_1.ProsodyOptions(), ...options };
|
||||
return `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="${this._metadataOptions.voiceLocale}">
|
||||
<voice name="${this._voice}">
|
||||
<prosody pitch="${options.pitch}" rate="${options.rate}" volume="${options.volume}">
|
||||
${input}
|
||||
</prosody>
|
||||
</voice>
|
||||
</speak>`;
|
||||
}
|
||||
/**
|
||||
* Fetch the list of voices available in Microsoft Edge.
|
||||
* These, however, are not all. The complete list of voices supported by this module [can be found here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support) (neural, standard, and preview).
|
||||
*/
|
||||
getVoices() {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios_1.default.get(MsEdgeTTS.VOICES_URL)
|
||||
.then((res) => resolve(res.data))
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Sets the required information for the speech to be synthesised and inits a new WebSocket connection.
|
||||
* Must be called at least once before text can be synthesised.
|
||||
* Saved in this instance. Can be called at any time times to update the metadata.
|
||||
* Merges specified values into previously provided values.
|
||||
*
|
||||
* @param voiceName a string with any `ShortName`. A list of all available neural voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#neural-voices). However, it is not limited to neural voices: standard voices can also be used. A list of standard voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#standard-voices). Changing the voiceName will reset the voiceLocale.
|
||||
* @param outputFormat any {@link OUTPUT_FORMAT}
|
||||
* @param metadataOptions (optional) {@link MetadataOptions}
|
||||
*/
|
||||
async setMetadata(voiceName, outputFormat, metadataOptions) {
|
||||
const oldVoice = this._voice;
|
||||
const oldOutputFormat = this._outputFormat;
|
||||
const oldOptions = JSON.stringify(this._metadataOptions);
|
||||
this._voice = voiceName;
|
||||
if (!this._metadataOptions.voiceLocale || (!metadataOptions.voiceLocale && oldVoice !== this._voice)) {
|
||||
const voiceLangMatch = MsEdgeTTS.VOICE_LANG_REGEX.exec(this._voice);
|
||||
if (!voiceLangMatch)
|
||||
throw new Error("Could not infer voiceLocale from voiceName, and no voiceLocale was specified!");
|
||||
this._metadataOptions.voiceLocale = voiceLangMatch[0];
|
||||
}
|
||||
this._outputFormat = outputFormat;
|
||||
Object.assign(this._metadataOptions, metadataOptions);
|
||||
const changed = oldVoice !== this._voice
|
||||
|| oldOutputFormat !== this._outputFormat
|
||||
|| oldOptions !== JSON.stringify(this._metadataOptions);
|
||||
if (!changed && this._ws.readyState === this._ws.OPEN) {
|
||||
return;
|
||||
}
|
||||
// create new client
|
||||
this._startTime = Date.now();
|
||||
await this._initClient();
|
||||
}
|
||||
_metadataCheck() {
|
||||
if (!this._ws)
|
||||
throw new Error("Speech synthesis not configured yet. Run setMetadata before calling toStream or toFile.");
|
||||
}
|
||||
/**
|
||||
* Close the WebSocket connection.
|
||||
*/
|
||||
close() {
|
||||
this._ws?.close();
|
||||
}
|
||||
/**
|
||||
* Writes raw audio synthesised from text to a file. Uses a basic {@link _SSMLTemplate SML template}.
|
||||
*
|
||||
* @param dirPath a valid output directory path
|
||||
* @param input the input to synthesise
|
||||
* @param options (optional) {@link ProsodyOptions}
|
||||
@returns {Promise<{audioFilePath: string, metadataFilePath: string | null}>} - a `Promise` with the full filepaths
|
||||
*/
|
||||
toFile(dirPath, input, options) {
|
||||
return this._rawSSMLRequestToFile(dirPath, this._SSMLTemplate(input, options));
|
||||
}
|
||||
/**
|
||||
* Writes raw audio synthesised from text in real-time to a {@link Readable}. Uses a basic {@link _SSMLTemplate SML template}.
|
||||
*
|
||||
* @param input the text to synthesise. Can include SSML elements.
|
||||
* @param options (optional) {@link ProsodyOptions}
|
||||
@returns {Promise<{audioStream: Readable, metadataStream: Readable | null}>} - a `Promise` with the streams
|
||||
*/
|
||||
toStream(input, options) {
|
||||
return this._rawSSMLRequest(this._SSMLTemplate(input, options));
|
||||
}
|
||||
/**
|
||||
* Writes raw audio synthesised from text to a file. Has no SSML template. Basic SSML should be provided in the request.
|
||||
*
|
||||
* @param dirPath a valid output directory path.
|
||||
* @param requestSSML the SSML to send. SSML elements required in order to work.
|
||||
* @returns {Promise<{audioFilePath: string, metadataFilePath: string | null}>} - a `Promise` with the full filepaths
|
||||
*/
|
||||
rawToFile(dirPath, requestSSML) {
|
||||
return this._rawSSMLRequestToFile(dirPath, requestSSML);
|
||||
}
|
||||
/**
|
||||
* Writes raw audio synthesised from a request in real-time to a {@link Readable}. Has no SSML template. Basic SSML should be provided in the request.
|
||||
*
|
||||
* @param requestSSML the SSML to send. SSML elements required in order to work.
|
||||
@returns {Promise<{audioStream: Readable, metadataStream: Readable | null}>} - a `Promise` with the streams
|
||||
*/
|
||||
rawToStream(requestSSML) {
|
||||
return this._rawSSMLRequest(requestSSML);
|
||||
}
|
||||
_hasMetadataBoundaries() {
|
||||
return this._metadataOptions.sentenceBoundaryEnabled
|
||||
|| this._metadataOptions.wordBoundaryEnabled;
|
||||
}
|
||||
async _rawSSMLRequestToFile(dirPath, requestSSML) {
|
||||
const { audioStream, metadataStream, requestId } = this._rawSSMLRequest(requestSSML);
|
||||
const audioFilePath = (0, utils_1.joinPath)(dirPath, "audio." + Output_1.OUTPUT_EXTENSIONS[this._outputFormat]);
|
||||
const metadataFilePath = !!metadataStream ? (0, utils_1.joinPath)(dirPath, "metadata.json") : null;
|
||||
try {
|
||||
await Promise.all([
|
||||
new Promise((resolve, reject) => {
|
||||
const writableAudioFile = audioStream.pipe(fs.createWriteStream(audioFilePath));
|
||||
writableAudioFile.once("close", async () => {
|
||||
if (writableAudioFile.bytesWritten > 0) {
|
||||
resolve(audioFilePath);
|
||||
}
|
||||
else {
|
||||
reject("No audio data received");
|
||||
fs.unlinkSync(audioFilePath);
|
||||
}
|
||||
});
|
||||
writableAudioFile.once("error", reject);
|
||||
}),
|
||||
new Promise((resolve, reject) => {
|
||||
if (!metadataFilePath) {
|
||||
return resolve(null);
|
||||
}
|
||||
// get metadata from buffer and combine all MetaData root elements
|
||||
const metadataItems = { Metadata: [] };
|
||||
metadataStream.on("data", (chunk) => {
|
||||
const chunkObj = JSON.parse(chunk.toString());
|
||||
// .Metadata is an array of objects, just combine them
|
||||
metadataItems.Metadata.push(...chunkObj["Metadata"]);
|
||||
});
|
||||
metadataStream.once("close", () => {
|
||||
if (metadataItems.Metadata.length > 0) {
|
||||
// create file if not exists
|
||||
fs.writeFileSync(metadataFilePath, JSON.stringify(metadataItems, null, 2));
|
||||
resolve(metadataFilePath);
|
||||
}
|
||||
else {
|
||||
reject("No metadata received");
|
||||
fs.unlinkSync(metadataFilePath);
|
||||
}
|
||||
});
|
||||
metadataStream.once("error", reject);
|
||||
}),
|
||||
]);
|
||||
return { audioFilePath, metadataFilePath, requestId };
|
||||
}
|
||||
catch (e) {
|
||||
audioStream.destroy();
|
||||
metadataStream?.destroy();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
static randomHex(bytes) {
|
||||
const arr = new Uint8Array(bytes);
|
||||
crypto.getRandomValues(arr);
|
||||
return Array.from(arr, b => b.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
_rawSSMLRequest(requestSSML) {
|
||||
this._metadataCheck();
|
||||
const requestId = MsEdgeTTS.randomHex(16);
|
||||
const request = `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:${messageTypes.SSML}${MsEdgeTTS.JSON_XML_DELIM}` + requestSSML.trim();
|
||||
// https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup
|
||||
const self = this;
|
||||
const audioStream = new stream_1.Readable({
|
||||
read() {
|
||||
},
|
||||
destroy(error, callback) {
|
||||
delete self._streams[requestId];
|
||||
callback(error);
|
||||
},
|
||||
});
|
||||
const metadataStream = this._hasMetadataBoundaries() ? new stream_1.Readable({
|
||||
read() {
|
||||
},
|
||||
}) : null;
|
||||
audioStream.on("error", (e) => {
|
||||
audioStream.destroy();
|
||||
metadataStream?.destroy();
|
||||
});
|
||||
audioStream.once("close", () => {
|
||||
audioStream.destroy();
|
||||
metadataStream?.destroy();
|
||||
});
|
||||
this._streams[requestId] = {
|
||||
audio: audioStream,
|
||||
metadata: metadataStream,
|
||||
};
|
||||
this._send(request).then();
|
||||
return { audioStream, metadataStream, requestId };
|
||||
}
|
||||
}
|
||||
exports.MsEdgeTTS = MsEdgeTTS;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Only a few of the [possible formats](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/rest-text-to-speech#audio-outputs) are accepted.
|
||||
*/
|
||||
export declare enum OUTPUT_FORMAT {
|
||||
AUDIO_24KHZ_48KBITRATE_MONO_MP3 = "audio-24khz-48kbitrate-mono-mp3",
|
||||
AUDIO_24KHZ_96KBITRATE_MONO_MP3 = "audio-24khz-96kbitrate-mono-mp3",
|
||||
WEBM_24KHZ_16BIT_MONO_OPUS = "webm-24khz-16bit-mono-opus"
|
||||
}
|
||||
export declare const OUTPUT_EXTENSIONS: {
|
||||
"audio-24khz-48kbitrate-mono-mp3": string;
|
||||
"audio-24khz-96kbitrate-mono-mp3": string;
|
||||
"webm-24khz-16bit-mono-opus": string;
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.OUTPUT_EXTENSIONS = exports.OUTPUT_FORMAT = void 0;
|
||||
/**
|
||||
* Only a few of the [possible formats](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/rest-text-to-speech#audio-outputs) are accepted.
|
||||
*/
|
||||
var OUTPUT_FORMAT;
|
||||
(function (OUTPUT_FORMAT) {
|
||||
// Streaming =============================
|
||||
// AMR_WB_16000HZ = "amr-wb-16000hz",
|
||||
// AUDIO_16KHZ_16BIT_32KBPS_MONO_OPUS = "audio-16khz-16bit-32kbps-mono-opus",
|
||||
// AUDIO_16KHZ_32KBITRATE_MONO_MP3 = "audio-16khz-32kbitrate-mono-mp3",
|
||||
// AUDIO_16KHZ_64KBITRATE_MONO_MP3 = "audio-16khz-64kbitrate-mono-mp3",
|
||||
// AUDIO_16KHZ_128KBITRATE_MONO_MP3 = "audio-16khz-128kbitrate-mono-mp3",
|
||||
// AUDIO_24KHZ_16BIT_24KBPS_MONO_OPUS = "audio-24khz-16bit-24kbps-mono-opus",
|
||||
// AUDIO_24KHZ_16BIT_48KBPS_MONO_OPUS = "audio-24khz-16bit-48kbps-mono-opus",
|
||||
OUTPUT_FORMAT["AUDIO_24KHZ_48KBITRATE_MONO_MP3"] = "audio-24khz-48kbitrate-mono-mp3";
|
||||
OUTPUT_FORMAT["AUDIO_24KHZ_96KBITRATE_MONO_MP3"] = "audio-24khz-96kbitrate-mono-mp3";
|
||||
// AUDIO_24KHZ_160KBITRATE_MONO_MP3 = "audio-24khz-160kbitrate-mono-mp3",
|
||||
// AUDIO_48KHZ_96KBITRATE_MONO_MP3 = "audio-48khz-96kbitrate-mono-mp3",
|
||||
// AUDIO_48KHZ_192KBITRATE_MONO_MP3 = "audio-48khz-192kbitrate-mono-mp3",
|
||||
// OGG_16KHZ_16BIT_MONO_OPUS = "ogg-16khz-16bit-mono-opus",
|
||||
// OGG_24KHZ_16BIT_MONO_OPUS = "ogg-24khz-16bit-mono-opus",
|
||||
// OGG_48KHZ_16BIT_MONO_OPUS = "ogg-48khz-16bit-mono-opus",
|
||||
// RAW_8KHZ_8BIT_MONO_ALAW = "raw-8khz-8bit-mono-alaw",
|
||||
// RAW_8KHZ_8BIT_MONO_MULAW = "raw-8khz-8bit-mono-mulaw",
|
||||
// RAW_8KHZ_16BIT_MONO_PCM = "raw-8khz-16bit-mono-pcm",
|
||||
// RAW_16KHZ_16BIT_MONO_PCM = "raw-16khz-16bit-mono-pcm",
|
||||
// RAW_16KHZ_16BIT_MONO_TRUESILK = "raw-16khz-16bit-mono-truesilk",
|
||||
// RAW_22050HZ_16BIT_MONO_PCM = "raw-22050hz-16bit-mono-pcm",
|
||||
// RAW_24KHZ_16BIT_MONO_PCM = "raw-24khz-16bit-mono-pcm",
|
||||
// RAW_24KHZ_16BIT_MONO_TRUESILK = "raw-24khz-16bit-mono-truesilk",
|
||||
// RAW_44100HZ_16BIT_MONO_PCM = "raw-44100hz-16bit-mono-pcm",
|
||||
// RAW_48KHZ_16BIT_MONO_PCM = "raw-48khz-16bit-mono-pcm",
|
||||
// WEBM_16KHZ_16BIT_MONO_OPUS = "webm-16khz-16bit-mono-opus",
|
||||
// WEBM_24KHZ_16BIT_24KBPS_MONO_OPUS = "webm-24khz-16bit-24kbps-mono-opus",
|
||||
OUTPUT_FORMAT["WEBM_24KHZ_16BIT_MONO_OPUS"] = "webm-24khz-16bit-mono-opus";
|
||||
// Non-streaming =============================
|
||||
// RIFF_8KHZ_8BIT_MONO_ALAW = "riff-8khz-8bit-mono-alaw",
|
||||
// RIFF_8KHZ_8BIT_MONO_MULAW = "riff-8khz-8bit-mono-mulaw",
|
||||
// RIFF_8KHZ_16BIT_MONO_PCM = "riff-8khz-16bit-mono-pcm",
|
||||
// RIFF_22050HZ_16BIT_MONO_PCM = "riff-22050hz-16bit-mono-pcm",
|
||||
// RIFF_24KHZ_16BIT_MONO_PCM = "riff-24khz-16bit-mono-pcm",
|
||||
// RIFF_44100HZ_16BIT_MONO_PCM = "riff-44100hz-16bit-mono-pcm",
|
||||
// RIFF_48KHZ_16BIT_MONO_PCM = "riff-48khz-16bit-mono-pcm",
|
||||
})(OUTPUT_FORMAT || (exports.OUTPUT_FORMAT = OUTPUT_FORMAT = {}));
|
||||
exports.OUTPUT_EXTENSIONS = {
|
||||
[OUTPUT_FORMAT.AUDIO_24KHZ_48KBITRATE_MONO_MP3]: "mp3",
|
||||
[OUTPUT_FORMAT.AUDIO_24KHZ_96KBITRATE_MONO_MP3]: "mp3",
|
||||
[OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS]: "webm",
|
||||
};
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
export declare class ProsodyOptions {
|
||||
/**
|
||||
* The pitch to use.
|
||||
* Can be any {@link PITCH}, or a relative frequency in Hz (+50Hz), a relative semitone (+2st), or a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline)
|
||||
*/
|
||||
pitch?: PITCH | string;
|
||||
/**
|
||||
* The rate to use.
|
||||
* Can be any {@link RATE}, or a relative number (0.5), or string with a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking)
|
||||
*/
|
||||
rate?: RATE | string | number;
|
||||
/**
|
||||
* The volume to use.
|
||||
* Can be any {@link VOLUME}, or an absolute number (0, 100), a string with a relative number (+50), or a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume)
|
||||
*/
|
||||
volume?: VOLUME | string | number;
|
||||
}
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking
|
||||
*/
|
||||
export declare enum RATE {
|
||||
X_SLOW = "x-slow",
|
||||
SLOW = "slow",
|
||||
MEDIUM = "medium",
|
||||
FAST = "fast",
|
||||
X_FAST = "x-fast",
|
||||
DEFAULT = "default"
|
||||
}
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline
|
||||
*/
|
||||
export declare enum PITCH {
|
||||
X_LOW = "x-low",
|
||||
LOW = "low",
|
||||
MEDIUM = "medium",
|
||||
HIGH = "high",
|
||||
X_HIGH = "x-high",
|
||||
DEFAULT = "default"
|
||||
}
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume
|
||||
*/
|
||||
export declare enum VOLUME {
|
||||
SILENT = "silent",
|
||||
X_SOFT = "x-soft",
|
||||
SOFT = "soft",
|
||||
MEDIUM = "medium",
|
||||
LOUD = "loud",
|
||||
X_LOUD = "x-LOUD",
|
||||
DEFAULT = "default"
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VOLUME = exports.PITCH = exports.RATE = exports.ProsodyOptions = void 0;
|
||||
class ProsodyOptions {
|
||||
/**
|
||||
* The pitch to use.
|
||||
* Can be any {@link PITCH}, or a relative frequency in Hz (+50Hz), a relative semitone (+2st), or a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline)
|
||||
*/
|
||||
pitch = "+0Hz";
|
||||
/**
|
||||
* The rate to use.
|
||||
* Can be any {@link RATE}, or a relative number (0.5), or string with a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking)
|
||||
*/
|
||||
rate = 1.0;
|
||||
/**
|
||||
* The volume to use.
|
||||
* Can be any {@link VOLUME}, or an absolute number (0, 100), a string with a relative number (+50), or a relative percentage (+50%).
|
||||
* [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume)
|
||||
*/
|
||||
volume = 100.0;
|
||||
}
|
||||
exports.ProsodyOptions = ProsodyOptions;
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking
|
||||
*/
|
||||
var RATE;
|
||||
(function (RATE) {
|
||||
RATE["X_SLOW"] = "x-slow";
|
||||
RATE["SLOW"] = "slow";
|
||||
RATE["MEDIUM"] = "medium";
|
||||
RATE["FAST"] = "fast";
|
||||
RATE["X_FAST"] = "x-fast";
|
||||
RATE["DEFAULT"] = "default";
|
||||
})(RATE || (exports.RATE = RATE = {}));
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline
|
||||
*/
|
||||
var PITCH;
|
||||
(function (PITCH) {
|
||||
PITCH["X_LOW"] = "x-low";
|
||||
PITCH["LOW"] = "low";
|
||||
PITCH["MEDIUM"] = "medium";
|
||||
PITCH["HIGH"] = "high";
|
||||
PITCH["X_HIGH"] = "x-high";
|
||||
PITCH["DEFAULT"] = "default";
|
||||
})(PITCH || (exports.PITCH = PITCH = {}));
|
||||
/**
|
||||
* https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume
|
||||
*/
|
||||
var VOLUME;
|
||||
(function (VOLUME) {
|
||||
VOLUME["SILENT"] = "silent";
|
||||
VOLUME["X_SOFT"] = "x-soft";
|
||||
VOLUME["SOFT"] = "soft";
|
||||
VOLUME["MEDIUM"] = "medium";
|
||||
VOLUME["LOUD"] = "loud";
|
||||
VOLUME["X_LOUD"] = "x-LOUD";
|
||||
VOLUME["DEFAULT"] = "default";
|
||||
})(VOLUME || (exports.VOLUME = VOLUME = {}));
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export * from "./MsEdgeTTS";
|
||||
export * from "./Output";
|
||||
export * from "./Prosody";
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./MsEdgeTTS"), exports);
|
||||
__exportStar(require("./Output"), exports);
|
||||
__exportStar(require("./Prosody"), exports);
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare const joinPath: (...parts: any[]) => string;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.joinPath = void 0;
|
||||
const joinPath = (...parts) => {
|
||||
return parts
|
||||
.filter(Boolean)
|
||||
.join("/")
|
||||
.replace(/\/{2,}/g, "/");
|
||||
};
|
||||
exports.joinPath = joinPath;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "msedge-tts",
|
||||
"version": "2.0.5",
|
||||
"description": "An Azure Speech Service module that uses the Microsoft Edge Read Aloud API.",
|
||||
"author": "Migushthe2nd <Migushthe2nd@users.noreply.github.com>",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/Migushthe2nd/MsEdgeTTS.git",
|
||||
"private": false,
|
||||
"module": "./dist/index",
|
||||
"main": "./dist/index",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.17",
|
||||
"@types/ws": "^8.5.10",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.25.8",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.11.0",
|
||||
"buffer": "^6.0.3",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"ws": "^8.14.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node",
|
||||
"setupFilesAfterEnv": [],
|
||||
"testTimeout": 15000
|
||||
},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "pnpm run build && ts-node src/test/test.ts",
|
||||
"build": "tsc",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config src/test/jest-e2e.json"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user