Skip to content

Commit 97dc965

Browse files
committed
Optimize audio
1 parent 6e59f88 commit 97dc965

File tree

7 files changed

+292
-114
lines changed

7 files changed

+292
-114
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {EventEmitter} from '../eventemitter.mjs';
2+
3+
const currentScript = import.meta;
4+
let basePath = '';
5+
if (currentScript) {
6+
basePath = currentScript.url
7+
.replace(/#.*$/, '')
8+
.replace(/\?.*$/, '')
9+
.replace(/\/[^\/]+$/, '/');
10+
}
11+
12+
const assetPath = (file) => {
13+
return basePath + file;
14+
};
15+
16+
export class ChannelCounterNode extends EventEmitter {
17+
constructor(ctx) {
18+
super();
19+
this.ctx = ctx;
20+
}
21+
22+
async init() {
23+
await this.ctx.audioWorklet.addModule(assetPath('channelcounter.worklet.mjs'));
24+
const counterNode = new AudioWorkletNode(this.ctx, 'channelcounter-worklet');
25+
this.counterNode = counterNode;
26+
27+
counterNode.port.onmessage = (ev) => {
28+
this.emit('channelcount', ev.data);
29+
};
30+
return this;
31+
}
32+
33+
getNode() {
34+
return this.counterNode;
35+
}
36+
37+
destroy() {
38+
if (!this.counterNode) {
39+
return;
40+
}
41+
this.getNode().port.postMessage({type: 'close'});
42+
this.counterNode.disconnect();
43+
this.counterNode = undefined;
44+
}
45+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
registerProcessor('channelcounter-worklet', class ChannelCounter extends AudioWorkletProcessor {
2+
constructor() {
3+
super();
4+
this.inputChannelCount = 0;
5+
this._closed = false;
6+
7+
this.port.onmessage = (event) => {
8+
if (event.data.type === 'close') {
9+
this.close();
10+
} else if (event.data.type === 'configure') {
11+
this.configure(event.data.options);
12+
}
13+
};
14+
}
15+
process(input, output, parameters) {
16+
if (this._closed) {
17+
return false;
18+
}
19+
if (input && input[0].length != this.inputChannelCount) {
20+
this.inputChannelCount = input[0].length;
21+
this.port.postMessage(input[0].length);
22+
}
23+
return true;
24+
}
25+
26+
close() {
27+
console.debug('closing counter worklet');
28+
this._closed = true;
29+
}
30+
});

chrome/player/ui/audio/AudioChannelMixer.mjs

Lines changed: 113 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import {VirtualAudioNode} from './VirtualAudioNode.mjs';
1212
const CHANNEL_NAMES = ['Left', 'Right', 'Center', 'Bass (LFE)', 'Left Surround', 'Right Surround'];
1313

1414
export class AudioChannelMixer extends AbstractAudioModule {
15-
constructor() {
15+
constructor(configManager) {
1616
super('AudioChannelMixer');
1717

18+
this.configManager = configManager;
19+
1820
this.channelConfigs = null;
1921
this.masterConfig = null;
2022

@@ -27,8 +29,8 @@ export class AudioChannelMixer extends AbstractAudioModule {
2729
this.masterElements = null;
2830
}
2931

30-
needsUpscaler() {
31-
return this.channelSplitter !== null;
32+
async getChannelCount() {
33+
return this.configManager.getChannelCount();
3234
}
3335

3436
getElement() {
@@ -468,8 +470,6 @@ export class AudioChannelMixer extends AbstractAudioModule {
468470
return;
469471
}
470472

471-
this.updateNodes();
472-
473473
this.channelNodes.forEach((nodes) => {
474474
const analyser = this.audioContext.createAnalyser();
475475
analyser.fftSize = 256;
@@ -481,6 +481,7 @@ export class AudioChannelMixer extends AbstractAudioModule {
481481
masterAnalyzer.fftSize = 256;
482482
this.getOutputNode().connect(masterAnalyzer);
483483
this.masterNodes.analyzer = masterAnalyzer;
484+
this.updateNodes();
484485
}
485486

486487
destroyAnalyzers(skipDisconnect = false) {
@@ -504,7 +505,6 @@ export class AudioChannelMixer extends AbstractAudioModule {
504505
this.masterNodes.analyzer.disconnect();
505506
}
506507
this.masterNodes.analyzer = null;
507-
508508
this.updateNodes();
509509
}
510510

@@ -539,7 +539,7 @@ export class AudioChannelMixer extends AbstractAudioModule {
539539
preGain: new VirtualAudioNode(`AudioChannelMixer preGain ${i}`),
540540
preMerge: new VirtualAudioNode(`AudioChannelMixer preMerge ${i}`),
541541
equalizer: new AudioEqualizer(`${CHANNEL_NAMES[i]} `),
542-
compressor: new AudioCompressor(true, `${CHANNEL_NAMES[i]} `),
542+
compressor: new AudioCompressor(`${CHANNEL_NAMES[i]} `),
543543
};
544544

545545
nodes.compressor.setupNodes(audioContext);
@@ -554,6 +554,7 @@ export class AudioChannelMixer extends AbstractAudioModule {
554554
nodes.postSplit.connect(nodes.equalizer.getInputNode());
555555
nodes.equalizer.getOutputNode().connect(nodes.compressor.getInputNode());
556556
nodes.compressor.getOutputNode().connect(nodes.preGain);
557+
nodes.preGain.connect(nodes.preMerge);
557558

558559
let oldEqualizerState = nodes.equalizer.hasNodes();
559560
let oldCompressorState = nodes.compressor.isEnabled();
@@ -584,7 +585,7 @@ export class AudioChannelMixer extends AbstractAudioModule {
584585
analyzer: null,
585586
postMerge: new VirtualAudioNode('AudioChannelMixer postMerge master'),
586587
equalizer: new AudioEqualizer('Master '),
587-
compressor: new AudioCompressor(false, 'Master '),
588+
compressor: new AudioCompressor('Master ', this.getChannelCount.bind(this)),
588589
preGain: new VirtualAudioNode(`AudioChannelMixer preGain master`),
589590
};
590591
this.masterNodes.compressor.setupNodes(audioContext);
@@ -624,18 +625,39 @@ export class AudioChannelMixer extends AbstractAudioModule {
624625
els.dynButton.classList.toggle('configured', isEqActive || isCompActive);
625626
}
626627

627-
updateNodes() {
628+
async updateNodes() {
628629
if (!this.audioContext) return;
629630

630631
const gains = this.getChannelGainsFromConfig();
631632
if (!gains) {
632633
return;
633634
}
634635

636+
const numberOfChannels = await this.getChannelCount().catch(() => 0);
637+
if (numberOfChannels === 0) {
638+
return;
639+
}
640+
635641
const hasNonUnityMasterGain = this.masterConfig.gain !== 1;
636642
const isMono = this.masterConfig.mono;
643+
const needsMasterGain = hasNonUnityMasterGain || isMono;
644+
const needsAnalyzer = this.needsAnalyzer();
645+
646+
647+
const activeChannels = AudioUtils.getActiveChannelsForChannelCount(Math.min(numberOfChannels, 6));
648+
if (numberOfChannels === 1) {
649+
activeChannels.push(1); // mono sources are always stereo internally
650+
}
637651

638-
if (hasNonUnityMasterGain || isMono) {
652+
const hasNonUnityChannelGains = activeChannels.some((i) => gains[i] !== 1);
653+
const hasActiveNodes = activeChannels.some((i) => {
654+
const nodes = this.channelNodes[i];
655+
return nodes.equalizer.hasNodes() || nodes.compressor.isEnabled();
656+
});
657+
658+
const needsMerger = numberOfChannels > 6 || hasNonUnityChannelGains || hasActiveNodes || needsAnalyzer;
659+
const needsSplitter = needsMerger; // numberOfChannels > 1 && needsMerger;
660+
if (needsMasterGain) {
639661
if (!this.masterNodes.gain) {
640662
this.masterNodes.gain = this.audioContext.createGain();
641663
this.masterNodes.preGain.disconnect(this.getOutputNode());
@@ -659,73 +681,92 @@ export class AudioChannelMixer extends AbstractAudioModule {
659681
}
660682
}
661683

662-
const hasNonUnityChannelGains = gains.some((gain) => gain !== 1);
663-
const hasActiveNodes = this.channelNodes.some((nodes) => {
664-
return nodes.equalizer.hasNodes() || nodes.compressor.isEnabled();
665-
});
666-
const needsAnalyzer = this.needsAnalyzer();
667-
if (hasActiveNodes || hasNonUnityChannelGains || needsAnalyzer) {
668-
if (!this.channelSplitter) {
669-
this.channelSplitter = this.audioContext.createChannelSplitter();
670-
this.channelMerger = this.audioContext.createChannelMerger();
671-
this.getInputNode().disconnect(this.masterNodes.postMerge);
672-
this.getInputNode().connect(this.channelSplitter);
673-
this.masterNodes.postMerge.connectFrom(this.channelMerger);
674-
675-
this.channelNodes.forEach((nodes, i) => {
676-
nodes.postSplit.connectFrom(this.channelSplitter, i, 0);
677-
nodes.preGain.connect(nodes.preMerge);
678-
nodes.preMerge.connect(this.channelMerger, 0, i);
679-
});
684+
const shouldDestroySplitter = this.channelSplitter && (!needsSplitter || activeChannels.length !== this.channelSplitter.numberOfOutputs);
685+
const shouldDestroyMerger = this.channelMerger && (!needsMerger || activeChannels.length !== this.channelMerger.numberOfInputs);
686+
687+
// if (!this.channelSplitter && this.channelMerger && (needsSplitter || shouldDestroyMerger)) {
688+
// const mergerActiveChannels = AudioUtils.getActiveChannelsForChannelCount(this.channelMerger.numberOfInputs);
689+
// mergerActiveChannels.forEach((idx, i) => {
690+
// const nodes = this.channelNodes[idx];
691+
// nodes.postSplit.disconnectFrom(this.getInputNode(), 0, 0);
692+
// });
693+
// }
694+
695+
if (shouldDestroySplitter) {
696+
const splitterActiveChannels = AudioUtils.getActiveChannelsForChannelCount(this.channelSplitter.numberOfOutputs);
697+
splitterActiveChannels.forEach((idx, i) => {
698+
const nodes = this.channelNodes[idx];
699+
nodes.postSplit.disconnectFrom(this.channelSplitter, i, 0);
700+
});
680701

681-
this.emit('upscale');
682-
}
702+
this.channelSplitter.disconnect();
703+
this.channelSplitter = null;
704+
}
683705

684-
this.channelNodes.forEach((nodes, i) => {
685-
const gain = gains[i];
686-
if (gain === 1) {
687-
if (nodes.gain) {
688-
nodes.preGain.disconnect(nodes.gain);
689-
nodes.preMerge.disconnectFrom(nodes.gain);
690-
nodes.preGain.connect(nodes.preMerge);
691-
nodes.gain = null;
692-
}
693-
} else {
694-
if (!nodes.gain) {
695-
nodes.gain = this.audioContext.createGain();
696-
nodes.preGain.disconnect(nodes.preMerge);
697-
nodes.preGain.connect(nodes.gain);
698-
nodes.preMerge.connectFrom(nodes.gain);
699-
}
700-
701-
nodes.gain.gain.value = gains[i];
702-
}
706+
if (shouldDestroyMerger) {
707+
const mergerActiveChannels = AudioUtils.getActiveChannelsForChannelCount(this.channelMerger.numberOfInputs);
708+
mergerActiveChannels.forEach((idx, i) => {
709+
const nodes = this.channelNodes[idx];
710+
nodes.preMerge.disconnect(this.channelMerger, 0, i);
703711
});
704-
} else {
705-
if (this.channelSplitter) {
706-
this.getInputNode().disconnect(this.channelSplitter);
707-
this.masterNodes.postMerge.disconnectFrom(this.channelMerger);
708-
this.getInputNode().connect(this.masterNodes.postMerge);
709-
710-
this.channelNodes.forEach((nodes, i) => {
711-
nodes.postSplit.disconnectFrom(this.channelSplitter, i, 0);
712-
if (nodes.gain) {
713-
nodes.preGain.disconnect(nodes.gain);
714-
nodes.preMerge.disconnectFrom(nodes.gain);
715-
nodes.gain = null;
716-
} else {
717-
nodes.preGain.disconnect(nodes.preMerge);
718-
}
719-
nodes.preMerge.disconnect(this.channelMerger, 0, i);
720-
});
721712

722-
this.channelSplitter.disconnect();
723-
this.channelMerger.disconnect();
713+
this.masterNodes.postMerge.disconnectFrom(this.channelMerger);
714+
this.getInputNode().connect(this.masterNodes.postMerge);
715+
this.channelMerger.disconnect();
716+
this.channelMerger = null;
717+
}
724718

725-
this.channelSplitter = null;
726-
this.channelMerger = null;
727-
this.emit('upscale');
728-
}
719+
const shouldCreateSplitter = !this.channelSplitter && needsSplitter;
720+
const shouldCreateMerger = !this.channelMerger && needsMerger;
721+
722+
if (shouldCreateSplitter) {
723+
this.channelSplitter = this.audioContext.createChannelSplitter(activeChannels.length);
724+
this.getInputNode().disconnect(this.masterNodes.postMerge);
725+
this.getInputNode().connect(this.channelSplitter);
726+
727+
activeChannels.forEach((idx, i) => {
728+
const nodes = this.channelNodes[idx];
729+
nodes.postSplit.connectFrom(this.channelSplitter, i, 0);
730+
});
731+
}
732+
733+
if (shouldCreateMerger) {
734+
this.channelMerger = this.audioContext.createChannelMerger(activeChannels.length);
735+
this.masterNodes.postMerge.connectFrom(this.channelMerger);
736+
737+
activeChannels.forEach((idx, i) => {
738+
const nodes = this.channelNodes[idx];
739+
nodes.preMerge.connect(this.channelMerger, 0, i);
740+
});
729741
}
742+
743+
// if (!needsSplitter && needsMerger && (shouldCreateMerger || shouldDestroySplitter)) {
744+
// activeChannels.forEach((idx, i) => {
745+
// const nodes = this.channelNodes[idx];
746+
// nodes.postSplit.connectFrom(this.getInputNode(), 0, 0);
747+
// });
748+
// }
749+
750+
this.channelNodes.forEach((nodes, i) => {
751+
const gain = gains[i];
752+
const neededGainNode = gain !== 1 && needsMerger && activeChannels.includes(i);
753+
if (!neededGainNode) {
754+
if (nodes.gain) {
755+
nodes.preGain.disconnect(nodes.gain);
756+
nodes.preMerge.disconnectFrom(nodes.gain);
757+
nodes.preGain.connect(nodes.preMerge);
758+
nodes.gain = null;
759+
}
760+
} else {
761+
if (!nodes.gain) {
762+
nodes.gain = this.audioContext.createGain();
763+
nodes.preGain.disconnect(nodes.preMerge);
764+
nodes.preGain.connect(nodes.gain);
765+
nodes.preMerge.connectFrom(nodes.gain);
766+
}
767+
768+
nodes.gain.gain.value = gain;
769+
}
770+
});
730771
}
731772
}

0 commit comments

Comments
 (0)