Exemple #1
0
/* This is called whenever new data may be written to the stream */
static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
    pa_assert(s);
    pa_assert(length > 0);

    if (raw) {
        pa_assert(!sndfile);

        if (stdio_event)
            mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);

        if (!buffer)
            return;

        do_stream_write(length);

    } else {
        sf_count_t bytes;
        void *data;

        pa_assert(sndfile);

        for (;;) {
            size_t data_length = length;

            if (pa_stream_begin_write(s, &data, &data_length) < 0) {
                pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
                quit(1);
                return;
            }

            if (readf_function) {
                size_t k = pa_frame_size(&sample_spec);

                if ((bytes = readf_function(sndfile, data, (sf_count_t) (data_length/k))) > 0)
                    bytes *= (sf_count_t) k;

            } else
                bytes = sf_read_raw(sndfile, data, (sf_count_t) data_length);

            if (bytes > 0)
                pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
            else
                pa_stream_cancel_write(s);

            /* EOF? */
            if (bytes < (sf_count_t) data_length) {
                start_drain();
                break;
            }

            /* Request fulfilled */
            if ((size_t) bytes >= length)
                break;

            length -= bytes;
        }
    }
}
Exemple #2
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);
}
Exemple #3
0
/******************************************************************************
 * stream_write_cb()
 * This will be called by PulseAudio when we need to provide audio data
 * for playback.
 *****************************************************************************/
static void stream_write_cb(pa_stream *s, size_t n_requested_bytes,
        void *userdata) {
    _mbx_out out = (_mbx_out ) userdata;
    sample_t *data_to_write = NULL;
    size_t n_bytes_written = 0;
    assert ( out != NULL && out->stream == s);
    if ( out->trigger_shutdown ) {
        do_shutdown(out);
        return;
    }
    while ( n_bytes_written < n_requested_bytes ) {
        int r;
        size_t n_bytes_to_write = n_requested_bytes - n_bytes_written;
        r = pa_stream_begin_write(s, (void**)&data_to_write, &n_bytes_to_write);
        assert(n_bytes_to_write % (2*sizeof(sample_t)) == 0);
        if ( r < 0 ) {
            mbx_log_error(MBX_LOG_AUDIO_OUTPUT, "Prepare writing data to the pulseaudio server"
                " failed: %s", pa_msg(out));
            pa_stream_cancel_write(s);
            return;
        }
        if ( n_bytes_to_write > 0 ) {
            int i;
            assert ( n_bytes_to_write / sizeof(sample_t) < 44100L*2*8 );
            sample_t left[44100L*2*8];
            sample_t right[44100L*2*8];
            out->cb(left, right, n_bytes_to_write / (2*sizeof(sample_t)), out->output_cb_userdata);
            for ( i=0; i<n_bytes_to_write/(2*sizeof(sample_t)); i++ ) {
                data_to_write[2*i] = left[i];
                data_to_write[2*i+1] = right[i];
            }
            pa_stream_write(s, data_to_write, n_bytes_to_write, NULL, 0,
                PA_SEEK_RELATIVE);
            n_bytes_written += n_bytes_to_write;
        }
    }
}
Exemple #4
0
/*! Starts up audio streaming to the host. */
void HostAudio::open()
{
	m_mainloop = pa_threaded_mainloop_new();
	if (!m_mainloop) {
		qDebug("Could not acquire PulseAudio main loop");
		return;
	}
	m_api = pa_threaded_mainloop_get_api(m_mainloop);
	m_context = pa_context_new(m_api, "emumaster");
	pa_context_set_state_callback(m_context, contextStreamCallback, this);

	if (!m_context) {
		qDebug("Could not acquire PulseAudio device context");
		return;
	}
#if defined(MEEGO_EDITION_HARMATTAN)
	if (pa_context_connect(m_context, 0, PA_CONTEXT_NOFLAGS, 0) < 0) {
#elif defined(Q_WS_MAEMO_5)
	if (pa_context_connect(m_context, 0, (pa_context_flags_t)0, 0) < 0) {
#endif
		int error = pa_context_errno(m_context);
		qDebug("Could not connect to PulseAudio server: %s", pa_strerror(error));
		return;
	}
	pa_threaded_mainloop_lock(m_mainloop);
	if (pa_threaded_mainloop_start(m_mainloop) < 0) {
		qDebug("Could not start mainloop");
		return;
	}

	waitForStreamReady();

	pa_sample_spec fmt;
	fmt.channels = 2;
	fmt.format = PA_SAMPLE_S16LE;
	fmt.rate = 44100;

	pa_buffer_attr buffer_attributes;
	buffer_attributes.tlength = pa_bytes_per_second(&fmt) / 5;
	buffer_attributes.maxlength = buffer_attributes.tlength * 3;
	buffer_attributes.minreq = buffer_attributes.tlength / 3;
	buffer_attributes.prebuf = buffer_attributes.tlength;

	m_stream = pa_stream_new(m_context, "emumaster", &fmt, 0);
	if (!m_stream) {
		int error = pa_context_errno(m_context);
		qDebug("Could not acquire new PulseAudio stream: %s", pa_strerror(error));
		return;
	}
	pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
//	pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS);
	if (pa_stream_connect_playback(m_stream, 0, &buffer_attributes, flags, 0, 0) < 0) {
		m_stream = 0;
		int error = pa_context_errno(m_context);
		qDebug("Could not connect for playback: %s", pa_strerror(error));
		return;
	}

	waitForStreamReady();

	pa_threaded_mainloop_unlock(m_mainloop);
}

/*! Stops audio streaming. */
void HostAudio::close()
{
	if (m_mainloop)
		pa_threaded_mainloop_stop(m_mainloop);
	if (m_stream) {
		pa_stream_unref(m_stream);
		m_stream = 0;
	}
	if (m_context) {
		pa_context_disconnect(m_context);
		pa_context_unref(m_context);
		m_context = 0;
	}
	if (m_mainloop) {
		pa_threaded_mainloop_free(m_mainloop);
		m_mainloop = 0;
	}
}

/*! Streams a frame of audio from emulated system to the host. */
void HostAudio::sendFrame()
{
	if (!m_stream)
		return;
	pa_threaded_mainloop_lock(m_mainloop);
	void *data;
#if defined(MEEGO_EDITION_HARMATTAN)
	size_t size = -1;
	pa_stream_begin_write(m_stream, &data, &size);
#elif defined(Q_WS_MAEMO_5)
	size_t size = 4096;
	static char buf[4096];
	data = buf;
#endif
	size = qMin(size, pa_stream_writable_size(m_stream));
	if (size)
		size = m_emu->fillAudioBuffer(reinterpret_cast<char *>(data), size);
	if (size)
		pa_stream_write(m_stream, data, size, 0, 0, PA_SEEK_RELATIVE);
#if defined(MEEGO_EDITION_HARMATTAN)
	else
		pa_stream_cancel_write(m_stream);
#endif
	pa_threaded_mainloop_unlock(m_mainloop);
}

/*! \internal */
void HostAudio::waitForStreamReady()
{
	pa_context_state_t context_state = pa_context_get_state(m_context);
	while (context_state != PA_CONTEXT_READY) {
		context_state = pa_context_get_state(m_context);
		if (!PA_CONTEXT_IS_GOOD(context_state)) {
			int error = pa_context_errno(m_context);
			qDebug("Context state is not good: %s", pa_strerror(error));
			return;
		} else if (context_state == PA_CONTEXT_READY) {
			break;
		} else {
			//qDebug("PulseAudio context state is %d", context_state);
		}
		pa_threaded_mainloop_wait(m_mainloop);
	}
}