Previous Next

Programming /dev/sequencer

The dev/sequencer device allows you to program the FM or wavetable synthesizer built into the sound card or external devices on a MIDI bus. It is intended for computer music applications.

The word sequencer refers to an application that controls MIDI devices, sending messages to them in an appropriate sequence to play music.

The interface to this device is quite complex. It uses commands loosely based on the events used by the MIDI protocol. A typical command might start playing a note using a particular instrument voice.

Before using the on-board synthesizer, you have to download patches to define one or more instruments. These can be individual patches loaded on a per-instrument basis or, more commonly, a full set such as the 128 standard MIDI voices. These patches are usually contained in files.

I'm not going to cover this device in any more detail. It is of less interest to most multimedia application developers because it is limited to music, and applications already exist that can play most computer music files (these were covered in Part III).

There is a higher-level interface to the synthesizer and MIDI bus, /dev/sequencer2, but it is not yet available in the current sound driver. A higher-level interface for managing patches for the /dev/sequencer devices is planned, but not yet complete. Called /dev/patmgr, it is quite specialized and probably not of interest to most multimedia application developers, so I won't explore it further.

The device /dev/midi will provide low-level access to the MIDI bus. It is not currently fully implemented, and the API is in the process of changing. The MIDI interface is somewhat specialized and normally used only by musicians.

If you are interested in computer music, I urge you to look at the source code for some of the existing software applications such as mp02, adagio, and glib, in order to gain an understanding of the programming interface.

Example Program

For what it's worth, in Example 14-5 I offer a small example program that illustrates how to determine the sequencer devices supported by a sound card.Example of Accessing /dev/sequencer

/*
 * seq_info.c
 * Example program for /dev/sequencer
 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include <sys/soundcard.h>

/* return string given a MIDI device type */
char *midi_device_type(int type)
{
  switch (type) {
  case SNDCARD_ADLIB:      return("Adlib");
  case SNDCARD_SB:         return("SoundBlaster");
  case SNDCARD_PAS:        return("ProAudioSpectrum");
  case SNDCARD_GUS:        return("GravisUltraSound");
  case SNDCARD_MPU401:     return("MPU401");
  case SNDCARD_SB16:       return("SoundBlaster16");
  case SNDCARD_SB16MIDI:   return("SoundBlaster16 MIDI");
  case SNDCARD_UART6850:   return("6850 UART");
  case SNDCARD_GUS16:      return("Gravis UltraSound 16");
  case SNDCARD_MSS:        return("Microsoft Sound System");
  case SNDCARD_PSS:        return("Personal Sound System");
  case SNDCARD_SSCAPE:     return("Ensoniq SoundScape");
/* these require a more recent version of the sound driver */
#if SOUND_VERSION >= 301
  case SNDCARD_PSS_MPU:    return("Personal Sound System + MPU");
  case SNDCARD_PSS_MSS:    return("Personal Sound System/Microsoft Sound 
                                   System");
  case SNDCARD_TRXPRO_MPU: return("MediaTrix Pro + MPU");
  case SNDCARD_MAD16:      return("MAD16");
  case SNDCARD_MAD16_MPU:  return("MAD16 + MPU");
  case SNDCARD_CS4232:     return("CS4232");
  case SNDCARD_CS4232_MPU: return("CS4232 + MPU");
  case SNDCARD_MAUI:       return("Maui");
  case SNDCARD_PSEUDO_MSS: return("Pseudo-MSS");
#endif /* SOUND_VERSION >= 301 */
#if SOUND_VERSION >= 350
  case SNDCARD_GUSPNP:     return ("Gravis UltraSound PlugNPlay");
#endif /* SOUND_VERSION >= 301 */
  default:                 return("unknown");
  }
}
/* return string given a synthesizer device type */
char *synth_device_type(int type)
{
  switch (type) {
  case SYNTH_TYPE_FM:     return("FM");
  case SYNTH_TYPE_SAMPLE: return("Sampling");
  case SYNTH_TYPE_MIDI:   return("MIDI");
  default:                return("unknown");
  }
}

/* return string given a synthesizer device subtype */
char *synth_device_subtype(int type)
{
  switch (type) {
  case FM_TYPE_ADLIB:   return("Adlib");
  case FM_TYPE_OPL3:    return("OPL-3");
  case SAMPLE_TYPE_GUS: return("GUS");
  default:              return("unknown");
  }
}

int main(int argc, char *argv[])
{
  int i, status, numdevs;
  struct synth_info sinfo;
  struct midi_info minfo;
  int fd;        /* file descriptor for /dev/sequencer */
  char *device; /* name of device to report on */

  /* get device name from command line or use default */  
  if (argc == 2)
    device = argv[1];
  else
    device = "/dev/sequencer";

  /* try to open device */
  fd = open(device, O_WRONLY);
  if (fd == -1) {
    fprintf(stderr, "%s: unable to open `%s', ", argv[0], device);
    perror("");
    return 1;
  }

  status = ioctl(fd, SNDCTL_SEQ_NRSYNTHS, &numdevs);
  if (status == -1) {
    perror("ioctl failed");
    exit(1);
  }
  printf("%s:\n%d synthesizer device%s installed\n", device, numdevs,
	 numdevs == 1 ? "" : "s");

  for (i = 0 ; i < numdevs ; i++) {
    sinfo.device = i;
    status = ioctl(fd, SNDCTL_SYNTH_INFO, &sinfo);
    if (status == -1) {
      perror("ioctl failed");
      exit(1);
    }
    printf("Device %d: `%s' type: `%s' subtype: `%s' voices: %d\n",
	   i,
	   sinfo.name,
	   synth_device_type(sinfo.synth_type),
	   synth_device_subtype(sinfo.synth_subtype),
	   sinfo.nr_voices);
  }
  status = ioctl(fd, SNDCTL_SEQ_NRMIDIS, &numdevs);
  if (status == -1) {
    perror("ioctl failed");
    exit(1);
  }
  printf("%d MIDI device%s installed\n", numdevs,
	 numdevs == 1 ? "" : "s");
  for (i = 0 ; i < numdevs ; i++) {
    minfo.device = i;
    status = ioctl(fd, SNDCTL_MIDI_INFO, &minfo);
    if (status == -1) {
      perror("ioctl failed");
      exit(1);
    }
    printf("Device %d: `%s' type: `%s'\n",
	   i,
	   minfo.name,
	   midi_device_type(minfo.dev_type));
  }
  /* close file and exit */
  close(fd);
  return 0;
}

On my system, the program reported one FM synthesizer present and one MIDI interface, and produced the following output:

/dev/sequencer:
1 synthesizer device installed
Device 0: `Yamaha OPL-3' type: `FM' subtype: `OPL-3' voices: 18
1 MIDI device installed
Device 0: `SoundBlaster' type: `SoundBlaster'


Next  Advanced Sound Programming
Previous  Mixer Channels


O'Reilly Home