PageRenderTime 34ms CodeModel.GetById 22ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1
  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. using std::wstring;
  31. static float sVolumeLevel = 1.f;
  32. static bool sMute = false;
  33. static CRITICAL_SECTION sCriticalSection;
  34. BOOL APIENTRY DllMain( HMODULE hModule,
  35. DWORD ul_reason_for_call,
  36. LPVOID lpReserved
  37. )
  38. {
  39. InitializeCriticalSection(&sCriticalSection);
  40. return TRUE;
  41. }
  42. void ll_winmm_shim_initialize(){
  43. static bool initialized = false;
  44. // do this only once
  45. EnterCriticalSection(&sCriticalSection);
  46. if (!initialized)
  47. { // bind to original winmm.dll
  48. TCHAR system_path[MAX_PATH];
  49. TCHAR dll_path[MAX_PATH];
  50. ::GetSystemDirectory(system_path, MAX_PATH);
  51. // grab winmm.dll from system path, where it should live
  52. wsprintf(dll_path, "%s\\winmm.dll", system_path);
  53. HMODULE winmm_handle = ::LoadLibrary(dll_path);
  54. if (winmm_handle != NULL)
  55. { // we have a dll, let's get out pointers!
  56. initialized = true;
  57. init_function_pointers(winmm_handle);
  58. ::OutputDebugStringA("WINMM_SHIM.DLL: real winmm.dll initialized successfully\n");
  59. }
  60. else
  61. {
  62. // failed to initialize real winmm.dll
  63. ::OutputDebugStringA("WINMM_SHIM.DLL: Failed to initialize real winmm.dll\n");
  64. }
  65. }
  66. LeaveCriticalSection(&sCriticalSection);
  67. }
  68. extern "C"
  69. {
  70. // tracks the requested format for a given waveout buffer
  71. struct WaveOutFormat
  72. {
  73. WaveOutFormat(int bits_per_sample)
  74. : mBitsPerSample(bits_per_sample)
  75. {}
  76. int mBitsPerSample;
  77. };
  78. typedef std::map<HWAVEOUT, WaveOutFormat*> wave_out_map_t;
  79. static wave_out_map_t sWaveOuts;
  80. MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
  81. {
  82. ll_winmm_shim_initialize();
  83. if (pwfx->wFormatTag != WAVE_FORMAT_PCM
  84. || (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16))
  85. { // uncompressed 8 and 16 bit sound are the only types we support
  86. return WAVERR_BADFORMAT;
  87. }
  88. MMRESULT result = waveOutOpen_orig(phwo, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen);
  89. if (result == MMSYSERR_NOERROR
  90. && ((fdwOpen & WAVE_FORMAT_QUERY) == 0)) // not just querying for format support
  91. { // remember the requested bits per sample, and associate with the given handle
  92. WaveOutFormat* wave_outp = new WaveOutFormat(pwfx->wBitsPerSample);
  93. sWaveOuts.insert(std::make_pair(*phwo, wave_outp));
  94. }
  95. return result;
  96. }
  97. MMRESULT WINAPI waveOutClose( HWAVEOUT hwo)
  98. {
  99. ll_winmm_shim_initialize();
  100. wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
  101. if (found_it != sWaveOuts.end())
  102. { // forget what we know about this handle
  103. delete found_it->second;
  104. sWaveOuts.erase(found_it);
  105. }
  106. return waveOutClose_orig( hwo);
  107. }
  108. MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
  109. {
  110. ll_winmm_shim_initialize();
  111. MMRESULT result = MMSYSERR_NOERROR;
  112. if (sMute)
  113. { // zero out the audio buffer when muted
  114. memset(pwh->lpData, 0, pwh->dwBufferLength);
  115. }
  116. else if (sVolumeLevel != 1.f)
  117. { // need to apply volume level
  118. wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
  119. if (found_it != sWaveOuts.end())
  120. {
  121. WaveOutFormat* formatp = found_it->second;
  122. switch (formatp->mBitsPerSample){
  123. case 8:
  124. {
  125. char volume = (char)(sVolumeLevel * 127.f);
  126. for (unsigned int i = 0; i < pwh->dwBufferLength; i++)
  127. {
  128. // unsigned multiply doesn't use most significant bit, so shift by 7 bits
  129. // to get resulting value back into 8 bits
  130. pwh->lpData[i] = (pwh->lpData[i] * volume) >> 7;
  131. }
  132. break;
  133. }
  134. case 16:
  135. {
  136. short volume_16 = (short)(sVolumeLevel * 32767.f);
  137. // copy volume level 4 times into 64 bit MMX register
  138. __m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16);
  139. __m64* sample_64;
  140. __m64* last_sample_64 = (__m64*)(pwh->lpData + pwh->dwBufferLength - sizeof(__m64));
  141. // for everything that can be addressed in 64 bit multiples...
  142. for (sample_64 = (__m64*)pwh->lpData;
  143. sample_64 <= last_sample_64;
  144. ++sample_64)
  145. {
  146. //...multiply the samples by the volume...
  147. __m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64);
  148. // ...and shift left 1 bit since an unsigned multiple loses the most significant bit
  149. // 0x7FFF * 0x7FFF = 0x3fff0001
  150. // 0x3fff0001 << 1 = 0x7ffe0002
  151. // notice that the LSB is always 0...should consider dithering
  152. *sample_64 = _mm_slli_pi16(scaled_sample, 1);
  153. }
  154. // the captain has turned off the MMX sign, you are now free to use floating point registers
  155. _mm_empty();
  156. // finish remaining samples that didn't fit into 64 bit register
  157. for (short* sample_16 = (short*)sample_64;
  158. sample_16 < (short*)(pwh->lpData + pwh->dwBufferLength);
  159. ++sample_16)
  160. {
  161. *sample_16 = (*sample_16 * volume_16) >> 15;
  162. }
  163. break;
  164. }
  165. default:
  166. // don't do anything
  167. break;
  168. }
  169. }
  170. }
  171. return waveOutWrite_orig( hwo, pwh, cbwh);
  172. }
  173. void WINAPI setPluginVolume(float volume)
  174. {
  175. sVolumeLevel = volume;
  176. }
  177. void WINAPI setPluginMute(bool mute)
  178. {
  179. sMute = mute;
  180. }
  181. }