add opus server-side integration

This commit is contained in:
Jakob Ketterl 2022-06-18 20:23:50 +02:00
parent 26440d4e24
commit a6f2b6b31a
4 changed files with 51 additions and 22 deletions

View File

@ -1,5 +1,5 @@
from csdr.chain import Chain from csdr.chain import Chain
from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, Limit from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, OpusEncoder, Limit
from pycsdr.types import Format from pycsdr.types import Format
@ -27,6 +27,8 @@ class ClientAudioChain(Chain):
workers += [converter] workers += [converter]
if compression == "adpcm": if compression == "adpcm":
workers += [AdpcmEncoder(sync=True)] workers += [AdpcmEncoder(sync=True)]
elif compression == "opus":
workers += [OpusEncoder()]
super().__init__(workers) super().__init__(workers)
def _buildConverter(self): def _buildConverter(self):
@ -63,10 +65,18 @@ class ClientAudioChain(Chain):
self._updateConverter() self._updateConverter()
def setAudioCompression(self, compression: str) -> None: def setAudioCompression(self, compression: str) -> None:
index = self.indexOf(lambda x: isinstance(x, AdpcmEncoder)) index = self.indexOf(lambda x: isinstance(x, AdpcmEncoder) or isinstance(x, OpusEncoder))
newEncoder = None
if compression == "adpcm": if compression == "adpcm":
newEncoder = AdpcmEncoder(sync=True)
elif compression == "opus":
newEncoder = OpusEncoder()
if newEncoder:
if index < 0: if index < 0:
self.append(AdpcmEncoder(sync=True)) self.append(newEncoder)
else:
self.replace(index, newEncoder)
else: else:
if index >= 0: if index >= 0:
self.remove(index) self.remove(index)

View File

@ -21,7 +21,11 @@ function AudioEngine(maxBufferLength, audioReporter) {
me._start(); me._start();
} }
this.audioCodec = new ImaAdpcmCodec(); this.audioCodecs = {
"adpcm": new ImaAdpcmCodec(),
"opus": new OpusCodec(this.audioContext)
};
this.compression = 'none'; this.compression = 'none';
this.setupResampling(); this.setupResampling();
@ -277,25 +281,31 @@ AudioEngine.prototype.getSampleRate = function() {
}; };
AudioEngine.prototype.processAudio = function(data, resampler) { AudioEngine.prototype.processAudio = function(data, resampler) {
var me = this;
if (!this.audioNode) return; if (!this.audioNode) return;
this.audioBytes.add(data.byteLength); this.audioBytes.add(data.byteLength);
var buffer;
if (this.compression === "adpcm") { var audioDataCallback = function(buffer) {
//resampling & ADPCM
buffer = this.audioCodec.decodeWithSync(new Uint8Array(data));
} else {
buffer = new Int16Array(data);
}
buffer = resampler.process(buffer); buffer = resampler.process(buffer);
if (this.audioNode.port) { if (me.audioNode.port) {
// AudioWorklets supported // AudioWorklets supported
this.audioNode.port.postMessage(buffer); me.audioNode.port.postMessage(buffer);
} else { } else {
// silently drop excess samples // silently drop excess samples
if (this.getBuffersize() + buffer.length <= this.maxBufferSize) { if (this.getBuffersize() + buffer.length <= this.maxBufferSize) {
this.audioBuffers.push(buffer); this.audioBuffers.push(buffer);
} }
} }
}
if (this.compression !== "none") {
//resampling & ADPCM
this.audioCodecs[this.compression].decodeAsync(new Uint8Array(data), audioDataCallback);
} else {
audioDataCallback(new Int16Array(data))
}
} }
AudioEngine.prototype.pushAudio = function(data) { AudioEngine.prototype.pushAudio = function(data) {
@ -357,7 +367,7 @@ ImaAdpcmCodec.prototype.decode = function(data) {
return output; return output;
}; };
ImaAdpcmCodec.prototype.decodeWithSync = function(data) { ImaAdpcmCodec.prototype.decodeAsync = function(data, callback) {
var output = new Int16Array(data.length * 2); var output = new Int16Array(data.length * 2);
var index = this.skip; var index = this.skip;
var oi = 0; var oi = 0;
@ -391,7 +401,7 @@ ImaAdpcmCodec.prototype.decodeWithSync = function(data) {
} }
} }
this.skip = index - data.length; this.skip = index - data.length;
return output.slice(0, oi); callback(output.slice(0, oi));
}; };
ImaAdpcmCodec.prototype.decodeNibble = function(nibble) { ImaAdpcmCodec.prototype.decodeNibble = function(nibble) {
@ -412,6 +422,14 @@ ImaAdpcmCodec.prototype.decodeNibble = function(nibble) {
return this.predictor; return this.predictor;
}; };
function OpusCodec(context) {
this.context = context;
}
OpusCodec.prototype.decodeAsync = function(data, callback) {
this.context.decodeAudioData(new ArrayBuffer(data)).then(callback);
};
function Interpolator(factor) { function Interpolator(factor) {
this.factor = factor; this.factor = factor;
this.lowpass = new Lowpass(factor) this.lowpass = new Lowpass(factor)

View File

@ -754,7 +754,7 @@ function on_ws_recv(evt) {
if ('audio_compression' in config) { if ('audio_compression' in config) {
var audio_compression = config['audio_compression']; var audio_compression = config['audio_compression'];
audioEngine.setCompression(audio_compression); audioEngine.setCompression(audio_compression);
divlog("Audio stream is " + ((audio_compression === "adpcm") ? "compressed" : "uncompressed") + "."); divlog("Audio stream is " + ((audio_compression !== "none") ? "compressed" : "uncompressed") + ".");
} }
if ('fft_compression' in config) { if ('fft_compression' in config) {
fft_compression = config['fft_compression']; fft_compression = config['fft_compression'];

View File

@ -132,6 +132,7 @@ class GeneralSettingsController(SettingsFormController):
"Audio compression", "Audio compression",
options=[ options=[
Option("adpcm", "ADPCM"), Option("adpcm", "ADPCM"),
Option("opus", "OPUS"),
Option("none", "None"), Option("none", "None"),
], ],
), ),