audio: add support for multichannel to dual mono conversion
Add support to set channel mix coffecients to convert from
multichannel to dual mono.
CRs-Fixed: 2211047
Change-Id: I2ecd8babfe53ca48b6e596dce0579e95879ce7e0
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index ba096c9..ae1fef7 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -64,6 +64,8 @@
#define MAX_SLEEP_RETRY 100
#define WIFI_INIT_WAIT_SLEEP 50
+#define MAX_NUM_CHANNELS 8
+#define Q14_GAIN_UNITY 0x4000
struct audio_extn_module {
bool anc_enabled;
@@ -301,6 +303,74 @@
ALOGV("%s: Setting custom stereo state success", __func__);
}
}
+
+void audio_extn_send_dual_mono_mixing_coefficients(struct stream_out *out)
+{
+ struct audio_device *adev = out->dev;
+ struct mixer_ctl *ctl;
+ char mixer_ctl_name[128];
+ int cust_ch_mixer_cfg[128], len = 0;
+ int ip_channel_cnt = audio_channel_count_from_out_mask(out->channel_mask);
+ int pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+ int op_channel_cnt = 2;
+ int i, j, err;
+
+ ALOGV("%s", __func__);
+ if (!out->started) {
+ out->set_dual_mono = true;
+ goto exit;
+ }
+
+ ALOGD("%s: i/p channel count %d, o/p channel count %d, pcm id %d", __func__,
+ ip_channel_cnt, op_channel_cnt, pcm_device_id);
+
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Audio Stream %d Channel Mix Cfg", pcm_device_id);
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name);
+ goto exit;
+ }
+
+ /* Output channel count corresponds to backend configuration channels.
+ * Input channel count corresponds to ASM session channels.
+ * Set params is called with channels that need to be selected from
+ * input to generate output.
+ * ex: "8,2" to downmix from 8 to 2 i.e. to downmix from 8 to 2,
+ *
+ * This mixer control takes values in the following sequence:
+ * - input channel count(m)
+ * - output channel count(n)
+ * - weight coeff for [out ch#1, in ch#1]
+ * ....
+ * - weight coeff for [out ch#1, in ch#m]
+ *
+ * - weight coeff for [out ch#2, in ch#1]
+ * ....
+ * - weight coeff for [out ch#2, in ch#m]
+ *
+ * - weight coeff for [out ch#n, in ch#1]
+ * ....
+ * - weight coeff for [out ch#n, in ch#m]
+ *
+ * To get dualmono ouptu weightage coeff is calculated as Unity gain
+ * divided by number of input channels.
+ */
+ cust_ch_mixer_cfg[len++] = ip_channel_cnt;
+ cust_ch_mixer_cfg[len++] = op_channel_cnt;
+ for (i = 0; i < op_channel_cnt; i++) {
+ for (j = 0; j < ip_channel_cnt; j++) {
+ cust_ch_mixer_cfg[len++] = Q14_GAIN_UNITY/ip_channel_cnt;
+ }
+ }
+
+ err = mixer_ctl_set_array(ctl, cust_ch_mixer_cfg, len);
+ if (err)
+ ALOGE("%s: ERROR. Mixer ctl set failed", __func__);
+exit:
+ return;
+}
#endif /* CUSTOM_STEREO_ENABLED */
#ifndef DTS_EAGLE
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index e74bd36..b2b0e73 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -43,6 +43,8 @@
#include "ip_hdlr_intf.h"
#include "battery_listener.h"
+#define AUDIO_PARAMETER_DUAL_MONO "dual_mono"
+
#ifndef AFE_PROXY_ENABLED
#define AUDIO_DEVICE_OUT_PROXY 0x40000
#endif
@@ -1132,4 +1134,9 @@
int audio_extn_ext_hw_plugin_set_audio_gain(void *plugin,
struct audio_usecase *usecase, uint32_t gain);
#endif
+#ifndef CUSTOM_STEREO_ENABLED
+#define audio_extn_send_dual_mono_mixing_coefficients(out) (0)
+#else
+void audio_extn_send_dual_mono_mixing_coefficients(struct stream_out *out);
+#endif
#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d35ff0d..034f491 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -3213,6 +3213,8 @@
}
break;
}
+ platform_set_stream_channel_map(adev->platform, out->channel_mask,
+ out->pcm_device_id, &out->channel_map_param.channel_map[0]);
ALOGV("%s: pcm_prepare", __func__);
if (pcm_is_ready(out->pcm)) {
@@ -3226,8 +3228,6 @@
goto error_open;
}
}
- platform_set_stream_channel_map(adev->platform, out->channel_mask,
- out->pcm_device_id, &out->channel_map_param.channel_map[0]);
// apply volume for voip playback after path is set up
if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP)
out_set_voip_volume(&out->stream, out->volume_l, out->volume_r);
@@ -3987,6 +3987,13 @@
pthread_mutex_unlock(&out->lock);
}
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_DUAL_MONO, value,
+ sizeof(value));
+ if (err >= 0) {
+ if (!strncmp("true", value, sizeof("true")) || atoi(value))
+ audio_extn_send_dual_mono_mixing_coefficients(out);
+ }
+
err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_PROFILE, value, sizeof(value));
if (err >= 0) {
strlcpy(out->profile, value, sizeof(out->profile));
@@ -4597,6 +4604,8 @@
ret = -EINVAL;
goto exit;
}
+ if (out->set_dual_mono)
+ audio_extn_send_dual_mono_mixing_coefficients(out);
}
if (adev->is_channel_status_set == false && (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)){
@@ -5969,6 +5978,7 @@
out->a2dp_compress_mute = false;
out->hal_output_suspend_supported = 0;
out->dynamic_pm_qos_config_supported = 0;
+ out->set_dual_mono = false;
if ((flags & AUDIO_OUTPUT_FLAG_BD) &&
(property_get_bool("vendor.audio.matrix.limiter.enable", false)))
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index c13ca56..1675893 100755
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -354,6 +354,7 @@
bool stream_config_changed;
mix_matrix_params_t pan_scale_params;
mix_matrix_params_t downmix_params;
+ bool set_dual_mono;
};
struct stream_in {
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 4160754..a048d3e 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -251,6 +251,7 @@
stream_param[i].ethread_data = nullptr;
stream_param[i].device_url = "stream";
stream_param[i].play_later = false;
+ stream_param[i].set_params = nullptr;
pthread_mutex_init(&stream_param[i].write_lock, (const pthread_mutexattr_t *)NULL);
pthread_cond_init(&stream_param[i].write_cond, (const pthread_condattr_t *) NULL);
@@ -797,6 +798,14 @@
if (bytes_to_read <= 0)
read_complete_file = true;
+ if (params->set_params) {
+ rc = qahw_out_set_parameters(params->out_handle, params->set_params);
+ if (rc) {
+ fprintf(log_file, "stream %s: failed to set kvpairs\n", params->set_params);
+ fprintf(stderr, "stream %s: failed to set kvpairs\n", params->set_params);
+ }
+ }
+
while (!exit && !stop_playback) {
if (!bytes_remaining) {
fprintf(log_file, "\nstream %d: reading bytes %zd\n", params->stream_index, bytes_wanted);
@@ -2095,7 +2104,7 @@
while ((opt = getopt_long(argc,
argv,
- "-f:r:c:b:d:s:v:V:l:t:a:w:k:PD:KF:Ee:A:u:m:S:C:p::x:y:qQh:i:h:g:",
+ "-f:r:c:b:d:s:v:V:l:t:a:w:k:PD:KF:Ee:A:u:m:S:C:p::x:y:qQh:i:h:g:O:",
long_options,
&option_index)) != -1) {
@@ -2233,6 +2242,9 @@
case 'K':
kpi_mode = true;
break;
+ case 'O':
+ stream_param[i].set_params = optarg;
+ break;
case 'F':
stream_param[i].flags = atoll(optarg);
stream_param[i].flags_set = true;
@@ -2533,6 +2545,7 @@
fprintf(log_file, "stream %d: Bitwidth:%d\n", stream->stream_index, stream->config.offload_info.bit_width);
fprintf(log_file, "stream %d: AAC Format Type:%d\n", stream->stream_index, stream->aac_fmt_type);
fprintf(log_file, "stream %d: Kvpair Values:%s\n", stream->stream_index, stream->kvpair_values);
+ fprintf(log_file, "stream %d: set params Values:%s\n", stream->stream_index, stream->set_params);
fprintf(log_file, "Log file:%s\n", log_filename);
fprintf(log_file, "Volume level:%f\n", vol_level);
diff --git a/qahw_api/test/qahw_playback_test.h b/qahw_api/test/qahw_playback_test.h
index 66229f5..b8bddbc 100644
--- a/qahw_api/test/qahw_playback_test.h
+++ b/qahw_api/test/qahw_playback_test.h
@@ -143,6 +143,7 @@
pthread_cond_t input_buffer_available_cond;
pthread_mutex_t input_buffer_available_lock;
uint32_t input_buffer_available_size;
+ char *set_params;
}stream_config;
qahw_module_handle_t * load_hal(audio_devices_t dev);