Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions src/audio.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "audio.h"

#include <algorithm>
#include <cstdlib>

std::vector<DeviceInfo> Audio::getDevicesList() {
std::vector<DeviceInfo> deviceInfos;
Expand All @@ -23,6 +24,14 @@ std::vector<DeviceInfo> Audio::getDevicesList() {
}

void Audio::selectDevice(size_t deviceIndex) {
if (m_lastDevicesList.empty()) {
spdlog::warn("Cannot select audio device: device list is empty");
return;
}
if (deviceIndex >= m_lastDevicesList.size()) {
spdlog::warn("Device index {} is out of range. Falling back to 0.", deviceIndex);
deviceIndex = 0;
}
m_selectedDeviceID = m_lastDevicesList[deviceIndex].id;
}

Expand All @@ -33,9 +42,39 @@ bool Audio::playAudioData(const int channels, const int sampleRate, const int bi
return false;
}

if (channels <= 0 || bitsPerSample <= 0 || sampleRate <= 0) {
spdlog::error("Invalid audio metadata: channels={}, sampleRate={}, bitsPerSample={}", channels, sampleRate,
bitsPerSample);
free((void*)buffer);
return false;
}
ma_format format = determineFormat(bitsPerSample);
if (format == ma_format_unknown) {
spdlog::error("Unsupported bits per sample value: {}", bitsPerSample);
free((void*)buffer);
return false;
}
if (bufferSize == 0) {
free((void*)buffer);
return true;
}

auto devices = getDevicesList();
if (devices.empty()) {
spdlog::error("No playback devices are available");
free((void*)buffer);
return false;
}
if (std::find_if(devices.begin(), devices.end(), [&](const DeviceInfo& device) {
return ma_device_id_equal(&device.id, &m_selectedDeviceID);
}) == devices.end()) {
spdlog::warn("Selected audio device is unavailable. Falling back to index 0.");
m_selectedDeviceID = devices[0].id;
}

freeSounds();
updateDevice();
updateResampler(determineFormat(bitsPerSample), channels, sampleRate, AUDIO_DEFAULT_SAMPLE_RATE);
updateResampler(format, channels, sampleRate, AUDIO_DEFAULT_SAMPLE_RATE);
const ma_uint64 frameCountIn = (bufferSize * 8) / (channels * bitsPerSample);
ma_uint64 frameCountOut = 0;
ma_result result =
Expand Down Expand Up @@ -70,8 +109,8 @@ bool Audio::playAudioData(const int channels, const int sampleRate, const int bi
return true;
}

ma_audio_buffer_config config = ma_audio_buffer_config_init(determineFormat(bitsPerSample), channels, frameCountOut,
pPayload->pcmData.data(), nullptr);
ma_audio_buffer_config config =
ma_audio_buffer_config_init(format, channels, frameCountOut, pPayload->pcmData.data(), nullptr);
config.sampleRate = AUDIO_DEFAULT_SAMPLE_RATE;

pPayload->audioBuffer = std::make_unique<ma_audio_buffer>();
Expand Down
28 changes: 22 additions & 6 deletions src/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "singleton.h"

#include <climits>
#include <cstring>
#include <memory>
#include <miniaudio.h>
#include <spdlog/spdlog.h>
Expand Down Expand Up @@ -131,15 +132,18 @@ class CResampler {
return resampler != nullptr ? ma_resampler_set_rate(&*resampler, sampleRateIn, sampleRateOut) : MA_ERROR;
}

ma_result processAudioData(const void* pFramesIn, const ma_uint64& frameCountIn, void* pFramesOut,
ma_result processAudioData(const void* pFramesIn, ma_uint64 frameCountIn, void* pFramesOut,
ma_uint64& frameCountOut) {
if (pFramesIn == nullptr || pFramesOut == nullptr) {
return MA_INVALID_ARGS;
}

return resampler != nullptr ? ma_resampler_process_pcm_frames(&*resampler, pFramesIn, (ma_uint64*)&frameCountIn,
pFramesOut, &frameCountOut)
: MA_ERROR;
if (resampler == nullptr) {
return MA_ERROR;
}

ma_uint64 inFrames = frameCountIn;
return ma_resampler_process_pcm_frames(&*resampler, pFramesIn, &inFrames, pFramesOut, &frameCountOut);
}

private:
Expand All @@ -149,7 +153,17 @@ class CResampler {

class Audio {
public:
Audio() : m_device(nullptr) { m_selectedDeviceID = getDevicesList()[0].id; }
Audio() : m_device(nullptr), m_hasCurrentDevice(false) {
auto devices = getDevicesList();
if (devices.empty()) {
spdlog::warn("No audio devices found during Audio initialization");
std::memset(&m_selectedDeviceID, 0, sizeof(m_selectedDeviceID));
std::memset(&m_currentDeviceID, 0, sizeof(m_currentDeviceID));
return;
}
m_selectedDeviceID = devices[0].id;
std::memset(&m_currentDeviceID, 0, sizeof(m_currentDeviceID));
}
~Audio() = default;

std::vector<DeviceInfo> getDevicesList();
Expand All @@ -164,16 +178,18 @@ class Audio {
std::unique_ptr<CResampler> m_resampler;
ma_device_id m_selectedDeviceID;
ma_device_id m_currentDeviceID;
bool m_hasCurrentDevice;
std::vector<DeviceInfo> m_lastDevicesList;

void updateDevice() {
if (ma_device_id_equal(&m_currentDeviceID, &m_selectedDeviceID)) {
if (m_hasCurrentDevice && ma_device_id_equal(&m_currentDeviceID, &m_selectedDeviceID)) {
return;
}
spdlog::debug("Initializing new audio device");
m_device = std::make_unique<CDevice>(&m_selectedDeviceID, &Audio::audioDataCallback);
ma_device_start(*m_device);
m_currentDeviceID = m_selectedDeviceID;
m_hasCurrentDevice = true;
}

void updateResampler(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) {
Expand Down
22 changes: 16 additions & 6 deletions src/speech.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "unsupportedVoicesFilter.h"

#include <climits>
#include <cstdlib>
#include <memory>
#include <spdlog/spdlog.h>

Expand Down Expand Up @@ -61,13 +62,22 @@ bool Speech::speak(const char* text) {
spdlog::warn("Trying to speak with unsupported voice");
return false;
}
uint64_t bufferSize;
int channels;
int sampleRate;
int bitsPerSample;
uint64_t bufferSize = 0;
int channels = 0;
int sampleRate = 0;
int bitsPerSample = 0;
auto* data = SRAL_SpeakToMemoryEx(SRAL_ENGINE_SAPI, text, &bufferSize, &channels, &sampleRate, &bitsPerSample);
g_Audio.playAudioData(channels, sampleRate, bitsPerSample, bufferSize, data);
return true;
if (data == nullptr) {
spdlog::error("SRAL_SpeakToMemoryEx returned nullptr");
return false;
}
if (channels <= 0 || sampleRate <= 0 || bitsPerSample <= 0) {
spdlog::error("SRAL returned invalid audio metadata: channels={}, sampleRate={}, bitsPerSample={}", channels,
sampleRate, bitsPerSample);
free(data);
return false;
}
return g_Audio.playAudioData(channels, sampleRate, bitsPerSample, bufferSize, data);
}

bool Speech::setRate(uint64_t rate) {
Expand Down
25 changes: 22 additions & 3 deletions src/ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ void MainFrame::populateVoicesList() {
auto voices = Speech::GetInstance().getVoicesList();
if (voices.empty()) {
m_voicesList->AppendString("No voices available");
spdlog::warn("No voices available, voice selection is disabled");
return;
}
size_t voiceCounter = 0;
bool isVoiceFoundByName = false;
Expand All @@ -105,8 +107,12 @@ void MainFrame::populateVoicesList() {
}
voiceCounter++;
}
if (m_cliVoiceIndex < 0 || static_cast<size_t>(m_cliVoiceIndex) >= voices.size()) {
spdlog::warn("Voice index {} is out of range. Falling back to 0.", m_cliVoiceIndex);
m_cliVoiceIndex = 0;
}
m_voicesList->SetSelection(m_cliVoiceIndex);
Speech::GetInstance().setVoice(m_cliVoiceIndex);
Speech::GetInstance().setVoice(static_cast<uint64_t>(m_cliVoiceIndex));
}

void MainFrame::populateDevicesList() {
Expand All @@ -120,7 +126,12 @@ void MainFrame::populateDevicesList() {
auto isDefaultStr = device.isDefault ? "[default]" : "";
m_outputDevicesList->AppendString(wxString::FromUTF8(std::format("{} {}", isDefaultStr, device.name)));
}
if (m_cliOutputDeviceIndex < 0 || static_cast<size_t>(m_cliOutputDeviceIndex) >= devices.size()) {
spdlog::warn("Device index {} is out of range. Falling back to 0.", m_cliOutputDeviceIndex);
m_cliOutputDeviceIndex = 0;
}
m_outputDevicesList->SetSelection(m_cliOutputDeviceIndex);
g_Audio.selectDevice(static_cast<size_t>(m_cliOutputDeviceIndex));
}

void MainFrame::OnRateSliderChange(wxCommandEvent& event) {
Expand Down Expand Up @@ -162,12 +173,20 @@ void MainFrame::OnMessageFieldKeyDown(wxKeyEvent& event) {

void MainFrame::OnVoiceChange(wxCommandEvent& event) {
int value = m_voicesList->GetSelection();
Speech::GetInstance().setVoice(value);
if (value == wxNOT_FOUND) {
spdlog::warn("Voice selection event received with no selection");
return;
}
Speech::GetInstance().setVoice(static_cast<uint64_t>(value));
}

void MainFrame::OnOutputDeviceChange(wxCommandEvent& event) {
int value = m_outputDevicesList->GetSelection();
g_Audio.selectDevice(value);
if (value == wxNOT_FOUND) {
spdlog::warn("Device selection event received with no selection");
return;
}
g_Audio.selectDevice(static_cast<size_t>(value));
}

void MainFrame::OnCharEvent(wxKeyEvent& event) {
Expand Down