Skip to content

Commit

Permalink
Rework sample rate handling
Browse files Browse the repository at this point in the history
- Device::SampleRate is renamed to Device::NominalSampleRate
  to make it clear that it's different from streams rates

- Stream::SetPhysicalSampleRateAsync and SetVirtualSampleRateAsync
  methods are removed

- When device or stream sample rates are updated, they are not
  propagated anymore to all streams and root device
  • Loading branch information
gavv committed Jun 26, 2023
1 parent 6eef024 commit af501ee
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 187 deletions.
26 changes: 13 additions & 13 deletions include/aspl/Device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct DeviceParameters
bool CanBeDefaultForSystemSounds = true;

//! Device sample rate.
//! Used by default implementation of Device::GetSampleRate().
//! Used by default implementation of Device::GetNominalSampleRate().
UInt32 SampleRate = 44100;

//! Preferred number of channels.
Expand Down Expand Up @@ -330,26 +330,28 @@ class Device : public Object
OSStatus SetZeroTimeStampPeriodAsync(UInt32 period);

//! Get nominal sample rate.
//! By default returns the last value set by SetSampleRateAsync().
//! By default returns the last value set by SetNominalSampleRateAsync().
//! Initial value is DeviceParameters::SampleRate.
//! Note that each device stream can define its own sample rate in its
//! physical and virtual formats.
//! @note
//! Backs @c kAudioDevicePropertyNominalSampleRate property.
virtual Float64 GetSampleRate() const;
virtual Float64 GetNominalSampleRate() const;

//! Asynchronously set nominal sample rate.
//! Requests HAL to asynchronously invoke SetSampleRateImpl().
//! Requests HAL to asynchronously invoke SetNominalSampleRateImpl().
//! Fails if rate is not present in GetAvailableSampleRates(), which by default
//! returns only one rate, provided during initialization.
//! If you want to make your device supporting multiple rates, you typically
//! need to override both of these methods.
//! @note
//! Backs @c kAudioDevicePropertyNominalSampleRate property.
OSStatus SetSampleRateAsync(Float64 rate);
OSStatus SetNominalSampleRateAsync(Float64 rate);

//! Get list of supported nominal sample rates.
//! By default returns the list set by SetAvailableSampleRatesAsync().
//! If nothing was set, returns a single-element list with a range which min and max
//! are both set to the value returned by GetSampleRate().
//! are both set to the value returned by GetNominalSampleRate().
//! @remarks
//! Empty list means that any rate is allowed.
//! For discrete sampler rates, the range should have the minimum value equal
Expand Down Expand Up @@ -970,13 +972,11 @@ class Device : public Object
virtual OSStatus SetZeroTimeStampPeriodImpl(UInt32 period);

//! Set nominal sample rate.
//! Invoked by SetSampleRateAsync() to actually change the rate.
//! Default implementation changes the value returned by GetSampleRate() and then
//! invokes Stream::SetPhysicalSampleRateAsync() on every stream, to ensure that
//! device and all streams have the same sample rate.
//! Invoked by SetNominalSampleRateAsync() to actually change the rate.
//! Default implementation just changes the value returned by GetNominalSampleRate().
//! @note
//! Backs @c kAudioDevicePropertyNominalSampleRate property.
virtual OSStatus SetSampleRateImpl(Float64 rate);
virtual OSStatus SetNominalSampleRateImpl(Float64 rate);

//! Set list of supported nominal sample rates.
//! Invoked by SetAvailableSampleRatesAsync() to actually change the list.
Expand Down Expand Up @@ -1042,7 +1042,7 @@ class Device : public Object

private:
// value checkers for async setters
OSStatus CheckSampleRate(Float64 rate) const;
OSStatus CheckNominalSampleRate(Float64 rate) const;

// these fields are immutable and can be accessed w/o lock
const DeviceParameters params_;
Expand All @@ -1061,7 +1061,7 @@ class Device : public Object
std::atomic<UInt32> latency_;
std::atomic<UInt32> safetyOffset_;
std::atomic<UInt32> zeroTimeStampPeriod_;
std::atomic<Float64> sampleRate_;
std::atomic<Float64> nominalSampleRate_;

std::atomic<SInt32> startCount_ = 0;

Expand Down
12 changes: 0 additions & 12 deletions include/aspl/Stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,6 @@ class Stream : public Object
//! Backs @c kAudioStreamPropertyPhysicalFormat property.
OSStatus SetPhysicalFormatAsync(AudioStreamBasicDescription format);

//! Set rate in the current physical format.
//! Invokes SetPhysicalFormatAsync().
//! @note
//! Used by Device::SetSampleRateAsync().
OSStatus SetPhysicalSampleRateAsync(Float64 rate);

//! Get list of supported physical formats.
//! Empty list means that any format is allowed.
//! By default returns the last value set by SetAvailablePhysicalFormatsAsync().
Expand Down Expand Up @@ -206,12 +200,6 @@ class Stream : public Object
//! Backs @c kAudioStreamPropertyVirtualFormat property.
OSStatus SetVirtualFormatAsync(AudioStreamBasicDescription format);

//! Set rate in the current virtual format.
//! Invokes SetVirtualFormatAsync().
//! @note
//! Used by Device::SetSampleRateAsync().
OSStatus SetVirtualSampleRateAsync(Float64 rate);

//! Get list of supported virtual formats.
//! Empty list means that any format is allowed.
//! By default returns the last value set by SetAvailableVirtualFormatsAsync().
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge.g.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Generator: generate-bridge.py
// Source: Bridge.json
// Timestamp: Thu Feb 03 11:44:38 2022 UTC
// Timestamp: Mon Jun 26 14:04:30 2023 UTC

// Copyright (c) libASPL authors
// Licensed under MIT
Expand Down
38 changes: 11 additions & 27 deletions src/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Device::Device(std::shared_ptr<const Context> context, const DeviceParameters& p
, safetyOffset_(params.SafetyOffset)
, zeroTimeStampPeriod_(
params_.ZeroTimeStampPeriod ? params_.ZeroTimeStampPeriod : params_.SampleRate)
, sampleRate_(params.SampleRate)
, nominalSampleRate_(params.SampleRate)
, preferredChannelsForStereo_({1, 2})
{
SetControlHandler(nullptr);
Expand Down Expand Up @@ -133,12 +133,12 @@ OSStatus Device::SetZeroTimeStampPeriodImpl(UInt32 period)
return kAudioHardwareNoError;
}

Float64 Device::GetSampleRate() const
Float64 Device::GetNominalSampleRate() const
{
return sampleRate_;
return nominalSampleRate_;
}

OSStatus Device::CheckSampleRate(Float64 rate) const
OSStatus Device::CheckNominalSampleRate(Float64 rate) const
{
const auto availRates = GetAvailableSampleRates();

Expand All @@ -157,28 +157,11 @@ OSStatus Device::CheckSampleRate(Float64 rate) const
return kAudioHardwareUnsupportedOperationError;
}

OSStatus Device::SetSampleRateImpl(Float64 rate)
OSStatus Device::SetNominalSampleRateImpl(Float64 rate)
{
sampleRate_ = rate;
nominalSampleRate_ = rate;

OSStatus status = kAudioHardwareNoError;

auto readLock = streams_.GetReadLock();

const auto& streams = readLock.GetReference();

for (const auto dir : {Direction::Output, Direction::Input}) {
if (streams.count(dir)) {
for (const auto& stream : streams.at(dir)) {
OSStatus streamStatus = stream->SetPhysicalSampleRateAsync(rate);
if (streamStatus != kAudioHardwareNoError) {
status = streamStatus;
}
}
}
}

return status;
return kAudioHardwareNoError;
}

std::vector<AudioValueRange> Device::GetAvailableSampleRates() const
Expand All @@ -187,7 +170,7 @@ std::vector<AudioValueRange> Device::GetAvailableSampleRates() const
return *rates;
}

const auto sampleRate = GetSampleRate();
const auto sampleRate = GetNominalSampleRate();

AudioValueRange range;
range.mMinimum = sampleRate;
Expand Down Expand Up @@ -489,7 +472,7 @@ std::shared_ptr<Stream> Device::AddStreamAsync(Direction dir)
StreamParameters params;

params.Direction = dir;
params.Format.mSampleRate = GetSampleRate();
params.Format.mSampleRate = GetNominalSampleRate();
params.Format.mChannelsPerFrame = GetPreferredChannelCount();
params.Format.mBytesPerFrame =
params.Format.mChannelsPerFrame * (params.Format.mBitsPerChannel / 8);
Expand Down Expand Up @@ -1184,7 +1167,8 @@ OSStatus Device::GetZeroTimeStamp(AudioObjectID objectID,
goto end;
}

if (const Float64 sampleRate = GetSampleRate(); sampleRate != lastSampleRate_) {
if (const Float64 sampleRate = GetNominalSampleRate();
sampleRate != lastSampleRate_) {
struct mach_timebase_info timeBase;
mach_timebase_info(&timeBase);

Expand Down
26 changes: 13 additions & 13 deletions src/Device.g.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Generator: generate-accessors.py
// Source: Device.json
// Timestamp: Fri Nov 25 16:43:25 2022 UTC
// Timestamp: Mon Jun 26 14:04:29 2023 UTC

// Copyright (c) libASPL authors
// Licensed under MIT
Expand Down Expand Up @@ -176,24 +176,24 @@ OSStatus Device::SetZeroTimeStampPeriodAsync(UInt32 value)
return status;
}

OSStatus Device::SetSampleRateAsync(Float64 value)
OSStatus Device::SetNominalSampleRateAsync(Float64 value)
{
std::lock_guard writeLock(writeMutex_);

Tracer::Operation op;
op.Name = "Device::SetSampleRateAsync()";
op.Name = "Device::SetNominalSampleRateAsync()";
op.ObjectID = GetID();

GetContext()->Tracer->OperationBegin(op);

OSStatus status = kAudioHardwareNoError;

if (value == GetSampleRate()) {
if (value == GetNominalSampleRate()) {
GetContext()->Tracer->Message("value not changed");
goto end;
}

status = CheckSampleRate(value);
status = CheckNominalSampleRate(value);
if (status != kAudioHardwareNoError) {
GetContext()->Tracer->Message("value is invalid");
goto end;
Expand All @@ -203,20 +203,20 @@ OSStatus Device::SetSampleRateAsync(Float64 value)
std::lock_guard writeLock(writeMutex_);

Tracer::Operation op;
op.Name = "Device::SetSampleRateImpl()";
op.Name = "Device::SetNominalSampleRateImpl()";
op.ObjectID = GetID();

GetContext()->Tracer->OperationBegin(op);

OSStatus status = kAudioHardwareNoError;

if (value == GetSampleRate()) {
if (value == GetNominalSampleRate()) {
GetContext()->Tracer->Message("value not changed");
} else {
GetContext()->Tracer->Message("setting value to %s",
Convert::ToString(value).c_str());

status = SetSampleRateImpl(std::move(value));
status = SetNominalSampleRateImpl(std::move(value));
}

GetContext()->Tracer->OperationEnd(op, status);
Expand Down Expand Up @@ -2068,12 +2068,12 @@ OSStatus Device::GetPropertyData(AudioObjectID objectID,
GetContext()->Tracer->Message("size buffer is null");
}
if (outData) {
const auto value = GetSampleRate();
const auto value = GetNominalSampleRate();
Convert::ToFoundation(
value,
*static_cast<Float64*>(outData));
GetContext()->Tracer->Message(
"returning SampleRate=%s",
"returning NominalSampleRate=%s",
Convert::ToString(value).c_str());
} else {
GetContext()->Tracer->Message("data buffer is null");
Expand Down Expand Up @@ -2443,14 +2443,14 @@ OSStatus Device::SetPropertyData(AudioObjectID objectID,
goto end;
}
std::remove_cv_t<std::remove_reference_t<
decltype(GetSampleRate())>> value;
decltype(GetNominalSampleRate())>> value;
Convert::FromFoundation(
*static_cast<const Float64*>(inData),
value);
GetContext()->Tracer->Message(
"setting SampleRate=%s",
"setting NominalSampleRate=%s",
Convert::ToString(value).c_str());
status = SetSampleRateAsync(std::move(value));
status = SetNominalSampleRateAsync(std::move(value));
if (status != kAudioHardwareNoError) {
GetContext()->Tracer->Message("setter failed");
goto end;
Expand Down
2 changes: 1 addition & 1 deletion src/Device.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"is_user_settable": true,
"is_async": true
},
"SampleRate": {
"NominalSampleRate": {
"id": "kAudioDevicePropertyNominalSampleRate",
"type": "Float64",
"is_settable": true,
Expand Down
2 changes: 1 addition & 1 deletion src/MuteControl.g.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Generator: generate-accessors.py
// Source: MuteControl.json
// Timestamp: Fri Nov 25 16:43:25 2022 UTC
// Timestamp: Mon Jun 26 14:04:30 2023 UTC

// Copyright (c) libASPL authors
// Licensed under MIT
Expand Down
2 changes: 1 addition & 1 deletion src/Object.g.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Generator: generate-accessors.py
// Source: Object.json
// Timestamp: Fri Nov 25 16:43:25 2022 UTC
// Timestamp: Mon Jun 26 14:04:30 2023 UTC

// Copyright (c) libASPL authors
// Licensed under MIT
Expand Down
2 changes: 1 addition & 1 deletion src/Plugin.g.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Generator: generate-accessors.py
// Source: Plugin.json
// Timestamp: Fri Nov 25 16:43:25 2022 UTC
// Timestamp: Mon Jun 26 14:04:30 2023 UTC

// Copyright (c) libASPL authors
// Licensed under MIT
Expand Down
56 changes: 2 additions & 54 deletions src/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,33 +99,7 @@ OSStatus Stream::SetPhysicalFormatImpl(const AudioStreamBasicDescription& format
{
physicalFormat_.Set(format);

OSStatus status = kAudioHardwareNoError;

if (auto device = device_.lock()) {
status = device->SetSampleRateAsync(format.mSampleRate);
}

return status;
}

OSStatus Stream::SetPhysicalSampleRateAsync(Float64 rate)
{
std::lock_guard writeLock(writeMutex_);

Tracer::Operation op;
op.Name = "Stream::SetPhysicalSampleRateAsync()";
op.ObjectID = GetID();

GetContext()->Tracer->OperationBegin(op);

auto format = GetPhysicalFormat();
format.mSampleRate = rate;

const auto status = SetPhysicalFormatAsync(format);

GetContext()->Tracer->OperationEnd(op, status);

return status;
return kAudioHardwareNoError;
}

std::vector<AudioStreamRangedDescription> Stream::GetAvailablePhysicalFormats() const
Expand Down Expand Up @@ -179,33 +153,7 @@ OSStatus Stream::SetVirtualFormatImpl(const AudioStreamBasicDescription& format)
{
virtualFormat_.Set(format);

OSStatus status = kAudioHardwareNoError;

if (auto device = device_.lock()) {
status = device->SetSampleRateAsync(format.mSampleRate);
}

return status;
}

OSStatus Stream::SetVirtualSampleRateAsync(Float64 rate)
{
std::lock_guard writeLock(writeMutex_);

Tracer::Operation op;
op.Name = "Stream::SetVirtualSampleRateAsync()";
op.ObjectID = GetID();

GetContext()->Tracer->OperationBegin(op);

auto format = GetVirtualFormat();
format.mSampleRate = rate;

const auto status = SetVirtualFormatAsync(format);

GetContext()->Tracer->OperationEnd(op, status);

return status;
return kAudioHardwareNoError;
}

std::vector<AudioStreamRangedDescription> Stream::GetAvailableVirtualFormats() const
Expand Down
Loading

0 comments on commit af501ee

Please sign in to comment.