VirtualBox

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

Last change on this file since 95423 was 95423, checked in by vboxsync, 3 years ago

Audio/Main: Bigger revamp of the audio interface(s) to later also support host audio device enumeration and selection for individual VMs. The audio settings now live in a dedicated (per-VM) IAudioSettings interface (audio adapter + audio host device stuff), to further tidy up the IMachine interface. Also added stubs for IAudioDevice + IHostAudioDevice, plus enmuerations, left for further implementation. Added a new IHostAudioDeviceChangedEvent that can also be used later by API clients. bugref:10050

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

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