Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //compile with kernel32, and ole32
- /**
- * \file kit_w32_audio.h
- * \brief Header file for KIT Win32's audio module
- */
- #ifndef _KIT_W32_AUDIO_H
- #define _KIT_W32_AUDIO_H
- # ifndef _KIT_AUDIO
- # define _KIT_AUDIO
- #include <wchar.h> //might be redundant, but this for sure includes wchar_t
- #include <stdint.h>
- #include <string.h> //used for memcpy
- #include <synchapi.h> //used for CRITICAL_SECTION
- #include <processthreadsapi.h> //used to create and manage threads
- #include <combaseapi.h>
- # define _kit_audioMalloc(_type,_len) CoTaskMemAlloc(sizeof(_type)*(_len))
- # define _kit_audioFree(_ptr) CoTaskMemFree(_ptr)
- # define _kit_audioRealloc(_ptr,_type,_len) CoTaskMemRealloc(_ptr,sizeof(_type)*(_len))
- #ifndef COM_NO_WINDOWS_H
- # define COM_NO_WINDOWS_H
- # include <windef.h> //includes winnt.h
- # include <initguid.h>
- # include <audioclient.h>
- # include <mmdeviceapi.h>
- # undef COM_NO_WINDOWS_H
- #else
- # include <windef.h>
- # include <initguid.h>
- # include <audioclient.h>
- # include <mmdeviceapi.h>
- #endif
- #include <dsound.h>
- /* GENERAL */
- /**
- * \name Audio Format Constants
- */
- /** @{ */
- #define KIT_AUDIO_FMT_I8 (0x8007) ///< \brief signed 8-bit samples
- #define KIT_AUDIO_FMT_U8 (0x0007) ///< \brief unsigned 8-bit samples
- #define KIT_AUDIO_FMT_I16LSB (0x800F) ///< \brief signed 16-bit samples (little endian)
- #define KIT_AUDIO_FMT_U16LSB (0x000F) ///< \brief unsigned 16-bit samples (little endian)
- #define KIT_AUDIO_FMT_I24LSB (0x8017) ///< \brief signed 24-bit samples (little endian)
- #define KIT_AUDIO_FMT_U24LSB (0x0017) ///< \brief unsigned 24-bit samples (little endian)
- #define KIT_AUDIO_FMT_I32LSB (0x801F) ///< \brief signed 32-bit samples (little endian)
- #define KIT_AUDIO_FMT_U32LSB (0x001F) ///< \brief unsigned 32-bit samples (little endian)
- #define KIT_AUDIO_FMT_F32LSB (0x811F) ///< \brief 32-bit float samples (little endian)
- #define KIT_AUDIO_FMT_F64LSB (0x813F) ///< \brief 64-bit float samples (little endian)
- //x86 is little endian
- //#define KIT_AUDIO_FMT_I16MSB (0x900F) ///< \brief signed 16-bit samples (big endian)
- //#define KIT_AUDIO_FMT_U16MSB (0x100F) ///< \brief unsigned 16-bit samples (big endian)
- //#define KIT_AUDIO_FMT_I24MSB (0x9017) ///< \brief signed 24-bit samples (big endian)
- //#define KIT_AUDIO_FMT_U24MSB (0x1017) ///< \brief unsigned 24-bit samples (big endian)
- //#define KIT_AUDIO_FMT_I32MSB (0x901F) ///< \brief signed 32-bit samples (big endian)
- //#define KIT_AUDIO_FMT_U32MSB (0x101F) ///< \brief unsigned 32-bit samples (big endian)
- //#define KIT_AUDIO_FMT_F32MSB (0x911F) ///< \brief 32-bit float samples (big endian)
- //#define KIT_AUDIO_FMT_F64MSB (0x913F) ///< \brief 64-bit float samples (big endian)
- #define KIT_AUDIO_FMT_I16 KIT_AUDIO_FMT_I16LSB
- #define KIT_AUDIO_FMT_U16 KIT_AUDIO_FMT_U16LSB
- #define KIT_AUDIO_FMT_I24 KIT_AUDIO_FMT_I24LSB
- #define KIT_AUDIO_FMT_U24 KIT_AUDIO_FMT_U24LSB
- #define KIT_AUDIO_FMT_I32 KIT_AUDIO_FMT_I32LSB
- #define KIT_AUDIO_FMT_U32 KIT_AUDIO_FMT_U32LSB
- #define KIT_AUDIO_FMT_F32 KIT_AUDIO_FMT_F32LSB
- #define KIT_AUDIO_FMT_F64 KIT_AUDIO_FMT_F64LSB
- #define KIT_AUDIO_FMT_INVALID (0x7FFF)
- /** @} */
- /**
- * \name Audio Format Bitmasks & Macros
- */
- /** @{ */ /* (the "M" in FMT_M means (bit)mask) */
- #define KIT_AUDIO_FMT_MBITSIZE (0x00FF) ///< \brief format bitsize mask
- #define KIT_AUDIO_FMT_MFLOAT (0x0100) ///< \brief format float mask
- #define KIT_AUDIO_FMT_MENDIAN (0X1000) ///< \brief format endianness mask
- #define KIT_AUDIO_FMT_MSIGNED (0x8000) ///< \brief format signedness mask
- #define KIT_AUDIO_FMT_BITSIZE(x) ((x) & KIT_AUDIO_FMT_MBITSIZE)
- #define KIT_AUDIO_FMT_ISFLOAT(x) ((x) & KIT_AUDIO_FMT_MFLOAT)
- #define KIT_AUDIO_FMT_ISBIGENDIAN(x) ((x) & KIT_AUDIO_FMT_MENDIAN)
- #define KIT_AUDIO_FMT_ISSIGNED(x) ((x) & KIT_AUDIO_FMT_MSIGNED)
- #define KIT_AUDIO_FMT_ISINT(x) (!KIT_AUDIO_FMT_ISFLOAT(x))
- #define KIT_AUDIO_FMT_ISLITTLEENDIAN(x) (!KIT_AUDIO_FMT_ISBIGENDIAN(x))
- #define KIT_AUDIO_FMT_ISUNSIGNED(x) (!KIT_AUDIO_FMT_ISSIGNED(x))
- /** @} */
- /**
- * \name Audio Device Type Constants
- */
- /** @{ */
- #define _KIT_AUDIO_DEVTYPE_BASE 0
- #define KIT_AUDIO_DEVTYPE_WASAPIIN (_KIT_AUDIO_DEVTYPE_BASE+1)
- #define KIT_AUDIO_DEVTYPE_WASAPIOUT (_KIT_AUDIO_DEVTYPE_BASE+2)
- /** @} */
- /**
- * \name Audio Device Type Macros
- */
- /** @{ */
- #define KIT_AUDIO_IFNOTDEVICE(_device)\
- if(_device == NULL || \
- _device->_magic.num != 0x007665446F69647561 \
- )
- #define KIT_AUDIO_IFDEVICEINVALID(_device,_type)\
- if(_device == NULL || \
- _device->_magic.num != 0x007665446F69647561 || \
- _device->_device_type != _type \
- )
- #define KIT_AUDIO_AUDITANDLOCK(_device,_type,_errorvalue)\
- if(!kit_audioLockDevice(_device) || \
- _device->device_type != _type \
- ) return _errorvalue;
- #define KIT_AUDIO_LOCK(_device) \
- EnterCriticalSection(&_device->_mutex);
- //assumes audit already happened
- #define KIT_AUDIO_UNLOCK(_device) \
- LeaveCriticalSection(&_device->_mutex);
- /** @} */
- /**
- * Audio Format (FMT)
- * \verbatim
- Bit Layout is as follows:
- +----------------------sample is signed if set
- |
- | +----------sample is bigendian if set
- | |
- | | +--sample is float if set
- | | |
- | | | +-sample bit size (-1)+
- | | | | |
- 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
- \endverbatim
- */
- typedef uint16_t kit_audioFormat;
- /**
- * PCM Audio Callback
- * This type of function is called when an opened (and unpaused)
- * audio device's audio buffer needs more PCM data.
- * The given audio buffer must be completely filled before returning
- * \param userdata A user-defined pointer passed to the callback, which
- * is stored in the same kit_audioSpec as the callback itself
- * \param stream A pointer to the audio data buffer
- * \param len The length of that buffer in samples (number of sample frames is len/<# of channels>)
- */
- typedef void (*kit_audioCallback) (void* userdata, void* stream, unsigned int len);
- /**
- * \brief This enum is used to indicate the play status of an audio device
- */
- typedef enum {
- ADPStateInvalid = 0,
- ADPStateStopped = 1,
- ADPStatePlaying = 2,
- ADPStatePaused = 3
- } kit_audioDevicePState;
- /**
- * \brief This enum is used to indicate an audio device's state
- */
- typedef enum { //there might be a better way to name these...
- ADStatusInvalid = (0),
- ADStatusActive = (DEVICE_STATE_ACTIVE),
- ADStatusDisabled = (DEVICE_STATE_DISABLED),
- ADStatusNotPresent = (DEVICE_STATE_NOTPRESENT),
- ADStatusUnplugged = (DEVICE_STATE_UNPLUGGED),
- ADStatusAll = (DEVICE_STATE_ACTIVE|DEVICE_STATE_DISABLED|DEVICE_STATE_NOTPRESENT|DEVICE_STATE_UNPLUGGED)
- } kit_audioDeviceStatus;
- /* *
- * \brief This struct contains info about audio format conversion
- * /
- typedef struct {
- } kit_audioCVT; */
- /**
- * \brief This is the struct for an audio specification (something like a config but for a kit_audioDevice)
- * \verbatim
- For multi-channel audio, the current channel mapping is:
- 2: FL FR (stereo)
- 3: FL FR LFE (2.1 surround)
- 4: FL FR BL BR (quad)
- 5: FL FR LFE BL BR (4.1 surround)
- 6: FL FR FC LFE BL BR (5.1 surround)
- 7: FL FR FC LFE BC SL SR (6.1 surround)
- 8: FL FR FC LFE BL BR SL SR (7.1 surround)
- \endverbatim
- */
- typedef struct {
- kit_audioCallback callback; ///< \brief Called when the audio buffer needs to be refilled
- void* userdata; ///< \brief User-defined pointer passed to callback
- uint32_t frequency; ///< \brief Sample rate of audio in Hz
- uint32_t buffer_size; ///< \brief Audio buffer length in bytes (automatically calculated)
- uint32_t buffer_len; ///< \brief Audio buffer length in samples (must a be power of 2)
- uint16_t channels; ///< \brief Number of audio channels (1=mono, 2=stereo, etc.)
- kit_audioFormat format; ///< \brief Audio data format information
- } kit_audioSpec;
- /**
- * \brief This is the struct used for both WASAPI in/out devices. \n
- * Something of note is that most members of this struct have the "_" prefix,
- * which states that they are private and most likely should not be interacted
- * with or modified unless through kit_audio's interface.
- *
- * (While this struct uses an ID in the form of _magic, this type of ID
- * is not present in every kind of 'kit_moduleName' struct.)
- */
- typedef struct {
- union {
- char str[8]; ///< \brief String portion of ID ("audioDev\x00")
- uint64_t num; ///< \brief Number portion of ID (0x007665446F69647561)
- } /* --------------- */ _magic; ///< \brief Struct ID number; union of uint64_t and char[8]
- CRITICAL_SECTION _mutex; ///< \brief Used to stop callback from triggering if kit_audioLockDevice is called
- //CRITICAL_SECTION _callback_mutex; ///< \brief Used to lock access to _callback_thread specifically
- kit_audioSpec src; ///< \brief Audio format of input stream buffer
- //kit_audioCVT cvt; ///< \brief Contains format conversion info
- kit_audioSpec cvt; ///< \brief Audio format of ConVerTed (output) stream
- HANDLE _event; ///< \brief Event handle used in callback handling
- union {
- HANDLE _handle; ///< \brief Pointer handle of the device
- IAudioClient* _client;
- };
- //HANDLE _callback_thread; ///< \brief System callback thread, which kit_audioDeviceReset locks until completion for
- void* _stream; ///< \brief Points to the input stream data that is passed to _src.callback
- void* _buffer[2]; ///< \brief Output streams (while 0 is playing, 1 is being filled, and vice versa)
- float* volume; ///< \brief Volume multiplier for each audio channel (0.0 -> 1.0)
- kit_audioDevicePState _playing; ///< \brief play state of device (stopped, playing, paused)
- kit_audioDeviceStatus _status; ///< \brief current device status (active, disabled, etc.)
- uint32_t callback_timeout; ///< \brief Timeout for callback thread, in milliseconds (default of 10000)
- uint8_t _cvt_needed; ///< \brief A boolean of 'is format conversion needed?'
- uint8_t device_type; ///< \brief 1, 2 = WASAPI capture, WASAPI render
- uint8_t _which; ///< \brief Which output stream to fill with more audio data
- uint8_t _apply_volume; ///< \brief A boolean of whether to apply volume settings
- } kit_audioDevice;
- /* INLINES */
- /**
- * \brief Lock the audio callback of a given device from being called
- * (useful for manipulating the device struct)
- *
- * \param device A pointer to the device
- * \return A boolean of if the given pointer is an actual audio device
- *
- * \remark This activates a CRITICAL_SECTION mutex, so unlock as soon as possible
- */
- static inline int kit_audioLockDevice(kit_audioDevice* device){
- KIT_AUDIO_IFNOTDEVICE(device) return 0;
- else { EnterCriticalSection(&device->_mutex); return 1; }
- }
- /**
- * \brief Unlock the audio callback of a given device
- *
- * \param device A pointer to the device
- * \return A boolean of if the given pointer is an actual audio device
- */
- static inline int kit_audioUnlockDevice(kit_audioDevice* device){
- KIT_AUDIO_IFNOTDEVICE(device) return 0;
- else { LeaveCriticalSection(&device->_mutex); return 1; }
- }
- /* *
- * \brief Get the current playing state of a given device
- *
- * \param device A pointer to the device
- * \return A boolean value of whether the device is currently unpaused or not
- * /
- static inline int kit_audioIsPlaying(kit_audioDevice* device){
- if(!kit_audioLockDevice(device)) return 0;
- int isPlaying=device->_playing;
- KIT_AUDIO_UNLOCK(device);
- return isPlaying;
- }*/
- /**
- * \brief Set a device channel's volume multiplier (values should be 0.0 -> 1.0)
- *
- * \param device A pointer to the device
- * \param volume New volume multiplier for selected channel
- * \param channel Channel to assign new multiplier to
- */
- static inline void kit_audioSetVolume(kit_audioDevice* device, float volume,uint32_t channel){
- if(!kit_audioLockDevice(device)) return;
- if(channel>=device->cvt.channels) return;
- device->volume[channel]=volume;
- KIT_AUDIO_UNLOCK(device);
- }
- /**
- * \brief Set Stereo volume multiplier for device (values should be 0.0 -> 1.0)
- *
- * \param device A pointer to the device
- * \param volL New volume multiplier for left channel (or just channel 0 if channels is equal to 1)
- * \param volR New volume multiplier for right channel (ignored if channels is <2)
- */
- static inline void kit_audioSetVolumeLR(kit_audioDevice* device, float volL,float volR){
- if(!kit_audioLockDevice(device)) return;
- device->volume[0]=volL;
- if(device->cvt.channels>1) device->volume[1]=volR;
- KIT_AUDIO_UNLOCK(device);
- }
- /* COM-RELATED FUNCTIONS */
- //extern kit_audioSpec _kit_audioWaveFormatToSpec(void* format);
- //extern WAVEFORMATEXTENSIBLE _kit_audioSpecToWaveFormat(kit_audioSpec* spec, int* returnStatus_p);
- //extern int _kit_audioQueryDevice(unsigned int index, int isCapture); //assumes globals are already locked
- //extern int _kit_audioQueryDeviceProps(unsigned int index, int isCapture); //assumes globals are already locked
- /**
- * \brief Query and update render/capture audio device list, given a device status
- *
- * \param isCapture A boolean of whether to update the render or capture device list
- * \param status A kit_audioDeviceStatus enum value used to filter
- * which devices are included in the new list
- * \return A return status code
- * \verbatim Possible return status codes:
- \endverbatim
- */
- extern int kit_audioQueryDevices(int isCapture, kit_audioDeviceStatus status);
- /**
- * \brief Get number of devices in current device list
- *
- * \param isCapture A boolean of whether to reference the render or capture device list
- * \return The current number of audio endpoint devices
- *
- * \remark This device list can be updated with a call to kit_audioQueryDevices
- * \sa kit_audioQueryDevices
- */
- extern unsigned int kit_audioGetNumDevices(int isCapture);
- /**
- * \brief Get the 'friendly name' of a given audio device
- *
- * \param index The index of an audio device in the current device list
- * \param isCapture A boolean of whether to reference the render or capture audio device list
- * \param mode Which name property to pull from; 0 -> 2
- * (PKEY_Device_FriendlyName, PKEY_Device_DeviceDesc, PKEY_DeviceInterface_FriendlyName)
- * \param returnStatus_p A pointer to an int to be filled with the return status code (can be NULL)
- * \verbatim Possible return status codes:
- \endverbatim
- * \return A wide string pointer to the device name, or NULL on error
- *
- * \remark Do not attempt to free the returned pointer yourself. This string is managed internally
- */
- extern wchar_t* kit_audioGetDeviceName(unsigned int index, int isCapture,
- int mode, int* returnStatus_p);
- /**
- * \brief Get the device ID of a given device, as a wide string
- *
- * \param index The index of an audio device in the current device list
- * \param isCapture A boolean of whether to reference the render or capture audio device list
- * \param returnStatus_p A pointer to an int to be filled with the return status code (can be NULL)
- * \verbatim Possible return status codes
- \endverbatim
- * \return A wide string pointer to the device ID, or NULL on error
- *
- * \remark Do not attempt to free the returned pointer yourself. This string is managed internally
- */
- extern wchar_t* kit_audioGetDeviceID(unsigned int index, int isCapture, int* returnStatus_p);
- /**
- * \brief Get current connection status of an audio device
- *
- * \param index The index of an audio device in the current device list
- * \param isCapture A boolean of whether to reference the render or capture audio device list
- * \return A kit_audioDeviceStatus enum value (ADStateInvalid if index is out of range)
- */
- extern kit_audioDeviceStatus kit_audioGetDeviceStatus(unsigned int index, int isCapture, int* returnStatus_p);
- /**
- * \brief Get the audio format specification of a given audio device
- *
- * \param index The index of an audio device in the current device list
- * \param isCapture A boolean of whether to reference the render or capture audio device list
- * \param spec A pointer to a kit_audioSpec struct to be filled in with the device's specification
- * \return A return status code
- * \verbatim Possible return status codes:
- \endverbatim
- */
- extern int kit_audioGetDeviceSpec(unsigned int index, int isCapture, kit_audioSpec* spec);
- extern int kit_audioIsSpecSupported(unsigned int index, int isCapture,
- kit_audioSpec* spec, kit_audioSpec* closestMatch);
- extern unsigned int kit_audioGetDeviceIndexFromID(wchar_t* deviceID, int isCapture, int* returnStatus_p);
- /* kit_audioDevice-RELATED FUNCTIONS */
- //TBD
- extern int kit_audioDevicePlay(kit_audioDevice* device, kit_audioDevicePState playState);
- /**
- * \brief Get the current play state of a given device
- *
- * \param device A pointer to the device
- * \return A kit_audioDevicePState enum value of the device's play state
- */
- extern kit_audioDevicePState kit_audioGetDevicePlayState(kit_audioDevice* device);
- /* (UN)INIT FUNCTIONS */
- /**
- * \brief Initialize kit_w32_audio, while optionally initializing COM, too. \n
- * This function should ideally be called only once, and in the process's main thread. \n
- * (Bad things will happen if kit_audio functions are called before initializing.)
- *
- * \param initCOM A boolean of whether to initialize COM within the function
- * \return A return status code
- * \verbatim Possible return status codes:
- 0: Success; no error
- 1: Init has already been called (this check is prone to race conditions, and is therefore unreliable)
- 2: Failed to create device enumerator object
- 3: Failed to enumerate input (capture) audio endpoints
- 4: Failed to enumerate output (render) audio endpoints
- \endverbatim
- *
- * \remark If initCOM=0, "CoInitialize(NULL,COINIT_MULTITHREADED)"
- * must be called prior to calling kit_audioInit.
- * \sa kit_audioQuit
- */
- extern int kit_audioInit(int initCOM);
- /**
- * \brief Uninitialize kit_w32_audio, freeing and releasing remaining interfaces in the process. \n
- * It is recommended to call kit_audioQuit only once, and in the same thread as the prior call to kit_audioInit.
- *
- * \param uninitCOM A boolean of whether to uninitialize COM after freeing/releasing everything
- * \return 0 on success, and 1 if kit_w32_audio is already uninitialized
- *
- * \remark Due to chronic and unexplainable problems with CoUninitialize, setting uninitCOM to 0 is recommended
- * \sa kit_audioInit
- */
- extern int kit_audioQuit(int uninitCOM);
- //TBD
- extern kit_audioDevice* kit_audioDeviceOpen(kit_audioSpec* spec, unsigned int index,
- int isCapture, int* returnStatus_p);
- //TBD
- extern int kit_audioDeviceClose(kit_audioDevice** device_p);
- # endif //_KIT_AUDIO
- #endif //_KIT_W32_AUDIO_H
- //move this back to kit_w32_audio.c once i'm done testing with it
- typedef struct {
- union {
- WAVEFORMATEX device_format;
- WAVEFORMATEXTENSIBLE device_format_ext;
- };
- IMMDeviceCollection* devices;
- IMMDevice* device;
- IPropertyStore* props;
- IAudioClient* client;
- LPWSTR device_id; //aka wide char* (wchar_t*)!
- LPWSTR device_iname;
- LPWSTR device_name;
- LPWSTR device_desc;
- size_t device_iname_len;
- size_t device_name_len;
- size_t device_desc_len;
- UINT device_index;
- EDataFlow data_flow; //read-only; set by init
- int data_type; //1,2,3 = pcm&format, float&format_ext, pcm&format_ext
- int event_driven_support; //might not be used
- } _kit_audioGlobalsDevInfo;
- struct _kit_audioGlobals_s {
- CRITICAL_SECTION lock;
- IMMDeviceEnumerator* enumerator;
- kit_audioDevice** kit_devices;
- UINT kit_devices_len;
- _kit_audioGlobalsDevInfo i;
- _kit_audioGlobalsDevInfo o;
- int init;
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement