PageRenderTime 1330ms CodeModel.GetById 202ms app.highlight 493ms RepoModel.GetById 623ms app.codeStats 1ms

/indra/media_plugins/webkit/mac_volume_catcher.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 268 lines | 156 code | 55 blank | 57 comment | 10 complexity | c265d909c9a4dd07a6adcefd05e6424c MD5 | raw file
  1/** 
  2 * @file mac_volume_catcher.cpp
  3 * @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process.
  4 *
  5 * @cond
  6 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 * @endcond
 27 */
 28
 29/**************************************************************************************************************
 30	This code works by using CaptureComponent to capture the "Default Output" audio component
 31	(kAudioUnitType_Output/kAudioUnitSubType_DefaultOutput) and delegating all calls to the original component.
 32	It does this just to keep track of all instances of the default output component, so that it can set the
 33	kHALOutputParam_Volume parameter on all of them to adjust the output volume.
 34**************************************************************************************************************/
 35
 36#include "volume_catcher.h"
 37
 38#include <Carbon/Carbon.h>
 39#include <QuickTime/QuickTime.h>
 40#include <AudioUnit/AudioUnit.h>
 41
 42struct VolumeCatcherStorage;
 43
 44class VolumeCatcherImpl
 45{
 46public:
 47
 48	void setVolume(F32 volume);
 49	void setPan(F32 pan);
 50	
 51	void setInstanceVolume(VolumeCatcherStorage *instance);
 52	
 53	std::list<VolumeCatcherStorage*> mComponentInstances;
 54	Component mOriginalDefaultOutput;
 55	Component mVolumeAdjuster;
 56	
 57	static VolumeCatcherImpl *getInstance();
 58private:
 59	// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
 60	VolumeCatcherImpl();
 61	static VolumeCatcherImpl *sInstance;
 62	
 63	// The singlar instance of this class is expected to last until the process exits.
 64	// To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link.
 65	~VolumeCatcherImpl();	
 66	
 67	F32 mVolume;
 68	F32 mPan;
 69};
 70
 71VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;;
 72
 73struct VolumeCatcherStorage
 74{
 75	ComponentInstance self;
 76	ComponentInstance delegate;
 77};
 78
 79static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage);
 80static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self);
 81static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self);
 82
 83VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
 84{
 85	if(!sInstance)
 86	{
 87		sInstance = new VolumeCatcherImpl;
 88	}
 89	
 90	return sInstance;
 91}
 92
 93VolumeCatcherImpl::VolumeCatcherImpl()
 94{
 95	mVolume = 1.0;	// default to full volume
 96	mPan = 0.0;		// and center pan
 97		
 98	ComponentDescription desc;
 99	desc.componentType = kAudioUnitType_Output;
100	desc.componentSubType = kAudioUnitSubType_DefaultOutput;
101	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
102	desc.componentFlags = 0;
103	desc.componentFlagsMask = 0;
104	
105	// Find the original default output component
106	mOriginalDefaultOutput = FindNextComponent(NULL, &desc);
107
108	// Register our own output component with the same parameters
109	mVolumeAdjuster = RegisterComponent(&desc, NewComponentRoutineUPP(volume_catcher_component_entry), 0, NULL, NULL, NULL);
110
111	// Capture the original component, so we always get found instead.
112	CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster);
113
114}
115
116static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage)
117{
118	ComponentResult result = badComponentSelector;
119	VolumeCatcherStorage *storage = (VolumeCatcherStorage*)componentStorage;
120	
121	switch(cp->what)
122	{
123		case kComponentOpenSelect:
124//			std::cerr << "kComponentOpenSelect" << std::endl;
125			result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_open, uppCallComponentOpenProcInfo);
126		break;
127
128		case kComponentCloseSelect:
129//			std::cerr << "kComponentCloseSelect" << std::endl;
130			result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_close, uppCallComponentCloseProcInfo);
131			// CallComponentFunctionWithStorageProcInfo
132		break;
133		
134		default:
135//			std::cerr << "Delegating selector: " << cp->what << " to component instance " << storage->delegate << std::endl;
136			result = DelegateComponentCall(cp, storage->delegate);
137		break;
138	}
139	
140	return result;
141}
142
143static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self)
144{
145	ComponentResult result = noErr;
146	VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();	
147	
148	storage = new VolumeCatcherStorage;
149
150	storage->self = self;
151	storage->delegate = NULL;
152
153	result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate));
154	
155	if(result != noErr)
156	{
157//		std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl;
158		
159		// If we failed to open the delagate component, our open is going to fail.  Clean things up.
160		delete storage;
161	}
162	else
163	{
164		// Success -- set up this component's storage
165		SetComponentInstanceStorage(self, (Handle)storage);
166
167		// add this instance to the global list
168		impl->mComponentInstances.push_back(storage);	
169		
170		// and set up the initial volume
171		impl->setInstanceVolume(storage);
172	}
173
174	return result;
175}
176
177static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self)
178{
179	ComponentResult result = noErr;
180	
181	if(storage)
182	{
183		if(storage->delegate)
184		{
185			CloseComponent(storage->delegate);
186			storage->delegate = NULL;
187		}
188		
189		VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();	
190		impl->mComponentInstances.remove(storage);
191		delete[] storage;
192	}
193		
194	return result;
195}
196
197void VolumeCatcherImpl::setVolume(F32 volume)
198{
199	VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();	
200	impl->mVolume = volume;
201	
202	// Iterate through all known instances, setting the volume on each.
203	for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter)
204	{
205		impl->setInstanceVolume(*iter);
206	}
207}
208
209void VolumeCatcherImpl::setPan(F32 pan)
210{
211	VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();	
212	impl->mPan = pan;
213	
214	// TODO: implement this.
215	// This will probably require adding a "panner" audio unit to the chain somehow.
216	// There's also a "3d mixer" component that we might be able to use...
217}
218
219void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance)
220{
221//	std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
222	
223	OSStatus err = noErr;
224	
225	if(instance && instance->delegate)
226	{
227		err = AudioUnitSetParameter(
228				instance->delegate, 
229				kHALOutputParam_Volume, 
230				kAudioUnitScope_Global,
231				0, 
232				mVolume, 
233				0);
234	}
235	
236	if(err)
237	{
238//		std::cerr << "    AudioUnitSetParameter returned " << err << std::endl;
239	}
240}
241
242/////////////////////////////////////////////////////
243
244VolumeCatcher::VolumeCatcher()
245{
246	pimpl = VolumeCatcherImpl::getInstance();
247}
248
249VolumeCatcher::~VolumeCatcher()
250{
251	// Let the instance persist until exit.
252}
253
254void VolumeCatcher::setVolume(F32 volume)
255{
256	pimpl->setVolume(volume);
257}
258
259void VolumeCatcher::setPan(F32 pan)
260{
261	pimpl->setPan(pan);
262}
263
264void VolumeCatcher::pump()
265{
266	// No periodic tasks are necessary for this implementation.
267}
268