summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-10-08 00:35:37 +0200
committerwm4 <wm4@nowhere>2020-10-08 00:35:37 +0200
commit4f18e7927bacd2e887f8cca48a967804ce7adf86 (patch)
treedc6f7b2244d54868a09ff632c7536ec34d00c238
parent5dc16a4a183d51736cc56b4ef0a6b91a7b56fa09 (diff)
downloadmpv-4f18e7927bacd2e887f8cca48a967804ce7adf86.tar.bz2
mpv-4f18e7927bacd2e887f8cca48a967804ce7adf86.tar.xz
demux: add a POS
I regret doing this so much, it's fucking garbage. Fixes: #5100
-rw-r--r--demux/demux.c4
-rw-r--r--demux/demux_midi.c180
-rw-r--r--wscript5
-rw-r--r--wscript_build.py1
4 files changed, 190 insertions, 0 deletions
diff --git a/demux/demux.c b/demux/demux.c
index e4dda43cf0..1c09d1c1bf 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -66,6 +66,7 @@ extern const demuxer_desc_t demuxer_desc_rar;
extern const demuxer_desc_t demuxer_desc_libarchive;
extern const demuxer_desc_t demuxer_desc_null;
extern const demuxer_desc_t demuxer_desc_timeline;
+extern const demuxer_desc_t demuxer_desc_midi;
static const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_disc,
@@ -78,6 +79,9 @@ static const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_libarchive,
#endif
&demuxer_desc_lavf,
+#if HAVE_FLUIDSYNTH
+ &demuxer_desc_midi,
+#endif
&demuxer_desc_mf,
&demuxer_desc_playlist,
&demuxer_desc_null,
diff --git a/demux/demux_midi.c b/demux/demux_midi.c
new file mode 100644
index 0000000000..a2d6beb0ff
--- /dev/null
+++ b/demux/demux_midi.c
@@ -0,0 +1,180 @@
+#include <math.h>
+
+#include <fluidsynth.h>
+
+#include "codec_tags.h"
+#include "demux.h"
+#include "stream/stream.h"
+
+// Arbitrary.
+#define FRAME_SAMPLES 1024
+#define SAMPLERATE 48000
+
+struct priv {
+ struct sh_stream *sh;
+ fluid_settings_t *settings;
+ fluid_synth_t *synth;
+ fluid_player_t *player;
+ uint64_t samples;
+ struct demux_packet *first_pkt;
+};
+
+static bool check_midi(uint8_t *buf, int size)
+{
+ if (size < 4 + 4 + 6 + 4)
+ return false;
+ if (memcmp(buf, "MThd", 4))
+ return false;
+ if (buf[4] || buf[5] || buf[6] || buf[7] != 6)
+ return false; // length must always be 6
+ if (buf[8] || buf[9] > 2)
+ return false; // version is always 0/1/2
+ if (memcmp(&buf[4 + 4 + 6], "MTrk", 4))
+ return false; // expect a MTrk chunk to follow
+ // Regarding bit 15 (SMPTE format flag): fluidsynth doesn't support it
+ int division = (buf[12] << 8) | buf[13];
+ return division > 0 && !(division & (1 << 15));
+}
+
+static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **pkt)
+{
+ struct priv *p = demuxer->priv;
+
+ if (p->first_pkt) {
+ *pkt = p->first_pkt;
+ p->first_pkt = NULL;
+ return true;
+ }
+
+ if (fluid_player_get_status(p->player) != FLUID_PLAYER_PLAYING)
+ return false;
+
+ struct demux_packet *dp = new_demux_packet(FRAME_SAMPLES * 4 * 2);
+ if (!dp)
+ return true;
+
+ fluid_synth_write_float(p->synth, FRAME_SAMPLES,
+ dp->buffer, 0, 2,
+ dp->buffer, 1, 2);
+
+ dp->pts = p->samples / (double)p->sh->codec->samplerate;
+ p->samples += FRAME_SAMPLES;
+
+ dp->stream = p->sh->index;
+ dp->keyframe = true;
+ *pkt = dp;
+
+ return true;
+}
+
+static void d_close(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ if (p->player)
+ delete_fluid_player(p->player);
+ if (p->synth)
+ delete_fluid_synth(p->synth);
+ if (p->settings)
+ delete_fluid_settings(p->settings);
+ talloc_free(p->first_pkt);
+}
+
+static void no(int level, const char *message, void *data)
+{
+}
+
+static int try_open_file(struct demuxer *demuxer, enum demux_check check)
+{
+ struct priv *p = talloc_zero(demuxer, struct priv);
+ demuxer->priv = p;
+
+ uint8_t probe[STREAM_BUFFER_SIZE];
+ int len = stream_read_peek(demuxer->stream, probe, sizeof(probe));
+ if (len < 1 || !check_midi(probe, len))
+ return -1;
+
+ bstr data = stream_read_complete(demuxer->stream, demuxer, 1000000);
+ if (data.start == NULL)
+ return -1;
+
+ // Another idiot API with a global log callback and defaulting to stderr (or
+ // stdout on win32 - lol?). Shut it up to disable particularly stupid
+ // messages, such as about SDL (wtf? oh yes, they mess with SDL's fucking
+ // stupid global state too, even if you're not asking for it).
+ int fucking_stupid[] = {FLUID_PANIC, FLUID_ERR, FLUID_WARN, FLUID_INFO,
+ FLUID_DBG};
+ for (int n = 0; n < MP_ARRAY_SIZE(fucking_stupid); n++)
+ fluid_set_log_function(fucking_stupid[n], no, NULL);
+
+ p->settings = new_fluid_settings();
+ if (!p->settings)
+ goto error;
+ if (fluid_settings_setstr(p->settings, "player.timing-source", "sample"))
+ goto error;
+ if (fluid_settings_setnum(p->settings, "synth.sample-rate", SAMPLERATE))
+ goto error;
+ p->synth = new_fluid_synth(p->settings);
+ if (!p->synth)
+ goto error;
+ p->player = new_fluid_player(p->synth);
+ if (!p->player)
+ goto error;
+
+ char *soundfont;
+ if (fluid_settings_dupstr(p->settings, "synth.default-soundfont", &soundfont))
+ soundfont = NULL;
+ if (!soundfont) {
+ MP_ERR(demuxer, "No sound font available.\n");
+ goto error;
+ }
+ int soundfont_st = fluid_synth_sfload(p->synth, soundfont, 1);
+ fluid_free(soundfont);
+ if (!soundfont_st) {
+ MP_ERR(demuxer, "Failed to load sound font available.\n");
+ goto error;
+ }
+
+ if (fluid_player_add_mem(p->player, data.start, data.len))
+ goto error;
+
+ p->sh = demux_alloc_sh_stream(STREAM_AUDIO);
+ struct mp_codec_params *c = p->sh->codec;
+ c->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
+ c->samplerate = SAMPLERATE;
+ c->native_tb_num = 1;
+ c->native_tb_den = c->samplerate;
+ mp_set_pcm_codec(p->sh->codec, true, true, 32, false);
+
+ demux_add_sh_stream(demuxer, p->sh);
+
+ if (fluid_player_play(p->player))
+ goto error;
+
+ // Fluidsynth has some sort of internal playlist, and it advances to the
+ // current one only if you "pull" some samples. That means we don't know
+ // whether the MIDI file can even be loaded by Fluidsynth until we read
+ // same data.
+ // This heuristic may fail on very short MIDI files.
+ d_read_packet(demuxer, &p->first_pkt);
+ if (fluid_player_get_status(p->player) != FLUID_PLAYER_PLAYING)
+ goto error;
+
+ talloc_free(data.start);
+ demuxer->seekable = false;
+ demux_close_stream(demuxer);
+
+ return 0;
+
+error:
+ MP_ERR(demuxer, "Fluidsynth failed to initialize.\n");
+ return -1;
+}
+
+const struct demuxer_desc demuxer_desc_midi = {
+ .name = "midi",
+ .desc = "MIDI via fluidsynth",
+ .open = try_open_file,
+ .close = d_close,
+ .read_packet = d_read_packet,
+};
diff --git a/wscript b/wscript
index 6daf92e60b..0467206462 100644
--- a/wscript
+++ b/wscript
@@ -383,6 +383,11 @@ iconv support use --disable-iconv.",
'desc': 'SDL2 gamepad input',
'deps': 'sdl2',
'func': check_true,
+ }, {
+ 'name': '--fluidsynth',
+ 'desc': 'MIDI file playback support via fluidsynth',
+ 'func': check_pkg_config('fluidsynth', '>= 2.1.5'),
+ 'default': 'disable',
}
]
diff --git a/wscript_build.py b/wscript_build.py
index e8428fa9dc..a12badaf24 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -276,6 +276,7 @@ def build(ctx):
( "demux/demux_lavf.c" ),
( "demux/demux_libarchive.c", "libarchive" ),
( "demux/demux_mf.c" ),
+ ( "demux/demux_midi.c", "fluidsynth" ),
( "demux/demux_mkv.c" ),
( "demux/demux_mkv_timeline.c" ),
( "demux/demux_null.c" ),