Skip to content
Merged
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
13 changes: 7 additions & 6 deletions src/detail/standalone/standalone_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,13 @@ bool StandaloneHost::saveStandaloneSettings()
void StandaloneHost::captureAudioSettings()
{
settings.audioApiName = audioApiName;
settings.sampleRate = currentSampleRate;
settings.bufferSize = currentBufferSize;

// Device names and used flags are deliberately not captured: they describe what
// could actually be opened (a fallback device, a side that failed to open), and
// saveSettings() runs often enough that persisting them would silently replace
// the user's choice. The frontend writes them at the point of choice instead.
// Device names, used flags and the sample rate are deliberately not captured:
// they describe what could actually be opened (a fallback device, a side that
// failed to open, a rate the device does not offer), and saveSettings() runs
// often enough that persisting them would silently replace the user's choice.
// The frontend writes them at the point of choice instead.
}

void StandaloneHost::applyAudioSettings()
Expand Down Expand Up @@ -526,7 +526,8 @@ void StandaloneHost::applyAudioSettings()
}
}

// The sample rate needs no clamp: startAudioThreadOn() validates it.
// The sample rate needs no clamp: startAudioThreadOn() resolves it against the
// device it is about to open.
}

bool StandaloneHost::isKnownDevice(unsigned int deviceID)
Expand Down
4 changes: 2 additions & 2 deletions src/detail/standalone/standalone_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ struct StandaloneHost : Clap::IHost
// Current audio configuration -> settings, and back. applyAudioSettings()
// selects the API and resolves the persisted device *names* against the
// devices this machine actually has right now.
// captureAudioSettings() writes back only the API, sample rate and buffer size;
// the device names and used flags are the user's request, written by the frontend.
// captureAudioSettings() writes back only the API and buffer size; the device
// names, used flags and sample rate are the user's request, written by the frontend.
void captureAudioSettings();
void applyAudioSettings();

Expand Down
40 changes: 21 additions & 19 deletions src/detail/standalone/standalone_host_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@

namespace freeaudio::clap_wrapper::standalone
{
namespace
{
// The requested rate if the device offers it, its preferred rate otherwise.
int32_t rateOfferedBy(const RtAudio::DeviceInfo &info, int32_t requested)
{
if (requested > 0 && std::find(info.sampleRates.begin(), info.sampleRates.end(),
static_cast<unsigned int>(requested)) != info.sampleRates.end())
{
return requested;
}

return static_cast<int32_t>(info.preferredSampleRate);
}
} // namespace

int rtaCallback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
double /* streamTime */, RtAudioStreamStatus status, void *data)
{
Expand Down Expand Up @@ -380,23 +395,7 @@ void StandaloneHost::startAudioThreadOnImpl(unsigned int inputDeviceID, uint32_t
outInfo = deviceInfoFor(outputDeviceID);
oParams.nChannels = std::min(outputChannels, outInfo.outputChannels);
oParams.firstChannel = 0;
if (sampleRate < 0)
{
sampleRate = outInfo.preferredSampleRate;
}
else
{
// Mkae sure this sample rate is available
bool isPossible{false};
for (auto sr : outInfo.sampleRates)
{
isPossible = isPossible || ((int)sr == (int)sampleRate);
}
if (!isPossible)
{
sampleRate = outInfo.preferredSampleRate;
}
}
sampleRate = rateOfferedBy(outInfo, sampleRate);
}

RtAudio::StreamParameters iParams;
Expand All @@ -406,10 +405,13 @@ void StandaloneHost::startAudioThreadOnImpl(unsigned int inputDeviceID, uint32_t
inInfo = deviceInfoFor(inputDeviceID);
iParams.nChannels = std::min(inputChannels, inInfo.inputChannels);
iParams.firstChannel = 0;
if (sampleRate < 0) sampleRate = inInfo.preferredSampleRate;

// With no output side nothing else has vetted the rate, and the .conf is
// hand-editable; openStream() would simply fail and raise a dialog.
if (!useOutput) sampleRate = rateOfferedBy(inInfo, sampleRate);
}

if (sampleRate < 0)
if (sampleRate <= 0)
{
LOGINFO("[WARNING] No preferred sample rate detected; using 48k");
sampleRate = 48000;
Expand Down
4 changes: 2 additions & 2 deletions src/detail/standalone/standalone_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ struct StandaloneSettings
std::string audioApiName; // RtAudio::getApiName(); empty means unspecified

// What the user asked for, written only at the point of choice - never from the
// device actually opened, which may be a fallback.
// device actually opened or the rate actually granted, which may be a fallback.
std::string inputDeviceName; // empty means the system default device
std::string outputDeviceName; // empty means the system default device
bool audioInputUsed{true};
bool audioOutputUsed{true}; // no UI turns this off yet; the .conf can
int32_t sampleRate{0}; // 0 means the device's preferred rate

int32_t sampleRate{0}; // 0 means the device's preferred rate
// Clamped on apply; see StandaloneHost::applyAudioSettings().
uint32_t bufferSize{defaultBufferSize};

Expand Down
5 changes: 5 additions & 0 deletions src/detail/standalone/windows/windows_standalone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ Plugin::Plugin(std::shared_ptr<Clap::Plugin> clapPlugin, int nCmdShow)
if (auto index{settings.sampleRate.selection(sampleRates.size())}; index)
{
sah->currentSampleRate = sampleRates[*index];
sah->settings.sampleRate = sampleRates[*index];

saveSettings();
startAudio();
Expand Down Expand Up @@ -1370,6 +1371,10 @@ Plugin::Plugin(std::shared_ptr<Clap::Plugin> clapPlugin, int nCmdShow)

startAudio();

// The rate asked for may have been 0 ("device preferred") or unsupported, so the
// combo can only show the truth once the stream is open.
refreshSampleRates();

// Honor the show state requested by the launcher (shortcut "Run:" / STARTUPINFO),
// falling back to a normal window. SW_HIDE would otherwise leave us invisible-but-running.
::ShowWindow(hwnd.get(), nCmdShow == SW_HIDE ? SW_SHOWNORMAL : nCmdShow);
Expand Down
Loading