Skip to content

Commit

Permalink
Lazily initialize PortAudio
Browse files Browse the repository at this point in the history
  • Loading branch information
sakertooth committed Mar 1, 2025
1 parent b8914c5 commit a47c8ac
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 34 deletions.
2 changes: 2 additions & 0 deletions include/AudioPortAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ class AudioPortAudioSetupWidget : public AudioDeviceSetupWidget
AudioPortAudioSetupWidget(QWidget* parent);
~AudioPortAudioSetupWidget();

void show() override;
void saveSettings() override;

private:
class DeviceSpecWidget;
QComboBox* m_backendComboBox = nullptr;
DeviceSpecWidget* m_inputDevice = nullptr;
DeviceSpecWidget* m_outputDevice = nullptr;
PaError m_portAudioInitError = paNoError;
};
} // namespace lmms::gui

Expand Down
80 changes: 46 additions & 34 deletions src/core/audio/AudioPortAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ AudioPortAudio::AudioPortAudio(AudioEngine* engine)
throw std::runtime_error{std::string{"PortAudio: could not open stream, "} + Pa_GetErrorText(err)};
}

setSampleRate(sampleRate);
setChannels(outputStreamParameters.channelCount);
setSampleRate(sampleRate);
setChannels(outputStreamParameters.channelCount);
}

AudioPortAudio::~AudioPortAudio()
Expand Down Expand Up @@ -249,19 +249,19 @@ class AudioPortAudioSetupWidget::DeviceSpecWidget : public QGroupBox
: QGroupBox{parent}
, m_direction(direction)
{
m_deviceComboBox = new QComboBox{this};
m_channelSpinBox = new LcdSpinBox{1, this};
m_deviceComboBox = new QComboBox{this};
m_channelSpinBox = new LcdSpinBox{1, this};

const auto layout = new QFormLayout{this};
auto rowHeader = "";
auto rowHeader = "";

switch (direction)
{
switch (direction)
{
case Direction::Input:
rowHeader = "Input device";
break;
rowHeader = "Input device";
break;
case Direction::Output:
rowHeader = "Output device";
rowHeader = "Output device";
break;
}

Expand Down Expand Up @@ -295,22 +295,22 @@ class AudioPortAudioSetupWidget::DeviceSpecWidget : public QGroupBox
const auto selectedDeviceName = ConfigManager::inst()->value(tag(), deviceNameAttribute(m_direction));
const auto selectedDeviceIndex = std::max(0, m_deviceComboBox->findText(selectedDeviceName));
m_deviceComboBox->setCurrentIndex(selectedDeviceIndex);
refreshChannelRange();

refreshChannelRange();

const auto defaultNumChannels = QString::number(DEFAULT_CHANNELS);
const auto selectedNumChannels
= ConfigManager::inst()->value(tag(), channelsAttribute(m_direction), defaultNumChannels);
= ConfigManager::inst()->value(tag(), channelsAttribute(m_direction), defaultNumChannels);
m_channelModel.setValue(selectedNumChannels.toInt());
}

void refreshChannelRange()
{
const auto deviceIndex = m_deviceComboBox->currentData().toInt();
void refreshChannelRange()
{
const auto deviceIndex = m_deviceComboBox->currentData().toInt();
const auto deviceSpec = DeviceSpec::loadFromIndex(deviceIndex, m_direction);
m_channelModel.setRange(1, deviceSpec.maxChannels());
m_channelSpinBox->setNumDigits(QString::number(deviceSpec.maxChannels()).length());
}
}

void saveToConfig()
{
Expand All @@ -328,41 +328,53 @@ class AudioPortAudioSetupWidget::DeviceSpecWidget : public QGroupBox
AudioPortAudioSetupWidget::AudioPortAudioSetupWidget(QWidget* parent)
: AudioDeviceSetupWidget{AudioPortAudio::name(), parent}
{
if (Pa_Initialize() != paNoError) { throw std::runtime_error{"PortAudio: could not initialize"}; }

const auto form = new QFormLayout{this};
form->setRowWrapPolicy(QFormLayout::WrapLongRows);
form->setVerticalSpacing(10);

m_backendComboBox = new QComboBox{};
for (auto i = 0, backendCount = Pa_GetHostApiCount(); i < backendCount; ++i)
{
m_backendComboBox->addItem(Pa_GetHostApiInfo(i)->name, i);
}

const auto selectedBackendName = ConfigManager::inst()->value(tag(), backendAttribute());
const auto selectedBackendIndex = std::max(0, m_backendComboBox->findText(selectedBackendName));
m_backendComboBox->setCurrentIndex(selectedBackendIndex);

m_backendComboBox = new QComboBox{this};
m_inputDevice = new DeviceSpecWidget{Direction::Input};
m_outputDevice = new DeviceSpecWidget{Direction::Output};

form->addRow(tr("Backend"), m_backendComboBox);
form->addRow(m_outputDevice);
form->addRow(m_inputDevice);

const auto onBackendIndexChanged = [&] {
connect(m_backendComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, [&] {
m_inputDevice->refreshFromConfig(m_backendComboBox->currentData().toInt());
m_outputDevice->refreshFromConfig(m_backendComboBox->currentData().toInt());
};
});
}

onBackendIndexChanged();
connect(m_backendComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, onBackendIndexChanged);
void AudioPortAudioSetupWidget::show()
{
if (m_portAudioInitError == paNoError)
{
m_portAudioInitError = Pa_Initialize();
if (m_portAudioInitError != paNoError) { throw std::runtime_error{"PortAudio: could not initialize"}; }
}

if (m_backendComboBox->count() == 0)
{
for (auto i = 0, backendCount = Pa_GetHostApiCount(); i < backendCount; ++i)
{
m_backendComboBox->addItem(Pa_GetHostApiInfo(i)->name, i);
}

const auto selectedBackendName = ConfigManager::inst()->value(tag(), backendAttribute());
const auto selectedBackendIndex = std::max(0, m_backendComboBox->findText(selectedBackendName));

m_backendComboBox->setCurrentIndex(selectedBackendIndex);
m_inputDevice->refreshFromConfig(m_backendComboBox->currentData().toInt());
m_outputDevice->refreshFromConfig(m_backendComboBox->currentData().toInt());
}

AudioDeviceSetupWidget::show();
}

AudioPortAudioSetupWidget::~AudioPortAudioSetupWidget()
{
Pa_Terminate();
if (m_portAudioInitError == paNoError) { Pa_Terminate(); }
}

void AudioPortAudioSetupWidget::saveSettings()
Expand Down

0 comments on commit a47c8ac

Please sign in to comment.