speechrecorderng 3.4.5 → 3.6.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.
- package/README.md +1 -1
- package/esm2020/lib/audio/audio_player.mjs +13 -26
- package/esm2020/lib/audio/capture/capture.mjs +244 -207
- package/esm2020/lib/audio/context.mjs +64 -2
- package/esm2020/lib/audio/net_audio_buffer.mjs +5 -9
- package/esm2020/lib/audio/playback/player.mjs +137 -96
- package/esm2020/lib/audio/ui/audio_display_control.mjs +1 -1
- package/esm2020/lib/speechrecorder/project/project.mjs +1 -1
- package/esm2020/lib/speechrecorder/recordings/basic_recording.service.mjs +9 -8
- package/esm2020/lib/speechrecorder/recordings/recordings.service.mjs +38 -37
- package/esm2020/lib/speechrecorder/session/audiorecorder.mjs +28 -77
- package/esm2020/lib/speechrecorder/session/basicrecorder.mjs +9 -2
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-meta.component.mjs +3 -3
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-navi.component.mjs +1 -1
- package/esm2020/lib/speechrecorder/session/recordingfile/recording-file-view.component.mjs +44 -47
- package/esm2020/lib/speechrecorder/session/recordingfile/recordingfile-service.mjs +11 -11
- package/esm2020/lib/speechrecorder/session/sessionmanager.mjs +66 -70
- package/esm2020/lib/speechrecorderng.component.mjs +8 -14
- package/esm2020/lib/spr.module.version.mjs +2 -2
- package/fesm2015/speechrecorderng.mjs +682 -610
- package/fesm2015/speechrecorderng.mjs.map +1 -1
- package/fesm2020/speechrecorderng.mjs +679 -610
- package/fesm2020/speechrecorderng.mjs.map +1 -1
- package/lib/audio/audio_player.d.ts +0 -1
- package/lib/audio/capture/capture.d.ts +5 -4
- package/lib/audio/context.d.ts +2 -0
- package/lib/audio/net_audio_buffer.d.ts +2 -4
- package/lib/audio/playback/player.d.ts +3 -2
- package/lib/speechrecorder/project/project.d.ts +1 -0
- package/lib/speechrecorder/recordings/basic_recording.service.d.ts +2 -2
- package/lib/speechrecorder/recordings/recordings.service.d.ts +8 -8
- package/lib/speechrecorder/session/basicrecorder.d.ts +3 -0
- package/lib/speechrecorder/session/recordingfile/recording-file-meta.component.d.ts +1 -0
- package/lib/speechrecorder/session/recordingfile/recordingfile-service.d.ts +2 -2
- package/lib/speechrecorder/session/sessionmanager.d.ts +1 -0
- package/lib/spr.module.version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1973,9 +1973,8 @@ class IndexedDbAudioBufferSourceNode extends AudioSourceNode {
|
|
|
1973
1973
|
}
|
|
1974
1974
|
|
|
1975
1975
|
class NetAudioBuffer extends BasicAudioSource {
|
|
1976
|
-
constructor(
|
|
1976
|
+
constructor(_recFileService, _baseUrl, _channelCount, _sampleRate, _chunkFrameLen, _frameLen, _uuid = null, _orgFetchChunkFrameLen = _chunkFrameLen) {
|
|
1977
1977
|
super();
|
|
1978
|
-
this._audioContext = _audioContext;
|
|
1979
1978
|
this._recFileService = _recFileService;
|
|
1980
1979
|
this._baseUrl = _baseUrl;
|
|
1981
1980
|
this._channelCount = _channelCount;
|
|
@@ -1993,9 +1992,6 @@ class NetAudioBuffer extends BasicAudioSource {
|
|
|
1993
1992
|
get recFileService() {
|
|
1994
1993
|
return this._recFileService;
|
|
1995
1994
|
}
|
|
1996
|
-
get audioContext() {
|
|
1997
|
-
return this._audioContext;
|
|
1998
|
-
}
|
|
1999
1995
|
get baseUrl() {
|
|
2000
1996
|
return this._baseUrl;
|
|
2001
1997
|
}
|
|
@@ -2033,8 +2029,8 @@ class NetAudioBuffer extends BasicAudioSource {
|
|
|
2033
2029
|
toString() {
|
|
2034
2030
|
return "Indexed db audio buffer. Channels: " + this.channelCount + ", sample rate: " + this.sampleRate + ", chunk frame length: " + this._chunkFrameLen + ", number of chunks: " + this.chunkCount + ", frame length: " + this.frameLen + ", sealed: " + this.sealed();
|
|
2035
2031
|
}
|
|
2036
|
-
static fromChunkAudioBuffer(
|
|
2037
|
-
let nab = new NetAudioBuffer(
|
|
2032
|
+
static fromChunkAudioBuffer(recordingsService, baseUrl, ab, frameLen, orgFetchChunkFrameLen = ab.length) {
|
|
2033
|
+
let nab = new NetAudioBuffer(recordingsService, baseUrl, ab.numberOfChannels, ab.sampleRate, ab.length, frameLen, null, orgFetchChunkFrameLen);
|
|
2038
2034
|
nab.ready();
|
|
2039
2035
|
return nab;
|
|
2040
2036
|
}
|
|
@@ -2077,7 +2073,7 @@ class NetRandomAccessAudioStream {
|
|
|
2077
2073
|
}
|
|
2078
2074
|
chunk(baseUrl, ci, cb, errCb) {
|
|
2079
2075
|
let startFrame = ci * this._netAb.orgFetchChunkFrameLen;
|
|
2080
|
-
this._netAb.recFileService.chunkAudioRequest(
|
|
2076
|
+
this._netAb.recFileService.chunkAudioRequest(baseUrl, startFrame, this._netAb.orgFetchChunkFrameLen).subscribe({
|
|
2081
2077
|
next: (chDl) => {
|
|
2082
2078
|
if (chDl) {
|
|
2083
2079
|
const ab = chDl.decodedAudioBuffer;
|
|
@@ -2479,6 +2475,85 @@ class NetAudioBufferSourceNode extends AudioSourceNode {
|
|
|
2479
2475
|
}
|
|
2480
2476
|
}
|
|
2481
2477
|
|
|
2478
|
+
class AudioContextProvider {
|
|
2479
|
+
static audioContextInstance() {
|
|
2480
|
+
if (!this._audioContext) {
|
|
2481
|
+
let debugFail = false;
|
|
2482
|
+
if (!window.AudioContext || typeof window.AudioContext !== 'function' || debugFail) {
|
|
2483
|
+
this._audioContext = null;
|
|
2484
|
+
throw new Error('Browser does not support Web Audio API!');
|
|
2485
|
+
}
|
|
2486
|
+
else {
|
|
2487
|
+
console.debug("Get new audio context...");
|
|
2488
|
+
this._audioContext = new window.AudioContext();
|
|
2489
|
+
console.debug("Created new audio context.");
|
|
2490
|
+
this._audioContext.addEventListener('statechange', () => {
|
|
2491
|
+
console.debug("Audio context state changed: " + this._audioContext?.state);
|
|
2492
|
+
});
|
|
2493
|
+
console.debug("Created new audio context with state: " + this._audioContext?.state);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return this._audioContext;
|
|
2497
|
+
}
|
|
2498
|
+
// public static audioContextInstanceRunning(audioContext?:AudioContext):Promise<AudioContext>{
|
|
2499
|
+
//
|
|
2500
|
+
// return new Promise<AudioContext>((resolve,reject)=>{
|
|
2501
|
+
// let aCtx=audioContext?audioContext:AudioContextProvider.audioContextInstance();
|
|
2502
|
+
// if(aCtx) {
|
|
2503
|
+
// if(aCtx.state==='closed') {
|
|
2504
|
+
// reject(new Error('Audio context already closed.'));
|
|
2505
|
+
// }else if(aCtx.state==='running') {
|
|
2506
|
+
// resolve(aCtx);
|
|
2507
|
+
// }else{
|
|
2508
|
+
// aCtx.resume().then(() => {
|
|
2509
|
+
// if(aCtx) {
|
|
2510
|
+
// resolve(aCtx);
|
|
2511
|
+
// }else{
|
|
2512
|
+
// reject(new Error('Could not get audio context'));
|
|
2513
|
+
// }
|
|
2514
|
+
// }).catch(() => {
|
|
2515
|
+
// reject(new Error('Could not resume audio context'));
|
|
2516
|
+
// })
|
|
2517
|
+
// }
|
|
2518
|
+
// }else{
|
|
2519
|
+
// reject(new Error('Could not get audio context from browser'));
|
|
2520
|
+
// }
|
|
2521
|
+
// });
|
|
2522
|
+
// }
|
|
2523
|
+
// public static decodeAudioData(data:ArrayBuffer,audioContext?:AudioContext):Promise<AudioBuffer>{
|
|
2524
|
+
// return new Promise<AudioBuffer>((resolve,reject)=>{
|
|
2525
|
+
// // decodeAudioData requires an audio context in running state
|
|
2526
|
+
// AudioContextProvider.audioContextInstanceRunning(audioContext).then(
|
|
2527
|
+
// (aCtx)=>{
|
|
2528
|
+
// // Do not use Promise version, which does not work with Safari 13
|
|
2529
|
+
// aCtx.decodeAudioData(data,decodedData => {
|
|
2530
|
+
// resolve(decodedData);
|
|
2531
|
+
// },(reason) => {
|
|
2532
|
+
// reject(reason);
|
|
2533
|
+
// });
|
|
2534
|
+
// }
|
|
2535
|
+
// ).catch((reason)=>{
|
|
2536
|
+
// reject(reason);
|
|
2537
|
+
// })
|
|
2538
|
+
// })
|
|
2539
|
+
// }
|
|
2540
|
+
static decodeAudioData(data) {
|
|
2541
|
+
return new Promise((resolve, reject) => {
|
|
2542
|
+
if (!this._offlineAudioContext) {
|
|
2543
|
+
this._offlineAudioContext = new OfflineAudioContext(1, 44100, 44100);
|
|
2544
|
+
}
|
|
2545
|
+
// Do not use Promise version, which does not work with Safari 13
|
|
2546
|
+
this._offlineAudioContext.decodeAudioData(data, decodedData => {
|
|
2547
|
+
resolve(decodedData);
|
|
2548
|
+
}, (reason) => {
|
|
2549
|
+
reject(reason);
|
|
2550
|
+
});
|
|
2551
|
+
});
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
AudioContextProvider._audioContext = null;
|
|
2555
|
+
AudioContextProvider._offlineAudioContext = null;
|
|
2556
|
+
|
|
2482
2557
|
var EventType;
|
|
2483
2558
|
(function (EventType) {
|
|
2484
2559
|
EventType[EventType["CLOSED"] = 0] = "CLOSED";
|
|
@@ -2502,8 +2577,9 @@ class AudioPlayerEvent {
|
|
|
2502
2577
|
}
|
|
2503
2578
|
}
|
|
2504
2579
|
class AudioPlayer {
|
|
2505
|
-
constructor(
|
|
2580
|
+
constructor(listener) {
|
|
2506
2581
|
this.running = false;
|
|
2582
|
+
this.context = null;
|
|
2507
2583
|
this.ready = false;
|
|
2508
2584
|
this._audioClip = null;
|
|
2509
2585
|
this._audioSource = null;
|
|
@@ -2511,7 +2587,6 @@ class AudioPlayer {
|
|
|
2511
2587
|
this.sourceAudioWorkletNode = null;
|
|
2512
2588
|
this.playStartTime = null;
|
|
2513
2589
|
this.timerVar = null;
|
|
2514
|
-
this.context = context;
|
|
2515
2590
|
this.listener = listener;
|
|
2516
2591
|
this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
|
|
2517
2592
|
this.n = navigator;
|
|
@@ -2528,15 +2603,23 @@ class AudioPlayer {
|
|
|
2528
2603
|
this._stopAction = new Action('Stop');
|
|
2529
2604
|
this._stopAction.disabled = true;
|
|
2530
2605
|
this._stopAction.onAction = () => this.stop();
|
|
2531
|
-
this.context.addEventListener('statechange', (ev) => {
|
|
2532
|
-
if (this.context.state !== 'running') {
|
|
2533
|
-
this.stop();
|
|
2534
|
-
}
|
|
2535
|
-
});
|
|
2536
2606
|
}
|
|
2537
2607
|
get autoPlayOnSelectToggleAction() {
|
|
2538
2608
|
return this._autoPlayOnSelectToggleAction;
|
|
2539
2609
|
}
|
|
2610
|
+
_audioContext() {
|
|
2611
|
+
if (!this.context) {
|
|
2612
|
+
this.context = AudioContextProvider.audioContextInstance();
|
|
2613
|
+
if (this.context) {
|
|
2614
|
+
this.context.addEventListener('statechange', (ev) => {
|
|
2615
|
+
if (this.context && this.context.state !== 'running') {
|
|
2616
|
+
this.stop();
|
|
2617
|
+
}
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return this.context;
|
|
2622
|
+
}
|
|
2540
2623
|
get startAction() {
|
|
2541
2624
|
return this._startAction;
|
|
2542
2625
|
}
|
|
@@ -2555,9 +2638,9 @@ class AudioPlayer {
|
|
|
2555
2638
|
chs = audioDataHolder.numberOfChannels;
|
|
2556
2639
|
if (chs > 0) {
|
|
2557
2640
|
length = audioDataHolder.frameLen;
|
|
2558
|
-
if (chs > this.context.destination.maxChannelCount) {
|
|
2559
|
-
|
|
2560
|
-
}
|
|
2641
|
+
//if (chs > this.context.destination.maxChannelCount) {
|
|
2642
|
+
// // TODO exception
|
|
2643
|
+
//}
|
|
2561
2644
|
}
|
|
2562
2645
|
this.audioSource = audioDataHolder.audioSource;
|
|
2563
2646
|
audioClip.addSelectionObserver((ac) => {
|
|
@@ -2577,16 +2660,11 @@ class AudioPlayer {
|
|
|
2577
2660
|
set audioSource(value) {
|
|
2578
2661
|
this.stop();
|
|
2579
2662
|
this._audioSource = value;
|
|
2580
|
-
if (this._audioSource
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
2588
|
-
else {
|
|
2589
|
-
this._loadSourceWorkletAndInitStart();
|
|
2663
|
+
if (this._audioSource) {
|
|
2664
|
+
this.ready = true;
|
|
2665
|
+
this.updateStartActions();
|
|
2666
|
+
if (this.listener) {
|
|
2667
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2590
2668
|
}
|
|
2591
2669
|
}
|
|
2592
2670
|
else {
|
|
@@ -2598,24 +2676,27 @@ class AudioPlayer {
|
|
|
2598
2676
|
}
|
|
2599
2677
|
}
|
|
2600
2678
|
_loadSourceWorkletAndInitStart() {
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
this.listener
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
this.listener
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2679
|
+
if (this.context) {
|
|
2680
|
+
AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
|
|
2681
|
+
//console.debug("Player ready. ( by Player::_loadSourceWorkletAndInitStart()");
|
|
2682
|
+
this.ready = true;
|
|
2683
|
+
this.updateStartActions();
|
|
2684
|
+
if (this.listener) {
|
|
2685
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2686
|
+
}
|
|
2687
|
+
}).catch((error) => {
|
|
2688
|
+
this.ready = false;
|
|
2689
|
+
this.updateStartActions();
|
|
2690
|
+
if (this.listener) {
|
|
2691
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
2692
|
+
}
|
|
2693
|
+
console.error('Could not add module ' + error);
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2616
2696
|
}
|
|
2617
2697
|
_startAudioSourceWorkletNode() {
|
|
2618
|
-
|
|
2698
|
+
this._loadSourceWorkletAndInitStart();
|
|
2699
|
+
if (this.context && this.sourceAudioWorkletNode) {
|
|
2619
2700
|
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2620
2701
|
let msg = 'Unknwon error';
|
|
2621
2702
|
if (ev instanceof ErrorEvent) {
|
|
@@ -2642,16 +2723,25 @@ class AudioPlayer {
|
|
|
2642
2723
|
}
|
|
2643
2724
|
}
|
|
2644
2725
|
start() {
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2726
|
+
this._audioContext();
|
|
2727
|
+
if (this.context) {
|
|
2728
|
+
if (!this._startAction.disabled && !this.running) {
|
|
2729
|
+
if (this.context.state === 'suspended') {
|
|
2730
|
+
this.context.resume().then(() => {
|
|
2731
|
+
this._start();
|
|
2732
|
+
}).catch((reason) => {
|
|
2733
|
+
console.error(reason.message());
|
|
2734
|
+
throw reason;
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
else if (this.context.state === 'closed') {
|
|
2738
|
+
const msg = 'Error: Cannot start playback. Audio context is already closed!';
|
|
2739
|
+
console.error(msg);
|
|
2740
|
+
throw new Error(msg);
|
|
2741
|
+
}
|
|
2742
|
+
else {
|
|
2648
2743
|
this._start();
|
|
2649
|
-
}
|
|
2650
|
-
console.error('Could not resume audio context: ' + reason);
|
|
2651
|
-
});
|
|
2652
|
-
}
|
|
2653
|
-
else {
|
|
2654
|
-
this._start();
|
|
2744
|
+
}
|
|
2655
2745
|
}
|
|
2656
2746
|
}
|
|
2657
2747
|
}
|
|
@@ -2676,9 +2766,12 @@ class AudioPlayer {
|
|
|
2676
2766
|
}
|
|
2677
2767
|
}
|
|
2678
2768
|
startSelectionDisabled() {
|
|
2679
|
-
return !(this._audioClip &&
|
|
2769
|
+
return !(this._audioClip && !this.startAction.disabled && this._audioClip.selection);
|
|
2680
2770
|
}
|
|
2681
2771
|
_start(playSelection = false) {
|
|
2772
|
+
if (!this.context) {
|
|
2773
|
+
throw new Error("Could not get audio context!");
|
|
2774
|
+
}
|
|
2682
2775
|
if (this._audioSource instanceof AudioBufferSource) {
|
|
2683
2776
|
this.sourceBufferNode = this.context.createBufferSource();
|
|
2684
2777
|
this.sourceBufferNode.buffer = this._audioSource.audioBuffer;
|
|
@@ -2709,68 +2802,90 @@ class AudioPlayer {
|
|
|
2709
2802
|
}
|
|
2710
2803
|
}
|
|
2711
2804
|
else if (this._audioSource instanceof ArrayAudioBuffer || this._audioSource instanceof IndexedDbAudioBuffer || this._audioSource instanceof NetAudioBuffer) {
|
|
2712
|
-
this.
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
const iasn = new IndexedDbAudioBufferSourceNode(this.context);
|
|
2720
|
-
iasn.inddbAudioBuffer = this._audioSource;
|
|
2721
|
-
this.sourceAudioWorkletNode = iasn;
|
|
2722
|
-
}
|
|
2723
|
-
else if (this._audioSource instanceof NetAudioBuffer) {
|
|
2724
|
-
const nabsn = new NetAudioBufferSourceNode(this.context);
|
|
2725
|
-
nabsn.netAudioBuffer = this._audioSource;
|
|
2726
|
-
this.sourceAudioWorkletNode = nabsn;
|
|
2727
|
-
}
|
|
2728
|
-
if (this.sourceAudioWorkletNode) {
|
|
2729
|
-
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2730
|
-
let msg = 'Unknwon error';
|
|
2731
|
-
if (ev instanceof ErrorEvent) {
|
|
2732
|
-
msg = ev.message;
|
|
2805
|
+
AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
|
|
2806
|
+
this.playStartTime = null;
|
|
2807
|
+
if (this.context) {
|
|
2808
|
+
if (this._audioSource instanceof ArrayAudioBuffer) {
|
|
2809
|
+
const aabsn = new ArrayAudioBufferSourceNode(this.context);
|
|
2810
|
+
aabsn.arrayAudioBuffer = this._audioSource;
|
|
2811
|
+
this.sourceAudioWorkletNode = aabsn;
|
|
2733
2812
|
}
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2813
|
+
else if (this._audioSource instanceof IndexedDbAudioBuffer) {
|
|
2814
|
+
const iasn = new IndexedDbAudioBufferSourceNode(this.context);
|
|
2815
|
+
iasn.inddbAudioBuffer = this._audioSource;
|
|
2816
|
+
this.sourceAudioWorkletNode = iasn;
|
|
2817
|
+
}
|
|
2818
|
+
else if (this._audioSource instanceof NetAudioBuffer) {
|
|
2819
|
+
const nabsn = new NetAudioBufferSourceNode(this.context);
|
|
2820
|
+
nabsn.netAudioBuffer = this._audioSource;
|
|
2821
|
+
this.sourceAudioWorkletNode = nabsn;
|
|
2822
|
+
}
|
|
2823
|
+
if (this.sourceAudioWorkletNode) {
|
|
2824
|
+
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2825
|
+
let msg = 'Unknwon error';
|
|
2826
|
+
if (ev instanceof ErrorEvent) {
|
|
2827
|
+
msg = ev.message;
|
|
2828
|
+
}
|
|
2829
|
+
console.error("Audio source worklet error: " + msg);
|
|
2830
|
+
if (this.listener) {
|
|
2831
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.ERROR));
|
|
2832
|
+
}
|
|
2833
|
+
};
|
|
2834
|
+
this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
|
|
2835
|
+
this.sourceAudioWorkletNode.onended = () => this.onended();
|
|
2836
|
+
this.running = true;
|
|
2837
|
+
const ac = this._audioClip;
|
|
2838
|
+
let offset = 0;
|
|
2839
|
+
if (playSelection && ac && ac.selection) {
|
|
2840
|
+
const s = ac.selection;
|
|
2841
|
+
const sr = ac.audioDataHolder.sampleRate;
|
|
2842
|
+
offset = s.leftFrame / sr;
|
|
2843
|
+
const stopPosInsecs = s.rightFrame / sr;
|
|
2844
|
+
const dur = stopPosInsecs - offset;
|
|
2845
|
+
this.sourceAudioWorkletNode.start(0, offset, dur);
|
|
2846
|
+
}
|
|
2847
|
+
else {
|
|
2848
|
+
this.sourceAudioWorkletNode.start();
|
|
2849
|
+
}
|
|
2850
|
+
//this.playStartTime = this.context.currentTime - offset;
|
|
2851
|
+
this._startAction.disabled = true;
|
|
2852
|
+
this._startSelectionAction.disabled = true;
|
|
2853
|
+
this._stopAction.disabled = false;
|
|
2854
|
+
if (this.listener) {
|
|
2855
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
2856
|
+
}
|
|
2737
2857
|
}
|
|
2738
|
-
};
|
|
2739
|
-
this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
|
|
2740
|
-
this.sourceAudioWorkletNode.onended = () => this.onended();
|
|
2741
|
-
this.running = true;
|
|
2742
|
-
const ac = this._audioClip;
|
|
2743
|
-
let offset = 0;
|
|
2744
|
-
if (playSelection && ac && ac.selection) {
|
|
2745
|
-
const s = ac.selection;
|
|
2746
|
-
const sr = ac.audioDataHolder.sampleRate;
|
|
2747
|
-
offset = s.leftFrame / sr;
|
|
2748
|
-
const stopPosInsecs = s.rightFrame / sr;
|
|
2749
|
-
const dur = stopPosInsecs - offset;
|
|
2750
|
-
this.sourceAudioWorkletNode.start(0, offset, dur);
|
|
2751
|
-
}
|
|
2752
|
-
else {
|
|
2753
|
-
this.sourceAudioWorkletNode.start();
|
|
2754
2858
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
this.
|
|
2758
|
-
this.
|
|
2859
|
+
}).catch((error) => {
|
|
2860
|
+
console.error(error.message);
|
|
2861
|
+
this.ready = false;
|
|
2862
|
+
this.updateStartActions();
|
|
2759
2863
|
if (this.listener) {
|
|
2760
|
-
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.
|
|
2864
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
2761
2865
|
}
|
|
2762
|
-
|
|
2866
|
+
throw error;
|
|
2867
|
+
});
|
|
2763
2868
|
}
|
|
2764
2869
|
}
|
|
2765
2870
|
startSelected() {
|
|
2871
|
+
this._audioContext();
|
|
2872
|
+
if (!this.context) {
|
|
2873
|
+
throw new Error("Could not get audio context!");
|
|
2874
|
+
}
|
|
2766
2875
|
if (!this._startAction.disabled && !this.running) {
|
|
2767
|
-
if (this.context.state
|
|
2876
|
+
if (this.context.state === 'suspended') {
|
|
2768
2877
|
this.context.resume().then(() => {
|
|
2769
2878
|
this._start(true);
|
|
2770
2879
|
}).catch((reason) => {
|
|
2771
|
-
console.error(
|
|
2880
|
+
console.error(reason.message);
|
|
2881
|
+
throw reason;
|
|
2772
2882
|
});
|
|
2773
2883
|
}
|
|
2884
|
+
else if (this.context.state === 'closed') {
|
|
2885
|
+
const msg = 'Error: Cannot start playback of selection. Audio context is already closed!';
|
|
2886
|
+
console.error(msg);
|
|
2887
|
+
throw new Error(msg);
|
|
2888
|
+
}
|
|
2774
2889
|
else {
|
|
2775
2890
|
this._start(true);
|
|
2776
2891
|
}
|
|
@@ -2807,7 +2922,7 @@ class AudioPlayer {
|
|
|
2807
2922
|
}
|
|
2808
2923
|
get playPositionTime() {
|
|
2809
2924
|
let ppt = null;
|
|
2810
|
-
if (this.playStartTime !== null) {
|
|
2925
|
+
if (this.context && this.playStartTime !== null) {
|
|
2811
2926
|
ppt = this.context.currentTime - this.playStartTime;
|
|
2812
2927
|
}
|
|
2813
2928
|
else if (this.sourceAudioWorkletNode) {
|
|
@@ -3149,8 +3264,10 @@ const awpStr = "class AudioCaptureInterceptorProcessor extends AudioWorkletProce
|
|
|
3149
3264
|
"\n" +
|
|
3150
3265
|
"registerProcessor('capture-interceptor',AudioCaptureInterceptorProcessor);\n";
|
|
3151
3266
|
class AudioCapture {
|
|
3152
|
-
|
|
3267
|
+
//private context:AudioContext|null=null;
|
|
3268
|
+
constructor() {
|
|
3153
3269
|
this._maxAutoNetMemStoreSamples = AudioCapture.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES;
|
|
3270
|
+
this.context = null;
|
|
3154
3271
|
this._recUUID = null;
|
|
3155
3272
|
this.agcStatus = null;
|
|
3156
3273
|
this.bufferingNode = null;
|
|
@@ -3165,13 +3282,7 @@ class AudioCapture {
|
|
|
3165
3282
|
this.persisted = true;
|
|
3166
3283
|
this.persistError = null;
|
|
3167
3284
|
this.inddbAudioBuffer = null;
|
|
3168
|
-
this.context = context;
|
|
3169
3285
|
this.n = navigator;
|
|
3170
|
-
this.context.addEventListener('statechange', () => {
|
|
3171
|
-
if (this.context.state !== 'running') {
|
|
3172
|
-
this.close();
|
|
3173
|
-
}
|
|
3174
|
-
});
|
|
3175
3286
|
}
|
|
3176
3287
|
get maxAutoNetMemStoreSamples() {
|
|
3177
3288
|
return this._maxAutoNetMemStoreSamples;
|
|
@@ -3200,6 +3311,19 @@ class AudioCapture {
|
|
|
3200
3311
|
get opened() {
|
|
3201
3312
|
return this._opened;
|
|
3202
3313
|
}
|
|
3314
|
+
_audioContext() {
|
|
3315
|
+
if (!this.context) {
|
|
3316
|
+
this.context = AudioContextProvider.audioContextInstance();
|
|
3317
|
+
if (this.context) {
|
|
3318
|
+
this.context.addEventListener('statechange', () => {
|
|
3319
|
+
if (this.context && this.context.state !== 'running') {
|
|
3320
|
+
this.close();
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
return this.context;
|
|
3326
|
+
}
|
|
3203
3327
|
initData() {
|
|
3204
3328
|
if (!this._recUUID) {
|
|
3205
3329
|
this._recUUID = UUID.generate();
|
|
@@ -3314,119 +3438,137 @@ class AudioCapture {
|
|
|
3314
3438
|
}
|
|
3315
3439
|
}
|
|
3316
3440
|
addCaptureInterceptor() {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
this.listener
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
let chunk = new Array(chs);
|
|
3339
|
-
const samples = this.framesRecorded * chs;
|
|
3340
|
-
if ((AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.audioStorageType) && this.data && samples > this._maxAutoNetMemStoreSamples) {
|
|
3341
|
-
this.data = null;
|
|
3342
|
-
}
|
|
3343
|
-
//console.debug("Data initialized: "+(this.data!=null));
|
|
3344
|
-
for (let ch = 0; ch < chs; ch++) {
|
|
3345
|
-
//console.debug("Data ch initialized: "+(this.data !=null && this.data[ch] !=null));
|
|
3346
|
-
if (ch < this.channelCount) {
|
|
3347
|
-
if (dt.data[ch]) {
|
|
3348
|
-
let fa = new Float32Array(dt.data[ch]);
|
|
3349
|
-
if (this.data && this.data[ch]) {
|
|
3350
|
-
this.data[ch].push(fa);
|
|
3351
|
-
}
|
|
3352
|
-
chunk[ch] = fa;
|
|
3353
|
-
// Use samples of channel 0 to count frames (samples)
|
|
3354
|
-
if (ch == 0) {
|
|
3355
|
-
this.framesRecorded += fa.length;
|
|
3356
|
-
}
|
|
3357
|
-
}
|
|
3441
|
+
if (this.context) {
|
|
3442
|
+
const awn = new AudioWorkletNode(this.context, 'capture-interceptor');
|
|
3443
|
+
awn.onprocessorerror = (ev) => {
|
|
3444
|
+
let msg = 'Unknwon error';
|
|
3445
|
+
if (ev instanceof ErrorEvent) {
|
|
3446
|
+
msg = ev.message;
|
|
3447
|
+
}
|
|
3448
|
+
console.error("Capture audio worklet error: " + msg);
|
|
3449
|
+
if (this.listener) {
|
|
3450
|
+
this.listener.error(msg);
|
|
3451
|
+
}
|
|
3452
|
+
};
|
|
3453
|
+
let awnPt = awn.port;
|
|
3454
|
+
if (awnPt) {
|
|
3455
|
+
awnPt.onmessage = (ev) => {
|
|
3456
|
+
if (this.capturing) {
|
|
3457
|
+
let dt = ev.data;
|
|
3458
|
+
let chs = dt.chs;
|
|
3459
|
+
let adaLen = dt.data.length;
|
|
3460
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
3461
|
+
console.debug('Received data from worklet: ' + chs + ' ' + dt.len + ' Data chs: ' + adaLen);
|
|
3358
3462
|
}
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
this.
|
|
3363
|
-
// // Random test error:
|
|
3364
|
-
// if(Math.random()>0.98) {
|
|
3365
|
-
// throw new Error('Test');
|
|
3366
|
-
// }
|
|
3463
|
+
let chunk = new Array(chs);
|
|
3464
|
+
const samples = this.framesRecorded * chs;
|
|
3465
|
+
if ((AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.audioStorageType) && this.data && samples > this._maxAutoNetMemStoreSamples) {
|
|
3466
|
+
this.data = null;
|
|
3367
3467
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3468
|
+
//console.debug("Data initialized: "+(this.data!=null));
|
|
3469
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3470
|
+
//console.debug("Data ch initialized: "+(this.data !=null && this.data[ch] !=null));
|
|
3471
|
+
if (ch < this.channelCount) {
|
|
3472
|
+
if (dt.data[ch]) {
|
|
3473
|
+
let fa = new Float32Array(dt.data[ch]);
|
|
3474
|
+
if (this.data && this.data[ch]) {
|
|
3475
|
+
this.data[ch].push(fa);
|
|
3476
|
+
}
|
|
3477
|
+
chunk[ch] = fa;
|
|
3478
|
+
// Use samples of channel 0 to count frames (samples)
|
|
3479
|
+
if (ch == 0) {
|
|
3480
|
+
this.framesRecorded += fa.length;
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3374
3483
|
}
|
|
3375
|
-
|
|
3484
|
+
}
|
|
3485
|
+
if (this.audioOutStream) {
|
|
3376
3486
|
try {
|
|
3377
|
-
this.
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3487
|
+
this.audioOutStream.write(chunk);
|
|
3488
|
+
// // Random test error:
|
|
3489
|
+
// if(Math.random()>0.98) {
|
|
3490
|
+
// throw new Error('Test');
|
|
3491
|
+
// }
|
|
3381
3492
|
}
|
|
3382
|
-
|
|
3383
|
-
if (
|
|
3384
|
-
|
|
3385
|
-
if (err instanceof DOMException) {
|
|
3386
|
-
errExpl = ': ' + err.name + ': ' + err.message;
|
|
3387
|
-
}
|
|
3388
|
-
this.listener.error("Could not handle recorded audio data" + errExpl, "Please try to record again.");
|
|
3493
|
+
catch (err) {
|
|
3494
|
+
if (err instanceof Error) {
|
|
3495
|
+
this.persistError = err;
|
|
3389
3496
|
}
|
|
3390
3497
|
else {
|
|
3391
|
-
this.
|
|
3498
|
+
this.persistError = new Error('Error handling recorded audio data');
|
|
3499
|
+
}
|
|
3500
|
+
console.error("Capture error: " + err);
|
|
3501
|
+
try {
|
|
3502
|
+
this.stop();
|
|
3503
|
+
}
|
|
3504
|
+
catch (err2) {
|
|
3505
|
+
console.error("Capture next error (ignored): " + err2);
|
|
3506
|
+
}
|
|
3507
|
+
finally {
|
|
3508
|
+
if (this.listener) {
|
|
3509
|
+
let errExpl = '';
|
|
3510
|
+
if (err instanceof DOMException) {
|
|
3511
|
+
errExpl = ': ' + err.name + ': ' + err.message;
|
|
3512
|
+
}
|
|
3513
|
+
this.listener.error("Could not handle recorded audio data" + errExpl, "Please try to record again.");
|
|
3514
|
+
}
|
|
3515
|
+
else {
|
|
3516
|
+
this.close();
|
|
3517
|
+
}
|
|
3392
3518
|
}
|
|
3393
3519
|
}
|
|
3394
3520
|
}
|
|
3521
|
+
if (AudioStorageType.DB_CHUNKED === this._audioStorageType && this._persistentAudioStorageTarget) {
|
|
3522
|
+
this.store();
|
|
3523
|
+
}
|
|
3395
3524
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
this._opened = true;
|
|
3410
|
-
if (this.listener) {
|
|
3411
|
-
this.listener.opened();
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
// Tried to fix that Safari does not record the second channel
|
|
3528
|
+
// Does not help
|
|
3529
|
+
//awn.channelCount=this.channelCount;
|
|
3530
|
+
//awn.channelCountMode='explicit';
|
|
3531
|
+
//console.debug('Channel count explicitly set to '+this.channelCount);
|
|
3532
|
+
this.bufferingNode = awn;
|
|
3533
|
+
//this.bufferingNode.channelCount=this.channelCount;
|
|
3534
|
+
this._opened = true;
|
|
3535
|
+
if (this.listener) {
|
|
3536
|
+
this.listener.opened();
|
|
3537
|
+
}
|
|
3412
3538
|
}
|
|
3413
3539
|
}
|
|
3414
|
-
open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
3540
|
+
open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
|
|
3415
3541
|
//console.debug("Capture open: ctx state: "+this.context.state);
|
|
3416
|
-
|
|
3542
|
+
this.context = this._audioContext();
|
|
3543
|
+
if (!this.context) {
|
|
3544
|
+
throw new Error("Could not get audio context!");
|
|
3545
|
+
}
|
|
3546
|
+
if (this.context.state === 'suspended') {
|
|
3417
3547
|
//console.debug("Capture open: Resume context");
|
|
3418
3548
|
this.context.resume().then(() => {
|
|
3419
3549
|
//console.debug("Capture open (ctx resumed): ctx state: "+this.context.state);
|
|
3420
|
-
this._open(channelCount, selDeviceId, autoGainControlConfigs);
|
|
3550
|
+
this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
|
|
3551
|
+
}).catch((err) => {
|
|
3552
|
+
console.error(err.message);
|
|
3553
|
+
throw err;
|
|
3421
3554
|
});
|
|
3422
3555
|
}
|
|
3556
|
+
else if (this.context.state === 'closed') {
|
|
3557
|
+
const msg = 'Error on start capture: The audio context is already closed.';
|
|
3558
|
+
console.error(msg);
|
|
3559
|
+
throw new Error(msg);
|
|
3560
|
+
}
|
|
3423
3561
|
else {
|
|
3424
|
-
this._open(channelCount, selDeviceId, autoGainControlConfigs);
|
|
3562
|
+
this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
|
|
3425
3563
|
}
|
|
3426
3564
|
}
|
|
3427
|
-
_open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
3565
|
+
_open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
|
|
3428
3566
|
this.channelCount = channelCount;
|
|
3429
3567
|
this.framesRecorded = 0;
|
|
3568
|
+
this.context = this._audioContext();
|
|
3569
|
+
if (!this.context) {
|
|
3570
|
+
throw new Error("Could not get audio context!");
|
|
3571
|
+
}
|
|
3430
3572
|
//var msc = new AudioStreamConstr();
|
|
3431
3573
|
// var msc={};
|
|
3432
3574
|
//msc.video = false;
|
|
@@ -3534,7 +3676,7 @@ class AudioCapture {
|
|
|
3534
3676
|
audio: {
|
|
3535
3677
|
deviceId: selDeviceId,
|
|
3536
3678
|
channelCount: channelCount,
|
|
3537
|
-
|
|
3679
|
+
echoCancellation: allowEchoCancellation ? undefined : false
|
|
3538
3680
|
},
|
|
3539
3681
|
video: false,
|
|
3540
3682
|
};
|
|
@@ -3545,106 +3687,109 @@ class AudioCapture {
|
|
|
3545
3687
|
console.debug("Audio capture, AGC: " + this.agcStatus);
|
|
3546
3688
|
let ump = navigator.mediaDevices.getUserMedia(msc);
|
|
3547
3689
|
ump.then((s) => {
|
|
3548
|
-
this.
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
let
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
let
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
//
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
//
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
else {
|
|
3595
|
-
// Register capture interceptor module
|
|
3596
|
-
let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
|
|
3597
|
-
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
3598
|
-
this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
3599
|
-
AudioCapture.captureInterceptorModuleRegistered = true;
|
|
3690
|
+
if (this.context) {
|
|
3691
|
+
this.stream = s;
|
|
3692
|
+
let aTracks = s.getAudioTracks();
|
|
3693
|
+
for (let i = 0; i < aTracks.length; i++) {
|
|
3694
|
+
let aTrack = aTracks[i];
|
|
3695
|
+
console.info("Track audio info: id: " + aTrack.id + " kind: " + aTrack.kind + " label: \"" + aTrack.label + "\"");
|
|
3696
|
+
let mtrSts = aTrack.getSettings();
|
|
3697
|
+
// Typescript lib.dom.ts MediaTrackSettings.channelCount is missing
|
|
3698
|
+
// https://github.com/mdn/browser-compat-data/blob/5493d8f937e05b2ddbd41b99f5bdfad4a1f2ed85/api/MediaTrackSettings.json
|
|
3699
|
+
//@ts-ignore
|
|
3700
|
+
console.info("Track audio settings: Ch cnt: " + mtrSts.channelCount + ", AGC: " + mtrSts.autoGainControl + ", Echo cancell.: " + mtrSts.echoCancellation);
|
|
3701
|
+
if (mtrSts.autoGainControl) {
|
|
3702
|
+
this.agcStatus = mtrSts.autoGainControl;
|
|
3703
|
+
}
|
|
3704
|
+
console.debug("Echo cancellation: " + mtrSts.echoCancellation);
|
|
3705
|
+
}
|
|
3706
|
+
let vTracks = s.getVideoTracks();
|
|
3707
|
+
for (let i = 0; i < vTracks.length; i++) {
|
|
3708
|
+
let vTrack = vTracks[i];
|
|
3709
|
+
console.info("Track video info: id: " + vTrack.id + " kind: " + vTrack.kind + " label: " + vTrack.label);
|
|
3710
|
+
}
|
|
3711
|
+
this.mediaStream = this.context.createMediaStreamSource(s);
|
|
3712
|
+
// stream channel count ( is always 2 !)
|
|
3713
|
+
let streamChannelCount = this.mediaStream.channelCount;
|
|
3714
|
+
console.info("Stream channel count: " + streamChannelCount);
|
|
3715
|
+
// is not set!!
|
|
3716
|
+
//this.currentSampleRate = this.mediaStream.sampleRate;
|
|
3717
|
+
this.currentSampleRate = this.context.sampleRate;
|
|
3718
|
+
console.info("Source audio node: channels: " + streamChannelCount + " samplerate: " + this.currentSampleRate);
|
|
3719
|
+
if (this.audioOutStream) {
|
|
3720
|
+
this.audioOutStream.setFormat(this.channelCount, this.currentSampleRate);
|
|
3721
|
+
}
|
|
3722
|
+
// W3C -> new name is createScriptProcessor
|
|
3723
|
+
//
|
|
3724
|
+
// Again deprecated, but AudioWorker not yet implemented in stable releases (June 2016)
|
|
3725
|
+
// AudioWorker is now AudioWorkletProcessor ... (May 2017)
|
|
3726
|
+
// Update 12-2020:
|
|
3727
|
+
// The ScriptProcessorNode Interface - DEPRECATED
|
|
3728
|
+
// Update 06-2021
|
|
3729
|
+
// AudioWorkletProcessor is here to stay. Web Audio API has now Recommendation status !
|
|
3730
|
+
if (this.context.audioWorklet) {
|
|
3731
|
+
//const workletFileName = ('file-loader!./interceptor_worklet.js');
|
|
3732
|
+
//const workletFileName = 'http://localhost:4200/assets/interceptor_worklet.js';
|
|
3733
|
+
//console.log(awpStr);
|
|
3734
|
+
if (AudioCapture.captureInterceptorModuleRegistered) {
|
|
3735
|
+
// Required capture interceptor module already registered
|
|
3600
3736
|
this.addCaptureInterceptor();
|
|
3601
|
-
}
|
|
3602
|
-
|
|
3603
|
-
|
|
3737
|
+
}
|
|
3738
|
+
else {
|
|
3739
|
+
// Register capture interceptor module
|
|
3740
|
+
let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
|
|
3741
|
+
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
3742
|
+
this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
3743
|
+
AudioCapture.captureInterceptorModuleRegistered = true;
|
|
3744
|
+
this.addCaptureInterceptor();
|
|
3745
|
+
}).catch((error) => {
|
|
3746
|
+
console.log('Could not add module ' + error);
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3604
3749
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3750
|
+
else if (this.context.createScriptProcessor) {
|
|
3751
|
+
// The ScriptProcessorNode Interface - DEPRECATED Only as fallback
|
|
3752
|
+
// TODO should we use streamChannelCount or channelCount here ?
|
|
3753
|
+
let scriptProcessorNode = this.context.createScriptProcessor(AudioCapture.BUFFER_SIZE, streamChannelCount, streamChannelCount);
|
|
3754
|
+
this.bufferingNode = scriptProcessorNode;
|
|
3755
|
+
let c = 0;
|
|
3756
|
+
if (scriptProcessorNode.onaudioprocess) {
|
|
3757
|
+
scriptProcessorNode.onaudioprocess = (e) => {
|
|
3758
|
+
if (this.capturing) {
|
|
3759
|
+
let inBuffer = e.inputBuffer;
|
|
3760
|
+
// only process requested count of channels
|
|
3761
|
+
let currentBuffers = new Array(channelCount);
|
|
3762
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
3763
|
+
let chSamples = inBuffer.getChannelData(ch);
|
|
3764
|
+
let chSamplesCopy = chSamples.slice(0);
|
|
3765
|
+
currentBuffers[ch] = chSamplesCopy.slice(0);
|
|
3766
|
+
if (this.data) {
|
|
3767
|
+
this.data[ch].push(chSamplesCopy);
|
|
3768
|
+
}
|
|
3769
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
3770
|
+
console.debug("Process " + chSamplesCopy.length + " samples.");
|
|
3771
|
+
}
|
|
3772
|
+
this.framesRecorded += chSamplesCopy.length;
|
|
3624
3773
|
}
|
|
3625
|
-
|
|
3626
|
-
|
|
3774
|
+
c++;
|
|
3775
|
+
if (this.audioOutStream) {
|
|
3776
|
+
this.audioOutStream.write(currentBuffers);
|
|
3627
3777
|
}
|
|
3628
|
-
this.framesRecorded += chSamplesCopy.length;
|
|
3629
|
-
}
|
|
3630
|
-
c++;
|
|
3631
|
-
if (this.audioOutStream) {
|
|
3632
|
-
this.audioOutStream.write(currentBuffers);
|
|
3633
3778
|
}
|
|
3779
|
+
};
|
|
3780
|
+
this._opened = true;
|
|
3781
|
+
if (this.listener) {
|
|
3782
|
+
this.listener.opened();
|
|
3634
3783
|
}
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
this.listener.opened();
|
|
3784
|
+
}
|
|
3785
|
+
else {
|
|
3786
|
+
this.listener.error('Browser does not support audio processing (ScriptProcessor.onaudioprocess method not found)!');
|
|
3639
3787
|
}
|
|
3640
3788
|
}
|
|
3641
3789
|
else {
|
|
3642
|
-
this.listener.error('Browser does not support audio processing (
|
|
3790
|
+
this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
|
|
3643
3791
|
}
|
|
3644
3792
|
}
|
|
3645
|
-
else {
|
|
3646
|
-
this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
|
|
3647
|
-
}
|
|
3648
3793
|
}, (e) => {
|
|
3649
3794
|
console.error(e + " Error name: " + e.name);
|
|
3650
3795
|
if (this.listener) {
|
|
@@ -3665,36 +3810,42 @@ class AudioCapture {
|
|
|
3665
3810
|
});
|
|
3666
3811
|
}
|
|
3667
3812
|
_start() {
|
|
3668
|
-
this.
|
|
3669
|
-
|
|
3670
|
-
this.audioOutStream
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
this.listener
|
|
3813
|
+
if (this.context) {
|
|
3814
|
+
this.initData();
|
|
3815
|
+
if (this.audioOutStream) {
|
|
3816
|
+
this.audioOutStream.nextStream();
|
|
3817
|
+
}
|
|
3818
|
+
this.capturing = true;
|
|
3819
|
+
if (this.bufferingNode) {
|
|
3820
|
+
this.mediaStream.connect(this.bufferingNode);
|
|
3821
|
+
this.bufferingNode.connect(this.context.destination);
|
|
3822
|
+
}
|
|
3823
|
+
if (this.listener) {
|
|
3824
|
+
this.listener.started();
|
|
3825
|
+
}
|
|
3679
3826
|
}
|
|
3680
3827
|
}
|
|
3681
3828
|
start() {
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
}
|
|
3686
|
-
else {
|
|
3687
|
-
console.debug("Capture start: audio context not running, state: " + aSt + ", resuming...");
|
|
3688
|
-
this.context.resume().then(() => {
|
|
3689
|
-
console.debug("Capture start: audio context resumed, starting...");
|
|
3829
|
+
if (this.context) {
|
|
3830
|
+
const aSt = this.context.state;
|
|
3831
|
+
if (aSt === 'running') {
|
|
3690
3832
|
this._start();
|
|
3691
|
-
}
|
|
3833
|
+
}
|
|
3834
|
+
else {
|
|
3835
|
+
console.debug("Capture start: audio context not running, state: " + aSt + ", resuming...");
|
|
3836
|
+
this.context.resume().then(() => {
|
|
3837
|
+
console.debug("Capture start: audio context resumed, starting...");
|
|
3838
|
+
this._start();
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3692
3841
|
}
|
|
3693
3842
|
}
|
|
3694
3843
|
stop() {
|
|
3695
3844
|
if (this.disconnectStreams && this.bufferingNode) {
|
|
3696
3845
|
this.mediaStream.disconnect(this.bufferingNode);
|
|
3697
|
-
|
|
3846
|
+
if (this.context) {
|
|
3847
|
+
this.bufferingNode.disconnect(this.context.destination);
|
|
3848
|
+
}
|
|
3698
3849
|
}
|
|
3699
3850
|
try {
|
|
3700
3851
|
if (this.audioOutStream) {
|
|
@@ -3817,7 +3968,7 @@ class AudioCapture {
|
|
|
3817
3968
|
}
|
|
3818
3969
|
audioBuffer() {
|
|
3819
3970
|
let ab = null;
|
|
3820
|
-
if (this.data) {
|
|
3971
|
+
if (this.context && this.data) {
|
|
3821
3972
|
let frameLen = 0;
|
|
3822
3973
|
let ch0Data = this.data[0];
|
|
3823
3974
|
for (let ch0Chk of ch0Data) {
|
|
@@ -9001,7 +9152,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9001
9152
|
withCredentials: this.withCredentials
|
|
9002
9153
|
});
|
|
9003
9154
|
}
|
|
9004
|
-
chunkAudioRequest(
|
|
9155
|
+
chunkAudioRequest(baseAudioUrl, startFrame = 0, frameLength) {
|
|
9005
9156
|
let ausps = new URLSearchParams();
|
|
9006
9157
|
ausps.set('startFrame', startFrame.toString());
|
|
9007
9158
|
ausps.set('frameLength', frameLength.toString());
|
|
@@ -9033,12 +9184,12 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9033
9184
|
// console.error("Could not read WAVE format of original download chunk!");
|
|
9034
9185
|
// }
|
|
9035
9186
|
if (pcmFmt && orgFl) {
|
|
9036
|
-
|
|
9187
|
+
AudioContextProvider.decodeAudioData(resp.body).then((ab) => {
|
|
9037
9188
|
//console.debug("Decoded audio chunk frames: "+ab.length);
|
|
9038
9189
|
let chDl = new ChunkDownload(pcmFmt, orgFl, ab);
|
|
9039
9190
|
observer.next(chDl);
|
|
9040
9191
|
observer.complete();
|
|
9041
|
-
}
|
|
9192
|
+
}).catch(error => {
|
|
9042
9193
|
//if(error instanceof HttpErrorResponse) {
|
|
9043
9194
|
// if (error.status == 404) {
|
|
9044
9195
|
// // Interpret not as an error, the file ist not recorded yet
|
|
@@ -9074,7 +9225,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9074
9225
|
});
|
|
9075
9226
|
return obs;
|
|
9076
9227
|
}
|
|
9077
|
-
chunkAudioRequestToNetAudioBuffer(
|
|
9228
|
+
chunkAudioRequestToNetAudioBuffer(baseAudioUrl, startFrame = 0, orgSampleRate, seconds, frames) {
|
|
9078
9229
|
//let audioUrl=baseAudioUrl+'?startFrame='+startFrame+'&frameLength='+frameLength;
|
|
9079
9230
|
//let audioUrl=new URL(baseAudioUrl);
|
|
9080
9231
|
// if(orgSampleRate!=null && frameLength%orgSampleRate>0){
|
|
@@ -9098,7 +9249,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9098
9249
|
if (resp.body) {
|
|
9099
9250
|
//console.debug("chunkAudioRequestTonetAb: subscriber.closed: "+subscriber.closed);
|
|
9100
9251
|
//console.debug("chunkAudioRequestTonetAb: Audio file bytes: "+resp.body.byteLength);
|
|
9101
|
-
|
|
9252
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9102
9253
|
//console.debug("chunkAudioRequestTonetAb: Decoded audio chunk frames for netAb: "+ab.length);
|
|
9103
9254
|
//console.debug("chunkAudioRequestTonetAb: Create netAb ab from chunk ab...");
|
|
9104
9255
|
if (frames === null) {
|
|
@@ -9112,7 +9263,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9112
9263
|
//console.debug("Platform sr: "+ab.sampleRate+", file sr: "+orgSampleRate+", decoded/org frame length: "+fl+"/"+frames+", ab.length: "+ab.length);
|
|
9113
9264
|
}
|
|
9114
9265
|
}
|
|
9115
|
-
let nab = NetAudioBuffer.fromChunkAudioBuffer(
|
|
9266
|
+
let nab = NetAudioBuffer.fromChunkAudioBuffer(this, baseAudioUrl, ab, fl, frameLength);
|
|
9116
9267
|
//let rp=new ReadyProvider();
|
|
9117
9268
|
//nab.readyProvider=rp;
|
|
9118
9269
|
//rp.ready();
|
|
@@ -9124,7 +9275,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9124
9275
|
subscriber.next(nab);
|
|
9125
9276
|
subscriber.complete();
|
|
9126
9277
|
}
|
|
9127
|
-
}
|
|
9278
|
+
}).catch(error => {
|
|
9128
9279
|
console.error('chunkAudioRequestToNetAb: error: ' + error);
|
|
9129
9280
|
//if(error instanceof HttpErrorResponse) {
|
|
9130
9281
|
subscriber.error(error);
|
|
@@ -9232,7 +9383,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9232
9383
|
withCredentials: this.withCredentials
|
|
9233
9384
|
});
|
|
9234
9385
|
}
|
|
9235
|
-
chunkAudioRequestToIndDb(
|
|
9386
|
+
chunkAudioRequestToIndDb(persistentAudioStorageTarget, inddbAudioBuffer, baseAudioUrl, startFrame = 0, orgSampleRate, seconds) {
|
|
9236
9387
|
//let audioUrl=baseAudioUrl+'?startFrame='+startFrame+'&frameLength='+frameLength;
|
|
9237
9388
|
//let audioUrl=new URL(baseAudioUrl);
|
|
9238
9389
|
let frameLength = orgSampleRate * Math.round(seconds); // Important: multiple of original sample rate to prevent numeric rounding errors on resampling. 10 seconds is a good value for ind db storage.
|
|
@@ -9251,7 +9402,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9251
9402
|
if (resp.body) {
|
|
9252
9403
|
//console.debug("chunkAudioRequestToIndDb: subscriber.closed: "+subscriber.closed);
|
|
9253
9404
|
//console.debug("chunkAudioRequestToIndDb: Audio file bytes: "+resp.body.byteLength);
|
|
9254
|
-
|
|
9405
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9255
9406
|
//console.debug("chunkAudioRequestToIndDb: Decoded audio chunk frames for inddb: "+ab.length);
|
|
9256
9407
|
if (!inddbAudioBuffer) {
|
|
9257
9408
|
//console.debug("chunkAudioRequestToIndDb: Create inddb ab from chunk ab...");
|
|
@@ -9295,7 +9446,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9295
9446
|
}
|
|
9296
9447
|
});
|
|
9297
9448
|
}
|
|
9298
|
-
}
|
|
9449
|
+
}).catch(error => {
|
|
9299
9450
|
console.error('chunkAudioRequestToIndDb: error: ' + error);
|
|
9300
9451
|
//if(error instanceof HttpErrorResponse) {
|
|
9301
9452
|
subscriber.error(error);
|
|
@@ -9303,8 +9454,9 @@ class RecordingService extends BasicRecordingService {
|
|
|
9303
9454
|
});
|
|
9304
9455
|
}
|
|
9305
9456
|
else {
|
|
9306
|
-
|
|
9307
|
-
|
|
9457
|
+
const errMsg = 'chunkAudioRequestToIndDb: Fetching audio file: response has no body';
|
|
9458
|
+
console.error(errMsg);
|
|
9459
|
+
subscriber.error(new Error(errMsg));
|
|
9308
9460
|
}
|
|
9309
9461
|
},
|
|
9310
9462
|
error: (error) => {
|
|
@@ -9316,13 +9468,13 @@ class RecordingService extends BasicRecordingService {
|
|
|
9316
9468
|
});
|
|
9317
9469
|
return obs;
|
|
9318
9470
|
}
|
|
9319
|
-
chunkedAudioRequestToArrayBuffer(
|
|
9471
|
+
chunkedAudioRequestToArrayBuffer(baseAudioUrl, orgSampleRate, seconds) {
|
|
9320
9472
|
let obs = new Observable(subscriber => {
|
|
9321
9473
|
let arrayAudioBuffer = null;
|
|
9322
9474
|
let startFrame = 0;
|
|
9323
9475
|
let frameLength = orgSampleRate * Math.round(seconds); // Important: multiple of original sample rate to prevent numeric rounding errors on resampling.
|
|
9324
9476
|
//console.debug("Chunk audio request startFrame 0");
|
|
9325
|
-
let subscr = this.chunkAudioRequest(
|
|
9477
|
+
let subscr = this.chunkAudioRequest(baseAudioUrl, startFrame, frameLength).pipe(expand(value => {
|
|
9326
9478
|
if (subscriber.closed) {
|
|
9327
9479
|
subscr.unsubscribe();
|
|
9328
9480
|
}
|
|
@@ -9341,7 +9493,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9341
9493
|
startFrame += frameLength;
|
|
9342
9494
|
//console.debug("Next start frame: "+startFrame);
|
|
9343
9495
|
//console.debug("chunkedAudioRequest: expand() subscriber.closed: "+subscriber.closed);
|
|
9344
|
-
return this.chunkAudioRequest(
|
|
9496
|
+
return this.chunkAudioRequest(baseAudioUrl, startFrame, frameLength);
|
|
9345
9497
|
}
|
|
9346
9498
|
}
|
|
9347
9499
|
else {
|
|
@@ -9411,14 +9563,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9411
9563
|
});
|
|
9412
9564
|
return obs;
|
|
9413
9565
|
}
|
|
9414
|
-
chunkedInddbAudioRequest(
|
|
9566
|
+
chunkedInddbAudioRequest(persistentAudioStorageTarget, baseAudioUrl, orgSampleRate, seconds) {
|
|
9415
9567
|
let obs = new Observable(subscriber => {
|
|
9416
9568
|
let inddbAudioBuffer = null;
|
|
9417
9569
|
let startFrame = 0;
|
|
9418
9570
|
//let frameLength = DEFAULT_CHUNKED_DOWNLOAD_FRAMELENGTH;
|
|
9419
9571
|
let frameLength = orgSampleRate * Math.round(seconds);
|
|
9420
9572
|
//console.debug("chunkedInddbAudioRequest: Chunk audio request for inddb. startFrame: "+startFrame);
|
|
9421
|
-
let subscr = this.chunkAudioRequestToIndDb(
|
|
9573
|
+
let subscr = this.chunkAudioRequestToIndDb(persistentAudioStorageTarget, null, baseAudioUrl, startFrame, orgSampleRate, seconds).pipe(expand(iab => {
|
|
9422
9574
|
// console.debug("chunkedInddbAudioRequest (pipe/expand): Got inddb ab: "+iab);
|
|
9423
9575
|
if (subscriber.closed) {
|
|
9424
9576
|
subscr.unsubscribe();
|
|
@@ -9439,7 +9591,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9439
9591
|
startFrame += frameLength;
|
|
9440
9592
|
//console.debug("Next start frame: "+startFrame);
|
|
9441
9593
|
//console.debug("chunkedInddbAudioRequest: expand() subscriber.closed: "+subscriber.closed);
|
|
9442
|
-
return this.chunkAudioRequestToIndDb(
|
|
9594
|
+
return this.chunkAudioRequestToIndDb(persistentAudioStorageTarget, inddbAudioBuffer, baseAudioUrl, startFrame, orgSampleRate, seconds);
|
|
9443
9595
|
}
|
|
9444
9596
|
// } else {
|
|
9445
9597
|
// return EMPTY;
|
|
@@ -9631,7 +9783,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9631
9783
|
// //let recUrl=new URL(recUrlStr);
|
|
9632
9784
|
// return this.chunkedInddbAudioRequest(aCtx,recUrlStr);
|
|
9633
9785
|
// }
|
|
9634
|
-
fetchRecordingFileAudioBuffer(
|
|
9786
|
+
fetchRecordingFileAudioBuffer(projectName, recordingFile) {
|
|
9635
9787
|
let wobs = new Observable(observer => {
|
|
9636
9788
|
let recFileId = recordingFile.recordingFileId;
|
|
9637
9789
|
if (!recFileId) {
|
|
@@ -9643,10 +9795,10 @@ class RecordingService extends BasicRecordingService {
|
|
|
9643
9795
|
next: resp => {
|
|
9644
9796
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9645
9797
|
if (resp.body) {
|
|
9646
|
-
|
|
9798
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9647
9799
|
observer.next(ab);
|
|
9648
9800
|
observer.complete();
|
|
9649
|
-
}
|
|
9801
|
+
}).catch(error => {
|
|
9650
9802
|
observer.error(error);
|
|
9651
9803
|
observer.complete();
|
|
9652
9804
|
});
|
|
@@ -9691,7 +9843,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9691
9843
|
//console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
|
|
9692
9844
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9693
9845
|
if (resp.body) {
|
|
9694
|
-
|
|
9846
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9695
9847
|
let abs = new AudioBufferSource(ab);
|
|
9696
9848
|
let adh = new AudioDataHolder(abs);
|
|
9697
9849
|
RecordingFileUtils.setAudioData(recordingFile, adh);
|
|
@@ -9705,7 +9857,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9705
9857
|
observer.next(recordingFile);
|
|
9706
9858
|
observer.complete();
|
|
9707
9859
|
}
|
|
9708
|
-
}
|
|
9860
|
+
}).catch(error => {
|
|
9709
9861
|
observer.error(error);
|
|
9710
9862
|
observer.complete();
|
|
9711
9863
|
});
|
|
@@ -9738,7 +9890,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9738
9890
|
});
|
|
9739
9891
|
return wobs;
|
|
9740
9892
|
}
|
|
9741
|
-
fetchSprRecordingFileAudioBuffer(
|
|
9893
|
+
fetchSprRecordingFileAudioBuffer(projectName, recordingFile) {
|
|
9742
9894
|
let wobs = new Observable(observer => {
|
|
9743
9895
|
if (recordingFile.session) {
|
|
9744
9896
|
let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
@@ -9746,11 +9898,11 @@ class RecordingService extends BasicRecordingService {
|
|
|
9746
9898
|
next: resp => {
|
|
9747
9899
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9748
9900
|
if (resp.body) {
|
|
9749
|
-
|
|
9901
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9750
9902
|
//RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
|
|
9751
9903
|
observer.next(ab);
|
|
9752
9904
|
observer.complete();
|
|
9753
|
-
}
|
|
9905
|
+
}).catch(error => {
|
|
9754
9906
|
observer.error(error);
|
|
9755
9907
|
observer.complete();
|
|
9756
9908
|
});
|
|
@@ -9778,14 +9930,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9778
9930
|
});
|
|
9779
9931
|
return wobs;
|
|
9780
9932
|
}
|
|
9781
|
-
fetchSprRecordingFileArrayAudioBuffer(
|
|
9933
|
+
fetchSprRecordingFileArrayAudioBuffer(projectName, recordingFile) {
|
|
9782
9934
|
let wobs = new Observable(observer => {
|
|
9783
9935
|
if (recordingFile.session) {
|
|
9784
9936
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9785
9937
|
if (baseUrl) {
|
|
9786
9938
|
if (recordingFile.samplerate) {
|
|
9787
9939
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9788
|
-
let obs = this.chunkedAudioRequestToArrayBuffer(
|
|
9940
|
+
let obs = this.chunkedAudioRequestToArrayBuffer(baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9789
9941
|
//let obs = this.fetchSprAudiofileArrayBuffer(aCtx,projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
9790
9942
|
let subscr = obs.subscribe({
|
|
9791
9943
|
next: aab => {
|
|
@@ -9822,14 +9974,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9822
9974
|
});
|
|
9823
9975
|
return wobs;
|
|
9824
9976
|
}
|
|
9825
|
-
fetchRecordingFileArrayAudioBuffer(
|
|
9977
|
+
fetchRecordingFileArrayAudioBuffer(projectName, recordingFile) {
|
|
9826
9978
|
let wobs = new Observable(observer => {
|
|
9827
9979
|
if (recordingFile.session) {
|
|
9828
9980
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
9829
9981
|
if (baseUrl) {
|
|
9830
9982
|
if (recordingFile.samplerate) {
|
|
9831
9983
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9832
|
-
let obs = this.chunkedAudioRequestToArrayBuffer(
|
|
9984
|
+
let obs = this.chunkedAudioRequestToArrayBuffer(baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9833
9985
|
//let obs = this.fetchSprAudiofileArrayBuffer(aCtx,projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
9834
9986
|
let subscr = obs.subscribe({
|
|
9835
9987
|
next: aab => {
|
|
@@ -9866,14 +10018,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9866
10018
|
});
|
|
9867
10019
|
return wobs;
|
|
9868
10020
|
}
|
|
9869
|
-
fetchSprRecordingFileIndDbAudioBuffer(
|
|
10021
|
+
fetchSprRecordingFileIndDbAudioBuffer(persistentAudioStorageTarget, projectName, recordingFile) {
|
|
9870
10022
|
let wobs = new Observable(observer => {
|
|
9871
10023
|
if (recordingFile.session) {
|
|
9872
10024
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9873
10025
|
if (baseUrl) {
|
|
9874
10026
|
if (recordingFile.samplerate) {
|
|
9875
10027
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9876
|
-
let obs = this.chunkedInddbAudioRequest(
|
|
10028
|
+
let obs = this.chunkedInddbAudioRequest(persistentAudioStorageTarget, baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9877
10029
|
let subscr = obs.subscribe({
|
|
9878
10030
|
next: aab => {
|
|
9879
10031
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -9909,14 +10061,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9909
10061
|
});
|
|
9910
10062
|
return wobs;
|
|
9911
10063
|
}
|
|
9912
|
-
fetchRecordingFileIndDbAudioBuffer(
|
|
10064
|
+
fetchRecordingFileIndDbAudioBuffer(persistentAudioStorageTarget, projectName, recordingFile) {
|
|
9913
10065
|
let wobs = new Observable(observer => {
|
|
9914
10066
|
if (recordingFile.session) {
|
|
9915
10067
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
9916
10068
|
if (baseUrl) {
|
|
9917
10069
|
if (recordingFile.samplerate) {
|
|
9918
10070
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9919
|
-
let obs = this.chunkedInddbAudioRequest(
|
|
10071
|
+
let obs = this.chunkedInddbAudioRequest(persistentAudioStorageTarget, baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9920
10072
|
let subscr = obs.subscribe({
|
|
9921
10073
|
next: aab => {
|
|
9922
10074
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -9952,14 +10104,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9952
10104
|
});
|
|
9953
10105
|
return wobs;
|
|
9954
10106
|
}
|
|
9955
|
-
fetchSprRecordingFileNetAudioBuffer(
|
|
10107
|
+
fetchSprRecordingFileNetAudioBuffer(projectName, recordingFile) {
|
|
9956
10108
|
let wobs = new Observable(observer => {
|
|
9957
10109
|
if (recordingFile.session) {
|
|
9958
10110
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9959
10111
|
if (baseUrl) {
|
|
9960
10112
|
let seconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9961
10113
|
if (recordingFile.samplerate) {
|
|
9962
|
-
let obs = this.chunkAudioRequestToNetAudioBuffer(
|
|
10114
|
+
let obs = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, recordingFile.samplerate, seconds, recordingFile.frames);
|
|
9963
10115
|
let subscr = obs.subscribe({
|
|
9964
10116
|
next: aab => {
|
|
9965
10117
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -10000,14 +10152,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
10000
10152
|
});
|
|
10001
10153
|
return wobs;
|
|
10002
10154
|
}
|
|
10003
|
-
fetchRecordingFileNetAudioBuffer(
|
|
10155
|
+
fetchRecordingFileNetAudioBuffer(projectName, recordingFile) {
|
|
10004
10156
|
let wobs = new Observable(observer => {
|
|
10005
10157
|
if (recordingFile.session) {
|
|
10006
10158
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
10007
10159
|
if (baseUrl) {
|
|
10008
10160
|
let seconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
10009
10161
|
if (recordingFile.samplerate) {
|
|
10010
|
-
let obs = this.chunkAudioRequestToNetAudioBuffer(
|
|
10162
|
+
let obs = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, recordingFile.samplerate, seconds, recordingFile.frames);
|
|
10011
10163
|
let subscr = obs.subscribe({
|
|
10012
10164
|
next: aab => {
|
|
10013
10165
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -10057,7 +10209,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10057
10209
|
//console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
|
|
10058
10210
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
10059
10211
|
if (resp.body) {
|
|
10060
|
-
|
|
10212
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
10061
10213
|
let abs = new AudioBufferSource(ab);
|
|
10062
10214
|
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(abs));
|
|
10063
10215
|
if (this.debugDelay > 0) {
|
|
@@ -10070,7 +10222,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10070
10222
|
observer.next(recordingFile);
|
|
10071
10223
|
observer.complete();
|
|
10072
10224
|
}
|
|
10073
|
-
}
|
|
10225
|
+
}).catch(error => {
|
|
10074
10226
|
observer.error(error);
|
|
10075
10227
|
observer.complete();
|
|
10076
10228
|
});
|
|
@@ -10102,9 +10254,8 @@ class RecordingService extends BasicRecordingService {
|
|
|
10102
10254
|
let wobs = new Observable(observer => {
|
|
10103
10255
|
let obs = this.fetchSprAudiofile(projectName, sessId, itemcode, version);
|
|
10104
10256
|
obs.subscribe({ next: resp => {
|
|
10105
|
-
// Do not use Promise version, which does not work with Safari 13
|
|
10106
10257
|
if (resp.body) {
|
|
10107
|
-
|
|
10258
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
10108
10259
|
let abs = new AudioBufferSource(ab);
|
|
10109
10260
|
let adh = new AudioDataHolder(abs);
|
|
10110
10261
|
let rf = new SprRecordingFile(sessId, itemcode, version, adh);
|
|
@@ -10118,7 +10269,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10118
10269
|
observer.next(rf);
|
|
10119
10270
|
observer.complete();
|
|
10120
10271
|
}
|
|
10121
|
-
});
|
|
10272
|
+
}).catch((reason) => { observer.error(reason); });
|
|
10122
10273
|
}
|
|
10123
10274
|
else {
|
|
10124
10275
|
observer.error();
|
|
@@ -10153,23 +10304,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
10153
10304
|
args: [SPEECHRECORDER_CONFIG]
|
|
10154
10305
|
}] }]; } });
|
|
10155
10306
|
|
|
10156
|
-
class AudioContextProvider {
|
|
10157
|
-
static audioContextInstance() {
|
|
10158
|
-
if (!this._audioContext) {
|
|
10159
|
-
let debugFail = false;
|
|
10160
|
-
if (!window.AudioContext || typeof window.AudioContext !== 'function' || debugFail) {
|
|
10161
|
-
throw new Error('Browser does not support Web Audio API!');
|
|
10162
|
-
this._audioContext = null;
|
|
10163
|
-
}
|
|
10164
|
-
else {
|
|
10165
|
-
this._audioContext = new window.AudioContext();
|
|
10166
|
-
}
|
|
10167
|
-
}
|
|
10168
|
-
return this._audioContext;
|
|
10169
|
-
}
|
|
10170
|
-
}
|
|
10171
|
-
AudioContextProvider._audioContext = null;
|
|
10172
|
-
|
|
10173
10307
|
class Item {
|
|
10174
10308
|
constructor(_promptAsString, _training, _recording) {
|
|
10175
10309
|
this._promptAsString = _promptAsString;
|
|
@@ -12040,6 +12174,7 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12040
12174
|
this._wakeLock = false;
|
|
12041
12175
|
this._selectedDeviceId = undefined;
|
|
12042
12176
|
this._channelCount = 2;
|
|
12177
|
+
this._allowEchoCancellation = false;
|
|
12043
12178
|
this._session = null;
|
|
12044
12179
|
this._recordingFile = null;
|
|
12045
12180
|
this.startedDate = null;
|
|
@@ -12081,6 +12216,12 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12081
12216
|
this.streamLevelMeasure = new StreamLevelMeasure();
|
|
12082
12217
|
this.selCaptureDeviceId = null;
|
|
12083
12218
|
}
|
|
12219
|
+
get allowEchoCancellation() {
|
|
12220
|
+
return this._allowEchoCancellation;
|
|
12221
|
+
}
|
|
12222
|
+
set allowEchoCancellation(value) {
|
|
12223
|
+
this._allowEchoCancellation = value;
|
|
12224
|
+
}
|
|
12084
12225
|
get maxAutoNetMemStoreSamples() {
|
|
12085
12226
|
return this._maxAutoNetMemStoreSamples;
|
|
12086
12227
|
}
|
|
@@ -12465,7 +12606,7 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12465
12606
|
else {
|
|
12466
12607
|
console.log("Open session with default audio device for " + this._channelCount + " channels");
|
|
12467
12608
|
}
|
|
12468
|
-
this.ac.open(this._channelCount, this._selectedDeviceId, this._autoGainControlConfigs);
|
|
12609
|
+
this.ac.open(this._channelCount, this._selectedDeviceId, this._autoGainControlConfigs, this._allowEchoCancellation);
|
|
12469
12610
|
}
|
|
12470
12611
|
else {
|
|
12471
12612
|
this.ac.start();
|
|
@@ -12821,6 +12962,7 @@ class SessionManager extends BasicRecorder {
|
|
|
12821
12962
|
this.bpo = bpo;
|
|
12822
12963
|
this.renderer = renderer;
|
|
12823
12964
|
this.recFileService = recFileService;
|
|
12965
|
+
this.offlineAudioContext = null;
|
|
12824
12966
|
this.enableUploadRecordings = true;
|
|
12825
12967
|
this.enableDownloadRecordings = false;
|
|
12826
12968
|
this.status = 0 /* BLOCKED */;
|
|
@@ -12877,37 +13019,58 @@ class SessionManager extends BasicRecorder {
|
|
|
12877
13019
|
this.transportActions.nextAction.disabled = true;
|
|
12878
13020
|
this.transportActions.pauseAction.disabled = true;
|
|
12879
13021
|
this.playStartAction.disabled = true;
|
|
12880
|
-
let context
|
|
12881
|
-
try {
|
|
12882
|
-
|
|
12883
|
-
}
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
13022
|
+
// let context:AudioContext|null=null;
|
|
13023
|
+
// try {
|
|
13024
|
+
// context = AudioContextProvider.audioContextInstance();
|
|
13025
|
+
// } catch (err) {
|
|
13026
|
+
// this.status = Status.ERROR;
|
|
13027
|
+
// let errMsg = 'Unknown error';
|
|
13028
|
+
// if(err instanceof Error){
|
|
13029
|
+
// errMsg=err.message;
|
|
13030
|
+
// }
|
|
13031
|
+
// this.statusMsg = 'ERROR: ' + errMsg;
|
|
13032
|
+
// this.statusAlertType = 'error';
|
|
13033
|
+
// this.dialog.open(MessageDialog, {
|
|
13034
|
+
// data: {
|
|
13035
|
+
// type: 'error',
|
|
13036
|
+
// title: 'Error',
|
|
13037
|
+
// msg: errMsg,
|
|
13038
|
+
// advice: 'Please use a supported browser.',
|
|
13039
|
+
// }
|
|
13040
|
+
// });
|
|
13041
|
+
// return;
|
|
13042
|
+
// }
|
|
13043
|
+
// if(context) {
|
|
13044
|
+
// console.info("State of audio context: " + context.state)
|
|
13045
|
+
// }else{
|
|
13046
|
+
// console.info("No audio context available!");
|
|
13047
|
+
// }
|
|
13048
|
+
// if (!context || !navigator.mediaDevices) {
|
|
13049
|
+
// this.status = Status.ERROR;
|
|
13050
|
+
// let errMsg = 'Browser does not support Media streams!';
|
|
13051
|
+
// this.statusMsg = 'ERROR: ' + errMsg;
|
|
13052
|
+
// this.statusAlertType = 'error';
|
|
13053
|
+
// this.dialog.open(MessageDialog, {
|
|
13054
|
+
// data: {
|
|
13055
|
+
// type: 'error',
|
|
13056
|
+
// title: 'Error',
|
|
13057
|
+
// msg: errMsg,
|
|
13058
|
+
// advice: 'Please use a supported browser.',
|
|
13059
|
+
// }
|
|
13060
|
+
// });
|
|
13061
|
+
// return;
|
|
13062
|
+
// } else {
|
|
13063
|
+
this.ac = new AudioCapture();
|
|
13064
|
+
if (this.ac) {
|
|
13065
|
+
this.transportActions.startAction.onAction = () => this.startItem();
|
|
13066
|
+
this.ac.listener = this;
|
|
13067
|
+
this.configureStreamCaptureStream();
|
|
13068
|
+
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
13069
|
+
//this.ac.listDevices();
|
|
12904
13070
|
}
|
|
12905
13071
|
else {
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
if (!context || !navigator.mediaDevices) {
|
|
12909
|
-
this.status = 9 /* ERROR */;
|
|
12910
|
-
let errMsg = 'Browser does not support Media streams!';
|
|
13072
|
+
this.transportActions.startAction.disabled = true;
|
|
13073
|
+
let errMsg = 'Browser does not support Media/Audio API!';
|
|
12911
13074
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
12912
13075
|
this.statusAlertType = 'error';
|
|
12913
13076
|
this.dialog.open(MessageDialog, {
|
|
@@ -12920,38 +13083,13 @@ class SessionManager extends BasicRecorder {
|
|
|
12920
13083
|
});
|
|
12921
13084
|
return;
|
|
12922
13085
|
}
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
//this.ac.listDevices();
|
|
12931
|
-
}
|
|
12932
|
-
else {
|
|
12933
|
-
this.transportActions.startAction.disabled = true;
|
|
12934
|
-
let errMsg = 'Browser does not support Media/Audio API!';
|
|
12935
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
12936
|
-
this.statusAlertType = 'error';
|
|
12937
|
-
this.dialog.open(MessageDialog, {
|
|
12938
|
-
data: {
|
|
12939
|
-
type: 'error',
|
|
12940
|
-
title: 'Error',
|
|
12941
|
-
msg: errMsg,
|
|
12942
|
-
advice: 'Please use a supported browser.',
|
|
12943
|
-
}
|
|
12944
|
-
});
|
|
12945
|
-
return;
|
|
12946
|
-
}
|
|
12947
|
-
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
12948
|
-
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
12949
|
-
this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
12950
|
-
this.transportActions.fwdAction.onAction = () => this.nextItem();
|
|
12951
|
-
this.transportActions.fwdNextAction.onAction = () => this.nextUnrecordedItem();
|
|
12952
|
-
this.transportActions.bwdAction.onAction = () => this.prevItem();
|
|
12953
|
-
this.playStartAction.onAction = () => this.controlAudioPlayer?.start();
|
|
12954
|
-
}
|
|
13086
|
+
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
13087
|
+
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
13088
|
+
this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
13089
|
+
this.transportActions.fwdAction.onAction = () => this.nextItem();
|
|
13090
|
+
this.transportActions.fwdNextAction.onAction = () => this.nextUnrecordedItem();
|
|
13091
|
+
this.transportActions.bwdAction.onAction = () => this.prevItem();
|
|
13092
|
+
this.playStartAction.onAction = () => this.controlAudioPlayer?.start();
|
|
12955
13093
|
this.startStopSignalState = 4 /* OFF */;
|
|
12956
13094
|
}
|
|
12957
13095
|
onKeyPress(ke) {
|
|
@@ -13258,7 +13396,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13258
13396
|
}
|
|
13259
13397
|
else {
|
|
13260
13398
|
//console.debug("Fetch audio and store to indexed db...");
|
|
13261
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileIndDbAudioBuffer(this.
|
|
13399
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileIndDbAudioBuffer(this._persistentAudioStorageTarget, this._session.project, rf).subscribe({
|
|
13262
13400
|
next: (iab) => {
|
|
13263
13401
|
//console.debug("Sessionmanager: Received inddb audio buffer: "+iab);
|
|
13264
13402
|
nextIab = iab;
|
|
@@ -13300,7 +13438,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13300
13438
|
// Fetch chunked audio buffer from network
|
|
13301
13439
|
let nextNetAb = null;
|
|
13302
13440
|
//console.debug("Fetch chunked audio from network");
|
|
13303
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileNetAudioBuffer(this.
|
|
13441
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileNetAudioBuffer(this._session.project, rf).subscribe({
|
|
13304
13442
|
next: (netAb) => {
|
|
13305
13443
|
//console.debug("Sessionmanager: Received net audio buffer: "+netAb);
|
|
13306
13444
|
nextNetAb = netAb;
|
|
@@ -13342,7 +13480,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13342
13480
|
// Fetch chunked array audio buffer
|
|
13343
13481
|
let nextAab = null;
|
|
13344
13482
|
//console.debug("Fetch audio and store to (chunked) array buffer...");
|
|
13345
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileArrayAudioBuffer(this.
|
|
13483
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileArrayAudioBuffer(this._session.project, rf).subscribe({
|
|
13346
13484
|
next: (aab) => {
|
|
13347
13485
|
nextAab = aab;
|
|
13348
13486
|
},
|
|
@@ -13380,7 +13518,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13380
13518
|
else {
|
|
13381
13519
|
// Fetch regular audio buffer
|
|
13382
13520
|
//console.debug("Fetch audio and store to audio buffer...");
|
|
13383
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this.
|
|
13521
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._session.project, rf).subscribe({
|
|
13384
13522
|
next: (ab) => {
|
|
13385
13523
|
this.liveLevelDisplayState = State.READY;
|
|
13386
13524
|
let fabDh = null;
|
|
@@ -13767,7 +13905,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13767
13905
|
const sr = this.ac.currentSampleRate;
|
|
13768
13906
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
13769
13907
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
13770
|
-
let netAb = new NetAudioBuffer(this.
|
|
13908
|
+
let netAb = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
13771
13909
|
as = netAb;
|
|
13772
13910
|
if (this.uploadSet) {
|
|
13773
13911
|
//let rp=new ReadyProvider();
|
|
@@ -13814,7 +13952,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13814
13952
|
const sr = this.ac.currentSampleRate;
|
|
13815
13953
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
13816
13954
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
13817
|
-
const netAb = new NetAudioBuffer(this.
|
|
13955
|
+
const netAb = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
13818
13956
|
as = netAb;
|
|
13819
13957
|
if (this.uploadSet) {
|
|
13820
13958
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -13852,8 +13990,8 @@ class SessionManager extends BasicRecorder {
|
|
|
13852
13990
|
// TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
|
|
13853
13991
|
// TODO duplicate conversion for manual download
|
|
13854
13992
|
//console.log("Build wav writer...");
|
|
13855
|
-
this.processingRecording = true;
|
|
13856
13993
|
if (ab) {
|
|
13994
|
+
this.processingRecording = true;
|
|
13857
13995
|
let ww = new WavWriter();
|
|
13858
13996
|
//new REST API URL
|
|
13859
13997
|
let apiEndPoint = '';
|
|
@@ -14361,18 +14499,10 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
14361
14499
|
console.error(errMsg);
|
|
14362
14500
|
}
|
|
14363
14501
|
ngOnInit() {
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
|
-
|
|
14367
|
-
|
|
14368
|
-
}
|
|
14369
|
-
this.sm.controlAudioPlayer = this.controlAudioPlayer;
|
|
14370
|
-
this.sm.statusAlertType = 'info';
|
|
14371
|
-
this.sm.statusMsg = 'Player initialized.';
|
|
14372
|
-
}
|
|
14373
|
-
catch (err) {
|
|
14374
|
-
this.handleError(err);
|
|
14375
|
-
}
|
|
14502
|
+
this.controlAudioPlayer = new AudioPlayer(this);
|
|
14503
|
+
this.sm.controlAudioPlayer = this.controlAudioPlayer;
|
|
14504
|
+
this.sm.statusAlertType = 'info';
|
|
14505
|
+
this.sm.statusMsg = 'Player initialized.';
|
|
14376
14506
|
}
|
|
14377
14507
|
ngAfterViewInit() {
|
|
14378
14508
|
// let wakeLockSupp=('wakeLock' in navigator);
|
|
@@ -14629,6 +14759,9 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
14629
14759
|
chCnt = ProjectUtil.audioChannelCount(project);
|
|
14630
14760
|
console.info("Project requested recording channel count: " + chCnt);
|
|
14631
14761
|
this.sm.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
14762
|
+
if (project.allowEchoCancellation !== undefined) {
|
|
14763
|
+
this.sm.allowEchoCancellation = project.allowEchoCancellation;
|
|
14764
|
+
}
|
|
14632
14765
|
if (project.chunkedRecording === true) {
|
|
14633
14766
|
console.debug("Enable chunked upload: chunkSize: " + BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS);
|
|
14634
14767
|
this.sm.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
|
|
@@ -14725,10 +14858,8 @@ class AudioDisplayPlayer {
|
|
|
14725
14858
|
this.ref = ref;
|
|
14726
14859
|
this.eRef = eRef;
|
|
14727
14860
|
this._audioUrl = null;
|
|
14728
|
-
this.aCtx = null;
|
|
14729
14861
|
this._audioClip = null;
|
|
14730
14862
|
this.currentLoader = null;
|
|
14731
|
-
//console.log("constructor: "+this.ac);
|
|
14732
14863
|
this.parentE = this.eRef.nativeElement;
|
|
14733
14864
|
this.playStartAction = new Action("Start");
|
|
14734
14865
|
this.playSelectionAction = new Action("Play selected");
|
|
@@ -14736,26 +14867,18 @@ class AudioDisplayPlayer {
|
|
|
14736
14867
|
this.status = "Player created.";
|
|
14737
14868
|
}
|
|
14738
14869
|
ngOnInit() {
|
|
14739
|
-
//console.log("OnInit: "+this.ac);
|
|
14740
14870
|
this.zoomSelectedAction = this.audioDisplayScrollPane.zoomSelectedAction;
|
|
14741
14871
|
this.zoomFitToPanelAction = this.audioDisplayScrollPane.zoomFitToPanelAction;
|
|
14742
14872
|
this.zoomOutAction = this.audioDisplayScrollPane.zoomOutAction;
|
|
14743
14873
|
this.zoomInAction = this.audioDisplayScrollPane.zoomInAction;
|
|
14744
|
-
|
|
14745
|
-
this.aCtx = AudioContextProvider.audioContextInstance();
|
|
14746
|
-
if (this.aCtx) {
|
|
14747
|
-
this.ap = new AudioPlayer(this.aCtx, this);
|
|
14748
|
-
}
|
|
14749
|
-
}
|
|
14750
|
-
catch (err) {
|
|
14751
|
-
if (err instanceof Error) {
|
|
14752
|
-
this.status = err.message;
|
|
14753
|
-
}
|
|
14754
|
-
}
|
|
14874
|
+
this.ap = new AudioPlayer(this);
|
|
14755
14875
|
}
|
|
14756
14876
|
ngAfterViewInit() {
|
|
14757
|
-
if (this.
|
|
14758
|
-
this.playStartAction.onAction = () =>
|
|
14877
|
+
if (this.ap) {
|
|
14878
|
+
this.playStartAction.onAction = () => {
|
|
14879
|
+
console.debug("Start action, player: " + this.ap);
|
|
14880
|
+
this.ap?.start();
|
|
14881
|
+
};
|
|
14759
14882
|
this.playSelectionAction.onAction = () => this.ap?.startSelected();
|
|
14760
14883
|
this.playStopAction.onAction = () => this.ap?.stop();
|
|
14761
14884
|
}
|
|
@@ -14820,15 +14943,12 @@ class AudioDisplayPlayer {
|
|
|
14820
14943
|
//console.debug("Loaded");
|
|
14821
14944
|
this.status = 'Audio file loaded.';
|
|
14822
14945
|
//console.debug("Received data ", data.byteLength);
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
this.audioClip = new AudioClip(adh);
|
|
14830
|
-
});
|
|
14831
|
-
}
|
|
14946
|
+
AudioContextProvider.decodeAudioData(data).then(audioBuffer => {
|
|
14947
|
+
//console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
|
|
14948
|
+
let as = new AudioBufferSource(audioBuffer);
|
|
14949
|
+
let adh = new AudioDataHolder(as);
|
|
14950
|
+
this.audioClip = new AudioClip(adh);
|
|
14951
|
+
});
|
|
14832
14952
|
}
|
|
14833
14953
|
set audioData(audioData) {
|
|
14834
14954
|
this.audioDisplayScrollPane.audioData = audioData;
|
|
@@ -15088,14 +15208,13 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15088
15208
|
});
|
|
15089
15209
|
}
|
|
15090
15210
|
// TODO test
|
|
15091
|
-
fetchAndApplyRecordingFile(
|
|
15211
|
+
fetchAndApplyRecordingFile(recordingFile) {
|
|
15092
15212
|
let wobs = new Observable(observer => {
|
|
15093
15213
|
if (recordingFile.recordingFileId) {
|
|
15094
15214
|
let obs = this.fetchAudiofile(recordingFile.recordingFileId);
|
|
15095
15215
|
obs.subscribe({ next: resp => {
|
|
15096
|
-
// Do not use Promise version, which does not work with Safari 13
|
|
15097
15216
|
if (resp.body) {
|
|
15098
|
-
|
|
15217
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15099
15218
|
let as = new AudioBufferSource(ab);
|
|
15100
15219
|
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(as));
|
|
15101
15220
|
if (this.debugDelay > 0) {
|
|
@@ -15108,7 +15227,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15108
15227
|
observer.next(recordingFile);
|
|
15109
15228
|
observer.complete();
|
|
15110
15229
|
}
|
|
15111
|
-
});
|
|
15230
|
+
}).catch(reason => { observer.error(reason); });
|
|
15112
15231
|
}
|
|
15113
15232
|
else {
|
|
15114
15233
|
observer.error('Received no audio data!');
|
|
@@ -15148,7 +15267,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15148
15267
|
next: resp => {
|
|
15149
15268
|
// Do not use Promise version, which does not work with Safari 13
|
|
15150
15269
|
if (resp.body) {
|
|
15151
|
-
|
|
15270
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15152
15271
|
if (rf) {
|
|
15153
15272
|
let as = new AudioBufferSource(ab);
|
|
15154
15273
|
RecordingFileUtils.setAudioData(rf, new AudioDataHolder(as));
|
|
@@ -15166,7 +15285,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15166
15285
|
observer.next(rf);
|
|
15167
15286
|
observer.complete();
|
|
15168
15287
|
}
|
|
15169
|
-
});
|
|
15288
|
+
}).catch(reason => { observer.error(reason); });
|
|
15170
15289
|
}
|
|
15171
15290
|
else {
|
|
15172
15291
|
observer.error('Received no audio data');
|
|
@@ -15190,7 +15309,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15190
15309
|
});
|
|
15191
15310
|
return wobs;
|
|
15192
15311
|
}
|
|
15193
|
-
fetchSprRecordingFile(
|
|
15312
|
+
fetchSprRecordingFile(recordingFileId) {
|
|
15194
15313
|
let wobs = new Observable(observer => {
|
|
15195
15314
|
let rf = null;
|
|
15196
15315
|
let rfDescrObs = this.sprRecordingFileDescrObserver(recordingFileId);
|
|
@@ -15207,7 +15326,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15207
15326
|
// TODO use download storage type depending on sample count of file
|
|
15208
15327
|
if (rf && rf.samplerate && sampleCnt != null && sampleCnt > this._maxAutoNetMemStoreSamples) {
|
|
15209
15328
|
const baseUrl = this.recoFileUrl(recordingFileId);
|
|
15210
|
-
const obNetAb = this.chunkAudioRequestToNetAudioBuffer(
|
|
15329
|
+
const obNetAb = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, rf?.samplerate, BasicRecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS, rf.frames);
|
|
15211
15330
|
obNetAb.subscribe({
|
|
15212
15331
|
next: (nab) => {
|
|
15213
15332
|
let adh = new AudioDataHolder(nab);
|
|
@@ -15238,7 +15357,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15238
15357
|
rfAudioObs.subscribe({ next: resp => {
|
|
15239
15358
|
// Do not use Promise version, which does not work with Safari 13
|
|
15240
15359
|
if (resp.body) {
|
|
15241
|
-
|
|
15360
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15242
15361
|
if (rf) {
|
|
15243
15362
|
let as = new AudioBufferSource(ab);
|
|
15244
15363
|
let adh = new AudioDataHolder(as);
|
|
@@ -15257,7 +15376,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15257
15376
|
observer.next(rf);
|
|
15258
15377
|
observer.complete();
|
|
15259
15378
|
}
|
|
15260
|
-
});
|
|
15379
|
+
}).catch(reason => { observer.error(reason); });
|
|
15261
15380
|
}
|
|
15262
15381
|
else {
|
|
15263
15382
|
observer.error('Received no audio data');
|
|
@@ -15415,9 +15534,9 @@ class RecordingFileMetaComponent {
|
|
|
15415
15534
|
constructor() {
|
|
15416
15535
|
this.sessionId = null;
|
|
15417
15536
|
this._recordingFile = null;
|
|
15418
|
-
this.stateLoading = false;
|
|
15419
15537
|
this.itemCode = null;
|
|
15420
15538
|
this.uuid = null;
|
|
15539
|
+
this.stateLoading = false;
|
|
15421
15540
|
}
|
|
15422
15541
|
get recordingFile() {
|
|
15423
15542
|
return this._recordingFile;
|
|
@@ -15525,7 +15644,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
15525
15644
|
</mat-card-content>
|
|
15526
15645
|
</mat-card>
|
|
15527
15646
|
` }]
|
|
15528
|
-
}], propDecorators: { sessionId: [{
|
|
15647
|
+
}], ctorParameters: function () { return []; }, propDecorators: { sessionId: [{
|
|
15529
15648
|
type: Input
|
|
15530
15649
|
}], stateLoading: [{
|
|
15531
15650
|
type: Input
|
|
@@ -15546,7 +15665,6 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15546
15665
|
this.ref = ref;
|
|
15547
15666
|
this.eRef = eRef;
|
|
15548
15667
|
this.dialog = dialog;
|
|
15549
|
-
//protected _recordingFileId: string | number = null;
|
|
15550
15668
|
this.sessionId = null;
|
|
15551
15669
|
this.sessionIdFromRoute = null;
|
|
15552
15670
|
this.availRecFiles = null;
|
|
@@ -15555,9 +15673,11 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15555
15673
|
this.recordingFileVersion = null;
|
|
15556
15674
|
this.routedByQueryParam = false;
|
|
15557
15675
|
this.posInList = null;
|
|
15558
|
-
this.audioFetching = false;
|
|
15559
|
-
this.naviInfoLoading = false;
|
|
15560
15676
|
this.parentE = this.eRef.nativeElement;
|
|
15677
|
+
// TODO Should be initialized with false, but this causes in debug mode:
|
|
15678
|
+
// ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'. Find more at https://angular.io/errors/NG0100
|
|
15679
|
+
this.audioFetching = true;
|
|
15680
|
+
this.naviInfoLoading = false;
|
|
15561
15681
|
this.firstAction = new Action('First');
|
|
15562
15682
|
this.firstAction.onAction = () => {
|
|
15563
15683
|
this.posInList = 0;
|
|
@@ -15723,54 +15843,51 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15723
15843
|
this.recordingFile = null;
|
|
15724
15844
|
this.posInList = null;
|
|
15725
15845
|
this.updateActions();
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
this.recordingFile
|
|
15735
|
-
if (
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
if (
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
if (eeffsr != null) {
|
|
15751
|
-
sel = new Selection(ab.sampleRate, esffsr, eeffsr);
|
|
15752
|
-
}
|
|
15753
|
-
else {
|
|
15754
|
-
//let ch0 = ab.getChannelData(0);
|
|
15755
|
-
let frameLength = ab.frameLen;
|
|
15756
|
-
sel = new Selection(esr, esffsr, frameLength);
|
|
15757
|
-
}
|
|
15846
|
+
this.audioFetching = true;
|
|
15847
|
+
this.recordingFileService.fetchSprRecordingFile(rfId).subscribe({
|
|
15848
|
+
next: value => {
|
|
15849
|
+
this.audioFetching = false;
|
|
15850
|
+
this.status = 'Audio file loaded.';
|
|
15851
|
+
let clip = null;
|
|
15852
|
+
this.recordingFile = value;
|
|
15853
|
+
if (this.recordingFile) {
|
|
15854
|
+
let ab = this.recordingFile.audioDataHolder;
|
|
15855
|
+
if (ab) {
|
|
15856
|
+
clip = new AudioClip(ab);
|
|
15857
|
+
let esffsr = null;
|
|
15858
|
+
let eeffsr = null;
|
|
15859
|
+
let esr = null;
|
|
15860
|
+
if (clip.audioDataHolder != null) {
|
|
15861
|
+
esr = ab.sampleRate;
|
|
15862
|
+
if (esr != null) {
|
|
15863
|
+
esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
|
|
15864
|
+
eeffsr = RecordingFileUtil.editEndFrameForSampleRate(this.recordingFile, esr);
|
|
15865
|
+
}
|
|
15866
|
+
let sel = null;
|
|
15867
|
+
if (esffsr != null) {
|
|
15868
|
+
if (eeffsr != null) {
|
|
15869
|
+
sel = new Selection(ab.sampleRate, esffsr, eeffsr);
|
|
15758
15870
|
}
|
|
15759
|
-
else
|
|
15760
|
-
|
|
15871
|
+
else {
|
|
15872
|
+
//let ch0 = ab.getChannelData(0);
|
|
15873
|
+
let frameLength = ab.frameLen;
|
|
15874
|
+
sel = new Selection(esr, esffsr, frameLength);
|
|
15761
15875
|
}
|
|
15762
|
-
clip.selection = sel;
|
|
15763
15876
|
}
|
|
15877
|
+
else if (eeffsr != null) {
|
|
15878
|
+
sel = new Selection(esr, 0, eeffsr);
|
|
15879
|
+
}
|
|
15880
|
+
clip.selection = sel;
|
|
15764
15881
|
}
|
|
15765
15882
|
}
|
|
15766
|
-
this.audioClip = clip;
|
|
15767
|
-
this.loadedRecfile();
|
|
15768
|
-
}, error: error1 => {
|
|
15769
|
-
this.audioFetching = false;
|
|
15770
|
-
this.status = 'Error loading audio file!';
|
|
15771
15883
|
}
|
|
15772
|
-
|
|
15773
|
-
|
|
15884
|
+
this.audioClip = clip;
|
|
15885
|
+
this.loadedRecfile();
|
|
15886
|
+
}, error: error1 => {
|
|
15887
|
+
this.audioFetching = false;
|
|
15888
|
+
this.status = 'Error loading audio file!';
|
|
15889
|
+
}
|
|
15890
|
+
});
|
|
15774
15891
|
}
|
|
15775
15892
|
loadedRecfile() {
|
|
15776
15893
|
if (this.recordingFile && !this.sessionId) {
|
|
@@ -16380,38 +16497,17 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16380
16497
|
this.transportActions.nextAction.disabled = true;
|
|
16381
16498
|
this.transportActions.pauseAction.disabled = true;
|
|
16382
16499
|
this.playStartAction.disabled = true;
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
|
|
16388
|
-
|
|
16389
|
-
this.
|
|
16390
|
-
let errMsg = 'Unknown error';
|
|
16391
|
-
if (err instanceof Error) {
|
|
16392
|
-
errMsg = err.message;
|
|
16393
|
-
}
|
|
16394
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16395
|
-
this.statusAlertType = 'error';
|
|
16396
|
-
this.dialog.open(MessageDialog, {
|
|
16397
|
-
data: {
|
|
16398
|
-
type: 'error',
|
|
16399
|
-
title: 'Error',
|
|
16400
|
-
msg: errMsg,
|
|
16401
|
-
advice: 'Please use a supported browser.',
|
|
16402
|
-
}
|
|
16403
|
-
});
|
|
16404
|
-
return;
|
|
16405
|
-
}
|
|
16406
|
-
if (context) {
|
|
16407
|
-
console.info("State of audio context: " + context.state);
|
|
16500
|
+
this.ac = new AudioCapture();
|
|
16501
|
+
if (this.ac) {
|
|
16502
|
+
this.transportActions.startAction.onAction = () => this.startItem();
|
|
16503
|
+
this.ac.listener = this;
|
|
16504
|
+
this.configureStreamCaptureStream();
|
|
16505
|
+
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
16506
|
+
//this.ac.listDevices();
|
|
16408
16507
|
}
|
|
16409
16508
|
else {
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
if (!context || !navigator.mediaDevices) {
|
|
16413
|
-
this.status = 5 /* ERROR */;
|
|
16414
|
-
let errMsg = 'Browser does not support Media streams!';
|
|
16509
|
+
this.transportActions.startAction.disabled = true;
|
|
16510
|
+
let errMsg = 'Browser does not support Media/Audio API!';
|
|
16415
16511
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16416
16512
|
this.statusAlertType = 'error';
|
|
16417
16513
|
this.dialog.open(MessageDialog, {
|
|
@@ -16424,36 +16520,10 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16424
16520
|
});
|
|
16425
16521
|
return;
|
|
16426
16522
|
}
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
this.transportActions.startAction.onAction = () => this.startItem();
|
|
16432
|
-
this.ac.listener = this;
|
|
16433
|
-
this.configureStreamCaptureStream();
|
|
16434
|
-
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
16435
|
-
//this.ac.listDevices();
|
|
16436
|
-
}
|
|
16437
|
-
else {
|
|
16438
|
-
this.transportActions.startAction.disabled = true;
|
|
16439
|
-
let errMsg = 'Browser does not support Media/Audio API!';
|
|
16440
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16441
|
-
this.statusAlertType = 'error';
|
|
16442
|
-
this.dialog.open(MessageDialog, {
|
|
16443
|
-
data: {
|
|
16444
|
-
type: 'error',
|
|
16445
|
-
title: 'Error',
|
|
16446
|
-
msg: errMsg,
|
|
16447
|
-
advice: 'Please use a supported browser.',
|
|
16448
|
-
}
|
|
16449
|
-
});
|
|
16450
|
-
return;
|
|
16451
|
-
}
|
|
16452
|
-
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
16453
|
-
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
16454
|
-
//this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
16455
|
-
this.playStartAction.onAction = () => this.controlAudioPlayer?.start();
|
|
16456
|
-
}
|
|
16523
|
+
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
16524
|
+
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
16525
|
+
//this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
16526
|
+
this.playStartAction.onAction = () => this.controlAudioPlayer?.start();
|
|
16457
16527
|
this.uploader.listener = (ue) => {
|
|
16458
16528
|
this.uploadUpdate(ue);
|
|
16459
16529
|
};
|
|
@@ -16562,6 +16632,9 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16562
16632
|
chCnt = ProjectUtil.audioChannelCount(project);
|
|
16563
16633
|
console.info("Project requested recording channel count: " + chCnt);
|
|
16564
16634
|
this.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
16635
|
+
if (project.allowEchoCancellation !== undefined) {
|
|
16636
|
+
this.allowEchoCancellation = project.allowEchoCancellation;
|
|
16637
|
+
}
|
|
16565
16638
|
if (project.chunkedRecording === true) {
|
|
16566
16639
|
this.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
|
|
16567
16640
|
}
|
|
@@ -16762,7 +16835,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16762
16835
|
}
|
|
16763
16836
|
else {
|
|
16764
16837
|
//console.debug("Fetch audio and store to indexed db...");
|
|
16765
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileIndDbAudioBuffer(this.
|
|
16838
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileIndDbAudioBuffer(this._persistentAudioStorageTarget, this._session.project, rf).subscribe({
|
|
16766
16839
|
next: (iab) => {
|
|
16767
16840
|
//console.debug("Sessionmanager: Received inddb audio buffer: "+iab);
|
|
16768
16841
|
nextIab = iab;
|
|
@@ -16805,7 +16878,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16805
16878
|
// Fetch chunked audio buffer from network
|
|
16806
16879
|
let nextNetAb = null;
|
|
16807
16880
|
//console.debug("Fetch chunked audio from network");
|
|
16808
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileNetAudioBuffer(this.
|
|
16881
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileNetAudioBuffer(this._session.project, rf).subscribe({
|
|
16809
16882
|
next: (netAb) => {
|
|
16810
16883
|
//console.debug("Sessionmanager: Received net audio buffer: "+netAb);
|
|
16811
16884
|
nextNetAb = netAb;
|
|
@@ -16850,7 +16923,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16850
16923
|
// Fetch chunked array audio buffer
|
|
16851
16924
|
let nextAab = null;
|
|
16852
16925
|
//console.debug("Fetch audio and store to (chunked) array buffer...");
|
|
16853
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileArrayAudioBuffer(this.
|
|
16926
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileArrayAudioBuffer(this._session.project, rf).subscribe({
|
|
16854
16927
|
next: (aab) => {
|
|
16855
16928
|
nextAab = aab;
|
|
16856
16929
|
},
|
|
@@ -16887,7 +16960,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16887
16960
|
});
|
|
16888
16961
|
}
|
|
16889
16962
|
else {
|
|
16890
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this.
|
|
16963
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._session.project, rf).subscribe({
|
|
16891
16964
|
next: ab => {
|
|
16892
16965
|
this.liveLevelDisplayState = State.READY;
|
|
16893
16966
|
let fabDh = null;
|
|
@@ -17026,7 +17099,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17026
17099
|
this.transportActions.pauseAction.disabled = true;
|
|
17027
17100
|
this.statusAlertType = 'info';
|
|
17028
17101
|
this.statusMsg = 'Recorded.';
|
|
17029
|
-
let
|
|
17102
|
+
let ab = null;
|
|
17030
17103
|
if (this.ac) {
|
|
17031
17104
|
let adh = null;
|
|
17032
17105
|
let as = null;
|
|
@@ -17058,7 +17131,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17058
17131
|
const sr = this.ac.currentSampleRate;
|
|
17059
17132
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
17060
17133
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
17061
|
-
let netAs = new NetAudioBuffer(this.
|
|
17134
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
17062
17135
|
as = netAs;
|
|
17063
17136
|
if (this.uploadSet) {
|
|
17064
17137
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -17105,7 +17178,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17105
17178
|
const sr = this.ac.currentSampleRate;
|
|
17106
17179
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
17107
17180
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
17108
|
-
let netAs = new NetAudioBuffer(this.
|
|
17181
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
17109
17182
|
as = netAs;
|
|
17110
17183
|
if (this.uploadSet) {
|
|
17111
17184
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -17124,7 +17197,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17124
17197
|
as = this.ac.audioBufferArray();
|
|
17125
17198
|
}
|
|
17126
17199
|
else {
|
|
17127
|
-
|
|
17200
|
+
ab = this.ac.audioBuffer();
|
|
17128
17201
|
if (ab) {
|
|
17129
17202
|
as = new AudioBufferSource(ab);
|
|
17130
17203
|
}
|
|
@@ -17144,7 +17217,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17144
17217
|
RecordingFileUtils.setAudioData(rf, adh);
|
|
17145
17218
|
this.recorderCombiPane.addRecFile(rf);
|
|
17146
17219
|
// Upload if upload enabled and not in chunked upload mode
|
|
17147
|
-
if (this.enableUploadRecordings && this._uploadChunkSizeSeconds === null && rf != null &&
|
|
17220
|
+
if (this.enableUploadRecordings && this._uploadChunkSizeSeconds === null && AudioStorageType.MEM_ENTIRE === this._clientAudioStorageType && rf != null && ab != null) {
|
|
17148
17221
|
let apiEndPoint = '';
|
|
17149
17222
|
if (this.config && this.config.apiEndPoint) {
|
|
17150
17223
|
apiEndPoint = this.config.apiEndPoint;
|
|
@@ -17159,7 +17232,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17159
17232
|
// TODO duplicate conversion for manual download
|
|
17160
17233
|
this.processingRecording = true;
|
|
17161
17234
|
let ww = new WavWriter();
|
|
17162
|
-
ww.writeAsync(
|
|
17235
|
+
ww.writeAsync(ab, (wavFile) => {
|
|
17163
17236
|
this.postRecordingMultipart(wavFile, recUrl, rf);
|
|
17164
17237
|
this.processingRecording = false;
|
|
17165
17238
|
this.updateWakeLock();
|
|
@@ -17395,11 +17468,7 @@ class AudioRecorderComponent extends RecorderComponent {
|
|
|
17395
17468
|
this.uploader = uploader;
|
|
17396
17469
|
}
|
|
17397
17470
|
ngOnInit() {
|
|
17398
|
-
|
|
17399
|
-
let audioContext = AudioContextProvider.audioContextInstance();
|
|
17400
|
-
if (audioContext) {
|
|
17401
|
-
this.controlAudioPlayer = new AudioPlayer(audioContext, this.ar);
|
|
17402
|
-
}
|
|
17471
|
+
this.controlAudioPlayer = new AudioPlayer(this.ar);
|
|
17403
17472
|
this.ar.controlAudioPlayer = this.controlAudioPlayer;
|
|
17404
17473
|
//TODO Duplicate code in SpeechRecorderComponent
|
|
17405
17474
|
window.addEventListener('beforeunload', (e) => {
|
|
@@ -17567,7 +17636,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
17567
17636
|
}]
|
|
17568
17637
|
}] });
|
|
17569
17638
|
|
|
17570
|
-
const VERSION = '3.
|
|
17639
|
+
const VERSION = '3.6.0';
|
|
17571
17640
|
|
|
17572
17641
|
/*
|
|
17573
17642
|
* Public API Surface of speechrecorderng
|