Source code of Windows XP (NT5)
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.

550 lines
16 KiB

  1. //
  2. // Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
  3. // MIDI.cpp
  4. //
  5. #include "common.h"
  6. #define STR_MODULENAME "DDKSynth.sys:Midi: "
  7. #include "math.h"
  8. #pragma code_seg()
  9. #pragma data_seg()
  10. CMIDIDataList * CMIDIRecorder::m_sFreeList = (CMIDIDataList *) NULL;
  11. ULONG CMIDIRecorder::sm_cRefCnt = 0;
  12. //
  13. // Array for converting MIDI to volume.
  14. // value = log10((index/127)^4)*1000 where index = 0..127
  15. //
  16. const VREL vrMIDIToVREL[] = {
  17. // 0 1 2 3 4 5 6 7
  18. -9600, -8415, -7211, -6506, -6006, -5619, -5302, -5034,
  19. -4802, -4598, -4415, -4249, -4098, -3959, -3830, -3710,
  20. -3598, -3493, -3394, -3300, -3211, -3126, -3045, -2968,
  21. -2894, -2823, -2755, -2689, -2626, -2565, -2506, -2449,
  22. -2394, -2341, -2289, -2238, -2190, -2142, -2096, -2050,
  23. -2006, -1964, -1922, -1881, -1841, -1802, -1764, -1726,
  24. -1690, -1654, -1619, -1584, -1551, -1518, -1485, -1453,
  25. -1422, -1391, -1361, -1331, -1302, -1273, -1245, -1217,
  26. -1190, -1163, -1137, -1110, -1085, -1059, -1034, -1010,
  27. -985, -961, -938, -914, -891, -869, -846, -824,
  28. -802, -781, -759, -738, -718, -697, -677, -657,
  29. -637, -617, -598, -579, -560, -541, -522, -504,
  30. -486, -468, -450, -432, -415, -397, -380, -363,
  31. -347, -330, -313, -297, -281, -265, -249, -233,
  32. -218, -202, -187, -172, -157, -142, -127, -113,
  33. -98, -84, -69, -55, -41, -27, -13, 0
  34. };
  35. /*****************************************************************************
  36. * Constructor for CMidiData object
  37. *****************************************************************************/
  38. /*****************************************************************************
  39. * CMIDIData::CMIDIData()
  40. *****************************************************************************
  41. * Constructor for this object.
  42. */
  43. CMIDIData::CMIDIData()
  44. {
  45. m_stTime = 0;
  46. m_lData = 0;
  47. }
  48. /*****************************************************************************
  49. * CMIDIRecorder::CMIDIRecorder()
  50. *****************************************************************************
  51. * Constructor for this object.
  52. */
  53. CMIDIRecorder::CMIDIRecorder()
  54. {
  55. m_lCurrentData = 0;
  56. m_stCurrentTime = 0;
  57. KeInitializeSpinLock(&m_SpinLock);
  58. ++sm_cRefCnt;
  59. }
  60. /*****************************************************************************
  61. * CMIDIRecorder::~CMIDIRecorder()
  62. *****************************************************************************
  63. * Destructor for this object.
  64. */
  65. CMIDIRecorder::~CMIDIRecorder()
  66. {
  67. ClearMIDI(0x7FFFFFFF);
  68. --sm_cRefCnt;
  69. if(sm_cRefCnt == 0)
  70. {
  71. if(CMIDIRecorder::m_sFreeList != NULL)
  72. {
  73. while(!CMIDIRecorder::m_sFreeList->IsEmpty())
  74. {
  75. CMIDIData *event;
  76. event = CMIDIRecorder::m_sFreeList->RemoveHead();
  77. delete event;
  78. }
  79. delete CMIDIRecorder::m_sFreeList;
  80. CMIDIRecorder::m_sFreeList = NULL;
  81. }
  82. }
  83. }
  84. /*****************************************************************************
  85. * CMIDIRecorder::Init()
  86. *****************************************************************************
  87. * Initialize the CMIDIRecorder object. Make a list of CMIDIData objects for later.
  88. */
  89. void CMIDIRecorder::Init()
  90. {
  91. int nIndex;
  92. if(CMIDIRecorder::m_sFreeList == NULL)
  93. {
  94. CMIDIRecorder::m_sFreeList = new(NonPagedPool,'LSmD') CMIDIDataList; // DmSL
  95. if(CMIDIRecorder::m_sFreeList != NULL)
  96. {
  97. CMIDIRecorder::m_sFreeList->RemoveAll();
  98. for(nIndex = 0; nIndex < MAX_MIDI_EVENTS; nIndex++)
  99. {
  100. CMIDIData* pEvent;
  101. pEvent = new(NonPagedPool,'MSmD') CMIDIData; // DmSM
  102. if(pEvent != NULL)
  103. {
  104. CMIDIRecorder::m_sFreeList->AddHead(pEvent);
  105. }
  106. }
  107. }
  108. }
  109. }
  110. /*****************************************************************************
  111. * CMIDIRecorder::GrabSpinLock()
  112. *****************************************************************************
  113. * In kernel mode, acquire the spinlock.
  114. */
  115. inline void CMIDIRecorder::GrabSpinLock()
  116. {
  117. KeAcquireSpinLock(&m_SpinLock, &m_OldIrql);
  118. }
  119. /*****************************************************************************
  120. * CMIDIRecorder::ReleaseSpinLock()
  121. *****************************************************************************
  122. * In kernel mode, release the spinlock.
  123. */
  124. inline void CMIDIRecorder::ReleaseSpinLock()
  125. {
  126. KeReleaseSpinLock(&m_SpinLock, m_OldIrql);
  127. }
  128. /*****************************************************************************
  129. * CMIDIRecorder::FlushMIDI()
  130. *****************************************************************************
  131. * Remove all events from the event list, and put them back on the free list.
  132. */
  133. BOOL CMIDIRecorder::FlushMIDI(STIME stTime)
  134. {
  135. GrabSpinLock();
  136. CMIDIData *pMD;
  137. CMIDIData *pLast = NULL;
  138. for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
  139. {
  140. if (pMD->m_stTime >= stTime)
  141. {
  142. if (pLast == NULL)
  143. {
  144. m_EventList.RemoveAll();
  145. }
  146. else
  147. {
  148. pLast->SetNext(NULL);
  149. }
  150. if (CMIDIRecorder::m_sFreeList)
  151. {
  152. CMIDIRecorder::m_sFreeList->Cat(pMD);
  153. }
  154. break;
  155. }
  156. pLast = pMD;
  157. }
  158. BOOL fIsEmpty = m_EventList.IsEmpty();
  159. ReleaseSpinLock();
  160. return fIsEmpty;
  161. }
  162. /*****************************************************************************
  163. * CMIDIRecorder::ClearMIDI()
  164. *****************************************************************************
  165. * Clear out any MIDI that is before the given time.
  166. */
  167. BOOL CMIDIRecorder::ClearMIDI(STIME stTime)
  168. {
  169. GrabSpinLock();
  170. CMIDIData *pMD;
  171. for (;pMD = m_EventList.GetHead();)
  172. {
  173. if (pMD->m_stTime < stTime)
  174. {
  175. m_EventList.RemoveHead();
  176. m_stCurrentTime = pMD->m_stTime;
  177. m_lCurrentData = pMD->m_lData;
  178. if (CMIDIRecorder::m_sFreeList)
  179. {
  180. CMIDIRecorder::m_sFreeList->AddHead(pMD);
  181. }
  182. else
  183. {
  184. delete pMD;
  185. }
  186. }
  187. else break;
  188. }
  189. BOOL fIsEmpty = m_EventList.IsEmpty();
  190. ReleaseSpinLock();
  191. return fIsEmpty;
  192. }
  193. /*****************************************************************************
  194. * CMIDIRecorder::VelocityToVolume()
  195. *****************************************************************************
  196. * Translate from 16-bit velocity to a VREL volume (dB).
  197. */
  198. VREL CMIDIRecorder::VelocityToVolume(WORD nVelocity)
  199. {
  200. return (::vrMIDIToVREL[nVelocity]);
  201. }
  202. /*****************************************************************************
  203. * CMIDIRecorder::RecordMIDI()
  204. *****************************************************************************
  205. * Queue up a given note at a given time. Use an element from the free pool,
  206. * or allocate a new one if the pool is exhausted.
  207. */
  208. BOOL CMIDIRecorder::RecordMIDI(STIME stTime, long lData)
  209. {
  210. GrabSpinLock();
  211. CMIDIData *pMD = NULL;
  212. if (CMIDIRecorder::m_sFreeList)
  213. {
  214. pMD = CMIDIRecorder::m_sFreeList->RemoveHead();
  215. }
  216. if (pMD == NULL)
  217. {
  218. pMD = new(NonPagedPool,'MSmD') CMIDIData; // DmSM
  219. }
  220. if (pMD != NULL)
  221. {
  222. CMIDIData *pScan = m_EventList.GetHead();
  223. CMIDIData *pNext;
  224. pMD->m_stTime = stTime;
  225. pMD->m_lData = lData;
  226. if (pScan == NULL)
  227. {
  228. m_EventList.AddHead(pMD);
  229. }
  230. else
  231. {
  232. if (pScan->m_stTime > stTime)
  233. {
  234. m_EventList.AddHead(pMD);
  235. }
  236. else
  237. {
  238. for (;pScan != NULL; pScan = pNext)
  239. {
  240. pNext = pScan->GetNext();
  241. if (pNext == NULL)
  242. {
  243. pScan->SetNext(pMD);
  244. }
  245. else
  246. {
  247. if (pNext->m_stTime > stTime)
  248. {
  249. pMD->SetNext(pNext);
  250. pScan->SetNext(pMD);
  251. break;
  252. }
  253. }
  254. }
  255. }
  256. }
  257. ReleaseSpinLock();
  258. return (TRUE);
  259. }
  260. ReleaseSpinLock();
  261. Trace(1,"MIDI Event pool empty.\n");
  262. return (FALSE);
  263. }
  264. /*****************************************************************************
  265. * CMIDIRecorder::GetData()
  266. *****************************************************************************
  267. * Retrieve any data that is at or before the given time.
  268. */
  269. long CMIDIRecorder::GetData(STIME stTime)
  270. {
  271. GrabSpinLock();
  272. CMIDIData *pMD = m_EventList.GetHead();
  273. long lData = m_lCurrentData;
  274. for (;pMD;pMD = pMD->GetNext())
  275. {
  276. if (pMD->m_stTime > stTime)
  277. {
  278. break;
  279. }
  280. lData = pMD->m_lData;
  281. }
  282. ReleaseSpinLock();
  283. return (lData);
  284. }
  285. /*****************************************************************************
  286. * CNoteIn::RecordNote()
  287. *****************************************************************************
  288. * Record the given note object into the NoteIn receptor.
  289. */
  290. BOOL CNoteIn::RecordNote(STIME stTime, CNote * pNote)
  291. {
  292. long lData = pNote->m_bPart << 16;
  293. lData |= pNote->m_bKey << 8;
  294. lData |= pNote->m_bVelocity;
  295. return (RecordMIDI(stTime,lData));
  296. }
  297. /*****************************************************************************
  298. * CNoteIn::RecordEvent()
  299. *****************************************************************************
  300. * Record the given note object into the NoteIn receptor.
  301. */
  302. BOOL CNoteIn::RecordEvent(STIME stTime, DWORD dwPart, DWORD dwCommand, BYTE bData)
  303. {
  304. long lData = dwPart;
  305. lData <<= 8;
  306. lData |= dwCommand;
  307. lData <<= 8;
  308. lData |= bData;
  309. return (RecordMIDI(stTime,lData));
  310. }
  311. /*****************************************************************************
  312. * CNoteIn::GetNote()
  313. *****************************************************************************
  314. * Retrieve the first note at or before the given time.
  315. */
  316. BOOL CNoteIn::GetNote(STIME stTime, CNote * pNote)
  317. {
  318. GrabSpinLock();
  319. CMIDIData *pMD = m_EventList.GetHead();
  320. if (pMD != NULL)
  321. {
  322. if (pMD->m_stTime <= stTime)
  323. {
  324. pNote->m_stTime = pMD->m_stTime;
  325. pNote->m_bPart = (BYTE) (pMD->m_lData >> 16);
  326. pNote->m_bKey = (BYTE) (pMD->m_lData >> 8) & 0xFF;
  327. pNote->m_bVelocity = (BYTE) pMD->m_lData & 0xFF;
  328. m_EventList.RemoveHead();
  329. if (CMIDIRecorder::m_sFreeList)
  330. {
  331. CMIDIRecorder::m_sFreeList->AddHead(pMD);
  332. }
  333. else
  334. {
  335. delete pMD;
  336. }
  337. ReleaseSpinLock();
  338. return (TRUE);
  339. }
  340. }
  341. ReleaseSpinLock();
  342. return (FALSE);
  343. }
  344. /*****************************************************************************
  345. * CNoteIn::FlushMIDI()
  346. *****************************************************************************
  347. * Flush out any MIDI after the given time, with attention given
  348. * to special events.
  349. */
  350. void CNoteIn::FlushMIDI(STIME stTime)
  351. {
  352. GrabSpinLock();
  353. CMIDIData *pMD;
  354. for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
  355. {
  356. if (pMD->m_stTime >= stTime)
  357. {
  358. pMD->m_stTime = stTime; // Play now.
  359. BYTE command = (BYTE) ((pMD->m_lData & 0x0000FF00) >> 8);
  360. if (command < NOTE_PROGRAMCHANGE)
  361. {
  362. pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
  363. }
  364. // otherwise it is a special command
  365. // so don't mess with the velocity
  366. }
  367. }
  368. ReleaseSpinLock();
  369. }
  370. /*****************************************************************************
  371. * CNoteIn::FlushPart()
  372. *****************************************************************************
  373. * Flush the given channel, with attention given to special events.
  374. */
  375. void CNoteIn::FlushPart(STIME stTime, BYTE bChannel)
  376. {
  377. GrabSpinLock();
  378. CMIDIData *pMD;
  379. for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
  380. {
  381. if (pMD->m_stTime >= stTime)
  382. {
  383. if (bChannel == (BYTE) (pMD->m_lData >> 16))
  384. {
  385. pMD->m_stTime = stTime; // Play now.
  386. BYTE command = (BYTE) ((pMD->m_lData & 0x0000FF00) >> 8);
  387. if (command < NOTE_PROGRAMCHANGE)
  388. {
  389. pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
  390. }
  391. // otherwise it is a special command
  392. // so don't mess with the velocity
  393. }
  394. }
  395. }
  396. ReleaseSpinLock();
  397. }
  398. /*****************************************************************************
  399. * CModWheelIn::GetModulation()
  400. *****************************************************************************
  401. * Get the modulation data.
  402. */
  403. DWORD CModWheelIn::GetModulation(STIME stTime)
  404. {
  405. DWORD nResult = CMIDIRecorder::GetData(stTime);
  406. return (nResult);
  407. }
  408. /*****************************************************************************
  409. * CPitchBendIn::CPitchBendIn()
  410. *****************************************************************************
  411. * Constructor for this object.
  412. */
  413. CPitchBendIn::CPitchBendIn()
  414. {
  415. m_lCurrentData = 0x2000; // initially at midpoint, no bend
  416. m_prRange = 200; // whole tone range by default.
  417. }
  418. /*****************************************************************************
  419. * CPitchBendIn::GetPitch()
  420. *****************************************************************************
  421. * Get the pitch data.
  422. * Note: we don't keep a time-stamped range.
  423. * If people are changing the pitch bend range often, this won't work right,
  424. * but that didn't seem likely enough to warrant a new list.
  425. */
  426. PREL CPitchBendIn::GetPitch(STIME stTime)
  427. {
  428. PREL prResult = (PREL) CMIDIRecorder::GetData(stTime);
  429. prResult -= 0x2000; // Subtract MIDI Midpoint.
  430. prResult *= m_prRange; // adjust by current range
  431. prResult >>= 13;
  432. return (prResult);
  433. }
  434. /*****************************************************************************
  435. * CVolumeIn::CVolumeIn()
  436. *****************************************************************************
  437. * Constructor for this object.
  438. */
  439. CVolumeIn::CVolumeIn()
  440. {
  441. m_lCurrentData = 100;
  442. }
  443. /*****************************************************************************
  444. * CVolumeIn::GetVolume()
  445. *****************************************************************************
  446. * Get the volume data.
  447. */
  448. VREL CVolumeIn::GetVolume(STIME stTime)
  449. {
  450. long lResult = CMIDIRecorder::GetData(stTime);
  451. return (::vrMIDIToVREL[lResult]);
  452. }
  453. /*****************************************************************************
  454. * CExpressionIn::CExpressionIn()
  455. *****************************************************************************
  456. * Constructor for this object.
  457. */
  458. CExpressionIn::CExpressionIn()
  459. {
  460. m_lCurrentData = 127;
  461. }
  462. /*****************************************************************************
  463. * CExpressionIn::GetVolume()
  464. *****************************************************************************
  465. * Get the volume data.
  466. */
  467. VREL CExpressionIn::GetVolume(STIME stTime)
  468. {
  469. long lResult = CMIDIRecorder::GetData(stTime);
  470. return (::vrMIDIToVREL[lResult]);
  471. }
  472. /*****************************************************************************
  473. * CPanIn::CPanIn()
  474. *****************************************************************************
  475. * Constructor for this object.
  476. */
  477. CPanIn::CPanIn()
  478. {
  479. m_lCurrentData = 64;
  480. }
  481. /*****************************************************************************
  482. * CPanIn::GetPan()
  483. *****************************************************************************
  484. * Get the pan data.
  485. */
  486. long CPanIn::GetPan(STIME stTime)
  487. {
  488. long lResult = (long) CMIDIRecorder::GetData(stTime);
  489. return (lResult);
  490. }