diff --git a/Audio/Chain/AudioChunk.m b/Audio/Chain/AudioChunk.m index 936584d6b..0c8702201 100644 --- a/Audio/Chain/AudioChunk.m +++ b/Audio/Chain/AudioChunk.m @@ -197,7 +197,7 @@ static const uint32_t AudioChannelConfigTable[] = { } - (double)duration { - if(formatAssigned) { + if(formatAssigned && [chunkData length]) { const size_t bytesPerPacket = format.mBytesPerPacket; const double sampleRate = format.mSampleRate; return (double)([chunkData length] / bytesPerPacket) / sampleRate; diff --git a/Audio/Chain/ChunkList.h b/Audio/Chain/ChunkList.h index 6ecaae103..cdc45300a 100644 --- a/Audio/Chain/ChunkList.h +++ b/Audio/Chain/ChunkList.h @@ -73,6 +73,10 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)peekTimestamp:(nonnull double *)timestamp timeRatio:(nonnull double *)timeRatio; +// Helpers +- (AudioChunk *)removeAndMergeSamples:(size_t)maxFrameCount; +- (AudioChunk *)removeAndMergeSamplesAsFloat32:(size_t)maxFrameCount; + @end NS_ASSUME_NONNULL_END diff --git a/Audio/Chain/ChunkList.m b/Audio/Chain/ChunkList.m index 11f0afd7b..c0fa3e08f 100644 --- a/Audio/Chain/ChunkList.m +++ b/Audio/Chain/ChunkList.m @@ -550,8 +550,79 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes } } +- (AudioChunk *)removeAndMergeSamples:(size_t)maxFrameCount { + BOOL formatSet = NO; + AudioStreamBasicDescription currentFormat; + uint32_t currentChannelConfig = 0; + + double streamTimestamp = 0.0; + double streamTimeRatio = 1.0; + if(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { + return [[AudioChunk alloc] init]; + } + + AudioChunk *chunk; + size_t totalFrameCount = 0; + AudioChunk *outputChunk = [[AudioChunk alloc] init]; + + [outputChunk setStreamTimestamp:streamTimestamp]; + [outputChunk setStreamTimeRatio:streamTimeRatio]; + + while(totalFrameCount < maxFrameCount) { + AudioStreamBasicDescription newFormat; + uint32_t newChannelConfig; + if(![self peekFormat:&newFormat channelConfig:&newChannelConfig]) { + break; + } + if(formatSet && + (memcmp(&newFormat, ¤tFormat, sizeof(newFormat)) != 0 || + newChannelConfig != currentChannelConfig)) { + break; + } else if(!formatSet) { + [outputChunk setFormat:newFormat]; + [outputChunk setChannelConfig:newChannelConfig]; + currentFormat = newFormat; + currentChannelConfig = newChannelConfig; + formatSet = YES; + } + + chunk = [self removeSamples:maxFrameCount - totalFrameCount]; + if(![chunk duration]) { + break; + } + + if([chunk isHDCD]) { + [outputChunk setHDCD]; + } + + size_t frameCount = [chunk frameCount]; + NSData *sampleData = [chunk removeSamples:frameCount]; + + [outputChunk assignData:sampleData]; + + totalFrameCount += frameCount; + } + + if(!totalFrameCount) { + return [[AudioChunk alloc] init]; + } + + return outputChunk; +} + +- (AudioChunk *)removeAndMergeSamplesAsFloat32:(size_t)maxFrameCount { + AudioChunk *ret = [self removeAndMergeSamples:maxFrameCount]; + return [self convertChunk:ret]; +} + - (AudioChunk *)convertChunk:(AudioChunk *)inChunk { AudioStreamBasicDescription chunkFormat = [inChunk format]; + if(![inChunk duration] || + (chunkFormat.mFormatFlags == kAudioFormatFlagsNativeFloatPacked && + chunkFormat.mBitsPerChannel == 32)) { + return inChunk; + } + uint32_t chunkConfig = [inChunk channelConfig]; BOOL chunkLossless = [inChunk lossless]; if(!formatRead || memcmp(&chunkFormat, &inputFormat, sizeof(chunkFormat)) != 0 || diff --git a/Audio/Chain/DSP/DSPEqualizerNode.m b/Audio/Chain/DSP/DSPEqualizerNode.m index f2f29f09f..fc7983df8 100644 --- a/Audio/Chain/DSP/DSPEqualizerNode.m +++ b/Audio/Chain/DSP/DSPEqualizerNode.m @@ -354,13 +354,6 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA return nil; } - double streamTimestamp; - double streamTimeRatio; - if(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { - processEntered = NO; - return nil; - } - if((enableEqualizer && !equalizerInitialized) || memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 || inputChannelConfig != lastInputChannelConfig) { @@ -379,43 +372,21 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA } size_t totalFrameCount = 0; - AudioChunk *chunk; + AudioChunk *chunk = [self readAndMergeChunksAsFloat32:4096]; + if(![chunk duration]) { + processEntered = NO; + return nil; + } + + double streamTimestamp = [chunk streamTimestamp]; samplePtr = &inBuffer[0]; size_t channels = inputFormat.mChannelsPerFrame; - BOOL isHDCD = NO; + size_t frameCount = [chunk frameCount]; + NSData *sampleData = [chunk removeSamples:frameCount]; - while(!stopping && totalFrameCount < 4096) { - AudioStreamBasicDescription newInputFormat; - uint32_t newChannelConfig; - if(![self peekFormat:&newInputFormat channelConfig:&newChannelConfig] || - memcmp(&newInputFormat, &inputFormat, sizeof(newInputFormat)) != 0 || - newChannelConfig != inputChannelConfig) { - break; - } - - chunk = [self readChunkAsFloat32:4096 - totalFrameCount]; - if(!chunk) { - break; - } - - if([chunk isHDCD]) { - isHDCD = YES; - } - - size_t frameCount = [chunk frameCount]; - NSData *sampleData = [chunk removeSamples:frameCount]; - - cblas_scopy((int)(frameCount * channels), [sampleData bytes], 1, &inBuffer[totalFrameCount * channels], 1); - - totalFrameCount += frameCount; - } - - if(!totalFrameCount) { - processEntered = NO; - return nil; - } + cblas_scopy((int)(frameCount * channels), [sampleData bytes], 1, &inBuffer[0], 1); const size_t channelsminusone = channels - 1; uint8_t tempBuffer[sizeof(AudioBufferList) + sizeof(AudioBuffer) * channelsminusone]; @@ -428,21 +399,21 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA ioData->mBuffers[i].mNumberChannels = 1; } - OSStatus status = AudioUnitRender(_eq, NULL, &timeStamp, 0, (UInt32)totalFrameCount, ioData); + OSStatus status = AudioUnitRender(_eq, NULL, &timeStamp, 0, (UInt32)frameCount, ioData); if(status != noErr) { processEntered = NO; return nil; } - timeStamp.mSampleTime += ((double)totalFrameCount) / inputFormat.mSampleRate; + timeStamp.mSampleTime += ((double)frameCount) / inputFormat.mSampleRate; for(int i = 0; i < channels; ++i) { - cblas_scopy((int)totalFrameCount, &eqBuffer[4096 * i], 1, &outBuffer[i], (int)channels); + cblas_scopy((int)frameCount, &eqBuffer[4096 * i], 1, &outBuffer[i], (int)channels); } AudioChunk *outputChunk = nil; - if(totalFrameCount) { + if(frameCount) { scale_by_volume(&outBuffer[0], totalFrameCount * channels, equalizerPreamp); outputChunk = [[AudioChunk alloc] init]; @@ -450,9 +421,9 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA if(outputChannelConfig) { [outputChunk setChannelConfig:inputChannelConfig]; } - if(isHDCD) [outputChunk setHDCD]; + if([chunk isHDCD]) [outputChunk setHDCD]; [outputChunk setStreamTimestamp:streamTimestamp]; - [outputChunk setStreamTimeRatio:streamTimeRatio]; + [outputChunk setStreamTimeRatio:[chunk streamTimeRatio]]; [outputChunk assignSamples:&outBuffer[0] frameCount:totalFrameCount]; } diff --git a/Audio/Chain/DSP/DSPFSurroundNode.m b/Audio/Chain/DSP/DSPFSurroundNode.m index c0e4bbf02..3d881b6f6 100644 --- a/Audio/Chain/DSP/DSPFSurroundNode.m +++ b/Audio/Chain/DSP/DSPFSurroundNode.m @@ -173,13 +173,6 @@ static void * kDSPFSurroundNodeContext = &kDSPFSurroundNodeContext; return nil; } - double streamTimestamp; - double streamTimeRatio; - if(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { - processEntered = NO; - return nil; - } - if((enableFSurround && !fsurround) || memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 || inputChannelConfig != lastInputChannelConfig) { @@ -200,43 +193,23 @@ static void * kDSPFSurroundNodeContext = &kDSPFSurroundNodeContext; size_t totalRequestedSamples = resetStreamFormat ? 2048 : 4096; size_t totalFrameCount = 0; - AudioChunk *chunk; - - float *samplePtr = resetStreamFormat ? &inBuffer[2048 * 2] : &inBuffer[0]; - - BOOL isHDCD = NO; - - while(!stopping && totalFrameCount < totalRequestedSamples) { - AudioStreamBasicDescription newInputFormat; - uint32_t newChannelConfig; - if(![self peekFormat:&newInputFormat channelConfig:&newChannelConfig] || - memcmp(&newInputFormat, &inputFormat, sizeof(newInputFormat)) != 0 || - newChannelConfig != inputChannelConfig) { - break; - } - - chunk = [self readChunkAsFloat32:totalRequestedSamples - totalFrameCount]; - if(!chunk) { - break; - } - - if([chunk isHDCD]) { - isHDCD = YES; - } - - size_t frameCount = [chunk frameCount]; - NSData *sampleData = [chunk removeSamples:frameCount]; - - cblas_scopy((int)frameCount * 2, [sampleData bytes], 1, &samplePtr[totalFrameCount * 2], 1); - - totalFrameCount += frameCount; - } - - if(!totalFrameCount) { + AudioChunk *chunk = [self readAndMergeChunksAsFloat32:totalRequestedSamples]; + if(![chunk duration]) { processEntered = NO; return nil; } + double streamTimestamp = [chunk streamTimestamp]; + + float *samplePtr = resetStreamFormat ? &inBuffer[2048 * 2] : &inBuffer[0]; + + size_t frameCount = [chunk frameCount]; + NSData *sampleData = [chunk removeSamples:frameCount]; + + cblas_scopy((int)frameCount * 2, [sampleData bytes], 1, &samplePtr[0], 1); + + totalFrameCount = frameCount; + if(resetStreamFormat) { bzero(&inBuffer[0], 2048 * 2 * sizeof(float)); totalFrameCount += 2048; @@ -275,9 +248,9 @@ static void * kDSPFSurroundNodeContext = &kDSPFSurroundNodeContext; if(outputChannelConfig) { [outputChunk setChannelConfig:outputChannelConfig]; } - if(isHDCD) [outputChunk setHDCD]; + if([chunk isHDCD]) [outputChunk setHDCD]; [outputChunk setStreamTimestamp:streamTimestamp]; - [outputChunk setStreamTimeRatio:streamTimeRatio]; + [outputChunk setStreamTimeRatio:[chunk streamTimeRatio]]; [outputChunk assignSamples:samplePtr frameCount:samplesRendered]; } diff --git a/Audio/Chain/DSP/DSPRubberbandNode.m b/Audio/Chain/DSP/DSPRubberbandNode.m index d035e311c..da52fe809 100644 --- a/Audio/Chain/DSP/DSPRubberbandNode.m +++ b/Audio/Chain/DSP/DSPRubberbandNode.m @@ -373,13 +373,6 @@ static void * kDSPRubberbandNodeContext = &kDSPRubberbandNodeContext; return nil; } - double streamTimestamp; - double streamTimeRatio; - if(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) { - processEntered = NO; - return nil; - } - if(!ts || memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 || inputChannelConfig != lastInputChannelConfig) { lastInputFormat = inputFormat; @@ -395,51 +388,26 @@ static void * kDSPRubberbandNodeContext = &kDSPRubberbandNodeContext; if(samplesToProcess > blockSize) samplesToProcess = blockSize; - size_t totalFrameCount = 0; - AudioChunk *chunk; - int channels = (int)(inputFormat.mChannelsPerFrame); - - BOOL isHDCD = NO; - - while(!stopping && totalFrameCount < samplesToProcess) { - AudioStreamBasicDescription newInputFormat; - uint32_t newChannelConfig; - if(![self peekFormat:&newInputFormat channelConfig:&newChannelConfig] || - memcmp(&newInputFormat, &inputFormat, sizeof(newInputFormat)) != 0 || - newChannelConfig != inputChannelConfig) { - break; - } - - chunk = [self readChunkAsFloat32:samplesToProcess - totalFrameCount]; - if(!chunk) { - break; - } - - if([chunk isHDCD]) { - isHDCD = YES; - } - - size_t frameCount = [chunk frameCount]; - NSData *sampleData = [chunk removeSamples:frameCount]; - - for (size_t i = 0; i < channels; ++i) { - cblas_scopy((int)frameCount, ((const float *)[sampleData bytes]) + i, channels, rsPtrs[i] + totalFrameCount, 1); - } - - totalFrameCount += frameCount; - } - - if(!totalFrameCount) { + AudioChunk *chunk = [self readAndMergeChunksAsFloat32:samplesToProcess]; + if(![chunk duration]) { processEntered = NO; return nil; } + double streamTimestamp = [chunk streamTimestamp]; + + int channels = (int)(inputFormat.mChannelsPerFrame); + size_t frameCount = [chunk frameCount]; + NSData *sampleData = [chunk removeSamples:frameCount]; + + for (size_t i = 0; i < channels; ++i) { + cblas_scopy((int)frameCount, ((const float *)[sampleData bytes]) + i, channels, rsPtrs[i], 1); + } + stretchIn += [chunk duration] / tempo; bool endOfStream = [[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES; - size_t frameCount = totalFrameCount; - int len = (int)frameCount; rubberband_process(ts, (const float * const *)rsPtrs, len, endOfStream); @@ -492,9 +460,9 @@ static void * kDSPRubberbandNodeContext = &kDSPRubberbandNodeContext; if(inputChannelConfig) { [outputChunk setChannelConfig:inputChannelConfig]; } - if(isHDCD) [outputChunk setHDCD]; + if([chunk isHDCD]) [outputChunk setHDCD]; [outputChunk setStreamTimestamp:streamTimestamp]; - [outputChunk setStreamTimeRatio:streamTimeRatio * tempo]; + [outputChunk setStreamTimeRatio:[chunk streamTimeRatio] * tempo]; [outputChunk assignSamples:rsOutBuffer frameCount:samplesBuffered]; samplesBuffered = 0; stretchOut += [outputChunk duration]; diff --git a/Audio/Chain/Node.h b/Audio/Chain/Node.h index bf41068dc..18e35d7fd 100644 --- a/Audio/Chain/Node.h +++ b/Audio/Chain/Node.h @@ -43,6 +43,9 @@ - (AudioChunk *_Nonnull)readChunk:(size_t)maxFrames; - (AudioChunk *_Nonnull)readChunkAsFloat32:(size_t)maxFrames; +- (AudioChunk *_Nonnull)readAndMergeChunks:(size_t)maxFrames; +- (AudioChunk *_Nonnull)readAndMergeChunksAsFloat32:(size_t)maxFrames; + - (BOOL)peekFormat:(AudioStreamBasicDescription *_Nonnull)format channelConfig:(uint32_t *_Nonnull)config; - (BOOL)peekTimestamp:(double *_Nonnull)timestamp timeRatio:(double *_Nonnull)timeRatio; diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index f9a75aa87..a63b86f9f 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -239,6 +239,76 @@ return ret; } +- (AudioChunk *)readAndMergeChunks:(size_t)maxFrames { + [accessLock lock]; + + if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) { + endOfStream = YES; + [accessLock unlock]; + return [[AudioChunk alloc] init]; + } + + if([previousNode shouldReset] == YES) { + @autoreleasepool { + [buffer reset]; + } + + shouldReset = YES; + [previousNode setShouldReset:NO]; + + [[previousNode semaphore] signal]; + } + + AudioChunk *ret; + + @autoreleasepool { + ret = [[previousNode buffer] removeAndMergeSamples:maxFrames]; + } + + [accessLock unlock]; + + if([ret frameCount]) { + [[previousNode semaphore] signal]; + } + + return ret; +} + +- (AudioChunk *)readAndMergeChunksAsFloat32:(size_t)maxFrames { + [accessLock lock]; + + if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) { + endOfStream = YES; + [accessLock unlock]; + return [[AudioChunk alloc] init]; + } + + if([previousNode shouldReset] == YES) { + @autoreleasepool { + [buffer reset]; + } + + shouldReset = YES; + [previousNode setShouldReset:NO]; + + [[previousNode semaphore] signal]; + } + + AudioChunk *ret; + + @autoreleasepool { + ret = [[previousNode buffer] removeAndMergeSamplesAsFloat32:maxFrames]; + } + + [accessLock unlock]; + + if([ret frameCount]) { + [[previousNode semaphore] signal]; + } + + return ret; +} + - (void)launchThread { [NSThread detachNewThreadSelector:@selector(threadEntry:) toTarget:self withObject:nil]; }