PageRenderTime 73ms CodeModel.GetById 40ms app.highlight 18ms RepoModel.GetById 12ms app.codeStats 0ms

/indra/media_plugins/winmmshim/winmm_shim.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 200 lines | 140 code | 19 blank | 41 comment | 25 complexity | 3c121f1df07fe99462271177c2af0666 MD5 | raw file
  1/** 
  2 * @file winmmshim.cpp
  3 * @brief controls volume level of process by intercepting calls to winmm.dll
  4 *
  5 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26#include "forwarding_api.h"
 27#include <xmmintrin.h>
 28#include <map>
 29#include <math.h>
 30
 31using std::wstring;
 32
 33static float sVolumeLevel = 1.f;
 34static bool sMute = false;
 35static CRITICAL_SECTION sCriticalSection;
 36
 37BOOL APIENTRY DllMain( HMODULE hModule,
 38                       DWORD  ul_reason_for_call,
 39                       LPVOID lpReserved
 40					 )
 41{
 42	InitializeCriticalSection(&sCriticalSection);
 43	return TRUE;
 44}
 45
 46void ll_winmm_shim_initialize(){
 47	static bool initialized = false;
 48	// do this only once
 49	EnterCriticalSection(&sCriticalSection);
 50	if (!initialized)
 51	{	// bind to original winmm.dll
 52		TCHAR system_path[MAX_PATH];
 53		TCHAR dll_path[MAX_PATH];
 54		::GetSystemDirectory(system_path, MAX_PATH);
 55
 56		// grab winmm.dll from system path, where it should live
 57		wsprintf(dll_path, "%s\\winmm.dll", system_path);
 58		HMODULE winmm_handle = ::LoadLibrary(dll_path);
 59		
 60		if (winmm_handle != NULL)
 61		{	// we have a dll, let's get out pointers!
 62			initialized = true;
 63			init_function_pointers(winmm_handle);
 64			::OutputDebugStringA("WINMM_SHIM.DLL: real winmm.dll initialized successfully\n");
 65		}
 66		else
 67		{
 68			// failed to initialize real winmm.dll
 69			::OutputDebugStringA("WINMM_SHIM.DLL: Failed to initialize real winmm.dll\n");
 70		}
 71	}
 72	LeaveCriticalSection(&sCriticalSection);
 73}
 74
 75
 76extern "C" 
 77{
 78	// tracks the requested format for a given waveout buffer
 79	struct WaveOutFormat
 80	{
 81		WaveOutFormat(int bits_per_sample)
 82		:	mBitsPerSample(bits_per_sample)
 83		{}
 84		int	mBitsPerSample;
 85	};
 86	typedef std::map<HWAVEOUT, WaveOutFormat*> wave_out_map_t;
 87	static wave_out_map_t sWaveOuts;
 88
 89	MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
 90	{
 91		ll_winmm_shim_initialize();
 92		if (pwfx->wFormatTag != WAVE_FORMAT_PCM
 93			|| (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16))
 94		{ // uncompressed 8 and 16 bit sound are the only types we support
 95			return WAVERR_BADFORMAT;
 96		}
 97
 98		MMRESULT result = waveOutOpen_orig(phwo, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen);
 99		if (result == MMSYSERR_NOERROR 
100			&& ((fdwOpen & WAVE_FORMAT_QUERY) == 0)) // not just querying for format support
101		{	// remember the requested bits per sample, and associate with the given handle
102			WaveOutFormat* wave_outp = new WaveOutFormat(pwfx->wBitsPerSample);
103			sWaveOuts.insert(std::make_pair(*phwo, wave_outp));
104		}
105		return result;
106	}
107
108	MMRESULT WINAPI waveOutClose( HWAVEOUT hwo)
109	{
110		ll_winmm_shim_initialize();
111		wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
112		if (found_it != sWaveOuts.end())
113		{	// forget what we know about this handle
114			delete found_it->second;
115			sWaveOuts.erase(found_it);
116		}
117		return waveOutClose_orig( hwo);
118	}
119
120	MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
121	{
122		ll_winmm_shim_initialize();
123		MMRESULT result = MMSYSERR_NOERROR;
124
125		if (sMute)
126		{ // zero out the audio buffer when muted
127			memset(pwh->lpData, 0, pwh->dwBufferLength);
128		}
129		else if (sVolumeLevel != 1.f) 
130		{ // need to apply volume level
131			wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
132			if (found_it != sWaveOuts.end())
133			{
134				WaveOutFormat* formatp = found_it->second;
135				switch (formatp->mBitsPerSample){
136				case 8:
137					{
138						char volume = (char)(sVolumeLevel * 127.f);
139						for (unsigned int i = 0; i < pwh->dwBufferLength; i++)
140						{
141							// unsigned multiply doesn't use most significant bit, so shift by 7 bits
142							// to get resulting value back into 8 bits
143							pwh->lpData[i] = (pwh->lpData[i] * volume) >> 7;
144						}
145						break;
146					}
147				case 16:
148					{
149						short volume_16 = (short)(sVolumeLevel * 32767.f);
150
151						// copy volume level 4 times into 64 bit MMX register
152						__m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16);
153						__m64* sample_64;
154						__m64* last_sample_64 =  (__m64*)(pwh->lpData + pwh->dwBufferLength - sizeof(__m64));
155						// for everything that can be addressed in 64 bit multiples...
156						for (sample_64 = (__m64*)pwh->lpData;
157							sample_64 <= last_sample_64;
158							++sample_64)
159						{
160							//...multiply the samples by the volume...
161							__m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64);
162							// ...and shift left 1 bit since an unsigned multiple loses the most significant bit
163							// 0x7FFF * 0x7FFF = 0x3fff0001
164							// 0x3fff0001 << 1 = 0x7ffe0002
165							// notice that the LSB is always 0...should consider dithering
166							*sample_64 =  _mm_slli_pi16(scaled_sample, 1); 
167						}
168
169						// the captain has turned off the MMX sign, you are now free to use floating point registers
170						_mm_empty();
171
172						// finish remaining samples that didn't fit into 64 bit register
173						for (short* sample_16 = (short*)sample_64;
174							sample_16 < (short*)(pwh->lpData + pwh->dwBufferLength);
175							++sample_16)
176						{	
177							*sample_16 = (*sample_16 * volume_16) >> 15;
178						}
179
180						break;
181					}
182				default:
183					// don't do anything
184					break;
185				}
186			}
187		}
188		return waveOutWrite_orig( hwo, pwh, cbwh);
189	}
190
191	void WINAPI setPluginVolume(float volume)
192	{
193		sVolumeLevel = volume;
194	}
195
196	void WINAPI setPluginMute(bool mute)
197	{
198		sMute = mute;
199	}
200}