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; }
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(); }
int DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples) { DSBUFFERDESC dsbd; WAVEFORMATEX wfx; DSBPOSITIONNOTIFY notifies[NSEGS]; int i; memset(&dsbd,0,sizeof(dsbd)); memset(&wfx,0,sizeof(wfx)); // Make wave format: wfx.wFormatTag=WAVE_FORMAT_PCM; wfx.nChannels=stereo ? 2 : 1; wfx.nSamplesPerSec=rate; wfx.wBitsPerSample=16; wfx.nBlockAlign=(WORD)((wfx.nChannels*wfx.wBitsPerSample)>>3); wfx.nAvgBytesPerSec=wfx.nBlockAlign*wfx.nSamplesPerSec; // Create the DirectSound interface: DirectSoundCreate(NULL,&DSound,NULL); if (DSound==NULL) return 1; LoopSeg = seg_samples * 2; if (stereo) LoopSeg *= 2; LoopLen = LoopSeg * NSEGS; DSound->SetCooperativeLevel(wnd_coop, DSSCL_PRIORITY); dsbd.dwFlags=DSBCAPS_GLOBALFOCUS; // Play in background dsbd.dwFlags|=DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY; // Create the looping buffer: dsbd.dwSize=sizeof(dsbd); dsbd.dwBufferBytes=LoopLen; dsbd.lpwfxFormat=&wfx; DSound->CreateSoundBuffer(&dsbd,&LoopBuffer,NULL); if (LoopBuffer==NULL) return 1; LoopBuffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&DSoundNotify); if (DSoundNotify == NULL) { lprintf("QueryInterface(IID_IDirectSoundNotify) failed\n"); goto out; } seg_played_event = CreateEvent(NULL, 0, 0, NULL); if (seg_played_event == NULL) goto out; for (i = 0; i < NSEGS; i++) { notifies[i].dwOffset = i * LoopSeg; notifies[i].hEventNotify = seg_played_event; } i = DSoundNotify->SetNotificationPositions(NSEGS, notifies); if (i != DS_OK) { lprintf("SetNotificationPositions failed\n"); goto out; } out: LoopBlank(); LoopBuffer->Play(0, 0, DSBPLAY_LOOPING); return 0; }
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; }
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(); }
//----------------------------------------------------------------------------- // Name: CSoundManager::CreateStreaming() // Desc: //----------------------------------------------------------------------------- HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound, LPWSTR strWaveFileName, DWORD dwCreationFlags, GUID guid3DAlgorithm, DWORD dwNotifyCount, DWORD dwNotifySize, HANDLE hNotifyEvent ) { HRESULT hr; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL ) return E_INVALIDARG; LPDIRECTSOUNDBUFFER pDSBuffer = NULL; DWORD dwDSBufferSize = NULL; CWaveFile* pWaveFile = NULL; DSBPOSITIONNOTIFY* aPosNotify = NULL; LPDIRECTSOUNDNOTIFY pDSNotify = NULL; pWaveFile = new CWaveFile(); if( pWaveFile == NULL ) return E_OUTOFMEMORY; pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); // Figure out how big the DirectSound buffer should be dwDSBufferSize = dwNotifySize * dwNotifyCount; // Set up the direct sound buffer. Request the NOTIFY flag, so // that we are notified as the sound buffer plays. Note, that using this flag // may limit the amount of hardware acceleration that can occur. DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) ); dsbd.dwSize = sizeof( DSBUFFERDESC ); dsbd.dwFlags = dwCreationFlags | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = pWaveFile->m_pwfx; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) ) { // If wave format isn't then it will return // either DSERR_BADFORMAT or E_INVALIDARG if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG ) return DXUT_ERR( L"CreateSoundBuffer", hr ); return DXUT_ERR( L"CreateSoundBuffer", hr ); } // Create the notification events, so that we know when to fill // the buffer as the sound plays. if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify, ( VOID** )&pDSNotify ) ) ) { SAFE_DELETE_ARRAY( aPosNotify ); return DXUT_ERR( L"QueryInterface", hr ); } aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ]; if( aPosNotify == NULL ) return E_OUTOFMEMORY; for( DWORD i = 0; i < dwNotifyCount; i++ ) { aPosNotify[i].dwOffset = ( dwNotifySize * i ) + dwNotifySize - 1; aPosNotify[i].hEventNotify = hNotifyEvent; } // Tell DirectSound when to notify us. The notification will come in the from // of signaled events that are handled in WinMain() if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, aPosNotify ) ) ) { SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); return DXUT_ERR( L"SetNotificationPositions", hr ); } SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); // Create the sound *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize ); return S_OK; }
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; }
/*----------------------------------------------------------------------------- Initialize DirectSound -----------------------------------------------------------------------------*/ static BOOL d_init( Sint32 soundType, //Kitao追加。1…通常(ソフトバッファ&ミキシング。環境にもよるかもしれないがベストな音質)。2…ハードバッファ&ソフトミキシング(それなりにいい音が鳴る)。 HWND hWnd, WORD nChannels, WORD nSamplesPerSec, WORD wBitsPerSample, DWORD dwBufSize) // in bytes { int i;//Kitao追加 DSBUFFERDESC dsbd; WAVEFORMATEX waveFormat;//Kitao更新 // Create IDirectSound if (FAILED(DirectSoundCreate(NULL, &_pDS, NULL))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: DIRECTSOUND::DirectSoundCreate() failed. ", "Ootake", MB_OK); //Kitao追加 return FALSE; } /* ** Set coop level to DSSCL_PRIORITY ** ** プライマリバッファのフォーマットを設定できるよう、プライマリ協調レベル ** を設定する。デフォルトのフォーマットに変更を加えない場合、入力の ** フォーマットにかかわりなく、出力は 8 ビット、22 kHz フォーマットになる。 ** IDirectSoundBuffer::SetFormat の呼び出しが失敗しても問題はない点に ** 注意する。DirectSound は単純に、利用できる中で最も近いフォーマットに ** 設定する。 */ if (FAILED(_pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: DIRECTSOUND::SetCooperativeLevel() failed. ", "Ootake", MB_OK); //Kitao更新 return FALSE; } /* ** Get the primary buffer. ** ** プライマリバッファのフォーマットを設定するには、最初に ** DSBUFFERDESC 構造体でそのフォーマットを記述し、次にその記述を ** IDirectSound::CreateSoundBuffer メソッドに渡す。 */ ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; if (FAILED(_pDS->CreateSoundBuffer(&dsbd, &_pDSBP, NULL))) //Kitao更新。v1.03。プライマリは作成した後すぐには開放しないようにした。 { MessageBox(WINMAIN_GetHwnd(), "ERROR: DIRECTSOUND::CreateSoundBuffer() failed. ", "Ootake", MB_OK); //Kitao更新 return FALSE; } /* ** Set primary buffer to desired format. ** ** プライマリバッファオブジェクトを取得した後で、希望のウェーブ ** フォーマットを記述し、その記述を IDirectSoundBuffer::SetFormat ** メソッドに渡す。 */ ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX)); waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = nChannels; waveFormat.wBitsPerSample = wBitsPerSample; waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nSamplesPerSec = nSamplesPerSec; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; if (FAILED(_pDSBP->SetFormat(&waveFormat))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: DIRECTSOUNDBUFFER::SetFormat() failed. ", "Ootake", MB_OK); //Kitao更新 // return FALSE; } // DSBUFFERDESC 構造体を設定する。 ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); dsbd.dwSize = sizeof(DSBUFFERDESC); switch (soundType) //Kitao更新。バッファ設定を選択できるようにした。v1.03。v1.31から2択に絞った。 { case 2: //Kitao更新。1の設定のほうがベストと思うが、バッファをDSBCAPS_STATICでハードメモリ上にしたら音は大人しいが音の解像度が上がる感じでこれも良し。 dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE; break; default: //=1 //Kitao更新。グローバルフォーカスにした。&ハードウェアミキシングだとノイズが入りやすいのでソフトミキシングに。PCエンジンの内蔵音源はこの設定が一番高音が心に響く感じ。 dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE; break; } dsbd.dwBufferBytes = dwBufSize * AOUT_BUFFERRATE; //Kitao更新。AOUT_BUFFERRATE倍ぶん用意する。 dsbd.lpwfxFormat = &waveFormat; // セカンダリバッファを作成する Kitao更新。3chぶん作成。 if (FAILED(_pDS->CreateSoundBuffer(&dsbd, &_pDSB1, NULL))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: AudioOut: Failed creating secondary buffer1. ", "Ootake", MB_OK); //Kitao更新 return FALSE; } if (FAILED(_pDS->CreateSoundBuffer(&dsbd, &_pDSB2, NULL))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: AudioOut: Failed creating secondary buffer2. ", "Ootake", MB_OK); //Kitao更新 return FALSE; } if (FAILED(_pDS->CreateSoundBuffer(&dsbd, &_pDSB3, NULL))) { MessageBox(WINMAIN_GetHwnd(), "ERROR: AudioOut: Failed creating secondary buffer3. ", "Ootake", MB_OK); //Kitao更新 return FALSE; } //Kitao更新。3chぶん用意。[0]をstop notificationとし、[1]〜[AOUT_BUFFERRATE]までを分岐点イベントとした。 for (i =0; i<=AOUT_BUFFERRATE; i++) { _hEvent1[i] = CreateEvent(NULL, FALSE, FALSE, NULL); //v2.36更新。自動リセットするようにした。 _hEvent2[i] = CreateEvent(NULL, FALSE, FALSE, NULL); _hEvent3[i] = CreateEvent(NULL, FALSE, FALSE, NULL); } // DIRECTSOUNDNOTIFY のインタフェイスを得る if (FAILED(_pDSB1->QueryInterface(IID_IDirectSoundNotify, (void**)&_LpDSN1))) return FALSE; if (FAILED(_pDSB2->QueryInterface(IID_IDirectSoundNotify, (void**)&_LpDSN2))) return FALSE; if (FAILED(_pDSB3->QueryInterface(IID_IDirectSoundNotify, (void**)&_LpDSN3))) return FALSE; // Kitao更新。再生が停止されたときの[0]で処理することにして、[1]〜[AOUT_BUFFERRATE]ぶんまでを音の分岐点通知用とした。 // 再生が停止されたときの notification 用 _PosNotify1[0].dwOffset = DSBPN_OFFSETSTOP; _PosNotify1[0].hEventNotify = _hEvent1[0]; _PosNotify2[0].dwOffset = DSBPN_OFFSETSTOP; _PosNotify2[0].hEventNotify = _hEvent2[0]; _PosNotify3[0].dwOffset = DSBPN_OFFSETSTOP; _PosNotify3[0].hEventNotify = _hEvent3[0]; // Kitao更新。バッファの分岐点(先頭含む)通知用 for (i = 1; i<=AOUT_BUFFERRATE; i++) { //_PosNotify1[i].dwOffset = (i-1)*dwBufSize; //v2.36更新。各バッファの切れ目よりも手前(半分の位置)でイベントを起こすようにした。反応が遅いドライバでもこれなら確実で、半分の位置なら早すぎる心配もないようだ。 if (i == 1) { _PosNotify1[i].dwOffset = AOUT_BUFFERRATE*dwBufSize - dwBufSize/2; _PosNotify2[i].dwOffset = AOUT_BUFFERRATE*dwBufSize - dwBufSize/2; _PosNotify3[i].dwOffset = AOUT_BUFFERRATE*dwBufSize - dwBufSize/2; } else { _PosNotify1[i].dwOffset = (i-1)*dwBufSize - dwBufSize/2; _PosNotify2[i].dwOffset = (i-1)*dwBufSize - dwBufSize/2; _PosNotify3[i].dwOffset = (i-1)*dwBufSize - dwBufSize/2; } _PosNotify1[i].hEventNotify = _hEvent1[i]; _PosNotify2[i].hEventNotify = _hEvent2[i]; _PosNotify3[i].hEventNotify = _hEvent3[i]; } // notification を設定する if (FAILED(_LpDSN1->SetNotificationPositions(AOUT_BUFFERRATE+1, _PosNotify1))) return FALSE; if (FAILED(_LpDSN2->SetNotificationPositions(AOUT_BUFFERRATE+1, _PosNotify2))) return FALSE; if (FAILED(_LpDSN3->SetNotificationPositions(AOUT_BUFFERRATE+1, _PosNotify3))) return FALSE; // オーディオバッファを確保する _pAudioBuf1 = (Sint16*)GlobalAlloc(GMEM_FIXED, dwBufSize); _pAudioBuf2 = (Sint16*)GlobalAlloc(GMEM_FIXED, dwBufSize); _pAudioBuf3 = (Sint16*)GlobalAlloc(GMEM_FIXED, dwBufSize); _pAudioBuf0 = (Sint16*)GlobalAlloc(GMEM_FIXED, dwBufSize); if (_pAudioBuf1 == NULL) { d_deinit(); return FALSE; } //Kitao更新。高速化のため、メモリのロックは最初に1度だけ行うようにした。v1.02 _dwBufSize = dwBufSize; _bPlay = FALSE; //スレッドを開始する前にオーディオ初期化完了フラグをたてる。 //[2004.04.28] fixed _bAudioInit = TRUE; //スレッドを作成し実行する _bThreadEnd = FALSE; _hThread = CreateThread(NULL, 0, playback_thread, NULL, 0, &_dwThreadID); if (_hThread == NULL) { d_deinit(); _bAudioInit = FALSE; return FALSE; } //スレッドの優先順位を上げる。v2.36追加。効果が無かったためカット //OpenThread(THREAD_SET_INFORMATION, TRUE, _dwThreadID); //if (SetThreadPriority(_hThread, THREAD_PRIORITY_HIGHEST) == NULL) // return FALSE; 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 *) ¬ify); assert(rv == DS_OK); DSBPOSITIONNOTIFY note; note.dwOffset = (writepos + p1sz + p2sz) % stm->buffer_size; note.hEventNotify = stm->context->streams_event; if (notify->SetNotificationPositions(1, ¬e) != 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 *) ¬ify) != 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; }