Advertisement
Kitomas

kit_w32_audio.c (scrapped)

Jul 14th, 2023
750
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 24.31 KB | Source Code | 0 0
  1. //compile with kernel32, and ole32
  2.  /**
  3.   * \file kit_w32_audio.c
  4.   * \brief Header file for KIT Win32's audio module
  5.   */
  6.  
  7. #include <kit_w32_audio.h>
  8. #include <_kit_w32_audioCOM.h>
  9. #include <_kit_w32_audioPropKeyDef.h> //#include <Functiondiscoverykeys_devpkey.h>
  10.  
  11.  
  12. #define _KIT_AUDIO_MMONO   (SPEAKER_FRONT_LEFT)
  13. #define _KIT_AUDIO_MSTEREO (_KIT_AUDIO_MMONO  |SPEAKER_FRONT_RIGHT  )
  14. #define _KIT_AUDIO_M2_1S   (_KIT_AUDIO_MSTEREO|SPEAKER_LOW_FREQUENCY)
  15. #define _KIT_AUDIO_MQUAD   (_KIT_AUDIO_MSTEREO|SPEAKER_BACK_LEFT    |SPEAKER_BACK_RIGHT)
  16. #define _KIT_AUDIO_M4_1S   (_KIT_AUDIO_MQUAD  |SPEAKER_LOW_FREQUENCY)
  17. #define _KIT_AUDIO_M5_1S   (_KIT_AUDIO_M4_1S  |SPEAKER_FRONT_CENTER )
  18. #define _KIT_AUDIO_M6_1S   (_KIT_AUDIO_M2_1S  |SPEAKER_FRONT_CENTER |SPEAKER_SIDE_LEFT |SPEAKER_SIDE_RIGHT)
  19. #define _KIT_AUDIO_M7_1S   (_KIT_AUDIO_M5_1S  |SPEAKER_SIDE_LEFT    |SPEAKER_SIDE_RIGHT)
  20.  
  21.  
  22. #define _KIT_AUDIO_LOCKCALLBACKTHREAD(_device) \
  23.   EnterCriticalSection(&_device->_callback_mutex);
  24. #define _KIT_AUDIO_UNLOCKCALLBACKTHREAD(_device) \
  25.   LeaveCriticalSection(&_device->_callback_mutex);
  26.  
  27.  
  28. #define _IS_ERROR(_value,_before_goto) \
  29.   { returnStatus=_value; _before_goto; goto _error_; }
  30. #define _IF_ERROR(_condition,_value,_before_goto) \
  31.   if(_condition){ _IS_ERROR(_value,_before_goto); }
  32.  
  33. #define _KIT_AUDIO_FREE(_action,_pointer) \
  34.   if(_pointer != NULL){ _action(_pointer); }
  35. #define _KIT_AUDIO_RELEASE(_action,_pointer) \
  36.   if(_pointer != NULL){ while(numRefs){ \
  37.     numRefs=_action(_pointer); \
  38.   }} \
  39.   numRefs=1;
  40.  
  41.  
  42. #define _LOCK_GLOBALS_AUDIO EnterCriticalSection(&_kit_audioGlobals.lock);
  43. #define _UNLOCK_GLOBALS_AUDIO LeaveCriticalSection(&_kit_audioGlobals.lock);
  44. /*
  45. typedef struct {
  46.   union {
  47.     WAVEFORMATEX         device_format;
  48.     WAVEFORMATEXTENSIBLE device_format_ext;
  49.   };
  50.   IMMDeviceCollection* devices;
  51.   IMMDevice*            device;
  52.   IPropertyStore*        props;
  53.   IAudioClient*         client;
  54.   LPWSTR             device_id; //aka wide char* (wchar_t*)!
  55.   LPWSTR          device_iname;
  56.   LPWSTR           device_name;
  57.   LPWSTR           device_desc;
  58.   size_t      device_iname_len;
  59.   size_t       device_name_len;
  60.   size_t       device_desc_len;
  61.   UINT            device_index;
  62.   EDataFlow          data_flow; //read-only; set by init
  63.   int                data_type; //1,2,3 = pcm&format, float&format_ext, pcm&format_ext
  64.   int     event_driven_support; //might not be used
  65. } _kit_audioGlobalsDevInfo;
  66. struct _kit_audioGlobals_s {
  67.   CRITICAL_SECTION           lock;
  68.   IMMDeviceEnumerator* enumerator;
  69.   kit_audioDevice**   kit_devices;
  70.   UINT            kit_devices_len;
  71.   _kit_audioGlobalsDevInfo      i;
  72.   _kit_audioGlobalsDevInfo      o;
  73.   int                        init;
  74. };
  75. */
  76. //(todo: eventually make this struct opaque and anonymous)
  77. struct _kit_audioGlobals_s _kit_audioGlobals={0};
  78. //KSDATAFORMAT_SUBTYPE_IEEE_FLOAT="00000003-0000-0010-8000-00aa00389b71"
  79. const GUID    _SUBTYPE_IEEE_FLOAT={.Data1=0x00000003,.Data2=0x0000,.Data3=0x0010,.Data4={0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71}};
  80. //KSDATAFORMAT_SUBTYPE_PCM       ="00000001-0000-0010-8000-00aa00389b71"
  81. const GUID    _SUBTYPE_PCM       ={.Data1=0x00000001,.Data2=0x0000,.Data3=0x0010,.Data4={0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71}};
  82.  
  83.  
  84.  
  85. /* (private) INLINES */
  86.  
  87. static inline int _kit_audioCountBits(long long unsigned int bitState){
  88.   int bitCount=0;
  89.   while(bitState){ bitCount+=bitState&1; bitState>>=1; }
  90.   return bitCount;
  91. }
  92.  
  93.  
  94.  
  95. /* CALLBACK-RELATED FUNCTIONS */
  96.  
  97.  
  98.  
  99. /* COM-RELATED FUNCTIONS */
  100.  
  101. kit_audioSpec _kit_audioWaveFormatToSpec(WAVEFORMATEX* format_ex, int* returnStatus_p){ /*0 -> 5*/
  102.   int returnStatus=0; kit_audioSpec spec; //memset(&spec,0xff,sizeof(kit_audioSpec));
  103.   WAVEFORMATEXTENSIBLE* format_ext=(void*)format_ex;
  104.  
  105.   _IF_ERROR(format_ex==NULL,1,;);
  106.   WORD format_tag=format_ex->wFormatTag;
  107.  
  108.   kit_audioFormat format=0;
  109.   if(format_tag==WAVE_FORMAT_EXTENSIBLE){
  110.     _IF_ERROR(format_ext->Samples.wValidBitsPerSample>256,3,;);
  111.     format|=format_ext->Samples.wValidBitsPerSample-1;
  112.     _IF_ERROR(format_ex->cbSize!=(sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)),4,;);
  113.  
  114.     GUID* subformat_tag=&format_ext->SubFormat;
  115.     if(IsEqualGUID(subformat_tag,&_SUBTYPE_IEEE_FLOAT)) format |= KIT_AUDIO_FMT_MFLOAT|KIT_AUDIO_FMT_MSIGNED;
  116.     else if(IsEqualGUID(subformat_tag,&_SUBTYPE_PCM)) format |= KIT_AUDIO_FMT_MSIGNED*(format>8);
  117.     else _IS_ERROR(5,;);
  118.   } else if(format_tag==WAVE_FORMAT_PCM){
  119.     _IF_ERROR(format_ex->wBitsPerSample>256,3,;);
  120.     format|=format_ex->wBitsPerSample-1;
  121.   } else _IS_ERROR(2,;);
  122.  
  123.   spec.format=format;
  124.   spec.frequency=format_ex->nSamplesPerSec;
  125.   spec.channels=format_ex->nChannels;
  126.  
  127.   spec.buffer_len=spec.buffer_size=0;
  128.   spec.callback=spec.userdata=0;
  129.   _error_:
  130.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  131.   return spec;
  132. }
  133.  
  134.  
  135. WAVEFORMATEXTENSIBLE _kit_audioSpecToWaveFormat(kit_audioSpec* spec, int* returnStatus_p){ /*0 -> 5*/
  136.   int returnStatus=0; WAVEFORMATEXTENSIBLE format={0}, null={0};
  137.   _IF_ERROR(spec==NULL,1,;);
  138.  
  139.   //get kit_audioFormat data
  140.   int bitsPerSample=(spec->format&KIT_AUDIO_FMT_MBITSIZE)+1;
  141.   int isFloat      =(spec->format&KIT_AUDIO_FMT_MFLOAT  )!=0;
  142.   int isBigEndian  =(spec->format&KIT_AUDIO_FMT_MENDIAN )!=0;
  143.   int isSigned     =(spec->format&KIT_AUDIO_FMT_MSIGNED )!=0;
  144.   _IF_ERROR(isBigEndian                 ,2,;);
  145.   _IF_ERROR(isFloat&&(!isSigned)        ,3,;);
  146.   _IF_ERROR(bitsPerSample<=8 && isSigned,4,;);
  147.  
  148.   //fill in WAVEFORMATEX info
  149.   format.Format.wFormatTag=WAVE_FORMAT_EXTENSIBLE;
  150.   format.Format.nChannels=spec->channels;
  151.   format.Format.nSamplesPerSec=spec->frequency;
  152.   format.Format.wBitsPerSample=(bitsPerSample>=8) ? ((bitsPerSample/8)*8) : 8;
  153.   format.Format.nBlockAlign=spec->channels*(format.Format.wBitsPerSample/8);
  154.   format.Format.nAvgBytesPerSec=spec->frequency*format.Format.nBlockAlign;
  155.   format.Format.cbSize=sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
  156.  
  157.   //fill in WAVEFORMATEXTENSIBLE info
  158.   format.Samples.wValidBitsPerSample=bitsPerSample;
  159.    //set guid
  160.   if(isFloat) memcpy(&format.SubFormat,&_SUBTYPE_IEEE_FLOAT,sizeof(GUID));
  161.   else        memcpy(&format.SubFormat,&_SUBTYPE_PCM       ,sizeof(GUID));
  162.    //set channel masks based on channel mapping
  163.   switch(spec->channels){
  164.   case 1: format.dwChannelMask=_KIT_AUDIO_MMONO;   break;
  165.   case 2: format.dwChannelMask=_KIT_AUDIO_MSTEREO; break;
  166.   case 3: format.dwChannelMask=_KIT_AUDIO_M2_1S;   break;
  167.   case 4: format.dwChannelMask=_KIT_AUDIO_MQUAD;   break;
  168.   case 5: format.dwChannelMask=_KIT_AUDIO_M4_1S;   break;
  169.   case 6: format.dwChannelMask=_KIT_AUDIO_M5_1S;   break;
  170.   case 7: format.dwChannelMask=_KIT_AUDIO_M6_1S;   break;
  171.   case 8: format.dwChannelMask=_KIT_AUDIO_M7_1S;   break;
  172.   default: _IS_ERROR(5,;); }
  173.  
  174.   _error_:
  175.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  176.   if(!returnStatus) return format;
  177.   else              return null;
  178. }
  179.  
  180.  
  181. int _kit_audioQueryDevice(unsigned int index, int isCapture){ /*0 -> 4*/ //assumes globals are already locked
  182.   int returnStatus=0; ULONG numRefs=1; UINT numDevices;
  183.  
  184.   //choose between capture and render
  185.   _kit_audioGlobalsDevInfo* info;
  186.   if(isCapture&1) info=&_kit_audioGlobals.i;
  187.   else            info=&_kit_audioGlobals.o;
  188.  
  189.   //is given id greater than maximum valid index?
  190.   _kit_audioCOM_IMMDeviceCollection_GetCount(info->devices, &numDevices);
  191.   _IF_ERROR((index>=numDevices) && (index!=0xffffffff),1,;);
  192.  
  193.   //retrieve IMMDevice & IAudioClient, only if given index is different to the last one retrieved
  194.   if(index!=info->device_index || info->device==NULL){
  195.     //retrieve IMMDevice
  196.      //clean up previous references
  197.     _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDevice_Release,info->device);
  198.     if(index != 0xffffffff){ // index!=-1
  199.       _kit_audioCOM_IMMDeviceCollection_Item(info->devices, (UINT)index, &info->device);
  200.     } else {
  201.       _kit_audioCOM_IMMDeviceEnumerator_GetDefaultAudioEndpoint(_kit_audioGlobals.enumerator,
  202.         info->data_flow, eConsole, &info->device);
  203.     }
  204.     info->device_index=index;
  205.  
  206.     //retrieve IAudioClient
  207.      //clean up previous references
  208.     _KIT_AUDIO_RELEASE(_kit_audioCOM_IAudioClient_Release,info->client);
  209.     printf("!A!last Win32 error=%lu\n",GetLastError());
  210.     switch(_kit_audioCOM_IMMDevice_Activate(info->device,
  211.       &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&info->client)
  212.     ){
  213.     case AUDCLNT_E_DEVICE_INVALIDATED: _IS_ERROR(2,;);
  214.     case E_NOINTERFACE:                _IS_ERROR(3,;);
  215.     case E_OUTOFMEMORY:                _IS_ERROR(4,;);
  216.     case S_OK:;
  217.     }
  218.     printf("!B!last Win32 error=%lu\n",GetLastError());
  219.   }
  220.  
  221.   _error_: return returnStatus;
  222. }
  223.  
  224.  
  225. int _kit_audioQueryDeviceProps(unsigned int index, int isCapture){ /*0 -> 14*/ //assumes globals are already locked
  226.   int returnStatus=0, property_init=0; ULONG numRefs=1;
  227.  
  228.   //choose between capture and render
  229.   _kit_audioGlobalsDevInfo* info;
  230.   if(isCapture&=1) info=&_kit_audioGlobals.i;
  231.   else             info=&_kit_audioGlobals.o;
  232.  
  233.   int equal_to_last_index = index==info->device_index;
  234.   returnStatus=_kit_audioQueryDevice(index,isCapture);
  235.   if(returnStatus) goto _error_; //errors 1 -> 4
  236.  
  237.   //retrieve property store unless current one is identical
  238.   if(!equal_to_last_index || info->props==NULL){
  239.     //clean up previous references
  240.     _KIT_AUDIO_RELEASE(_kit_audioCOM_IPropertyStore_Release,info->props);
  241.     _IF_ERROR(_kit_audioCOM_IMMDevice_OpenPropertyStore(info->device,
  242.       STGM_READ, &info->props) == E_OUTOFMEMORY,
  243.     5,;);
  244.   }
  245.  
  246.   //get WAVEFORMATEX(TENSIBLE) from device's property store
  247.   PROPVARIANT property;
  248.   _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_AudioEngine_DeviceFormat, &property);
  249.   if(property.blob.pBlobData==NULL){
  250.     PropVariantClear(&property);
  251.     //try oem format instead
  252.     _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_AudioEngine_OEMFormat, &property);
  253.     _IF_ERROR(property.blob.pBlobData==NULL,6,;);
  254.   }
  255.   WORD format_tag=((WAVEFORMATEX*)property.blob.pBlobData)->wFormatTag; /* --> */ property_init=1;
  256.  
  257.   if(format_tag == WAVE_FORMAT_EXTENSIBLE){
  258.     GUID* subformat_tag=&((WAVEFORMATEXTENSIBLE*)property.blob.pBlobData)->SubFormat;
  259.  
  260.     if(IsEqualGUID(subformat_tag,&_SUBTYPE_IEEE_FLOAT)) info->data_type=2;
  261.     else if(IsEqualGUID(subformat_tag,&_SUBTYPE_PCM)) info->data_type=3;
  262.     else { return returnStatus=8; goto _error_; }
  263.  
  264.     info->device_format_ext=*(WAVEFORMATEXTENSIBLE*)property.blob.pBlobData;
  265.   } else if(format_tag == WAVE_FORMAT_PCM){ /* --> */ info->data_type=1;
  266.     info->device_format=*(WAVEFORMATEX*)property.blob.pBlobData;
  267.   }
  268.   else{ returnStatus=7; goto _error_; }
  269.   if(property_init) PropVariantClear(&property);
  270.   property_init=0;
  271.  
  272.   //get whether device supports event-driven mode
  273.   _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_AudioEndpoint_Supports_EventDriven_Mode, &property);
  274.   info->event_driven_support=property.uintVal;
  275.   PropVariantClear(&property);
  276.  
  277.   //get name of device
  278.   _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_Device_FriendlyName, &property);
  279.   _IF_ERROR(property.pwszVal==NULL,9,;); /* --> */ property_init=1;
  280.   info->device_name_len=wcslen(property.pwszVal);
  281.   info->device_name=_kit_audioRealloc(info->device_name,wchar_t,info->device_name_len+1);
  282.   _IF_ERROR(info->device_name==NULL,10,;);
  283.   wcscpy(info->device_name,property.pwszVal);
  284.   PropVariantClear(&property); /* --> */ property_init=0;
  285.  
  286.   //get description of device
  287.   _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_Device_DeviceDesc, &property);
  288.   _IF_ERROR(property.pwszVal==NULL,11,;); /* --> */ property_init=1;
  289.   info->device_desc_len=wcslen(property.pwszVal);
  290.   info->device_desc=_kit_audioRealloc(info->device_desc,wchar_t,info->device_desc_len+1);
  291.   _IF_ERROR(info->device_desc==NULL,12,;);
  292.   wcscpy(info->device_desc,property.pwszVal);
  293.   PropVariantClear(&property); /* --> */ property_init=0;
  294.  
  295.   //get interface name of device
  296.   _kit_audioCOM_IPropertyStore_GetValue(info->props, &PKEY_DeviceInterface_FriendlyName, &property);
  297.   _IF_ERROR(property.pwszVal==NULL,13,;); /* --> */ property_init=1;
  298.   info->device_iname_len=wcslen(property.pwszVal);
  299.   info->device_iname=_kit_audioRealloc(info->device_iname,wchar_t,info->device_iname_len+1);
  300.   _IF_ERROR(info->device_iname==NULL,14,;);
  301.   wcscpy(info->device_iname,property.pwszVal);
  302.   PropVariantClear(&property); /* --> */ property_init=0;
  303.  
  304.   _error_:
  305.   if(property_init) PropVariantClear(&property);
  306.   return returnStatus;
  307. }
  308.  
  309.  
  310. int kit_audioQueryDevices(int isCapture, kit_audioDeviceStatus status){ /*0 -> 2*/
  311.   int returnStatus=0; ULONG numRefs=1;
  312.   _LOCK_GLOBALS_AUDIO;
  313.   _IF_ERROR(!(status&ADStatusAll),1,;); //error if status is not a valid enum value
  314.  
  315.   //choose between capture and render
  316.   _kit_audioGlobalsDevInfo* info;
  317.   if(isCapture&1) info=&_kit_audioGlobals.i;
  318.   else            info=&_kit_audioGlobals.o;
  319.  
  320.   //clean up previous references
  321.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDeviceCollection_Release,info->devices);
  322.  
  323.   if(_kit_audioCOM_IMMDeviceEnumerator_EnumAudioEndpoints(_kit_audioGlobals.enumerator,
  324.     info->data_flow, (DWORD)status, &info->devices) == E_OUTOFMEMORY
  325.   ) returnStatus=2;
  326.  
  327.   _error_:
  328.   _UNLOCK_GLOBALS_AUDIO;
  329.   return returnStatus;
  330. }
  331.  
  332.  
  333. unsigned int kit_audioGetNumDevices(int isCapture){
  334.   UINT numDevices;
  335.   _LOCK_GLOBALS_AUDIO;
  336.   IMMDeviceCollection* devices;
  337.   if(isCapture&1) devices=_kit_audioGlobals.i.devices;
  338.   else            devices=_kit_audioGlobals.o.devices;
  339.   _kit_audioCOM_IMMDeviceCollection_GetCount(devices,&numDevices);
  340.   _UNLOCK_GLOBALS_AUDIO;
  341.   return (unsigned int)numDevices;
  342. }
  343.  
  344.  
  345. wchar_t* kit_audioGetDeviceName(unsigned int index, int isCapture,
  346.                                 int mode, int* returnStatus_p){ /*0 -> 14*/
  347.   int returnStatus=0; wchar_t* deviceName=NULL;
  348.   _LOCK_GLOBALS_AUDIO;
  349.  
  350.   //choose between capture and render
  351.   _kit_audioGlobalsDevInfo* info;
  352.   if(isCapture&=1) info=&_kit_audioGlobals.i;
  353.   else             info=&_kit_audioGlobals.o;
  354.  
  355.   returnStatus=_kit_audioQueryDeviceProps(index,isCapture);
  356.   if(returnStatus) goto _error_; //errors 1 -> 14
  357.  
  358.   switch(mode%3){
  359.   case 0: deviceName=info->device_name;  break;
  360.   case 1: deviceName=info->device_desc;  break;
  361.   case 2: deviceName=info->device_iname; }
  362.  
  363.   _error_:
  364.   _UNLOCK_GLOBALS_AUDIO;
  365.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  366.   return deviceName;
  367. }
  368.  
  369.  
  370. wchar_t* kit_audioGetDeviceID(unsigned int index, int isCapture, int* returnStatus_p){ /*0 -> 5*/
  371.   int returnStatus=0; wchar_t* deviceID=NULL;
  372.   _LOCK_GLOBALS_AUDIO;
  373.  
  374.   //choose between capture and render
  375.   _kit_audioGlobalsDevInfo* info;
  376.   if(isCapture&=1) info=&_kit_audioGlobals.i;
  377.   else             info=&_kit_audioGlobals.o;
  378.  
  379.   int equal_to_last_index = index==info->device_index;
  380.   returnStatus=_kit_audioQueryDevice(index,isCapture);
  381.   if(returnStatus) goto _error_; //errors 1 -> 4
  382.  
  383.   //return already cached id if id is the same as the last
  384.   _IF_ERROR(equal_to_last_index,0, deviceID=info->device_id);
  385.   if(_kit_audioCOM_IMMDevice_GetId(info->device,(LPWSTR*)&deviceID) != E_OUTOFMEMORY){
  386.     _KIT_AUDIO_FREE(_kit_audioFree,info->device_id);
  387.     info->device_id=deviceID;
  388.   } else returnStatus=5;
  389.  
  390.   _error_:
  391.   _UNLOCK_GLOBALS_AUDIO;
  392.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  393.   return deviceID;
  394. }
  395.  
  396.  
  397. kit_audioDeviceStatus kit_audioGetDeviceStatus(unsigned int index, int isCapture, int* returnStatus_p){ /*0 -> 4*/
  398.   int returnStatus=0; kit_audioDeviceStatus deviceStatus;
  399.   _LOCK_GLOBALS_AUDIO;
  400.  
  401.   //choose between capture and render
  402.   IMMDevice* device;
  403.   if(isCapture) device=_kit_audioGlobals.i.device;
  404.   else          device=_kit_audioGlobals.o.device;
  405.  
  406.   returnStatus=_kit_audioQueryDevice(index,isCapture&=1);
  407.   if(returnStatus) goto _error_; //errors 1 -> 4
  408.  
  409.   _kit_audioCOM_IMMDevice_GetState(device, (DWORD*)&deviceStatus);
  410.  
  411.   _error_:
  412.   _UNLOCK_GLOBALS_AUDIO;
  413.   if(returnStatus){
  414.     deviceStatus=ADStatusInvalid;
  415.     if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  416.   }
  417.   return deviceStatus;
  418. }
  419.  
  420.  
  421. int kit_audioGetDeviceSpec(unsigned int index, int isCapture, kit_audioSpec* spec){ /*0 -> 20*/
  422.   int returnStatus=0; //ULONG numRefs=1; //HRESULT hr;
  423.   _LOCK_GLOBALS_AUDIO;
  424.   _IF_ERROR(spec==NULL,1,;);
  425.  
  426.   //choose between capture and render
  427.   WAVEFORMATEX* device_format;
  428.   if(isCapture&=1) device_format=&_kit_audioGlobals.i.device_format;
  429.   else             device_format=&_kit_audioGlobals.o.device_format;
  430.  
  431.   //retrieve relevant property store info
  432.   returnStatus=_kit_audioQueryDeviceProps(index,isCapture); //calls _kit_audioQueryDevice
  433.   if(returnStatus){ ++returnStatus; goto _error_; } //errors 2 -> 15
  434.  
  435.   //convert format info to kit_audioSpec
  436.   kit_audioSpec _spec=_kit_audioWaveFormatToSpec(device_format, &returnStatus);
  437.   if(returnStatus){ returnStatus+=15; goto _error_; } //errors 16 -> 20
  438.   *spec=_spec;
  439.  
  440.   _error_:
  441.   _UNLOCK_GLOBALS_AUDIO;
  442.   return returnStatus;
  443. }
  444.  
  445.  
  446. int kit_audioIsSpecSupported(unsigned int index, int isCapture, /*int exclusive,*/ /*-1 -> 17*/
  447.                              kit_audioSpec* spec, kit_audioSpec* closestMatch){
  448.   int returnStatus=0;
  449.   //AUDCLNT_SHAREMODE shareMode=(exclusive&1) ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
  450.   AUDCLNT_SHAREMODE shareMode=AUDCLNT_SHAREMODE_SHARED;
  451.   WAVEFORMATEX* _closestMatch_WFE=NULL;
  452.   _LOCK_GLOBALS_AUDIO;
  453.   _IF_ERROR(spec==NULL,1,;);
  454.  
  455.   //choose between capture and render
  456.   IAudioClient* client;
  457.   if(isCapture&=1) client=_kit_audioGlobals.i.client;
  458.   else             client=_kit_audioGlobals.o.client;
  459.  
  460.   //retrieve device (as well as the associated audio client)
  461.   returnStatus=_kit_audioQueryDevice(index,isCapture);
  462.   if(returnStatus){ ++returnStatus; goto _error_; } //errors 2 -> 5
  463.  
  464.   //convert specification to wave format struct
  465.   WAVEFORMATEXTENSIBLE format=_kit_audioSpecToWaveFormat(spec,&returnStatus);
  466.   if(returnStatus){ returnStatus+=5; goto _error_; } //errors 6 -> 10
  467.  
  468.   switch(_kit_audioCOM_IAudioClient_IsFormatSupported(client,
  469.     shareMode, (WAVEFORMATEX*)&format, &_closestMatch_WFE)
  470.   ){
  471.   //case AUDCLNT_E_UNSUPPORTED_FORMAT: (only applicable in exclusive sharemode)
  472.   //i don't get how this would realistically happen in practice
  473.    //(like, the fundamental audio service isn't running at all?)
  474.   case AUDCLNT_E_SERVICE_NOT_RUNNING: _IS_ERROR(11,;);
  475.   case AUDCLNT_E_DEVICE_INVALIDATED:  _IS_ERROR(12,;);
  476.   case S_FALSE: returnStatus=-1;
  477.   case S_OK:;
  478.   }
  479.  
  480.   _error_:
  481.   _UNLOCK_GLOBALS_AUDIO;
  482.   if(_closestMatch_WFE != NULL){
  483.     kit_audioSpec _closestMatch=_kit_audioWaveFormatToSpec(_closestMatch_WFE,&returnStatus);
  484.     if(returnStatus){ returnStatus+=12; goto free_wfe_; } //errors 13 -> 17
  485.     *closestMatch=_closestMatch;
  486.   }
  487.   free_wfe_:
  488.   _KIT_AUDIO_FREE(_kit_audioFree,_closestMatch_WFE);
  489.   return returnStatus;
  490. }
  491.  
  492.  
  493. //this is more of a utility function, as everything used here is public anyway
  494. unsigned int kit_audioGetDeviceIndexFromID(wchar_t* deviceID, int isCapture, int* returnStatus_p){ /*-1 -> 9*/
  495.   int returnStatus=0, locked=0; unsigned int index=0xfffffffe; wchar_t *_deviceID=NULL, *deviceID2;
  496.   _IF_ERROR(deviceID==NULL,1,;);
  497.  
  498.   //copy id to a temporary location, to make sure globals aren't tripped over
  499.   _LOCK_GLOBALS_AUDIO; locked=1;
  500.   int IDLen=wcslen(deviceID);
  501.   _IF_ERROR(!IDLen,2,;);
  502.   _deviceID=_kit_audioMalloc(wchar_t,IDLen);
  503.   _IF_ERROR(_deviceID==NULL,3,;);
  504.   wcscpy(_deviceID,deviceID);
  505.   _UNLOCK_GLOBALS_AUDIO; locked=0;
  506.  
  507.   //do a linear search of ids
  508.   unsigned int numDevices=kit_audioGetNumDevices(isCapture&=1);
  509.   _IF_ERROR(!numDevices,4,;);
  510.   for(unsigned int i=-1; (i==-1)||(i<numDevices); ++i){
  511.     deviceID2=kit_audioGetDeviceID(i,isCapture,&returnStatus);
  512.     if(returnStatus){ returnStatus+=4; goto _error_; } //errors 5 -> 9
  513.     _LOCK_GLOBALS_AUDIO; locked=1;
  514.     if(wcscmp(_deviceID,deviceID2)){ index=i; break; }
  515.     _UNLOCK_GLOBALS_AUDIO; locked=0;
  516.   }
  517.   if(index == 0xfffffffe) returnStatus=-1; //index == -2
  518.  
  519.   _error_:
  520.   if(locked) _UNLOCK_GLOBALS_AUDIO;
  521.   _KIT_AUDIO_FREE(_kit_audioFree,_deviceID);
  522.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  523.   return index;
  524. }
  525.  
  526.  
  527.  
  528. /* kit_audioDevice-RELATED FUNCTIONS */
  529.  
  530. int kit_audioDevicePlay(kit_audioDevice* device, kit_audioDevicePState playState){
  531.   int returnStatus=0;
  532.   //_error_:
  533.   return returnStatus;
  534. }
  535.  
  536.  
  537. kit_audioDevicePState kit_audioGetDevicePlayState(kit_audioDevice* device){
  538.   kit_audioDevicePState returnStatus=ADPStateInvalid;
  539.   //_error_:
  540.   return returnStatus;
  541. }
  542.  
  543.  
  544.  
  545. /* (UN)INIT FUNCTIONS */
  546.  
  547. int kit_audioInit(int initCOM){
  548.   int returnStatus=0, critical_section_init=0;
  549.   //might cause race conditions, but i don't know how to structure this better
  550.   _IF_ERROR(_kit_audioGlobals.init,1,;);
  551.   InitializeCriticalSection(&_kit_audioGlobals.lock);
  552.   critical_section_init=1;
  553.   _LOCK_GLOBALS_AUDIO;
  554.   memset(((void*)&_kit_audioGlobals)+sizeof(CRITICAL_SECTION),0,
  555.          sizeof(_kit_audioGlobals)-sizeof(CRITICAL_SECTION));
  556.  
  557.   //set i and o's data_flow to eCapture and eRender respectively
  558.   _kit_audioGlobals.i.data_flow=eCapture;
  559.   _kit_audioGlobals.o.data_flow=eRender;
  560.  
  561.   //initialize com
  562.   if(initCOM&1) CoInitializeEx(NULL,COINIT_MULTITHREADED);
  563.  
  564.   //create enumerator object
  565.   HRESULT hr=CoCreateInstance(
  566.     &CLSID_MMDeviceEnumerator,
  567.     NULL, CLSCTX_ALL,
  568.     &IID_IMMDeviceEnumerator,
  569.     (void**)&_kit_audioGlobals.enumerator
  570.   ); _IF_ERROR(FAILED(hr),2,;);
  571.  
  572.   //get input device collection
  573.   hr=_kit_audioCOM_IMMDeviceEnumerator_EnumAudioEndpoints(_kit_audioGlobals.enumerator,
  574.     eCapture, DEVICE_STATEMASK_ALL, &_kit_audioGlobals.i.devices
  575.   ); _IF_ERROR(FAILED(hr),3,;);
  576.  
  577.   //get output device collection
  578.   hr=_kit_audioCOM_IMMDeviceEnumerator_EnumAudioEndpoints(_kit_audioGlobals.enumerator,
  579.     eRender, DEVICE_STATEMASK_ALL, &_kit_audioGlobals.o.devices
  580.   ); _IF_ERROR(FAILED(hr),4,;);
  581.  
  582.   _kit_audioGlobals.init=1;
  583.   _error_:
  584.   if(critical_section_init) _UNLOCK_GLOBALS_AUDIO;
  585.   return returnStatus;
  586. }
  587.  
  588.  
  589. int kit_audioQuit(int uninitCOM){
  590.   int returnStatus=0; ULONG numRefs=1;
  591.   _IF_ERROR(!_kit_audioGlobals.init,1,;);
  592.   _LOCK_GLOBALS_AUDIO;
  593.  
  594.   //free last i/o device names (wchar_t*)
  595.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.i.device_name);
  596.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.i.device_desc);
  597.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.i.device_iname);
  598.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.o.device_name);
  599.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.o.device_desc);
  600.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.o.device_iname);
  601.  
  602.   //free last i/o device ids (wchar_t*)
  603.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.i.device_id);
  604.   _KIT_AUDIO_FREE(_kit_audioFree,_kit_audioGlobals.o.device_id);
  605.  
  606.   //release last i/o device audio clients
  607.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IAudioClient_Release,_kit_audioGlobals.i.client);
  608.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IAudioClient_Release,_kit_audioGlobals.o.client);
  609.  
  610.   //release last i/o device property stores
  611.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IPropertyStore_Release,_kit_audioGlobals.i.props);
  612.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IPropertyStore_Release,_kit_audioGlobals.o.props);
  613.  
  614.   //release last i/o devices
  615.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDevice_Release,_kit_audioGlobals.i.device);
  616.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDevice_Release,_kit_audioGlobals.o.device);
  617.  
  618.   //release i/o device collections
  619.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDeviceCollection_Release,_kit_audioGlobals.i.devices);
  620.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDeviceCollection_Release,_kit_audioGlobals.o.devices);
  621.  
  622.   //release enumerator
  623.   _KIT_AUDIO_RELEASE(_kit_audioCOM_IMMDeviceEnumerator_Release,_kit_audioGlobals.enumerator);
  624.  
  625.   //todo: find out why CoUninitialize() makes GetLastError()=126 (ERROR_MOD_NOT_FOUND)
  626.   if(uninitCOM&1) CoUninitialize();
  627.   _kit_audioGlobals.init=0;
  628.   _error_:
  629.   _UNLOCK_GLOBALS_AUDIO;
  630.   if(!returnStatus) DeleteCriticalSection(&_kit_audioGlobals.lock);
  631.   return returnStatus;
  632. }
  633.  
  634.  
  635. kit_audioDevice* kit_audioDeviceOpen(kit_audioSpec* spec, unsigned int index,
  636.                                      int isCapture, int* returnStatus_p){
  637.   int returnStatus=0;
  638.   _IF_ERROR(spec==NULL,1,;);
  639.  
  640.   _error_:
  641.   if(returnStatus_p != NULL) *returnStatus_p=returnStatus;
  642.   return NULL;
  643. }
  644.  
  645.  
  646. int kit_audioDeviceClose(kit_audioDevice** device_p){
  647.   int returnStatus=0;
  648.   _IF_ERROR(device_p==NULL,1,;);
  649.  
  650.   _error_:
  651.   return returnStatus;
  652. }
  653.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement