Professional Documents
Culture Documents
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/time.h>
#include <fcntl.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <cutils/str_parms.h>
#include <hardware/audio.h>
#include <hardware/hardware.h>
#include <linux/videodev2.h>
#include <videodev2_exynos_media.h>
#include <system/audio.h>
#include <tinyalsa/asoundlib.h>
#include <audio_utils/resampler.h>
#include "audio_route.h"
#define PCM_CARD 0
#define PCM_CARD_SPDIF 1
#define PCM_TOTAL 2
#define PCM_DEVICE 0
#define PCM_DEVICE_DEEP 1
#define PCM_DEVICE_HDMI 2
#define PCM_DEVICE_VOICE 3
#define PCM_DEVICE_SCO 4
#define AUDIO_DEVICE_OUT_WIRED_HEADSET_3_INCALL 12
enum output_type {
OUTPUT_DEEP_BUF, // deep PCM buffers output stream
OUTPUT_LOW_LATENCY, // low latency output stream
OUTPUT_HDMI, // HDMI multi channel
OUTPUT_TOTAL
};
struct audio_device {
struct audio_hw_device hw_device;
struct stream_out {
struct audio_stream_out stream;
struct stream_in {
struct audio_stream_in stream;
struct string_to_enum {
const char *name;
uint32_t value;
};
enum {
OUT_DEVICE_SPEAKER, /* 0 */
OUT_DEVICE_HEADSET, /* 1 */
OUT_DEVICE_HEADPHONES, /* 2 */
OUT_DEVICE_BT_SCO, /* 3 */
OUT_DEVICE_SPEAKER_AND_HEADSET, /* 4 */
OUT_DEVICE_TAB_SIZE, /* number of rows in route_configs[][] */
/* 5 */
OUT_DEVICE_NONE, /* 6 */
OUT_DEVICE_CNT /* 7 */
};
enum {
IN_SOURCE_MIC,
IN_SOURCE_CAMCORDER,
IN_SOURCE_VOICE_RECOGNITION,
IN_SOURCE_VOICE_COMMUNICATION,
IN_SOURCE_TAB_SIZE, /* number of lines in route_configs[][] */
IN_SOURCE_NONE,
IN_SOURCE_CNT
};
if (popcount(device) == 2) {
if ((device == (AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADSET)) ||
(device == (AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE)))
return OUT_DEVICE_SPEAKER_AND_HEADSET;
else
return OUT_DEVICE_NONE;
}
if (popcount(device) != 1)
return OUT_DEVICE_NONE;
switch (device) {
case AUDIO_DEVICE_OUT_SPEAKER:
return OUT_DEVICE_SPEAKER;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
return OUT_DEVICE_HEADSET;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
return OUT_DEVICE_HEADPHONES;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
return OUT_DEVICE_BT_SCO;
default:
return OUT_DEVICE_SPEAKER;
}
}
int get_input_source_id(audio_source_t source)
{
int i;
switch (source) {
case AUDIO_SOURCE_DEFAULT:
return IN_SOURCE_NONE;
case AUDIO_SOURCE_MIC:
return IN_SOURCE_MIC;
case AUDIO_SOURCE_VOICE_CALL:
return IN_SOURCE_VOICE_COMMUNICATION;
case AUDIO_SOURCE_CAMCORDER:
return IN_SOURCE_CAMCORDER;
case AUDIO_SOURCE_VOICE_RECOGNITION:
return IN_SOURCE_VOICE_RECOGNITION;
case AUDIO_SOURCE_VOICE_COMMUNICATION:
return IN_SOURCE_VOICE_RECOGNITION;
default:
return IN_SOURCE_NONE;
}
}
//mod by orange@2014/7/16
struct route_config {
const char * const output_route;
const char * const input_route;
};
/**
* NOTE: when multiple mutexes have to be acquired, always respect the
* following order: hw device > in stream > out stream
*/
/* Helper functions */
ret = open_hdmi_driver(adev);
if (ret < 0)
return ret;
ctrl.id = V4L2_CID_TV_ENABLE_HDMI_AUDIO;
ctrl.value = enable;
ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl);
if (ret < 0)
ALOGE("V4L2_CID_TV_ENABLE_HDMI_AUDIO ioctl error (%d)", errno);
return ret;
}
ret = open_hdmi_driver(adev);
if (ret < 0)
return ret;
ctrl.id = V4L2_CID_TV_MAX_AUDIO_CHANNELS;
ret = ioctl(adev->hdmi_drv_fd, VIDIOC_G_CTRL, &ctrl);
if (ret < 0) {
ALOGE("V4L2_CID_TV_MAX_AUDIO_CHANNELS ioctl error (%d)", errno);
return ret;
}
return ret;
}
int ret;
struct v4l2_control ctrl;
ret = open_hdmi_driver(adev);
if (ret < 0)
return ret;
ctrl.id = V4L2_CID_TV_SET_NUM_CHANNELS;
ctrl.value = channels;
ALOGE("V4L2_CID_TV_SET_NUM_CHANNELS ctrl.value=%d", ctrl.value);
ret = ioctl(adev->hdmi_drv_fd, VIDIOC_S_CTRL, &ctrl);
if (ret < 0)
ALOGE("V4L2_CID_TV_SET_NUM_CHANNELS ioctl error (%d)", errno);
return ret;
}
}
ALOGE("start_call\n");
pcm_start(adev->pcm_voice_out);
pcm_start(adev->pcm_voice_in);
return 0;
err_open_out:
pcm_close(adev->pcm_voice_out);
adev->pcm_voice_out = NULL;
return -ENOMEM;
}
pcm_stop(adev->pcm_voice_out);
pcm_stop(adev->pcm_voice_in);
pcm_close(adev->pcm_voice_out);
pcm_close(adev->pcm_voice_in);
adev->pcm_voice_out = NULL;
}
/* only needed for low latency output streams as other streams are not used
* for voice use cases */
if (adev->active_input) {
in = adev->active_input;
pthread_mutex_lock(&in->lock);
do_in_standby(in);
pthread_mutex_unlock(&in->lock);
}
if (input_source_id != IN_SOURCE_NONE) {
if (output_device_id != OUT_DEVICE_NONE) {
input_route =
route_configs[input_source_id][output_device_id]->input_route;
output_route =
route_configs[input_source_id][output_device_id]->output_route;
} else {
switch (adev->in_device) {
case AUDIO_DEVICE_IN_WIRED_HEADSET:
output_device_id = OUT_DEVICE_HEADSET;
break;
case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET:
output_device_id = OUT_DEVICE_BT_SCO;
break;
default:
output_device_id = OUT_DEVICE_SPEAKER;
break;
}
input_route =
route_configs[input_source_id][output_device_id]->input_route;
}
} else {
if (output_device_id != OUT_DEVICE_NONE) {
output_route =
route_configs[IN_SOURCE_MIC][output_device_id]->output_route;
}
}
//added by orangeyang@2014/7/28
/*if (adev->mode == AUDIO_MODE_IN_CALL) {
if (adev->out_device == 0x4 || adev->out_device == 0x6 || adev-
>out_device == 0x12) {
adev->out_device = 0x2;
output_route = "communication-speaker";
input_route = "communication-main-mic";
}
}*/
if (output_route)
audio_route_apply_path(adev->ar, output_route);
if (input_route)
audio_route_apply_path(adev->ar, input_route);
update_mixer_state(adev->ar);
}
if (adev->mode == AUDIO_MODE_IN_CALL) {
if (!adev->in_call) {
switch (temp_out_device) {
case AUDIO_DEVICE_OUT_SPEAKER:
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADSET;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
adev->out_device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
default:
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
}
start_call(adev);
select_devices(adev);
set_voice_volume(adev);
adev->in_call = 1;
}
} else {
ALOGV("Leaving IN_CALL state, in_call=%d, mode=%d",
adev->in_call, adev->mode);
if (adev->in_call) {
adev->in_call = 0;
end_call(adev);
force_all_standby(adev);
/*
if (adev->out_device == AUDIO_DEVICE_OUT_SPEAKER){
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_DEFAULT;
}else{
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADSET;
adev->input_source = AUDIO_SOURCE_DEFAULT;
}*/
switch (temp_out_device) {
case AUDIO_DEVICE_OUT_SPEAKER:
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_DEFAULT;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADSET;
adev->input_source = AUDIO_SOURCE_DEFAULT;
break;
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
adev->input_source = AUDIO_SOURCE_DEFAULT;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
adev->out_device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
adev->input_source = AUDIO_SOURCE_DEFAULT;
break;
default:
adev->out_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
adev->input_source = AUDIO_SOURCE_DEFAULT;
break;
}
ALOGV("D:adev->out_device:%d,adev->input_source:%d",adev-
>out_device,adev->input_source);
select_devices(adev);
}
}
if (out == adev->outputs[OUTPUT_HDMI]) {
force_non_hdmi_out_standby(adev);
} else if (adev->outputs[OUTPUT_HDMI] && !adev->outputs[OUTPUT_HDMI]->standby)
{
out->disabled = true;
return 0;
}
out->disabled = false;
dump_stream_out(out);
if (out->pcm[PCM_CARD_SPDIF] &&
!pcm_is_ready(out->pcm[PCM_CARD_SPDIF])) {
ALOGE("pcm_open(PCM_CARD_SPDIF) failed: %s",
pcm_get_error(out->pcm[PCM_CARD_SPDIF]));
pcm_close(out->pcm[PCM_CARD_SPDIF]);
return -ENOMEM;
}
}
adev->out_device |= out->device;
ALOGV("start_output_stream adev->out_device:%d",adev->out_device);
select_devices(adev);
return 0;
}
in->frames_in = 0;
adev->input_source = in->input_source;
adev->in_device = in->device;
ALOGV("start_input_stream adev->in_device:%x,adev->input_source:%d",adev-
>in_device,adev->input_source);
select_devices(adev);
return 0;
}
/* must be called with the hw device mutex locked, OK to hold other mutexes */
static void start_bt_sco(struct audio_device *adev) {
ALOGV("start_bt_sco() is called.\n");
pcm_start(adev->pcm_voice_out);
pcm_start(adev->pcm_sco_out);
pcm_start(adev->pcm_voice_in);
pcm_start(adev->pcm_sco_in);
return;
err_sco_in:
pcm_close(adev->pcm_sco_in);
err_voice_in:
pcm_close(adev->pcm_voice_in);
err_sco_out:
pcm_close(adev->pcm_sco_out);
err_voice_out:
pcm_close(adev->pcm_voice_out);
}
/* must be called with the hw device mutex locked, OK to hold other mutexes */
static void stop_bt_sco(struct audio_device *adev) {
pcm_stop(adev->pcm_voice_out);
pcm_stop(adev->pcm_sco_out);
pcm_stop(adev->pcm_voice_in);
pcm_stop(adev->pcm_sco_in);
pcm_close(adev->pcm_voice_out);
pcm_close(adev->pcm_sco_out);
pcm_close(adev->pcm_voice_in);
pcm_close(adev->pcm_sco_in);
if(adev->pcm_voice_out)
adev->pcm_voice_out = NULL;
if(adev->pcm_sco_out)
adev->pcm_sco_out = NULL;
if(adev->pcm_voice_in)
adev->pcm_voice_in = NULL;
if(adev->pcm_sco_in)
adev->pcm_sco_in = NULL;
}
/*
* take resampling into account and return the closest majoring
* multiple of 16 frames, as audioflinger expects audio buffers to
* be a multiple of 16 frames
*/
size = (pcm_config_in.period_size * sample_rate) / pcm_config_in.rate;
size = ((size + 15) / 16) * 16;
if (in->pcm == NULL) {
buffer->raw = NULL;
buffer->frame_count = 0;
in->read_status = -ENODEV;
return -ENODEV;
}
if (in->frames_in == 0) {
in->read_status = pcm_read(in->pcm,
(void*)in->buffer,
pcm_frames_to_bytes(in->pcm,
pcm_config_in.period_size));
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 = pcm_config_in.period_size;
return in->read_status;
in->frames_in -= buffer->frame_count;
}
/* read_frames() reads frames from kernel driver, down samples to capture rate
* if necessary and output the number of frames requested to the buffer specified
*/
static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames)
{
ssize_t frames_wr = 0;
size_t frame_size = audio_stream_frame_size(&in->stream.common);
while (frames_wr < frames) {
size_t frames_rd = frames - frames_wr;
if (in->resampler != NULL) {
in->resampler->resample_from_provider(in->resampler,
(int16_t *)((char *)buffer +
frames_wr * frame_size),
&frames_rd);
} else {
struct resampler_buffer buf = {
{ raw : NULL, },
frame_count : frames_rd,
};
get_next_buffer(&in->buf_provider, &buf);
if (buf.raw != NULL) {
memcpy((char *)buffer +
frames_wr * frame_size,
buf.raw,
buf.frame_count * frame_size);
frames_rd = buf.frame_count;
}
release_buffer(&in->buf_provider, &buf);
}
/* in->read_status is updated by getNextBuffer() also called by
* in->resampler->resample_from_provider() */
if (in->read_status != 0)
return in->read_status;
frames_wr += frames_rd;
}
return frames_wr;
}
/* API functions */
return out->config.rate;
}
return out->config.period_size *
audio_stream_frame_size((struct audio_stream *)stream);
}
return devices;
}
if (!out->standby) {
for (i = 0; i < PCM_TOTAL; i++) {
if (out->pcm[i]) {
pcm_close(out->pcm[i]);
out->pcm[i] = NULL;
}
}
out->standby = true;
if (out == adev->outputs[OUTPUT_HDMI]) {
/* force standby on low latency output stream so that it can reuse HDMI
driver if
* necessary when restarted */
force_non_hdmi_out_standby(adev);
}
return 0;
}
pthread_mutex_lock(&out->dev->lock);
pthread_mutex_lock(&out->lock);
ret = do_out_standby(out);
pthread_mutex_unlock(&out->lock);
pthread_mutex_unlock(&out->dev->lock);
return ret;
}
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
value, sizeof(value));
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
if (ret >= 0) {
val = atoi(value);
if ((out->device != val) && (val != 0)) {
/* Force standby if moving to/from SPDIF or if the output
* device changes when in SPDIF mode */
if (((val & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ^
(adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) ||
(adev->out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) {
do_out_standby(out);
}
select_devices(adev);
do_out_standby(out);
}
/*
* add by yaowei.li@samsung.com
* change from different device
*/
if (adev->in_call){
if (val ^ adev->out_device){
switch (val) {
case AUDIO_DEVICE_OUT_SPEAKER:
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
//added by orangeyang@2014/8/7
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
adev->out_device =
AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
//added end
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
adev->out_device =
AUDIO_DEVICE_OUT_WIRED_HEADSET;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
adev->out_device =
AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
default:
adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
adev->input_source = AUDIO_SOURCE_VOICE_CALL;
break;
}
out->device = adev->out_device;
ALOGV("adev->in_call out_device: %d,input_source:
%d",adev->out_device,adev->input_source);
select_devices(adev);
} else {
out->device = val;
}
} else {
out->device = val;
}
}
}
pthread_mutex_unlock(&out->lock);
pthread_mutex_unlock(&adev->lock);
str_parms_destroy(parms);
return ret;
}
str_parms_destroy(query);
str_parms_destroy(reply);
return str;
}
/*
* acquiring hw device mutex systematically is useful if a low
* priority thread is waiting on the output stream mutex - e.g.
* executing out_set_parameters() while holding the hw device
* mutex
*/
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&out->lock);
if (out->standby) {
ret = start_output_stream(out);
if (ret != 0) {
pthread_mutex_unlock(&adev->lock);
goto exit;
}
out->standby = false;
}
pthread_mutex_unlock(&adev->lock);
if (out->disabled) {
ret = -EPIPE;
goto exit;
}
if (ret != 0) {
usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
out_get_sample_rate(&stream->common));
}
return bytes;
}
return in->requested_rate;
}
return 0;
}
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
ret = do_in_standby(in);
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&in->dev->lock);
return ret;
}
parms = str_parms_create_str(kvpairs);
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
value, sizeof(value));
if (ret >= 0) {
val = atoi(value);
/* no audio source uses val == 0 */
if ((in->input_source != val) && (val != 0)) {
in->input_source = val;
apply_now = !in->standby;
}
}
if (apply_now) {
adev->input_source = in->input_source;
adev->in_device = in->device;
select_devices(adev);
}
pthread_mutex_unlock(&in->lock);
pthread_mutex_unlock(&adev->lock);
str_parms_destroy(parms);
return ret;
}
in->ramp_vol = vol;
in->ramp_frames -= frames;
}
/*
* acquiring hw device mutex systematically is useful if a low
* priority thread is waiting on the input stream mutex - e.g.
* executing in_set_parameters() while holding the hw device
* mutex
*/
pthread_mutex_lock(&adev->lock);
pthread_mutex_lock(&in->lock);
if (in->standby) {
ret = start_input_stream(in);
if (ret == 0)
in->standby = 0;
}
pthread_mutex_unlock(&adev->lock);
if (ret < 0)
goto exit;
/*if (in->num_preprocessors != 0)
ret = process_frames(in, buffer, frames_rq);
else */
ret = read_frames(in, buffer, frames_rq);
if (ret > 0)
ret = 0;
if (in->ramp_frames > 0)
in_apply_ramp(in, buffer, frames_rq);
/*
* Instead of writing zeroes here, we could trust the hardware
* to always provide zeroes when muted.
*/
if (ret == 0 && adev->mic_mute)
memset(buffer, 0, bytes);
exit:
if (ret < 0)
usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
in_get_sample_rate(&stream->common));
pthread_mutex_unlock(&in->lock);
return bytes;
}
out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
if (devices == AUDIO_DEVICE_NONE)
devices = AUDIO_DEVICE_OUT_SPEAKER;
out->device = devices;
out->stream.common.get_sample_rate = out_get_sample_rate;
out->stream.common.set_sample_rate = out_set_sample_rate;
out->stream.common.get_buffer_size = out_get_buffer_size;
out->stream.common.get_channels = out_get_channels;
out->stream.common.get_format = out_get_format;
out->stream.common.set_format = out_set_format;
out->stream.common.standby = out_standby;
out->stream.common.dump = out_dump;
out->stream.common.set_parameters = out_set_parameters;
out->stream.common.get_parameters = out_get_parameters;
out->stream.common.add_audio_effect = out_add_audio_effect;
out->stream.common.remove_audio_effect = out_remove_audio_effect;
out->stream.get_latency = out_get_latency;
out->stream.set_volume = out_set_volume;
out->stream.write = out_write;
out->stream.get_render_position = out_get_render_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
out->dev = adev;
config->format = out_get_format(&out->stream.common);
config->channel_mask = out_get_channels(&out->stream.common);
config->sample_rate = out_get_sample_rate(&out->stream.common);
out->standby = true;
pthread_mutex_lock(&adev->lock);
if (adev->outputs[type]) {
pthread_mutex_unlock(&adev->lock);
ret = -EBUSY;
goto err_open;
}
adev->outputs[type] = out;
pthread_mutex_unlock(&adev->lock);
*stream_out = &out->stream;
return 0;
err_open:
free(out);
*stream_out = NULL;
return ret;
}
out_standby(&stream->common);
adev = (struct audio_device *)dev;
pthread_mutex_lock(&adev->lock);
for (type = 0; type < OUTPUT_TOTAL; ++type) {
if (adev->outputs[type] == (struct stream_out *) stream) {
adev->outputs[type] = NULL;
break;
}
}
pthread_mutex_unlock(&adev->lock);
free(stream);
}
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
ALOGV("### bluetooth call volume");
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
ALOGV("### headset call volume");
mixer_name = "Headphone Volume";
break;
default:
//mod by orangeyang@2014/8/19
//ALOGV("Call volume setting error!");
ALOGV("### speaker call volume");
mixer_name = "Speaker Volume";
//mod end
break;
}
if (NULL == mixer_name){
ALOGV("mixer_name is empty.");
return 0;
}
ctl = mixer_get_ctl_by_name(mMixer, mixer_name);
if (NULL != ctl) {
unsigned int idx;
for (idx = 0 ; mixer_ctl_get_num_values(ctl) > idx ; ++idx)
mixer_ctl_set_percent(ctl, idx, int_volume);
ALOGV("write() wakeup setting %s(%d)", mixer_name, int_volume);
} else {
ALOGV("failed get mixer control");
}
mixer_close(mMixer);
return 0;
}
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
{
struct audio_device *adev = (struct audio_device *) dev;
adev->voice_volume = volume;
pthread_mutex_lock(&adev->lock);
if (adev->mode == AUDIO_MODE_IN_CALL)
set_voice_volume(adev);
pthread_mutex_unlock(&adev->lock);
return 0;
}
adev->mic_mute = state;
return 0;
}
*state = adev->mic_mute;
return 0;
}
*stream_in = NULL;
in->stream.common.get_sample_rate = in_get_sample_rate;
in->stream.common.set_sample_rate = in_set_sample_rate;
in->stream.common.get_buffer_size = in_get_buffer_size;
in->stream.common.get_channels = in_get_channels;
in->stream.common.get_format = in_get_format;
in->stream.common.set_format = in_set_format;
in->stream.common.standby = in_standby;
in->stream.common.dump = in_dump;
in->stream.common.set_parameters = in_set_parameters;
in->stream.common.get_parameters = in_get_parameters;
in->stream.common.add_audio_effect = in_add_audio_effect;
in->stream.common.remove_audio_effect = in_remove_audio_effect;
in->stream.set_gain = in_set_gain;
in->stream.read = in_read;
in->stream.get_input_frames_lost = in_get_input_frames_lost;
in->dev = adev;
in->standby = true;
in->requested_rate = config->sample_rate;
in->input_source = AUDIO_SOURCE_DEFAULT;
in->device = devices;
in->io_handle = handle;
if (!in->buffer) {
ret = -ENOMEM;
goto err_malloc;
}
if (in->requested_rate != pcm_config_in.rate) {
in->buf_provider.get_next_buffer = get_next_buffer;
in->buf_provider.release_buffer = release_buffer;
ret = create_resampler(pcm_config_in.rate,
in->requested_rate,
1,
RESAMPLER_QUALITY_DEFAULT,
&in->buf_provider,
&in->resampler);
if (ret != 0) {
ret = -EINVAL;
goto err_resampler;
}
}
*stream_in = &in->stream;
return 0;
err_resampler:
free(in->buffer);
err_malloc:
free(in);
return ret;
}
in_standby(&stream->common);
if (in->resampler) {
release_resampler(in->resampler);
in->resampler = NULL;
}
free(in->buffer);
free(stream);
}
audio_route_free(adev->ar);
if (adev->hdmi_drv_fd >= 0)
close(adev->hdmi_drv_fd);
free(device);
return 0;
}
adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
adev->hw_device.common.module = (struct hw_module_t *) module;
adev->hw_device.common.close = adev_close;
adev->hw_device.init_check = adev_init_check;
adev->hw_device.set_voice_volume = adev_set_voice_volume;
adev->hw_device.set_master_volume = adev_set_master_volume;
adev->hw_device.set_mode = adev_set_mode;
adev->hw_device.set_mic_mute = adev_set_mic_mute;
adev->hw_device.get_mic_mute = adev_get_mic_mute;
adev->hw_device.set_parameters = adev_set_parameters;
adev->hw_device.get_parameters = adev_get_parameters;
adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
adev->hw_device.open_output_stream = adev_open_output_stream;
adev->hw_device.close_output_stream = adev_close_output_stream;
adev->hw_device.open_input_stream = adev_open_input_stream;
adev->hw_device.close_input_stream = adev_close_input_stream;
adev->hw_device.dump = adev_dump;
adev->ar = audio_route_init();
adev->input_source = AUDIO_SOURCE_DEFAULT;
/* adev->cur_route_id initial value is 0 and such that first device
* selection is always applied by select_devices() */
adev->hdmi_drv_fd = -1;
*device = &adev->hw_device.common;
return 0;
}