From 7fccc995ce2fa191025a6860ac30c66557edf893 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Wed, 27 Feb 2019 23:13:51 +0100 Subject: [PATCH] Initial Mic setup --- src/audio_core/CMakeLists.txt | 4 + src/audio_core/cubeb_input.cpp | 166 ++++++++++++++++++ src/audio_core/cubeb_input.h | 31 ++++ src/audio_core/cubeb_sink.cpp | 4 +- src/citra_qt/configuration/config.cpp | 5 + .../configuration/configure_audio.cpp | 38 +++- src/citra_qt/configuration/configure_audio.h | 3 +- src/citra_qt/configuration/configure_audio.ui | 56 +++++- src/core/CMakeLists.txt | 2 + src/core/frontend/mic.cpp | 23 +++ src/core/frontend/mic.h | 110 ++++++++++++ src/core/hle/service/mic_u.cpp | 129 +++++++++----- src/core/hle/service/mic_u.h | 2 + src/core/settings.cpp | 12 ++ src/core/settings.h | 2 + 15 files changed, 528 insertions(+), 59 deletions(-) create mode 100644 src/audio_core/cubeb_input.cpp create mode 100644 src/audio_core/cubeb_input.h create mode 100644 src/core/frontend/mic.cpp create mode 100644 src/core/frontend/mic.h diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 27e7d723b..fc298323f 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -2,6 +2,10 @@ add_library(audio_core STATIC audio_types.h codec.cpp codec.h + cubeb_input.cpp + cubeb_input.h + cubeb_sink.cpp + cubeb_sink.h dsp_interface.cpp dsp_interface.h hle/adts.h diff --git a/src/audio_core/cubeb_input.cpp b/src/audio_core/cubeb_input.cpp new file mode 100644 index 000000000..691c2b9c4 --- /dev/null +++ b/src/audio_core/cubeb_input.cpp @@ -0,0 +1,166 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "audio_core/cubeb_input.h" +#include "common/logging/log.h" + +namespace AudioCore { + +struct CubebInput::Impl { + // unsigned int sample_rate = 0; + // std::vector device_list; + + cubeb* ctx = nullptr; + cubeb_stream* stream = nullptr; + + bool looped_buffer; + u8* buffer; + u32 buffer_size; + u32 initial_offset; + u32 offset; + u32 audio_buffer_size; + + 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); +}; + +CubebInput::CubebInput() : impl(std::make_unique()) { + if (cubeb_init(&impl->ctx, "Citra Input", nullptr) != CUBEB_OK) { + LOG_ERROR(Audio, "cubeb_init failed! Mic will not work properly"); + return; + } +} + +CubebInput::~CubebInput() { + if (!impl->ctx) + return; + + if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { + LOG_ERROR(Audio, "Error stopping cubeb input stream."); + } + + cubeb_destroy(impl->ctx); +} + +void CubebInput::StartSampling(Frontend::Mic::Parameters params) { + // Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support) + // TODO resample the input stream + if (params.sign == Frontend::Mic::Signedness::Unsigned) { + LOG_ERROR(Audio, + "Application requested unsupported unsigned pcm format. Falling back to signed"); + } + if (params.sample_size != 16) { + LOG_ERROR(Audio, + "Application requested unsupported 8 bit pcm format. Falling back to 16 bits"); + } + + impl->buffer = backing_memory; + impl->buffer_size = backing_memory_size; + impl->audio_buffer_size = params.buffer_size; + impl->offset = params.buffer_offset; + impl->looped_buffer = params.buffer_loop; + + cubeb_devid input_device = nullptr; + cubeb_stream_params input_params; + input_params.channels = 1; + input_params.layout = CUBEB_LAYOUT_UNDEFINED; + input_params.prefs = CUBEB_STREAM_PREF_NONE; + input_params.format = CUBEB_SAMPLE_S16LE; + input_params.rate = params.sample_rate; + + u32 latency_frames; + if (cubeb_get_min_latency(impl->ctx, &input_params, &latency_frames) != CUBEB_OK) { + LOG_ERROR(Audio, "Could not get minimum latency"); + } + + if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Microphone", input_device, &input_params, + nullptr, nullptr, latency_frames, Impl::DataCallback, Impl::StateCallback, + impl.get()) != CUBEB_OK) { + LOG_CRITICAL(Audio, "Error creating cubeb input stream"); + } + + cubeb_stream_start(impl->stream); +} + +void CubebInput::StopSampling() { + if (impl->stream) { + cubeb_stream_stop(impl->stream); + } +} + +void CubebInput::AdjustSampleRate(u32 sample_rate) { + // TODO This should restart the stream with the new sample rate + LOG_ERROR(Audio, "AdjustSampleRate unimplemented!"); +} + +long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, + void* output_buffer, long num_frames) { + Impl* impl = static_cast(user_data); + const u8* data = reinterpret_cast(input_buffer); + + if (!impl) { + return 0; + } + + if (!impl->buffer) { + return 0; + } + + u64 total_written = 0; + u64 to_write = num_frames; + u64 remaining_space = impl->audio_buffer_size - impl->offset; + if (to_write > remaining_space) { + to_write = remaining_space; + } + std::memcpy(impl->buffer + impl->offset, data, to_write); + impl->offset += to_write; + total_written += to_write; + + if (impl->looped_buffer && num_frames > total_written) { + impl->offset = impl->initial_offset; + to_write = num_frames - to_write; + std::memcpy(impl->buffer + impl->offset, data, to_write); + impl->offset += to_write; + total_written += to_write; + } + // The last 4 bytes of the shared memory contains the latest offset + // so update that as well https://www.3dbrew.org/wiki/MIC_Shared_Memory + std::memcpy(impl->buffer + (impl->buffer_size - sizeof(u32)), + reinterpret_cast(&impl->offset), sizeof(u32)); + + // returning less than num_frames here signals cubeb to stop sampling + return total_written; +} + +void CubebInput::Impl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} + +std::vector ListCubebInputDevices() { + std::vector device_list; + cubeb* ctx; + + if (cubeb_init(&ctx, "Citra Input Device Enumerator", nullptr) != CUBEB_OK) { + LOG_CRITICAL(Audio, "cubeb_init failed"); + return {}; + } + + cubeb_device_collection collection; + if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { + LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported"); + } else { + for (size_t i = 0; i < collection.count; i++) { + const cubeb_device_info& device = collection.device[i]; + if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { + device_list.emplace_back(device.friendly_name); + } + } + cubeb_device_collection_destroy(ctx, &collection); + } + + cubeb_destroy(ctx); + return device_list; +} +} // namespace AudioCore diff --git a/src/audio_core/cubeb_input.h b/src/audio_core/cubeb_input.h new file mode 100644 index 000000000..be764ba43 --- /dev/null +++ b/src/audio_core/cubeb_input.h @@ -0,0 +1,31 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "core/frontend/mic.h" + +namespace AudioCore { + +class CubebInput final : public Frontend::Mic::Interface { +public: + CubebInput(); + ~CubebInput(); + + void StartSampling(Frontend::Mic::Parameters params) override; + + void StopSampling() override; + + void AdjustSampleRate(u32 sample_rate) override; + +private: + struct Impl; + std::unique_ptr impl; +}; + +std::vector ListCubebInputDevices(); + +} // namespace AudioCore diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 49bf3aaa9..a0b0eb944 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -27,7 +27,7 @@ struct CubebSink::Impl { }; CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_unique()) { - if (cubeb_init(&impl->ctx, "Citra", nullptr) != CUBEB_OK) { + if (cubeb_init(&impl->ctx, "Citra Output", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return; } @@ -179,7 +179,7 @@ std::vector ListCubebSinkDevices() { } else { for (std::size_t i = 0; i < collection.count; i++) { const cubeb_device_info& device = collection.device[i]; - if (device.friendly_name) { + if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { device_list.emplace_back(device.friendly_name); } } diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 201acafe4..29f61fae3 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -198,6 +198,9 @@ void Config::ReadValues() { Settings::values.audio_device_id = ReadSetting("output_device", "auto").toString().toStdString(); Settings::values.volume = ReadSetting("volume", 1).toFloat(); + Settings::values.mic_input_type = ReadSetting("mic_input_type", 0).toInt(); + Settings::values.mic_input_device = + ReadSetting("mic_input_device", "Default").toString().toStdString(); qt_config->endGroup(); using namespace Service::CAM; @@ -480,6 +483,8 @@ void Config::SaveValues() { WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true); WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto"); WriteSetting("volume", Settings::values.volume, 1.0f); + WriteSetting("mic_input_device", QString::fromStdString(Settings::values.mic_input_device), 0); + WriteSetting("mic_input_type", Settings::values.mic_input_type, "Default"); qt_config->endGroup(); using namespace Service::CAM; diff --git a/src/citra_qt/configuration/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp index 04211a161..1d9731cd3 100644 --- a/src/citra_qt/configuration/configure_audio.cpp +++ b/src/citra_qt/configuration/configure_audio.cpp @@ -3,6 +3,9 @@ // Refer to the license.txt file included. #include +#include +#include +#include "audio_core/cubeb_input.h" #include "audio_core/sink.h" #include "audio_core/sink_details.h" #include "citra_qt/configuration/configure_audio.h" @@ -28,10 +31,21 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) connect(ui->volume_slider, &QSlider::valueChanged, this, &ConfigureAudio::setVolumeIndicatorText); + ui->input_device_combo_box->clear(); + ui->input_device_combo_box->addItem(tr("Default")); + for (const auto& device : AudioCore::ListCubebInputDevices()) { + ui->input_device_combo_box->addItem(QString::fromStdString(device)); + } + + connect(ui->input_type_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + &ConfigureAudio::updateAudioInputDevices); + + ui->input_type_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->input_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + this->setConfiguration(); - connect(ui->output_sink_combo_box, - static_cast(&QComboBox::currentIndexChanged), this, - &ConfigureAudio::updateAudioDevices); + connect(ui->output_sink_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, + &ConfigureAudio::updateAudioOutputDevices); } ConfigureAudio::~ConfigureAudio() {} @@ -40,7 +54,7 @@ void ConfigureAudio::setConfiguration() { setOutputSinkFromSinkID(); // The device list cannot be pre-populated (nor listed) until the output sink is known. - updateAudioDevices(ui->output_sink_combo_box->currentIndex()); + updateAudioOutputDevices(ui->output_sink_combo_box->currentIndex()); setAudioDeviceFromDeviceID(); @@ -59,6 +73,11 @@ void ConfigureAudio::setConfiguration() { selection = 0; } ui->emulation_combo_box->setCurrentIndex(selection); + + ui->input_type_combo_box->setCurrentIndex(Settings::values.mic_input_type); + ui->input_device_combo_box->setCurrentText( + QString::fromStdString(Settings::values.mic_input_device)); + updateAudioInputDevices(Settings::values.mic_input_type); } void ConfigureAudio::setOutputSinkFromSinkID() { @@ -105,9 +124,11 @@ void ConfigureAudio::applyConfiguration() { static_cast(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0; Settings::values.enable_dsp_lle_multithread = ui->emulation_combo_box->currentIndex() == 2; + Settings::values.mic_input_type = ui->input_type_combo_box->currentIndex(); + Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString(); } -void ConfigureAudio::updateAudioDevices(int sink_index) { +void ConfigureAudio::updateAudioOutputDevices(int sink_index) { ui->audio_device_combo_box->clear(); ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); @@ -117,6 +138,13 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { } } +void ConfigureAudio::updateAudioInputDevices(int index) { + // TODO: Don't hardcode this to the index for "Real Device" without making it a constant + // somewhere + ui->input_device_combo_box->setEnabled(index == 1 && + !Core::System::GetInstance().IsPoweredOn()); +} + void ConfigureAudio::retranslateUi() { ui->retranslateUi(this); } diff --git a/src/citra_qt/configuration/configure_audio.h b/src/citra_qt/configuration/configure_audio.h index b8e347a42..9952739cf 100644 --- a/src/citra_qt/configuration/configure_audio.h +++ b/src/citra_qt/configuration/configure_audio.h @@ -23,7 +23,8 @@ public: void setConfiguration(); private: - void updateAudioDevices(int sink_index); + void updateAudioOutputDevices(int sink_index); + void updateAudioInputDevices(int index); void setOutputSinkFromSinkID(); void setAudioDeviceFromDeviceID(); diff --git a/src/citra_qt/configuration/configure_audio.ui b/src/citra_qt/configuration/configure_audio.ui index 9a3768b2a..d83fb0fcb 100644 --- a/src/citra_qt/configuration/configure_audio.ui +++ b/src/citra_qt/configuration/configure_audio.ui @@ -6,8 +6,8 @@ 0 0 - 188 - 246 + 329 + 332 @@ -39,7 +39,7 @@ - Output Engine: + Output Engine @@ -63,7 +63,7 @@ - Audio Device: + Audio Device @@ -137,6 +137,54 @@ + + + + Microphone + + + + + + + + Input Type + + + + + + + + None + + + + + Real Device + + + + + + + + + + + + Input Device + + + + + + + + + + + diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6904aaaf6..b70ef57be 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -97,6 +97,8 @@ add_library(core STATIC frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h frontend/input.h + frontend/mic.h + frontend/mic.cpp gdbstub/gdbstub.cpp gdbstub/gdbstub.h hle/applets/applet.cpp diff --git a/src/core/frontend/mic.cpp b/src/core/frontend/mic.cpp new file mode 100644 index 000000000..6ea843f41 --- /dev/null +++ b/src/core/frontend/mic.cpp @@ -0,0 +1,23 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/frontend/mic.h" +#include "core/hle/service/mic_u.h" + +namespace Frontend { + +static std::shared_ptr current_mic; + +void RegisterMic(std::shared_ptr mic) { + current_mic = mic; +} + +std::shared_ptr GetCurrentMic() { + if (!current_mic) { + current_mic = std::make_shared(); + } + return current_mic; +} + +} // namespace Frontend diff --git a/src/core/frontend/mic.h b/src/core/frontend/mic.h new file mode 100644 index 000000000..39ceae197 --- /dev/null +++ b/src/core/frontend/mic.h @@ -0,0 +1,110 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/swap.h" + +namespace Frontend { + +namespace Mic { + +enum class Signedness : u8 { + Signed, + Unsigned, +}; + +struct Parameters { + Signedness sign; + u8 sample_size; + bool buffer_loop; + u32 sample_rate; + u32 buffer_offset; + u32 buffer_size; +}; + +class Interface { +public: + /// Starts the microphone. Called by Core + virtual void StartSampling(Parameters params) = 0; + + /// Stops the microphone. Called by Core + virtual void StopSampling() = 0; + + Interface() = default; + + Interface(const Interface& other) + : gain(other.gain), powered(other.powered), backing_memory(other.backing_memory), + backing_memory_size(other.backing_memory_size), parameters(other.parameters) {} + + /// Sets the backing memory that the mic should write raw samples into. Called by Core + void SetBackingMemory(u8* pointer, u32 size) { + backing_memory = pointer; + backing_memory_size = size; + } + + /// Adjusts the Parameters. Implementations should update the parameters field in addition to + /// changing the mic to sample according to the new parameters. Called by Core + virtual void AdjustSampleRate(u32 sample_rate) = 0; + + /// Value from 0 - 100 to adjust the mic gain setting. Called by Core + virtual void SetGain(u8 mic_gain) { + gain = mic_gain; + } + + u8 GetGain() const { + return gain; + } + + void SetPower(bool power) { + powered = power; + } + + bool GetPower() const { + return powered; + } + + bool IsSampling() const { + return is_sampling; + } + + Parameters GetParameters() const { + return parameters; + } + +protected: + u8* backing_memory; + u32 backing_memory_size; + + Parameters parameters; + u8 gain = 0; + bool is_sampling = false; + bool powered = false; +}; + +class NullMic final : public Interface { +public: + void StartSampling(Parameters params) override { + parameters = params; + is_sampling = true; + } + + void StopSampling() override { + is_sampling = false; + } + + void AdjustSampleRate(u32 sample_rate) override { + parameters.sample_rate = sample_rate; + } +}; + +} // namespace Mic + +void RegisterMic(std::shared_ptr mic); + +std::shared_ptr GetCurrentMic(); + +} // namespace Frontend diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 6c97282aa..0014f5e8d 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" #include "core/core.h" +#include "core/frontend/mic.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" @@ -14,20 +15,37 @@ namespace Service::MIC { +/// Microphone audio encodings. enum class Encoding : u8 { - PCM8 = 0, - PCM16 = 1, - PCM8Signed = 2, - PCM16Signed = 3, + PCM8 = 0, ///< Unsigned 8-bit PCM. + PCM16 = 1, ///< Unsigned 16-bit PCM. + PCM8Signed = 2, ///< Signed 8-bit PCM. + PCM16Signed = 3, ///< Signed 16-bit PCM. }; +/// Microphone audio sampling rates. enum class SampleRate : u8 { - SampleRate32730 = 0, - SampleRate16360 = 1, - SampleRate10910 = 2, - SampleRate8180 = 3 + Rate32730 = 0, ///< 32728.498 Hz + Rate16360 = 1, ///< 16364.479 Hz + Rate10910 = 2, ///< 10909.499 Hz + Rate8180 = 3 ///< 8182.1245 Hz }; +constexpr u32 GetSampleRateInHz(SampleRate sample_rate) { + switch (sample_rate) { + case SampleRate::Rate8180: + return 8180; + case SampleRate::Rate10910: + return 10910; + case SampleRate::Rate16360: + return 16360; + case SampleRate::Rate32730: + return 32730; + default: + UNREACHABLE(); + } +} + struct MIC_U::Impl { explicit Impl(Core::System& system) { buffer_full_event = @@ -43,10 +61,12 @@ struct MIC_U::Impl { shared_memory->SetName("MIC_U:shared_memory"); } + mic->SetBackingMemory(shared_memory->GetPointer(), size); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_MIC, "called, size=0x{:X}", size); + LOG_TRACE(Service_MIC, "MIC:U MapSharedMem called, size=0x{:X}", size); } void UnmapSharedMem(Kernel::HLERequestContext& ctx) { @@ -54,55 +74,68 @@ struct MIC_U::Impl { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); shared_memory = nullptr; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_MIC, "called"); + LOG_TRACE(Service_MIC, "MIC:U UnmapSharedMem called"); } void StartSampling(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x03, 5, 0}; - encoding = rp.PopEnum(); - sample_rate = rp.PopEnum(); - audio_buffer_offset = rp.PopRaw(); - audio_buffer_size = rp.Pop(); - audio_buffer_loop = rp.Pop(); + Encoding encoding = rp.PopEnum(); + SampleRate sample_rate = rp.PopEnum(); + u32 audio_buffer_offset = rp.PopRaw(); + u32 audio_buffer_size = rp.Pop(); + bool audio_buffer_loop = rp.Pop(); + + auto sign = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM16Signed + ? Frontend::Mic::Signedness::Signed + : Frontend::Mic::Signedness::Unsigned; + u8 sample_size = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM8 ? 8 : 16; + + mic->StartSampling({sign, sample_size, audio_buffer_loop, GetSampleRateInHz(sample_rate), + audio_buffer_offset, audio_buffer_size}); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - is_sampling = true; - LOG_WARNING(Service_MIC, - "(STUBBED) called, encoding={}, sample_rate={}, " - "audio_buffer_offset={}, audio_buffer_size={}, audio_buffer_loop={}", - static_cast(encoding), static_cast(sample_rate), audio_buffer_offset, - audio_buffer_size, audio_buffer_loop); + LOG_TRACE(Service_MIC, + "MIC:U StartSampling called, encoding={}, sample_rate={}, " + "audio_buffer_offset={}, audio_buffer_size={}, audio_buffer_loop={}", + static_cast(encoding), static_cast(sample_rate), audio_buffer_offset, + audio_buffer_size, audio_buffer_loop); } void AdjustSampling(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x04, 1, 0}; - sample_rate = rp.PopEnum(); + SampleRate sample_rate = rp.PopEnum(); + mic->AdjustSampleRate(GetSampleRateInHz(sample_rate)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate={}", static_cast(sample_rate)); + LOG_TRACE(Service_MIC, "MIC:U AdjustSampling sample_rate={}", + static_cast(sample_rate)); } void StopSampling(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x05, 0, 0}; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - is_sampling = false; - LOG_WARNING(Service_MIC, "(STUBBED) called"); + mic->StopSampling(); + LOG_TRACE(Service_MIC, "MIC:U StopSampling called"); } void IsSampling(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x06, 0, 0}; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); + bool is_sampling = mic->IsSampling(); rb.Push(is_sampling); - LOG_WARNING(Service_MIC, "(STUBBED) called"); + LOG_TRACE(Service_MIC, "MIC:U IsSampling: {}", is_sampling); } void GetBufferFullEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x07, 0, 0}; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(buffer_full_event); @@ -111,11 +144,12 @@ struct MIC_U::Impl { void SetGain(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x08, 1, 0}; - mic_gain = rp.Pop(); + u8 gain = rp.Pop(); + mic->SetGain(gain); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain={}", mic_gain); + LOG_TRACE(Service_MIC, "MIC:U SetGain gain={}", gain); } void GetGain(Kernel::HLERequestContext& ctx) { @@ -123,25 +157,29 @@ struct MIC_U::Impl { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - rb.Push(mic_gain); - LOG_WARNING(Service_MIC, "(STUBBED) called"); + u8 gain = mic->GetGain(); + rb.Push(gain); + LOG_TRACE(Service_MIC, "MIC:U GetGain gain={}", gain); } void SetPower(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x0A, 1, 0}; - mic_power = rp.Pop(); + bool power = rp.Pop(); + mic->SetPower(power); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power={}", mic_power); + LOG_TRACE(Service_MIC, "MIC:U SetPower mic_power={}", power); } void GetPower(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x0B, 0, 0}; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); + bool mic_power = mic->GetPower(); rb.Push(mic_power); - LOG_WARNING(Service_MIC, "(STUBBED) called"); + LOG_TRACE(Service_MIC, "MIC:U GetPower called"); } void SetIirFilterMic(Kernel::HLERequestContext& ctx) { @@ -167,6 +205,7 @@ struct MIC_U::Impl { void GetClamp(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x0E, 0, 0}; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(clamp); @@ -184,27 +223,20 @@ struct MIC_U::Impl { void SetClientVersion(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x10, 1, 0}; - const u32 version = rp.Pop(); LOG_WARNING(Service_MIC, "(STUBBED) called, version: 0x{:08X}", version); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); } - - u32 client_version = 0; - Kernel::SharedPtr buffer_full_event; + Kernel::SharedPtr buffer_full_event = + Core::System::GetInstance().Kernel().CreateEvent(Kernel::ResetType::OneShot, + "MIC_U::buffer_full_event"); Kernel::SharedPtr shared_memory; - u8 mic_gain = 0; - bool mic_power = false; - bool is_sampling = false; - bool allow_shell_closed; + u32 client_version = 0; + bool allow_shell_closed = false; bool clamp = false; - Encoding encoding = Encoding::PCM8; - SampleRate sample_rate = SampleRate::SampleRate32730; - s32 audio_buffer_offset = 0; - u32 audio_buffer_size = 0; - bool audio_buffer_loop = false; + std::shared_ptr mic; }; void MIC_U::MapSharedMem(Kernel::HLERequestContext& ctx) { @@ -292,10 +324,13 @@ MIC_U::MIC_U(Core::System& system) {0x00100040, &MIC_U::SetClientVersion, "SetClientVersion"}, }; + impl->mic = Frontend::GetCurrentMic(); RegisterHandlers(functions); } -MIC_U::~MIC_U() = default; +MIC_U::~MIC_U() { + impl->mic->StopSampling(); +} void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index bc4933229..bdd94fdd9 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -192,4 +192,6 @@ private: void InstallInterfaces(Core::System& system); +void ChangeMicImpl(); + } // namespace Service::MIC diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 4b80e3436..202a6c444 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -3,8 +3,11 @@ // Refer to the license.txt file included. #include +#include "audio_core/cubeb_input.h" #include "audio_core/dsp_interface.h" #include "core/core.h" +#include "core/frontend/emu_window.h" +#include "core/frontend/mic.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir_rst.h" @@ -57,6 +60,15 @@ void Apply() { cam->ReloadCameraDevices(); } } + // TODO support mic hotswapping by creating the new impl, and copying any parameters to it. + switch (Settings::values.mic_input_type) { + case 0: + Frontend::RegisterMic(std::make_shared()); + break; + case 1: + Frontend::RegisterMic(std::make_shared()); + break; + } } template diff --git a/src/core/settings.h b/src/core/settings.h index cea502693..702dd4d21 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -167,6 +167,8 @@ struct Values { bool enable_audio_stretching; std::string audio_device_id; float volume; + u8 mic_input_type; + std::string mic_input_device; // Camera std::array camera_name;