Created
November 15, 2016 19:14
-
-
Save elzii/ca06101a4e59f40e2d90ae8467b65ac2 to your computer and use it in GitHub Desktop.
Patch CMUS to have libgme (game-music-emulator) support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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