From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/Output/out_wasapi/api.h | 9 + Src/Plugins/Output/out_wasapi/main.cpp | 393 +++++++++++++++++++++ Src/Plugins/Output/out_wasapi/out_wasapi.rc | 81 +++++ Src/Plugins/Output/out_wasapi/out_wasapi.sln | 31 ++ Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj | 250 +++++++++++++ .../Output/out_wasapi/out_wasapi.vcxproj.filters | 32 ++ Src/Plugins/Output/out_wasapi/resource.h | 18 + Src/Plugins/Output/out_wasapi/version.rc2 | 39 ++ Src/Plugins/Output/out_wasapi/waveformat.cpp | 69 ++++ 9 files changed, 922 insertions(+) create mode 100644 Src/Plugins/Output/out_wasapi/api.h create mode 100644 Src/Plugins/Output/out_wasapi/main.cpp create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.rc create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.sln create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters create mode 100644 Src/Plugins/Output/out_wasapi/resource.h create mode 100644 Src/Plugins/Output/out_wasapi/version.rc2 create mode 100644 Src/Plugins/Output/out_wasapi/waveformat.cpp (limited to 'Src/Plugins/Output/out_wasapi') diff --git a/Src/Plugins/Output/out_wasapi/api.h b/Src/Plugins/Output/out_wasapi/api.h new file mode 100644 index 00000000..e07ad62a --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/api.h @@ -0,0 +1,9 @@ +#pragma once + +#include "../Agave/Config/api_config.h" +extern api_config *AGAVE_API_CONFIG; + +#include +#define WASABI_API_APP applicationApi + +#include "../Agave/Language/api_language.h" diff --git a/Src/Plugins/Output/out_wasapi/main.cpp b/Src/Plugins/Output/out_wasapi/main.cpp new file mode 100644 index 00000000..8878cedb --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/main.cpp @@ -0,0 +1,393 @@ +#include "../Winamp/OUT.H" +#include "api.h" +#include "resource.h" +#include +#include +#include +#include "../winamp/wa_ipc.h" +#include +#include + +#define WASAPI_PLUGIN_VERSION L"0.3" + +constexpr auto VolumeLevelMultiplier = 255; +static wchar_t plugin_name[256]; + +// wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static const UINT32 REFTIMES_PER_SEC = 10000000; +static const UINT32 REFTIMES_PER_MILLISEC = 10000; + +// TODO(benski) is there some library that has this +static const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +static const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +static const IID IID_IAudioClient = __uuidof(IAudioClient); +static const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); +static const IID IID_IAudioClock = __uuidof(IAudioClock); + +static bool InitializedCOM; +extern Out_Module plugin; + +static IAudioClient *client=0; +static IAudioRenderClient *render_client=0; +static IAudioClock *clock=0; +static ISimpleAudioVolume *audio_volume = 0; +static IChannelAudioVolume *channel_volume = 0; + +static UINT32 bufferFrameCount; +static WORD bytes_per_frame; +static UINT64 frequency=0; +static UINT32 sample_rate; +static double start_time_ms = 0; +static bool paused=false; +static float start_volume = 1.0; +static float start_pan = 0; +WAVEFORMATEXTENSIBLE WaveFormatForParameters(int samplerate, int numchannels, int bitspersamp); +static void SetVolume(int volume); +static void SetPan(int pan); +static CRITICAL_SECTION ThreadSync; + + +static void Config(HWND hwndParent) +{ +} + +static int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message) +{ + MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0}; + msgbx.lpszText = message; + msgbx.lpszCaption = title; + msgbx.lpszIcon = MAKEINTRESOURCEW(102); + msgbx.hInstance = GetModuleHandle(0); + msgbx.dwStyle = MB_USERICON; + msgbx.hwndOwner = parent; + return MessageBoxIndirectW(&msgbx); +} + +static void About(HWND hwndParent) +{ + wchar_t message[1024], text[1024] =L""; + WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_WASAPI_OLD,text,1024); + StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT), + plugin.description, TEXT(__DATE__)); + DoAboutMessageBox(hwndParent,text,message); +} + +static void Init() +{ + + /* + HRESULT hr; + hr=CoInitializeEx(0, COINIT_MULTITHREADED); + if (SUCCEEDED(hr)) { + InitializedCOM = true; + } else { + InitializedCOM = false; + } + */ + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(plugin.hDllInstance,OutWasapiLangGUID); + + StringCbPrintfW(plugin_name,sizeof(plugin_name),WASABI_API_LNGSTRINGW(IDS_NULLSOFT_WASAPI), WASAPI_PLUGIN_VERSION); + plugin.description = (char *)plugin_name; +} + +static void Quit() +{ + /* + if (InitializedCOM) { + CoUninitialize(); + }*/ +} + +static int Open(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms) +{ + + CoInitialize(0); + IMMDeviceEnumerator *enumerator=0; + IMMDevice *device=0; + sample_rate = samplerate; + HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator); + hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device); + + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&client); + if (FAILED(hr)) { + wchar_t temp[1234]; + wsprintf(temp, L"device->Activate: %x", hr); + ::MessageBox(NULL, temp, L"error", MB_OK); + return -1; + } + + WAVEFORMATEXTENSIBLE wave_format = WaveFormatForParameters(samplerate, numchannels, bitspersamp); + bytes_per_frame = wave_format.Format.nBlockAlign; + + hr = client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + 0x80000000, + 1 * REFTIMES_PER_SEC, + 0, + (WAVEFORMATEX *)&wave_format, + NULL); + if (FAILED(hr)) { + wchar_t temp[1234]; + wsprintf(temp, L"client->Initialize: %x", hr); + ::MessageBox(NULL, temp, L"error", MB_OK); + return -1; + } + + // Get the actual size of the allocated buffer. + hr = client->GetBufferSize(&bufferFrameCount); + if (FAILED(hr)) { + wchar_t temp[1234]; + wsprintf(temp, L"client->GetBufferSize: %x", hr); + ::MessageBox(NULL, temp, L"error", MB_OK); + return -1; + } + hr = client->GetService( + IID_IAudioRenderClient, + (void**)&render_client); + if (FAILED(hr)) { + wchar_t temp[1234]; + wsprintf(temp, L"client->GetService(IID_IAudioRenderClient): %x", hr); + ::MessageBox(NULL, temp, L"error", MB_OK); + return -1; + } + hr = client->GetService( + IID_IAudioClock, + (void**)&clock); + if (FAILED(hr)) { + wchar_t temp[1234]; + wsprintf(temp, L"client->GetService(IID_IAudioClock): %x", hr); + ::MessageBox(NULL, temp, L"error", MB_OK); + return -1; + } + hr = clock->GetFrequency(&frequency); + + hr = client->GetService(__uuidof(ISimpleAudioVolume), reinterpret_cast( & audio_volume)); + + hr = client->GetService(__uuidof(IChannelAudioVolume), (void **)&channel_volume); + + start_time_ms = 0; + paused=false; + client->Start(); + + // Start volume is in range 0.0 to 1.0, should be converted + SetVolume((int)(start_volume * VolumeLevelMultiplier)); + + SetPan((int)start_pan); + + return 1000; +} + +static void Close() +{ + + if (client) { + client->Stop(); + client->Release(); + client=0; + } + + if (render_client) { + render_client->Release(); + render_client=0; + } + + if (clock) { + clock->Release(); + clock=0; + } + + if (audio_volume) { + audio_volume->Release(); + audio_volume=0; + } + + if (channel_volume) { + channel_volume->Release(); + channel_volume=0; + } + +} + +static int CanWrite() +{ + + if (client) { + UINT32 numFramesPadding; + HRESULT hr = client->GetCurrentPadding(&numFramesPadding); + return (bufferFrameCount - numFramesPadding) * bytes_per_frame; + } + else { + return 0; + } +} + +static int Write(char* buf, int len) +{ + + if (!render_client) + { + return -1; + } + else + { + int LenghtToWrite = CanWrite(); + if (LenghtToWrite > 0 && LenghtToWrite >= len) + { + BYTE* data; + render_client->GetBuffer(len / bytes_per_frame, &data); + memcpy(data, buf, len); + render_client->ReleaseBuffer(len / bytes_per_frame, 0); + } + } + + return 0; + +} + + + +static int IsPlaying() +{ + return CanWrite() == 0; +} + +static int Pause(int pause) +{ + + + int old_paused = paused?1:0; + if (client) { + if (pause) { + client->Stop(); + paused=true; + } else { + client->Start(); + paused=false; + } + } + + return old_paused; +} + +static void SetVolume(int volume) +{ + + + float fVolume = (float)volume / (float)VolumeLevelMultiplier; + if (volume >= 0) { + start_volume = fVolume; + if (audio_volume) { + audio_volume->SetMasterVolume(fVolume, 0); + } + } + +} + +static void SetPan(int pan) +{ + + + float fPan = (float)pan/128.0f; + if (channel_volume) { + start_pan = fPan; + if (fPan < 0) { + channel_volume->SetChannelVolume(0, 1.0f, NULL); + channel_volume->SetChannelVolume(1, 1.0f-fPan, NULL); + } else if (fPan > 0) { + channel_volume->SetChannelVolume(1, 1.0f, NULL); + channel_volume->SetChannelVolume(0, 1.0f-fPan, NULL); + } + } + +} + +static void Flush(int t) +{ + + + if (client) { + client->Stop(); + client->Reset(); + start_time_ms = t; + client->Start(); + } + +} + +static double GetOutputTimeAsDouble() +{ + + + if (clock) { + UINT64 position; + HRESULT hr = clock->GetPosition(&position, NULL); + double output_time = (double)position * 1000.0 / (double)frequency; + + return output_time + start_time_ms; + } else { + + return 0; + } +} + +static int GetOutputTime() +{ + return (int)GetOutputTimeAsDouble(); +} + +static int GetWrittenTime() +{ + double time_in_buffer = (1000.0 * (double)CanWrite()) / ((double)bytes_per_frame * (double)sample_rate); + return (int)(GetOutputTimeAsDouble() + time_in_buffer); +} + +Out_Module plugin = { + OUT_VER_U, + 0, + 70, + NULL, + NULL, + Config, + About, + Init, + Quit, + Open, + Close, + Write, + CanWrite, + IsPlaying, + Pause, + SetVolume, + SetPan, + Flush, + GetOutputTime, + GetWrittenTime, +}; + + +extern "C" { + + __declspec(dllexport) int __cdecl winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) + { + return OUT_PLUGIN_UNINSTALL_REBOOT; + } + + __declspec(dllexport) Out_Module * __cdecl winampGetOutModule(){ return &plugin; } + + __declspec(dllexport) void __cdecl winampGetOutModeChange(int mode) + { + } + +} diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.rc b/Src/Plugins/Output/out_wasapi/out_wasapi.rc new file mode 100644 index 00000000..1e0e163d --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/out_wasapi.rc @@ -0,0 +1,81 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_WASAPI "Nullsoft WASAPI Output v%s" + 65535 "{CA8AD152-9760-4AA3-AC54-7464279C5D63}" +END + +STRINGTABLE +BEGIN + IDS_NULLSOFT_WASAPI_OLD "Nullsoft WASAPI Output" + IDS_ABOUT_TEXT "%s\nWritten by Ben Allison\n© 2015-2023 Ben Allison\nBuild date: %s" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.sln b/Src/Plugins/Output/out_wasapi/out_wasapi.sln new file mode 100644 index 00000000..2997f103 --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/out_wasapi.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "out_wasapi", "out_wasapi.vcxproj", "{389AB984-6C9E-40FF-9556-51B80200AE89}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|Win32.ActiveCfg = Debug|Win32 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|Win32.Build.0 = Debug|Win32 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|Win32.ActiveCfg = Release|Win32 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|Win32.Build.0 = Release|Win32 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|x64.ActiveCfg = Debug|x64 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Debug|x64.Build.0 = Debug|x64 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|x64.ActiveCfg = Release|x64 + {389AB984-6C9E-40FF-9556-51B80200AE89}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FD99919C-E1E8-4534-BAF6-4C7E97120250} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj new file mode 100644 index 00000000..56b614f0 --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj @@ -0,0 +1,250 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {389AB984-6C9E-40FF-9556-51B80200AE89} + out_wasapi + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + + + false + Debug + x86-windows-static-md + + + + + false + x86-windows-static-md + + + + + false + x86-windows-static-md + Debug + + + + + false + x86-windows-static-md + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + MachineX86 + false + %(AdditionalLibraryDirectories) + $(IntDir)$(TargetName).pdb + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + false + %(AdditionalLibraryDirectories) + $(IntDir)$(TargetName).pdb + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + true + WIN64;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters new file mode 100644 index 00000000..637a2ca0 --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + Source Files + + + Source Files + + + + + {f03738fe-bfa7-4223-9730-d5fd6da53b8e} + + + {87e33b04-0bd1-46f2-8b54-2f02d76834e9} + + + {50096f2a-3c40-4451-aa4d-b82937277d9d} + + + + + Ressource Files + + + + + Header Files + + + \ No newline at end of file diff --git a/Src/Plugins/Output/out_wasapi/resource.h b/Src/Plugins/Output/out_wasapi/resource.h new file mode 100644 index 00000000..47e1cfa2 --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/resource.h @@ -0,0 +1,18 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by out_wasapi.rc +// +#define IDS_NULLSOFT_WASAPI_OLD 0 +#define IDS_ABOUT_TEXT 2 +#define IDS_NULLSOFT_WASAPI 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 3 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Output/out_wasapi/version.rc2 b/Src/Plugins/Output/out_wasapi/version.rc2 new file mode 100644 index 00000000..d00d644d --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,03,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "" + VALUE "FileDescription", "Winamp Output Plug-in" + VALUE "FileVersion", "0,03,0,0" + VALUE "InternalName", "Nullsoft WASAPI Output" + VALUE "LegalCopyright", "Copyright © 2015-2023 Ben Allison" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "out_wasapi.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Output/out_wasapi/waveformat.cpp b/Src/Plugins/Output/out_wasapi/waveformat.cpp new file mode 100644 index 00000000..2f4b8009 --- /dev/null +++ b/Src/Plugins/Output/out_wasapi/waveformat.cpp @@ -0,0 +1,69 @@ +#define INITGUID +#include +#undef INITGUID +#include + +static WAVEFORMATEXTENSIBLE format_24_stereo = +{ + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 705600, + 8, + 32, + 22, + }, + 24, + SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT, + KSDATAFORMAT_SUBTYPE_PCM +}; + +static WORD BitsPerSampleContainer(int bitspersamp) +{ + if (bitspersamp <= 8) { + return 8; + } else if (bitspersamp <= 16) { + return 16; + } else if (bitspersamp <= 24) { + return 24; + } else { + return 32; + } +} + +static WORD ChannelMask(int numchannels) +{ + switch(numchannels) { + case 1: + return SPEAKER_FRONT_CENTER; + case 2: + return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT; + case 4: // quadraphonic + return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT; + case 6: // 5.1 + return SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY; + default: + return 0; + } +} + +WAVEFORMATEXTENSIBLE WaveFormatForParameters(int samplerate, int numchannels, int bitspersamp) +{ + WAVEFORMATEXTENSIBLE wave_format; + wave_format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; /* format type */ + wave_format.Format.nChannels = numchannels; /* number of channels (i.e. mono, stereo...) */ + wave_format.Format.nSamplesPerSec = samplerate; /* sample rate */ + wave_format.Format.nAvgBytesPerSec = BitsPerSampleContainer(bitspersamp) * numchannels * samplerate / 8; /* for buffer estimation */ + wave_format.Format.nBlockAlign = BitsPerSampleContainer(bitspersamp) * numchannels / 8; /* block size of data */ + wave_format.Format.wBitsPerSample = BitsPerSampleContainer(bitspersamp); + wave_format.Format.cbSize = 22; /* the count in bytes of the size of */ + /* extra information (after cbSize) */ + + wave_format.Samples.wValidBitsPerSample = bitspersamp; + + wave_format.dwChannelMask = ChannelMask(numchannels); + wave_format.SubFormat=KSDATAFORMAT_SUBTYPE_PCM; + + return wave_format; +} \ No newline at end of file -- cgit