Music routines (MIDI)

Allegro allows you to play MIDI files. MIDI files basically contain notes and the type of instrument that is meant to play them, so they are usually very small in size. However, it's up to the sound card of the end user to play the notes, and sound cards have been historically known to have poor MIDI performance (at least those oriented to the consumer market). Few consumer cards feature decent MIDI playback. Still, as a game creator you can never be sure if the music of your game will be played as you meant it, because it totally depends on the hardware of the user.

For this reason Allegro also provides a DIGMID driver. This is a software implementation of the so called Wavetable synthesis. Sound cards featuring this store digital samples of real instruments at different pitches, interpolating those that are not recorded, thus achieving a high sound quality. Implementing this in software makes you sure that the quality you hear on your computer is that which will be heard by end users using the same driver.

The disadvantage of the DIGMID driver is that it uses more CPU than simple MIDI playback, and it steals some hardware voices from the sound card, which might be more critical for the end user experience than the background music. At the Allegro homepage (http://alleg.sourceforge.net/) you can find more information about DIGMID and where to download digital samples for your MIDI files.


MIDI *load_midi(const char *filename);

Loads a MIDI file (handles both format 0 and format 1). Example:
      MIDI *music;
      music = load_midi("backmus.mid");
      if (!music)
         abort_on_error("Couldn't load background music!");

Return value: Returns a pointer to a MIDI structure, or NULL on error. Remember to free this MIDI file later to avoid memory leaks.

See also: destroy_midi, play_midi, get_midi_length.
Examples using this: exmidi.
void destroy_midi(MIDI *midi);

Destroys a MIDI structure when you are done with it. It is safe to call this even when the MIDI file might be playing, because it checks and will kill it off if it is active. Use this to avoid memory leaks in your program.
See also: load_midi.
Examples using this: exmidi.
void lock_midi(MIDI *midi);

Under DOS, locks all the memory used by a MIDI file. You don't normally need to call this function because load_midi() does it for you.
See also: load_midi.
int play_midi(MIDI *midi, int loop);

Starts playing the specified MIDI file, first stopping whatever music was previously playing. If the loop flag is set to non-zero, the data will be repeated until replaced with something else, otherwise it will stop at the end of the file. Passing a NULL pointer will stop whatever music is currently playing.

Return value: Returns non-zero if an error occurs (this may happen if a patch-caching wavetable driver is unable to load the required samples, or at least it might in the future when somebody writes some patch-caching wavetable drivers :-)

See also: install_sound, load_midi, play_looped_midi, stop_midi, midi_pause, midi_seek, midi_pos, midi_time, midi_msg_callback.
Examples using this: exmidi.
int play_looped_midi(MIDI *midi, int loop_start, int loop_end);

Starts playing a MIDI file with a user-defined loop position. When the player reaches the loop end position or the end of the file (loop_end may be -1 to only loop at EOF), it will wind back to the loop start point. Both positions are specified in the same beat number format as the midi_pos variable.

Return value: The return value has the same meaning as that of play_midi(): non-zero if an error occurs, zero otherwise.

See also: play_midi, midi_pos, midi_loop_start.
void stop_midi();

Stops whatever music is currently playing. This is the same thing as calling play_midi(NULL, FALSE).
See also: play_midi, midi_pause.
void midi_pause();

Pauses the MIDI player.
See also: play_midi, stop_midi, midi_resume, midi_seek.
Examples using this: exmidi.
void midi_resume();

Resumes playback of a paused MIDI file.
See also: midi_pause.
Examples using this: exmidi.
int midi_seek(int target);

Seeks to the given midi_pos in the current MIDI file. If the target is earlier in the file than the current midi_pos it seeks from the beginning; otherwise it seeks from the current position.

Return value: Returns zero if it could successfully seek to the requested position. Otherwise, a return value of 1 means it stopped playing, and midi_pos is set to the negative length of the MIDI file (so you can use this function to determine the length of a MIDI file). A return value of 2 means the MIDI file looped back to the start.

See also: play_midi, midi_pos.
int get_midi_length(MIDI *midi);

This function will simulate playing the given MIDI, from start to end, to determine how long it takes to play. After calling this function, midi_pos will contain the negative number of beats, and midi_time the length of the midi, in seconds.

Note that any currently playing midi is stopped when you call this function. Usually you would call it before play_midi, to get the length of the midi to be played, like in this example:

      length = get_midi_length(my_midi);
      play_midi(my_midi);
      do {
         pos = midi_time;
         textprintf_ex(screen, font, 0, 0, c, -1, "%d:%02d / %d:%02d\n",
            pos / 60, pos % 60, length / 60, length % 60);
         rest(100);
      } while(pos <= length);

Return value: Returns the value of midi_time, the length of the midi.

See also: load_midi, midi_time, midi_pos.
Examples using this: exmidi.
void midi_out(unsigned char *data, int length);

Streams a block of MIDI commands into the player in real-time, allowing you to trigger notes, jingles, etc, over the top of whatever MIDI file is currently playing.
See also: install_sound, load_midi_patches, midi_recorder.
int load_midi_patches();

Forces the MIDI driver to load the entire set of patches ready for use. You will not normally need to call this, because Allegro automatically loads whatever data is required for the current MIDI file, but you must call it before sending any program change messages via the midi_out() command.

Return value: Returns non-zero if an error occurred.

See also: install_sound, midi_out.
extern volatile long midi_pos;

Stores the current position (beat number) in the MIDI file, or contains a negative number if no music is currently playing. Useful for synchronising animations with the music, and for checking whether a MIDI file has finished playing.
See also: play_midi, midi_msg_callback.
Examples using this: exmidi.
extern volatile long midi_time;

Contains the position in seconds in the currently playing midi. This is useful if you want to display the current song position in seconds, not as beat number.
See also: play_midi, midi_pos, get_midi_length.
Examples using this: exmidi.
extern long midi_loop_start;

extern long midi_loop_end;

The loop start and end points, set by the play_looped_midi() function. These may safely be altered while the music is playing, but you should be sure they are always set to sensible values (start < end). If you are changing them both at the same time, make sure to alter them in the right order in case a MIDI interrupt happens to occur in between your two writes! Setting these values to -1 represents the start and end of the file respectively.
See also: play_looped_midi.
extern void (*midi_msg_callback)(int msg, int byte1, int byte2);

extern void (*midi_meta_callback)(int type, const unsigned char *data, int length);

extern void (*midi_sysex_callback)(const unsigned char *data, int length);

Hook functions allowing you to intercept MIDI player events. If set to anything other than NULL, these routines will be called for each MIDI message, meta-event, and system exclusive data block respectively. They will execute in an interrupt handler context, so all the code and data they use should be locked, and they must not call any operating system functions. In general you just use these routines to set some flags and respond to them later in your mainline code.
See also: play_midi.
int load_ibk(char *filename, int drums);

Reads in a .IBK patch definition file for use by the Adlib driver. If drums is set, it will load it as a percussion patch set, otherwise it will use it as a replacement set of General MIDI instruments. You may call this before or after initialising the sound code, or can simply set the ibk_file and ibk_drum_file variables in the configuration file to have the data loaded automatically. Note that this function has no effect on any drivers other than the Adlib one!

Return value: Returns non-zero on error.

See also: install_sound.

Back to contents