2007-10-03 20:23:14 +00:00
|
|
|
//
|
|
|
|
// ConverterNode.m
|
|
|
|
// Cog
|
|
|
|
//
|
|
|
|
// Created by Zaphod Beeblebrox on 8/2/05.
|
|
|
|
// Copyright 2005 __MyCompanyName__. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "ConverterNode.h"
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
|
|
|
{
|
|
|
|
if (!inDesc) {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog (@"Can't print a NULL desc!\n");
|
2007-10-03 20:23:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog (@"- - - - - - - - - - - - - - - - - - - -\n");
|
|
|
|
DLog (@" Sample Rate:%f\n", inDesc->mSampleRate);
|
|
|
|
DLog (@" Format ID:%s\n", (char*)&inDesc->mFormatID);
|
|
|
|
DLog (@" Format Flags:%X\n", inDesc->mFormatFlags);
|
|
|
|
DLog (@" Bytes per Packet:%d\n", inDesc->mBytesPerPacket);
|
|
|
|
DLog (@" Frames per Packet:%d\n", inDesc->mFramesPerPacket);
|
|
|
|
DLog (@" Bytes per Frame:%d\n", inDesc->mBytesPerFrame);
|
|
|
|
DLog (@" Channels per Frame:%d\n", inDesc->mChannelsPerFrame);
|
|
|
|
DLog (@" Bits per Channel:%d\n", inDesc->mBitsPerChannel);
|
|
|
|
DLog (@"- - - - - - - - - - - - - - - - - - - -\n");
|
2007-10-03 20:23:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@implementation ConverterNode
|
|
|
|
|
2013-10-11 03:02:02 +00:00
|
|
|
- (id)initWithController:(id)c previous:(id)p
|
|
|
|
{
|
|
|
|
self = [super initWithController:c previous:p];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
rgInfo = nil;
|
|
|
|
|
2013-10-13 12:14:57 +00:00
|
|
|
converterFloat = NULL;
|
|
|
|
converter = NULL;
|
|
|
|
floatBuffer = NULL;
|
2021-12-25 23:02:13 +00:00
|
|
|
floatBufferSize = 0;
|
2013-10-13 12:14:57 +00:00
|
|
|
callbackBuffer = NULL;
|
2021-12-25 23:02:13 +00:00
|
|
|
callbackBufferSize = 0;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
stopping = NO;
|
|
|
|
convertEntered = NO;
|
|
|
|
ACInputEntered = NO;
|
|
|
|
ACFloatEntered = NO;
|
2013-10-13 12:14:57 +00:00
|
|
|
|
2013-10-11 03:02:02 +00:00
|
|
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2013-10-07 07:24:26 +00:00
|
|
|
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)
|
|
|
|
{
|
2021-12-25 23:02:13 +00:00
|
|
|
if (channels >= 3 && channels <= 8)
|
2013-10-07 07:24:26 +00:00
|
|
|
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];
|
|
|
|
}
|
2021-12-25 23:02:13 +00:00
|
|
|
buffer[i * 2 + 0] = left;
|
|
|
|
buffer[i * 2 + 1] = right;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-26 06:32:43 +00:00
|
|
|
static void downmix_to_mono(float * buffer, int channels, int count)
|
|
|
|
{
|
|
|
|
if (channels >= 3 && channels <= 8)
|
|
|
|
{
|
|
|
|
downmix_to_stereo(buffer, channels, count);
|
|
|
|
channels = 2;
|
|
|
|
}
|
|
|
|
float invchannels = 1.0 / (float)channels;
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
float sample = 0;
|
|
|
|
for (int j = 0; j < channels; ++j)
|
|
|
|
{
|
|
|
|
sample += buffer[i * channels + j];
|
|
|
|
}
|
|
|
|
buffer[i] = sample * invchannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void upmix(float * buffer, int inchannels, int outchannels, int count)
|
|
|
|
{
|
|
|
|
for (int i = count - 1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
if (inchannels == 1)
|
|
|
|
{
|
|
|
|
float sample = buffer[i];
|
|
|
|
for (int j = 0; j < 2; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = sample;
|
|
|
|
}
|
|
|
|
for (int j = 2; j < outchannels; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float samples[inchannels];
|
|
|
|
for (int j = 0; j < inchannels; ++j)
|
|
|
|
{
|
|
|
|
samples[j] = buffer[i * inchannels + j];
|
|
|
|
}
|
|
|
|
for (int j = 0; j < inchannels; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = samples[j];
|
|
|
|
}
|
|
|
|
for (int j = inchannels; j < outchannels; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
static void scale_by_volume(float * buffer, int count, float volume)
|
|
|
|
{
|
|
|
|
if ( volume != 1.0 )
|
|
|
|
for (int i = 0; i < count; ++i )
|
|
|
|
buffer[i] *= volume;
|
|
|
|
}
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
//called from the complexfill when the audio is converted...good clean fun
|
2013-10-12 20:52:58 +00:00
|
|
|
static OSStatus ACInputProc(AudioConverterRef inAudioConverter,
|
|
|
|
UInt32* ioNumberDataPackets,
|
|
|
|
AudioBufferList* ioData,
|
|
|
|
AudioStreamPacketDescription** outDataPacketDescription,
|
|
|
|
void* inUserData)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
ConverterNode *converter = (__bridge ConverterNode *)inUserData;
|
2007-10-03 20:23:14 +00:00
|
|
|
OSStatus err = noErr;
|
|
|
|
int amountToWrite;
|
|
|
|
int amountRead;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
if (converter->stopping || [converter shouldContinue] == NO || [converter endOfStream] == YES)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
|
|
|
ioData->mBuffers[0].mDataByteSize = 0;
|
|
|
|
*ioNumberDataPackets = 0;
|
|
|
|
|
|
|
|
return noErr;
|
|
|
|
}
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
converter->ACInputEntered = YES;
|
2007-10-03 20:23:14 +00:00
|
|
|
|
|
|
|
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
if (!converter->callbackBuffer || converter->callbackBufferSize < amountToWrite)
|
|
|
|
converter->callbackBuffer = realloc(converter->callbackBuffer, converter->callbackBufferSize = amountToWrite + 1024);
|
2007-10-03 20:23:14 +00:00
|
|
|
|
|
|
|
amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite];
|
2007-10-20 03:24:27 +00:00
|
|
|
if (amountRead == 0 && [converter endOfStream] == NO)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
|
|
|
ioData->mBuffers[0].mDataByteSize = 0;
|
|
|
|
*ioNumberDataPackets = 0;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
converter->ACInputEntered = NO;
|
2007-10-03 20:23:14 +00:00
|
|
|
|
|
|
|
return 100; //Keep asking for data
|
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
ioData->mBuffers[0].mData = converter->callbackBuffer;
|
|
|
|
ioData->mBuffers[0].mDataByteSize = amountRead;
|
|
|
|
ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame);
|
|
|
|
ioData->mNumberBuffers = 1;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
converter->ACInputEntered = NO;
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-10-12 20:52:58 +00:00
|
|
|
static OSStatus ACFloatProc(AudioConverterRef inAudioConverter,
|
|
|
|
UInt32* ioNumberDataPackets,
|
|
|
|
AudioBufferList* ioData,
|
|
|
|
AudioStreamPacketDescription** outDataPacketDescription,
|
|
|
|
void* inUserData)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
ConverterNode *converter = (__bridge ConverterNode *)inUserData;
|
2013-10-07 07:24:26 +00:00
|
|
|
OSStatus err = noErr;
|
|
|
|
int amountToWrite;
|
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
if (converter->stopping || [converter shouldContinue] == NO)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
|
|
|
ioData->mBuffers[0].mDataByteSize = 0;
|
|
|
|
*ioNumberDataPackets = 0;
|
|
|
|
|
|
|
|
return noErr;
|
|
|
|
}
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
converter->ACFloatEntered = YES;
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
amountToWrite = (*ioNumberDataPackets) * (converter->dmFloatFormat.mBytesPerPacket);
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
if ( amountToWrite + converter->floatOffset > converter->floatSize )
|
2021-12-25 23:02:13 +00:00
|
|
|
{
|
2013-10-07 10:59:04 +00:00
|
|
|
amountToWrite = converter->floatSize - converter->floatOffset;
|
2021-12-25 23:02:13 +00:00
|
|
|
*ioNumberDataPackets = amountToWrite / (converter->dmFloatFormat.mBytesPerPacket);
|
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
ioData->mBuffers[0].mData = converter->floatBuffer + converter->floatOffset;
|
2013-10-07 07:24:26 +00:00
|
|
|
ioData->mBuffers[0].mDataByteSize = amountToWrite;
|
2021-12-25 23:02:13 +00:00
|
|
|
ioData->mBuffers[0].mNumberChannels = (converter->dmFloatFormat.mChannelsPerFrame);
|
2013-10-07 07:24:26 +00:00
|
|
|
ioData->mNumberBuffers = 1;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
|
|
|
if (amountToWrite == 0)
|
2021-12-26 07:41:45 +00:00
|
|
|
{
|
|
|
|
converter->ACFloatEntered = NO;
|
2021-12-25 23:02:13 +00:00
|
|
|
return 100;
|
2021-12-26 07:41:45 +00:00
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
converter->floatOffset += amountToWrite;
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
converter->ACFloatEntered = NO;
|
|
|
|
|
2013-10-07 07:24:26 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
-(void)process
|
|
|
|
{
|
2007-10-20 03:24:27 +00:00
|
|
|
char writeBuf[CHUNK_SIZE];
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2007-10-20 03:24:27 +00:00
|
|
|
while ([self shouldContinue] == YES && [self endOfStream] == NO) //Need to watch EOS somehow....
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
2007-10-20 03:24:27 +00:00
|
|
|
int amountConverted = [self convert:writeBuf amount:CHUNK_SIZE];
|
2007-10-03 20:23:14 +00:00
|
|
|
[self writeData:writeBuf amount:amountConverted];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)convert:(void *)dest amount:(int)amount
|
|
|
|
{
|
|
|
|
AudioBufferList ioData;
|
2021-12-25 23:02:13 +00:00
|
|
|
UInt32 ioNumberPackets;
|
2007-10-03 20:23:14 +00:00
|
|
|
OSStatus err;
|
2021-12-25 23:02:13 +00:00
|
|
|
int amountReadFromFC;
|
2013-10-07 07:24:26 +00:00
|
|
|
int amountRead = 0;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
convertEntered = YES;
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
tryagain2:
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping || [self shouldContinue] == NO || [self endOfStream] == YES)
|
2021-12-26 06:47:17 +00:00
|
|
|
{
|
2021-12-26 07:41:45 +00:00
|
|
|
convertEntered = NO;
|
2021-12-26 06:47:17 +00:00
|
|
|
return amountRead;
|
|
|
|
}
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
amountReadFromFC = 0;
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
if (floatOffset == floatSize) {
|
2021-12-25 23:02:13 +00:00
|
|
|
UInt32 ioWantedNumberPackets;
|
|
|
|
|
|
|
|
ioNumberPackets = amount / outputFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
ioNumberPackets = (UInt32)((float)ioNumberPackets * sampleRatio);
|
|
|
|
ioNumberPackets = (ioNumberPackets + 255) & ~255;
|
|
|
|
|
|
|
|
ioWantedNumberPackets = ioNumberPackets;
|
|
|
|
|
|
|
|
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
2021-12-26 06:32:43 +00:00
|
|
|
if (newSize < (ioNumberPackets * dmFloatFormat.mBytesPerPacket))
|
|
|
|
newSize = ioNumberPackets * dmFloatFormat.mBytesPerPacket;
|
2021-12-25 23:02:13 +00:00
|
|
|
if (!floatBuffer || floatBufferSize < newSize)
|
|
|
|
floatBuffer = realloc( floatBuffer, floatBufferSize = newSize + 1024 );
|
2013-10-07 10:59:04 +00:00
|
|
|
ioData.mBuffers[0].mData = floatBuffer;
|
2021-12-25 23:02:13 +00:00
|
|
|
ioData.mBuffers[0].mDataByteSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
2013-10-07 10:59:04 +00:00
|
|
|
ioData.mBuffers[0].mNumberChannels = floatFormat.mChannelsPerFrame;
|
2013-10-07 07:24:26 +00:00
|
|
|
ioData.mNumberBuffers = 1;
|
2013-10-07 10:59:04 +00:00
|
|
|
|
|
|
|
tryagain:
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping)
|
|
|
|
{
|
|
|
|
convertEntered = NO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
err = AudioConverterFillComplexBuffer(converterFloat, ACInputProc, (__bridge void * _Nullable)(self), &ioNumberPackets, &ioData, NULL);
|
|
|
|
amountReadFromFC += ioNumberPackets * floatFormat.mBytesPerPacket;
|
2013-10-07 07:24:26 +00:00
|
|
|
if (err == 100)
|
|
|
|
{
|
2021-12-25 23:02:13 +00:00
|
|
|
ioData.mBuffers[0].mData = (void *)(((uint8_t*)floatBuffer) + amountReadFromFC);
|
|
|
|
ioNumberPackets = ioWantedNumberPackets - ioNumberPackets;
|
|
|
|
ioWantedNumberPackets = ioNumberPackets;
|
|
|
|
ioData.mBuffers[0].mDataByteSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
2015-08-02 07:18:22 +00:00
|
|
|
usleep(10000);
|
2013-10-07 10:59:04 +00:00
|
|
|
goto tryagain;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
|
|
|
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Error: %i", err);
|
2021-12-26 07:41:45 +00:00
|
|
|
convertEntered = NO;
|
2013-10-07 10:59:04 +00:00
|
|
|
return amountRead;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
|
|
|
|
if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 )
|
2021-12-25 23:02:13 +00:00
|
|
|
{
|
|
|
|
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
|
|
|
|
downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples );
|
|
|
|
amountReadFromFC = samples * sizeof(float) * 2;
|
|
|
|
}
|
2021-12-26 06:32:43 +00:00
|
|
|
else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 )
|
|
|
|
{
|
|
|
|
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
|
|
|
|
downmix_to_mono( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples );
|
|
|
|
amountReadFromFC = samples * sizeof(float);
|
|
|
|
}
|
|
|
|
else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame )
|
|
|
|
{
|
|
|
|
int samples = amountReadFromFC / floatFormat.mBytesPerFrame;
|
|
|
|
upmix( (float*) floatBuffer, inputFormat.mChannelsPerFrame, outputFormat.mChannelsPerFrame, samples );
|
|
|
|
amountReadFromFC = samples * sizeof(float) * outputFormat.mChannelsPerFrame;
|
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale);
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
floatSize = amountReadFromFC;
|
2013-10-07 10:59:04 +00:00
|
|
|
floatOffset = 0;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
ioNumberPackets = amount / outputFormat.mBytesPerPacket;
|
|
|
|
ioData.mBuffers[0].mData = dest + amountRead;
|
|
|
|
ioData.mBuffers[0].mDataByteSize = amount - amountRead;
|
2013-10-07 10:59:04 +00:00
|
|
|
ioData.mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
|
|
|
|
ioData.mNumberBuffers = 1;
|
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping)
|
|
|
|
{
|
|
|
|
convertEntered = NO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
err = AudioConverterFillComplexBuffer(converter, ACFloatProc, (__bridge void *)(self), &ioNumberPackets, &ioData, NULL);
|
|
|
|
amountRead += ioNumberPackets * outputFormat.mBytesPerPacket;
|
2013-10-07 10:59:04 +00:00
|
|
|
if (err == 100)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2013-10-07 10:59:04 +00:00
|
|
|
goto tryagain2;
|
|
|
|
}
|
|
|
|
else if (err != noErr && err != kAudioConverterErr_InvalidInputSize)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Error: %i", err);
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
convertEntered = NO;
|
2007-10-20 03:24:27 +00:00
|
|
|
return amountRead;
|
2007-10-03 20:23:14 +00:00
|
|
|
}
|
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
|
|
ofObject:(id)object
|
|
|
|
change:(NSDictionary *)change
|
|
|
|
context:(void *)context
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"SOMETHING CHANGED!");
|
2013-10-07 10:59:04 +00:00
|
|
|
if ([keyPath isEqual:@"values.volumeScaling"]) {
|
|
|
|
//User reset the volume scaling option
|
|
|
|
[self refreshVolumeScaling];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static float db_to_scale(float db)
|
|
|
|
{
|
|
|
|
return pow(10.0, db / 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)refreshVolumeScaling
|
|
|
|
{
|
|
|
|
if (rgInfo == nil)
|
|
|
|
{
|
|
|
|
volumeScale = 1.0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString * scaling = [[NSUserDefaults standardUserDefaults] stringForKey:@"volumeScaling"];
|
|
|
|
BOOL useAlbum = [scaling hasPrefix:@"albumGain"];
|
|
|
|
BOOL useTrack = useAlbum || [scaling hasPrefix:@"trackGain"];
|
|
|
|
BOOL useVolume = useAlbum || useTrack || [scaling isEqualToString:@"volumeScale"];
|
|
|
|
BOOL usePeak = [scaling hasSuffix:@"WithPeak"];
|
|
|
|
float scale = 1.0;
|
|
|
|
float peak = 0.0;
|
|
|
|
if (useVolume) {
|
|
|
|
id pVolumeScale = [rgInfo objectForKey:@"volume"];
|
|
|
|
if (pVolumeScale != nil)
|
|
|
|
scale = [pVolumeScale floatValue];
|
|
|
|
}
|
|
|
|
if (useTrack) {
|
|
|
|
id trackGain = [rgInfo objectForKey:@"replayGainTrackGain"];
|
|
|
|
id trackPeak = [rgInfo objectForKey:@"replayGainTrackPeak"];
|
|
|
|
if (trackGain != nil)
|
|
|
|
scale = db_to_scale([trackGain floatValue]);
|
|
|
|
if (trackPeak != nil)
|
|
|
|
peak = [trackPeak floatValue];
|
|
|
|
}
|
|
|
|
if (useAlbum) {
|
|
|
|
id albumGain = [rgInfo objectForKey:@"replayGainAlbumGain"];
|
|
|
|
id albumPeak = [rgInfo objectForKey:@"replayGainAlbumPeak"];
|
|
|
|
if (albumGain != nil)
|
|
|
|
scale = db_to_scale([albumGain floatValue]);
|
|
|
|
if (albumPeak != nil)
|
|
|
|
peak = [albumPeak floatValue];
|
|
|
|
}
|
|
|
|
if (usePeak) {
|
|
|
|
if (scale * peak > 1.0)
|
|
|
|
scale = 1.0 / peak;
|
|
|
|
}
|
|
|
|
volumeScale = scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf outputFormat:(AudioStreamBasicDescription)outf
|
|
|
|
{
|
|
|
|
//Make the converter
|
|
|
|
OSStatus stat = noErr;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
stopping = NO;
|
|
|
|
convertEntered = NO;
|
|
|
|
ACInputEntered = NO;
|
|
|
|
ACFloatEntered = NO;
|
2007-10-03 20:23:14 +00:00
|
|
|
|
|
|
|
inputFormat = inf;
|
|
|
|
outputFormat = outf;
|
2013-10-07 10:59:04 +00:00
|
|
|
|
|
|
|
floatFormat = inputFormat;
|
|
|
|
floatFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
|
|
|
floatFormat.mBitsPerChannel = 32;
|
|
|
|
floatFormat.mBytesPerFrame = (32/8)*floatFormat.mChannelsPerFrame;
|
|
|
|
floatFormat.mBytesPerPacket = floatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
|
|
|
|
|
2013-10-13 12:14:57 +00:00
|
|
|
floatOffset = 0;
|
|
|
|
floatSize = 0;
|
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
stat = AudioConverterNew( &inputFormat, &floatFormat, &converterFloat );
|
|
|
|
if (stat != noErr)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Error creating converter %i", stat);
|
2013-10-07 10:59:04 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
dmFloatFormat = floatFormat;
|
2021-12-26 06:32:43 +00:00
|
|
|
dmFloatFormat.mChannelsPerFrame = outputFormat.mChannelsPerFrame;
|
|
|
|
dmFloatFormat.mBytesPerFrame = (32/8)*dmFloatFormat.mChannelsPerFrame;
|
|
|
|
dmFloatFormat.mBytesPerPacket = dmFloatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
2021-12-26 06:32:43 +00:00
|
|
|
stat = AudioConverterNew ( &dmFloatFormat, &outputFormat, &converter );
|
2013-10-07 10:59:04 +00:00
|
|
|
if (stat != noErr)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Error creating converter %i", stat);
|
2013-10-07 10:59:04 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
#if 0
|
|
|
|
// These mappings don't do what I want, so avoid them.
|
2013-10-07 07:24:26 +00:00
|
|
|
if (inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2)
|
|
|
|
{
|
|
|
|
SInt32 channelMap[2] = { 0, 1 };
|
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,sizeof(channelMap),channelMap);
|
2013-10-07 07:24:26 +00:00
|
|
|
if (stat != noErr)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Error mapping channels %i", stat);
|
2013-10-07 10:59:04 +00:00
|
|
|
return NO;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-25 23:02:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2021-12-26 06:32:43 +00:00
|
|
|
else if (inputFormat.mChannelsPerFrame == 1 && outputFormat.mChannelsPerFrame > 1)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
2021-12-25 23:02:13 +00:00
|
|
|
SInt32 channelMap[outputFormat.mChannelsPerFrame];
|
|
|
|
|
|
|
|
memset(channelMap, 0, sizeof(channelMap));
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
stat = AudioConverterSetProperty(converter,kAudioConverterChannelMap,(int)sizeof(channelMap),channelMap);
|
2007-10-03 20:23:14 +00:00
|
|
|
if (stat != noErr)
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Error mapping channels %i", stat);
|
2013-10-07 10:59:04 +00:00
|
|
|
return NO;
|
2007-10-03 20:23:14 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-26 06:32:43 +00:00
|
|
|
#endif
|
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
PrintStreamDesc(&inf);
|
|
|
|
PrintStreamDesc(&outf);
|
2013-10-07 10:59:04 +00:00
|
|
|
|
|
|
|
[self refreshVolumeScaling];
|
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
sampleRatio = (float)inputFormat.mSampleRate / (float)outputFormat.mSampleRate;
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2007-10-13 07:09:46 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Decoder dealloc");
|
2013-10-11 03:02:02 +00:00
|
|
|
|
|
|
|
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling"];
|
|
|
|
|
2007-10-13 07:09:46 +00:00
|
|
|
[self cleanUp];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-12 02:55:59 +00:00
|
|
|
- (void)setOutputFormat:(AudioStreamBasicDescription)format
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"SETTING OUTPUT FORMAT!");
|
2007-10-12 02:55:59 +00:00
|
|
|
outputFormat = format;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"FORMAT CHANGED");
|
2007-10-12 02:55:59 +00:00
|
|
|
[self cleanUp];
|
|
|
|
[self setupWithInputFormat:format outputFormat:outputFormat];
|
|
|
|
}
|
|
|
|
|
2013-10-07 10:59:04 +00:00
|
|
|
- (void)setRGInfo:(NSDictionary *)rgi
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Setting ReplayGain info");
|
2013-10-07 10:59:04 +00:00
|
|
|
rgInfo = rgi;
|
|
|
|
[self refreshVolumeScaling];
|
|
|
|
}
|
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
- (void)cleanUp
|
|
|
|
{
|
2021-12-26 12:34:33 +00:00
|
|
|
stopping = YES;
|
|
|
|
while (convertEntered || ACInputEntered || ACFloatEntered)
|
|
|
|
{
|
|
|
|
usleep(500);
|
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
rgInfo = nil;
|
|
|
|
if (converterFloat)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2013-10-07 10:59:04 +00:00
|
|
|
AudioConverterDispose(converterFloat);
|
|
|
|
converterFloat = NULL;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2007-10-12 02:55:59 +00:00
|
|
|
if (converter)
|
|
|
|
{
|
|
|
|
AudioConverterDispose(converter);
|
|
|
|
converter = NULL;
|
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
if (floatBuffer)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2013-10-07 10:59:04 +00:00
|
|
|
free(floatBuffer);
|
|
|
|
floatBuffer = NULL;
|
2021-12-25 23:02:13 +00:00
|
|
|
floatBufferSize = 0;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2007-10-13 07:09:46 +00:00
|
|
|
if (callbackBuffer) {
|
|
|
|
free(callbackBuffer);
|
2008-02-16 02:46:19 +00:00
|
|
|
callbackBuffer = NULL;
|
2021-12-25 23:02:13 +00:00
|
|
|
callbackBufferSize = 0;
|
2007-10-13 07:09:46 +00:00
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
floatOffset = 0;
|
|
|
|
floatSize = 0;
|
2007-10-03 20:23:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|