In DX8, DirectMusic introduced three features that could easily cause reference loops. These are Scripting, Script Tracks, and Segment Playback Tracks. For example, one script might reference another script that calls back into the first. On the other hand, a Segment Trigger Track triggers the same Segment or another that triggers the original Segment again. These are all perfectly reasonable scenarios, and they do load and run appropriately. However, when it comes time to clean up, a self-referencing loop keeps the reference counts of all objects in the loop incremented by at least one, and so they never go away, even though nothing actually references them outside the loop.
The CollectGarbage() method solves this problem. CollectGarbage() scans for all objects that have been completely released by the Loader as well as the application and calls an internal method on the object, forcing it to release its references to other objects and thus breaking any cyclic references and removing unneeded objects from memory.
LoaderView Sample Application
The LoaderView sample demonstrates both how to load files with the Loader and how to manage the cache. In truth, it goes a little overboard demonstrating all of the caching features, but by playing with this, you can get a good understanding of how the Loader manages its file list and how you can consequently control it for your application.
LoaderView is a windowed application that lets you open a Segment, play it, view the Loader's internal lists of all object types that were loaded as a result of loading the Segment, and then play with the object lists using the standard Loader methods and observe the behavior. You can run LoaderView by either going to the Unit II\ 9_Loader directory and building LoaderView or running it directly from the Bin directory. LoaderView opens a simple dialog window with commands for loading a Segment and managing the Loader (see Figure 9-2).
Figure 9-2: LoaderView.
Let's first explore LoaderView, discuss how it uses the various Loader features, and then look at the code.
Click on the Open… button to open a File dialog.
Search for a Segment, MIDI, or wave file to load. Files that reference other files are particularly interesting. For example, EvilDonuts.sgt has references to Style, DLS, and wave files.
When you click on the Open… button, the Loader's GetObjectFromFile() method is called to load the Segment. Once the file is loaded, its name is listed in the box underneath the Open… button.
Click on the Play and Stop buttons to audition the Segment. We talk a lot more about playing Segments in the next chapter, but at least we can audition our Segment for now. With the Loader being as dull a subject as it is, it helps to be able to make noise.
Now, let's look at what is in the Loader's cache.
The Type drop-down lets you choose which media type to enumerate and display. Click on the list to choose from Segments, Waves, DLS Collections, Styles, and quite a few others.
The list box below the Type drop-down displays all files of the media type that you requested that the Loader is currently managing. Choose a list that displays one or more files. For example, "Evil Donut" uses two DLS Collections, so you might try that. Note that they all have "-cached" after their name. This indicates that the Loader currently has a reference to an instance of the object loaded in memory.
You can turn off caching. Uncheck the Enable Cache option. This calls the EnableCache() method with a false flag to disable it. Disabling a cache also automatically clears it, so notice now that the display lists these files no longer as cached. Remember: Once the Loader does not have the file cached, it must reload the file when the application subsequently requests the file. This incurs file and CPU overhead and extra memory overhead if the object is still in use since there are two copies of the object in memory once the second instance loads.
Go back and press the Play button. Notice that the music still plays. Although the Loader no longer has its references to the objects in memory (Style, DLS, etc.), the Segment still does, so it plays flawlessly. Later, when the Loader and application finally release the Segment, it in turn releases all objects that it references. Ah, such is the power and beauty of COM.
Likewise, you can release an individual item. Go to another list, such as DLS Collections, that has two or more objects. Click on one to select it, and then click on the Release Item button. Note that just the one object loses its "cached" designation.
Finally, click on the Scan Files button. This calls ScanDirectory() for the selected file type in the current search directory. The list updates with all additional files found in the directory. Notice that ScanDirectory() only retrieves the file information and does not load and cache the file objects themselves.