[Scummvm-git-logs] scummvm master -> 1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3

dreammaster noreply at scummvm.org
Sun Jun 21 02:42:02 UTC 2026


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
4bd44c641c MADS: FOREST: global_digi_play is actual global_midi_play
76905fd6af MADS: FOREST: Move digi/midi files into Forest namespace
1ce640b1a1 MADS: FOREST: Skeleton MidiPlayer class


Commit: 4bd44c641c8a672f55947e3dd8d4d9bd10b96032
    https://github.com/scummvm/scummvm/commit/4bd44c641c8a672f55947e3dd8d4d9bd10b96032
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:49+10:00

Commit Message:
MADS: FOREST: global_digi_play is actual global_midi_play

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 5b6de4b6fe6..3615775203e 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -985,9 +985,16 @@ void global_error_code() {
 		text_show(text_id);
 }
 
-void global_digi_play(int num) {
-	// TODO
-	warning("TODO: global_digi_play");
+void global_midi_play(int num) {
+	static const char *NAMES[14] = {
+		"adven2", "foolarnd", "homeag", "humorus1", "humorus2", "pianogtr", "raindrop",
+		"xad", "xcarey", "xuspens1", "travels1", "birdsong", "adventur", "action1"
+	};
+
+	assert(num >= 1 && num <= 14);
+	Common::String name = Common::String::format("*%s.hmi", NAMES[num - 1]);
+
+	midi_play(name.c_str());
 }
 
 void global_daemon_code() {
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index 0c47ac2b5b2..5c847f1e4df 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -268,7 +268,7 @@ extern void global_error_code();
 extern void global_anim1(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim2(int arg_0, int arg_2, int arg_4, int16 *arg_6);
 extern void global_anim3(int handle, int16 *frame);
-extern void global_digi_play(int num);
+extern void global_midi_play(int num);
 extern void global_daemon_code();
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 45a43b03e8a..8e60e2a8050 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -116,7 +116,7 @@ static void room_101_init2() {
 	kernel_position_anim(scratch._a4, 194, 145, 75, 4);
 
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 
 	if (previous_room != KERNEL_RESTORING_GAME) {
 		player.x = 96;
@@ -557,7 +557,7 @@ static void room_101_anim10() {
 				scratch._b4 = 35;
 			} else if (frame < 35) {
 				if (frame == 5)
-					global_digi_play(11);
+					global_midi_play(11);
 				else if (frame == 30) {
 					digi_play_build(101, '_', 3, 1);
 					scratch._b4 = 30;
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index 89426005151..f4f4b3a16ff 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -333,7 +333,7 @@ static void room_103_anim4() {
 		} else if (frame < 50) {
 			switch (frame) {
 			case 16:
-				global_digi_play(8);
+				global_midi_play(8);
 				aainfo[3]._val3 = 15;
 				digi_play_build(103, 'c', 1, 1);
 				scratch._90 = 16;
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 7191b997870..71c10cb5336 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -151,7 +151,7 @@ static void room_104_init() {
 static void room_104_init1() {
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 	viewing_at_y = 22;
 	player.walker_visible = 0;
 	player.commands_allowed = 0;
@@ -186,7 +186,7 @@ static void room_104_init2() {
 
 static void room_104_init3() {
 	global[g009] = -1;
-	global_digi_play(8);
+	global_midi_play(8);
 	global[g131] = 0;
 	global[g141] = 0;
 
@@ -593,7 +593,7 @@ static void room_104_anim3() {
 			aainfo[8]._val3 = 8;
 			digi_play_build(104, 'e', 3, 1);
 			scratch._a2 = 84;
-			global_digi_play(14);
+			global_midi_play(14);
 			break;
 		case 89:
 			if (aainfo[8]._val3 == 8) {
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index e54b918ce02..398d276c017 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -363,7 +363,7 @@ static void room_106_anim4() {
 		return;
 	case 105:
 		global[g009] = 0;
-		global_digi_play(4);
+		global_midi_play(4);
 		global[g154] = 0;
 		aainfo[7]._val3 = 13;
 		aainfo[7]._frame = 20;
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index ebf9365e9e6..f762365faeb 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -120,7 +120,7 @@ static void room_107_anim1() {
 		if (scratch._8e == 21) {
 			aainfo[0]._val3 = 2;
 			scratch._8e = -1;
-			global_digi_play(4);
+			global_midi_play(4);
 			aainfo[0]._frame = 26;
 			kernel_reset_animation(aa[0], 26);
 		}
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index e84be3e44d7..87cbfd4a322 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -102,7 +102,7 @@ static void room_199_init1() {
 static void room_199_init() {
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(7);
+	global_midi_play(7);
 
 	aainfo[2]._val3 = -1;
 	aainfo[2]._val4 = 0;
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index 31ab0cd044c..9684f3d598b 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -73,7 +73,7 @@ static Scratch scratch;
 static void room_201_init1() {
 	global[player_score] = -1;
 	global[g009] = -1;
-	global_digi_play(11);
+	global_midi_play(11);
 	global[g131] = 0;
 	global[g141] = 0;
 
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 72638c88bd0..5088c5f7fd9 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -125,7 +125,7 @@ static void room_203_init1() {
 		aainfo[0]._active = -1;
 		scratch._9a = 11;
 		scratch._92 = 74;
-		global_digi_play(11);
+		global_midi_play(11);
 		scratch._a6 = 5;
 		return;
 	}
@@ -143,7 +143,7 @@ static void room_203_init1() {
 	global[g143] = 0;
 
 	if (global[g046] == 0) {
-		global_digi_play(15);
+		global_midi_play(15);
 		scratch._a6 = 2;
 		aa[4] = kernel_run_animation(kernel_name('w', 4), 105);
 		aainfo[4]._active = -1;
@@ -171,7 +171,7 @@ static void room_203_init1() {
 		kernel_flip_hotspot(119, global[g047] == 0 ? 1 : 0);
 		kernel_flip_hotspot(27, global[g047]);
 	} else {
-		global_digi_play(11);
+		global_midi_play(11);
 		scratch._a6 = 5;
 	}
 }
@@ -256,7 +256,7 @@ static void room_203_anim1() {
 			} else if (frame < 50) {
 				if (frame == 22) {
 					if (local->_a6 != 1) {
-						global_digi_play(10);
+						global_midi_play(10);
 						local->_a6 = 1;
 					}
 					aainfo[4]._frame = 3;
@@ -270,7 +270,7 @@ static void room_203_anim1() {
 
 		if (aainfo[0]._frame == 1) {
 			if (global[g046] == 0 && flags[8] > 1 && local->_a6 != 1) {
-				global_digi_play(10);
+				global_midi_play(10);
 				local->_a6 = 1;
 			}
 		}
@@ -917,12 +917,12 @@ static void room_203_anim5() {
 			result = 27;
 		} else if (frame < 29) {
 			if (frame == 1) {
-				global_digi_play(12);
+				global_midi_play(12);
 				local->_a6 = 4;
 			} else if (frame == 19) {
 				digi_play_build(103, '_', 3, 2);
 				if (local->_a6 != 2) {
-					global_digi_play(15);
+					global_midi_play(15);
 					local->_a6 = 2;
 				}
 			} else if (frame == 22) {
@@ -957,7 +957,7 @@ static void room_203_anim5() {
 			local->_a6 = 0;
 		} else if (frame < 25) {
 			if (frame == 1) {
-				global_digi_play(5);
+				global_midi_play(5);
 				local->_a6 = 3;
 			} else if (frame == 18) {
 				if (!local->_ac && local->_98 == 33) {
@@ -1297,7 +1297,7 @@ static void room_203_daemon() {
 				player.commands_allowed = true;
 			} else {
 				if (local->_a6 != 1) {
-					global_digi_play(10);
+					global_midi_play(10);
 					local->_a6 = 1;
 				}
 
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index a6f427c53f2..384865a30d3 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -253,7 +253,7 @@ static void room_205_anim5() {
 
 	if (aainfo[6]._frame == 135) {
 		global[g009] = 0;
-		global_digi_play(9);
+		global_midi_play(9);
 		kernel_timing_trigger(180, 105);
 	} else if (aainfo[6]._frame == 138) {
 		aainfo[6]._frame = 137;
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 4973007164b..465632497d6 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -63,7 +63,7 @@ static Scratch scratch;
 static void room_220_init() {
 	global[player_score] = -1;
 	global[g009] = -1;
-	global_digi_play(10);
+	global_midi_play(10);
 	viewing_at_y = 22;
 	player.walker_visible = 0;
 	player.commands_allowed = 0;
@@ -90,7 +90,7 @@ static void room_220_anim1() {
 		kernel_abort_animation(aa[0]);
 		aainfo[0]._active = 0;
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		aa[1] = kernel_run_animation("*RM220Y12", 0);
 		aainfo[1]._active = -1;
 		scratch._92 = 55;
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index 70235ac4f5e..ec8ee34447f 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -273,7 +273,7 @@ static void room_304_anim4() {
 			digi_play_build(304, '_', 3, 2);
 			scratch._a0 = 3;
 			global[g009] = 0;
-			global_digi_play(14);
+			global_midi_play(14);
 		} else if (frame == 11) {
 			digi_stop(2);
 		}
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index 468ae49e6e6..ff0d670596e 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -89,7 +89,7 @@ static void room_305_init1() {
 	if (previous_room == 301) {
 		aa[1] = kernel_run_animation(kernel_name('t', 1), 104);
 		aainfo[1]._active = -1;
-		global_digi_play(14);
+		global_midi_play(14);
 		return;
 	}
 
@@ -243,7 +243,7 @@ static void room_305_init() {
 
 	global[g009] = -1;
 	if (global[g066] == 1)
-		global_digi_play(8);
+		global_midi_play(8);
 }
 
 static void room_305_anim1() {
@@ -484,7 +484,7 @@ static void room_305_anim6() {
 		kernel_timing_trigger(45, 105);
 		scratch._a4++;
 		global[g009] = -1;
-		global_digi_play(6);
+		global_midi_play(6);
 		break;
 	case 15:
 		if (global[g065] != 0) {
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index ec247be676f..7ef97d10d99 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -108,7 +108,7 @@ static void room_308_anim1() {
 		break;
 	case 70:
 		global[g009] = 0;
-		global_digi_play(9);
+		global_midi_play(9);
 		break;
 	case 88:
 		digi_initial_volume(60);
@@ -794,7 +794,7 @@ static void room_308_anim20() {
 	int16 result = -1;
 	if (cur == 6) {
 		global[g009] = 0;
-		global_digi_play(5);
+		global_midi_play(5);
 		digi_play_build(308, 't', 5, 1);
 		scratch._94 = 3;
 		scratch._b4 = 3;
@@ -923,7 +923,7 @@ static void room_308_anim24() {
 			result = imath_random(20, 25);
 	} else if (cur == 29) {
 		global[g009] = 0;
-		global_digi_play(5);
+		global_midi_play(5);
 	}
 
 	if (result >= 0) {
@@ -1055,7 +1055,7 @@ static void room_308_init() {
 			aainfo[1]._frame = -1;
 			kernel_synch(KERNEL_ANIM, ss[7], KERNEL_NOW, 0);
 			global[g009] = -1;
-			global_digi_play(15);
+			global_midi_play(15);
 			player.commands_allowed = 0;
 			return;
 		}
@@ -1070,7 +1070,7 @@ static void room_308_init() {
 			aainfo[1]._val4 = -1;
 			kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
 			global[g009] = -1;
-			global_digi_play(15);
+			global_midi_play(15);
 			return;
 		}
 		if (player_has(15)) {
@@ -1083,7 +1083,7 @@ static void room_308_init() {
 		aainfo[1]._val4 = -1;
 		kernel_synch(KERNEL_ANIM, ss[8], KERNEL_NOW, 0);
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		return;
 	}
 
@@ -1114,7 +1114,7 @@ static void room_308_init() {
 
 	if (global[g073] == 0) {
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		return;
 	}
 
@@ -1125,7 +1125,7 @@ static void room_308_init() {
 	aainfo[8]._frame = 0;
 	scratch._c2 = 100;
 	global[g009] = -1;
-	global_digi_play(15);
+	global_midi_play(15);
 }
 
 static void room_308_daemon() {
@@ -1176,7 +1176,7 @@ static void room_308_daemon() {
 		aainfo[0]._val4 = -1;
 		kernel_synch(KERNEL_ANIM, ss[6], KERNEL_NOW, 0);
 		global[g009] = -1;
-		global_digi_play(15);
+		global_midi_play(15);
 		break;
 
 	case 102:
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index bbf9024206f..fc47eafab3e 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -155,9 +155,9 @@ static void room_321_init() {
 		kernel_seq_loc(seq[4], 230, 105);
 		kernel_seq_scale(seq[4], 83);
 		kernel_flip_hotspot(177, 0);
-		global_digi_play(13);
+		global_midi_play(13);
 	} else {
-		global_digi_play(3);
+		global_midi_play(3);
 	}
 
 	global[g009] = -1;
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index 6fbecfcff86..c510d1402e7 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -339,7 +339,7 @@ static void room_322_anim8() {
 		digi_play_build(322, '_', 1, 2);
 	} else if (cur == 27) {
 		global[g009] = 0;
-		global_digi_play(4);
+		global_midi_play(4);
 	}
 
 	if (result >= 0) {
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index cbee1bfb2c0..f1387102cf6 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -168,7 +168,7 @@ static void room_402_init1() {
 	}
 
 	global[player_score] = 0;
-	global_digi_play(15);
+	global_midi_play(15);
 }
 
 static void room_402_init() {
@@ -356,7 +356,7 @@ static void room_402_anim_state() {
 		kernel_flip_hotspot(187, 0);
 		break;
 	case 17:
-		global_digi_play(5);
+		global_midi_play(5);
 		global[g009] = 0;
 		aa[1] = kernel_run_animation(kernel_name('L', 13), 103);
 		aainfo[1]._active = -1;
@@ -584,7 +584,7 @@ static void room_402_anim2() {
 	case 20: {
 		int16 frame = aainfo[1]._frame;
 		if (frame == 2) {
-			global_digi_play(15);
+			global_midi_play(15);
 			player.commands_allowed = -1;
 			global[g083] = 0;
 			room_402_anim6();
@@ -612,7 +612,7 @@ static void room_402_anim2() {
 	case 2: {
 		int16 frame = aainfo[1]._frame;
 		if (frame == 57) {
-			global_digi_play(10);
+			global_midi_play(10);
 			global[player_score] = 0;
 		} else if (frame < 57) {
 			if (frame == 37) {
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index d777d24502b..d35ee2533fa 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -438,7 +438,7 @@ static void room_403_anim6() {
 			aainfo[5]._frame = 12;
 			kernel_reset_animation(aa[5], 12);
 			scratch._a4 = -1;
-			global_digi_play(14);
+			global_midi_play(14);
 			kernel_seq_delete(seq[1]);
 			global[g080] = -1;
 		} else if (a4 == 89) {
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index ed5f1ee07ca..b0a53052ea0 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -113,7 +113,7 @@ static void room_420_anim2() {
 					scratch._8e = 34;
 					scratch.animation_info[1]._val3 = 10;
 					digi_play_build(420, 'b', 1, 1);
-					global_digi_play(3);
+					global_midi_play(3);
 				} else if (cur == 39) {
 					if (scratch.animation_info[1]._val3 == 10) {
 						scratch.animation_info[1]._frame = 34;
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index e8ed6ca0d89..ed46140e7b8 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -356,7 +356,7 @@ static void room_501_anim5() {
 			digi_play_build(501, 'e', 1, 1);
 			scratch._a4 = 3;
 		} else if (scratch._a4 == 3) {
-			global_digi_play(14);
+			global_midi_play(14);
 			global[g156] = 0;
 			aainfo[1]._val3 = 23;
 			aainfo[1]._frame = 105;
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 26ed0aa44e7..35bd8fb3393 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -144,7 +144,7 @@ static void room_503_init1() {
 
 	if (previous_room == 199) {
 		if (global[g100] != 0) {
-			global_digi_play(14);
+			global_midi_play(14);
 			viewing_at_y = 22;
 			scratch._ba = 2;
 			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
@@ -182,7 +182,7 @@ static void room_503_init1() {
 			scratch._b8 = 1;
 		}
 
-		global_digi_play(6);
+		global_midi_play(6);
 		aa[0] = kernel_run_animation(kernel_name('y', 2), 100);
 		aainfo[0]._val3 = -1;
 		scratch._a6 = 52;
@@ -207,7 +207,7 @@ static void room_503_init3() {
 
 	if (previous_room == 199) {
 		if (global[g100] != 0) {
-			global_digi_play(14);
+			global_midi_play(14);
 			viewing_at_y = 22;
 			scratch._ba = 2;
 			aainfo[0]._active = kernel_run_animation(kernel_name('N', 2), 106);
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 480dbdc7665..10ff60818a0 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -63,7 +63,7 @@ static void room_509_init() {
 	mouse_hide();
 	global[player_score] = 0;
 	global[g009] = -1;
-	global_digi_play(15);
+	global_midi_play(15);
 	global[g009] = -1;
 	viewing_at_y = 22;
 	global[player_score] = 0;
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 5627f2ef5cc..213430b7c43 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -226,7 +226,7 @@ static void room_510_anim3() {
 			digi_play_build(510, '_', 1, 2);
 			scratch._90 = 1;
 			midi_stop();
-			global_digi_play(4);
+			global_midi_play(4);
 		} else if (aa_frame == 40) {
 			midi_stop();
 			new_room = 107;
@@ -288,7 +288,7 @@ static void room_510_daemon() {
 		if (scratch._8e == 668) {
 			scratch._8e = -1;
 			global[g009] = 0;
-			global_digi_play(14);
+			global_midi_play(14);
 			kernel_timing_trigger(2100, 104);
 		}
 		break;
@@ -314,7 +314,7 @@ static void room_510_daemon() {
 		break;
 	case 104:
 		global[g009] = -1;
-		global_digi_play(11);
+		global_midi_play(11);
 		break;
 	case 106:
 		scratch._96 = -1;
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 4c44a7458df..13d6596dcc9 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -130,7 +130,7 @@ static void room_520_daemon() {
 		scratch._8c = 66;
 		break;
 	case 101:
-		global_digi_play(14);
+		global_midi_play(14);
 		break;
 	}
 
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index 6d17b2af68b..af19e94920a 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -53,7 +53,7 @@ static void room_903_init() {
 
 static void room_903_daemon() {
 	if (kernel.trigger == TRIGGER1)
-		global_digi_play(14);
+		global_midi_play(14);
 
 	if (mouse_any_stroke || g_engine->hasPendingKey() || kernel.trigger == TRIGGER0) {
 		g_engine->flushKeys();
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 5a08adbba09..bfc535ce269 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -88,7 +88,7 @@ static void room_904_seen_intro() {
 
 static void room_904_init() {
 	last_keypressed = -1;
-	global_digi_play(3);
+	global_midi_play(3);
 	aainfo[1]._active = 0;
 	aainfo[1]._frame = 0;
 	global[g009] = -1;


Commit: 76905fd6afab17eaec7c88c6f7aba61cb9df1bce
    https://github.com/scummvm/scummvm/commit/76905fd6afab17eaec7c88c6f7aba61cb9df1bce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:50+10:00

Commit Message:
MADS: FOREST: Move digi/midi files into Forest namespace

It's the only game that uses them, so it's best to be clear.

Changed paths:
  A engines/mads/madsv2/forest/digi.cpp
  A engines/mads/madsv2/forest/digi.h
  A engines/mads/madsv2/forest/midi.cpp
  A engines/mads/madsv2/forest/midi.h
  R engines/mads/madsv2/core/digi.cpp
  R engines/mads/madsv2/core/digi.h
  R engines/mads/madsv2/core/midi.cpp
  R engines/mads/madsv2/core/midi.h
  R engines/mads/madsv2/forest/asound.cpp
  R engines/mads/madsv2/forest/asound.h
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/rooms/room101.cpp
    engines/mads/madsv2/forest/rooms/room103.cpp
    engines/mads/madsv2/forest/rooms/room104.cpp
    engines/mads/madsv2/forest/rooms/room106.cpp
    engines/mads/madsv2/forest/rooms/room107.cpp
    engines/mads/madsv2/forest/rooms/room199.cpp
    engines/mads/madsv2/forest/rooms/room201.cpp
    engines/mads/madsv2/forest/rooms/room203.cpp
    engines/mads/madsv2/forest/rooms/room204.cpp
    engines/mads/madsv2/forest/rooms/room205.cpp
    engines/mads/madsv2/forest/rooms/room210.cpp
    engines/mads/madsv2/forest/rooms/room211.cpp
    engines/mads/madsv2/forest/rooms/room220.cpp
    engines/mads/madsv2/forest/rooms/room301.cpp
    engines/mads/madsv2/forest/rooms/room302.cpp
    engines/mads/madsv2/forest/rooms/room303.cpp
    engines/mads/madsv2/forest/rooms/room304.cpp
    engines/mads/madsv2/forest/rooms/room305.cpp
    engines/mads/madsv2/forest/rooms/room306.cpp
    engines/mads/madsv2/forest/rooms/room307.cpp
    engines/mads/madsv2/forest/rooms/room308.cpp
    engines/mads/madsv2/forest/rooms/room321.cpp
    engines/mads/madsv2/forest/rooms/room322.cpp
    engines/mads/madsv2/forest/rooms/room401.cpp
    engines/mads/madsv2/forest/rooms/room402.cpp
    engines/mads/madsv2/forest/rooms/room403.cpp
    engines/mads/madsv2/forest/rooms/room404.cpp
    engines/mads/madsv2/forest/rooms/room405.cpp
    engines/mads/madsv2/forest/rooms/room420.cpp
    engines/mads/madsv2/forest/rooms/room501.cpp
    engines/mads/madsv2/forest/rooms/room503.cpp
    engines/mads/madsv2/forest/rooms/room509.cpp
    engines/mads/madsv2/forest/rooms/room510.cpp
    engines/mads/madsv2/forest/rooms/room520.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/forest/asound.cpp b/engines/mads/madsv2/forest/asound.cpp
deleted file mode 100644
index 70022f4fdb8..00000000000
--- a/engines/mads/madsv2/forest/asound.cpp
+++ /dev/null
@@ -1,1805 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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 3 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 "audio/fmopl.h"
-#include "mads/madsv2/forest/asound.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-
-bool AdlibChannel::_isDisabled;
-
-/*
-  * PATCH_ATTEN_TO_TL  (seg001:0x0092 / offset from _asound_samples base)
-  * patchAttenuation (0-127) -> 6-bit OPL total-level.
-  * The Dragonsphere asm uses PATCH_ATTEN_TO_TL[bx] for the modulator lookup
-  * and unk_12431 - bx (i.e. PATCH_ATTEN_TO_TL[127 - patchAtt]) for the
-  * carrier lookup.
-  */
-static const uint8 PATCH_ATTEN_TO_TL[128] = {
-	63, 54, 49, 45, 42, 40, 38, 36, 34, 33, 32, 31, 30, 29, 28, 27,
-	26, 25, 25, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18,
-	18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13,
-	12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10,  9,  9,  9,
-	 9,  9,  8,  8,  8,  8,  8,  7,  7,  7,  7,  7,  7,  6,  6,  6,
-	 6,  6,  6,  5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  4,
-	 4,  3,  3,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,
-	 2,  2,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0
-};
-
-/*
- * VOL_VEL_TO_ATTEN_STEP  (_volVelToAttenStep)
- * volume or velocity byte (0-127) -> attenuation step (1-32).
- * Groups of 4 inputs map to the same step.
- */
-static const uint8 VOL_VEL_TO_ATTEN_STEP[128] = {
-	 1, 1, 1, 1,  2, 2, 2, 2,  3, 3, 3, 3,  4, 4, 4, 4,
-	 5, 5, 5, 5,  6, 6, 6, 6,  7, 7, 7, 7,  8, 8, 8, 8,
-	 9, 9, 9, 9, 10,10,10,10, 11,11,11,11, 12,12,12,12,
-	13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16,
-	17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20,
-	21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24,
-	25,25,25,25, 26,26,26,26, 27,27,27,27, 28,28,28,28,
-	29,29,29,29, 30,30,30,30, 31,31,31,31, 32,32,32,32
-};
-
-/*
- * SEMITONE_FREQ_TABLE  (_semitoneFreqTable)
- * OPL F-number base for each semitone within an octave.
- */
-static const uint16 SEMITONE_FREQ_TABLE[12] = {
-	0x0200, 0x021E, 0x023F, 0x0261, 0x0285, 0x02AB,
-	0x02D4, 0x02FF, 0x032D, 0x035D, 0x0390, 0x03C7
-};
-
-/*
- * VOICE_SLOTS  (byte_1239B in the binary, also used as the operator-reg
- * index table for command6/7)
- *
- * Layout for each voice: { slot0 (modulator), slot1 (carrier) }
- * The writeVolume loop uses:
- *   pass 0 -> VOICE_SLOTS[ch][0]  (modulator)
- *   pass 1 -> VOICE_SLOTS[ch][1]  (carrier)
- * The alg!=0 single-op path (loc_11692) goes directly to VOICE_SLOTS[ch][1].
- */
-static const uint8 VOICE_SLOTS[ADLIB_CHANNEL_COUNT][2] = {
-	{  0,  3 }, {  1,  4 }, {  2,  5 },
-	{  6,  9 }, {  7, 10 }, {  8, 11 },
-	{ 12, 15 }, { 13, 16 }, { 14, 17 }
-};
-
-/*
- * SLOT_TO_REG_OFFSET  (_slotToRegOffset)
- * operator-slot index (0-17) -> OPL register group offset.
- */
-static const uint8 SLOT_TO_REG_OFFSET[18] = {
-	 0,  1,  2,  3,  4,  5,
-	 8,  9, 10, 11, 12, 13,
-	16, 17, 18, 19, 20, 21
-};
-
-/*
- * byte_1239B  - all 22 operator TL register indices muted/restored by
- * command6 and command7.  They cover every OPL operator slot (0x40-0x55).
- */
-static const uint8 ALL_OP_TL_REGS[22] = {
-	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
-	0x50, 0x51, 0x52, 0x53, 0x54, 0x55
-};
-
-/* Null / silence sound-data placeholder stream. */
-static uint8 ADLIB_NULLDATA[] = {
-	0x00, 0x01, 0xF3, 0x00, 0x00, 0xEF, 0x00, 0x00,
-	0xF5, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF8, 0x1D,
-	0xFF, 0x00, 0xF4, 0x6E, 0x2A, 0x1C, 0xF4, 0x5F,
-	0x2A, 0x1C, 0x2A, 0x1C, 0x2A, 0x1C, 0xFF, 0xFF,
-	0x00, 0x00, 0x00, 0x00
-};
-
-/* OPL version flag (_adlib_v5660 / opl_version_flag).
- * 0x18 = OPL3 (patch-attenuation-aware writeVolume path). */
-constexpr uint16 OPL_VERSION_FLAG = 0x18;
-
-/* Rhythm-mode flags (from data segment). */
-constexpr bool RHYTHM_HI_HAT = true;
-constexpr bool RHYTHM_CYMBAL = true;
-constexpr bool RHYTHM_ENABLE = true;
-
-AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
-	_attackRate = s.readByte();
-	_decayRate = s.readByte();
-	_sustainLevel = s.readByte();
-	_releaseRate = s.readByte();
-	_egTyp = s.readByte();
-	_ksr = s.readByte();
-	_totalLevel = s.readByte();
-	_scalingLevel = s.readByte();
-	_waveformSelect = s.readByte();
-	_freqMultiple = s.readByte();
-	_feedback = s.readByte();
-	_ampMod = s.readByte();
-	_vib = s.readByte();
-	_alg = s.readByte();
-	_freqSweepInit = s.readByte();
-	_reserved = s.readByte();
-	_freqMask = s.readUint16LE();
-	_freqBase = s.readUint16LE();
-	_outerLoopPtr = s.readUint16LE();
-}
-
-void AdlibChannel::reset() {
-	/* Zero the scalar "live" fields only.
-	 *
-	 * From AdlibChannel_reset in the binary: clears _activeCount, _pitchBend,
-	 * _volumeFadeStep, _vibratoDepth, _arpCounterReload (field_13),
-	 * _writeVolumePending (field_11), _transpose, _patchAttenuation,
-	 * _pendingStop, _velocity, _volume, _freqSweepCounter, _noiseFreqMask,
-	 * _freqAccum, _freqStep, _loopStartPtr, _pSrc.
-	 * All other fields are left as-is (they are overwritten by load() or
-	 * by the bytecode stream before being read).
-	 */
-	_activeCount = 0;
-	_pitchBend = 0;
-	_volumeFadeStep = 0;
-	_vibratoDepth = 0;
-	_arpCounterReload = 0;   /* field_13 */
-	_writeVolumePending = 0;   /* field_11 */
-	_transpose = 0;
-	_patchAttenuation = 0;
-	_pendingStop = 0;
-	_velocity = 0;
-	_volume = 0;
-	_freqSweepCounter = 0;
-	_noiseFreqMask = 0;
-	_freqAccum = 0;
-	_freqStep = 0;
-	_loopStartPtr = nullptr;
-	_pSrc = nullptr;
-}
-
-void AdlibChannel::load(byte *soundData) {
-	_isDisabled = true;
-
-	/* Zero all bytes in the struct. */
-	memset(this, 0, sizeof(AdlibChannel));
-
-	/* Initialise all loop/source pointers to the supplied data block. */
-	_loopStartPtr = soundData;
-	_pSrc = soundData;
-	_innerLoopPtr = soundData;
-	_outerLoopPtr = soundData;
-	_soundData = soundData;
-
-	/* Start at maximum attenuation (silent) and mark the channel active. */
-	_patchAttenuation = 0x40;
-	_activeCount = 1;
-
-	_isDisabled = false;
-}
-
-void AdlibChannel::setPtr2(byte *ptr) {
-	/* Hard-redirect both read pointers, then arm a one-step fade. */
-	_loopStartPtr = ptr;
-	_pSrc = ptr;
-	_volumeFadeStep = 0xFF;   /* -1 signed: fade down */
-	_fadePeriodReload = 1;
-	_fadePeriodCounter = 1;
-}
-
-void AdlibChannel::enable() {
-	/* AdlibChannel_enable: mark pending-stop on active channels only. */
-	if (_activeCount == 0)
-		return;
-	_pendingStop = 0xFF;
-	_soundData = ADLIB_NULLDATA;
-}
-
-void AdlibChannel::processChannelFade() {
-	if (_activeCount == 0)
-		return;
-	if (_pendingStop == 0)
-		return;
-
-	/* Channel has fully faded when both velocity and volume are zero. */
-	if (_velocity == 0 && _volume == 0) {
-		_loopStartPtr = ADLIB_NULLDATA;
-		_pSrc = ADLIB_NULLDATA;
-		_pendingStop = 0;
-		return;
-	}
-
-	/* Arm a quick fade-down (period 2, step -1). */
-	_volumeFadeStep = 0xFF;
-	_fadePeriodReload = 2;
-	if (_fadePeriodCounter == 0)
-		_fadePeriodCounter = 1;
-}
-
-ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
-	int dataOffset, int dataSize)
-	: SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
-	AdlibChannel::_isDisabled = false;
-
-	/* Standard OPL timer-reset sequence. */
-	write(4, 0x60);
-	write(4, 0x80);
-	write(2, 0xFF);
-	write(4, 0x21);
-	write(4, 0x60);
-	write(4, 0x80);
-
-	Common::fill(_adlibPorts, _adlibPorts + 256, 0);
-	command0();
-
-	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer),
-		CALLBACKS_PER_SECOND);
-}
-
-int ASound::stop() {
-	command0();
-	int result = _pollResult;
-	_pollResult = 0;
-	return result;
-}
-
-int ASound::poll() {
-	update();
-	int result = _pollResult;
-	_pollResult = 0;
-	return result;
-}
-
-void ASound::noise() {
-	Common::StackLock slock(_driverMutex);
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		noise_inner(i);
-}
-
-int ASound::command0() {
-	/* Push _isDisabled, temporarily force to 0xFFFF while resetting. */
-	uint16 savedDisabled = _isDisabled;
-	_isDisabled = 0xFFFF;
-
-	/* 1. Reset all 9 channels. */
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		_channels[i]->reset();
-
-	/* 2. Mute all operator TL registers (0x40 down to 0x4F, via adlib_write2
-	 *    with value 0x3F, stepping from 0x4F down while reg >= 0x40). */
-	for (int reg = 0x4F; reg >= 0x40; --reg)
-		write((uint8)reg, 0x3F);
-
-	/* 3. Zero registers 0xFF down to 0x60. */
-	for (int reg = 0xFF; reg >= 0x60; --reg)
-		write((uint8)reg, 0x00);
-
-	/* 4. Zero registers 0x3F down to 0x01. */
-	for (int reg = 0x3F; reg >= 0x01; --reg)
-		write((uint8)reg, 0x00);
-
-	/* 5. Waveform Select Enable. */
-	write(0x01, 0x20);
-
-	/* 6. Reset tick callback. */
-	resetCallback();
-
-	_isDisabled = savedDisabled;
-	return 0;
-}
-
-int ASound::command1() {
-	/* Fade all channels: music 0-6 (command3) then SFX 7-8 (command5). */
-	command3();
-	command5();
-	return 0;
-}
-
-int ASound::command2() {
-	/* Hard-redirect music channels 0-6 to null stream with setPtr2. */
-	_channel0.setPtr2(ADLIB_NULLDATA);
-	_channel1.setPtr2(ADLIB_NULLDATA);
-	_channel2.setPtr2(ADLIB_NULLDATA);
-	_channel3.setPtr2(ADLIB_NULLDATA);
-	_channel4.setPtr2(ADLIB_NULLDATA);
-	_channel5.setPtr2(ADLIB_NULLDATA);
-	_channel6.setPtr2(ADLIB_NULLDATA);
-	return 0;
-}
-
-int ASound::command3() {
-	/* Pending-stop music channels 0-6 (natural envelope fade via enable). */
-	_channel0.enable();
-	_channel1.enable();
-	_channel2.enable();
-	_channel3.enable();
-	_channel4.enable();
-	_channel5.enable();
-	_channel6.enable();
-	return 0;
-}
-
-int ASound::command4() {
-	/* Hard-redirect SFX channels 7-8. */
-	_channel7.setPtr2(ADLIB_NULLDATA);
-	_channel8.setPtr2(ADLIB_NULLDATA);
-	return 0;
-}
-
-int ASound::command5() {
-	/* Pending-stop SFX channels 7-8. */
-	_channel7.enable();
-	_channel8.enable();
-	return 0;
-}
-
-int ASound::command6() {
-	/* Guard: already disabled. */
-	if (_isDisabled == 0xFFFF)
-		return 0;
-	_isDisabled = 0xFFFF;
-
-	/* Save each channel's _freqSweepCounter -> _savedSweepCounter, then zero it. */
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
-		AdlibChannel *ch = _channels[i];
-		ch->_savedSweepCounter = ch->_freqSweepCounter;
-		ch->_freqSweepCounter = 0;
-	}
-
-	/* Mute all 22 operator TL registers. */
-	for (int i = 0; i < 22; ++i)
-		adlib_channelOff(ALL_OP_TL_REGS[i]);
-
-	return 0;
-}
-
-int ASound::command7() {
-	/* Guard: only resume from a paused state. */
-	if (_isDisabled != 0xFFFF)
-		return 0;
-
-	/* Restore all operator volumes from the _adlibPorts shadow
-	 * (asound_playMusicC: just re-writes whatever was last written). */
-	for (int i = 0; i < 22; ++i)
-		adlib_channelOn(ALL_OP_TL_REGS[i]);
-
-	/* Restore each channel's sweep counter and check whether any was non-zero. */
-	uint8 anySweep = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
-		AdlibChannel *ch = _channels[i];
-		ch->_freqSweepCounter = ch->_savedSweepCounter;
-		anySweep |= ch->_savedSweepCounter;
-	}
-
-	if (anySweep != 0)
-		signalSoundPlaying();
-
-	_isDisabled = 0;
-	return 0;
-}
-
-int ASound::command8() {
-	/* Returns non-zero if any channel is currently active.
-	 * Clears byte_12393 (music-only flag) first so all 9 channels are checked. */
-	_musicOnlyFlag = 0;
-	uint8 result = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-int ASound::command18() {
-	/* Re-entrant background-music launcher (asound_command18 in the binary).
-	 * Fades everything, then dispatches back through the command table using
-	 * _musicIndex (word_12370) as the command ID. */
-	command1();
-	return command(_musicIndex, 0);
-}
-
-void ASound::callFunction(uint16 offset) {
-	error("Unsupported call to sound driver function at offset %.4x", offset);
-}
-
-void ASound::write(uint8 reg, uint8 value) {
-	_adlibPorts[reg] = value;
-	_opl->writeReg(reg, value);
-}
-
-void ASound::onTimer() {
-	Common::StackLock slock(_driverMutex);
-	poll();
-}
-
-uint16 ASound::getRandomNumber() {
-	/* asound_getRandomNumber: ax = ror3(0x9248 + seed). */
-	uint16 ax = (uint16)(0x9248u + _randomSeed);
-	ax = (uint16)((ax >> 3) | (ax << 13));
-	_randomSeed = ax;
-	return ax;
-}
-
-void ASound::adlib_channelOff(uint8 portIndex) {
-	/* sub_1018F: OR the register with 0x3F (force max attenuation),
-	 * then write back both to _adlibPorts and to the OPL chip.
-	 * Note: unlike the Phantom driver, the original value is NOT preserved
-	 * in _adlibPorts - the ORed value is stored back. */
-	uint8 val = _adlibPorts[portIndex] | 0x3F;
-	_adlibPorts[portIndex] = val;
-	_opl->writeReg(portIndex, val);
-}
-
-void ASound::adlib_channelOn(uint8 portIndex) {
-	/* asound_playMusicC: re-writes whatever _adlibPorts already holds.
-	 * command7 simply replays the shadow without recalculating anything. */
-	_opl->writeReg(portIndex, _adlibPorts[portIndex]);
-}
-
-void ASound::signalSoundPlaying() {
-	/* asound_signalSoundPlaying: set resultFlag/pollResult to non-zero. */
-	if (_resultFlag == (int16)0xFFFF)
-		return;
-	_resultFlag = (int16)0xFFFF;
-	_pollResult = (int16)0xFFFF;
-}
-
-void ASound::clearCallback() {
-	_callbackFnPtr = nullptr;
-	_callbackCounter = 0;
-	_callbackPeriod = 0;
-}
-
-void ASound::tickCallback() {
-	if (_callbackPeriod == 0)
-		return;
-	if (--_callbackCounter != 0)
-		return;
-
-	_callbackCounter = _callbackPeriod;
-	if (_callbackFnPtr == nullptr)
-		return;
-
-	auto fn = _callbackFnPtr;
-	_callbackFnPtr = nullptr;
-	(this->*fn)();
-}
-
-void ASound::writeVolume() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-
-	/* Compute combined attenuation step from volume and velocity. */
-	uint8  volIdx = ch->_volume;
-	uint8  velIdx = ch->_velocity;
-	int16  volStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[volIdx];
-	int16  velStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[velIdx];
-	int16  var4 = volStep + velStep - 1;   /* var_4: combined step (shared) */
-
-	/* Check _alg of the first sample to determine loop count. */
-	AdlibSample *smpFirst = &_samples[ch->_sampleIndex * 2];
-	int passes = (smpFirst->_alg == 0) ? 2 : 1;
-
-	/* si and di track the two TL values written per pass for caching. */
-	int16 finalSi = 0, finalDi = 0;
-
-	/* var_6 = loop counter (0, then 1 for the two-pass case).
-	 * For alg != 0 we jump straight into the carrier path (var_6 stays at
-	 * effectively 1: VOICE_SLOTS[ch][1]). */
-	for (int var6 = (passes == 1 ? 1 : 0); var6 < 2; ++var6) {
-
-		/* Reload var_2 = var_4 at the start of each pass (loc_11642). */
-		int16 var2 = var4;
-
-		/* Select the operator slot. */
-		uint8 slot = VOICE_SLOTS[chanNum][var6];
-		uint8 regOff = SLOT_TO_REG_OFFSET[slot];
-		uint16 tlReg = (uint16)regOff + 0x40;   /* var_8 */
-
-		/* KSL bits from the port shadow. */
-		int16 kslBits = (int16)((uint16)_adlibPorts[tlReg] & 0xC0);   /* var_A */
-
-		int16 si, di;
-
-		if (OPL_VERSION_FLAG < 0x18) {
-			/* ---- OPL2 simple path (loc_1167C / loc_1167C equivalent) ---- */
-			int16 tl = (int16)0x3F - var2;
-			tl |= kslBits;
-			si = tl;
-			di = tl;
-			/* adlib_write2(8, tlReg, tl) */
-			write((uint8)tlReg, (uint8)tl);
-		} else {
-			/* ---- OPL3 patch-attenuation path (loc_115BE / loc_116D4) ---- */
-			uint8 pa = ch->_patchAttenuation;
-
-			/* Modulator TL (first register, offset 0): */
-			int16 tlMod = (int16)(uint16)PATCH_ATTEN_TO_TL[pa];
-			/* -(tlMod - var2) = var2 - tlMod */
-			int16 atten1 = var2 - tlMod;   /* ax = si initially */
-			si = atten1;
-			if (si < 0)       si = 0;
-			else if (si > 63) si = 63;
-			int16 reg0val = ((int16)0x3F - si) | kslBits;
-			si = reg0val;
-			write((uint8)tlReg, (uint8)reg0val);
-
-			/* Carrier TL (second register, offset 2):
-			 * unk_12431 - bx (where bx = pa) == PATCH_ATTEN_TO_TL[127 - pa]. */
-			int16 tlCar = (int16)(uint16)PATCH_ATTEN_TO_TL[127 - pa];
-			/* di = var_2 - tlCar  (var_4 for alg!=0, var_2=var_4 reload for alg==0) */
-			di = var2 - tlCar;
-			int16 diClamped = di;
-			if (diClamped < 0)        diClamped = 0;
-			else if (diClamped > 0x3F) diClamped = 0x3F;
-			int16 reg2val = ((int16)0x3F - diClamped) | kslBits;
-			di = reg2val;
-			write((uint8)(tlReg + 2), (uint8)reg2val);
-		}
-
-		/* For the alg!=0 single-pass case we only run this once (var6==1).
-		 * For the alg==0 two-pass case, record both sets. */
-		if (var6 == 1) {
-			finalSi = si;
-			finalDi = di;
-		} else {
-			/* var6==0: record si/di for the mod pass; di is overwritten on
-			 * the carrier pass so we use si for the high byte of field_2A. */
-			finalSi = si;
-			finalDi = di;
-		}
-	}
-
-	/* Cache the written TL bytes in field_2A:
-	 *   dl = di & 0x3F  (carrier / second register)
-	 *   dh = si & 0x3F  (modulator / first register)
-	 * Packed as a word at offset 0x2A. */
-	ch->_cachedCarrierTL = (uint8)(finalDi & 0x3F);   /* dl -> field_2A low byte  */
-	/* The high byte (si & 0x3F) is stored in _savedFreqSweep (offset 0x2B)
-	 * by the assembly.  We write it separately to avoid clobbering the real
-	 * savedFreqSweep which lives there - the original asm stores both bytes
-	 * as a word at [bx+2Ah], meaning field_2A=dl and field_2B=dh=si&0x3F.
-	 * In our C++ layout _savedFreqSweep is at 0x2B; we shadow the dh byte
-	 * into it only when writing volume, consistent with the original. */
-	ch->_savedFreqSweep = (uint8)(finalSi & 0x3F);
-}
-
-void ASound::writeFrequency() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* Note is 1-based; _octaveTranspose shifts by whole octaves. */
-	int note = (int)ch->_note + (int)ch->_octaveTranspose - 1;
-	int octave = note / 12;
-	int semi = note % 12;
-	if (semi < 0) {
-		semi += 12; --octave;
-	}
-
-	/* F-number from table, with optional signed transpose offset. */
-	int16 fnum = (int16)SEMITONE_FREQ_TABLE[semi] + (int16)(int8)ch->_transpose;
-
-	write((uint8)aReg, (uint8)fnum);
-
-	uint8 bVal = _adlibPorts[bReg] & 0x20;          /* preserve key-on bit */
-	bVal |= (uint8)((fnum >> 8) & 0x03);            /* F-num bits 9:8 */
-	bVal |= (uint8)(((uint8)octave & 0x07) << 2);   /* block/octave bits 4:2 */
-	write((uint8)bReg, bVal);
-}
-
-void ASound::writePitchBend() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* Reconstruct the 10-bit F-number from the port shadow. */
-	uint16 fnum = (uint16)_adlibPorts[aReg]
-		| ((uint16)(_adlibPorts[bReg] & 0x1F) << 8);
-
-	int16 bent = (int16)fnum + (int16)(int8)ch->_pitchBend;
-
-	write((uint8)aReg, (uint8)bent);
-
-	/* Preserve block + key-on, update F-num[9:8]. */
-	uint8 bVal = _adlibPorts[bReg] & 0x20;
-	bVal |= (uint8)((bent >> 8) & 0x03);
-	write((uint8)bReg, bVal);
-}
-
-void ASound::writeArpeggio() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-	uint16 aReg = (uint16)chanNum + 0xA0;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-
-	/* dl = _note + _octaveTranspose + _writeVolumePending - 1 */
-	int note = (int)ch->_note + (int)ch->_octaveTranspose
-		+ (int)ch->_writeVolumePending - 1;
-	int octave = note / 12;
-	int semi = note % 12;
-	if (semi < 0) {
-		semi += 12; --octave;
-	}
-
-	uint16 freqEntry = SEMITONE_FREQ_TABLE[semi];
-	uint8  fnHigh = (uint8)((freqEntry >> 8) & 0x03);
-	uint8  block = (uint8)(octave & 0x07);
-	uint8  bVal = (uint8)((block << 2) | fnHigh) | (_adlibPorts[bReg] & 0x20);
-
-	write((uint8)aReg, (uint8)freqEntry);
-	write((uint8)bReg, bVal);
-}
-
-void ASound::updateOctave() {
-	uint8 chanNum = _activeChannelNumber;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-	write((uint8)bReg, _adlibPorts[bReg] & 0xDF);
-}
-
-void ASound::noteOn() {
-	AdlibChannel *ch = _activeChannelPtr;
-	AdlibSample *smp = &_samples[ch->_sampleIndex];
-
-	writeVolume();
-
-	ch->_freqSweepCounter = smp->_freqSweepInit;
-
-	if (ch->_freqSweepCounter != 0) {
-		/* Sweep is pending; signal that sound is active but don't key on yet. */
-		signalSoundPlaying();
-		return;
-	}
-
-	/* No sweep - write frequency and set key-on immediately. */
-	writeFrequency();
-
-	uint8 chanNum = _activeChannelNumber;
-	uint16 bReg = (uint16)chanNum + 0xB0;
-	write((uint8)bReg, _adlibPorts[bReg] | 0x20);
-}
-
-void ASound::writeSampleRegs() {
-	AdlibSample *smp = _samplePtr;
-	uint16 base = _currentOpBase;
-
-	/* 0xBD: rhythm mode flags. */
-	uint8 bdVal = _adlibPorts[0xBD] & 0x3F;
-	if (RHYTHM_HI_HAT) bdVal |= 0x80;
-	if (RHYTHM_CYMBAL) bdVal |= 0x40;
-	write(0xBD, bdVal);
-
-	/* 0x08: note-select / rhythm enable. */
-	write(0x08, RHYTHM_ENABLE ? 0x40 : 0x00);
-
-	/* 0xC0+voice: feedback and algorithm.
-	 * asm: feedback << 1, then alg==1 -> bit0=0, else -> bit0=1. */
-	write((uint8)(_activeChannelReg + 0xC0),
-		(uint8)((smp->_feedback << 1) | (smp->_alg == 1 ? 0 : 1)));
-
-	/* 0x60+base: attack / decay. */
-	write((uint8)(base + 0x60),
-		(uint8)(((smp->_attackRate & 0x0F) << 4) | (smp->_decayRate & 0x0F)));
-
-	/* 0x80+base: sustain level / release rate. */
-	write((uint8)(base + 0x80),
-		(uint8)(((smp->_sustainLevel & 0x0F) << 4) | (smp->_releaseRate & 0x0F)));
-
-	/* 0x20+base: AM/VIB/EGT/KSR/mult flags. */
-	uint8 amVal = smp->_freqMultiple & 0x0F;
-	if (smp->_egTyp == 1) amVal |= 0x20;
-	if (smp->_vib == 1) amVal |= 0x40;
-	if (smp->_ksr == 1) amVal |= 0x10;
-	if (smp->_ampMod == 1) amVal |= 0x80;
-	write((uint8)(base + 0x20), amVal);
-
-	/* 0xE0+base: waveform select. */
-	write((uint8)(base + 0xE0), (uint8)(smp->_waveformSelect & 0x03));
-}
-
-void ASound::loadSample() {
-	AdlibChannel *ch = _activeChannelPtr;
-	uint8 chanNum = _activeChannelNumber;
-
-	/* Phase 1: Silence modulator operator (slot 0). */
-	uint8 mSlot = VOICE_SLOTS[chanNum][0];
-	uint8 mRegOff = SLOT_TO_REG_OFFSET[mSlot];
-	write((uint8)(mRegOff + 0x80), 0xFF);
-	write((uint8)(mRegOff + 0x40), 63);
-
-	/* Phase 2: Silence carrier operator (slot 1). */
-	uint8 cSlot = VOICE_SLOTS[chanNum][1];
-	uint8 cRegOff = SLOT_TO_REG_OFFSET[cSlot];
-	write((uint8)(cRegOff + 0x80), 0xFF);
-	write((uint8)(cRegOff + 0x40), 63);
-
-	/* Phase 3: Write first sample (modulator) registers. */
-	_activeChannelReg = (uint16)chanNum;
-	_currentOpBase = mRegOff;
-	_samplePtr = &_samples[ch->_sampleIndex * 2];
-	writeSampleRegs();
-
-	/* Phase 4: Write second sample (carrier) registers. */
-	_currentOpBase = cRegOff;
-	_samplePtr = &_samples[ch->_sampleIndex * 2 + 1];
-	writeSampleRegs();
-
-	/* Phase 5: Write carrier TL (KSL | 0x3F for full attenuation initially). */
-	write((uint8)(cRegOff + 0x40),
-		(uint8)((_samplePtr->_scalingLevel << 6) | 0x3F));
-
-	/* Phase 6: Use the first sample again for the modulator TL and for
-	 * sweep/frequency initialisation. */
-	AdlibSample *smp3 = &_samples[ch->_sampleIndex * 2];
-	uint16        mTLReg = (uint16)mRegOff + 0x40;
-
-	if (smp3->_alg == 0) {
-		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | 0x3F));
-	} else {
-		/* alg != 0: negate (totalLevel - 63) to get the TL value. */
-		uint8 tl = (uint8)(-(int8)(smp3->_totalLevel - 63));
-		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | (tl & 0x3F)));
-	}
-
-	/* Copy sweep/frequency parameters from the sample into the channel. */
-	ch->_freqSweepCounter = smp3->_freqSweepInit;
-	ch->_noiseFreqMask = smp3->_freqMask;
-	ch->_freqAccum = smp3->_freqBase;
-	ch->_freqStep = smp3->_outerLoopPtr;
-}
-
-void ASound::update() {
-	if (_isDisabled)
-		return;
-
-	(void)getRandomNumber();
-	++_frameNumber2;
-
-	pollAllChannels();
-	tickCallback();       /* asound_updateCallback */
-	updateAllChannels();
-
-	_anySweepActive = 0;
-
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		update1(i);
-
-	/* If no sweep is active, flag the result as "done". */
-	if (!_anySweepActive) {
-		if (_resultFlag != (int16)0xFFFF) {
-			_resultFlag = (int16)0xFFFF;
-			_pollResult = (int16)0xFFFF;
-		}
-	}
-}
-
-void ASound::update1(int channelIndex) {
-	/* asound_update1: called with cx = channelIndex+1 in the original loop.
-	 * Accesses channel via the adlib_channels pointer table. */
-	AdlibChannel *ch = _channels[channelIndex];
-
-	if (ch->_freqSweepCounter == 0)
-		return;
-
-	_anySweepActive = 1;
-	ch->_freqAccum += ch->_freqStep;
-	ch->_freqSweepCounter--;
-
-	if (ch->_freqSweepCounter != 0)
-		return;
-
-	/* Sweep finished: write zero frequency to silence this OPL voice. */
-	uint8 voice = (uint8)channelIndex;
-	write((uint8)(voice + 0xA0), 0x00);
-	write((uint8)(voice + 0xB0), 0x00);
-}
-
-void ASound::setFrequency(uint8 voice, uint16 freq) {
-	/* asound_setFrequency: writes A0+v and B0+v (with key-on). */
-	write((uint8)(voice + 0xA0), (uint8)(freq & 0xFF));
-	uint8 bVal = (_adlibPorts[voice + 0xB0] & 0x20)
-		| (uint8)((freq >> 8) & 0x03)
-		| 0x20;
-	write((uint8)(voice + 0xB0), bVal);
-}
-
-void ASound::noise_inner(int channelIndex) {
-	/* adlib_noise_inner */
-	AdlibChannel *ch = _channels[channelIndex];
-
-	if (ch->_freqSweepCounter == 0)
-		return;
-
-	uint16 rnd = getRandomNumber();
-	uint16 freq = (rnd & ch->_noiseFreqMask) + ch->_freqAccum;
-	setFrequency((uint8)channelIndex, freq);
-}
-
-void ASound::updateAllChannels() {
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		_channels[i]->processChannelFade();
-}
-
-void ASound::findFreeChannel(byte *soundData) {
-	_findChannelMode = 0;
-
-	/* Scan channels 0-6 for an empty slot. */
-	for (int ch = 0; ch < 7; ++ch) {
-		if (_channels[ch]->_activeCount == 0) {
-			_channels[ch]->load(soundData);
-			return;
-		}
-	}
-
-	/* _findChannelMode==0: fall through to full search. */
-	findFreeChannelFull(soundData);
-}
-
-void ASound::findFreeChannelFull(byte *soundData) {
-	_findChannelMode = 2;
-
-	/* Scan channels 7-8 for an empty slot. */
-	if (_channel7._activeCount == 0) {
-		_channel7.load(soundData); return;
-	}
-	if (_channel8._activeCount == 0) {
-		_channel8.load(soundData); return;
-	}
-
-	/* Scan ch8 then ch7 for pending-stop (pre-emptible). */
-	if (_channel8._pendingStop == 0xFF) {
-		_channel8.load(soundData); return;
-	}
-	if (_channel7._pendingStop == 0xFF) {
-		_channel7.load(soundData); return;
-	}
-
-	/* If _findChannelMode allows it, scan ch6..ch0 for pending-stop. */
-	if (_findChannelMode == 0) {
-		if (_channel6._pendingStop == 0xFF) {
-			_channel6.load(soundData); return;
-		}
-		if (_channel5._pendingStop == 0xFF) {
-			_channel5.load(soundData); return;
-		}
-		if (_channel4._pendingStop == 0xFF) {
-			_channel4.load(soundData); return;
-		}
-		if (_channel3._pendingStop == 0xFF) {
-			_channel3.load(soundData); return;
-		}
-		if (_channel2._pendingStop == 0xFF) {
-			_channel2.load(soundData); return;
-		}
-		if (_channel1._pendingStop == 0xFF) {
-			_channel1.load(soundData); return;
-		}
-		if (_channel0._pendingStop == 0xFF) {
-			_channel0.load(soundData); return;
-		}
-	}
-}
-
-int ASound::isMusicChannelsActive() {
-	_musicOnlyFlag = 1;
-	uint8 result = 0;
-	for (int i = 0; i <= 6; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-int ASound::isAnyChannelActive() {
-	_musicOnlyFlag = 0;
-	uint8 result = 0;
-	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
-		result |= _channels[i]->_activeCount;
-	return result;
-}
-
-void ASound::pollAllChannels() {
-	_activeChannelNumber = 0;
-	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
-		_activeChannelPtr = _channels[ch];
-		pollActiveChannel();
-		/* Note: _activeChannelNumber is incremented at the end of
-		 * pollActiveChannel, not here. */
-	}
-}
-
-bool ASound::isSoundActive(byte *ptr) const {
-	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
-		if (_channels[ch]->_activeCount && _channels[ch]->_soundData == ptr)
-			return true;
-	}
-	return false;
-}
-
-uint16 ASound::readWord_impl() {
-	/* asound_readWord: advance pSrc past the low byte, read lo then hi. */
-	uint16 lo = *++pSrc;
-	uint16 hi = *++pSrc;
-	return lo | (hi << 8);
-}
-
-void ASound::pollActiveChannel() {
-	AdlibChannel *ch = _activeChannelPtr;
-
-	if (ch->_activeCount == 0) {
-		++_activeChannelNumber;
-		return;
-	}
-
-	/* byte_16A0A: volume-dirty flag.  Cleared here, set by various opcodes
-	 * and by the fade/vibrato sections; causes writeVolume at the end. */
-	bool volDirty = false;
-
-	/* ---- Key-on delay countdown ---- */
-	if (ch->_keyOnDelay != 0) {
-		ch->_keyOnDelay--;
-		ch = _activeChannelPtr;
-		if (ch->_keyOnDelay == 0)
-			updateOctave();
-	}
-
-	/* ---- Duration countdown ---- */
-	ch = _activeChannelPtr;
-	ch->_activeCount--;
-	ch = _activeChannelPtr;
-	if (ch->_activeCount != 0)
-		goto post_keyon;
-
-	/* ================================================================
-	 * Command-dispatch loop.
-	 * Re-entered (via goto dispatch) after each command that does not
-	 * consume a duration tick.  Falls through to the note-event path
-	 * when a note byte is encountered.
-	 * ================================================================ */
-dispatch:
-	{
-		ch = _activeChannelPtr;
-		pSrc = ch->_pSrc;
-
-		uint8 b = *pSrc;
-
-		if (!(b & 0x80))
-			goto note_event;
-
-		/* Decode group (si) and sub-opcode (di). */
-		uint8 si = b & 0x70;
-		uint8 di = b & 0x0F;
-
-		if (si == 0x00) {
-			/* ============================================================
-			 * opcodes1  (group 0, 0x8_)
-			 * Called with: pSrc pointing at the command byte.
-			 * On entry, opcodes1 does: inc pSrc (skip past command byte).
-			 * On exit, sets _pSrc = pSrc + 1 (def_10CDC).
-			 * ============================================================ */
-			pSrc++;   /* skip command byte */
-
-			switch (di) {
-
-			case 0x0: /* set sampleIndex, call loadSample */
-				ch->_sampleIndex = *pSrc;
-				loadSample();
-				volDirty = true;
-				/* def_10CDC: _pSrc = pSrc + 1 */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x1: /* set velocity */
-			{
-				uint8 vel = *pSrc;
-				if (ch->_pendingStop != 0) {
-					/* If current velocity (signed) > new vel (signed), clamp down. */
-					if ((int16)(int8)ch->_velocity > (int16)(int8)vel)
-						ch->_velocity = vel;
-				} else {
-					ch->_velocity = vel;
-				}
-				volDirty = true;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-
-			case 0x2: /* set volume */
-			{
-				uint8 vol = *pSrc;
-				if (ch->_pendingStop != 0) {
-					/* Don't raise volume above current when fading out. */
-					uint16 curVol = (uint16)ch->_volume;
-					if (curVol <= (uint16)vol)
-						goto op2_set_vol;
-					/* else fall through and skip the set */
-					volDirty = true;
-					ch->_pSrc = pSrc + 1;
-					goto dispatch;
-				}
-op2_set_vol:
-				ch->_volume = vol;
-				volDirty = true;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-
-			case 0x3: /* set patchAttenuation */
-				ch->_patchAttenuation = *pSrc;
-				volDirty = true;   /* opcodes1 case 3 jumps to loc_10B56 -> byte_16A0A=1 */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x4: /* set fade: fadePeriodReload, volumeFadeStep */
-				if (ch->_pendingStop != 0) {
-					/* Skip this opcode entirely when pending-stop is set. */
-					ch->_pSrc = pSrc + 2;
-					goto dispatch;
-				}
-				ch->_fadePeriodReload = *pSrc++;
-				ch->_volumeFadeStep = *pSrc;
-				ch->_fadePeriodCounter = 1;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x5: /* set vibrato: vibPeriodReload, vibPeriodCounter=reload, vibratoDepth, vibratoMode */
-				ch->_vibPeriodReload = *pSrc++;
-				ch->_vibPeriodCounter = ch->_vibPeriodReload;
-				ch->_vibratoDepth = *pSrc++;
-				ch->_vibratoMode = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x6: /* set transpose */
-				ch->_transpose = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x7: /* set octaveTranspose */
-				ch->_octaveTranspose = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x8: /* set field_11 (_writeVolumePending) */
-				ch->_writeVolumePending = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0x9: /* set arpeggio: field_12->field_C, pitchBend, field_13 */
-				ch->_arpPeriodReload = *pSrc++;
-				ch->_arpPeriodCounter = ch->_arpPeriodReload;   /* field_C = field_12 */
-				ch->_pitchBend = *pSrc++;
-				ch->_arpCounterReload = *pSrc;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0xA: /* skip forward by N+3 bytes (from the command byte) */
-			{
-				uint8 skip = *pSrc;
-				/* pSrc currently points at the byte after the command byte.
-				 * Advance _pSrc by (skip + 3) from the *command* byte position,
-				 * which is pSrc-1.  So _pSrc += skip + 3 from original _pSrc,
-				 * which was command byte.  But def_10CDC does _pSrc = pSrc+1
-				 * giving +2 past command.  Then skip+3 total from command: */
-				 /* asm: si = skip; ax = si + 3; add pSrc, ax; then def_10CDC
-				  * does pSrc+1.  So net advance from the command byte =
-				  * skip + 3 + 1 = skip + 4.  But pSrc already points one past
-				  * command byte, so _pSrc = pSrc + skip + 3. */
-				ch->_pSrc = pSrc + (uint16)skip + 3;
-				goto dispatch;
-			}
-
-			case 0xB: /* set durationOverride, clear noteOffset */
-				ch->_durationOverride = *pSrc;
-				ch->_noteOffset = 0;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			case 0xC: /* set noteOffset, clear durationOverride */
-				ch->_noteOffset = *pSrc;
-				ch->_durationOverride = 0;
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-
-			default:
-				/* Unknown sub-opcode: advance past the single operand byte. */
-				ch->_pSrc = pSrc + 1;
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x10) {
-			/* ============================================================
-			 * opcodes2  (group 1, 0x9_)
-			 * Loop control, restart, branch / call.
-			 * On exit: either sets _pSrc directly or falls to def_10EAC
-			 * (which just returns without touching _pSrc - the handlers
-			 * themselves are responsible for updating _pSrc).
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* inner loop */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_innerLoopCount == 0) {
-					pSrc++;   /* advance to count byte */
-					uint8 cnt = *pSrc;
-					if (cnt == 0) {
-						ch->_pSrc += 2;
-						ch = _activeChannelPtr;
-						ch->_innerLoopPtr = ch->_pSrc;
-						ch->_innerLoopCount = 0;
-						goto dispatch;
-					}
-					ch->_innerLoopCount = (uint16)cnt;
-					/* Jump to innerLoopPtr (loc_10D5A). */
-					ch->_pSrc = ch->_innerLoopPtr;
-					goto dispatch;
-				}
-				/* Count is non-zero: decrement. */
-				ch->_innerLoopCount--;
-				ch = _activeChannelPtr;
-				if (ch->_innerLoopCount == 0) {
-					/* Loop done: advance past the two-byte opcode. */
-					ch->_pSrc += 2;
-					ch = _activeChannelPtr;
-					ch->_innerLoopPtr = ch->_pSrc;
-					goto dispatch;
-				}
-				/* Still looping: jump back. */
-				ch->_pSrc = ch->_innerLoopPtr;
-				goto dispatch;
-			}
-
-			case 0x1: /* outer loop */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_outerLoopCount == 0) {
-					pSrc++;
-					uint8 cnt = *pSrc;
-					if (cnt == 0) {
-						ch->_pSrc += 2;
-						ch = _activeChannelPtr;
-						ch->_outerLoopPtr = ch->_pSrc;
-						ch->_innerLoopPtr = ch->_pSrc;
-						ch->_innerLoopCount = 0;
-						ch->_outerLoopCount = 0;
-						goto dispatch;
-					}
-					ch->_outerLoopCount = (uint16)cnt;
-					/* Jump to outerLoopPtr. */
-					ch->_pSrc = ch->_outerLoopPtr;
-					ch = _activeChannelPtr;
-					ch->_innerLoopPtr = ch->_outerLoopPtr;
-					goto dispatch;
-				}
-				/* Count non-zero: decrement. */
-				ch->_outerLoopCount--;
-				ch = _activeChannelPtr;
-				if (ch->_outerLoopCount == 0) {
-					ch->_pSrc += 2;
-					ch = _activeChannelPtr;
-					ch->_outerLoopPtr = ch->_pSrc;
-					ch->_innerLoopPtr = ch->_pSrc;
-					goto dispatch;
-				}
-				ch->_pSrc = ch->_outerLoopPtr;
-				ch = _activeChannelPtr;
-				ch->_innerLoopPtr = ch->_outerLoopPtr;
-				goto dispatch;
-			}
-
-			case 0x2: /* restart: reset all loop pointers to _soundData */
-			{
-				ch = _activeChannelPtr;
-				uint16 sd = (uint16)(uintptr_t)ch->_soundData;
-				ch->_loopStartPtr = ch->_soundData;
-				ch->_pSrc = ch->_soundData;
-				ch->_innerLoopPtr = ch->_soundData;
-				ch->_outerLoopPtr = ch->_soundData;
-				(void)sd;
-				goto dispatch;
-			}
-
-			case 0x3: /* set sound-data pointer (abs word offset), reset all loops */
-			{
-				byte *ptr = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_loopStartPtr = ptr;
-				ch->_pSrc = ptr;
-				ch->_innerLoopPtr = ptr;
-				ch->_outerLoopPtr = ptr;
-				ch->_soundData = ptr;
-				goto dispatch;
-			}
-
-			case 0x4: /* branch unconditional */
-			{
-				byte *dest = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_pSrc = dest;
-				goto dispatch;
-			}
-
-			case 0x5: /* branch with return-address save */
-			{
-				byte *dest = &_soundData[readWord_impl()];
-				ch = _activeChannelPtr;
-				ch->_branchTargetPtr = ch->_pSrc + 3;
-				ch->_pSrc = dest;
-				goto dispatch;
-			}
-
-			case 0x6: /* return from branch */
-			{
-				ch = _activeChannelPtr;
-				if (ch->_branchTargetPtr != nullptr) {
-					ch->_pSrc = ch->_branchTargetPtr;
-					ch->_branchTargetPtr = nullptr;
-				} else {
-					ch->_pSrc++;
-				}
-				goto dispatch;
-			}
-
-			default:
-				/* Unknown sub-opcode: no _pSrc advance (def_10EAC). */
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x20) {
-			/* ============================================================
-			 * opcodes3  (group 2, 0xA_)
-			 * Tempo/callback globals, random table operations,
-			 * indirect-table operations, call-by-address, nullsub.
-			 *
-			 * opcodes3 receives di (sub-opcode) but NOT the already-
-			 * incremented pSrc; each case increments pSrc itself as needed.
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* random pick from packed table -> write to stream */
-			{
-				/* Format: [cmd] [tblSize] [entry0..N-1] [targetSlot]
-				 * pSrc points at the command byte on entry. */
-				pSrc++;
-				uint8  tblSize = *pSrc;
-				pSrc++;   /* now at entry[0] */
-				(void)getRandomNumber();
-				uint16 rnd = _randomSeed & 0x7FFF;
-				int16  idx = (int16)((int16)rnd % (int16)(uint16)tblSize);
-				uint8  chosen = *(pSrc + idx);
-				uint8  target = *(pSrc + tblSize);
-				*(pSrc + (uint16)target + 1) = chosen;
-				ch = _activeChannelPtr;
-				ch->_pSrc += (uint16)tblSize + 3;
-				goto dispatch;
-			}
-
-			case 0x1: /* random range [lo..hi] -> write to stream */
-			{
-				/* Format: [cmd] [lo] [hi] [targetSlot] ... */
-				pSrc++;
-				uint8 lo = *pSrc++;
-				uint8 hi = *pSrc++;
-				uint16 range = (uint16)(hi - lo) + 1;
-				/* pSrc now at targetSlot byte. */
-				(void)getRandomNumber();
-				uint16 rnd = _randomSeed & 0x7FFF;
-				uint8  res = (uint8)((int16)rnd % (int16)range) + lo;
-				uint8  slot = *pSrc;
-				*(pSrc + (uint16)slot + 1) = res;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 4;   /* command+lo+hi+slot = 4 bytes consumed */
-				goto dispatch;
-			}
-
-			case 0x2: /* indirect table read from scriptVar -> write to stream */
-			{
-				/* Format: [cmd] [varIdx] [tableSlot] [table...] */
-				pSrc++;
-				uint8 varIdx = *pSrc++;
-				uint8 tableSlot = *pSrc++;
-				/* pSrc now points at the inline table. */
-				uint8 idxVal = _scriptVars[varIdx];
-				uint8 chosen = *(pSrc + idxVal);
-				uint8 target = *(pSrc + tableSlot);
-				*(pSrc + (uint16)target + 1) = chosen;
-				ch = _activeChannelPtr;
-				ch->_pSrc += (uint16)tableSlot + 4;
-				goto dispatch;
-			}
-
-			case 0x3: /* call function by address (near call in original) */
-			{
-				uint16 fnOffset = readWord_impl();
-				callFunction(fnOffset);
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x4: /* nullsub_1 with byte arg */
-			{
-				/* "call near ptr aAsoundDriverAn+33h" - the target is a no-op. */
-				pSrc++;
-				/* (void)*pSrc; */
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x5: /* advance _pSrc by 4 (from command byte) */
-			{
-				/* loc_10F55 is shared with case 1's epilogue: _pSrc += 4. */
-				ch = _activeChannelPtr;
-				ch->_pSrc += 4;
-				goto dispatch;
-			}
-
-			case 0x6: /* set word_124F2 (_tempoFineStep) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoFineStep = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x7: /* set word_124F0 (_tempoCoarseStep) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoCoarseStep = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x8: /* set word_124EE (_tempoPeriod), enable tick callback */
-			{
-				uint16 period = readWord_impl();
-				_tempoPeriod = period;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				_tempoEnabled = 1;
-				_tempoTickCounter = 1;
-				goto dispatch;
-			}
-
-			case 0x9: /* set word_124F4 (_tempoShift) */
-			{
-				pSrc++;
-				uint8 val = *pSrc;
-				_tempoShift = (uint16)val;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			default:
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x30) {
-			/* ============================================================
-			 * opcodes4  (group 3, 0xB_)
-			 * Script-variable load (imm or var), store to stream, inc, dec.
-			 * Each case increments pSrc itself, then sets _pSrc.
-			 *
-			 * Switch on di (0-4); di >= 5 falls through to return.
-			 * ============================================================ */
-			switch (di) {
-
-			case 0x0: /* var[dst] = imm */
-			{
-				pSrc++;
-				uint8 dst = *pSrc++;
-				uint8 imm = *pSrc;
-				_scriptVars[dst] = imm;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x1: /* var[dst] = var[src] */
-			{
-				pSrc++;
-				uint8 dst = *pSrc++;
-				uint8 src = *pSrc;
-				_scriptVars[dst] = _scriptVars[src];
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x2: /* stream[tableSlot+1] = var[src] */
-			{
-				/* asm: [bx+di+1] = cl, where bx=pSrc, di=tableSlot. */
-				pSrc++;
-				uint8 src = *pSrc++;
-				uint8 tableSlot = *pSrc;
-				*(pSrc + (uint16)tableSlot + 1) = _scriptVars[src];
-				ch = _activeChannelPtr;
-				ch->_pSrc += 3;
-				goto dispatch;
-			}
-
-			case 0x3: /* var[dst]++ */
-			{
-				pSrc++;
-				uint8 dst = *pSrc;
-				_scriptVars[dst]++;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			case 0x4: /* var[dst]-- */
-			{
-				pSrc++;
-				uint8 dst = *pSrc;
-				_scriptVars[dst]--;
-				ch = _activeChannelPtr;
-				ch->_pSrc += 2;
-				goto dispatch;
-			}
-
-			default:
-				goto dispatch;
-			}
-		}
-
-		if (si == 0x40) {
-			/* ============================================================
-			 * opcodes5  (group 4, 0xC_)
-			 * Script-variable ALU: add/sub/mul/div/rem/and/or/xor.
-			 *
-			 * Entry: inc pSrc, read dst (si), inc pSrc, read src (di).
-			 * Then _pSrc += 3 (command + dst + src).
-			 * 16 sub-opcodes (0-15).
-			 *
-			 * For sub-opcodes 0,2,4: src is an immediate (di from the byte).
-			 * For sub-opcodes 1,3,5: src is _scriptVars[di].
-			 * For 6-9: signed or unsigned division/remainder.
-			 * For 10-15: bitwise AND/OR/XOR, imm or var.
-			 * ============================================================ */
-			pSrc++;
-			uint8 dst = *pSrc++;
-			uint8 src = *pSrc;   /* immediate value or var index */
-			ch = _activeChannelPtr;
-			ch->_pSrc += 3;
-
-			switch (di) {
-			case 0x0: _scriptVars[dst] += src;                                       break;
-			case 0x1: _scriptVars[dst] += _scriptVars[src];                          break;
-			case 0x2: _scriptVars[dst] -= src;                                       break;
-			case 0x3: _scriptVars[dst] -= _scriptVars[src];                          break;
-			case 0x4: /* mul ax=di, mul [si] -> al stored */
-				_scriptVars[dst] = (uint8)((uint16)src * (uint16)_scriptVars[dst]);  break;
-			case 0x5:
-				_scriptVars[dst] = (uint8)((uint16)_scriptVars[src] * (uint16)_scriptVars[dst]); break;
-			case 0x6: /* signed idiv by imm -> quotient in al */
-				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] / (int8)src);      break;
-			case 0x7: /* unsigned div by var -> quotient in al */
-				_scriptVars[dst] = _scriptVars[dst] / _scriptVars[src];              break;
-			case 0x8: /* signed idiv by imm -> remainder in dl (stored to dst) */
-				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] % (int8)src);      break;
-			case 0x9: /* unsigned div by var -> remainder in ah (stored to dst) */
-				_scriptVars[dst] = _scriptVars[dst] % _scriptVars[src];              break;
-			case 0xA: _scriptVars[dst] &= src;                                       break;
-			case 0xB: _scriptVars[dst] &= _scriptVars[src];                          break;
-			case 0xC: _scriptVars[dst] |= src;                                       break;
-			case 0xD: _scriptVars[dst] |= _scriptVars[src];                          break;
-			case 0xE: _scriptVars[dst] ^= src;                                       break;
-			case 0xF: _scriptVars[dst] ^= _scriptVars[src];                          break;
-			default: break;
-			}
-			goto dispatch;
-		}
-
-		if (si == 0x50) {
-			/* ============================================================
-			 * opcodes6  (group 5, 0xD_)
-			 * Conditional branch: compare var[si] vs immediate (di as imm byte).
-			 *
-			 * Format: [cmd] [varIdx] [imm] [addrLo] [addrHi]
-			 * Entry: inc pSrc, read varIdx (si reg), inc pSrc, read imm (di reg).
-			 * Comparison: var[varIdx] vs imm.
-			 *
-			 * If condition met: call asound_readWord (reads addrLo/addrHi
-			 *   from current pSrc position), set _pSrc = &_soundData[addr].
-			 * If not met: _pSrc += 5 (command+varIdx+imm+addr word = 5 bytes).
-			 *
-			 * var_2 is used as a flag: 0 = branch taken, 1 = not taken.
-			 * (Confusingly, var_2 = 1 means "taken" in opcodes7 but
-			 *  0 means "taken" in opcodes6 - see def_11292 / def_11360 difference.)
-			 *
-			 * opcodes6: var_2=1 -> taken (call readWord, set pSrc),
-			 *            var_2=0 -> not taken (_pSrc += 5).
-			 * ============================================================ */
-			pSrc++;
-			uint8 varIdx = *pSrc++;
-			uint8 imm = *pSrc;
-			uint8 vval = _scriptVars[varIdx];
-			bool  taken = false;
-
-			switch (di) {
-			case 0x0: taken = ((uint16)vval == (uint16)imm);                    break; /* == */
-			case 0x1: taken = ((uint16)vval != (uint16)imm);                    break; /* != */
-			case 0x2: taken = ((int16)(uint16)vval >= (int16)(uint16)imm);      break; /* jge -> taken if >= */
-			case 0x3: taken = ((int16)(uint16)vval <= (int16)(uint16)imm);      break; /* jle -> taken if <= */
-			case 0x4: taken = (_scriptVars[varIdx] != _scriptVars[imm]);        break; /* jnz after cmp [di],[si] */
-			case 0x5: taken = (_scriptVars[varIdx] == _scriptVars[imm]);        break; /* jz  after cmp [di],[si] */
-			case 0x6: taken = (_scriptVars[imm] <= _scriptVars[varIdx]);        break; /* jbe -> [di]<=[si] taken */
-			case 0x7: taken = (_scriptVars[imm] >= _scriptVars[varIdx]);        break; /* jnb -> [di]>=[si] taken */
-			default:  break;
-			}
-
-			ch = _activeChannelPtr;
-			if (taken) {
-				ch->_pSrc = &_soundData[readWord_impl()];
-			} else {
-				ch->_pSrc += 5;
-			}
-			goto dispatch;
-		}
-
-		if (si == 0x60) {
-			/* ============================================================
-			 * opcodes7  (group 6, 0xE_)
-			 * Conditional branch: compare var[si] vs var[di] (both scriptVars).
-			 *
-			 * Format: [cmd] [varIdxA] [varIdxB] [addrLo] [addrHi]
-			 * Entry: inc pSrc, read varIdxA (si), inc pSrc, read varIdxB (di).
-			 * var_2 = 0 initially.
-			 *
-			 * If condition met: var_2 = 1 -> branch taken:
-			 *   _pSrc = pSrc + 5; _branchTargetPtr = pSrc + 5;
-			 *   call readWord -> _pSrc = &_soundData[addr].
-			 * If not met (var_2 = 0): _pSrc += 5 (skip condition + word).
-			 *
-			 * Note: unlike opcodes6, opcodes7 DOES save a branch-target
-			 * (_branchTargetPtr = _pSrc + 5) on the taken path.
-			 * ============================================================ */
-			pSrc++;
-			uint8 idxA = *pSrc++;
-			uint8 idxB = *pSrc;
-			uint8 va = _scriptVars[idxA];
-			uint8 vb = (uint8)idxB;   /* di byte used as an immediate in some cases... */
-
-			/* Actually in opcodes7 the layout is var vs var:
-			 * si = varIdxA, di = varIdxB (both are variable indices). */
-			vb = _scriptVars[idxB];
-
-			bool taken = false;
-			switch (di & 0x07) {
-			case 0x0: taken = ((uint16)va != (uint16)vb);  break; /* jnz after cmp ax,di (case 0: jz -> NOT taken if ==; var_2 stays 0; but then def path: var_2==0 -> skip. Wait - let me re-read.) */
-				/* Re-reading loc_112F0: cmp ax,di; jz loc_11352 (-> var_2=1=taken).
-				 * So case 0: taken = (va == vb). */
-			default: break;
-			}
-
-			/* Actually re-reading carefully:
-			 * case 0 (loc_112F0): cmp ax,di; jz -> loc_11352 (var_2=1, taken)
-			 *                      else -> loc_112FA (ax=0, var_2=0, not taken)
-			 * case 1 (loc_11302): cmp ax,di; jz -> loc_1130A -> (jz loc_112FA, not taken)
-			 *                     else -> loc_11284 (var_2=1, taken)
-			 * -> case 1: taken = (va != vb)
-			 * case 2 (loc_1130E): jge -> loc_112FA (not taken); else loc_11284 (taken)
-			 * -> taken = (va < vb) (signed)
-			 * case 3 (loc_1131A): jle -> not taken; else taken
-			 * -> taken = (va > vb) (signed)
-			 * case 4 (loc_11326): cmp [di],al; jnz -> loc_112FA (not taken); else loc_11284 (taken)
-			 * -> taken = (_scriptVars[idxB] == va) (already same as case 0 with vars swapped)
-			 * case 5 (loc_11332): cmp [di],al; jz->loc_1130A (not taken if ==, taken if !=)
-			 * -> taken = (_scriptVars[idxB] != va)
-			 * case 6 (loc_1133C): jbe -> not taken; else taken -> taken = (_scriptVars[idxB] > va)
-			 * case 7 (loc_11348): jnb -> not taken; else taken -> taken = (_scriptVars[idxB] < va)
-			 */
-			switch (di) {
-			case 0x0: taken = (va == vb);               break;
-			case 0x1: taken = (va != vb);               break;
-			case 0x2: taken = ((int8)va < (int8)vb);   break;
-			case 0x3: taken = ((int8)va > (int8)vb);   break;
-			case 0x4: taken = (vb == va);               break;
-			case 0x5: taken = (vb != va);               break;
-			case 0x6: taken = (vb > va);                break;
-			case 0x7: taken = (vb < va);                break;
-			default:  break;
-			}
-
-			ch = _activeChannelPtr;
-			if (taken) {
-				/* Save return target (pSrc+5 from the command byte). */
-				ch->_branchTargetPtr = ch->_pSrc + 5;
-				ch->_pSrc = &_soundData[readWord_impl()];
-			} else {
-				ch->_pSrc += 5;
-			}
-			goto dispatch;
-		}
-
-		/* Unknown group: skip. */
-		goto dispatch;
-	}
-
-note_event:
-	{
-		/* ---- Note event: [note][duration] ---- */
-		ch = _activeChannelPtr;
-		pSrc = ch->_pSrc;
-		ch->_note = *pSrc;
-		pSrc++;
-		ch->_activeCount = *pSrc;
-		pSrc++;
-		ch->_pSrc += 2;
-
-		ch = _activeChannelPtr;
-		if (ch->_note == 0 || ch->_activeCount == 0) {
-			updateOctave();
-			goto post_keyon;
-		}
-
-		/* ---- Key-on ---- */
-		ch = _activeChannelPtr;
-		if (ch->_durationOverride != 0)
-			ch->_keyOnDelay = ch->_durationOverride;
-		else
-			ch->_keyOnDelay = ch->_activeCount - ch->_noteOffset;
-		noteOn();
-	}
-
-post_keyon:
-	{
-		ch = _activeChannelPtr;
-
-		/* ---- Arpeggio counter (field_13 / _arpCounterReload) ---- */
-		if (ch->_arpCounterReload != 0) {
-			/* Decrement the arpeggio period counter (field_C / _arpPeriodCounter). */
-			ch->_arpPeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_arpPeriodCounter == 0) {
-				/* Reload from field_12 (_arpPeriodReload). */
-				ch->_arpPeriodCounter = ch->_arpPeriodReload;
-				/* Call sub_117E8 (writeArpeggio - writes the arpeggio frequency). */
-				writeArpeggio();
-			}
-			ch = _activeChannelPtr;
-			/* Decrement the repeat counter unless it is the infinite sentinel 0xFF. */
-			if (ch->_arpCounterReload != 0xFF)
-				ch->_arpCounterReload--;
-		}
-
-		/* ---- Write-volume pending (field_11 / _writeVolumePending) ---- */
-		ch = _activeChannelPtr;
-		if (ch->_writeVolumePending != 0) {
-			/* sub_11856 was already called by writeArpeggio above (or this is
-			 * a standalone field_11 set via opcode 8).  Clear the flag. */
-			writeArpeggio();
-			ch = _activeChannelPtr;
-			ch->_writeVolumePending = 0;
-		}
-
-		/* ---- Fade / volume step ---- */
-		ch = _activeChannelPtr;
-		if (ch->_fadePeriodCounter != 0) {
-			ch->_fadePeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_fadePeriodCounter == 0) {
-				ch->_fadePeriodCounter = ch->_fadePeriodReload;
-				ch = _activeChannelPtr;
-				if (ch->_volumeFadeStep != 0) {
-					if (ch->_pendingStop != 0) {
-						/* Pending-stop fade: step velocity and volume down. */
-						if (ch->_velocity > 0)
-							ch->_velocity += ch->_volumeFadeStep;
-						ch = _activeChannelPtr;
-						if (ch->_volume != 0)
-							ch->_volume += ch->_volumeFadeStep;
-					} else {
-						/* Normal fade: step velocity, clamp to [0, 0x7F]. */
-						if ((int8)ch->_volumeFadeStep > 0) {
-							ch->_velocity += ch->_volumeFadeStep;
-							ch = _activeChannelPtr;
-							if ((uint16)ch->_velocity > 0x7F)
-								ch->_velocity = 0x7F;
-						} else {
-							ch->_velocity += ch->_volumeFadeStep;
-							ch = _activeChannelPtr;
-							if ((int8)ch->_velocity < 0)
-								ch->_velocity = 0;
-						}
-					}
-					volDirty = true;   /* byte_16A0A = 1 */
-				}
-			}
-		}
-
-		/* ---- Vibrato ---- */
-		ch = _activeChannelPtr;
-		if (ch->_vibPeriodCounter != 0) {
-			ch->_vibPeriodCounter--;
-			ch = _activeChannelPtr;
-			if (ch->_vibPeriodCounter == 0) {
-				ch->_vibPeriodCounter = ch->_vibPeriodReload;
-				ch = _activeChannelPtr;
-
-				if ((int8)ch->_vibratoDepth > 0) {
-					/* Positive vibrato: ceiling at 0x7F. */
-					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
-					if (sum > 0x7F) {
-						if (ch->_vibratoMode == 0xFF) {
-							/* One-shot: clamp and stop. */
-							ch->_vibratoDepth = 0;
-							ch->_patchAttenuation = 0x7F;
-						} else if (sum == 0x80 && ch->_vibratoDepth != 1) {
-							/* Soft bounce at 0x80: patchAtten = 0x7F - vibratoDepth. */
-							ch->_patchAttenuation = 0x7F - ch->_vibratoDepth;
-						} else {
-							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
-						}
-					}
-					ch = _activeChannelPtr;
-					ch->_patchAttenuation += ch->_vibratoDepth;
-					volDirty = true;
-
-				} else if ((int8)ch->_vibratoDepth < 0) {
-					/* Negative vibrato: floor at 0. */
-					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
-					/* Note: sum is uint8 wrapping, but the asm uses "or al,al; jge"
-					 * treating the result as signed. */
-					if ((int8)sum < 0) {
-						if (ch->_vibratoMode == 0xFF) {
-							ch->_vibratoDepth = 0;
-							ch->_patchAttenuation = 0;
-						} else if (sum == 0xFF && ch->_vibratoDepth != 0xFF) {
-							/* Soft bounce: patchAtten = neg(vibratoDepth). */
-							ch->_patchAttenuation = (uint8)(-(int8)ch->_vibratoDepth);
-						} else {
-							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
-						}
-					}
-					ch = _activeChannelPtr;
-					ch->_patchAttenuation += ch->_vibratoDepth;
-					volDirty = true;
-				}
-				/* vibratoDepth == 0: nothing to do. */
-			}
-		}
-
-		if (volDirty)
-			writeVolume();
-	}
-
-	++_activeChannelNumber;
-}
-
-void ASound::playSound(int offset) {
-	findFreeChannelFull(loadData(offset));
-}
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
diff --git a/engines/mads/madsv2/forest/asound.h b/engines/mads/madsv2/forest/asound.h
deleted file mode 100644
index 32fefa08461..00000000000
--- a/engines/mads/madsv2/forest/asound.h
+++ /dev/null
@@ -1,649 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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 3 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/>.
- *
- */
-
-#ifndef MADS_FOREST_ASOUND_H
-#define MADS_FOREST_ASOUND_H
-
-#include "common/mutex.h"
-#include "common/queue.h"
-#include "common/util.h"
-#include "mads/core/sound_manager.h"
-
-namespace MADS {
-namespace MADSV2 {
-namespace Forest {
-
-#define ADLIB_CHANNEL_COUNT 9
-
-struct AdlibChannel {
-	static bool _isDisabled;
-
-	// ---- byte fields (offsets 0x00 - 0x13) --------------------------------
-
-	uint8  _activeCount = 0; // 0x00  duration tick countdown; 0 = channel idle
-	uint8  _pitchBend = 0; // 0x01  signed pitch-bend offset applied by writePitchBend
-	uint8  _volumeFadeStep = 0; // 0x02  signed per-period volume/velocity delta (0xFF = fade out)
-	uint8  _vibratoDepth = 0; // 0x03  signed LFO depth; negated each period to oscillate
-	uint8  _note = 0; // 0x04  MIDI note number for current event
-	uint8  _sampleIndex = 0; // 0x05  index into _samples[] selecting the OPL patch
-	uint8  _volume = 0; // 0x06  channel volume (0-127)
-	uint8  _noteOffset = 0; // 0x07  subtracted from _activeCount to shorten note duration
-	uint8  _durationOverride = 0; // 0x08  when non-zero, overrides the stream's duration byte
-	uint8  _keyOnDelay = 0; // 0x09  countdown before the OPL key-on bit is cleared (gate time)
-	uint8  _fadePeriodCounter = 0; // 0x0A  counts down to 0 before applying _volumeFadeStep
-	uint8  _fadePeriodReload = 0; // 0x0B  reload value for _fadePeriodCounter
-	uint8  _arpPeriodCounter = 0; // 0x0C  arpeggio tick counter (reloaded from _arpPeriodReload)
-	uint8  _vibPeriodCounter = 0; // 0x0D  LFO tick counter; when it reaches 0 a vibrato step fires
-	uint8  _vibPeriodReload = 0; // 0x0E  reload value for _vibPeriodCounter
-	uint8  _patchAttenuation = 0; // 0x0F  per-note attenuation offset added on top of the patch TL
-	uint8  _velocity = 0; // 0x10  note velocity (0-127); used together with _volume for TL
-	uint8  _writeVolumePending = 0; // 0x11  non-zero -> call sub_11856 (secondary freq write) this tick
-	uint8  _arpPeriodReload = 0; // 0x12  arpeggio period reload value (set by opcode 9)
-	uint8  _arpCounterReload = 0; // 0x13  arpeggio counter reload (decremented; 0xFF = infinite)
-
-	// ---- pointer fields (offsets 0x14 - 0x1F, word-sized in original) ----
-
-	byte *_loopStartPtr = nullptr; // 0x14  start of the current loop body
-	byte *_pSrc = nullptr; // 0x16  current read pointer into the sound-data stream
-	byte *_innerLoopPtr = nullptr; // 0x18  inner-loop restart address
-	byte *_outerLoopPtr = nullptr; // 0x1A  outer-loop restart address
-	byte *_soundData = nullptr; // 0x1C  base address of this channel's sound-data block
-	byte *_branchTargetPtr = nullptr; // 0x1E  target of the most recent branch/jump opcode
-
-	// ---- word counter fields (offsets 0x20 - 0x29) ----------------------
-
-	uint16 _innerLoopCount = 0; // 0x20  remaining inner-loop iterations (0 = infinite until opcode)
-	uint16 _outerLoopCount = 0; // 0x22  remaining outer-loop iterations (0 = infinite until opcode)
-	uint16 _noiseFreqMask = 0; // 0x24  AND mask applied to the random number in noise mode
-	uint16 _freqAccum = 0; // 0x26  frequency sweep accumulator (base frequency + swept offset)
-	uint16 _freqStep = 0; // 0x28  per-tick increment added to _freqAccum during a sweep
-
-	// ---- sweep / pause state (offsets 0x2A - 0x2D) ----------------------
-
-	// 0x2A-0x2B  Last computed OPL total-level bytes written by writeVolume.
-	//            lo byte (0x2A) = carrier TL nibble; hi byte (0x2B) = modulator TL nibble.
-	//            Written at the end of asound_writeVolume so that command7 (resume) can
-	//            restore operator levels without a full recalculation.
-	uint8  _cachedCarrierTL = 0; // 0x2A  cached carrier total-level (6-bit, 0 = loudest)
-	uint8  _savedFreqSweep = 0; // 0x2B  copy of _freqSweepCounter saved by command6 (pause)
-	uint8  _freqSweepCounter = 0; // 0x2C  countdown for frequency-sweep ticks; 0 = sweep done
-	uint8  _savedSweepCounter = 0; // 0x2D  second save slot: _freqSweepCounter is mirrored here
-	//       by command6/7 so command7 can restore the live value
-
-// ---- tuning / control (offsets 0x2E - 0x31) ------------------------
-
-	uint8  _transpose = 0; // 0x2E  semitone offset added when looking up _semitoneFreqTable
-	uint8  _octaveTranspose = 0; // 0x2F  octave shift: added to _note before octave calculation
-	uint8  _pendingStop = 0; // 0x30  0xFF = channel is fading out and will go idle when silent
-	uint8  _vibratoMode = 0; // 0x31  0xFF = one-shot vibrato (clamp & stop); else = continuous
-
-	/**
-	 * Zeroes the "live" fields of one channel without touching the
-	 * loop/sound-data pointers (those are handled separately by load).
-	 */
-	void reset();
-
-	/**
-	 * Clears the entire channel struct, then initialises all loop/source
-	 * pointers to the supplied sound-data address, sets _patchAttenuation to
-	 * 0x40 (maximum, silent), and marks the channel active (_activeCount = 1).
-	 */
-	void load(byte *soundData);
-
-	/**
-	 * Redirects _loopStartPtr and _pSrc to the null/silence stream and arms a
-	 * one-step volume fade (_volumeFadeStep = 0xFF, _fadePeriodReload/Counter = 1)
-	 * so the channel fades out gracefully over the next tick.
-	 */
-	void setPtr2(byte *ptr);
-
-	/**
-	 * Marks the channel pending-stop (_pendingStop = 0xFF) and redirects
-	 * _soundData to the null/silence stream.  Unlike setPtr2 this does not
-	 * touch _volumeFadeStep; the channel fades naturally via processChannelFade.
-	 */
-	void enable();
-
-	/**
-	 * Called every frame for each channel.  When _pendingStop is set, waits
-	 * until both _velocity and _volume are zero before marking the channel idle
-	 * (_activeCount = 0); otherwise applies _volumeFadeStep to velocity/volume
-	 * each _fadePeriodReload ticks and clamps the result to [0, 0x7F].
-	 */
-	void processChannelFade();
-};
-
-// ---------------------------------------------------------------------------
-// AdlibSample  (unchanged from Return of the Phantom, 0x16 bytes)
-// ---------------------------------------------------------------------------
-struct AdlibSample {
-	uint8  _attackRate = 0;
-	uint8  _decayRate = 0;
-	uint8  _sustainLevel = 0;
-	uint8  _releaseRate = 0;
-	uint8  _egTyp = 0;
-	uint8  _ksr = 0;
-	uint8  _totalLevel = 0;
-	uint8  _scalingLevel = 0;
-	uint8  _waveformSelect = 0;
-	uint8  _freqMultiple = 0;
-	uint8  _feedback = 0;
-	uint8  _ampMod = 0;
-	uint8  _vib = 0;
-	uint8  _alg = 0;
-	uint8  _freqSweepInit = 0; // initial _freqSweepCounter value; 0 = immediate key-on
-	uint8  _reserved = 0;
-	uint16 _freqMask = 0; // loaded into channel _noiseFreqMask
-	uint16 _freqBase = 0; // loaded into channel _freqAccum
-	uint16 _outerLoopPtr = 0; // loaded into channel _freqStep
-
-	AdlibSample() {
-	}
-	AdlibSample(Common::SeekableReadStream &s);
-};
-
-// ---------------------------------------------------------------------------
-// ASound  -  Dragonsphere Adlib sound driver base class
-//
-// Command dispatch table layout (from off_11A14 / off_11A26 / off_11A2E /
-// funcs_12251 / off_11A64 in the disassembly):
-//
-//   Table 1  off_11A14  commands  0- 8   (max=8,    base=0,    9 entries)
-//   Table 2  off_11A26  commands 16-19   (max=0x13, base=0x10, 4 entries)
-//             command16  = background-music dispatcher (calls command18)
-//             command17  = play specific piece (loads 7 channels direct)
-//             command18  = re-entrant music launcher (reads word_12370 to pick a sub-command)
-//             command19  = no-op (asound_command98)
-//   Table 3  off_11A2E  commands 24-32   (max=0x20, base=0x18, 9 entries)
-//   Table 4  funcs_12251 commands 32-49  (max=0x31, base=0x20, 18 entries)
-//             Includes asound_command32-48 (music pieces / SFX loaders)
-//             and asound_command98 (no-op) in the last slot.
-//   Table 5  off_11A64  commands 64-101  (max=0x65, base=0x40, 38 entries)
-//             Includes asound_command64-101 (single-shot SFX loaders via
-//             findFreeChannel / findFreeChannelFull) and two no-ops.
-//
-// The driver also exposes:
-//   asound_command90 / 91  - two-voice SFX (findFreeChannelFull x2)
-//   asound_command95       - four-voice music piece (findFreeChannel x4)
-//   sub_11F98              - two-voice SFX (findFreeChannelFull x2)
-//
-// word_12370 tracks the "current music index" used by command18 to select
-// which music-piece loader to call.
-// ---------------------------------------------------------------------------
-class ASound : public SoundDriver {
-protected:
-	/** Member-function pointer type for deferred sound-loader callbacks. */
-	typedef void (ASound::*CallbackFunction)();
-
-private:
-	// ---- callback / tick state ------------------------------------------
-	uint16 _callbackCounter = 0;  // per-tick countdown
-	uint16 _callbackPeriod = 0;  // reload value; 0 = callback disabled
-	// Pointer to the deferred sound-loader called when the
-	// counter fires (stored as uint16 in the original; typed as a member
-	// function pointer in the C++ port).
-	CallbackFunction _callbackFnPtr = nullptr;
-
-	// ---- active-channel context (set by pollAllChannels) ----------------
-	AdlibChannel *_activeChannelPtr = nullptr;
-	uint8          _activeChannelNumber = 0;
-	uint16         _activeChannelReg = 0; // 0xA0+ch register for the active channel
-	uint16         _currentOpBase = 0; // operator register base for the active channel
-
-	// ---- per-frame working state ----------------------------------------
-	AdlibSample *_samplePtr = nullptr; // patch being loaded/written
-	byte *pSrc = nullptr; // current read pointer (mirrors channel _pSrc)
-	int16          _pollResult = 0;
-	int16          _resultFlag = 0;
-	uint16         _randomSeed = 0x4D2;
-
-	// ---- driver-wide flags ----------------------------------------------
-	uint16 _isDisabled = 0; // non-zero while the engine is paused (command6)
-	uint8  _findChannelMode = 0; // 0=full search, 1=ch0-5 only, 2=ch6-8 then pending
-
-	// ---- per-channel sweep shadows (for channel 5 special-casing) -------
-	//      Not present as separate globals in Dragonsphere; the channel
-	//      struct fields _savedFreqSweep / _savedSweepCounter handle this.
-
-	// ---- misc globals ---------------------------------------------------
-	uint8  _anySweepActive = 0; // set to 1 if any channel has _freqSweepCounter > 0
-	int    _frameNumber2 = 0; // secondary frame counter incremented every update
-
-	// ---- script / sequencer registers -----------------------------------
-	uint8  _scriptVars[32] = {}; // byte_16A10: 32 general-purpose script registers
-
-	// ---- music-index tracker (word_12370) --------------------------------
-	// Tracks which music piece was last launched by command18.
-	uint16 _musicIndex = 0;
-
-	// ---- tempo / sequencer state (from opcodes3 group 2 handlers) --------
-	uint8  _musicOnlyFlag = 0;     // byte_12393: 1=music-only check, 0=all channels
-	uint16 _tempoFineStep = 0;     // word_124F2: fine tempo step (opcode A6)
-	uint16 _tempoCoarseStep = 0;   // word_124F0: coarse tempo step (opcode A7)
-	uint16 _tempoPeriod = 0;       // word_124EE: tempo period in ticks (opcode A8)
-	uint8  _tempoEnabled = 0;      // non-zero when tempo tick is active (opcode A8)
-	uint16 _tempoTickCounter = 0;  // countdown for tempo callback
-	uint16 _tempoShift = 0;        // word_124F4: tempo shift (opcode A9)
-
-	// =========================================================================
-	// Private helpers
-	// =========================================================================
-
-	/** Timer callback entry point; calls update(). */
-	void onTimer();
-
-	/** Zeros _callbackFnPtr, _callbackCounter, and _callbackPeriod. */
-	void clearCallback();
-	void resetCallback() {
-		clearCallback();
-	}
-
-	/**
-	 * Forces one OPL operator to maximum attenuation (total-level = 0x3F)
-	 * while preserving its upper KSL bits.  'portIndex' is the register index
-	 * into _adlibPorts (i.e. the 0x40-range operator register).
-	 * Used by command6 to mute all operator pairs during a pause.
-	 */
-	void adlib_channelOff(uint8 portIndex);
-
-	/**
-	 * Computes and writes the total-level registers for the active channel
-	 * (asound_writeVolume).
-	 *
-	 * Two top-level paths depending on the _alg field of the first sample:
-	 *   _alg == 0  (FM): both operators contribute; loop twice writing
-	 *              VOICE_SLOTS[ch][0] then VOICE_SLOTS[ch][1].
-	 *   _alg != 0  (additive): only the carrier (slot 1) carries volume.
-	 *
-	 * Within each pass, two inner paths based on OPL version:
-	 *   < 0x18  (OPL2): TL = clamp(0x3F - var_2, 0, 0x3F).
-	 *   >= 0x18 (OPL3): patch-attenuation-aware mapping using
-	 *              PATCH_ATTEN_TO_TL and VOL_VEL_TO_ATTEN_STEP tables;
-	 *              writes two registers (offset 0 and offset 2).
-	 *
-	 * Caches the two written TL bytes in _cachedCarrierTL / _savedFreqSweep
-	 * so command7 can restore levels without a full recalculation.
-	 */
-	void writeVolume();
-
-	/**
-	 * Derives the OPL F-number and block (octave) from _note, _octaveTranspose,
-	 * and _transpose using _semitoneFreqTable, then writes registers 0xA0+ch
-	 * and 0xB0+ch (with key-on bit set).
-	 */
-	void writeFrequency();
-
-	/**
-	 * Applies _pitchBend as a signed offset to the already-computed frequency
-	 * registers without a full note recalculation.
-	 */
-	void writePitchBend();
-
-	/**
-	 * Arpeggio frequency write (sub_11856).
-	 * Called from pollActiveChannel when _writeVolumePending (field_11) is set.
-	 * Computes a modified frequency from _note + _octaveTranspose + field_11 - 1
-	 * and writes it to the OPL registers, preserving the key-on bit.
-	 */
-	void writeArpeggio();
-
-	/**
-	 * Clears the key-on bit (bit 5) of the 0xB0+ch register, silencing the
-	 * note while preserving pitch.  Called during _keyOnDelay countdown.
-	 */
-	void updateOctave();
-
-	/**
-	 * Arms the frequency sweep counter from the sample definition, then - if
-	 * _freqSweepCounter is zero - immediately triggers the note by writing
-	 * volume and frequency registers and setting the key-on bit.
-	 */
-	void noteOn();
-
-	/**
-	 * Writes all OPL operator registers for the patch pointed to by _samplePtr,
-	 * using _currentOpBase as the operator base.  Covers ADSR, KSL/TL,
-	 * feedback/algorithm, AM/VIB flags, and waveform-select registers.
-	 */
-	void writeSampleRegs();
-
-	/**
-	 * Loads all OPL registers for the patch assigned to the active channel,
-	 * covering both modulator and carrier operators, and sets up the channel's
-	 * sweep/frequency state from the AdlibSample definition.
-	 */
-	void loadSample();
-
-	/**
-	 * Main per-frame update: increments frame counters, polls all channels,
-	 * fires the tick callback, calls updateAllChannels, then runs the per-channel
-	 * frequency-sweep tick for all 9 voices.
-	 */
-	void update();
-
-	/**
-	 * Per-channel frequency-sweep tick for channel 'channelIndex' (0-based).
-	 * When _freqSweepCounter > 0: adds _freqStep to _freqAccum, decrements the
-	 * counter, and - when it reaches zero - zeroes the voice's frequency registers.
-	 * Sets _anySweepActive when the counter is still non-zero.
-	 */
-	void update1(int channelIndex);
-
-	/**
-	 * Writes a frequency value directly to the OPL registers for 'voice'.
-	 * The high byte selects the block/octave; the low byte is the F-number LSB.
-	 */
-	void setFrequency(uint8 voice, uint16 freq);
-
-	/**
-	 * Noise-channel inner tick: picks a random frequency offset masked by
-	 * _noiseFreqMask, adds _freqAccum, and writes the result to the voice
-	 * frequency registers.
-	 */
-	void noise_inner(int channelIndex);
-
-	/**
-	 * Calls processChannelFade for all 9 channels each frame.
-	 */
-	void updateAllChannels();
-
-	/**
-	 * Advances pSrc by one, reads two bytes little-endian, and returns the word.
-	 * Used by the bytecode opcodes that take 16-bit operands.
-	 */
-	uint16 readWord_impl();
-
-	/**
-	 * Deferred-callback tick: decrements _callbackCounter; when it reaches zero,
-	 * reloads it from _callbackPeriod and calls _callbackFnPtr (if non-null),
-	 * then clears _callbackFnPtr so it fires exactly once.
-	 */
-	void tickCallback();
-
-protected:
-	// ---- nine AdlibChannel instances ------------------------------------
-	AdlibChannel _channel0, _channel1, _channel2;
-	AdlibChannel _channel3, _channel4, _channel5;
-	AdlibChannel _channel6, _channel7, _channel8;
-	AdlibChannel *_channels[ADLIB_CHANNEL_COUNT] = {
-		&_channel0, &_channel1, &_channel2,
-		&_channel3, &_channel4, &_channel5,
-		&_channel6, &_channel7, &_channel8
-	};
-
-	uint8  _adlibPorts[0x100] = { 0 };
-	Common::Array<AdlibSample> _samples;
-
-protected:
-	// =========================================================================
-	// Protected helpers  (used by ASound1–ASound9)
-	// =========================================================================
-
-	/**
-	 * Checks whether any of channels 0-6 (or 0-8 when _musicOnlyFlag is clear)
-	 * have a non-zero _activeCount.  Returns non-zero if sound is playing.
-	 * This is 'sub_1061A' in the disassembly.
-	 */
-	int isMusicChannelsActive();
-
-	/**
-	 * Like isMusicChannelsActive but scans all 9 channels unconditionally
-	 * (clears the ch0-6-only flag first).  This is 'sub_1064E'.
-	 */
-	int isAnyChannelActive();
-
-	/**
-	 * Schedule fn as the next deferred-load callback.
-	 * Does NOT touch _callbackCounter or _callbackPeriod — those are preserved
-	 * from the previous loader so the callback fires on the right beat.
-	 * Cast the derived-class member-function pointer with reinterpret_cast.
-	 */
-	void scheduleCallback(CallbackFunction fn) { _callbackFnPtr = fn; }
-
-	/**
-	 * Arm the periodic timer and clear any pending callback pointer.
-	 * Call at the head of every immediate-load function (symmetric counter/period).
-	 */
-	void resetCallbackTimer(uint16 period) {
-		_callbackFnPtr = nullptr;
-		_callbackCounter = period;
-		_callbackPeriod = period;
-	}
-
-	/**
-	 * Arm the periodic timer with separate counter and period values.
-	 * Used by command44 which sets counter=0x60 but period=0xE0.
-	 */
-	void resetCallbackTimerEx(uint16 counter, uint16 period) {
-		_callbackFnPtr = nullptr;
-		_callbackCounter = counter;
-		_callbackPeriod = period;
-	}
-
-	/** Set the music-piece index (word_12370) read by command18. */
-	void setMusicIndex(uint16 idx) { _musicIndex = idx; }
-	/** Read the current music-piece index. */
-	uint16 getMusicIndex() const { return _musicIndex; }
-
-	/** Write one script-variable register (byte_16A10[idx]). */
-	void setScriptVar(int idx, uint8 val) { _scriptVars[idx] = val; }
-	/**
-	 * Writes (reg, value) to the OPL chip and updates the _adlibPorts shadow
-	 * array so subsequent reads return the last-written value.
-	 */
-	void write(uint8 reg, uint8 value);
-
-	/** Updates and returns _randomSeed using a simple linear-feedback shift. */
-	uint16 getRandomNumber();
-
-	/**
-	 * Restores one OPL operator by re-writing the value already stored in the
-	 * _adlibPorts shadow array.  command7 uses this to resume playback after
-	 * a command6 pause without recalculating any TL values.
-	 * 'portIndex' is the operator register index into _adlibPorts.
-	 */
-	void adlib_channelOn(uint8 portIndex);
-
-	/** Sets _pollResult and _resultFlag to indicate that sound is playing. */
-	void signalSoundPlaying();
-
-	/**
-	 * Iterates over all 9 channels, sets _activeChannelPtr / _activeChannelNumber,
-	 * and calls pollActiveChannel for each one.
-	 */
-	void pollAllChannels();
-
-	/**
-	 * Per-channel bytecode interpreter, called once per frame per active channel.
-	 *
-	 * Sound data is a sequence of (note, duration) byte pairs plus command bytes
-	 * with the high bit set (0x80-0xFF).  The upper nibble of a command byte
-	 * (after masking out the high bit) selects one of seven opcode groups:
-	 *
-	 *   0x0_  -> opcodes1  (patch/velocity/volume/vibrato/transpose/arpeggio)
-	 *   0x1_  -> opcodes2  (inner/outer loop control, restart, branch/call)
-	 *   0x2_  -> opcodes3  (tempo, script-variable arithmetic, call-by-address)
-	 *   0x3_  -> opcodes4  (script-variable load/store/copy/inc/dec)
-	 *   0x4_  -> opcodes5  (script-variable ALU: add/sub/mul/div with imm or var)
-	 *   0x5_  -> opcodes6  (extended: random-range, indexed table read/write)
-	 *   0x6_  -> opcodes7  (driver-level calls: command dispatch, etc.)
-	 *
-	 * The lower nibble is passed as the sub-opcode to each group handler.
-	 * Note bytes (high bit clear) consume one duration tick per call.
-	 */
-	void pollActiveChannel();
-
-	/** Returns true if the sound data block at 'ptr' is already playing. */
-	bool isSoundActive(byte *ptr) const;
-
-	/**
-	 * Scans channels 0-6 for an empty slot (_activeCount == 0) and calls
-	 * load() on the first one found.  Falls through to findFreeChannelFull
-	 * for channels 6-8 when _findChannelMode != 1.
-	 */
-	void findFreeChannel(byte *soundData);
-
-	/**
-	 * Extends the search to channels 7-8, then checks for pending-stop
-	 * channels (which can be pre-empted), working in reverse priority order
-	 * (ch8, ch7, ch6 ... ch0).
-	 */
-	void findFreeChannelFull(byte *soundData);
-
-	/** Returns a pointer to the sound data at the given offset. */
-	byte *loadData(int offset) {
-		return &_soundData[offset];
-	}
-
-protected:
-	// =========================================================================
-	// Core commands (0-8)  -  identical in purpose to Return of the Phantom
-	// =========================================================================
-
-	/**
-	 * command0: Full hardware reset.
-	 *   1. Reset all 9 channels.
-	 *   2. Mute all operator TL registers (0x40-0x55) to 0x3F.
-	 *   3. Zero remaining operator registers (0x60-0xFF and 0x01-0x3F).
-	 *   4. Write Waveform Select Enable (register 0x01 = 0x20).
-	 *   5. Reset the tick callback.
-	 */
-	int command0();
-
-	/**
-	 * command1: Fade out all channels.
-	 *   Calls command3 (fade music channels 0-6) then command5 (fade SFX 7-8).
-	 */
-	int command1();
-
-	/**
-	 * command2: Fade out music channels 0-6.
-	 *   Calls AdlibChannel::setPtr2 on each, redirecting to the null stream
-	 *   and arming a one-step fade.
-	 */
-	int command2();
-
-	/**
-	 * command3: Fade out music channels 0-6 with pending-stop.
-	 *   Calls AdlibChannel::enable on channels 0-6.
-	 */
-	int command3();
-
-	/**
-	 * command4: Fade out SFX channels 7-8.
-	 *   Calls AdlibChannel::setPtr2 on channels 7 and 8.
-	 */
-	int command4();
-
-	/**
-	 * command5: Stop SFX channels 7-8 with pending-stop flag.
-	 *   Calls AdlibChannel::enable on channels 7 and 8, letting each channel
-	 *   finish its current OPL envelope before going idle.
-	 */
-	int command5();
-
-	/**
-	 * command6: Pause playback.
-	 *   Saves each channel's _freqSweepCounter into _savedSweepCounter, zeroes
-	 *   _freqSweepCounter on all channels, then mutes all 22 operator TL
-	 *   registers (the byte_1239B table covers all operator slots 0x40-0x55).
-	 *   Sets _isDisabled to prevent further updates.
-	 */
-	int command6();
-
-	/**
-	 * command7: Resume playback.
-	 *   Restores operator volumes for all channels from _adlibPorts shadow,
-	 *   copies _savedSweepCounter back to _freqSweepCounter on all channels,
-	 *   signals sound playing if any channel was active, then clears _isDisabled.
-	 */
-	int command7();
-
-	/**
-	 * command8: Returns non-zero if any of the 9 channels has a non-zero
-	 *   _activeCount (i.e. sound is currently playing).
-	 *   Also clears the music-only flag (byte_12393 = 0) so the check covers
-	 *   all 9 channels.
-	 */
-	int command8();
-
-	/**
-	 * Calls a function at a fixed offset within the sound driver.
-	 * @param offset		Offset of the function
-	 */
-	virtual void callFunction(uint16 offset);
-
-	// =========================================================================
-	// Music-index launcher (called via command18)
-	// =========================================================================
-
-	/**
-	 * command18: Re-entrant music launcher.
-	 *   First calls command1 to fade current output, then branches on _musicIndex
-	 *   (word_12370):
-	 *     <= 0x12  -> calls off_11A26 table (commands 16-19)
-	 *     > 0x12  -> calls funcs_12251 table (commands 32-49), index = _musicIndex - 0x20
-	 */
-	int command18();
-
-public:
-	/**
-	 * Constructor.
-	 * @param mixer       Mixer instance
-	 * @param opl         OPL chip instance
-	 * @param filename    Path to the .DR1 (or equivalent) sound-driver file
-	 * @param dataOffset  Offset in the file of the data segment
-	 * @param dataSize    Size of the data segment
-	 */
-	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
-		int dataOffset, int dataSize);
-
-	~ASound() override {
-	}
-
-	/** Stop all currently playing sounds (wraps command0). */
-	virtual int stop() override;
-
-	/** Main poll method; drives the per-frame update. */
-	int poll() override;
-
-	/**
-	 * Noise channel tick: for each of the 9 channels calls noise_inner,
-	 * which randomises the voice frequency each frame using _noiseFreqMask.
-	 */
-	void noise() override;
-
-	/**
-	 * Starts playback of the sound data at the given byte offset within the
-	 * driver's data segment, using findFreeChannelFull to select a channel.
-	 */
-	void playSound(int offset);
-
-	void setVolume(int volume) override {
-		// TODO: implement if needed
-	}
-};
-
-} // namespace Forest
-} // namespace MADSV2
-} // namespace MADS
-
-#endif
diff --git a/engines/mads/madsv2/core/digi.cpp b/engines/mads/madsv2/forest/digi.cpp
similarity index 85%
rename from engines/mads/madsv2/core/digi.cpp
rename to engines/mads/madsv2/forest/digi.cpp
index c598d28ae94..3ab8e397273 100644
--- a/engines/mads/madsv2/core/digi.cpp
+++ b/engines/mads/madsv2/forest/digi.cpp
@@ -19,12 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/config.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 int digi_val2;
 int digi_timing_index;
@@ -55,11 +56,17 @@ void digi_play_build(int room, char thing, int num, int slot) {
 	digi_play(name.c_str(), slot);
 }
 
-void digi_play(const char *name, int slot) {}
-void digi_stop(int which_one) {}
-void digi_read_another_chunk() {}
-void digi_initial_volume(int vol) {}
-void digi_set_volume(int vol, int slot) {}
+void digi_play(const char *name, int slot) {
+}
+void digi_stop(int which_one) {
+}
+void digi_read_another_chunk() {
+}
+void digi_initial_volume(int vol) {
+}
+void digi_set_volume(int vol, int slot) {
+}
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/digi.h b/engines/mads/madsv2/forest/digi.h
similarity index 94%
rename from engines/mads/madsv2/core/digi.h
rename to engines/mads/madsv2/forest/digi.h
index f212c2f334c..58b64fc11a8 100644
--- a/engines/mads/madsv2/core/digi.h
+++ b/engines/mads/madsv2/forest/digi.h
@@ -19,13 +19,14 @@
  *
  */
 
-#ifndef MADS_DIGI_SOUND_H
-#define MADS_DIGI_SOUND_H
+#ifndef MADS_FOREST_DIGI_H
+#define MADS_FOREST_DIGI_H
 
 #include "common/scummsys.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 //extern int config_file.forest1;
 extern int digi_val2;
@@ -42,6 +43,7 @@ extern void digi_read_another_chunk();
 extern void digi_initial_volume(int vol);
 extern void digi_set_volume(int vol, int slot);
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 3615775203e..d5bae9788be 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "common/textconsole.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/text.h"
@@ -28,7 +28,7 @@
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/pal.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/extra.h"
diff --git a/engines/mads/madsv2/core/midi.cpp b/engines/mads/madsv2/forest/midi.cpp
similarity index 85%
rename from engines/mads/madsv2/core/midi.cpp
rename to engines/mads/madsv2/forest/midi.cpp
index 34ed0051361..6d5533bf289 100644
--- a/engines/mads/madsv2/core/midi.cpp
+++ b/engines/mads/madsv2/forest/midi.cpp
@@ -20,13 +20,20 @@
  */
 
 #include "common/textconsole.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
+#include "mads/madsv2/core/env.h"
 #include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 void midi_play(const char *name) {
+	assert(env_exist(name));
+
+	// TODO
+	warning("TODO: global_midi_play");
+
 	// TODO
 }
 
@@ -34,5 +41,6 @@ void midi_stop() {
 	// TODO
 }
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/core/midi.h b/engines/mads/madsv2/forest/midi.h
similarity index 89%
rename from engines/mads/madsv2/core/midi.h
rename to engines/mads/madsv2/forest/midi.h
index 52515cc9adc..508c7ec82ac 100644
--- a/engines/mads/madsv2/core/midi.h
+++ b/engines/mads/madsv2/forest/midi.h
@@ -19,18 +19,21 @@
  *
  */
 
-#ifndef MADS_CORE_MIDI_H
-#define MADS_CORE_MIDI_H
+#ifndef MADS_FOREST_MIDI_H
+#define MADS_FOREST_MIDI_H
 
 #include "common/scummsys.h"
 
 namespace MADS {
 namespace MADSV2 {
+namespace Forest {
 
 extern void midi_play(const char *name);
 extern void midi_stop();
-inline void midi_loop() {}
+inline void midi_loop() {
+}
 
+} // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
index 8e60e2a8050..650ac79c08f 100644
--- a/engines/mads/madsv2/forest/rooms/room101.cpp
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
index f4f4b3a16ff..a3f08454721 100644
--- a/engines/mads/madsv2/forest/rooms/room103.cpp
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
index 71c10cb5336..02f3b94bce2 100644
--- a/engines/mads/madsv2/forest/rooms/room104.cpp
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
index 398d276c017..d600c93d2eb 100644
--- a/engines/mads/madsv2/forest/rooms/room106.cpp
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -20,13 +20,13 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
index f762365faeb..7176b0e1239 100644
--- a/engines/mads/madsv2/forest/rooms/room107.cpp
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
index 87cbfd4a322..bb9bb8458c3 100644
--- a/engines/mads/madsv2/forest/rooms/room199.cpp
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
index 9684f3d598b..a90c96828f9 100644
--- a/engines/mads/madsv2/forest/rooms/room201.cpp
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
index 5088c5f7fd9..8324ca890c2 100644
--- a/engines/mads/madsv2/forest/rooms/room203.cpp
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/extra.h"
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
index 11c9f3dc451..70fbaec8dab 100644
--- a/engines/mads/madsv2/forest/rooms/room204.cpp
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
index 384865a30d3..3396f9183d9 100644
--- a/engines/mads/madsv2/forest/rooms/room205.cpp
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -19,14 +19,14 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
index abea8109c55..97afb3ccc66 100644
--- a/engines/mads/madsv2/forest/rooms/room210.cpp
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -20,11 +20,11 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/speech.h"
 #include "mads/madsv2/forest/mads/inventory.h"
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
index a572b5b546d..1477e3d4af2 100644
--- a/engines/mads/madsv2/forest/rooms/room211.cpp
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -19,11 +19,11 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
index 465632497d6..bb1d922ab1e 100644
--- a/engines/mads/madsv2/forest/rooms/room220.cpp
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -19,11 +19,11 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
index 45d959c7884..949784b5af7 100644
--- a/engines/mads/madsv2/forest/rooms/room301.cpp
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -20,11 +20,11 @@
  */
 
 #include "mads/madsv2/forest/journal.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
index ee3db098fd5..a6a8b061c41 100644
--- a/engines/mads/madsv2/forest/rooms/room302.cpp
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
index c6f8028e090..5587005d36a 100644
--- a/engines/mads/madsv2/forest/rooms/room303.cpp
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
index ec8ee34447f..ef4329aaee3 100644
--- a/engines/mads/madsv2/forest/rooms/room304.cpp
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
index ff0d670596e..744461f52fa 100644
--- a/engines/mads/madsv2/forest/rooms/room305.cpp
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
index 72858656bac..4af54428a68 100644
--- a/engines/mads/madsv2/forest/rooms/room306.cpp
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -23,9 +23,9 @@
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/sprite.h"
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
index 122ff122079..9e6999583c6 100644
--- a/engines/mads/madsv2/forest/rooms/room307.cpp
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
index 7ef97d10d99..f6e514b7cb9 100644
--- a/engines/mads/madsv2/forest/rooms/room308.cpp
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/forest/extra.h"
 #include "mads/madsv2/forest/journal.h"
 #include "mads/madsv2/core/game.h"
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
index fc47eafab3e..b5db451096b 100644
--- a/engines/mads/madsv2/forest/rooms/room321.cpp
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
index c510d1402e7..f2dacc67a0e 100644
--- a/engines/mads/madsv2/forest/rooms/room322.cpp
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -23,8 +23,8 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
index f2c9c3c9766..b2f8706c53e 100644
--- a/engines/mads/madsv2/forest/rooms/room401.cpp
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -24,9 +24,9 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/sprite.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
index f1387102cf6..c18b0b0453e 100644
--- a/engines/mads/madsv2/forest/rooms/room402.cpp
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -19,12 +19,12 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
 #include "mads/madsv2/forest/journal.h"
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
index d35ee2533fa..e1713a0dd2b 100644
--- a/engines/mads/madsv2/forest/rooms/room403.cpp
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sprite.h"
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
index b17a9c55636..86bfcca25e2 100644
--- a/engines/mads/madsv2/forest/rooms/room404.cpp
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
index 31e712d124f..b17501b8f1f 100644
--- a/engines/mads/madsv2/forest/rooms/room405.cpp
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
index b0a53052ea0..b212e995bc4 100644
--- a/engines/mads/madsv2/forest/rooms/room420.cpp
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/forest/rooms/section1.h"
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
index ed46140e7b8..8c930fbe334 100644
--- a/engines/mads/madsv2/forest/rooms/room501.cpp
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -23,8 +23,8 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/digi.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
index 35bd8fb3393..19672e9ccae 100644
--- a/engines/mads/madsv2/forest/rooms/room503.cpp
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -19,13 +19,13 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/sound.h"
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
index 10ff60818a0..c9bf05a440d 100644
--- a/engines/mads/madsv2/forest/rooms/room509.cpp
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
index 213430b7c43..4626dbd6968 100644
--- a/engines/mads/madsv2/forest/rooms/room510.cpp
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -20,12 +20,12 @@
  */
 
 #include "mads/madsv2/core/config.h"
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/font.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/player.h"
 #include "mads/madsv2/core/quote.h"
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
index 13d6596dcc9..3ff8a8239f0 100644
--- a/engines/mads/madsv2/forest/rooms/room520.cpp
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "mads/madsv2/core/digi.h"
+#include "mads/madsv2/forest/digi.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index bfc535ce269..52fcc246db8 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -24,7 +24,7 @@
 #include "mads/madsv2/core/global.h"
 #include "mads/madsv2/core/kernel.h"
 #include "mads/madsv2/core/matte.h"
-#include "mads/madsv2/core/midi.h"
+#include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/player.h"
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index b5e8aee7e80..cee9da707fd 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -72,7 +72,6 @@ MODULE_OBJS += \
 	madsv2/core/cursor.o \
 	madsv2/core/cycle.o \
 	madsv2/core/dialog.o \
-	madsv2/core/digi.o \
 	madsv2/core/ems.o \
 	madsv2/core/env.o \
 	madsv2/core/error.o \
@@ -96,7 +95,6 @@ MODULE_OBJS += \
 	madsv2/core/matte.o \
 	madsv2/core/mcga.o \
 	madsv2/core/mem.o \
-	madsv2/core/midi.o \
 	madsv2/core/mouse.o \
 	madsv2/core/object.o \
 	madsv2/core/pack.o \
@@ -263,12 +261,13 @@ MODULE_OBJS += \
 	madsv2/dragonsphere/menus.o \
 	madsv2/dragonsphere/sound_dragonsphere.o \
 	madsv2/forest/forest.o \
-	madsv2/forest/asound.o \
+	madsv2/forest/digi.o \
 	madsv2/forest/extra.o \
 	madsv2/forest/global.o \
 	madsv2/forest/journal.o \
 	madsv2/forest/main.o \
 	madsv2/forest/menus.o \
+	madsv2/forest/midi.o \
 	madsv2/forest/mads/mads.o \
 	madsv2/forest/rooms/room101.o \
 	madsv2/forest/rooms/room103.o \


Commit: 1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3
    https://github.com/scummvm/scummvm/commit/1ce640b1a11d5187cb4c780a4ee1e3e49a5f6bd3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-06-21T12:41:50+10:00

Commit Message:
MADS: FOREST: Skeleton MidiPlayer class

Changed paths:
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/madsv2/forest/forest.h
    engines/mads/madsv2/forest/midi.cpp
    engines/mads/madsv2/forest/midi.h


diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index 751831779f0..56f0f3eaf3d 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -53,6 +53,17 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+ForestEngine *g_engine;
+
+ForestEngine::ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
+		MADSV2Engine(syst, gameDesc) {
+	g_engine = this;
+}
+
+ForestEngine::~ForestEngine() {
+	g_engine = nullptr;
+}
+
 Common::Error ForestEngine::run() {
 	initGraphics(320, 200);
 	_screen = new Graphics::Screen();
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index 6765d195a40..d012bd26022 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -23,6 +23,8 @@
 #define MADS_FOREST_H
 
 #include "mads/madsv2/engine.h"
+#include "mads/madsv2/forest/digi.h"
+#include "mads/madsv2/forest/midi.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -30,9 +32,11 @@ namespace Forest {
 
 class ForestEngine : public MADSV2Engine {
 public:
-	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
-		MADSV2Engine(syst, gameDesc) {}
-	~ForestEngine() override {}
+	MidiPlayer _midiPlayer;
+
+public:
+	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc);
+	~ForestEngine() override;
 
 	Common::Error run() override;
 	void syncRoom(Common::Serializer &s) override;
@@ -48,6 +52,8 @@ public:
 	void global_sound_driver() override;
 };
 
+extern ForestEngine *g_engine;
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/midi.cpp b/engines/mads/madsv2/forest/midi.cpp
index 6d5533bf289..2691ca8d5c7 100644
--- a/engines/mads/madsv2/forest/midi.cpp
+++ b/engines/mads/madsv2/forest/midi.cpp
@@ -20,25 +20,55 @@
  */
 
 #include "common/textconsole.h"
+#include "audio/midiparser_smf.h"
 #include "mads/madsv2/forest/midi.h"
 #include "mads/madsv2/core/env.h"
-#include "mads/madsv2/engine.h"
+#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
-void midi_play(const char *name) {
-	assert(env_exist(name));
+void MidiPlayer::send(uint32 b) {
+	//_driver->send(b);
+	Audio::MidiPlayer::send(b);
+}
+
+void MidiPlayer::play(const char *name) {
+	// Open up the MIDI file
+	Common::SeekableReadStream *f = env_open(name);
+	if (!f) {
+		warning("MIDI not found - %s", name);
+		return;
+	}
+
+#if 0
+	// Read in the file contents
+	size_t size = f->size();
+	byte *buf = (byte *)malloc(size);
+	f->read(buf, size);
+	delete f;
 
-	// TODO
-	warning("TODO: global_midi_play");
+	// Set up the parser
+	_parser = new MidiParser_SMF();
+	if (!_parser->loadMusic(buf, size))
+		error("midiPlay() couldn't load music resource");
 
-	// TODO
+	_parser->setTrack(0);
+	_parser->setMidiDriver(this);
+	_parser->setTimerRate(_driver->getBaseTempo());
+#else
+	warning("TODO: Support hmi midi playback");
+	delete f;
+#endif
+}
+
+void midi_play(const char *name) {
+	g_engine->_midiPlayer.play(name);
 }
 
 void midi_stop() {
-	// TODO
+	g_engine->_midiPlayer.stop();
 }
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/midi.h b/engines/mads/madsv2/forest/midi.h
index 508c7ec82ac..82a5e5d8b40 100644
--- a/engines/mads/madsv2/forest/midi.h
+++ b/engines/mads/madsv2/forest/midi.h
@@ -22,16 +22,27 @@
 #ifndef MADS_FOREST_MIDI_H
 #define MADS_FOREST_MIDI_H
 
-#include "common/scummsys.h"
+#include "audio/midiplayer.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+class MidiPlayer : public Audio::MidiPlayer {
+private:
+	// MidiDriver_BASE interface implementation
+	void send(uint32 b) override;
+
+public:
+	MidiPlayer() : Audio::MidiPlayer() {}
+	~MidiPlayer() override {}
+
+	void play(const char *name);
+};
+
 extern void midi_play(const char *name);
 extern void midi_stop();
-inline void midi_loop() {
-}
+inline void midi_loop() {}
 
 } // namespace Forest
 } // namespace MADSV2




More information about the Scummvm-git-logs mailing list