summaryrefslogtreecommitdiffstats
path: root/libfreerdp/codec/dsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/codec/dsp.c')
-rw-r--r--libfreerdp/codec/dsp.c1507
1 files changed, 1507 insertions, 0 deletions
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
new file mode 100644
index 0000000..68800e4
--- /dev/null
+++ b/libfreerdp/codec/dsp.c
@@ -0,0 +1,1507 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Digital Sound Processing
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <winpr/crt.h>
+
+#include <freerdp/types.h>
+#include <freerdp/log.h>
+#include <freerdp/codec/dsp.h>
+
+#if !defined(WITH_DSP_FFMPEG)
+#if defined(WITH_GSM)
+#include <gsm/gsm.h>
+#endif
+
+#if defined(WITH_LAME)
+#include <lame/lame.h>
+#endif
+
+#if defined(WITH_OPUS)
+#include <opus/opus.h>
+
+#define OPUS_MAX_FRAMES 5760
+#endif
+
+#if defined(WITH_FAAD2)
+#include <neaacdec.h>
+#endif
+
+#if defined(WITH_FAAC)
+#include <faac.h>
+#endif
+
+#if defined(WITH_SOXR)
+#include <soxr.h>
+#endif
+
+#else
+#include "dsp_ffmpeg.h"
+#endif
+
+#define TAG FREERDP_TAG("dsp")
+
+#if !defined(WITH_DSP_FFMPEG)
+
+typedef union
+{
+ struct
+ {
+ size_t packet_size;
+ INT16 last_sample[2];
+ INT16 last_step[2];
+ } ima;
+ struct
+ {
+ BYTE predictor[2];
+ INT32 delta[2];
+ INT32 sample1[2];
+ INT32 sample2[2];
+ } ms;
+} ADPCM;
+
+struct S_FREERDP_DSP_CONTEXT
+{
+ BOOL encoder;
+
+ ADPCM adpcm;
+ AUDIO_FORMAT format;
+
+ wStream* channelmix;
+ wStream* resample;
+ wStream* buffer;
+
+#if defined(WITH_GSM)
+ gsm gsm;
+#endif
+#if defined(WITH_LAME)
+ lame_t lame;
+ hip_t hip;
+#endif
+#if defined(WITH_OPUS)
+ OpusDecoder* opus_decoder;
+ OpusEncoder* opus_encoder;
+#endif
+#if defined(WITH_FAAD2)
+ NeAACDecHandle faad;
+ BOOL faadSetup;
+#endif
+
+#if defined(WITH_FAAC)
+ faacEncHandle faac;
+ unsigned long faacInputSamples;
+ unsigned long faacMaxOutputBytes;
+#endif
+
+#if defined(WITH_SOXR)
+ soxr_t sox;
+#endif
+};
+
+#if defined(WITH_OPUS)
+static BOOL opus_is_valid_samplerate(const AUDIO_FORMAT* format)
+{
+ WINPR_ASSERT(format);
+
+ switch (format->nSamplesPerSec)
+ {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 48000:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+#endif
+
+static INT16 read_int16(const BYTE* src)
+{
+ return (INT16)(src[0] | (src[1] << 8));
+}
+
+static BOOL freerdp_dsp_channel_mix(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ const AUDIO_FORMAT* srcFormat, const BYTE** data,
+ size_t* length)
+{
+ UINT32 bpp;
+ size_t samples;
+
+ if (!context || !data || !length)
+ return FALSE;
+
+ if (srcFormat->wFormatTag != WAVE_FORMAT_PCM)
+ return FALSE;
+
+ bpp = srcFormat->wBitsPerSample > 8 ? 2 : 1;
+ samples = size / bpp / srcFormat->nChannels;
+
+ if (context->format.nChannels == srcFormat->nChannels)
+ {
+ *data = src;
+ *length = size;
+ return TRUE;
+ }
+
+ Stream_SetPosition(context->channelmix, 0);
+
+ /* Destination has more channels than source */
+ if (context->format.nChannels > srcFormat->nChannels)
+ {
+ switch (srcFormat->nChannels)
+ {
+ case 1:
+ if (!Stream_EnsureCapacity(context->channelmix, size * 2))
+ return FALSE;
+
+ for (size_t x = 0; x < samples; x++)
+ {
+ for (size_t y = 0; y < bpp; y++)
+ Stream_Write_UINT8(context->channelmix, src[x * bpp + y]);
+
+ for (size_t y = 0; y < bpp; y++)
+ Stream_Write_UINT8(context->channelmix, src[x * bpp + y]);
+ }
+
+ Stream_SealLength(context->channelmix);
+ *data = Stream_Buffer(context->channelmix);
+ *length = Stream_Length(context->channelmix);
+ return TRUE;
+
+ case 2: /* We only support stereo, so we can not handle this case. */
+ default: /* Unsupported number of channels */
+ return FALSE;
+ }
+ }
+
+ /* Destination has less channels than source */
+ switch (srcFormat->nChannels)
+ {
+ case 2:
+ if (!Stream_EnsureCapacity(context->channelmix, size / 2))
+ return FALSE;
+
+ /* Simply drop second channel.
+ * TODO: Calculate average */
+ for (size_t x = 0; x < samples; x++)
+ {
+ for (size_t y = 0; y < bpp; y++)
+ Stream_Write_UINT8(context->channelmix, src[2 * x * bpp + y]);
+ }
+
+ Stream_SealLength(context->channelmix);
+ *data = Stream_Buffer(context->channelmix);
+ *length = Stream_Length(context->channelmix);
+ return TRUE;
+
+ case 1: /* Invalid, do we want to use a 0 channel sound? */
+ default: /* Unsupported number of channels */
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Microsoft Multimedia Standards Update
+ * http://download.microsoft.com/download/9/8/6/9863C72A-A3AA-4DDB-B1BA-CA8D17EFD2D4/RIFFNEW.pdf
+ */
+
+static BOOL freerdp_dsp_resample(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ const AUDIO_FORMAT* srcFormat, const BYTE** data, size_t* length)
+{
+#if defined(WITH_SOXR)
+ soxr_error_t error;
+ size_t idone, odone;
+ size_t sframes, rframes;
+ size_t rsize;
+ size_t sbytes, rbytes;
+ size_t dstChannels;
+ size_t srcChannels;
+ size_t srcBytesPerFrame, dstBytesPerFrame;
+#endif
+ AUDIO_FORMAT format;
+
+ if (srcFormat->wFormatTag != WAVE_FORMAT_PCM)
+ {
+ WLog_ERR(TAG, "requires %s for sample input, got %s",
+ audio_format_get_tag_string(WAVE_FORMAT_PCM),
+ audio_format_get_tag_string(srcFormat->wFormatTag));
+ return FALSE;
+ }
+
+ /* We want to ignore differences of source and destination format. */
+ format = *srcFormat;
+ format.wFormatTag = WAVE_FORMAT_UNKNOWN;
+ format.wBitsPerSample = 0;
+
+ if (audio_format_compatible(&format, &context->format))
+ {
+ *data = src;
+ *length = size;
+ return TRUE;
+ }
+
+#if defined(WITH_SOXR)
+ srcBytesPerFrame = (srcFormat->wBitsPerSample > 8) ? 2 : 1;
+ dstBytesPerFrame = (context->format.wBitsPerSample > 8) ? 2 : 1;
+ srcChannels = srcFormat->nChannels;
+ dstChannels = context->format.nChannels;
+ sbytes = srcChannels * srcBytesPerFrame;
+ sframes = size / sbytes;
+ rbytes = dstBytesPerFrame * dstChannels;
+ /* Integer rounding correct division */
+ rframes = (sframes * context->format.nSamplesPerSec + (srcFormat->nSamplesPerSec + 1) / 2) /
+ srcFormat->nSamplesPerSec;
+ rsize = rframes * rbytes;
+
+ if (!Stream_EnsureCapacity(context->resample, rsize))
+ return FALSE;
+
+ error = soxr_process(context->sox, src, sframes, &idone, Stream_Buffer(context->resample),
+ Stream_Capacity(context->resample) / rbytes, &odone);
+ Stream_SetLength(context->resample, odone * rbytes);
+ *data = Stream_Buffer(context->resample);
+ *length = Stream_Length(context->resample);
+ return (error == 0) ? TRUE : FALSE;
+#else
+ WLog_ERR(TAG, "Missing resample support, recompile -DWITH_SOXR=ON or -DWITH_DSP_FFMPEG=ON");
+ return FALSE;
+#endif
+}
+
+/**
+ * Microsoft IMA ADPCM specification:
+ *
+ * http://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM
+ * http://wiki.multimedia.cx/index.php?title=IMA_ADPCM
+ */
+
+static const INT16 ima_step_index_table[] = {
+ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8
+};
+
+static const INT16 ima_step_size_table[] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23,
+ 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80,
+ 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279,
+ 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
+ 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+ 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487,
+ 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+};
+
+static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BYTE sample)
+{
+ INT32 ss;
+ INT32 d;
+ ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ d = (ss >> 3);
+
+ if (sample & 1)
+ d += (ss >> 2);
+
+ if (sample & 2)
+ d += (ss >> 1);
+
+ if (sample & 4)
+ d += ss;
+
+ if (sample & 8)
+ d = -d;
+
+ d += adpcm->ima.last_sample[channel];
+
+ if (d < -32768)
+ d = -32768;
+ else if (d > 32767)
+ d = 32767;
+
+ adpcm->ima.last_sample[channel] = (INT16)d;
+ adpcm->ima.last_step[channel] += ima_step_index_table[sample];
+
+ if (adpcm->ima.last_step[channel] < 0)
+ adpcm->ima.last_step[channel] = 0;
+ else if (adpcm->ima.last_step[channel] > 88)
+ adpcm->ima.last_step[channel] = 88;
+
+ return (UINT16)d;
+}
+
+static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ BYTE sample;
+ UINT16 decoded;
+ size_t out_size = size * 4;
+ UINT32 channel;
+ const UINT32 block_size = context->format.nBlockAlign;
+ const UINT32 channels = context->format.nChannels;
+
+ if (!Stream_EnsureCapacity(out, out_size))
+ return FALSE;
+
+ while (size > 0)
+ {
+ if (size % block_size == 0)
+ {
+ context->adpcm.ima.last_sample[0] =
+ (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
+ context->adpcm.ima.last_step[0] = (INT16)(*(src + 2));
+ src += 4;
+ size -= 4;
+ out_size -= 16;
+
+ if (channels > 1)
+ {
+ context->adpcm.ima.last_sample[1] =
+ (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
+ context->adpcm.ima.last_step[1] = (INT16)(*(src + 2));
+ src += 4;
+ size -= 4;
+ out_size -= 16;
+ }
+ }
+
+ if (channels > 1)
+ {
+ for (size_t i = 0; i < 8; i++)
+ {
+ BYTE* dst = Stream_Pointer(out);
+
+ channel = (i < 4 ? 0 : 1);
+ sample = ((*src) & 0x0f);
+ decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, channel, sample);
+ dst[((i & 3) << 3) + (channel << 1)] = (decoded & 0xFF);
+ dst[((i & 3) << 3) + (channel << 1) + 1] = (decoded >> 8);
+ sample = ((*src) >> 4);
+ decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, channel, sample);
+ dst[((i & 3) << 3) + (channel << 1) + 4] = (decoded & 0xFF);
+ dst[((i & 3) << 3) + (channel << 1) + 5] = (decoded >> 8);
+ src++;
+ }
+
+ if (!Stream_SafeSeek(out, 32))
+ return FALSE;
+ size -= 8;
+ }
+ else
+ {
+ BYTE* dst = Stream_Pointer(out);
+ if (!Stream_SafeSeek(out, 4))
+ return FALSE;
+
+ sample = ((*src) & 0x0f);
+ decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
+ *dst++ = (decoded & 0xFF);
+ *dst++ = (decoded >> 8);
+ sample = ((*src) >> 4);
+ decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
+ *dst++ = (decoded & 0xFF);
+ *dst++ = (decoded >> 8);
+ src++;
+ size--;
+ }
+ }
+
+ return TRUE;
+}
+
+#if defined(WITH_GSM)
+static BOOL freerdp_dsp_decode_gsm610(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ size_t offset = 0;
+
+ while (offset < size)
+ {
+ int rc;
+ gsm_signal gsmBlockBuffer[160] = { 0 };
+ rc = gsm_decode(context->gsm, (gsm_byte*)/* API does not modify */ &src[offset],
+ gsmBlockBuffer);
+
+ if (rc < 0)
+ return FALSE;
+
+ if ((offset % 65) == 0)
+ offset += 33;
+ else
+ offset += 32;
+
+ if (!Stream_EnsureRemainingCapacity(out, sizeof(gsmBlockBuffer)))
+ return FALSE;
+
+ Stream_Write(out, (void*)gsmBlockBuffer, sizeof(gsmBlockBuffer));
+ }
+
+ return TRUE;
+}
+
+static BOOL freerdp_dsp_encode_gsm610(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ size_t offset = 0;
+
+ while (offset < size)
+ {
+ const gsm_signal* signal = (const gsm_signal*)&src[offset];
+
+ if (!Stream_EnsureRemainingCapacity(out, sizeof(gsm_frame)))
+ return FALSE;
+
+ gsm_encode(context->gsm, (gsm_signal*)/* API does not modify */ signal,
+ Stream_Pointer(out));
+
+ if ((offset % 65) == 0)
+ Stream_Seek(out, 33);
+ else
+ Stream_Seek(out, 32);
+
+ offset += 160;
+ }
+
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_LAME)
+static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ int rc;
+ short* pcm_l;
+ short* pcm_r;
+ size_t buffer_size;
+
+ if (!context || !src || !out)
+ return FALSE;
+
+ buffer_size = 2 * context->format.nChannels * context->format.nSamplesPerSec;
+
+ if (!Stream_EnsureCapacity(context->buffer, 2 * buffer_size))
+ return FALSE;
+
+ pcm_l = (short*)Stream_Buffer(context->buffer);
+ pcm_r = (short*)Stream_Buffer(context->buffer) + buffer_size;
+ rc = hip_decode(context->hip, (unsigned char*)/* API is not modifying content */ src, size,
+ pcm_l, pcm_r);
+
+ if (rc <= 0)
+ return FALSE;
+
+ if (!Stream_EnsureRemainingCapacity(out, (size_t)rc * context->format.nChannels * 2))
+ return FALSE;
+
+ for (size_t x = 0; x < rc; x++)
+ {
+ Stream_Write_UINT16(out, (UINT16)pcm_l[x]);
+ Stream_Write_UINT16(out, (UINT16)pcm_r[x]);
+ }
+
+ return TRUE;
+}
+
+static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ size_t samples_per_channel;
+ int rc;
+
+ if (!context || !src || !out)
+ return FALSE;
+
+ samples_per_channel = size / context->format.nChannels / context->format.wBitsPerSample / 8;
+
+ /* Ensure worst case buffer size for mp3 stream taken from LAME header */
+ if (!Stream_EnsureRemainingCapacity(out, 5 / 4 * samples_per_channel + 7200))
+ return FALSE;
+
+ samples_per_channel = size / 2 /* size of a sample */ / context->format.nChannels;
+ rc = lame_encode_buffer_interleaved(context->lame, (short*)src, samples_per_channel,
+ Stream_Pointer(out), Stream_GetRemainingCapacity(out));
+
+ if (rc < 0)
+ return FALSE;
+
+ Stream_Seek(out, (size_t)rc);
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_FAAC)
+static BOOL freerdp_dsp_encode_faac(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ const int16_t* inSamples = (const int16_t*)src;
+ unsigned int bpp;
+ size_t nrSamples;
+ int rc;
+
+ if (!context || !src || !out)
+ return FALSE;
+
+ bpp = context->format.wBitsPerSample / 8;
+ nrSamples = size / bpp;
+
+ if (!Stream_EnsureRemainingCapacity(context->buffer, nrSamples * sizeof(int16_t)))
+ return FALSE;
+
+ for (size_t x = 0; x < nrSamples; x++)
+ {
+ Stream_Write_INT16(context->buffer, inSamples[x]);
+ if (Stream_GetPosition(context->buffer) / bpp >= context->faacInputSamples)
+ {
+ if (!Stream_EnsureRemainingCapacity(out, context->faacMaxOutputBytes))
+ return FALSE;
+ rc = faacEncEncode(context->faac, (int32_t*)Stream_Buffer(context->buffer),
+ context->faacInputSamples, Stream_Pointer(out),
+ Stream_GetRemainingCapacity(out));
+ if (rc < 0)
+ return FALSE;
+ if (rc > 0)
+ Stream_Seek(out, (size_t)rc);
+ Stream_SetPosition(context->buffer, 0);
+ }
+ }
+
+ return TRUE;
+}
+#endif
+
+#if defined(WITH_OPUS)
+static BOOL freerdp_dsp_decode_opus(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ size_t max_size = 5760;
+ int frames;
+
+ if (!context || !src || !out)
+ return FALSE;
+
+ /* Max packet duration is 120ms (5760 at 48KHz) */
+ max_size = OPUS_MAX_FRAMES * context->format.nChannels * sizeof(int16_t);
+ if (!Stream_EnsureRemainingCapacity(context->buffer, max_size))
+ return FALSE;
+
+ frames = opus_decode(context->opus_decoder, src, size, Stream_Pointer(out), OPUS_MAX_FRAMES, 0);
+ if (frames < 0)
+ return FALSE;
+
+ Stream_Seek(out, frames * context->format.nChannels * sizeof(int16_t));
+
+ return TRUE;
+}
+
+static BOOL freerdp_dsp_encode_opus(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ if (!context || !src || !out)
+ return FALSE;
+
+ /* Max packet duration is 120ms (5760 at 48KHz) */
+ const size_t max_size = OPUS_MAX_FRAMES * context->format.nChannels * sizeof(int16_t);
+ if (!Stream_EnsureRemainingCapacity(context->buffer, max_size))
+ return FALSE;
+
+ const int src_frames = size / sizeof(opus_int16) / context->format.nChannels;
+ const opus_int16* src_data = (const opus_int16*)src;
+ const int frames =
+ opus_encode(context->opus_encoder, src_data, src_frames, Stream_Pointer(out), max_size);
+ if (frames < 0)
+ return FALSE;
+ return Stream_SafeSeek(out, frames * context->format.nChannels * sizeof(int16_t));
+}
+#endif
+
+#if defined(WITH_FAAD2)
+static BOOL freerdp_dsp_decode_faad(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ NeAACDecFrameInfo info;
+ size_t offset = 0;
+
+ if (!context || !src || !out)
+ return FALSE;
+
+ if (!context->faadSetup)
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ unsigned long samplerate;
+ unsigned char channels;
+ long err;
+ cnv.cpv = src;
+ err = NeAACDecInit(context->faad, /* API is not modifying content */ cnv.pv, size,
+ &samplerate, &channels);
+
+ if (err != 0)
+ return FALSE;
+
+ if (channels != context->format.nChannels)
+ return FALSE;
+
+ if (samplerate != context->format.nSamplesPerSec)
+ return FALSE;
+
+ context->faadSetup = TRUE;
+ }
+
+ while (offset < size)
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ size_t outSize;
+ void* sample_buffer;
+ outSize = context->format.nSamplesPerSec * context->format.nChannels *
+ context->format.wBitsPerSample / 8;
+
+ if (!Stream_EnsureRemainingCapacity(out, outSize))
+ return FALSE;
+
+ sample_buffer = Stream_Pointer(out);
+
+ cnv.cpv = &src[offset];
+ NeAACDecDecode2(context->faad, &info, cnv.pv, size - offset, &sample_buffer,
+ Stream_GetRemainingCapacity(out));
+
+ if (info.error != 0)
+ return FALSE;
+
+ offset += info.bytesconsumed;
+
+ if (info.samples == 0)
+ continue;
+
+ Stream_Seek(out, info.samples * context->format.wBitsPerSample / 8);
+ }
+
+ return TRUE;
+}
+
+#endif
+
+/**
+ * 0 1 2 3
+ * 2 0 6 4 10 8 14 12 <left>
+ *
+ * 4 5 6 7
+ * 3 1 7 5 11 9 15 13 <right>
+ */
+static const struct
+{
+ BYTE byte_num;
+ BYTE byte_shift;
+} ima_stereo_encode_map[] = { { 0, 0 }, { 4, 0 }, { 0, 4 }, { 4, 4 }, { 1, 0 }, { 5, 0 },
+ { 1, 4 }, { 5, 4 }, { 2, 0 }, { 6, 0 }, { 2, 4 }, { 6, 4 },
+ { 3, 0 }, { 7, 0 }, { 3, 4 }, { 7, 4 } };
+
+static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
+{
+ INT32 e;
+ INT32 d;
+ INT32 ss;
+ BYTE enc;
+ INT32 diff;
+ ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ d = e = sample - adpcm->ima.last_sample[channel];
+ diff = ss >> 3;
+ enc = 0;
+
+ if (e < 0)
+ {
+ enc = 8;
+ e = -e;
+ }
+
+ if (e >= ss)
+ {
+ enc |= 4;
+ e -= ss;
+ }
+
+ ss >>= 1;
+
+ if (e >= ss)
+ {
+ enc |= 2;
+ e -= ss;
+ }
+
+ ss >>= 1;
+
+ if (e >= ss)
+ {
+ enc |= 1;
+ e -= ss;
+ }
+
+ if (d < 0)
+ diff = d + e - diff;
+ else
+ diff = d - e + diff;
+
+ diff += adpcm->ima.last_sample[channel];
+
+ if (diff < -32768)
+ diff = -32768;
+ else if (diff > 32767)
+ diff = 32767;
+
+ adpcm->ima.last_sample[channel] = (INT16)diff;
+ adpcm->ima.last_step[channel] += ima_step_index_table[enc];
+
+ if (adpcm->ima.last_step[channel] < 0)
+ adpcm->ima.last_step[channel] = 0;
+ else if (adpcm->ima.last_step[channel] > 88)
+ adpcm->ima.last_step[channel] = 88;
+
+ return enc;
+}
+
+static BOOL freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ INT16 sample;
+ BYTE encoded;
+ size_t align;
+
+ if (!Stream_EnsureRemainingCapacity(out, size))
+ return FALSE;
+
+ align = (context->format.nChannels > 1) ? 32 : 4;
+
+ while (size >= align)
+ {
+ if (Stream_GetPosition(context->buffer) % context->format.nBlockAlign == 0)
+ {
+ Stream_Write_UINT8(context->buffer, context->adpcm.ima.last_sample[0] & 0xFF);
+ Stream_Write_UINT8(context->buffer, (context->adpcm.ima.last_sample[0] >> 8) & 0xFF);
+ Stream_Write_UINT8(context->buffer, (BYTE)context->adpcm.ima.last_step[0]);
+ Stream_Write_UINT8(context->buffer, 0);
+
+ if (context->format.nChannels > 1)
+ {
+ Stream_Write_UINT8(context->buffer, context->adpcm.ima.last_sample[1] & 0xFF);
+ Stream_Write_UINT8(context->buffer,
+ (context->adpcm.ima.last_sample[1] >> 8) & 0xFF);
+ Stream_Write_UINT8(context->buffer, (BYTE)context->adpcm.ima.last_step[1]);
+ Stream_Write_UINT8(context->buffer, 0);
+ }
+ }
+
+ if (context->format.nChannels > 1)
+ {
+ BYTE* dst = Stream_Pointer(context->buffer);
+ ZeroMemory(dst, 8);
+
+ for (size_t i = 0; i < 16; i++)
+ {
+ sample = (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
+ src += 2;
+ encoded = dsp_encode_ima_adpcm_sample(&context->adpcm, i % 2, sample);
+ dst[ima_stereo_encode_map[i].byte_num] |= encoded
+ << ima_stereo_encode_map[i].byte_shift;
+ }
+
+ if (!Stream_SafeSeek(context->buffer, 8))
+ return FALSE;
+ size -= 32;
+ }
+ else
+ {
+ sample = (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
+ src += 2;
+ encoded = dsp_encode_ima_adpcm_sample(&context->adpcm, 0, sample);
+ sample = (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
+ src += 2;
+ encoded |= dsp_encode_ima_adpcm_sample(&context->adpcm, 0, sample) << 4;
+ Stream_Write_UINT8(context->buffer, encoded);
+ size -= 4;
+ }
+
+ if (Stream_GetPosition(context->buffer) >= context->adpcm.ima.packet_size)
+ {
+ BYTE* bsrc = Stream_Buffer(context->buffer);
+ Stream_Write(out, bsrc, context->adpcm.ima.packet_size);
+ Stream_SetPosition(context->buffer, 0);
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Microsoft ADPCM Specification:
+ *
+ * http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM
+ */
+
+static const INT32 ms_adpcm_adaptation_table[] = { 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230 };
+
+static const INT32 ms_adpcm_coeffs1[7] = { 256, 512, 0, 192, 240, 460, 392 };
+
+static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 };
+
+static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample, int channel)
+{
+ INT8 nibble;
+ INT32 presample;
+ nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample);
+ presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
+ (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
+ 256;
+ presample += nibble * adpcm->ms.delta[channel];
+
+ if (presample > 32767)
+ presample = 32767;
+ else if (presample < -32768)
+ presample = -32768;
+
+ adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
+ adpcm->ms.sample1[channel] = presample;
+ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256;
+
+ if (adpcm->ms.delta[channel] < 16)
+ adpcm->ms.delta[channel] = 16;
+
+ return (INT16)presample;
+}
+
+static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ BYTE sample;
+ const size_t out_size = size * 4;
+ const UINT32 channels = context->format.nChannels;
+ const UINT32 block_size = context->format.nBlockAlign;
+
+ if (!Stream_EnsureCapacity(out, out_size))
+ return FALSE;
+
+ while (size > 0)
+ {
+ if (size % block_size == 0)
+ {
+ if (channels > 1)
+ {
+ context->adpcm.ms.predictor[0] = *src++;
+ context->adpcm.ms.predictor[1] = *src++;
+ context->adpcm.ms.delta[0] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.delta[1] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample1[0] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample1[1] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample2[0] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample2[1] = read_int16(src);
+ src += 2;
+ size -= 14;
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[1]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[1]);
+ }
+ else
+ {
+ context->adpcm.ms.predictor[0] = *src++;
+ context->adpcm.ms.delta[0] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample1[0] = read_int16(src);
+ src += 2;
+ context->adpcm.ms.sample2[0] = read_int16(src);
+ src += 2;
+ size -= 7;
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[0]);
+ }
+ }
+
+ if (channels > 1)
+ {
+ sample = *src++;
+ size--;
+ Stream_Write_INT16(out,
+ freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
+ Stream_Write_INT16(
+ out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1));
+ sample = *src++;
+ size--;
+ Stream_Write_INT16(out,
+ freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
+ Stream_Write_INT16(
+ out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1));
+ }
+ else
+ {
+ sample = *src++;
+ size--;
+ Stream_Write_INT16(out,
+ freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
+ Stream_Write_INT16(
+ out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 0));
+ }
+ }
+
+ return TRUE;
+}
+
+static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int channel)
+{
+ INT32 presample;
+ INT32 errordelta;
+ presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
+ (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
+ 256;
+ errordelta = (sample - presample) / adpcm->ms.delta[channel];
+
+ if ((sample - presample) % adpcm->ms.delta[channel] > adpcm->ms.delta[channel] / 2)
+ errordelta++;
+
+ if (errordelta > 7)
+ errordelta = 7;
+ else if (errordelta < -8)
+ errordelta = -8;
+
+ presample += adpcm->ms.delta[channel] * errordelta;
+
+ if (presample > 32767)
+ presample = 32767;
+ else if (presample < -32768)
+ presample = -32768;
+
+ adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
+ adpcm->ms.sample1[channel] = presample;
+ adpcm->ms.delta[channel] =
+ adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256;
+
+ if (adpcm->ms.delta[channel] < 16)
+ adpcm->ms.delta[channel] = 16;
+
+ return ((BYTE)errordelta) & 0x0F;
+}
+
+static BOOL freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
+ wStream* out)
+{
+ size_t start;
+ INT32 sample;
+ const size_t step = 8 + ((context->format.nChannels > 1) ? 4 : 0);
+
+ if (!Stream_EnsureRemainingCapacity(out, size))
+ return FALSE;
+
+ start = Stream_GetPosition(out);
+
+ if (context->adpcm.ms.delta[0] < 16)
+ context->adpcm.ms.delta[0] = 16;
+
+ if (context->adpcm.ms.delta[1] < 16)
+ context->adpcm.ms.delta[1] = 16;
+
+ while (size >= step)
+ {
+ BYTE val;
+ if ((Stream_GetPosition(out) - start) % context->format.nBlockAlign == 0)
+ {
+ if (context->format.nChannels > 1)
+ {
+ Stream_Write_UINT8(out, context->adpcm.ms.predictor[0]);
+ Stream_Write_UINT8(out, context->adpcm.ms.predictor[1]);
+ Stream_Write_UINT8(out, (context->adpcm.ms.delta[0] & 0xFF));
+ Stream_Write_UINT8(out, ((context->adpcm.ms.delta[0] >> 8) & 0xFF));
+ Stream_Write_UINT8(out, (context->adpcm.ms.delta[1] & 0xFF));
+ Stream_Write_UINT8(out, ((context->adpcm.ms.delta[1] >> 8) & 0xFF));
+
+ context->adpcm.ms.sample1[0] = read_int16(src + 4);
+ context->adpcm.ms.sample1[1] = read_int16(src + 6);
+ context->adpcm.ms.sample2[0] = read_int16(src + 0);
+ context->adpcm.ms.sample2[1] = read_int16(src + 2);
+
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[1]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[1]);
+
+ src += 8;
+ size -= 8;
+ }
+ else
+ {
+ Stream_Write_UINT8(out, context->adpcm.ms.predictor[0]);
+ Stream_Write_UINT8(out, (BYTE)(context->adpcm.ms.delta[0] & 0xFF));
+ Stream_Write_UINT8(out, (BYTE)((context->adpcm.ms.delta[0] >> 8) & 0xFF));
+
+ context->adpcm.ms.sample1[0] = read_int16(src + 2);
+ context->adpcm.ms.sample2[0] = read_int16(src + 0);
+
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample1[0]);
+ Stream_Write_INT16(out, (INT16)context->adpcm.ms.sample2[0]);
+ src += 4;
+ size -= 4;
+ }
+ }
+
+ sample = read_int16(src);
+ src += 2;
+ Stream_Write_UINT8(
+ out, (freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample, 0) << 4) & 0xFF);
+ sample = read_int16(src);
+ src += 2;
+
+ Stream_Read_UINT8(out, val);
+ val += freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample,
+ context->format.nChannels > 1 ? 1 : 0);
+ Stream_Write_UINT8(out, val);
+ size -= 4;
+ }
+
+ return TRUE;
+}
+
+#endif
+
+FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(BOOL encoder)
+{
+#if defined(WITH_DSP_FFMPEG)
+ return freerdp_dsp_ffmpeg_context_new(encoder);
+#else
+ FREERDP_DSP_CONTEXT* context = calloc(1, sizeof(FREERDP_DSP_CONTEXT));
+
+ if (!context)
+ return NULL;
+
+ context->channelmix = Stream_New(NULL, 4096);
+
+ if (!context->channelmix)
+ goto fail;
+
+ context->resample = Stream_New(NULL, 4096);
+
+ if (!context->resample)
+ goto fail;
+
+ context->buffer = Stream_New(NULL, 4096);
+
+ if (!context->buffer)
+ goto fail;
+
+ context->encoder = encoder;
+#if defined(WITH_GSM)
+ context->gsm = gsm_create();
+
+ if (!context->gsm)
+ goto fail;
+
+ {
+ int rc;
+ int val = 1;
+ rc = gsm_option(context->gsm, GSM_OPT_WAV49, &val);
+
+ if (rc < 0)
+ goto fail;
+ }
+#endif
+#if defined(WITH_LAME)
+
+ if (encoder)
+ {
+ context->lame = lame_init();
+
+ if (!context->lame)
+ goto fail;
+ }
+ else
+ {
+ context->hip = hip_decode_init();
+
+ if (!context->hip)
+ goto fail;
+ }
+
+#endif
+#if defined(WITH_FAAD2)
+
+ if (!encoder)
+ {
+ context->faad = NeAACDecOpen();
+
+ if (!context->faad)
+ goto fail;
+ }
+
+#endif
+ return context;
+fail:
+ freerdp_dsp_context_free(context);
+ return NULL;
+#endif
+}
+
+void freerdp_dsp_context_free(FREERDP_DSP_CONTEXT* context)
+{
+#if defined(WITH_DSP_FFMPEG)
+ freerdp_dsp_ffmpeg_context_free(context);
+#else
+
+ if (context)
+ {
+ Stream_Free(context->channelmix, TRUE);
+ Stream_Free(context->resample, TRUE);
+ Stream_Free(context->buffer, TRUE);
+#if defined(WITH_GSM)
+ gsm_destroy(context->gsm);
+#endif
+#if defined(WITH_LAME)
+
+ if (context->encoder)
+ lame_close(context->lame);
+ else
+ hip_decode_exit(context->hip);
+
+#endif
+#if defined(WITH_OPUS)
+
+ if (context->opus_decoder)
+ opus_decoder_destroy(context->opus_decoder);
+ if (context->opus_encoder)
+ opus_encoder_destroy(context->opus_encoder);
+
+#endif
+#if defined(WITH_FAAD2)
+
+ if (!context->encoder)
+ NeAACDecClose(context->faad);
+
+#endif
+#if defined(WITH_FAAC)
+
+ if (context->faac)
+ faacEncClose(context->faac);
+
+#endif
+#if defined(WITH_SOXR)
+ soxr_delete(context->sox);
+#endif
+ free(context);
+ }
+
+#endif
+}
+
+BOOL freerdp_dsp_encode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
+ const BYTE* data, size_t length, wStream* out)
+{
+#if defined(WITH_DSP_FFMPEG)
+ return freerdp_dsp_ffmpeg_encode(context, srcFormat, data, length, out);
+#else
+ const BYTE* resampleData;
+ size_t resampleLength;
+ AUDIO_FORMAT format;
+
+ if (!context || !context->encoder || !srcFormat || !data || !out)
+ return FALSE;
+
+ format = *srcFormat;
+
+ if (!freerdp_dsp_channel_mix(context, data, length, srcFormat, &resampleData, &resampleLength))
+ return FALSE;
+
+ format.nChannels = context->format.nChannels;
+
+ if (!freerdp_dsp_resample(context, resampleData, resampleLength, &format, &data, &length))
+ return FALSE;
+
+ switch (context->format.wFormatTag)
+ {
+ case WAVE_FORMAT_PCM:
+ if (!Stream_EnsureRemainingCapacity(out, length))
+ return FALSE;
+
+ Stream_Write(out, data, length);
+ return TRUE;
+
+ case WAVE_FORMAT_ADPCM:
+ return freerdp_dsp_encode_ms_adpcm(context, data, length, out);
+
+ case WAVE_FORMAT_DVI_ADPCM:
+ return freerdp_dsp_encode_ima_adpcm(context, data, length, out);
+#if defined(WITH_GSM)
+
+ case WAVE_FORMAT_GSM610:
+ return freerdp_dsp_encode_gsm610(context, data, length, out);
+#endif
+#if defined(WITH_LAME)
+
+ case WAVE_FORMAT_MPEGLAYER3:
+ return freerdp_dsp_encode_mp3(context, data, length, out);
+#endif
+#if defined(WITH_FAAC)
+
+ case WAVE_FORMAT_AAC_MS:
+ return freerdp_dsp_encode_faac(context, data, length, out);
+#endif
+#if defined(WITH_OPUS)
+
+ case WAVE_FORMAT_OPUS:
+ return freerdp_dsp_encode_opus(context, data, length, out);
+#endif
+ default:
+ return FALSE;
+ }
+
+ return FALSE;
+#endif
+}
+
+BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
+ const BYTE* data, size_t length, wStream* out)
+{
+#if defined(WITH_DSP_FFMPEG)
+ return freerdp_dsp_ffmpeg_decode(context, srcFormat, data, length, out);
+#else
+
+ if (!context || context->encoder || !srcFormat || !data || !out)
+ return FALSE;
+
+ switch (context->format.wFormatTag)
+ {
+ case WAVE_FORMAT_PCM:
+ if (!Stream_EnsureRemainingCapacity(out, length))
+ return FALSE;
+
+ Stream_Write(out, data, length);
+ return TRUE;
+
+ case WAVE_FORMAT_ADPCM:
+ return freerdp_dsp_decode_ms_adpcm(context, data, length, out);
+
+ case WAVE_FORMAT_DVI_ADPCM:
+ return freerdp_dsp_decode_ima_adpcm(context, data, length, out);
+#if defined(WITH_GSM)
+
+ case WAVE_FORMAT_GSM610:
+ return freerdp_dsp_decode_gsm610(context, data, length, out);
+#endif
+#if defined(WITH_LAME)
+
+ case WAVE_FORMAT_MPEGLAYER3:
+ return freerdp_dsp_decode_mp3(context, data, length, out);
+#endif
+#if defined(WITH_FAAD2)
+
+ case WAVE_FORMAT_AAC_MS:
+ return freerdp_dsp_decode_faad(context, data, length, out);
+#endif
+
+#if defined(WITH_OPUS)
+ case WAVE_FORMAT_OPUS:
+ return freerdp_dsp_decode_opus(context, data, length, out);
+#endif
+ default:
+ return FALSE;
+ }
+
+ return FALSE;
+#endif
+}
+
+BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* format, BOOL encode)
+{
+#if defined(WITH_DSP_FFMPEG)
+ return freerdp_dsp_ffmpeg_supports_format(format, encode);
+#else
+
+#if !defined(WITH_DSP_EXPERIMENTAL)
+ WINPR_UNUSED(encode);
+#endif
+ switch (format->wFormatTag)
+ {
+ case WAVE_FORMAT_PCM:
+ return TRUE;
+#if defined(WITH_DSP_EXPERIMENTAL)
+
+ case WAVE_FORMAT_ADPCM:
+ return FALSE;
+ case WAVE_FORMAT_DVI_ADPCM:
+ return TRUE;
+#endif
+#if defined(WITH_GSM)
+
+ case WAVE_FORMAT_GSM610:
+#if defined(WITH_DSP_EXPERIMENTAL)
+ return TRUE;
+#else
+ return !encode;
+#endif
+#endif
+#if defined(WITH_LAME)
+
+ case WAVE_FORMAT_MPEGLAYER3:
+#if defined(WITH_DSP_EXPERIMENTAL)
+ return TRUE;
+#else
+ return !encode;
+#endif
+#endif
+
+ case WAVE_FORMAT_AAC_MS:
+#if defined(WITH_FAAD2)
+ if (!encode)
+ return TRUE;
+
+#endif
+#if defined(WITH_FAAC)
+
+ if (encode)
+ return TRUE;
+
+#endif
+#if defined(WITH_OPUS)
+ WINPR_FALLTHROUGH
+ case WAVE_FORMAT_OPUS:
+ return opus_is_valid_samplerate(format);
+#endif
+ WINPR_FALLTHROUGH
+ default:
+ return FALSE;
+ }
+
+ return FALSE;
+#endif
+}
+
+BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* targetFormat,
+ UINT32 FramesPerPacket)
+{
+#if defined(WITH_DSP_FFMPEG)
+ return freerdp_dsp_ffmpeg_context_reset(context, targetFormat);
+#else
+
+ if (!context || !targetFormat)
+ return FALSE;
+
+ context->format = *targetFormat;
+
+ if (context->format.wFormatTag == WAVE_FORMAT_DVI_ADPCM)
+ {
+ size_t min_frame_data =
+ 1ull * context->format.wBitsPerSample * context->format.nChannels * FramesPerPacket;
+ size_t data_per_block = (context->format.nBlockAlign - 4 * context->format.nChannels) * 8;
+ size_t nb_block_per_packet = min_frame_data / data_per_block;
+
+ if (min_frame_data % data_per_block)
+ nb_block_per_packet++;
+
+ context->adpcm.ima.packet_size = nb_block_per_packet * context->format.nBlockAlign;
+ Stream_EnsureCapacity(context->buffer, context->adpcm.ima.packet_size);
+ Stream_SetPosition(context->buffer, 0);
+ }
+
+#if defined(WITH_OPUS)
+
+ if (opus_is_valid_samplerate(&context->format))
+ {
+ if (!context->encoder)
+ {
+ int opus_error = OPUS_OK;
+
+ context->opus_decoder = opus_decoder_create(context->format.nSamplesPerSec,
+ context->format.nChannels, &opus_error);
+ if (opus_error != OPUS_OK)
+ return FALSE;
+ }
+ else
+ {
+ int opus_error = OPUS_OK;
+
+ context->opus_encoder =
+ opus_encoder_create(context->format.nSamplesPerSec, context->format.nChannels,
+ OPUS_APPLICATION_VOIP, &opus_error);
+ if (opus_error != OPUS_OK)
+ return FALSE;
+
+ opus_error = opus_encoder_ctl(context->opus_encoder,
+ OPUS_SET_BITRATE(context->format.nAvgBytesPerSec * 8));
+ if (opus_error != OPUS_OK)
+ return FALSE;
+ }
+ }
+
+#endif
+#if defined(WITH_FAAD2)
+ context->faadSetup = FALSE;
+#endif
+#if defined(WITH_FAAC)
+
+ if (context->encoder)
+ {
+ faacEncConfigurationPtr cfg;
+
+ if (context->faac)
+ faacEncClose(context->faac);
+
+ context->faac = faacEncOpen(targetFormat->nSamplesPerSec, targetFormat->nChannels,
+ &context->faacInputSamples, &context->faacMaxOutputBytes);
+
+ if (!context->faac)
+ return FALSE;
+
+ cfg = faacEncGetCurrentConfiguration(context->faac);
+ cfg->inputFormat = FAAC_INPUT_16BIT;
+ cfg->outputFormat = 0;
+ cfg->mpegVersion = MPEG4;
+ cfg->useTns = 1;
+ cfg->bandWidth = targetFormat->nAvgBytesPerSec;
+ faacEncSetConfiguration(context->faac, cfg);
+ }
+
+#endif
+#if defined(WITH_SOXR)
+ {
+ soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16);
+ soxr_error_t error;
+ soxr_delete(context->sox);
+ context->sox = soxr_create(context->format.nSamplesPerSec, targetFormat->nSamplesPerSec,
+ targetFormat->nChannels, &error, &iospec, NULL, NULL);
+
+ if (!context->sox || (error != 0))
+ return FALSE;
+ }
+#endif
+ return TRUE;
+#endif
+}