This commit is contained in:
2026-03-29 14:01:52 +03:00
commit 0611279128
210 changed files with 60454 additions and 0 deletions

464
ip/mpc.c Normal file
View File

@@ -0,0 +1,464 @@
/*
* Copyright 2008-2013 Various Authors
* Copyright 2006 Chun-Yu Shei <cshei AT cs.indiana.edu>
*
* Cleaned up by Timo Hirvonen <tihirvon@gmail.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "../ip.h"
#include "../ape.h"
#include "../comment.h"
#include "../file.h"
#include "../xmalloc.h"
#include "../read_wrapper.h"
#ifdef HAVE_CONFIG
#include "../config/mpc.h"
#endif
#if MPC_SV8
#include <mpc/mpcdec.h>
#define callback_t mpc_reader
#define get_ip_data(d) (d)->data
#else
#include <mpcdec/mpcdec.h>
#define MPC_FALSE FALSE
#define MPC_TRUE TRUE
#define callback_t void
#define get_ip_data(d) (d)
#endif
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
struct mpc_private {
#if MPC_SV8
mpc_demux *decoder;
#else
mpc_decoder decoder;
#endif
mpc_reader reader;
mpc_streaminfo info;
off_t file_size;
int samples_pos;
int samples_avail;
/* mpcdec/mpcdec.h
*
* the api doc says this is pcm samples per mpc frame
* but it's really pcm _frames_ per mpc frame
* MPC_FRAME_LENGTH = 36 * 32 (1152)
*
* this is wrong, it should be 2 * MPC_FRAME_LENGTH (2304)
* MPC_DECODER_BUFFER_LENGTH = 4 * MPC_FRAME_LENGTH (4608)
*
* use MPC_DECODER_BUFFER_LENGTH just to be sure it works
*/
MPC_SAMPLE_FORMAT samples[MPC_DECODER_BUFFER_LENGTH];
struct {
unsigned long samples;
unsigned long bits;
} current;
};
/* callbacks */
static mpc_int32_t read_impl(callback_t *data, void *ptr, mpc_int32_t size)
{
struct input_plugin_data *ip_data = get_ip_data(data);
int rc;
rc = read_wrapper(ip_data, ptr, size);
if (rc == -1)
return -1;
if (rc == 0) {
errno = 0;
return 0;
}
return rc;
}
static mpc_bool_t seek_impl(callback_t *data, mpc_int32_t offset)
{
struct input_plugin_data *ip_data = get_ip_data(data);
if (lseek(ip_data->fd, offset, SEEK_SET) == -1)
return MPC_FALSE;
return MPC_TRUE;
}
static mpc_int32_t tell_impl(callback_t *data)
{
struct input_plugin_data *ip_data = get_ip_data(data);
return lseek(ip_data->fd, 0, SEEK_CUR);
}
static mpc_int32_t get_size_impl(callback_t *data)
{
struct input_plugin_data *ip_data = get_ip_data(data);
struct mpc_private *priv = ip_data->private;
return priv->file_size;
}
static mpc_bool_t canseek_impl(callback_t *data)
{
struct input_plugin_data *ip_data = get_ip_data(data);
return !ip_data->remote;
}
static int mpc_open(struct input_plugin_data *ip_data)
{
struct mpc_private *priv;
const struct mpc_private priv_init = {
.file_size = -1,
/* set up an mpc_reader linked to our function implementations */
.reader = {
.read = read_impl,
.seek = seek_impl,
.tell = tell_impl,
.get_size = get_size_impl,
.canseek = canseek_impl,
.data = ip_data
}
};
priv = xnew(struct mpc_private, 1);
*priv = priv_init;
if (!ip_data->remote) {
priv->file_size = lseek(ip_data->fd, 0, SEEK_END);
lseek(ip_data->fd, 0, SEEK_SET);
}
/* must be before mpc_streaminfo_read() */
ip_data->private = priv;
/* read file's streaminfo data */
#if MPC_SV8
priv->decoder = mpc_demux_init(&priv->reader);
if (!priv->decoder) {
#else
mpc_streaminfo_init(&priv->info);
if (mpc_streaminfo_read(&priv->info, &priv->reader) != ERROR_CODE_OK) {
#endif
free(priv);
return -IP_ERROR_FILE_FORMAT;
}
#if MPC_SV8
mpc_demux_get_info(priv->decoder, &priv->info);
#else
/* instantiate a decoder with our file reader */
mpc_decoder_setup(&priv->decoder, &priv->reader);
if (!mpc_decoder_initialize(&priv->decoder, &priv->info)) {
free(priv);
return -IP_ERROR_FILE_FORMAT;
}
#endif
priv->samples_avail = 0;
priv->samples_pos = 0;
ip_data->sf = sf_rate(priv->info.sample_freq) | sf_channels(priv->info.channels) |
sf_bits(16) | sf_signed(1);
channel_map_init_waveex(priv->info.channels, 0, ip_data->channel_map);
return 0;
}
static int mpc_close(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
#if MPC_SV8
mpc_demux_exit(priv->decoder);
#endif
free(priv);
ip_data->private = NULL;
return 0;
}
static int scale(struct input_plugin_data *ip_data, char *buffer, int count)
{
struct mpc_private *priv = ip_data->private;
const MPC_SAMPLE_FORMAT *samples;
const int clip_min = (unsigned)-1 << (16 - 1);
const int clip_max = (1 << (16 - 1)) - 1;
const int float_scale = 1 << (16 - 1);
int i, sample_count;
/* number of bytes to 16-bit samples */
sample_count = count / 2;
if (sample_count > priv->samples_avail)
sample_count = priv->samples_avail;
/* scale 32-bit samples to 16-bit */
samples = priv->samples + priv->samples_pos;
for (i = 0; i < sample_count; i++) {
int val;
val = samples[i] * float_scale;
if (val < clip_min) {
val = clip_min;
} else if (val > clip_max) {
val = clip_max;
}
buffer[i * 2 + 0] = val & 0xff;
buffer[i * 2 + 1] = val >> 8;
}
priv->samples_pos += sample_count;
priv->samples_avail -= sample_count;
if (priv->samples_avail == 0)
priv->samples_pos = 0;
/* number of 16-bit samples to bytes */
return sample_count * 2;
}
static int mpc_read(struct input_plugin_data *ip_data, char *buffer, int count)
{
struct mpc_private *priv = ip_data->private;
#if MPC_SV8
mpc_status status;
mpc_frame_info frame;
int samples;
frame.buffer = priv->samples;
while (priv->samples_avail == 0) {
status = mpc_demux_decode(priv->decoder, &frame);
if (status != MPC_STATUS_OK) {
return -IP_ERROR_ERRNO;
}
if (frame.bits == -1) {
/* EOF */
return 0;
}
samples = frame.samples;
priv->samples_avail = samples * priv->info.channels;
priv->current.samples += frame.samples;
priv->current.bits += frame.bits;
}
#else
if (priv->samples_avail == 0) {
uint32_t acc = 0, bits = 0;
uint32_t status = mpc_decoder_decode(&priv->decoder, priv->samples, &acc, &bits);
if (status == (uint32_t)(-1)) {
/* right ret val? */
return -IP_ERROR_ERRNO;
}
if (status == 0) {
/* EOF */
return 0;
}
/* status seems to be number of _frames_
* the api documentation is wrong
*/
priv->samples_avail = status * priv->info.channels;
priv->current.samples += status;
priv->current.bits += bits;
}
#endif
return scale(ip_data, buffer, count);
}
static int mpc_seek(struct input_plugin_data *ip_data, double offset)
{
struct mpc_private *priv = ip_data->private;
priv->samples_pos = 0;
priv->samples_avail = 0;
#if MPC_SV8
if (mpc_demux_seek_second(priv->decoder, offset) == MPC_STATUS_OK)
#else
if (mpc_decoder_seek_seconds(&priv->decoder, offset))
#endif
return 0;
return -1;
}
static const char *gain_to_str(int gain)
{
static char buf[16];
#if MPC_SV8
float g = MPC_OLD_GAIN_REF - gain / 256.f;
sprintf(buf, "%.2f", g);
#else
int b, a = gain / 100;
if (gain < 0) {
b = -gain % 100;
} else {
b = gain % 100;
}
sprintf(buf, "%d.%02d", a, b);
#endif
return buf;
}
static const char *peak_to_str(unsigned int peak)
{
static char buf[16];
#if MPC_SV8
sprintf(buf, "%.5f", peak / 256.f / 100.f);
#else
sprintf(buf, "%d.%05d", peak / 32767, peak % 32767);
#endif
return buf;
}
static int mpc_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
{
struct mpc_private *priv = ip_data->private;
GROWING_KEYVALS(c);
int count, i;
APETAG(ape);
count = ape_read_tags(&ape, ip_data->fd, 1);
if (count < 0)
goto out;
for (i = 0; i < count; i++) {
char *k, *v;
k = ape_get_comment(&ape, &v);
if (!k)
break;
comments_add(&c, k, v);
free(k);
}
out:
if (priv->info.gain_title && priv->info.peak_title) {
comments_add_const(&c, "replaygain_track_gain", gain_to_str(priv->info.gain_title));
comments_add_const(&c, "replaygain_track_peak", peak_to_str(priv->info.peak_title));
}
if (priv->info.gain_album && priv->info.peak_album) {
comments_add_const(&c, "replaygain_album_gain", gain_to_str(priv->info.gain_album));
comments_add_const(&c, "replaygain_album_peak", peak_to_str(priv->info.peak_album));
}
keyvals_terminate(&c);
*comments = c.keyvals;
ape_free(&ape);
return 0;
}
static int mpc_duration(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
/* priv->info.pcm_samples seems to be number of frames
* priv->info.frames is _not_ pcm frames
*/
#if MPC_SV8
return mpc_streaminfo_get_length(&priv->info);
#else
return priv->info.pcm_samples / priv->info.sample_freq;
#endif
}
static long mpc_bitrate(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
if (priv->info.average_bitrate)
return (long) (priv->info.average_bitrate + 0.5);
if (priv->info.bitrate)
return priv->info.bitrate;
return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
}
static long mpc_current_bitrate(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
long bitrate = -1;
if (priv->current.samples > 0) {
bitrate = (priv->info.sample_freq * priv->current.bits) / priv->current.samples;
priv->current.samples = 0;
priv->current.bits = 0;
}
return bitrate;
}
static char *mpc_codec(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
switch (priv->info.stream_version) {
case 7:
return xstrdup("mpc7");
case 8:
return xstrdup("mpc8");
}
return NULL;
}
static char *mpc_codec_profile(struct input_plugin_data *ip_data)
{
struct mpc_private *priv = ip_data->private;
const char *profile = priv->info.profile_name;
char *s = NULL;
if (profile && profile[0]) {
int i;
/* remove single quotes */
while (*profile == '\'')
profile++;
s = xstrdup(profile);
for (i = strlen(s) - 1; s[i] == '\'' && i >= 0; i--)
s[i] = '\0';
}
return s;
}
const struct input_plugin_ops ip_ops = {
.open = mpc_open,
.close = mpc_close,
.read = mpc_read,
.seek = mpc_seek,
.read_comments = mpc_read_comments,
.duration = mpc_duration,
.bitrate = mpc_bitrate,
.bitrate_current = mpc_current_bitrate,
.codec = mpc_codec,
.codec_profile = mpc_codec_profile
};
const int ip_priority = 50;
const char *const ip_extensions[] = { "mpc", "mpp", "mp+", NULL };
const char *const ip_mime_types[] = { "audio/x-musepack", NULL };
const struct input_plugin_opt ip_options[] = { { NULL } };
const unsigned ip_abi_version = IP_ABI_VERSION;