aboutsummaryrefslogtreecommitdiffstats
path: root/src/outputBuffer_ob_send.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/outputBuffer_ob_send.h')
-rw-r--r--src/outputBuffer_ob_send.h133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/outputBuffer_ob_send.h b/src/outputBuffer_ob_send.h
new file mode 100644
index 000000000..2a4a84763
--- /dev/null
+++ b/src/outputBuffer_ob_send.h
@@ -0,0 +1,133 @@
+/*
+ * This file only contains ob_send() and private functions
+ * needed to implement ob_send()
+ */
+
+/*
+ * This is one of two places where dc.thread can block,
+ * the other is readFromInputStream
+ */
+static enum dc_action await_buffer_space(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ /* DEBUG("Waiting for buffer space\n"); */
+ assert(dc.state != DC_STATE_STOP);
+
+ dc_halt();
+ /* DEBUG("done waiting for buffer space\n"); */
+ return dc.action;
+}
+
+/* This will modify its input */
+static void do_audio_conversion(void **data, size_t *len)
+{
+ size_t newlen;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ newlen = pcm_sizeOfConvBuffer(&dc.audio_format, *len, &ob.audio_format);
+ if (newlen > ob.conv_buf_len) {
+ ob.conv_buf = xrealloc(ob.conv_buf, newlen);
+ ob.conv_buf_len = newlen;
+ }
+ *len = pcm_convertAudioFormat(&dc.audio_format, *data, *len,
+ &ob.audio_format, ob.conv_buf,
+ &ob.conv_state);
+ *data = ob.conv_buf;
+}
+
+static void ensure_audio_format_sanity(void **data, size_t *len)
+{
+ static mpd_uint8 seq_last;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+ if (mpd_unlikely(seq_last != ob.seq_decoder)) {
+ seq_last = ob.seq_decoder;
+ if (cmpAudioFormat(&dc.audio_format, &ob.audio_format))
+ getOutputAudioFormat(&dc.audio_format,
+ &ob.audio_format);
+ }
+ if (cmpAudioFormat(&ob.audio_format, &dc.audio_format))
+ do_audio_conversion(data, len);
+}
+
+static void start_playback(void)
+{
+ assert(pthread_equal(pthread_self(), dc.thread));
+ if (mpd_unlikely(ob.state == OB_STATE_STOP &&
+ player_errno == PLAYER_ERROR_NONE)) {
+ ob_trigger_action(OB_ACTION_PLAY);
+ }
+}
+
+enum dc_action
+ob_send(void *data, size_t len,
+ float decode_time, mpd_uint16 bit_rate, ReplayGainInfo * rgi)
+{
+ struct iovec vec[2];
+ struct ob_chunk *c;
+ size_t idx;
+ size_t i, j;
+
+ assert(pthread_equal(pthread_self(), dc.thread));
+
+ ensure_audio_format_sanity(&data, &len);
+
+ if (rgi && (replayGainState != REPLAYGAIN_OFF))
+ doReplayGain(rgi, data, len, &ob.audio_format);
+ else if (normalizationEnabled)
+ normalizeData(data, len, &ob.audio_format);
+
+ while (1) {
+ /* full buffer, loop check in case of spurious wakeups */
+ while (!ringbuf_get_write_vector(ob.index, vec)) {
+ enum dc_action rv = await_buffer_space();
+ if (rv != DC_ACTION_NONE)
+ return rv;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vec); i++) {
+ for (j = 0; j < vec[i].iov_len; j++) {
+ size_t c_len;
+ idx = vec[i].iov_base - ob.index->buf + j;
+ c = &(ob.chunks[idx]);
+
+ if (!c->len) { /* populate empty chunk */
+ c->seq = ob.seq_decoder;
+ c->time = decode_time;
+ c->bit_rate = bit_rate;
+ c_len = len > CHUNK_SIZE ? CHUNK_SIZE
+ : len;
+ c->len = (mpd_uint16)c_len;
+ memcpy(c->data, data, c_len);
+ } else { /* partially filled chunk */
+ size_t max = CHUNK_SIZE - c->len;
+ assert(c->seq == ob.seq_decoder);
+ c_len = len > max ? max : len;
+ assert(c_len <= CHUNK_SIZE);
+ memcpy(c->data + c->len, data, c_len);
+ c->len += c_len;
+ assert(c->len <= CHUNK_SIZE);
+ }
+
+ /*
+ * feed ob.thread ASAP, otherwise ob.thread
+ * will just play silence
+ */
+ if (c->len == CHUNK_SIZE)
+ ringbuf_write_advance(ob.index, 1);
+
+ assert(len >= c_len);
+ len -= c_len;
+ if (!len) {
+ start_playback();
+ return dc.action;
+ }
+ data += c_len;
+ }
+ }
+ }
+ assert(__FILE__ && __LINE__ && "We should never get here" && 0);
+ return DC_ACTION_NONE;
+}
+
+