summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2012-03-30 15:51:03 -0700
committerEric Laurent <elaurent@google.com>2012-04-10 19:52:43 -0700
commit91950263ed345336b46caa0a78fec960b7ee6493 (patch)
tree99f5a8c45627eccc476db0ceed6aed8d9908d516 /audio
parent3585c8b3bc8c28c0c47b03d832a9295e66b42d12 (diff)
downloaddevice_samsung_tuna-91950263ed345336b46caa0a78fec960b7ee6493.zip
device_samsung_tuna-91950263ed345336b46caa0a78fec960b7ee6493.tar.gz
device_samsung_tuna-91950263ed345336b46caa0a78fec960b7ee6493.tar.bz2
audio: add dual mic support for pre processing
Added support for audio pre processing libraries implementing dual mic solutions. When a pre processor is enabled, its multi channel capabilities are queried and compared to capture channel combinations supported by the device and other enabled pre processings. The most favorable configuration is chosen and pcm capture driver is restarted with the appropriate channel config. Also made various capture and process buffers naming and allocation more consistent. Change-Id: I90be4798951d0a34dc77d6bdc93ef15cad3ff5af
Diffstat (limited to 'audio')
-rw-r--r--audio/audio_hw.c685
1 files changed, 584 insertions, 101 deletions
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index a26ea14..0a0d9c6 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -39,6 +39,7 @@
#include "ril_interface.h"
+
/* Mixer control names */
#define MIXER_DL2_LEFT_EQUALIZER "DL2 Left Equalizer"
#define MIXER_DL2_RIGHT_EQUALIZER "DL2 Right Equalizer"
@@ -399,6 +400,38 @@ struct route_setting mm_ul2_amic_right[] = {
},
};
+/* dual mic configuration with main mic on main channel and sub mic on aux channel.
+ * Used for handset mode (near talk) */
+struct route_setting mm_ul2_amic_dual_main_sub[] = {
+ {
+ .ctl_name = MIXER_MUX_UL10,
+ .strval = MIXER_AMIC0,
+ },
+ {
+ .ctl_name = MIXER_MUX_UL11,
+ .strval = MIXER_AMIC1,
+ },
+ {
+ .ctl_name = NULL,
+ },
+};
+
+/* dual mic configuration with sub mic on main channel and main mic on aux channel.
+ * Used for speakerphone mode (far talk) */
+struct route_setting mm_ul2_amic_dual_sub_main[] = {
+ {
+ .ctl_name = MIXER_MUX_UL10,
+ .strval = MIXER_AMIC1,
+ },
+ {
+ .ctl_name = MIXER_MUX_UL11,
+ .strval = MIXER_AMIC0,
+ },
+ {
+ .ctl_name = NULL,
+ },
+};
+
/* VX UL front-end paths */
struct route_setting vx_ul_amic_left[] = {
{
@@ -527,6 +560,19 @@ struct tuna_stream_out {
#define MAX_PREPROCESSORS 3 /* maximum one AGC + one NS + one AEC per input stream */
+struct effect_info_s {
+ effect_handle_t effect_itfe;
+ size_t num_channel_configs;
+ channel_config_t* channel_configs;
+};
+
+#define NUM_IN_AUX_CNL_CONFIGS 2
+channel_config_t in_aux_cnl_configs[NUM_IN_AUX_CNL_CONFIGS] = {
+ { AUDIO_CHANNEL_IN_FRONT , AUDIO_CHANNEL_IN_BACK},
+ { AUDIO_CHANNEL_IN_STEREO , AUDIO_CHANNEL_IN_RIGHT}
+};
+
+
struct tuna_stream_in {
struct audio_stream_in stream;
@@ -536,23 +582,33 @@ struct tuna_stream_in {
int device;
struct resampler_itfe *resampler;
struct resampler_buffer_provider buf_provider;
- int16_t *buffer;
- size_t frames_in;
unsigned int requested_rate;
int standby;
int source;
struct echo_reference_itfe *echo_reference;
bool need_echo_reference;
- effect_handle_t preprocessors[MAX_PREPROCESSORS];
- int num_preprocessors;
- int16_t *proc_buf;
+
+ int16_t *read_buf;
+ size_t read_buf_size;
+ size_t read_buf_frames;
+
+ int16_t *proc_buf_in;
+ int16_t *proc_buf_out;
size_t proc_buf_size;
- size_t proc_frames_in;
+ size_t proc_buf_frames;
+
int16_t *ref_buf;
size_t ref_buf_size;
- size_t ref_frames_in;
+ size_t ref_buf_frames;
+
int read_status;
+ int num_preprocessors;
+ struct effect_info_s preprocessors[MAX_PREPROCESSORS];
+
+ bool aux_channels_changed;
+ uint32_t main_channels;
+ uint32_t aux_channels;
struct tuna_audio_device *dev;
};
@@ -567,6 +623,7 @@ static void select_input_device(struct tuna_audio_device *adev);
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume);
static int do_input_standby(struct tuna_stream_in *in);
static int do_output_standby(struct tuna_stream_out *out);
+static void in_update_aux_channels(struct tuna_stream_in *in, effect_handle_t effect);
/* Returns true on devices that are toro, false otherwise */
static int is_device_toro(void)
@@ -740,7 +797,7 @@ static void set_input_volumes(struct tuna_audio_device *adev, int main_mic_on,
if (adev->mode == AUDIO_MODE_IN_CALL) {
int sub_mic_volume = is_device_toro() ? VOICE_CALL_SUB_MIC_VOLUME_TORO :
- VOICE_CALL_SUB_MIC_VOLUME_MAGURO;
+ VOICE_CALL_SUB_MIC_VOLUME_MAGURO;
/* special case: don't look at input source for IN_CALL state */
volume = DB_TO_ABE_GAIN(main_mic_on ? VOICE_CALL_MAIN_MIC_VOLUME :
(headset_mic_on ? VOICE_CALL_HEADSET_MIC_VOLUME :
@@ -1082,12 +1139,32 @@ static void select_input_device(struct tuna_audio_device *adev)
set_route_by_array(adev->mixer, mm_ul2_bt, 1);
else {
/* Select front end */
- if (main_mic_on || headset_on)
- set_route_by_array(adev->mixer, mm_ul2_amic_left, 1);
- else if (sub_mic_on)
- set_route_by_array(adev->mixer, mm_ul2_amic_right, 1);
- else
- set_route_by_array(adev->mixer, mm_ul2_amic_left, 0);
+
+
+ if ((adev->active_input != 0) && (adev->active_input->aux_channels)) {
+ ALOGV("select input device(): multi-mic configuration main mic %s sub mic %s",
+ main_mic_on ? "ON" : "OFF", sub_mic_on ? "ON" : "OFF");
+ if (main_mic_on) {
+ set_route_by_array(adev->mixer, mm_ul2_amic_dual_main_sub, 1);
+ sub_mic_on = 1;
+ }
+ else if (sub_mic_on) {
+ set_route_by_array(adev->mixer, mm_ul2_amic_dual_sub_main, 1);
+ main_mic_on = 1;
+ }
+ else {
+ set_route_by_array(adev->mixer, mm_ul2_amic_dual_main_sub, 0);
+ }
+ } else {
+ ALOGV("select input device(): single mic configuration");
+ if (main_mic_on || headset_on)
+ set_route_by_array(adev->mixer, mm_ul2_amic_left, 1);
+ else if (sub_mic_on)
+ set_route_by_array(adev->mixer, mm_ul2_amic_right, 1);
+ else
+ set_route_by_array(adev->mixer, mm_ul2_amic_left, 0);
+ }
+
/* Select back end */
mixer_ctl_set_enum_by_string(adev->mixer_ctls.right_capture,
@@ -1485,7 +1562,6 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
struct tuna_stream_in *in;
bool low_power;
int kernel_frames;
- void *buf;
/* If we're in out_write, we will find at least one pcm active */
int primary_pcm = -1;
int i;
@@ -1645,6 +1721,27 @@ static int start_input_stream(struct tuna_stream_in *in)
select_input_device(adev);
}
+ if (in->aux_channels_changed)
+ {
+ in->aux_channels_changed = false;
+ in->config.channels = popcount(in->main_channels | in->aux_channels);
+
+ if (in->resampler) {
+ /* release and recreate the resampler with the new number of channel of the input */
+ release_resampler(in->resampler);
+ in->resampler = NULL;
+ ret = create_resampler(in->config.rate,
+ in->requested_rate,
+ in->config.channels,
+ RESAMPLER_QUALITY_DEFAULT,
+ &in->buf_provider,
+ &in->resampler);
+ }
+ ALOGV("start_input_stream(): New channel configuration, "
+ "main_channels = [%04x], aux_channels = [%04x], config.channels = %d",
+ in->main_channels, in->aux_channels, in->config.channels);
+ }
+
if (in->need_echo_reference && in->echo_reference == NULL)
in->echo_reference = get_echo_reference(adev,
AUDIO_FORMAT_PCM_16_BIT,
@@ -1660,10 +1757,14 @@ static int start_input_stream(struct tuna_stream_in *in)
return -ENOMEM;
}
+ /* force read and proc buf reallocation case of frame size or channel count change */
+ in->read_buf_frames = 0;
+ in->read_buf_size = 0;
+ in->proc_buf_frames = 0;
+ in->proc_buf_size = 0;
/* if no supported sample rate is available, use the resampler */
if (in->resampler) {
in->resampler->reset(in->resampler);
- in->frames_in = 0;
}
return 0;
}
@@ -1686,14 +1787,14 @@ static size_t in_get_buffer_size(const struct audio_stream *stream)
return get_input_buffer_size(in->requested_rate,
AUDIO_FORMAT_PCM_16_BIT,
- in->config.channels);
+ popcount(in->main_channels));
}
static uint32_t in_get_channels(const struct audio_stream *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
- return audio_channel_in_mask_from_count(in->config.channels);
+ return in->main_channels;
}
static audio_format_t in_get_format(const struct audio_stream *stream)
@@ -1782,6 +1883,9 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
if ((in->device != val) && (val != 0)) {
in->device = val;
do_standby = true;
+ /* make sure new device selection is incompatible with multi-mic pre processing
+ * configuration */
+ in_update_aux_channels(in, NULL);
}
}
@@ -1831,8 +1935,9 @@ static void get_capture_delay(struct tuna_stream_in *in,
* in current buffer */
/* frames in in->buffer are at driver sampling rate while frames in in->proc_buf are
* at requested sampling rate */
- buf_delay = (long)(((int64_t)(in->frames_in) * 1000000000) / in->config.rate +
- ((int64_t)(in->proc_frames_in) * 1000000000) / in->requested_rate);
+ buf_delay = (long)(((int64_t)(in->read_buf_frames) * 1000000000) / in->config.rate +
+ ((int64_t)(in->proc_buf_frames) * 1000000000) /
+ in->requested_rate);
/* add delay introduced by resampler */
rsmp_delay = 0;
@@ -1848,10 +1953,10 @@ static void get_capture_delay(struct tuna_stream_in *in,
buffer->delay_ns = delay_ns;
ALOGV("get_capture_delay time_stamp = [%ld].[%ld], delay_ns: [%d],"
" kernel_delay:[%ld], buf_delay:[%ld], rsmp_delay:[%ld], kernel_frames:[%d], "
- "in->frames_in:[%d], in->proc_frames_in:[%d], frames:[%d]",
+ "in->read_buf_frames:[%d], in->proc_buf_frames:[%d], frames:[%d]",
buffer->time_stamp.tv_sec , buffer->time_stamp.tv_nsec, buffer->delay_ns,
kernel_delay, buf_delay, rsmp_delay, kernel_frames,
- in->frames_in, in->proc_frames_in, frames);
+ in->read_buf_frames, in->proc_buf_frames, frames);
}
@@ -1860,31 +1965,32 @@ static int32_t update_echo_reference(struct tuna_stream_in *in, size_t frames)
struct echo_reference_buffer b;
b.delay_ns = 0;
- ALOGV("update_echo_reference, frames = [%d], in->ref_frames_in = [%d], "
+ ALOGV("update_echo_reference, frames = [%d], in->ref_buf_frames = [%d], "
"b.frame_count = [%d]",
- frames, in->ref_frames_in, frames - in->ref_frames_in);
- if (in->ref_frames_in < frames) {
+ frames, in->ref_buf_frames, frames - in->ref_buf_frames);
+ if (in->ref_buf_frames < frames) {
if (in->ref_buf_size < frames) {
in->ref_buf_size = frames;
- in->ref_buf = (int16_t *)realloc(in->ref_buf,
- in->ref_buf_size *
- in->config.channels * sizeof(int16_t));
+ in->ref_buf = (int16_t *)realloc(in->ref_buf, pcm_frames_to_bytes(in->pcm, frames));
+ ALOG_ASSERT((in->ref_buf != NULL),
+ "update_echo_reference() failed to reallocate ref_buf");
+ ALOGV("update_echo_reference(): ref_buf %p extended to %d bytes",
+ in->ref_buf, pcm_frames_to_bytes(in->pcm, frames));
}
-
- b.frame_count = frames - in->ref_frames_in;
- b.raw = (void *)(in->ref_buf + in->ref_frames_in * in->config.channels);
+ b.frame_count = frames - in->ref_buf_frames;
+ b.raw = (void *)(in->ref_buf + in->ref_buf_frames * in->config.channels);
get_capture_delay(in, frames, &b);
if (in->echo_reference->read(in->echo_reference, &b) == 0)
{
- in->ref_frames_in += b.frame_count;
- ALOGV("update_echo_reference: in->ref_frames_in:[%d], "
+ in->ref_buf_frames += b.frame_count;
+ ALOGV("update_echo_reference(): in->ref_buf_frames:[%d], "
"in->ref_buf_size:[%d], frames:[%d], b.frame_count:[%d]",
- in->ref_frames_in, in->ref_buf_size, frames, b.frame_count);
+ in->ref_buf_frames, in->ref_buf_size, frames, b.frame_count);
}
} else
- ALOGW("update_echo_reference: NOT enough frames to read ref buffer");
+ ALOGW("update_echo_reference(): NOT enough frames to read ref buffer");
return b.delay_ns;
}
@@ -1924,32 +2030,32 @@ static int set_preprocessor_echo_delay(effect_handle_t handle,
static void push_echo_reference(struct tuna_stream_in *in, size_t frames)
{
/* read frames from echo reference buffer and update echo delay
- * in->ref_frames_in is updated with frames available in in->ref_buf */
+ * in->ref_buf_frames is updated with frames available in in->ref_buf */
int32_t delay_us = update_echo_reference(in, frames)/1000;
int i;
audio_buffer_t buf;
- if (in->ref_frames_in < frames)
- frames = in->ref_frames_in;
+ if (in->ref_buf_frames < frames)
+ frames = in->ref_buf_frames;
buf.frameCount = frames;
buf.raw = in->ref_buf;
for (i = 0; i < in->num_preprocessors; i++) {
- if ((*in->preprocessors[i])->process_reverse == NULL)
+ if ((*in->preprocessors[i].effect_itfe)->process_reverse == NULL)
continue;
- (*in->preprocessors[i])->process_reverse(in->preprocessors[i],
+ (*in->preprocessors[i].effect_itfe)->process_reverse(in->preprocessors[i].effect_itfe,
&buf,
NULL);
- set_preprocessor_echo_delay(in->preprocessors[i], delay_us);
+ set_preprocessor_echo_delay(in->preprocessors[i].effect_itfe, delay_us);
}
- in->ref_frames_in -= buf.frameCount;
- if (in->ref_frames_in) {
+ in->ref_buf_frames -= buf.frameCount;
+ if (in->ref_buf_frames) {
memcpy(in->ref_buf,
in->ref_buf + buf.frameCount * in->config.channels,
- in->ref_frames_in * in->config.channels * sizeof(int16_t));
+ in->ref_buf_frames * in->config.channels * sizeof(int16_t));
}
}
@@ -1971,23 +2077,31 @@ static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
return -ENODEV;
}
- if (in->frames_in == 0) {
- in->read_status = pcm_read(in->pcm,
- (void*)in->buffer,
- in->config.period_size *
- audio_stream_frame_size(&in->stream.common));
+ if (in->read_buf_frames == 0) {
+ size_t size_in_bytes = pcm_frames_to_bytes(in->pcm, in->config.period_size);
+ if (in->read_buf_size < in->config.period_size) {
+ in->read_buf_size = in->config.period_size;
+ in->read_buf = (int16_t *) realloc(in->read_buf, size_in_bytes);
+ ALOG_ASSERT((in->read_buf != NULL),
+ "get_next_buffer() failed to reallocate read_buf");
+ ALOGV("get_next_buffer(): read_buf %p extended to %d bytes",
+ in->read_buf, size_in_bytes);
+ }
+
+ in->read_status = pcm_read(in->pcm, (void*)in->read_buf, size_in_bytes);
+
if (in->read_status != 0) {
ALOGE("get_next_buffer() pcm_read error %d", in->read_status);
buffer->raw = NULL;
buffer->frame_count = 0;
return in->read_status;
}
- in->frames_in = in->config.period_size;
+ in->read_buf_frames = in->config.period_size;
}
- buffer->frame_count = (buffer->frame_count > in->frames_in) ?
- in->frames_in : buffer->frame_count;
- buffer->i16 = in->buffer + (in->config.period_size - in->frames_in) *
+ buffer->frame_count = (buffer->frame_count > in->read_buf_frames) ?
+ in->read_buf_frames : buffer->frame_count;
+ buffer->i16 = in->read_buf + (in->config.period_size - in->read_buf_frames) *
in->config.channels;
return in->read_status;
@@ -2005,7 +2119,7 @@ static void release_buffer(struct resampler_buffer_provider *buffer_provider,
in = (struct tuna_stream_in *)((char *)buffer_provider -
offsetof(struct tuna_stream_in, buf_provider));
- in->frames_in -= buffer->frame_count;
+ in->read_buf_frames -= buffer->frame_count;
}
/* read_frames() reads frames from kernel driver, down samples to capture rate
@@ -2018,9 +2132,10 @@ static ssize_t read_frames(struct tuna_stream_in *in, void *buffer, ssize_t fram
size_t frames_rd = frames - frames_wr;
if (in->resampler != NULL) {
in->resampler->resample_from_provider(in->resampler,
- (int16_t *)((char *)buffer +
- frames_wr * audio_stream_frame_size(&in->stream.common)),
- &frames_rd);
+ (int16_t *)((char *)buffer +
+ pcm_frames_to_bytes(in->pcm ,frames_wr)),
+ &frames_rd);
+
} else {
struct resampler_buffer buf = {
{ raw : NULL, },
@@ -2029,9 +2144,9 @@ static ssize_t read_frames(struct tuna_stream_in *in, void *buffer, ssize_t fram
get_next_buffer(&in->buf_provider, &buf);
if (buf.raw != NULL) {
memcpy((char *)buffer +
- frames_wr * audio_stream_frame_size(&in->stream.common),
+ pcm_frames_to_bytes(in->pcm, frames_wr),
buf.raw,
- buf.frame_count * audio_stream_frame_size(&in->stream.common));
+ pcm_frames_to_bytes(in->pcm, buf.frame_count));
frames_rd = buf.frame_count;
}
release_buffer(&in->buf_provider, &buf);
@@ -2055,66 +2170,127 @@ static ssize_t process_frames(struct tuna_stream_in *in, void* buffer, ssize_t f
audio_buffer_t in_buf;
audio_buffer_t out_buf;
int i;
+ bool has_aux_channels = (~in->main_channels & in->aux_channels);
+ void *proc_buf_out;
+ if (has_aux_channels)
+ proc_buf_out = in->proc_buf_out;
+ else
+ proc_buf_out = buffer;
+
+ /* since all the processing below is done in frames and using the config.channels
+ * as the number of channels, no changes is required in case aux_channels are present */
while (frames_wr < frames) {
/* first reload enough frames at the end of process input buffer */
- if (in->proc_frames_in < (size_t)frames) {
+ if (in->proc_buf_frames < (size_t)frames) {
ssize_t frames_rd;
if (in->proc_buf_size < (size_t)frames) {
+ size_t size_in_bytes = pcm_frames_to_bytes(in->pcm, frames);
+
in->proc_buf_size = (size_t)frames;
- in->proc_buf = (int16_t *)realloc(in->proc_buf,
- in->proc_buf_size *
- in->config.channels * sizeof(int16_t));
- ALOGV("process_frames(): in->proc_buf %p size extended to %d frames",
- in->proc_buf, in->proc_buf_size);
+ in->proc_buf_in = (int16_t *)realloc(in->proc_buf_in, size_in_bytes);
+ ALOG_ASSERT((in->proc_buf_in != NULL),
+ "process_frames() failed to reallocate proc_buf_in");
+ if (has_aux_channels) {
+ in->proc_buf_out = (int16_t *)realloc(in->proc_buf_out, size_in_bytes);
+ ALOG_ASSERT((in->proc_buf_out != NULL),
+ "process_frames() failed to reallocate proc_buf_out");
+ proc_buf_out = in->proc_buf_out;
+ }
+ ALOGV("process_frames(): proc_buf_in %p extended to %d bytes",
+ in->proc_buf_in, size_in_bytes);
}
frames_rd = read_frames(in,
- in->proc_buf +
- in->proc_frames_in * in->config.channels,
- frames - in->proc_frames_in);
+ in->proc_buf_in +
+ in->proc_buf_frames * in->config.channels,
+ frames - in->proc_buf_frames);
if (frames_rd < 0) {
frames_wr = frames_rd;
break;
}
- in->proc_frames_in += frames_rd;
+ in->proc_buf_frames += frames_rd;
}
if (in->echo_reference != NULL)
- push_echo_reference(in, in->proc_frames_in);
+ push_echo_reference(in, in->proc_buf_frames);
/* in_buf.frameCount and out_buf.frameCount indicate respectively
* the maximum number of frames to be consumed and produced by process() */
- in_buf.frameCount = in->proc_frames_in;
- in_buf.s16 = in->proc_buf;
+ in_buf.frameCount = in->proc_buf_frames;
+ in_buf.s16 = in->proc_buf_in;
out_buf.frameCount = frames - frames_wr;
- out_buf.s16 = (int16_t *)buffer + frames_wr * in->config.channels;
-
- for (i = 0; i < in->num_preprocessors; i++)
- (*in->preprocessors[i])->process(in->preprocessors[i],
+ out_buf.s16 = (int16_t *)proc_buf_out + frames_wr * in->config.channels;
+
+ /* FIXME: this works because of current pre processing library implementation that
+ * does the actual process only when the last enabled effect process is called.
+ * The generic solution is to have an output buffer for each effect and pass it as
+ * input to the next.
+ */
+ for (i = 0; i < in->num_preprocessors; i++) {
+ (*in->preprocessors[i].effect_itfe)->process(in->preprocessors[i].effect_itfe,
&in_buf,
&out_buf);
+ }
/* process() has updated the number of frames consumed and produced in
* in_buf.frameCount and out_buf.frameCount respectively
- * move remaining frames to the beginning of in->proc_buf */
- in->proc_frames_in -= in_buf.frameCount;
- if (in->proc_frames_in) {
- memcpy(in->proc_buf,
- in->proc_buf + in_buf.frameCount * in->config.channels,
- in->proc_frames_in * in->config.channels * sizeof(int16_t));
+ * move remaining frames to the beginning of in->proc_buf_in */
+ in->proc_buf_frames -= in_buf.frameCount;
+
+ if (in->proc_buf_frames) {
+ memcpy(in->proc_buf_in,
+ in->proc_buf_in + in_buf.frameCount * in->config.channels,
+ in->proc_buf_frames * in->config.channels * sizeof(int16_t));
}
/* if not enough frames were passed to process(), read more and retry. */
- if (out_buf.frameCount == 0)
+ if (out_buf.frameCount == 0) {
+ ALOGW("No frames produced by preproc");
continue;
+ }
- frames_wr += out_buf.frameCount;
+ if ((frames_wr + (ssize_t)out_buf.frameCount) <= frames) {
+ frames_wr += out_buf.frameCount;
+ } else {
+ /* The effect does not comply to the API. In theory, we should never end up here! */
+ ALOGE("preprocessing produced too many frames: %d + %d > %d !",
+ (unsigned int)frames_wr, out_buf.frameCount, (unsigned int)frames);
+ frames_wr = frames;
+ }
+ }
+
+ /* Remove aux_channels that have been added on top of main_channels
+ * Assumption is made that the channels are interleaved and that the main
+ * channels are first. */
+ if (has_aux_channels)
+ {
+ size_t src_channels = in->config.channels;
+ size_t dst_channels = popcount(in->main_channels);
+ int16_t* src_buffer = (int16_t *)proc_buf_out;
+ int16_t* dst_buffer = (int16_t *)buffer;
+
+ if (dst_channels == 1) {
+ for (i = frames_wr; i > 0; i--)
+ {
+ *dst_buffer++ = *src_buffer;
+ src_buffer += src_channels;
+ }
+ } else {
+ for (i = frames_wr; i > 0; i--)
+ {
+ memcpy(dst_buffer, src_buffer, dst_channels*sizeof(int16_t));
+ dst_buffer += dst_channels;
+ src_buffer += src_channels;
+ }
+ }
}
+
return frames_wr;
}
static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
+
size_t bytes)
{
int ret = 0;
@@ -2165,6 +2341,284 @@ static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
return 0;
}
+#define GET_COMMAND_STATUS(status, fct_status, cmd_status) \
+ do { \
+ if (fct_status != 0) \
+ status = fct_status; \
+ else if (cmd_status != 0) \
+ status = cmd_status; \
+ } while(0)
+
+static int in_configure_reverse(struct tuna_stream_in *in)
+{
+ int32_t cmd_status;
+ uint32_t size = sizeof(int);
+ effect_config_t config;
+ int32_t status = 0;
+ int32_t fct_status = 0;
+ int i;
+
+ if (in->num_preprocessors > 0) {
+ config.inputCfg.channels = in->main_channels;
+ config.outputCfg.channels = in->main_channels;
+ config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ config.inputCfg.samplingRate = in->requested_rate;
+ config.outputCfg.samplingRate = in->requested_rate;
+ config.inputCfg.mask =
+ ( EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT );
+ config.outputCfg.mask =
+ ( EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT );
+
+ for (i = 0; i < in->num_preprocessors; i++)
+ {
+ if ((*in->preprocessors[i].effect_itfe)->process_reverse == NULL)
+ continue;
+ fct_status = (*(in->preprocessors[i].effect_itfe))->command(
+ in->preprocessors[i].effect_itfe,
+ EFFECT_CMD_SET_CONFIG_REVERSE,
+ sizeof(effect_config_t),
+ &config,
+ &size,
+ &cmd_status);
+ GET_COMMAND_STATUS(status, fct_status, cmd_status);
+ }
+ }
+ return status;
+}
+
+#define MAX_NUM_CHANNEL_CONFIGS 10
+
+static void in_read_audio_effect_channel_configs(struct tuna_stream_in *in,
+ struct effect_info_s *effect_info)
+{
+ /* size and format of the cmd are defined in hardware/audio_effect.h */
+ effect_handle_t effect = effect_info->effect_itfe;
+ uint32_t cmd_size = 2 * sizeof(uint32_t);
+ uint32_t cmd[] = { EFFECT_FEATURE_AUX_CHANNELS, MAX_NUM_CHANNEL_CONFIGS };
+ /* reply = status + number of configs (n) + n x channel_config_t */
+ uint32_t reply_size =
+ 2 * sizeof(uint32_t) + (MAX_NUM_CHANNEL_CONFIGS * sizeof(channel_config_t));
+ int32_t reply[reply_size];
+ int32_t cmd_status;
+
+ ALOG_ASSERT((effect_info->num_channel_configs == 0),
+ "in_read_audio_effect_channel_configs() num_channel_configs not cleared");
+ ALOG_ASSERT((effect_info->channel_configs == NULL),
+ "in_read_audio_effect_channel_configs() channel_configs not cleared");
+
+ /* if this command is not supported, then the effect is supposed to return -EINVAL.
+ * This error will be interpreted as if the effect supports the main_channels but does not
+ * support any aux_channels */
+ cmd_status = (*effect)->command(effect,
+ EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,
+ cmd_size,
+ (void*)&cmd,
+ &reply_size,
+ (void*)&reply);
+
+ if (cmd_status != 0) {
+ ALOGV("in_read_audio_effect_channel_configs(): "
+ "fx->command returned %d", cmd_status);
+ return;
+ }
+
+ if (reply[0] != 0) {
+ ALOGW("in_read_audio_effect_channel_configs(): "
+ "command EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS error %d num configs %d",
+ reply[0], (reply[0] == -ENOMEM) ? reply[1] : MAX_NUM_CHANNEL_CONFIGS);
+ return;
+ }
+
+ /* the feature is not supported */
+ ALOGV("in_read_audio_effect_channel_configs()(): "
+ "Feature supported and adding %d channel configs to the list", reply[1]);
+ effect_info->num_channel_configs = reply[1];
+ effect_info->channel_configs =
+ (channel_config_t *) malloc(sizeof(channel_config_t) * reply[1]); /* n x configs */
+ memcpy(effect_info->channel_configs, (reply + 2), sizeof(channel_config_t) * reply[1]);
+}
+
+
+static uint32_t in_get_aux_channels(struct tuna_stream_in *in)
+{
+ int i;
+ channel_config_t new_chcfg = {0, 0};
+
+ if (in->num_preprocessors == 0)
+ return 0;
+
+ /* do not enable dual mic configurations when capturing from other microphones than
+ * main or sub */
+ if (!(in->device & (AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BACK_MIC)))
+ return 0;
+
+ /* retain most complex aux channels configuration compatible with requested main channels and
+ * supported by audio driver and all pre processors */
+ for (i = 0; i < NUM_IN_AUX_CNL_CONFIGS; i++) {
+ channel_config_t *cur_chcfg = &in_aux_cnl_configs[i];
+ if (cur_chcfg->main_channels == in->main_channels) {
+ size_t match_cnt;
+ size_t idx_preproc;
+ for (idx_preproc = 0, match_cnt = 0;
+ /* no need to continue if at least one preprocessor doesn't match */
+ idx_preproc < (size_t)in->num_preprocessors && match_cnt == idx_preproc;
+ idx_preproc++) {
+ struct effect_info_s *effect_info = &in->preprocessors[idx_preproc];
+ size_t idx_chcfg;
+
+ for (idx_chcfg = 0; idx_chcfg < effect_info->num_channel_configs; idx_chcfg++) {
+ if (memcmp(effect_info->channel_configs + idx_chcfg,
+ cur_chcfg,
+ sizeof(channel_config_t)) == 0) {
+ match_cnt++;
+ break;
+ }
+ }
+ }
+ /* if all preprocessors match, we have a candidate */
+ if (match_cnt == (size_t)in->num_preprocessors) {
+ /* retain most complex aux channels configuration */
+ if (popcount(cur_chcfg->aux_channels) > popcount(new_chcfg.aux_channels)) {
+ new_chcfg = *cur_chcfg;
+ }
+ }
+ }
+ }
+
+ ALOGV("in_get_aux_channels(): return %04x", new_chcfg.aux_channels);
+
+ return new_chcfg.aux_channels;
+}
+
+static int in_configure_effect_channels(effect_handle_t effect,
+ channel_config_t *channel_config)
+{
+ int status = 0;
+ int fct_status;
+ int32_t cmd_status;
+ uint32_t reply_size;
+ effect_config_t config;
+ uint32_t cmd[(sizeof(uint32_t) + sizeof(channel_config_t) - 1) / sizeof(uint32_t) + 1];
+
+ ALOGV("in_configure_effect_channels(): configure effect with channels: [%04x][%04x]",
+ channel_config->main_channels,
+ channel_config->aux_channels);
+
+ config.inputCfg.mask = EFFECT_CONFIG_CHANNELS;
+ config.outputCfg.mask = EFFECT_CONFIG_CHANNELS;
+ reply_size = sizeof(effect_config_t);
+ fct_status = (*effect)->command(effect,
+ EFFECT_CMD_GET_CONFIG,
+ 0,
+ NULL,
+ &reply_size,
+ &config);
+ if (fct_status != 0) {
+ ALOGE("in_configure_effect_channels(): EFFECT_CMD_GET_CONFIG failed");
+ return fct_status;
+ }
+
+ config.inputCfg.channels = channel_config->main_channels | channel_config->aux_channels;
+ config.outputCfg.channels = config.inputCfg.channels;
+ reply_size = sizeof(uint32_t);
+ fct_status = (*effect)->command(effect,
+ EFFECT_CMD_SET_CONFIG,
+ sizeof(effect_config_t),
+ &config,
+ &reply_size,
+ &cmd_status);
+ GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+ cmd[0] = EFFECT_FEATURE_AUX_CHANNELS;
+ memcpy(cmd + 1, channel_config, sizeof(channel_config_t));
+ reply_size = sizeof(uint32_t);
+ fct_status = (*effect)->command(effect,
+ EFFECT_CMD_SET_FEATURE_CONFIG,
+ sizeof(cmd), //sizeof(uint32_t) + sizeof(channel_config_t),
+ cmd,
+ &reply_size,
+ &cmd_status);
+ GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+ /* some implementations need to be re-enabled after a config change */
+ reply_size = sizeof(uint32_t);
+ fct_status = (*effect)->command(effect,
+ EFFECT_CMD_ENABLE,
+ 0,
+ NULL,
+ &reply_size,
+ &cmd_status);
+ GET_COMMAND_STATUS(status, fct_status, cmd_status);
+
+ return status;
+}
+
+static int in_reconfigure_channels(struct tuna_stream_in *in,
+ effect_handle_t effect,
+ channel_config_t *channel_config,
+ bool config_changed) {
+
+ int status = 0;
+
+ ALOGV("in_reconfigure_channels(): config_changed %d effect %p",
+ config_changed, effect);
+
+ /* if config changed, reconfigure all previously added effects */
+ if (config_changed) {
+ int i;
+ for (i = 0; i < in->num_preprocessors; i++)
+ {
+ int cur_status = in_configure_effect_channels(in->preprocessors[i].effect_itfe,
+ channel_config);
+ if (cur_status != 0) {
+ ALOGV("in_reconfigure_channels(): error %d configuring effect "
+ "%d with channels: [%04x][%04x]",
+ cur_status,
+ i,
+ channel_config->main_channels,
+ channel_config->aux_channels);
+ status = cur_status;
+ }
+ }
+ } else if (effect != NULL && channel_config->aux_channels) {
+ /* if aux channels config did not change but aux channels are present,
+ * we still need to configure the effect being added */
+ status = in_configure_effect_channels(effect, channel_config);
+ }
+ return status;
+}
+
+static void in_update_aux_channels(struct tuna_stream_in *in,
+ effect_handle_t effect)
+{
+ uint32_t aux_channels;
+ channel_config_t channel_config;
+ int status;
+
+ aux_channels = in_get_aux_channels(in);
+
+ channel_config.main_channels = in->main_channels;
+ channel_config.aux_channels = aux_channels;
+ status = in_reconfigure_channels(in,
+ effect,
+ &channel_config,
+ (aux_channels != in->aux_channels));
+
+ if (status != 0) {
+ ALOGV("in_update_aux_channels(): in_reconfigure_channels error %d", status);
+ /* resetting aux channels configuration */
+ aux_channels = 0;
+ channel_config.aux_channels = 0;
+ in_reconfigure_channels(in, effect, &channel_config, true);
+ }
+ if (in->aux_channels != aux_channels) {
+ in->aux_channels_changed = true;
+ in->aux_channels = aux_channels;
+ do_input_standby(in);
+ }
+}
+
static int in_add_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
@@ -2183,15 +2637,26 @@ static int in_add_audio_effect(const struct audio_stream *stream,
if (status != 0)
goto exit;
- in->preprocessors[in->num_preprocessors++] = effect;
+ in->preprocessors[in->num_preprocessors].effect_itfe = effect;
+ /* add the supported channel of the effect in the channel_configs */
+ in_read_audio_effect_channel_configs(in, &in->preprocessors[in->num_preprocessors]);
+
+ in->num_preprocessors++;
+
+ /* check compatibility between main channel supported and possible auxiliary channels */
+ in_update_aux_channels(in, effect);
+
+ ALOGV("in_add_audio_effect(), effect type: %08x", desc.type.timeLow);
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = true;
do_input_standby(in);
+ in_configure_reverse(in);
}
exit:
+ ALOGW_IF(status != 0, "in_add_audio_effect() error %d", status);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
@@ -2203,7 +2668,6 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
int i;
int status = -EINVAL;
- bool found = false;
effect_descriptor_t desc;
pthread_mutex_lock(&in->dev->lock);
@@ -2214,14 +2678,17 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
}
for (i = 0; i < in->num_preprocessors; i++) {
- if (found) {
- in->preprocessors[i - 1] = in->preprocessors[i];
+ if (status == 0) { /* status == 0 means an effect was removed from a previous slot */
+ in->preprocessors[i - 1].effect_itfe = in->preprocessors[i].effect_itfe;
+ in->preprocessors[i - 1].channel_configs = in->preprocessors[i].channel_configs;
+ in->preprocessors[i - 1].num_channel_configs = in->preprocessors[i].num_channel_configs;
+ ALOGV("in_remove_audio_effect moving fx from %d to %d", i, i - 1);
continue;
}
- if (in->preprocessors[i] == effect) {
- in->preprocessors[i] = NULL;
+ if (in->preprocessors[i].effect_itfe == effect) {
+ ALOGV("in_remove_audio_effect found fx at index %d", i);
+ free(in->preprocessors[i].channel_configs);
status = 0;
- found = true;
}
}
@@ -2229,10 +2696,21 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
goto exit;
in->num_preprocessors--;
+ /* if we remove one effect, at least the last preproc should be reset */
+ in->preprocessors[in->num_preprocessors].num_channel_configs = 0;
+ in->preprocessors[in->num_preprocessors].effect_itfe = NULL;
+ in->preprocessors[in->num_preprocessors].channel_configs = NULL;
+
+
+ /* check compatibility between main channel supported and possible auxiliary channels */
+ in_update_aux_channels(in, NULL);
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
goto exit;
+
+ ALOGV("in_remove_audio_effect(), effect type: %08x", desc.type.timeLow);
+
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
in->need_echo_reference = false;
do_input_standby(in);
@@ -2240,6 +2718,7 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
exit:
+ ALOGW_IF(status != 0, "in_remove_audio_effect() error %d", status);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return status;
@@ -2487,12 +2966,10 @@ static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices,
memcpy(&in->config, &pcm_config_mm_ul, sizeof(pcm_config_mm_ul));
in->config.channels = channel_count;
- in->buffer = malloc(in->config.period_size *
- audio_stream_frame_size(&in->stream.common));
- if (!in->buffer) {
- ret = -ENOMEM;
- goto err;
- }
+ in->main_channels = *channel_mask;
+
+ /* initialisation of preprocessor structure array is implicit with the calloc.
+ * same for in->aux_channels and in->aux_channels_changed */
if (in->requested_rate != in->config.rate) {
in->buf_provider.get_next_buffer = get_next_buffer;
@@ -2518,7 +2995,6 @@ static int adev_open_input_stream(struct audio_hw_device *dev, uint32_t devices,
return 0;
err:
- free(in->buffer);
if (in->resampler)
release_resampler(in->resampler);
@@ -2531,15 +3007,22 @@ static void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
struct tuna_stream_in *in = (struct tuna_stream_in *)stream;
+ int i;
in_standby(&stream->common);
- free(in->buffer);
+ for (i = 0; i < in->num_preprocessors; i++) {
+ free(in->preprocessors[i].channel_configs);
+ }
+
+ free(in->read_buf);
if (in->resampler) {
release_resampler(in->resampler);
}
- if (in->proc_buf)
- free(in->proc_buf);
+ if (in->proc_buf_in)
+ free(in->proc_buf_in);
+ if (in->proc_buf_out)
+ free(in->proc_buf_out);
if (in->ref_buf)
free(in->ref_buf);