GSI(3)

NAME
gsi (gsi_*) - sound functions (part of GSI - general sound interface)

SYNOPSIS
#include <gsi/gsi.h> Link the program with -lgsi or add a line #include <gsi/gsi_interface.c> to a new or existing file for your program which will compile the code directly into your program (the compiler can optimize more in that way).

INTRODUCTION
If you're unfamiliar with GSI, have a look at gsi_overview(7) first.

SYNTAX

int  gsi_init (char *remote_host);
    This initializes the GSI sound system (connects to gsi_server or the NAS server when using the NAS interface, initializes various things, such as the mixer settings when using gsi_server) and is to be called before using any other GSI function. If remote_host is "" or NULL the host is assumed to be 'localhost' (or whatever your real hostname is, as reported by hostname(3)/gethostname(2)). The environment variable GSI_HOST , if set, overrides the given hostname. Returns 0 if all went ok, -1 if unable to connect to the server.

void gsi_close (gsi_server_t *c);
    Closes the GSI sound system.

void gsi_chdir (gsi_server_t *c, char *dir);
    The server will load all sound lists, samples etc with a relative path, from the given directory. Note that this function can be called more than once.

void gsi_load_samples_from_list (gsi_server_t *c,  char *sample_list);
    sample_list may be NULL or a zero length string. In that case no sounds will be loaded (nor will already loaded ones be unloaded).

int  gsi_init_pcm (gsi_server_t *c, int pcm_samplerate, int pcm_channels, int pcm_type, int pcm_log2_fragsize);
    Initializes the pcm system: This function can be called more than once to change all the given settings at once. Returns 0 if all ok, -1 if failed (eg. unable to connect to the server).

int  gsi_init_synth (gsi_server_t *c, int type);
    OBSOLETE; Sets the the synthesizer type you want and grabs the synth device; type is 0 for a real synth, 254 for external midi, 255 for wavetable emulation in software. [ N.B. use 0 for now as this value is currently ignored and playing is always on the synth hardware].

void gsi_flush (gsi_server_t *c);
    Send as many cached commands to the sound server as possibly without blocking.

void gsi_flush_all (gsi_server_t *c);
    Same as gsi_flush but send ALL cached commands to the sound server. Use of this fucntion is generally not recommended as it may lead to deadlock (if the server sends back so much event data in response that it has to wait for some space).

int  gsi_sync (gsi_server_t *c);
    Sends a GSI_CMD_SYNC, flushes ALL commands, and waits for GSI_EV_SYNC. This call can be used to make sure all data was sent before disconnecting, e.g. for cd playing, getting information from an event without having to use a callback function, and for debugging. Returns 0 if all went well, -1 if timed out (no event within 10 s) while waiting for GSI_EV_SYNC.

int  gsi_commands_pending (gsi_server_t *c);
    Returns number of known to be internally buffered commands (i.e. have not yet been sent to the server), this may be 1 even if 10 commands are in the buffer, but it will only return 0 if no commands are buffered internally.

int  gsi_events_pending (gsi_server_t *c);
    Returns number of known to be available events, i.e. this may be 1 even if 10 events have been received from the server (but possibly not yet decoded), but it will only return 0 if no events have been received.

int  gsi_next_event (gsi_server_t *c);
    Returns -1 if no event available, 0 if one was handled (a callback as set above will have been called in that case).

void gsi_play_sound (gsi_server_t *c, int ch, int slot, int command);
    commands are:
    GSI_CMD_PLAY
        = play if ch is not used
    GSI_CMD_REPEAT
        = ditto, repeat the sound when finished
    GSI_CMD_OVERWRITE
        = overwrite any sound (slot) that might be playing on ch
    GSI_CMD_OVERWRITE_REPEAT
        = ditto, repeat the sound when finished

-- Special compound playing commands, use ch=-1 for automatic channel allocation. Note that speeds are the distance travelled in 1/32 s (automatically recalculated for the given fragment size of the pcm device, as positions are adjusted each fragment). Commands are the same as in gsi_play_sound.

void gsi_play_sound_3d (gsi_server_t *c, int ch, int slot, int command, int x, int y, int z, int dx, int dy, int dz, int doppler_on);

void gsi_play_sound_stereo (gsi_server_t *c, int ch, int slot, int command, int x, int dx, int doppler_on);

void gsi_play_sound_pan (gsi_server_t *c, int ch, int slot, int command, int pan);
    pan = -256 = full left, 256 = full right

void gsi_kill_sound (gsi_server_t *c, int ch)
    kill the sound that's playing on channel ch

void gsi_kill_sounds (gsi_server_t *c);
    stops all sounds playing channels.

void gsi_set_sound_volume (gsi_server_t *c, int ch, int volume);
    sets the volume of a sound started on ch to volume

int  gsi_get_channel_status (gsi_server_t *c, int ch);
    Get the status of channel ch , is it playing a sound? (only works if callbacks are enabled with gsi_enable_events(GSI_EVMASK_CHANNEL)). Returns 0 if free, 1 if locked, 2 if playing.

int  gsi_load_sample (gsi_server_t *c, int slot, char *filename);
    See below for currently supported file formats. Failure to load the file will result in an event GSI_EV_NO_SUCH_FILE.

int  gsi_load_sample_from (gsi_server_t *c, int slot, char *filename, int offset);
    Same as the previous function, but the sample's header is at the given offset in the file.

void gsi_send_raw_sample (gsi_server_t *c, int slot, int samplerate, int channels, int type, int size, unsigned char *data);
    Sample types must one of GSI_8BIT_MULAW, GSI_8BIT, GSI_8BIT_SIGNED, GSI_16BIT, GSI_16BIT_LE, and left channel first (for stereo sounds).

Song loading : (note that all these commands stop any playing song first) :

int  gsi_load_song (gsi_server_t *c, char *filename);
    Failure to load the file will result in an event GSI_EV_NO_SUCH_SONG_FILE.

int  gsi_load_song_from (gsi_server_t *c, char *filename, int offset);
    Same as the previous function but the song's header is at the given offset in the file.

int  gsi_send_song (gsi_server_t *c, int data_len, unsigned char *data);
    [ NOT YET IMPLEMENTED ]

int  gsi_send_song_track (gsi_server_t *c, int trackno, int data_len, unsigned char *data);
    [ NOT YET IMPLEMENTED ]
    For music making programs in particular

Song playing :

void gsi_play_song (gsi_server_t *c, int loopno);
    Starts playing the currently loaded song loopno times. If loopno =0 the song will continue to play until it is stopped (gsi_stop_song) or a new song is loaded.

void gsi_stop_song (gsi_server_t *c);
    Stops playing the currently loaded song

void gsi_continue_song (gsi_server_t *c);
    Continues playing the currently loaded song (if given before a gsi_play_song, this is ignored)

Custom instrument loading :

int gsi_load_instrument (gsi_server_t *c, int pgm, char *filename, int offset);
    =U= offset = where in the file to get the instrument.

int gsi_send_instrument_data (gsi_server_t *c, int pgm, int instrtype, int instrlen, unsigned char *instrdata);
    instrtype = GSI_INSTRUMENT_OPL2, GSI_INSTRUMENT_OPL3, GSI_INSTRUMENT_GUS, ..

Grabbing & releasing devices :

void gsi_grab_mixer (gsi_server_t *c);

void gsi_grab_pcm (gsi_server_t *c);
    Requests the pcm device to be grabbed and sets the samplerate, channels etc. as set by earlier commands (if actually assigned to the client). Calling this when the pcm device is already grabbed by the client will set the pcm to the new settings.

void gsi_grab_synth (gsi_server_t *c);

void gsi_grab_cd (gsi_server_t *c, int cd);

void gsi_share_cd (gsi_server_t *c, int cd);
    Opens the cd device if not yet done and allows other GSI programs to also give CD commands (when they have opened the CD device with gsi_share_cd).

void gsi_release_mixer (gsi_server_t *c);

void gsi_release_pcm (gsi_server_t *c);
    Releases the pcm device (to share it with other programs, which can then set the pcm characteristics).

void gsi_release_synth (gsi_server_t *c);

void gsi_release_cd (gsi_server_t *c, int cd);

Mixer & pseudo mixer setting : The following mixer commands only have effect if the client has (succesfully) grabbed the mixer.

void gsi_set_balance (gsi_server_t *c, int balance);
    [ NOT YET IMPLEMENTED ]
    balance =-256 (full left) .. 256 full right. The default is 0 (center). This affects the master volume (perhaps also direct cd output (for headphones on cd front panel output etc).

void gsi_mute (gsi_server_t *c);
    Disable all sound output.

void gsi_unmute (gsi_server_t *c);
    Enable again.

The following two functions set/get the mixer device multipliers (maximum hardware volumes). In case of a non-shared device, usually the hardware volume = mixer_volume * application_volume/256. For shared devices, hardware volume = mixer_volume and application volumes only touch the internal server calculations.

void gsi_set_mixer_volume (gsi_server_t *c, int mixer_ch, int vol);
    Sets the volume multiplier (i.e. maximum volume) for the given device. Note: If there's no master volume, it is emulated with an internal volume and all others (SYNTH, PCM, CD, ...) will be adjusted accordingly.

int  gsi_get_mixer_volume (gsi_server_t *c, int mixer_ch);

void gsi_set_volume (gsi_server_t *c, int mixer_ch, int vol);
    Sets the application's volume for the device.

int  gsi_get_volume (gsi_server_t *c, int mixer_ch);

CD playing :

void gsi_cd_play (gsi_server_t *c, int cd, int track_1, int minute_1, int second_1, int frame_1, int track_2, int minute_2, int second_2, int frame_2);
    Play from track_1 at the given offset to track_2 until the given offset. Offsets and tracks are clipped into range, so to play until the end of a track, just give as second offset 255,255,255 and to play the entire cd just enter gsi_cd_play(c, 0,0,0,0,255,255,255,255);

void gsi_cd_pause (gsi_server_t *c, int cd); void gsi_cd_resume (gsi_server_t *c, int cd); void gsi_cd_stop (gsi_server_t *c, int cd);

Note: If you want to go to the next or previous track, or restart a track, you should use the track events and then send a play command from the track you want to start at, as it would be bad to use next_track type commands over a network (a new track may have started just as you send it, so you will move on 2 tracks...). void gsi_eject_cd (gsi_server_t *c, int cd); void gsi_insert_cd (gsi_server_t *c, int cd); void gsi_change_cd (gsi_server_t *c, int cd, int cd_slot); Selecting events sent to the program (see gsi_commands(3) for a list of event masks):
void gsi_enable_events (gsi_server_t *c, int bits); void gsi_disable_events (gsi_server_t *c, int bits);

Calback functions (WILL BE REPLACED BY EVENTS) :
Use ch=-1 for songs, -2 for cd playing.
void *gsi_set_channel_callback (gsi_server_t *c, int ch, void (*cbf)(int ch)); r void gsi_clear_channel_callback (gsi_server_t *c, int ch);
Equivalent to gsi_set_channel_callback(c, NULL); void *gsi_set_error_callback (gsi_server_t *c, void (*cbf)(int error)); void gsi_clear_error_callback (gsi_server_t *c); void *gsi_set_info_callback (gsi_server_t *c, void (*cbf)(int info)); void gsi_clear_info_callback (gsi_server_t *c);

Events (will replace the above callbacks) :

gsi_event *gsi_get_event(gsi_server_t *c, int ev_mask);
    [ NOT YET IMPLEMENTED ]

OR

int  gsi_get_event (gsi_server_t *c, gsi_event *event, int event_mask);
    [ NOT YET IMPLEMENTED ]
    Returns 0 if succesful, -1 if unable to find an event fitting event_mask

int  gsi_event_fd (gsi_server_t *c);
    Returns the file descriptor used for callback information so you can use it in a select together with other descriptors

int  gsi_wait_for_event (gsi_server_t *c, int sec, int musec);
    waits using select, if sec <0, wait forever until an event arrives. Returns 0 if an event arrived within the given time, -1 if not. NB. May change to use a timeval struct.

Dynamic slot & channel allocation :

int  gsi_find_free_slot (gsi_server_t *c);
    Returns -1 when all slots are used, 0-255 otherwise.

int  gsi_find_free_channel (gsi_server_t *c);
    Returns -1 when all channels are playing sounds, 0-255 otherwise.

NB. These are higher level functions that need locks which must be set whenever you allocate a slot or channel and want to use the above two functions; callbacks will set the flags definitely. The standard commands defined in gsi.h all do this automatically, so this is only needed if you use the GSI_CMD_* commands directly with gsi_send_commands(..) (e.g. to write your own compound commands)).
int gsi_lock_channel (gsi_server_t *c, int ch);
int gsi_lock_slot (gsi_server_t *c, int slot);
-- The following two functions can be called whenever you want to reuse the given positions (not actually necessary for channels).
int gsi_unlock_channel (gsi_server_t *c, int ch);
int gsi_unlock_slot (gsi_server_t *c, int slot);

LOW LEVEL STUFF
-- To make up more sophisticated effects (eg stereo position adjustment, observer rotation; see gsi_commands (3) ) send commands yourself to the server (in bytes, 16 and 32 bit quantities must be sent MSB first):
void gsi_send_commands (gsi_server_t *c, int argc, ...); -- You can also use:
void gsi_send_byte (gsi_server_t *c, int data);
void gsi_send_int16 (gsi_server_t *c, int data);
void gsi_send_int32 (gsi_server_t *c, int data);
void gsi_send_string (gsi_server_t *c, char *string);
void gsi_send_block (gsi_server_t *c, unsigned char *data, int len);

DATA
The following data from GSI are accessible in callbacks: int gsi_event_data[16]; struct gsi_cd_track_info {
    unsigned char min;
    unsigned char sec;
    unsigned char frame;
    unsigned char type; /* 0 = DATA, 1 = AUDIO */
}; int gsi_cd_track_no;
struct gsi_cd_track_info *gsi_cd_track_list;

ARGUMENTS
dir This is the directory where all sound files (also sound lists!) with a relative path (i.e. not starting with '/') are loaded. Note that sounddir itself must be an absolute path as this gets passed to a server which does not know where the program that's calling it gets started (use getcwd if necessary).
samplerate & pcm_samplerate
    Number of samples per second. A samplerate of 8000 means 8000 bytes are needed for 8 bit mono for one second of sound. Standard pcm samplerates are 8000, 11025, 22050, 44100.
pcm_channels
    Either 1 for mono, or 2 for stereo (future: 3,4,5,6 for multichannel & surround); GSI_MONO, GSI_STEREO (future: GSI_3CHANNEL, GSI_QUAD, GSI_SURROUND4, GSI_SURROUND6) can also be given which are defined as 1,2 (3,4,5,6).
pcm_type
    Either GSI_8BIT_MULAW, GSI_8BIT or GSI_16BIT (future: GSI_24BIT). If the given format is not supported, the server will try the next format, until a format that works is found.
slot
    Sound slot in the server numbered from 0 to 255. You can load these slots with samples by using a sound list, loading a specific file, or loading it from memory in the client.
channel
    A number from 0 to 255. Sounds are played on channels and mixed together. This means you can have up to 256 simultaneous sounds playing on channels (per client).
pcm_log2_fragsize
    log2 of the fragment size used by the pcm device. This sets the granularity. Lowering this will give faster response. For 8 bit mono 8000Hz I recommend 8 (fragment = 256), which gives 1/32 to 2/32 second delays (as there are minimally 2 such fragments used by the sound driver [ currently the server uses 3 fragments of half the requested size which gives the same results, but with a better delay_dt = 1/32 to 1.5/32 s ]) which is just acceptable for fast action games. Very low values will give stuttering sound as unix isn't real time (I need RT!). For stereo increment log2_frag by 1 and for 16 bits the same again. Obviously, for higher pcm samplerates, a corresponding bigger fragsize is recommended (e.g. 22Khz, 8 bit mono: 512 or 1024). As a 10ms system resolution as used in most unix variants will give a minimal repeated usleep time of 20 ms, you should make sure that fragsize/(samplerate*bytes*channels) >= .02s. pcm_log2_fragsize must be in the range 4-13. If you give a log2_fragsize of 0,1,2 or 3, the server will find a good value itself, based on the samplerate, channels and bytes per sample, where 0 gives the minimum (for fast response), 1 gives the minimum +1, etc.
sample_list
    A file with a list of files to be loaded in the given slots in the following format:
       <n> <name> [<offset> [<len> <samplerate> <ch> <type>]] [autofree] volume=<vol> E.g.
        0 swarmer.au
        2 /usr/local/shared/games/pool/cueball.au
        3 doom.wad 10003 1000 11025 2 GSI_8BIT autofree volume=128
        4 descent.pig 24000 autofree volume=128
        5 man_down.au 0 will load swarmer.au in slot 0, cueball.au in slot 2, a raw file from doom.wad at offset 10003 and 1000 bytes longs in slot 3, a file with header from descent.pig at offset 24000. This removes hardcoded filenames in the source and allows easily changing sounds used by replacing the sample list.
vol Volume of the hardware or pseudo mixer for various devices (pcm, synth, cd, master).
data
    For samples, this is a PCM encoded file. For songs, this is a midi file when loading a song, or a midi track when loading a track.
command
    An actual server command. See gsi_commands (3) for more information.
The GSI server, when necessary, converts all sound samples on the fly to the current pcm settings. This also means pcm settings can be changed at any time.

NOTES ABOUT SOUND VOLUMES
Consider these cases:
    1 mono sound on mono pcm device.
    2 mono sound on stereo pcm (played as mono) -> on left + right channel.
    3 mono positioned on L/R with stereo pcm -> if on L, then only one speaker instead of two as with 1 & 2. Hence pan/3d calls actually make the volume of a sound half as loud... Further, panning should keep the volume the same wherever the sound is placed. What's the best L/R volume for that? (see doom too).
    4 mono positioned on L/R with mono pcm -> volume is halved to make loudness the same as in case 3.
The details above that I considered while writing gsi are essential to make it transparent whether or not the pcm device is currently mono or stereo. This also means that one should be aware of the fact that using PAN/3d stuff will halve volumes compared to direct playing!

SUPPORTED FILE FORMATS
The formats supported by the library for samples (see gsif(3) ) are .snd/.au 8 bit mu-law, 8 bit uncompressed and 16 bit MSB (mono and stereo), standard .aif[fc], .voc, .wav. Raw sounds ("*.raw") are assumed to be 8 kHz mono 8 bit.

EXAMPLE APPLICATION
This program plays a sample (any format supported by the gsif library) and then waits for that sound to finish. /*__________________INCLUDES___________________*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h> #include <gsi/gsi.h>
#include <gsi/whs_types.h> /* only for True/False defs */ /*__________________VARIABLES___________________*/
int pcm_channels = 1;
int pcm_type = GSI_8BIT;
int pcm_samplerate = 8000;
int pcm_fragsize = 8; int called_back = False; /*__________________PROGRAM___________________*/
void my_callback(int ch) {
    if (ch == 0 && gsi_event_data[0] == GSI_EV_CHANNEL_END)
        called_back = True;
} void error_callback(int error) {
    called_back = True;
} int main(int argc, char *argv[]) {
    char pwd_buf[256];
    gsi_server_t *c;
    if (argc == 1) {
        fprintf(stderr, "This program needs one argument\n");
        exit(1);
    }
    getcwd(pwd_buf, 256); /* longer and you're screwed */
    c = gsi_init(NULL);
    if (c == NULL) {
        fprintf(stderr, "Unable to connect to server\n");
        exit(1);
    }
    gsi_chdir(c, pwd_buf);
    /* This fragsize calculation only works with
     * GSI_8BIT, GSI_8BIT_MULAW, GSI_16BIT... */
    pcm_fragsize = 8+pcm_channels-1+(pcm_type+1)/2-1;
    gsi_init_pcm(c, pcm_samplerate, pcm_channels, pcm_type, pcm_fragsize);
    gsi_enable_events(c, GSI_EVMASK_CHANNEL);
    gsi_send_commands(c, 1, GSI_CMD_USE_BEST_PCM_SETTINGS);
    /* This selects the settings of the first sound to
     * be played after a silence. */
    gsi_load_sample(c, 0, argv[1]);
    gsi_set_channel_callback(c, 0, my_callback);
    gsi_set_error_callback(c, error_callback);
    gsi_play_sound(c, 0, 0, GSI_CMD_PLAY);
    /* Now wait for the playing-done callback */
    for (;;) {
        fd_set sel;
        int selno;
        gsi_flush(c); /* sends commands */
        while (gsi_next_event(c) == 0) ; /* handles all callbacks */
        if (called_back) break;
        FD_ZERO(&sel);
        FD_SET(gsi_event_fd (), &sel);
        selno = gsi_event_fd ()+1;
        select (selno, &sel, NULL, NULL, NULL);
    }
    gsi_close(c);
    return 0;
} Note: a bigger program would select on another input source and have to check if events & commands are stored internally (not yet handled with gsi_next_event()/not yet sent by gsi_flush()). Have a look at e.g. the streamtest program.

CLIENTS AND PCM SHARING
Clients can send:
    GSI_CMD_GRAB_PCM GSI_CMD_AUTO_PCM GSI_CMD_RELEASE_PCM
Interface functions: gsi_grab_pcm(), gsi_release_pcm() (GSI_CMD_AUTO_PCM has no interface function so use gsi_send_commands to use it). Clients should send a GSI_CMD_RELEASE_PCM command (or call gsi_release_pcm()) whenever they don't need to play sounds (e.g. upon a LeaveNotify event in X11, or a ?? event in GGI (currently not defined in GGI)) so that the pcm settings can be set to what another client prefers, or so that non-GSI aware programs can use the pcm device (when all GSI clients have released the pcm device, it is closed and thus available to such programs). Clients that only need to play sounds occasionaly, and don't want to wait for them to finish so they can send a GSI_CMD_RELEASE_PCM, could send the GSI_CMD_AUTO_PCM command which removes the client's pcm request automatically when no sounds are being played for that client, and tries to grab it again when needed.
NOTE: For sample playing the client must stay connected or else the samples will be stopped when the client disconnects.

CLIENTS AND SYNTH ACCESS
Is synth sharing useful? I think not.
Clients can send:
    GSI_CMD_GRAB_SYNTH: gives the program exclusive use of the synthesizer. GSI_CMD_RELEASE_SYNTH: gives up the synth (if the program had exclusive possesion), frees the device so other GSI programs or non-GSI programs can it. This should be used in the same way as GSI_CMD_RELEASE_PCM, i.e. when an X program looses focus it probably should give up the synth, then grab it and continue playing again when the pointer is in the window.
Interface functions: gsi_grab_synth(), gsi_release_synth(). NOTE: For song playing the client must stay connected or else the song will be stopped when the client disconnects.

CLIENTS AND CD SHARING
The CD can be shared. The use of this is for ??
=> let cd commands finish if the client quits (to start a CD but leave the device available to non-GSI programs).
Clients can send:
    GSI_CMD_GRAB_CD: gives the program exclusive use of the cd player (if not busy, i.e. succeeds if the device is not busy and no other clients use GSI_CMD_SHARE_CD). GSI_CMD_SHARE_CD: share the CD with other GSI clients, i.e. others too can give cd commands. And look at the track listing etc (not possible with GRAB_CD? Or also allow that? Or consider that a security breach?). GSI_CMD_RELEASE_CD: frees the cd device so non-GSI programs can use the cd (actually, this will happen only when all programs have given up the cd in this way).
Interface counterparts: gsi_grab_cd(), gsi_share_cd(), gsi_release_cd().

CLIENTS AND MIXER ACCESS
There can only be one mixer owner.
The relevant server commands are
    GSI_CMD_GRAB_MIXER GSI_CMD_RELEASE_MIXER
Interface counterparts: gsi_grab_mixer, gsi_release_mixer. Mixer sharing is done by virtualizing the mixer and adjsuting volumes where necessary, so all applications can set mixer settings in a way that makes sense... The actual mixer settings are multipliers, not real hardware volumes (except for the MASTER volume, here multiplier = hardware volume) as we need to maintain a PCM/SYNT/CD volume ratio for a client that wants to use PCM + one of the other devices and this should not be influenced by any other client. Therefore, applications set pseudo device volumes with gsi_set_volume() (as opposed to gsi_set_mixer(). This uses the server command GSI_CMD_SET_APPLICATION_VOLUME which e.g. adjusts running sounds when adjusting the pcm volume, so the fact that pseudo devices are used is transparent. The values most useful for the multipliers (which you should put in /etc/gsi.conf or ~/.gsi.conf) depend on the sound card and driver. E.g. OSS + SB16: master=pcm=synth=cd=256; ULTRASOUND + GUS PNP: there's a problem sometimes with the balance (at least on my PNP; I've heard of other problems like noise which won't go away util the computer is rebooted when output is too high in the interwave mode) which goes left/right when the output volume (of synth playing) gets too high, and there's a little noise when master = 256; therefore master=200, pcm=200, synth=200, cd=256 are good values. The master volume can of course be altered at any time to change overall output volume, but the other multipliers should not be changed when a certain device is too loud or not loud enough in just one application. In that case, the application should use gsi_set_volume().

SECURITY [ PRELIMINARY ]
This applies to the socket version: Letting anyone connect to the server has severe security implications, especially if root runs it and we let the server save files (e.g. after recording). Therefore a password scheme will be used where the password is kept in the user's home directory, in the file gsi-passwd. The environment variable GSI_PASSWD_FILE overrides this if set. The server switches uid/gid before loading/saving (for this to work for everyone, the server must be started by root, or be suid root).

RUNTIME MESSAGES
Errors, warnings etc. are logged through syslog. The server uses facilty daemon while the interface doesn't call openlog(3) and thus will use defaults or anything the program using it has set. Explicit debug information that is enabled with environment variables is sent to stderr.

BUGS & UNIMPLEMENTED FEATURES
For GSI_CMD_ADJUST_X etc , store that time at which the value was last adjusted (to calc the speed with dx), in fragments of 1/32 s (24 bit + 8 bit fraction = upto 72 hours) OSS: GUS & OPL midi playing could sound better (compared to playmidi..). Add SYSV shared memory for sample editors, music players that manipulate a lot so you don't have to send the data through the pipe/socket each time. Observer settings are ignored. NAS interface file must be updated. Add SYNTH_PCM_EMULATION (i.e. emulation of a wave table card) which will give midi playing for NAS and better midi playing for OPL2/3. Add echo and possibly other effects Use DMA when available ? Replacing multiply/shift with a run length algorithm for the sound index and combine with a simpler/faster oversample algorithm

FILES
/etc/gsi.conf    the configuration file (can also be place in ~/.gsi.conf).
When security is implemented, the following files will be used :
/etc/gsi-passwd    encrypted passwords (user access)
or /etc/gsi ?
~/.gsi-passwd    Unencrypted password of the user
The devices to be used must be specified in the server's configuration file (see gsi.conf(5)) and are usually:
/dev/mixer    mixer (OSS linux)
/dev/mixer1    mixer (FreeBSD pcm driver)
/dev/dsp    pcm output (OSS, FreeBSD pcm driver)
/dev/audio    ditto (dev_audio)
/dev/sequencer    MIDI playing through sound card hardware (OSS)
/dev/cdrom    cd playing.
Various other devices are used for the ultrasound drivers, but these need not be specified as libgus handles these.

SEE ALSO
gsi_server(8), gsi_commands(3), gsi_events(3), gsif(3), gsiplay(1)

AUTHOR
Wouter Scholten email: whs@xs4all.nl
WWW page: http://www.xs4all.nl/~whs/