channels/rdpsnd: reduce ALSA buffer underruns

This commit is contained in:
Marc-André Moreau
2013-02-26 17:54:16 -05:00
parent 8df47fb7fa
commit 6a775ff032
3 changed files with 125 additions and 70 deletions

View File

@@ -506,20 +506,21 @@ static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
int offset;
int length;
int status;
int frames;
int frame_size;
UINT16 wPlaybackDelayA;
UINT16 wPlaybackDelayB;
snd_pcm_sframes_t delayA;
snd_pcm_sframes_t delayB;
snd_htimestamp_t tstampA;
snd_htimestamp_t tstampB;
snd_pcm_uframes_t framesA;
snd_pcm_uframes_t framesB;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
offset = 0;
data = wave->data;
length = wave->length;
frame_size = alsa->actual_channels * alsa->bytes_per_channel;
frames = (length - offset) / frame_size;
snd_pcm_delay(alsa->pcm_handle, &delayA);
wPlaybackDelayA = ((delayA * 1000) / alsa->actual_rate);
snd_pcm_htimestamp(alsa->pcm_handle, &framesA, &tstampA);
while (offset < length)
{
@@ -548,23 +549,17 @@ static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
free(data);
snd_pcm_delay(alsa->pcm_handle, &delayB);
wPlaybackDelayB = ((delayB * 1000) / alsa->actual_rate);
snd_pcm_htimestamp(alsa->pcm_handle, &framesB, &tstampB);
printf("wPlaybackDelayA: %d ms, wPlaybackDelayB: %d ms, wAudioLength: %d ms wPlaybackDiff: %d ms\n",
wPlaybackDelayA, wPlaybackDelayB, wave->wAudioLength,
wPlaybackDelayB - wPlaybackDelayA);
wave->wPlaybackDelay = (wPlaybackDelayB - wPlaybackDelayA);
if (wave->wPlaybackDelay > wave->wAudioLength)
wave->wPlaybackDelay = wave->wAudioLength;
wave->wPlaybackDelay = ((framesB * 1000) / alsa->actual_rate);
wave->wLocalTimeB = GetTickCount();
wave->wLocalTimeB += wave->wPlaybackDelay;
wave->wLatency = (UINT16) (wave->wLocalTimeB - wave->wLocalTimeA);
wave->wLatency += wave->wPlaybackDelay;
wave->wTimeStampB = wave->wTimeStampA + wave->wLatency;
//printf("wTimeStampA: %d wTimeStampB: %d wLatency: %d\n", wave->wTimeStampA, wave->wTimeStampB, wave->wLatency);
device->WaveConfirm(device, wave);
}

View File

@@ -32,6 +32,7 @@
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/print.h>
#include <winpr/cmdline.h>
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
@@ -195,7 +196,7 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
*/
wSamples = (size * 8) / format->wBitsPerSample;
mstime = ((wSamples * 1000) / format->nSamplesPerSec);
mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels);
return mstime;
}
@@ -253,8 +254,8 @@ void rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
UINT16 wNumberOfFormats;
AUDIO_FORMAT* clientFormat;
dwVolumeLeft = (0xFFFF / 2); /* 50% ? */
dwVolumeRight = (0xFFFF / 2); /* 50% ? */
dwVolumeLeft = (0xFFFF); /* 100% */
dwVolumeRight = (0xFFFF); /* 100% */
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
wNumberOfFormats = rdpsnd->NumberOfClientFormats;
@@ -391,6 +392,8 @@ static void rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, STREAM* s, UINT16 Bo
rdpsnd->isOpen = TRUE;
rdpsnd->wCurrentFormatNo = wFormatNo;
rdpsnd_print_audio_format(format);
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->Open, rdpsnd->device, format, rdpsnd->latency);

View File

@@ -31,6 +31,11 @@
#include <freerdp/codec/dsp.h>
/**
* Microsoft Multimedia Standards Update
* http://download.microsoft.com/download/9/8/6/9863C72A-A3AA-4DDB-B1BA-CA8D17EFD2D4/RIFFNEW.pdf
*/
static void freerdp_dsp_resample(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int bytes_per_sample,
UINT32 schan, UINT32 srate, int sframes,
@@ -57,12 +62,16 @@ static void freerdp_dsp_resample(FREERDP_DSP_CONTEXT* context,
dst = context->resampled_buffer;
p = dst;
for (i = 0; i < rframes; i++)
{
n1 = i * srate / rrate;
if (n1 >= sframes)
n1 = sframes - 1;
n2 = (n1 * rrate == i * srate || n1 == sframes - 1 ? n1 : n1 + 1);
for (j = 0; j < rbytes; j++)
{
/* Nearest Interpolation, probably the easiest, but works */
@@ -109,7 +118,9 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm,
INT32 d;
ss = ima_step_size_table[adpcm->ima.last_step[channel]];
d = (ss >> 3);
if (sample & 1)
d += (ss >> 2);
if (sample & 2)
@@ -118,6 +129,7 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm,
d += ss;
if (sample & 8)
d = -d;
d += adpcm->ima.last_sample[channel];
if (d < -32768)
@@ -128,6 +140,7 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm,
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)
@@ -147,12 +160,15 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
int i;
out_size = size * 4;
if (out_size > context->adpcm_maxlength)
{
context->adpcm_maxlength = out_size + 1024;
context->adpcm_buffer = realloc(context->adpcm_buffer, context->adpcm_maxlength);
}
dst = context->adpcm_buffer;
while (size > 0)
{
if (size % block_size == 0)
@@ -162,6 +178,7 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
src += 4;
size -= 4;
out_size -= 16;
if (channels > 1)
{
context->adpcm.ima.last_sample[1] = (INT16) (((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
@@ -178,27 +195,32 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
{
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)] = (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) + 4] = (decoded & 0xFF);
dst[((i & 3) << 3) + (channel << 1) + 5] = (decoded >> 8);
src++;
}
dst += 32;
size -= 8;
}
else
{
sample = ((*src) & 0x0f);
decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
*dst++ = (decoded & 0xff);
*dst++ = (decoded & 0xFF);
*dst++ = (decoded >> 8);
sample = ((*src) >> 4);
decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
*dst++ = (decoded & 0xff);
*dst++ = (decoded & 0xFF);
*dst++ = (decoded >> 8);
src++;
size--;
@@ -239,8 +261,7 @@ static const struct
{ 7, 4 }
};
static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm,
int channel, INT16 sample)
static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
{
INT32 e;
INT32 d;
@@ -252,23 +273,29 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm,
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;
@@ -281,13 +308,16 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm,
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)
@@ -299,31 +329,35 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm,
static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int size, int channels, int block_size)
{
int i;
BYTE* dst;
INT16 sample;
BYTE encoded;
UINT32 out_size;
int i;
out_size = size / 2;
if (out_size > context->adpcm_maxlength)
{
context->adpcm_maxlength = out_size + 1024;
context->adpcm_buffer = realloc(context->adpcm_buffer, context->adpcm_maxlength);
}
dst = context->adpcm_buffer;
while (size > 0)
{
if ((dst - context->adpcm_buffer) % block_size == 0)
{
*dst++ = context->adpcm.ima.last_sample[0] & 0xff;
*dst++ = (context->adpcm.ima.last_sample[0] >> 8) & 0xff;
*dst++ = context->adpcm.ima.last_sample[0] & 0xFF;
*dst++ = (context->adpcm.ima.last_sample[0] >> 8) & 0xFF;
*dst++ = (BYTE) context->adpcm.ima.last_step[0];
*dst++ = 0;
if (channels > 1)
{
*dst++ = context->adpcm.ima.last_sample[1] & 0xff;
*dst++ = (context->adpcm.ima.last_sample[1] >> 8) & 0xff;
*dst++ = context->adpcm.ima.last_sample[1] & 0xFF;
*dst++ = (context->adpcm.ima.last_sample[1] >> 8) & 0xFF;
*dst++ = (BYTE) context->adpcm.ima.last_step[1];
*dst++ = 0;
}
@@ -331,7 +365,8 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
if (channels > 1)
{
memset(dst, 0, 8);
ZeroMemory(dst, 8);
for (i = 0; i < 16; i++)
{
sample = (INT16) (((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8));
@@ -339,6 +374,7 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
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;
}
dst += 8;
size -= 32;
}
@@ -364,40 +400,46 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context,
* http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM
*/
static const INT16 ms_adpcm_adaptation_table[] =
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 INT16 ms_adpcm_coeff1_table[] =
static const INT32 ms_adpcm_coeffs1[7] =
{
256, 512, 0, 192, 240, 460, 392
};
static const INT16 ms_adpcm_coeff2_table[] =
static const INT32 ms_adpcm_coeffs2[7] =
{
0, -256, 0, 64, 0, -208, -232
};
static INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample, int channel)
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 : 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;
nibble = (sample & 0x08 ? (INT8) sample - 16 : 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;
}
@@ -409,12 +451,15 @@ static void freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
UINT32 out_size;
out_size = size * 4;
if (out_size > context->adpcm_maxlength)
{
context->adpcm_maxlength = out_size + 1024;
context->adpcm_buffer = realloc(context->adpcm_buffer, context->adpcm_maxlength);
}
dst = context->adpcm_buffer;
while (size > 0)
{
if (size % block_size == 0)
@@ -423,43 +468,43 @@ static void freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
{
context->adpcm.ms.predictor[0] = *src++;
context->adpcm.ms.predictor[1] = *src++;
context->adpcm.ms.delta[0] = *((INT16*)src);
context->adpcm.ms.delta[0] = *((INT16*) src);
src += 2;
context->adpcm.ms.delta[1] = *((INT16*)src);
context->adpcm.ms.delta[1] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample1[0] = *((INT16*)src);
context->adpcm.ms.sample1[0] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample1[1] = *((INT16*)src);
context->adpcm.ms.sample1[1] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample2[0] = *((INT16*)src);
context->adpcm.ms.sample2[0] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample2[1] = *((INT16*)src);
context->adpcm.ms.sample2[1] = *((INT16*) src);
src += 2;
size -= 14;
*((INT16*)dst) = context->adpcm.ms.sample2[0];
*((INT16*) dst) = context->adpcm.ms.sample2[0];
dst += 2;
*((INT16*)dst) = context->adpcm.ms.sample2[1];
*((INT16*) dst) = context->adpcm.ms.sample2[1];
dst += 2;
*((INT16*)dst) = context->adpcm.ms.sample1[0];
*((INT16*) dst) = context->adpcm.ms.sample1[0];
dst += 2;
*((INT16*)dst) = context->adpcm.ms.sample1[1];
*((INT16*) dst) = context->adpcm.ms.sample1[1];
dst += 2;
}
else
{
context->adpcm.ms.predictor[0] = *src++;
context->adpcm.ms.delta[0] = *((INT16*)src);
context->adpcm.ms.delta[0] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample1[0] = *((INT16*)src);
context->adpcm.ms.sample1[0] = *((INT16*) src);
src += 2;
context->adpcm.ms.sample2[0] = *((INT16*)src);
context->adpcm.ms.sample2[0] = *((INT16*) src);
src += 2;
size -= 7;
*((INT16*)dst) = context->adpcm.ms.sample2[0];
*((INT16*) dst) = context->adpcm.ms.sample2[0];
dst += 2;
*((INT16*)dst) = context->adpcm.ms.sample1[0];
*((INT16*) dst) = context->adpcm.ms.sample1[0];
dst += 2;
}
}
@@ -468,25 +513,25 @@ static void freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
{
sample = *src++;
size--;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
dst += 2;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1);
dst += 2;
sample = *src++;
size--;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
dst += 2;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1);
dst += 2;
}
else
{
sample = *src++;
size--;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0);
dst += 2;
*((INT16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 0);
*((INT16*) dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 0);
dst += 2;
}
}
@@ -499,26 +544,33 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c
INT32 presample;
INT32 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;
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;
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;
return ((BYTE) errordelta) & 0x0F;
}
static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
@@ -529,15 +581,18 @@ static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
UINT32 out_size;
out_size = size / 2;
if (out_size > context->adpcm_maxlength)
{
context->adpcm_maxlength = out_size + 1024;
context->adpcm_buffer = realloc(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;
@@ -549,10 +604,10 @@ static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
{
*dst++ = context->adpcm.ms.predictor[0];
*dst++ = context->adpcm.ms.predictor[1];
*dst++ = (BYTE) (context->adpcm.ms.delta[0] & 0xff);
*dst++ = (BYTE) ((context->adpcm.ms.delta[0] >> 8) & 0xff);
*dst++ = (BYTE) (context->adpcm.ms.delta[1] & 0xff);
*dst++ = (BYTE) ((context->adpcm.ms.delta[1] >> 8) & 0xff);
*dst++ = (BYTE) (context->adpcm.ms.delta[0] & 0xFF);
*dst++ = (BYTE) ((context->adpcm.ms.delta[0] >> 8) & 0xFF);
*dst++ = (BYTE) (context->adpcm.ms.delta[1] & 0xFF);
*dst++ = (BYTE) ((context->adpcm.ms.delta[1] >> 8) & 0xFF);
context->adpcm.ms.sample1[0] = *((INT16*) (src + 4));
context->adpcm.ms.sample1[1] = *((INT16*) (src + 6));
context->adpcm.ms.sample2[0] = *((INT16*) (src + 0));
@@ -568,8 +623,8 @@ static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context,
else
{
*dst++ = context->adpcm.ms.predictor[0];
*dst++ = (BYTE) (context->adpcm.ms.delta[0] & 0xff);
*dst++ = (BYTE) ((context->adpcm.ms.delta[0] >> 8) & 0xff);
*dst++ = (BYTE) (context->adpcm.ms.delta[0] & 0xFF);
*dst++ = (BYTE) ((context->adpcm.ms.delta[0] >> 8) & 0xFF);
context->adpcm.ms.sample1[0] = *((INT16*) (src + 2));
context->adpcm.ms.sample2[0] = *((INT16*) (src + 0));
*((INT16*) (dst + 0)) = (INT16) context->adpcm.ms.sample1[0];
@@ -615,8 +670,10 @@ void freerdp_dsp_context_free(FREERDP_DSP_CONTEXT* context)
{
if (context->resampled_buffer)
free(context->resampled_buffer);
if (context->adpcm_buffer)
free(context->adpcm_buffer);
free(context);
}
}