/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* For now just use speex, can add more resamplers later. */ #include <speex/speex_resampler.h> #include <sys/param.h> #include <syslog.h> #include "cras_fmt_conv.h" #include "cras_audio_format.h" #include "cras_util.h" #include "linear_resampler.h" /* The quality level is a value between 0 and 10. This is a tradeoff between * performance, latency, and quality. */ #define SPEEX_QUALITY_LEVEL 4 /* Max number of converters, src, down/up mix, 2xformat, and linear resample. */ #define MAX_NUM_CONVERTERS 5 /* Channel index for stereo. */ #define STEREO_L 0 #define STEREO_R 1 typedef void (*sample_format_converter_t)(const uint8_t *in, size_t in_samples, uint8_t *out); typedef size_t (*channel_converter_t)(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out); /* Member data for the resampler. */ struct cras_fmt_conv { SpeexResamplerState *speex_state; channel_converter_t channel_converter; float **ch_conv_mtx; /* Coefficient matrix for mixing channels. */ sample_format_converter_t in_format_converter; sample_format_converter_t out_format_converter; struct linear_resampler *resampler; struct cras_audio_format in_fmt; struct cras_audio_format out_fmt; uint8_t *tmp_bufs[MAX_NUM_CONVERTERS - 1]; size_t tmp_buf_frames; size_t pre_linear_resample; size_t num_converters; /* Incremented once for SRC, channel, format. */ }; /* Add and clip two s16 samples. */ static int16_t s16_add_and_clip(int16_t a, int16_t b) { int32_t sum; sum = a + b; sum = MAX(sum, -0x8000); sum = MIN(sum, 0x7fff); return (int16_t)sum; } /* * Convert between different sample formats. */ /* Converts from U8 to S16. */ static void convert_u8_to_s16le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; uint16_t *_out = (uint16_t *)out; for (i = 0; i < in_samples; i++, in++, _out++) *_out = ((int16_t)*in - 0x80) << 8; } /* Converts from S24 to S16. */ static void convert_s24le_to_s16le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int32_t *_in = (int32_t *)in; uint16_t *_out = (uint16_t *)out; for (i = 0; i < in_samples; i++, _in++, _out++) *_out = (int16_t)((*_in & 0x00ffffff) >> 8); } /* Converts from S32 to S16. */ static void convert_s32le_to_s16le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int32_t *_in = (int32_t *)in; uint16_t *_out = (uint16_t *)out; for (i = 0; i < in_samples; i++, _in++, _out++) *_out = (int16_t)(*_in >> 16); } /* Converts from S24_3LE to S16. */ static void convert_s243le_to_s16le(const uint8_t *in, size_t in_samples, uint8_t *out) { /* find how to calculate in and out size, implement the conversion * between S24_3LE and S16 */ size_t i; int8_t *_in = (int8_t *)in; uint16_t *_out = (uint16_t *)out; for (i = 0; i < in_samples; i++, _in += 3, _out++) memcpy(_out, _in + 1, 2); } /* Converts from S16 to U8. */ static void convert_s16le_to_u8(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int16_t *_in = (int16_t *)in; for (i = 0; i < in_samples; i++, _in++, out++) *out = (uint8_t)(*_in >> 8) + 128; } /* Converts from S16 to S24. */ static void convert_s16le_to_s24le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int16_t *_in = (int16_t *)in; uint32_t *_out = (uint32_t *)out; for (i = 0; i < in_samples; i++, _in++, _out++) *_out = ((int32_t)*_in << 8); } /* Converts from S16 to S32. */ static void convert_s16le_to_s32le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int16_t *_in = (int16_t *)in; uint32_t *_out = (uint32_t *)out; for (i = 0; i < in_samples; i++, _in++, _out++) *_out = ((int32_t)*_in << 16); } /* Converts from S16 to S24_3LE. */ static void convert_s16le_to_s243le(const uint8_t *in, size_t in_samples, uint8_t *out) { size_t i; int16_t *_in = (int16_t *)in; uint8_t *_out = (uint8_t *)out; for (i = 0; i < in_samples; i++, _in++, _out += 3) { *_out = 0; memcpy(_out + 1, _in, 2); } } /* * Convert between different channel numbers. */ /* Converts S16 mono to S16 stereo. The out buffer must be double the size of * the input buffer. */ static size_t s16_mono_to_stereo(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { size_t i; for (i = 0; i < in_frames; i++) { out[2 * i] = in[i]; out[2 * i + 1] = in[i]; } return in_frames; } /* Converts S16 Stereo to S16 mono. The output buffer only need be big enough * for mono samples. */ static size_t s16_stereo_to_mono(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { size_t i; for (i = 0; i < in_frames; i++) out[i] = s16_add_and_clip(in[2 * i], in[2 * i + 1]); return in_frames; } /* Converts S16 mono to 5.1 surround. Fit mono to front center of the * output, or split to front left/right if front center is missing * from the output channel layout. */ static size_t s16_mono_to_51(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { size_t i, left, right, center; memset(out, 0, sizeof(*out) * 6 * in_frames); left = conv->out_fmt.channel_layout[CRAS_CH_FL]; right = conv->out_fmt.channel_layout[CRAS_CH_FR]; center = conv->out_fmt.channel_layout[CRAS_CH_FC]; if (center != -1) for (i = 0; i < in_frames; i++) out[6 * i + center] = in[i]; else if (left != -1 && right != -1) for (i = 0; i < in_frames; i++) { out[6 * i + right] = in[i] / 2; out[6 * 1 + left] = in[i] / 2; } else /* Select the first channel to convert to as the * default behavior. */ for (i = 0; i < in_frames; i++) out[6 * i] = in[i]; return in_frames; } /* Converts S16 stereo to 5.1 surround. Fit the left/right of input * to the front left/right of output respectively and fill others * with zero. If any of the front left/right is missed from the output * channel layout, mix to front center. */ static size_t s16_stereo_to_51(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { size_t i, left, right, center; memset(out, 0, sizeof(*out) * 6 * in_frames); left = conv->out_fmt.channel_layout[CRAS_CH_FL]; right = conv->out_fmt.channel_layout[CRAS_CH_FR]; center = conv->out_fmt.channel_layout[CRAS_CH_FC]; if (left != -1 && right != -1) for (i = 0; i < in_frames; i++) { out[6 * i + left] = in[2 * i]; out[6 * i + right] = in[2 * i + 1]; } else if (center != -1) for (i = 0; i < in_frames; i++) out[6 * i + center] = s16_add_and_clip( in[2 * i], in[2 * i + 1]); else /* Select the first two channels to convert to as the * default behavior. */ for (i = 0; i < in_frames; i++) { out[6 * i] = in[2 * i]; out[6 * i + 1] = in[2 * i + 1]; } return in_frames; } /* Converts S16 5.1 to S16 stereo. The out buffer can have room for just * stereo samples. This convert function is used as the default behavior * when channel layout is not set from the client side. */ static size_t s16_51_to_stereo(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { static const unsigned int left_idx = 0; static const unsigned int right_idx = 1; /* static const unsigned int left_surround_idx = 2; */ /* static const unsigned int right_surround_idx = 3; */ static const unsigned int center_idx = 4; /* static const unsigned int lfe_idx = 5; */ size_t i; for (i = 0; i < in_frames; i++) { unsigned int half_center; half_center = in[6 * i + center_idx] / 2; out[2 * i + left_idx] = s16_add_and_clip(in[6 * i + left_idx], half_center); out[2 * i + right_idx] = s16_add_and_clip(in[6 * i + right_idx], half_center); } return in_frames; } /* Converts S16 N channels to S16 M channels. The out buffer must have room for * M channel. This convert function is used as the default behavior when channel * layout is not set from the client side. */ static size_t s16_default_all_to_all(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { unsigned int num_in_ch = conv->in_fmt.num_channels; unsigned int num_out_ch = conv->out_fmt.num_channels; unsigned int in_ch, out_ch, i; memset(out, 0, num_out_ch * in_frames * cras_get_format_bytes(&conv->out_fmt)); for (out_ch = 0; out_ch < num_out_ch; out_ch++) { for (in_ch = 0; in_ch < num_in_ch; in_ch++) { for (i = 0; i < in_frames; i++) { out[out_ch + i * num_out_ch] += in[in_ch + i * num_in_ch] / num_in_ch; } } } return in_frames; } static int is_channel_layout_equal(const struct cras_audio_format *a, const struct cras_audio_format *b) { int ch; for (ch = 0; ch < CRAS_CH_MAX; ch++) if (a->channel_layout[ch] != b->channel_layout[ch]) return 0; return 1; } /* Multiplies buffer vector with coefficient vector. */ static int16_t multiply_buf_with_coef(float *coef, const int16_t *buf, size_t size) { int32_t sum = 0; int i; for (i = 0; i < size; i++) sum += coef[i] * buf[i]; sum = MAX(sum, -0x8000); sum = MIN(sum, 0x7fff); return (int16_t)sum; } static void normalize_buf(float *buf, size_t size) { int i; float squre_sum = 0.0; for (i = 0; i < size; i++) squre_sum += buf[i] * buf[i]; for (i = 0; i < size; i ++) buf[i] /= squre_sum; } /* Converts channels based on the channel conversion * coefficient matrix. */ static size_t convert_channels(struct cras_fmt_conv *conv, const int16_t *in, size_t in_frames, int16_t *out) { unsigned i, fr; unsigned in_idx = 0; unsigned out_idx = 0; for (fr = 0; fr < in_frames; fr++) { for (i = 0; i < conv->out_fmt.num_channels; i++) out[out_idx + i] = multiply_buf_with_coef( conv->ch_conv_mtx[i], &in[in_idx], conv->in_fmt.num_channels); in_idx += conv->in_fmt.num_channels; out_idx += conv->out_fmt.num_channels; } return in_frames; } /* Populates the down mix matrix by rules: * 1. Front/side left(right) channel will mix to left(right) of * full scale. * 2. Center and LFE will be split equally to left and right. * Rear * 3. Rear left/right will split 1/4 of the power to opposite * channel. */ static void surround51_to_stereo_downmix_mtx(float **mtx, int8_t layout[CRAS_CH_MAX]) { if (layout[CRAS_CH_FC] != -1) { mtx[STEREO_L][layout[CRAS_CH_FC]] = 0.707; mtx[STEREO_R][layout[CRAS_CH_FC]] = 0.707; } if (layout[CRAS_CH_FL] != -1 && layout[CRAS_CH_FR] != -1) { mtx[STEREO_L][layout[CRAS_CH_FL]] = 1.0; mtx[STEREO_R][layout[CRAS_CH_FR]] = 1.0; } if (layout[CRAS_CH_SL] != -1 && layout[CRAS_CH_SR] != -1) { mtx[STEREO_L][layout[CRAS_CH_SL]] = 1.0; mtx[STEREO_R][layout[CRAS_CH_SR]] = 1.0; } if (layout[CRAS_CH_RL] != -1 && layout[CRAS_CH_RR] != -1) { /* Split 1/4 power to the other side */ mtx[STEREO_L][layout[CRAS_CH_RL]] = 0.866; mtx[STEREO_R][layout[CRAS_CH_RL]] = 0.5; mtx[STEREO_R][layout[CRAS_CH_RR]] = 0.866; mtx[STEREO_L][layout[CRAS_CH_RR]] = 0.5; } if (layout[CRAS_CH_LFE] != -1) { mtx[STEREO_L][layout[CRAS_CH_LFE]] = 0.707; mtx[STEREO_R][layout[CRAS_CH_LFE]] = 0.707; } normalize_buf(mtx[STEREO_L], 6); normalize_buf(mtx[STEREO_R], 6); } /* * Exported interface */ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, const struct cras_audio_format *out, size_t max_frames, size_t pre_linear_resample) { struct cras_fmt_conv *conv; int rc; unsigned i; conv = calloc(1, sizeof(*conv)); if (conv == NULL) return NULL; conv->in_fmt = *in; conv->out_fmt = *out; conv->tmp_buf_frames = max_frames; conv->pre_linear_resample = pre_linear_resample; /* Set up sample format conversion. */ /* TODO(dgreid) - modify channel and sample rate conversion so * converting to s16 isnt necessary. */ if (in->format != SND_PCM_FORMAT_S16_LE) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from format %d to %d.", in->format, out->format); switch(in->format) { case SND_PCM_FORMAT_U8: conv->in_format_converter = convert_u8_to_s16le; break; case SND_PCM_FORMAT_S24_LE: conv->in_format_converter = convert_s24le_to_s16le; break; case SND_PCM_FORMAT_S32_LE: conv->in_format_converter = convert_s32le_to_s16le; break; case SND_PCM_FORMAT_S24_3LE: conv->in_format_converter = convert_s243le_to_s16le; break; default: syslog(LOG_WARNING, "Invalid format %d", in->format); cras_fmt_conv_destroy(conv); return NULL; } } if (out->format != SND_PCM_FORMAT_S16_LE) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from format %d to %d.", in->format, out->format); switch (out->format) { case SND_PCM_FORMAT_U8: conv->out_format_converter = convert_s16le_to_u8; break; case SND_PCM_FORMAT_S24_LE: conv->out_format_converter = convert_s16le_to_s24le; break; case SND_PCM_FORMAT_S32_LE: conv->out_format_converter = convert_s16le_to_s32le; break; case SND_PCM_FORMAT_S24_3LE: conv->out_format_converter = convert_s16le_to_s243le; break; default: syslog(LOG_WARNING, "Invalid format %d", out->format); cras_fmt_conv_destroy(conv); return NULL; } } /* Set up channel number conversion. */ if (in->num_channels != out->num_channels) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from %zu to %zu channels.", in->num_channels, out->num_channels); /* Populate the conversion matrix base on in/out channel count * and layout. */ if (in->num_channels == 1 && out->num_channels == 2) { conv->channel_converter = s16_mono_to_stereo; } else if (in->num_channels == 1 && out->num_channels == 6) { conv->channel_converter = s16_mono_to_51; } else if (in->num_channels == 2 && out->num_channels == 1) { conv->channel_converter = s16_stereo_to_mono; } else if (in->num_channels == 2 && out->num_channels == 6) { conv->channel_converter = s16_stereo_to_51; } else if (in->num_channels == 6 && out->num_channels == 2) { int in_channel_layout_set = 0; /* Checks if channel_layout is set in the incoming format */ for (i = 0; i < CRAS_CH_MAX; i++) if (in->channel_layout[i] != -1) in_channel_layout_set = 1; /* Use the conversion matrix based converter when a * channel layout is set, or default to use existing * converter to downmix to stereo */ if (in_channel_layout_set) { conv->ch_conv_mtx = cras_channel_conv_matrix_alloc( in->num_channels, out->num_channels); if (conv->ch_conv_mtx == NULL) { cras_fmt_conv_destroy(conv); return NULL; } conv->channel_converter = convert_channels; surround51_to_stereo_downmix_mtx( conv->ch_conv_mtx, conv->in_fmt.channel_layout); } else { conv->channel_converter = s16_51_to_stereo; } } else { syslog(LOG_WARNING, "Using default channel map for %zu to %zu", in->num_channels, out->num_channels); conv->channel_converter = s16_default_all_to_all; } } else if (in->num_channels > 2 && !is_channel_layout_equal(in, out)){ conv->num_converters++; conv->ch_conv_mtx = cras_channel_conv_matrix_create(in, out); if (conv->ch_conv_mtx == NULL) { syslog(LOG_ERR, "Failed to create channel conversion matrix"); cras_fmt_conv_destroy(conv); return NULL; } conv->channel_converter = convert_channels; } /* Set up sample rate conversion. */ if (in->frame_rate != out->frame_rate) { conv->num_converters++; syslog(LOG_DEBUG, "Convert from %zu to %zu Hz.", in->frame_rate, out->frame_rate); conv->speex_state = speex_resampler_init(out->num_channels, in->frame_rate, out->frame_rate, SPEEX_QUALITY_LEVEL, &rc); if (conv->speex_state == NULL) { syslog(LOG_ERR, "Fail to create speex:%zu %zu %zu %d", out->num_channels, in->frame_rate, out->frame_rate, rc); cras_fmt_conv_destroy(conv); return NULL; } } /* Set up linear resampler. */ conv->num_converters++; conv->resampler = linear_resampler_create( out->num_channels, cras_get_format_bytes(out), out->frame_rate, out->frame_rate); if (conv->resampler == NULL) { syslog(LOG_ERR, "Fail to create linear resampler"); cras_fmt_conv_destroy(conv); return NULL; } /* Need num_converters-1 temp buffers, the final converter renders * directly into the output. */ for (i = 0; i < conv->num_converters - 1; i++) { conv->tmp_bufs[i] = malloc( max_frames * 4 * /* width in bytes largest format. */ MAX(in->num_channels, out->num_channels)); if (conv->tmp_bufs[i] == NULL) { cras_fmt_conv_destroy(conv); return NULL; } } assert(conv->num_converters <= MAX_NUM_CONVERTERS); return conv; } void cras_fmt_conv_destroy(struct cras_fmt_conv *conv) { unsigned i; if (conv->ch_conv_mtx) cras_channel_conv_matrix_destroy(conv->ch_conv_mtx, conv->out_fmt.num_channels); if (conv->speex_state) speex_resampler_destroy(conv->speex_state); if (conv->resampler) linear_resampler_destroy(conv->resampler); for (i = 0; i < MAX_NUM_CONVERTERS - 1; i++) free(conv->tmp_bufs[i]); free(conv); } struct cras_fmt_conv *cras_channel_remix_conv_create( unsigned int num_channels, const float *coefficient) { struct cras_fmt_conv *conv; unsigned out_ch, in_ch; conv = calloc(1, sizeof(*conv)); if (conv == NULL) return NULL; conv->in_fmt.num_channels = num_channels; conv->out_fmt.num_channels = num_channels; conv->ch_conv_mtx = cras_channel_conv_matrix_alloc(num_channels, num_channels); /* Convert the coeffiencnt array to conversion matrix. */ for (out_ch = 0; out_ch < num_channels; out_ch++) for (in_ch = 0; in_ch < num_channels; in_ch++) conv->ch_conv_mtx[out_ch][in_ch] = coefficient[in_ch + out_ch * num_channels]; conv->num_converters = 1; conv->tmp_bufs[0] = malloc(4 * /* width in bytes largest format. */ num_channels); return conv; } void cras_channel_remix_convert(struct cras_fmt_conv *conv, const struct cras_audio_format *fmt, uint8_t *in_buf, size_t nframes) { unsigned ch, fr; int16_t *tmp = (int16_t *)conv->tmp_bufs[0]; int16_t *buf = (int16_t *)in_buf; /* Do remix only when input buffer has the same number of channels. */ if (fmt->num_channels != conv->in_fmt.num_channels) return; for (fr = 0; fr < nframes; fr++) { for (ch = 0; ch < conv->in_fmt.num_channels; ch++) tmp[ch] = multiply_buf_with_coef( conv->ch_conv_mtx[ch], buf, conv->in_fmt.num_channels); for (ch = 0; ch < conv->in_fmt.num_channels; ch++) buf[ch] = tmp[ch]; buf += conv->in_fmt.num_channels; } } const struct cras_audio_format *cras_fmt_conv_in_format( const struct cras_fmt_conv *conv) { return &conv->in_fmt; } const struct cras_audio_format *cras_fmt_conv_out_format( const struct cras_fmt_conv *conv) { return &conv->out_fmt; } size_t cras_fmt_conv_in_frames_to_out(struct cras_fmt_conv *conv, size_t in_frames) { if (!conv) return in_frames; if (conv->pre_linear_resample) in_frames = linear_resampler_in_frames_to_out( conv->resampler, in_frames); in_frames = cras_frames_at_rate(conv->in_fmt.frame_rate, in_frames, conv->out_fmt.frame_rate); if (!conv->pre_linear_resample) in_frames = linear_resampler_in_frames_to_out( conv->resampler, in_frames); return in_frames; } size_t cras_fmt_conv_out_frames_to_in(struct cras_fmt_conv *conv, size_t out_frames) { if (!conv) return out_frames; if (!conv->pre_linear_resample) out_frames = linear_resampler_out_frames_to_in( conv->resampler, out_frames); out_frames = cras_frames_at_rate(conv->out_fmt.frame_rate, out_frames, conv->in_fmt.frame_rate); if (conv->pre_linear_resample) out_frames = linear_resampler_out_frames_to_in( conv->resampler, out_frames); return out_frames; } void cras_fmt_conv_set_linear_resample_rates(struct cras_fmt_conv *conv, float from, float to) { linear_resampler_set_rates(conv->resampler, from, to); } size_t cras_fmt_conv_convert_frames(struct cras_fmt_conv *conv, const uint8_t *in_buf, uint8_t *out_buf, unsigned int *in_frames, size_t out_frames) { uint32_t fr_in, fr_out; uint8_t *buffers[MAX_NUM_CONVERTERS + 1]; /* converters + out buffer. */ size_t buf_idx = 0; static int logged_frames_dont_fit; unsigned int used_converters = conv->num_converters; unsigned int post_linear_resample = 0; unsigned int pre_linear_resample = 0; unsigned int linear_resample_fr = 0; assert(conv); if (linear_resampler_needed(conv->resampler)) { post_linear_resample = !conv->pre_linear_resample; pre_linear_resample = conv->pre_linear_resample; } /* If no SRC, then in_frames should = out_frames. */ if (conv->speex_state == NULL) { fr_in = MIN(*in_frames, out_frames); if (out_frames < *in_frames && !logged_frames_dont_fit) { syslog(LOG_INFO, "fmt_conv: %u to %zu no SRC.", *in_frames, out_frames); logged_frames_dont_fit = 1; } } else { fr_in = *in_frames; } fr_out = fr_in; /* Set up a chain of buffers. The output buffer of the first conversion * is used as input to the second and so forth, ending in the output * buffer. */ if (!linear_resampler_needed(conv->resampler)) used_converters--; buffers[4] = (uint8_t *)conv->tmp_bufs[3]; buffers[3] = (uint8_t *)conv->tmp_bufs[2]; buffers[2] = (uint8_t *)conv->tmp_bufs[1]; buffers[1] = (uint8_t *)conv->tmp_bufs[0]; buffers[0] = (uint8_t *)in_buf; buffers[used_converters] = out_buf; if (pre_linear_resample) { linear_resample_fr = fr_in; unsigned resample_limit = out_frames; /* If there is a 2nd fmt conversion we should convert the * resample limit and round it to the lower bound in order * not to convert too many frames in the pre linear resampler. */ if (conv->speex_state != NULL) resample_limit = resample_limit * conv->in_fmt.frame_rate / conv->out_fmt.frame_rate; resample_limit = MIN(resample_limit, conv->tmp_buf_frames); fr_in = linear_resampler_resample( conv->resampler, buffers[buf_idx], &linear_resample_fr, buffers[buf_idx + 1], resample_limit); buf_idx++; } /* If the input format isn't S16_LE convert to it. */ if (conv->in_fmt.format != SND_PCM_FORMAT_S16_LE) { conv->in_format_converter(buffers[buf_idx], fr_in * conv->in_fmt.num_channels, (uint8_t *)buffers[buf_idx + 1]); buf_idx++; } /* Then channel conversion. */ if (conv->channel_converter != NULL) { conv->channel_converter(conv, (int16_t *)buffers[buf_idx], fr_in, (int16_t *)buffers[buf_idx + 1]); buf_idx++; } /* Then SRC. */ if (conv->speex_state != NULL) { unsigned int out_limit = out_frames; if (post_linear_resample) out_limit = linear_resampler_out_frames_to_in( conv->resampler, out_limit); fr_out = cras_frames_at_rate(conv->in_fmt.frame_rate, fr_in, conv->out_fmt.frame_rate); if (fr_out > out_frames + 1 && !logged_frames_dont_fit) { syslog(LOG_INFO, "fmt_conv: put %u frames in %zu sized buffer", fr_out, out_frames); logged_frames_dont_fit = 1; } /* limit frames to the output size. */ fr_out = MIN(fr_out, out_limit); speex_resampler_process_interleaved_int( conv->speex_state, (int16_t *)buffers[buf_idx], &fr_in, (int16_t *)buffers[buf_idx + 1], &fr_out); buf_idx++; } if (post_linear_resample) { linear_resample_fr = fr_out; unsigned resample_limit = MIN(conv->tmp_buf_frames, out_frames); fr_out = linear_resampler_resample( conv->resampler, buffers[buf_idx], &linear_resample_fr, buffers[buf_idx + 1], resample_limit); buf_idx++; } /* If the output format isn't S16_LE convert to it. */ if (conv->out_fmt.format != SND_PCM_FORMAT_S16_LE) { conv->out_format_converter(buffers[buf_idx], fr_out * conv->out_fmt.num_channels, (uint8_t *)buffers[buf_idx + 1]); buf_idx++; } if (pre_linear_resample) *in_frames = linear_resample_fr; else *in_frames = fr_in; return fr_out; } int cras_fmt_conversion_needed(const struct cras_fmt_conv *conv) { return linear_resampler_needed(conv->resampler) || (conv->num_converters > 1); } /* If the server cannot provide the requested format, configures an audio format * converter that handles transforming the input format to the format used by * the server. */ int config_format_converter(struct cras_fmt_conv **conv, enum CRAS_STREAM_DIRECTION dir, const struct cras_audio_format *from, const struct cras_audio_format *to, unsigned int frames) { struct cras_audio_format target; /* For input, preserve the channel count and layout of * from format */ if (dir == CRAS_STREAM_INPUT) { target = *from; target.format = to->format; target.frame_rate = to->frame_rate; } else { target = *to; } syslog(LOG_DEBUG, "format convert: from:%d %zu %zu target: %d %zu %zu " "frames = %u", from->format, from->frame_rate, from->num_channels, target.format, target.frame_rate, target.num_channels, frames); *conv = cras_fmt_conv_create(from, &target, frames, (dir == CRAS_STREAM_INPUT)); if (!*conv) { syslog(LOG_ERR, "Failed to create format converter"); return -ENOMEM; } return 0; }