EXI_DeviceMic: use Cubeb instead of PortAudio

This commit is contained in:
Michael Maltese 2017-03-22 15:57:04 -07:00
parent f6e1da0dc0
commit 48d6168c99
4 changed files with 66 additions and 76 deletions

View file

@ -269,6 +269,7 @@ set(LIBS
audiocommon
bdisasm
common
cubeb
discio
enet
inputcommon
@ -337,10 +338,6 @@ if(TARGET Hidapi::Hidapi)
add_definitions(-DHAVE_HIDAPI=1)
endif()
if(PORTAUDIO_FOUND)
set(LIBS ${LIBS} ${PORTAUDIO_LIBRARIES})
endif()
if(OPROFILE_FOUND)
set(LIBS ${LIBS} ${OPROFILE_LIBRARIES})
endif()

View file

@ -9,8 +9,6 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#if HAVE_PORTAUDIO
#include "Core/HW/EXI/EXI_DeviceMic.h"
#include "Core/CoreTiming.h"
@ -18,22 +16,14 @@
#include "Core/HW/GCPad.h"
#include "Core/HW/SystemTimers.h"
#include <portaudio.h>
#include <cubeb/cubeb.h>
namespace ExpansionInterface
{
void CEXIMic::StreamLog(const char* msg)
{
INFO_LOG(EXPANSIONINTERFACE, "%s: %s", msg, Pa_GetErrorText(pa_error));
}
void CEXIMic::StreamInit()
{
// Setup the wonderful c-interfaced lib...
pa_stream = nullptr;
if ((pa_error = Pa_Initialize()) != paNoError)
StreamLog("Pa_Initialize");
if (cubeb_init(&m_cubeb_ctx, "Dolphin", NULL) != CUBEB_OK)
ERROR_LOG(EXPANSIONINTERFACE, "Error initializing cubeb library");
stream_buffer = nullptr;
samples_avail = stream_wpos = stream_rpos = 0;
@ -43,73 +33,89 @@ void CEXIMic::StreamTerminate()
{
StreamStop();
if ((pa_error = Pa_Terminate()) != paNoError)
StreamLog("Pa_Terminate");
if (m_cubeb_ctx)
{
cubeb_destroy(m_cubeb_ctx);
m_cubeb_ctx = nullptr;
}
}
static int Pa_Callback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags,
void* userData)
static void state_callback(cubeb_stream* stream, void* user_data, cubeb_state state)
{
(void)outputBuffer;
(void)timeInfo;
(void)statusFlags;
}
CEXIMic* mic = (CEXIMic*)userData;
static long data_callback(cubeb_stream* stream, void* user_data, const void* inputbuffer,
void* /*outputbuffer*/, long nframes)
{
CEXIMic* mic = static_cast<CEXIMic*>(user_data);
std::lock_guard<std::mutex> lk(mic->ring_lock);
if (mic->stream_wpos + mic->buff_size_samples > mic->stream_size)
mic->stream_wpos = 0;
const s16* buff_in = static_cast<const s16*>(inputBuffer);
s16* buff_out = &mic->stream_buffer[mic->stream_wpos];
if (buff_in == nullptr)
const s16* buff_in = static_cast<const s16*>(inputbuffer);
for (long i = 0; i < nframes; i++)
{
for (int i = 0; i < mic->buff_size_samples; i++)
{
buff_out[i] = 0;
}
}
else
{
for (int i = 0; i < mic->buff_size_samples; i++)
{
buff_out[i] = buff_in[i];
}
mic->stream_buffer[mic->stream_wpos] = buff_in[i];
mic->stream_wpos = (mic->stream_wpos + 1) % mic->stream_size;
}
mic->samples_avail += mic->buff_size_samples;
mic->samples_avail += nframes;
if (mic->samples_avail > mic->stream_size)
{
mic->samples_avail = 0;
mic->status.buff_ovrflw = 1;
}
mic->stream_wpos += mic->buff_size_samples;
mic->stream_wpos %= mic->stream_size;
return paContinue;
return nframes;
}
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];
pa_error = Pa_OpenDefaultStream(&pa_stream, 1, 0, paInt16, sample_rate, buff_size_samples,
Pa_Callback, this);
StreamLog("Pa_OpenDefaultStream");
pa_error = Pa_StartStream(pa_stream);
StreamLog("Pa_StartStream");
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, params, &minimum_latency) != CUBEB_OK)
{
WARN_LOG(EXPANSIONINTERFACE, "Error getting minimum latency");
}
if (cubeb_stream_init(m_cubeb_ctx, &m_cubeb_stream, "Dolphin Emulated GameCube Microphone",
nullptr, &params, nullptr, nullptr,
std::max<u32>(buff_size_samples, minimum_latency), data_callback,
state_callback, this) != CUBEB_OK)
{
ERROR_LOG(EXPANSIONINTERFACE, "Error initializing cubeb stream");
return;
}
if (cubeb_stream_start(m_cubeb_stream) != CUBEB_OK)
{
ERROR_LOG(EXPANSIONINTERFACE, "Error starting cubeb stream");
return;
}
INFO_LOG(EXPANSIONINTERFACE, "started cubeb stream");
}
void CEXIMic::StreamStop()
{
if (pa_stream != nullptr && Pa_IsStreamActive(pa_stream) >= paNoError)
Pa_AbortStream(pa_stream);
if (m_cubeb_stream)
{
if (cubeb_stream_stop(m_cubeb_stream) != CUBEB_OK)
ERROR_LOG(EXPANSIONINTERFACE, "Error stopping cubeb stream");
cubeb_stream_destroy(m_cubeb_stream);
m_cubeb_stream = nullptr;
}
samples_avail = stream_wpos = stream_rpos = 0;
@ -134,11 +140,11 @@ void CEXIMic::StreamReadOne()
}
// EXI Mic Device
// This works by opening and starting a portaudio input stream when the is_active
// This works by opening and starting an input stream when the is_active
// bit is set. The interrupt is scheduled in the future based on sample rate and
// buffer size settings. When the console handles the interrupt, it will send
// cmdGetBuffer, which is when we actually read data from a buffer filled
// in the background by Pa_Callback.
// in the background.
u8 const CEXIMic::exi_id[] = {0, 0x0a, 0, 0, 0};
@ -271,4 +277,3 @@ void CEXIMic::TransferByte(u8& byte)
m_position++;
}
} // namespace ExpansionInterface
#endif

View file

@ -8,10 +8,11 @@
#include "Common/Common.h"
#include "Core/HW/EXI/EXI_Device.h"
struct cubeb;
struct cubeb_stream;
namespace ExpansionInterface
{
#if HAVE_PORTAUDIO
class CEXIMic : public IEXIDevice
{
public:
@ -68,8 +69,8 @@ private:
void UpdateNextInterruptTicks();
// Streaming input interface
int pa_error; // PaError
void* pa_stream; // PaStream
cubeb* m_cubeb_ctx = nullptr;
cubeb_stream* m_cubeb_stream = nullptr;
void StreamLog(const char* msg);
void StreamInit();
@ -99,14 +100,4 @@ public:
protected:
void TransferByte(u8& byte) override;
};
#else // HAVE_PORTAUDIO
class CEXIMic : public IEXIDevice
{
public:
CEXIMic(const int) {}
};
#endif
} // namespace ExpansionInterface

View file

@ -158,10 +158,7 @@ void GameCubeConfigPane::LoadGUIValues()
slot_devices.Add(_(EXIDEV_MEMDIR_STR));
slot_devices.Add(_(EXIDEV_GECKO_STR));
slot_devices.Add(_(EXIDEV_AGP_STR));
#if HAVE_PORTAUDIO
slot_devices.Add(_(EXIDEV_MIC_STR));
#endif
wxArrayString sp1_devices;
sp1_devices.Add(_(DEV_NONE_STR));