int SoundPlayer::getLatency() { if (connection) { int error; pa_usec_t ret = pa_simple_get_latency(connection, &error); return (int)ret; } else return 0; }
bool WaveOutPulseAudio::OpenWaveOutDevice(int pSampleRate, int pOutputChannels) { pa_sample_spec tOutputFormat; int tRes; pa_usec_t tLatency; LOG(LOG_VERBOSE, "Trying to open the wave out device"); if (mWaveOutOpened) return false; mSampleRate = pSampleRate; mAudioChannels = pOutputChannels; LOG(LOG_VERBOSE, "Desired device is %s", mDesiredDevice.c_str()); if ((mDesiredDevice == "") || (mDesiredDevice == "auto") || (mDesiredDevice == "automatic")) { LOG(LOG_VERBOSE, "Using default audio device"); mDesiredDevice = ""; } tOutputFormat.format = PA_SAMPLE_S16LE; tOutputFormat.rate = mSampleRate; tOutputFormat.channels = mAudioChannels; // create a new playback stream if (!(mOutputStream = pa_simple_new(NULL, "Homer-Conferencing", PA_STREAM_PLAYBACK, (mDesiredDevice != "" ? mDesiredDevice.c_str() : NULL) /* dev Name */, GetStreamName().c_str(), &tOutputFormat, NULL, NULL, &tRes))) { LOG(LOG_ERROR, "Couldn't create PulseAudio stream because %s(%d)", pa_strerror(tRes), tRes); return false; } if ((tLatency = pa_simple_get_latency(mOutputStream, &tRes)) == (pa_usec_t) -1) { LOG(LOG_ERROR, "Couldn't determine the latency of the output stream because %s(%d)", pa_strerror(tRes), tRes); pa_simple_free(mOutputStream); mOutputStream = NULL; return false; } mCurrentDevice = mDesiredDevice; //###################################################### //### give some verbose output //###################################################### LOG(LOG_INFO, "PortAudio wave out opened..."); LOG(LOG_INFO," ..sample rate: %d", mSampleRate); LOG(LOG_INFO," ..channels: %d", pOutputChannels); LOG(LOG_INFO," ..desired device: %s", mDesiredDevice.c_str()); LOG(LOG_INFO," ..selected device: %s", mCurrentDevice.c_str()); LOG(LOG_INFO," ..latency: %"PRIu64" seconds", (uint64_t)tLatency * 1000 * 1000); LOG(LOG_INFO," ..sample format: %d", PA_SAMPLE_S16LE); mWaveOutOpened = true; return true; }
float AudioDriverPulseAudio::get_latency() { if (latency == 0) { //only do this once since it's approximate anyway int error_code; pa_usec_t palat = pa_simple_get_latency(pulse, &error_code); latency = double(palat) / 1000000.0; } return latency; }
static int get_delay_pulse(void * p) { bg_pa_t * priv; int error; int ret; priv = p; ret = gavl_time_rescale(1000000, priv->format.samplerate, pa_simple_get_latency(priv->pa, &error)); return ret; }
qint64 OutputPulseAudio::latency() { if (!m_connection) return 0; int error = 0; qint64 delay = pa_simple_get_latency(m_connection, &error)/1000; if (error) { qWarning("OutputPulseAudio: %s", pa_strerror (error)); delay = 0; } return delay; }
static void print_info( pa_simple* simple, pa_sample_spec* sample_spec, pa_usec_t start_time, uint64_t n_bytes) { const pa_usec_t position = pa_bytes_to_usec(n_bytes, sample_spec); const pa_usec_t timestamp = pa_rtclock_now() - start_time; const pa_usec_t latency = pa_simple_get_latency(simple, NULL); fprintf(stdout, "position=%lu ms, timestamp=%lu ms, diff=%ld ms, latency=%lu ms\n", (unsigned long)(position / 1000), (unsigned long)(timestamp / 1000), (((long)position - (long)timestamp) / 1000), (unsigned long)(latency / 1000)); }
int ao_plugin_close(ao_device *device) { assert(device && device->internal); ao_pulse_internal *internal = (ao_pulse_internal *) device->internal; if(internal->simple){ /* this is a PulseAudio ALSA bug workaround; pa_simple_drain() always takes about 2 seconds, even if there's nothing to drain. Rather than wait for no reason, determine the current playback depth, wait that long, then kill the stream. Remove this code once Pulse gets fixed. */ pa_usec_t us = pa_simple_get_latency(internal->simple, NULL); if(us<0 || us>1000000){ pa_simple_drain(internal->simple, NULL); }else{ us -= internal->static_delay; if(us>0){ struct timespec sleep,wake; sleep.tv_sec = (int)(us/1000000); sleep.tv_nsec = (us-sleep.tv_sec*1000000)*1000; while(nanosleep(&sleep,&wake)<0){ if(errno==EINTR) sleep=wake; else break; } } } pa_simple_free(internal->simple); internal->simple = NULL; } return 1; }
int ao_plugin_open(ao_device *device, ao_sample_format *format) { char *p=NULL, t[256], t2[256]; const char *fn = NULL; ao_pulse_internal *internal; struct pa_sample_spec ss; struct pa_channel_map map; struct pa_buffer_attr battr; size_t allocated = 128; assert(device && device->internal && format); internal = (ao_pulse_internal *) device->internal; if (format->bits == 8) ss.format = PA_SAMPLE_U8; else if (format->bits == 16) ss.format = PA_SAMPLE_S16NE; #ifdef PA_SAMPLE_S24NE else if (format->bits == 24) ss.format = PA_SAMPLE_S24NE; #endif else return 0; if (device->output_channels <= 0 || device->output_channels > PA_CHANNELS_MAX) return 0; ss.channels = device->output_channels; ss.rate = format->rate; disable_sigpipe(); if (internal->client_name) { snprintf(t, sizeof(t), "libao[%s]", internal->client_name); snprintf(t2, sizeof(t2), "libao[%s] playback stream", internal->client_name); } else { while (1) { p = pa_xmalloc(allocated); if (!(fn = pa_get_binary_name(p, allocated))) { break; } if (fn != p || strlen(p) < allocated - 1) { fn = pa_path_get_filename(fn); snprintf(t, sizeof(t), "libao[%s]", fn); snprintf(t2, sizeof(t2), "libao[%s] playback stream", fn); break; } pa_xfree(p); allocated *= 2; } pa_xfree(p); p = NULL; if (!fn) { strcpy(t, "libao"); strcpy(t2, "libao playback stream"); } } if(device->input_map){ int i; pa_channel_map_init(&map); map.channels=device->output_channels; for(i=0;i<device->output_channels;i++){ if(device->input_map[i]==-1){ map.map[i] = PA_CHANNEL_POSITION_INVALID; }else{ map.map[i] = device->input_map[i]; } } } /* buffering attributes */ battr.prebuf = battr.minreq = battr.fragsize = -1; battr.tlength = (int)(internal->buffer_time * format->rate) / 1000000 * ((format->bits+7)/8) + device->output_channels; battr.minreq = battr.tlength/4; battr.maxlength = battr.tlength+battr.minreq; internal->simple = pa_simple_new(internal->server, t, PA_STREAM_PLAYBACK, internal->sink, t2, &ss, (device->input_map ? &map : NULL), &battr, NULL); if (!internal->simple) return 0; device->driver_byte_format = AO_FMT_NATIVE; internal->static_delay = pa_simple_get_latency(internal->simple, NULL); if(internal->static_delay<0) internal->static_delay = 0; return 1; }
UINT32 Pulse_GetLatency(void* drvObj) { DRV_PULSE* drv = (DRV_PULSE*)drvObj; return (UINT32)(pa_simple_get_latency(drv->hPulse, NULL) / 1000); }
/** \fn localInit \brief Take & initialize the device */ bool pulseSimpleAudioDevice::localInit(void) { ADM_info("Pulse, initiliazing channel=%d samplerate=%d\n",(int)_channels,(int)_frequency); pa_simple *s; pa_sample_spec ss; int er; pa_buffer_attr attr; pa_channel_map map,*pmap=NULL; attr.maxlength = (uint32_t) -1; attr.tlength = (uint32_t )-1; attr.prebuf =(uint32_t) -1; attr.minreq = (uint32_t) -1; attr.fragsize =(uint32_t) -1; // We want something like 20 ms latency uint64_t bufSize=_frequency; bufSize*=_channels; bufSize*=2; // 1 second worth of audio bufSize=bufSize/1000; bufSize*=ADM_PULSE_LATENCY; attr.tlength=bufSize; // Latency in bytes // Channel mapping if(_channels>2) { pmap=↦ map.channels=_channels; map.map[0]=PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1]=PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2]=PA_CHANNEL_POSITION_FRONT_CENTER; map.map[3]=PA_CHANNEL_POSITION_REAR_LEFT; map.map[4]=PA_CHANNEL_POSITION_REAR_RIGHT; map.map[5]=PA_CHANNEL_POSITION_SUBWOOFER; } ss.format = PA_SAMPLE_S16LE; ss.channels = _channels; ss.rate =_frequency; instance= pa_simple_new(NULL, // Use the default server. "Avidemux2", // Our application's name. PA_STREAM_PLAYBACK, NULL, // Use the default device. "Sound", // Description of our stream. &ss, // Our sample format. pmap, // Use default channel map &attr , // Use default buffering attributes. &er // Ignore error code. ); if(!instance) { ADM_info("[PulseSimple] open failed :%s\n",pa_strerror(er)); return 0; } #if 0 pa_usec_t l=0; // Latency... Clock ticktock; ticktock.reset(); if(0>pa_simple_write(INSTANCE,silence, sizeOf10ms,&er)) { fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(er)); } pa_simple_drain(INSTANCE,&er); latency=ticktock.getElapsedMS(); ADM_info("[Pulse] Latency :%"PRIu32", total %"PRIu32"\n",latency,pa_simple_get_latency(INSTANCE,&er)/1000); #endif ADM_info("[PulseSimple] open ok for fq=%d channels=%d\n",ss.rate,ss.channels); return 1; }
void *pa_fft_thread(void *arg) { struct pa_fft *t = (struct pa_fft *)arg; float weights[t->buffer_samples]; graph_init(t); avg_buf_init(t); weights_init(weights, t->fft_memb, t->win_type); while (t->cont) { while(SDL_PollEvent(&t->event)) { switch(t->event.type) { case SDL_QUIT: t->cont = 0; break; case SDL_WINDOWEVENT: SDL_GL_GetDrawableSize(t->win, &t->width, &t->height); glViewport(0, 0, t->width, t->height); break; default: break; } } if (t->overlap) memcpy(&t->pa_buf[0], &t->pa_buf[t->pa_samples], t->pa_samples*sizeof(float)); pa_usec_t lag = pa_simple_get_latency(t->s, &t->error); if (pa_simple_read(t->s, &t->pa_buf[t->overlap ? t->pa_samples : 0], t->pa_buf_size, &t->error) < 0) { fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(t->error)); t->cont = 0; continue; } apply_win(t->buffer, t->pa_buf, weights, t->buffer_samples); fftw_execute(t->plan); double freq_low, freq_disp, freq_range, freq_off, mag_max = 0.0f; if (t->log_graph) { freq_low = log10((t->start_low*t->fft_fund_freq)/((float)t->ss.rate/2)); freq_disp = 1.0 - log10((t->fft_memb*t->fft_fund_freq)/((float)t->ss.rate/2)); freq_range = (1.0 - freq_disp) - freq_low; freq_off = 0.0f; } else { freq_low = (t->start_low*t->fft_fund_freq)/((float)t->ss.rate/2); freq_disp = 1.0 - (t->fft_memb*t->fft_fund_freq)/((float)t->ss.rate/2); freq_range = (1.0 - freq_disp) - freq_low; freq_off = 1.0f; } for (int i = t->start_low; i < t->fft_memb; i++) { fftw_complex num = t->output[i]; double mag = creal(num)*creal(num) + cimag(num)*cimag(num); mag = log10(mag)/10; mag = frame_average(mag, t->frame_avg_mag[i], t->frame_avg, 1); mag_max = mag > mag_max ? mag : mag_max; } if (!t->no_refresh) glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_LINE_STRIP); if ((float)lag/1000000 < 1.0f) glColor3f(255.0,255.0,255.0); else glColor3f(255.0,0.0,0.0); for (int i = t->start_low; i < t->fft_memb; i++) { double freq; fftw_complex num = t->output[i]; if (t->log_graph) freq = log10((i*t->fft_fund_freq)/((float)t->ss.rate/2)); else freq = (i*t->fft_fund_freq)/((float)t->ss.rate/2); double mag = creal(num)*creal(num) + cimag(num)*cimag(num); mag = log10(mag)/10; mag = frame_average(mag, t->frame_avg_mag[i], t->frame_avg, 0); glVertex2f((freq/freq_range + freq_disp/2)*2 - freq_off, mag + mag_max + 0.5f); } glEnd(); SDL_GL_SwapWindow(t->win); } SDL_DestroyWindow(t->win); SDL_Quit(); deinit_fft(t); return NULL; }