diff --git a/Application/AppController.m b/Application/AppController.m index e583058f2..9c884b9e9 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -508,7 +508,7 @@ static AppController *kAppController = nil; } - (IBAction)privacyPolicy:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.iubenda.com/privacy-policy/59859310"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"PrivacyPolicyURL", @"Privacy policy URL from Iubenda.")]]; } - (IBAction)feedback:(id)sender { diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 25975f48e..41eb778f9 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -99,6 +99,14 @@ 839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; }; 8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */ = {isa = PBXBuildFile; fileRef = 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */; }; 8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */; }; + 839E56E52879450300DFB5F4 /* HrtfData.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E12879450300DFB5F4 /* HrtfData.h */; }; + 839E56E62879450300DFB5F4 /* Endianness.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E22879450300DFB5F4 /* Endianness.h */; }; + 839E56E72879450300DFB5F4 /* HrtfData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 839E56E32879450300DFB5F4 /* HrtfData.cpp */; }; + 839E56E82879450300DFB5F4 /* IHrtfData.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E42879450300DFB5F4 /* IHrtfData.h */; }; + 839E56EA28794F6300DFB5F4 /* HrtfTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E928794F6300DFB5F4 /* HrtfTypes.h */; }; + 839E56ED2879515D00DFB5F4 /* HeadphoneFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */; }; + 839E56EE2879515D00DFB5F4 /* HeadphoneFilter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */; }; + 839E56F7287974A100DFB5F4 /* SandboxBroker.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56F6287974A100DFB5F4 /* SandboxBroker.h */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; }; @@ -227,6 +235,14 @@ 839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = ""; }; 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../../Utils/NSDictionary+Merge.h"; sourceTree = ""; }; 8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../../Utils/NSDictionary+Merge.m"; sourceTree = ""; }; + 839E56E12879450300DFB5F4 /* HrtfData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HrtfData.h; sourceTree = ""; }; + 839E56E22879450300DFB5F4 /* Endianness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Endianness.h; sourceTree = ""; }; + 839E56E32879450300DFB5F4 /* HrtfData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HrtfData.cpp; sourceTree = ""; }; + 839E56E42879450300DFB5F4 /* IHrtfData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IHrtfData.h; sourceTree = ""; }; + 839E56E928794F6300DFB5F4 /* HrtfTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HrtfTypes.h; sourceTree = ""; }; + 839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeadphoneFilter.h; sourceTree = ""; }; + 839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HeadphoneFilter.mm; sourceTree = ""; }; + 839E56F6287974A100DFB5F4 /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../Utils/SandboxBroker.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = ""; }; @@ -321,6 +337,7 @@ 17F94DD40B8D0F7000A34E87 /* PluginController.mm */, 17D21C750B8BE4BA00D1EBDE /* Chain */, 17D21C9B0B8BE4BA00D1EBDE /* Output */, + 839E56F6287974A100DFB5F4 /* SandboxBroker.h */, 17D21C9E0B8BE4BA00D1EBDE /* Status.h */, B0575F2C0D687A0800411D77 /* Helper.h */, B0575F2F0D687A4000411D77 /* Helper.m */, @@ -380,6 +397,8 @@ 17D21C9B0B8BE4BA00D1EBDE /* Output */ = { isa = PBXGroup; children = ( + 839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */, + 839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */, 17D21C9C0B8BE4BA00D1EBDE /* OutputAVFoundation.h */, 17D21C9D0B8BE4BA00D1EBDE /* OutputAVFoundation.m */, ); @@ -389,6 +408,7 @@ 17D21CD80B8BE5B400D1EBDE /* ThirdParty */ = { isa = PBXGroup; children = ( + 839E56E02879450300DFB5F4 /* hrtf */, 831A50152865A8800049CFE4 /* r8bstate.cpp */, 831A50172865A8B30049CFE4 /* r8bstate.h */, 831A50132865A7FD0049CFE4 /* r8bstate.hpp */, @@ -523,6 +543,18 @@ path = Visualization; sourceTree = ""; }; + 839E56E02879450300DFB5F4 /* hrtf */ = { + isa = PBXGroup; + children = ( + 839E56E22879450300DFB5F4 /* Endianness.h */, + 839E56E32879450300DFB5F4 /* HrtfData.cpp */, + 839E56E12879450300DFB5F4 /* HrtfData.h */, + 839E56E928794F6300DFB5F4 /* HrtfTypes.h */, + 839E56E42879450300DFB5F4 /* IHrtfData.h */, + ); + path = hrtf; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -530,6 +562,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 839E56E82879450300DFB5F4 /* IHrtfData.h in Headers */, 17D21CA10B8BE4BA00D1EBDE /* BufferChain.h in Headers */, 831A4FE02865A7DC0049CFE4 /* pf_double.h in Headers */, 831A50142865A7FD0049CFE4 /* r8bstate.hpp in Headers */, @@ -546,10 +579,13 @@ 17D21CC50B8BE4BA00D1EBDE /* OutputAVFoundation.h in Headers */, 83504165286447DA006B32CC /* Downmix.h in Headers */, 831A4FDE2865A7DC0049CFE4 /* pffft_double.h in Headers */, + 839E56E52879450300DFB5F4 /* HrtfData.h in Headers */, 831A4FE12865A7DC0049CFE4 /* pf_neon_double.h in Headers */, 17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */, 17D21CF30B8BE5EF00D1EBDE /* Semaphore.h in Headers */, + 839E56E62879450300DFB5F4 /* Endianness.h in Headers */, 17D21DC70B8BE79700D1EBDE /* CoreAudioUtils.h in Headers */, + 839E56ED2879515D00DFB5F4 /* HeadphoneFilter.h in Headers */, 17D21EBD0B8BF44000D1EBDE /* AudioPlayer.h in Headers */, 831A50182865A8B30049CFE4 /* r8bstate.h in Headers */, 831A4FE52865A7DC0049CFE4 /* pffft_priv_impl.h in Headers */, @@ -572,6 +608,7 @@ 17C940230B900909008627D6 /* AudioMetadataReader.h in Headers */, 831A500F2865A7DC0049CFE4 /* CDSPFracInterpolator.h in Headers */, 831A4FE22865A7DC0049CFE4 /* pf_sse2_double.h in Headers */, + 839E56F7287974A100DFB5F4 /* SandboxBroker.h in Headers */, 839065F32853338700636FBB /* dsd2float.h in Headers */, 17B619300B909BC300BC003F /* AudioPropertiesReader.h in Headers */, 831A4FDF2865A7DC0049CFE4 /* pf_neon_double_from_avx.h in Headers */, @@ -587,6 +624,7 @@ 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */, B0575F2D0D687A0800411D77 /* Helper.h in Headers */, 07DB5F3E0ED353A900C2E3EF /* AudioMetadataWriter.h in Headers */, + 839E56EA28794F6300DFB5F4 /* HrtfTypes.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -663,6 +701,7 @@ files = ( 17D21CA20B8BE4BA00D1EBDE /* BufferChain.m in Sources */, 17D21CA60B8BE4BA00D1EBDE /* InputNode.m in Sources */, + 839E56EE2879515D00DFB5F4 /* HeadphoneFilter.mm in Sources */, 831A50112865A7DC0049CFE4 /* r8bbase.cpp in Sources */, 83504166286447DA006B32CC /* Downmix.m in Sources */, 8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */, @@ -682,6 +721,7 @@ 839366681815923C006DD712 /* CogPluginMulti.m in Sources */, 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */, 17F94DD60B8D0F7000A34E87 /* PluginController.mm in Sources */, + 839E56E72879450300DFB5F4 /* HrtfData.cpp in Sources */, 831A4FE62865A7DC0049CFE4 /* pffft_double.c in Sources */, 17A2D3C60B8D1D37000778C4 /* AudioDecoder.m in Sources */, 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */, @@ -702,6 +742,7 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; @@ -741,6 +782,7 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Audio/Output/HeadphoneFilter.h b/Audio/Output/HeadphoneFilter.h new file mode 100644 index 000000000..c6f500b0d --- /dev/null +++ b/Audio/Output/HeadphoneFilter.h @@ -0,0 +1,47 @@ +// +// HeadphoneFilter.h +// CogAudio Framework +// +// Created by Christopher Snowhill on 1/24/22. +// + +#ifndef HeadphoneFilter_h +#define HeadphoneFilter_h + +#import +#import + +@interface HeadphoneFilter : NSObject { + vDSP_DFT_Setup dftSetupF; + vDSP_DFT_Setup dftSetupB; + + size_t fftSize; + size_t fftSizeOver2; + size_t bufferSize; + size_t paddedBufferSize; + int channelCount; + + DSPSplitComplex signal_fft; + DSPSplitComplex input_filtered_signal_per_channel[2]; + DSPSplitComplex input_filtered_signal_totals[2]; + DSPSplitComplex *impulse_responses; + + float **prevInputs; + + float *left_result; + float *right_result; + + float *paddedSignal; +} + ++ (BOOL)validateImpulseFile:(NSURL *)url; + +- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config; + +- (void)process:(const float *)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer; + +- (void)reset; + +@end + +#endif /* HeadphoneFilter_h */ diff --git a/Audio/Output/HeadphoneFilter.mm b/Audio/Output/HeadphoneFilter.mm new file mode 100644 index 000000000..c856fc9ef --- /dev/null +++ b/Audio/Output/HeadphoneFilter.mm @@ -0,0 +1,516 @@ +// +// HeadphoneFilter.m +// CogAudio Framework +// +// Created by Christopher Snowhill on 1/24/22. +// + +#import "HeadphoneFilter.h" +#import "AudioChunk.h" +#import "AudioDecoder.h" +#import "AudioSource.h" + +#import + +#import + +#import "r8bstate.h" + +#import "HrtfData.h" + +#import "Logging.h" + +typedef struct speakerPosition { + float elevation; + float azimuth; + float distance; +} speakerPosition; + +#define DEGREES(x) ((x)*M_PI / 180.0) + +static const speakerPosition speakerPositions[18] = { + { .elevation = DEGREES(0.0), .azimuth = DEGREES(-30.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(+30.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(-135.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(+135.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(-15.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(+15.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(-180.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(-90.0), .distance = 1.0 }, + { .elevation = DEGREES(0.0), .azimuth = DEGREES(+90.0), .distance = 1.0 }, + { .elevation = DEGREES(-90.0), .azimuth = DEGREES(0.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(-30.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(0.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(+30.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(-135.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(0.0), .distance = 1.0 }, + { .elevation = DEGREES(-45.0), .azimuth = DEGREES(+135.0), .distance = 1.0 } +}; + +@interface impulseCacheObject : NSObject { +} +@property NSURL *URL; +@property int sampleCount; +@property int channelCount; +@property uint32_t channelConfig; +@property double sampleRate; +@property double targetSampleRate; +@property NSData *data; +@end + +@implementation impulseCacheObject +@synthesize URL; +@synthesize sampleCount; +@synthesize channelCount; +@synthesize channelConfig; +@synthesize sampleRate; +@synthesize targetSampleRate; +@synthesize data; +@end + +@interface impulseCache : NSObject { +} +@property NSMutableArray *cacheObjects; ++ (impulseCache *)sharedController; +- (const float *)getImpulse:(NSURL *)url sampleCount:(int *)sampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig sampleRate:(double)sampleRate; +@end + +// Apparently _mm_malloc is Intel-only on newer macOS targets, so use supported posix_memalign +static void *_memalign_malloc(size_t size, size_t align) { + void *ret = NULL; + if(posix_memalign(&ret, align, size) != 0) { + return NULL; + } + return ret; +} + +@implementation impulseCache + +static impulseCache *_sharedController = nil; + ++ (impulseCache *)sharedController { + @synchronized(self) { + if(!_sharedController) { + _sharedController = [[impulseCache alloc] init]; + } + } + return _sharedController; +} + +- (id)init { + self = [super init]; + if(self) { + self.cacheObjects = [[NSMutableArray alloc] init]; + } + return self; +} + +- (impulseCacheObject *)addImpulse:(NSURL *)url sampleCount:(int)sampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig originalSampleRate:(double)originalSampleRate targetSampleRate:(double)targetSampleRate impulseBuffer:(const float *)impulseBuffer { + impulseCacheObject *obj = [[impulseCacheObject alloc] init]; + + obj.URL = url; + obj.sampleCount = sampleCount; + obj.channelCount = channelCount; + obj.sampleRate = originalSampleRate; + obj.targetSampleRate = targetSampleRate; + obj.data = [NSData dataWithBytes:impulseBuffer length:(sampleCount * channelCount * sizeof(float) * 2)]; + + @synchronized(self.cacheObjects) { + [self.cacheObjects addObject:obj]; + } + + return obj; +} + +- (const float *)getImpulse:(NSURL *)url sampleCount:(int *)retSampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig sampleRate:(double)sampleRate { + BOOL impulseFound = NO; + const float *impulseData = NULL; + double sampleRateOfSource = 0; + int sampleCount = 0; + impulseCacheObject *cacheObject = nil; + + @synchronized(self.cacheObjects) { + for(impulseCacheObject *obj in self.cacheObjects) { + if([obj.URL isEqualTo:url] && + obj.targetSampleRate == sampleRate && + obj.channelCount == channelCount && + obj.channelConfig == channelConfig) { + *retSampleCount = obj.sampleCount; + return (const float *)[obj.data bytes]; + } + } + for(impulseCacheObject *obj in self.cacheObjects) { + if([obj.URL isEqualTo:url] && + obj.sampleRate == obj.targetSampleRate && + obj.channelCount == channelCount && + obj.channelConfig == channelConfig) { + impulseData = (const float *)[obj.data bytes]; + sampleCount = obj.sampleCount; + sampleRateOfSource = obj.sampleRate; + impulseFound = YES; + break; + } + } + } + + if(!impulseFound) { + NSString *filePath = [url path]; + + try { + std::ifstream file([filePath UTF8String], std::fstream::binary); + + if(!file.is_open()) { + throw std::logic_error("Cannot open file."); + } + + HrtfData data(file); + + file.close(); + + sampleRateOfSource = data.get_sample_rate(); + + uint32_t sampleCountExact = data.get_response_length(); + sampleCount = sampleCountExact + ((data.get_longest_delay() + 2) >> 2); + + std::vector hrtfData(sampleCount * channelCount * 2, 0.0); + + for(uint32_t i = 0; i < channelCount; ++i) { + uint32_t channelFlag = [AudioChunk extractChannelFlag:i fromConfig:channelConfig]; + uint32_t channelNumber = [AudioChunk findChannelIndex:channelFlag]; + + if(channelNumber < 18) { + const speakerPosition &speaker = speakerPositions[channelNumber]; + DirectionData hrtfLeft; + DirectionData hrtfRight; + + data.get_direction_data(speaker.elevation, speaker.azimuth, speaker.distance, hrtfLeft, hrtfRight); + + cblas_scopy(sampleCountExact, &hrtfLeft.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) * channelCount * 2 + i * 2], channelCount * 2); + cblas_scopy(sampleCountExact, &hrtfRight.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) * channelCount * 2 + i * 2 + 1], channelCount * 2); + } + } + + cacheObject = [self addImpulse:url sampleCount:sampleCount channelCount:channelCount channelConfig:channelConfig originalSampleRate:sampleRateOfSource targetSampleRate:sampleRateOfSource impulseBuffer:&hrtfData[0]]; + + impulseData = (const float *)[cacheObject.data bytes]; + } catch(std::exception &e) { + ALog(@"Exception caught: %s", e.what()); + return nil; + } + } + + if(sampleRateOfSource != sampleRate) { + double sampleRatio = sampleRate / sampleRateOfSource; + int resampledCount = (int)ceil((double)sampleCount * sampleRatio); + + void *r8bstate = r8bstate_new(channelCount * 2, 1024, sampleRateOfSource, sampleRate); + + float *resampledImpulse = (float *)_memalign_malloc(resampledCount * sizeof(float) * channelCount * 2, 16); + if(!resampledImpulse) { + r8bstate_delete(r8bstate); + return nil; + } + + size_t inputDone = 0; + size_t outputDone = 0; + + outputDone = r8bstate_resample(r8bstate, impulseData, sampleCount, &inputDone, resampledImpulse, resampledCount); + + while(outputDone < resampledCount) { + outputDone += r8bstate_flush(r8bstate, resampledImpulse + outputDone * channelCount * 2, resampledCount - outputDone); + } + + r8bstate_delete(r8bstate); + + sampleCount = (int)outputDone; + + // Normalize resampled impulse by sample ratio + float fSampleRatio = (float)sampleRatio; + vDSP_vsdiv(resampledImpulse, 1, &fSampleRatio, resampledImpulse, 1, sampleCount * channelCount * 2); + + cacheObject = [self addImpulse:url sampleCount:sampleCount channelCount:channelCount channelConfig:channelConfig originalSampleRate:sampleRateOfSource targetSampleRate:sampleRate impulseBuffer:resampledImpulse]; + + free(resampledImpulse); + + impulseData = (const float *)[cacheObject.data bytes]; + } + + *retSampleCount = sampleCount; + return impulseData; +} + +@end + +@implementation HeadphoneFilter + ++ (BOOL)validateImpulseFile:(NSURL *)url { + NSString *filePath = [url path]; + + try { + std::ifstream file([filePath UTF8String], std::fstream::binary); + + if(!file.is_open()) { + throw std::logic_error("Cannot open file."); + } + + HrtfData data(file); + + file.close(); + + return YES; + } catch(std::exception &e) { + ALog(@"Exception thrown: %s", e.what()); + return NO; + } +} + +- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config { + self = [super init]; + + if(self) { + int sampleCount = 0; + const float *impulseBuffer = [[impulseCache sharedController] getImpulse:url sampleCount:&sampleCount channelCount:channels channelConfig:config sampleRate:sampleRate]; + if(!impulseBuffer) { + return nil; + } + + channelCount = channels; + + bufferSize = 512; + fftSize = sampleCount + bufferSize; + + int pow = 1; + while(fftSize > 2) { + pow++; + fftSize /= 2; + } + fftSize = 2 << pow; + + float *deinterleavedImpulseBuffer = (float *)_memalign_malloc(fftSize * sizeof(float) * channelCount * 2, 16); + if(!deinterleavedImpulseBuffer) { + return nil; + } + + for(int i = 0; i < channelCount; ++i) { + cblas_scopy(sampleCount, impulseBuffer + i * 2, (int)channelCount * 2, deinterleavedImpulseBuffer + i * fftSize * 2, 1); + vDSP_vclr(deinterleavedImpulseBuffer + i * fftSize * 2 + sampleCount, 1, fftSize - sampleCount); + cblas_scopy(sampleCount, impulseBuffer + i * 2 + 1, (int)channelCount * 2, deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize, 1); + vDSP_vclr(deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize + sampleCount, 1, fftSize - sampleCount); + } + + paddedBufferSize = fftSize; + fftSizeOver2 = (fftSize + 1) / 2; + const size_t fftSizeOver2Plus1 = fftSizeOver2 + 1; // DFT float overwrites plus one, double doesn't + + dftSetupF = vDSP_DFT_zrop_CreateSetup(nil, fftSize, vDSP_DFT_FORWARD); + dftSetupB = vDSP_DFT_zrop_CreateSetup(nil, fftSize, vDSP_DFT_INVERSE); + if(!dftSetupF || !dftSetupB) { + free(deinterleavedImpulseBuffer); + return nil; + } + + paddedSignal = (float *)_memalign_malloc(sizeof(float) * paddedBufferSize, 16); + if(!paddedSignal) { + free(deinterleavedImpulseBuffer); + return nil; + } + + signal_fft.realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + signal_fft.imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + if(!signal_fft.realp || !signal_fft.imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + input_filtered_signal_per_channel[0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + input_filtered_signal_per_channel[0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + if(!input_filtered_signal_per_channel[0].realp || + !input_filtered_signal_per_channel[0].imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + input_filtered_signal_per_channel[1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + input_filtered_signal_per_channel[1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + if(!input_filtered_signal_per_channel[1].realp || + !input_filtered_signal_per_channel[1].imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + input_filtered_signal_totals[0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + input_filtered_signal_totals[0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + if(!input_filtered_signal_totals[0].realp || + !input_filtered_signal_totals[0].imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + input_filtered_signal_totals[1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + input_filtered_signal_totals[1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + if(!input_filtered_signal_totals[1].realp || + !input_filtered_signal_totals[1].imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + impulse_responses = (DSPSplitComplex *)calloc(sizeof(DSPSplitComplex), channels * 2); + if(!impulse_responses) { + free(deinterleavedImpulseBuffer); + return nil; + } + + for(int i = 0; i < channels; ++i) { + impulse_responses[i * 2 + 0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + impulse_responses[i * 2 + 0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + impulse_responses[i * 2 + 1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + impulse_responses[i * 2 + 1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16); + + if(!impulse_responses[i * 2 + 0].realp || !impulse_responses[i * 2 + 0].imagp || + !impulse_responses[i * 2 + 1].realp || !impulse_responses[i * 2 + 1].imagp) { + free(deinterleavedImpulseBuffer); + return nil; + } + + vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + i * fftSize * 2), 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2); + vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize), 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2); + + vDSP_DFT_Execute(dftSetupF, impulse_responses[i * 2 + 0].realp, impulse_responses[i * 2 + 0].imagp, impulse_responses[i * 2 + 0].realp, impulse_responses[i * 2 + 0].imagp); + vDSP_DFT_Execute(dftSetupF, impulse_responses[i * 2 + 1].realp, impulse_responses[i * 2 + 1].imagp, impulse_responses[i * 2 + 1].realp, impulse_responses[i * 2 + 1].imagp); + } + + free(deinterleavedImpulseBuffer); + + left_result = (float *)_memalign_malloc(sizeof(float) * fftSize, 16); + right_result = (float *)_memalign_malloc(sizeof(float) * fftSize, 16); + if(!left_result || !right_result) + return nil; + + prevInputs = (float **)calloc(channels, sizeof(float *)); + if(!prevInputs) + return nil; + for(int i = 0; i < channels; ++i) { + prevInputs[i] = (float *)_memalign_malloc(sizeof(float) * fftSize, 16); + if(!prevInputs[i]) + return nil; + vDSP_vclr(prevInputs[i], 1, fftSize); + } + } + + return self; +} + +- (void)dealloc { + if(dftSetupF) vDSP_DFT_DestroySetup(dftSetupF); + if(dftSetupB) vDSP_DFT_DestroySetup(dftSetupB); + + free(paddedSignal); + + free(signal_fft.realp); + free(signal_fft.imagp); + + free(input_filtered_signal_per_channel[0].realp); + free(input_filtered_signal_per_channel[0].imagp); + free(input_filtered_signal_per_channel[1].realp); + free(input_filtered_signal_per_channel[1].imagp); + + free(input_filtered_signal_totals[0].realp); + free(input_filtered_signal_totals[0].imagp); + free(input_filtered_signal_totals[1].realp); + free(input_filtered_signal_totals[1].imagp); + + if(impulse_responses) { + for(int i = 0; i < channelCount * 2; ++i) { + free(impulse_responses[i].realp); + free(impulse_responses[i].imagp); + } + free(impulse_responses); + } + + free(left_result); + free(right_result); + + if(prevInputs) { + for(int i = 0; i < channelCount; ++i) { + free(prevInputs[i]); + } + free(prevInputs); + } +} + +- (void)process:(const float *)inBuffer sampleCount:(int)count toBuffer:(float *)outBuffer { + const float scale = 1.0 / (4.0 * (float)fftSize); + + while(count > 0) { + const int countToDo = (count > bufferSize) ? bufferSize : count; + const int prevToDo = fftSize - countToDo; + + vDSP_vclr(input_filtered_signal_totals[0].realp, 1, fftSizeOver2); + vDSP_vclr(input_filtered_signal_totals[0].imagp, 1, fftSizeOver2); + vDSP_vclr(input_filtered_signal_totals[1].realp, 1, fftSizeOver2); + vDSP_vclr(input_filtered_signal_totals[1].imagp, 1, fftSizeOver2); + + for(int i = 0; i < channelCount; ++i) { + cblas_scopy((int)prevToDo, prevInputs[i] + countToDo, 1, paddedSignal, 1); + cblas_scopy((int)countToDo, inBuffer + i, (int)channelCount, paddedSignal + prevToDo, 1); + cblas_scopy((int)fftSize, paddedSignal, 1, prevInputs[i], 1); + + vDSP_ctoz((DSPComplex *)paddedSignal, 2, &signal_fft, 1, fftSizeOver2); + + vDSP_DFT_Execute(dftSetupF, signal_fft.realp, signal_fft.imagp, signal_fft.realp, signal_fft.imagp); + + // One channel forward, then multiply and back twice + + float preserveIRNyq = impulse_responses[i * 2 + 0].imagp[0]; + float preserveSigNyq = signal_fft.imagp[0]; + impulse_responses[i * 2 + 0].imagp[0] = 0; + signal_fft.imagp[0] = 0; + + vDSP_zvmul(&signal_fft, 1, &impulse_responses[i * 2 + 0], 1, &input_filtered_signal_per_channel[0], 1, fftSizeOver2, 1); + + input_filtered_signal_per_channel[0].imagp[0] = preserveIRNyq * preserveSigNyq; + impulse_responses[i * 2 + 0].imagp[0] = preserveIRNyq; + + preserveIRNyq = impulse_responses[i * 2 + 1].imagp[0]; + impulse_responses[i * 2 + 1].imagp[0] = 0; + + vDSP_zvmul(&signal_fft, 1, &impulse_responses[i * 2 + 1], 1, &input_filtered_signal_per_channel[1], 1, fftSizeOver2, 1); + + input_filtered_signal_per_channel[1].imagp[0] = preserveIRNyq * preserveSigNyq; + impulse_responses[i * 2 + 1].imagp[0] = preserveIRNyq; + + vDSP_zvadd(&input_filtered_signal_totals[0], 1, &input_filtered_signal_per_channel[0], 1, &input_filtered_signal_totals[0], 1, fftSizeOver2); + vDSP_zvadd(&input_filtered_signal_totals[1], 1, &input_filtered_signal_per_channel[1], 1, &input_filtered_signal_totals[1], 1, fftSizeOver2); + } + + vDSP_DFT_Execute(dftSetupB, input_filtered_signal_totals[0].realp, input_filtered_signal_totals[0].imagp, input_filtered_signal_totals[0].realp, input_filtered_signal_totals[0].imagp); + vDSP_DFT_Execute(dftSetupB, input_filtered_signal_totals[1].realp, input_filtered_signal_totals[1].imagp, input_filtered_signal_totals[1].realp, input_filtered_signal_totals[1].imagp); + + vDSP_ztoc(&input_filtered_signal_totals[0], 1, (DSPComplex *)left_result, 2, fftSizeOver2); + vDSP_ztoc(&input_filtered_signal_totals[1], 1, (DSPComplex *)right_result, 2, fftSizeOver2); + + float *left_ptr = left_result + prevToDo; + float *right_ptr = right_result + prevToDo; + + vDSP_vsmul(left_ptr, 1, &scale, left_ptr, 1, countToDo); + vDSP_vsmul(right_ptr, 1, &scale, right_ptr, 1, countToDo); + + cblas_scopy((int)countToDo, left_ptr, 1, outBuffer + 0, 2); + cblas_scopy((int)countToDo, right_ptr, 1, outBuffer + 1, 2); + + inBuffer += countToDo * channelCount; + outBuffer += countToDo * 2; + + count -= countToDo; + } +} + +- (void)reset { + for(int i = 0; i < channelCount; ++i) { + vDSP_vclr(prevInputs[i], 1, fftSize); + } +} + +@end diff --git a/Audio/Output/OutputAVFoundation.h b/Audio/Output/OutputAVFoundation.h index bc9920f1e..23bc1d97a 100644 --- a/Audio/Output/OutputAVFoundation.h +++ b/Audio/Output/OutputAVFoundation.h @@ -26,6 +26,8 @@ using std::atomic_long; #import "VisualizationController.h" +#import "HeadphoneFilter.h" + //#define OUTPUT_LOG #ifdef OUTPUT_LOG #import @@ -55,8 +57,6 @@ using std::atomic_long; BOOL eqEnabled; BOOL eqInitialized; - BOOL dontRemix; - BOOL streamFormatStarted; double secondsHdcdSustained; @@ -71,12 +71,16 @@ using std::atomic_long; float eqPreamp; AudioDeviceID outputDeviceID; + AudioStreamBasicDescription realStreamFormat; // stream format pre-hrtf AudioStreamBasicDescription streamFormat; // stream format last seen in render callback + AudioStreamBasicDescription realNewFormat; // in case of resampler flush AudioStreamBasicDescription newFormat; // in case of resampler flush AudioStreamBasicDescription visFormat; // Mono format for vis + uint32_t realStreamChannelConfig; uint32_t streamChannelConfig; + uint32_t realNewChannelConfig; uint32_t newChannelConfig; AVSampleBufferAudioRenderer *audioRenderer; @@ -100,7 +104,11 @@ using std::atomic_long; VisualizationController *visController; + BOOL enableHrtf; + HeadphoneFilter *hrtf; + float inputBuffer[2048 * 32]; // 2048 samples times maximum supported channel count + float hrtfBuffer[2048 * 2]; float eqBuffer[2048 * 32]; #ifdef OUTPUT_LOG diff --git a/Audio/Output/OutputAVFoundation.m b/Audio/Output/OutputAVFoundation.m index 897d12cf0..030699d66 100644 --- a/Audio/Output/OutputAVFoundation.m +++ b/Audio/Output/OutputAVFoundation.m @@ -92,7 +92,7 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA dstRate = maxSampleRate; formatClipped = YES; } - if(!streamFormatStarted || config != streamChannelConfig || memcmp(&newFormat, &format, sizeof(format)) != 0) { + if(!streamFormatStarted || config != realStreamChannelConfig || memcmp(&newFormat, &format, sizeof(format)) != 0) { [currentPtsLock lock]; if(formatClipped) { ALog(@"Sample rate clipped to no more than %f Hz!", maxSampleRate); @@ -117,8 +117,8 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA downmixerForVis = [[DownmixProcessor alloc] initWithInputFormat:format inputConfig:config andOutputFormat:visFormat outputConfig:AudioConfigMono]; if(!r8bold) { - streamFormat = format; - streamChannelConfig = config; + realStreamFormat = format; + realStreamChannelConfig = config; [self updateStreamFormat]; } } @@ -301,8 +301,10 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons } else if([keyPath isEqualToString:@"values.eqPreamp"]) { float preamp = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] floatForKey:@"eqPreamp"]; eqPreamp = pow(10.0, preamp / 20.0); - } else if([keyPath isEqualToString:@"values.dontRemix"]) { - dontRemix = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"dontRemix"]; + } else if([keyPath isEqualToString:@"values.enableHrtf"]) { + enableHrtf = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableHrtf"]; + if(streamFormatStarted) + [self updateStreamFormat]; } } @@ -598,6 +600,25 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons - (void)updateStreamFormat { /* Set the channel layout for the audio queue */ + if(enableHrtf) { + NSURL *presetUrl = [[NSBundle mainBundle] URLForResource:@"SADIE_D02-96000" withExtension:@"mhr"]; + + hrtf = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:realStreamFormat.mSampleRate withInputChannels:realStreamFormat.mChannelsPerFrame withConfig:realStreamChannelConfig]; + + streamFormat = realStreamFormat; + streamFormat.mChannelsPerFrame = 2; + streamFormat.mBytesPerFrame = sizeof(float) * 2; + streamFormat.mFramesPerPacket = 1; + streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame; + + streamChannelConfig = AudioChannelSideLeft | AudioChannelSideRight; + } else { + hrtf = nil; + + streamFormat = realStreamFormat; + streamChannelConfig = realStreamChannelConfig; + } + AudioChannelLayoutTag tag = 0; AudioChannelLayout layout = { 0 }; @@ -741,13 +762,18 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons samplePtr = &inputBuffer[0]; if(r8bDone) { r8bDone = NO; - streamFormat = newFormat; - streamChannelConfig = newChannelConfig; + realStreamFormat = newFormat; + realStreamChannelConfig = newChannelConfig; [self updateStreamFormat]; } } if(samplesRendered) { + if(enableHrtf && hrtf) { + [hrtf process:samplePtr sampleCount:samplesRendered toBuffer:&hrtfBuffer[0]]; + samplePtr = &hrtfBuffer[0]; + } + if(eqEnabled && eqInitialized) { const int channels = streamFormat.mChannelsPerFrame; if(channels > 0) { @@ -939,7 +965,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:kOutputAVFoundationContext]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:kOutputAVFoundationContext]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.eqPreamp" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputAVFoundationContext]; - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.dontRemix" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputAVFoundationContext]; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHrtf" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputAVFoundationContext]; observersapplied = YES; [renderSynchronizer addRenderer:audioRenderer]; @@ -1060,7 +1086,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice" context:kOutputAVFoundationContext]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable" context:kOutputAVFoundationContext]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.eqPreamp" context:kOutputAVFoundationContext]; - [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.dontRemix" context:kOutputAVFoundationContext]; + [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHrtf" context:kOutputAVFoundationContext]; observersapplied = NO; } stopping = YES; diff --git a/Audio/ThirdParty/hrtf/Endianness.h b/Audio/ThirdParty/hrtf/Endianness.h new file mode 100644 index 000000000..9fd598793 --- /dev/null +++ b/Audio/ThirdParty/hrtf/Endianness.h @@ -0,0 +1,25 @@ +#pragma once + +// The functions provide little endianness to native endianness conversion and back again +#if(defined(_MSC_VER) && defined(_WIN32)) || defined(__APPLE__) +template +inline void from_little_endian_inplace(T& x) { +} + +template +inline T from_little_endian(T x) { + return x; +} + +template +inline void to_little_endian_inplace(T& x) { +} + +template +inline T to_little_endian(T x) { + return x; +} + +#else +#error "Specify endianness conversion for your platform" +#endif diff --git a/Audio/ThirdParty/hrtf/HrtfData.cpp b/Audio/ThirdParty/hrtf/HrtfData.cpp new file mode 100644 index 000000000..13b46e6b7 --- /dev/null +++ b/Audio/ThirdParty/hrtf/HrtfData.cpp @@ -0,0 +1,640 @@ + +#include "HrtfData.h" +#include "Endianness.h" +#include +#include + +typedef struct { + uint8_t bytes[3]; +} sample_int24_t; + +const double pi = 3.1415926535897932385; + +template +void read_stream(std::istream& stream, T& value) { + stream.read(reinterpret_cast(&value), sizeof(value)); + from_little_endian_inplace(value); +} + +HrtfData::HrtfData(std::istream& stream) { + const char required_magic00[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '0' }; + const char required_magic01[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '1' }; + const char required_magic02[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '2' }; + const char required_magic03[] = { 'M', 'i', 'n', 'P', 'H', 'R', '0', '3' }; + char actual_magic[sizeof(required_magic03) / sizeof(required_magic03[0])]; + + stream.read(actual_magic, sizeof(actual_magic)); + if(std::equal(std::begin(required_magic03), std::end(required_magic03), std::begin(actual_magic), std::end(actual_magic))) { + LoadHrtf03(stream); + } else if(std::equal(std::begin(required_magic02), std::end(required_magic02), std::begin(actual_magic), std::end(actual_magic))) { + LoadHrtf02(stream); + } else if(std::equal(std::begin(required_magic01), std::end(required_magic01), std::begin(actual_magic), std::end(actual_magic))) { + LoadHrtf01(stream); + } else if(std::equal(std::begin(required_magic00), std::end(required_magic00), std::begin(actual_magic), std::end(actual_magic))) { + LoadHrtf00(stream); + } else { + throw std::logic_error("Bad file format."); + } +} + +void HrtfData::LoadHrtf03(std::istream& stream) { + // const uint8_t ChanType_LeftOnly{0}; + const uint8_t ChanType_LeftRight{ 1 }; + + uint32_t sample_rate; + uint8_t channel_type; + uint8_t impulse_response_length; + uint8_t distances_count; + + read_stream(stream, sample_rate); + read_stream(stream, channel_type); + read_stream(stream, impulse_response_length); + read_stream(stream, distances_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + if(channel_type > ChanType_LeftRight) { + throw std::logic_error("Invalid channel format."); + } + + int channel_count = channel_type == ChanType_LeftRight ? 2 : 1; + + std::vector distances(distances_count); + + for(uint8_t i = 0; i < distances_count; i++) { + uint16_t distance; + read_stream(stream, distance); + distances[i].distance = float(distance) / 1000.0f; + + uint8_t elevations_count; + read_stream(stream, elevations_count); + distances[i].elevations.resize(elevations_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + for(uint8_t j = 0; j < elevations_count; j++) { + uint8_t azimuth_count; + read_stream(stream, azimuth_count); + distances[i].elevations[j].azimuths.resize(azimuth_count); + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + } + + const float normalization_factor = 1.0f / 8388608.0f; + + for(auto& distance : distances) { + for(auto& elevation : distance.elevations) { + for(auto& azimuth : elevation.azimuths) { + azimuth.impulse_response.resize(impulse_response_length * channel_count); + for(auto& sample : azimuth.impulse_response) { + union { + sample_int24_t sample; + int32_t sample_int; + } sample_union; + sample_union.sample_int = 0; + read_stream(stream, sample_union.sample); + sample_union.sample_int <<= 8; + sample_union.sample_int >>= 8; + sample = sample_union.sample_int * normalization_factor; + } + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + uint8_t longest_delay = 0; + for(auto& distance : distances) { + for(auto& elevation : distance.elevations) { + for(auto& azimuth : elevation.azimuths) { + uint8_t delay; + read_stream(stream, delay); + azimuth.delay = delay; + longest_delay = std::max(longest_delay, delay); + if(channel_type == ChanType_LeftRight) { + read_stream(stream, delay); + azimuth.delay_right = delay; + longest_delay = std::max(longest_delay, delay); + } + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + std::sort(distances.begin(), distances.end(), + [](const DistanceData& lhs, const DistanceData& rhs) noexcept { return lhs.distance > rhs.distance; }); + + m_distances = std::move(distances); + m_channel_count = channel_count; + m_response_length = impulse_response_length; + m_sample_rate = sample_rate; + m_longest_delay = longest_delay; +} + +void HrtfData::LoadHrtf02(std::istream& stream) { + // const uint8_t SampleType_S16{0}; + const uint8_t SampleType_S24{ 1 }; + // const uint8_t ChanType_LeftOnly{0}; + const uint8_t ChanType_LeftRight{ 1 }; + + uint32_t sample_rate; + uint8_t sample_type; + uint8_t channel_type; + uint8_t impulse_response_length; + uint8_t distances_count; + + read_stream(stream, sample_rate); + read_stream(stream, sample_type); + read_stream(stream, channel_type); + read_stream(stream, impulse_response_length); + read_stream(stream, distances_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + if(sample_type > SampleType_S24) { + throw std::logic_error("Invalid sample type."); + } + + if(channel_type > ChanType_LeftRight) { + throw std::logic_error("Invalid channel format."); + } + + int channel_count = channel_type == ChanType_LeftRight ? 2 : 1; + + std::vector distances(distances_count); + + for(uint8_t i = 0; i < distances_count; i++) { + uint16_t distance; + read_stream(stream, distance); + distances[i].distance = float(distance) / 1000.0f; + + uint8_t elevations_count; + read_stream(stream, elevations_count); + distances[i].elevations.resize(elevations_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + for(uint8_t j = 0; j < elevations_count; j++) { + uint8_t azimuth_count; + read_stream(stream, azimuth_count); + distances[i].elevations[j].azimuths.resize(azimuth_count); + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + } + + const float normalization_factor = (sample_type == SampleType_S24) ? 1.0f / 8388608.0f : 1.0f / 32768.0f; + + for(auto& distance : distances) { + for(auto& elevation : distance.elevations) { + for(auto& azimuth : elevation.azimuths) { + azimuth.impulse_response.resize(impulse_response_length * channel_count); + if(sample_type == SampleType_S24) { + for(auto& sample : azimuth.impulse_response) { + union { + sample_int24_t sample; + int32_t sample_int; + } sample_union; + sample_union.sample_int = 0; + read_stream(stream, sample_union.sample); + sample_union.sample_int <<= 8; + sample_union.sample_int >>= 8; + sample = sample_union.sample_int * normalization_factor; + } + } else { + for(auto& sample : azimuth.impulse_response) { + int16_t sample_from_file; + read_stream(stream, sample_from_file); + sample = sample_from_file * normalization_factor; + } + } + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + uint8_t longest_delay = 0; + for(auto& distance : distances) { + for(auto& elevation : distance.elevations) { + for(auto& azimuth : elevation.azimuths) { + uint8_t delay; + read_stream(stream, delay); + azimuth.delay = delay; + longest_delay = std::max(longest_delay, delay); + if(channel_type == ChanType_LeftRight) { + read_stream(stream, delay); + azimuth.delay_right = delay; + longest_delay = std::max(longest_delay, delay); + } + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + std::sort(distances.begin(), distances.end(), + [](const DistanceData& lhs, const DistanceData& rhs) noexcept { return lhs.distance > rhs.distance; }); + + m_distances = std::move(distances); + m_channel_count = channel_count; + m_response_length = impulse_response_length; + m_sample_rate = sample_rate; + m_longest_delay = longest_delay; +} + +void HrtfData::LoadHrtf01(std::istream& stream) { + uint32_t sample_rate; + uint8_t impulse_response_length; + + read_stream(stream, sample_rate); + read_stream(stream, impulse_response_length); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + std::vector distances(1); + + distances[0].distance = 1.0; + + uint8_t elevations_count; + read_stream(stream, elevations_count); + distances[0].elevations.resize(elevations_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + for(uint8_t i = 0; i < elevations_count; i++) { + uint8_t azimuth_count; + read_stream(stream, azimuth_count); + distances[0].elevations[i].azimuths.resize(azimuth_count); + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + const float normalization_factor = 1.0f / 32768.0f; + + for(auto& elevation : distances[0].elevations) { + for(auto& azimuth : elevation.azimuths) { + azimuth.impulse_response.resize(impulse_response_length); + for(auto& sample : azimuth.impulse_response) { + int16_t sample_from_file; + read_stream(stream, sample_from_file); + sample = sample_from_file * normalization_factor; + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + uint8_t longest_delay = 0; + for(auto& elevation : distances[0].elevations) { + for(auto& azimuth : elevation.azimuths) { + uint8_t delay; + read_stream(stream, delay); + delay <<= 2; + azimuth.delay = delay; + longest_delay = std::max(longest_delay, delay); + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + m_distances = std::move(distances); + m_channel_count = 1; + m_response_length = impulse_response_length; + m_sample_rate = sample_rate; + m_longest_delay = longest_delay; +} + +void HrtfData::LoadHrtf00(std::istream& stream) { + uint32_t sample_rate; + uint16_t impulse_response_count; + uint16_t impulse_response_length; + + read_stream(stream, sample_rate); + read_stream(stream, impulse_response_count); + read_stream(stream, impulse_response_length); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + std::vector distances(1); + + distances[0].distance = 1.0; + + uint8_t elevations_count; + read_stream(stream, elevations_count); + distances[0].elevations.resize(elevations_count); + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + std::vector irOffsets(elevations_count); + + for(uint8_t i = 0; i < elevations_count; i++) { + read_stream(stream, irOffsets[i]); + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + for(size_t i = 1; i < elevations_count; i++) { + if(irOffsets[i] <= irOffsets[i - 1]) { + throw std::logic_error("Invalid elevation offset."); + } + } + if(impulse_response_count <= irOffsets[elevations_count - 1]) { + throw std::logic_error("Invalid elevation offset."); + } + + for(size_t i = 1; i < elevations_count; i++) { + distances[0].elevations[i - 1].azimuths.resize(irOffsets[i] - irOffsets[i - 1]); + } + distances[0].elevations[elevations_count - 1].azimuths.resize(impulse_response_count - irOffsets[elevations_count - 1]); + + const float normalization_factor = 1.0f / 32768.0f; + + for(auto& elevation : distances[0].elevations) { + for(auto& azimuth : elevation.azimuths) { + azimuth.impulse_response.resize(impulse_response_length); + for(auto& sample : azimuth.impulse_response) { + int16_t sample_from_file; + read_stream(stream, sample_from_file); + sample = sample_from_file * normalization_factor; + } + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + uint8_t longest_delay = 0; + for(auto& elevation : distances[0].elevations) { + for(auto& azimuth : elevation.azimuths) { + uint8_t delay; + read_stream(stream, delay); + delay <<= 2; + azimuth.delay = delay; + longest_delay = std::max(longest_delay, delay); + } + } + + if(!stream || stream.eof()) { + throw std::logic_error("Failed reading file."); + } + + m_distances = std::move(distances); + m_channel_count = 1; + m_response_length = impulse_response_length; + m_sample_rate = sample_rate; + m_longest_delay = longest_delay; +} + +void HrtfData::get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t channel, DirectionData& ref_data) const { + assert(elevation >= -angle_t(pi * 0.5)); + assert(elevation <= angle_t(pi * 0.5)); + assert(azimuth >= -angle_t(2.0 * pi)); + assert(azimuth <= angle_t(2.0 * pi)); + + const float azimuth_mod = std::fmod(azimuth + angle_t(pi * 2.0), angle_t(pi * 2.0)); + + size_t distance_index0 = 0; + while(distance_index0 < m_distances.size() - 1 && + m_distances[distance_index0].distance > distance) { + distance_index0++; + } + const size_t distance_index1 = std::min(distance_index0 + 1, m_distances.size() - 1); + const distance_t distance0 = m_distances[distance_index0].distance; + const distance_t distance1 = m_distances[distance_index1].distance; + const distance_t distance_delta = distance0 - distance1; + const float distance_fractional_part = distance_delta ? (distance - distance1) / distance_delta : 0; + + const auto& elevations0 = m_distances[distance_index0].elevations; + const auto& elevations1 = m_distances[distance_index1].elevations; + + const angle_t elevation_scaled0 = (elevation + angle_t(pi * 0.5)) * (elevations0.size() - 1) / angle_t(pi); + const angle_t elevation_scaled1 = (elevation + angle_t(pi * 0.5)) * (elevations1.size() - 1) / angle_t(pi); + const size_t elevation_index00 = static_cast(elevation_scaled0); + const size_t elevation_index10 = static_cast(elevation_scaled1); + const size_t elevation_index01 = std::min(elevation_index00 + 1, elevations0.size() - 1); + const size_t elevation_index11 = std::min(elevation_index10 + 1, elevations1.size() - 1); + + const float elevation_fractional_part0 = std::fmod(elevation_scaled0, 1.0); + const float elevation_fractional_part1 = std::fmod(elevation_scaled1, 1.0); + + const angle_t azimuth_scaled00 = azimuth_mod * elevations0[elevation_index00].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index000 = static_cast(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size(); + const size_t azimuth_index001 = static_cast(azimuth_scaled00 + 1) % elevations0[elevation_index00].azimuths.size(); + const float azimuth_fractional_part00 = std::fmod(azimuth_scaled00, 1.0); + + const angle_t azimuth_scaled10 = azimuth_mod * elevations1[elevation_index10].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index100 = static_cast(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size(); + const size_t azimuth_index101 = static_cast(azimuth_scaled10 + 1) % elevations1[elevation_index10].azimuths.size(); + const float azimuth_fractional_part10 = std::fmod(azimuth_scaled10, 1.0); + + const angle_t azimuth_scaled01 = azimuth_mod * elevations0[elevation_index01].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index010 = static_cast(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size(); + const size_t azimuth_index011 = static_cast(azimuth_scaled01 + 1) % elevations0[elevation_index01].azimuths.size(); + const float azimuth_fractional_part01 = std::fmod(azimuth_scaled01, 1.0); + + const angle_t azimuth_scaled11 = azimuth_mod * elevations1[elevation_index11].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index110 = static_cast(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size(); + const size_t azimuth_index111 = static_cast(azimuth_scaled11 + 1) % elevations1[elevation_index11].azimuths.size(); + const float azimuth_fractional_part11 = std::fmod(azimuth_scaled11, 1.0); + + const float blend_factor_000 = (1.0f - elevation_fractional_part0) * (1.0f - azimuth_fractional_part00) * distance_fractional_part; + const float blend_factor_001 = (1.0f - elevation_fractional_part0) * azimuth_fractional_part00 * distance_fractional_part; + const float blend_factor_010 = elevation_fractional_part0 * (1.0f - azimuth_fractional_part01) * distance_fractional_part; + const float blend_factor_011 = elevation_fractional_part0 * azimuth_fractional_part01 * distance_fractional_part; + + const float blend_factor_100 = (1.0f - elevation_fractional_part1) * (1.0f - azimuth_fractional_part10) * (1.0f - distance_fractional_part); + const float blend_factor_101 = (1.0f - elevation_fractional_part1) * azimuth_fractional_part10 * (1.0f - distance_fractional_part); + const float blend_factor_110 = elevation_fractional_part1 * (1.0f - azimuth_fractional_part11) * (1.0f - distance_fractional_part); + const float blend_factor_111 = elevation_fractional_part1 * azimuth_fractional_part11 * (1.0f - distance_fractional_part); + + delay_t delay0; + delay_t delay1; + + if(channel == 0) { + delay0 = + elevations0[elevation_index00].azimuths[azimuth_index000].delay * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay * blend_factor_011; + + delay1 = + elevations1[elevation_index10].azimuths[azimuth_index100].delay * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay * blend_factor_111; + } else { + delay0 = + elevations0[elevation_index00].azimuths[azimuth_index000].delay_right * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay_right * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay_right * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay_right * blend_factor_011; + + delay1 = + elevations1[elevation_index10].azimuths[azimuth_index100].delay_right * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay_right * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay_right * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay_right * blend_factor_111; + } + + ref_data.delay = delay0 + delay1; + + if(ref_data.impulse_response.size() < m_response_length) + ref_data.impulse_response.resize(m_response_length); + + for(size_t i = 0, j = channel; i < m_response_length; i++, j += m_channel_count) { + float sample0 = + elevations0[elevation_index00].azimuths[azimuth_index000].impulse_response[j] * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].impulse_response[j] * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].impulse_response[j] * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].impulse_response[j] * blend_factor_011; + float sample1 = + elevations1[elevation_index10].azimuths[azimuth_index100].impulse_response[j] * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].impulse_response[j] * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].impulse_response[j] * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].impulse_response[j] * blend_factor_111; + + ref_data.impulse_response[i] = sample0 + sample1; + } +} + +void HrtfData::get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, DirectionData& ref_data_left, DirectionData& ref_data_right) const { + assert(elevation >= -angle_t(pi * 0.5)); + assert(elevation <= angle_t(pi * 0.5)); + assert(azimuth >= -angle_t(2.0 * pi)); + assert(azimuth <= angle_t(2.0 * pi)); + + get_direction_data(elevation, azimuth, distance, 0, ref_data_left); + if(m_channel_count == 1) { + get_direction_data(elevation, -azimuth, distance, 0, ref_data_right); + } else { + get_direction_data(elevation, azimuth, distance, 1, ref_data_right); + } +} + +void HrtfData::sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, uint32_t channel, float& value, float& delay) const { + assert(elevation >= -angle_t(pi * 0.5)); + assert(elevation <= angle_t(pi * 0.5)); + assert(azimuth >= -angle_t(2.0 * pi)); + assert(azimuth <= angle_t(2.0 * pi)); + + size_t distance_index0 = 0; + while(distance_index0 < m_distances.size() - 1 && + m_distances[distance_index0].distance > distance) { + distance_index0++; + } + const size_t distance_index1 = std::min(distance_index0 + 1, m_distances.size() - 1); + const distance_t distance0 = m_distances[distance_index0].distance; + const distance_t distance1 = m_distances[distance_index1].distance; + const distance_t distance_delta = distance0 - distance1; + const float distance_fractional_part = distance_delta ? (distance - distance1) / distance_delta : 0; + + const auto& elevations0 = m_distances[distance_index0].elevations; + const auto& elevations1 = m_distances[distance_index1].elevations; + + const float azimuth_mod = std::fmod(azimuth + angle_t(pi * 2.0), angle_t(pi * 2.0)); + + const angle_t elevation_scaled0 = (elevation + angle_t(pi * 0.5)) * (elevations0.size() - 1) / angle_t(pi); + const angle_t elevation_scaled1 = (elevation + angle_t(pi * 0.5)) * (elevations1.size() - 1) / angle_t(pi); + const size_t elevation_index00 = static_cast(elevation_scaled0); + const size_t elevation_index10 = static_cast(elevation_scaled1); + const size_t elevation_index01 = std::min(elevation_index00 + 1, elevations0.size() - 1); + const size_t elevation_index11 = std::min(elevation_index10 + 1, elevations1.size() - 1); + + const float elevation_fractional_part0 = std::fmod(elevation_scaled0, 1.0); + const float elevation_fractional_part1 = std::fmod(elevation_scaled1, 1.0); + + const angle_t azimuth_scaled00 = azimuth_mod * elevations0[elevation_index00].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index000 = static_cast(azimuth_scaled00) % elevations0[elevation_index00].azimuths.size(); + const size_t azimuth_index001 = static_cast(azimuth_scaled00 + 1) % elevations0[elevation_index00].azimuths.size(); + const float azimuth_fractional_part00 = std::fmod(azimuth_scaled00, 1.0); + + const angle_t azimuth_scaled10 = azimuth_mod * elevations1[elevation_index10].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index100 = static_cast(azimuth_scaled10) % elevations1[elevation_index10].azimuths.size(); + const size_t azimuth_index101 = static_cast(azimuth_scaled10 + 1) % elevations1[elevation_index10].azimuths.size(); + const float azimuth_fractional_part10 = std::fmod(azimuth_scaled10, 1.0); + + const angle_t azimuth_scaled01 = azimuth_mod * elevations0[elevation_index01].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index010 = static_cast(azimuth_scaled01) % elevations0[elevation_index01].azimuths.size(); + const size_t azimuth_index011 = static_cast(azimuth_scaled01 + 1) % elevations0[elevation_index01].azimuths.size(); + const float azimuth_fractional_part01 = std::fmod(azimuth_scaled01, 1.0); + + const angle_t azimuth_scaled11 = azimuth_mod * elevations1[elevation_index11].azimuths.size() / angle_t(2 * pi); + const size_t azimuth_index110 = static_cast(azimuth_scaled11) % elevations1[elevation_index11].azimuths.size(); + const size_t azimuth_index111 = static_cast(azimuth_scaled11 + 1) % elevations1[elevation_index11].azimuths.size(); + const float azimuth_fractional_part11 = std::fmod(azimuth_scaled11, 1.0); + + const float blend_factor_000 = (1.0f - elevation_fractional_part0) * (1.0f - azimuth_fractional_part00) * distance_fractional_part; + const float blend_factor_001 = (1.0f - elevation_fractional_part0) * azimuth_fractional_part00 * distance_fractional_part; + const float blend_factor_010 = elevation_fractional_part0 * (1.0f - azimuth_fractional_part01) * distance_fractional_part; + const float blend_factor_011 = elevation_fractional_part0 * azimuth_fractional_part01 * distance_fractional_part; + + const float blend_factor_100 = (1.0f - elevation_fractional_part1) * (1.0f - azimuth_fractional_part10) * (1.0f - distance_fractional_part); + const float blend_factor_101 = (1.0f - elevation_fractional_part1) * azimuth_fractional_part10 * (1.0f - distance_fractional_part); + const float blend_factor_110 = elevation_fractional_part1 * (1.0f - azimuth_fractional_part11) * (1.0f - distance_fractional_part); + const float blend_factor_111 = elevation_fractional_part1 * azimuth_fractional_part11 * (1.0f - distance_fractional_part); + + float delay0; + float delay1; + + if(channel == 0) { + delay0 = + elevations0[elevation_index00].azimuths[azimuth_index000].delay * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay * blend_factor_011; + + delay1 = + elevations1[elevation_index10].azimuths[azimuth_index100].delay * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay * blend_factor_111; + } else { + delay0 = + elevations0[elevation_index00].azimuths[azimuth_index000].delay_right * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].delay_right * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].delay_right * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].delay_right * blend_factor_011; + + delay1 = + elevations1[elevation_index10].azimuths[azimuth_index100].delay_right * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].delay_right * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].delay_right * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].delay_right * blend_factor_111; + } + + delay = delay0 + delay1; + + sample = sample * m_channel_count + channel; + + float value0 = + elevations0[elevation_index00].azimuths[azimuth_index000].impulse_response[sample] * blend_factor_000 + elevations0[elevation_index00].azimuths[azimuth_index001].impulse_response[sample] * blend_factor_001 + elevations0[elevation_index01].azimuths[azimuth_index010].impulse_response[sample] * blend_factor_010 + elevations0[elevation_index01].azimuths[azimuth_index011].impulse_response[sample] * blend_factor_011; + + float value1 = + elevations1[elevation_index10].azimuths[azimuth_index100].impulse_response[sample] * blend_factor_100 + elevations1[elevation_index10].azimuths[azimuth_index101].impulse_response[sample] * blend_factor_101 + elevations1[elevation_index11].azimuths[azimuth_index110].impulse_response[sample] * blend_factor_110 + elevations1[elevation_index11].azimuths[azimuth_index111].impulse_response[sample] * blend_factor_111; + + value = value0 + value1; +} + +void HrtfData::sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, float& value_left, float& delay_left, float& value_right, float& delay_right) const { + assert(elevation >= -angle_t(pi * 0.5)); + assert(elevation <= angle_t(pi * 0.5)); + assert(azimuth >= -angle_t(2.0 * pi)); + assert(azimuth <= angle_t(2.0 * pi)); + + sample_direction(elevation, azimuth, distance, sample, 0, value_left, delay_left); + if(m_channel_count == 1) { + sample_direction(elevation, -azimuth, distance, sample, 0, value_right, delay_right); + } else { + sample_direction(elevation, azimuth, distance, sample, 1, value_right, delay_right); + } +} diff --git a/Audio/ThirdParty/hrtf/HrtfData.h b/Audio/ThirdParty/hrtf/HrtfData.h new file mode 100644 index 000000000..eec817430 --- /dev/null +++ b/Audio/ThirdParty/hrtf/HrtfData.h @@ -0,0 +1,48 @@ +#pragma once + +#include "HrtfTypes.h" +#include "IHrtfData.h" +#include +#include +#include + +struct ElevationData { + std::vector azimuths; +}; + +struct DistanceData { + distance_t distance; + std::vector elevations; +}; + +class HrtfData : public IHrtfData { + void LoadHrtf00(std::istream& stream); + void LoadHrtf01(std::istream& stream); + void LoadHrtf02(std::istream& stream); + void LoadHrtf03(std::istream& stream); + + public: + HrtfData(std::istream& stream); + + void get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t channel, DirectionData& ref_data) const override; + void get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, DirectionData& ref_data_left, DirectionData& ref_data_right) const override; + void sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, uint32_t channel, float& value, float& delay) const override; + void sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, float& value_left, float& delay_left, float& value_right, float& delay_right) const override; + + uint32_t get_sample_rate() const override { + return m_sample_rate; + } + uint32_t get_response_length() const override { + return m_response_length; + } + uint32_t get_longest_delay() const override { + return m_longest_delay; + } + + private: + uint32_t m_sample_rate; + uint32_t m_response_length; + uint32_t m_longest_delay; + uint32_t m_channel_count; + std::vector m_distances; +}; diff --git a/Audio/ThirdParty/hrtf/HrtfTypes.h b/Audio/ThirdParty/hrtf/HrtfTypes.h new file mode 100644 index 000000000..f260bb1d8 --- /dev/null +++ b/Audio/ThirdParty/hrtf/HrtfTypes.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef float distance_t; +typedef float angle_t; +typedef int delay_t; + +struct DirectionData { + std::vector impulse_response; + delay_t delay; + delay_t delay_right; +}; diff --git a/Audio/ThirdParty/hrtf/IHrtfData.h b/Audio/ThirdParty/hrtf/IHrtfData.h new file mode 100644 index 000000000..93a69aaa5 --- /dev/null +++ b/Audio/ThirdParty/hrtf/IHrtfData.h @@ -0,0 +1,19 @@ +#pragma once + +#include "HrtfTypes.h" + +class IHrtfData { + public: + virtual ~IHrtfData() = default; + + virtual void get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t channel, DirectionData& ref_data) const = 0; + virtual void get_direction_data(angle_t elevation, angle_t azimuth, distance_t distance, DirectionData& ref_data_left, DirectionData& ref_data_right) const = 0; + // Get only once IR sample at given direction. The delay returned is the delay of IR's beginning, not the sample's! + virtual void sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, uint32_t channel, float& value, float& delay) const = 0; + // Get only once IR sample at given direction for both channels. The delay returned is the delay of IR's beginning, not the sample's! + virtual void sample_direction(angle_t elevation, angle_t azimuth, distance_t distance, uint32_t sample, float& value_left, float& delay_left, float& value_right, float& delay_right) const = 0; + + virtual uint32_t get_sample_rate() const = 0; + virtual uint32_t get_response_length() const = 0; + virtual uint32_t get_longest_delay() const = 0; +}; diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 1bea5df1d..3e3fb116d 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -184,6 +184,7 @@ 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; 839B837F286D7F8D00F529EE /* NumberHertzToStringTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839B837E286D7F8D00F529EE /* NumberHertzToStringTransformer.swift */; }; 839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; }; + 839E56F52879625100DFB5F4 /* SADIE_D02-96000.mhr in Resources */ = {isa = PBXBuildFile; fileRef = 839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */; }; 83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */; }; 83AA7D04279EBCA900087AA4 /* libavcodec.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -1026,6 +1027,7 @@ 839DA7CB274A2D4C001B18E5 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Merge.h"; sourceTree = ""; }; 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Merge.m"; sourceTree = ""; }; 839E3B53286595D700880EA2 /* GeneralPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneralPane.h; path = Preferences/Preferences/GeneralPane.h; sourceTree = ""; }; + 839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SADIE_D02-96000.mhr"; sourceTree = ""; }; 83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ColorToValueTransformer.m; path = Preferences/Preferences/ColorToValueTransformer.m; sourceTree = ""; }; 83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ColorToValueTransformer.h; path = Preferences/Preferences/ColorToValueTransformer.h; sourceTree = ""; }; 83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = ""; }; @@ -1536,6 +1538,7 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + 839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */, 837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */, 8316B3922839FFD5004CC392 /* Scenes.scnassets */, 832C1252180BD1E2005507C1 /* Cog.help */, @@ -2486,6 +2489,7 @@ 8384916D18083EAB00E7332D /* volume1Template.pdf in Resources */, 171B57DD0C091F2B00F6AFAF /* flac.icns in Resources */, 171B57DE0C091F2B00F6AFAF /* m4a.icns in Resources */, + 839E56F52879625100DFB5F4 /* SADIE_D02-96000.mhr in Resources */, 8384916C18083EAB00E7332D /* stopTemplate.pdf in Resources */, 830C37A127B95E3000E02BB0 /* Equalizer.xib in Resources */, 171B57DF0C091F2B00F6AFAF /* mp3.icns in Resources */, diff --git a/Preferences/Preferences/Base.lproj/Preferences.xib b/Preferences/Preferences/Base.lproj/Preferences.xib index 2a64d5e5c..0185fbd07 100644 --- a/Preferences/Preferences/Base.lproj/Preferences.xib +++ b/Preferences/Preferences/Base.lproj/Preferences.xib @@ -1,8 +1,8 @@ - + - + @@ -207,11 +207,11 @@ - + - + @@ -232,7 +232,7 @@ - + @@ -241,7 +241,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -271,7 +271,7 @@ + - + @@ -363,7 +374,7 @@ - + @@ -385,7 +396,7 @@ - + @@ -420,7 +431,7 @@ - + diff --git a/Preferences/Preferences/en.lproj/Preferences.strings b/Preferences/Preferences/en.lproj/Preferences.strings index 80667f783..b84a2b684 100644 --- a/Preferences/Preferences/en.lproj/Preferences.strings +++ b/Preferences/Preferences/en.lproj/Preferences.strings @@ -230,3 +230,5 @@ /* Class = "NSButtonCell"; title = "Use 3D rendered spectrum"; ObjectID = "NMg-TO-amV"; */ "NMg-TO-amV.title" = "Use 3D rendered spectrum"; +/* Class = "NSButtonCell"; title = "Enable HRTF filter (Not needed with AirPods or Beats)"; ObjectID = "NGx-0c-WVR"; */ +"NGx-0c-WVR.title" = "Enable HRTF filter (Not needed with AirPods or Beats)"; diff --git a/Preferences/Preferences/es.lproj/Preferences.strings b/Preferences/Preferences/es.lproj/Preferences.strings index 1ddbb05be..a6cdb77a9 100644 --- a/Preferences/Preferences/es.lproj/Preferences.strings +++ b/Preferences/Preferences/es.lproj/Preferences.strings @@ -104,7 +104,7 @@ "FQF-vJ-hBx.title" = "Variedad de MIDI:"; /* Class = "NSButtonCell"; title = "Resume playback on startup"; ObjectID = "fUg-Cg-gXa"; */ -"fUg-Cg-gXa.title" = "Resumir reproducción al iniciar"; +"fUg-Cg-gXa.title" = "Resumir reproducción al abrir"; /* Class = "NSButtonCell"; title = "Colorful dock icons"; ObjectID = "GdX-5e-NeU"; */ "GdX-5e-NeU.title" = "Colorear icono del dock"; @@ -244,3 +244,8 @@ /* Class = "NSButtonCell"; title = "Use 3D rendered spectrum"; ObjectID = "NMg-TO-amV"; */ "NMg-TO-amV.title" = "Usar analizador en tres dimensiones"; +/* Class = "NSButtonCell"; title = "Enable HRTF filter (Not needed with AirPods or Beats)"; ObjectID = "NGx-0c-WVR"; */ +"NGx-0c-WVR.title" = "Activar filtro HRTF (no es necesario con AirPods o Beats)"; + +/* Class = "NSButtonCell"; title = "Automatically check for updates on startup"; ObjectID = "207"; */ +"207.title" = "Buscar actualizaciones al abrir"; diff --git a/SADIE_D02-96000.mhr b/SADIE_D02-96000.mhr new file mode 100644 index 000000000..bc7eb097b Binary files /dev/null and b/SADIE_D02-96000.mhr differ diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index f0a036299..d51f5fd16 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -86,3 +86,5 @@ "dhms" = "%@, %@, %@ and %@"; "hms" = "%@, %@ and %@"; "ms" = "%@ and %@"; + +"PrivacyPolicyURL" = "https://www.iubenda.com/privacy-policy/59859310"; diff --git a/es.lproj/Localizable.strings b/es.lproj/Localizable.strings index c84701c60..cb99bc982 100644 --- a/es.lproj/Localizable.strings +++ b/es.lproj/Localizable.strings @@ -193,3 +193,5 @@ "dhms" = "%@, %@, %@ y %@"; "hms" = "%@, %@ y %@"; "ms" = "%@ y %@"; + +"PrivacyPolicyURL" = "https://www.iubenda.com/privacy-policy/57237510"; diff --git a/es.lproj/MainMenu.strings b/es.lproj/MainMenu.strings index 89ff673f2..77cc9b77b 100644 --- a/es.lproj/MainMenu.strings +++ b/es.lproj/MainMenu.strings @@ -784,3 +784,5 @@ /* Class = "NSToolbarItem"; paletteLabel = "Show Equalizer"; ObjectID = "ZOn-sB-FR3"; */ "ZOn-sB-FR3.paletteLabel" = "Mostrar ecualizador"; +/* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "302"; */ +"302.title" = "Buscar actualizaciones..."; diff --git a/pl.lproj/AboutWindowController.strings b/pl.lproj/AboutWindowController.strings deleted file mode 100644 index f48f3721b..000000000 --- a/pl.lproj/AboutWindowController.strings +++ /dev/null @@ -1,12 +0,0 @@ -T -/* Class = "NSWindow"; title = "About Cog"; ObjectID = "F0z-JX-Cv5"; */ -"F0z-JX-Cv5.title" = "o Cog"; - -/* Class = "NSTextFieldCell"; title = "Version..."; ObjectID = "Wjf-By-C1F"; */ -"Wjf-By-C1F.title" = "wersja..."; - -/* Class = "NSTextFieldCell"; title = "Cog"; ObjectID = "hzl-Rl-e01"; */ -"hzl-Rl-e01.title" = "Cog"; - -/* Class = "NSTextFieldCell"; title = "Copyright..."; ObjectID = "wLU-AJ-J0b"; */ -"wLU-AJ-J0b.title" = "informacje Copyright..."; diff --git a/pl.lproj/Credits.html b/pl.lproj/Credits.html deleted file mode 100644 index 6952a3947..000000000 --- a/pl.lproj/Credits.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - Credits for the Cog audio player - - - - -
- Now with 92% more future!

Thanks to my patrons: -
Alexander Pecheny, Electric Keet, Antti Aro, Fjölnir Ásgeirsson, Giampaolo Bellavite, Louis Martinez V, deef.xyz -

Additional code contributions by Dzmitry Neviadomski and Kevin López Brante. -

- This program has been made possible through contributions from users like you. -

All Cog code is copyrighted by me, and is licensed under the GPL. Cog contains bits of other code from third parties that are under their own licenses. -

Sample rate converter designed by Aleksey Vaneev of Voxengo. -
- - - diff --git a/pl.lproj/Equalizer.strings b/pl.lproj/Equalizer.strings deleted file mode 100644 index 5414cd967..000000000 --- a/pl.lproj/Equalizer.strings +++ /dev/null @@ -1,144 +0,0 @@ - -/* Class = "NSTextFieldCell"; title = "63"; ObjectID = "0aZ-qR-VQB"; */ -"0aZ-qR-VQB.title" = "63"; - -/* Class = "NSTextFieldCell"; title = "80"; ObjectID = "0yY-ZG-55W"; */ -"0yY-ZG-55W.title" = "80"; - -/* Class = "NSTextFieldCell"; title = "31.5"; ObjectID = "1dn-Vq-wWg"; */ -"1dn-Vq-wWg.title" = "31.5"; - -/* Class = "NSTextFieldCell"; title = "10"; ObjectID = "3uY-U8-fJj"; */ -"3uY-U8-fJj.title" = "10"; - -/* Class = "NSTextFieldCell"; title = "-12 dB"; ObjectID = "7O0-sA-hE3"; */ -"7O0-sA-hE3.title" = "-12 dB"; - -/* Class = "NSTextFieldCell"; title = "8"; ObjectID = "8bT-Kt-cyl"; */ -"8bT-Kt-cyl.title" = "8"; - -/* Class = "NSWindow"; title = "Equalizer"; ObjectID = "9Xl-RA-0SW"; */ -"9Xl-RA-0SW.title" = "Equalizer"; - -/* Class = "NSTextFieldCell"; title = "1.2"; ObjectID = "A7Y-xq-LdW"; */ -"A7Y-xq-LdW.title" = "1.2"; - -/* Class = "NSTextFieldCell"; title = "100"; ObjectID = "B9P-B7-9bf"; */ -"B9P-B7-9bf.title" = "100"; - -/* Class = "NSTextFieldCell"; title = "400"; ObjectID = "ERP-9r-xp9"; */ -"ERP-9r-xp9.title" = "400"; - -/* Class = "NSButtonCell"; title = "Enabled"; ObjectID = "Id9-5k-q9Q"; */ -"Id9-5k-q9Q.title" = "Enabled"; - -/* Class = "NSTextFieldCell"; title = "500"; ObjectID = "JVJ-LV-LDc"; */ -"JVJ-LV-LDc.title" = "500"; - -/* Class = "NSTextFieldCell"; title = "20 dB"; ObjectID = "KP1-mo-Quy"; */ -"KP1-mo-Quy.title" = "20 dB"; - -/* Class = "NSTextFieldCell"; title = "20kHz"; ObjectID = "LoH-XP-I4Y"; */ -"LoH-XP-I4Y.title" = "20kHz"; - -/* Class = "NSTextFieldCell"; title = "2.5"; ObjectID = "Nc6-vb-nIc"; */ -"Nc6-vb-nIc.title" = "2.5"; - -/* Class = "NSTextFieldCell"; title = "3 dB"; ObjectID = "SBw-cK-76N"; */ -"SBw-cK-76N.title" = "3 dB"; - -/* Class = "NSTextFieldCell"; title = "2"; ObjectID = "TYp-Ae-UMg"; */ -"TYp-Ae-UMg.title" = "2"; - -/* Class = "NSTextFieldCell"; title = "160"; ObjectID = "TnM-7Z-BUu"; */ -"TnM-7Z-BUu.title" = "160"; - -/* Class = "NSTextFieldCell"; title = "12"; ObjectID = "UEO-zv-YmO"; */ -"UEO-zv-YmO.title" = "12"; - -/* Class = "NSTextFieldCell"; title = "50"; ObjectID = "UOZ-JN-QIr"; */ -"UOZ-JN-QIr.title" = "50"; - -/* Class = "NSTextFieldCell"; title = "20Hz"; ObjectID = "XmE-PI-PFN"; */ -"XmE-PI-PFN.title" = "20Hz"; - -/* Class = "NSTextFieldCell"; title = "200"; ObjectID = "Ze7-D0-V5z"; */ -"Ze7-D0-V5z.title" = "200"; - -/* Class = "NSTextFieldCell"; title = "-6 dB"; ObjectID = "Zlw-xU-QP7"; */ -"Zlw-xU-QP7.title" = "-6 dB"; - -/* Class = "NSButtonCell"; title = "Tracking genre tags"; ObjectID = "aeV-tB-rvh"; */ -"aeV-tB-rvh.title" = "Tracking genre tags"; - -/* Class = "NSButtonCell"; title = "Flatten EQ"; ObjectID = "bOS-GE-rwi"; */ -"bOS-GE-rwi.title" = "Flatten EQ"; - -/* Class = "NSTextFieldCell"; title = "630"; ObjectID = "bqX-Dk-bJ9"; */ -"bqX-Dk-bJ9.title" = "630"; - -/* Class = "NSTextFieldCell"; title = "25"; ObjectID = "cO5-1C-qEl"; */ -"cO5-1C-qEl.title" = "25"; - -/* Class = "NSTextFieldCell"; title = "0 dB"; ObjectID = "cq3-MT-eo1"; */ -"cq3-MT-eo1.title" = "0 dB"; - -/* Class = "NSTextFieldCell"; title = "4"; ObjectID = "e6h-zW-Op6"; */ -"e6h-zW-Op6.title" = "4"; - -/* Class = "NSTextFieldCell"; title = "16"; ObjectID = "e71-GI-ntH"; */ -"e71-GI-ntH.title" = "16"; - -/* Class = "NSTextFieldCell"; title = "800"; ObjectID = "eB6-XM-qUy"; */ -"eB6-XM-qUy.title" = "800"; - -/* Class = "NSTextFieldCell"; title = "250"; ObjectID = "efh-nh-mDD"; */ -"efh-nh-mDD.title" = "250"; - -/* Class = "NSTextFieldCell"; title = "-20 dB"; ObjectID = "fJ3-i8-mw9"; */ -"fJ3-i8-mw9.title" = "-20 dB"; - -/* Class = "NSTextFieldCell"; title = "6 dB"; ObjectID = "fpg-RS-w2Y"; */ -"fpg-RS-w2Y.title" = "6 dB"; - -/* Class = "NSTextFieldCell"; title = "315"; ObjectID = "g10-wn-Agu"; */ -"g10-wn-Agu.title" = "315"; - -/* Class = "NSTextFieldCell"; title = "40"; ObjectID = "ihA-eC-L0F"; */ -"ihA-eC-L0F.title" = "40"; - -/* Class = "NSTextFieldCell"; title = "3.1"; ObjectID = "kdf-iA-xrZ"; */ -"kdf-iA-xrZ.title" = "3.1"; - -/* Class = "NSTextFieldCell"; title = "5"; ObjectID = "ke7-4o-FJG"; */ -"ke7-4o-FJG.title" = "5"; - -/* Class = "NSTextFieldCell"; title = "Note: You may use right-click to draw an equalizer shape"; ObjectID = "lwG-Tm-rr1"; */ -"lwG-Tm-rr1.title" = "Note: You may use right-click to draw an equalizer shape"; - -/* Class = "NSTextFieldCell"; title = "6.3"; ObjectID = "neW-5g-R0i"; */ -"neW-5g-R0i.title" = "6.3"; - -/* Class = "NSTextFieldCell"; title = "12 dB"; ObjectID = "nqJ-h3-mQq"; */ -"nqJ-h3-mQq.title" = "12 dB"; - -/* Class = "NSTextFieldCell"; title = "125"; ObjectID = "opU-zn-biU"; */ -"opU-zn-biU.title" = "125"; - -/* Class = "NSTextFieldCell"; title = "-3 dB"; ObjectID = "pGw-Cf-6Nj"; */ -"pGw-Cf-6Nj.title" = "-3 dB"; - -/* Class = "NSTextFieldCell"; title = "1.6"; ObjectID = "qj9-4A-tfF"; */ -"qj9-4A-tfF.title" = "1.6"; - -/* Class = "NSButtonCell"; title = "Level Preamp"; ObjectID = "rck-mI-bed"; */ -"rck-mI-bed.title" = "Level Preamp"; - -/* Class = "NSTextFieldCell"; title = "Preamp"; ObjectID = "wzI-tF-Y2S"; */ -"wzI-tF-Y2S.title" = "Preamp"; - -/* Class = "NSTextFieldCell"; title = "1k"; ObjectID = "xM8-HB-8XL"; */ -"xM8-HB-8XL.title" = "1k"; - -/* Class = "NSTextFieldCell"; title = "Preset:"; ObjectID = "xsQ-DN-AcS"; */ -"xsQ-DN-AcS.title" = "Preset:"; diff --git a/pl.lproj/Feedback.strings b/pl.lproj/Feedback.strings deleted file mode 100644 index 6687aec04..000000000 --- a/pl.lproj/Feedback.strings +++ /dev/null @@ -1,17 +0,0 @@ -/* Class = "NSWindow"; title = "Send Feedback"; ObjectID = "1"; */ -"1.title" = "Wyślij opinie"; - -/* Class = "NSButtonCell"; title = "Send"; ObjectID = "17"; */ -"17.title" = "Wyślij"; - -/* Class = "NSTextFieldCell"; title = "Subject:"; ObjectID = "18"; */ -"18.title" = "temat:"; - -/* Class = "NSTextFieldCell"; title = "Message:"; ObjectID = "19"; */ -"19.title" = "wiadomość:"; - -/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "20"; */ -"20.title" = "anuluj"; - -/* Class = "NSTextFieldCell"; title = "Email:"; ObjectID = "22"; */ -"22.title" = "Email:"; diff --git a/pl.lproj/FileTree.strings b/pl.lproj/FileTree.strings deleted file mode 100644 index 5e90ea13b..000000000 --- a/pl.lproj/FileTree.strings +++ /dev/null @@ -1,21 +0,0 @@ - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "71"; */ -"71.title" = "Text Cell"; - -/* Class = "NSMenu"; title = "Menu"; ObjectID = "110"; */ -"110.title" = "Menu"; - -/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "112"; */ -"112.title" = "Show in Finder"; - -/* Class = "NSMenuItem"; title = "Add to Playlist"; ObjectID = "119"; */ -"119.title" = "Add to Playlist"; - -/* Class = "NSMenuItem"; title = "Set as Root"; ObjectID = "124"; */ -"124.title" = "Set as Root"; - -/* Class = "NSMenuItem"; title = "Set as Playlist"; ObjectID = "129"; */ -"129.title" = "Set as Playlist"; - -/* Class = "NSBox"; title = "Box"; ObjectID = "147"; */ -"147.title" = "Box"; diff --git a/pl.lproj/InfoInspector.strings b/pl.lproj/InfoInspector.strings deleted file mode 100644 index 0a67f6400..000000000 --- a/pl.lproj/InfoInspector.strings +++ /dev/null @@ -1,84 +0,0 @@ - -/* Class = "NSWindow"; title = "Info Inspector"; ObjectID = "1"; */ -"1.title" = "Info Inspector"; - -/* Class = "NSTextFieldCell"; title = "Artist:"; ObjectID = "10"; */ -"10.title" = "Artist:"; - -/* Class = "NSTextFieldCell"; title = "Album:"; ObjectID = "12"; */ -"12.title" = "Album:"; - -/* Class = "NSTextFieldCell"; title = "Track:"; ObjectID = "14"; */ -"14.title" = "Track:"; - -/* Class = "NSTextFieldCell"; title = "Length:"; ObjectID = "16"; */ -"16.title" = "Length:"; - -/* Class = "NSTextFieldCell"; title = "Genre:"; ObjectID = "20"; */ -"20.title" = "Genre:"; - -/* Class = "NSTextFieldCell"; title = "Sample Rate:"; ObjectID = "22"; */ -"22.title" = "Sample Rate:"; - -/* Class = "NSTextFieldCell"; title = "Title:"; ObjectID = "24"; */ -"24.title" = "Title:"; - -/* Class = "NSTextFieldCell"; title = "Channels:"; ObjectID = "28"; */ -"28.title" = "Channels:"; - -/* Class = "NSTextFieldCell"; title = "Bits Per Sample:"; ObjectID = "31"; */ -"31.title" = "Bits Per Sample:"; - -/* Class = "NSTextFieldCell"; title = "Bitrate:"; ObjectID = "32"; */ -"32.title" = "Bitrate:"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "34"; */ -"34.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "36"; */ -"36.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "38"; */ -"38.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "40"; */ -"40.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "42"; */ -"42.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "44"; */ -"44.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "46"; */ -"46.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "50"; */ -"50.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "52"; */ -"52.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "54"; */ -"54.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "56"; */ -"56.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "Filename:"; ObjectID = "85"; */ -"85.title" = "Filename:"; - -/* Class = "NSTextFieldCell"; title = "N/A"; ObjectID = "87"; */ -"87.title" = "N/A"; - -/* Class = "NSTextFieldCell"; title = "Album Artist:"; ObjectID = "vB6-9J-5qg"; */ -"vB6-9J-5qg.title" = "Album Artist:"; - -/* Class = "NSTextFieldCell"; title = "Codec:"; ObjectID = "QPg-Mb-Urn"; */ -"QPg-Mb-Urn.title" = "Codec:"; - -/* Class = "NSTextFieldCell"; title = "Date:"; ObjectID = "17"; */ -"18.title" = "Date:"; - -/* Class = "NSTextFieldCell"; title = "Comment:"; ObjectID = "cd3-Qt-hCm"; */ -"Ule-N3-dKW.title" = "Comment:"; diff --git a/pl.lproj/InfoPlist.strings b/pl.lproj/InfoPlist.strings deleted file mode 100644 index 936c0ee3d..000000000 --- a/pl.lproj/InfoPlist.strings +++ /dev/null @@ -1,4 +0,0 @@ -/* Localized versions of Info.plist keys */ - -CFBundleName = "Cog"; -NSHumanReadableCopyright = "© Vincent Spader, 2005-2011\n© mamburu, 2012-2013\n© Christopher Snowhill, 2013-2022"; diff --git a/pl.lproj/Localizable.strings b/pl.lproj/Localizable.strings deleted file mode 100644 index f0a036299..000000000 --- a/pl.lproj/Localizable.strings +++ /dev/null @@ -1,88 +0,0 @@ -"FeedbackFailedMessageText" = "Failed"; -"FeedbackFailedInformativeText" = "Feedback failed to send."; -"FeedbackSuccessMessageText" = "Success"; -"FeedbackSuccessInformativeText" = "Feedback successfully sent!"; - -"PlayButtonTooltip" = "Play"; -"StopButtonTooltip" = "Stop"; -"PrevButtonTooltip" = "Previous"; -"NextButtonTooltip" = "Next"; - -"TimeElapsed" = "%i:%02i"; -"TimeRemaining" = "-%i:%02i"; - -"AddButtonTooltip" = "Add files"; -"RemoveButtonTooltip" = "Remove selected files"; -"InfoButtonTooltip" = "Information on the selected file"; -"ShuffleButtonTooltip" = "Shuffle mode"; -"RepeatButtonTooltip" = "Repeat mode"; -"RandomizeButtonTooltip" = "Randomize playlist"; -"FileButtonTooltip" = "File drawer"; - -"replayGainAlbumGain" = "The volume level of playback output will be detected through enumeration, \ - re"; - -"InvalidURLShort" = "Invalid URL"; -"InvalidURLLong" = "The URL is not valid."; - -"SwitchToMiniPlayer" = "Switch to Mini Player"; -"SwitchFromMiniPlayer" = "Switch from Mini Player"; - -"PlayingTrackTooltip" = "Playing"; -"QueuedTrackTooltip" = "Queued"; -"ErrorTrackTooltip" = "Error"; -"StopAfterTrackTooltip" = "Stop After Track"; - -"PlaylistHeaderContextMenuTitle" = "Playlist Header Context Menu"; - -"PlaylistStatusColumn" = "Status"; -"PlaylistIndexColumn" = "Index"; - -"PlaylistRandomizationAction" = "Playlist Randomization"; - -"SkipAction" = "Skip"; - -"CogTitle" = "Cog"; - -"PreferencesTitle" = "Preferences"; - -"ProgressActionLoader" = "playlist loader inserting files"; -"ProgressSubActionLoaderListingFiles" = "collecting files"; -"ProgressSubActionLoaderFilteringContainerFiles" = "handling container file types"; -"ProgressSubActionLoaderFilteringFiles" = "eliminating unsupported file types"; -"ProgressSubActionLoaderFilteringContainedFiles" = "eliminating unsupported file types from containers"; -"ProgressSubActionLoaderAddingEntries" = "creating and adding playlist entries"; - -"ProgressActionLoadingMetadata" = "loading metadata for tracks"; -"ProgressSubActionLoadingMetadata" = "processing files"; -"ProgressSubActionMetadataApply" = "applying info to playlist storage"; - -"ErrorMetadata" = "Unable to retrieve metadata."; -"ErrorMessageBadFile" = "Unable to parse metadata for bad file."; -"ErrorInvalidTrackId" = "Invalid track ID sent to SQLite request."; -"ErrorSqliteProblem" = "General problem accessing track from SQLite database."; -"ErrorTrackMissing" = "Track entry is missing from SQLite database."; - -"TimeLastPlayed" = "Last played"; -"TimeFirstSeen" = "First seen"; - -"GainAlbumGain" = "Album Gain"; -"GainAlbumGainPeak" = "Album Gain plus Peak"; -"GainAlbumPeak" = "Album Peak"; -"GainTrackGain" = "Track Gain"; -"GainTrackGainPeak" = "Track Gain plus Peak"; -"GainTrackPeak" = "Track Peak"; -"GainVolumeScale" = "Volume Scale"; -"GainNone" = "None"; - -"CrashlyticsConsentTitle" = "Crashlytics crash collection"; -"CrashlyticsConsentText" = "Would you like to allow Crashlytics to submit crash reports? You may turn this off again in Preferences. We won't ask you again."; -"ConsentYes" = "Yes"; -"ConsentNo" = "No"; - -"Total duration: %@" = "Total duration: %@"; - -"wdhms" = "%@, %@, %@, %@ and %@"; -"dhms" = "%@, %@, %@ and %@"; -"hms" = "%@, %@ and %@"; -"ms" = "%@ and %@"; diff --git a/pl.lproj/Localizable.stringsdict b/pl.lproj/Localizable.stringsdict deleted file mode 100644 index 424e72dd5..000000000 --- a/pl.lproj/Localizable.stringsdict +++ /dev/null @@ -1,96 +0,0 @@ - - - - - %1d week(s) - - NSStringLocalizedFormatKey - %#@tygodni@ - weeks - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - 0 tygodni - one - 1 tydźeń - other - %d tygodni - - - %1d day(s) - - NSStringLocalizedFormatKey - %#@dni@ - days - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - 0 dni - one - 1 dzień - other - %d dni - - - %1d hour(s) - - NSStringLocalizedFormatKey - %#@godzin@ - hours - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - 0 godzin - one - 1 godzina - other - %d godzin - - - %1d minute(s) - - NSStringLocalizedFormatKey - %#@minut@ - minutes - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - 0 minut - one - 1 minuta - other - %d minut - - - %1d second(s) - - NSStringLocalizedFormatKey - %#@sekund@ - seconds - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - 0 secund - one - 1 secunda - other - %d sekund - - - - diff --git a/pl.lproj/MainMenu.strings b/pl.lproj/MainMenu.strings deleted file mode 100644 index dca12ee63..000000000 --- a/pl.lproj/MainMenu.strings +++ /dev/null @@ -1,628 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Bring All to Front"; ObjectID = "5"; */ -"5.title" = "Bring All to Front"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "okno"; - -/* Class = "NSWindow"; title = "Cog"; ObjectID = "21"; */ -"21.title" = "Cog"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "zminimalizuj"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "okno"; - -/* Class = "NSMenu"; title = "MainMenu"; ObjectID = "29"; */ -"29.title" = "menu głuwne"; - -/* Class = "NSMenuItem"; title = "Cog"; ObjectID = "56"; */ -"56.title" = "Cog"; - -/* Class = "NSMenu"; title = "Cog"; ObjectID = "57"; */ -"57.title" = "Cog"; - -/* Class = "NSMenuItem"; title = "About Cog"; ObjectID = "58"; */ -"58.title" = "o Cog"; - -/* Class = "NSMenuItem"; title = "Add File..."; ObjectID = "72"; */ -"72.title" = "dodaj plik..."; - -/* Class = "NSMenuItem"; title = "Close Window"; ObjectID = "73"; */ -"73.title" = "zamknij okno"; - -/* Class = "NSMenuItem"; title = "Save As..."; ObjectID = "75"; */ -"75.title" = "Zapisz jako..."; - -/* Class = "NSMenu"; title = "File"; ObjectID = "81"; */ -"81.title" = "plik"; - -/* Class = "NSMenuItem"; title = "File"; ObjectID = "83"; */ -"83.title" = "plik"; - -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "pomoc"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "pomoc"; - -/* Class = "NSMenuItem"; title = "Cog Help"; ObjectID = "111"; */ -"111.title" = "Pomoc programó cog"; - -/* Class = "NSMenuItem"; title = "Preferences…"; ObjectID = "129"; */ -"129.title" = "Preferencje…"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "usługi"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "usługi"; - -/* Class = "NSMenuItem"; title = "Hide Cog"; ObjectID = "134"; */ -"134.title" = "ukryj Cog"; - -/* Class = "NSMenuItem"; title = "Quit Cog"; ObjectID = "136"; */ -"136.title" = "zamknij Cog"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "ukryj inne aplikacje"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "pokaż wszystko"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "157"; */ -"157.title" = "kopiuj"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "158"; */ -"158.title" = "cofnij"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "160"; */ -"160.title" = "wytnij"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "163"; */ -"163.title" = "edycja"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "164"; */ -"164.title" = "usuń"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "169"; */ -"169.title" = "edycja"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "171"; */ -"171.title" = "wklej"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "172"; */ -"172.title" = "Zaznacz wszystko"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "173"; */ -"173.title" = "anuluj cofnięcie"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "197"; */ -"197.title" = "Zoom"; - -/* Class = "NSTableColumn"; headerCell.title = "Rating"; ObjectID = "208"; */ -"208.headerCell.title" = "Rating"; - -/* Class = "NSTableColumn"; headerCell.title = "#"; ObjectID = "209"; */ -"209.headerCell.title" = "#"; - -/* Class = "NSMenuItem"; title = "sprawdź, czy jest nowa wersja..."; ObjectID = "302"; */..."; -/* Class = "NSMenuItem"; title = "send feedback..."; ObjectID = "303"; */ -"303.title" = "wyślij opinię..."; - -/* Class = "NSTableColumn"; headerCell.title = "Artist"; ObjectID = "391"; */ -"391.headerCell.title" = "wykonawca"; - -/* Class = "NSMenuItem"; title = "Control"; ObjectID = "418"; */ -"418.title" = "Control"; - -/* Class = "NSMenu"; title = "Control"; ObjectID = "419"; */ -"419.title" = "Control"; - -/* Class = "NSMenuItem"; title = "Play/Pause"; ObjectID = "420"; */ -"420.title" = "graj/pałza"; - -/* Class = "NSMenuItem"; title = "Next Song"; ObjectID = "422"; */ -"422.title" = "następna piosenka"; - -/* Class = "NSMenuItem"; title = "Previous Song"; ObjectID = "423"; */ -"423.title" = "poprzednia piosenka"; - -/* Class = "NSMenu"; title = "Menu"; ObjectID = "513"; */ -"513.title" = "Menu"; - -/* Class = "NSMenuItem"; title = "Stop"; ObjectID = "517"; */ -"517.title" = "Stop"; - -/* Class = "NSMenuItem"; title = "Next Track"; ObjectID = "518"; */ -"518.title" = "następny utwór"; - -/* Class = "NSMenuItem"; title = "Previous Track"; ObjectID = "519"; */ -"519.title" = "poprzedni utwór"; - -/* Class = "NSMenuItem"; title = "Play/Pause"; ObjectID = "520"; */ -"520.title" = "graj/pauza"; - -/* Class = "NSMenuItem"; title = "Cog"; ObjectID = "702"; */ -"702.title" = "Cog"; - -/* Class = "NSMenuItem"; title = "Current Song"; ObjectID = "791"; */ -"791.title" = "grana piosenka"; - -/* Class = "NSTableColumn"; headerCell.title = "Album"; ObjectID = "806"; */ -"806.headerCell.title" = "Album"; - -/* Class = "NSTableColumn"; headerCell.title = "Length"; ObjectID = "807"; */ -"807.headerCell.title" = "długość"; - -/* Class = "NSTableColumn"; headerCell.title = "Year"; ObjectID = "848"; */ -"848.headerCell.title" = "rok"; - -/* Class = "NSTableColumn"; headerCell.title = "Genre"; ObjectID = "849"; */ -"849.headerCell.title" = "gatunek"; - -/* Class = "NSTableColumn"; headerCell.title = "№"; ObjectID = "850"; */ -"850.headerCell.title" = "№"; - -/* Class = "NSMenuItem"; title = "Playlist"; ObjectID = "881"; */ -"881.title" = "Playlista"; - -/* Class = "NSMenu"; title = "Playlist"; ObjectID = "882"; */ -"882.title" = "Playlista"; - -/* Class = "NSMenu"; title = "Menu"; ObjectID = "1063"; */ -"1063.title" = "Menu"; - -/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "1064"; */ -"1064.title" = "pokaż w finderze"; - -/* Class = "NSMenuItem"; title = "Show All Songs"; ObjectID = "1132"; */ -"1132.title" = "pokaż wszystkie piosenki";tunek - -/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "1135"; */ -"1135.title" = "pokaż w finderze"; - -/* Class = "NSMenuItem"; title = "Volume Up"; ObjectID = "1136"; */ -"1136.title" = "głośniej"; - -/* Class = "NSMenuItem"; title = "Volume Down"; ObjectID = "1137"; */ -"1137.title" = "ciszej"; - -/* Class = "NSMenu"; title = "Menu"; ObjectID = "1324"; */ -"1324.title" = "Menu"; - -/* Class = "NSMenuItem"; title = "Index"; ObjectID = "1337"; */ -"1337.title" = "Index"; - -/* Class = "NSMenuItem"; title = "Title"; ObjectID = "1338"; */ -"1338.title" = "tytuł"; - -/* Class = "NSMenuItem"; title = "Artist"; ObjectID = "1339"; */ -"1339.title" = "wykonawca"; - -/* Class = "NSMenuItem"; title = "Album"; ObjectID = "1340"; */ -"1340.title" = "Album"; - -/* Class = "NSMenuItem"; title = "Genre"; ObjectID = "1341"; */ -"1341.title" = "gatunek"; - -/* Class = "NSMenuItem"; title = "Length"; ObjectID = "1342"; */ -"1342.title" = "długość"; - -/* Class = "NSMenuItem"; title = "Track"; ObjectID = "1343"; */ -"1343.title" = "utwór"; - -/* Class = "NSMenuItem"; title = "Year"; ObjectID = "1344"; */ -"1344.title" = "rok"; - -/* Class = "NSMenuItem"; title = "Remove"; ObjectID = "1360"; */ -"1360.title" = "usuń"; - -/* Class = "NSMenuItem"; title = "Stop"; ObjectID = "1362"; */ -"1362.title" = "Stop"; - -/* Class = "NSMenuItem"; title = "Remove All"; ObjectID = "1402"; */ -"1402.title" = "usuń wszystko"; - -/* Class = "NSMenuItem"; title = "Add URL..."; ObjectID = "1403"; */ -"1403.title" = "dodaj adres URL..."; - -/* Class = "NSMenuItem"; title = "Remove"; ObjectID = "1408"; */ -"1408.title" = "usuń"; - -/* Class = "NSMenuItem"; title = "Current Artist"; ObjectID = "1454"; */ -"1454.title" = "Obecny wykonawca"; - -/* Class = "NSTextFieldCell"; title = "Total Duration: 00 hours 00 minutes 00 seconds"; ObjectID = "1473"; */ -"1473.title" = "całkowita długość: 00 godzin 00 minut 00 sekund"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1506"; */ -"1506.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1507"; */ -"1507.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1508"; */ -"1508.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1509"; */ -"1509.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1510"; */ -"1510.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1511"; */ -"1511.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1512"; */ -"1512.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1513"; */ -"1513.title" = "Text Cell"; - -/* Class = "NSSearchFieldCell"; placeholderString = "All"; ObjectID = "1532"; */ -"1532.placeholderString" = "wszystkie"; - -/* Class = "NSToolbarItem"; label = "Search"; ObjectID = "1533"; */ -"1533.label" = "szukaj"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "1533"; */ -"1533.paletteLabel" = "Szukaj"; - -/* Class = "NSSegmentedCell"; 1538.ibShadowedToolTips[0] = "Previous"; ObjectID = "1538"; */ -"1538.ibShadowedToolTips[0]" = "Poprzedni"; - -/* Class = "NSSegmentedCell"; 1538.ibShadowedToolTips[1] = "Play"; ObjectID = "1538"; */ -"1538.ibShadowedToolTips[1]" = "odtwarzaj"; - -/* Class = "NSSegmentedCell"; 1538.ibShadowedToolTips[2] = "Stop"; ObjectID = "1538"; */ -"1538.ibShadowedToolTips[2]" = "pauza"; - -/* Class = "NSSegmentedCell"; 1538.ibShadowedToolTips[3] = "Next"; ObjectID = "1538"; */ -"1538.ibShadowedToolTips[3]" = "następne"; - -/* Class = "NSToolbarItem"; label = "Playback con""; ObjectID = "1539"; */ -"1539.label" = "kontrola odtwarzania; - -/* Class = "NSToolbarItem"; paletteLabel = "Playback Control"; ObjectID = "1539"; */ -"1539.paletteLabel" = "kontrola odtwarzania"; - -/* Class = "NSToolbarItem"; label = "Position"; ObjectID = "1551"; */ -"1551.label" = "pozycja"; - -/* Class = "NSToolbarItem"; paletteLabel = "Position"; ObjectID = "1551"; */ -"1551.paletteLabel" = "Pozycja"; - -/* Class = "NSTextFieldCell"; title = "0:00"; ObjectID = "1567"; */ -"1567.title" = "0:00"; - -/* Class = "NSToolbarItem"; label = "Current Time"; ObjectID = "1568"; */ -"1568.label" = "Przesłuchano:"; - -/* Class = "NSToolbarItem"; paletteLabel = "Current Time"; ObjectID = "1568"; */ -"1568.paletteLabel" =przesłuchano"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayName = "All"; ObjectID = "1576"; */ -"1576.ibShadowedDisplayName" = "All"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayName = "Title"; ObjectID = "1578"; */ -"1578.ibShadowedDisplayName" = "tytuł"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayName = "Artist"; ObjectID = "1580"; */ -"1580.ibShadowedDisplayName" = "artysta"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayName = "Album"; ObjectID = "1582"; */ -"1582.ibShadowedDisplayName" = "Album"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayName = "Genre"; ObjectID = "1583"; */ -"1583.ibShadowedDisplayName" = "gatunek"; - -/* Class = "NSToolbarItem"; label = "Volume"; ObjectID = "1610"; */ -"1610.label" = "głośność"; - -/* Class = "NSToolbarItem"; paletteLabel = "Volume"; ObjectID = "1610"; */ -"1610.paletteLabel" = "głośność"; - -/* Class = "NSToolbarItem"; label = "Info Inspector"; ObjectID = "1629"; */ -"1629.label" = "inspektor informacji o pliku"; - -/* Class = "NSToolbarItem"; paletteLabel = "InfoInspector"; ObjectID = "1629"; */ -"1629.paletteLabel" = "inspektor informacji o pliku"; - -/* Class = "NSToolbarItem"; label = "File Tree"; ObjectID = "1630"; */ -"1630.label" = "drzewo plików"; - -/* Class = "NSToolbarItem"; paletteLabel = "File Tree"; ObjectID = "1630"; */ -"1630.paletteLabel" = "drzewo plików"; - -/* Class = "NSToolbarItem"; label = "Shuffle"; ObjectID = "1636"; */ -"1636.label" = "losowo"; - -/* Class = "NSToolbarItem"; paletteLabel = "Shuffle"; ObjectID = "1636"; */ -"1636.paletteLabel" = "losowo"; - -/* Class = "NSToolbarItem"; label = "Repeat"; ObjectID = "1639"; */ -"1639.label" = "Powtórz"; - -/* Class = "NSToolbarItem"; paletteLabel = "Repeat"; ObjectID = "1639"; */ -"1639.paletteLabel" = "Powtórz"; - -/* Class = "NSMenuItem"; title = "Seek Backward"; ObjectID = "1683"; */ -"1683.title" = "przewiń do tyłu"; - -/* Class = "NSMenuItem"; title = "Seek Forward"; ObjectID = "1685"; */ -"1685.title" = "przewiń do przodu"; - -/* Class = "NSMenuItem"; title = "Previous Album"; ObjectID = "1699"; */ -"1699.title" = "poprzedni Album"; - -/* Class = "NSMenuItem"; title = "Next Album"; ObjectID = "1701"; */ -"1701.title" = "następny Album"; - -/* Class = "NSTableColumn"; headerCell.title = "Path"; ObjectID = "1712"; */ -"1712.headerCell.title" = "Ścieżka"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1713"; */ -"1713.title" = "Text Cell"; - -/* Class = "NSTableColumn"; headerCell.title = "Filename"; ObjectID = "1736"; */ -"1736.headerCell.title" = "Nazwa pliku"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "1737"; */ -"1737.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Repeat"; ObjectID = "1740"; */ -"1740.title" = "powtórz"; - -/* Class = "NSMenu"; title = "Repeat"; ObjectID = "1741"; */ -"1741.title" = "Powtórz"; - -/* Class = "NSMenuItem"; title = "All"; ObjectID = "1742"; */ -"1742.title" = "wszystkie"; - -/* Class = "NSMenuItem"; title = "One"; ObjectID = "1746"; */ -"1746.title" = "jedną"; - -/* Class = "NSMenuItem"; title = "None"; ObjectID = "1749"; */ -"1749.title" = "nic"; - -/* Class = "NSMenuItem"; title = "Remove All from Queue"; ObjectID = "1791"; */ -"1791.title" = "usuń wszystko z kolejki"; - -/* Class = "NSMenuItem"; title = "Select Currently Playing"; ObjectID = "1823"; */ -"1823.title" = "zaznacz obecnie grane."; - -/* Class = "NSMenuItem"; title = "Filter Playlist"; ObjectID = "1824"; */ -"1824.title" = "filtruj Playlistę"; - -/* Class = "NSMenuItem"; title = "View"; ObjectID = "1848"; */ -"1848.title" = "widok"; - -/* Class = "NSMenu"; title = "View"; ObjectID = "1849"; */ -"1849.title" = "widok"; - -/* Class = "NSMenuItem"; title = "Show Spotlight Panel"; ObjectID = "1853"; */ -"1853.title" = "pokaż panel Spotlight"; - -/* Class = "NSMenuItem"; title = "Customize Toolbar..."; ObjectID = "1856"; */ -"1856.title" = "spersonalizuj pasek narzędzi..."; - -/* Class = "NSMenuItem"; title = "Increase Font Size"; ObjectID = "1858"; */ -"1858.title" = "powiększ rozmiar czcionki"; - -/* Class = "NSMenuItem"; title = "Decrease Font Size"; ObjectID = "1859"; */ -"1859.title" = "pomniejsz rozmiar czcionki"; - -/* Class = "NSMenuItem"; title = "Add to Queue"; ObjectID = "1864"; */ -"1864.title" = "dodaj do kolejki"; - -/* Class = "NSMenuItem"; title = "Remove from Queue"; ObjectID = "1865"; */ -"1865.title" = "usuń z kolejki"; - -/* Class = "NSMenuItem"; title = "Search for Songs by Artist"; ObjectID = "1867"; */ -"1867.title" = "szukaj piosenek od wykonawcy"; - -/* Class = "NSMenuItem"; title = "Search for Songs from Album"; ObjectID = "1868"; */ -"1868.title" = "wyszukaj piosenkę z albumu"; - -/* Class = "NSMenuItem"; title = "Fade In/Out"; ObjectID = "1885"; */ -"1885.title" = "Fade In/Out"; - -/* Class = "CocoaBindingsConnection"; ibShadowedDisplayPattern = "Total Duration: %{value1}@"; ObjectID = "1891"; */ -"1891.ibShadowedDisplayPattern" = "całkowity czas trwania: %{value1}@"; - -/* Class = "NSMenuItem"; title = "Album"; ObjectID = "1892"; */ -"1892.title" = "album"; - -/* Class = "NSMenuItem"; title = "Add to Queue"; ObjectID = "1893"; */ -"1893.title" = "dodaj do kolejki"; - -/* Class = "NSMenuItem"; title = "Stop After Current"; ObjectID = "1895"; */ -"1895.title" = "przestań grać po tym utworze."; - -/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Cog"; ObjectID = "1903"; */ -"1903.ibShadowedIsNilPlaceholder" = "Cog"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNoSelectionPlaceholder = "Cog"; ObjectID = "1903"; */ -"1903.ibShadowedNoSelectionPlaceholder" = "Cog"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNotApplicablePlaceholder = "Cog"; ObjectID = "1903"; */ -"1903.ibShadowedNotApplicablePlaceholder" = "Cog"; - -/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Toggle Queued"; ObjectID = "2042"; */ -"2042.ibShadowedIsNilPlaceholder" = "Toggle Queued"; - -/* Class = "CocoaBindingsConnection"; ibShadowedMultipleValuesPlaceholder = "Toggle Queued"; ObjectID = "2042"; */ -"2042.ibShadowedMultipleValuesPlaceholder" = "Toggle Queued"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNoSelectionPlaceholder = "Toggle Queued"; ObjectID = "2042"; */ -"2042.ibShadowedNoSelectionPlaceholder" = "oggle Queued"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNotApplicablePlaceholder = "Toggle Queued"; ObjectID = "2042"; */ -"2042.ibShadowedNotApplicablePlaceholder" = "Toggle Queued"; - -/* Class = "NSMenuItem"; title = "Search for Artist"; ObjectID = "2045"; */ -"2045.title" = "Szukaj wykonawcy"; - -/* Class = "NSMenuItem"; title = "Search for Album"; ObjectID = "2046"; */ -"2046.title" = "Szukaj Albumu"; - -/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = ""; ObjectID = "2083"; */ -"2083.ibShadowedIsNilPlaceholder" = "nine gra"; - -/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Not Playing"; ObjectID = "2085"; */ -"2085.ibShadowedIsNilPlaceholder" = "nie gra"; - -/* Class = "NSMenuItem"; title = "Toggle File Tree Orientation"; ObjectID = "2160"; */ -"2160.title" = "Przełącz widok drzewa plików"; - -/* Class = "NSMenuItem"; title = "Information"; ObjectID = "2212"; */ -"2212.title" = "informacje"; - -/* Class = "NSWindow"; title = "Cog"; ObjectID = "2234"; */ -"2234.title" = "Cog"; - -/* Class = "NSToolbarItem"; label = "Playback Buttons"; ObjectID = "2272"; */ -"2272.label" = "Przyciski odtwarzania"; - -/* Class = "NSToolbarItem"; paletteLabel = "Playback Buttons"; ObjectID = "2272"; */ -"2272.paletteLabel" = "Przyciski odtwarzania"; - -/* Class = "NSToolbarItem"; label = "Position"; ObjectID = "2273"; */ -"2273.label" = "pozycja"; - -/* Class = "NSToolbarItem"; paletteLabel = "Position"; ObjectID = "2273"; */ -"2273.paletteLabel" = "Pozycja"; - -/* Class = "NSToolbarItem"; label = "Current Time"; ObjectID = "2274"; */ -"2274.label" = "Obecny czas"; - -/* Class = "NSToolbarItem"; paletteLabel = "Current Time"; ObjectID = "2274"; */ -"2274.paletteLabel" = "obecny czas odtwarzania"; - -/* Class = "NSToolbarItem"; label = "Volume"; ObjectID = "2275"; */ -"2275.label" = "głośność"; - -/* Class = "NSToolbarItem"; paletteLabel = "Volume"; ObjectID = "2275"; */ -"2275.paletteLabel" = "głośność"; - -/* Class = "NSToolbarItem"; label = "Shuffle"; ObjectID = "2278"; */ -"2278.label" = "losowo"; - -/* Class = "NSToolbarItem"; paletteLabel = "Shuffle"; ObjectID = "2278"; */ -"2278.paletteLabel" = "losowo"; - -/* Class = "NSToolbarItem"; label = "Repeat"; ObjectID = "2279"; */ -"2279.label" = "powtórz"; - -/* Class = "NSToolbarItem"; paletteLabel = "Repeat"; ObjectID = "2279"; */ -"2279.paletteLabel" = "powtórz"; - -/* Class = "NSTextFieldCell"; title = "0:00"; ObjectID = "2292"; */ -"2292.title" = "0:00"; - -/* Class = "NSSegmentedCell"; 2296.ibShadowedToolTips[0] = "Previous"; ObjectID = "2296"; */ -"2296.ibShadowedToolTips[0]" = "Poprzedni"; - -/* Class = "NSSegmentedCell"; 2296.ibShadowedToolTips[1] = "Play"; ObjectID = "2296"; */ -"2296.ibShadowedToolTips[1]" = "odtwarzaj"; - -/* Class = "NSSegmentedCell"; 2296.ibShadowedToolTips[2] = "Stop"; ObjectID = "2296"; */ -"2296.ibShadowedToolTips[2]" = "pauza"; - -/* Class = "NSSegmentedCell"; 2296.ibShadowedToolTips[3] = "Next"; ObjectID = "2296"; */ -"2296.ibShadowedToolTips[3]" = "następny"; - -/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Cog"; ObjectID = "2374"; */ -"2374.ibShadowedIsNilPlaceholder" = "Cog"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNoSelectionPlaceholder = "Cog"; ObjectID = "2374"; */ -"2374.ibShadowedNoSelectionPlaceholder" = "Cog"; - -/* Class = "CocoaBindingsConnection"; ibShadowedNotApplicablePlaceholder = "Cog"; ObjectID = "2374"; */ -"2374.ibShadowedNotApplicablePlaceholder" = "Cog"; - -/* Class = "NSMenuItem"; title = "Show File Tree"; ObjectID = "2417"; */ -"2417.title" = "Pokaż drzewo plików"; - -/* Class = "NSToolbarItem"; label = "Info Inspector"; ObjectID = "2429"; */ -"2429.label" = "inspektor informacji o pliku"; - -/* Class = "NSToolbarItem"; paletteLabel = "Info Inspector"; ObjectID = "2429"; */ -"2429.paletteLabel" = inspektor informacji o plikó""; - -/* Class = "NSMenuItem"; title = "Shuffle"; ObjectID = "2438"; */ -"2438.title" = "losowo"; - -/* Class = "NSMenu"; title = "Shuffle"; ObjectID = "2439"; */ -"2439.title" = "losowo"; - -/* Class = "NSMenuItem"; title = "Off"; ObjectID = "2440"; */ -"2440.title" = "wyłączony"; - -/* Class = "NSMenuItem"; title = "Albums"; ObjectID = "2442"; */ -"2442.title" = "Albumy"; - -/* Class = "NSMenuItem"; title = "All"; ObjectID = "2443"; */ -"2443.title" = "wszystkie"; - -/* Class = "NSMenuItem"; title = "Show Info Inspector"; ObjectID = "2453"; */ -"2453.title" = "pokaż inspektor informacji o pliku"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "2456"; */ -"2456.title" = "pełny ekran"; - -/* Class = "NSToolbarItem"; label = "Randomize"; ObjectID = "2466"; */ -"2466.label" = "losuj"; - -/* Class = "NSToolbarItem"; paletteLabel = "Randomize"; ObjectID = "2466"; */ -"2466.paletteLabel" = "losuj"; - -/* Class = "NSMenuItem"; title = "Toggle"; ObjectID = "2472"; */ -"2472.title" = "przełącz"; - -/* Class = "NSMenuItem"; title = "Toggle"; ObjectID = "2476"; */ -"2476.title" = "przełącz"; - -/* Class = "NSMenuItem"; title = "Stop After Current"; ObjectID = "2483"; */ -"2483.title" = "przestań grać, gdy skończy się utwór"; - -/* Class = "NSMenuItem"; title = "Next Album"; ObjectID = "2485"; */ -"2485.title" = "następny album"; - -/* Class = "NSMenuItem"; title = "Previous Album"; ObjectID = "2487"; */ -"2487.title" = "Poprzedni Album"; - -/* Class = "NSMenuItem"; title = "Toggle Mini Mode"; ObjectID = "2493"; */ -"2493.title" = "włącz tryb Mini"; - -/* Class = "NSToolbarItem"; label = "Normal Mode"; ObjectID = "2526"; */ -"2526.label" = "Tryb normalny"; - -/* Class = "NSToolbarItem"; paletteLabel = "Normal Mode"; ObjectID = "2526"; */ -"2526.paletteLabel" = "tryb normalny"; - -/* Class = "NSToolbarItem"; label = "Mini Mode"; ObjectID = "2532"; */ -"2532.label" = "Tryb mini"; - -/* Class = "NSToolbarItem"; paletteLabel = "Mini Mode"; ObjectID = "2532"; */ -"2532.paletteLabel" = "tryb mini"; - -/* Class = "NSMenuItem"; title = "Randomize"; ObjectID = "2545"; */ -"2545.title" = "losuj"; - -/* Class = "NSMenuItem"; title = "Remove Duplicate Items"; ObjectID = "2qB-Bq-t2u"; */ -"2qB-Bq-t2u.title" = "Usuń duplikaty"; - -/* Class = "NSMenuItem"; title = "Remove Dead Items"; ObjectID = "Ajn-k4-afd"; */ -"Ajn-k4-afd.title" = "usuń uszkodzone rzeczy"; - -/* Class = "NSMenuItem"; title = "Properties"; ObjectID = "Eds-my-DQr"; */ -"Eds-my-DQr.title" = "właściwości"; - -/* Class = "NSTableColumn"; title = "Album Artist"; ObjectID = "yGV-gP-Wl6"; */ -"yGV-gP-Wl6.title" = "Album artysta"; - -/* Class = "NSTableColumn"; title = "Codec"; ObjectID = "3A3-9o-Gh9"; */ -"3A3-9o-Gh9.title" = "kodek"; diff --git a/pl.lproj/OpenURLPanel.strings b/pl.lproj/OpenURLPanel.strings deleted file mode 100644 index 8518883ad..000000000 --- a/pl.lproj/OpenURLPanel.strings +++ /dev/null @@ -1,9 +0,0 @@ - -/* Class = "NSWindow"; title = "Open URL"; ObjectID = "5"; */ -"5.title" = "Open URL"; - -/* Class = "NSButtonCell"; title = "OK"; ObjectID = "23"; */ -"23.title" = "OK"; - -/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "24"; */ -"24.title" = "Cancel"; diff --git a/pl.lproj/SpectrumWindow.strings b/pl.lproj/SpectrumWindow.strings deleted file mode 100644 index 72ed648cb..000000000 --- a/pl.lproj/SpectrumWindow.strings +++ /dev/null @@ -1,3 +0,0 @@ - -/* Class = "NSWindow"; title = "Spectrum"; ObjectID = "F0z-JX-Cv5"; */ -"F0z-JX-Cv5.title" = "Spectrum"; diff --git a/pl.lproj/SpotlightPanel.strings b/pl.lproj/SpotlightPanel.strings deleted file mode 100644 index bb7585c0a..000000000 --- a/pl.lproj/SpotlightPanel.strings +++ /dev/null @@ -1,57 +0,0 @@ - -/* Class = "NSWindow"; title = "Spotlight"; ObjectID = "1"; */ -"1.title" = "Spotlight"; - -/* Class = "NSButtonCell"; title = "Add to Playlist"; ObjectID = "6"; */ -"6.title" = "dodaj do Playlisty"; - -/* Class = "NSTableColumn"; headerCell.title = "Track"; ObjectID = "29"; */ -"29.headerCell.title" = "utwór"; - -/* Class = "NSTableColumn"; headerCell.title = "Genre"; ObjectID = "30"; */ -"30.headerCell.title" = "gatunek"; - -/* Class = "NSTableColumn"; headerCell.title = "Year"; ObjectID = "31"; */ -"31.headerCell.title" = "rok"; - -/* Class = "NSTableColumn"; headerCell.title = length""; ObjectID = "32"; */ -"32.headerCell.title" = "długość"; - -/* Class = "NSTableColumn"; headerCell.title = "Album"; ObjectID = "33"; */ -"33.headerCell.title" = "Album"; - -/* Class = "NSTableColumn"; headerCell.title = "Artist"; ObjectID = "34"; */ -"34.headerCell.title" = "wykonawca"; - -/* Class = "NSTableColumn"; headerCell.title = "Title"; ObjectID = "36"; */ -"36.headerCell.title" = "tytuł"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "37"; */ -"37.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "39"; */ -"39.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "40"; */ -"40.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "41"; */ -"41.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "42"; */ -"42.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ -"43.title" = "Text Cell";tekstowe - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "44"; */ -"44.title" = "Text Cell"; - -/* Class = "NSTextFieldCell"; title = "Search Location:"; ObjectID = "80"; */ -"80.title" = "lokalizacja wyszukiwania:"; - -/* Class = "NSMenu"; title = "ContextualMenu"; ObjectID = "171"; */ -"171.title" = " menu kontekstowe"; - -/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "172"; */ -"172.title" = "pokaż w finderze";