Things that bug MadIce
The cure for headaches.
Published on November 13, 2003 By MadIce In Software Development
If you are coding for a fairly long time like me then there is always a piece of code you are proud of. One of the things I was proud of until recently was a games engine. But, since it is based on (what we call now) old technology it's merely an artifact. When looking back at ObjectMedia 1.0 (a player thingy I made for Stardock) it struck me how many features it has and how many of those are not used (yet). There are lots of features for skinners (through DXPlayer and WBAmp) and even lots of features for plugin writers. There is even an SDK which needs a little brushing up. But it is fairly final. And no, it is not the number of features I am proud of. One part that I haven't documented yet is using a very efficient but very unusual plugin interface. While attempting to document that it looked to me this could be a cool article for the freaks under the coders.

Every piece of code you write starts with a problem to be solved. In this case I wanted (a future version of a plugin like DXPlayer or some other DesktopX plugin) to be able to show a scope or a spectrum analyzer. OM media plugins cause two major headaches for interprocess communication. Rememeber that OM is a stand alone player and that DXPlayer, WBAmp and KLPlayer are NOT part of its process. These are conventional plugins that are DLLs and are part of DesktopX's process for an example. So DXPlayer can simply and efficiently communicate to DesktopX using callback functions.

Headache #1
For the moment let's forget about the scope problem and focus on common player functionality. DXPlayer, WBAmp and KLPlayer also need to communicate with ObjectMedia. The plugins simply send WM_COMMAND or WM_COPYDATA messages to ObjectMedia to make it execute certain functions like "Play" and "Insert a clip in the playlist at the current cursor position". Sending back messages from OM to the plugins is NOT a good idea. It may work for DXPlayer and KLPlayer, but certainly not for WBAmp. Subclassing for WindowBlinds is out of the question. Neil told me that was not a good idea. It would cause all kinds of trouble for software and WindowBlinds. So, what data usually needs to be send to the plugin? It turns out that for most functions... none. Things like a clip duration are requested, not sent. Besides, I didn't want to keep track of which plugins are in use. Too much work and too error prone. Now we have a little problem, because messages can send data, but they cannot be used to receive data without subclassing. One can send a message and maybe get an integer result of the message being sent back, but that's it. To overcome that ObjectMedia has a chunk of shared memory and is updating that memory with things like the name of the current clip, its duration, its position, etc. It turns out that OM doesn't have to do much real-time updating, other than the position and player status: playing, paused, stopped, etc. Only ObjectMedia has read/write access to that memory. The plugins can only read it: a cool safety feature for a multiple plugin environment. Accessing shared memory is an extremely fast way to retrieve data. Problem #1 is now solved.

Headache #2
Back to the scope problem. This one is much more complicated. OM is based on DirectShow and DirectShow is doing much of the hard work and tries to hide a lot of details. Sometimes you need those details and infact for a scope access to the real-time raw audio data is needed. A little COM object can do that. A one second audio buffer is created and the data copied to the sound renderer is copied to that buffer. To prevent hickups this is all the COM object should do. Whenever the buffer has been updated an event will be signaled and the position and size of the buffer is stored in shared memory. This causes a thread to be activated and normalize it to 16-bit stereo and converting the format when required. Because this is done in another thread no hickups will occur. It's a good thing that reading a buffer is slower than normalizing it. It will simplify synchronizing the different tasks involved. Although large blocks of data are involved you will not notice any CPU performance penalties. For a GHz processor a 48KHz sound buffer is a piece of cake. After the normalization step a different event will be signalled. That event will not be handled by ObjectMedia. Other processes can respond to it. The cool thing about events is that these are handled by threads and a thread waiting for an event does NOT use any CPU time. Any process that has a thread running that monitors the event has now access to a 16-bit stereo buffer in real-time with virually no performance penalties! So, a plugin just needs to draw the scope everytime the event has been signalled. No fiddling arround with copying chunks of memory and format conversion, because all the hard work already has been done. The plugin doesn't even need to know what format is being used, because it is always the same: 16-bit stereo. Problem #2 is now solved too.

All I need to do now is to find me a couple of programmers who want to write visual effects, because I am not sure if I can find the time to write it myself.

BTW: If you are really interested then let me know. I have writen very efficient and easy to use C++ classes that handle threads and signals like events, mutexes, sempahores, periodic timers, alarms and thread messages. I will be glad to share that with you. The same classes are used by OM to do the buffer fiddling and event stuff. With a few lines of code you can respond to an OM event. A typical response would be to send a message to the main thread and draw the visual effect. That message is required to make DesktopX happy and the actual drawing will take places when receiving that message. The DesktopX drawing routines should be very efficient thanks to its new DirectGUI technology.

You can find more information about ObjectMedia's design at the following link:

Comments
No one has commented on this article. Be the first!