speechrecorderng 2.25.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/esm2020/lib/audio/array_audio_buffer.mjs +101 -0
  2. package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +86 -0
  3. package/esm2020/lib/audio/audio_data_holder.mjs +109 -0
  4. package/esm2020/lib/audio/audio_display.mjs +5 -5
  5. package/esm2020/lib/audio/audio_player.mjs +9 -7
  6. package/esm2020/lib/audio/capture/capture.mjs +6 -1
  7. package/esm2020/lib/audio/dsp/level_measure.mjs +97 -55
  8. package/esm2020/lib/audio/io/stream.mjs +3 -3
  9. package/esm2020/lib/audio/persistor.mjs +12 -6
  10. package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +265 -0
  11. package/esm2020/lib/audio/playback/player.mjs +184 -50
  12. package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +25 -13
  13. package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +2 -2
  14. package/esm2020/lib/audio/ui/audiosignal.mjs +206 -77
  15. package/esm2020/lib/audio/ui/container.mjs +14 -14
  16. package/esm2020/lib/audio/ui/livelevel.mjs +17 -3
  17. package/esm2020/lib/audio/ui/sonagram.mjs +343 -160
  18. package/esm2020/lib/io/stream.mjs +77 -1
  19. package/esm2020/lib/net/uploader.mjs +15 -3
  20. package/esm2020/lib/speechrecorder/recording.mjs +63 -7
  21. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +96 -7
  22. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +119 -106
  23. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +34 -7
  24. package/esm2020/lib/speechrecorder/session/progress.mjs +2 -2
  25. package/esm2020/lib/speechrecorder/session/prompting.mjs +2 -2
  26. package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -3
  27. package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +195 -0
  28. package/esm2020/lib/speechrecorder/session/recording_list.mjs +23 -10
  29. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +12 -6
  30. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +5 -5
  31. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +13 -9
  32. package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +8 -5
  33. package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +123 -101
  34. package/esm2020/lib/speechrecorderng.component.mjs +2 -2
  35. package/esm2020/lib/speechrecorderng.module.mjs +5 -4
  36. package/esm2020/lib/spr.module.version.mjs +2 -2
  37. package/esm2020/lib/ui/recordingitem_display.mjs +2 -2
  38. package/fesm2015/speechrecorderng.mjs +2355 -753
  39. package/fesm2015/speechrecorderng.mjs.map +1 -1
  40. package/fesm2020/speechrecorderng.mjs +2338 -746
  41. package/fesm2020/speechrecorderng.mjs.map +1 -1
  42. package/lib/audio/array_audio_buffer.d.ts +15 -0
  43. package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -0
  44. package/lib/audio/audio_data_holder.d.ts +29 -0
  45. package/lib/audio/audio_display.d.ts +2 -1
  46. package/lib/audio/audio_player.d.ts +2 -1
  47. package/lib/audio/capture/capture.d.ts +2 -0
  48. package/lib/audio/dsp/level_measure.d.ts +2 -1
  49. package/lib/audio/persistor.d.ts +8 -3
  50. package/lib/audio/playback/array_audio_buffer_source_node.d.ts +24 -0
  51. package/lib/audio/playback/player.d.ts +6 -0
  52. package/lib/audio/ui/audio_canvas_layer_comp.d.ts +4 -1
  53. package/lib/audio/ui/audio_display_scroll_pane.d.ts +2 -1
  54. package/lib/audio/ui/audiosignal.d.ts +3 -2
  55. package/lib/audio/ui/container.d.ts +3 -2
  56. package/lib/audio/ui/livelevel.d.ts +4 -2
  57. package/lib/audio/ui/sonagram.d.ts +3 -2
  58. package/lib/io/stream.d.ts +17 -0
  59. package/lib/net/uploader.d.ts +6 -1
  60. package/lib/speechrecorder/recording.d.ts +16 -3
  61. package/lib/speechrecorder/recordings/recordings.service.d.ts +2 -0
  62. package/lib/speechrecorder/session/audiorecorder.d.ts +2 -7
  63. package/lib/speechrecorder/session/basicrecorder.d.ts +10 -5
  64. package/lib/speechrecorder/session/progress.d.ts +1 -1
  65. package/lib/speechrecorder/session/prompting.d.ts +1 -1
  66. package/lib/speechrecorder/session/recorder_combi_pane.d.ts +3 -1
  67. package/lib/speechrecorder/session/recording_file_cache.d.ts +33 -0
  68. package/lib/speechrecorder/session/recording_list.d.ts +3 -1
  69. package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +2 -1
  70. package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -0
  71. package/lib/speechrecorder/session/sessionmanager.d.ts +5 -11
  72. package/lib/speechrecorderng.module.d.ts +2 -1
  73. package/lib/spr.module.version.d.ts +1 -1
  74. package/package.json +1 -1
@@ -36,6 +36,7 @@ import * as i2$2 from '@angular/material/table';
36
36
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
37
37
  import { MatSelectModule } from '@angular/material/select';
38
38
  import { MatInputModule } from '@angular/material/input';
39
+ import { MatMenuModule } from '@angular/material/menu';
39
40
 
40
41
  class ActionEvent {
41
42
  constructor(_value = null) {
@@ -109,6 +110,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,42 @@ 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 equals(recordinFile, otherRecordingFile) {
2299
+ if (recordinFile && otherRecordingFile) {
2300
+ if (otherRecordingFile === recordinFile) {
2301
+ return true;
2302
+ }
2303
+ if (otherRecordingFile.uuid === recordinFile.uuid) {
2304
+ return true;
2305
+ }
2306
+ }
2307
+ return false;
2308
+ }
2309
+ static setAudioData(rf, audioDataHolder) {
2310
+ rf.audioDataHolder = audioDataHolder;
2311
+ if (audioDataHolder) {
2312
+ rf.frames = audioDataHolder.frameLen;
2313
+ rf.timeLength = audioDataHolder.duration;
2314
+ }
2315
+ }
2316
+ static sampleCount(rf) {
2317
+ if (rf.audioDataHolder) {
2318
+ return rf.audioDataHolder.sampleCounts();
2319
+ }
2320
+ else {
2321
+ return 0;
2322
+ }
2323
+ }
2324
+ static expireAudioData(rf) {
2325
+ let rv = rf.audioDataHolder;
2326
+ rf.audioDataHolder = null;
2327
+ return rv;
2328
+ }
1522
2329
  }
1523
2330
 
1524
2331
  // state of an upload
@@ -1569,7 +2376,8 @@ class UploaderStatusChangeEvent {
1569
2376
  }
1570
2377
  }
1571
2378
  class Upload {
1572
- constructor(blob, url) {
2379
+ constructor(blob, url, serverPersistable = null) {
2380
+ this.serverPersistable = serverPersistable;
1573
2381
  this.toString = () => {
1574
2382
  let s = `Upload: Status: ${this.status}, URL: ${this._url}`;
1575
2383
  if (this._data instanceof Blob) {
@@ -1590,6 +2398,17 @@ class Upload {
1590
2398
  get data() {
1591
2399
  return this._data;
1592
2400
  }
2401
+ done() {
2402
+ this.status = UploadStatus$1.DONE;
2403
+ //console.debug("Single upload done.");
2404
+ if (this.serverPersistable) {
2405
+ this.serverPersistable.serverPersisted = true;
2406
+ //console.debug("Single upload set server persisted: "+this.serverPersistable);
2407
+ }
2408
+ else {
2409
+ //console.debug("Server persistable not set.");
2410
+ }
2411
+ }
1593
2412
  }
1594
2413
  class Uploader {
1595
2414
  constructor(http, withCredentials = false) {
@@ -1628,7 +2447,7 @@ class Uploader {
1628
2447
  return si;
1629
2448
  }
1630
2449
  uploadDone(ul) {
1631
- ul.status = UploadStatus$1.DONE;
2450
+ ul.done();
1632
2451
  // remove upload from queue
1633
2452
  for (let i = 0; i < this.que.length; i++) {
1634
2453
  if (this.que[i] === ul) {
@@ -2063,15 +2882,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2063
2882
  }], ctorParameters: function () { return []; } });
2064
2883
 
2065
2884
  class AudioClip {
2066
- constructor(buffer) {
2885
+ constructor(_audioDataHolder) {
2886
+ this._audioDataHolder = _audioDataHolder;
2067
2887
  this._selection = null;
2888
+ this._levelInfos = null;
2068
2889
  this.selectionObservers = new Array();
2069
- this._buffer = buffer;
2070
2890
  }
2071
- get buffer() {
2072
- return this._buffer;
2891
+ get audioDataHolder() {
2892
+ return this._audioDataHolder;
2073
2893
  }
2074
- ;
2075
2894
  get selection() {
2076
2895
  return this._selection;
2077
2896
  }
@@ -2086,6 +2905,12 @@ class AudioClip {
2086
2905
  selObs(this);
2087
2906
  }
2088
2907
  }
2908
+ get levelInfos() {
2909
+ return this._levelInfos;
2910
+ }
2911
+ set levelInfos(value) {
2912
+ this._levelInfos = value;
2913
+ }
2089
2914
  addSelectionObserver(selectionObserver, init = false) {
2090
2915
  let obsAlreadyInList = this.selectionObservers.find((obs) => (obs === selectionObserver));
2091
2916
  if (!obsAlreadyInList) {
@@ -2168,19 +2993,32 @@ class ViewSelection {
2168
2993
  class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2169
2994
  constructor() {
2170
2995
  super(...arguments);
2171
- this._audioData = null;
2996
+ //protected _audioData: AudioBuffer|null=null;
2997
+ //protected _arrayAudioData: ArrayAudioBuffer|null=null;
2998
+ this._audioDataHolder = null;
2172
2999
  this._bgColor = 'white';
2173
3000
  this._selectColor = 'rgba(0%,0%,100%,25%)';
2174
3001
  }
3002
+ frameLength() {
3003
+ let frameLength = null;
3004
+ // if (this._audioData && this._audioData.numberOfChannels > 0) {
3005
+ // let ch0 = this._audioData.getChannelData(0);
3006
+ // frameLength = ch0.length;
3007
+ //
3008
+ // }else if(this._arrayAudioData){
3009
+ // frameLength=this._arrayAudioData.frameLen;
3010
+ // }
3011
+ return frameLength;
3012
+ }
2175
3013
  /**
2176
3014
  * Returns pixel position depending on current zoom setting.
2177
3015
  * @param framePos audio frame (sample) position
2178
3016
  */
2179
3017
  frameToXPixelPosition(framePos) {
3018
+ var _a;
2180
3019
  let pixelPos = null;
2181
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2182
- let ch0 = this._audioData.getChannelData(0);
2183
- let frameLength = ch0.length;
3020
+ let frameLength = (_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.frameLen;
3021
+ if (frameLength !== undefined) {
2184
3022
  let vw;
2185
3023
  if (this.bounds) {
2186
3024
  vw = this.bounds.dimension.width;
@@ -2209,10 +3047,10 @@ class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2209
3047
  }
2210
3048
  }
2211
3049
  viewPortXPixelToFramePosition(xViewPortPixelPos) {
3050
+ var _a;
2212
3051
  let vpXramePos = null;
2213
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2214
- let ch0 = this._audioData.getChannelData(0);
2215
- let frameLength = ch0.length;
3052
+ let frameLength = (_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.frameLen;
3053
+ if (frameLength !== undefined) {
2216
3054
  let vw;
2217
3055
  if (this.bounds) {
2218
3056
  vw = this.bounds.dimension.width;
@@ -2389,8 +3227,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2389
3227
  if (viewSel) {
2390
3228
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2391
3229
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2392
- if (this._audioData && frameStart != null && frameEnd != null) {
2393
- 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);
2394
3232
  }
2395
3233
  }
2396
3234
  this.selectingEventEmitter.emit(ns);
@@ -2400,8 +3238,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2400
3238
  if (viewSel) {
2401
3239
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2402
3240
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2403
- if (this._audioData && frameStart != null && frameEnd != null) {
2404
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3241
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3242
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2405
3243
  }
2406
3244
  }
2407
3245
  this.selectedEventEmitter.emit(ns);
@@ -2482,7 +3320,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2482
3320
  g.lineTo(xViewPortPixelpos, h);
2483
3321
  g.closePath();
2484
3322
  g.stroke();
2485
- if (this._audioData) {
3323
+ if (this._audioDataHolder) {
2486
3324
  g.font = '14px sans-serif';
2487
3325
  g.fillStyle = 'yellow';
2488
3326
  g.fillText(framePos.toString(), xViewPortPixelpos + 2, 50);
@@ -2494,6 +3332,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2494
3332
  }
2495
3333
  }
2496
3334
  }
3335
+ AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD = 10 * 60 * 48000; // Use streaming/chunking if audio clip has more than this number of samples
2497
3336
  AudioCanvasLayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2498
3337
  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 });
2499
3338
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, decorators: [{
@@ -2528,7 +3367,7 @@ class AudioSignal extends AudioCanvasLayerComponent {
2528
3367
  this._playFramePosition = null;
2529
3368
  this.worker = null;
2530
3369
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
2531
- this._audioData = null;
3370
+ this._audioDataHolder = null;
2532
3371
  this._bgColor = 'black';
2533
3372
  this._selectColor = 'rgba(255,255,0,0.8)';
2534
3373
  }
@@ -2581,25 +3420,26 @@ class AudioSignal extends AudioCanvasLayerComponent {
2581
3420
  */
2582
3421
  workerFunction() {
2583
3422
  addEventListener('message', ({ data }) => {
2584
- let audioData = data.audioData;
2585
- let l = data.l;
2586
- let w = data.w;
2587
- let h = data.h;
2588
- let vw = data.vw;
2589
- let chs = data.chs;
2590
- let frameLength = data.frameLength;
3423
+ let audioData = data.audioData; // audio data part required to render view port
3424
+ let auOffset = data.audioDataOffset;
3425
+ let l = data.l; // left pixel position of view port
3426
+ let w = data.w; // width of viewport
3427
+ let vw = data.vw; // total width of (virtual) audio view (not viewport width)
3428
+ let chs = data.chs; // number of channels
3429
+ let dataFrameLength = data.audioDataFrameLength; // frame length of audio data part required for view port
3430
+ let frameLength = data.frameLength; // total frame length (of audio clip)
3431
+ //console.debug("W: left: "+l+", w:"+w+", vw: "+vw+", chs: "+chs+", frameLength: "+frameLength);
2591
3432
  let psMinMax = new Float32Array(0);
2592
- if (audioData && w >= 0 && vw > 0) {
3433
+ if (audioData && audioData.length > 0 && w >= 0 && vw > 0) {
2593
3434
  let framesPerPixel = frameLength / vw;
2594
- let y = 0;
2595
3435
  let pointsLen = w * chs;
2596
3436
  // one for min one for max
2597
3437
  let arrLen = pointsLen * 2;
2598
3438
  psMinMax = new Float32Array(arrLen);
2599
3439
  let chFramePos = 0;
3440
+ let chFrameLength = audioData.length / chs;
2600
3441
  for (let ch = 0; ch < chs; ch++) {
2601
- let x = 0;
2602
- chFramePos = ch * frameLength;
3442
+ chFramePos = ch * chFrameLength;
2603
3443
  for (let pii = 0; pii < w; pii++) {
2604
3444
  let virtPii = l + pii;
2605
3445
  let pMin = Infinity;
@@ -2609,24 +3449,28 @@ class AudioSignal extends AudioCanvasLayerComponent {
2609
3449
  for (let ai = 0; ai < framesPerPixel; ai++) {
2610
3450
  let framePos = pixelFramePos + ai;
2611
3451
  let a = 0;
2612
- if (framePos >= 0 && framePos < audioData.length) {
2613
- a = audioData[framePos];
2614
- }
2615
- if (a < pMin) {
2616
- pMin = a;
2617
- }
2618
- if (a > pMax) {
2619
- pMax = a;
3452
+ let bufPos = framePos - auOffset;
3453
+ //let bufPos=framePos;
3454
+ if (bufPos >= 0 && bufPos < audioData.length) {
3455
+ a = audioData[bufPos];
3456
+ //console.debug("W: ch: "+ch+", pixelFramePos: "+pixelFramePos+", framePos: "+framePos+", auOffset: "+auOffset+", bufPos: "+bufPos+", audioData.length: "+audioData.length+", a: "+a);
3457
+ if (a < pMin) {
3458
+ pMin = a;
3459
+ }
3460
+ if (a > pMax) {
3461
+ pMax = a;
3462
+ }
2620
3463
  }
2621
3464
  }
2622
3465
  let psMinPos = ch * w + pii;
2623
3466
  psMinMax[psMinPos] = pMin;
2624
3467
  let psMaxPos = pointsLen + psMinPos;
2625
3468
  psMinMax[psMaxPos] = pMax;
3469
+ //console.debug("psMinMax["+psMinPos+"]="+pMin+",psMinMax["+psMaxPos+"]="+pMax);
2626
3470
  }
2627
3471
  }
2628
3472
  }
2629
- postMessage({ psMinMax: psMinMax, l: data.l, t: data.t, w: data.w, h: data.h, chs: data.chs }, [psMinMax.buffer]);
3473
+ postMessage({ psMinMax: psMinMax, l: data.l, w: data.w, chs: data.chs, eod: data.eod }, [psMinMax.buffer]);
2630
3474
  });
2631
3475
  }
2632
3476
  startDraw(clear = true) {
@@ -2654,41 +3498,143 @@ class AudioSignal extends AudioCanvasLayerComponent {
2654
3498
  if (this.bounds && this.bounds.dimension) {
2655
3499
  let w = Math.round(this.bounds.dimension.width);
2656
3500
  let h = Math.round(this.bounds.dimension.height);
2657
- if (this._audioData && w > 0 && h > 0) {
3501
+ if (this._audioDataHolder && w > 0 && h > 0) {
2658
3502
  //this.wo = new Worker('./audiosignal.worker.js',{type: 'module'});
2659
3503
  this.worker = new Worker(this.workerURL);
2660
3504
  //this.wo = new Worker('worker/audiosignal.worker.ts');
2661
3505
  //let Worker = require('worker!../../../workers/uploader/main');
2662
- let chs = this._audioData.numberOfChannels;
2663
- let frameLength = this._audioData.getChannelData(0).length;
2664
- // if(frameLength != this.audioData.getChannelData(1).length){
2665
- // alert("Ungleiche Länge");
2666
- // }
2667
- let ad = new Float32Array(chs * frameLength);
2668
- for (let ch = 0; ch < chs; ch++) {
2669
- ad.set(this._audioData.getChannelData(ch), ch * frameLength);
2670
- }
2671
- //let start = Date.now();
3506
+ let chs = this._audioDataHolder.numberOfChannels;
3507
+ let leftPos = Math.round(this.bounds.position.left);
3508
+ let renderPos = leftPos;
3509
+ let vw = Math.round(this.virtualDimension.width);
3510
+ let frameLength = this._audioDataHolder.frameLen;
3511
+ let framesPerPixel = Math.ceil(frameLength / vw);
3512
+ let audioBuffer = this._audioDataHolder.buffer;
3513
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
3514
+ let arrAbBuf;
3515
+ //let ais:ArrayAudioBufferInputStream|null=null;
3516
+ //let aisBuf:Float32Array[]|null=null;
3517
+ let psMinMax = null;
2672
3518
  if (this.worker) {
2673
3519
  this.worker.onmessage = (me) => {
2674
- //console.log("As rendertime: ", Date.now() - start);
2675
- this.drawRendered(me);
2676
- if (this.worker) {
2677
- this.worker.terminate();
3520
+ if (me.data.eod === true) {
3521
+ let psMinMaxTmp;
3522
+ if (psMinMax) {
3523
+ psMinMaxTmp = psMinMax;
3524
+ }
3525
+ else {
3526
+ psMinMaxTmp = me.data.psMinMax;
3527
+ }
3528
+ this.drawRendered(leftPos, w, h, chs, psMinMaxTmp);
3529
+ if (this.worker) {
3530
+ this.worker.terminate();
3531
+ }
3532
+ this.worker = null;
3533
+ }
3534
+ else if (this._audioDataHolder && arrAbBuf) {
3535
+ let rw = me.data.w;
3536
+ let rPointsLen = chs * rw;
3537
+ let pointsLen = chs * w;
3538
+ for (let ch = 0; ch < chs; ch++) {
3539
+ if (psMinMax) {
3540
+ let rBasePos = ch * rw;
3541
+ let basePos = ch * w;
3542
+ let rPosMin = rBasePos;
3543
+ let rPosMax = rPointsLen + rPosMin;
3544
+ let posMin = basePos + (renderPos - leftPos);
3545
+ let posMax = pointsLen + posMin;
3546
+ psMinMax[posMin] = me.data.psMinMax[rPosMin];
3547
+ //console.debug('Min: ('+pos+'): '+me.data.psMinMax[0]);
3548
+ psMinMax[posMax] = me.data.psMinMax[rPosMax];
3549
+ // console.debug('Max: ('+(pointsLen+pos)+'): '+me.data.psMinMax[1]);
3550
+ //console.debug("psMinMax["+posMin+"]="+me.data.psMinMax[rPosMin]+" (rPosMin="+rPosMin+"),psMinMax["+posMax+"]="+me.data.psMinMax[rPosMax]);
3551
+ }
3552
+ }
3553
+ let eod = false;
3554
+ renderPos++;
3555
+ let ad;
3556
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3557
+ if (renderPos < leftPos + w) {
3558
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3559
+ //console.debug("First read frame: "+arrAbBuf[0][0]);
3560
+ ad = new Float32Array(chs * framesPerPixel);
3561
+ for (let ch = 0; ch < chs; ch++) {
3562
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3563
+ }
3564
+ eod = (read <= 0);
3565
+ }
3566
+ else {
3567
+ ad = new Float32Array();
3568
+ eod = true;
3569
+ }
3570
+ let adBuf = ad.buffer;
3571
+ if (this.worker) {
3572
+ this.worker.postMessage({
3573
+ l: renderPos,
3574
+ w: me.data.w,
3575
+ h: h,
3576
+ vw: vw,
3577
+ chs: chs,
3578
+ frameLength: frameLength,
3579
+ audioData: ad,
3580
+ audioDataOffset: leftFramePos,
3581
+ eod: eod
3582
+ }, [adBuf]);
3583
+ }
2678
3584
  }
2679
- this.worker = null;
2680
3585
  };
2681
3586
  }
2682
- this.worker.postMessage({
2683
- l: Math.round(this.bounds.position.left),
2684
- t: Math.round(this.bounds.position.top),
2685
- w: w,
2686
- h: h,
2687
- vw: Math.round(this.virtualDimension.width),
2688
- chs: chs,
2689
- frameLength: frameLength,
2690
- audioData: ad
2691
- }, [ad.buffer]);
3587
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
3588
+ // Render whole clip at once
3589
+ arrAbBuf = null;
3590
+ psMinMax = null;
3591
+ let ad = new Float32Array(chs * frameLength);
3592
+ for (let ch = 0; ch < chs; ch++) {
3593
+ ad.set(audioBuffer.getChannelData(ch), ch * frameLength);
3594
+ }
3595
+ this.worker.postMessage({
3596
+ l: leftPos,
3597
+ w: w,
3598
+ vw: vw,
3599
+ chs: chs,
3600
+ frameLength: frameLength,
3601
+ audioData: ad,
3602
+ audioDataOffset: 0,
3603
+ eod: true
3604
+ }, [ad.buffer]);
3605
+ }
3606
+ else {
3607
+ // Render pixel by pixel
3608
+ if (w > 0) {
3609
+ if (framesPerPixel > 0) {
3610
+ let rw = 1;
3611
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
3612
+ arrAbBuf = new Array(chs);
3613
+ psMinMax = new Float32Array(chs * w * 2);
3614
+ for (let ch = 0; ch < chs; ch++) {
3615
+ arrAbBuf[ch] = new Float32Array(framesPerPixel);
3616
+ }
3617
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3618
+ let auOffset = leftFramePos; // should always be 0
3619
+ //let read=arrayAudioBuffer.frames(leftFramePos,framesPerPixel,arrAbBuf);
3620
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3621
+ let ad = new Float32Array(chs * framesPerPixel);
3622
+ for (let ch = 0; ch < chs; ch++) {
3623
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3624
+ }
3625
+ this.worker.postMessage({
3626
+ l: renderPos,
3627
+ w: rw,
3628
+ vw: vw,
3629
+ chs: chs,
3630
+ frameLength: frameLength,
3631
+ audioData: ad,
3632
+ audioDataOffset: auOffset,
3633
+ eod: (read <= 0)
3634
+ }, [ad.buffer]);
3635
+ }
3636
+ }
3637
+ }
2692
3638
  }
2693
3639
  else {
2694
3640
  let g = this.signalCanvas.getContext("2d");
@@ -2698,42 +3644,42 @@ class AudioSignal extends AudioCanvasLayerComponent {
2698
3644
  }
2699
3645
  }
2700
3646
  }
2701
- drawRendered(me) {
3647
+ drawRendered(left, w, h, chs, psMinMax) {
2702
3648
  this.drawBg();
2703
- this.signalCanvas.style.left = me.data.l.toString() + 'px';
2704
- this.signalCanvas.width = me.data.w;
2705
- this.signalCanvas.height = me.data.h;
3649
+ this.signalCanvas.style.left = left.toString() + 'px';
3650
+ this.signalCanvas.width = w;
3651
+ this.signalCanvas.height = h;
2706
3652
  let g = this.signalCanvas.getContext("2d");
2707
3653
  if (g) {
2708
- g.clearRect(0, 0, me.data.w, me.data.h);
3654
+ g.clearRect(0, 0, w, h);
2709
3655
  //g.fillStyle = "black";
2710
3656
  //g.fillRect(0, 0, me.data.w, me.data.h);
2711
- let pointsLen = me.data.w * me.data.chs;
3657
+ let pointsLen = w * chs;
2712
3658
  // one for min one for max
2713
3659
  let arrLen = pointsLen * 2;
2714
- if (this._audioData) {
3660
+ if (this._audioDataHolder) {
2715
3661
  let std = Date.now();
2716
- let chH = me.data.h / me.data.chs;
3662
+ let chH = h / chs;
2717
3663
  let y = 0;
2718
- for (let ch = 0; ch < me.data.chs; ch++) {
3664
+ for (let ch = 0; ch < chs; ch++) {
2719
3665
  let x = 0;
2720
- let psMinPos = ch * me.data.w;
3666
+ let psMinPos = ch * w;
2721
3667
  let psMaxPos = pointsLen + psMinPos;
2722
3668
  g.fillStyle = 'green';
2723
3669
  g.strokeStyle = 'green';
2724
3670
  // draw audio signal as single polygon
2725
3671
  g.beginPath();
2726
- g.moveTo(0, y + (chH / 2) + me.data.psMinMax[psMaxPos] * chH / 2);
2727
- for (let pii = 0; pii < me.data.w; pii++) {
2728
- let psMax = me.data.psMinMax[psMaxPos + pii];
3672
+ g.moveTo(0, y + (chH / 2) + psMinMax[psMaxPos] * chH / 2);
3673
+ for (let pii = 0; pii < w; pii++) {
3674
+ let psMax = psMinMax[psMaxPos + pii];
2729
3675
  let pv = psMax * chH / 2;
2730
3676
  let yd = y + (chH / 2) - pv;
2731
3677
  //console.log("LineTo: : "+pii+" "+yd)
2732
3678
  g.lineTo(pii, yd);
2733
3679
  }
2734
- let revPixelStart = me.data.w - 1;
3680
+ let revPixelStart = w - 1;
2735
3681
  for (let pii = revPixelStart; pii >= 0; pii--) {
2736
- let psMin = me.data.psMinMax[psMinPos + pii];
3682
+ let psMin = psMinMax[psMinPos + pii];
2737
3683
  let pv = psMin * chH / 2;
2738
3684
  let yd = y + (chH / 2) - pv;
2739
3685
  //console.log("LineTo: : "+pii+" "+yd)
@@ -2757,13 +3703,23 @@ class AudioSignal extends AudioCanvasLayerComponent {
2757
3703
  g.clearRect(0, 0, w, h);
2758
3704
  //g.fillStyle = "black";
2759
3705
  //g.fillRect(0, 0, w, h);
2760
- if (this._audioData) {
3706
+ if (this._audioDataHolder) {
2761
3707
  let std = Date.now();
2762
- let chs = this._audioData.numberOfChannels;
3708
+ let chs = this._audioDataHolder.numberOfChannels;
2763
3709
  let chH = h / chs;
2764
- let frameLength = this._audioData.getChannelData(0).length;
3710
+ let frameLength = this._audioDataHolder.frameLen;
2765
3711
  let framesPerPixel = frameLength / w;
2766
3712
  let y = 0;
3713
+ let ais = null;
3714
+ let audioBuffer = this._audioDataHolder.buffer;
3715
+ let aisBuffer = null;
3716
+ if (!audioBuffer) {
3717
+ ais = this._audioDataHolder.audioInputStream();
3718
+ aisBuffer = new Array(chs);
3719
+ for (let ch = 0; ch < chs; ch++) {
3720
+ aisBuffer[ch] = new Float32Array(framesPerPixel);
3721
+ }
3722
+ }
2767
3723
  for (let ch = 0; ch < chs; ch++) {
2768
3724
  let x = 0;
2769
3725
  let psMin = new Float32Array(w);
@@ -2772,20 +3728,33 @@ class AudioSignal extends AudioCanvasLayerComponent {
2772
3728
  for (let pii = 0; pii < w; pii++) {
2773
3729
  let pMin = 0;
2774
3730
  let pMax = 0;
2775
- // calculate pixel min/max amplitude
2776
- for (let ai = 0; ai < framesPerPixel; ai++) {
2777
- //let framePos=(pii*framesPerPixel)+ai;
2778
- let a = this._audioData.getChannelData(ch)[framePos++];
2779
- if (a < pMin) {
2780
- pMin = a;
3731
+ if (audioBuffer) {
3732
+ // calculate pixel min/max amplitude
3733
+ for (let ai = 0; ai < framesPerPixel; ai++) {
3734
+ //let framePos=(pii*framesPerPixel)+ai;
3735
+ let a = audioBuffer.getChannelData(ch)[framePos++];
3736
+ if (a < pMin) {
3737
+ pMin = a;
3738
+ }
3739
+ if (a > pMax) {
3740
+ pMax = a;
3741
+ }
2781
3742
  }
2782
- if (a > pMax) {
2783
- pMax = a;
3743
+ }
3744
+ else if (ais && aisBuffer) {
3745
+ let r = ais.read(aisBuffer);
3746
+ for (let ai = 0; ai < r; ai++) {
3747
+ let a = aisBuffer[ch][ai];
3748
+ if (a < pMin) {
3749
+ pMin = a;
3750
+ }
3751
+ if (a > pMax) {
3752
+ pMax = a;
3753
+ }
2784
3754
  }
2785
3755
  }
2786
3756
  psMin[pii] = pMin;
2787
3757
  psMax[pii] = pMax;
2788
- //console.log("Min: ", pMin, " max: ", pMax);
2789
3758
  }
2790
3759
  g.fillStyle = 'green';
2791
3760
  g.strokeStyle = 'green';
@@ -2810,12 +3779,11 @@ class AudioSignal extends AudioCanvasLayerComponent {
2810
3779
  g.stroke();
2811
3780
  y += chH;
2812
3781
  }
2813
- //this.drawPlayPosition();
2814
3782
  }
2815
3783
  }
2816
3784
  }
2817
3785
  setData(audioData) {
2818
- this._audioData = audioData;
3786
+ this._audioDataHolder = audioData;
2819
3787
  this.playFramePosition = 0;
2820
3788
  }
2821
3789
  }
@@ -3184,7 +4152,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3184
4152
  this._playFramePosition = null;
3185
4153
  this.dftSize = DEFAULT_DFT_SIZE;
3186
4154
  this.worker = null;
3187
- this._audioData = null;
4155
+ this._audioDataHolder = null;
3188
4156
  this.markers = new Array();
3189
4157
  this.dft = new DFTFloat32(this.dftSize);
3190
4158
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
@@ -3237,7 +4205,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3237
4205
  g.lineTo(xViewPortPixelpos, h);
3238
4206
  g.closePath();
3239
4207
  g.stroke();
3240
- if (this._audioData) {
4208
+ if (this._audioDataHolder) {
3241
4209
  let framePosRound = this.viewPortXPixelToFramePosition(xViewPortPixelpos);
3242
4210
  if (framePosRound != null) {
3243
4211
  g.font = '14px sans-serif';
@@ -3501,11 +4469,21 @@ class Sonagram extends AudioCanvasLayerComponent {
3501
4469
  }
3502
4470
  GaussianWindow.DEFAULT_SIGMA = 0.3;
3503
4471
  self.onmessage = function (msg) {
4472
+ //console.debug("Sonagram render thread");
3504
4473
  let l = msg.data.l;
3505
4474
  let w = msg.data.w;
3506
4475
  let h = msg.data.h;
3507
4476
  let vw = msg.data.vw;
3508
4477
  let chs = msg.data.chs;
4478
+ let audioDataOffset = 0;
4479
+ let adOffset = msg.data.audioDataOffset;
4480
+ if (adOffset) {
4481
+ audioDataOffset = adOffset;
4482
+ }
4483
+ let maxPsd = null;
4484
+ if (msg.data.maxPsd !== undefined) {
4485
+ maxPsd = msg.data.maxPsd;
4486
+ }
3509
4487
  let audioData = new Array(chs);
3510
4488
  for (let ch = 0; ch < chs; ch++) {
3511
4489
  audioData[ch] = new Float32Array(msg.data['audioData'][ch]);
@@ -3521,16 +4499,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3521
4499
  }
3522
4500
  let imgData = new Uint8ClampedArray(arrSize);
3523
4501
  //console.log("Render method:");
3524
- if (audioData && arrSize > 0) {
4502
+ //console.debug("Created imgData arrSize: "+arrSize+" ", w, "x", h);
4503
+ let calcMaxPsd = -Infinity;
4504
+ if (arrSize > 0) {
3525
4505
  let chH = Math.round(h / chs);
3526
4506
  let framesPerPixel = frameLength / vw;
3527
- //console.log("Render: ", w, "x", h);
4507
+ //console.debug("Render: ", w, "x", h);
3528
4508
  let b = new Float32Array(dftSize);
3529
4509
  let sona = new Array(chs);
3530
- let maxPsd = -Infinity;
3531
- let p = 0;
4510
+ //let p = 0;
3532
4511
  for (let ch = 0; ch < chs; ch++) {
3533
- p = ch * frameLength;
4512
+ //p = ch * frameLength;
3534
4513
  let chDataLen = audioData[ch].length;
3535
4514
  let x = 0;
3536
4515
  // initialize DFT array buffer
@@ -3547,8 +4526,13 @@ class Sonagram extends AudioCanvasLayerComponent {
3547
4526
  // initialize for negative sample positions and out of bounds positions
3548
4527
  let chDat = 0;
3549
4528
  // Set audio sample if available
3550
- if (samplePos >= 0 && samplePos < chDataLen) {
3551
- chDat = audioData[ch][samplePos];
4529
+ let adp = samplePos - audioDataOffset;
4530
+ if (adp >= 0 && adp < chDataLen) {
4531
+ chDat = audioData[ch][adp];
4532
+ //console.debug("Audio data: "+chDat);
4533
+ }
4534
+ else {
4535
+ //console.debug("Sample buf pos oob: adp: "+adp+", chDataLen: "+chDataLen+", samplePos: "+samplePos+", i: "+i);
3552
4536
  }
3553
4537
  // apply Window
3554
4538
  b[i] = chDat * wf.getScale(i);
@@ -3558,63 +4542,78 @@ class Sonagram extends AudioCanvasLayerComponent {
3558
4542
  // Get maximum value of spectral energy
3559
4543
  for (let s = 0; s < dftBands; s++) {
3560
4544
  let psd = (2 * Math.pow(spectr[s], 2)) / dftBands;
3561
- if (psd > maxPsd) {
3562
- maxPsd = psd;
4545
+ if (psd > calcMaxPsd) {
4546
+ calcMaxPsd = psd;
3563
4547
  }
3564
4548
  }
3565
4549
  // Set render model data for this pixel
3566
4550
  sona[ch][pii] = spectr;
3567
4551
  }
3568
4552
  }
3569
- //maxPsd = (2 * Math.pow(max, 2)) / dftBands;
3570
- for (let ch = 0; ch < chs; ch++) {
3571
- for (let pii = 0; pii < w; pii++) {
3572
- let allBlack = true;
3573
- for (let y = 0; y < chH; y++) {
3574
- let freqIdx = Math.round(y * dftBands / chH);
3575
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3576
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3577
- let val = sona[ch][pii][freqIdx];
3578
- let psd = (2 * Math.pow(val, 2)) / dftBands;
3579
- // Calculate logarithmic value
3580
- //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
3581
- let linearLevel = psd / maxPsd;
3582
- let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
3583
- // Fixed dynamic Range value of 70dB for now
3584
- let dynRangeInDb = 70;
3585
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3586
- // are the following checks necessary for clamped array ?
3587
- if (scaledVal > 1.0)
3588
- scaledVal = 1;
3589
- if (scaledVal < 0.0) {
3590
- scaledVal = 0;
3591
- }
3592
- let rgbVal = Math.round(255 * scaledVal);
3593
- if (rgbVal < 0) {
3594
- // System.out.println("Neg RGB val: "+rgbVal);
3595
- rgbVal = 0;
3596
- }
3597
- if (rgbVal > 255) {
3598
- rgbVal = 255;
3599
- }
3600
- rgbVal = 255 - rgbVal;
3601
- if (rgbVal > 0) {
3602
- allBlack = false;
4553
+ if (!msg.data.norender) {
4554
+ if (!maxPsd) {
4555
+ maxPsd = calcMaxPsd;
4556
+ }
4557
+ for (let ch = 0; ch < chs; ch++) {
4558
+ for (let pii = 0; pii < w; pii++) {
4559
+ let allBlack = true;
4560
+ for (let y = 0; y < chH; y++) {
4561
+ let freqIdx = Math.round(y * dftBands / chH);
4562
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4563
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4564
+ let val = sona[ch][pii][freqIdx];
4565
+ let psd = (2 * Math.pow(val, 2)) / dftBands;
4566
+ // Calculate logarithmic value
4567
+ //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
4568
+ let linearLevel = psd / maxPsd;
4569
+ let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
4570
+ // Fixed dynamic Range value of 70dB for now
4571
+ let dynRangeInDb = 70;
4572
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4573
+ // are the following checks necessary for clamped array ?
4574
+ if (scaledVal > 1.0)
4575
+ scaledVal = 1;
4576
+ if (scaledVal < 0.0) {
4577
+ scaledVal = 0;
4578
+ }
4579
+ let rgbVal = Math.round(255 * scaledVal);
4580
+ if (rgbVal < 0) {
4581
+ // System.out.println("Neg RGB val: "+rgbVal);
4582
+ rgbVal = 0;
4583
+ }
4584
+ if (rgbVal > 255) {
4585
+ rgbVal = 255;
4586
+ }
4587
+ rgbVal = 255 - rgbVal;
4588
+ if (rgbVal > 0) {
4589
+ allBlack = false;
4590
+ }
4591
+ let py = chH - y;
4592
+ let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
4593
+ imgData[dataPos + 0] = rgbVal; //R
4594
+ imgData[dataPos + 1] = rgbVal; //G
4595
+ imgData[dataPos + 2] = rgbVal; //B
4596
+ imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4597
+ //console.debug("Rendered: py: "+py+", rgbval: "+rgbVal);
4598
+ // example 1x1, 2chs
4599
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4600
+ // ch0x1y0R,ch0x1y0G,ch0x1y0B,ch0x1y0A,
4601
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4602
+ // ch0x1y1R,ch0x1y1G,ch0x1y1B,ch0x1y1A,
4603
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4604
+ // ch1x1y0R,ch1x1y0G,ch1x1y0B,ch1x1y0A,
4605
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4606
+ // ch1x1y1R,ch1x1y1G,ch1x1y1B,ch1x1y1A
3603
4607
  }
3604
- let py = chH - y;
3605
- let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
3606
- imgData[dataPos + 0] = rgbVal; //R
3607
- imgData[dataPos + 1] = rgbVal; //G
3608
- imgData[dataPos + 2] = rgbVal; //B
3609
- imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4608
+ // if (allBlack) {
4609
+ // console.log("Black: ", pii, " ", ch);
4610
+ // }
3610
4611
  }
3611
- // if (allBlack) {
3612
- // console.log("Black: ", pii, " ", ch);
3613
- // }
3614
4612
  }
3615
4613
  }
3616
4614
  }
3617
- postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw }, [imgData.buffer]);
4615
+ //console.debug("Render thread post message imgData: "+imgData.length)
4616
+ postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw, maxPsd: calcMaxPsd, terminate: msg.data.terminate }, [imgData.buffer]);
3618
4617
  };
3619
4618
  }
3620
4619
  startDraw(clear = true) {
@@ -3644,42 +4643,190 @@ class Sonagram extends AudioCanvasLayerComponent {
3644
4643
  if (this.bounds) {
3645
4644
  let w = Math.round(this.bounds.dimension.width);
3646
4645
  let h = Math.round(this.bounds.dimension.height);
3647
- if (this._audioData && w > 0 && h > 0) {
4646
+ if (this._audioDataHolder && w > 0 && h > 0) {
3648
4647
  this.worker = new Worker(this.workerURL);
3649
4648
  //this.wo = new Worker('./worker/sonagram.worker', { type: `module` });
3650
- let chs = this._audioData.numberOfChannels;
3651
- let frameLength = this._audioData.getChannelData(0).length;
3652
- let ada = new Array(chs);
3653
- for (let ch = 0; ch < chs; ch++) {
3654
- // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
3655
- ada[ch] = this._audioData.getChannelData(ch).buffer.slice(0);
3656
- }
3657
- let start = Date.now();
4649
+ let chs = this._audioDataHolder.numberOfChannels;
4650
+ let vw = Math.round(this.virtualDimension.width);
4651
+ let frameLength = this._audioDataHolder.frameLen;
4652
+ let framesPerPixel = Math.ceil(frameLength / vw);
4653
+ let leftPos = Math.round(this.bounds.position.left);
4654
+ let renderPos = leftPos;
4655
+ let audioBuffer = this._audioDataHolder.buffer;
4656
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
4657
+ let arrAbBuf;
4658
+ let imgData;
4659
+ let maxPsd = -Infinity;
4660
+ let norender = true;
3658
4661
  if (this.worker) {
3659
4662
  this.worker.onmessage = (me) => {
3660
- this.drawRendered(me);
3661
- if (this.worker) {
3662
- this.worker.terminate();
4663
+ if (true === me.data.terminate) {
4664
+ let drawImgData;
4665
+ if (imgData) {
4666
+ drawImgData = imgData;
4667
+ }
4668
+ else {
4669
+ drawImgData = me.data.imgData;
4670
+ }
4671
+ this.drawRendered(w, h, drawImgData);
4672
+ if (this.worker) {
4673
+ this.worker.terminate();
4674
+ }
4675
+ this.worker = null;
4676
+ }
4677
+ else {
4678
+ // set rendered vertical values of one pixel of timescale
4679
+ //let dataPos = renderPos * h * 4;
4680
+ if (norender) {
4681
+ if (me.data.maxPsd > maxPsd) {
4682
+ maxPsd = me.data.maxPsd;
4683
+ //console.debug("new maxPsd: "+maxPsd);
4684
+ }
4685
+ }
4686
+ else {
4687
+ let chH = Math.round(h / chs);
4688
+ let idp = me.data.l - leftPos;
4689
+ for (let ch = 0; ch < chs; ch++) {
4690
+ for (let y = 0; y < chH; y++) {
4691
+ let py = chH - y;
4692
+ let dataPos = ((((ch * chH) + py) * w) + idp) * 4;
4693
+ let mePos = ((ch * chH) + py) * 4;
4694
+ //let lastPos = dataPos + me.data.imgData.length;
4695
+ //if (lastPos < imgData.length) {
4696
+ // set one pixel
4697
+ imgData[dataPos] = me.data.imgData[mePos];
4698
+ imgData[dataPos + 1] = me.data.imgData[mePos + 1];
4699
+ imgData[dataPos + 2] = me.data.imgData[mePos + 2];
4700
+ imgData[dataPos + 3] = me.data.imgData[mePos + 3];
4701
+ //} else {
4702
+ //console.error("Out of range: " + dataPos + "+" + me.data.imgData.length + ">=" + imgData.length);
4703
+ // }
4704
+ }
4705
+ }
4706
+ }
4707
+ if (this._audioDataHolder && arrAbBuf && this.worker) {
4708
+ // proceed with next pixel
4709
+ renderPos++;
4710
+ //console.debug("Render pos: "+renderPos);
4711
+ let terminate = false;
4712
+ let windowEnd = renderPos >= leftPos + w;
4713
+ if (windowEnd) {
4714
+ if (norender) {
4715
+ // phase two: rendering
4716
+ norender = false;
4717
+ // start from beginning
4718
+ renderPos = leftPos;
4719
+ //console.debug("now rendering: maxPsd: "+maxPsd);
4720
+ }
4721
+ else {
4722
+ // terminate render phase
4723
+ terminate = true;
4724
+ }
4725
+ }
4726
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4727
+ if (leftFramePos < 0) {
4728
+ leftFramePos = 0;
4729
+ }
4730
+ let ada = new Array(chs);
4731
+ //console.debug("Render pos: "+renderPos+" leftFramePos: "+leftFramePos);
4732
+ if (!terminate) {
4733
+ if (this._audioDataHolder) {
4734
+ let read = this._audioDataHolder.frames(leftFramePos, this.dftSize, arrAbBuf);
4735
+ for (let ch = 0; ch < chs; ch++) {
4736
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4737
+ ada[ch] = arrAbBuf[ch].buffer.slice(0);
4738
+ }
4739
+ }
4740
+ }
4741
+ else {
4742
+ for (let ch = 0; ch < chs; ch++) {
4743
+ ada[ch] = new ArrayBuffer(0);
4744
+ }
4745
+ }
4746
+ this.worker.postMessage({
4747
+ audioData: ada,
4748
+ audioDataOffset: leftFramePos,
4749
+ l: renderPos,
4750
+ w: me.data.w,
4751
+ h: h,
4752
+ vw: vw,
4753
+ chs: chs,
4754
+ frameLength: frameLength,
4755
+ dftSize: this.dftSize,
4756
+ maxPsd: maxPsd,
4757
+ norender: norender,
4758
+ terminate: terminate
4759
+ }, ada);
4760
+ }
3663
4761
  }
3664
- this.worker = null;
3665
4762
  };
3666
4763
  }
3667
- if (this.markerCanvas) {
3668
- let g = this.markerCanvas.getContext("2d");
3669
- if (g) {
3670
- g.fillText("Rendering...", 10, 20);
4764
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
4765
+ let ada = new Array(chs);
4766
+ for (let ch = 0; ch < chs; ch++) {
4767
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4768
+ ada[ch] = audioBuffer.getChannelData(ch).buffer.slice(0);
4769
+ }
4770
+ let start = Date.now();
4771
+ if (this.markerCanvas) {
4772
+ let g = this.markerCanvas.getContext("2d");
4773
+ if (g) {
4774
+ g.fillText("Rendering...", 10, 20);
4775
+ }
4776
+ }
4777
+ this.worker.postMessage({
4778
+ audioData: ada,
4779
+ l: leftPos,
4780
+ w: w,
4781
+ h: h,
4782
+ vw: Math.round(this.virtualDimension.width),
4783
+ chs: chs,
4784
+ frameLength: frameLength,
4785
+ dftSize: this.dftSize,
4786
+ terminate: true
4787
+ }, ada);
4788
+ }
4789
+ else {
4790
+ if (w > 0) {
4791
+ if (framesPerPixel > 0) {
4792
+ let arrSize = w * h * 4;
4793
+ if (arrSize < 0) {
4794
+ arrSize = 0;
4795
+ }
4796
+ imgData = new Uint8ClampedArray(arrSize);
4797
+ let rw = 1;
4798
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
4799
+ arrAbBuf = new Array(chs);
4800
+ for (let ch = 0; ch < chs; ch++) {
4801
+ arrAbBuf[ch] = new Float32Array(this.dftSize);
4802
+ }
4803
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4804
+ let framesToRead = this.dftSize;
4805
+ if (leftFramePos < 0) {
4806
+ //framesToRead=this.dftSize+leftFramePos;
4807
+ leftFramePos = 0;
4808
+ }
4809
+ let read = this._audioDataHolder.frames(leftFramePos, framesToRead, arrAbBuf);
4810
+ let ad = new Float32Array(chs * framesToRead);
4811
+ for (let ch = 0; ch < chs; ch++) {
4812
+ ad.set(arrAbBuf[ch], ch * framesToRead);
4813
+ }
4814
+ this.worker.postMessage({
4815
+ l: renderPos,
4816
+ w: rw,
4817
+ h: h,
4818
+ vw: vw,
4819
+ chs: chs,
4820
+ frameLength: frameLength,
4821
+ audioData: ad,
4822
+ audioDataOffset: leftFramePos,
4823
+ dftSize: this.dftSize,
4824
+ norender: norender,
4825
+ terminate: false
4826
+ }, [ad.buffer]);
4827
+ }
3671
4828
  }
3672
4829
  }
3673
- this.worker.postMessage({
3674
- audioData: ada,
3675
- l: Math.round(this.bounds.position.left),
3676
- w: w,
3677
- h: h,
3678
- vw: Math.round(this.virtualDimension.width),
3679
- chs: chs,
3680
- frameLength: frameLength,
3681
- dftSize: this.dftSize
3682
- }, ada);
3683
4830
  }
3684
4831
  else {
3685
4832
  let g = this.sonagramCanvas.getContext("2d");
@@ -3689,17 +4836,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3689
4836
  }
3690
4837
  }
3691
4838
  }
3692
- drawRendered(me) {
4839
+ drawRendered(w, h, imgData) {
3693
4840
  if (this.sonagramCanvas) {
3694
- this.sonagramCanvas.width = me.data.w;
3695
- this.sonagramCanvas.height = me.data.h;
4841
+ this.sonagramCanvas.width = w;
4842
+ this.sonagramCanvas.height = h;
3696
4843
  let g = this.sonagramCanvas.getContext("2d");
3697
4844
  if (g) {
3698
- let imgDataArr = me.data.imgData;
3699
- if (me.data.w > 0 && me.data.h > 0) {
3700
- let imgData = g.createImageData(me.data.w, me.data.h);
3701
- imgData.data.set(imgDataArr);
3702
- g.putImageData(imgData, 0, 0);
4845
+ let imgDataArr = imgData;
4846
+ if (w > 0 && h > 0) {
4847
+ let gImgData = g.createImageData(w, h);
4848
+ gImgData.data.set(imgDataArr);
4849
+ g.putImageData(gImgData, 0, 0);
3703
4850
  }
3704
4851
  }
3705
4852
  }
@@ -3715,91 +4862,95 @@ class Sonagram extends AudioCanvasLayerComponent {
3715
4862
  g.clearRect(0, 0, w, h);
3716
4863
  g.fillStyle = "white";
3717
4864
  g.fillRect(0, 0, w, h);
3718
- if (this._audioData) {
4865
+ if (this._audioDataHolder) {
3719
4866
  let spectSize = Math.floor(this.dftSize / 2);
3720
- let chs = this._audioData.numberOfChannels;
4867
+ let chs = this._audioDataHolder.numberOfChannels;
3721
4868
  let chH = h / chs;
3722
- let frameLength = this._audioData.getChannelData(0).length;
4869
+ let frameLength = this._audioDataHolder.frameLen;
3723
4870
  let framesPerPixel = frameLength / w;
3724
4871
  let y = 0;
3725
- // TODO
3726
- let b = new Float32Array(this.dftSize);
3727
- let sona = new Array(chs);
3728
- let max = 0;
3729
- let maxPsd = -Infinity;
3730
- for (let ch = 0; ch < chs; ch++) {
3731
- let x = 0;
3732
- sona[ch] = new Array(w);
3733
- let chData = this._audioData.getChannelData(ch);
3734
- // TODO center buffer
3735
- let framePos = 0;
3736
- for (let pii = 0; pii < w; pii++) {
3737
- framePos = Math.round(pii * framesPerPixel);
3738
- // calculate DFT at pixel position
3739
- for (let i = 0; i < this.dftSize; i++) {
3740
- let chDat = chData[framePos + i];
3741
- b[i] = chDat;
3742
- }
3743
- let spectr = this.dft.processRealMagnitude(b);
3744
- sona[ch][pii] = spectr;
3745
- // @ts-ignore
3746
- let pMax = Math.max.apply(null, spectr);
3747
- if (pMax > max) {
3748
- max = pMax;
3749
- }
3750
- for (let s = 0; s < spectSize; s++) {
3751
- let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
3752
- if (psd > maxPsd) {
3753
- maxPsd = psd;
4872
+ let audioBuffer = this._audioDataHolder.buffer;
4873
+ let arrayAudioBuffer = this._audioDataHolder.arrayBuffer;
4874
+ if (audioBuffer) {
4875
+ let b = new Float32Array(this.dftSize);
4876
+ let sona = new Array(chs);
4877
+ let max = 0;
4878
+ let maxPsd = -Infinity;
4879
+ for (let ch = 0; ch < chs; ch++) {
4880
+ let x = 0;
4881
+ sona[ch] = new Array(w);
4882
+ let chData = audioBuffer.getChannelData(ch);
4883
+ // TODO center buffer
4884
+ let framePos = 0;
4885
+ for (let pii = 0; pii < w; pii++) {
4886
+ framePos = Math.round(pii * framesPerPixel);
4887
+ // calculate DFT at pixel position
4888
+ for (let i = 0; i < this.dftSize; i++) {
4889
+ let chDat = chData[framePos + i];
4890
+ b[i] = chDat;
3754
4891
  }
3755
- }
3756
- }
3757
- }
3758
- //console.log("max: ", max);
3759
- maxPsd = (2 * Math.pow(max, 2)) / spectSize;
3760
- for (let ch = 0; ch < chs; ch++) {
3761
- let framePos = 0;
3762
- for (let pii = 0; pii < w; pii++) {
3763
- framePos = pii * framesPerPixel;
3764
- for (let y = 0; y < h; y++) {
3765
- let freqIdx = Math.round(y * spectSize / h);
3766
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3767
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3768
- let val = sona[ch][pii][freqIdx];
3769
- let psd = (2 * Math.pow(val, 2)) / spectSize;
3770
- // Calculate logarithmic
3771
- let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
3772
- let dynRangeInDb = 70;
3773
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3774
- if (scaledVal > 1)
3775
- scaledVal = 1;
3776
- if (scaledVal < 0) {
3777
- scaledVal = 0;
4892
+ let spectr = this.dft.processRealMagnitude(b);
4893
+ sona[ch][pii] = spectr;
4894
+ // @ts-ignore
4895
+ let pMax = Math.max.apply(null, spectr);
4896
+ if (pMax > max) {
4897
+ max = pMax;
3778
4898
  }
3779
- let rgbVal = (255 * scaledVal);
3780
- if (rgbVal < 0) {
3781
- // System.out.println("Neg RGB val: "+rgbVal);
3782
- rgbVal = 0;
4899
+ for (let s = 0; s < spectSize; s++) {
4900
+ let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
4901
+ if (psd > maxPsd) {
4902
+ maxPsd = psd;
4903
+ }
3783
4904
  }
3784
- if (rgbVal > 255) {
3785
- rgbVal = 255;
4905
+ }
4906
+ }
4907
+ //console.log("max: ", max);
4908
+ maxPsd = (2 * Math.pow(max, 2)) / spectSize;
4909
+ for (let ch = 0; ch < chs; ch++) {
4910
+ let framePos = 0;
4911
+ for (let pii = 0; pii < w; pii++) {
4912
+ framePos = pii * framesPerPixel;
4913
+ for (let y = 0; y < h; y++) {
4914
+ let freqIdx = Math.round(y * spectSize / h);
4915
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4916
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4917
+ let val = sona[ch][pii][freqIdx];
4918
+ let psd = (2 * Math.pow(val, 2)) / spectSize;
4919
+ // Calculate logarithmic
4920
+ let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
4921
+ let dynRangeInDb = 70;
4922
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4923
+ if (scaledVal > 1)
4924
+ scaledVal = 1;
4925
+ if (scaledVal < 0) {
4926
+ scaledVal = 0;
4927
+ }
4928
+ let rgbVal = (255 * scaledVal);
4929
+ if (rgbVal < 0) {
4930
+ // System.out.println("Neg RGB val: "+rgbVal);
4931
+ rgbVal = 0;
4932
+ }
4933
+ if (rgbVal > 255) {
4934
+ rgbVal = 255;
4935
+ }
4936
+ rgbVal = 255 - rgbVal;
4937
+ let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
4938
+ g.fillStyle = colorStr;
4939
+ g.fillRect(pii, chH - y, 1, 1);
3786
4940
  }
3787
- rgbVal = 255 - rgbVal;
3788
- let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
3789
- g.fillStyle = colorStr;
3790
- g.fillRect(pii, chH - y, 1, 1);
3791
4941
  }
3792
4942
  }
4943
+ this.drawPlayPosition();
4944
+ }
4945
+ else if (arrayAudioBuffer) {
4946
+ throw Error("Redraw with array audio buffer not supported.");
3793
4947
  }
3794
- this.drawPlayPosition();
3795
4948
  }
3796
4949
  }
3797
4950
  }
3798
4951
  setData(audioData) {
3799
- this._audioData = audioData;
4952
+ this._audioDataHolder = audioData;
3800
4953
  this.playFramePosition = 0;
3801
- //this.redraw();
3802
- //this.startRender();
3803
4954
  }
3804
4955
  }
3805
4956
  Sonagram.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: Sonagram, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4024,13 +5175,13 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4024
5175
  }
4025
5176
  currentXZoom() {
4026
5177
  let xz = this._xZoom;
4027
- if (xz == null && this._audioData) {
5178
+ if (xz == null && this._audioDataHolder) {
4028
5179
  let ow = this.ce.offsetWidth;
4029
5180
  if (ow < 1) {
4030
5181
  // at least one pixel width to avoid x-zoom zero values
4031
5182
  ow = 1;
4032
5183
  }
4033
- xz = ow / this._audioData.duration;
5184
+ xz = ow / this._audioDataHolder.duration;
4034
5185
  }
4035
5186
  return xz;
4036
5187
  }
@@ -4109,9 +5260,10 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4109
5260
  this._layout(false, false);
4110
5261
  }
4111
5262
  layout(clear = true) {
5263
+ var _a;
4112
5264
  if (this.ce && this.dc) {
4113
5265
  const clientW = this.ce.clientWidth;
4114
- if (this._audioData) {
5266
+ if (this._audioDataHolder) {
4115
5267
  if (this._fixFitToPanel) {
4116
5268
  // Set the virtual canvas width to the visible width
4117
5269
  if (this.ce.style.width != '100%') {
@@ -4122,7 +5274,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4122
5274
  else {
4123
5275
  if (this._xZoom) {
4124
5276
  // Set the virtual canvas width according to the value of the user selected xZoom value
4125
- const newClW = Math.round(this._xZoom * this._audioData.duration);
5277
+ const newClW = Math.round(this._xZoom * ((_a = this._audioDataHolder) === null || _a === void 0 ? void 0 : _a.duration));
4126
5278
  this.ce.style.width = newClW + 'px';
4127
5279
  }
4128
5280
  else {
@@ -4134,22 +5286,22 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4134
5286
  this._layout(clear, true);
4135
5287
  }
4136
5288
  }
4137
- set audioData(audioData) {
5289
+ set audioData(audioDataHolder) {
4138
5290
  this._audioClip = null;
4139
- this._audioData = audioData;
4140
- this.as.setData(audioData);
4141
- this.so.setData(audioData);
5291
+ this._audioDataHolder = audioDataHolder;
5292
+ this.as.setData(audioDataHolder);
5293
+ this.so.setData(audioDataHolder);
4142
5294
  this.layout();
4143
5295
  }
4144
5296
  get audioData() {
4145
- return this._audioData;
5297
+ return this._audioDataHolder;
4146
5298
  }
4147
5299
  set audioClip(audioClip) {
4148
5300
  this._audioClip = audioClip;
4149
5301
  let audioData = null;
4150
5302
  let sel = null;
4151
5303
  if (audioClip) {
4152
- audioData = audioClip.buffer;
5304
+ audioData = audioClip.audioDataHolder;
4153
5305
  if (this._audioClip) {
4154
5306
  this._audioClip.addSelectionObserver((clip) => {
4155
5307
  this.selection = clip.selection;
@@ -4157,9 +5309,9 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4157
5309
  }
4158
5310
  sel = audioClip.selection;
4159
5311
  }
4160
- this._audioData = audioData;
4161
- this.as.setData(this._audioData);
4162
- this.so.setData(this._audioData);
5312
+ this._audioDataHolder = audioData;
5313
+ this.as.setData(this._audioDataHolder);
5314
+ this.so.setData(this._audioDataHolder);
4163
5315
  this.selecting = null;
4164
5316
  this.selection = sel;
4165
5317
  this.layout();
@@ -4351,7 +5503,7 @@ class AudioDisplayScrollPane {
4351
5503
  let audioData = null;
4352
5504
  let sel = null;
4353
5505
  if (audioClip) {
4354
- audioData = audioClip.buffer;
5506
+ audioData = audioClip.audioDataHolder;
4355
5507
  sel = audioClip.selection;
4356
5508
  audioClip.addSelectionObserver((clip) => {
4357
5509
  this.zoomSelectedAction.disabled = (clip.selection == null);
@@ -4622,17 +5774,17 @@ class AudioDisplay {
4622
5774
  console.log("Play started");
4623
5775
  this.status = 'Playing...';
4624
5776
  }
4625
- set audioData(audioBuffer) {
4626
- this.audioDisplayScrollPane.audioData = audioBuffer;
5777
+ set audioData(audioData) {
5778
+ this.audioDisplayScrollPane.audioData = audioData;
4627
5779
  if (this.playStartAction) {
4628
- this.playStartAction.disabled = (audioBuffer == null);
5780
+ this.playStartAction.disabled = (audioData == null);
4629
5781
  }
4630
5782
  }
4631
5783
  set audioClip(audioClip) {
4632
5784
  let audioData = null;
4633
5785
  let sel = null;
4634
5786
  if (audioClip) {
4635
- audioData = audioClip.buffer;
5787
+ audioData = audioClip.audioDataHolder.buffer;
4636
5788
  sel = audioClip.selection;
4637
5789
  }
4638
5790
  this._audioClip = audioClip;
@@ -4745,7 +5897,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
4745
5897
 
4746
5898
  class Progress {
4747
5899
  constructor() {
4748
- this.items = null;
5900
+ this.items = undefined;
4749
5901
  this.selectedItemIdx = 0;
4750
5902
  this.enableDownload = false;
4751
5903
  this.onRowSelect = new EventEmitter();
@@ -5485,7 +6637,7 @@ class Prompting {
5485
6637
  constructor() {
5486
6638
  this.promptItem = null;
5487
6639
  this.showPrompt = false;
5488
- this.items = null;
6640
+ this.items = undefined;
5489
6641
  this.enableDownload = false;
5490
6642
  this.audioSignalCollapsed = true;
5491
6643
  this.displayAudioClip = null;
@@ -5800,6 +6952,7 @@ class LevelBar {
5800
6952
  this._streamingMode = false;
5801
6953
  this._staticLevelInfos = null;
5802
6954
  this._playFramePosition = null;
6955
+ this._stateLoading = false;
5803
6956
  this.warnDBLevel = DEFAULT_WARN_DB_LEVEL$1;
5804
6957
  this.dbValues = new Array();
5805
6958
  }
@@ -5833,17 +6986,22 @@ class LevelBar {
5833
6986
  }
5834
6987
  }
5835
6988
  set displayLevelInfos(levelInfos) {
5836
- this._staticLevelInfos = levelInfos;
5837
6989
  if (levelInfos) {
6990
+ this._staticLevelInfos = levelInfos;
5838
6991
  this.dbValues = levelInfos.bufferLevelInfos.map((li) => {
5839
6992
  return li.powerLevelsDB();
5840
6993
  });
5841
6994
  }
5842
6995
  else {
6996
+ this._staticLevelInfos = null;
5843
6997
  this.dbValues = new Array();
5844
6998
  }
5845
6999
  this.layoutStatic();
5846
7000
  }
7001
+ set stateLoading(loading) {
7002
+ this._stateLoading = loading;
7003
+ this.layoutStatic();
7004
+ }
5847
7005
  set channelCount(channelCount) {
5848
7006
  this.reset();
5849
7007
  }
@@ -6024,6 +7182,12 @@ class LevelBar {
6024
7182
  }
6025
7183
  }
6026
7184
  }
7185
+ else if (this._stateLoading) {
7186
+ g.strokeStyle = 'white';
7187
+ g.fillStyle = 'white';
7188
+ g.font = '20px sans-serif';
7189
+ g.fillText("Loading...", 10, 25);
7190
+ }
6027
7191
  }
6028
7192
  }
6029
7193
  }
@@ -6066,7 +7230,7 @@ class LevelBar {
6066
7230
  }
6067
7231
  }
6068
7232
  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 });
6069
- 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: `
7233
+ 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: `
6070
7234
  <div #virtualCanvas>
6071
7235
  <canvas #levelbar></canvas>
6072
7236
  <canvas #markerCanvas></canvas>
@@ -6123,6 +7287,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
6123
7287
  type: Input
6124
7288
  }], displayLevelInfos: [{
6125
7289
  type: Input
7290
+ }], stateLoading: [{
7291
+ type: Input
6126
7292
  }], onResize: [{
6127
7293
  type: HostListener,
6128
7294
  args: ['window:resize', ['$event']]
@@ -6713,47 +7879,68 @@ class PeakLevelInterceptor {
6713
7879
  }
6714
7880
  }
6715
7881
  class LevelMeasure {
7882
+ //private bufferLevelInfos=new Array<LevelInfo>();
7883
+ //private peakLevelInfo!:LevelInfo;
6716
7884
  constructor() {
6717
7885
  this.worker = null;
6718
7886
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
6719
7887
  }
6720
- calcBufferLevelInfos(audioBuffer, bufferTimeLength) {
7888
+ calcBufferLevelInfos(audioDataHolder, bufferTimeLength) {
6721
7889
  return new Promise((resolve) => {
6722
- let chs = audioBuffer.numberOfChannels;
6723
- let bufferFrameLength = Math.round(audioBuffer.sampleRate * bufferTimeLength);
6724
- let buffers = new Array(chs);
7890
+ var _a;
7891
+ let chs = audioDataHolder.numberOfChannels;
7892
+ let bufferFrameLength = Math.round(audioDataHolder.sampleRate * bufferTimeLength);
7893
+ let ais = audioDataHolder.audioInputStream();
7894
+ let audioBuffers = new Array(chs);
7895
+ let trBuffers = new Array(chs);
6725
7896
  for (let ch = 0; ch < chs; ch++) {
6726
- let adCh = audioBuffer.getChannelData(ch);
6727
- let adChCopy = new Float32Array(adCh.length);
6728
- adChCopy.set(adCh);
6729
- buffers[ch] = adChCopy.buffer;
7897
+ audioBuffers[ch] = new Float32Array(bufferFrameLength);
6730
7898
  }
7899
+ let bufferLevelInfos = new Array();
7900
+ let peakLevelInfo = new LevelInfo(chs);
6731
7901
  this.worker = new Worker(this.workerURL);
6732
7902
  this.worker.onmessage = (me) => {
6733
- let linLevelArrs = new Array(chs);
6734
- for (let ch = 0; ch < chs; ch++) {
6735
- linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
6736
- }
6737
- let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
6738
- let framePos = 0;
6739
- let bufferLevelInfos = new Array();
6740
- let peakLevelInfo = new LevelInfo(chs);
6741
- for (let bi = 0; bi < bufferCount; bi++) {
6742
- let minLevels = new Array(chs);
6743
- let maxLevels = new Array(chs);
7903
+ var _a;
7904
+ if (me.data.linLevelBuffers) {
7905
+ let linLevelArrs = new Array(chs);
7906
+ for (let ch = 0; ch < chs; ch++) {
7907
+ linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
7908
+ }
7909
+ let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
7910
+ let framePos = 0;
7911
+ for (let bi = 0; bi < bufferCount; bi++) {
7912
+ let minLevels = new Array(chs);
7913
+ let maxLevels = new Array(chs);
7914
+ for (let ch = 0; ch < chs; ch++) {
7915
+ let linLvlArrPos = bi * 2;
7916
+ minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
7917
+ maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7918
+ }
7919
+ let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
7920
+ bufferLevelInfos.push(bli);
7921
+ peakLevelInfo.merge(bli);
7922
+ }
7923
+ }
7924
+ if (me.data.eod === true) {
7925
+ // end of data, terminate worker and return result
7926
+ this.terminateWorker();
7927
+ resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
7928
+ }
7929
+ else {
7930
+ let read = ais.read(audioBuffers);
6744
7931
  for (let ch = 0; ch < chs; ch++) {
6745
- let linLvlArrPos = bi * 2;
6746
- minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
6747
- maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7932
+ let copy = new Float32Array(audioBuffers[ch]);
7933
+ trBuffers[ch] = copy.buffer;
6748
7934
  }
6749
- let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
6750
- bufferLevelInfos.push(bli);
6751
- peakLevelInfo.merge(bli);
7935
+ (_a = this.worker) === null || _a === void 0 ? void 0 : _a.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6752
7936
  }
6753
- this.terminateWorker();
6754
- resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
6755
7937
  };
6756
- this.worker.postMessage({ bufferFrameLength: bufferFrameLength, audioData: buffers, chs: chs }, buffers);
7938
+ let read = ais.read(audioBuffers);
7939
+ for (let ch = 0; ch < chs; ch++) {
7940
+ let copy = new Float32Array(audioBuffers[ch]);
7941
+ trBuffers[ch] = copy.buffer;
7942
+ }
7943
+ (_a = this.worker) === null || _a === void 0 ? void 0 : _a.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6757
7944
  });
6758
7945
  }
6759
7946
  terminateWorker() {
@@ -6765,40 +7952,63 @@ class LevelMeasure {
6765
7952
  */
6766
7953
  workerFunction() {
6767
7954
  self.onmessage = function (msg) {
6768
- let chs = msg.data.chs;
7955
+ let len = msg.data.len;
6769
7956
  let bufferFrameLength = msg.data.bufferFrameLength;
6770
- let audioData = new Array(chs);
6771
- let linLevels = new Array(chs);
6772
- for (let ch = 0; ch < chs; ch++) {
6773
- audioData[ch] = new Float32Array(msg.data.audioData[ch]);
7957
+ if (len == -1) {
7958
+ // start
7959
+ postMessage({
7960
+ eod: false,
7961
+ bufferFrameLength: bufferFrameLength,
7962
+ frameLength: len
7963
+ });
6774
7964
  }
6775
- let frameLength = audioData[0].length;
6776
- let bufferCount = Math.ceil(frameLength / bufferFrameLength);
6777
- for (let ch = 0; ch < chs; ch++) {
6778
- linLevels[ch] = new Float32Array(bufferCount * 2);
7965
+ if (len == 0) {
7966
+ postMessage({
7967
+ eod: true,
7968
+ bufferFrameLength: bufferFrameLength,
7969
+ frameLength: len
7970
+ });
6779
7971
  }
6780
- if (audioData && chs > 0) {
7972
+ else {
7973
+ let chs = msg.data.chs;
7974
+ let audioData = new Array(chs);
7975
+ let linLevels = new Array(chs);
6781
7976
  for (let ch = 0; ch < chs; ch++) {
6782
- let chData = audioData[ch];
6783
- for (let s = 0; s < frameLength; s++) {
6784
- let bi = Math.floor(s / bufferFrameLength);
6785
- let lvlArrPos = bi * 2;
6786
- //let bs = s % bufferFrameLength;
6787
- let chSample = chData[s];
6788
- if (chSample < linLevels[ch][lvlArrPos]) {
6789
- linLevels[ch][lvlArrPos] = chSample;
6790
- }
6791
- lvlArrPos++;
6792
- if (chSample > linLevels[ch][lvlArrPos]) {
6793
- linLevels[ch][lvlArrPos] = chSample;
6794
- }
6795
- }
7977
+ audioData[ch] = new Float32Array(msg.data.audioData[ch]);
6796
7978
  }
6797
- const linLevelBufs = new Array(chs);
7979
+ //let frameLength = audioData[0].length;
7980
+ let bufferCount = Math.ceil(len / bufferFrameLength);
6798
7981
  for (let ch = 0; ch < chs; ch++) {
6799
- linLevelBufs[ch] = linLevels[ch].buffer;
7982
+ linLevels[ch] = new Float32Array(bufferCount * 2);
7983
+ }
7984
+ if (audioData && chs > 0) {
7985
+ for (let ch = 0; ch < chs; ch++) {
7986
+ let chData = audioData[ch];
7987
+ for (let s = 0; s < len; s++) {
7988
+ let bi = Math.floor(s / bufferFrameLength);
7989
+ let lvlArrPos = bi * 2;
7990
+ //let bs = s % bufferFrameLength;
7991
+ let chSample = chData[s];
7992
+ if (chSample < linLevels[ch][lvlArrPos]) {
7993
+ linLevels[ch][lvlArrPos] = chSample;
7994
+ }
7995
+ lvlArrPos++;
7996
+ if (chSample > linLevels[ch][lvlArrPos]) {
7997
+ linLevels[ch][lvlArrPos] = chSample;
7998
+ }
7999
+ }
8000
+ }
8001
+ const linLevelBufs = new Array(chs);
8002
+ for (let ch = 0; ch < chs; ch++) {
8003
+ linLevelBufs[ch] = linLevels[ch].buffer;
8004
+ }
8005
+ postMessage({
8006
+ eod: false,
8007
+ bufferFrameLength: bufferFrameLength,
8008
+ frameLength: len,
8009
+ linLevelBuffers: linLevelBufs
8010
+ }, linLevelBufs);
6800
8011
  }
6801
- postMessage({ bufferFrameLength: bufferFrameLength, frameLength: frameLength, linLevelBuffers: linLevelBufs }, linLevelBufs);
6802
8012
  }
6803
8013
  };
6804
8014
  }
@@ -7174,7 +8384,7 @@ RecordingItemDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", v
7174
8384
  <audio-levelbar fxFlex="1 0 1" [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
7175
8385
  <spr-recordingitemcontrols fxFlex="0 0 0" [audioLoaded]="displayAudioBuffer!==null" [playStartAction]="playStartAction" [playStopAction]="playStopAction" [peakDbLvl]="peakDbLvl" [agc]="_agc" (onShowRecordingDetails)="onShowRecordingDetails.emit()"></spr-recordingitemcontrols>
7176
8386
  </div>
7177
- `, 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"] }] });
8387
+ `, 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"] }] });
7178
8388
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingItemDisplay, decorators: [{
7179
8389
  type: Component,
7180
8390
  args: [{
@@ -7220,89 +8430,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
7220
8430
  type: Input
7221
8431
  }] } });
7222
8432
 
7223
- class Float32ArrayChunkerOutStream {
7224
- constructor(outStream) {
7225
- this.outStream = outStream;
7226
- this.bufs = new Array();
7227
- this._channels = 0;
7228
- this._chunkSize = 0;
7229
- this.filled = 0;
7230
- this.receivedFrames = 0;
7231
- this.sentFrames = 0;
7232
- }
7233
- createBuffers() {
7234
- this.bufs = new Array(this._channels);
7235
- for (let ch = 0; ch < this._channels; ch++) {
7236
- this.bufs[ch] = new Float32Array(this._chunkSize);
7237
- }
7238
- }
7239
- set chunkSize(chunkSize) {
7240
- this._chunkSize = chunkSize;
7241
- this.createBuffers();
7242
- }
7243
- get chunkSize() {
7244
- return this._chunkSize;
7245
- }
7246
- set channels(channels) {
7247
- this._channels = channels;
7248
- this.createBuffers();
7249
- }
7250
- get channels() {
7251
- return this._channels;
7252
- }
7253
- available() {
7254
- return this._chunkSize - this.filled;
7255
- }
7256
- write(buffers) {
7257
- let copied = 0;
7258
- if (buffers.length > 0) {
7259
- let buffersLen = buffers[0].length;
7260
- this.receivedFrames += buffersLen;
7261
- let avail = buffersLen;
7262
- // Fill out buffers until all values copied
7263
- while (avail > 0) {
7264
- let toFill = this._chunkSize - this.filled;
7265
- if (toFill > avail) {
7266
- toFill = avail;
7267
- }
7268
- let sliceEnd = copied + toFill;
7269
- // Firefox on Android sends only the first channel
7270
- for (let ch = 0; ch < buffersLen; ch++) {
7271
- if (buffers[ch]) {
7272
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
7273
- let buf = this.bufs[ch];
7274
- buf.set(cpPrt, this.filled);
7275
- }
7276
- }
7277
- copied += toFill;
7278
- avail -= toFill;
7279
- this.filled += toFill;
7280
- if (this.filled == this._chunkSize) {
7281
- this.outStream.write(this.bufs);
7282
- this.sentFrames += this.filled;
7283
- this.filled = 0;
7284
- }
7285
- }
7286
- }
7287
- return copied;
7288
- }
7289
- flush() {
7290
- if (this.filled > 0) {
7291
- let restBufs = new Array(this._channels);
7292
- for (let ch = 0; ch < this._channels; ch++) {
7293
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
7294
- }
7295
- this.outStream.write(restBufs);
7296
- this.outStream.flush();
7297
- this.sentFrames += this.filled;
7298
- this.filled = 0;
7299
- }
7300
- }
7301
- close() {
7302
- this.outStream.close();
7303
- }
7304
- }
7305
-
7306
8433
  class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
7307
8434
  constructor(outStream, chunkDurationSeconds) {
7308
8435
  super(outStream);
@@ -7311,11 +8438,11 @@ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream
7311
8438
  this.sequenceAudioFloat32OutStream = outStream;
7312
8439
  }
7313
8440
  setFormat(channels, sampleRate) {
7314
- console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: " + channels + ",sampleRate: " + sampleRate + ")");
8441
+ //console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "+channels+",sampleRate: "+sampleRate+")")
7315
8442
  this.channels = channels;
7316
8443
  this.sampleRate = sampleRate;
7317
8444
  this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
7318
- console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: " + this.chunkSize);
8445
+ //console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "+this.chunkSize);
7319
8446
  this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
7320
8447
  }
7321
8448
  nextStream() {
@@ -7522,7 +8649,8 @@ class ChunkManager {
7522
8649
  }
7523
8650
  }
7524
8651
  let BasicRecorder = class BasicRecorder {
7525
- constructor(dialog, sessionService, uploader, config) {
8652
+ constructor(changeDetectorRef, dialog, sessionService, uploader, config) {
8653
+ this.changeDetectorRef = changeDetectorRef;
7526
8654
  this.dialog = dialog;
7527
8655
  this.sessionService = sessionService;
7528
8656
  this.uploader = uploader;
@@ -7537,14 +8665,15 @@ let BasicRecorder = class BasicRecorder {
7537
8665
  this._selectedDeviceId = undefined;
7538
8666
  this._channelCount = 2;
7539
8667
  this._session = null;
8668
+ this._recordingFile = null;
7540
8669
  this.startedDate = null;
7541
8670
  this.uploadProgress = 100;
7542
8671
  this.uploadStatus = 'ok';
7543
8672
  this.audioSignalCollapsed = true;
7544
8673
  this.peakLevelInDb = MIN_DB_LEVEL;
7545
- this.displayLevelInfos = null;
7546
8674
  this.displayAudioClip = null;
7547
8675
  this.audioFetchSubscription = null;
8676
+ this.audioFetching = false;
7548
8677
  this.destroyed = false;
7549
8678
  this.navigationDisabled = true;
7550
8679
  // Default: Disabled chunked upload
@@ -7639,6 +8768,28 @@ let BasicRecorder = class BasicRecorder {
7639
8768
  this.ac.audioOutStream = outStream;
7640
8769
  }
7641
8770
  }
8771
+ showRecording() {
8772
+ this._controlAudioPlayer.stop();
8773
+ if (this.displayAudioClip) {
8774
+ let dap = this.displayAudioClip;
8775
+ let adh = dap.audioDataHolder;
8776
+ if (adh) {
8777
+ this.levelMeasure.calcBufferLevelInfos(adh, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8778
+ dap.levelInfos = levelInfos;
8779
+ this.changeDetectorRef.detectChanges();
8780
+ });
8781
+ }
8782
+ this.playStartAction.disabled = false;
8783
+ }
8784
+ else {
8785
+ this.playStartAction.disabled = true;
8786
+ // Collapse audio signal display if open
8787
+ if (!this.audioSignalCollapsed) {
8788
+ this.audioSignalCollapsed = true;
8789
+ }
8790
+ }
8791
+ this.changeDetectorRef.detectChanges();
8792
+ }
7642
8793
  start() {
7643
8794
  this.statusAlertType = 'info';
7644
8795
  this.statusMsg = 'Starting session...';
@@ -7859,9 +9010,9 @@ let BasicRecorder = class BasicRecorder {
7859
9010
  }
7860
9011
  this.transportActions.startAction.disabled = true;
7861
9012
  }
7862
- postRecording(wavFile, recUrl) {
9013
+ postRecording(wavFile, recUrl, rf) {
7863
9014
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
7864
- let ul = new Upload(wavBlob, recUrl);
9015
+ let ul = new Upload(wavBlob, recUrl, rf);
7865
9016
  this.uploader.queueUpload(ul);
7866
9017
  }
7867
9018
  postAudioStreamStart() {
@@ -7900,39 +9051,344 @@ let BasicRecorder = class BasicRecorder {
7900
9051
  if (apiEndPoint !== '') {
7901
9052
  apiEndPoint = apiEndPoint + '/';
7902
9053
  }
7903
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
7904
- let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
7905
- let fd = new FormData();
7906
- fd.set('uuid', this.rfUuid);
7907
- fd.set('chunkCount', chunkCount.toString());
7908
- let ul = new Upload(fd, recUrl);
7909
- this.uploader.queueUpload(ul);
9054
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9055
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
9056
+ let fd = new FormData();
9057
+ fd.set('uuid', this.rfUuid);
9058
+ fd.set('chunkCount', chunkCount.toString());
9059
+ let ul = new Upload(fd, recUrl, this._recordingFile);
9060
+ this.uploader.queueUpload(ul);
9061
+ console.debug("Queued for upload: " + this._recordingFile);
9062
+ }
9063
+ else {
9064
+ console.error("Recording file UUID not set!");
9065
+ }
9066
+ }
9067
+ closed() {
9068
+ this.statusAlertType = 'info';
9069
+ this.statusMsg = 'Session closed.';
9070
+ }
9071
+ error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
9072
+ this.statusMsg = 'ERROR: Recording.';
9073
+ this.statusAlertType = 'error';
9074
+ this.dialog.open(MessageDialog, {
9075
+ data: {
9076
+ type: 'error',
9077
+ title: 'Recording error',
9078
+ msg: msg,
9079
+ advice: advice
9080
+ }
9081
+ });
9082
+ }
9083
+ };
9084
+ // Enable only for developemnt/debug purposes of array audio buffers !!
9085
+ BasicRecorder.FORCE_ARRRAY_AUDIO_BUFFER = false;
9086
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
9087
+ BasicRecorder = __decorate([
9088
+ __param(4, Inject(SPEECHRECORDER_CONFIG))
9089
+ ], BasicRecorder);
9090
+
9091
+ class AudioDataHolder {
9092
+ constructor(_buffer, _arrayBuffer = null) {
9093
+ this._buffer = _buffer;
9094
+ this._arrayBuffer = _arrayBuffer;
9095
+ this._numberOfChannels = 0;
9096
+ this._sampleRate = 0;
9097
+ this._frameLen = 0;
9098
+ this._duration = 0;
9099
+ if (this._buffer && this._arrayBuffer) {
9100
+ throw Error('Only one of either audio buffer or array audio buffer must be set!');
9101
+ }
9102
+ if (this._buffer || this._arrayBuffer) {
9103
+ if (this._buffer) {
9104
+ this._numberOfChannels = this._buffer.numberOfChannels;
9105
+ this._sampleRate = this._buffer.sampleRate;
9106
+ this._frameLen = this._buffer.length;
9107
+ this._duration = this._frameLen / this._sampleRate;
9108
+ }
9109
+ else if (this._arrayBuffer) {
9110
+ this._numberOfChannels = this._arrayBuffer.channelCount;
9111
+ this._sampleRate = this._arrayBuffer.sampleRate;
9112
+ this._frameLen = this._arrayBuffer.frameLen;
9113
+ this._duration = this._frameLen / this._sampleRate;
9114
+ }
9115
+ }
9116
+ else {
9117
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9118
+ }
9119
+ }
9120
+ get duration() {
9121
+ return this._duration;
9122
+ }
9123
+ get sampleRate() {
9124
+ return this._sampleRate;
9125
+ }
9126
+ get numberOfChannels() {
9127
+ return this._numberOfChannels;
9128
+ }
9129
+ get frameLen() {
9130
+ return this._frameLen;
9131
+ }
9132
+ sampleCounts() {
9133
+ return this._numberOfChannels * this._frameLen;
9134
+ }
9135
+ frames(framePos, frameLen, bufs) {
9136
+ let read = 0;
9137
+ if (this._buffer) {
9138
+ let toRead = frameLen;
9139
+ if (this._buffer.length < framePos + frameLen) {
9140
+ toRead = this._buffer.length - framePos;
9141
+ }
9142
+ for (let ch = 0; ch < bufs.length; ch++) {
9143
+ let chData = this._buffer.getChannelData(ch);
9144
+ for (let i = 0; i < toRead; i++) {
9145
+ bufs[ch][i] = chData[framePos + i];
9146
+ }
9147
+ }
9148
+ read = toRead;
9149
+ }
9150
+ else if (this._arrayBuffer) {
9151
+ read = this._arrayBuffer.frames(framePos, frameLen, bufs);
9152
+ }
9153
+ return read;
9154
+ }
9155
+ audioInputStream() {
9156
+ if (this._buffer) {
9157
+ return new AudioBufferInputStream(this._buffer);
9158
+ }
9159
+ if (this._arrayBuffer) {
9160
+ return new ArrayAudioBufferInputStream(this._arrayBuffer);
9161
+ }
9162
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9163
+ }
9164
+ get buffer() {
9165
+ return this._buffer;
9166
+ }
9167
+ get arrayBuffer() {
9168
+ return this._arrayBuffer;
9169
+ }
9170
+ }
9171
+ AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG = 'One of either audio buffer or array audio buffer must be set!';
9172
+ class AudioBufferInputStream {
9173
+ constructor(audioBuffer) {
9174
+ this.audioBuffer = audioBuffer;
9175
+ this.framePos = 0;
9176
+ }
9177
+ close() {
9178
+ }
9179
+ skipFrames(n) {
9180
+ this.framePos += n;
9181
+ }
9182
+ read(buffers) {
9183
+ let read = 0;
9184
+ let toRead = buffers[0].length;
9185
+ if (this.framePos + toRead > this.audioBuffer.length) {
9186
+ toRead = this.audioBuffer.length - this.framePos;
9187
+ }
9188
+ for (let ch = 0; ch < buffers.length; ch++) {
9189
+ for (let i = 0; i < toRead; i++) {
9190
+ buffers[ch][i] = this.audioBuffer.getChannelData(ch)[this.framePos + i];
9191
+ }
9192
+ }
9193
+ read = toRead;
9194
+ this.framePos += toRead;
9195
+ return read;
9196
+ }
9197
+ }
9198
+
9199
+ class BasicRecFilesCache {
9200
+ constructor() {
9201
+ //public static readonly DEFAULT_MAX_SAMPLES=30*48000; // TEST 30 seconds
9202
+ this._sampleCount = 0;
9203
+ this.maxSampleCount = BasicRecFilesCache.DEFAULT_MAX_SAMPLES;
9204
+ this._currentRecordingFile = null;
9205
+ }
9206
+ get currentRecordingFile() {
9207
+ return this._currentRecordingFile;
9208
+ }
9209
+ set currentRecordingFile(value) {
9210
+ this._currentRecordingFile = value;
9211
+ this.expire();
9212
+ }
9213
+ }
9214
+ BasicRecFilesCache.DEBUG = false;
9215
+ BasicRecFilesCache.DEFAULT_MAX_SAMPLES = 10 * 60 * 48000; // 20 Minutes mono 48kHz
9216
+ class SprItemsCache extends BasicRecFilesCache {
9217
+ constructor() {
9218
+ super(...arguments);
9219
+ this._items = new Array();
9220
+ }
9221
+ get items() {
9222
+ return this._items;
9223
+ }
9224
+ addItem(item) {
9225
+ this._items.push(item);
9226
+ }
9227
+ getItem(index) {
9228
+ return this._items[index];
9229
+ }
9230
+ length() {
9231
+ return this._items.length;
9232
+ }
9233
+ tryExpire(toBeExpiredRf) {
9234
+ if (BasicRecFilesCache.DEBUG)
9235
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " try expire:");
9236
+ if (!RecordingFileUtils.equals(toBeExpiredRf, this.currentRecordingFile)) {
9237
+ if (BasicRecFilesCache.DEBUG)
9238
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " not current file...");
9239
+ if (toBeExpiredRf.serverPersisted) {
9240
+ if (BasicRecFilesCache.DEBUG)
9241
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is server persisted...");
9242
+ // expire recording files first stored to the cache
9243
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9244
+ if (expiredAudio) {
9245
+ this._sampleCount -= expiredAudio.sampleCounts();
9246
+ if (BasicRecFilesCache.DEBUG)
9247
+ console.debug("Rec. files cache: Expired: " + toBeExpiredRf.toString() + ". Cache samples now: " + this._sampleCount);
9248
+ }
9249
+ }
9250
+ else {
9251
+ if (BasicRecFilesCache.DEBUG)
9252
+ console.debug("Rec. files cache: #" + toBeExpiredRf.toString() + " not yet persisted on server.");
9253
+ }
9254
+ }
9255
+ else {
9256
+ if (BasicRecFilesCache.DEBUG)
9257
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is current file.");
9258
+ }
9259
+ }
9260
+ expire() {
9261
+ // expire corrected versions first
9262
+ if (BasicRecFilesCache.DEBUG)
9263
+ console.debug("Rec. files cache: Expire? current: " + this._sampleCount + ", max: " + this.maxSampleCount);
9264
+ if (this._sampleCount > this.maxSampleCount) {
9265
+ // expire older versions of an item first
9266
+ for (let ii = 0; ii < this._items.length; ii++) {
9267
+ if (this._sampleCount <= this.maxSampleCount) {
9268
+ break;
9269
+ }
9270
+ let it = this._items[ii];
9271
+ let itRfs = it.recs;
9272
+ if (itRfs && itRfs.length > 1) {
9273
+ for (let rfi = 0; rfi < itRfs.length - 1; rfi++) {
9274
+ if (this._sampleCount <= this.maxSampleCount) {
9275
+ break;
9276
+ }
9277
+ let toBeExpiredRf = itRfs[rfi];
9278
+ this.tryExpire(toBeExpiredRf);
9279
+ }
9280
+ }
9281
+ }
9282
+ // if that's not sufficient expire audio data of latest versions as well
9283
+ for (let ii = 0; ii < this._items.length; ii++) {
9284
+ if (this._sampleCount <= this.maxSampleCount) {
9285
+ break;
9286
+ }
9287
+ let it = this._items[ii];
9288
+ let itRfs = it.recs;
9289
+ if (itRfs && itRfs.length > 0) {
9290
+ for (let rfi = 0; rfi < itRfs.length; rfi++) {
9291
+ if (this._sampleCount <= this.maxSampleCount) {
9292
+ break;
9293
+ }
9294
+ let toBeExpiredRf = itRfs[rfi];
9295
+ this.tryExpire(toBeExpiredRf);
9296
+ }
9297
+ }
9298
+ }
7910
9299
  }
7911
- else {
7912
- console.error("Recording file UUID not set!");
9300
+ }
9301
+ addSprRecFile(item, sprRecFile) {
9302
+ this.expire();
9303
+ if (!item.recs) {
9304
+ item.recs = new Array();
7913
9305
  }
9306
+ item.recs.push(sprRecFile);
9307
+ this._sampleCount += RecordingFileUtils.sampleCount(sprRecFile);
9308
+ if (BasicRecFilesCache.DEBUG)
9309
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
7914
9310
  }
7915
- closed() {
7916
- this.statusAlertType = 'info';
7917
- this.statusMsg = 'Session closed.';
9311
+ setSprRecFileAudioData(sprRecFile, adh) {
9312
+ this.expire();
9313
+ if (BasicRecFilesCache.DEBUG)
9314
+ console.debug("Rec. files cache: Set audio data after expire. Cache samples: " + this._sampleCount);
9315
+ let currSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9316
+ this._sampleCount -= currSampleCnt;
9317
+ if (BasicRecFilesCache.DEBUG)
9318
+ console.debug("Rec. files cache: Set audio data subtracted curr sample count: " + currSampleCnt + ". Cache samples: " + this._sampleCount);
9319
+ RecordingFileUtils.setAudioData(sprRecFile, adh);
9320
+ let newSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9321
+ this._sampleCount += newSampleCnt;
9322
+ if (BasicRecFilesCache.DEBUG)
9323
+ console.debug("Rec. files cache: Set audio data added new sample count: " + newSampleCnt + ". Cache samples: " + this._sampleCount);
9324
+ let fl = adh === null || adh === void 0 ? void 0 : adh.frameLen;
9325
+ if (fl) {
9326
+ sprRecFile.frames = fl;
9327
+ }
9328
+ if (BasicRecFilesCache.DEBUG)
9329
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
7918
9330
  }
7919
- error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
7920
- this.statusMsg = 'ERROR: Recording.';
7921
- this.statusAlertType = 'error';
7922
- this.dialog.open(MessageDialog, {
7923
- data: {
7924
- type: 'error',
7925
- title: 'Recording error',
7926
- msg: msg,
7927
- advice: advice
9331
+ }
9332
+ class RecFilesCache extends BasicRecFilesCache {
9333
+ constructor() {
9334
+ super(...arguments);
9335
+ this._recFiles = new Array();
9336
+ }
9337
+ get recFiles() {
9338
+ return this._recFiles;
9339
+ }
9340
+ getRecFile(index) {
9341
+ return this._recFiles[index];
9342
+ }
9343
+ length() {
9344
+ return this._recFiles.length;
9345
+ }
9346
+ expire() {
9347
+ if (this._sampleCount > this.maxSampleCount) {
9348
+ // audio recorder list is sorted: lower index is newer
9349
+ for (let rfI = this._recFiles.length - 1; rfI >= 0; rfI--) {
9350
+ if (this._sampleCount <= this.maxSampleCount) {
9351
+ break;
9352
+ }
9353
+ let toBeExpiredRf = this._recFiles[rfI];
9354
+ if (toBeExpiredRf.serverPersisted) {
9355
+ if (!RecordingFileUtils.equals(toBeExpiredRf, this.currentRecordingFile)) {
9356
+ // expire recording files first stored to the cache
9357
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9358
+ if (expiredAudio) {
9359
+ this._sampleCount -= expiredAudio.sampleCounts();
9360
+ if (BasicRecFilesCache.DEBUG)
9361
+ console.debug("Rec. files cache: Expired #" + rfI + ". Cache samples: " + this._sampleCount);
9362
+ }
9363
+ }
9364
+ else {
9365
+ if (BasicRecFilesCache.DEBUG)
9366
+ console.debug("Rec. files cache: #" + rfI + " is current file. (not expiring)");
9367
+ }
9368
+ }
9369
+ else {
9370
+ if (BasicRecFilesCache.DEBUG)
9371
+ console.debug("Rec. files cache: #" + rfI + " not yet server persisted.");
9372
+ }
7928
9373
  }
7929
- });
9374
+ }
7930
9375
  }
7931
- };
7932
- BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7933
- BasicRecorder = __decorate([
7934
- __param(3, Inject(SPEECHRECORDER_CONFIG))
7935
- ], BasicRecorder);
9376
+ addRecFile(recFile) {
9377
+ this.expire();
9378
+ this._recFiles.push(recFile);
9379
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9380
+ if (BasicRecFilesCache.DEBUG)
9381
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
9382
+ }
9383
+ setRecFileAudioData(recFile, adh) {
9384
+ this.expire();
9385
+ this._sampleCount -= RecordingFileUtils.sampleCount(recFile);
9386
+ RecordingFileUtils.setAudioData(recFile, adh);
9387
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9388
+ if (BasicRecFilesCache.DEBUG)
9389
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9390
+ }
9391
+ }
7936
9392
 
7937
9393
  /**
7938
9394
  * Created by klausj on 17.06.2017.
@@ -8025,16 +9481,62 @@ class RecordingService {
8025
9481
  let recUrl = recFilesUrl + '/' + encItemcode + '/' + version;
8026
9482
  return this.audioRequest(recUrl);
8027
9483
  }
9484
+ fetchRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9485
+ let wobs = new Observable(observer => {
9486
+ let recFileId = recordingFile.recordingFileId;
9487
+ if (!recFileId) {
9488
+ recFileId = recordingFile.uuid;
9489
+ }
9490
+ if (recordingFile.session && recFileId) {
9491
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
9492
+ obs.subscribe(resp => {
9493
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9494
+ if (resp.body) {
9495
+ aCtx.decodeAudioData(resp.body, ab => {
9496
+ observer.next(ab);
9497
+ observer.complete();
9498
+ }, error => {
9499
+ observer.error(error);
9500
+ observer.complete();
9501
+ });
9502
+ }
9503
+ else {
9504
+ observer.error('Fetching audio file: response has no body');
9505
+ }
9506
+ }, (error) => {
9507
+ if (error.status == 404) {
9508
+ // Interpret not as an error, the file ist not recorded yet
9509
+ observer.next(null);
9510
+ observer.complete();
9511
+ }
9512
+ else {
9513
+ // all other states are errors
9514
+ observer.error(error);
9515
+ observer.complete();
9516
+ }
9517
+ });
9518
+ }
9519
+ else {
9520
+ observer.error();
9521
+ }
9522
+ });
9523
+ return wobs;
9524
+ }
8028
9525
  fetchAndApplyRecordingFile(aCtx, projectName, recordingFile) {
8029
9526
  let wobs = new Observable(observer => {
8030
- if (recordingFile.session && recordingFile.recordingFileId) {
8031
- let obs = this.fetchAudiofile(projectName, recordingFile.session, recordingFile.recordingFileId);
9527
+ let recFileId = recordingFile.recordingFileId;
9528
+ if (!recFileId) {
9529
+ recFileId = recordingFile.uuid;
9530
+ }
9531
+ if (recordingFile.session && recFileId) {
9532
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
8032
9533
  obs.subscribe(resp => {
8033
9534
  //console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
8034
9535
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8035
9536
  if (resp.body) {
8036
9537
  aCtx.decodeAudioData(resp.body, ab => {
8037
- recordingFile.audioBuffer = ab;
9538
+ let adh = new AudioDataHolder(ab, null);
9539
+ RecordingFileUtils.setAudioData(recordingFile, adh);
8038
9540
  if (this.debugDelay > 0) {
8039
9541
  window.setTimeout(() => {
8040
9542
  observer.next(recordingFile);
@@ -8072,6 +9574,47 @@ class RecordingService {
8072
9574
  });
8073
9575
  return wobs;
8074
9576
  }
9577
+ fetchSprRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9578
+ let wobs = new Observable(observer => {
9579
+ if (recordingFile.session) {
9580
+ let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
9581
+ obs.subscribe({
9582
+ next: resp => {
9583
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9584
+ if (resp.body) {
9585
+ aCtx.decodeAudioData(resp.body, ab => {
9586
+ //RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
9587
+ observer.next(ab);
9588
+ observer.complete();
9589
+ }, error => {
9590
+ observer.error(error);
9591
+ observer.complete();
9592
+ });
9593
+ }
9594
+ else {
9595
+ observer.error('Fetching audio file: response has no body');
9596
+ }
9597
+ },
9598
+ error: (error) => {
9599
+ if (error.status == 404) {
9600
+ // Interpret not as an error, the file ist not recorded yet
9601
+ observer.next(null);
9602
+ observer.complete();
9603
+ }
9604
+ else {
9605
+ // all other states are errors
9606
+ observer.error(error);
9607
+ observer.complete();
9608
+ }
9609
+ }
9610
+ });
9611
+ }
9612
+ else {
9613
+ observer.error();
9614
+ }
9615
+ });
9616
+ return wobs;
9617
+ }
8075
9618
  fetchAndApplySprRecordingFile(aCtx, projectName, recordingFile) {
8076
9619
  let wobs = new Observable(observer => {
8077
9620
  if (recordingFile.session) {
@@ -8081,7 +9624,7 @@ class RecordingService {
8081
9624
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8082
9625
  if (resp.body) {
8083
9626
  aCtx.decodeAudioData(resp.body, ab => {
8084
- recordingFile.audioBuffer = ab;
9627
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab, null));
8085
9628
  if (this.debugDelay > 0) {
8086
9629
  window.setTimeout(() => {
8087
9630
  observer.next(recordingFile);
@@ -8126,7 +9669,8 @@ class RecordingService {
8126
9669
  // Do not use Promise version, which does not work with Safari 13
8127
9670
  if (resp.body) {
8128
9671
  aCtx.decodeAudioData(resp.body, ab => {
8129
- let rf = new SprRecordingFile(sessId, itemcode, version, ab);
9672
+ let adh = new AudioDataHolder(ab, null);
9673
+ let rf = new SprRecordingFile(sessId, itemcode, version, adh);
8130
9674
  if (this.debugDelay > 0) {
8131
9675
  window.setTimeout(() => {
8132
9676
  observer.next(rf);
@@ -8246,14 +9790,9 @@ const DEFAULT_PRE_REC_DELAY = 1000;
8246
9790
  const DEFAULT_POST_REC_DELAY = 500;
8247
9791
  class SessionManager extends BasicRecorder {
8248
9792
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
8249
- super(dialog, sessionService, uploader, config);
8250
- this.changeDetectorRef = changeDetectorRef;
9793
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
8251
9794
  this.renderer = renderer;
8252
- this.dialog = dialog;
8253
- this.sessionService = sessionService;
8254
9795
  this.recFileService = recFileService;
8255
- this.uploader = uploader;
8256
- this.config = config;
8257
9796
  this.enableUploadRecordings = true;
8258
9797
  this.enableDownloadRecordings = false;
8259
9798
  this.status = 0 /* BLOCKED */;
@@ -8287,7 +9826,7 @@ class SessionManager extends BasicRecorder {
8287
9826
  };
8288
9827
  }
8289
9828
  ngOnDestroy() {
8290
- console.debug("Com destroy, disable wake lock.");
9829
+ //console.debug("Com destroy, disable wake lock.")
8291
9830
  this.disableWakeLockCond();
8292
9831
  this.destroyed = true;
8293
9832
  // TODO stop capture /playback
@@ -8418,7 +9957,7 @@ class SessionManager extends BasicRecorder {
8418
9957
  progressPercentValue() {
8419
9958
  let v = 100;
8420
9959
  if (this.items) {
8421
- v = this.promptIndex * 100 / (this.items.length - 1);
9960
+ v = this.promptIndex * 100 / (this.items.length() - 1);
8422
9961
  }
8423
9962
  return v;
8424
9963
  }
@@ -8432,9 +9971,6 @@ class SessionManager extends BasicRecorder {
8432
9971
  return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
8433
9972
  }
8434
9973
  set controlAudioPlayer(controlAudioPlayer) {
8435
- if (this._controlAudioPlayer) {
8436
- //this._controlAudioPlayer.listener=null;
8437
- }
8438
9974
  this._controlAudioPlayer = controlAudioPlayer;
8439
9975
  if (this._controlAudioPlayer) {
8440
9976
  this._controlAudioPlayer.listener = this;
@@ -8519,6 +10055,8 @@ class SessionManager extends BasicRecorder {
8519
10055
  this.autorecording = true;
8520
10056
  }
8521
10057
  if (this.ac != null) {
10058
+ // Hide loading hint on livelevel display
10059
+ this.audioFetching = false;
8522
10060
  if (!this.ac.opened) {
8523
10061
  if (this._selectedDeviceId) {
8524
10062
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -8535,7 +10073,7 @@ class SessionManager extends BasicRecorder {
8535
10073
  }
8536
10074
  loadScript() {
8537
10075
  this.promptItemCount = 0;
8538
- this.items = new Array();
10076
+ this.items = new SprItemsCache();
8539
10077
  let ln = 0;
8540
10078
  //TODO randomize not supported
8541
10079
  for (let si = 0; si < this._script.sections.length; si++) {
@@ -8549,7 +10087,7 @@ class SessionManager extends BasicRecorder {
8549
10087
  let pi = pis[piSectIdx];
8550
10088
  let promptAsStr = PromptitemUtil.toPlainTextString(pi);
8551
10089
  let it = new Item$1(promptAsStr, section.training, (!pi.type || pi.type === 'recording'));
8552
- this.items.push(it);
10090
+ this.items.addItem(it);
8553
10091
  ln++;
8554
10092
  }
8555
10093
  }
@@ -8590,10 +10128,10 @@ class SessionManager extends BasicRecorder {
8590
10128
  }
8591
10129
  downloadRecording() {
8592
10130
  if (this.displayRecFile) {
8593
- let ab = this.displayRecFile.audioBuffer;
10131
+ let ab = this.displayRecFile.audioDataHolder;
8594
10132
  let ww = new WavWriter();
8595
- if (ab) {
8596
- let wavFile = ww.writeAsync(ab, (wavFile) => {
10133
+ if (ab === null || ab === void 0 ? void 0 : ab.buffer) {
10134
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
8597
10135
  let blob = new Blob([wavFile], { type: 'audio/wav' });
8598
10136
  let rfUrl = URL.createObjectURL(blob);
8599
10137
  let dataDnlLnk = this.renderer.createElement('a');
@@ -8616,7 +10154,10 @@ class SessionManager extends BasicRecorder {
8616
10154
  set displayRecFile(displayRecFile) {
8617
10155
  this._displayRecFile = displayRecFile;
8618
10156
  if (this._displayRecFile) {
8619
- let ab = this._displayRecFile.audioBuffer;
10157
+ if (this.items) {
10158
+ this.items.currentRecordingFile = this._displayRecFile;
10159
+ }
10160
+ let ab = this._displayRecFile.audioDataHolder;
8620
10161
  if (ab) {
8621
10162
  this.displayAudioClip = new AudioClip(ab);
8622
10163
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
@@ -8624,27 +10165,46 @@ class SessionManager extends BasicRecorder {
8624
10165
  else {
8625
10166
  // clear for now ...
8626
10167
  this.displayAudioClip = null;
8627
- this.controlAudioPlayer.audioClip = null;
10168
+ if (this.controlAudioPlayer) {
10169
+ this.controlAudioPlayer.audioClip = null;
10170
+ }
8628
10171
  if (this._controlAudioPlayer && this._session) {
8629
10172
  //... and try to fetch from server
8630
- this.audioFetchSubscription = this.recFileService.fetchAndApplySprRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
8631
- let fab = null;
8632
- if (rf && this._displayRecFile) {
8633
- fab = this._displayRecFile.audioBuffer;
8634
- }
8635
- else {
8636
- this.statusMsg = 'Recording file could not be loaded.';
10173
+ this.audioFetching = true;
10174
+ let rf = this._displayRecFile;
10175
+ this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
10176
+ next: (ab) => {
10177
+ this.audioFetching = false;
10178
+ let fabDh = null;
10179
+ if (ab) {
10180
+ if (rf && this.items) {
10181
+ if (SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10182
+ let aab = ArrayAudioBuffer.fromAudioBuffer(ab);
10183
+ fabDh = new AudioDataHolder(null, aab);
10184
+ }
10185
+ else {
10186
+ fabDh = new AudioDataHolder(ab);
10187
+ }
10188
+ this.items.setSprRecFileAudioData(rf, fabDh);
10189
+ }
10190
+ }
10191
+ else {
10192
+ // Should actually be handled by the error resolver
10193
+ this.statusMsg = 'Recording file could not be loaded.';
10194
+ this.statusAlertType = 'error';
10195
+ }
10196
+ if (fabDh) {
10197
+ // 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
10198
+ this.displayAudioClip = new AudioClip(fabDh);
10199
+ }
10200
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
10201
+ this.showRecording();
10202
+ }, error: err => {
10203
+ console.error("Could not load recording file from server: " + err);
10204
+ this.audioFetching = false;
10205
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
8637
10206
  this.statusAlertType = 'error';
8638
10207
  }
8639
- if (fab) {
8640
- this.displayAudioClip = new AudioClip(fab);
8641
- }
8642
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
8643
- this.showRecording();
8644
- }, err => {
8645
- console.error("Could not load recording file from server: " + err);
8646
- this.statusMsg = 'Recording file could not be loaded: ' + err;
8647
- this.statusAlertType = 'error';
8648
10208
  });
8649
10209
  }
8650
10210
  else {
@@ -8655,34 +10215,14 @@ class SessionManager extends BasicRecorder {
8655
10215
  }
8656
10216
  else {
8657
10217
  this.displayAudioClip = null;
8658
- this.controlAudioPlayer.audioClip = null;
10218
+ if (this.controlAudioPlayer) {
10219
+ this.controlAudioPlayer.audioClip = null;
10220
+ }
8659
10221
  }
8660
10222
  }
8661
10223
  get displayRecFile() {
8662
10224
  return this._displayRecFile;
8663
10225
  }
8664
- showRecording() {
8665
- this.controlAudioPlayer.stop();
8666
- if (this.displayAudioClip) {
8667
- this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8668
- this.displayLevelInfos = levelInfos;
8669
- this.changeDetectorRef.detectChanges();
8670
- });
8671
- this.playStartAction.disabled = false;
8672
- }
8673
- else {
8674
- // TODO
8675
- // Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
8676
- // The level bar display does not clear, it shows the last captured stream
8677
- this.displayLevelInfos = null;
8678
- this.playStartAction.disabled = true;
8679
- // Collapse audio signal display if open
8680
- if (!this.audioSignalCollapsed) {
8681
- this.audioSignalCollapsed = true;
8682
- }
8683
- }
8684
- this.changeDetectorRef.detectChanges();
8685
- }
8686
10226
  isRecordingItem() {
8687
10227
  return (this.promptItem != null && this.promptItem.type !== 'nonrecording');
8688
10228
  }
@@ -8697,17 +10237,20 @@ class SessionManager extends BasicRecorder {
8697
10237
  if (this.audioFetchSubscription) {
8698
10238
  this.audioFetchSubscription.unsubscribe();
8699
10239
  }
10240
+ this.audioFetching = false;
8700
10241
  this.clearPrompt();
8701
10242
  let isNonrecording = (this.promptItem.type === 'nonrecording');
8702
10243
  if (isNonrecording || !this.section.promptphase || this.section.promptphase === 'IDLE') {
8703
10244
  this.applyPrompt();
8704
10245
  }
8705
10246
  if (isNonrecording) {
10247
+ this.displayRecFile = null;
10248
+ this.displayRecFileVersion = 0;
8706
10249
  this.startStopSignalState = 4 /* OFF */;
8707
10250
  }
8708
10251
  else {
8709
10252
  if (this.items) {
8710
- let it = this.items[this.promptIndex];
10253
+ let it = this.items.getItem(this.promptIndex);
8711
10254
  if (!it.recs) {
8712
10255
  it.recs = new Array();
8713
10256
  }
@@ -8724,13 +10267,13 @@ class SessionManager extends BasicRecorder {
8724
10267
  this.displayRecFileVersion = 0;
8725
10268
  }
8726
10269
  }
8727
- if (!temporary) {
8728
- this.showRecording();
8729
- }
8730
10270
  if (!this.readonly) {
8731
10271
  this.startStopSignalState = 0 /* IDLE */;
8732
10272
  }
8733
10273
  }
10274
+ if (!temporary) {
10275
+ this.showRecording();
10276
+ }
8734
10277
  this.updateStartActionDisableState();
8735
10278
  this.updateNavigationActions();
8736
10279
  }
@@ -8768,7 +10311,7 @@ class SessionManager extends BasicRecorder {
8768
10311
  updateNavigationActions() {
8769
10312
  let fwdNxtActionDisabled = true;
8770
10313
  if (this.items) {
8771
- let currRecs = this.items[this._promptIndex].recs;
10314
+ let currRecs = this.items.getItem(this._promptIndex).recs;
8772
10315
  fwdNxtActionDisabled = !(currRecs != null && currRecs.length > 0);
8773
10316
  }
8774
10317
  this.transportActions.fwdNextAction.disabled = this.navigationDisabled || fwdNxtActionDisabled;
@@ -8782,10 +10325,10 @@ class SessionManager extends BasicRecorder {
8782
10325
  newPrIdx = 0;
8783
10326
  }
8784
10327
  if (this.items != null) {
8785
- let itRecs = this.items[newPrIdx].recs;
10328
+ let itRecs = this.items.getItem(newPrIdx).recs;
8786
10329
  while (itRecs != null && (itRecs.length > 0) && newPrIdx < this.promptItemCount) {
8787
10330
  newPrIdx++;
8788
- itRecs = this.items[newPrIdx].recs;
10331
+ itRecs = this.items.getItem(newPrIdx).recs;
8789
10332
  }
8790
10333
  if (!itRecs || itRecs.length == 0) {
8791
10334
  this.promptIndex = newPrIdx;
@@ -8806,6 +10349,23 @@ class SessionManager extends BasicRecorder {
8806
10349
  }
8807
10350
  }
8808
10351
  started() {
10352
+ let ic = this.promptItem.itemcode;
10353
+ let rf = null;
10354
+ if (this._session && ic) {
10355
+ let sessId = this._session.sessionId;
10356
+ let cpIdx = this.promptIndex;
10357
+ if (this.items) {
10358
+ let it = this.items.getItem(cpIdx);
10359
+ if (!it.recs) {
10360
+ it.recs = new Array();
10361
+ }
10362
+ rf = new SprRecordingFile(sessId, ic, it.recs.length, null);
10363
+ //it.recs.push(rf);
10364
+ this.items.addSprRecFile(it, rf);
10365
+ this.items.currentRecordingFile = rf;
10366
+ }
10367
+ }
10368
+ this._recordingFile = rf;
8809
10369
  this.status = 3 /* PRE_RECORDING */;
8810
10370
  super.started();
8811
10371
  this.startStopSignalState = 1 /* PRERECORDING */;
@@ -8932,13 +10492,14 @@ class SessionManager extends BasicRecorder {
8932
10492
  if (rfd.recording && rfd.recording.itemcode) {
8933
10493
  let prIdx = this.promptIndexByItemcode(rfd.recording.itemcode);
8934
10494
  if (this.items != null && prIdx !== null) {
8935
- let it = this.items[prIdx];
10495
+ let it = this.items.getItem(prIdx);
8936
10496
  if (it && this._session) {
8937
- if (!it.recs) {
8938
- it.recs = new Array();
8939
- }
10497
+ // if (!it.recs) {
10498
+ // it.recs = new Array<SprRecordingFile>();
10499
+ // }
8940
10500
  let rf = new SprRecordingFile(this._session.sessionId, rfd.recording.itemcode, rfd.version, null);
8941
- it.recs[rfd.version] = rf;
10501
+ rf.serverPersisted = true;
10502
+ this.items.addSprRecFile(it, rf);
8942
10503
  }
8943
10504
  else {
8944
10505
  //console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
@@ -8952,6 +10513,7 @@ class SessionManager extends BasicRecorder {
8952
10513
  addRecordingFileByPromptIndex(promptIndex, rf) {
8953
10514
  }
8954
10515
  stopped() {
10516
+ var _a;
8955
10517
  this.updateStartActionDisableState();
8956
10518
  this.transportActions.stopAction.disabled = true;
8957
10519
  this.transportActions.nextAction.disabled = true;
@@ -8960,22 +10522,24 @@ class SessionManager extends BasicRecorder {
8960
10522
  this.statusMsg = 'Recorded.';
8961
10523
  this.startStopSignalState = 0 /* IDLE */;
8962
10524
  let ad = null;
10525
+ let ada = null;
10526
+ let adh = null;
10527
+ let frameLen = 0;
8963
10528
  if (this.ac != null) {
8964
- ad = this.ac.audioBuffer();
8965
- }
8966
- let ic = this.promptItem.itemcode;
8967
- let rf = null;
8968
- if (this._session && ic) {
8969
- let sessId = this._session.sessionId;
8970
- let cpIdx = this.promptIndex;
8971
- if (this.items) {
8972
- let it = this.items[cpIdx];
8973
- if (!it.recs) {
8974
- it.recs = new Array();
8975
- }
8976
- rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8977
- it.recs.push(rf);
10529
+ if (this.uploadChunkSizeSeconds || SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10530
+ ada = this.ac.audioBufferArray();
10531
+ frameLen = ada.frameLen;
8978
10532
  }
10533
+ else {
10534
+ ad = this.ac.audioBuffer();
10535
+ frameLen = ad.length;
10536
+ }
10537
+ adh = new AudioDataHolder(ad, ada);
10538
+ }
10539
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
10540
+ let rf = this._recordingFile;
10541
+ if (rf && rf instanceof SprRecordingFile) {
10542
+ (_a = this.items) === null || _a === void 0 ? void 0 : _a.setSprRecFileAudioData(rf, adh);
8979
10543
  if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8980
10544
  // TODO use SpeechRecorderconfig resp. RecfileService
8981
10545
  // convert asynchronously to 16-bit integer PCM
@@ -8983,7 +10547,7 @@ class SessionManager extends BasicRecorder {
8983
10547
  // TODO duplicate conversion for manual download
8984
10548
  //console.log("Build wav writer...");
8985
10549
  this.processingRecording = true;
8986
- if (ad && rf) {
10550
+ if (ad) {
8987
10551
  let ww = new WavWriter();
8988
10552
  //new REST API URL
8989
10553
  let apiEndPoint = '';
@@ -8996,7 +10560,7 @@ class SessionManager extends BasicRecorder {
8996
10560
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
8997
10561
  let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.itemCode;
8998
10562
  ww.writeAsync(ad, (wavFile) => {
8999
- this.postRecording(wavFile, recUrl);
10563
+ this.postRecording(wavFile, recUrl, rf);
9000
10564
  this.processingRecording = false;
9001
10565
  this.updateWakeLock();
9002
10566
  });
@@ -9007,8 +10571,8 @@ class SessionManager extends BasicRecorder {
9007
10571
  let complete = true;
9008
10572
  if (this.items) {
9009
10573
  // search backwards, to gain faster detection of incomplete state
9010
- for (let ri = this.items.length - 1; ri >= 0; ri--) {
9011
- let it = this.items[ri];
10574
+ for (let ri = this.items.length() - 1; ri >= 0; ri--) {
10575
+ let it = this.items.getItem(ri);
9012
10576
  if (it.recording && !it.training && (!it.recs || it.recs.length == 0)) {
9013
10577
  complete = false;
9014
10578
  break;
@@ -9073,13 +10637,13 @@ class SessionManager extends BasicRecorder {
9073
10637
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9074
10638
  let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
9075
10639
  ww.writeAsync(audioBuffer, (wavFile) => {
9076
- this.postRecording(wavFile, recUrl);
10640
+ this.postRecording(wavFile, recUrl, null);
9077
10641
  this.processingRecording = false;
9078
10642
  });
9079
10643
  }
9080
- postRecording(wavFile, recUrl) {
10644
+ postRecording(wavFile, recUrl, rf) {
9081
10645
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
9082
- let ul = new Upload(wavBlob, recUrl);
10646
+ let ul = new Upload(wavBlob, recUrl, rf);
9083
10647
  this.uploader.queueUpload(ul);
9084
10648
  }
9085
10649
  stop() {
@@ -9115,7 +10679,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9115
10679
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9116
10680
  <app-sprprompting [projectName]="projectName"
9117
10681
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9118
- [items]="items"
10682
+ [items]="items?.items"
9119
10683
  [transportActions]="transportActions"
9120
10684
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9121
10685
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9129,10 +10693,10 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9129
10693
 
9130
10694
 
9131
10695
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9132
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
10696
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9133
10697
  <div fxLayout="row">
9134
10698
  <spr-recordingitemcontrols fxFlex="10 0 1"
9135
- [audioLoaded]="displayAudioClip?.buffer!==null"
10699
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9136
10700
  [playStartAction]="controlAudioPlayer?.startAction"
9137
10701
  [playStopAction]="controlAudioPlayer?.stopAction"
9138
10702
  [peakDbLvl]="peakLevelInDb"
@@ -9150,7 +10714,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9150
10714
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9151
10715
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9152
10716
  </div>
9153
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10717
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9154
10718
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9155
10719
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9156
10720
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9158,7 +10722,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9158
10722
  <app-readystateindicator class="ricontrols" fxLayoutAlign="end center" fxHide.xs [ready]="dataSaved && !isActive()"></app-readystateindicator>
9159
10723
  </div>
9160
10724
  </div>
9161
- `, 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"] }] });
10725
+ `, 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"] }] });
9162
10726
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SessionManager, decorators: [{
9163
10727
  type: Component,
9164
10728
  args: [{
@@ -9169,7 +10733,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9169
10733
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9170
10734
  <app-sprprompting [projectName]="projectName"
9171
10735
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9172
- [items]="items"
10736
+ [items]="items?.items"
9173
10737
  [transportActions]="transportActions"
9174
10738
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9175
10739
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9183,10 +10747,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9183
10747
 
9184
10748
 
9185
10749
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9186
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
10750
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9187
10751
  <div fxLayout="row">
9188
10752
  <spr-recordingitemcontrols fxFlex="10 0 1"
9189
- [audioLoaded]="displayAudioClip?.buffer!==null"
10753
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9190
10754
  [playStartAction]="controlAudioPlayer?.startAction"
9191
10755
  [playStopAction]="controlAudioPlayer?.stopAction"
9192
10756
  [peakDbLvl]="peakLevelInDb"
@@ -9204,7 +10768,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9204
10768
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9205
10769
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9206
10770
  </div>
9207
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10771
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9208
10772
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9209
10773
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9210
10774
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9497,7 +11061,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
9497
11061
  this.uploadUpdate(ue);
9498
11062
  };
9499
11063
  window.addEventListener('beforeunload', (e) => {
9500
- console.debug("Before page unload event");
11064
+ //console.debug("Before page unload event");
9501
11065
  if (this.ready()) {
9502
11066
  return;
9503
11067
  }
@@ -9816,14 +11380,15 @@ class AudioDisplayPlayer {
9816
11380
  if (this.aCtx) {
9817
11381
  this.aCtx.decodeAudioData(data, (audioBuffer) => {
9818
11382
  //console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
9819
- this.audioClip = new AudioClip(audioBuffer);
11383
+ let adh = new AudioDataHolder(audioBuffer, null);
11384
+ this.audioClip = new AudioClip(adh);
9820
11385
  });
9821
11386
  }
9822
11387
  }
9823
- set audioData(audioBuffer) {
9824
- this.audioDisplayScrollPane.audioData = audioBuffer;
9825
- if (audioBuffer) {
9826
- let clip = new AudioClip(audioBuffer);
11388
+ set audioData(audioData) {
11389
+ this.audioDisplayScrollPane.audioData = audioData;
11390
+ if (audioData) {
11391
+ let clip = new AudioClip(audioData);
9827
11392
  if (this.ap) {
9828
11393
  this.ap.audioClip = clip;
9829
11394
  this.playStartAction.disabled = false;
@@ -9845,7 +11410,7 @@ class AudioDisplayPlayer {
9845
11410
  let audioData = null;
9846
11411
  let sel = null;
9847
11412
  if (audioClip) {
9848
- audioData = audioClip.buffer;
11413
+ audioData = audioClip.audioDataHolder;
9849
11414
  sel = audioClip.selection;
9850
11415
  if (this._audioClip) {
9851
11416
  this._audioClip.addSelectionObserver((ac) => {
@@ -10116,7 +11681,7 @@ class RecordingFileService {
10116
11681
  // Do not use Promise version, which does not work with Safari 13
10117
11682
  if (resp.body) {
10118
11683
  aCtx.decodeAudioData(resp.body, ab => {
10119
- recordingFile.audioBuffer = ab;
11684
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab));
10120
11685
  if (this.debugDelay > 0) {
10121
11686
  window.setTimeout(() => {
10122
11687
  observer.next(recordingFile);
@@ -10166,7 +11731,7 @@ class RecordingFileService {
10166
11731
  if (resp.body) {
10167
11732
  aCtx.decodeAudioData(resp.body, ab => {
10168
11733
  if (rf) {
10169
- rf.audioBuffer = ab;
11734
+ RecordingFileUtils.setAudioData(rf, new AudioDataHolder(ab));
10170
11735
  }
10171
11736
  else {
10172
11737
  observer.error('Recording file object null');
@@ -10217,7 +11782,8 @@ class RecordingFileService {
10217
11782
  if (resp.body) {
10218
11783
  aCtx.decodeAudioData(resp.body, ab => {
10219
11784
  if (rf) {
10220
- rf.audioBuffer = ab;
11785
+ let adh = new AudioDataHolder(ab);
11786
+ RecordingFileUtils.setAudioData(rf, adh);
10221
11787
  }
10222
11788
  else {
10223
11789
  observer.error('Recording file object null');
@@ -10260,7 +11826,7 @@ class RecordingFileService {
10260
11826
  // append UUID to make request URL unique to avoid localhost server caching
10261
11827
  recUrl = recUrl + '.json?requestUUID=' + UUID.generate();
10262
11828
  }
10263
- console.log("Path request URL: " + recUrl);
11829
+ //console.log("Path request URL: "+recUrl)
10264
11830
  return this.http.patch(recUrl, { editSampleRate: editSampleRate, editStartFrame: editStartFrame, editEndFrame: editEndFrame }, { withCredentials: this.withCredentials });
10265
11831
  }
10266
11832
  }
@@ -10280,6 +11846,7 @@ class RecordingFileMetaComponent {
10280
11846
  constructor() {
10281
11847
  this.sessionId = null;
10282
11848
  this._recordingFile = null;
11849
+ this.stateLoading = false;
10283
11850
  this.itemCode = null;
10284
11851
  this.uuid = null;
10285
11852
  }
@@ -10296,12 +11863,12 @@ class RecordingFileMetaComponent {
10296
11863
  }
10297
11864
  if (this.itemCode) {
10298
11865
  this.uuid = null;
10299
- console.debug("SprRecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11866
+ //console.debug("SprRecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10300
11867
  }
10301
11868
  else {
10302
11869
  this.itemCode = null;
10303
11870
  this.uuid = (_b = this._recordingFile) === null || _b === void 0 ? void 0 : _b.uuid;
10304
- console.debug("RecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11871
+ //console.debug("RecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10305
11872
  }
10306
11873
  }
10307
11874
  else {
@@ -10318,10 +11885,11 @@ class RecordingFileMetaComponent {
10318
11885
  }
10319
11886
  }
10320
11887
  RecordingFileMetaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10321
- 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: `
11888
+ 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: `
10322
11889
  <mat-card>
10323
11890
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10324
11891
  <mat-card-content>
11892
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10325
11893
  <table>
10326
11894
  <tr *ngIf="itemCode">
10327
11895
  <td>Itemcode:</td>
@@ -10351,7 +11919,7 @@ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10351
11919
  </table>
10352
11920
  </mat-card-content>
10353
11921
  </mat-card>
10354
- `, 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"] }] });
11922
+ `, 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"] }] });
10355
11923
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, decorators: [{
10356
11924
  type: Component,
10357
11925
  args: [{
@@ -10360,6 +11928,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10360
11928
  <mat-card>
10361
11929
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10362
11930
  <mat-card-content>
11931
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10363
11932
  <table>
10364
11933
  <tr *ngIf="itemCode">
10365
11934
  <td>Itemcode:</td>
@@ -10394,6 +11963,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10394
11963
  }]
10395
11964
  }], propDecorators: { sessionId: [{
10396
11965
  type: Input
11966
+ }], stateLoading: [{
11967
+ type: Input
10397
11968
  }], recordingFile: [{
10398
11969
  type: Input
10399
11970
  }] } });
@@ -10536,6 +12107,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10536
12107
  this.recordingFileVersion = null;
10537
12108
  this.routedByQueryParam = false;
10538
12109
  this.posInList = null;
12110
+ this.audioFetching = false;
10539
12111
  this.naviInfoLoading = false;
10540
12112
  this.parentE = this.eRef.nativeElement;
10541
12113
  this.firstAction = new Action('First');
@@ -10707,18 +12279,20 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10707
12279
  this.updateActions();
10708
12280
  let audioContext = AudioContextProvider.audioContextInstance();
10709
12281
  if (audioContext) {
12282
+ this.audioFetching = true;
10710
12283
  this.recordingFileService.fetchSprRecordingFile(audioContext, rfId).subscribe(value => {
12284
+ this.audioFetching = false;
10711
12285
  this.status = 'Audio file loaded.';
10712
12286
  let clip = null;
10713
12287
  this.recordingFile = value;
10714
12288
  if (this.recordingFile) {
10715
- let ab = this.recordingFile.audioBuffer;
12289
+ let ab = this.recordingFile.audioDataHolder;
10716
12290
  if (ab) {
10717
12291
  clip = new AudioClip(ab);
10718
12292
  let esffsr = null;
10719
12293
  let eeffsr = null;
10720
12294
  let esr = null;
10721
- if (clip.buffer != null) {
12295
+ if (clip.audioDataHolder != null) {
10722
12296
  esr = ab.sampleRate;
10723
12297
  if (esr != null) {
10724
12298
  esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
@@ -10730,8 +12304,8 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10730
12304
  sel = new Selection(ab.sampleRate, esffsr, eeffsr);
10731
12305
  }
10732
12306
  else {
10733
- let ch0 = ab.getChannelData(0);
10734
- let frameLength = ch0.length;
12307
+ //let ch0 = ab.getChannelData(0);
12308
+ let frameLength = ab.frameLen;
10735
12309
  sel = new Selection(esr, esffsr, frameLength);
10736
12310
  }
10737
12311
  }
@@ -10745,6 +12319,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10745
12319
  this.audioClip = clip;
10746
12320
  this.loadedRecfile();
10747
12321
  }, error1 => {
12322
+ this.audioFetching = false;
10748
12323
  this.status = 'Error loading audio file!';
10749
12324
  });
10750
12325
  }
@@ -10854,7 +12429,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10854
12429
  for (let avRfV of avRf) {
10855
12430
  os += avRfV.version + '/';
10856
12431
  }
10857
- console.debug(os);
12432
+ //console.debug(os);
10858
12433
  }
10859
12434
  }
10860
12435
  this.updatePos();
@@ -10870,7 +12445,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10870
12445
 
10871
12446
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10872
12447
  <div class="ctrlview">
10873
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12448
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10874
12449
 
10875
12450
  <audio-display-control [audioClip]="audioClip"
10876
12451
  [playStartAction]="playStartAction"
@@ -10883,7 +12458,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10883
12458
  [zoomFitToPanelAction]="zoomFitToPanelAction"></audio-display-control>
10884
12459
  <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>
10885
12460
  </div>
10886
- `, 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"] }] });
12461
+ `, 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"] }] });
10887
12462
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileViewComponent, decorators: [{
10888
12463
  type: Component,
10889
12464
  args: [{
@@ -10892,7 +12467,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10892
12467
 
10893
12468
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10894
12469
  <div class="ctrlview">
10895
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12470
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10896
12471
 
10897
12472
  <audio-display-control [audioClip]="audioClip"
10898
12473
  [playStartAction]="playStartAction"
@@ -10986,7 +12561,7 @@ class RecordingFileUI extends RecordingFileViewComponent {
10986
12561
  applySelection() {
10987
12562
  var _a;
10988
12563
  if (this.audioClip) {
10989
- let ab = this.audioClip.buffer;
12564
+ let ab = this.audioClip.audioDataHolder;
10990
12565
  let s = this.audioClip.selection;
10991
12566
  if (ab && ((_a = this.recordingFile) === null || _a === void 0 ? void 0 : _a.recordingFileId)) {
10992
12567
  let sr = null;
@@ -11024,7 +12599,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11024
12599
 
11025
12600
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11026
12601
  <div class="ctrlview">
11027
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12602
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11028
12603
  <audio-display-control [audioClip]="audioClip"
11029
12604
  [playStartAction]="playStartAction"
11030
12605
  [playSelectionAction]="playSelectionAction"
@@ -11038,7 +12613,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11038
12613
  </div>
11039
12614
 
11040
12615
  <button mat-raised-button color="accent" (click)="applySelection()" [disabled]="editSaved">{{this.applyButtonText()}}</button>
11041
- `, 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"] }] });
12616
+ `, 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"] }] });
11042
12617
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileUI, decorators: [{
11043
12618
  type: Component,
11044
12619
  args: [{
@@ -11049,7 +12624,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
11049
12624
 
11050
12625
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11051
12626
  <div class="ctrlview">
11052
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12627
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11053
12628
  <audio-display-control [audioClip]="audioClip"
11054
12629
  [playStartAction]="playStartAction"
11055
12630
  [playSelectionAction]="playSelectionAction"
@@ -11106,7 +12681,8 @@ class MediaUtils {
11106
12681
 
11107
12682
  class RecordingList {
11108
12683
  constructor() {
11109
- this.recordingList = new Array();
12684
+ //private recordingList:Array<RecordingFile>=new Array<RecordingFile>();
12685
+ this.recordingList = new RecFilesCache();
11110
12686
  //cols=['index','length','samples','samplerate','action'];
11111
12687
  this.cols = ['index', 'startedDate', 'length', 'action'];
11112
12688
  this.selectDisabled = false;
@@ -11118,7 +12694,7 @@ class RecordingList {
11118
12694
  this.buildDataSource();
11119
12695
  }
11120
12696
  buildDataSource() {
11121
- this.recordingList.sort((a, b) => {
12697
+ this.recordingList.recFiles.sort((a, b) => {
11122
12698
  let cmp = 0;
11123
12699
  let aD = null;
11124
12700
  let bD = null;
@@ -11139,25 +12715,36 @@ class RecordingList {
11139
12715
  }
11140
12716
  return cmp;
11141
12717
  });
11142
- this.recordingListDataSource.data = this.recordingList;
12718
+ this.recordingListDataSource.data = this.recordingList.recFiles;
11143
12719
  }
11144
- push(rf) {
11145
- this.recordingList.push(rf);
12720
+ addRecFile(rf) {
12721
+ this.recordingList.addRecFile(rf);
11146
12722
  this.buildDataSource();
11147
12723
  }
12724
+ setRecFileAudioData(recFile, adh) {
12725
+ this.recordingList.setRecFileAudioData(recFile, adh);
12726
+ }
11148
12727
  selectRecordingFile(rf) {
12728
+ this.recordingList.currentRecordingFile = rf;
11149
12729
  this.selectedRecordingFileChanged.emit(rf);
11150
12730
  }
11151
12731
  selectTop() {
11152
- if (this.recordingList.length > 0) {
11153
- this.selectRecordingFile(this.recordingList[0]);
12732
+ if (this.recordingList.recFiles.length > 0) {
12733
+ this.selectRecordingFile(this.recordingList.recFiles[0]);
11154
12734
  }
11155
12735
  }
11156
12736
  lengthTimeFormatted(rf) {
11157
12737
  var _a;
11158
12738
  let str = '--:--:--';
11159
- if (rf.frames && rf.audioBuffer) {
11160
- str = MediaUtils.toMediaTime(rf.frames / ((_a = rf.audioBuffer) === null || _a === void 0 ? void 0 : _a.sampleRate));
12739
+ let tl = null;
12740
+ if (rf.timeLength) {
12741
+ tl = rf.timeLength;
12742
+ }
12743
+ else if (rf.frames && rf.audioDataHolder) {
12744
+ tl = rf.frames / ((_a = rf.audioDataHolder) === null || _a === void 0 ? void 0 : _a.sampleRate);
12745
+ }
12746
+ if (tl) {
12747
+ str = MediaUtils.toMediaTime(tl);
11161
12748
  }
11162
12749
  return str;
11163
12750
  }
@@ -11243,8 +12830,11 @@ class RecorderCombiPane {
11243
12830
  }
11244
12831
  ngAfterViewInit() {
11245
12832
  }
11246
- push(rf) {
11247
- this.recordingListComp.push(rf);
12833
+ addRecFile(rf) {
12834
+ this.recordingListComp.addRecFile(rf);
12835
+ }
12836
+ setRecFileAudioData(recFile, adh) {
12837
+ this.recordingListComp.setRecFileAudioData(recFile, adh);
11248
12838
  }
11249
12839
  selectRecordingFile(rf) {
11250
12840
  this.selectedRecordingFileChanged.emit(rf);
@@ -11317,15 +12907,11 @@ class Item {
11317
12907
  }
11318
12908
  class AudioRecorder extends BasicRecorder {
11319
12909
  constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11320
- super(dialog, sessionService, uploader, config);
11321
- this.changeDetectorRef = changeDetectorRef;
12910
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
11322
12911
  this.renderer = renderer;
11323
12912
  this.route = route;
11324
- this.dialog = dialog;
11325
- this.sessionService = sessionService;
11326
12913
  this.recFileService = recFileService;
11327
12914
  this.uploader = uploader;
11328
- this.config = config;
11329
12915
  this._project = null;
11330
12916
  this.projectName = null;
11331
12917
  this.enableUploadRecordings = true;
@@ -11508,13 +13094,15 @@ class AudioRecorder extends BasicRecorder {
11508
13094
  if (rfs) {
11509
13095
  if (rfs instanceof Array) {
11510
13096
  rfs.forEach((rf) => {
13097
+ // the list comes from the server, asssuem all recording files as server persisted
13098
+ rf.serverPersisted = true;
11511
13099
  if (rf.startedDate) {
11512
13100
  rf._startedAsDateObj = new Date(rf.startedDate);
11513
13101
  }
11514
13102
  if (rf.date) {
11515
13103
  rf._dateAsDateObj = new Date(rf.date);
11516
13104
  }
11517
- this.recorderCombiPane.push(rf);
13105
+ this.recorderCombiPane.addRecFile(rf);
11518
13106
  });
11519
13107
  }
11520
13108
  else {
@@ -11568,6 +13156,7 @@ class AudioRecorder extends BasicRecorder {
11568
13156
  return this._project;
11569
13157
  }
11570
13158
  selectRecordingFile(rf) {
13159
+ this.audioFetching = false;
11571
13160
  this.displayRecFile = rf;
11572
13161
  }
11573
13162
  uploadUpdate(ue) {
@@ -11589,9 +13178,6 @@ class AudioRecorder extends BasicRecorder {
11589
13178
  this.changeDetectorRef.detectChanges();
11590
13179
  }
11591
13180
  set controlAudioPlayer(controlAudioPlayer) {
11592
- if (this._controlAudioPlayer) {
11593
- //this._controlAudioPlayer.listener=null;
11594
- }
11595
13181
  this._controlAudioPlayer = controlAudioPlayer;
11596
13182
  if (this._controlAudioPlayer) {
11597
13183
  this._controlAudioPlayer.listener = this;
@@ -11671,6 +13257,7 @@ class AudioRecorder extends BasicRecorder {
11671
13257
  this.displayAudioClip = null;
11672
13258
  this.showRecording();
11673
13259
  if (this.ac) {
13260
+ this.audioFetching = false;
11674
13261
  if (!this.ac.opened) {
11675
13262
  if (this._selectedDeviceId) {
11676
13263
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -11687,10 +13274,10 @@ class AudioRecorder extends BasicRecorder {
11687
13274
  }
11688
13275
  downloadRecording() {
11689
13276
  if (this.displayRecFile) {
11690
- let ab = this.displayRecFile.audioBuffer;
13277
+ let ab = this.displayRecFile.audioDataHolder;
11691
13278
  let ww = new WavWriter();
11692
- if (ab) {
11693
- let wavFile = ww.writeAsync(ab, (wavFile) => {
13279
+ if (ab === null || ab === void 0 ? void 0 : ab.buffer) {
13280
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
11694
13281
  let blob = new Blob([wavFile], { type: 'audio/wav' });
11695
13282
  let rfUrl = URL.createObjectURL(blob);
11696
13283
  let dataDnlLnk = this.renderer.createElement('a');
@@ -11713,35 +13300,50 @@ class AudioRecorder extends BasicRecorder {
11713
13300
  set displayRecFile(displayRecFile) {
11714
13301
  this._displayRecFile = displayRecFile;
11715
13302
  if (this._displayRecFile) {
11716
- let ab = this._displayRecFile.audioBuffer;
11717
- if (ab) {
11718
- this.displayAudioClip = new AudioClip(ab);
13303
+ let adh = this._displayRecFile.audioDataHolder;
13304
+ if (adh) {
13305
+ this.displayAudioClip = new AudioClip(adh);
13306
+ console.debug(" set recording file: display audio clip set");
11719
13307
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
11720
13308
  }
11721
13309
  else {
11722
13310
  // clear for now ...
11723
13311
  this.displayAudioClip = null;
13312
+ console.debug("set recording file: display audio clip null");
11724
13313
  this.controlAudioPlayer.audioClip = null;
11725
13314
  if (this._controlAudioPlayer && this._session) {
11726
13315
  //... and try to fetch from server
11727
- this.audioFetchSubscription = this.recFileService.fetchAndApplyRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
11728
- let fab = null;
11729
- if (rf && this._displayRecFile) {
11730
- fab = this._displayRecFile.audioBuffer;
11731
- }
11732
- else {
11733
- this.statusMsg = 'Recording file could not be loaded.';
13316
+ this.audioFetching = true;
13317
+ let rf = this._displayRecFile;
13318
+ let clip = this.displayAudioClip;
13319
+ this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
13320
+ next: ab => {
13321
+ this.audioFetching = false;
13322
+ let fabDh = null;
13323
+ if (ab) {
13324
+ if (rf) {
13325
+ fabDh = new AudioDataHolder(ab);
13326
+ this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
13327
+ }
13328
+ }
13329
+ else {
13330
+ console.error('Recording file could not be loaded.');
13331
+ this.statusMsg = 'Recording file could not be loaded.';
13332
+ this.statusAlertType = 'error';
13333
+ }
13334
+ if (fabDh) {
13335
+ // 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
13336
+ this.displayAudioClip = new AudioClip(fabDh);
13337
+ //console.debug("set recording file: display audio clip from fetched audio buffer");
13338
+ }
13339
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
13340
+ this.showRecording();
13341
+ }, error: err => {
13342
+ console.error("Could not load recording file from server: " + err);
13343
+ this.audioFetching = false;
13344
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
11734
13345
  this.statusAlertType = 'error';
11735
13346
  }
11736
- if (fab) {
11737
- this.displayAudioClip = new AudioClip(fab);
11738
- }
11739
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
11740
- this.showRecording();
11741
- }, err => {
11742
- console.error("Could not load recording file from server: " + err);
11743
- this.statusMsg = 'Recording file could not be loaded: ' + err;
11744
- this.statusAlertType = 'error';
11745
13347
  });
11746
13348
  }
11747
13349
  else {
@@ -11751,6 +13353,7 @@ class AudioRecorder extends BasicRecorder {
11751
13353
  }
11752
13354
  }
11753
13355
  else {
13356
+ console.debug("recording file null");
11754
13357
  this.displayAudioClip = null;
11755
13358
  this.controlAudioPlayer.audioClip = null;
11756
13359
  }
@@ -11759,28 +13362,6 @@ class AudioRecorder extends BasicRecorder {
11759
13362
  get displayRecFile() {
11760
13363
  return this._displayRecFile;
11761
13364
  }
11762
- showRecording() {
11763
- this.controlAudioPlayer.stop();
11764
- if (this.displayAudioClip) {
11765
- this.levelMeasure.calcBufferLevelInfos(this.displayAudioClip.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
11766
- this.displayLevelInfos = levelInfos;
11767
- this.changeDetectorRef.detectChanges();
11768
- });
11769
- this.playStartAction.disabled = false;
11770
- }
11771
- else {
11772
- // TODO
11773
- // Setting to null does not trigger a change if it was null before (happens after nextitem() in AUTOPROGRESS mode)
11774
- // The level bar display does not clear, it shows the last captured stream
11775
- this.displayLevelInfos = null;
11776
- this.playStartAction.disabled = true;
11777
- // Collapse audio signal display if open
11778
- if (!this.audioSignalCollapsed) {
11779
- this.audioSignalCollapsed = true;
11780
- }
11781
- }
11782
- this.changeDetectorRef.detectChanges();
11783
- }
11784
13365
  updateStartActionDisableState() {
11785
13366
  this.transportActions.startAction.disabled = !(this.ac);
11786
13367
  }
@@ -11824,6 +13405,19 @@ class AudioRecorder extends BasicRecorder {
11824
13405
  super.started();
11825
13406
  this.statusAlertType = 'info';
11826
13407
  this.statusMsg = 'Recording...';
13408
+ if (!this.rfUuid) {
13409
+ this.rfUuid = UUID.generate();
13410
+ }
13411
+ let sessId = 0;
13412
+ if (this._session) {
13413
+ sessId = this._session.sessionId;
13414
+ }
13415
+ let rf = new RecordingFile(this.rfUuid, sessId, null);
13416
+ rf._startedAsDateObj = this.startedDate;
13417
+ if (rf._startedAsDateObj) {
13418
+ rf.startedDate = rf._startedAsDateObj.toString();
13419
+ }
13420
+ this._recordingFile = rf;
11827
13421
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
11828
13422
  this.maxRecTimerId = window.setTimeout(() => {
11829
13423
  this.stopRecordingMaxRec();
@@ -11860,68 +13454,75 @@ class AudioRecorder extends BasicRecorder {
11860
13454
  this.transportActions.pauseAction.disabled = true;
11861
13455
  this.statusAlertType = 'info';
11862
13456
  this.statusMsg = 'Recorded.';
11863
- let ad;
13457
+ let ad = null;
13458
+ let ada = null;
13459
+ let adh = null;
13460
+ let frameLen = 0;
11864
13461
  if (this.ac) {
11865
- ad = this.ac.audioBuffer();
13462
+ if (this.uploadChunkSizeSeconds || AudioRecorder.FORCE_ARRRAY_AUDIO_BUFFER) {
13463
+ ada = this.ac.audioBufferArray();
13464
+ frameLen = ada.frameLen;
13465
+ }
13466
+ else {
13467
+ ad = this.ac.audioBuffer();
13468
+ frameLen = ad.length;
13469
+ }
13470
+ adh = new AudioDataHolder(ad, ada);
11866
13471
  let sessId = 0;
11867
13472
  if (this._session) {
11868
13473
  sessId = this._session.sessionId;
11869
13474
  }
11870
- if (!this.rfUuid) {
11871
- this.rfUuid = UUID.generate();
11872
- }
11873
- let rf = new RecordingFile(this.rfUuid, sessId, ad);
11874
- rf._startedAsDateObj = this.startedDate;
11875
- if (rf._startedAsDateObj) {
11876
- rf.startedDate = rf._startedAsDateObj.toString();
11877
- }
11878
- rf.frames = ad.length;
11879
- this.displayRecFile = rf;
11880
- this.recorderCombiPane.push(rf);
11881
- // Upload if upload enabled and not in chunked upload mode
11882
- if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
11883
- let apiEndPoint = '';
11884
- if (this.config && this.config.apiEndPoint) {
11885
- apiEndPoint = this.config.apiEndPoint;
11886
- }
11887
- if (apiEndPoint !== '') {
11888
- apiEndPoint = apiEndPoint + '/';
13475
+ if (this._recordingFile) {
13476
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
13477
+ let rf = this._recordingFile;
13478
+ RecordingFileUtils.setAudioData(rf, adh);
13479
+ this.recorderCombiPane.addRecFile(rf);
13480
+ // Upload if upload enabled and not in chunked upload mode
13481
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds && rf != null && ad != null) {
13482
+ let apiEndPoint = '';
13483
+ if (this.config && this.config.apiEndPoint) {
13484
+ apiEndPoint = this.config.apiEndPoint;
13485
+ }
13486
+ if (apiEndPoint !== '') {
13487
+ apiEndPoint = apiEndPoint + '/';
13488
+ }
13489
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
13490
+ let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
13491
+ // convert asynchronously to 16-bit integer PCM
13492
+ // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
13493
+ // TODO duplicate conversion for manual download
13494
+ this.processingRecording = true;
13495
+ let ww = new WavWriter();
13496
+ ww.writeAsync(ad, (wavFile) => {
13497
+ this.postRecordingMultipart(wavFile, recUrl, rf);
13498
+ this.processingRecording = false;
13499
+ this.updateWakeLock();
13500
+ this.changeDetectorRef.detectChanges();
13501
+ });
11889
13502
  }
11890
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11891
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11892
- // convert asynchronously to 16-bit integer PCM
11893
- // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11894
- // TODO duplicate conversion for manual download
11895
- this.processingRecording = true;
11896
- let ww = new WavWriter();
11897
- ww.writeAsync(ad, (wavFile) => {
11898
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11899
- this.processingRecording = false;
11900
- this.updateWakeLock();
11901
- this.changeDetectorRef.detectChanges();
11902
- });
11903
13503
  }
11904
13504
  }
13505
+ this.displayRecFile = this._recordingFile;
11905
13506
  this.status = 1 /* IDLE */;
11906
13507
  this.navigationDisabled = false;
11907
13508
  this.updateNavigationActions();
11908
13509
  this.updateWakeLock();
11909
13510
  this.changeDetectorRef.detectChanges();
11910
13511
  }
11911
- postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
13512
+ postRecordingMultipart(wavFile, recUrl, rf) {
11912
13513
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11913
13514
  let fd = new FormData();
11914
- if (uuid) {
11915
- fd.set('uuid', uuid);
13515
+ if (rf.uuid) {
13516
+ fd.set('uuid', rf.uuid);
11916
13517
  }
11917
- if (sessionId !== null) {
11918
- fd.set('sessionId', sessionId.toString());
13518
+ if (rf.session !== null) {
13519
+ fd.set('sessionId', rf.session.toString());
11919
13520
  }
11920
- if (startedDate) {
11921
- fd.set('startedDate', startedDate.toJSON());
13521
+ if (rf._startedAsDateObj) {
13522
+ fd.set('startedDate', rf._startedAsDateObj.toJSON());
11922
13523
  }
11923
13524
  fd.set('audio', wavBlob);
11924
- let ul = new Upload(fd, recUrl);
13525
+ let ul = new Upload(fd, recUrl, rf);
11925
13526
  this.uploader.queueUpload(ul);
11926
13527
  }
11927
13528
  postChunkAudioBuffer(audioBuffer, chunkIdx) {
@@ -11938,8 +13539,9 @@ class AudioRecorder extends BasicRecorder {
11938
13539
  }
11939
13540
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11940
13541
  let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
13542
+ let rf = this._recordingFile;
11941
13543
  ww.writeAsync(audioBuffer, (wavFile) => {
11942
- this.postRecording(wavFile, recUrl);
13544
+ this.postRecording(wavFile, recUrl, rf);
11943
13545
  this.processingRecording = false;
11944
13546
  });
11945
13547
  }
@@ -11988,11 +13590,11 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
11988
13590
 
11989
13591
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
11990
13592
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
11991
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
11992
- [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
13593
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
13594
+ [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
11993
13595
  <div fxLayout="row">
11994
13596
  <spr-recordingitemcontrols fxFlex="10 0 1"
11995
- [audioLoaded]="displayAudioClip?.buffer!==null"
13597
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
11996
13598
  [playStartAction]="controlAudioPlayer?.startAction"
11997
13599
  [playStopAction]="controlAudioPlayer?.stopAction"
11998
13600
  [peakDbLvl]="peakLevelInDb"
@@ -12031,7 +13633,7 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
12031
13633
  [ready]="dataSaved && !isActive()"></app-readystateindicator>
12032
13634
  </div>
12033
13635
  </div>
12034
- `, 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"] }] });
13636
+ `, 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"] }] });
12035
13637
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioRecorder, decorators: [{
12036
13638
  type: Component,
12037
13639
  args: [{
@@ -12054,11 +13656,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
12054
13656
 
12055
13657
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
12056
13658
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
12057
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
12058
- [displayLevelInfos]="displayLevelInfos"></audio-levelbar>
13659
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
13660
+ [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
12059
13661
  <div fxLayout="row">
12060
13662
  <spr-recordingitemcontrols fxFlex="10 0 1"
12061
- [audioLoaded]="displayAudioClip?.buffer!==null"
13663
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
12062
13664
  [playStartAction]="controlAudioPlayer?.startAction"
12063
13665
  [playStopAction]="controlAudioPlayer?.stopAction"
12064
13666
  [peakDbLvl]="peakLevelInDb"
@@ -12338,20 +13940,20 @@ class SpeechrecorderngModule {
12338
13940
  }
12339
13941
  SpeechrecorderngModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
12340
13942
  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,
12341
- 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] });
12342
- 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]] });
13943
+ 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] });
13944
+ 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]] });
12343
13945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, decorators: [{
12344
13946
  type: NgModule,
12345
13947
  args: [{
12346
13948
  declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
12347
13949
  ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder],
12348
13950
  exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder],
12349
- imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule],
13951
+ imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule],
12350
13952
  providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader]
12351
13953
  }]
12352
13954
  }] });
12353
13955
 
12354
- const VERSION = '2.25.2';
13956
+ const VERSION = '3.0.1';
12355
13957
 
12356
13958
  /*
12357
13959
  * Public API Surface of speechrecorderng