1 /* C Source File: ADLIB *****************************************************
\r
5 Last Amended: 27th March, 1993
\r
7 Description: Low-level interface to the Adlib (or compatible)
\r
8 FM sound card. All information gleaned from
\r
9 Jeffrey S. Lee's "Programming the Adlib/Sound
\r
10 Blaster FM Music Chips". See Lee's document for
\r
11 further information.
\r
12 Compiled succesfully under Turbo C, Borland C++,
\r
13 and Microsoft Quick C (all latest versions).
\r
15 ****************************************************************************/
\r
16 #include "src\lib\opl2.h"
\r
21 /* Function: WriteFM ********************************************************
\r
23 * Parameters: reg - which FM register to write to.
\r
24 * value - value to write.
\r
26 * Description: writes a value to the specified register and
\r
27 * waits for the "official" recommended periods.
\r
30 void WriteFM(int reg, int value){
\r
33 outp(ADLIB_FM_ADDRESS, (byte)reg); /* set up the register */
\r
34 for (i = 0; i < 6; i++) inp(ADLIB_FM_ADDRESS); /* wait 12 cycles */
\r
35 outp(ADLIB_FM_DATA, (byte)value); /* write out the value */
\r
36 for(i = 0; i < 35; i++) inp(ADLIB_FM_ADDRESS); /* wait 84 cycles */
\r
37 } /* End of WriteFM */
\r
39 /* Function: ReadFM *********************************************************
\r
41 * Returns: the value in the status register.
\r
43 * Description: return a value in the status register.
\r
47 return(inp(ADLIB_FM_ADDRESS));
\r
48 } /* End of ReadFM */
\r
50 /* Function: AdlibExists ****************************************************
\r
52 * Returns: 1 (true) if an Adlib compatible sound card
\r
53 * is present, else 0 (false).
\r
55 * Description: determines whether an Adlib (or compatible)
\r
56 * sound card is present.
\r
59 int AdlibExists(void){
\r
62 WriteFM(0x04, 0x60); /* reset both timers */
\r
63 WriteFM(0x04, 0x80); /* enable timer interrupts */
\r
64 stat1 = ReadFM(); /* read status register */
\r
65 WriteFM(0x02, 0xFF);
\r
66 WriteFM(0x04, 0x21); /* start timer 1 */
\r
67 // wait(80); /* could do something useful*/
\r
68 stat2 = ReadFM(); /* read status register */
\r
69 WriteFM(0x04, 0x60); /* reset both timers */
\r
70 WriteFM(0x04, 0x80); /* enable timer interrupts */
\r
72 if(((stat1 & 0xE0) == 0x00) && ((stat2 & 0xE0) == 0xC0)) return(1);
\r
74 } /* End of AdlibExists */
\r
76 /* Function: FMResest *******************************************************
\r
78 * Description: quick and dirty sound card reset (zeros all
\r
82 void FMReset(void/*int percusiveMode*/){
\r
85 /* zero all registers */
\r
86 for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) WriteFM(i, 0);
\r
88 /* allow FM chips to control the waveform of each operator */
\r
89 WriteFM(0x01, 0x20);
\r
91 /* set rhythm enabled (6 melodic voices, 5 percussive) */
\r
92 WriteFM(0xBD, 0x20);
\r
94 //FMSetPercusiveMode(percusiveMode);
\r
95 } /* End of FMReset */
\r
97 void FMSetPercusiveMode(int state){
\r
99 WriteFM(0xBD, 0x20);
\r
100 currentBDContents = 0x20;
\r
101 percussiveMode = 1;
\r
102 voiceModulator[7] = 16;
\r
103 voiceModulator[8] = 14;
\r
104 // we have to set the freq of voice 7 & 8 for the white noise gen.
\r
105 // these frequency choices could certainly be better
\r
106 WriteFM(0xa7, 1844 & 0xff);
\r
107 WriteFM(0xb7, 1844 >> 8);
\r
108 WriteFM(0xa8, 3764 & 0xff);
\r
109 WriteFM(0xb8, 3764 >> 8);
\r
112 percussiveMode = 0;
\r
113 currentBDContents = 0;
\r
114 voiceModulator[7] = 13;
\r
115 voiceModulator[8] = 14;
\r
119 /* Function: FMKeyOff *******************************************************
\r
121 * Parameters: voice - which voice to turn off.
\r
123 * Description: turns off the specified voice.
\r
126 void FMKeyOff(int voice){
\r
129 /* turn voice off */
\r
130 regNum = 0xB0 + voice % NUMVOICE;
\r
131 WriteFM(regNum, 0x0E);
\r
132 } /* End of FMKeyOff */
\r
134 /* Function: FMKeyOn *******************************************************
\r
136 * Parameters: voice - which voice to turn on.
\r
137 * freq - its frequency (note).
\r
138 * octave - its octave.
\r
140 * Description: turns on a voice of specfied frequency and
\r
144 void FMKeyOn(int voice, int freq, int octave){
\r
147 regNum = 0xA0 + voice % NUMVOICE;
\r
148 WriteFM(regNum, freq & 0xff);
\r
149 regNum = 0xB0 + voice % NUMVOICE;
\r
150 tmp = (freq >> 8) | (octave << 2) | 0x20;
151 WriteFM(regNum, tmp);
\r
152 } /* End of FMKeyOn */
\r
154 /* Function: FMVoiceVolume **************************************************
\r
156 * Parameters: voice - which voice to set volume of
\r
157 * vol - new volume value (experiment).
\r
159 * Description: sets the volume of a voice to the specified
\r
160 * value in the range (0-63)?
\r
163 void FMVoiceVolume(int voice, int vol){
\r
166 regNum = 0x40 + voice % NUMVOICE;
\r
167 WriteFM(regNum, vol);
\r
168 } /* End of FMVoiceVolume */
\r
170 /* Function: FMSetVoice *****************************************************
\r
172 * Parameters: voiceNum - which voice to set.
\r
173 * ins - instrument to set voice.
\r
175 * Description: sets the instrument of a voice.
\r
178 void FMSetVoice(int voiceNum, FMInstrument *ins){
\r
179 int opCellNum, cellOffset;
\r
181 voiceNum %= NUMVOICE;
\r
182 cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);
\r
184 /* set sound characteristic */
\r
185 opCellNum = 0x20 + (char)cellOffset;
\r
186 WriteFM(opCellNum, ins->SoundCharacteristic[0]);
\r
188 WriteFM(opCellNum, ins->SoundCharacteristic[1]);
\r
190 /* set level/output */
\r
191 opCellNum = 0x40 + (char)cellOffset;
\r
192 WriteFM(opCellNum, ins->Level[0]);
\r
194 WriteFM(opCellNum, ins->Level[1]);
\r
196 /* set Attack/Decay */
\r
197 opCellNum = 0x60 + (char)cellOffset;
\r
198 WriteFM(opCellNum, ins->AttackDecay[0]);
\r
200 WriteFM(opCellNum, ins->AttackDecay[1]);
\r
202 /* set Sustain/Release */
\r
203 opCellNum = 0x80 + (char)cellOffset;
\r
204 WriteFM(opCellNum, ins->SustainRelease[0]);
\r
206 WriteFM(opCellNum, ins->SustainRelease[1]);
\r
208 /* set Wave Select */
\r
209 opCellNum = 0xE0 + (char)cellOffset;
\r
210 WriteFM(opCellNum, ins->WaveSelect[0]);
\r
212 WriteFM(opCellNum, ins->WaveSelect[1]);
\r
214 /* set Feedback/Selectivity */
\r
215 opCellNum = (byte)0xC0 + (byte)voiceNum;
\r
216 WriteFM(opCellNum, ins->Feedback);
\r
217 } /* End of FMSetVoice */
\r
219 /* Function: LoadSBI ********************************************************
\r
221 * Parameters: fileName - name of .SBI file.
\r
222 * ins - variable to place data in.
\r
224 * Description: loads a .SBI into the instrument structure.
\r
227 //int LoadSBI(char fileName[], FMInstrument *ins){
\r
230 // size_t structSize = sizeof(FMInstrument);
\r
232 // if ((fp = fopen(fileName, "rb")) == NULL) return (0);
\r
234 // /* skip the header - or do we? */
\r
235 // for (i = 0; i < 36; i++) fgetc(fp);
\r
237 // /* read the data */
\r
238 // fread(ins, structSize, 1, fp);
\r
242 //} /* End of LoadSBI */
\r
244 unsigned short Notes[] = {
\r
247 17218 , /* C # ( D b ) */
\r
249 15340 , /* D # ( E b ) */
\r
250 14479 , /* E ( F b ) */
\r
251 13666 , /* F ( E # ) */
\r
252 12899 , /* F # ( G b ) */
\r
254 11492 , /* G # ( A b ) */
\r
256 10238 , /* A # ( B b ) */
\r
257 9664 , /* B ( C b ) */
\r
262 /* test of the routines */
\r
264 enum SCALE test[] = { D4, E4, F4, G4, A4, B4, C4 };
\r
265 // enum SCALE oct4[] = { 493.88, 466.16, 440, 415.3, 392, 369.99, 349.23, 329.63, 311.13, 293.66, 277.18, 261.63 };
\r
266 static FMInstrument testInst =
\r
268 0x00, 0x01, /* modulator frequency multiple... 0x20 */
\r
269 0x00, 0x00, /* modulator frequency level... 0x40 */
\r
270 0xF0, 0xF0, /* modulator attack/decay... 0x60 */
\r
271 0x73, 0x73, /* modulator sustain/release... 0x80 */
\r
272 0x03, 0x00, /* output waveform distortion 0xE0 */
\r
273 0x36, /* feedback algorithm and strength 0xC0 */
\r
276 printf("Now testing tune....\n");
\r
277 // printf("just hit any key 7 times.\n");
\r
279 FMSetVoice(0, &testInst);
281 // WriteFM(0xB0, 0x09);
282 // WriteFM(0xB3, 0x07);
284 for(i = 0; i < 7; i++){
\r
285 FMKeyOn(0, test[i], 4);
297 enum SCALE test[] = { D4, E4, F4, G4, A4, B4, C4 };
\r
298 //FMKeyOn(0, test[sq], 4);