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