diff --git a/csdr/__init__.py b/csdr/__init__.py index 3f8cbb8..cc0ca81 100644 --- a/csdr/__init__.py +++ b/csdr/__init__.py @@ -258,7 +258,7 @@ class Dsp(DirewolfConfigSubscriber): chain += ["csdr convert_f_s16"] if self.audio_compression == "adpcm": - chain += ["csdr encode_ima_adpcm_i16_u8"] + chain += ["csdr++ adpcm -e --sync"] return chain def secondary_chain(self, which): diff --git a/csdr/chain/clientaudio.py b/csdr/chain/clientaudio.py index d331bf8..1f346f9 100644 --- a/csdr/chain/clientaudio.py +++ b/csdr/chain/clientaudio.py @@ -10,5 +10,5 @@ class ClientAudioChain(Chain): workers += [AudioResampler(inputRate, clientRate)] workers += [Convert(Format.FLOAT, Format.SHORT)] if compression == "adpcm": - workers += [AdpcmEncoder()] + workers += [AdpcmEncoder(sync=True)] super().__init__(*workers) diff --git a/htdocs/lib/AudioEngine.js b/htdocs/lib/AudioEngine.js index 8a3df67..48efb26 100644 --- a/htdocs/lib/AudioEngine.js +++ b/htdocs/lib/AudioEngine.js @@ -282,7 +282,7 @@ AudioEngine.prototype.processAudio = function(data, resampler) { var buffer; if (this.compression === "adpcm") { //resampling & ADPCM - buffer = this.audioCodec.decode(new Uint8Array(data)); + buffer = this.audioCodec.decodeWithSync(new Uint8Array(data)); } else { buffer = new Int16Array(data); } @@ -328,6 +328,10 @@ ImaAdpcmCodec.prototype.reset = function() { this.stepIndex = 0; this.predictor = 0; this.step = 0; + this.synchronized = 0; + this.syncWord = "SYNC"; + this.syncCounter = 0; + this.skip = 0; }; ImaAdpcmCodec.imaIndexTable = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 ]; @@ -353,6 +357,43 @@ ImaAdpcmCodec.prototype.decode = function(data) { return output; }; +ImaAdpcmCodec.prototype.decodeWithSync = function(data) { + var output = new Int16Array(data.length * 2); + var index = this.skip; + var oi = 0; + while (index < data.length) { + while (this.synchronized < 4 && index < data.length) { + if (data[index] === this.syncWord.charCodeAt(this.synchronized)) { + this.synchronized++; + } else { + this.synchronized = 0; + } + index++; + if (this.synchronized === 4) { + if (index + 4 < data.length) { + var syncData = new Int16Array(data.buffer.slice(index, index + 4)); + this.stepIndex = syncData[0]; + this.predictor = syncData[1]; + } + this.syncCounter = 1000; + index += 4; + break; + } + } + while (index < data.length) { + if (this.syncCounter-- < 0) { + this.synchronized = 0; + break; + } + output[oi++] = this.decodeNibble(data[index] & 0x0F); + output[oi++] = this.decodeNibble(data[index] >> 4); + index++; + } + } + this.skip = index - data.length; + return output.slice(0, oi); +}; + ImaAdpcmCodec.prototype.decodeNibble = function(nibble) { this.stepIndex += ImaAdpcmCodec.imaIndexTable[nibble]; this.stepIndex = Math.min(Math.max(this.stepIndex, 0), 88);