VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/AudioAdapterImpl.cpp@ 68485

Last change on this file since 68485 was 68485, checked in by vboxsync, 7 years ago

Audio: Implemented ability to enable / disable audio input / output on-the-fly via API.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: AudioAdapterImpl.cpp 68485 2017-08-21 13:48:12Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "AudioAdapterImpl.h"
20#include "MachineImpl.h"
21
22#include <iprt/cpp/utils.h>
23
24#include <VBox/settings.h>
25
26#include "AutoStateDep.h"
27#include "AutoCaller.h"
28#include "Logging.h"
29
30
31// constructor / destructor
32/////////////////////////////////////////////////////////////////////////////
33
34AudioAdapter::AudioAdapter()
35 : mParent(NULL)
36{
37}
38
39AudioAdapter::~AudioAdapter()
40{
41}
42
43HRESULT AudioAdapter::FinalConstruct()
44{
45 return BaseFinalConstruct();
46}
47
48void AudioAdapter::FinalRelease()
49{
50 uninit();
51 BaseFinalRelease();
52}
53
54// public initializer/uninitializer for internal purposes only
55/////////////////////////////////////////////////////////////////////////////
56
57/**
58 * Initializes the audio adapter object.
59 *
60 * @param aParent Handle of the parent object.
61 */
62HRESULT AudioAdapter::init(Machine *aParent)
63{
64 LogFlowThisFunc(("aParent=%p\n", aParent));
65
66 ComAssertRet(aParent, E_INVALIDARG);
67
68 /* Enclose the state transition NotReady->InInit->Ready */
69 AutoInitSpan autoInitSpan(this);
70 AssertReturn(autoInitSpan.isOk(), E_FAIL);
71
72 /* Get the default audio driver out of the system properties */
73 ComPtr<IVirtualBox> VBox;
74 HRESULT rc = aParent->COMGETTER(Parent)(VBox.asOutParam());
75 if (FAILED(rc)) return rc;
76 ComPtr<ISystemProperties> sysProps;
77 rc = VBox->COMGETTER(SystemProperties)(sysProps.asOutParam());
78 if (FAILED(rc)) return rc;
79 AudioDriverType_T defaultAudioDriver;
80 rc = sysProps->COMGETTER(DefaultAudioDriver)(&defaultAudioDriver);
81 if (FAILED(rc)) return rc;
82
83 unconst(mParent) = aParent;
84 /* mPeer is left null */
85
86 mData.allocate();
87 mData->driverType = defaultAudioDriver;
88
89 /* Confirm a successful initialization */
90 autoInitSpan.setSucceeded();
91
92 return S_OK;
93}
94
95/**
96 * Initializes the audio adapter object given another audio adapter object
97 * (a kind of copy constructor). This object shares data with
98 * the object passed as an argument.
99 *
100 * @note This object must be destroyed before the original object
101 * it shares data with is destroyed.
102 *
103 * @note Locks @a aThat object for reading.
104 */
105HRESULT AudioAdapter::init(Machine *aParent, AudioAdapter *aThat)
106{
107 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
108
109 ComAssertRet(aParent && aThat, E_INVALIDARG);
110
111 /* Enclose the state transition NotReady->InInit->Ready */
112 AutoInitSpan autoInitSpan(this);
113 AssertReturn(autoInitSpan.isOk(), E_FAIL);
114
115 unconst(mParent) = aParent;
116 unconst(mPeer) = aThat;
117
118 AutoCaller thatCaller(aThat);
119 AssertComRCReturnRC(thatCaller.rc());
120
121 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
122 mData.share(aThat->mData);
123
124 /* Confirm a successful initialization */
125 autoInitSpan.setSucceeded();
126
127 return S_OK;
128}
129
130/**
131 * Initializes the guest object given another guest object
132 * (a kind of copy constructor). This object makes a private copy of data
133 * of the original object passed as an argument.
134 *
135 * @note Locks @a aThat object for reading.
136 */
137HRESULT AudioAdapter::initCopy(Machine *aParent, AudioAdapter *aThat)
138{
139 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
140
141 ComAssertRet(aParent && aThat, E_INVALIDARG);
142
143 /* Enclose the state transition NotReady->InInit->Ready */
144 AutoInitSpan autoInitSpan(this);
145 AssertReturn(autoInitSpan.isOk(), E_FAIL);
146
147 unconst(mParent) = aParent;
148 /* mPeer is left null */
149
150 AutoCaller thatCaller(aThat);
151 AssertComRCReturnRC(thatCaller.rc());
152
153 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
154 mData.attachCopy(aThat->mData);
155
156 /* Confirm a successful initialization */
157 autoInitSpan.setSucceeded();
158
159 return S_OK;
160}
161
162/**
163 * Uninitializes the instance and sets the ready flag to FALSE.
164 * Called either from FinalRelease() or by the parent when it gets destroyed.
165 */
166void AudioAdapter::uninit()
167{
168 LogFlowThisFunc(("\n"));
169
170 /* Enclose the state transition Ready->InUninit->NotReady */
171 AutoUninitSpan autoUninitSpan(this);
172 if (autoUninitSpan.uninitDone())
173 return;
174
175 mData.free();
176
177 unconst(mPeer) = NULL;
178 unconst(mParent) = NULL;
179}
180
181// IAudioAdapter properties
182/////////////////////////////////////////////////////////////////////////////
183
184HRESULT AudioAdapter::getEnabled(BOOL *aEnabled)
185{
186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
187
188 *aEnabled = mData->fEnabled;
189
190 return S_OK;
191}
192
193HRESULT AudioAdapter::setEnabled(BOOL aEnabled)
194{
195 /* the machine needs to be mutable */
196 AutoMutableStateDependency adep(mParent);
197 if (FAILED(adep.rc())) return adep.rc();
198
199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
200
201 if (mData->fEnabled != RT_BOOL(aEnabled))
202 {
203 mData.backup();
204 mData->fEnabled = RT_BOOL(aEnabled);
205
206 alock.release();
207 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
208 mParent->i_setModified(Machine::IsModified_AudioAdapter);
209 }
210
211 return S_OK;
212}
213
214HRESULT AudioAdapter::getEnabledIn(BOOL *aEnabled)
215{
216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
217
218 *aEnabled = mData->fEnabledIn;
219
220 return S_OK;
221}
222
223HRESULT AudioAdapter::setEnabledIn(BOOL aEnabled)
224{
225 /* the machine needs to be mutable */
226 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
227 if (FAILED(adep.rc())) return adep.rc();
228
229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
230 if (RT_BOOL(aEnabled) != mData->fEnabledIn)
231 {
232 mData.backup();
233 mData->fEnabledIn = RT_BOOL(aEnabled);
234
235 // leave the lock before informing callbacks
236 alock.release();
237
238 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
239 mParent->i_setModified(Machine::IsModified_AudioAdapter);
240 mlock.release();
241
242 mParent->i_onAudioAdapterChange(this);
243 }
244
245 return S_OK;
246}
247
248HRESULT AudioAdapter::getEnabledOut(BOOL *aEnabled)
249{
250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
251
252 *aEnabled = mData->fEnabledOut;
253
254 return S_OK;
255}
256
257HRESULT AudioAdapter::setEnabledOut(BOOL aEnabled)
258{
259 /* the machine needs to be mutable */
260 AutoMutableOrSavedOrRunningStateDependency adep(mParent);
261 if (FAILED(adep.rc())) return adep.rc();
262
263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
264 if (RT_BOOL(aEnabled) != mData->fEnabledOut)
265 {
266 mData.backup();
267 mData->fEnabledOut = RT_BOOL(aEnabled);
268
269 // leave the lock before informing callbacks
270 alock.release();
271
272 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
273 mParent->i_setModified(Machine::IsModified_AudioAdapter);
274 mlock.release();
275
276 mParent->i_onAudioAdapterChange(this);
277 }
278
279 return S_OK;
280}
281
282HRESULT AudioAdapter::getAudioDriver(AudioDriverType_T *aAudioDriver)
283{
284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
285
286 *aAudioDriver = mData->driverType;
287
288 return S_OK;
289}
290
291HRESULT AudioAdapter::setAudioDriver(AudioDriverType_T aAudioDriver)
292{
293
294 /* the machine needs to be mutable */
295 AutoMutableOrSavedStateDependency adep(mParent);
296 if (FAILED(adep.rc())) return adep.rc();
297
298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
299
300 HRESULT rc = S_OK;
301
302 if (mData->driverType != aAudioDriver)
303 {
304 if (settings::MachineConfigFile::isAudioDriverAllowedOnThisHost(aAudioDriver))
305 {
306 mData.backup();
307 mData->driverType = aAudioDriver;
308 alock.release();
309 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
310 mParent->i_setModified(Machine::IsModified_AudioAdapter);
311 }
312 else
313 {
314 AssertMsgFailed(("Wrong audio driver type %d\n", aAudioDriver));
315 rc = E_FAIL;
316 }
317 }
318
319 return rc;
320}
321
322HRESULT AudioAdapter::getAudioController(AudioControllerType_T *aAudioController)
323{
324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
325
326 *aAudioController = mData->controllerType;
327
328 return S_OK;
329}
330
331HRESULT AudioAdapter::setAudioController(AudioControllerType_T aAudioController)
332{
333 /* the machine needs to be mutable */
334 AutoMutableStateDependency adep(mParent);
335 if (FAILED(adep.rc())) return adep.rc();
336
337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
338
339 HRESULT rc = S_OK;
340
341 if (mData->controllerType != aAudioController)
342 {
343 AudioCodecType_T defaultCodec;
344
345 /*
346 * which audio hardware type are we supposed to use?
347 */
348 switch (aAudioController)
349 {
350 /* codec type needs to match the controller. */
351 case AudioControllerType_AC97:
352 defaultCodec = AudioCodecType_STAC9700;
353 break;
354 case AudioControllerType_SB16:
355 defaultCodec = AudioCodecType_SB16;
356 break;
357 case AudioControllerType_HDA:
358 defaultCodec = AudioCodecType_STAC9221;
359 break;
360
361 default:
362 AssertMsgFailed(("Wrong audio controller type %d\n", aAudioController));
363 defaultCodec = AudioCodecType_Null; /* Shut up MSC */
364 rc = E_FAIL;
365 }
366 if (rc == S_OK)
367 {
368 mData.backup();
369 mData->controllerType = aAudioController;
370 mData->codecType = defaultCodec;
371 alock.release();
372 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
373 mParent->i_setModified(Machine::IsModified_AudioAdapter);
374 }
375 }
376
377 return rc;
378}
379
380HRESULT AudioAdapter::getAudioCodec(AudioCodecType_T *aAudioCodec)
381{
382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 *aAudioCodec = mData->codecType;
385
386 return S_OK;
387}
388
389HRESULT AudioAdapter::setAudioCodec(AudioCodecType_T aAudioCodec)
390{
391 /* the machine needs to be mutable */
392 AutoMutableStateDependency adep(mParent);
393 if (FAILED(adep.rc())) return adep.rc();
394
395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
396
397 HRESULT rc = S_OK;
398
399 /*
400 * ensure that the codec type matches the audio controller
401 */
402 switch (mData->controllerType)
403 {
404 case AudioControllerType_AC97:
405 {
406 if ( (aAudioCodec != AudioCodecType_STAC9700)
407 && (aAudioCodec != AudioCodecType_AD1980))
408 rc = E_INVALIDARG;
409 break;
410 }
411
412 case AudioControllerType_SB16:
413 {
414 if (aAudioCodec != AudioCodecType_SB16)
415 rc = E_INVALIDARG;
416 break;
417 }
418
419 case AudioControllerType_HDA:
420 {
421 if (aAudioCodec != AudioCodecType_STAC9221)
422 rc = E_INVALIDARG;
423 break;
424 }
425
426 default:
427 AssertMsgFailed(("Wrong audio controller type %d\n",
428 mData->controllerType));
429 rc = E_FAIL;
430 }
431
432 if (!SUCCEEDED(rc))
433 return setError(rc,
434 tr("Invalid audio codec type %d"),
435 aAudioCodec);
436
437 if (mData->codecType != aAudioCodec)
438 {
439 mData.backup();
440 mData->codecType = aAudioCodec;
441 alock.release();
442 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
443 mParent->i_setModified(Machine::IsModified_AudioAdapter);
444 }
445
446 return rc;
447}
448
449HRESULT AudioAdapter::getPropertiesList(std::vector<com::Utf8Str>& aProperties)
450{
451 using namespace settings;
452
453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
454
455 aProperties.resize(0);
456 StringsMap::const_iterator cit = mData->properties.begin();
457 while(cit != mData->properties.end())
458 {
459 Utf8Str key = cit->first;
460 aProperties.push_back(cit->first);
461 ++cit;
462 }
463
464 return S_OK;
465}
466
467HRESULT AudioAdapter::getProperty(const com::Utf8Str &aKey, com::Utf8Str &aValue)
468{
469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
470
471 settings::StringsMap::const_iterator cit = mData->properties.find(aKey);
472 if (cit != mData->properties.end())
473 aValue = cit->second;
474
475 return S_OK;
476}
477
478HRESULT AudioAdapter::setProperty(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
479{
480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
481
482 /* Generic properties processing.
483 * Look up the old value first; if nothing's changed then do nothing.
484 */
485 Utf8Str strOldValue;
486
487 settings::StringsMap::const_iterator cit = mData->properties.find(aKey);
488 if (cit != mData->properties.end())
489 strOldValue = cit->second;
490
491 if (strOldValue != aValue)
492 {
493 if (aValue.isEmpty())
494 mData->properties.erase(aKey);
495 else
496 mData->properties[aKey] = aValue;
497 }
498
499 alock.release();
500
501 return S_OK;
502}
503
504// IAudioAdapter methods
505/////////////////////////////////////////////////////////////////////////////
506
507// public methods only for internal purposes
508/////////////////////////////////////////////////////////////////////////////
509
510/**
511 * Loads settings from the given machine node.
512 * May be called once right after this object creation.
513 *
514 * @param data Configuration settings.
515 *
516 * @note Locks this object for writing.
517 */
518HRESULT AudioAdapter::i_loadSettings(const settings::AudioAdapter &data)
519{
520 AutoCaller autoCaller(this);
521 AssertComRCReturnRC(autoCaller.rc());
522
523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
524
525 /* Note: we assume that the default values for attributes of optional
526 * nodes are assigned in the Data::Data() constructor and don't do it
527 * here. It implies that this method may only be called after constructing
528 * a new AudioAdapter object while all its data fields are in the default
529 * values. Exceptions are fields whose creation time defaults don't match
530 * values that should be applied when these fields are not explicitly set
531 * in the settings file (for backwards compatibility reasons). This takes
532 * place when a setting of a newly created object must default to A while
533 * the same setting of an object loaded from the old settings file must
534 * default to B. */
535 mData.assignCopy(&data);
536
537 return S_OK;
538}
539
540/**
541 * Saves settings to the given machine node.
542 *
543 * @param data Configuration settings.
544 *
545 * @note Locks this object for reading.
546 */
547HRESULT AudioAdapter::i_saveSettings(settings::AudioAdapter &data)
548{
549 AutoCaller autoCaller(this);
550 AssertComRCReturnRC(autoCaller.rc());
551
552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
553
554 data = *mData.data();
555
556 return S_OK;
557}
558
559/**
560 * @note Locks this object for writing.
561 */
562void AudioAdapter::i_rollback()
563{
564 /* sanity */
565 AutoCaller autoCaller(this);
566 AssertComRCReturnVoid(autoCaller.rc());
567
568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
569
570 mData.rollback();
571}
572
573/**
574 * @note Locks this object for writing, together with the peer object (also
575 * for writing) if there is one.
576 */
577void AudioAdapter::i_commit()
578{
579 /* sanity */
580 AutoCaller autoCaller(this);
581 AssertComRCReturnVoid(autoCaller.rc());
582
583 /* sanity too */
584 AutoCaller peerCaller(mPeer);
585 AssertComRCReturnVoid(peerCaller.rc());
586
587 /* lock both for writing since we modify both (mPeer is "master" so locked
588 * first) */
589 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
590
591 if (mData.isBackedUp())
592 {
593 mData.commit();
594 if (mPeer)
595 {
596 /* attach new data to the peer and reshare it */
597 mPeer->mData.attach(mData);
598 }
599 }
600}
601
602/**
603 * @note Locks this object for writing, together with the peer object
604 * represented by @a aThat (locked for reading).
605 */
606void AudioAdapter::i_copyFrom(AudioAdapter *aThat)
607{
608 AssertReturnVoid(aThat != NULL);
609
610 /* sanity */
611 AutoCaller autoCaller(this);
612 AssertComRCReturnVoid(autoCaller.rc());
613
614 /* sanity too */
615 AutoCaller thatCaller(aThat);
616 AssertComRCReturnVoid(thatCaller.rc());
617
618 /* peer is not modified, lock it for reading (aThat is "master" so locked
619 * first) */
620 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
621 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
622
623 /* this will back up current data */
624 mData.assignCopy(aThat->mData);
625}
626/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette