Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: chrome/browser/chromeos/audio_mixer_alsa.cc

Issue 5859003: Add ALSA support to volume keys (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/chrome/browser/chromeos
Patch Set: cleanups Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/audio_mixer_alsa.h"
6
7 #include "base/logging.h"
8 #include "base/task.h"
9
10 namespace chromeos {
11
12 // Connect to the ALSA mixer using their simple element API. Init is performed
13 // asynchronously on the worker thread.
scherkus (not reviewing) 2010/12/22 22:31:58 I think it'd be good to expand this to explain the
14
15 // TODO(davej): Serialize volume/mute to preserve settings when restarting.
16
17 typedef long alsa_long_t; // 'long' is required for ALSA API calls.
18
19 namespace {
20 const char* kMasterVolume = {"Master"};
scherkus (not reviewing) 2010/12/22 22:31:58 nits: - no need to have this indented - also do
davejcool 2010/12/23 03:09:02 :-) Microsoft + 1990's "C" = {"Funny Braces"}
21 const char* kPCMVolume = {"PCM"};
22 } // namespace
23
24 AudioMixerAlsa::AudioMixerAlsa()
25 : min_volume_(-90),
zhurunz 2010/12/22 22:21:26 Define a const for this.
scherkus (not reviewing) 2010/12/22 22:31:58 magic number alert! do we have a constant for thi
davejcool 2010/12/23 03:09:02 We do now!
26 max_volume_(0),
27 save_volume_(0),
28 mixer_state_lock_(),
scherkus (not reviewing) 2010/12/22 22:31:58 nit: no need to list variables that have default c
29 mixer_state_(UNINITIALIZED),
30 alsa_mixer_(NULL),
31 elem_master_(NULL),
32 elem_pcm_(NULL),
33 thread_(NULL) {
scherkus (not reviewing) 2010/12/22 22:31:58 nit: no need to list variables that have default c
34 }
35
36 AudioMixerAlsa::~AudioMixerAlsa() {
37 {
38 AutoLock lock(mixer_state_lock_);
39 mixer_state_ = SHUTTING_DOWN;
40 FreeAlsaMixer();
41 }
42
43 if (thread_ != NULL) {
44 thread_->Stop();
45 thread_.reset();
46 }
47
48 {
49 AutoLock lock(mixer_state_lock_);
scherkus (not reviewing) 2010/12/22 22:31:58 nit: over indented do we need to lock + update st
50 mixer_state_ = UNINITIALIZED;
51 }
52 }
53
54 bool AudioMixerAlsa::Init(InitDoneCallback* callback) {
55 if (!InitThread())
56 return false;
57
58 // Post the task of starting up, which can block for 200-500ms,
59 // so best not to do it on the caller's thread.
60 thread_->message_loop()->PostTask(FROM_HERE,
scherkus (not reviewing) 2010/12/22 22:31:58 we only seem to use this thread for doing initiali
davejcool 2010/12/23 03:09:02 I just wanted the init to happen away from everyth
61 NewRunnableMethod(this, &AudioMixerAlsa::DoInit, callback));
scherkus (not reviewing) 2010/12/22 22:31:58 nit: indent by extra 2 spaces
62 return true;
63 }
64
65 bool AudioMixerAlsa::InitSync() {
66 if (!InitThread())
67 return false;
68 return InitializeAlsaMixer();
69 }
70
71 double AudioMixerAlsa::GetVolumeDb() const {
72 if (mixer_state_ != READY)
73 return kSilenceDb;
74
75 float vol_total = 0;
76 GetElementVolume(elem_master_, &vol_total);
77
78 float vol_pcm = 0;
79 if (elem_pcm_ && (GetElementVolume(elem_pcm_, &vol_pcm)))
80 vol_total += vol_pcm;
81
82 return vol_total;
83 }
84
85 void AudioMixerAlsa::GetVolumeLimits(double* vol_min, double* vol_max) {
86 if (!MixerReady())
87 return;
88 if (vol_min)
89 *vol_min = min_volume_;
90 if (vol_max)
91 *vol_max = max_volume_;
92 }
93
94 void AudioMixerAlsa::SetVolumeDb(double vol_db) {
95 if (!MixerReady())
96 return;
97 float actual_vol = 0;
98
99 // If a PCM volume slider exists, then first set the Master volume to the
100 // nearest volume >= requested volume, then adjust PCM volume down to get
101 // closer to the requested volume. This allows for going to a quieter volume
102 // than the Master alone would allow, as well as give a finer resolution
103 // to the overall volume.
104
105 if (elem_pcm_) {
106 SetElementVolume(elem_master_, vol_db, &actual_vol, 0.9999f);
107 SetElementVolume(elem_pcm_, vol_db - actual_vol, NULL, 0.5f);
108 } else {
109 SetElementVolume(elem_master_, vol_db, NULL, 0.5f);
110 }
111 }
112
113 bool AudioMixerAlsa::IsMute() const {
114 if (!MixerReady())
115 return false;
116 int switched = 0;
117 GetElementSwitched(elem_master_, &switched);
118 return (switched) ? false : true;
scherkus (not reviewing) 2010/12/22 22:31:58 instead of having callees do the int<->bool conver
davejcool 2010/12/23 03:09:02 Yup, removing the 'switched' language from Get/Set
119 }
120
121 void AudioMixerAlsa::SetMute(bool mute) {
122 if (!MixerReady())
123 return;
124 int new_value = mute ? 0 : 1;
scherkus (not reviewing) 2010/12/22 22:31:58 ditto
125
126 // Set volume to minimum on mute, since switching the element off does not
127 // always mute as it should.
128
129 // TODO(davej): Setting volume to minimum can be removed once switching the
130 // element off can be guaranteed to work.
131
132 int old_value;
133 GetElementSwitched(elem_master_, &old_value);
134
135 if (old_value != new_value) {
136 if (!new_value) {
137 save_volume_ = GetVolumeDb();
138 SetVolumeDb(min_volume_);
139 } else {
140 SetVolumeDb(save_volume_);
141 }
142 }
143
144 SetElementSwitched(elem_master_, new_value);
145 if (elem_pcm_)
146 SetElementSwitched(elem_pcm_, new_value);
147 }
148
149 AudioMixerBase::State AudioMixerAlsa::CheckState() const {
150 AutoLock lock(mixer_state_lock_);
151 // If we think it's ready, verify it is actually so.
152 if ((mixer_state_ == READY) &&
scherkus (not reviewing) 2010/12/22 22:31:58 nit: condition can fit on one line
153 (alsa_mixer_ == NULL))
zhurunz 2010/12/22 22:21:26 can be in one line.
154 mixer_state_ = IN_ERROR;
155 return mixer_state_;
156 }
157
158 ////////////////////////////////////////////////////////////////////////////////
159 // Private functions follow
160
161 void AudioMixerAlsa::DoInit(InitDoneCallback* callback) {
162 bool success = InitializeAlsaMixer();
163
164 if (callback) {
165 callback->Run(success);
166 delete callback;
167 }
168 }
169
170 bool AudioMixerAlsa::InitThread() {
171 AutoLock lock(mixer_state_lock_);
172
173 if (mixer_state_ != UNINITIALIZED)
174 return false;
175
176 if (thread_ == NULL) {
177 thread_.reset(new base::Thread("AudioMixerAlsa"));
178 if (!thread_->Start()) {
179 thread_.reset();
180 return false;
181 }
182 }
183
184 mixer_state_ = INITIALIZING;
185 return true;
186 }
187
188 bool AudioMixerAlsa::InitializeAlsaMixer() {
189 AutoLock lock(mixer_state_lock_);
scherkus (not reviewing) 2010/12/22 22:31:58 sanity check: did you intend to lock for this enti
davejcool 2010/12/23 03:09:02 In this case I did intend this. The initializatio
190 if (mixer_state_ != INITIALIZING)
191 return false;
192
193 // ALSA hardware devices start at 0
scherkus (not reviewing) 2010/12/22 22:31:58 nit: end comment w/ period
194 int device_id_ = -1;
195 if ((snd_card_next(&device_id_) < 0) || (device_id_ < 0)) {
scherkus (not reviewing) 2010/12/22 22:31:58 so this selects the first ALSA device found on the
davejcool 2010/12/23 03:09:02 Oops... this was left over from earlier testing.
196 LOG(ERROR) << "No ALSA Soundcards Found";
197 return false;
198 }
199
200 int err;
201 snd_mixer_t* handle = NULL;
scherkus (not reviewing) 2010/12/22 22:31:58 you may want to consider defining a scoped_ptr_mal
davejcool 2010/12/23 03:09:02 Thanks, I'll have learn more about that one.
202 char card[16];
203
204 snprintf(card, sizeof(card), "hw:%i", device_id_);
scherkus (not reviewing) 2010/12/22 22:31:58 nit: use base/stringprintf.h, std::string and c_st
205
206 if ((err = snd_mixer_open(&handle, 0)) < 0) {
207 LOG(ERROR) << "ALSA mixer " << card << " open error: " << snd_strerror(err);
208 return NULL;
209 }
zhurunz 2010/12/22 22:21:26 return false. Same below.
210
211 if ((err = snd_mixer_attach(handle, card)) < 0) {
212 LOG(ERROR) << "ALSA Attach to card " << card << " failed: "
213 << snd_strerror(err);
214 snd_mixer_close(handle);
215 return NULL;
216 }
217
218 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
219 LOG(ERROR) << "ALSA mixer register error: " << snd_strerror(err);
220 snd_mixer_close(handle);
221 return NULL;
222 }
223
224 err = snd_mixer_load(handle);
225 if (err < 0) {
scherkus (not reviewing) 2010/12/22 22:31:58 nit: for consistency might as well put snd_mixer_l
226 LOG(ERROR) << "ALSA mixer " << card << " load error: %s"
227 << snd_strerror(err);
228 snd_mixer_close(handle);
229 return NULL;
230 }
231
232 VLOG(1) << "Opened ALSA mixer " << card << " OK";
233
234 elem_master_ = FindElementWithName(handle, kMasterVolume);
235 if (elem_master_) {
236 alsa_long_t long_lo, long_hi;
237 snd_mixer_selem_get_playback_dB_range(elem_master_, &long_lo, &long_hi);
238 min_volume_ = static_cast<double>(long_lo) / 100.0;
239 max_volume_ = static_cast<double>(long_hi) / 100.0;
240 }
241
242 elem_pcm_ = FindElementWithName(handle, kPCMVolume);
243 if (elem_pcm_) {
244 alsa_long_t long_lo, long_hi;
245 snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi);
246 min_volume_ += static_cast<double>(long_lo) / 100.0;
247 max_volume_ += static_cast<double>(long_hi) / 100.0;
248 }
249
250 VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to "
251 << max_volume_ << " dB";
252
253 alsa_mixer_ = handle;
254 mixer_state_ = READY;
255 return true;
256 }
257
258 void AudioMixerAlsa::FreeAlsaMixer() {
259 if (alsa_mixer_) {
260 snd_mixer_close(alsa_mixer_);
261 alsa_mixer_ = NULL;
262 }
263 }
264
265 snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName(snd_mixer_t* handle,
scherkus (not reviewing) 2010/12/22 22:31:58 nit: these two arguments should be dropped to next
266 const char* element_name) const {
267 snd_mixer_selem_id_t* sid;
268 snd_mixer_selem_id_alloca(&sid);
269 snd_mixer_selem_id_set_index(sid, 0);
270 snd_mixer_selem_id_set_name(sid, element_name);
271 snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
272 if (!elem) {
273 LOG(ERROR) << "ALSA unable to find simple control "
274 << snd_mixer_selem_id_get_name(sid);
275 }
276 return elem;
277 }
278
279 bool AudioMixerAlsa::GetElementVolume(snd_mixer_elem_t* elem,
280 float* current_vol) const {
281 alsa_long_t long_vol;
282 snd_mixer_selem_get_playback_dB(elem, (snd_mixer_selem_channel_id_t)0,
scherkus (not reviewing) 2010/12/22 22:31:58 no c-style casts
283 &long_vol);
284 *current_vol = static_cast<float>(long_vol) / 100.0f;
285
286 return true;
287 }
288
289 bool AudioMixerAlsa::SetElementVolume(snd_mixer_elem_t* elem,
290 float new_vol,
291 float* actual_vol,
292 float rounding_bias) {
293 alsa_long_t vol_lo, vol_hi;
294 alsa_long_t db_lo_int, db_hi_int;
zhurunz 2010/12/22 22:21:26 Better initialize them just in case those snd_xxx
295 snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi);
296 snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int);
297 float db_lo = static_cast<float>(db_lo_int) / 100.0f;
298 float db_hi = static_cast<float>(db_hi_int) / 100.0f;
299 float db_step = static_cast<float>(db_hi - db_lo) / (vol_hi - vol_lo);
300
zhurunz 2010/12/22 22:21:26 check vol_hi != vol_lo
301 if (new_vol < db_lo)
302 new_vol = db_lo;
303
304 alsa_long_t value = static_cast<alsa_long_t>(rounding_bias +
305 (new_vol - db_lo) / db_step) + vol_lo;
306 snd_mixer_selem_set_playback_volume_all(elem, value);
307
308 VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem)
309 << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo
310 << " dB";
311
312 if (actual_vol) {
313 alsa_long_t volume;
314 snd_mixer_selem_get_playback_volume(elem, (snd_mixer_selem_channel_id_t)0,
315 &volume);
316 *actual_vol = db_lo + (volume - vol_lo) * db_step;
317
318 VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem)
319 << " now " << *actual_vol << " dB";
320 }
321 return true;
322 }
323
324 bool AudioMixerAlsa::GetElementSwitched(snd_mixer_elem_t* elem,
scherkus (not reviewing) 2010/12/22 22:31:58 this function always return true... maybe it shoul
325 int * switched) const {
zhurunz 2010/12/22 22:21:26 space after *
326 snd_mixer_selem_get_playback_switch(elem, (snd_mixer_selem_channel_id_t)0,
zhurunz 2010/12/22 22:21:26 use static_cast to be consistent.
327 switched);
328 return true;
329 }
330
331 bool AudioMixerAlsa::SetElementSwitched(snd_mixer_elem_t* elem,
scherkus (not reviewing) 2010/12/22 22:31:58 this function always return true.. perhaps it shou
davejcool 2010/12/23 03:09:02 Ahh, of course! In a previous incarnation the fun
332 int enabled) {
333 snd_mixer_selem_set_playback_switch_all(elem, enabled);
334
335 VLOG(1) << "Set playback switch" << snd_mixer_selem_get_name(elem)
zhurunz 2010/12/22 22:21:26 space here after ' switch'?
336 << " to " << enabled;
337 return true;
338 }
339
340 bool AudioMixerAlsa::MixerReady() const {
341 AutoLock lock(mixer_state_lock_);
scherkus (not reviewing) 2010/12/22 22:31:58 nit: over indented
342 return (mixer_state_ == READY);
343 }
344
345 } // namespace chromeos
346
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698