speechrecorderng 2.25.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/esm2020/lib/audio/array_audio_buffer.mjs +101 -0
  2. package/esm2020/lib/audio/array_audio_buffer_input_stream.mjs +86 -0
  3. package/esm2020/lib/audio/audio_data_holder.mjs +109 -0
  4. package/esm2020/lib/audio/audio_display.mjs +5 -5
  5. package/esm2020/lib/audio/audio_player.mjs +9 -7
  6. package/esm2020/lib/audio/capture/capture.mjs +6 -1
  7. package/esm2020/lib/audio/dsp/level_measure.mjs +97 -55
  8. package/esm2020/lib/audio/io/stream.mjs +3 -3
  9. package/esm2020/lib/audio/persistor.mjs +5 -6
  10. package/esm2020/lib/audio/playback/array_audio_buffer_source_node.mjs +265 -0
  11. package/esm2020/lib/audio/playback/player.mjs +184 -50
  12. package/esm2020/lib/audio/ui/audio_canvas_layer_comp.mjs +25 -13
  13. package/esm2020/lib/audio/ui/audio_display_scroll_pane.mjs +2 -2
  14. package/esm2020/lib/audio/ui/audiosignal.mjs +206 -77
  15. package/esm2020/lib/audio/ui/container.mjs +14 -14
  16. package/esm2020/lib/audio/ui/livelevel.mjs +15 -2
  17. package/esm2020/lib/audio/ui/sonagram.mjs +343 -160
  18. package/esm2020/lib/io/stream.mjs +77 -1
  19. package/esm2020/lib/net/uploader.mjs +15 -3
  20. package/esm2020/lib/speechrecorder/recording.mjs +52 -7
  21. package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +99 -7
  22. package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +117 -101
  23. package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +34 -6
  24. package/esm2020/lib/speechrecorder/session/progress.mjs +2 -2
  25. package/esm2020/lib/speechrecorder/session/prompting.mjs +2 -2
  26. package/esm2020/lib/speechrecorder/session/recorder_combi_pane.mjs +6 -3
  27. package/esm2020/lib/speechrecorder/session/recording_file_cache.mjs +182 -0
  28. package/esm2020/lib/speechrecorder/session/recording_list.mjs +23 -10
  29. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +12 -6
  30. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-u-i.component.mjs +5 -5
  31. package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +13 -9
  32. package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +8 -5
  33. package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +118 -95
  34. package/esm2020/lib/speechrecorderng.component.mjs +2 -2
  35. package/esm2020/lib/speechrecorderng.module.mjs +5 -4
  36. package/esm2020/lib/spr.module.version.mjs +2 -2
  37. package/esm2020/lib/ui/recordingitem_display.mjs +2 -2
  38. package/fesm2015/speechrecorderng.mjs +2321 -743
  39. package/fesm2015/speechrecorderng.mjs.map +1 -1
  40. package/fesm2020/speechrecorderng.mjs +2321 -753
  41. package/fesm2020/speechrecorderng.mjs.map +1 -1
  42. package/lib/audio/array_audio_buffer.d.ts +15 -0
  43. package/lib/audio/array_audio_buffer_input_stream.d.ts +14 -0
  44. package/lib/audio/audio_data_holder.d.ts +29 -0
  45. package/lib/audio/audio_display.d.ts +2 -1
  46. package/lib/audio/audio_player.d.ts +2 -1
  47. package/lib/audio/capture/capture.d.ts +2 -0
  48. package/lib/audio/dsp/level_measure.d.ts +2 -1
  49. package/lib/audio/persistor.d.ts +4 -3
  50. package/lib/audio/playback/array_audio_buffer_source_node.d.ts +24 -0
  51. package/lib/audio/playback/player.d.ts +6 -0
  52. package/lib/audio/ui/audio_canvas_layer_comp.d.ts +4 -1
  53. package/lib/audio/ui/audio_display_scroll_pane.d.ts +2 -1
  54. package/lib/audio/ui/audiosignal.d.ts +3 -2
  55. package/lib/audio/ui/container.d.ts +3 -2
  56. package/lib/audio/ui/livelevel.d.ts +3 -1
  57. package/lib/audio/ui/sonagram.d.ts +3 -2
  58. package/lib/io/stream.d.ts +17 -0
  59. package/lib/net/uploader.d.ts +6 -1
  60. package/lib/speechrecorder/recording.d.ts +15 -3
  61. package/lib/speechrecorder/recordings/recordings.service.d.ts +2 -0
  62. package/lib/speechrecorder/session/audiorecorder.d.ts +2 -7
  63. package/lib/speechrecorder/session/basicrecorder.d.ts +9 -3
  64. package/lib/speechrecorder/session/progress.d.ts +1 -1
  65. package/lib/speechrecorder/session/prompting.d.ts +1 -1
  66. package/lib/speechrecorder/session/recorder_combi_pane.d.ts +3 -1
  67. package/lib/speechrecorder/session/recording_file_cache.d.ts +30 -0
  68. package/lib/speechrecorder/session/recording_list.d.ts +3 -1
  69. package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +2 -1
  70. package/lib/speechrecorder/session/recordingfile/recording-file-view.component.d.ts +1 -0
  71. package/lib/speechrecorder/session/sessionmanager.d.ts +5 -11
  72. package/lib/speechrecorderng.module.d.ts +2 -1
  73. package/lib/spr.module.version.d.ts +1 -1
  74. package/package.json +1 -1
@@ -36,6 +36,7 @@ import * as i2$2 from '@angular/material/table';
36
36
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
37
37
  import { MatSelectModule } from '@angular/material/select';
38
38
  import { MatInputModule } from '@angular/material/input';
39
+ import { MatMenuModule } from '@angular/material/menu';
39
40
 
40
41
  class ActionEvent {
41
42
  constructor(_value = null) {
@@ -109,6 +110,514 @@ class Action {
109
110
  }
110
111
  }
111
112
 
113
+ class ArrayAudioBufferInputStream {
114
+ constructor(arrayAudioBuffer) {
115
+ this.arrayAudioBuffer = arrayAudioBuffer;
116
+ this._framePos = 0;
117
+ this.chunkFramePos = 0;
118
+ this.chunkIdx = 0;
119
+ this.eod = false;
120
+ //console.debug("Array audio input stream array audio buffer frames: "+arrayAudioBuffer.frameLen);
121
+ }
122
+ close() {
123
+ }
124
+ skipFrames(n) {
125
+ let toSkip = n;
126
+ if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
127
+ this.eod = true;
128
+ if (toSkip > 0) {
129
+ throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
130
+ }
131
+ }
132
+ else {
133
+ let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
134
+ let chunkBufsLen = chunkBuf0.length;
135
+ let currBufSkippable = chunkBufsLen - this.chunkFramePos;
136
+ if (n >= currBufSkippable) {
137
+ this._framePos += currBufSkippable;
138
+ toSkip -= currBufSkippable;
139
+ this.chunkIdx++;
140
+ this.chunkFramePos = 0;
141
+ if (this.chunkIdx < this.arrayAudioBuffer.chunkCount) {
142
+ this.skipFrames(toSkip);
143
+ }
144
+ else {
145
+ if (toSkip > 0) {
146
+ throw Error('Skip out of bounds: Cannot skip ' + toSkip + ' frames.');
147
+ }
148
+ }
149
+ }
150
+ else {
151
+ toSkip -= n;
152
+ this.chunkFramePos += n;
153
+ }
154
+ }
155
+ }
156
+ read(buffers) {
157
+ let read = 0;
158
+ let toRead = buffers[0].length;
159
+ while (read < toRead && !this.eod) {
160
+ //console.debug("Chunk "+this.chunkIdx+" of "+this.arrayAudioBuffer.chunkCount+" chunks.")
161
+ if (this.chunkIdx >= this.arrayAudioBuffer.chunkCount) {
162
+ this.eod = true;
163
+ //console.debug("Array audio input stream end of data read frames: "+this.framePos);
164
+ }
165
+ else {
166
+ let chunkBuf0 = this.arrayAudioBuffer.data[0][this.chunkIdx];
167
+ let chunkBufsLen = chunkBuf0.length;
168
+ let chunkBufAvail = chunkBufsLen - this.chunkFramePos;
169
+ let r = chunkBufAvail;
170
+ if (r > toRead - read) {
171
+ r = toRead - read;
172
+ }
173
+ for (let ch = 0; ch < buffers.length; ch++) {
174
+ let chChunkBuf = this.arrayAudioBuffer.data[ch][this.chunkIdx];
175
+ for (let bi = 0; bi < r; bi++) {
176
+ buffers[ch][read + bi] = chChunkBuf[this.chunkFramePos + bi];
177
+ }
178
+ }
179
+ read += r;
180
+ this.chunkFramePos += r;
181
+ if (this.chunkFramePos >= chunkBufsLen) {
182
+ this.chunkIdx++;
183
+ this.chunkFramePos = 0;
184
+ }
185
+ }
186
+ }
187
+ this._framePos += read;
188
+ //console.debug("Read: "+read+", frame pos: "+this.framePos)
189
+ if (this._framePos > this.arrayAudioBuffer.frameLen) {
190
+ console.error("Array audio input stream frame pos: " + this._framePos + " greater then frame length: " + this.arrayAudioBuffer.frameLen);
191
+ }
192
+ return read;
193
+ }
194
+ get framePos() {
195
+ return this._framePos;
196
+ }
197
+ }
198
+
199
+ class EditFloat32ArrayInputStream {
200
+ constructor(_srcInputStream, offset = 0, length) {
201
+ this._srcInputStream = _srcInputStream;
202
+ this.offset = offset;
203
+ this.length = length;
204
+ this.framePos = 0;
205
+ this.readFrames = 0;
206
+ if (this.offset < 0) {
207
+ throw Error('Parameter offset must be undefined or greater or equal zero.');
208
+ }
209
+ if (this.length !== undefined && this.length < 0) {
210
+ throw Error('Parameter length must be undefined or greater or equal zero.');
211
+ }
212
+ }
213
+ skipToOffset() {
214
+ if (this.framePos == 0 && this.offset > 0) {
215
+ this._srcInputStream.skipFrames(this.offset);
216
+ this.framePos += this.offset;
217
+ }
218
+ }
219
+ read(buffers) {
220
+ this.skipToOffset();
221
+ let read = 0;
222
+ if (this.length === undefined) {
223
+ read = this._srcInputStream.read(buffers);
224
+ }
225
+ else {
226
+ if (buffers.length > 0) {
227
+ let bufsCh0 = buffers[0];
228
+ let bufsLen = bufsCh0.length;
229
+ let avail = this.length - this.readFrames;
230
+ if (avail > 0) {
231
+ if (avail > bufsLen) {
232
+ read = this._srcInputStream.read(buffers);
233
+ }
234
+ else {
235
+ // temporary buffers required
236
+ let tmpBufs = new Array(buffers.length);
237
+ for (let ch = 0; ch < buffers.length; ch++) {
238
+ tmpBufs[ch] = new Float32Array(avail);
239
+ }
240
+ read = this._srcInputStream.read(tmpBufs);
241
+ for (let ch = 0; ch < buffers.length; ch++) {
242
+ buffers[ch].set(tmpBufs[ch]);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ this.readFrames += read;
249
+ this.framePos += read;
250
+ return read;
251
+ }
252
+ skipFrames(n) {
253
+ this.skipToOffset();
254
+ if (this.length === undefined) {
255
+ this._srcInputStream.skipFrames(n);
256
+ }
257
+ else {
258
+ let avail = this.length - this.readFrames;
259
+ if (avail > 0) {
260
+ if (avail >= n) {
261
+ this._srcInputStream.skipFrames(n);
262
+ this.readFrames += n;
263
+ this.framePos += n;
264
+ }
265
+ else {
266
+ throw Error('Tried to skip out of bounds.');
267
+ }
268
+ }
269
+ }
270
+ }
271
+ close() {
272
+ this._srcInputStream.close();
273
+ }
274
+ }
275
+ class Float32ArrayChunkerOutStream {
276
+ constructor(outStream) {
277
+ this.outStream = outStream;
278
+ this.bufs = new Array();
279
+ this._channels = 0;
280
+ this._chunkSize = 0;
281
+ this.filled = 0;
282
+ this.receivedFrames = 0;
283
+ this.sentFrames = 0;
284
+ }
285
+ createBuffers() {
286
+ this.bufs = new Array(this._channels);
287
+ for (let ch = 0; ch < this._channels; ch++) {
288
+ this.bufs[ch] = new Float32Array(this._chunkSize);
289
+ }
290
+ }
291
+ set chunkSize(chunkSize) {
292
+ this._chunkSize = chunkSize;
293
+ this.createBuffers();
294
+ }
295
+ get chunkSize() {
296
+ return this._chunkSize;
297
+ }
298
+ set channels(channels) {
299
+ this._channels = channels;
300
+ this.createBuffers();
301
+ }
302
+ get channels() {
303
+ return this._channels;
304
+ }
305
+ available() {
306
+ return this._chunkSize - this.filled;
307
+ }
308
+ write(buffers) {
309
+ let copied = 0;
310
+ if (buffers.length > 0) {
311
+ let buffersLen = buffers[0].length;
312
+ this.receivedFrames += buffersLen;
313
+ let avail = buffersLen;
314
+ // Fill out buffers until all values copied
315
+ while (avail > 0) {
316
+ let toFill = this._chunkSize - this.filled;
317
+ if (toFill > avail) {
318
+ toFill = avail;
319
+ }
320
+ let sliceEnd = copied + toFill;
321
+ // Firefox on Android sends only the first channel
322
+ for (let ch = 0; ch < buffersLen; ch++) {
323
+ if (buffers[ch]) {
324
+ let cpPrt = buffers[ch].slice(copied, sliceEnd);
325
+ let buf = this.bufs[ch];
326
+ buf.set(cpPrt, this.filled);
327
+ }
328
+ }
329
+ copied += toFill;
330
+ avail -= toFill;
331
+ this.filled += toFill;
332
+ if (this.filled == this._chunkSize) {
333
+ this.outStream.write(this.bufs);
334
+ this.sentFrames += this.filled;
335
+ this.filled = 0;
336
+ }
337
+ }
338
+ }
339
+ return copied;
340
+ }
341
+ flush() {
342
+ if (this.filled > 0) {
343
+ let restBufs = new Array(this._channels);
344
+ for (let ch = 0; ch < this._channels; ch++) {
345
+ restBufs[ch] = this.bufs[ch].slice(0, this.filled);
346
+ }
347
+ this.outStream.write(restBufs);
348
+ this.outStream.flush();
349
+ this.sentFrames += this.filled;
350
+ this.filled = 0;
351
+ }
352
+ }
353
+ close() {
354
+ this.outStream.close();
355
+ }
356
+ }
357
+
358
+ // Code from audio_source_worklet.js as string constant to be loaded as module
359
+ // Changes in audio_source_worklet.js must be copied and pasted to this string constant
360
+ const aswpStr = "\n" +
361
+ "// Important note: Changes in audio_source_worklet.js must be copied and pasted to the string constant aswpStr in array_audio_buffer_source_node.ts\n" +
362
+ "\n" +
363
+ "\n" +
364
+ "class AudioSourceProcessor extends AudioWorkletProcessor{\n" +
365
+ "\n" +
366
+ " filledFrames=0;\n" +
367
+ " audioBuffers=new Array();\n" +
368
+ " currentAudioBuffer=null;\n" +
369
+ " currentAudioBufferFramePos=0;\n" +
370
+ " currentAudioBufferAvail=0;\n" +
371
+ " running=false;\n" +
372
+ " ended=false;\n" +
373
+ "\n" +
374
+ " constructor() {\n" +
375
+ " super({numberOfInputs:0,numberOfOutputs:1});\n" +
376
+ " this.port.onmessage=(msgEv)=>{\n" +
377
+ " // received audio playback data from application\n" +
378
+ " //console.debug(\"Audio source worklet msg: Received.\");\n" +
379
+ "\n" +
380
+ " if(msgEv.data.cmd){\n" +
381
+ " if('data'===msgEv.data.cmd) {\n" +
382
+ " let chs = msgEv.data.chs;\n" +
383
+ "\n" +
384
+ " let audioData = new Array(chs);\n" +
385
+ " for (let ch = 0; ch < chs; ch++) {\n" +
386
+ " audioData[ch] = new Float32Array(msgEv.data.audioData[ch]);\n" +
387
+ " }\n" +
388
+ " let msgChBufLen=audioData[0].length;\n" +
389
+ " this.audioBuffers.push(audioData);\n" +
390
+ " this.filledFrames += msgChBufLen;\n" +
391
+ " //console.debug(\"Audio source worklet msg: Filled \" + this.filledFrames+ \" in \"+this.audioBuffers.length+\" buffers.\");\n" +
392
+ "\n" +
393
+ " }else if('start'===msgEv.data.cmd){\n" +
394
+ " this.running=true;\n" +
395
+ " }else if('stop'===msgEv.data.cmd){\n" +
396
+ " //console.debug(\"Stop...\");\n" +
397
+ " this.running=false;\n" +
398
+ " // clear buffers\n" +
399
+ " this.filledFrames=0;\n" +
400
+ " while(this.audioBuffers.length > 0) {\n" +
401
+ " this.audioBuffers.pop();\n" +
402
+ " }\n" +
403
+ " this.currentAudioBuffer=new Float32Array(0);\n" +
404
+ " }\n" +
405
+ " }\n" +
406
+ " }\n" +
407
+ " }\n" +
408
+ "\n" +
409
+ " process(\n" +
410
+ " inputs,\n" +
411
+ " outputs,\n" +
412
+ " parameters\n" +
413
+ " ){\n" +
414
+ " //console.debug(\"Audio source worklet: process \"+outputs.length+ \" output buffers.\");\n" +
415
+ " // copy ring buffer data to outputs\n" +
416
+ " if(!this.running){\n" +
417
+ " return !this.ended;\n" +
418
+ " }\n" +
419
+ "\n" +
420
+ " let output=outputs[0];\n" +
421
+ " let chs=output.length;\n" +
422
+ " //console.debug(\"Audio source worklet: Output channels: \"+chs);\n" +
423
+ " if(chs>0) {\n" +
424
+ "\n" +
425
+ " let outCh0 = output[0];\n" +
426
+ " let outChLen = outCh0.length;\n" +
427
+ "\n" +
428
+ " if(!this.currentAudioBuffer){\n" +
429
+ " // get first buffer\n" +
430
+ " let nxtBuff=this.audioBuffers.shift();\n" +
431
+ " if(nxtBuff) {\n" +
432
+ " this.currentAudioBuffer = nxtBuff;\n" +
433
+ " this.currentAudioBufferFramePos=0;\n" +
434
+ " this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
435
+ " }else{\n" +
436
+ " return true;\n" +
437
+ " }\n" +
438
+ " }\n" +
439
+ "\n" +
440
+ " let copied=0;\n" +
441
+ " do{\n" +
442
+ " if(this.currentAudioBufferAvail==0){\n" +
443
+ " let nxtBuff=this.audioBuffers.shift();\n" +
444
+ " if(nxtBuff){\n" +
445
+ " this.currentAudioBuffer=nxtBuff;\n" +
446
+ " this.currentAudioBufferFramePos=0;\n" +
447
+ " this.currentAudioBufferAvail=this.currentAudioBuffer[0].length;\n" +
448
+ " //console.debug(\"Next buffer with \"+this.currentAudioBufferAvail+ \" frames\");\n" +
449
+ " this.port.postMessage({eventType:'bufferNotification',filledFrames:this.filledFrames});\n" +
450
+ " }else{\n" +
451
+ " this.ended=true;\n" +
452
+ " this.port.postMessage({eventType:'ended'});\n" +
453
+ " //console.debug(\"Stream ended\");\n" +
454
+ " break;\n" +
455
+ " }\n" +
456
+ " }\n" +
457
+ " //console.debug(\"outChLen: \"+outChLen+\", copied: \"+copied+\", current avail: \"+this.currentAudioBufferAvail);\n" +
458
+ " let toCopy=outChLen-copied;\n" +
459
+ " if(toCopy>this.currentAudioBufferAvail){\n" +
460
+ " toCopy=this.currentAudioBufferAvail;\n" +
461
+ " }\n" +
462
+ " //console.debug(\"Copy \"+toCopy+\" frames...\");\n" +
463
+ " for(let ch=0;ch<chs;ch++) {\n" +
464
+ " let outCh=output[ch];\n" +
465
+ " for (let i = 0; i < toCopy; i++) {\n" +
466
+ " outCh[copied+i]=this.currentAudioBuffer[ch][this.currentAudioBufferFramePos+i];\n" +
467
+ " }\n" +
468
+ " }\n" +
469
+ " copied+=toCopy;\n" +
470
+ " this.currentAudioBufferFramePos+=toCopy;\n" +
471
+ " this.currentAudioBufferAvail-=toCopy;\n" +
472
+ "\n" +
473
+ " }while(copied<outChLen);\n" +
474
+ " this.filledFrames-=copied;\n" +
475
+ " //console.debug(\"Copied \"+copied+\" frames.\");\n" +
476
+ " }\n" +
477
+ " return !this.ended;\n" +
478
+ " }\n" +
479
+ "}\n" +
480
+ "\n" +
481
+ "registerProcessor('audio-source-worklet',AudioSourceProcessor);\n";
482
+ class ArrayAudioBufferSourceNode extends AudioWorkletNode {
483
+ constructor(context) {
484
+ super(context, 'audio-source-worklet');
485
+ this._bufferFillSeconds = ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS;
486
+ this.bufferFillFrames = 0;
487
+ this._arrayAudioBuffer = null;
488
+ this._audioInputStream = null;
489
+ this._aisBufs = null;
490
+ this.filledFrames = 0;
491
+ this.onended = null;
492
+ this.channelCountMode = 'explicit';
493
+ this.port.onmessage = (msgEv) => {
494
+ if (msgEv.data) {
495
+ let evType = msgEv.data.eventType;
496
+ if (evType) {
497
+ if ('bufferNotification' === evType) {
498
+ this.filledFrames = msgEv.data.filledFrames;
499
+ //console.debug("Buffer notification: filled frames: " + this.filledFrames);
500
+ this.fillBuffer();
501
+ }
502
+ else if ('ended' === evType) {
503
+ let drainTime = 0;
504
+ if (this._arrayAudioBuffer?.sampleRate) {
505
+ drainTime = ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN / this._arrayAudioBuffer.sampleRate;
506
+ }
507
+ //let dstAny:any=this.context.destination;
508
+ //console.debug('Destination node tail-time: '+dstAny['tail-time']);
509
+ window.setTimeout(() => {
510
+ this.onended?.call(this);
511
+ }, drainTime * 1000);
512
+ }
513
+ }
514
+ }
515
+ };
516
+ }
517
+ fillBuffer(frameOffset) {
518
+ if (this._arrayAudioBuffer && this._audioInputStream && this._aisBufs) {
519
+ let filled = this.filledFrames;
520
+ let bufLen = 0;
521
+ while (filled < this.bufferFillFrames) {
522
+ let read = this._audioInputStream.read(this._aisBufs);
523
+ //console.log("ArrayAudioBufferSourceNode: read: "+read)
524
+ if (read) {
525
+ let trBuffers = new Array(this.channelCount);
526
+ for (let ch = 0; ch < this.channelCount; ch++) {
527
+ let adCh = this._aisBufs[ch];
528
+ let adChCopy = new Float32Array(adCh.length);
529
+ bufLen = adChCopy.length;
530
+ adChCopy.set(adCh);
531
+ trBuffers[ch] = adChCopy.buffer;
532
+ }
533
+ this.port.postMessage({
534
+ cmd: 'data',
535
+ chs: this.channelCount,
536
+ audioData: trBuffers
537
+ }, trBuffers);
538
+ filled += read;
539
+ }
540
+ else {
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ }
546
+ get arrayAudioBuffer() {
547
+ return this._arrayAudioBuffer;
548
+ }
549
+ set arrayAudioBuffer(value) {
550
+ this._arrayAudioBuffer = value;
551
+ if (this._arrayAudioBuffer?.channelCount) {
552
+ this.channelCount = this._arrayAudioBuffer?.channelCount;
553
+ this.bufferFillFrames = this._arrayAudioBuffer.sampleRate * this._bufferFillSeconds;
554
+ }
555
+ }
556
+ get bufferFillSeconds() {
557
+ return this._bufferFillSeconds;
558
+ }
559
+ set bufferFillSeconds(value) {
560
+ this._bufferFillSeconds = value;
561
+ }
562
+ start(when, offset, duration) {
563
+ if (when) {
564
+ throw Error("when, offest,duration parameters currently not supported by ArrayAudioBufferSourceNode class");
565
+ }
566
+ if (this._arrayAudioBuffer) {
567
+ let arrAis = new ArrayAudioBufferInputStream(this._arrayAudioBuffer);
568
+ if (offset === undefined && duration === undefined) {
569
+ this._audioInputStream = arrAis;
570
+ }
571
+ else {
572
+ let offsetFrames = 0;
573
+ let durationFrames = undefined;
574
+ if (offset !== undefined) {
575
+ offsetFrames = Math.floor(offset * this._arrayAudioBuffer.sampleRate);
576
+ }
577
+ if (duration !== undefined) {
578
+ durationFrames = Math.floor(duration * this._arrayAudioBuffer.sampleRate);
579
+ }
580
+ this._audioInputStream = new EditFloat32ArrayInputStream(arrAis, offsetFrames, durationFrames);
581
+ }
582
+ let chs = this._arrayAudioBuffer.channelCount;
583
+ this._aisBufs = new Array(chs);
584
+ for (let ch = 0; ch < chs; ch++) {
585
+ this._aisBufs[ch] = new Float32Array(1024);
586
+ }
587
+ this.fillBuffer();
588
+ this.port.postMessage({ cmd: 'start' });
589
+ }
590
+ }
591
+ stop() {
592
+ this.port.postMessage({ cmd: 'stop' });
593
+ this.onended?.call(this);
594
+ }
595
+ }
596
+ ArrayAudioBufferSourceNode.QUANTUM_FRAME_LEN = 128;
597
+ ArrayAudioBufferSourceNode.DEFAULT_BUFFER_FILL_SECONDS = 10;
598
+ class AudioSourceWorkletModuleLoader {
599
+ static loadModule(context) {
600
+ return new Promise((resolve, reject) => {
601
+ if (AudioSourceWorkletModuleLoader.moduleLoaded) {
602
+ resolve.call(self);
603
+ }
604
+ else {
605
+ let audioWorkletModuleBlob = new Blob([aswpStr], { type: 'text/javascript' });
606
+ let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
607
+ context.resume();
608
+ context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
609
+ AudioSourceWorkletModuleLoader.moduleLoaded = true;
610
+ resolve.call(self);
611
+ }).catch((reason) => {
612
+ console.error(reason);
613
+ reject.call(reason);
614
+ });
615
+ }
616
+ });
617
+ }
618
+ }
619
+ AudioSourceWorkletModuleLoader.moduleLoaded = false;
620
+
112
621
  var EventType;
113
622
  (function (EventType) {
114
623
  EventType[EventType["CLOSED"] = 0] = "CLOSED";
@@ -135,10 +644,17 @@ class AudioPlayer {
135
644
  this.running = false;
136
645
  this._audioClip = null;
137
646
  this._audioBuffer = null;
647
+ this._arrayAudioBuffer = null;
138
648
  this.sourceBufferNode = null;
649
+ this.sourceAudioWorkletNode = null;
139
650
  this.playStartTime = null;
140
651
  this.timerVar = null;
141
652
  this.context = context;
653
+ // AudioSourceWorkletModuleLoader.loadModule(this.context).then(()=>{
654
+ // console.debug("Audio source worklet module loaded.");
655
+ // }).catch((error: any)=>{
656
+ // console.error('Could not add module '+error);
657
+ // });
142
658
  this.listener = listener;
143
659
  this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
144
660
  this.n = navigator;
@@ -169,17 +685,23 @@ class AudioPlayer {
169
685
  return this._stopAction;
170
686
  }
171
687
  set audioClip(audioClip) {
172
- var length = 0;
173
- var chs = 0;
174
- if (audioClip && audioClip.buffer) {
175
- chs = audioClip.buffer.numberOfChannels;
688
+ let length = 0;
689
+ let chs = 0;
690
+ if (audioClip && audioClip.audioDataHolder) {
691
+ let audioDataHolder = audioClip.audioDataHolder;
692
+ chs = audioDataHolder.numberOfChannels;
176
693
  if (chs > 0) {
177
- length = audioClip.buffer.length;
694
+ length = audioDataHolder.frameLen;
178
695
  if (chs > this.context.destination.maxChannelCount) {
179
696
  // TODO exception
180
697
  }
181
698
  }
182
- this.audioBuffer = audioClip.buffer;
699
+ if (audioDataHolder.buffer) {
700
+ this.audioBuffer = audioDataHolder.buffer;
701
+ }
702
+ if (audioDataHolder.arrayBuffer) {
703
+ this.arrayAudioBuffer = audioDataHolder.arrayBuffer;
704
+ }
183
705
  audioClip.addSelectionObserver((ac) => {
184
706
  this._startSelectionAction.disabled = this.startSelectionDisabled();
185
707
  if (!this.startSelectionAction.disabled && this._autoPlayOnSelectToggleAction.value) {
@@ -189,12 +711,14 @@ class AudioPlayer {
189
711
  }
190
712
  else {
191
713
  this.audioBuffer = null;
714
+ //this.arrayAudioBuffer=null;
192
715
  }
193
716
  this._audioClip = audioClip;
194
717
  }
195
718
  set audioBuffer(audioBuffer) {
196
719
  this.stop();
197
720
  this._audioBuffer = audioBuffer;
721
+ this._arrayAudioBuffer = null;
198
722
  if (audioBuffer && this.context) {
199
723
  this._startAction.disabled = false;
200
724
  this._startSelectionAction.disabled = this.startSelectionDisabled();
@@ -213,23 +737,86 @@ class AudioPlayer {
213
737
  get audioBuffer() {
214
738
  return this._audioBuffer;
215
739
  }
216
- start() {
217
- if (!this._startAction.disabled && !this.running) {
218
- this.context.resume();
219
- this.sourceBufferNode = this.context.createBufferSource();
220
- this.sourceBufferNode.buffer = this._audioBuffer;
221
- this.sourceBufferNode.connect(this.context.destination);
222
- this.sourceBufferNode.onended = () => this.onended();
223
- this.playStartTime = this.context.currentTime;
224
- this.running = true;
225
- this.sourceBufferNode.start();
226
- this.playStartTime = this.context.currentTime;
740
+ set arrayAudioBuffer(arrayAudioBuffer) {
741
+ this.stop();
742
+ this._audioBuffer = null;
743
+ this._arrayAudioBuffer = arrayAudioBuffer;
744
+ if (arrayAudioBuffer && this.context) {
745
+ AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
746
+ this._startAction.disabled = false;
747
+ this._startSelectionAction.disabled = this.startSelectionDisabled();
748
+ if (this.listener) {
749
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
750
+ }
751
+ }).catch((error) => {
752
+ this._startAction.disabled = true;
753
+ this._startSelectionAction.disabled = true;
754
+ if (this.listener) {
755
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
756
+ }
757
+ console.error('Could not add module ' + error);
758
+ });
759
+ }
760
+ else {
227
761
  this._startAction.disabled = true;
228
762
  this._startSelectionAction.disabled = true;
229
- this._stopAction.disabled = false;
230
- //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
231
763
  if (this.listener) {
232
- this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
764
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
765
+ }
766
+ }
767
+ }
768
+ get arrayAudioBuffer() {
769
+ return this._arrayAudioBuffer;
770
+ }
771
+ start() {
772
+ if (!this._startAction.disabled && !this.running) {
773
+ this.context.resume();
774
+ if (this._audioBuffer) {
775
+ this.sourceBufferNode = this.context.createBufferSource();
776
+ this.sourceBufferNode.buffer = this._audioBuffer;
777
+ this.sourceBufferNode.connect(this.context.destination);
778
+ this.sourceBufferNode.onended = () => this.onended();
779
+ this.playStartTime = this.context.currentTime;
780
+ this.running = true;
781
+ this.sourceBufferNode.start();
782
+ this.playStartTime = this.context.currentTime;
783
+ this._startAction.disabled = true;
784
+ this._startSelectionAction.disabled = true;
785
+ this._stopAction.disabled = false;
786
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
787
+ if (this.listener) {
788
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
789
+ }
790
+ }
791
+ else if (this._arrayAudioBuffer) {
792
+ if (this._arrayAudioBuffer) {
793
+ this.sourceAudioWorkletNode = new ArrayAudioBufferSourceNode(this.context);
794
+ this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
795
+ let msg = 'Unknwon error';
796
+ if (ev instanceof ErrorEvent) {
797
+ msg = ev.message;
798
+ }
799
+ console.error("Audio source worklet error: " + msg);
800
+ if (this.listener) {
801
+ // TODO
802
+ // this.listener.error(msg);
803
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
804
+ }
805
+ };
806
+ this.sourceAudioWorkletNode.arrayAudioBuffer = this._arrayAudioBuffer;
807
+ this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
808
+ this.sourceAudioWorkletNode.onended = () => this.onended();
809
+ this.playStartTime = this.context.currentTime;
810
+ this.running = true;
811
+ this.sourceAudioWorkletNode.start();
812
+ this.playStartTime = this.context.currentTime;
813
+ this._startAction.disabled = true;
814
+ this._startSelectionAction.disabled = true;
815
+ this._stopAction.disabled = false;
816
+ if (this.listener) {
817
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
818
+ }
819
+ }
233
820
  }
234
821
  }
235
822
  }
@@ -239,34 +826,79 @@ class AudioPlayer {
239
826
  startSelected() {
240
827
  if (!this._startAction.disabled && !this.running) {
241
828
  this.context.resume();
242
- this.sourceBufferNode = this.context.createBufferSource();
243
- this.sourceBufferNode.buffer = this._audioBuffer;
244
- this.sourceBufferNode.connect(this.context.destination);
245
- this.sourceBufferNode.onended = () => this.onended();
246
- this.playStartTime = this.context.currentTime;
247
- this.running = true;
248
- // unfortunately Web Audio API uses time values not frames
249
- let ac = this._audioClip;
250
- let offset = 0;
251
- if (ac && ac.selection) {
252
- let s = ac.selection;
253
- let sr = ac.buffer.sampleRate;
254
- offset = s.leftFrame / sr;
255
- let stopPosInsecs = s.rightFrame / sr;
256
- let dur = stopPosInsecs - offset;
257
- // TODO check valid values
258
- this.sourceBufferNode.start(0, offset, dur);
259
- }
260
- else {
261
- this.sourceBufferNode.start();
262
- }
263
- this.playStartTime = this.context.currentTime - offset;
264
- this._startAction.disabled = true;
265
- this._startSelectionAction.disabled = true;
266
- this._stopAction.disabled = false;
267
- //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
268
- if (this.listener) {
269
- this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
829
+ if (this._audioBuffer) {
830
+ this.sourceBufferNode = this.context.createBufferSource();
831
+ this.sourceBufferNode.buffer = this._audioBuffer;
832
+ this.sourceBufferNode.connect(this.context.destination);
833
+ this.sourceBufferNode.onended = () => this.onended();
834
+ this.playStartTime = this.context.currentTime;
835
+ this.running = true;
836
+ // unfortunately Web Audio API uses time values not frames
837
+ let ac = this._audioClip;
838
+ let offset = 0;
839
+ if (ac && ac.selection) {
840
+ let s = ac.selection;
841
+ let sr = ac.audioDataHolder.sampleRate;
842
+ offset = s.leftFrame / sr;
843
+ let stopPosInsecs = s.rightFrame / sr;
844
+ let dur = stopPosInsecs - offset;
845
+ // TODO check valid values
846
+ this.sourceBufferNode.start(0, offset, dur);
847
+ }
848
+ else {
849
+ this.sourceBufferNode.start();
850
+ }
851
+ this.playStartTime = this.context.currentTime - offset;
852
+ this._startAction.disabled = true;
853
+ this._startSelectionAction.disabled = true;
854
+ this._stopAction.disabled = false;
855
+ //this.timerVar = window.setInterval((e)=>this.updatePlayPosition(), 200);
856
+ if (this.listener) {
857
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
858
+ }
859
+ }
860
+ else if (this._arrayAudioBuffer) {
861
+ if (this._arrayAudioBuffer) {
862
+ let aabsn = new ArrayAudioBufferSourceNode(this.context);
863
+ this.sourceAudioWorkletNode = aabsn;
864
+ aabsn.onprocessorerror = (ev) => {
865
+ let msg = 'Unknwon error';
866
+ if (ev instanceof ErrorEvent) {
867
+ msg = ev.message;
868
+ }
869
+ console.error("Audio source worklet error: " + msg);
870
+ if (this.listener) {
871
+ // TODO
872
+ // this.listener.error(msg);
873
+ // this.listener.audioPlayerUpdate(new AudioPlayerEvent());
874
+ }
875
+ };
876
+ aabsn.arrayAudioBuffer = this._arrayAudioBuffer;
877
+ aabsn.connect(this.context.destination); // this already starts playing
878
+ aabsn.onended = () => this.onended();
879
+ this.playStartTime = this.context.currentTime;
880
+ this.running = true;
881
+ let ac = this._audioClip;
882
+ let offset = 0;
883
+ if (ac && ac.selection) {
884
+ let s = ac.selection;
885
+ let sr = ac.audioDataHolder.sampleRate;
886
+ offset = s.leftFrame / sr;
887
+ let stopPosInsecs = s.rightFrame / sr;
888
+ let dur = stopPosInsecs - offset;
889
+ aabsn.start(0, offset, dur);
890
+ }
891
+ else {
892
+ aabsn.start();
893
+ }
894
+ this.playStartTime = this.context.currentTime - offset;
895
+ this._startAction.disabled = true;
896
+ this._startSelectionAction.disabled = true;
897
+ this._stopAction.disabled = false;
898
+ if (this.listener) {
899
+ this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
900
+ }
901
+ }
270
902
  }
271
903
  }
272
904
  }
@@ -275,6 +907,9 @@ class AudioPlayer {
275
907
  if (this.sourceBufferNode) {
276
908
  this.sourceBufferNode.stop();
277
909
  }
910
+ if (this.sourceAudioWorkletNode) {
911
+ this.sourceAudioWorkletNode.stop();
912
+ }
278
913
  if (this.timerVar !== null) {
279
914
  window.clearInterval(this.timerVar);
280
915
  }
@@ -288,7 +923,7 @@ class AudioPlayer {
288
923
  if (this.timerVar != null) {
289
924
  window.clearInterval(this.timerVar);
290
925
  }
291
- this._startAction.disabled = !(this.audioBuffer);
926
+ this._startAction.disabled = !(this.audioBuffer || this.arrayAudioBuffer);
292
927
  this._startSelectionAction.disabled = this.startSelectionDisabled();
293
928
  this._stopAction.disabled = true;
294
929
  this.running = false;
@@ -305,10 +940,17 @@ class AudioPlayer {
305
940
  }
306
941
  get playPositionFrames() {
307
942
  let ppFrs = null;
943
+ let sr = null;
308
944
  if (this._audioBuffer) {
945
+ sr = this._audioBuffer.sampleRate;
946
+ }
947
+ else if (this._arrayAudioBuffer) {
948
+ sr = this._arrayAudioBuffer.sampleRate;
949
+ }
950
+ if (sr) {
309
951
  let ppTime = this.playPositionTime;
310
952
  if (ppTime !== null) {
311
- ppFrs = this._audioBuffer.sampleRate * ppTime;
953
+ ppFrs = sr * ppTime;
312
954
  }
313
955
  }
314
956
  return ppFrs;
@@ -522,6 +1164,107 @@ class ProjectUtil {
522
1164
  }
523
1165
  ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
524
1166
 
1167
+ class ArrayAudioBuffer {
1168
+ constructor(_channelCount, _sampleRate, _data) {
1169
+ this._channelCount = _channelCount;
1170
+ this._sampleRate = _sampleRate;
1171
+ this._data = _data;
1172
+ this._chunkCount = 0;
1173
+ this._frameLen = 0;
1174
+ if (this._data.length > 0) {
1175
+ let ch0Data = this.data[0];
1176
+ for (let ch0Chk of ch0Data) {
1177
+ this._chunkCount++;
1178
+ this._frameLen += ch0Chk.length;
1179
+ }
1180
+ }
1181
+ }
1182
+ static fromAudioBuffer(audioBuffer, chunkFrameSize = 8192) {
1183
+ let aab;
1184
+ let chs = audioBuffer.numberOfChannels;
1185
+ let frameLength = audioBuffer.length;
1186
+ //let chunksSize=Math.ceil(frameLength/chunkFrameSize);
1187
+ let framePos = 0;
1188
+ let data = new Array(chs);
1189
+ for (let ch = 0; ch < chs; ch++) {
1190
+ data[ch] = new Array();
1191
+ }
1192
+ let toCopy = frameLength - framePos;
1193
+ while (toCopy > 0) {
1194
+ let toCopyChunk = chunkFrameSize;
1195
+ if (toCopyChunk > toCopy) {
1196
+ // last chunk, the rest
1197
+ toCopyChunk = toCopy;
1198
+ }
1199
+ for (let ch = 0; ch < chs; ch++) {
1200
+ data[ch].push(audioBuffer.getChannelData(ch).slice(framePos, framePos + toCopyChunk));
1201
+ }
1202
+ framePos += toCopyChunk;
1203
+ toCopy -= toCopyChunk;
1204
+ }
1205
+ aab = new ArrayAudioBuffer(chs, audioBuffer.sampleRate, data);
1206
+ return aab;
1207
+ }
1208
+ get channelCount() {
1209
+ return this._channelCount;
1210
+ }
1211
+ frames(framePos, frameLen, bufs) {
1212
+ let ccFramePos = 0;
1213
+ let trgFramePos = framePos;
1214
+ let ch0Data = this.data[0];
1215
+ let cPos = 0;
1216
+ let filled = 0;
1217
+ let ci = 0;
1218
+ while (filled < frameLen && ci < this._chunkCount) {
1219
+ // Current chunk
1220
+ let cc0 = ch0Data[ci];
1221
+ let ccLen = cc0.length;
1222
+ let ccFrameEndPos = ccFramePos + ccLen;
1223
+ if (trgFramePos >= ccFramePos && trgFramePos < ccFrameEndPos) {
1224
+ let toCp = frameLen - filled;
1225
+ cPos = trgFramePos - ccFramePos;
1226
+ if (cPos + toCp > ccLen) {
1227
+ toCp = ccLen - cPos;
1228
+ }
1229
+ for (let ch = 0; ch < bufs.length; ch++) {
1230
+ let cc = this.data[ch][ci];
1231
+ for (let i = 0; i < toCp; i++) {
1232
+ bufs[ch][filled + i] = cc[cPos + i];
1233
+ }
1234
+ }
1235
+ filled += toCp;
1236
+ trgFramePos += toCp;
1237
+ cPos += toCp;
1238
+ ccFramePos += toCp;
1239
+ if (cPos >= ccLen) {
1240
+ ccFramePos = ccFrameEndPos;
1241
+ cPos = 0;
1242
+ ci++;
1243
+ }
1244
+ }
1245
+ else {
1246
+ // next chunk
1247
+ ccFramePos = ccFrameEndPos;
1248
+ cPos = 0;
1249
+ ci++;
1250
+ }
1251
+ }
1252
+ return filled;
1253
+ }
1254
+ get sampleRate() {
1255
+ return this._sampleRate;
1256
+ }
1257
+ get frameLen() {
1258
+ return this._frameLen;
1259
+ }
1260
+ get chunkCount() {
1261
+ return this._chunkCount;
1262
+ }
1263
+ get data() {
1264
+ return this._data;
1265
+ }
1266
+ }
1267
+
525
1268
  const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
526
1269
  const DEBUG_TRACE_LEVEL = 0;
527
1270
  const ENABLE_AUDIO_WORKLET = true;
@@ -1092,6 +1835,10 @@ class AudioCapture {
1092
1835
  }
1093
1836
  return ab;
1094
1837
  }
1838
+ audioBufferArray() {
1839
+ let aba = new ArrayAudioBuffer(this.channelCount, this.currentSampleRate, this.data);
1840
+ return aba;
1841
+ }
1095
1842
  }
1096
1843
  AudioCapture.BUFFER_SIZE = 8192;
1097
1844
 
@@ -1471,21 +2218,28 @@ class RecordingFileDescriptorImpl {
1471
2218
  constructor() { }
1472
2219
  }
1473
2220
  class RecordingFile {
1474
- constructor(uuid, sessionId, audioBuffer) {
2221
+ constructor(uuid, sessionId, audioDataHolder) {
1475
2222
  this.recordingFileId = null;
1476
2223
  this.uuid = null;
2224
+ this.serverPersisted = false;
2225
+ this.keepAudioDataCache = false;
1477
2226
  this.date = null;
1478
2227
  this._dateAsDateObj = null;
1479
2228
  this.startedDate = null;
1480
2229
  this._startedAsDateObj = null;
1481
- this.audioBuffer = null;
2230
+ this.audioDataHolder = null;
1482
2231
  this.session = null;
1483
2232
  this.frames = null;
2233
+ this.timeLength = null;
1484
2234
  this.editSampleRate = null;
1485
2235
  this.editStartFrame = null;
1486
2236
  this.editEndFrame = null;
1487
2237
  this.session = sessionId;
1488
- this.audioBuffer = audioBuffer;
2238
+ this.audioDataHolder = audioDataHolder;
2239
+ if (audioDataHolder) {
2240
+ this.frames = audioDataHolder.frameLen;
2241
+ this.timeLength = audioDataHolder.duration;
2242
+ }
1489
2243
  this.uuid = uuid;
1490
2244
  }
1491
2245
  filenameString() {
@@ -1497,14 +2251,27 @@ class RecordingFile {
1497
2251
  fns += this.uuid;
1498
2252
  return fns;
1499
2253
  }
2254
+ equals(otherRecordingFile) {
2255
+ if (otherRecordingFile !== null) {
2256
+ if (otherRecordingFile === this) {
2257
+ return true;
2258
+ }
2259
+ if (otherRecordingFile.uuid === this.uuid) {
2260
+ return true;
2261
+ }
2262
+ }
2263
+ return false;
2264
+ }
2265
+ toString() {
2266
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session;
2267
+ }
1500
2268
  }
1501
2269
  class SprRecordingFile extends RecordingFile {
1502
- constructor(sessionId, itemcode, version, audioBuffer) {
1503
- super(UUID.generate(), sessionId, audioBuffer);
2270
+ constructor(sessionId, itemcode, version, audioDataHolder) {
2271
+ super(UUID.generate(), sessionId, audioDataHolder);
1504
2272
  this.session = sessionId;
1505
2273
  this.itemCode = itemcode;
1506
2274
  this.version = version;
1507
- this.audioBuffer = audioBuffer;
1508
2275
  }
1509
2276
  filenameString() {
1510
2277
  let fns = '';
@@ -1519,6 +2286,31 @@ class SprRecordingFile extends RecordingFile {
1519
2286
  fns += this.uuid;
1520
2287
  return fns;
1521
2288
  }
2289
+ toString() {
2290
+ return 'Recording file: UUID: ' + this.uuid + ', session: ' + this.session + ', itemcode: ' + this.itemCode + ', version: ' + this.version + ', UUID: ' + this.uuid;
2291
+ }
2292
+ }
2293
+ class RecordingFileUtils {
2294
+ static setAudioData(rf, audioDataHolder) {
2295
+ rf.audioDataHolder = audioDataHolder;
2296
+ if (audioDataHolder) {
2297
+ rf.frames = audioDataHolder.frameLen;
2298
+ rf.timeLength = audioDataHolder.duration;
2299
+ }
2300
+ }
2301
+ static sampleCount(rf) {
2302
+ if (rf.audioDataHolder) {
2303
+ return rf.audioDataHolder.sampleCounts();
2304
+ }
2305
+ else {
2306
+ return 0;
2307
+ }
2308
+ }
2309
+ static expireAudioData(rf) {
2310
+ let rv = rf.audioDataHolder;
2311
+ rf.audioDataHolder = null;
2312
+ return rv;
2313
+ }
1522
2314
  }
1523
2315
 
1524
2316
  // state of an upload
@@ -1569,7 +2361,8 @@ class UploaderStatusChangeEvent {
1569
2361
  }
1570
2362
  }
1571
2363
  class Upload {
1572
- constructor(blob, url) {
2364
+ constructor(blob, url, serverPersistable = null) {
2365
+ this.serverPersistable = serverPersistable;
1573
2366
  this.toString = () => {
1574
2367
  let s = `Upload: Status: ${this.status}, URL: ${this._url}`;
1575
2368
  if (this._data instanceof Blob) {
@@ -1590,6 +2383,17 @@ class Upload {
1590
2383
  get data() {
1591
2384
  return this._data;
1592
2385
  }
2386
+ done() {
2387
+ this.status = UploadStatus$1.DONE;
2388
+ //console.debug("Single upload done.");
2389
+ if (this.serverPersistable) {
2390
+ this.serverPersistable.serverPersisted = true;
2391
+ //console.debug("Single upload set server persisted: "+this.serverPersistable);
2392
+ }
2393
+ else {
2394
+ //console.debug("Server persistable not set.");
2395
+ }
2396
+ }
1593
2397
  }
1594
2398
  class Uploader {
1595
2399
  constructor(http, withCredentials = false) {
@@ -1628,7 +2432,7 @@ class Uploader {
1628
2432
  return si;
1629
2433
  }
1630
2434
  uploadDone(ul) {
1631
- ul.status = UploadStatus$1.DONE;
2435
+ ul.done();
1632
2436
  // remove upload from queue
1633
2437
  for (let i = 0; i < this.que.length; i++) {
1634
2438
  if (this.que[i] === ul) {
@@ -2059,16 +2863,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2059
2863
  }], ctorParameters: function () { return []; } });
2060
2864
 
2061
2865
  class AudioClip {
2062
- constructor(buffer) {
2866
+ constructor(_audioDataHolder) {
2867
+ this._audioDataHolder = _audioDataHolder;
2063
2868
  this._selection = null;
2064
2869
  this._levelInfos = null;
2065
2870
  this.selectionObservers = new Array();
2066
- this._buffer = buffer;
2067
2871
  }
2068
- get buffer() {
2069
- return this._buffer;
2872
+ get audioDataHolder() {
2873
+ return this._audioDataHolder;
2070
2874
  }
2071
- ;
2072
2875
  get selection() {
2073
2876
  return this._selection;
2074
2877
  }
@@ -2171,19 +2974,31 @@ class ViewSelection {
2171
2974
  class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2172
2975
  constructor() {
2173
2976
  super(...arguments);
2174
- this._audioData = null;
2977
+ //protected _audioData: AudioBuffer|null=null;
2978
+ //protected _arrayAudioData: ArrayAudioBuffer|null=null;
2979
+ this._audioDataHolder = null;
2175
2980
  this._bgColor = 'white';
2176
2981
  this._selectColor = 'rgba(0%,0%,100%,25%)';
2177
2982
  }
2983
+ frameLength() {
2984
+ let frameLength = null;
2985
+ // if (this._audioData && this._audioData.numberOfChannels > 0) {
2986
+ // let ch0 = this._audioData.getChannelData(0);
2987
+ // frameLength = ch0.length;
2988
+ //
2989
+ // }else if(this._arrayAudioData){
2990
+ // frameLength=this._arrayAudioData.frameLen;
2991
+ // }
2992
+ return frameLength;
2993
+ }
2178
2994
  /**
2179
2995
  * Returns pixel position depending on current zoom setting.
2180
2996
  * @param framePos audio frame (sample) position
2181
2997
  */
2182
2998
  frameToXPixelPosition(framePos) {
2183
2999
  let pixelPos = null;
2184
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2185
- let ch0 = this._audioData.getChannelData(0);
2186
- let frameLength = ch0.length;
3000
+ let frameLength = this._audioDataHolder?.frameLen;
3001
+ if (frameLength !== undefined) {
2187
3002
  let vw;
2188
3003
  if (this.bounds) {
2189
3004
  vw = this.bounds.dimension.width;
@@ -2213,9 +3028,8 @@ class BasicAudioCanvasLayerComponent extends CanvasLayerComponent {
2213
3028
  }
2214
3029
  viewPortXPixelToFramePosition(xViewPortPixelPos) {
2215
3030
  let vpXramePos = null;
2216
- if (this._audioData && this._audioData.numberOfChannels > 0) {
2217
- let ch0 = this._audioData.getChannelData(0);
2218
- let frameLength = ch0.length;
3031
+ let frameLength = this._audioDataHolder?.frameLen;
3032
+ if (frameLength !== undefined) {
2219
3033
  let vw;
2220
3034
  if (this.bounds) {
2221
3035
  vw = this.bounds.dimension.width;
@@ -2392,8 +3206,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2392
3206
  if (viewSel) {
2393
3207
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2394
3208
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2395
- if (this._audioData && frameStart != null && frameEnd != null) {
2396
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3209
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3210
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2397
3211
  }
2398
3212
  }
2399
3213
  this.selectingEventEmitter.emit(ns);
@@ -2403,8 +3217,8 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2403
3217
  if (viewSel) {
2404
3218
  let frameStart = this.viewPortXPixelToFramePosition(viewSel.startX);
2405
3219
  let frameEnd = this.viewPortXPixelToFramePosition(viewSel.endX);
2406
- if (this._audioData && frameStart != null && frameEnd != null) {
2407
- ns = new Selection(this._audioData.sampleRate, frameStart, frameEnd);
3220
+ if (this._audioDataHolder && frameStart != null && frameEnd != null) {
3221
+ ns = new Selection(this._audioDataHolder.sampleRate, frameStart, frameEnd);
2408
3222
  }
2409
3223
  }
2410
3224
  this.selectedEventEmitter.emit(ns);
@@ -2485,7 +3299,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2485
3299
  g.lineTo(xViewPortPixelpos, h);
2486
3300
  g.closePath();
2487
3301
  g.stroke();
2488
- if (this._audioData) {
3302
+ if (this._audioDataHolder) {
2489
3303
  g.font = '14px sans-serif';
2490
3304
  g.fillStyle = 'yellow';
2491
3305
  g.fillText(framePos.toString(), xViewPortPixelpos + 2, 50);
@@ -2497,6 +3311,7 @@ class AudioCanvasLayerComponent extends BasicAudioCanvasLayerComponent {
2497
3311
  }
2498
3312
  }
2499
3313
  }
3314
+ AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD = 10 * 60 * 48000; // Use streaming/chunking if audio clip has more than this number of samples
2500
3315
  AudioCanvasLayerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2501
3316
  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 });
2502
3317
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioCanvasLayerComponent, decorators: [{
@@ -2531,7 +3346,7 @@ class AudioSignal extends AudioCanvasLayerComponent {
2531
3346
  this._playFramePosition = null;
2532
3347
  this.worker = null;
2533
3348
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
2534
- this._audioData = null;
3349
+ this._audioDataHolder = null;
2535
3350
  this._bgColor = 'black';
2536
3351
  this._selectColor = 'rgba(255,255,0,0.8)';
2537
3352
  }
@@ -2584,25 +3399,26 @@ class AudioSignal extends AudioCanvasLayerComponent {
2584
3399
  */
2585
3400
  workerFunction() {
2586
3401
  addEventListener('message', ({ data }) => {
2587
- let audioData = data.audioData;
2588
- let l = data.l;
2589
- let w = data.w;
2590
- let h = data.h;
2591
- let vw = data.vw;
2592
- let chs = data.chs;
2593
- let frameLength = data.frameLength;
3402
+ let audioData = data.audioData; // audio data part required to render view port
3403
+ let auOffset = data.audioDataOffset;
3404
+ let l = data.l; // left pixel position of view port
3405
+ let w = data.w; // width of viewport
3406
+ let vw = data.vw; // total width of (virtual) audio view (not viewport width)
3407
+ let chs = data.chs; // number of channels
3408
+ let dataFrameLength = data.audioDataFrameLength; // frame length of audio data part required for view port
3409
+ let frameLength = data.frameLength; // total frame length (of audio clip)
3410
+ //console.debug("W: left: "+l+", w:"+w+", vw: "+vw+", chs: "+chs+", frameLength: "+frameLength);
2594
3411
  let psMinMax = new Float32Array(0);
2595
- if (audioData && w >= 0 && vw > 0) {
3412
+ if (audioData && audioData.length > 0 && w >= 0 && vw > 0) {
2596
3413
  let framesPerPixel = frameLength / vw;
2597
- let y = 0;
2598
3414
  let pointsLen = w * chs;
2599
3415
  // one for min one for max
2600
3416
  let arrLen = pointsLen * 2;
2601
3417
  psMinMax = new Float32Array(arrLen);
2602
3418
  let chFramePos = 0;
3419
+ let chFrameLength = audioData.length / chs;
2603
3420
  for (let ch = 0; ch < chs; ch++) {
2604
- let x = 0;
2605
- chFramePos = ch * frameLength;
3421
+ chFramePos = ch * chFrameLength;
2606
3422
  for (let pii = 0; pii < w; pii++) {
2607
3423
  let virtPii = l + pii;
2608
3424
  let pMin = Infinity;
@@ -2612,24 +3428,28 @@ class AudioSignal extends AudioCanvasLayerComponent {
2612
3428
  for (let ai = 0; ai < framesPerPixel; ai++) {
2613
3429
  let framePos = pixelFramePos + ai;
2614
3430
  let a = 0;
2615
- if (framePos >= 0 && framePos < audioData.length) {
2616
- a = audioData[framePos];
2617
- }
2618
- if (a < pMin) {
2619
- pMin = a;
2620
- }
2621
- if (a > pMax) {
2622
- pMax = a;
3431
+ let bufPos = framePos - auOffset;
3432
+ //let bufPos=framePos;
3433
+ if (bufPos >= 0 && bufPos < audioData.length) {
3434
+ a = audioData[bufPos];
3435
+ //console.debug("W: ch: "+ch+", pixelFramePos: "+pixelFramePos+", framePos: "+framePos+", auOffset: "+auOffset+", bufPos: "+bufPos+", audioData.length: "+audioData.length+", a: "+a);
3436
+ if (a < pMin) {
3437
+ pMin = a;
3438
+ }
3439
+ if (a > pMax) {
3440
+ pMax = a;
3441
+ }
2623
3442
  }
2624
3443
  }
2625
3444
  let psMinPos = ch * w + pii;
2626
3445
  psMinMax[psMinPos] = pMin;
2627
3446
  let psMaxPos = pointsLen + psMinPos;
2628
3447
  psMinMax[psMaxPos] = pMax;
3448
+ //console.debug("psMinMax["+psMinPos+"]="+pMin+",psMinMax["+psMaxPos+"]="+pMax);
2629
3449
  }
2630
3450
  }
2631
3451
  }
2632
- postMessage({ psMinMax: psMinMax, l: data.l, t: data.t, w: data.w, h: data.h, chs: data.chs }, [psMinMax.buffer]);
3452
+ postMessage({ psMinMax: psMinMax, l: data.l, w: data.w, chs: data.chs, eod: data.eod }, [psMinMax.buffer]);
2633
3453
  });
2634
3454
  }
2635
3455
  startDraw(clear = true) {
@@ -2657,41 +3477,143 @@ class AudioSignal extends AudioCanvasLayerComponent {
2657
3477
  if (this.bounds && this.bounds.dimension) {
2658
3478
  let w = Math.round(this.bounds.dimension.width);
2659
3479
  let h = Math.round(this.bounds.dimension.height);
2660
- if (this._audioData && w > 0 && h > 0) {
3480
+ if (this._audioDataHolder && w > 0 && h > 0) {
2661
3481
  //this.wo = new Worker('./audiosignal.worker.js',{type: 'module'});
2662
3482
  this.worker = new Worker(this.workerURL);
2663
3483
  //this.wo = new Worker('worker/audiosignal.worker.ts');
2664
3484
  //let Worker = require('worker!../../../workers/uploader/main');
2665
- let chs = this._audioData.numberOfChannels;
2666
- let frameLength = this._audioData.getChannelData(0).length;
2667
- // if(frameLength != this.audioData.getChannelData(1).length){
2668
- // alert("Ungleiche Länge");
2669
- // }
2670
- let ad = new Float32Array(chs * frameLength);
2671
- for (let ch = 0; ch < chs; ch++) {
2672
- ad.set(this._audioData.getChannelData(ch), ch * frameLength);
2673
- }
2674
- //let start = Date.now();
3485
+ let chs = this._audioDataHolder.numberOfChannels;
3486
+ let leftPos = Math.round(this.bounds.position.left);
3487
+ let renderPos = leftPos;
3488
+ let vw = Math.round(this.virtualDimension.width);
3489
+ let frameLength = this._audioDataHolder.frameLen;
3490
+ let framesPerPixel = Math.ceil(frameLength / vw);
3491
+ let audioBuffer = this._audioDataHolder.buffer;
3492
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
3493
+ let arrAbBuf;
3494
+ //let ais:ArrayAudioBufferInputStream|null=null;
3495
+ //let aisBuf:Float32Array[]|null=null;
3496
+ let psMinMax = null;
2675
3497
  if (this.worker) {
2676
3498
  this.worker.onmessage = (me) => {
2677
- //console.log("As rendertime: ", Date.now() - start);
2678
- this.drawRendered(me);
2679
- if (this.worker) {
2680
- this.worker.terminate();
3499
+ if (me.data.eod === true) {
3500
+ let psMinMaxTmp;
3501
+ if (psMinMax) {
3502
+ psMinMaxTmp = psMinMax;
3503
+ }
3504
+ else {
3505
+ psMinMaxTmp = me.data.psMinMax;
3506
+ }
3507
+ this.drawRendered(leftPos, w, h, chs, psMinMaxTmp);
3508
+ if (this.worker) {
3509
+ this.worker.terminate();
3510
+ }
3511
+ this.worker = null;
3512
+ }
3513
+ else if (this._audioDataHolder && arrAbBuf) {
3514
+ let rw = me.data.w;
3515
+ let rPointsLen = chs * rw;
3516
+ let pointsLen = chs * w;
3517
+ for (let ch = 0; ch < chs; ch++) {
3518
+ if (psMinMax) {
3519
+ let rBasePos = ch * rw;
3520
+ let basePos = ch * w;
3521
+ let rPosMin = rBasePos;
3522
+ let rPosMax = rPointsLen + rPosMin;
3523
+ let posMin = basePos + (renderPos - leftPos);
3524
+ let posMax = pointsLen + posMin;
3525
+ psMinMax[posMin] = me.data.psMinMax[rPosMin];
3526
+ //console.debug('Min: ('+pos+'): '+me.data.psMinMax[0]);
3527
+ psMinMax[posMax] = me.data.psMinMax[rPosMax];
3528
+ // console.debug('Max: ('+(pointsLen+pos)+'): '+me.data.psMinMax[1]);
3529
+ //console.debug("psMinMax["+posMin+"]="+me.data.psMinMax[rPosMin]+" (rPosMin="+rPosMin+"),psMinMax["+posMax+"]="+me.data.psMinMax[rPosMax]);
3530
+ }
3531
+ }
3532
+ let eod = false;
3533
+ renderPos++;
3534
+ let ad;
3535
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3536
+ if (renderPos < leftPos + w) {
3537
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3538
+ //console.debug("First read frame: "+arrAbBuf[0][0]);
3539
+ ad = new Float32Array(chs * framesPerPixel);
3540
+ for (let ch = 0; ch < chs; ch++) {
3541
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3542
+ }
3543
+ eod = (read <= 0);
3544
+ }
3545
+ else {
3546
+ ad = new Float32Array();
3547
+ eod = true;
3548
+ }
3549
+ let adBuf = ad.buffer;
3550
+ if (this.worker) {
3551
+ this.worker.postMessage({
3552
+ l: renderPos,
3553
+ w: me.data.w,
3554
+ h: h,
3555
+ vw: vw,
3556
+ chs: chs,
3557
+ frameLength: frameLength,
3558
+ audioData: ad,
3559
+ audioDataOffset: leftFramePos,
3560
+ eod: eod
3561
+ }, [adBuf]);
3562
+ }
2681
3563
  }
2682
- this.worker = null;
2683
3564
  };
2684
3565
  }
2685
- this.worker.postMessage({
2686
- l: Math.round(this.bounds.position.left),
2687
- t: Math.round(this.bounds.position.top),
2688
- w: w,
2689
- h: h,
2690
- vw: Math.round(this.virtualDimension.width),
2691
- chs: chs,
2692
- frameLength: frameLength,
2693
- audioData: ad
2694
- }, [ad.buffer]);
3566
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
3567
+ // Render whole clip at once
3568
+ arrAbBuf = null;
3569
+ psMinMax = null;
3570
+ let ad = new Float32Array(chs * frameLength);
3571
+ for (let ch = 0; ch < chs; ch++) {
3572
+ ad.set(audioBuffer.getChannelData(ch), ch * frameLength);
3573
+ }
3574
+ this.worker.postMessage({
3575
+ l: leftPos,
3576
+ w: w,
3577
+ vw: vw,
3578
+ chs: chs,
3579
+ frameLength: frameLength,
3580
+ audioData: ad,
3581
+ audioDataOffset: 0,
3582
+ eod: true
3583
+ }, [ad.buffer]);
3584
+ }
3585
+ else {
3586
+ // Render pixel by pixel
3587
+ if (w > 0) {
3588
+ if (framesPerPixel > 0) {
3589
+ let rw = 1;
3590
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
3591
+ arrAbBuf = new Array(chs);
3592
+ psMinMax = new Float32Array(chs * w * 2);
3593
+ for (let ch = 0; ch < chs; ch++) {
3594
+ arrAbBuf[ch] = new Float32Array(framesPerPixel);
3595
+ }
3596
+ let leftFramePos = Math.floor(frameLength * renderPos / vw);
3597
+ let auOffset = leftFramePos; // should always be 0
3598
+ //let read=arrayAudioBuffer.frames(leftFramePos,framesPerPixel,arrAbBuf);
3599
+ let read = this._audioDataHolder.frames(leftFramePos, framesPerPixel, arrAbBuf);
3600
+ let ad = new Float32Array(chs * framesPerPixel);
3601
+ for (let ch = 0; ch < chs; ch++) {
3602
+ ad.set(arrAbBuf[ch], ch * framesPerPixel);
3603
+ }
3604
+ this.worker.postMessage({
3605
+ l: renderPos,
3606
+ w: rw,
3607
+ vw: vw,
3608
+ chs: chs,
3609
+ frameLength: frameLength,
3610
+ audioData: ad,
3611
+ audioDataOffset: auOffset,
3612
+ eod: (read <= 0)
3613
+ }, [ad.buffer]);
3614
+ }
3615
+ }
3616
+ }
2695
3617
  }
2696
3618
  else {
2697
3619
  let g = this.signalCanvas.getContext("2d");
@@ -2701,42 +3623,42 @@ class AudioSignal extends AudioCanvasLayerComponent {
2701
3623
  }
2702
3624
  }
2703
3625
  }
2704
- drawRendered(me) {
3626
+ drawRendered(left, w, h, chs, psMinMax) {
2705
3627
  this.drawBg();
2706
- this.signalCanvas.style.left = me.data.l.toString() + 'px';
2707
- this.signalCanvas.width = me.data.w;
2708
- this.signalCanvas.height = me.data.h;
3628
+ this.signalCanvas.style.left = left.toString() + 'px';
3629
+ this.signalCanvas.width = w;
3630
+ this.signalCanvas.height = h;
2709
3631
  let g = this.signalCanvas.getContext("2d");
2710
3632
  if (g) {
2711
- g.clearRect(0, 0, me.data.w, me.data.h);
3633
+ g.clearRect(0, 0, w, h);
2712
3634
  //g.fillStyle = "black";
2713
3635
  //g.fillRect(0, 0, me.data.w, me.data.h);
2714
- let pointsLen = me.data.w * me.data.chs;
3636
+ let pointsLen = w * chs;
2715
3637
  // one for min one for max
2716
3638
  let arrLen = pointsLen * 2;
2717
- if (this._audioData) {
3639
+ if (this._audioDataHolder) {
2718
3640
  let std = Date.now();
2719
- let chH = me.data.h / me.data.chs;
3641
+ let chH = h / chs;
2720
3642
  let y = 0;
2721
- for (let ch = 0; ch < me.data.chs; ch++) {
3643
+ for (let ch = 0; ch < chs; ch++) {
2722
3644
  let x = 0;
2723
- let psMinPos = ch * me.data.w;
3645
+ let psMinPos = ch * w;
2724
3646
  let psMaxPos = pointsLen + psMinPos;
2725
3647
  g.fillStyle = 'green';
2726
3648
  g.strokeStyle = 'green';
2727
3649
  // draw audio signal as single polygon
2728
3650
  g.beginPath();
2729
- g.moveTo(0, y + (chH / 2) + me.data.psMinMax[psMaxPos] * chH / 2);
2730
- for (let pii = 0; pii < me.data.w; pii++) {
2731
- let psMax = me.data.psMinMax[psMaxPos + pii];
3651
+ g.moveTo(0, y + (chH / 2) + psMinMax[psMaxPos] * chH / 2);
3652
+ for (let pii = 0; pii < w; pii++) {
3653
+ let psMax = psMinMax[psMaxPos + pii];
2732
3654
  let pv = psMax * chH / 2;
2733
3655
  let yd = y + (chH / 2) - pv;
2734
3656
  //console.log("LineTo: : "+pii+" "+yd)
2735
3657
  g.lineTo(pii, yd);
2736
3658
  }
2737
- let revPixelStart = me.data.w - 1;
3659
+ let revPixelStart = w - 1;
2738
3660
  for (let pii = revPixelStart; pii >= 0; pii--) {
2739
- let psMin = me.data.psMinMax[psMinPos + pii];
3661
+ let psMin = psMinMax[psMinPos + pii];
2740
3662
  let pv = psMin * chH / 2;
2741
3663
  let yd = y + (chH / 2) - pv;
2742
3664
  //console.log("LineTo: : "+pii+" "+yd)
@@ -2760,13 +3682,23 @@ class AudioSignal extends AudioCanvasLayerComponent {
2760
3682
  g.clearRect(0, 0, w, h);
2761
3683
  //g.fillStyle = "black";
2762
3684
  //g.fillRect(0, 0, w, h);
2763
- if (this._audioData) {
3685
+ if (this._audioDataHolder) {
2764
3686
  let std = Date.now();
2765
- let chs = this._audioData.numberOfChannels;
3687
+ let chs = this._audioDataHolder.numberOfChannels;
2766
3688
  let chH = h / chs;
2767
- let frameLength = this._audioData.getChannelData(0).length;
3689
+ let frameLength = this._audioDataHolder.frameLen;
2768
3690
  let framesPerPixel = frameLength / w;
2769
3691
  let y = 0;
3692
+ let ais = null;
3693
+ let audioBuffer = this._audioDataHolder.buffer;
3694
+ let aisBuffer = null;
3695
+ if (!audioBuffer) {
3696
+ ais = this._audioDataHolder.audioInputStream();
3697
+ aisBuffer = new Array(chs);
3698
+ for (let ch = 0; ch < chs; ch++) {
3699
+ aisBuffer[ch] = new Float32Array(framesPerPixel);
3700
+ }
3701
+ }
2770
3702
  for (let ch = 0; ch < chs; ch++) {
2771
3703
  let x = 0;
2772
3704
  let psMin = new Float32Array(w);
@@ -2775,20 +3707,33 @@ class AudioSignal extends AudioCanvasLayerComponent {
2775
3707
  for (let pii = 0; pii < w; pii++) {
2776
3708
  let pMin = 0;
2777
3709
  let pMax = 0;
2778
- // calculate pixel min/max amplitude
2779
- for (let ai = 0; ai < framesPerPixel; ai++) {
2780
- //let framePos=(pii*framesPerPixel)+ai;
2781
- let a = this._audioData.getChannelData(ch)[framePos++];
2782
- if (a < pMin) {
2783
- pMin = a;
3710
+ if (audioBuffer) {
3711
+ // calculate pixel min/max amplitude
3712
+ for (let ai = 0; ai < framesPerPixel; ai++) {
3713
+ //let framePos=(pii*framesPerPixel)+ai;
3714
+ let a = audioBuffer.getChannelData(ch)[framePos++];
3715
+ if (a < pMin) {
3716
+ pMin = a;
3717
+ }
3718
+ if (a > pMax) {
3719
+ pMax = a;
3720
+ }
2784
3721
  }
2785
- if (a > pMax) {
2786
- pMax = a;
3722
+ }
3723
+ else if (ais && aisBuffer) {
3724
+ let r = ais.read(aisBuffer);
3725
+ for (let ai = 0; ai < r; ai++) {
3726
+ let a = aisBuffer[ch][ai];
3727
+ if (a < pMin) {
3728
+ pMin = a;
3729
+ }
3730
+ if (a > pMax) {
3731
+ pMax = a;
3732
+ }
2787
3733
  }
2788
3734
  }
2789
3735
  psMin[pii] = pMin;
2790
3736
  psMax[pii] = pMax;
2791
- //console.log("Min: ", pMin, " max: ", pMax);
2792
3737
  }
2793
3738
  g.fillStyle = 'green';
2794
3739
  g.strokeStyle = 'green';
@@ -2813,12 +3758,11 @@ class AudioSignal extends AudioCanvasLayerComponent {
2813
3758
  g.stroke();
2814
3759
  y += chH;
2815
3760
  }
2816
- //this.drawPlayPosition();
2817
3761
  }
2818
3762
  }
2819
3763
  }
2820
3764
  setData(audioData) {
2821
- this._audioData = audioData;
3765
+ this._audioDataHolder = audioData;
2822
3766
  this.playFramePosition = 0;
2823
3767
  }
2824
3768
  }
@@ -3187,7 +4131,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3187
4131
  this._playFramePosition = null;
3188
4132
  this.dftSize = DEFAULT_DFT_SIZE;
3189
4133
  this.worker = null;
3190
- this._audioData = null;
4134
+ this._audioDataHolder = null;
3191
4135
  this.markers = new Array();
3192
4136
  this.dft = new DFTFloat32(this.dftSize);
3193
4137
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
@@ -3240,7 +4184,7 @@ class Sonagram extends AudioCanvasLayerComponent {
3240
4184
  g.lineTo(xViewPortPixelpos, h);
3241
4185
  g.closePath();
3242
4186
  g.stroke();
3243
- if (this._audioData) {
4187
+ if (this._audioDataHolder) {
3244
4188
  let framePosRound = this.viewPortXPixelToFramePosition(xViewPortPixelpos);
3245
4189
  if (framePosRound != null) {
3246
4190
  g.font = '14px sans-serif';
@@ -3504,11 +4448,21 @@ class Sonagram extends AudioCanvasLayerComponent {
3504
4448
  }
3505
4449
  GaussianWindow.DEFAULT_SIGMA = 0.3;
3506
4450
  self.onmessage = function (msg) {
4451
+ //console.debug("Sonagram render thread");
3507
4452
  let l = msg.data.l;
3508
4453
  let w = msg.data.w;
3509
4454
  let h = msg.data.h;
3510
4455
  let vw = msg.data.vw;
3511
4456
  let chs = msg.data.chs;
4457
+ let audioDataOffset = 0;
4458
+ let adOffset = msg.data.audioDataOffset;
4459
+ if (adOffset) {
4460
+ audioDataOffset = adOffset;
4461
+ }
4462
+ let maxPsd = null;
4463
+ if (msg.data.maxPsd !== undefined) {
4464
+ maxPsd = msg.data.maxPsd;
4465
+ }
3512
4466
  let audioData = new Array(chs);
3513
4467
  for (let ch = 0; ch < chs; ch++) {
3514
4468
  audioData[ch] = new Float32Array(msg.data['audioData'][ch]);
@@ -3524,16 +4478,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3524
4478
  }
3525
4479
  let imgData = new Uint8ClampedArray(arrSize);
3526
4480
  //console.log("Render method:");
3527
- if (audioData && arrSize > 0) {
4481
+ //console.debug("Created imgData arrSize: "+arrSize+" ", w, "x", h);
4482
+ let calcMaxPsd = -Infinity;
4483
+ if (arrSize > 0) {
3528
4484
  let chH = Math.round(h / chs);
3529
4485
  let framesPerPixel = frameLength / vw;
3530
- //console.log("Render: ", w, "x", h);
4486
+ //console.debug("Render: ", w, "x", h);
3531
4487
  let b = new Float32Array(dftSize);
3532
4488
  let sona = new Array(chs);
3533
- let maxPsd = -Infinity;
3534
- let p = 0;
4489
+ //let p = 0;
3535
4490
  for (let ch = 0; ch < chs; ch++) {
3536
- p = ch * frameLength;
4491
+ //p = ch * frameLength;
3537
4492
  let chDataLen = audioData[ch].length;
3538
4493
  let x = 0;
3539
4494
  // initialize DFT array buffer
@@ -3550,8 +4505,13 @@ class Sonagram extends AudioCanvasLayerComponent {
3550
4505
  // initialize for negative sample positions and out of bounds positions
3551
4506
  let chDat = 0;
3552
4507
  // Set audio sample if available
3553
- if (samplePos >= 0 && samplePos < chDataLen) {
3554
- chDat = audioData[ch][samplePos];
4508
+ let adp = samplePos - audioDataOffset;
4509
+ if (adp >= 0 && adp < chDataLen) {
4510
+ chDat = audioData[ch][adp];
4511
+ //console.debug("Audio data: "+chDat);
4512
+ }
4513
+ else {
4514
+ //console.debug("Sample buf pos oob: adp: "+adp+", chDataLen: "+chDataLen+", samplePos: "+samplePos+", i: "+i);
3555
4515
  }
3556
4516
  // apply Window
3557
4517
  b[i] = chDat * wf.getScale(i);
@@ -3561,63 +4521,78 @@ class Sonagram extends AudioCanvasLayerComponent {
3561
4521
  // Get maximum value of spectral energy
3562
4522
  for (let s = 0; s < dftBands; s++) {
3563
4523
  let psd = (2 * Math.pow(spectr[s], 2)) / dftBands;
3564
- if (psd > maxPsd) {
3565
- maxPsd = psd;
4524
+ if (psd > calcMaxPsd) {
4525
+ calcMaxPsd = psd;
3566
4526
  }
3567
4527
  }
3568
4528
  // Set render model data for this pixel
3569
4529
  sona[ch][pii] = spectr;
3570
4530
  }
3571
4531
  }
3572
- //maxPsd = (2 * Math.pow(max, 2)) / dftBands;
3573
- for (let ch = 0; ch < chs; ch++) {
3574
- for (let pii = 0; pii < w; pii++) {
3575
- let allBlack = true;
3576
- for (let y = 0; y < chH; y++) {
3577
- let freqIdx = Math.round(y * dftBands / chH);
3578
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3579
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3580
- let val = sona[ch][pii][freqIdx];
3581
- let psd = (2 * Math.pow(val, 2)) / dftBands;
3582
- // Calculate logarithmic value
3583
- //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
3584
- let linearLevel = psd / maxPsd;
3585
- let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
3586
- // Fixed dynamic Range value of 70dB for now
3587
- let dynRangeInDb = 70;
3588
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3589
- // are the following checks necessary for clamped array ?
3590
- if (scaledVal > 1.0)
3591
- scaledVal = 1;
3592
- if (scaledVal < 0.0) {
3593
- scaledVal = 0;
3594
- }
3595
- let rgbVal = Math.round(255 * scaledVal);
3596
- if (rgbVal < 0) {
3597
- // System.out.println("Neg RGB val: "+rgbVal);
3598
- rgbVal = 0;
3599
- }
3600
- if (rgbVal > 255) {
3601
- rgbVal = 255;
3602
- }
3603
- rgbVal = 255 - rgbVal;
3604
- if (rgbVal > 0) {
3605
- allBlack = false;
4532
+ if (!msg.data.norender) {
4533
+ if (!maxPsd) {
4534
+ maxPsd = calcMaxPsd;
4535
+ }
4536
+ for (let ch = 0; ch < chs; ch++) {
4537
+ for (let pii = 0; pii < w; pii++) {
4538
+ let allBlack = true;
4539
+ for (let y = 0; y < chH; y++) {
4540
+ let freqIdx = Math.round(y * dftBands / chH);
4541
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4542
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4543
+ let val = sona[ch][pii][freqIdx];
4544
+ let psd = (2 * Math.pow(val, 2)) / dftBands;
4545
+ // Calculate logarithmic value
4546
+ //let psdLog = ips.dsp.DSPUtils.toLevelInDB(psd / maxPsd);
4547
+ let linearLevel = psd / maxPsd;
4548
+ let psdLog = 10 * Math.log(linearLevel) / Math.log(10);
4549
+ // Fixed dynamic Range value of 70dB for now
4550
+ let dynRangeInDb = 70;
4551
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4552
+ // are the following checks necessary for clamped array ?
4553
+ if (scaledVal > 1.0)
4554
+ scaledVal = 1;
4555
+ if (scaledVal < 0.0) {
4556
+ scaledVal = 0;
4557
+ }
4558
+ let rgbVal = Math.round(255 * scaledVal);
4559
+ if (rgbVal < 0) {
4560
+ // System.out.println("Neg RGB val: "+rgbVal);
4561
+ rgbVal = 0;
4562
+ }
4563
+ if (rgbVal > 255) {
4564
+ rgbVal = 255;
4565
+ }
4566
+ rgbVal = 255 - rgbVal;
4567
+ if (rgbVal > 0) {
4568
+ allBlack = false;
4569
+ }
4570
+ let py = chH - y;
4571
+ let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
4572
+ imgData[dataPos + 0] = rgbVal; //R
4573
+ imgData[dataPos + 1] = rgbVal; //G
4574
+ imgData[dataPos + 2] = rgbVal; //B
4575
+ imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4576
+ //console.debug("Rendered: py: "+py+", rgbval: "+rgbVal);
4577
+ // example 1x1, 2chs
4578
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4579
+ // ch0x1y0R,ch0x1y0G,ch0x1y0B,ch0x1y0A,
4580
+ // ch0x0y0R,ch0x0y0G,ch0x0y0B,ch0x0y0A,
4581
+ // ch0x1y1R,ch0x1y1G,ch0x1y1B,ch0x1y1A,
4582
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4583
+ // ch1x1y0R,ch1x1y0G,ch1x1y0B,ch1x1y0A,
4584
+ // ch1x0y0R,ch1x0y0G,ch1x0y0B,ch1x0y0A,
4585
+ // ch1x1y1R,ch1x1y1G,ch1x1y1B,ch1x1y1A
3606
4586
  }
3607
- let py = chH - y;
3608
- let dataPos = ((((ch * chH) + py) * w) + pii) * 4;
3609
- imgData[dataPos + 0] = rgbVal; //R
3610
- imgData[dataPos + 1] = rgbVal; //G
3611
- imgData[dataPos + 2] = rgbVal; //B
3612
- imgData[dataPos + 3] = 255; //A (alpha: fully opaque)
4587
+ // if (allBlack) {
4588
+ // console.log("Black: ", pii, " ", ch);
4589
+ // }
3613
4590
  }
3614
- // if (allBlack) {
3615
- // console.log("Black: ", pii, " ", ch);
3616
- // }
3617
4591
  }
3618
4592
  }
3619
4593
  }
3620
- postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw }, [imgData.buffer]);
4594
+ //console.debug("Render thread post message imgData: "+imgData.length)
4595
+ postMessage({ imgData: imgData, l: l, w: msg.data.w, h: msg.data.h, vw: vw, maxPsd: calcMaxPsd, terminate: msg.data.terminate }, [imgData.buffer]);
3621
4596
  };
3622
4597
  }
3623
4598
  startDraw(clear = true) {
@@ -3647,42 +4622,190 @@ class Sonagram extends AudioCanvasLayerComponent {
3647
4622
  if (this.bounds) {
3648
4623
  let w = Math.round(this.bounds.dimension.width);
3649
4624
  let h = Math.round(this.bounds.dimension.height);
3650
- if (this._audioData && w > 0 && h > 0) {
4625
+ if (this._audioDataHolder && w > 0 && h > 0) {
3651
4626
  this.worker = new Worker(this.workerURL);
3652
4627
  //this.wo = new Worker('./worker/sonagram.worker', { type: `module` });
3653
- let chs = this._audioData.numberOfChannels;
3654
- let frameLength = this._audioData.getChannelData(0).length;
3655
- let ada = new Array(chs);
3656
- for (let ch = 0; ch < chs; ch++) {
3657
- // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
3658
- ada[ch] = this._audioData.getChannelData(ch).buffer.slice(0);
3659
- }
3660
- let start = Date.now();
4628
+ let chs = this._audioDataHolder.numberOfChannels;
4629
+ let vw = Math.round(this.virtualDimension.width);
4630
+ let frameLength = this._audioDataHolder.frameLen;
4631
+ let framesPerPixel = Math.ceil(frameLength / vw);
4632
+ let leftPos = Math.round(this.bounds.position.left);
4633
+ let renderPos = leftPos;
4634
+ let audioBuffer = this._audioDataHolder.buffer;
4635
+ //let arrayAudioBuffer=this._audioDataHolder.arrayBuffer;
4636
+ let arrAbBuf;
4637
+ let imgData;
4638
+ let maxPsd = -Infinity;
4639
+ let norender = true;
3661
4640
  if (this.worker) {
3662
4641
  this.worker.onmessage = (me) => {
3663
- this.drawRendered(me);
3664
- if (this.worker) {
3665
- this.worker.terminate();
4642
+ if (true === me.data.terminate) {
4643
+ let drawImgData;
4644
+ if (imgData) {
4645
+ drawImgData = imgData;
4646
+ }
4647
+ else {
4648
+ drawImgData = me.data.imgData;
4649
+ }
4650
+ this.drawRendered(w, h, drawImgData);
4651
+ if (this.worker) {
4652
+ this.worker.terminate();
4653
+ }
4654
+ this.worker = null;
4655
+ }
4656
+ else {
4657
+ // set rendered vertical values of one pixel of timescale
4658
+ //let dataPos = renderPos * h * 4;
4659
+ if (norender) {
4660
+ if (me.data.maxPsd > maxPsd) {
4661
+ maxPsd = me.data.maxPsd;
4662
+ //console.debug("new maxPsd: "+maxPsd);
4663
+ }
4664
+ }
4665
+ else {
4666
+ let chH = Math.round(h / chs);
4667
+ let idp = me.data.l - leftPos;
4668
+ for (let ch = 0; ch < chs; ch++) {
4669
+ for (let y = 0; y < chH; y++) {
4670
+ let py = chH - y;
4671
+ let dataPos = ((((ch * chH) + py) * w) + idp) * 4;
4672
+ let mePos = ((ch * chH) + py) * 4;
4673
+ //let lastPos = dataPos + me.data.imgData.length;
4674
+ //if (lastPos < imgData.length) {
4675
+ // set one pixel
4676
+ imgData[dataPos] = me.data.imgData[mePos];
4677
+ imgData[dataPos + 1] = me.data.imgData[mePos + 1];
4678
+ imgData[dataPos + 2] = me.data.imgData[mePos + 2];
4679
+ imgData[dataPos + 3] = me.data.imgData[mePos + 3];
4680
+ //} else {
4681
+ //console.error("Out of range: " + dataPos + "+" + me.data.imgData.length + ">=" + imgData.length);
4682
+ // }
4683
+ }
4684
+ }
4685
+ }
4686
+ if (this._audioDataHolder && arrAbBuf && this.worker) {
4687
+ // proceed with next pixel
4688
+ renderPos++;
4689
+ //console.debug("Render pos: "+renderPos);
4690
+ let terminate = false;
4691
+ let windowEnd = renderPos >= leftPos + w;
4692
+ if (windowEnd) {
4693
+ if (norender) {
4694
+ // phase two: rendering
4695
+ norender = false;
4696
+ // start from beginning
4697
+ renderPos = leftPos;
4698
+ //console.debug("now rendering: maxPsd: "+maxPsd);
4699
+ }
4700
+ else {
4701
+ // terminate render phase
4702
+ terminate = true;
4703
+ }
4704
+ }
4705
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4706
+ if (leftFramePos < 0) {
4707
+ leftFramePos = 0;
4708
+ }
4709
+ let ada = new Array(chs);
4710
+ //console.debug("Render pos: "+renderPos+" leftFramePos: "+leftFramePos);
4711
+ if (!terminate) {
4712
+ if (this._audioDataHolder) {
4713
+ let read = this._audioDataHolder.frames(leftFramePos, this.dftSize, arrAbBuf);
4714
+ for (let ch = 0; ch < chs; ch++) {
4715
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4716
+ ada[ch] = arrAbBuf[ch].buffer.slice(0);
4717
+ }
4718
+ }
4719
+ }
4720
+ else {
4721
+ for (let ch = 0; ch < chs; ch++) {
4722
+ ada[ch] = new ArrayBuffer(0);
4723
+ }
4724
+ }
4725
+ this.worker.postMessage({
4726
+ audioData: ada,
4727
+ audioDataOffset: leftFramePos,
4728
+ l: renderPos,
4729
+ w: me.data.w,
4730
+ h: h,
4731
+ vw: vw,
4732
+ chs: chs,
4733
+ frameLength: frameLength,
4734
+ dftSize: this.dftSize,
4735
+ maxPsd: maxPsd,
4736
+ norender: norender,
4737
+ terminate: terminate
4738
+ }, ada);
4739
+ }
3666
4740
  }
3667
- this.worker = null;
3668
4741
  };
3669
4742
  }
3670
- if (this.markerCanvas) {
3671
- let g = this.markerCanvas.getContext("2d");
3672
- if (g) {
3673
- g.fillText("Rendering...", 10, 20);
4743
+ if (audioBuffer && audioBuffer.length * audioBuffer.numberOfChannels < AudioCanvasLayerComponent.ENABLE_STREAMING_NUMBER_OF_SAMPLES_THRESHOLD) {
4744
+ let ada = new Array(chs);
4745
+ for (let ch = 0; ch < chs; ch++) {
4746
+ // Need a copy here for the worker, otherwise this.audioData is not accessible after posting to the worker
4747
+ ada[ch] = audioBuffer.getChannelData(ch).buffer.slice(0);
4748
+ }
4749
+ let start = Date.now();
4750
+ if (this.markerCanvas) {
4751
+ let g = this.markerCanvas.getContext("2d");
4752
+ if (g) {
4753
+ g.fillText("Rendering...", 10, 20);
4754
+ }
4755
+ }
4756
+ this.worker.postMessage({
4757
+ audioData: ada,
4758
+ l: leftPos,
4759
+ w: w,
4760
+ h: h,
4761
+ vw: Math.round(this.virtualDimension.width),
4762
+ chs: chs,
4763
+ frameLength: frameLength,
4764
+ dftSize: this.dftSize,
4765
+ terminate: true
4766
+ }, ada);
4767
+ }
4768
+ else {
4769
+ if (w > 0) {
4770
+ if (framesPerPixel > 0) {
4771
+ let arrSize = w * h * 4;
4772
+ if (arrSize < 0) {
4773
+ arrSize = 0;
4774
+ }
4775
+ imgData = new Uint8ClampedArray(arrSize);
4776
+ let rw = 1;
4777
+ //ais = new ArrayAudioBufferInputStream(arrayAudioBuffer);
4778
+ arrAbBuf = new Array(chs);
4779
+ for (let ch = 0; ch < chs; ch++) {
4780
+ arrAbBuf[ch] = new Float32Array(this.dftSize);
4781
+ }
4782
+ let leftFramePos = Math.floor(frameLength * renderPos / vw) - this.dftSize / 2;
4783
+ let framesToRead = this.dftSize;
4784
+ if (leftFramePos < 0) {
4785
+ //framesToRead=this.dftSize+leftFramePos;
4786
+ leftFramePos = 0;
4787
+ }
4788
+ let read = this._audioDataHolder.frames(leftFramePos, framesToRead, arrAbBuf);
4789
+ let ad = new Float32Array(chs * framesToRead);
4790
+ for (let ch = 0; ch < chs; ch++) {
4791
+ ad.set(arrAbBuf[ch], ch * framesToRead);
4792
+ }
4793
+ this.worker.postMessage({
4794
+ l: renderPos,
4795
+ w: rw,
4796
+ h: h,
4797
+ vw: vw,
4798
+ chs: chs,
4799
+ frameLength: frameLength,
4800
+ audioData: ad,
4801
+ audioDataOffset: leftFramePos,
4802
+ dftSize: this.dftSize,
4803
+ norender: norender,
4804
+ terminate: false
4805
+ }, [ad.buffer]);
4806
+ }
3674
4807
  }
3675
4808
  }
3676
- this.worker.postMessage({
3677
- audioData: ada,
3678
- l: Math.round(this.bounds.position.left),
3679
- w: w,
3680
- h: h,
3681
- vw: Math.round(this.virtualDimension.width),
3682
- chs: chs,
3683
- frameLength: frameLength,
3684
- dftSize: this.dftSize
3685
- }, ada);
3686
4809
  }
3687
4810
  else {
3688
4811
  let g = this.sonagramCanvas.getContext("2d");
@@ -3692,17 +4815,17 @@ class Sonagram extends AudioCanvasLayerComponent {
3692
4815
  }
3693
4816
  }
3694
4817
  }
3695
- drawRendered(me) {
4818
+ drawRendered(w, h, imgData) {
3696
4819
  if (this.sonagramCanvas) {
3697
- this.sonagramCanvas.width = me.data.w;
3698
- this.sonagramCanvas.height = me.data.h;
4820
+ this.sonagramCanvas.width = w;
4821
+ this.sonagramCanvas.height = h;
3699
4822
  let g = this.sonagramCanvas.getContext("2d");
3700
4823
  if (g) {
3701
- let imgDataArr = me.data.imgData;
3702
- if (me.data.w > 0 && me.data.h > 0) {
3703
- let imgData = g.createImageData(me.data.w, me.data.h);
3704
- imgData.data.set(imgDataArr);
3705
- g.putImageData(imgData, 0, 0);
4824
+ let imgDataArr = imgData;
4825
+ if (w > 0 && h > 0) {
4826
+ let gImgData = g.createImageData(w, h);
4827
+ gImgData.data.set(imgDataArr);
4828
+ g.putImageData(gImgData, 0, 0);
3706
4829
  }
3707
4830
  }
3708
4831
  }
@@ -3715,94 +4838,98 @@ class Sonagram extends AudioCanvasLayerComponent {
3715
4838
  let w = this.sonagramCanvas.width;
3716
4839
  let h = this.sonagramCanvas.height;
3717
4840
  if (g) {
3718
- g.clearRect(0, 0, w, h);
3719
- g.fillStyle = "white";
3720
- g.fillRect(0, 0, w, h);
3721
- if (this._audioData) {
3722
- let spectSize = Math.floor(this.dftSize / 2);
3723
- let chs = this._audioData.numberOfChannels;
3724
- let chH = h / chs;
3725
- let frameLength = this._audioData.getChannelData(0).length;
3726
- let framesPerPixel = frameLength / w;
3727
- let y = 0;
3728
- // TODO
3729
- let b = new Float32Array(this.dftSize);
3730
- let sona = new Array(chs);
3731
- let max = 0;
3732
- let maxPsd = -Infinity;
3733
- for (let ch = 0; ch < chs; ch++) {
3734
- let x = 0;
3735
- sona[ch] = new Array(w);
3736
- let chData = this._audioData.getChannelData(ch);
3737
- // TODO center buffer
3738
- let framePos = 0;
3739
- for (let pii = 0; pii < w; pii++) {
3740
- framePos = Math.round(pii * framesPerPixel);
3741
- // calculate DFT at pixel position
3742
- for (let i = 0; i < this.dftSize; i++) {
3743
- let chDat = chData[framePos + i];
3744
- b[i] = chDat;
3745
- }
3746
- let spectr = this.dft.processRealMagnitude(b);
3747
- sona[ch][pii] = spectr;
3748
- // @ts-ignore
3749
- let pMax = Math.max.apply(null, spectr);
3750
- if (pMax > max) {
3751
- max = pMax;
3752
- }
3753
- for (let s = 0; s < spectSize; s++) {
3754
- let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
3755
- if (psd > maxPsd) {
3756
- maxPsd = psd;
4841
+ g.clearRect(0, 0, w, h);
4842
+ g.fillStyle = "white";
4843
+ g.fillRect(0, 0, w, h);
4844
+ if (this._audioDataHolder) {
4845
+ let spectSize = Math.floor(this.dftSize / 2);
4846
+ let chs = this._audioDataHolder.numberOfChannels;
4847
+ let chH = h / chs;
4848
+ let frameLength = this._audioDataHolder.frameLen;
4849
+ let framesPerPixel = frameLength / w;
4850
+ let y = 0;
4851
+ let audioBuffer = this._audioDataHolder.buffer;
4852
+ let arrayAudioBuffer = this._audioDataHolder.arrayBuffer;
4853
+ if (audioBuffer) {
4854
+ let b = new Float32Array(this.dftSize);
4855
+ let sona = new Array(chs);
4856
+ let max = 0;
4857
+ let maxPsd = -Infinity;
4858
+ for (let ch = 0; ch < chs; ch++) {
4859
+ let x = 0;
4860
+ sona[ch] = new Array(w);
4861
+ let chData = audioBuffer.getChannelData(ch);
4862
+ // TODO center buffer
4863
+ let framePos = 0;
4864
+ for (let pii = 0; pii < w; pii++) {
4865
+ framePos = Math.round(pii * framesPerPixel);
4866
+ // calculate DFT at pixel position
4867
+ for (let i = 0; i < this.dftSize; i++) {
4868
+ let chDat = chData[framePos + i];
4869
+ b[i] = chDat;
3757
4870
  }
3758
- }
3759
- }
3760
- }
3761
- //console.log("max: ", max);
3762
- maxPsd = (2 * Math.pow(max, 2)) / spectSize;
3763
- for (let ch = 0; ch < chs; ch++) {
3764
- let framePos = 0;
3765
- for (let pii = 0; pii < w; pii++) {
3766
- framePos = pii * framesPerPixel;
3767
- for (let y = 0; y < h; y++) {
3768
- let freqIdx = Math.round(y * spectSize / h);
3769
- // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
3770
- // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
3771
- let val = sona[ch][pii][freqIdx];
3772
- let psd = (2 * Math.pow(val, 2)) / spectSize;
3773
- // Calculate logarithmic
3774
- let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
3775
- let dynRangeInDb = 70;
3776
- let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
3777
- if (scaledVal > 1)
3778
- scaledVal = 1;
3779
- if (scaledVal < 0) {
3780
- scaledVal = 0;
4871
+ let spectr = this.dft.processRealMagnitude(b);
4872
+ sona[ch][pii] = spectr;
4873
+ // @ts-ignore
4874
+ let pMax = Math.max.apply(null, spectr);
4875
+ if (pMax > max) {
4876
+ max = pMax;
3781
4877
  }
3782
- let rgbVal = (255 * scaledVal);
3783
- if (rgbVal < 0) {
3784
- // System.out.println("Neg RGB val: "+rgbVal);
3785
- rgbVal = 0;
4878
+ for (let s = 0; s < spectSize; s++) {
4879
+ let psd = (2 * Math.pow(spectr[s], 2)) / spectSize;
4880
+ if (psd > maxPsd) {
4881
+ maxPsd = psd;
4882
+ }
3786
4883
  }
3787
- if (rgbVal > 255) {
3788
- rgbVal = 255;
4884
+ }
4885
+ }
4886
+ //console.log("max: ", max);
4887
+ maxPsd = (2 * Math.pow(max, 2)) / spectSize;
4888
+ for (let ch = 0; ch < chs; ch++) {
4889
+ let framePos = 0;
4890
+ for (let pii = 0; pii < w; pii++) {
4891
+ framePos = pii * framesPerPixel;
4892
+ for (let y = 0; y < h; y++) {
4893
+ let freqIdx = Math.round(y * spectSize / h);
4894
+ // calculate the one sided power spectral density PSD (f, t) in Pa2/Hz
4895
+ // PSD(f) proportional to 2|X(f)|2 / (t2 - t1)
4896
+ let val = sona[ch][pii][freqIdx];
4897
+ let psd = (2 * Math.pow(val, 2)) / spectSize;
4898
+ // Calculate logarithmic
4899
+ let psdLog = DSPUtils.toLevelInDB(psd / maxPsd);
4900
+ let dynRangeInDb = 70;
4901
+ let scaledVal = (psdLog + dynRangeInDb) / dynRangeInDb;
4902
+ if (scaledVal > 1)
4903
+ scaledVal = 1;
4904
+ if (scaledVal < 0) {
4905
+ scaledVal = 0;
4906
+ }
4907
+ let rgbVal = (255 * scaledVal);
4908
+ if (rgbVal < 0) {
4909
+ // System.out.println("Neg RGB val: "+rgbVal);
4910
+ rgbVal = 0;
4911
+ }
4912
+ if (rgbVal > 255) {
4913
+ rgbVal = 255;
4914
+ }
4915
+ rgbVal = 255 - rgbVal;
4916
+ let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
4917
+ g.fillStyle = colorStr;
4918
+ g.fillRect(pii, chH - y, 1, 1);
3789
4919
  }
3790
- rgbVal = 255 - rgbVal;
3791
- let colorStr = CSSUtils.toColorString(rgbVal, rgbVal, rgbVal);
3792
- g.fillStyle = colorStr;
3793
- g.fillRect(pii, chH - y, 1, 1);
3794
4920
  }
3795
4921
  }
4922
+ this.drawPlayPosition();
4923
+ }
4924
+ else if (arrayAudioBuffer) {
4925
+ throw Error("Redraw with array audio buffer not supported.");
3796
4926
  }
3797
- this.drawPlayPosition();
3798
4927
  }
3799
4928
  }
3800
4929
  }
3801
4930
  setData(audioData) {
3802
- this._audioData = audioData;
4931
+ this._audioDataHolder = audioData;
3803
4932
  this.playFramePosition = 0;
3804
- //this.redraw();
3805
- //this.startRender();
3806
4933
  }
3807
4934
  }
3808
4935
  Sonagram.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: Sonagram, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
@@ -4027,13 +5154,13 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4027
5154
  }
4028
5155
  currentXZoom() {
4029
5156
  let xz = this._xZoom;
4030
- if (xz == null && this._audioData) {
5157
+ if (xz == null && this._audioDataHolder) {
4031
5158
  let ow = this.ce.offsetWidth;
4032
5159
  if (ow < 1) {
4033
5160
  // at least one pixel width to avoid x-zoom zero values
4034
5161
  ow = 1;
4035
5162
  }
4036
- xz = ow / this._audioData.duration;
5163
+ xz = ow / this._audioDataHolder.duration;
4037
5164
  }
4038
5165
  return xz;
4039
5166
  }
@@ -4114,7 +5241,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4114
5241
  layout(clear = true) {
4115
5242
  if (this.ce && this.dc) {
4116
5243
  const clientW = this.ce.clientWidth;
4117
- if (this._audioData) {
5244
+ if (this._audioDataHolder) {
4118
5245
  if (this._fixFitToPanel) {
4119
5246
  // Set the virtual canvas width to the visible width
4120
5247
  if (this.ce.style.width != '100%') {
@@ -4125,7 +5252,7 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4125
5252
  else {
4126
5253
  if (this._xZoom) {
4127
5254
  // Set the virtual canvas width according to the value of the user selected xZoom value
4128
- const newClW = Math.round(this._xZoom * this._audioData.duration);
5255
+ const newClW = Math.round(this._xZoom * this._audioDataHolder?.duration);
4129
5256
  this.ce.style.width = newClW + 'px';
4130
5257
  }
4131
5258
  else {
@@ -4137,22 +5264,22 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4137
5264
  this._layout(clear, true);
4138
5265
  }
4139
5266
  }
4140
- set audioData(audioData) {
5267
+ set audioData(audioDataHolder) {
4141
5268
  this._audioClip = null;
4142
- this._audioData = audioData;
4143
- this.as.setData(audioData);
4144
- this.so.setData(audioData);
5269
+ this._audioDataHolder = audioDataHolder;
5270
+ this.as.setData(audioDataHolder);
5271
+ this.so.setData(audioDataHolder);
4145
5272
  this.layout();
4146
5273
  }
4147
5274
  get audioData() {
4148
- return this._audioData;
5275
+ return this._audioDataHolder;
4149
5276
  }
4150
5277
  set audioClip(audioClip) {
4151
5278
  this._audioClip = audioClip;
4152
5279
  let audioData = null;
4153
5280
  let sel = null;
4154
5281
  if (audioClip) {
4155
- audioData = audioClip.buffer;
5282
+ audioData = audioClip.audioDataHolder;
4156
5283
  if (this._audioClip) {
4157
5284
  this._audioClip.addSelectionObserver((clip) => {
4158
5285
  this.selection = clip.selection;
@@ -4160,9 +5287,9 @@ class AudioClipUIContainer extends BasicAudioCanvasLayerComponent {
4160
5287
  }
4161
5288
  sel = audioClip.selection;
4162
5289
  }
4163
- this._audioData = audioData;
4164
- this.as.setData(this._audioData);
4165
- this.so.setData(this._audioData);
5290
+ this._audioDataHolder = audioData;
5291
+ this.as.setData(this._audioDataHolder);
5292
+ this.so.setData(this._audioDataHolder);
4166
5293
  this.selecting = null;
4167
5294
  this.selection = sel;
4168
5295
  this.layout();
@@ -4354,7 +5481,7 @@ class AudioDisplayScrollPane {
4354
5481
  let audioData = null;
4355
5482
  let sel = null;
4356
5483
  if (audioClip) {
4357
- audioData = audioClip.buffer;
5484
+ audioData = audioClip.audioDataHolder;
4358
5485
  sel = audioClip.selection;
4359
5486
  audioClip.addSelectionObserver((clip) => {
4360
5487
  this.zoomSelectedAction.disabled = (clip.selection == null);
@@ -4625,17 +5752,17 @@ class AudioDisplay {
4625
5752
  console.log("Play started");
4626
5753
  this.status = 'Playing...';
4627
5754
  }
4628
- set audioData(audioBuffer) {
4629
- this.audioDisplayScrollPane.audioData = audioBuffer;
5755
+ set audioData(audioData) {
5756
+ this.audioDisplayScrollPane.audioData = audioData;
4630
5757
  if (this.playStartAction) {
4631
- this.playStartAction.disabled = (audioBuffer == null);
5758
+ this.playStartAction.disabled = (audioData == null);
4632
5759
  }
4633
5760
  }
4634
5761
  set audioClip(audioClip) {
4635
5762
  let audioData = null;
4636
5763
  let sel = null;
4637
5764
  if (audioClip) {
4638
- audioData = audioClip.buffer;
5765
+ audioData = audioClip.audioDataHolder.buffer;
4639
5766
  sel = audioClip.selection;
4640
5767
  }
4641
5768
  this._audioClip = audioClip;
@@ -4748,7 +5875,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
4748
5875
 
4749
5876
  class Progress {
4750
5877
  constructor() {
4751
- this.items = null;
5878
+ this.items = undefined;
4752
5879
  this.selectedItemIdx = 0;
4753
5880
  this.enableDownload = false;
4754
5881
  this.onRowSelect = new EventEmitter();
@@ -5488,7 +6615,7 @@ class Prompting {
5488
6615
  constructor() {
5489
6616
  this.promptItem = null;
5490
6617
  this.showPrompt = false;
5491
- this.items = null;
6618
+ this.items = undefined;
5492
6619
  this.enableDownload = false;
5493
6620
  this.audioSignalCollapsed = true;
5494
6621
  this.displayAudioClip = null;
@@ -5799,6 +6926,7 @@ class LevelBar {
5799
6926
  this._streamingMode = false;
5800
6927
  this._staticLevelInfos = null;
5801
6928
  this._playFramePosition = null;
6929
+ this._stateLoading = false;
5802
6930
  this.warnDBLevel = DEFAULT_WARN_DB_LEVEL$1;
5803
6931
  this.dbValues = new Array();
5804
6932
  }
@@ -5844,6 +6972,10 @@ class LevelBar {
5844
6972
  }
5845
6973
  this.layoutStatic();
5846
6974
  }
6975
+ set stateLoading(loading) {
6976
+ this._stateLoading = loading;
6977
+ this.layoutStatic();
6978
+ }
5847
6979
  set channelCount(channelCount) {
5848
6980
  this.reset();
5849
6981
  }
@@ -6024,6 +7156,12 @@ class LevelBar {
6024
7156
  }
6025
7157
  }
6026
7158
  }
7159
+ else if (this._stateLoading) {
7160
+ g.strokeStyle = 'white';
7161
+ g.fillStyle = 'white';
7162
+ g.font = '20px sans-serif';
7163
+ g.fillText("Loading...", 10, 25);
7164
+ }
6027
7165
  }
6028
7166
  }
6029
7167
  }
@@ -6066,7 +7204,7 @@ class LevelBar {
6066
7204
  }
6067
7205
  }
6068
7206
  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: `
7207
+ 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
7208
  <div #virtualCanvas>
6071
7209
  <canvas #levelbar></canvas>
6072
7210
  <canvas #markerCanvas></canvas>
@@ -6123,6 +7261,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
6123
7261
  type: Input
6124
7262
  }], displayLevelInfos: [{
6125
7263
  type: Input
7264
+ }], stateLoading: [{
7265
+ type: Input
6126
7266
  }], onResize: [{
6127
7267
  type: HostListener,
6128
7268
  args: ['window:resize', ['$event']]
@@ -6713,47 +7853,66 @@ class PeakLevelInterceptor {
6713
7853
  }
6714
7854
  }
6715
7855
  class LevelMeasure {
7856
+ //private bufferLevelInfos=new Array<LevelInfo>();
7857
+ //private peakLevelInfo!:LevelInfo;
6716
7858
  constructor() {
6717
7859
  this.worker = null;
6718
7860
  this.workerURL = WorkerHelper.buildWorkerBlobURL(this.workerFunction);
6719
7861
  }
6720
- calcBufferLevelInfos(audioBuffer, bufferTimeLength) {
7862
+ calcBufferLevelInfos(audioDataHolder, bufferTimeLength) {
6721
7863
  return new Promise((resolve) => {
6722
- let chs = audioBuffer.numberOfChannels;
6723
- let bufferFrameLength = Math.round(audioBuffer.sampleRate * bufferTimeLength);
6724
- let buffers = new Array(chs);
7864
+ let chs = audioDataHolder.numberOfChannels;
7865
+ let bufferFrameLength = Math.round(audioDataHolder.sampleRate * bufferTimeLength);
7866
+ let ais = audioDataHolder.audioInputStream();
7867
+ let audioBuffers = new Array(chs);
7868
+ let trBuffers = new Array(chs);
6725
7869
  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;
7870
+ audioBuffers[ch] = new Float32Array(bufferFrameLength);
6730
7871
  }
7872
+ let bufferLevelInfos = new Array();
7873
+ let peakLevelInfo = new LevelInfo(chs);
6731
7874
  this.worker = new Worker(this.workerURL);
6732
7875
  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);
7876
+ if (me.data.linLevelBuffers) {
7877
+ let linLevelArrs = new Array(chs);
7878
+ for (let ch = 0; ch < chs; ch++) {
7879
+ linLevelArrs[ch] = new Float32Array(me.data.linLevelBuffers[ch]);
7880
+ }
7881
+ let bufferCount = Math.ceil(me.data.frameLength / me.data.bufferFrameLength);
7882
+ let framePos = 0;
7883
+ for (let bi = 0; bi < bufferCount; bi++) {
7884
+ let minLevels = new Array(chs);
7885
+ let maxLevels = new Array(chs);
7886
+ for (let ch = 0; ch < chs; ch++) {
7887
+ let linLvlArrPos = bi * 2;
7888
+ minLevels[ch] = linLevelArrs[ch][linLvlArrPos];
7889
+ maxLevels[ch] = linLevelArrs[ch][linLvlArrPos + 1];
7890
+ }
7891
+ let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
7892
+ bufferLevelInfos.push(bli);
7893
+ peakLevelInfo.merge(bli);
7894
+ }
7895
+ }
7896
+ if (me.data.eod === true) {
7897
+ // end of data, terminate worker and return result
7898
+ this.terminateWorker();
7899
+ resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
7900
+ }
7901
+ else {
7902
+ let read = ais.read(audioBuffers);
6744
7903
  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];
7904
+ let copy = new Float32Array(audioBuffers[ch]);
7905
+ trBuffers[ch] = copy.buffer;
6748
7906
  }
6749
- let bli = new LevelInfo(chs, framePos, me.data.bufferFrameLength, minLevels, maxLevels);
6750
- bufferLevelInfos.push(bli);
6751
- peakLevelInfo.merge(bli);
7907
+ this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6752
7908
  }
6753
- this.terminateWorker();
6754
- resolve(new LevelInfos(bufferLevelInfos, peakLevelInfo));
6755
7909
  };
6756
- this.worker.postMessage({ bufferFrameLength: bufferFrameLength, audioData: buffers, chs: chs }, buffers);
7910
+ let read = ais.read(audioBuffers);
7911
+ for (let ch = 0; ch < chs; ch++) {
7912
+ let copy = new Float32Array(audioBuffers[ch]);
7913
+ trBuffers[ch] = copy.buffer;
7914
+ }
7915
+ this.worker?.postMessage({ bufferFrameLength: bufferFrameLength, audioData: trBuffers, chs: chs, len: read }, trBuffers);
6757
7916
  });
6758
7917
  }
6759
7918
  terminateWorker() {
@@ -6764,40 +7923,63 @@ class LevelMeasure {
6764
7923
  */
6765
7924
  workerFunction() {
6766
7925
  self.onmessage = function (msg) {
6767
- let chs = msg.data.chs;
7926
+ let len = msg.data.len;
6768
7927
  let bufferFrameLength = msg.data.bufferFrameLength;
6769
- let audioData = new Array(chs);
6770
- let linLevels = new Array(chs);
6771
- for (let ch = 0; ch < chs; ch++) {
6772
- audioData[ch] = new Float32Array(msg.data.audioData[ch]);
7928
+ if (len == -1) {
7929
+ // start
7930
+ postMessage({
7931
+ eod: false,
7932
+ bufferFrameLength: bufferFrameLength,
7933
+ frameLength: len
7934
+ });
6773
7935
  }
6774
- let frameLength = audioData[0].length;
6775
- let bufferCount = Math.ceil(frameLength / bufferFrameLength);
6776
- for (let ch = 0; ch < chs; ch++) {
6777
- linLevels[ch] = new Float32Array(bufferCount * 2);
7936
+ if (len == 0) {
7937
+ postMessage({
7938
+ eod: true,
7939
+ bufferFrameLength: bufferFrameLength,
7940
+ frameLength: len
7941
+ });
6778
7942
  }
6779
- if (audioData && chs > 0) {
7943
+ else {
7944
+ let chs = msg.data.chs;
7945
+ let audioData = new Array(chs);
7946
+ let linLevels = new Array(chs);
6780
7947
  for (let ch = 0; ch < chs; ch++) {
6781
- let chData = audioData[ch];
6782
- for (let s = 0; s < frameLength; s++) {
6783
- let bi = Math.floor(s / bufferFrameLength);
6784
- let lvlArrPos = bi * 2;
6785
- //let bs = s % bufferFrameLength;
6786
- let chSample = chData[s];
6787
- if (chSample < linLevels[ch][lvlArrPos]) {
6788
- linLevels[ch][lvlArrPos] = chSample;
6789
- }
6790
- lvlArrPos++;
6791
- if (chSample > linLevels[ch][lvlArrPos]) {
6792
- linLevels[ch][lvlArrPos] = chSample;
6793
- }
6794
- }
7948
+ audioData[ch] = new Float32Array(msg.data.audioData[ch]);
6795
7949
  }
6796
- const linLevelBufs = new Array(chs);
7950
+ //let frameLength = audioData[0].length;
7951
+ let bufferCount = Math.ceil(len / bufferFrameLength);
6797
7952
  for (let ch = 0; ch < chs; ch++) {
6798
- linLevelBufs[ch] = linLevels[ch].buffer;
7953
+ linLevels[ch] = new Float32Array(bufferCount * 2);
7954
+ }
7955
+ if (audioData && chs > 0) {
7956
+ for (let ch = 0; ch < chs; ch++) {
7957
+ let chData = audioData[ch];
7958
+ for (let s = 0; s < len; s++) {
7959
+ let bi = Math.floor(s / bufferFrameLength);
7960
+ let lvlArrPos = bi * 2;
7961
+ //let bs = s % bufferFrameLength;
7962
+ let chSample = chData[s];
7963
+ if (chSample < linLevels[ch][lvlArrPos]) {
7964
+ linLevels[ch][lvlArrPos] = chSample;
7965
+ }
7966
+ lvlArrPos++;
7967
+ if (chSample > linLevels[ch][lvlArrPos]) {
7968
+ linLevels[ch][lvlArrPos] = chSample;
7969
+ }
7970
+ }
7971
+ }
7972
+ const linLevelBufs = new Array(chs);
7973
+ for (let ch = 0; ch < chs; ch++) {
7974
+ linLevelBufs[ch] = linLevels[ch].buffer;
7975
+ }
7976
+ postMessage({
7977
+ eod: false,
7978
+ bufferFrameLength: bufferFrameLength,
7979
+ frameLength: len,
7980
+ linLevelBuffers: linLevelBufs
7981
+ }, linLevelBufs);
6799
7982
  }
6800
- postMessage({ bufferFrameLength: bufferFrameLength, frameLength: frameLength, linLevelBuffers: linLevelBufs }, linLevelBufs);
6801
7983
  }
6802
7984
  };
6803
7985
  }
@@ -7169,7 +8351,7 @@ RecordingItemDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", v
7169
8351
  <audio-levelbar fxFlex="1 0 1" [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
7170
8352
  <spr-recordingitemcontrols fxFlex="0 0 0" [audioLoaded]="displayAudioBuffer!==null" [playStartAction]="playStartAction" [playStopAction]="playStopAction" [peakDbLvl]="peakDbLvl" [agc]="_agc" (onShowRecordingDetails)="onShowRecordingDetails.emit()"></spr-recordingitemcontrols>
7171
8353
  </div>
7172
- `, 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"] }] });
8354
+ `, 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"] }] });
7173
8355
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingItemDisplay, decorators: [{
7174
8356
  type: Component,
7175
8357
  args: [{
@@ -7215,89 +8397,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
7215
8397
  type: Input
7216
8398
  }] } });
7217
8399
 
7218
- class Float32ArrayChunkerOutStream {
7219
- constructor(outStream) {
7220
- this.outStream = outStream;
7221
- this.bufs = new Array();
7222
- this._channels = 0;
7223
- this._chunkSize = 0;
7224
- this.filled = 0;
7225
- this.receivedFrames = 0;
7226
- this.sentFrames = 0;
7227
- }
7228
- createBuffers() {
7229
- this.bufs = new Array(this._channels);
7230
- for (let ch = 0; ch < this._channels; ch++) {
7231
- this.bufs[ch] = new Float32Array(this._chunkSize);
7232
- }
7233
- }
7234
- set chunkSize(chunkSize) {
7235
- this._chunkSize = chunkSize;
7236
- this.createBuffers();
7237
- }
7238
- get chunkSize() {
7239
- return this._chunkSize;
7240
- }
7241
- set channels(channels) {
7242
- this._channels = channels;
7243
- this.createBuffers();
7244
- }
7245
- get channels() {
7246
- return this._channels;
7247
- }
7248
- available() {
7249
- return this._chunkSize - this.filled;
7250
- }
7251
- write(buffers) {
7252
- let copied = 0;
7253
- if (buffers.length > 0) {
7254
- let buffersLen = buffers[0].length;
7255
- this.receivedFrames += buffersLen;
7256
- let avail = buffersLen;
7257
- // Fill out buffers until all values copied
7258
- while (avail > 0) {
7259
- let toFill = this._chunkSize - this.filled;
7260
- if (toFill > avail) {
7261
- toFill = avail;
7262
- }
7263
- let sliceEnd = copied + toFill;
7264
- // Firefox on Android sends only the first channel
7265
- for (let ch = 0; ch < buffersLen; ch++) {
7266
- if (buffers[ch]) {
7267
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
7268
- let buf = this.bufs[ch];
7269
- buf.set(cpPrt, this.filled);
7270
- }
7271
- }
7272
- copied += toFill;
7273
- avail -= toFill;
7274
- this.filled += toFill;
7275
- if (this.filled == this._chunkSize) {
7276
- this.outStream.write(this.bufs);
7277
- this.sentFrames += this.filled;
7278
- this.filled = 0;
7279
- }
7280
- }
7281
- }
7282
- return copied;
7283
- }
7284
- flush() {
7285
- if (this.filled > 0) {
7286
- let restBufs = new Array(this._channels);
7287
- for (let ch = 0; ch < this._channels; ch++) {
7288
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
7289
- }
7290
- this.outStream.write(restBufs);
7291
- this.outStream.flush();
7292
- this.sentFrames += this.filled;
7293
- this.filled = 0;
7294
- }
7295
- }
7296
- close() {
7297
- this.outStream.close();
7298
- }
7299
- }
7300
-
7301
8400
  class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
7302
8401
  constructor(outStream, chunkDurationSeconds) {
7303
8402
  super(outStream);
@@ -7306,11 +8405,11 @@ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream
7306
8405
  this.sequenceAudioFloat32OutStream = outStream;
7307
8406
  }
7308
8407
  setFormat(channels, sampleRate) {
7309
- console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: " + channels + ",sampleRate: " + sampleRate + ")");
8408
+ //console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: "+channels+",sampleRate: "+sampleRate+")")
7310
8409
  this.channels = channels;
7311
8410
  this.sampleRate = sampleRate;
7312
8411
  this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
7313
- console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: " + this.chunkSize);
8412
+ //console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: "+this.chunkSize);
7314
8413
  this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
7315
8414
  }
7316
8415
  nextStream() {
@@ -7516,7 +8615,8 @@ class ChunkManager {
7516
8615
  }
7517
8616
  }
7518
8617
  let BasicRecorder = class BasicRecorder {
7519
- constructor(dialog, sessionService, uploader, config) {
8618
+ constructor(changeDetectorRef, dialog, sessionService, uploader, config) {
8619
+ this.changeDetectorRef = changeDetectorRef;
7520
8620
  this.dialog = dialog;
7521
8621
  this.sessionService = sessionService;
7522
8622
  this.uploader = uploader;
@@ -7531,6 +8631,7 @@ let BasicRecorder = class BasicRecorder {
7531
8631
  this._selectedDeviceId = undefined;
7532
8632
  this._channelCount = 2;
7533
8633
  this._session = null;
8634
+ this._recordingFile = null;
7534
8635
  this.startedDate = null;
7535
8636
  this.uploadProgress = 100;
7536
8637
  this.uploadStatus = 'ok';
@@ -7538,6 +8639,7 @@ let BasicRecorder = class BasicRecorder {
7538
8639
  this.peakLevelInDb = MIN_DB_LEVEL;
7539
8640
  this.displayAudioClip = null;
7540
8641
  this.audioFetchSubscription = null;
8642
+ this.audioFetching = false;
7541
8643
  this.destroyed = false;
7542
8644
  this.navigationDisabled = true;
7543
8645
  // Default: Disabled chunked upload
@@ -7631,6 +8733,28 @@ let BasicRecorder = class BasicRecorder {
7631
8733
  this.ac.audioOutStream = outStream;
7632
8734
  }
7633
8735
  }
8736
+ showRecording() {
8737
+ this._controlAudioPlayer.stop();
8738
+ if (this.displayAudioClip) {
8739
+ let dap = this.displayAudioClip;
8740
+ let adh = dap.audioDataHolder;
8741
+ if (adh) {
8742
+ this.levelMeasure.calcBufferLevelInfos(adh, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8743
+ dap.levelInfos = levelInfos;
8744
+ this.changeDetectorRef.detectChanges();
8745
+ });
8746
+ }
8747
+ this.playStartAction.disabled = false;
8748
+ }
8749
+ else {
8750
+ this.playStartAction.disabled = true;
8751
+ // Collapse audio signal display if open
8752
+ if (!this.audioSignalCollapsed) {
8753
+ this.audioSignalCollapsed = true;
8754
+ }
8755
+ }
8756
+ this.changeDetectorRef.detectChanges();
8757
+ }
7634
8758
  start() {
7635
8759
  this.statusAlertType = 'info';
7636
8760
  this.statusMsg = 'Starting session...';
@@ -7851,9 +8975,9 @@ let BasicRecorder = class BasicRecorder {
7851
8975
  }
7852
8976
  this.transportActions.startAction.disabled = true;
7853
8977
  }
7854
- postRecording(wavFile, recUrl) {
8978
+ postRecording(wavFile, recUrl, rf) {
7855
8979
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
7856
- let ul = new Upload(wavBlob, recUrl);
8980
+ let ul = new Upload(wavBlob, recUrl, rf);
7857
8981
  this.uploader.queueUpload(ul);
7858
8982
  }
7859
8983
  postAudioStreamStart() {
@@ -7881,48 +9005,340 @@ let BasicRecorder = class BasicRecorder {
7881
9005
  console.error("Recording file UUID not set!");
7882
9006
  }
7883
9007
  }
7884
- postAudioStreamEnd(chunkCount) {
7885
- if (this.rfUuid) {
7886
- let apiEndPoint = '';
7887
- if (this.config && this.config.apiEndPoint) {
7888
- apiEndPoint = this.config.apiEndPoint;
9008
+ postAudioStreamEnd(chunkCount) {
9009
+ if (this.rfUuid) {
9010
+ let apiEndPoint = '';
9011
+ if (this.config && this.config.apiEndPoint) {
9012
+ apiEndPoint = this.config.apiEndPoint;
9013
+ }
9014
+ if (apiEndPoint !== '') {
9015
+ apiEndPoint = apiEndPoint + '/';
9016
+ }
9017
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9018
+ let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
9019
+ let fd = new FormData();
9020
+ fd.set('uuid', this.rfUuid);
9021
+ fd.set('chunkCount', chunkCount.toString());
9022
+ let ul = new Upload(fd, recUrl, this._recordingFile);
9023
+ this.uploader.queueUpload(ul);
9024
+ console.debug("Queued for upload: " + this._recordingFile);
9025
+ }
9026
+ else {
9027
+ console.error("Recording file UUID not set!");
9028
+ }
9029
+ }
9030
+ closed() {
9031
+ this.statusAlertType = 'info';
9032
+ this.statusMsg = 'Session closed.';
9033
+ }
9034
+ error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
9035
+ this.statusMsg = 'ERROR: Recording.';
9036
+ this.statusAlertType = 'error';
9037
+ this.dialog.open(MessageDialog, {
9038
+ data: {
9039
+ type: 'error',
9040
+ title: 'Recording error',
9041
+ msg: msg,
9042
+ advice: advice
9043
+ }
9044
+ });
9045
+ }
9046
+ };
9047
+ // Enable only for developemnt/debug purposes of array audio buffers !!
9048
+ BasicRecorder.FORCE_ARRRAY_AUDIO_BUFFER = false;
9049
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
9050
+ BasicRecorder = __decorate([
9051
+ __param(4, Inject(SPEECHRECORDER_CONFIG))
9052
+ ], BasicRecorder);
9053
+
9054
+ class AudioDataHolder {
9055
+ constructor(_buffer, _arrayBuffer = null) {
9056
+ this._buffer = _buffer;
9057
+ this._arrayBuffer = _arrayBuffer;
9058
+ this._numberOfChannels = 0;
9059
+ this._sampleRate = 0;
9060
+ this._frameLen = 0;
9061
+ this._duration = 0;
9062
+ if (this._buffer && this._arrayBuffer) {
9063
+ throw Error('Only one of either audio buffer or array audio buffer must be set!');
9064
+ }
9065
+ if (this._buffer || this._arrayBuffer) {
9066
+ if (this._buffer) {
9067
+ this._numberOfChannels = this._buffer.numberOfChannels;
9068
+ this._sampleRate = this._buffer.sampleRate;
9069
+ this._frameLen = this._buffer.length;
9070
+ this._duration = this._frameLen / this._sampleRate;
9071
+ }
9072
+ else if (this._arrayBuffer) {
9073
+ this._numberOfChannels = this._arrayBuffer.channelCount;
9074
+ this._sampleRate = this._arrayBuffer.sampleRate;
9075
+ this._frameLen = this._arrayBuffer.frameLen;
9076
+ this._duration = this._frameLen / this._sampleRate;
9077
+ }
9078
+ }
9079
+ else {
9080
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9081
+ }
9082
+ }
9083
+ get duration() {
9084
+ return this._duration;
9085
+ }
9086
+ get sampleRate() {
9087
+ return this._sampleRate;
9088
+ }
9089
+ get numberOfChannels() {
9090
+ return this._numberOfChannels;
9091
+ }
9092
+ get frameLen() {
9093
+ return this._frameLen;
9094
+ }
9095
+ sampleCounts() {
9096
+ return this._numberOfChannels * this._frameLen;
9097
+ }
9098
+ frames(framePos, frameLen, bufs) {
9099
+ let read = 0;
9100
+ if (this._buffer) {
9101
+ let toRead = frameLen;
9102
+ if (this._buffer.length < framePos + frameLen) {
9103
+ toRead = this._buffer.length - framePos;
9104
+ }
9105
+ for (let ch = 0; ch < bufs.length; ch++) {
9106
+ let chData = this._buffer.getChannelData(ch);
9107
+ for (let i = 0; i < toRead; i++) {
9108
+ bufs[ch][i] = chData[framePos + i];
9109
+ }
9110
+ }
9111
+ read = toRead;
9112
+ }
9113
+ else if (this._arrayBuffer) {
9114
+ read = this._arrayBuffer.frames(framePos, frameLen, bufs);
9115
+ }
9116
+ return read;
9117
+ }
9118
+ audioInputStream() {
9119
+ if (this._buffer) {
9120
+ return new AudioBufferInputStream(this._buffer);
9121
+ }
9122
+ if (this._arrayBuffer) {
9123
+ return new ArrayAudioBufferInputStream(this._arrayBuffer);
9124
+ }
9125
+ throw Error(AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG);
9126
+ }
9127
+ get buffer() {
9128
+ return this._buffer;
9129
+ }
9130
+ get arrayBuffer() {
9131
+ return this._arrayBuffer;
9132
+ }
9133
+ }
9134
+ AudioDataHolder.ONE_OF_MUST_BE_SET_ERR_MSG = 'One of either audio buffer or array audio buffer must be set!';
9135
+ class AudioBufferInputStream {
9136
+ constructor(audioBuffer) {
9137
+ this.audioBuffer = audioBuffer;
9138
+ this.framePos = 0;
9139
+ }
9140
+ close() {
9141
+ }
9142
+ skipFrames(n) {
9143
+ this.framePos += n;
9144
+ }
9145
+ read(buffers) {
9146
+ let read = 0;
9147
+ let toRead = buffers[0].length;
9148
+ if (this.framePos + toRead > this.audioBuffer.length) {
9149
+ toRead = this.audioBuffer.length - this.framePos;
9150
+ }
9151
+ for (let ch = 0; ch < buffers.length; ch++) {
9152
+ for (let i = 0; i < toRead; i++) {
9153
+ buffers[ch][i] = this.audioBuffer.getChannelData(ch)[this.framePos + i];
9154
+ }
9155
+ }
9156
+ read = toRead;
9157
+ this.framePos += toRead;
9158
+ return read;
9159
+ }
9160
+ }
9161
+
9162
+ class BasicRecFilesCache {
9163
+ constructor() {
9164
+ //public static readonly DEFAULT_MAX_SAMPLES=30*48000; // TEST 30 seconds
9165
+ this._sampleCount = 0;
9166
+ this.maxSampleCount = SprItemsCache.DEFAULT_MAX_SAMPLES;
9167
+ this.currentRecordingFile = null;
9168
+ }
9169
+ }
9170
+ BasicRecFilesCache.DEBUG = false;
9171
+ BasicRecFilesCache.DEFAULT_MAX_SAMPLES = 10 * 60 * 48000; // 20 Minutes mono 48kHz
9172
+ class SprItemsCache extends BasicRecFilesCache {
9173
+ constructor() {
9174
+ super(...arguments);
9175
+ this._items = new Array();
9176
+ }
9177
+ get items() {
9178
+ return this._items;
9179
+ }
9180
+ addItem(item) {
9181
+ this._items.push(item);
9182
+ }
9183
+ getItem(index) {
9184
+ return this._items[index];
9185
+ }
9186
+ length() {
9187
+ return this._items.length;
9188
+ }
9189
+ tryExpire(toBeExpiredRf) {
9190
+ if (BasicRecFilesCache.DEBUG)
9191
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " try expire:");
9192
+ if (!toBeExpiredRf.equals(this.currentRecordingFile)) {
9193
+ if (BasicRecFilesCache.DEBUG)
9194
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " not current file...");
9195
+ if (toBeExpiredRf.serverPersisted) {
9196
+ if (BasicRecFilesCache.DEBUG)
9197
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is server persisted...");
9198
+ // expire recording files first stored to the cache
9199
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9200
+ if (expiredAudio) {
9201
+ this._sampleCount -= expiredAudio.sampleCounts();
9202
+ if (BasicRecFilesCache.DEBUG)
9203
+ console.debug("Rec. files cache: Expired: " + toBeExpiredRf.toString() + ". Cache samples now: " + this._sampleCount);
9204
+ }
7889
9205
  }
7890
- if (apiEndPoint !== '') {
7891
- apiEndPoint = apiEndPoint + '/';
9206
+ else {
9207
+ if (BasicRecFilesCache.DEBUG)
9208
+ console.debug("Rec. files cache: #" + toBeExpiredRf.toString() + " not yet persisted on server.");
7892
9209
  }
7893
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
7894
- let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
7895
- let fd = new FormData();
7896
- fd.set('uuid', this.rfUuid);
7897
- fd.set('chunkCount', chunkCount.toString());
7898
- let ul = new Upload(fd, recUrl);
7899
- this.uploader.queueUpload(ul);
7900
9210
  }
7901
9211
  else {
7902
- console.error("Recording file UUID not set!");
9212
+ if (BasicRecFilesCache.DEBUG)
9213
+ console.debug("Rec. files cache: " + toBeExpiredRf.toString() + " is current file.");
9214
+ }
9215
+ }
9216
+ expire() {
9217
+ // expire corrected versions first
9218
+ if (BasicRecFilesCache.DEBUG)
9219
+ console.debug("Rec. files cache: Expire? current: " + this._sampleCount + ", max: " + this.maxSampleCount);
9220
+ if (this._sampleCount > this.maxSampleCount) {
9221
+ // expire older versions of an item first
9222
+ for (let ii = 0; ii < this._items.length; ii++) {
9223
+ if (this._sampleCount <= this.maxSampleCount) {
9224
+ break;
9225
+ }
9226
+ let it = this._items[ii];
9227
+ let itRfs = it.recs;
9228
+ if (itRfs && itRfs.length > 1) {
9229
+ for (let rfi = 0; rfi < itRfs.length - 1; rfi++) {
9230
+ if (this._sampleCount <= this.maxSampleCount) {
9231
+ break;
9232
+ }
9233
+ let toBeExpiredRf = itRfs[rfi];
9234
+ this.tryExpire(toBeExpiredRf);
9235
+ }
9236
+ }
9237
+ }
9238
+ // if that's not sufficient expire audio data of latest versions as well
9239
+ for (let ii = 0; ii < this._items.length; ii++) {
9240
+ if (this._sampleCount <= this.maxSampleCount) {
9241
+ break;
9242
+ }
9243
+ let it = this._items[ii];
9244
+ let itRfs = it.recs;
9245
+ if (itRfs && itRfs.length > 0) {
9246
+ for (let rfi = 0; rfi < itRfs.length; rfi++) {
9247
+ if (this._sampleCount <= this.maxSampleCount) {
9248
+ break;
9249
+ }
9250
+ let toBeExpiredRf = itRfs[rfi];
9251
+ this.tryExpire(toBeExpiredRf);
9252
+ }
9253
+ }
9254
+ }
7903
9255
  }
7904
9256
  }
7905
- closed() {
7906
- this.statusAlertType = 'info';
7907
- this.statusMsg = 'Session closed.';
9257
+ addSprRecFile(item, sprRecFile) {
9258
+ this.expire();
9259
+ if (!item.recs) {
9260
+ item.recs = new Array();
9261
+ }
9262
+ item.recs.push(sprRecFile);
9263
+ this._sampleCount += RecordingFileUtils.sampleCount(sprRecFile);
9264
+ if (BasicRecFilesCache.DEBUG)
9265
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
7908
9266
  }
7909
- error(msg = 'An unknown error occured during recording.', advice = 'Please retry.') {
7910
- this.statusMsg = 'ERROR: Recording.';
7911
- this.statusAlertType = 'error';
7912
- this.dialog.open(MessageDialog, {
7913
- data: {
7914
- type: 'error',
7915
- title: 'Recording error',
7916
- msg: msg,
7917
- advice: advice
9267
+ setSprRecFileAudioData(sprRecFile, adh) {
9268
+ this.expire();
9269
+ if (BasicRecFilesCache.DEBUG)
9270
+ console.debug("Rec. files cache: Set audio data after expire. Cache samples: " + this._sampleCount);
9271
+ let currSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9272
+ this._sampleCount -= currSampleCnt;
9273
+ if (BasicRecFilesCache.DEBUG)
9274
+ console.debug("Rec. files cache: Set audio data subtracted curr sample count: " + currSampleCnt + ". Cache samples: " + this._sampleCount);
9275
+ RecordingFileUtils.setAudioData(sprRecFile, adh);
9276
+ let newSampleCnt = RecordingFileUtils.sampleCount(sprRecFile);
9277
+ this._sampleCount += newSampleCnt;
9278
+ if (BasicRecFilesCache.DEBUG)
9279
+ console.debug("Rec. files cache: Set audio data added new sample count: " + newSampleCnt + ". Cache samples: " + this._sampleCount);
9280
+ let fl = adh?.frameLen;
9281
+ if (fl) {
9282
+ sprRecFile.frames = fl;
9283
+ }
9284
+ if (BasicRecFilesCache.DEBUG)
9285
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9286
+ }
9287
+ }
9288
+ class RecFilesCache extends BasicRecFilesCache {
9289
+ constructor() {
9290
+ super(...arguments);
9291
+ this._recFiles = new Array();
9292
+ }
9293
+ get recFiles() {
9294
+ return this._recFiles;
9295
+ }
9296
+ getRecFile(index) {
9297
+ return this._recFiles[index];
9298
+ }
9299
+ length() {
9300
+ return this._recFiles.length;
9301
+ }
9302
+ expire() {
9303
+ if (this._sampleCount > this.maxSampleCount) {
9304
+ // audio recorder list is sorted: lower index is newer
9305
+ for (let rfI = this._recFiles.length - 1; rfI >= 0; rfI--) {
9306
+ if (this._sampleCount <= this.maxSampleCount) {
9307
+ break;
9308
+ }
9309
+ let toBeExpiredRf = this._recFiles[rfI];
9310
+ if (toBeExpiredRf.serverPersisted) {
9311
+ // expire recording files first stored to the cache
9312
+ let expiredAudio = RecordingFileUtils.expireAudioData(toBeExpiredRf);
9313
+ if (expiredAudio) {
9314
+ this._sampleCount -= expiredAudio.sampleCounts();
9315
+ if (BasicRecFilesCache.DEBUG)
9316
+ console.debug("Rec. files cache: Expired #" + rfI + ". Cache samples: " + this._sampleCount);
9317
+ }
9318
+ }
9319
+ else {
9320
+ if (BasicRecFilesCache.DEBUG)
9321
+ console.debug("Rec. files cache: #" + rfI + " not yet server persisted.");
9322
+ }
7918
9323
  }
7919
- });
9324
+ }
7920
9325
  }
7921
- };
7922
- BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7923
- BasicRecorder = __decorate([
7924
- __param(3, Inject(SPEECHRECORDER_CONFIG))
7925
- ], BasicRecorder);
9326
+ addRecFile(recFile) {
9327
+ this.expire();
9328
+ this._recFiles.push(recFile);
9329
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9330
+ if (BasicRecFilesCache.DEBUG)
9331
+ console.debug("Rec. files cache: Added. Cache samples: " + this._sampleCount);
9332
+ }
9333
+ setRecFileAudioData(recFile, adh) {
9334
+ this.expire();
9335
+ this._sampleCount -= RecordingFileUtils.sampleCount(recFile);
9336
+ RecordingFileUtils.setAudioData(recFile, adh);
9337
+ this._sampleCount += RecordingFileUtils.sampleCount(recFile);
9338
+ if (BasicRecFilesCache.DEBUG)
9339
+ console.debug("Rec. files cache: Set audio data. Cache samples: " + this._sampleCount);
9340
+ }
9341
+ }
7926
9342
 
7927
9343
  /**
7928
9344
  * Created by klausj on 17.06.2017.
@@ -8015,16 +9431,65 @@ class RecordingService {
8015
9431
  let recUrl = recFilesUrl + '/' + encItemcode + '/' + version;
8016
9432
  return this.audioRequest(recUrl);
8017
9433
  }
9434
+ fetchRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9435
+ let wobs = new Observable(observer => {
9436
+ let recFileId = recordingFile.recordingFileId;
9437
+ if (!recFileId) {
9438
+ recFileId = recordingFile.uuid;
9439
+ }
9440
+ if (recordingFile.session && recFileId) {
9441
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
9442
+ obs.subscribe({
9443
+ next: resp => {
9444
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9445
+ if (resp.body) {
9446
+ aCtx.decodeAudioData(resp.body, ab => {
9447
+ observer.next(ab);
9448
+ observer.complete();
9449
+ }, error => {
9450
+ observer.error(error);
9451
+ observer.complete();
9452
+ });
9453
+ }
9454
+ else {
9455
+ observer.error('Fetching audio file: response has no body');
9456
+ }
9457
+ },
9458
+ error: (error) => {
9459
+ if (error.status == 404) {
9460
+ // Interpret not as an error, the file ist not recorded yet
9461
+ observer.next(null);
9462
+ observer.complete();
9463
+ }
9464
+ else {
9465
+ // all other states are errors
9466
+ observer.error(error);
9467
+ observer.complete();
9468
+ }
9469
+ }
9470
+ });
9471
+ }
9472
+ else {
9473
+ observer.error();
9474
+ }
9475
+ });
9476
+ return wobs;
9477
+ }
8018
9478
  fetchAndApplyRecordingFile(aCtx, projectName, recordingFile) {
8019
9479
  let wobs = new Observable(observer => {
8020
- if (recordingFile.session && recordingFile.recordingFileId) {
8021
- let obs = this.fetchAudiofile(projectName, recordingFile.session, recordingFile.recordingFileId);
9480
+ let recFileId = recordingFile.recordingFileId;
9481
+ if (!recFileId) {
9482
+ recFileId = recordingFile.uuid;
9483
+ }
9484
+ if (recordingFile.session && recFileId) {
9485
+ let obs = this.fetchAudiofile(projectName, recordingFile.session, recFileId);
8022
9486
  obs.subscribe(resp => {
8023
9487
  //console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
8024
9488
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8025
9489
  if (resp.body) {
8026
9490
  aCtx.decodeAudioData(resp.body, ab => {
8027
- recordingFile.audioBuffer = ab;
9491
+ let adh = new AudioDataHolder(ab, null);
9492
+ RecordingFileUtils.setAudioData(recordingFile, adh);
8028
9493
  if (this.debugDelay > 0) {
8029
9494
  window.setTimeout(() => {
8030
9495
  observer.next(recordingFile);
@@ -8062,6 +9527,47 @@ class RecordingService {
8062
9527
  });
8063
9528
  return wobs;
8064
9529
  }
9530
+ fetchSprRecordingFileAudioBuffer(aCtx, projectName, recordingFile) {
9531
+ let wobs = new Observable(observer => {
9532
+ if (recordingFile.session) {
9533
+ let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
9534
+ obs.subscribe({
9535
+ next: resp => {
9536
+ // Do not use Promise version, which does not work with Safari 13 (13.0.5)
9537
+ if (resp.body) {
9538
+ aCtx.decodeAudioData(resp.body, ab => {
9539
+ //RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
9540
+ observer.next(ab);
9541
+ observer.complete();
9542
+ }, error => {
9543
+ observer.error(error);
9544
+ observer.complete();
9545
+ });
9546
+ }
9547
+ else {
9548
+ observer.error('Fetching audio file: response has no body');
9549
+ }
9550
+ },
9551
+ error: (error) => {
9552
+ if (error.status == 404) {
9553
+ // Interpret not as an error, the file ist not recorded yet
9554
+ observer.next(null);
9555
+ observer.complete();
9556
+ }
9557
+ else {
9558
+ // all other states are errors
9559
+ observer.error(error);
9560
+ observer.complete();
9561
+ }
9562
+ }
9563
+ });
9564
+ }
9565
+ else {
9566
+ observer.error();
9567
+ }
9568
+ });
9569
+ return wobs;
9570
+ }
8065
9571
  fetchAndApplySprRecordingFile(aCtx, projectName, recordingFile) {
8066
9572
  let wobs = new Observable(observer => {
8067
9573
  if (recordingFile.session) {
@@ -8071,7 +9577,7 @@ class RecordingService {
8071
9577
  // Do not use Promise version, which does not work with Safari 13 (13.0.5)
8072
9578
  if (resp.body) {
8073
9579
  aCtx.decodeAudioData(resp.body, ab => {
8074
- recordingFile.audioBuffer = ab;
9580
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab, null));
8075
9581
  if (this.debugDelay > 0) {
8076
9582
  window.setTimeout(() => {
8077
9583
  observer.next(recordingFile);
@@ -8116,7 +9622,8 @@ class RecordingService {
8116
9622
  // Do not use Promise version, which does not work with Safari 13
8117
9623
  if (resp.body) {
8118
9624
  aCtx.decodeAudioData(resp.body, ab => {
8119
- let rf = new SprRecordingFile(sessId, itemcode, version, ab);
9625
+ let adh = new AudioDataHolder(ab, null);
9626
+ let rf = new SprRecordingFile(sessId, itemcode, version, adh);
8120
9627
  if (this.debugDelay > 0) {
8121
9628
  window.setTimeout(() => {
8122
9629
  observer.next(rf);
@@ -8232,14 +9739,9 @@ const DEFAULT_PRE_REC_DELAY = 1000;
8232
9739
  const DEFAULT_POST_REC_DELAY = 500;
8233
9740
  class SessionManager extends BasicRecorder {
8234
9741
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
8235
- super(dialog, sessionService, uploader, config);
8236
- this.changeDetectorRef = changeDetectorRef;
9742
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
8237
9743
  this.renderer = renderer;
8238
- this.dialog = dialog;
8239
- this.sessionService = sessionService;
8240
9744
  this.recFileService = recFileService;
8241
- this.uploader = uploader;
8242
- this.config = config;
8243
9745
  this.enableUploadRecordings = true;
8244
9746
  this.enableDownloadRecordings = false;
8245
9747
  this.status = 0 /* BLOCKED */;
@@ -8273,7 +9775,7 @@ class SessionManager extends BasicRecorder {
8273
9775
  };
8274
9776
  }
8275
9777
  ngOnDestroy() {
8276
- console.debug("Com destroy, disable wake lock.");
9778
+ //console.debug("Com destroy, disable wake lock.")
8277
9779
  this.disableWakeLockCond();
8278
9780
  this.destroyed = true;
8279
9781
  // TODO stop capture /playback
@@ -8404,7 +9906,7 @@ class SessionManager extends BasicRecorder {
8404
9906
  progressPercentValue() {
8405
9907
  let v = 100;
8406
9908
  if (this.items) {
8407
- v = this.promptIndex * 100 / (this.items.length - 1);
9909
+ v = this.promptIndex * 100 / (this.items.length() - 1);
8408
9910
  }
8409
9911
  return v;
8410
9912
  }
@@ -8418,9 +9920,6 @@ class SessionManager extends BasicRecorder {
8418
9920
  return ((this._session != null) && (this._session.type === 'TEST_DEF_A') && (this.audioDevices != null) && this.audioDevices.length > 0);
8419
9921
  }
8420
9922
  set controlAudioPlayer(controlAudioPlayer) {
8421
- if (this._controlAudioPlayer) {
8422
- //this._controlAudioPlayer.listener=null;
8423
- }
8424
9923
  this._controlAudioPlayer = controlAudioPlayer;
8425
9924
  if (this._controlAudioPlayer) {
8426
9925
  this._controlAudioPlayer.listener = this;
@@ -8505,6 +10004,8 @@ class SessionManager extends BasicRecorder {
8505
10004
  this.autorecording = true;
8506
10005
  }
8507
10006
  if (this.ac != null) {
10007
+ // Hide loading hint on livelevel display
10008
+ this.audioFetching = false;
8508
10009
  if (!this.ac.opened) {
8509
10010
  if (this._selectedDeviceId) {
8510
10011
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -8521,7 +10022,7 @@ class SessionManager extends BasicRecorder {
8521
10022
  }
8522
10023
  loadScript() {
8523
10024
  this.promptItemCount = 0;
8524
- this.items = new Array();
10025
+ this.items = new SprItemsCache();
8525
10026
  let ln = 0;
8526
10027
  //TODO randomize not supported
8527
10028
  for (let si = 0; si < this._script.sections.length; si++) {
@@ -8535,7 +10036,7 @@ class SessionManager extends BasicRecorder {
8535
10036
  let pi = pis[piSectIdx];
8536
10037
  let promptAsStr = PromptitemUtil.toPlainTextString(pi);
8537
10038
  let it = new Item$1(promptAsStr, section.training, (!pi.type || pi.type === 'recording'));
8538
- this.items.push(it);
10039
+ this.items.addItem(it);
8539
10040
  ln++;
8540
10041
  }
8541
10042
  }
@@ -8576,10 +10077,10 @@ class SessionManager extends BasicRecorder {
8576
10077
  }
8577
10078
  downloadRecording() {
8578
10079
  if (this.displayRecFile) {
8579
- let ab = this.displayRecFile.audioBuffer;
10080
+ let ab = this.displayRecFile.audioDataHolder;
8580
10081
  let ww = new WavWriter();
8581
- if (ab) {
8582
- let wavFile = ww.writeAsync(ab, (wavFile) => {
10082
+ if (ab?.buffer) {
10083
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
8583
10084
  let blob = new Blob([wavFile], { type: 'audio/wav' });
8584
10085
  let rfUrl = URL.createObjectURL(blob);
8585
10086
  let dataDnlLnk = this.renderer.createElement('a');
@@ -8602,7 +10103,10 @@ class SessionManager extends BasicRecorder {
8602
10103
  set displayRecFile(displayRecFile) {
8603
10104
  this._displayRecFile = displayRecFile;
8604
10105
  if (this._displayRecFile) {
8605
- let ab = this._displayRecFile.audioBuffer;
10106
+ if (this.items) {
10107
+ this.items.currentRecordingFile = this._displayRecFile;
10108
+ }
10109
+ let ab = this._displayRecFile.audioDataHolder;
8606
10110
  if (ab) {
8607
10111
  this.displayAudioClip = new AudioClip(ab);
8608
10112
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
@@ -8610,27 +10114,46 @@ class SessionManager extends BasicRecorder {
8610
10114
  else {
8611
10115
  // clear for now ...
8612
10116
  this.displayAudioClip = null;
8613
- this.controlAudioPlayer.audioClip = null;
10117
+ if (this.controlAudioPlayer) {
10118
+ this.controlAudioPlayer.audioClip = null;
10119
+ }
8614
10120
  if (this._controlAudioPlayer && this._session) {
8615
10121
  //... and try to fetch from server
8616
- this.audioFetchSubscription = this.recFileService.fetchAndApplySprRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
8617
- let fab = null;
8618
- if (rf && this._displayRecFile) {
8619
- fab = this._displayRecFile.audioBuffer;
8620
- }
8621
- else {
8622
- this.statusMsg = 'Recording file could not be loaded.';
10122
+ this.audioFetching = true;
10123
+ let rf = this._displayRecFile;
10124
+ this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
10125
+ next: (ab) => {
10126
+ this.audioFetching = false;
10127
+ let fabDh = null;
10128
+ if (ab) {
10129
+ if (rf && this.items) {
10130
+ if (SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10131
+ let aab = ArrayAudioBuffer.fromAudioBuffer(ab);
10132
+ fabDh = new AudioDataHolder(null, aab);
10133
+ }
10134
+ else {
10135
+ fabDh = new AudioDataHolder(ab);
10136
+ }
10137
+ this.items.setSprRecFileAudioData(rf, fabDh);
10138
+ }
10139
+ }
10140
+ else {
10141
+ // Should actually be handled by the error resolver
10142
+ this.statusMsg = 'Recording file could not be loaded.';
10143
+ this.statusAlertType = 'error';
10144
+ }
10145
+ if (fabDh) {
10146
+ // 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
10147
+ this.displayAudioClip = new AudioClip(fabDh);
10148
+ }
10149
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
10150
+ this.showRecording();
10151
+ }, error: err => {
10152
+ console.error("Could not load recording file from server: " + err);
10153
+ this.audioFetching = false;
10154
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
8623
10155
  this.statusAlertType = 'error';
8624
10156
  }
8625
- if (fab) {
8626
- this.displayAudioClip = new AudioClip(fab);
8627
- }
8628
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
8629
- this.showRecording();
8630
- }, err => {
8631
- console.error("Could not load recording file from server: " + err);
8632
- this.statusMsg = 'Recording file could not be loaded: ' + err;
8633
- this.statusAlertType = 'error';
8634
10157
  });
8635
10158
  }
8636
10159
  else {
@@ -8641,31 +10164,14 @@ class SessionManager extends BasicRecorder {
8641
10164
  }
8642
10165
  else {
8643
10166
  this.displayAudioClip = null;
8644
- this.controlAudioPlayer.audioClip = null;
10167
+ if (this.controlAudioPlayer) {
10168
+ this.controlAudioPlayer.audioClip = null;
10169
+ }
8645
10170
  }
8646
10171
  }
8647
10172
  get displayRecFile() {
8648
10173
  return this._displayRecFile;
8649
10174
  }
8650
- showRecording() {
8651
- this.controlAudioPlayer.stop();
8652
- if (this.displayAudioClip) {
8653
- let dap = this.displayAudioClip;
8654
- this.levelMeasure.calcBufferLevelInfos(dap.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
8655
- dap.levelInfos = levelInfos;
8656
- this.changeDetectorRef.detectChanges();
8657
- });
8658
- this.playStartAction.disabled = false;
8659
- }
8660
- else {
8661
- this.playStartAction.disabled = true;
8662
- // Collapse audio signal display if open
8663
- if (!this.audioSignalCollapsed) {
8664
- this.audioSignalCollapsed = true;
8665
- }
8666
- }
8667
- this.changeDetectorRef.detectChanges();
8668
- }
8669
10175
  isRecordingItem() {
8670
10176
  return (this.promptItem != null && this.promptItem.type !== 'nonrecording');
8671
10177
  }
@@ -8680,6 +10186,7 @@ class SessionManager extends BasicRecorder {
8680
10186
  if (this.audioFetchSubscription) {
8681
10187
  this.audioFetchSubscription.unsubscribe();
8682
10188
  }
10189
+ this.audioFetching = false;
8683
10190
  this.clearPrompt();
8684
10191
  let isNonrecording = (this.promptItem.type === 'nonrecording');
8685
10192
  if (isNonrecording || !this.section.promptphase || this.section.promptphase === 'IDLE') {
@@ -8692,7 +10199,7 @@ class SessionManager extends BasicRecorder {
8692
10199
  }
8693
10200
  else {
8694
10201
  if (this.items) {
8695
- let it = this.items[this.promptIndex];
10202
+ let it = this.items.getItem(this.promptIndex);
8696
10203
  if (!it.recs) {
8697
10204
  it.recs = new Array();
8698
10205
  }
@@ -8753,7 +10260,7 @@ class SessionManager extends BasicRecorder {
8753
10260
  updateNavigationActions() {
8754
10261
  let fwdNxtActionDisabled = true;
8755
10262
  if (this.items) {
8756
- let currRecs = this.items[this._promptIndex].recs;
10263
+ let currRecs = this.items.getItem(this._promptIndex).recs;
8757
10264
  fwdNxtActionDisabled = !(currRecs != null && currRecs.length > 0);
8758
10265
  }
8759
10266
  this.transportActions.fwdNextAction.disabled = this.navigationDisabled || fwdNxtActionDisabled;
@@ -8767,10 +10274,10 @@ class SessionManager extends BasicRecorder {
8767
10274
  newPrIdx = 0;
8768
10275
  }
8769
10276
  if (this.items != null) {
8770
- let itRecs = this.items[newPrIdx].recs;
10277
+ let itRecs = this.items.getItem(newPrIdx).recs;
8771
10278
  while (itRecs != null && (itRecs.length > 0) && newPrIdx < this.promptItemCount) {
8772
10279
  newPrIdx++;
8773
- itRecs = this.items[newPrIdx].recs;
10280
+ itRecs = this.items.getItem(newPrIdx).recs;
8774
10281
  }
8775
10282
  if (!itRecs || itRecs.length == 0) {
8776
10283
  this.promptIndex = newPrIdx;
@@ -8791,6 +10298,23 @@ class SessionManager extends BasicRecorder {
8791
10298
  }
8792
10299
  }
8793
10300
  started() {
10301
+ let ic = this.promptItem.itemcode;
10302
+ let rf = null;
10303
+ if (this._session && ic) {
10304
+ let sessId = this._session.sessionId;
10305
+ let cpIdx = this.promptIndex;
10306
+ if (this.items) {
10307
+ let it = this.items.getItem(cpIdx);
10308
+ if (!it.recs) {
10309
+ it.recs = new Array();
10310
+ }
10311
+ rf = new SprRecordingFile(sessId, ic, it.recs.length, null);
10312
+ //it.recs.push(rf);
10313
+ this.items.addSprRecFile(it, rf);
10314
+ this.items.currentRecordingFile = rf;
10315
+ }
10316
+ }
10317
+ this._recordingFile = rf;
8794
10318
  this.status = 3 /* PRE_RECORDING */;
8795
10319
  super.started();
8796
10320
  this.startStopSignalState = 1 /* PRERECORDING */;
@@ -8917,13 +10441,14 @@ class SessionManager extends BasicRecorder {
8917
10441
  if (rfd.recording && rfd.recording.itemcode) {
8918
10442
  let prIdx = this.promptIndexByItemcode(rfd.recording.itemcode);
8919
10443
  if (this.items != null && prIdx !== null) {
8920
- let it = this.items[prIdx];
10444
+ let it = this.items.getItem(prIdx);
8921
10445
  if (it && this._session) {
8922
- if (!it.recs) {
8923
- it.recs = new Array();
8924
- }
10446
+ // if (!it.recs) {
10447
+ // it.recs = new Array<SprRecordingFile>();
10448
+ // }
8925
10449
  let rf = new SprRecordingFile(this._session.sessionId, rfd.recording.itemcode, rfd.version, null);
8926
- it.recs[rfd.version] = rf;
10450
+ rf.serverPersisted = true;
10451
+ this.items.addSprRecFile(it, rf);
8927
10452
  }
8928
10453
  else {
8929
10454
  //console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
@@ -8945,22 +10470,24 @@ class SessionManager extends BasicRecorder {
8945
10470
  this.statusMsg = 'Recorded.';
8946
10471
  this.startStopSignalState = 0 /* IDLE */;
8947
10472
  let ad = null;
10473
+ let ada = null;
10474
+ let adh = null;
10475
+ let frameLen = 0;
8948
10476
  if (this.ac != null) {
8949
- ad = this.ac.audioBuffer();
8950
- }
8951
- let ic = this.promptItem.itemcode;
8952
- let rf = null;
8953
- if (this._session && ic) {
8954
- let sessId = this._session.sessionId;
8955
- let cpIdx = this.promptIndex;
8956
- if (this.items) {
8957
- let it = this.items[cpIdx];
8958
- if (!it.recs) {
8959
- it.recs = new Array();
8960
- }
8961
- rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8962
- it.recs.push(rf);
10477
+ if (this.uploadChunkSizeSeconds || SessionManager.FORCE_ARRRAY_AUDIO_BUFFER) {
10478
+ ada = this.ac.audioBufferArray();
10479
+ frameLen = ada.frameLen;
8963
10480
  }
10481
+ else {
10482
+ ad = this.ac.audioBuffer();
10483
+ frameLen = ad.length;
10484
+ }
10485
+ adh = new AudioDataHolder(ad, ada);
10486
+ }
10487
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
10488
+ let rf = this._recordingFile;
10489
+ if (rf && rf instanceof SprRecordingFile) {
10490
+ this.items?.setSprRecFileAudioData(rf, adh);
8964
10491
  if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8965
10492
  // TODO use SpeechRecorderconfig resp. RecfileService
8966
10493
  // convert asynchronously to 16-bit integer PCM
@@ -8968,7 +10495,7 @@ class SessionManager extends BasicRecorder {
8968
10495
  // TODO duplicate conversion for manual download
8969
10496
  //console.log("Build wav writer...");
8970
10497
  this.processingRecording = true;
8971
- if (ad && rf) {
10498
+ if (ad) {
8972
10499
  let ww = new WavWriter();
8973
10500
  //new REST API URL
8974
10501
  let apiEndPoint = '';
@@ -8981,7 +10508,7 @@ class SessionManager extends BasicRecorder {
8981
10508
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
8982
10509
  let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.itemCode;
8983
10510
  ww.writeAsync(ad, (wavFile) => {
8984
- this.postRecording(wavFile, recUrl);
10511
+ this.postRecording(wavFile, recUrl, rf);
8985
10512
  this.processingRecording = false;
8986
10513
  this.updateWakeLock();
8987
10514
  });
@@ -8992,8 +10519,8 @@ class SessionManager extends BasicRecorder {
8992
10519
  let complete = true;
8993
10520
  if (this.items) {
8994
10521
  // search backwards, to gain faster detection of incomplete state
8995
- for (let ri = this.items.length - 1; ri >= 0; ri--) {
8996
- let it = this.items[ri];
10522
+ for (let ri = this.items.length() - 1; ri >= 0; ri--) {
10523
+ let it = this.items.getItem(ri);
8997
10524
  if (it.recording && !it.training && (!it.recs || it.recs.length == 0)) {
8998
10525
  complete = false;
8999
10526
  break;
@@ -9057,13 +10584,13 @@ class SessionManager extends BasicRecorder {
9057
10584
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
9058
10585
  let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
9059
10586
  ww.writeAsync(audioBuffer, (wavFile) => {
9060
- this.postRecording(wavFile, recUrl);
10587
+ this.postRecording(wavFile, recUrl, null);
9061
10588
  this.processingRecording = false;
9062
10589
  });
9063
10590
  }
9064
- postRecording(wavFile, recUrl) {
10591
+ postRecording(wavFile, recUrl, rf) {
9065
10592
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
9066
- let ul = new Upload(wavBlob, recUrl);
10593
+ let ul = new Upload(wavBlob, recUrl, rf);
9067
10594
  this.uploader.queueUpload(ul);
9068
10595
  }
9069
10596
  stop() {
@@ -9099,7 +10626,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9099
10626
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9100
10627
  <app-sprprompting [projectName]="projectName"
9101
10628
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9102
- [items]="items"
10629
+ [items]="items?.items"
9103
10630
  [transportActions]="transportActions"
9104
10631
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9105
10632
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9113,10 +10640,10 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9113
10640
 
9114
10641
 
9115
10642
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9116
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
10643
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9117
10644
  <div fxLayout="row">
9118
10645
  <spr-recordingitemcontrols fxFlex="10 0 1"
9119
- [audioLoaded]="displayAudioClip?.buffer!==null"
10646
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9120
10647
  [playStartAction]="controlAudioPlayer?.startAction"
9121
10648
  [playStopAction]="controlAudioPlayer?.stopAction"
9122
10649
  [peakDbLvl]="peakLevelInDb"
@@ -9134,7 +10661,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9134
10661
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9135
10662
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9136
10663
  </div>
9137
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10664
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9138
10665
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9139
10666
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9140
10667
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9142,7 +10669,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
9142
10669
  <app-readystateindicator class="ricontrols" fxLayoutAlign="end center" fxHide.xs [ready]="dataSaved && !isActive()"></app-readystateindicator>
9143
10670
  </div>
9144
10671
  </div>
9145
- `, 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"] }] });
10672
+ `, 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"] }] });
9146
10673
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SessionManager, decorators: [{
9147
10674
  type: Component,
9148
10675
  args: [{
@@ -9153,7 +10680,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9153
10680
  <app-warningbar [show]="isDefaultAudioTestSession()" warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
9154
10681
  <app-sprprompting [projectName]="projectName"
9155
10682
  [startStopSignalState]="startStopSignalState" [promptItem]="promptItem" [showPrompt]="showPrompt"
9156
- [items]="items"
10683
+ [items]="items?.items"
9157
10684
  [transportActions]="transportActions"
9158
10685
  [selectedItemIdx]="promptIndex" (onItemSelect)="itemSelect($event)" (onNextItem)="nextItem()" (onPrevItem)="prevItem()"
9159
10686
  [audioSignalCollapsed]="audioSignalCollapsed" [displayAudioClip]="displayAudioClip"
@@ -9167,10 +10694,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9167
10694
 
9168
10695
 
9169
10696
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}" [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
9170
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
10697
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [displayLevelInfos]="displayAudioClip?.levelInfos" [stateLoading]="audioFetching"></audio-levelbar>
9171
10698
  <div fxLayout="row">
9172
10699
  <spr-recordingitemcontrols fxFlex="10 0 1"
9173
- [audioLoaded]="displayAudioClip?.buffer!==null"
10700
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
9174
10701
  [playStartAction]="controlAudioPlayer?.startAction"
9175
10702
  [playStopAction]="controlAudioPlayer?.stopAction"
9176
10703
  [peakDbLvl]="peakLevelInDb"
@@ -9188,7 +10715,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
9188
10715
  <div fxFlex="1 1 30%" fxLayoutAlign="start center">
9189
10716
  <app-sprstatusdisplay fxHide.xs [statusMsg]="statusMsg" [statusAlertType]="statusAlertType" [statusWaiting]="statusWaiting"></app-sprstatusdisplay>
9190
10717
  </div>
9191
- <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="items==null || items.length>1"></app-sprtransport>
10718
+ <app-sprtransport fxFlex="10 0 30%" fxLayoutAlign="center center" [readonly]="readonly" [actions]="transportActions" [navigationEnabled]="!items || items.length()>1"></app-sprtransport>
9192
10719
  <div fxFlex="1 1 30%" fxLayoutAlign="end center" fxLayout="row">
9193
10720
  <app-uploadstatus class="ricontrols" fxHide.xs fxLayoutAlign="end center" *ngIf="enableUploadRecordings" [value]="uploadProgress"
9194
10721
  [status]="uploadStatus" [awaitNewUpload]="processingRecording"></app-uploadstatus>
@@ -9477,7 +11004,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
9477
11004
  this.uploadUpdate(ue);
9478
11005
  };
9479
11006
  window.addEventListener('beforeunload', (e) => {
9480
- console.debug("Before page unload event");
11007
+ //console.debug("Before page unload event");
9481
11008
  if (this.ready()) {
9482
11009
  return;
9483
11010
  }
@@ -9796,14 +11323,15 @@ class AudioDisplayPlayer {
9796
11323
  if (this.aCtx) {
9797
11324
  this.aCtx.decodeAudioData(data, (audioBuffer) => {
9798
11325
  //console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
9799
- this.audioClip = new AudioClip(audioBuffer);
11326
+ let adh = new AudioDataHolder(audioBuffer, null);
11327
+ this.audioClip = new AudioClip(adh);
9800
11328
  });
9801
11329
  }
9802
11330
  }
9803
- set audioData(audioBuffer) {
9804
- this.audioDisplayScrollPane.audioData = audioBuffer;
9805
- if (audioBuffer) {
9806
- let clip = new AudioClip(audioBuffer);
11331
+ set audioData(audioData) {
11332
+ this.audioDisplayScrollPane.audioData = audioData;
11333
+ if (audioData) {
11334
+ let clip = new AudioClip(audioData);
9807
11335
  if (this.ap) {
9808
11336
  this.ap.audioClip = clip;
9809
11337
  this.playStartAction.disabled = false;
@@ -9825,7 +11353,7 @@ class AudioDisplayPlayer {
9825
11353
  let audioData = null;
9826
11354
  let sel = null;
9827
11355
  if (audioClip) {
9828
- audioData = audioClip.buffer;
11356
+ audioData = audioClip.audioDataHolder;
9829
11357
  sel = audioClip.selection;
9830
11358
  if (this._audioClip) {
9831
11359
  this._audioClip.addSelectionObserver((ac) => {
@@ -10096,7 +11624,7 @@ class RecordingFileService {
10096
11624
  // Do not use Promise version, which does not work with Safari 13
10097
11625
  if (resp.body) {
10098
11626
  aCtx.decodeAudioData(resp.body, ab => {
10099
- recordingFile.audioBuffer = ab;
11627
+ RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(ab));
10100
11628
  if (this.debugDelay > 0) {
10101
11629
  window.setTimeout(() => {
10102
11630
  observer.next(recordingFile);
@@ -10146,7 +11674,7 @@ class RecordingFileService {
10146
11674
  if (resp.body) {
10147
11675
  aCtx.decodeAudioData(resp.body, ab => {
10148
11676
  if (rf) {
10149
- rf.audioBuffer = ab;
11677
+ RecordingFileUtils.setAudioData(rf, new AudioDataHolder(ab));
10150
11678
  }
10151
11679
  else {
10152
11680
  observer.error('Recording file object null');
@@ -10197,7 +11725,8 @@ class RecordingFileService {
10197
11725
  if (resp.body) {
10198
11726
  aCtx.decodeAudioData(resp.body, ab => {
10199
11727
  if (rf) {
10200
- rf.audioBuffer = ab;
11728
+ let adh = new AudioDataHolder(ab);
11729
+ RecordingFileUtils.setAudioData(rf, adh);
10201
11730
  }
10202
11731
  else {
10203
11732
  observer.error('Recording file object null');
@@ -10240,7 +11769,7 @@ class RecordingFileService {
10240
11769
  // append UUID to make request URL unique to avoid localhost server caching
10241
11770
  recUrl = recUrl + '.json?requestUUID=' + UUID.generate();
10242
11771
  }
10243
- console.log("Path request URL: " + recUrl);
11772
+ //console.log("Path request URL: "+recUrl)
10244
11773
  return this.http.patch(recUrl, { editSampleRate: editSampleRate, editStartFrame: editStartFrame, editEndFrame: editEndFrame }, { withCredentials: this.withCredentials });
10245
11774
  }
10246
11775
  }
@@ -10258,6 +11787,7 @@ class RecordingFileMetaComponent {
10258
11787
  constructor() {
10259
11788
  this.sessionId = null;
10260
11789
  this._recordingFile = null;
11790
+ this.stateLoading = false;
10261
11791
  this.itemCode = null;
10262
11792
  this.uuid = null;
10263
11793
  }
@@ -10273,12 +11803,12 @@ class RecordingFileMetaComponent {
10273
11803
  }
10274
11804
  if (this.itemCode) {
10275
11805
  this.uuid = null;
10276
- console.debug("SprRecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11806
+ //console.debug("SprRecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10277
11807
  }
10278
11808
  else {
10279
11809
  this.itemCode = null;
10280
11810
  this.uuid = this._recordingFile?.uuid;
10281
- console.debug("RecordingFile: " + this.itemCode + " UUID: " + this.uuid);
11811
+ //console.debug("RecordingFile: "+this.itemCode+ " UUID: "+this.uuid)
10282
11812
  }
10283
11813
  }
10284
11814
  else {
@@ -10295,10 +11825,11 @@ class RecordingFileMetaComponent {
10295
11825
  }
10296
11826
  }
10297
11827
  RecordingFileMetaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10298
- 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: `
11828
+ 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: `
10299
11829
  <mat-card>
10300
11830
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10301
11831
  <mat-card-content>
11832
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10302
11833
  <table>
10303
11834
  <tr *ngIf="itemCode">
10304
11835
  <td>Itemcode:</td>
@@ -10328,7 +11859,7 @@ RecordingFileMetaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10328
11859
  </table>
10329
11860
  </mat-card-content>
10330
11861
  </mat-card>
10331
- `, 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"] }] });
11862
+ `, 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"] }] });
10332
11863
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileMetaComponent, decorators: [{
10333
11864
  type: Component,
10334
11865
  args: [{
@@ -10337,6 +11868,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10337
11868
  <mat-card>
10338
11869
  <mat-card-title>Recording file ID: {{recordingFile?.recordingFileId}}</mat-card-title>
10339
11870
  <mat-card-content>
11871
+ <mat-progress-spinner *ngIf="stateLoading" mode="indeterminate" [diameter]="20"></mat-progress-spinner>
10340
11872
  <table>
10341
11873
  <tr *ngIf="itemCode">
10342
11874
  <td>Itemcode:</td>
@@ -10371,6 +11903,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10371
11903
  }]
10372
11904
  }], propDecorators: { sessionId: [{
10373
11905
  type: Input
11906
+ }], stateLoading: [{
11907
+ type: Input
10374
11908
  }], recordingFile: [{
10375
11909
  type: Input
10376
11910
  }] } });
@@ -10513,6 +12047,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10513
12047
  this.recordingFileVersion = null;
10514
12048
  this.routedByQueryParam = false;
10515
12049
  this.posInList = null;
12050
+ this.audioFetching = false;
10516
12051
  this.naviInfoLoading = false;
10517
12052
  this.parentE = this.eRef.nativeElement;
10518
12053
  this.firstAction = new Action('First');
@@ -10682,18 +12217,20 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10682
12217
  this.updateActions();
10683
12218
  let audioContext = AudioContextProvider.audioContextInstance();
10684
12219
  if (audioContext) {
12220
+ this.audioFetching = true;
10685
12221
  this.recordingFileService.fetchSprRecordingFile(audioContext, rfId).subscribe(value => {
12222
+ this.audioFetching = false;
10686
12223
  this.status = 'Audio file loaded.';
10687
12224
  let clip = null;
10688
12225
  this.recordingFile = value;
10689
12226
  if (this.recordingFile) {
10690
- let ab = this.recordingFile.audioBuffer;
12227
+ let ab = this.recordingFile.audioDataHolder;
10691
12228
  if (ab) {
10692
12229
  clip = new AudioClip(ab);
10693
12230
  let esffsr = null;
10694
12231
  let eeffsr = null;
10695
12232
  let esr = null;
10696
- if (clip.buffer != null) {
12233
+ if (clip.audioDataHolder != null) {
10697
12234
  esr = ab.sampleRate;
10698
12235
  if (esr != null) {
10699
12236
  esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
@@ -10705,8 +12242,8 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10705
12242
  sel = new Selection(ab.sampleRate, esffsr, eeffsr);
10706
12243
  }
10707
12244
  else {
10708
- let ch0 = ab.getChannelData(0);
10709
- let frameLength = ch0.length;
12245
+ //let ch0 = ab.getChannelData(0);
12246
+ let frameLength = ab.frameLen;
10710
12247
  sel = new Selection(esr, esffsr, frameLength);
10711
12248
  }
10712
12249
  }
@@ -10720,6 +12257,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10720
12257
  this.audioClip = clip;
10721
12258
  this.loadedRecfile();
10722
12259
  }, error1 => {
12260
+ this.audioFetching = false;
10723
12261
  this.status = 'Error loading audio file!';
10724
12262
  });
10725
12263
  }
@@ -10829,7 +12367,7 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
10829
12367
  for (let avRfV of avRf) {
10830
12368
  os += avRfV.version + '/';
10831
12369
  }
10832
- console.debug(os);
12370
+ //console.debug(os);
10833
12371
  }
10834
12372
  }
10835
12373
  this.updatePos();
@@ -10845,7 +12383,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10845
12383
 
10846
12384
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10847
12385
  <div class="ctrlview">
10848
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12386
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10849
12387
 
10850
12388
  <audio-display-control [audioClip]="audioClip"
10851
12389
  [playStartAction]="playStartAction"
@@ -10858,7 +12396,7 @@ RecordingFileViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0
10858
12396
  [zoomFitToPanelAction]="zoomFitToPanelAction"></audio-display-control>
10859
12397
  <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>
10860
12398
  </div>
10861
- `, 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"] }] });
12399
+ `, 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"] }] });
10862
12400
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileViewComponent, decorators: [{
10863
12401
  type: Component,
10864
12402
  args: [{
@@ -10867,7 +12405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
10867
12405
 
10868
12406
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
10869
12407
  <div class="ctrlview">
10870
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12408
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
10871
12409
 
10872
12410
  <audio-display-control [audioClip]="audioClip"
10873
12411
  [playStartAction]="playStartAction"
@@ -10960,7 +12498,7 @@ class RecordingFileUI extends RecordingFileViewComponent {
10960
12498
  }
10961
12499
  applySelection() {
10962
12500
  if (this.audioClip) {
10963
- let ab = this.audioClip.buffer;
12501
+ let ab = this.audioClip.audioDataHolder;
10964
12502
  let s = this.audioClip.selection;
10965
12503
  if (ab && this.recordingFile?.recordingFileId) {
10966
12504
  let sr = null;
@@ -10998,7 +12536,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
10998
12536
 
10999
12537
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11000
12538
  <div class="ctrlview">
11001
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12539
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11002
12540
  <audio-display-control [audioClip]="audioClip"
11003
12541
  [playStartAction]="playStartAction"
11004
12542
  [playSelectionAction]="playSelectionAction"
@@ -11012,7 +12550,7 @@ RecordingFileUI.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
11012
12550
  </div>
11013
12551
 
11014
12552
  <button mat-raised-button color="accent" (click)="applySelection()" [disabled]="editSaved">{{this.applyButtonText()}}</button>
11015
- `, 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"] }] });
12553
+ `, 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"] }] });
11016
12554
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: RecordingFileUI, decorators: [{
11017
12555
  type: Component,
11018
12556
  args: [{
@@ -11023,7 +12561,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
11023
12561
 
11024
12562
  <audio-display-scroll-pane #audioDisplayScrollPane></audio-display-scroll-pane>
11025
12563
  <div class="ctrlview">
11026
- <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile"></app-recording-file-meta>
12564
+ <app-recording-file-meta [sessionId]="sessionId" [recordingFile]="recordingFile" [stateLoading]="audioFetching"></app-recording-file-meta>
11027
12565
  <audio-display-control [audioClip]="audioClip"
11028
12566
  [playStartAction]="playStartAction"
11029
12567
  [playSelectionAction]="playSelectionAction"
@@ -11080,7 +12618,8 @@ class MediaUtils {
11080
12618
 
11081
12619
  class RecordingList {
11082
12620
  constructor() {
11083
- this.recordingList = new Array();
12621
+ //private recordingList:Array<RecordingFile>=new Array<RecordingFile>();
12622
+ this.recordingList = new RecFilesCache();
11084
12623
  //cols=['index','length','samples','samplerate','action'];
11085
12624
  this.cols = ['index', 'startedDate', 'length', 'action'];
11086
12625
  this.selectDisabled = false;
@@ -11092,7 +12631,7 @@ class RecordingList {
11092
12631
  this.buildDataSource();
11093
12632
  }
11094
12633
  buildDataSource() {
11095
- this.recordingList.sort((a, b) => {
12634
+ this.recordingList.recFiles.sort((a, b) => {
11096
12635
  let cmp = 0;
11097
12636
  let aD = null;
11098
12637
  let bD = null;
@@ -11113,24 +12652,35 @@ class RecordingList {
11113
12652
  }
11114
12653
  return cmp;
11115
12654
  });
11116
- this.recordingListDataSource.data = this.recordingList;
12655
+ this.recordingListDataSource.data = this.recordingList.recFiles;
11117
12656
  }
11118
- push(rf) {
11119
- this.recordingList.push(rf);
12657
+ addRecFile(rf) {
12658
+ this.recordingList.addRecFile(rf);
11120
12659
  this.buildDataSource();
11121
12660
  }
12661
+ setRecFileAudioData(recFile, adh) {
12662
+ this.recordingList.setRecFileAudioData(recFile, adh);
12663
+ }
11122
12664
  selectRecordingFile(rf) {
12665
+ this.recordingList.currentRecordingFile = rf;
11123
12666
  this.selectedRecordingFileChanged.emit(rf);
11124
12667
  }
11125
12668
  selectTop() {
11126
- if (this.recordingList.length > 0) {
11127
- this.selectRecordingFile(this.recordingList[0]);
12669
+ if (this.recordingList.recFiles.length > 0) {
12670
+ this.selectRecordingFile(this.recordingList.recFiles[0]);
11128
12671
  }
11129
12672
  }
11130
12673
  lengthTimeFormatted(rf) {
11131
12674
  let str = '--:--:--';
11132
- if (rf.frames && rf.audioBuffer) {
11133
- str = MediaUtils.toMediaTime(rf.frames / rf.audioBuffer?.sampleRate);
12675
+ let tl = null;
12676
+ if (rf.timeLength) {
12677
+ tl = rf.timeLength;
12678
+ }
12679
+ else if (rf.frames && rf.audioDataHolder) {
12680
+ tl = rf.frames / rf.audioDataHolder?.sampleRate;
12681
+ }
12682
+ if (tl) {
12683
+ str = MediaUtils.toMediaTime(tl);
11134
12684
  }
11135
12685
  return str;
11136
12686
  }
@@ -11216,8 +12766,11 @@ class RecorderCombiPane {
11216
12766
  }
11217
12767
  ngAfterViewInit() {
11218
12768
  }
11219
- push(rf) {
11220
- this.recordingListComp.push(rf);
12769
+ addRecFile(rf) {
12770
+ this.recordingListComp.addRecFile(rf);
12771
+ }
12772
+ setRecFileAudioData(recFile, adh) {
12773
+ this.recordingListComp.setRecFileAudioData(recFile, adh);
11221
12774
  }
11222
12775
  selectRecordingFile(rf) {
11223
12776
  this.selectedRecordingFileChanged.emit(rf);
@@ -11290,15 +12843,11 @@ class Item {
11290
12843
  }
11291
12844
  class AudioRecorder extends BasicRecorder {
11292
12845
  constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11293
- super(dialog, sessionService, uploader, config);
11294
- this.changeDetectorRef = changeDetectorRef;
12846
+ super(changeDetectorRef, dialog, sessionService, uploader, config);
11295
12847
  this.renderer = renderer;
11296
12848
  this.route = route;
11297
- this.dialog = dialog;
11298
- this.sessionService = sessionService;
11299
12849
  this.recFileService = recFileService;
11300
12850
  this.uploader = uploader;
11301
- this.config = config;
11302
12851
  this._project = null;
11303
12852
  this.projectName = null;
11304
12853
  this.enableUploadRecordings = true;
@@ -11481,13 +13030,15 @@ class AudioRecorder extends BasicRecorder {
11481
13030
  if (rfs) {
11482
13031
  if (rfs instanceof Array) {
11483
13032
  rfs.forEach((rf) => {
13033
+ // the list comes from the server, asssuem all recording files as server persisted
13034
+ rf.serverPersisted = true;
11484
13035
  if (rf.startedDate) {
11485
13036
  rf._startedAsDateObj = new Date(rf.startedDate);
11486
13037
  }
11487
13038
  if (rf.date) {
11488
13039
  rf._dateAsDateObj = new Date(rf.date);
11489
13040
  }
11490
- this.recorderCombiPane.push(rf);
13041
+ this.recorderCombiPane.addRecFile(rf);
11491
13042
  });
11492
13043
  }
11493
13044
  else {
@@ -11541,6 +13092,7 @@ class AudioRecorder extends BasicRecorder {
11541
13092
  return this._project;
11542
13093
  }
11543
13094
  selectRecordingFile(rf) {
13095
+ this.audioFetching = false;
11544
13096
  this.displayRecFile = rf;
11545
13097
  }
11546
13098
  uploadUpdate(ue) {
@@ -11562,9 +13114,6 @@ class AudioRecorder extends BasicRecorder {
11562
13114
  this.changeDetectorRef.detectChanges();
11563
13115
  }
11564
13116
  set controlAudioPlayer(controlAudioPlayer) {
11565
- if (this._controlAudioPlayer) {
11566
- //this._controlAudioPlayer.listener=null;
11567
- }
11568
13117
  this._controlAudioPlayer = controlAudioPlayer;
11569
13118
  if (this._controlAudioPlayer) {
11570
13119
  this._controlAudioPlayer.listener = this;
@@ -11644,6 +13193,7 @@ class AudioRecorder extends BasicRecorder {
11644
13193
  this.displayAudioClip = null;
11645
13194
  this.showRecording();
11646
13195
  if (this.ac) {
13196
+ this.audioFetching = false;
11647
13197
  if (!this.ac.opened) {
11648
13198
  if (this._selectedDeviceId) {
11649
13199
  console.log("Open session with audio device Id: \'" + this._selectedDeviceId + "\' for " + this._channelCount + " channels");
@@ -11660,10 +13210,10 @@ class AudioRecorder extends BasicRecorder {
11660
13210
  }
11661
13211
  downloadRecording() {
11662
13212
  if (this.displayRecFile) {
11663
- let ab = this.displayRecFile.audioBuffer;
13213
+ let ab = this.displayRecFile.audioDataHolder;
11664
13214
  let ww = new WavWriter();
11665
- if (ab) {
11666
- let wavFile = ww.writeAsync(ab, (wavFile) => {
13215
+ if (ab?.buffer) {
13216
+ let wavFile = ww.writeAsync(ab.buffer, (wavFile) => {
11667
13217
  let blob = new Blob([wavFile], { type: 'audio/wav' });
11668
13218
  let rfUrl = URL.createObjectURL(blob);
11669
13219
  let dataDnlLnk = this.renderer.createElement('a');
@@ -11686,35 +13236,50 @@ class AudioRecorder extends BasicRecorder {
11686
13236
  set displayRecFile(displayRecFile) {
11687
13237
  this._displayRecFile = displayRecFile;
11688
13238
  if (this._displayRecFile) {
11689
- let ab = this._displayRecFile.audioBuffer;
11690
- if (ab) {
11691
- this.displayAudioClip = new AudioClip(ab);
13239
+ let adh = this._displayRecFile.audioDataHolder;
13240
+ if (adh) {
13241
+ this.displayAudioClip = new AudioClip(adh);
13242
+ console.debug(" set recording file: display audio clip set");
11692
13243
  this.controlAudioPlayer.audioClip = this.displayAudioClip;
11693
13244
  }
11694
13245
  else {
11695
13246
  // clear for now ...
11696
13247
  this.displayAudioClip = null;
13248
+ console.debug("set recording file: display audio clip null");
11697
13249
  this.controlAudioPlayer.audioClip = null;
11698
13250
  if (this._controlAudioPlayer && this._session) {
11699
13251
  //... and try to fetch from server
11700
- this.audioFetchSubscription = this.recFileService.fetchAndApplyRecordingFile(this._controlAudioPlayer.context, this._session.project, this._displayRecFile).subscribe((rf) => {
11701
- let fab = null;
11702
- if (rf && this._displayRecFile) {
11703
- fab = this._displayRecFile.audioBuffer;
11704
- }
11705
- else {
11706
- this.statusMsg = 'Recording file could not be loaded.';
13252
+ this.audioFetching = true;
13253
+ let rf = this._displayRecFile;
13254
+ let clip = this.displayAudioClip;
13255
+ this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._controlAudioPlayer.context, this._session.project, rf).subscribe({
13256
+ next: ab => {
13257
+ this.audioFetching = false;
13258
+ let fabDh = null;
13259
+ if (ab) {
13260
+ if (rf) {
13261
+ fabDh = new AudioDataHolder(ab);
13262
+ this.recorderCombiPane.setRecFileAudioData(rf, fabDh);
13263
+ }
13264
+ }
13265
+ else {
13266
+ console.error('Recording file could not be loaded.');
13267
+ this.statusMsg = 'Recording file could not be loaded.';
13268
+ this.statusAlertType = 'error';
13269
+ }
13270
+ if (fabDh) {
13271
+ // 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
13272
+ this.displayAudioClip = new AudioClip(fabDh);
13273
+ //console.debug("set recording file: display audio clip from fetched audio buffer");
13274
+ }
13275
+ this.controlAudioPlayer.audioClip = this.displayAudioClip;
13276
+ this.showRecording();
13277
+ }, error: err => {
13278
+ console.error("Could not load recording file from server: " + err);
13279
+ this.audioFetching = false;
13280
+ this.statusMsg = 'Recording file could not be loaded: ' + err;
11707
13281
  this.statusAlertType = 'error';
11708
13282
  }
11709
- if (fab) {
11710
- this.displayAudioClip = new AudioClip(fab);
11711
- }
11712
- this.controlAudioPlayer.audioClip = this.displayAudioClip;
11713
- this.showRecording();
11714
- }, err => {
11715
- console.error("Could not load recording file from server: " + err);
11716
- this.statusMsg = 'Recording file could not be loaded: ' + err;
11717
- this.statusAlertType = 'error';
11718
13283
  });
11719
13284
  }
11720
13285
  else {
@@ -11724,6 +13289,7 @@ class AudioRecorder extends BasicRecorder {
11724
13289
  }
11725
13290
  }
11726
13291
  else {
13292
+ console.debug("recording file null");
11727
13293
  this.displayAudioClip = null;
11728
13294
  this.controlAudioPlayer.audioClip = null;
11729
13295
  }
@@ -11732,25 +13298,6 @@ class AudioRecorder extends BasicRecorder {
11732
13298
  get displayRecFile() {
11733
13299
  return this._displayRecFile;
11734
13300
  }
11735
- showRecording() {
11736
- this.controlAudioPlayer.stop();
11737
- if (this.displayAudioClip) {
11738
- let dap = this.displayAudioClip;
11739
- this.levelMeasure.calcBufferLevelInfos(dap.buffer, LEVEL_BAR_INTERVALL_SECONDS).then((levelInfos) => {
11740
- dap.levelInfos = levelInfos;
11741
- this.changeDetectorRef.detectChanges();
11742
- });
11743
- this.playStartAction.disabled = false;
11744
- }
11745
- else {
11746
- this.playStartAction.disabled = true;
11747
- // Collapse audio signal display if open
11748
- if (!this.audioSignalCollapsed) {
11749
- this.audioSignalCollapsed = true;
11750
- }
11751
- }
11752
- this.changeDetectorRef.detectChanges();
11753
- }
11754
13301
  updateStartActionDisableState() {
11755
13302
  this.transportActions.startAction.disabled = !(this.ac);
11756
13303
  }
@@ -11794,6 +13341,19 @@ class AudioRecorder extends BasicRecorder {
11794
13341
  super.started();
11795
13342
  this.statusAlertType = 'info';
11796
13343
  this.statusMsg = 'Recording...';
13344
+ if (!this.rfUuid) {
13345
+ this.rfUuid = UUID.generate();
13346
+ }
13347
+ let sessId = 0;
13348
+ if (this._session) {
13349
+ sessId = this._session.sessionId;
13350
+ }
13351
+ let rf = new RecordingFile(this.rfUuid, sessId, null);
13352
+ rf._startedAsDateObj = this.startedDate;
13353
+ if (rf._startedAsDateObj) {
13354
+ rf.startedDate = rf._startedAsDateObj.toString();
13355
+ }
13356
+ this._recordingFile = rf;
11797
13357
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
11798
13358
  this.maxRecTimerId = window.setTimeout(() => {
11799
13359
  this.stopRecordingMaxRec();
@@ -11830,68 +13390,75 @@ class AudioRecorder extends BasicRecorder {
11830
13390
  this.transportActions.pauseAction.disabled = true;
11831
13391
  this.statusAlertType = 'info';
11832
13392
  this.statusMsg = 'Recorded.';
11833
- let ad;
13393
+ let ad = null;
13394
+ let ada = null;
13395
+ let adh = null;
13396
+ let frameLen = 0;
11834
13397
  if (this.ac) {
11835
- ad = this.ac.audioBuffer();
13398
+ if (this.uploadChunkSizeSeconds || AudioRecorder.FORCE_ARRRAY_AUDIO_BUFFER) {
13399
+ ada = this.ac.audioBufferArray();
13400
+ frameLen = ada.frameLen;
13401
+ }
13402
+ else {
13403
+ ad = this.ac.audioBuffer();
13404
+ frameLen = ad.length;
13405
+ }
13406
+ adh = new AudioDataHolder(ad, ada);
11836
13407
  let sessId = 0;
11837
13408
  if (this._session) {
11838
13409
  sessId = this._session.sessionId;
11839
13410
  }
11840
- if (!this.rfUuid) {
11841
- this.rfUuid = UUID.generate();
11842
- }
11843
- let rf = new RecordingFile(this.rfUuid, sessId, ad);
11844
- rf._startedAsDateObj = this.startedDate;
11845
- if (rf._startedAsDateObj) {
11846
- rf.startedDate = rf._startedAsDateObj.toString();
11847
- }
11848
- rf.frames = ad.length;
11849
- this.displayRecFile = rf;
11850
- this.recorderCombiPane.push(rf);
11851
- // Upload if upload enabled and not in chunked upload mode
11852
- if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
11853
- let apiEndPoint = '';
11854
- if (this.config && this.config.apiEndPoint) {
11855
- apiEndPoint = this.config.apiEndPoint;
11856
- }
11857
- if (apiEndPoint !== '') {
11858
- apiEndPoint = apiEndPoint + '/';
13411
+ if (this._recordingFile) {
13412
+ // Use an own reference since the writing of the wave file is asynchronous and this._recordingFile might already contain the next recording
13413
+ let rf = this._recordingFile;
13414
+ RecordingFileUtils.setAudioData(rf, adh);
13415
+ this.recorderCombiPane.addRecFile(rf);
13416
+ // Upload if upload enabled and not in chunked upload mode
13417
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds && rf != null && ad != null) {
13418
+ let apiEndPoint = '';
13419
+ if (this.config && this.config.apiEndPoint) {
13420
+ apiEndPoint = this.config.apiEndPoint;
13421
+ }
13422
+ if (apiEndPoint !== '') {
13423
+ apiEndPoint = apiEndPoint + '/';
13424
+ }
13425
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
13426
+ let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
13427
+ // convert asynchronously to 16-bit integer PCM
13428
+ // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
13429
+ // TODO duplicate conversion for manual download
13430
+ this.processingRecording = true;
13431
+ let ww = new WavWriter();
13432
+ ww.writeAsync(ad, (wavFile) => {
13433
+ this.postRecordingMultipart(wavFile, recUrl, rf);
13434
+ this.processingRecording = false;
13435
+ this.updateWakeLock();
13436
+ this.changeDetectorRef.detectChanges();
13437
+ });
11859
13438
  }
11860
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11861
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11862
- // convert asynchronously to 16-bit integer PCM
11863
- // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11864
- // TODO duplicate conversion for manual download
11865
- this.processingRecording = true;
11866
- let ww = new WavWriter();
11867
- ww.writeAsync(ad, (wavFile) => {
11868
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11869
- this.processingRecording = false;
11870
- this.updateWakeLock();
11871
- this.changeDetectorRef.detectChanges();
11872
- });
11873
13439
  }
11874
13440
  }
13441
+ this.displayRecFile = this._recordingFile;
11875
13442
  this.status = 1 /* IDLE */;
11876
13443
  this.navigationDisabled = false;
11877
13444
  this.updateNavigationActions();
11878
13445
  this.updateWakeLock();
11879
13446
  this.changeDetectorRef.detectChanges();
11880
13447
  }
11881
- postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
13448
+ postRecordingMultipart(wavFile, recUrl, rf) {
11882
13449
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11883
13450
  let fd = new FormData();
11884
- if (uuid) {
11885
- fd.set('uuid', uuid);
13451
+ if (rf.uuid) {
13452
+ fd.set('uuid', rf.uuid);
11886
13453
  }
11887
- if (sessionId !== null) {
11888
- fd.set('sessionId', sessionId.toString());
13454
+ if (rf.session !== null) {
13455
+ fd.set('sessionId', rf.session.toString());
11889
13456
  }
11890
- if (startedDate) {
11891
- fd.set('startedDate', startedDate.toJSON());
13457
+ if (rf._startedAsDateObj) {
13458
+ fd.set('startedDate', rf._startedAsDateObj.toJSON());
11892
13459
  }
11893
13460
  fd.set('audio', wavBlob);
11894
- let ul = new Upload(fd, recUrl);
13461
+ let ul = new Upload(fd, recUrl, rf);
11895
13462
  this.uploader.queueUpload(ul);
11896
13463
  }
11897
13464
  postChunkAudioBuffer(audioBuffer, chunkIdx) {
@@ -11907,8 +13474,9 @@ class AudioRecorder extends BasicRecorder {
11907
13474
  }
11908
13475
  let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11909
13476
  let recUrl = sessionsUrl + '/' + this.session?.sessionId + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
13477
+ let rf = this._recordingFile;
11910
13478
  ww.writeAsync(audioBuffer, (wavFile) => {
11911
- this.postRecording(wavFile, recUrl);
13479
+ this.postRecording(wavFile, recUrl, rf);
11912
13480
  this.processingRecording = false;
11913
13481
  });
11914
13482
  }
@@ -11957,11 +13525,11 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
11957
13525
 
11958
13526
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
11959
13527
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
11960
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
13528
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
11961
13529
  [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
11962
13530
  <div fxLayout="row">
11963
13531
  <spr-recordingitemcontrols fxFlex="10 0 1"
11964
- [audioLoaded]="displayAudioClip?.buffer!==null"
13532
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
11965
13533
  [playStartAction]="controlAudioPlayer?.startAction"
11966
13534
  [playStopAction]="controlAudioPlayer?.stopAction"
11967
13535
  [peakDbLvl]="peakLevelInDb"
@@ -12000,7 +13568,7 @@ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version:
12000
13568
  [ready]="dataSaved && !isActive()"></app-readystateindicator>
12001
13569
  </div>
12002
13570
  </div>
12003
- `, 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"] }] });
13571
+ `, 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"] }] });
12004
13572
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: AudioRecorder, decorators: [{
12005
13573
  type: Component,
12006
13574
  args: [{
@@ -12023,11 +13591,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
12023
13591
 
12024
13592
  <div fxLayout="row" fxLayout.xs="column" [ngStyle]="{'height.px':100,'min-height.px': 100}"
12025
13593
  [ngStyle.xs]="{'height.px':125,'min-height.px': 125}">
12026
- <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()"
13594
+ <audio-levelbar fxFlex="1 0 1" [streamingMode]="isRecording()" [stateLoading]="audioFetching"
12027
13595
  [displayLevelInfos]="displayAudioClip?.levelInfos"></audio-levelbar>
12028
13596
  <div fxLayout="row">
12029
13597
  <spr-recordingitemcontrols fxFlex="10 0 1"
12030
- [audioLoaded]="displayAudioClip?.buffer!==null"
13598
+ [audioLoaded]="displayAudioClip?.audioDataHolder!==null"
12031
13599
  [playStartAction]="controlAudioPlayer?.startAction"
12032
13600
  [playStopAction]="controlAudioPlayer?.stopAction"
12033
13601
  [peakDbLvl]="peakLevelInDb"
@@ -12305,20 +13873,20 @@ class SpeechrecorderngModule {
12305
13873
  }
12306
13874
  SpeechrecorderngModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
12307
13875
  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,
12308
- 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] });
12309
- 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]] });
13876
+ 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] });
13877
+ 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]] });
12310
13878
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: SpeechrecorderngModule, decorators: [{
12311
13879
  type: NgModule,
12312
13880
  args: [{
12313
13881
  declarations: [AudioSignal, Sonagram, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, Progress, SimpleTrafficLight, Recinstructions, Prompter, PromptContainer, PromptingContainer, Prompting, StatusDisplay,
12314
13882
  ProgressDisplay, RecordingItemDisplay, RecordingItemControls, UploadStatus, TransportPanel, WakeLockIndicator, ReadyStateIndicator, ControlPanel, WarningBar, AudioRecorder, SessionManager, MessageDialog, SessionFinishedDialog, SpeechrecorderngComponent, AudioRecorderComponent, RecordingFileViewComponent, RecordingFileUI, ScrollIntoViewDirective, RecordingFileNaviComponent, RecordingFileMetaComponent, RecordingList, RecorderCombiPane, AudioRecorder],
12315
13883
  exports: [MessageDialog, SpeechrecorderngComponent, ScrollPaneHorizontal, AudioClipUIContainer, AudioDisplayScrollPane, AudioDisplay, AudioDisplayPlayer, AudioDisplayControl, LevelBar, AudioRecorder],
12316
- imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule],
13884
+ imports: [RouterModule.forChild(SPR_ROUTES), FlexLayoutModule, CommonModule, MatIconModule, MatButtonModule, MatDialogModule, MatProgressBarModule, MatProgressSpinnerModule, MatTooltipModule, HttpClientModule, MatCheckboxModule, MatCardModule, MatDividerModule, MatGridListModule, MatTableModule, MatInputModule, MatSelectModule, MatSnackBarModule, MatMenuModule],
12317
13885
  providers: [SessionService, ProjectService, ScriptService, RecordingService, RecordingFileService, SpeechRecorderUploader]
12318
13886
  }]
12319
13887
  }] });
12320
13888
 
12321
- const VERSION = '2.25.3';
13889
+ const VERSION = '3.0.0';
12322
13890
 
12323
13891
  /*
12324
13892
  * Public API Surface of speechrecorderng