speechrecorderng 2.24.0 → 2.25.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.
@@ -19,6 +19,7 @@ import * as i1$2 from '@angular/material/dialog';
19
19
  import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
20
20
  import * as i3$2 from '@angular/material/button';
21
21
  import { MatButtonModule } from '@angular/material/button';
22
+ import { __decorate, __param } from 'tslib';
22
23
  import * as i1$3 from '@angular/material/progress-spinner';
23
24
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
24
25
  import * as i8 from '@angular/flex-layout/flex';
@@ -1047,17 +1048,45 @@ class AudioCapture {
1047
1048
  this._opened = false;
1048
1049
  }
1049
1050
  audioBuffer() {
1050
- var frameLen = 0;
1051
- var ch0Data = this.data[0];
1052
- for (var ch0Chk of ch0Data) {
1051
+ let frameLen = 0;
1052
+ let ch0Data = this.data[0];
1053
+ for (let ch0Chk of ch0Data) {
1053
1054
  frameLen += ch0Chk.length;
1054
1055
  }
1055
- var ab = this.context.createBuffer(this.channelCount, frameLen, this.context.sampleRate);
1056
- for (var ch = 0; ch < this.channelCount; ch++) {
1057
- var chD = ab.getChannelData(ch);
1058
- var pos = 0;
1059
- for (var chChk of this.data[ch]) {
1060
- var bufLen = chChk.length;
1056
+ let ab;
1057
+ try {
1058
+ ab = this.context.createBuffer(this.channelCount, frameLen, this.context.sampleRate);
1059
+ }
1060
+ catch (err) {
1061
+ if (err instanceof DOMException) {
1062
+ if (err.name === 'NotSupportedError') {
1063
+ if (frameLen == 0) {
1064
+ // Empty buffers are not supported by Chromium
1065
+ // Create dummy buffer with one sample
1066
+ ab = this.context.createBuffer(this.channelCount, 1, this.context.sampleRate);
1067
+ }
1068
+ else {
1069
+ throw err;
1070
+ }
1071
+ }
1072
+ else {
1073
+ throw err;
1074
+ }
1075
+ }
1076
+ else if (err instanceof RangeError) {
1077
+ // Out of memory
1078
+ // TODO What to do ??
1079
+ throw err;
1080
+ }
1081
+ else {
1082
+ throw err;
1083
+ }
1084
+ }
1085
+ for (let ch = 0; ch < this.channelCount; ch++) {
1086
+ let chD = ab.getChannelData(ch);
1087
+ let pos = 0;
1088
+ for (let chChk of this.data[ch]) {
1089
+ let bufLen = chChk.length;
1061
1090
  chD.set(chChk, pos);
1062
1091
  pos += bufLen;
1063
1092
  }
@@ -5633,98 +5662,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
5633
5662
  type: Output
5634
5663
  }] } });
5635
5664
 
5636
- class Float32ArrayChunkerOutStream {
5637
- constructor(outStream) {
5638
- this.outStream = outStream;
5639
- this.bufs = new Array();
5640
- this._channels = 0;
5641
- this._chunkSize = 0;
5642
- this.filled = 0;
5643
- this.receivedFrames = 0;
5644
- this.sentFrames = 0;
5645
- }
5646
- createBuffers() {
5647
- this.bufs = new Array(this._channels);
5648
- for (let ch = 0; ch < this._channels; ch++) {
5649
- this.bufs[ch] = new Float32Array(this._chunkSize);
5650
- }
5651
- }
5652
- set chunkSize(chunkSize) {
5653
- this._chunkSize = chunkSize;
5654
- this.createBuffers();
5655
- }
5656
- set channels(channels) {
5657
- this._channels = channels;
5658
- this.createBuffers();
5659
- }
5660
- write(buffers) {
5661
- let copied = 0;
5662
- if (buffers.length > 0) {
5663
- let buffersLen = buffers[0].length;
5664
- this.receivedFrames += buffersLen;
5665
- let avail = buffersLen;
5666
- // Fill out buffers until all values copied
5667
- while (avail > 0) {
5668
- let toFill = this._chunkSize - this.filled;
5669
- if (toFill > avail) {
5670
- toFill = avail;
5671
- }
5672
- let sliceEnd = copied + toFill;
5673
- // Firefox on Android sends only the first channel
5674
- for (let ch = 0; ch < buffersLen; ch++) {
5675
- if (buffers[ch]) {
5676
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
5677
- let buf = this.bufs[ch];
5678
- buf.set(cpPrt, this.filled);
5679
- }
5680
- }
5681
- copied += toFill;
5682
- avail -= toFill;
5683
- this.filled += toFill;
5684
- if (this.filled == this._chunkSize) {
5685
- this.outStream.write(this.bufs);
5686
- this.sentFrames += this.filled;
5687
- this.filled = 0;
5688
- }
5689
- }
5690
- }
5691
- return copied;
5692
- }
5693
- flush() {
5694
- if (this.filled > 0) {
5695
- let restBufs = new Array(this._channels);
5696
- for (let ch = 0; ch < this._channels; ch++) {
5697
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
5698
- }
5699
- this.outStream.write(restBufs);
5700
- this.outStream.flush();
5701
- this.sentFrames += this.filled;
5702
- this.filled = 0;
5703
- }
5704
- }
5705
- close() {
5706
- this.outStream.close();
5707
- }
5708
- }
5709
-
5710
- class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
5711
- constructor(outStream, chunkDurationSeconds) {
5712
- super(outStream);
5713
- this.chunkDurationSeconds = chunkDurationSeconds;
5714
- this.sampleRate = null;
5715
- this.sequenceAudioFloat32OutStream = outStream;
5716
- }
5717
- setFormat(channels, sampleRate) {
5718
- this.channels = channels;
5719
- this.sampleRate = sampleRate;
5720
- this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
5721
- this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
5722
- }
5723
- nextStream() {
5724
- this.sequenceAudioFloat32OutStream.nextStream();
5725
- }
5726
- }
5727
-
5728
5665
  class SessionFinishedDialog {
5729
5666
  constructor(dialogRef, data) {
5730
5667
  this.dialogRef = dialogRef;
@@ -7260,23 +7197,209 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
7260
7197
  type: Input
7261
7198
  }] } });
7262
7199
 
7200
+ class Float32ArrayChunkerOutStream {
7201
+ constructor(outStream) {
7202
+ this.outStream = outStream;
7203
+ this.bufs = new Array();
7204
+ this._channels = 0;
7205
+ this._chunkSize = 0;
7206
+ this.filled = 0;
7207
+ this.receivedFrames = 0;
7208
+ this.sentFrames = 0;
7209
+ }
7210
+ createBuffers() {
7211
+ this.bufs = new Array(this._channels);
7212
+ for (let ch = 0; ch < this._channels; ch++) {
7213
+ this.bufs[ch] = new Float32Array(this._chunkSize);
7214
+ }
7215
+ }
7216
+ set chunkSize(chunkSize) {
7217
+ this._chunkSize = chunkSize;
7218
+ this.createBuffers();
7219
+ }
7220
+ get chunkSize() {
7221
+ return this._chunkSize;
7222
+ }
7223
+ set channels(channels) {
7224
+ this._channels = channels;
7225
+ this.createBuffers();
7226
+ }
7227
+ get channels() {
7228
+ return this._channels;
7229
+ }
7230
+ available() {
7231
+ return this._chunkSize - this.filled;
7232
+ }
7233
+ write(buffers) {
7234
+ let copied = 0;
7235
+ if (buffers.length > 0) {
7236
+ let buffersLen = buffers[0].length;
7237
+ this.receivedFrames += buffersLen;
7238
+ let avail = buffersLen;
7239
+ // Fill out buffers until all values copied
7240
+ while (avail > 0) {
7241
+ let toFill = this._chunkSize - this.filled;
7242
+ if (toFill > avail) {
7243
+ toFill = avail;
7244
+ }
7245
+ let sliceEnd = copied + toFill;
7246
+ // Firefox on Android sends only the first channel
7247
+ for (let ch = 0; ch < buffersLen; ch++) {
7248
+ if (buffers[ch]) {
7249
+ let cpPrt = buffers[ch].slice(copied, sliceEnd);
7250
+ let buf = this.bufs[ch];
7251
+ buf.set(cpPrt, this.filled);
7252
+ }
7253
+ }
7254
+ copied += toFill;
7255
+ avail -= toFill;
7256
+ this.filled += toFill;
7257
+ if (this.filled == this._chunkSize) {
7258
+ this.outStream.write(this.bufs);
7259
+ this.sentFrames += this.filled;
7260
+ this.filled = 0;
7261
+ }
7262
+ }
7263
+ }
7264
+ return copied;
7265
+ }
7266
+ flush() {
7267
+ if (this.filled > 0) {
7268
+ let restBufs = new Array(this._channels);
7269
+ for (let ch = 0; ch < this._channels; ch++) {
7270
+ restBufs[ch] = this.bufs[ch].slice(0, this.filled);
7271
+ }
7272
+ this.outStream.write(restBufs);
7273
+ this.outStream.flush();
7274
+ this.sentFrames += this.filled;
7275
+ this.filled = 0;
7276
+ }
7277
+ }
7278
+ close() {
7279
+ this.outStream.close();
7280
+ }
7281
+ }
7282
+
7283
+ class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
7284
+ constructor(outStream, chunkDurationSeconds) {
7285
+ super(outStream);
7286
+ this.chunkDurationSeconds = chunkDurationSeconds;
7287
+ this.sampleRate = null;
7288
+ this.sequenceAudioFloat32OutStream = outStream;
7289
+ }
7290
+ setFormat(channels, sampleRate) {
7291
+ console.debug("SequenceAudioFloat32ChunkerOutStream:setFormat(channels: " + channels + ",sampleRate: " + sampleRate + ")");
7292
+ this.channels = channels;
7293
+ this.sampleRate = sampleRate;
7294
+ this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
7295
+ console.debug("SequenceAudioFloat32ChunkerOutStream: chunkSize: " + this.chunkSize);
7296
+ this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
7297
+ }
7298
+ nextStream() {
7299
+ this.sequenceAudioFloat32OutStream.nextStream();
7300
+ }
7301
+ }
7302
+ /**
7303
+ * Streams a SequenceAudioFloat32OutStream to multiple streams
7304
+ */
7305
+ class SequenceAudioFloat32OutStreamMultiplier {
7306
+ constructor() {
7307
+ this._sequenceAudioFloat32OutStreams = new Array();
7308
+ }
7309
+ get sequenceAudioFloat32OutStreams() {
7310
+ return this._sequenceAudioFloat32OutStreams;
7311
+ }
7312
+ setFormat(channels, sampleRate) {
7313
+ for (let os of this._sequenceAudioFloat32OutStreams) {
7314
+ os.setFormat(channels, sampleRate);
7315
+ }
7316
+ }
7317
+ nextStream() {
7318
+ for (let os of this._sequenceAudioFloat32OutStreams) {
7319
+ os.nextStream();
7320
+ }
7321
+ }
7322
+ close() {
7323
+ for (let os of this._sequenceAudioFloat32OutStreams) {
7324
+ os.close();
7325
+ }
7326
+ }
7327
+ flush() {
7328
+ for (let os of this._sequenceAudioFloat32OutStreams) {
7329
+ os.flush();
7330
+ }
7331
+ }
7332
+ write(buffers) {
7333
+ let toWrite = buffers.length;
7334
+ for (let os of this._sequenceAudioFloat32OutStreams) {
7335
+ os.write(buffers);
7336
+ }
7337
+ return toWrite;
7338
+ }
7339
+ }
7340
+
7263
7341
  const FORCE_REQUEST_AUDIO_PERMISSIONS = false;
7264
7342
  const RECFILE_API_CTX = 'recfile';
7265
7343
  const MAX_RECORDING_TIME_MS = 1000 * 60 * 60 * 60; // 1 hour
7266
7344
  const LEVEL_BAR_INTERVALL_SECONDS = 0.1; // 100ms
7267
- class BasicRecorder {
7268
- constructor(dialog, sessionService) {
7345
+ class ChunkManager {
7346
+ constructor(chunkAudioBufferReceiver) {
7347
+ this.chunkAudioBufferReceiver = chunkAudioBufferReceiver;
7348
+ this.channels = 0;
7349
+ this.sampleRate = -1;
7350
+ this.chunkIdx = 0;
7351
+ }
7352
+ set recordingFile(value) {
7353
+ this._rf = value;
7354
+ }
7355
+ close() {
7356
+ // Nothing to do
7357
+ }
7358
+ flush() {
7359
+ this.chunkAudioBufferReceiver.postAudioStreamEnd(this.chunkIdx);
7360
+ }
7361
+ nextStream() {
7362
+ // reset chunk counter
7363
+ this.chunkIdx = 0;
7364
+ this.chunkAudioBufferReceiver.postAudioStreamStart();
7365
+ }
7366
+ setFormat(channels, sampleRate) {
7367
+ this.channels = channels;
7368
+ this.sampleRate = sampleRate;
7369
+ }
7370
+ write(buffers) {
7371
+ let aCtx = AudioContextProvider.audioContextInstance();
7372
+ let bChs = buffers.length;
7373
+ let frameLen = 0;
7374
+ if (aCtx && bChs > 0) {
7375
+ frameLen = buffers[0].length;
7376
+ let ad = aCtx.createBuffer(this.channels, frameLen, this.sampleRate);
7377
+ for (let ch = 0; ch < this.channels; ch++) {
7378
+ ad.copyToChannel(buffers[ch], ch);
7379
+ }
7380
+ this.chunkAudioBufferReceiver.postChunkAudioBuffer(ad, this.chunkIdx);
7381
+ this.chunkIdx++;
7382
+ }
7383
+ return frameLen;
7384
+ }
7385
+ }
7386
+ let BasicRecorder = class BasicRecorder {
7387
+ constructor(dialog, sessionService, uploader, config) {
7269
7388
  this.dialog = dialog;
7270
7389
  this.sessionService = sessionService;
7390
+ this.uploader = uploader;
7391
+ this.config = config;
7271
7392
  this.statusMsg = '';
7272
7393
  this.statusWaiting = false;
7273
7394
  this.readonly = false;
7395
+ this.rfUuid = null;
7274
7396
  this.processingRecording = false;
7275
7397
  this.ac = null;
7276
7398
  this._wakeLock = false;
7277
7399
  this._selectedDeviceId = undefined;
7278
7400
  this._channelCount = 2;
7279
7401
  this._session = null;
7402
+ this.startedDate = null;
7280
7403
  this.uploadProgress = 100;
7281
7404
  this.uploadStatus = 'ok';
7282
7405
  this.audioSignalCollapsed = true;
@@ -7286,7 +7409,10 @@ class BasicRecorder {
7286
7409
  this.audioFetchSubscription = null;
7287
7410
  this.destroyed = false;
7288
7411
  this.navigationDisabled = true;
7412
+ // Default: Do not try to keep the device awake
7289
7413
  this.noSleep = null;
7414
+ // Default: Disabled chunked upload
7415
+ this._uploadChunkSizeSeconds = null;
7290
7416
  this.userAgent = UserAgentBuilder.userAgent();
7291
7417
  console.debug("Detected platform: " + this.userAgent.detectedPlatform);
7292
7418
  console.debug("Detected browser: " + this.userAgent.detectedBrowser);
@@ -7296,6 +7422,16 @@ class BasicRecorder {
7296
7422
  this.streamLevelMeasure = new StreamLevelMeasure();
7297
7423
  this.selCaptureDeviceId = null;
7298
7424
  }
7425
+ get uploadChunkSizeSeconds() {
7426
+ return this._uploadChunkSizeSeconds;
7427
+ }
7428
+ set uploadChunkSizeSeconds(value) {
7429
+ let oldValue = this.uploadChunkSizeSeconds;
7430
+ this._uploadChunkSizeSeconds = value;
7431
+ if (value !== oldValue) {
7432
+ this.configureStreamCaptureStream();
7433
+ }
7434
+ }
7299
7435
  get wakeLock() {
7300
7436
  return this._wakeLock;
7301
7437
  }
@@ -7309,12 +7445,14 @@ class BasicRecorder {
7309
7445
  }
7310
7446
  if (!this.noSleep.isEnabled) {
7311
7447
  this.noSleep.enable();
7448
+ console.debug("Enabled wake lock.");
7312
7449
  }
7313
7450
  }
7314
7451
  }
7315
7452
  disableWakeLockCond() {
7316
7453
  if (this.noSleep && this.noSleep.isEnabled) {
7317
7454
  this.noSleep.disable();
7455
+ console.debug("Disabled wake lock.");
7318
7456
  }
7319
7457
  }
7320
7458
  set audioDevices(audioDevices) {
@@ -7336,6 +7474,27 @@ class BasicRecorder {
7336
7474
  this.statusAlertType = 'info';
7337
7475
  this.statusMsg = 'Ready.';
7338
7476
  }
7477
+ configureStreamCaptureStream() {
7478
+ let outStream;
7479
+ if (this.uploadChunkSizeSeconds) {
7480
+ // Multiply the capture stream to...
7481
+ let sasm = new SequenceAudioFloat32OutStreamMultiplier();
7482
+ // ...upload audio data in chunks...
7483
+ let chMng = new ChunkManager(this); // The chunk manager connects the chunked audio stream to this class to upload the chunks
7484
+ let chOsUpload = new SequenceAudioFloat32ChunkerOutStream(chMng, this.uploadChunkSizeSeconds); // The audio stream chunker itself
7485
+ sasm.sequenceAudioFloat32OutStreams.push(chOsUpload);
7486
+ // ...and to measure the level
7487
+ let chOsLvlMeas = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
7488
+ sasm.sequenceAudioFloat32OutStreams.push(chOsLvlMeas);
7489
+ outStream = sasm;
7490
+ }
7491
+ else {
7492
+ outStream = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
7493
+ }
7494
+ if (this.ac) {
7495
+ this.ac.audioOutStream = outStream;
7496
+ }
7497
+ }
7339
7498
  start() {
7340
7499
  this.statusAlertType = 'info';
7341
7500
  this.statusMsg = 'Starting session...';
@@ -7542,6 +7701,75 @@ class BasicRecorder {
7542
7701
  });
7543
7702
  }
7544
7703
  }
7704
+ startItem() {
7705
+ this.startedDate = null;
7706
+ this.enableWakeLockCond();
7707
+ this.rfUuid = UUID.generate();
7708
+ this.transportActions.startAction.disabled = true;
7709
+ this.transportActions.pauseAction.disabled = true;
7710
+ if (this.readonly) {
7711
+ return;
7712
+ }
7713
+ }
7714
+ started() {
7715
+ if (!this.startedDate) {
7716
+ this.startedDate = new Date();
7717
+ }
7718
+ this.transportActions.startAction.disabled = true;
7719
+ }
7720
+ postRecording(wavFile, recUrl) {
7721
+ let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
7722
+ let ul = new Upload(wavBlob, recUrl);
7723
+ this.uploader.queueUpload(ul);
7724
+ }
7725
+ postAudioStreamStart() {
7726
+ var _a;
7727
+ if (this.rfUuid) {
7728
+ let apiEndPoint = '';
7729
+ if (this.config && this.config.apiEndPoint) {
7730
+ apiEndPoint = this.config.apiEndPoint;
7731
+ }
7732
+ if (apiEndPoint !== '') {
7733
+ apiEndPoint = apiEndPoint + '/';
7734
+ }
7735
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
7736
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/prepareChunksRequest';
7737
+ let fd = new FormData();
7738
+ // Note: At least one parameter must be set
7739
+ fd.set('uuid', this.rfUuid);
7740
+ if (!this.startedDate) {
7741
+ this.startedDate = new Date();
7742
+ }
7743
+ fd.set('startedDate', this.startedDate.toJSON());
7744
+ let ul = new Upload(fd, recUrl);
7745
+ this.uploader.queueUpload(ul);
7746
+ }
7747
+ else {
7748
+ console.error("Recording file UUID not set!");
7749
+ }
7750
+ }
7751
+ postAudioStreamEnd(chunkCount) {
7752
+ var _a;
7753
+ if (this.rfUuid) {
7754
+ let apiEndPoint = '';
7755
+ if (this.config && this.config.apiEndPoint) {
7756
+ apiEndPoint = this.config.apiEndPoint;
7757
+ }
7758
+ if (apiEndPoint !== '') {
7759
+ apiEndPoint = apiEndPoint + '/';
7760
+ }
7761
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
7762
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/concatChunksRequest';
7763
+ let fd = new FormData();
7764
+ fd.set('uuid', this.rfUuid);
7765
+ fd.set('chunkCount', chunkCount.toString());
7766
+ let ul = new Upload(fd, recUrl);
7767
+ this.uploader.queueUpload(ul);
7768
+ }
7769
+ else {
7770
+ console.error("Recording file UUID not set!");
7771
+ }
7772
+ }
7545
7773
  closed() {
7546
7774
  this.statusAlertType = 'info';
7547
7775
  this.statusMsg = 'Session closed.';
@@ -7558,7 +7786,11 @@ class BasicRecorder {
7558
7786
  }
7559
7787
  });
7560
7788
  }
7561
- }
7789
+ };
7790
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7791
+ BasicRecorder = __decorate([
7792
+ __param(3, Inject(SPEECHRECORDER_CONFIG))
7793
+ ], BasicRecorder);
7562
7794
 
7563
7795
  /**
7564
7796
  * Created by klausj on 17.06.2017.
@@ -7872,7 +8104,7 @@ const DEFAULT_PRE_REC_DELAY = 1000;
7872
8104
  const DEFAULT_POST_REC_DELAY = 500;
7873
8105
  class SessionManager extends BasicRecorder {
7874
8106
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
7875
- super(dialog, sessionService);
8107
+ super(dialog, sessionService, uploader, config);
7876
8108
  this.changeDetectorRef = changeDetectorRef;
7877
8109
  this.renderer = renderer;
7878
8110
  this.dialog = dialog;
@@ -7975,7 +8207,7 @@ class SessionManager extends BasicRecorder {
7975
8207
  if (this.ac) {
7976
8208
  this.transportActions.startAction.onAction = () => this.startItem();
7977
8209
  this.ac.listener = this;
7978
- this.ac.audioOutStream = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
8210
+ this.configureStreamCaptureStream();
7979
8211
  // Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
7980
8212
  //this.ac.listDevices();
7981
8213
  }
@@ -8121,12 +8353,7 @@ class SessionManager extends BasicRecorder {
8121
8353
  }
8122
8354
  }
8123
8355
  startItem() {
8124
- this.enableWakeLockCond();
8125
- this.transportActions.startAction.disabled = true;
8126
- this.transportActions.pauseAction.disabled = true;
8127
- if (this.readonly) {
8128
- return;
8129
- }
8356
+ super.startItem();
8130
8357
  this.transportActions.fwdAction.disabled = true;
8131
8358
  this.transportActions.fwdNextAction.disabled = true;
8132
8359
  this.transportActions.bwdAction.disabled = true;
@@ -8425,8 +8652,8 @@ class SessionManager extends BasicRecorder {
8425
8652
  }
8426
8653
  }
8427
8654
  started() {
8655
+ super.started();
8428
8656
  this.status = 2 /* PRE_RECORDING */;
8429
- this.transportActions.startAction.disabled = true;
8430
8657
  this.startStopSignalState = 1 /* PRERECORDING */;
8431
8658
  if (this._session) {
8432
8659
  if (this._session.status === "LOADED") {
@@ -8595,7 +8822,7 @@ class SessionManager extends BasicRecorder {
8595
8822
  rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8596
8823
  it.recs.push(rf);
8597
8824
  }
8598
- if (this.enableUploadRecordings) {
8825
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8599
8826
  // TODO use SpeechRecorderconfig resp. RecfileService
8600
8827
  // convert asynchronously to 16-bit integer PCM
8601
8828
  // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
@@ -8674,6 +8901,25 @@ class SessionManager extends BasicRecorder {
8674
8901
  }
8675
8902
  this.changeDetectorRef.detectChanges();
8676
8903
  }
8904
+ postChunkAudioBuffer(audioBuffer, chunkIdx) {
8905
+ var _a;
8906
+ this.processingRecording = true;
8907
+ let ww = new WavWriter();
8908
+ //new REST API URL
8909
+ let apiEndPoint = '';
8910
+ if (this.config && this.config.apiEndPoint) {
8911
+ apiEndPoint = this.config.apiEndPoint;
8912
+ }
8913
+ if (apiEndPoint !== '') {
8914
+ apiEndPoint = apiEndPoint + '/';
8915
+ }
8916
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
8917
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.promptItem.itemcode + '/' + this.rfUuid + '/' + chunkIdx;
8918
+ ww.writeAsync(audioBuffer, (wavFile) => {
8919
+ this.postRecording(wavFile, recUrl);
8920
+ this.processingRecording = false;
8921
+ });
8922
+ }
8677
8923
  postRecording(wavFile, recUrl) {
8678
8924
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
8679
8925
  let ul = new Upload(wavBlob, recUrl);
@@ -8853,6 +9099,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
8853
9099
  }] } });
8854
9100
 
8855
9101
  class RecorderComponent {
9102
+ constructor(uploader) {
9103
+ this.uploader = uploader;
9104
+ this.dataSaved = true;
9105
+ }
8856
9106
  }
8857
9107
 
8858
9108
  /**
@@ -8905,7 +9155,7 @@ var Mode;
8905
9155
  })(Mode || (Mode = {}));
8906
9156
  class SpeechrecorderngComponent extends RecorderComponent {
8907
9157
  constructor(route, router, changeDetectorRef, sessionsService, projectService, scriptService, recFilesService, uploader) {
8908
- super();
9158
+ super(uploader);
8909
9159
  this.route = route;
8910
9160
  this.router = router;
8911
9161
  this.changeDetectorRef = changeDetectorRef;
@@ -8915,7 +9165,6 @@ class SpeechrecorderngComponent extends RecorderComponent {
8915
9165
  this.recFilesService = recFilesService;
8916
9166
  this.uploader = uploader;
8917
9167
  this._project = null;
8918
- this.dataSaved = true;
8919
9168
  }
8920
9169
  ngOnInit() {
8921
9170
  try {
@@ -8958,39 +9207,39 @@ class SpeechrecorderngComponent extends RecorderComponent {
8958
9207
  }
8959
9208
  }
8960
9209
  fetchSession(sessionId) {
8961
- //Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'statusMsg: Player initialized.'. Current value: 'statusMsg: Fetching session info...'.
8962
- // params.subscribe seems not to be asynchronous
8963
- // this.sm.statusAlertType='info';
8964
- // this.sm.statusMsg = 'Fetching session info...';
8965
- // this.sm.statusWaiting=true;
8966
9210
  let sessObs = this.sessionsService.sessionObserver(sessionId);
8967
9211
  if (sessObs) {
8968
- sessObs.subscribe(sess => {
8969
- this.setSession(sess);
8970
- this.sm.statusAlertType = 'info';
8971
- this.sm.statusMsg = 'Received session info.';
8972
- this.sm.statusWaiting = false;
8973
- if (sess.project) {
8974
- //console.debug("Session associated project: "+sess.project)
8975
- this.projectService.projectObservable(sess.project).subscribe(project => {
8976
- this.project = project;
9212
+ sessObs.subscribe({
9213
+ next: sess => {
9214
+ this.setSession(sess);
9215
+ this.sm.statusAlertType = 'info';
9216
+ this.sm.statusMsg = 'Received session info.';
9217
+ this.sm.statusWaiting = false;
9218
+ if (sess.project) {
9219
+ //console.debug("Session associated project: "+sess.project)
9220
+ this.projectService.projectObservable(sess.project).subscribe({
9221
+ next: (project) => {
9222
+ this.project = project;
9223
+ this.fetchScript(sess);
9224
+ }, error: (reason) => {
9225
+ this.sm.statusMsg = reason;
9226
+ this.sm.statusAlertType = 'error';
9227
+ this.sm.statusWaiting = false;
9228
+ console.error("Error fetching project config: " + reason);
9229
+ }
9230
+ });
9231
+ }
9232
+ else {
9233
+ console.info("Session has no associated project. Using default configuration.");
8977
9234
  this.fetchScript(sess);
8978
- }, reason => {
8979
- this.sm.statusMsg = reason;
8980
- this.sm.statusAlertType = 'error';
8981
- this.sm.statusWaiting = false;
8982
- console.error("Error fetching project config: " + reason);
8983
- });
9235
+ }
9236
+ },
9237
+ error: (reason) => {
9238
+ this.sm.statusMsg = reason;
9239
+ this.sm.statusAlertType = 'error';
9240
+ this.sm.statusWaiting = false;
9241
+ console.error("Error fetching session " + reason);
8984
9242
  }
8985
- else {
8986
- console.info("Session has no associated project. Using default configuration.");
8987
- this.fetchScript(sess);
8988
- }
8989
- }, (reason) => {
8990
- this.sm.statusMsg = reason;
8991
- this.sm.statusAlertType = 'error';
8992
- this.sm.statusWaiting = false;
8993
- console.error("Error fetching session " + reason);
8994
9243
  });
8995
9244
  }
8996
9245
  }
@@ -8999,19 +9248,21 @@ class SpeechrecorderngComponent extends RecorderComponent {
8999
9248
  this.sm.statusAlertType = 'info';
9000
9249
  this.sm.statusMsg = 'Fetching recording script...';
9001
9250
  this.sm.statusWaiting = true;
9002
- this.scriptService.scriptObservable(sess.script).subscribe(script => {
9003
- this.sm.statusAlertType = 'info';
9004
- this.sm.statusMsg = 'Received recording script.';
9005
- this.sm.statusWaiting = false;
9006
- this.setScript(script);
9007
- this.sm.session = sess;
9008
- this.fetchRecordings(sess, this.script);
9009
- }, reason => {
9010
- let errMsg = "Error fetching recording script: " + reason;
9011
- console.error(errMsg);
9012
- this.sm.statusMsg = errMsg;
9013
- this.sm.statusAlertType = 'error';
9014
- this.sm.statusWaiting = false;
9251
+ this.scriptService.scriptObservable(sess.script).subscribe({
9252
+ next: (script) => {
9253
+ this.sm.statusAlertType = 'info';
9254
+ this.sm.statusMsg = 'Received recording script.';
9255
+ this.sm.statusWaiting = false;
9256
+ this.setScript(script);
9257
+ this.sm.session = sess;
9258
+ this.fetchRecordings(sess, this.script);
9259
+ }, error: (reason) => {
9260
+ let errMsg = "Error fetching recording script: " + reason;
9261
+ console.error(errMsg);
9262
+ this.sm.statusMsg = errMsg;
9263
+ this.sm.statusAlertType = 'error';
9264
+ this.sm.statusWaiting = false;
9265
+ }
9015
9266
  });
9016
9267
  }
9017
9268
  else {
@@ -9028,29 +9279,31 @@ class SpeechrecorderngComponent extends RecorderComponent {
9028
9279
  let prNm = null;
9029
9280
  if (this.project) {
9030
9281
  let rfsObs = this.recFilesService.recordingFileDescrList(this.project.name, sess.sessionId);
9031
- rfsObs.subscribe((rfs) => {
9032
- this.sm.statusAlertType = 'info';
9033
- this.sm.statusMsg = 'Received infos of recordings.';
9034
- this.sm.statusWaiting = false;
9035
- if (rfs) {
9036
- if (rfs instanceof Array) {
9037
- rfs.forEach((rf) => {
9038
- //console.debug("Already recorded: " + rf+ " "+rf.recording.itemcode);
9039
- this.sm.addRecordingFileByDescriptor(rf);
9040
- });
9282
+ rfsObs.subscribe({
9283
+ next: (rfs) => {
9284
+ this.sm.statusAlertType = 'info';
9285
+ this.sm.statusMsg = 'Received infos of recordings.';
9286
+ this.sm.statusWaiting = false;
9287
+ if (rfs) {
9288
+ if (rfs instanceof Array) {
9289
+ rfs.forEach((rf) => {
9290
+ //console.debug("Already recorded: " + rf+ " "+rf.recording.itemcode);
9291
+ this.sm.addRecordingFileByDescriptor(rf);
9292
+ });
9293
+ }
9294
+ else {
9295
+ console.error('Expected type array for list of already recorded files ');
9296
+ }
9041
9297
  }
9042
9298
  else {
9043
- console.error('Expected type array for list of already recorded files ');
9299
+ //console.debug("Recording file list: " + rfs);
9044
9300
  }
9301
+ }, error: (err) => {
9302
+ // we start the session anyway
9303
+ this.startSession();
9304
+ }, complete: () => {
9305
+ this.startSession();
9045
9306
  }
9046
- else {
9047
- //console.debug("Recording file list: " + rfs);
9048
- }
9049
- }, () => {
9050
- // we start the session anyway
9051
- this.startSession();
9052
- }, () => {
9053
- this.startSession();
9054
9307
  });
9055
9308
  }
9056
9309
  else {
@@ -9073,36 +9326,10 @@ class SpeechrecorderngComponent extends RecorderComponent {
9073
9326
  return this.dataSaved && !this.sm.isActive();
9074
9327
  }
9075
9328
  init() {
9076
- var n = navigator;
9077
- //var getUserMediaFnct= n.getUserMedia || n.webkitGetUserMedia ||
9078
- // n.mozGetUserMedia || n.msGetUserMedia;
9079
- var w = window;
9080
- // TODO test onyl !!
9081
- // let debugFail=true;
9082
- // AudioContext = w.AudioContext || w.webkitAudioContext;
9083
- // if (typeof AudioContext !== 'function' || debugFail) {
9084
- // this.sm.statusAlertType='error';
9085
- // this.sm.statusMsg = 'ERROR: Browser does not support Web Audio API!';
9086
- // return false;
9087
- // } else {
9088
- // var context = new AudioContext();
9089
- //
9090
- // if (typeof navigator.mediaDevices.getUserMedia !== 'function') {
9091
- // this.sm.statusAlertType='error';
9092
- // this.sm.statusMsg= 'ERROR: Browser does not support Media streams!';
9093
- // return false;
9094
- // } else {
9095
- //
9096
- //this.sm = new SessionManager(new SimpleTrafficLight(), this.uploader);
9097
- //this.sm.init();
9098
- //this.ap = new AudioPlayer(context,this);
9099
- //this.sm.listener=this;
9100
- // }
9101
- // }
9329
+ //TODO Duplicate code in AudioRecorderComponent
9102
9330
  this.uploader.listener = (ue) => {
9103
9331
  this.uploadUpdate(ue);
9104
9332
  };
9105
- //TODO Duplicate code in AudioRecorderComponent
9106
9333
  window.addEventListener('beforeunload', (e) => {
9107
9334
  console.debug("Before page unload event");
9108
9335
  if (this.ready()) {
@@ -9191,6 +9418,13 @@ class SpeechrecorderngComponent extends RecorderComponent {
9191
9418
  chCnt = ProjectUtil.audioChannelCount(project);
9192
9419
  console.info("Project requested recording channel count: " + chCnt);
9193
9420
  this.sm.autoGainControlConfigs = project.autoGainControlConfigs;
9421
+ if (project.chunkedRecording === true) {
9422
+ console.debug("Enable chunked upload: chunkSize: " + BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS);
9423
+ this.sm.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
9424
+ }
9425
+ else {
9426
+ this.sm.uploadChunkSizeSeconds = null;
9427
+ }
9194
9428
  }
9195
9429
  else {
9196
9430
  console.error("Empty project configuration!");
@@ -9227,50 +9461,7 @@ class SpeechrecorderngComponent extends RecorderComponent {
9227
9461
  pLoader.send();
9228
9462
  }
9229
9463
  }
9230
- // loadScript(callback: ()=>any){
9231
- // var scrLoader = new XMLHttpRequest();
9232
- // let hn:string =window.location.hostname;
9233
- // let pn:string =window.location.pathname;
9234
- // let pr:string =window.location.protocol;
9235
- // let po:string =window.location.port;
9236
- //
9237
- // let sessionId:string=this.session.sessionId;
9238
- //
9239
- // let scrUrl:string | null = null;
9240
- //
9241
- // if (hn === 'localhost' || this.mode===Mode.DEMO) {
9242
- // // local debug mode
9243
- // // FF caches
9244
- // scrUrl = 'test/' + this.session.script.toString() + '.json?' + new Date().getTime();
9245
- //
9246
- // } else {
9247
- // let scrPath:string = '/wikispeech/session/scripts/servlet';
9248
- // let scrQu:string = 'sessionId=' + sessionId;
9249
- // scrUrl = pr + '//' + hn + ':' + po + scrPath;
9250
- // if (scrQu) {
9251
- // scrUrl = scrUrl + '?' + scrQu;
9252
- // }
9253
- // }
9254
- //
9255
- // scrLoader.open("GET",scrUrl , true);
9256
- // scrLoader.setRequestHeader('Accept','application/json');
9257
- // scrLoader.responseType = "json";
9258
- // scrLoader.onload = (e) => {
9259
- //
9260
- // this.script = scrLoader.response;
9261
- //
9262
- //
9263
- // this.sm.script = this.script;
9264
- //
9265
- // callback();
9266
- // }
9267
- // scrLoader.onerror = (e) => {
9268
- // console.log("Error downloading recording script data ...");
9269
- // }
9270
- // scrLoader.send();
9271
- // }
9272
9464
  start() {
9273
- //this.configure();
9274
9465
  }
9275
9466
  audioPlayerUpdate(e) {
9276
9467
  if (EventType.STARTED === e.type) {
@@ -10957,13 +11148,12 @@ class Item {
10957
11148
  }
10958
11149
  }
10959
11150
  class AudioRecorder extends BasicRecorder {
10960
- constructor(changeDetectorRef, renderer, route, dialog, projectService, sessionService, recFileService, uploader, config) {
10961
- super(dialog, sessionService);
11151
+ constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11152
+ super(dialog, sessionService, uploader, config);
10962
11153
  this.changeDetectorRef = changeDetectorRef;
10963
11154
  this.renderer = renderer;
10964
11155
  this.route = route;
10965
11156
  this.dialog = dialog;
10966
- this.projectService = projectService;
10967
11157
  this.sessionService = sessionService;
10968
11158
  this.recFileService = recFileService;
10969
11159
  this.uploader = uploader;
@@ -10974,7 +11164,6 @@ class AudioRecorder extends BasicRecorder {
10974
11164
  this.enableDownloadRecordings = false;
10975
11165
  this.status = 0 /* BLOCKED */;
10976
11166
  this.dataSaved = true;
10977
- this.startedDate = null;
10978
11167
  this.maxRecTimerId = null;
10979
11168
  this.maxRecTimerRunning = false;
10980
11169
  this._promptIndex = null;
@@ -11065,7 +11254,7 @@ class AudioRecorder extends BasicRecorder {
11065
11254
  if (this.ac) {
11066
11255
  this.transportActions.startAction.onAction = () => this.startItem();
11067
11256
  this.ac.listener = this;
11068
- this.ac.audioOutStream = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
11257
+ this.configureStreamCaptureStream();
11069
11258
  // Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
11070
11259
  //this.ac.listDevices();
11071
11260
  }
@@ -11092,23 +11281,6 @@ class AudioRecorder extends BasicRecorder {
11092
11281
  this.uploader.listener = (ue) => {
11093
11282
  this.uploadUpdate(ue);
11094
11283
  };
11095
- let wakeLockSupp = ('wakeLock' in navigator);
11096
- // if(wakeLockSupp) {
11097
- // let wakeLock = null;
11098
- // try {
11099
- // //@ts-ignore
11100
- // wakeLock = navigator.wakeLock.request('screen');
11101
- //
11102
- // //statusElem.textContent = 'Wake Lock is active!';
11103
- // } catch (err) {
11104
- // // The Wake Lock request has failed - usually system related, such as battery.
11105
- // console.error('Wakelock failed'+err)
11106
- // }
11107
- // }else{
11108
- // let noSleep=new NoSleep();
11109
- // noSleep.enable();
11110
- //
11111
- // }
11112
11284
  }
11113
11285
  onKeyPress(ke) {
11114
11286
  if (ke.key == ' ') {
@@ -11161,36 +11333,36 @@ class AudioRecorder extends BasicRecorder {
11161
11333
  let prNm = null;
11162
11334
  if (this.project) {
11163
11335
  let rfsObs = this.recFileService.recordingFileList(this.project.name, sess.sessionId);
11164
- rfsObs.subscribe((rfs) => {
11165
- this.statusAlertType = 'info';
11166
- this.statusMsg = 'Received infos of recordings.';
11167
- this.statusWaiting = false;
11168
- if (rfs) {
11169
- if (rfs instanceof Array) {
11170
- rfs.forEach((rf) => {
11171
- if (rf.startedDate) {
11172
- rf._startedAsDateObj = new Date(rf.startedDate);
11173
- }
11174
- if (rf.date) {
11175
- rf._dateAsDateObj = new Date(rf.date);
11176
- }
11177
- this.recorderCombiPane.push(rf);
11178
- });
11336
+ rfsObs.subscribe({ next: (rfs) => {
11337
+ this.statusAlertType = 'info';
11338
+ this.statusMsg = 'Received infos of recordings.';
11339
+ this.statusWaiting = false;
11340
+ if (rfs) {
11341
+ if (rfs instanceof Array) {
11342
+ rfs.forEach((rf) => {
11343
+ if (rf.startedDate) {
11344
+ rf._startedAsDateObj = new Date(rf.startedDate);
11345
+ }
11346
+ if (rf.date) {
11347
+ rf._dateAsDateObj = new Date(rf.date);
11348
+ }
11349
+ this.recorderCombiPane.push(rf);
11350
+ });
11351
+ }
11352
+ else {
11353
+ console.error('Expected type array for list of already recorded files ');
11354
+ }
11179
11355
  }
11180
11356
  else {
11181
- console.error('Expected type array for list of already recorded files ');
11357
+ //console.debug("Recording file list: " + rfs);
11182
11358
  }
11183
- }
11184
- else {
11185
- //console.debug("Recording file list: " + rfs);
11186
- }
11187
- }, () => {
11188
- // Failed fetching existing, but we start the session anyway
11189
- this.start();
11190
- }, () => {
11191
- // Normal start
11192
- this.start();
11193
- });
11359
+ }, error: (err) => {
11360
+ // Failed fetching existing, but we start the session anyway
11361
+ this.start();
11362
+ }, complete: () => {
11363
+ // Normal start
11364
+ this.start();
11365
+ } });
11194
11366
  }
11195
11367
  else {
11196
11368
  // No project def -> error
@@ -11212,6 +11384,12 @@ class AudioRecorder extends BasicRecorder {
11212
11384
  chCnt = ProjectUtil.audioChannelCount(project);
11213
11385
  console.info("Project requested recording channel count: " + chCnt);
11214
11386
  this.autoGainControlConfigs = project.autoGainControlConfigs;
11387
+ if (project.chunkedRecording === true) {
11388
+ this.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
11389
+ }
11390
+ else {
11391
+ this.uploadChunkSizeSeconds = null;
11392
+ }
11215
11393
  }
11216
11394
  else {
11217
11395
  console.error("Empty project configuration!");
@@ -11311,12 +11489,7 @@ class AudioRecorder extends BasicRecorder {
11311
11489
  }
11312
11490
  }
11313
11491
  startItem() {
11314
- this.enableWakeLockCond();
11315
- this.transportActions.startAction.disabled = true;
11316
- this.transportActions.pauseAction.disabled = true;
11317
- if (this.readonly) {
11318
- return;
11319
- }
11492
+ super.startItem();
11320
11493
  this.transportActions.fwdAction.disabled = true;
11321
11494
  this.transportActions.fwdNextAction.disabled = true;
11322
11495
  this.transportActions.bwdAction.disabled = true;
@@ -11468,8 +11641,7 @@ class AudioRecorder extends BasicRecorder {
11468
11641
  }
11469
11642
  }
11470
11643
  started() {
11471
- this.startedDate = new Date();
11472
- this.transportActions.startAction.disabled = true;
11644
+ super.started();
11473
11645
  this.statusAlertType = 'info';
11474
11646
  this.statusMsg = 'Recording...';
11475
11647
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
@@ -11502,24 +11674,6 @@ class AudioRecorder extends BasicRecorder {
11502
11674
  this.ac.stop();
11503
11675
  }
11504
11676
  }
11505
- // addRecordingFileByDescriptor(rfd:RecordingFileDescriptorImpl){
11506
- // let prIdx=0;
11507
- // if(this.items) {
11508
- // let it = this.items[prIdx];
11509
- // if (it) {
11510
- // if (!it.recs) {
11511
- // it.recs = new Array<SprRecordingFile>();
11512
- // }
11513
- //
11514
- // } else {
11515
- // //console.debug("WARN: No recording item with code: \"" +rfd.recording.itemcode+ "\" found.");
11516
- // }
11517
- // }
11518
- // }
11519
- //
11520
- // addRecordingFileByPromptIndex(promptIndex:number, rf:SprRecordingFile){
11521
- //
11522
- // }
11523
11677
  stopped() {
11524
11678
  this.updateStartActionDisableState();
11525
11679
  this.transportActions.stopAction.disabled = true;
@@ -11534,41 +11688,39 @@ class AudioRecorder extends BasicRecorder {
11534
11688
  if (this._session) {
11535
11689
  sessId = this._session.sessionId;
11536
11690
  }
11537
- let rf = new RecordingFile(UUID.generate(), sessId, ad);
11691
+ if (!this.rfUuid) {
11692
+ this.rfUuid = UUID.generate();
11693
+ }
11694
+ let rf = new RecordingFile(this.rfUuid, sessId, ad);
11538
11695
  rf._startedAsDateObj = this.startedDate;
11539
11696
  if (rf._startedAsDateObj) {
11540
11697
  rf.startedDate = rf._startedAsDateObj.toString();
11541
11698
  }
11542
- let apiEndPoint = '';
11543
- if (this.config && this.config.apiEndPoint) {
11544
- apiEndPoint = this.config.apiEndPoint;
11545
- }
11546
- if (apiEndPoint !== '') {
11547
- apiEndPoint = apiEndPoint + '/';
11699
+ rf.frames = ad.length;
11700
+ this.displayRecFile = rf;
11701
+ this.recorderCombiPane.push(rf);
11702
+ // Upload if upload enabled and not in chunked upload mode
11703
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
11704
+ let apiEndPoint = '';
11705
+ if (this.config && this.config.apiEndPoint) {
11706
+ apiEndPoint = this.config.apiEndPoint;
11707
+ }
11708
+ if (apiEndPoint !== '') {
11709
+ apiEndPoint = apiEndPoint + '/';
11710
+ }
11711
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11712
+ let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11713
+ // convert asynchronously to 16-bit integer PCM
11714
+ // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11715
+ // TODO duplicate conversion for manual download
11716
+ this.processingRecording = true;
11717
+ let ww = new WavWriter();
11718
+ ww.writeAsync(ad, (wavFile) => {
11719
+ this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11720
+ this.processingRecording = false;
11721
+ this.changeDetectorRef.detectChanges();
11722
+ });
11548
11723
  }
11549
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11550
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11551
- //
11552
- //
11553
- //
11554
- // // convert asynchronously to 16-bit integer PCM
11555
- // // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11556
- // // TODO duplicate conversion for manual download
11557
- // //console.log("Build wav writer...");
11558
- this.processingRecording = true;
11559
- let ww = new WavWriter();
11560
- ww.writeAsync(ad, (wavFile) => {
11561
- //this.postRecording(wavFile, recUrl);
11562
- //rf._dateAsDateObj=new Date();
11563
- rf.frames = ad.length;
11564
- this.displayRecFile = rf;
11565
- //this.recordingListComp.recordingList.push(rf);
11566
- this.recorderCombiPane.push(rf);
11567
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11568
- this.processingRecording = false;
11569
- this.changeDetectorRef.detectChanges();
11570
- });
11571
- // }
11572
11724
  }
11573
11725
  // check complete session
11574
11726
  let complete = true;
@@ -11579,11 +11731,6 @@ class AudioRecorder extends BasicRecorder {
11579
11731
  this.updateNavigationActions();
11580
11732
  this.changeDetectorRef.detectChanges();
11581
11733
  }
11582
- postRecording(wavFile, recUrl) {
11583
- let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11584
- let ul = new Upload(wavBlob, recUrl);
11585
- this.uploader.queueUpload(ul);
11586
- }
11587
11734
  postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
11588
11735
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11589
11736
  let fd = new FormData();
@@ -11600,6 +11747,25 @@ class AudioRecorder extends BasicRecorder {
11600
11747
  let ul = new Upload(fd, recUrl);
11601
11748
  this.uploader.queueUpload(ul);
11602
11749
  }
11750
+ postChunkAudioBuffer(audioBuffer, chunkIdx) {
11751
+ var _a;
11752
+ this.processingRecording = true;
11753
+ let ww = new WavWriter();
11754
+ //new REST API URL
11755
+ let apiEndPoint = '';
11756
+ if (this.config && this.config.apiEndPoint) {
11757
+ apiEndPoint = this.config.apiEndPoint;
11758
+ }
11759
+ if (apiEndPoint !== '') {
11760
+ apiEndPoint = apiEndPoint + '/';
11761
+ }
11762
+ let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11763
+ let recUrl = sessionsUrl + '/' + ((_a = this.session) === null || _a === void 0 ? void 0 : _a.sessionId) + '/' + RECFILE_API_CTX + '/' + this.rfUuid + '/' + chunkIdx;
11764
+ ww.writeAsync(audioBuffer, (wavFile) => {
11765
+ this.postRecording(wavFile, recUrl);
11766
+ this.processingRecording = false;
11767
+ });
11768
+ }
11603
11769
  stop() {
11604
11770
  if (this.ac) {
11605
11771
  this.ac.close();
@@ -11627,8 +11793,8 @@ class AudioRecorder extends BasicRecorder {
11627
11793
  }
11628
11794
  }
11629
11795
  }
11630
- AudioRecorder.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorder, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i1$1.ActivatedRoute }, { token: i1$2.MatDialog }, { token: ProjectService }, { token: SessionService }, { token: RecordingService }, { token: SpeechRecorderUploader }, { token: SPEECHRECORDER_CONFIG }], target: i0.ɵɵFactoryTarget.Component });
11631
- AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.2", type: AudioRecorder, selector: "app-audiorecorder", inputs: { _project: "_project", projectName: "projectName", dataSaved: "dataSaved" }, host: { listeners: { "window:keypress": "onKeyPress($event)", "window:keydown": "onKeyDown($event)" } }, providers: [SessionService], viewQueries: [{ propertyName: "recorderCombiPane", first: true, predicate: RecorderCombiPane, descendants: true, static: true }, { propertyName: "liveLevelDisplay", first: true, predicate: LevelBar, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
11796
+ AudioRecorder.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorder, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i1$1.ActivatedRoute }, { token: i1$2.MatDialog }, { token: SessionService }, { token: RecordingService }, { token: SpeechRecorderUploader }, { token: SPEECHRECORDER_CONFIG }], target: i0.ɵɵFactoryTarget.Component });
11797
+ AudioRecorder.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.2", type: AudioRecorder, selector: "app-audiorecorder", inputs: { projectName: "projectName", dataSaved: "dataSaved" }, host: { listeners: { "window:keypress": "onKeyPress($event)", "window:keydown": "onKeyDown($event)" } }, providers: [SessionService], viewQueries: [{ propertyName: "recorderCombiPane", first: true, predicate: RecorderCombiPane, descendants: true, static: true }, { propertyName: "liveLevelDisplay", first: true, predicate: LevelBar, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
11632
11798
  <app-warningbar [show]="isTestSession()" warningText="Test recording only!"></app-warningbar>
11633
11799
  <app-warningbar [show]="isDefaultAudioTestSession()"
11634
11800
  warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
@@ -11784,13 +11950,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11784
11950
  ]
11785
11951
  }]
11786
11952
  }], ctorParameters: function () {
11787
- return [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i1$1.ActivatedRoute }, { type: i1$2.MatDialog }, { type: ProjectService }, { type: SessionService }, { type: RecordingService }, { type: SpeechRecorderUploader }, { type: SpeechRecorderConfig, decorators: [{
11953
+ return [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i1$1.ActivatedRoute }, { type: i1$2.MatDialog }, { type: SessionService }, { type: RecordingService }, { type: SpeechRecorderUploader }, { type: SpeechRecorderConfig, decorators: [{
11788
11954
  type: Inject,
11789
11955
  args: [SPEECHRECORDER_CONFIG]
11790
11956
  }] }];
11791
- }, propDecorators: { _project: [{
11792
- type: Input
11793
- }], projectName: [{
11957
+ }, propDecorators: { projectName: [{
11794
11958
  type: Input
11795
11959
  }], recorderCombiPane: [{
11796
11960
  type: ViewChild,
@@ -11808,16 +11972,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11808
11972
  args: ['window:keydown', ['$event']]
11809
11973
  }] } });
11810
11974
  class AudioRecorderComponent extends RecorderComponent {
11811
- constructor(injector, route, router, changeDetectorRef, sessionService, projectService, recFilesService) {
11812
- super();
11975
+ constructor(injector, route, router, changeDetectorRef, sessionService, projectService, uploader) {
11976
+ super(uploader);
11813
11977
  this.injector = injector;
11814
11978
  this.route = route;
11815
11979
  this.router = router;
11816
11980
  this.changeDetectorRef = changeDetectorRef;
11817
11981
  this.sessionService = sessionService;
11818
11982
  this.projectService = projectService;
11819
- this.recFilesService = recFilesService;
11820
- this.dataSaved = true;
11983
+ this.uploader = uploader;
11821
11984
  }
11822
11985
  ngOnInit() {
11823
11986
  //super.ngOnInit();
@@ -11852,6 +12015,9 @@ class AudioRecorderComponent extends RecorderComponent {
11852
12015
  });
11853
12016
  }
11854
12017
  ngAfterViewInit() {
12018
+ this.uploader.listener = (ue) => {
12019
+ this.uploadUpdate(ue);
12020
+ };
11855
12021
  this.route.queryParams.subscribe((params) => {
11856
12022
  if (params['sessionId']) {
11857
12023
  this.fetchSession(params['sessionId']);
@@ -11868,50 +12034,68 @@ class AudioRecorderComponent extends RecorderComponent {
11868
12034
  //super.ngOnDestroy();
11869
12035
  }
11870
12036
  fetchSession(sessionId) {
11871
- //Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'statusMsg: Player initialized.'. Current value: 'statusMsg: Fetching session info...'.
11872
- // params.subscribe seems not to be asynchronous
11873
- // this.sm.statusAlertType='info';
11874
- // this.sm.statusMsg = 'Fetching session info...';
11875
- // this.sm.statusWaiting=true;
11876
12037
  let sessObs = this.sessionService.sessionObserver(sessionId);
11877
12038
  if (sessObs) {
11878
- sessObs.subscribe(sess => {
11879
- this.ar.statusAlertType = 'info';
11880
- this.ar.statusMsg = 'Received session info.';
11881
- this.ar.statusWaiting = false;
11882
- this.session = sess;
11883
- this.ar.session = sess;
11884
- if (sess.project) {
11885
- //console.debug("Session associated project: "+sess.project)
11886
- this.projectService.projectObservable(sess.project).subscribe(project => {
11887
- this.ar.project = project;
11888
- this.ar.fetchRecordings(sess);
11889
- }, reason => {
11890
- this.ar.statusMsg = reason;
11891
- this.ar.statusAlertType = 'error';
11892
- this.ar.statusWaiting = false;
11893
- console.error("Error fetching project config: " + reason);
11894
- });
11895
- }
11896
- else {
11897
- console.info("Session has no associated project. Using default configuration.");
12039
+ sessObs.subscribe({
12040
+ next: (sess) => {
12041
+ this.ar.statusAlertType = 'info';
12042
+ this.ar.statusMsg = 'Received session info.';
12043
+ this.ar.statusWaiting = false;
12044
+ this.session = sess;
12045
+ this.ar.session = sess;
12046
+ if (sess.project) {
12047
+ //console.debug("Session associated project: "+sess.project)
12048
+ this.projectService.projectObservable(sess.project).subscribe({
12049
+ next: (project) => {
12050
+ this.ar.project = project;
12051
+ this.ar.fetchRecordings(sess);
12052
+ }, error: (reason) => {
12053
+ this.ar.statusMsg = reason;
12054
+ this.ar.statusAlertType = 'error';
12055
+ this.ar.statusWaiting = false;
12056
+ console.error("Error fetching project config: " + reason);
12057
+ }
12058
+ });
12059
+ }
12060
+ else {
12061
+ console.info("Session has no associated project. Using default configuration.");
12062
+ }
12063
+ },
12064
+ error: (reason) => {
12065
+ this.ar.statusMsg = reason;
12066
+ this.ar.statusAlertType = 'error';
12067
+ this.ar.statusWaiting = false;
12068
+ console.error("Error fetching session " + reason);
11898
12069
  }
11899
- }, (reason) => {
11900
- this.ar.statusMsg = reason;
11901
- this.ar.statusAlertType = 'error';
11902
- this.ar.statusWaiting = false;
11903
- console.error("Error fetching session " + reason);
11904
12070
  });
11905
12071
  }
11906
12072
  }
12073
+ uploadUpdate(ue) {
12074
+ let upStatus = ue.status;
12075
+ this.dataSaved = (UploaderStatus.DONE === upStatus);
12076
+ let percentUpl = ue.percentDone();
12077
+ if (UploaderStatus.ERR === upStatus) {
12078
+ this.ar.uploadStatus = 'warn';
12079
+ }
12080
+ else {
12081
+ if (percentUpl < 50) {
12082
+ this.ar.uploadStatus = 'accent';
12083
+ }
12084
+ else {
12085
+ this.ar.uploadStatus = 'success';
12086
+ }
12087
+ this.ar.uploadProgress = percentUpl;
12088
+ }
12089
+ this.changeDetectorRef.detectChanges();
12090
+ }
11907
12091
  ready() {
11908
12092
  return this.dataSaved && !this.ar.isActive();
11909
12093
  }
11910
12094
  }
11911
- AudioRecorderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorderComponent, deps: [{ token: i0.Injector }, { token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: i0.ChangeDetectorRef }, { token: SessionService }, { token: ProjectService }, { token: RecordingService }], target: i0.ɵɵFactoryTarget.Component });
12095
+ AudioRecorderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorderComponent, deps: [{ token: i0.Injector }, { token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: i0.ChangeDetectorRef }, { token: SessionService }, { token: ProjectService }, { token: SpeechRecorderUploader }], target: i0.ɵɵFactoryTarget.Component });
11912
12096
  AudioRecorderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.2", type: AudioRecorderComponent, selector: "app-audiorecorder-comp", providers: [SessionService], viewQueries: [{ propertyName: "ar", first: true, predicate: AudioRecorder, descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: `
11913
12097
  <app-audiorecorder [projectName]="_project?.name" [dataSaved]="dataSaved"></app-audiorecorder>
11914
- `, isInline: true, styles: [":host{flex:2;display:flex;height:100%;flex-direction:column;min-height:0}\n"], components: [{ type: AudioRecorder, selector: "app-audiorecorder", inputs: ["_project", "projectName", "dataSaved"] }] });
12098
+ `, isInline: true, styles: [":host{flex:2;display:flex;height:100%;flex-direction:column;min-height:0}\n"], components: [{ type: AudioRecorder, selector: "app-audiorecorder", inputs: ["projectName", "dataSaved"] }] });
11915
12099
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorderComponent, decorators: [{
11916
12100
  type: Component,
11917
12101
  args: [{
@@ -11929,7 +12113,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11929
12113
 
11930
12114
  }`]
11931
12115
  }]
11932
- }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: SessionService }, { type: ProjectService }, { type: RecordingService }]; }, propDecorators: { ar: [{
12116
+ }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: SessionService }, { type: ProjectService }, { type: SpeechRecorderUploader }]; }, propDecorators: { ar: [{
11933
12117
  type: ViewChild,
11934
12118
  args: [AudioRecorder, { static: true }]
11935
12119
  }] } });
@@ -11978,7 +12162,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11978
12162
  }]
11979
12163
  }] });
11980
12164
 
11981
- const VERSION = '2.24.0';
12165
+ const VERSION = '2.25.0';
11982
12166
 
11983
12167
  /*
11984
12168
  * Public API Surface of speechrecorderng