VirtualBox

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

Last change on this file since 102869 was 101313, checked in by vboxsync, 14 months ago

Main: Don't return E_NOTIMPL when setting VirtioSound audio adapter, even if this is not implemented yet. Will confuse callers. bugref:10384

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