[comp.music] Perfect Pitch+ IBMPC<--->MIDI

curry@milton.acs.washington.edu (Susan Curry) (06/05/90)

     Again let me thank everyone who wrote to me shared
their thoughts on perfect pitch and or computerized ear
training.  Since so many people wrote me requesting a
summary I'll bring you up to date.
 
     First a quick overview:
 
     I've often had difficulty finding other musicians who
were willing to practice ear training with me.  I'd read an
instruction booklet published by Mr. David Burge, on
acquiring perfect pitch, and was actually doing the
recommended exercises but was wanting for a regular practice
partner.  It occurred to me that it should be possible to
rig up some sort of ear training system based on an IBM PC
and a MIDI device.  The software, which I would write, would
have some low level support to control the MIDI device.
That being done, it seemed easy implement exercises from Mr.
Burge's book, or any other source, as subroutines.  One
additional constraint is that since I'd be using computers
at work I'd need a serial MIDI interface, as opposed to a
MIDI card.
 
     I telephoned KEY ELECTRONICS (800-533-MIDI) and asked
for their brochure.  They have a serial port interface for
$119.95, not including cables, which is cheap I'm told.
They also have a software toolkit to control the MIDI
interface for $39.95.  I'm not sure how necessary this
toolkit is.  There are lots toolkits for serial
communications and I don't see why driving the serial port
to control the modem is fundamentally different from driving
the serial port to control the MIDI port.  If anyone out
there has personal experience and or strong opinions on this
subject let me know.
 
     There are NeXT computers in a computer services
building on campus.  The NeXT already has an impressive
sound device built in, which is a consideration since I
don't currently own a MIDI device.  Writing for the NeXT
would involve a steeper learning curve as I am a novice in C
and UNIX.  Someone I spoke with briefly at the UW School of
Music encouraged me to learn LISP (!) in order to take
advantage of a music toolkit for the NeXT.
 
     What other people had to say:
 
     dan@mlp.scs.com (Dan Adler) writes:
>Since I work on a SUN sparc-station which has (not so good
>but acceptable) sound producing capabilities, I've
>programmed a whole bunch of stuff on it as exercises which
>I listen to by earphones at work. This includes the perfect
>pitch ones plus the ones from Burge's relative pitch
>course, which I very highly reccomend. Unfortunately, if
>you don't have a SUN workstation, you can't use my stuff.
 
     Yes, unfortunately.
     He continues:
>    By the way, I thought that for a practice system it may
>be best to use something closer to pure sine waves, so that
>you don't end up listening for the `wrong' quality of a
>note (i.e. to its timbre - which results from the overtone
>compositions as opposed to the frequency).
 
     That's interesting, is it more confusing to listen to a
"natural tone" which contains pronounced overtones, that may
influence one's perception of absolute pitch, or is it more
confusing to listen to a sine wave, which is acoustically
pure but is foreign to the musical ear.  When I call the
sine wave a "foreign" sound I mean people may have some
distracting emotional reaction to the sine wave if they
don't associate that timber with music and a music making.
The situation is further complicated by the fact that many
"natural" instruments don't have a perfectly uniform timber
throughout their range.  David Burge encourages people to
use the instrument they play as the sound source for the
exercises, until they have a certain fluency, and then try
to identify tones sounding on a less familiar instrument.
 
     Commenting on my original posting:
>The gist of this color hearing is that each note has, using
>my own words, has a timber that is independent of voices or
>instruments.  The notes F# and E flat have the most
>pronounced timber, F# being bright, E flat being mellow or
>dull.  The exercises start out using just these two notes
 
     Justin Shuttleworth responds:
>I have some questions.  If the you're using the well-
>tempered scale, then (by definition) the pitch ratio from
>semitone to adjacent semitone is is exactly the same, no
>matter where you are in the scale (e.g. ascending ratio =
>12th root of 2).  If you use a synthesiser which produces
>the same timbral content for any pitch, I don't see how
>different absolute pitches can have different "absolute"
>timbres.....(stuff deleted).....
 
>Where does the absolute pitch detection come from?  I have
>to say at this point that I have perfect pitch, and have
>tried to explain why I say things like F# is bright.
 
     I understand that those with perfect pitch can be very
sensitive to intonation, but as far as whether or not
perfect pitch is teachable, I'm not convinced scale
temperament is relevant at all.  Certainly since the
tempered scale is what we in this century listen to, I fail
to see how it could be a confounder.  Perhaps I
misunderstood his point entirely.
 
     He continues:
>A common statistic given is that 1 in 10 people have
>perfect pitch (altough not all of them have musical
>training and hence are not aware of their ability).  What's
>different about these people?
 
     In my reply to Mr. Justin I forgot to ask about that 1
in 10 statistic.  Has anybody else heard this?
 
     Don Mullen writes:
Brian,
>    I had the same trouble that you did with the Perfect
>Pitch course - no one willing to put in a little time
>everyday to be my partner.  So I put it on the shelf and
>decided to wait until I got a MIDI set up and some
>programming time.
>    Last week I got a MusicQuest MQX-16S card/Cakewalk Pro
>3.0 combination and the MQX programmer's toolkit.  I
>discovered that I could use CakeWalk's CAL programming
>feature to randomly choose notes and play them
...(stuff deleted)
>    This is easy to to with few notes (I'm just now, as of
>last night, starting out on E-flat and F-sharp), but I will
>probably write some C code to do the more sophisticated
>drills.  I'll let you know how that goes.
 
     My regards  Mr. Mullen, and please *DO* let me know how
it goes!
 
     He continues:
>     Please let me know if you find out any more
>information.  If I had the time, I'd develop a really nice
>MS-Windows based Perfect Pitch and Relative Pitch (did you
>get that course?) program -- maybe I will later this year.
>Someone posted that Burge (sp?) was hiring a programmer
>last year - I wonder what the result will be.....
 
     If the new software is anything like the book it will
probably be theoretically sound, over priced, contain very
little code, and you'll need two computers to run it. ;)
 
     These final postings I received this week and haven't
gotten a chance to follow up on them yet.
 
     Mark Gresham writes:
>I suggest you talk to Dr. <NameDeleted> who is a research
>fellow out there at the University in Seattle.  She
>developed software under an IBM grant (with a programming
>assistant doing the hacking) for teaching Modulo-12 ear
>training/theory.  If you can't reach her via the
>University, you can reach find her under <NameDeleted> in
>the phone book.  So she won't think you're wierd and out of
>left field, use my name.  Then she'll think you're wierder
>and farther out into left field! :-)  However, she teaches
>various and sundry including Dalcroze.  Let me know if you
>are able to reach her.
 
     Thanks for the tip!  I'm sorry I haven't responded to
your letter personally yet.  When I do you can explain to me
what Modulo-12 and Dalcroze are.  BTW I didn't want to quote
your friend's name on the newsgroup until I'd gotten a
chance to speak with her.
 
     Peter Velikonja writes:
>Brian,
>I'm pretty late in responding to your post regarding
>perfect pitch but I don't read the news too often (since it
>sucks up all my time). Anyway, I have been doing some work
>in this area, and I'm curious about replies you may have
>gotten.
          (stuff deleted)
>    I'm working on a program which attempts to teach basic
>ear training. It runs on a NeXT machine.  Right now it just
>plays simple melodies, and you click the appropriate notes
>on screen with the mouse.  If you like I can send it to
>you.  I should have a Mac version of it in a few months.
>The program tries to come up with melodies that are
>appropriate to your level of expertise, and if you do well
>the melodies get harder.
          (stuff deleted)
>This is probably all old news to you, but if not let me
>know and I'll send what I have.  As I say, I'm interested
>in any references you have collected.
 
     I was very eager to try out his program, and to pick
his brain further.
 
     In his next letter:
>You can probably FTP my ET program from any NeXT computer,
>assuming you have an account on one that is on a network.
>This might be a problem for you if you don't get around
>much in UNIX (I speak from personal experience).  Get
>someone in CS to help you,
 
     He was right about it being a problem.  Actually I
don't think the public access NeXT machines here are on a
NeXT network.  I think people have to telenet to their
mainframe account to send mail, etc.  As I said before I'm
new at UNIX and I haven't had time to check it out further.
 
     He continues:
>Regarding hardware, you have a number of options, and they
>vary  according to which computer you want to use.  To use
>MIDI with an IBM or Amiga you can get the CMU MIDI Toolkit,
>which is available here for about $20 (pays for xeroxing
>the manual and for the diskettes).  If you send mail to
>Roger Dannenberg (rbd@spice.cmu.edu) he can give you better
>information about what it does and how to get it.  It
>essentially lets you trap notes from the keyboard and also
>to play notes from within your C program, which is what I
>guess you want to do (except maybe not in C, hmm...).  Ann
>Blombach is using something with the Mac in Pascal, which
>may be more what you want.  She describes it in an article:
>Tools for Macintosh Music Courseware Development: Hewlett's
>Representational System and Structured Programming.
>Journal of Computer Based Instruction, v. 16 No. 2:50
>Spring 1989.
          (stuff deleted)
 
>I'll send you the current version of an article I wrote
>which outlies the work I have been doing, and has a list of
>references you are probably familiar with anyway.  That
>will be in a mail message after this one.
 
     So now I have quite a number of leads I need to follow
up on.  They all sound very promising.  Mr. Velikonja thank
you again for your help!
 
     The article he sent is also very informative.  He
discusses his software in terms of traditional problems in
teaching ear training, user interface, algorithms for
adjusting the difficulty level of the exercises in response
the user's success rates, and follows up with a hefty
bibliography.
 
     Pending Mr Velikon's approval I'll post the address he
mentions for his software.  I'll also forward to the list,
or to interested parties his marvelously written article,
again pending his approval.
 
     Thanks again to all of you who were kind enough to
write.  I'll let you know how things progress.  And again,
if any of you are interested in discussing ear training or
ear training systems post to the group, or by all means feel
free to write to  me personally.
 
     Brian R. Russell
     curry@milton.u.washington.edu
     Seattle, WA
: 

dan@sdl.scs.com (Dan Adler) (06/05/90)

Since there was so much talk about creating a system for perfect
pitch ear-training, I just wanted to make the point that 
it's a VERY simple matter. 
	Sure, you can get fancy, but for the basic 
functionality all you need is a computer which has SOME way of
sounding frequencies. Assume you can figure out how to play
a note on your computer given its frequency, then you can use
the following piece of C code as a basis. This happens to be for
a sparc-station using the SST library published on the net.
	the program is called "testme" and it works as follows:
you pass as parameters the set of notes you want to be quizzed on:
	testme eb f#
this will test you on these two notes. the format of notes is
	[abcdefg]{#}{b}
you can't use double flats or double sharps, and the program 
doesn't do any sanity checks (hey, it's for my private use...).
On the sparcstation it sets the mode to earphone mode if you want
it on the speaker set mode to 0.

Here is the section from the Makefile:

testme: note.o testme.o libsst.o play_tone.o volume.o
	cc -o testme testme.o libsst.o note.o play_tone.o volume.o -lm

and here are the files:
(some of them are not used but I don't have the patience to sift
through them and clean it up...)
	
	Save the part above this in README then save the part from
the first line with a "#" in a file and run sh <filename> to 
unbundle the files.

	If you have anything other than a sparcstation you need to
change the play_tone() routine to what ever works on your system.
If there is interest I can post a very similar program which is
a set of exercises for David Burge's Relative pitch course.
This basically takes a list of intervals like p5, M3, m6 etc. and
exercises you on them. By the way, if anyone has ANY problems with
relative pitch at all - I can testify that David Burge's course 
will cure them all. It's $200, but it's not just a booklet, it's
25 90-minute cassettes and it's well worth it if you have any
weaknesses in your relative hearing.

#!/bin/sh
if [ -r /unix -o -r /vmunix ] ; then
  PATH=/bin:/usr/bin:/usr/ucb; export PATH
fi 
echo x - note.c
sed 's/^X//' >note.c <<'-END-of-note.c-'
X#include <stdio.h>
X#include <math.h>
Xdouble	scale[13];
Xchar 	*scale_name[13] = {"A", "Bb", "B", "C", "Db", "D", "Eb",
X							"E", "F", "Gb", "G", "Ab"};
X
Xdouble	twelfth_root;
X
X/**************************************************/
X
Xinit_scale()
X
X{
Xint	i;
X
Xtwelfth_root = pow(2.0,1.0/12.0);
X
Xscale[0] = 220.0;
Xfor (i = 0; i < 11; ++i)
X	scale[i+1] = (scale[i] * twelfth_root);
X
Xscale[12] = 0.0;
X}
X
Xflat(note)
X	char	*note;
X
X{
Xif (1 < strlen(note) && 'b' == note[1])
X	return(1);
Xelse
X	return(0);
X}
X
Xdouble
Xscale_note(i)
X	int i;
X{
X	if (i >= 13) return(0.0);
X	else return(scale[i]);
X}
X
X 
X/**************************************************/
X
Xsharp(note)
X	char	*note;
X
X{
Xif (1 < strlen(note) && '#' == note[1])
X	return(1);
Xelse
X	return(0);
X}
X
X/**************************************************/
X
Xstatic int init=0;
X
Xdouble freq(note,octave)
Xchar *note;
Xchar *octave;
X{
Xint	index;
Xdouble	frequency;	/* Frequency of note. */
Xint oct = atoi(octave);
X
Xif (!init) {init_scale(); init=1;}
X	switch (note[0])
X		{
X		case 'a':	index = 0;
X				break;
X		case 'b':	index = 2;
X				break;
X		case 'c':	index = 3;
X				break;
X		case 'd':	index = 5;
X				break;
X		case 'e':	index = 7;
X				break;
X		case 'f':	index = 8;
X				break;
X		case 'g':	index = 10;
X				break;
X		default:	index = 12;
X				break;
X		}
X
X		if (flat(note))
X			{
X			if ('a' == note[0])
X				{
X				index = 11;
X				}
X			else
X				--index;
X			}
X		else if (sharp(note))
X			++index;
X
X		frequency = scale[index];
X		if (oct) frequency *= (oct*2);
X		return(frequency);
X}
X
X#define ABS(x) ((x) < 0.0 ? -(x) : (x))
X
Xchar *
Xnote_name(freq)
X	double freq;
X{
X	int i;
X	while (freq >= 440.0) freq = (freq/2.0);
X	while (freq < 220.0) freq = (freq * 2.0);
X	for(i=0;i<13;i++) 
X		if (ABS(freq - scale[i]) <= 4) return(scale_name[i]);
X	return("?");
X}
X
-END-of-note.c-
echo x - testme.c
sed 's/^X//' >testme.c <<'-END-of-testme.c-'
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <sys/time.h>
X#include "libsst.h"
X
X#define nrnd(x) ((unsigned int)random() % (x))
X
Xint interrupt_catch();
Xint breakit;
X
Xextern int optind;
Xextern char *optarg;
Xextern double freq(); /* in note.c */
Xextern play_tone(); /* in play_tone.c */
Xstatic int breakit=0;
Xstatic int sst_fd;
Xstatic int played=0;
Xstatic int errors=0;
X
Xmain( argc, argv )
Xint argc;
Xchar *argv[];
X{
Xdouble thz;
Xint dtmf, tone, ringer;
Xint duration, mode, c;
Xint play_lev;
Xchar oct[16];
Xint which, octave;
Xchar guess[64];
Xchar cont;
Xint last=0, last_oct=0;
X
Xplay_lev = 0;
Xmode = O_EARPHONE;
X
Xsrandom((int) time((long *)0));
Xif (argc < 2)  {
X	printf("usage: %s <note_name> {<note_name>}*",argv[0]);
X	exit(1);
X}
Xsignal(SIGINT, interrupt_catch);
Xsst_fd = sst_fopen(play_lev, 0, mode);
X
Xprintf("ready ?\n");
Xwhile(!breakit) {
X	cont = getchar();
X	if (cont == 'q') break;
X
X	/* don't want two in a row same picth and same octave */
X	do {
X		which = nrnd(argc);
X		octave = nrnd(3);
X		if (!which) which++; /* can't use 0 */
X	} while ((which == last) && (octave == last_oct));
X	last = which; 
X	last_oct = octave;
X	
X	sprintf(oct,"%d",octave);
X	thz = freq(argv[which],oct);
X	duration = 1000000;
Xsame:
X	played++;
X   	play_tone( sst_fd, thz);
X	printf("what note?\n");
X	scanf("%s",guess);
X	if (!strcmp(guess,"x")) goto same;
X	if (!strncmp(guess,argv[which],1))
X		printf("Correct ! frequency = %.1f\n", thz);
X	else { 	
X		printf("Wrong. note is %s, frequency = %.1f\n",
X				 argv[which],thz);
X		errors++;
X		printf("again ?\n");
X		cont = getchar();
X		if (cont == 'q') break;
X		if (cont == 'n') break;
X		goto same;
X	}	
X}
Xsst_close( sst_fd );
Xprintf("\n\nYou had %d errors out of %d notes.\n",errors,played);
Xexit( 0 );
X}
X
Xinterrupt_catch()
X{
Xsst_close( sst_fd );
Xprintf("\n\nYou had %d errors out of %d notes.\n",errors,played);
Xexit( 0 );
X}
-END-of-testme.c-
echo x - libsst.c
sed 's/^X//' >libsst.c <<'-END-of-libsst.c-'
X/* libsst.c - SPARC sound tools library
X**
X** Copyright (C) 1989 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X*/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include "libsst.h"
X
X#define AUDBUF 1024
X
Xint
Xsst_open( )
X    {
X    int fd, i, play_level, record_level, gr, ger, gx;
X    struct audio_ioctl ai;
X    char *getenv(), *ep;
X
X    fd = open( "/dev/audio", O_RDWR );
X    if ( fd < 0 )
X	{
X	perror( "sst_open: open /dev/audio" );
X	exit( 1 );
X	}
X
X#if 0
X    /* Shrink audio device's queue size, to cut down time delay. */
X    i = AUDBUF;
X    if ( ioctl( fd, AUDIOSETQSIZE, &i ) < 0 )
X	{
X	perror( "sst_open: SETQSIZE" );
X	exit( 1 );
X	}
X#endif
X
X    /* Set gains.  -10 <= ger <= 18,  -18 <= gr <= 12,  -18 <= gx <= 12. */
X    play_level = 95;
X    record_level = 75;
X    if ( (ep = getenv( "SST_PLAY" )) != NULL )
X	{
X	play_level = atoi( ep );
X	if ( play_level < 0 || play_level > 99 )
X	    {
X	    fprintf( stderr, "sst_open: SST_PLAY must be between 0 and 99\n" );
X	    exit( 1 );
X	    }
X	}
X    if ( (ep = getenv( "SST_RECORD" )) != NULL )
X	{
X	record_level = atoi( ep );
X	if ( record_level < 0 || record_level > 99 )
X	    {
X	    fprintf( stderr, "sst_open: SST_RECORD must be between 0 and 99\n" );
X	    exit( 1 );
X	    }
X	}
X
X    play_level = play_level * 59 / 100 - 28;
X    ger = play_level / 2;
X    gr = play_level - ger;
X    if ( ger < -10 )
X	{
X	ger = -10;
X	gr = play_level - ger;
X	}
X    if ( gr > 12 )
X	{
X	gr = 12;
X	ger = play_level - gr;
X	}
X    gx = record_level * 31 / 100 - 18;
X    sst_set_gr( fd, gr );
X    sst_set_ger( fd, ger );
X    sst_set_gx( fd, gx );
X
X    /*  Initialize the MMR2 register to send the output to either
X    **  the speaker or the earphone jack, depending on SST_EARPHONES.
X    */
X    ai.control = AUDIO_MAP_MMR2;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_open: GETREG MMR2" );
X	exit( 1 );
X	}
X    if ( (ep = getenv( "SST_EARPHONES" )) != NULL )
X	ai.data[0] &= ~AUDIO_MMR2_BITS_LS;
X    else
X	ai.data[0] |= AUDIO_MMR2_BITS_LS;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_open: SETREG MMR2" );
X	exit( 1 );
X	}
X
X    return fd;
X    }
X
Xint
Xsst_fopen( play_level, record_level, flags )
Xint play_level, record_level, flags;
X    {
X    int fd, i, gr, ger, gx;
X    struct audio_ioctl ai;
X    char *getenv(), *ep;
X
X    fd = open( "/dev/audio", O_RDWR );
X    if ( fd < 0 )
X	{
X	perror( "sst_open: open /dev/audio" );
X	exit( 1 );
X	}
X
X    /* Shrink audio device's queue size, to cut down time delay. */
X    i = AUDBUF;
X    if ( ioctl( fd, AUDIOSETQSIZE, &i ) < 0 )
X	{
X	perror( "sst_open: SETQSIZE" );
X	exit( 1 );
X	}
X
X    /* Set gains.  -10 <= ger <= 18,  -18 <= gr <= 12,  -18 <= gx <= 12. */
X    play_level = play_level * 59 / 100 - 28;
X    ger = play_level / 2;
X    gr = play_level - ger;
X    if ( ger < -10 )
X	{
X	ger = -10;
X	gr = play_level - ger;
X	}
X    if ( gr > 12 )
X	{
X	gr = 12;
X	ger = play_level - gr;
X	}
X    gx = record_level * 31 / 100 - 18;
X    sst_set_gr( fd, gr );
X    sst_set_ger( fd, ger );
X    sst_set_gx( fd, gx );
X
X    /*  Initialize the MMR2 register to send the output to either
X    **  the speaker or the earphone jack, depending on SST_EARPHONES.
X    */
X    ai.control = AUDIO_MAP_MMR2;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_open: GETREG MMR2" );
X	exit( 1 );
X	}
X    if (flags & O_EARPHONE)
X	ai.data[0] &= ~AUDIO_MMR2_BITS_LS;
X    else
X	ai.data[0] |= AUDIO_MMR2_BITS_LS;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_open: SETREG MMR2" );
X	exit( 1 );
X	}
X
X    return fd;
X    }
Xvoid
Xsst_close( fd )
Xint fd;
X    {
X    struct audio_ioctl ai;
X
X#if 0
X    ai.control = AUDIO_MAP_MMR1;
X    ai.data[0] = 0;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_close: SETREG MMR1" );
X	exit( 1 );
X	}
X    ai.control = AUDIO_MAP_MMR2;
X    ai.data[0] = 0;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_close: SETREG MMR2" );
X	exit( 1 );
X	}
X#endif
X    close( fd );
X    }
X
X/* These are tables of values to be loaded into various gain registers.
X*/
X
Xstatic unsigned char ger_table[][2] = {
X    0xaa,	0xaa,	/* -10db */
X    0x79,	0xac,
X    0x41,	0x99,
X    0x9c,	0xde,
X    0x74,	0x9c,	/* -6db */
X    0x6a,	0xae,
X    0xab,	0xdf,
X    0x64,	0xab,
X    0x2a,	0xbd,
X    0x5c,	0xce,
X    0x00,	0x99,	/* 0db */
X    0x43,	0xdd,
X    0x52,	0xef,
X    0x55,	0x42,
X    0x31,	0xdd,
X    0x43,	0x1f,
X    0x40,	0xdd,	/* 6db */
X    0x44,	0x0f,
X    0x31,	0x1f,
X    0x10,	0xdd,
X    0x41,	0x0f,
X    0x60,	0x0b,
X    0x42,	0x10,	/* 12db */
X    0x11,	0x0f,
X    0x72,	0x00,
X    0x21,	0x10,
X    0x22,	0x00,
X    0x00,	0x0b,
X    0x00,	0x0f,	/* 18db */
X    };
X
X
Xstatic unsigned char gr_gx_table[][2] = {
X    0x8b,	0x7c,	/* -18db */
X    0x8b,	0x35,
X    0x8b,	0x24,
X    0x91,	0x23,
X    0x91,	0x2a,
X    0x91,	0x3b,
X    0x91,	0xf9,	/* -12db */
X    0x91,	0xb6,
X    0x91,	0xa4,
X    0x92,	0x32,
X    0x92,	0xaa,
X    0x93,	0xb3,
X    0x9f,	0x91,	/* -6db */
X    0x9b,	0xf9,
X    0x9a,	0x4a,
X    0xa2,	0xa2,
X    0xaa,	0xa3,
X    0xbb,	0x52,
X    0x08,	0x08,	/* 0db */
X    0x3d,	0xac,
X    0x25,	0x33,
X    0x21,	0x22,
X    0x12,	0xa2,
X    0x11,	0x3b,
X    0x10,	0xf2,	/* 6db */
X    0x02,	0xca,
X    0x01,	0x5a,
X    0x01,	0x12,
X    0x00,	0x32,
X    0x00,	0x13,
X    0x00,	0x0e,	/* 12db */
X    };
X
Xvoid
Xsst_set_ger( fd, value )
Xint fd, value;
X    {
X    struct audio_ioctl ai;
X
X    if ( ( value < -10 ) || ( value > 18 ) )
X	{
X	fprintf( stderr, "sst_set_ger: GER %d out of range\n", value );
X	return;
X	}
X
X    /*  Add 10 to the value to get the index into the table.  */
X    ai.control = AUDIO_MAP_GER;
X    ai.data[0] = ger_table[value + 10][1];
X    ai.data[1] = ger_table[value + 10][0];
X
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_ger: SETREG GER" );
X	exit( 1 );
X	}
X
X    ai.control = AUDIO_MAP_MMR1;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_ger: GETREG MMR1" );
X	exit( 1 );
X	}
X    ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GER;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_ger: SETREG MMR1" );
X	exit( 1 );
X	}
X    }
X
Xvoid
Xsst_set_gr( fd, value )
Xint fd, value;
X    {
X    struct audio_ioctl ai;
X
X    if ( ( value < -18 ) || ( value > 12 ) )
X	{
X	fprintf( stderr, "sst_set_gr: GR %d out of range\n", value );
X	return;
X	}
X
X    ai.control = AUDIO_MAP_GR;
X    ai.data[0] = gr_gx_table[value + 18][1];
X    ai.data[1] = gr_gx_table[value + 18][0];
X
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gr: SETREG GR" );
X	exit( 1 );
X	}
X
X    ai.control = AUDIO_MAP_MMR1;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gr: GETREG MMR1" );
X	exit( 1 );
X	}
X    ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GR;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gr: SETREG MMR1" );
X	exit( 1 );
X	}
X    }
X
Xvoid
Xsst_set_gx( fd, value )
Xint fd, value;
X    {
X    struct audio_ioctl ai;
X
X    if ( ( value < -18 ) || ( value > 12 ) )
X	{
X	fprintf( stderr, "sst_set_gx: GX %d out of range\n", value );
X	return;
X	}
X
X    /*  We add 18 to get the index into the table, since entry 0 represents
X    *  -18db.
X    */
X    ai.control = AUDIO_MAP_GX;
X    ai.data[0] = gr_gx_table[value + 18][1];
X    ai.data[1] = gr_gx_table[value + 18][0];
X
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gx: SETREG GX" );
X	exit( 1 );
X	}
X
X    ai.control = AUDIO_MAP_MMR1;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gx: GETREG MMR1" );
X	exit( 1 );
X	}
X    ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GX;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
X	{
X	perror( "sst_set_gx: SETREG MMR1" );
X	exit( 1 );
X	}
X    }
X
X#define val(b)	byte_table[b+127]
Xstatic char	byte_table[256];
X
X
X/**************************************************/
X
Xstatic inited=0;
X
Xvalu(x)
X	int x;
X{
X	int level;
X	if (!inited) {
X		init_byte_table();
X		inited = 1;
X	}
X
X	if (x >= 0)
X		level = (int) (0.5 + x);
X	else
X		level = (int) (-0.5 + x);
X	return(val(level));
X}
X
X
X/**************************************************/
X
Xinit_byte_table()
X
X/* The 8 bit integers are some kind of wierd signed format. */
X/* Take regular numbers from -127 to 127 and map them to the wierd ones. */
X
X{
Xregister int	i;
X
Xfor (i = 0; i < 127; ++i)
X	byte_table[i] = (char)(i + 1);
X
Xfor (i = 0; i < 128; ++i)
X	byte_table[i+127] = (char)(255 - i);
X}
X
Xvoid
Xsst_tones( fd, thz, usec )
Xint fd, thz, usec;
X    {
X    struct audio_ioctl ai;
X    int  tval;
X    unsigned char oldmmr2, newmmr2;
X
X    if ( thz == 0 ) tval = 0;
X    else {
X		tval = ( thz * 128 + 63 ) / 2000;
X		printf("sst_tones: %d --> %d\n",thz,tval);
X		if ( ( tval < 1 ) || ( tval > 255 ) ) {
X	    	fprintf(stderr, "sst_tones: thz %d out of range\n", thz );
X	    	return;
X	    }
X	}
X
X    ai.control = AUDIO_MAP_MMR2;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_tones: GETREG MMR2" );
X	exit( 1 );
X	}
X    oldmmr2 = newmmr2 = ai.data[0];
X
X    if (tval) {
X		newmmr2 |= AUDIO_MMR2_BITS_TONE;
X		ai.control = AUDIO_MAP_FTGR;
X		ai.data[0] = tval;
X		ai.data[1] = 0;
X		if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X	    	perror( "sst_tones: SETREG FTGR" );
X	    	exit( 1 );
X	    }
X	}
X
X    ai.control = AUDIO_MAP_MMR2;
X    ai.data[0] = newmmr2;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X		perror( "sst_tones: SETREG MMR2" );
X		exit( 1 );
X	}
X
X    usleep( usec );
X
X    ai.data[0] = oldmmr2;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X		perror( "sst_tones: SETREG MMR2" );
X		exit( 1 );
X	}
X}
X
Xstatic int sss=0;
X
Xvoid
Xsst_all( fd ,thz, usec)
Xint fd, thz, usec;
X    {
X    struct audio_ioctl ai;
X    int  tval;
X    unsigned char oldmmr2, newmmr2;
X
X    {
X		tval = sss++;
X		printf("sst_tones: %d\n",tval);
X		if ( ( tval < 1 ) || ( tval > 255 ) ) {
X	    	fprintf(stderr, "sst_tones: thz %d out of range\n", thz );
X	    	return;
X	    }
X	}
X
X    ai.control = AUDIO_MAP_MMR2;
X    if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
X	{
X	perror( "sst_tones: GETREG MMR2" );
X	exit( 1 );
X	}
X    oldmmr2 = newmmr2 = ai.data[0];
X
X    if (tval) {
X		newmmr2 |= AUDIO_MMR2_BITS_TONE;
X		ai.control = AUDIO_MAP_FTGR;
X		ai.data[0] = tval;
X		ai.data[1] = 0;
X		if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X	    	perror( "sst_tones: SETREG FTGR" );
X	    	exit( 1 );
X	    }
X	}
X
X    ai.control = AUDIO_MAP_MMR2;
X    ai.data[0] = newmmr2;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X		perror( "sst_tones: SETREG MMR2" );
X		exit( 1 );
X	}
X
X    usleep( usec );
X
X    ai.data[0] = oldmmr2;
X    if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 ) {
X		perror( "sst_tones: SETREG MMR2" );
X		exit( 1 );
X	}
X}
X
X
Xvoid
Xsst_dtmf( fd, dial, usecper, usecpause )
Xint fd, usecper, usecpause;
Xchar *dial;
X    {
X    char *cp;
X
X    for ( cp = dial; *cp != '\0'; cp++ )
X	{
X	switch ( *cp )
X	    {
X	    case '1': sst_tones( fd, 703, 1211, 0, 0, usecper ); break;
X	    case '2': sst_tones( fd, 703, 1336, 0, 0, usecper ); break;
X	    case '3': sst_tones( fd, 703, 1492, 0, 0, usecper ); break;
X	    case 'A': sst_tones( fd, 703, 1648, 0, 0, usecper ); break;
X	    case '4': sst_tones( fd, 773, 1211, 0, 0, usecper ); break;
X	    case '5': sst_tones( fd, 773, 1336, 0, 0, usecper ); break;
X	    case '6': sst_tones( fd, 773, 1492, 0, 0, usecper ); break;
X	    case 'B': sst_tones( fd, 773, 1648, 0, 0, usecper ); break;
X	    case '7': sst_tones( fd, 859, 1211, 0, 0, usecper ); break;
X	    case '8': sst_tones( fd, 859, 1336, 0, 0, usecper ); break;
X	    case '9': sst_tones( fd, 859, 1492, 0, 0, usecper ); break;
X	    case 'C': sst_tones( fd, 859, 1648, 0, 0, usecper ); break;
X	    case '*': sst_tones( fd, 945, 1211, 0, 0, usecper ); break;
X	    case '0': sst_tones( fd, 945, 1336, 0, 0, usecper ); break;
X	    case '#': sst_tones( fd, 945, 1492, 0, 0, usecper ); break;
X	    case 'D': sst_tones( fd, 945, 1648, 0, 0, usecper ); break;
X
X	    case ' ': case '-': case '(': case ')': case '+':
X	    continue;	/* ignore */
X
X	    case ',': usleep( usecper ); break;	/* big pause */
X
X	    default:
X	    fprintf( stderr, "sst_dtmf: unknown dialing code '%c'\n", *cp );
X	    }
X	usleep( usecpause );
X	}
X    }
-END-of-libsst.c-
echo x - play_tone.c
sed 's/^X//' >play_tone.c <<'-END-of-play_tone.c-'
X#include <stdio.h>
X#include <math.h>
X#include <sys/ioctl.h>
X#include <sun/audioio.h>
X#include "util.h"
X
X
X#define SAMPLE_RATE	8192.0
X#define MOST_INTENSE	127
X#define BUFLEN 256
Xstatic char	byte_table[BUFLEN];
X
X#define WAVE_TABLE_SIZE 8192
Xstatic double sine[WAVE_TABLE_SIZE];
X#define MAX_Q 256
Xstatic char queue[260];
Xstatic int queue_size=0;
Xstatic double	two_pi;
X#define QUIET -10
X
X
X/**************************************************/
X
Xstatic int local_init=0;
Xplay_tone(fd,freq)
X	int fd;
X	double	freq;
X{
Xint	dur;
X
Xif (!local_init) init(fd);
Xqueue_size=0;
Xtone(fd,freq);
X}
X
X/**************************************************/
X
Xinit(fd)
Xint fd;
X{
Xextern set_volume();
Xinit_sine();
X_init_byte_table();
Xlocal_init=1;
Xset_volume(fd,QUIET);
X}
X
X
X/**************************************************/
X
Xinit_sine()
X
X{
Xdouble	cycle;
Xint	i;
X
Xtwo_pi = 4.0 * asin(1.0);
Xcycle = (double)WAVE_TABLE_SIZE;
X
Xfor (i = 0; i < WAVE_TABLE_SIZE; ++i)
X	{
X	sine[i] = (sin(two_pi * ((double)i) / cycle));
X	}
X}
X
X_init_byte_table()
X
X/* The 8 bit integers are some kind of wierd signed format. */
X/* Take regular numbers from -127 to 127 and map them to the wierd ones. */
X
X{
Xregister int	i;
X
Xfor (i = 0; i < 127; ++i)
X	byte_table[i] = (char)(i + 1);
X
Xbyte_table[127] = (char)0;
X
Xfor (i = 0; i < 128; ++i)
X	byte_table[i+128] = (char)(255 - i);
X}
X
X
X/**************************************************/
X
Xsend_to_dev_audio(fd)
X	int fd;
X{
X	int err;
X	err = write(fd,queue,queue_size);
X	if (err < 0) {
X		fprintf("write returned %d\n",err);
X		exit(1);
X	}
X	queue_size = 0;
X}
X
Xenqueue(x)
X	double x;
X{
X	int level;
X	int err;
X	if (x >= 0)
X		level = (int) (0.5 + (MOST_INTENSE * x));
X	else
X		level = (int) (-0.5 + (MOST_INTENSE * x));
X	queue[queue_size++] = byte_table[level+128];
X	if (queue_size > MAX_Q) {
X		printf("Error too long.\n");
X		exit(1);
X	}
X}
X
Xtone(fd,f)
X	int fd;         /* file descriptor of /dev/audio */
X	double	f;		/* Frequency in cycles/second. */
X{
X	int	dur;		/* Duration in seconds. */
X	register int	i;
X	int	index;
X	double	factor;
X	double sample_rate;
X	int t_loud;		/* Loudness at time t. */
X	int n;
X	int period = 31;
X	int j;
X
X   	do {
X      ioctl (fd, AUDIOWRITEQ, &n);
X   	} while (n);
X	dur = Int(SAMPLE_RATE / 2);
X	sample_rate = (double)SAMPLE_RATE;
X	factor = f / sample_rate;
X	t_loud = 1;
X	for (i = 0; i < dur; ++i) {
X		index = ((int)(0.1 + WAVE_TABLE_SIZE * factor * (double)i))
X					% WAVE_TABLE_SIZE;
X		enqueue(t_loud * sine[index]);
X		if (queue_size >= MAX_Q) send_to_dev_audio(fd);
X	}
X	if (queue_size) send_to_dev_audio(fd);
X	usleep(10);
X}
-END-of-play_tone.c-
echo x - volume.c
sed 's/^X//' >volume.c <<'-END-of-volume.c-'
X#include <stdio.h>
X#include <sys/ioctl.h>
X#include <sbusdev/audioreg.h>
X#include <sun/audioio.h>
X
Xstatic int Fd;
X
Xvoid
Xset_volume(fd,value)
X	int	fd,value;
X{
X	int	gr;
X	int	ger;
X	struct	audio_ioctl	tmp;
X
X	Fd = fd;
X	/* Initialize the three gain registers to 0db. */
X	audio_set_ger(0);
X	audio_set_gr(0);
X	audio_set_gx(0);
X
X	tmp.control = AUDIO_MAP_MMR1;
X	tmp.data[0] = AUDIO_MMR1_BITS_LOAD_GX |
X	    AUDIO_MMR1_BITS_LOAD_GR |
X	    AUDIO_MMR1_BITS_LOAD_GER;
X
X	if (ioctl(Fd,AUDIOSETREG,&tmp) < 0) {
X		printf("Volume out of range (-28->30).\n");
X		exit (1);
X	}
X
X	if (value < -28 || value > 30) {
X		printf("Volume out of range (-28->30).\n");
X		exit (1);
X	}
X
X	ger = value/2;
X	gr = value - ger;
X	if (ger < -10) {
X		ger = -10;
X		gr = value - ger;
X	}
X	if (gr > 12) {
X		gr = 12;
X		ger = value - gr;
X	}
X
X	if ((gr + ger) != value) {
X		fprintf( "Error: gr (%d) + ger (%d) != value (%d)\n",
X		    gr,ger,value);
X	}
X	audio_set_gr(gr);
X	audio_set_ger(ger);
X}
X	/*	These are tables of values to be loaded into various
X		gain registers.
X
X		Note that for the ger entry for -8db, we use the data
X		sheet value for -7.5db.  The data sheet gives values for
X		-8db which are wrong and produce too much gain.
X	*/
X
Xstatic	unsigned char ger_table[][2] = {
X		0xaa,	0xaa,	/* -10db */
X		0x79,	0xac,
X		/*0x41,	0x91,*/
X		0x31,	0x99,	/* -7.5db */
X		0x9c,	0xde,
X		0x74,	0x9c,	/* -6db */
X		0x6a,	0xae,
X		0xab,	0xdf,
X		0x64,	0xab,
X		0x2a,	0xbd,
X		0x5c,	0xce,
X		0x00,	0x99,	/* 0db */
X		0x43,	0xdd,
X		0x52,	0xef,
X		0x55,	0x42,
X		0x31,	0xdd,
X		0x43,	0x1f,
X		0x40,	0xdd,	/* 6db */
X		0x44,	0x0f,
X		0x31,	0x1f,
X		0x10,	0xdd,
X		0x41,	0x0f,
X		0x60,	0x0b,
X		0x42,	0x10,	/* 12db */
X		0x11,	0x0f,
X		0x72,	0x00,
X		0x21,	0x10,
X		0x22,	0x00,
X		0x00,	0x0b,
X		0x00,	0x0f,	/* 18db */
X};
X
X
Xstatic	unsigned char gr_gx_table[][2] = {
X		0x8b,	0x7c,	/* -18db */
X		0x8b,	0x35,
X		0x8b,	0x24,
X		0x91,	0x23,
X		0x91,	0x2a,
X		0x91,	0x3b,
X		0x91,	0xf9,	/* -12db */
X		0x91,	0xb6,
X		0x91,	0xa4,
X		0x92,	0x32,
X		0x92,	0xaa,
X		0x93,	0xb3,
X		0x9f,	0x91,	/* -6db */
X		0x9b,	0xf9,
X		0x9a,	0x4a,
X		0xa2,	0xa2,
X		0xaa,	0xa3,
X		0xbb,	0x52,
X		0x08,	0x08,	/* 0db */
X		0x3d,	0xac,
X		0x25,	0x33,
X		0x21,	0x22,
X		0x12,	0xa2,
X		0x11,	0x3b,
X		0x10,	0xf2,	/* 6db */
X		0x02,	0xca,
X		0x01,	0x5a,
X		0x01,	0x12,
X		0x00,	0x32,
X		0x00,	0x13,
X		0x00,	0x0e,	/* 12db */
X};
X
X
X
Xaudio_set_ger(value)
X	int	value;
X{
X	struct	audio_ioctl	tmp;
X
X	if ((value < -10) || (value > 18)) {
X		fprintf(stderr,
X		    "GER value %d out of range; %d <= GER <=  %d\n",
X		    value,0,18);
X		return;
X	}
X
X	/*  Add 10 to the value to get the index into the table.
X	 */
X	tmp.control = AUDIO_MAP_GER;
X	tmp.data[0] = ger_table[value + 10][1];
X	tmp.data[1] = ger_table[value + 10][0];
X
X	if (ioctl(Fd,AUDIOSETREG,&tmp) < 0) {
X			printf("volume error!.\n");
X			exit(1);
X	}
X}
X
X
Xaudio_set_gr(value)
X	int	value;
X{
X	struct	audio_ioctl	tmp;
X
X	if ((value < -18) || (value > 12)) {
X		fprintf(stderr,
X		    "GR value %d out of range; %d <= GR <=  %d\n",
X		    value,0,12);
X		return;
X	}
X
X	tmp.control = AUDIO_MAP_GR;
X	tmp.data[0] = gr_gx_table[value + 18][1];
X	tmp.data[1] = gr_gx_table[value + 18][0];
X
X	if (ioctl(Fd,AUDIOSETREG,&tmp) < 0) {
X			printf("volume error!.\n");
X			exit(1);
X	}
X}
X
X
Xaudio_set_gx(value)
X	int	value;
X{
X	struct	audio_ioctl	tmp;
X
X	if ((value < -18) || (value > 12)) {
X		fprintf(stderr, 
X		    "GX value %d out of range; %d <= GX <=  %d\n",
X		    value,0,12);
X		return;
X	}
X
X	/*  We add 18 to get the index into the table, since entry 0 represents
X	 *  -18db.
X	 */
X	tmp.control = AUDIO_MAP_GX;
X	tmp.data[0] = gr_gx_table[value + 18][1];
X	tmp.data[1] = gr_gx_table[value + 18][0];
X
X	if (ioctl(Fd,AUDIOSETREG,&tmp) < 0) {
X		printf("volume error!.\n");
X		exit(1);
X	}
X}
X
-END-of-volume.c-
exit

-- 
Dan  Adler                     UUCP: dan@dan.scs.com
Mentor Graphics			       phone: (201) 580-0102 
15 Independence Boulevard
Warren, NJ 07060