speechrecorderng 2.24.2 → 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';
@@ -5661,98 +5662,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
5661
5662
  type: Output
5662
5663
  }] } });
5663
5664
 
5664
- class Float32ArrayChunkerOutStream {
5665
- constructor(outStream) {
5666
- this.outStream = outStream;
5667
- this.bufs = new Array();
5668
- this._channels = 0;
5669
- this._chunkSize = 0;
5670
- this.filled = 0;
5671
- this.receivedFrames = 0;
5672
- this.sentFrames = 0;
5673
- }
5674
- createBuffers() {
5675
- this.bufs = new Array(this._channels);
5676
- for (let ch = 0; ch < this._channels; ch++) {
5677
- this.bufs[ch] = new Float32Array(this._chunkSize);
5678
- }
5679
- }
5680
- set chunkSize(chunkSize) {
5681
- this._chunkSize = chunkSize;
5682
- this.createBuffers();
5683
- }
5684
- set channels(channels) {
5685
- this._channels = channels;
5686
- this.createBuffers();
5687
- }
5688
- write(buffers) {
5689
- let copied = 0;
5690
- if (buffers.length > 0) {
5691
- let buffersLen = buffers[0].length;
5692
- this.receivedFrames += buffersLen;
5693
- let avail = buffersLen;
5694
- // Fill out buffers until all values copied
5695
- while (avail > 0) {
5696
- let toFill = this._chunkSize - this.filled;
5697
- if (toFill > avail) {
5698
- toFill = avail;
5699
- }
5700
- let sliceEnd = copied + toFill;
5701
- // Firefox on Android sends only the first channel
5702
- for (let ch = 0; ch < buffersLen; ch++) {
5703
- if (buffers[ch]) {
5704
- let cpPrt = buffers[ch].slice(copied, sliceEnd);
5705
- let buf = this.bufs[ch];
5706
- buf.set(cpPrt, this.filled);
5707
- }
5708
- }
5709
- copied += toFill;
5710
- avail -= toFill;
5711
- this.filled += toFill;
5712
- if (this.filled == this._chunkSize) {
5713
- this.outStream.write(this.bufs);
5714
- this.sentFrames += this.filled;
5715
- this.filled = 0;
5716
- }
5717
- }
5718
- }
5719
- return copied;
5720
- }
5721
- flush() {
5722
- if (this.filled > 0) {
5723
- let restBufs = new Array(this._channels);
5724
- for (let ch = 0; ch < this._channels; ch++) {
5725
- restBufs[ch] = this.bufs[ch].slice(0, this.filled);
5726
- }
5727
- this.outStream.write(restBufs);
5728
- this.outStream.flush();
5729
- this.sentFrames += this.filled;
5730
- this.filled = 0;
5731
- }
5732
- }
5733
- close() {
5734
- this.outStream.close();
5735
- }
5736
- }
5737
-
5738
- class SequenceAudioFloat32ChunkerOutStream extends Float32ArrayChunkerOutStream {
5739
- constructor(outStream, chunkDurationSeconds) {
5740
- super(outStream);
5741
- this.chunkDurationSeconds = chunkDurationSeconds;
5742
- this.sampleRate = null;
5743
- this.sequenceAudioFloat32OutStream = outStream;
5744
- }
5745
- setFormat(channels, sampleRate) {
5746
- this.channels = channels;
5747
- this.sampleRate = sampleRate;
5748
- this.chunkSize = Math.round(sampleRate * this.chunkDurationSeconds);
5749
- this.sequenceAudioFloat32OutStream.setFormat(channels, sampleRate);
5750
- }
5751
- nextStream() {
5752
- this.sequenceAudioFloat32OutStream.nextStream();
5753
- }
5754
- }
5755
-
5756
5665
  class SessionFinishedDialog {
5757
5666
  constructor(dialogRef, data) {
5758
5667
  this.dialogRef = dialogRef;
@@ -7288,23 +7197,209 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
7288
7197
  type: Input
7289
7198
  }] } });
7290
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
+
7291
7341
  const FORCE_REQUEST_AUDIO_PERMISSIONS = false;
7292
7342
  const RECFILE_API_CTX = 'recfile';
7293
7343
  const MAX_RECORDING_TIME_MS = 1000 * 60 * 60 * 60; // 1 hour
7294
7344
  const LEVEL_BAR_INTERVALL_SECONDS = 0.1; // 100ms
7295
- class BasicRecorder {
7296
- 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) {
7297
7388
  this.dialog = dialog;
7298
7389
  this.sessionService = sessionService;
7390
+ this.uploader = uploader;
7391
+ this.config = config;
7299
7392
  this.statusMsg = '';
7300
7393
  this.statusWaiting = false;
7301
7394
  this.readonly = false;
7395
+ this.rfUuid = null;
7302
7396
  this.processingRecording = false;
7303
7397
  this.ac = null;
7304
7398
  this._wakeLock = false;
7305
7399
  this._selectedDeviceId = undefined;
7306
7400
  this._channelCount = 2;
7307
7401
  this._session = null;
7402
+ this.startedDate = null;
7308
7403
  this.uploadProgress = 100;
7309
7404
  this.uploadStatus = 'ok';
7310
7405
  this.audioSignalCollapsed = true;
@@ -7314,7 +7409,10 @@ class BasicRecorder {
7314
7409
  this.audioFetchSubscription = null;
7315
7410
  this.destroyed = false;
7316
7411
  this.navigationDisabled = true;
7412
+ // Default: Do not try to keep the device awake
7317
7413
  this.noSleep = null;
7414
+ // Default: Disabled chunked upload
7415
+ this._uploadChunkSizeSeconds = null;
7318
7416
  this.userAgent = UserAgentBuilder.userAgent();
7319
7417
  console.debug("Detected platform: " + this.userAgent.detectedPlatform);
7320
7418
  console.debug("Detected browser: " + this.userAgent.detectedBrowser);
@@ -7324,6 +7422,16 @@ class BasicRecorder {
7324
7422
  this.streamLevelMeasure = new StreamLevelMeasure();
7325
7423
  this.selCaptureDeviceId = null;
7326
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
+ }
7327
7435
  get wakeLock() {
7328
7436
  return this._wakeLock;
7329
7437
  }
@@ -7337,12 +7445,14 @@ class BasicRecorder {
7337
7445
  }
7338
7446
  if (!this.noSleep.isEnabled) {
7339
7447
  this.noSleep.enable();
7448
+ console.debug("Enabled wake lock.");
7340
7449
  }
7341
7450
  }
7342
7451
  }
7343
7452
  disableWakeLockCond() {
7344
7453
  if (this.noSleep && this.noSleep.isEnabled) {
7345
7454
  this.noSleep.disable();
7455
+ console.debug("Disabled wake lock.");
7346
7456
  }
7347
7457
  }
7348
7458
  set audioDevices(audioDevices) {
@@ -7364,6 +7474,27 @@ class BasicRecorder {
7364
7474
  this.statusAlertType = 'info';
7365
7475
  this.statusMsg = 'Ready.';
7366
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
+ }
7367
7498
  start() {
7368
7499
  this.statusAlertType = 'info';
7369
7500
  this.statusMsg = 'Starting session...';
@@ -7570,6 +7701,75 @@ class BasicRecorder {
7570
7701
  });
7571
7702
  }
7572
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
+ }
7573
7773
  closed() {
7574
7774
  this.statusAlertType = 'info';
7575
7775
  this.statusMsg = 'Session closed.';
@@ -7586,7 +7786,11 @@ class BasicRecorder {
7586
7786
  }
7587
7787
  });
7588
7788
  }
7589
- }
7789
+ };
7790
+ BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS = 30;
7791
+ BasicRecorder = __decorate([
7792
+ __param(3, Inject(SPEECHRECORDER_CONFIG))
7793
+ ], BasicRecorder);
7590
7794
 
7591
7795
  /**
7592
7796
  * Created by klausj on 17.06.2017.
@@ -7900,7 +8104,7 @@ const DEFAULT_PRE_REC_DELAY = 1000;
7900
8104
  const DEFAULT_POST_REC_DELAY = 500;
7901
8105
  class SessionManager extends BasicRecorder {
7902
8106
  constructor(changeDetectorRef, renderer, dialog, sessionService, recFileService, uploader, config) {
7903
- super(dialog, sessionService);
8107
+ super(dialog, sessionService, uploader, config);
7904
8108
  this.changeDetectorRef = changeDetectorRef;
7905
8109
  this.renderer = renderer;
7906
8110
  this.dialog = dialog;
@@ -8003,7 +8207,7 @@ class SessionManager extends BasicRecorder {
8003
8207
  if (this.ac) {
8004
8208
  this.transportActions.startAction.onAction = () => this.startItem();
8005
8209
  this.ac.listener = this;
8006
- this.ac.audioOutStream = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
8210
+ this.configureStreamCaptureStream();
8007
8211
  // Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
8008
8212
  //this.ac.listDevices();
8009
8213
  }
@@ -8149,12 +8353,7 @@ class SessionManager extends BasicRecorder {
8149
8353
  }
8150
8354
  }
8151
8355
  startItem() {
8152
- this.enableWakeLockCond();
8153
- this.transportActions.startAction.disabled = true;
8154
- this.transportActions.pauseAction.disabled = true;
8155
- if (this.readonly) {
8156
- return;
8157
- }
8356
+ super.startItem();
8158
8357
  this.transportActions.fwdAction.disabled = true;
8159
8358
  this.transportActions.fwdNextAction.disabled = true;
8160
8359
  this.transportActions.bwdAction.disabled = true;
@@ -8453,8 +8652,8 @@ class SessionManager extends BasicRecorder {
8453
8652
  }
8454
8653
  }
8455
8654
  started() {
8655
+ super.started();
8456
8656
  this.status = 2 /* PRE_RECORDING */;
8457
- this.transportActions.startAction.disabled = true;
8458
8657
  this.startStopSignalState = 1 /* PRERECORDING */;
8459
8658
  if (this._session) {
8460
8659
  if (this._session.status === "LOADED") {
@@ -8623,7 +8822,7 @@ class SessionManager extends BasicRecorder {
8623
8822
  rf = new SprRecordingFile(sessId, ic, it.recs.length, ad);
8624
8823
  it.recs.push(rf);
8625
8824
  }
8626
- if (this.enableUploadRecordings) {
8825
+ if (this.enableUploadRecordings && !this.uploadChunkSizeSeconds) {
8627
8826
  // TODO use SpeechRecorderconfig resp. RecfileService
8628
8827
  // convert asynchronously to 16-bit integer PCM
8629
8828
  // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
@@ -8702,6 +8901,25 @@ class SessionManager extends BasicRecorder {
8702
8901
  }
8703
8902
  this.changeDetectorRef.detectChanges();
8704
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
+ }
8705
8923
  postRecording(wavFile, recUrl) {
8706
8924
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
8707
8925
  let ul = new Upload(wavBlob, recUrl);
@@ -9200,6 +9418,13 @@ class SpeechrecorderngComponent extends RecorderComponent {
9200
9418
  chCnt = ProjectUtil.audioChannelCount(project);
9201
9419
  console.info("Project requested recording channel count: " + chCnt);
9202
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
+ }
9203
9428
  }
9204
9429
  else {
9205
9430
  console.error("Empty project configuration!");
@@ -10923,13 +11148,12 @@ class Item {
10923
11148
  }
10924
11149
  }
10925
11150
  class AudioRecorder extends BasicRecorder {
10926
- constructor(changeDetectorRef, renderer, route, dialog, projectService, sessionService, recFileService, uploader, config) {
10927
- super(dialog, sessionService);
11151
+ constructor(changeDetectorRef, renderer, route, dialog, sessionService, recFileService, uploader, config) {
11152
+ super(dialog, sessionService, uploader, config);
10928
11153
  this.changeDetectorRef = changeDetectorRef;
10929
11154
  this.renderer = renderer;
10930
11155
  this.route = route;
10931
11156
  this.dialog = dialog;
10932
- this.projectService = projectService;
10933
11157
  this.sessionService = sessionService;
10934
11158
  this.recFileService = recFileService;
10935
11159
  this.uploader = uploader;
@@ -10940,7 +11164,6 @@ class AudioRecorder extends BasicRecorder {
10940
11164
  this.enableDownloadRecordings = false;
10941
11165
  this.status = 0 /* BLOCKED */;
10942
11166
  this.dataSaved = true;
10943
- this.startedDate = null;
10944
11167
  this.maxRecTimerId = null;
10945
11168
  this.maxRecTimerRunning = false;
10946
11169
  this._promptIndex = null;
@@ -11031,7 +11254,7 @@ class AudioRecorder extends BasicRecorder {
11031
11254
  if (this.ac) {
11032
11255
  this.transportActions.startAction.onAction = () => this.startItem();
11033
11256
  this.ac.listener = this;
11034
- this.ac.audioOutStream = new SequenceAudioFloat32ChunkerOutStream(this.streamLevelMeasure, LEVEL_BAR_INTERVALL_SECONDS);
11257
+ this.configureStreamCaptureStream();
11035
11258
  // Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
11036
11259
  //this.ac.listDevices();
11037
11260
  }
@@ -11058,23 +11281,6 @@ class AudioRecorder extends BasicRecorder {
11058
11281
  this.uploader.listener = (ue) => {
11059
11282
  this.uploadUpdate(ue);
11060
11283
  };
11061
- let wakeLockSupp = ('wakeLock' in navigator);
11062
- // if(wakeLockSupp) {
11063
- // let wakeLock = null;
11064
- // try {
11065
- // //@ts-ignore
11066
- // wakeLock = navigator.wakeLock.request('screen');
11067
- //
11068
- // //statusElem.textContent = 'Wake Lock is active!';
11069
- // } catch (err) {
11070
- // // The Wake Lock request has failed - usually system related, such as battery.
11071
- // console.error('Wakelock failed'+err)
11072
- // }
11073
- // }else{
11074
- // let noSleep=new NoSleep();
11075
- // noSleep.enable();
11076
- //
11077
- // }
11078
11284
  }
11079
11285
  onKeyPress(ke) {
11080
11286
  if (ke.key == ' ') {
@@ -11178,6 +11384,12 @@ class AudioRecorder extends BasicRecorder {
11178
11384
  chCnt = ProjectUtil.audioChannelCount(project);
11179
11385
  console.info("Project requested recording channel count: " + chCnt);
11180
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
+ }
11181
11393
  }
11182
11394
  else {
11183
11395
  console.error("Empty project configuration!");
@@ -11277,12 +11489,7 @@ class AudioRecorder extends BasicRecorder {
11277
11489
  }
11278
11490
  }
11279
11491
  startItem() {
11280
- this.enableWakeLockCond();
11281
- this.transportActions.startAction.disabled = true;
11282
- this.transportActions.pauseAction.disabled = true;
11283
- if (this.readonly) {
11284
- return;
11285
- }
11492
+ super.startItem();
11286
11493
  this.transportActions.fwdAction.disabled = true;
11287
11494
  this.transportActions.fwdNextAction.disabled = true;
11288
11495
  this.transportActions.bwdAction.disabled = true;
@@ -11434,8 +11641,7 @@ class AudioRecorder extends BasicRecorder {
11434
11641
  }
11435
11642
  }
11436
11643
  started() {
11437
- this.startedDate = new Date();
11438
- this.transportActions.startAction.disabled = true;
11644
+ super.started();
11439
11645
  this.statusAlertType = 'info';
11440
11646
  this.statusMsg = 'Recording...';
11441
11647
  let maxRecordingTimeMs = MAX_RECORDING_TIME_MS;
@@ -11482,38 +11688,39 @@ class AudioRecorder extends BasicRecorder {
11482
11688
  if (this._session) {
11483
11689
  sessId = this._session.sessionId;
11484
11690
  }
11485
- 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);
11486
11695
  rf._startedAsDateObj = this.startedDate;
11487
11696
  if (rf._startedAsDateObj) {
11488
11697
  rf.startedDate = rf._startedAsDateObj.toString();
11489
11698
  }
11490
- let apiEndPoint = '';
11491
- if (this.config && this.config.apiEndPoint) {
11492
- apiEndPoint = this.config.apiEndPoint;
11493
- }
11494
- if (apiEndPoint !== '') {
11495
- 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
+ });
11496
11723
  }
11497
- let sessionsUrl = apiEndPoint + SessionService.SESSION_API_CTX;
11498
- let recUrl = sessionsUrl + '/' + rf.session + '/' + RECFILE_API_CTX + '/' + rf.uuid;
11499
- //
11500
- //
11501
- //
11502
- // // convert asynchronously to 16-bit integer PCM
11503
- // // TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
11504
- // // TODO duplicate conversion for manual download
11505
- // //console.log("Build wav writer...");
11506
- this.processingRecording = true;
11507
- let ww = new WavWriter();
11508
- ww.writeAsync(ad, (wavFile) => {
11509
- rf.frames = ad.length;
11510
- this.displayRecFile = rf;
11511
- this.recorderCombiPane.push(rf);
11512
- this.postRecordingMultipart(wavFile, rf.uuid, rf.session, rf._startedAsDateObj, recUrl);
11513
- this.processingRecording = false;
11514
- this.changeDetectorRef.detectChanges();
11515
- });
11516
- // }
11517
11724
  }
11518
11725
  // check complete session
11519
11726
  let complete = true;
@@ -11524,11 +11731,6 @@ class AudioRecorder extends BasicRecorder {
11524
11731
  this.updateNavigationActions();
11525
11732
  this.changeDetectorRef.detectChanges();
11526
11733
  }
11527
- postRecording(wavFile, recUrl) {
11528
- let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11529
- let ul = new Upload(wavBlob, recUrl);
11530
- this.uploader.queueUpload(ul);
11531
- }
11532
11734
  postRecordingMultipart(wavFile, uuid, sessionId, startedDate, recUrl) {
11533
11735
  let wavBlob = new Blob([wavFile], { type: 'audio/wav' });
11534
11736
  let fd = new FormData();
@@ -11545,6 +11747,25 @@ class AudioRecorder extends BasicRecorder {
11545
11747
  let ul = new Upload(fd, recUrl);
11546
11748
  this.uploader.queueUpload(ul);
11547
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
+ }
11548
11769
  stop() {
11549
11770
  if (this.ac) {
11550
11771
  this.ac.close();
@@ -11572,8 +11793,8 @@ class AudioRecorder extends BasicRecorder {
11572
11793
  }
11573
11794
  }
11574
11795
  }
11575
- 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 });
11576
- 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: `
11577
11798
  <app-warningbar [show]="isTestSession()" warningText="Test recording only!"></app-warningbar>
11578
11799
  <app-warningbar [show]="isDefaultAudioTestSession()"
11579
11800
  warningText="This test uses default audio device! Regular sessions may require a particular audio device (microphone)!"></app-warningbar>
@@ -11729,13 +11950,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11729
11950
  ]
11730
11951
  }]
11731
11952
  }], ctorParameters: function () {
11732
- 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: [{
11733
11954
  type: Inject,
11734
11955
  args: [SPEECHRECORDER_CONFIG]
11735
11956
  }] }];
11736
- }, propDecorators: { _project: [{
11737
- type: Input
11738
- }], projectName: [{
11957
+ }, propDecorators: { projectName: [{
11739
11958
  type: Input
11740
11959
  }], recorderCombiPane: [{
11741
11960
  type: ViewChild,
@@ -11753,7 +11972,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11753
11972
  args: ['window:keydown', ['$event']]
11754
11973
  }] } });
11755
11974
  class AudioRecorderComponent extends RecorderComponent {
11756
- constructor(injector, route, router, changeDetectorRef, sessionService, projectService, recFilesService, uploader) {
11975
+ constructor(injector, route, router, changeDetectorRef, sessionService, projectService, uploader) {
11757
11976
  super(uploader);
11758
11977
  this.injector = injector;
11759
11978
  this.route = route;
@@ -11761,7 +11980,6 @@ class AudioRecorderComponent extends RecorderComponent {
11761
11980
  this.changeDetectorRef = changeDetectorRef;
11762
11981
  this.sessionService = sessionService;
11763
11982
  this.projectService = projectService;
11764
- this.recFilesService = recFilesService;
11765
11983
  this.uploader = uploader;
11766
11984
  }
11767
11985
  ngOnInit() {
@@ -11874,10 +12092,10 @@ class AudioRecorderComponent extends RecorderComponent {
11874
12092
  return this.dataSaved && !this.ar.isActive();
11875
12093
  }
11876
12094
  }
11877
- 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 }, { token: SpeechRecorderUploader }], 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 });
11878
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: `
11879
12097
  <app-audiorecorder [projectName]="_project?.name" [dataSaved]="dataSaved"></app-audiorecorder>
11880
- `, 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"] }] });
11881
12099
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AudioRecorderComponent, decorators: [{
11882
12100
  type: Component,
11883
12101
  args: [{
@@ -11895,7 +12113,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11895
12113
 
11896
12114
  }`]
11897
12115
  }]
11898
- }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: SessionService }, { type: ProjectService }, { type: RecordingService }, { type: SpeechRecorderUploader }]; }, 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: [{
11899
12117
  type: ViewChild,
11900
12118
  args: [AudioRecorder, { static: true }]
11901
12119
  }] } });
@@ -11944,7 +12162,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImpor
11944
12162
  }]
11945
12163
  }] });
11946
12164
 
11947
- const VERSION = '2.24.2';
12165
+ const VERSION = '2.25.0';
11948
12166
 
11949
12167
  /*
11950
12168
  * Public API Surface of speechrecorderng