diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm
index 37a9ba41f..3a7b6e4ef 100644
--- a/Plugins/MIDI/MIDI/AUPlayer.mm
+++ b/Plugins/MIDI/MIDI/AUPlayer.mm
@@ -205,7 +205,7 @@ void AUPlayer::setComponent(OSType uSubType, OSType uManufacturer) {
 
 void AUPlayer::setSoundFont(const char *in) {
 	const char *ext = strrchr(in, '.');
-	if(*ext && ((strncasecmp(ext + 1, "sf2", 3) == 0) || (strncasecmp(ext + 1, "dls", 3) == 0))) {
+	if(ext && *ext && ((strncasecmp(ext + 1, "sf2", 3) == 0) || (strncasecmp(ext + 1, "dls", 3) == 0))) {
 		sSoundFontName = in;
 		shutdown();
 	}
diff --git a/Plugins/MIDI/MIDI/BMPlayer.cpp b/Plugins/MIDI/MIDI/BMPlayer.cpp
index a3bd26527..49f7ad2b0 100644
--- a/Plugins/MIDI/MIDI/BMPlayer.cpp
+++ b/Plugins/MIDI/MIDI/BMPlayer.cpp
@@ -256,7 +256,7 @@ static class Bass_Initializer {
 
 BMPlayer::BMPlayer()
 : MIDIPlayer() {
-	memset(_stream, 0, sizeof(_stream));
+	_stream = NULL;
 	bSincInterpolation = false;
 	_presetList = 0;
 
@@ -279,39 +279,19 @@ void BMPlayer::send_event(uint32_t b) {
 	event[1] = static_cast<uint8_t>(b >> 8);
 	event[2] = static_cast<uint8_t>(b >> 16);
 	unsigned port = (b >> 24) & 0x7F;
-	const unsigned channel = b & 0x0F;
+	if(port > 2) port = 0;
+	const unsigned channel = (b & 0x0F) + port * 16;
 	const unsigned command = b & 0xF0;
 	const unsigned event_length = (command >= 0xF8 && command <= 0xFF) ? 1 : ((command == 0xC0 || command == 0xD0) ? 2 : 3);
-	if(port > 2) port = 0;
-	if(bank_lsb_overridden && command == 0xB0 && event[1] == 0x20) return;
-	BASS_MIDI_StreamEvents(_stream[port], BASS_MIDI_EVENTS_RAW + 1 + channel, event, event_length);
+	BASS_MIDI_StreamEvents(_stream, BASS_MIDI_EVENTS_RAW + 1 + channel, event, event_length);
 }
 
 void BMPlayer::send_sysex(const uint8_t *data, size_t size, size_t port) {
-	if(port > 2) port = 0;
-	BASS_MIDI_StreamEvents(_stream[port], BASS_MIDI_EVENTS_RAW, data, static_cast<unsigned int>(size));
-	if(port == 0) {
-		BASS_MIDI_StreamEvents(_stream[1], BASS_MIDI_EVENTS_RAW, data, static_cast<unsigned int>(size));
-		BASS_MIDI_StreamEvents(_stream[2], BASS_MIDI_EVENTS_RAW, data, static_cast<unsigned int>(size));
-	}
+	BASS_MIDI_StreamEvents(_stream, BASS_MIDI_EVENTS_RAW, data, static_cast<unsigned int>(size));
 }
 
 void BMPlayer::render(float *out, unsigned long count) {
-	float buffer[1024];
-	while(count) {
-		unsigned long todo = count;
-		if(todo > 512)
-			todo = 512;
-		memset(out, 0, todo * sizeof(float) * 2);
-		for(unsigned long i = 0; i < 3; ++i) {
-			BASS_ChannelGetData(_stream[i], buffer, BASS_DATA_FLOAT | (unsigned int)(todo * sizeof(float) * 2));
-			for(unsigned long j = 0; j < todo * 2; ++j) {
-				out[j] += buffer[j];
-			}
-		}
-		out += todo * 2;
-		count -= todo;
-	}
+	BASS_ChannelGetData(_stream, out, BASS_DATA_FLOAT | (unsigned int)(count * sizeof(float) * 2));
 }
 
 void BMPlayer::setSoundFont(const char *in) {
@@ -325,10 +305,8 @@ void BMPlayer::setFileSoundFont(const char *in) {
 }
 
 void BMPlayer::shutdown() {
-	if(_stream[2]) BASS_StreamFree(_stream[2]);
-	if(_stream[1]) BASS_StreamFree(_stream[1]);
-	if(_stream[0]) BASS_StreamFree(_stream[0]);
-	memset(_stream, 0, sizeof(_stream));
+	if(_stream) BASS_StreamFree(_stream);
+	_stream = NULL;
 	for(unsigned long i = 0; i < _soundFonts.size(); ++i) {
 		cache_close_font(_soundFonts[i]);
 	}
@@ -339,37 +317,17 @@ void BMPlayer::shutdown() {
 	}
 }
 
-void BMPlayer::compound_presets(std::vector<BASS_MIDI_FONTEX> &out, std::vector<BASS_MIDI_FONTEX> &in, std::vector<long> &channels) {
-	if(!in.size())
-		in.push_back({ 0, -1, -1, -1, 0, 0 });
-	if(channels.size()) {
-		for(auto pit = in.begin(); pit != in.end(); ++pit) {
-			for(auto it = channels.begin(); it != channels.end(); ++it) {
-				bank_lsb_override[*it - 1] = *it;
-
-				int dbanklsb = (int)*it;
-				pit->dbanklsb = dbanklsb;
-				out.push_back(*pit);
-			}
-		}
-	} else {
-		for(auto pit = in.begin(); pit != in.end(); ++pit) {
-			out.push_back(*pit);
-		}
-	}
-}
-
 bool BMPlayer::startup() {
-	if(_stream[0] && _stream[1] && _stream[2]) return true;
+	if(_stream) return true;
 
-	_stream[0] = BASS_MIDI_StreamCreate(16, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | (bSincInterpolation ? BASS_MIDI_SINCINTER : 0), (unsigned int)uSampleRate);
-	_stream[1] = BASS_MIDI_StreamCreate(16, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | (bSincInterpolation ? BASS_MIDI_SINCINTER : 0), (unsigned int)uSampleRate);
-	_stream[2] = BASS_MIDI_StreamCreate(16, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | (bSincInterpolation ? BASS_MIDI_SINCINTER : 0), (unsigned int)uSampleRate);
-	if(!_stream[0] || !_stream[1] || !_stream[2]) {
+	_stream = BASS_MIDI_StreamCreate(48, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | (bSincInterpolation ? BASS_MIDI_SINCINTER : 0), (unsigned int)uSampleRate);
+	if(!_stream) {
 		return false;
 	}
-	memset(bank_lsb_override, 0, sizeof(bank_lsb_override));
-	std::vector<BASS_MIDI_FONTEX> presetList;
+	BASS_MIDI_StreamEvent(_stream, 9, MIDI_EVENT_DEFDRUMS, 1);
+	BASS_MIDI_StreamEvent(_stream, 9 + 16, MIDI_EVENT_DEFDRUMS, 1);
+	BASS_MIDI_StreamEvent(_stream, 9 + 32, MIDI_EVENT_DEFDRUMS, 1);
+	std::vector<BASS_MIDI_FONTEX2> presetList;
 	if(sFileSoundFontName.length()) {
 		HSOUNDFONT font = cache_open_font(sFileSoundFontName.c_str());
 		if(!font) {
@@ -377,14 +335,14 @@ bool BMPlayer::startup() {
 			return false;
 		}
 		_soundFonts.push_back(font);
-		presetList.push_back({ font, -1, -1, -1, 0, 0 });
+		presetList.push_back({ font, -1, -1, -1, 0, 0, 0, 48 });
 	}
 
 	if(sSoundFontName.length()) {
 		std::string ext;
 		size_t dot = sSoundFontName.find_last_of('.');
 		if(dot != std::string::npos) ext.assign(sSoundFontName.begin() + dot + 1, sSoundFontName.end());
-		if(!strcasecmp(ext.c_str(), "sf2")
+		if(!strcasecmp(ext.c_str(), "sf2") || !strcasecmp(ext.c_str(), "sf3")
 #ifdef SF2PACK
 		   || !strcasecmp(ext.c_str(), "sf2pack")
 #endif
@@ -395,7 +353,7 @@ bool BMPlayer::startup() {
 				return false;
 			}
 			_soundFonts.push_back(font);
-			presetList.push_back({ font, -1, -1, -1, 0, 0 });
+			presetList.push_back({ font, -1, -1, -1, 0, 0, 0, 48 });
 		} else if(!strcasecmp(ext.c_str(), "sflist") || !strcasecmp(ext.c_str(), "json")) {
 			_presetList = cache_open_list(sSoundFontName.c_str());
 			if(!_presetList) {
@@ -408,20 +366,7 @@ bool BMPlayer::startup() {
 		}
 	}
 
-	BASS_MIDI_StreamSetFonts(_stream[0], &presetList[0], (unsigned int)presetList.size() | BASS_MIDI_FONT_EX);
-	BASS_MIDI_StreamSetFonts(_stream[1], &presetList[0], (unsigned int)presetList.size() | BASS_MIDI_FONT_EX);
-	BASS_MIDI_StreamSetFonts(_stream[2], &presetList[0], (unsigned int)presetList.size() | BASS_MIDI_FONT_EX);
-
-	reset_parameters();
+	BASS_MIDI_StreamSetFonts(_stream, &presetList[0], (unsigned int)presetList.size() | BASS_MIDI_FONT_EX2);
 
 	return true;
 }
-
-void BMPlayer::reset_parameters() {
-	bank_lsb_overridden = false;
-	for(unsigned int i = 0; i < 48; ++i) {
-		if(bank_lsb_override[i])
-			bank_lsb_overridden = true;
-		BASS_MIDI_StreamEvent(_stream[i / 16], i % 16, MIDI_EVENT_BANK_LSB, bank_lsb_override[i]);
-	}
-}
diff --git a/Plugins/MIDI/MIDI/BMPlayer.h b/Plugins/MIDI/MIDI/BMPlayer.h
index 81be0608e..3c6e62ae3 100644
--- a/Plugins/MIDI/MIDI/BMPlayer.h
+++ b/Plugins/MIDI/MIDI/BMPlayer.h
@@ -28,8 +28,6 @@ class BMPlayer : public MIDIPlayer {
 	virtual void shutdown();
 	virtual bool startup();
 
-	void compound_presets(std::vector<BASS_MIDI_FONTEX>& out, std::vector<BASS_MIDI_FONTEX>& in, std::vector<long>& channels);
-
 	void reset_parameters();
 
 	std::vector<HSOUNDFONT> _soundFonts;
@@ -37,12 +35,9 @@ class BMPlayer : public MIDIPlayer {
 	std::string sSoundFontName;
 	std::string sFileSoundFontName;
 
-	HSTREAM _stream[3];
+	HSTREAM _stream;
 
 	bool bSincInterpolation;
-
-	bool bank_lsb_overridden;
-	uint8_t bank_lsb_override[48];
 };
 
 #endif
diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm
index 660d5f9bb..28c270e63 100644
--- a/Plugins/MIDI/MIDI/MIDIDecoder.mm
+++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm
@@ -120,7 +120,7 @@ static OSType getOSType(const char *in_) {
 
 	if([[source url] isFileURL]) {
 		// Let's check for a SoundFont
-		NSArray *extensions = @[@"sflist", @"sf2pack", @"sf2"];
+		NSArray *extensions = @[@"sflist", @"sf2pack", @"sf2", @"sf3"];
 		NSString *filePath = [[source url] path];
 		NSString *fileNameBase = [filePath lastPathComponent];
 		filePath = [filePath stringByDeletingLastPathComponent];
diff --git a/Preferences/Preferences/MIDIPane.m b/Preferences/Preferences/MIDIPane.m
index f6ef876dd..40bfaa55a 100644
--- a/Preferences/Preferences/MIDIPane.m
+++ b/Preferences/Preferences/MIDIPane.m
@@ -25,7 +25,7 @@
 }
 
 - (IBAction)setSoundFont:(id)sender {
-	NSArray *fileTypes = @[@"sf2", @"sf2pack", @"sflist"];
+	NSArray *fileTypes = @[@"sf2", @"sf2pack", @"sflist", @"sf3"];
 	NSOpenPanel *panel = [NSOpenPanel openPanel];
 	[panel setAllowsMultipleSelection:NO];
 	[panel setCanChooseDirectories:NO];
diff --git a/ThirdParty/BASS/bassmidi.h b/ThirdParty/BASS/bassmidi.h
index dcfecd460..fce6e6d47 100644
--- a/ThirdParty/BASS/bassmidi.h
+++ b/ThirdParty/BASS/bassmidi.h
@@ -1,6 +1,6 @@
 /*
 	BASSMIDI 2.4 C/C++ header file
-	Copyright (c) 2006-2020 Un4seen Developments Ltd.
+	Copyright (c) 2006-2022 Un4seen Developments Ltd.
 
 	See the BASSMIDI.CHM file for more detailed documentation
 */
@@ -14,6 +14,11 @@
 #error conflicting BASS and BASSMIDI versions
 #endif
 
+#ifdef __OBJC__
+typedef int BOOL32;
+#define BOOL BOOL32 // override objc's BOOL
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -35,10 +40,11 @@ typedef DWORD HSOUNDFONT;	// soundfont handle
 #define BASS_CONFIG_MIDI_SAMPLETHREADS 0x10406
 #define BASS_CONFIG_MIDI_SAMPLEMEM	0x10407
 #define BASS_CONFIG_MIDI_SAMPLEREAD	0x10408
+#define BASS_CONFIG_MIDI_SAMPLELOADING	0x1040a
 
 // Additional BASS_SetConfigPtr options
 #define BASS_CONFIG_MIDI_DEFFONT	0x10403
-#define BASS_CONFIG_MIDI_SFZHEAD	0x10408
+#define BASS_CONFIG_MIDI_SFZHEAD	0x10409
 
 // Additional sync types
 #define BASS_SYNC_MIDI_MARK		0x10000
@@ -52,12 +58,14 @@ typedef DWORD HSOUNDFONT;	// soundfont handle
 #define BASS_SYNC_MIDI_KEYSIG	0x10007
 
 // Additional BASS_MIDI_StreamCreateFile/etc flags
+#define BASS_MIDI_NODRUMPARAM	0x400
 #define BASS_MIDI_NOSYSRESET	0x800
 #define BASS_MIDI_DECAYEND		0x1000
 #define BASS_MIDI_NOFX			0x2000
 #define BASS_MIDI_DECAYSEEK		0x4000
 #define BASS_MIDI_NOCROP		0x8000
 #define BASS_MIDI_NOTEOFF1		0x10000
+#define BASS_MIDI_ASYNC			0x400000
 #define BASS_MIDI_SINCINTER		0x800000
 
 // BASS_MIDI_FontInit flags
@@ -69,6 +77,7 @@ typedef DWORD HSOUNDFONT;	// soundfont handle
 #define BASS_MIDI_FONT_LINDECVOL	0x200000
 #define BASS_MIDI_FONT_NORAMPIN		0x400000
 #define BASS_MIDI_FONT_NOLIMITS		0x800000
+#define BASS_MIDI_FONT_MINFX		0x1000000
 
 typedef struct {
 	HSOUNDFONT font;	// soundfont
@@ -85,8 +94,20 @@ typedef struct {
 	int dbanklsb;		// destination bank number LSB
 } BASS_MIDI_FONTEX;
 
+typedef struct {
+	HSOUNDFONT font;	// soundfont
+	int spreset;		// source preset number
+	int sbank;			// source bank number
+	int dpreset;		// destination preset/program number
+	int dbank;			// destination bank number
+	int dbanklsb;		// destination bank number LSB
+	DWORD minchan;		// minimum channel number
+	DWORD numchan;		// number of channels from minchan
+} BASS_MIDI_FONTEX2;
+
 // BASS_MIDI_StreamSet/GetFonts flag
 #define BASS_MIDI_FONT_EX		0x1000000	// BASS_MIDI_FONTEX
+#define BASS_MIDI_FONT_EX2		0x2000000	// BASS_MIDI_FONTEX2
 
 typedef struct {
 	const char *name;
@@ -197,10 +218,13 @@ typedef struct {
 #define MIDI_EVENT_VIBRATO_RATE		80
 #define MIDI_EVENT_VIBRATO_DEPTH	81
 #define MIDI_EVENT_VIBRATO_DELAY	82
+#define MIDI_EVENT_MASTER_FINETUNE	83
+#define MIDI_EVENT_MASTER_COARSETUNE	84
 #define MIDI_EVENT_MIXLEVEL			0x10000
 #define MIDI_EVENT_TRANSPOSE		0x10001
 #define MIDI_EVENT_SYSTEMEX			0x10002
 #define MIDI_EVENT_SPEED			0x10004
+#define MIDI_EVENT_DEFDRUMS			0x10006
 
 #define MIDI_EVENT_END				0
 #define MIDI_EVENT_END_TRACK		0x10003
@@ -230,6 +254,9 @@ typedef struct {
 #define BASS_MIDI_EVENTS_CANCEL		0x4000000 // flag: cancel pending events
 #define BASS_MIDI_EVENTS_TIME		0x8000000 // flag: delta-time info is present
 #define BASS_MIDI_EVENTS_ABSTIME	0x10000000 // flag: absolute time info is present
+#define BASS_MIDI_EVENTS_ASYNC		0x20000000 // flag: process asynchronously
+#define BASS_MIDI_EVENTS_FILTER		0x40000000 // flag: apply filtering
+#define BASS_MIDI_EVENTS_FLUSH		0x80000000 // flag: flush async events
 
 // BASS_MIDI_StreamGetChannel special channels
 #define BASS_MIDI_CHAN_CHORUS		(DWORD)-1
@@ -259,7 +286,7 @@ typedef struct {
 // BASS_ChannelGetLength/GetPosition/SetPosition mode
 #define BASS_POS_MIDI_TICK		2		// tick position
 
-typedef BOOL (CALLBACK MIDIFILTERPROC)(HSTREAM handle, DWORD track, BASS_MIDI_EVENT *event, BOOL seeking, void *user);
+typedef BOOL (CALLBACK MIDIFILTERPROC)(HSTREAM handle, int track, BASS_MIDI_EVENT *event, BOOL seeking, void *user);
 /* Event filtering callback function.
 handle : MIDI stream handle
 track  : Track containing the event
@@ -294,6 +321,8 @@ buffer : Buffer containing MIDI data
 length : Number of bytes of data
 user   : The 'user' parameter value given when calling BASS_MIDI_InInit */
 
+DWORD BASSMIDIDEF(BASS_MIDI_GetVersion)(void);
+
 HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreate)(DWORD channels, DWORD flags, DWORD freq);
 HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags, DWORD freq);
 HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user, DWORD freq);
@@ -325,6 +354,7 @@ BOOL BASSMIDIDEF(BASS_MIDI_FontUnload)(HSOUNDFONT handle, int preset, int bank);
 BOOL BASSMIDIDEF(BASS_MIDI_FontCompact)(HSOUNDFONT handle);
 BOOL BASSMIDIDEF(BASS_MIDI_FontPack)(HSOUNDFONT handle, const void *outfile, const void *encoder, DWORD flags);
 BOOL BASSMIDIDEF(BASS_MIDI_FontUnpack)(HSOUNDFONT handle, const void *outfile, DWORD flags);
+DWORD BASSMIDIDEF(BASS_MIDI_FontFlags)(HSOUNDFONT handle, DWORD flags, DWORD mask);
 BOOL BASSMIDIDEF(BASS_MIDI_FontSetVolume)(HSOUNDFONT handle, float volume);
 float BASSMIDIDEF(BASS_MIDI_FontGetVolume)(HSOUNDFONT handle);
 
@@ -341,23 +371,33 @@ BOOL BASSMIDIDEF(BASS_MIDI_InStop)(DWORD device);
 
 static inline BOOL BASS_MIDI_StreamSetFonts(HSTREAM handle, const BASS_MIDI_FONTEX *fonts, DWORD count)
 {
-	return BASS_MIDI_StreamSetFonts(handle, (const void*)fonts, count|BASS_MIDI_FONT_EX);
+	return BASS_MIDI_StreamSetFonts(handle, (const void*)fonts, count | BASS_MIDI_FONT_EX);
+}
+
+static inline BOOL BASS_MIDI_StreamSetFonts(HSTREAM handle, const BASS_MIDI_FONTEX2 *fonts, DWORD count)
+{
+	return BASS_MIDI_StreamSetFonts(handle, (const void*)fonts, count | BASS_MIDI_FONT_EX2);
 }
 
 static inline DWORD BASS_MIDI_StreamGetFonts(HSTREAM handle, BASS_MIDI_FONTEX *fonts, DWORD count)
 {
-	return BASS_MIDI_StreamGetFonts(handle, (void*)fonts, count|BASS_MIDI_FONT_EX);
+	return BASS_MIDI_StreamGetFonts(handle, (void*)fonts, count | BASS_MIDI_FONT_EX);
+}
+
+static inline DWORD BASS_MIDI_StreamGetFonts(HSTREAM handle, BASS_MIDI_FONTEX2 *fonts, DWORD count)
+{
+	return BASS_MIDI_StreamGetFonts(handle, (void*)fonts, count | BASS_MIDI_FONT_EX2);
 }
 
 #ifdef _WIN32
 static inline HSTREAM BASS_MIDI_StreamCreateFile(BOOL mem, const WCHAR *file, QWORD offset, QWORD length, DWORD flags, DWORD freq)
 {
-	return BASS_MIDI_StreamCreateFile(mem, (const void*)file, offset, length, flags|BASS_UNICODE, freq);
+	return BASS_MIDI_StreamCreateFile(mem, (const void*)file, offset, length, flags | BASS_UNICODE, freq);
 }
 
 static inline HSTREAM BASS_MIDI_StreamCreateURL(const WCHAR *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user, DWORD freq)
 {
-	return BASS_MIDI_StreamCreateURL((const char*)url, offset, flags|BASS_UNICODE, proc, user, freq);
+	return BASS_MIDI_StreamCreateURL((const char*)url, offset, flags | BASS_UNICODE, proc, user, freq);
 }
 
 static inline HSOUNDFONT BASS_MIDI_FontInit(const WCHAR *file, DWORD flags)
@@ -367,14 +407,18 @@ static inline HSOUNDFONT BASS_MIDI_FontInit(const WCHAR *file, DWORD flags)
 
 static inline BOOL BASS_MIDI_FontPack(HSOUNDFONT handle, const WCHAR *outfile, const WCHAR *encoder, DWORD flags)
 {
-	return BASS_MIDI_FontPack(handle, (const void*)outfile, (const void*)encoder, flags|BASS_UNICODE);
+	return BASS_MIDI_FontPack(handle, (const void*)outfile, (const void*)encoder, flags | BASS_UNICODE);
 }
 
 static inline BOOL BASS_MIDI_FontUnpack(HSOUNDFONT handle, const WCHAR *outfile, DWORD flags)
 {
-	return BASS_MIDI_FontUnpack(handle, (const void*)outfile, flags|BASS_UNICODE);
+	return BASS_MIDI_FontUnpack(handle, (const void*)outfile, flags | BASS_UNICODE);
 }
 #endif
 #endif
 
+#ifdef __OBJC__
+#undef BOOL
+#endif
+
 #endif
diff --git a/ThirdParty/BASS/sflist.c b/ThirdParty/BASS/sflist.c
index 2ebdfdd3c..1ef81ddb5 100644
--- a/ThirdParty/BASS/sflist.c
+++ b/ThirdParty/BASS/sflist.c
@@ -457,7 +457,7 @@ static const json_value *json_object_item(const json_value *object, const char *
 	return &json_value_none;
 }
 
-static void sflist_process_patchmappings(BASS_MIDI_FONTEX *out, BASS_MIDI_FONTEX *fontex, const json_value *patchMappings, unsigned int channel) {
+static void sflist_process_patchmappings(BASS_MIDI_FONTEX2 *out, BASS_MIDI_FONTEX2 *fontex, const json_value *patchMappings, unsigned int channel, unsigned int channelCount) {
 	unsigned int i, j;
 	for(i = 0, j = patchMappings->u.array.length; i < j; ++i) {
 		json_value *preset = patchMappings->u.array.values[i];
@@ -471,7 +471,8 @@ static void sflist_process_patchmappings(BASS_MIDI_FONTEX *out, BASS_MIDI_FONTEX
 		fontex->sbank = (source_bank->type == json_none) ? -1 : (int)source_bank->u.integer;
 		fontex->dpreset = (destination_program->type == json_none) ? -1 : (int)destination_program->u.integer;
 		fontex->dbank = (destination_bank->type == json_none) ? 0 : (int)destination_bank->u.integer;
-		fontex->dbanklsb = channel;
+		fontex->minchan = channel;
+		fontex->numchan = channelCount;
 		*out++ = *fontex;
 	}
 }
@@ -487,7 +488,7 @@ static sflist_presets *sflist_process(const json_value *sflist, const char *base
 	json_value *arr;
 	unsigned int i, j, k, l, preset_number;
 	HSOUNDFONT hfont = 0;
-	BASS_MIDI_FONTEX fontex;
+	BASS_MIDI_FONTEX2 fontex;
 
 	if(!rval) {
 		strcpy(error_buf, "Out of memory");
@@ -544,6 +545,8 @@ static sflist_presets *sflist_process(const json_value *sflist, const char *base
 				sprintf(error_buf, "soundFont item #%u 'channels' is not an array", i + 1);
 				goto error;
 			}
+			int prevchannel = -1;
+			int contiguouschannelsets = 0;
 			for(k = 0, l = channels->u.array.length; k < l; ++k) {
 				json_value *channel = channels->u.array.values[k];
 				if(channel->type != json_integer) {
@@ -554,8 +557,12 @@ static sflist_presets *sflist_process(const json_value *sflist, const char *base
 					sprintf(error_buf, "soundFont item #%u 'channels' #%u is out of range (wanted 1-48, got %" PRId64 ")", i + 1, k + 1, channel->u.integer);
 					goto error;
 				}
+				if(prevchannel < 0 || channel->u.integer > (prevchannel + 1)) {
+					++contiguouschannelsets;
+				}
+				prevchannel = (int)channel->u.integer;
 			}
-			patches_needed = l;
+			patches_needed = contiguouschannelsets;
 		}
 		if(patchMappings->type != json_none) {
 			if(patchMappings->type != json_array) {
@@ -647,7 +654,7 @@ static sflist_presets *sflist_process(const json_value *sflist, const char *base
 	}
 
 	rval->count = presets_to_allocate;
-	rval->presets = calloc(sizeof(BASS_MIDI_FONTEX), rval->count);
+	rval->presets = calloc(sizeof(BASS_MIDI_FONTEX2), rval->count);
 
 	if(!rval->presets) {
 		strcpy(error_buf, "Out of memory");
@@ -725,22 +732,51 @@ static sflist_presets *sflist_process(const json_value *sflist, const char *base
 		fontex.dpreset = -1;
 		fontex.dbank = 0;
 		fontex.dbanklsb = 0;
+		fontex.minchan = 0;
+		fontex.numchan = 48;
 		/* Simplest case, whole bank loading */
 		if(channels->type == json_none && patchMappings->type == json_none) {
 			rval->presets[preset_number++] = fontex;
 		} else if(patchMappings->type == json_none) {
+			int prevchannel = -1;
+			int firstchannel = -1;
 			for(k = 0, l = channels->u.array.length; k < l; ++k) {
-				fontex.dbanklsb = (int)channels->u.array.values[k]->u.integer;
-				rval->presets[preset_number++] = fontex;
+				int channel = (int)channels->u.array.values[k]->u.integer;
+				if(firstchannel < 0) {
+					firstchannel = channel;
+					prevchannel = channel;
+				}
+				if(channel > (prevchannel + 1)) {
+					fontex.minchan = firstchannel;
+					fontex.numchan = prevchannel - firstchannel + 1;
+					rval->presets[preset_number++] = fontex;
+					firstchannel = channel;
+				}
+				prevchannel = channel;
 			}
+			fontex.minchan = firstchannel;
+			fontex.numchan = prevchannel - firstchannel + 1;
+			rval->presets[preset_number++] = fontex;
 		} else if(channels->type == json_none) {
-			sflist_process_patchmappings(rval->presets + preset_number, &fontex, patchMappings, 0);
+			sflist_process_patchmappings(rval->presets + preset_number, &fontex, patchMappings, 0, 48);
 			preset_number += patchMappings->u.array.length;
 		} else {
+			int prevchannel = -1;
+			int firstchannel = -1;
 			for(k = 0, l = channels->u.array.length; k < l; ++k) {
-				sflist_process_patchmappings(rval->presets + preset_number, &fontex, patchMappings, (int)channels->u.array.values[k]->u.integer);
-				preset_number += patchMappings->u.array.length;
+				int channel = (int)channels->u.array.values[k]->u.integer;
+				if(firstchannel < 0) {
+					firstchannel = channel;
+					prevchannel = channel;
+				}
+				if(channel > (prevchannel + 1)) {
+					sflist_process_patchmappings(rval->presets + preset_number, &fontex, patchMappings, firstchannel, prevchannel - firstchannel + 1);
+					preset_number += patchMappings->u.array.length;
+				}
+				prevchannel = channel;
 			}
+			sflist_process_patchmappings(rval->presets + preset_number, &fontex, patchMappings, firstchannel, prevchannel - firstchannel + 1);
+			preset_number += patchMappings->u.array.length;
 		}
 	}
 
diff --git a/ThirdParty/BASS/sflist.h b/ThirdParty/BASS/sflist.h
index e758c1697..bc44d534d 100644
--- a/ThirdParty/BASS/sflist.h
+++ b/ThirdParty/BASS/sflist.h
@@ -42,7 +42,7 @@ extern "C" {
 
 typedef struct sflist_presets {
 	unsigned int count;
-	BASS_MIDI_FONTEX *presets;
+	BASS_MIDI_FONTEX2 *presets;
 } sflist_presets;
 
 #define sflist_max_error 1024
diff --git a/ThirdParty/libraries.tar.xz b/ThirdParty/libraries.tar.xz
index 09fb0bd0e..3bce75834 100644
Binary files a/ThirdParty/libraries.tar.xz and b/ThirdParty/libraries.tar.xz differ
diff --git a/ThirdParty/libvgm/README.md b/ThirdParty/libvgm/README.md
index 39cca4770..2c67f0541 100644
--- a/ThirdParty/libvgm/README.md
+++ b/ThirdParty/libvgm/README.md
@@ -6,6 +6,6 @@ Built on an M1 Mac mini, using CMake from Homebrew, with the following
 options:
 
 ```
-cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.12" \
+cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET="10.13" \
     -DBUILD_LIBAUDIO=NO -DBUILD_PLAYER=NO -DBUILD_VGM2WAV=NO
 ```