VirtualBox

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

Last change on this file since 100078 was 98825, checked in by vboxsync, 15 months ago

Main/AudioAdapter: Fix a small memory leak in VBoxSVC: AudioAdapter::init()
calls 'm->bd.allocate()' but AudioAdapter::uninit() neglects to call
'm->bd.free()'.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/* $Id: AudioAdapterImpl.cpp 98825 2023-03-03 10:24:48Z 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
400 default:
401 AssertMsgFailed(("Wrong audio controller type %d\n", aAudioController));
402 defaultCodec = AudioCodecType_Null; /* Shut up MSC */
403 hrc = E_FAIL;
404 }
405
406 if (SUCCEEDED(hrc))
407 {
408 m->bd.backup();
409 m->bd->controllerType = aAudioController;
410 m->bd->codecType = defaultCodec;
411
412 alock.release();
413
414 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
415 }
416 }
417
418 return hrc;
419}
420
421HRESULT AudioAdapter::getAudioCodec(AudioCodecType_T *aAudioCodec)
422{
423 AutoCaller autoCaller(this);
424 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
425
426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
427
428 *aAudioCodec = m->bd->codecType;
429
430 return S_OK;
431}
432
433HRESULT AudioAdapter::setAudioCodec(AudioCodecType_T aAudioCodec)
434{
435 AutoCaller autoCaller(this);
436 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
437
438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
439
440 HRESULT hrc = S_OK;
441
442 /*
443 * ensure that the codec type matches the audio controller
444 */
445 switch (m->bd->controllerType)
446 {
447 case AudioControllerType_AC97:
448 {
449 if ( (aAudioCodec != AudioCodecType_STAC9700)
450 && (aAudioCodec != AudioCodecType_AD1980))
451 hrc = E_INVALIDARG;
452 break;
453 }
454
455 case AudioControllerType_SB16:
456 {
457 if (aAudioCodec != AudioCodecType_SB16)
458 hrc = E_INVALIDARG;
459 break;
460 }
461
462 case AudioControllerType_HDA:
463 {
464 if (aAudioCodec != AudioCodecType_STAC9221)
465 hrc = E_INVALIDARG;
466 break;
467 }
468
469 default:
470 AssertMsgFailed(("Wrong audio controller type %d\n",
471 m->bd->controllerType));
472 hrc = E_FAIL;
473 }
474
475 if (!SUCCEEDED(hrc))
476 return setError(hrc,
477 tr("Invalid audio codec type %d"),
478 aAudioCodec);
479
480 if (m->bd->codecType != aAudioCodec)
481 {
482 m->bd.backup();
483 m->bd->codecType = aAudioCodec;
484
485 alock.release();
486
487 m->pParent->i_onSettingsChanged(); // mParent is const, needs no locking
488 }
489
490 return hrc;
491}
492
493HRESULT AudioAdapter::getPropertiesList(std::vector<com::Utf8Str>& aProperties)
494{
495 AutoCaller autoCaller(this);
496 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
497
498 using namespace settings;
499
500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
501
502 aProperties.resize(0);
503 StringsMap::const_iterator cit = m->bd->properties.begin();
504 while(cit != m->bd->properties.end())
505 {
506 Utf8Str key = cit->first;
507 aProperties.push_back(cit->first);
508 ++cit;
509 }
510
511 return S_OK;
512}
513
514HRESULT AudioAdapter::getProperty(const com::Utf8Str &aKey, com::Utf8Str &aValue)
515{
516 AutoCaller autoCaller(this);
517 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
518
519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
520
521 settings::StringsMap::const_iterator cit = m->bd->properties.find(aKey);
522 if (cit != m->bd->properties.end())
523 aValue = cit->second;
524
525 return S_OK;
526}
527
528HRESULT AudioAdapter::setProperty(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
529{
530 AutoCaller autoCaller(this);
531 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
532
533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
534
535 /* Generic properties processing.
536 * Look up the old value first; if nothing's changed then do nothing.
537 */
538 Utf8Str strOldValue;
539
540 settings::StringsMap::const_iterator cit = m->bd->properties.find(aKey);
541 if (cit != m->bd->properties.end())
542 strOldValue = cit->second;
543
544 if (strOldValue != aValue)
545 {
546 if (aValue.isEmpty())
547 m->bd->properties.erase(aKey);
548 else
549 m->bd->properties[aKey] = aValue;
550 }
551
552 alock.release();
553
554 return S_OK;
555}
556
557// IAudioAdapter methods
558/////////////////////////////////////////////////////////////////////////////
559
560// public methods only for internal purposes
561/////////////////////////////////////////////////////////////////////////////
562
563/**
564 * Loads settings from the given machine node.
565 * May be called once right after this object creation.
566 *
567 * @returns HRESULT
568 * @param data Audio adapter configuration settings to load from.
569 *
570 * @note Locks this object for writing.
571 */
572HRESULT AudioAdapter::i_loadSettings(const settings::AudioAdapter &data)
573{
574 AutoCaller autoCaller(this);
575 AssertComRCReturnRC(autoCaller.hrc());
576
577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
578
579 /* Note: we assume that the default values for attributes of optional
580 * nodes are assigned in the Data::Data() constructor and don't do it
581 * here. It implies that this method may only be called after constructing
582 * a new AudioAdapter object while all its data fields are in the default
583 * values. Exceptions are fields whose creation time defaults don't match
584 * values that should be applied when these fields are not explicitly set
585 * in the settings file (for backwards compatibility reasons). This takes
586 * place when a setting of a newly created object must default to A while
587 * the same setting of an object loaded from the old settings file must
588 * default to B. */
589 m->bd.assignCopy(&data);
590
591 return S_OK;
592}
593
594/**
595 * Saves settings to the given machine node.
596 *
597 * @returns HRESULT
598 * @param data Audio adapter configuration settings to save to.
599 *
600 * @note Locks this object for reading.
601 */
602HRESULT AudioAdapter::i_saveSettings(settings::AudioAdapter &data)
603{
604 AutoCaller autoCaller(this);
605 AssertComRCReturnRC(autoCaller.hrc());
606
607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
608
609 data = *m->bd.data();
610
611 return S_OK;
612}
613
614/**
615 * Rolls back the current configuration to a former state.
616 *
617 * @note Locks this object for writing.
618 */
619void AudioAdapter::i_rollback()
620{
621 /* sanity */
622 AutoCaller autoCaller(this);
623 AssertComRCReturnVoid(autoCaller.hrc());
624
625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
626
627 m->bd.rollback();
628}
629
630/**
631 * Commits the current settings and propagates those to a peer (if assigned).
632 *
633 * @note Locks this object for writing, together with the peer object (also
634 * for writing) if there is one.
635 */
636void AudioAdapter::i_commit()
637{
638 /* sanity */
639 AutoCaller autoCaller(this);
640 AssertComRCReturnVoid(autoCaller.hrc());
641
642 /* sanity too */
643 AutoCaller peerCaller(m->pPeer);
644 AssertComRCReturnVoid(peerCaller.hrc());
645
646 /* lock both for writing since we modify both (mPeer is "master" so locked
647 * first) */
648 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
649
650 if (m->bd.isBackedUp())
651 {
652 m->bd.commit();
653 if (m->pPeer)
654 {
655 /* attach new data to the peer and reshare it */
656 m->pPeer->m->bd.attach(m->bd);
657 }
658 }
659}
660
661/**
662 * Copies settings from a given audio adapter object.
663 *
664 * This object makes a private copy of data of the original object passed as
665 * an argument.
666 *
667 * @note Locks this object for writing, together with the peer object
668 * represented by @a aThat (locked for reading).
669 *
670 * @param aThat Audio adapter to load settings from.
671 */
672void AudioAdapter::i_copyFrom(AudioAdapter *aThat)
673{
674 AssertReturnVoid(aThat != NULL);
675
676 /* sanity */
677 AutoCaller autoCaller(this);
678 AssertComRCReturnVoid(autoCaller.hrc());
679
680 /* sanity too */
681 AutoCaller thatCaller(aThat);
682 AssertComRCReturnVoid(thatCaller.hrc());
683
684 /* peer is not modified, lock it for reading (aThat is "master" so locked
685 * first) */
686 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
687 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
688
689 /* this will back up current data */
690 m->bd.assignCopy(aThat->m->bd);
691}
692/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use