#define NO_MIN_MAX 1 #define WIN32_LEAN_AND_MEAN #include #include #include typedef unsigned long UInt32; typedef signed long SInt32; typedef signed long long SInt64; typedef double Float64; typedef SInt32 OSStatus; typedef unsigned char Boolean; typedef UInt32 AudioFormatPropertyID; enum { kVariableLengthArray = 1, }; struct OpaqueAudioConverter; typedef struct OpaqueAudioConverter *AudioConverterRef; typedef UInt32 AudioConverterPropertyID; struct AudioValueRange { Float64 mMinimum; Float64 mMaximum; }; typedef struct AudioValueRange AudioValueRange; struct AudioBuffer { UInt32 mNumberChannels; UInt32 mDataByteSize; void *mData; }; typedef struct AudioBuffer AudioBuffer; struct AudioBufferList { UInt32 mNumberBuffers; AudioBuffer mBuffers[kVariableLengthArray]; }; typedef struct AudioBufferList AudioBufferList; struct AudioStreamBasicDescription { Float64 mSampleRate; UInt32 mFormatID; UInt32 mFormatFlags; UInt32 mBytesPerPacket; UInt32 mFramesPerPacket; UInt32 mBytesPerFrame; UInt32 mChannelsPerFrame; UInt32 mBitsPerChannel; UInt32 mReserved; }; typedef struct AudioStreamBasicDescription AudioStreamBasicDescription; struct AudioStreamPacketDescription { SInt64 mStartOffset; UInt32 mVariableFramesInPacket; UInt32 mDataByteSize; }; typedef struct AudioStreamPacketDescription AudioStreamPacketDescription; typedef UInt32 AudioChannelLabel; typedef UInt32 AudioChannelLayoutTag; struct AudioChannelDescription { AudioChannelLabel mChannelLabel; UInt32 mChannelFlags; float mCoordinates[3]; }; typedef struct AudioChannelDescription AudioChannelDescription; struct AudioChannelLayout { AudioChannelLayoutTag mChannelLayoutTag; UInt32 mChannelBitmap; UInt32 mNumberChannelDescriptions; AudioChannelDescription mChannelDescriptions[kVariableLengthArray]; }; typedef struct AudioChannelLayout AudioChannelLayout; typedef OSStatus (*AudioConverterComplexInputDataProc)( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData); enum { kAudioCodecPropertyNameCFString = 'lnam', kAudioCodecPropertyManufacturerCFString = 'lmak', kAudioCodecPropertyFormatCFString = 'lfor', //kAudioCodecPropertyHasVariablePacketByteSizes = 'vpk?', kAudioCodecPropertySupportedInputFormats = 'ifm#', kAudioCodecPropertySupportedOutputFormats = 'ofm#', kAudioCodecPropertyAvailableInputSampleRates = 'aisr', kAudioCodecPropertyAvailableOutputSampleRates = 'aosr', kAudioCodecPropertyAvailableBitRateRange = 'abrt', kAudioCodecPropertyMinimumNumberInputPackets = 'mnip', kAudioCodecPropertyMinimumNumberOutputPackets = 'mnop', kAudioCodecPropertyAvailableNumberChannels = 'cmnc', kAudioCodecPropertyDoesSampleRateConversion = 'lmrc', kAudioCodecPropertyAvailableInputChannelLayoutTags = 'aicl', kAudioCodecPropertyAvailableOutputChannelLayoutTags = 'aocl', kAudioCodecPropertyInputFormatsForOutputFormat = 'if4o', kAudioCodecPropertyOutputFormatsForInputFormat = 'of4i', kAudioCodecPropertyFormatInfo = 'acfi', }; enum { kAudioCodecPropertyInputBufferSize = 'tbuf', kAudioCodecPropertyPacketFrameSize = 'pakf', kAudioCodecPropertyMaximumPacketByteSize = 'pakb', kAudioCodecPropertyCurrentInputFormat = 'ifmt', kAudioCodecPropertyCurrentOutputFormat = 'ofmt', kAudioCodecPropertyMagicCookie = 'kuki', kAudioCodecPropertyUsedInputBufferSize = 'ubuf', kAudioCodecPropertyIsInitialized = 'init', kAudioCodecPropertyCurrentTargetBitRate = 'brat', kAudioCodecPropertyCurrentInputSampleRate = 'cisr', kAudioCodecPropertyCurrentOutputSampleRate = 'cosr', kAudioCodecPropertyQualitySetting = 'srcq', kAudioCodecPropertyApplicableBitRateRange = 'brta', kAudioCodecPropertyApplicableInputSampleRates = 'isra', kAudioCodecPropertyApplicableOutputSampleRates = 'osra', kAudioCodecPropertyPaddedZeros = 'pad0', kAudioCodecPropertyPrimeMethod = 'prmm', kAudioCodecPropertyPrimeInfo = 'prim', kAudioCodecPropertyCurrentInputChannelLayout = 'icl ', kAudioCodecPropertyCurrentOutputChannelLayout = 'ocl ', kAudioCodecPropertySettings = 'acs ', kAudioCodecPropertyFormatList = 'acfl', kAudioCodecPropertyBitRateControlMode = 'acbf', kAudioCodecPropertySoundQualityForVBR = 'vbrq', kAudioCodecPropertyMinimumDelayMode = 'mdel' }; enum { kAudioCodecBitRateControlMode_Constant = 0, kAudioCodecBitRateControlMode_LongTermAverage = 1, kAudioCodecBitRateControlMode_VariableConstrained = 2, kAudioCodecBitRateControlMode_Variable = 3, }; enum { kAudioFormatLinearPCM = 'lpcm', kAudioFormatAC3 = 'ac-3', kAudioFormat60958AC3 = 'cac3', kAudioFormatAppleIMA4 = 'ima4', kAudioFormatMPEG4AAC = 'aac ', kAudioFormatMPEG4CELP = 'celp', kAudioFormatMPEG4HVXC = 'hvxc', kAudioFormatMPEG4TwinVQ = 'twvq', kAudioFormatMACE3 = 'MAC3', kAudioFormatMACE6 = 'MAC6', kAudioFormatULaw = 'ulaw', kAudioFormatALaw = 'alaw', kAudioFormatQDesign = 'QDMC', kAudioFormatQDesign2 = 'QDM2', kAudioFormatQUALCOMM = 'Qclp', kAudioFormatMPEGLayer1 = '.mp1', kAudioFormatMPEGLayer2 = '.mp2', kAudioFormatMPEGLayer3 = '.mp3', kAudioFormatTimeCode = 'time', kAudioFormatMIDIStream = 'midi', kAudioFormatParameterValueStream = 'apvs', kAudioFormatAppleLossless = 'alac', kAudioFormatMPEG4AAC_HE = 'aach', kAudioFormatMPEG4AAC_LD = 'aacl', kAudioFormatMPEG4AAC_ELD = 'aace', kAudioFormatMPEG4AAC_ELD_SBR = 'aacf', kAudioFormatMPEG4AAC_ELD_V2 = 'aacg', kAudioFormatMPEG4AAC_HE_V2 = 'aacp', kAudioFormatMPEG4AAC_Spatial = 'aacs', kAudioFormatAMR = 'samr', kAudioFormatAudible = 'AUDB', kAudioFormatiLBC = 'ilbc', kAudioFormatDVIIntelIMA = 0x6D730011, kAudioFormatMicrosoftGSM = 0x6D730031, kAudioFormatAES3 = 'aes3', }; enum { kAudioFormatFlagIsFloat = (1L << 0), kAudioFormatFlagIsBigEndian = (1L << 1), kAudioFormatFlagIsSignedInteger = (1L << 2), kAudioFormatFlagIsPacked = (1L << 3), kAudioFormatFlagIsAlignedHigh = (1L << 4), kAudioFormatFlagIsNonInterleaved = (1L << 5), kAudioFormatFlagIsNonMixable = (1L << 6), kAudioFormatFlagsAreAllClear = (1L << 31), kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat, kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian, kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger, kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked, kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh, kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved, kLinearPCMFormatFlagIsNonMixable = kAudioFormatFlagIsNonMixable, kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear, kAppleLosslessFormatFlag_16BitSourceData = 1, kAppleLosslessFormatFlag_20BitSourceData = 2, kAppleLosslessFormatFlag_24BitSourceData = 3, kAppleLosslessFormatFlag_32BitSourceData = 4, }; enum { kAudioFormatFlagsNativeEndian = 0 }; enum { // AudioStreamBasicDescription structure properties kAudioFormatProperty_FormatInfo = 'fmti', kAudioFormatProperty_FormatName = 'fnam', kAudioFormatProperty_EncodeFormatIDs = 'acof', kAudioFormatProperty_DecodeFormatIDs = 'acif', kAudioFormatProperty_FormatList = 'flst', kAudioFormatProperty_ASBDFromESDS = 'essd', kAudioFormatProperty_ChannelLayoutFromESDS = 'escl', kAudioFormatProperty_OutputFormatList = 'ofls', kAudioFormatProperty_Encoders = 'aven', kAudioFormatProperty_Decoders = 'avde', kAudioFormatProperty_FormatIsVBR = 'fvbr', kAudioFormatProperty_FormatIsExternallyFramed = 'fexf', kAudioFormatProperty_AvailableEncodeBitRates = 'aebr', kAudioFormatProperty_AvailableEncodeSampleRates = 'aesr', kAudioFormatProperty_AvailableEncodeChannelLayoutTags = 'aecl', kAudioFormatProperty_AvailableEncodeNumberChannels = 'avnc', kAudioFormatProperty_ASBDFromMPEGPacket = 'admp', // // AudioChannelLayout structure properties kAudioFormatProperty_BitmapForLayoutTag = 'bmtg', kAudioFormatProperty_MatrixMixMap = 'mmap', kAudioFormatProperty_ChannelMap = 'chmp', kAudioFormatProperty_NumberOfChannelsForLayout = 'nchm', kAudioFormatProperty_ValidateChannelLayout = 'vacl', kAudioFormatProperty_ChannelLayoutForTag = 'cmpl', kAudioFormatProperty_TagForChannelLayout = 'cmpt', kAudioFormatProperty_ChannelLayoutName = 'lonm', kAudioFormatProperty_ChannelLayoutSimpleName = 'lsnm', kAudioFormatProperty_ChannelLayoutForBitmap = 'cmpb', kAudioFormatProperty_ChannelName = 'cnam', kAudioFormatProperty_ChannelShortName = 'csnm', kAudioFormatProperty_TagsForNumberOfChannels = 'tagc', kAudioFormatProperty_PanningMatrix = 'panm', kAudioFormatProperty_BalanceFade = 'balf', // // ID3 tag (MP3 metadata) properties kAudioFormatProperty_ID3TagSize = 'id3s', kAudioFormatProperty_ID3TagToDictionary = 'id3d', }; enum { kAudioConverterPropertyMinimumInputBufferSize = 'mibs', kAudioConverterPropertyMinimumOutputBufferSize = 'mobs', kAudioConverterPropertyMaximumInputBufferSize = 'xibs', kAudioConverterPropertyMaximumInputPacketSize = 'xips', kAudioConverterPropertyMaximumOutputPacketSize = 'xops', kAudioConverterPropertyCalculateInputBufferSize = 'cibs', kAudioConverterPropertyCalculateOutputBufferSize = 'cobs', kAudioConverterPropertyInputCodecParameters = 'icdp', kAudioConverterPropertyOutputCodecParameters = 'ocdp', kAudioConverterSampleRateConverterAlgorithm = 'srci', kAudioConverterSampleRateConverterComplexity = 'srca', kAudioConverterSampleRateConverterQuality = 'srcq', kAudioConverterSampleRateConverterInitialPhase = 'srcp', kAudioConverterCodecQuality = 'cdqu', kAudioConverterPrimeMethod = 'prmm', kAudioConverterPrimeInfo = 'prim', kAudioConverterChannelMap = 'chmp', kAudioConverterDecompressionMagicCookie = 'dmgc', kAudioConverterCompressionMagicCookie = 'cmgc', kAudioConverterEncodeBitRate = 'brat', kAudioConverterEncodeAdjustableSampleRate = 'ajsr', kAudioConverterInputChannelLayout = 'icl ', kAudioConverterOutputChannelLayout = 'ocl ', kAudioConverterApplicableEncodeBitRates = 'aebr', kAudioConverterAvailableEncodeBitRates = 'vebr', kAudioConverterApplicableEncodeSampleRates = 'aesr', kAudioConverterAvailableEncodeSampleRates = 'vesr', kAudioConverterAvailableEncodeChannelLayoutTags = 'aecl', kAudioConverterCurrentOutputStreamDescription = 'acod', kAudioConverterCurrentInputStreamDescription = 'acid', kAudioConverterPropertySettings = 'acps', kAudioConverterPropertyBitDepthHint = 'acbd', kAudioConverterPropertyFormatList = 'flst', }; enum { kAudioConverterQuality_Max = 0x7F, kAudioConverterQuality_High = 0x60, kAudioConverterQuality_Medium = 0x40, kAudioConverterQuality_Low = 0x20, kAudioConverterQuality_Min = 0, }; enum { kAudio_UnimplementedError = -4, kAudio_FileNotFoundError = -43, kAudio_FilePermissionError = -54, kAudio_TooManyFilesOpenError = -42, kAudio_BadFilePathError = '!pth', // 0x21707468, 561017960 kAudio_ParamError = -50, kAudio_MemFullError = -108, kAudioConverterErr_FormatNotSupported = 'fmt?', kAudioConverterErr_OperationNotSupported = 0x6F703F3F, // 'op??', integer used because of trigraph kAudioConverterErr_PropertyNotSupported = 'prop', kAudioConverterErr_InvalidInputSize = 'insz', kAudioConverterErr_InvalidOutputSize = 'otsz', // e.g. byte size is not a multiple of the frame size kAudioConverterErr_UnspecifiedError = 'what', kAudioConverterErr_BadPropertySizeError = '!siz', kAudioConverterErr_RequiresPacketDescriptionsError = '!pkd', kAudioConverterErr_InputSampleRateOutOfRange = '!isr', kAudioConverterErr_OutputSampleRateOutOfRange = '!osr', }; typedef OSStatus (*AudioConverterNew_t)( const AudioStreamBasicDescription *inSourceFormat, const AudioStreamBasicDescription *inDestinationFormat, AudioConverterRef *outAudioConverter); typedef OSStatus (*AudioConverterDispose_t)(AudioConverterRef inAudioConverter); typedef OSStatus (*AudioConverterReset_t)(AudioConverterRef inAudioConverter); typedef OSStatus (*AudioConverterGetProperty_t)( AudioConverterRef inAudioConverter, AudioConverterPropertyID inPropertyID, UInt32 *ioPropertyDataSize, void *outPropertyData); typedef OSStatus (*AudioConverterGetPropertyInfo_t)( AudioConverterRef inAudioConverter, AudioConverterPropertyID inPropertyID, UInt32 *outSize, Boolean *outWritable); typedef OSStatus (*AudioConverterSetProperty_t)( AudioConverterRef inAudioConverter, AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData); typedef OSStatus (*AudioConverterFillComplexBuffer_t)( AudioConverterRef inAudioConverter, AudioConverterComplexInputDataProc inInputDataProc, void *inInputDataProcUserData, UInt32 *ioOutputDataPacketSize, AudioBufferList *outOutputData, AudioStreamPacketDescription *outPacketDescription); typedef OSStatus (*AudioFormatGetProperty_t)(AudioFormatPropertyID inPropertyID, UInt32 inSpecifierSize, const void *inSpecifier, UInt32 *ioPropertyDataSize, void *outPropertyData); typedef OSStatus (*AudioFormatGetPropertyInfo_t)( AudioFormatPropertyID inPropertyID, UInt32 inSpecifierSize, const void *inSpecifier, UInt32 *outPropertyDataSize); static AudioConverterNew_t AudioConverterNew = NULL; static AudioConverterDispose_t AudioConverterDispose = NULL; static AudioConverterReset_t AudioConverterReset = NULL; static AudioConverterGetProperty_t AudioConverterGetProperty = NULL; static AudioConverterGetPropertyInfo_t AudioConverterGetPropertyInfo = NULL; static AudioConverterSetProperty_t AudioConverterSetProperty = NULL; static AudioConverterFillComplexBuffer_t AudioConverterFillComplexBuffer = NULL; static AudioFormatGetProperty_t AudioFormatGetProperty = NULL; static AudioFormatGetPropertyInfo_t AudioFormatGetPropertyInfo = NULL; static HMODULE audio_toolbox = NULL; static void release_lib(void) { if (audio_toolbox) { FreeLibrary(audio_toolbox); audio_toolbox = NULL; } } static bool load_from_shell_path(REFKNOWNFOLDERID rfid, const wchar_t *subpath) { wchar_t *sh_path; if (SHGetKnownFolderPath(rfid, 0, NULL, &sh_path) != S_OK) { CA_LOG(LOG_WARNING, "Could not retrieve shell path"); return false; } wchar_t path[MAX_PATH]; _snwprintf(path, MAX_PATH, L"%s\\%s", sh_path, subpath); CoTaskMemFree(sh_path); SetDllDirectory(path); audio_toolbox = LoadLibraryW(L"CoreAudioToolbox.dll"); SetDllDirectory(nullptr); return !!audio_toolbox; } static bool load_lib(void) { /* -------------------------------------------- */ /* attempt to load from path */ audio_toolbox = LoadLibraryW(L"CoreAudioToolbox.dll"); if (!!audio_toolbox) return true; /* -------------------------------------------- */ /* attempt to load from known install locations */ struct path_list_t { REFKNOWNFOLDERID rfid; const wchar_t *subpath; }; path_list_t path_list[] = { {FOLDERID_ProgramFilesCommon, L"Apple\\Apple Application Support"}, {FOLDERID_ProgramFiles, L"iTunes"}, }; for (auto &val : path_list) { if (load_from_shell_path(val.rfid, val.subpath)) { return true; } } return false; } static void unload_core_audio(void) { AudioConverterNew = NULL; AudioConverterDispose = NULL; AudioConverterReset = NULL; AudioConverterGetProperty = NULL; AudioConverterGetPropertyInfo = NULL; AudioConverterSetProperty = NULL; AudioConverterFillComplexBuffer = NULL; AudioFormatGetProperty = NULL; AudioFormatGetPropertyInfo = NULL; release_lib(); } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4706) #endif static bool load_core_audio(void) { if (!load_lib()) return false; #define LOAD_SYM_FROM_LIB(sym, lib, dll) \ if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \ DWORD err = GetLastError(); \ CA_LOG(LOG_ERROR, \ "Couldn't load " #sym " from " dll ": %lu (0x%lx)", \ err, err); \ goto unload_everything; \ } #define LOAD_SYM(sym) \ LOAD_SYM_FROM_LIB(sym, audio_toolbox, "CoreAudioToolbox.dll") LOAD_SYM(AudioConverterNew); LOAD_SYM(AudioConverterDispose); LOAD_SYM(AudioConverterReset); LOAD_SYM(AudioConverterGetProperty); LOAD_SYM(AudioConverterGetPropertyInfo); LOAD_SYM(AudioConverterSetProperty); LOAD_SYM(AudioConverterFillComplexBuffer); LOAD_SYM(AudioFormatGetProperty); LOAD_SYM(AudioFormatGetPropertyInfo); #undef LOAD_SYM return true; unload_everything: unload_core_audio(); return false; } #ifdef _MSC_VER #pragma warning(pop) #endif