VirtualBox

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

Last change on this file was 103395, checked in by vboxsync, 3 months ago

Main/Audio: Added mutable state dependency checks for audio adapter settings. bugref:10600

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

© 2023 Oracle
ContactPrivacy policyTerms of Use