Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
393
Src/Plugins/Output/out_wasapi/main.cpp
Normal file
393
Src/Plugins/Output/out_wasapi/main.cpp
Normal file
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue