From 59bcf4697561017e8e3a15e5ac3bf244c0bd1bf5 Mon Sep 17 00:00:00 2001 From: Single Date: Thu, 26 Dec 2019 18:48:22 +0800 Subject: [PATCH] SGAudioPlayer: Support for changing pitch. --- SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.h | 5 ++ SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.m | 60 ++++++++++++++----- .../Classes/Core/SGRenderer/SGAudioRenderer.h | 9 ++- .../Classes/Core/SGRenderer/SGAudioRenderer.m | 25 ++++++++ 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.h b/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.h index 458b9c88..e281ae88 100644 --- a/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.h +++ b/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.h @@ -43,6 +43,11 @@ */ @property (nonatomic) float rate; +/** + * Pitch. + */ +@property (nonatomic) float pitch; + /** * Volume. */ diff --git a/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.m b/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.m index a719d7f3..98f3aa13 100644 --- a/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.m +++ b/SGPlayer/Classes/Core/SGAudio/SGAudioPlayer.m @@ -21,6 +21,8 @@ @interface SGAudioPlayer () AudioUnit _timePitchUnit; } +@property (nonatomic, readonly) BOOL needsTimePitchNode; + @end @implementation SGAudioPlayer @@ -121,9 +123,18 @@ - (void)setup AUGraphSetNodeInputCallback(_graph, _mixerNode, 0, &inputCallbackStruct); AudioUnitAddRenderNotify(_outputUnit, outputCallback, (__bridge void *)self); - [self setRate:1]; - [self setVolume:1]; + AudioUnitParameterID mixerParam; +#if SGPLATFORM_TARGET_OS_MAC + mixerParam = kStereoMixerParam_Volume; +#elif SGPLATFORM_TARGET_OS_IPHONE_OR_TV + mixerParam = kMultiChannelMixerParam_Volume; +#endif + AudioUnitGetParameter(_mixerUnit, mixerParam, kAudioUnitScope_Input, 0, &_volume); + AudioUnitGetParameter(_timePitchUnit, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, &_rate); + AudioUnitGetParameter(_timePitchUnit, kNewTimePitchParam_Pitch, kAudioUnitScope_Global, 0, &_pitch); + [self setAsbd:asbd]; + [self reconnectTimePitchNodeForce:YES]; AUGraphInitialize(_graph); } @@ -154,6 +165,24 @@ - (void)disconnectNodeInput:(AUNode)sourceNode destNode:(AUNode)destNode } } +- (void)reconnectTimePitchNodeForce:(BOOL)force +{ + BOOL needsTimePitchNode = (_rate != 1.0) || (_pitch != 0.0); + if (_needsTimePitchNode != needsTimePitchNode || force) { + _needsTimePitchNode = needsTimePitchNode; + if (needsTimePitchNode) { + [self disconnectNodeInput:_mixerNode destNode:_outputNode]; + AUGraphConnectNodeInput(_graph, _mixerNode, 0, _timePitchNode, 0); + AUGraphConnectNodeInput(_graph, _timePitchNode, 0, _outputNode, 0); + } else { + [self disconnectNodeInput:_mixerNode destNode:_timePitchNode]; + [self disconnectNodeInput:_timePitchNode destNode:_outputNode]; + AUGraphConnectNodeInput(_graph, _mixerNode, 0, _outputNode, 0); + } + AUGraphUpdate(_graph, NULL); + } +} + #pragma mark - Interface - (void)play @@ -188,6 +217,9 @@ - (BOOL)isPlaying - (void)setVolume:(float)volume { + if (_volume == volume) { + return; + } AudioUnitParameterID param; #if SGPLATFORM_TARGET_OS_MAC param = kStereoMixerParam_Volume; @@ -205,19 +237,19 @@ - (void)setRate:(float)rate return; } if (AudioUnitSetParameter(_timePitchUnit, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, rate, 0) == noErr) { - if (_rate == 1.0 || rate == 1.0) { - if (rate == 1.0) { - [self disconnectNodeInput:_mixerNode destNode:_timePitchNode]; - [self disconnectNodeInput:_timePitchNode destNode:_outputNode]; - AUGraphConnectNodeInput(_graph, _mixerNode, 0, _outputNode, 0); - } else { - [self disconnectNodeInput:_mixerNode destNode:_outputNode]; - AUGraphConnectNodeInput(_graph, _mixerNode, 0, _timePitchNode, 0); - AUGraphConnectNodeInput(_graph, _timePitchNode, 0, _outputNode, 0); - } - AUGraphUpdate(_graph, NULL); - } _rate = rate; + [self reconnectTimePitchNodeForce:NO]; + } +} + +- (void)setPitch:(float)pitch +{ + if (_pitch == pitch) { + return; + } + if (AudioUnitSetParameter(_timePitchUnit, kNewTimePitchParam_Pitch, kAudioUnitScope_Global, 0, pitch, 0) == noErr) { + _pitch = pitch; + [self reconnectTimePitchNodeForce:NO]; } } diff --git a/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.h b/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.h index 2067b8f9..fb5c9acf 100644 --- a/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.h +++ b/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.h @@ -18,10 +18,17 @@ */ + (SGAudioDescriptor *)supportedAudioDescriptor; +/*! + @property pitch + @abstract + Indicates the current pitch. + */ +@property (nonatomic) Float64 pitch; + /*! @property volume @abstract - Indicates the current audio volume. + Indicates the current volume. */ @property (nonatomic) Float64 volume; diff --git a/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.m b/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.m index 52fd97fe..feb64d90 100644 --- a/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.m +++ b/SGPlayer/Classes/Core/SGRenderer/SGAudioRenderer.m @@ -37,6 +37,7 @@ @interface SGAudioRenderer () @implementation SGAudioRenderer @synthesize rate = _rate; +@synthesize pitch = _pitch; @synthesize volume = _volume; @synthesize delegate = _delegate; @synthesize descriptor = _descriptor; @@ -57,6 +58,7 @@ - (instancetype)initWithClock:(SGClock *)clock if (self = [super init]) { self->_clock = clock; self->_rate = 1.0; + self->_pitch = 0.0; self->_volume = 1.0; self->_lock = [[NSLock alloc] init]; self->_capacity = SGCapacityCreate(); @@ -123,6 +125,28 @@ - (Float64)rate return ret; } +- (void)setPitch:(Float64)pitch +{ + SGLockCondEXE11(self->_lock, ^BOOL { + return self->_pitch != pitch; + }, ^SGBlock { + self->_pitch = pitch; + return nil; + }, ^BOOL(SGBlock block) { + self->_player.pitch = pitch; + return YES; + }); +} + +- (Float64)pitch +{ + __block Float64 ret = 0.0f; + SGLockEXE00(self->_lock, ^{ + ret = self->_pitch; + }); + return ret; +} + - (void)setVolume:(Float64)volume { SGLockCondEXE11(self->_lock, ^BOOL { @@ -164,6 +188,7 @@ - (BOOL)open self->_player = [[SGAudioPlayer alloc] init]; self->_player.delegate = self; self->_player.rate = self->_rate; + self->_player.pitch = self->_pitch; self->_player.volume = self->_volume; return [self setState:SGRenderableStatePaused]; }, nil);