767b4c95bd
No CamelCase in the file name. The output_buffer struct is going to be renamed to music_pipe. There are so many buffer levels in MPD, and calling this one "output buffer" is wrong, because it's not the last buffer before the music reaches the output devices.
198 lines
4.9 KiB
C
198 lines
4.9 KiB
C
/* the Music Player Daemon (MPD)
|
|
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
|
|
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
|
|
* This project's homepage is: 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 "decoder_internal.h"
|
|
#include "decoder_control.h"
|
|
#include "player_control.h"
|
|
#include "audio.h"
|
|
#include "song.h"
|
|
|
|
#include "utils.h"
|
|
#include "normalize.h"
|
|
#include "pipe.h"
|
|
#include "gcc.h"
|
|
|
|
#include <assert.h>
|
|
|
|
void decoder_initialized(struct decoder * decoder,
|
|
const struct audio_format *audio_format,
|
|
float total_time)
|
|
{
|
|
assert(dc.state == DECODE_STATE_START);
|
|
assert(audio_format != NULL);
|
|
|
|
pcm_convert_init(&decoder->conv_state);
|
|
|
|
dc.audioFormat = *audio_format;
|
|
getOutputAudioFormat(audio_format, &ob.audioFormat);
|
|
|
|
dc.totalTime = total_time;
|
|
|
|
dc.state = DECODE_STATE_DECODE;
|
|
notify_signal(&pc.notify);
|
|
}
|
|
|
|
const char *decoder_get_url(mpd_unused struct decoder * decoder, char * buffer)
|
|
{
|
|
return song_get_url(dc.current_song, buffer);
|
|
}
|
|
|
|
enum decoder_command decoder_get_command(mpd_unused struct decoder * decoder)
|
|
{
|
|
return dc.command;
|
|
}
|
|
|
|
void decoder_command_finished(mpd_unused struct decoder * decoder)
|
|
{
|
|
assert(dc.command != DECODE_COMMAND_NONE);
|
|
assert(dc.command != DECODE_COMMAND_SEEK ||
|
|
dc.seekError || decoder->seeking);
|
|
|
|
if (dc.command == DECODE_COMMAND_SEEK)
|
|
/* delete frames from the old song position */
|
|
ob_clear();
|
|
|
|
dc.command = DECODE_COMMAND_NONE;
|
|
notify_signal(&pc.notify);
|
|
}
|
|
|
|
double decoder_seek_where(mpd_unused struct decoder * decoder)
|
|
{
|
|
assert(dc.command == DECODE_COMMAND_SEEK);
|
|
|
|
decoder->seeking = true;
|
|
|
|
return dc.seekWhere;
|
|
}
|
|
|
|
void decoder_seek_error(struct decoder * decoder)
|
|
{
|
|
assert(dc.command == DECODE_COMMAND_SEEK);
|
|
|
|
dc.seekError = true;
|
|
decoder_command_finished(decoder);
|
|
}
|
|
|
|
size_t decoder_read(struct decoder *decoder,
|
|
struct input_stream *inStream,
|
|
void *buffer, size_t length)
|
|
{
|
|
size_t nbytes;
|
|
|
|
assert(inStream != NULL);
|
|
assert(buffer != NULL);
|
|
|
|
while (true) {
|
|
/* XXX don't allow decoder==NULL */
|
|
if (decoder != NULL &&
|
|
(dc.command != DECODE_COMMAND_SEEK ||
|
|
!decoder->seeking) &&
|
|
dc.command != DECODE_COMMAND_NONE)
|
|
return 0;
|
|
|
|
nbytes = input_stream_read(inStream, buffer, length);
|
|
if (nbytes > 0 || input_stream_eof(inStream))
|
|
return nbytes;
|
|
|
|
/* sleep for a fraction of a second! */
|
|
/* XXX don't sleep, wait for an event instead */
|
|
my_usleep(10000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* All chunks are full of decoded data; wait for the player to free
|
|
* one.
|
|
*/
|
|
static enum decoder_command
|
|
need_chunks(struct decoder *decoder,
|
|
struct input_stream *inStream, int seekable)
|
|
{
|
|
if (dc.command == DECODE_COMMAND_STOP)
|
|
return DECODE_COMMAND_STOP;
|
|
|
|
if (dc.command == DECODE_COMMAND_SEEK) {
|
|
if (seekable) {
|
|
return DECODE_COMMAND_SEEK;
|
|
} else {
|
|
decoder_seek_error(decoder);
|
|
}
|
|
}
|
|
|
|
if (!inStream ||
|
|
input_stream_buffer(inStream) <= 0) {
|
|
notify_wait(&dc.notify);
|
|
notify_signal(&pc.notify);
|
|
}
|
|
|
|
return DECODE_COMMAND_NONE;
|
|
}
|
|
|
|
enum decoder_command
|
|
decoder_data(struct decoder *decoder,
|
|
struct input_stream *inStream, int seekable,
|
|
void *dataIn, size_t dataInLen,
|
|
float data_time, uint16_t bitRate,
|
|
ReplayGainInfo * replayGainInfo)
|
|
{
|
|
size_t nbytes;
|
|
char *data;
|
|
size_t datalen;
|
|
static char *convBuffer;
|
|
static size_t convBufferLen;
|
|
|
|
if (audio_format_equals(&ob.audioFormat, &dc.audioFormat)) {
|
|
data = dataIn;
|
|
datalen = dataInLen;
|
|
} else {
|
|
datalen = pcm_convert_size(&(dc.audioFormat), dataInLen,
|
|
&(ob.audioFormat));
|
|
if (datalen > convBufferLen) {
|
|
if (convBuffer != NULL)
|
|
free(convBuffer);
|
|
convBuffer = xmalloc(datalen);
|
|
convBufferLen = datalen;
|
|
}
|
|
data = convBuffer;
|
|
datalen = pcm_convert(&(dc.audioFormat), dataIn,
|
|
dataInLen, &(ob.audioFormat),
|
|
data, &decoder->conv_state);
|
|
}
|
|
|
|
if (replayGainInfo != NULL && (replayGainState != REPLAYGAIN_OFF))
|
|
doReplayGain(replayGainInfo, data, datalen, &ob.audioFormat);
|
|
else if (normalizationEnabled)
|
|
normalizeData(data, datalen, &ob.audioFormat);
|
|
|
|
while (datalen > 0) {
|
|
nbytes = ob_append(data, datalen, data_time, bitRate);
|
|
datalen -= nbytes;
|
|
data += nbytes;
|
|
|
|
if (datalen > 0) {
|
|
enum decoder_command cmd =
|
|
need_chunks(decoder, inStream, seekable);
|
|
if (cmd != DECODE_COMMAND_NONE)
|
|
return cmd;
|
|
}
|
|
}
|
|
|
|
return DECODE_COMMAND_NONE;
|
|
}
|