Core Audio: Add a slight fading to operations
Add 10 millisecond fade to seeking, pausing and unpausing, and stopping on command. Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
ff5a1c6c2c
commit
46aac2fa91
5 changed files with 86 additions and 3 deletions
|
@ -75,6 +75,7 @@
|
||||||
|
|
||||||
[self waitUntilCallbacksExit];
|
[self waitUntilCallbacksExit];
|
||||||
if(output) {
|
if(output) {
|
||||||
|
[output fadeOut];
|
||||||
[output setShouldContinue:NO];
|
[output setShouldContinue:NO];
|
||||||
[output close];
|
[output close];
|
||||||
}
|
}
|
||||||
|
@ -83,6 +84,9 @@
|
||||||
}
|
}
|
||||||
[output setupWithInterval:resumeInterval];
|
[output setupWithInterval:resumeInterval];
|
||||||
[output setVolume:volume];
|
[output setVolume:volume];
|
||||||
|
if(resumeInterval) {
|
||||||
|
[output fadeIn];
|
||||||
|
}
|
||||||
@synchronized(chainQueue) {
|
@synchronized(chainQueue) {
|
||||||
for(id anObject in chainQueue) {
|
for(id anObject in chainQueue) {
|
||||||
[anObject setShouldContinue:NO];
|
[anObject setShouldContinue:NO];
|
||||||
|
@ -166,7 +170,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pause {
|
- (void)pause {
|
||||||
[output pause];
|
[output fadeOut];
|
||||||
|
|
||||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||||
}
|
}
|
||||||
|
@ -178,6 +182,7 @@
|
||||||
[self launchOutputThread];
|
[self launchOutputThread];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[output fadeIn];
|
||||||
[output resume];
|
[output resume];
|
||||||
|
|
||||||
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
||||||
|
|
|
@ -49,6 +49,9 @@
|
||||||
- (void)close;
|
- (void)close;
|
||||||
- (void)seek:(double)time;
|
- (void)seek:(double)time;
|
||||||
|
|
||||||
|
- (void)fadeOut;
|
||||||
|
- (void)fadeIn;
|
||||||
|
|
||||||
- (AudioChunk *)readChunk:(size_t)amount;
|
- (AudioChunk *)readChunk:(size_t)amount;
|
||||||
|
|
||||||
- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig;
|
- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig;
|
||||||
|
|
|
@ -99,6 +99,14 @@
|
||||||
[output resume];
|
[output resume];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)fadeOut {
|
||||||
|
[output fadeOut];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)fadeIn {
|
||||||
|
[output fadeIn];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)incrementAmountPlayed:(double)seconds {
|
- (void)incrementAmountPlayed:(double)seconds {
|
||||||
amountPlayed += seconds;
|
amountPlayed += seconds;
|
||||||
amountPlayedInterval += seconds;
|
amountPlayedInterval += seconds;
|
||||||
|
|
|
@ -57,6 +57,11 @@ using std::atomic_long;
|
||||||
BOOL commandStop;
|
BOOL commandStop;
|
||||||
BOOL resetting;
|
BOOL resetting;
|
||||||
|
|
||||||
|
BOOL fading, faded;
|
||||||
|
float fadeLevel;
|
||||||
|
float fadeStep;
|
||||||
|
float fadeTarget;
|
||||||
|
|
||||||
BOOL eqEnabled;
|
BOOL eqEnabled;
|
||||||
BOOL eqInitialized;
|
BOOL eqInitialized;
|
||||||
|
|
||||||
|
@ -110,6 +115,9 @@ using std::atomic_long;
|
||||||
- (void)resume;
|
- (void)resume;
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
|
|
||||||
|
- (void)fadeOut;
|
||||||
|
- (void)fadeIn;
|
||||||
|
|
||||||
- (double)latency;
|
- (double)latency;
|
||||||
|
|
||||||
- (double)volume;
|
- (double)volume;
|
||||||
|
|
|
@ -534,6 +534,29 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL fadeAudio(float * samples, size_t channels, size_t count, float * fadeLevel, float fadeStep, float fadeTarget) {
|
||||||
|
float _fadeLevel = *fadeLevel;
|
||||||
|
BOOL towardZero = fadeStep < 0.0;
|
||||||
|
BOOL stopping = NO;
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
for(size_t j = 0; j < channels; ++j) {
|
||||||
|
samples[j] *= _fadeLevel;
|
||||||
|
}
|
||||||
|
_fadeLevel += fadeStep;
|
||||||
|
if(towardZero && _fadeLevel <= fadeTarget) {
|
||||||
|
_fadeLevel = fadeTarget;
|
||||||
|
fadeStep = 0.0;
|
||||||
|
stopping = YES;
|
||||||
|
} else if(!towardZero && _fadeLevel >= fadeTarget) {
|
||||||
|
_fadeLevel = fadeTarget;
|
||||||
|
fadeStep = 0.0;
|
||||||
|
stopping = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*fadeLevel = _fadeLevel;
|
||||||
|
return stopping;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)renderAndConvert {
|
- (void)renderAndConvert {
|
||||||
if(resetStreamFormat) {
|
if(resetStreamFormat) {
|
||||||
[self updateStreamFormat];
|
[self updateStreamFormat];
|
||||||
|
@ -583,7 +606,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)refCon;
|
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)refCon;
|
||||||
int renderedSamples = 0;
|
int renderedSamples = 0;
|
||||||
|
|
||||||
if(_self->resetting) {
|
if(_self->resetting || _self->faded) {
|
||||||
inputData->mBuffers[0].mDataByteSize = frameCount * format->mBytesPerPacket;
|
inputData->mBuffers[0].mDataByteSize = frameCount * format->mBytesPerPacket;
|
||||||
bzero(inputData->mBuffers[0].mData, inputData->mBuffers[0].mDataByteSize);
|
bzero(inputData->mBuffers[0].mData, inputData->mBuffers[0].mDataByteSize);
|
||||||
inputData->mBuffers[0].mNumberChannels = channels;
|
inputData->mBuffers[0].mNumberChannels = channels;
|
||||||
|
@ -610,7 +633,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
renderedSamples += inputTodo;
|
renderedSamples += inputTodo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_self->stopping || _self->resetting) {
|
if(_self->stopping || _self->resetting || _self->faded) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,6 +653,17 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
|
|
||||||
scale_by_volume((float*)inputData->mBuffers[0].mData, renderedSamples * channels, volumeScale * _self->volume);
|
scale_by_volume((float*)inputData->mBuffers[0].mData, renderedSamples * channels, volumeScale * _self->volume);
|
||||||
|
|
||||||
|
if(_self->fading) {
|
||||||
|
BOOL faded = fadeAudio((float*)inputData->mBuffers[0].mData, channels, renderedSamples, &_self->fadeLevel, _self->fadeStep, _self->fadeTarget);
|
||||||
|
if(faded) {
|
||||||
|
if(_self->fadeStep < 0.0f) {
|
||||||
|
_self->faded = YES;
|
||||||
|
}
|
||||||
|
_self->fading = NO;
|
||||||
|
_self->fadeStep = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inputData->mBuffers[0].mDataByteSize = renderedSamples * format->mBytesPerPacket;
|
inputData->mBuffers[0].mDataByteSize = renderedSamples * format->mBytesPerPacket;
|
||||||
inputData->mBuffers[0].mNumberChannels = channels;
|
inputData->mBuffers[0].mNumberChannels = channels;
|
||||||
|
|
||||||
|
@ -667,6 +701,12 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
outputDeviceID = -1;
|
outputDeviceID = -1;
|
||||||
restarted = NO;
|
restarted = NO;
|
||||||
|
|
||||||
|
fadeTarget = 1.0f;
|
||||||
|
fadeLevel = 1.0f;
|
||||||
|
fadeStep = 0.0f;
|
||||||
|
fading = NO;
|
||||||
|
faded = NO;
|
||||||
|
|
||||||
AudioComponentDescription desc;
|
AudioComponentDescription desc;
|
||||||
NSError *err;
|
NSError *err;
|
||||||
|
|
||||||
|
@ -788,6 +828,11 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
compareVal = [outputController getTotalLatency];
|
compareVal = [outputController getTotalLatency];
|
||||||
usleep(5000);
|
usleep(5000);
|
||||||
} while(!commandStop && compareVal > 0 && compareMax-- > 0);
|
} while(!commandStop && compareVal > 0 && compareMax-- > 0);
|
||||||
|
} else {
|
||||||
|
[self fadeOut];
|
||||||
|
while(fading && !faded) {
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
[_au stopHardware];
|
[_au stopHardware];
|
||||||
_au = nil;
|
_au = nil;
|
||||||
|
@ -850,4 +895,18 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
||||||
return deviceChannelConfig;
|
return deviceChannelConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 10 milliseconds
|
||||||
|
- (void)fadeOut {
|
||||||
|
fadeTarget = 0.0f;
|
||||||
|
fadeStep = ((fadeTarget - fadeLevel) / deviceFormat.mSampleRate) * (1000.0f / 10.0f);
|
||||||
|
fading = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)fadeIn {
|
||||||
|
fadeTarget = 1.0;
|
||||||
|
fadeStep = ((fadeTarget - fadeLevel) / deviceFormat.mSampleRate) * (1000.0f / 10.0f);
|
||||||
|
fading = YES;
|
||||||
|
faded = NO;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in a new issue