Added downmixing of surround for stereo output devices

CQTexperiment
Chris Moeller 2013-10-07 00:24:26 -07:00
parent 167524b6f2
commit 8aa01894ee
2 changed files with 207 additions and 21 deletions

View File

@ -16,9 +16,14 @@
@interface ConverterNode : Node { @interface ConverterNode : Node {
AudioConverterRef converter; AudioConverterRef converter;
AudioConverterRef converterDownmix;
void *callbackBuffer; void *callbackBuffer;
void *downmixBuffer;
int downmixSize, downmixOffset;
AudioStreamBasicDescription inputFormat; AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription downmixFormat;
AudioStreamBasicDescription outputFormat; AudioStreamBasicDescription outputFormat;
} }

View File

@ -28,6 +28,53 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
@implementation ConverterNode @implementation ConverterNode
static const float STEREO_DOWNMIX[8-2][8][2]={
/*3.0*/
{
{0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F}
},
/*quadrophonic*/
{
{0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
},
/*5.0*/
{
{0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F},
{0.3254F,0.5636F}
},
/*5.1*/
{
{0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F},
{0.2645F,0.4582F}
},
/*6.1*/
{
{0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.3943F,0.2277F},
{0.2277F,0.3943F},{0.2788F,0.2788F}
},
/*7.1*/
{
{0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F},
{0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F}
}
};
static void downmix_to_stereo(float * buffer, int channels, int count)
{
if (channels >= 3 && channels < 8)
for (int i = 0; i < count; ++i)
{
float left = 0, right = 0;
for (int j = 0; j < channels; ++j)
{
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;
}
}
//called from the complexfill when the audio is converted...good clean fun //called from the complexfill when the audio is converted...good clean fun
static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
{ {
@ -58,7 +105,7 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
return 100; //Keep asking for data return 100; //Keep asking for data
} }
ioData->mBuffers[0].mData = converter->callbackBuffer; ioData->mBuffers[0].mData = converter->callbackBuffer;
ioData->mBuffers[0].mDataByteSize = amountRead; ioData->mBuffers[0].mDataByteSize = amountRead;
ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame); ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame);
@ -67,6 +114,35 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
return err; return err;
} }
static OSStatus ACDownmixProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
{
ConverterNode *converter = (ConverterNode *)inUserData;
OSStatus err = noErr;
int amountToWrite;
if ([converter shouldContinue] == NO || [converter endOfStream] == YES)
{
ioData->mBuffers[0].mDataByteSize = 0;
*ioNumberDataPackets = 0;
return noErr;
}
amountToWrite = (*ioNumberDataPackets)*(converter->downmixFormat.mBytesPerPacket);
if ( amountToWrite + converter->downmixOffset > converter->downmixSize )
amountToWrite = converter->downmixSize - converter->downmixOffset;
ioData->mBuffers[0].mData = converter->downmixBuffer + converter->downmixOffset;
ioData->mBuffers[0].mDataByteSize = amountToWrite;
ioData->mBuffers[0].mNumberChannels = (converter->downmixFormat.mChannelsPerFrame);
ioData->mNumberBuffers = 1;
converter->downmixOffset += amountToWrite;
return err;
}
-(void)process -(void)process
{ {
char writeBuf[CHUNK_SIZE]; char writeBuf[CHUNK_SIZE];
@ -83,23 +159,88 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
AudioBufferList ioData; AudioBufferList ioData;
UInt32 ioNumberFrames; UInt32 ioNumberFrames;
OSStatus err; OSStatus err;
int amountRead = 0;
ioNumberFrames = amount/outputFormat.mBytesPerFrame; if ( converterDownmix )
ioData.mBuffers[0].mData = dest; {
ioData.mBuffers[0].mDataByteSize = amount; if (downmixOffset == downmixSize) {
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame; ioNumberFrames = amount / outputFormat.mBytesPerFrame;
ioData.mNumberBuffers = 1;
downmixBuffer = realloc( downmixBuffer, ioNumberFrames * downmixFormat.mBytesPerFrame );
ioData.mBuffers[0].mData = downmixBuffer;
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * downmixFormat.mBytesPerFrame;
ioData.mBuffers[0].mNumberChannels = downmixFormat.mChannelsPerFrame;
ioData.mNumberBuffers = 1;
tryagain:
err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL);
amountRead += ioData.mBuffers[0].mDataByteSize;
if (err == 100)
{
NSLog(@"INSIZE: %i", amountRead);
ioData.mBuffers[0].mData = downmixBuffer + amountRead;
ioNumberFrames = ( amount / outputFormat.mBytesPerFrame ) - ( amountRead / downmixFormat.mBytesPerFrame );
ioData.mBuffers[0].mDataByteSize = ioNumberFrames * downmixFormat.mBytesPerFrame;
goto tryagain;
}
else if (err != noErr)
{
NSLog(@"Error: %i", err);
}
downmix_to_stereo( (float*) downmixBuffer, downmixFormat.mChannelsPerFrame, amountRead / downmixFormat.mBytesPerFrame );
downmixSize = amountRead;
downmixOffset = 0;
}
ioNumberFrames = amount / outputFormat.mBytesPerFrame;
ioData.mBuffers[0].mData = dest;
ioData.mBuffers[0].mDataByteSize = amount;
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
ioData.mNumberBuffers = 1;
amountRead = 0;
tryagain2:
err = AudioConverterFillComplexBuffer(converterDownmix, ACDownmixProc, self, &ioNumberFrames, &ioData, NULL);
amountRead += ioData.mBuffers[0].mDataByteSize;
if (err == 100)
{
NSLog(@"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)
{
NSLog(@"Error: %i", err);
}
}
else
{
ioNumberFrames = amount/outputFormat.mBytesPerFrame;
ioData.mBuffers[0].mData = dest;
ioData.mBuffers[0].mDataByteSize = amount;
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
ioData.mNumberBuffers = 1;
err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL); tryagain3:
int amountRead = ioData.mBuffers[0].mDataByteSize; err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL);
if (err == kAudioConverterErr_InvalidInputSize) //It returns insz at EOS at times...so run it again to make sure all data is converted amountRead += ioData.mBuffers[0].mDataByteSize;
{ if (err == 100) //It returns insz at EOS at times...so run it again to make sure all data is converted
NSLog(@"INSIZE: %i", amountRead); {
amountRead += [self convert:dest + amountRead amount:amount - amountRead]; NSLog(@"INSIZE: %i", amountRead);
} ioData.mBuffers[0].mData = dest + amountRead;
else if (err != noErr && err != 100) { ioNumberFrames = ( amount - amountRead ) / outputFormat.mBytesPerFrame;
NSLog(@"Error: %i", err); ioData.mBuffers[0].mDataByteSize = ioNumberFrames * outputFormat.mBytesPerFrame;
} goto tryagain3;
}
else if (err != noErr) {
NSLog(@"Error: %i", err);
}
}
return amountRead; return amountRead;
} }
@ -112,11 +253,41 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
inputFormat = inf; inputFormat = inf;
outputFormat = outf; outputFormat = outf;
stat = AudioConverterNew ( &inputFormat, &outputFormat, &converter); if (inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2)
if (stat != noErr) {
{ downmixFormat = inputFormat;
NSLog(@"Error creating converter %i", stat); downmixFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
} downmixFormat.mBitsPerChannel = 32;
downmixFormat.mBytesPerFrame = (32/8)*downmixFormat.mChannelsPerFrame;
downmixFormat.mBytesPerPacket = downmixFormat.mBytesPerFrame * downmixFormat.mFramesPerPacket;
stat = AudioConverterNew( &inputFormat, &downmixFormat, &converter );
if (stat != noErr)
{
NSLog(@"Error creating converter %i", stat);
}
stat = AudioConverterNew ( &downmixFormat, &outputFormat, &converterDownmix );
if (stat != noErr)
{
NSLog(@"Error creating converter %i", stat);
}
SInt32 channelMap[2] = { 0, 1 };
stat = AudioConverterSetProperty(converterDownmix,kAudioConverterChannelMap,sizeof(channelMap),channelMap);
if (stat != noErr)
{
NSLog(@"Error mapping channels %i", stat);
}
}
else
{
stat = AudioConverterNew ( &inputFormat, &outputFormat, &converter);
if (stat != noErr)
{
NSLog(@"Error creating converter %i", stat);
}
}
if (inputFormat.mChannelsPerFrame == 1) if (inputFormat.mChannelsPerFrame == 1)
{ {
@ -158,11 +329,21 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
- (void)cleanUp - (void)cleanUp
{ {
if (converterDownmix)
{
AudioConverterDispose(converterDownmix);
converterDownmix = NULL;
}
if (converter) if (converter)
{ {
AudioConverterDispose(converter); AudioConverterDispose(converter);
converter = NULL; converter = NULL;
} }
if (downmixBuffer)
{
free(downmixBuffer);
downmixBuffer = NULL;
}
if (callbackBuffer) { if (callbackBuffer) {
free(callbackBuffer); free(callbackBuffer);
callbackBuffer = NULL; callbackBuffer = NULL;