diff options
Diffstat (limited to 'src/outputBuffer_ob_send.h')
-rw-r--r-- | src/outputBuffer_ob_send.h | 133 |
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; +} + + |