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"
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
#import <audio/conversion/s16_to_float.h>
|
|
|
|
#import <audio/conversion/s32_to_float.h>
|
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
#import "lpc.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;
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
resampler = NULL;
|
|
|
|
resampler_data = NULL;
|
|
|
|
inputBuffer = NULL;
|
|
|
|
inputBufferSize = 0;
|
2013-10-13 12:14:57 +00:00
|
|
|
floatBuffer = NULL;
|
2021-12-25 23:02:13 +00:00
|
|
|
floatBufferSize = 0;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
|
|
|
stopping = NO;
|
|
|
|
convertEntered = NO;
|
2022-01-13 07:13:00 +00:00
|
|
|
paused = NO;
|
|
|
|
outputFormatChanged = NO;
|
2022-01-11 12:00:34 +00:00
|
|
|
|
|
|
|
skipResampler = NO;
|
|
|
|
|
|
|
|
latencyStarted = -1;
|
|
|
|
latencyEaten = 0;
|
|
|
|
latencyPostfill = NO;
|
2022-01-14 03:43:18 +00:00
|
|
|
|
|
|
|
refillNode = nil;
|
|
|
|
originalPreviousNode = nil;
|
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];
|
2022-01-11 12:00:34 +00:00
|
|
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
2013-10-11 03:02:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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*/
|
|
|
|
{
|
2021-12-28 08:10:44 +00:00
|
|
|
{0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F},
|
|
|
|
{0.3943F,0.2277F},{0.2277F,0.3943F}
|
2013-10-07 07:24:26 +00:00
|
|
|
},
|
|
|
|
/*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}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void downmix_to_stereo(float * buffer, int channels, size_t count)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2021-12-25 23:02:13 +00:00
|
|
|
if (channels >= 3 && channels <= 8)
|
2022-01-11 12:00:34 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void downmix_to_mono(float * buffer, int channels, size_t count)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
|
|
|
if (channels >= 3 && channels <= 8)
|
|
|
|
{
|
|
|
|
downmix_to_stereo(buffer, channels, count);
|
|
|
|
channels = 2;
|
|
|
|
}
|
|
|
|
float invchannels = 1.0 / (float)channels;
|
2022-01-11 12:00:34 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
|
|
|
float sample = 0;
|
|
|
|
for (int j = 0; j < channels; ++j)
|
|
|
|
{
|
|
|
|
sample += buffer[i * channels + j];
|
|
|
|
}
|
|
|
|
buffer[i] = sample * invchannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void upmix(float * buffer, int inchannels, int outchannels, size_t count)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
for (ssize_t i = count - 1; i >= 0; --i)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
2021-12-28 08:09:57 +00:00
|
|
|
if (inchannels == 1 && outchannels == 2)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
2021-12-28 08:09:57 +00:00
|
|
|
// upmix mono to stereo
|
2021-12-26 06:32:43 +00:00
|
|
|
float sample = buffer[i];
|
2021-12-28 08:09:57 +00:00
|
|
|
buffer[i * 2 + 0] = sample;
|
|
|
|
buffer[i * 2 + 1] = sample;
|
|
|
|
}
|
|
|
|
else if (inchannels == 1 && outchannels == 4)
|
|
|
|
{
|
|
|
|
// upmix mono to quad
|
|
|
|
float sample = buffer[i];
|
|
|
|
buffer[i * 4 + 0] = sample;
|
|
|
|
buffer[i * 4 + 1] = sample;
|
|
|
|
buffer[i * 4 + 2] = 0;
|
|
|
|
buffer[i * 4 + 3] = 0;
|
|
|
|
}
|
|
|
|
else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5))
|
|
|
|
{
|
|
|
|
// upmix mono to center channel
|
|
|
|
float sample = buffer[i];
|
|
|
|
buffer[i * outchannels + 2] = sample;
|
2021-12-26 06:32:43 +00:00
|
|
|
for (int j = 0; j < 2; ++j)
|
|
|
|
{
|
2021-12-28 08:09:57 +00:00
|
|
|
buffer[i * outchannels + j] = 0;
|
2021-12-26 06:32:43 +00:00
|
|
|
}
|
2021-12-28 08:09:57 +00:00
|
|
|
for (int j = 3; j < outchannels; ++j)
|
2021-12-26 06:32:43 +00:00
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = 0;
|
|
|
|
}
|
|
|
|
}
|
2021-12-28 23:39:23 +00:00
|
|
|
else if (inchannels == 4 && outchannels >= 5)
|
|
|
|
{
|
|
|
|
float fl = buffer[i * 4 + 0];
|
|
|
|
float fr = buffer[i * 4 + 1];
|
|
|
|
float bl = buffer[i * 4 + 2];
|
|
|
|
float br = buffer[i * 4 + 3];
|
|
|
|
const int skipclfe = (outchannels == 5) ? 1 : 2;
|
|
|
|
buffer[i * outchannels + 0] = fl;
|
|
|
|
buffer[i * outchannels + 1] = fr;
|
|
|
|
buffer[i * outchannels + skipclfe + 2] = bl;
|
|
|
|
buffer[i * outchannels + skipclfe + 3] = br;
|
|
|
|
for (int j = 0; j < skipclfe; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + 2 + j] = 0;
|
|
|
|
}
|
|
|
|
for (int j = 4 + skipclfe; j < outchannels; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (inchannels == 5 && outchannels >= 6)
|
|
|
|
{
|
|
|
|
float fl = buffer[i * 5 + 0];
|
|
|
|
float fr = buffer[i * 5 + 1];
|
|
|
|
float c = buffer[i * 5 + 2];
|
|
|
|
float bl = buffer[i * 5 + 3];
|
|
|
|
float br = buffer[i * 5 + 4];
|
|
|
|
buffer[i * outchannels + 0] = fl;
|
|
|
|
buffer[i * outchannels + 1] = fr;
|
|
|
|
buffer[i * outchannels + 2] = c;
|
|
|
|
buffer[i * outchannels + 3] = 0;
|
|
|
|
buffer[i * outchannels + 4] = bl;
|
|
|
|
buffer[i * outchannels + 5] = br;
|
|
|
|
for (int j = 6; j < outchannels; ++j)
|
|
|
|
{
|
|
|
|
buffer[i * outchannels + j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (inchannels == 7 && outchannels == 8)
|
|
|
|
{
|
|
|
|
float fl = buffer[i * 7 + 0];
|
|
|
|
float fr = buffer[i * 7 + 1];
|
|
|
|
float c = buffer[i * 7 + 2];
|
|
|
|
float lfe = buffer[i * 7 + 3];
|
|
|
|
float sl = buffer[i * 7 + 4];
|
|
|
|
float sr = buffer[i * 7 + 5];
|
|
|
|
float bc = buffer[i * 7 + 6];
|
|
|
|
buffer[i * 8 + 0] = fl;
|
|
|
|
buffer[i * 8 + 1] = fr;
|
|
|
|
buffer[i * 8 + 2] = c;
|
|
|
|
buffer[i * 8 + 3] = lfe;
|
|
|
|
buffer[i * 8 + 4] = bc;
|
|
|
|
buffer[i * 8 + 5] = bc;
|
|
|
|
buffer[i * 8 + 6] = sl;
|
|
|
|
buffer[i * 8 + 7] = sr;
|
|
|
|
}
|
2021-12-26 06:32:43 +00:00
|
|
|
else
|
|
|
|
{
|
2021-12-28 08:09:57 +00:00
|
|
|
// upmix N channels to N channels plus silence the empty channels
|
2021-12-26 06:32:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 15:06:40 +00:00
|
|
|
void scale_by_volume(float * buffer, size_t count, float volume)
|
2013-10-07 10:59:04 +00:00
|
|
|
{
|
|
|
|
if ( volume != 1.0 )
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2013-10-07 10:59:04 +00:00
|
|
|
buffer[i] *= volume;
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_u8_to_s16(int16_t *output, uint8_t *input, size_t count)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
uint16_t sample = (input[i] << 8) | input[i];
|
|
|
|
sample ^= 0x8080;
|
|
|
|
output[i] = (int16_t)(sample);
|
|
|
|
}
|
|
|
|
}
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_s8_to_s16(int16_t *output, uint8_t *input, size_t count)
|
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
uint16_t sample = (input[i] << 8) | input[i];
|
|
|
|
output[i] = (int16_t)(sample);
|
|
|
|
}
|
|
|
|
}
|
2021-12-26 07:41:45 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_u16_to_s16(int16_t *buffer, size_t count)
|
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
buffer[i] ^= 0x8000;
|
|
|
|
}
|
2007-10-03 20:23:14 +00:00
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_s24_to_s32(int32_t *output, uint8_t *input, size_t count)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
|
|
|
output[i] = sample;
|
|
|
|
}
|
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_u24_to_s32(int32_t *output, uint8_t *input, size_t count)
|
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2021-12-25 23:02:13 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
|
|
|
output[i] = sample ^ 0x80000000;
|
2021-12-25 23:02:13 +00:00
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void convert_u32_to_s32(int32_t *buffer, size_t count)
|
|
|
|
{
|
2022-01-12 01:09:06 +00:00
|
|
|
for (size_t i = 0; i < count; ++i)
|
2021-12-26 07:41:45 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
buffer[i] ^= 0x80000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-12 01:09:06 +00:00
|
|
|
static void convert_f64_to_f32(float *output, double *input, size_t count)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
output[i] = (float)(input[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
uint8_t temp;
|
|
|
|
bitsPerSample = (bitsPerSample + 7) / 8;
|
|
|
|
switch (bitsPerSample) {
|
|
|
|
case 2:
|
|
|
|
for (i = 0; i < bytes; i += 2)
|
|
|
|
{
|
|
|
|
temp = buffer[1];
|
|
|
|
buffer[1] = buffer[0];
|
|
|
|
buffer[0] = temp;
|
|
|
|
buffer += 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
for (i = 0; i < bytes; i += 3)
|
|
|
|
{
|
|
|
|
temp = buffer[2];
|
|
|
|
buffer[2] = buffer[0];
|
|
|
|
buffer[0] = temp;
|
|
|
|
buffer += 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
for (i = 0; i < bytes; i += 4)
|
|
|
|
{
|
|
|
|
temp = buffer[3];
|
|
|
|
buffer[3] = buffer[0];
|
|
|
|
buffer[0] = temp;
|
|
|
|
temp = buffer[2];
|
|
|
|
buffer[2] = buffer[1];
|
|
|
|
buffer[1] = temp;
|
|
|
|
buffer += 4;
|
|
|
|
}
|
|
|
|
break;
|
2022-01-12 01:09:06 +00:00
|
|
|
|
|
|
|
case 8:
|
|
|
|
for (i = 0; i < bytes; i += 8)
|
|
|
|
{
|
|
|
|
temp = buffer[7];
|
|
|
|
buffer[7] = buffer[0];
|
|
|
|
buffer[0] = temp;
|
|
|
|
temp = buffer[6];
|
|
|
|
buffer[6] = buffer[1];
|
|
|
|
buffer[1] = temp;
|
|
|
|
temp = buffer[5];
|
|
|
|
buffer[5] = buffer[2];
|
|
|
|
buffer[2] = temp;
|
|
|
|
temp = buffer[4];
|
|
|
|
buffer[4] = buffer[3];
|
|
|
|
buffer[3] = temp;
|
|
|
|
buffer += 8;
|
|
|
|
}
|
|
|
|
break;
|
2021-12-26 07:41:45 +00:00
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
static const int extrapolate_order = 16;
|
|
|
|
|
|
|
|
static void extrapolate(float *buffer, size_t channels, size_t frameSize, size_t size, BOOL backward)
|
|
|
|
{
|
|
|
|
const size_t delta = (backward ? -1 : 1) * channels;
|
|
|
|
|
|
|
|
float lpc[extrapolate_order];
|
|
|
|
float work[frameSize];
|
|
|
|
|
|
|
|
for (size_t ch = 0; ch < channels; ch++)
|
|
|
|
{
|
|
|
|
if (frameSize - size > extrapolate_order * 2)
|
|
|
|
{
|
|
|
|
float *chPcmBuf = buffer + ch + (backward ? frameSize : -1) * channels;
|
|
|
|
for (size_t i = 0; i < frameSize; i++) work[i] = *(chPcmBuf += delta);
|
|
|
|
|
|
|
|
vorbis_lpc_from_data(work, lpc, (int)(frameSize - size), extrapolate_order);
|
|
|
|
|
|
|
|
vorbis_lpc_predict(lpc, work + frameSize - size - extrapolate_order, extrapolate_order, work + frameSize - size, size);
|
|
|
|
|
|
|
|
chPcmBuf = buffer + ch + (backward ? frameSize : -1) * channels;
|
|
|
|
for (size_t i = 0; i < frameSize; i++) *(chPcmBuf += delta) = work[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-01-12 11:36:27 +00:00
|
|
|
// Removed endOfStream check from here, since we want to be able to flush the converter
|
|
|
|
// when the end of stream is reached. Convert function instead processes what it can,
|
|
|
|
// and returns 0 samples when it has nothing more to process at the end of stream.
|
|
|
|
while ([self shouldContinue] == YES)
|
2007-10-03 20:23:14 +00:00
|
|
|
{
|
2007-10-20 03:24:27 +00:00
|
|
|
int amountConverted = [self convert:writeBuf amount:CHUNK_SIZE];
|
2022-01-12 10:55:11 +00:00
|
|
|
if (!amountConverted)
|
|
|
|
{
|
2022-01-13 07:13:00 +00:00
|
|
|
if (paused)
|
2022-01-12 10:55:11 +00:00
|
|
|
{
|
2022-01-13 07:13:00 +00:00
|
|
|
while (paused)
|
|
|
|
usleep(500);
|
|
|
|
continue;
|
2022-01-12 10:55:11 +00:00
|
|
|
}
|
2022-01-14 03:43:18 +00:00
|
|
|
else if (refillNode)
|
|
|
|
{
|
|
|
|
// refill node just ended, file resumes
|
|
|
|
[self setPreviousNode:originalPreviousNode];
|
|
|
|
[self setEndOfStream:NO];
|
|
|
|
[self setShouldContinue:YES];
|
|
|
|
refillNode = nil;
|
|
|
|
[self cleanUp];
|
|
|
|
[self setupWithInputFormat:rememberedInputFormat outputFormat:outputFormat];
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-12 10:55:11 +00:00
|
|
|
else break;
|
|
|
|
}
|
2007-10-03 20:23:14 +00:00
|
|
|
[self writeData:writeBuf amount:amountConverted];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)convert:(void *)dest amount:(int)amount
|
|
|
|
{
|
2021-12-25 23:02:13 +00:00
|
|
|
UInt32 ioNumberPackets;
|
|
|
|
int amountReadFromFC;
|
2013-10-07 07:24:26 +00:00
|
|
|
int amountRead = 0;
|
2022-01-12 02:19:30 +00:00
|
|
|
int extrapolateStart = 0;
|
|
|
|
int extrapolateEnd = 0;
|
|
|
|
size_t amountToSkip = 0;
|
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
convertEntered = YES;
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
tryagain:
|
2021-12-28 01:04:16 +00:00
|
|
|
if (stopping || [self shouldContinue] == NO)
|
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;
|
2022-01-11 12:00:34 +00:00
|
|
|
|
2022-01-11 15:06:40 +00:00
|
|
|
if (floatOffset == floatSize) // skip this step if there's still float buffered
|
2022-01-11 12:00:34 +00:00
|
|
|
while (inpOffset == inpSize) {
|
|
|
|
size_t samplesRead = 0;
|
|
|
|
|
|
|
|
// Approximately the most we want on input
|
|
|
|
ioNumberPackets = (amount - amountRead) / outputFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
|
|
|
if (!inputBuffer || inputBufferSize < newSize)
|
|
|
|
inputBuffer = realloc( inputBuffer, inputBufferSize = newSize * 3 );
|
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
// Pad end of track with floats. For simplicity, pad start in track
|
|
|
|
// native format.
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
if (stopping || [self shouldContinue] == NO || [self endOfStream] == YES)
|
|
|
|
{
|
|
|
|
if (!skipResampler && !latencyPostfill)
|
|
|
|
{
|
|
|
|
ioNumberPackets = (int)resampler->latency(resampler_data);
|
|
|
|
newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
2022-01-12 03:37:17 +00:00
|
|
|
newSize += inpSize;
|
2022-01-11 12:00:34 +00:00
|
|
|
if (!inputBuffer || inputBufferSize < newSize)
|
2022-01-12 03:37:17 +00:00
|
|
|
inputBuffer = realloc( inputBuffer, inputBufferSize = newSize * 3);
|
2022-01-11 12:00:34 +00:00
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
extrapolateEnd = ioNumberPackets;
|
|
|
|
|
|
|
|
// Extrapolate end samples
|
2022-01-14 07:03:53 +00:00
|
|
|
if ( inpSize < sizeof(floatConvertedLast))
|
2022-01-12 03:37:17 +00:00
|
|
|
{
|
2022-01-14 07:03:53 +00:00
|
|
|
size_t inpTotal = newSize + floatConvertedSize;
|
|
|
|
if (inpTotal > sizeof(floatConvertedLast))
|
|
|
|
inpTotal = sizeof(floatConvertedLast);
|
|
|
|
|
|
|
|
if (inpTotal - newSize < floatConvertedSize)
|
|
|
|
{
|
|
|
|
memmove(floatConvertedLast, ((uint8_t*)floatConvertedLast) + newSize, inpTotal - newSize);
|
|
|
|
floatConvertedSize = inpTotal - newSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(((uint8_t*)floatConvertedLast) + floatConvertedSize, inputBuffer, inpSize);
|
|
|
|
|
|
|
|
extrapolate( floatConvertedLast, floatFormat.mChannelsPerFrame, inpTotal / floatFormat.mBytesPerPacket, extrapolateEnd, NO );
|
|
|
|
|
|
|
|
newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
memcpy( inputBuffer, ((uint8_t*)floatConvertedLast) + inpTotal - newSize, newSize );
|
|
|
|
|
|
|
|
inpSize = newSize;
|
|
|
|
inpOffset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extrapolate( inputBuffer, floatFormat.mChannelsPerFrame, newSize / floatFormat.mBytesPerPacket, extrapolateEnd, NO );
|
2022-01-12 02:19:30 +00:00
|
|
|
|
2022-01-12 03:37:17 +00:00
|
|
|
inpOffset = inpSize;
|
2022-01-14 07:03:53 +00:00
|
|
|
inpSize = newSize;
|
2022-01-12 03:37:17 +00:00
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
latencyPostfill = YES;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
convertEntered = NO;
|
|
|
|
return amountRead;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t amountToWrite = ioNumberPackets * inputFormat.mBytesPerPacket;
|
2022-01-13 07:13:00 +00:00
|
|
|
amountToSkip = 0;
|
2022-01-11 12:00:34 +00:00
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
|
|
|
BOOL isUnsigned = !isFloat && !(inputFormat.mFormatFlags & kAudioFormatFlagIsSignedInteger);
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
if (!skipResampler)
|
|
|
|
{
|
|
|
|
if (latencyStarted < 0)
|
|
|
|
{
|
|
|
|
latencyStarted = resampler->latency(resampler_data);
|
2022-01-12 02:19:30 +00:00
|
|
|
extrapolateStart = (int)latencyStarted;
|
2022-01-11 12:00:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (latencyStarted)
|
|
|
|
{
|
|
|
|
size_t latencyToWrite = latencyStarted * inputFormat.mBytesPerPacket;
|
|
|
|
if (latencyToWrite > amountToWrite)
|
|
|
|
latencyToWrite = amountToWrite;
|
2022-01-12 02:19:30 +00:00
|
|
|
|
|
|
|
if (isUnsigned)
|
2022-01-11 12:00:34 +00:00
|
|
|
memset(inputBuffer, 0x80, latencyToWrite);
|
|
|
|
else
|
|
|
|
memset(inputBuffer, 0, latencyToWrite);
|
|
|
|
|
|
|
|
amountToSkip = latencyToWrite;
|
|
|
|
amountToWrite -= amountToSkip;
|
|
|
|
|
|
|
|
latencyEaten = latencyStarted * sampleRatio;
|
|
|
|
|
|
|
|
latencyStarted -= latencyToWrite / inputFormat.mBytesPerPacket;
|
|
|
|
}
|
|
|
|
}
|
2021-12-25 23:02:13 +00:00
|
|
|
|
2022-01-13 07:13:00 +00:00
|
|
|
size_t bytesReadFromInput = 0;
|
|
|
|
|
|
|
|
while (bytesReadFromInput < amountToWrite && !stopping && [self shouldContinue] == YES && [self endOfStream] == NO)
|
2022-01-12 02:19:30 +00:00
|
|
|
{
|
2022-01-13 07:13:00 +00:00
|
|
|
size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)];
|
|
|
|
bytesReadFromInput += bytesRead;
|
|
|
|
if (!bytesRead)
|
2022-01-14 03:43:18 +00:00
|
|
|
{
|
|
|
|
if (refillNode)
|
|
|
|
[self setEndOfStream:YES];
|
|
|
|
else
|
|
|
|
usleep(500);
|
|
|
|
}
|
2022-01-12 02:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bytesReadFromInput += amountToSkip;
|
2022-01-12 01:09:06 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
if (bytesReadFromInput &&
|
|
|
|
(inputFormat.mFormatFlags & kAudioFormatFlagIsBigEndian))
|
|
|
|
{
|
|
|
|
// Time for endian swap!
|
|
|
|
convert_be_to_le(inputBuffer, inputFormat.mBitsPerChannel, bytesReadFromInput);
|
|
|
|
}
|
|
|
|
|
2022-01-12 01:09:06 +00:00
|
|
|
if (bytesReadFromInput && isFloat && inputFormat.mBitsPerChannel == 64)
|
|
|
|
{
|
|
|
|
// Time for precision loss from weird inputs
|
|
|
|
samplesRead = bytesReadFromInput / sizeof(double);
|
|
|
|
convert_f64_to_f32(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead);
|
|
|
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float));
|
|
|
|
bytesReadFromInput = samplesRead * sizeof(float);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytesReadFromInput && !isFloat)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
|
|
|
if (bitsPerSample <= 8) {
|
|
|
|
samplesRead = bytesReadFromInput;
|
2022-01-12 03:37:47 +00:00
|
|
|
if (!isUnsigned)
|
2022-01-11 12:00:34 +00:00
|
|
|
convert_s8_to_s16(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead);
|
|
|
|
else
|
|
|
|
convert_u8_to_s16(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead);
|
|
|
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * 2);
|
|
|
|
bitsPerSample = 16;
|
|
|
|
bytesReadFromInput = samplesRead * 2;
|
|
|
|
isUnsigned = NO;
|
|
|
|
}
|
|
|
|
if (bitsPerSample <= 16) {
|
|
|
|
samplesRead = bytesReadFromInput / 2;
|
|
|
|
if (isUnsigned)
|
|
|
|
convert_u16_to_s16(inputBuffer, samplesRead);
|
|
|
|
convert_s16_to_float(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead, 1.0);
|
|
|
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float));
|
|
|
|
bitsPerSample = 32;
|
|
|
|
bytesReadFromInput = samplesRead * sizeof(float);
|
|
|
|
isUnsigned = NO;
|
|
|
|
isFloat = YES;
|
|
|
|
}
|
|
|
|
else if (bitsPerSample <= 24) {
|
|
|
|
samplesRead = bytesReadFromInput / 3;
|
|
|
|
if (isUnsigned)
|
|
|
|
convert_u24_to_s32(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead);
|
|
|
|
else
|
|
|
|
convert_s24_to_s32(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead);
|
|
|
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * 4);
|
|
|
|
bitsPerSample = 32;
|
|
|
|
bytesReadFromInput = samplesRead * 4;
|
|
|
|
isUnsigned = NO;
|
|
|
|
}
|
|
|
|
if (!isFloat && bitsPerSample <= 32) {
|
|
|
|
samplesRead = bytesReadFromInput / 4;
|
|
|
|
if (isUnsigned)
|
|
|
|
convert_u32_to_s32(inputBuffer, samplesRead);
|
|
|
|
convert_s32_to_float(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead, 1.0);
|
|
|
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float));
|
|
|
|
bitsPerSample = 32;
|
|
|
|
bytesReadFromInput = samplesRead * sizeof(float);
|
|
|
|
isUnsigned = NO;
|
|
|
|
isFloat = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-12 02:19:30 +00:00
|
|
|
// Extrapolate start
|
|
|
|
if (extrapolateStart)
|
2022-01-13 07:13:00 +00:00
|
|
|
{
|
2022-01-12 02:19:30 +00:00
|
|
|
extrapolate( inputBuffer, floatFormat.mChannelsPerFrame, bytesReadFromInput / floatFormat.mBytesPerPacket, extrapolateStart, YES);
|
|
|
|
extrapolateStart = 0;
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
// Input now contains bytesReadFromInput worth of floats, in the input sample rate
|
|
|
|
inpSize = bytesReadFromInput;
|
|
|
|
inpOffset = 0;
|
2022-01-14 07:03:53 +00:00
|
|
|
|
|
|
|
// Preserve last samples, for end extrapolator, if needed
|
|
|
|
size_t preserved = inpSize;
|
|
|
|
if (preserved > sizeof(floatConvertedLast))
|
|
|
|
preserved = sizeof(floatConvertedLast);
|
|
|
|
memcpy(floatConvertedLast, inputBuffer + inpSize - preserved, preserved);
|
|
|
|
floatConvertedSize = preserved;
|
2022-01-11 12:00:34 +00:00
|
|
|
}
|
|
|
|
|
2022-01-14 03:43:18 +00:00
|
|
|
if (inpOffset != inpSize && floatOffset == floatSize)
|
2022-01-11 12:00:34 +00:00
|
|
|
{
|
|
|
|
struct resampler_data src_data;
|
|
|
|
|
|
|
|
size_t inputSamples = (inpSize - inpOffset) / floatFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
ioNumberPackets = (UInt32)inputSamples;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
|
|
|
ioNumberPackets = (UInt32)((float)ioNumberPackets * sampleRatio);
|
|
|
|
ioNumberPackets = (ioNumberPackets + 255) & ~255;
|
|
|
|
|
|
|
|
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)
|
2022-01-11 12:00:34 +00:00
|
|
|
floatBuffer = realloc( floatBuffer, floatBufferSize = newSize * 3 );
|
|
|
|
|
2021-12-26 07:41:45 +00:00
|
|
|
if (stopping)
|
|
|
|
{
|
|
|
|
convertEntered = NO;
|
|
|
|
return 0;
|
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
|
|
|
|
src_data.data_out = floatBuffer;
|
|
|
|
src_data.output_frames = 0;
|
|
|
|
|
|
|
|
src_data.data_in = (float*)(((uint8_t*)inputBuffer) + inpOffset);
|
|
|
|
src_data.input_frames = inputSamples;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
src_data.ratio = sampleRatio;
|
|
|
|
|
|
|
|
if (!skipResampler)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
resampler->process(resampler_data, &src_data);
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
else
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
memcpy(src_data.data_out, src_data.data_in, inputSamples * floatFormat.mBytesPerPacket);
|
|
|
|
src_data.output_frames = inputSamples;
|
|
|
|
}
|
|
|
|
|
|
|
|
inpOffset += inputSamples * floatFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
if (!skipResampler && latencyEaten)
|
|
|
|
{
|
|
|
|
if (src_data.output_frames > latencyEaten)
|
|
|
|
{
|
|
|
|
src_data.output_frames -= latencyEaten;
|
|
|
|
memmove(src_data.data_out, src_data.data_out + latencyEaten * inputFormat.mChannelsPerFrame, src_data.output_frames * floatFormat.mBytesPerPacket);
|
|
|
|
latencyEaten = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
latencyEaten -= src_data.output_frames;
|
|
|
|
src_data.output_frames = 0;
|
|
|
|
}
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
amountReadFromFC = (int)(src_data.output_frames * floatFormat.mBytesPerPacket);
|
|
|
|
|
|
|
|
scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale);
|
|
|
|
|
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
|
|
|
floatSize = amountReadFromFC;
|
2013-10-07 10:59:04 +00:00
|
|
|
floatOffset = 0;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
2021-12-26 07:41:45 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
if (floatOffset == floatSize)
|
|
|
|
goto tryagain;
|
2021-12-26 07:41:45 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
ioNumberPackets = (amount - amountRead);
|
|
|
|
if (ioNumberPackets > (floatSize - floatOffset))
|
|
|
|
ioNumberPackets = (UInt32)(floatSize - floatOffset);
|
|
|
|
|
|
|
|
memcpy(dest + amountRead, floatBuffer + floatOffset, ioNumberPackets);
|
|
|
|
|
|
|
|
floatOffset += ioNumberPackets;
|
|
|
|
amountRead += ioNumberPackets;
|
|
|
|
|
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];
|
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
else if ([keyPath isEqual:@"values.outputResampling"]) {
|
|
|
|
// Reset resampler
|
|
|
|
if (resampler && resampler_data)
|
|
|
|
[self inputFormatDidChange:inputFormat];
|
|
|
|
}
|
2013-10-07 10:59:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
inputFormat = inf;
|
|
|
|
outputFormat = outf;
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2022-01-12 01:09:06 +00:00
|
|
|
// These are the only sample formats we support translating
|
|
|
|
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
|
|
|
if ((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32))
|
|
|
|
|| (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64)))
|
|
|
|
return NO;
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
// These are really placeholders, as we're doing everything internally now
|
|
|
|
|
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;
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
inpOffset = 0;
|
|
|
|
inpSize = 0;
|
|
|
|
|
2013-10-13 12:14:57 +00:00
|
|
|
floatOffset = 0;
|
|
|
|
floatSize = 0;
|
|
|
|
|
2022-01-14 07:03:53 +00:00
|
|
|
floatConvertedSize = 0;
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
// This is a post resampler, post-down/upmix format
|
2013-10-07 10:59:04 +00:00
|
|
|
|
2021-12-25 23:02:13 +00:00
|
|
|
dmFloatFormat = floatFormat;
|
2022-01-11 12:00:34 +00:00
|
|
|
dmFloatFormat.mSampleRate = outputFormat.mSampleRate;
|
2021-12-26 06:32:43 +00:00
|
|
|
dmFloatFormat.mChannelsPerFrame = outputFormat.mChannelsPerFrame;
|
|
|
|
dmFloatFormat.mBytesPerFrame = (32/8)*dmFloatFormat.mChannelsPerFrame;
|
|
|
|
dmFloatFormat.mBytesPerPacket = dmFloatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
|
2007-10-03 20:23:14 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
convert_s16_to_float_init_simd();
|
|
|
|
convert_s32_to_float_init_simd();
|
|
|
|
|
|
|
|
skipResampler = outputFormat.mSampleRate == inputFormat.mSampleRate;
|
|
|
|
|
|
|
|
sampleRatio = (double)outputFormat.mSampleRate / (double)inputFormat.mSampleRate;
|
|
|
|
|
|
|
|
if (!skipResampler)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2022-01-11 12:00:34 +00:00
|
|
|
enum resampler_quality quality = RESAMPLER_QUALITY_DONTCARE;
|
2013-10-07 07:24:26 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"outputResampling"];
|
|
|
|
if ([resampling isEqualToString:@"lowest"])
|
|
|
|
quality = RESAMPLER_QUALITY_LOWEST;
|
|
|
|
else if ([resampling isEqualToString:@"lower"])
|
|
|
|
quality = RESAMPLER_QUALITY_LOWER;
|
|
|
|
else if ([resampling isEqualToString:@"normal"])
|
|
|
|
quality = RESAMPLER_QUALITY_NORMAL;
|
|
|
|
else if ([resampling isEqualToString:@"higher"])
|
|
|
|
quality = RESAMPLER_QUALITY_HIGHER;
|
|
|
|
else if ([resampling isEqualToString:@"highest"])
|
|
|
|
quality = RESAMPLER_QUALITY_HIGHEST;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
if (!retro_resampler_realloc(&resampler_data, &resampler, "sinc", quality, inputFormat.mChannelsPerFrame, sampleRatio))
|
2021-12-25 23:02:13 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
|
|
|
|
latencyStarted = -1;
|
|
|
|
latencyEaten = 0;
|
|
|
|
latencyPostfill = NO;
|
2021-12-25 23:02:13 +00:00
|
|
|
}
|
2021-12-26 06:32:43 +00:00
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
PrintStreamDesc(&inf);
|
|
|
|
PrintStreamDesc(&outf);
|
2013-10-07 10:59:04 +00:00
|
|
|
|
|
|
|
[self refreshVolumeScaling];
|
|
|
|
|
2022-01-11 12:00:34 +00:00
|
|
|
// Move this here so process call isn't running the resampler until it's allocated
|
|
|
|
stopping = NO;
|
|
|
|
convertEntered = NO;
|
2022-01-13 07:13:00 +00:00
|
|
|
paused = NO;
|
|
|
|
outputFormatChanged = NO;
|
2021-12-25 23:02:13 +00:00
|
|
|
|
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"];
|
2022-01-11 12:00:34 +00:00
|
|
|
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputResampling"];
|
2013-10-11 03:02:02 +00:00
|
|
|
|
2022-01-13 07:13:00 +00:00
|
|
|
paused = NO;
|
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!");
|
2022-01-14 03:43:18 +00:00
|
|
|
previousOutputFormat = outputFormat;
|
2007-10-12 02:55:59 +00:00
|
|
|
outputFormat = format;
|
2022-01-13 07:13:00 +00:00
|
|
|
outputFormatChanged = YES;
|
2007-10-12 02:55:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"FORMAT CHANGED");
|
2022-01-13 07:13:00 +00:00
|
|
|
paused = YES;
|
2007-10-12 02:55:59 +00:00
|
|
|
[self cleanUp];
|
2022-01-14 03:43:18 +00:00
|
|
|
if (outputFormatChanged && ![buffer isEmpty])
|
|
|
|
{
|
|
|
|
// Transfer previously buffered data, remember input format
|
|
|
|
rememberedInputFormat = format;
|
|
|
|
originalPreviousNode = previousNode;
|
|
|
|
refillNode = [[RefillNode alloc] initWithController:controller previous:nil];
|
|
|
|
[self setPreviousNode:refillNode];
|
|
|
|
|
|
|
|
int dataRead = 0;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
void * ptr;
|
|
|
|
dataRead = [buffer lengthAvailableToReadReturningPointer:&ptr];
|
|
|
|
if (dataRead) {
|
|
|
|
[refillNode writeData:(float*)ptr floatCount:dataRead / sizeof(float)];
|
|
|
|
[buffer didReadLength:dataRead];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat];
|
|
|
|
}
|
|
|
|
else
|
2022-01-13 07:13:00 +00:00
|
|
|
{
|
2022-01-14 03:43:18 +00:00
|
|
|
[self setupWithInputFormat:format outputFormat:outputFormat];
|
2022-01-13 07:13:00 +00:00
|
|
|
}
|
2007-10-12 02:55:59 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2022-01-11 12:00:34 +00:00
|
|
|
while (convertEntered)
|
2021-12-26 12:34:33 +00:00
|
|
|
{
|
|
|
|
usleep(500);
|
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
if (resampler && resampler_data)
|
2013-10-07 07:24:26 +00:00
|
|
|
{
|
2022-01-13 02:45:43 +00:00
|
|
|
resampler->free(resampler, resampler_data);
|
2022-01-11 12:00:34 +00:00
|
|
|
resampler = NULL;
|
|
|
|
resampler_data = NULL;
|
2013-10-07 07:24:26 +00:00
|
|
|
}
|
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
|
|
|
}
|
2022-01-11 12:00:34 +00:00
|
|
|
if (inputBuffer) {
|
|
|
|
free(inputBuffer);
|
|
|
|
inputBuffer = NULL;
|
|
|
|
inputBufferSize = 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
|