view src/console/gme_notes.txt @ 648:0eca44731221 trunk

[svn] - fix timing on fades
author nenolod
date Wed, 14 Feb 2007 01:43:35 -0800
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.