Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

922 lines
26 KiB

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