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 <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-02-23 19:58:56 -08:00
parent 7c169f9ef1
commit fa34d469df
9 changed files with 68 additions and 21 deletions

View file

@ -25,6 +25,7 @@
BufferChain *bufferChain; BufferChain *bufferChain;
OutputNode *output; OutputNode *output;
BOOL muted;
double volume; double volume;
double pitch; double pitch;
double tempo; double tempo;
@ -76,6 +77,9 @@
- (double)volumeUp:(double)amount; - (double)volumeUp:(double)amount;
- (double)volumeDown:(double)amount; - (double)volumeDown:(double)amount;
- (void)mute;
- (void)unmute;
- (double)amountPlayed; - (double)amountPlayed;
- (double)amountPlayedInterval; - (double)amountPlayedInterval;

View file

@ -78,7 +78,9 @@
output = [[OutputNode alloc] initWithController:self previous:nil]; output = [[OutputNode alloc] initWithController:self previous:nil];
} }
[output setup]; [output setup];
[output setVolume:volume]; if(muted) {
[output mute];
}
@synchronized(chainQueue) { @synchronized(chainQueue) {
for(id anObject in chainQueue) { for(id anObject in chainQueue) {
[anObject setShouldContinue:NO]; [anObject setShouldContinue:NO];
@ -177,6 +179,10 @@
[output resume]; [output resume];
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES]; [self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
if(muted) {
[self unmute];
}
} }
- (void)seekToTimeBG:(NSNumber *)time { - (void)seekToTimeBG:(NSNumber *)time {
@ -184,6 +190,7 @@
} }
- (void)seekToTime:(double)time { - (void)seekToTime:(double)time {
[self mute];
if(endOfInputReached) { if(endOfInputReached) {
// This is a dirty hack in case the playback has finished with the track // This is a dirty hack in case the playback has finished with the track
// that the user thinks they're seeking into // that the user thinks they're seeking into
@ -218,6 +225,20 @@
return volume; 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. // This is called by the delegate DURING a requestNextStream request.
- (void)setNextStream:(NSURL *)url { - (void)setNextStream:(NSURL *)url {
[self setNextStream:url withUserInfo:nil withRGInfo:nil]; [self setNextStream:url withUserInfo:nil withRGInfo:nil];

View file

@ -230,7 +230,7 @@
AudioPlayer * audioPlayer = controller; AudioPlayer * audioPlayer = controller;
OutputNode *outputNode = [audioPlayer output]; OutputNode *outputNode = [audioPlayer output];
[inputNode setLastVolume:[outputNode volume]]; [audioPlayer mute];
[inputNode seek:frame]; [inputNode seek:frame];
} }
@ -371,9 +371,6 @@
[outputNode setVolume:v]; [outputNode setVolume:v];
} }
} }
if(inputNode) {
[inputNode setLastVolume:v];
}
} }
@end @end

View file

@ -29,8 +29,6 @@
BOOL shouldSeek; BOOL shouldSeek;
long seekFrame; long seekFrame;
double lastVolume;
BOOL observersAdded; BOOL observersAdded;
Semaphore *exitAtTheEndOfTheStream; Semaphore *exitAtTheEndOfTheStream;
@ -53,6 +51,4 @@
- (id<CogDecoder>_Nonnull)decoder; - (id<CogDecoder>_Nonnull)decoder;
- (void)setLastVolume:(double)v;
@end @end

View file

@ -29,7 +29,6 @@ static void *kInputNodeContext = &kInputNodeContext;
if(self) { if(self) {
exitAtTheEndOfTheStream = [[Semaphore alloc] init]; exitAtTheEndOfTheStream = [[Semaphore alloc] init];
threadExited = NO; threadExited = NO;
lastVolume = 1.0;
} }
return self; return self;
@ -161,7 +160,10 @@ static void *kInputNodeContext = &kInputNodeContext;
while([self shouldContinue] == YES && [self endOfStream] == NO) { while([self shouldContinue] == YES && [self endOfStream] == NO) {
if(shouldSeek == YES) { if(shouldSeek == YES) {
BufferChain *bufferChain = controller; BufferChain *bufferChain = controller;
[bufferChain setVolume:0.0]; AudioPlayer *audioPlayer = [controller controller];
[audioPlayer mute];
OutputNode *outputNode = [audioPlayer output];
[outputNode resetBuffer];
ConverterNode *converter = [bufferChain converter]; ConverterNode *converter = [bufferChain converter];
DSPRubberbandNode *rubberband = [bufferChain rubberband]; DSPRubberbandNode *rubberband = [bufferChain rubberband];
@ -198,7 +200,7 @@ static void *kInputNodeContext = &kInputNodeContext;
[controller setError:YES]; [controller setError:YES];
} }
[bufferChain setVolume:lastVolume]; [audioPlayer unmute];
} }
AudioChunk *chunk; AudioChunk *chunk;
@ -251,6 +253,7 @@ static void *kInputNodeContext = &kInputNodeContext;
seekFrame = frame; seekFrame = frame;
shouldSeek = YES; shouldSeek = YES;
DLog(@"Should seek!"); DLog(@"Should seek!");
[self resetBuffer];
[writeSemaphore signal]; [writeSemaphore signal];
if(endOfStream) { if(endOfStream) {
@ -300,8 +303,4 @@ static void *kInputNodeContext = &kInputNodeContext;
return [buffer listDuration]; return [buffer listDuration];
} }
- (void)setLastVolume:(double)v {
lastVolume = v;
}
@end @end

View file

@ -62,6 +62,9 @@
- (double)volume; - (double)volume;
- (void)setVolume:(double)v; - (void)setVolume:(double)v;
- (void)mute;
- (void)unmute;
- (void)setShouldContinue:(BOOL)s; - (void)setShouldContinue:(BOOL)s;
- (void)setShouldPlayOutBuffer:(BOOL)s; - (void)setShouldPlayOutBuffer:(BOOL)s;

View file

@ -50,6 +50,14 @@
[output resume]; [output resume];
} }
- (void)mute {
[output mute];
}
- (void)unmute {
[output unmute];
}
- (void)incrementAmountPlayed:(double)seconds { - (void)incrementAmountPlayed:(double)seconds {
amountPlayed += seconds; amountPlayed += seconds;
amountPlayedInterval += seconds; amountPlayedInterval += seconds;
@ -156,7 +164,8 @@
config = channelConfig; config = channelConfig;
// Calculate a ratio and add to double(seconds) instead, as format may change // Calculate a ratio and add to double(seconds) instead, as format may change
// double oldSampleRatio = sampleRatio; // double oldSampleRatio = sampleRatio;
BufferChain *bufferChain = [controller bufferChain]; AudioPlayer *audioPlayer = controller;
BufferChain *bufferChain = [audioPlayer bufferChain];
if(bufferChain) { if(bufferChain) {
ConverterNode *converter = [bufferChain converter]; ConverterNode *converter = [bufferChain converter];
DSPDownmixNode *downmix = [bufferChain downmix]; DSPDownmixNode *downmix = [bufferChain downmix];
@ -180,11 +189,8 @@
} }
} }
if(formatChanged) { if(formatChanged) {
[audioPlayer mute];
InputNode *inputNode = [bufferChain inputNode]; InputNode *inputNode = [bufferChain inputNode];
if(inputNode) {
[inputNode setLastVolume:[output volume]];
[output setVolume:0.0];
}
if(converter) { if(converter) {
[converter setOutputFormat:format]; [converter setOutputFormat:format];
} }

View file

@ -105,6 +105,7 @@ using std::atomic_long;
float tempBuffer[512 * 32]; float tempBuffer[512 * 32];
float inputBuffer[4096 * 32]; // 4096 samples times maximum supported channel count float inputBuffer[4096 * 32]; // 4096 samples times maximum supported channel count
BOOL muted;
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
FILE *_logFile; FILE *_logFile;
#endif #endif
@ -125,6 +126,9 @@ using std::atomic_long;
- (double)volume; - (double)volume;
- (void)setVolume:(double)v; - (void)setVolume:(double)v;
- (void)mute;
- (void)unmute;
- (void)setShouldPlayOutBuffer:(BOOL)enabled; - (void)setShouldPlayOutBuffer:(BOOL)enabled;
- (void)sustainHDCD; - (void)sustainHDCD;

View file

@ -618,6 +618,12 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
@autoreleasepool { @autoreleasepool {
while(renderedSamples < frameCount) { 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; int inputRemain = _self->inputRemain;
while(!inputRemain) { while(!inputRemain) {
inputRemain = [_self renderAndConvert]; inputRemain = [_self renderAndConvert];
@ -740,6 +746,17 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
volume = v * 0.01f; volume = v * 0.01f;
} }
- (void)mute {
if(!muted) {
muted = YES;
inputRemain = 0;
}
}
- (void)unmute {
muted = NO;
}
- (double)latency { - (double)latency {
return 0.0; return 0.0;
} }