Greetings, This is the Hackercorp Commercial SMUS Player, a music player for embedding within other programs (such as games) on the Commodore Amiga computer system. This is version 2.2 This document, the source and object code that came with it and any other documentation is copyrighted. Copyright 1989, 1991 Hackercorp All Rights Reserved Noncommercial reuse is permitted providing included documentation, "About" pull-down menu selection, and any other documentation display or title screen contains a message saying Hackercorp Commercial SMUS Player by Karl Lehenbauer Copyright (C) 1989, 1991 Hackercorp For commercial use (use within commercial programs, shareware, etc. -- i.e., any program which is sold or for which funds are requested for use), a resale license must be purchased. Resale licenses cost US$100 per package the player is used in, and permits unlimited resale of the server without royalties within that package. For example, if you write a game, foo, $100 allows you to include the server within foo without any additional payments, no matter how many copies are sold. If you write another game, bar, another license (and another $100) is required. A license must be obtained before your program goes on sale, or you are in violation of our copyrights. To purchase a license, send a letter requesting a license and a check drawn on U.S. funds for $100 to: Hackercorp ATTN: SMUS server license 3918 Panorama Missouri City, TX 77459 These terms are extraordinarly kind, in our opinion, considering the amount of work that has gone into this program. Consequently, if we find that you have bundled the server into a commercial or shareware program and offered it for sale without first obtaining a license, we will pursue all legal means to recover our license fee, plus any costs incurred in enforcing our copyrights. You will find that the costs of defending yourself in a lawsuit to far exceed the $100 you will save by cheating us. Your use of this program gives your assent that any legal actions resulting from the misuse of it will be adjudicated under the laws of the state of Texas. Disclaimer ---------- I hate this part, but... HACKERCORP/KARL LEHENBAUER ("HACKERCORP") MAKE NO WARRANTIES, EITHER EXPRESSED OR IMPLIED, WITH RESPECT TO THE INFORMATION AND COMPUTER SOFTWARE DESCRIBED HEREIN, IT'S QUALITY, PERFORMANCE, MERCHANTABILITY, OR FITNESS FOR ANY PARTICULAR PURPOSE. SUCH COMPUTER SOFTWARE AND DOCUMENTATION IS PROVIDED ON AN "AS IS" BASIS. THE ENTIRE RISK AS TO THEIR QUALITY AND PERFORMANCE IS WITH THE LICENSEE. SHOULD THE INFORMATION AND/OR COMPUTER SOFTWARE PROVE DEFECTIVE, THE LICENSEE (AND NOT THE CREATOR, HACKERCORP) ASSUMES THE ENTIRE COST OF ALL NECESSARY DAMAGES. IN NO EVENT WILL HACKERCORP BE LIABLE FOR DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT IN THE INFORMATION AND COMPUTER SOFTWARE EVEN IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME LAWS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES OR LIABILITIES FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATION OR EXCLUSION MAY NOT APPLY. Before You Go Any Further ------------------------- Please make a copy of your SMUS server master disk and put it in a safe place. Since it is a development tool, it is not copy protected. Overview -------- SMUS stands for Simple Musical Score, it is an Interchange File Format (IFF) standard. For more information, see the IFF documentation in the Amiga ROM Kernel Reference Manual for version 1.3 of the system software, ISBN 0-201-18177-0, or the IFF documetation disk from Fred Fish. Many SMUS players do not support all of the capabilities defined in the IFF SMUS specification. The spec explicitly allows this. I have tried to implement all of the features described in the spec. The SMUS server has many advantages over other available music players for use within other programs. Some of the capabilities of this SMUS player are: o can have multiple songs loaded concurrently and maintain a shared pool of samples o can operate simultaneously with floppy and hard disk I/O, as well as concurrent window resizing, depth arranging, etc, etc. o can play one song while simultaneously loading another o supports instrument changes within tracks o plays both the attack and loop portions of the waveform o can play chords within tracks o well behaved; communicates with audio through audio.device and allocates and deallocates voices on the fly so other well-behaved audio programs can run concurrently. o uses a free CIA timer (Timer A on CIA B) as an interval timer o can play IFF 8SVX single and multioctave sample files, including those that have been compressed by Fibonacci delta compression. o server runs as two separate programs, so a game or application consisting of several programs need only load the server once, and music can play while new programs are loading as above o player, based on simple testing with the Amiga performance monitor program (PM), consumes very little CPU time, typically around one or two percent. o sample management routines permit flexible control of what samples (instruments) are loaded and unloaded, and permit minimizing disk load times by retaining in memory samples used in multiple songs o server can load songs, instruments and voicebanks from IFF CAT archive files, reducing clutter and, more importantly, reducing load time by 50% (a tool to create and manipulate CAT archives is included) o message-based server interface is also provided o server code size is less than 30K bytes The SMUS server consists of two programs and an interface library. The two programs are the loader and the player. The loader, "loadsmus", is responsible for loading and unloading the IFF SMUS song files and the IFF 8SVX sample files. The player, "playsmus", plays the songs. On your target disk (the disk you are building to ship to your customers) you will need to create an assign named "ksmu:" that points to the directory into which you will copy the files "playsmus" and "loadsmus", included in this distribution. The C source files included in this directory are the interface routines to the SMUS server, including routines that can do a workbench launch and free, and a cleanup facility. Before you make any requests to load or play songs, you must first initialize the SMUS server by calling: SMUSInitServer(); When your program is terminating, assuming that it does return control to the Amiga OS, you will need to call either SMUSTerminateServer() or TerminateServerWhenFinished(). The latter will, as the name implies, wait for the current song to finish before terminating the server. Requesting the server to terminate causes all currently loaded songs and samples to be unloaded, and the programs "loadsmus" and "playsmus" are terminated and unloaded. There are two ways to interface to the SMUS server. One way is to send messages directly to the SMUS loader and SMUS player. The other way is to use the SMUS server's remote procedure call (RPC) routines, which will handle the communications to the loader and player automatically. The sample C program for interfacing with the server that is included on this disk demonstrates the RPC interface. Those wishing to send the messages directly (and there are advantages) are encouraged to study the source code to the RPC routines included herein, in lieu of documentation of said routines, which has yet to be written. Contents -------- This disk contains the README file you are now looking at, the binary programs for the server and the player, debug versions of the server and the player, and source code to an interface library and demo program for Aztec C 3.60 or later (earlier versions of the C compiler can probably still compile and link the program successfully) Demo Program ------------ The demo program allows you to experiment with the SMUS server. It is run from the CLI and provides a simple command-oriented interface to the SMUS server functions. Help is included. The name of the program is "smus". It is included in executable format, but also as C source code along with a makefile. Aztec users should be able to do a "make" and regenerate "smus" without trouble. When you're running smus and you want to play a song, use the load command ("l filename") to load the filename. Then you need to do an attachsamples ("a"). Finally, you can play ("p" command, doesn't return until song is finished) or play with quick return ("i" command, returns immediately -- song continues to play). Enter '?' for help. Assignments ----------- The SMUS server needs four assignments to be made (via the ASSIGN command). It needs ksmu: defined to be a directory that contains the loader and player programs, loadsmus and playsmus. It needs Instruments: (case is not significant) to point to your instruments and voicebanks, and it needs Songs: to point to your IFF SMUS songs. Voice banks are IFF files that define arbitrary ranges of an instrument's virtual keyboard to be different IFF 8SVX samples. A directory, voicebanks, may be present on the disk. If so, it should contain a tool for creating voicebank files suitable for use by the server. If you have some voicebanks, there will be samples corresponding to them in the instrument directory. For example, the piano voicebank references several piano samples named things like piano.g5, corresponding to the key and octave of that sample range. Note that the piano sample is huge. Here are some example assigns: assign instruments: yourdisk:instruments assign songs: yourdisk:songs assign ksmu: yourdisk:c This would assign instruments: to a directory on your disk "yourdisk" named instruments, and ksmu: to a directory named "c" on your disk "yourdisk." Thus, loadsmus and playsmus can be in your C: directory with other programs, or whatever. Track Priorities ---------------- The SMUS player uses audio.device for doing it's audio I/O, and allocates and deallocates the channels on-the-fly for compatibility with other well-behaved sound programs/subroutines. As such, it sets up the priority field in the allocation request according to Amiga documentation, which suggests a high priority for the start of the sound and a low priority for the tail-end, and offers some suggested ranges. The lowest-numbered track, 0, has the highest priority, with priorities reducing as the track numbers increase. The one-shot portion of the sound is queued with a priority of 50 for channel zero, decrementing by 10 for each successive channel. The loop portion is played at a priority of zero for the first channel, decrementing by 10 for each successive channel's loop portion. (Bug -- the priority is not lowered as of at least 2.2 -- a problem with audio.device?) Stereo Channel Placement ------------------------ The player tries to play even tracks on the left side and odd tracks on the right. If it cannot allocate a channel on the stereo side it prefers, it will use a channel from the other stereo side if one is available. Notes about Deluxe Music Construction Set (DMCS) ------------------------------------------------ The SMUS file format does not support repeats with "1" and "2" sections, as normal DMCS files do. When you use DMCS to save in SMUS format it does absolutely the wrong thing with any repeats, converting the "1" section and following it immediately by the "2" section, then the part following the repeat -- i.e., no repeat. The solution, and a rather tedious one it can be, is to block copy your score around inside DMCS until there are no repeats prior to converting to SMUS format. The Two Versions of the SMUS Server ----------------------------------- Two versions of the SMUS server program, loadsmus and playsmus, are included. One pair, "loadsmus.debug" and "playsmus.debug", are compiled with "audit" code included. The other pair, "loadsmus" and "playsmus", is compiled with audit code removed. Audit code in this case is code that is included to check for all sorts of different possible errors within the operation of the SMUS player while playing your songs. To use the debug versions, copy loadsmus.debug and playsmus.debug to your ksmu: directory, naming them loadsmus and playsmus (I hope you backed up your master disk!). Alternatively, you can reassign ksmu: to a different directory containing the debug versions (again renamed to loadsmus and playsmus). Or you can run the programs directly, no need to rename them then, as described down below. There are two types of audit code within the debug versions of the SMUS server. Part of it checks for problems within your SMUS song file. These are reported in windows that the debug versions of the programs open when they start up. These are problems like selecting nonexistent instruments, going out of range within a sample, etc. The other kind of audit code checks for internal errors within the SMUS player. These are conditions that would cause the SMUS player to crash or guru the system if they were not detected, such as addresses of sample data structures being passed around that weren't valid, the player getting lost, etc. Some errors are noted and the server goes on. Others cause the server to abort. Note that since the SMUS server prints the audit messages while playing, if it detects a problem it needs to report, the player may audibly glitch as it doesn't play during the brief interval while it's writing to the window. The non-debug versions won't print these messages, hence they don't glitch. I recommend that you try your SMUS files with the debug version of the server first and, if everything works, you can then use the version without audit code. Alternatively, try it with the non-debug version and if it blows up, then try it with the debug version. The advantages of the non-debug version are somewhat faster execution and significantly smaller size, plus no error messages are printed that'll glitch the sound. If you find that one of the debug-enabled programs is printing something in its window and terminating and closing the window before you can read it, you can launch the program by hand from one of your CLI or Shell windows. The program's debug information will then come out in that window. Note of course that you must start the program before you initialize the SMUS server, else the SMUS server initialization will launch the program automatically as normal. The startup routine recognizes whether or not either of the server programs are already there, connecting to them immediately if they are and launching them if they are not. If you do start one of the server programs by hand, realize that since the interface code didn't start it, the interface code won't receive the workbench termination message for the task (the interface code launches the servers as if they were launched from the workbench, hence they receive a startup message, and it is replied to when they exit). This can cause the shutdown handshaking to mess up, resulting in a hang. This should be a soft hang, though; that is, you should be able to hit control-C in the various windows and get the programs to exit. Note that this problem should only occur if one or both of the server programs are launched by hand via CLI commands. RPC Routines ------------ Below are the routines that constitute the Remote Procedure Call interface to the SMUS server. The demo program, main.c, uses this interface to exercise the server. AbortSongImmediately() Stops the current song immediately. Does not unload the song or any samples, nor does it terminate playsmus or loadsmus. AttachAllSamples(replyport) struct MsgPort *replyport; loads any samples necessary to insure all samples are loaded for all songs that are currently loaded int AttachSamples(songptr) Song *songptr; Given a pointer to a song returned by LoadSong, AttachSamples looks in Instruments: for an IFF file that's either IFF 8SVX, indicating that it is an 8SVX instrument, or IFF VOCE, indicating that it's a HIFI sample, and loads the voicebank. (More on voicebanks later) Returns a status as to whether it succeeded or not. ClearSampleTags() clears "tag" bits for all samples LoadCATArchive(catname) char *catname; Loads all the SMUS songs, voicebanks and 8SVX samples it finds in the named IFF CAT archive. void *LoadSong(songname) char *songname; loads a song, returns a pointer to the song structure or NULL if it couldn't load the song. Note that LoadSong does not insure that all samples are loaded, you must call AttachSamples to get the samples loaded (if they aren't already loaded) and attached to the song. PlaySong(songptr) void *songptr; Play the song pointed to by songptr. AttachSamples or AttachAllSamples must have been called. Does not return until song is finished. See StartSong for a version that returns immediately. PurgeUntaggedSamples() unloads all samples that aren't referenced by a song or "tagged" SetSampleTag(samplename) char *samplename; set "tag" bit for samplename SetSampleTagsOfSong(songptr) void *songptr; set the "tag" bit for any currently loaded samples that are referenced by the specified song StartFadeOut() Begin a fade out. The song will stop once the fade volume reaches zero. Does not unload the song or samples or terminate playsmus or loadsmus when the song stops. StartSong(songptr) void *songptr; Play the song pointed to by songptr. AttachSamples or AttachAllSamples must have been called prior to calling this guy. Returns immediately. AbortSongImmediately can be called to stop the song while it's playing. StartFadeOut starts a fade out which results shortly thereafter in the song stopping as well. After starting a song with StartSong, if the caller wants to wait at a later point, they may call WaitTilSongFinishes. Here is a case where perhaps using the direct message interface is better than the RPC library. When a song finishes, you get the play message back from playsmus at your reply port. SMUSTerminateServer() Terminate the server immediately. Causes the loader to unload all the currently loaded songs and samples, and terminates the loader and player immediately, even if a song is playing. TerminateServerWhenFinished() The same function as SMUSTerminateServer, except it waits until the end of the current song before terminating. If no song is playing, it terminates immediately. UnloadSong(songptr) Unloads song but does not remove song's samples. Returns an error if songptr is invalid. WaitTilSongFinishes() The caller waits until the song finishes, then WaitTilSongFinishes returns, again without unloading or terminating anything. Sample Management ----------------- New to the 2.2 release is sample management. This provides the programmer interfacing to the SMUS server with a number of capabilities for controlling what samples are to be loaded and unloaded from memory. Careful use of the routines can result in a minimized amount of sample loading from disk by leaving instruments for the current song that are used in the next song loaded while unloading the rest (any instruments the current song uses that the next song doesn't use). Example pseudocode of this type of activity is shown below: LoadSong A AttachSamples for song A PlaySong A LoadSong B ClearAllSampleTags SetSampleTagsOfSong B PurgeUntaggedSamples UnloadSong A AttachsSamples for song B PlaySong B If any of the samples used by song A are also used by song B, they will be retained when the PurgeUntaggedSamples is done. The subsequent AttachSamples for song B will only load any samples that song B uses that song A did not use. Note that the UnloadSong for song A need not have been done if you wanted to leave it in memory to play later. Songs are usually pretty small. (UnloadSong unloads the song but not any of its samples.) Note also that more complicated sample management involving multiple songs can be performed with the above routines. For example, you could do a SetSampleTagsOfSong for the next two songs you were going to play -- stuff like that. It was the decision to provide the capability of having many songs loaded without all their samples loaded that dictated making the routines such as AttachSamples, ClearAllSampleTags, SetSampleTagsOfSong, etc, separate rather than implicit within LoadSong and UnloadSong. A sort of "back door" is provided by which you can set the sample tag for a given sample explicitly by name. This is useful if you want to keep an instrument around even if no song that you're doing a SetSampleTagsOfSong for references it. The name needs to match the name of the sample as specified by the SMUS song that loaded it. When you do a PurgeUntaggedSamples while a song is playing, any samples referenced by that song are automatically tagged to prevent them from being purged while the player is still using them. Thus, while a song is playing, a ClearAllSampleTags followed by a PurgeUntaggedSamples with no intervening SetSampleTagsOfSong or SetSampleTag will purge all samples but the ones referenced by that song. The same activity with no song playing would purge all the currently loaded samples. CAT Archives ------------ The ability to read IFF CAT archives containing songs and samples is new to version 2.2. This allows you will to use Hackercorp's public domain IFF CAT archiver, iffar (included with your developer disk), to create a single file containing many songs, samples voicebanks and load them all once. This reduces clutter in your applications' directories and, more importantly, can improve floppy disk loading time by 50%. In order that your program get up and playing as soon as possible once it starts, we recommend that you construct a CAT archive containing your intro song and the samples and voicebanks it needs, then calling SMUSInitServer(), LoadCATArchive(), LoadSong(), AttachSamples() and StartSong() very early in your program's initialization sequence. Your program can then continue initializing while the server plays a song. When your program is fully up, it can start a fade on your intro song, or the intro song can continue playing while the user makes their first selection, or whatever. Remember that LoadSong() returns a pointer to a song which you use to identify the song when you ask the server to start playing the song. LoadSong first checks its list of already-loaded songs before loading a song, so if your intro song was in the CAT archive, the LoadSong() call will find it, hence LoadSong will not go to the disk for the song. The call to AttachSamples is necessary in order to connect the song's instrument pointers to the actual instrument samples. If the samples were in the archive or have already been loaded, the AttachSamples call will not cause any disk activity. Note that if some of the samples are present in memory (from reading a CAT archive or from doing an AttachSamples on another song, for example), they will be attached without disk activity, and then AttachSamples will load any instruments that the song requires that were not already present in memory. You should probably check the return code from the AttachSamples, because it returns an error if it couldn't find all the samples. An attempt to play a song which hasn't had all its samples attached also returns an error (and the song doesn't play). Loading from archives can be freely intermixed with regular song loading and sample attaching. Samples loaded from archives are manipulated by the sample management routines in the same manner as samples loaded from individual 8SVX files. Memory Model ------------ The code has only been tested under the Manx +L memory model. This is 32-bit integers and pointers, and requires linking with the "l32" libraries, such as cl32 for the C library (-lcl32 on the linker command line). Most of the casts are there for it to work in small model, but it hasn't been tested, and would probably require some dinking with sdb to find the problems (missed casts). If someone needs it really badly, let me know. If someone decides to fix it, better yet, and please forward the changes to me. Revision History ---------------- 0.0a The Epoch 0.0b - player no longer screws up when floppies are in use 0.0c - selection of nonexistent instruments are ignored - selection of nonexistent instruments are reported if code was compiled without NOAUDIT defined - added support for inline tempo changes (SMUS spec revised to include these, October 1988) - number of context switches per second has been vastly reduced, over 90% if, for example, sequencer is playing nothing faster than sixteenth notes - now does a much better job at finding the "right" octave and playback rate, or seems to. (The IFF SMUS and 8SVX docs are very unclear and/or missing stuff on the correlation between SMUS' MIDI notes and what 8SVX note and sample to play.) - doesn't hang at the end of songs where the length of all the tracks don't match (condition indicates a problem with the SMUS file itself, but we still don't want to hang) 0.0d 7/20/89 - interrupt handler rewritten in assembler - now cleans up small amounts of memory left behind when using voicebanks 1.0a 7/27/89 - uniprogram SMUS player assigned rev level 1, version 2.0 to be a split-program server capable of loading and playing at the same time 2.0 8/5/89 - server version of SMUS player working - RPC library and demo/test program - dropped the sub-letter version naming convention for SCCS compatibility 2.1 8/24/89 - squeezed all interface code into one file - code documentation improved - README file improved - README file written for cleanup routines - pruned superfluous includes from interface and demo code 2.2 beta 9/22/89 - implemented sample management - sample name comparisons are now case insensitive - updated documentation - shutdown code improved; bogus panics on shutdown eliminated - implemented CAT archive reading for songs and samples - song name comparisons are now case insensitive - song list management now handled with standard Amiga Exec routines (AddTail, Remove, FindName) rather than homegrown (sample list management already used standard routines) - demo program can now call TerminateServerWhenFinished - songs are now expected to be in a directory pointed to by a Song: assign. This makes the simultaneous use of both CAT archive-based and explicitly loaded songs easier. - The debug version of the loader now prints a message (including the sample name) whenever it loads or unloads a sample. This makes it easier to figure out what's going on when you're using the sample management routines. 2.2 beta-2 10/1/89 - voicebanks are now cached in memory. They are small enough that tagging is not supported for them, although the samples they reference are part of the tagging system, and they are unloaded when the server exits. - voicebanks are now recognized and loaded when found while loading CAT archives - VoiceBank: directory assign is no longer required (program now looks in the Instruments: directory for the instrument, and loads is as a voicebank if it's IFF VOCE or a regular 8SVX instrument if it's type is IFF 8SVX.) This also improves performance when loading samples. 2.2 10/15/89 - 2.2 formal release, no new capabilities added since 2.2-b2 Things In The Spec That Aren't In The Server -------------------------------------------- The server currently does not support embedded instruments. This is where instruments' 8SVX FORMs can be contained within a song file. I am unaware of any programs that can write files like that, anyway. It also makes storing and reusing samples harder because they're in songs rather than broken out in a directory where they can be found at will. Also, CAT archives (version 2.2 and beyond) provide a superset of these capabilities. The spec suggests that a player should downsample an instrument for which a request to play a note would result in a playback rate that's too high for the Amiga to play. We play the note down an octave rather than downsampling. We feel that this is appropriate for such a "captive" program as the SMUS server; that is, you have determined in advance what songs and samples are to be played and can use Audiomaster or another 8SVX-manipulating tool to make sure your instruments have all the necessary octaves. Epilogue -------- Anyway, give it a spin. I'm not claiming that it's 100% compatible with DMCS or anything, but it can play everything I've been able to scrounge up, about forty songs as of the time of this writing (7/89). By the way, if you have a SMUS file that the server does not play properly, please send it to me, email or surface mail, and I will try to fix the server to make it work. Regards, Karl Lehenbauer @ The Hacker's Haven 3918 Panorama Missouri City, TX 77459 (Usenet: uunet!sugar!karl, Internet: karl@sugar.hackercorp.com)