// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see .
//
// VC_Object.h
// VCDriver
//
// Copyright © 2016 Kyle Neideck
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_Object.h from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
//
// The base class for our classes that represent audio objects. (See AudioServerPlugIn.h for a
// quick explanation of audio objects.)
//
// This is a stripped down version of SA_Object.h. Unlike the sample code plugin that SA_Object.h
// is from, we have a fixed set of audio objects. So we've removed the code that handled
// growing/shrinking that set.
//
#ifndef __VCDriver__VC_Object__
#define __VCDriver__VC_Object__
// System Includes
#include
// TODO: Update the large comment below to reflect what was removed from SA_Object.h? It hasn't
// been changed at all so far, except to replace "SA_Object" with "VC_Object".
//==================================================================================================
// VC_Object
//
// This is the base class for objects managed by VC_ObjectMap. It's only job is to ensure that
// objects of this type have the proper external semantics for a reference counted object. This
// means that the destructor is protected so that these objects cannot be deleted directly. Also,
// these objects many not make a copy of another object or be assigned from another object. Note
// that the reference count of the object is tracked and owned by the VC_ObjectMap.
//
// These objects provide RTTI information tied to the constants describing the HAL's API class
// hierarchy as described in the headers. The class ID and base class IDs passed in to the
// constructor must operate with the semantics described in AudioObjectBase.h where the base class
// has to always be one of the standard classes. The class ID can be a custom value or a standard
// value however. If it is a standard value, the base class should be the proper standard base
// class. So for example, a standard volume control object will say that it's class is
// kAudioVolumeControlClassID and that its base class is kAudioLevelControlClassID. In the case of
// a custom boolean control, it would say that it's class is a custom value like 'MYBL' and that
// its base class is kAudioBooleanControlClassID.
//
// Subclasses of this class must implement Activate(). This method is called after an object has
// been constructed and inserted into the object map. Until Activate() is called, a constructed
// object may not do anything active such as sending/receiving notifications or creating other
// objects. Active operations may be performed in the Activate() method proper however. Note that
// Activate() is called prior to any references to the object being handed out. As such, it does
// not need to worry about being thread safe while Activate() is in progress.
//
// Subclasses of this class must also implement Deactivate(). This method is called when the object
// is at the end of its lifecycle. Once Deactivate() has been called, the object may no longer
// perform active operations, including Deactivating other objects. This is based on the notion that
// all the objects have a definite point at which they are considered dead to the outside world.
// For example, an AudioDevice object is dead if it's hardware is unplugged. The point of death is
// the notification the owner of the device gets to signal that it has been unplugged. Note that it
// is both normal and expected that a dead object might still have outstanding references. Thus, an
// object has to put in some care to do the right thing when these zombie references are used. The
// best thing to do is to just have those queries return appropriate errors.
//
// Deactivate() itself needs to be thread safe with respect to other operations taking place on the
// object. This also means taking care to handle the Deactivation of owned objects. For example, an
// AudioDevice object will almost always own one or more AudioStream objects. If the stream is in a
// separate lock domain from it's owning device, then the device has to be very careful about how
// it deactivates the stream such that it doesn't try to lock the stream's lock while holding the
// device's lock which will inevitably lead to a deadlock situation. There are two reasonable
// approaches to dealing with this kind of situation. The first is to just not get into it by
// making the device share a lock domain with all it's owned objects like streams and controls. The
// other approach is to use dispatch queues to make the work of Deactivating owned objects take
// place outside of the device's lock domain. For example, if the device needs to deactivate a
// stream, it can remove the stream from any tracking in the device object and then dispatch
// asynchronously the Deactivate() call on the stream and the release of the reference the device
// has on the stream.
//
// Note that both Activate() and Deactivate() are called by objects at large. Typically,
// Activate() is called by the creator of the object, usually right after the object has been
// allocated. Deactivate() will usually be called by the owner of the object upon recognizing that
// the object is dead to the outside world. Going back to the example of an AudioDevice getting
// unplugged, the Deactivate() method will be called by whomever receives the notification about
// the hardware going away, which is often the owner of the object.
//
// This class also defines methods to implement the portion of the
// AudioServerPlugInDriverInterface that deals with properties. The five methods all have the same
// basic arguments and semantics. The class also provides the implementation for
// the minimum required properties for all AudioObjects. There is a detailed commentary about each
// specific property in the GetPropertyData() method.
//
// It is important that a thread retain and hold a reference while it is using an VC_Object and
// that the reference be released promptly when the thread is finished using the object. By
// assuming this, a VC_Object can minimize the amount of locking it needs to do. In particular,
// purely static or invariant data can be handled without any locking at all.
//==================================================================================================
class VC_Object
{
#pragma mark Construction/Destruction
public:
VC_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID);
virtual void Activate();
virtual void Deactivate();
protected:
virtual ~VC_Object();
private:
VC_Object(const VC_Object&);
VC_Object& operator=(const VC_Object&);
#pragma mark Attributes
public:
AudioObjectID GetObjectID() const { return mObjectID; }
void* GetObjectIDAsPtr() const { uintptr_t thePtr = mObjectID; return reinterpret_cast(thePtr); }
AudioClassID GetClassID() const { return mClassID; }
AudioClassID GetBaseClassID() const { return mBaseClassID; }
AudioObjectID GetOwnerObjectID() const { return mOwnerObjectID; }
bool IsActive() const { return mIsActive; }
#pragma mark Property Operations
public:
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
#pragma mark Implementation
protected:
AudioObjectID mObjectID;
AudioClassID mClassID;
AudioClassID mBaseClassID;
AudioObjectID mOwnerObjectID;
bool mIsActive;
};
#endif /* __VCDriver__VC_Object__ */