audio: Enable voice call support

Pair-Programmed-With: Andreas Schneider <asn@cryptomilk.org>

Change-Id: I284f21240e56dda93cb8a2ab98b903ff712504a6
tirimbino
Christopher N. Hesse 8 years ago
parent 5a2f10031a
commit 696959dda1
  1. 44
      audio/audio_hw.c
  2. 4
      audio/include/samsung_audio.h
  3. 270
      audio/voice.c
  4. 9
      audio/voice.h

@ -502,11 +502,11 @@ static struct audio_usecase *get_usecase_from_type(struct audio_device *adev,
static int set_voice_volume_l(struct audio_device *adev, float volume)
{
int err = 0;
(void)volume;
if (adev->mode == AUDIO_MODE_IN_CALL) {
/* TODO */
set_voice_session_volume(adev->voice.session, volume);
}
return err;
}
@ -861,6 +861,7 @@ static int select_devices(struct audio_device *adev,
if (usecase->type == VOICE_CALL) {
out_snd_device = get_output_snd_device(adev, active_out->devices);
prepare_voice_session(adev->voice.session, active_out->devices);
in_snd_device = get_input_snd_device(adev, active_out->devices);
usecase->devices = active_out->devices;
} else {
@ -925,6 +926,11 @@ static int select_devices(struct audio_device *adev,
/* Enable new sound devices */
if (out_snd_device != SND_DEVICE_NONE) {
/* We need to update the audio path if we switch the out devices */
if (adev->voice.in_call) {
set_voice_session_audio_path(adev->voice.session);
}
enable_snd_device(adev, usecase, out_snd_device, false);
}
@ -2079,6 +2085,11 @@ static int start_input_stream(struct stream_in *in)
#endif
#endif
if (in->dev->voice.in_call) {
ALOGV("%s: in_call, not handling PCMs", __func__);
goto skip_pcm_handling;
}
/* Open the PCM device.
* The HW is limited to support only the default pcm_profile settings.
* As such a change in aux_channels will not have an effect.
@ -2116,6 +2127,7 @@ static int start_input_stream(struct stream_in *in)
}
}
skip_pcm_handling:
/* force read and proc buffer reallocation in case of frame size or
* channel count change */
in->proc_buf_frames = 0;
@ -2250,6 +2262,11 @@ static int out_open_pcm_devices(struct stream_out *out)
if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
pcm_device_id = pcm_device_deep_buffer.id;
if (out->dev->voice.in_call) {
ALOGV("%s: in_call, not opening PCMs", __func__);
return ret;
}
ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
__func__, pcm_device_card, pcm_device_id);
@ -2396,14 +2413,14 @@ error_config:
return ret;
}
static int stop_voice_call(struct audio_device *adev)
int stop_voice_call(struct audio_device *adev)
{
struct audio_usecase *uc_info;
ALOGV("%s: enter", __func__);
adev->voice.in_call = false;
/* TODO: implement voice call stop */
stop_voice_session(adev->voice.session);
uc_info = get_usecase_from_id(adev, USECASE_VOICE_CALL);
if (uc_info == NULL) {
@ -2415,7 +2432,6 @@ static int stop_voice_call(struct audio_device *adev)
disable_snd_device(adev, uc_info, uc_info->out_snd_device, false);
disable_snd_device(adev, uc_info, uc_info->in_snd_device, true);
uc_release_pcm_devices(uc_info);
list_remove(&uc_info->adev_list_node);
free(uc_info);
@ -2424,7 +2440,7 @@ static int stop_voice_call(struct audio_device *adev)
}
/* always called with adev lock held */
static int start_voice_call(struct audio_device *adev)
int start_voice_call(struct audio_device *adev)
{
struct audio_usecase *uc_info;
int ret = 0;
@ -2436,6 +2452,12 @@ static int start_voice_call(struct audio_device *adev)
ret = -ENOMEM;
goto exit;
}
/*
* We set this early so that functions called after this is being set
* can use it. It is e.g. needed in select_devices() to inform the RILD
* which output device we use.
*/
adev->voice.in_call = true;
uc_info->id = USECASE_VOICE_CALL;
uc_info->type = VOICE_CALL;
@ -2444,19 +2466,19 @@ static int start_voice_call(struct audio_device *adev)
uc_info->in_snd_device = SND_DEVICE_NONE;
uc_info->out_snd_device = SND_DEVICE_NONE;
uc_select_pcm_devices(uc_info);
list_init(&uc_info->mixer_list);
list_add_tail(&uc_info->mixer_list,
&adev_get_mixer_for_card(adev, SOUND_CARD)->uc_list_node[uc_info->id]);
list_add_tail(&adev->usecase_list, &uc_info->adev_list_node);
select_devices(adev, USECASE_VOICE_CALL);
/* TODO: implement voice call start */
start_voice_session(adev->voice.session);
/* set cached volume */
set_voice_volume_l(adev, adev->voice.volume);
adev->voice.in_call = true;
exit:
ALOGV("%s: exit", __func__);
return ret;
@ -4317,7 +4339,7 @@ static int adev_open(const hw_module_t *module, const char *name,
}
}
adev->voice.session = voice_session_init();
adev->voice.session = voice_session_init(adev);
if (adev->voice.session == NULL) {
ALOGE("%s: Failed to initialize voice session data", __func__);

@ -39,6 +39,10 @@
#define SOUND_CAPTURE_DEVICE 0
#define SOUND_CAPTURE_SCO_DEVICE 2
/* Voice calls */
#define SOUND_PLAYBACK_VOICE_DEVICE 1
#define SOUND_CAPTURE_VOICE_DEVICE 1
/* Unusupported
#define SOUND_CAPTURE_LOOPBACK_AEC_DEVICE 1
#define SOUND_CAPTURE_HOTWORD_DEVICE 0

@ -26,11 +26,252 @@
#include <stdlib.h>
#include <pthread.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <samsung_audio.h>
#include "audio_hw.h"
#include "voice.h"
struct voice_session *voice_session_init(void)
static struct pcm_config pcm_config_voicecall = {
.channels = 2,
.rate = 8000,
.period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
.period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
.format = PCM_FORMAT_S16_LE,
};
static struct pcm_config pcm_config_voicecall_wideband = {
.channels = 2,
.rate = 16000,
.period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
.period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
.format = PCM_FORMAT_S16_LE,
};
/* Prototypes */
int start_voice_call(struct audio_device *adev);
int stop_voice_call(struct audio_device *adev);
void set_voice_session_audio_path(struct voice_session *session)
{
enum _AudioPath device_type;
switch(session->out_device) {
case AUDIO_DEVICE_OUT_SPEAKER:
device_type = SOUND_AUDIO_PATH_SPEAKER;
break;
case AUDIO_DEVICE_OUT_EARPIECE:
device_type = SOUND_AUDIO_PATH_HANDSET;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
device_type = SOUND_AUDIO_PATH_HEADSET;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
device_type = SOUND_AUDIO_PATH_HEADPHONE;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
device_type = SOUND_AUDIO_PATH_BLUETOOTH;
break;
default:
/* if output device isn't supported, use handset by default */
device_type = SOUND_AUDIO_PATH_HANDSET;
break;
}
ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
ril_set_call_audio_path(&session->ril, device_type);
}
/*
* This decides based on the output device, if we enable
* two mic control
*/
void prepare_voice_session(struct voice_session *session,
audio_devices_t active_out_devices)
{
ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
session->out_device = active_out_devices;
switch (session->out_device) {
case AUDIO_DEVICE_OUT_EARPIECE:
case AUDIO_DEVICE_OUT_SPEAKER:
session->two_mic_control = true;
break;
default:
session->two_mic_control = false;
break;
}
if (session->two_mic_disabled) {
session->two_mic_control = false;
}
}
/*
* This function must be called with hw device mutex locked, OK to hold other
* mutexes
*/
int start_voice_session(struct voice_session *session)
{
struct pcm_config *voice_config;
if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
ALOGW("%s: Voice PCMs already open!\n", __func__);
return 0;
}
ALOGV("%s: Opening voice PCMs", __func__);
if (session->wb_amr) {
ALOGV("%s: pcm_config wideband", __func__);
voice_config = &pcm_config_voicecall_wideband;
} else {
ALOGV("%s: pcm_config narrowband", __func__);
voice_config = &pcm_config_voicecall;
}
/* Open modem PCM channels */
session->pcm_voice_rx = pcm_open(SOUND_CARD,
SOUND_PLAYBACK_VOICE_DEVICE,
PCM_OUT|PCM_MONOTONIC,
voice_config);
if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
ALOGE("%s: cannot open PCM voice RX stream: %s",
__func__,
pcm_get_error(session->pcm_voice_rx));
pcm_close(session->pcm_voice_tx);
session->pcm_voice_tx = NULL;
return -ENOMEM;
}
session->pcm_voice_tx = pcm_open(SOUND_CARD,
SOUND_CAPTURE_VOICE_DEVICE,
PCM_IN|PCM_MONOTONIC,
voice_config);
if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
ALOGE("%s: cannot open PCM voice TX stream: %s",
__func__,
pcm_get_error(session->pcm_voice_tx));
pcm_close(session->pcm_voice_rx);
session->pcm_voice_rx = NULL;
return -ENOMEM;
}
pcm_start(session->pcm_voice_rx);
pcm_start(session->pcm_voice_tx);
/* TODO: handle SCO */
if (session->two_mic_control) {
ALOGV("%s: enabling two mic control", __func__);
ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
} else {
ALOGV("%s: disabling two mic control", __func__);
ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
}
ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
return 0;
}
/*
* This function must be called with hw device mutex locked, OK to hold other
* mutexes
*/
void stop_voice_session(struct voice_session *session)
{
int status = 0;
ALOGV("%s: Closing active PCMs", __func__);
if (session->pcm_voice_rx != NULL) {
pcm_stop(session->pcm_voice_rx);
pcm_close(session->pcm_voice_rx);
session->pcm_voice_rx = NULL;
status++;
}
if (session->pcm_voice_tx != NULL) {
pcm_stop(session->pcm_voice_tx);
pcm_close(session->pcm_voice_tx);
session->pcm_voice_tx = NULL;
status++;
}
/* TODO: handle SCO */
session->out_device = AUDIO_DEVICE_NONE;
ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
}
void set_voice_session_volume(struct voice_session *session, float volume)
{
enum _SoundType sound_type;
switch (session->out_device) {
case AUDIO_DEVICE_OUT_EARPIECE:
sound_type = SOUND_TYPE_VOICE;
break;
case AUDIO_DEVICE_OUT_SPEAKER:
sound_type = SOUND_TYPE_SPEAKER;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
sound_type = SOUND_TYPE_HEADSET;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
case AUDIO_DEVICE_OUT_ALL_SCO:
sound_type = SOUND_TYPE_BTVOICE;
break;
default:
sound_type = SOUND_TYPE_VOICE;
}
ril_set_call_volume(&session->ril, sound_type, volume);
}
static void voice_session_wb_amr_callback(void *data, int enable)
{
struct audio_device *adev = (struct audio_device *)data;
struct voice_session *session =
(struct voice_session *)adev->voice.session;
pthread_mutex_lock(&adev->lock);
if (session->wb_amr != enable) {
session->wb_amr = enable;
/* reopen the modem PCMs at the new rate */
if (adev->voice.in_call) {
ALOGV("%s: %s wide band voice call",
__func__,
enable ? "Enable" : "Disable");
stop_voice_call(adev);
start_voice_call(adev);
}
}
pthread_mutex_unlock(&adev->lock);
}
struct voice_session *voice_session_init(struct audio_device *adev)
{
char voice_config[PROPERTY_VALUE_MAX];
struct voice_session *session;
int ret;
@ -39,6 +280,12 @@ struct voice_session *voice_session_init(void)
return NULL;
}
/* Two mic control */
ret = property_get_bool("audio_hal.disable_two_mic", false);
if (ret > 0) {
session->two_mic_disabled = true;
}
/* Do this as the last step so we do not have to close it on error */
ret = ril_open(&session->ril);
if (ret != 0) {
@ -46,6 +293,27 @@ struct voice_session *voice_session_init(void)
return NULL;
}
ret = property_get("audio_hal.force_voice_config", voice_config, "");
if (ret > 0) {
if ((strncmp(voice_config, "narrow", 6)) == 0)
session->wb_amr = false;
else if ((strncmp(voice_config, "wide", 4)) == 0)
session->wb_amr = true;
ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
} else {
/* register callback for wideband AMR setting */
ret = ril_set_wb_amr_callback(&session->ril,
voice_session_wb_amr_callback,
(void *)adev);
if (ret != 0) {
ALOGE("%s: Failed to register WB_AMR callback", __func__);
free(session);
return NULL;
}
ALOGV("%s: Registered WB_AMR callback", __func__);
}
return session;
}

@ -33,7 +33,14 @@ struct voice_session {
audio_devices_t out_device;
};
struct voice_session *voice_session_init(void);
void prepare_voice_session(struct voice_session *session,
audio_devices_t active_out_devices);
int start_voice_session(struct voice_session *session);
void stop_voice_session(struct voice_session *session);
void set_voice_session_volume(struct voice_session *session, float volume);
void set_voice_session_audio_path(struct voice_session *session);
struct voice_session *voice_session_init(struct audio_device *adev);
void voice_session_deinit(struct voice_session *s);
#endif /* VOICE_CALL_H */

Loading…
Cancel
Save