diff -rupN cmus-v2.5.0/Makefile cmus/Makefile --- cmus-v2.5.0/Makefile 2012-11-07 09:46:17.000000000 +0100 +++ cmus/Makefile 2013-01-04 07:15:35.293069953 +0100 @@ -74,6 +74,7 @@ mikmod-objs := mikmod.lo modplug-objs := modplug.lo mpc-objs := mpc.lo vorbis-objs := vorbis.lo +opus-objs := opus.lo wavpack-objs := wavpack.lo wav-objs := wav.lo mp4-objs := mp4.lo @@ -88,6 +89,7 @@ ip-$(CONFIG_MIKMOD) += mikmod.so ip-$(CONFIG_MODPLUG) += modplug.so ip-$(CONFIG_MPC) += mpc.so ip-$(CONFIG_VORBIS) += vorbis.so +ip-$(CONFIG_OPUS) += opus.so ip-$(CONFIG_WAVPACK) += wavpack.so ip-$(CONFIG_WAV) += wav.so ip-$(CONFIG_MP4) += mp4.so @@ -102,6 +104,7 @@ $(mikmod-objs): CFLAGS += $(MIKMOD_CFLA $(modplug-objs): CFLAGS += $(MODPLUG_CFLAGS) $(mpc-objs): CFLAGS += $(MPC_CFLAGS) $(vorbis-objs): CFLAGS += $(VORBIS_CFLAGS) +$(opus-objs): CFLAGS += $(OPUS_CFLAGS) $(wavpack-objs): CFLAGS += $(WAVPACK_CFLAGS) $(mp4-objs): CFLAGS += $(MP4_CFLAGS) $(aac-objs): CFLAGS += $(AAC_CFLAGS) @@ -129,6 +132,9 @@ mpc.so: $(mpc-objs) $(libcmus-y) vorbis.so: $(vorbis-objs) $(libcmus-y) $(call cmd,ld_dl,$(VORBIS_LIBS)) +opus.so: $(opus-objs) $(libcmus-y) + $(call cmd,ld_dl,$(OPUS_LIBS)) + wavpack.so: $(wavpack-objs) $(libcmus-y) $(call cmd,ld_dl,$(WAVPACK_LIBS)) diff -rupN cmus-v2.5.0/configure cmus/configure --- cmus-v2.5.0/configure 2012-11-07 09:46:17.000000000 +0100 +++ cmus/configure 2013-01-04 07:15:35.293069953 +0100 @@ -228,6 +228,12 @@ check_vorbis() fi } +check_opus() +{ + pkg_config OPUS "opusfile" + return $? +} + check_wavpack() { pkg_config WAVPACK "wavpack" "" "-lwavpack" @@ -425,6 +431,7 @@ Optional Features: y/n CONFIG_MPC libmpcdec (Musepack .mpc, .mpp, .mp+) [auto] CONFIG_VORBIS Ogg/Vorbis (.ogg, application/ogg, audio/x-ogg) [auto] CONFIG_TREMOR Use Tremor as Ogg/Vorbis input plugin [n] + CONFIG_OPUS Opus (.opus) [auto] CONFIG_WAV WAV [y] CONFIG_WAVPACK WavPack (.wv, audio/x-wavpack) [auto] CONFIG_MP4 MPEG-4 AAC (.mp4, .m4a, .m4b) [auto] @@ -485,6 +492,7 @@ check check_mikmod CONFIG_MIKMOD check check_modplug CONFIG_MODPLUG check check_mpc CONFIG_MPC check check_vorbis CONFIG_VORBIS +check check_opus CONFIG_OPUS check check_wavpack CONFIG_WAVPACK check check_mp4 CONFIG_MP4 check check_aac CONFIG_AAC @@ -534,7 +542,7 @@ config_header config/cue.h CONFIG_CUE CFLAGS="${CFLAGS} -DHAVE_CONFIG" makefile_vars bindir datadir libdir mandir exampledir -makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG CONFIG_CUE +makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_OPUS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG CONFIG_CUE makefile_vars CONFIG_ROAR CONFIG_PULSE CONFIG_ALSA CONFIG_AO CONFIG_ARTS CONFIG_OSS CONFIG_SUN CONFIG_WAVEOUT generate_config_mk diff -rupN cmus-v2.5.0/opus.c cmus/opus.c --- cmus-v2.5.0/opus.c 1970-01-01 01:00:00.000000000 +0100 +++ cmus/opus.c 2013-01-04 07:15:35.493069945 +0100 @@ -0,0 +1,335 @@ +/* + * Copyright 2008-2012 Various Authors + * Copyright 2004-2005 Timo Hirvonen + * + * 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 . + */ + +#include "ip.h" +#include "xmalloc.h" +#include "read_wrapper.h" +#include "debug.h" +#include "comment.h" + +#include + +#include +#include +#include +#include + +#define SAMPLING_RATE 48000 +#define CHANNELS 2 + +struct opus_private { + OggOpusFile *of; + int current_link; +}; + +static int read_func(void *datasource, unsigned char *ptr, int size) +{ + struct input_plugin_data *ip_data = datasource; + return read_wrapper(ip_data, ptr, size); +} + +static int seek_func(void *datasource, opus_int64 offset, int whence) +{ + struct input_plugin_data *ip_data = datasource; + return lseek(ip_data->fd, offset, whence); +} + +static int close_func(void *datasource) +{ + struct input_plugin_data *ip_data; + int rc; + + ip_data = datasource; + rc = close(ip_data->fd); + ip_data->fd = -1; + return rc; +} + +static opus_int64 tell_func(void *datasource) +{ + struct input_plugin_data *ip_data = datasource; + return lseek(ip_data->fd, 0, SEEK_CUR); +} + +static OpusFileCallbacks callbacks = { + .read = read_func, + .seek = seek_func, + .tell = tell_func, + .close = close_func +}; + +static int opus_open(struct input_plugin_data *ip_data) +{ + struct opus_private *priv; + int rc; + void *source; + + priv = xnew(struct opus_private, 1); + priv->current_link = -1; + priv->of = NULL; + + source = op_fdopen(&callbacks, ip_data->fd, "r"); + if (source == NULL) { + free(priv); + return -IP_ERROR_INTERNAL; + } + + priv->of = op_open_callbacks(source, &callbacks, NULL, 0, &rc); + if (rc != 0) { + d_print("op_open_callbacks failed: %d:%s\n", rc, strerror(rc)); + free(priv); + /* ogg is a container format, so it is likely to contain + * something else if it isn't opus */ + return -IP_ERROR_UNSUPPORTED_FILE_TYPE; + } + ip_data->private = priv; + + ip_data->sf = sf_rate(SAMPLING_RATE) + | sf_channels(CHANNELS) + | sf_bits(16) + | sf_signed(1); +#ifdef WORDS_BIGENDIAN + ip_data->sf |= sf_bigendian(1); +#endif + return 0; +} + +static int opus_close(struct input_plugin_data *ip_data) +{ + struct opus_private *priv = ip_data->private; + /* this closes ip_data->fd! */ + op_free(priv->of); + ip_data->fd = -1; + free(priv); + ip_data->private = NULL; + return 0; +} + +/* + * -n + * indicates error + * 0 + * indicates EOF + * n + * indicates actual number of bytes read + */ +static int opus_read(struct input_plugin_data *ip_data, char *buffer, int count) +{ + struct opus_private *priv; + int samples, current_link, rc; + + priv = ip_data->private; + + /* samples = number of samples read per channel */ + samples = op_read_stereo(priv->of, (opus_int16*)buffer, + count / sizeof(opus_int16)); + if (samples < 0) { + switch (samples) { + case OP_HOLE: + errno = EAGAIN; + rc = -1; + break; + case OP_EREAD: + errno = EINVAL; + rc = -1; + break; + case OP_EFAULT: + errno = EINVAL; + rc = -1; + break; + case OP_EIMPL: + rc = -IP_ERROR_FUNCTION_NOT_SUPPORTED; + break; + case OP_EINVAL: + errno = EINVAL; + rc = -1; + break; + case OP_ENOTFORMAT: + rc = -IP_ERROR_FILE_FORMAT; + break; + case OP_EBADHEADER: + rc = -IP_ERROR_FILE_FORMAT; + break; + case OP_EVERSION: + rc = -IP_ERROR_FILE_FORMAT; + break; + case OP_EBADPACKET: + errno = EINVAL; + rc = -1; + break; + case OP_EBADLINK: + errno = EINVAL; + rc = -1; + break; + case OP_EBADTIMESTAMP: + rc = -IP_ERROR_FILE_FORMAT; + break; + default: + d_print("error: %d\n", samples); + rc = -IP_ERROR_FILE_FORMAT; + } + } else if (samples == 0) { + /* EOF or buffer too small */ + rc = 0; + } else { + current_link = op_current_link(priv->of); + if (current_link < 0) { + d_print("error: %d\n", current_link); + rc = -1; + } else { + if (ip_data->remote && current_link != priv->current_link) { + ip_data->metadata_changed = 1; + priv->current_link = current_link; + } + + /* bytes = samples * channels * sample_size */ + rc = samples * CHANNELS * sizeof(opus_int16); + } + } + + return rc; +} + +static int opus_seek(struct input_plugin_data *ip_data, double offset) +{ + struct opus_private *priv; + int rc; + + priv = ip_data->private; + + rc = op_pcm_seek(priv->of, offset * SAMPLING_RATE); + switch (rc) { + case OP_ENOSEEK: + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; + case OP_EINVAL: + return -IP_ERROR_INTERNAL; + case OP_EREAD: + return -IP_ERROR_INTERNAL; + case OP_EFAULT: + return -IP_ERROR_INTERNAL; + case OP_EBADLINK: + return -IP_ERROR_INTERNAL; + } + return 0; +} + +static int opus_read_comments(struct input_plugin_data *ip_data, + struct keyval **comments) +{ + GROWING_KEYVALS(c); + struct opus_private *priv; + const OpusTags *ot; + int i; + + priv = ip_data->private; + + ot = op_tags(priv->of, -1); + if (ot == NULL) { + d_print("ot == NULL\n"); + *comments = keyvals_new(0); + return 0; + } + + for (i = 0; i < ot->comments; i++) { + const char *str = ot->user_comments[i]; + const char *eq = strchr(str, '='); + char *key; + + if (!eq) { + d_print("invalid comment: '%s' ('=' expected)\n", str); + continue; + } + + key = xstrndup(str, eq - str); + comments_add_const(&c, key, eq + 1); + free(key); + } + keyvals_terminate(&c); + *comments = c.keyvals; + return 0; +} + +static int opus_duration(struct input_plugin_data *ip_data) +{ + struct opus_private *priv; + ogg_int64_t samples; + + priv = ip_data->private; + + samples = op_pcm_total(priv->of, -1); + if (samples < 0) + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; + + return samples / SAMPLING_RATE; +} + +static long opus_bitrate(struct input_plugin_data *ip_data) +{ + struct opus_private *priv; + opus_int32 bitrate; + + priv = ip_data->private; + + bitrate = op_bitrate(priv->of, -1); + if (bitrate < 0) + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; + else + return bitrate; +} + +static long opus_current_bitrate(struct input_plugin_data *ip_data) +{ + struct opus_private *priv; + opus_int32 bitrate; + + priv = ip_data->private; + + bitrate = op_bitrate_instant(priv->of); + if (bitrate < 0) + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; + else + return bitrate; +} + +static char *opus_codec(struct input_plugin_data *ip_data) +{ + return xstrdup("opus"); +} + +static char *opus_codec_profile(struct input_plugin_data *ip_data) +{ + return NULL; +} + +const struct input_plugin_ops ip_ops = { + .open = opus_open, + .close = opus_close, + .read = opus_read, + .seek = opus_seek, + .read_comments = opus_read_comments, + .duration = opus_duration, + .bitrate = opus_bitrate, + .bitrate_current = opus_current_bitrate, + .codec = opus_codec, + .codec_profile = opus_codec_profile +}; + +const int ip_priority = 50; +const char * const ip_extensions[] = { "opus", NULL }; +const char * const ip_mime_types[] = { NULL }; +const char * const ip_options[] = { NULL };