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; }
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 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; }
static ALuint OSSCaptureProc(ALvoid *ptr) { ALCdevice *pDevice = (ALCdevice*)ptr; oss_data *data = (oss_data*)pDevice->ExtraData; int frameSize; int amt; SetRTPriority(); frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); while(!data->killNow) { amt = read(data->fd, data->mix_data, data->data_size); if(amt < 0) { ERR("read failed: %s\n", strerror(errno)); aluHandleDisconnect(pDevice); break; } if(amt == 0) { Sleep(1); continue; } if(data->doCapture) WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); } 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 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; }
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; }
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; }
/* 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); SetRTPriority(); while(GetMessage(&msg, NULL, 0, 0)) { if(msg.message != WOM_DONE) continue; if(pData->bWaveShutdown) { if(pData->lWaveBuffersCommitted == 0) break; 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 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; }
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; }
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 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 int ALCopenslPlayback_mixerProc(void *arg) { ALCopenslPlayback *self = arg; ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice; SLAndroidSimpleBufferQueueItf bufferQueue; ll_ringbuffer_data_t data[2]; SLPlayItf player; SLresult result; size_t padding; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue); PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); if(SL_RESULT_SUCCESS == result) { result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player); PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY"); } if(SL_RESULT_SUCCESS != result) { ALCopenslPlayback_lock(self); aluHandleDisconnect(device); ALCopenslPlayback_unlock(self); return 1; } /* NOTE: The ringbuffer will be larger than the desired buffer metrics. * Calculate the amount of extra space so we know how much to keep unused. */ padding = ll_ringbuffer_write_space(self->mRing) - device->NumUpdates; ALCopenslPlayback_lock(self); while(ATOMIC_LOAD_SEQ(&self->mKillNow) == AL_FALSE && device->Connected) { size_t todo, len0, len1; if(ll_ringbuffer_write_space(self->mRing) <= padding) { SLuint32 state = 0; result = VCALL(player,GetPlayState)(&state); PRINTERR(result, "player->GetPlayState"); if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING) { result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING); PRINTERR(result, "player->SetPlayState"); } if(SL_RESULT_SUCCESS != result) { aluHandleDisconnect(device); break; } /* NOTE: Unfortunately, there is an unavoidable race condition * here. It's possible for the process() method to run, updating * the read pointer and signaling the condition variable, in * between checking the write size and waiting for the condition * variable here. This will cause alcnd_wait to wait until the * *next* process() invocation signals the condition variable * again. * * However, this should only happen if the mixer is running behind * anyway (as ideally we'll be asleep in alcnd_wait by the time the * process() method is invoked), so this behavior is not completely * unwarranted. It's unfortunate since it'll be wasting time * sleeping that could be used to catch up, but there's no way * around it without blocking in the process() method. */ if(ll_ringbuffer_write_space(self->mRing) <= padding) { alcnd_wait(&self->mCond, &STATIC_CAST(ALCbackend,self)->mMutex); continue; } } ll_ringbuffer_get_write_vector(self->mRing, data); todo = data[0].len+data[1].len - padding; len0 = minu(todo, data[0].len); len1 = minu(todo-len0, data[1].len); aluMixData(device, data[0].buf, len0*device->UpdateSize); for(size_t i = 0;i < len0;i++) { result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize); PRINTERR(result, "bufferQueue->Enqueue"); if(SL_RESULT_SUCCESS == result) ll_ringbuffer_write_advance(self->mRing, 1); data[0].buf += device->UpdateSize*self->mFrameSize; } if(len1 > 0) { aluMixData(device, data[1].buf, len1*device->UpdateSize); for(size_t i = 0;i < len1;i++) { result = VCALL(bufferQueue,Enqueue)(data[1].buf, device->UpdateSize*self->mFrameSize); PRINTERR(result, "bufferQueue->Enqueue"); if(SL_RESULT_SUCCESS == result) ll_ringbuffer_write_advance(self->mRing, 1); data[1].buf += device->UpdateSize*self->mFrameSize; } } } ALCopenslPlayback_unlock(self); return 0; }
static int ALCsolarisBackend_mixerProc(void *ptr) { ALCsolarisBackend *self = ptr; ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; struct timeval timeout; ALubyte *write_ptr; ALint frame_size; ALint to_write; ssize_t wrote; fd_set wfds; int sret; SetRTPriority(); althrd_setname(althrd_current(), MIXER_THREAD_NAME); frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder); ALCsolarisBackend_lock(self); while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && ATOMIC_LOAD(&device->Connected, almemory_order_acquire) != DeviceConnect_Disconnected) { FD_ZERO(&wfds); FD_SET(self->fd, &wfds); timeout.tv_sec = 1; timeout.tv_usec = 0; ALCsolarisBackend_unlock(self); sret = select(self->fd+1, NULL, &wfds, NULL, &timeout); ALCsolarisBackend_lock(self); if(sret < 0) { if(errno == EINTR) continue; ERR("select failed: %s\n", strerror(errno)); aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno)); break; } else if(sret == 0) { WARN("select timeout\n"); continue; } write_ptr = self->mix_data; to_write = self->data_size; aluMixData(device, write_ptr, to_write/frame_size); while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow)) { wrote = write(self->fd, write_ptr, to_write); if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; ERR("write failed: %s\n", strerror(errno)); aluHandleDisconnect(device, "Failed to write playback samples: %s", strerror(errno)); break; } to_write -= wrote; write_ptr += wrote; } } ALCsolarisBackend_unlock(self); return 0; }
static ALuint ALSAProc(ALvoid *ptr) { ALCdevice *Device = (ALCdevice*)ptr; alsa_data *data = (alsa_data*)Device->ExtraData; const snd_pcm_channel_area_t *areas = NULL; snd_pcm_uframes_t update_size, num_updates; snd_pcm_sframes_t avail, commitres; snd_pcm_uframes_t offset, frames; char *WritePtr; int err; SetRTPriority(); update_size = Device->UpdateSize; num_updates = Device->NumUpdates; while(!data->killNow) { int state = verify_state(data->pcmHandle); if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); aluHandleDisconnect(Device); break; } avail = snd_pcm_avail_update(data->pcmHandle); if(avail < 0) { ERR("available update failed: %s\n", snd_strerror(avail)); continue; } if((snd_pcm_uframes_t)avail > update_size*(num_updates+1)) { WARN("available samples exceeds the buffer size\n"); snd_pcm_reset(data->pcmHandle); continue; } // make sure there's frames to process if((snd_pcm_uframes_t)avail < update_size) { if(state != SND_PCM_STATE_RUNNING) { err = snd_pcm_start(data->pcmHandle); if(err < 0) { ERR("start failed: %s\n", snd_strerror(err)); continue; } } if(snd_pcm_wait(data->pcmHandle, 1000) == 0) ERR("Wait timeout... buffer size too low?\n"); continue; } avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop while(avail > 0) { frames = avail; err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); if(err < 0) { ERR("mmap begin error: %s\n", snd_strerror(err)); break; } WritePtr = (char*)areas->addr + (offset * areas->step / 8); aluMixData(Device, WritePtr, frames); commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); if(commitres < 0 || (commitres-frames) != 0) { ERR("mmap commit error: %s\n", snd_strerror(commitres >= 0 ? -EPIPE : commitres)); break; } avail -= frames; } } return 0; }
static ALuint MMDevApiProc(ALvoid *ptr) { ALCdevice *device = ptr; MMDevApiData *data = device->ExtraData; UINT32 buffer_len, written; ALuint update_size, len; BYTE *buffer; HRESULT hr; hr = CoInitialize(NULL); if(FAILED(hr)) { ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); aluHandleDisconnect(device); return 0; } hr = IAudioClient_GetBufferSize(data->client, &buffer_len); if(FAILED(hr)) { ERR("Failed to get audio buffer size: 0x%08lx\n", hr); aluHandleDisconnect(device); CoUninitialize(); return 0; } SetRTPriority(); update_size = device->UpdateSize; while(!data->killNow) { hr = IAudioClient_GetCurrentPadding(data->client, &written); if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); aluHandleDisconnect(device); break; } len = buffer_len - written; if(len < update_size) { DWORD res; res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE); if(res != WAIT_OBJECT_0) ERR("WaitForSingleObjectEx error: 0x%lx\n", res); continue; } len -= len%update_size; hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer); if(SUCCEEDED(hr)) { aluMixData(device, buffer, len); hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0); } if(FAILED(hr)) { ERR("Failed to buffer data: 0x%08lx\n", hr); aluHandleDisconnect(device); break; } } CoUninitialize(); return 0; }