/** Destroy libao driver */
static void uninit(int immed) {
    if (stream && !immed) {
            pa_threaded_mainloop_lock(mainloop);
            waitop(pa_stream_drain(stream, success_cb, NULL));
    }

    if (mainloop)
        pa_threaded_mainloop_stop(mainloop);

    if (stream) {
        pa_stream_disconnect(stream);
        pa_stream_unref(stream);
        stream = NULL;
    }

    if (context) {
        pa_context_disconnect(context);
        pa_context_unref(context);
        context = NULL;
    }

    if (mainloop) {
        pa_threaded_mainloop_free(mainloop);
        mainloop = NULL;
    }
}
Esempio n. 2
0
static void
pulse_output_close(struct audio_output *ao)
{
	struct pulse_output *po = (struct pulse_output *)ao;
	pa_operation *o;

	assert(po->mainloop != NULL);

	pa_threaded_mainloop_lock(po->mainloop);

	if (pa_stream_get_state(po->stream) == PA_STREAM_READY) {
		o = pa_stream_drain(po->stream,
				    pulse_output_stream_success_cb, po);
		if (o == NULL) {
			g_warning("pa_stream_drain() has failed: %s",
				  pa_strerror(pa_context_errno(po->context)));
		} else
			pulse_wait_for_operation(po->mainloop, o);
	}

	pulse_output_delete_stream(po);

	if (po->context != NULL &&
	    pa_context_get_state(po->context) != PA_CONTEXT_READY)
		pulse_output_delete_context(po);

	pa_threaded_mainloop_unlock(po->mainloop);
}
Esempio n. 3
0
/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close ( vlc_object_t *p_this )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t * p_sys = p_aout->output.p_sys;

    msg_Dbg(p_aout, "Pulse Close");

    if(p_sys->stream) {
        pa_threaded_mainloop_lock(p_sys->mainloop);
        pa_stream_set_write_callback(p_sys->stream, NULL, NULL);

        pa_operation *o;

        o = pa_stream_flush(p_sys->stream, success_cb, p_aout);
        while( pa_operation_get_state(o) == PA_OPERATION_RUNNING )
            pa_threaded_mainloop_wait(p_sys->mainloop);
        pa_operation_unref(o);

        o = pa_stream_drain(p_sys->stream, success_cb, p_aout);
        while( pa_operation_get_state(o) == PA_OPERATION_RUNNING )
            pa_threaded_mainloop_wait(p_sys->mainloop);
        pa_operation_unref(o);

        pa_threaded_mainloop_unlock(p_sys->mainloop);
    }
    uninit(p_aout);
}
Esempio n. 4
0
static void uninit(struct ao *ao, bool cut_audio)
{
    struct priv *priv = ao->priv;
    if (priv->stream && !cut_audio) {
        pa_threaded_mainloop_lock(priv->mainloop);
        waitop(priv, pa_stream_drain(priv->stream, success_cb, ao));
    }

    if (priv->mainloop)
        pa_threaded_mainloop_stop(priv->mainloop);

    if (priv->stream) {
        pa_stream_disconnect(priv->stream);
        pa_stream_unref(priv->stream);
        priv->stream = NULL;
    }

    if (priv->context) {
        pa_context_disconnect(priv->context);
        pa_context_unref(priv->context);
        priv->context = NULL;
    }

    if (priv->mainloop) {
        pa_threaded_mainloop_free(priv->mainloop);
        priv->mainloop = NULL;
    }
}
Esempio n. 5
0
/// @brief Called by PA to request more data (and other things?)
void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread)
{
	if (!thread->is_playing) return;

	if (thread->cur_frame >= thread->end_frame + thread->provider->GetSampleRate()) {
		// More than a second past end of stream
		thread->is_playing = false;
		pa_operation *op = pa_stream_drain(p, NULL, NULL);
		pa_operation_unref(op);
		//printf("PA requested more buffer, but no more to stream\n");
		return;

	} else if (thread->cur_frame >= thread->end_frame) {
		// Past end of stream, but not a full second, add some silence
		void *buf = calloc(length, 1);
		::pa_stream_write(p, buf, length, free, 0, PA_SEEK_RELATIVE);
		thread->cur_frame += length / thread->bpf;
		return;
	}

	//printf("PA requested more buffer, %lu bytes\n", (unsigned long)length);
	unsigned long bpf = thread->bpf;
	unsigned long frames = length / thread->bpf;
	unsigned long maxframes = thread->end_frame - thread->cur_frame;
	if (frames > maxframes) frames = maxframes;
	//printf("Handing it %lu frames\n", frames);
	void *buf = malloc(frames * bpf);
	thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume);
	::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE);
	thread->cur_frame += frames;
}
Esempio n. 6
0
int pa_simple_drain(pa_simple *p, int *rerror) {
    pa_operation *o = NULL;

    pa_assert(p);

    CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);

    pa_threaded_mainloop_lock(p->mainloop);
    CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);

    o = pa_stream_drain(p->stream, success_cb, p);
    CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);

    p->operation_success = 0;
    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
        pa_threaded_mainloop_wait(p->mainloop);
        CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
    }
    CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail);

    pa_operation_unref(o);
    pa_threaded_mainloop_unlock(p->mainloop);

    return 0;

unlock_and_fail:

    if (o) {
        pa_operation_cancel(o);
        pa_operation_unref(o);
    }

    pa_threaded_mainloop_unlock(p->mainloop);
    return -1;
}
Esempio n. 7
0
File: pulse.c Progetto: Kafay/vlc
/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close ( vlc_object_t *p_this )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t * p_sys = p_aout->output.p_sys;

    msg_Dbg(p_aout, "Pulse Close");

    if(p_sys->stream){
        pa_operation *o;
        pa_threaded_mainloop_lock(p_sys->mainloop);
        pa_stream_set_write_callback(p_sys->stream, NULL, NULL);

        if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
            while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
                CHECK_DEAD_GOTO(fail);
                pa_threaded_mainloop_wait(p_sys->mainloop);
            }

        fail:

            pa_operation_unref(o);
        }

        pa_threaded_mainloop_unlock(p_sys->mainloop);
    }
    uninit(p_aout);
}
Esempio n. 8
0
static void pulse_drain(void) {
    pa_operation *o = NULL;
    int success = 0;

    CHECK_CONNECTED();

    pa_threaded_mainloop_lock(mainloop);
    CHECK_DEAD_GOTO(fail, 0);

    if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
        AUDDBG("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context)));
        goto fail;
    }

    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }

    if (!success)
        AUDDBG("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context)));

fail:
    if (o)
        pa_operation_unref(o);

    pa_threaded_mainloop_unlock(mainloop);
}
Esempio n. 9
0
static void drain(struct ao *ao)
{
    struct priv *priv = ao->priv;
    if (priv->stream) {
        pa_threaded_mainloop_lock(priv->mainloop);
        waitop(priv, pa_stream_drain(priv->stream, success_cb, ao));
    }
}
Esempio n. 10
0
void PulseOutput::drain() {
  if (stream) {
    pa_operation * operation = pa_stream_drain(stream,nullptr,nullptr);
    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
      context->wait_plugin_events();
    pa_operation_unref(operation);
    }
  }
Esempio n. 11
0
void CAESinkPULSE::Drain()
{
  if (!m_IsAllocated)
    return;

  pa_threaded_mainloop_lock(m_MainLoop);
  WaitForOperation(pa_stream_drain(m_Stream, NULL, NULL), m_MainLoop, "Drain");
  pa_threaded_mainloop_unlock(m_MainLoop);
}
Esempio n. 12
0
/*drain stream function*/
void quisk_drain_cork_stream(struct sound_dev *dev) {
    pa_stream *s = dev->handle;
    pa_operation *o;
   
    if (!(o = pa_stream_drain(s, stream_drain_complete, NULL))) {
        printf("pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
        exit(1);
    }
    
    pa_operation_unref(o);
}
Esempio n. 13
0
void AudioOutputPulseAudio::Drain(void)
{
    AudioOutputBase::Drain();
    pa_threaded_mainloop_lock(mainloop);
    pa_operation *op = pa_stream_drain(pstream, NULL, this);
    pa_threaded_mainloop_unlock(mainloop);

    if (op)
        pa_operation_unref(op);
    else
        VBERROR("Drain, stream drain failed");
}
Esempio n. 14
0
void CPulseAEStream::Drain()
{
  if (!m_Initialized)
    return;

  if (m_DrainOperation)
    return;

  pa_threaded_mainloop_lock(m_MainLoop);
  m_DrainOperation = pa_stream_drain(m_Stream, CPulseAEStream::StreamDrainComplete, this);
  pa_threaded_mainloop_unlock(m_MainLoop);
}
Esempio n. 15
0
/* Shutdown the application. This is called from the pulseaudio mainloop
 * thread in stream_write_cb() */
static void do_shutdown(_mbx_out out) {
    mbx_log_debug(MBX_LOG_AUDIO_OUTPUT, "Shutting down.");
    unset_all_callbacks(out);
    /* stream_drain_complete_cb will be called when drain is done */
    pa_operation *o = pa_stream_drain(out->stream,stream_drain_complete_cb,out);
    if ( o == NULL ) {
        mbx_log_error(MBX_LOG_AUDIO_OUTPUT, "Failed to start pulseaudio stream drain: %s",
            pa_msg(out));
        /* In case of error, we do our best and continue manually */
        stream_drain_complete_cb(out->stream, 0, out);
    }
    pa_operation_unref(o);
}
Esempio n. 16
0
void m1sdr_PlayStop(void)
{
	#if 0
	pa_operation *op;
	#endif

	#ifdef USE_SDL
	if (lnxdrv_apimode == 0) 
	{
		SDL_PauseAudio(1);
	}
	#endif

	if (lnxdrv_apimode == 1)
	{
//		snd_pcm_pause(pHandle, 1);
		snd_pcm_drop(pHandle);
	}

	#if PULSE_USE_SIMPLE
	if ((lnxdrv_apimode == 3) && (my_simple))
	{
		pa_simple_flush(my_simple, NULL);
		pa_simple_free(my_simple);
		my_simple = NULL;
	}
	#else
#if 0
	if (lnxdrv_apimode == 3)
	{
		op = pa_stream_drain(my_pa_stream, &pa_stream_drain_complete, NULL);
		if (op)
		{
			while (pa_operation_get_state(op) != PA_OPERATION_DONE)
			{
				if (pa_context_get_state(my_pa_context) != PA_CONTEXT_READY ||
				    pa_stream_get_state(my_pa_stream) != PA_STREAM_READY ||
				    pa_mainloop_iterate(my_pa_mainloop, 0, NULL) < 0)
				    {
				    	pa_operation_cancel(op);
					break;
				    }
			}
		}
	}
#endif
	#endif

	waveLogStop();
	oss_playing = 0;
}
Esempio n. 17
0
static void
stream_request_callback(pa_stream * s, size_t nbytes, void * u)
{
  cubeb_stream * stm;
  void * buffer;
  size_t size;
  int r;
  long got;
  size_t towrite;
  size_t frame_size;

  stm = u;

  if (stm->shutdown)
    return;

  frame_size = pa_frame_size(&stm->sample_spec);

  assert(nbytes % frame_size == 0);

  towrite = nbytes;

  while (towrite) {
    size = towrite;
    r = pa_stream_begin_write(s, &buffer, &size);
    assert(r == 0);
    assert(size > 0);
    assert(size % frame_size == 0);

    got = stm->data_callback(stm, stm->user_ptr, buffer, size / frame_size);
    if (got < 0) {
      pa_stream_cancel_write(s);
      stm->shutdown = 1;
      return;
    }

    r = pa_stream_write(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
    assert(r == 0);

    if ((size_t) got < size / frame_size) {
      stm->draining = pa_stream_drain(s, stream_drain_success_callback, stm);
      stm->shutdown = 1;
      return;
    }

    towrite -= size;
  }

  assert(towrite == 0);
}
Esempio n. 18
0
static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
{
	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;

	if (!pulse->context || !pulse->stream)
		return;

	pa_threaded_mainloop_lock(pulse->mainloop);
	rdpsnd_pulse_wait_for_operation(pulse,
		pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
	pa_stream_disconnect(pulse->stream);
	pa_stream_unref(pulse->stream);
	pulse->stream = NULL;
	pa_threaded_mainloop_unlock(pulse->mainloop);
}
Esempio n. 19
0
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice *pulse)
{
	if(!pulse->context || !pulse->stream)
		return FALSE;
	DEBUG_TSMF("");
	pa_threaded_mainloop_lock(pulse->mainloop);
	pa_stream_set_write_callback(pulse->stream, NULL, NULL);
	tsmf_pulse_wait_for_operation(pulse,
								  pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
	pa_stream_disconnect(pulse->stream);
	pa_stream_unref(pulse->stream);
	pulse->stream = NULL;
	pa_threaded_mainloop_unlock(pulse->mainloop);
	return TRUE;
}
Esempio n. 20
0
static int
tsmf_pulse_close_stream(TSMFPulseAudioDevice * pulse)
{
	if (!pulse->context || !pulse->stream)
		return 1;
	LLOGLN(0, ("tsmf_pulse_close_stream:"));
	pa_threaded_mainloop_lock(pulse->mainloop);
	pa_stream_set_write_callback(pulse->stream, NULL, NULL);
	tsmf_pulse_wait_for_operation(pulse,
		pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
	pa_stream_disconnect(pulse->stream);
	pa_stream_unref(pulse->stream);
	pulse->stream = NULL;
	pa_threaded_mainloop_unlock(pulse->mainloop);
	return 0;
}
Esempio n. 21
0
/* Start draining */
static void start_drain(void) {
  printf("Draining\n");
  if (stream) {
    pa_operation *o;

    pa_stream_set_write_callback(stream, NULL, NULL);

    if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
      //printf("pa_stream_drain(): %s", pa_strerror(pa_context_errno(context)));
      exit(1);
      return;
    }

    pa_operation_unref(o);
  } else
    exit(0);
}
Esempio n. 22
0
/* Start draining */
static void start_drain(void) {

    if (stream) {
        pa_operation *o;

        pa_stream_set_write_callback(stream, NULL, NULL);

        if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
            pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context)));
            quit(1);
            return;
        }

        pa_operation_unref(o);
    } else
        quit(0);
}
Esempio n. 23
0
static int
rdpsnd_pulse_close(rdpsndDevicePlugin * devplugin)
{
	struct pulse_device_data * pulse_data;

	pulse_data = (struct pulse_device_data *) devplugin->device_data;
	if (!pulse_data->context || !pulse_data->stream)
		return 1;
	LLOGLN(0, ("rdpsnd_pulse_close:"));
	pa_threaded_mainloop_lock(pulse_data->mainloop);
	rdpsnd_pulse_wait_for_operation(devplugin,
		pa_stream_drain(pulse_data->stream, rdpsnd_pulse_stream_success_callback, devplugin));
	pa_stream_disconnect(pulse_data->stream);
	pa_stream_unref(pulse_data->stream);
	pulse_data->stream = NULL;
	pa_threaded_mainloop_unlock(pulse_data->mainloop);
	return 0;
}
Esempio n. 24
0
/** Destroy libao driver */
static void uninit(int immed) {
    if (stream) {
        if (!immed && pa_stream_get_state(stream) == PA_STREAM_READY)
                wait_for_operation(pa_stream_drain(stream, NULL, NULL));
        
        pa_stream_unref(stream);
        stream = NULL;
    }

    if (context) {
        pa_context_unref(context);
        context = NULL;
    }

    if (mainloop) {
        pa_mainloop_free(mainloop);
        mainloop = NULL;
    }
}
Esempio n. 25
0
static int drain(void) {
    pa_operation *o = NULL;
    int success = 0;
    int ret = PULSE_ERROR;

    ENTER(__FUNCTION__);

    CHECK_CONNECTED(ret);

    pa_threaded_mainloop_lock(mainloop);
    CHECK_DEAD_GOTO(fail, 0);

    SHOW_TIME("pa_stream_drain (call)");
    if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
        SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
        goto fail;
    }
    
    SHOW_TIME("pa_threaded_mainloop_wait (call)");
    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }
    SHOW_TIME("pa_threaded_mainloop_wait (ret)");

    if (!success) {
      SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
    } 
    else {
      ret = PULSE_OK;
    }
    
fail:
    SHOW_TIME("pa_operation_unref (call)");
    if (o)
        pa_operation_unref(o);
 
    pa_threaded_mainloop_unlock(mainloop);
    SHOW_TIME("drain (ret)");
    
    return ret;
}
static PyObject* PulseAudio_flush(output_PulseAudio *self, PyObject *args)
{
    pa_operation *op;

    /*ensure outuput stream is still running*/
    /*FIXME*/

    pa_threaded_mainloop_lock(self->mainloop);

    /*uncork output stream, if necessary*/
    if (pa_stream_is_corked(self->stream)) {
        op = pa_stream_cork(
            self->stream,
            0,
            (pa_stream_success_cb_t)success_callback,
            self->mainloop);

        while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
            pa_threaded_mainloop_wait(self->mainloop);
        }

        pa_operation_unref(op);
    }

    /*drain output stream*/
    op = pa_stream_drain(
        self->stream,
        (pa_stream_success_cb_t)success_callback,
        self->mainloop);

    while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
        pa_threaded_mainloop_wait(self->mainloop);
    }

    pa_operation_unref(op);

    pa_threaded_mainloop_unlock(self->mainloop);

    Py_INCREF(Py_None);
    return Py_None;
}
Esempio n. 27
0
gboolean xmms_pulse_backend_drain (xmms_pulse *p, int *rerror)
{
	pa_operation *o = NULL;
	assert (p);

	if (!check_pulse_health (p, rerror))
		goto unlock_and_fail;

	o = pa_stream_drain (p->stream, drain_result_cb, p);
	if (!o) {
		if (rerror)
			*rerror = pa_context_errno ((p)->context);
		goto unlock_and_fail;
	}

	p->operation_success = 0;
	while (pa_operation_get_state (o) != PA_OPERATION_DONE) {
		pa_threaded_mainloop_wait (p->mainloop);
		if (!check_pulse_health (p, rerror))
			goto unlock_and_fail;
	}
	pa_operation_unref (o);
	o = NULL;
	if (!p->operation_success) {
		if (rerror)
			*rerror = pa_context_errno ((p)->context);
		goto unlock_and_fail;
	}

	return TRUE;

 unlock_and_fail:
	if (o) {
		pa_operation_cancel (o);
		pa_operation_unref (o);
	}

	return FALSE;
}
Esempio n. 28
0
static void
_eventd_sound_pulseaudio_stream_state_callback(pa_stream *stream, gpointer user_data)
{
    EventdSoundPulseaudioEventData *data = user_data;
    pa_stream_state_t state = pa_stream_get_state(stream);
    pa_operation *op;
    switch ( state )
    {
    case PA_STREAM_FAILED:
        g_warning("Failed sample creation");
    case PA_STREAM_TERMINATED:
        g_free(data);
        pa_stream_unref(stream);
    break;
    case PA_STREAM_READY:
        pa_stream_write(stream, data->data, data->length, g_free, 0, PA_SEEK_RELATIVE);
        op = pa_stream_drain(stream, _eventd_sound_pulseaudio_stream_drain_callback, NULL);
        if ( op != NULL )
            pa_operation_unref(op);
    default:
    break;
    }
}
Esempio n. 29
0
static void
swfdec_playback_stream_close (Stream *stream)
{
  /* Pull it off of the active stream list. */
  stream->sound->streams = g_list_remove (stream->sound->streams, stream);

  /* If we have created a PA stream, defer freeing until we drain it. */
  if (stream->pa != NULL) {
    pa_operation *o;

    stream->no_more = 1;

    o = pa_stream_drain (stream->pa, stream_drain_complete, stream);
    if (o != NULL) {
      pa_operation_unref (o);
      return;
    } else {
      g_printerr("PA stream drain failed: %s\n",
		 pa_strerror(pa_context_errno(stream->sound->pa)));
    }
  }
  g_object_unref (stream->audio);
  g_free (stream);
}
/*
 * Callback to be called whenever new data may be written to the
 * playback data stream
 */
static void stream_write_callback(pa_stream *stream, size_t length, void *userdata) {
    struct context *ctx = userdata;
    struct audio_file *file;
    struct pa_operation *operation;
    size_t to_write, write_unit;
    int ret;

    assert(ctx);
    assert(ctx->context);
    assert((file = ctx->file));
    assert(file->buf);
    assert(file->readi <= file->size);

    /* Writes must be in multiple of audio sample size * channel count */
    write_unit = pa_frame_size(&file->spec);

    to_write = file->size - file->readi;
    to_write = min(length, to_write);
    to_write -= (to_write % write_unit);

    ret = pa_stream_write(stream, &file->buf[file->readi], to_write, NULL, 0,
                          PA_SEEK_RELATIVE);
    if (ret < 0) {
        error("Failed writing audio data to stream: %s",
              pa_strerror(pa_context_errno(ctx->context)));
        goto fail;
    }

    file->readi += to_write;
    assert(file->readi <= file->size);

    /*
     * EOF! yay ..
     *
     * When reaching audio EOF, do not just close the application!
     * Doing so leads to losing playback of the latest portion of
     * the audio file (~ 0.5 seconds). Moreover, it produces ugly,
     * quite loud, sound cracks :-(
     *
     * The playback stream needs to be drained first. Thus close
     * the application, and the PA event loop, only after getting
     * a confirmation that the stream drain is complete.
     */
    if ((file->size - file->readi) < write_unit) {
        out("Success! - Reached end of file");
        out("Draining playback stream before exit");

        /* Don't invoke our write callback again */
        pa_stream_set_write_callback(stream, NULL, NULL);

        operation = pa_stream_drain(stream, stream_drain_complete, ctx);
        if (!operation) {
            error("Could not drain playback stream: %s",
                  pa_strerror(pa_context_errno(ctx->context)));
            goto fail;
        }
    }

    return;

fail:
    quit(ctx, EXIT_FAILURE);
}