1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "../inputPlugin.h"
26 #include "../pcm_utils.h"
27 #include "../inputStream.h"
28 #include "../outputBuffer.h"
29 #include "../replayGain.h"
35 #include <mpcdec/mpcdec.h>
39 typedef struct _MpcCallbackData
{
40 InputStream
*inStream
;
44 static mpc_int32_t
mpc_read_cb(void *vdata
, void *ptr
, mpc_int32_t size
)
47 MpcCallbackData
*data
= (MpcCallbackData
*) vdata
;
50 ret
= readFromInputStream(data
->inStream
, ptr
, 1, size
);
51 if (ret
== 0 && !inputStreamAtEOF(data
->inStream
) &&
52 (data
->dc
&& !data
->dc
->stop
)) {
61 static mpc_bool_t
mpc_seek_cb(void *vdata
, mpc_int32_t offset
)
63 MpcCallbackData
*data
= (MpcCallbackData
*) vdata
;
65 return seekInputStream(data
->inStream
, offset
, SEEK_SET
) < 0 ? 0 : 1;
68 static mpc_int32_t
mpc_tell_cb(void *vdata
)
70 MpcCallbackData
*data
= (MpcCallbackData
*) vdata
;
72 return (long)(data
->inStream
->offset
);
75 static mpc_bool_t
mpc_canseek_cb(void *vdata
)
77 MpcCallbackData
*data
= (MpcCallbackData
*) vdata
;
79 return data
->inStream
->seekable
;
82 static mpc_int32_t
mpc_getsize_cb(void *vdata
)
84 MpcCallbackData
*data
= (MpcCallbackData
*) vdata
;
86 return data
->inStream
->size
;
89 /* this _looks_ performance-critical, don't de-inline -- eric */
90 static inline mpd_sint16
convertSample(MPC_SAMPLE_FORMAT sample
)
92 /* only doing 16-bit audio for now */
95 const int clip_min
= -1 << (16 - 1);
96 const int clip_max
= (1 << (16 - 1)) - 1;
98 #ifdef MPC_FIXED_POINT
99 const int shift
= 16 - MPC_FIXED_POINT_SCALE_SHIFT
;
103 } else if (shift
< 0) {
108 const int float_scale
= 1 << (16 - 1);
110 val
= sample
* float_scale
;
115 else if (val
> clip_max
)
121 static int mpc_decode(OutputBuffer
* cb
, DecoderControl
* dc
,
122 InputStream
* inStream
)
128 MpcCallbackData data
;
130 MPC_SAMPLE_FORMAT sample_buffer
[MPC_DECODER_BUFFER_LENGTH
];
134 #define MPC_CHUNK_SIZE 4096
135 char chunk
[MPC_CHUNK_SIZE
];
138 mpd_sint16
*s16
= (mpd_sint16
*) chunk
;
139 unsigned long samplePos
= 0;
140 mpc_uint32_t vbrUpdateAcc
;
141 mpc_uint32_t vbrUpdateBits
;
144 ReplayGainInfo
*replayGainInfo
= NULL
;
146 data
.inStream
= inStream
;
149 reader
.read
= mpc_read_cb
;
150 reader
.seek
= mpc_seek_cb
;
151 reader
.tell
= mpc_tell_cb
;
152 reader
.get_size
= mpc_getsize_cb
;
153 reader
.canseek
= mpc_canseek_cb
;
156 mpc_streaminfo_init(&info
);
158 if ((ret
= mpc_streaminfo_read(&info
, &reader
)) != ERROR_CODE_OK
) {
159 closeInputStream(inStream
);
161 ERROR("Not a valid musepack stream");
164 dc
->state
= DECODE_STATE_STOP
;
170 mpc_decoder_setup(&decoder
, &reader
);
172 if (!mpc_decoder_initialize(&decoder
, &info
)) {
173 closeInputStream(inStream
);
175 ERROR("Not a valid musepack stream");
177 dc
->state
= DECODE_STATE_STOP
;
182 dc
->totalTime
= mpc_streaminfo_get_length(&info
);
184 dc
->audioFormat
.bits
= 16;
185 dc
->audioFormat
.channels
= info
.channels
;
186 dc
->audioFormat
.sampleRate
= info
.sample_freq
;
188 getOutputAudioFormat(&(dc
->audioFormat
), &(cb
->audioFormat
));
190 replayGainInfo
= newReplayGainInfo();
191 replayGainInfo
->albumGain
= info
.gain_album
* 0.01;
192 replayGainInfo
->albumPeak
= info
.peak_album
/ 32767.0;
193 replayGainInfo
->trackGain
= info
.gain_title
* 0.01;
194 replayGainInfo
->trackPeak
= info
.peak_title
/ 32767.0;
196 dc
->state
= DECODE_STATE_DECODE
;
200 samplePos
= dc
->seekWhere
* dc
->audioFormat
.sampleRate
;
201 if (mpc_decoder_seek_sample(&decoder
, samplePos
)) {
202 clearOutputBuffer(cb
);
203 s16
= (mpd_sint16
*) chunk
;
212 ret
= mpc_decoder_decode(&decoder
, sample_buffer
,
213 &vbrUpdateAcc
, &vbrUpdateBits
);
215 if (ret
<= 0 || dc
->stop
) {
222 /* ret is in samples, and we have stereo */
225 for (i
= 0; i
< ret
; i
++) {
226 /* 16 bit audio again */
227 *s16
= convertSample(sample_buffer
[i
]);
231 if (chunkpos
>= MPC_CHUNK_SIZE
) {
232 time
= ((float)samplePos
) /
233 dc
->audioFormat
.sampleRate
;
235 bitRate
= vbrUpdateBits
*
236 dc
->audioFormat
.sampleRate
/ 1152 / 1000;
238 sendDataToOutputBuffer(cb
, inStream
, dc
,
242 bitRate
, replayGainInfo
);
245 s16
= (mpd_sint16
*) chunk
;
254 if (!dc
->stop
&& chunkpos
> 0) {
255 time
= ((float)samplePos
) / dc
->audioFormat
.sampleRate
;
258 vbrUpdateBits
* dc
->audioFormat
.sampleRate
/ 1152 / 1000;
260 sendDataToOutputBuffer(cb
, NULL
, dc
, inStream
->seekable
,
261 chunk
, chunkpos
, time
, bitRate
,
265 closeInputStream(inStream
);
267 flushOutputBuffer(cb
);
269 freeReplayGainInfo(replayGainInfo
);
272 dc
->state
= DECODE_STATE_STOP
;
275 dc
->state
= DECODE_STATE_STOP
;
281 static float mpcGetTime(char *file
)
283 InputStream inStream
;
288 MpcCallbackData data
;
290 data
.inStream
= &inStream
;
293 reader
.read
= mpc_read_cb
;
294 reader
.seek
= mpc_seek_cb
;
295 reader
.tell
= mpc_tell_cb
;
296 reader
.get_size
= mpc_getsize_cb
;
297 reader
.canseek
= mpc_canseek_cb
;
300 mpc_streaminfo_init(&info
);
302 if (openInputStream(&inStream
, file
) < 0) {
303 DEBUG("mpcGetTime: Failed to open file: %s\n", file
);
307 if (mpc_streaminfo_read(&info
, &reader
) != ERROR_CODE_OK
) {
308 closeInputStream(&inStream
);
312 time
= mpc_streaminfo_get_length(&info
);
314 closeInputStream(&inStream
);
319 static MpdTag
*mpcTagDup(char *file
)
322 float time
= mpcGetTime(file
);
325 DEBUG("mpcTagDup: Failed to get Songlength of file: %s\n",
340 static char *mpcSuffixes
[] = { "mpc", NULL
};
342 InputPlugin mpcPlugin
= {
350 INPUT_PLUGIN_STREAM_URL
| INPUT_PLUGIN_STREAM_FILE
,
357 InputPlugin mpcPlugin
;
359 #endif /* HAVE_MPCDEC */