Mercurial > audlegacy-plugins
view src/console/gme_notes.txt @ 2216:3673c7ec4ea2
Sync with schism's modplug engine. Suggested by G¸«ärkan Seng¸«än.
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Fri, 07 Dec 2007 12:08:47 -0600 |
parents | 986f098da058 |
children |
line wrap: on
line source
Game_Music_Emu 0.5.2 -------------------- Author : Shay Green <gblargg@gmail.com> 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 * C and C++ interfaces * Function reference * 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 * Deprecated features * Thanks Overview -------- This library contains several game music file emulators with a common interface. Each can load a game music file and play from any track. To play a track from a game music file, do the following: ** TODO: things are simpler now * Determine file's type (i.e. gme_nsf_emu, gme_spc_emu, etc.) * Create appropriate emulator and set sample rate with gme_new_emu() * Load file into emulator with gme_load() * 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. C and C++ interfaces -------------------- While the library is written in C++, a fairly complete C interface is provided in gme.h. This C interface will be referred to throughout this documentation unless a feature is only available in the full C++ interface. All C interface functions and other names have the gme_ prefix, so you can recognize a C++-only feature by the lack of gme_ in the names used (contact me if you'd like a feature added to the C interface). If you're building a shared library, I highly recommend sticking to the C interface only, because it will be much more stable between releases of the library than the C++ interface. Finally, the C and C++ interfaces can be freely mixed without problems. Compare demo/basics.c with demo/cpp_basics.cpp to see how the C and C++ interfaces translate between each other. Function reference ------------------ Read the following header files for a complete reference to functions and features. The second group of header files can only be used in C++. blargg_config.h Library configuration gme.h C interface Gme_File.h File loading and track information Music_Emu.h Track playback and adjustments Data_Reader.h Custom data readers Effects_Buffer.h Sound buffer with adjustable stereo echo and panning M3u_Playlist.h M3U playlist support Gbs_Emu.h GBS equalizer settings Nsf_Emu.h NSF equalizer settings Spc_Emu.h SPC surround disable Vgm_Emu.h VGM oversampling disable and custom buffer query 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. To improve usability for C programmers, C++ programmers unfamiliar with exceptions, and compatibility with older C++ compilers, the library does *not* throw any C++ exceptions and uses malloc() instead of the standard operator new. This means that you *must* check for NULL when creating a library object with the new operator. 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_load_file( music_emu, path, 0 ); 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. Ultimately you will use one of these to select which emulator to use for a file. 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. It takes an array of gme_type_t elements, allowing you to customize what game music types you accept. If you want to accept all types supported by the library, use gme_type_list(), otherwise pass an array of the types you support. For example, to support just NSF and GBS, and avoid having to compile/link the other emulator files, do this (be sure to end your array with 0): static gme_type_t types [] = { gme_nsf_type, gme_gbs_type, 0 }; file_type = gme_identify_extension( path, types ); 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(). M3U playlist support -------------------- The library supports playlists in the .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 (not shown here for clarity). The most basic is always available, simple calling load_file() with the path of a file: gme_load_file( music_emu, file_path ); * From a block of memory: gme_load_data( music_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 } gme_load_custom( music_emu, my_read, file_size, my_data ); * If you must load the file data into memory yourself, you can have the library use your data directly *without* making a copy. If you do this, you must not free the data until you're done playing the file. music_emu->load_mem( pointer, size ); * If you've already read the first bytes of a file (perhaps to determine the file type) and want to avoid seeking back to the beginning for performance reasons, use Remaining_Reader: Std_File_Reader in; in.open( file_path ); char header [4]; in.read( &header, sizeof header ); ... Remaining_Reader rem( &header, sizeof header, &in ); music_emu->load( rem ); If you merely need access to a file's header after loading, use the emulator-specific header() functions, after casting the Music_Emu pointer to the specific emulator's type. This example examines the chip_flags field of the header if it's an NSF file: if ( music_emu->type() == gme_nsf_type ) { Nsf_Emu* nsf_emu = (Nsf_Emu*) music_emu; if ( nsf_emu->header().chip_flags & 0x01 ) ... } Contact me if you want more information about loading files. 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. 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. Deprecated features ------------------- The following functions and other features have been deprecated and will be removed in a future release of the library. Alternatives to the deprecated features are listed to the right. Music_Emu::error_count() warning() load( header, reader ) see "Loading file data" above Spc_Emu::trailer() track_info() Spc_Emu::trailer_size() Gym_Emu::track_length() track_info() Vgm_Emu::gd3_data() track_info() Nsfe_Emu::disable_playlist() clear_playlist() 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.