Dummy audio thread to keep display responsive when no audio device.

This commit is contained in:
Robin Davies
2024-08-26 22:42:15 -04:00
parent 150ca25491
commit 179d1aa025
18 changed files with 972 additions and 332 deletions
+131 -113
View File
@@ -74,7 +74,7 @@ PiPedalModel::PiPedalModel()
this->jackServerSettings = this->storage.GetJackServerSettings();
#endif
updater.SetUpdateListener(
[this](const UpdateStatus&updateStatus)
[this](const UpdateStatus &updateStatus)
{
this->OnUpdateStatusChanged(updateStatus);
});
@@ -247,64 +247,8 @@ void PiPedalModel::Load()
scheduler_params.sched_priority = 10;
memset(&scheduler_params, 0, sizeof(sched_param));
sched_setscheduler(0, SCHED_RR, &scheduler_params);
#if JACK_HOST
this->jackConfiguration = this->jackConfiguration.JackInitialize();
#else
this->jackServerSettings = storage.GetJackServerSettings();
if (jackServerSettings.IsValid())
{
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
if (!jackServerSettings.IsValid())
{
for (size_t retry = 0; retry < 5; ++retry)
{
// retry until the device comes online.
sleep(3);
Lv2Log::info("Retrying AlsaInitialize");
jackConfiguration.isValid(true);
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
if (jackConfiguration.isValid())
{
Lv2Log::info("Retry succeeded.");
break;
}
}
}
}
else
{
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
}
#endif
if (this->jackConfiguration.isValid())
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
this->pluginHost.OnConfigurationChanged(jackConfiguration, selection);
try
{
audioHost->Open(this->jackServerSettings, selection);
bool loadedSuccessfully = false;
try
{
LoadCurrentPedalboard();
loadedSuccessfully = true;
}
catch (const std::exception &e)
{
Lv2Log::error("Failed to load initial plugin. %s", e.what());
}
}
catch (std::exception &e)
{
Lv2Log::error("Failed to start audio device. %s", e.what());
}
}
else
{
Lv2Log::error("Audio device not configured.");
}
RestartAudio();
}
IPiPedalModelSubscriber *PiPedalModel::GetNotificationSubscriber(int64_t clientId)
@@ -391,7 +335,6 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId)
item->stateUpdateCount(item->stateUpdateCount() + 1);
Lv2PluginState newState = item->lv2State();
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i)
@@ -1143,59 +1086,65 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
return this->jackConfiguration;
}
void PiPedalModel::RestartAudio()
void PiPedalModel::RestartAudio(bool useDummyAudioDriver)
{
if (this->audioHost->IsOpen())
{
this->audioHost->Close();
}
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread.
// for the meantime, just rely on the fact that the admin service will restart
// the process.
//...
// do a complete reload.
this->audioHost->SetPedalboard(nullptr);
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
if (this->jackConfiguration.isValid())
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
}
else
{
jackConfiguration.setErrorStatus("Error");
}
FireJackConfigurationChanged(jackConfiguration);
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{
Lv2Log::error("Audio configuration not valid.");
return;
}
try
{
JackChannelSelection channelSelection = GetJackChannelSelection();
if (this->audioHost->IsOpen())
{
this->audioHost->Close();
}
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread.
// do a complete reload.
this->audioHost->SetPedalboard(nullptr);
auto jackServerSettings = this->jackServerSettings;
if (useDummyAudioDriver)
{
jackServerSettings.UseDummyAudioDevice();
}
auto jackConfiguration = this->jackConfiguration;
jackConfiguration.AlsaInitialize(jackServerSettings);
if (jackConfiguration.isValid())
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
}
else
{
jackConfiguration.setErrorStatus("Error");
}
if (!useDummyAudioDriver)
{
this->jackConfiguration = jackConfiguration;
FireJackConfigurationChanged(jackConfiguration);
}
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{
throw std::runtime_error("Audio configuration not valid.");
}
JackChannelSelection channelSelection = this->storage.GetJackChannelSelection(jackConfiguration);
if (this->jackConfiguration.isValid())
{
channelSelection.RemoveInvalidChannels(this->jackConfiguration);
}
if (!channelSelection.isValid())
{
Lv2Log::error("Audio configuration not valid.");
return;
throw std::runtime_error("Audio configuration not valid.");
}
this->audioHost->Open(this->jackServerSettings, channelSelection);
this->audioHost->Open(jackServerSettings, channelSelection);
this->pluginHost.OnConfigurationChanged(jackConfiguration, channelSelection);
std::vector<std::string> errorMessages;
LoadCurrentPedalboard();
this->UpdateRealtimeVuSubscriptions();
@@ -1203,8 +1152,19 @@ void PiPedalModel::RestartAudio()
}
catch (const std::exception &e)
{
Lv2Log::error(SS("Failed to start audio. " << e.what()));
throw;
this->audioHost->Close();
if (useDummyAudioDriver)
{
Lv2Log::error(SS("Failed to start dummy audio driver. " << e.what()));
}
else
{
Lv2Log::error(SS("Failed to start audio. " << e.what()));
}
if (!useDummyAudioDriver)
{
RestartAudio(true); // use the dummy audio driver.
}
}
}
@@ -2178,7 +2138,7 @@ void PiPedalModel::SetRestartListener(std::function<void(void)> &&listener)
this->restartListener = std::move(listener);
}
void PiPedalModel::OnUpdateStatusChanged(const UpdateStatus&updateStatus)
void PiPedalModel::OnUpdateStatusChanged(const UpdateStatus &updateStatus)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
@@ -2188,40 +2148,98 @@ void PiPedalModel::OnUpdateStatusChanged(const UpdateStatus&updateStatus)
FireUpdateStatusChanged(this->currentUpdateStatus);
}
}
void PiPedalModel::FireUpdateStatusChanged(const UpdateStatus&updateStatus)
void PiPedalModel::FireUpdateStatusChanged(const UpdateStatus &updateStatus)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
std::vector<IPiPedalModelSubscriber *> t;
t.reserve(subscribers.size());
for (auto subscriber: subscribers)
for (auto subscriber : subscribers)
{
t.push_back(subscriber);
}
for (auto subscriber: t)
for (auto subscriber : t)
{
subscriber->OnUpdateStatusChanged(updateStatus);
}
}
UpdateStatus PiPedalModel::GetUpdateStatus() {
UpdateStatus PiPedalModel::GetUpdateStatus()
{
std::lock_guard<std::recursive_mutex> lock(mutex);
return currentUpdateStatus;
}
void PiPedalModel::UpdateNow(const std::string&updateUrl)
void PiPedalModel::UpdateNow(const std::string &updateUrl)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
auto fileName = updater.DownloadUpdate(updateUrl);
adminClient.InstallUpdate(fileName);
}
void PiPedalModel::ForceUpdateCheck() {
void PiPedalModel::ForceUpdateCheck()
{
updater.ForceUpdateCheck();
}
void PiPedalModel::SetUpdatePolicy(UpdatePolicyT updatePolicy)
{
updater.SetUpdatePolicy(updatePolicy);
}
static bool HasAlsaDevice(const std::vector<AlsaDeviceInfo> devices, const std::string &deviceId)
{
for (auto &device : devices)
{
if (device.id_ == deviceId)
return true;
}
return false;
}
void PiPedalModel::WaitForAudioDeviceToComeOnline()
{
auto serverSettings = this->GetJackServerSettings();
// Wait for selected audio device to be initialized.
// It may take some time for ALSA to publish all available devices when rebooting.
if (serverSettings.IsValid())
{
// wait up to 15 seconds for the midi device to come online.
auto devices = GetAlsaDevices();
bool found = false;
if (HasAlsaDevice(devices, serverSettings.GetAlsaInputDevice()))
{
Lv2Log::info(SS("Found ALSA device " << serverSettings.GetAlsaInputDevice() << "."));
}
else
{
Lv2Log::info(SS("Waiting for ALSA device " << serverSettings.GetAlsaInputDevice() << "."));
for (int i = 0; i < 5; ++i)
{
sleep(2);
devices = GetAlsaDevices();
if (HasAlsaDevice(devices, serverSettings.GetAlsaInputDevice()))
{
found = true;
break;
}
}
if (found)
{
Lv2Log::info(SS("Found ALSA device " << serverSettings.GetAlsaInputDevice() << "."));
}
else
{
Lv2Log::info(SS("ALSA device " << serverSettings.GetAlsaInputDevice() << " not found."));
}
}
}
else
{
Lv2Log::info("No ALSA device selected.");
}
// pre-cache device info before we let audio services run.
GetAlsaDevices();
}