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