|
|
1.1 root 1: /*
2: * Copyright (c) 1992 Microsoft Corporation
3: */
4:
5:
6: /*
7: * definition of interface functions to the adlib midi device type.
8: *
9: * These functions are called from midi.c when the kernel driver
10: * has decreed that this is an adlib-compatible device.
11: *
12: * Geraint Davies, Dec 92
13: */
14:
15: #include <windows.h>
16: #include <mmsystem.h>
17: #include <mmddk.h>
18: #include "driver.h"
19: #include "adlib.h"
20:
21: /*
22: * overview
23: *
24: * The FM synthesis chip consists of 18 operator cells or 'slots'. Each slot
25: * can produce a sine wave modified by a number of parameters such
26: * as frequency, output level and envelope shape (attack/decay/sustain
27: * release). Slots are arranged in pairs, with one slot modulating
28: * the sine wave of another to produce the harmonics desired for
29: * a given instrument sound. Thus one pair of slots is a 'voice' and can
30: * play one note at a time.
31: *
32: * In percussive mode (which we always use), there are 6 melodic voices
33: * available, and one voice for the base drum. The remaining four slots
34: * are used singly rather than in pairs to produce four further percussive
35: * voices. The 6 melodic voices will be changed to any given timbre
36: * as needed. The five percussive voices are fixed to particular instrument
37: * timbres: bass drum, snare, tom-tom, hi-hat and cymbal.
38: *
39: * To play a note, we first find a free voice of the appropriate type. If
40: * there are none free, we use the oldest busy one. We then set the
41: * parameters for both operator slots from the patch table - this table gives
42: * parameter settings for the different instrument timbres available.
43: * We adjust the output level and frequency depending on the
44: * note played and the velocity it was played with, and then switch on the
45: * note.
46: */
47:
48: /* --- typedefs ------------------------------------------------- */
49:
50: #define NUMVOICES (11) // number of voices we have
51: #define NUMMELODIC (6) // number of melodic voices
52: #define NUMPERCUSSIVE (5) // number of percussive voices
53:
54: #define MAXPATCH 180 // nr of patches (including drums)
55: #define MAXVOLUME 0x7f
56: #define NUMLOCPARAM 14 // number of loc params per slot
57:
58: #define FIRSTDRUMNOTE 35
59: #define LASTDRUMNOTE 81
60: #define NUMDRUMNOTES (LASTDRUMNOTE - FIRSTDRUMNOTE + 1)
61:
62: #define MAX_PITCH 0x3fff // maximum pitch bend value
63: #define MID_PITCH 0x2000 // mid pitch bend value (no shift)
64: #define PITCHRANGE 2 // total bend range +- 2 semitones
65: #define NR_STEP_PITCH 25 // steps per half-tone for pitch bend
66: #define MID_C 60 // MIDI standard mid C
67: #define CHIP_MID_C 48 // sound chip mid C
68:
69: /*
70: * to write to the device, we write a SYNTH_DATA port,data pair
71: * to the kernel driver.
72: */
73: #define SndOutput(p,d) MidiSendFM(p, d)
74:
75:
76:
77:
78: /****************************************************************************
79: *
80: * definitions of sound chip parameters
81: */
82:
83: // parameters of each voice
84: #define prmKsl 0 // key scale level (KSL) - level controller
85: #define prmMulti 1 // frequency multiplier (MULTI) - oscillator
86: #define prmFeedBack 2 // modulation feedback (FB) - oscillator
87: #define prmAttack 3 // attack rate (AR) - envelope generator
88: #define prmSustain 4 // sustain level (SL) - envelope generator
89: #define prmStaining 5 // sustaining sound (SS) - envelope generator
90: #define prmDecay 6 // decay rate (DR) - envelope generator
91: #define prmRelease 7 // release rate (RR) - envelope generator
92: #define prmLevel 8 // output level (OL) - level controller
93: #define prmAm 9 // amplitude vibrato (AM) - level controller
94: #define prmVib 10 // frequency vibrator (VIB) - oscillator
95: #define prmKsr 11 // envelope scaling (KSR) - envelope generator
96: #define prmFm 12 // fm=0, additive=1 (FM) (operator 0 only)
97: #define prmWaveSel 13 // wave select
98:
99: // global parameters
100: #define prmAmDepth 14
101: #define prmVibDepth 15
102: #define prmNoteSel 16
103: #define prmPercussion 17
104:
105: // percussive voice numbers (0-5 are melodic voices, 2 op):
106: #define BD 6 // bass drum (2 op)
107: #define SD 7 // snare drum (1 op)
108: #define TOM 8 // tom-tom (1 op)
109: #define CYMB 9 // cymbal (1 op)
110: #define HIHAT 10 // hi-hat (1 op)
111:
112: // In percussive mode, the last 4 voices (SD TOM HH CYMB) are created
113: // using melodic voices 7 & 8. A noise generator uses channels 7 & 8
114: // frequency information for creating rhythm instruments. Best results
115: // are obtained by setting TOM two octaves below mid C and SD 7 half-tones
116: // above TOM. In this implementation, only the TOM pitch may vary, with the
117: // SD always set 7 half-tones above.
118: #define TOM_PITCH 24 // best frequency, in range of 0 to 95
119: #define TOM_TO_SD 7 // 7 half-tones between voice 7 & 8
120: #define SD_PITCH (TOM_PITCH + TOM_TO_SD)
121:
122: /****************************************************************************
123: *
124: * bank file support
125: *
126: ***************************************************************************/
127:
128: #define BANK_SIG_LEN 6
129: #define BANK_FILLER_SIZE 8
130:
131: typedef BYTE *HPBYTE;
132: typedef BYTE NEAR * NPBYTE;
133: typedef WORD NEAR * NPWORD;
134:
135: // instrument bank file header
136: typedef struct {
137: char majorVersion;
138: char minorVersion;
139: char sig[BANK_SIG_LEN]; // signature: "ADLIB-"
140: WORD nrDefined; // number of list entries used
141: WORD nrEntry; // number of total entries in list
142: long offsetIndex; // offset of start of list of names
143: long offsetTimbre; // offset of start of data
144: char filler[BANK_FILLER_SIZE]; // must be zero
145: } BANKHDR, NEAR *NPBANKHDR, FAR *LPBANKHDR;
146:
147:
148: // all the parameters for one slot
149: typedef struct {
150: BYTE ksl; // KSL
151: BYTE freqMult; // MULTI
152: BYTE feedBack; // FB
153: BYTE attack; // AR
154: BYTE sustLevel; // SL
155: BYTE sustain; // SS
156: BYTE decay; // DR
157: BYTE release; // RR
158: BYTE output; // OL
159: BYTE am; // AM
160: BYTE vib; // VIB
161: BYTE ksr; // KSR
162: BYTE fm; // FM
163: } OPERATOR, NEAR *NPOPERATOR, FAR *LPOPERATOR;
164:
165: // definition of a particular instrument sound or timbre -
166: // one of these per patch
167: typedef struct {
168: BYTE mode; // 0 = melodic, 1 = percussive
169: BYTE percVoice; // if mode == 1, voice number to be used
170: OPERATOR op0; // params for slot 0
171: OPERATOR op1; // params for slot 1
172: BYTE wave0; // waveform for operator 0
173: BYTE wave1; // waveform for operator 1
174: } TIMBRE, NEAR *NPTIMBRE, FAR *LPTIMBRE;
175:
176: typedef struct drumpatch_tag {
177: BYTE patch; // the patch to use
178: BYTE note; // the note to play
179: } DRUMPATCH;
180:
181: // format of drumkit.dat file
182: typedef struct drumfilepatch_tag {
183: BYTE key; // the key to map
184: BYTE patch; // the patch to use
185: BYTE note; // the note to play
186: } DRUMFILEPATCH, *PDRUMFILEPATCH;
187:
188:
189: typedef struct _VOICE {
190: BYTE alloc; // is voice allocated?
191: BYTE note; // note that is currently being played
192: BYTE channel; // channel that it is being played on
193: BYTE volume; // current volume setting of voice
194: DWORD dwTimeStamp; // time voice was allocated
195: } VOICE;
196:
197: #define GetLocPrm(slot_num, prm) ((WORD)paramSlot[slot_num][prm])
198:
199:
200: /* --- module data ---------------------------------------------- */
201:
202: // this table gives the offset of each slot within the chip.
203: BYTE offsetSlot[] = {
204: 0, 1, 2, 3, 4, 5,
205: 8, 9, 10, 11, 12, 13,
206: 16, 17, 18, 19, 20, 21
207: };
208:
209: static BYTE percMasks[] = {
210: 0x10, 0x08, 0x04, 0x02, 0x01 };
211:
212: // voice number associated with each slot (melodic mode only)
213: static BYTE voiceSlot[] = {
214: 0, 1, 2,
215: 0, 1, 2,
216: 3, 4, 5,
217: 3, 4, 5,
218: 6, 7, 8,
219: 6, 7, 8,
220: };
221:
222: // slot numbers for percussive voices (0 indicates that there is only one slot)
223: static BYTE slotPerc[][2] = {
224: {12, 15}, // Bass Drum
225: {16, 0}, // SD
226: {14, 0}, // TOM
227: {17, 0}, // TOP-CYM
228: {13, 0} // HH
229: };
230:
231: // slot numbers as a function of the voice and the operator (melodic only)
232: static BYTE slotVoice[][2] = {
233: {0, 3}, // voice 0
234: {1, 4}, // 1
235: {2, 5}, // 2
236: {6, 9}, // 3
237: {7, 10}, // 4
238: {8, 11}, // 5
239: {12, 15}, // 6
240: {13, 16}, // 7
241: {14, 17} // 8
242: };
243:
244: // this table indicates if the slot is a modulator (0) or a carrier (1).
245: BYTE operSlot[] = {
246: 0, 0, 0, // 1 2 3
247: 1, 1, 1, // 4 5 6
248: 0, 0, 0, // 7 8 9
249: 1, 1, 1, // 10 11 12
250: 0, 0, 0, // 13 14 15
251: 1, 1, 1, // 16 17 18
252: };
253:
254: // this array adjusts the octave of a note for certain patches.
255: static char patchKeyOffset[] = {
256: 0, -12, 12, 0, 0, 12, -12, 0, 0, -24, // 0 - 9
257: 0, 0, 0, 0, 0, 0, 0, 0, -12, 0, // 10 - 19
258: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 - 29
259: 0, 0, 12, 12, 12, 0, 0, 12, 12, 0, // 30 - 39
260: -12, -12, 0, 12, -12, -12, 0, 12, 0, 0, // 40 - 49
261: -12, 0, 0, 0, 12, 12, 0, 0, 12, 0, // 50 - 59
262: 0, 0, 12, 0, 0, 0, 12, 12, 0, 12, // 60 - 69
263: 0, 0, -12, 0, -12, -12, 0, 0, -12, -12, // 70 - 79
264: 0, 0, 0, 0, 0, -12, -19, 0, 0, -12, // 80 - 89
265: 0, 0, 0, 0, 0, 0, -31, -12, 0, 12, // 90 - 99
266: 12, 12, 12, 0, 12, 0, 12, 0, 0, 0, // 100 - 109
267: 0, 12, 0, 0, 0, 0, 12, 12, 12, 0, // 110 - 119
268: 0, 0, 0, 0, -24, -36, 0, 0}; // 120 - 127
269:
270: static BYTE loudervol[128] = {
271: 0, 0, 65, 65, 66, 66, 67, 67, // 0 - 7
272: 68, 68, 69, 69, 70, 70, 71, 71, // 8 - 15
273: 72, 72, 73, 73, 74, 74, 75, 75, // 16 - 23
274: 76, 76, 77, 77, 78, 78, 79, 79, // 24 - 31
275: 80, 80, 81, 81, 82, 82, 83, 83, // 32 - 39
276: 84, 84, 85, 85, 86, 86, 87, 87, // 40 - 47
277: 88, 88, 89, 89, 90, 90, 91, 91, // 48 - 55
278: 92, 92, 93, 93, 94, 94, 95, 95, // 56 - 63
279: 96, 96, 97, 97, 98, 98, 99, 99, // 64 - 71
280: 100, 100, 101, 101, 102, 102, 103, 103, // 72 - 79
281: 104, 104, 105, 105, 106, 106, 107, 107, // 80 - 87
282: 108, 108, 109, 109, 110, 110, 111, 111, // 88 - 95
283: 112, 112, 113, 113, 114, 114, 115, 115, // 96 - 103
284: 116, 116, 117, 117, 118, 118, 119, 119, // 104 - 111
285: 120, 120, 121, 121, 122, 122, 123, 123, // 112 - 119
286: 124, 124, 125, 125, 126, 126, 127, 127}; // 120 - 127
287:
288: /*
289: * attenuation setting for each slot - combination
290: * of the channel attenuation for this channel, and the
291: * velocity (converted to attenuation). Combine with
292: * global attenuation and timbre output attenuation before
293: * writing to device.
294: */
295: WORD slotAtten[18]; // vol-control attenuation of slots
296:
297: WORD wSynthAtten; // overall volume setting
298:
299: BYTE gbChanAtten[NUMCHANNELS]; // attenuation for each channel
300:
301: VOICE voices[11]; // 9 voices if melodic mode or 11 if percussive
302: BYTE voiceKeyOn[11]; // keyOn bit for each voice (implicit 0 init)
303: BYTE paramSlot[18][NUMLOCPARAM]; // all the parameters of slots...
304:
305: BYTE percBits; // control bits of percussive voices
306:
307: WORD fNumNotes[NR_STEP_PITCH][12];
308: PWORD fNumFreqPtr[11]; // lines of fNumNotes table (one per voice)
309: int halfToneOffset[11]; // one per voice
310: BYTE notePitch[11]; // pitch value for each voice (implicit 0 init)
311:
312: // patches - parameters for different instrument timbres
313: TIMBRE patches[MAXPATCH]; // patch data
314: DRUMPATCH drumpatch[NUMDRUMNOTES]; // drum kit data
315:
316:
317: DWORD dwAge = 0; // voice relative age
318:
319:
320:
321:
322: /* --- internal functions --------------------------------------- */
323:
324:
325:
326: /* -- initialisation --- */
327: /****************************************************************************
328: * @doc INTERNAL
329: *
330: * @api int | LoadPatches | Reads the patch set from the BANK resource and
331: * builds the <p patches> array.
332: *
333: * @rdesc Returns the number of patches loaded, or 0 if an error occurs.
334: ***************************************************************************/
335: static BYTE PatchRes[] = {
336: #include "adlib.dat"
337: };
338:
339: int LoadPatches(void)
340: {
341: #ifdef WIN16
342: HANDLE hResInfo;
343: HANDLE hResData;
344: #endif
345: LPSTR lpRes;
346: int iPatches;
347: DWORD dwOffset;
348: DWORD dwResSize;
349: LPTIMBRE lpBankTimbre;
350: LPTIMBRE lpPatchTimbre;
351: LPBANKHDR lpBankHdr;
352:
353:
354: // find resource and get its size
355: #ifdef WIN16
356: hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTBANK), MAKEINTRESOURCE(RT_BANK));
357: if (!hResInfo) {
358: D1("Default bank resource not found");
359: return 0;
360: }
361: dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
362:
363: // load and lock resource
364: hResData = LoadResource(ghInstance, hResInfo);
365: if (!hResData) {
366: D1("Bank resource not loaded");
367: return 0;
368: }
369: lpRes = LockResource(hResData);
370: if (!lpRes) {
371: D1("Bank resource not locked");
372: return 0;
373: }
374: #else
375: lpRes = PatchRes;
376: dwResSize = sizeof(PatchRes);
377: #endif
378:
379: // read the bank resource, loading patches as we find them
380:
381: D2("loading patches");
382: lpBankHdr = (LPBANKHDR)lpRes;
383: dwOffset = lpBankHdr->offsetTimbre; // point to first one
384:
385: for (iPatches = 0; iPatches < MAXPATCH; iPatches++) {
386:
387: lpBankTimbre = (LPTIMBRE)(lpRes + dwOffset);
388: lpPatchTimbre = &patches[iPatches];
389: *lpPatchTimbre = *lpBankTimbre;
390:
391: dwOffset += sizeof(TIMBRE);
392: if (dwOffset + sizeof(TIMBRE) > dwResSize) {
393: D1("Attempt to read past end of bank resource");
394: break;
395: }
396: }
397:
398: #ifdef WIN16
399: UnlockResource(hResData);
400: FreeResource(hResData);
401: #endif
402:
403: return iPatches;
404: }
405:
406:
407: /****************************************************************************
408: * @doc INTERNAL
409: *
410: * @api int | LoadDrumPatches | Reads the drum kit patch set from the
411: * DRUMKIT resource and builds the <p drumpatch> array.
412: *
413: * @comm Each entry of the <t drumpatch> array (representing a key number
414: * from the "drum patch") consists of a patch number and note number
415: * from some other patch.
416: *
417: * @rdesc Returns the number of patches loaded, or 0 if an error occurs.
418: ***************************************************************************/
419:
420: static BYTE DrumRes[] = {
421: #include "drumkit.dat"
422: };
423:
424: int LoadDrumPatches(void)
425: {
426: #ifdef WIN16
427: HANDLE hResInfo;
428: HANDLE hResData;
429: #endif
430: LPSTR lpRes;
431: int iPatches;
432: int key;
433: DWORD dwOffset;
434: DWORD dwResSize;
435: PDRUMFILEPATCH lpResPatch;
436:
437: #ifdef WIN16
438: // find resource and get its size
439: hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTDRUMKIT), MAKEINTRESOURCE(RT_DRUMKIT));
440: if (!hResInfo) {
441: D1("Default drum resource not found");
442: return 0;
443: }
444: dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
445:
446: // load and lock resource
447: hResData = LoadResource(ghInstance, hResInfo);
448: if (!hResData) {
449: D1("Drum resource not loaded");
450: return 0;
451: }
452: lpRes = LockResource(hResData);
453: if (!lpRes) {
454: D1("Drum resource not locked");
455: return 0;
456: }
457: #else
458: lpRes = DrumRes;
459: dwResSize = sizeof(DrumRes);
460: #endif
461:
462: // read the drum resource, loading patches as we find them
463:
464: D2("reading drum data");
465: dwOffset = 0;
466: for (iPatches = 0; iPatches < NUMDRUMNOTES; iPatches++) {
467:
468: lpResPatch = (PDRUMFILEPATCH)(lpRes + dwOffset);
469: key = lpResPatch->key;
470: if ((key >= FIRSTDRUMNOTE) && (key <= LASTDRUMNOTE)) {
471: drumpatch[key - FIRSTDRUMNOTE].patch = lpResPatch->patch;
472: drumpatch[key - FIRSTDRUMNOTE].note = lpResPatch->note;
473: }
474: else {
475: D1("Drum patch key out of range");
476: }
477:
478: dwOffset += sizeof(DRUMFILEPATCH);
479: if (dwOffset > dwResSize) {
480: D1("Attempt to read past end of drum resource");
481: break;
482: }
483: }
484:
485: #ifdef WIN16
486: UnlockResource(hResData);
487: FreeResource(hResData);
488: #endif
489:
490: return iPatches;
491: }
492:
493: /****************************************************************************
494: * @doc INTERNAL
495: *
496: * @api long | CalcPremFNum | Calculates some magic number that is used in
497: * setting the values in the <p fNumNotes> table.
498: *
499: * @parm int | numDeltaDemiTon | Numerator (-100 to +100).
500: *
501: * @parm int | denDeltaDemiTon | Denominator (1 to 100).
502: *
503: * @comm If the numerator (numDeltaDemiTon) is positive, the frequency is
504: * increased; if negative, it is decreased. The function calculates:
505: * f8 = Fb(1 + 0.06 num /den) (where Fb = 26044 * 2 / 25)
506: * fNum8 = f8 * 65536 * 72 / 3.58e6
507: *
508: * @rdesc Returns fNum8, which is the binary value of the frequency 260.44 (C)
509: * shifted by +/- <p numdeltaDemiTon> / <p denDeltaDemiTon> * 8.
510: ***************************************************************************/
511: long NEAR CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon)
512: {
513: long f8;
514: long fNum8;
515: long d100;
516:
517: d100 = denDeltaDemiTon * 100;
518: f8 = (d100 + 6 * numDeltaDemiTon) * (26044L * 2L);
519: f8 /= d100 * 25;
520:
521: fNum8 = f8 * 16384;
522: fNum8 *= 9L;
523: fNum8 /= 179L * 625L;
524:
525: return fNum8;
526: }
527:
528: /****************************************************************************
529: * @doc INTERNAL
530: *
531: * @api void | SetFNum | Initializes a line in the frequency table with
532: * shifted frequency values. The values are shifted a fraction (num/den)
533: * of a half-tone.
534: *
535: * @parm NPWORD | fNumVec | The line from the frequency table.
536: *
537: * @parm int | num | Numerator.
538: *
539: * @parm int | den | Denominator.
540: *
541: * @xref CalcPremFNum
542: *
543: * @rdesc There is no return value.
544: ***************************************************************************/
545: void NEAR SetFNum(NPWORD fNumVec, int num, int den)
546: {
547: int i;
548: long val;
549:
550: *fNumVec++ = (WORD)((4 + (val = CalcPremFNum(num, den))) >> 3);
551: for (i = 1; i < 12; i++) {
552: val *= 106;
553: *fNumVec++ = (WORD)((4 + (val /= 100)) >> 3);
554: }
555: }
556:
557: /****************************************************************************
558: * @doc INTERNAL
559: *
560: * @api void | InitFNums | Initializes all lines of the frequency table
561: * (the <p fNumNotes> array). Each line represents 12 half-tones shifted
562: * by (n / NR_STEP_PITCH), where 'n' is the line number and ranges from
563: * 1 to NR_STEP_PITCH.
564: *
565: * @rdesc There is no return value.
566: ***************************************************************************/
567: void NEAR InitFNums(void)
568: {
569: WORD i;
570: WORD num; // numerator
571: WORD numStep; // step value for numerator
572: WORD row; // row in the frequency table
573:
574: // calculate each row in the fNumNotes table
575: numStep = 100 / NR_STEP_PITCH;
576: for (num = row = 0; row < NR_STEP_PITCH; row++, num += numStep)
577: SetFNum(fNumNotes[row], num, 100);
578:
579: // fNumFreqPtr has an element for each voice, pointing to the
580: // appropriate row in the fNumNotes table. They're all initialized
581: // to the first row, which represents no pitch shift.
582: for (i = 0; i < 11; i++) {
583: fNumFreqPtr[i] = fNumNotes[0];
584: halfToneOffset[i] = 0;
585: }
586: }
587:
588: /****************************************************************************
589: * @doc INTERNAL
590: *
591: * @api void | SoundChut | Sets the frequency of voice <p voice> to 0 Hz.
592: *
593: * @parm BYTE | voice | Specifies which voice to set.
594: *
595: * @rdesc There is no return value.
596: ***************************************************************************/
597: void NEAR SoundChut(BYTE voice)
598: {
599:
600: SndOutput((BYTE)(0xA0 | voice), 0);
601: SndOutput((BYTE)(0xB0 | voice), 0);
602: }
603:
604: /* --- write parameters to chip ------------------------*/
605:
606: /*
607: * switch chip to rhythm (percussive) mode, set the amplitude and
608: * vibrato depth and switch on any percussion voices that are on
609: */
610: void SndSAmVibRhythm(void)
611: {
612:
613: // we always set amdepth, vibdepth to 0 and perc mode on
614:
615: // t1 = (BYTE)(amDepth ? 0x80 : 0);
616: // t1 |= vibDepth ? 0x40 : 0;
617: // t1 |= (percussionmode ? 0x20 : 0);
618:
619: SndOutput((BYTE)0xBD, (BYTE)(0x20|percBits));
620: }
621:
622: /****************************************************************************
623: * @doc INTERNAL
624: *
625: * @api void | SndSNoteSel | Sets the NoteSel value.
626: *
627: * @rdesc There is no return value.
628: ***************************************************************************/
629: void SndSNoteSel(BOOL bNoteSel)
630: {
631: SndOutput(0x08, (BYTE)(bNoteSel ? 64 : 0));
632: }
633:
634: /****************************************************************************
635: * @doc INTERNAL
636: *
637: * @api void | SndSKslLevel | Sets the KSL and LEVEL values.
638: *
639: * @parm BYTE | slot | Specifies which slot to set.
640: *
641: * @rdesc There is no return value.
642: ***************************************************************************/
643: void SndSKslLevel(BYTE slot)
644: {
645: WORD t1;
646:
647: t1 = GetLocPrm(slot, prmLevel) & 0x3f;
648:
649: t1 += slotAtten[slot];
650: t1 = min (t1, 0x3f);
651: t1 |= GetLocPrm(slot, prmKsl) << 6;
652: SndOutput((BYTE)(0x40 | offsetSlot[slot]), (BYTE) t1);
653: }
654:
655:
656: /****************************************************************************
657: * @doc INTERNAL
658: *
659: * @api void | SndSAVEK | Sets the AM, VIB, EG-TYP (sustaining), KSR, and
660: * MULTI values.
661: *
662: * @parm BYTE | slot | Specifies which slot to set.
663: *
664: * @rdesc There is no return value.
665: ***************************************************************************/
666: void SndSAVEK(BYTE slot)
667: {
668: BYTE t1;
669:
670: t1 = (BYTE)(GetLocPrm(slot, prmAm) ? 0x80 : 0);
671: t1 += GetLocPrm(slot, prmVib) ? 0x40 : 0;
672: t1 += GetLocPrm(slot, prmStaining) ? 0x20 : 0;
673: t1 += GetLocPrm(slot, prmKsr) ? 0x10 : 0;
674: t1 += GetLocPrm(slot, prmMulti) & 0xf;
675: SndOutput((BYTE)(0x20 | offsetSlot[slot]), t1);
676: }
677:
678: /****************************************************************************
679: * @doc INTERNAL
680: *
681: * @api void | SndSFeedFm | Sets the FEEDBACK and FM (connection) values.
682: * Applicable only to operator 0 for melodic voices.
683: *
684: * @parm BYTE | slot | Specifies which slot to set.
685: *
686: * @rdesc There is no return value.
687: ***************************************************************************/
688: void SndSFeedFm(BYTE slot)
689: {
690: BYTE t1;
691:
692: if (operSlot[slot])
693: return;
694:
695: t1 = (BYTE)(GetLocPrm(slot, prmFeedBack) << 1);
696: t1 |= GetLocPrm(slot, prmFm) ? 0 : 1;
697: SndOutput((BYTE)(0xC0 | voiceSlot[slot]), t1);
698: }
699:
700: /****************************************************************************
701: * @doc INTERNAL
702: *
703: * @api void | SndSAttDecay | Sets the ATTACK and DECAY values.
704: *
705: * @parm BYTE | slot | Specifies which slot to set.
706: *
707: * @rdesc There is no return value.
708: ***************************************************************************/
709: void SndSAttDecay(BYTE slot)
710: {
711: BYTE t1;
712:
713: t1 = (BYTE)(GetLocPrm(slot, prmAttack) << 4);
714: t1 |= GetLocPrm(slot, prmDecay) & 0xf;
715: SndOutput((BYTE)(0x60 | offsetSlot[slot]), t1);
716: }
717:
718: /****************************************************************************
719: * @doc INTERNAL
720: *
721: * @api void | SndSSusRelease | Sets the SUSTAIN and RELEASE values.
722: *
723: * @parm BYTE | slot | Specifies which slot to set.
724: *
725: * @rdesc There is no return value.
726: ***************************************************************************/
727: void SndSSusRelease(BYTE slot)
728: {
729: BYTE t1;
730:
731: t1 = (BYTE)(GetLocPrm(slot, prmSustain) << 4);
732: t1 |= GetLocPrm(slot, prmRelease) & 0xf;
733: SndOutput((BYTE)(0x80 | offsetSlot[slot]), t1);
734: }
735:
736: /****************************************************************************
737: * @doc INTERNAL
738: *
739: * @api void | SndWaveSelect | Sets the wave-select parameter.
740: *
741: * @parm BYTE | slot | Specifies which slot to set.
742: *
743: * @rdesc There is no return value.
744: ***************************************************************************/
745: void SndWaveSelect(BYTE slot)
746: {
747: BYTE wave;
748:
749: wave = (BYTE)(GetLocPrm(slot, prmWaveSel) & 0x03);
750:
751: SndOutput((BYTE)(0xE0 | offsetSlot[slot]), wave);
752: }
753:
754:
755: /****************************************************************************
756: * @doc INTERNAL
757: *
758: * @api void | SndSetAllPrm | Transfers all the parameters from slot <p slot>
759: * to the chip.
760: *
761: * @parm BYTE | slot | Specifies which slot to set.
762: *
763: * @rdesc There is no return value.
764: ***************************************************************************/
765: void SndSetAllPrm(BYTE slot)
766: {
767:
768: /* global am-depth and vib-depth settings */
769: SndSAmVibRhythm();
770:
771: /* note sel is always false */
772: SndSNoteSel(FALSE);
773:
774: /* slot-specific parameters */
775:
776: /* initialise volume to minimum to avoid clicks if the note is
777: * off but still playing (during decay phase).
778: *
779: * only applies to carrier slots.
780: */
781: if (operSlot[slot]) {
782: /* its a carrier slot */
783: slotAtten[slot] = 0x3f; // max attenuation
784: }
785: SndSKslLevel(slot);
786:
787: SndSFeedFm(slot);
788: SndSAttDecay(slot);
789: SndSSusRelease(slot);
790: SndSAVEK(slot);
791: SndWaveSelect(slot);
792: }
793:
794:
795:
796: /* --- setting slot parameters ------------------ */
797:
798: /****************************************************************************
799: * @doc INTERNAL
800: *
801: * @api void | SetSlotParam | Sets the 14 parameters (13 in <p param>,
802: * 1 in <p waveSel>) of slot <p slot>. Updates both the parameter array
803: * and the chip.
804: *
805: * @parm BYTE | slot | Specifies which slot to set.
806: *
807: * @parm NPBYTE | param | Pointer to the new parameter array.
808: *
809: * @parm BYTE | waveSel | The new waveSel value.
810: *
811: * @rdesc There is no return value.
812: ***************************************************************************/
813: void SetSlotParam(BYTE slot, NPOPERATOR pOper, BYTE waveSel)
814: {
815: LPBYTE ptr;
816:
817: ptr = ¶mSlot[slot][0];
818:
819: *ptr++ = pOper->ksl;
820: *ptr++ = pOper->freqMult;
821: *ptr++ = pOper->feedBack;
822: *ptr++ = pOper->attack;
823: *ptr++ = pOper->sustLevel;
824: *ptr++ = pOper->sustain;
825: *ptr++ = pOper->decay;
826: *ptr++ = pOper->release;
827: *ptr++ = pOper->output;
828: *ptr++ = pOper->am;
829: *ptr++ = pOper->vib;
830: *ptr++ = pOper->ksr;
831: *ptr++ = pOper->fm;
832:
833: *ptr = waveSel & 0x3;
834:
835: // set default volume settings
836: slotAtten[slot] = 0;
837:
838: SndSetAllPrm(slot);
839: }
840:
841: /*
842: * set this voice up to the correct parameters for a timbre
843: * copy the parameters to the slot array and write them to the
844: * chip
845: *
846: */
847:
848: void SetVoiceTimbre(BYTE voice, NPTIMBRE pTimbre)
849: {
850:
851: if (voice < BD) { // melodic only
852: SetSlotParam(slotVoice[voice][0], &pTimbre->op0, pTimbre->wave0);
853: SetSlotParam(slotVoice[voice][1], &pTimbre->op1, pTimbre->wave1);
854: }
855: else if (voice == BD) { // bass drum
856: SetSlotParam(slotPerc[0][0], &pTimbre->op0, pTimbre->wave0);
857: SetSlotParam(slotPerc[0][1], &pTimbre->op1, pTimbre->wave1);
858: } else { // percussion, 1 slot
859: SetSlotParam(slotPerc[voice - BD][0], &pTimbre->op0, pTimbre->wave0);
860: }
861: }
862:
863:
864:
865: /* --- frequency calculation -------------------- */
866:
867:
868: /* convert the given pitch (0 .. 95) into a frequency
869: * and write to the chip.
870: *
871: * this will turn the note on if keyon is true.
872: */
873: VOID SetFreq(BYTE voice, BYTE pitch, BYTE keyOn)
874: {
875: WORD FNum;
876: BYTE t1;
877:
878:
879: // remember the keyon and pitch of the voice
880:
881: voiceKeyOn[voice] = keyOn;
882: notePitch[voice] = pitch;
883:
884: pitch += halfToneOffset[voice];
885: if (pitch > 95)
886: pitch = 95;
887:
888: // get the FNum for the voice
889: FNum = * (fNumFreqPtr[voice] + pitch % 12);
890:
891: // output the FNum, KeyOn and Block values
892: SndOutput((BYTE)(0xA0 | voice), (BYTE)FNum); // FNum bits 0 - 7 (D0 - D7)
893: t1 = (BYTE)(keyOn ? 32 : 0); // Key On (D5)
894: t1 += (pitch / 12 << 2); // Block (D2 - D4)
895: t1 += (0x3 & (FNum >> 8)); // FNum bits 8 - 9 (D0 - D1)
896: SndOutput((BYTE)(0xB0 | voice), t1);
897: }
898:
899:
900: /****************************************************************************
901: * @doc INTERNAL
902: *
903: * @api void | ChangePitch | This routine sets the <t halfToneOffset[]> and
904: * <t fNumFreqPtr[]> arrays. These two global variables are used to
905: * determine the frequency variation to use when a note is played.
906: *
907: * @parm BYTE | voice | Specifies which voice to use.
908: *
909: * @parm WORD | pitchBend | Specifies the pitch bend value (0 to 0x3fff,
910: * where 0x2000 is exact tuning).
911: *
912: * @rdesc There is no return value.
913: ***************************************************************************/
914: void ChangePitch(BYTE voice, WORD pitchBend)
915: {
916: int t1;
917: int t2;
918: int delta;
919: int pitchrange;
920:
921: /* pitch bend range 0-3fff is +-PITCHRANGE semitones. We move
922: * NR_STEP_PITCH steps per semitone.
923: * so the bend range is +- (PITCHRANGE * NR_STEP_PITCH) steps.
924: */
925: pitchrange = PITCHRANGE * NR_STEP_PITCH;
926:
927: t1 = (int)(((long)((int)pitchBend - MID_PITCH) * pitchrange) / MID_PITCH);
928:
929: if (t1 < 0)
930: {
931: t2 = NR_STEP_PITCH - 1 - t1;
932: halfToneOffset[voice] = -(t2 / NR_STEP_PITCH);
933: delta = (t2 - NR_STEP_PITCH + 1) % NR_STEP_PITCH;
934: if (delta) {
935: delta = NR_STEP_PITCH - delta;
936: }
937: }
938: else
939: {
940: halfToneOffset[voice] = t1 / NR_STEP_PITCH;
941: delta = t1 % NR_STEP_PITCH;
942: }
943: fNumFreqPtr[voice] = fNumNotes[delta];
944:
945: }
946:
947: /****************************************************************************
948: * @doc INTERNAL
949: *
950: * @api void | SetVoicePitch | Changes the pitch value of a voice. Does
951: * not affect the percussive voices, except for the bass drum. The change
952: * takes place immediately.
953: *
954: * @parm BYTE | voice | Specifies which voice to set.
955: *
956: * @parm WORD | pitchBend | Specifies the new pitch bend value (0 to 0x3fff,
957: * where 0x2000 == exact tuning).
958: *
959: * @comm The variation in pitch is a function of the previous call to
960: * <f SetPitchRange> and the value of <p pitchBend>. A value of 0 means
961: * -half-tone * pitchRangeStep, 0x2000 means no variation (exact pitch) and
962: * 0x3fff means +half-tone * pitchRangeStep.
963: *
964: * @rdesc There is no return value.
965: ***************************************************************************/
966: void SetVoicePitch(BYTE voice, WORD pitchBend)
967: {
968: if (voice <= BD) { // melodic and bass drum voices
969: if (pitchBend > MAX_PITCH) {
970: pitchBend = MAX_PITCH;
971: }
972: ChangePitch(voice, pitchBend);
973: SetFreq(voice, notePitch[voice], voiceKeyOn[voice]);
974: }
975: }
976:
977: /* --- volume calculation ------------------------ */
978:
979: /*
980: * set the attenuation for a slot (combine the channel attenuation
981: * setting with the key velocity).
982: */
983: VOID SetVoiceAtten(BYTE voice, BYTE bChannel, BYTE bVelocity)
984: {
985: BYTE bAtten;
986: BYTE slot;
987: PBYTE slots;
988:
989: // scale up the volume since the patch maps are too quiet
990: //bVelocity = loudervol[bVelocity];
991:
992:
993: /*
994: * set channel attenuation
995: */
996: bAtten = gbVelocityAtten[bVelocity >> 2] + gbChanAtten[bChannel];
997:
998:
999: /*
1000: * add on any global (volume-setting) attenuation
1001: */
1002: bAtten += wSynthAtten;
1003:
1004:
1005: /* save this for each non-modifier slot */
1006:
1007: if (voice <= BD) { // melodic voice
1008: slots = slotVoice[voice];
1009:
1010: slotAtten[slots[1]] = bAtten;
1011: SndSKslLevel(slots[1]);
1012:
1013: if (!GetLocPrm(slots[0], prmFm)) {
1014: // additive synthesis: set volume of first slot too
1015: slotAtten[slots[0]] = bAtten;
1016: SndSKslLevel(slots[0]);
1017: }
1018: }
1019: else { // percussive voice
1020: slot = slotPerc[voice - BD][0];
1021: slotAtten[slot] = bAtten;
1022: SndSKslLevel(slot);
1023: }
1024: }
1025:
1026: /* adjust each slot attenuation to allow for a global volume
1027: * change - note that we only need to change the
1028: * carrier, not the modifier slots.
1029: *
1030: * change for channel bChannel, or all channels if bChannel is
1031: * 0xff
1032: */
1033: VOID ChangeAtten(BYTE bChannel, int iChangeAtten)
1034: {
1035: BYTE voice;
1036: BYTE slot;
1037: PBYTE slots;
1038:
1039: /* find voices using this channel */
1040: for (voice = 0; voice < NUMVOICES; voice++) {
1041: if ((voices[voice].channel == bChannel) || (bChannel == 0xff)) {
1042:
1043: if (voice <= BD) {
1044:
1045: /* melodic voice */
1046: slots = slotVoice[voice];
1047:
1048: slotAtten[slots[1]] += iChangeAtten;
1049: SndSKslLevel(slots[1]);
1050:
1051: if (!GetLocPrm(slots[0], prmFm)) {
1052: // additive synthesis: set volume of first slot too
1053: slotAtten[slots[0]] += iChangeAtten;
1054: SndSKslLevel(slots[0]);
1055: }
1056: } else {
1057: slot = slotPerc[voice - BD][0];
1058: slotAtten[slot] += iChangeAtten;
1059: SndSKslLevel(slot);
1060: }
1061: }
1062: }
1063: }
1064:
1065:
1066: /* --- note on/off ------------------------------- */
1067:
1068: /* switch off the note a given voice is playing */
1069: void NoteOff(BYTE voice)
1070: {
1071:
1072: if (voice < BD) {
1073:
1074: /* melodic voice */
1075: SetFreq(voice, notePitch[voice], 0);
1076: } else {
1077: percBits &= ~percMasks[voice-BD];
1078: SndSAmVibRhythm();
1079: }
1080: }
1081:
1082: /* switch on a voice at a given note (0 - 127, where 60 is middle c) */
1083: void NoteOn(BYTE voice, BYTE bNote)
1084: {
1085: BYTE bPitch;
1086:
1087: /* convert note to chip pitch */
1088: if (bNote < (MID_C - CHIP_MID_C)) {
1089: bPitch = 0;
1090: } else {
1091: bPitch = bNote - (MID_C - CHIP_MID_C);
1092: }
1093:
1094: if (voice < BD) {
1095: /* melodic voice */
1096:
1097: /* set frequency and start note */
1098: SetFreq(voice, bPitch, 1);
1099: } else {
1100: /*
1101: * nb we don't change the pitch of some percussion instruments.
1102: *
1103: * also note that for percussive instruments (including BD),
1104: * the note-on setting should always be 0. You switch the percussion
1105: * on by writing to the AmVibRhythm register.
1106: */
1107:
1108: if (voice == BD) {
1109: SetFreq(voice, bPitch, 0);
1110: } else if (voice == TOM) {
1111: /*
1112: * for best sounds, we do change the TOM freq, but we always keep
1113: * the SD 7 semi-tones above TOM.
1114: */
1115: SetFreq(TOM, bPitch, 0);
1116: SetFreq(SD, (BYTE)(bPitch + TOM_TO_SD), 0);
1117: }
1118: /* other instruments never change */
1119:
1120: percBits |= percMasks[voice - BD];
1121: SndSAmVibRhythm();
1122: }
1123: }
1124:
1125:
1126:
1127: /* -- voice allocation -------- */
1128:
1129: /*
1130: * find the voice that is currently playing a given channel/note pair
1131: * (if any)
1132: */
1133: BYTE
1134: FindVoice(BYTE bNote, BYTE bChannel)
1135: {
1136: BYTE i;
1137:
1138: for (i = 0; i < (BYTE)NUMVOICES; i++) {
1139: if ((voices[i].alloc) && (voices[i].note == bNote)
1140: && (voices[i].channel == bChannel)) {
1141: voices[i].dwTimeStamp = dwAge++;
1142: return(i);
1143: }
1144: }
1145:
1146: /* no voice is playing this */
1147: return(0xff);
1148: }
1149:
1150: /*
1151: * mark a voice as unused
1152: */
1153: VOID FreeVoice(voice)
1154: {
1155: voices[voice].alloc = 0;
1156: }
1157:
1158:
1159: /*
1160: * GetNewVoice - allocate a voice to play this note. if no voices are
1161: * free, re-use the note with the oldest timestamp.
1162: */
1163: BYTE GetNewVoice(BYTE patch, BYTE note, BYTE channel)
1164: {
1165: BYTE i;
1166: BYTE voice;
1167: BYTE bVoiceToUse, bVoiceSame, bVoiceOldest;
1168: DWORD dwOldestTime = dwAge + 1; // init to past current "time"
1169: DWORD dwOldestSame = dwAge + 1;
1170: DWORD dwOldestOff = dwAge + 1;
1171:
1172: if (patches[patch].mode) { // it's a percussive patch
1173: voice = patches[patch].percVoice; // use fixed percussion voice
1174: voices[voice].alloc = TRUE;
1175: voices[voice].note = note;
1176: voices[voice].channel = channel;
1177: voices[voice].dwTimeStamp = MAKELONG(patch, 0);
1178:
1179: SetVoiceTimbre(voice, &patches[patch]);
1180:
1181: return voice;
1182: }
1183:
1184: bVoiceToUse = bVoiceSame = bVoiceOldest = 0xff;
1185:
1186: // find a free melodic voice to use
1187: for (i = 0; i < (BYTE)NUMMELODIC; i++) { // it's a melodic patch
1188: if (!voices[i].alloc) {
1189:
1190: if (voices[i].dwTimeStamp < dwOldestOff) {
1191: bVoiceToUse = i;
1192: dwOldestOff = voices[i].dwTimeStamp;
1193: }
1194:
1195: } else if (voices[i].channel == channel) {
1196: if (voices[i].dwTimeStamp < dwOldestSame) {
1197: dwOldestSame = voices[i].dwTimeStamp;
1198: bVoiceSame = i;
1199: }
1200: } else if (voices[i].dwTimeStamp < dwOldestTime) {
1201: dwOldestTime = voices[i].dwTimeStamp;
1202: bVoiceOldest = i; // remember oldest one to steal
1203: }
1204: }
1205:
1206: // choose a free voice if we have found one. If not, choose the
1207: // oldest voice of the same channel. if none, choose the oldest voice.
1208: if (bVoiceToUse == 0xff) {
1209: if (bVoiceSame != 0xff) {
1210: bVoiceToUse = bVoiceSame;
1211: } else {
1212: bVoiceToUse = bVoiceOldest;
1213: }
1214: }
1215:
1216:
1217: if (voices[bVoiceToUse].alloc) { // if we stole it, turn it off
1218: NoteOff(bVoiceToUse);
1219: }
1220:
1221: voices[bVoiceToUse].alloc = 1;
1222: voices[bVoiceToUse].note = note;
1223: voices[bVoiceToUse].channel = channel;
1224: voices[bVoiceToUse].dwTimeStamp = dwAge++;
1225:
1226: SetVoiceTimbre(bVoiceToUse, &patches[patch]);
1227:
1228: return bVoiceToUse;
1229: }
1230:
1231:
1232: /* --- externally-called functions ------------------------------ */
1233:
1234: /*
1235: * Adlib_NoteOn - This turns a note on. (Including drums, with
1236: * a patch # of the drum Note + 128)
1237: *
1238: * inputs
1239: * BYTE bPatch - MIDI patch number
1240: * BYTE bNote - MIDI note number
1241: * BYTE bChannel - MIDI channel #
1242: * BYTE bVelocity - Velocity #
1243: * short iBend - current pitch bend from -32768, to 32767
1244: * returns
1245: * none
1246: */
1247: VOID NEAR PASCAL Adlib_NoteOn (BYTE bPatch,
1248: BYTE bNote, BYTE bChannel, BYTE bVelocity,
1249: short iBend)
1250: {
1251:
1252: BYTE voice;
1253: WORD wBend;
1254:
1255: if (bVelocity == 0) { // 0 velocity means note off
1256: Adlib_NoteOff(bPatch, bNote, bChannel);
1257: return;
1258: }
1259:
1260: // octave registration for melodic patches
1261: if (bPatch < 128) {
1262: bNote += patchKeyOffset[bPatch];
1263: if ((bNote < 0) || (bNote > 127)) {
1264: bNote -= patchKeyOffset[bPatch];
1265: }
1266: }
1267:
1268:
1269: if (bPatch >= 128) {
1270: /*
1271: * it's a percussion note
1272: */
1273:
1274: bNote = bPatch - 128;
1275:
1276: if ((bNote < FIRSTDRUMNOTE) || (bNote > LASTDRUMNOTE)) {
1277: return;
1278: }
1279:
1280: /* use the drum patch table to map the note to a given
1281: * TIMBRE/note pair
1282: */
1283: bPatch = drumpatch[bNote - FIRSTDRUMNOTE].patch;
1284: bNote = drumpatch[bNote - FIRSTDRUMNOTE].note;
1285:
1286: /* each drum patch plays on one specific voice.
1287: * find that voice
1288: */
1289: voice = patches[bPatch].percVoice;
1290:
1291: /* switch note off if playing */
1292: if (voices[voice].alloc) {
1293: NoteOff(voice);
1294: }
1295:
1296: /* call GetNewVoice to set the voice params and timestamp
1297: * even if we found it.
1298: */
1299: voice = GetNewVoice(bPatch, bNote, bChannel);
1300:
1301: } else {
1302:
1303: /* switch note off if it's playing */
1304: if ( (voice = FindVoice(bNote, bChannel)) != 0xFF ) {
1305: NoteOff(voice);
1306: } else {
1307: voice = GetNewVoice(bPatch, bNote, bChannel);
1308: }
1309: }
1310:
1311: /* convert the velocity to an attenuation setting, and
1312: * write that to the device
1313: */
1314: SetVoiceAtten(voice, bChannel, bVelocity);
1315:
1316: /*
1317: * apply pitch bend. note that we are passed a pitch bend in the
1318: * range 8000-7fff, but our code assumes 0-3fff, so we convert here.
1319: */
1320: wBend = (((WORD)iBend + 0x8000) >> 2) & 0x3fff;
1321: SetVoicePitch(voice, wBend);
1322:
1323: // play the note
1324: NoteOn(voice, bNote);
1325:
1326: }
1327:
1328:
1329: /* Adlib_NoteOff - This turns a note off. (Including drums,
1330: * with a patch # of the drum note + 128)
1331: *
1332: * inputs
1333: * BYTE bPatch - MIDI patch #
1334: * BYTE bNote - MIDI note number
1335: * BYTE bChannel - MIDI channel #
1336: * returns
1337: * none
1338: */
1339: VOID FAR PASCAL Adlib_NoteOff (BYTE bPatch,
1340: BYTE bNote, BYTE bChannel)
1341: {
1342: BYTE bVoice;
1343:
1344: if (bPatch > 127) {
1345:
1346: /* drum note. These all use a fixed voice */
1347:
1348:
1349: if ((bNote < FIRSTDRUMNOTE) || (bNote > LASTDRUMNOTE)) {
1350: return;
1351: }
1352:
1353: /* use the drum patch table to map the note to a given
1354: * TIMBRE/note pair
1355: */
1356: bPatch = drumpatch[bNote - FIRSTDRUMNOTE].patch;
1357: bNote = drumpatch[bNote - FIRSTDRUMNOTE].note;
1358: bVoice = patches[bPatch].percVoice;
1359:
1360: /* switch note off if playing our patch */
1361: if (LOWORD(voices[bVoice].dwTimeStamp) == bPatch) {
1362: NoteOff(bVoice);
1363: }
1364: } else {
1365:
1366: bVoice = FindVoice(bNote, bChannel);
1367: if (bVoice != 0xff) {
1368:
1369: if (voices[bVoice].note) {
1370: NoteOff(bVoice);
1371: FreeVoice(bVoice);
1372: }
1373: }
1374: }
1375: }
1376:
1377: /* Adlib_AllNotesOff - turn off all notes
1378: *
1379: * inputs - none
1380: * returns - none
1381: */
1382: VOID Adlib_AllNotesOff(void)
1383: {
1384:
1385: BYTE i;
1386:
1387: for (i = 0; i < NUMVOICES; i++) {
1388: NoteOff(i);
1389: }
1390: }
1391:
1392:
1393: /* Adlib_NewVolume - This should be called if a volume level
1394: * has changed. This will adjust the levels of all the playing
1395: * voices.
1396: *
1397: * inputs
1398: * WORD wLeft - left attenuation (1.5 db units)
1399: * WORD wRight - right attenuation (ignore if mono)
1400: * returns
1401: * none
1402: */
1403: VOID FAR PASCAL Adlib_NewVolume (WORD wLeft, WORD wRight)
1404: {
1405: /* ignore the right attenuation since this is a mono device */
1406: int iChange;
1407:
1408: wLeft = min(wLeft, wRight) << 1;
1409: iChange = wLeft - wSynthAtten;
1410:
1411: wSynthAtten = wLeft;
1412:
1413: /* change attenuation for all channels */
1414: ChangeAtten(0xff, iChange);
1415: }
1416:
1417:
1418:
1419: /* Adlib_ChannelVolume - set the volume level for an individual channel.
1420: *
1421: * inputs
1422: * BYTE bChannel - channel number to change
1423: * WORD wAtten - attenuation in 1.5 db units
1424: *
1425: * returns
1426: * none
1427: */
1428: VOID FAR PASCAL Adlib_ChannelVolume(BYTE bChannel, WORD wAtten)
1429: {
1430: int iChange;
1431:
1432: iChange = wAtten - gbChanAtten[bChannel];
1433:
1434: gbChanAtten[bChannel] = wAtten;
1435:
1436:
1437: /* change attenuation for this channel */
1438: ChangeAtten(bChannel, iChange);
1439: }
1440:
1441:
1442: /* Adlib_SetPan - set the left-right pan position.
1443: *
1444: * inputs
1445: * BYTE bChannel - channel number to alter
1446: * BYTE bPan - 0 for left, 127 for right or somewhere in the middle.
1447: *
1448: * returns - none
1449: *
1450: * does nothing - this is a mono device
1451: */
1452: VOID FAR PASCAL Adlib_SetPan(BYTE bChannel, BYTE bPan)
1453: {
1454:
1455: /* do nothing */
1456: }
1457:
1458:
1459: /* Adlib_PitchBend - This pitch bends a channel.
1460: *
1461: * inputs
1462: * BYTE bChannel - channel
1463: * short iBend - Values from -32768 to 32767, being
1464: * -2 to +2 half steps
1465: * returns
1466: * none
1467: */
1468: VOID NEAR PASCAL Adlib_PitchBend (BYTE bChannel, short iBend)
1469: {
1470: BYTE i;
1471: WORD w;
1472:
1473: /* note that our code expects 0 - 0x3fff not 0x8000 - 0x7fff */
1474: w = (((WORD) iBend + 0x8000) >> 2) & 0x3fff;
1475:
1476: for (i = 0; i < NUMVOICES; i++) {
1477: if ((voices[i].alloc) && (voices[i].channel == bChannel)) {
1478: SetVoicePitch(i, w);
1479: }
1480: }
1481: }
1482:
1483:
1484:
1485: /* Adlib_BoardInit - initialise board and load patches as necessary.
1486: *
1487: * inputs - none
1488: * returns - 0 for success or the error code
1489: */
1490: WORD Adlib_BoardInit(void)
1491: {
1492: BYTE i;
1493:
1494: wSynthAtten = 0;
1495:
1496: /* build the freq table */
1497: InitFNums();
1498:
1499: /* silence and free all voices */
1500: for (i = 0; i <= 8; i++) {
1501: SoundChut(i);
1502: FreeVoice(i);
1503: }
1504:
1505: /* switch to percussive mode and set fixed frequencies */
1506: SetFreq(TOM, TOM_PITCH, 0);
1507: SetFreq(SD, SD_PITCH, 0);
1508: percBits = 0;
1509: SndSAmVibRhythm();
1510:
1511: /* init all slots to sine-wave */
1512: for (i= 0; i < 18; i++) {
1513: SndOutput((BYTE)(0xE0 | offsetSlot[i]), 0);
1514: }
1515: /* enable wave-form selection */
1516: SndOutput(1, 0x20);
1517:
1518: LoadPatches();
1519: LoadDrumPatches();
1520:
1521: // don't initialise - the data is static and will thus
1522: // be initialised to 0 at load time. no other change should be made
1523: // since the mci sequencer will not re-send channel volume change
1524: // messages.
1525: //
1526: // /* init all channels to loudest */
1527: // for (i = 0; i < NUMCHANNELS; i++) {
1528: // gbChanAtten[i] = 4;
1529: // }
1530:
1531: return(0);
1532: }
1533:
1534:
1535: /*
1536: * Adlib_BoardReset - silence the board and set all voices off.
1537: */
1538: VOID Adlib_BoardReset(void)
1539: {
1540: BYTE i;
1541:
1542: /* silence and free all voices */
1543: for (i = 0; i <= 8; i++) {
1544: SoundChut(i);
1545: FreeVoice(i);
1546: }
1547:
1548: /* switch to percussive mode and set fixed frequencies */
1549: SetFreq(TOM, TOM_PITCH, 0);
1550: SetFreq(SD, SD_PITCH, 0);
1551: percBits = 0;
1552: SndSAmVibRhythm();
1553:
1554: }
1555:
1556:
1557:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.