Skip to content

Instantly share code, notes, and snippets.

@elzii
Created November 15, 2016 19:14
Show Gist options
  • Save elzii/ca06101a4e59f40e2d90ae8467b65ac2 to your computer and use it in GitHub Desktop.
Save elzii/ca06101a4e59f40e2d90ae8467b65ac2 to your computer and use it in GitHub Desktop.
Patch CMUS to have libgme (game-music-emulator) support
diff --git a/Makefile b/Makefile
index 162f0f2..61dc054 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ VERSION = $(or $(_ver0),$(_ver1),$(_ver2),$(_ver3))
all: main plugins man
-include config.mk
+-include config.mx
include scripts/lib.mk
CFLAGS += -D_FILE_OFFSET_BITS=64 -I"${topdir}"
@@ -83,6 +84,7 @@ mp4-objs := ip/mp4.lo
aac-objs := ip/aac.lo
ffmpeg-objs := ip/ffmpeg.lo
cue-objs := ip/cue.lo
+gme-objs := ip/gme.lo
vtx-objs := ip/vtx.lo
ip-$(CONFIG_CDIO) += ip/cdio.so
@@ -99,6 +101,7 @@ ip-$(CONFIG_MP4) += ip/mp4.so
ip-$(CONFIG_AAC) += ip/aac.so
ip-$(CONFIG_FFMPEG) += ip/ffmpeg.so
ip-$(CONFIG_CUE) += ip/cue.so
+ip-$(CONFIG_GME) += ip/gme.so
ip-$(CONFIG_VTX) += ip/vtx.so
$(cdio-objs): CFLAGS += $(CDIO_CFLAGS) $(CDDB_CFLAGS)
@@ -114,6 +117,7 @@ $(mp4-objs): CFLAGS += $(MP4_CFLAGS)
$(aac-objs): CFLAGS += $(AAC_CFLAGS)
$(ffmpeg-objs): CFLAGS += $(FFMPEG_CFLAGS)
$(vtx-objs): CFLAGS += $(VTX_CFLAGS)
+$(gme-objs): CFLAGS += $(GME_CFLAGS) $(ZLIB_CFLAGS)
ip/cdio.so: $(cdio-objs) $(libcmus-y)
$(call cmd,ld_dl,$(CDIO_LIBS) $(CDDB_LIBS))
@@ -157,6 +161,9 @@ ip/ffmpeg.so: $(ffmpeg-objs) $(libcmus-y)
ip/cue.so: $(cue-objs) $(libcmus-y)
$(call cmd,ld_dl,$(CUE_LIBS))
+ip/gme.so: $(gme-objs) $(libcmus-y)
+ $(call cmd,ld_dl,$(GME_LIBS) $(ZLIB_LIBS))
+
ip/vtx.so: $(vtx-objs) $(libcmus-y)
$(call cmd,ld_dl,$(VTX_LIBS))
diff --git a/configure b/configure
index 44eec64..cf2223d 100755
--- a/configure
+++ b/configure
@@ -227,6 +227,13 @@ check_modplug()
return 0
}
+
+check_gme()
+{
+ pkg_config GME "libgme" && pkg_config ZLIB "zlib"
+ return $?
+}
+
check_vtx()
{
check_header ayemu.h &&
@@ -468,6 +475,7 @@ Optional Features: y/n
CONFIG_CUE CUE sheets (.cue) [y]
CONFIG_DISCID libdiscid CDDA identification [auto]
CONFIG_FFMPEG FFMPEG (.shn, .wma) [auto]
+ CONFIG_GME Game Music Emu (.ay, .gym, .spc, .vgm, .vgz) [auto]
CONFIG_FLAC Free Lossless Audio Codec (.flac, .fla) [auto]
CONFIG_JACK JACK [auto]
CONFIG_MAD MPEG Audio Decoder (.mp3, .mp2, streams) [auto]
@@ -542,6 +550,7 @@ check check_mp4 CONFIG_MP4
check check_aac CONFIG_AAC
check check_ffmpeg CONFIG_FFMPEG
check check_vtx CONFIG_VTX
+check check_gme CONFIG_GME
# nothing to check, just validate the variable values
check true CONFIG_TREMOR
check true CONFIG_WAV
@@ -598,6 +607,8 @@ makefile_vars \
CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MP4 CONFIG_MPC \
CONFIG_MPRIS CONFIG_OPUS CONFIG_OSS CONFIG_PULSE CONFIG_ROAR \
CONFIG_SAMPLERATE CONFIG_SNDIO CONFIG_SUN CONFIG_VORBIS CONFIG_VTX \
- CONFIG_WAV CONFIG_WAVEOUT CONFIG_WAVPACK
+ CONFIG_WAV CONFIG_WAVEOUT CONFIG_WAVPACK CONFIG_GME
+
+
generate_config_mk
diff --git a/ip/gme.c b/ip/gme.c
new file mode 100644
index 0000000..85cb952
--- /dev/null
+++ b/ip/gme.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2014 Boris Timofeev <[email protected]>
+ *
+ * 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 "comment.h"
+#include "debug.h"
+#include "ip.h"
+#include "path.h"
+#include "xmalloc.h"
+#include <gme/gme.h>
+#include <string.h>
+#include <zlib.h>
+
+struct gme_private {
+ Music_Emu *emu;
+ gme_info_t *info;
+ int track_id;
+};
+
+#define sample_rate 44100
+
+static int is_vgz_file(char *filename)
+{
+ const char *ext = get_extension(filename);
+ if (ext && !strcasecmp(ext, "vgz"))
+ return 1;
+ return 0;
+}
+
+static int read_gzip(char *filename, char **buffer, int *size)
+{
+ gzFile gz = gzopen(filename, "rb");
+ if (!gz) {
+ d_print("failed to open gzip file %s\n", filename);
+ return -1;
+ }
+
+ int readsize = 256 * 1024;
+ *buffer = xnew(char, readsize);
+ int offset = 0;
+ int count;
+ while (!gzeof(gz)) {
+ count = gzread(gz, *buffer + offset , readsize);
+ if (count < 0) {
+ d_print("failed to read from gzip file %s\n", filename);
+ free(*buffer);
+ gzclose(gz);
+ return -1;
+ }
+ if (count > 0) {
+ offset += count;
+ }
+ if (count == readsize) {
+ *buffer = xrenew(char, *buffer, offset + readsize);
+ }
+ }
+ *size = offset;
+ gzclose(gz);
+ return 0;
+}
+
+static int gme_open(struct input_plugin_data *ip_data)
+{
+ gme_err_t rc;
+ struct gme_private *priv;
+ priv = xnew(struct gme_private, 1);
+ ip_data->private = priv;
+
+ if (is_vgz_file(ip_data->filename)) {
+ char *buffer;
+ int size;
+ if (read_gzip(ip_data->filename, &buffer, &size) != 0) {
+ free(priv);
+ return -IP_ERROR_INTERNAL;
+ }
+ rc = gme_open_data(buffer, size, &priv->emu, sample_rate);
+ free(buffer);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ free(priv);
+ return -IP_ERROR_INTERNAL;
+ }
+ } else {
+ rc = gme_open_file(ip_data->filename, &priv->emu, sample_rate);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ free(priv);
+ return -IP_ERROR_INTERNAL;
+ }
+ }
+
+ priv->track_id = 0;
+
+ rc = gme_track_info(priv->emu, &priv->info, priv->track_id);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ free(priv);
+ return -IP_ERROR_INTERNAL;
+ }
+
+ ip_data->sf = sf_bits(16) | sf_rate(sample_rate) | sf_channels(2) | sf_signed(1);
+#ifdef WORDS_BIGENDIAN
+ ip_data->sf |= sf_bigendian(1);
+#endif
+ channel_map_init_stereo(ip_data->channel_map);
+
+ gme_enable_accuracy(priv->emu, 1);
+
+ rc = gme_start_track(priv->emu, priv->track_id);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ free(priv);
+ return -IP_ERROR_INTERNAL;
+ }
+
+ return 0;
+}
+
+static int gme_close(struct input_plugin_data *ip_data)
+{
+ struct gme_private *priv = ip_data->private;
+
+ gme_free_info(priv->info);
+ gme_delete(priv->emu);
+ free(priv);
+ ip_data->private = NULL;
+ return 0;
+}
+
+static int gme_read(struct input_plugin_data *ip_data, char *buffer, int count)
+{
+ gme_err_t rc;
+ struct gme_private *priv = ip_data->private;
+
+ if (gme_track_ended(priv->emu)) {
+ if(++priv->track_id >= gme_track_count(priv->emu))
+ return 0;
+ gme_start_track(priv->emu, priv->track_id);
+ d_print("start track #%d\n", priv->track_id + 1);
+ }
+
+ rc = gme_play(priv->emu, count / 2, (short*)buffer);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ return -IP_ERROR_INTERNAL;
+ }
+
+ return count;
+}
+
+static int gme_plugin_seek(struct input_plugin_data *ip_data, double offset)
+{
+ struct gme_private *priv = ip_data->private;
+ int msec = (int)(offset * 1000);
+
+ gme_err_t rc = gme_seek(priv->emu, msec);
+ if (rc != NULL) {
+ d_print("error: %s\n", rc);
+ return -IP_ERROR_INTERNAL;
+ }
+
+ return 0;
+}
+
+static int gme_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
+{
+
+ struct gme_private *priv = ip_data->private;
+ GROWING_KEYVALS(c);
+ const char *val;
+
+ val = priv->info->author;
+ if (val && val[0])
+ comments_add_const(&c, "artist", val);
+ val = priv->info->game;
+ if (val && val[0])
+ comments_add_const(&c, "album", val);
+ val = priv->info->song;
+ if (val && val[0])
+ comments_add_const(&c, "title", val);
+
+ keyvals_terminate(&c);
+ *comments = c.keyvals;
+ return 0;
+}
+
+static int gme_duration(struct input_plugin_data *ip_data)
+{
+ struct gme_private *priv = ip_data->private;
+ int len = priv->info->play_length / 1000;
+ d_print("l: %d, il: %d, ll: %d, pl: %d\n", priv->info->length,
+ priv->info->intro_length,
+ priv->info->loop_length,
+ priv->info->play_length);
+
+ return len;
+}
+
+static long gme_bitrate(struct input_plugin_data *ip_data)
+{
+ return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
+}
+
+static long gme_current_bitrate(struct input_plugin_data *ip_data)
+{
+ return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
+}
+
+static char *gme_codec(struct input_plugin_data *ip_data)
+{
+ return NULL;
+}
+
+static char *gme_codec_profile(struct input_plugin_data *ip_data)
+{
+ return NULL;
+}
+
+const struct input_plugin_ops ip_ops = {
+ .open = gme_open,
+ .close = gme_close,
+ .read = gme_read,
+ .seek = gme_plugin_seek,
+ .read_comments = gme_read_comments,
+ .duration = gme_duration,
+ .bitrate = gme_bitrate,
+ .bitrate_current = gme_current_bitrate,
+ .codec = gme_codec,
+ .codec_profile = gme_codec_profile
+};
+
+// const int ip_priority = 50;
+// const char * const ip_extensions[] = {
+// "ay", "gbs", "gym", "hes", "kss", "nsf", "nsfe", "sap", "spc", "vgm",
+// "vgz", NULL
+// };
+// const char * const ip_mime_types[] = { NULL };
+// const char * const ip_options[] = { NULL };
+
+
+const int ip_priority = 50;
+const char * const ip_extensions[] = {
+ "ay", "gbs", "gym", "hes", "kss", "nsf", "nsfe", "sap", "spc", "vgm",
+ "vgz", NULL
+};
+const char * const ip_mime_types[] = { NULL };
+const struct input_plugin_opt ip_options[] = { { NULL } };
+const unsigned ip_abi_version = IP_ABI_VERSION;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment