From bd08f1989461b8b81ed04df6f8dc2097862a20e8 Mon Sep 17 00:00:00 2001 From: Richard Nelson Date: Tue, 27 Apr 2010 09:41:33 -0700 Subject: EXT-6921 - Windows Xp/Vista unified volume control --- indra/media_plugins/winmmshim/winmm_shim.cpp | 178 +++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 indra/media_plugins/winmmshim/winmm_shim.cpp (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp') diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp new file mode 100644 index 0000000000..a46d908962 --- /dev/null +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -0,0 +1,178 @@ +/** + * @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 +#include +#include + +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; + if (!initialized) + { + TCHAR system_path[MAX_PATH]; + TCHAR dll_path[MAX_PATH]; + ::GetSystemDirectory(system_path, MAX_PATH); + + wsprintf(dll_path, "%s\\winmm.dll", system_path); + HMODULE winmm_handle = ::LoadLibrary(dll_path); + + if (winmm_handle != NULL) + { + initialized = true; + init_function_pointers(winmm_handle); + return true; + } + return false; + } + return true; +} + + +extern "C" +{ + struct WaveOutFormat + { + WaveOutFormat(int bits_per_sample) + : mBitsPerSample(bits_per_sample) + {} + int mBitsPerSample; + }; + typedef std::map 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) + { + //OutputDebugString(L"waveOutOpen\n"); + if (pwfx->wFormatTag != WAVE_FORMAT_PCM) + { + 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 + { + WaveOutFormat* wave_outp = new WaveOutFormat(pwfx->wBitsPerSample); + sWaveOuts.insert(std::make_pair(*phwo, wave_outp)); + } + return result; + } + + MMRESULT WINAPI waveOutClose( HWAVEOUT hwo) + { + //OutputDebugString(L"waveOutClose\n"); + wave_out_map_t::iterator found_it = sWaveOuts.find(hwo); + if (found_it != sWaveOuts.end()) + { + delete found_it->second; + sWaveOuts.erase(found_it); + } + return waveOutClose_orig( hwo); + } + + MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh) + { + //OutputDebugString(L"waveOutWrite\n"); + MMRESULT result = MMSYSERR_NOERROR; + + if (sMute) + { + memset(pwh->lpData, 0, pwh->dwBufferLength); + } + else + { + 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++) + { + pwh->lpData[i] = (pwh->lpData[i] * volume) >> 7; + } + break; + } + case 16: + { + short volume_16 = (short)(sVolumeLevel * 32767.f); + + __m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16); + __m64 *sample_64; + for (sample_64 = (__m64*)pwh->lpData; + sample_64 < (__m64*)(pwh->lpData + pwh->dwBufferLength); + ++sample_64) + { + __m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64); + *sample_64 = _mm_slli_pi16(scaled_sample, 1); //lose 1 bit of precision here + } + + _mm_empty(); + + 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; + } +} \ No newline at end of file -- cgit v1.3 From 0c643af5289dd20f47b94f4dc7cce1ab9020fba0 Mon Sep 17 00:00:00 2001 From: Richard Nelson Date: Tue, 27 Apr 2010 09:47:33 -0700 Subject: fixed eol and tabs/spaces problems --- indra/media_plugins/winmmshim/CMakeLists.txt | 2 +- indra/media_plugins/winmmshim/forwarding_api.cpp | 2 +- indra/media_plugins/winmmshim/winmm_shim.cpp | 2 +- indra/newview/viewer_manifest.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp') diff --git a/indra/media_plugins/winmmshim/CMakeLists.txt b/indra/media_plugins/winmmshim/CMakeLists.txt index d35e97fc62..387214088f 100644 --- a/indra/media_plugins/winmmshim/CMakeLists.txt +++ b/indra/media_plugins/winmmshim/CMakeLists.txt @@ -29,6 +29,6 @@ if (WINDOWS) winmm_shim PROPERTIES LINK_FLAGS "/MANIFEST:NO" - OUTPUT_NAME "winmm" + OUTPUT_NAME "winmm" ) endif (WINDOWS) diff --git a/indra/media_plugins/winmmshim/forwarding_api.cpp b/indra/media_plugins/winmmshim/forwarding_api.cpp index aaa229fe9d..17bffdd850 100644 --- a/indra/media_plugins/winmmshim/forwarding_api.cpp +++ b/indra/media_plugins/winmmshim/forwarding_api.cpp @@ -1457,4 +1457,4 @@ extern "C" { //OutputDebugString(L"mciExecute\n"); return mciExecute_orig(pszCommand); } -} \ No newline at end of file +} diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp index a46d908962..974744333a 100644 --- a/indra/media_plugins/winmmshim/winmm_shim.cpp +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -175,4 +175,4 @@ extern "C" { sMute = mute; } -} \ No newline at end of file +} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 653dbefa18..668e21c253 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -332,7 +332,7 @@ class WindowsManifest(ViewerManifest): self.path("media_plugin_webkit.dll") self.end_prefix() - # winmm.dll shim + # winmm.dll shim if self.prefix(src='../media_plugins/winmmshim/%s' % self.args['configuration'], dst="llplugin"): self.path("winmm.dll") self.end_prefix() -- cgit v1.3 From 48b3ac20d99e842b6d7adba63e9fa75a7d5cd82c Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Tue, 27 Apr 2010 15:50:51 -0700 Subject: added some documentation to winmm_shim --- indra/media_plugins/winmmshim/forwarding_api.cpp | 6 ++-- indra/media_plugins/winmmshim/winmm_shim.cpp | 38 ++++++++++++++++-------- 2 files changed, 29 insertions(+), 15 deletions(-) (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp') diff --git a/indra/media_plugins/winmmshim/forwarding_api.cpp b/indra/media_plugins/winmmshim/forwarding_api.cpp index 17bffdd850..c2f553dfef 100644 --- a/indra/media_plugins/winmmshim/forwarding_api.cpp +++ b/indra/media_plugins/winmmshim/forwarding_api.cpp @@ -210,9 +210,9 @@ mciGetYieldProc_type mciGetYieldProc_orig; mciLoadCommandResource_type mciLoadCommandResource_orig; mciExecute_type mciExecute_orig; - +// grab pointers to function calls in the real DLL void init_function_pointers(HMODULE winmm_handle) -{ +{ CloseDriver_orig = (CloseDriver_type)::GetProcAddress(winmm_handle, "CloseDriver"); OpenDriver_orig = (OpenDriver_type)::GetProcAddress(winmm_handle, "OpenDriver"); SendDriverMessage_orig = (SendDriverMessage_type)::GetProcAddress(winmm_handle, "SendDriverMessage"); @@ -536,7 +536,7 @@ extern "C" { //MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh) //{ // //OutputDebugString(L"waveOutUnprepareHeader\n"); - // return waveOutUnprepareHeader_orig( hwo, pwh, cbwh); + // return waveOutWrite_orig( hwo, pwh, cbwh); //} MMRESULT WINAPI waveOutPause( HWAVEOUT hwo) diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp index 974744333a..f7df3b19a0 100644 --- a/indra/media_plugins/winmmshim/winmm_shim.cpp +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -45,21 +45,25 @@ BOOL APIENTRY DllMain( HMODULE hModule, ) { 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; @@ -68,6 +72,7 @@ BOOL APIENTRY DllMain( HMODULE hModule, extern "C" { + // tracks the requested format for a given waveout buffer struct WaveOutFormat { WaveOutFormat(int bits_per_sample) @@ -80,15 +85,16 @@ extern "C" MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) { - //OutputDebugString(L"waveOutOpen\n"); - if (pwfx->wFormatTag != WAVE_FORMAT_PCM) - { + 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)); } @@ -97,10 +103,9 @@ extern "C" MMRESULT WINAPI waveOutClose( HWAVEOUT hwo) { - //OutputDebugString(L"waveOutClose\n"); 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); } @@ -109,11 +114,10 @@ extern "C" MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh) { - //OutputDebugString(L"waveOutWrite\n"); MMRESULT result = MMSYSERR_NOERROR; if (sMute) - { + { // zero out the audio buffer when muted memset(pwh->lpData, 0, pwh->dwBufferLength); } else @@ -128,6 +132,8 @@ extern "C" 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; @@ -136,22 +142,30 @@ extern "C" { 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; + // for everything that can be addressed in 64 bit multiples... for (sample_64 = (__m64*)pwh->lpData; sample_64 < (__m64*)(pwh->lpData + pwh->dwBufferLength); ++sample_64) { + //...multiply the samples by the volume... __m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64); - *sample_64 = _mm_slli_pi16(scaled_sample, 1); //lose 1 bit of precision here + // ...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(); for (short* sample_16 = (short*)sample_64; sample_16 < (short*)(pwh->lpData + pwh->dwBufferLength); ++sample_16) - { + { // finish remaining samples that didn't fit into 64 bit register *sample_16 = (*sample_16 * volume_16) >> 15; } -- cgit v1.3 From f1f07e66e3caaf4f633592f7d121c4b938899f49 Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Thu, 29 Apr 2010 18:06:09 -0700 Subject: fixed potential buffer overrun in volume code --- indra/media_plugins/winmmshim/winmm_shim.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp') diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp index f7df3b19a0..54bfa652e9 100644 --- a/indra/media_plugins/winmmshim/winmm_shim.cpp +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -144,10 +144,11 @@ extern "C" // 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* 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 < (__m64*)(pwh->lpData + pwh->dwBufferLength); + sample_64 <= last_sample_64; ++sample_64) { //...multiply the samples by the volume... -- cgit v1.3 From deb2791e55ad1b79fbc44d5d8417301889823ad6 Mon Sep 17 00:00:00 2001 From: Richard Nelson Date: Wed, 28 Apr 2010 18:27:10 -0700 Subject: added optimization for media volume level of 1.0 (don't do anything) --- indra/media_plugins/winmmshim/winmm_shim.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'indra/media_plugins/winmmshim/winmm_shim.cpp') diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp index f7df3b19a0..88c2a28443 100644 --- a/indra/media_plugins/winmmshim/winmm_shim.cpp +++ b/indra/media_plugins/winmmshim/winmm_shim.cpp @@ -120,8 +120,8 @@ extern "C" { // zero out the audio buffer when muted memset(pwh->lpData, 0, pwh->dwBufferLength); } - else - { + 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()) { @@ -162,10 +162,11 @@ extern "C" // 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) - { // finish remaining samples that didn't fit into 64 bit register + { *sample_16 = (*sample_16 * volume_16) >> 15; } -- cgit v1.3