// // ConverterNode.m // Cog // // Created by Zaphod Beeblebrox on 8/2/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "ConverterNode.h" #import "BufferChain.h" #import "OutputNode.h" #import "Logging.h" #import <audio/conversion/s16_to_float.h> #import <audio/conversion/s32_to_float.h> #import "lpc.h" #import "util.h" #import "hdcd_decode2.h" #import <TargetConditionals.h> #if TARGET_CPU_X86 || TARGET_CPU_X86_64 #include <emmintrin.h> #elif TARGET_CPU_ARM || TARGET_CPU_ARM64 #include <arm_neon.h> #endif void PrintStreamDesc (AudioStreamBasicDescription *inDesc) { if (!inDesc) { DLog (@"Can't print a NULL desc!\n"); return; } 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"); } @implementation ConverterNode @synthesize inputFormat; - (id)initWithController:(id)c previous:(id)p { self = [super initWithController:c previous:p]; if (self) { rgInfo = nil; resampler = NULL; resampler_data = NULL; inputBuffer = NULL; inputBufferSize = 0; floatBuffer = NULL; floatBufferSize = 0; stopping = NO; convertEntered = NO; paused = NO; outputFormatChanged = NO; skipResampler = YES; refillNode = nil; originalPreviousNode = nil; extrapolateBuffer = NULL; extrapolateBufferSize = 0; dsd2pcm = NULL; dsd2pcmCount = 0; outputResampling = @""; hdcd_decoder = NULL; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil]; } return self; } static const float STEREO_DOWNMIX[8-2][8][2]={ /*3.0*/ { {0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F} }, /*quadrophonic*/ { {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F} }, /*5.0*/ { {0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F}, {0.3254F,0.5636F} }, /*5.1*/ { {0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F}, {0.2645F,0.4582F} }, /*6.1*/ { {0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F}, {0.3943F,0.2277F},{0.2277F,0.3943F} }, /*7.1*/ { {0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F}, {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F} } }; static void downmix_to_stereo(float * buffer, int channels, size_t count) { if (channels >= 3 && channels <= 8) for (size_t i = 0; i < count; ++i) { float left = 0, right = 0; for (int j = 0; j < channels; ++j) { left += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0]; right += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1]; } buffer[i * 2 + 0] = left; buffer[i * 2 + 1] = right; } } static void downmix_to_mono(float * buffer, int channels, size_t count) { if (channels >= 3 && channels <= 8) { downmix_to_stereo(buffer, channels, count); channels = 2; } float invchannels = 1.0 / (float)channels; for (size_t i = 0; i < count; ++i) { float sample = 0; for (int j = 0; j < channels; ++j) { sample += buffer[i * channels + j]; } buffer[i] = sample * invchannels; } } static void upmix(float * buffer, int inchannels, int outchannels, size_t count) { for (ssize_t i = count - 1; i >= 0; --i) { if (inchannels == 1 && outchannels == 2) { // upmix mono to stereo float sample = buffer[i]; 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; for (int j = 0; j < 2; ++j) { buffer[i * outchannels + j] = 0; } for (int j = 3; j < outchannels; ++j) { buffer[i * outchannels + j] = 0; } } 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; } else { // upmix N channels to N channels plus silence the empty channels 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; } } } } void scale_by_volume(float * buffer, size_t count, float volume) { if ( volume != 1.0 ) { #if TARGET_CPU_X86 || TARGET_CPU_X86_64 if ( count >= 8 ) { __m128 vgf = _mm_set1_ps(volume); while ( count >= 8 ) { __m128 input = _mm_loadu_ps(buffer); __m128 input2 = _mm_loadu_ps(buffer + 4); __m128 output = _mm_mul_ps(input, vgf); __m128 output2 = _mm_mul_ps(input2, vgf); _mm_storeu_ps(buffer + 0, output); _mm_storeu_ps(buffer + 4, output2); buffer += 8; count -= 8; } } #elif TARGET_CPU_ARM || TARGET_CPU_ARM64 if ( count >= 8 ) { float32x4_t vgf = vdupq_n_f32(volume); while ( count >= 8 ) { float32x4x2_t oreg; float32x4x2_t inreg = vld1q_f32_x2(buffer); oreg.val[0] = vmulq_f32(inreg.val[0], vgf); oreg.val[1] = vmulq_f32(inreg.val[1], vgf); vst1q_f32_x2(buffer, oreg); buffer += 8; count -= 8; } } #endif for (size_t i = 0; i < count; ++i) buffer[i] *= volume; } } /** * 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)); 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; float* FILT_LOOKUP_TABLE = state->FILT_LOOKUP_TABLE; double * 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; uint8_t* 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, 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); } } 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) { for (size_t i = 0; i < count; ++i) { output[i] = (float)(input[i]); } } 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; 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; } } -(void)process { char writeBuf[CHUNK_SIZE]; // 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) { int amountConverted = [self convert:writeBuf amount:CHUNK_SIZE]; if (!amountConverted) { if (paused) { while (paused) usleep(500); continue; } 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 isLossless:rememberedLossless]; continue; } else break; } [self writeData:writeBuf amount:amountConverted]; } } - (int)convert:(void *)dest amount:(int)amount { UInt32 ioNumberPackets; int amountReadFromFC; int amountRead = 0; int amountToSkip; if (stopping) return 0; convertEntered = YES; tryagain: if (stopping || [self shouldContinue] == NO) { convertEntered = NO; return amountRead; } amountReadFromFC = 0; if (floatOffset == floatSize) // skip this step if there's still float buffered while (inpOffset == inpSize) { size_t samplesRead = 0; BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat); BOOL isUnsigned = !isFloat && !(inputFormat.mFormatFlags & kAudioFormatFlagIsSignedInteger); // Approximately the most we want on input ioNumberPackets = (amount - amountRead) / outputFormat.mBytesPerPacket; if (!skipResampler && ioNumberPackets < PRIME_LEN_) ioNumberPackets = PRIME_LEN_; // We want to upscale this count if the ratio is below zero if (sampleRatio < 1.0) { ioNumberPackets = ((uint32_t)(ioNumberPackets / sampleRatio) + 15) & ~15; } amountToSkip = 0; if (dsd2pcm && !is_preextrapolated_) { amountToSkip = dsd2pcmLatency * inputFormat.mBytesPerPacket; ioNumberPackets += dsd2pcmLatency; } size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket; if (!inputBuffer || inputBufferSize < newSize) inputBuffer = realloc( inputBuffer, inputBufferSize = newSize * 3 ); ssize_t amountToWrite = ioNumberPackets * inputFormat.mBytesPerPacket; amountToWrite -= amountToSkip; ssize_t bytesReadFromInput = 0; while (bytesReadFromInput < amountToWrite && !stopping && [self shouldContinue] == YES && [self endOfStream] == NO) { size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)]; bytesReadFromInput += bytesRead; if (!bytesRead) { if (refillNode) [self setEndOfStream:YES]; else usleep(500); } } // Pad end of track with input format silence if (stopping || [self shouldContinue] == NO || [self endOfStream] == YES) { if (!skipResampler && !is_postextrapolated_) { if (dsd2pcm) { amountToSkip = dsd2pcmLatency * inputFormat.mBytesPerPacket; memset(inputBuffer + bytesReadFromInput, 0x55, amountToSkip); bytesReadFromInput += amountToSkip; amountToSkip = 0; } is_postextrapolated_ = 1; } else if (!is_postextrapolated_ && dsd2pcm) { is_postextrapolated_ = 3; } } if (!bytesReadFromInput) { convertEntered = NO; return amountRead; } bytesReadFromInput += amountToSkip; if (dsd2pcm && amountToSkip) { memset(inputBuffer, 0x55, amountToSkip); dsdLatencyEaten = (int)ceil(dsd2pcmLatency * sampleRatio); } if (bytesReadFromInput && (inputFormat.mFormatFlags & kAudioFormatFlagIsBigEndian)) { // Time for endian swap! convert_be_to_le(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(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead); memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float)); bytesReadFromInput = samplesRead * sizeof(float); } if (bytesReadFromInput && !isFloat) { float gain = 1.0; size_t bitsPerSample = inputFormat.mBitsPerChannel; if (bitsPerSample == 1) { samplesRead = bytesReadFromInput / inputFormat.mBytesPerPacket; convert_dsd_to_f32(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead, inputFormat.mChannelsPerFrame, dsd2pcm); memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * inputFormat.mChannelsPerFrame * sizeof(float)); bitsPerSample = 32; bytesReadFromInput = samplesRead * inputFormat.mChannelsPerFrame * sizeof(float); isFloat = YES; } else if (bitsPerSample <= 8) { samplesRead = bytesReadFromInput; if (!isUnsigned) 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 (hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale samplesRead = bytesReadFromInput / 2; if (isUnsigned) convert_u16_to_s16(inputBuffer, samplesRead); convert_s16_to_hdcd_input(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead); memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * 4); hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, 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(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, gain); memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float)); bitsPerSample = 32; bytesReadFromInput = samplesRead * sizeof(float); isUnsigned = NO; isFloat = YES; } } // Extrapolate start if (!skipResampler && !is_preextrapolated_) { size_t samples_in_buffer = bytesReadFromInput / floatFormat.mBytesPerPacket; size_t prime = min(samples_in_buffer, PRIME_LEN_); size_t newSize = N_samples_to_add_ * floatFormat.mBytesPerPacket; newSize += bytesReadFromInput; if (newSize > inputBufferSize) { inputBuffer = realloc(inputBuffer, inputBufferSize = newSize * 3); } memmove(inputBuffer + N_samples_to_add_ * floatFormat.mBytesPerPacket, inputBuffer, bytesReadFromInput); // Great padding! And we want to eat more, based on the resampler filter size int samplesLatency = (int)ceil(resampler->latency(resampler_data) * sampleRatio); // Guess what? This extrapolates into the memory before its input pointer! lpc_extrapolate_bkwd(inputBuffer + N_samples_to_add_ * floatFormat.mBytesPerPacket, samples_in_buffer, prime, floatFormat.mChannelsPerFrame, LPC_ORDER, N_samples_to_add_, &extrapolateBuffer, &extrapolateBufferSize); bytesReadFromInput += N_samples_to_add_ * floatFormat.mBytesPerPacket; latencyEaten = N_samples_to_drop_ + samplesLatency; if (dsd2pcm) latencyEaten += dsdLatencyEaten; is_preextrapolated_ = 2; } else if (dsd2pcm && !is_preextrapolated_) { latencyEaten = dsd2pcmLatency; is_preextrapolated_ = 3; } if (is_postextrapolated_ == 1) { size_t samples_in_buffer = bytesReadFromInput / floatFormat.mBytesPerPacket; size_t prime = min(samples_in_buffer, PRIME_LEN_); size_t newSize = bytesReadFromInput; newSize += N_samples_to_add_ * floatFormat.mBytesPerPacket; if (newSize > inputBufferSize) { inputBuffer = realloc(inputBuffer, inputBufferSize = newSize * 3); } // And now that we've reached the end, we eat slightly less, due to the filter size int samplesLatency = (int)resampler->latency(resampler_data); if (dsd2pcm) samplesLatency += dsd2pcmLatency; samplesLatency = (int)ceil(samplesLatency * sampleRatio); lpc_extrapolate_fwd(inputBuffer, samples_in_buffer, prime, floatFormat.mChannelsPerFrame, LPC_ORDER, N_samples_to_add_, &extrapolateBuffer, &extrapolateBufferSize); bytesReadFromInput += N_samples_to_add_ * floatFormat.mBytesPerPacket; latencyEatenPost = N_samples_to_drop_; if (latencyEatenPost > samplesLatency) { latencyEatenPost -= samplesLatency; } else { latencyEatenPost = 0; } is_postextrapolated_ = 2; } else if (is_postextrapolated_ == 3) { // skip end of DSD output latencyEatenPost = dsd2pcmLatency; } // Input now contains bytesReadFromInput worth of floats, in the input sample rate inpSize = bytesReadFromInput; inpOffset = 0; } if (inpOffset != inpSize && floatOffset == floatSize) { struct resampler_data src_data; size_t inputSamples = (inpSize - inpOffset) / floatFormat.mBytesPerPacket; ioNumberPackets = (UInt32)inputSamples; ioNumberPackets = (UInt32)ceil((float)ioNumberPackets * sampleRatio); 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; } src_data.data_out = floatBuffer; src_data.output_frames = 0; src_data.data_in = (float*)(((uint8_t*)inputBuffer) + inpOffset); src_data.input_frames = inputSamples; src_data.ratio = sampleRatio; if (!skipResampler) { resampler->process(resampler_data, &src_data); } else { memcpy(src_data.data_out, src_data.data_in, inputSamples * floatFormat.mBytesPerPacket); src_data.output_frames = inputSamples; } inpOffset += inputSamples * floatFormat.mBytesPerPacket; if (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; } } else if (latencyEatenPost) { if (src_data.output_frames > latencyEatenPost) { src_data.output_frames -= latencyEatenPost; } else { src_data.output_frames = 0; } latencyEatenPost = 0; } amountReadFromFC = (int)(src_data.output_frames * floatFormat.mBytesPerPacket); scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale); if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 ) { int samples = amountReadFromFC / floatFormat.mBytesPerFrame; downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples ); amountReadFromFC = samples * sizeof(float) * 2; } 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; } floatSize = amountReadFromFC; floatOffset = 0; } if (floatOffset == floatSize) goto tryagain; ioNumberPackets = (amount - amountRead); if (ioNumberPackets > (floatSize - floatOffset)) ioNumberPackets = (UInt32)(floatSize - floatOffset); memcpy(dest + amountRead, floatBuffer + floatOffset, ioNumberPackets); floatOffset += ioNumberPackets; amountRead += ioNumberPackets; convertEntered = NO; return amountRead; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { DLog(@"SOMETHING CHANGED!"); if ([keyPath isEqual:@"values.volumeScaling"]) { //User reset the volume scaling option [self refreshVolumeScaling]; } else if ([keyPath isEqual:@"values.outputResampling"]) { // Reset resampler if (resampler && resampler_data) { NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:@"outputResampling"]; if (![value isEqualToString:outputResampling]) [self inputFormatDidChange:inputFormat]; } } } 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; } - (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf outputFormat:(AudioStreamBasicDescription)outf isLossless:(BOOL)lossless { //Make the converter inputFormat = inf; outputFormat = outf; rememberedLossless = lossless; // 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; // 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.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; floatFormat.mBitsPerChannel = 32; floatFormat.mBytesPerFrame = (32/8)*floatFormat.mChannelsPerFrame; floatFormat.mBytesPerPacket = floatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket; if (inputFormat.mBitsPerChannel == 1) { // Decimate this for speed floatFormat.mSampleRate *= 1.0 / 8.0; dsd2pcmCount = floatFormat.mChannelsPerFrame; dsd2pcm = 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]); } } inpOffset = 0; inpSize = 0; floatOffset = 0; floatSize = 0; // This is a post resampler, post-down/upmix format dmFloatFormat = floatFormat; dmFloatFormat.mSampleRate = outputFormat.mSampleRate; dmFloatFormat.mChannelsPerFrame = outputFormat.mChannelsPerFrame; dmFloatFormat.mBytesPerFrame = (32/8)*dmFloatFormat.mChannelsPerFrame; dmFloatFormat.mBytesPerPacket = dmFloatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket; convert_s16_to_float_init_simd(); convert_s32_to_float_init_simd(); skipResampler = outputFormat.mSampleRate == floatFormat.mSampleRate; sampleRatio = (double)outputFormat.mSampleRate / (double)floatFormat.mSampleRate; if (!skipResampler) { enum resampler_quality quality = RESAMPLER_QUALITY_DONTCARE; 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; outputResampling = resampling; if (!retro_resampler_realloc(&resampler_data, &resampler, "sinc", quality, inputFormat.mChannelsPerFrame, sampleRatio)) { return NO; } PRIME_LEN_ = max(floatFormat.mSampleRate/20, 1024u); PRIME_LEN_ = min(PRIME_LEN_, 16384u); PRIME_LEN_ = max(PRIME_LEN_, 2*LPC_ORDER + 1); N_samples_to_add_ = floatFormat.mSampleRate; N_samples_to_drop_ = outputFormat.mSampleRate; samples_len(&N_samples_to_add_, &N_samples_to_drop_, 20, 8192u); is_preextrapolated_ = 0; is_postextrapolated_ = 0; } latencyEaten = 0; latencyEatenPost = 0; PrintStreamDesc(&inf); PrintStreamDesc(&outf); [self refreshVolumeScaling]; // Move this here so process call isn't running the resampler until it's allocated stopping = NO; convertEntered = NO; paused = NO; outputFormatChanged = NO; return YES; } - (void)dealloc { DLog(@"Decoder dealloc"); [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling"]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputResampling"]; paused = NO; [self cleanUp]; } - (void)setOutputFormat:(AudioStreamBasicDescription)format { DLog(@"SETTING OUTPUT FORMAT!"); previousOutputFormat = outputFormat; outputFormat = format; outputFormatChanged = YES; } - (void)inputFormatDidChange:(AudioStreamBasicDescription)format { DLog(@"FORMAT CHANGED"); paused = YES; [self cleanUp]; if (outputFormatChanged && ![buffer isEmpty] && memcmp(&outputFormat, &previousOutputFormat, sizeof(outputFormat)) != 0) { // 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 isLossless:rememberedLossless]; } else { [self setupWithInputFormat:format outputFormat:outputFormat isLossless:rememberedLossless]; } } - (void)setRGInfo:(NSDictionary *)rgi { DLog(@"Setting ReplayGain info"); rgInfo = rgi; [self refreshVolumeScaling]; } - (void)cleanUp { stopping = YES; while (convertEntered) { usleep(500); } if (hdcd_decoder) { free(hdcd_decoder); hdcd_decoder = NULL; } if (resampler && resampler_data) { resampler->free(resampler, resampler_data); resampler = NULL; resampler_data = NULL; } if (dsd2pcm && dsd2pcmCount) { for (size_t i = 0; i < dsd2pcmCount; ++i) { dsd2pcm_free(dsd2pcm[i]); dsd2pcm[i] = NULL; } free(dsd2pcm); dsd2pcm = NULL; } if (extrapolateBuffer) { free(extrapolateBuffer); extrapolateBuffer = NULL; extrapolateBufferSize = 0; } if (floatBuffer) { free(floatBuffer); floatBuffer = NULL; floatBufferSize = 0; } if (inputBuffer) { free(inputBuffer); inputBuffer = NULL; inputBufferSize = 0; } floatOffset = 0; floatSize = 0; } - (double) secondsBuffered { return ((double)[buffer bufferedLength] / (outputFormat.mSampleRate * outputFormat.mBytesPerPacket)); } @end