質問編集履歴

4

変更

2019/01/17 06:49

投稿

cuku
cuku

スコア108

test CHANGED
File without changes
test CHANGED
@@ -1,57 +1,589 @@
1
+ https://github.com/guest271314/SpeechSynthesisRecorderを参考にしてやってみましたがindex.html:1 Uncaught (in promise) DOMExceptionというエラーが出てしまいます。
2
+
3
+
4
+
1
- 下記ドはtextbox文字を入力しbuttonを押下ると入力した文字音声として発生し
5
+ クロームデベロッパで見てみると .then(({tts, data}) => の処理×がついいるのですが何がダメなのかがわかりせん
2
-
3
-
4
-
5
- *やりたいこと
6
+
6
-
7
- textボックスに文字を入力してbuttonを押下したら一度mp3に変換してから音声を発生させたいです。
8
-
9
- 調べたところMediaStreamを使えばできるそうなのでよくわかりません。
10
-
11
- かる人がいましたら教えてほしです。
7
+ かる人がいましたら教えてくださ
12
-
13
-
14
-
8
+
9
+
10
+
15
- ```html
11
+ ```HTML
16
12
 
17
13
  <!DOCTYPE html>
18
14
 
19
15
  <html lang="ja">
20
16
 
21
-
22
-
23
17
  <head>
24
18
 
25
- <meta charset="utf-8">
19
+ <meta charset="utf-8">
20
+
26
-
21
+ <script type="text/javascript" src="js/voiceappclass.js"></script>
22
+
27
- <script type="text/javascript">
23
+ <script type="text/javascript" src="js/voiceapp.js"></script>
28
-
29
- function do_speech() {
30
-
31
- var ssu = new SpeechSynthesisUtterance();
32
-
33
- ssu.text = document.getElementById("word").value;
34
-
35
- ssu.lang = 'ja-JP';
36
-
37
- speechSynthesis.speak(ssu);
38
-
39
- }
40
-
41
- </script>
42
24
 
43
25
  </head>
44
26
 
45
27
  <body>
46
28
 
47
- <p>API版</p>
29
+ <p>API版</p>
48
-
30
+
31
+
32
+
49
- <button onclick="do_speech();">test!</button>
33
+ <button onclick="dospeech();">test!</button>
50
-
34
+
51
- <input type="text" value="テストでーーーす" id="word" style="width:80%;" />
35
+ <input type="text" value="TEST" id="word" style="width:80%;" />
52
36
 
53
37
  </body>
54
38
 
55
39
  </html>
56
40
 
57
41
  ```
42
+
43
+
44
+
45
+ ```javascript
46
+
47
+ function dospeech(){
48
+
49
+ let ttsRecorder = new SpeechSynthesisRecorder({
50
+
51
+ text: "The revolution will not be televised",
52
+
53
+ utteranceOptions: {
54
+
55
+ voice: "english-us espeak",
56
+
57
+ lang: "en-US",
58
+
59
+ pitch: .75,
60
+
61
+ rate: 1
62
+
63
+ }
64
+
65
+ });
66
+
67
+
68
+
69
+ // ArrayBuffer
70
+
71
+ ttsRecorder.start()
72
+
73
+ .then(tts => tts.arrayBuffer())
74
+
75
+ .then(({tts, data}) => {
76
+
77
+ tts.audioNode.src = URL.createObjectURL(new Blob([data], {type:tts.mimeType}));
78
+
79
+ tts.audioNode.title = tts.utterance.text;
80
+
81
+ tts.audioNode.onloadedmetadata = () => {
82
+
83
+ console.log(tts.audioNode.duration);
84
+
85
+ tts.audioNode.play();
86
+
87
+ }
88
+
89
+ })
90
+
91
+ // AudioBuffer
92
+
93
+ ttsRecorder.start()
94
+
95
+ .then(tts => tts.audioBuffer())
96
+
97
+ .then(({tts, data}) => {
98
+
99
+ let source = tts.audioContext.createBufferSource();
100
+
101
+ source.buffer = data;
102
+
103
+ source.connect(tts.audioContext.destination);
104
+
105
+ source.start()
106
+
107
+ })
108
+
109
+
110
+
111
+ // Blob
112
+
113
+ ttsRecorder.start()
114
+
115
+ .then(tts => tts.blob())
116
+
117
+ .then(({tts, data}) => {
118
+
119
+ tts.audioNode.src = URL.createObjectURL(blob);
120
+
121
+ tts.audioNode.title = tts.utterance.text;
122
+
123
+ tts.audioNode.onloadedmetadata = () => {
124
+
125
+ console.log(tts.audioNode.duration);
126
+
127
+ tts.audioNode.play();
128
+
129
+ }
130
+
131
+ })
132
+
133
+ // ReadableStream
134
+
135
+ ttsRecorder.start()
136
+
137
+ .then(tts => tts.readableStream())
138
+
139
+ .then(({tts, data}) => {
140
+
141
+ console.log(tts, data);
142
+
143
+ data.getReader().read().then(({value, done}) => {
144
+
145
+ tts.audioNode.src = URL.createObjectURL(value[0]);
146
+
147
+ tts.audioNode.title = tts.utterance.text;
148
+
149
+ tts.audioNode.onloadedmetadata = () => {
150
+
151
+ console.log(tts.audioNode.duration);
152
+
153
+ tts.audioNode.play();
154
+
155
+ }
156
+
157
+ })
158
+
159
+ })
160
+
161
+ // MediaSource
162
+
163
+ ttsRecorder.start()
164
+
165
+ .then(tts => tts.mediaSource())
166
+
167
+ .then(({tts, data}) => {
168
+
169
+ console.log(tts, data);
170
+
171
+ tts.audioNode.srcObj = data;
172
+
173
+ tts.audioNode.title = tts.utterance.text;
174
+
175
+ tts.audioNode.onloadedmetadata = () => {
176
+
177
+ console.log(tts.audioNode.duration);
178
+
179
+ tts.audioNode.play();
180
+
181
+ }
182
+
183
+ })
184
+
185
+ // MediaStream
186
+
187
+ // let ttsRecorder = new SpeechSynthesisRecorder({
188
+
189
+ // text: "",
190
+
191
+ // utternanceOptions: {
192
+
193
+ // voice: "日本人",
194
+
195
+ // lang: "ja-JP",
196
+
197
+ // pitch: 1.0,
198
+
199
+ // rate: 1
200
+
201
+ // },
202
+
203
+ // dataType:"mediaStream"
204
+
205
+ // });
206
+
207
+ // ttsRecorder.start()
208
+
209
+ // .then(({tts, data}) => {
210
+
211
+ // })
212
+
213
+ // .catch(err => console.log(err))
214
+
215
+ };
216
+
217
+ ```
218
+
219
+
220
+
221
+ ```javascript
222
+
223
+ class SpeechSynthesisRecorder {
224
+
225
+ constructor({
226
+
227
+ text = '', utteranceOptions = {}, recorderOptions = {}, dataType = ''
228
+
229
+ }) {
230
+
231
+ if (text === '') throw new Error('no words to synthesize')
232
+
233
+ this.dataType = dataType
234
+
235
+ this.text = text
236
+
237
+ this.mimeType = MediaRecorder.isTypeSupported('audio/webm; codecs=opus') ? 'audio/webm; codecs=opus' : 'audio/ogg; codecs=opus'
238
+
239
+ this.utterance = new SpeechSynthesisUtterance(this.text)
240
+
241
+ this.speechSynthesis = window.speechSynthesis
242
+
243
+ this.mediaStream_ = new MediaStream()
244
+
245
+ this.mediaSource_ = new MediaSource()
246
+
247
+ this.mediaRecorder = new MediaRecorder(this.mediaStream_, {
248
+
249
+ mimeType: this.mimeType,
250
+
251
+ bitsPerSecond: 256 * 8 * 1024
252
+
253
+ })
254
+
255
+ this.audioContext = new AudioContext()
256
+
257
+ this.audioNode = new Audio()
258
+
259
+ this.chunks = []
260
+
261
+ if (utteranceOptions) {
262
+
263
+ if (utteranceOptions.voice) {
264
+
265
+ this.speechSynthesis.onvoiceschanged = e => {
266
+
267
+ const voice = this.speechSynthesis.getVoices().find(({
268
+
269
+ name: _name
270
+
271
+ }) => _name === utteranceOptions.voice)
272
+
273
+ this.utterance.voice = voice
274
+
275
+ console.log(voice, this.utterance)
276
+
277
+ }
278
+
279
+ this.speechSynthesis.getVoices()
280
+
281
+ }
282
+
283
+ let {
284
+
285
+ lang, rate, pitch, volume
286
+
287
+ } = utteranceOptions;
288
+
289
+ console.log(rate)
290
+
291
+ this.utterance.lang = 'en-US'
292
+
293
+ this.utterance.voice = window.speechSynthesis.getVoices()[0]; // 7:Google 日本人 ja-JP ※他は英語のみ(次項参照)
294
+
295
+ this.utterance.volume = 1.0; // 音量 min 0 ~ max 1
296
+
297
+ this.utterance.rate = 1.0; // 速度 min 0 ~ max 10
298
+
299
+ this.utterance.pitch = 1.0; // 音程 min 0 ~ max 2
300
+
301
+ }
302
+
303
+ console.log(this.utterance)
304
+
305
+ this.audioNode.controls = 'controls'
306
+
307
+ document.body.appendChild(this.audioNode)
308
+
309
+ }
310
+
311
+ start(text = '') {
312
+
313
+ if (text) this.text = text
314
+
315
+ if (this.text === '') throw new Error('no words to synthesize')
316
+
317
+ return navigator.mediaDevices.getUserMedia({
318
+
319
+ audio: true
320
+
321
+ })
322
+
323
+ .then(stream => navigator.mediaDevices.enumerateDevices()
324
+
325
+ .then(devices => {
326
+
327
+ const audiooutput = devices.find(device => device.kind == "audiooutput");
328
+
329
+ stream.getTracks().forEach(track => track.stop())
330
+
331
+ if (audiooutput) {
332
+
333
+ const constraints = {
334
+
335
+ deviceId: {
336
+
337
+ exact: audiooutput.deviceId
338
+
339
+ }
340
+
341
+ };
342
+
343
+ return navigator.mediaDevices.getUserMedia({
344
+
345
+ audio: constraints
346
+
347
+ });
348
+
349
+ }
350
+
351
+ return navigator.mediaDevices.getUserMedia({
352
+
353
+ audio: true
354
+
355
+ });
356
+
357
+ }))
358
+
359
+ .then(stream => new Promise(resolve => {
360
+
361
+ const track = stream.getAudioTracks()[0]
362
+
363
+ this.mediaStream_.addTrack(track)
364
+
365
+ // return the current `MediaStream`
366
+
367
+ if (this.dataType && this.dataType === 'mediaStream') {
368
+
369
+ resolve({
370
+
371
+ tts: this,
372
+
373
+ data: this.mediaStream_
374
+
375
+ })
376
+
377
+ };
378
+
379
+ this.mediaRecorder.ondataavailable = event => {
380
+
381
+ if (event.data.size > 0) {
382
+
383
+ this.chunks.push(event.data)
384
+
385
+ };
386
+
387
+ }
388
+
389
+ this.mediaRecorder.onstop = () => {
390
+
391
+ track.stop()
392
+
393
+ this.mediaStream_.getAudioTracks()[0].stop()
394
+
395
+ this.mediaStream_.removeTrack(track)
396
+
397
+ console.log(`Completed recording ${this.utterance.text}`, this.chunks)
398
+
399
+ resolve(this)
400
+
401
+ }
402
+
403
+ this.mediaRecorder.start()
404
+
405
+ this.utterance.onstart = () => {
406
+
407
+ console.log(`Starting recording SpeechSynthesisUtterance ${this.utterance.text}`)
408
+
409
+ }
410
+
411
+ this.utterance.onend = () => {
412
+
413
+ this.mediaRecorder.stop()
414
+
415
+ console.log(`Ending recording SpeechSynthesisUtterance ${this.utterance.text}`)
416
+
417
+ }
418
+
419
+ this.speechSynthesis.speak(this.utterance)
420
+
421
+ }))
422
+
423
+ }
424
+
425
+ blob() {
426
+
427
+ if (!this.chunks.length) throw new Error('no data to return')
428
+
429
+ return Promise.resolve({
430
+
431
+ tts: this,
432
+
433
+ data: this.chunks.length === 1 ? this.chunks[0] : new Blob(this.chunks, {
434
+
435
+ type: this.mimeType
436
+
437
+ })
438
+
439
+ })
440
+
441
+ }
442
+
443
+ arrayBuffer(blob) {
444
+
445
+ if (!this.chunks.length) throw new Error('no data to return')
446
+
447
+ return new Promise(resolve => {
448
+
449
+ const reader = new FileReader()
450
+
451
+ reader.onload = e => resolve(({
452
+
453
+ tts: this,
454
+
455
+ data: reader.result
456
+
457
+ }))
458
+
459
+ reader.readAsArrayBuffer(blob ? new Blob(blob, {
460
+
461
+ type: blob.type
462
+
463
+ }) : this.chunks.length === 1 ? this.chunks[0] : new Blob(this.chunks, {
464
+
465
+ type: this.mimeType
466
+
467
+ }))
468
+
469
+ })
470
+
471
+ }
472
+
473
+ audioBuffer() {
474
+
475
+ if (!this.chunks.length) throw new Error('no data to return')
476
+
477
+ return this.arrayBuffer()
478
+
479
+ .then(({
480
+
481
+ tts, data
482
+
483
+ }) => this.audioContext.decodeAudioData(data))
484
+
485
+ .then(buffer => ({
486
+
487
+ tts: this,
488
+
489
+ data: buffer
490
+
491
+ }))
492
+
493
+ }
494
+
495
+ mediaSource() {
496
+
497
+ if (!this.chunks.length) throw new Error('no data to return')
498
+
499
+ return this.arrayBuffer()
500
+
501
+ .then(({
502
+
503
+ data: ab
504
+
505
+ }) => new Promise((resolve, reject) => {
506
+
507
+ this.mediaSource_.onsourceended = () => resolve({
508
+
509
+ tts: this,
510
+
511
+ data: this.mediaSource_
512
+
513
+ })
514
+
515
+ this.mediaSource_.onsourceopen = () => {
516
+
517
+ if (MediaSource.isTypeSupported(this.mimeType)) {
518
+
519
+ const sourceBuffer = this.mediaSource_.addSourceBuffer(this.mimeType)
520
+
521
+ sourceBuffer.mode = 'sequence'
522
+
523
+ sourceBuffer.onupdateend = () =>
524
+
525
+ this.mediaSource_.endOfStream()
526
+
527
+ sourceBuffer.appendBuffer(ab)
528
+
529
+ } else {
530
+
531
+ reject(new Error(`${this.mimeType} is not supported`))
532
+
533
+ }
534
+
535
+ }
536
+
537
+ this.audioNode.src = URL.createObjectURL(this.mediaSource_)
538
+
539
+ }))
540
+
541
+ }
542
+
543
+ readableStream({
544
+
545
+ size = 1024, controllerOptions = {}, rsOptions = {}
546
+
547
+ }) {
548
+
549
+ if (!this.chunks.length) throw new Error('no data to return')
550
+
551
+ const src = this.chunks.slice(0)
552
+
553
+ const chunk = size
554
+
555
+ return Promise.resolve({
556
+
557
+ tts: this,
558
+
559
+ data: new ReadableStream(controllerOptions || {
560
+
561
+ start(controller) {
562
+
563
+ console.log(src.length)
564
+
565
+ controller.enqueue(src.splice(0, chunk))
566
+
567
+ },
568
+
569
+ pull(controller) {
570
+
571
+ if (src.length === 0) controller.close()
572
+
573
+ controller.enqueue(src.splice(0, chunk))
574
+
575
+ }
576
+
577
+ }, rsOptions)
578
+
579
+ })
580
+
581
+ }
582
+
583
+ }
584
+
585
+ if (typeof module !== 'undefined') module.exports = SpeechSynthesisRecorder
586
+
587
+ if (typeof window !== 'undefined') window.SpeechSynthesisRecorder = SpeechSynthesisRecorder
588
+
589
+ ```

3

修正

2019/01/17 06:49

投稿

cuku
cuku

スコア108

test CHANGED
@@ -1 +1 @@
1
- API MediaStreamを使って入力したTextをmp3に変換
1
+ API Textをmp3に変換
test CHANGED
File without changes

2

修正

2019/01/17 02:07

投稿

cuku
cuku

スコア108

test CHANGED
@@ -1 +1 @@
1
- API MediaStreamを使ってmp3に変換
1
+ API MediaStreamを使って入力したTextをmp3に変換
test CHANGED
File without changes

1

修正

2019/01/16 08:19

投稿

cuku
cuku

スコア108

test CHANGED
@@ -1 +1 @@
1
- MediaStreamを使ってmp3に変換
1
+ API MediaStreamを使ってmp3に変換
test CHANGED
File without changes