@andresaya/edge-tts 1.8.3 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,17 +3,30 @@
3
3
 
4
4
  **Edge TTS** is a powerful Text-to-Speech (TTS) package that leverages Microsoft's Edge capabilities. This package allows you to synthesize speech from text and manage voice options easily through a command-line interface (CLI).
5
5
 
6
+ ## Available in three languages
7
+
8
+ The same library for the Edge text-to-speech service, in Node.js, PHP and Go:
9
+
10
+ | Language | Install | Repository |
11
+ | --- | --- | --- |
12
+ | **Node.js and browsers** (this one) | `npm install @andresaya/edge-tts` | [andresayac/edge-tts](https://github.com/andresayac/edge-tts) |
13
+ | PHP | `composer require afaya/edge-tts` | [andresayac/edge-tts-php](https://github.com/andresayac/edge-tts-php) |
14
+ | Go | `go get github.com/andresayac/edge-tts-go` | [andresayac/edge-tts-go](https://github.com/andresayac/edge-tts-go) |
15
+
6
16
  ## Features
7
17
 
8
18
  - **Text-to-Speech**: Convert text into natural-sounding speech using Microsoft Edge's TTS capabilities.
9
19
  - **TypeScript Support**: Full TypeScript support with comprehensive type definitions included.
10
- - **Multiple Audio Formats**: Support for 36+ audio formats (MP3, WebM, OGG, WAV, PCM, and more).
20
+ - **Audio Formats**: MP3 at 48 or 96 kbit/s, and WebM/Opus.
11
21
  - **Multiple Voices**: Access a variety of voices to suit your project's needs.
12
22
  - **Voice Filtering**: Filter voices by language and gender for better selection.
13
23
  - **Audio Information**: Get detailed information about generated audio (size, duration, format).
14
24
  - **Audio Export Options**: Export synthesized audio in different formats (raw, base64, or directly to a file).
15
25
  - **Streaming Support**: Stream audio data in real-time for better performance.
16
- - **Word Boundaries Metadata**: Get word boundary information with precise timestamps.
26
+ - **Word and Sentence Boundaries**: Get word and sentence timings with precise timestamps.
27
+ - **Subtitles**: Generate SRT or WebVTT subtitles, one cue per sentence.
28
+ - **Concurrent Use**: `generate()` returns its own result, so one instance can run several syntheses at once.
29
+ - **Configurable**: Override the endpoint, headers, timeout and voice cache without waiting for a release.
17
30
  - **Command-Line Interface**: Use a simple CLI for easy access to functionality.
18
31
  - **Easy Integration**: Modular structure allows for easy inclusion in existing projects.
19
32
 
@@ -28,6 +41,8 @@ bun add @andresaya/edge-tts
28
41
  npm install @andresaya/edge-tts
29
42
  ```
30
43
 
44
+ Tested on Node.js 18, 20 and 24, and on Bun.
45
+
31
46
  ## TypeScript Support
32
47
 
33
48
  Edge TTS is written in **TypeScript** and includes full type definitions. No additional `@types` packages are needed.
@@ -37,20 +52,29 @@ Edge TTS is written in **TypeScript** and includes full type definitions. No add
37
52
  ```typescript
38
53
  import {
39
54
  EdgeTTS,
55
+ EdgeTTSError,
40
56
  Constants,
41
57
  Voice,
42
58
  SynthesisOptions,
43
- WordBoundary
59
+ SynthesisResult,
60
+ EdgeTTSOptions,
61
+ WordBoundary,
62
+ SentenceBoundary
44
63
  } from '@andresaya/edge-tts';
45
64
 
46
65
  // Voice interface
47
66
  interface Voice {
48
67
  Name: string;
49
68
  ShortName: string;
50
- Gender: 'Male' | 'Female';
69
+ Gender: string; // 'Male' | 'Female'
51
70
  Locale: string;
52
71
  FriendlyName: string;
53
72
  LocalName: string;
73
+ DisplayName?: string;
74
+ LocaleName?: string;
75
+ VoiceType?: string;
76
+ SecondaryLocaleList?: string[];
77
+ VoiceTag?: { TailoredScenarios: string[]; VoicePersonalities: string[] };
54
78
  }
55
79
 
56
80
  // Synthesis options
@@ -59,15 +83,34 @@ interface SynthesisOptions {
59
83
  rate?: string | number; // e.g., '50%' or 50
60
84
  volume?: string | number; // e.g., '90%' or 90
61
85
  outputFormat?: string; // e.g., Constants.OUTPUT_FORMAT.AUDIO_24KHZ_96KBITRATE_MONO_MP3
86
+ signal?: AbortSignal; // cancels the synthesis
87
+ timeout?: number; // inactivity timeout in ms for this call
62
88
  }
63
89
 
64
- // Word boundary metadata
90
+ // Word and sentence boundary metadata (offset and duration in 100 ns ticks)
65
91
  interface WordBoundary {
66
92
  type: "WordBoundary";
67
93
  offset: number;
68
94
  duration: number;
69
95
  text: string;
70
96
  }
97
+
98
+ interface SentenceBoundary {
99
+ type: "SentenceBoundary";
100
+ offset: number;
101
+ duration: number;
102
+ text: string;
103
+ }
104
+
105
+ // Returned by generate()
106
+ interface SynthesisResult {
107
+ audio: Uint8Array; // a Buffer in Node.js
108
+ format: string; // file extension: 'mp3' or 'webm'
109
+ outputFormat: string;
110
+ wordBoundaries: WordBoundary[];
111
+ sentenceBoundaries: SentenceBoundary[];
112
+ duration: number; // estimated, in seconds
113
+ }
71
114
  ```
72
115
 
73
116
  ### Type-Safe Usage Example
@@ -105,9 +148,19 @@ To synthesize speech from text:
105
148
  edge-tts synthesize -t "Hello, world!" -o hello_world_audio
106
149
  ```
107
150
 
108
- To list available voices:
151
+ Other options:
152
+ ```bash
153
+ # Choose the voice, read the text from a file, write subtitles too
154
+ edge-tts synthesize -f story.txt -v es-CO-SalomeNeural -o story.mp3 --subtitles story.srt
155
+
156
+ # Read from stdin, WebM/Opus output, WebVTT subtitles
157
+ cat story.txt | edge-tts synthesize -f - --format webm-24khz-16bit-mono-opus -o story --subtitles story.vtt
158
+ ```
159
+
160
+ To list available voices, optionally filtered:
109
161
  ```bash
110
162
  edge-tts voice-list
163
+ edge-tts voice-list --locale es-CO --gender female
111
164
  ```
112
165
 
113
166
  ### Integration into Your Project
@@ -119,6 +172,23 @@ import { EdgeTTS } from '@andresaya/edge-tts';
119
172
  const tts = new EdgeTTS();
120
173
  ```
121
174
 
175
+ ### Configuration
176
+
177
+ Every option is optional. The defaults come from `Constants`, so when Microsoft changes the service you can point the library at the new values yourself instead of waiting for a release.
178
+
179
+ ```js
180
+ const tts = new EdgeTTS({
181
+ wssUrl: 'wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1',
182
+ voicesUrl: 'https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list',
183
+ secMsGecVersion: '1-155.0.4283.45',
184
+ headers: { 'User-Agent': '...' }, // Node.js only, added to the default headers
185
+ timeout: 30000, // ms without data before giving up
186
+ voicesCacheTtl: 600000, // ms to reuse the voice list, 0 to always fetch it
187
+ });
188
+ ```
189
+
190
+ If the computer's clock is off, the service refuses the connection. In Node.js the library then reads the time from the server and retries once.
191
+
122
192
  ## API Reference
123
193
 
124
194
  ### Voice Management
@@ -186,9 +256,26 @@ await tts.synthesize("Hello, world!", 'en-US-AriaNeural', {
186
256
  });
187
257
  ```
188
258
 
259
+ #### Independent Results with generate()
260
+
261
+ `synthesize()` keeps the audio on the instance, so two calls at the same time on one instance overwrite each other. `generate()` returns everything in its own result instead and leaves the instance alone:
262
+
263
+ ```js
264
+ const [hello, bye] = await Promise.all([
265
+ tts.generate("Hello!", 'en-US-AriaNeural'),
266
+ tts.generate("Goodbye!", 'en-US-AriaNeural'),
267
+ ]);
268
+
269
+ hello.audio; // Buffer in Node.js, Uint8Array in the browser
270
+ hello.format; // 'mp3'
271
+ hello.duration; // estimated seconds
272
+ hello.wordBoundaries; // WordBoundary[]
273
+ hello.sentenceBoundaries; // SentenceBoundary[]
274
+ ```
275
+
189
276
  #### Audio Output Formats
190
277
 
191
- Edge TTS supports multiple audio formats. You can specify the format using the `outputFormat` option:
278
+ You can specify the format using the `outputFormat` option:
192
279
 
193
280
  ```js
194
281
  import { EdgeTTS, Constants } from '@andresaya/edge-tts';
@@ -206,28 +293,17 @@ await tts.synthesize("Hello!", 'en-US-AriaNeural', {
206
293
  outputFormat: Constants.OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS
207
294
  });
208
295
  await tts.toFile('./output/audio'); // Automatically saved as .webm
209
-
210
- // Lossless WAV
211
- await tts.synthesize("Hello!", 'en-US-AriaNeural', {
212
- outputFormat: Constants.OUTPUT_FORMAT.RIFF_24KHZ_16BIT_MONO_PCM
213
- });
214
- await tts.toFile('./output/audio'); // Automatically saved as .wav
215
296
  ```
216
297
 
217
- **Available formats (all 36 tested and compatible):**
298
+ **Available formats:**
218
299
 
219
- - **MP3 Formats** (Streaming): 16kHz, 24kHz, 48kHz with various bitrates (32-192 kbps)
220
- - **Opus Formats** (Streaming): Audio, WebM, and OGG containers
221
- - **WAV/PCM Formats** (Non-streaming): RIFF (8-48 kHz) and RAW variants
222
- - **Specialized Codecs**: AMR-WB, G.722, TrueSilk, A-law, μ-law
300
+ | Constant | Value |
301
+ |----------|-------|
302
+ | `AUDIO_24KHZ_48KBITRATE_MONO_MP3` (default) | `audio-24khz-48kbitrate-mono-mp3` |
303
+ | `AUDIO_24KHZ_96KBITRATE_MONO_MP3` | `audio-24khz-96kbitrate-mono-mp3` |
304
+ | `WEBM_24KHZ_16BIT_MONO_OPUS` | `webm-24khz-16bit-mono-opus` |
223
305
 
224
- See `Constants.OUTPUT_FORMAT` for the complete list. The file extension is automatically detected based on the format.
225
-
226
- **Format recommendations:**
227
- - 🌐 **Web streaming**: `WEBM_24KHZ_16BIT_MONO_OPUS` or `AUDIO_24KHZ_96KBITRATE_MONO_MP3`
228
- - 📱 **Mobile apps**: `AUDIO_24KHZ_48KBITRATE_MONO_MP3`
229
- - 💾 **High quality**: `AUDIO_48KHZ_192KBITRATE_MONO_MP3` or `RIFF_48KHZ_16BIT_MONO_PCM`
230
- - ⚡ **Low bandwidth**: `AUDIO_16KHZ_32KBITRATE_MONO_MP3`
306
+ The service returns no audio for other formats, such as WAV, PCM or other MP3 bitrates.
231
307
 
232
308
 
233
309
  #### Streaming Synthesis
@@ -239,6 +315,22 @@ for await (const chunk of tts.synthesizeStream("Long text to stream...", 'en-US-
239
315
  }
240
316
  ```
241
317
 
318
+ Leaving the loop early with `break` closes the connection.
319
+
320
+ #### Cancelling and Timeouts
321
+ ```js
322
+ const controller = new AbortController();
323
+ setTimeout(() => controller.abort(), 5000);
324
+
325
+ try {
326
+ await tts.synthesize("Hello!", 'en-US-AriaNeural', { signal: controller.signal, timeout: 10000 });
327
+ } catch (error) {
328
+ if (error.name === 'AbortError') console.log('Cancelled');
329
+ }
330
+ ```
331
+
332
+ `signal` works with `synthesize()`, `synthesizeStream()` and `generate()`. `timeout` is how long to wait without receiving data, 30 seconds by default.
333
+
242
334
  ### Audio Information
243
335
 
244
336
  #### Get Audio Details
@@ -277,16 +369,39 @@ const buffer = tts.toBuffer(); // Get as Buffer object
277
369
  const filePath = await tts.toFile("output_audio");
278
370
  console.log(`Audio saved to: ${filePath}`);
279
371
  // Creates: output_audio.mp3
372
+
373
+ await tts.toFile("speech.mp3"); // Creates: speech.mp3 (the extension is not repeated)
374
+ ```
375
+
376
+ ### Word and Sentence Boundaries
377
+
378
+ Offsets and durations are in 100-nanosecond ticks (10,000,000 per second).
379
+
380
+ ```js
381
+ await tts.synthesize("Hello, world. How are you?", 'en-US-AriaNeural');
382
+
383
+ const words = tts.getWordBoundaries(); // [{ type: 'WordBoundary', offset, duration, text: 'Hello' }, ...]
384
+ const sentences = tts.getSentenceBoundaries(); // [{ type: 'SentenceBoundary', offset, duration, text: 'Hello, world.' }, ...]
385
+
386
+ // Save word boundaries to a JSON file
387
+ await tts.saveMetadata('metadata.json');
280
388
  ```
281
389
 
282
- ### Word Boundaries Metadata
390
+ ### Subtitles
391
+
392
+ One cue per sentence, with the punctuation of the original text.
283
393
 
284
- ```php
285
- // Get word boundaries with timestamps
286
- $boundaries = $tts->getWordBoundaries();
394
+ ```js
395
+ import { writeFile } from 'fs/promises';
396
+ import { toSRT, toVTT } from '@andresaya/edge-tts';
287
397
 
288
- // Save metadata to file
289
- $tts->saveMetadata('metadata.json');
398
+ await tts.synthesize("¿Hola? Esto es una prueba.", 'es-CO-SalomeNeural');
399
+ await writeFile('speech.srt', tts.toSRT());
400
+ await writeFile('speech.vtt', tts.toVTT());
401
+
402
+ // From a generate() result
403
+ const result = await tts.generate("Hello, world.", 'en-US-AriaNeural');
404
+ await writeFile('hello.srt', toSRT(result.sentenceBoundaries));
290
405
  ```
291
406
 
292
407
 
@@ -396,7 +511,7 @@ exploreVoices().catch(console.error);
396
511
 
397
512
  This library can be used directly in web browsers via CDN or ES modules.
398
513
 
399
- **⚠️ Important:** In the browser, synthesis only works in **Microsoft Edge**. The speech service refuses connections whose user agent is not Edge, and a web page cannot change the user agent its browser sends. Chrome, Firefox and Safari get a 403 when synthesizing. The Node.js version is not affected.
514
+ **⚠️ Important:** Connecting directly, synthesis only works in **Microsoft Edge**. The speech service refuses connections whose user agent is not Edge, and a web page cannot change the user agent its browser sends. Chrome, Firefox and Safari get a 403 when synthesizing. For those browsers, use a relay (see [Other Browsers](#other-browsers-relay)). The Node.js version is not affected.
400
515
 
401
516
  ### CDN Usage (UMD)
402
517
 
@@ -470,6 +585,23 @@ This library can be used directly in web browsers via CDN or ES modules.
470
585
  </script>
471
586
  ```
472
587
 
588
+ ### Other Browsers (Relay)
589
+
590
+ A small relay running in Node.js connects to the service with Edge's headers and passes the messages through. Start the example relay:
591
+
592
+ ```bash
593
+ node examples/relay-server.mjs # ws://127.0.0.1:8080
594
+ ```
595
+
596
+ And point the browser at it:
597
+
598
+ ```js
599
+ const tts = new EdgeTTS({ wssUrl: 'ws://127.0.0.1:8080' });
600
+ await tts.synthesize("Hello from Chrome!", 'en-US-AriaNeural');
601
+ ```
602
+
603
+ The example only listens on `127.0.0.1`. If you deploy it, put it behind your own authentication or origin check so it is not open to everyone.
604
+
473
605
  ### Complete Browser Example
474
606
 
475
607
  For a full working example with voice selection and synthesis, see [`examples/browser-standalone.html`](examples/browser-standalone.html).
@@ -500,37 +632,43 @@ For a full working example with voice selection and synthesis, see [`examples/br
500
632
 
501
633
  ## Error Handling
502
634
 
635
+ Errors raised by the library are `EdgeTTSError` instances with a `code`. Network errors from Node.js (such as `ECONNREFUSED`) are passed through unchanged.
636
+
637
+ | `code` | When |
638
+ |--------|------|
639
+ | `HANDSHAKE` | The service refused the connection. `status` holds the HTTP status in Node.js (usually 403). |
640
+ | `NETWORK` | The connection failed after it was open (browser). |
641
+ | `TIMEOUT` | No data arrived for `timeout` ms. |
642
+ | `NO_AUDIO` | No synthesis yet, or the service returned no audio (voice and text in different scripts, or a text of only punctuation). |
643
+ | `REFUSED` | The service refused the request and said why, such as `Unsupported voice xx-XX-NobodyNeural.` or an unsupported output format. `status` holds the WebSocket close code (1007). |
644
+ | `INVALID_OPTION` | The text is empty, or `pitch`, `rate` or `volume` is not valid. |
645
+ | `VOICES` | The voice list request failed. `status` holds the HTTP status. |
646
+
647
+ Cancelling with an `AbortSignal` rejects with the signal's reason, an `AbortError`.
648
+
503
649
  ```js
504
- import { EdgeTTS } from '@andresaya/edge-tts';
650
+ import { EdgeTTS, EdgeTTSError } from '@andresaya/edge-tts';
505
651
 
506
652
  async function handleErrors() {
507
653
  const tts = new EdgeTTS();
508
654
 
509
655
  try {
510
- await tts.synthesize("Test text", 'invalid-voice-name');
656
+ await tts.synthesize("Test", 'en-US-AriaNeural', { volume: -150 });
511
657
  } catch (error) {
512
- console.error('Synthesis failed:', error.message);
658
+ if (error instanceof EdgeTTSError && error.code === 'INVALID_OPTION') {
659
+ console.error('Invalid parameter:', error.message);
660
+ }
513
661
  }
514
662
 
515
663
  try {
516
664
  // This will throw an error - no audio data
517
665
  const duration = tts.getDuration();
518
666
  } catch (error) {
519
- console.error('No audio data available:', error.message);
520
- }
521
-
522
- try {
523
- // Invalid volume range
524
- await tts.synthesize("Test", 'en-US-AriaNeural', { volume: -150 });
525
- } catch (error) {
526
- console.error('Invalid parameter:', error.message);
667
+ console.error(error.code, error.message); // NO_AUDIO ...
527
668
  }
528
669
  }
529
670
  ```
530
671
 
531
- ## PHP Version
532
- If you want to use Edge TTS with PHP, you can check out the PHP version of this package: [Edge TTS PHP](https://github.com/andresayac/edge-tts-php)
533
-
534
672
  ## License
535
673
  This project is licensed under the GNU General Public License v3 (GPLv3).
536
674