Tidying up.

This commit is contained in:
Robin E. R. Davies
2025-08-06 05:36:27 -04:00
parent b1f80785a1
commit 5383a55ebd
+66 -34
View File
@@ -245,6 +245,7 @@ namespace pipedal
class AlsaDriverImpl : public AudioDriver class AlsaDriverImpl : public AudioDriver
{ {
private: private:
void TraceBuffers(size_t framesInBuffers, char code = ' ');
std::recursive_mutex restartMutex; std::recursive_mutex restartMutex;
pipedal::CpuUse cpuUse; pipedal::CpuUse cpuUse;
@@ -418,7 +419,6 @@ namespace pipedal
snd_pcm_sw_params_free(playbackSwParams); snd_pcm_sw_params_free(playbackSwParams);
playbackSwParams = nullptr; playbackSwParams = nullptr;
} }
} }
void AlsaCleanup() void AlsaCleanup()
{ {
@@ -427,7 +427,6 @@ namespace pipedal
std::string discover_alsa_using_apps() std::string discover_alsa_using_apps()
{ {
return ""; // xxx fix me. return ""; // xxx fix me.
} }
@@ -1102,18 +1101,24 @@ namespace pipedal
std::lock_guard lock{restartMutex}; std::lock_guard lock{restartMutex};
Lv2Log::info("Restarting ALSA devices."); Lv2Log::info("Restarting ALSA devices.");
try { try
{
AlsaCloseAudio(); AlsaCloseAudio();
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
Lv2Log::error(SS("Error cleaning up ALSA: " << e.what())); Lv2Log::error(SS("Error cleaning up ALSA: " << e.what()));
throw std::runtime_error("Unable to restart the audio stream."); throw std::runtime_error("Unable to restart the audio stream.");
} }
try { try
{
OpenAudio(this->jackServerSettings, this->channelSelection); OpenAudio(this->jackServerSettings, this->channelSelection);
validate_capture_handle(); validate_capture_handle();
FillOutputBuffer(); FillOutputBuffer();
audioRunning = true; audioRunning = true;
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
Lv2Log::error(SS("Error opening ALSA: " << e.what())); Lv2Log::error(SS("Error opening ALSA: " << e.what()));
throw std::runtime_error("Unable to restart the audio stream."); throw std::runtime_error("Unable to restart the audio stream.");
} }
@@ -1123,7 +1128,6 @@ namespace pipedal
Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err))); Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err)));
throw PiPedalStateException("Unable to restart ALSA capture."); throw PiPedalStateException("Unable to restart ALSA capture.");
} }
} }
void PrepareCaptureFunctions(snd_pcm_format_t captureFormat) void PrepareCaptureFunctions(snd_pcm_format_t captureFormat)
@@ -1262,7 +1266,7 @@ namespace pipedal
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection) void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
{ {
std::lock_guard lock{ restartMutex }; std::lock_guard lock{restartMutex};
int err; int err;
@@ -1418,6 +1422,9 @@ namespace pipedal
} }
if (avail == 0) if (avail == 0)
break; break;
if (avail > this->bufferSize)
avail = this->bufferSize;
ssize_t err = WriteBuffer(playbackHandle, rawPlaybackBuffer.data(), avail); ssize_t err = WriteBuffer(playbackHandle, rawPlaybackBuffer.data(), avail);
if (err < 0) if (err < 0)
{ {
@@ -1426,13 +1433,13 @@ namespace pipedal
} }
validate_capture_handle(); validate_capture_handle();
} }
void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err) void recover_from_output_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err, size_t framesRead)
{ {
// Lv2Log::error("XXX: Output underrun");
validate_capture_handle(); validate_capture_handle();
try { try
{
TraceBuffers(framesRead,'w');
if (err == -EPIPE) if (err == -EPIPE)
{ {
err = snd_pcm_prepare(playback_handle); err = snd_pcm_prepare(playback_handle);
@@ -1449,31 +1456,28 @@ namespace pipedal
Lv2Log::error(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")")); Lv2Log::error(SS("Can't recover from ALSA output underrun. (" << snd_strerror(err) << ")"));
throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")")); throw PiPedalStateException(SS("Can't recover from ALSA output error. (" << snd_strerror(err) << ")"));
} }
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
Lv2Log::info(SS("Soft audio restart failed. " << e.what())); Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
RestartAlsa(); RestartAlsa();
audioRunning = true; audioRunning = true;
} }
validate_capture_handle(); validate_capture_handle();
} }
void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err) void recover_from_input_underrun(snd_pcm_t *capture_handle, snd_pcm_t *playback_handle, int err, size_t bufferedFrames)
{ {
Lv2Log::error("XXX: Input underring");
validate_capture_handle(); validate_capture_handle();
try { try
{
TraceBuffers(bufferedFrames,'r');
if (err == -EPIPE) if (err == -EPIPE)
{ {
// Unlink the streams before recovery // Unlink the streams before recovery
snd_pcm_unlink(capture_handle); snd_pcm_unlink(capture_handle);
err = snd_pcm_drain(capture_handle);
if (err < 0)
{
throw PiPedalStateException(SS("Can't recover from ALSA underrun. (" << snd_strerror(err) << ")"));
}
// Prepare both streams // Prepare both streams
if ((err = snd_pcm_prepare(playback_handle)) < 0) if ((err = snd_pcm_prepare(playback_handle)) < 0)
{ {
@@ -1484,7 +1488,6 @@ namespace pipedal
throw std::runtime_error(SS("Cannot prepare capture stream: " << snd_strerror(err))); throw std::runtime_error(SS("Cannot prepare capture stream: " << snd_strerror(err)));
} }
// Resynchronize the streams // Resynchronize the streams
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0) if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0)
{ {
@@ -1497,7 +1500,7 @@ namespace pipedal
{ {
throw std::runtime_error(SS("Cannot restart capture stream: " << snd_strerror(err))); throw std::runtime_error(SS("Cannot restart capture stream: " << snd_strerror(err)));
} }
validate_capture_handle(); validate_capture_handle();
} }
else if (err == ESTRPIPE) else if (err == ESTRPIPE)
@@ -1507,7 +1510,7 @@ namespace pipedal
while ((err = snd_pcm_resume(capture_handle)) == -EAGAIN) while ((err = snd_pcm_resume(capture_handle)) == -EAGAIN)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
if (err < 0) if (err < 0)
{ {
@@ -1524,11 +1527,12 @@ namespace pipedal
{ {
throw PiPedalStateException(SS("Can't recover from ALSA input error. (" << snd_strerror(err) << ")")); throw PiPedalStateException(SS("Can't recover from ALSA input error. (" << snd_strerror(err) << ")"));
} }
} catch (const std::exception &e) { }
catch (const std::exception &e)
{
Lv2Log::info(SS("Soft audio restart failed. " << e.what())); Lv2Log::info(SS("Soft audio restart failed. " << e.what()));
RestartAlsa(); RestartAlsa();
audioRunning = true; audioRunning = true;
} }
} }
@@ -1571,6 +1575,7 @@ namespace pipedal
{ {
snd_pcm_wait(captureHandle, 1); snd_pcm_wait(captureHandle, 1);
} }
TraceBuffers(framesRead);
} while (frames > 0); } while (frames > 0);
return framesRead; return framesRead;
} }
@@ -1641,7 +1646,6 @@ namespace pipedal
{ {
SetThreadName("alsaDriver"); SetThreadName("alsaDriver");
try try
{ {
SetThreadPriority(SchedulerPriority::RealtimeAudio); SetThreadPriority(SchedulerPriority::RealtimeAudio);
@@ -1659,7 +1663,7 @@ namespace pipedal
} }
CrashGuardLock crashGuardLock; CrashGuardLock crashGuardLock;
cpuUse.SetStartTime(cpuUse.Now()); cpuUse.SetStartTime(cpuUse.Now());
while (true) while (true)
{ {
@@ -1690,7 +1694,7 @@ namespace pipedal
framesToRead)) < 0) framesToRead)) < 0)
{ {
this->driverHost->OnUnderrun(); this->driverHost->OnUnderrun();
recover_from_input_underrun(captureHandle, playbackHandle, nFrames); recover_from_input_underrun(captureHandle, playbackHandle, nFrames, framesRead);
xrun = true; xrun = true;
break; break;
} }
@@ -1727,8 +1731,8 @@ namespace pipedal
if (err < 0) if (err < 0)
{ {
this->driverHost->OnUnderrun(); this->driverHost->OnUnderrun();
recover_from_output_underrun(captureHandle, playbackHandle, err); recover_from_output_underrun(captureHandle, playbackHandle, err, framesRead);
} }
cpuUse.AddSample(ProfileCategory::Write); cpuUse.AddSample(ProfileCategory::Write);
} }
@@ -1757,7 +1761,6 @@ namespace pipedal
while (!terminateAudio()) while (!terminateAudio())
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
// zero out input buffers.
this->driverHost->OnProcess(this->bufferSize); this->driverHost->OnProcess(this->bufferSize);
} }
} }
@@ -1829,7 +1832,7 @@ namespace pipedal
} }
audioThread = std::make_unique<std::jthread>([this]() audioThread = std::make_unique<std::jthread>([this]()
{ AudioThread(); }); { AudioThread(); });
} }
virtual void Deactivate() virtual void Deactivate()
@@ -2267,6 +2270,35 @@ namespace pipedal
#endif #endif
} }
struct BufferTrace
{
snd_pcm_sframes_t inAvail;
snd_pcm_sframes_t outAvail;
snd_pcm_sframes_t buffered;
snd_pcm_sframes_t total;
char code;
};
std::vector<BufferTrace> bufferTraces(1000);
size_t bufferTraceIndex;
void AlsaDriverImpl::TraceBuffers(size_t framesInBuffer,char code)
{
auto inAvail = snd_pcm_avail_update(this->captureHandle);
auto outAvail = snd_pcm_avail_update(this->playbackHandle);
auto total = inAvail + outAvail + framesInBuffer;
bufferTraces[bufferTraceIndex++] = {
inAvail,
outAvail,
(snd_pcm_sframes_t)framesInBuffer,
(snd_pcm_sframes_t)total,
code};
if (bufferTraceIndex == bufferTraces.size())
{
bufferTraceIndex = 0;
}
}
void FreeAlsaGlobals() void FreeAlsaGlobals()
{ {