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
|
@@ -1982,9 +1982,8 @@ class IndexedDbAudioBufferSourceNode extends AudioSourceNode {
|
|
|
1982
1982
|
}
|
|
1983
1983
|
|
|
1984
1984
|
class NetAudioBuffer extends BasicAudioSource {
|
|
1985
|
-
constructor(
|
|
1985
|
+
constructor(_recFileService, _baseUrl, _channelCount, _sampleRate, _chunkFrameLen, _frameLen, _uuid = null, _orgFetchChunkFrameLen = _chunkFrameLen) {
|
|
1986
1986
|
super();
|
|
1987
|
-
this._audioContext = _audioContext;
|
|
1988
1987
|
this._recFileService = _recFileService;
|
|
1989
1988
|
this._baseUrl = _baseUrl;
|
|
1990
1989
|
this._channelCount = _channelCount;
|
|
@@ -2002,9 +2001,6 @@ class NetAudioBuffer extends BasicAudioSource {
|
|
|
2002
2001
|
get recFileService() {
|
|
2003
2002
|
return this._recFileService;
|
|
2004
2003
|
}
|
|
2005
|
-
get audioContext() {
|
|
2006
|
-
return this._audioContext;
|
|
2007
|
-
}
|
|
2008
2004
|
get baseUrl() {
|
|
2009
2005
|
return this._baseUrl;
|
|
2010
2006
|
}
|
|
@@ -2042,8 +2038,8 @@ class NetAudioBuffer extends BasicAudioSource {
|
|
|
2042
2038
|
toString() {
|
|
2043
2039
|
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();
|
|
2044
2040
|
}
|
|
2045
|
-
static fromChunkAudioBuffer(
|
|
2046
|
-
let nab = new NetAudioBuffer(
|
|
2041
|
+
static fromChunkAudioBuffer(recordingsService, baseUrl, ab, frameLen, orgFetchChunkFrameLen = ab.length) {
|
|
2042
|
+
let nab = new NetAudioBuffer(recordingsService, baseUrl, ab.numberOfChannels, ab.sampleRate, ab.length, frameLen, null, orgFetchChunkFrameLen);
|
|
2047
2043
|
nab.ready();
|
|
2048
2044
|
return nab;
|
|
2049
2045
|
}
|
|
@@ -2086,7 +2082,7 @@ class NetRandomAccessAudioStream {
|
|
|
2086
2082
|
}
|
|
2087
2083
|
chunk(baseUrl, ci, cb, errCb) {
|
|
2088
2084
|
let startFrame = ci * this._netAb.orgFetchChunkFrameLen;
|
|
2089
|
-
this._netAb.recFileService.chunkAudioRequest(
|
|
2085
|
+
this._netAb.recFileService.chunkAudioRequest(baseUrl, startFrame, this._netAb.orgFetchChunkFrameLen).subscribe({
|
|
2090
2086
|
next: (chDl) => {
|
|
2091
2087
|
if (chDl) {
|
|
2092
2088
|
const ab = chDl.decodedAudioBuffer;
|
|
@@ -2493,6 +2489,87 @@ class NetAudioBufferSourceNode extends AudioSourceNode {
|
|
|
2493
2489
|
}
|
|
2494
2490
|
}
|
|
2495
2491
|
|
|
2492
|
+
class AudioContextProvider {
|
|
2493
|
+
static audioContextInstance() {
|
|
2494
|
+
var _a;
|
|
2495
|
+
if (!this._audioContext) {
|
|
2496
|
+
let debugFail = false;
|
|
2497
|
+
if (!window.AudioContext || typeof window.AudioContext !== 'function' || debugFail) {
|
|
2498
|
+
this._audioContext = null;
|
|
2499
|
+
throw new Error('Browser does not support Web Audio API!');
|
|
2500
|
+
}
|
|
2501
|
+
else {
|
|
2502
|
+
console.debug("Get new audio context...");
|
|
2503
|
+
this._audioContext = new window.AudioContext();
|
|
2504
|
+
console.debug("Created new audio context.");
|
|
2505
|
+
this._audioContext.addEventListener('statechange', () => {
|
|
2506
|
+
var _a;
|
|
2507
|
+
console.debug("Audio context state changed: " + ((_a = this._audioContext) === null || _a === void 0 ? void 0 : _a.state));
|
|
2508
|
+
});
|
|
2509
|
+
console.debug("Created new audio context with state: " + ((_a = this._audioContext) === null || _a === void 0 ? void 0 : _a.state));
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
return this._audioContext;
|
|
2513
|
+
}
|
|
2514
|
+
// public static audioContextInstanceRunning(audioContext?:AudioContext):Promise<AudioContext>{
|
|
2515
|
+
//
|
|
2516
|
+
// return new Promise<AudioContext>((resolve,reject)=>{
|
|
2517
|
+
// let aCtx=audioContext?audioContext:AudioContextProvider.audioContextInstance();
|
|
2518
|
+
// if(aCtx) {
|
|
2519
|
+
// if(aCtx.state==='closed') {
|
|
2520
|
+
// reject(new Error('Audio context already closed.'));
|
|
2521
|
+
// }else if(aCtx.state==='running') {
|
|
2522
|
+
// resolve(aCtx);
|
|
2523
|
+
// }else{
|
|
2524
|
+
// aCtx.resume().then(() => {
|
|
2525
|
+
// if(aCtx) {
|
|
2526
|
+
// resolve(aCtx);
|
|
2527
|
+
// }else{
|
|
2528
|
+
// reject(new Error('Could not get audio context'));
|
|
2529
|
+
// }
|
|
2530
|
+
// }).catch(() => {
|
|
2531
|
+
// reject(new Error('Could not resume audio context'));
|
|
2532
|
+
// })
|
|
2533
|
+
// }
|
|
2534
|
+
// }else{
|
|
2535
|
+
// reject(new Error('Could not get audio context from browser'));
|
|
2536
|
+
// }
|
|
2537
|
+
// });
|
|
2538
|
+
// }
|
|
2539
|
+
// public static decodeAudioData(data:ArrayBuffer,audioContext?:AudioContext):Promise<AudioBuffer>{
|
|
2540
|
+
// return new Promise<AudioBuffer>((resolve,reject)=>{
|
|
2541
|
+
// // decodeAudioData requires an audio context in running state
|
|
2542
|
+
// AudioContextProvider.audioContextInstanceRunning(audioContext).then(
|
|
2543
|
+
// (aCtx)=>{
|
|
2544
|
+
// // Do not use Promise version, which does not work with Safari 13
|
|
2545
|
+
// aCtx.decodeAudioData(data,decodedData => {
|
|
2546
|
+
// resolve(decodedData);
|
|
2547
|
+
// },(reason) => {
|
|
2548
|
+
// reject(reason);
|
|
2549
|
+
// });
|
|
2550
|
+
// }
|
|
2551
|
+
// ).catch((reason)=>{
|
|
2552
|
+
// reject(reason);
|
|
2553
|
+
// })
|
|
2554
|
+
// })
|
|
2555
|
+
// }
|
|
2556
|
+
static decodeAudioData(data) {
|
|
2557
|
+
return new Promise((resolve, reject) => {
|
|
2558
|
+
if (!this._offlineAudioContext) {
|
|
2559
|
+
this._offlineAudioContext = new OfflineAudioContext(1, 44100, 44100);
|
|
2560
|
+
}
|
|
2561
|
+
// Do not use Promise version, which does not work with Safari 13
|
|
2562
|
+
this._offlineAudioContext.decodeAudioData(data, decodedData => {
|
|
2563
|
+
resolve(decodedData);
|
|
2564
|
+
}, (reason) => {
|
|
2565
|
+
reject(reason);
|
|
2566
|
+
});
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
AudioContextProvider._audioContext = null;
|
|
2571
|
+
AudioContextProvider._offlineAudioContext = null;
|
|
2572
|
+
|
|
2496
2573
|
var EventType;
|
|
2497
2574
|
(function (EventType) {
|
|
2498
2575
|
EventType[EventType["CLOSED"] = 0] = "CLOSED";
|
|
@@ -2516,8 +2593,9 @@ class AudioPlayerEvent {
|
|
|
2516
2593
|
}
|
|
2517
2594
|
}
|
|
2518
2595
|
class AudioPlayer {
|
|
2519
|
-
constructor(
|
|
2596
|
+
constructor(listener) {
|
|
2520
2597
|
this.running = false;
|
|
2598
|
+
this.context = null;
|
|
2521
2599
|
this.ready = false;
|
|
2522
2600
|
this._audioClip = null;
|
|
2523
2601
|
this._audioSource = null;
|
|
@@ -2525,7 +2603,6 @@ class AudioPlayer {
|
|
|
2525
2603
|
this.sourceAudioWorkletNode = null;
|
|
2526
2604
|
this.playStartTime = null;
|
|
2527
2605
|
this.timerVar = null;
|
|
2528
|
-
this.context = context;
|
|
2529
2606
|
this.listener = listener;
|
|
2530
2607
|
this.bufSize = AudioPlayer.DEFAULT_BUFSIZE;
|
|
2531
2608
|
this.n = navigator;
|
|
@@ -2542,15 +2619,23 @@ class AudioPlayer {
|
|
|
2542
2619
|
this._stopAction = new Action('Stop');
|
|
2543
2620
|
this._stopAction.disabled = true;
|
|
2544
2621
|
this._stopAction.onAction = () => this.stop();
|
|
2545
|
-
this.context.addEventListener('statechange', (ev) => {
|
|
2546
|
-
if (this.context.state !== 'running') {
|
|
2547
|
-
this.stop();
|
|
2548
|
-
}
|
|
2549
|
-
});
|
|
2550
2622
|
}
|
|
2551
2623
|
get autoPlayOnSelectToggleAction() {
|
|
2552
2624
|
return this._autoPlayOnSelectToggleAction;
|
|
2553
2625
|
}
|
|
2626
|
+
_audioContext() {
|
|
2627
|
+
if (!this.context) {
|
|
2628
|
+
this.context = AudioContextProvider.audioContextInstance();
|
|
2629
|
+
if (this.context) {
|
|
2630
|
+
this.context.addEventListener('statechange', (ev) => {
|
|
2631
|
+
if (this.context && this.context.state !== 'running') {
|
|
2632
|
+
this.stop();
|
|
2633
|
+
}
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
return this.context;
|
|
2638
|
+
}
|
|
2554
2639
|
get startAction() {
|
|
2555
2640
|
return this._startAction;
|
|
2556
2641
|
}
|
|
@@ -2569,9 +2654,9 @@ class AudioPlayer {
|
|
|
2569
2654
|
chs = audioDataHolder.numberOfChannels;
|
|
2570
2655
|
if (chs > 0) {
|
|
2571
2656
|
length = audioDataHolder.frameLen;
|
|
2572
|
-
if (chs > this.context.destination.maxChannelCount) {
|
|
2573
|
-
|
|
2574
|
-
}
|
|
2657
|
+
//if (chs > this.context.destination.maxChannelCount) {
|
|
2658
|
+
// // TODO exception
|
|
2659
|
+
//}
|
|
2575
2660
|
}
|
|
2576
2661
|
this.audioSource = audioDataHolder.audioSource;
|
|
2577
2662
|
audioClip.addSelectionObserver((ac) => {
|
|
@@ -2591,16 +2676,11 @@ class AudioPlayer {
|
|
|
2591
2676
|
set audioSource(value) {
|
|
2592
2677
|
this.stop();
|
|
2593
2678
|
this._audioSource = value;
|
|
2594
|
-
if (this._audioSource
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
else {
|
|
2603
|
-
this._loadSourceWorkletAndInitStart();
|
|
2679
|
+
if (this._audioSource) {
|
|
2680
|
+
this.ready = true;
|
|
2681
|
+
this.updateStartActions();
|
|
2682
|
+
if (this.listener) {
|
|
2683
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2604
2684
|
}
|
|
2605
2685
|
}
|
|
2606
2686
|
else {
|
|
@@ -2612,24 +2692,27 @@ class AudioPlayer {
|
|
|
2612
2692
|
}
|
|
2613
2693
|
}
|
|
2614
2694
|
_loadSourceWorkletAndInitStart() {
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
this.listener
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
this.listener
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2695
|
+
if (this.context) {
|
|
2696
|
+
AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
|
|
2697
|
+
//console.debug("Player ready. ( by Player::_loadSourceWorkletAndInitStart()");
|
|
2698
|
+
this.ready = true;
|
|
2699
|
+
this.updateStartActions();
|
|
2700
|
+
if (this.listener) {
|
|
2701
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.READY));
|
|
2702
|
+
}
|
|
2703
|
+
}).catch((error) => {
|
|
2704
|
+
this.ready = false;
|
|
2705
|
+
this.updateStartActions();
|
|
2706
|
+
if (this.listener) {
|
|
2707
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
2708
|
+
}
|
|
2709
|
+
console.error('Could not add module ' + error);
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2630
2712
|
}
|
|
2631
2713
|
_startAudioSourceWorkletNode() {
|
|
2632
|
-
|
|
2714
|
+
this._loadSourceWorkletAndInitStart();
|
|
2715
|
+
if (this.context && this.sourceAudioWorkletNode) {
|
|
2633
2716
|
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2634
2717
|
let msg = 'Unknwon error';
|
|
2635
2718
|
if (ev instanceof ErrorEvent) {
|
|
@@ -2656,16 +2739,25 @@ class AudioPlayer {
|
|
|
2656
2739
|
}
|
|
2657
2740
|
}
|
|
2658
2741
|
start() {
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2742
|
+
this._audioContext();
|
|
2743
|
+
if (this.context) {
|
|
2744
|
+
if (!this._startAction.disabled && !this.running) {
|
|
2745
|
+
if (this.context.state === 'suspended') {
|
|
2746
|
+
this.context.resume().then(() => {
|
|
2747
|
+
this._start();
|
|
2748
|
+
}).catch((reason) => {
|
|
2749
|
+
console.error(reason.message());
|
|
2750
|
+
throw reason;
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
else if (this.context.state === 'closed') {
|
|
2754
|
+
const msg = 'Error: Cannot start playback. Audio context is already closed!';
|
|
2755
|
+
console.error(msg);
|
|
2756
|
+
throw new Error(msg);
|
|
2757
|
+
}
|
|
2758
|
+
else {
|
|
2662
2759
|
this._start();
|
|
2663
|
-
}
|
|
2664
|
-
console.error('Could not resume audio context: ' + reason);
|
|
2665
|
-
});
|
|
2666
|
-
}
|
|
2667
|
-
else {
|
|
2668
|
-
this._start();
|
|
2760
|
+
}
|
|
2669
2761
|
}
|
|
2670
2762
|
}
|
|
2671
2763
|
}
|
|
@@ -2690,9 +2782,12 @@ class AudioPlayer {
|
|
|
2690
2782
|
}
|
|
2691
2783
|
}
|
|
2692
2784
|
startSelectionDisabled() {
|
|
2693
|
-
return !(this._audioClip &&
|
|
2785
|
+
return !(this._audioClip && !this.startAction.disabled && this._audioClip.selection);
|
|
2694
2786
|
}
|
|
2695
2787
|
_start(playSelection = false) {
|
|
2788
|
+
if (!this.context) {
|
|
2789
|
+
throw new Error("Could not get audio context!");
|
|
2790
|
+
}
|
|
2696
2791
|
if (this._audioSource instanceof AudioBufferSource) {
|
|
2697
2792
|
this.sourceBufferNode = this.context.createBufferSource();
|
|
2698
2793
|
this.sourceBufferNode.buffer = this._audioSource.audioBuffer;
|
|
@@ -2723,68 +2818,90 @@ class AudioPlayer {
|
|
|
2723
2818
|
}
|
|
2724
2819
|
}
|
|
2725
2820
|
else if (this._audioSource instanceof ArrayAudioBuffer || this._audioSource instanceof IndexedDbAudioBuffer || this._audioSource instanceof NetAudioBuffer) {
|
|
2726
|
-
this.
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
const iasn = new IndexedDbAudioBufferSourceNode(this.context);
|
|
2734
|
-
iasn.inddbAudioBuffer = this._audioSource;
|
|
2735
|
-
this.sourceAudioWorkletNode = iasn;
|
|
2736
|
-
}
|
|
2737
|
-
else if (this._audioSource instanceof NetAudioBuffer) {
|
|
2738
|
-
const nabsn = new NetAudioBufferSourceNode(this.context);
|
|
2739
|
-
nabsn.netAudioBuffer = this._audioSource;
|
|
2740
|
-
this.sourceAudioWorkletNode = nabsn;
|
|
2741
|
-
}
|
|
2742
|
-
if (this.sourceAudioWorkletNode) {
|
|
2743
|
-
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2744
|
-
let msg = 'Unknwon error';
|
|
2745
|
-
if (ev instanceof ErrorEvent) {
|
|
2746
|
-
msg = ev.message;
|
|
2821
|
+
AudioSourceWorkletModuleLoader.loadModule(this.context).then(() => {
|
|
2822
|
+
this.playStartTime = null;
|
|
2823
|
+
if (this.context) {
|
|
2824
|
+
if (this._audioSource instanceof ArrayAudioBuffer) {
|
|
2825
|
+
const aabsn = new ArrayAudioBufferSourceNode(this.context);
|
|
2826
|
+
aabsn.arrayAudioBuffer = this._audioSource;
|
|
2827
|
+
this.sourceAudioWorkletNode = aabsn;
|
|
2747
2828
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2829
|
+
else if (this._audioSource instanceof IndexedDbAudioBuffer) {
|
|
2830
|
+
const iasn = new IndexedDbAudioBufferSourceNode(this.context);
|
|
2831
|
+
iasn.inddbAudioBuffer = this._audioSource;
|
|
2832
|
+
this.sourceAudioWorkletNode = iasn;
|
|
2833
|
+
}
|
|
2834
|
+
else if (this._audioSource instanceof NetAudioBuffer) {
|
|
2835
|
+
const nabsn = new NetAudioBufferSourceNode(this.context);
|
|
2836
|
+
nabsn.netAudioBuffer = this._audioSource;
|
|
2837
|
+
this.sourceAudioWorkletNode = nabsn;
|
|
2838
|
+
}
|
|
2839
|
+
if (this.sourceAudioWorkletNode) {
|
|
2840
|
+
this.sourceAudioWorkletNode.onprocessorerror = (ev) => {
|
|
2841
|
+
let msg = 'Unknwon error';
|
|
2842
|
+
if (ev instanceof ErrorEvent) {
|
|
2843
|
+
msg = ev.message;
|
|
2844
|
+
}
|
|
2845
|
+
console.error("Audio source worklet error: " + msg);
|
|
2846
|
+
if (this.listener) {
|
|
2847
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.ERROR));
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
|
|
2851
|
+
this.sourceAudioWorkletNode.onended = () => this.onended();
|
|
2852
|
+
this.running = true;
|
|
2853
|
+
const ac = this._audioClip;
|
|
2854
|
+
let offset = 0;
|
|
2855
|
+
if (playSelection && ac && ac.selection) {
|
|
2856
|
+
const s = ac.selection;
|
|
2857
|
+
const sr = ac.audioDataHolder.sampleRate;
|
|
2858
|
+
offset = s.leftFrame / sr;
|
|
2859
|
+
const stopPosInsecs = s.rightFrame / sr;
|
|
2860
|
+
const dur = stopPosInsecs - offset;
|
|
2861
|
+
this.sourceAudioWorkletNode.start(0, offset, dur);
|
|
2862
|
+
}
|
|
2863
|
+
else {
|
|
2864
|
+
this.sourceAudioWorkletNode.start();
|
|
2865
|
+
}
|
|
2866
|
+
//this.playStartTime = this.context.currentTime - offset;
|
|
2867
|
+
this._startAction.disabled = true;
|
|
2868
|
+
this._startSelectionAction.disabled = true;
|
|
2869
|
+
this._stopAction.disabled = false;
|
|
2870
|
+
if (this.listener) {
|
|
2871
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.STARTED));
|
|
2872
|
+
}
|
|
2751
2873
|
}
|
|
2752
|
-
};
|
|
2753
|
-
this.sourceAudioWorkletNode.connect(this.context.destination); // this already starts playing
|
|
2754
|
-
this.sourceAudioWorkletNode.onended = () => this.onended();
|
|
2755
|
-
this.running = true;
|
|
2756
|
-
const ac = this._audioClip;
|
|
2757
|
-
let offset = 0;
|
|
2758
|
-
if (playSelection && ac && ac.selection) {
|
|
2759
|
-
const s = ac.selection;
|
|
2760
|
-
const sr = ac.audioDataHolder.sampleRate;
|
|
2761
|
-
offset = s.leftFrame / sr;
|
|
2762
|
-
const stopPosInsecs = s.rightFrame / sr;
|
|
2763
|
-
const dur = stopPosInsecs - offset;
|
|
2764
|
-
this.sourceAudioWorkletNode.start(0, offset, dur);
|
|
2765
|
-
}
|
|
2766
|
-
else {
|
|
2767
|
-
this.sourceAudioWorkletNode.start();
|
|
2768
2874
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
this.
|
|
2772
|
-
this.
|
|
2875
|
+
}).catch((error) => {
|
|
2876
|
+
console.error(error.message);
|
|
2877
|
+
this.ready = false;
|
|
2878
|
+
this.updateStartActions();
|
|
2773
2879
|
if (this.listener) {
|
|
2774
|
-
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.
|
|
2880
|
+
this.listener.audioPlayerUpdate(new AudioPlayerEvent(EventType.CLOSED));
|
|
2775
2881
|
}
|
|
2776
|
-
|
|
2882
|
+
throw error;
|
|
2883
|
+
});
|
|
2777
2884
|
}
|
|
2778
2885
|
}
|
|
2779
2886
|
startSelected() {
|
|
2887
|
+
this._audioContext();
|
|
2888
|
+
if (!this.context) {
|
|
2889
|
+
throw new Error("Could not get audio context!");
|
|
2890
|
+
}
|
|
2780
2891
|
if (!this._startAction.disabled && !this.running) {
|
|
2781
|
-
if (this.context.state
|
|
2892
|
+
if (this.context.state === 'suspended') {
|
|
2782
2893
|
this.context.resume().then(() => {
|
|
2783
2894
|
this._start(true);
|
|
2784
2895
|
}).catch((reason) => {
|
|
2785
|
-
console.error(
|
|
2896
|
+
console.error(reason.message);
|
|
2897
|
+
throw reason;
|
|
2786
2898
|
});
|
|
2787
2899
|
}
|
|
2900
|
+
else if (this.context.state === 'closed') {
|
|
2901
|
+
const msg = 'Error: Cannot start playback of selection. Audio context is already closed!';
|
|
2902
|
+
console.error(msg);
|
|
2903
|
+
throw new Error(msg);
|
|
2904
|
+
}
|
|
2788
2905
|
else {
|
|
2789
2906
|
this._start(true);
|
|
2790
2907
|
}
|
|
@@ -2821,7 +2938,7 @@ class AudioPlayer {
|
|
|
2821
2938
|
}
|
|
2822
2939
|
get playPositionTime() {
|
|
2823
2940
|
let ppt = null;
|
|
2824
|
-
if (this.playStartTime !== null) {
|
|
2941
|
+
if (this.context && this.playStartTime !== null) {
|
|
2825
2942
|
ppt = this.context.currentTime - this.playStartTime;
|
|
2826
2943
|
}
|
|
2827
2944
|
else if (this.sourceAudioWorkletNode) {
|
|
@@ -3163,8 +3280,10 @@ const awpStr = "class AudioCaptureInterceptorProcessor extends AudioWorkletProce
|
|
|
3163
3280
|
"\n" +
|
|
3164
3281
|
"registerProcessor('capture-interceptor',AudioCaptureInterceptorProcessor);\n";
|
|
3165
3282
|
class AudioCapture {
|
|
3166
|
-
|
|
3283
|
+
//private context:AudioContext|null=null;
|
|
3284
|
+
constructor() {
|
|
3167
3285
|
this._maxAutoNetMemStoreSamples = AudioCapture.DEFAULT_MAX_NET_AUTO_MEM_STORE_SAMPLES;
|
|
3286
|
+
this.context = null;
|
|
3168
3287
|
this._recUUID = null;
|
|
3169
3288
|
this.agcStatus = null;
|
|
3170
3289
|
this.bufferingNode = null;
|
|
@@ -3179,13 +3298,7 @@ class AudioCapture {
|
|
|
3179
3298
|
this.persisted = true;
|
|
3180
3299
|
this.persistError = null;
|
|
3181
3300
|
this.inddbAudioBuffer = null;
|
|
3182
|
-
this.context = context;
|
|
3183
3301
|
this.n = navigator;
|
|
3184
|
-
this.context.addEventListener('statechange', () => {
|
|
3185
|
-
if (this.context.state !== 'running') {
|
|
3186
|
-
this.close();
|
|
3187
|
-
}
|
|
3188
|
-
});
|
|
3189
3302
|
}
|
|
3190
3303
|
get maxAutoNetMemStoreSamples() {
|
|
3191
3304
|
return this._maxAutoNetMemStoreSamples;
|
|
@@ -3214,6 +3327,19 @@ class AudioCapture {
|
|
|
3214
3327
|
get opened() {
|
|
3215
3328
|
return this._opened;
|
|
3216
3329
|
}
|
|
3330
|
+
_audioContext() {
|
|
3331
|
+
if (!this.context) {
|
|
3332
|
+
this.context = AudioContextProvider.audioContextInstance();
|
|
3333
|
+
if (this.context) {
|
|
3334
|
+
this.context.addEventListener('statechange', () => {
|
|
3335
|
+
if (this.context && this.context.state !== 'running') {
|
|
3336
|
+
this.close();
|
|
3337
|
+
}
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
return this.context;
|
|
3342
|
+
}
|
|
3217
3343
|
initData() {
|
|
3218
3344
|
if (!this._recUUID) {
|
|
3219
3345
|
this._recUUID = UUID.generate();
|
|
@@ -3328,119 +3454,137 @@ class AudioCapture {
|
|
|
3328
3454
|
}
|
|
3329
3455
|
}
|
|
3330
3456
|
addCaptureInterceptor() {
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
this.listener
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
let chunk = new Array(chs);
|
|
3353
|
-
const samples = this.framesRecorded * chs;
|
|
3354
|
-
if ((AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.audioStorageType) && this.data && samples > this._maxAutoNetMemStoreSamples) {
|
|
3355
|
-
this.data = null;
|
|
3356
|
-
}
|
|
3357
|
-
//console.debug("Data initialized: "+(this.data!=null));
|
|
3358
|
-
for (let ch = 0; ch < chs; ch++) {
|
|
3359
|
-
//console.debug("Data ch initialized: "+(this.data !=null && this.data[ch] !=null));
|
|
3360
|
-
if (ch < this.channelCount) {
|
|
3361
|
-
if (dt.data[ch]) {
|
|
3362
|
-
let fa = new Float32Array(dt.data[ch]);
|
|
3363
|
-
if (this.data && this.data[ch]) {
|
|
3364
|
-
this.data[ch].push(fa);
|
|
3365
|
-
}
|
|
3366
|
-
chunk[ch] = fa;
|
|
3367
|
-
// Use samples of channel 0 to count frames (samples)
|
|
3368
|
-
if (ch == 0) {
|
|
3369
|
-
this.framesRecorded += fa.length;
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3457
|
+
if (this.context) {
|
|
3458
|
+
const awn = new AudioWorkletNode(this.context, 'capture-interceptor');
|
|
3459
|
+
awn.onprocessorerror = (ev) => {
|
|
3460
|
+
let msg = 'Unknwon error';
|
|
3461
|
+
if (ev instanceof ErrorEvent) {
|
|
3462
|
+
msg = ev.message;
|
|
3463
|
+
}
|
|
3464
|
+
console.error("Capture audio worklet error: " + msg);
|
|
3465
|
+
if (this.listener) {
|
|
3466
|
+
this.listener.error(msg);
|
|
3467
|
+
}
|
|
3468
|
+
};
|
|
3469
|
+
let awnPt = awn.port;
|
|
3470
|
+
if (awnPt) {
|
|
3471
|
+
awnPt.onmessage = (ev) => {
|
|
3472
|
+
if (this.capturing) {
|
|
3473
|
+
let dt = ev.data;
|
|
3474
|
+
let chs = dt.chs;
|
|
3475
|
+
let adaLen = dt.data.length;
|
|
3476
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
3477
|
+
console.debug('Received data from worklet: ' + chs + ' ' + dt.len + ' Data chs: ' + adaLen);
|
|
3372
3478
|
}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
this.
|
|
3377
|
-
// // Random test error:
|
|
3378
|
-
// if(Math.random()>0.98) {
|
|
3379
|
-
// throw new Error('Test');
|
|
3380
|
-
// }
|
|
3479
|
+
let chunk = new Array(chs);
|
|
3480
|
+
const samples = this.framesRecorded * chs;
|
|
3481
|
+
if ((AudioStorageType.MEM_ENTIRE_AUTO_NET_CHUNKED === this.audioStorageType || AudioStorageType.MEM_CHUNKED_AUTO_NET_CHUNKED === this.audioStorageType) && this.data && samples > this._maxAutoNetMemStoreSamples) {
|
|
3482
|
+
this.data = null;
|
|
3381
3483
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3484
|
+
//console.debug("Data initialized: "+(this.data!=null));
|
|
3485
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
3486
|
+
//console.debug("Data ch initialized: "+(this.data !=null && this.data[ch] !=null));
|
|
3487
|
+
if (ch < this.channelCount) {
|
|
3488
|
+
if (dt.data[ch]) {
|
|
3489
|
+
let fa = new Float32Array(dt.data[ch]);
|
|
3490
|
+
if (this.data && this.data[ch]) {
|
|
3491
|
+
this.data[ch].push(fa);
|
|
3492
|
+
}
|
|
3493
|
+
chunk[ch] = fa;
|
|
3494
|
+
// Use samples of channel 0 to count frames (samples)
|
|
3495
|
+
if (ch == 0) {
|
|
3496
|
+
this.framesRecorded += fa.length;
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3388
3499
|
}
|
|
3389
|
-
|
|
3500
|
+
}
|
|
3501
|
+
if (this.audioOutStream) {
|
|
3390
3502
|
try {
|
|
3391
|
-
this.
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3503
|
+
this.audioOutStream.write(chunk);
|
|
3504
|
+
// // Random test error:
|
|
3505
|
+
// if(Math.random()>0.98) {
|
|
3506
|
+
// throw new Error('Test');
|
|
3507
|
+
// }
|
|
3395
3508
|
}
|
|
3396
|
-
|
|
3397
|
-
if (
|
|
3398
|
-
|
|
3399
|
-
if (err instanceof DOMException) {
|
|
3400
|
-
errExpl = ': ' + err.name + ': ' + err.message;
|
|
3401
|
-
}
|
|
3402
|
-
this.listener.error("Could not handle recorded audio data" + errExpl, "Please try to record again.");
|
|
3509
|
+
catch (err) {
|
|
3510
|
+
if (err instanceof Error) {
|
|
3511
|
+
this.persistError = err;
|
|
3403
3512
|
}
|
|
3404
3513
|
else {
|
|
3405
|
-
this.
|
|
3514
|
+
this.persistError = new Error('Error handling recorded audio data');
|
|
3515
|
+
}
|
|
3516
|
+
console.error("Capture error: " + err);
|
|
3517
|
+
try {
|
|
3518
|
+
this.stop();
|
|
3519
|
+
}
|
|
3520
|
+
catch (err2) {
|
|
3521
|
+
console.error("Capture next error (ignored): " + err2);
|
|
3522
|
+
}
|
|
3523
|
+
finally {
|
|
3524
|
+
if (this.listener) {
|
|
3525
|
+
let errExpl = '';
|
|
3526
|
+
if (err instanceof DOMException) {
|
|
3527
|
+
errExpl = ': ' + err.name + ': ' + err.message;
|
|
3528
|
+
}
|
|
3529
|
+
this.listener.error("Could not handle recorded audio data" + errExpl, "Please try to record again.");
|
|
3530
|
+
}
|
|
3531
|
+
else {
|
|
3532
|
+
this.close();
|
|
3533
|
+
}
|
|
3406
3534
|
}
|
|
3407
3535
|
}
|
|
3408
3536
|
}
|
|
3537
|
+
if (AudioStorageType.DB_CHUNKED === this._audioStorageType && this._persistentAudioStorageTarget) {
|
|
3538
|
+
this.store();
|
|
3539
|
+
}
|
|
3409
3540
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
this._opened = true;
|
|
3424
|
-
if (this.listener) {
|
|
3425
|
-
this.listener.opened();
|
|
3541
|
+
};
|
|
3542
|
+
}
|
|
3543
|
+
// Tried to fix that Safari does not record the second channel
|
|
3544
|
+
// Does not help
|
|
3545
|
+
//awn.channelCount=this.channelCount;
|
|
3546
|
+
//awn.channelCountMode='explicit';
|
|
3547
|
+
//console.debug('Channel count explicitly set to '+this.channelCount);
|
|
3548
|
+
this.bufferingNode = awn;
|
|
3549
|
+
//this.bufferingNode.channelCount=this.channelCount;
|
|
3550
|
+
this._opened = true;
|
|
3551
|
+
if (this.listener) {
|
|
3552
|
+
this.listener.opened();
|
|
3553
|
+
}
|
|
3426
3554
|
}
|
|
3427
3555
|
}
|
|
3428
|
-
open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
3556
|
+
open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
|
|
3429
3557
|
//console.debug("Capture open: ctx state: "+this.context.state);
|
|
3430
|
-
|
|
3558
|
+
this.context = this._audioContext();
|
|
3559
|
+
if (!this.context) {
|
|
3560
|
+
throw new Error("Could not get audio context!");
|
|
3561
|
+
}
|
|
3562
|
+
if (this.context.state === 'suspended') {
|
|
3431
3563
|
//console.debug("Capture open: Resume context");
|
|
3432
3564
|
this.context.resume().then(() => {
|
|
3433
3565
|
//console.debug("Capture open (ctx resumed): ctx state: "+this.context.state);
|
|
3434
|
-
this._open(channelCount, selDeviceId, autoGainControlConfigs);
|
|
3566
|
+
this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
|
|
3567
|
+
}).catch((err) => {
|
|
3568
|
+
console.error(err.message);
|
|
3569
|
+
throw err;
|
|
3435
3570
|
});
|
|
3436
3571
|
}
|
|
3572
|
+
else if (this.context.state === 'closed') {
|
|
3573
|
+
const msg = 'Error on start capture: The audio context is already closed.';
|
|
3574
|
+
console.error(msg);
|
|
3575
|
+
throw new Error(msg);
|
|
3576
|
+
}
|
|
3437
3577
|
else {
|
|
3438
|
-
this._open(channelCount, selDeviceId, autoGainControlConfigs);
|
|
3578
|
+
this._open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation);
|
|
3439
3579
|
}
|
|
3440
3580
|
}
|
|
3441
|
-
_open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
3581
|
+
_open(channelCount, selDeviceId, autoGainControlConfigs, allowEchoCancellation) {
|
|
3442
3582
|
this.channelCount = channelCount;
|
|
3443
3583
|
this.framesRecorded = 0;
|
|
3584
|
+
this.context = this._audioContext();
|
|
3585
|
+
if (!this.context) {
|
|
3586
|
+
throw new Error("Could not get audio context!");
|
|
3587
|
+
}
|
|
3444
3588
|
//var msc = new AudioStreamConstr();
|
|
3445
3589
|
// var msc={};
|
|
3446
3590
|
//msc.video = false;
|
|
@@ -3548,7 +3692,7 @@ class AudioCapture {
|
|
|
3548
3692
|
audio: {
|
|
3549
3693
|
deviceId: selDeviceId,
|
|
3550
3694
|
channelCount: channelCount,
|
|
3551
|
-
|
|
3695
|
+
echoCancellation: allowEchoCancellation ? undefined : false
|
|
3552
3696
|
},
|
|
3553
3697
|
video: false,
|
|
3554
3698
|
};
|
|
@@ -3559,106 +3703,109 @@ class AudioCapture {
|
|
|
3559
3703
|
console.debug("Audio capture, AGC: " + this.agcStatus);
|
|
3560
3704
|
let ump = navigator.mediaDevices.getUserMedia(msc);
|
|
3561
3705
|
ump.then((s) => {
|
|
3562
|
-
this.
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
let
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
let
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
//
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
//
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
else {
|
|
3609
|
-
// Register capture interceptor module
|
|
3610
|
-
let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
|
|
3611
|
-
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
3612
|
-
this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
3613
|
-
AudioCapture.captureInterceptorModuleRegistered = true;
|
|
3706
|
+
if (this.context) {
|
|
3707
|
+
this.stream = s;
|
|
3708
|
+
let aTracks = s.getAudioTracks();
|
|
3709
|
+
for (let i = 0; i < aTracks.length; i++) {
|
|
3710
|
+
let aTrack = aTracks[i];
|
|
3711
|
+
console.info("Track audio info: id: " + aTrack.id + " kind: " + aTrack.kind + " label: \"" + aTrack.label + "\"");
|
|
3712
|
+
let mtrSts = aTrack.getSettings();
|
|
3713
|
+
// Typescript lib.dom.ts MediaTrackSettings.channelCount is missing
|
|
3714
|
+
// https://github.com/mdn/browser-compat-data/blob/5493d8f937e05b2ddbd41b99f5bdfad4a1f2ed85/api/MediaTrackSettings.json
|
|
3715
|
+
//@ts-ignore
|
|
3716
|
+
console.info("Track audio settings: Ch cnt: " + mtrSts.channelCount + ", AGC: " + mtrSts.autoGainControl + ", Echo cancell.: " + mtrSts.echoCancellation);
|
|
3717
|
+
if (mtrSts.autoGainControl) {
|
|
3718
|
+
this.agcStatus = mtrSts.autoGainControl;
|
|
3719
|
+
}
|
|
3720
|
+
console.debug("Echo cancellation: " + mtrSts.echoCancellation);
|
|
3721
|
+
}
|
|
3722
|
+
let vTracks = s.getVideoTracks();
|
|
3723
|
+
for (let i = 0; i < vTracks.length; i++) {
|
|
3724
|
+
let vTrack = vTracks[i];
|
|
3725
|
+
console.info("Track video info: id: " + vTrack.id + " kind: " + vTrack.kind + " label: " + vTrack.label);
|
|
3726
|
+
}
|
|
3727
|
+
this.mediaStream = this.context.createMediaStreamSource(s);
|
|
3728
|
+
// stream channel count ( is always 2 !)
|
|
3729
|
+
let streamChannelCount = this.mediaStream.channelCount;
|
|
3730
|
+
console.info("Stream channel count: " + streamChannelCount);
|
|
3731
|
+
// is not set!!
|
|
3732
|
+
//this.currentSampleRate = this.mediaStream.sampleRate;
|
|
3733
|
+
this.currentSampleRate = this.context.sampleRate;
|
|
3734
|
+
console.info("Source audio node: channels: " + streamChannelCount + " samplerate: " + this.currentSampleRate);
|
|
3735
|
+
if (this.audioOutStream) {
|
|
3736
|
+
this.audioOutStream.setFormat(this.channelCount, this.currentSampleRate);
|
|
3737
|
+
}
|
|
3738
|
+
// W3C -> new name is createScriptProcessor
|
|
3739
|
+
//
|
|
3740
|
+
// Again deprecated, but AudioWorker not yet implemented in stable releases (June 2016)
|
|
3741
|
+
// AudioWorker is now AudioWorkletProcessor ... (May 2017)
|
|
3742
|
+
// Update 12-2020:
|
|
3743
|
+
// The ScriptProcessorNode Interface - DEPRECATED
|
|
3744
|
+
// Update 06-2021
|
|
3745
|
+
// AudioWorkletProcessor is here to stay. Web Audio API has now Recommendation status !
|
|
3746
|
+
if (this.context.audioWorklet) {
|
|
3747
|
+
//const workletFileName = ('file-loader!./interceptor_worklet.js');
|
|
3748
|
+
//const workletFileName = 'http://localhost:4200/assets/interceptor_worklet.js';
|
|
3749
|
+
//console.log(awpStr);
|
|
3750
|
+
if (AudioCapture.captureInterceptorModuleRegistered) {
|
|
3751
|
+
// Required capture interceptor module already registered
|
|
3614
3752
|
this.addCaptureInterceptor();
|
|
3615
|
-
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3753
|
+
}
|
|
3754
|
+
else {
|
|
3755
|
+
// Register capture interceptor module
|
|
3756
|
+
let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
|
|
3757
|
+
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
3758
|
+
this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
3759
|
+
AudioCapture.captureInterceptorModuleRegistered = true;
|
|
3760
|
+
this.addCaptureInterceptor();
|
|
3761
|
+
}).catch((error) => {
|
|
3762
|
+
console.log('Could not add module ' + error);
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3618
3765
|
}
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3766
|
+
else if (this.context.createScriptProcessor) {
|
|
3767
|
+
// The ScriptProcessorNode Interface - DEPRECATED Only as fallback
|
|
3768
|
+
// TODO should we use streamChannelCount or channelCount here ?
|
|
3769
|
+
let scriptProcessorNode = this.context.createScriptProcessor(AudioCapture.BUFFER_SIZE, streamChannelCount, streamChannelCount);
|
|
3770
|
+
this.bufferingNode = scriptProcessorNode;
|
|
3771
|
+
let c = 0;
|
|
3772
|
+
if (scriptProcessorNode.onaudioprocess) {
|
|
3773
|
+
scriptProcessorNode.onaudioprocess = (e) => {
|
|
3774
|
+
if (this.capturing) {
|
|
3775
|
+
let inBuffer = e.inputBuffer;
|
|
3776
|
+
// only process requested count of channels
|
|
3777
|
+
let currentBuffers = new Array(channelCount);
|
|
3778
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
3779
|
+
let chSamples = inBuffer.getChannelData(ch);
|
|
3780
|
+
let chSamplesCopy = chSamples.slice(0);
|
|
3781
|
+
currentBuffers[ch] = chSamplesCopy.slice(0);
|
|
3782
|
+
if (this.data) {
|
|
3783
|
+
this.data[ch].push(chSamplesCopy);
|
|
3784
|
+
}
|
|
3785
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
3786
|
+
console.debug("Process " + chSamplesCopy.length + " samples.");
|
|
3787
|
+
}
|
|
3788
|
+
this.framesRecorded += chSamplesCopy.length;
|
|
3638
3789
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
3790
|
+
c++;
|
|
3791
|
+
if (this.audioOutStream) {
|
|
3792
|
+
this.audioOutStream.write(currentBuffers);
|
|
3641
3793
|
}
|
|
3642
|
-
this.framesRecorded += chSamplesCopy.length;
|
|
3643
|
-
}
|
|
3644
|
-
c++;
|
|
3645
|
-
if (this.audioOutStream) {
|
|
3646
|
-
this.audioOutStream.write(currentBuffers);
|
|
3647
3794
|
}
|
|
3795
|
+
};
|
|
3796
|
+
this._opened = true;
|
|
3797
|
+
if (this.listener) {
|
|
3798
|
+
this.listener.opened();
|
|
3648
3799
|
}
|
|
3649
|
-
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
this.listener.opened();
|
|
3800
|
+
}
|
|
3801
|
+
else {
|
|
3802
|
+
this.listener.error('Browser does not support audio processing (ScriptProcessor.onaudioprocess method not found)!');
|
|
3653
3803
|
}
|
|
3654
3804
|
}
|
|
3655
3805
|
else {
|
|
3656
|
-
this.listener.error('Browser does not support audio processing (
|
|
3806
|
+
this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
|
|
3657
3807
|
}
|
|
3658
3808
|
}
|
|
3659
|
-
else {
|
|
3660
|
-
this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
|
|
3661
|
-
}
|
|
3662
3809
|
}, (e) => {
|
|
3663
3810
|
console.error(e + " Error name: " + e.name);
|
|
3664
3811
|
if (this.listener) {
|
|
@@ -3679,36 +3826,42 @@ class AudioCapture {
|
|
|
3679
3826
|
});
|
|
3680
3827
|
}
|
|
3681
3828
|
_start() {
|
|
3682
|
-
this.
|
|
3683
|
-
|
|
3684
|
-
this.audioOutStream
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
this.listener
|
|
3829
|
+
if (this.context) {
|
|
3830
|
+
this.initData();
|
|
3831
|
+
if (this.audioOutStream) {
|
|
3832
|
+
this.audioOutStream.nextStream();
|
|
3833
|
+
}
|
|
3834
|
+
this.capturing = true;
|
|
3835
|
+
if (this.bufferingNode) {
|
|
3836
|
+
this.mediaStream.connect(this.bufferingNode);
|
|
3837
|
+
this.bufferingNode.connect(this.context.destination);
|
|
3838
|
+
}
|
|
3839
|
+
if (this.listener) {
|
|
3840
|
+
this.listener.started();
|
|
3841
|
+
}
|
|
3693
3842
|
}
|
|
3694
3843
|
}
|
|
3695
3844
|
start() {
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
}
|
|
3700
|
-
else {
|
|
3701
|
-
console.debug("Capture start: audio context not running, state: " + aSt + ", resuming...");
|
|
3702
|
-
this.context.resume().then(() => {
|
|
3703
|
-
console.debug("Capture start: audio context resumed, starting...");
|
|
3845
|
+
if (this.context) {
|
|
3846
|
+
const aSt = this.context.state;
|
|
3847
|
+
if (aSt === 'running') {
|
|
3704
3848
|
this._start();
|
|
3705
|
-
}
|
|
3849
|
+
}
|
|
3850
|
+
else {
|
|
3851
|
+
console.debug("Capture start: audio context not running, state: " + aSt + ", resuming...");
|
|
3852
|
+
this.context.resume().then(() => {
|
|
3853
|
+
console.debug("Capture start: audio context resumed, starting...");
|
|
3854
|
+
this._start();
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3706
3857
|
}
|
|
3707
3858
|
}
|
|
3708
3859
|
stop() {
|
|
3709
3860
|
if (this.disconnectStreams && this.bufferingNode) {
|
|
3710
3861
|
this.mediaStream.disconnect(this.bufferingNode);
|
|
3711
|
-
|
|
3862
|
+
if (this.context) {
|
|
3863
|
+
this.bufferingNode.disconnect(this.context.destination);
|
|
3864
|
+
}
|
|
3712
3865
|
}
|
|
3713
3866
|
try {
|
|
3714
3867
|
if (this.audioOutStream) {
|
|
@@ -3831,7 +3984,7 @@ class AudioCapture {
|
|
|
3831
3984
|
}
|
|
3832
3985
|
audioBuffer() {
|
|
3833
3986
|
let ab = null;
|
|
3834
|
-
if (this.data) {
|
|
3987
|
+
if (this.context && this.data) {
|
|
3835
3988
|
let frameLen = 0;
|
|
3836
3989
|
let ch0Data = this.data[0];
|
|
3837
3990
|
for (let ch0Chk of ch0Data) {
|
|
@@ -9027,7 +9180,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9027
9180
|
withCredentials: this.withCredentials
|
|
9028
9181
|
});
|
|
9029
9182
|
}
|
|
9030
|
-
chunkAudioRequest(
|
|
9183
|
+
chunkAudioRequest(baseAudioUrl, startFrame = 0, frameLength) {
|
|
9031
9184
|
let ausps = new URLSearchParams();
|
|
9032
9185
|
ausps.set('startFrame', startFrame.toString());
|
|
9033
9186
|
ausps.set('frameLength', frameLength.toString());
|
|
@@ -9059,12 +9212,12 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9059
9212
|
// console.error("Could not read WAVE format of original download chunk!");
|
|
9060
9213
|
// }
|
|
9061
9214
|
if (pcmFmt && orgFl) {
|
|
9062
|
-
|
|
9215
|
+
AudioContextProvider.decodeAudioData(resp.body).then((ab) => {
|
|
9063
9216
|
//console.debug("Decoded audio chunk frames: "+ab.length);
|
|
9064
9217
|
let chDl = new ChunkDownload(pcmFmt, orgFl, ab);
|
|
9065
9218
|
observer.next(chDl);
|
|
9066
9219
|
observer.complete();
|
|
9067
|
-
}
|
|
9220
|
+
}).catch(error => {
|
|
9068
9221
|
//if(error instanceof HttpErrorResponse) {
|
|
9069
9222
|
// if (error.status == 404) {
|
|
9070
9223
|
// // Interpret not as an error, the file ist not recorded yet
|
|
@@ -9100,7 +9253,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9100
9253
|
});
|
|
9101
9254
|
return obs;
|
|
9102
9255
|
}
|
|
9103
|
-
chunkAudioRequestToNetAudioBuffer(
|
|
9256
|
+
chunkAudioRequestToNetAudioBuffer(baseAudioUrl, startFrame = 0, orgSampleRate, seconds, frames) {
|
|
9104
9257
|
//let audioUrl=baseAudioUrl+'?startFrame='+startFrame+'&frameLength='+frameLength;
|
|
9105
9258
|
//let audioUrl=new URL(baseAudioUrl);
|
|
9106
9259
|
// if(orgSampleRate!=null && frameLength%orgSampleRate>0){
|
|
@@ -9124,7 +9277,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9124
9277
|
if (resp.body) {
|
|
9125
9278
|
//console.debug("chunkAudioRequestTonetAb: subscriber.closed: "+subscriber.closed);
|
|
9126
9279
|
//console.debug("chunkAudioRequestTonetAb: Audio file bytes: "+resp.body.byteLength);
|
|
9127
|
-
|
|
9280
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9128
9281
|
//console.debug("chunkAudioRequestTonetAb: Decoded audio chunk frames for netAb: "+ab.length);
|
|
9129
9282
|
//console.debug("chunkAudioRequestTonetAb: Create netAb ab from chunk ab...");
|
|
9130
9283
|
if (frames === null) {
|
|
@@ -9138,7 +9291,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9138
9291
|
//console.debug("Platform sr: "+ab.sampleRate+", file sr: "+orgSampleRate+", decoded/org frame length: "+fl+"/"+frames+", ab.length: "+ab.length);
|
|
9139
9292
|
}
|
|
9140
9293
|
}
|
|
9141
|
-
let nab = NetAudioBuffer.fromChunkAudioBuffer(
|
|
9294
|
+
let nab = NetAudioBuffer.fromChunkAudioBuffer(this, baseAudioUrl, ab, fl, frameLength);
|
|
9142
9295
|
//let rp=new ReadyProvider();
|
|
9143
9296
|
//nab.readyProvider=rp;
|
|
9144
9297
|
//rp.ready();
|
|
@@ -9150,7 +9303,7 @@ let BasicRecordingService = class BasicRecordingService {
|
|
|
9150
9303
|
subscriber.next(nab);
|
|
9151
9304
|
subscriber.complete();
|
|
9152
9305
|
}
|
|
9153
|
-
}
|
|
9306
|
+
}).catch(error => {
|
|
9154
9307
|
console.error('chunkAudioRequestToNetAb: error: ' + error);
|
|
9155
9308
|
//if(error instanceof HttpErrorResponse) {
|
|
9156
9309
|
subscriber.error(error);
|
|
@@ -9258,7 +9411,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9258
9411
|
withCredentials: this.withCredentials
|
|
9259
9412
|
});
|
|
9260
9413
|
}
|
|
9261
|
-
chunkAudioRequestToIndDb(
|
|
9414
|
+
chunkAudioRequestToIndDb(persistentAudioStorageTarget, inddbAudioBuffer, baseAudioUrl, startFrame = 0, orgSampleRate, seconds) {
|
|
9262
9415
|
//let audioUrl=baseAudioUrl+'?startFrame='+startFrame+'&frameLength='+frameLength;
|
|
9263
9416
|
//let audioUrl=new URL(baseAudioUrl);
|
|
9264
9417
|
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.
|
|
@@ -9277,7 +9430,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9277
9430
|
if (resp.body) {
|
|
9278
9431
|
//console.debug("chunkAudioRequestToIndDb: subscriber.closed: "+subscriber.closed);
|
|
9279
9432
|
//console.debug("chunkAudioRequestToIndDb: Audio file bytes: "+resp.body.byteLength);
|
|
9280
|
-
|
|
9433
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9281
9434
|
//console.debug("chunkAudioRequestToIndDb: Decoded audio chunk frames for inddb: "+ab.length);
|
|
9282
9435
|
if (!inddbAudioBuffer) {
|
|
9283
9436
|
//console.debug("chunkAudioRequestToIndDb: Create inddb ab from chunk ab...");
|
|
@@ -9321,7 +9474,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9321
9474
|
}
|
|
9322
9475
|
});
|
|
9323
9476
|
}
|
|
9324
|
-
}
|
|
9477
|
+
}).catch(error => {
|
|
9325
9478
|
console.error('chunkAudioRequestToIndDb: error: ' + error);
|
|
9326
9479
|
//if(error instanceof HttpErrorResponse) {
|
|
9327
9480
|
subscriber.error(error);
|
|
@@ -9329,8 +9482,9 @@ class RecordingService extends BasicRecordingService {
|
|
|
9329
9482
|
});
|
|
9330
9483
|
}
|
|
9331
9484
|
else {
|
|
9332
|
-
|
|
9333
|
-
|
|
9485
|
+
const errMsg = 'chunkAudioRequestToIndDb: Fetching audio file: response has no body';
|
|
9486
|
+
console.error(errMsg);
|
|
9487
|
+
subscriber.error(new Error(errMsg));
|
|
9334
9488
|
}
|
|
9335
9489
|
},
|
|
9336
9490
|
error: (error) => {
|
|
@@ -9342,13 +9496,13 @@ class RecordingService extends BasicRecordingService {
|
|
|
9342
9496
|
});
|
|
9343
9497
|
return obs;
|
|
9344
9498
|
}
|
|
9345
|
-
chunkedAudioRequestToArrayBuffer(
|
|
9499
|
+
chunkedAudioRequestToArrayBuffer(baseAudioUrl, orgSampleRate, seconds) {
|
|
9346
9500
|
let obs = new Observable(subscriber => {
|
|
9347
9501
|
let arrayAudioBuffer = null;
|
|
9348
9502
|
let startFrame = 0;
|
|
9349
9503
|
let frameLength = orgSampleRate * Math.round(seconds); // Important: multiple of original sample rate to prevent numeric rounding errors on resampling.
|
|
9350
9504
|
//console.debug("Chunk audio request startFrame 0");
|
|
9351
|
-
let subscr = this.chunkAudioRequest(
|
|
9505
|
+
let subscr = this.chunkAudioRequest(baseAudioUrl, startFrame, frameLength).pipe(expand(value => {
|
|
9352
9506
|
if (subscriber.closed) {
|
|
9353
9507
|
subscr.unsubscribe();
|
|
9354
9508
|
}
|
|
@@ -9367,7 +9521,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9367
9521
|
startFrame += frameLength;
|
|
9368
9522
|
//console.debug("Next start frame: "+startFrame);
|
|
9369
9523
|
//console.debug("chunkedAudioRequest: expand() subscriber.closed: "+subscriber.closed);
|
|
9370
|
-
return this.chunkAudioRequest(
|
|
9524
|
+
return this.chunkAudioRequest(baseAudioUrl, startFrame, frameLength);
|
|
9371
9525
|
}
|
|
9372
9526
|
}
|
|
9373
9527
|
else {
|
|
@@ -9437,14 +9591,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9437
9591
|
});
|
|
9438
9592
|
return obs;
|
|
9439
9593
|
}
|
|
9440
|
-
chunkedInddbAudioRequest(
|
|
9594
|
+
chunkedInddbAudioRequest(persistentAudioStorageTarget, baseAudioUrl, orgSampleRate, seconds) {
|
|
9441
9595
|
let obs = new Observable(subscriber => {
|
|
9442
9596
|
let inddbAudioBuffer = null;
|
|
9443
9597
|
let startFrame = 0;
|
|
9444
9598
|
//let frameLength = DEFAULT_CHUNKED_DOWNLOAD_FRAMELENGTH;
|
|
9445
9599
|
let frameLength = orgSampleRate * Math.round(seconds);
|
|
9446
9600
|
//console.debug("chunkedInddbAudioRequest: Chunk audio request for inddb. startFrame: "+startFrame);
|
|
9447
|
-
let subscr = this.chunkAudioRequestToIndDb(
|
|
9601
|
+
let subscr = this.chunkAudioRequestToIndDb(persistentAudioStorageTarget, null, baseAudioUrl, startFrame, orgSampleRate, seconds).pipe(expand(iab => {
|
|
9448
9602
|
// console.debug("chunkedInddbAudioRequest (pipe/expand): Got inddb ab: "+iab);
|
|
9449
9603
|
if (subscriber.closed) {
|
|
9450
9604
|
subscr.unsubscribe();
|
|
@@ -9465,7 +9619,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9465
9619
|
startFrame += frameLength;
|
|
9466
9620
|
//console.debug("Next start frame: "+startFrame);
|
|
9467
9621
|
//console.debug("chunkedInddbAudioRequest: expand() subscriber.closed: "+subscriber.closed);
|
|
9468
|
-
return this.chunkAudioRequestToIndDb(
|
|
9622
|
+
return this.chunkAudioRequestToIndDb(persistentAudioStorageTarget, inddbAudioBuffer, baseAudioUrl, startFrame, orgSampleRate, seconds);
|
|
9469
9623
|
}
|
|
9470
9624
|
// } else {
|
|
9471
9625
|
// return EMPTY;
|
|
@@ -9657,7 +9811,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9657
9811
|
// //let recUrl=new URL(recUrlStr);
|
|
9658
9812
|
// return this.chunkedInddbAudioRequest(aCtx,recUrlStr);
|
|
9659
9813
|
// }
|
|
9660
|
-
fetchRecordingFileAudioBuffer(
|
|
9814
|
+
fetchRecordingFileAudioBuffer(projectName, recordingFile) {
|
|
9661
9815
|
let wobs = new Observable(observer => {
|
|
9662
9816
|
let recFileId = recordingFile.recordingFileId;
|
|
9663
9817
|
if (!recFileId) {
|
|
@@ -9669,10 +9823,10 @@ class RecordingService extends BasicRecordingService {
|
|
|
9669
9823
|
next: resp => {
|
|
9670
9824
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9671
9825
|
if (resp.body) {
|
|
9672
|
-
|
|
9826
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9673
9827
|
observer.next(ab);
|
|
9674
9828
|
observer.complete();
|
|
9675
|
-
}
|
|
9829
|
+
}).catch(error => {
|
|
9676
9830
|
observer.error(error);
|
|
9677
9831
|
observer.complete();
|
|
9678
9832
|
});
|
|
@@ -9717,7 +9871,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9717
9871
|
//console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
|
|
9718
9872
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9719
9873
|
if (resp.body) {
|
|
9720
|
-
|
|
9874
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9721
9875
|
let abs = new AudioBufferSource(ab);
|
|
9722
9876
|
let adh = new AudioDataHolder(abs);
|
|
9723
9877
|
RecordingFileUtils.setAudioData(recordingFile, adh);
|
|
@@ -9731,7 +9885,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9731
9885
|
observer.next(recordingFile);
|
|
9732
9886
|
observer.complete();
|
|
9733
9887
|
}
|
|
9734
|
-
}
|
|
9888
|
+
}).catch(error => {
|
|
9735
9889
|
observer.error(error);
|
|
9736
9890
|
observer.complete();
|
|
9737
9891
|
});
|
|
@@ -9764,7 +9918,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
9764
9918
|
});
|
|
9765
9919
|
return wobs;
|
|
9766
9920
|
}
|
|
9767
|
-
fetchSprRecordingFileAudioBuffer(
|
|
9921
|
+
fetchSprRecordingFileAudioBuffer(projectName, recordingFile) {
|
|
9768
9922
|
let wobs = new Observable(observer => {
|
|
9769
9923
|
if (recordingFile.session) {
|
|
9770
9924
|
let obs = this.fetchSprAudiofile(projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
@@ -9772,11 +9926,11 @@ class RecordingService extends BasicRecordingService {
|
|
|
9772
9926
|
next: resp => {
|
|
9773
9927
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
9774
9928
|
if (resp.body) {
|
|
9775
|
-
|
|
9929
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
9776
9930
|
//RecordingFileUtils.setAudioData(recordingFile,new AudioDataHolder(ab,null));
|
|
9777
9931
|
observer.next(ab);
|
|
9778
9932
|
observer.complete();
|
|
9779
|
-
}
|
|
9933
|
+
}).catch(error => {
|
|
9780
9934
|
observer.error(error);
|
|
9781
9935
|
observer.complete();
|
|
9782
9936
|
});
|
|
@@ -9804,14 +9958,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9804
9958
|
});
|
|
9805
9959
|
return wobs;
|
|
9806
9960
|
}
|
|
9807
|
-
fetchSprRecordingFileArrayAudioBuffer(
|
|
9961
|
+
fetchSprRecordingFileArrayAudioBuffer(projectName, recordingFile) {
|
|
9808
9962
|
let wobs = new Observable(observer => {
|
|
9809
9963
|
if (recordingFile.session) {
|
|
9810
9964
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9811
9965
|
if (baseUrl) {
|
|
9812
9966
|
if (recordingFile.samplerate) {
|
|
9813
9967
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9814
|
-
let obs = this.chunkedAudioRequestToArrayBuffer(
|
|
9968
|
+
let obs = this.chunkedAudioRequestToArrayBuffer(baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9815
9969
|
//let obs = this.fetchSprAudiofileArrayBuffer(aCtx,projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
9816
9970
|
let subscr = obs.subscribe({
|
|
9817
9971
|
next: aab => {
|
|
@@ -9848,14 +10002,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9848
10002
|
});
|
|
9849
10003
|
return wobs;
|
|
9850
10004
|
}
|
|
9851
|
-
fetchRecordingFileArrayAudioBuffer(
|
|
10005
|
+
fetchRecordingFileArrayAudioBuffer(projectName, recordingFile) {
|
|
9852
10006
|
let wobs = new Observable(observer => {
|
|
9853
10007
|
if (recordingFile.session) {
|
|
9854
10008
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
9855
10009
|
if (baseUrl) {
|
|
9856
10010
|
if (recordingFile.samplerate) {
|
|
9857
10011
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9858
|
-
let obs = this.chunkedAudioRequestToArrayBuffer(
|
|
10012
|
+
let obs = this.chunkedAudioRequestToArrayBuffer(baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9859
10013
|
//let obs = this.fetchSprAudiofileArrayBuffer(aCtx,projectName, recordingFile.session, recordingFile.itemCode, recordingFile.version);
|
|
9860
10014
|
let subscr = obs.subscribe({
|
|
9861
10015
|
next: aab => {
|
|
@@ -9892,14 +10046,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9892
10046
|
});
|
|
9893
10047
|
return wobs;
|
|
9894
10048
|
}
|
|
9895
|
-
fetchSprRecordingFileIndDbAudioBuffer(
|
|
10049
|
+
fetchSprRecordingFileIndDbAudioBuffer(persistentAudioStorageTarget, projectName, recordingFile) {
|
|
9896
10050
|
let wobs = new Observable(observer => {
|
|
9897
10051
|
if (recordingFile.session) {
|
|
9898
10052
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9899
10053
|
if (baseUrl) {
|
|
9900
10054
|
if (recordingFile.samplerate) {
|
|
9901
10055
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9902
|
-
let obs = this.chunkedInddbAudioRequest(
|
|
10056
|
+
let obs = this.chunkedInddbAudioRequest(persistentAudioStorageTarget, baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9903
10057
|
let subscr = obs.subscribe({
|
|
9904
10058
|
next: aab => {
|
|
9905
10059
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -9935,14 +10089,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9935
10089
|
});
|
|
9936
10090
|
return wobs;
|
|
9937
10091
|
}
|
|
9938
|
-
fetchRecordingFileIndDbAudioBuffer(
|
|
10092
|
+
fetchRecordingFileIndDbAudioBuffer(persistentAudioStorageTarget, projectName, recordingFile) {
|
|
9939
10093
|
let wobs = new Observable(observer => {
|
|
9940
10094
|
if (recordingFile.session) {
|
|
9941
10095
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
9942
10096
|
if (baseUrl) {
|
|
9943
10097
|
if (recordingFile.samplerate) {
|
|
9944
10098
|
let lengthInSeconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9945
|
-
let obs = this.chunkedInddbAudioRequest(
|
|
10099
|
+
let obs = this.chunkedInddbAudioRequest(persistentAudioStorageTarget, baseUrl, recordingFile.samplerate, lengthInSeconds);
|
|
9946
10100
|
let subscr = obs.subscribe({
|
|
9947
10101
|
next: aab => {
|
|
9948
10102
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -9978,14 +10132,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
9978
10132
|
});
|
|
9979
10133
|
return wobs;
|
|
9980
10134
|
}
|
|
9981
|
-
fetchSprRecordingFileNetAudioBuffer(
|
|
10135
|
+
fetchSprRecordingFileNetAudioBuffer(projectName, recordingFile) {
|
|
9982
10136
|
let wobs = new Observable(observer => {
|
|
9983
10137
|
if (recordingFile.session) {
|
|
9984
10138
|
let baseUrl = this.sprAudioFileUrl(projectName, recordingFile);
|
|
9985
10139
|
if (baseUrl) {
|
|
9986
10140
|
let seconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
9987
10141
|
if (recordingFile.samplerate) {
|
|
9988
|
-
let obs = this.chunkAudioRequestToNetAudioBuffer(
|
|
10142
|
+
let obs = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, recordingFile.samplerate, seconds, recordingFile.frames);
|
|
9989
10143
|
let subscr = obs.subscribe({
|
|
9990
10144
|
next: aab => {
|
|
9991
10145
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -10026,14 +10180,14 @@ class RecordingService extends BasicRecordingService {
|
|
|
10026
10180
|
});
|
|
10027
10181
|
return wobs;
|
|
10028
10182
|
}
|
|
10029
|
-
fetchRecordingFileNetAudioBuffer(
|
|
10183
|
+
fetchRecordingFileNetAudioBuffer(projectName, recordingFile) {
|
|
10030
10184
|
let wobs = new Observable(observer => {
|
|
10031
10185
|
if (recordingFile.session) {
|
|
10032
10186
|
let baseUrl = this.audioFileUrl(projectName, recordingFile);
|
|
10033
10187
|
if (baseUrl) {
|
|
10034
10188
|
let seconds = RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
10035
10189
|
if (recordingFile.samplerate) {
|
|
10036
|
-
let obs = this.chunkAudioRequestToNetAudioBuffer(
|
|
10190
|
+
let obs = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, recordingFile.samplerate, seconds, recordingFile.frames);
|
|
10037
10191
|
let subscr = obs.subscribe({
|
|
10038
10192
|
next: aab => {
|
|
10039
10193
|
//console.debug("fetchSprRecordingFileIndDbAudioBuffer: observer.closed: "+observer.closed);
|
|
@@ -10083,7 +10237,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10083
10237
|
//console.log("Fetched audio file. HTTP response status: "+resp.status+", type: "+resp.type+", byte length: "+ resp.body.byteLength);
|
|
10084
10238
|
// Do not use Promise version, which does not work with Safari 13 (13.0.5)
|
|
10085
10239
|
if (resp.body) {
|
|
10086
|
-
|
|
10240
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
10087
10241
|
let abs = new AudioBufferSource(ab);
|
|
10088
10242
|
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(abs));
|
|
10089
10243
|
if (this.debugDelay > 0) {
|
|
@@ -10096,7 +10250,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10096
10250
|
observer.next(recordingFile);
|
|
10097
10251
|
observer.complete();
|
|
10098
10252
|
}
|
|
10099
|
-
}
|
|
10253
|
+
}).catch(error => {
|
|
10100
10254
|
observer.error(error);
|
|
10101
10255
|
observer.complete();
|
|
10102
10256
|
});
|
|
@@ -10128,9 +10282,8 @@ class RecordingService extends BasicRecordingService {
|
|
|
10128
10282
|
let wobs = new Observable(observer => {
|
|
10129
10283
|
let obs = this.fetchSprAudiofile(projectName, sessId, itemcode, version);
|
|
10130
10284
|
obs.subscribe({ next: resp => {
|
|
10131
|
-
// Do not use Promise version, which does not work with Safari 13
|
|
10132
10285
|
if (resp.body) {
|
|
10133
|
-
|
|
10286
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
10134
10287
|
let abs = new AudioBufferSource(ab);
|
|
10135
10288
|
let adh = new AudioDataHolder(abs);
|
|
10136
10289
|
let rf = new SprRecordingFile(sessId, itemcode, version, adh);
|
|
@@ -10144,7 +10297,7 @@ class RecordingService extends BasicRecordingService {
|
|
|
10144
10297
|
observer.next(rf);
|
|
10145
10298
|
observer.complete();
|
|
10146
10299
|
}
|
|
10147
|
-
});
|
|
10300
|
+
}).catch((reason) => { observer.error(reason); });
|
|
10148
10301
|
}
|
|
10149
10302
|
else {
|
|
10150
10303
|
observer.error();
|
|
@@ -10181,23 +10334,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
10181
10334
|
}] }];
|
|
10182
10335
|
} });
|
|
10183
10336
|
|
|
10184
|
-
class AudioContextProvider {
|
|
10185
|
-
static audioContextInstance() {
|
|
10186
|
-
if (!this._audioContext) {
|
|
10187
|
-
let debugFail = false;
|
|
10188
|
-
if (!window.AudioContext || typeof window.AudioContext !== 'function' || debugFail) {
|
|
10189
|
-
throw new Error('Browser does not support Web Audio API!');
|
|
10190
|
-
this._audioContext = null;
|
|
10191
|
-
}
|
|
10192
|
-
else {
|
|
10193
|
-
this._audioContext = new window.AudioContext();
|
|
10194
|
-
}
|
|
10195
|
-
}
|
|
10196
|
-
return this._audioContext;
|
|
10197
|
-
}
|
|
10198
|
-
}
|
|
10199
|
-
AudioContextProvider._audioContext = null;
|
|
10200
|
-
|
|
10201
10337
|
class Item {
|
|
10202
10338
|
constructor(_promptAsString, _training, _recording) {
|
|
10203
10339
|
this._promptAsString = _promptAsString;
|
|
@@ -12073,6 +12209,7 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12073
12209
|
this._wakeLock = false;
|
|
12074
12210
|
this._selectedDeviceId = undefined;
|
|
12075
12211
|
this._channelCount = 2;
|
|
12212
|
+
this._allowEchoCancellation = false;
|
|
12076
12213
|
this._session = null;
|
|
12077
12214
|
this._recordingFile = null;
|
|
12078
12215
|
this.startedDate = null;
|
|
@@ -12114,6 +12251,12 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12114
12251
|
this.streamLevelMeasure = new StreamLevelMeasure();
|
|
12115
12252
|
this.selCaptureDeviceId = null;
|
|
12116
12253
|
}
|
|
12254
|
+
get allowEchoCancellation() {
|
|
12255
|
+
return this._allowEchoCancellation;
|
|
12256
|
+
}
|
|
12257
|
+
set allowEchoCancellation(value) {
|
|
12258
|
+
this._allowEchoCancellation = value;
|
|
12259
|
+
}
|
|
12117
12260
|
get maxAutoNetMemStoreSamples() {
|
|
12118
12261
|
return this._maxAutoNetMemStoreSamples;
|
|
12119
12262
|
}
|
|
@@ -12499,7 +12642,7 @@ let BasicRecorder = class BasicRecorder extends ResponsiveComponent {
|
|
|
12499
12642
|
else {
|
|
12500
12643
|
console.log("Open session with default audio device for " + this._channelCount + " channels");
|
|
12501
12644
|
}
|
|
12502
|
-
this.ac.open(this._channelCount, this._selectedDeviceId, this._autoGainControlConfigs);
|
|
12645
|
+
this.ac.open(this._channelCount, this._selectedDeviceId, this._autoGainControlConfigs, this._allowEchoCancellation);
|
|
12503
12646
|
}
|
|
12504
12647
|
else {
|
|
12505
12648
|
this.ac.start();
|
|
@@ -12859,6 +13002,7 @@ class SessionManager extends BasicRecorder {
|
|
|
12859
13002
|
this.bpo = bpo;
|
|
12860
13003
|
this.renderer = renderer;
|
|
12861
13004
|
this.recFileService = recFileService;
|
|
13005
|
+
this.offlineAudioContext = null;
|
|
12862
13006
|
this.enableUploadRecordings = true;
|
|
12863
13007
|
this.enableDownloadRecordings = false;
|
|
12864
13008
|
this.status = 0 /* BLOCKED */;
|
|
@@ -12915,37 +13059,58 @@ class SessionManager extends BasicRecorder {
|
|
|
12915
13059
|
this.transportActions.nextAction.disabled = true;
|
|
12916
13060
|
this.transportActions.pauseAction.disabled = true;
|
|
12917
13061
|
this.playStartAction.disabled = true;
|
|
12918
|
-
let context
|
|
12919
|
-
try {
|
|
12920
|
-
|
|
12921
|
-
}
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
13062
|
+
// let context:AudioContext|null=null;
|
|
13063
|
+
// try {
|
|
13064
|
+
// context = AudioContextProvider.audioContextInstance();
|
|
13065
|
+
// } catch (err) {
|
|
13066
|
+
// this.status = Status.ERROR;
|
|
13067
|
+
// let errMsg = 'Unknown error';
|
|
13068
|
+
// if(err instanceof Error){
|
|
13069
|
+
// errMsg=err.message;
|
|
13070
|
+
// }
|
|
13071
|
+
// this.statusMsg = 'ERROR: ' + errMsg;
|
|
13072
|
+
// this.statusAlertType = 'error';
|
|
13073
|
+
// this.dialog.open(MessageDialog, {
|
|
13074
|
+
// data: {
|
|
13075
|
+
// type: 'error',
|
|
13076
|
+
// title: 'Error',
|
|
13077
|
+
// msg: errMsg,
|
|
13078
|
+
// advice: 'Please use a supported browser.',
|
|
13079
|
+
// }
|
|
13080
|
+
// });
|
|
13081
|
+
// return;
|
|
13082
|
+
// }
|
|
13083
|
+
// if(context) {
|
|
13084
|
+
// console.info("State of audio context: " + context.state)
|
|
13085
|
+
// }else{
|
|
13086
|
+
// console.info("No audio context available!");
|
|
13087
|
+
// }
|
|
13088
|
+
// if (!context || !navigator.mediaDevices) {
|
|
13089
|
+
// this.status = Status.ERROR;
|
|
13090
|
+
// let errMsg = 'Browser does not support Media streams!';
|
|
13091
|
+
// this.statusMsg = 'ERROR: ' + errMsg;
|
|
13092
|
+
// this.statusAlertType = 'error';
|
|
13093
|
+
// this.dialog.open(MessageDialog, {
|
|
13094
|
+
// data: {
|
|
13095
|
+
// type: 'error',
|
|
13096
|
+
// title: 'Error',
|
|
13097
|
+
// msg: errMsg,
|
|
13098
|
+
// advice: 'Please use a supported browser.',
|
|
13099
|
+
// }
|
|
13100
|
+
// });
|
|
13101
|
+
// return;
|
|
13102
|
+
// } else {
|
|
13103
|
+
this.ac = new AudioCapture();
|
|
13104
|
+
if (this.ac) {
|
|
13105
|
+
this.transportActions.startAction.onAction = () => this.startItem();
|
|
13106
|
+
this.ac.listener = this;
|
|
13107
|
+
this.configureStreamCaptureStream();
|
|
13108
|
+
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
13109
|
+
//this.ac.listDevices();
|
|
12942
13110
|
}
|
|
12943
13111
|
else {
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
if (!context || !navigator.mediaDevices) {
|
|
12947
|
-
this.status = 9 /* ERROR */;
|
|
12948
|
-
let errMsg = 'Browser does not support Media streams!';
|
|
13112
|
+
this.transportActions.startAction.disabled = true;
|
|
13113
|
+
let errMsg = 'Browser does not support Media/Audio API!';
|
|
12949
13114
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
12950
13115
|
this.statusAlertType = 'error';
|
|
12951
13116
|
this.dialog.open(MessageDialog, {
|
|
@@ -12958,38 +13123,13 @@ class SessionManager extends BasicRecorder {
|
|
|
12958
13123
|
});
|
|
12959
13124
|
return;
|
|
12960
13125
|
}
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
//this.ac.listDevices();
|
|
12969
|
-
}
|
|
12970
|
-
else {
|
|
12971
|
-
this.transportActions.startAction.disabled = true;
|
|
12972
|
-
let errMsg = 'Browser does not support Media/Audio API!';
|
|
12973
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
12974
|
-
this.statusAlertType = 'error';
|
|
12975
|
-
this.dialog.open(MessageDialog, {
|
|
12976
|
-
data: {
|
|
12977
|
-
type: 'error',
|
|
12978
|
-
title: 'Error',
|
|
12979
|
-
msg: errMsg,
|
|
12980
|
-
advice: 'Please use a supported browser.',
|
|
12981
|
-
}
|
|
12982
|
-
});
|
|
12983
|
-
return;
|
|
12984
|
-
}
|
|
12985
|
-
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
12986
|
-
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
12987
|
-
this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
12988
|
-
this.transportActions.fwdAction.onAction = () => this.nextItem();
|
|
12989
|
-
this.transportActions.fwdNextAction.onAction = () => this.nextUnrecordedItem();
|
|
12990
|
-
this.transportActions.bwdAction.onAction = () => this.prevItem();
|
|
12991
|
-
this.playStartAction.onAction = () => { var _a; return (_a = this.controlAudioPlayer) === null || _a === void 0 ? void 0 : _a.start(); };
|
|
12992
|
-
}
|
|
13126
|
+
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
13127
|
+
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
13128
|
+
this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
13129
|
+
this.transportActions.fwdAction.onAction = () => this.nextItem();
|
|
13130
|
+
this.transportActions.fwdNextAction.onAction = () => this.nextUnrecordedItem();
|
|
13131
|
+
this.transportActions.bwdAction.onAction = () => this.prevItem();
|
|
13132
|
+
this.playStartAction.onAction = () => { var _a; return (_a = this.controlAudioPlayer) === null || _a === void 0 ? void 0 : _a.start(); };
|
|
12993
13133
|
this.startStopSignalState = 4 /* OFF */;
|
|
12994
13134
|
}
|
|
12995
13135
|
onKeyPress(ke) {
|
|
@@ -13296,7 +13436,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13296
13436
|
}
|
|
13297
13437
|
else {
|
|
13298
13438
|
//console.debug("Fetch audio and store to indexed db...");
|
|
13299
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileIndDbAudioBuffer(this.
|
|
13439
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileIndDbAudioBuffer(this._persistentAudioStorageTarget, this._session.project, rf).subscribe({
|
|
13300
13440
|
next: (iab) => {
|
|
13301
13441
|
//console.debug("Sessionmanager: Received inddb audio buffer: "+iab);
|
|
13302
13442
|
nextIab = iab;
|
|
@@ -13338,7 +13478,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13338
13478
|
// Fetch chunked audio buffer from network
|
|
13339
13479
|
let nextNetAb = null;
|
|
13340
13480
|
//console.debug("Fetch chunked audio from network");
|
|
13341
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileNetAudioBuffer(this.
|
|
13481
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileNetAudioBuffer(this._session.project, rf).subscribe({
|
|
13342
13482
|
next: (netAb) => {
|
|
13343
13483
|
//console.debug("Sessionmanager: Received net audio buffer: "+netAb);
|
|
13344
13484
|
nextNetAb = netAb;
|
|
@@ -13380,7 +13520,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13380
13520
|
// Fetch chunked array audio buffer
|
|
13381
13521
|
let nextAab = null;
|
|
13382
13522
|
//console.debug("Fetch audio and store to (chunked) array buffer...");
|
|
13383
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileArrayAudioBuffer(this.
|
|
13523
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileArrayAudioBuffer(this._session.project, rf).subscribe({
|
|
13384
13524
|
next: (aab) => {
|
|
13385
13525
|
nextAab = aab;
|
|
13386
13526
|
},
|
|
@@ -13418,7 +13558,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13418
13558
|
else {
|
|
13419
13559
|
// Fetch regular audio buffer
|
|
13420
13560
|
//console.debug("Fetch audio and store to audio buffer...");
|
|
13421
|
-
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this.
|
|
13561
|
+
this.audioFetchSubscription = this.recFileService.fetchSprRecordingFileAudioBuffer(this._session.project, rf).subscribe({
|
|
13422
13562
|
next: (ab) => {
|
|
13423
13563
|
this.liveLevelDisplayState = State.READY;
|
|
13424
13564
|
let fabDh = null;
|
|
@@ -13806,7 +13946,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13806
13946
|
const sr = this.ac.currentSampleRate;
|
|
13807
13947
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
13808
13948
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
13809
|
-
let netAb = new NetAudioBuffer(this.
|
|
13949
|
+
let netAb = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
13810
13950
|
as = netAb;
|
|
13811
13951
|
if (this.uploadSet) {
|
|
13812
13952
|
//let rp=new ReadyProvider();
|
|
@@ -13853,7 +13993,7 @@ class SessionManager extends BasicRecorder {
|
|
|
13853
13993
|
const sr = this.ac.currentSampleRate;
|
|
13854
13994
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
13855
13995
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
13856
|
-
const netAb = new NetAudioBuffer(this.
|
|
13996
|
+
const netAb = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
13857
13997
|
as = netAb;
|
|
13858
13998
|
if (this.uploadSet) {
|
|
13859
13999
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -13891,8 +14031,8 @@ class SessionManager extends BasicRecorder {
|
|
|
13891
14031
|
// TODO could we avoid conversion to save CPU resources and transfer float PCM directly?
|
|
13892
14032
|
// TODO duplicate conversion for manual download
|
|
13893
14033
|
//console.log("Build wav writer...");
|
|
13894
|
-
this.processingRecording = true;
|
|
13895
14034
|
if (ab) {
|
|
14035
|
+
this.processingRecording = true;
|
|
13896
14036
|
let ww = new WavWriter();
|
|
13897
14037
|
//new REST API URL
|
|
13898
14038
|
let apiEndPoint = '';
|
|
@@ -14405,18 +14545,10 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
14405
14545
|
console.error(errMsg);
|
|
14406
14546
|
}
|
|
14407
14547
|
ngOnInit() {
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
}
|
|
14413
|
-
this.sm.controlAudioPlayer = this.controlAudioPlayer;
|
|
14414
|
-
this.sm.statusAlertType = 'info';
|
|
14415
|
-
this.sm.statusMsg = 'Player initialized.';
|
|
14416
|
-
}
|
|
14417
|
-
catch (err) {
|
|
14418
|
-
this.handleError(err);
|
|
14419
|
-
}
|
|
14548
|
+
this.controlAudioPlayer = new AudioPlayer(this);
|
|
14549
|
+
this.sm.controlAudioPlayer = this.controlAudioPlayer;
|
|
14550
|
+
this.sm.statusAlertType = 'info';
|
|
14551
|
+
this.sm.statusMsg = 'Player initialized.';
|
|
14420
14552
|
}
|
|
14421
14553
|
ngAfterViewInit() {
|
|
14422
14554
|
// let wakeLockSupp=('wakeLock' in navigator);
|
|
@@ -14673,6 +14805,9 @@ class SpeechrecorderngComponent extends RecorderComponent {
|
|
|
14673
14805
|
chCnt = ProjectUtil.audioChannelCount(project);
|
|
14674
14806
|
console.info("Project requested recording channel count: " + chCnt);
|
|
14675
14807
|
this.sm.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
14808
|
+
if (project.allowEchoCancellation !== undefined) {
|
|
14809
|
+
this.sm.allowEchoCancellation = project.allowEchoCancellation;
|
|
14810
|
+
}
|
|
14676
14811
|
if (project.chunkedRecording === true) {
|
|
14677
14812
|
console.debug("Enable chunked upload: chunkSize: " + BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS);
|
|
14678
14813
|
this.sm.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
|
|
@@ -14769,10 +14904,8 @@ class AudioDisplayPlayer {
|
|
|
14769
14904
|
this.ref = ref;
|
|
14770
14905
|
this.eRef = eRef;
|
|
14771
14906
|
this._audioUrl = null;
|
|
14772
|
-
this.aCtx = null;
|
|
14773
14907
|
this._audioClip = null;
|
|
14774
14908
|
this.currentLoader = null;
|
|
14775
|
-
//console.log("constructor: "+this.ac);
|
|
14776
14909
|
this.parentE = this.eRef.nativeElement;
|
|
14777
14910
|
this.playStartAction = new Action("Start");
|
|
14778
14911
|
this.playSelectionAction = new Action("Play selected");
|
|
@@ -14780,26 +14913,19 @@ class AudioDisplayPlayer {
|
|
|
14780
14913
|
this.status = "Player created.";
|
|
14781
14914
|
}
|
|
14782
14915
|
ngOnInit() {
|
|
14783
|
-
//console.log("OnInit: "+this.ac);
|
|
14784
14916
|
this.zoomSelectedAction = this.audioDisplayScrollPane.zoomSelectedAction;
|
|
14785
14917
|
this.zoomFitToPanelAction = this.audioDisplayScrollPane.zoomFitToPanelAction;
|
|
14786
14918
|
this.zoomOutAction = this.audioDisplayScrollPane.zoomOutAction;
|
|
14787
14919
|
this.zoomInAction = this.audioDisplayScrollPane.zoomInAction;
|
|
14788
|
-
|
|
14789
|
-
this.aCtx = AudioContextProvider.audioContextInstance();
|
|
14790
|
-
if (this.aCtx) {
|
|
14791
|
-
this.ap = new AudioPlayer(this.aCtx, this);
|
|
14792
|
-
}
|
|
14793
|
-
}
|
|
14794
|
-
catch (err) {
|
|
14795
|
-
if (err instanceof Error) {
|
|
14796
|
-
this.status = err.message;
|
|
14797
|
-
}
|
|
14798
|
-
}
|
|
14920
|
+
this.ap = new AudioPlayer(this);
|
|
14799
14921
|
}
|
|
14800
14922
|
ngAfterViewInit() {
|
|
14801
|
-
if (this.
|
|
14802
|
-
this.playStartAction.onAction = () => {
|
|
14923
|
+
if (this.ap) {
|
|
14924
|
+
this.playStartAction.onAction = () => {
|
|
14925
|
+
var _a;
|
|
14926
|
+
console.debug("Start action, player: " + this.ap);
|
|
14927
|
+
(_a = this.ap) === null || _a === void 0 ? void 0 : _a.start();
|
|
14928
|
+
};
|
|
14803
14929
|
this.playSelectionAction.onAction = () => { var _a; return (_a = this.ap) === null || _a === void 0 ? void 0 : _a.startSelected(); };
|
|
14804
14930
|
this.playStopAction.onAction = () => { var _a; return (_a = this.ap) === null || _a === void 0 ? void 0 : _a.stop(); };
|
|
14805
14931
|
}
|
|
@@ -14864,15 +14990,12 @@ class AudioDisplayPlayer {
|
|
|
14864
14990
|
//console.debug("Loaded");
|
|
14865
14991
|
this.status = 'Audio file loaded.';
|
|
14866
14992
|
//console.debug("Received data ", data.byteLength);
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
this.audioClip = new AudioClip(adh);
|
|
14874
|
-
});
|
|
14875
|
-
}
|
|
14993
|
+
AudioContextProvider.decodeAudioData(data).then(audioBuffer => {
|
|
14994
|
+
//console.debug("Audio Buffer Samplerate: ", audioBuffer.sampleRate)
|
|
14995
|
+
let as = new AudioBufferSource(audioBuffer);
|
|
14996
|
+
let adh = new AudioDataHolder(as);
|
|
14997
|
+
this.audioClip = new AudioClip(adh);
|
|
14998
|
+
});
|
|
14876
14999
|
}
|
|
14877
15000
|
set audioData(audioData) {
|
|
14878
15001
|
this.audioDisplayScrollPane.audioData = audioData;
|
|
@@ -15132,14 +15255,13 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15132
15255
|
});
|
|
15133
15256
|
}
|
|
15134
15257
|
// TODO test
|
|
15135
|
-
fetchAndApplyRecordingFile(
|
|
15258
|
+
fetchAndApplyRecordingFile(recordingFile) {
|
|
15136
15259
|
let wobs = new Observable(observer => {
|
|
15137
15260
|
if (recordingFile.recordingFileId) {
|
|
15138
15261
|
let obs = this.fetchAudiofile(recordingFile.recordingFileId);
|
|
15139
15262
|
obs.subscribe({ next: resp => {
|
|
15140
|
-
// Do not use Promise version, which does not work with Safari 13
|
|
15141
15263
|
if (resp.body) {
|
|
15142
|
-
|
|
15264
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15143
15265
|
let as = new AudioBufferSource(ab);
|
|
15144
15266
|
RecordingFileUtils.setAudioData(recordingFile, new AudioDataHolder(as));
|
|
15145
15267
|
if (this.debugDelay > 0) {
|
|
@@ -15152,7 +15274,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15152
15274
|
observer.next(recordingFile);
|
|
15153
15275
|
observer.complete();
|
|
15154
15276
|
}
|
|
15155
|
-
});
|
|
15277
|
+
}).catch(reason => { observer.error(reason); });
|
|
15156
15278
|
}
|
|
15157
15279
|
else {
|
|
15158
15280
|
observer.error('Received no audio data!');
|
|
@@ -15192,7 +15314,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15192
15314
|
next: resp => {
|
|
15193
15315
|
// Do not use Promise version, which does not work with Safari 13
|
|
15194
15316
|
if (resp.body) {
|
|
15195
|
-
|
|
15317
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15196
15318
|
if (rf) {
|
|
15197
15319
|
let as = new AudioBufferSource(ab);
|
|
15198
15320
|
RecordingFileUtils.setAudioData(rf, new AudioDataHolder(as));
|
|
@@ -15210,7 +15332,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15210
15332
|
observer.next(rf);
|
|
15211
15333
|
observer.complete();
|
|
15212
15334
|
}
|
|
15213
|
-
});
|
|
15335
|
+
}).catch(reason => { observer.error(reason); });
|
|
15214
15336
|
}
|
|
15215
15337
|
else {
|
|
15216
15338
|
observer.error('Received no audio data');
|
|
@@ -15234,7 +15356,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15234
15356
|
});
|
|
15235
15357
|
return wobs;
|
|
15236
15358
|
}
|
|
15237
|
-
fetchSprRecordingFile(
|
|
15359
|
+
fetchSprRecordingFile(recordingFileId) {
|
|
15238
15360
|
let wobs = new Observable(observer => {
|
|
15239
15361
|
let rf = null;
|
|
15240
15362
|
let rfDescrObs = this.sprRecordingFileDescrObserver(recordingFileId);
|
|
@@ -15251,7 +15373,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15251
15373
|
// TODO use download storage type depending on sample count of file
|
|
15252
15374
|
if (rf && rf.samplerate && sampleCnt != null && sampleCnt > this._maxAutoNetMemStoreSamples) {
|
|
15253
15375
|
const baseUrl = this.recoFileUrl(recordingFileId);
|
|
15254
|
-
const obNetAb = this.chunkAudioRequestToNetAudioBuffer(
|
|
15376
|
+
const obNetAb = this.chunkAudioRequestToNetAudioBuffer(baseUrl, 0, rf === null || rf === void 0 ? void 0 : rf.samplerate, BasicRecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS, rf.frames);
|
|
15255
15377
|
obNetAb.subscribe({
|
|
15256
15378
|
next: (nab) => {
|
|
15257
15379
|
let adh = new AudioDataHolder(nab);
|
|
@@ -15282,7 +15404,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15282
15404
|
rfAudioObs.subscribe({ next: resp => {
|
|
15283
15405
|
// Do not use Promise version, which does not work with Safari 13
|
|
15284
15406
|
if (resp.body) {
|
|
15285
|
-
|
|
15407
|
+
AudioContextProvider.decodeAudioData(resp.body).then(ab => {
|
|
15286
15408
|
if (rf) {
|
|
15287
15409
|
let as = new AudioBufferSource(ab);
|
|
15288
15410
|
let adh = new AudioDataHolder(as);
|
|
@@ -15301,7 +15423,7 @@ class RecordingFileService extends BasicRecordingService {
|
|
|
15301
15423
|
observer.next(rf);
|
|
15302
15424
|
observer.complete();
|
|
15303
15425
|
}
|
|
15304
|
-
});
|
|
15426
|
+
}).catch(reason => { observer.error(reason); });
|
|
15305
15427
|
}
|
|
15306
15428
|
else {
|
|
15307
15429
|
observer.error('Received no audio data');
|
|
@@ -15461,9 +15583,9 @@ class RecordingFileMetaComponent {
|
|
|
15461
15583
|
constructor() {
|
|
15462
15584
|
this.sessionId = null;
|
|
15463
15585
|
this._recordingFile = null;
|
|
15464
|
-
this.stateLoading = false;
|
|
15465
15586
|
this.itemCode = null;
|
|
15466
15587
|
this.uuid = null;
|
|
15588
|
+
this.stateLoading = false;
|
|
15467
15589
|
}
|
|
15468
15590
|
get recordingFile() {
|
|
15469
15591
|
return this._recordingFile;
|
|
@@ -15572,7 +15694,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
15572
15694
|
</mat-card-content>
|
|
15573
15695
|
</mat-card>
|
|
15574
15696
|
` }]
|
|
15575
|
-
}], propDecorators: { sessionId: [{
|
|
15697
|
+
}], ctorParameters: function () { return []; }, propDecorators: { sessionId: [{
|
|
15576
15698
|
type: Input
|
|
15577
15699
|
}], stateLoading: [{
|
|
15578
15700
|
type: Input
|
|
@@ -15593,7 +15715,6 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15593
15715
|
this.ref = ref;
|
|
15594
15716
|
this.eRef = eRef;
|
|
15595
15717
|
this.dialog = dialog;
|
|
15596
|
-
//protected _recordingFileId: string | number = null;
|
|
15597
15718
|
this.sessionId = null;
|
|
15598
15719
|
this.sessionIdFromRoute = null;
|
|
15599
15720
|
this.availRecFiles = null;
|
|
@@ -15602,9 +15723,11 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15602
15723
|
this.recordingFileVersion = null;
|
|
15603
15724
|
this.routedByQueryParam = false;
|
|
15604
15725
|
this.posInList = null;
|
|
15605
|
-
this.audioFetching = false;
|
|
15606
|
-
this.naviInfoLoading = false;
|
|
15607
15726
|
this.parentE = this.eRef.nativeElement;
|
|
15727
|
+
// TODO Should be initialized with false, but this causes in debug mode:
|
|
15728
|
+
// 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
|
|
15729
|
+
this.audioFetching = true;
|
|
15730
|
+
this.naviInfoLoading = false;
|
|
15608
15731
|
this.firstAction = new Action('First');
|
|
15609
15732
|
this.firstAction.onAction = () => {
|
|
15610
15733
|
this.posInList = 0;
|
|
@@ -15772,54 +15895,51 @@ class RecordingFileViewComponent extends AudioDisplayPlayer {
|
|
|
15772
15895
|
this.recordingFile = null;
|
|
15773
15896
|
this.posInList = null;
|
|
15774
15897
|
this.updateActions();
|
|
15775
|
-
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
this.recordingFile
|
|
15784
|
-
if (
|
|
15785
|
-
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
if (
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
15797
|
-
|
|
15798
|
-
|
|
15799
|
-
if (eeffsr != null) {
|
|
15800
|
-
sel = new Selection(ab.sampleRate, esffsr, eeffsr);
|
|
15801
|
-
}
|
|
15802
|
-
else {
|
|
15803
|
-
//let ch0 = ab.getChannelData(0);
|
|
15804
|
-
let frameLength = ab.frameLen;
|
|
15805
|
-
sel = new Selection(esr, esffsr, frameLength);
|
|
15806
|
-
}
|
|
15898
|
+
this.audioFetching = true;
|
|
15899
|
+
this.recordingFileService.fetchSprRecordingFile(rfId).subscribe({
|
|
15900
|
+
next: value => {
|
|
15901
|
+
this.audioFetching = false;
|
|
15902
|
+
this.status = 'Audio file loaded.';
|
|
15903
|
+
let clip = null;
|
|
15904
|
+
this.recordingFile = value;
|
|
15905
|
+
if (this.recordingFile) {
|
|
15906
|
+
let ab = this.recordingFile.audioDataHolder;
|
|
15907
|
+
if (ab) {
|
|
15908
|
+
clip = new AudioClip(ab);
|
|
15909
|
+
let esffsr = null;
|
|
15910
|
+
let eeffsr = null;
|
|
15911
|
+
let esr = null;
|
|
15912
|
+
if (clip.audioDataHolder != null) {
|
|
15913
|
+
esr = ab.sampleRate;
|
|
15914
|
+
if (esr != null) {
|
|
15915
|
+
esffsr = RecordingFileUtil.editStartFrameForSampleRate(this.recordingFile, esr);
|
|
15916
|
+
eeffsr = RecordingFileUtil.editEndFrameForSampleRate(this.recordingFile, esr);
|
|
15917
|
+
}
|
|
15918
|
+
let sel = null;
|
|
15919
|
+
if (esffsr != null) {
|
|
15920
|
+
if (eeffsr != null) {
|
|
15921
|
+
sel = new Selection(ab.sampleRate, esffsr, eeffsr);
|
|
15807
15922
|
}
|
|
15808
|
-
else
|
|
15809
|
-
|
|
15923
|
+
else {
|
|
15924
|
+
//let ch0 = ab.getChannelData(0);
|
|
15925
|
+
let frameLength = ab.frameLen;
|
|
15926
|
+
sel = new Selection(esr, esffsr, frameLength);
|
|
15810
15927
|
}
|
|
15811
|
-
clip.selection = sel;
|
|
15812
15928
|
}
|
|
15929
|
+
else if (eeffsr != null) {
|
|
15930
|
+
sel = new Selection(esr, 0, eeffsr);
|
|
15931
|
+
}
|
|
15932
|
+
clip.selection = sel;
|
|
15813
15933
|
}
|
|
15814
15934
|
}
|
|
15815
|
-
this.audioClip = clip;
|
|
15816
|
-
this.loadedRecfile();
|
|
15817
|
-
}, error: error1 => {
|
|
15818
|
-
this.audioFetching = false;
|
|
15819
|
-
this.status = 'Error loading audio file!';
|
|
15820
15935
|
}
|
|
15821
|
-
|
|
15822
|
-
|
|
15936
|
+
this.audioClip = clip;
|
|
15937
|
+
this.loadedRecfile();
|
|
15938
|
+
}, error: error1 => {
|
|
15939
|
+
this.audioFetching = false;
|
|
15940
|
+
this.status = 'Error loading audio file!';
|
|
15941
|
+
}
|
|
15942
|
+
});
|
|
15823
15943
|
}
|
|
15824
15944
|
loadedRecfile() {
|
|
15825
15945
|
if (this.recordingFile && !this.sessionId) {
|
|
@@ -16431,38 +16551,17 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16431
16551
|
this.transportActions.nextAction.disabled = true;
|
|
16432
16552
|
this.transportActions.pauseAction.disabled = true;
|
|
16433
16553
|
this.playStartAction.disabled = true;
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
this.
|
|
16441
|
-
let errMsg = 'Unknown error';
|
|
16442
|
-
if (err instanceof Error) {
|
|
16443
|
-
errMsg = err.message;
|
|
16444
|
-
}
|
|
16445
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16446
|
-
this.statusAlertType = 'error';
|
|
16447
|
-
this.dialog.open(MessageDialog, {
|
|
16448
|
-
data: {
|
|
16449
|
-
type: 'error',
|
|
16450
|
-
title: 'Error',
|
|
16451
|
-
msg: errMsg,
|
|
16452
|
-
advice: 'Please use a supported browser.',
|
|
16453
|
-
}
|
|
16454
|
-
});
|
|
16455
|
-
return;
|
|
16456
|
-
}
|
|
16457
|
-
if (context) {
|
|
16458
|
-
console.info("State of audio context: " + context.state);
|
|
16554
|
+
this.ac = new AudioCapture();
|
|
16555
|
+
if (this.ac) {
|
|
16556
|
+
this.transportActions.startAction.onAction = () => this.startItem();
|
|
16557
|
+
this.ac.listener = this;
|
|
16558
|
+
this.configureStreamCaptureStream();
|
|
16559
|
+
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
16560
|
+
//this.ac.listDevices();
|
|
16459
16561
|
}
|
|
16460
16562
|
else {
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
if (!context || !navigator.mediaDevices) {
|
|
16464
|
-
this.status = 5 /* ERROR */;
|
|
16465
|
-
let errMsg = 'Browser does not support Media streams!';
|
|
16563
|
+
this.transportActions.startAction.disabled = true;
|
|
16564
|
+
let errMsg = 'Browser does not support Media/Audio API!';
|
|
16466
16565
|
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16467
16566
|
this.statusAlertType = 'error';
|
|
16468
16567
|
this.dialog.open(MessageDialog, {
|
|
@@ -16475,36 +16574,10 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16475
16574
|
});
|
|
16476
16575
|
return;
|
|
16477
16576
|
}
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
this.transportActions.startAction.onAction = () => this.startItem();
|
|
16483
|
-
this.ac.listener = this;
|
|
16484
|
-
this.configureStreamCaptureStream();
|
|
16485
|
-
// Don't list the devices here. If we do not have audio permissions we only get anonymized devices without labels.
|
|
16486
|
-
//this.ac.listDevices();
|
|
16487
|
-
}
|
|
16488
|
-
else {
|
|
16489
|
-
this.transportActions.startAction.disabled = true;
|
|
16490
|
-
let errMsg = 'Browser does not support Media/Audio API!';
|
|
16491
|
-
this.statusMsg = 'ERROR: ' + errMsg;
|
|
16492
|
-
this.statusAlertType = 'error';
|
|
16493
|
-
this.dialog.open(MessageDialog, {
|
|
16494
|
-
data: {
|
|
16495
|
-
type: 'error',
|
|
16496
|
-
title: 'Error',
|
|
16497
|
-
msg: errMsg,
|
|
16498
|
-
advice: 'Please use a supported browser.',
|
|
16499
|
-
}
|
|
16500
|
-
});
|
|
16501
|
-
return;
|
|
16502
|
-
}
|
|
16503
|
-
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
16504
|
-
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
16505
|
-
//this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
16506
|
-
this.playStartAction.onAction = () => { var _a; return (_a = this.controlAudioPlayer) === null || _a === void 0 ? void 0 : _a.start(); };
|
|
16507
|
-
}
|
|
16577
|
+
this.transportActions.stopAction.onAction = () => this.stopItem();
|
|
16578
|
+
this.transportActions.nextAction.onAction = () => this.stopItem();
|
|
16579
|
+
//this.transportActions.pauseAction.onAction = () => this.pauseItem();
|
|
16580
|
+
this.playStartAction.onAction = () => { var _a; return (_a = this.controlAudioPlayer) === null || _a === void 0 ? void 0 : _a.start(); };
|
|
16508
16581
|
this.uploader.listener = (ue) => {
|
|
16509
16582
|
this.uploadUpdate(ue);
|
|
16510
16583
|
};
|
|
@@ -16613,6 +16686,9 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16613
16686
|
chCnt = ProjectUtil.audioChannelCount(project);
|
|
16614
16687
|
console.info("Project requested recording channel count: " + chCnt);
|
|
16615
16688
|
this.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
16689
|
+
if (project.allowEchoCancellation !== undefined) {
|
|
16690
|
+
this.allowEchoCancellation = project.allowEchoCancellation;
|
|
16691
|
+
}
|
|
16616
16692
|
if (project.chunkedRecording === true) {
|
|
16617
16693
|
this.uploadChunkSizeSeconds = BasicRecorder.DEFAULT_CHUNK_SIZE_SECONDS;
|
|
16618
16694
|
}
|
|
@@ -16813,7 +16889,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16813
16889
|
}
|
|
16814
16890
|
else {
|
|
16815
16891
|
//console.debug("Fetch audio and store to indexed db...");
|
|
16816
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileIndDbAudioBuffer(this.
|
|
16892
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileIndDbAudioBuffer(this._persistentAudioStorageTarget, this._session.project, rf).subscribe({
|
|
16817
16893
|
next: (iab) => {
|
|
16818
16894
|
//console.debug("Sessionmanager: Received inddb audio buffer: "+iab);
|
|
16819
16895
|
nextIab = iab;
|
|
@@ -16856,7 +16932,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16856
16932
|
// Fetch chunked audio buffer from network
|
|
16857
16933
|
let nextNetAb = null;
|
|
16858
16934
|
//console.debug("Fetch chunked audio from network");
|
|
16859
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileNetAudioBuffer(this.
|
|
16935
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileNetAudioBuffer(this._session.project, rf).subscribe({
|
|
16860
16936
|
next: (netAb) => {
|
|
16861
16937
|
//console.debug("Sessionmanager: Received net audio buffer: "+netAb);
|
|
16862
16938
|
nextNetAb = netAb;
|
|
@@ -16901,7 +16977,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16901
16977
|
// Fetch chunked array audio buffer
|
|
16902
16978
|
let nextAab = null;
|
|
16903
16979
|
//console.debug("Fetch audio and store to (chunked) array buffer...");
|
|
16904
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileArrayAudioBuffer(this.
|
|
16980
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileArrayAudioBuffer(this._session.project, rf).subscribe({
|
|
16905
16981
|
next: (aab) => {
|
|
16906
16982
|
nextAab = aab;
|
|
16907
16983
|
},
|
|
@@ -16938,7 +17014,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
16938
17014
|
});
|
|
16939
17015
|
}
|
|
16940
17016
|
else {
|
|
16941
|
-
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this.
|
|
17017
|
+
this.audioFetchSubscription = this.recFileService.fetchRecordingFileAudioBuffer(this._session.project, rf).subscribe({
|
|
16942
17018
|
next: ab => {
|
|
16943
17019
|
this.liveLevelDisplayState = State.READY;
|
|
16944
17020
|
let fabDh = null;
|
|
@@ -17078,7 +17154,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17078
17154
|
this.transportActions.pauseAction.disabled = true;
|
|
17079
17155
|
this.statusAlertType = 'info';
|
|
17080
17156
|
this.statusMsg = 'Recorded.';
|
|
17081
|
-
let
|
|
17157
|
+
let ab = null;
|
|
17082
17158
|
if (this.ac) {
|
|
17083
17159
|
let adh = null;
|
|
17084
17160
|
let as = null;
|
|
@@ -17110,7 +17186,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17110
17186
|
const sr = this.ac.currentSampleRate;
|
|
17111
17187
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
17112
17188
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
17113
|
-
let netAs = new NetAudioBuffer(this.
|
|
17189
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
17114
17190
|
as = netAs;
|
|
17115
17191
|
if (this.uploadSet) {
|
|
17116
17192
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -17157,7 +17233,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17157
17233
|
const sr = this.ac.currentSampleRate;
|
|
17158
17234
|
const chFl = sr * RecordingService.DEFAULT_CHUNKED_DOWNLOAD_SECONDS;
|
|
17159
17235
|
//console.debug("stopped(): rfID: "+this._recordingFile?.recordingFileId+", net ab url: " + burl+", frames: "+this.ac.framesRecorded+", sample rate: "+sr);
|
|
17160
|
-
let netAs = new NetAudioBuffer(this.
|
|
17236
|
+
let netAs = new NetAudioBuffer(this.recFileService, burl, this.ac.channelCount, sr, chFl, this.ac.framesRecorded, rUUID, chFl);
|
|
17161
17237
|
as = netAs;
|
|
17162
17238
|
if (this.uploadSet) {
|
|
17163
17239
|
this.uploadSet.onDone = (uploadSet) => {
|
|
@@ -17176,7 +17252,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17176
17252
|
as = this.ac.audioBufferArray();
|
|
17177
17253
|
}
|
|
17178
17254
|
else {
|
|
17179
|
-
|
|
17255
|
+
ab = this.ac.audioBuffer();
|
|
17180
17256
|
if (ab) {
|
|
17181
17257
|
as = new AudioBufferSource(ab);
|
|
17182
17258
|
}
|
|
@@ -17196,7 +17272,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17196
17272
|
RecordingFileUtils.setAudioData(rf, adh);
|
|
17197
17273
|
this.recorderCombiPane.addRecFile(rf);
|
|
17198
17274
|
// Upload if upload enabled and not in chunked upload mode
|
|
17199
|
-
if (this.enableUploadRecordings && this._uploadChunkSizeSeconds === null && rf != null &&
|
|
17275
|
+
if (this.enableUploadRecordings && this._uploadChunkSizeSeconds === null && AudioStorageType.MEM_ENTIRE === this._clientAudioStorageType && rf != null && ab != null) {
|
|
17200
17276
|
let apiEndPoint = '';
|
|
17201
17277
|
if (this.config && this.config.apiEndPoint) {
|
|
17202
17278
|
apiEndPoint = this.config.apiEndPoint;
|
|
@@ -17211,7 +17287,7 @@ class AudioRecorder extends BasicRecorder {
|
|
|
17211
17287
|
// TODO duplicate conversion for manual download
|
|
17212
17288
|
this.processingRecording = true;
|
|
17213
17289
|
let ww = new WavWriter();
|
|
17214
|
-
ww.writeAsync(
|
|
17290
|
+
ww.writeAsync(ab, (wavFile) => {
|
|
17215
17291
|
this.postRecordingMultipart(wavFile, recUrl, rf);
|
|
17216
17292
|
this.processingRecording = false;
|
|
17217
17293
|
this.updateWakeLock();
|
|
@@ -17450,11 +17526,7 @@ class AudioRecorderComponent extends RecorderComponent {
|
|
|
17450
17526
|
this.uploader = uploader;
|
|
17451
17527
|
}
|
|
17452
17528
|
ngOnInit() {
|
|
17453
|
-
|
|
17454
|
-
let audioContext = AudioContextProvider.audioContextInstance();
|
|
17455
|
-
if (audioContext) {
|
|
17456
|
-
this.controlAudioPlayer = new AudioPlayer(audioContext, this.ar);
|
|
17457
|
-
}
|
|
17529
|
+
this.controlAudioPlayer = new AudioPlayer(this.ar);
|
|
17458
17530
|
this.ar.controlAudioPlayer = this.controlAudioPlayer;
|
|
17459
17531
|
//TODO Duplicate code in SpeechRecorderComponent
|
|
17460
17532
|
window.addEventListener('beforeunload', (e) => {
|
|
@@ -17622,7 +17694,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
17622
17694
|
}]
|
|
17623
17695
|
}] });
|
|
17624
17696
|
|
|
17625
|
-
const VERSION = '3.
|
|
17697
|
+
const VERSION = '3.6.0';
|
|
17626
17698
|
|
|
17627
17699
|
/*
|
|
17628
17700
|
* Public API Surface of speechrecorderng
|