Cog Audio: Implement HDCD decoding
parent
f2feb3bcd7
commit
6f0a737123
|
@ -75,4 +75,6 @@
|
||||||
|
|
||||||
- (double)secondsBuffered;
|
- (double)secondsBuffered;
|
||||||
|
|
||||||
|
- (void)sustainHDCD;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -231,7 +231,7 @@
|
||||||
- (double)secondsBuffered
|
- (double)secondsBuffered
|
||||||
{
|
{
|
||||||
double duration = 0.0;
|
double duration = 0.0;
|
||||||
OutputNode * outputNode = [controller output];
|
OutputNode * outputNode = (OutputNode *) [controller output];
|
||||||
duration += [outputNode secondsBuffered];
|
duration += [outputNode secondsBuffered];
|
||||||
|
|
||||||
Node * node = [self finalNode];
|
Node * node = [self finalNode];
|
||||||
|
@ -242,4 +242,10 @@
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)sustainHDCD
|
||||||
|
{
|
||||||
|
OutputNode * outputNode = (OutputNode *) [controller output];
|
||||||
|
[outputNode sustainHDCD];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -71,6 +71,8 @@
|
||||||
id __weak originalPreviousNode;
|
id __weak originalPreviousNode;
|
||||||
|
|
||||||
NSString *outputResampling;
|
NSString *outputResampling;
|
||||||
|
|
||||||
|
void *hdcd_decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property AudioStreamBasicDescription inputFormat;
|
@property AudioStreamBasicDescription inputFormat;
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#import "lpc.h"
|
#import "lpc.h"
|
||||||
#import "util.h"
|
#import "util.h"
|
||||||
|
|
||||||
|
#import "hdcd_decode2.h"
|
||||||
|
|
||||||
#import <TargetConditionals.h>
|
#import <TargetConditionals.h>
|
||||||
|
|
||||||
#if TARGET_CPU_X86 || TARGET_CPU_X86_64
|
#if TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||||
|
@ -81,6 +83,8 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
||||||
|
|
||||||
outputResampling = @"";
|
outputResampling = @"";
|
||||||
|
|
||||||
|
hdcd_decoder = NULL;
|
||||||
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
||||||
}
|
}
|
||||||
|
@ -542,7 +546,7 @@ static void dsd2pcm_process(void * _state, uint8_t * src, size_t sofs, size_t si
|
||||||
state->fpos = fpos;
|
state->fpos = fpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_dsd_to_f32(float *output, uint8_t *input, size_t count, size_t channels, void ** dsd2pcm)
|
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)
|
for (size_t channel = 0; channel < channels; ++channel)
|
||||||
{
|
{
|
||||||
|
@ -550,7 +554,7 @@ static void convert_dsd_to_f32(float *output, uint8_t *input, size_t count, size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_u8_to_s16(int16_t *output, uint8_t *input, size_t 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)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -560,7 +564,7 @@ static void convert_u8_to_s16(int16_t *output, uint8_t *input, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_s8_to_s16(int16_t *output, uint8_t *input, size_t count)
|
static void convert_s8_to_s16(int16_t *output, const uint8_t *input, size_t count)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -577,7 +581,15 @@ static void convert_u16_to_s16(int16_t *buffer, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_s24_to_s32(int32_t *output, uint8_t *input, size_t count)
|
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)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -586,7 +598,7 @@ static void convert_s24_to_s32(int32_t *output, uint8_t *input, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_u24_to_s32(int32_t *output, uint8_t *input, size_t count)
|
static void convert_u24_to_s32(int32_t *output, const uint8_t *input, size_t count)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -603,7 +615,7 @@ static void convert_u32_to_s32(int32_t *buffer, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_f64_to_f32(float *output, double *input, size_t count)
|
static void convert_f64_to_f32(float *output, const double *input, size_t count)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -822,6 +834,7 @@ tryagain:
|
||||||
|
|
||||||
if (bytesReadFromInput && !isFloat)
|
if (bytesReadFromInput && !isFloat)
|
||||||
{
|
{
|
||||||
|
float gain = 1.0;
|
||||||
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
||||||
if (bitsPerSample == 1) {
|
if (bitsPerSample == 1) {
|
||||||
samplesRead = bytesReadFromInput / inputFormat.mBytesPerPacket;
|
samplesRead = bytesReadFromInput / inputFormat.mBytesPerPacket;
|
||||||
|
@ -842,7 +855,23 @@ tryagain:
|
||||||
bytesReadFromInput = samplesRead * 2;
|
bytesReadFromInput = samplesRead * 2;
|
||||||
isUnsigned = NO;
|
isUnsigned = NO;
|
||||||
}
|
}
|
||||||
if (bitsPerSample <= 16) {
|
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;
|
samplesRead = bytesReadFromInput / 2;
|
||||||
if (isUnsigned)
|
if (isUnsigned)
|
||||||
convert_u16_to_s16(inputBuffer, samplesRead);
|
convert_u16_to_s16(inputBuffer, samplesRead);
|
||||||
|
@ -868,7 +897,7 @@ tryagain:
|
||||||
samplesRead = bytesReadFromInput / 4;
|
samplesRead = bytesReadFromInput / 4;
|
||||||
if (isUnsigned)
|
if (isUnsigned)
|
||||||
convert_u32_to_s32(inputBuffer, samplesRead);
|
convert_u32_to_s32(inputBuffer, samplesRead);
|
||||||
convert_s32_to_float(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead, 1.0);
|
convert_s32_to_float(inputBuffer + bytesReadFromInput, inputBuffer, samplesRead, gain);
|
||||||
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float));
|
memmove(inputBuffer, inputBuffer + bytesReadFromInput, samplesRead * sizeof(float));
|
||||||
bitsPerSample = 32;
|
bitsPerSample = 32;
|
||||||
bytesReadFromInput = samplesRead * sizeof(float);
|
bytesReadFromInput = samplesRead * sizeof(float);
|
||||||
|
@ -1137,6 +1166,13 @@ static float db_to_scale(float db)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
// These are really placeholders, as we're doing everything internally now
|
// These are really placeholders, as we're doing everything internally now
|
||||||
|
if (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;
|
||||||
|
@ -1301,6 +1337,11 @@ static float db_to_scale(float db)
|
||||||
{
|
{
|
||||||
usleep(500);
|
usleep(500);
|
||||||
}
|
}
|
||||||
|
if (hdcd_decoder)
|
||||||
|
{
|
||||||
|
free(hdcd_decoder);
|
||||||
|
hdcd_decoder = NULL;
|
||||||
|
}
|
||||||
if (resampler && resampler_data)
|
if (resampler && resampler_data)
|
||||||
{
|
{
|
||||||
resampler->free(resampler, resampler_data);
|
resampler->free(resampler, resampler_data);
|
||||||
|
|
|
@ -60,4 +60,6 @@
|
||||||
|
|
||||||
- (BOOL)isPaused;
|
- (BOOL)isPaused;
|
||||||
|
|
||||||
|
- (void)sustainHDCD;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -171,4 +171,9 @@
|
||||||
{
|
{
|
||||||
[controller endEqualizer:eq];
|
[controller endEqualizer:eq];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)sustainHDCD
|
||||||
|
{
|
||||||
|
[output sustainHDCD];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
835C88AA2797D4D400E28EAE /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C88A62797D4D400E28EAE /* lpc.c */; };
|
835C88AA2797D4D400E28EAE /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C88A62797D4D400E28EAE /* lpc.c */; };
|
||||||
835C88AB2797D4D400E28EAE /* lpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88A72797D4D400E28EAE /* lpc.h */; };
|
835C88AB2797D4D400E28EAE /* lpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88A72797D4D400E28EAE /* lpc.h */; };
|
||||||
835C88AD2797DA5800E28EAE /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88AC2797DA5800E28EAE /* util.h */; };
|
835C88AD2797DA5800E28EAE /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88AC2797DA5800E28EAE /* util.h */; };
|
||||||
|
835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88AF279811A500E28EAE /* hdcd_decode2.h */; };
|
||||||
|
835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C88B0279811A500E28EAE /* hdcd_decode2.c */; };
|
||||||
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
|
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
|
||||||
8389F270278E64590074164C /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 8389F225278E64590074164C /* config.h */; };
|
8389F270278E64590074164C /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 8389F225278E64590074164C /* config.h */; };
|
||||||
8389F279278E64590074164C /* utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8389F236278E64590074164C /* utf.h */; };
|
8389F279278E64590074164C /* utf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8389F236278E64590074164C /* utf.h */; };
|
||||||
|
@ -164,6 +166,8 @@
|
||||||
835C88A62797D4D400E28EAE /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
835C88A62797D4D400E28EAE /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
||||||
835C88A72797D4D400E28EAE /* lpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lpc.h; sourceTree = "<group>"; };
|
835C88A72797D4D400E28EAE /* lpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lpc.h; sourceTree = "<group>"; };
|
||||||
835C88AC2797DA5800E28EAE /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = util.h; path = ThirdParty/lvqcl/util.h; sourceTree = SOURCE_ROOT; };
|
835C88AC2797DA5800E28EAE /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = util.h; path = ThirdParty/lvqcl/util.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
835C88AF279811A500E28EAE /* hdcd_decode2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hdcd_decode2.h; sourceTree = "<group>"; };
|
||||||
|
835C88B0279811A500E28EAE /* hdcd_decode2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hdcd_decode2.c; sourceTree = "<group>"; };
|
||||||
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||||
8389F225278E64590074164C /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
|
8389F225278E64590074164C /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
|
||||||
8389F228278E64590074164C /* encoding_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = encoding_utf.c; sourceTree = "<group>"; };
|
8389F228278E64590074164C /* encoding_utf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = encoding_utf.c; sourceTree = "<group>"; };
|
||||||
|
@ -369,6 +373,7 @@
|
||||||
17D21CD80B8BE5B400D1EBDE /* ThirdParty */ = {
|
17D21CD80B8BE5B400D1EBDE /* ThirdParty */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
835C88AE279811A500E28EAE /* hdcd */,
|
||||||
835C88A22797D4D400E28EAE /* lvqcl */,
|
835C88A22797D4D400E28EAE /* lvqcl */,
|
||||||
8389F224278E64590074164C /* RetroArch */,
|
8389F224278E64590074164C /* RetroArch */,
|
||||||
17D21DC40B8BE79700D1EBDE /* CoreAudioUtils */,
|
17D21DC40B8BE79700D1EBDE /* CoreAudioUtils */,
|
||||||
|
@ -443,6 +448,15 @@
|
||||||
path = License;
|
path = License;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
835C88AE279811A500E28EAE /* hdcd */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
835C88AF279811A500E28EAE /* hdcd_decode2.h */,
|
||||||
|
835C88B0279811A500E28EAE /* hdcd_decode2.c */,
|
||||||
|
);
|
||||||
|
path = hdcd;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
8389F224278E64590074164C /* RetroArch */ = {
|
8389F224278E64590074164C /* RetroArch */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -766,6 +780,7 @@
|
||||||
839366671815923C006DD712 /* CogPluginMulti.h in Headers */,
|
839366671815923C006DD712 /* CogPluginMulti.h in Headers */,
|
||||||
17ADB13C0B97926D00257CA2 /* AudioSource.h in Headers */,
|
17ADB13C0B97926D00257CA2 /* AudioSource.h in Headers */,
|
||||||
8389F292278E64590074164C /* s16_to_float.h in Headers */,
|
8389F292278E64590074164C /* s16_to_float.h in Headers */,
|
||||||
|
835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */,
|
||||||
8389F279278E64590074164C /* utf.h in Headers */,
|
8389F279278E64590074164C /* utf.h in Headers */,
|
||||||
8389F288278E64590074164C /* retro_environment.h in Headers */,
|
8389F288278E64590074164C /* retro_environment.h in Headers */,
|
||||||
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */,
|
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */,
|
||||||
|
@ -864,6 +879,7 @@
|
||||||
17D21CE00B8BE5B400D1EBDE /* VirtualRingBuffer.m in Sources */,
|
17D21CE00B8BE5B400D1EBDE /* VirtualRingBuffer.m in Sources */,
|
||||||
8389F29D278E64590074164C /* s16_to_float.c in Sources */,
|
8389F29D278E64590074164C /* s16_to_float.c in Sources */,
|
||||||
8389F29B278E64590074164C /* sinc_resampler.c in Sources */,
|
8389F29B278E64590074164C /* sinc_resampler.c in Sources */,
|
||||||
|
835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */,
|
||||||
8389F299278E64590074164C /* memalign.c in Sources */,
|
8389F299278E64590074164C /* memalign.c in Sources */,
|
||||||
17D21CF40B8BE5EF00D1EBDE /* Semaphore.m in Sources */,
|
17D21CF40B8BE5EF00D1EBDE /* Semaphore.m in Sources */,
|
||||||
8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */,
|
8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */,
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
BOOL eqEnabled;
|
BOOL eqEnabled;
|
||||||
|
|
||||||
atomic_long bytesRendered;
|
atomic_long bytesRendered;
|
||||||
|
atomic_long bytesHdcdSustained;
|
||||||
|
|
||||||
BOOL listenerapplied;
|
BOOL listenerapplied;
|
||||||
|
|
||||||
|
@ -76,4 +77,6 @@
|
||||||
|
|
||||||
- (void)setEqualizerEnabled:(BOOL)enabled;
|
- (void)setEqualizerEnabled:(BOOL)enabled;
|
||||||
|
|
||||||
|
- (void)sustainHDCD;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -121,7 +121,19 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scaleBuffersByVolume(ioData, _self->volume);
|
float volumeScale = 1.0;
|
||||||
|
long sustained = atomic_load_explicit(&_self->bytesHdcdSustained, memory_order_relaxed);
|
||||||
|
if (sustained) {
|
||||||
|
if (sustained < amountRead) {
|
||||||
|
atomic_store(&_self->bytesHdcdSustained, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
atomic_fetch_sub(&_self->bytesHdcdSustained, amountRead);
|
||||||
|
}
|
||||||
|
volumeScale = 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleBuffersByVolume(ioData, _self->volume * volumeScale);
|
||||||
|
|
||||||
if (amountRead < amountToRead)
|
if (amountRead < amountToRead)
|
||||||
{
|
{
|
||||||
|
@ -150,6 +162,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
||||||
started = NO;
|
started = NO;
|
||||||
|
|
||||||
atomic_init(&bytesRendered, 0);
|
atomic_init(&bytesRendered, 0);
|
||||||
|
atomic_init(&bytesHdcdSustained, 0);
|
||||||
|
|
||||||
writeSemaphore = [[Semaphore alloc] init];
|
writeSemaphore = [[Semaphore alloc] init];
|
||||||
readSemaphore = [[Semaphore alloc] init];
|
readSemaphore = [[Semaphore alloc] init];
|
||||||
|
@ -767,4 +780,9 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
paused = NO;
|
paused = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)sustainHDCD
|
||||||
|
{
|
||||||
|
atomic_store(&bytesHdcdSustained, deviceFormat.mSampleRate * 10 * sizeof(float) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2010-2017, Christopher Snowhill,
|
||||||
|
All rights reserved.
|
||||||
|
Optimizations by Gumboot
|
||||||
|
Additional work by Burt P.
|
||||||
|
Original code reverse engineered from HDCD decoder library by Christopher Key,
|
||||||
|
which was likely reverse engineered from Windows Media Player.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. The names of its contributors may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
HDCD is High Definition Compatible Digital
|
||||||
|
http://wiki.hydrogenaud.io/index.php?title=High_Definition_Compatible_Digital
|
||||||
|
|
||||||
|
More information about HDCD-encoded audio CDs:
|
||||||
|
http://www.audiomisc.co.uk/HFN/HDCD/Enigma.html
|
||||||
|
http://www.audiomisc.co.uk/HFN/HDCD/Examined.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HDCD_DECODE2_H_
|
||||||
|
#define _HDCD_DECODE2_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t window;
|
||||||
|
unsigned char readahead;
|
||||||
|
|
||||||
|
/* arg is set when a packet prefix is found.
|
||||||
|
* control is the active control code, where
|
||||||
|
* bit 0-3: target_gain, 4-bit (3.1) fixed-point value
|
||||||
|
* bit 4 : peak_extend
|
||||||
|
* bit 5 : transient_filter
|
||||||
|
* bit 6,7: always zero */
|
||||||
|
unsigned char arg, control;
|
||||||
|
unsigned sustain, sustain_reset; /* code detect timer */
|
||||||
|
|
||||||
|
int running_gain; /* 11-bit (3.8) fixed point, extended from target_gain */
|
||||||
|
|
||||||
|
/* counters */
|
||||||
|
int code_counterA; /* 8-bit format packet */
|
||||||
|
int code_counterA_almost; /* looks like an A code, but a bit expected to be 0 is 1 */
|
||||||
|
int code_counterB; /* 16-bit format packet, 8-bit code, 8-bit XOR of code */
|
||||||
|
int code_counterB_checkfails; /* looks like a B code, but doesn't pass the XOR check */
|
||||||
|
int code_counterC; /* packet prefix was found, expect a code */
|
||||||
|
int code_counterC_unmatched; /* told to look for a code, but didn't find one */
|
||||||
|
int count_peak_extend; /* valid packets where peak_extend was enabled */
|
||||||
|
int count_transient_filter; /* valid packets where filter was detected */
|
||||||
|
/* target_gain is a 4-bit (3.1) fixed-point value, always
|
||||||
|
* negative, but stored positive.
|
||||||
|
* The 16 possible values range from -7.5 to 0.0 dB in
|
||||||
|
* steps of 0.5, but no value below -6.0 dB should appear. */
|
||||||
|
int gain_counts[16]; /* for cursiosity, mostly */
|
||||||
|
int max_gain;
|
||||||
|
/* occurences of code detect timer expiring without detecting
|
||||||
|
* a code. -1 for timer never set. */
|
||||||
|
int count_sustain_expired;
|
||||||
|
} hdcd_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hdcd_state_t channel[2];
|
||||||
|
int val_target_gain;
|
||||||
|
int count_tg_mismatch;
|
||||||
|
} hdcd_state_stereo_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HDCD_PE_NEVER = 0,
|
||||||
|
HDCD_PE_INTERMITTENT = 1,
|
||||||
|
HDCD_PE_PERMANENT = 2,
|
||||||
|
} hdcd_pe_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int hdcd_detected;
|
||||||
|
int errors; /* detectable errors */
|
||||||
|
hdcd_pe_t peak_extend;
|
||||||
|
int uses_transient_filter;
|
||||||
|
float max_gain_adjustment; /* in dB, expected in the range -7.5 to 0.0 */
|
||||||
|
} hdcd_detection_data_t;
|
||||||
|
|
||||||
|
void hdcd_reset(hdcd_state_t *state, unsigned rate);
|
||||||
|
void hdcd_process(hdcd_state_t *state, int *samples, int count, int stride);
|
||||||
|
|
||||||
|
void hdcd_reset_stereo(hdcd_state_stereo_t *state, unsigned rate);
|
||||||
|
void hdcd_process_stereo(hdcd_state_stereo_t *state, int *samples, int count);
|
||||||
|
|
||||||
|
void hdcd_detect_reset(hdcd_detection_data_t *detect);
|
||||||
|
void hdcd_detect_str(hdcd_detection_data_t *detect, char *str); /* char str[256] should be enough */
|
||||||
|
/* there isn't a non-stereo version */
|
||||||
|
void hdcd_detect_stereo(hdcd_state_stereo_t *state, hdcd_detection_data_t *detect);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue