You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
8.1 KiB
C
250 lines
8.1 KiB
C
/*
|
|
Copyright (c) 2005-2009, The Musepack Development Team
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer in the documentation and/or other materials provided
|
|
with the distribution.
|
|
|
|
* Neither the name of the The Musepack Development Team nor the
|
|
names of its contributors may be used to endorse or promote
|
|
products derived from this software without specific prior
|
|
written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
#include <mpc/mpcdec.h>
|
|
#include "../libmpcdec/decoder.h"
|
|
#include "../libmpcdec/internal.h"
|
|
#include <libwaveformat.h>
|
|
#include <getopt.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#include <crtdbg.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
# include <fcntl.h>
|
|
# include <io.h>
|
|
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
|
|
#else
|
|
# define SET_BINARY_MODE(file)
|
|
#endif
|
|
|
|
#define MPCDEC_MAJOR 1
|
|
#define MPCDEC_MINOR 0
|
|
#define MPCDEC_BUILD 0
|
|
|
|
#define _cat(a,b,c) #a"."#b"."#c
|
|
#define cat(a,b,c) _cat(a,b,c)
|
|
#define MPCDEC_VERSION cat(MPCDEC_MAJOR,MPCDEC_MINOR,MPCDEC_BUILD)
|
|
|
|
const char About [] = "mpcdec - Musepack (MPC) decoder v" MPCDEC_VERSION " (C) 2006-2009 MDT\nBuilt " __DATE__ " " __TIME__ "\n";
|
|
|
|
|
|
t_wav_uint32 mpc_wav_output_write(void* p_user_data, void const* p_buffer, t_wav_uint32 p_bytes)
|
|
{
|
|
FILE* p_handle = (FILE*) p_user_data;
|
|
return (t_wav_uint32) fwrite(p_buffer, 1, p_bytes, p_handle);
|
|
}
|
|
|
|
t_wav_uint32 mpc_wav_output_seek(void* p_user_data, t_wav_uint32 p_position)
|
|
{
|
|
FILE* p_handle = (FILE*) p_user_data;
|
|
return (t_wav_uint32) !fseek(p_handle, p_position, SEEK_SET);
|
|
}
|
|
|
|
static void print_info(mpc_streaminfo * info, char * filename)
|
|
{
|
|
int time = (int) mpc_streaminfo_get_length(info);
|
|
int minutes = time / 60;
|
|
int seconds = time % 60;
|
|
|
|
fprintf(stderr, "file: %s\n", filename);
|
|
fprintf(stderr, "stream version %d\n", info->stream_version);
|
|
fprintf(stderr, "encoder: %s\n", info->encoder);
|
|
fprintf(stderr, "profile: %s (q=%0.2f)\n", info->profile_name, info->profile - 5);
|
|
fprintf(stderr, "PNS: %s\n", info->pns == 0xFF ? "unknow" : info->pns ? "on" : "off");
|
|
fprintf(stderr, "mid/side stereo: %s\n", info->ms ? "on" : "off");
|
|
fprintf(stderr, "gapless: %s\n", info->is_true_gapless ? "on" : "off");
|
|
fprintf(stderr, "average bitrate: %6.1f kbps\n", info->average_bitrate * 1.e-3);
|
|
fprintf(stderr, "samplerate: %d Hz\n", info->sample_freq);
|
|
fprintf(stderr, "channels: %d\n", info->channels);
|
|
fprintf(stderr, "length: %d:%.2d (%u samples)\n", minutes, seconds, (mpc_uint32_t)mpc_streaminfo_get_length_samples(info));
|
|
fprintf(stderr, "file size: %d Bytes\n", info->total_file_length);
|
|
fprintf(stderr, "track peak: %2.2f dB\n", info->peak_title / 256.f);
|
|
fprintf(stderr, "track gain: %2.2f dB / %2.2f dB\n", info->gain_title / 256.f, info->gain_title == 0 ? 0 : 64.82f - info->gain_title / 256.f);
|
|
fprintf(stderr, "album peak: %2.2f dB\n", info->peak_album / 256.f);
|
|
fprintf(stderr, "album gain: %2.2f dB / %2.2f dB\n", info->gain_album / 256.f, info->gain_album == 0 ? 0 : 64.82f - info->gain_album / 256.f);
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
static void
|
|
usage(const char *exename)
|
|
{
|
|
fprintf(stderr, "Usage: %s [-i] [-h] <infile.mpc> [<outfile.wav>]\n"
|
|
"-i : print file information on stdout\n"
|
|
"-c : check the file for stream errors\n"
|
|
" (doesn't fully decode, outfile will be ignored)\n"
|
|
"-h : print this help\n"
|
|
"you can use stdin and stdout as resp. <infile.mpc> and\n"
|
|
"<outfile.wav> replacing the file name by \"-\"\n", exename);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
mpc_reader reader;
|
|
mpc_demux* demux;
|
|
mpc_streaminfo si;
|
|
mpc_status err;
|
|
mpc_bool_t info = MPC_FALSE, is_wav_output = MPC_FALSE, check = MPC_FALSE;
|
|
MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
|
|
clock_t begin, end, sum; int total_samples; t_wav_output_file wav_output;
|
|
int c;
|
|
|
|
fprintf(stderr, About);
|
|
|
|
while ((c = getopt(argc , argv, "ihc")) != -1) {
|
|
switch (c) {
|
|
case 'i':
|
|
info = MPC_TRUE;
|
|
break;
|
|
case 'c':
|
|
check = MPC_TRUE;
|
|
break;
|
|
case 'h':
|
|
usage(argv[0]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if(2 < argc - optind || argc - optind < 1)
|
|
{
|
|
usage(argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(argv[optind], "-") == 0) {
|
|
SET_BINARY_MODE(stdin);
|
|
err = mpc_reader_init_stdio_stream(& reader, stdin);
|
|
} else
|
|
err = mpc_reader_init_stdio(&reader, argv[optind]);
|
|
if(err < 0) return !MPC_STATUS_OK;
|
|
|
|
demux = mpc_demux_init(&reader);
|
|
if(!demux) return !MPC_STATUS_OK;
|
|
mpc_demux_get_info(demux, &si);
|
|
|
|
if (info == MPC_TRUE) {
|
|
print_info(&si, argv[optind]);
|
|
mpc_demux_exit(demux);
|
|
mpc_reader_exit_stdio(&reader);
|
|
return 0;
|
|
}
|
|
|
|
if (!check)
|
|
is_wav_output = argc - optind > 1;
|
|
if(is_wav_output)
|
|
{
|
|
t_wav_output_file_callback wavo_fc;
|
|
memset(&wav_output, 0, sizeof wav_output);
|
|
wavo_fc.m_seek = mpc_wav_output_seek;
|
|
wavo_fc.m_write = mpc_wav_output_write;
|
|
if (strcmp(argv[optind + 1], "-") == 0) {
|
|
SET_BINARY_MODE(stdout);
|
|
wavo_fc.m_user_data = stdout;
|
|
} else
|
|
wavo_fc.m_user_data = fopen(argv[optind + 1], "wb");
|
|
if(!wavo_fc.m_user_data) return !MPC_STATUS_OK;
|
|
err = waveformat_output_open(&wav_output, wavo_fc, si.channels, 16, 0, si.sample_freq, (t_wav_uint32) si.samples * si.channels);
|
|
if(!err) return !MPC_STATUS_OK;
|
|
}
|
|
|
|
sum = total_samples = 0;
|
|
while(MPC_TRUE)
|
|
{
|
|
mpc_frame_info frame;
|
|
|
|
frame.buffer = sample_buffer;
|
|
if (check)
|
|
demux->d->samples_to_skip = MPC_FRAME_LENGTH + MPC_DECODER_SYNTH_DELAY;
|
|
begin = clock();
|
|
err = mpc_demux_decode(demux, &frame);
|
|
end = clock();
|
|
if(frame.bits == -1) break;
|
|
|
|
total_samples += frame.samples;
|
|
sum += end - begin;
|
|
|
|
if(is_wav_output) {
|
|
#ifdef MPC_FIXED_POINT
|
|
mpc_int16_t tmp_buff[MPC_DECODER_BUFFER_LENGTH];
|
|
int i;
|
|
for( i = 0; i < MPC_DECODER_BUFFER_LENGTH; i++) {
|
|
int tmp = sample_buffer[i] >> MPC_FIXED_POINT_FRACTPART;
|
|
if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
|
|
if (tmp < -(1 << 15)) tmp = -(1 << 15);
|
|
tmp_buff[i] = tmp;
|
|
}
|
|
if(waveformat_output_process_int16(&wav_output, tmp_buff, frame.samples * si.channels) < 0)
|
|
#else
|
|
if(waveformat_output_process_float32(&wav_output, sample_buffer, frame.samples * si.channels) < 0)
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err != MPC_STATUS_OK)
|
|
fprintf(stderr, "An error occured while decoding\n");
|
|
else if (check)
|
|
fprintf(stderr, "No error found\n");
|
|
|
|
if (!check) {
|
|
fprintf(stderr, "%u samples ", total_samples);
|
|
if (sum <= 0) sum = 1;
|
|
total_samples = (mpc_uint32_t) ((mpc_uint64_t) total_samples * CLOCKS_PER_SEC * 100 / ((mpc_uint64_t)si.sample_freq * sum));
|
|
fprintf(stderr, "decoded in %u ms (%u.%02ux)\n",
|
|
(unsigned int) (sum * 1000 / CLOCKS_PER_SEC),
|
|
total_samples / 100,
|
|
total_samples % 100
|
|
);
|
|
}
|
|
|
|
mpc_demux_exit(demux);
|
|
mpc_reader_exit_stdio(&reader);
|
|
if(is_wav_output)
|
|
{
|
|
waveformat_output_close(&wav_output);
|
|
fclose(wav_output.m_callback.m_user_data);
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
assert(_CrtCheckMemory());
|
|
_CrtDumpMemoryLeaks();
|
|
#endif
|
|
return err;
|
|
}
|