VirtualBox

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

Last change on this file since 98028 was 98028, checked in by vboxsync, 22 months ago

Audio/Main: Made the Backupable<T> align with the rest of Main (via a private struct), added more documentation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: AudioAdapterImpl.cpp 98028 2023-01-09 09:46:16Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2022 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.rc());
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.rc());
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 unconst(m->pPeer) = NULL;
208 unconst(m->pParent) = NULL;
209
210 delete m;
211 m = NULL;
212}
213
214// IAudioAdapter properties
215/////////////////////////////////////////////////////////////////////////////
216
217HRESULT AudioAdapter::getEnabled(BOOL *aEnabled)
218{
219 AutoCaller autoCaller(this);
220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
221
222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
223
224 *aEnabled = m->bd->fEnabled;
225
226 return S_OK;
227}
228
229HRESULT AudioAdapter::setEnabled(BOOL aEnabled)
230{
231 AutoCaller autoCaller(this);
232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
233
234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
235
236 if (m->bd->fEnabled != RT_BOOL(aEnabled))
237 {
238 m->bd.backup();
239 m->bd->fEnabled = RT_BOOL(aEnabled);
240 alock.release();
241
242 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
243 m->pParent->i_onAdapterChanged(this);
244 }
245
246 return S_OK;
247}
248
249HRESULT AudioAdapter::getEnabledIn(BOOL *aEnabled)
250{
251 AutoCaller autoCaller(this);
252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
253
254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
255
256 *aEnabled = RT_BOOL(m->bd->fEnabledIn);
257
258 return S_OK;
259}
260
261HRESULT AudioAdapter::setEnabledIn(BOOL aEnabled)
262{
263 AutoCaller autoCaller(this);
264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
265
266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
267
268 if (RT_BOOL(aEnabled) != m->bd->fEnabledIn)
269 {
270 m->bd.backup();
271 m->bd->fEnabledIn = RT_BOOL(aEnabled);
272
273 alock.release();
274
275 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
276 m->pParent->i_onAdapterChanged(this);
277 }
278
279 return S_OK;
280}
281
282HRESULT AudioAdapter::getEnabledOut(BOOL *aEnabled)
283{
284 AutoCaller autoCaller(this);
285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
286
287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
288
289 *aEnabled = RT_BOOL(m->bd->fEnabledOut);
290
291 return S_OK;
292}
293
294HRESULT AudioAdapter::setEnabledOut(BOOL aEnabled)
295{
296 AutoCaller autoCaller(this);
297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
298
299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
300
301 if (RT_BOOL(aEnabled) != m->bd->fEnabledOut)
302 {
303 m->bd.backup();
304 m->bd->fEnabledOut = RT_BOOL(aEnabled);
305
306 alock.release();
307
308 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
309 m->pParent->i_onAdapterChanged(this);
310 }
311
312 return S_OK;
313}
314
315HRESULT AudioAdapter::getAudioDriver(AudioDriverType_T *aAudioDriver)
316{
317 AutoCaller autoCaller(this);
318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
319
320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
321
322 *aAudioDriver = m->bd->driverType;
323
324 return S_OK;
325}
326
327HRESULT AudioAdapter::setAudioDriver(AudioDriverType_T aAudioDriver)
328{
329 AutoCaller autoCaller(this);
330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
331
332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
333
334 HRESULT rc = S_OK;
335
336 if (m->bd->driverType != aAudioDriver)
337 {
338 if (settings::MachineConfigFile::isAudioDriverAllowedOnThisHost(aAudioDriver))
339 {
340 m->bd.backup();
341 m->bd->driverType = aAudioDriver;
342
343 alock.release();
344
345 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
346 }
347 else
348 {
349 AssertMsgFailed(("Wrong audio driver type %d\n", aAudioDriver));
350 rc = E_FAIL;
351 }
352 }
353
354 return rc;
355}
356
357HRESULT AudioAdapter::getAudioController(AudioControllerType_T *aAudioController)
358{
359 AutoCaller autoCaller(this);
360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
361
362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
363
364 *aAudioController = m->bd->controllerType;
365
366 return S_OK;
367}
368
369HRESULT AudioAdapter::setAudioController(AudioControllerType_T aAudioController)
370{
371 AutoCaller autoCaller(this);
372 if (FAILED(autoCaller.rc())) return autoCaller.rc();
373
374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
375
376 HRESULT hrc = S_OK;
377
378 if (m->bd->controllerType != aAudioController)
379 {
380 AudioCodecType_T defaultCodec;
381
382 /*
383 * which audio hardware type are we supposed to use?
384 */
385 switch (aAudioController)
386 {
387 /* codec type needs to match the controller. */
388 case AudioControllerType_AC97:
389 defaultCodec = AudioCodecType_STAC9700;
390 break;
391 case AudioControllerType_SB16:
392 defaultCodec = AudioCodecType_SB16;
393 break;
394 case AudioControllerType_HDA:
395 defaultCodec = AudioCodecType_STAC9221;
396 break;
397
398 default:
399 AssertMsgFailed(("Wrong audio controller type %d\n", aAudioController));
400 defaultCodec = AudioCodecType_Null; /* Shut up MSC */
401 hrc = E_FAIL;
402 }
403
404 if (SUCCEEDED(hrc))
405 {
406 m->bd.backup();
407 m->bd->controllerType = aAudioController;
408 m->bd->codecType = defaultCodec;
409
410 alock.release();
411
412 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
413 }
414 }
415
416 return hrc;
417}
418
419HRESULT AudioAdapter::getAudioCodec(AudioCodecType_T *aAudioCodec)
420{
421 AutoCaller autoCaller(this);
422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
423
424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
425
426 *aAudioCodec = m->bd->codecType;
427
428 return S_OK;
429}
430
431HRESULT AudioAdapter::setAudioCodec(AudioCodecType_T aAudioCodec)
432{
433 AutoCaller autoCaller(this);
434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
435
436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
437
438 HRESULT hrc = S_OK;
439
440 /*
441 * ensure that the codec type matches the audio controller
442 */
443 switch (m->bd->controllerType)
444 {
445 case AudioControllerType_AC97:
446 {
447 if ( (aAudioCodec != AudioCodecType_STAC9700)
448 && (aAudioCodec != AudioCodecType_AD1980))
449 hrc = E_INVALIDARG;
450 break;
451 }
452
453 case AudioControllerType_SB16:
454 {
455 if (aAudioCodec != AudioCodecType_SB16)
456 hrc = E_INVALIDARG;
457 break;
458 }
459
460 case AudioControllerType_HDA:
461 {
462 if (aAudioCodec != AudioCodecType_STAC9221)
463 hrc = E_INVALIDARG;
464 break;
465 }
466
467 default:
468 AssertMsgFailed(("Wrong audio controller type %d\n",
469 m->bd->controllerType));
470 hrc = E_FAIL;
471 }
472
473 if (!SUCCEEDED(hrc))
474 return setError(hrc,
475 tr("Invalid audio codec type %d"),
476 aAudioCodec);
477
478 if (m->bd->codecType != aAudioCodec)
479 {
480 m->bd.backup();
481 m->bd->codecType = aAudioCodec;
482
483 alock.release();
484
485 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
486 }
487
488 return hrc;
489}
490
491HRESULT AudioAdapter::getPropertiesList(std::vector<com::Utf8Str>& aProperties)
492{
493 AutoCaller autoCaller(this);
494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
495
496 using namespace settings;
497
498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
499
500 aProperties.resize(0);
501 StringsMap::const_iterator cit = m->bd->properties.begin();
502 while(cit != m->bd->properties.end())
503 {
504 Utf8Str key = cit->first;
505 aProperties.push_back(cit->first);
506 ++cit;
507 }
508
509 return S_OK;
510}
511
512HRESULT AudioAdapter::getProperty(const com::Utf8Str &aKey, com::Utf8Str &aValue)
513{
514 AutoCaller autoCaller(this);
515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
516
517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
518
519 settings::StringsMap::const_iterator cit = m->bd->properties.find(aKey);
520 if (cit != m->bd->properties.end())
521 aValue = cit->second;
522
523 return S_OK;
524}
525
526HRESULT AudioAdapter::setProperty(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
527{
528 AutoCaller autoCaller(this);
529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
530
531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
532
533 /* Generic properties processing.
534 * Look up the old value first; if nothing's changed then do nothing.
535 */
536 Utf8Str strOldValue;
537
538 settings::StringsMap::const_iterator cit = m->bd->properties.find(aKey);
539 if (cit != m->bd->properties.end())
540 strOldValue = cit->second;
541
542 if (strOldValue != aValue)
543 {
544 if (aValue.isEmpty())
545 m->bd->properties.erase(aKey);
546 else
547 m->bd->properties[aKey] = aValue;
548 }
549
550 alock.release();
551
552 return S_OK;
553}
554
555// IAudioAdapter methods
556/////////////////////////////////////////////////////////////////////////////
557
558// public methods only for internal purposes
559/////////////////////////////////////////////////////////////////////////////
560
561/**
562 * Loads settings from the given machine node.
563 * May be called once right after this object creation.
564 *
565 * @returns HRESULT
566 * @param data Audio adapter configuration settings to load from.
567 *
568 * @note Locks this object for writing.
569 */
570HRESULT AudioAdapter::i_loadSettings(const settings::AudioAdapter &data)
571{
572 AutoCaller autoCaller(this);
573 AssertComRCReturnRC(autoCaller.rc());
574
575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
576
577 /* Note: we assume that the default values for attributes of optional
578 * nodes are assigned in the Data::Data() constructor and don't do it
579 * here. It implies that this method may only be called after constructing
580 * a new AudioAdapter object while all its data fields are in the default
581 * values. Exceptions are fields whose creation time defaults don't match
582 * values that should be applied when these fields are not explicitly set
583 * in the settings file (for backwards compatibility reasons). This takes
584 * place when a setting of a newly created object must default to A while
585 * the same setting of an object loaded from the old settings file must
586 * default to B. */
587 m->bd.assignCopy(&data);
588
589 return S_OK;
590}
591
592/**
593 * Saves settings to the given machine node.
594 *
595 * @returns HRESULT
596 * @param data Audio adapter configuration settings to save to.
597 *
598 * @note Locks this object for reading.
599 */
600HRESULT AudioAdapter::i_saveSettings(settings::AudioAdapter &data)
601{
602 AutoCaller autoCaller(this);
603 AssertComRCReturnRC(autoCaller.rc());
604
605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
606
607 data = *m->bd.data();
608
609 return S_OK;
610}
611
612/**
613 * Rolls back the current configuration to a former state.
614 *
615 * @note Locks this object for writing.
616 */
617void AudioAdapter::i_rollback()
618{
619 /* sanity */
620 AutoCaller autoCaller(this);
621 AssertComRCReturnVoid(autoCaller.rc());
622
623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
624
625 m->bd.rollback();
626}
627
628/**
629 * Commits the current settings and propagates those to a peer (if assigned).
630 *
631 * @note Locks this object for writing, together with the peer object (also
632 * for writing) if there is one.
633 */
634void AudioAdapter::i_commit()
635{
636 /* sanity */
637 AutoCaller autoCaller(this);
638 AssertComRCReturnVoid(autoCaller.rc());
639
640 /* sanity too */
641 AutoCaller peerCaller(m->pPeer);
642 AssertComRCReturnVoid(peerCaller.rc());
643
644 /* lock both for writing since we modify both (mPeer is "master" so locked
645 * first) */
646 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
647
648 if (m->bd.isBackedUp())
649 {
650 m->bd.commit();
651 if (m->pPeer)
652 {
653 /* attach new data to the peer and reshare it */
654 m->pPeer->m->bd.attach(m->bd);
655 }
656 }
657}
658
659/**
660 * Copies settings from a given audio adapter object.
661 *
662 * This object makes a private copy of data of the original object passed as
663 * an argument.
664 *
665 * @note Locks this object for writing, together with the peer object
666 * represented by @a aThat (locked for reading).
667 *
668 * @param aThat Audio adapter to load settings from.
669 */
670void AudioAdapter::i_copyFrom(AudioAdapter *aThat)
671{
672 AssertReturnVoid(aThat != NULL);
673
674 /* sanity */
675 AutoCaller autoCaller(this);
676 AssertComRCReturnVoid(autoCaller.rc());
677
678 /* sanity too */
679 AutoCaller thatCaller(aThat);
680 AssertComRCReturnVoid(thatCaller.rc());
681
682 /* peer is not modified, lock it for reading (aThat is "master" so locked
683 * first) */
684 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
685 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
686
687 /* this will back up current data */
688 m->bd.assignCopy(aThat->m->bd);
689}
690/* 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