This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -7,10 +7,16 @@

#include "AudioCommon/CubebUtils.h"
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/Thread.h"
#include "Core/Config/MainSettings.h"

#ifdef _WIN32
#include <Objbase.h>
#endif

// ~10 ms - needs to be at least 240 for surround
constexpr u32 BUFFER_SAMPLES = 512;

@@ -31,55 +37,129 @@ void CubebStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_sta
{
}

bool CubebStream::Init()
CubebStream::CubebStream()
#ifdef _WIN32
: m_work_queue([](const std::function<void()>& func) { func(); })
{
m_ctx = CubebUtils::GetContext();
if (!m_ctx)
return false;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
auto result = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
m_coinit_success = result == S_OK;
});
sync_event.Wait();
}
#else
= default;
#endif

m_stereo = !Config::ShouldUseDPL2Decoder();
bool CubebStream::Init()
{
bool return_value = false;

cubeb_stream_params params;
params.rate = m_mixer->GetSampleRate();
if (m_stereo)
{
params.channels = 2;
params.format = CUBEB_SAMPLE_S16NE;
params.layout = CUBEB_LAYOUT_STEREO;
}
else
{
params.channels = 6;
params.format = CUBEB_SAMPLE_FLOAT32NE;
params.layout = CUBEB_LAYOUT_3F2_LFE;
}

u32 minimum_latency = 0;
if (cubeb_get_min_latency(m_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
ERROR_LOG_FMT(AUDIO, "Error getting minimum latency");
INFO_LOG_FMT(AUDIO, "Minimum latency: {} frames", minimum_latency);

return cubeb_stream_init(m_ctx.get(), &m_stream, "Dolphin Audio Output", nullptr, nullptr,
nullptr, &params, std::max(BUFFER_SAMPLES, minimum_latency),
DataCallback, StateCallback, this) == CUBEB_OK;
#ifdef _WIN32
if (!m_coinit_success)
return false;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &return_value, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif

m_ctx = CubebUtils::GetContext();
if (m_ctx)
{
m_stereo = !Config::ShouldUseDPL2Decoder();

cubeb_stream_params params{};
params.rate = m_mixer->GetSampleRate();
if (m_stereo)
{
params.channels = 2;
params.format = CUBEB_SAMPLE_S16NE;
params.layout = CUBEB_LAYOUT_STEREO;
}
else
{
params.channels = 6;
params.format = CUBEB_SAMPLE_FLOAT32NE;
params.layout = CUBEB_LAYOUT_3F2_LFE;
}

u32 minimum_latency = 0;
if (cubeb_get_min_latency(m_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
ERROR_LOG_FMT(AUDIO, "Error getting minimum latency");
INFO_LOG_FMT(AUDIO, "Minimum latency: {} frames", minimum_latency);

return_value =
cubeb_stream_init(m_ctx.get(), &m_stream, "Dolphin Audio Output", nullptr, nullptr,
nullptr, &params, std::max(BUFFER_SAMPLES, minimum_latency),
DataCallback, StateCallback, this) == CUBEB_OK;
}

#ifdef _WIN32
});
sync_event.Wait();
#endif

return return_value;
}

bool CubebStream::SetRunning(bool running)
{
if (running)
return cubeb_stream_start(m_stream) == CUBEB_OK;
else
return cubeb_stream_stop(m_stream) == CUBEB_OK;
bool return_value = false;

#ifdef _WIN32
if (!m_coinit_success)
return false;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
if (running)
return_value = cubeb_stream_start(m_stream) == CUBEB_OK;
else
return_value = cubeb_stream_stop(m_stream) == CUBEB_OK;
#ifdef _WIN32
});
sync_event.Wait();
#endif

return return_value;
}

CubebStream::~CubebStream()
{
SetRunning(false);
cubeb_stream_destroy(m_stream);
#ifdef _WIN32
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
cubeb_stream_stop(m_stream);
cubeb_stream_destroy(m_stream);
#ifdef _WIN32
if (m_coinit_success)
{
m_coinit_success = false;
CoUninitialize();
}
});
sync_event.Wait();
#endif
m_ctx.reset();
}

void CubebStream::SetVolume(int volume)
{
cubeb_stream_set_volume(m_stream, volume / 100.0f);
#ifdef _WIN32
if (!m_coinit_success)
return;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, volume, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
cubeb_stream_set_volume(m_stream, volume / 100.0f);
#ifdef _WIN32
});
sync_event.Wait();
#endif
}
@@ -4,16 +4,23 @@
#pragma once

#include <cstddef>
#include <functional>
#include <memory>
#include <vector>

#include "AudioCommon/SoundStream.h"
#include "Common/WorkQueueThread.h"

#include <cubeb/cubeb.h>

class CubebStream final : public SoundStream
{
public:
CubebStream();
CubebStream(const CubebStream& other) = delete;
CubebStream(CubebStream&& other) = delete;
CubebStream& operator=(const CubebStream& other) = delete;
CubebStream& operator=(CubebStream&& other) = delete;
~CubebStream() override;
bool Init() override;
bool SetRunning(bool running) override;
@@ -27,6 +34,11 @@ class CubebStream final : public SoundStream
std::vector<short> m_short_buffer;
std::vector<float> m_floatstereo_buffer;

#ifdef _WIN32
Common::WorkQueueThread<std::function<void()>> m_work_queue;
bool m_coinit_success = false;
#endif

static long DataCallback(cubeb_stream* stream, void* user_data, const void* /*input_buffer*/,
void* output_buffer, long num_frames);
static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
@@ -6,7 +6,9 @@
#include <cstdarg>
#include <cstddef>
#include <cstring>
#include <thread>

#include "Common/Assert.h"
#include "Common/CommonPaths.h"
#include "Common/Logging/Log.h"
#include "Common/Logging/LogManager.h"
@@ -3,6 +3,7 @@

#pragma once

#include <functional>
#include <memory>

struct cubeb;
@@ -12,27 +12,59 @@
#include "AudioCommon/CubebUtils.h"
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"

#include "Core/CoreTiming.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/SystemTimers.h"

#ifdef _WIN32
#include <Objbase.h>
#endif

namespace ExpansionInterface
{
void CEXIMic::StreamInit()
{
m_cubeb_ctx = CubebUtils::GetContext();

stream_buffer = nullptr;
samples_avail = stream_wpos = stream_rpos = 0;

#ifdef _WIN32
if (!m_coinit_success)
return;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
m_cubeb_ctx = CubebUtils::GetContext();
#ifdef _WIN32
});
sync_event.Wait();
#endif
}

void CEXIMic::StreamTerminate()
{
StreamStop();
m_cubeb_ctx.reset();

if (m_cubeb_ctx)
{
#ifdef _WIN32
if (!m_coinit_success)
return;
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
m_cubeb_ctx.reset();
#ifdef _WIN32
});
sync_event.Wait();
#endif
}
}

static void state_callback(cubeb_stream* stream, void* user_data, cubeb_state state)
@@ -68,48 +100,68 @@ void CEXIMic::StreamStart()
if (!m_cubeb_ctx)
return;

// Open stream with current parameters
stream_size = buff_size_samples * 500;
stream_buffer = new s16[stream_size];

cubeb_stream_params params;
params.format = CUBEB_SAMPLE_S16LE;
params.rate = sample_rate;
params.channels = 1;
params.layout = CUBEB_LAYOUT_MONO;

u32 minimum_latency;
if (cubeb_get_min_latency(m_cubeb_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
{
WARN_LOG_FMT(EXPANSIONINTERFACE, "Error getting minimum latency");
}

if (cubeb_stream_init(m_cubeb_ctx.get(), &m_cubeb_stream, "Dolphin Emulated GameCube Microphone",
nullptr, &params, nullptr, nullptr,
std::max<u32>(buff_size_samples, minimum_latency), DataCallback,
state_callback, this) != CUBEB_OK)
{
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error initializing cubeb stream");
#ifdef _WIN32
if (!m_coinit_success)
return;
}
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
// Open stream with current parameters
stream_size = buff_size_samples * 500;
stream_buffer = new s16[stream_size];

cubeb_stream_params params{};
params.format = CUBEB_SAMPLE_S16LE;
params.rate = sample_rate;
params.channels = 1;
params.layout = CUBEB_LAYOUT_MONO;

u32 minimum_latency;
if (cubeb_get_min_latency(m_cubeb_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
{
WARN_LOG_FMT(EXPANSIONINTERFACE, "Error getting minimum latency");
}

if (cubeb_stream_start(m_cubeb_stream) != CUBEB_OK)
{
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error starting cubeb stream");
return;
}
if (cubeb_stream_init(m_cubeb_ctx.get(), &m_cubeb_stream,
"Dolphin Emulated GameCube Microphone", nullptr, &params, nullptr,
nullptr, std::max<u32>(buff_size_samples, minimum_latency), DataCallback,
state_callback, this) != CUBEB_OK)
{
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error initializing cubeb stream");
return;
}

if (cubeb_stream_start(m_cubeb_stream) != CUBEB_OK)
{
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error starting cubeb stream");
return;
}

INFO_LOG_FMT(EXPANSIONINTERFACE, "started cubeb stream");
INFO_LOG_FMT(EXPANSIONINTERFACE, "started cubeb stream");
#ifdef _WIN32
});
sync_event.Wait();
#endif
}

void CEXIMic::StreamStop()
{
if (m_cubeb_stream)
{
if (cubeb_stream_stop(m_cubeb_stream) != CUBEB_OK)
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error stopping cubeb stream");
cubeb_stream_destroy(m_cubeb_stream);
m_cubeb_stream = nullptr;
#ifdef _WIN32
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
#endif
if (cubeb_stream_stop(m_cubeb_stream) != CUBEB_OK)
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Error stopping cubeb stream");
cubeb_stream_destroy(m_cubeb_stream);
m_cubeb_stream = nullptr;
#ifdef _WIN32
});
sync_event.Wait();
#endif
}

samples_avail = stream_wpos = stream_rpos = 0;
@@ -143,7 +195,12 @@ void CEXIMic::StreamReadOne()

u8 const CEXIMic::exi_id[] = {0, 0x0a, 0, 0, 0};

CEXIMic::CEXIMic(int index) : slot(index)
CEXIMic::CEXIMic(int index)
: slot(index)
#ifdef _WIN32
,
m_work_queue([](const std::function<void()>& func) { func(); })
#endif
{
m_position = 0;
command = 0;
@@ -158,6 +215,16 @@ CEXIMic::CEXIMic(int index) : slot(index)

next_int_ticks = 0;

#ifdef _WIN32
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
auto result = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
m_coinit_success = result == S_OK;
});
sync_event.Wait();
#endif

StreamInit();
}

@@ -6,6 +6,7 @@
#include <mutex>

#include "Common/CommonTypes.h"
#include "Common/WorkQueueThread.h"
#include "Core/HW/EXI/EXI_Device.h"

struct cubeb;
@@ -99,5 +100,10 @@ class CEXIMic : public IEXIDevice
int stream_wpos;
int stream_rpos;
int samples_avail;

#ifdef _WIN32
Common::WorkQueueThread<std::function<void()>> m_work_queue;
bool m_coinit_success = false;
#endif
};
} // namespace ExpansionInterface
@@ -71,7 +71,7 @@
</ClCompile>
<!--Link Base:Application-->
<Link Condition="'$(ConfigurationType)'=='Application'">
<AdditionalDependencies>avrt.lib;comctl32.lib;iphlpapi.lib;setupapi.lib;shlwapi.lib;winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>avrt.lib;comctl32.lib;iphlpapi.lib;ksuser.lib;setupapi.lib;shlwapi.lib;winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='x64'">opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!--FFmpeg and the libs it pulls in-->
<AdditionalLibraryDirectories>$(ExternalsDir)FFmpeg-bin\$(Platform)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>