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 a7f1fd9d6c
commit e76defbfd4
9 changed files with 68 additions and 21 deletions

View file

@ -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;

View file

@ -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];

View file

@ -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

View file

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

View file

@ -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

View file

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

View file

@ -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];
}

View file

@ -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;

View file

@ -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;
}