[Audio Processing] Move float32 converter
Move the Float32 converter to a different location, for any future plans to support decoding audio files to common data for any other purpose. Signed-off-by: Christopher Snowhill <kode54@gmail.com>xcode15
parent
1713e0df7c
commit
34884d825a
|
@ -67,6 +67,7 @@ enum {
|
||||||
uint32_t channelConfig;
|
uint32_t channelConfig;
|
||||||
BOOL formatAssigned;
|
BOOL formatAssigned;
|
||||||
BOOL lossless;
|
BOOL lossless;
|
||||||
|
BOOL hdcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property AudioStreamBasicDescription format;
|
@property AudioStreamBasicDescription format;
|
||||||
|
@ -93,6 +94,9 @@ enum {
|
||||||
|
|
||||||
- (double)duration;
|
- (double)duration;
|
||||||
|
|
||||||
|
- (BOOL)isHDCD;
|
||||||
|
- (void)setHDCD;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
chunkData = [[NSMutableData alloc] init];
|
chunkData = [[NSMutableData alloc] init];
|
||||||
formatAssigned = NO;
|
formatAssigned = NO;
|
||||||
lossless = NO;
|
lossless = NO;
|
||||||
|
hdcd = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -189,4 +190,12 @@ static const uint32_t AudioChannelConfigTable[] = {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isHDCD {
|
||||||
|
return hdcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setHDCD {
|
||||||
|
hdcd = YES;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
#define DSD_DECIMATE 1
|
||||||
|
|
||||||
@interface ChunkList : NSObject {
|
@interface ChunkList : NSObject {
|
||||||
NSMutableArray<AudioChunk *> *chunkList;
|
NSMutableArray<AudioChunk *> *chunkList;
|
||||||
double listDuration;
|
double listDuration;
|
||||||
|
@ -23,6 +25,26 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
BOOL inRemover;
|
BOOL inRemover;
|
||||||
BOOL inPeeker;
|
BOOL inPeeker;
|
||||||
BOOL stopping;
|
BOOL stopping;
|
||||||
|
|
||||||
|
// For format converter
|
||||||
|
void *inputBuffer;
|
||||||
|
size_t inputBufferSize;
|
||||||
|
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
void **dsd2pcm;
|
||||||
|
size_t dsd2pcmCount;
|
||||||
|
int dsd2pcmLatency;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *hdcd_decoder;
|
||||||
|
|
||||||
|
BOOL formatRead;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription inputFormat;
|
||||||
|
AudioStreamBasicDescription floatFormat;
|
||||||
|
|
||||||
|
uint32_t inputChannelConfig;
|
||||||
|
BOOL inputLossless;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property(readonly) double listDuration;
|
@property(readonly) double listDuration;
|
||||||
|
@ -38,6 +60,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (void)addChunk:(AudioChunk *)chunk;
|
- (void)addChunk:(AudioChunk *)chunk;
|
||||||
- (AudioChunk *)removeSamples:(size_t)maxFrameCount;
|
- (AudioChunk *)removeSamples:(size_t)maxFrameCount;
|
||||||
|
|
||||||
|
- (AudioChunk *)removeSamplesAsFloat32:(size_t)maxFrameCount;
|
||||||
|
|
||||||
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config;
|
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -5,8 +5,357 @@
|
||||||
// Created by Christopher Snowhill on 2/5/22.
|
// Created by Christopher Snowhill on 2/5/22.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import <Accelerate/Accelerate.h>
|
||||||
|
|
||||||
#import "ChunkList.h"
|
#import "ChunkList.h"
|
||||||
|
|
||||||
|
#import "hdcd_decode2.h"
|
||||||
|
|
||||||
|
#if !DSD_DECIMATE
|
||||||
|
#import "dsd2float.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#import "BadSampleCleaner.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
/**
|
||||||
|
* DSD 2 PCM: Stage 1:
|
||||||
|
* Decimate by factor 8
|
||||||
|
* (one byte (8 samples) -> one float sample)
|
||||||
|
* The bits are processed from least signicifant to most signicicant.
|
||||||
|
* @author Sebastian Gesemann
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define dsd2pcm_FILTER_COEFFS_COUNT 64
|
||||||
|
static const float dsd2pcm_FILTER_COEFFS[64] = {
|
||||||
|
0.09712411121659f, 0.09613438994044f, 0.09417884216316f, 0.09130441727307f,
|
||||||
|
0.08757947648990f, 0.08309142055179f, 0.07794369263673f, 0.07225228745463f,
|
||||||
|
0.06614191680338f, 0.05974199351302f, 0.05318259916599f, 0.04659059631228f,
|
||||||
|
0.04008603356890f, 0.03377897290478f, 0.02776684382775f, 0.02213240062966f,
|
||||||
|
0.01694232798846f, 0.01224650881275f, 0.00807793792573f, 0.00445323755944f,
|
||||||
|
0.00137370697215f, -0.00117318019994f, -0.00321193033831f, -0.00477694265140f,
|
||||||
|
-0.00591028841335f, -0.00665946056286f, -0.00707518873201f, -0.00720940203988f,
|
||||||
|
-0.00711340642819f, -0.00683632603227f, -0.00642384017266f, -0.00591723006715f,
|
||||||
|
-0.00535273320457f, -0.00476118922548f, -0.00416794965654f, -0.00359301524813f,
|
||||||
|
-0.00305135909510f, -0.00255339111833f, -0.00210551956895f, -0.00171076760278f,
|
||||||
|
-0.00136940723130f, -0.00107957856005f, -0.00083786862365f, -0.00063983084245f,
|
||||||
|
-0.00048043272086f, -0.00035442550015f, -0.00025663481039f, -0.00018217573430f,
|
||||||
|
-0.00012659899635f, -0.00008597726991f, -0.00005694188820f, -0.00003668060332f,
|
||||||
|
-0.00002290670286f, -0.00001380895679f, -0.00000799057558f, -0.00000440385083f,
|
||||||
|
-0.00000228567089f, -0.00000109760778f, -0.00000047286430f, -0.00000017129652f,
|
||||||
|
-0.00000004282776f, 0.00000000119422f, 0.00000000949179f, 0.00000000747450f
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dsd2pcm_state {
|
||||||
|
/*
|
||||||
|
* This is the 2nd half of an even order symmetric FIR
|
||||||
|
* lowpass filter (to be used on a signal sampled at 44100*64 Hz)
|
||||||
|
* Passband is 0-24 kHz (ripples +/- 0.025 dB)
|
||||||
|
* Stopband starts at 176.4 kHz (rejection: 170 dB)
|
||||||
|
* The overall gain is 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* These remain constant for the duration */
|
||||||
|
int FILT_LOOKUP_PARTS;
|
||||||
|
float *FILT_LOOKUP_TABLE;
|
||||||
|
uint8_t *REVERSE_BITS;
|
||||||
|
int FIFO_LENGTH;
|
||||||
|
int FIFO_OFS_MASK;
|
||||||
|
|
||||||
|
/* These are altered */
|
||||||
|
int *fifo;
|
||||||
|
int fpos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dsd2pcm_free(void *);
|
||||||
|
static void dsd2pcm_reset(void *);
|
||||||
|
|
||||||
|
static void *dsd2pcm_alloc() {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)calloc(1, sizeof(struct dsd2pcm_state));
|
||||||
|
|
||||||
|
float *FILT_LOOKUP_TABLE;
|
||||||
|
double *temp;
|
||||||
|
uint8_t *REVERSE_BITS;
|
||||||
|
|
||||||
|
if(!state)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
state->FILT_LOOKUP_PARTS = (dsd2pcm_FILTER_COEFFS_COUNT + 7) / 8;
|
||||||
|
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
||||||
|
// The current 128 tap FIR leads to an 8 KB lookup table
|
||||||
|
state->FILT_LOOKUP_TABLE = (float *)calloc(sizeof(float), FILT_LOOKUP_PARTS << 8);
|
||||||
|
if(!state->FILT_LOOKUP_TABLE)
|
||||||
|
goto fail;
|
||||||
|
FILT_LOOKUP_TABLE = state->FILT_LOOKUP_TABLE;
|
||||||
|
temp = (double *)calloc(sizeof(double), 0x100);
|
||||||
|
if(!temp)
|
||||||
|
goto fail;
|
||||||
|
for(int part = 0, sofs = 0, dofs = 0; part < FILT_LOOKUP_PARTS;) {
|
||||||
|
memset(temp, 0, 0x100 * sizeof(double));
|
||||||
|
for(int bit = 0, bitmask = 0x80; bit < 8 && sofs + bit < dsd2pcm_FILTER_COEFFS_COUNT;) {
|
||||||
|
double coeff = dsd2pcm_FILTER_COEFFS[sofs + bit];
|
||||||
|
for(int bite = 0; bite < 0x100; bite++) {
|
||||||
|
if((bite & bitmask) == 0) {
|
||||||
|
temp[bite] -= coeff;
|
||||||
|
} else {
|
||||||
|
temp[bite] += coeff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bit++;
|
||||||
|
bitmask >>= 1;
|
||||||
|
}
|
||||||
|
for(int s = 0; s < 0x100;) {
|
||||||
|
FILT_LOOKUP_TABLE[dofs++] = (float)temp[s++];
|
||||||
|
}
|
||||||
|
part++;
|
||||||
|
sofs += 8;
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
{ // calculate FIFO stuff
|
||||||
|
int k = 1;
|
||||||
|
while(k < FILT_LOOKUP_PARTS * 2) k <<= 1;
|
||||||
|
state->FIFO_LENGTH = k;
|
||||||
|
state->FIFO_OFS_MASK = k - 1;
|
||||||
|
}
|
||||||
|
state->REVERSE_BITS = (uint8_t *)calloc(1, 0x100);
|
||||||
|
if(!state->REVERSE_BITS)
|
||||||
|
goto fail;
|
||||||
|
REVERSE_BITS = state->REVERSE_BITS;
|
||||||
|
for(int i = 0, j = 0; i < 0x100; i++) {
|
||||||
|
REVERSE_BITS[i] = (uint8_t)j;
|
||||||
|
// "reverse-increment" of j
|
||||||
|
for(int bitmask = 0x80;;) {
|
||||||
|
if(((j ^= bitmask) & bitmask) != 0) break;
|
||||||
|
if(bitmask == 1) break;
|
||||||
|
bitmask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state->fifo = (int *)calloc(sizeof(int), state->FIFO_LENGTH);
|
||||||
|
if(!state->fifo)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
dsd2pcm_reset(state);
|
||||||
|
|
||||||
|
return (void *)state;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
dsd2pcm_free(state);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *dsd2pcm_dup(void *_state) {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||||
|
if(state) {
|
||||||
|
struct dsd2pcm_state *newstate = (struct dsd2pcm_state *)calloc(1, sizeof(struct dsd2pcm_state));
|
||||||
|
if(newstate) {
|
||||||
|
newstate->FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
||||||
|
newstate->FIFO_LENGTH = state->FIFO_LENGTH;
|
||||||
|
newstate->FIFO_OFS_MASK = state->FIFO_OFS_MASK;
|
||||||
|
newstate->fpos = state->fpos;
|
||||||
|
|
||||||
|
newstate->FILT_LOOKUP_TABLE = (float *)calloc(sizeof(float), state->FILT_LOOKUP_PARTS << 8);
|
||||||
|
if(!newstate->FILT_LOOKUP_TABLE)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
memcpy(newstate->FILT_LOOKUP_TABLE, state->FILT_LOOKUP_TABLE, sizeof(float) * (state->FILT_LOOKUP_PARTS << 8));
|
||||||
|
|
||||||
|
newstate->REVERSE_BITS = (uint8_t *)calloc(1, 0x100);
|
||||||
|
if(!newstate->REVERSE_BITS)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
memcpy(newstate->REVERSE_BITS, state->REVERSE_BITS, 0x100);
|
||||||
|
|
||||||
|
newstate->fifo = (int *)calloc(sizeof(int), state->FIFO_LENGTH);
|
||||||
|
if(!newstate->fifo)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
memcpy(newstate->fifo, state->fifo, sizeof(int) * state->FIFO_LENGTH);
|
||||||
|
|
||||||
|
return (void *)newstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
dsd2pcm_free(newstate);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsd2pcm_free(void *_state) {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||||
|
if(state) {
|
||||||
|
free(state->fifo);
|
||||||
|
free(state->REVERSE_BITS);
|
||||||
|
free(state->FILT_LOOKUP_TABLE);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsd2pcm_reset(void *_state) {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||||
|
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
||||||
|
int *fifo = state->fifo;
|
||||||
|
for(int i = 0; i < FILT_LOOKUP_PARTS; i++) {
|
||||||
|
fifo[i] = 0x55;
|
||||||
|
fifo[i + FILT_LOOKUP_PARTS] = 0xAA;
|
||||||
|
}
|
||||||
|
state->fpos = FILT_LOOKUP_PARTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsd2pcm_latency(void *_state) {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||||
|
if(state)
|
||||||
|
return state->FIFO_LENGTH;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsd2pcm_process(void *_state, const uint8_t *src, size_t sofs, size_t sinc, float *dest, size_t dofs, size_t dinc, size_t len) {
|
||||||
|
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||||
|
int bite1, bite2, temp;
|
||||||
|
float sample;
|
||||||
|
int *fifo = state->fifo;
|
||||||
|
const uint8_t *REVERSE_BITS = state->REVERSE_BITS;
|
||||||
|
const float *FILT_LOOKUP_TABLE = state->FILT_LOOKUP_TABLE;
|
||||||
|
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
||||||
|
const int FIFO_OFS_MASK = state->FIFO_OFS_MASK;
|
||||||
|
int fpos = state->fpos;
|
||||||
|
while(len > 0) {
|
||||||
|
fifo[fpos] = REVERSE_BITS[fifo[fpos]] & 0xFF;
|
||||||
|
fifo[(fpos + FILT_LOOKUP_PARTS) & FIFO_OFS_MASK] = src[sofs] & 0xFF;
|
||||||
|
sofs += sinc;
|
||||||
|
temp = (fpos + 1) & FIFO_OFS_MASK;
|
||||||
|
sample = 0;
|
||||||
|
for(int k = 0, lofs = 0; k < FILT_LOOKUP_PARTS;) {
|
||||||
|
bite1 = fifo[(fpos - k) & FIFO_OFS_MASK];
|
||||||
|
bite2 = fifo[(temp + k) & FIFO_OFS_MASK];
|
||||||
|
sample += FILT_LOOKUP_TABLE[lofs + bite1] + FILT_LOOKUP_TABLE[lofs + bite2];
|
||||||
|
k++;
|
||||||
|
lofs += 0x100;
|
||||||
|
}
|
||||||
|
fpos = temp;
|
||||||
|
dest[dofs] = sample;
|
||||||
|
dofs += dinc;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
state->fpos = fpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_dsd_to_f32(float *output, const uint8_t *input, size_t count, size_t channels, void **dsd2pcm) {
|
||||||
|
for(size_t channel = 0; channel < channels; ++channel) {
|
||||||
|
dsd2pcm_process(dsd2pcm[channel], input, channel, channels, output, channel, channels, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void convert_dsd_to_f32(float *output, const uint8_t *input, size_t count, size_t channels) {
|
||||||
|
const uint8_t *iptr = input;
|
||||||
|
float *optr = output;
|
||||||
|
for(size_t index = 0; index < count; ++index) {
|
||||||
|
for(size_t channel = 0; channel < channels; ++channel) {
|
||||||
|
uint8_t sample = *iptr++;
|
||||||
|
cblas_scopy(8, &dsd2float[sample][0], 1, optr++, (int)channels);
|
||||||
|
}
|
||||||
|
optr += channels * 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void convert_u8_to_s16(int16_t *output, const uint8_t *input, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
uint16_t sample = (input[i] << 8) | input[i];
|
||||||
|
sample ^= 0x8080;
|
||||||
|
output[i] = (int16_t)(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_s8_to_s16(int16_t *output, const uint8_t *input, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
uint16_t sample = (input[i] << 8) | input[i];
|
||||||
|
output[i] = (int16_t)(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_u16_to_s16(int16_t *buffer, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
buffer[i] ^= 0x8000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_s16_to_hdcd_input(int32_t *output, const int16_t *input, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
output[i] = input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_s24_to_s32(int32_t *output, const uint8_t *input, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
||||||
|
output[i] = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_u24_to_s32(int32_t *output, const uint8_t *input, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
||||||
|
output[i] = sample ^ 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_u32_to_s32(int32_t *buffer, size_t count) {
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
buffer[i] ^= 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_f64_to_f32(float *output, const double *input, size_t count) {
|
||||||
|
vDSP_vdpsp(input, 1, output, 1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes) {
|
||||||
|
size_t i;
|
||||||
|
bitsPerSample = (bitsPerSample + 7) / 8;
|
||||||
|
switch(bitsPerSample) {
|
||||||
|
case 2:
|
||||||
|
for(i = 0; i < bytes; i += 2) {
|
||||||
|
*(int16_t *)buffer = __builtin_bswap16(*(int16_t *)buffer);
|
||||||
|
buffer += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: {
|
||||||
|
union {
|
||||||
|
vDSP_int24 int24;
|
||||||
|
uint32_t int32;
|
||||||
|
} intval;
|
||||||
|
intval.int32 = 0;
|
||||||
|
for(i = 0; i < bytes; i += 3) {
|
||||||
|
intval.int24 = *(vDSP_int24 *)buffer;
|
||||||
|
intval.int32 = __builtin_bswap32(intval.int32 << 8);
|
||||||
|
*(vDSP_int24 *)buffer = intval.int24;
|
||||||
|
buffer += 3;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
for(i = 0; i < bytes; i += 4) {
|
||||||
|
*(uint32_t *)buffer = __builtin_bswap32(*(uint32_t *)buffer);
|
||||||
|
buffer += 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
for(i = 0; i < bytes; i += 8) {
|
||||||
|
*(uint64_t *)buffer = __builtin_bswap64(*(uint64_t *)buffer);
|
||||||
|
buffer += 8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@implementation ChunkList
|
@implementation ChunkList
|
||||||
|
|
||||||
@synthesize listDuration;
|
@synthesize listDuration;
|
||||||
|
@ -24,6 +373,17 @@
|
||||||
inRemover = NO;
|
inRemover = NO;
|
||||||
inPeeker = NO;
|
inPeeker = NO;
|
||||||
stopping = NO;
|
stopping = NO;
|
||||||
|
|
||||||
|
formatRead = NO;
|
||||||
|
|
||||||
|
inputBuffer = NULL;
|
||||||
|
inputBufferSize = 0;
|
||||||
|
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
dsd2pcm = NULL;
|
||||||
|
dsd2pcmCount = 0;
|
||||||
|
dsd2pcmLatency = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -34,6 +394,20 @@
|
||||||
while(inAdder || inRemover || inPeeker) {
|
while(inAdder || inRemover || inPeeker) {
|
||||||
usleep(500);
|
usleep(500);
|
||||||
}
|
}
|
||||||
|
if(hdcd_decoder) {
|
||||||
|
free(hdcd_decoder);
|
||||||
|
hdcd_decoder = NULL;
|
||||||
|
}
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
if(dsd2pcm && dsd2pcmCount) {
|
||||||
|
for(size_t i = 0; i < dsd2pcmCount; ++i) {
|
||||||
|
dsd2pcm_free(dsd2pcm[i]);
|
||||||
|
dsd2pcm[i] = NULL;
|
||||||
|
}
|
||||||
|
free(dsd2pcm);
|
||||||
|
dsd2pcm = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reset {
|
- (void)reset {
|
||||||
|
@ -97,6 +471,266 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (AudioChunk *)removeSamplesAsFloat32:(size_t)maxFrameCount {
|
||||||
|
if(stopping) {
|
||||||
|
return [[AudioChunk alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
@synchronized (chunkList) {
|
||||||
|
inRemover = YES;
|
||||||
|
if(![chunkList count]) {
|
||||||
|
inRemover = NO;
|
||||||
|
return [[AudioChunk alloc] init];
|
||||||
|
}
|
||||||
|
AudioChunk *chunk = [chunkList objectAtIndex:0];
|
||||||
|
if([chunk frameCount] <= maxFrameCount) {
|
||||||
|
[chunkList removeObjectAtIndex:0];
|
||||||
|
listDuration -= [chunk duration];
|
||||||
|
inRemover = NO;
|
||||||
|
return [self convertChunk:chunk];
|
||||||
|
}
|
||||||
|
NSData *removedData = [chunk removeSamples:maxFrameCount];
|
||||||
|
AudioChunk *ret = [[AudioChunk alloc] init];
|
||||||
|
[ret setFormat:[chunk format]];
|
||||||
|
[ret setChannelConfig:[chunk channelConfig]];
|
||||||
|
[ret assignSamples:[removedData bytes] frameCount:maxFrameCount];
|
||||||
|
listDuration -= [ret duration];
|
||||||
|
inRemover = NO;
|
||||||
|
return [self convertChunk:ret];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (AudioChunk *)convertChunk:(AudioChunk *)inChunk {
|
||||||
|
AudioStreamBasicDescription chunkFormat = [inChunk format];
|
||||||
|
uint32_t chunkConfig = [inChunk channelConfig];
|
||||||
|
BOOL chunkLossless = [inChunk lossless];
|
||||||
|
if(!formatRead || memcmp(&chunkConfig, &inputFormat, sizeof(chunkConfig)) != 0 ||
|
||||||
|
chunkConfig != inputChannelConfig || chunkLossless != inputLossless) {
|
||||||
|
formatRead = YES;
|
||||||
|
inputFormat = chunkFormat;
|
||||||
|
inputChannelConfig = chunkConfig;
|
||||||
|
inputLossless = chunkLossless;
|
||||||
|
|
||||||
|
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
||||||
|
if((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32)) || (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64)))
|
||||||
|
return [[AudioChunk alloc] init];
|
||||||
|
|
||||||
|
// These are really placeholders, as we're doing everything internally now
|
||||||
|
if(inputLossless &&
|
||||||
|
inputFormat.mBitsPerChannel == 16 &&
|
||||||
|
inputFormat.mChannelsPerFrame == 2 &&
|
||||||
|
inputFormat.mSampleRate == 44100) {
|
||||||
|
// possibly HDCD, run through decoder
|
||||||
|
if(hdcd_decoder) {
|
||||||
|
free(hdcd_decoder);
|
||||||
|
hdcd_decoder = NULL;
|
||||||
|
}
|
||||||
|
hdcd_decoder = calloc(1, sizeof(hdcd_state_stereo_t));
|
||||||
|
hdcd_reset_stereo((hdcd_state_stereo_t *)hdcd_decoder, 44100);
|
||||||
|
}
|
||||||
|
|
||||||
|
floatFormat = inputFormat;
|
||||||
|
floatFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
||||||
|
floatFormat.mBitsPerChannel = 32;
|
||||||
|
floatFormat.mBytesPerFrame = (32 / 8) * floatFormat.mChannelsPerFrame;
|
||||||
|
floatFormat.mBytesPerPacket = floatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket;
|
||||||
|
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
if(inputFormat.mBitsPerChannel == 1) {
|
||||||
|
// Decimate this for speed
|
||||||
|
floatFormat.mSampleRate *= 1.0 / 8.0;
|
||||||
|
if(dsd2pcm && dsd2pcmCount) {
|
||||||
|
for(size_t i = 0; i < dsd2pcmCount; ++i) {
|
||||||
|
dsd2pcm_free(dsd2pcm[i]);
|
||||||
|
dsd2pcm[i] = NULL;
|
||||||
|
}
|
||||||
|
free(dsd2pcm);
|
||||||
|
dsd2pcm = NULL;
|
||||||
|
}
|
||||||
|
dsd2pcmCount = floatFormat.mChannelsPerFrame;
|
||||||
|
dsd2pcm = (void **)calloc(dsd2pcmCount, sizeof(void *));
|
||||||
|
dsd2pcm[0] = dsd2pcm_alloc();
|
||||||
|
dsd2pcmLatency = dsd2pcm_latency(dsd2pcm[0]);
|
||||||
|
for(size_t i = 1; i < dsd2pcmCount; ++i) {
|
||||||
|
dsd2pcm[i] = dsd2pcm_dup(dsd2pcm[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger samplesRead = [inChunk frameCount];
|
||||||
|
|
||||||
|
if(!samplesRead) {
|
||||||
|
return [[AudioChunk alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
||||||
|
BOOL isUnsigned = !isFloat && !(inputFormat.mFormatFlags & kAudioFormatFlagIsSignedInteger);
|
||||||
|
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
||||||
|
BOOL isBigEndian = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsBigEndian);
|
||||||
|
|
||||||
|
NSData *inputData = [inChunk removeSamples:samplesRead];
|
||||||
|
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
const size_t sizeFactor = 2;
|
||||||
|
#else
|
||||||
|
const size_t sizeFactor = (bitsPerSample == 1) ? 9 : 2;
|
||||||
|
#endif
|
||||||
|
uint8_t tempData[samplesRead * floatFormat.mBytesPerPacket * sizeFactor + 32]; // Either two buffers plus padding, and/or double precision in case of endian flip
|
||||||
|
|
||||||
|
// double buffer system, with alignment
|
||||||
|
const size_t buffer_adder_base = (samplesRead * floatFormat.mBytesPerPacket + 31) & ~31;
|
||||||
|
|
||||||
|
NSUInteger bytesReadFromInput = samplesRead * inputFormat.mBytesPerPacket;
|
||||||
|
|
||||||
|
uint8_t *inputBuffer = (uint8_t *)[inputData bytes];
|
||||||
|
BOOL inputChanged = NO;
|
||||||
|
|
||||||
|
BOOL hdcdSustained = NO;
|
||||||
|
|
||||||
|
if(bytesReadFromInput && isBigEndian) {
|
||||||
|
// Time for endian swap!
|
||||||
|
memcpy(&tempData[0], [inputData bytes], bytesReadFromInput);
|
||||||
|
convert_be_to_le((uint8_t *)(&tempData[0]), inputFormat.mBitsPerChannel, bytesReadFromInput);
|
||||||
|
inputBuffer = &tempData[0];
|
||||||
|
inputChanged = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytesReadFromInput && isFloat && bitsPerSample == 64) {
|
||||||
|
// Time for precision loss from weird inputs
|
||||||
|
samplesRead = bytesReadFromInput / sizeof(double);
|
||||||
|
convert_f64_to_f32((float *)(&tempData[0]), (const double *)inputBuffer, samplesRead);
|
||||||
|
bytesReadFromInput = samplesRead * sizeof(float);
|
||||||
|
inputBuffer = (uint8_t *)(&tempData[0]);
|
||||||
|
inputChanged = YES;
|
||||||
|
bitsPerSample = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytesReadFromInput && !isFloat) {
|
||||||
|
float gain = 1.0;
|
||||||
|
if(bitsPerSample == 1) {
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
samplesRead = bytesReadFromInput / inputFormat.mBytesPerPacket;
|
||||||
|
convert_dsd_to_f32((float *)(&tempData[buffer_adder]), (const uint8_t *)inputBuffer, samplesRead, inputFormat.mChannelsPerFrame
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
,
|
||||||
|
dsd2pcm
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
#if !DSD_DECIMATE
|
||||||
|
samplesRead *= 8;
|
||||||
|
#endif
|
||||||
|
bitsPerSample = 32;
|
||||||
|
bytesReadFromInput = samplesRead * inputFormat.mChannelsPerFrame * sizeof(float);
|
||||||
|
isFloat = YES;
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
inputChanged = YES;
|
||||||
|
#if DSD_DECIMATE
|
||||||
|
float scaleFactor = 2.0f;
|
||||||
|
vDSP_vsdiv((float *)inputBuffer, 1, &scaleFactor, (float *)inputBuffer, 1, bytesReadFromInput / sizeof(float));
|
||||||
|
#endif
|
||||||
|
} else if(bitsPerSample <= 8) {
|
||||||
|
samplesRead = bytesReadFromInput;
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
if(!isUnsigned)
|
||||||
|
convert_s8_to_s16((int16_t *)(&tempData[buffer_adder]), (const uint8_t *)inputBuffer, samplesRead);
|
||||||
|
else
|
||||||
|
convert_u8_to_s16((int16_t *)(&tempData[buffer_adder]), (const uint8_t *)inputBuffer, samplesRead);
|
||||||
|
bitsPerSample = 16;
|
||||||
|
bytesReadFromInput = samplesRead * 2;
|
||||||
|
isUnsigned = NO;
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
inputChanged = YES;
|
||||||
|
}
|
||||||
|
if(hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale
|
||||||
|
samplesRead = bytesReadFromInput / 2;
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
if(isUnsigned)
|
||||||
|
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
||||||
|
convert_s16_to_hdcd_input((int32_t *)(&tempData[buffer_adder]), (int16_t *)inputBuffer, samplesRead);
|
||||||
|
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)(&tempData[buffer_adder]), (int)(samplesRead / 2));
|
||||||
|
if(((hdcd_state_stereo_t *)hdcd_decoder)->channel[0].sustain &&
|
||||||
|
((hdcd_state_stereo_t *)hdcd_decoder)->channel[1].sustain) {
|
||||||
|
hdcdSustained = YES;
|
||||||
|
}
|
||||||
|
gain = 2.0;
|
||||||
|
bitsPerSample = 32;
|
||||||
|
bytesReadFromInput = samplesRead * 4;
|
||||||
|
isUnsigned = NO;
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
inputChanged = YES;
|
||||||
|
} else if(bitsPerSample <= 16) {
|
||||||
|
samplesRead = bytesReadFromInput / 2;
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
if(isUnsigned) {
|
||||||
|
if(!inputChanged) {
|
||||||
|
memcpy(&tempData[buffer_adder], inputBuffer, samplesRead * 2);
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
inputChanged = YES;
|
||||||
|
}
|
||||||
|
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
||||||
|
}
|
||||||
|
const size_t buffer_adder2 = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
vDSP_vflt16((const short *)inputBuffer, 1, (float *)(&tempData[buffer_adder2]), 1, samplesRead);
|
||||||
|
float scale = 1ULL << 15;
|
||||||
|
vDSP_vsdiv((const float *)(&tempData[buffer_adder2]), 1, &scale, (float *)(&tempData[buffer_adder2]), 1, samplesRead);
|
||||||
|
bitsPerSample = 32;
|
||||||
|
bytesReadFromInput = samplesRead * sizeof(float);
|
||||||
|
isUnsigned = NO;
|
||||||
|
isFloat = YES;
|
||||||
|
inputBuffer = &tempData[buffer_adder2];
|
||||||
|
inputChanged = YES;
|
||||||
|
} else if(bitsPerSample <= 24) {
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||||
|
samplesRead = bytesReadFromInput / 3;
|
||||||
|
if(isUnsigned)
|
||||||
|
convert_u24_to_s32((int32_t *)(&tempData[buffer_adder]), (uint8_t *)inputBuffer, samplesRead);
|
||||||
|
else
|
||||||
|
convert_s24_to_s32((int32_t *)(&tempData[buffer_adder]), (uint8_t *)inputBuffer, samplesRead);
|
||||||
|
bitsPerSample = 32;
|
||||||
|
bytesReadFromInput = samplesRead * 4;
|
||||||
|
isUnsigned = NO;
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
inputChanged = YES;
|
||||||
|
}
|
||||||
|
if(!isFloat && bitsPerSample <= 32) {
|
||||||
|
samplesRead = bytesReadFromInput / 4;
|
||||||
|
if(isUnsigned) {
|
||||||
|
if(!inputChanged) {
|
||||||
|
memcpy(&tempData[0], inputBuffer, bytesReadFromInput);
|
||||||
|
inputBuffer = &tempData[0];
|
||||||
|
}
|
||||||
|
convert_u32_to_s32((int32_t *)inputBuffer, samplesRead);
|
||||||
|
}
|
||||||
|
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0; // vDSP functions expect aligned to four elements
|
||||||
|
vDSP_vflt32((const int *)inputBuffer, 1, (float *)(&tempData[buffer_adder]), 1, samplesRead);
|
||||||
|
float scale = (1ULL << 31) / gain;
|
||||||
|
vDSP_vsdiv((const float *)(&tempData[buffer_adder]), 1, &scale, (float *)(&tempData[buffer_adder]), 1, samplesRead);
|
||||||
|
bitsPerSample = 32;
|
||||||
|
bytesReadFromInput = samplesRead * sizeof(float);
|
||||||
|
isUnsigned = NO;
|
||||||
|
isFloat = YES;
|
||||||
|
inputBuffer = &tempData[buffer_adder];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
[BadSampleCleaner cleanSamples:(float *)inputBuffer
|
||||||
|
amount:bytesReadFromInput / sizeof(float)
|
||||||
|
location:@"post int to float conversion"];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioChunk *outChunk = [[AudioChunk alloc] init];
|
||||||
|
[outChunk setFormat:floatFormat];
|
||||||
|
[outChunk setChannelConfig:inputChannelConfig];
|
||||||
|
[outChunk setLossless:inputLossless];
|
||||||
|
if(hdcdSustained) [outChunk setHDCD];
|
||||||
|
|
||||||
|
[outChunk assignSamples:inputBuffer frameCount:bytesReadFromInput / floatFormat.mBytesPerPacket];
|
||||||
|
|
||||||
|
return outChunk;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)peekFormat:(AudioStreamBasicDescription *)format channelConfig:(uint32_t *)config {
|
- (BOOL)peekFormat:(AudioStreamBasicDescription *)format channelConfig:(uint32_t *)config {
|
||||||
if(stopping) return NO;
|
if(stopping) return NO;
|
||||||
@synchronized(chunkList) {
|
@synchronized(chunkList) {
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
#import "Node.h"
|
#import "Node.h"
|
||||||
|
|
||||||
#define DSD_DECIMATE 1
|
|
||||||
|
|
||||||
@interface ConverterNode : Node {
|
@interface ConverterNode : Node {
|
||||||
NSDictionary *rgInfo;
|
NSDictionary *rgInfo;
|
||||||
|
|
||||||
|
@ -29,31 +27,16 @@
|
||||||
|
|
||||||
float volumeScale;
|
float volumeScale;
|
||||||
|
|
||||||
void *floatBuffer;
|
|
||||||
size_t floatBufferSize;
|
|
||||||
size_t floatSize, floatOffset;
|
|
||||||
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
void **dsd2pcm;
|
|
||||||
size_t dsd2pcmCount;
|
|
||||||
int dsd2pcmLatency;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BOOL rememberedLossless;
|
BOOL rememberedLossless;
|
||||||
|
|
||||||
AudioStreamBasicDescription inputFormat;
|
AudioStreamBasicDescription inputFormat;
|
||||||
AudioStreamBasicDescription floatFormat;
|
AudioStreamBasicDescription floatFormat;
|
||||||
AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format
|
|
||||||
|
|
||||||
uint32_t inputChannelConfig;
|
uint32_t inputChannelConfig;
|
||||||
|
|
||||||
BOOL streamFormatChanged;
|
BOOL streamFormatChanged;
|
||||||
AudioStreamBasicDescription newInputFormat;
|
AudioStreamBasicDescription newInputFormat;
|
||||||
uint32_t newInputChannelConfig;
|
uint32_t newInputChannelConfig;
|
||||||
|
|
||||||
AudioChunk *lastChunkIn;
|
|
||||||
|
|
||||||
void *hdcd_decoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property AudioStreamBasicDescription inputFormat;
|
@property AudioStreamBasicDescription inputFormat;
|
||||||
|
@ -64,7 +47,7 @@
|
||||||
- (void)cleanUp;
|
- (void)cleanUp;
|
||||||
|
|
||||||
- (void)process;
|
- (void)process;
|
||||||
- (int)convert:(void *)dest amount:(int)amount;
|
- (AudioChunk *)convert;
|
||||||
|
|
||||||
- (void)setRGInfo:(NSDictionary *)rgi;
|
- (void)setRGInfo:(NSDictionary *)rgi;
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,10 @@
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
|
||||||
#import "hdcd_decode2.h"
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#import "BadSampleCleaner.h"
|
#import "BadSampleCleaner.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !DSD_DECIMATE
|
|
||||||
#include "dsd2float.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void PrintStreamDesc(AudioStreamBasicDescription *inDesc) {
|
void PrintStreamDesc(AudioStreamBasicDescription *inDesc) {
|
||||||
if(!inDesc) {
|
if(!inDesc) {
|
||||||
DLog(@"Can't print a NULL desc!\n");
|
DLog(@"Can't print a NULL desc!\n");
|
||||||
|
@ -56,22 +50,11 @@ static void *kConverterNodeContext = &kConverterNodeContext;
|
||||||
|
|
||||||
inputBuffer = NULL;
|
inputBuffer = NULL;
|
||||||
inputBufferSize = 0;
|
inputBufferSize = 0;
|
||||||
floatBuffer = NULL;
|
|
||||||
floatBufferSize = 0;
|
|
||||||
|
|
||||||
stopping = NO;
|
stopping = NO;
|
||||||
convertEntered = NO;
|
convertEntered = NO;
|
||||||
paused = NO;
|
paused = NO;
|
||||||
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
dsd2pcm = NULL;
|
|
||||||
dsd2pcmCount = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hdcd_decoder = NULL;
|
|
||||||
|
|
||||||
lastChunkIn = nil;
|
|
||||||
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:kConverterNodeContext];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:kConverterNodeContext];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,343 +77,6 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
/**
|
|
||||||
* DSD 2 PCM: Stage 1:
|
|
||||||
* Decimate by factor 8
|
|
||||||
* (one byte (8 samples) -> one float sample)
|
|
||||||
* The bits are processed from least signicifant to most signicicant.
|
|
||||||
* @author Sebastian Gesemann
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define dsd2pcm_FILTER_COEFFS_COUNT 64
|
|
||||||
static const float dsd2pcm_FILTER_COEFFS[64] = {
|
|
||||||
0.09712411121659f, 0.09613438994044f, 0.09417884216316f, 0.09130441727307f,
|
|
||||||
0.08757947648990f, 0.08309142055179f, 0.07794369263673f, 0.07225228745463f,
|
|
||||||
0.06614191680338f, 0.05974199351302f, 0.05318259916599f, 0.04659059631228f,
|
|
||||||
0.04008603356890f, 0.03377897290478f, 0.02776684382775f, 0.02213240062966f,
|
|
||||||
0.01694232798846f, 0.01224650881275f, 0.00807793792573f, 0.00445323755944f,
|
|
||||||
0.00137370697215f, -0.00117318019994f, -0.00321193033831f, -0.00477694265140f,
|
|
||||||
-0.00591028841335f, -0.00665946056286f, -0.00707518873201f, -0.00720940203988f,
|
|
||||||
-0.00711340642819f, -0.00683632603227f, -0.00642384017266f, -0.00591723006715f,
|
|
||||||
-0.00535273320457f, -0.00476118922548f, -0.00416794965654f, -0.00359301524813f,
|
|
||||||
-0.00305135909510f, -0.00255339111833f, -0.00210551956895f, -0.00171076760278f,
|
|
||||||
-0.00136940723130f, -0.00107957856005f, -0.00083786862365f, -0.00063983084245f,
|
|
||||||
-0.00048043272086f, -0.00035442550015f, -0.00025663481039f, -0.00018217573430f,
|
|
||||||
-0.00012659899635f, -0.00008597726991f, -0.00005694188820f, -0.00003668060332f,
|
|
||||||
-0.00002290670286f, -0.00001380895679f, -0.00000799057558f, -0.00000440385083f,
|
|
||||||
-0.00000228567089f, -0.00000109760778f, -0.00000047286430f, -0.00000017129652f,
|
|
||||||
-0.00000004282776f, 0.00000000119422f, 0.00000000949179f, 0.00000000747450f
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dsd2pcm_state {
|
|
||||||
/*
|
|
||||||
* This is the 2nd half of an even order symmetric FIR
|
|
||||||
* lowpass filter (to be used on a signal sampled at 44100*64 Hz)
|
|
||||||
* Passband is 0-24 kHz (ripples +/- 0.025 dB)
|
|
||||||
* Stopband starts at 176.4 kHz (rejection: 170 dB)
|
|
||||||
* The overall gain is 2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* These remain constant for the duration */
|
|
||||||
int FILT_LOOKUP_PARTS;
|
|
||||||
float *FILT_LOOKUP_TABLE;
|
|
||||||
uint8_t *REVERSE_BITS;
|
|
||||||
int FIFO_LENGTH;
|
|
||||||
int FIFO_OFS_MASK;
|
|
||||||
|
|
||||||
/* These are altered */
|
|
||||||
int *fifo;
|
|
||||||
int fpos;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dsd2pcm_free(void *);
|
|
||||||
static void dsd2pcm_reset(void *);
|
|
||||||
|
|
||||||
static void *dsd2pcm_alloc() {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)calloc(1, sizeof(struct dsd2pcm_state));
|
|
||||||
|
|
||||||
float *FILT_LOOKUP_TABLE;
|
|
||||||
double *temp;
|
|
||||||
uint8_t *REVERSE_BITS;
|
|
||||||
|
|
||||||
if(!state)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
state->FILT_LOOKUP_PARTS = (dsd2pcm_FILTER_COEFFS_COUNT + 7) / 8;
|
|
||||||
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
|
||||||
// The current 128 tap FIR leads to an 8 KB lookup table
|
|
||||||
state->FILT_LOOKUP_TABLE = (float *)calloc(sizeof(float), FILT_LOOKUP_PARTS << 8);
|
|
||||||
if(!state->FILT_LOOKUP_TABLE)
|
|
||||||
goto fail;
|
|
||||||
FILT_LOOKUP_TABLE = state->FILT_LOOKUP_TABLE;
|
|
||||||
temp = (double *)calloc(sizeof(double), 0x100);
|
|
||||||
if(!temp)
|
|
||||||
goto fail;
|
|
||||||
for(int part = 0, sofs = 0, dofs = 0; part < FILT_LOOKUP_PARTS;) {
|
|
||||||
memset(temp, 0, 0x100 * sizeof(double));
|
|
||||||
for(int bit = 0, bitmask = 0x80; bit < 8 && sofs + bit < dsd2pcm_FILTER_COEFFS_COUNT;) {
|
|
||||||
double coeff = dsd2pcm_FILTER_COEFFS[sofs + bit];
|
|
||||||
for(int bite = 0; bite < 0x100; bite++) {
|
|
||||||
if((bite & bitmask) == 0) {
|
|
||||||
temp[bite] -= coeff;
|
|
||||||
} else {
|
|
||||||
temp[bite] += coeff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bit++;
|
|
||||||
bitmask >>= 1;
|
|
||||||
}
|
|
||||||
for(int s = 0; s < 0x100;) {
|
|
||||||
FILT_LOOKUP_TABLE[dofs++] = (float)temp[s++];
|
|
||||||
}
|
|
||||||
part++;
|
|
||||||
sofs += 8;
|
|
||||||
}
|
|
||||||
free(temp);
|
|
||||||
{ // calculate FIFO stuff
|
|
||||||
int k = 1;
|
|
||||||
while(k < FILT_LOOKUP_PARTS * 2) k <<= 1;
|
|
||||||
state->FIFO_LENGTH = k;
|
|
||||||
state->FIFO_OFS_MASK = k - 1;
|
|
||||||
}
|
|
||||||
state->REVERSE_BITS = (uint8_t *)calloc(1, 0x100);
|
|
||||||
if(!state->REVERSE_BITS)
|
|
||||||
goto fail;
|
|
||||||
REVERSE_BITS = state->REVERSE_BITS;
|
|
||||||
for(int i = 0, j = 0; i < 0x100; i++) {
|
|
||||||
REVERSE_BITS[i] = (uint8_t)j;
|
|
||||||
// "reverse-increment" of j
|
|
||||||
for(int bitmask = 0x80;;) {
|
|
||||||
if(((j ^= bitmask) & bitmask) != 0) break;
|
|
||||||
if(bitmask == 1) break;
|
|
||||||
bitmask >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->fifo = (int *)calloc(sizeof(int), state->FIFO_LENGTH);
|
|
||||||
if(!state->fifo)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
dsd2pcm_reset(state);
|
|
||||||
|
|
||||||
return (void *)state;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
dsd2pcm_free(state);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *dsd2pcm_dup(void *_state) {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
|
||||||
if(state) {
|
|
||||||
struct dsd2pcm_state *newstate = (struct dsd2pcm_state *)calloc(1, sizeof(struct dsd2pcm_state));
|
|
||||||
if(newstate) {
|
|
||||||
newstate->FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
|
||||||
newstate->FIFO_LENGTH = state->FIFO_LENGTH;
|
|
||||||
newstate->FIFO_OFS_MASK = state->FIFO_OFS_MASK;
|
|
||||||
newstate->fpos = state->fpos;
|
|
||||||
|
|
||||||
newstate->FILT_LOOKUP_TABLE = (float *)calloc(sizeof(float), state->FILT_LOOKUP_PARTS << 8);
|
|
||||||
if(!newstate->FILT_LOOKUP_TABLE)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
memcpy(newstate->FILT_LOOKUP_TABLE, state->FILT_LOOKUP_TABLE, sizeof(float) * (state->FILT_LOOKUP_PARTS << 8));
|
|
||||||
|
|
||||||
newstate->REVERSE_BITS = (uint8_t *)calloc(1, 0x100);
|
|
||||||
if(!newstate->REVERSE_BITS)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
memcpy(newstate->REVERSE_BITS, state->REVERSE_BITS, 0x100);
|
|
||||||
|
|
||||||
newstate->fifo = (int *)calloc(sizeof(int), state->FIFO_LENGTH);
|
|
||||||
if(!newstate->fifo)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
memcpy(newstate->fifo, state->fifo, sizeof(int) * state->FIFO_LENGTH);
|
|
||||||
|
|
||||||
return (void *)newstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
dsd2pcm_free(newstate);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dsd2pcm_free(void *_state) {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
|
||||||
if(state) {
|
|
||||||
free(state->fifo);
|
|
||||||
free(state->REVERSE_BITS);
|
|
||||||
free(state->FILT_LOOKUP_TABLE);
|
|
||||||
free(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dsd2pcm_reset(void *_state) {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
|
||||||
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
|
||||||
int *fifo = state->fifo;
|
|
||||||
for(int i = 0; i < FILT_LOOKUP_PARTS; i++) {
|
|
||||||
fifo[i] = 0x55;
|
|
||||||
fifo[i + FILT_LOOKUP_PARTS] = 0xAA;
|
|
||||||
}
|
|
||||||
state->fpos = FILT_LOOKUP_PARTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dsd2pcm_latency(void *_state) {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
|
||||||
if(state)
|
|
||||||
return state->FIFO_LENGTH;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dsd2pcm_process(void *_state, const uint8_t *src, size_t sofs, size_t sinc, float *dest, size_t dofs, size_t dinc, size_t len) {
|
|
||||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
|
||||||
int bite1, bite2, temp;
|
|
||||||
float sample;
|
|
||||||
int *fifo = state->fifo;
|
|
||||||
const uint8_t *REVERSE_BITS = state->REVERSE_BITS;
|
|
||||||
const float *FILT_LOOKUP_TABLE = state->FILT_LOOKUP_TABLE;
|
|
||||||
const int FILT_LOOKUP_PARTS = state->FILT_LOOKUP_PARTS;
|
|
||||||
const int FIFO_OFS_MASK = state->FIFO_OFS_MASK;
|
|
||||||
int fpos = state->fpos;
|
|
||||||
while(len > 0) {
|
|
||||||
fifo[fpos] = REVERSE_BITS[fifo[fpos]] & 0xFF;
|
|
||||||
fifo[(fpos + FILT_LOOKUP_PARTS) & FIFO_OFS_MASK] = src[sofs] & 0xFF;
|
|
||||||
sofs += sinc;
|
|
||||||
temp = (fpos + 1) & FIFO_OFS_MASK;
|
|
||||||
sample = 0;
|
|
||||||
for(int k = 0, lofs = 0; k < FILT_LOOKUP_PARTS;) {
|
|
||||||
bite1 = fifo[(fpos - k) & FIFO_OFS_MASK];
|
|
||||||
bite2 = fifo[(temp + k) & FIFO_OFS_MASK];
|
|
||||||
sample += FILT_LOOKUP_TABLE[lofs + bite1] + FILT_LOOKUP_TABLE[lofs + bite2];
|
|
||||||
k++;
|
|
||||||
lofs += 0x100;
|
|
||||||
}
|
|
||||||
fpos = temp;
|
|
||||||
dest[dofs] = sample;
|
|
||||||
dofs += dinc;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
state->fpos = fpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_dsd_to_f32(float *output, const uint8_t *input, size_t count, size_t channels, void **dsd2pcm) {
|
|
||||||
for(size_t channel = 0; channel < channels; ++channel) {
|
|
||||||
dsd2pcm_process(dsd2pcm[channel], input, channel, channels, output, channel, channels, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void convert_dsd_to_f32(float *output, const uint8_t *input, size_t count, size_t channels) {
|
|
||||||
const uint8_t *iptr = input;
|
|
||||||
float *optr = output;
|
|
||||||
for(size_t index = 0; index < count; ++index) {
|
|
||||||
for(size_t channel = 0; channel < channels; ++channel) {
|
|
||||||
uint8_t sample = *iptr++;
|
|
||||||
cblas_scopy(8, &dsd2float[sample][0], 1, optr++, (int)channels);
|
|
||||||
}
|
|
||||||
optr += channels * 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void convert_u8_to_s16(int16_t *output, const uint8_t *input, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
uint16_t sample = (input[i] << 8) | input[i];
|
|
||||||
sample ^= 0x8080;
|
|
||||||
output[i] = (int16_t)(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_s8_to_s16(int16_t *output, const uint8_t *input, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
uint16_t sample = (input[i] << 8) | input[i];
|
|
||||||
output[i] = (int16_t)(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_u16_to_s16(int16_t *buffer, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
buffer[i] ^= 0x8000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_s16_to_hdcd_input(int32_t *output, const int16_t *input, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
output[i] = input[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_s24_to_s32(int32_t *output, const uint8_t *input, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
|
||||||
output[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_u24_to_s32(int32_t *output, const uint8_t *input, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
int32_t sample = (input[i * 3] << 8) | (input[i * 3 + 1] << 16) | (input[i * 3 + 2] << 24);
|
|
||||||
output[i] = sample ^ 0x80000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_u32_to_s32(int32_t *buffer, size_t count) {
|
|
||||||
for(size_t i = 0; i < count; ++i) {
|
|
||||||
buffer[i] ^= 0x80000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_f64_to_f32(float *output, const double *input, size_t count) {
|
|
||||||
vDSP_vdpsp(input, 1, output, 1, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes) {
|
|
||||||
size_t i;
|
|
||||||
bitsPerSample = (bitsPerSample + 7) / 8;
|
|
||||||
switch(bitsPerSample) {
|
|
||||||
case 2:
|
|
||||||
for(i = 0; i < bytes; i += 2) {
|
|
||||||
*(int16_t *)buffer = __builtin_bswap16(*(int16_t *)buffer);
|
|
||||||
buffer += 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: {
|
|
||||||
union {
|
|
||||||
vDSP_int24 int24;
|
|
||||||
uint32_t int32;
|
|
||||||
} intval;
|
|
||||||
intval.int32 = 0;
|
|
||||||
for(i = 0; i < bytes; i += 3) {
|
|
||||||
intval.int24 = *(vDSP_int24 *)buffer;
|
|
||||||
intval.int32 = __builtin_bswap32(intval.int32 << 8);
|
|
||||||
*(vDSP_int24 *)buffer = intval.int24;
|
|
||||||
buffer += 3;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
for(i = 0; i < bytes; i += 4) {
|
|
||||||
*(uint32_t *)buffer = __builtin_bswap32(*(uint32_t *)buffer);
|
|
||||||
buffer += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
|
||||||
for(i = 0; i < bytes; i += 8) {
|
|
||||||
*(uint64_t *)buffer = __builtin_bswap64(*(uint64_t *)buffer);
|
|
||||||
buffer += 8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)process {
|
- (void)process {
|
||||||
// Removed endOfStream check from here, since we want to be able to flush the converter
|
// 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,
|
// when the end of stream is reached. Convert function instead processes what it can,
|
||||||
|
@ -464,44 +110,26 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
||||||
|
|
||||||
- (AudioChunk *)convert {
|
- (AudioChunk *)convert {
|
||||||
UInt32 ioNumberPackets;
|
UInt32 ioNumberPackets;
|
||||||
int amountReadFromFC;
|
|
||||||
int amountRead = 0;
|
|
||||||
|
|
||||||
if(stopping)
|
if(stopping)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
convertEntered = YES;
|
convertEntered = YES;
|
||||||
|
|
||||||
tryagain:
|
|
||||||
if(stopping || [self shouldContinue] == NO) {
|
if(stopping || [self shouldContinue] == NO) {
|
||||||
convertEntered = NO;
|
convertEntered = NO;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
amountReadFromFC = 0;
|
|
||||||
|
|
||||||
if(floatOffset == floatSize) // skip this step if there's still float buffered
|
|
||||||
while(inpOffset == inpSize) {
|
while(inpOffset == inpSize) {
|
||||||
size_t samplesRead = 0;
|
|
||||||
|
|
||||||
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
|
||||||
BOOL isUnsigned = !isFloat && !(inputFormat.mFormatFlags & kAudioFormatFlagIsSignedInteger);
|
|
||||||
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
|
||||||
|
|
||||||
// Approximately the most we want on input
|
// Approximately the most we want on input
|
||||||
ioNumberPackets = CHUNK_SIZE;
|
ioNumberPackets = 4096;
|
||||||
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
const size_t sizeScale = 3;
|
|
||||||
#else
|
|
||||||
const size_t sizeScale = (bitsPerSample == 1) ? 10 : 3;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
||||||
if(!inputBuffer || inputBufferSize < newSize)
|
if(!inputBuffer || inputBufferSize < newSize)
|
||||||
inputBuffer = realloc(inputBuffer, inputBufferSize = newSize * sizeScale);
|
inputBuffer = realloc(inputBuffer, inputBufferSize = newSize);
|
||||||
|
|
||||||
ssize_t amountToWrite = ioNumberPackets * inputFormat.mBytesPerPacket;
|
ssize_t amountToWrite = ioNumberPackets * floatFormat.mBytesPerPacket;
|
||||||
|
|
||||||
ssize_t bytesReadFromInput = 0;
|
ssize_t bytesReadFromInput = 0;
|
||||||
|
|
||||||
|
@ -522,7 +150,7 @@ tryagain:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioChunk *chunk = [self readChunk:((amountToWrite - bytesReadFromInput) / inputFormat.mBytesPerPacket)];
|
AudioChunk *chunk = [self readChunkAsFloat32:((amountToWrite - bytesReadFromInput) / floatFormat.mBytesPerPacket)];
|
||||||
inf = [chunk format];
|
inf = [chunk format];
|
||||||
size_t frameCount = [chunk frameCount];
|
size_t frameCount = [chunk frameCount];
|
||||||
config = [chunk channelConfig];
|
config = [chunk channelConfig];
|
||||||
|
@ -530,11 +158,9 @@ tryagain:
|
||||||
if(frameCount) {
|
if(frameCount) {
|
||||||
NSData *samples = [chunk removeSamples:frameCount];
|
NSData *samples = [chunk removeSamples:frameCount];
|
||||||
memcpy(((uint8_t *)inputBuffer) + bytesReadFromInput, [samples bytes], bytesRead);
|
memcpy(((uint8_t *)inputBuffer) + bytesReadFromInput, [samples bytes], bytesRead);
|
||||||
lastChunkIn = [[AudioChunk alloc] init];
|
if([chunk isHDCD]) {
|
||||||
[lastChunkIn setFormat:inf];
|
[controller sustainHDCD];
|
||||||
[lastChunkIn setChannelConfig:config];
|
}
|
||||||
[lastChunkIn setLossless:[chunk lossless]];
|
|
||||||
[lastChunkIn assignSamples:[samples bytes] frameCount:frameCount];
|
|
||||||
}
|
}
|
||||||
bytesReadFromInput += bytesRead;
|
bytesReadFromInput += bytesRead;
|
||||||
if(!frameCount) {
|
if(!frameCount) {
|
||||||
|
@ -542,173 +168,19 @@ tryagain:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL isBigEndian = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsBigEndian);
|
|
||||||
|
|
||||||
if(!bytesReadFromInput) {
|
if(!bytesReadFromInput) {
|
||||||
convertEntered = NO;
|
convertEntered = NO;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bytesReadFromInput && isBigEndian) {
|
|
||||||
// Time for endian swap!
|
|
||||||
convert_be_to_le((uint8_t *)inputBuffer, inputFormat.mBitsPerChannel, bytesReadFromInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bytesReadFromInput && isFloat && inputFormat.mBitsPerChannel == 64) {
|
|
||||||
// Time for precision loss from weird inputs
|
|
||||||
samplesRead = bytesReadFromInput / sizeof(double);
|
|
||||||
convert_f64_to_f32((float *)(((uint8_t *)inputBuffer) + bytesReadFromInput), (const double *)inputBuffer, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + bytesReadFromInput, samplesRead * sizeof(float));
|
|
||||||
bytesReadFromInput = samplesRead * sizeof(float);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bytesReadFromInput && !isFloat) {
|
|
||||||
float gain = 1.0;
|
|
||||||
if(bitsPerSample == 1) {
|
|
||||||
samplesRead = bytesReadFromInput / inputFormat.mBytesPerPacket;
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 15) & ~15;
|
|
||||||
convert_dsd_to_f32((float *)(((uint8_t *)inputBuffer) + buffer_adder), (const uint8_t *)inputBuffer, samplesRead, inputFormat.mChannelsPerFrame
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
,
|
|
||||||
dsd2pcm
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
#if !DSD_DECIMATE
|
|
||||||
samplesRead *= 8;
|
|
||||||
#endif
|
|
||||||
memmove(inputBuffer, ((const uint8_t *)inputBuffer) + buffer_adder, samplesRead * inputFormat.mChannelsPerFrame * sizeof(float));
|
|
||||||
bitsPerSample = 32;
|
|
||||||
bytesReadFromInput = samplesRead * inputFormat.mChannelsPerFrame * sizeof(float);
|
|
||||||
isFloat = YES;
|
|
||||||
} else if(bitsPerSample <= 8) {
|
|
||||||
samplesRead = bytesReadFromInput;
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 1) & ~1;
|
|
||||||
if(!isUnsigned)
|
|
||||||
convert_s8_to_s16((int16_t *)(((uint8_t *)inputBuffer) + buffer_adder), (const uint8_t *)inputBuffer, samplesRead);
|
|
||||||
else
|
|
||||||
convert_u8_to_s16((int16_t *)(((uint8_t *)inputBuffer) + buffer_adder), (const uint8_t *)inputBuffer, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + buffer_adder, samplesRead * 2);
|
|
||||||
bitsPerSample = 16;
|
|
||||||
bytesReadFromInput = samplesRead * 2;
|
|
||||||
isUnsigned = NO;
|
|
||||||
}
|
|
||||||
if(hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale
|
|
||||||
samplesRead = bytesReadFromInput / 2;
|
|
||||||
if(isUnsigned)
|
|
||||||
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 3) & ~3;
|
|
||||||
convert_s16_to_hdcd_input((int32_t *)(((uint8_t *)inputBuffer) + buffer_adder), (int16_t *)inputBuffer, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + buffer_adder, samplesRead * 4);
|
|
||||||
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)inputBuffer, (int)(samplesRead / 2));
|
|
||||||
if(((hdcd_state_stereo_t *)hdcd_decoder)->channel[0].sustain &&
|
|
||||||
((hdcd_state_stereo_t *)hdcd_decoder)->channel[1].sustain) {
|
|
||||||
[controller sustainHDCD];
|
|
||||||
}
|
|
||||||
gain = 2.0;
|
|
||||||
bitsPerSample = 32;
|
|
||||||
bytesReadFromInput = samplesRead * 4;
|
|
||||||
isUnsigned = NO;
|
|
||||||
} else if(bitsPerSample <= 16) {
|
|
||||||
samplesRead = bytesReadFromInput / 2;
|
|
||||||
if(isUnsigned)
|
|
||||||
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 15) & ~15; // vDSP functions expect aligned to four elements
|
|
||||||
vDSP_vflt16((const short *)inputBuffer, 1, (float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, samplesRead);
|
|
||||||
float scale = 1ULL << 15;
|
|
||||||
vDSP_vsdiv((const float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, &scale, (float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + buffer_adder, samplesRead * sizeof(float));
|
|
||||||
bitsPerSample = 32;
|
|
||||||
bytesReadFromInput = samplesRead * sizeof(float);
|
|
||||||
isUnsigned = NO;
|
|
||||||
isFloat = YES;
|
|
||||||
} else if(bitsPerSample <= 24) {
|
|
||||||
samplesRead = bytesReadFromInput / 3;
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 3) & ~3;
|
|
||||||
if(isUnsigned)
|
|
||||||
convert_u24_to_s32((int32_t *)(((uint8_t *)inputBuffer) + buffer_adder), (uint8_t *)inputBuffer, samplesRead);
|
|
||||||
else
|
|
||||||
convert_s24_to_s32((int32_t *)(((uint8_t *)inputBuffer) + buffer_adder), (uint8_t *)inputBuffer, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + buffer_adder, samplesRead * 4);
|
|
||||||
bitsPerSample = 32;
|
|
||||||
bytesReadFromInput = samplesRead * 4;
|
|
||||||
isUnsigned = NO;
|
|
||||||
}
|
|
||||||
if(!isFloat && bitsPerSample <= 32) {
|
|
||||||
samplesRead = bytesReadFromInput / 4;
|
|
||||||
if(isUnsigned)
|
|
||||||
convert_u32_to_s32((int32_t *)inputBuffer, samplesRead);
|
|
||||||
size_t buffer_adder = (bytesReadFromInput + 31) & ~31; // vDSP functions expect aligned to four elements
|
|
||||||
vDSP_vflt32((const int *)inputBuffer, 1, (float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, samplesRead);
|
|
||||||
float scale = (1ULL << 31) / gain;
|
|
||||||
vDSP_vsdiv((const float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, &scale, (float *)(((uint8_t *)inputBuffer) + buffer_adder), 1, samplesRead);
|
|
||||||
memmove(inputBuffer, ((uint8_t *)inputBuffer) + buffer_adder, samplesRead * sizeof(float));
|
|
||||||
bitsPerSample = 32;
|
|
||||||
bytesReadFromInput = samplesRead * sizeof(float);
|
|
||||||
isUnsigned = NO;
|
|
||||||
isFloat = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
[BadSampleCleaner cleanSamples:(float *)inputBuffer
|
|
||||||
amount:bytesReadFromInput / sizeof(float)
|
|
||||||
location:@"post int to float conversion"];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input now contains bytesReadFromInput worth of floats, in the input sample rate
|
// Input now contains bytesReadFromInput worth of floats, in the input sample rate
|
||||||
inpSize = bytesReadFromInput;
|
inpSize = bytesReadFromInput;
|
||||||
inpOffset = 0;
|
inpOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inpOffset != inpSize && floatOffset == floatSize) {
|
ioNumberPackets = (UInt32)(inpSize - inpOffset);
|
||||||
#if DSD_DECIMATE
|
|
||||||
const float scaleModifier = (inputFormat.mBitsPerChannel == 1) ? 0.5f : 1.0f;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t inputSamples = (inpSize - inpOffset) / floatFormat.mBytesPerPacket;
|
ioNumberPackets -= ioNumberPackets % floatFormat.mBytesPerPacket;
|
||||||
|
|
||||||
ioNumberPackets = (UInt32)inputSamples;
|
|
||||||
|
|
||||||
ioNumberPackets = (ioNumberPackets + 255) & ~255;
|
|
||||||
|
|
||||||
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
|
||||||
if(newSize < (ioNumberPackets * dmFloatFormat.mBytesPerPacket))
|
|
||||||
newSize = ioNumberPackets * dmFloatFormat.mBytesPerPacket;
|
|
||||||
if(!floatBuffer || floatBufferSize < newSize)
|
|
||||||
floatBuffer = realloc(floatBuffer, floatBufferSize = newSize * 3);
|
|
||||||
|
|
||||||
if(stopping) {
|
|
||||||
convertEntered = NO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t inputDone = 0;
|
|
||||||
size_t outputDone = 0;
|
|
||||||
|
|
||||||
memcpy(floatBuffer, (((uint8_t *)inputBuffer) + inpOffset), inputSamples * floatFormat.mBytesPerPacket);
|
|
||||||
inputDone = inputSamples;
|
|
||||||
outputDone = inputSamples;
|
|
||||||
|
|
||||||
inpOffset += inputDone * floatFormat.mBytesPerPacket;
|
|
||||||
|
|
||||||
amountReadFromFC = (int)(outputDone * floatFormat.mBytesPerPacket);
|
|
||||||
|
|
||||||
scale_by_volume((float *)floatBuffer, amountReadFromFC / sizeof(float), volumeScale
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
* scaleModifier
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
|
|
||||||
floatSize = amountReadFromFC;
|
|
||||||
floatOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(floatOffset == floatSize)
|
|
||||||
goto tryagain;
|
|
||||||
|
|
||||||
ioNumberPackets = (UInt32)(floatSize - floatOffset);
|
|
||||||
|
|
||||||
ioNumberPackets -= ioNumberPackets % dmFloatFormat.mBytesPerPacket;
|
|
||||||
|
|
||||||
if(ioNumberPackets) {
|
if(ioNumberPackets) {
|
||||||
AudioChunk *chunk = [[AudioChunk alloc] init];
|
AudioChunk *chunk = [[AudioChunk alloc] init];
|
||||||
|
@ -716,10 +188,10 @@ tryagain:
|
||||||
if(nodeChannelConfig) {
|
if(nodeChannelConfig) {
|
||||||
[chunk setChannelConfig:nodeChannelConfig];
|
[chunk setChannelConfig:nodeChannelConfig];
|
||||||
}
|
}
|
||||||
[chunk assignSamples:floatBuffer frameCount:ioNumberPackets / dmFloatFormat.mBytesPerPacket];
|
scale_by_volume((float *)(((uint8_t *)inputBuffer) + inpOffset), ioNumberPackets / sizeof(float), volumeScale);
|
||||||
|
[chunk assignSamples:(((uint8_t *)inputBuffer) + inpOffset) frameCount:ioNumberPackets / floatFormat.mBytesPerPacket];
|
||||||
|
|
||||||
floatOffset += ioNumberPackets;
|
inpOffset += ioNumberPackets;
|
||||||
amountRead += ioNumberPackets;
|
|
||||||
convertEntered = NO;
|
convertEntered = NO;
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
@ -801,16 +273,6 @@ static float db_to_scale(float db) {
|
||||||
if((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32)) || (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64)))
|
if((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32)) || (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64)))
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
// These are really placeholders, as we're doing everything internally now
|
|
||||||
if(lossless &&
|
|
||||||
inputFormat.mBitsPerChannel == 16 &&
|
|
||||||
inputFormat.mChannelsPerFrame == 2 &&
|
|
||||||
inputFormat.mSampleRate == 44100) {
|
|
||||||
// possibly HDCD, run through decoder
|
|
||||||
hdcd_decoder = calloc(1, sizeof(hdcd_state_stereo_t));
|
|
||||||
hdcd_reset_stereo((hdcd_state_stereo_t *)hdcd_decoder, 44100);
|
|
||||||
}
|
|
||||||
|
|
||||||
floatFormat = inputFormat;
|
floatFormat = inputFormat;
|
||||||
floatFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
floatFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
||||||
floatFormat.mBitsPerChannel = 32;
|
floatFormat.mBitsPerChannel = 32;
|
||||||
|
@ -821,27 +283,15 @@ static float db_to_scale(float db) {
|
||||||
if(inputFormat.mBitsPerChannel == 1) {
|
if(inputFormat.mBitsPerChannel == 1) {
|
||||||
// Decimate this for speed
|
// Decimate this for speed
|
||||||
floatFormat.mSampleRate *= 1.0 / 8.0;
|
floatFormat.mSampleRate *= 1.0 / 8.0;
|
||||||
dsd2pcmCount = floatFormat.mChannelsPerFrame;
|
|
||||||
dsd2pcm = (void **)calloc(dsd2pcmCount, sizeof(void *));
|
|
||||||
dsd2pcm[0] = dsd2pcm_alloc();
|
|
||||||
dsd2pcmLatency = dsd2pcm_latency(dsd2pcm[0]);
|
|
||||||
for(size_t i = 1; i < dsd2pcmCount; ++i) {
|
|
||||||
dsd2pcm[i] = dsd2pcm_dup(dsd2pcm[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inpOffset = 0;
|
inpOffset = 0;
|
||||||
inpSize = 0;
|
inpSize = 0;
|
||||||
|
|
||||||
floatOffset = 0;
|
|
||||||
floatSize = 0;
|
|
||||||
|
|
||||||
// This is a post resampler, post-down/upmix format
|
// This is a post resampler, post-down/upmix format
|
||||||
|
|
||||||
dmFloatFormat = floatFormat;
|
nodeFormat = floatFormat;
|
||||||
|
|
||||||
nodeFormat = dmFloatFormat;
|
|
||||||
nodeChannelConfig = inputChannelConfig;
|
nodeChannelConfig = inputChannelConfig;
|
||||||
|
|
||||||
PrintStreamDesc(&inf);
|
PrintStreamDesc(&inf);
|
||||||
|
@ -888,32 +338,13 @@ static float db_to_scale(float db) {
|
||||||
while(convertEntered) {
|
while(convertEntered) {
|
||||||
usleep(500);
|
usleep(500);
|
||||||
}
|
}
|
||||||
if(hdcd_decoder) {
|
|
||||||
free(hdcd_decoder);
|
|
||||||
hdcd_decoder = NULL;
|
|
||||||
}
|
|
||||||
#if DSD_DECIMATE
|
|
||||||
if(dsd2pcm && dsd2pcmCount) {
|
|
||||||
for(size_t i = 0; i < dsd2pcmCount; ++i) {
|
|
||||||
dsd2pcm_free(dsd2pcm[i]);
|
|
||||||
dsd2pcm[i] = NULL;
|
|
||||||
}
|
|
||||||
free(dsd2pcm);
|
|
||||||
dsd2pcm = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(floatBuffer) {
|
|
||||||
free(floatBuffer);
|
|
||||||
floatBuffer = NULL;
|
|
||||||
floatBufferSize = 0;
|
|
||||||
}
|
|
||||||
if(inputBuffer) {
|
if(inputBuffer) {
|
||||||
free(inputBuffer);
|
free(inputBuffer);
|
||||||
inputBuffer = NULL;
|
inputBuffer = NULL;
|
||||||
inputBufferSize = 0;
|
inputBufferSize = 0;
|
||||||
}
|
}
|
||||||
floatOffset = 0;
|
inpOffset = 0;
|
||||||
floatSize = 0;
|
inpSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (double)secondsBuffered {
|
- (double)secondsBuffered {
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
- (void)writeData:(const void *_Nonnull)ptr amount:(size_t)a;
|
- (void)writeData:(const void *_Nonnull)ptr amount:(size_t)a;
|
||||||
- (void)writeChunk:(AudioChunk *_Nonnull)chunk;
|
- (void)writeChunk:(AudioChunk *_Nonnull)chunk;
|
||||||
- (AudioChunk *_Nonnull)readChunk:(size_t)maxFrames;
|
- (AudioChunk *_Nonnull)readChunk:(size_t)maxFrames;
|
||||||
|
- (AudioChunk *_Nonnull)readChunkAsFloat32:(size_t)maxFrames;
|
||||||
|
|
||||||
- (BOOL)peekFormat:(AudioStreamBasicDescription *_Nonnull)format channelConfig:(uint32_t *_Nonnull)config;
|
- (BOOL)peekFormat:(AudioStreamBasicDescription *_Nonnull)format channelConfig:(uint32_t *_Nonnull)config;
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,41 @@
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (AudioChunk *)readChunkAsFloat32:(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] removeSamplesAsFloat32:maxFrames];
|
||||||
|
}
|
||||||
|
|
||||||
|
[accessLock unlock];
|
||||||
|
|
||||||
|
if([ret frameCount]) {
|
||||||
|
[[previousNode semaphore] signal];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)launchThread {
|
- (void)launchThread {
|
||||||
[NSThread detachNewThreadSelector:@selector(threadEntry:) toTarget:self withObject:nil];
|
[NSThread detachNewThreadSelector:@selector(threadEntry:) toTarget:self withObject:nil];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue