Esempio n. 1
0
void DXAudioInput::run() {
	LPDIRECTSOUNDCAPTURE8      pDSCapture;
	LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer;
	LPDIRECTSOUNDNOTIFY        pDSNotify;

	DWORD dwBufferSize;
	bool bOk;
	DWORD dwReadPosition;
	DWORD dwCapturePosition;

	LPVOID aptr1, aptr2;
	DWORD nbytes1, nbytes2;

	HRESULT       hr;
	WAVEFORMATEX  wfx;
	DSCBUFFERDESC dscbd;

	pDSCapture = NULL;
	pDSCaptureBuffer = NULL;
	pDSNotify = NULL;

	bOk = false;

	bool failed = false;

	Timer t;

	ZeroMemory(&wfx, sizeof(wfx));
	wfx.wFormatTag = WAVE_FORMAT_PCM;

	ZeroMemory(&dscbd, sizeof(dscbd));
	dscbd.dwSize = sizeof(dscbd);

	dscbd.dwBufferBytes = dwBufferSize = iFrameSize * sizeof(short) * NBUFFBLOCKS;
	dscbd.lpwfxFormat = &wfx;

	wfx.nChannels = 1;
	wfx.nSamplesPerSec = iSampleRate;
	wfx.nBlockAlign = 2;
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
	wfx.wBitsPerSample = 16;

	// Create IDirectSoundCapture using the preferred capture device
	if (! g.s.qbaDXInput.isEmpty()) {
		LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXInput.data());
		if (FAILED(hr = DirectSoundCaptureCreate8(lpguid, &pDSCapture, NULL))) {
			failed = true;
		}
	}

	if (! pDSCapture && FAILED(hr = DirectSoundCaptureCreate8(&DSDEVID_DefaultVoiceCapture, &pDSCapture, NULL)))
		qWarning("DXAudioInput: DirectSoundCaptureCreate failed: hr=0x%08lx", hr);
	else if (FAILED(hr = pDSCapture->CreateCaptureBuffer(&dscbd, &pDSCaptureBuffer, NULL)))
		qWarning("DXAudioInput: CreateCaptureBuffer failed: hr=0x%08lx", hr);
	else if (FAILED(hr = pDSCaptureBuffer->QueryInterface(IID_IDirectSoundNotify, reinterpret_cast<void **>(&pDSNotify))))
		qWarning("DXAudioInput: QueryInterface (Notify) failed: hr=0x%08lx", hr);
	else
		bOk = true;



	if (failed)
		g.mw->msgBox(tr("Opening chosen DirectSound Input failed. Default device will be used."));

	qWarning("DXAudioInput: Initialized");

	if (! bOk)
		goto cleanup;

	if (FAILED(hr = pDSCaptureBuffer->Start(DSCBSTART_LOOPING))) {
		qWarning("DXAudioInput: Start failed: hr=0x%08lx", hr);
	} else {
		DWORD dwReadyBytes = 0;
		DWORD dwLastReadPos = 0;
		float safety = 2.0f;

		while (bRunning) {
			bool firstsleep = true;
			bool didsleep = false;

			do {
				if (FAILED(hr = pDSCaptureBuffer->GetCurrentPosition(&dwCapturePosition, &dwReadPosition))) {
					qWarning("DXAudioInput: GetCurrentPosition failed: hr=0x%08lx", hr);
					bRunning = false;
					break;
				}
				if (dwReadPosition < dwLastReadPos)
					dwReadyBytes = (dwBufferSize - dwLastReadPos) + dwReadPosition;
				else
					dwReadyBytes = dwReadPosition - dwLastReadPos;

				if (static_cast<size_t>(dwReadyBytes) < sizeof(short) * iFrameSize) {
					double msecleft = 20.0 - (dwReadyBytes * 20.0) / (sizeof(short) * iFrameSize);

					if (didsleep)
						safety *= 1.1f;
					else if (firstsleep)
						safety *= 0.998f;

					int msec = static_cast<int>(msecleft + (firstsleep ? safety : 0.0));

					msleep(msec);

					didsleep = true;
					firstsleep = false;
				}
			} while (static_cast<size_t>(dwReadyBytes) < sizeof(short) * iFrameSize);

			// Desynchonized?
			if (dwReadyBytes > (dwBufferSize / 2)) {
				qWarning("DXAudioInput: Lost synchronization");
				dwLastReadPos = dwReadPosition;
			} else if (bRunning) {
				if (FAILED(hr = pDSCaptureBuffer->Lock(dwLastReadPos, sizeof(short) * iFrameSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) {
					qWarning("DXAudioInput: Lock from %lu (%zu bytes) failed: hr=0x%08lx", static_cast<unsigned long>(dwLastReadPos), sizeof(short) * iFrameSize, hr);
					bRunning = false;
					break;
				}

				if (aptr1 && nbytes1)
					CopyMemory(psMic, aptr1, nbytes1);

				if (aptr2 && nbytes2)
					CopyMemory(psMic+nbytes1/2, aptr2, nbytes2);

				if (FAILED(hr = pDSCaptureBuffer->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
					qWarning("DXAudioInput: Unlock failed: hr=0x%08lx", hr);
					bRunning = false;
					break;
				}

				dwLastReadPos = (dwLastReadPos + sizeof(short) * iFrameSize) % dwBufferSize;

				encodeAudioFrame();
			}
		}
		if (! FAILED(hr))
			pDSCaptureBuffer->Stop();
	}
	if (FAILED(hr)) {
		g.mw->msgBox(tr("Lost DirectSound input device."));
	}

cleanup:
	if (! bOk) {
		g.mw->msgBox(tr("Opening chosen DirectSound Input device failed. No microphone capture will be done."));
	}
	if (pDSNotify)
		pDSNotify->Release();
	if (pDSCaptureBuffer)
		pDSCaptureBuffer->Release();
	if (pDSCapture)
		pDSCapture->Release();
}
bool nuiAudioDevice_DirectSound::Open(std::vector<uint32>& rInputChannels, std::vector<uint32>& rOutputChannels, double SampleRate, uint32 BufferSize, nuiAudioProcessFn pProcessFunction)
{
  if (!mpDirectSound)
    return false;
  
  HRESULT hr = S_OK;
  mAudioProcessFn = pProcessFunction;

  mBufferSize = BufferSize;

  hr = mpDirectSound->SetCooperativeLevel(GetDesktopWindow(), DSSCL_EXCLUSIVE);

  mHasInput = (rInputChannels.size() > 0) && (mInputChannels.size() > 0);
  mHasOutput = (rOutputChannels.size() > 0) && (mOutputChannels.size() > 0);


  mpInputBuffer = NULL;
  mpOutputBuffer = NULL;

  if (!mHasInput && !mHasOutput)
    return false;

  // init ringbuffer
  mpRingBuffer = new nglRingBuffer(BufferSize*4, sizeof(float), rOutputChannels.size());
  mpRingBuffer->AdvanceWriteIndex(BufferSize);

  // init input buffers
  if (mHasInput)
  {
    {
      mActiveInputChannels = rInputChannels;
    }

    WAVEFORMATEX IFormat;
    IFormat.wFormatTag = WAVE_FORMAT_PCM;
    IFormat.nChannels = (WORD)mInputChannels.size();
    IFormat.nSamplesPerSec = ToNearest(SampleRate);
    IFormat.wBitsPerSample = 16;
    IFormat.nAvgBytesPerSec = IFormat.nChannels * IFormat.nSamplesPerSec * (IFormat.wBitsPerSample / 8);
    IFormat.nBlockAlign = IFormat.nChannels * (IFormat.wBitsPerSample / 8);
    IFormat.cbSize = 0;

    DSCBUFFERDESC IBufferDesc;
    memset(&IBufferDesc, 0, sizeof(IBufferDesc));
    IBufferDesc.dwSize = sizeof(DSCBUFFERDESC);
    IBufferDesc.dwFlags = DSCBCAPS_WAVEMAPPED;
    IBufferDesc.dwBufferBytes = (IFormat.wBitsPerSample / 8) * IFormat.nChannels * BufferSize * 2;
    IBufferDesc.dwReserved = 0;
    IBufferDesc.lpwfxFormat = &IFormat;
    IBufferDesc.dwFXCount = 0;
    IBufferDesc.lpDSCFXDesc = NULL;

    NGL_ASSERT(mpDirectSoundCapture);
    hr = mpDirectSoundCapture->CreateCaptureBuffer(&IBufferDesc, &mpInputBuffer, NULL);
  }


  // init output buffers
  if (mHasOutput)
  {
    {
      mActiveOutputChannels = rOutputChannels;
    }

    WAVEFORMATEX OFormat;
    OFormat.wFormatTag = WAVE_FORMAT_PCM;
    OFormat.nChannels = (WORD)mOutputChannels.size();
    OFormat.nSamplesPerSec = ToNearest(SampleRate);
    OFormat.wBitsPerSample = 16;
    OFormat.nAvgBytesPerSec = OFormat.nChannels * OFormat.nSamplesPerSec * (OFormat.wBitsPerSample / 8);
    OFormat.nBlockAlign = OFormat.nChannels * OFormat.wBitsPerSample / 8;
    OFormat.cbSize = 0;

    DSBUFFERDESC  OBufferDesc;
    memset(&OBufferDesc, 0, sizeof(OBufferDesc));
    OBufferDesc.dwSize = sizeof(OBufferDesc);
    OBufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
    OBufferDesc.dwBufferBytes = (OFormat.wBitsPerSample / 8) * OFormat.nChannels * BufferSize * 2;
    OBufferDesc.dwReserved = 0;
    OBufferDesc.lpwfxFormat = &OFormat;

    hr = mpDirectSound->CreateSoundBuffer(&OBufferDesc, &mpOutputBuffer, NULL);
  }


  // create event for notifications
  mNotifInputEvent[0] = CreateEvent(NULL, FALSE, FALSE, _T("NUI_DSoundInputEvent0"));
  mNotifInputEvent[1] = CreateEvent(NULL, FALSE, FALSE, _T("NUI_DSoundInputEvent1"));
  mNotifOutputEvent[0] = CreateEvent(NULL, FALSE, FALSE, _T("NUI_DSoundOutputEvent0"));
  mNotifOutputEvent[1] = CreateEvent(NULL, FALSE, FALSE, _T("NUI_DSoundOutputEvent1"));




  // set the notification for the input buffer
  if (mHasInput)
  {
    // Setup the notification positions
    ZeroMemory( &mInputPosNotify, sizeof(DSBPOSITIONNOTIFY) * 2);
    mInputPosNotify[0].dwOffset = BufferSize * sizeof(int16) * mInputChannels.size() - 1;
    mInputPosNotify[0].hEventNotify = mNotifInputEvent[0];             
    mInputPosNotify[1].dwOffset = BufferSize * sizeof(int16) * mInputChannels.size() * 2 - 1;
    mInputPosNotify[1].hEventNotify = mNotifInputEvent[1];           

    LPDIRECTSOUNDNOTIFY pInputNotify = NULL;
    if( FAILED( hr = mpInputBuffer->QueryInterface( IID_IDirectSoundNotify, (VOID**)&pInputNotify ) ) )
    {
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("Open ERROR : failed in querying interface for input notifications.\n"));
      return false;
    }


    // Tell DirectSound when to notify us. the notification will come in the from 
    // of signaled events that are handled in WinMain()
    if( FAILED( hr = pInputNotify->SetNotificationPositions( 2, mInputPosNotify ) ) )
    {
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("Open ERROR : failed in setting notifications for input\n"));
      return false;
    }

    pInputNotify->Release();
  }



  // set the notification events for the output buffer
  if (mHasOutput)
  {
    // Setup the notification positions
    ZeroMemory( &mOutputPosNotify, sizeof(DSBPOSITIONNOTIFY) * 2);
    mOutputPosNotify[0].dwOffset = BufferSize * sizeof(int16) * mOutputChannels.size() - 1;
    mOutputPosNotify[0].hEventNotify = mNotifOutputEvent[0];             
    mOutputPosNotify[1].dwOffset = BufferSize * sizeof(int16) * mOutputChannels.size() * 2 - 1;
    mOutputPosNotify[1].hEventNotify = mNotifOutputEvent[1];    

    LPDIRECTSOUNDNOTIFY pOutputNotify = NULL;
    if( FAILED( hr = mpOutputBuffer->QueryInterface( IID_IDirectSoundNotify, (VOID**)&pOutputNotify ) ) )
    {
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("Open ERROR : failed in querying interface for output notifications.\n"));
      return false;
    }

    // Tell DirectSound when to notify us. the notification will come in the from 
    // of signaled events that are handled in WinMain()
    if( FAILED( hr = pOutputNotify->SetNotificationPositions( 2, mOutputPosNotify ) ) )
    {
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("Open ERROR : failed in setting notifications for output\n"));
      return false;
    }

    pOutputNotify->Release();
  }


  // start input processing thread
  mpProcessingTh = new nuiAudioDevice_DS_ProcessingTh(this, mNotifInputEvent[0], mNotifInputEvent[1], mAudioProcessFn);
  mpProcessingTh->Start();



  // start output thread
  if (mHasOutput)
  {
    mpOutputTh = new nuiAudioDevice_DS_OutputTh(this, mNotifInputEvent[0], mNotifInputEvent[1], mNotifOutputEvent[0], mNotifOutputEvent[1]);
    mpOutputTh->Start();
    hr = mpOutputBuffer->Play(0,0,DSCBSTART_LOOPING);
    if (FAILED(hr))
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("OutputBuffer->Play ERROR!\n"));
  }

  // start input capture
  if (mHasInput)
  {
    hr = mpInputBuffer->Start(DSCBSTART_LOOPING);
    if (FAILED(hr))
      NGL_LOG(_T("nuiAudioDevice_DirectSound"), NGL_LOG_ERROR, _T("InputBuffer->Start ERROR!\n"));    
  }





  return true;
}
Esempio n. 3
0
void DXAudioOutput::run() {
	HRESULT hr;
	DSBUFFERDESC        dsbdesc;
	WAVEFORMATEXTENSIBLE wfx;
	WAVEFORMATEXTENSIBLE wfxSet;
	int ns = 0;
	unsigned int chanmasks[32];

	LPDIRECTSOUND8             pDS = NULL;
	LPDIRECTSOUNDBUFFER       pDSBPrimary = NULL;
	LPDIRECTSOUNDBUFFER       pDSBOutput = NULL;
	LPDIRECTSOUNDNOTIFY       pDSNotify = NULL;

	int iLastwriteblock;
	LPVOID aptr1, aptr2;
	DWORD nbytes1, nbytes2;

	int playblock;
	int nowriteblock;
	DWORD dwPlayPosition, dwWritePosition;

	unsigned int iByteSize;

	bool bOk;
	DWORD dwSpeakerConfig;

	bool failed = false;

	bOk = false;
	DWORD dwMask = 0;
	bool bHead = false;

	ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize  = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;

	if (! g.s.qbaDXOutput.isEmpty()) {
		LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXOutput.data());
		if (FAILED(hr = DirectSoundCreate8(lpguid, &pDS, NULL))) {
			failed = true;
		}
	}

	if (! pDS && FAILED(hr = DirectSoundCreate8(&DSDEVID_DefaultVoicePlayback, &pDS, NULL))) {
		qWarning("DXAudioOutput: DirectSoundCreate failed: hr=0x%08lx", hr);
		goto cleanup;
	} else if (FAILED(hr = pDS->SetCooperativeLevel(mumble_mw_hwnd, DSSCL_PRIORITY))) {
		qWarning("DXAudioOutput: SetCooperativeLevel failed: hr=0x%08lx", hr);
		goto cleanup;
	} else if (FAILED(hr = pDS->CreateSoundBuffer(&dsbdesc, &pDSBPrimary, NULL))) {
		qWarning("DXAudioOutput: CreateSoundBuffer (Primary) failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	pDS->GetSpeakerConfig(&dwSpeakerConfig);


	switch (DSSPEAKER_CONFIG(dwSpeakerConfig)) {
		case DSSPEAKER_HEADPHONE:
			dwMask = KSAUDIO_SPEAKER_STEREO;
			bHead = true;
			break;
		case DSSPEAKER_MONO:
			dwMask = KSAUDIO_SPEAKER_MONO;
			break;
		case DSSPEAKER_QUAD:
			dwMask = KSAUDIO_SPEAKER_QUAD;
			break;
		case DSSPEAKER_STEREO:
			dwMask = KSAUDIO_SPEAKER_STEREO;
			break;
		case DSSPEAKER_SURROUND:
			dwMask = KSAUDIO_SPEAKER_SURROUND;
			break;
		case DSSPEAKER_5POINT1:
			dwMask = KSAUDIO_SPEAKER_5POINT1;
			break;
		case DSSPEAKER_7POINT1:
			dwMask = KSAUDIO_SPEAKER_7POINT1;
			break;
		case DSSPEAKER_7POINT1_SURROUND:
			dwMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
			break;
		case DSSPEAKER_5POINT1_SURROUND:
			dwMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
			break;
		default:
			dwMask = 0;
			break;
	}

	if (! g.s.doPositionalAudio())
		dwMask = KSAUDIO_SPEAKER_STEREO;

	for (int i=0;i<32;i++) {
		if (dwMask & (1 << i)) {
			chanmasks[ns++] = 1 << i;
		}
	}

	iMixerFreq = SAMPLE_RATE;
	iChannels = ns;
	eSampleFormat = SampleShort;

	iByteSize = iFrameSize * sizeof(short) * ns;

	ZeroMemory(&wfxSet, sizeof(wfxSet));
	wfxSet.Format.wFormatTag = WAVE_FORMAT_PCM;

	ZeroMemory(&wfx, sizeof(wfx));
	wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
	wfx.Format.nChannels = qMax(ns, 2);
	wfx.Format.nSamplesPerSec = SAMPLE_RATE;
	wfx.Format.nBlockAlign = sizeof(short) * wfx.Format.nChannels;
	wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
	wfx.Format.wBitsPerSample = 16;

	if (FAILED(hr = pDSBPrimary->SetFormat(reinterpret_cast<WAVEFORMATEX *>(&wfx)))) {
		qWarning("DXAudioOutput: SetFormat failed: hr=0x%08lx", hr);
		goto cleanup;
	}
	if (FAILED(hr = pDSBPrimary->GetFormat(reinterpret_cast<WAVEFORMATEX *>(&wfxSet), sizeof(wfxSet), NULL))) {
		qWarning("DXAudioOutput: GetFormat failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	qWarning("DXAudioOutput: Primary buffer of %ld Hz, %d channels, %d bits",wfxSet.Format.nSamplesPerSec,wfxSet.Format.nChannels,wfxSet.Format.wBitsPerSample);

	ZeroMemory(&wfx, sizeof(wfx));
	wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
	wfx.Format.nChannels = qMax(ns, 2);
	wfx.Format.nSamplesPerSec = SAMPLE_RATE;
	wfx.Format.nBlockAlign = sizeof(short) * wfx.Format.nChannels;
	wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
	wfx.Format.wBitsPerSample = 16;
	wfx.Format.cbSize = 32;
	wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
	wfx.dwChannelMask = dwMask;
	wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

	ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize          = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags         = DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
	dsbdesc.dwFlags	 |= DSBCAPS_CTRLPOSITIONNOTIFY;
	dsbdesc.dwBufferBytes = wfx.Format.nChannels * iFrameSize * sizeof(short) * NBLOCKS;
	dsbdesc.lpwfxFormat     = reinterpret_cast<WAVEFORMATEX *>(&wfx);

	if (FAILED(hr = pDS->CreateSoundBuffer(&dsbdesc, &pDSBOutput, NULL))) {
		qWarning("DXAudioOutputUser: CreateSoundBuffer (Secondary) failed: hr=0x%08lx", hr);
		goto cleanup;
	}


	if (FAILED(hr = pDSBOutput->QueryInterface(IID_IDirectSoundNotify, reinterpret_cast<void **>(&pDSNotify)))) {
		qWarning("DXAudioOutputUser: QueryInterface (Notify) failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	qWarning("DXAudioOutputUser: New %dHz output buffer of %ld bytes", SAMPLE_RATE, dsbdesc.dwBufferBytes);

	if (failed)
		g.mw->msgBox(tr("Opening chosen DirectSound Output failed. Default device will be used."));

	initializeMixer(chanmasks, bHead);

	if (FAILED(hr = pDSBOutput->Lock(0, 0, &aptr1, &nbytes1, &aptr2, &nbytes2, DSBLOCK_ENTIREBUFFER))) {
		qWarning("DXAudioOutputUser: Initial Lock failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	if (aptr1)
		ZeroMemory(aptr1, nbytes1);
	if (aptr2)
		ZeroMemory(aptr2, nbytes2);

	if (FAILED(hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
		qWarning("DXAudioOutputUser: Initial Unlock failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	if (FAILED(hr = pDSBOutput->Play(0, 0, DSBPLAY_LOOPING))) {
		qWarning("DXAudioOutputUser: Play failed: hr=0x%08lx", hr);
		goto cleanup;
	}

	iLastwriteblock = (NBLOCKS - 1 + g.s.iOutputDelay) % NBLOCKS;

	bOk = true;

	while (bRunning && ! FAILED(hr)) {
		if (FAILED(hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition))) {
			qWarning("DXAudioOutputUser: GetCurrentPosition failed: hr=0x%08lx", hr);
			break;
		}

		playblock = dwWritePosition / iByteSize;
		nowriteblock = (playblock + g.s.iOutputDelay + 1) % NBLOCKS;

		for (int block=(iLastwriteblock + 1) % NBLOCKS;(!FAILED(hr)) && (block!=nowriteblock);block=(block + 1) % NBLOCKS) {
			iLastwriteblock = block;

			if (FAILED(hr = pDSBOutput->Lock(block * iByteSize, iByteSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) {
				qWarning("DXAudioOutput: Lock block %u (%d bytes) failed: hr=0x%08lx",block, iByteSize, hr);
				break;
			}
			if (aptr2 || nbytes2) {
				qWarning("DXAudioOutput: Split buffer");
				break;
			}
			if (!aptr1 || ! nbytes1) {
				qWarning("DXAudioOutput: Zerolock");
				break;
			}
			if (! mix(reinterpret_cast<short *>(aptr1), iFrameSize))
				ZeroMemory(aptr1, iByteSize);

			if (FAILED(hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
				qWarning("DXAudioOutput: Unlock %p(%lu) %p(%lu) failed: hr=0x%08lx",aptr1,nbytes1,aptr2,nbytes2,hr);
				break;
			}

			if (FAILED(hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition))) {
				qWarning("DXAudioOutputUser: GetCurrentPosition failed: hr=0x%08lx", hr);
				break;
			}

			playblock = dwWritePosition / iByteSize;
			nowriteblock = (playblock + g.s.iOutputDelay + 1) % NBLOCKS;
		}
		if (! FAILED(hr))
			msleep(19);
	}

	if (FAILED(hr)) {
		g.mw->msgBox(tr("Lost DirectSound output device."));
	}
cleanup:
	if (! bOk) {
		g.mw->msgBox(tr("Opening chosen DirectSound Output failed. No audio will be heard."));
		return;
	}

	if (pDSNotify)
		pDSNotify->Release();
	if (pDSBOutput) {
		pDSBOutput->Stop();
		pDSBOutput->Release();
	}
	if (pDSBPrimary)
		pDSBPrimary->Release();
	if (pDS)
		pDS->Release();
}
bool VoiceRecord_DSound::Init(int sampleRate)
{
	HRESULT hr;
	DSCBUFFERDESC dscDesc;
	DirectSoundCaptureCreateFn createFn;

	
	Term();


	WAVEFORMATEX recordFormat =
	{
		WAVE_FORMAT_PCM,		// wFormatTag
		1,						// nChannels
		sampleRate,				// nSamplesPerSec
		sampleRate*2,			// nAvgBytesPerSec
		2,						// nBlockAlign
		16,						// wBitsPerSample
		sizeof(WAVEFORMATEX)	// cbSize
	};


	
	// Load the DSound DLL.
	m_hInstDS = LoadLibrary("dsound.dll");
	if(!m_hInstDS)
		goto HandleError;

	createFn = (DirectSoundCaptureCreateFn)GetProcAddress(m_hInstDS, "DirectSoundCaptureCreate");
	if(!createFn)
		goto HandleError;

	hr = createFn(NULL, &m_pCapture, NULL);
	if(FAILED(hr))
		goto HandleError;

	// Create the capture buffer.
	memset(&dscDesc, 0, sizeof(dscDesc));
	dscDesc.dwSize = sizeof(dscDesc);
	dscDesc.dwFlags = 0;
	dscDesc.dwBufferBytes = recordFormat.nAvgBytesPerSec;
	dscDesc.lpwfxFormat = &recordFormat;

	hr = m_pCapture->CreateCaptureBuffer(&dscDesc, &m_pCaptureBuffer, NULL);
	if(FAILED(hr))
		goto HandleError;


	// Figure out how many bytes we got in our capture buffer.
	DSCBCAPS caps;
	memset(&caps, 0, sizeof(caps));
	caps.dwSize = sizeof(caps);

	hr = m_pCaptureBuffer->GetCaps(&caps);
	if(FAILED(hr))
		goto HandleError;

	m_nCaptureBufferBytes = caps.dwBufferBytes;


	// Set it up so we get notification when the buffer wraps.
	m_hWrapEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if(!m_hWrapEvent)
		goto HandleError;

	DSBPOSITIONNOTIFY dsbNotify;
	dsbNotify.dwOffset = dscDesc.dwBufferBytes - 1;
	dsbNotify.hEventNotify = m_hWrapEvent;
	
	// Get the IDirectSoundNotify interface.
	LPDIRECTSOUNDNOTIFY pNotify;
	hr = m_pCaptureBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify);
	if(FAILED(hr))
		goto HandleError;
	
	hr = pNotify->SetNotificationPositions(1, &dsbNotify);
	pNotify->Release();
	if(FAILED(hr))
		goto HandleError;

	// Start capturing.
	hr = m_pCaptureBuffer->Start(DSCBSTART_LOOPING);
	if(FAILED(hr))
		return false;

	return true;


HandleError:;
	Term();
	return false;
}
bool DirectSound::init()
{
  HRESULT hr;

  dsoundDLL = LoadLibrary("DSOUND.DLL");
  HRESULT (WINAPI *DSoundCreate)(LPCGUID,LPDIRECTSOUND *,IUnknown *);  
  if(dsoundDLL != NULL) {    
    DSoundCreate = (HRESULT (WINAPI *)(LPCGUID,LPDIRECTSOUND *,IUnknown *))
      GetProcAddress(dsoundDLL, "DirectSoundCreate");
    
    if(DSoundCreate == NULL) {
      theApp.directXMessage("DirectSoundCreate");
      return false;
    }
  } else {
    theApp.directXMessage("DSOUND.DLL");
    return false;
  }
  
  if((hr = DSoundCreate(NULL,&pDirectSound,NULL) != DS_OK)) {
    //    errorMessage(myLoadString(IDS_ERROR_SOUND_CREATE), hr);
    systemMessage(IDS_CANNOT_CREATE_DIRECTSOUND,
                  "Cannot create DirectSound %08x", hr);
    pDirectSound = NULL;
    dsbSecondary = NULL;
    return false;
  }

  if((hr=pDirectSound->SetCooperativeLevel((HWND)*theApp.m_pMainWnd,
                                           DSSCL_EXCLUSIVE)) != DS_OK) {
    //    errorMessage(myLoadString(IDS_ERROR_SOUND_LEVEL), hr);
    systemMessage(IDS_CANNOT_SETCOOPERATIVELEVEL,
                  "Cannot SetCooperativeLevel %08x", hr);
    return false;
  }

  DSBUFFERDESC dsbdesc;
  ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC));
  dsbdesc.dwSize=sizeof(DSBUFFERDESC);
  dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
  
  if((hr=pDirectSound->CreateSoundBuffer(&dsbdesc,
                                         &dsbPrimary,
                                         NULL) != DS_OK)) {
    //    errorMessage(myLoadString(IDS_ERROR_SOUND_BUFFER),hr);
    systemMessage(IDS_CANNOT_CREATESOUNDBUFFER,
                  "Cannot CreateSoundBuffer %08x", hr);
    return false;
  }
  
  // Set primary buffer format

  memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
  wfx.wFormatTag = WAVE_FORMAT_PCM; 
  wfx.nChannels = 2;
  switch(soundQuality) {
  case 2:
    wfx.nSamplesPerSec = 22050;
    soundBufferLen = 736*2;
    soundBufferTotalLen = 7360*2;
    break;
  case 4:
    wfx.nSamplesPerSec = 11025;
    soundBufferLen = 368*2;
    soundBufferTotalLen = 3680*2;
    break;
  default:
    soundQuality = 1;
    wfx.nSamplesPerSec = 44100;
    soundBufferLen = 1470*2;
    soundBufferTotalLen = 14700*2;
  }
  wfx.wBitsPerSample = 16; 
  wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;
  wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

  if((hr = dsbPrimary->SetFormat(&wfx)) != DS_OK) {
    //    errorMessage(myLoadString(IDS_ERROR_SOUND_PRIMARY),hr);
    systemMessage(IDS_CANNOT_SETFORMAT_PRIMARY,
                  "Cannot SetFormat for primary %08x", hr);
    return false;
  }
  
  ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC));  
  dsbdesc.dwSize = sizeof(DSBUFFERDESC);
  dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY;
  dsbdesc.dwBufferBytes = soundBufferTotalLen;
  dsbdesc.lpwfxFormat = &wfx;

  if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))
     != DS_OK) {
    dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
    if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))
       != DS_OK) {
      systemMessage(IDS_CANNOT_CREATESOUNDBUFFER_SEC,
                    "Cannot CreateSoundBuffer secondary %08x", hr);
      return false;
    }
  }

  dsbSecondary->SetCurrentPosition(0);

  if(!theApp.useOldSync) {
    hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify,
                                      (void **)&dsbNotify);
    if(!FAILED(hr)) {
      dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      
      DSBPOSITIONNOTIFY notify[10];
      
      for(int i = 0; i < 10; i++) {
        notify[i].dwOffset = i*soundBufferLen;
        notify[i].hEventNotify = dsbEvent;
      }
      if(FAILED(dsbNotify->SetNotificationPositions(10, notify))) {
        dsbNotify->Release();
        dsbNotify = NULL;
        CloseHandle(dsbEvent);
        dsbEvent = NULL;
      }
    }
  }
  
  hr = dsbPrimary->Play(0,0,DSBPLAY_LOOPING);

  if(hr != DS_OK) {
    //    errorMessage(myLoadString(IDS_ERROR_SOUND_PLAYPRIM), hr);
    systemMessage(IDS_CANNOT_PLAY_PRIMARY, "Cannot Play primary %08x", hr);
    return false;
  }

  systemSoundOn = true;
  
  return true;
}
static void
refill_stream(cubeb_stream * stm, int prefill)
{
  VOID * p1, * p2;
  DWORD p1sz, p2sz;
  HRESULT rv;
  long dt;

  /* calculate how much has played since last refill */
  DWORD play, write;
  rv = stm->buffer->GetCurrentPosition(&play, &write);
  assert(rv == DS_OK);

  long gap = write - play;
  if (gap < 0) {
    gap += stm->buffer_size;
  }

#if 1
  dt = GetTickCount() - stm->last_refill;
  if (!prefill) {
    double buflen = (double) (stm->buffer_size - gap) / bytes_per_frame(stm->params) / stm->params.rate * 1000;
    if (dt > buflen) {
      fprintf(stderr, "*** buffer wrap (%ld, %f, %f)***\n", dt, buflen, dt - buflen);
      stm->slipped += (dt - buflen) / 1000.0 * bytes_per_frame(stm->params) * stm->params.rate;
    }
  }
#endif

  unsigned long writepos = stm->written % stm->buffer_size;

  long playsz = 0;
  if (write < writepos) {
    playsz = write + stm->buffer_size - writepos;
  } else {
    playsz = write - writepos;
  }

  /* can't write between play and write cursors */
  playsz -= gap;
  if (playsz < 0) {
#if 0
    fprintf(stderr, "** negcapped, dt=%u real nwl=%ld p=%u w=%u g=%ld wo=%u **\n",
	    dt, playsz, play, write, gap, writepos);
#endif
    return;
  }

  if (prefill) {
    playsz = stm->buffer_size;
  }

  playsz -= bytes_per_frame(stm->params);

  /* no space to refill */
  if (playsz <= 0)
    return;

  /*assert(writepos >= write && ((writepos + playsz) % stm->buffer_size) < play);*/

  /*
    assumptions: buffer with w==p is full or empty
    we know total writes is stm->written
    so w==p and stm->written%stm->buffer_size==0 full or empty
    need abs play pos to determine
    rel play pos is (write + stm->buffer_size) - play
    (0 + 10) - 0 -> 10 -> also assumes buffer is full
    absplayed must be between stm->written-stm->buffer_size and stm->written.

    XXX want prefill logic to work anytime as we will eventually call it from start()
  */

  rv = stm->buffer->Lock(writepos, playsz, &p1, &p1sz, &p2, &p2sz, 0);
  if (rv == DSERR_BUFFERLOST) {
    stm->buffer->Restore();
    rv = stm->buffer->Lock(writepos, playsz, &p1, &p1sz, &p2, &p2sz, 0);
  }
  assert(rv == DS_OK);
  assert(p1sz % bytes_per_frame(stm->params) == 0);
  assert(p2sz % bytes_per_frame(stm->params) == 0);

  int r = stm->data_callback(stm, stm->user_ptr, p1,
			     p1sz / bytes_per_frame(stm->params));
  if (p2 && r == CUBEB_OK) {
    r = stm->data_callback(stm, stm->user_ptr, p2,
			   p2sz / bytes_per_frame(stm->params));
  } else {
    p2sz = 0;
  }

#if 0
  // XXX fix EOS/drain handling
  if (r == CUBEB_EOS) {
    LPDIRECTSOUNDNOTIFY notify;
    rv = stm->buffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID *) &notify);
    assert(rv == DS_OK);

    DSBPOSITIONNOTIFY note;
    note.dwOffset = (writepos + p1sz + p2sz) % stm->buffer_size;
    note.hEventNotify = stm->context->streams_event;
    if (notify->SetNotificationPositions(1, &note) != DS_OK) {
      /* XXX free resources */
      assert(false);
    }

    notify->Release();
    stm->draining = 1;
  }
#endif

  stm->last_refill = GetTickCount();
  stm->written += p1sz + p2sz;
  rv = stm->buffer->Unlock(p1, p1sz, p2, p2sz);
  assert(rv == DS_OK);
}
static int
directsound_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                        cubeb_stream_params stream_params, unsigned int latency,
                        cubeb_data_callback data_callback,
                        cubeb_state_callback state_callback,
                        void * user_ptr)
{
  struct cubeb_list_node * node;

  assert(context);
  *stream = NULL;

  /*
    create primary buffer
  */
  DSBUFFERDESC bd;
  bd.dwSize = sizeof(DSBUFFERDESC);
  bd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  bd.dwBufferBytes = 0;
  bd.dwReserved = 0;
  bd.lpwfxFormat = NULL;
  bd.guid3DAlgorithm = DS3DALG_DEFAULT;

  LPDIRECTSOUNDBUFFER primary;
  if (FAILED(context->dsound->CreateSoundBuffer(&bd, &primary, NULL))) {
    return 1;
  }

  WAVEFORMATEXTENSIBLE wfx;
  wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  wfx.Format.nChannels = stream_params.channels;
  wfx.Format.nSamplesPerSec = stream_params.rate;
  wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);

  /* XXX fix channel mappings */
  wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;

  switch (stream_params.format) {
  case CUBEB_SAMPLE_S16LE:
    wfx.Format.wBitsPerSample = 16;
    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    wfx.Format.wBitsPerSample = 32;
    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
  wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
  wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;

  if (FAILED(primary->SetFormat((LPWAVEFORMATEX) &wfx))) {
    /* XXX free primary */
    return CUBEB_ERROR;
  }

  primary->Release();

  cubeb_stream * stm = (cubeb_stream *) calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = context;

  stm->params = stream_params;

  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;

  InitializeCriticalSection(&stm->lock);

  /*
    create secondary buffer
  */
  bd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;
  bd.dwBufferBytes = (DWORD) (wfx.Format.nSamplesPerSec / 1000.0 * latency * bytes_per_frame(stream_params));
  if (bd.dwBufferBytes % bytes_per_frame(stream_params) != 0) {
    bd.dwBufferBytes += bytes_per_frame(stream_params) - (bd.dwBufferBytes % bytes_per_frame(stream_params));
  }
  bd.lpwfxFormat = (LPWAVEFORMATEX) &wfx;
  if (FAILED(context->dsound->CreateSoundBuffer(&bd, &stm->buffer, NULL))) {
    return CUBEB_ERROR;
  }

  stm->buffer_size = bd.dwBufferBytes;

  LPDIRECTSOUNDNOTIFY notify;
  if (stm->buffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID *) &notify) != DS_OK) {
    /* XXX free resources */
    return CUBEB_ERROR;
  }

  DSBPOSITIONNOTIFY note[3];
  for (int i = 0; i < 3; ++i) {
    note[i].dwOffset = (stm->buffer_size / 4) * i;
    note[i].hEventNotify = context->streams_event;
  }
  if (notify->SetNotificationPositions(3, note) != DS_OK) {
    /* XXX free resources */
    return CUBEB_ERROR;
  }

  notify->Release();

  refill_stream(stm, 1);
  /* XXX remove this, just a test that double refill does not overwrite existing data */
  refill_stream(stm, 0);
  uint64_t pos;
  cubeb_stream_get_position(stm, &pos);

  stm->node = (struct cubeb_list_node *) calloc(1, sizeof(*node));
  stm->node->stream = stm;

  EnterCriticalSection(&context->lock);
  if (!context->streams) {
    context->streams = stm->node;
  } else {
    node = context->streams;
    while (node->next) {
      node = node->next;
    }
    node->next = stm->node;
    stm->node->prev = node;
  }
  LeaveCriticalSection(&context->lock);

  SetEvent(context->streams_event);

  *stream = stm;

  return CUBEB_OK;
}