@jokio/sdk 0.1.10 → 0.1.11

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.
@@ -1,153 +0,0 @@
1
- import { playAudioFromBuffer } from './common/playAudioFromBuffer'
2
-
3
- export class EdgeTtsService {
4
- async getAudioBuffer(
5
- text: string,
6
- voice: string,
7
- options?: { pitch?: string; rate?: string; volume?: string },
8
- ): Promise<ArrayBuffer> {
9
- const audioChunks: Uint8Array[] = []
10
- const reqId = crypto.randomUUID() // Supported in most modern browsers
11
-
12
- return new Promise((resolve, reject) => {
13
- const ws = new WebSocket(
14
- `${Constants.WSS_URL}?trustedclienttoken=${Constants.TRUSTED_CLIENT_TOKEN}&ConnectionId=${reqId}`,
15
- )
16
-
17
- ws.binaryType = 'arraybuffer'
18
-
19
- const SSML_text = getSSML(text, voice, options)
20
-
21
- ws.addEventListener('open', () => {
22
- const configMessage = buildTTSConfigMessage()
23
- ws.send(configMessage)
24
-
25
- const speechMessage =
26
- `X-RequestId:${reqId}\r\n` +
27
- `Content-Type:application/ssml+xml\r\n` +
28
- `X-Timestamp:${new Date().toISOString()}Z\r\n` +
29
- `Path:ssml\r\n\r\n` +
30
- SSML_text
31
-
32
- ws.send(speechMessage)
33
- })
34
-
35
- ws.addEventListener('message', event => {
36
- const data = event.data
37
-
38
- if (data instanceof ArrayBuffer) {
39
- const buffer = new Uint8Array(data)
40
- const needle = new TextEncoder().encode('Path:audio\r\n')
41
-
42
- const index = findSubarray(buffer, needle)
43
- if (index !== -1) {
44
- const audioData = buffer.subarray(index + needle.length)
45
- audioChunks.push(audioData)
46
- }
47
-
48
- const textData = new TextDecoder().decode(buffer)
49
- if (textData.includes('Path:turn.end')) {
50
- ws.close()
51
- }
52
- } else {
53
- if (data.includes('Path:turn.end')) {
54
- ws.close()
55
- }
56
- }
57
- })
58
-
59
- ws.addEventListener('close', () => {
60
- if (!audioChunks.length) {
61
- reject('No audio data available to save.')
62
- return
63
- }
64
-
65
- // Combine all Uint8Arrays into one ArrayBuffer
66
- const totalLength = audioChunks.reduce(
67
- (acc, chunk) => acc + chunk.length,
68
- 0,
69
- )
70
- const finalBuffer = new Uint8Array(totalLength)
71
- let offset = 0
72
- for (const chunk of audioChunks) {
73
- finalBuffer.set(chunk, offset)
74
- offset += chunk.length
75
- }
76
-
77
- resolve(finalBuffer.buffer) // ArrayBuffer
78
- })
79
-
80
- ws.addEventListener('error', err => {
81
- reject(err)
82
- })
83
- })
84
- }
85
-
86
- async getAudio(
87
- text: string,
88
- voice: string,
89
- options?: { pitch?: string; rate?: string; volume?: string },
90
- ) {
91
- return this.getAudioBuffer(text, voice, options).then(x =>
92
- playAudioFromBuffer(x),
93
- )
94
- }
95
-
96
- async getVoices(): Promise<any[]> {
97
- const response = await fetch(
98
- `${Constants.VOICES_URL}?trustedclienttoken=${Constants.TRUSTED_CLIENT_TOKEN}`,
99
- )
100
- const data = await response.json()
101
- return data.map((voice: any) => ({
102
- id: voice.ShortName,
103
- locale: voice.Locale,
104
- description: voice.FriendlyName,
105
- }))
106
- }
107
- }
108
-
109
- const Constants = {
110
- TRUSTED_CLIENT_TOKEN: '6A5AA1D4EAFF4E9FB37E23D68491D6F4',
111
- WSS_URL:
112
- 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1',
113
- VOICES_URL:
114
- 'https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list',
115
- }
116
-
117
- const buildTTSConfigMessage = (): string => {
118
- return (
119
- `X-Timestamp:${new Date().toISOString()}Z\r\nContent-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n` +
120
- `{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`
121
- )
122
- }
123
-
124
- const getSSML = (
125
- text: string,
126
- voice: string,
127
- options: any = {},
128
- ): string => {
129
- options.pitch = options.pitch?.replace('hz', 'Hz')
130
- const pitch = options.pitch || '0Hz' // this.validatePitch(options.pitch || '0Hz');
131
- const rate = options.rate || '0%' // this.validateRate(options.rate || '0%');
132
- const volume = options.volume || '0%' // this.validateVolume(options.volume || '0%');
133
-
134
- return `<speak version='1.0' xml:lang='en-US'><voice name='${voice}'><prosody pitch='${pitch}' rate='${rate}' volume='${volume}'>${text}</prosody></voice></speak>`
135
- }
136
-
137
- // Helper: Finds a subarray inside another Uint8Array
138
- function findSubarray(
139
- haystack: Uint8Array,
140
- needle: Uint8Array,
141
- ): number {
142
- for (let i = 0; i <= haystack.length - needle.length; i++) {
143
- let match = true
144
- for (let j = 0; j < needle.length; j++) {
145
- if (haystack[i + j] !== needle[j]) {
146
- match = false
147
- break
148
- }
149
- }
150
- if (match) return i
151
- }
152
- return -1
153
- }
@@ -1,94 +0,0 @@
1
- import { playAudioFromBuffer } from './common/playAudioFromBuffer'
2
-
3
- type Config = {
4
- voicevoxUrl: string
5
- }
6
-
7
- export class VoicevoxTtsService {
8
- constructor(private config: Config) {}
9
-
10
- async getAudioBuffer(text: string, voice: string) {
11
- const audioQueryResponse = await fetch(
12
- `${
13
- this.config.voicevoxUrl
14
- }/audio_query?text=${encodeURIComponent(text)}&speaker=${
15
- voice || 3
16
- }`,
17
- {
18
- method: 'POST',
19
- headers: {
20
- Accept: 'application/json',
21
- },
22
- },
23
- )
24
-
25
- if (!audioQueryResponse.ok) {
26
- throw new Error(
27
- `Audio query failed: ${audioQueryResponse.statusText}`,
28
- )
29
- }
30
-
31
- const audioQuery = await audioQueryResponse.json()
32
-
33
- // Step 2: Synthesize the speech
34
- const synthesisResponse = await fetch(
35
- `${this.config.voicevoxUrl}/synthesis?speaker=${voice}`,
36
- {
37
- method: 'POST',
38
- headers: {
39
- Accept: 'audio/wav',
40
- 'Content-Type': 'application/json',
41
- },
42
- body: JSON.stringify(audioQuery),
43
- },
44
- )
45
-
46
- if (!synthesisResponse.body) {
47
- return null
48
- }
49
-
50
- const reader = synthesisResponse.body.getReader()
51
- const chunks = []
52
-
53
- // Step 1: Read all the chunks
54
- while (true) {
55
- const { done, value } = await reader.read()
56
- if (done) break
57
- chunks.push(value)
58
- }
59
-
60
- // Step 2: Concatenate into one Uint8Array
61
- const totalLength = chunks.reduce(
62
- (acc, chunk) => acc + chunk.length,
63
- 0,
64
- )
65
- const audioData = new Uint8Array(totalLength)
66
- let offset = 0
67
- for (const chunk of chunks) {
68
- audioData.set(chunk, offset)
69
- offset += chunk.length
70
- }
71
-
72
- return audioData.buffer
73
- }
74
-
75
- async getAudio(text: string, voice: string) {
76
- return this.getAudioBuffer(text, voice).then(x =>
77
- x ? playAudioFromBuffer(x) : null,
78
- )
79
- }
80
-
81
- async getVoices(): Promise<any[]> {
82
- const speakers = await fetch(
83
- `${this.config.voicevoxUrl}/speakers`,
84
- ).then(x => x.json())
85
-
86
- return speakers.flatMap((x: any) =>
87
- x.styles.map((y: any) => ({
88
- id: y.id.toString(),
89
- locale: 'ja-JP',
90
- description: `${x.name} - ${y.name}`,
91
- })),
92
- )
93
- }
94
- }
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "declaration": true,
6
- "emitDeclarationOnly": true,
7
- "outDir": "dist",
8
- "strict": true,
9
- "moduleResolution": "Node",
10
- "esModuleInterop": true,
11
- "skipLibCheck": true
12
- },
13
- "include": ["src"]
14
- }
package/vite.config.ts DELETED
@@ -1,23 +0,0 @@
1
- import path from 'path'
2
- import { defineConfig } from 'vite'
3
-
4
- export default defineConfig({
5
- publicDir: "test",
6
- server: {
7
- open: '/index.html' // or 'test/index.html' if kept in /test
8
- },
9
- build: {
10
- lib: {
11
- entry: path.resolve(__dirname, 'src/index.ts'),
12
- name: '@jokio/sdk',
13
- fileName: format => `jokio.sdk.${format}.js`,
14
- },
15
- // rollupOptions: {
16
- // output: {
17
- // globals: {
18
- // // Example: react: 'React'
19
- // },
20
- // },
21
- // },
22
- },
23
- })
File without changes