From e76defbfd4dc054dc164bb3127ba9fedbe578a2a Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 23 Feb 2025 19:58:56 -0800 Subject: [PATCH] Bug Fix: Greatly improve seeking operations Seeking now mutes properly, and will not leave the audio muted across other operations. Audio output changes should also mute and destroy the buffers of the input chain, so that the audio resets properly. Signed-off-by: Christopher Snowhill --- Audio/AudioPlayer.h | 4 ++++ Audio/AudioPlayer.m | 23 ++++++++++++++++++++++- Audio/Chain/BufferChain.m | 5 +---- Audio/Chain/InputNode.h | 4 ---- Audio/Chain/InputNode.m | 13 ++++++------- Audio/Chain/OutputNode.h | 3 +++ Audio/Chain/OutputNode.m | 16 +++++++++++----- Audio/Output/OutputCoreAudio.h | 4 ++++ Audio/Output/OutputCoreAudio.m | 17 +++++++++++++++++ 9 files changed, 68 insertions(+), 21 deletions(-) diff --git a/Audio/AudioPlayer.h b/Audio/AudioPlayer.h index db22efcc8..20c41024a 100644 --- a/Audio/AudioPlayer.h +++ b/Audio/AudioPlayer.h @@ -25,6 +25,7 @@ BufferChain *bufferChain; OutputNode *output; + BOOL muted; double volume; double pitch; double tempo; @@ -76,6 +77,9 @@ - (double)volumeUp:(double)amount; - (double)volumeDown:(double)amount; +- (void)mute; +- (void)unmute; + - (double)amountPlayed; - (double)amountPlayedInterval; diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index 20a1e3fe4..dd2d8c521 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -78,7 +78,9 @@ output = [[OutputNode alloc] initWithController:self previous:nil]; } [output setup]; - [output setVolume:volume]; + if(muted) { + [output mute]; + } @synchronized(chainQueue) { for(id anObject in chainQueue) { [anObject setShouldContinue:NO]; @@ -177,6 +179,10 @@ [output resume]; [self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES]; + + if(muted) { + [self unmute]; + } } - (void)seekToTimeBG:(NSNumber *)time { @@ -184,6 +190,7 @@ } - (void)seekToTime:(double)time { + [self mute]; if(endOfInputReached) { // This is a dirty hack in case the playback has finished with the track // that the user thinks they're seeking into @@ -218,6 +225,20 @@ return volume; } +- (void)mute { + if(!muted) { + [output mute]; + muted = YES; + } +} + +- (void)unmute { + if(muted) { + [output unmute]; + muted = NO; + } +} + // This is called by the delegate DURING a requestNextStream request. - (void)setNextStream:(NSURL *)url { [self setNextStream:url withUserInfo:nil withRGInfo:nil]; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index 3097332b8..9aab848c3 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -230,7 +230,7 @@ AudioPlayer * audioPlayer = controller; OutputNode *outputNode = [audioPlayer output]; - [inputNode setLastVolume:[outputNode volume]]; + [audioPlayer mute]; [inputNode seek:frame]; } @@ -371,9 +371,6 @@ [outputNode setVolume:v]; } } - if(inputNode) { - [inputNode setLastVolume:v]; - } } @end diff --git a/Audio/Chain/InputNode.h b/Audio/Chain/InputNode.h index e7f68afb6..d5cb999d4 100644 --- a/Audio/Chain/InputNode.h +++ b/Audio/Chain/InputNode.h @@ -29,8 +29,6 @@ BOOL shouldSeek; long seekFrame; - double lastVolume; - BOOL observersAdded; Semaphore *exitAtTheEndOfTheStream; @@ -53,6 +51,4 @@ - (id_Nonnull)decoder; -- (void)setLastVolume:(double)v; - @end diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index 2f91ed71f..769eeae8e 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -29,7 +29,6 @@ static void *kInputNodeContext = &kInputNodeContext; if(self) { exitAtTheEndOfTheStream = [[Semaphore alloc] init]; threadExited = NO; - lastVolume = 1.0; } return self; @@ -161,7 +160,10 @@ static void *kInputNodeContext = &kInputNodeContext; while([self shouldContinue] == YES && [self endOfStream] == NO) { if(shouldSeek == YES) { BufferChain *bufferChain = controller; - [bufferChain setVolume:0.0]; + AudioPlayer *audioPlayer = [controller controller]; + [audioPlayer mute]; + OutputNode *outputNode = [audioPlayer output]; + [outputNode resetBuffer]; ConverterNode *converter = [bufferChain converter]; DSPRubberbandNode *rubberband = [bufferChain rubberband]; @@ -198,7 +200,7 @@ static void *kInputNodeContext = &kInputNodeContext; [controller setError:YES]; } - [bufferChain setVolume:lastVolume]; + [audioPlayer unmute]; } AudioChunk *chunk; @@ -251,6 +253,7 @@ static void *kInputNodeContext = &kInputNodeContext; seekFrame = frame; shouldSeek = YES; DLog(@"Should seek!"); + [self resetBuffer]; [writeSemaphore signal]; if(endOfStream) { @@ -300,8 +303,4 @@ static void *kInputNodeContext = &kInputNodeContext; return [buffer listDuration]; } -- (void)setLastVolume:(double)v { - lastVolume = v; -} - @end diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index 7c6a38877..a2a603667 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -62,6 +62,9 @@ - (double)volume; - (void)setVolume:(double)v; +- (void)mute; +- (void)unmute; + - (void)setShouldContinue:(BOOL)s; - (void)setShouldPlayOutBuffer:(BOOL)s; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index d8e85b779..1fa0fac8c 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -50,6 +50,14 @@ [output resume]; } +- (void)mute { + [output mute]; +} + +- (void)unmute { + [output unmute]; +} + - (void)incrementAmountPlayed:(double)seconds { amountPlayed += seconds; amountPlayedInterval += seconds; @@ -156,7 +164,8 @@ config = channelConfig; // Calculate a ratio and add to double(seconds) instead, as format may change // double oldSampleRatio = sampleRatio; - BufferChain *bufferChain = [controller bufferChain]; + AudioPlayer *audioPlayer = controller; + BufferChain *bufferChain = [audioPlayer bufferChain]; if(bufferChain) { ConverterNode *converter = [bufferChain converter]; DSPDownmixNode *downmix = [bufferChain downmix]; @@ -180,11 +189,8 @@ } } if(formatChanged) { + [audioPlayer mute]; InputNode *inputNode = [bufferChain inputNode]; - if(inputNode) { - [inputNode setLastVolume:[output volume]]; - [output setVolume:0.0]; - } if(converter) { [converter setOutputFormat:format]; } diff --git a/Audio/Output/OutputCoreAudio.h b/Audio/Output/OutputCoreAudio.h index c832fa9cc..2a833532f 100644 --- a/Audio/Output/OutputCoreAudio.h +++ b/Audio/Output/OutputCoreAudio.h @@ -105,6 +105,7 @@ using std::atomic_long; float tempBuffer[512 * 32]; float inputBuffer[4096 * 32]; // 4096 samples times maximum supported channel count + BOOL muted; #ifdef OUTPUT_LOG FILE *_logFile; #endif @@ -125,6 +126,9 @@ using std::atomic_long; - (double)volume; - (void)setVolume:(double)v; +- (void)mute; +- (void)unmute; + - (void)setShouldPlayOutBuffer:(BOOL)enabled; - (void)sustainHDCD; diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 0be277f59..80a30c4fc 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -618,6 +618,12 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons @autoreleasepool { while(renderedSamples < frameCount) { + if(_self->muted) { + inputData->mBuffers[0].mDataByteSize = frameCount * format->mBytesPerPacket; + inputData->mBuffers[0].mNumberChannels = channels; + bzero(inputData->mBuffers[0].mData, inputData->mBuffers[0].mDataByteSize); + return 0; + } int inputRemain = _self->inputRemain; while(!inputRemain) { inputRemain = [_self renderAndConvert]; @@ -740,6 +746,17 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons volume = v * 0.01f; } +- (void)mute { + if(!muted) { + muted = YES; + inputRemain = 0; + } +} + +- (void)unmute { + muted = NO; +} + - (double)latency { return 0.0; }