Browse Source

coreaudio-encoder: Make encoder available on windows

Palana 10 years ago
parent
commit
897fbcfe1c

+ 4 - 0
plugins/CMakeLists.txt

@@ -1,5 +1,9 @@
 
 if(WIN32)
+	option(BUILD_CA_ENCODER "Build CoreAudio encoder module" ON)
+	if (BUILD_CA_ENCODER)
+		add_subdirectory(coreaudio-encoder)
+	endif()
 	add_subdirectory(win-wasapi)
 	add_subdirectory(win-dshow)
 	add_subdirectory(win-capture)

+ 19 - 11
plugins/coreaudio-encoder/CMakeLists.txt

@@ -1,24 +1,32 @@
 project(coreaudio-encoder)
 
-find_library(COREFOUNDATION CoreFoundation)
-find_library(COREAUDIO CoreAudio)
-find_library(AUDIOTOOLBOX AudioToolbox)
-
-include_directories(${COREFOUNDATION}
-	${COREAUDIO}
-	${AUDIOTOOLBOX})
-
 set(coreaudio-encoder_SOURCES
 	encoder.c)
 
+if (WIN32)
+	set(coreaudio-encoder_HEADERS windows-imports.h)
+	set(coreaudio-encoder_LIBS )
+else()
+	find_library(COREFOUNDATION CoreFoundation)
+	find_library(COREAUDIO CoreAudio)
+	find_library(AUDIOTOOLBOX AudioToolbox)
+	set(coreaudio-encoder_HEADERS )
+	set(coreaudio-encoder_LIBS
+		${COREFOUNDATION}
+		${COREAUDIO}
+		${AUDIOTOOLBOX})
+
+	include_directories(${COREFOUNDATION}
+		${COREAUDIO}
+		${AUDIOTOOLBOX})
+endif()
+
 add_library(coreaudio-encoder MODULE
 	${coreaudio-encoder_SOURCES}
 	${coreaudio-encoder_HEADERS})
 
 target_link_libraries(coreaudio-encoder
 	libobs
-	${COREFOUNDATION}
-	${COREAUDIO}
-	${AUDIOTOOLBOX})
+	${coreaudio-encoder_LIBS})
 
 install_obs_plugin_with_data(coreaudio-encoder data)

+ 26 - 0
plugins/coreaudio-encoder/encoder.c

@@ -2,7 +2,9 @@
 #include <util/dstr.h>
 #include <obs-module.h>
 
+#ifndef _WIN32
 #include <AudioToolbox/AudioToolbox.h>
+#endif
 
 #define CA_LOG(level, format, ...) \
 	blog(level, "[CoreAudio encoder]: " format, ##__VA_ARGS__)
@@ -14,6 +16,10 @@
 	CA_LOG_ENCODER(ca->format_name, ca->encoder, level, format, \
 			##__VA_ARGS__)
 
+#ifdef _WIN32
+#include "windows-imports.h"
+#endif
+
 struct ca_encoder {
 	obs_encoder_t     *encoder;
 	const char        *format_name;
@@ -80,6 +86,7 @@ static const char *code_to_str(OSStatus code)
 
 static void log_osstatus(ca_encoder *ca, const char *context, OSStatus code)
 {
+#ifndef _WIN32
 	CFErrorRef err  = CFErrorCreate(kCFAllocatorDefault,
 			kCFErrorDomainOSStatus, code, NULL);
 	CFStringRef str = CFErrorCopyDescription(err);
@@ -95,6 +102,7 @@ static void log_osstatus(ca_encoder *ca, const char *context, OSStatus code)
 		else
 			CA_LOG(LOG_ERROR, "Error in %s: %s", context, c_str);
 	} else {
+#endif
 		const char *code_str = code_to_str(code);
 		if (ca)
 			CA_BLOG(LOG_ERROR, "Error in %s: %s%s%d%s", context,
@@ -108,11 +116,13 @@ static void log_osstatus(ca_encoder *ca, const char *context, OSStatus code)
 					code_str ? " (" : "",
 					(int)code,
 					code_str ? ")" : "");
+#ifndef _WIN32
 	}
 	free(c_str);
 
 	CFRelease(str);
 	CFRelease(err);
+#endif
 }
 
 static void aac_destroy(void *data)
@@ -755,6 +765,22 @@ OBS_MODULE_USE_DEFAULT_LOCALE("coreaudio-encoder", "en-US")
 
 bool obs_module_load(void)
 {
+#ifdef _WIN32
+	if (!load_core_audio()) {
+		CA_LOG(LOG_WARNING, "Couldn't load CoreAudio AAC encoder");
+		return true;
+	}
+
+	CA_LOG(LOG_INFO, "Adding CoreAudio AAC encoder");
+#endif
+
 	obs_register_encoder(&aac_info);
 	return true;
 }
+
+#ifdef _WIN32
+void obs_module_unload(void)
+{
+	unload_core_audio();
+}
+#endif

+ 459 - 0
plugins/coreaudio-encoder/windows-imports.h

@@ -0,0 +1,459 @@
+#define NO_MIN_MAX 1
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <ShlObj.h>
+
+#include <util/dstr.h>
+
+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 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_HE_V2          = 'aacp',
+	kAudioFormatMPEG4AAC_Spatial        = 'aacs',
+	kAudioFormatAMR                     = 'samr'
+};
+
+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
+);
+
+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 HMODULE audio_toolbox = NULL;
+
+static void release_lib(void)
+{
+#define RELEASE_LIB(x) if (x) { \
+		FreeLibrary(x); \
+		x = NULL; \
+	}
+
+	RELEASE_LIB(audio_toolbox);
+#undef RELEASE_LIB
+}
+
+static bool load_lib(void)
+{
+	PWSTR common_path;
+	if (SHGetKnownFolderPath(&FOLDERID_ProgramFilesCommon, 0, NULL,
+				&common_path) != S_OK) {
+		CA_LOG(LOG_WARNING, "Could not retrieve common files path");
+		return false;
+	}
+
+	struct dstr path = { 0 };
+	dstr_printf(&path, "%S\\Apple\\Apple Application Support", common_path);
+	CoTaskMemFree(common_path);
+
+	wchar_t *w_path = dstr_to_wcs(&path);
+	dstr_free(&path);
+
+	SetDllDirectory(w_path);
+	bfree(w_path);
+
+#define LOAD_LIB(x, n) x = LoadLibrary(TEXT(n)); \
+	if (!x) \
+		CA_LOG(LOG_WARNING, "Failed loading library '" n "'");
+
+	LOAD_LIB(audio_toolbox, "CoreAudioToolbox.dll");
+#undef LOAD_LIB
+
+	SetDllDirectory(NULL);
+
+	if (audio_toolbox)
+		return true;
+
+	release_lib();
+	return false;
+}
+
+static void unload_core_audio(void)
+{
+	AudioConverterNew = NULL;
+	AudioConverterDispose = NULL;
+	AudioConverterReset = NULL;
+	AudioConverterGetProperty = NULL;
+	AudioConverterGetPropertyInfo = NULL;
+	AudioConverterSetProperty = NULL;
+	AudioConverterFillComplexBuffer = NULL;
+	AudioFormatGetProperty = NULL;
+
+	release_lib();
+}
+
+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);
+#undef LOAD_SYM
+
+	return true;
+
+unload_everything:
+	unload_core_audio();
+
+	return false;
+}