[comp.sources.misc] v20i047: sndblast - Sound Blaster Unix Driver, Part01/02

brians@eecs.cs.pdx.edu (Brian Smith) (06/08/91)

Submitted-by: Brian Smith <brians@eecs.cs.pdx.edu>
Posting-number: Volume 20, Issue 47
Archive-name: sndblast/part01
Environment: ISC-Unix

This is the first release of my Sound Blaster driver for System V 3.2, 
for the 386.  The Sound Blaster is an audio and music card for the PC.
Included is the driver itself, with some user programs to play sound,
record sound, and play CMF music files.  The player plays standard mac 
files, which are just 8-bit sound samples.  There are many archive sites 
for those.

This driver and the associated user programs require a 386/486 system
running System V 3.2 Unix.  It has been tested on ISC Unix versions
2.0.2 through 2.2.1.  I believe it will work correctly on Esix, Intel,
and AT&T Unix.  SCO is a mystery to me.  I do not believe it will work 
in SysV 4.0 and above, but the modifications should be fairly minimal.

The driver supports sound recording and playback using DMA, and it
supports the programming of the FM music chips.  It does not support
the use of the "midi" port, nor does it support the use of C/MS chips.
I don't have the hardware to test either.  Due to copyright laws (and
lia^H^Hawyers), I can't just reprint all the information that came with
my developer's manual.  I think I can answer questions though, so if
you don't understand how to use features of the driver, send me email.
The programming of the FM chips is mildly twisted, but the DSP chip
should be simple.

Have fun!  Maybe this will spur development of multimedia and/or
entertainment software for Unix (SysV 386).

Brians
/---------------------------------------|------------------------------------\
| #include <std_disclaim.h>             | Inet: brians@cs.pdx.edu            |
| #include <human_errors.h>             | UUCP: tektronix!pdxgate!brians     |
|---------------------------------------|------------------------------------|
| Behold the warranty.. the bold print giveth and the fine print taketh away.|
\----------------------------------------------------------------------------/
---
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  . ./apps ./apps/play_cleanup.1 ./apps/play_cmf.c
#   ./apps/play_snd.c ./apps/record_snd.c ./sb.c ./sb.h
# Wrapped by kent@sparky on Fri Jun  7 22:16:56 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 1 (of 2)."'
if test ! -d './apps' ; then
    echo shar: Creating directory \"'./apps'\"
    mkdir './apps'
fi
if test -f './apps/play_cleanup.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./apps/play_cleanup.1'\"
else
  echo shar: Extracting \"'./apps/play_cleanup.1'\" \(526 characters\)
  sed "s/^X//" >'./apps/play_cleanup.1' <<'END_OF_FILE'
X.TH PLAY_CLEANUP 1 "3 June 1991"
X.UC 4
X.SH NAME
Xplay_cleanup \- Cleans up after a very abnormal exit from play_snd
X.SH SYNOPSIS
X.B play_cleanup
X.PP
XNo arguments are necessary.
X.SH DESCRIPTION
XIf play_snd exits without destroying the shared memory segment,
Xfurther invocations will fail until the segment is destroyed.  This
Xprogram destroys that memory segment.  This should not be needed,
Xbecause play_snd cleans up after getting eof and SIGINT.  It cannot,
Xhowever, catch SIGKILL.
X.B play_cleanup
X.SH AUTHOR
X.PP
XBrian Smith
END_OF_FILE
  if test 526 -ne `wc -c <'./apps/play_cleanup.1'`; then
    echo shar: \"'./apps/play_cleanup.1'\" unpacked with wrong size!
  fi
  # end of './apps/play_cleanup.1'
fi
if test -f './apps/play_cmf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./apps/play_cmf.c'\"
else
  echo shar: Extracting \"'./apps/play_cmf.c'\" \(8889 characters\)
  sed "s/^X//" >'./apps/play_cmf.c' <<'END_OF_FILE'
X/*
X * Copyrighted as an unpublished work.
X * (c) Copyright 1991 Brian Smith
X * All rights reserved.
X *
X * Read the LICENSE file for details on distribution and use.
X *
X */
X
X#include <sys/fcntl.h>
X#include <sys/unistd.h>
X#include <sys/sb.h>
X#include <sys/time.h>
X#include <stdio.h>
X
X#define lobyte(X)   (((unsigned char *)&X)[0])
X#define hibyte(X)   (((unsigned char *)&X)[1])
X
X/* Globals */
Xint fm_herz;        /* clock ticks per second */
Xint tempo;          /* clock ticks per quarter note */
Xint fm_fd;
Xint note_on[16];
Xchar **instrument_table;
Xint note_table[12] = {
X    343,
X    363,
X    385,
X    408,
X    432,
X    458,
X    485,
X    514,
X    544,
X    577,
X    611,
X    647
X    };
X
X
Xint main(argc, argv)
Xint argc;
Xchar **argv;
X{
X    int cmf_fd;
X
X    if (argc != 2)
X    {
X        printf("usage: %s <cmf file>\n", argv[0]);
X        exit(-1);
X    }
X
X    /* open cmf file */
X    cmf_fd = open(argv[1], O_RDONLY);
X    if (cmf_fd == -1)
X    {
X        printf("usage: %s <cmf file>\n", argv[0]);
X        exit(-1);
X    }
X
X    /* verify that file is a cmf file */
X    if (!verify_cmf(cmf_fd))
X    {
X        printf("file was not a cmf file\n");
X        printf("usage: %s <cmf file>\n", argv[0]);
X        exit(-1);
X    }
X
X    /* read and set instruments from cmf file */
X    get_instruments(cmf_fd);
X
X    /* get timing */
X    set_timing(cmf_fd);
X
X    /* open soundblaster fm chips */
X    fm_fd = open("/dev/sbfm", O_WRONLY);
X    if (fm_fd == -1)
X    {
X        perror("opening fm chips");
X        exit(-1);
X    }
X
X    /* play song */
X    play_song(cmf_fd);
X
X    return(0);
X}
X
X
X/* check for "CTMF" in first four bytes of file */
Xint verify_cmf(fd)
Xint fd;
X{
X    char idbuf[5];
X
X    /* get id */
X    lseek(fd, 0, SEEK_SET);
X    if (read(fd, idbuf, 4) != 4)
X        return(FALSE);
X    
X    /* compare to standard id */
X    idbuf[4] = (char)0;
X    if (strcmp(idbuf, "CTMF") != 0)
X        return(FALSE);
X    
X    return(TRUE);
X}
X
Xint get_instruments(fd)
Xint fd;
X{
X    int offset;
X    int num_instruments;
X    int i;
X    int rc;
X    int fnum, block, note;
X    unsigned char tmp_byte;
X    sb_fm_character note_character;
X
X    /* get offset of instrument block */
X    offset = 0;
X    lseek(fd, 0x06, SEEK_SET);
X    read(fd, &tmp_byte, 1);
X    lobyte(offset) = tmp_byte;
X    read(fd, &tmp_byte, 1);
X    hibyte(offset) = tmp_byte;
X
X    /* get number of instruments */
X    num_instruments = 0;
X    lseek(fd, 0x24, SEEK_SET);
X    read(fd, &tmp_byte, 1);
X    lobyte(num_instruments) = tmp_byte;
X    read(fd, &tmp_byte, 1);
X    hibyte(num_instruments) = tmp_byte;
X
X    /* allocate space */
X    instrument_table = (char **)malloc(sizeof(int *) * num_instruments);
X
X    /* read each instrument */
X    lseek(fd, offset, SEEK_SET);
X    for (i=0; i< num_instruments; i++)
X    {
X        /* allocate space */
X        instrument_table[i] = (char *)malloc(16);
X
X        /* set instrument characteristics */
X        read(fd, instrument_table[i], 16);
X    }
X
X    return(0);
X}
X
X
X/*
X * get and set timing parameters
X */
Xint set_timing(fd)
Xint fd;
X{
X    unsigned char tmp_byte;
X
X    /* get tempo */
X    tempo = 0;
X    lseek(fd, 0x0C, SEEK_SET);
X    read(fd, &tmp_byte, 1);
X    tempo = (unsigned int)tmp_byte;
X    read(fd, &tmp_byte, 1);
X    tempo += (unsigned int)tmp_byte << 8;
X
X    /* get herz of timing clock */
X    fm_herz = 0;
X    lseek(fd, 0x0C, SEEK_SET);
X    read(fd, &tmp_byte, 1);
X    fm_herz = (unsigned int)tmp_byte;
X    read(fd, &tmp_byte, 1);
X    fm_herz += (unsigned int)tmp_byte << 8;
X    
X    return(0);
X}
X
X
X/*
X * seek to the midi stream and handle midi events for the song
X */
Xint play_song(fd)
Xint fd;
X{
X    int offset;
X    unsigned char tmp_byte;
X    int delta;
X
X    /* get offset of music stream */
X    lseek(fd, 8, SEEK_SET);
X    read(fd, &tmp_byte, 1);
X    offset = (unsigned int)tmp_byte;
X    read(fd, &tmp_byte, 1);
X    offset += (unsigned int)tmp_byte << 8;
X    lseek(fd, offset, SEEK_SET);
X
X    /* process till EOF */
X    while(1)
X    {
X        /* get delta time */
X        delta = ReadVarLen(fd);
X        if (delta == -1)
X            break;
X
X        /* wait delta */
X        if (delta > 0)
X            high_res_sleep((double)delta/(double)fm_herz);
X
X        /* process midi event */
X        process_event(fd, delta);
X    }
X
X
X    return(0);
X}
X
X
X/*
X * read a variable length scalar in MIDI format
X */
Xint ReadVarLen(fd)
Xint fd;
X{
X    int value;
X    unsigned char tmp_byte;
X
X    if (read(fd, &tmp_byte, 1) == 0)
X        return(-1);
X    value = (int)tmp_byte;
X    if (tmp_byte & 0x80)
X    {
X        value &= 0x7F;
X        do
X        {
X            if (read(fd, &tmp_byte, 1) == 0)
X                return(-1);
X            value = (value << 7) + (tmp_byte & 0x7F);
X        } while (tmp_byte & 0x80);
X    }
X
X    return(value);
X}
X
X
X/*
X * process a midi event
X */
Xint process_event(fd, delta)
Xint fd;
X{
X    int rc;
X    unsigned char tmp_byte;
X    static int status = -1;
X    sb_fm_note note;
X
X    /* get status byte */
X    read(fd, &tmp_byte, 1);
X    if (tmp_byte & 0x80)
X    {
X        status = (unsigned int)tmp_byte;
X    }
X    else
X    {
X        /* running status, so back up one */
X        if (status == -1)
X        {
X            printf("ERROR in cmf file. Running status at beginning of file\n");
X            exit(-1);
X        }
X        lseek(fd, -1, SEEK_CUR);
X    }
X    
X    /* switch different events */
X    switch (status & 0xF0)
X    {
X        case 0x80:
X            /* turn note off */
X            ioctl(fm_fd, FM_IOCTL_NOTE_OFF, status & 0x0F);
X            note_on[status&0x0F] = 0;
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0x90:
X            /* get note */
X            read(fd, &tmp_byte, 1);
X            /* determine note */
X            note_num(note) = status & 0x0F;
X            fnum_low(note) = note_table[tmp_byte%12] & 0xFF;
X            fnum_low(note) = note_table[tmp_byte%12] & 0xFF;
X            keyon_blk_fnum(note) = 0;
X            keyon_blk_fnum(note) |= 1<<5;
X            keyon_blk_fnum(note) |= ((tmp_byte/12) & 7) << 2;
X            keyon_blk_fnum(note) |= (note_table[tmp_byte%12] & 0x3FF) >> 8;
X
X            /* turn note on */
X            if (note_on[status&0x0F])
X                ioctl(fm_fd, FM_IOCTL_NOTE_OFF, status&0x0F);
X            ioctl(fm_fd, FM_IOCTL_NOTE_ON, note);
X            note_on[status&0x0F] = 1;
X
X            /* waste a bytes */
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0xA0:
X            printf("polyphonic key pressure: not handled\n");
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0xB0:
X            printf("control change: not handled\n");
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0xC0:
X            /* change the instrument on a channel */
X            read(fd, &tmp_byte, 1);
X            load_instrument(status&0x0F, tmp_byte & 0x0F);
X            break;
X        case 0xD0:
X            printf("Channel Pressure: not handled\n");
X            /* waste a byte */
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0xE0:
X            printf("Pitch Wheel Change: not handled\n");
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X        case 0xF0:
X            printf("System Exclusive: not handled\n");
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X        default:
X            printf("internal program error\n");
X            /* waste two bytes */
X            read(fd, &tmp_byte, 1);
X            read(fd, &tmp_byte, 1);
X            break;
X    }
X
X
X    return(0);
X}
X
X
X/*
X * load an instrument from the instrument table into the SoundBlaster
X */
Xint load_instrument(channel, instrument)
X{
X    int rc;
X    sb_fm_character note_character;
X
X    /* error check! */
X    if ((channel <0) || (channel >= 9))
X        return;
X
X    /* abort instrument if being loaded */
X    if (note_on[channel])
X        ioctl(fm_fd, FM_IOCTL_NOTE_OFF, channel);
X
X    /* set instrument characteristics */
X    note_character.voice_num = channel;
X    memcpy(note_character.data, instrument_table[instrument], 16);
X    rc = ioctl(fm_fd, FM_IOCTL_SET_VOICE, (int)&note_character);
X    if (rc == -1)
X    {
X        perror("fm chips voice set");
X        exit(-1);
X    }
X
X    return(0);
X}
X
X
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
XHigher-resolution sleep
X* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Xint high_res_sleep(seconds)
X    double      seconds;
X{
X    int         fds = 0;
X    struct timeval timeout;
X
X    timeout.tv_sec = seconds;
X    timeout.tv_usec = (seconds - timeout.tv_sec) * 1000000.0;
X    select(0, &fds, &fds, &fds, &timeout);
X}
X
END_OF_FILE
  if test 8889 -ne `wc -c <'./apps/play_cmf.c'`; then
    echo shar: \"'./apps/play_cmf.c'\" unpacked with wrong size!
  fi
  # end of './apps/play_cmf.c'
fi
if test -f './apps/play_snd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./apps/play_snd.c'\"
else
  echo shar: Extracting \"'./apps/play_snd.c'\" \(5686 characters\)
  sed "s/^X//" >'./apps/play_snd.c' <<'END_OF_FILE'
X/*
X * Copyrighted as an unpublished work.
X * (c) Copyright 1991 Brian Smith
X * All rights reserved.
X *
X * Read the LICENSE file for details on distribution and use.
X *
X */
X
X#include <sys/fcntl.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/shm.h>
X#include <sys/sb.h>
X#include <signal.h>
X#include <errno.h>
X#include <stdio.h>
X
X#define NUMBUFS     4
X#define SHM_BUFSIZ  (8*4096)
X#define SHM_KEY     1796
X
Xtypedef struct {
X    char    buf[NUMBUFS*SHM_BUFSIZ];
X    int     write_waiting;
X    int     read_waiting;
X    int     locked[NUMBUFS];
X    int     length[NUMBUFS];
X}   buf_struct;
X
X
X/* GLOBALS */
Xint shmid;
Xbuf_struct *buffers;
Xint sound_fd;
X
Xvoid sigusr_handler()
X{
X    return;
X}
X
X
Xvoid cleanup()
X{
X    shmctl(shmid, IPC_RMID, 0);
X    exit(0);
X}
X
Xvoid detach()
X{
X    shmdt(buffers);
X    exit(0);
X}
X
X
Xint main(argc, argv)
Xint argc;
Xchar **argv;
X{
X    int infile;
X    int child_pid;
X    int buf_num;
X    int rc;
X
X    if (argc != 2)
X    {
X        printf("usage: %s <sound file>\n", argv[0]);
X        printf("\tif <sound file> is \"-\", then %s will play from stdin\n",
X            argv[0]);
X        exit(-1);
X    }
X
X    if (strcmp(argv[1], "-") == 0)
X        infile = 0;
X    else
X    {
X        infile = open(argv[1], O_RDONLY);
X        if (infile == -1)
X        {
X            perror("opening data file");
X            printf("usage: %s <sound file>\n", argv[0]);
X            printf("\tif <sound file> is \"-\", then %s will play from stdin\n",
X                argv[0]);
X            exit(-1);
X        }
X    }
X
X    /* open device */
X    sound_fd = open("/dev/sbdsp", O_WRONLY);
X    if (sound_fd == -1)
X    {
X        perror("opening SoundBlaster device");
X        exit(-1);
X    }
X
X    /* create shared memory segment */
X    shmid = shmget(SHM_KEY, sizeof(buf_struct), IPC_CREAT | IPC_EXCL | 0644);
X    if (shmid == -1)
X    {
X        perror("creating shared-mem buffer");
X        exit(-1);
X    }
X
X    /* attach handler for signal */
X    sigset(SIGUSR1, sigusr_handler);
X    sigset(SIGINT, cleanup);
X    sigset(SIGHUP, cleanup);
X
X    /* start read process */
X    child_pid = fork();
X    switch (child_pid)
X    {
X        case 0:
X            start_read(infile);
X            exit(0);
X        case -1:
X            perror("forking reader process");
X            cleanup();
X    }
X
X    /* attach shared memory segment */
X    buffers = (buf_struct *)shmat(shmid, 0, 0);
X    if (buffers == (buf_struct *)-1)
X    {
X        perror("attaching shared memory");
X        if (buffers->read_waiting)
X            kill(child_pid, SIGKILL);
X        cleanup();
X    }
X
X    /* start writing stuff in buffers */
X    while(1)
X    {
X        /* wait until buffer is locked for us, or flush and break on eof */
X        if (!buffers->locked[buf_num])
X        {
X            buffers->write_waiting = 1;
X            sigpause(SIGUSR1);
X            continue;
X        }
X
X        /* not waiting now */
X        buffers->write_waiting = 0;
X
X        /* eof check */
X        if (buffers->length[buf_num] <= 0)
X            break;
X
X        /* write out data in buffer */
X        rc = write(sound_fd, buffers->buf + (buf_num*SHM_BUFSIZ),
X            buffers->length[buf_num]);
X        if (rc != buffers->length[buf_num])
X        {
X            if ((errno == EINTR) || (errno == 0))
X                continue;
X
X            perror("writing to sound blaster");
X            kill(child_pid, SIGKILL);
X            cleanup();
X        }
X
X        /* unlock buffer for child's use */
X        buffers->locked[buf_num] = 0;
X        if (buffers->read_waiting)
X            kill(child_pid, SIGUSR1);
X
X        /* go to next buffer */
X        buf_num++;
X        buf_num %= NUMBUFS;
X    }
X
X
X    cleanup();
X    return(0);
X}
X
Xint start_read(infile)
Xint infile;
X{
X    buf_struct *buffers;
X    int buf_num = 0;
X
X    /* attach handler for signal */
X    sigset(SIGUSR1, sigusr_handler);
X    sigset(SIGINT, detach);
X    sigset(SIGHUP, detach);
X
X    /* attach shared memory */
X    buffers = (buf_struct *)shmat(shmid, 0, 0);
X    if (buffers == (buf_struct *)-1)
X    {
X        perror("attaching shared memory");
X        exit(0);
X    }
X
X    /* init shared mem stuff */
X    buffers->read_waiting = 0;
X    for (buf_num=0; buf_num < NUMBUFS; buf_num++)
X        buffers->locked[buf_num] = 0;
X
X    /* start reading into buffers */
X    buf_num = 0;
X    sleep(1);
X    while(1)
X    {
X        /* wait for current buffer to become unlocked */
X        if (buffers->locked[buf_num])
X        {
X            buffers->read_waiting = 1;
X            sigpause(SIGUSR1);
X            continue;
X        }
X
X        /* not waiting any more */
X        buffers->read_waiting = 0;
X
X        /* actually read data */
X        buffers->length[buf_num] =
X            read(infile, buffers->buf + (buf_num*SHM_BUFSIZ), SHM_BUFSIZ);
X        if (buffers->length[buf_num] == -1) 
X        {
X            if (errno == EINTR)
X                continue;
X            else
X            {
X                perror("reading from input file\n");
X                detach();
X            }
X        }
X        else if (buffers->length[buf_num] == 0)
X        {
X            buffers->locked[buf_num] = 1;
X
X            /* wake up parent */
X            if (buffers->write_waiting)
X                kill(getppid(), SIGUSR1);
X
X            break;
X        }
X
X        /* lock buffer for parent's use */
X        buffers->locked[buf_num] = 1;
X                
X        /* wake up parent */
X        if (buffers->write_waiting)
X            kill(getppid(), SIGUSR1);
X
X        /* go to next buffer */
X        buf_num++;
X        buf_num %= NUMBUFS;
X    }
X        
X
X    /* wake up parent */
X    if (buffers->write_waiting)
X        kill(getppid(), SIGUSR1);
X
X    /* detach shared memory */
X    shmdt(buffers);
X
X    return(0);
X}
END_OF_FILE
  if test 5686 -ne `wc -c <'./apps/play_snd.c'`; then
    echo shar: \"'./apps/play_snd.c'\" unpacked with wrong size!
  fi
  # end of './apps/play_snd.c'
fi
if test -f './apps/record_snd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./apps/record_snd.c'\"
else
  echo shar: Extracting \"'./apps/record_snd.c'\" \(5942 characters\)
  sed "s/^X//" >'./apps/record_snd.c' <<'END_OF_FILE'
X/*
X * Copyrighted as an unpublished work.
X * (c) Copyright 1991 Brian Smith
X * All rights reserved.
X *
X * Read the LICENSE file for details on distribution and use.
X *
X */
X
X#include <sys/fcntl.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/shm.h>
X#include <sys/sb.h>
X#include <signal.h>
X#include <errno.h>
X#include <stdio.h>
X
X#define NUMBUFS     4
X#define SHM_BUFSIZ  (8*4096)
X#define SHM_KEY     1796
X
Xtypedef struct {
X    char    buf[NUMBUFS*SHM_BUFSIZ];
X    int     write_waiting;
X    int     read_waiting;
X    int     locked[NUMBUFS];
X    int     length[NUMBUFS];
X}   buf_struct;
X
X
X/* GLOBALS */
Xint shmid;
Xbuf_struct *buffers;
X
Xvoid sigusr_handler()
X{
X    return;
X}
X
X
Xvoid cleanup()
X{
X    printf("cleanup\n");
X    shmctl(shmid, IPC_RMID, 0);
X    exit(0);
X}
X
Xvoid detach()
X{
X    printf("detaching\n");
X    shmdt(buffers);
X    exit(0);
X}
X
X
Xint main(argc, argv)
Xint argc;
Xchar **argv;
X{
X    int outfile;
X    int sound_fd;
X    int child_pid;
X    int buf_num;
X    int rc;
X
X    if (argc != 2)
X    {
X        printf("usage: %s <new sound file>\n", argv[0]);
X        printf("\tif <sound file> is \"-\", then %s will record to stdout\n",
X            argv[0]);
X        exit(-1);
X    }
X
X    if (strcmp(argv[1], "-") == 0)
X        outfile = 1;
X    else
X    {
X        outfile = open(argv[1], O_WRONLY | O_CREAT, 0666);
X        if (outfile == -1)
X        {
X            perror("opening output file");
X            printf("usage: %s <output sound file>\n", argv[0]);
X            printf("\tif <sound file> is \"-\", %s will record to stdout\n",
X                argv[0]);
X            exit(-1);
X        }
X    }
X
X    /* open device */
X    sound_fd = open("/dev/sbdsp", O_RDONLY);
X    if (sound_fd == -1)
X    {
X        perror("opening SoundBlaster device");
X        exit(-1);
X    }
X    if (ioctl(sound_fd, DSP_IOCTL_RESET) == -1)
X    {
X        perror("trying to reset DSP");
X        exit(-1);
X    }
X    if (ioctl(sound_fd, DSP_IOCTL_VOICE, 0) == -1)
X    {
X        perror("trying to set voice on");
X        exit(-1);
X    }
X
X    /* create shared memory segment */
X    shmid = shmget(SHM_KEY, sizeof(buf_struct), IPC_CREAT | IPC_EXCL | 0644);
X    if (shmid == -1)
X    {
X        perror("creating shared-mem buffer");
X        exit(-1);
X    }
X
X    /* attach handler for signal */
X    sigset(SIGUSR1, sigusr_handler);
X    sigset(SIGINT, cleanup);
X    sigset(SIGHUP, cleanup);
X
X    /* start read process */
X    child_pid = fork();
X    switch (child_pid)
X    {
X        case 0:
X            start_read(sound_fd);
X            exit(0);
X        case -1:
X            perror("forking read process");
X            cleanup();
X    }
X
X    /* attach shared memory segment */
X    buffers = (buf_struct *)shmat(shmid, 0, 0);
X    if (buffers == (buf_struct *)-1)
X    {
X        perror("attaching shared memory");
X        if (buffers->read_waiting)
X            kill(child_pid, SIGKILL);
X        cleanup();
X    }
X
X    /* start writing stuff from buffers */
X    while(1)
X    {
X        /* wait until buffer is locked for us, or flush and break on eof */
X        if (!buffers->locked[buf_num])
X        {
X            buffers->write_waiting = 1;
X            sigpause(SIGUSR1);
X            continue;
X        }
X
X        /* not waiting now */
X        buffers->write_waiting = 0;
X
X        /* eof check */
X        if (buffers->length[buf_num] <= 0)
X            break;
X
X        /* write out data in buffer */
X        rc = write(outfile, buffers->buf + (buf_num*SHM_BUFSIZ),
X            buffers->length[buf_num]);
X        if (rc != buffers->length[buf_num])
X        {
X            if ((errno == EINTR) || (errno == 0))
X                continue;
X
X            perror("writing to output file");
X            kill(child_pid, SIGKILL);
X            cleanup();
X        }
X
X        /* unlock buffer for child's use */
X        buffers->locked[buf_num] = 0;
X        if (buffers->read_waiting)
X            kill(child_pid, SIGUSR1);
X
X        /* go to next buffer */
X        buf_num++;
X        buf_num %= NUMBUFS;
X    }
X
X
X    printf("done\n");
X    cleanup();
X    return(0);
X}
X
Xint start_read(sound_fd)
Xint sound_fd;
X{
X    buf_struct *buffers;
X    int buf_num = 0;
X
X    /* attach handler for signal */
X    sigset(SIGUSR1, sigusr_handler);
X    sigset(SIGINT, detach);
X    sigset(SIGHUP, detach);
X
X    /* attach shared memory */
X    buffers = (buf_struct *)shmat(shmid, 0, 0);
X    if (buffers == (buf_struct *)-1)
X    {
X        perror("attaching shared memory");
X        exit(0);
X    }
X
X    for (buf_num=0; buf_num < NUMBUFS; buf_num++)
X        buffers->locked[buf_num] = 0;
X
X    /* start reading into buffers */
X    buf_num = 0;
X    sleep(1);
X    while(1)
X    {
X        /* wait for current buffer to become unlocked */
X        if (buffers->locked[buf_num])
X        {
X            buffers->read_waiting = 1;
X            sigpause(SIGUSR1);
X            continue;
X        }
X
X        /* not waiting any more */
X        buffers->read_waiting = 1;
X
X        /* actually read data */
X        buffers->length[buf_num] =
X            read(sound_fd, buffers->buf + (buf_num*SHM_BUFSIZ), SHM_BUFSIZ);
X        if (buffers->length[buf_num] == -1) 
X        {
X            if (errno == EINTR)
X                continue;
X            else
X            {
X                perror("reading from SoundBlaster\n");
X                detach();
X            }
X        }
X        else if (buffers->length[buf_num] == 0)
X        {
X            buffers->locked[buf_num] = 1;
X
X            /* wake up parent */
X            if (buffers->write_waiting)
X                kill(getppid(), SIGUSR1);
X
X            break;
X        }
X
X        /* lock buffer for parent's use */
X        buffers->locked[buf_num] = 1;
X                
X        /* wake up parent */
X        if (buffers->write_waiting)
X            kill(getppid(), SIGUSR1);
X
X        /* go to next buffer */
X        buf_num++;
X        buf_num %= NUMBUFS;
X    }
X        
X
X    /* wake up parent */
X    kill(getppid(), SIGUSR1);
X
X    /* detach shared memory */
X    shmdt(buffers);
X
X    return(0);
X}
END_OF_FILE
  if test 5942 -ne `wc -c <'./apps/record_snd.c'`; then
    echo shar: \"'./apps/record_snd.c'\" unpacked with wrong size!
  fi
  # end of './apps/record_snd.c'
fi
if test -f './sb.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./sb.c'\"
else
  echo shar: Extracting \"'./sb.c'\" \(23791 characters\)
  sed "s/^X//" >'./sb.c' <<'END_OF_FILE'
X/*
X * Copyrighted as an unpublished work.
X * (c) Copyright 1991 Brian Smith
X * All rights reserved.
X *
X * Read the LICENSE file for details on distribution and use.
X *
X */
X
X
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/file.h>
X#include <sys/ioctl.h>
X#include <sys/dir.h>
X#include <sys/buf.h>
X#include <sys/iobuf.h>
X#include <sys/signal.h>
X#include <sys/user.h>
X#include <sys/sysmacros.h>
X#include <sys/dma.h>
X#include <sys/cmn_err.h>
X#include <sys/errno.h>
X#include <sys/inline.h>
X#include "sb.h"
X
X/* defs needed because of ommisions ISC! */
X#ifndef DMA_Rdmode
X#define DMA_Rdmode 0x44
X#endif
X#ifndef DMA_Wrmode
X#define DMA_Wrmode 0x48
X#endif
X#ifndef DMA_BLOCK
X#define DMA_BLOCK 0
X#endif
X#ifndef DMA_NBLOCK
X#define DMA_NBLOCK 1
X#endif
X
Xextern ushort sb_configured;
Xextern ushort sb_dma_chan;
Xextern ushort sb_interrupt;
X
X/* GLOBALS */
Xstruct sb_stat_type sb_status;          /* Soundblaster Status */
Xextern time_t lbolt;
X
X
X/*
X * reset DSP chip, and return TRUE if successful
X */
Xstatic int dsp_reset()
X{
X    int i;
X    register unsigned char rc;
X
X    /* reset dsp */
X    outb(DSP_RESET, 0x01);
X    tenmicrosec();
X    outb(DSP_RESET, 0x00);
X    for (i=0; i<200; i++)
X    {
X        rc = (unsigned char)inb(DSP_RDAVAIL);
X        if (rc & 128)
X        {
X            rc = inb(DSP_RDDATA);
X            if (rc == 0xAA)
X                break;
X        }
X
X        i--;
X        continue;
X    }
X    if (i>=200)
X        cmn_err(CE_WARN, "SoundBlaster(tm) DSP failed initialization\n");
X
X    /* reset sampling speed */
X    dsp_speed();
X
X    return(TRUE);
X}
X
X
X/*
X * program the DSP's time constant: the sampling/output rate
X */
Xint dsp_speed()
X{
X    unchar time_constant;
X    unchar dsp_return;
X
X    time_constant = (char)(256 - (1000000/sb_status.dsp_speed));
X
X    /* send command SET_TIME_CONSTANT (0x40) */
X    do
X    {
X        dsp_return = inb(DSP_STATUS);
X    } while (dsp_return & (1<<7));
X    outb(DSP_COMMAND, 0x40);
X
X    /* send one byte time_constant */
X    do
X    {
X        dsp_return = inb(DSP_STATUS);
X    } while (dsp_return & (1<<7));
X    outb(DSP_COMMAND, time_constant);
X
X    return(TRUE);
X}
X
X
X/*
X * called at OS startup time to initialize the SoundBlaster
X */
Xint sbinit()
X{
X    int failure = FALSE;
X
X    if (!sb_configured)
X        cmn_err(CE_WARN, "sbinit(): SoundBlaster(tm) not configured\n");
X
X    sbtab[0].b_actf = NULL;
X    sbtab[0].b_actl = NULL;
X    sbtab[0].b_active = FALSE;
X
X    /* init cms chips' state */
X    sb_status.cms_open = NOT_OPEN;
X    sb_status.cms_waiting = 0;
X
X    /* init fm chips' state */
X    sb_status.fm_open = NOT_OPEN;
X    sb_status.fm_waiting = 0;
X
X    /* init dsp chip(s)' state */
X    sb_status.dsp_open = NOT_OPEN;
X    sb_status.dsp_speed = 11000;
X    sb_status.dsp_compression = ADCPM_8;
X    if (!dsp_reset())
X        failure = TRUE;
X
X    /* all OK, send notification to console */
X    if (!failure)
X    {
X        cmn_err(CE_CONT, "SoundBlaster(tm) is recognized and initialized\n");
X#ifdef DEBUG
X        cmn_err(CE_CONT, "operating upon DMA channel %d\n", sb_dma_chan);
X        cmn_err(CE_CONT, "operating upon interrupt vector %d\n", sb_interrupt);
X#endif
X    }
X    else
X    {
X        cmn_err(CE_WARN, "SoundBlaster(tm) initialization failed\n");
X        sb_status.dsp_open = OPEN_READ | OPEN_WRITE;
X        sb_status.fm_open  = OPEN_READ | OPEN_WRITE;
X        sb_status.cms_open = OPEN_READ | OPEN_WRITE;
X    }
X
X    return(0);
X}
X
X
X/*
X * turn the dsp voice on if param is true
X */
Xvoid dsp_voice(on)
Xint on;
X{
X    unchar dsp_return;
X
X    do
X    {
X        dsp_return = inb(DSP_STATUS);
X    } while (dsp_return & (1<<7));
X
X    if (on)
X    {
X        outb(DSP_COMMAND, 0xD1);
X#ifdef DEBUG
X        cmn_err(CE_CONT, "dsp_voice(): voice on\n");
X#endif
X    }
X    else
X    {
X        outb(DSP_COMMAND, 0xD3);
X#ifdef DEBUG
X        cmn_err(CE_CONT, "dsp_voice(): voice off\n");
X#endif
X    }
X
X    return;
X}
X
X
X/*
X * grabs the DSP chip for a process.
X * sets u.u_error to EBUSY if already opened by other device
X */
Xstatic void dsp_open(flag)
Xint flag;
X{
X    int old_pri;
X    int rc;
X
X
X    /* check if already open */
X    old_pri = spl6();
X    if (sb_status.dsp_open)
X    {
X        u.u_error = EBUSY;
X        splx(old_pri);
X        return;
X    }
X
X    /* grab dma and device */
X    dma_alloc(SB_DMA_CHAN, DMA_BLOCK);
X    dsp_reset();
X    dsp_voice(FALSE);
X    if (flag & FWRITE)
X    {
X        if (flag & FREAD)
X        {
X            sb_status.dsp_open = OPEN_WRITE | OPEN_READ;
X#ifdef DEBUG
X            cmn_err(CE_CONT, "open read/write %d\n", flag);
X#endif
X        }
X        else
X        {
X            sb_status.dsp_open = OPEN_WRITE;
X#ifdef DEBUG
X            cmn_err(CE_CONT, "open write %d\n", flag);
X#endif
X            dsp_voice(TRUE);
X        }
X    }
X    else if (flag & FREAD)
X    {
X        sb_status.dsp_open = OPEN_READ;
X#ifdef DEBUG
X        cmn_err(CE_CONT, "open read/write %d\n", flag);
X#endif
X    }
X    else
X    {
X#ifdef DEBUG
X        cmn_err(CE_CONT, "unknown flags on open %d\n", flag);
X#endif
X        u.u_error = ENXIO;
X    }
X
X    /* END CRITICAL */
X    splx(old_pri);
X
X    return;
X}
X
X
X/*
X * grabs the FM chips for a process.
X * sets u.u_error to EBUSY if already opened by other device
X */
Xstatic void fm_open(flag)
Xint flag;
X{
X    int old_pri;
X    int rc;
X
X
X    /* check if already open */
X    old_pri = spl6();
X    if (sb_status.fm_open != NOT_OPEN)
X    {
X        u.u_error = EBUSY;
X        splx(old_pri);
X        return;
X    }
X
X    /* grab device and affirm that only openable with write permission */
X    fm_reset();
X    if (flag & FWRITE)
X        sb_status.fm_open = OPEN_WRITE;
X    else
X        u.u_error = ENXIO;
X
X    /* END CRITICAL */
X    splx(old_pri);
X
X    return;
X}
X
X
X
X/*
X * multiplexes opens to dsp_open(), fm_open(), and cms_open()
X * depending upon which minor dev was used
X */
Xint sbopen(dev, flag)
Xint dev;
Xint flag;
X{
X    int minor_num;
X
X#ifdef DEBUG
X    cmn_err(CE_CONT, "opened with flag %d\n", flag);
X#endif
X
X    minor_num = minor(dev);
X    switch (minor_num)
X    {
X        case SB_CMS_NUM:
X            u.u_error = ENXIO;
X            break;
X        case SB_FM_NUM:
X            fm_open(flag);
X            break;
X        case SB_DSP_NUM:
X            dsp_open(flag);
X            break;
X        default:
X            u.u_error = ENXIO;
X    }
X
X    return(0);
X}
X
X
X/*
X * Release and reset the dsp chip
X */
Xvoid dsp_close()
X{
X    int old_pri;
X
X    old_pri = spl6();
X    sb_status.dsp_open = NOT_OPEN;
X    dsp_reset();
X    dsp_voice(FALSE);
X    dma_relse(SB_DMA_CHAN);
X    splx(old_pri);
X
X    return;
X}
X
X
X/*
X * Release and reset the fm chip
X */
Xvoid fm_close()
X{
X    int old_pri;
X
X    old_pri = spl6();
X    sb_status.fm_open = NOT_OPEN;
X    fm_reset();
X    splx(old_pri);
X
X    return;
X}
X
X
X/*
X * Multiplexes between the closes for dsp, fm, and cms chips
X */
Xint sbclose(dev)
Xint dev;
X{
X    int minor_num;
X
X    minor_num = minor(dev);
X    switch (minor_num)
X    {
X        case SB_CMS_NUM:
X            break;
X        case SB_FM_NUM:
X            fm_close();
X            break;
X        case SB_DSP_NUM:
X            dsp_close();
X            break;
X        default:
X            u.u_error = ENXIO;
X    }
X
X    return(0);
X}
X
X
X/*
X * start DMA rolling for DSP
X * only used by DSP chips, so no need for multiplexing on minor num
X */
Xint sb_iostart()
X{
X    int old_pri;
X    paddr_t phys_address;
X    unsigned char tmp_byte;
X    unsigned int length;
X
X    /* begin critical */
X    old_pri = spl6();
X
X    /* check for invalid length of buf */
X    if ((sbtab[0].b_actf->b_bcount < 1) || (sbtab[0].b_actf->b_bcount > 0xFFFF))
X    {
X        cmn_err(CE_WARN, "sb_iostart(): invalid length of buf, %d\n",
X            sbtab[0].b_actf->b_bcount);
X
X        /* artificially terminate */
X        sbtab[0].b_actf->b_resid = 0;
X        sbtab[0].b_actf = sbtab[0].b_actf->av_forw;
X        iodone(sbtab[0].b_actf);
X        splx(old_pri);
X        return(0);
X    }
X
X    /* mark device in sbtab as active */
X    sbtab[0].b_active = TRUE;
X
X    /* prep DMA channel */
X    phys_address = vtop(paddr(sbtab[0].b_actf), sbtab[0].b_actf->b_proc);
X    if (sbtab[0].b_actf->b_flags & B_READ)
X        dma_param(SB_DMA_CHAN, DMA_Rdmode, phys_address,
X            sbtab[0].b_actf->b_bcount);
X    else
X        dma_param(SB_DMA_CHAN, DMA_Wrmode, phys_address,
X            sbtab[0].b_actf->b_bcount);
X    dma_enable(SB_DMA_CHAN);
X
X    /* prep SoundBlaster for 8-bit DMA */
X    do {
X        tmp_byte = inb(DSP_STATUS);
X    } while (tmp_byte & (1<<7));
X    if (sbtab[0].b_actf->b_flags & B_READ)
X        outb(DSP_COMMAND, 0x24);
X    else
X        outb(DSP_COMMAND, 0x14);
X
X    /* prep SoundBlaster for length */
X    length = sbtab[0].b_actf->b_bcount-1;
X    do {
X        tmp_byte = inb(DSP_STATUS);
X    } while (tmp_byte & (1<<7));
X    tmp_byte = length & 0xFF;
X    outb(DSP_COMMAND, tmp_byte);
X    do {
X        tmp_byte = inb(DSP_STATUS);
X    } while (tmp_byte & (1<<7));
X    tmp_byte = (length & 0xFF00) >> 8;
X    outb(DSP_COMMAND, tmp_byte);
X
X    /* end critical */
X    splx(old_pri);
X
X    return(0);
X}
X
X
X/*
X * write sound samples to dsp
X */
Xint sb_strategy(bufhead)
Xregister struct buf *bufhead;
X{
X    int old_pri;
X
X    /* start critical section */
X    old_pri = spl6();
X
X    /* prep buffer for addition to queue */
X    bufhead->b_start = lbolt;
X    bufhead->av_forw = NULL;
X
X    /* add buffer to queue */
X    if (sbtab[0].b_actf == NULL)
X    {
X        /* add to empty queue */
X        sbtab[0].b_actf = bufhead;
X        sbtab[0].b_actl = bufhead;
X
X        /* start io */
X        sb_iostart();
X    }
X    else
X    {
X        /* add to already started queue */
X        sbtab[0].b_actl->av_forw = bufhead;
X        sbtab[0].b_actl = bufhead;
X    }
X
X    /* end critical section */
X    splx(old_pri);
X
X    return(0);
X}
X
X
X/*
X * breaks up io requests so that DMA can be done
X */
Xstatic int sb_breakup(bp)
Xregister struct buf *bp;
X{
X    dma_breakup(sb_strategy, bp);
X    return(0);
X}
X
X
X/*
X * Starts the DMA write to the  Soundblaster
X */
Xint dsp_write(dev)
Xint dev;
X{
X    physio(sb_breakup, 0, dev, B_WRITE);
X    return(0);
X}
X
X
X/*
X * multiplexes writes to dsp, cm/s and fm chips
X */
Xint sbwrite(dev)
Xint dev;
X{
X    int minor_num;
X
X    minor_num = minor(dev);
X    switch (minor_num)
X    {
X        case SB_CMS_NUM:
X            cmn_err(CE_CONT, "sbwrite(): error, cms device accessed\n");
X            u.u_error = ENXIO;
X            break;
X        case SB_FM_NUM:
X            u.u_error = ENXIO;
X            break;
X        case SB_DSP_NUM:
X            dsp_write(dev);
X            break;
X        default:
X            cmn_err(CE_CONT, "sbwrite(): unknown minor device %d\n", minor_num);
X            u.u_error = ENXIO;
X    }
X    return(0);
X}
X
X
X/*
X * Starts the DMA read from the Soundblaster
X */
Xint dsp_read(dev)
Xint dev;
X{
X    physio(sb_breakup, 0, dev, B_READ);
X    return(0);
X}
X
X
X/*
X * multiplexes read/writes to different functions
X */
Xint sbread(dev)
Xint dev;
X{
X    int minor_num;
X
X    minor_num = minor(dev);
X    switch (minor_num)
X    {
X        case SB_CMS_NUM:
X            cmn_err(CE_CONT, "sbread(): error, cms device accessed\n");
X            u.u_error = ENXIO;
X            break;
X        case SB_FM_NUM:
X            u.u_error = ENXIO;
X            break;
X        case SB_DSP_NUM:
X            dsp_read(dev);
X            break;
X        default:
X            cmn_err(CE_CONT, "sbread(): unknown minor device %d\n", minor_num);
X            u.u_error = ENXIO;
X    }
X    return(0);
X}
X
X
X/*
X * minor control function for the dsp
X */
Xvoid dsp_ioctl(cmd, arg1, arg2)
Xint cmd;
Xcaddr_t arg1, arg2;
X{
X    switch(cmd)
X    {
X        case DSP_IOCTL_RESET:
X            dsp_reset();
X            break;
X        case DSP_IOCTL_SPEED:
X            sb_status.dsp_speed = (int)arg1;
X            dsp_speed();
X            break;
X        case DSP_IOCTL_VOICE:
X            dsp_voice((int)arg1);
X            break;
X        default:
X            break;
X    }
X
X    return;
X}
X
X
X/*
X * turns a note/key off
X */
Xvoid fm_key_off(voice_num)
Xint voice_num;
X{
X    unsigned char reg_num;
X    
X    /* error checking to avoid munching kernel */
X    if ((voice_num < 0) || (voice_num >= MAX_FM_NOTES))
X    {
X        u.u_error = EFAULT;
X        return;
X    }
X    
X    /* turn voice off */
X    reg_num = (unsigned char)0xB0 + (unsigned char)voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "turning off voice for voice %d\n", voice_num);
X    cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
X#endif
X    outb(FM_SELECT, reg_num);
X    tenmicrosec();
X    outb(FM_REG, 0);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    return;
X}
X
X/*
X * turns a key on, with the frequency and octave so indicated in the
X * low 2 bytes of the data integer
X */
Xvoid fm_key_on(usr_note)
Xint usr_note;
X{
X    register unsigned char reg_num;
X    int tmp_int;
X
X#ifdef DEBUG
X    cmn_err(CE_CONT, "turning on voice for voice %d\n", note_num(usr_note));
X    cmn_err(CE_CONT, "fnum_low (dec): %d\n", fnum_low(usr_note));
X    cmn_err(CE_CONT, "fnum_low (hex): %x\n", fnum_low(usr_note));
X    cmn_err(CE_CONT, "keyon_blk_fnum (dec): %d\n", keyon_blk_fnum(usr_note));
X    cmn_err(CE_CONT, "keyon_blk_fnum (hex): %x\n", keyon_blk_fnum(usr_note));
X#endif
X
X    /* put out first byte */
X    reg_num = (unsigned char)0xA0 + note_num(usr_note);
X#ifdef DEBUG
X    cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
X#endif
X    outb(FM_SELECT, reg_num);
X    tenmicrosec();
X    outb(FM_REG, fnum_low(usr_note));
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* put out second byte */
X    reg_num = (unsigned char)0xB0 + note_num(usr_note);
X#ifdef DEBUG
X    cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
X#endif
X    outb(FM_SELECT, reg_num);
X    tenmicrosec();
X    outb(FM_REG, keyon_blk_fnum(usr_note));
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    
X    return;
X}
X
X/* at this point, it just turns all notes to off */
Xint fm_reset()
X{
X    int i;
X
X    /* must be initialized? */
X    outb(FM_SELECT, 1);
X    tenmicrosec();
X    outb(FM_REG, 0);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* dispense for time being */
X    for (i=0; i<MAX_FM_NOTES; i++)
X        fm_key_off(i);
X
X    return(0);
X}
X
X
X/*
X * set characteristics on a voice
X */
Xvoid fm_set_voice(usr_character)
Xsb_fm_character *usr_character;
X{
X    register unsigned char op_cell_num;
X    int cell_offset;
X    sb_fm_character voice_data;
X    int i;
X
X    /* copy in characteristics */
X    if (copyin(usr_character, &voice_data, sizeof(sb_fm_character)) == -1)
X    {
X#ifdef DEBUG
X        cmn_err(CE_CONT, "fm_set_voice(): bad address\n");
X#endif
X        u.u_error = EFAULT;
X        return;
X    }
X
X    /* echo voice characteristics */
X#ifdef DEBUG
X    cmn_err(CE_CONT, "setting voice number %d\n", voice_data.voice_num);
X    cmn_err(CE_CONT, "setting voice number(hex) %x\n", voice_data.voice_num);
X    cmn_err(CE_CONT, "data: ");
X    for (i=0; i<16; i++)
X        cmn_err(CE_CONT, "%x ", (unsigned int)voice_data.data[i]);
X    cmn_err(CE_CONT, "\n");
X#endif
X
X    /* check on voice_num range */
X    if ((voice_data.voice_num >= MAX_FM_NOTES) || (voice_data.voice_num < 0))
X    {
X        cmn_err(CE_CONT, "fm_set_voice(): voice number out of range\n");
X        u.u_error = EFAULT;
X    }
X    cell_offset = voice_data.voice_num%3 + ((voice_data.voice_num / 3) << 3);
X
X    /* set sound characteristic */
X    op_cell_num = 0x20 + (char)cell_offset;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[0]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    op_cell_num += 3;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[1]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set level/output */
X    op_cell_num = 0x40 + (char)cell_offset;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[2]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    op_cell_num += 3;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[3]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Attack/Decay */
X    op_cell_num = 0x60 + (char)cell_offset;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[4]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    op_cell_num += 3;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[5]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Sustain/Release */
X    op_cell_num = 0x80 + (char)cell_offset;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[6]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    op_cell_num += 3;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[7]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Wave Select */
X    op_cell_num = 0xE0 + (char)cell_offset;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[8]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X    op_cell_num += 3;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[9]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Feedback/Selectivity */
X    op_cell_num = (unsigned char)0xC0 + (unsigned char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for C0-C8 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[10]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    return;
X}
X
X
X/*
X * set characteristics on an opcell
X */
Xvoid fm_set_opcell(usr_character)
Xsb_fm_character *usr_character;
X{
X    register unsigned char op_cell_num;
X    int cell_offset;
X    sb_fm_character voice_data;
X    int i;
X
X    /* copy in characteristics */
X    if (copyin(usr_character, &voice_data, sizeof(sb_fm_character)) == -1)
X    {
X#ifdef DEBUG
X        cmn_err(CE_CONT, "bad address\n");
X#endif
X        u.u_error = EFAULT;
X        return;
X    }
X
X    /* echo voice characteristics */
X#ifdef DEBUG
X    cmn_err(CE_CONT, "setting opcell number %d\n", voice_data.voice_num);
X    cmn_err(CE_CONT, "setting opcell number(hex) %x\n", voice_data.voice_num);
X    cmn_err(CE_CONT, "data: ");
X    for (i=0; i<8; i++)
X        cmn_err(CE_CONT, "%x ", (unsigned int)voice_data.data[i]);
X    cmn_err(CE_CONT, "\n");
X#endif
X
X    /* check on opcell range */
X    if ((voice_data.voice_num >= 2*MAX_FM_NOTES) || (voice_data.voice_num < 0))
X    {
X        cmn_err(CE_CONT, "opcell number out of range\n");
X        u.u_error = EFAULT;
X    }
X
X    /* set sound characteristic */
X    op_cell_num = 0x20 + (char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[0]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set level/output */
X    op_cell_num = 0x40 + (char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[1]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Attack/Decay */
X    op_cell_num = 0x60 + (char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[2]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Sustain/Release */
X    op_cell_num = 0x80 + (char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[3]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Wave Select */
X    op_cell_num = 0xE0 + (char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[4]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    /* set Feedback/Selectivity */
X    op_cell_num = (unsigned char)0xC0 + (unsigned char)voice_data.voice_num;
X#ifdef DEBUG
X    cmn_err(CE_CONT, "op_cell for C0-C8 = %x\n", op_cell_num);
X#endif
X    outb(FM_SELECT, op_cell_num);
X    tenmicrosec();
X    outb(FM_REG, voice_data.data[5]);
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    return;
X}
X
X/*
X * set the register which contains the keyon/off for rhythm, and depth flags
X */
Xvoid fm_set_rhythm(new_rhythm)
Xint new_rhythm;
X{
X    /* herf it in */
X    outb(FM_SELECT, 0xBD);
X    tenmicrosec();
X    outb(FM_REG, lobyte(new_rhythm));
X    tenmicrosec();
X    tenmicrosec();
X    tenmicrosec();
X
X    return;
X}
X
X
X/*
X * The only control for the FM chips.  The rest belongs in user code.
X */
Xvoid fm_ioctl(cmd, arg1, arg2)
Xint cmd;
Xcaddr_t arg1, arg2;
X{
X    switch(cmd)
X    {
X        case FM_IOCTL_RESET:
X            fm_reset();
X            break;
X        case FM_IOCTL_NOTE_ON:
X            fm_key_on((int)arg1);
X            break;
X        case FM_IOCTL_NOTE_OFF:
X            fm_key_off((int)arg1);
X            break;
X        case FM_IOCTL_SET_VOICE:
X            fm_set_voice((sb_fm_character *)arg1);
X            break;
X        case FM_IOCTL_SET_OPCELL:
X            fm_set_opcell((sb_fm_character *)arg1);
X            break;
X        case FM_IOCTL_SET_RHYTHM:
X            fm_set_rhythm((int)arg1);
X            break;
X        default:
X            break;
X    }
X
X    return;
X}
X
X 
X/*
X * multiplex ioctl to different sub-devices (minor numbers)
X */
Xint sbioctl(dev, cmd, arg1, arg2)
Xint dev;
Xint cmd;
Xcaddr_t arg1, arg2;
X{
X    int minor_num;
X
X    minor_num = minor(dev);
X    switch (minor_num)
X    {
X        case SB_CMS_NUM:
X            cmn_err(CE_CONT, "sbioctl cms\n");
X            break;
X        case SB_FM_NUM:
X            fm_ioctl(cmd, arg1, arg2);
X            break;
X        case SB_DSP_NUM:
X            dsp_ioctl(cmd, arg1, arg2);
X            break;
X        default:
X            cmn_err(CE_CONT, "sbioctl unknown minor %d\n", minor_num);
X            u.u_error = ENXIO;
X    }
X    return(0);
X}
X
X
X/*
X * responds to interrupt sent by DSP chips
X * and starts the next block of DMA (if one is queued)
X */
Xint sbintr(vect)
Xint vect;
X{
X    int old_pri;
X    unsigned char tmp_byte;
X
X    /* ASSUME CRITICAL PRIORITY */
X    old_pri = spl6();
X
X    /* check for validity of interrupt */
X    if (! sbtab[0].b_active)
X    {   
X        cmn_err(CE_CONT, "sbintr(): spurious interrupt\n");
X        splx(old_pri);
X        return(0);
X    }
X
X    /* aknowledge interrupt */
X    tmp_byte = inb(DSP_RDAVAIL);
X
X    /* acknowledge as done to waiting thread and remove buf from queue */ 
X    sbtab[0].b_actf->b_resid = 0;
X    iodone(sbtab[0].b_actf);
X    sbtab[0].b_actf = sbtab[0].b_actf->av_forw;
X
X    /* mark device as inactive and service rest of queue (if not empty) */
X    sbtab[0].b_active = FALSE;
X    if (sbtab[0].b_actf != NULL)
X        sb_iostart();
X
X    /* end of critical section */
X    splx(old_pri);
X
X    return(0);
X}
END_OF_FILE
  if test 23791 -ne `wc -c <'./sb.c'`; then
    echo shar: \"'./sb.c'\" unpacked with wrong size!
  fi
  # end of './sb.c'
fi
if test -f './sb.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./sb.h'\"
else
  echo shar: Extracting \"'./sb.h'\" \(2511 characters\)
  sed "s/^X//" >'./sb.h' <<'END_OF_FILE'
X/*
X * Copyrighted as an unpublished work.
X * (c) Copyright 1991 Brian Smith
X * All rights reserved.
X *
X * Read the LICENSE file for details on distribution and use.
X *
X */
X
X#ifndef NBPP
X#include <sys/immu.h>
X#endif
X
X#if !defined(TRUE) || !defined(FALSE)
X#define FALSE 0
X#define TRUE  1
X#endif
X
X/* minor numbers */
X#define SB_CMS_NUM  0
X#define SB_FM_NUM   1
X#define SB_DSP_NUM  2
X
X/* These are hard wired for speed */
X#define SB_DMA_CHAN 1
X#define SB_IO_PORT 0x220
X
X/* C/MS (not supported) */
X#define CMS_DATA1   (SB_IO_PORT + 0x00)
X#define CMS_REG1    (SB_IO_PORT + 0x01)
X#define CMS_DATA2   (SB_IO_PORT + 0x02)
X#define CMS_REG2    (SB_IO_PORT + 0x03)
X
X/* FM Chips */
X#define FM_SELECT   (SB_IO_PORT + 0x08)
X#define FM_REG      (SB_IO_PORT + 0x09)
X#define MAX_FM_NOTES 9
X
X/* DSP (DAC and ADC) Chip(s) */
X#define DSP_RESET   (SB_IO_PORT + 0x06)
X#define DSP_RDDATA  (SB_IO_PORT + 0x0A)
X#define DSP_WRDATA  (SB_IO_PORT + 0x0C)
X#define DSP_COMMAND (SB_IO_PORT + 0x0C)
X#define DSP_STATUS  (SB_IO_PORT + 0x0C)
X#define DSP_RDAVAIL (SB_IO_PORT + 0x0E)
X
X/* status bytes (can be OR'ed) */
X#define NOT_OPEN    0
X#define OPEN_READ   1
X#define OPEN_WRITE  2
X
X/* compression types */
X#define ADCPM_8     0
X#define ADCPM_4     1
X#define ADCPM_2_6   2
X#define ADCPM_2     3
X
X/* ioctl numbers for DSP */
X#define DSP_IOCTL_RESET 00
X#define DSP_IOCTL_SPEED 01
X#define DSP_IOCTL_VOICE 02
X
X/* ioctl numbers for FM */
X#define FM_IOCTL_RESET      00
X#define FM_IOCTL_NOTE_ON    01
X#define FM_IOCTL_NOTE_OFF   02
X#define FM_IOCTL_SET_VOICE  03
X#define FM_IOCTL_SET_OPCELL 04
X#define FM_IOCTL_SET_RHYTHM 05
X
X/* struct for setting a note/voice/key on */
Xtypedef int sb_fm_note;
X#define note_num(X) (((unsigned char *)&X)[0])
X#define fnum_low(X) (((unsigned char *)&X)[1])
X#define keyon_blk_fnum(X) (((unsigned char *)&X)[2])
X
Xtypedef struct {
X    unsigned char   voice_num;
X    unsigned char   data[16];
X} sb_fm_character;
X
X#ifdef INKERNEL
Xstruct sb_stat_type {
X    char unsigned   cms_open;           /* whether in read/write */
X    char unsigned   cms_waiting;        /* number of procs waiting to open */
X    char unsigned   fm_open;            /* whether in read/write */
X    char unsigned   fm_waiting;         /* number of procs waiting to open */
X    char unsigned   dsp_open;           /* whether in read/write */
X    unsigned int    dsp_speed;          /* sample read/write HZ */
X    char unsigned   dsp_compression;    /* compression protocol */
X};
X
Xextern struct sb_stat_type sb_status;
Xextern struct iobuf sbtab[3];
X
X#endif
END_OF_FILE
  if test 2511 -ne `wc -c <'./sb.h'`; then
    echo shar: \"'./sb.h'\" unpacked with wrong size!
  fi
  # end of './sb.h'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.