FORCE_ALIGN static int PlaybackThreadProc(void *arg) { ALCdevice *Device = (ALCdevice*)arg; WinMMData *data = Device->ExtraData; WAVEHDR *WaveHdr; MSG msg; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); while(GetMessage(&msg, NULL, 0, 0)) { if(msg.message != WOM_DONE) continue; if(data->killNow) { if(ReadRef(&data->WaveBuffersCommitted) == 0) break; continue; } WaveHdr = ((WAVEHDR*)msg.lParam); aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength / data->Format.nBlockAlign); // Send buffer back to play more data waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR)); IncrementRef(&data->WaveBuffersCommitted); } return 0; }
void g_processBuffer(void *buffer, size_t size) { if (g_mixerRunning) { aluMixData(g_device, buffer, size); } }
/* PlaybackThreadProc Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its audio data. */ static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter) { ALCdevice *pDevice = (ALCdevice*)lpParameter; WinMMData *pData = pDevice->ExtraData; LPWAVEHDR pWaveHdr; ALuint FrameSize; MSG msg; FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); while(GetMessage(&msg, NULL, 0, 0)) { if(msg.message != WOM_DONE || pData->bWaveShutdown) continue; pWaveHdr = ((LPWAVEHDR)msg.lParam); aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize); // Send buffer back to play more data waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR)); InterlockedIncrement(&pData->lWaveBuffersCommitted); } // Signal Wave Thread completed event if(pData->hWaveThreadEvent) SetEvent(pData->hWaveThreadEvent); ExitThread(0); return 0; }
static ALuint sndio_proc(ALvoid *ptr) { ALCdevice *device = ptr; sndio_data *data = device->ExtraData; ALsizei frameSize; size_t wrote; SetRTPriority(); frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); while(!data->killNow && device->Connected) { ALsizei len = data->data_size; ALubyte *WritePtr = data->mix_data; aluMixData(device, WritePtr, len/frameSize); while(len > 0 && !data->killNow) { wrote = sio_write(data->sndHandle, WritePtr, len); if(wrote == 0) { ERR("sio_write failed\n"); aluHandleDisconnect(device); break; } len -= wrote; WritePtr += wrote; } } return 0; }
FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg) { ALCwinmmPlayback *self = arg; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; WAVEHDR *WaveHdr; MSG msg; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); while(GetMessage(&msg, NULL, 0, 0)) { if(msg.message != WOM_DONE) continue; if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire)) { if(ReadRef(&self->WaveBuffersCommitted) == 0) break; continue; } WaveHdr = ((WAVEHDR*)msg.lParam); ALCwinmmPlayback_lock(self); aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength / self->Format.nBlockAlign); ALCwinmmPlayback_unlock(self); // Send buffer back to play more data waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR)); IncrementRef(&self->WaveBuffersCommitted); } return 0; }
// PulseAudio I/O Callbacks //{{{ static void stream_write_callback( pa_stream* stream, size_t len, void* pdata ) //{{{ { ALCdevice* Device = pdata; pulse_data* data = Device->ExtraData; while ( len > 0 ) { size_t newlen = len; void* buf; pa_free_cb_t free_func = NULL; #if PA_CHECK_VERSION(0,9,16) if ( !ppa_stream_begin_write || ppa_stream_begin_write( stream, &buf, &newlen ) < 0 ) #endif { buf = ppa_xmalloc( newlen ); free_func = ppa_xfree; } aluMixData( Device, buf, newlen / data->frame_size ); ppa_stream_write( stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE ); len -= newlen; } } //}}}
static void *playback_function(void * context) { LOGV("playback_function started"); outputBuffer_t *buffer = NULL; SLresult result; struct timespec ts; int rc; assert(NULL != context); ALCdevice *pDevice = (ALCdevice *) context; opesles_data_t *devState = (opesles_data_t *) pDevice->ExtraData; unsigned int bufferIndex = devState->lastBufferMixed; ALint frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); // Show a sensible name for the thread in debug tools prctl(PR_SET_NAME, (unsigned long)"OpenAL/sl/m", 0, 0, 0); while (1) { if (devState->threadShouldRun == 0) { return NULL; } bufferIndex = (++bufferIndex) % bufferCount; buffer = &(devState->outputBuffers[bufferIndex]); pthread_mutex_lock(&(buffer->mutex)); while (1) { if (devState->threadShouldRun == 0) { pthread_mutex_unlock(&(buffer->mutex)); return NULL; } // This is a little hacky, but here we avoid mixing too much data if (buffer->state == OUTPUT_BUFFER_STATE_FREE) { int i = (bufferIndex - premixCount) % bufferCount; outputBuffer_t *buffer1 = &(devState->outputBuffers[i]); if (buffer1->state == OUTPUT_BUFFER_STATE_ENQUEUED || buffer1->state == OUTPUT_BUFFER_STATE_FREE) { break; } } // No buffer available, wait for a buffer to become available // or until playback is stopped/suspended clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += 5000000; rc = pthread_cond_timedwait(&(buffer->cond), &(buffer->mutex), &ts); } devState->threadIsReady = 1; aluMixData(pDevice, buffer->buffer, bufferSize/frameSize); buffer->state = OUTPUT_BUFFER_STATE_MIXED; pthread_cond_signal(&(buffer->cond)); pthread_mutex_unlock(&(buffer->mutex)); devState->lastBufferMixed = bufferIndex; } }
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo), const PaStreamCallbackFlags UNUSED(statusFlags), void *userData) { ALCdevice *device = (ALCdevice*)userData; aluMixData(device, outputBuffer, framesPerBuffer); return 0; }
static ALuint ALSANoMMapProc(ALvoid *ptr) { ALCdevice *Device = (ALCdevice*)ptr; alsa_data *data = (alsa_data*)Device->ExtraData; snd_pcm_sframes_t avail; char *WritePtr; SetRTPriority(); while(!data->killNow) { int state = verify_state(data->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); aluHandleDisconnect(Device); break; } WritePtr = data->buffer; avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1); aluMixData(Device, WritePtr, avail); while(avail > 0) { int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); switch (ret) { case -EAGAIN: continue; case -ESTRPIPE: case -EPIPE: case -EINTR: ret = snd_pcm_recover(data->pcmHandle, ret, 1); if(ret < 0) avail = 0; break; default: if (ret >= 0) { WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); avail -= ret; } break; } if (ret < 0) { ret = snd_pcm_prepare(data->pcmHandle); if(ret < 0) break; } } } return 0; }
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { ALCdevice *device = (ALCdevice*)inRefCon; ca_data *data = (ca_data*)device->ExtraData; aluMixData(device, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize / data->frameSize); return noErr; }
// PulseAudio I/O Callbacks //{{{ static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ { ALCdevice *Device = pdata; pulse_data *data = Device->ExtraData; void *buf = ppa_xmalloc0(data->attr.minreq); (void)len; aluMixData(Device, buf, data->attr.minreq/data->frame_size); ppa_stream_write(stream, buf, data->attr.minreq, ppa_xfree, 0, PA_SEEK_RELATIVE); } //}}}
/* this callback handler is called every time a buffer finishes playing */ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) { ALCdevice *Device = context; osl_data *data = Device->ExtraData; SLresult result; aluMixData(Device, data->buffer, data->bufferSize/data->frameSize); result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize); PRINTERR(result, "bq->Enqueue"); }
static ALuint ALSANoMMapProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; alsa_data *data = (alsa_data*)pDevice->ExtraData; snd_pcm_sframes_t avail; char *WritePtr; while(!data->killNow) { int state = verify_state(data->pcmHandle); if(state < 0) { AL_PRINT("Invalid state detected: %s\n", psnd_strerror(state)); aluHandleDisconnect(pDevice); break; } WritePtr = data->buffer; avail = data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1); aluMixData(pDevice, WritePtr, avail); while(avail > 0) { int ret = psnd_pcm_writei(data->pcmHandle, WritePtr, avail); switch (ret) { case -EAGAIN: continue; case -ESTRPIPE: while((ret=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN) Sleep(1); break; case -EPIPE: break; default: if (ret >= 0) { WritePtr += psnd_pcm_frames_to_bytes(data->pcmHandle, ret); avail -= ret; } break; } if (ret < 0) { ret = psnd_pcm_prepare(data->pcmHandle); if(ret < 0) break; } } } return 0; }
// PulseAudio I/O Callbacks //{{{ static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ { ALCdevice *Device = pdata; pulse_data *data = Device->ExtraData; len -= len%data->attr.minreq; if(len > 0) { void *buf = ppa_xmalloc0(len); aluMixData(Device, buf, len/data->frame_size); ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE); } } //}}}
static int pa_callback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags, void *userData) { ALCdevice *device = (ALCdevice*)userData; (void)inputBuffer; (void)timeInfo; (void)statusFlags; aluMixData(device, outputBuffer, framesPerBuffer); return 0; }
static void* thread_function(void* arg) { ALCdevice* device = (ALCdevice*)arg; AndroidData* data = (AndroidData*)device->ExtraData; JNIEnv* env; (*javaVM)->AttachCurrentThread(javaVM, &env, NULL); (*env)->PushLocalFrame(env, 2); int sampleRateInHz = device->Frequency; int channelConfig = aluChannelsFromFormat(device->Format) == 1 ? CHANNEL_CONFIGURATION_MONO : CHANNEL_CONFIGURATION_STEREO; int audioFormat = aluBytesFromFormat(device->Format) == 1 ? ENCODING_PCM_8BIT : ENCODING_PCM_16BIT; int bufferSizeInBytes = (*env)->CallStaticIntMethod(env, cAudioTrack, mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat) / 4; int bufferSizeInSamples = bufferSizeInBytes / aluFrameSizeFromFormat(device->Format); jobject track = (*env)->NewObject(env, cAudioTrack, mAudioTrack, STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, device->NumUpdates * bufferSizeInBytes, MODE_STREAM); (*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mPlay); jarray buffer = (*env)->NewByteArray(env, bufferSizeInBytes); while (data->running) { void* pBuffer = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL); if (pBuffer) { aluMixData(device, pBuffer, bufferSizeInSamples); (*env)->ReleasePrimitiveArrayCritical(env, buffer, pBuffer, 0); (*env)->CallNonvirtualIntMethod(env, track, cAudioTrack, mWrite, buffer, 0, bufferSizeInBytes); } else { AL_PRINT("Failed to get pointer to array bytes"); } } (*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mStop); (*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mRelease); (*env)->PopLocalFrame(env, NULL); (*javaVM)->DetachCurrentThread(javaVM); return NULL; }
static int ALCnullBackend_mixerProc(void *ptr) { ALCnullBackend *self = (ALCnullBackend*)ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; struct timespec now, start; ALuint64 avail, done; const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / device->Frequency / 2); SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); done = 0; if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) { ERR("Failed to get starting time\n"); return 1; } while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) { if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) { ERR("Failed to get current time\n"); return 1; } avail = (now.tv_sec - start.tv_sec) * device->Frequency; avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; if(avail < done) { /* Oops, time skipped backwards. Reset the number of samples done * with one update available since we (likely) just came back from * sleeping. */ done = avail - device->UpdateSize; } if(avail-done < device->UpdateSize) al_nssleep(restTime); else while(avail-done >= device->UpdateSize) { ALCnullBackend_lock(self); aluMixData(device, NULL, device->UpdateSize); ALCnullBackend_unlock(self); done += device->UpdateSize; } } return 0; }
/* this callback handler is called every time a buffer finishes playing */ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context) { ALCdevice *Device = context; osl_data *data = Device->ExtraData; ALvoid *buf; SLresult result; buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize; aluMixData(Device, buf, data->bufferSize/data->frameSize); result = VCALL(bq,Enqueue)(buf, data->bufferSize); PRINTERR(result, "bq->Enqueue"); data->curBuffer = (data->curBuffer+1) % Device->NumUpdates; }
static ALuint PulseProc(ALvoid *param) { ALCdevice *Device = param; pulse_data *data = Device->ExtraData; ssize_t len; SetRTPriority(); pa_threaded_mainloop_lock(data->loop); do { len = (Device->Connected ? pa_stream_writable_size(data->stream) : 0); len -= len%(Device->UpdateSize*data->frame_size); if(len == 0) { pa_threaded_mainloop_wait(data->loop); continue; } while(len > 0) { size_t newlen = len; void *buf; pa_free_cb_t free_func = NULL; #if PA_CHECK_VERSION(0,9,16) if(!pa_stream_begin_write || pa_stream_begin_write(data->stream, &buf, &newlen) < 0) #endif { buf = pa_xmalloc(newlen); free_func = pa_xfree; } pa_threaded_mainloop_unlock(data->loop); aluMixData(Device, buf, newlen/data->frame_size); pa_threaded_mainloop_lock(data->loop); pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); len -= newlen; } } while(Device->Connected && !data->killNow); pa_threaded_mainloop_unlock(data->loop); return 0; }
COM_DECLSPEC_NOTHROW void STDMETHODCALLTYPE XAudio2SourceVoiceCallback::OnVoiceProcessingPassStart(UINT32 numBytesRequired) { XAudio2Data* data = (XAudio2Data*)device->ExtraData; LONG running; if (data->buffer == NULL) { CreateBuffer(data, numBytesRequired); SetEvent(this->callbackEvent); } else { InterlockedExchange(&running, data->running); if (running) { HRESULT hr; if (data->bufferSize < numBytesRequired) CreateBuffer(data, numBytesRequired); size_t samples = numBytesRequired / data->frameSize; size_t bufferRegionSize = samples * data->frameSize;//must be divisible by frame size aluMixData(device, data->buffer, samples);//mix data #if 0//audio glitch simulation for (int i = 0; i < 10000000; ++i) { int a = 0; } #endif /*------------submit buffer region-------------*/ XAUDIO2_BUFFER bufferRegionToSubmit; memset(&bufferRegionToSubmit, 0, sizeof(bufferRegionToSubmit)); bufferRegionToSubmit.AudioBytes = bufferRegionSize; bufferRegionToSubmit.pAudioData = data->buffer; hr = data->sourceVoice->SubmitSourceBuffer(&bufferRegionToSubmit); if (FAILED(hr)) { ERR("SubmitSourceBuffer() failed: 0x%08lx\n", hr); } }//if (running) }//if (data->buffer == NULL) }
static ALuint OSSProc( ALvoid* ptr ) { ALCdevice* pDevice = ( ALCdevice* )ptr; oss_data* data = ( oss_data* )pDevice->ExtraData; ALint frameSize; ssize_t wrote; SetRTPriority(); frameSize = aluFrameSizeFromFormat( pDevice->Format ); while ( !data->killNow && pDevice->Connected ) { ALint len = data->data_size; ALubyte* WritePtr = data->mix_data; aluMixData( pDevice, WritePtr, len / frameSize ); while ( len > 0 && !data->killNow ) { wrote = write( data->fd, WritePtr, len ); if ( wrote < 0 ) { if ( errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR ) { AL_PRINT( "write failed: %s\n", strerror( errno ) ); aluHandleDisconnect( pDevice ); break; } Sleep( 1 ); continue; } len -= wrote; WritePtr += wrote; } } return 0; }
static ALuint NullProc(ALvoid *ptr) { ALCdevice *Device = (ALCdevice*)ptr; null_data *data = (null_data*)Device->ExtraData; ALuint now, start; ALuint64 avail, done; const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 / Device->Frequency / 2; done = 0; start = timeGetTime(); while(!data->killNow && Device->Connected) { now = timeGetTime(); avail = (ALuint64)(now-start) * Device->Frequency / 1000; if(avail < done) { /* Timer wrapped (50 days???). Add the remainder of the cycle to * the available count and reset the number of samples done */ avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done; done = 0; } if(avail-done < Device->UpdateSize) { Sleep(restTime); continue; } while(avail-done >= Device->UpdateSize) { aluMixData(Device, NULL, Device->UpdateSize); done += Device->UpdateSize; } } return 0; }
static ALuint OSSProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; oss_data *data = (oss_data*)pDevice->ExtraData; int remaining = 0; int wrote; while(!data->killNow) { int len = data->data_size - remaining; if(len > 0) { SuspendContext(NULL); aluMixData(pDevice->Context, data->mix_data+remaining, len, pDevice->Format); ProcessContext(NULL); } remaining += len; wrote = write(data->fd, data->mix_data, remaining); if(wrote < 0) { AL_PRINT("write failed: %s\n", strerror(errno)); remaining = 0; } else if(wrote > 0) { remaining -= wrote; if(remaining > 0) memmove(data->mix_data, data->mix_data+wrote, remaining); } else Sleep(1); } return 0; }
static ALuint ALSAProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; alsa_data *data = (alsa_data*)pDevice->ExtraData; const snd_pcm_channel_area_t *areas = NULL; snd_pcm_sframes_t avail, commitres; snd_pcm_uframes_t offset, frames; char *WritePtr; int err; while(!data->killNow) { int state = verify_state(data->pcmHandle); if(state < 0) { AL_PRINT("Invalid state detected: %s\n", psnd_strerror(state)); aluHandleDisconnect(pDevice); break; } avail = psnd_pcm_avail_update(data->pcmHandle); if(avail < 0) { AL_PRINT("available update failed: %s\n", psnd_strerror(avail)); continue; } // make sure there's frames to process if(avail >= 0 && avail < (snd_pcm_sframes_t)pDevice->UpdateSize) { if(state != SND_PCM_STATE_RUNNING) { err = psnd_pcm_start(data->pcmHandle); if(err < 0) { AL_PRINT("start failed: %s\n", psnd_strerror(err)); continue; } } if(psnd_pcm_wait(data->pcmHandle, 1000) == 0) AL_PRINT("Wait timeout... buffer size too low?\n"); continue; } avail -= avail%pDevice->UpdateSize; // it is possible that contiguous areas are smaller, thus we use a loop while(avail > 0) { frames = avail; err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); if(err < 0) { AL_PRINT("mmap begin error: %s\n", psnd_strerror(err)); break; } WritePtr = (char*)areas->addr + (offset * areas->step / 8); aluMixData(pDevice, WritePtr, frames); commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames); if(commitres < 0 || (commitres-frames) != 0) { AL_PRINT("mmap commit error: %s\n", psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); break; } avail -= frames; } } return 0; }
static ALuint PulseProc(ALvoid *param) { ALCdevice *Device = param; pulse_data *data = Device->ExtraData; ALuint buffer_size; ALint update_size; size_t frame_size; ssize_t len; SetRTPriority(); pa_threaded_mainloop_lock(data->loop); frame_size = pa_frame_size(&data->spec); update_size = Device->UpdateSize * frame_size; /* Sanitize buffer metrics, in case we actually have less than what we * asked for. */ buffer_size = minu(update_size*Device->NumUpdates, data->attr.tlength); update_size = minu(update_size, buffer_size/2); do { len = pa_stream_writable_size(data->stream) - data->attr.tlength + buffer_size; if(len < update_size) { if(pa_stream_is_corked(data->stream) == 1) { pa_operation *o; o = pa_stream_cork(data->stream, 0, NULL, NULL); if(o) pa_operation_unref(o); } pa_threaded_mainloop_unlock(data->loop); Sleep(1); pa_threaded_mainloop_lock(data->loop); continue; } len -= len%update_size; while(len > 0) { size_t newlen = len; void *buf; pa_free_cb_t free_func = NULL; #if PA_CHECK_VERSION(0,9,16) if(!pa_stream_begin_write || pa_stream_begin_write(data->stream, &buf, &newlen) < 0) #endif { buf = pa_xmalloc(newlen); free_func = pa_xfree; } pa_threaded_mainloop_unlock(data->loop); aluMixData(Device, buf, newlen/frame_size); pa_threaded_mainloop_lock(data->loop); pa_stream_write(data->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE); len -= newlen; } } while(!data->killNow && Device->Connected); pa_threaded_mainloop_unlock(data->loop); return 0; }
static ALuint DSoundProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; DSoundData *pData = (DSoundData*)pDevice->ExtraData; DSBCAPS DSBCaps; DWORD LastCursor = 0; DWORD PlayCursor; VOID *WritePtr1, *WritePtr2; DWORD WriteCnt1, WriteCnt2; DWORD FrameSize; DWORD FragSize; DWORD avail; HRESULT err; SetRTPriority(); memset(&DSBCaps, 0, sizeof(DSBCaps)); DSBCaps.dwSize = sizeof(DSBCaps); err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps); if(FAILED(err)) { AL_PRINT("Failed to get buffer caps: 0x%lx\n", err); aluHandleDisconnect(pDevice); return 1; } FrameSize = aluFrameSizeFromFormat(pDevice->Format); FragSize = pDevice->UpdateSize * FrameSize; IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL); while(!pData->killNow) { // Get current play and write cursors IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL); avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; if(avail < FragSize) { Sleep(1); continue; } avail -= avail%FragSize; // Lock output buffer WriteCnt1 = 0; WriteCnt2 = 0; err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); // If the buffer is lost, restore it, play and lock if(err == DSERR_BUFFERLOST) { err = IDirectSoundBuffer_Restore(pData->DSsbuffer); if(SUCCEEDED(err)) err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING); if(SUCCEEDED(err)) err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); } // Successfully locked the output buffer if(SUCCEEDED(err)) { // If we have an active context, mix data directly into output buffer otherwise fill with silence aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize); aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize); // Unlock output buffer only when successfully locked IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); } else AL_PRINT("Buffer lock error: %#lx\n", err); // Update old write cursor location LastCursor += WriteCnt1+WriteCnt2; LastCursor %= DSBCaps.dwBufferBytes; } return 0; }
static ALuint WaveProc( ALvoid* ptr ) { ALCdevice* pDevice = ( ALCdevice* )ptr; wave_data* data = ( wave_data* )pDevice->ExtraData; ALuint frameSize; ALuint now, last; size_t fs; ALuint avail; union { short s; char b[sizeof( short )]; } uSB; uSB.s = 1; frameSize = aluFrameSizeFromFormat( pDevice->Format ); last = timeGetTime() << 8; while ( !data->killNow && pDevice->Connected ) { now = timeGetTime() << 8; avail = ( ALuint64 )( now - last ) * pDevice->Frequency / ( 1000 << 8 ); if ( avail < pDevice->UpdateSize ) { Sleep( 1 ); continue; } while ( avail >= pDevice->UpdateSize ) { aluMixData( pDevice, data->buffer, pDevice->UpdateSize ); if ( uSB.b[0] != 1 ) { ALubyte* bytes = data->buffer; ALuint i; if ( aluBytesFromFormat( pDevice->Format ) == 1 ) { for ( i = 0; i < data->size; i++ ) { fputc( bytes[i], data->f ); } } else if ( aluBytesFromFormat( pDevice->Format ) == 2 ) { for ( i = 0; i < data->size; i++ ) { fputc( bytes[i ^ 1], data->f ); } } else if ( aluBytesFromFormat( pDevice->Format ) == 4 ) { for ( i = 0; i < data->size; i++ ) { fputc( bytes[i ^ 3], data->f ); } } } else fs = fwrite( data->buffer, frameSize, pDevice->UpdateSize, data->f ); if ( ferror( data->f ) ) { AL_PRINT( "Error writing to file\n" ); aluHandleDisconnect( pDevice ); break; } avail -= pDevice->UpdateSize; last += ( ALuint64 )pDevice->UpdateSize * ( 1000 << 8 ) / pDevice->Frequency; } } return 0; }
static ALuint DSoundPlaybackProc(ALvoid *ptr) { ALCdevice *Device = (ALCdevice*)ptr; DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData; DSBCAPS DSBCaps; DWORD LastCursor = 0; DWORD PlayCursor; VOID *WritePtr1, *WritePtr2; DWORD WriteCnt1, WriteCnt2; BOOL Playing = FALSE; DWORD FrameSize; DWORD FragSize; DWORD avail; HRESULT err; SetRTPriority(); memset(&DSBCaps, 0, sizeof(DSBCaps)); DSBCaps.dwSize = sizeof(DSBCaps); err = IDirectSoundBuffer_GetCaps(data->Buffer, &DSBCaps); if(FAILED(err)) { ERR("Failed to get buffer caps: 0x%lx\n", err); ALCdevice_Lock(Device); aluHandleDisconnect(Device); ALCdevice_Unlock(Device); return 1; } FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType); FragSize = Device->UpdateSize * FrameSize; IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &LastCursor, NULL); while(!data->killNow) { // Get current play cursor IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &PlayCursor, NULL); avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes; if(avail < FragSize) { if(!Playing) { err = IDirectSoundBuffer_Play(data->Buffer, 0, 0, DSBPLAY_LOOPING); if(FAILED(err)) { ERR("Failed to play buffer: 0x%lx\n", err); ALCdevice_Lock(Device); aluHandleDisconnect(Device); ALCdevice_Unlock(Device); return 1; } Playing = TRUE; } avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE); if(avail != WAIT_OBJECT_0) ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); continue; } avail -= avail%FragSize; // Lock output buffer WriteCnt1 = 0; WriteCnt2 = 0; err = IDirectSoundBuffer_Lock(data->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); // If the buffer is lost, restore it and lock if(err == DSERR_BUFFERLOST) { WARN("Buffer lost, restoring...\n"); err = IDirectSoundBuffer_Restore(data->Buffer); if(SUCCEEDED(err)) { Playing = FALSE; LastCursor = 0; err = IDirectSoundBuffer_Lock(data->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0); } } // Successfully locked the output buffer if(SUCCEEDED(err)) { // If we have an active context, mix data directly into output buffer otherwise fill with silence aluMixData(Device, WritePtr1, WriteCnt1/FrameSize); aluMixData(Device, WritePtr2, WriteCnt2/FrameSize); // Unlock output buffer only when successfully locked IDirectSoundBuffer_Unlock(data->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); } else { ERR("Buffer lock error: %#lx\n", err); ALCdevice_Lock(Device); aluHandleDisconnect(Device); ALCdevice_Unlock(Device); return 1; } // Update old write cursor location LastCursor += WriteCnt1+WriteCnt2; LastCursor %= DSBCaps.dwBufferBytes; } return 0; }
static ALuint WaveProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; wave_data *data = (wave_data*)pDevice->ExtraData; ALuint frameSize; ALuint now, start; ALuint64 avail, done; size_t fs; union { short s; char b[sizeof(short)]; } uSB; const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 / pDevice->Frequency / 2; uSB.s = 1; frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); done = 0; start = timeGetTime(); while(!data->killNow && pDevice->Connected) { now = timeGetTime(); avail = (ALuint64)(now-start) * pDevice->Frequency / 1000; if(avail < done) { /* Timer wrapped. Add the remainder of the cycle to the available * count and reset the number of samples done */ avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done; done = 0; } if(avail-done < pDevice->UpdateSize) { Sleep(restTime); continue; } while(avail-done >= pDevice->UpdateSize) { aluMixData(pDevice, data->buffer, pDevice->UpdateSize); done += pDevice->UpdateSize; if(uSB.b[0] != 1) { ALuint bytesize = BytesFromDevFmt(pDevice->FmtType); ALubyte *bytes = data->buffer; ALuint i; if(bytesize == 1) { for(i = 0;i < data->size;i++) fputc(bytes[i], data->f); } else if(bytesize == 2) { for(i = 0;i < data->size;i++) fputc(bytes[i^1], data->f); } else if(bytesize == 4) { for(i = 0;i < data->size;i++) fputc(bytes[i^3], data->f); } } else fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize, data->f); if(ferror(data->f)) { AL_PRINT("Error writing to file\n"); aluHandleDisconnect(pDevice); break; } } } return 0; }
FORCE_ALIGN static int qsa_proc_playback(void* ptr) { ALCdevice* device=(ALCdevice*)ptr; qsa_data* data=(qsa_data*)device->ExtraData; char* write_ptr; int avail; snd_pcm_channel_status_t status; struct sched_param param; fd_set wfds; int selectret; struct timeval timeout; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); /* Increase default 10 priority to 11 to avoid jerky sound */ SchedGet(0, 0, ¶m); param.sched_priority=param.sched_curpriority+1; SchedSet(0, 0, SCHED_NOCHANGE, ¶m); ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType); while (!data->killNow) { ALint len=data->size; write_ptr=data->buffer; avail=len/frame_size; aluMixData(device, write_ptr, avail); while (len>0 && !data->killNow) { FD_ZERO(&wfds); FD_SET(data->audio_fd, &wfds); timeout.tv_sec=2; timeout.tv_usec=0; /* Select also works like time slice to OS */ selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout); switch (selectret) { case -1: aluHandleDisconnect(device); return 1; case 0: break; default: if (FD_ISSET(data->audio_fd, &wfds)) { break; } break; } int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len); if (wrote<=0) { if ((errno==EAGAIN) || (errno==EWOULDBLOCK)) { continue; } memset(&status, 0, sizeof (status)); status.channel=SND_PCM_CHANNEL_PLAYBACK; snd_pcm_plugin_status(data->pcmHandle, &status); /* we need to reinitialize the sound channel if we've underrun the buffer */ if ((status.status==SND_PCM_STATUS_UNDERRUN) || (status.status==SND_PCM_STATUS_READY)) { if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0) { aluHandleDisconnect(device); break; } } } else { write_ptr+=wrote; len-=wrote; } } } return 0; }