raze/libraries/game-music-emu/gme.txt

377 lines
15 KiB
Text
Raw Normal View History

2019-11-10 22:58:51 +00:00
Game_Music_Emu 0.6.2
--------------------
Author : Shay Green <gblargg@gmail.com>
Maintainer : Michael Pyne <mpyne@purinchu.net>
Website : https://bitbucket.org/mpyne/game-music-emu/
Source : https://bitbucket.org/mpyne/game-music-emu/
License : GNU Lesser General Public License (LGPL), see LICENSE.txt
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.