static PaError BoostPriority( PaUnixThread* self ) { PaError result = paNoError; struct sched_param spm = { 0 }; /* Priority should only matter between contending FIFO threads? */ spm.sched_priority = 1; assert( self ); if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 ) { PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */ PA_DEBUG(( "Failed bumping priority\n" )); result = 0; } else { result = 1; /* Success */ } error: return result; }
static PaError BlockingWriteStream(PaStream* stream, const void *data, unsigned long numFrames) { PaSndioStream *s = (PaSndioStream *)stream; unsigned n, res; while (numFrames > 0) { n = s->par.round; if (n > numFrames) n = numFrames; PaUtil_SetOutputFrameCount(&s->bufferProcessor, n); PaUtil_SetInterleavedOutputChannels(&s->bufferProcessor, 0, s->writeBuffer, s->par.pchan); res = PaUtil_CopyOutput(&s->bufferProcessor, &data, n); if (res != n) { PA_DEBUG(("BlockingWriteStream: copyOutput: %u != %u\n")); return paUnanticipatedHostError; } res = sio_write(s->hdl, s->writeBuffer, n * s->par.pchan * s->par.bps); if (res == 0) return paUnanticipatedHostError; s->bytesWritten += n; numFrames -= n; } return paNoError; }
static void *WatchdogFunc( void *userData ) { PaError result = paNoError, *pres = NULL; int err; PaAlsaThreading *th = (PaAlsaThreading *) userData; unsigned intervalMsec = 500; const PaTime maxSeconds = 3.; /* Max seconds between callbacks */ PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed; double cpuLoad, avgCpuLoad = 0.; int throttled = 0; assert( th ); /* Execute OnWatchdogExit when exiting */ pthread_cleanup_push( &OnWatchdogExit, th ); /* Boost priority of callback thread */ PA_ENSURE( result = BoostPriority( th ) ); if( !result ) { /* Boost failed, might as well exit */ pthread_exit( NULL ); } cpuTimeThen = th->callbackCpuTime; { int policy; struct sched_param spm = { 0 }; pthread_getschedparam( pthread_self(), &policy, &spm ); PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority )); } while( 1 ) { double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff; /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */ pthread_testcancel(); Pa_Sleep( intervalMsec ); pthread_testcancel(); if( PaUtil_GetTime() - th->callbackTime > maxSeconds ) { PA_DEBUG(( "Watchdog: Terminating callback thread\n" )); /* Tell thread to terminate */ err = pthread_kill( th->callbackThread, SIGKILL ); pthread_exit( NULL ); } PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) )); /* Check if we should throttle, or unthrottle :P */ cpuTimeNow = th->callbackCpuTime; cpuTimeElapsed = cpuTimeNow - cpuTimeThen; cpuTimeThen = cpuTimeNow; timeNow = PaUtil_GetTime(); timeElapsed = timeNow - timeThen; timeThen = timeNow; cpuLoad = cpuTimeElapsed / timeElapsed; avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1; /* if( throttled ) PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed )); */ if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 ) { static int policy; static struct sched_param spm = { 0 }; static const struct sched_param defaultSpm = { 0 }; PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority )); pthread_getschedparam( th->callbackThread, &policy, &spm ); if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) ) { throttled = 1; } else PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) )); /* Give other processes a go, before raising priority again */ PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime )); Pa_Sleep( th->throttledSleepTime ); /* Reset callback priority */ if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) { PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) )); } if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 ) intervalMsec = 50; else intervalMsec = 100; /* lowpassCoeff = .97; lowpassCoeff1 = .99999 - lowpassCoeff; */ } else if( throttled && avgCpuLoad < .8 ) { intervalMsec = 500; throttled = 0; /* lowpassCoeff = .9; lowpassCoeff1 = .99999 - lowpassCoeff; */ } } pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */ error: /* Shouldn't get here in the normal case */ /* Pass on error code */ pres = malloc( sizeof (PaError) ); *pres = result; pthread_exit( pres ); }
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult ) { PaError result = paNoError; void* pret; if( exitResult ) { *exitResult = paNoError; } #if 0 if( watchdogExitResult ) *watchdogExitResult = paNoError; if( th->watchdogRunning ) { pthread_cancel( th->watchdogThread ); PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 ); if( pret && pret != PTHREAD_CANCELED ) { if( watchdogExitResult ) *watchdogExitResult = *(PaError *) pret; free( pret ); } } #endif /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ /* TODO: Make join time out */ self->stopRequested = wait; if( !wait ) { PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, self->thread )); /* XXX: Safe to call this if the thread has exited on its own? */ #ifdef PTHREAD_CANCELED pthread_cancel( self->thread ); #endif } PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread )); PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 ); #ifdef PTHREAD_CANCELED if( pret && PTHREAD_CANCELED != pret ) #else /* !wait means the thread may have been canceled */ if( pret && wait ) #endif { if( exitResult ) { *exitResult = *(PaError*)pret; } free( pret ); } error: PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError ); PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 ); return result; }
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild, int rtSched ) { PaError result = paNoError; pthread_attr_t attr; int started = 0; memset( self, 0, sizeof (PaUnixThread) ); PaUnixMutex_Initialize( &self->mtx ); PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 ); self->parentWaiting = 0 != waitForChild; /* Spawn thread */ /* Temporarily disabled since we should test during configuration for presence of required mman.h header */ #if 0 #if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1) if( rtSched ) { if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 ) { int savedErrno = errno; /* In case errno gets overwritten */ assert( savedErrno != EINVAL ); /* Most likely a programmer error */ PA_UNLESS( (savedErrno == EPERM), paInternalError ); PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ )); } else PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ )); } #endif #endif PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); /* Priority relative to other processes */ PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError ); started = 1; if( rtSched ) { #if 0 if( self->useWatchdog ) { int err; struct sched_param wdSpm = { 0 }; /* Launch watchdog, watchdog sets callback thread priority */ int prio = PA_MIN( self->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) ); wdSpm.sched_priority = prio; PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError ); PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError ); PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError ); if( (err = pthread_create( &self->watchdogThread, &attr, &WatchdogFunc, self )) ) { PA_UNLESS( err == EPERM, paInternalError ); /* Permission error, go on without realtime privileges */ PA_DEBUG(( "Failed bumping priority\n" )); } else { int policy; self->watchdogRunning = 1; PA_ENSURE_SYSTEM( pthread_getschedparam( self->watchdogThread, &policy, &wdSpm ), 0 ); /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */ if( wdSpm.sched_priority != prio ) { PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority )); PA_ENSURE( paInternalError ); } } } else #endif PA_ENSURE( BoostPriority( self ) ); { int policy; struct sched_param spm; pthread_getschedparam(self->thread, &policy, &spm); } } if( self->parentWaiting ) { PaTime till; struct timespec ts; int res = 0; PaTime now; PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) ); /* Wait for stream to be started */ now = PaUtil_GetTime(); till = now + waitForChild; while( self->parentWaiting && !res ) { if( waitForChild > 0 ) { ts.tv_sec = (time_t) floor( till ); ts.tv_nsec = (long) ((till - floor( till )) * 1e9); res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts ); } else { res = pthread_cond_wait( &self->cond, &self->mtx.mtx ); } } PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) ); PA_UNLESS( !res || ETIMEDOUT == res, paInternalError ); PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - now )); if( ETIMEDOUT == res ) { PA_ENSURE( paTimedOut ); } } end: return result; error: if( started ) { PaUnixThread_Terminate( self, 0, NULL ); } goto end; }
void PaWinDs_InitializeDSoundEntryPoints(void) { paWinDsDSoundEntryPoints.hInstance_ = LoadLibraryA("dsound.dll"); if( paWinDsDSoundEntryPoints.hInstance_ != NULL ) { paWinDsDSoundEntryPoints.DllGetClassObject = (HRESULT (WINAPI *)(REFCLSID, REFIID , LPVOID *)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DllGetClassObject" ); if( paWinDsDSoundEntryPoints.DllGetClassObject == NULL ) paWinDsDSoundEntryPoints.DllGetClassObject = DummyDllGetClassObject; paWinDsDSoundEntryPoints.DirectSoundCreate = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCreate" ); if( paWinDsDSoundEntryPoints.DirectSoundCreate == NULL ) paWinDsDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; paWinDsDSoundEntryPoints.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" ); if( paWinDsDSoundEntryPoints.DirectSoundEnumerateW == NULL ) paWinDsDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; paWinDsDSoundEntryPoints.DirectSoundEnumerateA = (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" ); if( paWinDsDSoundEntryPoints.DirectSoundEnumerateA == NULL ) paWinDsDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; paWinDsDSoundEntryPoints.DirectSoundCaptureCreate = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" ); if( paWinDsDSoundEntryPoints.DirectSoundCaptureCreate == NULL ) paWinDsDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" ); if( paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL ) paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA = (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" ); if( paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL ) paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 = (HRESULT (WINAPI *)(LPCGUID, LPCGUID, LPCDSCBUFFERDESC, LPCDSBUFFERDESC, HWND, DWORD, LPDIRECTSOUNDFULLDUPLEX *, LPDIRECTSOUNDCAPTUREBUFFER8 *, LPDIRECTSOUNDBUFFER8 *, LPUNKNOWN)) GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundFullDuplexCreate" ); if( paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 == NULL ) paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 = DummyDirectSoundFullDuplexCreate8; #endif } else { DWORD errorCode = GetLastError(); // 126 (0x7E) == ERROR_MOD_NOT_FOUND PA_DEBUG(("Couldn't load dsound.dll error code: %d \n",errorCode)); /* initialize with dummy entry points to make live easy when ds isn't present */ paWinDsDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; paWinDsDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; paWinDsDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; paWinDsDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8 = DummyDirectSoundFullDuplexCreate8; #endif } }
DEFINE_THREAD_ROUTINE(academy_upload, data) { char *directoryList = NULL; char *fileList = NULL; char dirname[ACADEMY_MAX_FILENAME]; _ftp_t *academy_ftp = NULL; _ftp_status academy_status; char *ptr = NULL; academy_upload_t *academy = (academy_upload_t *)data; printf("Start thread %s\n", __FUNCTION__); while( academy_upload_started && !ardrone_tool_exit() ) { vp_os_mutex_lock(&academy->mutex); vp_os_memset(&academy->user, 0, sizeof(academy_user_t)); academy->callback = &academy_upload_private_callback; academy->connected = FALSE; academy_upload_resetState(academy); vp_os_cond_wait(&academy->cond); vp_os_mutex_unlock(&academy->mutex); while(academy_upload_started && academy->connected) { academy_status = FTP_FAIL; academy_upload_nextState(academy); switch(academy->state.state) { case ACADEMY_STATE_CONNECTION: { struct dirent *dirent = NULL; DIR *dir = NULL; academy_status = FTP_FAIL; // Check if flight_* directory exist in local dir if((dir = opendir(flight_dir)) != NULL) { struct stat statbuf; while(FTP_FAILED(academy_status) && (dirent = readdir(dir)) != NULL) { if((strncmp(dirent->d_name, "flight_", strlen("flight_")) == 0)) { char local_dir[ACADEMY_MAX_FILENAME]; sprintf(dirname, "%s", dirent->d_name); sprintf(local_dir, "%s/%s", flight_dir, dirname); if((stat(local_dir, &statbuf) == 0) && S_ISDIR(statbuf.st_mode)) academy_status = FTP_SUCCESS; } } closedir(dir); } if(FTP_SUCCEDED(academy_status)) { if(academy_ftp == NULL) academy_ftp = ftpConnectFromName(ACADEMY_SERVERNAME, ACADEMY_PORT, academy->user.username, academy->user.password, &academy_status); } } break; case ACADEMY_STATE_PREPARE_PROCESS: academy_status = ftpCd(academy_ftp, "/Uploaded"); if(FTP_FAILED(academy_status)) { ftpMkdir(academy_ftp, "/Uploaded"); academy_status = ftpCd(academy_ftp, "/Uploaded"); } if(FTP_SUCCEDED(academy_status)) { academy_status = ftpList(academy_ftp, &directoryList, NULL); if(FTP_SUCCEDED(academy_status)) { bool_t found = FALSE; char *next_dir = NULL; while(!found && (ptr = academy_get_next_item_with_prefix(directoryList, &next_dir, "flight_", TRUE))) { if(strcmp(ptr, dirname) == 0) { found = TRUE; academy_upload_setState(academy, ACADEMY_STATE_FINISH_PROCESS); } } if(directoryList != NULL) { vp_os_free(directoryList); directoryList = NULL; } } } break; case ACADEMY_STATE_PROCESS: academy_status = ftpCd(academy_ftp, "/Uploading"); if(FTP_FAILED(academy_status)) { ftpMkdir(academy_ftp, "/Uploading"); academy_status = ftpCd(academy_ftp, "/Uploading"); } if(FTP_SUCCEDED(academy_status)) { ftpMkdir(academy_ftp, dirname); academy_status = ftpCd(academy_ftp, dirname); if(FTP_SUCCEDED(academy_status)) { char local_dir[ACADEMY_MAX_FILENAME]; struct dirent *dirent = NULL; DIR *dir = NULL; sprintf(local_dir, "%s/%s", flight_dir, dirname); if((dir = opendir(local_dir)) != NULL) { char local_filename[ACADEMY_MAX_FILENAME]; struct stat statbuf; while(FTP_SUCCEDED(academy_status) && ((dirent = readdir(dir)) != NULL)) { if((strncmp(dirent->d_name, "picture_", strlen("picture_")) == 0) || (strncmp(dirent->d_name, "userbox_", strlen("userbox_")) == 0)) { sprintf(local_filename, "%s/%s", local_dir, dirent->d_name); if((stat(local_filename, &statbuf) == 0) && !S_ISDIR(statbuf.st_mode)) { PA_DEBUG("Put %s from local directory to server...", dirent->d_name); academy_status = ftpPut(academy_ftp, local_filename, dirent->d_name, 1, NULL); if(FTP_SUCCEDED(academy_status)) PA_DEBUG("OK\n"); else PA_DEBUG("ERROR\n"); } } } closedir(dir); } } } break; case ACADEMY_STATE_FINISH_PROCESS: { char local_dir[ACADEMY_MAX_FILENAME]; char src[ACADEMY_MAX_FILENAME]; char dest[ACADEMY_MAX_FILENAME]; sprintf(src, "/Uploading/%s", dirname); sprintf(dest, "/Uploaded/%s", dirname); academy_status = ftpRename(academy_ftp, src, dest); // Penser à supprimer le fichier local academy_status = FTP_FAIL; sprintf(local_dir, "%s/%s", flight_dir, dirname); if(!ftw(local_dir, &academy_remove, 1)) { rmdir(local_dir); academy_status = FTP_SUCCESS; } } break; case ACADEMY_STATE_DISCONNECTION: if(academy_ftp != NULL) ftpClose(&academy_ftp); academy_status = FTP_SUCCESS; break; default: // Nothing to do break; } if(FTP_SUCCEDED(academy_status)) { PA_DEBUG("continue state from %d to %d\n", academy->state.state, (academy->state.state + 1) % ACADEMY_STATE_MAX); academy_upload_stateOK(academy); } else { PA_DEBUG("stop\n"); academy_upload_stateERROR(academy); if(fileList != NULL) { vp_os_free(fileList); fileList = NULL; } if(academy_ftp) ftpClose(&academy_ftp); vp_os_delay(1000); } } if(fileList != NULL) { vp_os_free(fileList); fileList = NULL; } if(academy_ftp) ftpClose(&academy_ftp); } // while THREAD_RETURN(C_OK); }
void *CallbackThread( void *userData ) { PaAlsaStream *stream = (PaAlsaStream*)userData; pthread_cleanup_push( &Stop, stream ); // Execute Stop on exit if( stream->pcm_playback ) snd_pcm_start( stream->pcm_playback ); else if( stream->pcm_capture ) snd_pcm_start( stream->pcm_capture ); while(1) { int frames_avail; int frames_got; PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ int callbackResult; int framesProcessed; pthread_testcancel(); { /* calculate time info */ snd_timestamp_t capture_timestamp; snd_timestamp_t playback_timestamp; snd_pcm_status_t *capture_status; snd_pcm_status_t *playback_status; snd_pcm_status_alloca( &capture_status ); snd_pcm_status_alloca( &playback_status ); if( stream->pcm_capture ) { snd_pcm_status( stream->pcm_capture, capture_status ); snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); } if( stream->pcm_playback ) { snd_pcm_status( stream->pcm_playback, playback_status ); snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); } /* Hmm, we potentially have both a playback and a capture timestamp. * Hopefully they are the same... */ if( stream->pcm_capture && stream->pcm_playback ) { float capture_time = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); float playback_time= playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); if( fabsf(capture_time-playback_time) > 0.01 ) PA_DEBUG(("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time))); timeInfo.currentTime = capture_time; } else if( stream->pcm_playback ) { timeInfo.currentTime = playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); } else { timeInfo.currentTime = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); } if( stream->pcm_capture ) { snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status ); timeInfo.inputBufferAdcTime = timeInfo.currentTime - (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; } if( stream->pcm_playback ) { snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status ); timeInfo.outputBufferDacTime = timeInfo.currentTime + (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; } } /* IMPLEMENT ME: - handle buffer slips */ /* depending on whether the host buffers are interleaved, non-interleaved or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(), PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here. */ framesProcessed = frames_avail = wait( stream ); while( frames_avail > 0 ) { //PA_DEBUG(( "%d frames available\n", frames_avail )); /* Now we know the soundcard is ready to produce/receive at least * one period. We just need to get the buffers for the client * to read/write. */ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* @todo pass underflow/overflow flags when necessary */ ); frames_got = setup_buffers( stream, frames_avail ); PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); callbackResult = paContinue; /* this calls the callback */ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); /* inform ALSA how many frames we wrote */ if( stream->pcm_capture ) snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_got ); if( stream->pcm_playback ) snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_got ); if( callbackResult != paContinue ) break; frames_avail -= frames_got; } /* If you need to byte swap outputBuffer, you can do it here using routines in pa_byteswappers.h */ if( callbackResult != paContinue ) { stream->callback_finished = 1; stream->callbackAbort = (callbackResult == paAbort); pthread_exit( NULL ); } } /* This code is unreachable, but important to include regardless because it * is possibly a macro with a closing brace to match the opening brace in * pthread_cleanup_push() above. The documentation states that they must * always occur in pairs. */ pthread_cleanup_pop( 1 ); }
PaError PaSndio_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) { PaSndioHostApiRepresentation *sndioHostApi; PaDeviceInfo *info; struct sio_hdl *hdl; PA_DEBUG(("PaSndio_Initialize: initializing...\n")); /* unusable APIs should return paNoError and a NULL hostApi */ *hostApi = NULL; sndioHostApi = PaUtil_AllocateMemory(sizeof(PaSndioHostApiRepresentation)); if (sndioHostApi == NULL) return paNoError; info = &sndioHostApi->default_info; info->structVersion = 2; info->name = "default"; info->hostApi = hostApiIndex; info->maxInputChannels = 128; info->maxOutputChannels = 128; info->defaultLowInputLatency = 0.01; info->defaultLowOutputLatency = 0.01; info->defaultHighInputLatency = 0.5; info->defaultHighOutputLatency = 0.5; info->defaultSampleRate = 48000; sndioHostApi->infos[0] = info; *hostApi = &sndioHostApi->base; (*hostApi)->info.structVersion = 1; (*hostApi)->info.type = paSndio; (*hostApi)->info.name = "sndio"; (*hostApi)->info.deviceCount = 1; (*hostApi)->info.defaultInputDevice = 0; (*hostApi)->info.defaultOutputDevice = 0; (*hostApi)->deviceInfos = sndioHostApi->infos; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; PaUtil_InitializeStreamInterface(&sndioHostApi->blocking, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, PaUtil_DummyGetCpuLoad, BlockingReadStream, BlockingWriteStream, BlockingGetStreamReadAvailable, BlockingGetStreamWriteAvailable); PaUtil_InitializeStreamInterface(&sndioHostApi->callback, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, PaUtil_DummyGetCpuLoad, PaUtil_DummyRead, PaUtil_DummyWrite, PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable); PA_DEBUG(("PaSndio_Initialize: done\n")); return paNoError; }
static PaError AbortStream(PaStream *stream) { PA_DEBUG(("AbortStream:\n")); return StopStream(stream); }
static PaError OpenStream(struct PaUtilHostApiRepresentation *hostApi, PaStream **stream, const PaStreamParameters *inputPar, const PaStreamParameters *outputPar, double sampleRate, unsigned long framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback *streamCallback, void *userData) { PaSndioHostApiRepresentation *sndioHostApi = (PaSndioHostApiRepresentation *)hostApi; PaSndioStream *s; PaError err; struct sio_hdl *hdl; struct sio_par par; unsigned mode; int readChannels, writeChannels; PaSampleFormat readFormat, writeFormat, deviceFormat; PA_DEBUG(("OpenStream:\n")); mode = 0; readChannels = writeChannels = 0; readFormat = writeFormat = 0; sio_initpar(&par); if (outputPar && outputPar->channelCount > 0) { if (outputPar->device != 0) { PA_DEBUG(("OpenStream: %d: bad output device\n", outputPar->device)); return paInvalidDevice; } if (outputPar->hostApiSpecificStreamInfo) { PA_DEBUG(("OpenStream: output specific info\n")); return paIncompatibleHostApiSpecificStreamInfo; } if (!SampleFormatToSndioParameters(&par, outputPar->sampleFormat)) { return paSampleFormatNotSupported; } writeFormat = outputPar->sampleFormat; writeChannels = par.pchan = outputPar->channelCount; mode |= SIO_PLAY; } if (inputPar && inputPar->channelCount > 0) { if (inputPar->device != 0) { PA_DEBUG(("OpenStream: %d: bad input device\n", inputPar->device)); return paInvalidDevice; } if (inputPar->hostApiSpecificStreamInfo) { PA_DEBUG(("OpenStream: input specific info\n")); return paIncompatibleHostApiSpecificStreamInfo; } if (!SampleFormatToSndioParameters(&par, inputPar->sampleFormat)) { return paSampleFormatNotSupported; } readFormat = inputPar->sampleFormat; readChannels = par.rchan = inputPar->channelCount; mode |= SIO_REC; } par.rate = sampleRate; if (framesPerBuffer != paFramesPerBufferUnspecified) par.round = framesPerBuffer; PA_DEBUG(("OpenStream: mode = %x, trying rate = %u\n", mode, par.rate)); hdl = sio_open(SIO_DEVANY, mode, 0); if (hdl == NULL) return paUnanticipatedHostError; if (!sio_setpar(hdl, &par)) { sio_close(hdl); return paUnanticipatedHostError; } if (!sio_getpar(hdl, &par)) { sio_close(hdl); return paUnanticipatedHostError; } if (!SndioParametersToSampleFormat(&par, &deviceFormat)) { sio_close(hdl); return paSampleFormatNotSupported; } if ((mode & SIO_REC) && par.rchan != inputPar->channelCount) { PA_DEBUG(("OpenStream: rchan(%u) != %d\n", par.rchan, inputPar->channelCount)); sio_close(hdl); return paInvalidChannelCount; } if ((mode & SIO_PLAY) && par.pchan != outputPar->channelCount) { PA_DEBUG(("OpenStream: pchan(%u) != %d\n", par.pchan, outputPar->channelCount)); sio_close(hdl); return paInvalidChannelCount; } if ((double)par.rate < sampleRate * 0.995 || (double)par.rate > sampleRate * 1.005) { PA_DEBUG(("OpenStream: rate(%u) != %g\n", par.rate, sampleRate)); sio_close(hdl); return paInvalidSampleRate; } s = (PaSndioStream *)PaUtil_AllocateMemory(sizeof(PaSndioStream)); if (s == NULL) { sio_close(hdl); return paInsufficientMemory; } PaUtil_InitializeStreamRepresentation(&s->base, streamCallback ? &sndioHostApi->callback : &sndioHostApi->blocking, streamCallback, userData); PA_DEBUG(("readChannels = %d, writeChannels = %d, readFormat = %x, writeFormat = %x\n", readChannels, writeChannels, readFormat, writeFormat)); err = PaUtil_InitializeBufferProcessor(&s->bufferProcessor, readChannels, readFormat, deviceFormat, writeChannels, writeFormat, deviceFormat, sampleRate, streamFlags, framesPerBuffer, par.round, paUtilFixedHostBufferSize, streamCallback, userData); if (err) { PA_DEBUG(("OpenStream: PaUtil_InitializeBufferProcessor failed\n")); PaUtil_FreeMemory(s); sio_close(hdl); return err; } if (mode & SIO_REC) { s->readBuffer = malloc(par.round * par.rchan * par.bps); if (s->readBuffer == NULL) { PA_DEBUG(("OpenStream: failed to allocate readBuffer\n")); PaUtil_FreeMemory(s); sio_close(hdl); return paInsufficientMemory; } } if (mode & SIO_PLAY) { s->writeBuffer = malloc(par.round * par.pchan * par.bps); if (s->writeBuffer == NULL) { PA_DEBUG(("OpenStream: failed to allocate writeBuffer\n")); free(s->readBuffer); PaUtil_FreeMemory(s); sio_close(hdl); return paInsufficientMemory; } } s->nfds = sio_nfds(hdl); s->pfds = malloc(sizeof(struct pollfd) * s->nfds); s->base.streamInfo.inputLatency = 0; s->base.streamInfo.outputLatency = (mode & SIO_PLAY) ? (double)(par.bufsz + PaUtil_GetBufferProcessorOutputLatencyFrames(&s->bufferProcessor)) / (double)par.rate : 0; s->base.streamInfo.sampleRate = par.rate; s->active = 0; s->stopped = 1; s->mode = mode; s->hdl = hdl; s->par = par; *stream = s; PA_DEBUG(("OpenStream: done\n")); return paNoError; }
/* * I/O loop for callback interface */ static void *sndioThread(void *arg) { PaSndioStream *s = (PaSndioStream *)arg; PaStreamCallbackTimeInfo ti; unsigned char *data; unsigned todo, rblksz, wblksz; int n, result; rblksz = s->par.round * s->par.rchan * s->par.bps; wblksz = s->par.round * s->par.pchan * s->par.bps; PA_DEBUG(("sndioThread: mode = %x, round = %u, rblksz = %u, wblksz = %u\n", s->mode, s->par.round, rblksz, wblksz)); while (!s->stopped) { if (s->mode & SIO_REC) { todo = rblksz; data = s->readBuffer; while (todo > 0) { n = sio_read(s->hdl, data, todo); if (n == 0) { PA_DEBUG(("sndioThread: sio_read failed\n")); goto failed; } todo -= n; data += n; } s->bytesRead += s->par.round; ti.inputBufferAdcTime = (double)s->framesProcessed / s->par.rate; } if (s->mode & SIO_PLAY) { ti.outputBufferDacTime = (double)(s->framesProcessed + s->par.bufsz) / s->par.rate; } ti.currentTime = s->framesProcessed / (double)s->par.rate; PaUtil_BeginBufferProcessing(&s->bufferProcessor, &ti, 0); if (s->mode & SIO_PLAY) { PaUtil_SetOutputFrameCount(&s->bufferProcessor, s->par.round); PaUtil_SetInterleavedOutputChannels(&s->bufferProcessor, 0, s->writeBuffer, s->par.pchan); } if (s->mode & SIO_REC) { PaUtil_SetInputFrameCount(&s->bufferProcessor, s->par.round); PaUtil_SetInterleavedInputChannels(&s->bufferProcessor, 0, s->readBuffer, s->par.rchan); } result = paContinue; n = PaUtil_EndBufferProcessing(&s->bufferProcessor, &result); if (n != s->par.round) { PA_DEBUG(("sndioThread: %d < %u frames, result = %d\n", n, s->par.round, result)); } if (result != paContinue) { break; } if (s->mode & SIO_PLAY) { n = sio_write(s->hdl, s->writeBuffer, wblksz); if (n < wblksz) { PA_DEBUG(("sndioThread: sio_write failed\n")); goto failed; } s->bytesWritten += s->par.round; } } failed: s->active = 0; PA_DEBUG(("sndioThread: done\n")); }