Audio Output: Take a more nuclear approach to output resetting when seeking
parent
1278b64afd
commit
d22ee14a36
|
@ -15,6 +15,8 @@
|
||||||
@interface BufferChain : NSObject {
|
@interface BufferChain : NSObject {
|
||||||
InputNode *inputNode;
|
InputNode *inputNode;
|
||||||
ConverterNode *converterNode;
|
ConverterNode *converterNode;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription _inputFormat;
|
||||||
|
|
||||||
NSURL *streamURL;
|
NSURL *streamURL;
|
||||||
id userInfo;
|
id userInfo;
|
||||||
|
@ -63,4 +65,7 @@
|
||||||
|
|
||||||
- (id)controller;
|
- (id)controller;
|
||||||
|
|
||||||
|
- (ConverterNode *)converter;
|
||||||
|
- (AudioStreamBasicDescription)inputFormat;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
if (![inputNode openWithSource:source])
|
if (![inputNode openWithSource:source])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (![converterNode setupWithInputFormat:propertiesToASBD([inputNode properties]) outputFormat:outputFormat])
|
if (![converterNode setupWithInputFormat:(_inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self setRGInfo:rgi];
|
[self setRGInfo:rgi];
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
DLog(@"Input Properties: %@", [inputNode properties]);
|
DLog(@"Input Properties: %@", [inputNode properties]);
|
||||||
if (![converterNode setupWithInputFormat:propertiesToASBD([inputNode properties]) outputFormat:outputFormat])
|
if (![converterNode setupWithInputFormat:(_inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self setRGInfo:rgi];
|
[self setRGInfo:rgi];
|
||||||
|
@ -199,4 +199,14 @@
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (ConverterNode *)converter
|
||||||
|
{
|
||||||
|
return converterNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (AudioStreamBasicDescription)inputFormat
|
||||||
|
{
|
||||||
|
return _inputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
void *callbackBuffer;
|
void *callbackBuffer;
|
||||||
size_t callbackBufferSize;
|
size_t callbackBufferSize;
|
||||||
|
|
||||||
|
BOOL stopping;
|
||||||
|
BOOL convertEntered;
|
||||||
|
BOOL ACInputEntered;
|
||||||
|
BOOL ACFloatEntered;
|
||||||
|
|
||||||
float sampleRatio;
|
float sampleRatio;
|
||||||
|
|
||||||
float volumeScale;
|
float volumeScale;
|
||||||
|
|
|
@ -43,6 +43,11 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
||||||
floatBufferSize = 0;
|
floatBufferSize = 0;
|
||||||
callbackBuffer = NULL;
|
callbackBuffer = NULL;
|
||||||
callbackBufferSize = 0;
|
callbackBufferSize = 0;
|
||||||
|
|
||||||
|
stopping = NO;
|
||||||
|
convertEntered = NO;
|
||||||
|
ACInputEntered = NO;
|
||||||
|
ACFloatEntered = NO;
|
||||||
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
||||||
}
|
}
|
||||||
|
@ -169,14 +174,16 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter,
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
int amountToWrite;
|
int amountToWrite;
|
||||||
int amountRead;
|
int amountRead;
|
||||||
|
|
||||||
if ([converter shouldContinue] == NO || [converter endOfStream] == YES)
|
if (converter->stopping || [converter shouldContinue] == NO || [converter endOfStream] == YES)
|
||||||
{
|
{
|
||||||
ioData->mBuffers[0].mDataByteSize = 0;
|
ioData->mBuffers[0].mDataByteSize = 0;
|
||||||
*ioNumberDataPackets = 0;
|
*ioNumberDataPackets = 0;
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
converter->ACInputEntered = YES;
|
||||||
|
|
||||||
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
|
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
|
||||||
|
|
||||||
|
@ -188,6 +195,8 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter,
|
||||||
{
|
{
|
||||||
ioData->mBuffers[0].mDataByteSize = 0;
|
ioData->mBuffers[0].mDataByteSize = 0;
|
||||||
*ioNumberDataPackets = 0;
|
*ioNumberDataPackets = 0;
|
||||||
|
|
||||||
|
converter->ACInputEntered = NO;
|
||||||
|
|
||||||
return 100; //Keep asking for data
|
return 100; //Keep asking for data
|
||||||
}
|
}
|
||||||
|
@ -196,7 +205,9 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter,
|
||||||
ioData->mBuffers[0].mDataByteSize = amountRead;
|
ioData->mBuffers[0].mDataByteSize = amountRead;
|
||||||
ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame);
|
ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame);
|
||||||
ioData->mNumberBuffers = 1;
|
ioData->mNumberBuffers = 1;
|
||||||
|
|
||||||
|
converter->ACInputEntered = NO;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,13 +221,15 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter,
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
int amountToWrite;
|
int amountToWrite;
|
||||||
|
|
||||||
if ([converter shouldContinue] == NO)
|
if (converter->stopping || [converter shouldContinue] == NO)
|
||||||
{
|
{
|
||||||
ioData->mBuffers[0].mDataByteSize = 0;
|
ioData->mBuffers[0].mDataByteSize = 0;
|
||||||
*ioNumberDataPackets = 0;
|
*ioNumberDataPackets = 0;
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
converter->ACFloatEntered = YES;
|
||||||
|
|
||||||
amountToWrite = (*ioNumberDataPackets) * (converter->dmFloatFormat.mBytesPerPacket);
|
amountToWrite = (*ioNumberDataPackets) * (converter->dmFloatFormat.mBytesPerPacket);
|
||||||
|
|
||||||
|
@ -232,10 +245,15 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter,
|
||||||
ioData->mNumberBuffers = 1;
|
ioData->mNumberBuffers = 1;
|
||||||
|
|
||||||
if (amountToWrite == 0)
|
if (amountToWrite == 0)
|
||||||
|
{
|
||||||
|
converter->ACFloatEntered = NO;
|
||||||
return 100;
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
converter->floatOffset += amountToWrite;
|
converter->floatOffset += amountToWrite;
|
||||||
|
|
||||||
|
converter->ACFloatEntered = NO;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,9 +276,15 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter,
|
||||||
int amountReadFromFC;
|
int amountReadFromFC;
|
||||||
int amountRead = 0;
|
int amountRead = 0;
|
||||||
|
|
||||||
|
if (stopping)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
convertEntered = YES;
|
||||||
|
|
||||||
tryagain2:
|
tryagain2:
|
||||||
if ([self shouldContinue] == NO || [self endOfStream] == YES)
|
if (stopping || [self shouldContinue] == NO || [self endOfStream] == YES)
|
||||||
{
|
{
|
||||||
|
convertEntered = NO;
|
||||||
return amountRead;
|
return amountRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +311,12 @@ tryagain2:
|
||||||
ioData.mNumberBuffers = 1;
|
ioData.mNumberBuffers = 1;
|
||||||
|
|
||||||
tryagain:
|
tryagain:
|
||||||
|
if (stopping)
|
||||||
|
{
|
||||||
|
convertEntered = NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, (__bridge void * _Nullable)(self), &ioNumberPackets, &ioData, NULL);
|
err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, (__bridge void * _Nullable)(self), &ioNumberPackets, &ioData, NULL);
|
||||||
amountReadFromFC += ioNumberPackets * floatFormat.mBytesPerPacket;
|
amountReadFromFC += ioNumberPackets * floatFormat.mBytesPerPacket;
|
||||||
if (err == 100)
|
if (err == 100)
|
||||||
|
@ -301,6 +331,7 @@ tryagain2:
|
||||||
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
||||||
{
|
{
|
||||||
DLog(@"Error: %i", err);
|
DLog(@"Error: %i", err);
|
||||||
|
convertEntered = NO;
|
||||||
return amountRead;
|
return amountRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +366,12 @@ tryagain2:
|
||||||
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
||||||
ioData.mNumberBuffers = 1;
|
ioData.mNumberBuffers = 1;
|
||||||
|
|
||||||
|
if (stopping)
|
||||||
|
{
|
||||||
|
convertEntered = NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
err = AudioConverterFillComplexBuffer(converter, ACFloatProc, (__bridge void *)(self), &ioNumberPackets, &ioData, NULL);
|
err = AudioConverterFillComplexBuffer(converter, ACFloatProc, (__bridge void *)(self), &ioNumberPackets, &ioData, NULL);
|
||||||
amountRead += ioNumberPackets * outputFormat.mBytesPerPacket;
|
amountRead += ioNumberPackets * outputFormat.mBytesPerPacket;
|
||||||
if (err == 100)
|
if (err == 100)
|
||||||
|
@ -345,7 +382,8 @@ tryagain2:
|
||||||
{
|
{
|
||||||
DLog(@"Error: %i", err);
|
DLog(@"Error: %i", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertEntered = NO;
|
||||||
return amountRead;
|
return amountRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,6 +452,11 @@ static float db_to_scale(float db)
|
||||||
{
|
{
|
||||||
//Make the converter
|
//Make the converter
|
||||||
OSStatus stat = noErr;
|
OSStatus stat = noErr;
|
||||||
|
|
||||||
|
stopping = NO;
|
||||||
|
convertEntered = NO;
|
||||||
|
ACInputEntered = NO;
|
||||||
|
ACFloatEntered = NO;
|
||||||
|
|
||||||
inputFormat = inf;
|
inputFormat = inf;
|
||||||
outputFormat = outf;
|
outputFormat = outf;
|
||||||
|
@ -514,6 +557,11 @@ static float db_to_scale(float db)
|
||||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format
|
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format
|
||||||
{
|
{
|
||||||
DLog(@"FORMAT CHANGED");
|
DLog(@"FORMAT CHANGED");
|
||||||
|
stopping = YES;
|
||||||
|
while (convertEntered || ACInputEntered || ACFloatEntered)
|
||||||
|
{
|
||||||
|
usleep(500);
|
||||||
|
}
|
||||||
[self cleanUp];
|
[self cleanUp];
|
||||||
[self setupWithInputFormat:format outputFormat:outputFormat];
|
[self setupWithInputFormat:format outputFormat:outputFormat];
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,17 +122,21 @@
|
||||||
if (shouldSeek == YES)
|
if (shouldSeek == YES)
|
||||||
{
|
{
|
||||||
OutputNode *output = [[controller controller] output];
|
OutputNode *output = [[controller controller] output];
|
||||||
BOOL isPaused = [output isPaused];
|
ConverterNode *converter = [[[controller controller] bufferChain] converter];
|
||||||
if ( !isPaused ) [output pause];
|
DLog(@"SEEKING! Resetting Buffer");
|
||||||
DLog(@"SEEKING!");
|
|
||||||
seekError = [decoder seek:seekFrame] < 0;
|
[self resetBuffer];
|
||||||
if ( !isPaused ) [output resumeWithFade];
|
[output reset];
|
||||||
|
[converter resetBuffer];
|
||||||
|
[converter inputFormatDidChange:[[[controller controller] bufferChain] inputFormat]];
|
||||||
|
|
||||||
|
DLog(@"Reset buffer!");
|
||||||
|
|
||||||
|
DLog(@"SEEKING!");
|
||||||
|
seekError = [decoder seek:seekFrame] < 0;
|
||||||
|
|
||||||
shouldSeek = NO;
|
shouldSeek = NO;
|
||||||
DLog(@"Seeked! Resetting Buffer");
|
DLog(@"Seeked! Resetting Buffer");
|
||||||
|
|
||||||
[self resetBuffer];
|
|
||||||
|
|
||||||
DLog(@"Reset buffer!");
|
|
||||||
initialBufferFilled = NO;
|
initialBufferFilled = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
- (void)process;
|
- (void)process;
|
||||||
- (void)close;
|
- (void)close;
|
||||||
- (void)seek:(double)time;
|
- (void)seek:(double)time;
|
||||||
|
- (void)reset;
|
||||||
|
|
||||||
- (int)readData:(void *)ptr amount:(int)amount;
|
- (int)readData:(void *)ptr amount:(int)amount;
|
||||||
|
|
||||||
|
@ -42,7 +43,6 @@
|
||||||
|
|
||||||
- (void)pause;
|
- (void)pause;
|
||||||
- (void)resume;
|
- (void)resume;
|
||||||
- (void)resumeWithFade;
|
|
||||||
|
|
||||||
- (BOOL)isPaused;
|
- (BOOL)isPaused;
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,11 @@
|
||||||
[output resume];
|
[output resume];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resumeWithFade
|
- (void)reset
|
||||||
{
|
{
|
||||||
paused = NO;
|
[output setup];
|
||||||
[output resumeWithFade];
|
if (!paused)
|
||||||
|
[output start];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)readData:(void *)ptr amount:(int)amount
|
- (int)readData:(void *)ptr amount:(int)amount
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
- (void)pause;
|
- (void)pause;
|
||||||
- (void)resume;
|
- (void)resume;
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
- (void)resumeWithFade;
|
|
||||||
|
|
||||||
- (void)setVolume:(double) v;
|
- (void)setVolume:(double) v;
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,6 @@ static void Sound_Renderer(void *userData, AudioQueueRef queue, AudioQueueBuffer
|
||||||
|
|
||||||
if (output->stopping == YES)
|
if (output->stopping == YES)
|
||||||
{
|
{
|
||||||
// *shrug* At least this will stop it from trying to emit data post-shutdown
|
|
||||||
memset(readPointer, 0, amountToRead);
|
|
||||||
buffer->mAudioDataByteSize = amountToRead;
|
|
||||||
AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +256,8 @@ static void Sound_Renderer(void *userData, AudioQueueRef queue, AudioQueueBuffer
|
||||||
{
|
{
|
||||||
if (outputUnit || audioQueue)
|
if (outputUnit || audioQueue)
|
||||||
[self stop];
|
[self stop];
|
||||||
|
|
||||||
|
stopping = NO;
|
||||||
|
|
||||||
AudioComponentDescription desc;
|
AudioComponentDescription desc;
|
||||||
OSStatus err;
|
OSStatus err;
|
||||||
|
@ -375,6 +373,8 @@ static void Sound_Renderer(void *userData, AudioQueueRef queue, AudioQueueBuffer
|
||||||
|
|
||||||
- (void)start
|
- (void)start
|
||||||
{
|
{
|
||||||
|
AudioQueueSetParameter(audioQueue, kAudioQueueParam_VolumeRampTime, 0);
|
||||||
|
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, volume);
|
||||||
AudioQueueStart(audioQueue, NULL);
|
AudioQueueStart(audioQueue, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,6 +389,9 @@ static void Sound_Renderer(void *userData, AudioQueueRef queue, AudioQueueBuffer
|
||||||
}
|
}
|
||||||
if (audioQueue && buffers)
|
if (audioQueue && buffers)
|
||||||
{
|
{
|
||||||
|
AudioQueuePause(audioQueue);
|
||||||
|
AudioQueueStop(audioQueue, true);
|
||||||
|
|
||||||
for (UInt32 i = 0; i < numberOfBuffers; ++i)
|
for (UInt32 i = 0; i < numberOfBuffers; ++i)
|
||||||
{
|
{
|
||||||
if (buffers[i])
|
if (buffers[i])
|
||||||
|
@ -414,23 +417,12 @@ static void Sound_Renderer(void *userData, AudioQueueRef queue, AudioQueueBuffer
|
||||||
|
|
||||||
- (void)pause
|
- (void)pause
|
||||||
{
|
{
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_VolumeRampTime, 0);
|
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 0);
|
|
||||||
AudioQueuePause(audioQueue);
|
AudioQueuePause(audioQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resume
|
- (void)resume
|
||||||
{
|
{
|
||||||
AudioQueueStart(audioQueue, NULL);
|
AudioQueueStart(audioQueue, NULL);
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_VolumeRampTime, 0);
|
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resumeWithFade
|
|
||||||
{
|
|
||||||
AudioQueueStart(audioQueue, NULL);
|
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_VolumeRampTime, 0.4);
|
|
||||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue