MusicPipe: expose the C++ API
This commit is contained in:
parent
17e108a10a
commit
ce1d897575
@ -164,7 +164,7 @@ decoder_command_finished(struct decoder *decoder)
|
|||||||
if (decoder->initial_seek_running) {
|
if (decoder->initial_seek_running) {
|
||||||
assert(!decoder->seeking);
|
assert(!decoder->seeking);
|
||||||
assert(decoder->chunk == NULL);
|
assert(decoder->chunk == NULL);
|
||||||
assert(music_pipe_empty(dc->pipe));
|
assert(dc->pipe->IsEmpty());
|
||||||
|
|
||||||
decoder->initial_seek_running = false;
|
decoder->initial_seek_running = false;
|
||||||
decoder->timestamp = dc->start_ms / 1000.;
|
decoder->timestamp = dc->start_ms / 1000.;
|
||||||
@ -182,7 +182,7 @@ decoder_command_finished(struct decoder *decoder)
|
|||||||
decoder->chunk = NULL;
|
decoder->chunk = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
music_pipe_clear(dc->pipe, dc->buffer);
|
dc->pipe->Clear(dc->buffer);
|
||||||
|
|
||||||
decoder->timestamp = dc->seek_where;
|
decoder->timestamp = dc->seek_where;
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,11 @@ decoder_control::IsCurrentSong(const Song *_song) const
|
|||||||
void
|
void
|
||||||
decoder_control::Start(Song *_song,
|
decoder_control::Start(Song *_song,
|
||||||
unsigned _start_ms, unsigned _end_ms,
|
unsigned _start_ms, unsigned _end_ms,
|
||||||
music_buffer *_buffer, music_pipe *_pipe)
|
music_buffer *_buffer, MusicPipe &_pipe)
|
||||||
{
|
{
|
||||||
assert(_song != NULL);
|
assert(_song != NULL);
|
||||||
assert(_buffer != NULL);
|
assert(_buffer != NULL);
|
||||||
assert(_pipe != NULL);
|
assert(_pipe.IsEmpty());
|
||||||
assert(music_pipe_empty(_pipe));
|
|
||||||
|
|
||||||
if (song != nullptr)
|
if (song != nullptr)
|
||||||
song->Free();
|
song->Free();
|
||||||
@ -119,7 +118,7 @@ decoder_control::Start(Song *_song,
|
|||||||
start_ms = _start_ms;
|
start_ms = _start_ms;
|
||||||
end_ms = _end_ms;
|
end_ms = _end_ms;
|
||||||
buffer = _buffer;
|
buffer = _buffer;
|
||||||
pipe = _pipe;
|
pipe = &_pipe;
|
||||||
|
|
||||||
dc_command(this, DECODE_COMMAND_START);
|
dc_command(this, DECODE_COMMAND_START);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
struct Song;
|
struct Song;
|
||||||
|
class MusicPipe;
|
||||||
|
|
||||||
enum decoder_state {
|
enum decoder_state {
|
||||||
DECODE_STATE_STOP = 0,
|
DECODE_STATE_STOP = 0,
|
||||||
@ -127,7 +128,7 @@ struct decoder_control {
|
|||||||
* The destination pipe for decoded chunks. The caller thread
|
* The destination pipe for decoded chunks. The caller thread
|
||||||
* owns this object, and is responsible for freeing it.
|
* owns this object, and is responsible for freeing it.
|
||||||
*/
|
*/
|
||||||
struct music_pipe *pipe;
|
MusicPipe *pipe;
|
||||||
|
|
||||||
float replay_gain_db;
|
float replay_gain_db;
|
||||||
float replay_gain_prev_db;
|
float replay_gain_prev_db;
|
||||||
@ -287,7 +288,7 @@ struct decoder_control {
|
|||||||
* the caller)
|
* the caller)
|
||||||
*/
|
*/
|
||||||
void Start(Song *song, unsigned start_ms, unsigned end_ms,
|
void Start(Song *song, unsigned start_ms, unsigned end_ms,
|
||||||
music_buffer *buffer, music_pipe *pipe);
|
music_buffer *buffer, MusicPipe &pipe);
|
||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ decoder_flush_chunk(struct decoder *decoder)
|
|||||||
if (decoder->chunk->IsEmpty())
|
if (decoder->chunk->IsEmpty())
|
||||||
music_buffer_return(dc->buffer, decoder->chunk);
|
music_buffer_return(dc->buffer, decoder->chunk);
|
||||||
else
|
else
|
||||||
music_pipe_push(dc->pipe, decoder->chunk);
|
dc->pipe->Push(decoder->chunk);
|
||||||
|
|
||||||
decoder->chunk = NULL;
|
decoder->chunk = NULL;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ struct Tag;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A chunk of music data. Its format is defined by the
|
* A chunk of music data. Its format is defined by the
|
||||||
* music_pipe_append() caller.
|
* MusicPipe::Push() caller.
|
||||||
*/
|
*/
|
||||||
struct music_chunk {
|
struct music_chunk {
|
||||||
/** the next chunk in a linked list */
|
/** the next chunk in a linked list */
|
||||||
|
@ -21,74 +21,15 @@
|
|||||||
#include "MusicPipe.hxx"
|
#include "MusicPipe.hxx"
|
||||||
#include "MusicBuffer.hxx"
|
#include "MusicBuffer.hxx"
|
||||||
#include "MusicChunk.hxx"
|
#include "MusicChunk.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
struct music_pipe {
|
|
||||||
/** the first chunk */
|
|
||||||
struct music_chunk *head;
|
|
||||||
|
|
||||||
/** a pointer to the tail of the chunk */
|
|
||||||
struct music_chunk **tail_r;
|
|
||||||
|
|
||||||
/** the current number of chunks */
|
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
/** a mutex which protects #head and #tail_r */
|
|
||||||
mutable Mutex mutex;
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
AudioFormat audio_format;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
music_pipe()
|
|
||||||
:head(nullptr), tail_r(&head), size(0) {
|
|
||||||
#ifndef NDEBUG
|
|
||||||
audio_format.Clear();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
~music_pipe() {
|
|
||||||
assert(head == nullptr);
|
|
||||||
assert(tail_r == &head);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct music_pipe *
|
|
||||||
music_pipe_new(void)
|
|
||||||
{
|
|
||||||
return new music_pipe();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
music_pipe_free(struct music_pipe *mp)
|
|
||||||
{
|
|
||||||
delete mp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
||||||
bool
|
bool
|
||||||
music_pipe_check_format(const struct music_pipe *pipe,
|
MusicPipe::Contains(const music_chunk *chunk) const
|
||||||
const AudioFormat audio_format)
|
|
||||||
{
|
{
|
||||||
assert(pipe != NULL);
|
const ScopeLock protect(mutex);
|
||||||
|
|
||||||
return !pipe->audio_format.IsDefined() ||
|
for (const struct music_chunk *i = head; i != nullptr; i = i->next)
|
||||||
pipe->audio_format == audio_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
music_pipe_contains(const struct music_pipe *mp,
|
|
||||||
const struct music_chunk *chunk)
|
|
||||||
{
|
|
||||||
const ScopeLock protect(mp->mutex);
|
|
||||||
|
|
||||||
for (const struct music_chunk *i = mp->head;
|
|
||||||
i != NULL; i = i->next)
|
|
||||||
if (i == chunk)
|
if (i == chunk)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -97,40 +38,34 @@ music_pipe_contains(const struct music_pipe *mp,
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const struct music_chunk *
|
music_chunk *
|
||||||
music_pipe_peek(const struct music_pipe *mp)
|
MusicPipe::Shift()
|
||||||
{
|
{
|
||||||
return mp->head;
|
const ScopeLock protect(mutex);
|
||||||
}
|
|
||||||
|
|
||||||
struct music_chunk *
|
music_chunk *chunk = head;
|
||||||
music_pipe_shift(struct music_pipe *mp)
|
if (chunk != nullptr) {
|
||||||
{
|
|
||||||
const ScopeLock protect(mp->mutex);
|
|
||||||
|
|
||||||
struct music_chunk *chunk = mp->head;
|
|
||||||
if (chunk != NULL) {
|
|
||||||
assert(!chunk->IsEmpty());
|
assert(!chunk->IsEmpty());
|
||||||
|
|
||||||
mp->head = chunk->next;
|
head = chunk->next;
|
||||||
--mp->size;
|
--size;
|
||||||
|
|
||||||
if (mp->head == NULL) {
|
if (head == nullptr) {
|
||||||
assert(mp->size == 0);
|
assert(size == 0);
|
||||||
assert(mp->tail_r == &chunk->next);
|
assert(tail_r == &chunk->next);
|
||||||
|
|
||||||
mp->tail_r = &mp->head;
|
tail_r = &head;
|
||||||
} else {
|
} else {
|
||||||
assert(mp->size > 0);
|
assert(size > 0);
|
||||||
assert(mp->tail_r != &chunk->next);
|
assert(tail_r != &chunk->next);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
/* poison the "next" reference */
|
/* poison the "next" reference */
|
||||||
chunk->next = (struct music_chunk *)(void *)0x01010101;
|
chunk->next = (music_chunk *)(void *)0x01010101;
|
||||||
|
|
||||||
if (mp->size == 0)
|
if (size == 0)
|
||||||
mp->audio_format.Clear();
|
audio_format.Clear();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,41 +73,34 @@ music_pipe_shift(struct music_pipe *mp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer)
|
MusicPipe::Clear(music_buffer *buffer)
|
||||||
{
|
{
|
||||||
struct music_chunk *chunk;
|
music_chunk *chunk;
|
||||||
|
|
||||||
while ((chunk = music_pipe_shift(mp)) != NULL)
|
while ((chunk = Shift()) != nullptr)
|
||||||
music_buffer_return(buffer, chunk);
|
music_buffer_return(buffer, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
|
MusicPipe::Push(music_chunk *chunk)
|
||||||
{
|
{
|
||||||
assert(!chunk->IsEmpty());
|
assert(!chunk->IsEmpty());
|
||||||
assert(chunk->length == 0 || chunk->audio_format.IsValid());
|
assert(chunk->length == 0 || chunk->audio_format.IsValid());
|
||||||
|
|
||||||
const ScopeLock protect(mp->mutex);
|
const ScopeLock protect(mutex);
|
||||||
|
|
||||||
assert(mp->size > 0 || !mp->audio_format.IsDefined());
|
assert(size > 0 || !audio_format.IsDefined());
|
||||||
assert(!mp->audio_format.IsDefined() ||
|
assert(!audio_format.IsDefined() ||
|
||||||
chunk->CheckFormat(mp->audio_format));
|
chunk->CheckFormat(audio_format));
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (!mp->audio_format.IsDefined() && chunk->length > 0)
|
if (!audio_format.IsDefined() && chunk->length > 0)
|
||||||
mp->audio_format = chunk->audio_format;
|
audio_format = chunk->audio_format;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
chunk->next = NULL;
|
chunk->next = nullptr;
|
||||||
*mp->tail_r = chunk;
|
*tail_r = chunk;
|
||||||
mp->tail_r = &chunk->next;
|
tail_r = &chunk->next;
|
||||||
|
|
||||||
++mp->size;
|
++size;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
music_pipe_size(const struct music_pipe *mp)
|
|
||||||
{
|
|
||||||
const ScopeLock protect(mp->mutex);
|
|
||||||
return mp->size;
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,15 @@
|
|||||||
#ifndef MPD_PIPE_H
|
#ifndef MPD_PIPE_H
|
||||||
#define MPD_PIPE_H
|
#define MPD_PIPE_H
|
||||||
|
|
||||||
|
#include "thread/Mutex.hxx"
|
||||||
#include "gcc.h"
|
#include "gcc.h"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
struct AudioFormat;
|
#include "AudioFormat.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
struct music_chunk;
|
struct music_chunk;
|
||||||
struct music_buffer;
|
struct music_buffer;
|
||||||
|
|
||||||
@ -33,80 +36,98 @@ struct music_buffer;
|
|||||||
* A queue of #music_chunk objects. One party appends chunks at the
|
* A queue of #music_chunk objects. One party appends chunks at the
|
||||||
* tail, and the other consumes them from the head.
|
* tail, and the other consumes them from the head.
|
||||||
*/
|
*/
|
||||||
struct music_pipe;
|
class MusicPipe {
|
||||||
|
/** the first chunk */
|
||||||
|
music_chunk *head;
|
||||||
|
|
||||||
/**
|
/** a pointer to the tail of the chunk */
|
||||||
* Creates a new #music_pipe object. It is empty.
|
music_chunk **tail_r;
|
||||||
*/
|
|
||||||
gcc_malloc
|
|
||||||
struct music_pipe *
|
|
||||||
music_pipe_new(void);
|
|
||||||
|
|
||||||
/**
|
/** the current number of chunks */
|
||||||
* Frees the object. It must be empty now.
|
unsigned size;
|
||||||
*/
|
|
||||||
void
|
/** a mutex which protects #head and #tail_r */
|
||||||
music_pipe_free(struct music_pipe *mp);
|
mutable Mutex mutex;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
AudioFormat audio_format;
|
||||||
/**
|
|
||||||
* Checks if the audio format if the chunk is equal to the specified
|
|
||||||
* audio_format.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
music_pipe_check_format(const struct music_pipe *pipe,
|
|
||||||
AudioFormat audio_format);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the specified chunk is enqueued in the music pipe.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
music_pipe_contains(const struct music_pipe *mp,
|
|
||||||
const struct music_chunk *chunk);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
public:
|
||||||
* Returns the first #music_chunk from the pipe. Returns NULL if the
|
/**
|
||||||
* pipe is empty.
|
* Creates a new #MusicPipe object. It is empty.
|
||||||
*/
|
*/
|
||||||
gcc_pure
|
MusicPipe()
|
||||||
const struct music_chunk *
|
:head(nullptr), tail_r(&head), size(0) {
|
||||||
music_pipe_peek(const struct music_pipe *mp);
|
#ifndef NDEBUG
|
||||||
|
audio_format.Clear();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the first chunk from the head, and returns it.
|
* Frees the object. It must be empty now.
|
||||||
*/
|
*/
|
||||||
struct music_chunk *
|
~MusicPipe() {
|
||||||
music_pipe_shift(struct music_pipe *mp);
|
assert(head == nullptr);
|
||||||
|
assert(tail_r == &head);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
#ifndef NDEBUG
|
||||||
* Clears the whole pipe and returns the chunks to the buffer.
|
/**
|
||||||
*
|
* Checks if the audio format if the chunk is equal to the specified
|
||||||
* @param buffer the buffer object to return the chunks to
|
* audio_format.
|
||||||
*/
|
*/
|
||||||
void
|
gcc_pure
|
||||||
music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer);
|
bool CheckFormat(AudioFormat other) const {
|
||||||
|
return !audio_format.IsDefined() ||
|
||||||
|
audio_format == other;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pushes a chunk to the tail of the pipe.
|
* Checks if the specified chunk is enqueued in the music pipe.
|
||||||
*/
|
*/
|
||||||
void
|
gcc_pure
|
||||||
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
|
bool Contains(const music_chunk *chunk) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of chunks currently in this pipe.
|
* Returns the first #music_chunk from the pipe. Returns
|
||||||
*/
|
* nullptr if the pipe is empty.
|
||||||
gcc_pure
|
*/
|
||||||
unsigned
|
gcc_pure
|
||||||
music_pipe_size(const struct music_pipe *mp);
|
const music_chunk *Peek() const {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
gcc_pure
|
/**
|
||||||
static inline bool
|
* Removes the first chunk from the head, and returns it.
|
||||||
music_pipe_empty(const struct music_pipe *mp)
|
*/
|
||||||
{
|
music_chunk *Shift();
|
||||||
return music_pipe_size(mp) == 0;
|
|
||||||
}
|
/**
|
||||||
|
* Clears the whole pipe and returns the chunks to the buffer.
|
||||||
|
*
|
||||||
|
* @param buffer the buffer object to return the chunks to
|
||||||
|
*/
|
||||||
|
void Clear(music_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a chunk to the tail of the pipe.
|
||||||
|
*/
|
||||||
|
void Push(music_chunk *chunk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of chunks currently in this pipe.
|
||||||
|
*/
|
||||||
|
gcc_pure
|
||||||
|
unsigned GetSize() const {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return GetSize() == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,10 +50,10 @@ static unsigned int num_audio_outputs;
|
|||||||
static struct music_buffer *g_music_buffer;
|
static struct music_buffer *g_music_buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The #music_pipe object which feeds all audio outputs. It is filled
|
* The #MusicPipe object which feeds all audio outputs. It is filled
|
||||||
* by audio_output_all_play().
|
* by audio_output_all_play().
|
||||||
*/
|
*/
|
||||||
static struct music_pipe *g_mp;
|
static MusicPipe *g_mp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "elapsed_time" stamp of the most recently finished chunk.
|
* The "elapsed_time" stamp of the most recently finished chunk.
|
||||||
@ -262,7 +262,7 @@ audio_output_all_update(void)
|
|||||||
|
|
||||||
for (i = 0; i < num_audio_outputs; ++i)
|
for (i = 0; i < num_audio_outputs; ++i)
|
||||||
ret = audio_output_update(audio_outputs[i],
|
ret = audio_output_update(audio_outputs[i],
|
||||||
input_audio_format, g_mp) || ret;
|
input_audio_format, *g_mp) || ret;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ audio_output_all_play(struct music_chunk *chunk, Error &error)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
music_pipe_push(g_mp, chunk);
|
g_mp->Push(chunk);
|
||||||
|
|
||||||
for (i = 0; i < num_audio_outputs; ++i)
|
for (i = 0; i < num_audio_outputs; ++i)
|
||||||
audio_output_play(audio_outputs[i]);
|
audio_output_play(audio_outputs[i]);
|
||||||
@ -316,15 +316,14 @@ audio_output_all_open(const AudioFormat audio_format,
|
|||||||
|
|
||||||
/* the audio format must be the same as existing chunks in the
|
/* the audio format must be the same as existing chunks in the
|
||||||
pipe */
|
pipe */
|
||||||
assert(g_mp == NULL || music_pipe_check_format(g_mp, audio_format));
|
assert(g_mp == NULL || g_mp->CheckFormat(audio_format));
|
||||||
|
|
||||||
if (g_mp == NULL)
|
if (g_mp == NULL)
|
||||||
g_mp = music_pipe_new();
|
g_mp = new MusicPipe();
|
||||||
else
|
else
|
||||||
/* if the pipe hasn't been cleared, the the audio
|
/* if the pipe hasn't been cleared, the the audio
|
||||||
format must not have changed */
|
format must not have changed */
|
||||||
assert(music_pipe_empty(g_mp) ||
|
assert(g_mp->IsEmpty() || audio_format == input_audio_format);
|
||||||
audio_format == input_audio_format);
|
|
||||||
|
|
||||||
input_audio_format = audio_format;
|
input_audio_format = audio_format;
|
||||||
|
|
||||||
@ -366,7 +365,7 @@ chunk_is_consumed_in(const struct audio_output *ao,
|
|||||||
if (ao->chunk == NULL)
|
if (ao->chunk == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
assert(chunk == ao->chunk || music_pipe_contains(g_mp, ao->chunk));
|
assert(chunk == ao->chunk || g_mp->Contains(ao->chunk));
|
||||||
|
|
||||||
if (chunk != ao->chunk) {
|
if (chunk != ao->chunk) {
|
||||||
assert(chunk->next != NULL);
|
assert(chunk->next != NULL);
|
||||||
@ -401,7 +400,7 @@ static void
|
|||||||
clear_tail_chunk(gcc_unused const struct music_chunk *chunk, bool *locked)
|
clear_tail_chunk(gcc_unused const struct music_chunk *chunk, bool *locked)
|
||||||
{
|
{
|
||||||
assert(chunk->next == NULL);
|
assert(chunk->next == NULL);
|
||||||
assert(music_pipe_contains(g_mp, chunk));
|
assert(g_mp->Contains(chunk));
|
||||||
|
|
||||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||||
struct audio_output *ao = audio_outputs[i];
|
struct audio_output *ao = audio_outputs[i];
|
||||||
@ -433,13 +432,13 @@ audio_output_all_check(void)
|
|||||||
assert(g_music_buffer != NULL);
|
assert(g_music_buffer != NULL);
|
||||||
assert(g_mp != NULL);
|
assert(g_mp != NULL);
|
||||||
|
|
||||||
while ((chunk = music_pipe_peek(g_mp)) != NULL) {
|
while ((chunk = g_mp->Peek()) != nullptr) {
|
||||||
assert(!music_pipe_empty(g_mp));
|
assert(!g_mp->IsEmpty());
|
||||||
|
|
||||||
if (!chunk_is_consumed(chunk))
|
if (!chunk_is_consumed(chunk))
|
||||||
/* at least one output is not finished playing
|
/* at least one output is not finished playing
|
||||||
this chunk */
|
this chunk */
|
||||||
return music_pipe_size(g_mp);
|
return g_mp->GetSize();
|
||||||
|
|
||||||
if (chunk->length > 0 && chunk->times >= 0.0)
|
if (chunk->length > 0 && chunk->times >= 0.0)
|
||||||
/* only update elapsed_time if the chunk
|
/* only update elapsed_time if the chunk
|
||||||
@ -453,7 +452,7 @@ audio_output_all_check(void)
|
|||||||
clear_tail_chunk(chunk, locked);
|
clear_tail_chunk(chunk, locked);
|
||||||
|
|
||||||
/* remove the chunk from the pipe */
|
/* remove the chunk from the pipe */
|
||||||
shifted = music_pipe_shift(g_mp);
|
shifted = g_mp->Shift();
|
||||||
assert(shifted == chunk);
|
assert(shifted == chunk);
|
||||||
|
|
||||||
if (is_tail)
|
if (is_tail)
|
||||||
@ -523,7 +522,7 @@ audio_output_all_cancel(void)
|
|||||||
/* clear the music pipe and return all chunks to the buffer */
|
/* clear the music pipe and return all chunks to the buffer */
|
||||||
|
|
||||||
if (g_mp != NULL)
|
if (g_mp != NULL)
|
||||||
music_pipe_clear(g_mp, g_music_buffer);
|
g_mp->Clear(g_music_buffer);
|
||||||
|
|
||||||
/* the audio outputs are now waiting for a signal, to
|
/* the audio outputs are now waiting for a signal, to
|
||||||
synchronize the cleared music pipe */
|
synchronize the cleared music pipe */
|
||||||
@ -546,8 +545,8 @@ audio_output_all_close(void)
|
|||||||
if (g_mp != NULL) {
|
if (g_mp != NULL) {
|
||||||
assert(g_music_buffer != NULL);
|
assert(g_music_buffer != NULL);
|
||||||
|
|
||||||
music_pipe_clear(g_mp, g_music_buffer);
|
g_mp->Clear(g_music_buffer);
|
||||||
music_pipe_free(g_mp);
|
delete g_mp;
|
||||||
g_mp = NULL;
|
g_mp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,8 +568,8 @@ audio_output_all_release(void)
|
|||||||
if (g_mp != NULL) {
|
if (g_mp != NULL) {
|
||||||
assert(g_music_buffer != NULL);
|
assert(g_music_buffer != NULL);
|
||||||
|
|
||||||
music_pipe_clear(g_mp, g_music_buffer);
|
g_mp->Clear(g_music_buffer);
|
||||||
music_pipe_free(g_mp);
|
delete g_mp;
|
||||||
g_mp = NULL;
|
g_mp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ audio_output_all_set_replay_gain_mode(enum replay_gain_mode mode);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
|
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
|
||||||
* #music_pipe.
|
* #MusicPipe.
|
||||||
*
|
*
|
||||||
* @param chunk the #music_chunk object to be played
|
* @param chunk the #music_chunk object to be played
|
||||||
* @return true on success, false if no audio output was able to play
|
* @return true on success, false if no audio output was able to play
|
||||||
@ -117,13 +117,13 @@ audio_output_all_play(struct music_chunk *chunk, Error &error);
|
|||||||
* Checks if the output devices have drained their music pipe, and
|
* Checks if the output devices have drained their music pipe, and
|
||||||
* returns the consumed music chunks to the #music_buffer.
|
* returns the consumed music chunks to the #music_buffer.
|
||||||
*
|
*
|
||||||
* @return the number of chunks to play left in the #music_pipe
|
* @return the number of chunks to play left in the #MusicPipe
|
||||||
*/
|
*/
|
||||||
unsigned
|
unsigned
|
||||||
audio_output_all_check(void);
|
audio_output_all_check(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the size of the #music_pipe is below the #threshold. If
|
* Checks if the size of the #MusicPipe is below the #threshold. If
|
||||||
* not, it attempts to synchronize with all output threads, and waits
|
* not, it attempts to synchronize with all output threads, and waits
|
||||||
* until another #music_chunk is finished.
|
* until another #music_chunk is finished.
|
||||||
*
|
*
|
||||||
|
@ -141,14 +141,13 @@ audio_output_disable(struct audio_output *ao)
|
|||||||
static bool
|
static bool
|
||||||
audio_output_open(struct audio_output *ao,
|
audio_output_open(struct audio_output *ao,
|
||||||
const AudioFormat audio_format,
|
const AudioFormat audio_format,
|
||||||
const struct music_pipe *mp)
|
const MusicPipe &mp)
|
||||||
{
|
{
|
||||||
bool open;
|
bool open;
|
||||||
|
|
||||||
assert(ao != NULL);
|
assert(ao != NULL);
|
||||||
assert(ao->allow_play);
|
assert(ao->allow_play);
|
||||||
assert(audio_format.IsValid());
|
assert(audio_format.IsValid());
|
||||||
assert(mp != NULL);
|
|
||||||
|
|
||||||
if (ao->fail_timer != NULL) {
|
if (ao->fail_timer != NULL) {
|
||||||
g_timer_destroy(ao->fail_timer);
|
g_timer_destroy(ao->fail_timer);
|
||||||
@ -156,12 +155,12 @@ audio_output_open(struct audio_output *ao,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ao->open && audio_format == ao->in_audio_format) {
|
if (ao->open && audio_format == ao->in_audio_format) {
|
||||||
assert(ao->pipe == mp ||
|
assert(ao->pipe == &mp ||
|
||||||
(ao->always_on && ao->pause));
|
(ao->always_on && ao->pause));
|
||||||
|
|
||||||
if (ao->pause) {
|
if (ao->pause) {
|
||||||
ao->chunk = NULL;
|
ao->chunk = NULL;
|
||||||
ao->pipe = mp;
|
ao->pipe = ∓
|
||||||
|
|
||||||
/* unpause with the CANCEL command; this is a
|
/* unpause with the CANCEL command; this is a
|
||||||
hack, but suits well for forcing the thread
|
hack, but suits well for forcing the thread
|
||||||
@ -179,7 +178,7 @@ audio_output_open(struct audio_output *ao,
|
|||||||
ao->in_audio_format = audio_format;
|
ao->in_audio_format = audio_format;
|
||||||
ao->chunk = NULL;
|
ao->chunk = NULL;
|
||||||
|
|
||||||
ao->pipe = mp;
|
ao->pipe = ∓
|
||||||
|
|
||||||
if (ao->thread == NULL)
|
if (ao->thread == NULL)
|
||||||
audio_output_thread_start(ao);
|
audio_output_thread_start(ao);
|
||||||
@ -223,10 +222,8 @@ audio_output_close_locked(struct audio_output *ao)
|
|||||||
bool
|
bool
|
||||||
audio_output_update(struct audio_output *ao,
|
audio_output_update(struct audio_output *ao,
|
||||||
const AudioFormat audio_format,
|
const AudioFormat audio_format,
|
||||||
const struct music_pipe *mp)
|
const MusicPipe &mp)
|
||||||
{
|
{
|
||||||
assert(mp != NULL);
|
|
||||||
|
|
||||||
const ScopeLock protect(ao->mutex);
|
const ScopeLock protect(ao->mutex);
|
||||||
|
|
||||||
if (ao->enabled && ao->really_enabled) {
|
if (ao->enabled && ao->really_enabled) {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
struct audio_output;
|
struct audio_output;
|
||||||
struct AudioFormat;
|
struct AudioFormat;
|
||||||
struct config_param;
|
struct config_param;
|
||||||
struct music_pipe;
|
class MusicPipe;
|
||||||
struct player_control;
|
struct player_control;
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -54,7 +54,7 @@ audio_output_disable(struct audio_output *ao);
|
|||||||
bool
|
bool
|
||||||
audio_output_update(struct audio_output *ao,
|
audio_output_update(struct audio_output *ao,
|
||||||
AudioFormat audio_format,
|
AudioFormat audio_format,
|
||||||
const struct music_pipe *mp);
|
const MusicPipe &mp);
|
||||||
|
|
||||||
void
|
void
|
||||||
audio_output_play(struct audio_output *ao);
|
audio_output_play(struct audio_output *ao);
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
class Filter;
|
class Filter;
|
||||||
|
class MusicPipe;
|
||||||
struct config_param;
|
struct config_param;
|
||||||
|
|
||||||
enum audio_output_command {
|
enum audio_output_command {
|
||||||
@ -209,7 +210,7 @@ struct audio_output {
|
|||||||
/**
|
/**
|
||||||
* The music pipe which provides music chunks to be played.
|
* The music pipe which provides music chunks to be played.
|
||||||
*/
|
*/
|
||||||
const struct music_pipe *pipe;
|
const MusicPipe *pipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mutex protects #open, #fail_timer, #chunk and
|
* This mutex protects #open, #fail_timer, #chunk and
|
||||||
|
@ -263,7 +263,7 @@ ao_reopen(struct audio_output *ao)
|
|||||||
{
|
{
|
||||||
if (!ao->config_audio_format.IsFullyDefined()) {
|
if (!ao->config_audio_format.IsFullyDefined()) {
|
||||||
if (ao->open) {
|
if (ao->open) {
|
||||||
const struct music_pipe *mp = ao->pipe;
|
const MusicPipe *mp = ao->pipe;
|
||||||
ao_close(ao, true);
|
ao_close(ao, true);
|
||||||
ao->pipe = mp;
|
ao->pipe = mp;
|
||||||
}
|
}
|
||||||
@ -484,7 +484,7 @@ ao_next_chunk(struct audio_output *ao)
|
|||||||
/* continue the previous play() call */
|
/* continue the previous play() call */
|
||||||
? ao->chunk->next
|
? ao->chunk->next
|
||||||
/* get the first chunk from the pipe */
|
/* get the first chunk from the pipe */
|
||||||
: music_pipe_peek(ao->pipe);
|
: ao->pipe->Peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -621,7 +621,7 @@ static gpointer audio_output_task(gpointer arg)
|
|||||||
case AO_COMMAND_DRAIN:
|
case AO_COMMAND_DRAIN:
|
||||||
if (ao->open) {
|
if (ao->open) {
|
||||||
assert(ao->chunk == NULL);
|
assert(ao->chunk == NULL);
|
||||||
assert(music_pipe_peek(ao->pipe) == NULL);
|
assert(ao->pipe->Peek() == nullptr);
|
||||||
|
|
||||||
ao->mutex.unlock();
|
ao->mutex.unlock();
|
||||||
ao_plugin_drain(ao);
|
ao_plugin_drain(ao);
|
||||||
|
@ -54,7 +54,7 @@ struct player {
|
|||||||
|
|
||||||
struct decoder_control *dc;
|
struct decoder_control *dc;
|
||||||
|
|
||||||
struct music_pipe *pipe;
|
MusicPipe *pipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* are we waiting for buffered_before_play?
|
* are we waiting for buffered_before_play?
|
||||||
@ -166,7 +166,7 @@ player_command_finished(struct player_control *pc)
|
|||||||
* Player lock is not held.
|
* Player lock is not held.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
player_dc_start(struct player *player, struct music_pipe *pipe)
|
player_dc_start(struct player *player, MusicPipe &pipe)
|
||||||
{
|
{
|
||||||
struct player_control *pc = player->pc;
|
struct player_control *pc = player->pc;
|
||||||
struct decoder_control *dc = player->dc;
|
struct decoder_control *dc = player->dc;
|
||||||
@ -224,10 +224,10 @@ player_dc_stop(struct player *player)
|
|||||||
if (dc->pipe != NULL) {
|
if (dc->pipe != NULL) {
|
||||||
/* clear and free the decoder pipe */
|
/* clear and free the decoder pipe */
|
||||||
|
|
||||||
music_pipe_clear(dc->pipe, player_buffer);
|
dc->pipe->Clear(player_buffer);
|
||||||
|
|
||||||
if (dc->pipe != player->pipe)
|
if (dc->pipe != player->pipe)
|
||||||
music_pipe_free(dc->pipe);
|
delete dc->pipe;
|
||||||
|
|
||||||
dc->pipe = NULL;
|
dc->pipe = NULL;
|
||||||
}
|
}
|
||||||
@ -493,10 +493,10 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
|
|
||||||
/* clear music chunks which might still reside in the
|
/* clear music chunks which might still reside in the
|
||||||
pipe */
|
pipe */
|
||||||
music_pipe_clear(player->pipe, player_buffer);
|
player->pipe->Clear(player_buffer);
|
||||||
|
|
||||||
/* re-start the decoder */
|
/* re-start the decoder */
|
||||||
player_dc_start(player, player->pipe);
|
player_dc_start(player, *player->pipe);
|
||||||
if (!player_wait_for_decoder(player)) {
|
if (!player_wait_for_decoder(player)) {
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished(pc);
|
player_command_finished(pc);
|
||||||
@ -506,8 +506,8 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
if (!player_dc_at_current_song(player)) {
|
if (!player_dc_at_current_song(player)) {
|
||||||
/* the decoder is already decoding the "next" song,
|
/* the decoder is already decoding the "next" song,
|
||||||
but it is the same song file; exchange the pipe */
|
but it is the same song file; exchange the pipe */
|
||||||
music_pipe_clear(player->pipe, player_buffer);
|
player->pipe->Clear(player_buffer);
|
||||||
music_pipe_free(player->pipe);
|
delete player->pipe;
|
||||||
player->pipe = dc->pipe;
|
player->pipe = dc->pipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,11 +734,10 @@ play_next_chunk(struct player *player)
|
|||||||
struct music_chunk *chunk = NULL;
|
struct music_chunk *chunk = NULL;
|
||||||
if (player->xfade == XFADE_ENABLED &&
|
if (player->xfade == XFADE_ENABLED &&
|
||||||
player_dc_at_next_song(player) &&
|
player_dc_at_next_song(player) &&
|
||||||
(cross_fade_position = music_pipe_size(player->pipe))
|
(cross_fade_position = player->pipe->GetSize())
|
||||||
<= player->cross_fade_chunks) {
|
<= player->cross_fade_chunks) {
|
||||||
/* perform cross fade */
|
/* perform cross fade */
|
||||||
struct music_chunk *other_chunk =
|
music_chunk *other_chunk = dc->pipe->Shift();
|
||||||
music_pipe_shift(dc->pipe);
|
|
||||||
|
|
||||||
if (!player->cross_fading) {
|
if (!player->cross_fading) {
|
||||||
/* beginning of the cross fade - adjust
|
/* beginning of the cross fade - adjust
|
||||||
@ -750,7 +749,7 @@ play_next_chunk(struct player *player)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (other_chunk != NULL) {
|
if (other_chunk != NULL) {
|
||||||
chunk = music_pipe_shift(player->pipe);
|
chunk = player->pipe->Shift();
|
||||||
assert(chunk != NULL);
|
assert(chunk != NULL);
|
||||||
assert(chunk->other == NULL);
|
assert(chunk->other == NULL);
|
||||||
|
|
||||||
@ -806,7 +805,7 @@ play_next_chunk(struct player *player)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chunk == NULL)
|
if (chunk == NULL)
|
||||||
chunk = music_pipe_shift(player->pipe);
|
chunk = player->pipe->Shift();
|
||||||
|
|
||||||
assert(chunk != NULL);
|
assert(chunk != NULL);
|
||||||
|
|
||||||
@ -848,8 +847,8 @@ play_next_chunk(struct player *player)
|
|||||||
larger block at a time */
|
larger block at a time */
|
||||||
dc->Lock();
|
dc->Lock();
|
||||||
if (!dc->IsIdle() &&
|
if (!dc->IsIdle() &&
|
||||||
music_pipe_size(dc->pipe) <= (pc->buffered_before_play +
|
dc->pipe->GetSize() <= (pc->buffered_before_play +
|
||||||
music_buffer_size(player_buffer) * 3) / 4)
|
music_buffer_size(player_buffer) * 3) / 4)
|
||||||
dc->Signal();
|
dc->Signal();
|
||||||
dc->Unlock();
|
dc->Unlock();
|
||||||
|
|
||||||
@ -874,7 +873,7 @@ player_song_border(struct player *player)
|
|||||||
g_message("played \"%s\"", uri);
|
g_message("played \"%s\"", uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
music_pipe_free(player->pipe);
|
delete player->pipe;
|
||||||
player->pipe = player->dc->pipe;
|
player->pipe = player->dc->pipe;
|
||||||
|
|
||||||
audio_output_all_song_border();
|
audio_output_all_song_border();
|
||||||
@ -910,15 +909,15 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
|
|
||||||
pc->Unlock();
|
pc->Unlock();
|
||||||
|
|
||||||
player.pipe = music_pipe_new();
|
player.pipe = new MusicPipe();
|
||||||
|
|
||||||
player_dc_start(&player, player.pipe);
|
player_dc_start(&player, *player.pipe);
|
||||||
if (!player_wait_for_decoder(&player)) {
|
if (!player_wait_for_decoder(&player)) {
|
||||||
assert(player.song == NULL);
|
assert(player.song == NULL);
|
||||||
|
|
||||||
player_dc_stop(&player);
|
player_dc_stop(&player);
|
||||||
player_command_finished(pc);
|
player_command_finished(pc);
|
||||||
music_pipe_free(player.pipe);
|
delete player.pipe;
|
||||||
GlobalEvents::Emit(GlobalEvents::PLAYLIST);
|
GlobalEvents::Emit(GlobalEvents::PLAYLIST);
|
||||||
pc->Lock();
|
pc->Lock();
|
||||||
return;
|
return;
|
||||||
@ -949,7 +948,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
until the buffer is large enough, to
|
until the buffer is large enough, to
|
||||||
prevent stuttering on slow machines */
|
prevent stuttering on slow machines */
|
||||||
|
|
||||||
if (music_pipe_size(player.pipe) < pc->buffered_before_play &&
|
if (player.pipe->GetSize() < pc->buffered_before_play &&
|
||||||
!dc->LockIsIdle()) {
|
!dc->LockIsIdle()) {
|
||||||
/* not enough decoded buffer space yet */
|
/* not enough decoded buffer space yet */
|
||||||
|
|
||||||
@ -996,7 +995,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
|
|
||||||
assert(dc->pipe == NULL || dc->pipe == player.pipe);
|
assert(dc->pipe == NULL || dc->pipe == player.pipe);
|
||||||
|
|
||||||
player_dc_start(&player, music_pipe_new());
|
player_dc_start(&player, *new MusicPipe());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/* no cross-fading if MPD is going to pause at the
|
if (/* no cross-fading if MPD is going to pause at the
|
||||||
@ -1035,7 +1034,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
if (pc->command == PLAYER_COMMAND_NONE)
|
if (pc->command == PLAYER_COMMAND_NONE)
|
||||||
pc->Wait();
|
pc->Wait();
|
||||||
continue;
|
continue;
|
||||||
} else if (!music_pipe_empty(player.pipe)) {
|
} else if (!player.pipe->IsEmpty()) {
|
||||||
/* at least one music chunk is ready - send it
|
/* at least one music chunk is ready - send it
|
||||||
to the audio output */
|
to the audio output */
|
||||||
|
|
||||||
@ -1056,7 +1055,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
/* check the size of the pipe again, because
|
/* check the size of the pipe again, because
|
||||||
the decoder thread may have added something
|
the decoder thread may have added something
|
||||||
since we last checked */
|
since we last checked */
|
||||||
if (music_pipe_empty(player.pipe)) {
|
if (player.pipe->IsEmpty()) {
|
||||||
/* wait for the hardware to finish
|
/* wait for the hardware to finish
|
||||||
playback */
|
playback */
|
||||||
audio_output_all_drain();
|
audio_output_all_drain();
|
||||||
@ -1075,8 +1074,8 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
|
|||||||
|
|
||||||
player_dc_stop(&player);
|
player_dc_stop(&player);
|
||||||
|
|
||||||
music_pipe_clear(player.pipe, player_buffer);
|
player.pipe->Clear(player_buffer);
|
||||||
music_pipe_free(player.pipe);
|
delete player.pipe;
|
||||||
|
|
||||||
delete player.cross_fade_tag;
|
delete player.cross_fade_tag;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
*
|
*
|
||||||
* The player thread itself does not do any I/O. It synchronizes with
|
* The player thread itself does not do any I/O. It synchronizes with
|
||||||
* other threads via #GMutex and #GCond objects, and passes
|
* other threads via #GMutex and #GCond objects, and passes
|
||||||
* #music_chunk instances around in #music_pipe objects.
|
* #music_chunk instances around in #MusicPipe objects.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MPD_PLAYER_THREAD_HXX
|
#ifndef MPD_PLAYER_THREAD_HXX
|
||||||
|
Loading…
Reference in New Issue
Block a user