From af501ee92670b07e3b5796e702dc7a943f1a8b14 Mon Sep 17 00:00:00 2001 From: Victor Gaydov Date: Mon, 26 Jun 2023 16:55:58 +0400 Subject: [PATCH] Rework sample rate handling - 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 --- include/aspl/Device.hpp | 26 ++-- include/aspl/Stream.hpp | 12 -- src/Bridge.g.cpp | 2 +- src/Device.cpp | 38 ++--- src/Device.g.cpp | 26 ++-- src/Device.json | 2 +- src/MuteControl.g.cpp | 2 +- src/Object.g.cpp | 2 +- src/Plugin.g.cpp | 2 +- src/Stream.cpp | 56 +------- src/Stream.g.cpp | 2 +- src/Strings.g.cpp | 2 +- src/VolumeControl.g.cpp | 2 +- test/TestOperations.cpp | 311 ++++++++++++++++++++++++++++++++-------- test/TestProperties.cpp | 6 +- 15 files changed, 304 insertions(+), 187 deletions(-) diff --git a/include/aspl/Device.hpp b/include/aspl/Device.hpp index dac8f4a..89ee518 100644 --- a/include/aspl/Device.hpp +++ b/include/aspl/Device.hpp @@ -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. @@ -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 @@ -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. @@ -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_; @@ -1061,7 +1061,7 @@ class Device : public Object std::atomic latency_; std::atomic safetyOffset_; std::atomic zeroTimeStampPeriod_; - std::atomic sampleRate_; + std::atomic nominalSampleRate_; std::atomic startCount_ = 0; diff --git a/include/aspl/Stream.hpp b/include/aspl/Stream.hpp index 45987e3..9e1024a 100644 --- a/include/aspl/Stream.hpp +++ b/include/aspl/Stream.hpp @@ -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(). @@ -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(). diff --git a/src/Bridge.g.cpp b/src/Bridge.g.cpp index e065d94..c7ce1a2 100644 --- a/src/Bridge.g.cpp +++ b/src/Bridge.g.cpp @@ -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 diff --git a/src/Device.cpp b/src/Device.cpp index 2358f57..737dba9 100644 --- a/src/Device.cpp +++ b/src/Device.cpp @@ -25,7 +25,7 @@ Device::Device(std::shared_ptr 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); @@ -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(); @@ -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 Device::GetAvailableSampleRates() const @@ -187,7 +170,7 @@ std::vector Device::GetAvailableSampleRates() const return *rates; } - const auto sampleRate = GetSampleRate(); + const auto sampleRate = GetNominalSampleRate(); AudioValueRange range; range.mMinimum = sampleRate; @@ -489,7 +472,7 @@ std::shared_ptr 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); @@ -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); diff --git a/src/Device.g.cpp b/src/Device.g.cpp index cf38e44..6e3cd0e 100644 --- a/src/Device.g.cpp +++ b/src/Device.g.cpp @@ -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 @@ -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; @@ -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); @@ -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(outData)); GetContext()->Tracer->Message( - "returning SampleRate=%s", + "returning NominalSampleRate=%s", Convert::ToString(value).c_str()); } else { GetContext()->Tracer->Message("data buffer is null"); @@ -2443,14 +2443,14 @@ OSStatus Device::SetPropertyData(AudioObjectID objectID, goto end; } std::remove_cv_t> value; + decltype(GetNominalSampleRate())>> value; Convert::FromFoundation( *static_cast(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; diff --git a/src/Device.json b/src/Device.json index 87d7313..82ce099 100644 --- a/src/Device.json +++ b/src/Device.json @@ -86,7 +86,7 @@ "is_user_settable": true, "is_async": true }, - "SampleRate": { + "NominalSampleRate": { "id": "kAudioDevicePropertyNominalSampleRate", "type": "Float64", "is_settable": true, diff --git a/src/MuteControl.g.cpp b/src/MuteControl.g.cpp index 39cbc17..e62ca88 100644 --- a/src/MuteControl.g.cpp +++ b/src/MuteControl.g.cpp @@ -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 diff --git a/src/Object.g.cpp b/src/Object.g.cpp index 9cc22e0..46c610c 100644 --- a/src/Object.g.cpp +++ b/src/Object.g.cpp @@ -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 diff --git a/src/Plugin.g.cpp b/src/Plugin.g.cpp index 4640715..7a09368 100644 --- a/src/Plugin.g.cpp +++ b/src/Plugin.g.cpp @@ -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 diff --git a/src/Stream.cpp b/src/Stream.cpp index 0d19ce0..f008a5b 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -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 Stream::GetAvailablePhysicalFormats() const @@ -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 Stream::GetAvailableVirtualFormats() const diff --git a/src/Stream.g.cpp b/src/Stream.g.cpp index 8437e42..d2358fa 100644 --- a/src/Stream.g.cpp +++ b/src/Stream.g.cpp @@ -2,7 +2,7 @@ // Generator: generate-accessors.py // Source: Stream.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 diff --git a/src/Strings.g.cpp b/src/Strings.g.cpp index ff52228..201972d 100644 --- a/src/Strings.g.cpp +++ b/src/Strings.g.cpp @@ -2,7 +2,7 @@ // Generator: generate-strings.py // Source: CoreAudio/AudioServerPlugIn.h -// 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 diff --git a/src/VolumeControl.g.cpp b/src/VolumeControl.g.cpp index 85a8511..a9f0125 100644 --- a/src/VolumeControl.g.cpp +++ b/src/VolumeControl.g.cpp @@ -2,7 +2,7 @@ // Generator: generate-accessors.py // Source: VolumeControl.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 diff --git a/test/TestOperations.cpp b/test/TestOperations.cpp index 2491307..37f3571 100644 --- a/test/TestOperations.cpp +++ b/test/TestOperations.cpp @@ -9,13 +9,104 @@ namespace { -template -void ExpectVectorsEq(const Vector& a, const Vector& b) +void SetupAvailNominalRates(std::shared_ptr device, + std::vector rates) { - ASSERT_EQ(a.size(), b.size()); - for (size_t i = 0; i < a.size(); i++) { - ASSERT_TRUE(a[i] == b[i]); + std::vector ranges; + + for (auto r : rates) { + AudioValueRange rng = { + .mMinimum = r, + .mMaximum = r, + }; + ranges.push_back(rng); + } + + EXPECT_EQ(kAudioHardwareNoError, device->SetAvailableSampleRatesAsync(ranges)); +} + +void SetupAvailPhysicalRates(std::shared_ptr stream, + std::vector rates) +{ + std::vector formats; + + for (auto r : rates) { + AudioStreamRangedDescription fmt = { + .mFormat = + { + .mSampleRate = r, + .mFormatID = kAudioFormatLinearPCM, + .mFormatFlags = kAudioFormatFlagIsSignedInteger | + kAudioFormatFlagsNativeEndian | + kAudioFormatFlagIsPacked, + .mBitsPerChannel = 16, + .mChannelsPerFrame = 2, + .mBytesPerFrame = 4, + .mFramesPerPacket = 1, + .mBytesPerPacket = 4, + }, + .mSampleRateRange = + { + .mMinimum = r, + .mMaximum = r, + }, + }; + formats.push_back(fmt); + } + + EXPECT_EQ(kAudioHardwareNoError, stream->SetAvailablePhysicalFormatsAsync(formats)); +} + +void SetupAvailVirtualRates(std::shared_ptr stream, + std::vector rates) +{ + std::vector formats; + + for (auto r : rates) { + AudioStreamRangedDescription fmt = { + .mFormat = + { + .mSampleRate = r, + .mFormatID = kAudioFormatLinearPCM, + .mFormatFlags = kAudioFormatFlagIsSignedInteger | + kAudioFormatFlagsNativeEndian | + kAudioFormatFlagIsPacked, + .mBitsPerChannel = 16, + .mChannelsPerFrame = 2, + .mBytesPerFrame = 4, + .mFramesPerPacket = 1, + .mBytesPerPacket = 4, + }, + .mSampleRateRange = + { + .mMinimum = r, + .mMaximum = r, + }, + }; + formats.push_back(fmt); } + + EXPECT_EQ(kAudioHardwareNoError, stream->SetAvailableVirtualFormatsAsync(formats)); +} + +void ExpectNominalRate(Float64 rate, std::shared_ptr device) +{ + EXPECT_EQ(rate, device->GetNominalSampleRate()); +} + +void ExpectPhysicalRate(Float64 rate, std::shared_ptr stream) +{ + EXPECT_EQ(rate, stream->GetPhysicalFormat().mSampleRate); + EXPECT_EQ(rate, stream->GetPhysicalFormat().mSampleRate); + + EXPECT_EQ(rate, stream->GetSampleRate()); + EXPECT_EQ(rate, stream->GetSampleRate()); +} + +void ExpectVirtualRate(Float64 rate, std::shared_ptr stream) +{ + EXPECT_EQ(rate, stream->GetVirtualFormat().mSampleRate); + EXPECT_EQ(rate, stream->GetVirtualFormat().mSampleRate); } } // anonymous namespace @@ -26,83 +117,189 @@ struct OperationsTest : ::testing::Test std::shared_ptr context = std::make_shared(tracer); }; -TEST_F(OperationsTest, StreamFormat) +TEST_F(OperationsTest, DeviceSampleRate) { - const auto device = std::make_shared(context); + { // supported + const auto device = std::make_shared(context); - const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); - const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); - ASSERT_TRUE(stream1); - ASSERT_TRUE(stream2); + SetupAvailNominalRates(device, {44100, 48000}); + SetupAvailPhysicalRates(stream1, {44100, 48000}); + SetupAvailVirtualRates(stream1, {44100, 48000}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); - { - // SetAvailableSampleRatesAsync - auto rate_list = device->GetAvailableSampleRates(); - ASSERT_EQ(1, rate_list.size()); - ASSERT_EQ(44100, rate_list[0].mMinimum); - ASSERT_EQ(44100, rate_list[0].mMaximum); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); - { - auto rate48 = rate_list[0]; + EXPECT_EQ(kAudioHardwareNoError, device->SetNominalSampleRateAsync(48000)); - rate48.mMinimum = 48000; - rate48.mMaximum = 48000; + ExpectNominalRate(48000, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); + } + { // unsupported + const auto device = std::make_shared(context); - rate_list.push_back(rate48); - } + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + + SetupAvailNominalRates(device, {44100}); + SetupAvailPhysicalRates(stream1, {44100, 48000}); + SetupAvailVirtualRates(stream1, {44100, 48000}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); + + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); + + EXPECT_EQ(kAudioHardwareUnsupportedOperationError, + device->SetNominalSampleRateAsync(48000)); - EXPECT_EQ(kAudioHardwareNoError, device->SetAvailableSampleRatesAsync(rate_list)); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); } +} - { - // SetAvailablePhysicalFormatsAsync - auto format_list = stream1->GetAvailablePhysicalFormats(); - ASSERT_EQ(1, format_list.size()); - ASSERT_EQ(44100, format_list[0].mFormat.mSampleRate); +TEST_F(OperationsTest, StreamPhysicalFormat) +{ + { // supported + const auto device = std::make_shared(context); - { - auto format48 = format_list[0]; + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); - format48.mFormat.mSampleRate = 48000; - format48.mSampleRateRange.mMinimum = 48000; - format48.mSampleRateRange.mMaximum = 48000; + SetupAvailNominalRates(device, {44100, 48000}); + SetupAvailPhysicalRates(stream1, {44100, 48000}); + SetupAvailVirtualRates(stream1, {44100, 48000}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); - format_list.push_back(format48); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); + + { + auto format = stream1->GetPhysicalFormat(); + format.mSampleRate = 48000; + EXPECT_EQ(kAudioHardwareNoError, stream1->SetPhysicalFormatAsync(format)); } - EXPECT_EQ(kAudioHardwareNoError, - stream1->SetAvailablePhysicalFormatsAsync(format_list)); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(48000, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); + } + { // unsupported + const auto device = std::make_shared(context); + + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + + SetupAvailNominalRates(device, {44100, 48000}); + SetupAvailPhysicalRates(stream1, {44100}); + SetupAvailVirtualRates(stream1, {44100, 48000}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); - EXPECT_EQ(kAudioHardwareNoError, - stream2->SetAvailablePhysicalFormatsAsync(format_list)); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); - ExpectVectorsEq(format_list, stream1->GetAvailablePhysicalFormats()); - ExpectVectorsEq(format_list, stream2->GetAvailablePhysicalFormats()); + { + auto format = stream1->GetPhysicalFormat(); + format.mSampleRate = 48000; + EXPECT_EQ(kAudioDeviceUnsupportedFormatError, + stream1->SetPhysicalFormatAsync(format)); + } + + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); } +} + +TEST_F(OperationsTest, StreamVirtualFormat) +{ + { // supported + const auto device = std::make_shared(context); + + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); - { - // SetPhysicalFormatAsync - auto format1 = stream1->GetPhysicalFormat(); - auto format2 = stream2->GetPhysicalFormat(); + SetupAvailNominalRates(device, {44100, 48000}); + SetupAvailPhysicalRates(stream1, {44100, 48000}); + SetupAvailVirtualRates(stream1, {44100, 48000}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); - ASSERT_EQ(44100, format1.mSampleRate); - ASSERT_EQ(44100, format2.mSampleRate); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); - ASSERT_EQ(44100, stream1->GetSampleRate()); - ASSERT_EQ(44100, stream2->GetSampleRate()); + { + auto format = stream1->GetVirtualFormat(); + format.mSampleRate = 48000; + EXPECT_EQ(kAudioHardwareNoError, stream1->SetVirtualFormatAsync(format)); + } + + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(48000, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); + } + { // unsupported + const auto device = std::make_shared(context); - ASSERT_EQ(44100, device->GetSampleRate()); + const auto stream1 = device->AddStreamWithControlsAsync(aspl::Direction::Output); + const auto stream2 = device->AddStreamWithControlsAsync(aspl::Direction::Output); - format1.mSampleRate = 48000; - EXPECT_EQ(kAudioHardwareNoError, stream1->SetPhysicalFormatAsync(format1)); + SetupAvailNominalRates(device, {44100, 48000}); + SetupAvailPhysicalRates(stream1, {44100, 48000}); + SetupAvailVirtualRates(stream1, {44100}); + SetupAvailPhysicalRates(stream2, {44100, 48000}); + SetupAvailVirtualRates(stream2, {44100, 48000}); - ASSERT_EQ(48000, stream1->GetPhysicalFormat().mSampleRate); - ASSERT_EQ(48000, stream2->GetPhysicalFormat().mSampleRate); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); - ASSERT_EQ(48000, stream1->GetSampleRate()); - ASSERT_EQ(48000, stream2->GetSampleRate()); + { + auto format = stream1->GetVirtualFormat(); + format.mSampleRate = 48000; + EXPECT_EQ(kAudioDeviceUnsupportedFormatError, + stream1->SetVirtualFormatAsync(format)); + } - ASSERT_EQ(48000, device->GetSampleRate()); + ExpectNominalRate(44100, device); + ExpectPhysicalRate(44100, stream1); + ExpectVirtualRate(44100, stream1); + ExpectPhysicalRate(44100, stream2); + ExpectVirtualRate(44100, stream2); } } diff --git a/test/TestProperties.cpp b/test/TestProperties.cpp index 9c53464..47021aa 100644 --- a/test/TestProperties.cpp +++ b/test/TestProperties.cpp @@ -809,13 +809,13 @@ TEST_F(PropertiesTest, Device) ExpectUInt32Property( TestClockDomain, device->GetID(), kAudioDevicePropertyClockDomain); - // SampleRate - EXPECT_EQ(TestSampleRate, device->GetSampleRate()); + // NominalSampleRate + EXPECT_EQ(TestSampleRate, device->GetNominalSampleRate()); ExpectFloat64Property( TestSampleRate, device->GetID(), kAudioDevicePropertyNominalSampleRate); EXPECT_EQ(kAudioHardwareUnsupportedOperationError, - device->SetSampleRateAsync(TestSampleRate / 2)); + device->SetNominalSampleRateAsync(TestSampleRate / 2)); WriteFloat64Property(TestSampleRate / 2, kAudioHardwareUnsupportedOperationError, device->GetID(),