speechrecorderng 2.25.3 → 3.0.0

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 +5 -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 +15 -2
  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 +52 -7
  21. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +99 -7
  22. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +117 -101
  23. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +34 -6
  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 +182 -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 +118 -95
  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 +2321 -743
  39. package/fesm2015/speechrecorderng.mjs.map +1 -1
  40. package/fesm2020/speechrecorderng.mjs +2321 -753
  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 +4 -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 +3 -1
  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 +15 -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 +9 -3
  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 +30 -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,518 @@ 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
+ var _a;
495
+ if (msgEv.data) {
496
+ let evType = msgEv.data.eventType;
497
+ if (evType) {
498
+ if ('bufferNotification' === evType) {
499
+ this.filledFrames = msgEv.data.filledFrames;
500
+ //console.debug("Buffer notification: filled frames: " + this.filledFrames);
501
+ this.fillBuffer();
502
+ }
503
+ else if ('ended' === evType) {
504
+ let drainTime = 0;
505
+ if ((_a = this._arrayAudioBuffer) === null || _a === void 0 ? void 0 : _a.sampleRate) {
506
+ drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._arrayAudioBuffer.sampleRate;
507
+ }
508
+ //let dstAny:any=this.context.destination;
509
+ //console.debug('Destination node tail-time: '+dstAny['tail-time']);
510
+ window.setTimeout(() => {
511
+ var _a;
512
+ (_a = this.onended) === null || _a === void 0 ? void 0 : _a.call(this);
513
+ }, drainTime * 1000);
514
+ }
515
+ }
516
+ }
517
+ };
518
+ }
519
+ fillBuffer(frameOffset) {
520
+ if (this._arrayAudioBuffer && this._audioInputStream && this._aisBufs) {
521
+ let filled = this.filledFrames;
522
+ let bufLen = 0;
523
+ while (filled < this.bufferFillFrames) {
524
+ let read = this._audioInputStream.read(this._aisBufs);
525
+ //console.log("ArrayAudioBufferSourceNode: read: "+read)
526
+ if (read) {
527
+ let trBuffers = new Array(this.channelCount);
528
+ for (let ch = 0; ch < this.channelCount; ch++) {
529
+ let adCh = this._aisBufs[ch];
530
+ let adChCopy = new Float32Array(adCh.length);
531
+ bufLen = adChCopy.length;
532
+ adChCopy.set(adCh);
533
+ trBuffers[ch] = adChCopy.buffer;
534
+ }
535
+ this.port.postMessage({
536
+ cmd: 'data',
537
+ chs: this.channelCount,
538
+ audioData: trBuffers
539
+ }, trBuffers);
540
+ filled += read;
541
+ }
542
+ else {
543
+ break;
544
+ }
545
+ }
546
+ }
547
+ }
548
+ get arrayAudioBuffer() {
549
+ return this._arrayAudioBuffer;
550
+ }
551
+ set arrayAudioBuffer(value) {
552
+ var _a, _b;
553
+ this._arrayAudioBuffer = value;
554
+ if ((_a = this._arrayAudioBuffer) === null || _a === void 0 ? void 0 : _a.channelCount) {
555
+ this.channelCount = (_b = this._arrayAudioBuffer) === null || _b === void 0 ? void 0 : _b.channelCount;
556
+ this.bufferFillFrames = this._arrayAudioBuffer.sampleRate * this._bufferFillSeconds;
557
+ }
558
+ }
559
+ get bufferFillSeconds() {
560
+ return this._bufferFillSeconds;
561
+ }
562
+ set bufferFillSeconds(value) {
563
+ this._bufferFillSeconds = value;
564
+ }
565
+ start(when, offset, duration) {
566
+ if (when) {
567
+ throw Error("when, offest,duration parameters currently not supported by ArrayAudioBufferSourceNode class");
568
+ }
569
+ if (this._arrayAudioBuffer) {
570
+ let arrAis = new ArrayAudioBufferInputStream(this._arrayAudioBuffer);
571
+ if (offset === undefined && duration === undefined) {
572
+ this._audioInputStream = arrAis;
573
+ }
574
+ else {
575
+ let offsetFrames = 0;
576
+ let durationFrames = undefined;
577
+ if (offset !== undefined) {
578
+ offsetFrames = Math.floor(offset * this._arrayAudioBuffer.sampleRate);
579
+ }
580
+ if (duration !== undefined) {
581
+ durationFrames = Math.floor(duration * this._arrayAudioBuffer.sampleRate);
582
+ }
583
+ this._audioInputStream = new EditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
584
+ }
585
+ let chs = this._arrayAudioBuffer.channelCount;
586
+ this._aisBufs = new Array(chs);
587
+ for (let ch = 0; ch < chs; ch++) {
588
+ this._aisBufs[ch] = new Float32Array(1024);
589
+ }
590
+ this.fillBuffer();
591
+ this.port.postMessage({ cmd: 'start' });
592
+ }
593
+ }
594
+ stop() {
595
+ var _a;
596
+ this.port.postMessage({ cmd: 'stop' });
597
+ (_a = this.onended) === null || _a === void 0 ? void 0 : _a.call(this);
598
+ }
599
+ }
600
+ ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN = 128;
601
+ ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS = 10;
602
+ class AudioSourceWorkletModuleLoader {
603
+ static loadModule(context) {
604
+ return new Promise((resolve, reject) => {
605
+ if (AudioSourceWorkletModuleLoader.moduleLoaded) {
606
+ resolve.call(self);
607
+ }
608
+ else {
609
+ let audioWorkletModuleBlob = new Blob([aswpStr], { type: 'text/javascript' });
610
+ let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
611
+ context.resume();
612
+ context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
613
+ AudioSourceWorkletModuleLoader.moduleLoaded = true;
614
+ resolve.call(self);
615
+ }).catch((reason) => {
616
+ console.error(reason);
617
+ reject.call(reason);
618
+ });
619
+ }
620
+ });
621
+ }
622
+ }
623
+ AudioSourceWorkletModuleLoader.moduleLoaded = false;
624
+
112
625
  var EventType;
113
626
  (function (EventType) {
114
627
  EventType[EventType["CLOSED"] = 0] = "CLOSED";
@@ -135,10 +648,17 @@ class AudioPlayer {
135
648
  this.running = false;
136
649
  this._audioClip = null;
137
650
  this._audioBuffer = null;
651
+ this._arrayAudioBuffer = null;
138
652
  this.sourceBufferNode = null;
653
+ this.sourceAudioWorkletNode = null;
139
654
  this.playStartTime = null;
140
655
  this.timerVar = null;
141
656
  this.context = context;
657
+ // AudioSourceWorkletModuleLoader.loadModule(this.context).then(()=>{
658
+ // console.debug("Audio source worklet module loaded.");
659
+ // }).catch((error: any)=>{
660
+ // console.error('Could not add module '+error);
661
+ // });
142
662
  this.listener = listener;
143
663
  this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
144
664
  this.n = navigator;
@@ -169,17 +689,23 @@ class AudioPlayer {
169
689
  return this._stopAction;
170
690
  }
171
691
  set audioClip(audioClip) {
172
- var length = 0;
173
- var chs = 0;
174
- if (audioClip && audioClip.buffer) {
175
- chs = audioClip.buffer.numberOfChannels;
692
+ let length = 0;
693
+ let chs = 0;
694
+ if (audioClip && audioClip.audioDataHolder) {
695
+ let audioDataHolder = audioClip.audioDataHolder;
696
+ chs = audioDataHolder.numberOfChannels;
176
697
  if (chs > 0) {
177
- length = audioClip.buffer.length;
698
+ length = audioDataHolder.frameLen;
178
699
  if (chs > this.context.destination.maxChannelCount) {
179
700
  // TODO exception
180
701
  }
181
702
  }
182
- this.audioBuffer = audioClip.buffer;
703
+ if (audioDataHolder.buffer) {
704
+ this.audioBuffer = audioDataHolder.buffer;
705
+ }
706
+ if (audioDataHolder.arrayBuffer) {
707
+ this.arrayAudioBuffer = audioDataHolder.arrayBuffer;
708
+ }
183
709
  audioClip.addSelectionObserver((ac) => {
184
710
  this._startSelectionAction.disabled = this.startSelectionDisabled();
185
711
  if (!this.startSelectionAction.disabled && this._autoPlayOnSelectToggleAction.value) {
@@ -189,12 +715,14 @@ class AudioPlayer {
189
715
  }
190
716
  else {
191
717
  this.audioBuffer = null;
718
+ //this.arrayAudioBuffer=null;
192
719
  }
193
720
  this._audioClip = audioClip;
194
721
  }
195
722
  set audioBuffer(audioBuffer) {
196
723
  this.stop();
197
724
  this._audioBuffer = audioBuffer;
725
+ this._arrayAudioBuffer = null;
198
726
  if (audioBuffer && this.context) {
199
727
  this._startAction.disabled = false;
200
728
  this._startSelectionAction.disabled = this.startSelectionDisabled();
@@ -213,23 +741,86 @@ class AudioPlayer {
213
741
  get audioBuffer() {
214
742
  return this._audioBuffer;
215
743
  }
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;
744
+ set arrayAudioBuffer(arrayAudioBuffer) {
745
+ this.stop();
746
+ this._audioBuffer = null;
747
+ this._arrayAudioBuffer = arrayAudioBuffer;
748
+ if (arrayAudioBuffer && this.context) {
749
+ AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
750
+ this._startAction.disabled = false;
751
+ this._startSelectionAction.disabled = this.startSelectionDisabled();
752
+ if (this.listener) {
753
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
754
+ }
755
+ }).catch((error) => {
756
+ this._startAction.disabled = true;
757
+ this._startSelectionAction.disabled = true;
758
+ if (this.listener) {
759
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
760
+ }
761
+ console.error('Could not add module ' + error);
762
+ });
763
+ }
764
+ else {
227
765
  this._startAction.disabled = true;
228
766
  this._startSelectionAction.disabled = true;
229
- this._stopAction.disabled = false;
230
- //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
231
767
  if (this.listener) {
232
- this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
768
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
769
+ }
770
+ }
771
+ }
772
+ get arrayAudioBuffer() {
773
+ return this._arrayAudioBuffer;
774
+ }
775
+ start() {
776
+ if (!this._startAction.disabled && !this.running) {
777
+ this.context.resume();
778
+ if (this._audioBuffer) {
779
+ this.sourceBufferNode = this.context.createBufferSource();
780
+ this.sourceBufferNode.buffer = this._audioBuffer;
781
+ this.sourceBufferNode.connect(this.context.destination);
782
+ this.sourceBufferNode.onended = () => this.onended();
783
+ this.playStartTime = this.context.currentTime;
784
+ this.running = true;
785
+ this.sourceBufferNode.start();
786
+ this.playStartTime = this.context.currentTime;
787
+ this._startAction.disabled = true;
788
+ this._startSelectionAction.disabled = true;
789
+ this._stopAction.disabled = false;
790
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
791
+ if (this.listener) {
792
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
793
+ }
794
+ }
795
+ else if (this._arrayAudioBuffer) {
796
+ if (this._arrayAudioBuffer) {
797
+ this.sourceAudioWorkletNode = new ArrayAudioBufferSourceNode(this.context);
798
+ this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
799
+ let msg = 'Unknwon error';
800
+ if (ev instanceof ErrorEvent) {
801
+ msg = ev.message;
802
+ }
803
+ console.error("Audio source worklet error: " + msg);
804
+ if (this.listener) {
805
+ // TODO
806
+ // this.listener.error(msg);
807
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
808
+ }
809
+ };
810
+ this.sourceAudioWorkletNode.arrayAudioBuffer = this._arrayAudioBuffer;
811
+ this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
812
+ this.sourceAudioWorkletNode.onended = () => this.onended();
813
+ this.playStartTime = this.context.currentTime;
814
+ this.running = true;
815
+ this.sourceAudioWorkletNode.start();
816
+ this.playStartTime = this.context.currentTime;
817
+ this._startAction.disabled = true;
818
+ this._startSelectionAction.disabled = true;
819
+ this._stopAction.disabled = false;
820
+ if (this.listener) {
821
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
822
+ }
823
+ }
233
824
  }
234
825
  }
235
826
  }
@@ -239,34 +830,79 @@ class AudioPlayer {
239
830
  startSelected() {
240
831
  if (!this._startAction.disabled && !this.running) {
241
832
  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));
833
+ if (this._audioBuffer) {
834
+ this.sourceBufferNode = this.context.createBufferSource();
835
+ this.sourceBufferNode.buffer = this._audioBuffer;
836
+ this.sourceBufferNode.connect(this.context.destination);
837
+ this.sourceBufferNode.onended = () => this.onended();
838
+ this.playStartTime = this.context.currentTime;
839
+ this.running = true;
840
+ // unfortunately Web Audio API uses time values not frames
841
+ let ac = this._audioClip;
842
+ let offset = 0;
843
+ if (ac && ac.selection) {
844
+ let s = ac.selection;
845
+ let sr = ac.audioDataHolder.sampleRate;
846
+ offset = s.leftFrame / sr;
847
+ let stopPosInsecs = s.rightFrame / sr;
848
+ let dur = stopPosInsecs - offset;
849
+ // TODO check valid values
850
+ this.sourceBufferNode.start(0, offset, dur);
851
+ }
852
+ else {
853
+ this.sourceBufferNode.start();
854
+ }
855
+ this.playStartTime = this.context.currentTime - offset;
856
+ this._startAction.disabled = true;
857
+ this._startSelectionAction.disabled = true;
858
+ this._stopAction.disabled = false;
859
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
860
+ if (this.listener) {
861
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
862
+ }
863
+ }
864
+ else if (this._arrayAudioBuffer) {
865
+ if (this._arrayAudioBuffer) {
866
+ let aabsn = new ArrayAudioBufferSourceNode(this.context);
867
+ this.sourceAudioWorkletNode = aabsn;
868
+ aabsn.onprocessorerror = (ev) => {
869
+ let msg = 'Unknwon error';
870
+ if (ev instanceof ErrorEvent) {
871
+ msg = ev.message;
872
+ }
873
+ console.error("Audio source worklet error: " + msg);
874
+ if (this.listener) {
875
+ // TODO
876
+ // this.listener.error(msg);
877
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
878
+ }
879
+ };
880
+ aabsn.arrayAudioBuffer = this._arrayAudioBuffer;
881
+ aabsn.connect(this.context.destination); // this already starts playing
882
+ aabsn.onended = () => this.onended();
883
+ this.playStartTime = this.context.currentTime;
884
+ this.running = true;
885
+ let ac = this._audioClip;
886
+ let offset = 0;
887
+ if (ac && ac.selection) {
888
+ let s = ac.selection;
889
+ let sr = ac.audioDataHolder.sampleRate;
890
+ offset = s.leftFrame / sr;
891
+ let stopPosInsecs = s.rightFrame / sr;
892
+ let dur = stopPosInsecs - offset;
893
+ aabsn.start(0, offset, dur);
894
+ }
895
+ else {
896
+ aabsn.start();
897
+ }
898
+ this.playStartTime = this.context.currentTime - offset;
899
+ this._startAction.disabled = true;
900
+ this._startSelectionAction.disabled = true;
901
+ this._stopAction.disabled = false;
902
+ if (this.listener) {
903
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
904
+ }
905
+ }
270
906
  }
271
907
  }
272
908
  }
@@ -275,6 +911,9 @@ class AudioPlayer {
275
911
  if (this.sourceBufferNode) {
276
912
  this.sourceBufferNode.stop();
277
913
  }
914
+ if (this.sourceAudioWorkletNode) {
915
+ this.sourceAudioWorkletNode.stop();
916
+ }
278
917
  if (this.timerVar !== null) {
279
918
  window.clearInterval(this.timerVar);
280
919
  }
@@ -288,7 +927,7 @@ class AudioPlayer {
288
927
  if (this.timerVar != null) {
289
928
  window.clearInterval(this.timerVar);
290
929
  }
291
- this._startAction.disabled = !(this.audioBuffer);
930
+ this._startAction.disabled = !(this.audioBuffer || this.arrayAudioBuffer);
292
931
  this._startSelectionAction.disabled = this.startSelectionDisabled();
293
932
  this._stopAction.disabled = true;
294
933
  this.running = false;
@@ -305,10 +944,17 @@ class AudioPlayer {
305
944
  }
306
945
  get playPositionFrames() {
307
946
  let ppFrs = null;
947
+ let sr = null;
308
948
  if (this._audioBuffer) {
949
+ sr = this._audioBuffer.sampleRate;
950
+ }
951
+ else if (this._arrayAudioBuffer) {
952
+ sr = this._arrayAudioBuffer.sampleRate;
953
+ }
954
+ if (sr) {
309
955
  let ppTime = this.playPositionTime;
310
956
  if (ppTime !== null) {
311
- ppFrs = this._audioBuffer.sampleRate * ppTime;
957
+ ppFrs = sr * ppTime;
312
958
  }
313
959
  }
314
960
  return ppFrs;
@@ -522,6 +1168,107 @@ class ProjectUtil {
522
1168
  }
523
1169
  ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
524
1170
 
1171
+ class ArrayAudioBuffer {
1172
+ constructor(_channelCount, _sampleRate, _data) {
1173
+ this._channelCount = _channelCount;
1174
+ this._sampleRate = _sampleRate;
1175
+ this._data = _data;
1176
+ this._chunkCount = 0;
1177
+ this._frameLen = 0;
1178
+ if (this._data.length > 0) {
1179
+ let ch0Data = this.data[0];
1180
+ for (let ch0Chk of ch0Data) {
1181
+ this._chunkCount++;
1182
+ this._frameLen += ch0Chk.length;
1183
+ }
1184
+ }
1185
+ }
1186
+ static fromAudioBuffer(audioBuffer, chunkFrameSize = 8192) {
1187
+ let aab;
1188
+ let chs = audioBuffer.numberOfChannels;
1189
+ let frameLength = audioBuffer.length;
1190
+ //let chunksSize=Math.ceil(frameLength/chunkFrameSize);
1191
+ let framePos = 0;
1192
+ let data = new Array(chs);
1193
+ for (let ch = 0; ch < chs; ch++) {
1194
+ data[ch] = new Array();
1195
+ }
1196
+ let toCopy = frameLength - framePos;
1197
+ while (toCopy > 0) {
1198
+ let toCopyChunk = chunkFrameSize;
1199
+ if (toCopyChunk > toCopy) {
1200
+ // last chunk, the rest
1201
+ toCopyChunk = toCopy;
1202
+ }
1203
+ for (let ch = 0; ch < chs; ch++) {
1204
+ data[ch].push(audioBuffer.getChannelData(ch).slice(framePos, framePos + toCopyChunk));
1205
+ }
1206
+ framePos += toCopyChunk;
1207
+ toCopy -= toCopyChunk;
1208
+ }
1209
+ aab = new ArrayAudioBuffer(chs, audioBuffer.sampleRate, data);
1210
+ return aab;
1211
+ }
1212
+ get channelCount() {
1213
+ return this._channelCount;
1214
+ }
1215
+ frames(framePos, frameLen, bufs) {
1216
+ let ccFramePos = 0;
1217
+ let trgFramePos = framePos;
1218
+ let ch0Data = this.data[0];
1219
+ let cPos = 0;
1220
+ let filled = 0;
1221
+ let ci = 0;
1222
+ while (filled < frameLen && ci < this._chunkCount) {
1223
+ // Current chunk
1224
+ let cc0 = ch0Data[ci];
1225
+ let ccLen = cc0.length;
1226
+ let ccFrameEndPos = ccFramePos + ccLen;
1227
+ if (trgFramePos >= ccFramePos && trgFramePos < ccFrameEndPos) {
1228
+ let toCp = frameLen - filled;
1229
+ cPos = trgFramePos - ccFramePos;
1230
+ if (cPos + toCp > ccLen) {
1231
+ toCp = ccLen - cPos;
1232
+ }
1233
+ for (let ch = 0; ch < bufs.length; ch++) {
1234
+ let cc = this.data[ch][ci];
1235
+ for (let i = 0; i < toCp; i++) {
1236
+ bufs[ch][filled + i] = cc[cPos + i];
1237
+ }
1238
+ }
1239
+ filled += toCp;
1240
+ trgFramePos += toCp;
1241
+ cPos += toCp;
1242
+ ccFramePos += toCp;
1243
+ if (cPos >= ccLen) {
1244
+ ccFramePos = ccFrameEndPos;
1245
+ cPos = 0;
1246
+ ci++;
1247
+ }
1248
+ }
1249
+ else {
1250
+ // next chunk
1251
+ ccFramePos = ccFrameEndPos;
1252
+ cPos = 0;
1253
+ ci++;
1254
+ }
1255
+ }
1256
+ return filled;
1257
+ }
1258
+ get sampleRate() {
1259
+ return this._sampleRate;
1260
+ }
1261
+ get frameLen() {
1262
+ return this._frameLen;
1263
+ }
1264
+ get chunkCount() {
1265
+ return this._chunkCount;
1266
+ }
1267
+ get data() {
1268
+ return this._data;
1269
+ }
1270
+ }
1271
+
525
1272
  const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
526
1273
  const DEBUG_TRACE_LEVEL = 0;
527
1274
  const ENABLE_AUDIO_WORKLET = true;
@@ -1092,6 +1839,10 @@ class AudioCapture {
1092
1839
  }
1093
1840
  return ab;
1094
1841
  }
1842
+ audioBufferArray() {
1843
+ let aba = new ArrayAudioBuffer(this.channelCount, this.currentSampleRate, this.data);
1844
+ return aba;
1845
+ }
1095
1846
  }
1096
1847
  AudioCapture.BUFFER_SIZE = 8192;
1097
1848
 
@@ -1471,21 +2222,28 @@ class RecordingFileDescriptorImpl {
1471
2222
  constructor() { }
1472
2223
  }
1473
2224
  class RecordingFile {
1474
- constructor(uuid, sessionId, audioBuffer) {
2225
+ constructor(uuid, sessionId, audioDataHolder) {
1475
2226
  this.recordingFileId = null;
1476
2227
  this.uuid = null;
2228
+ this.serverPersisted = false;
2229
+ this.keepAudioDataCache = false;
1477
2230
  this.date = null;
1478
2231
  this._dateAsDateObj = null;
1479
2232
  this.startedDate = null;
1480
2233
  this._startedAsDateObj = null;
1481
- this.audioBuffer = null;
2234
+ this.audioDataHolder = null;
1482
2235
  this.session = null;
1483
2236
  this.frames = null;
2237
+ this.timeLength = null;
1484
2238
  this.editSampleRate = null;
1485
2239
  this.editStartFrame = null;
1486
2240
  this.editEndFrame = null;
1487
2241
  this.session = sessionId;
1488
- this.audioBuffer = audioBuffer;
2242
+ this.audioDataHolder = audioDataHolder;
2243
+ if (audioDataHolder) {
2244
+ this.frames = audioDataHolder.frameLen;
2245
+ this.timeLength = audioDataHolder.duration;
2246
+ }
1489
2247
  this.uuid = uuid;
1490
2248
  }
1491
2249
  filenameString() {
@@ -1497,14 +2255,27 @@ class RecordingFile {
1497
2255
  fns += this.uuid;
1498
2256
  return fns;
1499
2257
  }
2258
+ equals(otherRecordingFile) {
2259
+ if (otherRecordingFile !== null) {
2260
+ if (otherRecordingFile === this) {
2261
+ return true;
2262
+ }
2263
+ if (otherRecordingFile.uuid === this.uuid) {
2264
+ return true;
2265
+ }
2266
+ }
2267
+ return false;
2268
+ }
2269
+ toString() {
2270
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session;
2271
+ }
1500
2272
  }
1501
2273
  class SprRecordingFile extends RecordingFile {
1502
- constructor(sessionId, itemcode, version, audioBuffer) {
1503
- super(UUID.generate(), sessionId, audioBuffer);
2274
+ constructor(sessionId, itemcode, version, audioDataHolder) {
2275
+ super(UUID.generate(), sessionId, audioDataHolder);
1504
2276
  this.session = sessionId;
1505
2277
  this.itemCode = itemcode;
1506
2278
  this.version = version;
1507
- this.audioBuffer = audioBuffer;
1508
2279
  }
1509
2280
  filenameString() {
1510
2281
  let fns = '';
@@ -1519,6 +2290,31 @@ class SprRecordingFile extends RecordingFile {
1519
2290
  fns += this.uuid;
1520
2291
  return fns;
1521
2292
  }
2293
+ toString() {
2294
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session + ', itemcode: ' + this.itemCode + ', version: ' + this.version + ', UUID: ' + this.uuid;
2295
+ }
2296
+ }
2297
+ class RecordingFileUtils {
2298
+ static setAudioData(rf, audioDataHolder) {
2299
+ rf.audioDataHolder = audioDataHolder;
2300
+ if (audioDataHolder) {
2301
+ rf.frames = audioDataHolder.frameLen;
2302
+ rf.timeLength = audioDataHolder.duration;
2303
+ }
2304
+ }
2305
+ static sampleCount(rf) {
2306
+ if (rf.audioDataHolder) {
2307
+ return rf.audioDataHolder.sampleCounts();
2308
+ }
2309
+ else {
2310
+ return 0;
2311
+ }
2312
+ }
2313
+ static expireAudioData(rf) {
2314
+ let rv = rf.audioDataHolder;
2315
+ rf.audioDataHolder = null;
2316
+ return rv;
2317
+ }
1522
2318
  }
1523
2319
 
1524
2320
  // state of an upload
@@ -1569,7 +2365,8 @@ class UploaderStatusChangeEvent {
1569
2365
  }
1570
2366
  }
1571
2367
  class Upload {
1572
- constructor(blob, url) {
2368
+ constructor(blob, url, serverPersistable = null) {
2369
+ this.serverPersistable = serverPersistable;
1573
2370
  this.toString = () => {
1574
2371
  let s = `Upload: Status: ${this.status}, URL: ${this._url}`;
1575
2372
  if (this._data instanceof Blob) {
@@ -1590,6 +2387,17 @@ class Upload {
1590
2387
  get data() {
1591
2388
  return this._data;
1592
2389
  }
2390
+ done() {
2391
+ this.status = UploadStatus$1.DONE;
2392
+ //console.debug("Single upload done.");
2393
+ if (this.serverPersistable) {
2394
+ this.serverPersistable.serverPersisted = true;
2395
+ //console.debug("Single upload set server persisted: "+this.serverPersistable);
2396
+ }
2397
+ else {
2398
+ //console.debug("Server persistable not set.");
2399
+ }
2400
+ }
1593
2401
  }
1594
2402
  class Uploader {
1595
2403
  constructor(http, withCredentials = false) {
@@ -1628,7 +2436,7 @@ class Uploader {
1628
2436
  return si;
1629
2437
  }
1630
2438
  uploadDone(ul) {
1631
- ul.status = UploadStatus$1.DONE;
2439
+ ul.done();
1632
2440
  // remove upload from queue
1633
2441
  for (let i = 0; i < this.que.length; i++) {
1634
2442
  if (this.que[i] === ul) {
@@ -2063,16 +2871,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2063
2871
  }], ctorParameters: function () { return []; } });
2064
2872
 
2065
2873
  class AudioClip {
2066
- constructor(buffer) {
2874
+ constructor(_audioDataHolder) {
2875
+ this._audioDataHolder = _audioDataHolder;
2067
2876
  this._selection = null;
2068
2877
  this._levelInfos = null;
2069
2878
  this.selectionObservers = new Array();
2070
- this._buffer = buffer;
2071
2879
  }
2072
- get buffer() {
2073
- return this._buffer;
2880
+ get audioDataHolder() {
2881
+ return this._audioDataHolder;
2074
2882
  }
2075
- ;
2076
2883
  get selection() {
2077
2884
  return this._selection;
2078
2885
  }
@@ -2175,19 +2982,32 @@ class ViewSelection {
2175
2982
  class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2176
2983
  constructor() {
2177
2984
  super(...arguments);
2178
- this._audioData = null;
2985
+ //protected _audioData: AudioBuffer|null=null;
2986
+ //protected _arrayAudioData: ArrayAudioBuffer|null=null;
2987
+ this._audioDataHolder = null;
2179
2988
  this._bgColor = 'white';
2180
2989
  this._selectColor = 'rgba(0%,0%,100%,25%)';
2181
2990
  }
2991
+ frameLength() {
2992
+ let frameLength = null;
2993
+ // if (this._audioData && this._audioData.numberOfChannels > 0) {
2994
+ // let ch0 = this._audioData.getChannelData(0);
2995
+ // frameLength = ch0.length;
2996
+ //
2997
+ // }else if(this._arrayAudioData){
2998
+ // frameLength=this._arrayAudioData.frameLen;
2999
+ // }
3000
+ return frameLength;
3001
+ }
2182
3002
  /**
2183
3003
  * Returns pixel position depending on current zoom setting.
2184
3004
  * @param framePos audio frame (sample) position
2185
3005
  */
2186
3006
  frameToXPixelPosition(framePos) {
3007
+ var _a;
2187
3008
  let pixelPos = null;
2188
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2189
- let ch0 = this._audioData.getChannelData(0);
2190
- let frameLength = ch0.length;
3009
+ let frameLength = (_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.frameLen;
3010
+ if (frameLength !== undefined) {
2191
3011
  let vw;
2192
3012
  if (this.bounds) {
2193
3013
  vw = this.bounds.dimension.width;
@@ -2216,10 +3036,10 @@ class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2216
3036
  }
2217
3037
  }
2218
3038
  viewPortXPixelToFramePosition(xViewPortPixelPos) {
3039
+ var _a;
2219
3040
  let vpXramePos = null;
2220
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2221
- let ch0 = this._audioData.getChannelData(0);
2222
- let frameLength = ch0.length;
3041
+ let frameLength = (_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.frameLen;
3042
+ if (frameLength !== undefined) {
2223
3043
  let vw;
2224
3044
  if (this.bounds) {
2225
3045
  vw = this.bounds.dimension.width;
@@ -2396,8 +3216,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2396
3216
  if (viewSel) {
2397
3217
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2398
3218
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2399
- if (this._audioData && frameStart != null && frameEnd != null) {
2400
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3219
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3220
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2401
3221
  }
2402
3222
  }
2403
3223
  this.selectingEventEmitter.emit(ns);
@@ -2407,8 +3227,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2407
3227
  if (viewSel) {
2408
3228
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2409
3229
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2410
- if (this._audioData && frameStart != null && frameEnd != null) {
2411
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3230
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3231
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2412
3232
  }
2413
3233
  }
2414
3234
  this.selectedEventEmitter.emit(ns);
@@ -2489,7 +3309,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2489
3309
  g.lineTo(xViewPortPixelpos, h);
2490
3310
  g.closePath();
2491
3311
  g.stroke();
2492
- if (this._audioData) {
3312
+ if (this._audioDataHolder) {
2493
3313
  g.font = '14px sans-serif';
2494
3314
  g.fillStyle = 'yellow';
2495
3315
  g.fillText(framePos.toString(), xViewPortPixelpos + 2, 50);
@@ -2501,6 +3321,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2501
3321
  }
2502
3322
  }
2503
3323
  }
3324
+ AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD = 10 * 60 * 48000; // Use streaming/chunking if audio clip has more than this number of samples
2504
3325
  AudioCanvasLayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2505
3326
  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 });
2506
3327
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, decorators: [{
@@ -2535,7 +3356,7 @@ class AudioSignal extends AudioCanvasLayerComponent {
2535
3356
  this._playFramePosition = null;
2536
3357
  this.worker = null;
2537
3358
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
2538
- this._audioData = null;
3359
+ this._audioDataHolder = null;
2539
3360
  this._bgColor = 'black';
2540
3361
  this._selectColor = 'rgba(255,255,0,0.8)';
2541
3362
  }
@@ -2588,25 +3409,26 @@ class AudioSignal extends AudioCanvasLayerComponent {
2588
3409
  */
2589
3410
  workerFunction() {
2590
3411
  addEventListener('message', ({ data }) => {
2591
- let audioData = data.audioData;
2592
- let l = data.l;
2593
- let w = data.w;
2594
- let h = data.h;
2595
- let vw = data.vw;
2596
- let chs = data.chs;
2597
- let frameLength = data.frameLength;
3412
+ let audioData = data.audioData; // audio data part required to render view port
3413
+ let auOffset = data.audioDataOffset;
3414
+ let l = data.l; // left pixel position of view port
3415
+ let w = data.w; // width of viewport
3416
+ let vw = data.vw; // total width of (virtual) audio view (not viewport width)
3417
+ let chs = data.chs; // number of channels
3418
+ let dataFrameLength = data.audioDataFrameLength; // frame length of audio data part required for view port
3419
+ let frameLength = data.frameLength; // total frame length (of audio clip)
3420
+ //console.debug("W: left: "+l+", w:"+w+", vw: "+vw+", chs: "+chs+", frameLength: "+frameLength);
2598
3421
  let psMinMax = new Float32Array(0);
2599
- if (audioData && w >= 0 && vw > 0) {
3422
+ if (audioData && audioData.length > 0 && w >= 0 && vw > 0) {
2600
3423
  let framesPerPixel = frameLength / vw;
2601
- let y = 0;
2602
3424
  let pointsLen = w * chs;
2603
3425
  // one for min one for max
2604
3426
  let arrLen = pointsLen * 2;
2605
3427
  psMinMax = new Float32Array(arrLen);
2606
3428
  let chFramePos = 0;
3429
+ let chFrameLength = audioData.length / chs;
2607
3430
  for (let ch = 0; ch < chs; ch++) {
2608
- let x = 0;
2609
- chFramePos = ch * frameLength;
3431
+ chFramePos = ch * chFrameLength;
2610
3432
  for (let pii = 0; pii < w; pii++) {
2611
3433
  let virtPii = l + pii;
2612
3434
  let pMin = Infinity;
@@ -2616,24 +3438,28 @@ class AudioSignal extends AudioCanvasLayerComponent {
2616
3438
  for (let ai = 0; ai < framesPerPixel; ai++) {
2617
3439
  let framePos = pixelFramePos + ai;
2618
3440
  let a = 0;
2619
- if (framePos >= 0 && framePos < audioData.length) {
2620
- a = audioData[framePos];
2621
- }
2622
- if (a < pMin) {
2623
- pMin = a;
2624
- }
2625
- if (a > pMax) {
2626
- pMax = a;
3441
+ let bufPos = framePos - auOffset;
3442
+ //let bufPos=framePos;
3443
+ if (bufPos >= 0 && bufPos < audioData.length) {
3444
+ a = audioData[bufPos];
3445
+ //console.debug("W: ch: "+ch+", pixelFramePos: "+pixelFramePos+", framePos: "+framePos+", auOffset: "+auOffset+", bufPos: "+bufPos+", audioData.length: "+audioData.length+", a: "+a);
3446
+ if (a < pMin) {
3447
+ pMin = a;
3448
+ }
3449
+ if (a > pMax) {
3450
+ pMax = a;
3451
+ }
2627
3452
  }
2628
3453
  }
2629
3454
  let psMinPos = ch * w + pii;
2630
3455
  psMinMax[psMinPos] = pMin;
2631
3456
  let psMaxPos = pointsLen + psMinPos;
2632
3457
  psMinMax[psMaxPos] = pMax;
3458
+ //console.debug("psMinMax["+psMinPos+"]="+pMin+",psMinMax["+psMaxPos+"]="+pMax);
2633
3459
  }
2634
3460
  }
2635
3461
  }
2636
- postMessage({ psMinMax: psMinMax, l: data.l, t: data.t, w: data.w, h: data.h, chs: data.chs }, [psMinMax.buffer]);
3462
+ postMessage({ psMinMax: psMinMax, l: data.l, w: data.w, chs: data.chs, eod: data.eod }, [psMinMax.buffer]);
2637
3463
  });
2638
3464
  }
2639
3465
  startDraw(clear = true) {
@@ -2661,41 +3487,143 @@ class AudioSignal extends AudioCanvasLayerComponent {
2661
3487
  if (this.bounds && this.bounds.dimension) {
2662
3488
  let w = Math.round(this.bounds.dimension.width);
2663
3489
  let h = Math.round(this.bounds.dimension.height);
2664
- if (this._audioData && w > 0 && h > 0) {
3490
+ if (this._audioDataHolder && w > 0 && h > 0) {
2665
3491
  //this.wo = new Worker('./audiosignal.worker.js',{type: 'module'});
2666
3492
  this.worker = new Worker(this.workerURL);
2667
3493
  //this.wo = new Worker('worker/audiosignal.worker.ts');
2668
3494
  //let Worker = require('worker!../../../workers/uploader/main');
2669
- let chs = this._audioData.numberOfChannels;
2670
- let frameLength = this._audioData.getChannelData(0).length;
2671
- // if(frameLength != this.audioData.getChannelData(1).length){
2672
- // alert("Ungleiche Länge");
2673
- // }
2674
- let ad = new Float32Array(chs * frameLength);
2675
- for (let ch = 0; ch < chs; ch++) {
2676
- ad.set(this._audioData.getChannelData(ch), ch * frameLength);
2677
- }
2678
- //let start = Date.now();
3495
+ let chs = this._audioDataHolder.numberOfChannels;
3496
+ let leftPos = Math.round(this.bounds.position.left);
3497
+ let renderPos = leftPos;
3498
+ let vw = Math.round(this.virtualDimension.width);
3499
+ let frameLength = this._audioDataHolder.frameLen;
3500
+ let framesPerPixel = Math.ceil(frameLength / vw);
3501
+ let audioBuffer = this._audioDataHolder.buffer;
3502
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
3503
+ let arrAbBuf;
3504
+ //let ais:ArrayAudioBufferInputStream|null=null;
3505
+ //let aisBuf:Float32Array[]|null=null;
3506
+ let psMinMax = null;
2679
3507
  if (this.worker) {
2680
3508
  this.worker.onmessage = (me) => {
2681
- //console.log("As rendertime: ", Date.now() - start);
2682
- this.drawRendered(me);
2683
- if (this.worker) {
2684
- this.worker.terminate();
3509
+ if (me.data.eod === true) {
3510
+ let psMinMaxTmp;
3511
+ if (psMinMax) {
3512
+ psMinMaxTmp = psMinMax;
3513
+ }
3514
+ else {
3515
+ psMinMaxTmp = me.data.psMinMax;
3516
+ }
3517
+ this.drawRendered(leftPos, w, h, chs, psMinMaxTmp);
3518
+ if (this.worker) {
3519
+ this.worker.terminate();
3520
+ }
3521
+ this.worker = null;
3522
+ }
3523
+ else if (this._audioDataHolder && arrAbBuf) {
3524
+ let rw = me.data.w;
3525
+ let rPointsLen = chs * rw;
3526
+ let pointsLen = chs * w;
3527
+ for (let ch = 0; ch < chs; ch++) {
3528
+ if (psMinMax) {
3529
+ let rBasePos = ch * rw;
3530
+ let basePos = ch * w;
3531
+ let rPosMin = rBasePos;
3532
+ let rPosMax = rPointsLen + rPosMin;
3533
+ let posMin = basePos + (renderPos - leftPos);
3534
+ let posMax = pointsLen + posMin;
3535
+ psMinMax[posMin] = me.data.psMinMax[rPosMin];
3536
+ //console.debug('Min: ('+pos+'): '+me.data.psMinMax[0]);
3537
+ psMinMax[posMax] = me.data.psMinMax[rPosMax];
3538
+ // console.debug('Max: ('+(pointsLen+pos)+'): '+me.data.psMinMax[1]);
3539
+ //console.debug("psMinMax["+posMin+"]="+me.data.psMinMax[rPosMin]+" (rPosMin="+rPosMin+"),psMinMax["+posMax+"]="+me.data.psMinMax[rPosMax]);
3540
+ }
3541
+ }
3542
+ let eod = false;
3543
+ renderPos++;
3544
+ let ad;
3545
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3546
+ if (renderPos < leftPos + w) {
3547
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3548
+ //console.debug("First read frame: "+arrAbBuf[0][0]);
3549
+ ad = new Float32Array(chs * framesPerPixel);
3550
+ for (let ch = 0; ch < chs; ch++) {
3551
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3552
+ }
3553
+ eod = (read <= 0);
3554
+ }
3555
+ else {
3556
+ ad = new Float32Array();
3557
+ eod = true;
3558
+ }
3559
+ let adBuf = ad.buffer;
3560
+ if (this.worker) {
3561
+ this.worker.postMessage({
3562
+ l: renderPos,
3563
+ w: me.data.w,
3564
+ h: h,
3565
+ vw: vw,
3566
+ chs: chs,
3567
+ frameLength: frameLength,
3568
+ audioData: ad,
3569
+ audioDataOffset: leftFramePos,
3570
+ eod: eod
3571
+ }, [adBuf]);
3572
+ }
2685
3573
  }
2686
- this.worker = null;
2687
3574
  };
2688
3575
  }
2689
- this.worker.postMessage({
2690
- l: Math.round(this.bounds.position.left),
2691
- t: Math.round(this.bounds.position.top),
2692
- w: w,
2693
- h: h,
2694
- vw: Math.round(this.virtualDimension.width),
2695
- chs: chs,
2696
- frameLength: frameLength,
2697
- audioData: ad
2698
- }, [ad.buffer]);
3576
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
3577
+ // Render whole clip at once
3578
+ arrAbBuf = null;
3579
+ psMinMax = null;
3580
+ let ad = new Float32Array(chs * frameLength);
3581
+ for (let ch = 0; ch < chs; ch++) {
3582
+ ad.set(audioBuffer.getChannelData(ch), ch * frameLength);
3583
+ }
3584
+ this.worker.postMessage({
3585
+ l: leftPos,
3586
+ w: w,
3587
+ vw: vw,
3588
+ chs: chs,
3589
+ frameLength: frameLength,
3590
+ audioData: ad,
3591
+ audioDataOffset: 0,
3592
+ eod: true
3593
+ }, [ad.buffer]);
3594
+ }
3595
+ else {
3596
+ // Render pixel by pixel
3597
+ if (w > 0) {
3598
+ if (framesPerPixel > 0) {
3599
+ let rw = 1;
3600
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
3601
+ arrAbBuf = new Array(chs);
3602
+ psMinMax = new Float32Array(chs * w * 2);
3603
+ for (let ch = 0; ch < chs; ch++) {
3604
+ arrAbBuf[ch] = new Float32Array(framesPerPixel);
3605
+ }
3606
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3607
+ let auOffset = leftFramePos; // should always be 0
3608
+ //let read=arrayAudioBuffer.frames(leftFramePos,framesPerPixel,arrAbBuf);
3609
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3610
+ let ad = new Float32Array(chs * framesPerPixel);
3611
+ for (let ch = 0; ch < chs; ch++) {
3612
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3613
+ }
3614
+ this.worker.postMessage({
3615
+ l: renderPos,
3616
+ w: rw,
3617
+ vw: vw,
3618
+ chs: chs,
3619
+ frameLength: frameLength,
3620
+ audioData: ad,
3621
+ audioDataOffset: auOffset,
3622
+ eod: (read <= 0)
3623
+ }, [ad.buffer]);
3624
+ }
3625
+ }
3626
+ }
2699
3627
  }
2700
3628
  else {
2701
3629
  let g = this.signalCanvas.getContext("2d");
@@ -2705,42 +3633,42 @@ class AudioSignal extends AudioCanvasLayerComponent {
2705
3633
  }
2706
3634
  }
2707
3635
  }
2708
- drawRendered(me) {
3636
+ drawRendered(left, w, h, chs, psMinMax) {
2709
3637
  this.drawBg();
2710
- this.signalCanvas.style.left = me.data.l.toString() + 'px';
2711
- this.signalCanvas.width = me.data.w;
2712
- this.signalCanvas.height = me.data.h;
3638
+ this.signalCanvas.style.left = left.toString() + 'px';
3639
+ this.signalCanvas.width = w;
3640
+ this.signalCanvas.height = h;
2713
3641
  let g = this.signalCanvas.getContext("2d");
2714
3642
  if (g) {
2715
- g.clearRect(0, 0, me.data.w, me.data.h);
3643
+ g.clearRect(0, 0, w, h);
2716
3644
  //g.fillStyle = "black";
2717
3645
  //g.fillRect(0, 0, me.data.w, me.data.h);
2718
- let pointsLen = me.data.w * me.data.chs;
3646
+ let pointsLen = w * chs;
2719
3647
  // one for min one for max
2720
3648
  let arrLen = pointsLen * 2;
2721
- if (this._audioData) {
3649
+ if (this._audioDataHolder) {
2722
3650
  let std = Date.now();
2723
- let chH = me.data.h / me.data.chs;
3651
+ let chH = h / chs;
2724
3652
  let y = 0;
2725
- for (let ch = 0; ch < me.data.chs; ch++) {
3653
+ for (let ch = 0; ch < chs; ch++) {
2726
3654
  let x = 0;
2727
- let psMinPos = ch * me.data.w;
3655
+ let psMinPos = ch * w;
2728
3656
  let psMaxPos = pointsLen + psMinPos;
2729
3657
  g.fillStyle = 'green';
2730
3658
  g.strokeStyle = 'green';
2731
3659
  // draw audio signal as single polygon
2732
3660
  g.beginPath();
2733
- g.moveTo(0, y + (chH / 2) + me.data.psMinMax[psMaxPos] * chH / 2);
2734
- for (let pii = 0; pii < me.data.w; pii++) {
2735
- let psMax = me.data.psMinMax[psMaxPos + pii];
3661
+ g.moveTo(0, y + (chH / 2) + psMinMax[psMaxPos] * chH / 2);
3662
+ for (let pii = 0; pii < w; pii++) {
3663
+ let psMax = psMinMax[psMaxPos + pii];
2736
3664
  let pv = psMax * chH / 2;
2737
3665
  let yd = y + (chH / 2) - pv;
2738
3666
  //console.log("LineTo: : "+pii+" "+yd)
2739
3667
  g.lineTo(pii, yd);
2740
3668
  }
2741
- let revPixelStart = me.data.w - 1;
3669
+ let revPixelStart = w - 1;
2742
3670
  for (let pii = revPixelStart; pii >= 0; pii--) {
2743
- let psMin = me.data.psMinMax[psMinPos + pii];
3671
+ let psMin = psMinMax[psMinPos + pii];
2744
3672
  let pv = psMin * chH / 2;
2745
3673
  let yd = y + (chH / 2) - pv;
2746
3674
  //console.log("LineTo: : "+pii+" "+yd)
@@ -2764,13 +3692,23 @@ class AudioSignal extends AudioCanvasLayerComponent {
2764
3692
  g.clearRect(0, 0, w, h);
2765
3693
  //g.fillStyle = "black";
2766
3694
  //g.fillRect(0, 0, w, h);
2767
- if (this._audioData) {
3695
+ if (this._audioDataHolder) {
2768
3696
  let std = Date.now();
2769
- let chs = this._audioData.numberOfChannels;
3697
+ let chs = this._audioDataHolder.numberOfChannels;
2770
3698
  let chH = h / chs;
2771
- let frameLength = this._audioData.getChannelData(0).length;
3699
+ let frameLength = this._audioDataHolder.frameLen;
2772
3700
  let framesPerPixel = frameLength / w;
2773
3701
  let y = 0;
3702
+ let ais = null;
3703
+ let audioBuffer = this._audioDataHolder.buffer;
3704
+ let aisBuffer = null;
3705
+ if (!audioBuffer) {
3706
+ ais = this._audioDataHolder.audioInputStream();
3707
+ aisBuffer = new Array(chs);
3708
+ for (let ch = 0; ch < chs; ch++) {
3709
+ aisBuffer[ch] = new Float32Array(framesPerPixel);
3710
+ }
3711
+ }
2774
3712
  for (let ch = 0; ch < chs; ch++) {
2775
3713
  let x = 0;
2776
3714
  let psMin = new Float32Array(w);
@@ -2779,20 +3717,33 @@ class AudioSignal extends AudioCanvasLayerComponent {
2779
3717
  for (let pii = 0; pii < w; pii++) {
2780
3718
  let pMin = 0;
2781
3719
  let pMax = 0;
2782
- // calculate pixel min/max amplitude
2783
- for (let ai = 0; ai < framesPerPixel; ai++) {
2784
- //let framePos=(pii*framesPerPixel)+ai;
2785
- let a = this._audioData.getChannelData(ch)[framePos++];
2786
- if (a < pMin) {
2787
- pMin = a;
3720
+ if (audioBuffer) {
3721
+ // calculate pixel min/max amplitude
3722
+ for (let ai = 0; ai < framesPerPixel; ai++) {
3723
+ //let framePos=(pii*framesPerPixel)+ai;
3724
+ let a = audioBuffer.getChannelData(ch)[framePos++];
3725
+ if (a < pMin) {
3726
+ pMin = a;
3727
+ }
3728
+ if (a > pMax) {
3729
+ pMax = a;
3730
+ }
2788
3731
  }
2789
- if (a > pMax) {
2790
- pMax = a;
3732
+ }
3733
+ else if (ais && aisBuffer) {
3734
+ let r = ais.read(aisBuffer);
3735
+ for (let ai = 0; ai < r; ai++) {
3736
+ let a = aisBuffer[ch][ai];
3737
+ if (a < pMin) {
3738
+ pMin = a;
3739
+ }
3740
+ if (a > pMax) {
3741
+ pMax = a;
3742
+ }
2791
3743
  }
2792
3744
  }
2793
3745
  psMin[pii] = pMin;
2794
3746
  psMax[pii] = pMax;
2795
- //console.log("Min: ", pMin, " max: ", pMax);
2796
3747
  }
2797
3748
  g.fillStyle = 'green';
2798
3749
  g.strokeStyle = 'green';
@@ -2817,12 +3768,11 @@ class AudioSignal extends AudioCanvasLayerComponent {
2817
3768
  g.stroke();
2818
3769
  y += chH;
2819
3770
  }
2820
- //this.drawPlayPosition();
2821
3771
  }
2822
3772
  }
2823
3773
  }
2824
3774
  setData(audioData) {
2825
- this._audioData = audioData;
3775
+ this._audioDataHolder = audioData;
2826
3776
  this.playFramePosition = 0;
2827
3777
  }
2828
3778
  }
@@ -3191,7 +4141,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3191
4141
  this._playFramePosition = null;
3192
4142
  this.dftSize = DEFAULT_DFT_SIZE;
3193
4143
  this.worker = null;
3194
- this._audioData = null;
4144
+ this._audioDataHolder = null;
3195
4145
  this.markers = new Array();
3196
4146
  this.dft = new DFTFloat32(this.dftSize);
3197
4147
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
@@ -3244,7 +4194,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3244
4194
  g.lineTo(xViewPortPixelpos, h);
3245
4195
  g.closePath();
3246
4196
  g.stroke();
3247
- if (this._audioData) {
4197
+ if (this._audioDataHolder) {
3248
4198
  let framePosRound = this.viewPortXPixelToFramePosition(xViewPortPixelpos);
3249
4199
  if (framePosRound != null) {
3250
4200
  g.font = '14px sans-serif';
@@ -3508,11 +4458,21 @@ class Sonagram extends AudioCanvasLayerComponent {
3508
4458
  }
3509
4459
  GaussianWindow.DEFAULT_SIGMA = 0.3;
3510
4460
  self.onmessage = function (msg) {
4461
+ //console.debug("Sonagram render thread");
3511
4462
  let l = msg.data.l;
3512
4463
  let w = msg.data.w;
3513
4464
  let h = msg.data.h;
3514
4465
  let vw = msg.data.vw;
3515
4466
  let chs = msg.data.chs;
4467
+ let audioDataOffset = 0;
4468
+ let adOffset = msg.data.audioDataOffset;
4469
+ if (adOffset) {
4470
+ audioDataOffset = adOffset;
4471
+ }
4472
+ let maxPsd = null;
4473
+ if (msg.data.maxPsd !== undefined) {
4474
+ maxPsd = msg.data.maxPsd;
4475
+ }
3516
4476
  let audioData = new Array(chs);
3517
4477
  for (let ch = 0; ch < chs; ch++) {
3518
4478
  audioData[ch] = new Float32Array(msg.data['audioData'][ch]);
@@ -3528,16 +4488,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3528
4488
  }
3529
4489
  let imgData = new Uint8ClampedArray(arrSize);
3530
4490
  //console.log("Render method:");
3531
- if (audioData && arrSize > 0) {
4491
+ //console.debug("Created imgData arrSize: "+arrSize+" ", w, "x", h);
4492
+ let calcMaxPsd = -Infinity;
4493
+ if (arrSize > 0) {
3532
4494
  let chH = Math.round(h / chs);
3533
4495
  let framesPerPixel = frameLength / vw;
3534
- //console.log("Render: ", w, "x", h);
4496
+ //console.debug("Render: ", w, "x", h);
3535
4497
  let b = new Float32Array(dftSize);
3536
4498
  let sona = new Array(chs);
3537
- let maxPsd = -Infinity;
3538
- let p = 0;
4499
+ //let p = 0;
3539
4500
  for (let ch = 0; ch < chs; ch++) {
3540
- p = ch * frameLength;
4501
+ //p = ch * frameLength;
3541
4502
  let chDataLen = audioData[ch].length;
3542
4503
  let x = 0;
3543
4504
  // initialize DFT array buffer
@@ -3554,8 +4515,13 @@ class Sonagram extends AudioCanvasLayerComponent {
3554
4515
  // initialize for negative sample positions and out of bounds positions
3555
4516
  let chDat = 0;
3556
4517
  // Set audio sample if available
3557
- if (samplePos >= 0 && samplePos < chDataLen) {
3558
- chDat = audioData[ch][samplePos];
4518
+ let adp = samplePos - audioDataOffset;
4519
+ if (adp >= 0 && adp < chDataLen) {
4520
+ chDat = audioData[ch][adp];
4521
+ //console.debug("Audio data: "+chDat);
4522
+ }
4523
+ else {
4524
+ //console.debug("Sample buf pos oob: adp: "+adp+", chDataLen: "+chDataLen+", samplePos: "+samplePos+", i: "+i);
3559
4525
  }
3560
4526
  // apply Window
3561
4527
  b[i] = chDat * wf.getScale(i);
@@ -3565,63 +4531,78 @@ class Sonagram extends AudioCanvasLayerComponent {
3565
4531
  // Get maximum value of spectral energy
3566
4532
  for (let s = 0; s < dftBands; s++) {
3567
4533
  let psd = (2 * Math.pow(spectr[s], 2)) / dftBands;
3568
- if (psd > maxPsd) {
3569
- maxPsd = psd;
4534
+ if (psd > calcMaxPsd) {
4535
+ calcMaxPsd = psd;
3570
4536
  }
3571
4537
  }
3572
4538
  // Set render model data for this pixel
3573
4539
  sona[ch][pii] = spectr;
3574
4540
  }
3575
4541
  }
3576
- //maxPsd = (2 * Math.pow(max, 2)) / dftBands;
3577
- for (let ch = 0; ch < chs; ch++) {
3578
- for (let pii = 0; pii < w; pii++) {
3579
- let allBlack = true;
3580
- for (let y = 0; y < chH; y++) {
3581
- let freqIdx = Math.round(y * dftBands / chH);
3582
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3583
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3584
- let val = sona[ch][pii][freqIdx];
3585
- let psd = (2 * Math.pow(val, 2)) / dftBands;
3586
- // Calculate logarithmic value
3587
- //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
3588
- let linearLevel = psd / maxPsd;
3589
- let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
3590
- // Fixed dynamic Range value of 70dB for now
3591
- let dynRangeInDb = 70;
3592
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3593
- // are the following checks necessary for clamped array ?
3594
- if (scaledVal > 1.0)
3595
- scaledVal = 1;
3596
- if (scaledVal < 0.0) {
3597
- scaledVal = 0;
3598
- }
3599
- let rgbVal = Math.round(255 * scaledVal);
3600
- if (rgbVal < 0) {
3601
- // System.out.println("Neg RGB val: "+rgbVal);
3602
- rgbVal = 0;
3603
- }
3604
- if (rgbVal > 255) {
3605
- rgbVal = 255;
3606
- }
3607
- rgbVal = 255 - rgbVal;
3608
- if (rgbVal > 0) {
3609
- allBlack = false;
4542
+ if (!msg.data.norender) {
4543
+ if (!maxPsd) {
4544
+ maxPsd = calcMaxPsd;
4545
+ }
4546
+ for (let ch = 0; ch < chs; ch++) {
4547
+ for (let pii = 0; pii < w; pii++) {
4548
+ let allBlack = true;
4549
+ for (let y = 0; y < chH; y++) {
4550
+ let freqIdx = Math.round(y * dftBands / chH);
4551
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4552
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4553
+ let val = sona[ch][pii][freqIdx];
4554
+ let psd = (2 * Math.pow(val, 2)) / dftBands;
4555
+ // Calculate logarithmic value
4556
+ //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
4557
+ let linearLevel = psd / maxPsd;
4558
+ let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
4559
+ // Fixed dynamic Range value of 70dB for now
4560
+ let dynRangeInDb = 70;
4561
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4562
+ // are the following checks necessary for clamped array ?
4563
+ if (scaledVal > 1.0)
4564
+ scaledVal = 1;
4565
+ if (scaledVal < 0.0) {
4566
+ scaledVal = 0;
4567
+ }
4568
+ let rgbVal = Math.round(255 * scaledVal);
4569
+ if (rgbVal < 0) {
4570
+ // System.out.println("Neg RGB val: "+rgbVal);
4571
+ rgbVal = 0;
4572
+ }
4573
+ if (rgbVal > 255) {
4574
+ rgbVal = 255;
4575
+ }
4576
+ rgbVal = 255 - rgbVal;
4577
+ if (rgbVal > 0) {
4578
+ allBlack = false;
4579
+ }
4580
+ let py = chH - y;
4581
+ let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
4582
+ imgData[dataPos + 0] = rgbVal; //R
4583
+ imgData[dataPos + 1] = rgbVal; //G
4584
+ imgData[dataPos + 2] = rgbVal; //B
4585
+ imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4586
+ //console.debug("Rendered: py: "+py+", rgbval: "+rgbVal);
4587
+ // example 1x1, 2chs
4588
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4589
+ // ch0x1y0R,ch0x1y0G,ch0x1y0B,ch0x1y0A,
4590
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4591
+ // ch0x1y1R,ch0x1y1G,ch0x1y1B,ch0x1y1A,
4592
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4593
+ // ch1x1y0R,ch1x1y0G,ch1x1y0B,ch1x1y0A,
4594
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4595
+ // ch1x1y1R,ch1x1y1G,ch1x1y1B,ch1x1y1A
3610
4596
  }
3611
- let py = chH - y;
3612
- let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
3613
- imgData[dataPos + 0] = rgbVal; //R
3614
- imgData[dataPos + 1] = rgbVal; //G
3615
- imgData[dataPos + 2] = rgbVal; //B
3616
- imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4597
+ // if (allBlack) {
4598
+ // console.log("Black: ", pii, " ", ch);
4599
+ // }
3617
4600
  }
3618
- // if (allBlack) {
3619
- // console.log("Black: ", pii, " ", ch);
3620
- // }
3621
4601
  }
3622
4602
  }
3623
4603
  }
3624
- postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw }, [imgData.buffer]);
4604
+ //console.debug("Render thread post message imgData: "+imgData.length)
4605
+ postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw, maxPsd: calcMaxPsd, terminate: msg.data.terminate }, [imgData.buffer]);
3625
4606
  };
3626
4607
  }
3627
4608
  startDraw(clear = true) {
@@ -3651,42 +4632,190 @@ class Sonagram extends AudioCanvasLayerComponent {
3651
4632
  if (this.bounds) {
3652
4633
  let w = Math.round(this.bounds.dimension.width);
3653
4634
  let h = Math.round(this.bounds.dimension.height);
3654
- if (this._audioData && w > 0 && h > 0) {
4635
+ if (this._audioDataHolder && w > 0 && h > 0) {
3655
4636
  this.worker = new Worker(this.workerURL);
3656
4637
  //this.wo = new Worker('./worker/sonagram.worker', { type: `module` });
3657
- let chs = this._audioData.numberOfChannels;
3658
- let frameLength = this._audioData.getChannelData(0).length;
3659
- let ada = new Array(chs);
3660
- for (let ch = 0; ch < chs; ch++) {
3661
- // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
3662
- ada[ch] = this._audioData.getChannelData(ch).buffer.slice(0);
3663
- }
3664
- let start = Date.now();
4638
+ let chs = this._audioDataHolder.numberOfChannels;
4639
+ let vw = Math.round(this.virtualDimension.width);
4640
+ let frameLength = this._audioDataHolder.frameLen;
4641
+ let framesPerPixel = Math.ceil(frameLength / vw);
4642
+ let leftPos = Math.round(this.bounds.position.left);
4643
+ let renderPos = leftPos;
4644
+ let audioBuffer = this._audioDataHolder.buffer;
4645
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
4646
+ let arrAbBuf;
4647
+ let imgData;
4648
+ let maxPsd = -Infinity;
4649
+ let norender = true;
3665
4650
  if (this.worker) {
3666
4651
  this.worker.onmessage = (me) => {
3667
- this.drawRendered(me);
3668
- if (this.worker) {
3669
- this.worker.terminate();
4652
+ if (true === me.data.terminate) {
4653
+ let drawImgData;
4654
+ if (imgData) {
4655
+ drawImgData = imgData;
4656
+ }
4657
+ else {
4658
+ drawImgData = me.data.imgData;
4659
+ }
4660
+ this.drawRendered(w, h, drawImgData);
4661
+ if (this.worker) {
4662
+ this.worker.terminate();
4663
+ }
4664
+ this.worker = null;
4665
+ }
4666
+ else {
4667
+ // set rendered vertical values of one pixel of timescale
4668
+ //let dataPos = renderPos * h * 4;
4669
+ if (norender) {
4670
+ if (me.data.maxPsd > maxPsd) {
4671
+ maxPsd = me.data.maxPsd;
4672
+ //console.debug("new maxPsd: "+maxPsd);
4673
+ }
4674
+ }
4675
+ else {
4676
+ let chH = Math.round(h / chs);
4677
+ let idp = me.data.l - leftPos;
4678
+ for (let ch = 0; ch < chs; ch++) {
4679
+ for (let y = 0; y < chH; y++) {
4680
+ let py = chH - y;
4681
+ let dataPos = ((((ch * chH) + py) * w) + idp) * 4;
4682
+ let mePos = ((ch * chH) + py) * 4;
4683
+ //let lastPos = dataPos + me.data.imgData.length;
4684
+ //if (lastPos < imgData.length) {
4685
+ // set one pixel
4686
+ imgData[dataPos] = me.data.imgData[mePos];
4687
+ imgData[dataPos + 1] = me.data.imgData[mePos + 1];
4688
+ imgData[dataPos + 2] = me.data.imgData[mePos + 2];
4689
+ imgData[dataPos + 3] = me.data.imgData[mePos + 3];
4690
+ //} else {
4691
+ //console.error("Out of range: " + dataPos + "+" + me.data.imgData.length + ">=" + imgData.length);
4692
+ // }
4693
+ }
4694
+ }
4695
+ }
4696
+ if (this._audioDataHolder && arrAbBuf && this.worker) {
4697
+ // proceed with next pixel
4698
+ renderPos++;
4699
+ //console.debug("Render pos: "+renderPos);
4700
+ let terminate = false;
4701
+ let windowEnd = renderPos >= leftPos + w;
4702
+ if (windowEnd) {
4703
+ if (norender) {
4704
+ // phase two: rendering
4705
+ norender = false;
4706
+ // start from beginning
4707
+ renderPos = leftPos;
4708
+ //console.debug("now rendering: maxPsd: "+maxPsd);
4709
+ }
4710
+ else {
4711
+ // terminate render phase
4712
+ terminate = true;
4713
+ }
4714
+ }
4715
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4716
+ if (leftFramePos < 0) {
4717
+ leftFramePos = 0;
4718
+ }
4719
+ let ada = new Array(chs);
4720
+ //console.debug("Render pos: "+renderPos+" leftFramePos: "+leftFramePos);
4721
+ if (!terminate) {
4722
+ if (this._audioDataHolder) {
4723
+ let read = this._audioDataHolder.frames(leftFramePos, this.dftSize, arrAbBuf);
4724
+ for (let ch = 0; ch < chs; ch++) {
4725
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4726
+ ada[ch] = arrAbBuf[ch].buffer.slice(0);
4727
+ }
4728
+ }
4729
+ }
4730
+ else {
4731
+ for (let ch = 0; ch < chs; ch++) {
4732
+ ada[ch] = new ArrayBuffer(0);
4733
+ }
4734
+ }
4735
+ this.worker.postMessage({
4736
+ audioData: ada,
4737
+ audioDataOffset: leftFramePos,
4738
+ l: renderPos,
4739
+ w: me.data.w,
4740
+ h: h,
4741
+ vw: vw,
4742
+ chs: chs,
4743
+ frameLength: frameLength,
4744
+ dftSize: this.dftSize,
4745
+ maxPsd: maxPsd,
4746
+ norender: norender,
4747
+ terminate: terminate
4748
+ }, ada);
4749
+ }
4750
+ }
4751
+ };
4752
+ }
4753
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
4754
+ let ada = new Array(chs);
4755
+ for (let ch = 0; ch < chs; ch++) {
4756
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4757
+ ada[ch] = audioBuffer.getChannelData(ch).buffer.slice(0);
4758
+ }
4759
+ let start = Date.now();
4760
+ if (this.markerCanvas) {
4761
+ let g = this.markerCanvas.getContext("2d");
4762
+ if (g) {
4763
+ g.fillText("Rendering...", 10, 20);
4764
+ }
4765
+ }
4766
+ this.worker.postMessage({
4767
+ audioData: ada,
4768
+ l: leftPos,
4769
+ w: w,
4770
+ h: h,
4771
+ vw: Math.round(this.virtualDimension.width),
4772
+ chs: chs,
4773
+ frameLength: frameLength,
4774
+ dftSize: this.dftSize,
4775
+ terminate: true
4776
+ }, ada);
4777
+ }
4778
+ else {
4779
+ if (w > 0) {
4780
+ if (framesPerPixel > 0) {
4781
+ let arrSize = w * h * 4;
4782
+ if (arrSize < 0) {
4783
+ arrSize = 0;
4784
+ }
4785
+ imgData = new Uint8ClampedArray(arrSize);
4786
+ let rw = 1;
4787
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
4788
+ arrAbBuf = new Array(chs);
4789
+ for (let ch = 0; ch < chs; ch++) {
4790
+ arrAbBuf[ch] = new Float32Array(this.dftSize);
4791
+ }
4792
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4793
+ let framesToRead = this.dftSize;
4794
+ if (leftFramePos < 0) {
4795
+ //framesToRead=this.dftSize+leftFramePos;
4796
+ leftFramePos = 0;
4797
+ }
4798
+ let read = this._audioDataHolder.frames(leftFramePos, framesToRead, arrAbBuf);
4799
+ let ad = new Float32Array(chs * framesToRead);
4800
+ for (let ch = 0; ch < chs; ch++) {
4801
+ ad.set(arrAbBuf[ch], ch * framesToRead);
4802
+ }
4803
+ this.worker.postMessage({
4804
+ l: renderPos,
4805
+ w: rw,
4806
+ h: h,
4807
+ vw: vw,
4808
+ chs: chs,
4809
+ frameLength: frameLength,
4810
+ audioData: ad,
4811
+ audioDataOffset: leftFramePos,
4812
+ dftSize: this.dftSize,
4813
+ norender: norender,
4814
+ terminate: false
4815
+ }, [ad.buffer]);
3670
4816
  }
3671
- this.worker = null;
3672
- };
3673
- }
3674
- if (this.markerCanvas) {
3675
- let g = this.markerCanvas.getContext("2d");
3676
- if (g) {
3677
- g.fillText("Rendering...", 10, 20);
3678
4817
  }
3679
4818
  }
3680
- this.worker.postMessage({
3681
- audioData: ada,
3682
- l: Math.round(this.bounds.position.left),
3683
- w: w,
3684
- h: h,
3685
- vw: Math.round(this.virtualDimension.width),
3686
- chs: chs,
3687
- frameLength: frameLength,
3688
- dftSize: this.dftSize
3689
- }, ada);
3690
4819
  }
3691
4820
  else {
3692
4821
  let g = this.sonagramCanvas.getContext("2d");
@@ -3696,17 +4825,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3696
4825
  }
3697
4826
  }
3698
4827
  }
3699
- drawRendered(me) {
4828
+ drawRendered(w, h, imgData) {
3700
4829
  if (this.sonagramCanvas) {
3701
- this.sonagramCanvas.width = me.data.w;
3702
- this.sonagramCanvas.height = me.data.h;
4830
+ this.sonagramCanvas.width = w;
4831
+ this.sonagramCanvas.height = h;
3703
4832
  let g = this.sonagramCanvas.getContext("2d");
3704
4833
  if (g) {
3705
- let imgDataArr = me.data.imgData;
3706
- if (me.data.w > 0 && me.data.h > 0) {
3707
- let imgData = g.createImageData(me.data.w, me.data.h);
3708
- imgData.data.set(imgDataArr);
3709
- g.putImageData(imgData, 0, 0);
4834
+ let imgDataArr = imgData;
4835
+ if (w > 0 && h > 0) {
4836
+ let gImgData = g.createImageData(w, h);
4837
+ gImgData.data.set(imgDataArr);
4838
+ g.putImageData(gImgData, 0, 0);
3710
4839
  }
3711
4840
  }
3712
4841
  }
@@ -3722,91 +4851,95 @@ class Sonagram extends AudioCanvasLayerComponent {
3722
4851
  g.clearRect(0, 0, w, h);
3723
4852
  g.fillStyle = "white";
3724
4853
  g.fillRect(0, 0, w, h);
3725
- if (this._audioData) {
4854
+ if (this._audioDataHolder) {
3726
4855
  let spectSize = Math.floor(this.dftSize / 2);
3727
- let chs = this._audioData.numberOfChannels;
4856
+ let chs = this._audioDataHolder.numberOfChannels;
3728
4857
  let chH = h / chs;
3729
- let frameLength = this._audioData.getChannelData(0).length;
4858
+ let frameLength = this._audioDataHolder.frameLen;
3730
4859
  let framesPerPixel = frameLength / w;
3731
4860
  let y = 0;
3732
- // TODO
3733
- let b = new Float32Array(this.dftSize);
3734
- let sona = new Array(chs);
3735
- let max = 0;
3736
- let maxPsd = -Infinity;
3737
- for (let ch = 0; ch < chs; ch++) {
3738
- let x = 0;
3739
- sona[ch] = new Array(w);
3740
- let chData = this._audioData.getChannelData(ch);
3741
- // TODO center buffer
3742
- let framePos = 0;
3743
- for (let pii = 0; pii < w; pii++) {
3744
- framePos = Math.round(pii * framesPerPixel);
3745
- // calculate DFT at pixel position
3746
- for (let i = 0; i < this.dftSize; i++) {
3747
- let chDat = chData[framePos + i];
3748
- b[i] = chDat;
3749
- }
3750
- let spectr = this.dft.processRealMagnitude(b);
3751
- sona[ch][pii] = spectr;
3752
- // @ts-ignore
3753
- let pMax = Math.max.apply(null, spectr);
3754
- if (pMax > max) {
3755
- max = pMax;
3756
- }
3757
- for (let s = 0; s < spectSize; s++) {
3758
- let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
3759
- if (psd > maxPsd) {
3760
- maxPsd = psd;
4861
+ let audioBuffer = this._audioDataHolder.buffer;
4862
+ let arrayAudioBuffer = this._audioDataHolder.arrayBuffer;
4863
+ if (audioBuffer) {
4864
+ let b = new Float32Array(this.dftSize);
4865
+ let sona = new Array(chs);
4866
+ let max = 0;
4867
+ let maxPsd = -Infinity;
4868
+ for (let ch = 0; ch < chs; ch++) {
4869
+ let x = 0;
4870
+ sona[ch] = new Array(w);
4871
+ let chData = audioBuffer.getChannelData(ch);
4872
+ // TODO center buffer
4873
+ let framePos = 0;
4874
+ for (let pii = 0; pii < w; pii++) {
4875
+ framePos = Math.round(pii * framesPerPixel);
4876
+ // calculate DFT at pixel position
4877
+ for (let i = 0; i < this.dftSize; i++) {
4878
+ let chDat = chData[framePos + i];
4879
+ b[i] = chDat;
3761
4880
  }
3762
- }
3763
- }
3764
- }
3765
- //console.log("max: ", max);
3766
- maxPsd = (2 * Math.pow(max, 2)) / spectSize;
3767
- for (let ch = 0; ch < chs; ch++) {
3768
- let framePos = 0;
3769
- for (let pii = 0; pii < w; pii++) {
3770
- framePos = pii * framesPerPixel;
3771
- for (let y = 0; y < h; y++) {
3772
- let freqIdx = Math.round(y * spectSize / h);
3773
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3774
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3775
- let val = sona[ch][pii][freqIdx];
3776
- let psd = (2 * Math.pow(val, 2)) / spectSize;
3777
- // Calculate logarithmic
3778
- let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
3779
- let dynRangeInDb = 70;
3780
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3781
- if (scaledVal > 1)
3782
- scaledVal = 1;
3783
- if (scaledVal < 0) {
3784
- scaledVal = 0;
4881
+ let spectr = this.dft.processRealMagnitude(b);
4882
+ sona[ch][pii] = spectr;
4883
+ // @ts-ignore
4884
+ let pMax = Math.max.apply(null, spectr);
4885
+ if (pMax > max) {
4886
+ max = pMax;
3785
4887
  }
3786
- let rgbVal = (255 * scaledVal);
3787
- if (rgbVal < 0) {
3788
- // System.out.println("Neg RGB val: "+rgbVal);
3789
- rgbVal = 0;
4888
+ for (let s = 0; s < spectSize; s++) {
4889
+ let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
4890
+ if (psd > maxPsd) {
4891
+ maxPsd = psd;
4892
+ }
3790
4893
  }
3791
- if (rgbVal > 255) {
3792
- rgbVal = 255;
4894
+ }
4895
+ }
4896
+ //console.log("max: ", max);
4897
+ maxPsd = (2 * Math.pow(max, 2)) / spectSize;
4898
+ for (let ch = 0; ch < chs; ch++) {
4899
+ let framePos = 0;
4900
+ for (let pii = 0; pii < w; pii++) {
4901
+ framePos = pii * framesPerPixel;
4902
+ for (let y = 0; y < h; y++) {
4903
+ let freqIdx = Math.round(y * spectSize / h);
4904
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4905
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4906
+ let val = sona[ch][pii][freqIdx];
4907
+ let psd = (2 * Math.pow(val, 2)) / spectSize;
4908
+ // Calculate logarithmic
4909
+ let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
4910
+ let dynRangeInDb = 70;
4911
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4912
+ if (scaledVal > 1)
4913
+ scaledVal = 1;
4914
+ if (scaledVal < 0) {
4915
+ scaledVal = 0;
4916
+ }
4917
+ let rgbVal = (255 * scaledVal);
4918
+ if (rgbVal < 0) {
4919
+ // System.out.println("Neg RGB val: "+rgbVal);
4920
+ rgbVal = 0;
4921
+ }
4922
+ if (rgbVal > 255) {
4923
+ rgbVal = 255;
4924
+ }
4925
+ rgbVal = 255 - rgbVal;
4926
+ let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
4927
+ g.fillStyle = colorStr;
4928
+ g.fillRect(pii, chH - y, 1, 1);
3793
4929
  }
3794
- rgbVal = 255 - rgbVal;
3795
- let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
3796
- g.fillStyle = colorStr;
3797
- g.fillRect(pii, chH - y, 1, 1);
3798
4930
  }
3799
4931
  }
4932
+ this.drawPlayPosition();
4933
+ }
4934
+ else if (arrayAudioBuffer) {
4935
+ throw Error("Redraw with array audio buffer not supported.");
3800
4936
  }
3801
- this.drawPlayPosition();
3802
4937
  }
3803
4938
  }
3804
4939
  }
3805
4940
  setData(audioData) {
3806
- this._audioData = audioData;
4941
+ this._audioDataHolder = audioData;
3807
4942
  this.playFramePosition = 0;
3808
- //this.redraw();
3809
- //this.startRender();
3810
4943
  }
3811
4944
  }
3812
4945
  Sonagram.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: Sonagram, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4031,13 +5164,13 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4031
5164
  }
4032
5165
  currentXZoom() {
4033
5166
  let xz = this._xZoom;
4034
- if (xz == null && this._audioData) {
5167
+ if (xz == null && this._audioDataHolder) {
4035
5168
  let ow = this.ce.offsetWidth;
4036
5169
  if (ow < 1) {
4037
5170
  // at least one pixel width to avoid x-zoom zero values
4038
5171
  ow = 1;
4039
5172
  }
4040
- xz = ow / this._audioData.duration;
5173
+ xz = ow / this._audioDataHolder.duration;
4041
5174
  }
4042
5175
  return xz;
4043
5176
  }
@@ -4116,9 +5249,10 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4116
5249
  this._layout(false, false);
4117
5250
  }
4118
5251
  layout(clear = true) {
5252
+ var _a;
4119
5253
  if (this.ce && this.dc) {
4120
5254
  const clientW = this.ce.clientWidth;
4121
- if (this._audioData) {
5255
+ if (this._audioDataHolder) {
4122
5256
  if (this._fixFitToPanel) {
4123
5257
  // Set the virtual canvas width to the visible width
4124
5258
  if (this.ce.style.width != '100%') {
@@ -4129,7 +5263,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4129
5263
  else {
4130
5264
  if (this._xZoom) {
4131
5265
  // Set the virtual canvas width according to the value of the user selected xZoom value
4132
- const newClW = Math.round(this._xZoom * this._audioData.duration);
5266
+ const newClW = Math.round(this._xZoom * ((_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.duration));
4133
5267
  this.ce.style.width = newClW + 'px';
4134
5268
  }
4135
5269
  else {
@@ -4141,22 +5275,22 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4141
5275
  this._layout(clear, true);
4142
5276
  }
4143
5277
  }
4144
- set audioData(audioData) {
5278
+ set audioData(audioDataHolder) {
4145
5279
  this._audioClip = null;
4146
- this._audioData = audioData;
4147
- this.as.setData(audioData);
4148
- this.so.setData(audioData);
5280
+ this._audioDataHolder = audioDataHolder;
5281
+ this.as.setData(audioDataHolder);
5282
+ this.so.setData(audioDataHolder);
4149
5283
  this.layout();
4150
5284
  }
4151
5285
  get audioData() {
4152
- return this._audioData;
5286
+ return this._audioDataHolder;
4153
5287
  }
4154
5288
  set audioClip(audioClip) {
4155
5289
  this._audioClip = audioClip;
4156
5290
  let audioData = null;
4157
5291
  let sel = null;
4158
5292
  if (audioClip) {
4159
- audioData = audioClip.buffer;
5293
+ audioData = audioClip.audioDataHolder;
4160
5294
  if (this._audioClip) {
4161
5295
  this._audioClip.addSelectionObserver((clip) => {
4162
5296
  this.selection = clip.selection;
@@ -4164,9 +5298,9 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4164
5298
  }
4165
5299
  sel = audioClip.selection;
4166
5300
  }
4167
- this._audioData = audioData;
4168
- this.as.setData(this._audioData);
4169
- this.so.setData(this._audioData);
5301
+ this._audioDataHolder = audioData;
5302
+ this.as.setData(this._audioDataHolder);
5303
+ this.so.setData(this._audioDataHolder);
4170
5304
  this.selecting = null;
4171
5305
  this.selection = sel;
4172
5306
  this.layout();
@@ -4358,7 +5492,7 @@ class AudioDisplayScrollPane {
4358
5492
  let audioData = null;
4359
5493
  let sel = null;
4360
5494
  if (audioClip) {
4361
- audioData = audioClip.buffer;
5495
+ audioData = audioClip.audioDataHolder;
4362
5496
  sel = audioClip.selection;
4363
5497
  audioClip.addSelectionObserver((clip) => {
4364
5498
  this.zoomSelectedAction.disabled = (clip.selection == null);
@@ -4629,17 +5763,17 @@ class AudioDisplay {
4629
5763
  console.log("Play started");
4630
5764
  this.status = 'Playing...';
4631
5765
  }
4632
- set audioData(audioBuffer) {
4633
- this.audioDisplayScrollPane.audioData = audioBuffer;
5766
+ set audioData(audioData) {
5767
+ this.audioDisplayScrollPane.audioData = audioData;
4634
5768
  if (this.playStartAction) {
4635
- this.playStartAction.disabled = (audioBuffer == null);
5769
+ this.playStartAction.disabled = (audioData == null);
4636
5770
  }
4637
5771
  }
4638
5772
  set audioClip(audioClip) {
4639
5773
  let audioData = null;
4640
5774
  let sel = null;
4641
5775
  if (audioClip) {
4642
- audioData = audioClip.buffer;
5776
+ audioData = audioClip.audioDataHolder.buffer;
4643
5777
  sel = audioClip.selection;
4644
5778
  }
4645
5779
  this._audioClip = audioClip;
@@ -4752,7 +5886,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
4752
5886
 
4753
5887
  class Progress {
4754
5888
  constructor() {
4755
- this.items = null;
5889
+ this.items = undefined;
4756
5890
  this.selectedItemIdx = 0;
4757
5891
  this.enableDownload = false;
4758
5892
  this.onRowSelect = new EventEmitter();
@@ -5492,7 +6626,7 @@ class Prompting {
5492
6626
  constructor() {
5493
6627
  this.promptItem = null;
5494
6628
  this.showPrompt = false;
5495
- this.items = null;
6629
+ this.items = undefined;
5496
6630
  this.enableDownload = false;
5497
6631
  this.audioSignalCollapsed = true;
5498
6632
  this.displayAudioClip = null;
@@ -5807,6 +6941,7 @@ class LevelBar {
5807
6941
  this._streamingMode = false;
5808
6942
  this._staticLevelInfos = null;
5809
6943
  this._playFramePosition = null;
6944
+ this._stateLoading = false;
5810
6945
  this.warnDBLevel = DEFAULT_WARN_DB_LEVEL$1;
5811
6946
  this.dbValues = new Array();
5812
6947
  }
@@ -5852,6 +6987,10 @@ class LevelBar {
5852
6987
  }
5853
6988
  this.layoutStatic();
5854
6989
  }
6990
+ set stateLoading(loading) {
6991
+ this._stateLoading = loading;
6992
+ this.layoutStatic();
6993
+ }
5855
6994
  set channelCount(channelCount) {
5856
6995
  this.reset();
5857
6996
  }
@@ -6032,6 +7171,12 @@ class LevelBar {
6032
7171
  }
6033
7172
  }
6034
7173
  }
7174
+ else if (this._stateLoading) {
7175
+ g.strokeStyle = 'white';
7176
+ g.fillStyle = 'white';
7177
+ g.font = '20px sans-serif';
7178
+ g.fillText("Loading...", 10, 25);
7179
+ }
6035
7180
  }
6036
7181
  }
6037
7182
  }
@@ -6074,7 +7219,7 @@ class LevelBar {
6074
7219
  }
6075
7220
  }
6076
7221
  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 });
6077
- 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: `
7222
+ 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: `
6078
7223
  <div #virtualCanvas>
6079
7224
  <canvas #levelbar></canvas>
6080
7225
  <canvas #markerCanvas></canvas>
@@ -6131,6 +7276,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
6131
7276
  type: Input
6132
7277
  }], displayLevelInfos: [{
6133
7278
  type: Input
7279
+ }], stateLoading: [{
7280
+ type: Input
6134
7281
  }], onResize: [{
6135
7282
  type: HostListener,
6136
7283
  args: ['window:resize', ['$event']]
@@ -6721,47 +7868,68 @@ class PeakLevelInterceptor {
6721
7868
  }
6722
7869
  }
6723
7870
  class LevelMeasure {
7871
+ //private bufferLevelInfos=new Array<LevelInfo>();
7872
+ //private peakLevelInfo!:LevelInfo;
6724
7873
  constructor() {
6725
7874
  this.worker = null;
6726
7875
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
6727
7876
  }
6728
- calcBufferLevelInfos(audioBuffer, bufferTimeLength) {
7877
+ calcBufferLevelInfos(audioDataHolder, bufferTimeLength) {
6729
7878
  return new Promise((resolve) => {
6730
- let chs = audioBuffer.numberOfChannels;
6731
- let bufferFrameLength = Math.round(audioBuffer.sampleRate * bufferTimeLength);
6732
- let buffers = new Array(chs);
7879
+ var _a;
7880
+ let chs = audioDataHolder.numberOfChannels;
7881
+ let bufferFrameLength = Math.round(audioDataHolder.sampleRate * bufferTimeLength);
7882
+ let ais = audioDataHolder.audioInputStream();
7883
+ let audioBuffers = new Array(chs);
7884
+ let trBuffers = new Array(chs);
6733
7885
  for (let ch = 0; ch < chs; ch++) {
6734
- let adCh = audioBuffer.getChannelData(ch);
6735
- let adChCopy = new Float32Array(adCh.length);
6736
- adChCopy.set(adCh);
6737
- buffers[ch] = adChCopy.buffer;
7886
+ audioBuffers[ch] = new Float32Array(bufferFrameLength);
6738
7887
  }
7888
+ let bufferLevelInfos = new Array();
7889
+ let peakLevelInfo = new LevelInfo(chs);
6739
7890
  this.worker = new Worker(this.workerURL);
6740
7891
  this.worker.onmessage = (me) => {
6741
- let linLevelArrs = new Array(chs);
6742
- for (let ch = 0; ch < chs; ch++) {
6743
- linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
6744
- }
6745
- let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
6746
- let framePos = 0;
6747
- let bufferLevelInfos = new Array();
6748
- let peakLevelInfo = new LevelInfo(chs);
6749
- for (let bi = 0; bi < bufferCount; bi++) {
6750
- let minLevels = new Array(chs);
6751
- let maxLevels = new Array(chs);
7892
+ var _a;
7893
+ if (me.data.linLevelBuffers) {
7894
+ let linLevelArrs = new Array(chs);
7895
+ for (let ch = 0; ch < chs; ch++) {
7896
+ linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
7897
+ }
7898
+ let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
7899
+ let framePos = 0;
7900
+ for (let bi = 0; bi < bufferCount; bi++) {
7901
+ let minLevels = new Array(chs);
7902
+ let maxLevels = new Array(chs);
7903
+ for (let ch = 0; ch < chs; ch++) {
7904
+ let linLvlArrPos = bi * 2;
7905
+ minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
7906
+ maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7907
+ }
7908
+ let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
7909
+ bufferLevelInfos.push(bli);
7910
+ peakLevelInfo.merge(bli);
7911
+ }
7912
+ }
7913
+ if (me.data.eod === true) {
7914
+ // end of data, terminate worker and return result
7915
+ this.terminateWorker();
7916
+ resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
7917
+ }
7918
+ else {
7919
+ let read = ais.read(audioBuffers);
6752
7920
  for (let ch = 0; ch < chs; ch++) {
6753
- let linLvlArrPos = bi * 2;
6754
- minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
6755
- maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7921
+ let copy = new Float32Array(audioBuffers[ch]);
7922
+ trBuffers[ch] = copy.buffer;
6756
7923
  }
6757
- let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
6758
- bufferLevelInfos.push(bli);
6759
- peakLevelInfo.merge(bli);
7924
+ (_a = this.worker) === null || _a === void 0 ? void 0 : _a.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6760
7925
  }
6761
- this.terminateWorker();
6762
- resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
6763
7926
  };
6764
- this.worker.postMessage({ bufferFrameLength: bufferFrameLength, audioData: buffers, chs: chs }, buffers);
7927
+ let read = ais.read(audioBuffers);
7928
+ for (let ch = 0; ch < chs; ch++) {
7929
+ let copy = new Float32Array(audioBuffers[ch]);
7930
+ trBuffers[ch] = copy.buffer;
7931
+ }
7932
+ (_a = this.worker) === null || _a === void 0 ? void 0 : _a.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6765
7933
  });
6766
7934
  }
6767
7935
  terminateWorker() {
@@ -6773,40 +7941,63 @@ class LevelMeasure {
6773
7941
  */
6774
7942
  workerFunction() {
6775
7943
  self.onmessage = function (msg) {
6776
- let chs = msg.data.chs;
7944
+ let len = msg.data.len;
6777
7945
  let bufferFrameLength = msg.data.bufferFrameLength;
6778
- let audioData = new Array(chs);
6779
- let linLevels = new Array(chs);
6780
- for (let ch = 0; ch < chs; ch++) {
6781
- audioData[ch] = new Float32Array(msg.data.audioData[ch]);
7946
+ if (len == -1) {
7947
+ // start
7948
+ postMessage({
7949
+ eod: false,
7950
+ bufferFrameLength: bufferFrameLength,
7951
+ frameLength: len
7952
+ });
6782
7953
  }
6783
- let frameLength = audioData[0].length;
6784
- let bufferCount = Math.ceil(frameLength / bufferFrameLength);
6785
- for (let ch = 0; ch < chs; ch++) {
6786
- linLevels[ch] = new Float32Array(bufferCount * 2);
7954
+ if (len == 0) {
7955
+ postMessage({
7956
+ eod: true,
7957
+ bufferFrameLength: bufferFrameLength,
7958
+ frameLength: len
7959
+ });
6787
7960
  }
6788
- if (audioData && chs > 0) {
7961
+ else {
7962
+ let chs = msg.data.chs;
7963
+ let audioData = new Array(chs);
7964
+ let linLevels = new Array(chs);
6789
7965
  for (let ch = 0; ch < chs; ch++) {
6790
- let chData = audioData[ch];
6791
- for (let s = 0; s < frameLength; s++) {
6792
- let bi = Math.floor(s / bufferFrameLength);
6793
- let lvlArrPos = bi * 2;
6794
- //let bs = s % bufferFrameLength;
6795
- let chSample = chData[s];
6796
- if (chSample < linLevels[ch][lvlArrPos]) {
6797
- linLevels[ch][lvlArrPos] = chSample;
6798
- }
6799
- lvlArrPos++;
6800
- if (chSample > linLevels[ch][lvlArrPos]) {
6801
- linLevels[ch][lvlArrPos] = chSample;
6802
- }
6803
- }
7966
+ audioData[ch] = new Float32Array(msg.data.audioData[ch]);
6804
7967
  }
6805
- const linLevelBufs = new Array(chs);
7968
+ //let frameLength = audioData[0].length;
7969
+ let bufferCount = Math.ceil(len / bufferFrameLength);
6806
7970
  for (let ch = 0; ch < chs; ch++) {
6807
- linLevelBufs[ch] = linLevels[ch].buffer;
7971
+ linLevels[ch] = new Float32Array(bufferCount * 2);
7972
+ }
7973
+ if (audioData && chs > 0) {
7974
+ for (let ch = 0; ch < chs; ch++) {
7975
+ let chData = audioData[ch];
7976
+ for (let s = 0; s < len; s++) {
7977
+ let bi = Math.floor(s / bufferFrameLength);
7978
+ let lvlArrPos = bi * 2;
7979
+ //let bs = s % bufferFrameLength;
7980
+ let chSample = chData[s];
7981
+ if (chSample < linLevels[ch][lvlArrPos]) {
7982
+ linLevels[ch][lvlArrPos] = chSample;
7983
+ }
7984
+ lvlArrPos++;
7985
+ if (chSample > linLevels[ch][lvlArrPos]) {
7986
+ linLevels[ch][lvlArrPos] = chSample;
7987
+ }
7988
+ }
7989
+ }
7990
+ const linLevelBufs = new Array(chs);
7991
+ for (let ch = 0; ch < chs; ch++) {
7992
+ linLevelBufs[ch] = linLevels[ch].buffer;
7993
+ }
7994
+ postMessage({
7995
+ eod: false,
7996
+ bufferFrameLength: bufferFrameLength,
7997
+ frameLength: len,
7998
+ linLevelBuffers: linLevelBufs
7999
+ }, linLevelBufs);
6808
8000
  }
6809
- postMessage({ bufferFrameLength: bufferFrameLength, frameLength: frameLength, linLevelBuffers: linLevelBufs }, linLevelBufs);
6810
8001
  }
6811
8002
  };
6812
8003
  }
@@ -7182,7 +8373,7 @@ RecordingItemDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", v
7182
8373
  <audio-levelbar fxFlex="1 0 1" [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
7183
8374
  <spr-recordingitemcontrols fxFlex="0 0 0" [audioLoaded]="displayAudioBuffer!==null" [playStartAction]="playStartAction" [playStopAction]="playStopAction" [peakDbLvl]="peakDbLvl" [agc]="_agc" (onShowRecordingDetails)="onShowRecordingDetails.emit()"></spr-recordingitemcontrols>
7184
8375
  </div>
7185
- `, 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"] }] });
8376
+ `, 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"] }] });
7186
8377
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingItemDisplay, decorators: [{
7187
8378
  type: Component,
7188
8379
  args: [{
@@ -7228,89 +8419,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
7228
8419
  type: Input
7229
8420
  }] } });
7230
8421
 
7231
- class Float32ArrayChunkerOutStream {
7232
- constructor(outStream) {
7233
- this.outStream = outStream;
7234
- this.bufs = new Array();
7235
- this._channels = 0;
7236
- this._chunkSize = 0;
7237
- this.filled = 0;
7238
- this.receivedFrames = 0;
7239
- this.sentFrames = 0;
7240
- }
7241
- createBuffers() {
7242
- this.bufs = new Array(this._channels);
7243
- for (let ch = 0; ch < this._channels; ch++) {
7244
- this.bufs[ch] = new Float32Array(this._chunkSize);
7245
- }
7246
- }
7247
- set chunkSize(chunkSize) {
7248
- this._chunkSize = chunkSize;
7249
- this.createBuffers();
7250
- }
7251
- get chunkSize() {
7252
- return this._chunkSize;
7253
- }
7254
- set channels(channels) {
7255
- this._channels = channels;
7256
- this.createBuffers();
7257
- }
7258
- get channels() {
7259
- return this._channels;
7260
- }
7261
- available() {
7262
- return this._chunkSize - this.filled;
7263
- }
7264
- write(buffers) {
7265
- let copied = 0;
7266
- if (buffers.length > 0) {
7267
- let buffersLen = buffers[0].length;
7268
- this.receivedFrames += buffersLen;
7269
- let avail = buffersLen;
7270
- // Fill out buffers until all values copied
7271
- while (avail > 0) {
7272
- let toFill = this._chunkSize - this.filled;
7273
- if (toFill > avail) {
7274
- toFill = avail;
7275
- }
7276
- let sliceEnd = copied + toFill;
7277
- // Firefox on Android sends only the first channel
7278
- for (let ch = 0; ch < buffersLen; ch++) {
7279
- if (buffers[ch]) {
7280
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
7281
- let buf = this.bufs[ch];
7282
- buf.set(cpPrt, this.filled);
7283
- }
7284
- }
7285
- copied += toFill;
7286
- avail -= toFill;
7287
- this.filled += toFill;
7288
- if (this.filled == this._chunkSize) {
7289
- this.outStream.write(this.bufs);
7290
- this.sentFrames += this.filled;
7291
- this.filled = 0;
7292
- }
7293
- }
7294
- }
7295
- return copied;
7296
- }
7297
- flush() {
7298
- if (this.filled > 0) {
7299
- let restBufs = new Array(this._channels);
7300
- for (let ch = 0; ch < this._channels; ch++) {
7301
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
7302
- }
7303
- this.outStream.write(restBufs);
7304
- this.outStream.flush();
7305
- this.sentFrames += this.filled;
7306
- this.filled = 0;
7307
- }
7308
- }
7309
- close() {
7310
- this.outStream.close();
7311
- }
7312
- }
7313
-
7314
8422
  class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
7315
8423
  constructor(outStream, chunkDurationSeconds) {
7316
8424
  super(outStream);
@@ -7319,11 +8427,11 @@ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream
7319
8427
  this.sequenceAudioFloat32OutStream = outStream;
7320
8428
  }
7321
8429
  setFormat(channels, sampleRate) {
7322
- console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: " + channels + ",sampleRate: " + sampleRate + ")");
8430
+ //console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "+channels+",sampleRate: "+sampleRate+")")
7323
8431
  this.channels = channels;
7324
8432
  this.sampleRate = sampleRate;
7325
8433
  this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
7326
- console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: " + this.chunkSize);
8434
+ //console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "+this.chunkSize);
7327
8435
  this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
7328
8436
  }
7329
8437
  nextStream() {
@@ -7530,7 +8638,8 @@ class ChunkManager {
7530
8638
  }
7531
8639
  }
7532
8640
  let BasicRecorder = class BasicRecorder {
7533
- constructor(dialog, sessionService, uploader, config) {
8641
+ constructor(changeDetectorRef, dialog, sessionService, uploader, config) {
8642
+ this.changeDetectorRef = changeDetectorRef;
7534
8643
  this.dialog = dialog;
7535
8644
  this.sessionService = sessionService;
7536
8645
  this.uploader = uploader;
@@ -7545,6 +8654,7 @@ let BasicRecorder = class BasicRecorder {
7545
8654
  this._selectedDeviceId = undefined;
7546
8655
  this._channelCount = 2;
7547
8656
  this._session = null;
8657
+ this._recordingFile = null;
7548
8658
  this.startedDate = null;
7549
8659
  this.uploadProgress = 100;
7550
8660
  this.uploadStatus = 'ok';
@@ -7552,6 +8662,7 @@ let BasicRecorder = class BasicRecorder {
7552
8662
  this.peakLevelInDb = MIN_DB_LEVEL;
7553
8663
  this.displayAudioClip = null;
7554
8664
  this.audioFetchSubscription = null;
8665
+ this.audioFetching = false;
7555
8666
  this.destroyed = false;
7556
8667
  this.navigationDisabled = true;
7557
8668
  // Default: Disabled chunked upload
@@ -7646,6 +8757,28 @@ let BasicRecorder = class BasicRecorder {
7646
8757
  this.ac.audioOutStream = outStream;
7647
8758
  }
7648
8759
  }
8760
+ showRecording() {
8761
+ this._controlAudioPlayer.stop();
8762
+ if (this.displayAudioClip) {
8763
+ let dap = this.displayAudioClip;
8764
+ let adh = dap.audioDataHolder;
8765
+ if (adh) {
8766
+ this.levelMeasure.calcBufferLevelInfos(adh, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8767
+ dap.levelInfos = levelInfos;
8768
+ this.changeDetectorRef.detectChanges();
8769
+ });
8770
+ }
8771
+ this.playStartAction.disabled = false;
8772
+ }
8773
+ else {
8774
+ this.playStartAction.disabled = true;
8775
+ // Collapse audio signal display if open
8776
+ if (!this.audioSignalCollapsed) {
8777
+ this.audioSignalCollapsed = true;
8778
+ }
8779
+ }
8780
+ this.changeDetectorRef.detectChanges();
8781
+ }
7649
8782
  start() {
7650
8783
  this.statusAlertType = 'info';
7651
8784
  this.statusMsg = 'Starting session...';
@@ -7866,9 +8999,9 @@ let BasicRecorder = class BasicRecorder {
7866
8999
  }
7867
9000
  this.transportActions.startAction.disabled = true;
7868
9001
  }
7869
- postRecording(wavFile, recUrl) {
9002
+ postRecording(wavFile, recUrl, rf) {
7870
9003
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
7871
- let ul = new Upload(wavBlob, recUrl);
9004
+ let ul = new Upload(wavBlob, recUrl, rf);
7872
9005
  this.uploader.queueUpload(ul);
7873
9006
  }
7874
9007
  postAudioStreamStart() {
@@ -7904,42 +9037,334 @@ let BasicRecorder = class BasicRecorder {
7904
9037
  if (this.config && this.config.apiEndPoint) {
7905
9038
  apiEndPoint = this.config.apiEndPoint;
7906
9039
  }
7907
- if (apiEndPoint !== '') {
7908
- apiEndPoint = apiEndPoint + '/';
9040
+ if (apiEndPoint !== '') {
9041
+ apiEndPoint = apiEndPoint + '/';
9042
+ }
9043
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9044
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
9045
+ let fd = new FormData();
9046
+ fd.set('uuid', this.rfUuid);
9047
+ fd.set('chunkCount', chunkCount.toString());
9048
+ let ul = new Upload(fd, recUrl, this._recordingFile);
9049
+ this.uploader.queueUpload(ul);
9050
+ console.debug("Queued for upload: " + this._recordingFile);
9051
+ }
9052
+ else {
9053
+ console.error("Recording file UUID not set!");
9054
+ }
9055
+ }
9056
+ closed() {
9057
+ this.statusAlertType = 'info';
9058
+ this.statusMsg = 'Session closed.';
9059
+ }
9060
+ error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
9061
+ this.statusMsg = 'ERROR: Recording.';
9062
+ this.statusAlertType = 'error';
9063
+ this.dialog.open(MessageDialog, {
9064
+ data: {
9065
+ type: 'error',
9066
+ title: 'Recording error',
9067
+ msg: msg,
9068
+ advice: advice
9069
+ }
9070
+ });
9071
+ }
9072
+ };
9073
+ // Enable only for developemnt/debug purposes of array audio buffers !!
9074
+ BasicRecorder.FORCE_ARRRAY_AUDIO_BUFFER = false;
9075
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
9076
+ BasicRecorder = __decorate([
9077
+ __param(4, Inject(SPEECHRECORDER_CONFIG))
9078
+ ], BasicRecorder);
9079
+
9080
+ class AudioDataHolder {
9081
+ constructor(_buffer, _arrayBuffer = null) {
9082
+ this._buffer = _buffer;
9083
+ this._arrayBuffer = _arrayBuffer;
9084
+ this._numberOfChannels = 0;
9085
+ this._sampleRate = 0;
9086
+ this._frameLen = 0;
9087
+ this._duration = 0;
9088
+ if (this._buffer && this._arrayBuffer) {
9089
+ throw Error('Only one of either audio buffer or array audio buffer must be set!');
9090
+ }
9091
+ if (this._buffer || this._arrayBuffer) {
9092
+ if (this._buffer) {
9093
+ this._numberOfChannels = this._buffer.numberOfChannels;
9094
+ this._sampleRate = this._buffer.sampleRate;
9095
+ this._frameLen = this._buffer.length;
9096
+ this._duration = this._frameLen / this._sampleRate;
9097
+ }
9098
+ else if (this._arrayBuffer) {
9099
+ this._numberOfChannels = this._arrayBuffer.channelCount;
9100
+ this._sampleRate = this._arrayBuffer.sampleRate;
9101
+ this._frameLen = this._arrayBuffer.frameLen;
9102
+ this._duration = this._frameLen / this._sampleRate;
9103
+ }
9104
+ }
9105
+ else {
9106
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9107
+ }
9108
+ }
9109
+ get duration() {
9110
+ return this._duration;
9111
+ }
9112
+ get sampleRate() {
9113
+ return this._sampleRate;
9114
+ }
9115
+ get numberOfChannels() {
9116
+ return this._numberOfChannels;
9117
+ }
9118
+ get frameLen() {
9119
+ return this._frameLen;
9120
+ }
9121
+ sampleCounts() {
9122
+ return this._numberOfChannels * this._frameLen;
9123
+ }
9124
+ frames(framePos, frameLen, bufs) {
9125
+ let read = 0;
9126
+ if (this._buffer) {
9127
+ let toRead = frameLen;
9128
+ if (this._buffer.length < framePos + frameLen) {
9129
+ toRead = this._buffer.length - framePos;
9130
+ }
9131
+ for (let ch = 0; ch < bufs.length; ch++) {
9132
+ let chData = this._buffer.getChannelData(ch);
9133
+ for (let i = 0; i < toRead; i++) {
9134
+ bufs[ch][i] = chData[framePos + i];
9135
+ }
9136
+ }
9137
+ read = toRead;
9138
+ }
9139
+ else if (this._arrayBuffer) {
9140
+ read = this._arrayBuffer.frames(framePos, frameLen, bufs);
9141
+ }
9142
+ return read;
9143
+ }
9144
+ audioInputStream() {
9145
+ if (this._buffer) {
9146
+ return new AudioBufferInputStream(this._buffer);
9147
+ }
9148
+ if (this._arrayBuffer) {
9149
+ return new ArrayAudioBufferInputStream(this._arrayBuffer);
9150
+ }
9151
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9152
+ }
9153
+ get buffer() {
9154
+ return this._buffer;
9155
+ }
9156
+ get arrayBuffer() {
9157
+ return this._arrayBuffer;
9158
+ }
9159
+ }
9160
+ AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG = 'One of either audio buffer or array audio buffer must be set!';
9161
+ class AudioBufferInputStream {
9162
+ constructor(audioBuffer) {
9163
+ this.audioBuffer = audioBuffer;
9164
+ this.framePos = 0;
9165
+ }
9166
+ close() {
9167
+ }
9168
+ skipFrames(n) {
9169
+ this.framePos += n;
9170
+ }
9171
+ read(buffers) {
9172
+ let read = 0;
9173
+ let toRead = buffers[0].length;
9174
+ if (this.framePos + toRead > this.audioBuffer.length) {
9175
+ toRead = this.audioBuffer.length - this.framePos;
9176
+ }
9177
+ for (let ch = 0; ch < buffers.length; ch++) {
9178
+ for (let i = 0; i < toRead; i++) {
9179
+ buffers[ch][i] = this.audioBuffer.getChannelData(ch)[this.framePos + i];
9180
+ }
9181
+ }
9182
+ read = toRead;
9183
+ this.framePos += toRead;
9184
+ return read;
9185
+ }
9186
+ }
9187
+
9188
+ class BasicRecFilesCache {
9189
+ constructor() {
9190
+ //public static readonly DEFAULT_MAX_SAMPLES=30*48000; // TEST 30 seconds
9191
+ this._sampleCount = 0;
9192
+ this.maxSampleCount = SprItemsCache.DEFAULT_MAX_SAMPLES;
9193
+ this.currentRecordingFile = null;
9194
+ }
9195
+ }
9196
+ BasicRecFilesCache.DEBUG = false;
9197
+ BasicRecFilesCache.DEFAULT_MAX_SAMPLES = 10 * 60 * 48000; // 20 Minutes mono 48kHz
9198
+ class SprItemsCache extends BasicRecFilesCache {
9199
+ constructor() {
9200
+ super(...arguments);
9201
+ this._items = new Array();
9202
+ }
9203
+ get items() {
9204
+ return this._items;
9205
+ }
9206
+ addItem(item) {
9207
+ this._items.push(item);
9208
+ }
9209
+ getItem(index) {
9210
+ return this._items[index];
9211
+ }
9212
+ length() {
9213
+ return this._items.length;
9214
+ }
9215
+ tryExpire(toBeExpiredRf) {
9216
+ if (BasicRecFilesCache.DEBUG)
9217
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " try expire:");
9218
+ if (!toBeExpiredRf.equals(this.currentRecordingFile)) {
9219
+ if (BasicRecFilesCache.DEBUG)
9220
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " not current file...");
9221
+ if (toBeExpiredRf.serverPersisted) {
9222
+ if (BasicRecFilesCache.DEBUG)
9223
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is server persisted...");
9224
+ // expire recording files first stored to the cache
9225
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9226
+ if (expiredAudio) {
9227
+ this._sampleCount -= expiredAudio.sampleCounts();
9228
+ if (BasicRecFilesCache.DEBUG)
9229
+ console.debug("Rec. files cache: Expired: " + toBeExpiredRf.toString() + ". Cache samples now: " + this._sampleCount);
9230
+ }
9231
+ }
9232
+ else {
9233
+ if (BasicRecFilesCache.DEBUG)
9234
+ console.debug("Rec. files cache: #" + toBeExpiredRf.toString() + " not yet persisted on server.");
7909
9235
  }
7910
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
7911
- let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
7912
- let fd = new FormData();
7913
- fd.set('uuid', this.rfUuid);
7914
- fd.set('chunkCount', chunkCount.toString());
7915
- let ul = new Upload(fd, recUrl);
7916
- this.uploader.queueUpload(ul);
7917
9236
  }
7918
9237
  else {
7919
- console.error("Recording file UUID not set!");
9238
+ if (BasicRecFilesCache.DEBUG)
9239
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is current file.");
9240
+ }
9241
+ }
9242
+ expire() {
9243
+ // expire corrected versions first
9244
+ if (BasicRecFilesCache.DEBUG)
9245
+ console.debug("Rec. files cache: Expire? current: " + this._sampleCount + ", max: " + this.maxSampleCount);
9246
+ if (this._sampleCount > this.maxSampleCount) {
9247
+ // expire older versions of an item first
9248
+ for (let ii = 0; ii < this._items.length; ii++) {
9249
+ if (this._sampleCount <= this.maxSampleCount) {
9250
+ break;
9251
+ }
9252
+ let it = this._items[ii];
9253
+ let itRfs = it.recs;
9254
+ if (itRfs && itRfs.length > 1) {
9255
+ for (let rfi = 0; rfi < itRfs.length - 1; rfi++) {
9256
+ if (this._sampleCount <= this.maxSampleCount) {
9257
+ break;
9258
+ }
9259
+ let toBeExpiredRf = itRfs[rfi];
9260
+ this.tryExpire(toBeExpiredRf);
9261
+ }
9262
+ }
9263
+ }
9264
+ // if that's not sufficient expire audio data of latest versions as well
9265
+ for (let ii = 0; ii < this._items.length; ii++) {
9266
+ if (this._sampleCount <= this.maxSampleCount) {
9267
+ break;
9268
+ }
9269
+ let it = this._items[ii];
9270
+ let itRfs = it.recs;
9271
+ if (itRfs && itRfs.length > 0) {
9272
+ for (let rfi = 0; rfi < itRfs.length; rfi++) {
9273
+ if (this._sampleCount <= this.maxSampleCount) {
9274
+ break;
9275
+ }
9276
+ let toBeExpiredRf = itRfs[rfi];
9277
+ this.tryExpire(toBeExpiredRf);
9278
+ }
9279
+ }
9280
+ }
7920
9281
  }
7921
9282
  }
7922
- closed() {
7923
- this.statusAlertType = 'info';
7924
- this.statusMsg = 'Session closed.';
9283
+ addSprRecFile(item, sprRecFile) {
9284
+ this.expire();
9285
+ if (!item.recs) {
9286
+ item.recs = new Array();
9287
+ }
9288
+ item.recs.push(sprRecFile);
9289
+ this._sampleCount += RecordingFileUtils.sampleCount(sprRecFile);
9290
+ if (BasicRecFilesCache.DEBUG)
9291
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
7925
9292
  }
7926
- error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
7927
- this.statusMsg = 'ERROR: Recording.';
7928
- this.statusAlertType = 'error';
7929
- this.dialog.open(MessageDialog, {
7930
- data: {
7931
- type: 'error',
7932
- title: 'Recording error',
7933
- msg: msg,
7934
- advice: advice
9293
+ setSprRecFileAudioData(sprRecFile, adh) {
9294
+ this.expire();
9295
+ if (BasicRecFilesCache.DEBUG)
9296
+ console.debug("Rec. files cache: Set audio data after expire. Cache samples: " + this._sampleCount);
9297
+ let currSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9298
+ this._sampleCount -= currSampleCnt;
9299
+ if (BasicRecFilesCache.DEBUG)
9300
+ console.debug("Rec. files cache: Set audio data subtracted curr sample count: " + currSampleCnt + ". Cache samples: " + this._sampleCount);
9301
+ RecordingFileUtils.setAudioData(sprRecFile, adh);
9302
+ let newSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9303
+ this._sampleCount += newSampleCnt;
9304
+ if (BasicRecFilesCache.DEBUG)
9305
+ console.debug("Rec. files cache: Set audio data added new sample count: " + newSampleCnt + ". Cache samples: " + this._sampleCount);
9306
+ let fl = adh === null || adh === void 0 ? void 0 : adh.frameLen;
9307
+ if (fl) {
9308
+ sprRecFile.frames = fl;
9309
+ }
9310
+ if (BasicRecFilesCache.DEBUG)
9311
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9312
+ }
9313
+ }
9314
+ class RecFilesCache extends BasicRecFilesCache {
9315
+ constructor() {
9316
+ super(...arguments);
9317
+ this._recFiles = new Array();
9318
+ }
9319
+ get recFiles() {
9320
+ return this._recFiles;
9321
+ }
9322
+ getRecFile(index) {
9323
+ return this._recFiles[index];
9324
+ }
9325
+ length() {
9326
+ return this._recFiles.length;
9327
+ }
9328
+ expire() {
9329
+ if (this._sampleCount > this.maxSampleCount) {
9330
+ // audio recorder list is sorted: lower index is newer
9331
+ for (let rfI = this._recFiles.length - 1; rfI >= 0; rfI--) {
9332
+ if (this._sampleCount <= this.maxSampleCount) {
9333
+ break;
9334
+ }
9335
+ let toBeExpiredRf = this._recFiles[rfI];
9336
+ if (toBeExpiredRf.serverPersisted) {
9337
+ // expire recording files first stored to the cache
9338
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9339
+ if (expiredAudio) {
9340
+ this._sampleCount -= expiredAudio.sampleCounts();
9341
+ if (BasicRecFilesCache.DEBUG)
9342
+ console.debug("Rec. files cache: Expired #" + rfI + ". Cache samples: " + this._sampleCount);
9343
+ }
9344
+ }
9345
+ else {
9346
+ if (BasicRecFilesCache.DEBUG)
9347
+ console.debug("Rec. files cache: #" + rfI + " not yet server persisted.");
9348
+ }
7935
9349
  }
7936
- });
9350
+ }
7937
9351
  }
7938
- };
7939
- BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7940
- BasicRecorder = __decorate([
7941
- __param(3, Inject(SPEECHRECORDER_CONFIG))
7942
- ], BasicRecorder);
9352
+ addRecFile(recFile) {
9353
+ this.expire();
9354
+ this._recFiles.push(recFile);
9355
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9356
+ if (BasicRecFilesCache.DEBUG)
9357
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
9358
+ }
9359
+ setRecFileAudioData(recFile, adh) {
9360
+ this.expire();
9361
+ this._sampleCount -= RecordingFileUtils.sampleCount(recFile);
9362
+ RecordingFileUtils.setAudioData(recFile, adh);
9363
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9364
+ if (BasicRecFilesCache.DEBUG)
9365
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9366
+ }
9367
+ }
7943
9368
 
7944
9369
  /**
7945
9370
  * Created by klausj on 17.06.2017.
@@ -8032,16 +9457,65 @@ class RecordingService {
8032
9457
  let recUrl = recFilesUrl + '/' + encItemcode + '/' + version;
8033
9458
  return this.audioRequest(recUrl);
8034
9459
  }
9460
+ fetchRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9461
+ let wobs = new Observable(observer => {
9462
+ let recFileId = recordingFile.recordingFileId;
9463
+ if (!recFileId) {
9464
+ recFileId = recordingFile.uuid;
9465
+ }
9466
+ if (recordingFile.session && recFileId) {
9467
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
9468
+ obs.subscribe({
9469
+ next: resp => {
9470
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9471
+ if (resp.body) {
9472
+ aCtx.decodeAudioData(resp.body, ab => {
9473
+ observer.next(ab);
9474
+ observer.complete();
9475
+ }, error => {
9476
+ observer.error(error);
9477
+ observer.complete();
9478
+ });
9479
+ }
9480
+ else {
9481
+ observer.error('Fetching audio file: response has no body');
9482
+ }
9483
+ },
9484
+ error: (error) => {
9485
+ if (error.status == 404) {
9486
+ // Interpret not as an error, the file ist not recorded yet
9487
+ observer.next(null);
9488
+ observer.complete();
9489
+ }
9490
+ else {
9491
+ // all other states are errors
9492
+ observer.error(error);
9493
+ observer.complete();
9494
+ }
9495
+ }
9496
+ });
9497
+ }
9498
+ else {
9499
+ observer.error();
9500
+ }
9501
+ });
9502
+ return wobs;
9503
+ }
8035
9504
  fetchAndApplyRecordingFile(aCtx, projectName, recordingFile) {
8036
9505
  let wobs = new Observable(observer => {
8037
- if (recordingFile.session && recordingFile.recordingFileId) {
8038
- let obs = this.fetchAudiofile(projectName, recordingFile.session, recordingFile.recordingFileId);
9506
+ let recFileId = recordingFile.recordingFileId;
9507
+ if (!recFileId) {
9508
+ recFileId = recordingFile.uuid;
9509
+ }
9510
+ if (recordingFile.session && recFileId) {
9511
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
8039
9512
  obs.subscribe(resp => {
8040
9513
  //console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
8041
9514
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8042
9515
  if (resp.body) {
8043
9516
  aCtx.decodeAudioData(resp.body, ab => {
8044
- recordingFile.audioBuffer = ab;
9517
+ let adh = new AudioDataHolder(ab, null);
9518
+ RecordingFileUtils.setAudioData(recordingFile, adh);
8045
9519
  if (this.debugDelay > 0) {
8046
9520
  window.setTimeout(() => {
8047
9521
  observer.next(recordingFile);
@@ -8079,6 +9553,47 @@ class RecordingService {
8079
9553
  });
8080
9554
  return wobs;
8081
9555
  }
9556
+ fetchSprRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9557
+ let wobs = new Observable(observer => {
9558
+ if (recordingFile.session) {
9559
+ let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
9560
+ obs.subscribe({
9561
+ next: resp => {
9562
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9563
+ if (resp.body) {
9564
+ aCtx.decodeAudioData(resp.body, ab => {
9565
+ //RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
9566
+ observer.next(ab);
9567
+ observer.complete();
9568
+ }, error => {
9569
+ observer.error(error);
9570
+ observer.complete();
9571
+ });
9572
+ }
9573
+ else {
9574
+ observer.error('Fetching audio file: response has no body');
9575
+ }
9576
+ },
9577
+ error: (error) => {
9578
+ if (error.status == 404) {
9579
+ // Interpret not as an error, the file ist not recorded yet
9580
+ observer.next(null);
9581
+ observer.complete();
9582
+ }
9583
+ else {
9584
+ // all other states are errors
9585
+ observer.error(error);
9586
+ observer.complete();
9587
+ }
9588
+ }
9589
+ });
9590
+ }
9591
+ else {
9592
+ observer.error();
9593
+ }
9594
+ });
9595
+ return wobs;
9596
+ }
8082
9597
  fetchAndApplySprRecordingFile(aCtx, projectName, recordingFile) {
8083
9598
  let wobs = new Observable(observer => {
8084
9599
  if (recordingFile.session) {
@@ -8088,7 +9603,7 @@ class RecordingService {
8088
9603
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8089
9604
  if (resp.body) {
8090
9605
  aCtx.decodeAudioData(resp.body, ab => {
8091
- recordingFile.audioBuffer = ab;
9606
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab, null));
8092
9607
  if (this.debugDelay > 0) {
8093
9608
  window.setTimeout(() => {
8094
9609
  observer.next(recordingFile);
@@ -8133,7 +9648,8 @@ class RecordingService {
8133
9648
  // Do not use Promise version, which does not work with Safari 13
8134
9649
  if (resp.body) {
8135
9650
  aCtx.decodeAudioData(resp.body, ab => {
8136
- let rf = new SprRecordingFile(sessId, itemcode, version, ab);
9651
+ let adh = new AudioDataHolder(ab, null);
9652
+ let rf = new SprRecordingFile(sessId, itemcode, version, adh);
8137
9653
  if (this.debugDelay > 0) {
8138
9654
  window.setTimeout(() => {
8139
9655
  observer.next(rf);
@@ -8253,14 +9769,9 @@ const DEFAULT_PRE_REC_DELAY = 1000;
8253
9769
  const DEFAULT_POST_REC_DELAY = 500;
8254
9770
  class SessionManager extends BasicRecorder {
8255
9771
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
8256
- super(dialog, sessionService, uploader, config);
8257
- this.changeDetectorRef = changeDetectorRef;
9772
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
8258
9773
  this.renderer = renderer;
8259
- this.dialog = dialog;
8260
- this.sessionService = sessionService;
8261
9774
  this.recFileService = recFileService;
8262
- this.uploader = uploader;
8263
- this.config = config;
8264
9775
  this.enableUploadRecordings = true;
8265
9776
  this.enableDownloadRecordings = false;
8266
9777
  this.status = 0 /* BLOCKED */;
@@ -8294,7 +9805,7 @@ class SessionManager extends BasicRecorder {
8294
9805
  };
8295
9806
  }
8296
9807
  ngOnDestroy() {
8297
- console.debug("Com destroy, disable wake lock.");
9808
+ //console.debug("Com destroy, disable wake lock.")
8298
9809
  this.disableWakeLockCond();
8299
9810
  this.destroyed = true;
8300
9811
  // TODO stop capture /playback
@@ -8425,7 +9936,7 @@ class SessionManager extends BasicRecorder {
8425
9936
  progressPercentValue() {
8426
9937
  let v = 100;
8427
9938
  if (this.items) {
8428
- v = this.promptIndex * 100 / (this.items.length - 1);
9939
+ v = this.promptIndex * 100 / (this.items.length() - 1);
8429
9940
  }
8430
9941
  return v;
8431
9942
  }
@@ -8439,9 +9950,6 @@ class SessionManager extends BasicRecorder {
8439
9950
  return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
8440
9951
  }
8441
9952
  set controlAudioPlayer(controlAudioPlayer) {
8442
- if (this._controlAudioPlayer) {
8443
- //this._controlAudioPlayer.listener=null;
8444
- }
8445
9953
  this._controlAudioPlayer = controlAudioPlayer;
8446
9954
  if (this._controlAudioPlayer) {
8447
9955
  this._controlAudioPlayer.listener = this;
@@ -8526,6 +10034,8 @@ class SessionManager extends BasicRecorder {
8526
10034
  this.autorecording = true;
8527
10035
  }
8528
10036
  if (this.ac != null) {
10037
+ // Hide loading hint on livelevel display
10038
+ this.audioFetching = false;
8529
10039
  if (!this.ac.opened) {
8530
10040
  if (this._selectedDeviceId) {
8531
10041
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -8542,7 +10052,7 @@ class SessionManager extends BasicRecorder {
8542
10052
  }
8543
10053
  loadScript() {
8544
10054
  this.promptItemCount = 0;
8545
- this.items = new Array();
10055
+ this.items = new SprItemsCache();
8546
10056
  let ln = 0;
8547
10057
  //TODO randomize not supported
8548
10058
  for (let si = 0; si < this._script.sections.length; si++) {
@@ -8556,7 +10066,7 @@ class SessionManager extends BasicRecorder {
8556
10066
  let pi = pis[piSectIdx];
8557
10067
  let promptAsStr = PromptitemUtil.toPlainTextString(pi);
8558
10068
  let it = new Item$1(promptAsStr, section.training, (!pi.type || pi.type === 'recording'));
8559
- this.items.push(it);
10069
+ this.items.addItem(it);
8560
10070
  ln++;
8561
10071
  }
8562
10072
  }
@@ -8597,10 +10107,10 @@ class SessionManager extends BasicRecorder {
8597
10107
  }
8598
10108
  downloadRecording() {
8599
10109
  if (this.displayRecFile) {
8600
- let ab = this.displayRecFile.audioBuffer;
10110
+ let ab = this.displayRecFile.audioDataHolder;
8601
10111
  let ww = new WavWriter();
8602
- if (ab) {
8603
- let wavFile = ww.writeAsync(ab, (wavFile) => {
10112
+ if (ab === null || ab === void 0 ? void 0 : ab.buffer) {
10113
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
8604
10114
  let blob = new Blob([wavFile], { type: 'audio/wav' });
8605
10115
  let rfUrl = URL.createObjectURL(blob);
8606
10116
  let dataDnlLnk = this.renderer.createElement('a');
@@ -8623,7 +10133,10 @@ class SessionManager extends BasicRecorder {
8623
10133
  set displayRecFile(displayRecFile) {
8624
10134
  this._displayRecFile = displayRecFile;
8625
10135
  if (this._displayRecFile) {
8626
- let ab = this._displayRecFile.audioBuffer;
10136
+ if (this.items) {
10137
+ this.items.currentRecordingFile = this._displayRecFile;
10138
+ }
10139
+ let ab = this._displayRecFile.audioDataHolder;
8627
10140
  if (ab) {
8628
10141
  this.displayAudioClip = new AudioClip(ab);
8629
10142
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
@@ -8631,27 +10144,46 @@ class SessionManager extends BasicRecorder {
8631
10144
  else {
8632
10145
  // clear for now ...
8633
10146
  this.displayAudioClip = null;
8634
- this.controlAudioPlayer.audioClip = null;
10147
+ if (this.controlAudioPlayer) {
10148
+ this.controlAudioPlayer.audioClip = null;
10149
+ }
8635
10150
  if (this._controlAudioPlayer && this._session) {
8636
10151
  //... and try to fetch from server
8637
- this.audioFetchSubscription = this.recFileService.fetchAndApplySprRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
8638
- let fab = null;
8639
- if (rf && this._displayRecFile) {
8640
- fab = this._displayRecFile.audioBuffer;
8641
- }
8642
- else {
8643
- this.statusMsg = 'Recording file could not be loaded.';
10152
+ this.audioFetching = true;
10153
+ let rf = this._displayRecFile;
10154
+ this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
10155
+ next: (ab) => {
10156
+ this.audioFetching = false;
10157
+ let fabDh = null;
10158
+ if (ab) {
10159
+ if (rf && this.items) {
10160
+ if (SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10161
+ let aab = ArrayAudioBuffer.fromAudioBuffer(ab);
10162
+ fabDh = new AudioDataHolder(null, aab);
10163
+ }
10164
+ else {
10165
+ fabDh = new AudioDataHolder(ab);
10166
+ }
10167
+ this.items.setSprRecFileAudioData(rf, fabDh);
10168
+ }
10169
+ }
10170
+ else {
10171
+ // Should actually be handled by the error resolver
10172
+ this.statusMsg = 'Recording file could not be loaded.';
10173
+ this.statusAlertType = 'error';
10174
+ }
10175
+ if (fabDh) {
10176
+ // 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
10177
+ this.displayAudioClip = new AudioClip(fabDh);
10178
+ }
10179
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
10180
+ this.showRecording();
10181
+ }, error: err => {
10182
+ console.error("Could not load recording file from server: " + err);
10183
+ this.audioFetching = false;
10184
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
8644
10185
  this.statusAlertType = 'error';
8645
10186
  }
8646
- if (fab) {
8647
- this.displayAudioClip = new AudioClip(fab);
8648
- }
8649
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
8650
- this.showRecording();
8651
- }, err => {
8652
- console.error("Could not load recording file from server: " + err);
8653
- this.statusMsg = 'Recording file could not be loaded: ' + err;
8654
- this.statusAlertType = 'error';
8655
10187
  });
8656
10188
  }
8657
10189
  else {
@@ -8662,31 +10194,14 @@ class SessionManager extends BasicRecorder {
8662
10194
  }
8663
10195
  else {
8664
10196
  this.displayAudioClip = null;
8665
- this.controlAudioPlayer.audioClip = null;
10197
+ if (this.controlAudioPlayer) {
10198
+ this.controlAudioPlayer.audioClip = null;
10199
+ }
8666
10200
  }
8667
10201
  }
8668
10202
  get displayRecFile() {
8669
10203
  return this._displayRecFile;
8670
10204
  }
8671
- showRecording() {
8672
- this.controlAudioPlayer.stop();
8673
- if (this.displayAudioClip) {
8674
- let dap = this.displayAudioClip;
8675
- this.levelMeasure.calcBufferLevelInfos(dap.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8676
- dap.levelInfos = levelInfos;
8677
- this.changeDetectorRef.detectChanges();
8678
- });
8679
- this.playStartAction.disabled = false;
8680
- }
8681
- else {
8682
- this.playStartAction.disabled = true;
8683
- // Collapse audio signal display if open
8684
- if (!this.audioSignalCollapsed) {
8685
- this.audioSignalCollapsed = true;
8686
- }
8687
- }
8688
- this.changeDetectorRef.detectChanges();
8689
- }
8690
10205
  isRecordingItem() {
8691
10206
  return (this.promptItem != null && this.promptItem.type !== 'nonrecording');
8692
10207
  }
@@ -8701,6 +10216,7 @@ class SessionManager extends BasicRecorder {
8701
10216
  if (this.audioFetchSubscription) {
8702
10217
  this.audioFetchSubscription.unsubscribe();
8703
10218
  }
10219
+ this.audioFetching = false;
8704
10220
  this.clearPrompt();
8705
10221
  let isNonrecording = (this.promptItem.type === 'nonrecording');
8706
10222
  if (isNonrecording || !this.section.promptphase || this.section.promptphase === 'IDLE') {
@@ -8713,7 +10229,7 @@ class SessionManager extends BasicRecorder {
8713
10229
  }
8714
10230
  else {
8715
10231
  if (this.items) {
8716
- let it = this.items[this.promptIndex];
10232
+ let it = this.items.getItem(this.promptIndex);
8717
10233
  if (!it.recs) {
8718
10234
  it.recs = new Array();
8719
10235
  }
@@ -8774,7 +10290,7 @@ class SessionManager extends BasicRecorder {
8774
10290
  updateNavigationActions() {
8775
10291
  let fwdNxtActionDisabled = true;
8776
10292
  if (this.items) {
8777
- let currRecs = this.items[this._promptIndex].recs;
10293
+ let currRecs = this.items.getItem(this._promptIndex).recs;
8778
10294
  fwdNxtActionDisabled = !(currRecs != null && currRecs.length > 0);
8779
10295
  }
8780
10296
  this.transportActions.fwdNextAction.disabled = this.navigationDisabled || fwdNxtActionDisabled;
@@ -8788,10 +10304,10 @@ class SessionManager extends BasicRecorder {
8788
10304
  newPrIdx = 0;
8789
10305
  }
8790
10306
  if (this.items != null) {
8791
- let itRecs = this.items[newPrIdx].recs;
10307
+ let itRecs = this.items.getItem(newPrIdx).recs;
8792
10308
  while (itRecs != null && (itRecs.length > 0) && newPrIdx < this.promptItemCount) {
8793
10309
  newPrIdx++;
8794
- itRecs = this.items[newPrIdx].recs;
10310
+ itRecs = this.items.getItem(newPrIdx).recs;
8795
10311
  }
8796
10312
  if (!itRecs || itRecs.length == 0) {
8797
10313
  this.promptIndex = newPrIdx;
@@ -8812,6 +10328,23 @@ class SessionManager extends BasicRecorder {
8812
10328
  }
8813
10329
  }
8814
10330
  started() {
10331
+ let ic = this.promptItem.itemcode;
10332
+ let rf = null;
10333
+ if (this._session && ic) {
10334
+ let sessId = this._session.sessionId;
10335
+ let cpIdx = this.promptIndex;
10336
+ if (this.items) {
10337
+ let it = this.items.getItem(cpIdx);
10338
+ if (!it.recs) {
10339
+ it.recs = new Array();
10340
+ }
10341
+ rf = new SprRecordingFile(sessId, ic, it.recs.length, null);
10342
+ //it.recs.push(rf);
10343
+ this.items.addSprRecFile(it, rf);
10344
+ this.items.currentRecordingFile = rf;
10345
+ }
10346
+ }
10347
+ this._recordingFile = rf;
8815
10348
  this.status = 3 /* PRE_RECORDING */;
8816
10349
  super.started();
8817
10350
  this.startStopSignalState = 1 /* PRERECORDING */;
@@ -8938,13 +10471,14 @@ class SessionManager extends BasicRecorder {
8938
10471
  if (rfd.recording && rfd.recording.itemcode) {
8939
10472
  let prIdx = this.promptIndexByItemcode(rfd.recording.itemcode);
8940
10473
  if (this.items != null && prIdx !== null) {
8941
- let it = this.items[prIdx];
10474
+ let it = this.items.getItem(prIdx);
8942
10475
  if (it && this._session) {
8943
- if (!it.recs) {
8944
- it.recs = new Array();
8945
- }
10476
+ // if (!it.recs) {
10477
+ // it.recs = new Array<SprRecordingFile>();
10478
+ // }
8946
10479
  let rf = new SprRecordingFile(this._session.sessionId, rfd.recording.itemcode, rfd.version, null);
8947
- it.recs[rfd.version] = rf;
10480
+ rf.serverPersisted = true;
10481
+ this.items.addSprRecFile(it, rf);
8948
10482
  }
8949
10483
  else {
8950
10484
  //console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
@@ -8958,6 +10492,7 @@ class SessionManager extends BasicRecorder {
8958
10492
  addRecordingFileByPromptIndex(promptIndex, rf) {
8959
10493
  }
8960
10494
  stopped() {
10495
+ var _a;
8961
10496
  this.updateStartActionDisableState();
8962
10497
  this.transportActions.stopAction.disabled = true;
8963
10498
  this.transportActions.nextAction.disabled = true;
@@ -8966,22 +10501,24 @@ class SessionManager extends BasicRecorder {
8966
10501
  this.statusMsg = 'Recorded.';
8967
10502
  this.startStopSignalState = 0 /* IDLE */;
8968
10503
  let ad = null;
10504
+ let ada = null;
10505
+ let adh = null;
10506
+ let frameLen = 0;
8969
10507
  if (this.ac != null) {
8970
- ad = this.ac.audioBuffer();
8971
- }
8972
- let ic = this.promptItem.itemcode;
8973
- let rf = null;
8974
- if (this._session && ic) {
8975
- let sessId = this._session.sessionId;
8976
- let cpIdx = this.promptIndex;
8977
- if (this.items) {
8978
- let it = this.items[cpIdx];
8979
- if (!it.recs) {
8980
- it.recs = new Array();
8981
- }
8982
- rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8983
- it.recs.push(rf);
10508
+ if (this.uploadChunkSizeSeconds || SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10509
+ ada = this.ac.audioBufferArray();
10510
+ frameLen = ada.frameLen;
8984
10511
  }
10512
+ else {
10513
+ ad = this.ac.audioBuffer();
10514
+ frameLen = ad.length;
10515
+ }
10516
+ adh = new AudioDataHolder(ad, ada);
10517
+ }
10518
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
10519
+ let rf = this._recordingFile;
10520
+ if (rf && rf instanceof SprRecordingFile) {
10521
+ (_a = this.items) === null || _a === void 0 ? void 0 : _a.setSprRecFileAudioData(rf, adh);
8985
10522
  if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8986
10523
  // TODO use SpeechRecorderconfig resp. RecfileService
8987
10524
  // convert asynchronously to 16-bit integer PCM
@@ -8989,7 +10526,7 @@ class SessionManager extends BasicRecorder {
8989
10526
  // TODO duplicate conversion for manual download
8990
10527
  //console.log("Build wav writer...");
8991
10528
  this.processingRecording = true;
8992
- if (ad && rf) {
10529
+ if (ad) {
8993
10530
  let ww = new WavWriter();
8994
10531
  //new REST API URL
8995
10532
  let apiEndPoint = '';
@@ -9002,7 +10539,7 @@ class SessionManager extends BasicRecorder {
9002
10539
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9003
10540
  let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.itemCode;
9004
10541
  ww.writeAsync(ad, (wavFile) => {
9005
- this.postRecording(wavFile, recUrl);
10542
+ this.postRecording(wavFile, recUrl, rf);
9006
10543
  this.processingRecording = false;
9007
10544
  this.updateWakeLock();
9008
10545
  });
@@ -9013,8 +10550,8 @@ class SessionManager extends BasicRecorder {
9013
10550
  let complete = true;
9014
10551
  if (this.items) {
9015
10552
  // search backwards, to gain faster detection of incomplete state
9016
- for (let ri = this.items.length - 1; ri >= 0; ri--) {
9017
- let it = this.items[ri];
10553
+ for (let ri = this.items.length() - 1; ri >= 0; ri--) {
10554
+ let it = this.items.getItem(ri);
9018
10555
  if (it.recording && !it.training && (!it.recs || it.recs.length == 0)) {
9019
10556
  complete = false;
9020
10557
  break;
@@ -9079,13 +10616,13 @@ class SessionManager extends BasicRecorder {
9079
10616
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9080
10617
  let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
9081
10618
  ww.writeAsync(audioBuffer, (wavFile) => {
9082
- this.postRecording(wavFile, recUrl);
10619
+ this.postRecording(wavFile, recUrl, null);
9083
10620
  this.processingRecording = false;
9084
10621
  });
9085
10622
  }
9086
- postRecording(wavFile, recUrl) {
10623
+ postRecording(wavFile, recUrl, rf) {
9087
10624
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
9088
- let ul = new Upload(wavBlob, recUrl);
10625
+ let ul = new Upload(wavBlob, recUrl, rf);
9089
10626
  this.uploader.queueUpload(ul);
9090
10627
  }
9091
10628
  stop() {
@@ -9121,7 +10658,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9121
10658
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9122
10659
  <app-sprprompting [projectName]="projectName"
9123
10660
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9124
- [items]="items"
10661
+ [items]="items?.items"
9125
10662
  [transportActions]="transportActions"
9126
10663
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9127
10664
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9135,10 +10672,10 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9135
10672
 
9136
10673
 
9137
10674
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9138
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
10675
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9139
10676
  <div fxLayout="row">
9140
10677
  <spr-recordingitemcontrols fxFlex="10 0 1"
9141
- [audioLoaded]="displayAudioClip?.buffer!==null"
10678
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9142
10679
  [playStartAction]="controlAudioPlayer?.startAction"
9143
10680
  [playStopAction]="controlAudioPlayer?.stopAction"
9144
10681
  [peakDbLvl]="peakLevelInDb"
@@ -9156,7 +10693,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9156
10693
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9157
10694
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9158
10695
  </div>
9159
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10696
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9160
10697
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9161
10698
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9162
10699
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9164,7 +10701,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9164
10701
  <app-readystateindicator class="ricontrols" fxLayoutAlign="end center" fxHide.xs [ready]="dataSaved && !isActive()"></app-readystateindicator>
9165
10702
  </div>
9166
10703
  </div>
9167
- `, 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"] }] });
10704
+ `, 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"] }] });
9168
10705
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SessionManager, decorators: [{
9169
10706
  type: Component,
9170
10707
  args: [{
@@ -9175,7 +10712,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9175
10712
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9176
10713
  <app-sprprompting [projectName]="projectName"
9177
10714
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9178
- [items]="items"
10715
+ [items]="items?.items"
9179
10716
  [transportActions]="transportActions"
9180
10717
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9181
10718
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9189,10 +10726,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9189
10726
 
9190
10727
 
9191
10728
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9192
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
10729
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9193
10730
  <div fxLayout="row">
9194
10731
  <spr-recordingitemcontrols fxFlex="10 0 1"
9195
- [audioLoaded]="displayAudioClip?.buffer!==null"
10732
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9196
10733
  [playStartAction]="controlAudioPlayer?.startAction"
9197
10734
  [playStopAction]="controlAudioPlayer?.stopAction"
9198
10735
  [peakDbLvl]="peakLevelInDb"
@@ -9210,7 +10747,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9210
10747
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9211
10748
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9212
10749
  </div>
9213
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10750
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9214
10751
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9215
10752
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9216
10753
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9503,7 +11040,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
9503
11040
  this.uploadUpdate(ue);
9504
11041
  };
9505
11042
  window.addEventListener('beforeunload', (e) => {
9506
- console.debug("Before page unload event");
11043
+ //console.debug("Before page unload event");
9507
11044
  if (this.ready()) {
9508
11045
  return;
9509
11046
  }
@@ -9822,14 +11359,15 @@ class AudioDisplayPlayer {
9822
11359
  if (this.aCtx) {
9823
11360
  this.aCtx.decodeAudioData(data, (audioBuffer) => {
9824
11361
  //console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
9825
- this.audioClip = new AudioClip(audioBuffer);
11362
+ let adh = new AudioDataHolder(audioBuffer, null);
11363
+ this.audioClip = new AudioClip(adh);
9826
11364
  });
9827
11365
  }
9828
11366
  }
9829
- set audioData(audioBuffer) {
9830
- this.audioDisplayScrollPane.audioData = audioBuffer;
9831
- if (audioBuffer) {
9832
- let clip = new AudioClip(audioBuffer);
11367
+ set audioData(audioData) {
11368
+ this.audioDisplayScrollPane.audioData = audioData;
11369
+ if (audioData) {
11370
+ let clip = new AudioClip(audioData);
9833
11371
  if (this.ap) {
9834
11372
  this.ap.audioClip = clip;
9835
11373
  this.playStartAction.disabled = false;
@@ -9851,7 +11389,7 @@ class AudioDisplayPlayer {
9851
11389
  let audioData = null;
9852
11390
  let sel = null;
9853
11391
  if (audioClip) {
9854
- audioData = audioClip.buffer;
11392
+ audioData = audioClip.audioDataHolder;
9855
11393
  sel = audioClip.selection;
9856
11394
  if (this._audioClip) {
9857
11395
  this._audioClip.addSelectionObserver((ac) => {
@@ -10122,7 +11660,7 @@ class RecordingFileService {
10122
11660
  // Do not use Promise version, which does not work with Safari 13
10123
11661
  if (resp.body) {
10124
11662
  aCtx.decodeAudioData(resp.body, ab => {
10125
- recordingFile.audioBuffer = ab;
11663
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab));
10126
11664
  if (this.debugDelay > 0) {
10127
11665
  window.setTimeout(() => {
10128
11666
  observer.next(recordingFile);
@@ -10172,7 +11710,7 @@ class RecordingFileService {
10172
11710
  if (resp.body) {
10173
11711
  aCtx.decodeAudioData(resp.body, ab => {
10174
11712
  if (rf) {
10175
- rf.audioBuffer = ab;
11713
+ RecordingFileUtils.setAudioData(rf, new AudioDataHolder(ab));
10176
11714
  }
10177
11715
  else {
10178
11716
  observer.error('Recording file object null');
@@ -10223,7 +11761,8 @@ class RecordingFileService {
10223
11761
  if (resp.body) {
10224
11762
  aCtx.decodeAudioData(resp.body, ab => {
10225
11763
  if (rf) {
10226
- rf.audioBuffer = ab;
11764
+ let adh = new AudioDataHolder(ab);
11765
+ RecordingFileUtils.setAudioData(rf, adh);
10227
11766
  }
10228
11767
  else {
10229
11768
  observer.error('Recording file object null');
@@ -10266,7 +11805,7 @@ class RecordingFileService {
10266
11805
  // append UUID to make request URL unique to avoid localhost server caching
10267
11806
  recUrl = recUrl + '.json?requestUUID=' + UUID.generate();
10268
11807
  }
10269
- console.log("Path request URL: " + recUrl);
11808
+ //console.log("Path request URL: "+recUrl)
10270
11809
  return this.http.patch(recUrl, { editSampleRate: editSampleRate, editStartFrame: editStartFrame, editEndFrame: editEndFrame }, { withCredentials: this.withCredentials });
10271
11810
  }
10272
11811
  }
@@ -10286,6 +11825,7 @@ class RecordingFileMetaComponent {
10286
11825
  constructor() {
10287
11826
  this.sessionId = null;
10288
11827
  this._recordingFile = null;
11828
+ this.stateLoading = false;
10289
11829
  this.itemCode = null;
10290
11830
  this.uuid = null;
10291
11831
  }
@@ -10302,12 +11842,12 @@ class RecordingFileMetaComponent {
10302
11842
  }
10303
11843
  if (this.itemCode) {
10304
11844
  this.uuid = null;
10305
- console.debug("SprRecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11845
+ //console.debug("SprRecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10306
11846
  }
10307
11847
  else {
10308
11848
  this.itemCode = null;
10309
11849
  this.uuid = (_b = this._recordingFile) === null || _b === void 0 ? void 0 : _b.uuid;
10310
- console.debug("RecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11850
+ //console.debug("RecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10311
11851
  }
10312
11852
  }
10313
11853
  else {
@@ -10324,10 +11864,11 @@ class RecordingFileMetaComponent {
10324
11864
  }
10325
11865
  }
10326
11866
  RecordingFileMetaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10327
- 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: `
11867
+ 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: `
10328
11868
  <mat-card>
10329
11869
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10330
11870
  <mat-card-content>
11871
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10331
11872
  <table>
10332
11873
  <tr *ngIf="itemCode">
10333
11874
  <td>Itemcode:</td>
@@ -10357,7 +11898,7 @@ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10357
11898
  </table>
10358
11899
  </mat-card-content>
10359
11900
  </mat-card>
10360
- `, 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"] }] });
11901
+ `, 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"] }] });
10361
11902
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, decorators: [{
10362
11903
  type: Component,
10363
11904
  args: [{
@@ -10366,6 +11907,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10366
11907
  <mat-card>
10367
11908
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10368
11909
  <mat-card-content>
11910
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10369
11911
  <table>
10370
11912
  <tr *ngIf="itemCode">
10371
11913
  <td>Itemcode:</td>
@@ -10400,6 +11942,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10400
11942
  }]
10401
11943
  }], propDecorators: { sessionId: [{
10402
11944
  type: Input
11945
+ }], stateLoading: [{
11946
+ type: Input
10403
11947
  }], recordingFile: [{
10404
11948
  type: Input
10405
11949
  }] } });
@@ -10542,6 +12086,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10542
12086
  this.recordingFileVersion = null;
10543
12087
  this.routedByQueryParam = false;
10544
12088
  this.posInList = null;
12089
+ this.audioFetching = false;
10545
12090
  this.naviInfoLoading = false;
10546
12091
  this.parentE = this.eRef.nativeElement;
10547
12092
  this.firstAction = new Action('First');
@@ -10713,18 +12258,20 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10713
12258
  this.updateActions();
10714
12259
  let audioContext = AudioContextProvider.audioContextInstance();
10715
12260
  if (audioContext) {
12261
+ this.audioFetching = true;
10716
12262
  this.recordingFileService.fetchSprRecordingFile(audioContext, rfId).subscribe(value => {
12263
+ this.audioFetching = false;
10717
12264
  this.status = 'Audio file loaded.';
10718
12265
  let clip = null;
10719
12266
  this.recordingFile = value;
10720
12267
  if (this.recordingFile) {
10721
- let ab = this.recordingFile.audioBuffer;
12268
+ let ab = this.recordingFile.audioDataHolder;
10722
12269
  if (ab) {
10723
12270
  clip = new AudioClip(ab);
10724
12271
  let esffsr = null;
10725
12272
  let eeffsr = null;
10726
12273
  let esr = null;
10727
- if (clip.buffer != null) {
12274
+ if (clip.audioDataHolder != null) {
10728
12275
  esr = ab.sampleRate;
10729
12276
  if (esr != null) {
10730
12277
  esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
@@ -10736,8 +12283,8 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10736
12283
  sel = new Selection(ab.sampleRate, esffsr, eeffsr);
10737
12284
  }
10738
12285
  else {
10739
- let ch0 = ab.getChannelData(0);
10740
- let frameLength = ch0.length;
12286
+ //let ch0 = ab.getChannelData(0);
12287
+ let frameLength = ab.frameLen;
10741
12288
  sel = new Selection(esr, esffsr, frameLength);
10742
12289
  }
10743
12290
  }
@@ -10751,6 +12298,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10751
12298
  this.audioClip = clip;
10752
12299
  this.loadedRecfile();
10753
12300
  }, error1 => {
12301
+ this.audioFetching = false;
10754
12302
  this.status = 'Error loading audio file!';
10755
12303
  });
10756
12304
  }
@@ -10860,7 +12408,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10860
12408
  for (let avRfV of avRf) {
10861
12409
  os += avRfV.version + '/';
10862
12410
  }
10863
- console.debug(os);
12411
+ //console.debug(os);
10864
12412
  }
10865
12413
  }
10866
12414
  this.updatePos();
@@ -10876,7 +12424,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10876
12424
 
10877
12425
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10878
12426
  <div class="ctrlview">
10879
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12427
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10880
12428
 
10881
12429
  <audio-display-control [audioClip]="audioClip"
10882
12430
  [playStartAction]="playStartAction"
@@ -10889,7 +12437,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10889
12437
  [zoomFitToPanelAction]="zoomFitToPanelAction"></audio-display-control>
10890
12438
  <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>
10891
12439
  </div>
10892
- `, 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"] }] });
12440
+ `, 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"] }] });
10893
12441
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileViewComponent, decorators: [{
10894
12442
  type: Component,
10895
12443
  args: [{
@@ -10898,7 +12446,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10898
12446
 
10899
12447
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10900
12448
  <div class="ctrlview">
10901
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12449
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10902
12450
 
10903
12451
  <audio-display-control [audioClip]="audioClip"
10904
12452
  [playStartAction]="playStartAction"
@@ -10992,7 +12540,7 @@ class RecordingFileUI extends RecordingFileViewComponent {
10992
12540
  applySelection() {
10993
12541
  var _a;
10994
12542
  if (this.audioClip) {
10995
- let ab = this.audioClip.buffer;
12543
+ let ab = this.audioClip.audioDataHolder;
10996
12544
  let s = this.audioClip.selection;
10997
12545
  if (ab && ((_a = this.recordingFile) === null || _a === void 0 ? void 0 : _a.recordingFileId)) {
10998
12546
  let sr = null;
@@ -11030,7 +12578,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11030
12578
 
11031
12579
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11032
12580
  <div class="ctrlview">
11033
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12581
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11034
12582
  <audio-display-control [audioClip]="audioClip"
11035
12583
  [playStartAction]="playStartAction"
11036
12584
  [playSelectionAction]="playSelectionAction"
@@ -11044,7 +12592,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11044
12592
  </div>
11045
12593
 
11046
12594
  <button mat-raised-button color="accent" (click)="applySelection()" [disabled]="editSaved">{{this.applyButtonText()}}</button>
11047
- `, 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"] }] });
12595
+ `, 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"] }] });
11048
12596
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileUI, decorators: [{
11049
12597
  type: Component,
11050
12598
  args: [{
@@ -11055,7 +12603,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
11055
12603
 
11056
12604
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11057
12605
  <div class="ctrlview">
11058
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12606
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11059
12607
  <audio-display-control [audioClip]="audioClip"
11060
12608
  [playStartAction]="playStartAction"
11061
12609
  [playSelectionAction]="playSelectionAction"
@@ -11112,7 +12660,8 @@ class MediaUtils {
11112
12660
 
11113
12661
  class RecordingList {
11114
12662
  constructor() {
11115
- this.recordingList = new Array();
12663
+ //private recordingList:Array<RecordingFile>=new Array<RecordingFile>();
12664
+ this.recordingList = new RecFilesCache();
11116
12665
  //cols=['index','length','samples','samplerate','action'];
11117
12666
  this.cols = ['index', 'startedDate', 'length', 'action'];
11118
12667
  this.selectDisabled = false;
@@ -11124,7 +12673,7 @@ class RecordingList {
11124
12673
  this.buildDataSource();
11125
12674
  }
11126
12675
  buildDataSource() {
11127
- this.recordingList.sort((a, b) => {
12676
+ this.recordingList.recFiles.sort((a, b) => {
11128
12677
  let cmp = 0;
11129
12678
  let aD = null;
11130
12679
  let bD = null;
@@ -11145,25 +12694,36 @@ class RecordingList {
11145
12694
  }
11146
12695
  return cmp;
11147
12696
  });
11148
- this.recordingListDataSource.data = this.recordingList;
12697
+ this.recordingListDataSource.data = this.recordingList.recFiles;
11149
12698
  }
11150
- push(rf) {
11151
- this.recordingList.push(rf);
12699
+ addRecFile(rf) {
12700
+ this.recordingList.addRecFile(rf);
11152
12701
  this.buildDataSource();
11153
12702
  }
12703
+ setRecFileAudioData(recFile, adh) {
12704
+ this.recordingList.setRecFileAudioData(recFile, adh);
12705
+ }
11154
12706
  selectRecordingFile(rf) {
12707
+ this.recordingList.currentRecordingFile = rf;
11155
12708
  this.selectedRecordingFileChanged.emit(rf);
11156
12709
  }
11157
12710
  selectTop() {
11158
- if (this.recordingList.length > 0) {
11159
- this.selectRecordingFile(this.recordingList[0]);
12711
+ if (this.recordingList.recFiles.length > 0) {
12712
+ this.selectRecordingFile(this.recordingList.recFiles[0]);
11160
12713
  }
11161
12714
  }
11162
12715
  lengthTimeFormatted(rf) {
11163
12716
  var _a;
11164
12717
  let str = '--:--:--';
11165
- if (rf.frames && rf.audioBuffer) {
11166
- str = MediaUtils.toMediaTime(rf.frames / ((_a = rf.audioBuffer) === null || _a === void 0 ? void 0 : _a.sampleRate));
12718
+ let tl = null;
12719
+ if (rf.timeLength) {
12720
+ tl = rf.timeLength;
12721
+ }
12722
+ else if (rf.frames && rf.audioDataHolder) {
12723
+ tl = rf.frames / ((_a = rf.audioDataHolder) === null || _a === void 0 ? void 0 : _a.sampleRate);
12724
+ }
12725
+ if (tl) {
12726
+ str = MediaUtils.toMediaTime(tl);
11167
12727
  }
11168
12728
  return str;
11169
12729
  }
@@ -11249,8 +12809,11 @@ class RecorderCombiPane {
11249
12809
  }
11250
12810
  ngAfterViewInit() {
11251
12811
  }
11252
- push(rf) {
11253
- this.recordingListComp.push(rf);
12812
+ addRecFile(rf) {
12813
+ this.recordingListComp.addRecFile(rf);
12814
+ }
12815
+ setRecFileAudioData(recFile, adh) {
12816
+ this.recordingListComp.setRecFileAudioData(recFile, adh);
11254
12817
  }
11255
12818
  selectRecordingFile(rf) {
11256
12819
  this.selectedRecordingFileChanged.emit(rf);
@@ -11323,15 +12886,11 @@ class Item {
11323
12886
  }
11324
12887
  class AudioRecorder extends BasicRecorder {
11325
12888
  constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11326
- super(dialog, sessionService, uploader, config);
11327
- this.changeDetectorRef = changeDetectorRef;
12889
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
11328
12890
  this.renderer = renderer;
11329
12891
  this.route = route;
11330
- this.dialog = dialog;
11331
- this.sessionService = sessionService;
11332
12892
  this.recFileService = recFileService;
11333
12893
  this.uploader = uploader;
11334
- this.config = config;
11335
12894
  this._project = null;
11336
12895
  this.projectName = null;
11337
12896
  this.enableUploadRecordings = true;
@@ -11514,13 +13073,15 @@ class AudioRecorder extends BasicRecorder {
11514
13073
  if (rfs) {
11515
13074
  if (rfs instanceof Array) {
11516
13075
  rfs.forEach((rf) => {
13076
+ // the list comes from the server, asssuem all recording files as server persisted
13077
+ rf.serverPersisted = true;
11517
13078
  if (rf.startedDate) {
11518
13079
  rf._startedAsDateObj = new Date(rf.startedDate);
11519
13080
  }
11520
13081
  if (rf.date) {
11521
13082
  rf._dateAsDateObj = new Date(rf.date);
11522
13083
  }
11523
- this.recorderCombiPane.push(rf);
13084
+ this.recorderCombiPane.addRecFile(rf);
11524
13085
  });
11525
13086
  }
11526
13087
  else {
@@ -11574,6 +13135,7 @@ class AudioRecorder extends BasicRecorder {
11574
13135
  return this._project;
11575
13136
  }
11576
13137
  selectRecordingFile(rf) {
13138
+ this.audioFetching = false;
11577
13139
  this.displayRecFile = rf;
11578
13140
  }
11579
13141
  uploadUpdate(ue) {
@@ -11595,9 +13157,6 @@ class AudioRecorder extends BasicRecorder {
11595
13157
  this.changeDetectorRef.detectChanges();
11596
13158
  }
11597
13159
  set controlAudioPlayer(controlAudioPlayer) {
11598
- if (this._controlAudioPlayer) {
11599
- //this._controlAudioPlayer.listener=null;
11600
- }
11601
13160
  this._controlAudioPlayer = controlAudioPlayer;
11602
13161
  if (this._controlAudioPlayer) {
11603
13162
  this._controlAudioPlayer.listener = this;
@@ -11677,6 +13236,7 @@ class AudioRecorder extends BasicRecorder {
11677
13236
  this.displayAudioClip = null;
11678
13237
  this.showRecording();
11679
13238
  if (this.ac) {
13239
+ this.audioFetching = false;
11680
13240
  if (!this.ac.opened) {
11681
13241
  if (this._selectedDeviceId) {
11682
13242
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -11693,10 +13253,10 @@ class AudioRecorder extends BasicRecorder {
11693
13253
  }
11694
13254
  downloadRecording() {
11695
13255
  if (this.displayRecFile) {
11696
- let ab = this.displayRecFile.audioBuffer;
13256
+ let ab = this.displayRecFile.audioDataHolder;
11697
13257
  let ww = new WavWriter();
11698
- if (ab) {
11699
- let wavFile = ww.writeAsync(ab, (wavFile) => {
13258
+ if (ab === null || ab === void 0 ? void 0 : ab.buffer) {
13259
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
11700
13260
  let blob = new Blob([wavFile], { type: 'audio/wav' });
11701
13261
  let rfUrl = URL.createObjectURL(blob);
11702
13262
  let dataDnlLnk = this.renderer.createElement('a');
@@ -11719,35 +13279,50 @@ class AudioRecorder extends BasicRecorder {
11719
13279
  set displayRecFile(displayRecFile) {
11720
13280
  this._displayRecFile = displayRecFile;
11721
13281
  if (this._displayRecFile) {
11722
- let ab = this._displayRecFile.audioBuffer;
11723
- if (ab) {
11724
- this.displayAudioClip = new AudioClip(ab);
13282
+ let adh = this._displayRecFile.audioDataHolder;
13283
+ if (adh) {
13284
+ this.displayAudioClip = new AudioClip(adh);
13285
+ console.debug(" set recording file: display audio clip set");
11725
13286
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
11726
13287
  }
11727
13288
  else {
11728
13289
  // clear for now ...
11729
13290
  this.displayAudioClip = null;
13291
+ console.debug("set recording file: display audio clip null");
11730
13292
  this.controlAudioPlayer.audioClip = null;
11731
13293
  if (this._controlAudioPlayer && this._session) {
11732
13294
  //... and try to fetch from server
11733
- this.audioFetchSubscription = this.recFileService.fetchAndApplyRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
11734
- let fab = null;
11735
- if (rf && this._displayRecFile) {
11736
- fab = this._displayRecFile.audioBuffer;
11737
- }
11738
- else {
11739
- this.statusMsg = 'Recording file could not be loaded.';
13295
+ this.audioFetching = true;
13296
+ let rf = this._displayRecFile;
13297
+ let clip = this.displayAudioClip;
13298
+ this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
13299
+ next: ab => {
13300
+ this.audioFetching = false;
13301
+ let fabDh = null;
13302
+ if (ab) {
13303
+ if (rf) {
13304
+ fabDh = new AudioDataHolder(ab);
13305
+ this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
13306
+ }
13307
+ }
13308
+ else {
13309
+ console.error('Recording file could not be loaded.');
13310
+ this.statusMsg = 'Recording file could not be loaded.';
13311
+ this.statusAlertType = 'error';
13312
+ }
13313
+ if (fabDh) {
13314
+ // 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
13315
+ this.displayAudioClip = new AudioClip(fabDh);
13316
+ //console.debug("set recording file: display audio clip from fetched audio buffer");
13317
+ }
13318
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
13319
+ this.showRecording();
13320
+ }, error: err => {
13321
+ console.error("Could not load recording file from server: " + err);
13322
+ this.audioFetching = false;
13323
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
11740
13324
  this.statusAlertType = 'error';
11741
13325
  }
11742
- if (fab) {
11743
- this.displayAudioClip = new AudioClip(fab);
11744
- }
11745
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
11746
- this.showRecording();
11747
- }, err => {
11748
- console.error("Could not load recording file from server: " + err);
11749
- this.statusMsg = 'Recording file could not be loaded: ' + err;
11750
- this.statusAlertType = 'error';
11751
13326
  });
11752
13327
  }
11753
13328
  else {
@@ -11757,6 +13332,7 @@ class AudioRecorder extends BasicRecorder {
11757
13332
  }
11758
13333
  }
11759
13334
  else {
13335
+ console.debug("recording file null");
11760
13336
  this.displayAudioClip = null;
11761
13337
  this.controlAudioPlayer.audioClip = null;
11762
13338
  }
@@ -11765,25 +13341,6 @@ class AudioRecorder extends BasicRecorder {
11765
13341
  get displayRecFile() {
11766
13342
  return this._displayRecFile;
11767
13343
  }
11768
- showRecording() {
11769
- this.controlAudioPlayer.stop();
11770
- if (this.displayAudioClip) {
11771
- let dap = this.displayAudioClip;
11772
- this.levelMeasure.calcBufferLevelInfos(dap.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
11773
- dap.levelInfos = levelInfos;
11774
- this.changeDetectorRef.detectChanges();
11775
- });
11776
- this.playStartAction.disabled = false;
11777
- }
11778
- else {
11779
- this.playStartAction.disabled = true;
11780
- // Collapse audio signal display if open
11781
- if (!this.audioSignalCollapsed) {
11782
- this.audioSignalCollapsed = true;
11783
- }
11784
- }
11785
- this.changeDetectorRef.detectChanges();
11786
- }
11787
13344
  updateStartActionDisableState() {
11788
13345
  this.transportActions.startAction.disabled = !(this.ac);
11789
13346
  }
@@ -11827,6 +13384,19 @@ class AudioRecorder extends BasicRecorder {
11827
13384
  super.started();
11828
13385
  this.statusAlertType = 'info';
11829
13386
  this.statusMsg = 'Recording...';
13387
+ if (!this.rfUuid) {
13388
+ this.rfUuid = UUID.generate();
13389
+ }
13390
+ let sessId = 0;
13391
+ if (this._session) {
13392
+ sessId = this._session.sessionId;
13393
+ }
13394
+ let rf = new RecordingFile(this.rfUuid, sessId, null);
13395
+ rf._startedAsDateObj = this.startedDate;
13396
+ if (rf._startedAsDateObj) {
13397
+ rf.startedDate = rf._startedAsDateObj.toString();
13398
+ }
13399
+ this._recordingFile = rf;
11830
13400
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
11831
13401
  this.maxRecTimerId = window.setTimeout(() => {
11832
13402
  this.stopRecordingMaxRec();
@@ -11863,68 +13433,75 @@ class AudioRecorder extends BasicRecorder {
11863
13433
  this.transportActions.pauseAction.disabled = true;
11864
13434
  this.statusAlertType = 'info';
11865
13435
  this.statusMsg = 'Recorded.';
11866
- let ad;
13436
+ let ad = null;
13437
+ let ada = null;
13438
+ let adh = null;
13439
+ let frameLen = 0;
11867
13440
  if (this.ac) {
11868
- ad = this.ac.audioBuffer();
13441
+ if (this.uploadChunkSizeSeconds || AudioRecorder.FORCE_ARRRAY_AUDIO_BUFFER) {
13442
+ ada = this.ac.audioBufferArray();
13443
+ frameLen = ada.frameLen;
13444
+ }
13445
+ else {
13446
+ ad = this.ac.audioBuffer();
13447
+ frameLen = ad.length;
13448
+ }
13449
+ adh = new AudioDataHolder(ad, ada);
11869
13450
  let sessId = 0;
11870
13451
  if (this._session) {
11871
13452
  sessId = this._session.sessionId;
11872
13453
  }
11873
- if (!this.rfUuid) {
11874
- this.rfUuid = UUID.generate();
11875
- }
11876
- let rf = new RecordingFile(this.rfUuid, sessId, ad);
11877
- rf._startedAsDateObj = this.startedDate;
11878
- if (rf._startedAsDateObj) {
11879
- rf.startedDate = rf._startedAsDateObj.toString();
11880
- }
11881
- rf.frames = ad.length;
11882
- this.displayRecFile = rf;
11883
- this.recorderCombiPane.push(rf);
11884
- // Upload if upload enabled and not in chunked upload mode
11885
- if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
11886
- let apiEndPoint = '';
11887
- if (this.config && this.config.apiEndPoint) {
11888
- apiEndPoint = this.config.apiEndPoint;
11889
- }
11890
- if (apiEndPoint !== '') {
11891
- apiEndPoint = apiEndPoint + '/';
13454
+ if (this._recordingFile) {
13455
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
13456
+ let rf = this._recordingFile;
13457
+ RecordingFileUtils.setAudioData(rf, adh);
13458
+ this.recorderCombiPane.addRecFile(rf);
13459
+ // Upload if upload enabled and not in chunked upload mode
13460
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds && rf != null && ad != null) {
13461
+ let apiEndPoint = '';
13462
+ if (this.config && this.config.apiEndPoint) {
13463
+ apiEndPoint = this.config.apiEndPoint;
13464
+ }
13465
+ if (apiEndPoint !== '') {
13466
+ apiEndPoint = apiEndPoint + '/';
13467
+ }
13468
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
13469
+ let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
13470
+ // convert asynchronously to 16-bit integer PCM
13471
+ // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
13472
+ // TODO duplicate conversion for manual download
13473
+ this.processingRecording = true;
13474
+ let ww = new WavWriter();
13475
+ ww.writeAsync(ad, (wavFile) => {
13476
+ this.postRecordingMultipart(wavFile, recUrl, rf);
13477
+ this.processingRecording = false;
13478
+ this.updateWakeLock();
13479
+ this.changeDetectorRef.detectChanges();
13480
+ });
11892
13481
  }
11893
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11894
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11895
- // convert asynchronously to 16-bit integer PCM
11896
- // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11897
- // TODO duplicate conversion for manual download
11898
- this.processingRecording = true;
11899
- let ww = new WavWriter();
11900
- ww.writeAsync(ad, (wavFile) => {
11901
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11902
- this.processingRecording = false;
11903
- this.updateWakeLock();
11904
- this.changeDetectorRef.detectChanges();
11905
- });
11906
13482
  }
11907
13483
  }
13484
+ this.displayRecFile = this._recordingFile;
11908
13485
  this.status = 1 /* IDLE */;
11909
13486
  this.navigationDisabled = false;
11910
13487
  this.updateNavigationActions();
11911
13488
  this.updateWakeLock();
11912
13489
  this.changeDetectorRef.detectChanges();
11913
13490
  }
11914
- postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
13491
+ postRecordingMultipart(wavFile, recUrl, rf) {
11915
13492
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11916
13493
  let fd = new FormData();
11917
- if (uuid) {
11918
- fd.set('uuid', uuid);
13494
+ if (rf.uuid) {
13495
+ fd.set('uuid', rf.uuid);
11919
13496
  }
11920
- if (sessionId !== null) {
11921
- fd.set('sessionId', sessionId.toString());
13497
+ if (rf.session !== null) {
13498
+ fd.set('sessionId', rf.session.toString());
11922
13499
  }
11923
- if (startedDate) {
11924
- fd.set('startedDate', startedDate.toJSON());
13500
+ if (rf._startedAsDateObj) {
13501
+ fd.set('startedDate', rf._startedAsDateObj.toJSON());
11925
13502
  }
11926
13503
  fd.set('audio', wavBlob);
11927
- let ul = new Upload(fd, recUrl);
13504
+ let ul = new Upload(fd, recUrl, rf);
11928
13505
  this.uploader.queueUpload(ul);
11929
13506
  }
11930
13507
  postChunkAudioBuffer(audioBuffer, chunkIdx) {
@@ -11941,8 +13518,9 @@ class AudioRecorder extends BasicRecorder {
11941
13518
  }
11942
13519
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11943
13520
  let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
13521
+ let rf = this._recordingFile;
11944
13522
  ww.writeAsync(audioBuffer, (wavFile) => {
11945
- this.postRecording(wavFile, recUrl);
13523
+ this.postRecording(wavFile, recUrl, rf);
11946
13524
  this.processingRecording = false;
11947
13525
  });
11948
13526
  }
@@ -11991,11 +13569,11 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
11991
13569
 
11992
13570
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
11993
13571
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
11994
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
13572
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
11995
13573
  [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
11996
13574
  <div fxLayout="row">
11997
13575
  <spr-recordingitemcontrols fxFlex="10 0 1"
11998
- [audioLoaded]="displayAudioClip?.buffer!==null"
13576
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
11999
13577
  [playStartAction]="controlAudioPlayer?.startAction"
12000
13578
  [playStopAction]="controlAudioPlayer?.stopAction"
12001
13579
  [peakDbLvl]="peakLevelInDb"
@@ -12034,7 +13612,7 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
12034
13612
  [ready]="dataSaved && !isActive()"></app-readystateindicator>
12035
13613
  </div>
12036
13614
  </div>
12037
- `, 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"] }] });
13615
+ `, 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"] }] });
12038
13616
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioRecorder, decorators: [{
12039
13617
  type: Component,
12040
13618
  args: [{
@@ -12057,11 +13635,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
12057
13635
 
12058
13636
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
12059
13637
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
12060
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
13638
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
12061
13639
  [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
12062
13640
  <div fxLayout="row">
12063
13641
  <spr-recordingitemcontrols fxFlex="10 0 1"
12064
- [audioLoaded]="displayAudioClip?.buffer!==null"
13642
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
12065
13643
  [playStartAction]="controlAudioPlayer?.startAction"
12066
13644
  [playStopAction]="controlAudioPlayer?.stopAction"
12067
13645
  [peakDbLvl]="peakLevelInDb"
@@ -12341,20 +13919,20 @@ class SpeechrecorderngModule {
12341
13919
  }
12342
13920
  SpeechrecorderngModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
12343
13921
  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,
12344
- 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] });
12345
- 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]] });
13922
+ 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] });
13923
+ 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]] });
12346
13924
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, decorators: [{
12347
13925
  type: NgModule,
12348
13926
  args: [{
12349
13927
  declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
12350
13928
  ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder],
12351
13929
  exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder],
12352
- imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule],
13930
+ imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule],
12353
13931
  providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader]
12354
13932
  }]
12355
13933
  }] });
12356
13934
 
12357
- const VERSION = '2.25.3';
13935
+ const VERSION = '3.0.0';
12358
13936
 
12359
13937
  /*
12360
13938
  * Public API Surface of speechrecorderng