MidiSource: Fix some channels being mute after looping
This commit is contained in:
		
							parent
							
								
									64a3ac3769
								
							
						
					
					
						commit
						e339964076
					
				
					 2 changed files with 71 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -87,16 +87,12 @@ The syntax is: `--<option>=<value>`
 | 
			
		|||
 | 
			
		||||
Example: `./mkxp --gameFolder="my game" --vsync=true --fixedFramerate=60`
 | 
			
		||||
 | 
			
		||||
## Midi music (*ALPHA STATUS*)
 | 
			
		||||
## Midi music
 | 
			
		||||
 | 
			
		||||
mkxp doesn't come with a soundfont by default, so you will have to supply it yourself (set its path in the config). Playback has been tested and should work reasonably well with all RTP assets.
 | 
			
		||||
 | 
			
		||||
You can use this public domain soundfont: [GMGSx.sf2](https://www.dropbox.com/s/qxdvoxxcexsvn43/GMGSx.sf2?dl=0)
 | 
			
		||||
 | 
			
		||||
Known issues with midi playback:
 | 
			
		||||
 | 
			
		||||
* Some songs' instruments become mute after looping
 | 
			
		||||
 | 
			
		||||
## Fonts
 | 
			
		||||
 | 
			
		||||
In the RMXP version of RGSS, fonts are loaded directly from system specific search paths (meaning they must be installed to be available to games). Because this whole thing is a giant platform-dependent headache, I decided to implement the behavior Enterbrain thankfully added in VX Ace: loading fonts will automatically search a folder called "Fonts", which obeys the default searchpath behavior (ie. it can be located directly in the game folder, or an RTP).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,13 @@
 | 
			
		|||
#define TICK_FRAMES 32
 | 
			
		||||
#define BUF_TICKS (STREAM_BUF_SIZE / TICK_FRAMES)
 | 
			
		||||
#define DEFAULT_BPM 120
 | 
			
		||||
#define LOOP_MARKER 111
 | 
			
		||||
#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
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -522,6 +528,59 @@ struct Track
 | 
			
		|||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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<uint8_t ctrl>
 | 
			
		||||
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;
 | 
			
		||||
| 
						 | 
				
			
			@ -530,6 +589,8 @@ struct MidiSource : ALDataSource, MidiReadHandler
 | 
			
		|||
	int16_t synthBuf[BUF_TICKS*TICK_FRAMES*2];
 | 
			
		||||
 | 
			
		||||
	std::vector<Track> tracks;
 | 
			
		||||
	CCResetter<CC_CTRL_VOLUME>     volReset;
 | 
			
		||||
	CCResetter<CC_CTRL_EXPRESSION> expReset;
 | 
			
		||||
 | 
			
		||||
	/* Index of longest track */
 | 
			
		||||
	uint8_t longestI;
 | 
			
		||||
| 
						 | 
				
			
			@ -631,7 +692,7 @@ struct MidiSource : ALDataSource, MidiReadHandler
 | 
			
		|||
		playbackSpeed = TICK_FRAMES / (deltaLength * freq);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void activateEvent(MidiEvent &e)
 | 
			
		||||
	void activateEvent(const MidiEvent &e)
 | 
			
		||||
	{
 | 
			
		||||
		int16_t key = e.e.note.key;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -705,9 +766,14 @@ struct MidiSource : ALDataSource, MidiReadHandler
 | 
			
		|||
	void onMidiEvent(const MidiEvent &e, uint32_t absDelta)
 | 
			
		||||
	{
 | 
			
		||||
		assert(curTrack >= 0 && curTrack < (int16_t) tracks.size());
 | 
			
		||||
		tracks[curTrack].appendEvent(e);
 | 
			
		||||
 | 
			
		||||
		if (e.type == CC && e.e.cc.ctrl == LOOP_MARKER)
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue