diff options
Diffstat (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp')
-rw-r--r-- | indra/media_plugins/winmmshim/winmm_shim.cpp | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp new file mode 100644 index 0000000000..6fbf517d7c --- /dev/null +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -0,0 +1,194 @@ +/** + * @file winmmshim.cpp + * @brief controls volume level of process by intercepting calls to winmm.dll + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "forwarding_api.h" +#include <xmmintrin.h> +#include <map> +#include <math.h> + +using std::wstring; + +static float sVolumeLevel = 1.f; +static bool sMute = false; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + static bool initialized = false; + // do this only once + if (!initialized) + { // bind to original winmm.dll + TCHAR system_path[MAX_PATH]; + TCHAR dll_path[MAX_PATH]; + ::GetSystemDirectory(system_path, MAX_PATH); + + // grab winmm.dll from system path, where it should live + wsprintf(dll_path, "%s\\winmm.dll", system_path); + HMODULE winmm_handle = ::LoadLibrary(dll_path); + + if (winmm_handle != NULL) + { // we have a dll, let's get out pointers! + initialized = true; + init_function_pointers(winmm_handle); + return true; + } + + // failed to initialize real winmm.dll + return false; + } + return true; +} + + +extern "C" +{ + // tracks the requested format for a given waveout buffer + struct WaveOutFormat + { + WaveOutFormat(int bits_per_sample) + : mBitsPerSample(bits_per_sample) + {} + int mBitsPerSample; + }; + typedef std::map<HWAVEOUT, WaveOutFormat*> wave_out_map_t; + static wave_out_map_t sWaveOuts; + + MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) + { + if (pwfx->wFormatTag != WAVE_FORMAT_PCM + || (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16)) + { // uncompressed 8 and 16 bit sound are the only types we support + return WAVERR_BADFORMAT; + } + + MMRESULT result = waveOutOpen_orig(phwo, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen); + if (result == MMSYSERR_NOERROR + && ((fdwOpen & WAVE_FORMAT_QUERY) == 0)) // not just querying for format support + { // remember the requested bits per sample, and associate with the given handle + WaveOutFormat* wave_outp = new WaveOutFormat(pwfx->wBitsPerSample); + sWaveOuts.insert(std::make_pair(*phwo, wave_outp)); + } + return result; + } + + MMRESULT WINAPI waveOutClose( HWAVEOUT hwo) + { + wave_out_map_t::iterator found_it = sWaveOuts.find(hwo); + if (found_it != sWaveOuts.end()) + { // forget what we know about this handle + delete found_it->second; + sWaveOuts.erase(found_it); + } + return waveOutClose_orig( hwo); + } + + MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh) + { + MMRESULT result = MMSYSERR_NOERROR; + + if (sMute) + { // zero out the audio buffer when muted + memset(pwh->lpData, 0, pwh->dwBufferLength); + } + else if (sVolumeLevel != 1.f) + { // need to apply volume level + wave_out_map_t::iterator found_it = sWaveOuts.find(hwo); + if (found_it != sWaveOuts.end()) + { + WaveOutFormat* formatp = found_it->second; + switch (formatp->mBitsPerSample){ + case 8: + { + char volume = (char)(sVolumeLevel * 127.f); + for (unsigned int i = 0; i < pwh->dwBufferLength; i++) + { + // unsigned multiply doesn't use most significant bit, so shift by 7 bits + // to get resulting value back into 8 bits + pwh->lpData[i] = (pwh->lpData[i] * volume) >> 7; + } + break; + } + case 16: + { + short volume_16 = (short)(sVolumeLevel * 32767.f); + + // copy volume level 4 times into 64 bit MMX register + __m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16); + __m64* sample_64; + __m64* last_sample_64 = (__m64*)(pwh->lpData + pwh->dwBufferLength - sizeof(__m64)); + // for everything that can be addressed in 64 bit multiples... + for (sample_64 = (__m64*)pwh->lpData; + sample_64 <= last_sample_64; + ++sample_64) + { + //...multiply the samples by the volume... + __m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64); + // ...and shift left 1 bit since an unsigned multiple loses the most significant bit + // 0x7FFF * 0x7FFF = 0x3fff0001 + // 0x3fff0001 << 1 = 0x7ffe0002 + // notice that the LSB is always 0...should consider dithering + *sample_64 = _mm_slli_pi16(scaled_sample, 1); + } + + // the captain has turned off the MMX sign, you are now free to use floating point registers + _mm_empty(); + + // finish remaining samples that didn't fit into 64 bit register + for (short* sample_16 = (short*)sample_64; + sample_16 < (short*)(pwh->lpData + pwh->dwBufferLength); + ++sample_16) + { + *sample_16 = (*sample_16 * volume_16) >> 15; + } + + break; + } + default: + // don't do anything + break; + } + } + } + return waveOutWrite_orig( hwo, pwh, cbwh); + } + + void WINAPI setPluginVolume(float volume) + { + sVolumeLevel = volume; + } + + void WINAPI setPluginMute(bool mute) + { + sMute = mute; + } +} |