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):

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


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:

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


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


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:

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


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