|
|
1.1 root 1: /*
2: * Copyright (c) 1992 Microsoft Corporation
3: */
4:
5: /*
6: * Interface functions for the OPL3 midi device type.
7: *
8: * These functions are called from midi.c when the kernel driver
9: * has decreed that this is an opl3-compatible device.
10: *
11: * Geraint Davies, Dec 92
12: */
13:
14: #include <windows.h>
15: #include <mmsystem.h>
16: #include <mmddk.h>
17: #include "driver.h"
18: #include "opl3.h"
19:
20: /* --- typedefs ----------------------------------------------- */
21:
22: /* typedefs for MIDI patches */
23: #define NUMOPS (4)
24: #define PATCH_1_4OP (0) /* use 4-operator patch */
25: #define PATCH_2_2OP (1) /* use two 2-operator patches */
26: #define PATCH_1_2OP (2) /* use one 2-operator patch */
27:
28: #define RIFF_PATCH (mmioFOURCC('P','t','c','h'))
29: #define RIFF_FM4 (mmioFOURCC('f','m','4',' '))
30: #define NUM4VOICES (6) /* # 4-op voices that can play at once */
31: #define NUM2VOICES (6) /* # 2operator voices */
32: #define NUMVOICES (NUM4VOICES + NUM2VOICES)
33:
34: typedef struct _operStruct {
35: BYTE bAt20; /* flags which are send to 0x20 on fm */
36: BYTE bAt40; /* flags seet to 0x40 */
37: /* the note velocity & midi velocity affect total level */
38: BYTE bAt60; /* flags sent to 0x60 */
39: BYTE bAt80; /* flags sent to 0x80 */
40: BYTE bAtE0; /* flags send to 0xe0 */
41: } operStruct;
42:
43: typedef struct _noteStruct {
44: operStruct op[NUMOPS]; /* operators */
45: BYTE bAtA0[2]; /* send to 0xA0, A3 */
46: BYTE bAtB0[2]; /* send to 0xB0, B3 */
47: /* use in a patch, the block should be 4 to indicate
48: normal pitch, 3 => octave below, etc. */
49: BYTE bAtC0[2]; /* sent to 0xc0, C3 */
50: BYTE bOp; /* see PATCH_??? */
51: BYTE bDummy; /* place holder */
52: } noteStruct;
53:
54:
55: typedef struct _patchStruct {
56: noteStruct note; /* note. This is all in the structure at the moment */
57: } patchStruct;
58:
59: /* MIDI */
60:
61: typedef struct _voiceStruct {
62: BYTE bNote; /* note played */
63: BYTE bChannel; /* channel played on */
64: BYTE bPatch; /* what patch is the note,
65: drums patch = drum note + 128 */
66: BYTE bOn; /* TRUE if note is on, FALSE if off */
67: BYTE bVelocity; /* velocity */
68: BYTE bJunk; /* filler */
69: DWORD dwTime; /* time that was turned on/off;
70: 0 time indicates that its not in use */
71: DWORD dwOrigPitch[2]; /* original pitch, for pitch bend */
72: BYTE bBlock[2]; /* value sent to the block */
73: } voiceStruct;
74:
75: /* --- module data -------------------------------------------- */
76:
77: /* a bit of tuning information */
78: #define FSAMP (50000.0) /* sampling frequency */
79: #define PITCH(x) ((DWORD)((x) * (double) (1L << 19) / FSAMP))
80: /* x is the desired frequency,
81: == FNUM at b=1 */
82: #define EQUAL (1.059463094359)
83: #ifdef EUROPE
84: # define A (442.0)
85: #else
86: # define A (440.0)
87: #endif
88: #define ASHARP (A * EQUAL)
89: #define B (ASHARP * EQUAL)
90: #define C (B * EQUAL / 2.0)
91: #define CSHARP (C * EQUAL)
92: #define D (CSHARP * EQUAL)
93: #define DSHARP (D * EQUAL)
94: #define E (DSHARP * EQUAL)
95: #define F (E * EQUAL)
96: #define FSHARP (F * EQUAL)
97: #define G (FSHARP * EQUAL)
98: #define GSHARP (G * EQUAL)
99:
100: /* volume */
101: WORD wSynthAttenL = 0; /* in 1.5dB steps */
102: WORD wSynthAttenR = 0; /* in 1.5dB steps */
103:
104: /* patch library */
105: patchStruct FAR * glpPatch = NULL; /* points to the patches */
106:
107: /* voices being played */
108: voiceStruct gVoice[NUMVOICES]; /* info on what voice is where */
109: static DWORD gdwCurTime = 1; /* for note on/off */
110:
111: BYTE gbCur4opReg = 0; /* current value to 4-operator connection */
112:
113: /* channel volumes */
114: BYTE gbChanAtten[NUMCHANNELS]; /* attenuation of each channel, in .75 db steps */
115: BYTE gbStereoMask[NUMCHANNELS]; /* mask for left/right for stereo midi files */
116:
117: /* operator offset location */
118: static WORD BCODE gw4OpOffset[NUM4VOICES][NUMOPS] = {
119: {0x000,0x003,0x008,0x00b}, {0x001,0x004,0x009,0x00c},
120: {0x002,0x005,0x00a,0x00d}, {0x100,0x103,0x108,0x10b},
121: {0x101,0x104,0x109,0x10c}, {0x102,0x105,0x10a,0x10d}};
122: static WORD BCODE gw2OpOffset[NUM2VOICES][2] = {
123: {0x010,0x013}, {0x011,0x014}, {0x012,0x015},
124: {0x110,0x113}, {0x111,0x114}, {0x112,0x115}
125: };
126:
127: /* voice offset location */
128: static WORD BCODE gw4VoiceOffset[NUM4VOICES] = {
129: 0x000, 0x001, 0x002, 0x100, 0x101, 0x102};
130: static WORD BCODE gw2VoiceOffset[NUM2VOICES] = {
131: 0x006, 0x007, 0x008, 0x106, 0x107, 0x108};
132:
133: /* pitch values, from middle c, to octave above it */
134: static DWORD BCODE gdwPitch[12] = {
135: PITCH(C), PITCH(CSHARP), PITCH(D), PITCH(DSHARP),
136: PITCH(E), PITCH(F), PITCH(FSHARP), PITCH(G),
137: PITCH(GSHARP), PITCH(A), PITCH(ASHARP), PITCH(B)};
138:
139:
140:
141: /* --- internal functions -------------------------------------- */
142:
143:
144: /***************************************************************
145: Opl3_FMNote - This turns on a FM-synthesizer note.
146:
147: inputs
148: WORD wNote - the note number from 0 to NUMVOICES.
149: If wNote < NUM4VOICES then dealing with 4 operator synth,
150: else using 2-operator synth.
151: noteStruct FAR * lpSN - structure containing information about
152: what is to be played.
153: returns
154: none
155: */
156: VOID NEAR PASCAL Opl3_FMNote (WORD wNote, noteStruct FAR * lpSN)
157: {
158: WORD i;
159: WORD wOffset;
160: operStruct FAR * lpOS;
161:
162: // D1 ("\nOpl3_FMNote");
163: /* write out a note off, just to make sure */
164: MidiSendFM (AD_BLOCK +
165: ((wNote < NUM4VOICES) ? gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES]),
166: (BYTE)0);
167:
168: /* write out information specifying if we are
169: to use 2 or 4 operators */
170: if (wNote < NUM4VOICES) {
171: if (lpSN->bOp == PATCH_1_4OP)
172: gbCur4opReg |= (1 << wNote);
173: else
174: gbCur4opReg &= (~(1 << wNote));
175: MidiSendFM (AD_CONNECTION, gbCur4opReg);
176: };
177: /* else send out nothing */
178:
179: /* writing the operator information */
180: for (i = 0; i < (WORD)((wNote < NUM4VOICES) ? NUMOPS : 2); i++) {
181: wOffset = (wNote < NUM4VOICES) ?
182: gw4OpOffset[wNote][i] : gw2OpOffset[wNote - NUM4VOICES][i];
183: lpOS = &lpSN->op[i];
184: MidiSendFM (0x20 + wOffset, lpOS->bAt20);
185: MidiSendFM (0x40 + wOffset, lpOS->bAt40);
186: MidiSendFM (0x60 + wOffset, lpOS->bAt60);
187: MidiSendFM (0x80 + wOffset, lpOS->bAt80);
188: MidiSendFM (0xE0 + wOffset, lpOS->bAtE0);
189: };
190: /* write out the voice information */
191: wOffset = (wNote < NUM4VOICES) ?
192: gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES];
193: MidiSendFM (0xa0 + wOffset, lpSN->bAtA0[0]);
194: MidiSendFM (0xc0 + wOffset, lpSN->bAtC0[0]);
195: if (wNote < NUM4VOICES) {
196: MidiSendFM (0xc3 + wOffset, lpSN->bAtC0[1]);
197: MidiSendFM (0xa3 + wOffset, lpSN->bAtA0[1]);
198: MidiSendFM (0xb3 + wOffset, (BYTE)(lpSN->bAtB0[1] | 0x20) /* note on */);
199: };
200: MidiSendFM (0xb0 + wOffset, (BYTE)(lpSN->bAtB0[0] | 0x20) /* note on */);
201:
202: /* done */
203: }
204:
205: /***************************************************************
206: Opl3_FindEmptySlot - This finds an empty note-slot for a MIDI voice.
207: If there are no empty slots then this looks for the oldest
208: off note. It this doesnt work then it looks\
209: for the oldest on-note of the same patch. If all notes are still on then this
210: finds the oldest turned-on-note.
211:
212: inputs
213: BYTE bPatch - MIDI patch that will replace it
214: BYTE b4Op - if TRUE then looking through 4-operator voices,
215: else looking through 2-operator voices
216: returns
217: WORD - note slot #
218: */
219: WORD NEAR PASCAL Opl3_FindEmptySlot (BYTE bPatch, BYTE b4Op)
220: {
221: WORD i, found;
222: DWORD dwOldest;
223:
224: // D1 ("\nOpl3_FindEmptySlot");
225: /* first, look for a slot with a time == 0 */
226: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
227: if (!gVoice[i].dwTime)
228: return i;
229:
230: /* now, look for a slot of the oldest off-note */
231: dwOldest = 0xffffffff;
232: found = 0xffff;
233: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
234: if (!gVoice[i].bOn && (gVoice[i].dwTime < dwOldest)) {
235:
236: dwOldest = gVoice[i].dwTime;
237: found = i;
238: };
239: if (found != 0xffff)
240: return found;
241:
242: /* now, look for a slot of the oldest note with the same patch */
243: dwOldest = 0xffffffff;
244: found = 0xffff;
245: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
246: if ((gVoice[i].bPatch == bPatch) &&
247: (gVoice[i].dwTime < dwOldest)) {
248:
249: dwOldest = gVoice[i].dwTime;
250: found = i;
251: };
252: if (found != 0xffff)
253: return found;
254:
255: /* now, just look for the oldest voice */
256: found = (b4Op ? 0 : NUM4VOICES);
257: dwOldest = gVoice[found].dwTime;
258: for (i = (found + 1); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
259: if (gVoice[i].dwTime < dwOldest) {
260:
261: dwOldest = gVoice[i].dwTime;
262: found = i;
263: };
264:
265: return found;
266: }
267:
268:
269: /***************************************************************
270: Opl3_FindFullSlot - This finds a slot with a specific note,
271: and channel. If it is not found then 0xffff is
272: returned.
273:
274: inputs
275: BYTE bNote - MIDI note number
276: BYTE bChannel - MIDI channel #
277: returns
278: WORD - note slot #, or 0xffff if cant find
279: */
280: WORD NEAR PASCAL Opl3_FindFullSlot (
281: BYTE bNote, BYTE bChannel)
282: {
283: WORD i;
284:
285: // D1("\nOpl3_FindFullSlot");
286: for (i = 0; i < NUMVOICES; i++)
287: if ((bChannel == gVoice[i].bChannel) &&
288: (bNote == gVoice[i].bNote) && (gVoice[i].bOn))
289: return i;
290:
291: /* couldnt find it */
292: return 0xffff;
293: }
294:
295: /**************************************************************
296: Opl3_CalcBend - This calculates the effects of pitch bend
297: on an original value.
298:
299: inputs
300: DWORD dwOrig - original frequency
301: short iBend - from -32768 to 32768, -2 half steps to +2
302: returns
303: DWORD - new frequency
304: */
305: DWORD NEAR PASCAL Opl3_CalcBend (DWORD dwOrig, short iBend)
306: {
307: DWORD dw;
308: // D1 ("\nOpl3_CalcBend");
309:
310: /* do different things depending upon positive or
311: negative bend */
312: if (iBend > 0)
313: {
314: dw = (DWORD)((iBend * (LONG)(256.0 * (EQUAL * EQUAL - 1.0))) >> 8);
315: dwOrig += (DWORD)(AsULMUL(dw, dwOrig) >> 15);
316: }
317: else if (iBend < 0)
318: {
319: dw = (DWORD)(((-iBend) * (LONG)(256.0 * (1.0 - 1.0 / EQUAL / EQUAL))) >> 8);
320: dwOrig -= (DWORD)(AsULMUL(dw, dwOrig) >> 15);
321: }
322:
323: return dwOrig;
324: }
325:
326:
327: /*****************************************************************
328: Opl3_CalcFAndB - Calculates the FNumber and Block given
329: a frequency.
330:
331: inputs
332: DWORD dwPitch - pitch
333: returns
334: WORD - High byte contains the 0xb0 section of the
335: block and fNum, and the low byte contains the
336: 0xa0 section of the fNumber
337: */
338: WORD NEAR PASCAL Opl3_CalcFAndB (DWORD dwPitch)
339: {
340: BYTE bBlock;
341:
342: // D1("\nOpl3_CalcFAndB");
343: /* bBlock is like an exponential to dwPitch (or FNumber) */
344: for (bBlock = 1; dwPitch >= 0x400; dwPitch >>= 1, bBlock++)
345: ;
346:
347: if (bBlock > 0x07)
348: bBlock = 0x07; /* we cant do anything about this */
349:
350: /* put in high two bits of F-num into bBlock */
351: return ((WORD) bBlock << 10) | (WORD) dwPitch;
352: }
353:
354:
355: /**************************************************************
356: Opl3_CalcVolume - This calculates the attenuation for an operator.
357:
358: inputs
359: BYTE bOrigAtten - original attenuation in 0.75 dB units
360: BYTE bChannel - MIDI channel
361: BYTE bVelocity - velocity of the note
362: BYTE bOper - operator number (from 0 to 3)
363: BYTE bMode - voice mode (from 0 through 7 for
364: modulator/carrier selection)
365: returns
366: BYTE - new attenuation in 0.75 dB units, maxing out at 0x3f.
367: */
368: BYTE NEAR PASCAL Opl3_CalcVolume (BYTE bOrigAtten, BYTE bChannel,
369: BYTE bVelocity, BYTE bOper, BYTE bMode)
370: {
371: BYTE bVolume;
372: WORD wTemp;
373: WORD wMin;
374:
375: switch (bMode) {
376: case 0:
377: bVolume = (BYTE)(bOper == 3);
378: break;
379: case 1:
380: bVolume = (BYTE)((bOper == 1) || (bOper == 3));
381: break;
382: case 2:
383: bVolume = (BYTE)((bOper == 0) || (bOper == 3));
384: break;
385: case 3:
386: bVolume = (BYTE)(bOper != 1);
387: break;
388: case 4:
389: bVolume = (BYTE)((bOper == 1) || (bOper == 3));
390: break;
391: case 5:
392: bVolume = (BYTE)(bOper >= 1);
393: break;
394: case 6:
395: bVolume = (BYTE)(bOper <= 2);
396: break;
397: case 7:
398: bVolume = TRUE;
399: break;
400: };
401: if (!bVolume)
402: return bOrigAtten; /* this is a modulator wave */
403:
404: wMin =(wSynthAttenL < wSynthAttenR) ? wSynthAttenL : wSynthAttenR;
405: wTemp = bOrigAtten + ((wMin << 1) +
406: gbChanAtten[bChannel] + gbVelocityAtten[bVelocity >> 2]);
407: return (wTemp > 0x3f) ? (BYTE) 0x3f : (BYTE) wTemp;
408: }
409:
410: /**************************************************************
411: Opl3_CalcStereoMask - This calculates the stereo mask.
412:
413: inputs
414: BYTE bChannel - MIDI channel
415: returns
416: BYTE - mask (for register 0xc0-c8) for eliminating the
417: left/right/both channels
418: */
419: BYTE NEAR PASCAL Opl3_CalcStereoMask (BYTE bChannel)
420: {
421: WORD wLeft, wRight;
422:
423: /* figure out the basic levels of the 2 channels */
424: wLeft = (wSynthAttenL << 1) + gbChanAtten[bChannel];
425: wRight = (wSynthAttenR << 1) + gbChanAtten[bChannel];
426:
427: /* if both are too quiet then mask to nothing */
428: if ((wLeft > 0x3f) && (wRight > 0x3f))
429: return 0xcf;
430:
431: /* if one channel is significantly quieter than the other than
432: eliminate it */
433: if ((wLeft + 8) < wRight)
434: return (BYTE)(0xef & gbStereoMask[bChannel]); /* right is too quiet so eliminate */
435: else if ((wRight + 8) < wLeft)
436: return (BYTE)(0xdf & gbStereoMask[bChannel]); /* left too quiet so eliminate */
437: else
438: return (BYTE)(gbStereoMask[bChannel]); /* use both channels */
439: }
440:
441:
442:
443: /*
444: * opl3_setvolume
445: *
446: * set the volume on channel bChannel (all channels if 0xff).
447: */
448: static VOID Opl3_setvolume(BYTE bChannel)
449: {
450: WORD i, j, wTemp, wOffset;
451: noteStruct FAR * lpPS;
452: BYTE b4Op, bMode, bStereo;
453:
454: /* loop through all the notes looking for the right
455: * channel. Anything with the right channel gets its vol. changed
456: */
457: for (i = 0; i < NUMVOICES; i++) {
458: if ((gVoice[i].bChannel == bChannel) || (bChannel == 0xff)) {
459:
460: /* get a pointer to the patch*/
461: lpPS = &(glpPatch + gVoice[i].bPatch)->note;
462: b4Op = (BYTE)(lpPS->bOp != PATCH_1_2OP);
463:
464: /* moify level for each operator, but only if they
465: are carrier waves*/
466: if (b4Op) {
467: bMode = (BYTE)((lpPS->bAtC0[0] & 0x01) * 2 |
468: (lpPS->bAtC0[1] & 0x01));
469: if (lpPS->bOp == PATCH_2_2OP)
470: bMode += 4;
471: }
472: else
473: bMode = (BYTE) ( (lpPS->bAtC0[0] & 0x01) * 2 + 4);
474:
475: for (j = 0; j < (WORD)(b4Op ? NUMOPS : 2); j++) {
476: wTemp = (BYTE) Opl3_CalcVolume (
477: (BYTE)(lpPS->op[j].bAt40 & (BYTE) 0x3f), gVoice[i].bChannel,
478: gVoice[i].bVelocity, (BYTE) j, bMode);
479:
480: /* write the new value out */
481: wOffset = (i < NUM4VOICES) ?
482: gw4OpOffset[i][j] : gw2OpOffset[i - NUM4VOICES][j];
483: MidiSendFM (0x40 + wOffset,
484: (BYTE) ((lpPS->op[j].bAt40 & (BYTE)0xc0) | (BYTE) wTemp));
485: };
486:
487: /* do stereo panning, but cutting off a left or right
488: channel if necessary */
489: bStereo = Opl3_CalcStereoMask(gVoice[i].bChannel);
490: wOffset = (i < NUM4VOICES) ?
491: gw4VoiceOffset[i] : gw2VoiceOffset[i - NUM4VOICES];
492: MidiSendFM (0xc0 + wOffset,
493: (BYTE)(lpPS->bAtC0[0] & bStereo));
494: if (b4Op)
495: MidiSendFM (0xc3 + wOffset,
496: (BYTE) (lpPS->bAtC0[1] & bStereo));
497: }
498: }
499: }
500:
501: /* --- externally called functions ---------------------------- */
502:
503: /*
504: * Opl3_NoteOn - This turns a note on. (Including drums, with
505: * a patch # of the drum Note + 128)
506: *
507: * inputs
508: * BYTE bPatch - MIDI patch number
509: * BYTE bNote - MIDI note number
510: * BYTE bChannel - MIDI channel #
511: * BYTE bVelocity - Velocity #
512: * short iBend - current pitch bend from -32768, to 32767
513: * returns
514: * none
515: */
516: VOID NEAR PASCAL Opl3_NoteOn (BYTE bPatch,
517: BYTE bNote, BYTE bChannel, BYTE bVelocity,
518: short iBend)
519: {
520: WORD wTemp, i, j;
521: BYTE bTemp, bMode, bStereo;
522: patchStruct FAR * lpPS;
523: DWORD dwBasicPitch, dwPitch[2];
524: noteStruct NS;
525: BYTE b4Op; /* use a 4-operator voice */
526:
527: /* get a pointer to the patch*/
528: lpPS = glpPatch + bPatch;
529:
530: /* find out the basic pitch according to our
531: note. This may be adjusted because of
532: pitch bends or special qualities for the note */
533: dwBasicPitch = gdwPitch[bNote % 12];
534: bTemp = bNote / (BYTE)12;
535: if (bTemp > (BYTE) (60 / 12))
536: dwBasicPitch = AsLSHL(dwBasicPitch, (BYTE)(bTemp - (BYTE)(60/12)));
537: else if (bTemp < (BYTE) (60/12))
538: dwBasicPitch = AsULSHR(dwBasicPitch, (BYTE)((BYTE) (60/12) - bTemp));
539:
540: /* copy the note information over and modify
541: the total level and pitch according to
542: the velocity, midi volume, and tuning */
543: AsMemCopy ((LPSTR) &NS, (LPSTR) &lpPS->note, sizeof(noteStruct));
544: b4Op = (BYTE)(NS.bOp != PATCH_1_2OP);
545:
546: for (j = 0; j < (WORD)(b4Op ? 2 : 1); j++) {
547: /* modify pitch */
548: dwPitch[j] = dwBasicPitch;
549: bTemp = (BYTE)((NS.bAtB0[j] >> 2) & 0x07);
550: if (bTemp > 4)
551: dwPitch[j] = AsLSHL(dwPitch[j], (BYTE)(bTemp - (BYTE)4));
552: else if (bTemp < 4)
553: dwPitch[j] = AsULSHR(dwPitch[j], (BYTE)((BYTE)4 - bTemp));
554:
555: wTemp = Opl3_CalcFAndB (Opl3_CalcBend (dwPitch[j], iBend));
556: NS.bAtA0[j] = (BYTE) wTemp;
557: NS.bAtB0[j] = (BYTE)0x20 | (BYTE) (wTemp >> 8);
558: };
559:
560: /* modify level for each operator, but only if they
561: are carrier waves*/
562: if (b4Op) {
563: bMode = (BYTE)((NS.bAtC0[0] & 0x01) * 2 | (NS.bAtC0[1] & 0x01));
564: if (NS.bOp == PATCH_2_2OP)
565: bMode += 4;
566: }
567: else
568: bMode = (BYTE) ( (NS.bAtC0[0] & 0x01) * 2 + 4);
569:
570: for (i = 0; i < (WORD)(b4Op ? NUMOPS : 2); i++) {
571: wTemp = (BYTE) Opl3_CalcVolume (
572: (BYTE)(NS.op[i].bAt40 & (BYTE) 0x3f), bChannel,
573: bVelocity, (BYTE) i, bMode);
574: NS.op[i].bAt40 = (NS.op[i].bAt40 & (BYTE)0xc0) | (BYTE) wTemp;
575: };
576:
577: /* do stereo panning, but cutting off a left or right
578: channel if necessary */
579: bStereo = Opl3_CalcStereoMask(bChannel);
580: NS.bAtC0[0] &= bStereo;
581: if (b4Op)
582: NS.bAtC0[1] &= bStereo;
583:
584: /* find an empty slot, and use it */
585: wTemp = Opl3_FindEmptySlot (bPatch, b4Op);
586: Opl3_FMNote (wTemp, &NS);
587: gVoice[wTemp].bNote = bNote;
588: gVoice[wTemp].bChannel = bChannel;
589: gVoice[wTemp].bPatch = bPatch;
590: gVoice[wTemp].bVelocity = bVelocity;
591: gVoice[wTemp].bOn = TRUE;
592: gVoice[wTemp].dwTime = gdwCurTime++;
593: gVoice[wTemp].dwOrigPitch[0] = dwPitch[0]; /* not including bend */
594: gVoice[wTemp].dwOrigPitch[1] = dwPitch[1]; /* not including bend */
595: gVoice[wTemp].bBlock[0] = NS.bAtB0[0];
596: gVoice[wTemp].bBlock[1] = NS.bAtB0[1];
597:
598: return;
599: }
600:
601:
602: /* Opl3_NoteOff - This turns a note off. (Including drums,
603: * with a patch # of the drum note + 128)
604: *
605: * inputs
606: * BYTE bPatch - MIDI patch #
607: * BYTE bNote - MIDI note number
608: * BYTE bChannel - MIDI channel #
609: * returns
610: * none
611: */
612: VOID FAR PASCAL Opl3_NoteOff (BYTE bPatch,
613: BYTE bNote, BYTE bChannel)
614: {
615: WORD wTemp;
616:
617: // D1("\nOpl3_NoteOff");
618:
619: /* find the note slot */
620: wTemp = Opl3_FindFullSlot (bNote, bChannel);
621: if (wTemp != 0xffff) {
622:
623: /* shut off the note portion */
624: /* we have the note slot. turn it off */
625: if (wTemp < NUM4VOICES) {
626: MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp],
627: (BYTE)(gVoice[wTemp].bBlock[0] & 0x1f));
628: MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp] + 3,
629: (BYTE)(gVoice[wTemp].bBlock[1] & 0x1f));
630: }
631: else
632: MidiSendFM (AD_BLOCK + gw2VoiceOffset[wTemp - NUM4VOICES],
633: (BYTE)(gVoice[wTemp].bBlock[0] & 0x1f));
634:
635: /* note this */
636: gVoice[wTemp].bOn = FALSE;
637: gVoice[wTemp].bBlock[0] &= 0x1f;
638: gVoice[wTemp].bBlock[1] &= 0x1f;
639: gVoice[wTemp].dwTime = gdwCurTime;
640: };
641:
642: }
643:
644: /*
645: * Opl3_AllNotesOff - turn off all notes
646: *
647: */
648: VOID Opl3_AllNotesOff(void)
649: {
650: BYTE i;
651:
652: for (i = 0; i < NUMVOICES; i++) {
653: Opl3_NoteOff (gVoice[i].bPatch, gVoice[i].bNote, gVoice[i].bChannel);
654: }
655: }
656:
657:
658:
659: /* Opl3_NewVolume - This should be called if a volume level
660: * has changed. This will adjust the levels of all the playing
661: * voices.
662: *
663: * inputs
664: * WORD wLeft - left attenuation (1.5 db units)
665: * WORD wRight - right attenuation (ignore if mono)
666: * returns
667: * none
668: */
669: VOID FAR PASCAL Opl3_NewVolume (WORD wLeft, WORD wRight)
670: {
671:
672: /* make sure that we are actually open */
673: if (!glpPatch)
674: return;
675:
676: wSynthAttenL = wLeft;
677: wSynthAttenR = wRight;
678:
679: Opl3_setvolume(0xff);
680:
681: }
682:
683:
684:
685: /* Opl3_ChannelVolume - set the volume level for an individual channel.
686: *
687: * inputs
688: * BYTE bChannel - channel number to change
689: * WORD wAtten - attenuation in 1.5 db units
690: *
691: * returns
692: * none
693: */
694: VOID FAR PASCAL Opl3_ChannelVolume(BYTE bChannel, WORD wAtten)
695: {
696: gbChanAtten[bChannel] = wAtten;
697:
698: Opl3_setvolume(bChannel);
699: }
700:
701:
702:
703: /* Opl3_SetPan - set the left-right pan position.
704: *
705: * inputs
706: * BYTE bChannel - channel number to alter
707: * BYTE bPan - 0 for left, 127 for right or somewhere in the middle.
708: *
709: * returns - none
710: */
711: VOID FAR PASCAL Opl3_SetPan(BYTE bChannel, BYTE bPan)
712: {
713: /* change the pan level */
714: if (bPan > (64 + 16))
715: gbStereoMask[bChannel] = 0xef; /* let only right channel through */
716: else if (bPan < (64 - 16))
717: gbStereoMask[bChannel] = 0xdf; /* let only left channel through */
718: else
719: gbStereoMask[bChannel] = 0xff; /* let both channels */
720:
721: /* change any curently playing patches */
722: Opl3_setvolume(bChannel);
723: }
724:
725:
726: /* Opl3_PitchBend - This pitch bends a channel.
727: *
728: * inputs
729: * BYTE bChannel - channel
730: * short iBend - Values from -32768 to 32767, being
731: * -2 to +2 half steps
732: * returns
733: * none
734: */
735: VOID NEAR PASCAL Opl3_PitchBend (BYTE bChannel, short iBend)
736: {
737: WORD i, wTemp[2], j;
738: DWORD dwNew;
739:
740: /* loop through all the notes looking for the right
741: * channel. Anything with the right channel gets its pitch bent
742: */
743: for (i = 0; i < NUMVOICES; i++) {
744: if (gVoice[i].bChannel == bChannel) {
745: for (j = 0; j < (WORD)((i < NUM4VOICES) ? 2 : 1); j++) {
746: dwNew = Opl3_CalcBend (gVoice[i].dwOrigPitch[j], iBend);
747: wTemp[j] = Opl3_CalcFAndB (dwNew);
748: gVoice[i].bBlock[j] = (gVoice[i].bBlock[j] & (BYTE)0xe0) |
749: (BYTE) (wTemp[j] >> 8);
750: }
751:
752: if (i < NUM4VOICES) {
753: MidiSendFM (AD_BLOCK + gw4VoiceOffset[i],
754: gVoice[i].bBlock[0]);
755: MidiSendFM (AD_BLOCK + gw4VoiceOffset[i] + 3,
756: gVoice[i].bBlock[1]);
757: MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i],
758: (BYTE) wTemp[0]);
759: MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i] + 3,
760: (BYTE) wTemp[1]);
761: } else {
762: MidiSendFM (AD_BLOCK + gw2VoiceOffset[i - NUM4VOICES],
763: gVoice[i].bBlock[0]);
764: MidiSendFM (AD_FNUMBER + gw2VoiceOffset[i - NUM4VOICES],
765: (BYTE) wTemp[0]);
766: }
767: }
768: }
769: }
770:
771: static TCHAR BCODE gszDefPatchLib[] = TEXT("SYNTH.PAT");
772: static TCHAR BCODE gszIniKeyPatchLib[] = INI_STR_PATCHLIB;
773: static TCHAR BCODE gszIniDrvSection[] = INI_DRIVER;
774: static TCHAR BCODE gszIniDrvFile[] = INI_SOUND;
775: static TCHAR BCODE gszSysIniSection[] = TEXT("synth.dll");
776: static TCHAR BCODE gszSysIniFile[] = TEXT("System.Ini");
777:
778: /** static DWORD NEAR PASCAL DrvGetProfileString(LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, UINT wBufLen)
779: *
780: * DESCRIPTION:
781: *
782: *
783: * ARGUMENTS:
784: * (LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, WORD wBufLen)
785: * HINT wSystem - if TRUE write/read to system.ini
786: *
787: * RETURN (static DWORD NEAR PASCAL):
788: *
789: *
790: * NOTES:
791: *
792: ** cjp */
793:
794: static DWORD NEAR PASCAL DrvGetProfileString(LPTSTR szKeyName, LPTSTR szDef, LPTSTR szBuf, UINT wBufLen,
795: UINT wSystem)
796: {
797: return GetPrivateProfileString(wSystem ? gszSysIniSection : gszIniDrvSection, szKeyName, szDef,
798: szBuf, wBufLen, wSystem ? gszSysIniFile : gszIniDrvFile);
799: } /* DrvGetProfileString() */
800:
801: /* Opl3_BoardInit - initialise board and load patches as necessary.
802: *
803: * inputs - none
804: * returns - 0 for success or the error code
805: */
806: WORD Opl3_BoardInit(void)
807: {
808: HMMIO hmmio;
809: MMCKINFO mmckinfo, mm2;
810: TCHAR szPatchLib[STRINGLEN]; /* patch libarary */
811:
812: D1 ("\nOpl3_Init");
813:
814: /* Check we haven't already initialized */
815: if (glpPatch != NULL) {
816: return 0;
817: }
818:
819: /* should the load patches be moved to the init section? */
820: DrvGetProfileString(gszIniKeyPatchLib, gszDefPatchLib,
821: szPatchLib, sizeof(szPatchLib), FALSE);
822:
823:
824: /* allocate the memory, and fill it up from the patch
825: * library. The name of the library has been set previously
826: * and written into szPatchLib
827: */
828: glpPatch = (patchStruct FAR *)GlobalLock(GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(patchStruct) * NUMPATCHES));
829: if (!glpPatch) {
830: D1 ("Opl3_Init: could not allocate patch container memory!");
831: return ERR_OUTOFMEMORY;
832: }
833:
834: hmmio = mmioOpen (szPatchLib, NULL, MMIO_READ);
835: if (hmmio) {
836: mmioDescend (hmmio, &mmckinfo, NULL, 0);
837: if ((mmckinfo.ckid == FOURCC_RIFF) &&
838: (mmckinfo.fccType == RIFF_PATCH)) {
839:
840: mm2.ckid = RIFF_FM4;
841: if (!mmioDescend (hmmio, &mm2, &mmckinfo, MMIO_FINDCHUNK)) {
842: /* we have found the synthesis chunk */
843: if (mm2.cksize > (sizeof(patchStruct)*NUMPATCHES))
844: mm2.cksize = sizeof(patchStruct)*NUMPATCHES;
845: mmioRead (hmmio, (LPSTR) glpPatch, mm2.cksize);
846: } else {
847: D1("\nBad mmioDescend2");
848: };
849: } else {
850: D1("\nBad mmioDescend1");
851: };
852:
853: mmioClose (hmmio, 0);
854: } else {
855:
856: TCHAR szAlert[50];
857: TCHAR szErrorBuffer[255];
858:
859: LoadString(ghModule, SR_ALERT, szAlert, sizeof(szAlert));
860: LoadString(ghModule, SR_ALERT_NOPATCH, szErrorBuffer, sizeof(szErrorBuffer));
861: MessageBox(NULL, szErrorBuffer, szAlert, MB_OK|MB_ICONHAND);
862: D1 ("\nBad mmioOpen");
863: };
864:
865:
866: return 0; /* done */
867: }
868:
869:
870: /*
871: * Opl3_BoardReset - silence the board and set all voices off.
872: */
873: VOID Opl3_BoardReset(void)
874: {
875:
876: BYTE i;
877:
878: /* make sure all notes turned off */
879: Opl3_AllNotesOff();
880:
881:
882: /* ---- silence the chip -------- */
883:
884: /* tell the FM chip to use 4-operator mode, and
885: fill in any other random variables */
886: MidiSendFM (AD_NEW, 0x01);
887: MidiSendFM (AD_MASK, 0x60);
888: MidiSendFM (AD_CONNECTION, 0x3f);
889: MidiSendFM (AD_NTS, 0x00);
890:
891:
892: /* turn off the drums, and use high vibrato/modulation */
893: MidiSendFM (AD_DRUM, 0xc0);
894:
895: /* turn off all the oscillators */
896: for (i = 0; i < 0x15; i++) {
897: MidiSendFM (AD_LEVEL + i, 0x3f);
898: MidiSendFM (AD_LEVEL2 + i, 0x3f);
899: };
900:
901: /* turn off all the voices */
902: for (i = 0; i < 0x08; i++) {
903: MidiSendFM (AD_BLOCK + i, 0x00);
904: MidiSendFM (AD_BLOCK2 + i, 0x00);
905: };
906:
907:
908: /* clear all of the voice slots to empty */
909: for (i = 0; i < NUMVOICES; i++)
910: gVoice[i].dwTime = 0;
911:
912: /* start attenuations at -3 dB, which is 90 MIDI level */
913: for (i = 0; i < NUMCHANNELS; i++) {
914: gbChanAtten[i] = 4;
915: gbStereoMask[i] = 0xff;
916: };
917:
918: }
919:
920:
921:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.