Address review comments

This commit is contained in:
James Rowe 2019-03-06 11:16:43 -07:00
parent c669aa8d55
commit 5f532c2560
11 changed files with 119 additions and 135 deletions

View file

@ -9,11 +9,13 @@
namespace AudioCore { namespace AudioCore {
using SampleQueue = Common::SPSCQueue<Frontend::Mic::Samples>;
struct CubebInput::Impl { struct CubebInput::Impl {
cubeb* ctx = nullptr; cubeb* ctx = nullptr;
cubeb_stream* stream = nullptr; cubeb_stream* stream = nullptr;
std::unique_ptr<Frontend::Mic::SampleQueue> sample_queue{}; std::unique_ptr<SampleQueue> sample_queue{};
u8 sample_size_in_bytes = 0; u8 sample_size_in_bytes = 0;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
@ -26,7 +28,7 @@ CubebInput::CubebInput() : impl(std::make_unique<Impl>()) {
LOG_ERROR(Audio, "cubeb_init failed! Mic will not work properly"); LOG_ERROR(Audio, "cubeb_init failed! Mic will not work properly");
return; return;
} }
impl->sample_queue = std::make_unique<Frontend::Mic::SampleQueue>(); impl->sample_queue = std::make_unique<SampleQueue>();
} }
CubebInput::~CubebInput() { CubebInput::~CubebInput() {
@ -40,7 +42,7 @@ CubebInput::~CubebInput() {
cubeb_destroy(impl->ctx); cubeb_destroy(impl->ctx);
} }
void CubebInput::StartSampling(Frontend::Mic::Parameters params) { void CubebInput::StartSampling(const Frontend::Mic::Parameters& params) {
// Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support) // Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support)
// TODO resample the input stream // TODO resample the input stream
if (params.sign == Frontend::Mic::Signedness::Unsigned) { if (params.sign == Frontend::Mic::Signedness::Unsigned) {
@ -103,12 +105,11 @@ Frontend::Mic::Samples CubebInput::Read() {
long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames) { void* output_buffer, long num_frames) {
Impl* impl = static_cast<Impl*>(user_data); Impl* impl = static_cast<Impl*>(user_data);
u8 const* data = reinterpret_cast<u8 const*>(input_buffer);
if (!impl) { if (!impl) {
return 0; return 0;
} }
u8 const* data = reinterpret_cast<u8 const*>(input_buffer);
impl->sample_queue->Push(std::vector(data, data + num_frames * impl->sample_size_in_bytes)); impl->sample_queue->Push(std::vector(data, data + num_frames * impl->sample_size_in_bytes));
// returning less than num_frames here signals cubeb to stop sampling // returning less than num_frames here signals cubeb to stop sampling
@ -130,7 +131,7 @@ std::vector<std::string> ListCubebInputDevices() {
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) {
LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported"); LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported");
} else { } else {
for (size_t i = 0; i < collection.count; i++) { for (std::size_t i = 0; i < collection.count; i++) {
const cubeb_device_info& device = collection.device[i]; const cubeb_device_info& device = collection.device[i];
if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) {
device_list.emplace_back(device.friendly_name); device_list.emplace_back(device.friendly_name);

View file

@ -15,7 +15,7 @@ public:
CubebInput(); CubebInput();
~CubebInput() override; ~CubebInput() override;
void StartSampling(Frontend::Mic::Parameters params) override; void StartSampling(const Frontend::Mic::Parameters& params) override;
void StopSampling() override; void StopSampling() override;

View file

@ -166,6 +166,10 @@ void Config::ReadValues() {
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
Settings::values.audio_device_id = sdl2_config->GetString("Audio", "output_device", "auto"); Settings::values.audio_device_id = sdl2_config->GetString("Audio", "output_device", "auto");
Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
Settings::values.mic_input_device =
sdl2_config->GetString("Audio", "mic_input_device", "Default");
Settings::values.mic_input_type =
static_cast<Settings::MicInputType>(sdl2_config->GetInteger("Audio", "mic_input_type", 0));
// Data Storage // Data Storage
Settings::values.use_virtual_sd = Settings::values.use_virtual_sd =

View file

@ -198,7 +198,8 @@ void Config::ReadValues() {
Settings::values.audio_device_id = Settings::values.audio_device_id =
ReadSetting("output_device", "auto").toString().toStdString(); ReadSetting("output_device", "auto").toString().toStdString();
Settings::values.volume = ReadSetting("volume", 1).toFloat(); Settings::values.volume = ReadSetting("volume", 1).toFloat();
Settings::values.mic_input_type = ReadSetting("mic_input_type", 0).toInt(); Settings::values.mic_input_type =
static_cast<Settings::MicInputType>(ReadSetting("mic_input_type", 0).toInt());
Settings::values.mic_input_device = Settings::values.mic_input_device =
ReadSetting("mic_input_device", "Default").toString().toStdString(); ReadSetting("mic_input_device", "Default").toString().toStdString();
qt_config->endGroup(); qt_config->endGroup();
@ -483,8 +484,8 @@ void Config::SaveValues() {
WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true); WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto"); WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
WriteSetting("volume", Settings::values.volume, 1.0f); WriteSetting("volume", Settings::values.volume, 1.0f);
WriteSetting("mic_input_device", QString::fromStdString(Settings::values.mic_input_device), 0); WriteSetting("mic_input_device", QString::fromStdString(Settings::values.mic_input_device), "Default");
WriteSetting("mic_input_type", Settings::values.mic_input_type, "Default"); WriteSetting("mic_input_type", static_cast<int>(Settings::values.mic_input_type), 0);
qt_config->endGroup(); qt_config->endGroup();
using namespace Service::CAM; using namespace Service::CAM;

View file

@ -3,7 +3,6 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <memory> #include <memory>
#include <QAudioDeviceInfo>
#include <QtGlobal> #include <QtGlobal>
#include "audio_core/cubeb_input.h" #include "audio_core/cubeb_input.h"
#include "audio_core/sink.h" #include "audio_core/sink.h"
@ -37,14 +36,14 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
ui->input_device_combo_box->addItem(QString::fromStdString(device)); ui->input_device_combo_box->addItem(QString::fromStdString(device));
} }
connect(ui->input_type_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(ui->input_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::updateAudioInputDevices); &ConfigureAudio::updateAudioInputDevices);
ui->input_type_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->input_type_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->input_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->input_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
this->setConfiguration(); this->setConfiguration();
connect(ui->output_sink_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::updateAudioOutputDevices); &ConfigureAudio::updateAudioOutputDevices);
} }
@ -74,10 +73,11 @@ void ConfigureAudio::setConfiguration() {
} }
ui->emulation_combo_box->setCurrentIndex(selection); ui->emulation_combo_box->setCurrentIndex(selection);
ui->input_type_combo_box->setCurrentIndex(Settings::values.mic_input_type); int index = static_cast<int>(Settings::values.mic_input_type);
ui->input_type_combo_box->setCurrentIndex(index);
ui->input_device_combo_box->setCurrentText( ui->input_device_combo_box->setCurrentText(
QString::fromStdString(Settings::values.mic_input_device)); QString::fromStdString(Settings::values.mic_input_device));
updateAudioInputDevices(Settings::values.mic_input_type); updateAudioInputDevices(index);
} }
void ConfigureAudio::setOutputSinkFromSinkID() { void ConfigureAudio::setOutputSinkFromSinkID() {
@ -124,7 +124,8 @@ void ConfigureAudio::applyConfiguration() {
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0; 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.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_type =
static_cast<Settings::MicInputType>(ui->input_type_combo_box->currentIndex());
Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString(); Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString();
} }

View file

@ -1,4 +1,4 @@
// Copyright 2018 Citra Emulator Project // Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -12,39 +12,28 @@ constexpr std::array<u8, 32> NOISE_SAMPLE_8_BIT = {
0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF,
0xF4, 0xE1, 0xBF, 0x9A, 0x71, 0x58, 0x5B, 0x5F, 0x62, 0xC2, 0x25, 0x05, 0x01, 0x01, 0x01, 0x01}; 0xF4, 0xE1, 0xBF, 0x9A, 0x71, 0x58, 0x5B, 0x5F, 0x62, 0xC2, 0x25, 0x05, 0x01, 0x01, 0x01, 0x01};
constexpr std::array<u8, 512> NOISE_SAMPLE_16_BIT = { constexpr std::array<u8, 32> NOISE_SAMPLE_16_BIT = {
0x64, 0x61, 0x74, 0x61, 0x56, 0xD7, 0x00, 0x00, 0x48, 0xF7, 0x86, 0x05, 0x77, 0x1A, 0xF4, 0x1F, 0x64, 0x61, 0x74, 0x61, 0x56, 0xD7, 0x00, 0x00, 0x48, 0xF7, 0x86, 0x05, 0x77, 0x1A, 0xF4, 0x1F,
0x28, 0x0F, 0x6B, 0xEB, 0x1C, 0xC0, 0xCB, 0x9D, 0x46, 0x90, 0xDF, 0x98, 0xEA, 0xAE, 0xB5, 0xC4, 0x28, 0x0F, 0x6B, 0xEB, 0x1C, 0xC0, 0xCB, 0x9D, 0x46, 0x90, 0xDF, 0x98, 0xEA, 0xAE, 0xB5, 0xC4};
0x9D, 0xCE, 0xB6, 0xC9, 0xDF, 0xBD, 0x82, 0xBA, 0x83, 0xCD, 0x57, 0xF9, 0x96, 0x30, 0x2C, 0x5B,
0x29, 0x64, 0xD3, 0x46, 0x0D, 0x12, 0x3E, 0xDE, 0x00, 0xBD, 0x74, 0xAE, 0xF0, 0xA4, 0x91, 0x93, Interface::~Interface() = default;
0x02, 0x80, 0x00, 0x80, 0x03, 0x80, 0x6F, 0xAA, 0x8E, 0xE8, 0xEE, 0x1A, 0x2B, 0x2C, 0xC6, 0x18,
0xF8, 0xED, 0xC2, 0xBE, 0x00, 0x99, 0xC1, 0x82, 0x00, 0x80, 0xA6, 0x8A, 0x37, 0xA8, 0x6B, 0xCE, void NullMic::StartSampling(const Parameters& params) {
0x20, 0xEF, 0xD0, 0xFD, 0x68, 0xF9, 0x50, 0xEF, 0x89, 0xF2, 0xDE, 0x0C, 0x24, 0x36, 0xDB, 0x58, parameters = params;
0xB2, 0x61, 0xD5, 0x4C, 0x0B, 0x27, 0x6D, 0x02, 0xDC, 0xE8, 0x13, 0xD8, 0x2F, 0xC9, 0x07, 0xBC, is_sampling = true;
0xAB, 0xBA, 0x70, 0xD1, 0xDE, 0x01, 0xF2, 0x3C, 0x64, 0x6A, 0xFF, 0x78, 0x47, 0x6B, 0x26, 0x56, }
0x9B, 0x51, 0xF3, 0x65, 0xFF, 0x7F, 0xFF, 0x7F, 0x88, 0x6B, 0x46, 0x24, 0xBF, 0xD8, 0x8C, 0xB4,
0xD9, 0xCF, 0x77, 0x1C, 0x43, 0x6B, 0xFE, 0x7F, 0x15, 0x64, 0xA6, 0x13, 0x03, 0xCE, 0x51, 0xBF, void NullMic::StopSampling() {
0xC7, 0xEB, 0xCB, 0x2E, 0x6C, 0x58, 0xA7, 0x51, 0x40, 0x2A, 0x24, 0x06, 0x45, 0xFB, 0xD3, 0xFE, is_sampling = false;
0x51, 0xF2, 0x1E, 0xC5, 0x79, 0x8A, 0x00, 0x80, 0x94, 0x8E, 0x7A, 0xDE, 0x83, 0x29, 0x8E, 0x3C, }
0x4B, 0x0F, 0xD8, 0xCB, 0x41, 0xAB, 0xC1, 0xC5, 0xC6, 0xFE, 0x0F, 0x1F, 0x92, 0x05, 0x4B, 0xC4,
0xDC, 0x8F, 0xE1, 0x90, 0xE3, 0xC0, 0xDB, 0xF1, 0xCC, 0xF8, 0xC8, 0xD4, 0x2A, 0xAF, 0xB7, 0xB5, void NullMic::AdjustSampleRate(u32 sample_rate) {
0x7F, 0xF0, 0xB2, 0x39, 0xD4, 0x5C, 0xD6, 0x41, 0x69, 0xFD, 0xEA, 0xBB, 0x57, 0x9B, 0x59, 0x98, parameters.sample_rate = sample_rate;
0x8E, 0x9B, 0x8A, 0x97, 0x54, 0x98, 0xE6, 0xB5, 0x25, 0xF8, 0x77, 0x48, 0xFF, 0x7F, 0xFF, 0x7F, }
0x49, 0x6B, 0xB4, 0x4B, 0xE9, 0x45, 0xE3, 0x57, 0x60, 0x64, 0x8C, 0x4D, 0x69, 0x10, 0x10, 0xCA,
0x13, 0xA4, 0xC5, 0xB7, 0xE4, 0xFD, 0x7C, 0x54, 0xFF, 0x7F, 0xFF, 0x7F, 0xFF, 0x7F, 0xE0, 0x52, Samples NullMic::Read() {
0x5E, 0x22, 0x31, 0x0B, 0xB5, 0x12, 0xB1, 0x2E, 0xA7, 0x4D, 0x17, 0x61, 0x46, 0x65, 0xA5, 0x5F, return {};
0x5C, 0x57, 0xB6, 0x4C, 0x3B, 0x38, 0xFD, 0x11, 0x70, 0xDC, 0x2A, 0xA8, 0xB9, 0x8C, 0xEA, 0x98, }
0x07, 0xC6, 0xDF, 0xF7, 0x2D, 0x0C, 0xEC, 0xF0, 0xA8, 0xB2, 0x02, 0x80, 0x00, 0x80, 0xBA, 0x8A,
0x0E, 0xCB, 0x54, 0xF7, 0x2B, 0xEE, 0x4C, 0xB9, 0x48, 0x89, 0x8F, 0x90, 0x0E, 0xD8, 0x83, 0x32,
0xC5, 0x5C, 0x54, 0x34, 0x86, 0xD6, 0x78, 0x8C, 0x88, 0x90, 0x87, 0xDE, 0x75, 0x37, 0x55, 0x56,
0xF1, 0x28, 0xF0, 0xDA, 0xE2, 0xAB, 0xE6, 0xB6, 0xD6, 0xDF, 0x4D, 0xF4, 0xCF, 0xDE, 0x3E, 0xBA,
0xC6, 0xB3, 0x81, 0xDA, 0xFE, 0x0D, 0xE9, 0x1D, 0xCE, 0xFB, 0x59, 0xCD, 0x57, 0xCA, 0x77, 0x06,
0x63, 0x5A, 0xFE, 0x7F, 0x49, 0x63, 0x7F, 0x14, 0x0E, 0xDB, 0x2A, 0xE5, 0x3B, 0x27, 0xFF, 0x69,
0x0A, 0x7C, 0xC5, 0x56, 0x65, 0x19, 0xEC, 0xE5, 0x0E, 0xC6, 0xA6, 0xB0, 0x09, 0xA2, 0x06, 0xA8,
0xC7, 0xD1, 0xE9, 0x15, 0xD5, 0x4E, 0xBB, 0x56, 0x85, 0x2A, 0x25, 0xF0, 0x78, 0xD6, 0x70, 0xEB,
0xF2, 0x0F, 0xE1, 0x15, 0x66, 0xEC, 0xC7, 0xB0, 0xE7, 0x93, 0x9D, 0xAD, 0xD1, 0xE6, 0xD3, 0x0D,
0xAC, 0x00, 0xB2, 0xC7, 0x5D, 0x8B, 0x00, 0x80, 0xA1, 0x88, 0x14, 0xBE, 0xDF, 0xFB, 0xCF, 0x32,
0xD8, 0x5B, 0x0F, 0x6F, 0x6C, 0x62, 0xD9, 0x33, 0x76, 0xF3, 0xD6, 0xBF, 0x41, 0xB3, 0x5C, 0xD1};
StaticMic::StaticMic() StaticMic::StaticMic()
: CACHE_8_BIT{NOISE_SAMPLE_8_BIT.begin(), NOISE_SAMPLE_8_BIT.end()}, : CACHE_8_BIT{NOISE_SAMPLE_8_BIT.begin(), NOISE_SAMPLE_8_BIT.end()},
@ -52,7 +41,7 @@ StaticMic::StaticMic()
StaticMic::~StaticMic() = default; StaticMic::~StaticMic() = default;
void StaticMic::StartSampling(Parameters params) { void StaticMic::StartSampling(const Parameters& params) {
sample_rate = params.sample_rate; sample_rate = params.sample_rate;
sample_size = params.sample_size; sample_size = params.sample_size;
@ -70,17 +59,4 @@ Samples StaticMic::Read() {
return (sample_size == 8) ? CACHE_8_BIT : CACHE_16_BIT; return (sample_size == 8) ? CACHE_8_BIT : CACHE_16_BIT;
} }
static std::shared_ptr<Mic::Interface> current_mic;
void RegisterMic(std::shared_ptr<Mic::Interface> mic) {
current_mic = mic;
}
std::shared_ptr<Mic::Interface> GetCurrentMic() {
if (!current_mic) {
current_mic = std::make_shared<Mic::NullMic>();
}
return current_mic;
}
} // namespace Frontend::Mic } // namespace Frontend::Mic

View file

@ -1,4 +1,4 @@
// Copyright 2018 Citra Emulator Project // Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -17,7 +17,6 @@ enum class Signedness : u8 {
}; };
using Samples = std::vector<u8>; using Samples = std::vector<u8>;
using SampleQueue = Common::SPSCQueue<Samples>;
struct Parameters { struct Parameters {
Signedness sign; Signedness sign;
@ -32,17 +31,19 @@ class Interface {
public: public:
Interface() = default; Interface() = default;
virtual ~Interface() = default; virtual ~Interface();
/// Starts the microphone. Called by Core /// Starts the microphone. Called by Core
virtual void StartSampling(Parameters params) = 0; virtual void StartSampling(const Parameters& params) = 0;
/// Stops the microphone. Called by Core /// Stops the microphone. Called by Core
virtual void StopSampling() = 0; virtual void StopSampling() = 0;
/// Called from the actual event timing read back. The frontend impl is responsible for wrapping /**
/// up any data and returning them to the core so the core can write them to the sharedmem. If * Called from the actual event timing at a constant period under a given sample rate.
/// theres nothing to return just return an empty vector * When sampling is enabled this function is expected to return a buffer of 16 samples in ideal
* conditions, but can be lax if the data is coming in from another source like a real mic.
*/
virtual Samples Read() = 0; virtual Samples Read() = 0;
/// Adjusts the Parameters. Implementations should update the parameters field in addition to /// Adjusts the Parameters. Implementations should update the parameters field in addition to
@ -70,7 +71,7 @@ public:
return is_sampling; return is_sampling;
} }
Parameters GetParameters() const { const Parameters& GetParameters() const {
return parameters; return parameters;
} }
@ -83,22 +84,13 @@ protected:
class NullMic final : public Interface { class NullMic final : public Interface {
public: public:
void StartSampling(Parameters params) override { void StartSampling(const Parameters& params) override;
parameters = params;
is_sampling = true;
}
void StopSampling() override { void StopSampling() override;
is_sampling = false;
}
void AdjustSampleRate(u32 sample_rate) override { void AdjustSampleRate(u32 sample_rate) override;
parameters.sample_rate = sample_rate;
}
Samples Read() override { Samples Read() override;
return {};
}
}; };
class StaticMic final : public Interface { class StaticMic final : public Interface {
@ -106,7 +98,7 @@ public:
StaticMic(); StaticMic();
~StaticMic() override; ~StaticMic() override;
void StartSampling(Parameters params) override; void StartSampling(const Parameters& params) override;
void StopSampling() override; void StopSampling() override;
void AdjustSampleRate(u32 sample_rate) override; void AdjustSampleRate(u32 sample_rate) override;

View file

@ -23,7 +23,8 @@ enum class Encoding : u8 {
PCM16Signed = 3, ///< Signed 16-bit PCM. PCM16Signed = 3, ///< Signed 16-bit PCM.
}; };
/// Microphone audio sampling rates. /// Microphone audio sampling rates. The actual accurate sampling rate can be calculated using
/// (16756991 / 512) / (SampleRate + 1) where SampleRate is one of the above values.
enum class SampleRate : u8 { enum class SampleRate : u8 {
Rate32730 = 0, ///< 32728.498 Hz Rate32730 = 0, ///< 32728.498 Hz
Rate16360 = 1, ///< 16364.479 Hz Rate16360 = 1, ///< 16364.479 Hz
@ -34,13 +35,13 @@ enum class SampleRate : u8 {
constexpr u32 GetSampleRateInHz(SampleRate sample_rate) { constexpr u32 GetSampleRateInHz(SampleRate sample_rate) {
switch (sample_rate) { switch (sample_rate) {
case SampleRate::Rate8180: case SampleRate::Rate8180:
return 8180; return 8182;
case SampleRate::Rate10910: case SampleRate::Rate10910:
return 10910; return 10909;
case SampleRate::Rate16360: case SampleRate::Rate16360:
return 16360; return 16364;
case SampleRate::Rate32730: case SampleRate::Rate32730:
return 32730; return 32728;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@ -54,45 +55,28 @@ constexpr u64 BufferUpdateRate16360 = BASE_CLOCK_RATE_ARM11 / 1022;
constexpr u64 BufferUpdateRate32730 = BASE_CLOCK_RATE_ARM11 / 2045; constexpr u64 BufferUpdateRate32730 = BASE_CLOCK_RATE_ARM11 / 2045;
constexpr u64 GetBufferUpdateRate(SampleRate sample_rate) { constexpr u64 GetBufferUpdateRate(SampleRate sample_rate) {
switch (sample_rate) { return GetSampleRateInHz(sample_rate) / 16;
case SampleRate::Rate8180:
return BufferUpdateRate8180;
case SampleRate::Rate10910:
return BufferUpdateRate10910;
case SampleRate::Rate16360:
return BufferUpdateRate16360;
case SampleRate::Rate32730:
return BufferUpdateRate32730;
default:
UNREACHABLE();
}
} }
// Variables holding the current mic buffer writing state // Variables holding the current mic buffer writing state
struct State { struct State {
u8* sharedmem_buffer = nullptr; u8* sharedmem_buffer = nullptr;
u32 sharedmem_size = 0; u32 sharedmem_size = 0;
size_t size = 0; std::size_t size = 0;
u32 offset = 0; u32 offset = 0;
u32 initial_offset = 0; u32 initial_offset = 0;
bool looped_buffer = false; bool looped_buffer = false;
u8 sample_size = 0; u8 sample_size = 0;
SampleRate sample_rate = SampleRate::Rate16360; SampleRate sample_rate = SampleRate::Rate16360;
void UpdateOffset() {
// 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(sharedmem_buffer + (sharedmem_size - sizeof(u32)),
reinterpret_cast<u8*>(&offset), sizeof(u32));
}
void WriteSamples(const std::vector<u8>& samples) { void WriteSamples(const std::vector<u8>& samples) {
u32 bytes_total_written = 0; u32 bytes_total_written = 0;
const size_t remaining_space = size - offset; const std::size_t remaining_space = size - offset;
size_t bytes_to_write = std::min(samples.size(), remaining_space); std::size_t bytes_to_write = std::min(samples.size(), remaining_space);
// Write as many samples as we can to the buffer. // Write as many samples as we can to the buffer.
// TODO if the sample size is 16bit, this could theoretically cut a sample // TODO if the sample size is 16bit, this could theoretically cut a sample in the case where
// the application configures an odd size
std::memcpy(sharedmem_buffer + offset, samples.data(), bytes_to_write); std::memcpy(sharedmem_buffer + offset, samples.data(), bytes_to_write);
offset += static_cast<u32>(bytes_to_write); offset += static_cast<u32>(bytes_to_write);
bytes_total_written += static_cast<u32>(bytes_to_write); bytes_total_written += static_cast<u32>(bytes_to_write);
@ -105,6 +89,12 @@ struct State {
bytes_to_write); bytes_to_write);
offset += static_cast<u32>(bytes_to_write); offset += static_cast<u32>(bytes_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
u32_le off = offset;
std::memcpy(sharedmem_buffer + (sharedmem_size - sizeof(u32)), reinterpret_cast<u8*>(&off),
sizeof(u32));
} }
}; };
@ -112,9 +102,10 @@ struct MIC_U::Impl {
explicit Impl(Core::System& system) : timing(system.CoreTiming()) { explicit Impl(Core::System& system) : timing(system.CoreTiming()) {
buffer_full_event = buffer_full_event =
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
buffer_write_event = timing.RegisterEvent( buffer_write_event =
"MIC_U::UpdateBuffer", std::bind(&Impl::UpdateSharedMemBuffer, this, timing.RegisterEvent("MIC_U::UpdateBuffer", [this](u64 userdata, s64 cycles_late) {
std::placeholders::_1, std::placeholders::_2)); UpdateSharedMemBuffer(userdata, cycles_late);
});
} }
void MapSharedMem(Kernel::HLERequestContext& ctx) { void MapSharedMem(Kernel::HLERequestContext& ctx) {
@ -131,7 +122,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, "MIC:U MapSharedMem called, size=0x{:X}", size); LOG_TRACE(Service_MIC, "MapSharedMem called, size=0x{:X}", size);
} }
void UnmapSharedMem(Kernel::HLERequestContext& ctx) { void UnmapSharedMem(Kernel::HLERequestContext& ctx) {
@ -139,7 +130,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
shared_memory = nullptr; shared_memory = nullptr;
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, "MIC:U UnmapSharedMem called"); LOG_TRACE(Service_MIC, "UnmapSharedMem called");
} }
void UpdateSharedMemBuffer(u64 userdata, s64 cycles_late) { void UpdateSharedMemBuffer(u64 userdata, s64 cycles_late) {
@ -152,8 +143,6 @@ struct MIC_U::Impl {
if (!samples.empty()) { if (!samples.empty()) {
// write the samples to sharedmem page // write the samples to sharedmem page
state.WriteSamples(samples); state.WriteSamples(samples);
// write the new offset to the last 4 bytes of the buffer
state.UpdateOffset();
} }
// schedule next run // schedule next run
@ -194,7 +183,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, LOG_TRACE(Service_MIC,
"MIC:U StartSampling called, encoding={}, sample_rate={}, " "StartSampling called, encoding={}, sample_rate={}, "
"audio_buffer_offset={}, audio_buffer_size={}, audio_buffer_loop={}", "audio_buffer_offset={}, audio_buffer_size={}, audio_buffer_loop={}",
static_cast<u32>(encoding), static_cast<u32>(sample_rate), audio_buffer_offset, static_cast<u32>(encoding), static_cast<u32>(sample_rate), audio_buffer_offset,
audio_buffer_size, audio_buffer_loop); audio_buffer_size, audio_buffer_loop);
@ -207,8 +196,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, "MIC:U AdjustSampling sample_rate={}", LOG_TRACE(Service_MIC, "AdjustSampling sample_rate={}", static_cast<u32>(sample_rate));
static_cast<u32>(sample_rate));
} }
void StopSampling(Kernel::HLERequestContext& ctx) { void StopSampling(Kernel::HLERequestContext& ctx) {
@ -217,7 +205,8 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
mic->StopSampling(); mic->StopSampling();
LOG_TRACE(Service_MIC, "MIC:U StopSampling called"); timing.RemoveEvent(buffer_write_event);
LOG_TRACE(Service_MIC, "StopSampling called");
} }
void IsSampling(Kernel::HLERequestContext& ctx) { void IsSampling(Kernel::HLERequestContext& ctx) {
@ -227,7 +216,7 @@ struct MIC_U::Impl {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
bool is_sampling = mic->IsSampling(); bool is_sampling = mic->IsSampling();
rb.Push<bool>(is_sampling); rb.Push<bool>(is_sampling);
LOG_TRACE(Service_MIC, "MIC:U IsSampling: {}", is_sampling); LOG_TRACE(Service_MIC, "IsSampling: {}", is_sampling);
} }
void GetBufferFullEvent(Kernel::HLERequestContext& ctx) { void GetBufferFullEvent(Kernel::HLERequestContext& ctx) {
@ -246,7 +235,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, "MIC:U SetGain gain={}", gain); LOG_TRACE(Service_MIC, "SetGain gain={}", gain);
} }
void GetGain(Kernel::HLERequestContext& ctx) { void GetGain(Kernel::HLERequestContext& ctx) {
@ -256,7 +245,7 @@ struct MIC_U::Impl {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
u8 gain = mic->GetGain(); u8 gain = mic->GetGain();
rb.Push<u8>(gain); rb.Push<u8>(gain);
LOG_TRACE(Service_MIC, "MIC:U GetGain gain={}", gain); LOG_TRACE(Service_MIC, "GetGain gain={}", gain);
} }
void SetPower(Kernel::HLERequestContext& ctx) { void SetPower(Kernel::HLERequestContext& ctx) {
@ -266,7 +255,7 @@ struct MIC_U::Impl {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_TRACE(Service_MIC, "MIC:U SetPower mic_power={}", power); LOG_TRACE(Service_MIC, "SetPower mic_power={}", power);
} }
void GetPower(Kernel::HLERequestContext& ctx) { void GetPower(Kernel::HLERequestContext& ctx) {
@ -276,7 +265,7 @@ struct MIC_U::Impl {
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
bool mic_power = mic->GetPower(); bool mic_power = mic->GetPower();
rb.Push<u8>(mic_power); rb.Push<u8>(mic_power);
LOG_TRACE(Service_MIC, "MIC:U GetPower called"); LOG_TRACE(Service_MIC, "GetPower called");
} }
void SetIirFilterMic(Kernel::HLERequestContext& ctx) { void SetIirFilterMic(Kernel::HLERequestContext& ctx) {
@ -437,3 +426,19 @@ void InstallInterfaces(Core::System& system) {
} }
} // namespace Service::MIC } // namespace Service::MIC
namespace Frontend::Mic {
static std::shared_ptr<Mic::Interface> current_mic;
void RegisterMic(std::shared_ptr<Mic::Interface> mic) {
current_mic = mic;
}
std::shared_ptr<Mic::Interface> GetCurrentMic() {
if (!current_mic) {
current_mic = std::make_shared<Mic::NullMic>();
}
return current_mic;
}
} // namespace Frontend::Mic

View file

@ -192,6 +192,4 @@ private:
void InstallInterfaces(Core::System& system); void InstallInterfaces(Core::System& system);
void ChangeMicImpl();
} // namespace Service::MIC } // namespace Service::MIC

View file

@ -62,13 +62,13 @@ void Apply() {
} }
// TODO support mic hotswapping by creating the new impl, and copying any parameters to it. // TODO support mic hotswapping by creating the new impl, and copying any parameters to it.
switch (Settings::values.mic_input_type) { switch (Settings::values.mic_input_type) {
case 0: case Settings::MicInputType::None:
Frontend::Mic::RegisterMic(std::make_shared<Frontend::Mic::NullMic>()); Frontend::Mic::RegisterMic(std::make_shared<Frontend::Mic::NullMic>());
break; break;
case 1: case Settings::MicInputType::Real:
Frontend::Mic::RegisterMic(std::make_shared<AudioCore::CubebInput>()); Frontend::Mic::RegisterMic(std::make_shared<AudioCore::CubebInput>());
break; break;
case 2: case Settings::MicInputType::Static:
Frontend::Mic::RegisterMic(std::make_shared<Frontend::Mic::StaticMic>()); Frontend::Mic::RegisterMic(std::make_shared<Frontend::Mic::StaticMic>());
break; break;
} }

View file

@ -26,6 +26,12 @@ enum class LayoutOption {
SideScreen, SideScreen,
}; };
enum class MicInputType {
None,
Real,
Static,
};
namespace NativeButton { namespace NativeButton {
enum Values { enum Values {
A, A,
@ -167,7 +173,7 @@ struct Values {
bool enable_audio_stretching; bool enable_audio_stretching;
std::string audio_device_id; std::string audio_device_id;
float volume; float volume;
u8 mic_input_type; MicInputType mic_input_type;
std::string mic_input_device; std::string mic_input_device;
// Camera // Camera