mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-05 20:40:30 +00:00
465 lines
18 KiB
Text
465 lines
18 KiB
Text
|
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 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.
|
||
|
|
||
|
|
||
|
C and C++ interfaces
|
||
|
--------------------
|
||
|
While the library is written in C++, an extensive 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 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 (also usable from C++)
|
||
|
|
||
|
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 (blargg_err_t
|
||
|
in the C++ interfaces), 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_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 );
|
||
|
|
||
|
* 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.
|
||
|
|
||
|
error = 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;
|
||
|
error = in.open( file_path );
|
||
|
|
||
|
char header [4];
|
||
|
error = in.read( &header, sizeof header );
|
||
|
...
|
||
|
|
||
|
Remaining_Reader rem( &header, sizeof header, &in );
|
||
|
error = 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, 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.
|
||
|
|
||
|
|
||
|
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.
|