# HG changeset patch # User Paper # Date 1760930102 14400 # Node ID 503e22dd6cf512e570829f98da2118da5088881f # Parent 6ca8af53d42437d35b9df1e3b288f876bfb78643 blog: add (unfinished) series on OMS I'll update this as I do more research into the inner workings of OMS. It's much more interesting (and more convoluted) than ASIO is unfortunately, but it means the blog posts will probably be more interesting diff -r 6ca8af53d424 -r 503e22dd6cf5 _posts/2025-10-19-oms-part-0.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_posts/2025-10-19-oms-part-0.html Sun Oct 19 23:15:02 2025 -0400 @@ -0,0 +1,25 @@ +--- +layout: post +author: Paper +title: 'The Open Music System, part 0 - preface' +nowplaying: 'Midnight Star - Midas Touch (Hell Interface remix)' +--- + + The primary method of organizing (and interacting with) MIDI devices + on Mac OS 9 is through the Open Music System, developed by Opcode. + However, OMS in itself isn't very "open" at all, as are most + standards for music software. Steinberg's ASIO and VST + are prime examples of this phenomena. + +

+ + In this series of blog posts I will be digging into how applications + interact with the OMS system extension, in an effort to allow other + programs to (legally) interact with OMS without using the proprietary + OMS SDK. + +

+ + Find the next post in this series + here. + \ No newline at end of file diff -r 6ca8af53d424 -r 503e22dd6cf5 _posts/2025-10-19-oms-part-1.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_posts/2025-10-19-oms-part-1.html Sun Oct 19 23:15:02 2025 -0400 @@ -0,0 +1,84 @@ +--- +layout: post +author: Paper +title: 'The Open Music System, part 1 - initialization' +nowplaying: 'The Radio Dept. - Brobygatan' +--- + + Applications wanting to interact with OMS must first call into + Gestalt with the first parameter being the magic + bytes " OMS" (0x204F3D53) and the + second parameter being a pointer to a 32-bit variable that + receives the magic pointer. + +{% comment %} + is there was a way to do this with newlines? this is ugly +{% endcomment %} +
UniversalProcPtr OMS_GetGestaltPtr(void)
+{
+	OSErr err;
+	uint32_t x;
+
+	err = Gestalt(0x204F3D53 /* " OMS" */, &x);
+	if (err != noErr)
+		return NULL;
+
+	return (UniversalProcPtr)x;
+}
+ + From here, applications must retrieve an array of functions + through calling that magic pointer. Note that users cannot + simply call this pointer directly, unless they are specifically + only targeting 68k. You must use the CallUniversalProc + function to safely call into 68k code. + +
UniversalProcPtr oms_table[0x5e];
+
+#define OMS_GESTALT_PROC_TYPE \
+	(kThinkCStackBased \
+		| RESULT_SIZE(kFourByteCode) \
+		| STACK_ROUTINE_PARAMETER(1, kTwoByteCode) \
+		| STACK_ROUTINE_PARAMETER(2, kFourByteCode))
+
+int32_t OMS_Init(void)
+{
+	uint32_t functable;
+	UniversalProcPtr omsptr;
+
+	omsptr = OMS_GetGestaltPtr();
+	if (!omsptr)
+		return -1;
+
+	functable = CallUniversalProc(omsptr, OMS_GESTALT_PROC_TYPE, 4, 0);
+	if (functable != (uint32_t)-1) {
+		memcpy(oms_table, (const void *)functable, sizeof(oms_table));
+	} else {
+		/* very old OMS */
+		uint32_t i;
+
+		functable = CallUniversalProc(omsptr, OMS_GESTALT_PROC_TYPE, 1, 0);
+		if (!functable)
+			return -1;
+
+		for (i = 0; i < ARRAY_SIZE(oms_table); i++, functable += 4)
+			oms_table[i] = (UniversalProcPtr)functable;
+	}
+
+	return 0;
+}
+ + At this point, you can now call into any of the pointers in + oms_table, provided that you know what the parameters + are. The easiest way I've found to find the parameters is to open an + OMS-capable program in a reverse-engineering tool, search for uses of + CallUniversalProc that look roughly like the initialization + code above, and mark the offset they are copied to as the OMS table with + the correct size and type. You can then find other uses of + CallUniversalProc that use the pointers in the OMS table, + and mark down the procedure type (the second parameter to + CallUniversalProc). To decode the procedure type, I've + written a simple utility to do the heavy lifting + here. + Do take note that it does not support register-based calling yet. + Feel free to send me any patches if you add support for it ;) +