Game_Music_Emu 0.5.5 -------------------- Author : Shay Green Website: http://www.slack.net/~ant/libs/ Forum : http://groups.google.com/group/blargg-sound-libs License: GNU Lesser General Public License (LGPL) Contents -------- * Overview * Error handling * Emulator types * M3U playlist support * Information fields * Track length * Loading file data * Sound parameters * VGM/GYM YM2413 & YM2612 FM sound * Modular construction * Obscure features * Solving problems * Thanks Overview -------- This library can open game music files, play tracks, and read game and track information tags. To play a game music file, do the following: * Open the file with gme_open_file() * Start a track with gme_start_track(); * Generate samples as needed with gme_play() * Play samples through speaker using your operating system * Delete emulator when done with gme_delete() Your code must arrange for the generated samples to be played through the computer's speaker using whatever method your operating system requires. There are many additional features available; you can: * Determine of the type of a music file without opening it with gme_identify_*() * Load just the file's information tags with gme_info_only * Load from a block of memory rather than a file with gme_load_data() * Arrange for a fade-out at a particular time with gme_set_fade * Find when a track has ended with gme_track_ended() * Seek to a new time in the track with gme_seek() * Load an extended m3u playlist with gme_load_m3u() * Get a list of the voices (channels) and mute them individually with gme_voice_names() and gme_mute_voice() * Change the playback tempo without affecting pitch with gme_set_tempo() * Adjust treble/bass equalization with gme_set_equalizer() * Associate your own data with an emulator and later get it back with gme_set_user_data() * Register a function of yours to be called back when the emulator is deleted with gme_set_user_cleanup() Refer to gme.h for a comprehensive summary of features. Error handling -------------- Functions which can fail have a return type of gme_err_t, which is a pointer to an error string (const char*). If a function is successful it returns NULL. Errors that you can easily avoid are checked with debug assertions; gme_err_t return values are only used for genuine run-time errors that can't be easily predicted in advance (out of memory, I/O errors, incompatible file data). Your code should check all error values. When loading a music file in the wrong emulator or trying to load a non-music file, gme_wrong_file_type is returned. You can check for this error in C++ like this: gme_err_t err = gme_open_file( path, &emu ); if ( err == gme_wrong_file_type ) ... To check for minor problems, call gme_warning() to get a string describing the last warning. Your player should allow the user some way of knowing when this is the case, since these minor errors could affect playback. Without this information the user can't solve problems as well. When playing a track, gme_warning() returns minor playback-related problems (major playback problems end the track immediately and set the warning string). Emulator types -------------- The library includes several game music emulators that each support a different file type. Each is identified by a gme_type_t constant defined in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use gme_open_file() or gme_open_data(), the library does the work of determining the file type and creating an appropriate emulator. If you want more control over this process, read on. There are two basic ways to identify a game music file's type: look at its file extension, or read the header data. The library includes functions to help with both methods. The first is preferable because it is fast and the most common way to identify files. Sometimes the extension is lost or wrong, so the header must be read. Use gme_identify_extension() to find the correct game music type based on a filename. To identify a file based on its extension and header contents, use gme_identify_file(). If you read the header data yourself, use gme_identify_header(). If you want to remove support for some music types to reduce your executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to support just NSF and GBS, use this: #define GME_TYPE_LIST \ gme_nsf_type,\ gme_gbs_type M3U playlist support -------------------- The library supports playlists in an extended m3u format with gme_load_m3u() to give track names and times to multi-song formats: AY, GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format itself is not well-defined so some m3u files won't work properly (particularly those provided with KSS files). Only m3u files referencing a single file are supported; your code must handle m3u files covering more than one game music file, though it can use the built-in m3u parsing provided by the library. Information fields ------------------ Support is provided for the various text fields and length information in a file with gme_track_info(). If you just need track information for a file (for example, building a playlist), use gme_new_info() in place of gme_new_emu(), load the file normally, then you can access the track count and info, but nothing else. M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS ------------------------------------------------------- Track Count | * * * * * * * * * | System | * * * * * * * * * * | Game | * * * * * * * | Song | * * * * * * * | Author | * * * * * * * * | Copyright | * * * * * * * * | Comment | * * * * | Dumper | * * * * | Length | * * * * * * | Intro Length| * * * | Loop Length | * * * As listed above, the HES and KSS file formats don't include a track count, and tracks are often scattered over the 0-255 range, so an m3u playlist for these is a must. Unavailable text fields are set to an empty string and times to -1. Your code should be prepared for any combination of available and unavailable fields, as a particular music file might not use all of the supported fields listed above. Currently text fields are truncated to 255 characters. Obscure fields of some formats are not currently decoded; contact me if you want one added. Track length ------------ The library leaves it up to you as to when to stop playing a track. You can ask for available length information and then tell the library what time it should start fading the track with gme_set_fade(). By default it also continually checks for 6 or more seconds of silence to mark the end of a track. Here is a reasonable algorithm you can use to decide how long to play a track: * If the track length is > 0, use it * If the loop length > 0, play for intro + loop * 2 * Otherwise, default to 2.5 minutes (150000 msec) If you want to play a track longer than normal, be sure the loop length isn't zero. See Music_Player.cpp around line 145 for example code. By default, the library skips silence at the beginning of a track. It also continually checks for the end of a non-looping track by watching for 6 seconds of unbroken silence. When doing this is scans *ahead* by several seconds so it can report the end of the track after only one second of silence has actually played. This feature can be disabled with gme_ignore_silence(). Loading file data ----------------- The library allows file data to be loaded in many different ways. All load functions return an error which you should check. The following examples assume these variables: Music_Emu* emu; gme_err_t error; If you're letting the library determine a file's type, you can use either gme_open_file() or gme_open_data(): error = gme_open_file( pathname, &emu ); error = gme_open_data( pointer, size, &emu ); If you're manually determining file type and using used gme_new_emu() to create an emulator, you can use the following methods of loading: * From a block of memory: error = gme_load_data( emu, pointer, size ); * Have library call your function to read data: gme_err_t my_read( void* my_data, void* out, long count ) { // code that reads 'count' bytes into 'out' buffer // and return 0 if no error } error = gme_load_custom( emu, my_read, file_size, my_data ); Sound parameters ---------------- All emulators support an arbitrary output sampling rate. A rate of 44100 Hz should work well on most systems. Since band-limited synthesis is used, a sampling rate above 48000 Hz is not necessary and will actually reduce sound quality and performance. All emulators also support adjustable gain, mainly for the purpose of getting consistent volume between different music formats and avoiding excessive modulation. The gain can only be set *before* setting the emulator's sampling rate, so it's not useful as a general volume control. The default gains of emulators are set so that they give generally similar volumes, though some soundtracks are significantly louder or quieter than normal. Some emulators support adjustable treble and bass frequency equalization (AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer(). Parameters are specified using gme_equalizer_t eq = { treble_dB, bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble for an extra crisp sound. Bass_freq sets the frequency where bass response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, and 15000 Hz removes all bass. For example, the following makes the sound extra-crisp but lacking bass: gme_equalizer_t eq = { 5.0, 1000 }; gme_set_equalizer( music_emu, &eq ); Each emulator's equalization defaults to approximate the particular console's sound quality; this default can be determined by calling equalizer() just after creating the emulator. The Music_Emu::tv_eq profile gives sound as if coming from a TV speaker, and some emulators include other profiles for different versions of the system. For example, to use Famicom sound equalization with the NSF emulator, do the following: music_emu->set_equalizer( Nsf_Emu::famicom_eq ); VGM/GYM YM2413 & YM2612 FM sound -------------------------------- The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound chip emulator based on the Gens project. Because this has some inaccuracies, other YM2612 emulators can be used in its place by re-implementing the interface in YM2612_Emu.h. Available on my website is a modified version of MAME's YM2612 emulator, which sounds better in some ways and whose author is still making improvements. VGM music files using the YM2413 FM sound chip are also supported, but a YM2413 emulator isn't included with the library due to technical reasons. I have put one of the available YM2413 emulators on my website that can be used directly. Modular construction -------------------- The library is made of many fairly independent modules. If you're using only one music file emulator, you can eliminate many of the library sources from your program. Refer to the files list in readme.txt to get a general idea of what can be removed, and be sure to edit GME_TYPE_LIST (see "Emulator types" above). Post to the forum if you'd like me to put together a smaller version for a particular use, as this only takes me a few minutes to do. If you want to use one of the individual sound chip emulators (or CPU cores) in your own console emulator, first check the libraries page on my website since I have released several of them as stand alone libraries with included documentation and examples on their use. If you don't find it as a standalone library, contact me and I'll consider separating it. The "classic" sound chips use my Blip_Buffer library, which greatly simplifies their implementation and efficiently handles band-limited synthesis. It is also available as a stand alone library with documentation and many examples. Obscure features ---------------- The library's flexibility allows many possibilities. Contact me if you want help implementing ideas or removing limitations. * Uses no global/static variables, allowing multiple instances of any emulator. This is useful in a music player if you want to allow simultaneous recording or scanning of other tracks while one is already playing. This will also be useful if your platform disallows global data. * Emulators that support a custom sound buffer can have *every* voice routed to a different Blip_Buffer, allowing custom processing on each voice. For example you could record a Game Boy track as a 4-channel sound file. * Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive synthesis on "classic" emulators, which might help on some really old processors. This significantly lowers sound quality and prevents treble equalization. Try this if your platform's processor isn't fast enough for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth the quality loss. Solving problems ---------------- If you're having problems, try the following: * If you're getting garbled sound, try this simple siren generator in place of your call to play(). This will quickly tell whether the problem is in the library or in your code. static void play_siren( long count, short* out ) { static double a, a2; while ( count-- ) *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); } * Enable debugging support in your environment. This enables assertions and other run-time checks. * Turn the compiler's optimizer is off. Sometimes an optimizer generates bad code. * If multiple threads are being used, ensure that only one at a time is accessing a given set of objects from the library. This library is not in general thread-safe, though independent objects can be used in separate threads. * If all else fails, see if the demos work. Thanks ------ Big thanks to Chris Moeller (kode54) for help with library testing and feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and for original work on openspc++ that was used when developing Spc_Emu. Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using, and giving feedback for the library in their respective game music players. More recently, Lucas Paul and Michael Pyne have helped nudge the library into a public repository and get its interface more stable for use in shared libraries.