hardware/libaudio
修訂 | cfde6b1213e204bb8eca55ce16b1a26d4d0eef0b (tree) |
---|---|
時間 | 2013-01-16 10:51:18 |
作者 | Chih-Wei Huang <cwhuang@linu...> |
Commiter | Chih-Wei Huang |
audio hal for jb-x86
Import from device/asus/grouper/audio of AOSP.
@@ -1,4 +1,4 @@ | ||
1 | -# Copyright (C) 2011 The Android-x86 Open Source Project | |
1 | +# Copyright (C) 2011-2013 The Android-x86 Open Source Project | |
2 | 2 | # |
3 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
4 | 4 | # you may not use this file except in compliance with the License. |
@@ -11,3 +11,28 @@ | ||
11 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
14 | + | |
15 | +LOCAL_PATH := $(call my-dir) | |
16 | + | |
17 | +include $(CLEAR_VARS) | |
18 | + | |
19 | +LOCAL_MODULE := audio.primary.x86 | |
20 | +LOCAL_MODULE_TAGS := optional | |
21 | +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw | |
22 | +LOCAL_SHARED_LIBRARIES := \ | |
23 | + liblog \ | |
24 | + libcutils \ | |
25 | + libtinyalsa \ | |
26 | + libaudioutils \ | |
27 | + libexpat \ | |
28 | + | |
29 | +LOCAL_SRC_FILES := \ | |
30 | + audio_hw.c \ | |
31 | + audio_route.c | |
32 | + | |
33 | +LOCAL_C_INCLUDES := \ | |
34 | + external/expat/lib \ | |
35 | + external/tinyalsa/include \ | |
36 | + $(call include-path-for, audio-utils) | |
37 | + | |
38 | +include $(BUILD_SHARED_LIBRARY) |
@@ -0,0 +1,54 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2012 The Android-x86 Open Source Project | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | + | |
11 | +TARGET_ALSA_CONF_DIR := system/usr/share/alsa | |
12 | +LOCAL_ALSA_CONF_DIR := external/alsa-lib/src/conf | |
13 | + | |
14 | +copy_conf := \ | |
15 | + alsa.conf \ | |
16 | + pcm/dsnoop.conf \ | |
17 | + pcm/modem.conf \ | |
18 | + pcm/dpl.conf \ | |
19 | + pcm/default.conf \ | |
20 | + pcm/surround51.conf \ | |
21 | + pcm/surround41.conf \ | |
22 | + pcm/surround50.conf \ | |
23 | + pcm/dmix.conf \ | |
24 | + pcm/center_lfe.conf \ | |
25 | + pcm/surround40.conf \ | |
26 | + pcm/side.conf \ | |
27 | + pcm/iec958.conf \ | |
28 | + pcm/rear.conf \ | |
29 | + pcm/surround71.conf \ | |
30 | + pcm/front.conf \ | |
31 | + cards/aliases.conf | |
32 | + | |
33 | +LOCAL_ALSA_INIT_DIR := external/alsa-utils/alsactl/init | |
34 | + | |
35 | +copy_init := \ | |
36 | + 00main \ | |
37 | + default \ | |
38 | + hda \ | |
39 | + help \ | |
40 | + info \ | |
41 | + test | |
42 | + | |
43 | +PRODUCT_COPY_FILES := \ | |
44 | + $(foreach f,$(copy_conf),$(LOCAL_ALSA_CONF_DIR)/$(f):$(TARGET_ALSA_CONF_DIR)/$(f)) \ | |
45 | + $(foreach f,$(copy_init),$(LOCAL_ALSA_INIT_DIR)/$(f):$(TARGET_ALSA_CONF_DIR)/init/$(f)) \ | |
46 | + $(if $(wildcard $(PRODUCT_DIR)audio_policy.conf),$(PRODUCT_DIR),$(LOCAL_PATH)/)audio_policy.conf:system/etc/audio_policy.conf \ | |
47 | + | |
48 | +PRODUCT_PACKAGES := \ | |
49 | + alsa_amixer \ | |
50 | + alsa_aplay \ | |
51 | + alsa_ctl \ | |
52 | + audio.primary.x86 \ | |
53 | + audio.a2dp.default \ | |
54 | + audio_policy.default \ |
@@ -0,0 +1,177 @@ | ||
1 | +# Android ALSA configuration file | |
2 | + | |
3 | +# | |
4 | +# Mixer Devices | |
5 | +# | |
6 | +ctl.AndroidPlayback { | |
7 | + type hw | |
8 | + card 0 | |
9 | + device 0 | |
10 | +} | |
11 | + | |
12 | +ctl.AndroidPlayback_Widi-Loopback { | |
13 | + type hw | |
14 | + card Loopback | |
15 | + device 0 | |
16 | +} | |
17 | + | |
18 | +ctl.AndroidRecord { | |
19 | + type hw | |
20 | + card 0 | |
21 | + device 0 | |
22 | +} | |
23 | + | |
24 | +ctl.AndroidPlayback_HDMIAudio { | |
25 | + type hw | |
26 | + card 0 | |
27 | + device 0 | |
28 | +} | |
29 | + | |
30 | +# | |
31 | +# Playback Devices | |
32 | +# | |
33 | +pcm.AndroidPlayback { | |
34 | + type plug | |
35 | + slave.pcm "both" | |
36 | +} | |
37 | + | |
38 | +pcm.AndroidPlayback_Speaker_normal { | |
39 | + type plug | |
40 | + slave.pcm "speaker" | |
41 | +} | |
42 | + | |
43 | +pcm.AndroidPlayback_Headset_normal { | |
44 | + type plug | |
45 | + slave.pcm "headphone" | |
46 | +} | |
47 | + | |
48 | +pcm.both { | |
49 | + type hooks | |
50 | + slave.pcm { | |
51 | + type hw | |
52 | + card 0 | |
53 | + device 0 | |
54 | + } | |
55 | + hooks.0 { | |
56 | + type ctl_elems | |
57 | + hook_args [ | |
58 | + {name "Speaker Mux Playback Route" value [1 1]} | |
59 | + {name "Mode Playback Route" value 0} | |
60 | + {name "Playback Switch" value 1} | |
61 | + ] | |
62 | + } | |
63 | +} | |
64 | + | |
65 | +pcm.speaker { | |
66 | + type hooks | |
67 | + slave.pcm { | |
68 | + type hw | |
69 | + card 0 | |
70 | + device 0 | |
71 | + } | |
72 | + hooks.0 { | |
73 | + type ctl_elems | |
74 | + hook_args [ | |
75 | + {name "Speaker Mux Playback Route" value [1 1]} | |
76 | + {name "Mode Playback Route" value 0} | |
77 | + {name "Playback Switch" value 0} | |
78 | + ] | |
79 | + } | |
80 | +} | |
81 | + | |
82 | +pcm.headphone { | |
83 | + type hooks | |
84 | + slave.pcm { | |
85 | + type hw | |
86 | + card 0 | |
87 | + device 0 | |
88 | + } | |
89 | + hooks.0 { | |
90 | + type ctl_elems | |
91 | + hook_args [ | |
92 | + {name "Speaker Mux Playback Route" value [0 0]} | |
93 | + {name "Mode Playback Route" value 0} | |
94 | + {name "Playback Switch" value 1} | |
95 | + ] | |
96 | + } | |
97 | +} | |
98 | + | |
99 | +# | |
100 | +# HDMI routing | |
101 | +# | |
102 | +pcm.HDMIAudio { | |
103 | + type hw | |
104 | + card 0 | |
105 | + device 0 | |
106 | +} | |
107 | + | |
108 | +pcm.AndroidPlayback_HDMIAudio { | |
109 | + type linear | |
110 | + slave { | |
111 | + pcm HDMIAudio | |
112 | + format S24_LE | |
113 | + } | |
114 | +} | |
115 | + | |
116 | +pcm.AndroidPlayback_HDMIAudio_normal { | |
117 | + type linear | |
118 | + slave { | |
119 | + pcm HDMIAudio | |
120 | + format S24_LE | |
121 | + } | |
122 | +} | |
123 | + | |
124 | +# | |
125 | +# WIDI routing | |
126 | +# | |
127 | +pcm.AndroidPlayback_Widi-Loopback{ | |
128 | + type plug | |
129 | + slave.pcm "loopback_sink" | |
130 | +} | |
131 | + | |
132 | +pcm.AndroidPlayback_Widi-Loopback_normal{ | |
133 | + type plug | |
134 | + slave.pcm "loopback_sink" | |
135 | +} | |
136 | + | |
137 | +pcm.loopback_sink { | |
138 | + type hw | |
139 | + card Loopback | |
140 | + device 0 | |
141 | + subdevice 0 | |
142 | +} | |
143 | + | |
144 | +# | |
145 | +# Capture device | |
146 | +# | |
147 | +pcm.AndroidCapture { | |
148 | + type hooks | |
149 | + slave.pcm { | |
150 | + type hw | |
151 | + card 0 | |
152 | + device 0 | |
153 | + } | |
154 | + hooks.0 { | |
155 | + type ctl_elems | |
156 | + hook_args [ | |
157 | + {name "DMIC12 Capture Route" value 1} | |
158 | + {name "DMIC34 Capture Route" value 1} | |
159 | + {name "Txpath1 Capture Route" value 0} | |
160 | + {name "Txpath2 Capture Route" value 2} | |
161 | + {name "Txpath3 Capture Route" value 6} | |
162 | + {name "Txpath4 Capture Route" value 6} | |
163 | + ] | |
164 | + } | |
165 | +} | |
166 | + | |
167 | + | |
168 | + | |
169 | +# | |
170 | +# WIDI | |
171 | +# | |
172 | +pcm.loopback_src { | |
173 | + type hw | |
174 | + card Loopback | |
175 | + device 1 | |
176 | + subdevice 0 | |
177 | +} |
@@ -0,0 +1,1274 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2012 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#define LOG_TAG "audio_hw_primary" | |
18 | +/*#define LOG_NDEBUG 0*/ | |
19 | + | |
20 | +#include <errno.h> | |
21 | +#include <pthread.h> | |
22 | +#include <stdint.h> | |
23 | +#include <stdlib.h> | |
24 | +#include <sys/time.h> | |
25 | + | |
26 | +#include <cutils/log.h> | |
27 | +#include <cutils/properties.h> | |
28 | +#include <cutils/str_parms.h> | |
29 | + | |
30 | +#include <hardware/audio.h> | |
31 | +#include <hardware/hardware.h> | |
32 | + | |
33 | +#include <system/audio.h> | |
34 | + | |
35 | +#include <tinyalsa/asoundlib.h> | |
36 | + | |
37 | +#include <audio_utils/resampler.h> | |
38 | + | |
39 | +#include "audio_route.h" | |
40 | + | |
41 | +#define PCM_CARD 0 | |
42 | +#define PCM_DEVICE 0 | |
43 | +#define PCM_DEVICE_SCO 2 | |
44 | + | |
45 | +#define OUT_PERIOD_SIZE 512 | |
46 | +#define OUT_SHORT_PERIOD_COUNT 2 | |
47 | +#define OUT_LONG_PERIOD_COUNT 8 | |
48 | +#define OUT_SAMPLING_RATE 44100 | |
49 | + | |
50 | +#define IN_PERIOD_SIZE 1024 | |
51 | +#define IN_PERIOD_COUNT 4 | |
52 | +#define IN_SAMPLING_RATE 44100 | |
53 | + | |
54 | +#define SCO_PERIOD_SIZE 256 | |
55 | +#define SCO_PERIOD_COUNT 4 | |
56 | +#define SCO_SAMPLING_RATE 8000 | |
57 | + | |
58 | +/* minimum sleep time in out_write() when write threshold is not reached */ | |
59 | +#define MIN_WRITE_SLEEP_US 2000 | |
60 | +#define MAX_WRITE_SLEEP_US ((OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT * 1000000) \ | |
61 | + / OUT_SAMPLING_RATE) | |
62 | + | |
63 | +enum { | |
64 | + OUT_BUFFER_TYPE_UNKNOWN, | |
65 | + OUT_BUFFER_TYPE_SHORT, | |
66 | + OUT_BUFFER_TYPE_LONG, | |
67 | +}; | |
68 | + | |
69 | +struct pcm_config pcm_config_out = { | |
70 | + .channels = 2, | |
71 | + .rate = OUT_SAMPLING_RATE, | |
72 | + .period_size = OUT_PERIOD_SIZE, | |
73 | + .period_count = OUT_LONG_PERIOD_COUNT, | |
74 | + .format = PCM_FORMAT_S16_LE, | |
75 | + .start_threshold = OUT_PERIOD_SIZE * OUT_SHORT_PERIOD_COUNT, | |
76 | +}; | |
77 | + | |
78 | +struct pcm_config pcm_config_in = { | |
79 | + .channels = 2, | |
80 | + .rate = IN_SAMPLING_RATE, | |
81 | + .period_size = IN_PERIOD_SIZE, | |
82 | + .period_count = IN_PERIOD_COUNT, | |
83 | + .format = PCM_FORMAT_S16_LE, | |
84 | + .start_threshold = 1, | |
85 | + .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT), | |
86 | +}; | |
87 | + | |
88 | +struct pcm_config pcm_config_sco = { | |
89 | + .channels = 1, | |
90 | + .rate = SCO_SAMPLING_RATE, | |
91 | + .period_size = SCO_PERIOD_SIZE, | |
92 | + .period_count = SCO_PERIOD_COUNT, | |
93 | + .format = PCM_FORMAT_S16_LE, | |
94 | +}; | |
95 | + | |
96 | +struct audio_device { | |
97 | + struct audio_hw_device hw_device; | |
98 | + | |
99 | + pthread_mutex_t lock; /* see note below on mutex acquisition order */ | |
100 | + unsigned int out_device; | |
101 | + unsigned int in_device; | |
102 | + bool standby; | |
103 | + bool mic_mute; | |
104 | + struct audio_route *ar; | |
105 | + int orientation; | |
106 | + bool screen_off; | |
107 | + | |
108 | + struct stream_out *active_out; | |
109 | + struct stream_in *active_in; | |
110 | +}; | |
111 | + | |
112 | +struct stream_out { | |
113 | + struct audio_stream_out stream; | |
114 | + | |
115 | + pthread_mutex_t lock; /* see note below on mutex acquisition order */ | |
116 | + struct pcm *pcm; | |
117 | + struct pcm_config *pcm_config; | |
118 | + bool standby; | |
119 | + | |
120 | + struct resampler_itfe *resampler; | |
121 | + int16_t *buffer; | |
122 | + size_t buffer_frames; | |
123 | + | |
124 | + int write_threshold; | |
125 | + int cur_write_threshold; | |
126 | + int buffer_type; | |
127 | + | |
128 | + struct audio_device *dev; | |
129 | +}; | |
130 | + | |
131 | +struct stream_in { | |
132 | + struct audio_stream_in stream; | |
133 | + | |
134 | + pthread_mutex_t lock; /* see note below on mutex acquisition order */ | |
135 | + struct pcm *pcm; | |
136 | + struct pcm_config *pcm_config; | |
137 | + bool standby; | |
138 | + | |
139 | + unsigned int requested_rate; | |
140 | + struct resampler_itfe *resampler; | |
141 | + struct resampler_buffer_provider buf_provider; | |
142 | + int16_t *buffer; | |
143 | + size_t buffer_size; | |
144 | + size_t frames_in; | |
145 | + int read_status; | |
146 | + | |
147 | + struct audio_device *dev; | |
148 | +}; | |
149 | + | |
150 | +enum { | |
151 | + ORIENTATION_LANDSCAPE, | |
152 | + ORIENTATION_PORTRAIT, | |
153 | + ORIENTATION_SQUARE, | |
154 | + ORIENTATION_UNDEFINED, | |
155 | +}; | |
156 | + | |
157 | +static uint32_t out_get_sample_rate(const struct audio_stream *stream); | |
158 | +static size_t out_get_buffer_size(const struct audio_stream *stream); | |
159 | +static audio_format_t out_get_format(const struct audio_stream *stream); | |
160 | +static uint32_t in_get_sample_rate(const struct audio_stream *stream); | |
161 | +static size_t in_get_buffer_size(const struct audio_stream *stream); | |
162 | +static audio_format_t in_get_format(const struct audio_stream *stream); | |
163 | +static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, | |
164 | + struct resampler_buffer* buffer); | |
165 | +static void release_buffer(struct resampler_buffer_provider *buffer_provider, | |
166 | + struct resampler_buffer* buffer); | |
167 | + | |
168 | +/* | |
169 | + * NOTE: when multiple mutexes have to be acquired, always take the | |
170 | + * audio_device mutex first, followed by the stream_in and/or | |
171 | + * stream_out mutexes. | |
172 | + */ | |
173 | + | |
174 | +/* Helper functions */ | |
175 | + | |
176 | +static void select_devices(struct audio_device *adev) | |
177 | +{ | |
178 | + int headphone_on; | |
179 | + int speaker_on; | |
180 | + int docked; | |
181 | + int main_mic_on; | |
182 | + | |
183 | + headphone_on = adev->out_device & (AUDIO_DEVICE_OUT_WIRED_HEADSET | | |
184 | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE); | |
185 | + speaker_on = adev->out_device & AUDIO_DEVICE_OUT_SPEAKER; | |
186 | + docked = adev->out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; | |
187 | + main_mic_on = adev->in_device & AUDIO_DEVICE_IN_BUILTIN_MIC; | |
188 | + | |
189 | + reset_mixer_state(adev->ar); | |
190 | + | |
191 | + if (speaker_on) | |
192 | + audio_route_apply_path(adev->ar, "speaker"); | |
193 | + if (headphone_on) | |
194 | + audio_route_apply_path(adev->ar, "headphone"); | |
195 | + if (docked) | |
196 | + audio_route_apply_path(adev->ar, "dock"); | |
197 | + if (main_mic_on) { | |
198 | + if (adev->orientation == ORIENTATION_LANDSCAPE) | |
199 | + audio_route_apply_path(adev->ar, "main-mic-left"); | |
200 | + else | |
201 | + audio_route_apply_path(adev->ar, "main-mic-top"); | |
202 | + } | |
203 | + | |
204 | + update_mixer_state(adev->ar); | |
205 | + | |
206 | + ALOGV("hp=%c speaker=%c dock=%c main-mic=%c", headphone_on ? 'y' : 'n', | |
207 | + speaker_on ? 'y' : 'n', docked ? 'y' : 'n', main_mic_on ? 'y' : 'n'); | |
208 | +} | |
209 | + | |
210 | +/* must be called with hw device and output stream mutexes locked */ | |
211 | +static void do_out_standby(struct stream_out *out) | |
212 | +{ | |
213 | + struct audio_device *adev = out->dev; | |
214 | + | |
215 | + if (!out->standby) { | |
216 | + pcm_close(out->pcm); | |
217 | + out->pcm = NULL; | |
218 | + adev->active_out = NULL; | |
219 | + if (out->resampler) { | |
220 | + release_resampler(out->resampler); | |
221 | + out->resampler = NULL; | |
222 | + } | |
223 | + if (out->buffer) { | |
224 | + free(out->buffer); | |
225 | + out->buffer = NULL; | |
226 | + } | |
227 | + out->standby = true; | |
228 | + } | |
229 | +} | |
230 | + | |
231 | +/* must be called with hw device and input stream mutexes locked */ | |
232 | +static void do_in_standby(struct stream_in *in) | |
233 | +{ | |
234 | + struct audio_device *adev = in->dev; | |
235 | + | |
236 | + if (!in->standby) { | |
237 | + pcm_close(in->pcm); | |
238 | + in->pcm = NULL; | |
239 | + adev->active_in = NULL; | |
240 | + if (in->resampler) { | |
241 | + release_resampler(in->resampler); | |
242 | + in->resampler = NULL; | |
243 | + } | |
244 | + if (in->buffer) { | |
245 | + free(in->buffer); | |
246 | + in->buffer = NULL; | |
247 | + } | |
248 | + in->standby = true; | |
249 | + } | |
250 | +} | |
251 | + | |
252 | +/* must be called with hw device and output stream mutexes locked */ | |
253 | +static int start_output_stream(struct stream_out *out) | |
254 | +{ | |
255 | + struct audio_device *adev = out->dev; | |
256 | + unsigned int device; | |
257 | + int ret; | |
258 | + | |
259 | + /* | |
260 | + * Due to the lack of sample rate converters in the SoC, | |
261 | + * it greatly simplifies things to have only the main | |
262 | + * (speaker/headphone) PCM or the BC SCO PCM open at | |
263 | + * the same time. | |
264 | + */ | |
265 | + if (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO) { | |
266 | + device = PCM_DEVICE_SCO; | |
267 | + out->pcm_config = &pcm_config_sco; | |
268 | + } else { | |
269 | + device = PCM_DEVICE; | |
270 | + out->pcm_config = &pcm_config_out; | |
271 | + out->buffer_type = OUT_BUFFER_TYPE_UNKNOWN; | |
272 | + } | |
273 | + | |
274 | + /* | |
275 | + * All open PCMs can only use a single group of rates at once: | |
276 | + * Group 1: 11.025, 22.05, 44.1 | |
277 | + * Group 2: 8, 16, 32, 48 | |
278 | + * Group 1 is used for digital audio playback since 44.1 is | |
279 | + * the most common rate, but group 2 is required for SCO. | |
280 | + */ | |
281 | + if (adev->active_in) { | |
282 | + struct stream_in *in = adev->active_in; | |
283 | + pthread_mutex_lock(&in->lock); | |
284 | + if (((out->pcm_config->rate % 8000 == 0) && | |
285 | + (in->pcm_config->rate % 8000) != 0) || | |
286 | + ((out->pcm_config->rate % 11025 == 0) && | |
287 | + (in->pcm_config->rate % 11025) != 0)) | |
288 | + do_in_standby(in); | |
289 | + pthread_mutex_unlock(&in->lock); | |
290 | + } | |
291 | + | |
292 | + out->pcm = pcm_open(PCM_CARD, device, PCM_OUT | PCM_NORESTART, out->pcm_config); | |
293 | + | |
294 | + if (out->pcm && !pcm_is_ready(out->pcm)) { | |
295 | + ALOGE("pcm_open(out) failed: %s", pcm_get_error(out->pcm)); | |
296 | + pcm_close(out->pcm); | |
297 | + return -ENOMEM; | |
298 | + } | |
299 | + | |
300 | + /* | |
301 | + * If the stream rate differs from the PCM rate, we need to | |
302 | + * create a resampler. | |
303 | + */ | |
304 | + if (out_get_sample_rate(&out->stream.common) != out->pcm_config->rate) { | |
305 | + ret = create_resampler(out_get_sample_rate(&out->stream.common), | |
306 | + out->pcm_config->rate, | |
307 | + out->pcm_config->channels, | |
308 | + RESAMPLER_QUALITY_DEFAULT, | |
309 | + NULL, | |
310 | + &out->resampler); | |
311 | + out->buffer_frames = (pcm_config_out.period_size * out->pcm_config->rate) / | |
312 | + out_get_sample_rate(&out->stream.common) + 1; | |
313 | + | |
314 | + out->buffer = malloc(pcm_frames_to_bytes(out->pcm, out->buffer_frames)); | |
315 | + } | |
316 | + | |
317 | + adev->active_out = out; | |
318 | + | |
319 | + return 0; | |
320 | +} | |
321 | + | |
322 | +/* must be called with hw device and input stream mutexes locked */ | |
323 | +static int start_input_stream(struct stream_in *in) | |
324 | +{ | |
325 | + struct audio_device *adev = in->dev; | |
326 | + unsigned int device; | |
327 | + int ret; | |
328 | + | |
329 | + /* | |
330 | + * Due to the lack of sample rate converters in the SoC, | |
331 | + * it greatly simplifies things to have only the main | |
332 | + * mic PCM or the BC SCO PCM open at the same time. | |
333 | + */ | |
334 | + if (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO) { | |
335 | + device = PCM_DEVICE_SCO; | |
336 | + in->pcm_config = &pcm_config_sco; | |
337 | + } else { | |
338 | + device = PCM_DEVICE; | |
339 | + in->pcm_config = &pcm_config_in; | |
340 | + } | |
341 | + | |
342 | + /* | |
343 | + * All open PCMs can only use a single group of rates at once: | |
344 | + * Group 1: 11.025, 22.05, 44.1 | |
345 | + * Group 2: 8, 16, 32, 48 | |
346 | + * Group 1 is used for digital audio playback since 44.1 is | |
347 | + * the most common rate, but group 2 is required for SCO. | |
348 | + */ | |
349 | + if (adev->active_out) { | |
350 | + struct stream_out *out = adev->active_out; | |
351 | + pthread_mutex_lock(&out->lock); | |
352 | + if (((in->pcm_config->rate % 8000 == 0) && | |
353 | + (out->pcm_config->rate % 8000) != 0) || | |
354 | + ((in->pcm_config->rate % 11025 == 0) && | |
355 | + (out->pcm_config->rate % 11025) != 0)) | |
356 | + do_out_standby(out); | |
357 | + pthread_mutex_unlock(&out->lock); | |
358 | + } | |
359 | + | |
360 | + in->pcm = pcm_open(PCM_CARD, device, PCM_IN, in->pcm_config); | |
361 | + | |
362 | + if (in->pcm && !pcm_is_ready(in->pcm)) { | |
363 | + ALOGE("pcm_open(in) failed: %s", pcm_get_error(in->pcm)); | |
364 | + pcm_close(in->pcm); | |
365 | + return -ENOMEM; | |
366 | + } | |
367 | + | |
368 | + /* | |
369 | + * If the stream rate differs from the PCM rate, we need to | |
370 | + * create a resampler. | |
371 | + */ | |
372 | + if (in_get_sample_rate(&in->stream.common) != in->pcm_config->rate) { | |
373 | + in->buf_provider.get_next_buffer = get_next_buffer; | |
374 | + in->buf_provider.release_buffer = release_buffer; | |
375 | + | |
376 | + ret = create_resampler(in->pcm_config->rate, | |
377 | + in_get_sample_rate(&in->stream.common), | |
378 | + 1, | |
379 | + RESAMPLER_QUALITY_DEFAULT, | |
380 | + &in->buf_provider, | |
381 | + &in->resampler); | |
382 | + } | |
383 | + in->buffer_size = pcm_frames_to_bytes(in->pcm, | |
384 | + in->pcm_config->period_size); | |
385 | + in->buffer = malloc(in->buffer_size); | |
386 | + in->frames_in = 0; | |
387 | + | |
388 | + adev->active_in = in; | |
389 | + | |
390 | + return 0; | |
391 | +} | |
392 | + | |
393 | +static int get_next_buffer(struct resampler_buffer_provider *buffer_provider, | |
394 | + struct resampler_buffer* buffer) | |
395 | +{ | |
396 | + struct stream_in *in; | |
397 | + | |
398 | + if (buffer_provider == NULL || buffer == NULL) | |
399 | + return -EINVAL; | |
400 | + | |
401 | + in = (struct stream_in *)((char *)buffer_provider - | |
402 | + offsetof(struct stream_in, buf_provider)); | |
403 | + | |
404 | + if (in->pcm == NULL) { | |
405 | + buffer->raw = NULL; | |
406 | + buffer->frame_count = 0; | |
407 | + in->read_status = -ENODEV; | |
408 | + return -ENODEV; | |
409 | + } | |
410 | + | |
411 | + if (in->frames_in == 0) { | |
412 | + in->read_status = pcm_read(in->pcm, | |
413 | + (void*)in->buffer, | |
414 | + in->buffer_size); | |
415 | + if (in->read_status != 0) { | |
416 | + ALOGE("get_next_buffer() pcm_read error %d", in->read_status); | |
417 | + buffer->raw = NULL; | |
418 | + buffer->frame_count = 0; | |
419 | + return in->read_status; | |
420 | + } | |
421 | + in->frames_in = in->pcm_config->period_size; | |
422 | + if (in->pcm_config->channels == 2) { | |
423 | + unsigned int i; | |
424 | + | |
425 | + /* Discard right channel */ | |
426 | + for (i = 1; i < in->frames_in; i++) | |
427 | + in->buffer[i] = in->buffer[i * 2]; | |
428 | + } | |
429 | + } | |
430 | + | |
431 | + buffer->frame_count = (buffer->frame_count > in->frames_in) ? | |
432 | + in->frames_in : buffer->frame_count; | |
433 | + buffer->i16 = in->buffer + (in->pcm_config->period_size - in->frames_in); | |
434 | + | |
435 | + return in->read_status; | |
436 | + | |
437 | +} | |
438 | + | |
439 | +static void release_buffer(struct resampler_buffer_provider *buffer_provider, | |
440 | + struct resampler_buffer* buffer) | |
441 | +{ | |
442 | + struct stream_in *in; | |
443 | + | |
444 | + if (buffer_provider == NULL || buffer == NULL) | |
445 | + return; | |
446 | + | |
447 | + in = (struct stream_in *)((char *)buffer_provider - | |
448 | + offsetof(struct stream_in, buf_provider)); | |
449 | + | |
450 | + in->frames_in -= buffer->frame_count; | |
451 | +} | |
452 | + | |
453 | +/* read_frames() reads frames from kernel driver, down samples to capture rate | |
454 | + * if necessary and output the number of frames requested to the buffer specified */ | |
455 | +static ssize_t read_frames(struct stream_in *in, void *buffer, ssize_t frames) | |
456 | +{ | |
457 | + ssize_t frames_wr = 0; | |
458 | + | |
459 | + while (frames_wr < frames) { | |
460 | + size_t frames_rd = frames - frames_wr; | |
461 | + if (in->resampler != NULL) { | |
462 | + in->resampler->resample_from_provider(in->resampler, | |
463 | + (int16_t *)((char *)buffer + | |
464 | + frames_wr * audio_stream_frame_size(&in->stream.common)), | |
465 | + &frames_rd); | |
466 | + } else { | |
467 | + struct resampler_buffer buf = { | |
468 | + { raw : NULL, }, | |
469 | + frame_count : frames_rd, | |
470 | + }; | |
471 | + get_next_buffer(&in->buf_provider, &buf); | |
472 | + if (buf.raw != NULL) { | |
473 | + memcpy((char *)buffer + | |
474 | + frames_wr * audio_stream_frame_size(&in->stream.common), | |
475 | + buf.raw, | |
476 | + buf.frame_count * audio_stream_frame_size(&in->stream.common)); | |
477 | + frames_rd = buf.frame_count; | |
478 | + } | |
479 | + release_buffer(&in->buf_provider, &buf); | |
480 | + } | |
481 | + /* in->read_status is updated by getNextBuffer() also called by | |
482 | + * in->resampler->resample_from_provider() */ | |
483 | + if (in->read_status != 0) | |
484 | + return in->read_status; | |
485 | + | |
486 | + frames_wr += frames_rd; | |
487 | + } | |
488 | + return frames_wr; | |
489 | +} | |
490 | + | |
491 | +/* API functions */ | |
492 | + | |
493 | +static uint32_t out_get_sample_rate(const struct audio_stream *stream) | |
494 | +{ | |
495 | + return pcm_config_out.rate; | |
496 | +} | |
497 | + | |
498 | +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) | |
499 | +{ | |
500 | + return -ENOSYS; | |
501 | +} | |
502 | + | |
503 | +static size_t out_get_buffer_size(const struct audio_stream *stream) | |
504 | +{ | |
505 | + return pcm_config_out.period_size * | |
506 | + audio_stream_frame_size((struct audio_stream *)stream); | |
507 | +} | |
508 | + | |
509 | +static uint32_t out_get_channels(const struct audio_stream *stream) | |
510 | +{ | |
511 | + return AUDIO_CHANNEL_OUT_STEREO; | |
512 | +} | |
513 | + | |
514 | +static audio_format_t out_get_format(const struct audio_stream *stream) | |
515 | +{ | |
516 | + return AUDIO_FORMAT_PCM_16_BIT; | |
517 | +} | |
518 | + | |
519 | +static int out_set_format(struct audio_stream *stream, audio_format_t format) | |
520 | +{ | |
521 | + return -ENOSYS; | |
522 | +} | |
523 | + | |
524 | +static int out_standby(struct audio_stream *stream) | |
525 | +{ | |
526 | + struct stream_out *out = (struct stream_out *)stream; | |
527 | + | |
528 | + pthread_mutex_lock(&out->dev->lock); | |
529 | + pthread_mutex_lock(&out->lock); | |
530 | + do_out_standby(out); | |
531 | + pthread_mutex_unlock(&out->lock); | |
532 | + pthread_mutex_unlock(&out->dev->lock); | |
533 | + | |
534 | + return 0; | |
535 | +} | |
536 | + | |
537 | +static int out_dump(const struct audio_stream *stream, int fd) | |
538 | +{ | |
539 | + return 0; | |
540 | +} | |
541 | + | |
542 | +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) | |
543 | +{ | |
544 | + struct stream_out *out = (struct stream_out *)stream; | |
545 | + struct audio_device *adev = out->dev; | |
546 | + struct str_parms *parms; | |
547 | + char value[32]; | |
548 | + int ret; | |
549 | + unsigned int val; | |
550 | + | |
551 | + parms = str_parms_create_str(kvpairs); | |
552 | + | |
553 | + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, | |
554 | + value, sizeof(value)); | |
555 | + pthread_mutex_lock(&adev->lock); | |
556 | + if (ret >= 0) { | |
557 | + val = atoi(value); | |
558 | + if ((adev->out_device != val) && (val != 0)) { | |
559 | + /* | |
560 | + * If SCO is turned on/off, we need to put audio into standby | |
561 | + * because SCO uses a different PCM. | |
562 | + */ | |
563 | + if ((val & AUDIO_DEVICE_OUT_ALL_SCO) ^ | |
564 | + (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) { | |
565 | + pthread_mutex_lock(&out->lock); | |
566 | + do_out_standby(out); | |
567 | + pthread_mutex_unlock(&out->lock); | |
568 | + } | |
569 | + | |
570 | + adev->out_device = val; | |
571 | + select_devices(adev); | |
572 | + } | |
573 | + } | |
574 | + pthread_mutex_unlock(&adev->lock); | |
575 | + | |
576 | + str_parms_destroy(parms); | |
577 | + return ret; | |
578 | +} | |
579 | + | |
580 | +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) | |
581 | +{ | |
582 | + return strdup(""); | |
583 | +} | |
584 | + | |
585 | +static uint32_t out_get_latency(const struct audio_stream_out *stream) | |
586 | +{ | |
587 | + struct stream_out *out = (struct stream_out *)stream; | |
588 | + struct audio_device *adev = out->dev; | |
589 | + size_t period_count; | |
590 | + | |
591 | + pthread_mutex_lock(&adev->lock); | |
592 | + | |
593 | + if (adev->screen_off && !adev->active_in && !(adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) | |
594 | + period_count = OUT_LONG_PERIOD_COUNT; | |
595 | + else | |
596 | + period_count = OUT_SHORT_PERIOD_COUNT; | |
597 | + | |
598 | + pthread_mutex_unlock(&adev->lock); | |
599 | + | |
600 | + return (pcm_config_out.period_size * period_count * 1000) / pcm_config_out.rate; | |
601 | +} | |
602 | + | |
603 | +static int out_set_volume(struct audio_stream_out *stream, float left, | |
604 | + float right) | |
605 | +{ | |
606 | + return -ENOSYS; | |
607 | +} | |
608 | + | |
609 | +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | |
610 | + size_t bytes) | |
611 | +{ | |
612 | + int ret = 0; | |
613 | + struct stream_out *out = (struct stream_out *)stream; | |
614 | + struct audio_device *adev = out->dev; | |
615 | + size_t frame_size = audio_stream_frame_size(&out->stream.common); | |
616 | + int16_t *in_buffer = (int16_t *)buffer; | |
617 | + size_t in_frames = bytes / frame_size; | |
618 | + size_t out_frames; | |
619 | + int buffer_type; | |
620 | + int kernel_frames; | |
621 | + bool sco_on; | |
622 | + | |
623 | + /* | |
624 | + * acquiring hw device mutex systematically is useful if a low | |
625 | + * priority thread is waiting on the output stream mutex - e.g. | |
626 | + * executing out_set_parameters() while holding the hw device | |
627 | + * mutex | |
628 | + */ | |
629 | + pthread_mutex_lock(&adev->lock); | |
630 | + pthread_mutex_lock(&out->lock); | |
631 | + if (out->standby) { | |
632 | + ret = start_output_stream(out); | |
633 | + if (ret != 0) { | |
634 | + pthread_mutex_unlock(&adev->lock); | |
635 | + goto exit; | |
636 | + } | |
637 | + out->standby = false; | |
638 | + } | |
639 | + buffer_type = (adev->screen_off && !adev->active_in) ? | |
640 | + OUT_BUFFER_TYPE_LONG : OUT_BUFFER_TYPE_SHORT; | |
641 | + sco_on = (adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO); | |
642 | + pthread_mutex_unlock(&adev->lock); | |
643 | + | |
644 | + /* detect changes in screen ON/OFF state and adapt buffer size | |
645 | + * if needed. Do not change buffer size when routed to SCO device. */ | |
646 | + if (!sco_on && (buffer_type != out->buffer_type)) { | |
647 | + size_t period_count; | |
648 | + | |
649 | + if (buffer_type == OUT_BUFFER_TYPE_LONG) | |
650 | + period_count = OUT_LONG_PERIOD_COUNT; | |
651 | + else | |
652 | + period_count = OUT_SHORT_PERIOD_COUNT; | |
653 | + | |
654 | + out->write_threshold = out->pcm_config->period_size * period_count; | |
655 | + /* reset current threshold if exiting standby */ | |
656 | + if (out->buffer_type == OUT_BUFFER_TYPE_UNKNOWN) | |
657 | + out->cur_write_threshold = out->write_threshold; | |
658 | + out->buffer_type = buffer_type; | |
659 | + } | |
660 | + | |
661 | + /* Reduce number of channels, if necessary */ | |
662 | + if (popcount(out_get_channels(&stream->common)) > | |
663 | + (int)out->pcm_config->channels) { | |
664 | + unsigned int i; | |
665 | + | |
666 | + /* Discard right channel */ | |
667 | + for (i = 1; i < in_frames; i++) | |
668 | + in_buffer[i] = in_buffer[i * 2]; | |
669 | + | |
670 | + /* The frame size is now half */ | |
671 | + frame_size /= 2; | |
672 | + } | |
673 | + | |
674 | + /* Change sample rate, if necessary */ | |
675 | + if (out_get_sample_rate(&stream->common) != out->pcm_config->rate) { | |
676 | + out_frames = out->buffer_frames; | |
677 | + out->resampler->resample_from_input(out->resampler, | |
678 | + in_buffer, &in_frames, | |
679 | + out->buffer, &out_frames); | |
680 | + in_buffer = out->buffer; | |
681 | + } else { | |
682 | + out_frames = in_frames; | |
683 | + } | |
684 | + | |
685 | + if (!sco_on) { | |
686 | + int total_sleep_time_us = 0; | |
687 | + size_t period_size = out->pcm_config->period_size; | |
688 | + | |
689 | + /* do not allow more than out->cur_write_threshold frames in kernel | |
690 | + * pcm driver buffer */ | |
691 | + do { | |
692 | + struct timespec time_stamp; | |
693 | + if (pcm_get_htimestamp(out->pcm, | |
694 | + (unsigned int *)&kernel_frames, | |
695 | + &time_stamp) < 0) | |
696 | + break; | |
697 | + kernel_frames = pcm_get_buffer_size(out->pcm) - kernel_frames; | |
698 | + | |
699 | + if (kernel_frames > out->cur_write_threshold) { | |
700 | + int sleep_time_us = | |
701 | + (int)(((int64_t)(kernel_frames - out->cur_write_threshold) | |
702 | + * 1000000) / out->pcm_config->rate); | |
703 | + if (sleep_time_us < MIN_WRITE_SLEEP_US) | |
704 | + break; | |
705 | + total_sleep_time_us += sleep_time_us; | |
706 | + if (total_sleep_time_us > MAX_WRITE_SLEEP_US) { | |
707 | + ALOGW("out_write() limiting sleep time %d to %d", | |
708 | + total_sleep_time_us, MAX_WRITE_SLEEP_US); | |
709 | + sleep_time_us = MAX_WRITE_SLEEP_US - | |
710 | + (total_sleep_time_us - sleep_time_us); | |
711 | + } | |
712 | + usleep(sleep_time_us); | |
713 | + } | |
714 | + | |
715 | + } while ((kernel_frames > out->cur_write_threshold) && | |
716 | + (total_sleep_time_us <= MAX_WRITE_SLEEP_US)); | |
717 | + | |
718 | + /* do not allow abrupt changes on buffer size. Increasing/decreasing | |
719 | + * the threshold by steps of 1/4th of the buffer size keeps the write | |
720 | + * time within a reasonable range during transitions. | |
721 | + * Also reset current threshold just above current filling status when | |
722 | + * kernel buffer is really depleted to allow for smooth catching up with | |
723 | + * target threshold. | |
724 | + */ | |
725 | + if (out->cur_write_threshold > out->write_threshold) { | |
726 | + out->cur_write_threshold -= period_size / 4; | |
727 | + if (out->cur_write_threshold < out->write_threshold) { | |
728 | + out->cur_write_threshold = out->write_threshold; | |
729 | + } | |
730 | + } else if (out->cur_write_threshold < out->write_threshold) { | |
731 | + out->cur_write_threshold += period_size / 4; | |
732 | + if (out->cur_write_threshold > out->write_threshold) { | |
733 | + out->cur_write_threshold = out->write_threshold; | |
734 | + } | |
735 | + } else if ((kernel_frames < out->write_threshold) && | |
736 | + ((out->write_threshold - kernel_frames) > | |
737 | + (int)(period_size * OUT_SHORT_PERIOD_COUNT))) { | |
738 | + out->cur_write_threshold = (kernel_frames / period_size + 1) * period_size; | |
739 | + out->cur_write_threshold += period_size / 4; | |
740 | + } | |
741 | + } | |
742 | + | |
743 | + ret = pcm_write(out->pcm, in_buffer, out_frames * frame_size); | |
744 | + if (ret == -EPIPE) { | |
745 | + /* In case of underrun, don't sleep since we want to catch up asap */ | |
746 | + pthread_mutex_unlock(&out->lock); | |
747 | + return ret; | |
748 | + } | |
749 | + | |
750 | +exit: | |
751 | + pthread_mutex_unlock(&out->lock); | |
752 | + | |
753 | + if (ret != 0) { | |
754 | + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / | |
755 | + out_get_sample_rate(&stream->common)); | |
756 | + } | |
757 | + | |
758 | + return bytes; | |
759 | +} | |
760 | + | |
761 | +static int out_get_render_position(const struct audio_stream_out *stream, | |
762 | + uint32_t *dsp_frames) | |
763 | +{ | |
764 | + return -EINVAL; | |
765 | +} | |
766 | + | |
767 | +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | |
768 | +{ | |
769 | + return 0; | |
770 | +} | |
771 | + | |
772 | +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) | |
773 | +{ | |
774 | + return 0; | |
775 | +} | |
776 | + | |
777 | +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, | |
778 | + int64_t *timestamp) | |
779 | +{ | |
780 | + return -EINVAL; | |
781 | +} | |
782 | + | |
783 | +/** audio_stream_in implementation **/ | |
784 | +static uint32_t in_get_sample_rate(const struct audio_stream *stream) | |
785 | +{ | |
786 | + struct stream_in *in = (struct stream_in *)stream; | |
787 | + | |
788 | + return in->requested_rate; | |
789 | +} | |
790 | + | |
791 | +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) | |
792 | +{ | |
793 | + return 0; | |
794 | +} | |
795 | + | |
796 | +static size_t in_get_buffer_size(const struct audio_stream *stream) | |
797 | +{ | |
798 | + struct stream_in *in = (struct stream_in *)stream; | |
799 | + size_t size; | |
800 | + | |
801 | + /* | |
802 | + * take resampling into account and return the closest majoring | |
803 | + * multiple of 16 frames, as audioflinger expects audio buffers to | |
804 | + * be a multiple of 16 frames | |
805 | + */ | |
806 | + size = (in->pcm_config->period_size * in_get_sample_rate(stream)) / | |
807 | + in->pcm_config->rate; | |
808 | + size = ((size + 15) / 16) * 16; | |
809 | + | |
810 | + return size * audio_stream_frame_size((struct audio_stream *)stream); | |
811 | +} | |
812 | + | |
813 | +static uint32_t in_get_channels(const struct audio_stream *stream) | |
814 | +{ | |
815 | + return AUDIO_CHANNEL_IN_MONO; | |
816 | +} | |
817 | + | |
818 | +static audio_format_t in_get_format(const struct audio_stream *stream) | |
819 | +{ | |
820 | + return AUDIO_FORMAT_PCM_16_BIT; | |
821 | +} | |
822 | + | |
823 | +static int in_set_format(struct audio_stream *stream, audio_format_t format) | |
824 | +{ | |
825 | + return -ENOSYS; | |
826 | +} | |
827 | + | |
828 | +static int in_standby(struct audio_stream *stream) | |
829 | +{ | |
830 | + struct stream_in *in = (struct stream_in *)stream; | |
831 | + | |
832 | + pthread_mutex_lock(&in->dev->lock); | |
833 | + pthread_mutex_lock(&in->lock); | |
834 | + do_in_standby(in); | |
835 | + pthread_mutex_unlock(&in->lock); | |
836 | + pthread_mutex_unlock(&in->dev->lock); | |
837 | + | |
838 | + return 0; | |
839 | +} | |
840 | + | |
841 | +static int in_dump(const struct audio_stream *stream, int fd) | |
842 | +{ | |
843 | + return 0; | |
844 | +} | |
845 | + | |
846 | +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) | |
847 | +{ | |
848 | + struct stream_in *in = (struct stream_in *)stream; | |
849 | + struct audio_device *adev = in->dev; | |
850 | + struct str_parms *parms; | |
851 | + char value[32]; | |
852 | + int ret; | |
853 | + unsigned int val; | |
854 | + | |
855 | + parms = str_parms_create_str(kvpairs); | |
856 | + | |
857 | + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, | |
858 | + value, sizeof(value)); | |
859 | + pthread_mutex_lock(&adev->lock); | |
860 | + if (ret >= 0) { | |
861 | + val = atoi(value) & ~AUDIO_DEVICE_BIT_IN; | |
862 | + if ((adev->in_device != val) && (val != 0)) { | |
863 | + /* | |
864 | + * If SCO is turned on/off, we need to put audio into standby | |
865 | + * because SCO uses a different PCM. | |
866 | + */ | |
867 | + if ((val & AUDIO_DEVICE_IN_ALL_SCO) ^ | |
868 | + (adev->in_device & AUDIO_DEVICE_IN_ALL_SCO)) { | |
869 | + pthread_mutex_lock(&in->lock); | |
870 | + do_in_standby(in); | |
871 | + pthread_mutex_unlock(&in->lock); | |
872 | + } | |
873 | + | |
874 | + adev->in_device = val; | |
875 | + select_devices(adev); | |
876 | + } | |
877 | + } | |
878 | + pthread_mutex_unlock(&adev->lock); | |
879 | + | |
880 | + str_parms_destroy(parms); | |
881 | + return ret; | |
882 | +} | |
883 | + | |
884 | +static char * in_get_parameters(const struct audio_stream *stream, | |
885 | + const char *keys) | |
886 | +{ | |
887 | + return strdup(""); | |
888 | +} | |
889 | + | |
890 | +static int in_set_gain(struct audio_stream_in *stream, float gain) | |
891 | +{ | |
892 | + return 0; | |
893 | +} | |
894 | + | |
895 | +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, | |
896 | + size_t bytes) | |
897 | +{ | |
898 | + int ret = 0; | |
899 | + struct stream_in *in = (struct stream_in *)stream; | |
900 | + struct audio_device *adev = in->dev; | |
901 | + size_t frames_rq = bytes / audio_stream_frame_size(&stream->common); | |
902 | + | |
903 | + /* | |
904 | + * acquiring hw device mutex systematically is useful if a low | |
905 | + * priority thread is waiting on the input stream mutex - e.g. | |
906 | + * executing in_set_parameters() while holding the hw device | |
907 | + * mutex | |
908 | + */ | |
909 | + pthread_mutex_lock(&adev->lock); | |
910 | + pthread_mutex_lock(&in->lock); | |
911 | + if (in->standby) { | |
912 | + ret = start_input_stream(in); | |
913 | + if (ret == 0) | |
914 | + in->standby = 0; | |
915 | + } | |
916 | + pthread_mutex_unlock(&adev->lock); | |
917 | + | |
918 | + if (ret < 0) | |
919 | + goto exit; | |
920 | + | |
921 | + /*if (in->num_preprocessors != 0) { | |
922 | + ret = process_frames(in, buffer, frames_rq); | |
923 | + } else */if (in->resampler != NULL) { | |
924 | + ret = read_frames(in, buffer, frames_rq); | |
925 | + } else if (in->pcm_config->channels == 2) { | |
926 | + /* | |
927 | + * If the PCM is stereo, capture twice as many frames and | |
928 | + * discard the right channel. | |
929 | + */ | |
930 | + unsigned int i; | |
931 | + int16_t *in_buffer = (int16_t *)buffer; | |
932 | + | |
933 | + ret = pcm_read(in->pcm, in->buffer, bytes * 2); | |
934 | + | |
935 | + /* Discard right channel */ | |
936 | + for (i = 0; i < frames_rq; i++) | |
937 | + in_buffer[i] = in->buffer[i * 2]; | |
938 | + } else { | |
939 | + ret = pcm_read(in->pcm, buffer, bytes); | |
940 | + } | |
941 | + | |
942 | + if (ret > 0) | |
943 | + ret = 0; | |
944 | + | |
945 | + /* | |
946 | + * Instead of writing zeroes here, we could trust the hardware | |
947 | + * to always provide zeroes when muted. | |
948 | + */ | |
949 | + if (ret == 0 && adev->mic_mute) | |
950 | + memset(buffer, 0, bytes); | |
951 | + | |
952 | +exit: | |
953 | + if (ret < 0) | |
954 | + usleep(bytes * 1000000 / audio_stream_frame_size(&stream->common) / | |
955 | + in_get_sample_rate(&stream->common)); | |
956 | + | |
957 | + pthread_mutex_unlock(&in->lock); | |
958 | + return bytes; | |
959 | +} | |
960 | + | |
961 | +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) | |
962 | +{ | |
963 | + return 0; | |
964 | +} | |
965 | + | |
966 | +static int in_add_audio_effect(const struct audio_stream *stream, | |
967 | + effect_handle_t effect) | |
968 | +{ | |
969 | + return 0; | |
970 | +} | |
971 | + | |
972 | +static int in_remove_audio_effect(const struct audio_stream *stream, | |
973 | + effect_handle_t effect) | |
974 | +{ | |
975 | + return 0; | |
976 | +} | |
977 | + | |
978 | + | |
979 | +static int adev_open_output_stream(struct audio_hw_device *dev, | |
980 | + audio_io_handle_t handle, | |
981 | + audio_devices_t devices, | |
982 | + audio_output_flags_t flags, | |
983 | + struct audio_config *config, | |
984 | + struct audio_stream_out **stream_out) | |
985 | +{ | |
986 | + struct audio_device *adev = (struct audio_device *)dev; | |
987 | + struct stream_out *out; | |
988 | + int ret; | |
989 | + | |
990 | + out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); | |
991 | + if (!out) | |
992 | + return -ENOMEM; | |
993 | + | |
994 | + out->stream.common.get_sample_rate = out_get_sample_rate; | |
995 | + out->stream.common.set_sample_rate = out_set_sample_rate; | |
996 | + out->stream.common.get_buffer_size = out_get_buffer_size; | |
997 | + out->stream.common.get_channels = out_get_channels; | |
998 | + out->stream.common.get_format = out_get_format; | |
999 | + out->stream.common.set_format = out_set_format; | |
1000 | + out->stream.common.standby = out_standby; | |
1001 | + out->stream.common.dump = out_dump; | |
1002 | + out->stream.common.set_parameters = out_set_parameters; | |
1003 | + out->stream.common.get_parameters = out_get_parameters; | |
1004 | + out->stream.common.add_audio_effect = out_add_audio_effect; | |
1005 | + out->stream.common.remove_audio_effect = out_remove_audio_effect; | |
1006 | + out->stream.get_latency = out_get_latency; | |
1007 | + out->stream.set_volume = out_set_volume; | |
1008 | + out->stream.write = out_write; | |
1009 | + out->stream.get_render_position = out_get_render_position; | |
1010 | + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; | |
1011 | + | |
1012 | + out->dev = adev; | |
1013 | + | |
1014 | + config->format = out_get_format(&out->stream.common); | |
1015 | + config->channel_mask = out_get_channels(&out->stream.common); | |
1016 | + config->sample_rate = out_get_sample_rate(&out->stream.common); | |
1017 | + | |
1018 | + out->standby = true; | |
1019 | + | |
1020 | + *stream_out = &out->stream; | |
1021 | + return 0; | |
1022 | + | |
1023 | +err_open: | |
1024 | + free(out); | |
1025 | + *stream_out = NULL; | |
1026 | + return ret; | |
1027 | +} | |
1028 | + | |
1029 | +static void adev_close_output_stream(struct audio_hw_device *dev, | |
1030 | + struct audio_stream_out *stream) | |
1031 | +{ | |
1032 | + out_standby(&stream->common); | |
1033 | + free(stream); | |
1034 | +} | |
1035 | + | |
1036 | +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) | |
1037 | +{ | |
1038 | + struct audio_device *adev = (struct audio_device *)dev; | |
1039 | + struct str_parms *parms; | |
1040 | + char *str; | |
1041 | + char value[32]; | |
1042 | + int ret; | |
1043 | + | |
1044 | + parms = str_parms_create_str(kvpairs); | |
1045 | + ret = str_parms_get_str(parms, "orientation", value, sizeof(value)); | |
1046 | + if (ret >= 0) { | |
1047 | + int orientation; | |
1048 | + | |
1049 | + if (strcmp(value, "landscape") == 0) | |
1050 | + orientation = ORIENTATION_LANDSCAPE; | |
1051 | + else if (strcmp(value, "portrait") == 0) | |
1052 | + orientation = ORIENTATION_PORTRAIT; | |
1053 | + else if (strcmp(value, "square") == 0) | |
1054 | + orientation = ORIENTATION_SQUARE; | |
1055 | + else | |
1056 | + orientation = ORIENTATION_UNDEFINED; | |
1057 | + | |
1058 | + pthread_mutex_lock(&adev->lock); | |
1059 | + if (orientation != adev->orientation) { | |
1060 | + adev->orientation = orientation; | |
1061 | + /* | |
1062 | + * Orientation changes can occur with the input device | |
1063 | + * closed so we must call select_devices() here to set | |
1064 | + * up the mixer. This is because select_devices() will | |
1065 | + * not be called when the input device is opened if no | |
1066 | + * other input parameter is changed. | |
1067 | + */ | |
1068 | + select_devices(adev); | |
1069 | + } | |
1070 | + pthread_mutex_unlock(&adev->lock); | |
1071 | + } | |
1072 | + | |
1073 | + ret = str_parms_get_str(parms, "screen_state", value, sizeof(value)); | |
1074 | + if (ret >= 0) { | |
1075 | + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) | |
1076 | + adev->screen_off = false; | |
1077 | + else | |
1078 | + adev->screen_off = true; | |
1079 | + } | |
1080 | + | |
1081 | + str_parms_destroy(parms); | |
1082 | + return ret; | |
1083 | +} | |
1084 | + | |
1085 | +static char * adev_get_parameters(const struct audio_hw_device *dev, | |
1086 | + const char *keys) | |
1087 | +{ | |
1088 | + return strdup(""); | |
1089 | +} | |
1090 | + | |
1091 | +static int adev_init_check(const struct audio_hw_device *dev) | |
1092 | +{ | |
1093 | + return 0; | |
1094 | +} | |
1095 | + | |
1096 | +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) | |
1097 | +{ | |
1098 | + return -ENOSYS; | |
1099 | +} | |
1100 | + | |
1101 | +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) | |
1102 | +{ | |
1103 | + return -ENOSYS; | |
1104 | +} | |
1105 | + | |
1106 | +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) | |
1107 | +{ | |
1108 | + return 0; | |
1109 | +} | |
1110 | + | |
1111 | +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) | |
1112 | +{ | |
1113 | + struct audio_device *adev = (struct audio_device *)dev; | |
1114 | + | |
1115 | + adev->mic_mute = state; | |
1116 | + | |
1117 | + return 0; | |
1118 | +} | |
1119 | + | |
1120 | +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) | |
1121 | +{ | |
1122 | + struct audio_device *adev = (struct audio_device *)dev; | |
1123 | + | |
1124 | + *state = adev->mic_mute; | |
1125 | + | |
1126 | + return 0; | |
1127 | +} | |
1128 | + | |
1129 | +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, | |
1130 | + const struct audio_config *config) | |
1131 | +{ | |
1132 | + size_t size; | |
1133 | + | |
1134 | + /* | |
1135 | + * take resampling into account and return the closest majoring | |
1136 | + * multiple of 16 frames, as audioflinger expects audio buffers to | |
1137 | + * be a multiple of 16 frames | |
1138 | + */ | |
1139 | + size = (pcm_config_in.period_size * config->sample_rate) / pcm_config_in.rate; | |
1140 | + size = ((size + 15) / 16) * 16; | |
1141 | + | |
1142 | + return (size * popcount(config->channel_mask) * | |
1143 | + audio_bytes_per_sample(config->format)); | |
1144 | +} | |
1145 | + | |
1146 | +static int adev_open_input_stream(struct audio_hw_device *dev, | |
1147 | + audio_io_handle_t handle, | |
1148 | + audio_devices_t devices, | |
1149 | + struct audio_config *config, | |
1150 | + struct audio_stream_in **stream_in) | |
1151 | +{ | |
1152 | + struct audio_device *adev = (struct audio_device *)dev; | |
1153 | + struct stream_in *in; | |
1154 | + int ret; | |
1155 | + | |
1156 | + *stream_in = NULL; | |
1157 | + | |
1158 | + /* Respond with a request for mono if a different format is given. */ | |
1159 | + if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) { | |
1160 | + config->channel_mask = AUDIO_CHANNEL_IN_MONO; | |
1161 | + return -EINVAL; | |
1162 | + } | |
1163 | + | |
1164 | + in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); | |
1165 | + if (!in) | |
1166 | + return -ENOMEM; | |
1167 | + | |
1168 | + in->stream.common.get_sample_rate = in_get_sample_rate; | |
1169 | + in->stream.common.set_sample_rate = in_set_sample_rate; | |
1170 | + in->stream.common.get_buffer_size = in_get_buffer_size; | |
1171 | + in->stream.common.get_channels = in_get_channels; | |
1172 | + in->stream.common.get_format = in_get_format; | |
1173 | + in->stream.common.set_format = in_set_format; | |
1174 | + in->stream.common.standby = in_standby; | |
1175 | + in->stream.common.dump = in_dump; | |
1176 | + in->stream.common.set_parameters = in_set_parameters; | |
1177 | + in->stream.common.get_parameters = in_get_parameters; | |
1178 | + in->stream.common.add_audio_effect = in_add_audio_effect; | |
1179 | + in->stream.common.remove_audio_effect = in_remove_audio_effect; | |
1180 | + in->stream.set_gain = in_set_gain; | |
1181 | + in->stream.read = in_read; | |
1182 | + in->stream.get_input_frames_lost = in_get_input_frames_lost; | |
1183 | + | |
1184 | + in->dev = adev; | |
1185 | + in->standby = true; | |
1186 | + in->requested_rate = config->sample_rate; | |
1187 | + in->pcm_config = &pcm_config_in; /* default PCM config */ | |
1188 | + | |
1189 | + *stream_in = &in->stream; | |
1190 | + return 0; | |
1191 | +} | |
1192 | + | |
1193 | +static void adev_close_input_stream(struct audio_hw_device *dev, | |
1194 | + struct audio_stream_in *stream) | |
1195 | +{ | |
1196 | + struct stream_in *in = (struct stream_in *)stream; | |
1197 | + | |
1198 | + in_standby(&stream->common); | |
1199 | + free(stream); | |
1200 | +} | |
1201 | + | |
1202 | +static int adev_dump(const audio_hw_device_t *device, int fd) | |
1203 | +{ | |
1204 | + return 0; | |
1205 | +} | |
1206 | + | |
1207 | +static int adev_close(hw_device_t *device) | |
1208 | +{ | |
1209 | + struct audio_device *adev = (struct audio_device *)device; | |
1210 | + | |
1211 | + audio_route_free(adev->ar); | |
1212 | + | |
1213 | + free(device); | |
1214 | + return 0; | |
1215 | +} | |
1216 | + | |
1217 | +static int adev_open(const hw_module_t* module, const char* name, | |
1218 | + hw_device_t** device) | |
1219 | +{ | |
1220 | + struct audio_device *adev; | |
1221 | + int ret; | |
1222 | + | |
1223 | + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) | |
1224 | + return -EINVAL; | |
1225 | + | |
1226 | + adev = calloc(1, sizeof(struct audio_device)); | |
1227 | + if (!adev) | |
1228 | + return -ENOMEM; | |
1229 | + | |
1230 | + adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; | |
1231 | + adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; | |
1232 | + adev->hw_device.common.module = (struct hw_module_t *) module; | |
1233 | + adev->hw_device.common.close = adev_close; | |
1234 | + | |
1235 | + adev->hw_device.init_check = adev_init_check; | |
1236 | + adev->hw_device.set_voice_volume = adev_set_voice_volume; | |
1237 | + adev->hw_device.set_master_volume = adev_set_master_volume; | |
1238 | + adev->hw_device.set_mode = adev_set_mode; | |
1239 | + adev->hw_device.set_mic_mute = adev_set_mic_mute; | |
1240 | + adev->hw_device.get_mic_mute = adev_get_mic_mute; | |
1241 | + adev->hw_device.set_parameters = adev_set_parameters; | |
1242 | + adev->hw_device.get_parameters = adev_get_parameters; | |
1243 | + adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; | |
1244 | + adev->hw_device.open_output_stream = adev_open_output_stream; | |
1245 | + adev->hw_device.close_output_stream = adev_close_output_stream; | |
1246 | + adev->hw_device.open_input_stream = adev_open_input_stream; | |
1247 | + adev->hw_device.close_input_stream = adev_close_input_stream; | |
1248 | + adev->hw_device.dump = adev_dump; | |
1249 | + | |
1250 | + adev->ar = audio_route_init(); | |
1251 | + adev->orientation = ORIENTATION_UNDEFINED; | |
1252 | + adev->out_device = AUDIO_DEVICE_OUT_SPEAKER; | |
1253 | + adev->in_device = AUDIO_DEVICE_IN_BUILTIN_MIC & ~AUDIO_DEVICE_BIT_IN; | |
1254 | + | |
1255 | + *device = &adev->hw_device.common; | |
1256 | + | |
1257 | + return 0; | |
1258 | +} | |
1259 | + | |
1260 | +static struct hw_module_methods_t hal_module_methods = { | |
1261 | + .open = adev_open, | |
1262 | +}; | |
1263 | + | |
1264 | +struct audio_module HAL_MODULE_INFO_SYM = { | |
1265 | + .common = { | |
1266 | + .tag = HARDWARE_MODULE_TAG, | |
1267 | + .module_api_version = AUDIO_MODULE_API_VERSION_0_1, | |
1268 | + .hal_api_version = HARDWARE_HAL_API_VERSION, | |
1269 | + .id = AUDIO_HARDWARE_MODULE_ID, | |
1270 | + .name = "Grouper audio HW HAL", | |
1271 | + .author = "The Android Open Source Project", | |
1272 | + .methods = &hal_module_methods, | |
1273 | + }, | |
1274 | +}; |
@@ -0,0 +1,68 @@ | ||
1 | +# Global configuration section: lists input and output devices always present on the device | |
2 | +# as well as the output device selected by default. | |
3 | +# Devices are designated by a string that corresponds to the enum in audio.h | |
4 | + | |
5 | +global_configuration { | |
6 | + attached_output_devices AUDIO_DEVICE_OUT_SPEAKER | |
7 | + default_output_device AUDIO_DEVICE_OUT_SPEAKER | |
8 | + attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC | |
9 | +} | |
10 | + | |
11 | +# audio hardware module section: contains descriptors for all audio hw modules present on the | |
12 | +# device. Each hw module node is named after the corresponding hw module library base name. | |
13 | +# For instance, "primary" corresponds to audio.primary.<device>.so. | |
14 | +# The "primary" module is mandatory and must include at least one output with | |
15 | +# AUDIO_OUTPUT_FLAG_PRIMARY flag. | |
16 | +# Each module descriptor contains one or more output profile descriptors and zero or more | |
17 | +# input profile descriptors. Each profile lists all the parameters supported by a given output | |
18 | +# or input stream category. | |
19 | +# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding | |
20 | +# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". | |
21 | + | |
22 | +audio_hw_modules { | |
23 | + primary { | |
24 | + outputs { | |
25 | + primary { | |
26 | + sampling_rates 44100 | |
27 | + channel_masks AUDIO_CHANNEL_OUT_STEREO | |
28 | + formats AUDIO_FORMAT_PCM_16_BIT | |
29 | + devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO | |
30 | + flags AUDIO_OUTPUT_FLAG_PRIMARY | |
31 | + } | |
32 | + } | |
33 | + inputs { | |
34 | + primary { | |
35 | + sampling_rates 8000|11025|16000|22050|24000|32000|44100|48000 | |
36 | + channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO | |
37 | + formats AUDIO_FORMAT_PCM_16_BIT | |
38 | + devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET|AUDIO_DEVICE_IN_WIRED_HEADSET | |
39 | + } | |
40 | + } | |
41 | + } | |
42 | + a2dp { | |
43 | + outputs { | |
44 | + a2dp { | |
45 | + sampling_rates 44100 | |
46 | + channel_masks AUDIO_CHANNEL_OUT_STEREO | |
47 | + formats AUDIO_FORMAT_PCM_16_BIT | |
48 | + devices AUDIO_DEVICE_OUT_ALL_A2DP | |
49 | + } | |
50 | + } | |
51 | + } | |
52 | + usb { | |
53 | + outputs { | |
54 | + usb_accessory { | |
55 | + sampling_rates 44100 | |
56 | + channel_masks AUDIO_CHANNEL_OUT_STEREO | |
57 | + formats AUDIO_FORMAT_PCM_16_BIT | |
58 | + devices AUDIO_DEVICE_OUT_USB_ACCESSORY | |
59 | + } | |
60 | + usb_device { | |
61 | + sampling_rates 44100 | |
62 | + channel_masks AUDIO_CHANNEL_OUT_STEREO | |
63 | + formats AUDIO_FORMAT_PCM_16_BIT | |
64 | + devices AUDIO_DEVICE_OUT_USB_DEVICE | |
65 | + } | |
66 | + } | |
67 | + } | |
68 | +} |
@@ -0,0 +1,494 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2012 The Android Open Source Project | |
3 | + * Inspired by TinyHW, written by Mark Brown at Wolfson Micro | |
4 | + * | |
5 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + * you may not use this file except in compliance with the License. | |
7 | + * You may obtain a copy of the License at | |
8 | + * | |
9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + * | |
11 | + * Unless required by applicable law or agreed to in writing, software | |
12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + * See the License for the specific language governing permissions and | |
15 | + * limitations under the License. | |
16 | + */ | |
17 | + | |
18 | +#define LOG_TAG "audio_hw_primary" | |
19 | +/*#define LOG_NDEBUG 0*/ | |
20 | + | |
21 | +#include <errno.h> | |
22 | +#include <expat.h> | |
23 | +#include <stdbool.h> | |
24 | +#include <stdio.h> | |
25 | + | |
26 | +#include <cutils/log.h> | |
27 | + | |
28 | +#include <tinyalsa/asoundlib.h> | |
29 | + | |
30 | +#define BUF_SIZE 1024 | |
31 | +#define MIXER_XML_PATH "/system/etc/mixer_paths.xml" | |
32 | +#define INITIAL_MIXER_PATH_SIZE 8 | |
33 | + | |
34 | +#define MIXER_CARD 1 | |
35 | + | |
36 | +struct mixer_state { | |
37 | + struct mixer_ctl *ctl; | |
38 | + int old_value; | |
39 | + int new_value; | |
40 | + int reset_value; | |
41 | +}; | |
42 | + | |
43 | +struct mixer_setting { | |
44 | + struct mixer_ctl *ctl; | |
45 | + int value; | |
46 | +}; | |
47 | + | |
48 | +struct mixer_path { | |
49 | + char *name; | |
50 | + unsigned int size; | |
51 | + unsigned int length; | |
52 | + struct mixer_setting *setting; | |
53 | +}; | |
54 | + | |
55 | +struct audio_route { | |
56 | + struct mixer *mixer; | |
57 | + unsigned int num_mixer_ctls; | |
58 | + struct mixer_state *mixer_state; | |
59 | + | |
60 | + unsigned int mixer_path_size; | |
61 | + unsigned int num_mixer_paths; | |
62 | + struct mixer_path *mixer_path; | |
63 | +}; | |
64 | + | |
65 | +struct config_parse_state { | |
66 | + struct audio_route *ar; | |
67 | + struct mixer_path *path; | |
68 | + int level; | |
69 | +}; | |
70 | + | |
71 | +/* path functions */ | |
72 | + | |
73 | +static void path_free(struct audio_route *ar) | |
74 | +{ | |
75 | + unsigned int i; | |
76 | + | |
77 | + for (i = 0; i < ar->num_mixer_paths; i++) { | |
78 | + if (ar->mixer_path[i].name) | |
79 | + free(ar->mixer_path[i].name); | |
80 | + if (ar->mixer_path[i].setting) | |
81 | + free(ar->mixer_path[i].setting); | |
82 | + } | |
83 | + free(ar->mixer_path); | |
84 | +} | |
85 | + | |
86 | +static struct mixer_path *path_get_by_name(struct audio_route *ar, | |
87 | + const char *name) | |
88 | +{ | |
89 | + unsigned int i; | |
90 | + | |
91 | + for (i = 0; i < ar->num_mixer_paths; i++) | |
92 | + if (strcmp(ar->mixer_path[i].name, name) == 0) | |
93 | + return &ar->mixer_path[i]; | |
94 | + | |
95 | + return NULL; | |
96 | +} | |
97 | + | |
98 | +static struct mixer_path *path_create(struct audio_route *ar, const char *name) | |
99 | +{ | |
100 | + struct mixer_path *new_mixer_path = NULL; | |
101 | + | |
102 | + if (path_get_by_name(ar, name)) { | |
103 | + ALOGE("Path name '%s' already exists", name); | |
104 | + return NULL; | |
105 | + } | |
106 | + | |
107 | + /* check if we need to allocate more space for mixer paths */ | |
108 | + if (ar->mixer_path_size <= ar->num_mixer_paths) { | |
109 | + if (ar->mixer_path_size == 0) | |
110 | + ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE; | |
111 | + else | |
112 | + ar->mixer_path_size *= 2; | |
113 | + | |
114 | + new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size * | |
115 | + sizeof(struct mixer_path)); | |
116 | + if (new_mixer_path == NULL) { | |
117 | + ALOGE("Unable to allocate more paths"); | |
118 | + return NULL; | |
119 | + } else { | |
120 | + ar->mixer_path = new_mixer_path; | |
121 | + } | |
122 | + } | |
123 | + | |
124 | + /* initialise the new mixer path */ | |
125 | + ar->mixer_path[ar->num_mixer_paths].name = strdup(name); | |
126 | + ar->mixer_path[ar->num_mixer_paths].size = 0; | |
127 | + ar->mixer_path[ar->num_mixer_paths].length = 0; | |
128 | + ar->mixer_path[ar->num_mixer_paths].setting = NULL; | |
129 | + | |
130 | + /* return the mixer path just added, then increment number of them */ | |
131 | + return &ar->mixer_path[ar->num_mixer_paths++]; | |
132 | +} | |
133 | + | |
134 | +static bool path_setting_exists(struct mixer_path *path, | |
135 | + struct mixer_setting *setting) | |
136 | +{ | |
137 | + unsigned int i; | |
138 | + | |
139 | + for (i = 0; i < path->length; i++) | |
140 | + if (path->setting[i].ctl == setting->ctl) | |
141 | + return true; | |
142 | + | |
143 | + return false; | |
144 | +} | |
145 | + | |
146 | +static int path_add_setting(struct mixer_path *path, | |
147 | + struct mixer_setting *setting) | |
148 | +{ | |
149 | + struct mixer_setting *new_path_setting; | |
150 | + | |
151 | + if (path_setting_exists(path, setting)) { | |
152 | + ALOGE("Duplicate path setting '%s'", | |
153 | + mixer_ctl_get_name(setting->ctl)); | |
154 | + return -1; | |
155 | + } | |
156 | + | |
157 | + /* check if we need to allocate more space for path settings */ | |
158 | + if (path->size <= path->length) { | |
159 | + if (path->size == 0) | |
160 | + path->size = INITIAL_MIXER_PATH_SIZE; | |
161 | + else | |
162 | + path->size *= 2; | |
163 | + | |
164 | + new_path_setting = realloc(path->setting, | |
165 | + path->size * sizeof(struct mixer_setting)); | |
166 | + if (new_path_setting == NULL) { | |
167 | + ALOGE("Unable to allocate more path settings"); | |
168 | + return -1; | |
169 | + } else { | |
170 | + path->setting = new_path_setting; | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + /* initialise the new path setting */ | |
175 | + path->setting[path->length].ctl = setting->ctl; | |
176 | + path->setting[path->length].value = setting->value; | |
177 | + path->length++; | |
178 | + | |
179 | + return 0; | |
180 | +} | |
181 | + | |
182 | +static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path) | |
183 | +{ | |
184 | + unsigned int i; | |
185 | + | |
186 | + for (i = 0; i < sub_path->length; i++) | |
187 | + if (path_add_setting(path, &sub_path->setting[i]) < 0) | |
188 | + return -1; | |
189 | + | |
190 | + return 0; | |
191 | +} | |
192 | + | |
193 | +static void path_print(struct mixer_path *path) | |
194 | +{ | |
195 | + unsigned int i; | |
196 | + | |
197 | + ALOGV("Path: %s, length: %d", path->name, path->length); | |
198 | + for (i = 0; i < path->length; i++) | |
199 | + ALOGV(" %d: %s -> %d", i, mixer_ctl_get_name(path->setting[i].ctl), | |
200 | + path->setting[i].value); | |
201 | +} | |
202 | + | |
203 | +static int path_apply(struct audio_route *ar, struct mixer_path *path) | |
204 | +{ | |
205 | + unsigned int i; | |
206 | + unsigned int j; | |
207 | + | |
208 | + for (i = 0; i < path->length; i++) { | |
209 | + struct mixer_ctl *ctl = path->setting[i].ctl; | |
210 | + | |
211 | + /* locate the mixer ctl in the list */ | |
212 | + for (j = 0; j < ar->num_mixer_ctls; j++) { | |
213 | + if (ar->mixer_state[j].ctl == ctl) | |
214 | + break; | |
215 | + } | |
216 | + | |
217 | + /* apply the new value */ | |
218 | + ar->mixer_state[j].new_value = path->setting[i].value; | |
219 | + } | |
220 | + | |
221 | + return 0; | |
222 | +} | |
223 | + | |
224 | +/* mixer helper function */ | |
225 | +static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string) | |
226 | +{ | |
227 | + unsigned int i; | |
228 | + | |
229 | + /* Search the enum strings for a particular one */ | |
230 | + for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) { | |
231 | + if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0) | |
232 | + break; | |
233 | + } | |
234 | + | |
235 | + return i; | |
236 | +} | |
237 | + | |
238 | +static void start_tag(void *data, const XML_Char *tag_name, | |
239 | + const XML_Char **attr) | |
240 | +{ | |
241 | + const XML_Char *attr_name = NULL; | |
242 | + const XML_Char *attr_value = NULL; | |
243 | + struct config_parse_state *state = data; | |
244 | + struct audio_route *ar = state->ar; | |
245 | + unsigned int i; | |
246 | + struct mixer_ctl *ctl; | |
247 | + int value; | |
248 | + struct mixer_setting mixer_setting; | |
249 | + | |
250 | + /* Get name, type and value attributes (these may be empty) */ | |
251 | + for (i = 0; attr[i]; i += 2) { | |
252 | + if (strcmp(attr[i], "name") == 0) | |
253 | + attr_name = attr[i + 1]; | |
254 | + else if (strcmp(attr[i], "value") == 0) | |
255 | + attr_value = attr[i + 1]; | |
256 | + } | |
257 | + | |
258 | + /* Look at tags */ | |
259 | + if (strcmp(tag_name, "path") == 0) { | |
260 | + if (attr_name == NULL) { | |
261 | + ALOGE("Unnamed path!"); | |
262 | + } else { | |
263 | + if (state->level == 1) { | |
264 | + /* top level path: create and stash the path */ | |
265 | + state->path = path_create(ar, (char *)attr_name); | |
266 | + } else { | |
267 | + /* nested path */ | |
268 | + struct mixer_path *sub_path = path_get_by_name(ar, attr_name); | |
269 | + path_add_path(state->path, sub_path); | |
270 | + } | |
271 | + } | |
272 | + } | |
273 | + | |
274 | + else if (strcmp(tag_name, "ctl") == 0) { | |
275 | + /* Obtain the mixer ctl and value */ | |
276 | + ctl = mixer_get_ctl_by_name(ar->mixer, attr_name); | |
277 | + switch (mixer_ctl_get_type(ctl)) { | |
278 | + case MIXER_CTL_TYPE_BOOL: | |
279 | + case MIXER_CTL_TYPE_INT: | |
280 | + value = atoi((char *)attr_value); | |
281 | + break; | |
282 | + case MIXER_CTL_TYPE_ENUM: | |
283 | + value = mixer_enum_string_to_value(ctl, (char *)attr_value); | |
284 | + break; | |
285 | + default: | |
286 | + value = 0; | |
287 | + break; | |
288 | + } | |
289 | + | |
290 | + if (state->level == 1) { | |
291 | + /* top level ctl (initial setting) */ | |
292 | + | |
293 | + /* locate the mixer ctl in the list */ | |
294 | + for (i = 0; i < ar->num_mixer_ctls; i++) { | |
295 | + if (ar->mixer_state[i].ctl == ctl) | |
296 | + break; | |
297 | + } | |
298 | + | |
299 | + /* apply the new value */ | |
300 | + ar->mixer_state[i].new_value = value; | |
301 | + } else { | |
302 | + /* nested ctl (within a path) */ | |
303 | + mixer_setting.ctl = ctl; | |
304 | + mixer_setting.value = value; | |
305 | + path_add_setting(state->path, &mixer_setting); | |
306 | + } | |
307 | + } | |
308 | + | |
309 | + state->level++; | |
310 | +} | |
311 | + | |
312 | +static void end_tag(void *data, const XML_Char *tag_name) | |
313 | +{ | |
314 | + struct config_parse_state *state = data; | |
315 | + | |
316 | + state->level--; | |
317 | +} | |
318 | + | |
319 | +static int alloc_mixer_state(struct audio_route *ar) | |
320 | +{ | |
321 | + unsigned int i; | |
322 | + | |
323 | + ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer); | |
324 | + ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state)); | |
325 | + if (!ar->mixer_state) | |
326 | + return -1; | |
327 | + | |
328 | + for (i = 0; i < ar->num_mixer_ctls; i++) { | |
329 | + ar->mixer_state[i].ctl = mixer_get_ctl(ar->mixer, i); | |
330 | + /* only get value 0, assume multiple ctl values are the same */ | |
331 | + ar->mixer_state[i].old_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0); | |
332 | + ar->mixer_state[i].new_value = ar->mixer_state[i].old_value; | |
333 | + } | |
334 | + | |
335 | + return 0; | |
336 | +} | |
337 | + | |
338 | +static void free_mixer_state(struct audio_route *ar) | |
339 | +{ | |
340 | + free(ar->mixer_state); | |
341 | + ar->mixer_state = NULL; | |
342 | +} | |
343 | + | |
344 | +void update_mixer_state(struct audio_route *ar) | |
345 | +{ | |
346 | + unsigned int i; | |
347 | + unsigned int j; | |
348 | + | |
349 | + for (i = 0; i < ar->num_mixer_ctls; i++) { | |
350 | + /* if the value has changed, update the mixer */ | |
351 | + if (ar->mixer_state[i].old_value != ar->mixer_state[i].new_value) { | |
352 | + /* set all ctl values the same */ | |
353 | + for (j = 0; j < mixer_ctl_get_num_values(ar->mixer_state[i].ctl); j++) | |
354 | + mixer_ctl_set_value(ar->mixer_state[i].ctl, j, | |
355 | + ar->mixer_state[i].new_value); | |
356 | + ar->mixer_state[i].old_value = ar->mixer_state[i].new_value; | |
357 | + } | |
358 | + } | |
359 | +} | |
360 | + | |
361 | +/* saves the current state of the mixer, for resetting all controls */ | |
362 | +static void save_mixer_state(struct audio_route *ar) | |
363 | +{ | |
364 | + unsigned int i; | |
365 | + | |
366 | + for (i = 0; i < ar->num_mixer_ctls; i++) { | |
367 | + /* only get value 0, assume multiple ctl values are the same */ | |
368 | + ar->mixer_state[i].reset_value = mixer_ctl_get_value(ar->mixer_state[i].ctl, 0); | |
369 | + } | |
370 | +} | |
371 | + | |
372 | +/* this resets all mixer settings to the saved values */ | |
373 | +void reset_mixer_state(struct audio_route *ar) | |
374 | +{ | |
375 | + unsigned int i; | |
376 | + | |
377 | + /* load all of the saved values */ | |
378 | + for (i = 0; i < ar->num_mixer_ctls; i++) | |
379 | + ar->mixer_state[i].new_value = ar->mixer_state[i].reset_value; | |
380 | +} | |
381 | + | |
382 | +void audio_route_apply_path(struct audio_route *ar, const char *name) | |
383 | +{ | |
384 | + struct mixer_path *path; | |
385 | + | |
386 | + if (!ar) { | |
387 | + ALOGE("invalid audio_route"); | |
388 | + return; | |
389 | + } | |
390 | + | |
391 | + path = path_get_by_name(ar, name); | |
392 | + if (!path) { | |
393 | + ALOGE("unable to find path '%s'", name); | |
394 | + return; | |
395 | + } | |
396 | + | |
397 | + path_apply(ar, path); | |
398 | +} | |
399 | + | |
400 | +struct audio_route *audio_route_init(void) | |
401 | +{ | |
402 | + struct config_parse_state state; | |
403 | + XML_Parser parser; | |
404 | + FILE *file; | |
405 | + int bytes_read; | |
406 | + void *buf; | |
407 | + int i; | |
408 | + struct mixer_path *path; | |
409 | + struct audio_route *ar; | |
410 | + | |
411 | + ar = calloc(1, sizeof(struct audio_route)); | |
412 | + if (!ar) | |
413 | + goto err_calloc; | |
414 | + | |
415 | + ar->mixer = mixer_open(MIXER_CARD); | |
416 | + if (!ar->mixer) { | |
417 | + ALOGE("Unable to open the mixer, aborting."); | |
418 | + goto err_mixer_open; | |
419 | + } | |
420 | + | |
421 | + ar->mixer_path = NULL; | |
422 | + ar->mixer_path_size = 0; | |
423 | + ar->num_mixer_paths = 0; | |
424 | + | |
425 | + /* allocate space for and read current mixer settings */ | |
426 | + if (alloc_mixer_state(ar) < 0) | |
427 | + goto err_mixer_state; | |
428 | + | |
429 | + file = fopen(MIXER_XML_PATH, "r"); | |
430 | + if (!file) { | |
431 | + ALOGE("Failed to open %s", MIXER_XML_PATH); | |
432 | + goto err_fopen; | |
433 | + } | |
434 | + | |
435 | + parser = XML_ParserCreate(NULL); | |
436 | + if (!parser) { | |
437 | + ALOGE("Failed to create XML parser"); | |
438 | + goto err_parser_create; | |
439 | + } | |
440 | + | |
441 | + memset(&state, 0, sizeof(state)); | |
442 | + state.ar = ar; | |
443 | + XML_SetUserData(parser, &state); | |
444 | + XML_SetElementHandler(parser, start_tag, end_tag); | |
445 | + | |
446 | + for (;;) { | |
447 | + buf = XML_GetBuffer(parser, BUF_SIZE); | |
448 | + if (buf == NULL) | |
449 | + goto err_parse; | |
450 | + | |
451 | + bytes_read = fread(buf, 1, BUF_SIZE, file); | |
452 | + if (bytes_read < 0) | |
453 | + goto err_parse; | |
454 | + | |
455 | + if (XML_ParseBuffer(parser, bytes_read, | |
456 | + bytes_read == 0) == XML_STATUS_ERROR) { | |
457 | + ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH); | |
458 | + goto err_parse; | |
459 | + } | |
460 | + | |
461 | + if (bytes_read == 0) | |
462 | + break; | |
463 | + } | |
464 | + | |
465 | + /* apply the initial mixer values, and save them so we can reset the | |
466 | + mixer to the original values */ | |
467 | + update_mixer_state(ar); | |
468 | + save_mixer_state(ar); | |
469 | + | |
470 | + XML_ParserFree(parser); | |
471 | + fclose(file); | |
472 | + return ar; | |
473 | + | |
474 | +err_parse: | |
475 | + XML_ParserFree(parser); | |
476 | +err_parser_create: | |
477 | + fclose(file); | |
478 | +err_fopen: | |
479 | + free_mixer_state(ar); | |
480 | +err_mixer_state: | |
481 | + mixer_close(ar->mixer); | |
482 | +err_mixer_open: | |
483 | + free(ar); | |
484 | + ar = NULL; | |
485 | +err_calloc: | |
486 | + return NULL; | |
487 | +} | |
488 | + | |
489 | +void audio_route_free(struct audio_route *ar) | |
490 | +{ | |
491 | + free_mixer_state(ar); | |
492 | + mixer_close(ar->mixer); | |
493 | + free(ar); | |
494 | +} |
@@ -0,0 +1,33 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2012 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef AUDIO_ROUTE_H | |
18 | +#define AUDIO_ROUTE_H | |
19 | + | |
20 | +/* Initialises and frees the audio routes */ | |
21 | +struct audio_route *audio_route_init(void); | |
22 | +void audio_route_free(struct audio_route *ar); | |
23 | + | |
24 | +/* Applies an audio route path by name */ | |
25 | +void audio_route_apply_path(struct audio_route *ar, const char *name); | |
26 | + | |
27 | +/* Resets the mixer back to its initial state */ | |
28 | +void reset_mixer_state(struct audio_route *ar); | |
29 | + | |
30 | +/* Updates the mixer with any changed values */ | |
31 | +void update_mixer_state(struct audio_route *ar); | |
32 | + | |
33 | +#endif |