Bug Fix: Ensure robust output format changes

Output format mostly requires stopping and restarting the output device,
and this also prevents us from using the latency function properly,
which apparently always returns 0 for output devices anyway. These
changes also prevent the output callback from hanging when resets occur.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-03-07 05:37:04 -08:00
parent a3385d1af9
commit 17b8647052
2 changed files with 29 additions and 12 deletions

View file

@ -55,6 +55,7 @@ using std::atomic_long;
BOOL paused;
BOOL restarted;
BOOL commandStop;
BOOL resetting;
BOOL eqEnabled;
BOOL eqInitialized;

View file

@ -450,6 +450,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
}
renderFormat = [[AVAudioFormat alloc] initWithStreamDescription:&deviceFormat channelLayout:[[AVAudioChannelLayout alloc] initWithLayoutTag:tag]];
resetting = YES;
[_au stopHardware];
[_au.inputBusses[0] setFormat:renderFormat error:&err];
if(err != nil)
return NO;
@ -459,6 +461,14 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
[outputLock lock];
[outputBuffer reset];
[outputLock unlock];
if(started) {
[_au startHardwareAndReturnError:&err];
if(err != nil)
return NO;
}
resetting = NO;
}
return YES;
@ -569,16 +579,26 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)refCon;
int renderedSamples = 0;
if(_self->resetting) {
inputData->mBuffers[0].mDataByteSize = frameCount * format->mBytesPerPacket;
bzero(inputData->mBuffers[0].mData, inputData->mBuffers[0].mDataByteSize);
inputData->mBuffers[0].mNumberChannels = channels;
return 0;
}
@autoreleasepool {
while(renderedSamples < frameCount) {
[refLock lock];
AudioChunk *chunk = [_self->outputBuffer removeSamples:frameCount - renderedSamples];
AudioChunk *chunk = nil;
if(![_self->outputBuffer isEmpty]) {
chunk = [_self->outputBuffer removeSamples:frameCount - renderedSamples];
}
[refLock unlock];
_self->streamTimestamp = [chunk streamTimestamp];
size_t _frameCount = [chunk frameCount];
if(_frameCount) {
if(chunk && [chunk frameCount]) {
_self->streamTimestamp = [chunk streamTimestamp];
size_t _frameCount = [chunk frameCount];
NSData *sampleData = [chunk removeSamples:_frameCount];
float *samplePtr = (float *)[sampleData bytes];
size_t inputTodo = MIN(_frameCount, frameCount - renderedSamples);
@ -586,7 +606,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
renderedSamples += inputTodo;
}
if(_self->stopping) {
if(_self->stopping || _self->resetting) {
break;
}
}
@ -708,11 +728,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
}
- (double)latency {
NSTimeInterval latency = 0.0;
if(_au) {
latency = [_au latency];
}
return [outputBuffer listDuration] + latency;
return [outputBuffer listDuration];
}
- (void)start {