DEVELOPERS' WORKSHOP: Updating An Old Sound Card Driver
By Marc Ferguson

The new Media Kit contains a compatibility interface which lets you use a pre-R4 sound card driver in R4 with only minor modifications to the driver. Here is a description of those modifications, assuming that you are starting with a driver written to the old API described in the Newsletter article:

http://www.be.com/aboutbe/benewsletter/volume_II/Issue22.html

The most important change is that the driver must publish a "sample clock" which allows the Media Kit to synchronize to the sound card. The sample clock corresponds to performance time measured by the DAC (or ADC) clock in microseconds. If the nominal sample rate is 44100 samples per second then the sample clock moves at (1000000 / 44100) microseconds per sample processed by the DAC (or ADC). If the DAC is actually processing 44109 samples per second than the DAC's sample clock will run slightly faster than real-time.

The sample clock is returned by the driver in the audio_buffer_header structure which now looks like this:

typedef struct audio_buffer_header {
  int32 buffer_number;
  int32 subscriber_count;
  bigtime_t time;
  int32 reserved_1
  int32 reserved_2;
  bigtime_t sample_clock;
} audio_buffer_header;

As before, the SOUND_WRITE_BUFFER and SOUND_READ_BUFFER ioctls should store an estimate of the system_time() corresponding to the beginning of the buffer in the "time" slot of the audio_buffer_header. They should also fill the "sample_clock" slot with the sample clock for the beginning of the buffer.

To calculate the sample clock, keep track of the number of samples processed and multiply by the sample clock rate:

header->sample_clock =
  (samples_processed * 1000000LL) / sample_rate;

As long as buffers are flowing continuously, the sample clock will move at the same rate as the performance time of the buffers. But if there is an interruption between buffers then the sample clock must account for the length of the interruption. One way to do this is to measure the duration of the interruption and increment "samples_processed" by the number of samples that would have been played during that time:

samples_processed += (time_skipped * sample_rate) / 1000000;

There are four new ioctl codes which a driver may optionally support to negotiate an optimal buffer size with the Media Kit. They appear at the end of this list:

#include 

enum {
  SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END,
  SOUND_SET_PARAMS,
  SOUND_SET_PLAYBACK_COMPLETION_SEM,
  SOUND_SET_CAPTURE_COMPLETION_SEM,
  SOUND_RESERVED_1,   /* unused */
  SOUND_RESERVED_2,   /* unused */
  SOUND_DEBUG_ON,     /* unused */
  SOUND_DEBUG_OFF,    /* unused */
  SOUND_WRITE_BUFFER,
  SOUND_READ_BUFFER,
  SOUND_LOCK_FOR_DMA,
  SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE,
  SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE,
  SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE,
  SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE
};

The SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE and SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE ioctls take an (int32) argument containing the buffer size (without the header) that the Media Kit plans to send. If the driver is using a circular DMA buffer it may want to set the size of the DMA buffer to twice the preferred buffer size to minimize latency.

The SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE and SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE ioctls take an (int32*) argument in which the driver can return the current setting of the preferred buffer size.

Devices supporting the above API should be published under the "/dev/audio/old/" directory where they will be found by the "legacy" media add-on.

Adding a sample clock to your old R3 sound card driver will get it up and running again under R4.

Copyright ©1999 Be, Inc. Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners.