C++ DirectSound Probleme

Affe007

Lt. Commander
Registriert
März 2004
Beiträge
1.462
Moinsen ich hab einige Probleme mit C++ und DirectSound. Was will ich überhaupt machen?

Ich habe vor ein Prog zu schreiben, was den Mic-Eingang aufnimmt, zwischenspeichert und dann wieder ausgibt. (Später einmal soll noch eine FFT und eine Ausgabe hinzukommen aber alles zu seiner Zeit :rolleyes:)

Da ist jetzt nicht sooo der C++ Held bin und mit DS noch nie in Berührung gekommen bin, haben sich da einige Probleme ergeben :cool_alt:

Was hab ich hin bekommen:
- Sound Device (Capture Render) erstellt
- Buffer konfiguriert und erstellt
- das Häppchenweise capturen sollte auch funktionieren

Aber jetzt zu meinen Probs, ich benutze z.Z. die Buffer so, dass ich immer das auslese/schreibe was gerade so neu drin ist (ohne Benachrichtigungen). Zwar hab ich gerafft wie ich die Notifications erstelle (dank Code Beispiel aus der Hilfe) allerdings hab ich kein Plan wie ich auf die Events reagieren kann. (Threads erstellen, WaitForMultipleEvents usw. Buch mit 3263 Siegeln)

Wie den auch sei, das Häppchenweise auslesen des Capture Buffers funktioniert (glaub ich) aber das schreiben des Render Buffers leider nicht :( Ich hab auh langsam keine Ideen mehr.

So vll. kann mir einer zeigen wie ich das Prog so hinbiege, dass es einen Stream aufnimmt, kurz zwischenspeichert und dann wieder den Stream ausgibt.

Was noch besser wäre, wenn mir einer die Notificationen erklären könnte :p

So hier das Unglück, ich hoffe die ganzen Programmierer drehen jetzt nicht gleich durch, wie scheisse das doch alles is ;)
Code:
#include <dsound.h>
#include <windows.h>
#include <iostream>
using namespace std;


// Handle des aktiven Consolen Fensters
HWND GetConsoleHwnd(void){        
	LPCWSTR newtitle = (LPCWSTR) L"SDR-Test";
    SetConsoleTitle(newtitle);
    Sleep(40);
    return(FindWindow(NULL, newtitle));
}

// setzen der WaveFormatEx Eigenschaften
void SetWaveFormatEx(WAVEFORMATEX *wfx){
	memset(wfx, 0, sizeof(WAVEFORMATEX)); 
	wfx->wFormatTag = WAVE_FORMAT_PCM;
	wfx->nChannels = 2;
	wfx->wBitsPerSample = 16;
	wfx->nSamplesPerSec = 44100; 
	wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
	wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
}

// setzen der Capture und Render Buffer Eigenschaften
void SetCRBufferDescribtion(DSCBUFFERDESC *dscbdesc, DSBUFFERDESC *dsbdesc, WAVEFORMATEX *wfx){
	// Capture
	memset (dscbdesc, 0, sizeof(DSCBUFFERDESC));
	dscbdesc->dwSize = sizeof(DSCBUFFERDESC);
	dscbdesc->dwFlags = 0;
	dscbdesc->dwBufferBytes = wfx->nSamplesPerSec;
	dscbdesc->lpwfxFormat = wfx;
	dscbdesc->dwFXCount = 0;
	dscbdesc->lpDSCFXDesc = NULL;

	// Render
	memset (dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc->dwSize = sizeof(DSBUFFERDESC);
	dsbdesc->dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
	dsbdesc->dwBufferBytes = wfx->nSamplesPerSec;
	dsbdesc->lpwfxFormat = wfx;
}


int main(int argc, char* argv[]){
	HWND CMDWHandle; // CMD Fenster Handl
	HRESULT hr; // Fehlercode

	LPDIRECTSOUNDFULLDUPLEX lpdsFullDuplex; // DirectSound Full Duplex Interface
	LPDIRECTSOUNDCAPTUREBUFFER8 lpdsCaptureBuffer; // DirectSound Capture Buffer Interface
	LPDIRECTSOUNDBUFFER8 lpdsRenderBuffer; // DirectSound Render Buffer Interface

	WAVEFORMATEX WaveFormatEx; // Format der Wave Audio Daten

	DSCBUFFERDESC DSCBufferDesc; // Eigenschaften des Capture Buffers
	DSBUFFERDESC DSBufferDesc; // Eigenschaften des Render Buffers
	
	// Handle des aktiven Consolen Fensters
	CMDWHandle = GetConsoleHwnd(); 

	// setzen der WaveFormatEx Eigenschaften
	SetWaveFormatEx(&WaveFormatEx);
	
	// setzen der Capture und Render Buffer Eigenschaften
	SetCRBufferDescribtion(&DSCBufferDesc, &DSBufferDesc, &WaveFormatEx);

	// erstellen des DirectSound Full Duplex Device und der Buffer
	hr = DirectSoundFullDuplexCreate8(
		NULL, 
		NULL,
		&DSCBufferDesc,
		&DSBufferDesc,
		CMDWHandle,
		DSSCL_PRIORITY,
		&lpdsFullDuplex,
		&lpdsCaptureBuffer,
		&lpdsRenderBuffer,
		NULL);
	if(FAILED(hr)){
		cout << "Full Duplex Device " << hr << endl;
	}
	// Capture Buffer starten
	hr = lpdsCaptureBuffer->Start(DSCBSTART_LOOPING);
	if(FAILED(hr)){
		cout << "Aufnahme Start " << hr << endl;
	}
	// Play Cursor auf 0 setzten
	hr = lpdsRenderBuffer->SetCurrentPosition(0);
	if(FAILED(hr)){
		cout << "Wiedergabe Position " << hr << endl;
	}
	// Play
	hr = lpdsRenderBuffer->Play(0, 0, 0);
	if(FAILED(hr)){
		cout << "Wiedergabe " << hr << endl;
	}
    
	LPVOID lpvRead1, lpvRead2, lpvWrite1, lpvWrite2;
	DWORD dwLength1 = 0, dwLength2 = 0;
	DWORD dwReadCursor = 0, dwLockSize = 0, dwLastReadCursor = 0; 
	DWORD dwWriteCursor, dwPlayCursor, dwOffset = 0;
	DWORD pbData[44100];
	
	while(1){
		Sleep(45);  // warten bis einige Daten geladen sind

		/********************************/
		/*			Capture				*/
		/********************************/

		// Poition des Read Cursors abfragen
		hr = lpdsCaptureBuffer->GetCurrentPosition(NULL, &dwReadCursor);
		if(FAILED(hr)){
			cout << "Capture Buffer Position " << hr << endl;
		}
		
		// Grösse des Locks bestimmen
		if(dwReadCursor < dwLastReadCursor){
			dwLockSize = dwLastReadCursor - dwReadCursor;
		}
		else{
			dwLockSize = dwReadCursor - dwLastReadCursor;
		}
		
		// Capture Buffer sperren
		hr = lpdsCaptureBuffer->Lock(
			dwLastReadCursor,				// Offset at which to start lock.
			dwLockSize,						// Size of lock.
			&lpvRead1,						// Gets address of first part of lock.
			&dwLength1,						// Gets size of first part of lock.
			&lpvRead2,						// Address of wraparound not needed. 
			&dwLength2,						// Size of wraparound not needed.
			0);								// Flag.
		
		// gesperrter Teil des Capture Buffer wird gespeichert
		if(SUCCEEDED(hr)){		
			memcpy(pbData, lpvRead1, dwLength1);
			if(lpvRead2 != NULL){
				memcpy(pbData + dwLength1, lpvRead2, dwLength2);
			}
			// Capture Buffer freigeben
			lpdsCaptureBuffer->Unlock(
				lpvRead1,	 // Address of lock start.
				dwLength1,   // Size of lock.
				lpvRead2,    // No wraparound portion.
				dwLength2);  // No wraparound size.

			dwLastReadCursor = dwReadCursor; // ReadCursor Position speichern
		}
		else{
			cout << "Capture Buffer Lock " << hr << endl; 
		}

		/********************************/
		/*			Render				*/
		/********************************/
	
		// Positionen der Capture und Write Cursor abfragen
		hr = lpdsRenderBuffer->GetCurrentPosition(&dwPlayCursor,&dwWriteCursor);
		if(FAILED(hr)){
			cout << "Render Buffer Position " << hr << endl;
		}

		hr = lpdsRenderBuffer->Lock(
			dwOffset,					// Offset at which to start lock.
			dwLockSize,					// Size of lock.
			&lpvWrite1,					// Gets address of first part of lock.
			&dwLength1,					// Gets size of first part of lock.
			&lpvWrite2,					// Address of wraparound not needed. 
			&dwLength2,					// Size of wraparound not needed.
			0);							// Flag.

		// schreiben in den gesperrten Teil des Render Buffers
		if (SUCCEEDED(hr)){
			memcpy(lpvWrite1, pbData, dwLength1);
			if(lpvWrite2 != NULL){
				memcpy(lpvWrite2, pbData + dwLength1, dwLength2);
			}
			// Render Buffer freigeben
			lpdsRenderBuffer->Unlock(
				lpvWrite1,	 // Address of lock start.
				dwLength1,   // Size of lock.
				lpvWrite2,   // No wraparound portion.
				dwLength2);  // No wraparound size.
			
			// Offset ermitteln ???
			if((dwOffset + dwLockSize) > 40099){
				dwOffset = dwOffset + dwLockSize - 40999;
			}
			else{
				dwOffset = dwOffset + dwLockSize;
			}
		}
		else{
			cout << "Render Buffer Lock " << hex << hr << endl; 
		}
	}

	system("PAUSE");
	lpdsRenderBuffer->Stop();
	lpdsRenderBuffer->Release();
	lpdsCaptureBuffer->Stop();
	lpdsCaptureBuffer->Release();
	lpdsFullDuplex->Release();
	return(0);
}
 
Zuletzt bearbeitet:
Las doch einfach debugger laufen... soviel code wird sich ja keine ansehen :D
 
Ich habs nach tagelangen surfen im Inet und msdn endlich geschafft einen Input->Output Stream mit DirectSound hinzubekommen. Ist zwar noch nicht endgültig fertig, Code kann man sicherlich noch schöner gestallten ;), aber es läuft und vll. kann ich ja einigen anderen verzweifelten DirectSound Anfängern damit helfen...

dsound.lib und dxguid.lib müssen eingebunden werden!

So jetzt nur noch FFTW Bibliothek einbinden Bandpass realisieren und dann ein Fensterproggi bauen. Leider hab ich nicht so viel Plan von MFC und noch weniger von Win32, deshlab werd ich mich mal demnächst bissel mit C# und .net beschäftigen. Ist sicherlich sinniger als sich in MFC einzuarbeiten ;)

Code:
#include <windows.h> 
#include <dsound.h>
#include <conio.h>
#include <iostream>

using namespace std;

#define			CEVENTS 3
#define			PEVENTS 2
#define			CHANNELS 2
#define			SAMPLESPERSEC 44100
#define			BITSPERSAMPLE 16

typedef struct{
	DWORD dwDataBuffer[44100];
	bool       bFull;			// Buffer Full or Empty
}DataT;

HRESULT	hr;					// Error Variable

WAVEFORMATEX			wfx;		// Input Output Waveformat
LPDIRECTSOUNDFULLDUPLEX		pDSFD;		// Full Duplex Device Pointer
LPDIRECTSOUNDCAPTUREBUFFER8	pDSCB;		// Capture Buffer Pointer
LPDIRECTSOUNDBUFFER8		pDSB;		// Sound Buffer Pointer

HANDLE	hThread[2],				// Thread Handle
		hCaptureEvent[CEVENTS] = {0},	// Capture Event Handle
		hPlayEvent[PEVENTS] = {0};	// Play Event Handle

bool	bStart = false;				// Start/Stop Variable

DataT	sData1,					// Data Buffer First Part
	sData2;					// Data Buffer Second Part

DWORD WINAPI WaitForCaptureNotifications(void);		// Wait for Capture Events Thread Proc
DWORD WINAPI WaitForPlayNotifications(void);		// Wait for Play Events	Thread Proc

DWORD WriteSoundBuffer(DWORD dwOffset, DataT* pData);	// Write Data Part from Data Buffer to Sound Buffer
DWORD ReadCaptureBuffer(DWORD dwOffset, DataT* pData);	// Write Data Part from Capture Buffer to Data Buffer


HRESULT CreateFullDuplexDevice(void);				// Create Full Duplex Device
HRESULT SetCaptureNotifications(void);				// Set the Capture Buffer Notifications
HRESULT SetPlayNotifications(void);				// Set the Play Buffer Notifications

HWND GetCMDHwnd(void);						// Get Window Handle


int main(void){
	hr = CreateFullDuplexDevice();
	if(FAILED(hr)){
		MessageBox(NULL,L"Full Duplex Device konnte nicht erstellt werden.",L"Fehler", NULL);  
	}

	hr = SetCaptureNotifications();	
	if(FAILED(hr)){
		MessageBox(NULL,L"Capture Notificationens konnten nicht erstellt werden.",L"Fehler", NULL);  
	}	

	hr = SetPlayNotifications();	
	if(FAILED(hr)){
		MessageBox(NULL,L"Play Notificationens konnten nicht erstellt werden.",L"Fehler", NULL);  
	}	
		
	hr = pDSCB->Start(DSCBSTART_LOOPING);
	if(FAILED(hr)){
		MessageBox(NULL,L"Capture Buffer konnte nicht gestartet werden.",L"Fehler", NULL); 
	}
	else{
		// Set Start Variable and Create Wait for Capture Events Thread
		bStart = true;
		hThread[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WaitForCaptureNotifications,NULL,0,NULL);
	}

	hr = pDSB->Play(0, 0, DSBPLAY_LOOPING);
	if(FAILED(hr)){
		MessageBox(NULL,L"Play Buffer konnte nicht gestartet werden.",L"Fehler", NULL); 
	}

	printf("'g' druecken um Aufnahme zu beenden.\n");
	while(_getch()!=113);
	hr = pDSCB->Stop();
	if(FAILED(hr)){
		MessageBox(NULL,L"Capture Buffer konnte nicht gestoppt werden.",L"Fehler", NULL); 
	}
	
	printf("'g' druecken um Wiedergabe zu beenden.\n");
	while(_getch()!=113);
	hr = pDSB->Stop();
	if(FAILED(hr)){
		MessageBox(NULL,L"Play Buffer konnte nicht gestoppt werden.",L"Fehler", NULL); 
	}
	
	// Close Handles
	for(int i=0; i<2; i++){
		CloseHandle(hThread[i]);	
		CloseHandle(hPlayEvent[i]);
	}
	for(int i=0; i<3; i++)
		CloseHandle(hCaptureEvent[i]);
	
	// Release Objects
	pDSCB->Release();
	pDSB->Release();
	pDSFD->Release();

	return(0);
}

DWORD ReadCaptureBuffer(DWORD dwOffset, DataT* pData){
	LPVOID	pbCaptureData;
    DWORD	dwCaptureLength;

	if(SUCCEEDED(hr = pDSCB->Lock(			// Lock Capture Buffer Part
		dwOffset,		// dwOffset
		wfx.nAvgBytesPerSec,	// dwBytes
		&pbCaptureData,		// ppvAudioPtr1
		&dwCaptureLength,	// pdwAudioBytes1	
		NULL,			// ppvAudioPtr2
		0,			// pdwAudioBytes2
		0)))			// dwFlags
	{		
		memcpy(pData->dwDataBuffer, pbCaptureData, dwCaptureLength);	// Copy Data from Capture Buffer to Data Buffer

		hr = pDSCB->Unlock(		// Unlock Capture Buffer Part
			pbCaptureData,	             // ppvAudioPtr1
			dwCaptureLength,	// pdwAudioBytes1
			NULL,			// ppvAudioPtr2
			0);			// pdwAudioBytes

		pData->bFull = true;		// Set Buffer Full Variable
		// Create Wait for Play Events Thread 
		hThread[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WaitForPlayNotifications,NULL,0,NULL);
		return(0);	
	}
	return(1);
}

DWORD WriteSoundBuffer(DWORD dwOffset, DataT* pData){
	LPVOID	pbPlayData;
    DWORD	dwPlayLength;

	pData->bFull = false;			// Reset Buffer Full Variable
	if(SUCCEEDED(hr = pDSB->Lock(	// Lock Sound Buffer Part
		dwOffset,			// dwOffset
		wfx.nAvgBytesPerSec,		// dwBytes
		&pbPlayData,	           // ppvAudioPtr1
		&dwPlayLength,		              // pdwAudioBytes1	
		NULL,				// ppvAudioPtr2
		0,				// pdwAudioBytes2
		0)))				// dwFlags
	{
		memcpy(pbPlayData, pData->dwDataBuffer, dwPlayLength);		// Copy Data from Data Buffer to Sound Buffer

		hr = pDSB->Unlock(						// Unlock Sound Buffer Part
			pbPlayData,				// ppvAudioPtr1
			dwPlayLength,				// pdwAudioBytes1
			NULL,					// ppvAudioPtr2
			0);					// pdwAudioBytes2

		return(0);
	}
	return(1);
}

DWORD WINAPI WaitForCaptureNotifications(void){
	DWORD dwResult;	

	do{
		// Wait for Capture Buffer Event
		dwResult = WaitForMultipleObjects(3, hCaptureEvent, false, INFINITE) - WAIT_OBJECT_0; 
		
		// Check witch Event is Signaled
		switch(dwResult){
			case 0:{
				ReadCaptureBuffer(0, &sData1);				// Read First Part 
				ResetEvent(hCaptureEvent[0]);				// Reset Capture Event
				break;}
			case 1:{
				ReadCaptureBuffer(wfx.nAvgBytesPerSec, &sData2);	// Read Second Part		
				ResetEvent(hCaptureEvent[1]);				// Reaset Capture Event
				break;}					
			case 2:{
				bStart = false;						// Stop Capure Buffer
				cout << "Aufnahme gestoppt!" << endl;
				break;}
		}
	}while(bStart);
	return(0);
}

DWORD WINAPI WaitForPlayNotifications(void){
	DWORD dwResult;
	DataT* pData;

	if(sData1.bFull) pData = &sData1;
	if(sData2.bFull) pData = &sData2;

	// Wait for Play Buffer Events
	dwResult = WaitForMultipleObjects(2, hPlayEvent, false, INFINITE) - WAIT_OBJECT_0;

	// Check witch Event is Signaled
	switch(dwResult){
		case 0:{
			WriteSoundBuffer(0, pData);					// Write First Part
			ResetEvent(hPlayEvent[0]);					// Reset Play Event
			break;}
		case 1:{
			WriteSoundBuffer(wfx.nAvgBytesPerSec, pData);			// Write Second Part
			ResetEvent(hPlayEvent[1]);					// Reset Play Event
			break;}		
	}
	return(0);
}

HRESULT CreateFullDuplexDevice(void){
	DSBUFFERDESC			dsbd;			
	DSCBUFFERDESC			dscbd;			
	HWND					CMDHwnd;
	
	// Set Wave Format
	ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = CHANNELS;
	wfx.nSamplesPerSec = SAMPLESPERSEC;
	wfx.wBitsPerSample = BITSPERSAMPLE;
	wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample /8;
	wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
	wfx.cbSize = 0;
	
	// Set Sound Buffer Attributes
	ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
	dsbd.dwSize = sizeof(DSBUFFERDESC); 
	dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
	dsbd.dwBufferBytes = wfx.nAvgBytesPerSec*2; 
	dsbd.lpwfxFormat = &wfx;

	// Set Capture Buffer Attributes
	ZeroMemory(&dscbd, sizeof(DSCBUFFERDESC));
	dscbd.dwSize = sizeof(DSCBUFFERDESC);
	dscbd.dwFlags = 0;
	dscbd.dwBufferBytes = wfx.nAvgBytesPerSec*2;
	dscbd.dwReserved = 0;
	dscbd.lpwfxFormat = &wfx;
	dscbd.dwFXCount = 0;
	dscbd.lpDSCFXDesc = NULL;
	
	// Get Console Handle
	CMDHwnd = GetCMDHwnd();
	
	// Create Full Duplex Device
	hr = DirectSoundFullDuplexCreate8(		
		NULL,					// pcGuidCaptureDevice
		NULL,					// pcGuidRenderDevice
		&dscbd,					// pcDSCBufferDesc
		&dsbd,					// pcDSBufferDesc
		CMDHwnd,				// hWnd
		DSSCL_PRIORITY,			// dwLevel
		&pDSFD,					// ppDSFD
		&pDSCB,					// ppDSCBuffer8
		&pDSB,					// ppDSBuffer8
		NULL);					// pUnkOuter

	return hr;
}

HRESULT SetCaptureNotifications(void){
	LPDIRECTSOUNDNOTIFY8	pDSNotify;
	DSBPOSITIONNOTIFY		cdsbpn[CEVENTS];
	
	if (NULL == pDSCB) return E_INVALIDARG;
	
	// Create Sound Notify Interface 
	if (FAILED(hr = pDSCB->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&pDSNotify))){
		return hr;
	}

	// Create Events.
	for (int i = 0; i < CEVENTS; ++i){
		hCaptureEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (NULL == hCaptureEvent[i]){
			hr = GetLastError();
			return hr;
		}
	}

	// Describe Notifications
	cdsbpn[0].dwOffset = wfx.nAvgBytesPerSec-1;
	cdsbpn[0].hEventNotify = hCaptureEvent[0];

	cdsbpn[1].dwOffset = wfx.nAvgBytesPerSec*2-1;
	cdsbpn[1].hEventNotify = hCaptureEvent[1];
	
	cdsbpn[2].dwOffset = DSBPN_OFFSETSTOP;
	cdsbpn[2].hEventNotify = hCaptureEvent[2];

	// Create Notifications
	hr = pDSNotify->SetNotificationPositions(CEVENTS, cdsbpn);

	// Release Notification Object
	pDSNotify->Release();
	return hr;
}

HRESULT SetPlayNotifications(void){
	LPDIRECTSOUNDNOTIFY8	pDSNotify;
	DSBPOSITIONNOTIFY		pdsbpn[PEVENTS];

	if (NULL == pDSB) return E_INVALIDARG;
	
	// Create Sound Notify Interface 
	if (FAILED(hr = pDSB->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&pDSNotify))){
		return hr;
	}

	// Create Events
	for (int i = 0; i < PEVENTS; ++i){
		hPlayEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (NULL == hPlayEvent[i]){
			hr = GetLastError();
			return hr;
		}
	}

	// Describe Notifications
	pdsbpn[0].dwOffset = wfx.nAvgBytesPerSec-1;
	pdsbpn[0].hEventNotify = hPlayEvent[0];

	pdsbpn[1].dwOffset = wfx.nAvgBytesPerSec*2-1;
	pdsbpn[1].hEventNotify = hPlayEvent[1];

	// Create Notifications.
	hr = pDSNotify->SetNotificationPositions(PEVENTS, pdsbpn);

	// Release Notification Object
	pDSNotify->Release();
	return hr;
}

HWND GetCMDHwnd(void){        
	LPCWSTR newtitle = (LPCWSTR) L"DirectSound-Test";
    SetConsoleTitle(newtitle);
    Sleep(40);
    return(FindWindow(NULL, newtitle));
}
 
Junge junge, machst es dir mit DirectSound ganz schoen umstaendlich. Wuerde dir eher XAudio2 empfehlen. Unter XP wird DirectSound als passthrough genommen und unter Vista laeufts sogar angeblich durch den low-level audio renderer. Ist nebenbei auch einfacher zu programmieren mit weniger code.
 
Hmm nur das man mit XAudio2 nicht capturen kann, also hätte ich entweder DirectSound, WinMM, DirectShow oder OpenAL nehmen müssen.

Macht für mich irgendwie keinen Sinn sich in zwei APIs einzulesen, eine ist schon schwer genug wenn man nicht soooo den Plan hat.
 
Zurück
Oben