From 4be6c0d592b9b9baf44880b7a8dd715dc68a9a04 Mon Sep 17 00:00:00 2001 From: Mathew Velasquez Date: Fri, 26 Feb 2016 16:59:52 -0500 Subject: [PATCH] Gutted midi system --- mkxp.conf.sample | 16 - mkxp.pro | 12 - src/aldatasource.h | 3 - src/alstream.cpp | 13 - src/audio.cpp | 6 - src/audio.h | 1 - src/config.cpp | 3 - src/config.h | 7 - src/fluid-fun.cpp | 67 --- src/fluid-fun.h | 60 --- src/midisource.cpp | 923 ------------------------------------------ src/sharedmidistate.h | 149 ------- src/sharedstate.cpp | 10 - src/sharedstate.h | 2 - 14 files changed, 1272 deletions(-) delete mode 100644 src/fluid-fun.cpp delete mode 100644 src/fluid-fun.h delete mode 100644 src/midisource.cpp delete mode 100644 src/sharedmidistate.h diff --git a/mkxp.conf.sample b/mkxp.conf.sample index 450265f..7b82fef 100644 --- a/mkxp.conf.sample +++ b/mkxp.conf.sample @@ -184,22 +184,6 @@ # rubyLoadpath=/usr/local/share/ruby/site_ruby -# SoundFont to use for midi playback (via fluidsynth) -# (default: none) -# -# midi.soundFont=/usr/share/mysoundfont.sf2 - - -# Activate "chorus" effect for midi playback -# -# midi.chorus=false - - -# Activate "reverb" effect for midi playback -# -# midi.reverb=false - - # Number of OpenAL sources to allocate for SE playback. # If there are a lot of sounds playing at the same time # and audibly cutting each other off, try increasing diff --git a/mkxp.pro b/mkxp.pro index 8c7e598..0f9afda 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -68,10 +68,6 @@ unix { LIBS += -L$$STEAMWORKS/redistributable_bin/linux$$STEAMARCH -lsteam_api } } - - SHARED_FLUID { - PKGCONFIG += fluidsynth - } } win32 { @@ -180,8 +176,6 @@ HEADERS += \ src/windowvx.h \ src/tilemapvx.h \ src/tileatlasvx.h \ - src/sharedmidistate.h \ - src/fluid-fun.h \ src/sdl-util.h \ src/oneshot.h @@ -227,8 +221,6 @@ SOURCES += \ src/tilemapvx.cpp \ src/tileatlasvx.cpp \ src/autotilesvx.cpp \ - src/midisource.cpp \ - src/fluid-fun.cpp \ src/oneshot.cpp \ binding-mri/steam-binding.cpp @@ -267,10 +259,6 @@ EMBED = \ assets/icon.png \ assets/gamecontrollerdb.txt -SHARED_FLUID { - DEFINES += SHARED_FLUID -} - defineReplace(xxdOutput) { return($$basename(1).xxd) } diff --git a/src/aldatasource.h b/src/aldatasource.h index 4f391eb..17209c7 100644 --- a/src/aldatasource.h +++ b/src/aldatasource.h @@ -65,7 +65,4 @@ ALDataSource *createSDLSource(SDL_RWops &ops, ALDataSource *createVorbisSource(SDL_RWops &ops, bool looped); -ALDataSource *createMidiSource(SDL_RWops &ops, - bool looped); - #endif // ALDATASOURCE_H diff --git a/src/alstream.cpp b/src/alstream.cpp index 487f09f..d423953 100644 --- a/src/alstream.cpp +++ b/src/alstream.cpp @@ -22,12 +22,10 @@ #include "alstream.h" #include "sharedstate.h" -#include "sharedmidistate.h" #include "eventthread.h" #include "filesystem.h" #include "exception.h" #include "aldatasource.h" -#include "fluid-fun.h" #include "sdl-util.h" #include "debugwriter.h" @@ -229,17 +227,6 @@ struct ALStreamOpenHandler : FileSystem::OpenHandler return true; } - if (!strcmp(sig, "MThd")) - { - shState->midiState().initIfNeeded(shState->config()); - - if (HAVE_FLUID) - { - source = createMidiSource(*srcOps, looped); - return true; - } - } - source = createSDLSource(*srcOps, ext, STREAM_BUF_SIZE, looped); } catch (const Exception &e) diff --git a/src/audio.cpp b/src/audio.cpp index d536c70..fbc62c1 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -24,7 +24,6 @@ #include "audiostream.h" #include "soundemitter.h" #include "sharedstate.h" -#include "sharedmidistate.h" #include "eventthread.h" #include "sdl-util.h" @@ -310,11 +309,6 @@ void Audio::seStop() p->se.stop(); } -void Audio::setupMidi() -{ - shState->midiState().initIfNeeded(shState->config()); -} - float Audio::bgmPos() { return p->bgm.playingOffset(); diff --git a/src/audio.h b/src/audio.h index b0aec26..e231fb6 100644 --- a/src/audio.h +++ b/src/audio.h @@ -63,7 +63,6 @@ public: int pitch = 100); void seStop(); - void setupMidi(); float bgmPos(); float bgsPos(); diff --git a/src/config.cpp b/src/config.cpp index c417b42..9f6a3a5 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -155,9 +155,6 @@ void Config::read(int argc, char *argv[]) PO_DESC(allowSymlinks, bool, false) \ PO_DESC(iconPath, std::string, "") \ PO_DESC(execName, std::string, "Game") \ - PO_DESC(midi.soundFont, std::string, "") \ - PO_DESC(midi.chorus, bool, false) \ - PO_DESC(midi.reverb, bool, false) \ PO_DESC(SE.sourceCount, int, 6) \ PO_DESC(pathCache, bool, true) diff --git a/src/config.h b/src/config.h index 49e0b24..d19c953 100644 --- a/src/config.h +++ b/src/config.h @@ -56,13 +56,6 @@ struct Config std::string iconPath; std::string execName; - struct - { - std::string soundFont; - bool chorus; - bool reverb; - } midi; - struct { int sourceCount; diff --git a/src/fluid-fun.cpp b/src/fluid-fun.cpp deleted file mode 100644 index d49157d..0000000 --- a/src/fluid-fun.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "fluid-fun.h" - -#include -#include -#include - -#include "debugwriter.h" - -#ifdef SHARED_FLUID -#include -#endif - -#if __LINUX__ || __ANDROID__ -#define FLUID_LIB "libfluidsynth.so.1" -#elif __MACOSX__ -#define FLUID_LIB "libfluidsynth.1.dylib" -#elif __WINDOWS__ -#define FLUID_LIB "fluidsynth.dll" -#else -#error "platform not recognized" -#endif - -struct FluidFunctions fluid; - -static void *so; - -void initFluidFunctions() -{ -#ifdef SHARED_FLUID - -#define FLUID_FUN(name, type) \ - fluid.name = fluid_##name; - -#define FLUID_FUN2(name, type, real_name) \ - fluid.name = real_name; - -#else - so = SDL_LoadObject(FLUID_LIB); - - if (!so) - goto fail; - -#define FLUID_FUN(name, type) \ - fluid.name = (type) SDL_LoadFunction(so, "fluid_" #name); \ - if (!fluid.name) \ - goto fail; - -#define FLUID_FUN2(name, type, real_name) \ - fluid.name = (type) SDL_LoadFunction(so, #real_name); \ - if (!fluid.name) \ - goto fail; -#endif - -FLUID_FUNCS -FLUID_FUNCS2 - - return; - -#ifndef SHARED_FLUID -fail: - Debug() << "Failed to load " FLUID_LIB ". Midi playback is disabled."; - - memset(&fluid, 0, sizeof(fluid)); - SDL_UnloadObject(so); - so = 0; -#endif -} diff --git a/src/fluid-fun.h b/src/fluid-fun.h deleted file mode 100644 index f26f9fe..0000000 --- a/src/fluid-fun.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef FLUIDFUN_H -#define FLUIDFUN_H - -typedef struct _fluid_hashtable_t fluid_settings_t; -typedef struct _fluid_synth_t fluid_synth_t; - -typedef int (*FLUIDSETTINGSSETNUMPROC)(fluid_settings_t* settings, const char *name, double val); -typedef int (*FLUIDSETTINGSSETSTRPROC)(fluid_settings_t* settings, const char *name, const char *str); -typedef int (*FLUIDSYNTHSFLOADPROC)(fluid_synth_t* synth, const char* filename, int reset_presets); -typedef int (*FLUIDSYNTHSYSTEMRESETPROC)(fluid_synth_t* synth); -typedef int (*FLUIDSYNTHWRITES16PROC)(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr); -typedef int (*FLUIDSYNTHNOTEONPROC)(fluid_synth_t* synth, int chan, int key, int vel); -typedef int (*FLUIDSYNTHNOTEOFFPROC)(fluid_synth_t* synth, int chan, int key); -typedef int (*FLUIDSYNTHCHANNELPRESSUREPROC)(fluid_synth_t* synth, int chan, int val); -typedef int (*FLUIDSYNTHPITCHBENDPROC)(fluid_synth_t* synth, int chan, int val); -typedef int (*FLUIDSYNTHCCPROC)(fluid_synth_t* synth, int chan, int ctrl, int val); -typedef int (*FLUIDSYNTHPROGRAMCHANGEPROC)(fluid_synth_t* synth, int chan, int program); - -typedef fluid_settings_t* (*NEWFLUIDSETTINGSPROC)(void); -typedef fluid_synth_t* (*NEWFLUIDSYNTHPROC)(fluid_settings_t* settings); -typedef void (*DELETEFLUIDSETTINGSPROC)(fluid_settings_t* settings); -typedef int (*DELETEFLUIDSYNTHPROC)(fluid_synth_t* synth); - -#define FLUID_FUNCS \ - FLUID_FUN(settings_setnum, FLUIDSETTINGSSETNUMPROC) \ - FLUID_FUN(settings_setstr, FLUIDSETTINGSSETSTRPROC) \ - FLUID_FUN(synth_sfload, FLUIDSYNTHSFLOADPROC) \ - FLUID_FUN(synth_system_reset, FLUIDSYNTHSYSTEMRESETPROC) \ - FLUID_FUN(synth_write_s16, FLUIDSYNTHWRITES16PROC) \ - FLUID_FUN(synth_noteon, FLUIDSYNTHNOTEONPROC) \ - FLUID_FUN(synth_noteoff, FLUIDSYNTHNOTEOFFPROC) \ - FLUID_FUN(synth_channel_pressure, FLUIDSYNTHCHANNELPRESSUREPROC) \ - FLUID_FUN(synth_pitch_bend, FLUIDSYNTHPITCHBENDPROC) \ - FLUID_FUN(synth_cc, FLUIDSYNTHCCPROC) \ - FLUID_FUN(synth_program_change, FLUIDSYNTHPROGRAMCHANGEPROC) - -/* Functions that don't fit into the default prefix naming scheme */ -#define FLUID_FUNCS2 \ - FLUID_FUN2(new_settings, NEWFLUIDSETTINGSPROC, new_fluid_settings) \ - FLUID_FUN2(new_synth, NEWFLUIDSYNTHPROC, new_fluid_synth) \ - FLUID_FUN2(delete_settings, DELETEFLUIDSETTINGSPROC, delete_fluid_settings) \ - FLUID_FUN2(delete_synth, DELETEFLUIDSYNTHPROC, delete_fluid_synth) - -struct FluidFunctions -{ -#define FLUID_FUN(name, type) type name; -#define FLUID_FUN2(name, type, rn) type name; - FLUID_FUNCS - FLUID_FUNCS2 -#undef FLUID_FUN -#undef FLUID_FUN2 -}; - -#define HAVE_FLUID fluid.new_synth - -extern FluidFunctions fluid; - -void initFluidFunctions(); - -#endif // FLUIDFUN_H diff --git a/src/midisource.cpp b/src/midisource.cpp deleted file mode 100644 index 3d569a7..0000000 --- a/src/midisource.cpp +++ /dev/null @@ -1,923 +0,0 @@ -/* -** midisource.cpp -** -** This file is part of mkxp. -** -** Copyright (C) 2014 Jonas Kulla -** -** mkxp 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. -** -** mkxp 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 mkxp. If not, see . -*/ - -#include "aldatasource.h" - -#include "al-util.h" -#include "exception.h" -#include "sharedstate.h" -#include "sharedmidistate.h" -#include "util.h" -#include "debugwriter.h" -#include "fluid-fun.h" - -#include - -#include -#include -#include -#include -#include - -/* Vocabulary: - * - * Tick: - * Ticks are the smallest batch of samples that fluidsynth - * allows midi state changes to take effect in, ie. if two midi - * events are fired within less than a tick, it will look as - * if they fired at the same time. Midisource therefore always - * synthesizes sample blocks which are multiples of ticks. - * One tick is 64 samples, or 32 frames with two channels. - * - * Delta: - * Deltas are the abstract time unit in which the relative - * offsets between midi events are encoded. - */ - -#define TICK_FRAMES 32 -#define BUF_TICKS (STREAM_BUF_SIZE / TICK_FRAMES) -#define DEFAULT_BPM 120 -#define MAX_CHANNELS 16 - -#define CC_CTRL_VOLUME 7 -#define CC_CTRL_EXPRESSION 11 -#define CC_CTRL_LOOP 111 - -#define CC_VAL_DEFAULT 127 - -enum MidiEventType -{ - NoteOff, - NoteOn, - ChanTouch, - PitchBend, - CC, - PC, - Tempo, - - Last -}; - -struct ChannelEvent -{ - uint8_t chan; -}; - -struct NoteEvent : ChannelEvent -{ - uint8_t key; - uint8_t vel; -}; - -struct NoteTouchEvent : ChannelEvent -{ - uint8_t val; -}; - -struct PitchBendEvent : ChannelEvent -{ - uint16_t val; -}; - -struct CCEvent : ChannelEvent -{ - uint8_t ctrl; - uint8_t val; -}; - -struct PCEvent : ChannelEvent -{ - uint8_t prog; -}; - -struct TempoEvent -{ - uint32_t bpm; -}; - -struct MidiEvent -{ - MidiEventType type; - uint32_t delta; - union - { - ChannelEvent chan; - NoteEvent note; - NoteTouchEvent chanTouch; - PitchBendEvent pitchBend; - CCEvent cc; - PCEvent pc; - TempoEvent tempo; - } e; -}; - -struct MidiReadHandler -{ - virtual void onMidiHeader(uint16_t midiType, uint16_t trackCount, uint16_t division) = 0; - virtual void onMidiTrackBegin() = 0; - virtual void onMidiEvent(const MidiEvent &e, uint32_t absDelta) = 0; -}; - -static void -badMidiFormat() -{ - throw Exception(Exception::MKXPError, "Midi: Bad format"); -} - -/* File-like interface to a read-only memory buffer */ -struct MemChunk -{ - const std::vector &data; - size_t i; - - MemChunk(const std::vector &data) - : data(data), - i(0) - {} - - uint8_t readByte() - { - if (i >= data.size()) - endOfFile(); - - return data[i++]; - } - - void readData(void *buf, size_t n) - { - if (i + n > data.size()) - endOfFile(); - - memcpy(buf, &data[i], n); - i += n; - } - - void skipData(size_t n) - { - if ((i += n) > data.size()) - endOfFile(); - } - - void endOfFile() - { - throw Exception(Exception::MKXPError, "Midi: EOF"); - } -}; - -static uint32_t -readVarNum(MemChunk &chunk) -{ - uint32_t result = 0; - uint8_t byte; - uint8_t len = 0; - - do - { - /* A variable length number can at most be made of 4 bytes */ - if (++len > 4) - badMidiFormat(); - - byte = chunk.readByte(); - result = (result << 0x7) | (byte & 0x7F); - } - while (byte & 0x80); - - return result; -} - -template -static T readBigEndian(MemChunk &chunk) -{ - T result = 0; - - for (size_t i = 0; i < sizeof(T); ++i) - result = (result << 0x8) | chunk.readByte(); - - return result; -} - -static void -readVoiceEvent(MidiEvent &e, MemChunk &chunk, - uint8_t type, uint8_t data1, bool &handled) -{ - e.e.chan.chan = (type & 0x0F); - - uint8_t tmp; - - switch (type >> 4) - { - case 0x8 : - e.type = NoteOff; - e.e.note.key = data1; - chunk.readByte(); /* We don't care about velocity */ - break; - - case 0x9 : - e.type = NoteOn; - e.e.note.key = data1; - e.e.note.vel = chunk.readByte(); - break; - - case 0xA : - /* Note aftertouch unhandled */ - handled = false; - break; - - case 0xB : - e.type = CC; - e.e.cc.ctrl = data1; - e.e.cc.val = chunk.readByte(); - break; - - case 0xC : - e.type = PC; - e.e.pc.prog = data1; - break; - - case 0xD : - e.type = ChanTouch; - e.e.chanTouch.val = data1; - break; - - case 0xE : - e.type = PitchBend; - tmp = chunk.readByte(); - e.e.pitchBend.val = ((tmp & 0x7F) << 7) | (data1 & 0x7F); - break; - - default : - assert(!"unreachable"); - break; - } -} - -static void -readEvent(MidiReadHandler *handler, MemChunk &chunk, - uint8_t &prevType, uint32_t &deltaBase, uint32_t &deltaCarry, - bool &endOfTrack) -{ - MidiEvent e; - bool handled = true; - - e.delta = readVarNum(chunk); - - uint8_t type = chunk.readByte(); - - /* Check for running status */ - if (!(type & 0x80)) - { - /* Running status for meta/system events is not allowed */ - if (prevType == 0) - badMidiFormat(); - - /* Running status voice event: 'type' becomes - * first data byte instead */ - readVoiceEvent(e, chunk, prevType, type, handled); - goto event_read; - } - - if ((type >> 4) != 0xF) - { - /* Normal voice event */ - readVoiceEvent(e, chunk, type, chunk.readByte(), handled); - prevType = type; - } - else if (type == 0xFF) - { - /* Meta event */ - uint8_t metaType = chunk.readByte(); - uint32_t len = readVarNum(chunk); - - if (metaType == 0x51) - { - /* Tempo event */ - if (len != 3) - badMidiFormat(); - - uint8_t data[3]; - chunk.readData(data, 3); - - uint32_t mpqn = (data[0] << 0x10) - | (data[1] << 0x08) - | (data[2] << 0x00); - - e.type = Tempo; - e.e.tempo.bpm = 60000000 / mpqn; - } - else if (metaType == 0x2F) - { - /* End-of-track event */ - if (len != 0) - badMidiFormat(); - - endOfTrack = true; - handled = false; - } - else - { - handled = false; - } - - if (!handled) - chunk.skipData(len); - - prevType = 0; - } - else if (type == 0xF0 || type == 0xF7) - { - /* SysEx event */ - uint32_t len = readVarNum(chunk); - handled = false; - chunk.skipData(len); - - prevType = 0; - } - else - { - badMidiFormat(); - } - -event_read: - - if (handled) - { - e.delta += deltaCarry; - deltaCarry = 0; - deltaBase += e.delta; - - handler->onMidiEvent(e, deltaBase); - } - else - { - deltaCarry += e.delta; - } -} - -void readMidiTrack(MidiReadHandler *handler, MemChunk &chunk) -{ - /* Track signature */ - char sig[5] = { 0 }; - chunk.readData(sig, 4); - if (strcmp(sig, "MTrk")) - badMidiFormat(); - - handler->onMidiTrackBegin(); - - uint32_t trackLen = readBigEndian(chunk); - - /* The combined delta of all events on this track so far */ - uint32_t deltaBase = 0; - - /* If we don't care about an event, instead of inserting a - * useless event, we carry over its delta into the next one */ - uint32_t deltaCarry = 0; - - /* Holds the previous status byte for voice event, in case - * we encounter a running status */ - uint8_t prevType = 0; - - /* The 'EndOfTrack' meta event will toggle this flag on */ - bool endOfTrack = false; - - size_t savedPos = chunk.i; - - /* Read all events */ - while (!endOfTrack) - readEvent(handler, chunk, prevType, deltaBase, deltaCarry, endOfTrack); - - /* Check that the track byte length from the header - * matches the amount we actually ended up reading */ - if ((chunk.i - savedPos) != trackLen) - badMidiFormat(); -} - -void readMidi(MidiReadHandler *handler, const std::vector &data) -{ - MemChunk chunk(data); - - /* Midi signature */ - char sig[5] = { 0 }; - chunk.readData(sig, 4); - if (strcmp(sig, "MThd")) - badMidiFormat(); - - /* Header length must always be 6 */ - uint32_t hdrLen = readBigEndian(chunk); - if (hdrLen != 6) - badMidiFormat(); - - /* Only types 0, 1, 2 exist */ - uint16_t type = readBigEndian(chunk); - if (type > 2) - badMidiFormat(); - - /* Type 0 only contains one track */ - uint16_t trackCount = readBigEndian(chunk); - if (trackCount == 0) - badMidiFormat(); - if (type == 0 && trackCount > 1) - badMidiFormat(); - - uint16_t timeDiv = readBigEndian(chunk); - - handler->onMidiHeader(type, trackCount, timeDiv); - - /* Read tracks */ - for (uint16_t i = 0; i < trackCount; ++i) - readMidiTrack(handler, chunk); -} - -struct Track -{ - std::vector events; - - /* Combined deltas of all events */ - uint64_t length; - - /* Event index that is resumed from after loop wraparound */ - int32_t loopI; - - /* Difference between last event's position and length of - * the longest overall track / song length */ - uint32_t loopOffsetEnd; - - /* Delta offset from the loop beginning to event[loopI] */ - uint32_t loopOffsetStart; - - bool valid; - MidiEvent event; - int32_t remDeltas; - - uint32_t index; - bool wrapAroundFlag; - bool atEnd; - - Track() - : length(0), - loopI(-1), - loopOffsetEnd(0), - loopOffsetStart(0), - valid(false), - index(0), - wrapAroundFlag(false), - atEnd(false) - {} - - void appendEvent(const MidiEvent &e) - { - length += e.delta; - events.push_back(e); - } - - void scheduleEvent(bool looped) - { - if (valid) - return; - - /* At end and not looped? */ - if (index == events.size() && ((!looped || loopI < 0) || length == 0)) - { - valid = false; - atEnd = true; - return; - } - - MidiEvent &e = events[index++]; - - event = e; - remDeltas = e.delta; - valid = true; - - if (wrapAroundFlag) - { - remDeltas = loopOffsetEnd + loopOffsetStart; - wrapAroundFlag = false; - } - - if (looped && (index == events.size()) && loopI >= 0) - { - index = loopI; - wrapAroundFlag = true; - } - } - - void reset() - { - valid = false; - index = 0; - wrapAroundFlag = false; - atEnd = false; - } -}; - -/* Some songs use CC events for effects like fade-out, - * slowly decreasing a channel's volume to 0. The problem is that - * for looped songs, events are continuously fed into the synth - * without restoring those controls back to their default value. - * We can't reset them at the very beginning of tracks because it - * might cause audible interactions with notes which are still decaying - * past the end of the song. Therefore, we insert a fake CC event right - * after the first NoteOn event for each channel which resets the - * control to its default state while avoiding audible glitches. - * If there is already a CC event for this control before the first - * NoteOn event, we don't clobber it by not inserting our fake event. */ -template -struct CCResetter -{ - bool chanHandled[MAX_CHANNELS]; - - CCResetter() - { - memset(&chanHandled, 0, sizeof(chanHandled)); - } - - void handleEvent(const MidiEvent &e, Track &track) - { - if (e.type != NoteOn && e.type != CC) - return; - - uint8_t chan = e.e.chan.chan; - - if (chanHandled[chan]) - return; - - if (e.type == CC && e.e.cc.ctrl == ctrl) - { - /* Don't clobber the existing CC value */ - chanHandled[chan] = true; - return; - } - else if (e.type == NoteOn) - { - chanHandled[chan] = true; - - MidiEvent re; - re.delta = 0; - re.type = CC; - re.e.cc.chan = chan; - re.e.cc.ctrl = ctrl; - re.e.cc.val = CC_VAL_DEFAULT; - - track.appendEvent(re); - } - } -}; - -struct MidiSource : ALDataSource, MidiReadHandler -{ - const uint16_t freq; - fluid_synth_t *synth; - - int16_t synthBuf[BUF_TICKS*TICK_FRAMES*2]; - - std::vector tracks; - CCResetter volReset; - CCResetter expReset; - - /* Index of longest track */ - uint8_t longestI; - - bool looped; - - /* Absolute delta at which we received the LOOP_MARKER CC event */ - uint32_t loopDelta; - - /* Deltas per beat */ - uint16_t dpb; - - int8_t pitchShift; - - /* Deltas per tick */ - float playbackSpeed; - - float genDeltasCarry; - - /* MidiReadHandler (track that's currently being read) */ - int16_t curTrack; - - MidiSource(SDL_RWops &ops, - bool looped) - : freq(SYNTH_SAMPLERATE), - looped(looped), - dpb(480), - pitchShift(0), - genDeltasCarry(0), - curTrack(-1) - { - size_t dataLen = SDL_RWsize(&ops); - std::vector data(dataLen); - - if (SDL_RWread(&ops, &data[0], 1, dataLen) < dataLen) - { - SDL_RWclose(&ops); - throw Exception(Exception::MKXPError, "Reading midi data failed"); - } - - try - { - readMidi(this, data); - } - catch (const Exception &) - { - SDL_RWclose(&ops); - throw; - } - - synth = shState->midiState().allocateSynth(); - - uint64_t longest = 0; - - for (size_t i = 0; i < tracks.size(); ++i) - if (tracks[i].length > longest) - { - longest = tracks[i].length; - longestI = i; - } - - for (size_t i = 0; i < tracks.size(); ++i) - tracks[i].loopOffsetEnd = longest - tracks[i].length; - - /* Enterbrain likes to be funny and put loop markers at - * the very end of ME tracks */ - if (loopDelta >= longest) - loopDelta = 0; - - for (size_t i = 0; i < tracks.size(); ++i) - { - Track &track = tracks[i]; - - int64_t base = 0; - for (size_t j = 0; j < track.events.size(); ++j) - { - MidiEvent &e = track.events[j]; - base += e.delta; - - if (base < loopDelta) - continue; - - /* Don't make the last event loop eternally */ - if (j < track.events.size()) - { - track.loopI = j; - track.loopOffsetStart = base - loopDelta; - } - - break; - } - } - - updatePlaybackSpeed(DEFAULT_BPM); - - // FIXME: It would make the code in 'fillBuffer' a lot nicer if - // we could combine all tracks into one giant one on construction, - // instead of having to constantly iterate through all of them - } - - ~MidiSource() - { - shState->midiState().releaseSynth(synth); - } - - - void updatePlaybackSpeed(uint32_t bpm) - { - float deltaLength = 60.0f / (dpb * bpm); - playbackSpeed = TICK_FRAMES / (deltaLength * freq); - } - - void activateEvent(const MidiEvent &e) - { - int16_t key = e.e.note.key; - - /* Apply pitch shift if necessary */ - if ((e.type == NoteOn || e.type == NoteOff) && e.e.chan.chan != 9) - { - key += pitchShift; - - /* Drop events whose keys are out of bounds */ - if (key < 0 || key > 127) - return; - } - - switch (e.type) - { - case NoteOn: - fluid.synth_noteon(synth, e.e.note.chan, key, e.e.note.vel); - break; - case NoteOff: - fluid.synth_noteoff(synth, e.e.note.chan, key); - break; - case ChanTouch: - fluid.synth_channel_pressure(synth, e.e.chanTouch.chan, e.e.chanTouch.val); - break; - case PitchBend: - fluid.synth_pitch_bend(synth, e.e.pitchBend.chan, e.e.pitchBend.val); - break; - case CC: - fluid.synth_cc(synth, e.e.cc.chan, e.e.cc.ctrl, e.e.cc.val); - break; - case PC: - fluid.synth_program_change(synth, e.e.pc.chan, e.e.pc.prog); - break; - case Tempo: - updatePlaybackSpeed(e.e.tempo.bpm); - break; - default: - break; - } - } - - void renderTicks(size_t count, size_t offset) - { - size_t bufOffset = offset * TICK_FRAMES * 2; - int len = count * TICK_FRAMES; - void *buffer = &synthBuf[bufOffset]; - - fluid.synth_write_s16(synth, len, buffer, 0, 2, buffer, 1, 2); - } - - /* MidiReadHandler */ - void onMidiHeader(uint16_t midiType, uint16_t trackCount, uint16_t division) - { - if (midiType != 0 && midiType != 1) - throw Exception(Exception::MKXPError, "Midi: Type 2 not supported"); - - tracks.resize(trackCount); - - // SMTP unhandled - if (division & 0x8000) - throw Exception(Exception::MKXPError, "Midi: SMTP parameters not supported"); - else - dpb = division; - } - - void onMidiTrackBegin() - { - ++curTrack; - } - - void onMidiEvent(const MidiEvent &e, uint32_t absDelta) - { - assert(curTrack >= 0 && curTrack < (int16_t) tracks.size()); - - Track &track = tracks[curTrack]; - - track.appendEvent(e); - volReset.handleEvent(e, track); - expReset.handleEvent(e, track); - - if (e.type == CC && e.e.cc.ctrl == CC_CTRL_LOOP) - loopDelta = absDelta; - } - - /* ALDataSource */ - Status fillBuffer(AL::Buffer::ID buf) - { - /* In case there is no currently scheduled one */ - for (size_t i = 0; i < tracks.size(); ++i) - tracks[i].scheduleEvent(looped); - - size_t remTicks = BUF_TICKS; - - /* Iterate until all ticks that fit into the buffer - * have been rendered */ - while (remTicks > 0) - { - /* Check for events that have to be activated now, activate them, - * and schedule new ones if the queue isn't empty */ - for (size_t i = 0; i < tracks.size(); ++i) - { - Track &track = tracks[i]; - - /* We have to loop and ensure that the final scheduled - * event lies in the future, as multiple events might - * have to be activated at once */ - while (true) - { - if (!track.valid || track.remDeltas > 0) - break; - - int32_t prevOffset = track.remDeltas; - - activateEvent(track.event); - - track.valid = false; - track.scheduleEvent(looped); - - /* Negative deltas from the previous event have to - * be carried over into the next to stay in sync */ - if (prevOffset < 0) - track.remDeltas += prevOffset; - - /* Ensure it lies in the future */ - if (track.remDeltas > 0) - break; - } - } - - size_t nextEvent = (size_t) -1; - bool allInvalid = true; - - /* Search all tracks for the temporally nearest event */ - for (size_t i = 0; i < tracks.size(); ++i) - { - Track &track = tracks[i]; - - if (!track.valid) - continue; - - allInvalid = false; - - uint32_t remDelta = track.remDeltas / playbackSpeed; - - /* We need to render at least one tick regardless to - * avoid an endless loop of waiting for the next event - * to become current */ - if (remDelta < nextEvent) - nextEvent = std::max(remDelta, 1); - } - - /* Calculate amount of ticks we'll render next */ - size_t genTicks = allInvalid ? remTicks : std::min(remTicks, nextEvent); - - if (genTicks == 0) - continue; - - renderTicks(genTicks, BUF_TICKS - remTicks); - remTicks -= genTicks; - - float genDeltas = (genTicks * playbackSpeed) + genDeltasCarry; - - float intDeltas; - genDeltasCarry = modff(genDeltas, &intDeltas); - - /* Substract integer part of consumed deltas while carrying - * over the fractional amount into the next iteration */ - for (size_t i = 0; i < tracks.size(); ++i) - if (tracks[i].valid) - tracks[i].remDeltas -= intDeltas; - } - - /* Fill AL buffer */ - AL::Buffer::uploadData(buf, AL_FORMAT_STEREO16, synthBuf, sizeof(synthBuf), freq); - - if (tracks[longestI].atEnd) - return EndOfStream; - - return NoError; - } - - int sampleRate() - { - return freq; - } - - /* Midi sources cannot seek, and so always reset to beginning */ - void seekToOffset(float) - { - /* Reset synth */ - fluid.synth_system_reset(synth); - - /* Reset runtime variables */ - genDeltasCarry = 0; - updatePlaybackSpeed(DEFAULT_BPM); - - /* Reset tracks */ - for (size_t i = 0; i < tracks.size(); ++i) - tracks[i].reset(); - } - - uint32_t loopStartFrames() { return 0; } - - bool setPitch(float value) - { - // not completely correct, but close - pitchShift = round((value > 1.0f ? 14 : 24) * (value - 1.0f)); - - return true; - } -}; - -ALDataSource *createMidiSource(SDL_RWops &ops, - bool looped) -{ - return new MidiSource(ops, looped); -} diff --git a/src/sharedmidistate.h b/src/sharedmidistate.h deleted file mode 100644 index 1692848..0000000 --- a/src/sharedmidistate.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -** sharedmidistate.h -** -** This file is part of mkxp. -** -** Copyright (C) 2014 Jonas Kulla -** -** mkxp 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. -** -** mkxp 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 mkxp. If not, see . -*/ - -#ifndef SHAREDMIDISTATE_H -#define SHAREDMIDISTATE_H - -#include "config.h" -#include "debugwriter.h" -#include "fluid-fun.h" - -#include -#include -#include - -#define SYNTH_INIT_COUNT 2 -#define SYNTH_SAMPLERATE 44100 - -struct Synth -{ - fluid_synth_t *synth; - bool inUse; -}; - -struct SharedMidiState -{ - bool inited; - std::vector synths; - const std::string &soundFont; - fluid_settings_t *flSettings; - - SharedMidiState(const Config &conf) - : inited(false), - soundFont(conf.midi.soundFont) - {} - - ~SharedMidiState() - { - /* We might have initialized, but if the consecutive libfluidsynth - * load failed, no resources will have been allocated */ - if (!inited || !HAVE_FLUID) - return; - - fluid.delete_settings(flSettings); - - for (size_t i = 0; i < synths.size(); ++i) - { - assert(!synths[i].inUse); - fluid.delete_synth(synths[i].synth); - } - } - - void initIfNeeded(const Config &conf) - { - if (inited) - return; - - inited = true; - - initFluidFunctions(); - - if (!HAVE_FLUID) - return; - - flSettings = fluid.new_settings(); - fluid.settings_setnum(flSettings, "synth.gain", 1.0f); - fluid.settings_setnum(flSettings, "synth.sample-rate", SYNTH_SAMPLERATE); - fluid.settings_setstr(flSettings, "synth.chorus.active", conf.midi.chorus ? "yes" : "no"); - fluid.settings_setstr(flSettings, "synth.reverb.active", conf.midi.reverb ? "yes" : "no"); - - for (size_t i = 0; i < SYNTH_INIT_COUNT; ++i) - addSynth(false); - } - - fluid_synth_t *allocateSynth() - { - assert(HAVE_FLUID); - assert(inited); - - size_t i; - - for (i = 0; i < synths.size(); ++i) - if (!synths[i].inUse) - break; - - if (i < synths.size()) - { - fluid_synth_t *syn = synths[i].synth; - fluid.synth_system_reset(syn); - synths[i].inUse = true; - - return syn; - } - else - { - return addSynth(true); - } - } - - void releaseSynth(fluid_synth_t *synth) - { - size_t i; - - for (i = 0; i < synths.size(); ++i) - if (synths[i].synth == synth) - break; - - assert(i < synths.size()); - - synths[i].inUse = false; - } - -private: - fluid_synth_t *addSynth(bool usedNow) - { - fluid_synth_t *syn = fluid.new_synth(flSettings); - - if (!soundFont.empty()) - fluid.synth_sfload(syn, soundFont.c_str(), 1); - else - Debug() << "Warning: No soundfont specified, sound might be mute"; - - Synth synth; - synth.inUse = usedNow; - synth.synth = syn; - synths.push_back(synth); - - return syn; - } -}; - -#endif // SHAREDMIDISTATE_H diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index c0193dc..ddee1f7 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -40,7 +40,6 @@ #include "quad.h" #include "binding.h" #include "exception.h" -#include "sharedmidistate.h" #include #include @@ -75,8 +74,6 @@ struct SharedStatePrivate RGSSThreadData &rtData; Config &config; - SharedMidiState midiState; - Graphics graphics; Input input; Audio audio; @@ -114,7 +111,6 @@ struct SharedStatePrivate eThread(*threadData->ethread), rtData(*threadData), config(threadData->config), - midiState(threadData->config), graphics(threadData), input(*threadData), audio(*threadData), @@ -157,11 +153,6 @@ struct SharedStatePrivate /* Reuse starting values */ TEXFBO::allocEmpty(gpTexFBO, globalTexW, globalTexH); TEXFBO::linkFBO(gpTexFBO); - - /* RGSS3 games will call setup_midi, so there's - * no need to do it on startup */ - if (rgssVer <= 2) - midiState.initIfNeeded(threadData->config); } ~SharedStatePrivate() @@ -243,7 +234,6 @@ GSATT(ShaderSet&, shaders) GSATT(TexPool&, texPool) GSATT(Quad&, gpQuad) GSATT(SharedFontState&, fontState) -GSATT(SharedMidiState&, midiState) void SharedState::setBindingData(void *data) { diff --git a/src/sharedstate.h b/src/sharedstate.h index fb128a3..62f4c35 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -89,8 +89,6 @@ struct SharedState SharedFontState &fontState() const; Font &defaultFont() const; - SharedMidiState &midiState() const; - sigc::signal prepareDraw; unsigned int genTimeStamp();