Skip to content
Browse files

audio: Try audio HAL grouper style

  • Loading branch information...
1 parent 4046fbb commit fbb9f8e8bb41801728ee0b42d7007a05248c858c @Borkata Borkata committed Dec 13, 2012
Showing with 1,083 additions and 860 deletions.
  1. +3 −1 BoardConfigCommon.mk
  2. +7 −5 audio/Android.mk
  3. +491 −853 audio/audio_hw.c
  4. +494 −0 audio/audio_route.c
  5. +33 −0 audio/audio_route.h
  6. +8 −1 device-common.mk
  7. +47 −0 files/mixer_paths.xml
View
4 BoardConfigCommon.mk
@@ -16,7 +16,9 @@
# This variable is set first, so it can be overridden
# by BoardConfigVendor.mk
-BOARD_USES_GENERIC_AUDIO := true
+BOARD_USES_GENERIC_AUDIO := false
+BOARD_USES_ALSA_AUDIO := false
+
USE_CAMERA_STUB := false
BOARD_USES_AUDIO_LEGACY := false
View
12 audio/Android.mk 100755 → 100644
@@ -1,4 +1,4 @@
-# Copyright (C) 2011 The Android Open Source Project
+# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -18,12 +18,14 @@ include $(CLEAR_VARS)
LOCAL_MODULE := audio.primary.harmony
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
-LOCAL_SRC_FILES := audio_hw.c
+LOCAL_SRC_FILES := \
+ audio_hw.c \
+ audio_route.c
LOCAL_C_INCLUDES += \
external/tinyalsa/include \
- system/media/audio_utils/include \
- system/media/audio_effects/include
-LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libdl
+ external/expat/lib \
+ $(call include-path-for, audio-utils)
+LOCAL_SHARED_LIBRARIES := liblog libcutils libtinyalsa libaudioutils libexpat
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
View
1,344 audio/audio_hw.c
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
- * Copyright (C) 2011-12 Eduardo Jose Tagle <ejtagle@tutopia.com>
+ * Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +15,7 @@
*/
#define LOG_TAG "audio_hw_primary"
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
#include <errno.h>
#include <pthread.h>
@@ -35,50 +34,16 @@
#include <tinyalsa/asoundlib.h>
+#include <audio_utils/resampler.h>
-/* Mixer control names */
-#define MIXER_PCM_PLAYBACK_VOLUME "PCM Playback Volume"
-#define MIXER_PCM_CAPTURE_VOLUME "Rec Capture Volume"
+#include "audio_route.h"
-#define MIXER_HEADSET_PLAYBACK_VOLUME "Headphone Playback Volume"
-#define MIXER_SPEAKER_PLAYBACK_VOLUME "Auxout Playback Volume"
-#define MIXER_MIC_CAPTURE_VOLUME "Mic1 Capture Volume" /*ok*/
+#define PCM_CARD 0
+#define PCM_DEVICE 0
+#define PCM_DEVICE_HDMI 1
+#define PCM_DEVICE_SCO 2
-#define MIXER_HEADSET_PLAYBACK_SWITCH "Headphone Playback Switch"
-#define MIXER_SPEAKER_PLAYBACK_SWITCH "Auxout Playback Switch"
-#define MIXER_MIC_LEFT_CAPTURE_SWITCH "Left Capture Mix Mic1 Capture Switch"
-#define MIXER_MIC_RIGHT_CAPTURE_SWITCH "Right Capture Mix Mic1 Capture Switch"
-
-#define HEADPHONE_JACK_SWITCH "Headphone Jack Switch"
-#define INTERNAL_SPEAKER_SWITCH "Int Spk Switch"
-#define INTERNAL_MIC_SWITCH "Int Mic Switch"
-
-#define MIXER_HPL_OUTMUX "Left Headphone Mux"
-#define MIXER_HPR_OUTMUX "Right Headphone Mux"
-#define MIXER_AUX_OUTMUX "AuxOut Mux"
-#define MIXER_HP_ENABLE_DAC "HP Mix DAC2HP Playback Switch"
-#define MIXER_SPK_ENABLE_DAC "Speaker Mix DAC2SPK Playback Switch"
-
-#define MIXER_HP_LEFT "HP Left Mix"
-#define MIXER_HP_RIGHT "HP Right Mix"
-#define MIXER_SPK "HPOut Mix"
-
-/* ALSA card */
-#define PCM_CARD_ADAM 0
-
-/* ALSA ports for card0 */
-#define PCM_DEVICE_MM 0 /* CODEC port */
-#define PCM_DEVICE_SCO 1 /* Bluetooth/3G port */
-#define PCM_DEVICE_SPDIF 2 /* SPDIF (HDMI) port */
-
-/* conversions from Percent to codec gains */
-#define PERC_TO_PCM_VOLUME(x) ( (int)((x) * 31 ))
-#define PERC_TO_CAPTURE_VOLUME(x) ( (int)((x) * 31 ))
-#define PERC_TO_MIC_VOLUME(x) ( (int)((x) * 31 ))
-#define PERC_TO_HEADSET_VOLUME(x) ( (int)((x) * 31 ))
-#define PERC_TO_SPEAKER_VOLUME(x) ( (int)((x) * 31 ))
-
-#define OUT_PERIOD_SIZE 880
+#define OUT_PERIOD_SIZE 512
#define OUT_SHORT_PERIOD_COUNT 2
#define OUT_LONG_PERIOD_COUNT 8
#define OUT_SAMPLING_RATE 48000
@@ -91,9 +56,6 @@
#define SCO_PERIOD_COUNT 4
#define SCO_SAMPLING_RATE 8000
-/* We need this stabilization time before outputting captured audio to app */
-#define FRAMES_MUTED_AT_CAPTURE_START 2048
-
/* minimum sleep time in out_write() when write threshold is not reached */
#define MIN_WRITE_SLEEP_US 2000
#define MAX_WRITE_SLEEP_US ((OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT * 1000000) \
@@ -105,7 +67,7 @@ enum {
OUT_BUFFER_TYPE_LONG,
};
-static const struct pcm_config pcm_config_out = {
+struct pcm_config pcm_config_out = {
.channels = 2,
.rate = OUT_SAMPLING_RATE,
.period_size = OUT_PERIOD_SIZE,
@@ -114,7 +76,7 @@ static const struct pcm_config pcm_config_out = {
.start_threshold = OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT,
};
-static const struct pcm_config pcm_config_in = {
+struct pcm_config pcm_config_in = {
.channels = 2,
.rate = IN_SAMPLING_RATE,
.period_size = IN_PERIOD_SIZE,
@@ -124,271 +86,24 @@ static const struct pcm_config pcm_config_in = {
.stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT),
};
-static const struct pcm_config pcm_config_sco = {
+struct pcm_config pcm_config_sco = {
.channels = 1,
.rate = SCO_SAMPLING_RATE,
.period_size = SCO_PERIOD_SIZE,
.period_count = SCO_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
-struct route_setting
-{
- char *ctl_name;
- int intval;
- char *strval;
- int post_delay_ms;
-};
-
-/* These are values that never change */
-struct route_setting defaults[] = {
- /* general */
- {
- .ctl_name = MIXER_PCM_PLAYBACK_VOLUME,
- .intval = 20,
- },
- {
- .ctl_name = MIXER_PCM_CAPTURE_VOLUME,
- .intval = PERC_TO_CAPTURE_VOLUME(1),
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_VOLUME,
- .intval = PERC_TO_HEADSET_VOLUME(1),
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_VOLUME,
- .intval = 20,
- },
- {
- .ctl_name = MIXER_MIC_CAPTURE_VOLUME,
- .intval = PERC_TO_MIC_VOLUME(1),
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = HEADPHONE_JACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = INTERNAL_SPEAKER_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = MIXER_MIC_LEFT_CAPTURE_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = MIXER_MIC_RIGHT_CAPTURE_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = INTERNAL_MIC_SWITCH,
- .intval = 1,
- .post_delay_ms = 200, /* Perform a post delay of 200 ms before next step */
- },
- {
- .ctl_name = MIXER_HPL_OUTMUX,
- .strval = MIXER_HP_LEFT,
- },
- {
- .ctl_name = MIXER_HPR_OUTMUX,
- .strval = MIXER_HP_RIGHT,
- },
- {
- .ctl_name = MIXER_AUX_OUTMUX,
- .strval = MIXER_SPK,
- },
- {
- .ctl_name = MIXER_HP_ENABLE_DAC,
- .intval = 1,
- },
- {
- .ctl_name = MIXER_SPK_ENABLE_DAC,
- .intval = 1,
- },
- {
- .ctl_name = NULL,
- }
-};
-
-/* Headphone playback route */
-struct route_setting headphone_route[] = {
- {
- .ctl_name = HEADPHONE_JACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = INTERNAL_SPEAKER_SWITCH,
- .intval = 0,
- .post_delay_ms = 200, /* Post delay before next step to avoid pops */
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = NULL,
- }
-};
-
-/* Speaker playback route */
-struct route_setting speaker_route[] = {
- {
- .ctl_name = HEADPHONE_JACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = INTERNAL_SPEAKER_SWITCH,
- .intval = 1,
- .post_delay_ms = 200, /* Post delay before next step to avoid pops */
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = NULL,
- }
-};
-
-/* Speaker Headphone playback route */
-struct route_setting speaker_headphone_route[] = {
- {
- .ctl_name = HEADPHONE_JACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = INTERNAL_SPEAKER_SWITCH,
- .intval = 1,
- .post_delay_ms = 200, /* Post delay before next step to avoid pops */
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_SWITCH,
- .intval = 1,
- },
- {
- .ctl_name = NULL,
- }
-};
-
-/* No out route */
-struct route_setting no_out_route[] = {
- {
- .ctl_name = HEADPHONE_JACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = MIXER_HEADSET_PLAYBACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = INTERNAL_SPEAKER_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = MIXER_SPEAKER_PLAYBACK_SWITCH,
- .intval = 0,
- },
- {
- .ctl_name = NULL,
- }
-};
-
-struct mixer_ctls
-{
- struct mixer_ctl *pcm_volume;
- struct mixer_ctl *pcm_cap_volume;
- struct mixer_ctl *headset_volume;
- struct mixer_ctl *speaker_volume;
- struct mixer_ctl *mic_volume;
- struct mixer_ctl *mic_switch_left;
- struct mixer_ctl *mic_switch_right;
- struct mixer_ctl *LHPMux;
- struct mixer_ctl *RHPMux;
- struct mixer_ctl *SpkMux;
- struct mixer_ctl *HPEnDAC;
- struct mixer_ctl *SpkEnDAC;
-};
-
-static int msleep(unsigned long milisec)
-{
- struct timespec req={0};
- time_t sec=(int)(milisec/1000);
- milisec=milisec-(sec*1000);
- req.tv_sec=sec;
- req.tv_nsec=milisec*1000000L;
- while(nanosleep(&req,&req)==-1 && errno == EINTR)
- continue;
- return 1;
-}
-
-/* The enable flag when 0 makes the assumption that enums are disabled by
- * "Off" and integers/booleans by 0 */
-static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
- int enable)
-{
- struct mixer_ctl *ctl;
- unsigned int i, j;
-
- /* Go through the route array and set each value */
- i = 0;
- while (route[i].ctl_name) {
- ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
- if (!ctl)
- return -EINVAL;
-
- if (route[i].strval) {
- if (enable)
- mixer_ctl_set_enum_by_string(ctl, route[i].strval);
- else
- mixer_ctl_set_enum_by_string(ctl, "Off");
- } else {
- /* This ensures multiple (i.e. stereo) values are set jointly */
- for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
- if (enable)
- mixer_ctl_set_value(ctl, j, route[i].intval);
- else
- mixer_ctl_set_value(ctl, j, 0);
- }
- }
- /* Perform a delay if needed for stabilization purposes */
- if (route[i].post_delay_ms) {
- msleep(route[i].post_delay_ms);
- }
- i++;
- }
-
- return 0;
-}
-
struct audio_device {
struct audio_hw_device hw_device;
- pthread_mutex_t lock; /* see note below on mutex acquisition order */
- struct mixer *mixer;
- struct mixer_ctls mixer_ctls;
- unsigned int devices;
- bool standby;
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ unsigned int out_device;
+ unsigned int in_device;
+ bool standby;
bool mic_mute;
-
+ struct audio_route *ar;
+ int orientation;
bool screen_off;
struct stream_out *active_out;
@@ -398,41 +113,58 @@ struct audio_device {
struct stream_out {
struct audio_stream_out stream;
- pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm *pcm;
- struct pcm_config pcm_config; /* HW config being used */
+ struct pcm_config *pcm_config;
bool standby;
+ struct resampler_itfe *resampler;
+ int16_t *buffer;
+ size_t buffer_frames;
+
int write_threshold;
int cur_write_threshold;
int buffer_type;
struct audio_device *dev;
};
-#define MAX_PREPROCESSORS 3 /* maximum one AGC + one NS + one AEC per input stream */
-
struct stream_in {
struct audio_stream_in stream;
- pthread_mutex_t lock; /* see note below on mutex acquisition order */
+ pthread_mutex_t lock; /* see note below on mutex acquisition order */
struct pcm *pcm;
- struct pcm_config pcm_config; /* HW config being used */
+ struct pcm_config *pcm_config;
bool standby;
- unsigned int subsample_shift; /* Subsampling factor (1<<shift), to emulate different submultiples of sampling rate .Only supported values are 0,1,2 */
- int16_t* buffer; /* Temporary buffer to make subsampling */
- unsigned int frames_to_mute; /* Count of samples to be muted. Hw needs some stabilization time, otherwise, a POP is captured that fools Voice Recognizer */
+ unsigned int requested_rate;
+ struct resampler_itfe *resampler;
+ struct resampler_buffer_provider buf_provider;
+ int16_t *buffer;
+ size_t buffer_size;
+ size_t frames_in;
+ int read_status;
struct audio_device *dev;
};
+enum {
+ ORIENTATION_LANDSCAPE,
+ ORIENTATION_PORTRAIT,
+ ORIENTATION_SQUARE,
+ ORIENTATION_UNDEFINED,
+};
+
static uint32_t out_get_sample_rate(const struct audio_stream *stream);
static size_t out_get_buffer_size(const struct audio_stream *stream);
static audio_format_t out_get_format(const struct audio_stream *stream);
static uint32_t in_get_sample_rate(const struct audio_stream *stream);
static size_t in_get_buffer_size(const struct audio_stream *stream);
static audio_format_t in_get_format(const struct audio_stream *stream);
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer);
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer);
/*
* NOTE: when multiple mutexes have to be acquired, always take the
@@ -443,64 +175,88 @@ static audio_format_t in_get_format(const struct audio_stream *stream);
/* Helper functions */
static void select_devices(struct audio_device *adev)
-{
- /* Switch between speaker and headphone if required */
- switch (adev->devices & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) {
- case 0:
- set_route_by_array(adev->mixer, no_out_route, 1);
- break;
- case AUDIO_DEVICE_OUT_SPEAKER:
- set_route_by_array(adev->mixer, speaker_route, 1);
- break;
- case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
- set_route_by_array(adev->mixer, headphone_route, 1);
- break;
- case AUDIO_DEVICE_OUT_WIRED_HEADPHONE | AUDIO_DEVICE_OUT_SPEAKER:
- set_route_by_array(adev->mixer, speaker_headphone_route, 1);
- break;
- }
-
- ALOGD("Headphone out:%c, Speaker out:%c, HDMI out:%c, BT out:%c\n",
- (adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) ? 'Y' : 'N',
- (adev->devices & AUDIO_DEVICE_OUT_SPEAKER) ? 'Y' : 'N',
- (adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) ? 'Y' : 'N',
- (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO) ? 'Y' : 'N'
- );
+{
+ ALOGD("select_devices+");
+ int headphone_on;
+ int speaker_on;
+ int hdmi_on;
+ int main_mic_on;
+
+ ALOGV("out_devices=%d in_devices=%d active_out=%c active_in=%c", adev->out_device, adev->in_device, adev->active_out!=NULL?'y':'n', adev->active_in!=NULL?'y':'n');
+
+ headphone_on = adev->out_device & (AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
+ speaker_on = adev->out_device & AUDIO_DEVICE_OUT_SPEAKER;
+ hdmi_on = adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL;
+ main_mic_on = adev->in_device & AUDIO_DEVICE_IN_BUILTIN_MIC;
+
+ reset_mixer_state(adev->ar);
+
+ if (speaker_on)
+ audio_route_apply_path(adev->ar, "speaker");
+ if (headphone_on)
+ audio_route_apply_path(adev->ar, "headphone");
+ if (hdmi_on)
+ audio_route_apply_path(adev->ar, "hdmi");
+ if (main_mic_on)
+ audio_route_apply_path(adev->ar, "mic");
+
+
+ update_mixer_state(adev->ar);
+
+ ALOGV("hp=%c speaker=%c hdmi=%c main-mic=%c", headphone_on ? 'y' : 'n',
+ speaker_on ? 'y' : 'n', hdmi_on ? 'y' : 'n', main_mic_on ? 'y' : 'n');
+ ALOGD("select_devices-");
}
/* must be called with hw device and output stream mutexes locked */
static void do_out_standby(struct stream_out *out)
{
struct audio_device *adev = out->dev;
+ ALOGV("do_out_standby+");
+
if (!out->standby) {
- pcm_close(out->pcm);
+ ALOGD("do_out_standby++");
+ pcm_close(out->pcm);
out->pcm = NULL;
adev->active_out = NULL;
-
+ if (out->resampler) {
+ release_resampler(out->resampler);
+ out->resampler = NULL;
+ }
+ if (out->buffer) {
+ free(out->buffer);
+ out->buffer = NULL;
+ }
out->standby = true;
}
+ ALOGV("do_out_standby-");
}
/* must be called with hw device and input stream mutexes locked */
static void do_in_standby(struct stream_in *in)
{
struct audio_device *adev = in->dev;
+ ALOGV("do_in_standby+");
+
if (!in->standby) {
+ ALOGD("do_in_standby++");
pcm_close(in->pcm);
in->pcm = NULL;
-
- if (in->buffer) {
- free(in->buffer);
- in->buffer = NULL;
- }
-
adev->active_in = NULL;
-
+ if (in->resampler) {
+ release_resampler(in->resampler);
+ in->resampler = NULL;
+ }
+ if (in->buffer) {
+ free(in->buffer);
+ in->buffer = NULL;
+ }
in->standby = true;
}
+ ALOGV("do_in_standby-");
}
/* must be called with hw device and output stream mutexes locked */
@@ -510,31 +266,71 @@ static int start_output_stream(struct stream_out *out)
unsigned int device;
int ret;
- if (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
- /* Bluetooth uses a fixed format */
+ /*
+ * Due to the lack of sample rate converters in the SoC,
+ * it greatly simplifies things to have only the main
+ * (speaker/headphone) PCM or the BC SCO PCM open at
+ * the same time.
+ */
+ if(adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ device = PCM_DEVICE_HDMI;
+ out->pcm_config = &pcm_config_out;
+ } else
+ if (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
device = PCM_DEVICE_SCO;
- memcpy(&out->pcm_config,&pcm_config_sco,sizeof(out->pcm_config));
+ out->pcm_config = &pcm_config_sco;
} else {
- if (adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
- device = PCM_DEVICE_SPDIF;
- } else {
- device = PCM_DEVICE_MM;
- }
-
- memcpy(&out->pcm_config,&pcm_config_out,sizeof(out->pcm_config));
+ device = PCM_DEVICE;
+ out->pcm_config = &pcm_config_out;
out->buffer_type = OUT_BUFFER_TYPE_UNKNOWN;
- }
+ }
- ALOGD("start_output_stream: device:%d, rate:%d, channels:%d",device,out->pcm_config.rate, out->pcm_config.channels);
- out->pcm = pcm_open(PCM_CARD_ADAM, device, PCM_OUT | PCM_NORESTART, &out->pcm_config);
+ /*
+ * All open PCMs can only use a single group of rates at once:
+ * Group 1: 11.025, 22.05, 44.1
+ * Group 2: 8, 16, 32, 48
+ * Group 1 is used for digital audio playback since 44.1 is
+ * the most common rate, but group 2 is required for SCO.
+ */
+ if (adev->active_in) {
+ struct stream_in *in = adev->active_in;
+ pthread_mutex_lock(&in->lock);
+ if (((out->pcm_config->rate % 8000 == 0) &&
+ (in->pcm_config->rate % 8000) != 0) ||
+ ((out->pcm_config->rate % 11025 == 0) &&
+ (in->pcm_config->rate % 11025) != 0))
+ do_in_standby(in);
+ pthread_mutex_unlock(&in->lock);
+ }
+
+ ALOGD("outpcm open: card=%d, device=%d, rate=%d", PCM_CARD, device, out->pcm_config->rate);
+
+ out->pcm = pcm_open(PCM_CARD, device, PCM_OUT | PCM_NORESTART, out->pcm_config);
if (out->pcm && !pcm_is_ready(out->pcm)) {
ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm));
pcm_close(out->pcm);
return -ENOMEM;
}
- adev->active_out = out;
+ /*
+ * If the stream rate differs from the PCM rate, we need to
+ * create a resampler.
+ */
+ if (out_get_sample_rate(&out->stream.common) != out->pcm_config->rate) {
+ ret = create_resampler(out_get_sample_rate(&out->stream.common),
+ out->pcm_config->rate,
+ out->pcm_config->channels,
+ RESAMPLER_QUALITY_DEFAULT,
+ NULL,
+ &out->resampler);
+ out->buffer_frames = (pcm_config_out.period_size * out->pcm_config->rate) /
+ out_get_sample_rate(&out->stream.common) + 1;
+
+ out->buffer = malloc(pcm_frames_to_bytes(out->pcm, out->buffer_frames));
+ }
+
+ adev->active_out = out;
return 0;
}
@@ -546,90 +342,203 @@ static int start_input_stream(struct stream_in *in)
unsigned int device;
int ret;
- if (adev->devices & AUDIO_DEVICE_IN_ALL_SCO) {
+ /*
+ * Due to the lack of sample rate converters in the SoC,
+ * it greatly simplifies things to have only the main
+ * mic PCM or the BC SCO PCM open at the same time.
+ */
+ if (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO) {
device = PCM_DEVICE_SCO;
- memcpy(&in->pcm_config,&pcm_config_sco,sizeof(in->pcm_config));
+ in->pcm_config = &pcm_config_sco;
} else {
- if (adev->devices & AUDIO_DEVICE_IN_AUX_DIGITAL) {
- device = PCM_DEVICE_SPDIF;
- } else {
- device = PCM_DEVICE_MM;
- }
- memcpy(&in->pcm_config,&pcm_config_in,sizeof(in->pcm_config));
+ device = PCM_DEVICE;
+ in->pcm_config = &pcm_config_in;
}
- ALOGD("start_input_stream: device:%d, rate:%d, channels:%d",device,in->pcm_config.rate,in->pcm_config.channels);
- in->pcm = pcm_open(PCM_CARD_ADAM, device, PCM_IN, &in->pcm_config);
+ /*
+ * All open PCMs can only use a single group of rates at once:
+ * Group 1: 11.025, 22.05, 44.1
+ * Group 2: 8, 16, 32, 48
+ * Group 1 is used for digital audio playback since 44.1 is
+ * the most common rate, but group 2 is required for SCO.
+ */
+ if (adev->active_out) {
+ struct stream_out *out = adev->active_out;
+ pthread_mutex_lock(&out->lock);
+ if (((in->pcm_config->rate % 8000 == 0) &&
+ (out->pcm_config->rate % 8000) != 0) ||
+ ((in->pcm_config->rate % 11025 == 0) &&
+ (out->pcm_config->rate % 11025) != 0))
+ do_out_standby(out);
+ pthread_mutex_unlock(&out->lock);
+ }
+
+ ALOGD("inpcm open: card=%d, device=%d, rate=%d", PCM_CARD, device, adev->active_out->pcm_config->rate);
+
+ in->pcm = pcm_open(PCM_CARD, device, PCM_IN, in->pcm_config);
if (in->pcm && !pcm_is_ready(in->pcm)) {
ALOGE("pcm_open(in) failed: %s", pcm_get_error(in->pcm));
pcm_close(in->pcm);
return -ENOMEM;
}
- /* If needed, allocate the resample buffer */
- if (in->subsample_shift) {
- in->buffer = malloc(in->pcm_config.period_size * in->pcm_config.channels * sizeof(int16_t));
+ /*
+ * If the stream rate differs from the PCM rate, we need to
+ * create a resampler.
+ */
+ if (in_get_sample_rate(&in->stream.common) != in->pcm_config->rate) {
+ in->buf_provider.get_next_buffer = get_next_buffer;
+ in->buf_provider.release_buffer = release_buffer;
+
+ ret = create_resampler(in->pcm_config->rate,
+ in_get_sample_rate(&in->stream.common),
+ 1,
+ RESAMPLER_QUALITY_DEFAULT,
+ &in->buf_provider,
+ &in->resampler);
}
-
- /* Mute some samples at start of capture, to let line calm down */
- in->frames_to_mute = FRAMES_MUTED_AT_CAPTURE_START;
-
- adev->active_in = in;
+ in->buffer_size = pcm_frames_to_bytes(in->pcm,
+ in->pcm_config->period_size);
+ in->buffer = malloc(in->buffer_size);
+ in->frames_in = 0;
+
+ adev->active_in = in;
+
return 0;
}
-/* xface */
+static int get_next_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer)
+{
+ struct stream_in *in;
+
+ if (buffer_provider == NULL || buffer == NULL)
+ return -EINVAL;
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ 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,
+ in->buffer_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 = in->pcm_config->period_size;
+ if (in->pcm_config->channels == 2) {
+ unsigned int i;
+
+ /* Discard right channel */
+ for (i = 1; i < in->frames_in; i++)
+ in->buffer[i] = in->buffer[i * 2];
+ }
+ }
+
+ buffer->frame_count = (buffer->frame_count > in->frames_in) ?
+ in->frames_in : buffer->frame_count;
+ buffer->i16 = in->buffer + (in->pcm_config->period_size - in->frames_in);
+
+ return in->read_status;
+
+}
+
+static void release_buffer(struct resampler_buffer_provider *buffer_provider,
+ struct resampler_buffer* buffer)
+{
+ struct stream_in *in;
+
+ if (buffer_provider == NULL || buffer == NULL)
+ return;
+
+ in = (struct stream_in *)((char *)buffer_provider -
+ offsetof(struct stream_in, buf_provider));
+
+ 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;
+
+ 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 * audio_stream_frame_size(&in->stream.common)),
+ &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 * audio_stream_frame_size(&in->stream.common),
+ buf.raw,
+ buf.frame_count * audio_stream_frame_size(&in->stream.common));
+ 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 */
+
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
- ALOGD("out_get_sample_rate");
- return ((struct stream_out*)stream)->pcm_config.rate;
+ return pcm_config_out.rate;
}
-/* xface */
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
- struct stream_out *out = (struct stream_out *)stream;
- ALOGD("out_set_sample_rate: %d",rate);
-
- if (rate == out->pcm_config.rate)
- return 0;
return -ENOSYS;
}
-/* xface */
static size_t out_get_buffer_size(const struct audio_stream *stream)
{
- ALOGD("out_get_buffer_size");
- return ((struct stream_out*)stream)->pcm_config.period_size *
+ return pcm_config_out.period_size *
audio_stream_frame_size((struct audio_stream *)stream);
}
-/* xface */
static uint32_t out_get_channels(const struct audio_stream *stream)
{
- struct stream_out *out = (struct stream_out *)stream;
- //ALOGV("out_get_channels");
- return AUDIO_CHANNEL_OUT_STEREO;
+ return AUDIO_CHANNEL_OUT_STEREO;
}
-/* xface */
static audio_format_t out_get_format(const struct audio_stream *stream)
{
- //ALOGV("out_get_format");
return AUDIO_FORMAT_PCM_16_BIT;
}
-/* xface */
static int out_set_format(struct audio_stream *stream, audio_format_t format)
{
- ALOGD("out_set_format: %d", format);
- if (format == AUDIO_FORMAT_PCM_16_BIT)
- return 0;
return -ENOSYS;
}
-/* xface */
static int out_standby(struct audio_stream *stream)
{
struct stream_out *out = (struct stream_out *)stream;
@@ -643,15 +552,15 @@ static int out_standby(struct audio_stream *stream)
return 0;
}
-/* xface */
static int out_dump(const struct audio_stream *stream, int fd)
{
return 0;
}
-/* xface */
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
+ ALOGD("out_set_parameters:%s", kvpairs);
+
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
struct str_parms *parms;
@@ -666,41 +575,32 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
pthread_mutex_lock(&adev->lock);
if (ret >= 0) {
val = atoi(value);
- if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) {
+ if ((adev->out_device != val) && (val != 0)) {
/*
- * If SCO is turned on/off or HDMI is turned on/off,
- * we need to put audio into standby
- * because SCO/HDMI uses a different PCM.
+ * If SCO or HDMI is turned on/off, we need to put audio into standby
+ * because they use a different PCM PORT.
*/
- if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^
- (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO) ||
- (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) ^
- (adev->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
- ) {
-
+ if ( (val & AUDIO_DEVICE_OUT_ALL_SCO) ^ (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO) || (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) ^ (adev->out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
pthread_mutex_lock(&out->lock);
do_out_standby(out);
pthread_mutex_unlock(&out->lock);
- }
+ }
- adev->devices &= ~AUDIO_DEVICE_OUT_ALL;
- adev->devices |= val;
+ adev->out_device = val;
select_devices(adev);
}
- }
- pthread_mutex_unlock(&adev->lock);
+ }
+ pthread_mutex_unlock(&adev->lock);
str_parms_destroy(parms);
return ret;
}
-/* xface */
static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
{
return strdup("");
}
-/* xface */
static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
struct stream_out *out = (struct stream_out *)stream;
@@ -709,52 +609,34 @@ static uint32_t out_get_latency(const struct audio_stream_out *stream)
pthread_mutex_lock(&adev->lock);
- if (adev->screen_off && !adev->active_in && !(adev->devices & AUDIO_DEVICE_OUT_ALL_SCO))
+ if (adev->screen_off && !adev->active_in && !(adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO))
period_count = OUT_LONG_PERIOD_COUNT;
else
period_count = OUT_SHORT_PERIOD_COUNT;
pthread_mutex_unlock(&adev->lock);
- return (out->pcm_config.period_size * period_count * 1000) / out->pcm_config.rate;
+ return (pcm_config_out.period_size * period_count * 1000) / pcm_config_out.rate;
}
-/* xface */
static int out_set_volume(struct audio_stream_out *stream, float left,
float right)
{
- struct stream_out *out = (struct stream_out *)stream;
- struct audio_device *adev = out->dev;
-
- ALOGD("out_set_volume: left:%f, right:%f\n",left,right);
-
- mixer_ctl_set_value(adev->mixer_ctls.speaker_volume, 0,
- PERC_TO_SPEAKER_VOLUME(left));
- mixer_ctl_set_value(adev->mixer_ctls.speaker_volume, 1,
- PERC_TO_SPEAKER_VOLUME(right));
-
- mixer_ctl_set_value(adev->mixer_ctls.headset_volume, 0,
- PERC_TO_HEADSET_VOLUME(left));
- mixer_ctl_set_value(adev->mixer_ctls.headset_volume, 1,
- PERC_TO_HEADSET_VOLUME(right));
-
- return 0;
+ return -ENOSYS;
}
-/* xface */
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
size_t bytes)
{
int ret = 0;
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
-
size_t frame_size = audio_stream_frame_size(&out->stream.common);
int16_t *in_buffer = (int16_t *)buffer;
size_t in_frames = bytes / frame_size;
-
+ size_t out_frames;
int buffer_type;
- int kernel_frames = 0;
+ int kernel_frames;
bool sco_on;
/*
@@ -775,7 +657,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
}
buffer_type = (adev->screen_off && !adev->active_in) ?
OUT_BUFFER_TYPE_LONG : OUT_BUFFER_TYPE_SHORT;
- sco_on = (adev->devices & AUDIO_DEVICE_OUT_ALL_SCO);
+ sco_on = (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO);
pthread_mutex_unlock(&adev->lock);
/* detect changes in screen ON/OFF state and adapt buffer size
@@ -788,44 +670,69 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
else
period_count = OUT_SHORT_PERIOD_COUNT;
- out->write_threshold = out->pcm_config.period_size * period_count;
+ out->write_threshold = out->pcm_config->period_size * period_count;
/* reset current threshold if exiting standby */
if (out->buffer_type == OUT_BUFFER_TYPE_UNKNOWN)
out->cur_write_threshold = out->write_threshold;
out->buffer_type = buffer_type;
}
+ /* Reduce number of channels, if necessary */
+ if (popcount(out_get_channels(&stream->common)) >
+ (int)out->pcm_config->channels) {
+ unsigned int i;
+
+ /* Discard right channel */
+ for (i = 1; i < in_frames; i++)
+ in_buffer[i] = in_buffer[i * 2];
+
+ /* The frame size is now half */
+ frame_size /= 2;
+ }
+
+ /* Change sample rate, if necessary */
+ if (out_get_sample_rate(&stream->common) != out->pcm_config->rate) {
+ out_frames = out->buffer_frames;
+ out->resampler->resample_from_input(out->resampler,
+ in_buffer, &in_frames,
+ out->buffer, &out_frames);
+ in_buffer = out->buffer;
+ } else {
+ out_frames = in_frames;
+ }
+
if (!sco_on) {
int total_sleep_time_us = 0;
- size_t period_size = out->pcm_config.period_size;
+ size_t period_size = out->pcm_config->period_size;
/* do not allow more than out->cur_write_threshold frames in kernel
* pcm driver buffer */
- do {
- struct timespec time_stamp;
- if (pcm_get_htimestamp(out->pcm, (unsigned int *)&kernel_frames, &time_stamp) < 0)
- break;
-
- kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames;
-
- if (kernel_frames > out->cur_write_threshold) {
- int sleep_time_us =
- (int)(((int64_t)(kernel_frames - out->cur_write_threshold)
- * 1000000) / out->pcm_config.rate);
- if (sleep_time_us < MIN_WRITE_SLEEP_US)
- break;
- total_sleep_time_us += sleep_time_us;
- if (total_sleep_time_us > MAX_WRITE_SLEEP_US) {
- ALOGW("out_write() limiting sleep time %d to %d",
- total_sleep_time_us, MAX_WRITE_SLEEP_US);
- sleep_time_us = MAX_WRITE_SLEEP_US -
- (total_sleep_time_us - sleep_time_us);
- }
- usleep(sleep_time_us);
- }
-
- } while ((kernel_frames > out->cur_write_threshold) &&
- (total_sleep_time_us <= MAX_WRITE_SLEEP_US));
+ do {
+ struct timespec time_stamp;
+ if (pcm_get_htimestamp(out->pcm,
+ (unsigned int *)&kernel_frames,
+ &time_stamp) < 0)
+ break;
+ kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames;
+
+ if (kernel_frames > out->cur_write_threshold) {
+ int sleep_time_us =
+ (int)(((int64_t)(kernel_frames - out->cur_write_threshold)
+ * 1000000) / out->pcm_config->rate);
+ if (sleep_time_us < MIN_WRITE_SLEEP_US)
+ break;
+ total_sleep_time_us += sleep_time_us;
+ if (total_sleep_time_us > MAX_WRITE_SLEEP_US) {
+ ALOGW("out_write() limiting sleep time %d to %d",
+ total_sleep_time_us, MAX_WRITE_SLEEP_US);
+ sleep_time_us = MAX_WRITE_SLEEP_US -
+ (total_sleep_time_us - sleep_time_us);
+ }
+ usleep(sleep_time_us);
+ }
+
+ } while ((kernel_frames > out->cur_write_threshold) &&
+ (total_sleep_time_us <= MAX_WRITE_SLEEP_US));
/* do not allow abrupt changes on buffer size. Increasing/decreasing
* the threshold by steps of 1/4th of the buffer size keeps the write
@@ -850,9 +757,9 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
out->cur_write_threshold = (kernel_frames / period_size + 1) * period_size;
out->cur_write_threshold += period_size / 4;
}
- }
+ }
- ret = pcm_write(out->pcm, in_buffer, in_frames * frame_size);
+ ret = pcm_write(out->pcm, in_buffer, out_frames * frame_size);
if (ret == -EPIPE) {
/* In case of underrun, don't sleep since we want to catch up asap */
pthread_mutex_unlock(&out->lock);
@@ -870,100 +777,76 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
return bytes;
}
-/* xface */
static int out_get_render_position(const struct audio_stream_out *stream,
uint32_t *dsp_frames)
{
- ALOGD("out_get_render_position");
return -EINVAL;
}
-/* xface */
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
- ALOGD("out_add_audio_effect");
return 0;
}
-/* xface */
static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
{
- ALOGD("out_remove_audio_effect");
return 0;
}
-/* xface */
static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
int64_t *timestamp)
{
- //ALOGV("out_get_next_write_timestamp");
return -EINVAL;
}
/** audio_stream_in implementation **/
-
-/* xface */
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
struct stream_in *in = (struct stream_in *)stream;
- //ALOGD("in_get_sample_rate");
- return in->pcm_config.rate >> in->subsample_shift;
+
+ return in->requested_rate;
}
-/* xface */
static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
- struct stream_in *in = (struct stream_in *)stream;
- ALOGD("in_set_sample_rate: %d",rate);
-
- if (rate == (in->pcm_config.rate >> in->subsample_shift))
return 0;
- return -ENOSYS;
}
-/* xface */
static size_t in_get_buffer_size(const struct audio_stream *stream)
{
struct stream_in *in = (struct stream_in *)stream;
size_t size;
- //ALOGD("in_get_buffer_size");
-
- size = in->pcm_config.period_size;
+ /*
+ * 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 = (in->pcm_config->period_size * in_get_sample_rate(stream)) /
+ in->pcm_config->rate;
size = ((size + 15) / 16) * 16;
return size * audio_stream_frame_size((struct audio_stream *)stream);
}
-/* xface */
static uint32_t in_get_channels(const struct audio_stream *stream)
{
- struct stream_in *in = (struct stream_in *)stream;
- return AUDIO_CHANNEL_IN_STEREO;
+ return AUDIO_CHANNEL_IN_MONO;
}
-/* xface */
static audio_format_t in_get_format(const struct audio_stream *stream)
{
- //ALOGD("in_get_format");
return AUDIO_FORMAT_PCM_16_BIT;
}
-/* xface */
static int in_set_format(struct audio_stream *stream, audio_format_t format)
{
- ALOGD("in_set_format: %d",format);
- if (format == AUDIO_FORMAT_PCM_16_BIT)
- return 0;
return -ENOSYS;
}
-
-/* xface */
static int in_standby(struct audio_stream *stream)
{
struct stream_in *in = (struct stream_in *)stream;
- ALOGD("in_standby");
pthread_mutex_lock(&in->dev->lock);
pthread_mutex_lock(&in->lock);
@@ -974,48 +857,42 @@ static int in_standby(struct audio_stream *stream)
return 0;
}
-/* xface */
static int in_dump(const struct audio_stream *stream, int fd)
{
- ALOGD("in_dump");
return 0;
}
-/* xface */
static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
+ ALOGD("in_set_parameters:%s", kvpairs);
+
struct stream_in *in = (struct stream_in *)stream;
struct audio_device *adev = in->dev;
struct str_parms *parms;
char value[32];
int ret;
unsigned int val;
- ALOGD("in_set_parameters");
parms = str_parms_create_str(kvpairs);
ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
value, sizeof(value));
pthread_mutex_lock(&adev->lock);
if (ret >= 0) {
- val = atoi(value);
- if (((adev->devices & AUDIO_DEVICE_IN_ALL) != val) && (val != 0)) {
+ val = atoi(value) & ~AUDIO_DEVICE_BIT_IN;
+ if ((adev->in_device != val) && (val != 0)) {
/*
- * If SCO is turned on/off or HDMI is turned on/off, we need to put audio into standby
+ * If SCO is turned on/off, we need to put audio into standby
* because SCO uses a different PCM.
*/
- if (((val & AUDIO_DEVICE_IN_ALL_SCO) ^
- (adev->devices & AUDIO_DEVICE_IN_ALL_SCO)) ||
- ((val & AUDIO_DEVICE_IN_AUX_DIGITAL) ^
- (adev->devices & AUDIO_DEVICE_IN_AUX_DIGITAL))
- ) {
+ if ((val & AUDIO_DEVICE_IN_ALL_SCO) ^
+ (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO)) {
pthread_mutex_lock(&in->lock);
do_in_standby(in);
pthread_mutex_unlock(&in->lock);
- }
+ }
- adev->devices &= ~AUDIO_DEVICE_IN_ALL;
- adev->devices |= val;
+ adev->in_device = val;
select_devices(adev);
}
}
@@ -1025,47 +902,24 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
return ret;
}
-/* xface */
static char * in_get_parameters(const struct audio_stream *stream,
const char *keys)
{
- ALOGD("in_get_parameters");
return strdup("");
}
-/* xface */
static int in_set_gain(struct audio_stream_in *stream, float gain)
{
- struct stream_in *in = (struct stream_in *)stream;
- struct audio_device *adev = in->dev;
-
- unsigned int channel;
- ALOGD("in_set_gain: %f",gain);
-
- for (channel = 0; channel < 2; channel++) {
- mixer_ctl_set_value(adev->mixer_ctls.mic_volume, channel,
- PERC_TO_CAPTURE_VOLUME(gain));
- mixer_ctl_set_value(adev->mixer_ctls.mic_volume, channel,
- PERC_TO_CAPTURE_VOLUME(gain));
- }
-
return 0;
}
-/* xface */
static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
size_t bytes)
{
int ret = 0;
struct stream_in *in = (struct stream_in *)stream;
struct audio_device *adev = in->dev;
-
- int frame_size = audio_stream_frame_size(&stream->common);
- size_t in_frames = bytes / frame_size;
-
- int16_t* in_buffer = (int16_t*)buffer;
-
- //ALOGD("in_read: bytes:%d",bytes);
+ size_t frames_rq = bytes / audio_stream_frame_size(&stream->common);
/*
* acquiring hw device mutex systematically is useful if a low
@@ -1082,132 +936,67 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
}
pthread_mutex_unlock(&adev->lock);
- if (ret < 0) {
- ALOGE("in_read: Failed to start input stream");
+ if (ret < 0)
goto exit;
- }
- /* If subsampling, we need to read more samples using the temporary buffer */
- if (in->subsample_shift) {
- int16_t* outp = in_buffer;
-
- /* We must read in chunks ... */
- unsigned int chunkstodo = 1 << in->subsample_shift;
- do {
- unsigned int frames_to_do = in_frames >> in->subsample_shift;
-
- /* Read requested samples, so we can subsample */
- //ALOGD("in_read: subsampling: pcm_read: buf:%p, bufsz:%d",in->buffer, in_frames * frame_size);
- ret = pcm_read(in->pcm, in->buffer, in_frames * frame_size);
-
- /* We always capture 16bit stereo... Downsample... */
- if (ret < 0)
- break;
- ret = 0;
-
- /* If no frames to subsample, skip subsampling */
- if (frames_to_do <= 0)
- break;
-
- //ALOGD("subsampling...");
- /*
- * Instead of writing zeroes here, we could trust the hardware
- * to always provide zeroes when muted.
- */
- if (ret == 0 &&
- (adev->mic_mute || in->frames_to_mute)
- ) {
- memset(outp, 0, frames_to_do * 4);
- outp += 2 * frames_to_do;
- } else {
- if (in->subsample_shift == 1) {
- int16_t* inp = in->buffer;
- do {
- int c1 = *inp++;
- int c2 = *inp++;
- c1 += *inp++;
- c2 += *inp++;
- *outp++ = c1 >> 1;
- *outp++ = c2 >> 1;
- } while (--frames_to_do);
- } else {
- /* subsample shift == 2 */
- int16_t* inp = in->buffer;
- do {
- int c1 = *inp++;
- int c2 = *inp++;
- c1 += *inp++;
- c2 += *inp++;
- c1 += *inp++;
- c2 += *inp++;
- c1 += *inp++;
- c2 += *inp++;
- *outp++ = c1 >> 2;
- *outp++ = c2 >> 2;
- } while (--frames_to_do);
- }
- }
- //ALOGD("done...");
-
- } while (--chunkstodo);
- } else {
+ /*if (in->num_preprocessors != 0) {
+ ret = process_frames(in, buffer, frames_rq);
+ } else */if (in->resampler != NULL) {
+ ret = read_frames(in, buffer, frames_rq);
+ } else if (in->pcm_config->channels == 2) {
+ /*
+ * If the PCM is stereo, capture twice as many frames and
+ * discard the right channel.
+ */
+ unsigned int i;
+ int16_t *in_buffer = (int16_t *)buffer;
- ret = pcm_read(in->pcm, in_buffer, in_frames * frame_size);
- if (ret > 0)
- ret = 0;
-
- /*
- * Instead of writing zeroes here, we could trust the hardware
- * to always provide zeroes when muted.
- */
- if (ret == 0 && (adev->mic_mute || in->frames_to_mute))
- memset(buffer, 0, bytes);
+ ret = pcm_read(in->pcm, in->buffer, bytes * 2);
+
+ /* Discard right channel */
+ for (i = 0; i < frames_rq; i++)
+ in_buffer[i] = in->buffer[i * 2];
+ } else {
+ ret = pcm_read(in->pcm, buffer, bytes);
}
- /* Decrement the number of samples to be muted if any */
- if (in->frames_to_mute) {
- if (in->frames_to_mute < in_frames) {
- in->frames_to_mute = 0;
- } else {
- in->frames_to_mute -= in_frames;
- }
- }
-
+ if (ret > 0)
+ ret = 0;
+
+ /*
+ * 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 / frame_size /
- in->pcm_config.rate);
+ usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) /
+ in_get_sample_rate(&stream->common));
pthread_mutex_unlock(&in->lock);
return bytes;
}
-/* xface */
static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
{
- ALOGD("in_get_input_frames_lost");
return 0;
}
-/* xface */
static int in_add_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
- struct stream_in *in = (struct stream_in *)stream;
- ALOGD("in_add_audio_effect");
- return 0;
+ return 0;
}
-/* xface */
static int in_remove_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
- struct stream_in *in = (struct stream_in *)stream;
- ALOGD("in_remove_audio_effect");
- return 0;
+ return 0;
}
-/* xface */
+
static int adev_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
@@ -1219,8 +1008,6 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
struct stream_out *out;
int ret;
- ALOGD("adev_open_output_stream");
-
out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
if (!out)
return -ENOMEM;
@@ -1244,14 +1031,12 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
out->dev = adev;
- out->standby = true;
- /* Suggest the playback format to the framework, otherwise it crashes */
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
- config->sample_rate = OUT_SAMPLING_RATE;
+ 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);
- memcpy(&out->pcm_config,&pcm_config_out,sizeof(out->pcm_config)); /* default PCM config */
+ out->standby = true;
*stream_out = &out->stream;
return 0;
@@ -1262,29 +1047,53 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
return ret;
}
-/* xface */
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
- ALOGD("adev_close_output_stream");
out_standby(&stream->common);
free(stream);
}
-/* xface */
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
+ ALOGD("adev_set_parameters:%s", kvpairs);
+
struct audio_device *adev = (struct audio_device *)dev;
struct str_parms *parms;
- char *str;
+ char *str;
char value[32];
int ret;
-
- ALOGD("adev_set_parameters: kppairs: %s", kvpairs);
+
parms = str_parms_create_str(kvpairs);
+ ret = str_parms_get_str(parms, "orientation", value, sizeof(value));
+ if (ret >= 0) {
+ int orientation;
+
+ if (strcmp(value, "landscape") == 0)
+ orientation = ORIENTATION_LANDSCAPE;
+ else if (strcmp(value, "portrait") == 0)
+ orientation = ORIENTATION_PORTRAIT;
+ else if (strcmp(value, "square") == 0)
+ orientation = ORIENTATION_SQUARE;
+ else
+ orientation = ORIENTATION_UNDEFINED;
+
+ pthread_mutex_lock(&adev->lock);
+ if (orientation != adev->orientation) {
+ adev->orientation = orientation;
+ /*
+ * Orientation changes can occur with the input device
+ * closed so we must call select_devices() here to set
+ * up the mixer. This is because select_devices() will
+ * not be called when the input device is opened if no
+ * other input parameter is changed.
+ */
+ select_devices(adev);
+ }
+ pthread_mutex_unlock(&adev->lock);
+ }
- /* Get the screen state as system power indicator */
- ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
+ ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
if (ret >= 0) {
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
adev->screen_off = false;
@@ -1296,85 +1105,41 @@ static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
return ret;
}
-/* xface */
static char * adev_get_parameters(const struct audio_hw_device *dev,
const char *keys)
{
return strdup("");
}
-/* xface */
static int adev_init_check(const struct audio_hw_device *dev)
{
return 0;
}
-/* xface */
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
{
- ALOGD("adev_set_voice_volume: volume: %f", volume);
-
return -ENOSYS;
}
-/* xface */
static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
{
- struct audio_device *adev = (struct audio_device *)dev;
-
- ALOGD("adev_set_master_volume: volume: %f", volume);
-
- mixer_ctl_set_value(adev->mixer_ctls.pcm_volume, 0,
- PERC_TO_PCM_VOLUME(volume));
- mixer_ctl_set_value(adev->mixer_ctls.pcm_volume, 1,
- PERC_TO_PCM_VOLUME(volume));
-
- return 0;
+ return -ENOSYS;
}
-/* xface */
-static int adev_set_mode(struct audio_hw_device *dev, int mode)
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
{
- ALOGD("adev_set_mode: mode: %d", mode);
return 0;
}
-/* xface */
static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
{
struct audio_device *adev = (struct audio_device *)dev;
- ALOGD("adev_set_mic_mute: state: %d", state);
-
- /* Get device lock */
- pthread_mutex_lock(&adev->lock);
-
- /* If already capturing ... */
- if (adev->active_in) {
- pthread_mutex_lock(&adev->active_in->lock);
- /* If unmuting, leave some time with forced silence, so the line stabilizes */
- if (!state && adev->mic_mute) {
-
- /* Mute some samples at start of capture, to let line calm down */
- adev->active_in->frames_to_mute = FRAMES_MUTED_AT_CAPTURE_START;
- }
- pthread_mutex_unlock(&adev->active_in->lock);
- }
-
- /* Store setting */
adev->mic_mute = state;
- /* Release device lock */
- pthread_mutex_unlock(&adev->lock);
-
- /* Disable mic if requested */
- mixer_ctl_set_value(adev->mixer_ctls.mic_switch_left, 0, state ? 0 : 1);
- mixer_ctl_set_value(adev->mixer_ctls.mic_switch_right, 0, state ? 0 : 1);
-
return 0;
}
-/* xface */
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
{
struct audio_device *adev = (struct audio_device *)dev;
@@ -1384,21 +1149,23 @@ static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
return 0;
}
-/* xface */
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
const struct audio_config *config)
{
size_t size;
- ALOGD("adev_get_input_buffer_size: sample_rate: %d, format: %d, channel_count:%d", config->sample_rate, config->format, popcount(config->channel_mask));
- /* Note: We ignore the requested format... We will force our format */
- size = pcm_config_in.period_size;
+ /*
+ * 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 * config->sample_rate) / pcm_config_in.rate;
size = ((size + 15) / 16) * 16;
- return size * pcm_config_in.channels * sizeof(int16_t);
+ return (size * popcount(config->channel_mask) *
+ audio_bytes_per_sample(config->format));
}
-/* xface */
static int adev_open_input_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
@@ -1409,39 +1176,13 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
struct stream_in *in;
int ret;
- ALOGD("adev_open_input_stream: channel_count:%d", popcount(config->channel_mask));
-
- /* Check if we support the requested format ... Also accept decimation of sampling frequencies in powers of 2 - We will downsample in sw*/
- if (config->format != AUDIO_FORMAT_PCM_16_BIT ||
- config->channel_mask != AUDIO_CHANNEL_IN_STEREO ||
- ( config->sample_rate != IN_SAMPLING_RATE &&
- config->sample_rate != (IN_SAMPLING_RATE>>1) &&
- config->sample_rate != (IN_SAMPLING_RATE>>2) )
- ) {
- ALOGD("adev_open_input_stream: Unsupported format. Let AudioFlinger do the conversion by returning the acceptable format");
-
- /* Suggest the record format to the framework, otherwise it crashes */
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
-
- /* But there is a catch here... AudioFlinger can't downsample to less than half
- the sampling rate... So we have to handle it somehow - We habe implemented a
- decimation by power of 2 filter ... Select the proper reported sampling frequency */
- if (config->sample_rate >= (IN_SAMPLING_RATE>>1) ) {
- config->sample_rate = IN_SAMPLING_RATE;
- } else if (config->sample_rate >= (IN_SAMPLING_RATE>>2) ) {
- config->sample_rate = (IN_SAMPLING_RATE>>1);
- } else {
- config->sample_rate = (IN_SAMPLING_RATE>>2);
- }
+ *stream_in = NULL;
- /* Let audioflinger adopt our suggested rate */
- return -EINVAL;
- }
-
- ALOGD("adev_open_input_stream: format accepted");
-
- *stream_in = NULL;
+ /* Respond with a request for mono if a different format is given. */
+ if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) {
+ config->channel_mask = AUDIO_CHANNEL_IN_MONO;
+ return -EINVAL;
+ }
in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
if (!in)
@@ -1465,49 +1206,33 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
in->dev = adev;
in->standby = true;
-
- memcpy(&in->pcm_config,&pcm_config_in,sizeof(in->pcm_config)); /* default PCM config */
-
- /* Calculate the audio subsampling factor */
- if (config->sample_rate == IN_SAMPLING_RATE) {
- in->subsample_shift = 0;
- } else if (config->sample_rate == (IN_SAMPLING_RATE>>1)) {
- in->subsample_shift = 1;
- } else {
- in->subsample_shift = 2;
- }
- ALOGD("adev_open_input_stream: Using subsampling shift: %d",in->subsample_shift);
+ in->requested_rate = config->sample_rate;
+ in->pcm_config = &pcm_config_in; /* default PCM config */
*stream_in = &in->stream;
return 0;
}
-/* xface */
static void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
struct stream_in *in = (struct stream_in *)stream;
- ALOGD("adev_close_input_stream");
-
in_standby(&stream->common);
free(stream);
}
-/* xface */
static int adev_dump(const audio_hw_device_t *device, int fd)
{
return 0;
}
-/* xface */
static int adev_close(hw_device_t *device)
{
struct audio_device *adev = (struct audio_device *)device;
- ALOGD("adev_close");
+ audio_route_free(adev->ar);
- mixer_close(adev->mixer);
free(device);
return 0;
}
@@ -1518,8 +1243,6 @@ static int adev_open(const hw_module_t* module, const char* name,
struct audio_device *adev;
int ret;
- ALOGE("adev_open: name:'%s'",name);
-
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;
@@ -1547,98 +1270,14 @@ static int adev_open(const hw_module_t* module, const char* name,
adev->hw_device.close_input_stream = adev_close_input_stream;
adev->hw_device.dump = adev_dump;
- adev->mixer = mixer_open(0);
- if (!adev->mixer) {
- free(adev);
- ALOGE("Unable to open the mixer, aborting.");
- return -EINVAL;
- }
-
- adev->mixer_ctls.mic_volume = mixer_get_ctl_by_name(adev->mixer,
- MIXER_MIC_CAPTURE_VOLUME);
- if (!adev->mixer_ctls.mic_volume) {
- ALOGE("Unable to find '%s' mixer control",MIXER_MIC_CAPTURE_VOLUME);
- goto error_out;
- }
-
- adev->mixer_ctls.pcm_volume = mixer_get_ctl_by_name(adev->mixer,
- MIXER_PCM_PLAYBACK_VOLUME);
- if (!adev->mixer_ctls.pcm_volume) {
- ALOGE("Unable to find '%s' mixer control",MIXER_PCM_PLAYBACK_VOLUME);
- goto error_out;
- }
-
- adev->mixer_ctls.pcm_cap_volume = mixer_get_ctl_by_name(adev->mixer, MIXER_PCM_CAPTURE_VOLUME);
-
- if (!adev->mixer_ctls.pcm_cap_volume) {
- ALOGE("Unable to find '%s' mixer control", MIXER_PCM_CAPTURE_VOLUME);
- goto error_out;
- }
-
- adev->mixer_ctls.headset_volume = mixer_get_ctl_by_name(adev->mixer,
- MIXER_HEADSET_PLAYBACK_VOLUME);
- if (!adev->mixer_ctls.headset_volume) {
- ALOGE("Unable to find '%s' mixer control",MIXER_HEADSET_PLAYBACK_VOLUME);
- goto error_out;
- }
-
- adev->mixer_ctls.speaker_volume = mixer_get_ctl_by_name(adev->mixer,
- MIXER_SPEAKER_PLAYBACK_VOLUME);
- if (!adev->mixer_ctls.speaker_volume) {
- ALOGE("Unable to find '%s' mixer control",MIXER_SPEAKER_PLAYBACK_VOLUME);
- goto error_out;
- }
-
- adev->mixer_ctls.mic_switch_left = mixer_get_ctl_by_name(adev->mixer,
- MIXER_MIC_LEFT_CAPTURE_SWITCH);
- if (!adev->mixer_ctls.mic_switch_left) {
- ALOGE("Unable to find '%s' mixer control",MIXER_MIC_LEFT_CAPTURE_SWITCH);
- goto error_out;
- }
-
- adev->mixer_ctls.mic_switch_right = mixer_get_ctl_by_name(adev->mixer,
- MIXER_MIC_RIGHT_CAPTURE_SWITCH);
- if (!adev->mixer_ctls.mic_switch_right) {
- ALOGE("Unable to find '%s' mixer control",MIXER_MIC_RIGHT_CAPTURE_SWITCH);
- goto error_out;
- }
-
-
- /* Set the default route before the PCM stream is opened */
- pthread_mutex_lock(&adev->lock);
- set_route_by_array(adev->mixer, defaults, 1);
- adev->devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_IN_BUILTIN_MIC;
- pthread_mutex_unlock(&adev->lock);
+ adev->ar = audio_route_init();
+ adev->orientation = ORIENTATION_UNDEFINED;
+ adev->out_device = AUDIO_DEVICE_OUT_SPEAKER;
+ adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN;
*device = &adev->hw_device.common;
return 0;
-
-error_out:
-
-#if !LOG_NDEBUG
- /* To aid debugging, dump all mixer controls */
- {
- unsigned int cnt = mixer_get_num_ctls(adev->mixer);
- unsigned int i;
- ALOGD("Mixer dump: Nr of controls: %d",cnt);
- for (i = 0; i < cnt; i++) {
- struct mixer_ctl* x = mixer_get_ctl(adev->mixer,i);
- if (x != NULL) {
- const char* name;
- const char* type;
- name = mixer_ctl_get_name(x);
- type = mixer_ctl_get_type_string(x);
- ALOGD("#%d: '%s' [%s]",i,name,type);
- }
- }
- }
-#endif
-
- mixer_close(adev->mixer);
- free(adev);
- return -EINVAL;
-
}
static struct hw_module_methods_t hal_module_methods = {
@@ -1656,4 +1295,3 @@ struct audio_module HAL_MODULE_INFO_SYM = {
.methods = &hal_module_methods,
},
};
-
View
494 audio/audio_route.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_primary"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <expat.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define BUF_SIZE 1024
+#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
+#define INITIAL_MIXER_PATH_SIZE 8
+
+#define MIXER_CARD 1
+
+struct mixer_state {
+ struct mixer_ctl *ctl;
+ int old_value;
+ int new_value;
+ int reset_value;
+};
+
+struct mixer_setting {
+ struct mixer_ctl *ctl;
+ int value;
+};
+
+struct mixer_path {
+ char *name;
+ unsigned int size;
+ unsigned int length;
+ struct mixer_setting *setting;
+};
+
+struct audio_route {
+ struct mixer *mixer;
+ unsigned int num_mixer_ctls;
+ struct mixer_state *mixer_state;
+
+ unsigned int mixer_path_size;
+ unsigned int num_mixer_paths;
+ struct mixer_path *mixer_path;
+};
+
+struct config_parse_state {
+ struct audio_route *ar;
+ struct mixer_path *path;
+ int level;
+};
+
+/* path functions */
+
+static void path_free(struct audio_route *ar)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_paths; i++) {
+ if (ar->mixer_path[i].name)
+ free(ar->mixer_path[i].name);
+ if (ar->mixer_path[i].setting)
+ free(ar->mixer_path[i].setting);
+ }
+ free(ar->mixer_path);
+}
+
+static struct mixer_path *path_get_by_name(struct audio_route *ar,
+ const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_paths; i++)
+ if (strcmp(ar->mixer_path[i].name, name) == 0)
+ return &ar->mixer_path[i];
+
+ return NULL;
+}
+
+static struct mixer_path *path_create(struct audio_route *ar, const char *name)
+{
+ struct mixer_path *new_mixer_path = NULL;
+
+ if (path_get_by_name(ar, name)) {
+ ALOGE("Path name '%s' already exists", name);
+ return NULL;
+ }
+
+ /* check if we need to allocate more space for mixer paths */
+ if (ar->mixer_path_size <= ar->num_mixer_paths) {
+ if (ar->mixer_path_size == 0)
+ ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
+ else
+ ar->mixer_path_size *= 2;
+
+ new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
+ sizeof(struct mixer_path));
+ if (new_mixer_path == NULL) {
+ ALOGE("Unable to allocate more paths");
+ return NULL;
+ } else {
+ ar->mixer_path = new_mixer_path;
+ }
+ }
+
+ /* initialise the new mixer path */
+ ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
+ ar->mixer_path[ar->num_mixer_paths].size = 0;
+ ar->mixer_path[ar->num_mixer_paths].length = 0;
+ ar->mixer_path[ar->num_mixer_paths].setting = NULL;
+
+ /* return the mixer path just added, then increment number of them */
+ return &ar->mixer_path[ar->num_mixer_paths++];
+}
+
+static bool path_setting_exists(struct mixer_path *path,
+ struct mixer_setting *setting)
+{
+ unsigned int i;
+
+ for (i = 0; i < path->length; i++)
+ if (path->setting[i].ctl == setting->ctl)
+ return true;
+
+ return false;
+}
+
+static int path_add_setting(struct mixer_path *path,
+ struct mixer_setting *setting)
+{
+ struct mixer_setting *new_path_setting;
+
+ if (path_setting_exists(path, setting)) {
+ ALOGE("Duplicate path setting '%s'",
+ mixer_ctl_get_name(setting->ctl));
+ return -1;
+ }
+
+ /* check if we need to allocate more space for path settings */
+ if (path->size <= path->length) {
+ if (path->size == 0)
+ path->size = INITIAL_MIXER_PATH_SIZE;
+ else
+ path->size *= 2;
+
+ new_path_setting = realloc(path->setting,
+ path->size * sizeof(struct mixer_setting));
+ if (new_path_setting == NULL) {
+ ALOGE("Unable to allocate more path settings");
+ return -1;
+ } else {
+ path->setting = new_path_setting;
+ }
+ }
+
+ /* initialise the new path setting */
+ path->setting[path->length].ctl = setting->ctl;
+ path->setting[path->length].value = setting->value;
+ path->length++;
+
+ return 0;
+}
+
+static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path)
+{
+ unsigned int i;
+
+ for (i = 0; i < sub_path->length; i++)
+ if (path_add_setting(path, &sub_path->setting[i]) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void path_print(struct mixer_path *path)
+{
+ unsigned int i;
+
+ ALOGV("Path: %s, length: %d", path->name, path->length);
+ for (i = 0; i < path->length; i++)
+ ALOGV(" %d: %s -> %d", i, mixer_ctl_get_name(path->setting[i].ctl),
+ path->setting[i].value);
+}
+
+static int path_apply(struct audio_route *ar, struct mixer_path *path)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < path->length; i++) {
+ struct mixer_ctl *ctl = path->setting[i].ctl;
+
+ /* locate the mixer ctl in the list */
+ for (j = 0; j < ar->num_mixer_ctls; j++) {
+ if (ar->mixer_state[j].ctl == ctl)
+ break;
+ }
+
+ /* apply the new value */
+ ar->mixer_state[j].new_value = path->setting[i].value;
+ }
+
+ return 0;
+}
+
+/* mixer helper function */
+static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
+{
+ unsigned int i;
+
+ /* Search the enum strings for a particular one */
+ for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
+ if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
+ break;
+ }
+
+ return i;
+}
+
+static void start_tag(void *data, const XML_Char *tag_name,
+ const XML_Char **attr)
+{
+ const XML_Char *attr_name = NULL;
+ const XML_Char *attr_value = NULL;
+ struct config_parse_state *state = data;
+ struct audio_route *ar = state->ar;
+ unsigned int i;
+ struct mixer_ctl *ctl;
+ int value;
+ struct mixer_setting mixer_setting;
+
+ /* Get name, type and value attributes (these may be empty) */
+ for (i = 0; attr[i]; i += 2) {
+ if (strcmp(attr[i], "name") == 0)
+ attr_name = attr[i + 1];
+ else if (strcmp(attr[i], "value") == 0)
+ attr_value = attr[i + 1];
+ }
+
+ /* Look at tags */
+ if (strcmp(tag_name, "path") == 0) {
+ if (attr_name == NULL) {
+ ALOGE("Unnamed path!");
+ } else {
+ if (state->level == 1) {
+ /* top level path: create and stash the path */
+ state->path = path_create(ar, (char *)attr_name);
+ } else {
+ /* nested path */
+ struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
+ path_add_path(state->path, sub_path);
+ }
+ }
+ }
+
+ else if (strcmp(tag_name, "ctl") == 0) {
+ /* Obtain the mixer ctl and value */
+ ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
+ switch (mixer_ctl_get_type(ctl)) {
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_INT:
+ value = atoi((char *)attr_value);
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ value = mixer_enum_string_to_value(ctl, (char *)attr_value);
+ break;
+ default:
+ value = 0;
+ break;
+ }
+
+ if (state->level == 1) {
+ /* top level ctl (initial setting) */
+
+ /* locate the mixer ctl in the list */
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ if (ar->mixer_state[i].ctl == ctl)
+ break;
+ }
+
+ /* apply the new value */
+ ar->mixer_state[i].new_value = value;
+ } else {
+ /* nested ctl (within a path) */
+ mixer_setting.ctl = ctl;
+ mixer_setting.value = value;
+ path_add_setting(state->path, &mixer_setting);
+ }
+ }
+
+ state->level++;
+}
+
+static void end_tag(void *data, const XML_Char *tag_name)
+{
+ struct config_parse_state *state = data;
+
+ state->level--;
+}
+
+static int alloc_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
+ ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
+ if (!ar->mixer_state)
+ return -1;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ ar->mixer_state[i].ctl = mixer_get_ctl(ar->mixer, i);
+ /* only get value 0, assume multiple ctl values are the same */
+ ar->mixer_state[i].old_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0);
+ ar->mixer_state[i].new_value = ar->mixer_state[i].old_value;
+ }
+
+ return 0;
+}
+
+static void free_mixer_state(struct audio_route *ar)
+{
+ free(ar->mixer_state);
+ ar->mixer_state = NULL;
+}
+
+void update_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ /* if the value has changed, update the mixer */
+ if (ar->mixer_state[i].old_value != ar->mixer_state[i].new_value) {
+ /* set all ctl values the same */
+ for (j = 0; j < mixer_ctl_get_num_values(ar->mixer_state[i].ctl); j++)
+ mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
+ ar->mixer_state[i].new_value);
+ ar->mixer_state[i].old_value = ar->mixer_state[i].new_value;
+ }
+ }
+}
+
+/* saves the current state of the mixer, for resetting all controls */
+static void save_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ for (i = 0; i < ar->num_mixer_ctls; i++) {
+ /* only get value 0, assume multiple ctl values are the same */
+ ar->mixer_state[i].reset_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0);
+ }
+}
+
+/* this resets all mixer settings to the saved values */
+void reset_mixer_state(struct audio_route *ar)
+{
+ unsigned int i;
+
+ /* load all of the saved values */
+ for (i = 0; i < ar->num_mixer_ctls; i++)
+ ar->mixer_state[i].new_value = ar->mixer_state[i].reset_value;
+}
+
+void audio_route_apply_path(struct audio_route *ar, const char *name)