pipe: added music_buffer, rewrite music_pipe
Turn the music_pipe into a simple music_chunk queue. The music_chunk allocation code is moved to music_buffer, and is now managed with a linked list instead of a ring buffer. Two separate music_pipe objects are used by the decoder for the "current" and the "next" song, which greatly simplifies the cross-fading code.
This commit is contained in:
parent
000b2d4f3a
commit
01cf7feac7
@ -84,6 +84,7 @@ mpd_headers = \
|
|||||||
src/daemon.h \
|
src/daemon.h \
|
||||||
src/normalize.h \
|
src/normalize.h \
|
||||||
src/compress.h \
|
src/compress.h \
|
||||||
|
src/buffer.h \
|
||||||
src/pipe.h \
|
src/pipe.h \
|
||||||
src/chunk.h \
|
src/chunk.h \
|
||||||
src/path.h \
|
src/path.h \
|
||||||
@ -180,6 +181,7 @@ src_mpd_SOURCES = \
|
|||||||
src/daemon.c \
|
src/daemon.c \
|
||||||
src/normalize.c \
|
src/normalize.c \
|
||||||
src/compress.c \
|
src/compress.c \
|
||||||
|
src/buffer.c \
|
||||||
src/pipe.c \
|
src/pipe.c \
|
||||||
src/chunk.c \
|
src/chunk.c \
|
||||||
src/path.c \
|
src/path.c \
|
||||||
|
127
src/buffer.c
Normal file
127
src/buffer.c
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "chunk.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct music_buffer {
|
||||||
|
struct music_chunk *chunks;
|
||||||
|
unsigned num_chunks;
|
||||||
|
|
||||||
|
struct music_chunk *available;
|
||||||
|
|
||||||
|
/** a mutex which protects #available */
|
||||||
|
GMutex *mutex;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
unsigned num_allocated;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct music_buffer *
|
||||||
|
music_buffer_new(unsigned num_chunks)
|
||||||
|
{
|
||||||
|
struct music_buffer *buffer;
|
||||||
|
struct music_chunk *chunk;
|
||||||
|
|
||||||
|
assert(num_chunks > 0);
|
||||||
|
|
||||||
|
buffer = g_new(struct music_buffer, 1);
|
||||||
|
|
||||||
|
buffer->chunks = g_new(struct music_chunk, num_chunks);
|
||||||
|
buffer->num_chunks = num_chunks;
|
||||||
|
|
||||||
|
chunk = buffer->available = buffer->chunks;
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < num_chunks; ++i) {
|
||||||
|
chunk->next = &buffer->chunks[i];
|
||||||
|
chunk = chunk->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->next = NULL;
|
||||||
|
|
||||||
|
buffer->mutex = g_mutex_new();
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
buffer->num_allocated = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
music_buffer_free(struct music_buffer *buffer)
|
||||||
|
{
|
||||||
|
assert(buffer->chunks != NULL);
|
||||||
|
assert(buffer->num_chunks > 0);
|
||||||
|
assert(buffer->num_allocated == 0);
|
||||||
|
|
||||||
|
g_mutex_free(buffer->mutex);
|
||||||
|
g_free(buffer->chunks);
|
||||||
|
g_free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
music_buffer_size(const struct music_buffer *buffer)
|
||||||
|
{
|
||||||
|
return buffer->num_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct music_chunk *
|
||||||
|
music_buffer_allocate(struct music_buffer *buffer)
|
||||||
|
{
|
||||||
|
struct music_chunk *chunk;
|
||||||
|
|
||||||
|
g_mutex_lock(buffer->mutex);
|
||||||
|
|
||||||
|
chunk = buffer->available;
|
||||||
|
if (chunk != NULL) {
|
||||||
|
buffer->available = chunk->next;
|
||||||
|
music_chunk_init(chunk);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
++buffer->num_allocated;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock(buffer->mutex);
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
|
||||||
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
assert(chunk != NULL);
|
||||||
|
|
||||||
|
g_mutex_lock(buffer->mutex);
|
||||||
|
|
||||||
|
music_chunk_free(chunk);
|
||||||
|
chunk->next = buffer->available;
|
||||||
|
buffer->available = chunk;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
--buffer->num_allocated;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_mutex_unlock(buffer->mutex);
|
||||||
|
}
|
66
src/buffer.h
Normal file
66
src/buffer.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_MUSIC_BUFFER_H
|
||||||
|
#define MPD_MUSIC_BUFFER_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An allocator for #music_chunk objects.
|
||||||
|
*/
|
||||||
|
struct music_buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new #music_buffer object.
|
||||||
|
*
|
||||||
|
* @param num_chunks the number of #music_chunk reserved in this
|
||||||
|
* buffer
|
||||||
|
*/
|
||||||
|
struct music_buffer *
|
||||||
|
music_buffer_new(unsigned num_chunks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the #music_buffer object
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
music_buffer_free(struct music_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of reserved chunks in this buffer. This
|
||||||
|
* is the same value which was passed to the constructor
|
||||||
|
* music_buffer_new().
|
||||||
|
*/
|
||||||
|
unsigned
|
||||||
|
music_buffer_size(const struct music_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates a chunk from the buffer. When it is not used anymore,
|
||||||
|
* call music_buffer_return().
|
||||||
|
*
|
||||||
|
* @return an empty chunk or NULL if there are no chunks available
|
||||||
|
*/
|
||||||
|
struct music_chunk *
|
||||||
|
music_buffer_allocate(struct music_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a chunk to the buffer. It can be reused by
|
||||||
|
* music_buffer_allocate() then.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk);
|
||||||
|
|
||||||
|
#endif
|
@ -35,6 +35,9 @@ struct audio_format;
|
|||||||
* music_pipe_append() caller.
|
* music_pipe_append() caller.
|
||||||
*/
|
*/
|
||||||
struct music_chunk {
|
struct music_chunk {
|
||||||
|
/** the next chunk in a linked list */
|
||||||
|
struct music_chunk *next;
|
||||||
|
|
||||||
/** number of bytes stored in this chunk */
|
/** number of bytes stored in this chunk */
|
||||||
uint16_t length;
|
uint16_t length;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
#include "normalize.h"
|
#include "normalize.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
@ -90,11 +91,11 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
|
|||||||
/* delete frames from the old song position */
|
/* delete frames from the old song position */
|
||||||
|
|
||||||
if (decoder->chunk != NULL) {
|
if (decoder->chunk != NULL) {
|
||||||
music_pipe_cancel(decoder->chunk);
|
music_buffer_return(dc.buffer, decoder->chunk);
|
||||||
decoder->chunk = NULL;
|
decoder->chunk = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
music_pipe_clear();
|
music_pipe_clear(dc.pipe, dc.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.command = DECODE_COMMAND_NONE;
|
dc.command = DECODE_COMMAND_NONE;
|
||||||
@ -167,15 +168,18 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
|
|||||||
if (decoder->chunk != NULL) {
|
if (decoder->chunk != NULL) {
|
||||||
/* there is a partial chunk - flush it, we want the
|
/* there is a partial chunk - flush it, we want the
|
||||||
tag in a new chunk */
|
tag in a new chunk */
|
||||||
enum decoder_command cmd =
|
decoder_flush_chunk(decoder);
|
||||||
decoder_flush_chunk(decoder, is);
|
notify_signal(&pc.notify);
|
||||||
if (cmd != DECODE_COMMAND_NONE)
|
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(decoder->chunk == NULL);
|
assert(decoder->chunk == NULL);
|
||||||
|
|
||||||
chunk = decoder_get_chunk(decoder);
|
chunk = decoder_get_chunk(decoder, is);
|
||||||
|
if (chunk == NULL) {
|
||||||
|
assert(dc.command != DECODE_COMMAND_NONE);
|
||||||
|
return dc.command;
|
||||||
|
}
|
||||||
|
|
||||||
chunk->tag = tag_dup(tag);
|
chunk->tag = tag_dup(tag);
|
||||||
return DECODE_COMMAND_NONE;
|
return DECODE_COMMAND_NONE;
|
||||||
}
|
}
|
||||||
@ -256,15 +260,18 @@ decoder_data(struct decoder *decoder,
|
|||||||
size_t nbytes;
|
size_t nbytes;
|
||||||
bool full;
|
bool full;
|
||||||
|
|
||||||
chunk = decoder_get_chunk(decoder);
|
chunk = decoder_get_chunk(decoder, is);
|
||||||
|
if (chunk == NULL) {
|
||||||
|
assert(dc.command != DECODE_COMMAND_NONE);
|
||||||
|
return dc.command;
|
||||||
|
}
|
||||||
|
|
||||||
dest = music_chunk_write(chunk, &dc.out_audio_format,
|
dest = music_chunk_write(chunk, &dc.out_audio_format,
|
||||||
data_time, bitRate, &nbytes);
|
data_time, bitRate, &nbytes);
|
||||||
if (dest == NULL) {
|
if (dest == NULL) {
|
||||||
/* the chunk is full, flush it */
|
/* the chunk is full, flush it */
|
||||||
enum decoder_command cmd =
|
decoder_flush_chunk(decoder);
|
||||||
decoder_flush_chunk(decoder, is);
|
notify_signal(&pc.notify);
|
||||||
if (cmd != DECODE_COMMAND_NONE)
|
|
||||||
return cmd;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,10 +298,8 @@ decoder_data(struct decoder *decoder,
|
|||||||
full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes);
|
full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes);
|
||||||
if (full) {
|
if (full) {
|
||||||
/* the chunk is full, flush it */
|
/* the chunk is full, flush it */
|
||||||
enum decoder_command cmd =
|
decoder_flush_chunk(decoder);
|
||||||
decoder_flush_chunk(decoder, is);
|
notify_signal(&pc.notify);
|
||||||
if (cmd != DECODE_COMMAND_NONE)
|
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data += nbytes;
|
data += nbytes;
|
||||||
|
@ -66,6 +66,12 @@ struct decoder_control {
|
|||||||
struct song *current_song;
|
struct song *current_song;
|
||||||
struct song *next_song;
|
struct song *next_song;
|
||||||
float total_time;
|
float total_time;
|
||||||
|
|
||||||
|
/** the #music_chunk allocator */
|
||||||
|
struct music_buffer *buffer;
|
||||||
|
|
||||||
|
/** the destination pipe for decoded chunks */
|
||||||
|
struct music_pipe *pipe;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct decoder_control dc;
|
extern struct decoder_control dc;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "input_stream.h"
|
#include "input_stream.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -46,35 +47,32 @@ need_chunks(struct input_stream *is, bool do_wait)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct music_chunk *
|
struct music_chunk *
|
||||||
decoder_get_chunk(struct decoder *decoder)
|
decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
|
||||||
{
|
{
|
||||||
|
enum decoder_command cmd;
|
||||||
|
|
||||||
assert(decoder != NULL);
|
assert(decoder != NULL);
|
||||||
|
|
||||||
if (decoder->chunk != NULL)
|
if (decoder->chunk != NULL)
|
||||||
return decoder->chunk;
|
return decoder->chunk;
|
||||||
|
|
||||||
decoder->chunk = music_pipe_allocate();
|
do {
|
||||||
return decoder->chunk;
|
decoder->chunk = music_buffer_allocate(dc.buffer);
|
||||||
|
if (decoder->chunk != NULL)
|
||||||
|
return decoder->chunk;
|
||||||
|
|
||||||
|
cmd = need_chunks(is, true);
|
||||||
|
} while (cmd == DECODE_COMMAND_NONE);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum decoder_command
|
void
|
||||||
decoder_flush_chunk(struct decoder *decoder, struct input_stream *is)
|
decoder_flush_chunk(struct decoder *decoder)
|
||||||
{
|
{
|
||||||
bool success;
|
|
||||||
enum decoder_command cmd;
|
|
||||||
|
|
||||||
assert(decoder != NULL);
|
assert(decoder != NULL);
|
||||||
assert(decoder->chunk != NULL);
|
assert(decoder->chunk != NULL);
|
||||||
|
|
||||||
while (true) {
|
music_pipe_push(dc.pipe, decoder->chunk);
|
||||||
success = music_pipe_push(decoder->chunk);
|
decoder->chunk = NULL;
|
||||||
if (success) {
|
|
||||||
decoder->chunk = NULL;
|
|
||||||
return DECODE_COMMAND_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = need_chunks(is, true);
|
|
||||||
if (cmd != DECODE_COMMAND_NONE)
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -42,17 +42,16 @@ struct decoder {
|
|||||||
/**
|
/**
|
||||||
* Returns the current chunk the decoder writes to, or allocates a new
|
* Returns the current chunk the decoder writes to, or allocates a new
|
||||||
* chunk if there is none.
|
* chunk if there is none.
|
||||||
|
*
|
||||||
|
* @return the chunk, or NULL if we have received a decoder command
|
||||||
*/
|
*/
|
||||||
struct music_chunk *
|
struct music_chunk *
|
||||||
decoder_get_chunk(struct decoder *decoder);
|
decoder_get_chunk(struct decoder *decoder, struct input_stream *is);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flushes a chunk. Waits for room in the music pipe if required.
|
* Flushes the current chunk.
|
||||||
*
|
|
||||||
* @return DECODE_COMMAND_NONE on success, any other command if we
|
|
||||||
* have received a decoder command while waiting
|
|
||||||
*/
|
*/
|
||||||
enum decoder_command
|
void
|
||||||
decoder_flush_chunk(struct decoder *decoder, struct input_stream *is);
|
decoder_flush_chunk(struct decoder *decoder);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -196,9 +196,8 @@ static void decoder_run_song(const struct song *song, const char *uri)
|
|||||||
pcm_convert_deinit(&decoder.conv_state);
|
pcm_convert_deinit(&decoder.conv_state);
|
||||||
|
|
||||||
/* flush the last chunk */
|
/* flush the last chunk */
|
||||||
if (decoder.chunk != NULL &&
|
if (decoder.chunk != NULL)
|
||||||
decoder_flush_chunk(&decoder, NULL) != DECODE_COMMAND_NONE)
|
decoder_flush_chunk(&decoder);
|
||||||
music_pipe_cancel(decoder.chunk);
|
|
||||||
|
|
||||||
if (close_instream)
|
if (close_instream)
|
||||||
input_stream_close(&input_stream);
|
input_stream_close(&input_stream);
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "mapper.h"
|
#include "mapper.h"
|
||||||
#include "pipe.h"
|
|
||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
#include "decoder_control.h"
|
#include "decoder_control.h"
|
||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
@ -179,8 +178,7 @@ initialize_decoder_and_player(void)
|
|||||||
if (buffered_before_play > buffered_chunks)
|
if (buffered_before_play > buffered_chunks)
|
||||||
buffered_before_play = buffered_chunks;
|
buffered_before_play = buffered_chunks;
|
||||||
|
|
||||||
pc_init(buffered_before_play);
|
pc_init(buffered_chunks, buffered_before_play);
|
||||||
music_pipe_init(buffered_chunks, &pc.notify);
|
|
||||||
dc_init();
|
dc_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +331,6 @@ int main(int argc, char *argv[])
|
|||||||
#ifdef ENABLE_ARCHIVE
|
#ifdef ENABLE_ARCHIVE
|
||||||
archive_plugin_deinit_all();
|
archive_plugin_deinit_all();
|
||||||
#endif
|
#endif
|
||||||
music_pipe_free();
|
|
||||||
config_global_finish();
|
config_global_finish();
|
||||||
tag_pool_deinit();
|
tag_pool_deinit();
|
||||||
songvec_deinit();
|
songvec_deinit();
|
||||||
|
247
src/pipe.c
247
src/pipe.c
@ -17,208 +17,103 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
|
#include "buffer.h"
|
||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
#include "notify.h"
|
|
||||||
#include "audio_format.h"
|
|
||||||
#include "tag.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct music_pipe music_pipe;
|
#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 */
|
||||||
|
GMutex *mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct music_pipe *
|
||||||
|
music_pipe_new(void)
|
||||||
|
{
|
||||||
|
struct music_pipe *mp = g_new(struct music_pipe, 1);
|
||||||
|
|
||||||
|
mp->head = NULL;
|
||||||
|
mp->tail_r = &mp->head;
|
||||||
|
mp->size = 0;
|
||||||
|
mp->mutex = g_mutex_new();
|
||||||
|
|
||||||
|
return mp;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
music_pipe_init(unsigned int size, struct notify *notify)
|
music_pipe_free(struct music_pipe *mp)
|
||||||
{
|
{
|
||||||
assert(size > 0);
|
assert(mp->head == NULL);
|
||||||
|
assert(mp->tail_r == &mp->head);
|
||||||
|
|
||||||
music_pipe.chunks = g_new(struct music_chunk, size);
|
g_mutex_free(mp->mutex);
|
||||||
music_pipe.num_chunks = size;
|
g_free(mp);
|
||||||
music_pipe.begin = 0;
|
|
||||||
music_pipe.end = 0;
|
|
||||||
music_pipe.lazy = false;
|
|
||||||
music_pipe.notify = notify;
|
|
||||||
music_chunk_init(&music_pipe.chunks[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void music_pipe_free(void)
|
|
||||||
{
|
|
||||||
assert(music_pipe.chunks != NULL);
|
|
||||||
|
|
||||||
music_pipe_clear();
|
|
||||||
|
|
||||||
g_free(music_pipe.chunks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** return the index of the chunk after i */
|
|
||||||
static inline unsigned successor(unsigned i)
|
|
||||||
{
|
|
||||||
assert(i < music_pipe.num_chunks);
|
|
||||||
|
|
||||||
++i;
|
|
||||||
return i == music_pipe.num_chunks ? 0 : i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void music_pipe_clear(void)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = music_pipe.begin; i != music_pipe.end; i = successor(i))
|
|
||||||
music_chunk_free(&music_pipe.chunks[i]);
|
|
||||||
|
|
||||||
music_chunk_free(&music_pipe.chunks[music_pipe.end]);
|
|
||||||
|
|
||||||
music_pipe.end = music_pipe.begin;
|
|
||||||
music_chunk_init(&music_pipe.chunks[music_pipe.end]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the tail chunk as "full" and wake up the player if is waiting
|
|
||||||
* for the decoder.
|
|
||||||
*/
|
|
||||||
static void output_buffer_expand(unsigned i)
|
|
||||||
{
|
|
||||||
int was_empty = music_pipe.notify != NULL && (!music_pipe.lazy || music_pipe_is_empty());
|
|
||||||
|
|
||||||
assert(i == (music_pipe.end + 1) % music_pipe.num_chunks);
|
|
||||||
assert(i != music_pipe.end);
|
|
||||||
|
|
||||||
music_pipe.end = i;
|
|
||||||
music_chunk_init(&music_pipe.chunks[i]);
|
|
||||||
|
|
||||||
if (was_empty)
|
|
||||||
/* if the buffer was empty, the player thread might be
|
|
||||||
waiting for us; wake it up now that another decoded
|
|
||||||
buffer has become available. */
|
|
||||||
notify_signal(music_pipe.notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
void music_pipe_set_lazy(bool lazy)
|
|
||||||
{
|
|
||||||
music_pipe.lazy = lazy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void music_pipe_shift(void)
|
|
||||||
{
|
|
||||||
assert(music_pipe.begin != music_pipe.end);
|
|
||||||
assert(music_pipe.begin < music_pipe.num_chunks);
|
|
||||||
|
|
||||||
music_chunk_free(&music_pipe.chunks[music_pipe.begin]);
|
|
||||||
|
|
||||||
music_pipe.begin = successor(music_pipe.begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int music_pipe_relative(const unsigned i)
|
|
||||||
{
|
|
||||||
if (i >= music_pipe.begin)
|
|
||||||
return i - music_pipe.begin;
|
|
||||||
else
|
|
||||||
return i + music_pipe.num_chunks - music_pipe.begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned music_pipe_available(void)
|
|
||||||
{
|
|
||||||
return music_pipe_relative(music_pipe.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
int music_pipe_absolute(const unsigned relative)
|
|
||||||
{
|
|
||||||
unsigned i, max;
|
|
||||||
|
|
||||||
max = music_pipe.end;
|
|
||||||
if (max < music_pipe.begin)
|
|
||||||
max += music_pipe.num_chunks;
|
|
||||||
i = (unsigned)music_pipe.begin + relative;
|
|
||||||
if (i >= max)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (i >= music_pipe.num_chunks)
|
|
||||||
i -= music_pipe.num_chunks;
|
|
||||||
|
|
||||||
return (int)i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct music_chunk *
|
struct music_chunk *
|
||||||
music_pipe_get_chunk(const unsigned i)
|
music_pipe_shift(struct music_pipe *mp)
|
||||||
{
|
|
||||||
assert(i < music_pipe.num_chunks);
|
|
||||||
|
|
||||||
return &music_pipe.chunks[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct music_chunk *
|
|
||||||
music_pipe_allocate(void)
|
|
||||||
{
|
{
|
||||||
struct music_chunk *chunk;
|
struct music_chunk *chunk;
|
||||||
|
|
||||||
/* the music_pipe.end chunk is always kept initialized */
|
g_mutex_lock(mp->mutex);
|
||||||
chunk = music_pipe_get_chunk(music_pipe.end);
|
|
||||||
assert(chunk->length == 0);
|
chunk = mp->head;
|
||||||
|
if (chunk != NULL) {
|
||||||
|
mp->head = chunk->next;
|
||||||
|
--mp->size;
|
||||||
|
|
||||||
|
if (mp->head == NULL) {
|
||||||
|
assert(mp->size == 0);
|
||||||
|
assert(mp->tail_r == &chunk->next);
|
||||||
|
|
||||||
|
mp->tail_r = &mp->head;
|
||||||
|
} else {
|
||||||
|
assert(mp->size > 0);
|
||||||
|
assert(mp->tail_r != &chunk->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock(mp->mutex);
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
music_pipe_push(struct music_chunk *chunk)
|
music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer)
|
||||||
{
|
{
|
||||||
unsigned int next;
|
struct music_chunk *chunk;
|
||||||
|
|
||||||
assert(chunk == music_pipe_get_chunk(music_pipe.end));
|
while ((chunk = music_pipe_shift(mp)) != NULL)
|
||||||
|
music_buffer_return(buffer, chunk);
|
||||||
next = successor(music_pipe.end);
|
|
||||||
if (music_pipe.begin == next)
|
|
||||||
/* no room */
|
|
||||||
return false;
|
|
||||||
|
|
||||||
output_buffer_expand(next);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
music_pipe_cancel(struct music_chunk *chunk)
|
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
|
||||||
{
|
{
|
||||||
assert(chunk == music_pipe_get_chunk(music_pipe.end));
|
g_mutex_lock(mp->mutex);
|
||||||
|
|
||||||
music_chunk_free(chunk);
|
chunk->next = NULL;
|
||||||
|
*mp->tail_r = chunk;
|
||||||
|
mp->tail_r = &chunk->next;
|
||||||
|
|
||||||
|
++mp->size;
|
||||||
|
|
||||||
|
g_mutex_unlock(mp->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void music_pipe_skip(unsigned num)
|
unsigned
|
||||||
|
music_pipe_size(const struct music_pipe *mp)
|
||||||
{
|
{
|
||||||
int i = music_pipe_absolute(num);
|
return mp->size;
|
||||||
if (i < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (music_pipe.begin != (unsigned)i)
|
|
||||||
music_pipe_shift();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void music_pipe_chop(unsigned first)
|
|
||||||
{
|
|
||||||
for (unsigned i = first; i != music_pipe.end; i = successor(i))
|
|
||||||
music_chunk_free(&music_pipe.chunks[i]);
|
|
||||||
|
|
||||||
music_chunk_free(&music_pipe.chunks[music_pipe.end]);
|
|
||||||
|
|
||||||
music_pipe.end = first;
|
|
||||||
music_chunk_init(&music_pipe.chunks[first]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
void music_pipe_check_format(const struct audio_format *current,
|
|
||||||
int next_index, const struct audio_format *next)
|
|
||||||
{
|
|
||||||
const struct audio_format *audio_format = current;
|
|
||||||
|
|
||||||
for (unsigned i = music_pipe.begin; i != music_pipe.end;
|
|
||||||
i = successor(i)) {
|
|
||||||
const struct music_chunk *chunk = music_pipe_get_chunk(i);
|
|
||||||
|
|
||||||
if (next_index > 0 && i == (unsigned)next_index)
|
|
||||||
audio_format = next;
|
|
||||||
|
|
||||||
assert(chunk->length % audio_format_frame_size(audio_format) == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
155
src/pipe.h
155
src/pipe.h
@ -19,160 +19,51 @@
|
|||||||
#ifndef MPD_PIPE_H
|
#ifndef MPD_PIPE_H
|
||||||
#define MPD_PIPE_H
|
#define MPD_PIPE_H
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
struct audio_format;
|
|
||||||
struct tag;
|
|
||||||
struct music_chunk;
|
struct music_chunk;
|
||||||
|
struct music_buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ring set of buffers where the decoder appends data after the end,
|
* A queue of #music_chunk objects. One party appends chunks at the
|
||||||
* and the player consumes data from the beginning.
|
* tail, and the other consumes them from the head.
|
||||||
*/
|
*/
|
||||||
struct music_pipe {
|
struct music_pipe;
|
||||||
struct music_chunk *chunks;
|
|
||||||
unsigned num_chunks;
|
|
||||||
|
|
||||||
/** the index of the first decoded chunk */
|
/**
|
||||||
unsigned begin;
|
* Creates a new #music_pipe object. It is empty.
|
||||||
|
*/
|
||||||
/** the index after the last decoded chunk */
|
struct music_pipe *
|
||||||
unsigned end;
|
music_pipe_new(void);
|
||||||
|
|
||||||
/** non-zero if the player thread should only we woken up if
|
|
||||||
the buffer becomes non-empty */
|
|
||||||
bool lazy;
|
|
||||||
|
|
||||||
struct notify *notify;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct music_pipe music_pipe;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object. It must be empty now.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
music_pipe_init(unsigned int size, struct notify *notify);
|
music_pipe_free(struct music_pipe *mp);
|
||||||
|
|
||||||
void music_pipe_free(void);
|
|
||||||
|
|
||||||
void music_pipe_clear(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a chunk is decoded, we wake up the player thread to tell him
|
* Removes the first chunk from the head, and returns it.
|
||||||
* about it. In "lazy" mode, we only wake him up when the buffer was
|
|
||||||
* previously empty, i.e. when the player thread has really been
|
|
||||||
* waiting for us.
|
|
||||||
*/
|
|
||||||
void music_pipe_set_lazy(bool lazy);
|
|
||||||
|
|
||||||
static inline unsigned
|
|
||||||
music_pipe_size(void)
|
|
||||||
{
|
|
||||||
return music_pipe.num_chunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** is the buffer empty? */
|
|
||||||
static inline bool music_pipe_is_empty(void)
|
|
||||||
{
|
|
||||||
return music_pipe.begin == music_pipe.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
music_pipe_head_is(unsigned i)
|
|
||||||
{
|
|
||||||
return !music_pipe_is_empty() && music_pipe.begin == i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned
|
|
||||||
music_pipe_tail_index(void)
|
|
||||||
{
|
|
||||||
return music_pipe.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
void music_pipe_shift(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* what is the position of the specified chunk number, relative to
|
|
||||||
* the first chunk in use?
|
|
||||||
*/
|
|
||||||
unsigned int music_pipe_relative(const unsigned i);
|
|
||||||
|
|
||||||
/** determine the number of decoded chunks */
|
|
||||||
unsigned music_pipe_available(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the absolute index of the nth used chunk after the first one.
|
|
||||||
* Returns -1 if there is no such chunk.
|
|
||||||
*/
|
|
||||||
int music_pipe_absolute(const unsigned relative);
|
|
||||||
|
|
||||||
struct music_chunk *
|
|
||||||
music_pipe_get_chunk(const unsigned i);
|
|
||||||
|
|
||||||
static inline struct music_chunk *
|
|
||||||
music_pipe_peek(void)
|
|
||||||
{
|
|
||||||
if (music_pipe_is_empty())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return music_pipe_get_chunk(music_pipe.begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocates a chunk for writing. When you are finished, append it
|
|
||||||
* with music_pipe_push().
|
|
||||||
*
|
|
||||||
* @return an empty chunk
|
|
||||||
*/
|
*/
|
||||||
struct music_chunk *
|
struct music_chunk *
|
||||||
music_pipe_allocate(void);
|
music_pipe_shift(struct music_pipe *mp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a chunk at the end of the music pipe.
|
* Clears the whole pipe and returns the chunks to the buffer.
|
||||||
*
|
*
|
||||||
* @param chunk a chunk allocated with music_pipe_allocate()
|
* @param buffer the buffer object to return the chunks to
|
||||||
* @return true on success, false if there is no room
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
music_pipe_push(struct music_chunk *chunk);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels a chunk that has been allocated with music_pipe_allocate().
|
|
||||||
*
|
|
||||||
* @param chunk a chunk allocated with music_pipe_allocate()
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
music_pipe_cancel(struct music_chunk *chunk);
|
music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares appending to the music pipe. Returns a buffer where you
|
* Pushes a chunk to the tail of the pipe.
|
||||||
* may write into. After you are finished, call music_pipe_expand().
|
|
||||||
*
|
|
||||||
* @return a writable buffer
|
|
||||||
*/
|
|
||||||
void *
|
|
||||||
music_pipe_write(const struct audio_format *audio_format,
|
|
||||||
float data_time, uint16_t bit_rate,
|
|
||||||
size_t *max_length_r);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the music pipe to move the end pointer, after you have
|
|
||||||
* written to the buffer returned by music_pipe_write().
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
music_pipe_expand(const struct audio_format *audio_format, size_t length);
|
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
|
||||||
|
|
||||||
void music_pipe_skip(unsigned num);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chop off the tail of the music pipe, starting with the chunk at
|
* Returns the number of chunks currently in this pipe.
|
||||||
* index "first".
|
|
||||||
*/
|
*/
|
||||||
void music_pipe_chop(unsigned first);
|
unsigned
|
||||||
|
music_pipe_size(const struct music_pipe *mp);
|
||||||
#ifndef NDEBUG
|
|
||||||
void music_pipe_check_format(const struct audio_format *current,
|
|
||||||
int next_index, const struct audio_format *next);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,8 +30,9 @@
|
|||||||
|
|
||||||
struct player_control pc;
|
struct player_control pc;
|
||||||
|
|
||||||
void pc_init(unsigned int buffered_before_play)
|
void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
|
||||||
{
|
{
|
||||||
|
pc.buffer_chunks = buffer_chunks;
|
||||||
pc.buffered_before_play = buffered_before_play;
|
pc.buffered_before_play = buffered_before_play;
|
||||||
notify_init(&pc.notify);
|
notify_init(&pc.notify);
|
||||||
pc.command = PLAYER_COMMAND_NONE;
|
pc.command = PLAYER_COMMAND_NONE;
|
||||||
|
@ -60,6 +60,8 @@ enum player_error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct player_control {
|
struct player_control {
|
||||||
|
unsigned buffer_chunks;
|
||||||
|
|
||||||
unsigned int buffered_before_play;
|
unsigned int buffered_before_play;
|
||||||
|
|
||||||
/** the handle of the player thread, or NULL if the player
|
/** the handle of the player thread, or NULL if the player
|
||||||
@ -84,7 +86,7 @@ struct player_control {
|
|||||||
|
|
||||||
extern struct player_control pc;
|
extern struct player_control pc;
|
||||||
|
|
||||||
void pc_init(unsigned int _buffered_before_play);
|
void pc_init(unsigned buffer_chunks, unsigned buffered_before_play);
|
||||||
|
|
||||||
void pc_deinit(void);
|
void pc_deinit(void);
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
#include "idle.h"
|
#include "idle.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
@ -44,6 +45,9 @@ enum xfade_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct player {
|
struct player {
|
||||||
|
struct music_buffer *buffer;
|
||||||
|
struct music_pipe *pipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* are we waiting for buffered_before_play?
|
* are we waiting for buffered_before_play?
|
||||||
*/
|
*/
|
||||||
@ -74,12 +78,6 @@ struct player {
|
|||||||
* is cross fading enabled?
|
* is cross fading enabled?
|
||||||
*/
|
*/
|
||||||
enum xfade_state xfade;
|
enum xfade_state xfade;
|
||||||
|
|
||||||
/**
|
|
||||||
* index of the first chunk of the next song, -1 if there is
|
|
||||||
* no next song
|
|
||||||
*/
|
|
||||||
int next_song_chunk;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void player_command_finished(void)
|
static void player_command_finished(void)
|
||||||
@ -90,6 +88,21 @@ static void player_command_finished(void)
|
|||||||
notify_signal(&main_notify);
|
notify_signal(&main_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
player_dc_stop(struct player *player)
|
||||||
|
{
|
||||||
|
dc_stop(&pc.notify);
|
||||||
|
|
||||||
|
if (dc.pipe != NULL) {
|
||||||
|
music_pipe_clear(dc.pipe, player->buffer);
|
||||||
|
|
||||||
|
if (dc.pipe != player->pipe)
|
||||||
|
music_pipe_free(dc.pipe);
|
||||||
|
|
||||||
|
dc.pipe = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void player_stop_decoder(void)
|
static void player_stop_decoder(void)
|
||||||
{
|
{
|
||||||
dc_stop(&pc.notify);
|
dc_stop(&pc.notify);
|
||||||
@ -133,9 +146,10 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
if (decoder_current_song() != pc.next_song) {
|
if (decoder_current_song() != pc.next_song) {
|
||||||
dc_stop(&pc.notify);
|
player_dc_stop(player);
|
||||||
player->next_song_chunk = -1;
|
|
||||||
music_pipe_clear();
|
music_pipe_clear(player->pipe, player->buffer);
|
||||||
|
dc.pipe = player->pipe;
|
||||||
dc_start_async(pc.next_song);
|
dc_start_async(pc.next_song);
|
||||||
|
|
||||||
ret = player_wait_for_decoder(player);
|
ret = player_wait_for_decoder(player);
|
||||||
@ -174,7 +188,7 @@ static void player_process_command(struct player *player)
|
|||||||
case PLAYER_COMMAND_QUEUE:
|
case PLAYER_COMMAND_QUEUE:
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
assert(!player->queued);
|
assert(!player->queued);
|
||||||
assert(player->next_song_chunk == -1);
|
assert(dc.pipe == NULL || dc.pipe == player->pipe);
|
||||||
|
|
||||||
player->queued = true;
|
player->queued = true;
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
@ -220,12 +234,11 @@ static void player_process_command(struct player *player)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->next_song_chunk != -1) {
|
if (dc.pipe != NULL && dc.pipe != player->pipe) {
|
||||||
/* the decoder is already decoding the song -
|
/* the decoder is already decoding the song -
|
||||||
stop it and reset the position */
|
stop it and reset the position */
|
||||||
|
player_dc_stop(player);
|
||||||
dc_stop(&pc.notify);
|
dc_stop(&pc.notify);
|
||||||
music_pipe_chop(player->next_song_chunk);
|
|
||||||
player->next_song_chunk = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.next_song = NULL;
|
pc.next_song = NULL;
|
||||||
@ -298,23 +311,25 @@ static void do_play(void)
|
|||||||
.queued = false,
|
.queued = false,
|
||||||
.song = NULL,
|
.song = NULL,
|
||||||
.xfade = XFADE_UNKNOWN,
|
.xfade = XFADE_UNKNOWN,
|
||||||
.next_song_chunk = -1,
|
|
||||||
};
|
};
|
||||||
unsigned int crossFadeChunks = 0;
|
unsigned int crossFadeChunks = 0;
|
||||||
/** the position of the next cross-faded chunk in the next
|
/** has cross-fading begun? */
|
||||||
song */
|
bool cross_fading = false;
|
||||||
int nextChunk = 0;
|
|
||||||
static const char silence[CHUNK_SIZE];
|
static const char silence[CHUNK_SIZE];
|
||||||
struct audio_format play_audio_format;
|
struct audio_format play_audio_format;
|
||||||
double sizeToTime = 0.0;
|
double sizeToTime = 0.0;
|
||||||
|
|
||||||
music_pipe_clear();
|
player.buffer = music_buffer_new(pc.buffer_chunks);
|
||||||
music_pipe_set_lazy(false);
|
player.pipe = music_pipe_new();
|
||||||
|
|
||||||
|
dc.buffer = player.buffer;
|
||||||
|
dc.pipe = player.pipe;
|
||||||
dc_start(&pc.notify, pc.next_song);
|
dc_start(&pc.notify, pc.next_song);
|
||||||
if (!player_wait_for_decoder(&player)) {
|
if (!player_wait_for_decoder(&player)) {
|
||||||
player_stop_decoder();
|
player_stop_decoder();
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
|
music_pipe_free(player.pipe);
|
||||||
|
music_buffer_free(player.buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +347,7 @@ static void do_play(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (player.buffering) {
|
if (player.buffering) {
|
||||||
if (music_pipe_available() < pc.buffered_before_play &&
|
if (music_pipe_size(player.pipe) < pc.buffered_before_play &&
|
||||||
!decoder_is_idle()) {
|
!decoder_is_idle()) {
|
||||||
/* not enough decoded buffer space yet */
|
/* not enough decoded buffer space yet */
|
||||||
notify_wait(&pc.notify);
|
notify_wait(&pc.notify);
|
||||||
@ -340,7 +355,6 @@ static void do_play(void)
|
|||||||
} else {
|
} else {
|
||||||
/* buffering is complete */
|
/* buffering is complete */
|
||||||
player.buffering = false;
|
player.buffering = false;
|
||||||
music_pipe_set_lazy(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,13 +409,14 @@ static void do_play(void)
|
|||||||
/* the decoder has finished the current song;
|
/* the decoder has finished the current song;
|
||||||
make it decode the next song */
|
make it decode the next song */
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
assert(player.next_song_chunk == -1);
|
assert(dc.pipe == NULL || dc.pipe == player.pipe);
|
||||||
|
|
||||||
player.queued = false;
|
player.queued = false;
|
||||||
player.next_song_chunk = music_pipe_tail_index();
|
dc.pipe = music_pipe_new();
|
||||||
dc_start_async(pc.next_song);
|
dc_start_async(pc.next_song);
|
||||||
}
|
}
|
||||||
if (player.next_song_chunk >= 0 &&
|
|
||||||
|
if (dc.pipe != NULL && dc.pipe != player.pipe &&
|
||||||
player.xfade == XFADE_UNKNOWN &&
|
player.xfade == XFADE_UNKNOWN &&
|
||||||
!decoder_is_starting()) {
|
!decoder_is_starting()) {
|
||||||
/* enable cross fading in this song? if yes,
|
/* enable cross fading in this song? if yes,
|
||||||
@ -411,11 +426,11 @@ static void do_play(void)
|
|||||||
cross_fade_calc(pc.cross_fade_seconds, dc.total_time,
|
cross_fade_calc(pc.cross_fade_seconds, dc.total_time,
|
||||||
&dc.out_audio_format,
|
&dc.out_audio_format,
|
||||||
&play_audio_format,
|
&play_audio_format,
|
||||||
music_pipe_size() -
|
music_buffer_size(player.buffer) -
|
||||||
pc.buffered_before_play);
|
pc.buffered_before_play);
|
||||||
if (crossFadeChunks > 0) {
|
if (crossFadeChunks > 0) {
|
||||||
player.xfade = XFADE_ENABLED;
|
player.xfade = XFADE_ENABLED;
|
||||||
nextChunk = -1;
|
cross_fading = false;
|
||||||
} else
|
} else
|
||||||
/* cross fading is disabled or the
|
/* cross fading is disabled or the
|
||||||
next song is too short */
|
next song is too short */
|
||||||
@ -424,31 +439,36 @@ static void do_play(void)
|
|||||||
|
|
||||||
if (player.paused)
|
if (player.paused)
|
||||||
notify_wait(&pc.notify);
|
notify_wait(&pc.notify);
|
||||||
else if (!music_pipe_is_empty() &&
|
else if (music_pipe_size(player.pipe) > 0) {
|
||||||
!music_pipe_head_is(player.next_song_chunk)) {
|
struct music_chunk *chunk = NULL;
|
||||||
struct music_chunk *beginChunk = music_pipe_peek();
|
|
||||||
unsigned int fadePosition;
|
unsigned int fadePosition;
|
||||||
|
bool success;
|
||||||
|
|
||||||
if (player.xfade == XFADE_ENABLED &&
|
if (player.xfade == XFADE_ENABLED &&
|
||||||
player.next_song_chunk >= 0 &&
|
dc.pipe != NULL && dc.pipe != player.pipe &&
|
||||||
(fadePosition = music_pipe_relative(player.next_song_chunk))
|
(fadePosition = music_pipe_size(player.pipe))
|
||||||
<= crossFadeChunks) {
|
<= crossFadeChunks) {
|
||||||
/* perform cross fade */
|
/* perform cross fade */
|
||||||
if (nextChunk < 0) {
|
struct music_chunk *other_chunk =
|
||||||
|
music_pipe_shift(dc.pipe);
|
||||||
|
|
||||||
|
if (!cross_fading) {
|
||||||
/* beginning of the cross fade
|
/* beginning of the cross fade
|
||||||
- adjust crossFadeChunks
|
- adjust crossFadeChunks
|
||||||
which might be bigger than
|
which might be bigger than
|
||||||
the remaining number of
|
the remaining number of
|
||||||
chunks in the old song */
|
chunks in the old song */
|
||||||
crossFadeChunks = fadePosition;
|
crossFadeChunks = fadePosition;
|
||||||
|
cross_fading = true;
|
||||||
}
|
}
|
||||||
nextChunk = music_pipe_absolute(crossFadeChunks);
|
|
||||||
if (nextChunk >= 0) {
|
if (other_chunk != NULL) {
|
||||||
music_pipe_set_lazy(true);
|
chunk = music_pipe_shift(player.pipe);
|
||||||
cross_fade_apply(beginChunk,
|
cross_fade_apply(chunk, other_chunk,
|
||||||
music_pipe_get_chunk(nextChunk),
|
|
||||||
&dc.out_audio_format,
|
&dc.out_audio_format,
|
||||||
fadePosition,
|
fadePosition,
|
||||||
crossFadeChunks);
|
crossFadeChunks);
|
||||||
|
music_buffer_return(player.buffer, other_chunk);
|
||||||
} else {
|
} else {
|
||||||
/* there are not enough
|
/* there are not enough
|
||||||
decoded chunks yet */
|
decoded chunks yet */
|
||||||
@ -460,47 +480,42 @@ static void do_play(void)
|
|||||||
} else {
|
} else {
|
||||||
/* wait for the
|
/* wait for the
|
||||||
decoder */
|
decoder */
|
||||||
music_pipe_set_lazy(false);
|
|
||||||
notify_signal(&dc.notify);
|
notify_signal(&dc.notify);
|
||||||
notify_wait(&pc.notify);
|
notify_wait(&pc.notify);
|
||||||
|
|
||||||
/* set nextChunk to a
|
|
||||||
non-negative value
|
|
||||||
so the next
|
|
||||||
iteration doesn't
|
|
||||||
assume crossfading
|
|
||||||
hasn't begun yet */
|
|
||||||
nextChunk = 0;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chunk == NULL)
|
||||||
|
chunk = music_pipe_shift(player.pipe);
|
||||||
|
|
||||||
/* play the current chunk */
|
/* play the current chunk */
|
||||||
if (!play_chunk(player.song, beginChunk,
|
|
||||||
&play_audio_format, sizeToTime))
|
success = play_chunk(player.song, chunk,
|
||||||
|
&play_audio_format, sizeToTime);
|
||||||
|
music_buffer_return(player.buffer, chunk);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
break;
|
break;
|
||||||
music_pipe_shift();
|
|
||||||
|
|
||||||
/* this formula should prevent that the
|
/* this formula should prevent that the
|
||||||
decoder gets woken up with each chunk; it
|
decoder gets woken up with each chunk; it
|
||||||
is more efficient to make it decode a
|
is more efficient to make it decode a
|
||||||
larger block at a time */
|
larger block at a time */
|
||||||
if (music_pipe_available() <= (pc.buffered_before_play + music_pipe_size() * 3) / 4)
|
if (!decoder_is_idle() &&
|
||||||
|
music_pipe_size(dc.pipe) <= (pc.buffered_before_play +
|
||||||
|
music_buffer_size(player.buffer) * 3) / 4)
|
||||||
notify_signal(&dc.notify);
|
notify_signal(&dc.notify);
|
||||||
} else if (music_pipe_head_is(player.next_song_chunk)) {
|
} else if (dc.pipe != NULL && dc.pipe != player.pipe) {
|
||||||
/* at the beginning of a new song */
|
/* at the beginning of a new song */
|
||||||
|
|
||||||
if (player.xfade == XFADE_ENABLED && nextChunk >= 0) {
|
|
||||||
/* the cross-fade is finished; skip
|
|
||||||
the section which was cross-faded
|
|
||||||
(and thus already played) */
|
|
||||||
music_pipe_skip(crossFadeChunks);
|
|
||||||
}
|
|
||||||
|
|
||||||
player.xfade = XFADE_UNKNOWN;
|
player.xfade = XFADE_UNKNOWN;
|
||||||
|
|
||||||
player.next_song_chunk = -1;
|
music_pipe_free(player.pipe);
|
||||||
|
player.pipe = dc.pipe;
|
||||||
|
|
||||||
if (!player_wait_for_decoder(&player))
|
if (!player_wait_for_decoder(&player))
|
||||||
break;
|
break;
|
||||||
} else if (decoder_is_idle()) {
|
} else if (decoder_is_idle()) {
|
||||||
@ -524,6 +539,16 @@ static void do_play(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
player_stop_decoder();
|
player_stop_decoder();
|
||||||
|
|
||||||
|
if (dc.pipe != NULL && dc.pipe != player.pipe) {
|
||||||
|
music_pipe_clear(dc.pipe, player.buffer);
|
||||||
|
music_pipe_free(dc.pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
music_pipe_clear(player.pipe, player.buffer);
|
||||||
|
music_pipe_free(player.pipe);
|
||||||
|
|
||||||
|
music_buffer_free(player.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
||||||
|
Loading…
Reference in New Issue
Block a user