Update Files
This commit is contained in:
		
							
								
								
									
										296
									
								
								Kinc/Backends/Audio2/WASAPI/Sources/kinc/backend/wasapi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								Kinc/Backends/Audio2/WASAPI/Sources/kinc/backend/wasapi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | ||||
| #include <kinc/audio2/audio.h> | ||||
|  | ||||
| #include <kinc/backend/SystemMicrosoft.h> | ||||
|  | ||||
| #include <kinc/error.h> | ||||
| #include <kinc/log.h> | ||||
|  | ||||
| // Windows 7 | ||||
| #define WINVER 0x0601 | ||||
| #define _WIN32_WINNT 0x0601 | ||||
|  | ||||
| #define NOATOM | ||||
| #define NOCLIPBOARD | ||||
| #define NOCOLOR | ||||
| #define NOCOMM | ||||
| #define NOCTLMGR | ||||
| #define NODEFERWINDOWPOS | ||||
| #define NODRAWTEXT | ||||
| //#define NOGDI | ||||
| #define NOGDICAPMASKS | ||||
| #define NOHELP | ||||
| #define NOICONS | ||||
| #define NOKANJI | ||||
| #define NOKEYSTATES | ||||
| #define NOMB | ||||
| #define NOMCX | ||||
| #define NOMEMMGR | ||||
| #define NOMENUS | ||||
| #define NOMETAFILE | ||||
| #define NOMINMAX | ||||
| //#define NOMSG | ||||
| #define NONLS | ||||
| #define NOOPENFILE | ||||
| #define NOPROFILER | ||||
| #define NORASTEROPS | ||||
| #define NOSCROLL | ||||
| #define NOSERVICE | ||||
| #define NOSHOWWINDOW | ||||
| #define NOSOUND | ||||
| #define NOSYSCOMMANDS | ||||
| #define NOSYSMETRICS | ||||
| #define NOTEXTMETRIC | ||||
| //#define NOUSER | ||||
| #define NOVIRTUALKEYCODES | ||||
| #define NOWH | ||||
| #define NOWINMESSAGES | ||||
| #define NOWINOFFSETS | ||||
| #define NOWINSTYLES | ||||
| #define WIN32_LEAN_AND_MEAN | ||||
|  | ||||
| #include <initguid.h> | ||||
|  | ||||
| #include <AudioClient.h> | ||||
| #include <mmdeviceapi.h> | ||||
|  | ||||
| // MIDL_INTERFACE("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") | ||||
| DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2); | ||||
| // MIDL_INTERFACE("F294ACFC-3146-4483-A7BF-ADDCA7C260E2") | ||||
| DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2); | ||||
| // MIDL_INTERFACE("A95664D2-9614-4F35-A746-DE8DB63617E6") | ||||
| DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); | ||||
| // DECLSPEC_UUID("BCDE0395-E52F-467C-8E3D-C4579291692E") | ||||
| DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); | ||||
|  | ||||
| // based on the implementation in soloud and Microsoft sample code | ||||
| static volatile void (*a2_callback)(kinc_a2_buffer_t *buffer, int samples) = NULL; | ||||
| static kinc_a2_buffer_t a2_buffer; | ||||
|  | ||||
| static IMMDeviceEnumerator *deviceEnumerator; | ||||
| static IMMDevice *device; | ||||
| static IAudioClient *audioClient = NULL; | ||||
| static IAudioRenderClient *renderClient = NULL; | ||||
| static HANDLE bufferEndEvent = 0; | ||||
| static HANDLE audioProcessingDoneEvent; | ||||
| static UINT32 bufferFrames; | ||||
| static WAVEFORMATEX requestedFormat; | ||||
| static WAVEFORMATEX *closestFormat; | ||||
| static WAVEFORMATEX *format; | ||||
|  | ||||
| static bool initDefaultDevice() { | ||||
| 	if (renderClient != NULL) { | ||||
| 		renderClient->lpVtbl->Release(renderClient); | ||||
| 		renderClient = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (audioClient != NULL) { | ||||
| 		audioClient->lpVtbl->Release(audioClient); | ||||
| 		audioClient = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (bufferEndEvent != 0) { | ||||
| 		CloseHandle(bufferEndEvent); | ||||
| 		bufferEndEvent = 0; | ||||
| 	} | ||||
|  | ||||
| 	kinc_log(KINC_LOG_LEVEL_INFO, "Initializing a new default audio device."); | ||||
|  | ||||
| 	HRESULT hr = deviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(deviceEnumerator, eRender, eConsole, &device); | ||||
| 	if (hr == S_OK) { | ||||
| 		hr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, 0, (void **)&audioClient); | ||||
| 	} | ||||
|  | ||||
| 	if (hr == S_OK) { | ||||
| 		const int sampleRate = 48000; | ||||
|  | ||||
| 		format = &requestedFormat; | ||||
| 		memset(&requestedFormat, 0, sizeof(WAVEFORMATEX)); | ||||
| 		requestedFormat.nChannels = 2; | ||||
| 		requestedFormat.nSamplesPerSec = sampleRate; | ||||
| 		requestedFormat.wFormatTag = WAVE_FORMAT_PCM; | ||||
| 		requestedFormat.wBitsPerSample = sizeof(short) * 8; | ||||
| 		requestedFormat.nBlockAlign = (requestedFormat.nChannels * requestedFormat.wBitsPerSample) / 8; | ||||
| 		requestedFormat.nAvgBytesPerSec = requestedFormat.nSamplesPerSec * requestedFormat.nBlockAlign; | ||||
| 		requestedFormat.cbSize = 0; | ||||
|  | ||||
| 		HRESULT supported = audioClient->lpVtbl->IsFormatSupported(audioClient, AUDCLNT_SHAREMODE_SHARED, format, &closestFormat); | ||||
| 		if (supported == S_FALSE) { | ||||
| 			kinc_log(KINC_LOG_LEVEL_WARNING, "Falling back to the system's preferred WASAPI mix format.", supported); | ||||
| 			if (closestFormat != NULL) { | ||||
| 				format = closestFormat; | ||||
| 			} | ||||
| 			else { | ||||
| 				audioClient->lpVtbl->GetMixFormat(audioClient, &format); | ||||
| 			} | ||||
| 		} | ||||
| 		HRESULT result = | ||||
| 		    audioClient->lpVtbl->Initialize(audioClient, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 40 * 1000 * 10, 0, format, 0); | ||||
| 		if (result != S_OK) { | ||||
| 			kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize WASAPI audio, going silent (error code 0x%x).", result); | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		kinc_a2_samples_per_second = format->nSamplesPerSec; | ||||
| 		a2_buffer.format.samples_per_second = kinc_a2_samples_per_second; | ||||
| 		a2_buffer.format.bits_per_sample = 16; | ||||
| 		a2_buffer.format.channels = 2; | ||||
|  | ||||
| 		bufferFrames = 0; | ||||
| 		kinc_microsoft_affirm(audioClient->lpVtbl->GetBufferSize(audioClient, &bufferFrames)); | ||||
| 		kinc_microsoft_affirm(audioClient->lpVtbl->GetService(audioClient, &IID_IAudioRenderClient, (void **)&renderClient)); | ||||
|  | ||||
| 		bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0); | ||||
| 		kinc_affirm(bufferEndEvent != 0); | ||||
|  | ||||
| 		kinc_microsoft_affirm(audioClient->lpVtbl->SetEventHandle(audioClient, bufferEndEvent)); | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| 	else { | ||||
| 		kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize WASAPI audio."); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void copyS16Sample(int16_t *buffer) { | ||||
| 	float value = *(float *)&a2_buffer.data[a2_buffer.read_location]; | ||||
| 	a2_buffer.read_location += 4; | ||||
| 	if (a2_buffer.read_location >= a2_buffer.data_size) | ||||
| 		a2_buffer.read_location = 0; | ||||
| 	*buffer = (int16_t)(value * 32767); | ||||
| } | ||||
|  | ||||
| static void copyFloatSample(float *buffer) { | ||||
| 	float value = *(float *)&a2_buffer.data[a2_buffer.read_location]; | ||||
| 	a2_buffer.read_location += 4; | ||||
| 	if (a2_buffer.read_location >= a2_buffer.data_size) | ||||
| 		a2_buffer.read_location = 0; | ||||
| 	*buffer = value; | ||||
| } | ||||
|  | ||||
| static void submitEmptyBuffer(unsigned frames) { | ||||
| 	BYTE *buffer = NULL; | ||||
| 	HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer); | ||||
| 	if (FAILED(result)) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	memset(buffer, 0, frames * format->nBlockAlign); | ||||
|  | ||||
| 	result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0); | ||||
| } | ||||
|  | ||||
| static void submitBuffer(unsigned frames) { | ||||
| 	BYTE *buffer = NULL; | ||||
| 	HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer); | ||||
| 	if (FAILED(result)) { | ||||
| 		if (result == AUDCLNT_E_DEVICE_INVALIDATED) { | ||||
| 			initDefaultDevice(); | ||||
| 			submitEmptyBuffer(bufferFrames); | ||||
| 			audioClient->lpVtbl->Start(audioClient); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (a2_callback != NULL) { | ||||
| 		a2_callback(&a2_buffer, frames * 2); | ||||
| 		memset(buffer, 0, frames * format->nBlockAlign); | ||||
| 		if (format->wFormatTag == WAVE_FORMAT_PCM) { | ||||
| 			for (UINT32 i = 0; i < frames; ++i) { | ||||
| 				copyS16Sample((int16_t *)&buffer[i * format->nBlockAlign]); | ||||
| 				copyS16Sample((int16_t *)&buffer[i * format->nBlockAlign + 2]); | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			for (UINT32 i = 0; i < frames; ++i) { | ||||
| 				copyFloatSample((float *)&buffer[i * format->nBlockAlign]); | ||||
| 				copyFloatSample((float *)&buffer[i * format->nBlockAlign + 4]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		memset(buffer, 0, frames * format->nBlockAlign); | ||||
| 	} | ||||
|  | ||||
| 	result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0); | ||||
| 	if (FAILED(result)) { | ||||
| 		if (result == AUDCLNT_E_DEVICE_INVALIDATED) { | ||||
| 			initDefaultDevice(); | ||||
| 			submitEmptyBuffer(bufferFrames); | ||||
| 			audioClient->lpVtbl->Start(audioClient); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static DWORD WINAPI audioThread(LPVOID ignored) { | ||||
| 	submitBuffer(bufferFrames); | ||||
| 	audioClient->lpVtbl->Start(audioClient); | ||||
| 	while (WAIT_OBJECT_0 != WaitForSingleObject(audioProcessingDoneEvent, 0)) { | ||||
| 		WaitForSingleObject(bufferEndEvent, INFINITE); | ||||
| 		UINT32 padding = 0; | ||||
| 		HRESULT result = audioClient->lpVtbl->GetCurrentPadding(audioClient, &padding); | ||||
| 		if (FAILED(result)) { | ||||
| 			if (result == AUDCLNT_E_DEVICE_INVALIDATED) { | ||||
| 				initDefaultDevice(); | ||||
| 				submitEmptyBuffer(bufferFrames); | ||||
| 				audioClient->lpVtbl->Start(audioClient); | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		UINT32 frames = bufferFrames - padding; | ||||
| 		submitBuffer(frames); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void kinc_windows_co_initialize(void); | ||||
|  | ||||
| static bool initialized = false; | ||||
|  | ||||
| void kinc_a2_init() { | ||||
| 	if (initialized) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	initialized = true; | ||||
|  | ||||
| 	a2_buffer.read_location = 0; | ||||
| 	a2_buffer.write_location = 0; | ||||
| 	a2_buffer.data_size = 128 * 1024; | ||||
| 	a2_buffer.data = (uint8_t *)malloc(a2_buffer.data_size); | ||||
|  | ||||
| 	audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0); | ||||
| 	kinc_affirm(audioProcessingDoneEvent != 0); | ||||
|  | ||||
| 	kinc_windows_co_initialize(); | ||||
| 	kinc_microsoft_affirm(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&deviceEnumerator)); | ||||
|  | ||||
| 	if (initDefaultDevice()) { | ||||
| 		CreateThread(0, 65536, audioThread, NULL, 0, 0); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void kinc_a2_set_callback(void (*kinc_a2_audio_callback)(kinc_a2_buffer_t *buffer, int samples)) { | ||||
| 	a2_callback = kinc_a2_audio_callback; | ||||
| } | ||||
|  | ||||
| void kinc_a2_update() {} | ||||
|  | ||||
| #define SAFE_RELEASE(punk)                                                                                                                                     \ | ||||
| 	if ((punk) != NULL) {                                                                                                                                      \ | ||||
| 		(punk)->Release();                                                                                                                                     \ | ||||
| 		(punk) = NULL;                                                                                                                                         \ | ||||
| 	} | ||||
|  | ||||
| void kinc_a2_shutdown() { | ||||
| 	// Wait for last data in buffer to play before stopping. | ||||
| 	// Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2)); | ||||
|  | ||||
| 	//	affirm(pAudioClient->Stop());  // Stop playing. | ||||
|  | ||||
| 	//	CoTaskMemFree(pwfx); | ||||
| 	//	SAFE_RELEASE(pEnumerator) | ||||
| 	//	SAFE_RELEASE(pDevice) | ||||
| 	//	SAFE_RELEASE(pAudioClient) | ||||
| 	//	SAFE_RELEASE(pRenderClient) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user