The Midi Kit: BMidiSynthFile

The Midi Kit Table of Contents | The Midi Kit Index

# BMidiSynthFile

Derived from: public BMidiSynth

Declared in: be/midi/MidiSynthFile.h

Library: libmidi.so

###### Summary

The BMidiSynthFile class reads a standard MIDI file and plays it on the General MIDI synthesizer. Each BMidiSynthFile object can read (and play) only one file at a time. To use a BMidiSynthFile, you create the object, load a MIDI file, and tell the object to `Start()`:

       /* Create and initialize a BMidiSynthFile. */
       BMidiSynthFile midiSynthFile;
       entry_ref midiRef;
       get_ref_for_path("/boot/optional/midi/QuickBrownFox.mid", &midiRef);
       midiSynthFile.LoadFile(&midiRef);

       /* Play the file. */
       midiSynthFile.Start();

You should create a different BMidiSynthFile object for each MIDI file that you want to mix together into a single performance.

| In certain circumstances, a single BMidiSynthFile object can load and play more than one MIDI file at the same time, but you shouldn't rely on this feature.

### Loading and Playing

When you call `LoadFile()`, the BMidiSynthFile object automatically calls...

       EnableInput(true, false)

It then loads the file's MIDI data into the synthesizer, which loads all the instruments that are needed by the file. If the file uses a lot of different instruments, loading the file can take some time.

When the file is finished playing (either because it's reached the end, or because you called `Stop()`) the instruments are not unloaded. This cuts the overhead if you play the file a second time.

### The Run Thread and Function

BMidiSynthFile is different from other `Start()`-able BMidi objects in that it doesn't have a run loop. The MIDI data is parsed and realized in the synthesizer's subscriber thread (the thread that dumps data into the DAC stream). The lack of a run loop shouldn't affect the way you write your code, but you should be aware that the thread isn't there so you won't go looking for it while you're developing your app

Furthermore, BMidiSynthFile doesn't implement the `Run()` function. Starting and stopping the object's performance (activities that are normally handled in the `Run()` function) are handled by the synthesizer in its subscriber thread. If you create a BMidiSynthFile subclass, don't try to resurrect the `Run()` function—leave it as a no-op.

As with the BMidiSynth class, the BMidiSynthFile MIDI hook implementations don't call the spray functions. This means that you can't, for example, connect a BMidiSynthFile to a BMidiPort. If you want to play a MIDI file out a MIDI port, use BMidiStore to represent and play the file.

## Constructor and Destructor

### BMidiSynthFile()

`BMidiSynthFile(`void`) `

Creates a new, empty BMidiSynthFile object. Also constructs a BSynth object and assigns the object to the app-wide `be_synth` variable (if the object doesn't already exist). To load a MIDI file into the BMidiSynthFile object, you must call `LoadFile()` after construction. Unlike plain BMidiSynth instances, however, you don't have to call `EnableInput()` (`LoadFile()` calls it for you).

### ~BMidiSynthFile()

virtual`` `~BMidiSynthFile() `

Disconnects the object from the synthesizer, unloads the object's file (and all its instruments) and destroys the object.

## Member Functions

### Duration() , Position() , Seek()

int32 `Duration(`void`)` const
status_t `Position(`int32 ticks`) `
int32 `Seek(`void`) `

`Duration()` returns the length of the object's loaded data, measured in 64ths of a MIDI tick.

`Position()` sets the object's current position within the data.

`Seek()` returns the object's current position.

If you want to reposition the "song pointer", you should do it as a percentage of the `Duration()` measurement.

A MIDI tick is defined by the "MIDI Division" field at the bginning of every MIDI file; this value can be specified by the file as either "ppgn" (tempo-dependent) or frames:subframes per second (tempo-independent). The BMidiSynthFile class assumes that the division is tempo-independent.

RETURN CODES

`Currently Position()` always returns `B_OK`.

### EnableLooping() see Start()

### GetMuteMap() see MuteTrack()

### GetPatches()

status_t `GetPatches(`int16 *instruments, int16 *count`) `

Returns an array of the instruments numbers that are needed by the loaded MIDI file. The instruments array should be 128 elements long (to be on the safe side), and must be allocated before it's passed in. Upon return, function sets count to the number of instrument numbers that it placed in the array. For example:

       int16 insts[128];
       int16 count;
       midiSynthFile.GetPatches(insts, &count);
       for (int n = 0; n < count; n++)
          printf("The file uses instrument #%dn", insts[n]);

### GetSoloMap() see MuteTrack()

### IsFinished() see Start()

### LoadFile() , UnloadFile()

status_t `LoadFile(`const entry_ref *midiFileRef`) `
void `UnloadFile(`void`)` const

`LoadFile()` tells the object to load, into the synthesizer, the MIDI data from the midiFileRef. The synthesizer caches the data for subsequent playback (which you initiate through `Start()`). All instruments that are needed to play the file are loaded into the synthesizer, if they're aren't loaded already.

`UnloadFile()` stops playback of the data that was loaded through this object (if it's playing), and flushes the data (removes it from the synthesizer).

| In certain circumstances, a single BMidiSynthFile object can load and play more than one MIDI file at the same time, but you shouldn't rely on this feature.

RETURN CODES

`B_OK`. The file was found and loaded.

- `B_BAD_MIDI_DATA`. midiFileRef isn't a MIDI file, or contains corrupt data.

- `B_NO_MEMORY`. Not enough memory to load the file.

- Other POSIX errors. The file couldn't be opened or read.

### MuteTrack() , GetMuteMap() , SoloTrack() , GetSoloMap()

| These functions are broken; don't use them.

### Pause() see Start()

### Position() see Duration()

### Resume() see Start()

### ScaleTempoBy() see Tempo()

### Seek() see Duration()

### SetFileHook() see Start()

### SetTempo() see Tempo()

### SoloTrack() see MuteTrack()

### Start() , Stop() , Fade() , Pause() , Resume() , IsFinished() , EnableLooping() , SetFileHook() , synth_file_hook

virtual status_t `Start(`void`) `
virtual void `Stop(`void`) `
void `Fade(`void`) `
void `Pause(`void`) `
void `Resume(`void`) `
bool `IsFinished(`void`)` const
void `EnableLooping(`bool loop`) `
void `SetFileHook(`synth_file_hook fileDone, int32 arg`) `
typedef void (*`synth_file_hook`)(int32 arg)

These functions control the object's performance.

`Start()` tells the synthesizer to start playing the object's loaded MIDI data beginning at the beginning. Note that Start() does not halt a performance in progress; in other words, if the object is already playing its data, you'll get a second, performance while the first continues.

`Stop()` immediately halts the currently playing data. `Fade()` also stops playback, but is a bit more graceful: It fades out the sound before killing it.

| BMidiSynthFile's `Start()` and `Stop()` replace the functions defined by BMidi; in particular, they spawn and control a "run" thread. See "The Run Thread and Function" on page33 for more information.

`Pause()` and `Resume()` do as they say. Note that when you resume playback, "old" notes are not regenerated, but exact timing is respected. For example, lets say you have a MIDI file that contains two notes, one that starts at time 1.0 (seconds) and lasts for 10.0 seconds, and the other starts at time 9.0. You start the file, then `Pause()` at time 3.0; as expected, the first note stops. After awhile, you `Resume()`; there's a six second silence and then the second note plays.

`IsFinished()` returns `false` if the object is currently playing (or paused during play). Note that the function returns `false` before you load a file, and returns `true` after you've loaded a file but before you begin playing it.

`EnableLooping(true)` tells the object to replay the file when it reaches the end of the data. If the argument is `false`, the file isn't replayed. `Stop()` always shuts up playback, even if looping is enabled.

`SetFileHook()` registers a function that's called when the object is finished playing (either because it ran out of data or `Stop()` was called. arg is passed to the hook function as its sole argument. Note that the hook function is called when the object is completely finished—it isn't called at the end of each pass through the data while looping is enabled.

RETURN CODES

Start() returns...

- B_OK. The data was successfully started.

- `B_TOO_MANY_SONGS_PLAYING`. The synthesizer is too busy with other data streams.

### Stop() see Start()

### synth_file_hook see Start()

### Tempo() , SetTempo() , ScaleTempoBy()

int32 `Tempo(`void`)` const
void `SetTempo(`int32 beatsPerMinute`) `
void`ScaleTempoBy(`double scalar`) `

`Tempo()` returns the tempo of the data, as read from the MIDI file (or as set by the other functions), in beats-per-minute. The other two functions set the tempo: `SetTempo()` sets it absolutely in beats-per-minute, and `ScaleTempoBy()` scales the current tempo by the argument (scalar == 2.0 means the data is played twice as fast, scalar == .5 is twice as slow, and so on).

### UnloadFile() see LoadFile()

The Midi Kit Table of Contents | The Midi Kit Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..