Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!sdl!dan From: dan@sdl.scs.com (Dan Adler) Newsgroups: comp.music Subject: Re: Perfect Pitch+ IBMPC<--->MIDI Summary: a sample program Keywords: Ear training, MIDI NeXT Message-ID: <578@sdl.scs.com> Date: 5 Jun 90 15:22:29 GMT References: <11177@sdcc6.ucsd.edu> <3936@milton.acs.washington.edu> Organization: Mentor Graphics Corp. - Silicon Design Division Lines: 1191 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 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 X#include 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 X#include X#include X#include 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 {}*",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 X#include 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 X#include X#include X#include 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 X#include X#include X#include 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