void PortAudioInput::run() { PaStream *inputStream = 0; PaError err; //qWarning() << "PortAudioInput::run() BEGIN ==="; if (!PortAudioSystem::initStream(&inputStream, g.s.iPortAudioInput, iFrameSize, reinterpret_cast<int *>(&iMicChannels), true)) return; // PA init or stream opening failed, we will give up iMicFreq = SAMPLE_RATE; eMicFormat = SampleShort; initializeMixer(); short inBuffer[iMicLength * iMicChannels]; // depend on the stream having started bRunning = PortAudioSystem::startStream(inputStream); while (bRunning) { err = Pa_ReadStream(inputStream, inBuffer, iMicLength); if (err == paNoError) { addMic(inBuffer, iMicLength); } else if (err == paInputOverflowed) { qWarning("PortAudioInput: Overflow on PortAudio input-stream, we lost incoming data!"); } else { // other error, aborg qWarning("PortAudioInput: Could not read from PortAudio stream, error %s", Pa_GetErrorText(err)); bRunning = false; } } // ignoring return value on purpose, we cannot do anything about it anyway PortAudioSystem::stopStream(inputStream); // ignoring return value on purpose, we cannot do anything about it anyway PortAudioSystem::terminateStream(inputStream); inputStream = 0; // just for gdb sessions ;) }
//TODO: redo this without busy waiting if possible void PortAudioOutput::run() { PaStream *outputStream = 0; PaError err = paNoError; int chans = 0; bool zero = true; if (!PortAudioSystem::initStream(&outputStream, g.s.iPortAudioOutput, iFrameSize, &chans, false)) return; // PA initialization or stream opening failed, we will give up const unsigned int cmask[2] = { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT }; iChannels = chans; iMixerFreq = SAMPLE_RATE; eSampleFormat = SampleShort; initializeMixer(cmask); short outBuffer[iFrameSize * iChannels]; while (bRunning) { bool avail = true; if (zero) memset(outBuffer, 0, sizeof(short) * iFrameSize * iChannels); else avail = mix(outBuffer, iFrameSize); if (avail) { if (! PortAudioSystem::startStream(outputStream)) { bRunning = false; break; } err = Pa_WriteStream(outputStream, outBuffer, iFrameSize); if (err == paOutputUnderflowed) { qWarning("PortAudioOutput: Output underflowed. Attempting graceful recovery."); zero = true; continue; } else if (err != paNoError) { qWarning("PortAudioOutput: Could not write to PortAudio stream, error %s", Pa_GetErrorText(err)); bRunning = false; } } else { if (! PortAudioSystem::stopStream(outputStream)) bRunning = false; this->msleep(20); } zero = false; } PortAudioSystem::terminateStream(outputStream); outputStream = NULL; // just for gdb sessions }
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; LPDIRECTSOUNDNOTIFY8 pDSNotify = NULL; DWORD dwBufferSize; DWORD dwLastWritePos; DWORD dwLastPlayPos; DWORD dwTotalPlayPos; 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(g.mw->winId(), 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_MONO; 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, 1); 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 = ns; 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; } dwBufferSize = nbytes1 + nbytes2; 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; } dwLastWritePos = 0; dwLastPlayPos = 0; dwTotalPlayPos = 0; 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(); }