The Midi Kit: BSynth

The Midi Kit Table of Contents | The Midi Kit Index

# BSynth

Derived from: (none)

Declared in: be/midi/Synth.h

Library: libmidi.so

###### Summary

The BeOS includes a 16-channel General MIDI software synthsizer designed by HeadSpace Inc. The BSynth class is the interface to the synthesizer itself. Any application that wants to use the synthesizer must include a BSynth object; however, most applications won't need to create the object directly: The BMidiSynth, BMidiSynthFile, and BSamples classes create a BSynth object for you. Furthermore, since BSynth doesn't inherit from BMidi, it doesn't have any API for actually playing MIDI data. To play MIDI data, you need an instance of BMidiSynth or BMidiSynthFile.

An application can have only one BSynth object at a time. The object is represented globally (within your app) as `be_synth`. The classes that create a BSynth for you (BMidiSynth and so on) won't clobber an existing `be_synth`, but the BSynth constructor will.

When it's created, the `be_synth` object tries to find an instrument definition (or "synth") file. This is a file that contains the data (samples and instructions) for creating General MIDI instruments. The BeOS provides two such files (both designed by HeadSpace, and both stored in `B_SYNTH_DIRECTORY`):

- `B_BIG_SYNTH_FILE (/boot/beos/etc/synth/big_synth.sy)` contains 16-bit, 22 kHz data. It takes about 5 Megs of memory when fully loaded.

- `B_LITTLE_SYNTH_FILE (/boot/beos/etc/synth/little_synth.sy)` is 8-bit, 11 kHz data. It's a quarter the size of the big synth, but lacks the big file's fidelity.

The instrument data is read from the file as it's needed. To "pre-load" the entire synth file, use the `BMidiSynth::EnableInput()` function.

## BSynth and the Audio Server

The synthesizer produces sound by taking over the Audio Server's DAC stream. It resets the size and number of buffers in the stream, sets the sampling rate, and adds a BSubscriber to the front of the stream. If you want to mix sound files into the MIDI synthesis, you should use the BSamples object rather than add your own DAC stream subscribers. However, if you really want to add your own sample-generating subscribers, don't add them to the front of the DAC stream after the `be_synth` subscriber has been added—your subscriber's samples will be clobbered.

The interaction between the synthesizer and the Media Kit will be cleaned up in a subsequent release.

The DAC stream's previous settings are restored when `be_synth` is destroyed.

## Synthesis Capacity

The synthesizer can generate up to 32 voices at a time, where a "voice" is either an individual (synthesized) note, or a stream of samples from a BSamples object. By default, it apportions 28 voice "slots" for synthesis and 4 for samples. You can change the settings through the `SetVoiceLimits()` function.

If you ask for more voices than there are voice slots (for example, if you ask for a 29'th note when there are already 28 singing), the synthesizer will try to kill an old note in order to make room for the new note.

There's no guarantee that the synthesizer and DAC stream will have enough time to generate and process everything you ask for, even if you're running below the 32 voice limit. On a lightly loaded, reasonably fast machine, you shouldn't hear any glitches, but a heavy MIDI command stream (for example) could bog it down.

## Recording a Performance

There's no API for automatically writing the synthesizer's output to a file. To record a synthesizer performance you have to create your own BSubscriber, add it to the DAC stream (downstream of the synthesizer), and write out the samples that it receives. (See the Media Kit for more information.)

In some cases, the act of recording can be enough of a CPU drag that the synthesizer falls behind realtime (actually, it's the synthesizer's BSubscriber that's getting behind). It may not sound great while you're monitoring the recording, but the data that's written to the file probably won't be affected—the glitches won't be written to the file.

## Constructor and Destructor

### BSynth()

`BSynth(`void`) `
`BSynth(`synth_mode mode`) `

Creates and initiailzes a new BSynth object and sets `be_synth` to point to it. The BSynth that `be_synth` currently points to (if any) is deleted. You can only construct one BSynth object per application. Every application that wants to use the synthesizer must have its own BSynth—you can't "share" another application's `be_synth` object. The constructors for the other synthesis classes (BMidiSynth, BMidiSynthFile, and BSamples) create a BSynth for you if one doesn't already exist.

The default constructor sets the following synthesis parameters, shown here with the functions that you can use to reset the values—and that you should refer to for further explanation:

Parameter | Value | Function
--- | --- | ---
Output sampling rate | 22 kHz | `SetSamplingRate()`
Sample interpolation | `B_LINEAR_INTERPOLATION` | `SetInterpolation()`
Max synth voices | 28 | `SetVoiceLimits()`
Max sample voices | 4 | `SetVoiceLimits()`
Limiter threshhold | 7 | `SetVoiceLimits()`
Reverb enabled | `true` | `EnableReverb()`
Reverb | `B_REVERB_BALLROOM` | `SetReverb()`
Synth mode | B_SYNTH_NONE | LoadSynthData()

You must call `LoadSynthData()` after calling the default constructor to set the synth mode.

The `synth_mode` constructor sets the synthesis parameters (as above) and then sets the synth mode to the argument, one of `B_BIG_SYNTH`, `B_LITTLE_SYNTH`, or `B_SAMPLES_ONLY`. See `LoadSynthData()` for `synth_mode` definitions.

### ~BSynth()

virtual `~BSynth() `

The destructor stops the synthesizer if it's currently playing anything, frees all synthesis-related storage that the BSynth object allocated, and sets `be_synth` to point to `NULL`.

## Member Functions

### CountClients()

int32 `CountClients(`void`)` const

Returns the number of synthesis objects (BMidiSynth and BMidiSynthFile) that are actively feeding data to the synthesizer. Note that this count does not include BSamples objects.

### EnableReverb() , IsReverbEnabled() , SetReverb() , Reverb() , reverb_mode

status_t `EnableReverb(`bool reverb_enabled`) `
bool `IsReverbEnabled(`void`)` const
void `SetReverb(`reverb_mode reverb`) `
reverb_mode `Reverb(`void`)` const
typedef enum { `B_REVERB_NONE`, `B_REVERB_CLOSET`, `B_REVERB_GARAGE`, `B_REVERB_BALLROOM`, `B_REVERB_CAVERN`, `B_REVERB_DUNGEON` } `reverb_mode `

`EnableReverb()` turns on and off `be_synth`'s reverberator. `IsReverbEnabled()` returns the current reverberator-enabled state. Reverb is enabled by default.

`SetReverb()` sets the reverberator's strength. The constants, shown above, are listed in order of increasing "wetness." `Reverb()` returns the current setting. Setting the reverb mode doesn't enable the reverberator.

To turn off the reverberator, do this:

       EnableReverb(false); /* Good *.

...rather than:

       SetReverb(B_REVERB_NONE); /* Bad */

RETURN CODES

`EnableReverb()` returns...

- B_OK. The reverberator was successfully enabled/disabled.

- `B_NO_MEMORY`. Not enough memory to setup the reverberator.

### GetAudio()

int32 `GetAudio(`int16 *left, int16 *right, int32 sampleCount) const

Returns, in left and right, the last sampleCount'th sample frames (split into left and right channels) generated by the synthesizer. Storage for the samples must be allocated by the caller. The function may return fewer samples than requested. The function returns the number of samples that were written into (each of) left and right.

This function is designed to feed waveform displays (and the like); it isn't intended to be used as a "sound spigot" that you can pipe to a file (for example).

### Interpolation() see SetSamplingRate()

### interpolation_mode see SetSamplingRate()

### IsLoaded() see LoadSynthData()

### IsReverbEnabled() see EnableReverb()

### LimiterThreshhold() see SetVoiceLimits()

### LoadSynthData() , Unload() , SynthMode() , IsLoaded()

status_t `LoadSynthData(`synth_mode mode`) `
status_t `LoadSynthData(`entry_ref *instrument_file`) `
void`` `Unload(`void`) `
synth_mode `SynthMode(`void`)` const
bool `IsLoaded(`void`)` const

`LoadSynthData(`) tells `be_synth` which synth file to use (and unloads the one currently in use, if any). The first version lets you specify the synth file through a synth_mode constant:

- `B_BIG_SYNTH`. Use the big synth file.

- `B_LITTLE_SYNTH`. Use the little synth file.

- `B_SAMPLES_ONLY`. Don't use a synth file, but prepare the object so it can play sampled sounds. You only use this mode if you're only going to use the BSamples object (in other words, if you're not using BMidiSynth or BMidiSynthFile).

If the synthesizer is initialized with a synth file, it will automatically know how to play BSample data.

| Currently, `B_SAMPLES_ONLY` doesn't work. You must use one of the other two constants (`B_BIG_SYNTH` or `B_LITTLE_SYNTH`).

The second version lets you set the synth file as an `entry_ref`, thus providing the opportunity to specify a custom synth file. Unfortunately, the synth file format isn't currently public, so you can't create your own synth files (yet).

LoadSynthData() doesn't actually read the instrument definitions from the synth file—in other words, it doesn't really "load" anything. The instruments are loaded as needed during a performance (as specified by a BMidiSynth[File] object). To force instruments to be read, use BMidiSynth's `EnableInput()` or `LoadInstrument()` function.

`Unload()` stops the synthesizer (if it's currently playing), forgets the instrument file that was used to initialize the synthesizer, and steps out of the audio output mechanism. After you call `Unload()`, the `be_synth` object is good for nothing until LoadSynthData() is called (whether directly or through a constructor).

`SynthMode()` returns `be_synth`'s current synth mode, one of the three modes listed above or `B_NO_SYNTH` if the mode hasn't been set.

`IsLoaded()` returns `true` if `be_synth` has been initialized and is ready to go. Otherwise, it returns `false`.

RETURN CODES

`LoadSynthData()` returns...

- `B_OK`. `be_synth` was successfully initialized.

- `B_BAD_VALUE`. Invalid argument.

- `B_NO_MEMORY`. Not enough memory to initialize the synthesizer.

- POSIX errors. The synth file wasn't found or couldn't be opened.

### MaxSampleVoices() see SetVoiceLimits()

### MaxSynthVoices() see SetVoiceLimits()

### Pause() , Resume()

void`Pause(`void`) `
void`Resume(`void`) `

`Pause()` tells the synthesizer to stop producing sound. It doesn't suspend non-synthesis BMidi objects—in other words, `Pause()` doesn't suspend BMidiPort or BMidiStore objects.

`Resume()` tells the synthesizer to resume producing sound. BMidiSynthFile objects continue reading from where they were paused; BSamples objects start playing from the beginning of their sample data (they don't continue from where they were paused).

### Reverb() see EnableReverb()

### reverb_mode see EnableReverb()

### SampleVolume() see SetSynthVolume()

### SamplingRate() see SetSamplingRate()

### SetControllerHook() , synth_controller_hook

void `SetControllerHook(`int16 controller, synth_controller_hook controlHook`)`
typedef void (*`synth_controller_hook`)`(`int16 channel, int16 controller, int16 value`) `

Registers a hook function (controlHook) that's invoked whenver a MIDI control message is applied to controller. The hook function is invoked just after the control message is processed by the synthesizer. The function is passed the channel, controller number, and controller value as taken from the control message.

### SetInterpolation() see SetSamplingRate()

### EnableReverb()

### SetSampleVolume() see SetSynthVolume()

### SetSamplingRate() , SamplingRate() , SetInterpolation() , Interpolation() , interpolation_mode

status_t `SetSamplingRate(`int32 rate`) `
int32 `SamplingRate(`void`)` const
status_t `SetInterpolation(`interpolation_mode interp`) `
interpolation_mode `Interpolation(`void`)` const
typedef enum { `B_DROP_SAMPLE`, `B_2_POINT_INTERPOLATION`, `B_LINEAR_INTERPOLATION` } `interpolation_mode `

`SetSamplingRate()` sets the frequency at which `be_synth` produces data, in frames (of audio data) per second. Acceptable rates are 44100, 22050, and 11025; rate is rounded to the nearest acceptable value. The default is 22050.

`SamplingRate()` returns the sampling rate as previously set by `SetSamplingRate()`.

`be_synth`'s sampling rate is independent of the DAC stream's sampling rate. For example, while the default `be_synth` rate is 22050, the default DAC stream rate is 44100. If the two rates don't match, `be_synth`'s BSubscriber object "interpolates" the be_synth data before dumping it into the DAC stream. There are three interpolation schemes, which you set through `SetInterpolation()`:

Constant | Meaning
--- | ---
`B_DROP_SAMPLE` | Samples are repeated or dropped. It sounds cheap because it is cheap.
`B_2_POINT_INTERPOLATION` | Linear interpolation between adjacent samples. Much better quality, and more expensive, than drop-sample.
`B_LINEAR_INTERPOLATION` | "Wide" linear interpolation. The best quality, but the most expensive.

`Interpolation()` returns the current interpolation mode setting. The default is `B_LINEAR_INTERPOLATION`.

RETURN CODES

SetSamplingRate() and SetInterpolation() return...

- `B_OK`. The function was successful.

- `B_BAD_VALUE`. Invalid argument.

### SetSynthVolume() , SetSampleVolume() , SynthVolume() , SampleVolume()

void `SetSynthVolume(`double scale`) `
void `SetSampleVolume(`double scale`) `
double`SynthVolume(`void`)` const
double`SampleVolume(`void`)` const

These functions get and set the master volume scalars for MIDI synthesis and BSamples playback. The scalar is linear: A scale of 1.0 (the default) has no affect; a scale of 2.0 multiplies the output by 2.0, and so on. The scale value must be at least 0.0 (no gain).

### SetVoiceLimits() , MaxSynthVoices() , MaxSampleVoices() , LimiterThreshhold()

status_t `SetVoiceLimits(`int16 maxSynthVoices, int16 maxSampleVoices, int16 limiter Threshhold`) `
int16`MaxSynthVoices(`void`)` const
int16`MaxSampleVoices(`void`)` const
int16`LimiterThreshhold(`void`)` const

The synthesizer can generate as many as 32 "voices" simultaneously, where a voice is a MIDI note or a stream of BSamples. The first two arguments tell the synthesizer to set aside some number of voice slots for MIDI synthesis and for samples, respectively; combined, the two arguments mustn't exceed 32. If you ask for too many voices during a performance, the synthesizer will (try to) kill old voices first. By default, the voices are allocated 28 for MIDI synthesis and 4 for samples.

You use the limiterThreshhold to estimate the typical voice density (number of simultaneous voices) for a performance. It must be at least 1; the default is 7. The synthesizer uses the value as an amplitude scalar:

- When the voice density during a performance is less than (or equal to) the threshhold (n), the dynamic range is "divided" into n parts, where each voice gets one part.

- When the voice density (m) exceeds the threshhold, the dynamic range is divided into m parts.

If you set the value too high (if there are typically fewer simultaneous voices than you estimated) the signal-to-noise ratio will suffer—you'll be dividing the dynamic range into too many (small) parts. If you set it too low and the voice density changes a lot, the balance between voices may become hard to predict and control. A change to the limiterTreshhold doesn't affect notes/samples that are currently being produced.

The other three functions return the values that you passed to `SetVoiceLimits()`. Note that these functions don't actually consult the synthsizer—if you pass illegal values to `SetVoiceLimits()`, the querying functions will return those values without complaint.

RETURN CODES

`SetVoiceLimits()` returns...

- `B_OK`. The limits were successfully set.

- `B_BAD_VALUE`. Bad argument value; the previous settings are left unchanged.

### synth_controller_hook see SetControllerHook()

### SynthMode() see LoadSynthData()

### SynthVolume() see SetSynthVolume()

### Unload() see LoadSynthData()

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..