diff --git a/channels/rdpsnd/alsa/rdpsnd_alsa.c b/channels/rdpsnd/alsa/rdpsnd_alsa.c index 91afdcdd5..72c6979d0 100644 --- a/channels/rdpsnd/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/alsa/rdpsnd_alsa.c @@ -138,6 +138,7 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ alsa->format = SND_PCM_FORMAT_S16_LE; alsa->bytes_per_channel = 2; @@ -212,6 +213,7 @@ static boolean rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, rdpsndFo } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ if (format->nSamplesPerSec <= 48000 && format->wBitsPerSample == 4 && @@ -243,7 +245,14 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, uint8* data, int size) if (alsa->out_handle == 0) return; - if (alsa->wformat == 0x11) + if (alsa->wformat == 2) + { + alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context, + data, size, alsa->source_channels, alsa->block_size); + size = alsa->dsp_context->adpcm_size; + src = alsa->dsp_context->adpcm_buffer; + } + else if (alsa->wformat == 0x11) { alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context, data, size, alsa->source_channels, alsa->block_size); diff --git a/channels/rdpsnd/pulse/rdpsnd_pulse.c b/channels/rdpsnd/pulse/rdpsnd_pulse.c index 5955db830..06f5a1716 100644 --- a/channels/rdpsnd/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/pulse/rdpsnd_pulse.c @@ -212,6 +212,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat* sample_spec.format = PA_SAMPLE_ULAW; break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ sample_spec.format = PA_SAMPLE_S16LE; break; @@ -364,6 +365,7 @@ static boolean rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndF } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ if ((format->nSamplesPerSec <= PA_RATE_MAX) && (format->wBitsPerSample == 4) && @@ -405,7 +407,14 @@ static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, uint8* data, int size) if (!pulse->stream) return; - if (pulse->format == 0x11) + if (pulse->format == 2) + { + pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, + data, size, pulse->sample_spec.channels, pulse->block_size); + size = pulse->dsp_context->adpcm_size; + src = pulse->dsp_context->adpcm_buffer; + } + else if (pulse->format == 0x11) { pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, data, size, pulse->sample_spec.channels, pulse->block_size); diff --git a/include/freerdp/utils/dsp.h b/include/freerdp/utils/dsp.h index 46ef066a4..835956ac8 100644 --- a/include/freerdp/utils/dsp.h +++ b/include/freerdp/utils/dsp.h @@ -22,12 +22,22 @@ #include -struct _ADPCM +union _ADPCM { - sint16 last_sample[2]; - sint16 last_step[2]; + struct + { + sint16 last_sample[2]; + sint16 last_step[2]; + } ima; + struct + { + uint8 predictor[2]; + sint32 delta[2]; + sint32 sample1[2]; + sint32 sample2[2]; + } ms; }; -typedef struct _ADPCM ADPCM; +typedef union _ADPCM ADPCM; typedef struct _FREERDP_DSP_CONTEXT FREERDP_DSP_CONTEXT; struct _FREERDP_DSP_CONTEXT @@ -52,6 +62,11 @@ struct _FREERDP_DSP_CONTEXT const uint8* src, int size, int channels, int block_size); void (*encode_ima_adpcm)(FREERDP_DSP_CONTEXT* context, const uint8* src, int size, int channels, int block_size); + + void (*decode_ms_adpcm)(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size); + void (*encode_ms_adpcm)(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size); }; FREERDP_API FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void); diff --git a/libfreerdp-utils/dsp.c b/libfreerdp-utils/dsp.c index fd8beb0f9..fca8de0f7 100644 --- a/libfreerdp-utils/dsp.c +++ b/libfreerdp-utils/dsp.c @@ -100,7 +100,7 @@ static uint16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, sint32 ss; sint32 d; - ss = ima_step_size_table[adpcm->last_step[channel]]; + ss = ima_step_size_table[adpcm->ima.last_step[channel]]; d = (ss >> 3); if (sample & 1) d += (ss >> 2); @@ -110,20 +110,20 @@ static uint16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, d += ss; if (sample & 8) d = -d; - d += adpcm->last_sample[channel]; + d += adpcm->ima.last_sample[channel]; if (d < -32768) d = -32768; else if (d > 32767) d = 32767; - adpcm->last_sample[channel] = (sint16) d; + adpcm->ima.last_sample[channel] = (sint16) d; - adpcm->last_step[channel] += ima_step_index_table[sample]; - if (adpcm->last_step[channel] < 0) - adpcm->last_step[channel] = 0; - else if (adpcm->last_step[channel] > 88) - adpcm->last_step[channel] = 88; + 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; } @@ -149,15 +149,15 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, { if (size % block_size == 0) { - context->adpcm.last_sample[0] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); - context->adpcm.last_step[0] = (sint16) (*(src + 2)); + context->adpcm.ima.last_sample[0] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (sint16) (*(src + 2)); src += 4; size -= 4; out_size -= 16; if (channels > 1) { - context->adpcm.last_sample[1] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); - context->adpcm.last_step[1] = (sint16) (*(src + 2)); + context->adpcm.ima.last_sample[1] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (sint16) (*(src + 2)); src += 4; size -= 4; out_size -= 16; @@ -197,7 +197,7 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, } } - context->adpcm_size = out_size; + context->adpcm_size = dst - context->adpcm_buffer; } /** @@ -240,8 +240,8 @@ static uint8 dsp_encode_ima_adpcm_sample(ADPCM* adpcm, uint8 enc; sint32 diff; - ss = ima_step_size_table[adpcm->last_step[channel]]; - d = e = sample - adpcm->last_sample[channel]; + 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) @@ -272,18 +272,18 @@ static uint8 dsp_encode_ima_adpcm_sample(ADPCM* adpcm, else diff = d - e + diff; - diff += adpcm->last_sample[channel]; + diff += adpcm->ima.last_sample[channel]; if (diff < -32768) diff = -32768; else if (diff > 32767) diff = 32767; - adpcm->last_sample[channel] = (sint16) diff; + adpcm->ima.last_sample[channel] = (sint16) diff; - adpcm->last_step[channel] += ima_step_index_table[enc]; - if (adpcm->last_step[channel] < 0) - adpcm->last_step[channel] = 0; - else if (adpcm->last_step[channel] > 88) - adpcm->last_step[channel] = 88; + 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; } @@ -308,15 +308,15 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, { if ((dst - context->adpcm_buffer) % block_size == 0) { - *dst++ = context->adpcm.last_sample[0] & 0xff; - *dst++ = (context->adpcm.last_sample[0] >> 8) & 0xff; - *dst++ = (uint8) context->adpcm.last_step[0]; + *dst++ = context->adpcm.ima.last_sample[0] & 0xff; + *dst++ = (context->adpcm.ima.last_sample[0] >> 8) & 0xff; + *dst++ = (uint8) context->adpcm.ima.last_step[0]; *dst++ = 0; if (channels > 1) { - *dst++ = context->adpcm.last_sample[1] & 0xff; - *dst++ = (context->adpcm.last_sample[1] >> 8) & 0xff; - *dst++ = (uint8) context->adpcm.last_step[1]; + *dst++ = context->adpcm.ima.last_sample[1] & 0xff; + *dst++ = (context->adpcm.ima.last_sample[1] >> 8) & 0xff; + *dst++ = (uint8) context->adpcm.ima.last_step[1]; *dst++ = 0; } } @@ -350,6 +350,241 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, context->adpcm_size = dst - context->adpcm_buffer; } +/** + * Microsoft ADPCM Specification: + * + * http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM + */ + +static const sint16 ms_adpcm_adaptation_table[] = +{ + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +static const sint16 ms_adpcm_coeff1_table[] = +{ + 256, 512, 0, 192, 240, 460, 392 +}; + +static const sint16 ms_adpcm_coeff2_table[] = +{ + 0, -256, 0, 64, 0, -208, -232 +}; + +static sint16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, uint8 sample, int channel) +{ + sint8 nibble; + sint32 presample; + + nibble = (sample & 0x08 ? (sint8)sample - 16 : sample); + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeff1_table[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeff2_table[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 (sint16) presample; +} + +static void freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size) +{ + uint8* dst; + uint8 sample; + uint32 out_size; + + out_size = size * 4; + if (out_size > context->adpcm_maxlength) + { + context->adpcm_maxlength = out_size + 1024; + context->adpcm_buffer = xrealloc(context->adpcm_buffer, context->adpcm_maxlength); + } + dst = context->adpcm_buffer; + 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] = *((sint16*)src); + src += 2; + context->adpcm.ms.delta[1] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[1] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[1] = *((sint16*)src); + src += 2; + size -= 14; + + *((sint16*)dst) = context->adpcm.ms.sample2[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample2[1]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[1]; + dst += 2; + } + else + { + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[0] = *((sint16*)src); + src += 2; + size -= 7; + + *((sint16*)dst) = context->adpcm.ms.sample2[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[0]; + dst += 2; + } + } + + if (channels > 1) + { + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1); + dst += 2; + + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1); + dst += 2; + } + else + { + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 0); + dst += 2; + } + } + + context->adpcm_size = dst - context->adpcm_buffer; +} + +static uint8 freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, sint32 sample, int channel) +{ + sint32 presample; + sint32 errordelta; + + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeff1_table[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeff2_table[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[(((uint8)errordelta) & 0x0F)] / 256; + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; + return ((uint8)errordelta) & 0x0F; +} + +static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size) +{ + uint8* dst; + sint32 sample; + uint32 out_size; + + out_size = size / 2; + if (out_size > context->adpcm_maxlength) + { + context->adpcm_maxlength = out_size + 1024; + context->adpcm_buffer = xrealloc(context->adpcm_buffer, context->adpcm_maxlength); + } + dst = context->adpcm_buffer; + + 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 > 0) + { + if ((dst - context->adpcm_buffer) % block_size == 0) + { + if (channels > 1) + { + *dst++ = context->adpcm.ms.predictor[0]; + *dst++ = context->adpcm.ms.predictor[1]; + *dst++ = (uint8) (context->adpcm.ms.delta[0] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[0] >> 8) & 0xff); + *dst++ = (uint8) (context->adpcm.ms.delta[1] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[1] >> 8) & 0xff); + context->adpcm.ms.sample1[0] = *((sint16*) (src + 4)); + context->adpcm.ms.sample1[1] = *((sint16*) (src + 6)); + context->adpcm.ms.sample2[0] = *((sint16*) (src + 0)); + context->adpcm.ms.sample2[1] = *((sint16*) (src + 2)); + *((sint16*) (dst + 0)) = (sint16) context->adpcm.ms.sample1[0]; + *((sint16*) (dst + 2)) = (sint16) context->adpcm.ms.sample1[1]; + *((sint16*) (dst + 4)) = (sint16) context->adpcm.ms.sample2[0]; + *((sint16*) (dst + 6)) = (sint16) context->adpcm.ms.sample2[1]; + dst += 8; + src += 8; + size -= 8; + } + else + { + *dst++ = context->adpcm.ms.predictor[0]; + *dst++ = (uint8) (context->adpcm.ms.delta[0] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[0] >> 8) & 0xff); + context->adpcm.ms.sample1[0] = *((sint16*) (src + 2)); + context->adpcm.ms.sample2[0] = *((sint16*) (src + 0)); + *((sint16*) (dst + 0)) = (sint16) context->adpcm.ms.sample1[0]; + *((sint16*) (dst + 2)) = (sint16) context->adpcm.ms.sample2[0]; + dst += 4; + src += 4; + size -= 4; + } + } + + sample = *((sint16*) src); + src += 2; + *dst = freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample, 0) << 4; + sample = *((sint16*) src); + src += 2; + *dst += freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample, channels > 1 ? 1 : 0); + dst++; + size -= 4; + } + + context->adpcm_size = dst - context->adpcm_buffer; +} + FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void) { FREERDP_DSP_CONTEXT* context; @@ -359,6 +594,8 @@ FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void) context->resample = freerdp_dsp_resample; context->decode_ima_adpcm = freerdp_dsp_decode_ima_adpcm; context->encode_ima_adpcm = freerdp_dsp_encode_ima_adpcm; + context->decode_ms_adpcm = freerdp_dsp_decode_ms_adpcm; + context->encode_ms_adpcm = freerdp_dsp_encode_ms_adpcm; return context; } diff --git a/server/channels/rdpsnd.c b/server/channels/rdpsnd.c index d4c0ac1d9..32a909487 100644 --- a/server/channels/rdpsnd.c +++ b/server/channels/rdpsnd.c @@ -255,6 +255,11 @@ static void rdpsnd_server_select_format(rdpsnd_server_context* context, int clie bs = (format->nBlockAlign - 4 * format->nChannels) * 4; rdpsnd->out_frames = (format->nBlockAlign * 4 * format->nChannels * 2 / bs + 1) * bs / (format->nChannels * 2); } + else if (format->wFormatTag == 0x02) + { + bs = (format->nBlockAlign - 7 * format->nChannels) * 2 / format->nChannels + 2; + rdpsnd->out_frames = bs * 4; + } else { rdpsnd->out_frames = 0x4000 / rdpsnd->src_bytes_per_frame; @@ -311,11 +316,19 @@ static boolean rdpsnd_server_send_audio_pdu(rdpsnd_server* rdpsnd) src = rdpsnd->dsp_context->adpcm_buffer; size = rdpsnd->dsp_context->adpcm_size; } + else if (format->wFormatTag == 0x02) + { + rdpsnd->dsp_context->encode_ms_adpcm(rdpsnd->dsp_context, + src, size, format->nChannels, format->nBlockAlign); + src = rdpsnd->dsp_context->adpcm_buffer; + size = rdpsnd->dsp_context->adpcm_size; + } rdpsnd->context.block_no = (rdpsnd->context.block_no + 1) % 256; /* Fill to nBlockAlign for the last audio packet */ - if (format->wFormatTag == 0x11 && rdpsnd->out_pending_frames < rdpsnd->out_frames && (size % format->nBlockAlign) != 0) + if ((format->wFormatTag == 0x11 || format->wFormatTag == 0x02) && + rdpsnd->out_pending_frames < rdpsnd->out_frames && (size % format->nBlockAlign) != 0) fill_size = format->nBlockAlign - (size % format->nBlockAlign); else fill_size = 0;