Skip to content

Commit

Permalink
Convert to cubeb
Browse files Browse the repository at this point in the history
  • Loading branch information
noahpistilli committed Feb 7, 2024
1 parent f43f3a0 commit c308aa8
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 161 deletions.
191 changes: 167 additions & 24 deletions Source/Core/Core/IOS/USB/Emulated/Microphone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,180 @@
// Created by Noah Pistilli on 2023-07-09.
//

#include <cubeb/cubeb.h>

#include "AudioCommon/CubebUtils.h"
#include <Common/Logging/Log.h>

#include "Microphone.h"
#include "Common/swap.h"
#include "Common/Swap.h"

#include <algorithm>
#include <mutex>

namespace IOS::HLE::USB
{
std::vector<std::string> Microphone::ListDevices()
Microphone::Microphone() {
StreamInit();
}

Microphone::~Microphone() {
StreamTerminate();

#ifdef _WIN32
if (m_should_couninit)
{
Common::Event sync_event;
m_work_queue.EmplaceItem([this, &sync_event] {
Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); });
m_should_couninit = false;
CoUninitialize();
});
sync_event.Wait();
}
m_coinit_success = false;
#endif
}

void Microphone::StreamInit()
{
#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

// TODO: Not here but rather inside the WiiSpeak device if possible?
StreamStart();
}

void Microphone::StreamTerminate()
{
std::vector<std::string> devices{};
const ALchar* pDeviceList = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER);
while (*pDeviceList)
StopStream();

if (m_cubeb_ctx)
{
devices.emplace_back(pDeviceList);
pDeviceList += strlen(pDeviceList) + 1;
#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
}
}

return devices;
static void state_callback(cubeb_stream* stream, void* user_data, cubeb_state state)
{
}

int Microphone::OpenMicrophone()
void Microphone::StreamStart()
{
m_device = alcCaptureOpenDevice(nullptr, SAMPLING_RATE, AL_FORMAT_MONO16, BUFFER_SIZE);
m_dsp_data.resize(BUFFER_SIZE, 0);
m_temp_buffer.resize(BUFFER_SIZE, 0);
return static_cast<int>(alcGetError(m_device));
if (!m_cubeb_ctx)
return;

#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
stream_size = buff_size_samples * 500;
stream_buffer = new s16[stream_size];

cubeb_stream_params params{};
params.format = CUBEB_SAMPLE_S16LE;
params.rate = SAMPLING_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>(16, minimum_latency), DataCallback,
state_callback, this) != CUBEB_OK)
{
ERROR_LOG_FMT(IOS_USB, "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");
#ifdef _WIN32
});
sync_event.Wait();
#endif
}

int Microphone::StartCapture()
void Microphone::StopStream()
{
alcCaptureStart(m_device);
return static_cast<int>(alcGetError(m_device));
if (m_cubeb_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
if (cubeb_stream_stop(m_cubeb_stream) != CUBEB_OK)
ERROR_LOG_FMT(IOS_USB, "Error stopping cubeb stream");
cubeb_stream_destroy(m_cubeb_stream);
m_cubeb_stream = nullptr;
#ifdef _WIN32
});
sync_event.Wait();
#endif
}
}

void Microphone::StopCapture()
long Microphone::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* /*output_buffer*/, long nframes)
{
alcCaptureStop(m_device);
auto* mic = static_cast<Microphone*>(user_data);

std::lock_guard lk(mic->ring_lock);

const s16* buff_in = static_cast<const s16*>(input_buffer);
for (long i = 0; i < nframes; i++)
{
mic->stream_buffer[mic->stream_wpos] = Common::swap16(buff_in[i]);
mic->stream_wpos = (mic->stream_wpos + 1) % mic->stream_size;
}

mic->samples_avail += nframes;
if (mic->samples_avail > mic->stream_size)
{
mic->samples_avail = 0;
}

return nframes;
}

void Microphone::PerformAudioCapture()
{
m_num_of_samples = BUFFER_SIZE / 2;
/*m_num_of_samples = BUFFER_SIZE / 2;
ALCint samples_in{};
alcGetIntegerv(m_device, ALC_CAPTURE_SAMPLES, 1, &samples_in);
Expand All @@ -52,7 +184,7 @@ void Microphone::PerformAudioCapture()
if (m_num_of_samples == 0)
return;
alcCaptureSamples(m_device, m_dsp_data.data(), m_num_of_samples);
alcCaptureSamples(m_device, m_dsp_data.data(), m_num_of_samples);*/
}

void Microphone::ByteSwap(const void* src, void* dst) const
Expand All @@ -62,7 +194,7 @@ void Microphone::ByteSwap(const void* src, void* dst) const

void Microphone::GetSoundData()
{
if (m_num_of_samples == 0)
/*if (m_num_of_samples == 0)
return;
u8* ptr = const_cast<u8*>(m_temp_buffer.data());
Expand All @@ -76,16 +208,27 @@ void Microphone::GetSoundData()
}
}
m_rbuf_dsp.write_bytes(ptr, m_num_of_samples * 2);
m_rbuf_dsp.write_bytes(ptr, m_num_of_samples * 2);*/
}

void Microphone::ReadIntoBuffer(u8* dst, u32 size)
{
m_rbuf_dsp.read_bytes(dst, size);
std::lock_guard lk(ring_lock);

if (samples_avail >= buff_size_samples)
{
u8* last_buffer = reinterpret_cast<u8*>(&stream_buffer[stream_rpos]);
std::memcpy(dst, static_cast<u8*>(last_buffer), size);

samples_avail -= buff_size_samples;

stream_rpos += buff_size_samples;
stream_rpos %= stream_size;
}
}

bool Microphone::HasData() const
{
return m_num_of_samples != 0;
return samples_avail > 0;
}
} // namespace IOS::HLE::USB
109 changes: 29 additions & 80 deletions Source/Core/Core/IOS/USB/Emulated/Microphone.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,106 +5,55 @@

#include <string>
#include <vector>
#include <mutex>

#include "Common/CommonTypes.h"

struct cubeb;
struct cubeb_stream;

namespace IOS::HLE::USB
{
template <size_t S>
class simple_ringbuf
{
public:
simple_ringbuf() { m_container.resize(S); }

bool has_data() const { return m_used != 0; }

u32 read_bytes(u8* buf, const u32 size)
{
u32 to_read = size > m_used ? m_used : size;
if (!to_read)
return 0;

u8* data = m_container.data();
u32 new_tail = m_tail + to_read;

if (new_tail >= S)
{
u32 first_chunk_size = S - m_tail;
std::memcpy(buf, data + m_tail, first_chunk_size);
std::memcpy(buf + first_chunk_size, data, to_read - first_chunk_size);
m_tail = (new_tail - S);
}
else
{
std::memcpy(buf, data + m_tail, to_read);
m_tail = new_tail;
}

m_used -= to_read;

return to_read;
}

void write_bytes(const u8* buf, const u32 size)
{
if (u32 over_size = m_used + size; over_size > S)
{
m_tail += (over_size - S);
if (m_tail > S)
m_tail -= S;

m_used = S;
}
else
{
m_used = over_size;
}

u8* data = m_container.data();
u32 new_head = m_head + size;

if (new_head >= S)
{
u32 first_chunk_size = S - m_head;
std::memcpy(data + m_head, buf, first_chunk_size);
std::memcpy(data, buf + first_chunk_size, size - first_chunk_size);
m_head = (new_head - S);
}
else
{
std::memcpy(data + m_head, buf, size);
m_head = new_head;
}
}

protected:
std::vector<u8> m_container;
u32 m_head = 0, m_tail = 0, m_used = 0;
};

class Microphone final
{
public:
static std::vector<std::string> ListDevices();
Microphone();
~Microphone();

int OpenMicrophone();
int StartCapture();
void StopCapture();
void StreamInit();
void StreamTerminate();
void PerformAudioCapture();
void GetSoundData();
void ReadIntoBuffer(u8* dst, u32 size);
bool HasData() const;

private:
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long nframes);

void StreamStart();
void StopStream();
void ByteSwap(const void* src, void* dst) const;

static constexpr u32 SAMPLING_RATE = 8000;
static constexpr u32 BUFFER_SIZE = SAMPLING_RATE / 2;

ALCdevice* m_device;
u32 m_num_of_samples{};
std::vector<u8> m_dsp_data{};
s16* stream_buffer;
std::mutex ring_lock;
std::shared_ptr<cubeb> m_cubeb_ctx = nullptr;
cubeb_stream* m_cubeb_stream = nullptr;
std::vector<u8> m_temp_buffer{};
simple_ringbuf<BUFFER_SIZE> m_rbuf_dsp;

int stream_size;
int stream_wpos;
int stream_rpos;
int samples_avail;
int buff_size_samples = 16;

#ifdef _WIN32
Common::WorkQueueThread<std::function<void()>> m_work_queue;
bool m_coinit_success = false;
bool m_should_couninit = false;
#endif
};
} // namespace IOS::HLE::USB
Loading

0 comments on commit c308aa8

Please sign in to comment.