1 #define NO_MIN_MAX 1
2 #define WIN32_LEAN_AND_MEAN
3 #include <windows.h>
4 #include <ShlObj.h>
5 
6 #include <util/dstr.h>
7 
8 typedef unsigned long UInt32;
9 typedef signed long SInt32;
10 typedef signed long long SInt64;
11 typedef double Float64;
12 
13 typedef SInt32 OSStatus;
14 typedef unsigned char Boolean;
15 
16 typedef UInt32 AudioFormatPropertyID;
17 
18 enum { kVariableLengthArray = 1,
19 };
20 
21 struct OpaqueAudioConverter;
22 typedef struct OpaqueAudioConverter *AudioConverterRef;
23 typedef UInt32 AudioConverterPropertyID;
24 
25 struct AudioValueRange {
26 	Float64 mMinimum;
27 	Float64 mMaximum;
28 };
29 typedef struct AudioValueRange AudioValueRange;
30 
31 struct AudioBuffer {
32 	UInt32 mNumberChannels;
33 	UInt32 mDataByteSize;
34 	void *mData;
35 };
36 typedef struct AudioBuffer AudioBuffer;
37 
38 struct AudioBufferList {
39 	UInt32 mNumberBuffers;
40 	AudioBuffer mBuffers[kVariableLengthArray];
41 };
42 typedef struct AudioBufferList AudioBufferList;
43 
44 struct AudioStreamBasicDescription {
45 	Float64 mSampleRate;
46 	UInt32 mFormatID;
47 	UInt32 mFormatFlags;
48 	UInt32 mBytesPerPacket;
49 	UInt32 mFramesPerPacket;
50 	UInt32 mBytesPerFrame;
51 	UInt32 mChannelsPerFrame;
52 	UInt32 mBitsPerChannel;
53 	UInt32 mReserved;
54 };
55 typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
56 
57 struct AudioStreamPacketDescription {
58 	SInt64 mStartOffset;
59 	UInt32 mVariableFramesInPacket;
60 	UInt32 mDataByteSize;
61 };
62 typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;
63 
64 typedef UInt32 AudioChannelLabel;
65 typedef UInt32 AudioChannelLayoutTag;
66 
67 struct AudioChannelDescription {
68 	AudioChannelLabel mChannelLabel;
69 	UInt32 mChannelFlags;
70 	float mCoordinates[3];
71 };
72 typedef struct AudioChannelDescription AudioChannelDescription;
73 
74 struct AudioChannelLayout {
75 	AudioChannelLayoutTag mChannelLayoutTag;
76 	UInt32 mChannelBitmap;
77 	UInt32 mNumberChannelDescriptions;
78 	AudioChannelDescription mChannelDescriptions[kVariableLengthArray];
79 };
80 typedef struct AudioChannelLayout AudioChannelLayout;
81 
82 typedef OSStatus (*AudioConverterComplexInputDataProc)(
83 	AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
84 	AudioBufferList *ioData,
85 	AudioStreamPacketDescription **outDataPacketDescription,
86 	void *inUserData);
87 
88 enum { kAudioCodecPropertyNameCFString = 'lnam',
89        kAudioCodecPropertyManufacturerCFString = 'lmak',
90        kAudioCodecPropertyFormatCFString = 'lfor',
91        //kAudioCodecPropertyHasVariablePacketByteSizes          = 'vpk?',
92        kAudioCodecPropertySupportedInputFormats = 'ifm#',
93        kAudioCodecPropertySupportedOutputFormats = 'ofm#',
94        kAudioCodecPropertyAvailableInputSampleRates = 'aisr',
95        kAudioCodecPropertyAvailableOutputSampleRates = 'aosr',
96        kAudioCodecPropertyAvailableBitRateRange = 'abrt',
97        kAudioCodecPropertyMinimumNumberInputPackets = 'mnip',
98        kAudioCodecPropertyMinimumNumberOutputPackets = 'mnop',
99        kAudioCodecPropertyAvailableNumberChannels = 'cmnc',
100        kAudioCodecPropertyDoesSampleRateConversion = 'lmrc',
101        kAudioCodecPropertyAvailableInputChannelLayoutTags = 'aicl',
102        kAudioCodecPropertyAvailableOutputChannelLayoutTags = 'aocl',
103        kAudioCodecPropertyInputFormatsForOutputFormat = 'if4o',
104        kAudioCodecPropertyOutputFormatsForInputFormat = 'of4i',
105        kAudioCodecPropertyFormatInfo = 'acfi',
106 };
107 
108 enum { kAudioCodecPropertyInputBufferSize = 'tbuf',
109        kAudioCodecPropertyPacketFrameSize = 'pakf',
110        kAudioCodecPropertyMaximumPacketByteSize = 'pakb',
111        kAudioCodecPropertyCurrentInputFormat = 'ifmt',
112        kAudioCodecPropertyCurrentOutputFormat = 'ofmt',
113        kAudioCodecPropertyMagicCookie = 'kuki',
114        kAudioCodecPropertyUsedInputBufferSize = 'ubuf',
115        kAudioCodecPropertyIsInitialized = 'init',
116        kAudioCodecPropertyCurrentTargetBitRate = 'brat',
117        kAudioCodecPropertyCurrentInputSampleRate = 'cisr',
118        kAudioCodecPropertyCurrentOutputSampleRate = 'cosr',
119        kAudioCodecPropertyQualitySetting = 'srcq',
120        kAudioCodecPropertyApplicableBitRateRange = 'brta',
121        kAudioCodecPropertyApplicableInputSampleRates = 'isra',
122        kAudioCodecPropertyApplicableOutputSampleRates = 'osra',
123        kAudioCodecPropertyPaddedZeros = 'pad0',
124        kAudioCodecPropertyPrimeMethod = 'prmm',
125        kAudioCodecPropertyPrimeInfo = 'prim',
126        kAudioCodecPropertyCurrentInputChannelLayout = 'icl ',
127        kAudioCodecPropertyCurrentOutputChannelLayout = 'ocl ',
128        kAudioCodecPropertySettings = 'acs ',
129        kAudioCodecPropertyFormatList = 'acfl',
130        kAudioCodecPropertyBitRateControlMode = 'acbf',
131        kAudioCodecPropertySoundQualityForVBR = 'vbrq',
132        kAudioCodecPropertyMinimumDelayMode = 'mdel' };
133 
134 enum { kAudioCodecBitRateControlMode_Constant = 0,
135        kAudioCodecBitRateControlMode_LongTermAverage = 1,
136        kAudioCodecBitRateControlMode_VariableConstrained = 2,
137        kAudioCodecBitRateControlMode_Variable = 3,
138 };
139 
140 enum { kAudioFormatLinearPCM = 'lpcm',
141        kAudioFormatAC3 = 'ac-3',
142        kAudioFormat60958AC3 = 'cac3',
143        kAudioFormatAppleIMA4 = 'ima4',
144        kAudioFormatMPEG4AAC = 'aac ',
145        kAudioFormatMPEG4CELP = 'celp',
146        kAudioFormatMPEG4HVXC = 'hvxc',
147        kAudioFormatMPEG4TwinVQ = 'twvq',
148        kAudioFormatMACE3 = 'MAC3',
149        kAudioFormatMACE6 = 'MAC6',
150        kAudioFormatULaw = 'ulaw',
151        kAudioFormatALaw = 'alaw',
152        kAudioFormatQDesign = 'QDMC',
153        kAudioFormatQDesign2 = 'QDM2',
154        kAudioFormatQUALCOMM = 'Qclp',
155        kAudioFormatMPEGLayer1 = '.mp1',
156        kAudioFormatMPEGLayer2 = '.mp2',
157        kAudioFormatMPEGLayer3 = '.mp3',
158        kAudioFormatTimeCode = 'time',
159        kAudioFormatMIDIStream = 'midi',
160        kAudioFormatParameterValueStream = 'apvs',
161        kAudioFormatAppleLossless = 'alac',
162        kAudioFormatMPEG4AAC_HE = 'aach',
163        kAudioFormatMPEG4AAC_LD = 'aacl',
164        kAudioFormatMPEG4AAC_ELD = 'aace',
165        kAudioFormatMPEG4AAC_ELD_SBR = 'aacf',
166        kAudioFormatMPEG4AAC_ELD_V2 = 'aacg',
167        kAudioFormatMPEG4AAC_HE_V2 = 'aacp',
168        kAudioFormatMPEG4AAC_Spatial = 'aacs',
169        kAudioFormatAMR = 'samr',
170        kAudioFormatAudible = 'AUDB',
171        kAudioFormatiLBC = 'ilbc',
172        kAudioFormatDVIIntelIMA = 0x6D730011,
173        kAudioFormatMicrosoftGSM = 0x6D730031,
174        kAudioFormatAES3 = 'aes3',
175 };
176 
177 enum { kAudioFormatFlagIsFloat = (1L << 0),
178        kAudioFormatFlagIsBigEndian = (1L << 1),
179        kAudioFormatFlagIsSignedInteger = (1L << 2),
180        kAudioFormatFlagIsPacked = (1L << 3),
181        kAudioFormatFlagIsAlignedHigh = (1L << 4),
182        kAudioFormatFlagIsNonInterleaved = (1L << 5),
183        kAudioFormatFlagIsNonMixable = (1L << 6),
184        kAudioFormatFlagsAreAllClear = (1L << 31),
185 
186        kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat,
187        kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian,
188        kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger,
189        kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked,
190        kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh,
191        kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved,
192        kLinearPCMFormatFlagIsNonMixable = kAudioFormatFlagIsNonMixable,
193        kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear,
194 
195        kAppleLosslessFormatFlag_16BitSourceData = 1,
196        kAppleLosslessFormatFlag_20BitSourceData = 2,
197        kAppleLosslessFormatFlag_24BitSourceData = 3,
198        kAppleLosslessFormatFlag_32BitSourceData = 4,
199 };
200 
201 enum { kAudioFormatFlagsNativeEndian = 0 };
202 
203 enum {
204 	// AudioStreamBasicDescription structure properties
205 	kAudioFormatProperty_FormatInfo = 'fmti',
206 	kAudioFormatProperty_FormatName = 'fnam',
207 	kAudioFormatProperty_EncodeFormatIDs = 'acof',
208 	kAudioFormatProperty_DecodeFormatIDs = 'acif',
209 	kAudioFormatProperty_FormatList = 'flst',
210 	kAudioFormatProperty_ASBDFromESDS = 'essd',
211 	kAudioFormatProperty_ChannelLayoutFromESDS = 'escl',
212 	kAudioFormatProperty_OutputFormatList = 'ofls',
213 	kAudioFormatProperty_Encoders = 'aven',
214 	kAudioFormatProperty_Decoders = 'avde',
215 	kAudioFormatProperty_FormatIsVBR = 'fvbr',
216 	kAudioFormatProperty_FormatIsExternallyFramed = 'fexf',
217 	kAudioFormatProperty_AvailableEncodeBitRates = 'aebr',
218 	kAudioFormatProperty_AvailableEncodeSampleRates = 'aesr',
219 	kAudioFormatProperty_AvailableEncodeChannelLayoutTags = 'aecl',
220 	kAudioFormatProperty_AvailableEncodeNumberChannels = 'avnc',
221 	kAudioFormatProperty_ASBDFromMPEGPacket = 'admp',
222 	//
223 	// AudioChannelLayout structure properties
224 	kAudioFormatProperty_BitmapForLayoutTag = 'bmtg',
225 	kAudioFormatProperty_MatrixMixMap = 'mmap',
226 	kAudioFormatProperty_ChannelMap = 'chmp',
227 	kAudioFormatProperty_NumberOfChannelsForLayout = 'nchm',
228 	kAudioFormatProperty_ValidateChannelLayout = 'vacl',
229 	kAudioFormatProperty_ChannelLayoutForTag = 'cmpl',
230 	kAudioFormatProperty_TagForChannelLayout = 'cmpt',
231 	kAudioFormatProperty_ChannelLayoutName = 'lonm',
232 	kAudioFormatProperty_ChannelLayoutSimpleName = 'lsnm',
233 	kAudioFormatProperty_ChannelLayoutForBitmap = 'cmpb',
234 	kAudioFormatProperty_ChannelName = 'cnam',
235 	kAudioFormatProperty_ChannelShortName = 'csnm',
236 	kAudioFormatProperty_TagsForNumberOfChannels = 'tagc',
237 	kAudioFormatProperty_PanningMatrix = 'panm',
238 	kAudioFormatProperty_BalanceFade = 'balf',
239 	//
240 	// ID3 tag (MP3 metadata) properties
241 	kAudioFormatProperty_ID3TagSize = 'id3s',
242 	kAudioFormatProperty_ID3TagToDictionary = 'id3d',
243 };
244 
245 enum { kAudioConverterPropertyMinimumInputBufferSize = 'mibs',
246        kAudioConverterPropertyMinimumOutputBufferSize = 'mobs',
247        kAudioConverterPropertyMaximumInputBufferSize = 'xibs',
248        kAudioConverterPropertyMaximumInputPacketSize = 'xips',
249        kAudioConverterPropertyMaximumOutputPacketSize = 'xops',
250        kAudioConverterPropertyCalculateInputBufferSize = 'cibs',
251        kAudioConverterPropertyCalculateOutputBufferSize = 'cobs',
252        kAudioConverterPropertyInputCodecParameters = 'icdp',
253        kAudioConverterPropertyOutputCodecParameters = 'ocdp',
254        kAudioConverterSampleRateConverterAlgorithm = 'srci',
255        kAudioConverterSampleRateConverterComplexity = 'srca',
256        kAudioConverterSampleRateConverterQuality = 'srcq',
257        kAudioConverterSampleRateConverterInitialPhase = 'srcp',
258        kAudioConverterCodecQuality = 'cdqu',
259        kAudioConverterPrimeMethod = 'prmm',
260        kAudioConverterPrimeInfo = 'prim',
261        kAudioConverterChannelMap = 'chmp',
262        kAudioConverterDecompressionMagicCookie = 'dmgc',
263        kAudioConverterCompressionMagicCookie = 'cmgc',
264        kAudioConverterEncodeBitRate = 'brat',
265        kAudioConverterEncodeAdjustableSampleRate = 'ajsr',
266        kAudioConverterInputChannelLayout = 'icl ',
267        kAudioConverterOutputChannelLayout = 'ocl ',
268        kAudioConverterApplicableEncodeBitRates = 'aebr',
269        kAudioConverterAvailableEncodeBitRates = 'vebr',
270        kAudioConverterApplicableEncodeSampleRates = 'aesr',
271        kAudioConverterAvailableEncodeSampleRates = 'vesr',
272        kAudioConverterAvailableEncodeChannelLayoutTags = 'aecl',
273        kAudioConverterCurrentOutputStreamDescription = 'acod',
274        kAudioConverterCurrentInputStreamDescription = 'acid',
275        kAudioConverterPropertySettings = 'acps',
276        kAudioConverterPropertyBitDepthHint = 'acbd',
277        kAudioConverterPropertyFormatList = 'flst',
278 };
279 
280 enum { kAudioConverterQuality_Max = 0x7F,
281        kAudioConverterQuality_High = 0x60,
282        kAudioConverterQuality_Medium = 0x40,
283        kAudioConverterQuality_Low = 0x20,
284        kAudioConverterQuality_Min = 0,
285 };
286 
287 enum { kAudio_UnimplementedError = -4,
288        kAudio_FileNotFoundError = -43,
289        kAudio_FilePermissionError = -54,
290        kAudio_TooManyFilesOpenError = -42,
291        kAudio_BadFilePathError = '!pth', // 0x21707468, 561017960
292        kAudio_ParamError = -50,
293        kAudio_MemFullError = -108,
294 
295        kAudioConverterErr_FormatNotSupported = 'fmt?',
296        kAudioConverterErr_OperationNotSupported = 0x6F703F3F,
297        // 'op??', integer used because of trigraph
298        kAudioConverterErr_PropertyNotSupported = 'prop',
299        kAudioConverterErr_InvalidInputSize = 'insz',
300        kAudioConverterErr_InvalidOutputSize = 'otsz',
301        // e.g. byte size is not a multiple of the frame size
302        kAudioConverterErr_UnspecifiedError = 'what',
303        kAudioConverterErr_BadPropertySizeError = '!siz',
304        kAudioConverterErr_RequiresPacketDescriptionsError = '!pkd',
305        kAudioConverterErr_InputSampleRateOutOfRange = '!isr',
306        kAudioConverterErr_OutputSampleRateOutOfRange = '!osr',
307 };
308 
309 typedef OSStatus (*AudioConverterNew_t)(
310 	const AudioStreamBasicDescription *inSourceFormat,
311 	const AudioStreamBasicDescription *inDestinationFormat,
312 	AudioConverterRef *outAudioConverter);
313 
314 typedef OSStatus (*AudioConverterDispose_t)(AudioConverterRef inAudioConverter);
315 
316 typedef OSStatus (*AudioConverterReset_t)(AudioConverterRef inAudioConverter);
317 
318 typedef OSStatus (*AudioConverterGetProperty_t)(
319 	AudioConverterRef inAudioConverter,
320 	AudioConverterPropertyID inPropertyID, UInt32 *ioPropertyDataSize,
321 	void *outPropertyData);
322 
323 typedef OSStatus (*AudioConverterGetPropertyInfo_t)(
324 	AudioConverterRef inAudioConverter,
325 	AudioConverterPropertyID inPropertyID, UInt32 *outSize,
326 	Boolean *outWritable);
327 
328 typedef OSStatus (*AudioConverterSetProperty_t)(
329 	AudioConverterRef inAudioConverter,
330 	AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize,
331 	const void *inPropertyData);
332 
333 typedef OSStatus (*AudioConverterFillComplexBuffer_t)(
334 	AudioConverterRef inAudioConverter,
335 	AudioConverterComplexInputDataProc inInputDataProc,
336 	void *inInputDataProcUserData, UInt32 *ioOutputDataPacketSize,
337 	AudioBufferList *outOutputData,
338 	AudioStreamPacketDescription *outPacketDescription);
339 
340 typedef OSStatus (*AudioFormatGetProperty_t)(AudioFormatPropertyID inPropertyID,
341 					     UInt32 inSpecifierSize,
342 					     const void *inSpecifier,
343 					     UInt32 *ioPropertyDataSize,
344 					     void *outPropertyData);
345 
346 typedef OSStatus (*AudioFormatGetPropertyInfo_t)(
347 	AudioFormatPropertyID inPropertyID, UInt32 inSpecifierSize,
348 	const void *inSpecifier, UInt32 *outPropertyDataSize);
349 
350 static AudioConverterNew_t AudioConverterNew = NULL;
351 static AudioConverterDispose_t AudioConverterDispose = NULL;
352 static AudioConverterReset_t AudioConverterReset = NULL;
353 static AudioConverterGetProperty_t AudioConverterGetProperty = NULL;
354 static AudioConverterGetPropertyInfo_t AudioConverterGetPropertyInfo = NULL;
355 static AudioConverterSetProperty_t AudioConverterSetProperty = NULL;
356 static AudioConverterFillComplexBuffer_t AudioConverterFillComplexBuffer = NULL;
357 static AudioFormatGetProperty_t AudioFormatGetProperty = NULL;
358 static AudioFormatGetPropertyInfo_t AudioFormatGetPropertyInfo = NULL;
359 
360 static HMODULE audio_toolbox = NULL;
361 
release_lib(void)362 static void release_lib(void)
363 {
364 	if (audio_toolbox) {
365 		FreeLibrary(audio_toolbox);
366 		audio_toolbox = NULL;
367 	}
368 }
369 
load_from_shell_path(REFKNOWNFOLDERID rfid,const wchar_t * subpath)370 static bool load_from_shell_path(REFKNOWNFOLDERID rfid, const wchar_t *subpath)
371 {
372 	wchar_t *sh_path;
373 	if (SHGetKnownFolderPath(rfid, 0, NULL, &sh_path) != S_OK) {
374 		CA_LOG(LOG_WARNING, "Could not retrieve shell path");
375 		return false;
376 	}
377 
378 	wchar_t path[MAX_PATH];
379 	_snwprintf(path, MAX_PATH, L"%s\\%s", sh_path, subpath);
380 	CoTaskMemFree(sh_path);
381 
382 	SetDllDirectory(path);
383 	audio_toolbox = LoadLibraryW(L"CoreAudioToolbox.dll");
384 	SetDllDirectory(nullptr);
385 
386 	return !!audio_toolbox;
387 }
388 
load_lib(void)389 static bool load_lib(void)
390 {
391 	/* -------------------------------------------- */
392 	/* attempt to load from path                    */
393 
394 	audio_toolbox = LoadLibraryW(L"CoreAudioToolbox.dll");
395 	if (!!audio_toolbox)
396 		return true;
397 
398 	/* -------------------------------------------- */
399 	/* attempt to load from known install locations */
400 
401 	struct path_list_t {
402 		REFKNOWNFOLDERID rfid;
403 		const wchar_t *subpath;
404 	};
405 
406 	path_list_t path_list[] = {
407 		{FOLDERID_ProgramFilesCommon,
408 		 L"Apple\\Apple Application Support"},
409 		{FOLDERID_ProgramFiles, L"iTunes"},
410 	};
411 
412 	for (auto &val : path_list) {
413 		if (load_from_shell_path(val.rfid, val.subpath)) {
414 			return true;
415 		}
416 	}
417 
418 	return false;
419 }
420 
unload_core_audio(void)421 static void unload_core_audio(void)
422 {
423 	AudioConverterNew = NULL;
424 	AudioConverterDispose = NULL;
425 	AudioConverterReset = NULL;
426 	AudioConverterGetProperty = NULL;
427 	AudioConverterGetPropertyInfo = NULL;
428 	AudioConverterSetProperty = NULL;
429 	AudioConverterFillComplexBuffer = NULL;
430 	AudioFormatGetProperty = NULL;
431 	AudioFormatGetPropertyInfo = NULL;
432 
433 	release_lib();
434 }
435 
436 #ifdef _MSC_VER
437 #pragma warning(push)
438 #pragma warning(disable : 4706)
439 #endif
load_core_audio(void)440 static bool load_core_audio(void)
441 {
442 	if (!load_lib())
443 		return false;
444 
445 #define LOAD_SYM_FROM_LIB(sym, lib, dll)                                   \
446 	if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) {                 \
447 		DWORD err = GetLastError();                                \
448 		CA_LOG(LOG_ERROR,                                          \
449 		       "Couldn't load " #sym " from " dll ": %lu (0x%lx)", \
450 		       err, err);                                          \
451 		goto unload_everything;                                    \
452 	}
453 
454 #define LOAD_SYM(sym) \
455 	LOAD_SYM_FROM_LIB(sym, audio_toolbox, "CoreAudioToolbox.dll")
456 	LOAD_SYM(AudioConverterNew);
457 	LOAD_SYM(AudioConverterDispose);
458 	LOAD_SYM(AudioConverterReset);
459 	LOAD_SYM(AudioConverterGetProperty);
460 	LOAD_SYM(AudioConverterGetPropertyInfo);
461 	LOAD_SYM(AudioConverterSetProperty);
462 	LOAD_SYM(AudioConverterFillComplexBuffer);
463 	LOAD_SYM(AudioFormatGetProperty);
464 	LOAD_SYM(AudioFormatGetPropertyInfo);
465 #undef LOAD_SYM
466 
467 	return true;
468 
469 unload_everything:
470 	unload_core_audio();
471 
472 	return false;
473 }
474 #ifdef _MSC_VER
475 #pragma warning(pop)
476 #endif
477