diff --git a/Audio/Chain/ConverterNode.h b/Audio/Chain/ConverterNode.h index 75771b49b..9077e2ad7 100644 --- a/Audio/Chain/ConverterNode.h +++ b/Audio/Chain/ConverterNode.h @@ -20,14 +20,19 @@ AudioConverterRef converter; AudioConverterRef converterFloat; void *callbackBuffer; + size_t callbackBufferSize; + + float sampleRatio; float volumeScale; void *floatBuffer; + size_t floatBufferSize; int floatSize, floatOffset; AudioStreamBasicDescription inputFormat; AudioStreamBasicDescription floatFormat; + AudioStreamBasicDescription dmFloatFormat; // downmixed float format AudioStreamBasicDescription outputFormat; } diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index 8a4849adc..54e5a3261 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -40,7 +40,9 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc) converterFloat = NULL; converter = NULL; floatBuffer = NULL; + floatBufferSize = 0; callbackBuffer = NULL; + callbackBufferSize = 0; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil]; } @@ -81,7 +83,7 @@ static const float STEREO_DOWNMIX[8-2][8][2]={ static void downmix_to_stereo(float * buffer, int channels, int count) { - if (channels >= 3 && channels < 8) + if (channels >= 3 && channels <= 8) for (int i = 0; i < count; ++i) { float left = 0, right = 0; @@ -90,8 +92,8 @@ static void downmix_to_stereo(float * buffer, int channels, int count) left += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0]; right += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1]; } - buffer[i * channels + 0] = left; - buffer[i * channels + 1] = right; + buffer[i * 2 + 0] = left; + buffer[i * 2 + 1] = right; } } @@ -124,7 +126,8 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket); - converter->callbackBuffer = realloc(converter->callbackBuffer, amountToWrite); + if (!converter->callbackBuffer || converter->callbackBufferSize < amountToWrite) + converter->callbackBuffer = realloc(converter->callbackBuffer, converter->callbackBufferSize = amountToWrite + 1024); amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite]; if (amountRead == 0 && [converter endOfStream] == NO) @@ -161,15 +164,21 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, return noErr; } - amountToWrite = (*ioNumberDataPackets)*(converter->floatFormat.mBytesPerPacket); + amountToWrite = (*ioNumberDataPackets) * (converter->dmFloatFormat.mBytesPerPacket); if ( amountToWrite + converter->floatOffset > converter->floatSize ) + { amountToWrite = converter->floatSize - converter->floatOffset; + *ioNumberDataPackets = amountToWrite / (converter->dmFloatFormat.mBytesPerPacket); + } ioData->mBuffers[0].mData = converter->floatBuffer + converter->floatOffset; ioData->mBuffers[0].mDataByteSize = amountToWrite; - ioData->mBuffers[0].mNumberChannels = (converter->floatFormat.mChannelsPerFrame); + ioData->mBuffers[0].mNumberChannels = (converter->dmFloatFormat.mChannelsPerFrame); ioData->mNumberBuffers = 1; + + if (amountToWrite == 0) + return 100; converter->floatOffset += amountToWrite; @@ -190,28 +199,41 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, - (int)convert:(void *)dest amount:(int)amount { AudioBufferList ioData; - UInt32 ioNumberFrames; + UInt32 ioNumberPackets; OSStatus err; + int amountReadFromFC; int amountRead = 0; + +tryagain2: + amountReadFromFC = 0; if (floatOffset == floatSize) { - ioNumberFrames = amount / outputFormat.mBytesPerFrame; - - floatBuffer = realloc( floatBuffer, ioNumberFrames * floatFormat.mBytesPerFrame ); + UInt32 ioWantedNumberPackets; + + ioNumberPackets = amount / outputFormat.mBytesPerPacket; + + ioNumberPackets = (UInt32)((float)ioNumberPackets * sampleRatio); + ioNumberPackets = (ioNumberPackets + 255) & ~255; + + ioWantedNumberPackets = ioNumberPackets; + + size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket; + if (!floatBuffer || floatBufferSize < newSize) + floatBuffer = realloc( floatBuffer, floatBufferSize = newSize + 1024 ); ioData.mBuffers[0].mData = floatBuffer; - ioData.mBuffers[0].mDataByteSize = ioNumberFrames * floatFormat.mBytesPerFrame; + ioData.mBuffers[0].mDataByteSize = ioNumberPackets * floatFormat.mBytesPerPacket; ioData.mBuffers[0].mNumberChannels = floatFormat.mChannelsPerFrame; ioData.mNumberBuffers = 1; tryagain: - err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, (__bridge void * _Nullable)(self), &ioNumberFrames, &ioData, NULL); - amountRead += ioData.mBuffers[0].mDataByteSize; + err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, (__bridge void * _Nullable)(self), &ioNumberPackets, &ioData, NULL); + amountReadFromFC += ioNumberPackets * floatFormat.mBytesPerPacket; if (err == 100) { - DLog(@"INSIZE: %i", amountRead); - ioData.mBuffers[0].mData = floatBuffer + amountRead; - ioNumberFrames = ( amount / outputFormat.mBytesPerFrame ) - ( amountRead / floatFormat.mBytesPerFrame ); - ioData.mBuffers[0].mDataByteSize = ioNumberFrames * floatFormat.mBytesPerFrame; + ioData.mBuffers[0].mData = (void *)(((uint8_t*)floatBuffer) + amountReadFromFC); + ioNumberPackets = ioWantedNumberPackets - ioNumberPackets; + ioWantedNumberPackets = ioNumberPackets; + ioData.mBuffers[0].mDataByteSize = ioNumberPackets * floatFormat.mBytesPerPacket; usleep(10000); goto tryagain; } @@ -222,31 +244,28 @@ static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, } if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 ) - downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, amountRead / floatFormat.mBytesPerFrame ); + { + int samples = amountReadFromFC / floatFormat.mBytesPerFrame; + downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples ); + amountReadFromFC = samples * sizeof(float) * 2; + } - scale_by_volume( (float*) floatBuffer, amountRead / sizeof(float), volumeScale); + scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale); - floatSize = amountRead; + floatSize = amountReadFromFC; floatOffset = 0; } - ioNumberFrames = amount / outputFormat.mBytesPerFrame; - ioData.mBuffers[0].mData = dest; - ioData.mBuffers[0].mDataByteSize = amount; + ioNumberPackets = amount / outputFormat.mBytesPerPacket; + ioData.mBuffers[0].mData = dest + amountRead; + ioData.mBuffers[0].mDataByteSize = amount - amountRead; ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame; ioData.mNumberBuffers = 1; - amountRead = 0; - -tryagain2: - err = AudioConverterFillComplexBuffer(converter, ACFloatProc, (__bridge void *)(self), &ioNumberFrames, &ioData, NULL); - amountRead += ioData.mBuffers[0].mDataByteSize; + err = AudioConverterFillComplexBuffer(converter, ACFloatProc, (__bridge void *)(self), &ioNumberPackets, &ioData, NULL); + amountRead += ioNumberPackets * outputFormat.mBytesPerPacket; if (err == 100) { - DLog(@"INSIZE: %i", amountRead); - ioData.mBuffers[0].mData = dest + amountRead; - ioNumberFrames = ( amount - amountRead ) / outputFormat.mBytesPerFrame; - ioData.mBuffers[0].mDataByteSize = ioNumberFrames * outputFormat.mBytesPerFrame; goto tryagain2; } else if (err != noErr && err != kAudioConverterErr_InvalidInputSize) @@ -342,6 +361,11 @@ static float db_to_scale(float db) return NO; } + dmFloatFormat = floatFormat; + floatFormat.mChannelsPerFrame = outputFormat.mChannelsPerFrame; + floatFormat.mBytesPerFrame = (32/8)*floatFormat.mChannelsPerFrame; + floatFormat.mBytesPerPacket = floatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket; + stat = AudioConverterNew ( &floatFormat, &outputFormat, &converter ); if (stat != noErr) { @@ -349,6 +373,8 @@ static float db_to_scale(float db) return NO; } +#if 0 + // These mappings don't do what I want, so avoid them. if (inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2) { SInt32 channelMap[2] = { 0, 1 }; @@ -360,11 +386,26 @@ static float db_to_scale(float db) return NO; } } - else if (inputFormat.mChannelsPerFrame == 1) + else if (inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1) + { + SInt32 channelMap[1] = { 0 }; + + stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,(int)sizeof(channelMap),channelMap); + if (stat != noErr) + { + ALog(@"Error mapping channels %i", stat); + return NO; + } + } + else +#endif + if (inputFormat.mChannelsPerFrame == 1 && outputFormat.mChannelsPerFrame > 1) { - SInt32 channelMap[2] = { 0, 0 }; + SInt32 channelMap[outputFormat.mChannelsPerFrame]; + + memset(channelMap, 0, sizeof(channelMap)); - stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,sizeof(channelMap),channelMap); + stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,(int)sizeof(channelMap),channelMap); if (stat != noErr) { ALog(@"Error mapping channels %i", stat); @@ -377,6 +418,8 @@ static float db_to_scale(float db) [self refreshVolumeScaling]; + sampleRatio = (float)inputFormat.mSampleRate / (float)outputFormat.mSampleRate; + return YES; } @@ -427,10 +470,12 @@ static float db_to_scale(float db) { free(floatBuffer); floatBuffer = NULL; + floatBufferSize = 0; } if (callbackBuffer) { free(callbackBuffer); callbackBuffer = NULL; + callbackBufferSize = 0; } floatOffset = 0; floatSize = 0; diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 959f4d459..28791a8eb 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -361,6 +361,14 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc ///Seems some 3rd party devices return incorrect stuff...or I just don't like noninterleaved data. deviceFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved; + // Bluetooth devices in communications mode tend to have reduced settings, + // so let's work around that. + // For some reason, mono output just doesn't work, so bleh. + if (deviceFormat.mChannelsPerFrame < 2) + deviceFormat.mChannelsPerFrame = 2; + // And sample rate will be cruddy for the duration of playback, so fix it. + if (deviceFormat.mSampleRate < 32000) + deviceFormat.mSampleRate = 48000; // deviceFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat; // deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; deviceFormat.mBytesPerFrame = deviceFormat.mChannelsPerFrame*(deviceFormat.mBitsPerChannel/8);