diff options
Diffstat (limited to 'Src/Plugins/Output/out_wasapi')
-rw-r--r-- | Src/Plugins/Output/out_wasapi/api.h | 9 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/main.cpp | 393 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/out_wasapi.rc | 81 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/out_wasapi.sln | 31 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj | 250 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters | 32 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/resource.h | 18 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/version.rc2 | 39 | ||||
-rw-r--r-- | Src/Plugins/Output/out_wasapi/waveformat.cpp | 69 |
9 files changed, 922 insertions, 0 deletions
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 <api/application/api_application.h> +#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 <Mmdeviceapi.h> +#include <Audioclient.h> +#include <Audiosessiontypes.h> +#include "../winamp/wa_ipc.h" +#include <api/service/waServiceFactory.h> +#include <strsafe.h> + +#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<api_language*>(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<void **>( & 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{389AB984-6C9E-40FF-9556-51B80200AE89}</ProjectGuid> + <RootNamespace>out_wasapi</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <SubSystem>Windows</SubSystem> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;OUT_WASAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="waveformat.cpp" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="out_wasapi.rc" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="resource.h" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="waveformat.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{f03738fe-bfa7-4223-9730-d5fd6da53b8e}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{87e33b04-0bd1-46f2-8b54-2f02d76834e9}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{50096f2a-3c40-4451-aa4d-b82937277d9d}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="out_wasapi.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ 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 <mmdeviceapi.h> +#undef INITGUID +#include <mmreg.h> + +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 |