aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Output/out_wasapi
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Output/out_wasapi')
-rw-r--r--Src/Plugins/Output/out_wasapi/api.h9
-rw-r--r--Src/Plugins/Output/out_wasapi/main.cpp393
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.rc81
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.sln31
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj250
-rw-r--r--Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters32
-rw-r--r--Src/Plugins/Output/out_wasapi/resource.h18
-rw-r--r--Src/Plugins/Output/out_wasapi/version.rc239
-rw-r--r--Src/Plugins/Output/out_wasapi/waveformat.cpp69
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