#include "CubebAPI.h" #if BOOST_OS_WINDOWS #include #include #include #pragma comment(lib, "Avrt.lib") #pragma comment(lib, "ksuser.lib") #endif void state_cb(cubeb_stream* stream, void* user, cubeb_state state) { if (!stream) return; /*switch (state) { case CUBEB_STATE_STARTED: fprintf(stderr, "stream started\n"); break; case CUBEB_STATE_STOPPED: fprintf(stderr, "stream stopped\n"); break; case CUBEB_STATE_DRAINED: fprintf(stderr, "stream drained\n"); break; default: fprintf(stderr, "unknown stream state %d\n", state); }*/ } long CubebAPI::data_cb(cubeb_stream* stream, void* user, const void* inputbuffer, void* outputbuffer, long nframes) { auto* thisptr = (CubebAPI*)user; //const auto size = (size_t)thisptr->m_bytesPerBlock; // (size_t)nframes* thisptr->m_channels; // m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8); const auto size = (size_t)nframes * thisptr->m_channels * (thisptr->m_bitsPerSample/8); std::unique_lock lock(thisptr->m_mutex); if (thisptr->m_buffer.empty()) { // we got no data, just write silence memset(outputbuffer, 0x00, size); } else { const auto copied = std::min(thisptr->m_buffer.size(), size); memcpy(outputbuffer, thisptr->m_buffer.data(), copied); thisptr->m_buffer.erase(thisptr->m_buffer.begin(), std::next(thisptr->m_buffer.begin(), copied)); lock.unlock(); // fill rest with silence if (copied != size) memset((uint8*)outputbuffer + copied, 0x00, size - copied); } return nframes; } CubebAPI::CubebAPI(cubeb_devid devid, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample) : IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample) { cubeb_stream_params output_params; output_params.format = CUBEB_SAMPLE_S16LE; output_params.rate = samplerate; output_params.channels = channels; output_params.prefs = CUBEB_STREAM_PREF_NONE; switch (channels) { case 8: output_params.layout = CUBEB_LAYOUT_3F4_LFE; break; case 6: output_params.layout = CUBEB_LAYOUT_QUAD_LFE | CHANNEL_FRONT_CENTER; break; case 4: output_params.layout = CUBEB_LAYOUT_QUAD; break; case 2: output_params.layout = CUBEB_LAYOUT_STEREO; break; default: output_params.layout = CUBEB_LAYOUT_MONO; break; } uint32 latency = 1; cubeb_get_min_latency(s_context, &output_params, &latency); m_buffer.reserve((size_t)m_bytesPerBlock * kBlockCount); if (cubeb_stream_init(s_context, &m_stream, "Cemu Cubeb output", nullptr, nullptr, devid, &output_params, latency, data_cb, state_cb, this) != CUBEB_OK) { throw std::runtime_error("can't initialize cubeb device"); } } CubebAPI::~CubebAPI() { if (m_stream) { Stop(); cubeb_stream_destroy(m_stream); } } bool CubebAPI::NeedAdditionalBlocks() const { std::shared_lock lock(m_mutex); return m_buffer.size() < s_audioDelay * m_bytesPerBlock; } bool CubebAPI::FeedBlock(sint16* data) { std::unique_lock lock(m_mutex); if (m_buffer.capacity() <= m_buffer.size() + m_bytesPerBlock) { forceLogDebug_printf("dropped direct sound block since too many buffers are queued"); return false; } m_buffer.insert(m_buffer.end(), (uint8*)data, (uint8*)data + m_bytesPerBlock); return true; } bool CubebAPI::Play() { if (m_is_playing) return true; if (cubeb_stream_start(m_stream) == CUBEB_OK) { m_is_playing = true; return true; } return false; } bool CubebAPI::Stop() { if (!m_is_playing) return true; if (cubeb_stream_stop(m_stream) == CUBEB_OK) { m_is_playing = false; return true; } return false; } void CubebAPI::SetVolume(sint32 volume) { IAudioAPI::SetVolume(volume); cubeb_stream_set_volume(m_stream, (float)volume / 100.0f); } bool CubebAPI::InitializeStatic() { #if BOOST_OS_WINDOWS s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); #endif if (cubeb_init(&s_context, "Cemu Cubeb", nullptr)) { cemuLog_force("can't create cubeb audio api"); #if BOOST_OS_WINDOWS if (s_com_initialized) { CoUninitialize(); s_com_initialized = false; } #endif return false; } return true; } void CubebAPI::Destroy() { if (s_context) cubeb_destroy(s_context); #if BOOST_OS_WINDOWS if (s_com_initialized) CoUninitialize(); #endif } std::vector CubebAPI::GetDevices() { cubeb_device_collection devices; if (cubeb_enumerate_devices(s_context, CUBEB_DEVICE_TYPE_OUTPUT, &devices) != CUBEB_OK) return {}; std::vector result; result.reserve(devices.count); for (size_t i = 0; i < devices.count; ++i) { //const auto& device = devices.device[i]; if (devices.device[i].state == CUBEB_DEVICE_STATE_ENABLED) { auto device = std::make_shared(devices.device[i].devid, devices.device[i].device_id, boost::nowide::widen( devices.device[i].friendly_name)); result.emplace_back(device); } } cubeb_device_collection_destroy(s_context, &devices); return result; }