static int pulse_pause(void) { trace ("pulse_pause\n"); pulse_free(); state = OUTPUT_STATE_PAUSED; return 0; }
static int pulse_stop(void) { trace ("pulse_stop\n"); state = OUTPUT_STATE_STOPPED; deadbeef->streamer_reset(1); pulse_free(); return 0; }
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) { ENTER("wave_write"); size_t bytes_to_write = theSize; char* aBuffer=theMono16BitsWaveBuffer; assert(stream); size_t aTotalFreeMem=0; pthread_mutex_lock(&pulse_mutex); while (1) { if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled())) { SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); theSize=0; goto terminate; } aTotalFreeMem = pulse_free(); if (aTotalFreeMem >= bytes_to_write) { SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); break; } // TBD: check if really helpful if (aTotalFreeMem >= MAXLENGTH*2) { aTotalFreeMem = MAXLENGTH*2; } SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); // 500: threshold for avoiding too many calls to pulse_write if (aTotalFreeMem>500) { pulse_write(aBuffer, aTotalFreeMem); bytes_to_write -= aTotalFreeMem; aBuffer += aTotalFreeMem; } usleep(10000); } pulse_write(aBuffer, bytes_to_write); terminate: pthread_mutex_unlock(&pulse_mutex); SHOW("wave_write: theSize=%d", theSize); SHOW_TIME("wave_write > LEAVE"); return theSize; }
static int pulse_pause(void) { trace ("pulse_pause\n"); if (state == OUTPUT_STATE_STOPPED) { return -1; } pulse_free(); state = OUTPUT_STATE_PAUSED; return 0; }
static void pulse_close(snd_ctl_ext_t * ext) { snd_ctl_pulse_t *ctl = ext->private_data; assert(ctl); if (ctl->p) pulse_free(ctl->p); free(ctl->source); free(ctl->sink); free(ctl); }
static int pulse_setformat (ddb_waveformat_t *fmt) { int st = state; memcpy (&requested_fmt, fmt, sizeof (ddb_waveformat_t)); if (!s || !memcmp (fmt, &plugin.fmt, sizeof (ddb_waveformat_t))) { return 0; } pulse_free (); pulse_init (); int res = 0; if (st == OUTPUT_STATE_PLAYING) { res = pulse_play (); } else if (st == OUTPUT_STATE_PAUSED) { res = pulse_pause (); } return res; }
static void pulse_thread(void *context) { #ifdef __linux__ prctl(PR_SET_NAME, "deadbeef-pulse", 0, 0, 0, 0); #endif while (!pulse_terminate) { if (state != OUTPUT_STATE_PLAYING || !deadbeef->streamer_ok_to_read (-1)) { usleep(10000); continue; } int sample_size = plugin.fmt.channels * (plugin.fmt.bps / 8); int bs = buffer_size; int mod = bs % sample_size; if (mod > 0) { bs -= mod; } char buf[bs]; pulse_callback (buf, sizeof (buf)); int error; deadbeef->mutex_lock(mutex); int res = pa_simple_write(s, buf, sizeof (buf), &error); deadbeef->mutex_unlock(mutex); if (res < 0) { fprintf(stderr, "pulse: failed to write buffer\n"); pulse_tid = 0; pulse_free (); break; } } }
static void *pulse_init(const char *device, unsigned rate, unsigned latency) { pa_sample_spec spec; pa_t *pa; pa_buffer_attr buffer_attr = {0}; const pa_buffer_attr *server_attr = NULL; memset(&spec, 0, sizeof(spec)); pa = (pa_t*)calloc(1, sizeof(*pa)); if (!pa) goto error; pa->mainloop = pa_threaded_mainloop_new(); if (!pa->mainloop) goto error; pa->context = pa_context_new(pa_threaded_mainloop_get_api(pa->mainloop), "RetroArch"); if (!pa->context) goto error; pa_context_set_state_callback(pa->context, context_state_cb, pa); if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0) goto error; pa_threaded_mainloop_lock(pa->mainloop); if (pa_threaded_mainloop_start(pa->mainloop) < 0) goto error; pa_threaded_mainloop_wait(pa->mainloop); if (pa_context_get_state(pa->context) != PA_CONTEXT_READY) goto unlock_error; spec.format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE; spec.channels = 2; spec.rate = rate; pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL); if (!pa->stream) goto unlock_error; pa_stream_set_state_callback(pa->stream, stream_state_cb, pa); pa_stream_set_write_callback(pa->stream, stream_request_cb, pa); pa_stream_set_latency_update_callback(pa->stream, stream_latency_update_cb, pa); pa_stream_set_underflow_callback(pa->stream, underrun_update_cb, pa); pa_stream_set_buffer_attr_callback(pa->stream, buffer_attr_cb, pa); buffer_attr.maxlength = -1; buffer_attr.tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &spec); buffer_attr.prebuf = -1; buffer_attr.minreq = -1; buffer_attr.fragsize = -1; if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) goto error; pa_threaded_mainloop_wait(pa->mainloop); if (pa_stream_get_state(pa->stream) != PA_STREAM_READY) goto unlock_error; server_attr = pa_stream_get_buffer_attr(pa->stream); if (server_attr) { pa->buffer_size = server_attr->tlength; RARCH_LOG("[PulseAudio]: Requested %u bytes buffer, got %u.\n", (unsigned)buffer_attr.tlength, (unsigned)pa->buffer_size); } else pa->buffer_size = buffer_attr.tlength; pa_threaded_mainloop_unlock(pa->mainloop); return pa; unlock_error: pa_threaded_mainloop_unlock(pa->mainloop); error: pulse_free(pa); return NULL; }
static int pulse_stop(void) { trace ("pulse_stop\n"); pulse_free(); return 0; }