158 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			158 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | #include <kinc/audio2/audio.h>
 | ||
|  | #include <kinc/system.h>
 | ||
|  | 
 | ||
|  | #include <kinc/backend/SystemMicrosoft.h>
 | ||
|  | #include <kinc/backend/Windows.h>
 | ||
|  | 
 | ||
|  | #include <dsound.h>
 | ||
|  | 
 | ||
|  | #include <assert.h>
 | ||
|  | 
 | ||
|  | namespace { | ||
|  | 	IDirectSound8 *dsound = nullptr; | ||
|  | 	IDirectSoundBuffer *dbuffer = nullptr; | ||
|  | 	const DWORD dsize = 50 * 1024; | ||
|  | 	const int samplesPerSecond = 44100; | ||
|  | 	const int bitsPerSample = 16; | ||
|  | 
 | ||
|  | 	DWORD lastPlayPosition = 0; | ||
|  | 	bool secondHalfFilled = false; | ||
|  | 
 | ||
|  | 	const int gap = 10 * 1024; | ||
|  | 	DWORD writePos = gap; | ||
|  | 
 | ||
|  | 	kinc_a2_buffer_t a2_buffer; | ||
|  | } | ||
|  | 
 | ||
|  | static bool initialized = false; | ||
|  | 
 | ||
|  | void kinc_a2_init() { | ||
|  | 	if (initialized) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	kinc_a2_internal_init(); | ||
|  | 	initialized = true; | ||
|  | 
 | ||
|  | 	a2_buffer.read_location = 0; | ||
|  | 	a2_buffer.write_location = 0; | ||
|  | 	a2_buffer.data_size = 128 * 1024; | ||
|  | 	a2_buffer.channel_count = 2; | ||
|  | 	a2_buffer.channels[0] = new float[a2_buffer.data_size]; | ||
|  | 	a2_buffer.channels[1] = new float[a2_buffer.data_size]; | ||
|  | 
 | ||
|  | 	kinc_microsoft_affirm(DirectSoundCreate8(nullptr, &dsound, nullptr)); | ||
|  | 	// TODO (DK) only for the main window?
 | ||
|  | 	kinc_microsoft_affirm(dsound->SetCooperativeLevel(kinc_windows_window_handle(0), DSSCL_PRIORITY)); | ||
|  | 
 | ||
|  | 	WAVEFORMATEX waveFormat; | ||
|  | 	waveFormat.wFormatTag = WAVE_FORMAT_PCM; | ||
|  | 	waveFormat.nSamplesPerSec = samplesPerSecond; | ||
|  | 	waveFormat.wBitsPerSample = bitsPerSample; | ||
|  | 	waveFormat.nChannels = 2; | ||
|  | 	waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels; | ||
|  | 	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; | ||
|  | 	waveFormat.cbSize = 0; | ||
|  | 
 | ||
|  | 	DSBUFFERDESC bufferDesc; | ||
|  | 	bufferDesc.dwSize = sizeof(DSBUFFERDESC); | ||
|  | 	bufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS; | ||
|  | 	bufferDesc.dwBufferBytes = dsize; | ||
|  | 	bufferDesc.dwReserved = 0; | ||
|  | 	bufferDesc.lpwfxFormat = &waveFormat; | ||
|  | 	bufferDesc.guid3DAlgorithm = GUID_NULL; | ||
|  | 
 | ||
|  | 	kinc_microsoft_affirm(dsound->CreateSoundBuffer(&bufferDesc, &dbuffer, nullptr)); | ||
|  | 
 | ||
|  | 	DWORD size1; | ||
|  | 	uint8_t *buffer1 = NULL; | ||
|  | 	kinc_microsoft_affirm(dbuffer->Lock(writePos, gap, (void **)&buffer1, &size1, nullptr, nullptr, 0)); | ||
|  | 	assert(buffer1 != NULL); | ||
|  | 	for (DWORD i = 0; i < size1; ++i) { | ||
|  | 		buffer1[i] = 0; | ||
|  | 	} | ||
|  | 	kinc_microsoft_affirm(dbuffer->Unlock(buffer1, size1, nullptr, 0)); | ||
|  | 
 | ||
|  | 	kinc_microsoft_affirm(dbuffer->Play(0, 0, DSBPLAY_LOOPING)); | ||
|  | } | ||
|  | 
 | ||
|  | uint32_t kinc_a2_samples_per_second(void) { | ||
|  | 	return samplesPerSecond; | ||
|  | } | ||
|  | 
 | ||
|  | namespace { | ||
|  | 	void copySample(uint8_t *buffer, DWORD &index, bool left) { | ||
|  | 		float value = *(float *)&a2_buffer.channels[left ? 0 : 1][a2_buffer.read_location]; | ||
|  | 		if (!left) { | ||
|  | 			a2_buffer.read_location += 1; | ||
|  | 			if (a2_buffer.read_location >= a2_buffer.data_size) { | ||
|  | 				a2_buffer.read_location = 0; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		*(int16_t *)&buffer[index] = static_cast<int16_t>(value * 32767); | ||
|  | 		index += 2; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_a2_update() { | ||
|  | 	DWORD playPosition; | ||
|  | 	DWORD writePosition; | ||
|  | 	kinc_microsoft_affirm(dbuffer->GetCurrentPosition(&playPosition, &writePosition)); | ||
|  | 
 | ||
|  | 	int dif; | ||
|  | 	if (writePos >= writePosition) { | ||
|  | 		dif = writePos - writePosition; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		dif = dsize - writePosition + writePos; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (dif < gap) { | ||
|  | 		return; | ||
|  | 	} | ||
|  | 	if (writePos + gap >= dsize) { | ||
|  | 		if (playPosition >= writePos || playPosition <= gap) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 		if (writePosition >= writePos || writePosition <= gap) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		if (playPosition >= writePos && playPosition <= writePos + gap) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 		if (writePosition >= writePos && writePosition <= writePos + gap) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	kinc_a2_internal_callback(&a2_buffer, (uint32_t)(gap / 4)); | ||
|  | 
 | ||
|  | 	DWORD size1; | ||
|  | 	uint8_t *buffer1; | ||
|  | 	kinc_microsoft_affirm(dbuffer->Lock(writePos, gap, (void **)&buffer1, &size1, NULL, NULL, 0)); | ||
|  | 
 | ||
|  | 	for (DWORD i = 0; i < size1 - (bitsPerSample / 8 - 1);) { | ||
|  | 		copySample(buffer1, i, ((writePos + i) / 2) % 2 == 0); | ||
|  | 	} | ||
|  | 	writePos += size1; | ||
|  | 
 | ||
|  | 	kinc_microsoft_affirm(dbuffer->Unlock(buffer1, size1, NULL, 0)); | ||
|  | 
 | ||
|  | 	if (writePos >= dsize) { | ||
|  | 		writePos -= dsize; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_a2_shutdown() { | ||
|  | 	if (dbuffer != nullptr) { | ||
|  | 		dbuffer->Release(); | ||
|  | 		dbuffer = nullptr; | ||
|  | 	} | ||
|  | 	if (dsound != nullptr) { | ||
|  | 		dsound->Release(); | ||
|  | 		dsound = nullptr; | ||
|  | 	} | ||
|  | } |