speechrecorderng 2.18.13 → 2.19.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/bundles/speechrecorderng.umd.js +558 -104
- package/bundles/speechrecorderng.umd.js.map +1 -1
- package/esm2015/lib/audio/capture/capture.js +262 -69
- package/esm2015/lib/io/stream.js +8 -7
- package/esm2015/lib/speechrecorder/project/project.js +27 -1
- package/esm2015/lib/speechrecorder/session/sessionmanager.js +35 -8
- package/esm2015/lib/speechrecorderng.component.js +2 -1
- package/esm2015/lib/spr.module.version.js +2 -2
- package/esm2015/lib/ui/livelevel_display.js +24 -4
- package/esm2015/lib/utils/ua-parser.js +166 -0
- package/fesm2015/speechrecorderng.js +526 -95
- package/fesm2015/speechrecorderng.js.map +1 -1
- package/lib/audio/capture/capture.d.ts +6 -3
- package/lib/speechrecorder/project/project.d.ts +25 -0
- package/lib/speechrecorder/session/sessionmanager.d.ts +7 -3
- package/lib/spr.module.version.d.ts +1 -1
- package/lib/ui/livelevel_display.d.ts +4 -1
- package/lib/utils/ua-parser.d.ts +40 -0
- package/package.json +3 -3
|
@@ -313,14 +313,295 @@ class AudioPlayer {
|
|
|
313
313
|
}
|
|
314
314
|
AudioPlayer.DEFAULT_BUFSIZE = 8192;
|
|
315
315
|
|
|
316
|
-
class
|
|
316
|
+
class UserAgentComponent {
|
|
317
|
+
constructor(name, version, comment) {
|
|
318
|
+
this.name = name;
|
|
319
|
+
this.version = version;
|
|
320
|
+
this.comment = comment;
|
|
321
|
+
}
|
|
322
|
+
toString() {
|
|
323
|
+
let s = '[' + this.name + ']';
|
|
324
|
+
if (this.version) {
|
|
325
|
+
s = s + ' [' + this.version + ']';
|
|
326
|
+
}
|
|
327
|
+
if (this.comment) {
|
|
328
|
+
s = s + ' [' + this.comment + ']';
|
|
329
|
+
}
|
|
330
|
+
return s;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const NAME_FIREFOX = 'Firefox';
|
|
334
|
+
const NAME_CHROME = 'Chrome';
|
|
335
|
+
const NAME_SAFARI = 'Safari';
|
|
336
|
+
const NAME_EDGE = 'Edge';
|
|
337
|
+
var Browser$1;
|
|
338
|
+
(function (Browser) {
|
|
339
|
+
Browser["Firefox"] = "Firefox";
|
|
340
|
+
Browser["Chrome"] = "Chrome";
|
|
341
|
+
Browser["Safari"] = "Safari";
|
|
342
|
+
Browser["Edge"] = "Edge";
|
|
343
|
+
})(Browser$1 || (Browser$1 = {}));
|
|
344
|
+
const OS_WINDOWS = 'Windows';
|
|
345
|
+
const OS_ANDROID = 'Android';
|
|
346
|
+
var Platform$1;
|
|
347
|
+
(function (Platform) {
|
|
348
|
+
Platform["Windows"] = "Windows";
|
|
349
|
+
Platform["Android"] = "Android";
|
|
350
|
+
Platform["macOS"] = "MAC OS X";
|
|
351
|
+
})(Platform$1 || (Platform$1 = {}));
|
|
352
|
+
class UserAgent {
|
|
353
|
+
constructor(_detectedPlatform, _detectedBrowser) {
|
|
354
|
+
this._detectedPlatform = _detectedPlatform;
|
|
355
|
+
this._detectedBrowser = _detectedBrowser;
|
|
356
|
+
}
|
|
357
|
+
get detectedBrowser() {
|
|
358
|
+
return this._detectedBrowser;
|
|
359
|
+
}
|
|
360
|
+
get detectedPlatform() {
|
|
361
|
+
return this._detectedPlatform;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
class UserAgentBuilder {
|
|
317
365
|
constructor() {
|
|
318
|
-
this.
|
|
319
|
-
|
|
366
|
+
this.comps = new Array();
|
|
367
|
+
}
|
|
368
|
+
build() {
|
|
369
|
+
// // @ts-ignore
|
|
370
|
+
// if(navigator.userAgentData){
|
|
371
|
+
// // maybe we can use this in the future
|
|
372
|
+
// console.info("Browser provides userAgentData:");
|
|
373
|
+
//
|
|
374
|
+
// console.info("Brands:");
|
|
375
|
+
// // @ts-ignore
|
|
376
|
+
// navigator.userAgentData.brands.forEach((br=>{
|
|
377
|
+
// console.info(br.brand +" "+br.version);
|
|
378
|
+
// }))
|
|
379
|
+
// // @ts-ignore
|
|
380
|
+
// console.info("Platform: "+navigator.userAgentData.platform);
|
|
381
|
+
// // @ts-ignore
|
|
382
|
+
// console.info("Mobile:"+navigator.userAgentData.mobile);
|
|
383
|
+
// // @ts-ignore
|
|
384
|
+
// //console.info(navigator.userAgentData.toJSON());
|
|
385
|
+
// }else {
|
|
386
|
+
// console.info("Browser does not provide userAgentData.");
|
|
387
|
+
// }
|
|
388
|
+
let ua = navigator.userAgent;
|
|
389
|
+
this.comps = new Array();
|
|
390
|
+
let pp = 0;
|
|
391
|
+
while (pp < ua.length) {
|
|
392
|
+
//parse name/version
|
|
393
|
+
let name = null;
|
|
394
|
+
let version = null;
|
|
395
|
+
let comment;
|
|
396
|
+
let blPos = ua.indexOf(' ', pp);
|
|
397
|
+
let prt;
|
|
398
|
+
if (blPos == -1) {
|
|
399
|
+
prt = ua.substr(pp);
|
|
400
|
+
pp += prt.length;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
prt = ua.substr(pp, blPos - pp);
|
|
404
|
+
pp = blPos + 1;
|
|
405
|
+
}
|
|
406
|
+
let sepPos = prt.indexOf('/');
|
|
407
|
+
if (sepPos > 0) {
|
|
408
|
+
name = prt.substr(0, sepPos);
|
|
409
|
+
version = prt.substr(sepPos + 1);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
name = prt;
|
|
413
|
+
}
|
|
414
|
+
while (ua[pp] === ' ' && pp < ua.length) {
|
|
415
|
+
pp++;
|
|
416
|
+
}
|
|
417
|
+
if (ua[pp] === '(') {
|
|
418
|
+
pp++;
|
|
419
|
+
let commEnd = ua.indexOf(')', pp);
|
|
420
|
+
comment = ua.substr(pp, commEnd - pp);
|
|
421
|
+
pp = commEnd + 1;
|
|
422
|
+
}
|
|
423
|
+
while (ua[pp] === ' ' && pp < ua.length) {
|
|
424
|
+
pp++;
|
|
425
|
+
}
|
|
426
|
+
this.comps.push(new UserAgentComponent(name, version, comment));
|
|
427
|
+
}
|
|
428
|
+
let detPlatf = null;
|
|
429
|
+
if (this.runsOnOS(Platform$1.Android)) {
|
|
430
|
+
detPlatf = Platform$1.Android;
|
|
431
|
+
}
|
|
432
|
+
else if (this.runsOnOS(Platform$1.Windows)) {
|
|
433
|
+
detPlatf = Platform$1.Windows;
|
|
434
|
+
}
|
|
435
|
+
else if (this.runsOnOS(Platform$1.macOS)) {
|
|
436
|
+
detPlatf = Platform$1.macOS;
|
|
437
|
+
}
|
|
438
|
+
let detBr = null;
|
|
439
|
+
if (this.matchesBrowser(Browser$1.Firefox)) {
|
|
440
|
+
detBr = Browser$1.Firefox;
|
|
441
|
+
}
|
|
442
|
+
else if (this.matchesBrowser(Browser$1.Chrome)) {
|
|
443
|
+
detBr = Browser$1.Chrome;
|
|
444
|
+
}
|
|
445
|
+
else if (this.matchesBrowser(Browser$1.Safari)) {
|
|
446
|
+
detBr = Browser$1.Safari;
|
|
447
|
+
}
|
|
448
|
+
this.userAgent = new UserAgent(detPlatf, detBr);
|
|
449
|
+
}
|
|
450
|
+
matchesBrowser(browserName) {
|
|
451
|
+
for (let ci = 0; ci < this.comps.length; ci++) {
|
|
452
|
+
let bn = this.comps[ci].name;
|
|
453
|
+
let bnRe = new RegExp(browserName, 'i');
|
|
454
|
+
if (bn.match(bnRe)) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
runsOnOS(os) {
|
|
461
|
+
for (let ci = 0; ci < this.comps.length; ci++) {
|
|
462
|
+
let cc = this.comps[ci].comment;
|
|
463
|
+
if (cc) {
|
|
464
|
+
var osRe = new RegExp(os, 'i');
|
|
465
|
+
if (cc.match(osRe)) {
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
static userAgent() {
|
|
473
|
+
if (!this.instance) {
|
|
474
|
+
this.instance = new UserAgentBuilder();
|
|
475
|
+
}
|
|
476
|
+
this.instance.build();
|
|
477
|
+
return this.instance.userAgent;
|
|
320
478
|
}
|
|
321
479
|
}
|
|
480
|
+
UserAgentBuilder.instance = undefined;
|
|
481
|
+
|
|
482
|
+
var ConstraintType;
|
|
483
|
+
(function (ConstraintType) {
|
|
484
|
+
ConstraintType["Exact"] = "EXACT";
|
|
485
|
+
ConstraintType["Ideal"] = "IDEAL";
|
|
486
|
+
})(ConstraintType || (ConstraintType = {}));
|
|
487
|
+
;
|
|
488
|
+
var Platform;
|
|
489
|
+
(function (Platform) {
|
|
490
|
+
Platform["Linux"] = "LINUX";
|
|
491
|
+
Platform["macOS"] = "MACOS";
|
|
492
|
+
Platform["Windows"] = "WINDOWS";
|
|
493
|
+
Platform["Android"] = "ANDROID";
|
|
494
|
+
})(Platform || (Platform = {}));
|
|
495
|
+
var BrowserBase;
|
|
496
|
+
(function (BrowserBase) {
|
|
497
|
+
BrowserBase["Chromium"] = "CHROMIUM";
|
|
498
|
+
})(BrowserBase || (BrowserBase = {}));
|
|
499
|
+
;
|
|
500
|
+
var Browser;
|
|
501
|
+
(function (Browser) {
|
|
502
|
+
Browser["Firefox"] = "FIREFOX";
|
|
503
|
+
Browser["Chromium"] = "CHROMIUM";
|
|
504
|
+
Browser["Chrome"] = "CHROME";
|
|
505
|
+
Browser["Edge"] = "EDGE";
|
|
506
|
+
Browser["Opera"] = "OPERA";
|
|
507
|
+
})(Browser || (Browser = {}));
|
|
508
|
+
class ProjectUtil {
|
|
509
|
+
static audioChannelCount(project) {
|
|
510
|
+
let chs = ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT;
|
|
511
|
+
if (project.mediaCaptureFormat) {
|
|
512
|
+
chs = project.mediaCaptureFormat.audioChannelCount;
|
|
513
|
+
}
|
|
514
|
+
else if (project.audioFormat) {
|
|
515
|
+
chs = project.audioFormat.channels;
|
|
516
|
+
}
|
|
517
|
+
return chs;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
|
|
521
|
+
|
|
522
|
+
const CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC = false;
|
|
523
|
+
const DEBUG_TRACE_LEVEL = 0;
|
|
524
|
+
const ENABLE_AUDIO_WORKLET = true;
|
|
525
|
+
// Super dirty way to load this module
|
|
526
|
+
// Copy content of interceptor_worklet.js to this string
|
|
527
|
+
const awpStr = "class AudioCaptureInterceptorProcessor extends AudioWorkletProcessor{\n" +
|
|
528
|
+
"\n" +
|
|
529
|
+
" BUFFER_QUANTUMS=64;\n" +
|
|
530
|
+
" QUANTUM_FRAME_LEN=128;\n" +
|
|
531
|
+
" BUFFER_FRAME_LEN=this.QUANTUM_FRAME_LEN*this.BUFFER_QUANTUMS;\n" +
|
|
532
|
+
" buffer=null;\n" +
|
|
533
|
+
" bufferPos=0;\n" +
|
|
534
|
+
" bufferPosBytes=0;\n" +
|
|
535
|
+
" constructor() {\n" +
|
|
536
|
+
" super();\n" +
|
|
537
|
+
"\n" +
|
|
538
|
+
" }\n" +
|
|
539
|
+
"\n" +
|
|
540
|
+
" process(\n" +
|
|
541
|
+
" inputs,\n" +
|
|
542
|
+
" outputs,\n" +
|
|
543
|
+
" parameters\n" +
|
|
544
|
+
" ){\n" +
|
|
545
|
+
"\n" +
|
|
546
|
+
" let inputsCnt=inputs.length;\n" +
|
|
547
|
+
" let channelCount=0;\n" +
|
|
548
|
+
" let inputLen=0;\n" +
|
|
549
|
+
" let inputLenBytes=0;\n" +
|
|
550
|
+
" if(inputsCnt>0) {\n" +
|
|
551
|
+
" let input0 = inputs[0];\n" +
|
|
552
|
+
" channelCount = input0.length;\n" +
|
|
553
|
+
" if (channelCount > 0) {\n" +
|
|
554
|
+
" let input0ch0=input0[0];\n" +
|
|
555
|
+
" inputLen=input0ch0.length;\n" +
|
|
556
|
+
" inputLenBytes=input0ch0.buffer.length;\n" +
|
|
557
|
+
" }\n" +
|
|
558
|
+
" }\n" +
|
|
559
|
+
" if (!this.buffer || this.buffer.length < channelCount) {\n" +
|
|
560
|
+
" this.buffer = new Array(channelCount);\n" +
|
|
561
|
+
" this.bufferPos = 0\n" +
|
|
562
|
+
" for (let bch = 0; bch < channelCount; bch++) {\n" +
|
|
563
|
+
" this.buffer[bch] = new Float32Array(this.BUFFER_FRAME_LEN);\n" +
|
|
564
|
+
" this.bufferPos = 0;\n" +
|
|
565
|
+
" this.bufferPosBytes=0;\n" +
|
|
566
|
+
" }\n" +
|
|
567
|
+
" }\n" +
|
|
568
|
+
" let bufAvail = this.BUFFER_FRAME_LEN - this.bufferPos;\n" +
|
|
569
|
+
" // check if buffer has to be transferred\n" +
|
|
570
|
+
" if (inputLen > bufAvail) {\n" +
|
|
571
|
+
" let ada=new Array(channelCount);\n" +
|
|
572
|
+
" for (let ch = 0; ch < channelCount; ch++) {\n" +
|
|
573
|
+
" ada[ch]=this.buffer[ch].buffer.slice(0);\n" +
|
|
574
|
+
" }\n" +
|
|
575
|
+
" this.port.postMessage({\n" +
|
|
576
|
+
" data: ada,\n" +
|
|
577
|
+
" chs: channelCount,\n" +
|
|
578
|
+
" len: this.bufferPos\n" +
|
|
579
|
+
" }, ada);\n" +
|
|
580
|
+
" // buffer transferred, reset\n" +
|
|
581
|
+
" this.bufferPos = 0;\n" +
|
|
582
|
+
" this.bufferPosBytes=0;\n" +
|
|
583
|
+
" }\n" +
|
|
584
|
+
"\n" +
|
|
585
|
+
" for(let ii=0;ii<inputsCnt;ii++) {\n" +
|
|
586
|
+
" for (let ch = 0; ch < channelCount; ch++) {\n" +
|
|
587
|
+
" // Mute outputs\n" +
|
|
588
|
+
" //outputs[ii][ch].fill(0);\n" +
|
|
589
|
+
" let chSamples = inputs[ii][ch];\n" +
|
|
590
|
+
" this.buffer[ch].set(chSamples,this.bufferPos);\n" +
|
|
591
|
+
" }\n" +
|
|
592
|
+
" this.bufferPos+=inputLen;\n" +
|
|
593
|
+
" this.bufferPosBytes+=inputLenBytes;\n" +
|
|
594
|
+
" }\n" +
|
|
595
|
+
" \n" +
|
|
596
|
+
" return true;\n" +
|
|
597
|
+
" }\n" +
|
|
598
|
+
"}\n" +
|
|
599
|
+
"\n" +
|
|
600
|
+
"registerProcessor('capture-interceptor',AudioCaptureInterceptorProcessor);\n";
|
|
322
601
|
class AudioCapture {
|
|
323
602
|
constructor(context) {
|
|
603
|
+
this.agcStatus = null;
|
|
604
|
+
this.bufferingNode = null;
|
|
324
605
|
this.audioOutStream = null;
|
|
325
606
|
this.disconnectStreams = true;
|
|
326
607
|
this._opened = false;
|
|
@@ -368,7 +649,7 @@ class AudioCapture {
|
|
|
368
649
|
if (!labelsAvailable) {
|
|
369
650
|
//console.debug("Media device enumeration: No labels.")
|
|
370
651
|
if (retry) {
|
|
371
|
-
|
|
652
|
+
console.info("Starting dummy session to request audio permissions...");
|
|
372
653
|
this.dummySession().then((s) => {
|
|
373
654
|
// and stop it immediately
|
|
374
655
|
if (s) {
|
|
@@ -433,12 +714,12 @@ class AudioCapture {
|
|
|
433
714
|
console.log("Audio device: Id: " + di.deviceId + " groupId: " + di.groupId + " label: " + di.label + " kind: " + di.kind);
|
|
434
715
|
}
|
|
435
716
|
}
|
|
436
|
-
open(channelCount, selDeviceId) {
|
|
717
|
+
open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
437
718
|
this.context.resume().then(() => {
|
|
438
|
-
this._open(channelCount, selDeviceId);
|
|
719
|
+
this._open(channelCount, selDeviceId, autoGainControlConfigs);
|
|
439
720
|
});
|
|
440
721
|
}
|
|
441
|
-
_open(channelCount, selDeviceId) {
|
|
722
|
+
_open(channelCount, selDeviceId, autoGainControlConfigs) {
|
|
442
723
|
this.channelCount = channelCount;
|
|
443
724
|
this.framesRecorded = 0;
|
|
444
725
|
//var msc = new AudioStreamConstr();
|
|
@@ -454,7 +735,48 @@ class AudioCapture {
|
|
|
454
735
|
// TODO test if input is unprocessed
|
|
455
736
|
let msc;
|
|
456
737
|
console.info('User agent: ' + navigator.userAgent);
|
|
457
|
-
|
|
738
|
+
let ua = UserAgentBuilder.userAgent();
|
|
739
|
+
// ua.components.forEach((c)=>{
|
|
740
|
+
// console.info("UA_Comp: "+c.toString());
|
|
741
|
+
// })
|
|
742
|
+
let agcCfg = null;
|
|
743
|
+
let autoGainControl = false;
|
|
744
|
+
let chromeEchoCancellation = false;
|
|
745
|
+
if (autoGainControlConfigs) {
|
|
746
|
+
for (let agcc of autoGainControlConfigs) {
|
|
747
|
+
if (agcc.platform === Platform.Android && ua.detectedPlatform === Platform$1.Android) {
|
|
748
|
+
agcCfg = agcc;
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
if (agcc.platform === Platform.Windows && ua.detectedPlatform === Platform$1.Windows) {
|
|
752
|
+
agcCfg = agcc;
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (agcCfg) {
|
|
757
|
+
// TODO use EXACT/IDEAL constraint
|
|
758
|
+
autoGainControl = agcCfg.value;
|
|
759
|
+
if (CHROME_ACTIVATE_ECHO_CANCELLATION_WITH_AGC) {
|
|
760
|
+
chromeEchoCancellation = agcCfg.value;
|
|
761
|
+
}
|
|
762
|
+
// TODO query real AGC status
|
|
763
|
+
this.agcStatus = agcCfg.value;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
this.agcStatus = false;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
// default
|
|
770
|
+
msc = {
|
|
771
|
+
audio: {
|
|
772
|
+
deviceId: selDeviceId,
|
|
773
|
+
echoCancellation: false,
|
|
774
|
+
channelCount: channelCount,
|
|
775
|
+
autoGainControl: autoGainControl
|
|
776
|
+
},
|
|
777
|
+
video: false
|
|
778
|
+
};
|
|
779
|
+
if (ua.detectedBrowser === Browser$1.Edge) {
|
|
458
780
|
// Microsoft Edge sends unmodified audio
|
|
459
781
|
// The constraint can follow the specification
|
|
460
782
|
console.info("Setting media track constraints for Microsoft Edge.");
|
|
@@ -462,12 +784,13 @@ class AudioCapture {
|
|
|
462
784
|
audio: {
|
|
463
785
|
deviceId: selDeviceId,
|
|
464
786
|
echoCancellation: false,
|
|
465
|
-
channelCount: channelCount
|
|
787
|
+
channelCount: channelCount,
|
|
788
|
+
autoGainControl: autoGainControl
|
|
466
789
|
},
|
|
467
790
|
video: false
|
|
468
791
|
};
|
|
469
792
|
}
|
|
470
|
-
else if (
|
|
793
|
+
else if (ua.detectedBrowser === Browser$1.Chrome) {
|
|
471
794
|
// Google Chrome: we need to switch of each of the preprocessing units including the
|
|
472
795
|
console.info("Setting media track constraints for Google Chrome.");
|
|
473
796
|
// Chrome 60 -> 61 changed
|
|
@@ -475,47 +798,38 @@ class AudioCapture {
|
|
|
475
798
|
// Requires at least Chrome 61
|
|
476
799
|
msc = {
|
|
477
800
|
audio: {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"googExperimentalEchoCancellation": false,
|
|
484
|
-
"googAutoGainControl": false,
|
|
485
|
-
"googTypingNoiseDetection": false,
|
|
486
|
-
"googNoiseSuppression": false,
|
|
487
|
-
"googHighpassFilter": false,
|
|
488
|
-
"googBeamforming": false
|
|
801
|
+
deviceId: selDeviceId,
|
|
802
|
+
channelCount: channelCount,
|
|
803
|
+
echoCancellation: { exact: chromeEchoCancellation },
|
|
804
|
+
autoGainControl: { exact: autoGainControl },
|
|
805
|
+
sampleSize: { min: 16 },
|
|
489
806
|
},
|
|
490
807
|
video: false,
|
|
491
808
|
};
|
|
492
809
|
}
|
|
493
|
-
else if (
|
|
810
|
+
else if (ua.detectedBrowser === Browser$1.Firefox) {
|
|
494
811
|
console.info("Setting media track constraints for Mozilla Firefox.");
|
|
495
812
|
// Firefox
|
|
496
813
|
msc = {
|
|
497
814
|
audio: {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
"mozAutoGainControl": false,
|
|
504
|
-
"noiseSuppression": false,
|
|
505
|
-
"mozNoiseSuppression": false
|
|
815
|
+
deviceId: selDeviceId,
|
|
816
|
+
channelCount: channelCount,
|
|
817
|
+
echoCancellation: false,
|
|
818
|
+
autoGainControl: autoGainControl,
|
|
819
|
+
noiseSuppression: false
|
|
506
820
|
},
|
|
507
821
|
video: false,
|
|
508
822
|
};
|
|
509
823
|
}
|
|
510
|
-
else if (
|
|
824
|
+
else if (ua.detectedBrowser === Browser$1.Safari) {
|
|
511
825
|
console.info("Setting media track constraints for Safari browser.");
|
|
512
826
|
console.info("Apply workaround for Safari: Avoid disconnect of streams.");
|
|
513
827
|
this.disconnectStreams = false;
|
|
514
828
|
msc = {
|
|
515
829
|
audio: {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
830
|
+
deviceId: selDeviceId,
|
|
831
|
+
channelCount: channelCount,
|
|
832
|
+
echoCancellation: false
|
|
519
833
|
},
|
|
520
834
|
video: false,
|
|
521
835
|
};
|
|
@@ -523,6 +837,7 @@ class AudioCapture {
|
|
|
523
837
|
else {
|
|
524
838
|
// TODO default constraints or error Browser not supported
|
|
525
839
|
}
|
|
840
|
+
console.debug("Audio capture, AGC: " + this.agcStatus);
|
|
526
841
|
let ump = navigator.mediaDevices.getUserMedia(msc);
|
|
527
842
|
ump.then((s) => {
|
|
528
843
|
this.stream = s;
|
|
@@ -530,6 +845,11 @@ class AudioCapture {
|
|
|
530
845
|
for (let i = 0; i < aTracks.length; i++) {
|
|
531
846
|
let aTrack = aTracks[i];
|
|
532
847
|
console.info("Track audio info: id: " + aTrack.id + " kind: " + aTrack.kind + " label: \"" + aTrack.label + "\"");
|
|
848
|
+
let mtrSts = aTrack.getSettings();
|
|
849
|
+
console.info("Track audio settings: Ch cnt: " + mtrSts.channelCount + ", AGC: " + mtrSts.autoGainControl + ", Echo cancell.: " + mtrSts.echoCancellation);
|
|
850
|
+
if (mtrSts.autoGainControl) {
|
|
851
|
+
this.agcStatus = mtrSts.autoGainControl;
|
|
852
|
+
}
|
|
533
853
|
}
|
|
534
854
|
let vTracks = s.getVideoTracks();
|
|
535
855
|
for (let i = 0; i < vTracks.length; i++) {
|
|
@@ -539,6 +859,7 @@ class AudioCapture {
|
|
|
539
859
|
this.mediaStream = this.context.createMediaStreamSource(s);
|
|
540
860
|
// stream channel count ( is always 2 !)
|
|
541
861
|
let streamChannelCount = this.mediaStream.channelCount;
|
|
862
|
+
console.info("Stream channel count: " + streamChannelCount);
|
|
542
863
|
// is not set!!
|
|
543
864
|
//this.currentSampleRate = this.mediaStream.sampleRate;
|
|
544
865
|
this.currentSampleRate = this.context.sampleRate;
|
|
@@ -548,43 +869,117 @@ class AudioCapture {
|
|
|
548
869
|
}
|
|
549
870
|
// W3C -> new name is createScriptProcessor
|
|
550
871
|
//
|
|
551
|
-
//
|
|
872
|
+
// Again deprecated, but AudioWorker not yet implemented in stable releases (June 2016)
|
|
552
873
|
// AudioWorker is now AudioWorkletProcessor ... (May 2017)
|
|
553
874
|
// Update 12-2020:
|
|
554
875
|
// The ScriptProcessorNode Interface - DEPRECATED
|
|
555
|
-
|
|
556
|
-
|
|
876
|
+
// Update 06-2021
|
|
877
|
+
// AudioWorkletProcessor is here to stay. Web Audio API has now Recommendation status !
|
|
878
|
+
if (ENABLE_AUDIO_WORKLET && this.context.audioWorklet) {
|
|
879
|
+
//const workletFileName = ('file-loader!./interceptor_worklet.js');
|
|
880
|
+
//const workletFileName = 'http://localhost:4200/assets/interceptor_worklet.js';
|
|
881
|
+
//console.log(awpStr);
|
|
882
|
+
let audioWorkletModuleBlob = new Blob([awpStr], { type: 'text/javascript' });
|
|
883
|
+
let audioWorkletModuleBlobUrl = window.URL.createObjectURL(audioWorkletModuleBlob);
|
|
884
|
+
this.context.audioWorklet.addModule(audioWorkletModuleBlobUrl).then(() => {
|
|
885
|
+
const awn = new AudioWorkletNode(this.context, 'capture-interceptor');
|
|
886
|
+
awn.onprocessorerror = (ev) => {
|
|
887
|
+
let msg = 'Unknwon error';
|
|
888
|
+
if (ev instanceof ErrorEvent) {
|
|
889
|
+
msg = ev.message;
|
|
890
|
+
}
|
|
891
|
+
console.error("Capture audio worklet error: " + msg);
|
|
892
|
+
if (this.listener) {
|
|
893
|
+
this.listener.error(msg);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
let awnPt = awn.port;
|
|
897
|
+
if (awnPt) {
|
|
898
|
+
awnPt.onmessage = (ev) => {
|
|
899
|
+
if (this.capturing) {
|
|
900
|
+
let dt = ev.data;
|
|
901
|
+
let chs = dt.chs;
|
|
902
|
+
let adaLen = dt.data.length;
|
|
903
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
904
|
+
console.debug('Received data from worklet: ' + chs + ' ' + dt.len + ' Data chs: ' + adaLen);
|
|
905
|
+
}
|
|
906
|
+
//let chunkLen = adaLen / chs;
|
|
907
|
+
let chunkLen = adaLen;
|
|
908
|
+
let chunk = new Array(chs);
|
|
909
|
+
for (let ch = 0; ch < chs; ch++) {
|
|
910
|
+
if (this.data && this.data[ch]) {
|
|
911
|
+
let adaPos = ch * chunkLen;
|
|
912
|
+
if (dt.data[ch]) {
|
|
913
|
+
let fa = new Float32Array(dt.data[ch]);
|
|
914
|
+
this.data[ch].push(fa);
|
|
915
|
+
chunk[ch] = fa;
|
|
916
|
+
// Use samples of channel 0 to count frames (samples)
|
|
917
|
+
if (ch == 0) {
|
|
918
|
+
this.framesRecorded += fa.length;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
923
|
+
console.debug('Channel ' + ch + ' data not set!!');
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (this.audioOutStream) {
|
|
929
|
+
this.audioOutStream.write(chunk);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
this.bufferingNode = awn;
|
|
935
|
+
this._opened = true;
|
|
936
|
+
if (this.listener) {
|
|
937
|
+
this.listener.opened();
|
|
938
|
+
}
|
|
939
|
+
}).catch((error) => {
|
|
940
|
+
console.log('Could not add module ' + error);
|
|
941
|
+
});
|
|
557
942
|
}
|
|
558
|
-
else {
|
|
559
|
-
// The ScriptProcessorNode Interface - DEPRECATED
|
|
560
|
-
//console.debug("Audio script processor implemented!!");
|
|
943
|
+
else if (this.context.createScriptProcessor) {
|
|
944
|
+
// The ScriptProcessorNode Interface - DEPRECATED Only as fallback
|
|
561
945
|
// TODO should we use streamChannelCount or channelCount here ?
|
|
562
|
-
|
|
946
|
+
let scriptProcessorNode = this.context.createScriptProcessor(AudioCapture.BUFFER_SIZE, streamChannelCount, streamChannelCount);
|
|
947
|
+
this.bufferingNode = scriptProcessorNode;
|
|
563
948
|
let c = 0;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
949
|
+
if (scriptProcessorNode.onaudioprocess) {
|
|
950
|
+
scriptProcessorNode.onaudioprocess = (e) => {
|
|
951
|
+
if (this.capturing) {
|
|
952
|
+
let inBuffer = e.inputBuffer;
|
|
953
|
+
let duration = inBuffer.duration;
|
|
954
|
+
// only process requested count of channels
|
|
955
|
+
let currentBuffers = new Array(channelCount);
|
|
956
|
+
for (let ch = 0; ch < channelCount; ch++) {
|
|
957
|
+
let chSamples = inBuffer.getChannelData(ch);
|
|
958
|
+
let chSamplesCopy = chSamples.slice(0);
|
|
959
|
+
currentBuffers[ch] = chSamplesCopy.slice(0);
|
|
960
|
+
this.data[ch].push(chSamplesCopy);
|
|
961
|
+
if (DEBUG_TRACE_LEVEL > 8) {
|
|
962
|
+
console.debug("Process " + chSamplesCopy.length + " samples.");
|
|
963
|
+
}
|
|
964
|
+
this.framesRecorded += chSamplesCopy.length;
|
|
965
|
+
}
|
|
966
|
+
c++;
|
|
967
|
+
if (this.audioOutStream) {
|
|
968
|
+
this.audioOutStream.write(currentBuffers);
|
|
969
|
+
}
|
|
581
970
|
}
|
|
971
|
+
};
|
|
972
|
+
this._opened = true;
|
|
973
|
+
if (this.listener) {
|
|
974
|
+
this.listener.opened();
|
|
582
975
|
}
|
|
583
|
-
}
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
this.listener.error('Browser does not support audio processing (ScriptProcessor.onaudioprocess method not found)!');
|
|
979
|
+
}
|
|
584
980
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
this.listener.opened();
|
|
981
|
+
else {
|
|
982
|
+
this.listener.error('Browser does not support audio processing (neither AudioWorkletProcessor nor ScriptProcessor)!');
|
|
588
983
|
}
|
|
589
984
|
}, (e) => {
|
|
590
985
|
console.error(e + " Error name: " + e.name);
|
|
@@ -611,14 +1006,16 @@ class AudioCapture {
|
|
|
611
1006
|
this.audioOutStream.nextStream();
|
|
612
1007
|
}
|
|
613
1008
|
this.capturing = true;
|
|
614
|
-
|
|
615
|
-
|
|
1009
|
+
if (this.bufferingNode) {
|
|
1010
|
+
this.mediaStream.connect(this.bufferingNode);
|
|
1011
|
+
this.bufferingNode.connect(this.context.destination);
|
|
1012
|
+
}
|
|
616
1013
|
if (this.listener) {
|
|
617
1014
|
this.listener.started();
|
|
618
1015
|
}
|
|
619
1016
|
}
|
|
620
1017
|
stop() {
|
|
621
|
-
if (this.disconnectStreams) {
|
|
1018
|
+
if (this.disconnectStreams && this.bufferingNode) {
|
|
622
1019
|
this.mediaStream.disconnect(this.bufferingNode);
|
|
623
1020
|
this.bufferingNode.disconnect(this.context.destination);
|
|
624
1021
|
}
|
|
@@ -1788,6 +2185,8 @@ class LevelBarDisplay {
|
|
|
1788
2185
|
this.peakDbLevelStr = "-___ dB";
|
|
1789
2186
|
this.peakDbLvl = MIN_DB_LEVEL;
|
|
1790
2187
|
this._displayLevelInfos = null;
|
|
2188
|
+
this._agc = undefined;
|
|
2189
|
+
this.agcString = 'n/a';
|
|
1791
2190
|
this.onShowRecordingDetails = new EventEmitter();
|
|
1792
2191
|
this.onDownloadRecording = new EventEmitter();
|
|
1793
2192
|
this.playStartEnabled = false;
|
|
@@ -1796,6 +2195,20 @@ class LevelBarDisplay {
|
|
|
1796
2195
|
this.destroyed = false;
|
|
1797
2196
|
this.warnDbLevel = DEFAULT_WARN_DB_LEVEL;
|
|
1798
2197
|
}
|
|
2198
|
+
set agc(agc) {
|
|
2199
|
+
this._agc = agc;
|
|
2200
|
+
if (this._agc === undefined || this._agc === null) {
|
|
2201
|
+
this.agcString = 'n/a';
|
|
2202
|
+
}
|
|
2203
|
+
else {
|
|
2204
|
+
if (this._agc === true) {
|
|
2205
|
+
this.agcString = 'On';
|
|
2206
|
+
}
|
|
2207
|
+
else {
|
|
2208
|
+
this.agcString = 'Off';
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
1799
2212
|
ngOnDestroy() {
|
|
1800
2213
|
this.destroyed = true;
|
|
1801
2214
|
}
|
|
@@ -1845,7 +2258,7 @@ class LevelBarDisplay {
|
|
|
1845
2258
|
}
|
|
1846
2259
|
}
|
|
1847
2260
|
LevelBarDisplay.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.10", ngImport: i0, type: LevelBarDisplay, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1848
|
-
LevelBarDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.10", type: LevelBarDisplay, selector: "spr-recordingitemdisplay", inputs: { streamingMode: "streamingMode", audioSignalCollapsed: "audioSignalCollapsed", enableDownload: "enableDownload", playStartAction: "playStartAction", playStopAction: "playStopAction", displayAudioBuffer: "displayAudioBuffer", displayLevelInfos: "displayLevelInfos" }, outputs: { onShowRecordingDetails: "onShowRecordingDetails", onDownloadRecording: "onDownloadRecording" }, viewQueries: [{ propertyName: "liveLevel", first: true, predicate: LevelBar, descendants: true, static: true }], ngImport: i0, template: `
|
|
2261
|
+
LevelBarDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.10", type: LevelBarDisplay, selector: "spr-recordingitemdisplay", inputs: { streamingMode: "streamingMode", audioSignalCollapsed: "audioSignalCollapsed", enableDownload: "enableDownload", agc: "agc", playStartAction: "playStartAction", playStopAction: "playStopAction", displayAudioBuffer: "displayAudioBuffer", displayLevelInfos: "displayLevelInfos" }, outputs: { onShowRecordingDetails: "onShowRecordingDetails", onDownloadRecording: "onDownloadRecording" }, viewQueries: [{ propertyName: "liveLevel", first: true, predicate: LevelBar, descendants: true, static: true }], ngImport: i0, template: `
|
|
1849
2262
|
<audio-levelbar [streamingMode]="streamingMode" [displayLevelInfos]="_displayLevelInfos"></audio-levelbar>
|
|
1850
2263
|
<button matTooltip="Start playback" (click)="playStartAction?.perform()"
|
|
1851
2264
|
[disabled]="playStartAction?.disabled"
|
|
@@ -1866,7 +2279,8 @@ LevelBarDisplay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", versio
|
|
|
1866
2279
|
<mat-icon>file_download</mat-icon>
|
|
1867
2280
|
</button>
|
|
1868
2281
|
<div style="min-width: 14ch;padding:2px"><table border="0"><tr><td>Peak:</td><td><span matTooltip="Peak level"
|
|
1869
|
-
[style.color]="(peakDbLvl > warnDbLevel)?'red':'black'">{{peakDbLvl | number:'1.1-1'}} dB </span></td></tr
|
|
2282
|
+
[style.color]="(peakDbLvl > warnDbLevel)?'red':'black'">{{peakDbLvl | number:'1.1-1'}} dB </span></td></tr>
|
|
2283
|
+
<tr *ngIf="_agc"><td>AGC:</td><td><span matTooltip="Auto gain control">{{agcString}}</span></td></tr></table></div>
|
|
1870
2284
|
`, isInline: true, styles: [":host {\n flex: 0; /* only required vertical space */\n width: 100%;\n background: darkgray;\n padding: 4px;\n box-sizing: border-box;\n height: 100px;\n min-height: 100px;\n display: flex; /* flex container: left level bar, right decimal peak level value */\n flex-direction: row;\n flex-wrap: nowrap; /* wrap could completely destroy the layout */\n }", "audio-levelbar {\n flex: 1;\n box-sizing: border-box;\n }", "span {\n flex: 0;\n font-weight: bold;\n display: inline-block;\n white-space: nowrap;\n box-sizing: border-box;\n }"], components: [{ type: LevelBar, selector: "audio-levelbar", inputs: ["streamingMode", "displayLevelInfos"] }, { type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], directives: [{ type: i3.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "number": i4.DecimalPipe } });
|
|
1871
2285
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImport: i0, type: LevelBarDisplay, decorators: [{
|
|
1872
2286
|
type: Component,
|
|
@@ -1893,7 +2307,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
1893
2307
|
<mat-icon>file_download</mat-icon>
|
|
1894
2308
|
</button>
|
|
1895
2309
|
<div style="min-width: 14ch;padding:2px"><table border="0"><tr><td>Peak:</td><td><span matTooltip="Peak level"
|
|
1896
|
-
[style.color]="(peakDbLvl > warnDbLevel)?'red':'black'">{{peakDbLvl | number:'1.1-1'}} dB </span></td></tr
|
|
2310
|
+
[style.color]="(peakDbLvl > warnDbLevel)?'red':'black'">{{peakDbLvl | number:'1.1-1'}} dB </span></td></tr>
|
|
2311
|
+
<tr *ngIf="_agc"><td>AGC:</td><td><span matTooltip="Auto gain control">{{agcString}}</span></td></tr></table></div>
|
|
1897
2312
|
`,
|
|
1898
2313
|
styles: [`:host {
|
|
1899
2314
|
flex: 0; /* only required vertical space */
|
|
@@ -1926,6 +2341,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
1926
2341
|
type: Input
|
|
1927
2342
|
}], enableDownload: [{
|
|
1928
2343
|
type: Input
|
|
2344
|
+
}], agc: [{
|
|
2345
|
+
type: Input
|
|
1929
2346
|
}], onShowRecordingDetails: [{
|
|
1930
2347
|
type: Output
|
|
1931
2348
|
}], onDownloadRecording: [{
|
|
@@ -6001,12 +6418,13 @@ class Float32ArrayChunkerOutStream {
|
|
|
6001
6418
|
toFill = avail;
|
|
6002
6419
|
}
|
|
6003
6420
|
let sliceEnd = copied + toFill;
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6421
|
+
// Firefox on Android sends only the first channel
|
|
6422
|
+
for (let ch = 0; ch < buffersLen; ch++) {
|
|
6423
|
+
if (buffers[ch]) {
|
|
6424
|
+
let cpPrt = buffers[ch].slice(copied, sliceEnd);
|
|
6425
|
+
let buf = this.bufs[ch];
|
|
6426
|
+
buf.set(cpPrt, this.filled);
|
|
6427
|
+
}
|
|
6010
6428
|
}
|
|
6011
6429
|
copied += toFill;
|
|
6012
6430
|
avail -= toFill;
|
|
@@ -6790,6 +7208,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
6790
7208
|
type: Input
|
|
6791
7209
|
}] } });
|
|
6792
7210
|
|
|
7211
|
+
const FORCE_REQUEST_AUDIO_PERMISSIONS = false;
|
|
6793
7212
|
const RECFILE_API_CTX = 'recfile';
|
|
6794
7213
|
const MAX_RECORDING_TIME_MS = 1000 * 60 * 60 * 60; // 1 hour
|
|
6795
7214
|
const DEFAULT_PRE_REC_DELAY = 1000;
|
|
@@ -6841,6 +7260,9 @@ class SessionManager {
|
|
|
6841
7260
|
this.selCaptureDeviceId = null;
|
|
6842
7261
|
this.levelMeasure = new LevelMeasure();
|
|
6843
7262
|
this.streamLevelMeasure = new StreamLevelMeasure();
|
|
7263
|
+
this.userAgent = UserAgentBuilder.userAgent();
|
|
7264
|
+
console.debug("Detected platform: " + this.userAgent.detectedPlatform);
|
|
7265
|
+
console.debug("Detected browser: " + this.userAgent.detectedBrowser);
|
|
6844
7266
|
if (this.config && this.config.enableUploadRecordings !== undefined) {
|
|
6845
7267
|
this.enableUploadRecordings = this.config.enableUploadRecordings;
|
|
6846
7268
|
}
|
|
@@ -7004,6 +7426,9 @@ class SessionManager {
|
|
|
7004
7426
|
set audioDevices(audioDevices) {
|
|
7005
7427
|
this._audioDevices = audioDevices;
|
|
7006
7428
|
}
|
|
7429
|
+
set autoGainControlConfigs(autoGainControlConfigs) {
|
|
7430
|
+
this._autoGainControlConfigs = autoGainControlConfigs;
|
|
7431
|
+
}
|
|
7007
7432
|
update(e) {
|
|
7008
7433
|
if (e.type == EventType.STARTED) {
|
|
7009
7434
|
this.playStartAction.disabled = true;
|
|
@@ -7082,7 +7507,7 @@ class SessionManager {
|
|
|
7082
7507
|
else {
|
|
7083
7508
|
console.log("Open session with default audio device for " + this._channelCount + " channels");
|
|
7084
7509
|
}
|
|
7085
|
-
this.ac.open(this._channelCount, this._selectedDeviceId);
|
|
7510
|
+
this.ac.open(this._channelCount, this._selectedDeviceId, this._autoGainControlConfigs);
|
|
7086
7511
|
}
|
|
7087
7512
|
else {
|
|
7088
7513
|
this.ac.start();
|
|
@@ -7325,9 +7750,22 @@ class SessionManager {
|
|
|
7325
7750
|
this.sessionService.patchSessionObserver(this._session, body).subscribe();
|
|
7326
7751
|
}
|
|
7327
7752
|
}
|
|
7753
|
+
// Check browser compatibility
|
|
7754
|
+
if (this.userAgent.detectedBrowser === Browser$1.Safari && this._channelCount > 1) {
|
|
7755
|
+
let eMsg = "Error: Safari browser does not support stereo recordings.";
|
|
7756
|
+
console.error(eMsg);
|
|
7757
|
+
this.dialog.open(MessageDialog, {
|
|
7758
|
+
data: {
|
|
7759
|
+
type: 'error',
|
|
7760
|
+
title: 'Browser not supported',
|
|
7761
|
+
msg: eMsg,
|
|
7762
|
+
advice: "Please use a supported browser, e.g. Mozilla Firefox."
|
|
7763
|
+
}
|
|
7764
|
+
});
|
|
7765
|
+
}
|
|
7328
7766
|
//console.log("Session ID: "+this._session.session+ " status: "+this._session.status)
|
|
7329
7767
|
this._selectedDeviceId = undefined;
|
|
7330
|
-
if (!this.readonly && this.ac) {
|
|
7768
|
+
if (!this.readonly && this.ac && (FORCE_REQUEST_AUDIO_PERMISSIONS || (this._audioDevices && this._audioDevices.length > 0))) {
|
|
7331
7769
|
this.statusMsg = 'Requesting audio permissions...';
|
|
7332
7770
|
this.statusAlertType = 'info';
|
|
7333
7771
|
this.ac.deviceInfos((mdis) => {
|
|
@@ -7444,6 +7882,7 @@ class SessionManager {
|
|
|
7444
7882
|
}
|
|
7445
7883
|
});
|
|
7446
7884
|
}
|
|
7885
|
+
// Safari does not list playback devices
|
|
7447
7886
|
if (!audioPlayDeviceAvail) {
|
|
7448
7887
|
// Firefox does not enumerate audiooutput devices
|
|
7449
7888
|
// Do not show this warning, because it would always appear on Firefox
|
|
@@ -7451,7 +7890,8 @@ class SessionManager {
|
|
|
7451
7890
|
// It is already implemneted but kept behind a preference setting https://bugzilla.mozilla.org/show_bug.cgi?id=1152401
|
|
7452
7891
|
// Output devices are listed if about:config media.setsinkid.enabled=true
|
|
7453
7892
|
// but default setting is false
|
|
7454
|
-
|
|
7893
|
+
// Same problem with Safari
|
|
7894
|
+
if (!(this.userAgent.detectedBrowser === Browser$1.Safari || this.userAgent.detectedBrowser === Browser$1.Firefox)) {
|
|
7455
7895
|
// no device found
|
|
7456
7896
|
this.statusMsg = 'WARNING: No audio playback device available!';
|
|
7457
7897
|
this.statusAlertType = 'warn';
|
|
@@ -7876,7 +8316,9 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
7876
8316
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
7877
8317
|
[streamingMode]="isRecording()"
|
|
7878
8318
|
[displayLevelInfos]="displayLevelInfos"
|
|
7879
|
-
[displayAudioBuffer]="displayAudioClip?.buffer"
|
|
8319
|
+
[displayAudioBuffer]="displayAudioClip?.buffer"
|
|
8320
|
+
[agc]="this.ac?.agcStatus"
|
|
8321
|
+
[audioSignalCollapsed]="audioSignalCollapsed"
|
|
7880
8322
|
(onShowRecordingDetails)="audioSignalCollapsed=!audioSignalCollapsed"
|
|
7881
8323
|
(onDownloadRecording)="downloadRecording()"
|
|
7882
8324
|
[enableDownload]="enableDownloadRecordings"></spr-recordingitemdisplay>
|
|
@@ -7885,7 +8327,7 @@ SessionManager.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version
|
|
|
7885
8327
|
[statusAlertType]="statusAlertType" [uploadProgress]="uploadProgress"
|
|
7886
8328
|
[uploadStatus]="uploadStatus" [ready]="dataSaved && !isActive()" [processing]="processingRecording" [navigationEnabled]="items==null || items.length>1"></app-sprcontrolpanel>
|
|
7887
8329
|
|
|
7888
|
-
`, isInline: true, styles: [":host {\n flex: 2;\n background: lightgrey;\n display: flex; /* Vertical flex container: Bottom transport panel, above prompting panel */\n flex-direction: column;\n margin: 0;\n padding: 0;\n min-height: 0px;\n\n /* Prevents horizontal scroll bar on swipe right */\n overflow: hidden;\n }"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "mode", "value", "bufferValue"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBarDisplay, selector: "spr-recordingitemdisplay", inputs: ["streamingMode", "audioSignalCollapsed", "enableDownload", "playStartAction", "playStopAction", "displayAudioBuffer", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: ControlPanel, selector: "app-sprcontrolpanel", inputs: ["readonly", "transportActions", "processing", "statusMsg", "statusAlertType", "statusWaiting", "uploadStatus", "uploadProgress", "currentRecording", "enableUploadRecordings", "navigationEnabled", "ready"] }], directives: [{ type: i6.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
|
|
8330
|
+
`, isInline: true, styles: [":host {\n flex: 2;\n background: lightgrey;\n display: flex; /* Vertical flex container: Bottom transport panel, above prompting panel */\n flex-direction: column;\n margin: 0;\n padding: 0;\n min-height: 0px;\n\n /* Prevents horizontal scroll bar on swipe right */\n overflow: hidden;\n }"], components: [{ type: WarningBar, selector: "app-warningbar", inputs: ["warningText", "show"] }, { type: Prompting, selector: "app-sprprompting", inputs: ["projectName", "startStopSignalState", "promptItem", "showPrompt", "items", "selectedItemIdx", "transportActions", "enableDownload", "audioSignalCollapsed", "displayAudioClip", "playStartAction", "playSelectionAction", "autoPlayOnSelectToggleAction", "playStopAction"], outputs: ["onItemSelect", "onNextItem", "onPrevItem"] }, { type: i7.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "mode", "value", "bufferValue"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { type: LevelBarDisplay, selector: "spr-recordingitemdisplay", inputs: ["streamingMode", "audioSignalCollapsed", "enableDownload", "agc", "playStartAction", "playStopAction", "displayAudioBuffer", "displayLevelInfos"], outputs: ["onShowRecordingDetails", "onDownloadRecording"] }, { type: ControlPanel, selector: "app-sprcontrolpanel", inputs: ["readonly", "transportActions", "processing", "statusMsg", "statusAlertType", "statusWaiting", "uploadStatus", "uploadProgress", "currentRecording", "enableUploadRecordings", "navigationEnabled", "ready"] }], directives: [{ type: i6.DefaultShowHideDirective, selector: " [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]", inputs: ["fxShow", "fxShow.print", "fxShow.xs", "fxShow.sm", "fxShow.md", "fxShow.lg", "fxShow.xl", "fxShow.lt-sm", "fxShow.lt-md", "fxShow.lt-lg", "fxShow.lt-xl", "fxShow.gt-xs", "fxShow.gt-sm", "fxShow.gt-md", "fxShow.gt-lg", "fxHide", "fxHide.print", "fxHide.xs", "fxHide.sm", "fxHide.md", "fxHide.lg", "fxHide.xl", "fxHide.lt-sm", "fxHide.lt-md", "fxHide.lt-lg", "fxHide.lt-xl", "fxHide.gt-xs", "fxHide.gt-sm", "fxHide.gt-md", "fxHide.gt-lg"] }] });
|
|
7889
8331
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImport: i0, type: SessionManager, decorators: [{
|
|
7890
8332
|
type: Component,
|
|
7891
8333
|
args: [{
|
|
@@ -7913,7 +8355,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
7913
8355
|
[playStopAction]="controlAudioPlayer?.stopAction"
|
|
7914
8356
|
[streamingMode]="isRecording()"
|
|
7915
8357
|
[displayLevelInfos]="displayLevelInfos"
|
|
7916
|
-
[displayAudioBuffer]="displayAudioClip?.buffer"
|
|
8358
|
+
[displayAudioBuffer]="displayAudioClip?.buffer"
|
|
8359
|
+
[agc]="this.ac?.agcStatus"
|
|
8360
|
+
[audioSignalCollapsed]="audioSignalCollapsed"
|
|
7917
8361
|
(onShowRecordingDetails)="audioSignalCollapsed=!audioSignalCollapsed"
|
|
7918
8362
|
(onDownloadRecording)="downloadRecording()"
|
|
7919
8363
|
[enableDownload]="enableDownloadRecordings"></spr-recordingitemdisplay>
|
|
@@ -7957,20 +8401,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
7957
8401
|
args: ['window:keydown', ['$event']]
|
|
7958
8402
|
}] } });
|
|
7959
8403
|
|
|
7960
|
-
class ProjectUtil {
|
|
7961
|
-
static audioChannelCount(project) {
|
|
7962
|
-
let chs = ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT;
|
|
7963
|
-
if (project.mediaCaptureFormat) {
|
|
7964
|
-
chs = project.mediaCaptureFormat.audioChannelCount;
|
|
7965
|
-
}
|
|
7966
|
-
else if (project.audioFormat) {
|
|
7967
|
-
chs = project.audioFormat.channels;
|
|
7968
|
-
}
|
|
7969
|
-
return chs;
|
|
7970
|
-
}
|
|
7971
|
-
}
|
|
7972
|
-
ProjectUtil.DEFAULT_AUDIO_CHANNEL_COUNT = 2;
|
|
7973
|
-
|
|
7974
8404
|
/**
|
|
7975
8405
|
* Created by klausj on 17.06.2017.
|
|
7976
8406
|
*/
|
|
@@ -8293,6 +8723,7 @@ class SpeechrecorderngComponent {
|
|
|
8293
8723
|
this.sm.audioDevices = project.audioDevices;
|
|
8294
8724
|
chCnt = ProjectUtil.audioChannelCount(project);
|
|
8295
8725
|
console.info("Project requested recording channel count: " + chCnt);
|
|
8726
|
+
this.sm.autoGainControlConfigs = project.autoGainControlConfigs;
|
|
8296
8727
|
}
|
|
8297
8728
|
else {
|
|
8298
8729
|
console.error("Empty project configuration!");
|
|
@@ -9711,7 +10142,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.10", ngImpo
|
|
|
9711
10142
|
}]
|
|
9712
10143
|
}] });
|
|
9713
10144
|
|
|
9714
|
-
const VERSION = '2.
|
|
10145
|
+
const VERSION = '2.19.0';
|
|
9715
10146
|
|
|
9716
10147
|
/*
|
|
9717
10148
|
* Public API Surface of speechrecorderng
|