From robert.fung@citicorp.com Sun Jun 25 15:04:43 1995
Path: lyra.csx.cam.ac.uk!sunsite.doc.ic.ac.uk!agate!newsxfer.itd.umich.edu!gatech!news.mathworks.com!solaris.cc.vt.edu!spcuna!citicorp.com!maple.cgin.us-md.citicorp.com!rfung
From: robert.fung@citicorp.com (Robert Fung)
Newsgroups: comp.speech
Subject: Whistle Recognition
Date: 25 Jun 1995 11:52:58 GMT
Organization: Citicorp - CGIN
Lines: 498
Message-ID: <3sjimq$h84@spruce.citicorp.com>
NNTP-Posting-Host: maple.cgin.us-md.citicorp.com
X-Newsreader: TIN [version 1.2 PL2]

  This program recognizes whistled notes and replays them
  on a sound blaster card. Whith some enhancement disabled/handicapped
  people might be able to use it to compose music and replay
  the notes as MIDI using sound fonts etc.
  The program was written while trying to recognize vowel formants.


/* Whistle.c by Robert Fung 1995-06
   This demo hears a note you whistle into a mike
   and then plays it back on the
   Sound Blaster using the FM synthsizer.
   Range 100-3000 hz in 100Hz steps
   8 bit DAC
   8000Hz sample rate
   128 sample FT
   default:
	IOport=0x220, IRQ=7, LDMA=1, HDMA=7, Cardtype=1;
   used with SB16 and Compaq 386/25 & 387
   Turbo C 2.0
   Use earphones to reduce feedback; check your volume first!
*/

/*
Robert Fung
70650.134@compuserve.com
This program was written using code in the public domain.
The author/s will not be responsible for any use made of it.
Be sure your BLASTER environment settings match the settings in the code
before using!
*/

/* Sound Blaster record code by
  Lars Otte
  Ringstr.2
  37154 Northeim
  Germany
  Email-address:        drcaos@fhlip.EE.fh-lippe.de
*/

/*
FM Sound Blaster code modified by R.F. from:
freqgen.c
Frequency Generator
Creative Labs Inc (c) 1994
by Mike Cox
*/

/* make sure the MIDI volume is up and on */
/* set IOPORT, IRQ, LDMA, HDMA, CardType below to your settings */

#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <mem.h>
#include <math.h>
#define	TWO_PI	((double)2.0 * M_PI)

int samples;
int Buffer[1024];
double real[129],imag[129];
void    dspwrite ( unsigned char );
unsigned char dspread ( void );
unsigned char far *data;
unsigned char far *aligned;
unsigned char aligned_physical;
float f[60];

/*-----------------------------------------------------------------------*/

void dspwrite ( unsigned char c )
{
    while(inportb(0x022C)&0x80);
    outportb(0x022C,c);
}

void sbinit ( void )
{
    unsigned short x;
    inportb(0x022E);
    outportb(0x0226,0x01);
    inportb(0x0226);
    inportb(0x0226);
    inportb(0x0226);
    inportb(0x0226);
    outportb(0x0226,0x00);
    for(x=0;x<100;x++)
    {
        if(inportb(0x022E)&0x80)
        {
            if(inportb(0x022A)==0xAA) break;
        }
    }
    if(x==100)
    {
        printf("Sound Blaster not found at 0220h\n");
        exit(1);
    }
}


/*------------------------------------------------------------------*/

void sbmalloc ( void )
{
    unsigned long physical;
    data=farmalloc(80000L);
    if(data==NULL)
    {
        printf("Memory Allocation Error\n");
        exit(1);
    }
    physical=((unsigned long)FP_OFF(data))+
       (((unsigned long)FP_SEG(data))<<4);
    physical+=0x0FFFFL;
    physical&=0xF0000L;
    aligned_physical=(physical>>16)&15;
    aligned=MK_FP(((unsigned short)aligned_physical<<12)&0xF000,0);
}

void sbsettc ( unsigned char tc )
/* tc = time constant = 256L - (1000000UL/samples per second)*/
{
    inportb(0x022E);
    dspwrite(0x40);
    dspwrite(tc);
}

void sbrec ( unsigned short len )
/* len = number of bytes to record to
   unsigned char *aligned (<=65000) */
{
    len--;
    outportb(0x0A,0x05);
    outportb(0x0C,0x00);
    outportb(0x0B,0x45);
    outportb(0x02,0);
    outportb(0x02,0);
    outportb(0x83,aligned_physical);
    outportb(0x03,(unsigned char)(len&0xFF));
    outportb(0x03,(unsigned char)((len>>8)&0xFF));
    outportb(0x0A,0x01);
    dspwrite(0x24);
    dspwrite((unsigned char)(len&0xFF));
    dspwrite((unsigned char)((len>>8)&0xFF));
}

unsigned short dmacount ( void )
{
    unsigned short x;
    x=inportb(0x03);
    x|=inportb(0x03)<<8;
    if(x==0xFFFF) inportb(0x022E);
    return(x);
}
/*-------------------------------------------------------------*/

double fft2(double hz) /* SFT */
{
 double creal,cimag,g;
 int x;

	  creal=0;   cimag=0;
	  for (x=0;x<samples;x++)
	  {
	       g=2*M_PI*hz*x/8000;
	       creal=creal+real[x]*(cos(g))/8000;
	       cimag=cimag-real[x]*(sin(g))/8000;
	  }
	  return(2*sqrt(cimag*cimag+creal*creal));
}

/*--------------------------------------------------------------*/

#define KEYON	0x20		/* key-on bit in regs B0-B8*/
#define	FM	8		/* SB (mono) ports (e.g. 228h and 229h)*/
#define PROFM1	0		/* Left OPL-2 on CT1330, Bank 0 on OPL-3*/
#define PROFM2	2		/* Right OPL-2 on CT1330, Bank 1 on OPL-3*/
#define LEFT	0x10		/* Left channel bit for OPL-3*/
#define RIGHT	0x20		/* Right channel bit for OPL-3*/
#define lChannel	0       /* Assign channel 0 to left speaker*/
#define rChannel	1	/* Assign channel 1 to right speaker*/

/*int i;*/

unsigned int block, m, lfreq, rfreq;	/* Needed for frequency calculations*/
long fn;				/* Frequency number*/
unsigned char STEREO = 0;		/* Set to 1 if stereo card*/

/* BLASTER default settings ---------------------------*/
unsigned IOport=0x220, IRQ=7, LDMA=1, HDMA=7, Cardtype=1;
/* Card setting variables------------------------------*/

int RegBase[]=				/* Array of modulator register*/
	{				/* offsets for various channels*/
	0x00,				/* on the FM chip.  Carrier*/
	0x01,				/* registers are always the*/
	0x02,				/* modulator register plus three*/
	0x08,				/* Exs.*/
	0x09,				/* Channel Modulator Carrier*/
	0x0A,				/*    0        20h      23h*/
	0x10,				/*    1        21h      24h*/
	0x11,				/*    2        22h      25h*/
	0x12				/*    3        28h	2Bh*/
	};				/*    4        29h	2Ch*/

void mydelay(unsigned long clocks);
void FMoutput(unsigned port, int reg, int val);
void fm(int reg, int val);
void Profm1(int reg, int val);
void Profm2(int reg, int val);
void CardInit(void);
void StartSound(int freq, int channel, int left_right);
void StopSound(int channel);
void IssueError(void);
void note(unsigned int,unsigned int);



void note(unsigned int lf,unsigned int rf)
{
	CardInit();
	lfreq=lf; rfreq=rf;
	StartSound(lfreq, lChannel, LEFT);	/* Start the sound*/
	StartSound(rfreq, rChannel, RIGHT);

	delay(100);

	StopSound(0);		/* Stop the sound*/
	StopSound(1);

	if ((Cardtype==4) || (Cardtype==6))  /* We need to take card out*/
		Profm2(5,0);	   /*  of OPL-3 mode*/

}

void IssueError(void)		/* Issue error showing usage syntax*/
	{
	printf("Usage:\nFREQGEN /F:nnn[,nnn]\n");
	printf("\n\t/F:l[,r]\tFrequencies for left and right channel\n");
	}


void StartSound(int freq, int channel, int left_right)	/* Start sound*/
	{
	int reg = RegBase[channel];
	block = 1;
	m = 1;


	if (freq<1) freq=1;
	if (freq>17500) freq=17500;

	fn = (long) ((double)freq * (double)1048576 /
	       (1<<block) / (2*m-1) / 50000L);
		  /* calculate function number */

	while (fn > 1023)
		{
		block++;
		if (block>7)
		{
			  block=7;
			  m*=2;
			  if (m>15) m=15;
		}

		fn = (long) ((double)freq * (double)1048576 /
		(1<<block) / (2*m-1) / 50000L);	/* calculate function number*/


		}

	fn = (long) ((double)freq * (double)1048576 /
	       (1<<block) / (2*m-1) / 50000L);	/* calculate function number */

	fm(reg+0x23, 0x21 | (m & 0xF));
	fm(reg+0x20, 0x21 | (m & 0xF));

	fm(0xC0+channel, left_right);

	fm(0xA0+channel, (fn & 0xFF));	    /* Set lower 8 bits of frequency*/

	fm(0xB0+channel, ((fn >> 8) & 0x3)  /* Set octave and upper two*/
		 + (block << 2) | KEYON);   /*   bits of frequency*/


	}

void StopSound(int channel)
	{
	int reg=RegBase[channel], oldval;	/* We want to retrieve*/
	outp( IOport+FM, reg );			/*  previous value and just*/
	mydelay(8);				/*  change the KEYON bit*/
	oldval = inp( IOport+FM );		/*  (bit 5) to avoid a*/
	mydelay(55);				/*  clinking sound when we*/
	fm(0xB0+channel, (oldval & 0xDF) );	/*  turn the channel off*/
	}




void CardInit(void)			/* Initialize card settings*/
	{
	int i,reg;
	lfreq = rfreq = 0;		/* Set starting freq's to 0*/
	block = 4;			/* Set starting octave to 4*/
	m = 1;				/* Set starting multiplier num to 1*/

	fm(1, 0);			/* Initialize card*/
	if ((Cardtype == 4) || (Cardtype == 6))
		{
		Profm2(5, 1);		    /* Set to OPL3 mode*/
		fm(0xC0, LEFT | RIGHT | 1); /* Set left channel parallel connection*/
		fm(0xC1, LEFT | RIGHT | 1); /* Set right channel*/
		STEREO = 1;  	            /* Set to stereo*/
		}
	else
		fm(0xC0, 1);			/* parallel connection*/
	if (Cardtype == 2)
		STEREO = 1;
	fm(8, 0);				/* Set up FM mode*/
	fm(0xbd, 0);				/* Set up FM mode*/

	for (i=0,reg=RegBase[i]; i<9; i++,reg=RegBase[i])
		{
		fm(reg+0x43, 0x0);	/* Max out carrier volume*/
		fm(reg+0x63, 0xff);	/* Set carrier attack & decay*/
		fm(reg+0x83, 0x05);	/* Set carrier sustain & release*/

		fm(reg+0x40, 0x3f);     /* Set modulator volume level*/
		fm(reg+0x60, 0x44);	/* Set modulator attack & decay*/
		fm(reg+0x80, 0x05);	/* Set modulator sustain & release*/
		}
	}

void mydelay(unsigned long clocks)
	{
	unsigned long elapsed=0;
	unsigned int last, next, ncopy, diff;

	outp(0x43, 0);
	last=inp(0x40);
	last=~((inp(0x40)<<8) + last);
	do
		{
		outp(0x43, 0);
		next=inp(0x40);
		ncopy=next=~((inp(0x40)<<8) + next);
		next-=last;
		elapsed+=next;
		last=ncopy;
		}
	while (elapsed<clocks);
	}



void FMoutput(unsigned port, int reg, int val)
	{
	outp(port, reg);
	mydelay(28);		/* delay about 2.3 microseconds*/
	outp(port+1, val);
	mydelay(85);		/* delay about 23 microseconds*/
	}

void fm(int reg, int val)
	{
	FMoutput(IOport+FM, reg, val);
	}

void Profm1(int reg, int val)
	{
	FMoutput(IOport+PROFM1, reg, val);
	}

void Profm2(int reg, int val)
	{
	FMoutput(IOport+PROFM2, reg, val);
	}



void cool()
{
 int i,k,tone,max;
 double m=0;
	/* Lars code */
	samples=128;
	sbrec(8000);
	while(dmacount()!=0xFFFF);
	for(i=999; i<7999; i++) m=m+ *(aligned+i);
	m=m/7000;    /* silence calibration */
    nochmal:
	for(i=0; i<samples; i++)
	{
	  /* imag[i]=0.0; */
	  real[i]= *(aligned+i)-m ;
	}
	sbrec(129);  /* record 129 bytes */


	max=0; /* get the maximum amplitude from the spectrum */
	for (i=0; i<30; i+=2)  /* check 30 frequencies only */
	{
	  f[i] = fft2(i*100);
	  if (f[i] > f[max]) max=i;
	}


	   printf("%d\n",100*max);
	   note(max*100,0);  /* play the note heard on one channel */
    while(dmacount()!=0xFFFF);
    if(!kbhit()) goto nochmal;

}

void main()
{
  long ra=8000, ca;       /*samplerate=8kHz fg=4kHz*/
  clrscr();

  sbinit();
  sbmalloc();
  ca= 256L - ( 1000000L / ra);
  sbsettc(ca);
  puts("Whistle notes into your mic.");
  puts("if the frequency matches one of");
  puts("the recognized notes it will replay the note");
  cool();

  getch();
}

/* Piano scale as frequencies
A3 26.667
B3 30
C3 32
D3 36
E3 40
F3 42.667
G3 48
A2 53.333
B2 60
C2 64
D2 72
E2 80
F2 85.333
G2 96
A1 106.667
B1 120
C1 128
D1 144
E1 160
F1 170.667
G1 192
A0 213.333
B0 240
C0 256
D0 288
E0 320
F0 341.333
G0 384
A3 426.667
B3 480
C3 512
D3 576
E3 640
F3 682.667
G3 768
A4 853.333
B4 960
C4 1024
D4 1152
E4 1280
F4 1365.333
G4 1536
A5 170.667
B5 1920
C5 2048
D5 2304
E5 2560
F5 2730.667
G5 3072
A6 3413.333
B6 3840
C6 4096
D6 4608
E6 5120
F6 5461.333
G6 6144
A7 6826.667
B7 7680
C7 8192
*/



