speechrecorderng 2.25.2 → 3.0.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.
Files changed (74) hide show
  1. package/esm2020/lib/audio/array_audio_buffer.mjs +101 -0
  2. package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +86 -0
  3. package/esm2020/lib/audio/audio_data_holder.mjs +109 -0
  4. package/esm2020/lib/audio/audio_display.mjs +5 -5
  5. package/esm2020/lib/audio/audio_player.mjs +9 -7
  6. package/esm2020/lib/audio/capture/capture.mjs +6 -1
  7. package/esm2020/lib/audio/dsp/level_measure.mjs +97 -55
  8. package/esm2020/lib/audio/io/stream.mjs +3 -3
  9. package/esm2020/lib/audio/persistor.mjs +12 -6
  10. package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +265 -0
  11. package/esm2020/lib/audio/playback/player.mjs +184 -50
  12. package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +25 -13
  13. package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +2 -2
  14. package/esm2020/lib/audio/ui/audiosignal.mjs +206 -77
  15. package/esm2020/lib/audio/ui/container.mjs +14 -14
  16. package/esm2020/lib/audio/ui/livelevel.mjs +17 -3
  17. package/esm2020/lib/audio/ui/sonagram.mjs +343 -160
  18. package/esm2020/lib/io/stream.mjs +77 -1
  19. package/esm2020/lib/net/uploader.mjs +15 -3
  20. package/esm2020/lib/speechrecorder/recording.mjs +63 -7
  21. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +96 -7
  22. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +119 -106
  23. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +34 -7
  24. package/esm2020/lib/speechrecorder/session/progress.mjs +2 -2
  25. package/esm2020/lib/speechrecorder/session/prompting.mjs +2 -2
  26. package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -3
  27. package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
  28. package/esm2020/lib/speechrecorder/session/recording_list.mjs +23 -10
  29. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +12 -6
  30. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +5 -5
  31. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +13 -9
  32. package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +8 -5
  33. package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +123 -101
  34. package/esm2020/lib/speechrecorderng.component.mjs +2 -2
  35. package/esm2020/lib/speechrecorderng.module.mjs +5 -4
  36. package/esm2020/lib/spr.module.version.mjs +2 -2
  37. package/esm2020/lib/ui/recordingitem_display.mjs +2 -2
  38. package/fesm2015/speechrecorderng.mjs +2355 -753
  39. package/fesm2015/speechrecorderng.mjs.map +1 -1
  40. package/fesm2020/speechrecorderng.mjs +2338 -746
  41. package/fesm2020/speechrecorderng.mjs.map +1 -1
  42. package/lib/audio/array_audio_buffer.d.ts +15 -0
  43. package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -0
  44. package/lib/audio/audio_data_holder.d.ts +29 -0
  45. package/lib/audio/audio_display.d.ts +2 -1
  46. package/lib/audio/audio_player.d.ts +2 -1
  47. package/lib/audio/capture/capture.d.ts +2 -0
  48. package/lib/audio/dsp/level_measure.d.ts +2 -1
  49. package/lib/audio/persistor.d.ts +8 -3
  50. package/lib/audio/playback/array_audio_buffer_source_node.d.ts +24 -0
  51. package/lib/audio/playback/player.d.ts +6 -0
  52. package/lib/audio/ui/audio_canvas_layer_comp.d.ts +4 -1
  53. package/lib/audio/ui/audio_display_scroll_pane.d.ts +2 -1
  54. package/lib/audio/ui/audiosignal.d.ts +3 -2
  55. package/lib/audio/ui/container.d.ts +3 -2
  56. package/lib/audio/ui/livelevel.d.ts +4 -2
  57. package/lib/audio/ui/sonagram.d.ts +3 -2
  58. package/lib/io/stream.d.ts +17 -0
  59. package/lib/net/uploader.d.ts +6 -1
  60. package/lib/speechrecorder/recording.d.ts +16 -3
  61. package/lib/speechrecorder/recordings/recordings.service.d.ts +2 -0
  62. package/lib/speechrecorder/session/audiorecorder.d.ts +2 -7
  63. package/lib/speechrecorder/session/basicrecorder.d.ts +10 -5
  64. package/lib/speechrecorder/session/progress.d.ts +1 -1
  65. package/lib/speechrecorder/session/prompting.d.ts +1 -1
  66. package/lib/speechrecorder/session/recorder_combi_pane.d.ts +3 -1
  67. package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -0
  68. package/lib/speechrecorder/session/recording_list.d.ts +3 -1
  69. package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +2 -1
  70. package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -0
  71. package/lib/speechrecorder/session/sessionmanager.d.ts +5 -11
  72. package/lib/speechrecorderng.module.d.ts +2 -1
  73. package/lib/spr.module.version.d.ts +1 -1
  74. package/package.json +1 -1
@@ -36,6 +36,7 @@ import * as i2$2 from '@angular/material/table';
36
36
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
37
37
  import { MatSelectModule } from '@angular/material/select';
38
38
  import { MatInputModule } from '@angular/material/input';
39
+ import { MatMenuModule } from '@angular/material/menu';
39
40
 
40
41
  class ActionEvent {
41
42
  constructor(_value = null) {
@@ -109,6 +110,514 @@ class Action {
109
110
  }
110
111
  }
111
112
 
113
+ class ArrayAudioBufferInputStream {
114
+ constructor(arrayAudioBuffer) {
115
+ this.arrayAudioBuffer = arrayAudioBuffer;
116
+ this._framePos = 0;
117
+ this.chunkFramePos = 0;
118
+ this.chunkIdx = 0;
119
+ this.eod = false;
120
+ //console.debug("Array audio input stream array audio buffer frames: "+arrayAudioBuffer.frameLen);
121
+ }
122
+ close() {
123
+ }
124
+ skipFrames(n) {
125
+ let toSkip = n;
126
+ if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
127
+ this.eod = true;
128
+ if (toSkip > 0) {
129
+ throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
130
+ }
131
+ }
132
+ else {
133
+ let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
134
+ let chunkBufsLen = chunkBuf0.length;
135
+ let currBufSkippable = chunkBufsLen - this.chunkFramePos;
136
+ if (n >= currBufSkippable) {
137
+ this._framePos += currBufSkippable;
138
+ toSkip -= currBufSkippable;
139
+ this.chunkIdx++;
140
+ this.chunkFramePos = 0;
141
+ if (this.chunkIdx < this.arrayAudioBuffer.chunkCount) {
142
+ this.skipFrames(toSkip);
143
+ }
144
+ else {
145
+ if (toSkip > 0) {
146
+ throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
147
+ }
148
+ }
149
+ }
150
+ else {
151
+ toSkip -= n;
152
+ this.chunkFramePos += n;
153
+ }
154
+ }
155
+ }
156
+ read(buffers) {
157
+ let read = 0;
158
+ let toRead = buffers[0].length;
159
+ while (read < toRead && !this.eod) {
160
+ //console.debug("Chunk "+this.chunkIdx+" of "+this.arrayAudioBuffer.chunkCount+" chunks.")
161
+ if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
162
+ this.eod = true;
163
+ //console.debug("Array audio input stream end of data read frames: "+this.framePos);
164
+ }
165
+ else {
166
+ let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
167
+ let chunkBufsLen = chunkBuf0.length;
168
+ let chunkBufAvail = chunkBufsLen - this.chunkFramePos;
169
+ let r = chunkBufAvail;
170
+ if (r > toRead - read) {
171
+ r = toRead - read;
172
+ }
173
+ for (let ch = 0; ch < buffers.length; ch++) {
174
+ let chChunkBuf = this.arrayAudioBuffer.data[ch][this.chunkIdx];
175
+ for (let bi = 0; bi < r; bi++) {
176
+ buffers[ch][read + bi] = chChunkBuf[this.chunkFramePos + bi];
177
+ }
178
+ }
179
+ read += r;
180
+ this.chunkFramePos += r;
181
+ if (this.chunkFramePos >= chunkBufsLen) {
182
+ this.chunkIdx++;
183
+ this.chunkFramePos = 0;
184
+ }
185
+ }
186
+ }
187
+ this._framePos += read;
188
+ //console.debug("Read: "+read+", frame pos: "+this.framePos)
189
+ if (this._framePos > this.arrayAudioBuffer.frameLen) {
190
+ console.error("Array audio input stream frame pos: " + this._framePos + " greater then frame length: " + this.arrayAudioBuffer.frameLen);
191
+ }
192
+ return read;
193
+ }
194
+ get framePos() {
195
+ return this._framePos;
196
+ }
197
+ }
198
+
199
+ class EditFloat32ArrayInputStream {
200
+ constructor(_srcInputStream, offset = 0, length) {
201
+ this._srcInputStream = _srcInputStream;
202
+ this.offset = offset;
203
+ this.length = length;
204
+ this.framePos = 0;
205
+ this.readFrames = 0;
206
+ if (this.offset < 0) {
207
+ throw Error('Parameter offset must be undefined or greater or equal zero.');
208
+ }
209
+ if (this.length !== undefined && this.length < 0) {
210
+ throw Error('Parameter length must be undefined or greater or equal zero.');
211
+ }
212
+ }
213
+ skipToOffset() {
214
+ if (this.framePos == 0 && this.offset > 0) {
215
+ this._srcInputStream.skipFrames(this.offset);
216
+ this.framePos += this.offset;
217
+ }
218
+ }
219
+ read(buffers) {
220
+ this.skipToOffset();
221
+ let read = 0;
222
+ if (this.length === undefined) {
223
+ read = this._srcInputStream.read(buffers);
224
+ }
225
+ else {
226
+ if (buffers.length > 0) {
227
+ let bufsCh0 = buffers[0];
228
+ let bufsLen = bufsCh0.length;
229
+ let avail = this.length - this.readFrames;
230
+ if (avail > 0) {
231
+ if (avail > bufsLen) {
232
+ read = this._srcInputStream.read(buffers);
233
+ }
234
+ else {
235
+ // temporary buffers required
236
+ let tmpBufs = new Array(buffers.length);
237
+ for (let ch = 0; ch < buffers.length; ch++) {
238
+ tmpBufs[ch] = new Float32Array(avail);
239
+ }
240
+ read = this._srcInputStream.read(tmpBufs);
241
+ for (let ch = 0; ch < buffers.length; ch++) {
242
+ buffers[ch].set(tmpBufs[ch]);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ this.readFrames += read;
249
+ this.framePos += read;
250
+ return read;
251
+ }
252
+ skipFrames(n) {
253
+ this.skipToOffset();
254
+ if (this.length === undefined) {
255
+ this._srcInputStream.skipFrames(n);
256
+ }
257
+ else {
258
+ let avail = this.length - this.readFrames;
259
+ if (avail > 0) {
260
+ if (avail >= n) {
261
+ this._srcInputStream.skipFrames(n);
262
+ this.readFrames += n;
263
+ this.framePos += n;
264
+ }
265
+ else {
266
+ throw Error('Tried to skip out of bounds.');
267
+ }
268
+ }
269
+ }
270
+ }
271
+ close() {
272
+ this._srcInputStream.close();
273
+ }
274
+ }
275
+ class Float32ArrayChunkerOutStream {
276
+ constructor(outStream) {
277
+ this.outStream = outStream;
278
+ this.bufs = new Array();
279
+ this._channels = 0;
280
+ this._chunkSize = 0;
281
+ this.filled = 0;
282
+ this.receivedFrames = 0;
283
+ this.sentFrames = 0;
284
+ }
285
+ createBuffers() {
286
+ this.bufs = new Array(this._channels);
287
+ for (let ch = 0; ch < this._channels; ch++) {
288
+ this.bufs[ch] = new Float32Array(this._chunkSize);
289
+ }
290
+ }
291
+ set chunkSize(chunkSize) {
292
+ this._chunkSize = chunkSize;
293
+ this.createBuffers();
294
+ }
295
+ get chunkSize() {
296
+ return this._chunkSize;
297
+ }
298
+ set channels(channels) {
299
+ this._channels = channels;
300
+ this.createBuffers();
301
+ }
302
+ get channels() {
303
+ return this._channels;
304
+ }
305
+ available() {
306
+ return this._chunkSize - this.filled;
307
+ }
308
+ write(buffers) {
309
+ let copied = 0;
310
+ if (buffers.length > 0) {
311
+ let buffersLen = buffers[0].length;
312
+ this.receivedFrames += buffersLen;
313
+ let avail = buffersLen;
314
+ // Fill out buffers until all values copied
315
+ while (avail > 0) {
316
+ let toFill = this._chunkSize - this.filled;
317
+ if (toFill > avail) {
318
+ toFill = avail;
319
+ }
320
+ let sliceEnd = copied + toFill;
321
+ // Firefox on Android sends only the first channel
322
+ for (let ch = 0; ch < buffersLen; ch++) {
323
+ if (buffers[ch]) {
324
+ let cpPrt = buffers[ch].slice(copied, sliceEnd);
325
+ let buf = this.bufs[ch];
326
+ buf.set(cpPrt, this.filled);
327
+ }
328
+ }
329
+ copied += toFill;
330
+ avail -= toFill;
331
+ this.filled += toFill;
332
+ if (this.filled == this._chunkSize) {
333
+ this.outStream.write(this.bufs);
334
+ this.sentFrames += this.filled;
335
+ this.filled = 0;
336
+ }
337
+ }
338
+ }
339
+ return copied;
340
+ }
341
+ flush() {
342
+ if (this.filled > 0) {
343
+ let restBufs = new Array(this._channels);
344
+ for (let ch = 0; ch < this._channels; ch++) {
345
+ restBufs[ch] = this.bufs[ch].slice(0, this.filled);
346
+ }
347
+ this.outStream.write(restBufs);
348
+ this.outStream.flush();
349
+ this.sentFrames += this.filled;
350
+ this.filled = 0;
351
+ }
352
+ }
353
+ close() {
354
+ this.outStream.close();
355
+ }
356
+ }
357
+
358
+ // Code from audio_source_worklet.js as string constant to be loaded as module
359
+ // Changes in audio_source_worklet.js must be copied and pasted to this string constant
360
+ const aswpStr = "\n" +
361
+ "// Important note: Changes in audio_source_worklet.js must be copied and pasted to the string constant aswpStr in array_audio_buffer_source_node.ts\n" +
362
+ "\n" +
363
+ "\n" +
364
+ "class AudioSourceProcessor extends AudioWorkletProcessor{\n" +
365
+ "\n" +
366
+ " filledFrames=0;\n" +
367
+ " audioBuffers=new Array();\n" +
368
+ " currentAudioBuffer=null;\n" +
369
+ " currentAudioBufferFramePos=0;\n" +
370
+ " currentAudioBufferAvail=0;\n" +
371
+ " running=false;\n" +
372
+ " ended=false;\n" +
373
+ "\n" +
374
+ " constructor() {\n" +
375
+ " super({numberOfInputs:0,numberOfOutputs:1});\n" +
376
+ " this.port.onmessage=(msgEv)=>{\n" +
377
+ " // received audio playback data from application\n" +
378
+ " //console.debug(\"Audio source worklet msg: Received.\");\n" +
379
+ "\n" +
380
+ " if(msgEv.data.cmd){\n" +
381
+ " if('data'===msgEv.data.cmd) {\n" +
382
+ " let chs = msgEv.data.chs;\n" +
383
+ "\n" +
384
+ " let audioData = new Array(chs);\n" +
385
+ " for (let ch = 0; ch < chs; ch++) {\n" +
386
+ " audioData[ch] = new Float32Array(msgEv.data.audioData[ch]);\n" +
387
+ " }\n" +
388
+ " let msgChBufLen=audioData[0].length;\n" +
389
+ " this.audioBuffers.push(audioData);\n" +
390
+ " this.filledFrames += msgChBufLen;\n" +
391
+ " //console.debug(\"Audio source worklet msg: Filled \" + this.filledFrames+ \" in \"+this.audioBuffers.length+\" buffers.\");\n" +
392
+ "\n" +
393
+ " }else if('start'===msgEv.data.cmd){\n" +
394
+ " this.running=true;\n" +
395
+ " }else if('stop'===msgEv.data.cmd){\n" +
396
+ " //console.debug(\"Stop...\");\n" +
397
+ " this.running=false;\n" +
398
+ " // clear buffers\n" +
399
+ " this.filledFrames=0;\n" +
400
+ " while(this.audioBuffers.length > 0) {\n" +
401
+ " this.audioBuffers.pop();\n" +
402
+ " }\n" +
403
+ " this.currentAudioBuffer=new Float32Array(0);\n" +
404
+ " }\n" +
405
+ " }\n" +
406
+ " }\n" +
407
+ " }\n" +
408
+ "\n" +
409
+ " process(\n" +
410
+ " inputs,\n" +
411
+ " outputs,\n" +
412
+ " parameters\n" +
413
+ " ){\n" +
414
+ " //console.debug(\"Audio source worklet: process \"+outputs.length+ \" output buffers.\");\n" +
415
+ " // copy ring buffer data to outputs\n" +
416
+ " if(!this.running){\n" +
417
+ " return !this.ended;\n" +
418
+ " }\n" +
419
+ "\n" +
420
+ " let output=outputs[0];\n" +
421
+ " let chs=output.length;\n" +
422
+ " //console.debug(\"Audio source worklet: Output channels: \"+chs);\n" +
423
+ " if(chs>0) {\n" +
424
+ "\n" +
425
+ " let outCh0 = output[0];\n" +
426
+ " let outChLen = outCh0.length;\n" +
427
+ "\n" +
428
+ " if(!this.currentAudioBuffer){\n" +
429
+ " // get first buffer\n" +
430
+ " let nxtBuff=this.audioBuffers.shift();\n" +
431
+ " if(nxtBuff) {\n" +
432
+ " this.currentAudioBuffer = nxtBuff;\n" +
433
+ " this.currentAudioBufferFramePos=0;\n" +
434
+ " this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
435
+ " }else{\n" +
436
+ " return true;\n" +
437
+ " }\n" +
438
+ " }\n" +
439
+ "\n" +
440
+ " let copied=0;\n" +
441
+ " do{\n" +
442
+ " if(this.currentAudioBufferAvail==0){\n" +
443
+ " let nxtBuff=this.audioBuffers.shift();\n" +
444
+ " if(nxtBuff){\n" +
445
+ " this.currentAudioBuffer=nxtBuff;\n" +
446
+ " this.currentAudioBufferFramePos=0;\n" +
447
+ " this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
448
+ " //console.debug(\"Next buffer with \"+this.currentAudioBufferAvail+ \" frames\");\n" +
449
+ " this.port.postMessage({eventType:'bufferNotification',filledFrames:this.filledFrames});\n" +
450
+ " }else{\n" +
451
+ " this.ended=true;\n" +
452
+ " this.port.postMessage({eventType:'ended'});\n" +
453
+ " //console.debug(\"Stream ended\");\n" +
454
+ " break;\n" +
455
+ " }\n" +
456
+ " }\n" +
457
+ " //console.debug(\"outChLen: \"+outChLen+\", copied: \"+copied+\", current avail: \"+this.currentAudioBufferAvail);\n" +
458
+ " let toCopy=outChLen-copied;\n" +
459
+ " if(toCopy>this.currentAudioBufferAvail){\n" +
460
+ " toCopy=this.currentAudioBufferAvail;\n" +
461
+ " }\n" +
462
+ " //console.debug(\"Copy \"+toCopy+\" frames...\");\n" +
463
+ " for(let ch=0;ch<chs;ch++) {\n" +
464
+ " let outCh=output[ch];\n" +
465
+ " for (let i = 0; i < toCopy; i++) {\n" +
466
+ " outCh[copied+i]=this.currentAudioBuffer[ch][this.currentAudioBufferFramePos+i];\n" +
467
+ " }\n" +
468
+ " }\n" +
469
+ " copied+=toCopy;\n" +
470
+ " this.currentAudioBufferFramePos+=toCopy;\n" +
471
+ " this.currentAudioBufferAvail-=toCopy;\n" +
472
+ "\n" +
473
+ " }while(copied<outChLen);\n" +
474
+ " this.filledFrames-=copied;\n" +
475
+ " //console.debug(\"Copied \"+copied+\" frames.\");\n" +
476
+ " }\n" +
477
+ " return !this.ended;\n" +
478
+ " }\n" +
479
+ "}\n" +
480
+ "\n" +
481
+ "registerProcessor('audio-source-worklet',AudioSourceProcessor);\n";
482
+ class ArrayAudioBufferSourceNode extends AudioWorkletNode {
483
+ constructor(context) {
484
+ super(context, 'audio-source-worklet');
485
+ this._bufferFillSeconds = ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS;
486
+ this.bufferFillFrames = 0;
487
+ this._arrayAudioBuffer = null;
488
+ this._audioInputStream = null;
489
+ this._aisBufs = null;
490
+ this.filledFrames = 0;
491
+ this.onended = null;
492
+ this.channelCountMode = 'explicit';
493
+ this.port.onmessage = (msgEv) => {
494
+ if (msgEv.data) {
495
+ let evType = msgEv.data.eventType;
496
+ if (evType) {
497
+ if ('bufferNotification' === evType) {
498
+ this.filledFrames = msgEv.data.filledFrames;
499
+ //console.debug("Buffer notification: filled frames: " + this.filledFrames);
500
+ this.fillBuffer();
501
+ }
502
+ else if ('ended' === evType) {
503
+ let drainTime = 0;
504
+ if (this._arrayAudioBuffer?.sampleRate) {
505
+ drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._arrayAudioBuffer.sampleRate;
506
+ }
507
+ //let dstAny:any=this.context.destination;
508
+ //console.debug('Destination node tail-time: '+dstAny['tail-time']);
509
+ window.setTimeout(() => {
510
+ this.onended?.call(this);
511
+ }, drainTime * 1000);
512
+ }
513
+ }
514
+ }
515
+ };
516
+ }
517
+ fillBuffer(frameOffset) {
518
+ if (this._arrayAudioBuffer && this._audioInputStream && this._aisBufs) {
519
+ let filled = this.filledFrames;
520
+ let bufLen = 0;
521
+ while (filled < this.bufferFillFrames) {
522
+ let read = this._audioInputStream.read(this._aisBufs);
523
+ //console.log("ArrayAudioBufferSourceNode: read: "+read)
524
+ if (read) {
525
+ let trBuffers = new Array(this.channelCount);
526
+ for (let ch = 0; ch < this.channelCount; ch++) {
527
+ let adCh = this._aisBufs[ch];
528
+ let adChCopy = new Float32Array(adCh.length);
529
+ bufLen = adChCopy.length;
530
+ adChCopy.set(adCh);
531
+ trBuffers[ch] = adChCopy.buffer;
532
+ }
533
+ this.port.postMessage({
534
+ cmd: 'data',
535
+ chs: this.channelCount,
536
+ audioData: trBuffers
537
+ }, trBuffers);
538
+ filled += read;
539
+ }
540
+ else {
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ }
546
+ get arrayAudioBuffer() {
547
+ return this._arrayAudioBuffer;
548
+ }
549
+ set arrayAudioBuffer(value) {
550
+ this._arrayAudioBuffer = value;
551
+ if (this._arrayAudioBuffer?.channelCount) {
552
+ this.channelCount = this._arrayAudioBuffer?.channelCount;
553
+ this.bufferFillFrames = this._arrayAudioBuffer.sampleRate * this._bufferFillSeconds;
554
+ }
555
+ }
556
+ get bufferFillSeconds() {
557
+ return this._bufferFillSeconds;
558
+ }
559
+ set bufferFillSeconds(value) {
560
+ this._bufferFillSeconds = value;
561
+ }
562
+ start(when, offset, duration) {
563
+ if (when) {
564
+ throw Error("when, offest,duration parameters currently not supported by ArrayAudioBufferSourceNode class");
565
+ }
566
+ if (this._arrayAudioBuffer) {
567
+ let arrAis = new ArrayAudioBufferInputStream(this._arrayAudioBuffer);
568
+ if (offset === undefined && duration === undefined) {
569
+ this._audioInputStream = arrAis;
570
+ }
571
+ else {
572
+ let offsetFrames = 0;
573
+ let durationFrames = undefined;
574
+ if (offset !== undefined) {
575
+ offsetFrames = Math.floor(offset * this._arrayAudioBuffer.sampleRate);
576
+ }
577
+ if (duration !== undefined) {
578
+ durationFrames = Math.floor(duration * this._arrayAudioBuffer.sampleRate);
579
+ }
580
+ this._audioInputStream = new EditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
581
+ }
582
+ let chs = this._arrayAudioBuffer.channelCount;
583
+ this._aisBufs = new Array(chs);
584
+ for (let ch = 0; ch < chs; ch++) {
585
+ this._aisBufs[ch] = new Float32Array(1024);
586
+ }
587
+ this.fillBuffer();
588
+ this.port.postMessage({ cmd: 'start' });
589
+ }
590
+ }
591
+ stop() {
592
+ this.port.postMessage({ cmd: 'stop' });
593
+ this.onended?.call(this);
594
+ }
595
+ }
596
+ ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN = 128;
597
+ ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS = 10;
598
+ class AudioSourceWorkletModuleLoader {
599
+ static loadModule(context) {
600
+ return new Promise((resolve, reject) => {
601
+ if (AudioSourceWorkletModuleLoader.moduleLoaded) {
602
+ resolve.call(self);
603
+ }
604
+ else {
605
+ let audioWorkletModuleBlob = new Blob([aswpStr], { type: 'text/javascript' });
606
+ let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
607
+ context.resume();
608
+ context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
609
+ AudioSourceWorkletModuleLoader.moduleLoaded = true;
610
+ resolve.call(self);
611
+ }).catch((reason) => {
612
+ console.error(reason);
613
+ reject.call(reason);
614
+ });
615
+ }
616
+ });
617
+ }
618
+ }
619
+ AudioSourceWorkletModuleLoader.moduleLoaded = false;
620
+
112
621
  var EventType;
113
622
  (function (EventType) {
114
623
  EventType[EventType["CLOSED"] = 0] = "CLOSED";
@@ -135,10 +644,17 @@ class AudioPlayer {
135
644
  this.running = false;
136
645
  this._audioClip = null;
137
646
  this._audioBuffer = null;
647
+ this._arrayAudioBuffer = null;
138
648
  this.sourceBufferNode = null;
649
+ this.sourceAudioWorkletNode = null;
139
650
  this.playStartTime = null;
140
651
  this.timerVar = null;
141
652
  this.context = context;
653
+ // AudioSourceWorkletModuleLoader.loadModule(this.context).then(()=>{
654
+ // console.debug("Audio source worklet module loaded.");
655
+ // }).catch((error: any)=>{
656
+ // console.error('Could not add module '+error);
657
+ // });
142
658
  this.listener = listener;
143
659
  this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
144
660
  this.n = navigator;
@@ -169,17 +685,23 @@ class AudioPlayer {
169
685
  return this._stopAction;
170
686
  }
171
687
  set audioClip(audioClip) {
172
- var length = 0;
173
- var chs = 0;
174
- if (audioClip && audioClip.buffer) {
175
- chs = audioClip.buffer.numberOfChannels;
688
+ let length = 0;
689
+ let chs = 0;
690
+ if (audioClip && audioClip.audioDataHolder) {
691
+ let audioDataHolder = audioClip.audioDataHolder;
692
+ chs = audioDataHolder.numberOfChannels;
176
693
  if (chs > 0) {
177
- length = audioClip.buffer.length;
694
+ length = audioDataHolder.frameLen;
178
695
  if (chs > this.context.destination.maxChannelCount) {
179
696
  // TODO exception
180
697
  }
181
698
  }
182
- this.audioBuffer = audioClip.buffer;
699
+ if (audioDataHolder.buffer) {
700
+ this.audioBuffer = audioDataHolder.buffer;
701
+ }
702
+ if (audioDataHolder.arrayBuffer) {
703
+ this.arrayAudioBuffer = audioDataHolder.arrayBuffer;
704
+ }
183
705
  audioClip.addSelectionObserver((ac) => {
184
706
  this._startSelectionAction.disabled = this.startSelectionDisabled();
185
707
  if (!this.startSelectionAction.disabled && this._autoPlayOnSelectToggleAction.value) {
@@ -189,12 +711,14 @@ class AudioPlayer {
189
711
  }
190
712
  else {
191
713
  this.audioBuffer = null;
714
+ //this.arrayAudioBuffer=null;
192
715
  }
193
716
  this._audioClip = audioClip;
194
717
  }
195
718
  set audioBuffer(audioBuffer) {
196
719
  this.stop();
197
720
  this._audioBuffer = audioBuffer;
721
+ this._arrayAudioBuffer = null;
198
722
  if (audioBuffer && this.context) {
199
723
  this._startAction.disabled = false;
200
724
  this._startSelectionAction.disabled = this.startSelectionDisabled();
@@ -213,23 +737,86 @@ class AudioPlayer {
213
737
  get audioBuffer() {
214
738
  return this._audioBuffer;
215
739
  }
216
- start() {
217
- if (!this._startAction.disabled && !this.running) {
218
- this.context.resume();
219
- this.sourceBufferNode = this.context.createBufferSource();
220
- this.sourceBufferNode.buffer = this._audioBuffer;
221
- this.sourceBufferNode.connect(this.context.destination);
222
- this.sourceBufferNode.onended = () => this.onended();
223
- this.playStartTime = this.context.currentTime;
224
- this.running = true;
225
- this.sourceBufferNode.start();
226
- this.playStartTime = this.context.currentTime;
740
+ set arrayAudioBuffer(arrayAudioBuffer) {
741
+ this.stop();
742
+ this._audioBuffer = null;
743
+ this._arrayAudioBuffer = arrayAudioBuffer;
744
+ if (arrayAudioBuffer && this.context) {
745
+ AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
746
+ this._startAction.disabled = false;
747
+ this._startSelectionAction.disabled = this.startSelectionDisabled();
748
+ if (this.listener) {
749
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
750
+ }
751
+ }).catch((error) => {
752
+ this._startAction.disabled = true;
753
+ this._startSelectionAction.disabled = true;
754
+ if (this.listener) {
755
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
756
+ }
757
+ console.error('Could not add module ' + error);
758
+ });
759
+ }
760
+ else {
227
761
  this._startAction.disabled = true;
228
762
  this._startSelectionAction.disabled = true;
229
- this._stopAction.disabled = false;
230
- //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
231
763
  if (this.listener) {
232
- this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
764
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
765
+ }
766
+ }
767
+ }
768
+ get arrayAudioBuffer() {
769
+ return this._arrayAudioBuffer;
770
+ }
771
+ start() {
772
+ if (!this._startAction.disabled && !this.running) {
773
+ this.context.resume();
774
+ if (this._audioBuffer) {
775
+ this.sourceBufferNode = this.context.createBufferSource();
776
+ this.sourceBufferNode.buffer = this._audioBuffer;
777
+ this.sourceBufferNode.connect(this.context.destination);
778
+ this.sourceBufferNode.onended = () => this.onended();
779
+ this.playStartTime = this.context.currentTime;
780
+ this.running = true;
781
+ this.sourceBufferNode.start();
782
+ this.playStartTime = this.context.currentTime;
783
+ this._startAction.disabled = true;
784
+ this._startSelectionAction.disabled = true;
785
+ this._stopAction.disabled = false;
786
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
787
+ if (this.listener) {
788
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
789
+ }
790
+ }
791
+ else if (this._arrayAudioBuffer) {
792
+ if (this._arrayAudioBuffer) {
793
+ this.sourceAudioWorkletNode = new ArrayAudioBufferSourceNode(this.context);
794
+ this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
795
+ let msg = 'Unknwon error';
796
+ if (ev instanceof ErrorEvent) {
797
+ msg = ev.message;
798
+ }
799
+ console.error("Audio source worklet error: " + msg);
800
+ if (this.listener) {
801
+ // TODO
802
+ // this.listener.error(msg);
803
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
804
+ }
805
+ };
806
+ this.sourceAudioWorkletNode.arrayAudioBuffer = this._arrayAudioBuffer;
807
+ this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
808
+ this.sourceAudioWorkletNode.onended = () => this.onended();
809
+ this.playStartTime = this.context.currentTime;
810
+ this.running = true;
811
+ this.sourceAudioWorkletNode.start();
812
+ this.playStartTime = this.context.currentTime;
813
+ this._startAction.disabled = true;
814
+ this._startSelectionAction.disabled = true;
815
+ this._stopAction.disabled = false;
816
+ if (this.listener) {
817
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
818
+ }
819
+ }
233
820
  }
234
821
  }
235
822
  }
@@ -239,34 +826,79 @@ class AudioPlayer {
239
826
  startSelected() {
240
827
  if (!this._startAction.disabled && !this.running) {
241
828
  this.context.resume();
242
- this.sourceBufferNode = this.context.createBufferSource();
243
- this.sourceBufferNode.buffer = this._audioBuffer;
244
- this.sourceBufferNode.connect(this.context.destination);
245
- this.sourceBufferNode.onended = () => this.onended();
246
- this.playStartTime = this.context.currentTime;
247
- this.running = true;
248
- // unfortunately Web Audio API uses time values not frames
249
- let ac = this._audioClip;
250
- let offset = 0;
251
- if (ac && ac.selection) {
252
- let s = ac.selection;
253
- let sr = ac.buffer.sampleRate;
254
- offset = s.leftFrame / sr;
255
- let stopPosInsecs = s.rightFrame / sr;
256
- let dur = stopPosInsecs - offset;
257
- // TODO check valid values
258
- this.sourceBufferNode.start(0, offset, dur);
259
- }
260
- else {
261
- this.sourceBufferNode.start();
262
- }
263
- this.playStartTime = this.context.currentTime - offset;
264
- this._startAction.disabled = true;
265
- this._startSelectionAction.disabled = true;
266
- this._stopAction.disabled = false;
267
- //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
268
- if (this.listener) {
269
- this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
829
+ if (this._audioBuffer) {
830
+ this.sourceBufferNode = this.context.createBufferSource();
831
+ this.sourceBufferNode.buffer = this._audioBuffer;
832
+ this.sourceBufferNode.connect(this.context.destination);
833
+ this.sourceBufferNode.onended = () => this.onended();
834
+ this.playStartTime = this.context.currentTime;
835
+ this.running = true;
836
+ // unfortunately Web Audio API uses time values not frames
837
+ let ac = this._audioClip;
838
+ let offset = 0;
839
+ if (ac && ac.selection) {
840
+ let s = ac.selection;
841
+ let sr = ac.audioDataHolder.sampleRate;
842
+ offset = s.leftFrame / sr;
843
+ let stopPosInsecs = s.rightFrame / sr;
844
+ let dur = stopPosInsecs - offset;
845
+ // TODO check valid values
846
+ this.sourceBufferNode.start(0, offset, dur);
847
+ }
848
+ else {
849
+ this.sourceBufferNode.start();
850
+ }
851
+ this.playStartTime = this.context.currentTime - offset;
852
+ this._startAction.disabled = true;
853
+ this._startSelectionAction.disabled = true;
854
+ this._stopAction.disabled = false;
855
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
856
+ if (this.listener) {
857
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
858
+ }
859
+ }
860
+ else if (this._arrayAudioBuffer) {
861
+ if (this._arrayAudioBuffer) {
862
+ let aabsn = new ArrayAudioBufferSourceNode(this.context);
863
+ this.sourceAudioWorkletNode = aabsn;
864
+ aabsn.onprocessorerror = (ev) => {
865
+ let msg = 'Unknwon error';
866
+ if (ev instanceof ErrorEvent) {
867
+ msg = ev.message;
868
+ }
869
+ console.error("Audio source worklet error: " + msg);
870
+ if (this.listener) {
871
+ // TODO
872
+ // this.listener.error(msg);
873
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
874
+ }
875
+ };
876
+ aabsn.arrayAudioBuffer = this._arrayAudioBuffer;
877
+ aabsn.connect(this.context.destination); // this already starts playing
878
+ aabsn.onended = () => this.onended();
879
+ this.playStartTime = this.context.currentTime;
880
+ this.running = true;
881
+ let ac = this._audioClip;
882
+ let offset = 0;
883
+ if (ac && ac.selection) {
884
+ let s = ac.selection;
885
+ let sr = ac.audioDataHolder.sampleRate;
886
+ offset = s.leftFrame / sr;
887
+ let stopPosInsecs = s.rightFrame / sr;
888
+ let dur = stopPosInsecs - offset;
889
+ aabsn.start(0, offset, dur);
890
+ }
891
+ else {
892
+ aabsn.start();
893
+ }
894
+ this.playStartTime = this.context.currentTime - offset;
895
+ this._startAction.disabled = true;
896
+ this._startSelectionAction.disabled = true;
897
+ this._stopAction.disabled = false;
898
+ if (this.listener) {
899
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
900
+ }
901
+ }
270
902
  }
271
903
  }
272
904
  }
@@ -275,6 +907,9 @@ class AudioPlayer {
275
907
  if (this.sourceBufferNode) {
276
908
  this.sourceBufferNode.stop();
277
909
  }
910
+ if (this.sourceAudioWorkletNode) {
911
+ this.sourceAudioWorkletNode.stop();
912
+ }
278
913
  if (this.timerVar !== null) {
279
914
  window.clearInterval(this.timerVar);
280
915
  }
@@ -288,7 +923,7 @@ class AudioPlayer {
288
923
  if (this.timerVar != null) {
289
924
  window.clearInterval(this.timerVar);
290
925
  }
291
- this._startAction.disabled = !(this.audioBuffer);
926
+ this._startAction.disabled = !(this.audioBuffer || this.arrayAudioBuffer);
292
927
  this._startSelectionAction.disabled = this.startSelectionDisabled();
293
928
  this._stopAction.disabled = true;
294
929
  this.running = false;
@@ -305,10 +940,17 @@ class AudioPlayer {
305
940
  }
306
941
  get playPositionFrames() {
307
942
  let ppFrs = null;
943
+ let sr = null;
308
944
  if (this._audioBuffer) {
945
+ sr = this._audioBuffer.sampleRate;
946
+ }
947
+ else if (this._arrayAudioBuffer) {
948
+ sr = this._arrayAudioBuffer.sampleRate;
949
+ }
950
+ if (sr) {
309
951
  let ppTime = this.playPositionTime;
310
952
  if (ppTime !== null) {
311
- ppFrs = this._audioBuffer.sampleRate * ppTime;
953
+ ppFrs = sr * ppTime;
312
954
  }
313
955
  }
314
956
  return ppFrs;
@@ -522,6 +1164,107 @@ class ProjectUtil {
522
1164
  }
523
1165
  ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
524
1166
 
1167
+ class ArrayAudioBuffer {
1168
+ constructor(_channelCount, _sampleRate, _data) {
1169
+ this._channelCount = _channelCount;
1170
+ this._sampleRate = _sampleRate;
1171
+ this._data = _data;
1172
+ this._chunkCount = 0;
1173
+ this._frameLen = 0;
1174
+ if (this._data.length > 0) {
1175
+ let ch0Data = this.data[0];
1176
+ for (let ch0Chk of ch0Data) {
1177
+ this._chunkCount++;
1178
+ this._frameLen += ch0Chk.length;
1179
+ }
1180
+ }
1181
+ }
1182
+ static fromAudioBuffer(audioBuffer, chunkFrameSize = 8192) {
1183
+ let aab;
1184
+ let chs = audioBuffer.numberOfChannels;
1185
+ let frameLength = audioBuffer.length;
1186
+ //let chunksSize=Math.ceil(frameLength/chunkFrameSize);
1187
+ let framePos = 0;
1188
+ let data = new Array(chs);
1189
+ for (let ch = 0; ch < chs; ch++) {
1190
+ data[ch] = new Array();
1191
+ }
1192
+ let toCopy = frameLength - framePos;
1193
+ while (toCopy > 0) {
1194
+ let toCopyChunk = chunkFrameSize;
1195
+ if (toCopyChunk > toCopy) {
1196
+ // last chunk, the rest
1197
+ toCopyChunk = toCopy;
1198
+ }
1199
+ for (let ch = 0; ch < chs; ch++) {
1200
+ data[ch].push(audioBuffer.getChannelData(ch).slice(framePos, framePos + toCopyChunk));
1201
+ }
1202
+ framePos += toCopyChunk;
1203
+ toCopy -= toCopyChunk;
1204
+ }
1205
+ aab = new ArrayAudioBuffer(chs, audioBuffer.sampleRate, data);
1206
+ return aab;
1207
+ }
1208
+ get channelCount() {
1209
+ return this._channelCount;
1210
+ }
1211
+ frames(framePos, frameLen, bufs) {
1212
+ let ccFramePos = 0;
1213
+ let trgFramePos = framePos;
1214
+ let ch0Data = this.data[0];
1215
+ let cPos = 0;
1216
+ let filled = 0;
1217
+ let ci = 0;
1218
+ while (filled < frameLen && ci < this._chunkCount) {
1219
+ // Current chunk
1220
+ let cc0 = ch0Data[ci];
1221
+ let ccLen = cc0.length;
1222
+ let ccFrameEndPos = ccFramePos + ccLen;
1223
+ if (trgFramePos >= ccFramePos && trgFramePos < ccFrameEndPos) {
1224
+ let toCp = frameLen - filled;
1225
+ cPos = trgFramePos - ccFramePos;
1226
+ if (cPos + toCp > ccLen) {
1227
+ toCp = ccLen - cPos;
1228
+ }
1229
+ for (let ch = 0; ch < bufs.length; ch++) {
1230
+ let cc = this.data[ch][ci];
1231
+ for (let i = 0; i < toCp; i++) {
1232
+ bufs[ch][filled + i] = cc[cPos + i];
1233
+ }
1234
+ }
1235
+ filled += toCp;
1236
+ trgFramePos += toCp;
1237
+ cPos += toCp;
1238
+ ccFramePos += toCp;
1239
+ if (cPos >= ccLen) {
1240
+ ccFramePos = ccFrameEndPos;
1241
+ cPos = 0;
1242
+ ci++;
1243
+ }
1244
+ }
1245
+ else {
1246
+ // next chunk
1247
+ ccFramePos = ccFrameEndPos;
1248
+ cPos = 0;
1249
+ ci++;
1250
+ }
1251
+ }
1252
+ return filled;
1253
+ }
1254
+ get sampleRate() {
1255
+ return this._sampleRate;
1256
+ }
1257
+ get frameLen() {
1258
+ return this._frameLen;
1259
+ }
1260
+ get chunkCount() {
1261
+ return this._chunkCount;
1262
+ }
1263
+ get data() {
1264
+ return this._data;
1265
+ }
1266
+ }
1267
+
525
1268
  const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
526
1269
  const DEBUG_TRACE_LEVEL = 0;
527
1270
  const ENABLE_AUDIO_WORKLET = true;
@@ -1092,6 +1835,10 @@ class AudioCapture {
1092
1835
  }
1093
1836
  return ab;
1094
1837
  }
1838
+ audioBufferArray() {
1839
+ let aba = new ArrayAudioBuffer(this.channelCount, this.currentSampleRate, this.data);
1840
+ return aba;
1841
+ }
1095
1842
  }
1096
1843
  AudioCapture.BUFFER_SIZE = 8192;
1097
1844
 
@@ -1471,21 +2218,28 @@ class RecordingFileDescriptorImpl {
1471
2218
  constructor() { }
1472
2219
  }
1473
2220
  class RecordingFile {
1474
- constructor(uuid, sessionId, audioBuffer) {
2221
+ constructor(uuid, sessionId, audioDataHolder) {
1475
2222
  this.recordingFileId = null;
1476
2223
  this.uuid = null;
2224
+ this.serverPersisted = false;
2225
+ this.keepAudioDataCache = false;
1477
2226
  this.date = null;
1478
2227
  this._dateAsDateObj = null;
1479
2228
  this.startedDate = null;
1480
2229
  this._startedAsDateObj = null;
1481
- this.audioBuffer = null;
2230
+ this.audioDataHolder = null;
1482
2231
  this.session = null;
1483
2232
  this.frames = null;
2233
+ this.timeLength = null;
1484
2234
  this.editSampleRate = null;
1485
2235
  this.editStartFrame = null;
1486
2236
  this.editEndFrame = null;
1487
2237
  this.session = sessionId;
1488
- this.audioBuffer = audioBuffer;
2238
+ this.audioDataHolder = audioDataHolder;
2239
+ if (audioDataHolder) {
2240
+ this.frames = audioDataHolder.frameLen;
2241
+ this.timeLength = audioDataHolder.duration;
2242
+ }
1489
2243
  this.uuid = uuid;
1490
2244
  }
1491
2245
  filenameString() {
@@ -1497,14 +2251,27 @@ class RecordingFile {
1497
2251
  fns += this.uuid;
1498
2252
  return fns;
1499
2253
  }
2254
+ equals(otherRecordingFile) {
2255
+ if (otherRecordingFile !== null) {
2256
+ if (otherRecordingFile === this) {
2257
+ return true;
2258
+ }
2259
+ if (otherRecordingFile.uuid === this.uuid) {
2260
+ return true;
2261
+ }
2262
+ }
2263
+ return false;
2264
+ }
2265
+ toString() {
2266
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session;
2267
+ }
1500
2268
  }
1501
2269
  class SprRecordingFile extends RecordingFile {
1502
- constructor(sessionId, itemcode, version, audioBuffer) {
1503
- super(UUID.generate(), sessionId, audioBuffer);
2270
+ constructor(sessionId, itemcode, version, audioDataHolder) {
2271
+ super(UUID.generate(), sessionId, audioDataHolder);
1504
2272
  this.session = sessionId;
1505
2273
  this.itemCode = itemcode;
1506
2274
  this.version = version;
1507
- this.audioBuffer = audioBuffer;
1508
2275
  }
1509
2276
  filenameString() {
1510
2277
  let fns = '';
@@ -1519,6 +2286,42 @@ class SprRecordingFile extends RecordingFile {
1519
2286
  fns += this.uuid;
1520
2287
  return fns;
1521
2288
  }
2289
+ toString() {
2290
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session + ', itemcode: ' + this.itemCode + ', version: ' + this.version + ', UUID: ' + this.uuid;
2291
+ }
2292
+ }
2293
+ class RecordingFileUtils {
2294
+ static equals(recordinFile, otherRecordingFile) {
2295
+ if (recordinFile && otherRecordingFile) {
2296
+ if (otherRecordingFile === recordinFile) {
2297
+ return true;
2298
+ }
2299
+ if (otherRecordingFile.uuid === recordinFile.uuid) {
2300
+ return true;
2301
+ }
2302
+ }
2303
+ return false;
2304
+ }
2305
+ static setAudioData(rf, audioDataHolder) {
2306
+ rf.audioDataHolder = audioDataHolder;
2307
+ if (audioDataHolder) {
2308
+ rf.frames = audioDataHolder.frameLen;
2309
+ rf.timeLength = audioDataHolder.duration;
2310
+ }
2311
+ }
2312
+ static sampleCount(rf) {
2313
+ if (rf.audioDataHolder) {
2314
+ return rf.audioDataHolder.sampleCounts();
2315
+ }
2316
+ else {
2317
+ return 0;
2318
+ }
2319
+ }
2320
+ static expireAudioData(rf) {
2321
+ let rv = rf.audioDataHolder;
2322
+ rf.audioDataHolder = null;
2323
+ return rv;
2324
+ }
1522
2325
  }
1523
2326
 
1524
2327
  // state of an upload
@@ -1569,7 +2372,8 @@ class UploaderStatusChangeEvent {
1569
2372
  }
1570
2373
  }
1571
2374
  class Upload {
1572
- constructor(blob, url) {
2375
+ constructor(blob, url, serverPersistable = null) {
2376
+ this.serverPersistable = serverPersistable;
1573
2377
  this.toString = () => {
1574
2378
  let s = `Upload: Status: ${this.status}, URL: ${this._url}`;
1575
2379
  if (this._data instanceof Blob) {
@@ -1590,6 +2394,17 @@ class Upload {
1590
2394
  get data() {
1591
2395
  return this._data;
1592
2396
  }
2397
+ done() {
2398
+ this.status = UploadStatus$1.DONE;
2399
+ //console.debug("Single upload done.");
2400
+ if (this.serverPersistable) {
2401
+ this.serverPersistable.serverPersisted = true;
2402
+ //console.debug("Single upload set server persisted: "+this.serverPersistable);
2403
+ }
2404
+ else {
2405
+ //console.debug("Server persistable not set.");
2406
+ }
2407
+ }
1593
2408
  }
1594
2409
  class Uploader {
1595
2410
  constructor(http, withCredentials = false) {
@@ -1628,7 +2443,7 @@ class Uploader {
1628
2443
  return si;
1629
2444
  }
1630
2445
  uploadDone(ul) {
1631
- ul.status = UploadStatus$1.DONE;
2446
+ ul.done();
1632
2447
  // remove upload from queue
1633
2448
  for (let i = 0; i < this.que.length; i++) {
1634
2449
  if (this.que[i] === ul) {
@@ -2059,15 +2874,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2059
2874
  }], ctorParameters: function () { return []; } });
2060
2875
 
2061
2876
  class AudioClip {
2062
- constructor(buffer) {
2877
+ constructor(_audioDataHolder) {
2878
+ this._audioDataHolder = _audioDataHolder;
2063
2879
  this._selection = null;
2880
+ this._levelInfos = null;
2064
2881
  this.selectionObservers = new Array();
2065
- this._buffer = buffer;
2066
2882
  }
2067
- get buffer() {
2068
- return this._buffer;
2883
+ get audioDataHolder() {
2884
+ return this._audioDataHolder;
2069
2885
  }
2070
- ;
2071
2886
  get selection() {
2072
2887
  return this._selection;
2073
2888
  }
@@ -2082,6 +2897,12 @@ class AudioClip {
2082
2897
  selObs(this);
2083
2898
  }
2084
2899
  }
2900
+ get levelInfos() {
2901
+ return this._levelInfos;
2902
+ }
2903
+ set levelInfos(value) {
2904
+ this._levelInfos = value;
2905
+ }
2085
2906
  addSelectionObserver(selectionObserver, init = false) {
2086
2907
  let obsAlreadyInList = this.selectionObservers.find((obs) => (obs === selectionObserver));
2087
2908
  if (!obsAlreadyInList) {
@@ -2164,19 +2985,31 @@ class ViewSelection {
2164
2985
  class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2165
2986
  constructor() {
2166
2987
  super(...arguments);
2167
- this._audioData = null;
2988
+ //protected _audioData: AudioBuffer|null=null;
2989
+ //protected _arrayAudioData: ArrayAudioBuffer|null=null;
2990
+ this._audioDataHolder = null;
2168
2991
  this._bgColor = 'white';
2169
2992
  this._selectColor = 'rgba(0%,0%,100%,25%)';
2170
2993
  }
2994
+ frameLength() {
2995
+ let frameLength = null;
2996
+ // if (this._audioData && this._audioData.numberOfChannels > 0) {
2997
+ // let ch0 = this._audioData.getChannelData(0);
2998
+ // frameLength = ch0.length;
2999
+ //
3000
+ // }else if(this._arrayAudioData){
3001
+ // frameLength=this._arrayAudioData.frameLen;
3002
+ // }
3003
+ return frameLength;
3004
+ }
2171
3005
  /**
2172
3006
  * Returns pixel position depending on current zoom setting.
2173
3007
  * @param framePos audio frame (sample) position
2174
3008
  */
2175
3009
  frameToXPixelPosition(framePos) {
2176
3010
  let pixelPos = null;
2177
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2178
- let ch0 = this._audioData.getChannelData(0);
2179
- let frameLength = ch0.length;
3011
+ let frameLength = this._audioDataHolder?.frameLen;
3012
+ if (frameLength !== undefined) {
2180
3013
  let vw;
2181
3014
  if (this.bounds) {
2182
3015
  vw = this.bounds.dimension.width;
@@ -2206,9 +3039,8 @@ class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2206
3039
  }
2207
3040
  viewPortXPixelToFramePosition(xViewPortPixelPos) {
2208
3041
  let vpXramePos = null;
2209
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2210
- let ch0 = this._audioData.getChannelData(0);
2211
- let frameLength = ch0.length;
3042
+ let frameLength = this._audioDataHolder?.frameLen;
3043
+ if (frameLength !== undefined) {
2212
3044
  let vw;
2213
3045
  if (this.bounds) {
2214
3046
  vw = this.bounds.dimension.width;
@@ -2385,8 +3217,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2385
3217
  if (viewSel) {
2386
3218
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2387
3219
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2388
- if (this._audioData && frameStart != null && frameEnd != null) {
2389
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3220
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3221
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2390
3222
  }
2391
3223
  }
2392
3224
  this.selectingEventEmitter.emit(ns);
@@ -2396,8 +3228,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2396
3228
  if (viewSel) {
2397
3229
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2398
3230
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2399
- if (this._audioData && frameStart != null && frameEnd != null) {
2400
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3231
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3232
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2401
3233
  }
2402
3234
  }
2403
3235
  this.selectedEventEmitter.emit(ns);
@@ -2478,7 +3310,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2478
3310
  g.lineTo(xViewPortPixelpos, h);
2479
3311
  g.closePath();
2480
3312
  g.stroke();
2481
- if (this._audioData) {
3313
+ if (this._audioDataHolder) {
2482
3314
  g.font = '14px sans-serif';
2483
3315
  g.fillStyle = 'yellow';
2484
3316
  g.fillText(framePos.toString(), xViewPortPixelpos + 2, 50);
@@ -2490,6 +3322,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2490
3322
  }
2491
3323
  }
2492
3324
  }
3325
+ AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD = 10 * 60 * 48000; // Use streaming/chunking if audio clip has more than this number of samples
2493
3326
  AudioCanvasLayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2494
3327
  AudioCanvasLayerComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: AudioCanvasLayerComponent, inputs: { pointerPosition: "pointerPosition", selecting: "selecting", selection: "selection" }, outputs: { pointerPositionEventEmitter: "pointerPositionEventEmitter", selectingEventEmitter: "selectingEventEmitter", selectedEventEmitter: "selectedEventEmitter" }, host: { listeners: { "document:mouseup": "onMouseup($event)" } }, viewQueries: [{ propertyName: "bgCanvasRef", first: true, predicate: ["bg"], descendants: true, static: true }, { propertyName: "cursorCanvasRef", first: true, predicate: ["cursor"], descendants: true, static: true }], usesInheritance: true, ngImport: i0 });
2495
3328
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, decorators: [{
@@ -2524,7 +3357,7 @@ class AudioSignal extends AudioCanvasLayerComponent {
2524
3357
  this._playFramePosition = null;
2525
3358
  this.worker = null;
2526
3359
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
2527
- this._audioData = null;
3360
+ this._audioDataHolder = null;
2528
3361
  this._bgColor = 'black';
2529
3362
  this._selectColor = 'rgba(255,255,0,0.8)';
2530
3363
  }
@@ -2577,25 +3410,26 @@ class AudioSignal extends AudioCanvasLayerComponent {
2577
3410
  */
2578
3411
  workerFunction() {
2579
3412
  addEventListener('message', ({ data }) => {
2580
- let audioData = data.audioData;
2581
- let l = data.l;
2582
- let w = data.w;
2583
- let h = data.h;
2584
- let vw = data.vw;
2585
- let chs = data.chs;
2586
- let frameLength = data.frameLength;
3413
+ let audioData = data.audioData; // audio data part required to render view port
3414
+ let auOffset = data.audioDataOffset;
3415
+ let l = data.l; // left pixel position of view port
3416
+ let w = data.w; // width of viewport
3417
+ let vw = data.vw; // total width of (virtual) audio view (not viewport width)
3418
+ let chs = data.chs; // number of channels
3419
+ let dataFrameLength = data.audioDataFrameLength; // frame length of audio data part required for view port
3420
+ let frameLength = data.frameLength; // total frame length (of audio clip)
3421
+ //console.debug("W: left: "+l+", w:"+w+", vw: "+vw+", chs: "+chs+", frameLength: "+frameLength);
2587
3422
  let psMinMax = new Float32Array(0);
2588
- if (audioData && w >= 0 && vw > 0) {
3423
+ if (audioData && audioData.length > 0 && w >= 0 && vw > 0) {
2589
3424
  let framesPerPixel = frameLength / vw;
2590
- let y = 0;
2591
3425
  let pointsLen = w * chs;
2592
3426
  // one for min one for max
2593
3427
  let arrLen = pointsLen * 2;
2594
3428
  psMinMax = new Float32Array(arrLen);
2595
3429
  let chFramePos = 0;
3430
+ let chFrameLength = audioData.length / chs;
2596
3431
  for (let ch = 0; ch < chs; ch++) {
2597
- let x = 0;
2598
- chFramePos = ch * frameLength;
3432
+ chFramePos = ch * chFrameLength;
2599
3433
  for (let pii = 0; pii < w; pii++) {
2600
3434
  let virtPii = l + pii;
2601
3435
  let pMin = Infinity;
@@ -2605,24 +3439,28 @@ class AudioSignal extends AudioCanvasLayerComponent {
2605
3439
  for (let ai = 0; ai < framesPerPixel; ai++) {
2606
3440
  let framePos = pixelFramePos + ai;
2607
3441
  let a = 0;
2608
- if (framePos >= 0 && framePos < audioData.length) {
2609
- a = audioData[framePos];
2610
- }
2611
- if (a < pMin) {
2612
- pMin = a;
2613
- }
2614
- if (a > pMax) {
2615
- pMax = a;
3442
+ let bufPos = framePos - auOffset;
3443
+ //let bufPos=framePos;
3444
+ if (bufPos >= 0 && bufPos < audioData.length) {
3445
+ a = audioData[bufPos];
3446
+ //console.debug("W: ch: "+ch+", pixelFramePos: "+pixelFramePos+", framePos: "+framePos+", auOffset: "+auOffset+", bufPos: "+bufPos+", audioData.length: "+audioData.length+", a: "+a);
3447
+ if (a < pMin) {
3448
+ pMin = a;
3449
+ }
3450
+ if (a > pMax) {
3451
+ pMax = a;
3452
+ }
2616
3453
  }
2617
3454
  }
2618
3455
  let psMinPos = ch * w + pii;
2619
3456
  psMinMax[psMinPos] = pMin;
2620
3457
  let psMaxPos = pointsLen + psMinPos;
2621
3458
  psMinMax[psMaxPos] = pMax;
3459
+ //console.debug("psMinMax["+psMinPos+"]="+pMin+",psMinMax["+psMaxPos+"]="+pMax);
2622
3460
  }
2623
3461
  }
2624
3462
  }
2625
- postMessage({ psMinMax: psMinMax, l: data.l, t: data.t, w: data.w, h: data.h, chs: data.chs }, [psMinMax.buffer]);
3463
+ postMessage({ psMinMax: psMinMax, l: data.l, w: data.w, chs: data.chs, eod: data.eod }, [psMinMax.buffer]);
2626
3464
  });
2627
3465
  }
2628
3466
  startDraw(clear = true) {
@@ -2650,41 +3488,143 @@ class AudioSignal extends AudioCanvasLayerComponent {
2650
3488
  if (this.bounds && this.bounds.dimension) {
2651
3489
  let w = Math.round(this.bounds.dimension.width);
2652
3490
  let h = Math.round(this.bounds.dimension.height);
2653
- if (this._audioData && w > 0 && h > 0) {
3491
+ if (this._audioDataHolder && w > 0 && h > 0) {
2654
3492
  //this.wo = new Worker('./audiosignal.worker.js',{type: 'module'});
2655
3493
  this.worker = new Worker(this.workerURL);
2656
3494
  //this.wo = new Worker('worker/audiosignal.worker.ts');
2657
3495
  //let Worker = require('worker!../../../workers/uploader/main');
2658
- let chs = this._audioData.numberOfChannels;
2659
- let frameLength = this._audioData.getChannelData(0).length;
2660
- // if(frameLength != this.audioData.getChannelData(1).length){
2661
- // alert("Ungleiche Länge");
2662
- // }
2663
- let ad = new Float32Array(chs * frameLength);
2664
- for (let ch = 0; ch < chs; ch++) {
2665
- ad.set(this._audioData.getChannelData(ch), ch * frameLength);
2666
- }
2667
- //let start = Date.now();
3496
+ let chs = this._audioDataHolder.numberOfChannels;
3497
+ let leftPos = Math.round(this.bounds.position.left);
3498
+ let renderPos = leftPos;
3499
+ let vw = Math.round(this.virtualDimension.width);
3500
+ let frameLength = this._audioDataHolder.frameLen;
3501
+ let framesPerPixel = Math.ceil(frameLength / vw);
3502
+ let audioBuffer = this._audioDataHolder.buffer;
3503
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
3504
+ let arrAbBuf;
3505
+ //let ais:ArrayAudioBufferInputStream|null=null;
3506
+ //let aisBuf:Float32Array[]|null=null;
3507
+ let psMinMax = null;
2668
3508
  if (this.worker) {
2669
3509
  this.worker.onmessage = (me) => {
2670
- //console.log("As rendertime: ", Date.now() - start);
2671
- this.drawRendered(me);
2672
- if (this.worker) {
2673
- this.worker.terminate();
3510
+ if (me.data.eod === true) {
3511
+ let psMinMaxTmp;
3512
+ if (psMinMax) {
3513
+ psMinMaxTmp = psMinMax;
3514
+ }
3515
+ else {
3516
+ psMinMaxTmp = me.data.psMinMax;
3517
+ }
3518
+ this.drawRendered(leftPos, w, h, chs, psMinMaxTmp);
3519
+ if (this.worker) {
3520
+ this.worker.terminate();
3521
+ }
3522
+ this.worker = null;
3523
+ }
3524
+ else if (this._audioDataHolder && arrAbBuf) {
3525
+ let rw = me.data.w;
3526
+ let rPointsLen = chs * rw;
3527
+ let pointsLen = chs * w;
3528
+ for (let ch = 0; ch < chs; ch++) {
3529
+ if (psMinMax) {
3530
+ let rBasePos = ch * rw;
3531
+ let basePos = ch * w;
3532
+ let rPosMin = rBasePos;
3533
+ let rPosMax = rPointsLen + rPosMin;
3534
+ let posMin = basePos + (renderPos - leftPos);
3535
+ let posMax = pointsLen + posMin;
3536
+ psMinMax[posMin] = me.data.psMinMax[rPosMin];
3537
+ //console.debug('Min: ('+pos+'): '+me.data.psMinMax[0]);
3538
+ psMinMax[posMax] = me.data.psMinMax[rPosMax];
3539
+ // console.debug('Max: ('+(pointsLen+pos)+'): '+me.data.psMinMax[1]);
3540
+ //console.debug("psMinMax["+posMin+"]="+me.data.psMinMax[rPosMin]+" (rPosMin="+rPosMin+"),psMinMax["+posMax+"]="+me.data.psMinMax[rPosMax]);
3541
+ }
3542
+ }
3543
+ let eod = false;
3544
+ renderPos++;
3545
+ let ad;
3546
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3547
+ if (renderPos < leftPos + w) {
3548
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3549
+ //console.debug("First read frame: "+arrAbBuf[0][0]);
3550
+ ad = new Float32Array(chs * framesPerPixel);
3551
+ for (let ch = 0; ch < chs; ch++) {
3552
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3553
+ }
3554
+ eod = (read <= 0);
3555
+ }
3556
+ else {
3557
+ ad = new Float32Array();
3558
+ eod = true;
3559
+ }
3560
+ let adBuf = ad.buffer;
3561
+ if (this.worker) {
3562
+ this.worker.postMessage({
3563
+ l: renderPos,
3564
+ w: me.data.w,
3565
+ h: h,
3566
+ vw: vw,
3567
+ chs: chs,
3568
+ frameLength: frameLength,
3569
+ audioData: ad,
3570
+ audioDataOffset: leftFramePos,
3571
+ eod: eod
3572
+ }, [adBuf]);
3573
+ }
2674
3574
  }
2675
- this.worker = null;
2676
3575
  };
2677
3576
  }
2678
- this.worker.postMessage({
2679
- l: Math.round(this.bounds.position.left),
2680
- t: Math.round(this.bounds.position.top),
2681
- w: w,
2682
- h: h,
2683
- vw: Math.round(this.virtualDimension.width),
2684
- chs: chs,
2685
- frameLength: frameLength,
2686
- audioData: ad
2687
- }, [ad.buffer]);
3577
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
3578
+ // Render whole clip at once
3579
+ arrAbBuf = null;
3580
+ psMinMax = null;
3581
+ let ad = new Float32Array(chs * frameLength);
3582
+ for (let ch = 0; ch < chs; ch++) {
3583
+ ad.set(audioBuffer.getChannelData(ch), ch * frameLength);
3584
+ }
3585
+ this.worker.postMessage({
3586
+ l: leftPos,
3587
+ w: w,
3588
+ vw: vw,
3589
+ chs: chs,
3590
+ frameLength: frameLength,
3591
+ audioData: ad,
3592
+ audioDataOffset: 0,
3593
+ eod: true
3594
+ }, [ad.buffer]);
3595
+ }
3596
+ else {
3597
+ // Render pixel by pixel
3598
+ if (w > 0) {
3599
+ if (framesPerPixel > 0) {
3600
+ let rw = 1;
3601
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
3602
+ arrAbBuf = new Array(chs);
3603
+ psMinMax = new Float32Array(chs * w * 2);
3604
+ for (let ch = 0; ch < chs; ch++) {
3605
+ arrAbBuf[ch] = new Float32Array(framesPerPixel);
3606
+ }
3607
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3608
+ let auOffset = leftFramePos; // should always be 0
3609
+ //let read=arrayAudioBuffer.frames(leftFramePos,framesPerPixel,arrAbBuf);
3610
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3611
+ let ad = new Float32Array(chs * framesPerPixel);
3612
+ for (let ch = 0; ch < chs; ch++) {
3613
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3614
+ }
3615
+ this.worker.postMessage({
3616
+ l: renderPos,
3617
+ w: rw,
3618
+ vw: vw,
3619
+ chs: chs,
3620
+ frameLength: frameLength,
3621
+ audioData: ad,
3622
+ audioDataOffset: auOffset,
3623
+ eod: (read <= 0)
3624
+ }, [ad.buffer]);
3625
+ }
3626
+ }
3627
+ }
2688
3628
  }
2689
3629
  else {
2690
3630
  let g = this.signalCanvas.getContext("2d");
@@ -2694,42 +3634,42 @@ class AudioSignal extends AudioCanvasLayerComponent {
2694
3634
  }
2695
3635
  }
2696
3636
  }
2697
- drawRendered(me) {
3637
+ drawRendered(left, w, h, chs, psMinMax) {
2698
3638
  this.drawBg();
2699
- this.signalCanvas.style.left = me.data.l.toString() + 'px';
2700
- this.signalCanvas.width = me.data.w;
2701
- this.signalCanvas.height = me.data.h;
3639
+ this.signalCanvas.style.left = left.toString() + 'px';
3640
+ this.signalCanvas.width = w;
3641
+ this.signalCanvas.height = h;
2702
3642
  let g = this.signalCanvas.getContext("2d");
2703
3643
  if (g) {
2704
- g.clearRect(0, 0, me.data.w, me.data.h);
3644
+ g.clearRect(0, 0, w, h);
2705
3645
  //g.fillStyle = "black";
2706
3646
  //g.fillRect(0, 0, me.data.w, me.data.h);
2707
- let pointsLen = me.data.w * me.data.chs;
3647
+ let pointsLen = w * chs;
2708
3648
  // one for min one for max
2709
3649
  let arrLen = pointsLen * 2;
2710
- if (this._audioData) {
3650
+ if (this._audioDataHolder) {
2711
3651
  let std = Date.now();
2712
- let chH = me.data.h / me.data.chs;
3652
+ let chH = h / chs;
2713
3653
  let y = 0;
2714
- for (let ch = 0; ch < me.data.chs; ch++) {
3654
+ for (let ch = 0; ch < chs; ch++) {
2715
3655
  let x = 0;
2716
- let psMinPos = ch * me.data.w;
3656
+ let psMinPos = ch * w;
2717
3657
  let psMaxPos = pointsLen + psMinPos;
2718
3658
  g.fillStyle = 'green';
2719
3659
  g.strokeStyle = 'green';
2720
3660
  // draw audio signal as single polygon
2721
3661
  g.beginPath();
2722
- g.moveTo(0, y + (chH / 2) + me.data.psMinMax[psMaxPos] * chH / 2);
2723
- for (let pii = 0; pii < me.data.w; pii++) {
2724
- let psMax = me.data.psMinMax[psMaxPos + pii];
3662
+ g.moveTo(0, y + (chH / 2) + psMinMax[psMaxPos] * chH / 2);
3663
+ for (let pii = 0; pii < w; pii++) {
3664
+ let psMax = psMinMax[psMaxPos + pii];
2725
3665
  let pv = psMax * chH / 2;
2726
3666
  let yd = y + (chH / 2) - pv;
2727
3667
  //console.log("LineTo: : "+pii+" "+yd)
2728
3668
  g.lineTo(pii, yd);
2729
3669
  }
2730
- let revPixelStart = me.data.w - 1;
3670
+ let revPixelStart = w - 1;
2731
3671
  for (let pii = revPixelStart; pii >= 0; pii--) {
2732
- let psMin = me.data.psMinMax[psMinPos + pii];
3672
+ let psMin = psMinMax[psMinPos + pii];
2733
3673
  let pv = psMin * chH / 2;
2734
3674
  let yd = y + (chH / 2) - pv;
2735
3675
  //console.log("LineTo: : "+pii+" "+yd)
@@ -2753,13 +3693,23 @@ class AudioSignal extends AudioCanvasLayerComponent {
2753
3693
  g.clearRect(0, 0, w, h);
2754
3694
  //g.fillStyle = "black";
2755
3695
  //g.fillRect(0, 0, w, h);
2756
- if (this._audioData) {
3696
+ if (this._audioDataHolder) {
2757
3697
  let std = Date.now();
2758
- let chs = this._audioData.numberOfChannels;
3698
+ let chs = this._audioDataHolder.numberOfChannels;
2759
3699
  let chH = h / chs;
2760
- let frameLength = this._audioData.getChannelData(0).length;
3700
+ let frameLength = this._audioDataHolder.frameLen;
2761
3701
  let framesPerPixel = frameLength / w;
2762
3702
  let y = 0;
3703
+ let ais = null;
3704
+ let audioBuffer = this._audioDataHolder.buffer;
3705
+ let aisBuffer = null;
3706
+ if (!audioBuffer) {
3707
+ ais = this._audioDataHolder.audioInputStream();
3708
+ aisBuffer = new Array(chs);
3709
+ for (let ch = 0; ch < chs; ch++) {
3710
+ aisBuffer[ch] = new Float32Array(framesPerPixel);
3711
+ }
3712
+ }
2763
3713
  for (let ch = 0; ch < chs; ch++) {
2764
3714
  let x = 0;
2765
3715
  let psMin = new Float32Array(w);
@@ -2768,20 +3718,33 @@ class AudioSignal extends AudioCanvasLayerComponent {
2768
3718
  for (let pii = 0; pii < w; pii++) {
2769
3719
  let pMin = 0;
2770
3720
  let pMax = 0;
2771
- // calculate pixel min/max amplitude
2772
- for (let ai = 0; ai < framesPerPixel; ai++) {
2773
- //let framePos=(pii*framesPerPixel)+ai;
2774
- let a = this._audioData.getChannelData(ch)[framePos++];
2775
- if (a < pMin) {
2776
- pMin = a;
3721
+ if (audioBuffer) {
3722
+ // calculate pixel min/max amplitude
3723
+ for (let ai = 0; ai < framesPerPixel; ai++) {
3724
+ //let framePos=(pii*framesPerPixel)+ai;
3725
+ let a = audioBuffer.getChannelData(ch)[framePos++];
3726
+ if (a < pMin) {
3727
+ pMin = a;
3728
+ }
3729
+ if (a > pMax) {
3730
+ pMax = a;
3731
+ }
2777
3732
  }
2778
- if (a > pMax) {
2779
- pMax = a;
3733
+ }
3734
+ else if (ais && aisBuffer) {
3735
+ let r = ais.read(aisBuffer);
3736
+ for (let ai = 0; ai < r; ai++) {
3737
+ let a = aisBuffer[ch][ai];
3738
+ if (a < pMin) {
3739
+ pMin = a;
3740
+ }
3741
+ if (a > pMax) {
3742
+ pMax = a;
3743
+ }
2780
3744
  }
2781
3745
  }
2782
3746
  psMin[pii] = pMin;
2783
3747
  psMax[pii] = pMax;
2784
- //console.log("Min: ", pMin, " max: ", pMax);
2785
3748
  }
2786
3749
  g.fillStyle = 'green';
2787
3750
  g.strokeStyle = 'green';
@@ -2806,12 +3769,11 @@ class AudioSignal extends AudioCanvasLayerComponent {
2806
3769
  g.stroke();
2807
3770
  y += chH;
2808
3771
  }
2809
- //this.drawPlayPosition();
2810
3772
  }
2811
3773
  }
2812
3774
  }
2813
3775
  setData(audioData) {
2814
- this._audioData = audioData;
3776
+ this._audioDataHolder = audioData;
2815
3777
  this.playFramePosition = 0;
2816
3778
  }
2817
3779
  }
@@ -3180,7 +4142,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3180
4142
  this._playFramePosition = null;
3181
4143
  this.dftSize = DEFAULT_DFT_SIZE;
3182
4144
  this.worker = null;
3183
- this._audioData = null;
4145
+ this._audioDataHolder = null;
3184
4146
  this.markers = new Array();
3185
4147
  this.dft = new DFTFloat32(this.dftSize);
3186
4148
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
@@ -3233,7 +4195,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3233
4195
  g.lineTo(xViewPortPixelpos, h);
3234
4196
  g.closePath();
3235
4197
  g.stroke();
3236
- if (this._audioData) {
4198
+ if (this._audioDataHolder) {
3237
4199
  let framePosRound = this.viewPortXPixelToFramePosition(xViewPortPixelpos);
3238
4200
  if (framePosRound != null) {
3239
4201
  g.font = '14px sans-serif';
@@ -3497,11 +4459,21 @@ class Sonagram extends AudioCanvasLayerComponent {
3497
4459
  }
3498
4460
  GaussianWindow.DEFAULT_SIGMA = 0.3;
3499
4461
  self.onmessage = function (msg) {
4462
+ //console.debug("Sonagram render thread");
3500
4463
  let l = msg.data.l;
3501
4464
  let w = msg.data.w;
3502
4465
  let h = msg.data.h;
3503
4466
  let vw = msg.data.vw;
3504
4467
  let chs = msg.data.chs;
4468
+ let audioDataOffset = 0;
4469
+ let adOffset = msg.data.audioDataOffset;
4470
+ if (adOffset) {
4471
+ audioDataOffset = adOffset;
4472
+ }
4473
+ let maxPsd = null;
4474
+ if (msg.data.maxPsd !== undefined) {
4475
+ maxPsd = msg.data.maxPsd;
4476
+ }
3505
4477
  let audioData = new Array(chs);
3506
4478
  for (let ch = 0; ch < chs; ch++) {
3507
4479
  audioData[ch] = new Float32Array(msg.data['audioData'][ch]);
@@ -3517,16 +4489,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3517
4489
  }
3518
4490
  let imgData = new Uint8ClampedArray(arrSize);
3519
4491
  //console.log("Render method:");
3520
- if (audioData && arrSize > 0) {
4492
+ //console.debug("Created imgData arrSize: "+arrSize+" ", w, "x", h);
4493
+ let calcMaxPsd = -Infinity;
4494
+ if (arrSize > 0) {
3521
4495
  let chH = Math.round(h / chs);
3522
4496
  let framesPerPixel = frameLength / vw;
3523
- //console.log("Render: ", w, "x", h);
4497
+ //console.debug("Render: ", w, "x", h);
3524
4498
  let b = new Float32Array(dftSize);
3525
4499
  let sona = new Array(chs);
3526
- let maxPsd = -Infinity;
3527
- let p = 0;
4500
+ //let p = 0;
3528
4501
  for (let ch = 0; ch < chs; ch++) {
3529
- p = ch * frameLength;
4502
+ //p = ch * frameLength;
3530
4503
  let chDataLen = audioData[ch].length;
3531
4504
  let x = 0;
3532
4505
  // initialize DFT array buffer
@@ -3543,8 +4516,13 @@ class Sonagram extends AudioCanvasLayerComponent {
3543
4516
  // initialize for negative sample positions and out of bounds positions
3544
4517
  let chDat = 0;
3545
4518
  // Set audio sample if available
3546
- if (samplePos >= 0 && samplePos < chDataLen) {
3547
- chDat = audioData[ch][samplePos];
4519
+ let adp = samplePos - audioDataOffset;
4520
+ if (adp >= 0 && adp < chDataLen) {
4521
+ chDat = audioData[ch][adp];
4522
+ //console.debug("Audio data: "+chDat);
4523
+ }
4524
+ else {
4525
+ //console.debug("Sample buf pos oob: adp: "+adp+", chDataLen: "+chDataLen+", samplePos: "+samplePos+", i: "+i);
3548
4526
  }
3549
4527
  // apply Window
3550
4528
  b[i] = chDat * wf.getScale(i);
@@ -3554,63 +4532,78 @@ class Sonagram extends AudioCanvasLayerComponent {
3554
4532
  // Get maximum value of spectral energy
3555
4533
  for (let s = 0; s < dftBands; s++) {
3556
4534
  let psd = (2 * Math.pow(spectr[s], 2)) / dftBands;
3557
- if (psd > maxPsd) {
3558
- maxPsd = psd;
4535
+ if (psd > calcMaxPsd) {
4536
+ calcMaxPsd = psd;
3559
4537
  }
3560
4538
  }
3561
4539
  // Set render model data for this pixel
3562
4540
  sona[ch][pii] = spectr;
3563
4541
  }
3564
4542
  }
3565
- //maxPsd = (2 * Math.pow(max, 2)) / dftBands;
3566
- for (let ch = 0; ch < chs; ch++) {
3567
- for (let pii = 0; pii < w; pii++) {
3568
- let allBlack = true;
3569
- for (let y = 0; y < chH; y++) {
3570
- let freqIdx = Math.round(y * dftBands / chH);
3571
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3572
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3573
- let val = sona[ch][pii][freqIdx];
3574
- let psd = (2 * Math.pow(val, 2)) / dftBands;
3575
- // Calculate logarithmic value
3576
- //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
3577
- let linearLevel = psd / maxPsd;
3578
- let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
3579
- // Fixed dynamic Range value of 70dB for now
3580
- let dynRangeInDb = 70;
3581
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3582
- // are the following checks necessary for clamped array ?
3583
- if (scaledVal > 1.0)
3584
- scaledVal = 1;
3585
- if (scaledVal < 0.0) {
3586
- scaledVal = 0;
3587
- }
3588
- let rgbVal = Math.round(255 * scaledVal);
3589
- if (rgbVal < 0) {
3590
- // System.out.println("Neg RGB val: "+rgbVal);
3591
- rgbVal = 0;
3592
- }
3593
- if (rgbVal > 255) {
3594
- rgbVal = 255;
3595
- }
3596
- rgbVal = 255 - rgbVal;
3597
- if (rgbVal > 0) {
3598
- allBlack = false;
4543
+ if (!msg.data.norender) {
4544
+ if (!maxPsd) {
4545
+ maxPsd = calcMaxPsd;
4546
+ }
4547
+ for (let ch = 0; ch < chs; ch++) {
4548
+ for (let pii = 0; pii < w; pii++) {
4549
+ let allBlack = true;
4550
+ for (let y = 0; y < chH; y++) {
4551
+ let freqIdx = Math.round(y * dftBands / chH);
4552
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4553
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4554
+ let val = sona[ch][pii][freqIdx];
4555
+ let psd = (2 * Math.pow(val, 2)) / dftBands;
4556
+ // Calculate logarithmic value
4557
+ //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
4558
+ let linearLevel = psd / maxPsd;
4559
+ let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
4560
+ // Fixed dynamic Range value of 70dB for now
4561
+ let dynRangeInDb = 70;
4562
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4563
+ // are the following checks necessary for clamped array ?
4564
+ if (scaledVal > 1.0)
4565
+ scaledVal = 1;
4566
+ if (scaledVal < 0.0) {
4567
+ scaledVal = 0;
4568
+ }
4569
+ let rgbVal = Math.round(255 * scaledVal);
4570
+ if (rgbVal < 0) {
4571
+ // System.out.println("Neg RGB val: "+rgbVal);
4572
+ rgbVal = 0;
4573
+ }
4574
+ if (rgbVal > 255) {
4575
+ rgbVal = 255;
4576
+ }
4577
+ rgbVal = 255 - rgbVal;
4578
+ if (rgbVal > 0) {
4579
+ allBlack = false;
4580
+ }
4581
+ let py = chH - y;
4582
+ let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
4583
+ imgData[dataPos + 0] = rgbVal; //R
4584
+ imgData[dataPos + 1] = rgbVal; //G
4585
+ imgData[dataPos + 2] = rgbVal; //B
4586
+ imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4587
+ //console.debug("Rendered: py: "+py+", rgbval: "+rgbVal);
4588
+ // example 1x1, 2chs
4589
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4590
+ // ch0x1y0R,ch0x1y0G,ch0x1y0B,ch0x1y0A,
4591
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4592
+ // ch0x1y1R,ch0x1y1G,ch0x1y1B,ch0x1y1A,
4593
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4594
+ // ch1x1y0R,ch1x1y0G,ch1x1y0B,ch1x1y0A,
4595
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4596
+ // ch1x1y1R,ch1x1y1G,ch1x1y1B,ch1x1y1A
3599
4597
  }
3600
- let py = chH - y;
3601
- let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
3602
- imgData[dataPos + 0] = rgbVal; //R
3603
- imgData[dataPos + 1] = rgbVal; //G
3604
- imgData[dataPos + 2] = rgbVal; //B
3605
- imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4598
+ // if (allBlack) {
4599
+ // console.log("Black: ", pii, " ", ch);
4600
+ // }
3606
4601
  }
3607
- // if (allBlack) {
3608
- // console.log("Black: ", pii, " ", ch);
3609
- // }
3610
4602
  }
3611
4603
  }
3612
4604
  }
3613
- postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw }, [imgData.buffer]);
4605
+ //console.debug("Render thread post message imgData: "+imgData.length)
4606
+ postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw, maxPsd: calcMaxPsd, terminate: msg.data.terminate }, [imgData.buffer]);
3614
4607
  };
3615
4608
  }
3616
4609
  startDraw(clear = true) {
@@ -3640,42 +4633,190 @@ class Sonagram extends AudioCanvasLayerComponent {
3640
4633
  if (this.bounds) {
3641
4634
  let w = Math.round(this.bounds.dimension.width);
3642
4635
  let h = Math.round(this.bounds.dimension.height);
3643
- if (this._audioData && w > 0 && h > 0) {
4636
+ if (this._audioDataHolder && w > 0 && h > 0) {
3644
4637
  this.worker = new Worker(this.workerURL);
3645
4638
  //this.wo = new Worker('./worker/sonagram.worker', { type: `module` });
3646
- let chs = this._audioData.numberOfChannels;
3647
- let frameLength = this._audioData.getChannelData(0).length;
3648
- let ada = new Array(chs);
3649
- for (let ch = 0; ch < chs; ch++) {
3650
- // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
3651
- ada[ch] = this._audioData.getChannelData(ch).buffer.slice(0);
3652
- }
3653
- let start = Date.now();
4639
+ let chs = this._audioDataHolder.numberOfChannels;
4640
+ let vw = Math.round(this.virtualDimension.width);
4641
+ let frameLength = this._audioDataHolder.frameLen;
4642
+ let framesPerPixel = Math.ceil(frameLength / vw);
4643
+ let leftPos = Math.round(this.bounds.position.left);
4644
+ let renderPos = leftPos;
4645
+ let audioBuffer = this._audioDataHolder.buffer;
4646
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
4647
+ let arrAbBuf;
4648
+ let imgData;
4649
+ let maxPsd = -Infinity;
4650
+ let norender = true;
3654
4651
  if (this.worker) {
3655
4652
  this.worker.onmessage = (me) => {
3656
- this.drawRendered(me);
3657
- if (this.worker) {
3658
- this.worker.terminate();
4653
+ if (true === me.data.terminate) {
4654
+ let drawImgData;
4655
+ if (imgData) {
4656
+ drawImgData = imgData;
4657
+ }
4658
+ else {
4659
+ drawImgData = me.data.imgData;
4660
+ }
4661
+ this.drawRendered(w, h, drawImgData);
4662
+ if (this.worker) {
4663
+ this.worker.terminate();
4664
+ }
4665
+ this.worker = null;
4666
+ }
4667
+ else {
4668
+ // set rendered vertical values of one pixel of timescale
4669
+ //let dataPos = renderPos * h * 4;
4670
+ if (norender) {
4671
+ if (me.data.maxPsd > maxPsd) {
4672
+ maxPsd = me.data.maxPsd;
4673
+ //console.debug("new maxPsd: "+maxPsd);
4674
+ }
4675
+ }
4676
+ else {
4677
+ let chH = Math.round(h / chs);
4678
+ let idp = me.data.l - leftPos;
4679
+ for (let ch = 0; ch < chs; ch++) {
4680
+ for (let y = 0; y < chH; y++) {
4681
+ let py = chH - y;
4682
+ let dataPos = ((((ch * chH) + py) * w) + idp) * 4;
4683
+ let mePos = ((ch * chH) + py) * 4;
4684
+ //let lastPos = dataPos + me.data.imgData.length;
4685
+ //if (lastPos < imgData.length) {
4686
+ // set one pixel
4687
+ imgData[dataPos] = me.data.imgData[mePos];
4688
+ imgData[dataPos + 1] = me.data.imgData[mePos + 1];
4689
+ imgData[dataPos + 2] = me.data.imgData[mePos + 2];
4690
+ imgData[dataPos + 3] = me.data.imgData[mePos + 3];
4691
+ //} else {
4692
+ //console.error("Out of range: " + dataPos + "+" + me.data.imgData.length + ">=" + imgData.length);
4693
+ // }
4694
+ }
4695
+ }
4696
+ }
4697
+ if (this._audioDataHolder && arrAbBuf && this.worker) {
4698
+ // proceed with next pixel
4699
+ renderPos++;
4700
+ //console.debug("Render pos: "+renderPos);
4701
+ let terminate = false;
4702
+ let windowEnd = renderPos >= leftPos + w;
4703
+ if (windowEnd) {
4704
+ if (norender) {
4705
+ // phase two: rendering
4706
+ norender = false;
4707
+ // start from beginning
4708
+ renderPos = leftPos;
4709
+ //console.debug("now rendering: maxPsd: "+maxPsd);
4710
+ }
4711
+ else {
4712
+ // terminate render phase
4713
+ terminate = true;
4714
+ }
4715
+ }
4716
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4717
+ if (leftFramePos < 0) {
4718
+ leftFramePos = 0;
4719
+ }
4720
+ let ada = new Array(chs);
4721
+ //console.debug("Render pos: "+renderPos+" leftFramePos: "+leftFramePos);
4722
+ if (!terminate) {
4723
+ if (this._audioDataHolder) {
4724
+ let read = this._audioDataHolder.frames(leftFramePos, this.dftSize, arrAbBuf);
4725
+ for (let ch = 0; ch < chs; ch++) {
4726
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4727
+ ada[ch] = arrAbBuf[ch].buffer.slice(0);
4728
+ }
4729
+ }
4730
+ }
4731
+ else {
4732
+ for (let ch = 0; ch < chs; ch++) {
4733
+ ada[ch] = new ArrayBuffer(0);
4734
+ }
4735
+ }
4736
+ this.worker.postMessage({
4737
+ audioData: ada,
4738
+ audioDataOffset: leftFramePos,
4739
+ l: renderPos,
4740
+ w: me.data.w,
4741
+ h: h,
4742
+ vw: vw,
4743
+ chs: chs,
4744
+ frameLength: frameLength,
4745
+ dftSize: this.dftSize,
4746
+ maxPsd: maxPsd,
4747
+ norender: norender,
4748
+ terminate: terminate
4749
+ }, ada);
4750
+ }
3659
4751
  }
3660
- this.worker = null;
3661
4752
  };
3662
4753
  }
3663
- if (this.markerCanvas) {
3664
- let g = this.markerCanvas.getContext("2d");
3665
- if (g) {
3666
- g.fillText("Rendering...", 10, 20);
4754
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
4755
+ let ada = new Array(chs);
4756
+ for (let ch = 0; ch < chs; ch++) {
4757
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4758
+ ada[ch] = audioBuffer.getChannelData(ch).buffer.slice(0);
4759
+ }
4760
+ let start = Date.now();
4761
+ if (this.markerCanvas) {
4762
+ let g = this.markerCanvas.getContext("2d");
4763
+ if (g) {
4764
+ g.fillText("Rendering...", 10, 20);
4765
+ }
4766
+ }
4767
+ this.worker.postMessage({
4768
+ audioData: ada,
4769
+ l: leftPos,
4770
+ w: w,
4771
+ h: h,
4772
+ vw: Math.round(this.virtualDimension.width),
4773
+ chs: chs,
4774
+ frameLength: frameLength,
4775
+ dftSize: this.dftSize,
4776
+ terminate: true
4777
+ }, ada);
4778
+ }
4779
+ else {
4780
+ if (w > 0) {
4781
+ if (framesPerPixel > 0) {
4782
+ let arrSize = w * h * 4;
4783
+ if (arrSize < 0) {
4784
+ arrSize = 0;
4785
+ }
4786
+ imgData = new Uint8ClampedArray(arrSize);
4787
+ let rw = 1;
4788
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
4789
+ arrAbBuf = new Array(chs);
4790
+ for (let ch = 0; ch < chs; ch++) {
4791
+ arrAbBuf[ch] = new Float32Array(this.dftSize);
4792
+ }
4793
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4794
+ let framesToRead = this.dftSize;
4795
+ if (leftFramePos < 0) {
4796
+ //framesToRead=this.dftSize+leftFramePos;
4797
+ leftFramePos = 0;
4798
+ }
4799
+ let read = this._audioDataHolder.frames(leftFramePos, framesToRead, arrAbBuf);
4800
+ let ad = new Float32Array(chs * framesToRead);
4801
+ for (let ch = 0; ch < chs; ch++) {
4802
+ ad.set(arrAbBuf[ch], ch * framesToRead);
4803
+ }
4804
+ this.worker.postMessage({
4805
+ l: renderPos,
4806
+ w: rw,
4807
+ h: h,
4808
+ vw: vw,
4809
+ chs: chs,
4810
+ frameLength: frameLength,
4811
+ audioData: ad,
4812
+ audioDataOffset: leftFramePos,
4813
+ dftSize: this.dftSize,
4814
+ norender: norender,
4815
+ terminate: false
4816
+ }, [ad.buffer]);
4817
+ }
3667
4818
  }
3668
4819
  }
3669
- this.worker.postMessage({
3670
- audioData: ada,
3671
- l: Math.round(this.bounds.position.left),
3672
- w: w,
3673
- h: h,
3674
- vw: Math.round(this.virtualDimension.width),
3675
- chs: chs,
3676
- frameLength: frameLength,
3677
- dftSize: this.dftSize
3678
- }, ada);
3679
4820
  }
3680
4821
  else {
3681
4822
  let g = this.sonagramCanvas.getContext("2d");
@@ -3685,17 +4826,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3685
4826
  }
3686
4827
  }
3687
4828
  }
3688
- drawRendered(me) {
4829
+ drawRendered(w, h, imgData) {
3689
4830
  if (this.sonagramCanvas) {
3690
- this.sonagramCanvas.width = me.data.w;
3691
- this.sonagramCanvas.height = me.data.h;
4831
+ this.sonagramCanvas.width = w;
4832
+ this.sonagramCanvas.height = h;
3692
4833
  let g = this.sonagramCanvas.getContext("2d");
3693
4834
  if (g) {
3694
- let imgDataArr = me.data.imgData;
3695
- if (me.data.w > 0 && me.data.h > 0) {
3696
- let imgData = g.createImageData(me.data.w, me.data.h);
3697
- imgData.data.set(imgDataArr);
3698
- g.putImageData(imgData, 0, 0);
4835
+ let imgDataArr = imgData;
4836
+ if (w > 0 && h > 0) {
4837
+ let gImgData = g.createImageData(w, h);
4838
+ gImgData.data.set(imgDataArr);
4839
+ g.putImageData(gImgData, 0, 0);
3699
4840
  }
3700
4841
  }
3701
4842
  }
@@ -3711,91 +4852,95 @@ class Sonagram extends AudioCanvasLayerComponent {
3711
4852
  g.clearRect(0, 0, w, h);
3712
4853
  g.fillStyle = "white";
3713
4854
  g.fillRect(0, 0, w, h);
3714
- if (this._audioData) {
4855
+ if (this._audioDataHolder) {
3715
4856
  let spectSize = Math.floor(this.dftSize / 2);
3716
- let chs = this._audioData.numberOfChannels;
4857
+ let chs = this._audioDataHolder.numberOfChannels;
3717
4858
  let chH = h / chs;
3718
- let frameLength = this._audioData.getChannelData(0).length;
4859
+ let frameLength = this._audioDataHolder.frameLen;
3719
4860
  let framesPerPixel = frameLength / w;
3720
4861
  let y = 0;
3721
- // TODO
3722
- let b = new Float32Array(this.dftSize);
3723
- let sona = new Array(chs);
3724
- let max = 0;
3725
- let maxPsd = -Infinity;
3726
- for (let ch = 0; ch < chs; ch++) {
3727
- let x = 0;
3728
- sona[ch] = new Array(w);
3729
- let chData = this._audioData.getChannelData(ch);
3730
- // TODO center buffer
3731
- let framePos = 0;
3732
- for (let pii = 0; pii < w; pii++) {
3733
- framePos = Math.round(pii * framesPerPixel);
3734
- // calculate DFT at pixel position
3735
- for (let i = 0; i < this.dftSize; i++) {
3736
- let chDat = chData[framePos + i];
3737
- b[i] = chDat;
3738
- }
3739
- let spectr = this.dft.processRealMagnitude(b);
3740
- sona[ch][pii] = spectr;
3741
- // @ts-ignore
3742
- let pMax = Math.max.apply(null, spectr);
3743
- if (pMax > max) {
3744
- max = pMax;
3745
- }
3746
- for (let s = 0; s < spectSize; s++) {
3747
- let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
3748
- if (psd > maxPsd) {
3749
- maxPsd = psd;
4862
+ let audioBuffer = this._audioDataHolder.buffer;
4863
+ let arrayAudioBuffer = this._audioDataHolder.arrayBuffer;
4864
+ if (audioBuffer) {
4865
+ let b = new Float32Array(this.dftSize);
4866
+ let sona = new Array(chs);
4867
+ let max = 0;
4868
+ let maxPsd = -Infinity;
4869
+ for (let ch = 0; ch < chs; ch++) {
4870
+ let x = 0;
4871
+ sona[ch] = new Array(w);
4872
+ let chData = audioBuffer.getChannelData(ch);
4873
+ // TODO center buffer
4874
+ let framePos = 0;
4875
+ for (let pii = 0; pii < w; pii++) {
4876
+ framePos = Math.round(pii * framesPerPixel);
4877
+ // calculate DFT at pixel position
4878
+ for (let i = 0; i < this.dftSize; i++) {
4879
+ let chDat = chData[framePos + i];
4880
+ b[i] = chDat;
3750
4881
  }
3751
- }
3752
- }
3753
- }
3754
- //console.log("max: ", max);
3755
- maxPsd = (2 * Math.pow(max, 2)) / spectSize;
3756
- for (let ch = 0; ch < chs; ch++) {
3757
- let framePos = 0;
3758
- for (let pii = 0; pii < w; pii++) {
3759
- framePos = pii * framesPerPixel;
3760
- for (let y = 0; y < h; y++) {
3761
- let freqIdx = Math.round(y * spectSize / h);
3762
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3763
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3764
- let val = sona[ch][pii][freqIdx];
3765
- let psd = (2 * Math.pow(val, 2)) / spectSize;
3766
- // Calculate logarithmic
3767
- let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
3768
- let dynRangeInDb = 70;
3769
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3770
- if (scaledVal > 1)
3771
- scaledVal = 1;
3772
- if (scaledVal < 0) {
3773
- scaledVal = 0;
4882
+ let spectr = this.dft.processRealMagnitude(b);
4883
+ sona[ch][pii] = spectr;
4884
+ // @ts-ignore
4885
+ let pMax = Math.max.apply(null, spectr);
4886
+ if (pMax > max) {
4887
+ max = pMax;
3774
4888
  }
3775
- let rgbVal = (255 * scaledVal);
3776
- if (rgbVal < 0) {
3777
- // System.out.println("Neg RGB val: "+rgbVal);
3778
- rgbVal = 0;
4889
+ for (let s = 0; s < spectSize; s++) {
4890
+ let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
4891
+ if (psd > maxPsd) {
4892
+ maxPsd = psd;
4893
+ }
3779
4894
  }
3780
- if (rgbVal > 255) {
3781
- rgbVal = 255;
4895
+ }
4896
+ }
4897
+ //console.log("max: ", max);
4898
+ maxPsd = (2 * Math.pow(max, 2)) / spectSize;
4899
+ for (let ch = 0; ch < chs; ch++) {
4900
+ let framePos = 0;
4901
+ for (let pii = 0; pii < w; pii++) {
4902
+ framePos = pii * framesPerPixel;
4903
+ for (let y = 0; y < h; y++) {
4904
+ let freqIdx = Math.round(y * spectSize / h);
4905
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4906
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4907
+ let val = sona[ch][pii][freqIdx];
4908
+ let psd = (2 * Math.pow(val, 2)) / spectSize;
4909
+ // Calculate logarithmic
4910
+ let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
4911
+ let dynRangeInDb = 70;
4912
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4913
+ if (scaledVal > 1)
4914
+ scaledVal = 1;
4915
+ if (scaledVal < 0) {
4916
+ scaledVal = 0;
4917
+ }
4918
+ let rgbVal = (255 * scaledVal);
4919
+ if (rgbVal < 0) {
4920
+ // System.out.println("Neg RGB val: "+rgbVal);
4921
+ rgbVal = 0;
4922
+ }
4923
+ if (rgbVal > 255) {
4924
+ rgbVal = 255;
4925
+ }
4926
+ rgbVal = 255 - rgbVal;
4927
+ let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
4928
+ g.fillStyle = colorStr;
4929
+ g.fillRect(pii, chH - y, 1, 1);
3782
4930
  }
3783
- rgbVal = 255 - rgbVal;
3784
- let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
3785
- g.fillStyle = colorStr;
3786
- g.fillRect(pii, chH - y, 1, 1);
3787
4931
  }
3788
4932
  }
4933
+ this.drawPlayPosition();
4934
+ }
4935
+ else if (arrayAudioBuffer) {
4936
+ throw Error("Redraw with array audio buffer not supported.");
3789
4937
  }
3790
- this.drawPlayPosition();
3791
4938
  }
3792
4939
  }
3793
4940
  }
3794
4941
  setData(audioData) {
3795
- this._audioData = audioData;
4942
+ this._audioDataHolder = audioData;
3796
4943
  this.playFramePosition = 0;
3797
- //this.redraw();
3798
- //this.startRender();
3799
4944
  }
3800
4945
  }
3801
4946
  Sonagram.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: Sonagram, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4020,13 +5165,13 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4020
5165
  }
4021
5166
  currentXZoom() {
4022
5167
  let xz = this._xZoom;
4023
- if (xz == null && this._audioData) {
5168
+ if (xz == null && this._audioDataHolder) {
4024
5169
  let ow = this.ce.offsetWidth;
4025
5170
  if (ow < 1) {
4026
5171
  // at least one pixel width to avoid x-zoom zero values
4027
5172
  ow = 1;
4028
5173
  }
4029
- xz = ow / this._audioData.duration;
5174
+ xz = ow / this._audioDataHolder.duration;
4030
5175
  }
4031
5176
  return xz;
4032
5177
  }
@@ -4107,7 +5252,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4107
5252
  layout(clear = true) {
4108
5253
  if (this.ce && this.dc) {
4109
5254
  const clientW = this.ce.clientWidth;
4110
- if (this._audioData) {
5255
+ if (this._audioDataHolder) {
4111
5256
  if (this._fixFitToPanel) {
4112
5257
  // Set the virtual canvas width to the visible width
4113
5258
  if (this.ce.style.width != '100%') {
@@ -4118,7 +5263,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4118
5263
  else {
4119
5264
  if (this._xZoom) {
4120
5265
  // Set the virtual canvas width according to the value of the user selected xZoom value
4121
- const newClW = Math.round(this._xZoom * this._audioData.duration);
5266
+ const newClW = Math.round(this._xZoom * this._audioDataHolder?.duration);
4122
5267
  this.ce.style.width = newClW + 'px';
4123
5268
  }
4124
5269
  else {
@@ -4130,22 +5275,22 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4130
5275
  this._layout(clear, true);
4131
5276
  }
4132
5277
  }
4133
- set audioData(audioData) {
5278
+ set audioData(audioDataHolder) {
4134
5279
  this._audioClip = null;
4135
- this._audioData = audioData;
4136
- this.as.setData(audioData);
4137
- this.so.setData(audioData);
5280
+ this._audioDataHolder = audioDataHolder;
5281
+ this.as.setData(audioDataHolder);
5282
+ this.so.setData(audioDataHolder);
4138
5283
  this.layout();
4139
5284
  }
4140
5285
  get audioData() {
4141
- return this._audioData;
5286
+ return this._audioDataHolder;
4142
5287
  }
4143
5288
  set audioClip(audioClip) {
4144
5289
  this._audioClip = audioClip;
4145
5290
  let audioData = null;
4146
5291
  let sel = null;
4147
5292
  if (audioClip) {
4148
- audioData = audioClip.buffer;
5293
+ audioData = audioClip.audioDataHolder;
4149
5294
  if (this._audioClip) {
4150
5295
  this._audioClip.addSelectionObserver((clip) => {
4151
5296
  this.selection = clip.selection;
@@ -4153,9 +5298,9 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4153
5298
  }
4154
5299
  sel = audioClip.selection;
4155
5300
  }
4156
- this._audioData = audioData;
4157
- this.as.setData(this._audioData);
4158
- this.so.setData(this._audioData);
5301
+ this._audioDataHolder = audioData;
5302
+ this.as.setData(this._audioDataHolder);
5303
+ this.so.setData(this._audioDataHolder);
4159
5304
  this.selecting = null;
4160
5305
  this.selection = sel;
4161
5306
  this.layout();
@@ -4347,7 +5492,7 @@ class AudioDisplayScrollPane {
4347
5492
  let audioData = null;
4348
5493
  let sel = null;
4349
5494
  if (audioClip) {
4350
- audioData = audioClip.buffer;
5495
+ audioData = audioClip.audioDataHolder;
4351
5496
  sel = audioClip.selection;
4352
5497
  audioClip.addSelectionObserver((clip) => {
4353
5498
  this.zoomSelectedAction.disabled = (clip.selection == null);
@@ -4618,17 +5763,17 @@ class AudioDisplay {
4618
5763
  console.log("Play started");
4619
5764
  this.status = 'Playing...';
4620
5765
  }
4621
- set audioData(audioBuffer) {
4622
- this.audioDisplayScrollPane.audioData = audioBuffer;
5766
+ set audioData(audioData) {
5767
+ this.audioDisplayScrollPane.audioData = audioData;
4623
5768
  if (this.playStartAction) {
4624
- this.playStartAction.disabled = (audioBuffer == null);
5769
+ this.playStartAction.disabled = (audioData == null);
4625
5770
  }
4626
5771
  }
4627
5772
  set audioClip(audioClip) {
4628
5773
  let audioData = null;
4629
5774
  let sel = null;
4630
5775
  if (audioClip) {
4631
- audioData = audioClip.buffer;
5776
+ audioData = audioClip.audioDataHolder.buffer;
4632
5777
  sel = audioClip.selection;
4633
5778
  }
4634
5779
  this._audioClip = audioClip;
@@ -4741,7 +5886,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
4741
5886
 
4742
5887
  class Progress {
4743
5888
  constructor() {
4744
- this.items = null;
5889
+ this.items = undefined;
4745
5890
  this.selectedItemIdx = 0;
4746
5891
  this.enableDownload = false;
4747
5892
  this.onRowSelect = new EventEmitter();
@@ -5481,7 +6626,7 @@ class Prompting {
5481
6626
  constructor() {
5482
6627
  this.promptItem = null;
5483
6628
  this.showPrompt = false;
5484
- this.items = null;
6629
+ this.items = undefined;
5485
6630
  this.enableDownload = false;
5486
6631
  this.audioSignalCollapsed = true;
5487
6632
  this.displayAudioClip = null;
@@ -5792,6 +6937,7 @@ class LevelBar {
5792
6937
  this._streamingMode = false;
5793
6938
  this._staticLevelInfos = null;
5794
6939
  this._playFramePosition = null;
6940
+ this._stateLoading = false;
5795
6941
  this.warnDBLevel = DEFAULT_WARN_DB_LEVEL$1;
5796
6942
  this.dbValues = new Array();
5797
6943
  }
@@ -5825,17 +6971,22 @@ class LevelBar {
5825
6971
  }
5826
6972
  }
5827
6973
  set displayLevelInfos(levelInfos) {
5828
- this._staticLevelInfos = levelInfos;
5829
6974
  if (levelInfos) {
6975
+ this._staticLevelInfos = levelInfos;
5830
6976
  this.dbValues = levelInfos.bufferLevelInfos.map((li) => {
5831
6977
  return li.powerLevelsDB();
5832
6978
  });
5833
6979
  }
5834
6980
  else {
6981
+ this._staticLevelInfos = null;
5835
6982
  this.dbValues = new Array();
5836
6983
  }
5837
6984
  this.layoutStatic();
5838
6985
  }
6986
+ set stateLoading(loading) {
6987
+ this._stateLoading = loading;
6988
+ this.layoutStatic();
6989
+ }
5839
6990
  set channelCount(channelCount) {
5840
6991
  this.reset();
5841
6992
  }
@@ -6016,6 +7167,12 @@ class LevelBar {
6016
7167
  }
6017
7168
  }
6018
7169
  }
7170
+ else if (this._stateLoading) {
7171
+ g.strokeStyle = 'white';
7172
+ g.fillStyle = 'white';
7173
+ g.font = '20px sans-serif';
7174
+ g.fillText("Loading...", 10, 25);
7175
+ }
6019
7176
  }
6020
7177
  }
6021
7178
  }
@@ -6058,7 +7215,7 @@ class LevelBar {
6058
7215
  }
6059
7216
  }
6060
7217
  LevelBar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: LevelBar, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
6061
- LevelBar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: LevelBar, selector: "audio-levelbar", inputs: { streamingMode: "streamingMode", displayLevelInfos: "displayLevelInfos" }, host: { listeners: { "scroll": "onScroll($event)", "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "virtualCanvasRef", first: true, predicate: ["virtualCanvas"], descendants: true, static: true }, { propertyName: "liveLevelCanvasRef", first: true, predicate: ["levelbar"], descendants: true, static: true }, { propertyName: "markerCanvasRef", first: true, predicate: ["markerCanvas"], descendants: true, static: true }], ngImport: i0, template: `
7218
+ LevelBar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: LevelBar, selector: "audio-levelbar", inputs: { streamingMode: "streamingMode", displayLevelInfos: "displayLevelInfos", stateLoading: "stateLoading" }, host: { listeners: { "scroll": "onScroll($event)", "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "virtualCanvasRef", first: true, predicate: ["virtualCanvas"], descendants: true, static: true }, { propertyName: "liveLevelCanvasRef", first: true, predicate: ["levelbar"], descendants: true, static: true }, { propertyName: "markerCanvasRef", first: true, predicate: ["markerCanvas"], descendants: true, static: true }], ngImport: i0, template: `
6062
7219
  <div #virtualCanvas>
6063
7220
  <canvas #levelbar></canvas>
6064
7221
  <canvas #markerCanvas></canvas>
@@ -6115,6 +7272,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
6115
7272
  type: Input
6116
7273
  }], displayLevelInfos: [{
6117
7274
  type: Input
7275
+ }], stateLoading: [{
7276
+ type: Input
6118
7277
  }], onResize: [{
6119
7278
  type: HostListener,
6120
7279
  args: ['window:resize', ['$event']]
@@ -6705,47 +7864,66 @@ class PeakLevelInterceptor {
6705
7864
  }
6706
7865
  }
6707
7866
  class LevelMeasure {
7867
+ //private bufferLevelInfos=new Array<LevelInfo>();
7868
+ //private peakLevelInfo!:LevelInfo;
6708
7869
  constructor() {
6709
7870
  this.worker = null;
6710
7871
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
6711
7872
  }
6712
- calcBufferLevelInfos(audioBuffer, bufferTimeLength) {
7873
+ calcBufferLevelInfos(audioDataHolder, bufferTimeLength) {
6713
7874
  return new Promise((resolve) => {
6714
- let chs = audioBuffer.numberOfChannels;
6715
- let bufferFrameLength = Math.round(audioBuffer.sampleRate * bufferTimeLength);
6716
- let buffers = new Array(chs);
7875
+ let chs = audioDataHolder.numberOfChannels;
7876
+ let bufferFrameLength = Math.round(audioDataHolder.sampleRate * bufferTimeLength);
7877
+ let ais = audioDataHolder.audioInputStream();
7878
+ let audioBuffers = new Array(chs);
7879
+ let trBuffers = new Array(chs);
6717
7880
  for (let ch = 0; ch < chs; ch++) {
6718
- let adCh = audioBuffer.getChannelData(ch);
6719
- let adChCopy = new Float32Array(adCh.length);
6720
- adChCopy.set(adCh);
6721
- buffers[ch] = adChCopy.buffer;
7881
+ audioBuffers[ch] = new Float32Array(bufferFrameLength);
6722
7882
  }
7883
+ let bufferLevelInfos = new Array();
7884
+ let peakLevelInfo = new LevelInfo(chs);
6723
7885
  this.worker = new Worker(this.workerURL);
6724
7886
  this.worker.onmessage = (me) => {
6725
- let linLevelArrs = new Array(chs);
6726
- for (let ch = 0; ch < chs; ch++) {
6727
- linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
6728
- }
6729
- let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
6730
- let framePos = 0;
6731
- let bufferLevelInfos = new Array();
6732
- let peakLevelInfo = new LevelInfo(chs);
6733
- for (let bi = 0; bi < bufferCount; bi++) {
6734
- let minLevels = new Array(chs);
6735
- let maxLevels = new Array(chs);
7887
+ if (me.data.linLevelBuffers) {
7888
+ let linLevelArrs = new Array(chs);
7889
+ for (let ch = 0; ch < chs; ch++) {
7890
+ linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
7891
+ }
7892
+ let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
7893
+ let framePos = 0;
7894
+ for (let bi = 0; bi < bufferCount; bi++) {
7895
+ let minLevels = new Array(chs);
7896
+ let maxLevels = new Array(chs);
7897
+ for (let ch = 0; ch < chs; ch++) {
7898
+ let linLvlArrPos = bi * 2;
7899
+ minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
7900
+ maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7901
+ }
7902
+ let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
7903
+ bufferLevelInfos.push(bli);
7904
+ peakLevelInfo.merge(bli);
7905
+ }
7906
+ }
7907
+ if (me.data.eod === true) {
7908
+ // end of data, terminate worker and return result
7909
+ this.terminateWorker();
7910
+ resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
7911
+ }
7912
+ else {
7913
+ let read = ais.read(audioBuffers);
6736
7914
  for (let ch = 0; ch < chs; ch++) {
6737
- let linLvlArrPos = bi * 2;
6738
- minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
6739
- maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7915
+ let copy = new Float32Array(audioBuffers[ch]);
7916
+ trBuffers[ch] = copy.buffer;
6740
7917
  }
6741
- let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
6742
- bufferLevelInfos.push(bli);
6743
- peakLevelInfo.merge(bli);
7918
+ this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6744
7919
  }
6745
- this.terminateWorker();
6746
- resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
6747
7920
  };
6748
- this.worker.postMessage({ bufferFrameLength: bufferFrameLength, audioData: buffers, chs: chs }, buffers);
7921
+ let read = ais.read(audioBuffers);
7922
+ for (let ch = 0; ch < chs; ch++) {
7923
+ let copy = new Float32Array(audioBuffers[ch]);
7924
+ trBuffers[ch] = copy.buffer;
7925
+ }
7926
+ this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6749
7927
  });
6750
7928
  }
6751
7929
  terminateWorker() {
@@ -6756,40 +7934,63 @@ class LevelMeasure {
6756
7934
  */
6757
7935
  workerFunction() {
6758
7936
  self.onmessage = function (msg) {
6759
- let chs = msg.data.chs;
7937
+ let len = msg.data.len;
6760
7938
  let bufferFrameLength = msg.data.bufferFrameLength;
6761
- let audioData = new Array(chs);
6762
- let linLevels = new Array(chs);
6763
- for (let ch = 0; ch < chs; ch++) {
6764
- audioData[ch] = new Float32Array(msg.data.audioData[ch]);
7939
+ if (len == -1) {
7940
+ // start
7941
+ postMessage({
7942
+ eod: false,
7943
+ bufferFrameLength: bufferFrameLength,
7944
+ frameLength: len
7945
+ });
6765
7946
  }
6766
- let frameLength = audioData[0].length;
6767
- let bufferCount = Math.ceil(frameLength / bufferFrameLength);
6768
- for (let ch = 0; ch < chs; ch++) {
6769
- linLevels[ch] = new Float32Array(bufferCount * 2);
7947
+ if (len == 0) {
7948
+ postMessage({
7949
+ eod: true,
7950
+ bufferFrameLength: bufferFrameLength,
7951
+ frameLength: len
7952
+ });
6770
7953
  }
6771
- if (audioData && chs > 0) {
7954
+ else {
7955
+ let chs = msg.data.chs;
7956
+ let audioData = new Array(chs);
7957
+ let linLevels = new Array(chs);
6772
7958
  for (let ch = 0; ch < chs; ch++) {
6773
- let chData = audioData[ch];
6774
- for (let s = 0; s < frameLength; s++) {
6775
- let bi = Math.floor(s / bufferFrameLength);
6776
- let lvlArrPos = bi * 2;
6777
- //let bs = s % bufferFrameLength;
6778
- let chSample = chData[s];
6779
- if (chSample < linLevels[ch][lvlArrPos]) {
6780
- linLevels[ch][lvlArrPos] = chSample;
6781
- }
6782
- lvlArrPos++;
6783
- if (chSample > linLevels[ch][lvlArrPos]) {
6784
- linLevels[ch][lvlArrPos] = chSample;
6785
- }
6786
- }
7959
+ audioData[ch] = new Float32Array(msg.data.audioData[ch]);
6787
7960
  }
6788
- const linLevelBufs = new Array(chs);
7961
+ //let frameLength = audioData[0].length;
7962
+ let bufferCount = Math.ceil(len / bufferFrameLength);
6789
7963
  for (let ch = 0; ch < chs; ch++) {
6790
- linLevelBufs[ch] = linLevels[ch].buffer;
7964
+ linLevels[ch] = new Float32Array(bufferCount * 2);
7965
+ }
7966
+ if (audioData && chs > 0) {
7967
+ for (let ch = 0; ch < chs; ch++) {
7968
+ let chData = audioData[ch];
7969
+ for (let s = 0; s < len; s++) {
7970
+ let bi = Math.floor(s / bufferFrameLength);
7971
+ let lvlArrPos = bi * 2;
7972
+ //let bs = s % bufferFrameLength;
7973
+ let chSample = chData[s];
7974
+ if (chSample < linLevels[ch][lvlArrPos]) {
7975
+ linLevels[ch][lvlArrPos] = chSample;
7976
+ }
7977
+ lvlArrPos++;
7978
+ if (chSample > linLevels[ch][lvlArrPos]) {
7979
+ linLevels[ch][lvlArrPos] = chSample;
7980
+ }
7981
+ }
7982
+ }
7983
+ const linLevelBufs = new Array(chs);
7984
+ for (let ch = 0; ch < chs; ch++) {
7985
+ linLevelBufs[ch] = linLevels[ch].buffer;
7986
+ }
7987
+ postMessage({
7988
+ eod: false,
7989
+ bufferFrameLength: bufferFrameLength,
7990
+ frameLength: len,
7991
+ linLevelBuffers: linLevelBufs
7992
+ }, linLevelBufs);
6791
7993
  }
6792
- postMessage({ bufferFrameLength: bufferFrameLength, frameLength: frameLength, linLevelBuffers: linLevelBufs }, linLevelBufs);
6793
7994
  }
6794
7995
  };
6795
7996
  }
@@ -7161,7 +8362,7 @@ RecordingItemDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", v
7161
8362
  <audio-levelbar fxFlex="1 0 1" [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
7162
8363
  <spr-recordingitemcontrols fxFlex="0 0 0" [audioLoaded]="displayAudioBuffer!==null" [playStartAction]="playStartAction" [playStopAction]="playStopAction" [peakDbLvl]="peakDbLvl" [agc]="_agc" (onShowRecordingDetails)="onShowRecordingDetails.emit()"></spr-recordingitemcontrols>
7163
8364
  </div>
7164
- `, isInline: true, styles: ["div{width:100%;background:darkgray;padding:4px;box-sizing:border-box;flex-wrap:nowrap}\n", "audio-levelbar{box-sizing:border-box}\n"], components: [{ type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }] });
8365
+ `, isInline: true, styles: ["div{width:100%;background:darkgray;padding:4px;box-sizing:border-box;flex-wrap:nowrap}\n", "audio-levelbar{box-sizing:border-box}\n"], components: [{ type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }] });
7165
8366
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingItemDisplay, decorators: [{
7166
8367
  type: Component,
7167
8368
  args: [{
@@ -7207,89 +8408,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
7207
8408
  type: Input
7208
8409
  }] } });
7209
8410
 
7210
- class Float32ArrayChunkerOutStream {
7211
- constructor(outStream) {
7212
- this.outStream = outStream;
7213
- this.bufs = new Array();
7214
- this._channels = 0;
7215
- this._chunkSize = 0;
7216
- this.filled = 0;
7217
- this.receivedFrames = 0;
7218
- this.sentFrames = 0;
7219
- }
7220
- createBuffers() {
7221
- this.bufs = new Array(this._channels);
7222
- for (let ch = 0; ch < this._channels; ch++) {
7223
- this.bufs[ch] = new Float32Array(this._chunkSize);
7224
- }
7225
- }
7226
- set chunkSize(chunkSize) {
7227
- this._chunkSize = chunkSize;
7228
- this.createBuffers();
7229
- }
7230
- get chunkSize() {
7231
- return this._chunkSize;
7232
- }
7233
- set channels(channels) {
7234
- this._channels = channels;
7235
- this.createBuffers();
7236
- }
7237
- get channels() {
7238
- return this._channels;
7239
- }
7240
- available() {
7241
- return this._chunkSize - this.filled;
7242
- }
7243
- write(buffers) {
7244
- let copied = 0;
7245
- if (buffers.length > 0) {
7246
- let buffersLen = buffers[0].length;
7247
- this.receivedFrames += buffersLen;
7248
- let avail = buffersLen;
7249
- // Fill out buffers until all values copied
7250
- while (avail > 0) {
7251
- let toFill = this._chunkSize - this.filled;
7252
- if (toFill > avail) {
7253
- toFill = avail;
7254
- }
7255
- let sliceEnd = copied + toFill;
7256
- // Firefox on Android sends only the first channel
7257
- for (let ch = 0; ch < buffersLen; ch++) {
7258
- if (buffers[ch]) {
7259
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
7260
- let buf = this.bufs[ch];
7261
- buf.set(cpPrt, this.filled);
7262
- }
7263
- }
7264
- copied += toFill;
7265
- avail -= toFill;
7266
- this.filled += toFill;
7267
- if (this.filled == this._chunkSize) {
7268
- this.outStream.write(this.bufs);
7269
- this.sentFrames += this.filled;
7270
- this.filled = 0;
7271
- }
7272
- }
7273
- }
7274
- return copied;
7275
- }
7276
- flush() {
7277
- if (this.filled > 0) {
7278
- let restBufs = new Array(this._channels);
7279
- for (let ch = 0; ch < this._channels; ch++) {
7280
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
7281
- }
7282
- this.outStream.write(restBufs);
7283
- this.outStream.flush();
7284
- this.sentFrames += this.filled;
7285
- this.filled = 0;
7286
- }
7287
- }
7288
- close() {
7289
- this.outStream.close();
7290
- }
7291
- }
7292
-
7293
8411
  class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
7294
8412
  constructor(outStream, chunkDurationSeconds) {
7295
8413
  super(outStream);
@@ -7298,11 +8416,11 @@ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream
7298
8416
  this.sequenceAudioFloat32OutStream = outStream;
7299
8417
  }
7300
8418
  setFormat(channels, sampleRate) {
7301
- console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: " + channels + ",sampleRate: " + sampleRate + ")");
8419
+ //console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "+channels+",sampleRate: "+sampleRate+")")
7302
8420
  this.channels = channels;
7303
8421
  this.sampleRate = sampleRate;
7304
8422
  this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
7305
- console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: " + this.chunkSize);
8423
+ //console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "+this.chunkSize);
7306
8424
  this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
7307
8425
  }
7308
8426
  nextStream() {
@@ -7508,7 +8626,8 @@ class ChunkManager {
7508
8626
  }
7509
8627
  }
7510
8628
  let BasicRecorder = class BasicRecorder {
7511
- constructor(dialog, sessionService, uploader, config) {
8629
+ constructor(changeDetectorRef, dialog, sessionService, uploader, config) {
8630
+ this.changeDetectorRef = changeDetectorRef;
7512
8631
  this.dialog = dialog;
7513
8632
  this.sessionService = sessionService;
7514
8633
  this.uploader = uploader;
@@ -7523,14 +8642,15 @@ let BasicRecorder = class BasicRecorder {
7523
8642
  this._selectedDeviceId = undefined;
7524
8643
  this._channelCount = 2;
7525
8644
  this._session = null;
8645
+ this._recordingFile = null;
7526
8646
  this.startedDate = null;
7527
8647
  this.uploadProgress = 100;
7528
8648
  this.uploadStatus = 'ok';
7529
8649
  this.audioSignalCollapsed = true;
7530
8650
  this.peakLevelInDb = MIN_DB_LEVEL;
7531
- this.displayLevelInfos = null;
7532
8651
  this.displayAudioClip = null;
7533
8652
  this.audioFetchSubscription = null;
8653
+ this.audioFetching = false;
7534
8654
  this.destroyed = false;
7535
8655
  this.navigationDisabled = true;
7536
8656
  // Default: Disabled chunked upload
@@ -7624,6 +8744,28 @@ let BasicRecorder = class BasicRecorder {
7624
8744
  this.ac.audioOutStream = outStream;
7625
8745
  }
7626
8746
  }
8747
+ showRecording() {
8748
+ this._controlAudioPlayer.stop();
8749
+ if (this.displayAudioClip) {
8750
+ let dap = this.displayAudioClip;
8751
+ let adh = dap.audioDataHolder;
8752
+ if (adh) {
8753
+ this.levelMeasure.calcBufferLevelInfos(adh, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8754
+ dap.levelInfos = levelInfos;
8755
+ this.changeDetectorRef.detectChanges();
8756
+ });
8757
+ }
8758
+ this.playStartAction.disabled = false;
8759
+ }
8760
+ else {
8761
+ this.playStartAction.disabled = true;
8762
+ // Collapse audio signal display if open
8763
+ if (!this.audioSignalCollapsed) {
8764
+ this.audioSignalCollapsed = true;
8765
+ }
8766
+ }
8767
+ this.changeDetectorRef.detectChanges();
8768
+ }
7627
8769
  start() {
7628
8770
  this.statusAlertType = 'info';
7629
8771
  this.statusMsg = 'Starting session...';
@@ -7844,9 +8986,9 @@ let BasicRecorder = class BasicRecorder {
7844
8986
  }
7845
8987
  this.transportActions.startAction.disabled = true;
7846
8988
  }
7847
- postRecording(wavFile, recUrl) {
8989
+ postRecording(wavFile, recUrl, rf) {
7848
8990
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
7849
- let ul = new Upload(wavBlob, recUrl);
8991
+ let ul = new Upload(wavBlob, recUrl, rf);
7850
8992
  this.uploader.queueUpload(ul);
7851
8993
  }
7852
8994
  postAudioStreamStart() {
@@ -7888,34 +9030,339 @@ let BasicRecorder = class BasicRecorder {
7888
9030
  let fd = new FormData();
7889
9031
  fd.set('uuid', this.rfUuid);
7890
9032
  fd.set('chunkCount', chunkCount.toString());
7891
- let ul = new Upload(fd, recUrl);
9033
+ let ul = new Upload(fd, recUrl, this._recordingFile);
7892
9034
  this.uploader.queueUpload(ul);
9035
+ console.debug("Queued for upload: " + this._recordingFile);
9036
+ }
9037
+ else {
9038
+ console.error("Recording file UUID not set!");
9039
+ }
9040
+ }
9041
+ closed() {
9042
+ this.statusAlertType = 'info';
9043
+ this.statusMsg = 'Session closed.';
9044
+ }
9045
+ error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
9046
+ this.statusMsg = 'ERROR: Recording.';
9047
+ this.statusAlertType = 'error';
9048
+ this.dialog.open(MessageDialog, {
9049
+ data: {
9050
+ type: 'error',
9051
+ title: 'Recording error',
9052
+ msg: msg,
9053
+ advice: advice
9054
+ }
9055
+ });
9056
+ }
9057
+ };
9058
+ // Enable only for developemnt/debug purposes of array audio buffers !!
9059
+ BasicRecorder.FORCE_ARRRAY_AUDIO_BUFFER = false;
9060
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
9061
+ BasicRecorder = __decorate([
9062
+ __param(4, Inject(SPEECHRECORDER_CONFIG))
9063
+ ], BasicRecorder);
9064
+
9065
+ class AudioDataHolder {
9066
+ constructor(_buffer, _arrayBuffer = null) {
9067
+ this._buffer = _buffer;
9068
+ this._arrayBuffer = _arrayBuffer;
9069
+ this._numberOfChannels = 0;
9070
+ this._sampleRate = 0;
9071
+ this._frameLen = 0;
9072
+ this._duration = 0;
9073
+ if (this._buffer && this._arrayBuffer) {
9074
+ throw Error('Only one of either audio buffer or array audio buffer must be set!');
9075
+ }
9076
+ if (this._buffer || this._arrayBuffer) {
9077
+ if (this._buffer) {
9078
+ this._numberOfChannels = this._buffer.numberOfChannels;
9079
+ this._sampleRate = this._buffer.sampleRate;
9080
+ this._frameLen = this._buffer.length;
9081
+ this._duration = this._frameLen / this._sampleRate;
9082
+ }
9083
+ else if (this._arrayBuffer) {
9084
+ this._numberOfChannels = this._arrayBuffer.channelCount;
9085
+ this._sampleRate = this._arrayBuffer.sampleRate;
9086
+ this._frameLen = this._arrayBuffer.frameLen;
9087
+ this._duration = this._frameLen / this._sampleRate;
9088
+ }
9089
+ }
9090
+ else {
9091
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9092
+ }
9093
+ }
9094
+ get duration() {
9095
+ return this._duration;
9096
+ }
9097
+ get sampleRate() {
9098
+ return this._sampleRate;
9099
+ }
9100
+ get numberOfChannels() {
9101
+ return this._numberOfChannels;
9102
+ }
9103
+ get frameLen() {
9104
+ return this._frameLen;
9105
+ }
9106
+ sampleCounts() {
9107
+ return this._numberOfChannels * this._frameLen;
9108
+ }
9109
+ frames(framePos, frameLen, bufs) {
9110
+ let read = 0;
9111
+ if (this._buffer) {
9112
+ let toRead = frameLen;
9113
+ if (this._buffer.length < framePos + frameLen) {
9114
+ toRead = this._buffer.length - framePos;
9115
+ }
9116
+ for (let ch = 0; ch < bufs.length; ch++) {
9117
+ let chData = this._buffer.getChannelData(ch);
9118
+ for (let i = 0; i < toRead; i++) {
9119
+ bufs[ch][i] = chData[framePos + i];
9120
+ }
9121
+ }
9122
+ read = toRead;
9123
+ }
9124
+ else if (this._arrayBuffer) {
9125
+ read = this._arrayBuffer.frames(framePos, frameLen, bufs);
9126
+ }
9127
+ return read;
9128
+ }
9129
+ audioInputStream() {
9130
+ if (this._buffer) {
9131
+ return new AudioBufferInputStream(this._buffer);
9132
+ }
9133
+ if (this._arrayBuffer) {
9134
+ return new ArrayAudioBufferInputStream(this._arrayBuffer);
9135
+ }
9136
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9137
+ }
9138
+ get buffer() {
9139
+ return this._buffer;
9140
+ }
9141
+ get arrayBuffer() {
9142
+ return this._arrayBuffer;
9143
+ }
9144
+ }
9145
+ AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG = 'One of either audio buffer or array audio buffer must be set!';
9146
+ class AudioBufferInputStream {
9147
+ constructor(audioBuffer) {
9148
+ this.audioBuffer = audioBuffer;
9149
+ this.framePos = 0;
9150
+ }
9151
+ close() {
9152
+ }
9153
+ skipFrames(n) {
9154
+ this.framePos += n;
9155
+ }
9156
+ read(buffers) {
9157
+ let read = 0;
9158
+ let toRead = buffers[0].length;
9159
+ if (this.framePos + toRead > this.audioBuffer.length) {
9160
+ toRead = this.audioBuffer.length - this.framePos;
9161
+ }
9162
+ for (let ch = 0; ch < buffers.length; ch++) {
9163
+ for (let i = 0; i < toRead; i++) {
9164
+ buffers[ch][i] = this.audioBuffer.getChannelData(ch)[this.framePos + i];
9165
+ }
9166
+ }
9167
+ read = toRead;
9168
+ this.framePos += toRead;
9169
+ return read;
9170
+ }
9171
+ }
9172
+
9173
+ class BasicRecFilesCache {
9174
+ constructor() {
9175
+ //public static readonly DEFAULT_MAX_SAMPLES=30*48000; // TEST 30 seconds
9176
+ this._sampleCount = 0;
9177
+ this.maxSampleCount = BasicRecFilesCache.DEFAULT_MAX_SAMPLES;
9178
+ this._currentRecordingFile = null;
9179
+ }
9180
+ get currentRecordingFile() {
9181
+ return this._currentRecordingFile;
9182
+ }
9183
+ set currentRecordingFile(value) {
9184
+ this._currentRecordingFile = value;
9185
+ this.expire();
9186
+ }
9187
+ }
9188
+ BasicRecFilesCache.DEBUG = false;
9189
+ BasicRecFilesCache.DEFAULT_MAX_SAMPLES = 10 * 60 * 48000; // 20 Minutes mono 48kHz
9190
+ class SprItemsCache extends BasicRecFilesCache {
9191
+ constructor() {
9192
+ super(...arguments);
9193
+ this._items = new Array();
9194
+ }
9195
+ get items() {
9196
+ return this._items;
9197
+ }
9198
+ addItem(item) {
9199
+ this._items.push(item);
9200
+ }
9201
+ getItem(index) {
9202
+ return this._items[index];
9203
+ }
9204
+ length() {
9205
+ return this._items.length;
9206
+ }
9207
+ tryExpire(toBeExpiredRf) {
9208
+ if (BasicRecFilesCache.DEBUG)
9209
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " try expire:");
9210
+ if (!RecordingFileUtils.equals(toBeExpiredRf, this.currentRecordingFile)) {
9211
+ if (BasicRecFilesCache.DEBUG)
9212
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " not current file...");
9213
+ if (toBeExpiredRf.serverPersisted) {
9214
+ if (BasicRecFilesCache.DEBUG)
9215
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is server persisted...");
9216
+ // expire recording files first stored to the cache
9217
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9218
+ if (expiredAudio) {
9219
+ this._sampleCount -= expiredAudio.sampleCounts();
9220
+ if (BasicRecFilesCache.DEBUG)
9221
+ console.debug("Rec. files cache: Expired: " + toBeExpiredRf.toString() + ". Cache samples now: " + this._sampleCount);
9222
+ }
9223
+ }
9224
+ else {
9225
+ if (BasicRecFilesCache.DEBUG)
9226
+ console.debug("Rec. files cache: #" + toBeExpiredRf.toString() + " not yet persisted on server.");
9227
+ }
7893
9228
  }
7894
9229
  else {
7895
- console.error("Recording file UUID not set!");
9230
+ if (BasicRecFilesCache.DEBUG)
9231
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is current file.");
9232
+ }
9233
+ }
9234
+ expire() {
9235
+ // expire corrected versions first
9236
+ if (BasicRecFilesCache.DEBUG)
9237
+ console.debug("Rec. files cache: Expire? current: " + this._sampleCount + ", max: " + this.maxSampleCount);
9238
+ if (this._sampleCount > this.maxSampleCount) {
9239
+ // expire older versions of an item first
9240
+ for (let ii = 0; ii < this._items.length; ii++) {
9241
+ if (this._sampleCount <= this.maxSampleCount) {
9242
+ break;
9243
+ }
9244
+ let it = this._items[ii];
9245
+ let itRfs = it.recs;
9246
+ if (itRfs && itRfs.length > 1) {
9247
+ for (let rfi = 0; rfi < itRfs.length - 1; rfi++) {
9248
+ if (this._sampleCount <= this.maxSampleCount) {
9249
+ break;
9250
+ }
9251
+ let toBeExpiredRf = itRfs[rfi];
9252
+ this.tryExpire(toBeExpiredRf);
9253
+ }
9254
+ }
9255
+ }
9256
+ // if that's not sufficient expire audio data of latest versions as well
9257
+ for (let ii = 0; ii < this._items.length; ii++) {
9258
+ if (this._sampleCount <= this.maxSampleCount) {
9259
+ break;
9260
+ }
9261
+ let it = this._items[ii];
9262
+ let itRfs = it.recs;
9263
+ if (itRfs && itRfs.length > 0) {
9264
+ for (let rfi = 0; rfi < itRfs.length; rfi++) {
9265
+ if (this._sampleCount <= this.maxSampleCount) {
9266
+ break;
9267
+ }
9268
+ let toBeExpiredRf = itRfs[rfi];
9269
+ this.tryExpire(toBeExpiredRf);
9270
+ }
9271
+ }
9272
+ }
7896
9273
  }
7897
9274
  }
7898
- closed() {
7899
- this.statusAlertType = 'info';
7900
- this.statusMsg = 'Session closed.';
9275
+ addSprRecFile(item, sprRecFile) {
9276
+ this.expire();
9277
+ if (!item.recs) {
9278
+ item.recs = new Array();
9279
+ }
9280
+ item.recs.push(sprRecFile);
9281
+ this._sampleCount += RecordingFileUtils.sampleCount(sprRecFile);
9282
+ if (BasicRecFilesCache.DEBUG)
9283
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
7901
9284
  }
7902
- error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
7903
- this.statusMsg = 'ERROR: Recording.';
7904
- this.statusAlertType = 'error';
7905
- this.dialog.open(MessageDialog, {
7906
- data: {
7907
- type: 'error',
7908
- title: 'Recording error',
7909
- msg: msg,
7910
- advice: advice
9285
+ setSprRecFileAudioData(sprRecFile, adh) {
9286
+ this.expire();
9287
+ if (BasicRecFilesCache.DEBUG)
9288
+ console.debug("Rec. files cache: Set audio data after expire. Cache samples: " + this._sampleCount);
9289
+ let currSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9290
+ this._sampleCount -= currSampleCnt;
9291
+ if (BasicRecFilesCache.DEBUG)
9292
+ console.debug("Rec. files cache: Set audio data subtracted curr sample count: " + currSampleCnt + ". Cache samples: " + this._sampleCount);
9293
+ RecordingFileUtils.setAudioData(sprRecFile, adh);
9294
+ let newSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9295
+ this._sampleCount += newSampleCnt;
9296
+ if (BasicRecFilesCache.DEBUG)
9297
+ console.debug("Rec. files cache: Set audio data added new sample count: " + newSampleCnt + ". Cache samples: " + this._sampleCount);
9298
+ let fl = adh?.frameLen;
9299
+ if (fl) {
9300
+ sprRecFile.frames = fl;
9301
+ }
9302
+ if (BasicRecFilesCache.DEBUG)
9303
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9304
+ }
9305
+ }
9306
+ class RecFilesCache extends BasicRecFilesCache {
9307
+ constructor() {
9308
+ super(...arguments);
9309
+ this._recFiles = new Array();
9310
+ }
9311
+ get recFiles() {
9312
+ return this._recFiles;
9313
+ }
9314
+ getRecFile(index) {
9315
+ return this._recFiles[index];
9316
+ }
9317
+ length() {
9318
+ return this._recFiles.length;
9319
+ }
9320
+ expire() {
9321
+ if (this._sampleCount > this.maxSampleCount) {
9322
+ // audio recorder list is sorted: lower index is newer
9323
+ for (let rfI = this._recFiles.length - 1; rfI >= 0; rfI--) {
9324
+ if (this._sampleCount <= this.maxSampleCount) {
9325
+ break;
9326
+ }
9327
+ let toBeExpiredRf = this._recFiles[rfI];
9328
+ if (toBeExpiredRf.serverPersisted) {
9329
+ if (!RecordingFileUtils.equals(toBeExpiredRf, this.currentRecordingFile)) {
9330
+ // expire recording files first stored to the cache
9331
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9332
+ if (expiredAudio) {
9333
+ this._sampleCount -= expiredAudio.sampleCounts();
9334
+ if (BasicRecFilesCache.DEBUG)
9335
+ console.debug("Rec. files cache: Expired #" + rfI + ". Cache samples: " + this._sampleCount);
9336
+ }
9337
+ }
9338
+ else {
9339
+ if (BasicRecFilesCache.DEBUG)
9340
+ console.debug("Rec. files cache: #" + rfI + " is current file. (not expiring)");
9341
+ }
9342
+ }
9343
+ else {
9344
+ if (BasicRecFilesCache.DEBUG)
9345
+ console.debug("Rec. files cache: #" + rfI + " not yet server persisted.");
9346
+ }
7911
9347
  }
7912
- });
9348
+ }
7913
9349
  }
7914
- };
7915
- BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7916
- BasicRecorder = __decorate([
7917
- __param(3, Inject(SPEECHRECORDER_CONFIG))
7918
- ], BasicRecorder);
9350
+ addRecFile(recFile) {
9351
+ this.expire();
9352
+ this._recFiles.push(recFile);
9353
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9354
+ if (BasicRecFilesCache.DEBUG)
9355
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
9356
+ }
9357
+ setRecFileAudioData(recFile, adh) {
9358
+ this.expire();
9359
+ this._sampleCount -= RecordingFileUtils.sampleCount(recFile);
9360
+ RecordingFileUtils.setAudioData(recFile, adh);
9361
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9362
+ if (BasicRecFilesCache.DEBUG)
9363
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9364
+ }
9365
+ }
7919
9366
 
7920
9367
  /**
7921
9368
  * Created by klausj on 17.06.2017.
@@ -8008,16 +9455,62 @@ class RecordingService {
8008
9455
  let recUrl = recFilesUrl + '/' + encItemcode + '/' + version;
8009
9456
  return this.audioRequest(recUrl);
8010
9457
  }
9458
+ fetchRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9459
+ let wobs = new Observable(observer => {
9460
+ let recFileId = recordingFile.recordingFileId;
9461
+ if (!recFileId) {
9462
+ recFileId = recordingFile.uuid;
9463
+ }
9464
+ if (recordingFile.session && recFileId) {
9465
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
9466
+ obs.subscribe(resp => {
9467
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9468
+ if (resp.body) {
9469
+ aCtx.decodeAudioData(resp.body, ab => {
9470
+ observer.next(ab);
9471
+ observer.complete();
9472
+ }, error => {
9473
+ observer.error(error);
9474
+ observer.complete();
9475
+ });
9476
+ }
9477
+ else {
9478
+ observer.error('Fetching audio file: response has no body');
9479
+ }
9480
+ }, (error) => {
9481
+ if (error.status == 404) {
9482
+ // Interpret not as an error, the file ist not recorded yet
9483
+ observer.next(null);
9484
+ observer.complete();
9485
+ }
9486
+ else {
9487
+ // all other states are errors
9488
+ observer.error(error);
9489
+ observer.complete();
9490
+ }
9491
+ });
9492
+ }
9493
+ else {
9494
+ observer.error();
9495
+ }
9496
+ });
9497
+ return wobs;
9498
+ }
8011
9499
  fetchAndApplyRecordingFile(aCtx, projectName, recordingFile) {
8012
9500
  let wobs = new Observable(observer => {
8013
- if (recordingFile.session && recordingFile.recordingFileId) {
8014
- let obs = this.fetchAudiofile(projectName, recordingFile.session, recordingFile.recordingFileId);
9501
+ let recFileId = recordingFile.recordingFileId;
9502
+ if (!recFileId) {
9503
+ recFileId = recordingFile.uuid;
9504
+ }
9505
+ if (recordingFile.session && recFileId) {
9506
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
8015
9507
  obs.subscribe(resp => {
8016
9508
  //console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
8017
9509
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8018
9510
  if (resp.body) {
8019
9511
  aCtx.decodeAudioData(resp.body, ab => {
8020
- recordingFile.audioBuffer = ab;
9512
+ let adh = new AudioDataHolder(ab, null);
9513
+ RecordingFileUtils.setAudioData(recordingFile, adh);
8021
9514
  if (this.debugDelay > 0) {
8022
9515
  window.setTimeout(() => {
8023
9516
  observer.next(recordingFile);
@@ -8055,6 +9548,47 @@ class RecordingService {
8055
9548
  });
8056
9549
  return wobs;
8057
9550
  }
9551
+ fetchSprRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9552
+ let wobs = new Observable(observer => {
9553
+ if (recordingFile.session) {
9554
+ let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
9555
+ obs.subscribe({
9556
+ next: resp => {
9557
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9558
+ if (resp.body) {
9559
+ aCtx.decodeAudioData(resp.body, ab => {
9560
+ //RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
9561
+ observer.next(ab);
9562
+ observer.complete();
9563
+ }, error => {
9564
+ observer.error(error);
9565
+ observer.complete();
9566
+ });
9567
+ }
9568
+ else {
9569
+ observer.error('Fetching audio file: response has no body');
9570
+ }
9571
+ },
9572
+ error: (error) => {
9573
+ if (error.status == 404) {
9574
+ // Interpret not as an error, the file ist not recorded yet
9575
+ observer.next(null);
9576
+ observer.complete();
9577
+ }
9578
+ else {
9579
+ // all other states are errors
9580
+ observer.error(error);
9581
+ observer.complete();
9582
+ }
9583
+ }
9584
+ });
9585
+ }
9586
+ else {
9587
+ observer.error();
9588
+ }
9589
+ });
9590
+ return wobs;
9591
+ }
8058
9592
  fetchAndApplySprRecordingFile(aCtx, projectName, recordingFile) {
8059
9593
  let wobs = new Observable(observer => {
8060
9594
  if (recordingFile.session) {
@@ -8064,7 +9598,7 @@ class RecordingService {
8064
9598
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8065
9599
  if (resp.body) {
8066
9600
  aCtx.decodeAudioData(resp.body, ab => {
8067
- recordingFile.audioBuffer = ab;
9601
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab, null));
8068
9602
  if (this.debugDelay > 0) {
8069
9603
  window.setTimeout(() => {
8070
9604
  observer.next(recordingFile);
@@ -8109,7 +9643,8 @@ class RecordingService {
8109
9643
  // Do not use Promise version, which does not work with Safari 13
8110
9644
  if (resp.body) {
8111
9645
  aCtx.decodeAudioData(resp.body, ab => {
8112
- let rf = new SprRecordingFile(sessId, itemcode, version, ab);
9646
+ let adh = new AudioDataHolder(ab, null);
9647
+ let rf = new SprRecordingFile(sessId, itemcode, version, adh);
8113
9648
  if (this.debugDelay > 0) {
8114
9649
  window.setTimeout(() => {
8115
9650
  observer.next(rf);
@@ -8225,14 +9760,9 @@ const DEFAULT_PRE_REC_DELAY = 1000;
8225
9760
  const DEFAULT_POST_REC_DELAY = 500;
8226
9761
  class SessionManager extends BasicRecorder {
8227
9762
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
8228
- super(dialog, sessionService, uploader, config);
8229
- this.changeDetectorRef = changeDetectorRef;
9763
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
8230
9764
  this.renderer = renderer;
8231
- this.dialog = dialog;
8232
- this.sessionService = sessionService;
8233
9765
  this.recFileService = recFileService;
8234
- this.uploader = uploader;
8235
- this.config = config;
8236
9766
  this.enableUploadRecordings = true;
8237
9767
  this.enableDownloadRecordings = false;
8238
9768
  this.status = 0 /* BLOCKED */;
@@ -8266,7 +9796,7 @@ class SessionManager extends BasicRecorder {
8266
9796
  };
8267
9797
  }
8268
9798
  ngOnDestroy() {
8269
- console.debug("Com destroy, disable wake lock.");
9799
+ //console.debug("Com destroy, disable wake lock.")
8270
9800
  this.disableWakeLockCond();
8271
9801
  this.destroyed = true;
8272
9802
  // TODO stop capture /playback
@@ -8397,7 +9927,7 @@ class SessionManager extends BasicRecorder {
8397
9927
  progressPercentValue() {
8398
9928
  let v = 100;
8399
9929
  if (this.items) {
8400
- v = this.promptIndex * 100 / (this.items.length - 1);
9930
+ v = this.promptIndex * 100 / (this.items.length() - 1);
8401
9931
  }
8402
9932
  return v;
8403
9933
  }
@@ -8411,9 +9941,6 @@ class SessionManager extends BasicRecorder {
8411
9941
  return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
8412
9942
  }
8413
9943
  set controlAudioPlayer(controlAudioPlayer) {
8414
- if (this._controlAudioPlayer) {
8415
- //this._controlAudioPlayer.listener=null;
8416
- }
8417
9944
  this._controlAudioPlayer = controlAudioPlayer;
8418
9945
  if (this._controlAudioPlayer) {
8419
9946
  this._controlAudioPlayer.listener = this;
@@ -8498,6 +10025,8 @@ class SessionManager extends BasicRecorder {
8498
10025
  this.autorecording = true;
8499
10026
  }
8500
10027
  if (this.ac != null) {
10028
+ // Hide loading hint on livelevel display
10029
+ this.audioFetching = false;
8501
10030
  if (!this.ac.opened) {
8502
10031
  if (this._selectedDeviceId) {
8503
10032
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -8514,7 +10043,7 @@ class SessionManager extends BasicRecorder {
8514
10043
  }
8515
10044
  loadScript() {
8516
10045
  this.promptItemCount = 0;
8517
- this.items = new Array();
10046
+ this.items = new SprItemsCache();
8518
10047
  let ln = 0;
8519
10048
  //TODO randomize not supported
8520
10049
  for (let si = 0; si < this._script.sections.length; si++) {
@@ -8528,7 +10057,7 @@ class SessionManager extends BasicRecorder {
8528
10057
  let pi = pis[piSectIdx];
8529
10058
  let promptAsStr = PromptitemUtil.toPlainTextString(pi);
8530
10059
  let it = new Item$1(promptAsStr, section.training, (!pi.type || pi.type === 'recording'));
8531
- this.items.push(it);
10060
+ this.items.addItem(it);
8532
10061
  ln++;
8533
10062
  }
8534
10063
  }
@@ -8569,10 +10098,10 @@ class SessionManager extends BasicRecorder {
8569
10098
  }
8570
10099
  downloadRecording() {
8571
10100
  if (this.displayRecFile) {
8572
- let ab = this.displayRecFile.audioBuffer;
10101
+ let ab = this.displayRecFile.audioDataHolder;
8573
10102
  let ww = new WavWriter();
8574
- if (ab) {
8575
- let wavFile = ww.writeAsync(ab, (wavFile) => {
10103
+ if (ab?.buffer) {
10104
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
8576
10105
  let blob = new Blob([wavFile], { type: 'audio/wav' });
8577
10106
  let rfUrl = URL.createObjectURL(blob);
8578
10107
  let dataDnlLnk = this.renderer.createElement('a');
@@ -8595,7 +10124,10 @@ class SessionManager extends BasicRecorder {
8595
10124
  set displayRecFile(displayRecFile) {
8596
10125
  this._displayRecFile = displayRecFile;
8597
10126
  if (this._displayRecFile) {
8598
- let ab = this._displayRecFile.audioBuffer;
10127
+ if (this.items) {
10128
+ this.items.currentRecordingFile = this._displayRecFile;
10129
+ }
10130
+ let ab = this._displayRecFile.audioDataHolder;
8599
10131
  if (ab) {
8600
10132
  this.displayAudioClip = new AudioClip(ab);
8601
10133
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
@@ -8603,27 +10135,46 @@ class SessionManager extends BasicRecorder {
8603
10135
  else {
8604
10136
  // clear for now ...
8605
10137
  this.displayAudioClip = null;
8606
- this.controlAudioPlayer.audioClip = null;
10138
+ if (this.controlAudioPlayer) {
10139
+ this.controlAudioPlayer.audioClip = null;
10140
+ }
8607
10141
  if (this._controlAudioPlayer && this._session) {
8608
10142
  //... and try to fetch from server
8609
- this.audioFetchSubscription = this.recFileService.fetchAndApplySprRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
8610
- let fab = null;
8611
- if (rf && this._displayRecFile) {
8612
- fab = this._displayRecFile.audioBuffer;
8613
- }
8614
- else {
8615
- this.statusMsg = 'Recording file could not be loaded.';
10143
+ this.audioFetching = true;
10144
+ let rf = this._displayRecFile;
10145
+ this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
10146
+ next: (ab) => {
10147
+ this.audioFetching = false;
10148
+ let fabDh = null;
10149
+ if (ab) {
10150
+ if (rf && this.items) {
10151
+ if (SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10152
+ let aab = ArrayAudioBuffer.fromAudioBuffer(ab);
10153
+ fabDh = new AudioDataHolder(null, aab);
10154
+ }
10155
+ else {
10156
+ fabDh = new AudioDataHolder(ab);
10157
+ }
10158
+ this.items.setSprRecFileAudioData(rf, fabDh);
10159
+ }
10160
+ }
10161
+ else {
10162
+ // Should actually be handled by the error resolver
10163
+ this.statusMsg = 'Recording file could not be loaded.';
10164
+ this.statusAlertType = 'error';
10165
+ }
10166
+ if (fabDh) {
10167
+ // this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore there should be no risk to set to wrong item
10168
+ this.displayAudioClip = new AudioClip(fabDh);
10169
+ }
10170
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
10171
+ this.showRecording();
10172
+ }, error: err => {
10173
+ console.error("Could not load recording file from server: " + err);
10174
+ this.audioFetching = false;
10175
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
8616
10176
  this.statusAlertType = 'error';
8617
10177
  }
8618
- if (fab) {
8619
- this.displayAudioClip = new AudioClip(fab);
8620
- }
8621
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
8622
- this.showRecording();
8623
- }, err => {
8624
- console.error("Could not load recording file from server: " + err);
8625
- this.statusMsg = 'Recording file could not be loaded: ' + err;
8626
- this.statusAlertType = 'error';
8627
10178
  });
8628
10179
  }
8629
10180
  else {
@@ -8634,34 +10185,14 @@ class SessionManager extends BasicRecorder {
8634
10185
  }
8635
10186
  else {
8636
10187
  this.displayAudioClip = null;
8637
- this.controlAudioPlayer.audioClip = null;
10188
+ if (this.controlAudioPlayer) {
10189
+ this.controlAudioPlayer.audioClip = null;
10190
+ }
8638
10191
  }
8639
10192
  }
8640
10193
  get displayRecFile() {
8641
10194
  return this._displayRecFile;
8642
10195
  }
8643
- showRecording() {
8644
- this.controlAudioPlayer.stop();
8645
- if (this.displayAudioClip) {
8646
- this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8647
- this.displayLevelInfos = levelInfos;
8648
- this.changeDetectorRef.detectChanges();
8649
- });
8650
- this.playStartAction.disabled = false;
8651
- }
8652
- else {
8653
- // TODO
8654
- // Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
8655
- // The level bar display does not clear, it shows the last captured stream
8656
- this.displayLevelInfos = null;
8657
- this.playStartAction.disabled = true;
8658
- // Collapse audio signal display if open
8659
- if (!this.audioSignalCollapsed) {
8660
- this.audioSignalCollapsed = true;
8661
- }
8662
- }
8663
- this.changeDetectorRef.detectChanges();
8664
- }
8665
10196
  isRecordingItem() {
8666
10197
  return (this.promptItem != null && this.promptItem.type !== 'nonrecording');
8667
10198
  }
@@ -8676,17 +10207,20 @@ class SessionManager extends BasicRecorder {
8676
10207
  if (this.audioFetchSubscription) {
8677
10208
  this.audioFetchSubscription.unsubscribe();
8678
10209
  }
10210
+ this.audioFetching = false;
8679
10211
  this.clearPrompt();
8680
10212
  let isNonrecording = (this.promptItem.type === 'nonrecording');
8681
10213
  if (isNonrecording || !this.section.promptphase || this.section.promptphase === 'IDLE') {
8682
10214
  this.applyPrompt();
8683
10215
  }
8684
10216
  if (isNonrecording) {
10217
+ this.displayRecFile = null;
10218
+ this.displayRecFileVersion = 0;
8685
10219
  this.startStopSignalState = 4 /* OFF */;
8686
10220
  }
8687
10221
  else {
8688
10222
  if (this.items) {
8689
- let it = this.items[this.promptIndex];
10223
+ let it = this.items.getItem(this.promptIndex);
8690
10224
  if (!it.recs) {
8691
10225
  it.recs = new Array();
8692
10226
  }
@@ -8703,13 +10237,13 @@ class SessionManager extends BasicRecorder {
8703
10237
  this.displayRecFileVersion = 0;
8704
10238
  }
8705
10239
  }
8706
- if (!temporary) {
8707
- this.showRecording();
8708
- }
8709
10240
  if (!this.readonly) {
8710
10241
  this.startStopSignalState = 0 /* IDLE */;
8711
10242
  }
8712
10243
  }
10244
+ if (!temporary) {
10245
+ this.showRecording();
10246
+ }
8713
10247
  this.updateStartActionDisableState();
8714
10248
  this.updateNavigationActions();
8715
10249
  }
@@ -8747,7 +10281,7 @@ class SessionManager extends BasicRecorder {
8747
10281
  updateNavigationActions() {
8748
10282
  let fwdNxtActionDisabled = true;
8749
10283
  if (this.items) {
8750
- let currRecs = this.items[this._promptIndex].recs;
10284
+ let currRecs = this.items.getItem(this._promptIndex).recs;
8751
10285
  fwdNxtActionDisabled = !(currRecs != null && currRecs.length > 0);
8752
10286
  }
8753
10287
  this.transportActions.fwdNextAction.disabled = this.navigationDisabled || fwdNxtActionDisabled;
@@ -8761,10 +10295,10 @@ class SessionManager extends BasicRecorder {
8761
10295
  newPrIdx = 0;
8762
10296
  }
8763
10297
  if (this.items != null) {
8764
- let itRecs = this.items[newPrIdx].recs;
10298
+ let itRecs = this.items.getItem(newPrIdx).recs;
8765
10299
  while (itRecs != null && (itRecs.length > 0) && newPrIdx < this.promptItemCount) {
8766
10300
  newPrIdx++;
8767
- itRecs = this.items[newPrIdx].recs;
10301
+ itRecs = this.items.getItem(newPrIdx).recs;
8768
10302
  }
8769
10303
  if (!itRecs || itRecs.length == 0) {
8770
10304
  this.promptIndex = newPrIdx;
@@ -8785,6 +10319,23 @@ class SessionManager extends BasicRecorder {
8785
10319
  }
8786
10320
  }
8787
10321
  started() {
10322
+ let ic = this.promptItem.itemcode;
10323
+ let rf = null;
10324
+ if (this._session && ic) {
10325
+ let sessId = this._session.sessionId;
10326
+ let cpIdx = this.promptIndex;
10327
+ if (this.items) {
10328
+ let it = this.items.getItem(cpIdx);
10329
+ if (!it.recs) {
10330
+ it.recs = new Array();
10331
+ }
10332
+ rf = new SprRecordingFile(sessId, ic, it.recs.length, null);
10333
+ //it.recs.push(rf);
10334
+ this.items.addSprRecFile(it, rf);
10335
+ this.items.currentRecordingFile = rf;
10336
+ }
10337
+ }
10338
+ this._recordingFile = rf;
8788
10339
  this.status = 3 /* PRE_RECORDING */;
8789
10340
  super.started();
8790
10341
  this.startStopSignalState = 1 /* PRERECORDING */;
@@ -8911,13 +10462,14 @@ class SessionManager extends BasicRecorder {
8911
10462
  if (rfd.recording && rfd.recording.itemcode) {
8912
10463
  let prIdx = this.promptIndexByItemcode(rfd.recording.itemcode);
8913
10464
  if (this.items != null && prIdx !== null) {
8914
- let it = this.items[prIdx];
10465
+ let it = this.items.getItem(prIdx);
8915
10466
  if (it && this._session) {
8916
- if (!it.recs) {
8917
- it.recs = new Array();
8918
- }
10467
+ // if (!it.recs) {
10468
+ // it.recs = new Array<SprRecordingFile>();
10469
+ // }
8919
10470
  let rf = new SprRecordingFile(this._session.sessionId, rfd.recording.itemcode, rfd.version, null);
8920
- it.recs[rfd.version] = rf;
10471
+ rf.serverPersisted = true;
10472
+ this.items.addSprRecFile(it, rf);
8921
10473
  }
8922
10474
  else {
8923
10475
  //console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
@@ -8939,22 +10491,24 @@ class SessionManager extends BasicRecorder {
8939
10491
  this.statusMsg = 'Recorded.';
8940
10492
  this.startStopSignalState = 0 /* IDLE */;
8941
10493
  let ad = null;
10494
+ let ada = null;
10495
+ let adh = null;
10496
+ let frameLen = 0;
8942
10497
  if (this.ac != null) {
8943
- ad = this.ac.audioBuffer();
8944
- }
8945
- let ic = this.promptItem.itemcode;
8946
- let rf = null;
8947
- if (this._session && ic) {
8948
- let sessId = this._session.sessionId;
8949
- let cpIdx = this.promptIndex;
8950
- if (this.items) {
8951
- let it = this.items[cpIdx];
8952
- if (!it.recs) {
8953
- it.recs = new Array();
8954
- }
8955
- rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8956
- it.recs.push(rf);
10498
+ if (this.uploadChunkSizeSeconds || SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10499
+ ada = this.ac.audioBufferArray();
10500
+ frameLen = ada.frameLen;
8957
10501
  }
10502
+ else {
10503
+ ad = this.ac.audioBuffer();
10504
+ frameLen = ad.length;
10505
+ }
10506
+ adh = new AudioDataHolder(ad, ada);
10507
+ }
10508
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
10509
+ let rf = this._recordingFile;
10510
+ if (rf && rf instanceof SprRecordingFile) {
10511
+ this.items?.setSprRecFileAudioData(rf, adh);
8958
10512
  if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8959
10513
  // TODO use SpeechRecorderconfig resp. RecfileService
8960
10514
  // convert asynchronously to 16-bit integer PCM
@@ -8962,7 +10516,7 @@ class SessionManager extends BasicRecorder {
8962
10516
  // TODO duplicate conversion for manual download
8963
10517
  //console.log("Build wav writer...");
8964
10518
  this.processingRecording = true;
8965
- if (ad && rf) {
10519
+ if (ad) {
8966
10520
  let ww = new WavWriter();
8967
10521
  //new REST API URL
8968
10522
  let apiEndPoint = '';
@@ -8975,7 +10529,7 @@ class SessionManager extends BasicRecorder {
8975
10529
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
8976
10530
  let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.itemCode;
8977
10531
  ww.writeAsync(ad, (wavFile) => {
8978
- this.postRecording(wavFile, recUrl);
10532
+ this.postRecording(wavFile, recUrl, rf);
8979
10533
  this.processingRecording = false;
8980
10534
  this.updateWakeLock();
8981
10535
  });
@@ -8986,8 +10540,8 @@ class SessionManager extends BasicRecorder {
8986
10540
  let complete = true;
8987
10541
  if (this.items) {
8988
10542
  // search backwards, to gain faster detection of incomplete state
8989
- for (let ri = this.items.length - 1; ri >= 0; ri--) {
8990
- let it = this.items[ri];
10543
+ for (let ri = this.items.length() - 1; ri >= 0; ri--) {
10544
+ let it = this.items.getItem(ri);
8991
10545
  if (it.recording && !it.training && (!it.recs || it.recs.length == 0)) {
8992
10546
  complete = false;
8993
10547
  break;
@@ -9051,13 +10605,13 @@ class SessionManager extends BasicRecorder {
9051
10605
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9052
10606
  let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
9053
10607
  ww.writeAsync(audioBuffer, (wavFile) => {
9054
- this.postRecording(wavFile, recUrl);
10608
+ this.postRecording(wavFile, recUrl, null);
9055
10609
  this.processingRecording = false;
9056
10610
  });
9057
10611
  }
9058
- postRecording(wavFile, recUrl) {
10612
+ postRecording(wavFile, recUrl, rf) {
9059
10613
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
9060
- let ul = new Upload(wavBlob, recUrl);
10614
+ let ul = new Upload(wavBlob, recUrl, rf);
9061
10615
  this.uploader.queueUpload(ul);
9062
10616
  }
9063
10617
  stop() {
@@ -9093,7 +10647,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9093
10647
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9094
10648
  <app-sprprompting [projectName]="projectName"
9095
10649
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9096
- [items]="items"
10650
+ [items]="items?.items"
9097
10651
  [transportActions]="transportActions"
9098
10652
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9099
10653
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9107,10 +10661,10 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9107
10661
 
9108
10662
 
9109
10663
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9110
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
10664
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9111
10665
  <div fxLayout="row">
9112
10666
  <spr-recordingitemcontrols fxFlex="10 0 1"
9113
- [audioLoaded]="displayAudioClip?.buffer!==null"
10667
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9114
10668
  [playStartAction]="controlAudioPlayer?.startAction"
9115
10669
  [playStopAction]="controlAudioPlayer?.stopAction"
9116
10670
  [peakDbLvl]="peakLevelInDb"
@@ -9128,7 +10682,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9128
10682
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9129
10683
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9130
10684
  </div>
9131
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10685
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9132
10686
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9133
10687
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9134
10688
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9136,7 +10690,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9136
10690
  <app-readystateindicator class="ricontrols" fxLayoutAlign="end center" fxHide.xs [ready]="dataSaved && !isActive()"></app-readystateindicator>
9137
10691
  </div>
9138
10692
  </div>
9139
- `, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: TransportPanel, selector: "app-sprtransport", inputs: ["readonly", "actions", "navigationEnabled", "pausingEnabled"] }], directives: [{ type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }, { type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.DefaultLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }] });
10693
+ `, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: TransportPanel, selector: "app-sprtransport", inputs: ["readonly", "actions", "navigationEnabled", "pausingEnabled"] }], directives: [{ type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }, { type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.DefaultLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }] });
9140
10694
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SessionManager, decorators: [{
9141
10695
  type: Component,
9142
10696
  args: [{
@@ -9147,7 +10701,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9147
10701
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9148
10702
  <app-sprprompting [projectName]="projectName"
9149
10703
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9150
- [items]="items"
10704
+ [items]="items?.items"
9151
10705
  [transportActions]="transportActions"
9152
10706
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9153
10707
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9161,10 +10715,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9161
10715
 
9162
10716
 
9163
10717
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9164
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
10718
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9165
10719
  <div fxLayout="row">
9166
10720
  <spr-recordingitemcontrols fxFlex="10 0 1"
9167
- [audioLoaded]="displayAudioClip?.buffer!==null"
10721
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9168
10722
  [playStartAction]="controlAudioPlayer?.startAction"
9169
10723
  [playStopAction]="controlAudioPlayer?.stopAction"
9170
10724
  [peakDbLvl]="peakLevelInDb"
@@ -9182,7 +10736,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9182
10736
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9183
10737
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9184
10738
  </div>
9185
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10739
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9186
10740
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9187
10741
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9188
10742
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9471,7 +11025,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
9471
11025
  this.uploadUpdate(ue);
9472
11026
  };
9473
11027
  window.addEventListener('beforeunload', (e) => {
9474
- console.debug("Before page unload event");
11028
+ //console.debug("Before page unload event");
9475
11029
  if (this.ready()) {
9476
11030
  return;
9477
11031
  }
@@ -9790,14 +11344,15 @@ class AudioDisplayPlayer {
9790
11344
  if (this.aCtx) {
9791
11345
  this.aCtx.decodeAudioData(data, (audioBuffer) => {
9792
11346
  //console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
9793
- this.audioClip = new AudioClip(audioBuffer);
11347
+ let adh = new AudioDataHolder(audioBuffer, null);
11348
+ this.audioClip = new AudioClip(adh);
9794
11349
  });
9795
11350
  }
9796
11351
  }
9797
- set audioData(audioBuffer) {
9798
- this.audioDisplayScrollPane.audioData = audioBuffer;
9799
- if (audioBuffer) {
9800
- let clip = new AudioClip(audioBuffer);
11352
+ set audioData(audioData) {
11353
+ this.audioDisplayScrollPane.audioData = audioData;
11354
+ if (audioData) {
11355
+ let clip = new AudioClip(audioData);
9801
11356
  if (this.ap) {
9802
11357
  this.ap.audioClip = clip;
9803
11358
  this.playStartAction.disabled = false;
@@ -9819,7 +11374,7 @@ class AudioDisplayPlayer {
9819
11374
  let audioData = null;
9820
11375
  let sel = null;
9821
11376
  if (audioClip) {
9822
- audioData = audioClip.buffer;
11377
+ audioData = audioClip.audioDataHolder;
9823
11378
  sel = audioClip.selection;
9824
11379
  if (this._audioClip) {
9825
11380
  this._audioClip.addSelectionObserver((ac) => {
@@ -10090,7 +11645,7 @@ class RecordingFileService {
10090
11645
  // Do not use Promise version, which does not work with Safari 13
10091
11646
  if (resp.body) {
10092
11647
  aCtx.decodeAudioData(resp.body, ab => {
10093
- recordingFile.audioBuffer = ab;
11648
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab));
10094
11649
  if (this.debugDelay > 0) {
10095
11650
  window.setTimeout(() => {
10096
11651
  observer.next(recordingFile);
@@ -10140,7 +11695,7 @@ class RecordingFileService {
10140
11695
  if (resp.body) {
10141
11696
  aCtx.decodeAudioData(resp.body, ab => {
10142
11697
  if (rf) {
10143
- rf.audioBuffer = ab;
11698
+ RecordingFileUtils.setAudioData(rf, new AudioDataHolder(ab));
10144
11699
  }
10145
11700
  else {
10146
11701
  observer.error('Recording file object null');
@@ -10191,7 +11746,8 @@ class RecordingFileService {
10191
11746
  if (resp.body) {
10192
11747
  aCtx.decodeAudioData(resp.body, ab => {
10193
11748
  if (rf) {
10194
- rf.audioBuffer = ab;
11749
+ let adh = new AudioDataHolder(ab);
11750
+ RecordingFileUtils.setAudioData(rf, adh);
10195
11751
  }
10196
11752
  else {
10197
11753
  observer.error('Recording file object null');
@@ -10234,7 +11790,7 @@ class RecordingFileService {
10234
11790
  // append UUID to make request URL unique to avoid localhost server caching
10235
11791
  recUrl = recUrl + '.json?requestUUID=' + UUID.generate();
10236
11792
  }
10237
- console.log("Path request URL: " + recUrl);
11793
+ //console.log("Path request URL: "+recUrl)
10238
11794
  return this.http.patch(recUrl, { editSampleRate: editSampleRate, editStartFrame: editStartFrame, editEndFrame: editEndFrame }, { withCredentials: this.withCredentials });
10239
11795
  }
10240
11796
  }
@@ -10252,6 +11808,7 @@ class RecordingFileMetaComponent {
10252
11808
  constructor() {
10253
11809
  this.sessionId = null;
10254
11810
  this._recordingFile = null;
11811
+ this.stateLoading = false;
10255
11812
  this.itemCode = null;
10256
11813
  this.uuid = null;
10257
11814
  }
@@ -10267,12 +11824,12 @@ class RecordingFileMetaComponent {
10267
11824
  }
10268
11825
  if (this.itemCode) {
10269
11826
  this.uuid = null;
10270
- console.debug("SprRecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11827
+ //console.debug("SprRecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10271
11828
  }
10272
11829
  else {
10273
11830
  this.itemCode = null;
10274
11831
  this.uuid = this._recordingFile?.uuid;
10275
- console.debug("RecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11832
+ //console.debug("RecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10276
11833
  }
10277
11834
  }
10278
11835
  else {
@@ -10289,10 +11846,11 @@ class RecordingFileMetaComponent {
10289
11846
  }
10290
11847
  }
10291
11848
  RecordingFileMetaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10292
- RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: { sessionId: "sessionId", recordingFile: "recordingFile" }, ngImport: i0, template: `
11849
+ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: { sessionId: "sessionId", stateLoading: "stateLoading", recordingFile: "recordingFile" }, ngImport: i0, template: `
10293
11850
  <mat-card>
10294
11851
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10295
11852
  <mat-card-content>
11853
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10296
11854
  <table>
10297
11855
  <tr *ngIf="itemCode">
10298
11856
  <td>Itemcode:</td>
@@ -10322,7 +11880,7 @@ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10322
11880
  </table>
10323
11881
  </mat-card-content>
10324
11882
  </mat-card>
10325
- `, isInline: true, components: [{ type: i1$4.MatCard, selector: "mat-card", exportAs: ["matCard"] }], directives: [{ type: i1$4.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { type: i1$4.MatCardContent, selector: "mat-card-content, [mat-card-content], [matCardContent]" }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
11883
+ `, isInline: true, components: [{ type: i1$4.MatCard, selector: "mat-card", exportAs: ["matCard"] }, { type: i1$3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }], directives: [{ type: i1$4.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { type: i1$4.MatCardContent, selector: "mat-card-content, [mat-card-content], [matCardContent]" }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
10326
11884
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, decorators: [{
10327
11885
  type: Component,
10328
11886
  args: [{
@@ -10331,6 +11889,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10331
11889
  <mat-card>
10332
11890
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10333
11891
  <mat-card-content>
11892
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10334
11893
  <table>
10335
11894
  <tr *ngIf="itemCode">
10336
11895
  <td>Itemcode:</td>
@@ -10365,6 +11924,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10365
11924
  }]
10366
11925
  }], propDecorators: { sessionId: [{
10367
11926
  type: Input
11927
+ }], stateLoading: [{
11928
+ type: Input
10368
11929
  }], recordingFile: [{
10369
11930
  type: Input
10370
11931
  }] } });
@@ -10507,6 +12068,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10507
12068
  this.recordingFileVersion = null;
10508
12069
  this.routedByQueryParam = false;
10509
12070
  this.posInList = null;
12071
+ this.audioFetching = false;
10510
12072
  this.naviInfoLoading = false;
10511
12073
  this.parentE = this.eRef.nativeElement;
10512
12074
  this.firstAction = new Action('First');
@@ -10676,18 +12238,20 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10676
12238
  this.updateActions();
10677
12239
  let audioContext = AudioContextProvider.audioContextInstance();
10678
12240
  if (audioContext) {
12241
+ this.audioFetching = true;
10679
12242
  this.recordingFileService.fetchSprRecordingFile(audioContext, rfId).subscribe(value => {
12243
+ this.audioFetching = false;
10680
12244
  this.status = 'Audio file loaded.';
10681
12245
  let clip = null;
10682
12246
  this.recordingFile = value;
10683
12247
  if (this.recordingFile) {
10684
- let ab = this.recordingFile.audioBuffer;
12248
+ let ab = this.recordingFile.audioDataHolder;
10685
12249
  if (ab) {
10686
12250
  clip = new AudioClip(ab);
10687
12251
  let esffsr = null;
10688
12252
  let eeffsr = null;
10689
12253
  let esr = null;
10690
- if (clip.buffer != null) {
12254
+ if (clip.audioDataHolder != null) {
10691
12255
  esr = ab.sampleRate;
10692
12256
  if (esr != null) {
10693
12257
  esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
@@ -10699,8 +12263,8 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10699
12263
  sel = new Selection(ab.sampleRate, esffsr, eeffsr);
10700
12264
  }
10701
12265
  else {
10702
- let ch0 = ab.getChannelData(0);
10703
- let frameLength = ch0.length;
12266
+ //let ch0 = ab.getChannelData(0);
12267
+ let frameLength = ab.frameLen;
10704
12268
  sel = new Selection(esr, esffsr, frameLength);
10705
12269
  }
10706
12270
  }
@@ -10714,6 +12278,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10714
12278
  this.audioClip = clip;
10715
12279
  this.loadedRecfile();
10716
12280
  }, error1 => {
12281
+ this.audioFetching = false;
10717
12282
  this.status = 'Error loading audio file!';
10718
12283
  });
10719
12284
  }
@@ -10823,7 +12388,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10823
12388
  for (let avRfV of avRf) {
10824
12389
  os += avRfV.version + '/';
10825
12390
  }
10826
- console.debug(os);
12391
+ //console.debug(os);
10827
12392
  }
10828
12393
  }
10829
12394
  this.updatePos();
@@ -10839,7 +12404,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10839
12404
 
10840
12405
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10841
12406
  <div class="ctrlview">
10842
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12407
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10843
12408
 
10844
12409
  <audio-display-control [audioClip]="audioClip"
10845
12410
  [playStartAction]="playStartAction"
@@ -10852,7 +12417,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10852
12417
  [zoomFitToPanelAction]="zoomFitToPanelAction"></audio-display-control>
10853
12418
  <app-recording-file-navi [items]="availRecFiles?.length" [itemPos]="posInList" [version]="recordingFileVersion" [versions]="versions" [firstAction]="firstAction" [prevAction]="prevAction" [nextAction]="nextAction" [lastAction]="lastAction" [selectVersion]="toVersionAction" [naviInfoLoading]="naviInfoLoading"></app-recording-file-navi>
10854
12419
  </div>
10855
- `, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }] });
12420
+ `, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "stateLoading", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }] });
10856
12421
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileViewComponent, decorators: [{
10857
12422
  type: Component,
10858
12423
  args: [{
@@ -10861,7 +12426,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10861
12426
 
10862
12427
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10863
12428
  <div class="ctrlview">
10864
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12429
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10865
12430
 
10866
12431
  <audio-display-control [audioClip]="audioClip"
10867
12432
  [playStartAction]="playStartAction"
@@ -10954,7 +12519,7 @@ class RecordingFileUI extends RecordingFileViewComponent {
10954
12519
  }
10955
12520
  applySelection() {
10956
12521
  if (this.audioClip) {
10957
- let ab = this.audioClip.buffer;
12522
+ let ab = this.audioClip.audioDataHolder;
10958
12523
  let s = this.audioClip.selection;
10959
12524
  if (ab && this.recordingFile?.recordingFileId) {
10960
12525
  let sr = null;
@@ -10992,7 +12557,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
10992
12557
 
10993
12558
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10994
12559
  <div class="ctrlview">
10995
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12560
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10996
12561
  <audio-display-control [audioClip]="audioClip"
10997
12562
  [playStartAction]="playStartAction"
10998
12563
  [playSelectionAction]="playSelectionAction"
@@ -11006,7 +12571,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11006
12571
  </div>
11007
12572
 
11008
12573
  <button mat-raised-button color="accent" (click)="applySelection()" [disabled]="editSaved">{{this.applyButtonText()}}</button>
11009
- `, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
12574
+ `, isInline: true, styles: [":host{flex:2;display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:20px;z-index:5;box-sizing:border-box;background-color:#fff}\n", ".ctrlview{display:flex;flex-direction:row}\n", "audio-display-control{flex:3}\n"], components: [{ type: AudioDisplayScrollPane, selector: "audio-display-scroll-pane", inputs: ["audioClip"], outputs: ["zoomInAction", "zoomOutAction", "zoomSelectedAction", "zoomFitToPanelAction"] }, { type: RecordingFileMetaComponent, selector: "app-recording-file-meta", inputs: ["sessionId", "stateLoading", "recordingFile"] }, { type: AudioDisplayControl, selector: "audio-display-control", inputs: ["audioClip", "playStartAction", "playSelectionAction", "playStopAction", "zoomInAction", "zoomOutAction", "zoomFitToPanelAction", "zoomSelectedAction", "autoPlayOnSelectToggleAction"] }, { type: RecordingFileNaviComponent, selector: "app-recording-file-navi", inputs: ["firstAction", "prevAction", "nextAction", "lastAction", "items", "itemPos", "selectVersion", "versions", "version", "naviInfoLoading"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
11010
12575
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileUI, decorators: [{
11011
12576
  type: Component,
11012
12577
  args: [{
@@ -11017,7 +12582,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
11017
12582
 
11018
12583
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11019
12584
  <div class="ctrlview">
11020
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12585
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11021
12586
  <audio-display-control [audioClip]="audioClip"
11022
12587
  [playStartAction]="playStartAction"
11023
12588
  [playSelectionAction]="playSelectionAction"
@@ -11074,7 +12639,8 @@ class MediaUtils {
11074
12639
 
11075
12640
  class RecordingList {
11076
12641
  constructor() {
11077
- this.recordingList = new Array();
12642
+ //private recordingList:Array<RecordingFile>=new Array<RecordingFile>();
12643
+ this.recordingList = new RecFilesCache();
11078
12644
  //cols=['index','length','samples','samplerate','action'];
11079
12645
  this.cols = ['index', 'startedDate', 'length', 'action'];
11080
12646
  this.selectDisabled = false;
@@ -11086,7 +12652,7 @@ class RecordingList {
11086
12652
  this.buildDataSource();
11087
12653
  }
11088
12654
  buildDataSource() {
11089
- this.recordingList.sort((a, b) => {
12655
+ this.recordingList.recFiles.sort((a, b) => {
11090
12656
  let cmp = 0;
11091
12657
  let aD = null;
11092
12658
  let bD = null;
@@ -11107,24 +12673,35 @@ class RecordingList {
11107
12673
  }
11108
12674
  return cmp;
11109
12675
  });
11110
- this.recordingListDataSource.data = this.recordingList;
12676
+ this.recordingListDataSource.data = this.recordingList.recFiles;
11111
12677
  }
11112
- push(rf) {
11113
- this.recordingList.push(rf);
12678
+ addRecFile(rf) {
12679
+ this.recordingList.addRecFile(rf);
11114
12680
  this.buildDataSource();
11115
12681
  }
12682
+ setRecFileAudioData(recFile, adh) {
12683
+ this.recordingList.setRecFileAudioData(recFile, adh);
12684
+ }
11116
12685
  selectRecordingFile(rf) {
12686
+ this.recordingList.currentRecordingFile = rf;
11117
12687
  this.selectedRecordingFileChanged.emit(rf);
11118
12688
  }
11119
12689
  selectTop() {
11120
- if (this.recordingList.length > 0) {
11121
- this.selectRecordingFile(this.recordingList[0]);
12690
+ if (this.recordingList.recFiles.length > 0) {
12691
+ this.selectRecordingFile(this.recordingList.recFiles[0]);
11122
12692
  }
11123
12693
  }
11124
12694
  lengthTimeFormatted(rf) {
11125
12695
  let str = '--:--:--';
11126
- if (rf.frames && rf.audioBuffer) {
11127
- str = MediaUtils.toMediaTime(rf.frames / rf.audioBuffer?.sampleRate);
12696
+ let tl = null;
12697
+ if (rf.timeLength) {
12698
+ tl = rf.timeLength;
12699
+ }
12700
+ else if (rf.frames && rf.audioDataHolder) {
12701
+ tl = rf.frames / rf.audioDataHolder?.sampleRate;
12702
+ }
12703
+ if (tl) {
12704
+ str = MediaUtils.toMediaTime(tl);
11128
12705
  }
11129
12706
  return str;
11130
12707
  }
@@ -11210,8 +12787,11 @@ class RecorderCombiPane {
11210
12787
  }
11211
12788
  ngAfterViewInit() {
11212
12789
  }
11213
- push(rf) {
11214
- this.recordingListComp.push(rf);
12790
+ addRecFile(rf) {
12791
+ this.recordingListComp.addRecFile(rf);
12792
+ }
12793
+ setRecFileAudioData(recFile, adh) {
12794
+ this.recordingListComp.setRecFileAudioData(recFile, adh);
11215
12795
  }
11216
12796
  selectRecordingFile(rf) {
11217
12797
  this.selectedRecordingFileChanged.emit(rf);
@@ -11284,15 +12864,11 @@ class Item {
11284
12864
  }
11285
12865
  class AudioRecorder extends BasicRecorder {
11286
12866
  constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11287
- super(dialog, sessionService, uploader, config);
11288
- this.changeDetectorRef = changeDetectorRef;
12867
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
11289
12868
  this.renderer = renderer;
11290
12869
  this.route = route;
11291
- this.dialog = dialog;
11292
- this.sessionService = sessionService;
11293
12870
  this.recFileService = recFileService;
11294
12871
  this.uploader = uploader;
11295
- this.config = config;
11296
12872
  this._project = null;
11297
12873
  this.projectName = null;
11298
12874
  this.enableUploadRecordings = true;
@@ -11475,13 +13051,15 @@ class AudioRecorder extends BasicRecorder {
11475
13051
  if (rfs) {
11476
13052
  if (rfs instanceof Array) {
11477
13053
  rfs.forEach((rf) => {
13054
+ // the list comes from the server, asssuem all recording files as server persisted
13055
+ rf.serverPersisted = true;
11478
13056
  if (rf.startedDate) {
11479
13057
  rf._startedAsDateObj = new Date(rf.startedDate);
11480
13058
  }
11481
13059
  if (rf.date) {
11482
13060
  rf._dateAsDateObj = new Date(rf.date);
11483
13061
  }
11484
- this.recorderCombiPane.push(rf);
13062
+ this.recorderCombiPane.addRecFile(rf);
11485
13063
  });
11486
13064
  }
11487
13065
  else {
@@ -11535,6 +13113,7 @@ class AudioRecorder extends BasicRecorder {
11535
13113
  return this._project;
11536
13114
  }
11537
13115
  selectRecordingFile(rf) {
13116
+ this.audioFetching = false;
11538
13117
  this.displayRecFile = rf;
11539
13118
  }
11540
13119
  uploadUpdate(ue) {
@@ -11556,9 +13135,6 @@ class AudioRecorder extends BasicRecorder {
11556
13135
  this.changeDetectorRef.detectChanges();
11557
13136
  }
11558
13137
  set controlAudioPlayer(controlAudioPlayer) {
11559
- if (this._controlAudioPlayer) {
11560
- //this._controlAudioPlayer.listener=null;
11561
- }
11562
13138
  this._controlAudioPlayer = controlAudioPlayer;
11563
13139
  if (this._controlAudioPlayer) {
11564
13140
  this._controlAudioPlayer.listener = this;
@@ -11638,6 +13214,7 @@ class AudioRecorder extends BasicRecorder {
11638
13214
  this.displayAudioClip = null;
11639
13215
  this.showRecording();
11640
13216
  if (this.ac) {
13217
+ this.audioFetching = false;
11641
13218
  if (!this.ac.opened) {
11642
13219
  if (this._selectedDeviceId) {
11643
13220
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -11654,10 +13231,10 @@ class AudioRecorder extends BasicRecorder {
11654
13231
  }
11655
13232
  downloadRecording() {
11656
13233
  if (this.displayRecFile) {
11657
- let ab = this.displayRecFile.audioBuffer;
13234
+ let ab = this.displayRecFile.audioDataHolder;
11658
13235
  let ww = new WavWriter();
11659
- if (ab) {
11660
- let wavFile = ww.writeAsync(ab, (wavFile) => {
13236
+ if (ab?.buffer) {
13237
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
11661
13238
  let blob = new Blob([wavFile], { type: 'audio/wav' });
11662
13239
  let rfUrl = URL.createObjectURL(blob);
11663
13240
  let dataDnlLnk = this.renderer.createElement('a');
@@ -11680,35 +13257,50 @@ class AudioRecorder extends BasicRecorder {
11680
13257
  set displayRecFile(displayRecFile) {
11681
13258
  this._displayRecFile = displayRecFile;
11682
13259
  if (this._displayRecFile) {
11683
- let ab = this._displayRecFile.audioBuffer;
11684
- if (ab) {
11685
- this.displayAudioClip = new AudioClip(ab);
13260
+ let adh = this._displayRecFile.audioDataHolder;
13261
+ if (adh) {
13262
+ this.displayAudioClip = new AudioClip(adh);
13263
+ console.debug(" set recording file: display audio clip set");
11686
13264
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
11687
13265
  }
11688
13266
  else {
11689
13267
  // clear for now ...
11690
13268
  this.displayAudioClip = null;
13269
+ console.debug("set recording file: display audio clip null");
11691
13270
  this.controlAudioPlayer.audioClip = null;
11692
13271
  if (this._controlAudioPlayer && this._session) {
11693
13272
  //... and try to fetch from server
11694
- this.audioFetchSubscription = this.recFileService.fetchAndApplyRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
11695
- let fab = null;
11696
- if (rf && this._displayRecFile) {
11697
- fab = this._displayRecFile.audioBuffer;
11698
- }
11699
- else {
11700
- this.statusMsg = 'Recording file could not be loaded.';
13273
+ this.audioFetching = true;
13274
+ let rf = this._displayRecFile;
13275
+ let clip = this.displayAudioClip;
13276
+ this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
13277
+ next: ab => {
13278
+ this.audioFetching = false;
13279
+ let fabDh = null;
13280
+ if (ab) {
13281
+ if (rf) {
13282
+ fabDh = new AudioDataHolder(ab);
13283
+ this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
13284
+ }
13285
+ }
13286
+ else {
13287
+ console.error('Recording file could not be loaded.');
13288
+ this.statusMsg = 'Recording file could not be loaded.';
13289
+ this.statusAlertType = 'error';
13290
+ }
13291
+ if (fabDh) {
13292
+ // this.displayAudioClip could have been changed meanwhile, but the recorder unsubcribes before changing the item. Therefore there should be no risk to set to wrong item
13293
+ this.displayAudioClip = new AudioClip(fabDh);
13294
+ //console.debug("set recording file: display audio clip from fetched audio buffer");
13295
+ }
13296
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
13297
+ this.showRecording();
13298
+ }, error: err => {
13299
+ console.error("Could not load recording file from server: " + err);
13300
+ this.audioFetching = false;
13301
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
11701
13302
  this.statusAlertType = 'error';
11702
13303
  }
11703
- if (fab) {
11704
- this.displayAudioClip = new AudioClip(fab);
11705
- }
11706
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
11707
- this.showRecording();
11708
- }, err => {
11709
- console.error("Could not load recording file from server: " + err);
11710
- this.statusMsg = 'Recording file could not be loaded: ' + err;
11711
- this.statusAlertType = 'error';
11712
13304
  });
11713
13305
  }
11714
13306
  else {
@@ -11718,6 +13310,7 @@ class AudioRecorder extends BasicRecorder {
11718
13310
  }
11719
13311
  }
11720
13312
  else {
13313
+ console.debug("recording file null");
11721
13314
  this.displayAudioClip = null;
11722
13315
  this.controlAudioPlayer.audioClip = null;
11723
13316
  }
@@ -11726,28 +13319,6 @@ class AudioRecorder extends BasicRecorder {
11726
13319
  get displayRecFile() {
11727
13320
  return this._displayRecFile;
11728
13321
  }
11729
- showRecording() {
11730
- this.controlAudioPlayer.stop();
11731
- if (this.displayAudioClip) {
11732
- this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
11733
- this.displayLevelInfos = levelInfos;
11734
- this.changeDetectorRef.detectChanges();
11735
- });
11736
- this.playStartAction.disabled = false;
11737
- }
11738
- else {
11739
- // TODO
11740
- // Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
11741
- // The level bar display does not clear, it shows the last captured stream
11742
- this.displayLevelInfos = null;
11743
- this.playStartAction.disabled = true;
11744
- // Collapse audio signal display if open
11745
- if (!this.audioSignalCollapsed) {
11746
- this.audioSignalCollapsed = true;
11747
- }
11748
- }
11749
- this.changeDetectorRef.detectChanges();
11750
- }
11751
13322
  updateStartActionDisableState() {
11752
13323
  this.transportActions.startAction.disabled = !(this.ac);
11753
13324
  }
@@ -11791,6 +13362,19 @@ class AudioRecorder extends BasicRecorder {
11791
13362
  super.started();
11792
13363
  this.statusAlertType = 'info';
11793
13364
  this.statusMsg = 'Recording...';
13365
+ if (!this.rfUuid) {
13366
+ this.rfUuid = UUID.generate();
13367
+ }
13368
+ let sessId = 0;
13369
+ if (this._session) {
13370
+ sessId = this._session.sessionId;
13371
+ }
13372
+ let rf = new RecordingFile(this.rfUuid, sessId, null);
13373
+ rf._startedAsDateObj = this.startedDate;
13374
+ if (rf._startedAsDateObj) {
13375
+ rf.startedDate = rf._startedAsDateObj.toString();
13376
+ }
13377
+ this._recordingFile = rf;
11794
13378
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
11795
13379
  this.maxRecTimerId = window.setTimeout(() => {
11796
13380
  this.stopRecordingMaxRec();
@@ -11827,68 +13411,75 @@ class AudioRecorder extends BasicRecorder {
11827
13411
  this.transportActions.pauseAction.disabled = true;
11828
13412
  this.statusAlertType = 'info';
11829
13413
  this.statusMsg = 'Recorded.';
11830
- let ad;
13414
+ let ad = null;
13415
+ let ada = null;
13416
+ let adh = null;
13417
+ let frameLen = 0;
11831
13418
  if (this.ac) {
11832
- ad = this.ac.audioBuffer();
13419
+ if (this.uploadChunkSizeSeconds || AudioRecorder.FORCE_ARRRAY_AUDIO_BUFFER) {
13420
+ ada = this.ac.audioBufferArray();
13421
+ frameLen = ada.frameLen;
13422
+ }
13423
+ else {
13424
+ ad = this.ac.audioBuffer();
13425
+ frameLen = ad.length;
13426
+ }
13427
+ adh = new AudioDataHolder(ad, ada);
11833
13428
  let sessId = 0;
11834
13429
  if (this._session) {
11835
13430
  sessId = this._session.sessionId;
11836
13431
  }
11837
- if (!this.rfUuid) {
11838
- this.rfUuid = UUID.generate();
11839
- }
11840
- let rf = new RecordingFile(this.rfUuid, sessId, ad);
11841
- rf._startedAsDateObj = this.startedDate;
11842
- if (rf._startedAsDateObj) {
11843
- rf.startedDate = rf._startedAsDateObj.toString();
11844
- }
11845
- rf.frames = ad.length;
11846
- this.displayRecFile = rf;
11847
- this.recorderCombiPane.push(rf);
11848
- // Upload if upload enabled and not in chunked upload mode
11849
- if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
11850
- let apiEndPoint = '';
11851
- if (this.config && this.config.apiEndPoint) {
11852
- apiEndPoint = this.config.apiEndPoint;
11853
- }
11854
- if (apiEndPoint !== '') {
11855
- apiEndPoint = apiEndPoint + '/';
13432
+ if (this._recordingFile) {
13433
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
13434
+ let rf = this._recordingFile;
13435
+ RecordingFileUtils.setAudioData(rf, adh);
13436
+ this.recorderCombiPane.addRecFile(rf);
13437
+ // Upload if upload enabled and not in chunked upload mode
13438
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds && rf != null && ad != null) {
13439
+ let apiEndPoint = '';
13440
+ if (this.config && this.config.apiEndPoint) {
13441
+ apiEndPoint = this.config.apiEndPoint;
13442
+ }
13443
+ if (apiEndPoint !== '') {
13444
+ apiEndPoint = apiEndPoint + '/';
13445
+ }
13446
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
13447
+ let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
13448
+ // convert asynchronously to 16-bit integer PCM
13449
+ // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
13450
+ // TODO duplicate conversion for manual download
13451
+ this.processingRecording = true;
13452
+ let ww = new WavWriter();
13453
+ ww.writeAsync(ad, (wavFile) => {
13454
+ this.postRecordingMultipart(wavFile, recUrl, rf);
13455
+ this.processingRecording = false;
13456
+ this.updateWakeLock();
13457
+ this.changeDetectorRef.detectChanges();
13458
+ });
11856
13459
  }
11857
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11858
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11859
- // convert asynchronously to 16-bit integer PCM
11860
- // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11861
- // TODO duplicate conversion for manual download
11862
- this.processingRecording = true;
11863
- let ww = new WavWriter();
11864
- ww.writeAsync(ad, (wavFile) => {
11865
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11866
- this.processingRecording = false;
11867
- this.updateWakeLock();
11868
- this.changeDetectorRef.detectChanges();
11869
- });
11870
13460
  }
11871
13461
  }
13462
+ this.displayRecFile = this._recordingFile;
11872
13463
  this.status = 1 /* IDLE */;
11873
13464
  this.navigationDisabled = false;
11874
13465
  this.updateNavigationActions();
11875
13466
  this.updateWakeLock();
11876
13467
  this.changeDetectorRef.detectChanges();
11877
13468
  }
11878
- postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
13469
+ postRecordingMultipart(wavFile, recUrl, rf) {
11879
13470
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11880
13471
  let fd = new FormData();
11881
- if (uuid) {
11882
- fd.set('uuid', uuid);
13472
+ if (rf.uuid) {
13473
+ fd.set('uuid', rf.uuid);
11883
13474
  }
11884
- if (sessionId !== null) {
11885
- fd.set('sessionId', sessionId.toString());
13475
+ if (rf.session !== null) {
13476
+ fd.set('sessionId', rf.session.toString());
11886
13477
  }
11887
- if (startedDate) {
11888
- fd.set('startedDate', startedDate.toJSON());
13478
+ if (rf._startedAsDateObj) {
13479
+ fd.set('startedDate', rf._startedAsDateObj.toJSON());
11889
13480
  }
11890
13481
  fd.set('audio', wavBlob);
11891
- let ul = new Upload(fd, recUrl);
13482
+ let ul = new Upload(fd, recUrl, rf);
11892
13483
  this.uploader.queueUpload(ul);
11893
13484
  }
11894
13485
  postChunkAudioBuffer(audioBuffer, chunkIdx) {
@@ -11904,8 +13495,9 @@ class AudioRecorder extends BasicRecorder {
11904
13495
  }
11905
13496
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11906
13497
  let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
13498
+ let rf = this._recordingFile;
11907
13499
  ww.writeAsync(audioBuffer, (wavFile) => {
11908
- this.postRecording(wavFile, recUrl);
13500
+ this.postRecording(wavFile, recUrl, rf);
11909
13501
  this.processingRecording = false;
11910
13502
  });
11911
13503
  }
@@ -11954,11 +13546,11 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
11954
13546
 
11955
13547
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
11956
13548
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
11957
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
11958
- [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
13549
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
13550
+ [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
11959
13551
  <div fxLayout="row">
11960
13552
  <spr-recordingitemcontrols fxFlex="10 0 1"
11961
- [audioLoaded]="displayAudioClip?.buffer!==null"
13553
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
11962
13554
  [playStartAction]="controlAudioPlayer?.startAction"
11963
13555
  [playStopAction]="controlAudioPlayer?.stopAction"
11964
13556
  [peakDbLvl]="peakLevelInDb"
@@ -11997,7 +13589,7 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
11997
13589
  [ready]="dataSaved && !isActive()"></app-readystateindicator>
11998
13590
  </div>
11999
13591
  </div>
12000
- `, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;text-align:center;align-content:center;align-items:center}\n", ".bigbutton{min-width:70px;min-height:50px;font-size:50px;border-radius:20px}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: RecorderCombiPane, selector: "app-recordercombipane", inputs: ["selectDisabled", "selectedRecordingFile", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["selectedRecordingFileChanged"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
13592
+ `, isInline: true, styles: [":host{flex:2;background:lightgrey;display:flex;flex-direction:column;margin:0;padding:0;height:100%;min-height:0px;overflow:hidden}\n", ".ricontrols{padding:4px;box-sizing:border-box;height:100%}\n", ".dark{background:darkgray}\n", ".controlpanel{align-content:center;align-items:center;margin:0;padding:20px;min-height:min-content}\n", ".startstop{width:100%;text-align:center;align-content:center;align-items:center}\n", ".bigbutton{min-width:70px;min-height:50px;font-size:50px;border-radius:20px}\n"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: RecorderCombiPane, selector: "app-recordercombipane", inputs: ["selectDisabled", "selectedRecordingFile", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["selectedRecordingFileChanged"] }, { type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos", "stateLoading"] }, { type: RecordingItemControls, selector: "spr-recordingitemcontrols", inputs: ["audioSignalCollapsed", "enableDownload", "peakDbLvl", "agc", "audioLoaded", "playStartAction", "playStopAction", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: UploadStatus, selector: "app-uploadstatus", inputs: ["value", "awaitNewUpload", "status"] }, { type: WakeLockIndicator, selector: "app-wakelockindicator", inputs: ["screenLocked"] }, { type: ReadyStateIndicator, selector: "app-readystateindicator", inputs: ["ready"] }, { type: StatusDisplay, selector: "app-sprstatusdisplay", inputs: ["statusAlertType", "statusMsg", "statusWaiting"] }, { type: i3$2.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i3$1.DefaultStyleDirective, selector: " [ngStyle], [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl], [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl], [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]", inputs: ["ngStyle", "ngStyle.xs", "ngStyle.sm", "ngStyle.md", "ngStyle.lg", "ngStyle.xl", "ngStyle.lt-sm", "ngStyle.lt-md", "ngStyle.lt-lg", "ngStyle.lt-xl", "ngStyle.gt-xs", "ngStyle.gt-sm", "ngStyle.gt-md", "ngStyle.gt-lg"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i8.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3$1.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
12001
13593
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioRecorder, decorators: [{
12002
13594
  type: Component,
12003
13595
  args: [{
@@ -12020,11 +13612,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
12020
13612
 
12021
13613
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
12022
13614
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
12023
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
12024
- [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
13615
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
13616
+ [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
12025
13617
  <div fxLayout="row">
12026
13618
  <spr-recordingitemcontrols fxFlex="10 0 1"
12027
- [audioLoaded]="displayAudioClip?.buffer!==null"
13619
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
12028
13620
  [playStartAction]="controlAudioPlayer?.startAction"
12029
13621
  [playStopAction]="controlAudioPlayer?.stopAction"
12030
13622
  [peakDbLvl]="peakLevelInDb"
@@ -12302,20 +13894,20 @@ class SpeechrecorderngModule {
12302
13894
  }
12303
13895
  SpeechrecorderngModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
12304
13896
  SpeechrecorderngModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
12305
- ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder], imports: [i1$1.RouterModule, FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule], exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder] });
12306
- SpeechrecorderngModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader], imports: [[RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule]] });
13897
+ ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder], imports: [i1$1.RouterModule, FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule], exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder] });
13898
+ SpeechrecorderngModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader], imports: [[RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule]] });
12307
13899
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, decorators: [{
12308
13900
  type: NgModule,
12309
13901
  args: [{
12310
13902
  declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
12311
13903
  ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder],
12312
13904
  exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder],
12313
- imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule],
13905
+ imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule],
12314
13906
  providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader]
12315
13907
  }]
12316
13908
  }] });
12317
13909
 
12318
- const VERSION = '2.25.2';
13910
+ const VERSION = '3.0.1';
12319
13911
 
12320
13912
  /*
12321
13913
  * Public API Surface of speechrecorderng