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.

2479 lines
76 KiB

  1. // ==============================================================================
  2. //
  3. // miniport.cpp - miniport driver implementation for FM synth.
  4. // Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
  5. //
  6. // ==============================================================================
  7. #include "private.h" // contains class definitions.
  8. #define STR_MODULENAME "FMSynth: "
  9. #pragma code_seg("PAGE")
  10. // ==============================================================================
  11. // CreateMiniportMidiFM()
  12. // Creates a MIDI FM miniport driver. This uses a
  13. // macro from STDUNK.H to do all the work.
  14. // ==============================================================================
  15. NTSTATUS CreateMiniportMidiFM
  16. (
  17. OUT PUNKNOWN * Unknown,
  18. IN REFCLSID ClassID,
  19. IN PUNKNOWN UnknownOuter OPTIONAL,
  20. IN POOL_TYPE PoolType
  21. )
  22. {
  23. PAGED_CODE();
  24. ASSERT(Unknown);
  25. _DbgPrintF(DEBUGLVL_VERBOSE, ("CreateMiniportMidiFM"));
  26. // expand STD_CREATE_BODY_ to take constructor(boolean) for whether to include volume
  27. NTSTATUS ntStatus;
  28. CMiniportMidiFM *p =
  29. new(PoolType,'MFcP') CMiniportMidiFM(
  30. UnknownOuter,
  31. (IsEqualGUIDAligned(ClassID,CLSID_MiniportDriverFmSynthWithVol))
  32. );
  33. #ifdef DEBUG
  34. if (IsEqualGUIDAligned(ClassID,CLSID_MiniportDriverFmSynthWithVol))
  35. {
  36. _DbgPrintF(DEBUGLVL_VERBOSE, ("Creating new FM miniport with volume node"));
  37. }
  38. #endif
  39. if (p)
  40. {
  41. *Unknown = PUNKNOWN((PMINIPORTMIDI)(p));
  42. (*Unknown)->AddRef();
  43. ntStatus = STATUS_SUCCESS;
  44. }
  45. else
  46. {
  47. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  48. }
  49. return ntStatus;
  50. }
  51. #pragma code_seg("PAGE")
  52. // ==============================================================================
  53. // CMiniportMidiFM::ProcessResources()
  54. // Processes the resource list.
  55. // ==============================================================================
  56. NTSTATUS
  57. CMiniportMidiFM::
  58. ProcessResources
  59. (
  60. IN PRESOURCELIST ResourceList
  61. )
  62. {
  63. PAGED_CODE();
  64. ASSERT(ResourceList);
  65. if (!ResourceList)
  66. {
  67. return STATUS_DEVICE_CONFIGURATION_ERROR;
  68. }
  69. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::ProcessResources"));
  70. //
  71. // Get counts for the types of resources.
  72. //
  73. ULONG countIO = ResourceList->NumberOfPorts();
  74. ULONG countIRQ = ResourceList->NumberOfInterrupts();
  75. ULONG countDMA = ResourceList->NumberOfDmas();
  76. NTSTATUS ntStatus = STATUS_SUCCESS;
  77. //
  78. // Make sure we have the expected number of resources.
  79. //
  80. if ( (countIO != 1)
  81. || (countIRQ != 0)
  82. || (countDMA != 0)
  83. )
  84. {
  85. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  86. }
  87. if (NT_SUCCESS(ntStatus))
  88. {
  89. //
  90. // Get the port address.
  91. //
  92. m_PortBase = PUCHAR(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart);
  93. _DbgPrintF(DEBUGLVL_VERBOSE, ("Port Address = 0x%X", m_PortBase));
  94. }
  95. return ntStatus;
  96. }
  97. #pragma code_seg("PAGE")
  98. // ==============================================================================
  99. // CMiniportMidiFM::NonDelegatingQueryInterface()
  100. // Obtains an interface. This function works just like a COM QueryInterface
  101. // call and is used if the object is not being aggregated.
  102. // ==============================================================================
  103. STDMETHODIMP_(NTSTATUS) CMiniportMidiFM::NonDelegatingQueryInterface
  104. (
  105. REFIID Interface,
  106. PVOID * Object
  107. )
  108. {
  109. PAGED_CODE();
  110. ASSERT(Object);
  111. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::NonDelegatingQueryInterface"));
  112. if (IsEqualGUIDAligned(Interface,IID_IUnknown))
  113. {
  114. *Object = PVOID(PUNKNOWN(PMINIPORT(this)));
  115. }
  116. else if (IsEqualGUIDAligned(Interface,IID_IMiniport))
  117. {
  118. *Object = PVOID(PMINIPORT(this));
  119. }
  120. else if (IsEqualGUIDAligned(Interface,IID_IMiniportMidi))
  121. {
  122. *Object = PVOID(PMINIPORTMIDI(this));
  123. }
  124. else if (IsEqualGUIDAligned(Interface, IID_IPowerNotify))
  125. {
  126. *Object = PVOID(PPOWERNOTIFY(this));
  127. }
  128. else
  129. {
  130. *Object = NULL;
  131. }
  132. if (*Object)
  133. {
  134. //
  135. // We reference the interface for the caller.
  136. //
  137. PUNKNOWN((PMINIPORT)*Object)->AddRef();
  138. return STATUS_SUCCESS;
  139. }
  140. return STATUS_INVALID_PARAMETER;
  141. }
  142. #pragma code_seg()
  143. // ==============================================================================
  144. // CMiniportMidiFM::~CMiniportMidiFM()
  145. // Destructor.
  146. // ==============================================================================
  147. CMiniportMidiFM::~CMiniportMidiFM
  148. (
  149. void
  150. )
  151. {
  152. KIRQL oldIrql;
  153. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::~CMiniportMidiFM"));
  154. KeAcquireSpinLock(&m_SpinLock,&oldIrql);
  155. // Set silence on the device
  156. Opl3_BoardReset();
  157. KeReleaseSpinLock(&m_SpinLock,oldIrql);
  158. if (m_Port)
  159. {
  160. m_Port->Release();
  161. }
  162. }
  163. #pragma code_seg()
  164. // ==============================================================================
  165. // CMiniportMidiFM::Init()
  166. // Initializes a the miniport.
  167. // ==============================================================================
  168. STDMETHODIMP_(NTSTATUS)
  169. CMiniportMidiFM::
  170. Init
  171. (
  172. IN PUNKNOWN UnknownAdapter OPTIONAL,
  173. IN PRESOURCELIST ResourceList,
  174. IN PPORTMIDI Port_,
  175. OUT PSERVICEGROUP * ServiceGroup
  176. )
  177. {
  178. PAGED_CODE();
  179. ASSERT(ResourceList);
  180. if (!ResourceList)
  181. {
  182. return STATUS_DEVICE_CONFIGURATION_ERROR;
  183. }
  184. ASSERT(Port_);
  185. ASSERT(ServiceGroup);
  186. int i;
  187. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::init"));
  188. //
  189. // AddRef() is required because we are keeping this pointer.
  190. //
  191. m_Port = Port_;
  192. m_Port->AddRef();
  193. //
  194. // m_fStreamExists is not explicitly set to FALSE because C++ zeros
  195. // them out on a 'new'
  196. //
  197. KeInitializeSpinLock(&m_SpinLock);
  198. //
  199. // We want the IAdapterCommon interface on the adapter common object,
  200. // which is given to us as a IUnknown. The QueryInterface call gives us
  201. // an AddRefed pointer to the interface we want.
  202. //
  203. NTSTATUS ntStatus = ProcessResources(ResourceList);
  204. if (NT_SUCCESS(ntStatus))
  205. {
  206. KIRQL oldIrql;
  207. KeAcquireSpinLock(&m_SpinLock,&oldIrql);
  208. for (i = 0; i < 0x200; i++) // initialize the shadow registers, used
  209. m_SavedRegValues[i] = 0x00; // in case of power-down during playback
  210. // Initialize the hardware.
  211. // 1. First check to see if an opl device is present.
  212. // 2. Then determine if it is an opl2 or opl3. Bail if opl2.
  213. // 3. Call Opl3_BoardReset to silence and reset the device.
  214. if (SoundSynthPresent(m_PortBase, m_PortBase))
  215. {
  216. // Now check if the device is an opl2 or opl3 type.
  217. // The patches are already declared for opl3. So Init() is not defined.
  218. // For opl2 we have to go through an init and load the patches structure.
  219. if (SoundMidiIsOpl3())
  220. {
  221. _DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM::Init Type = OPL3"));
  222. // now silence the device and reset the board.
  223. Opl3_BoardReset();
  224. *ServiceGroup = NULL;
  225. }
  226. else
  227. {
  228. _DbgPrintF(DEBUGLVL_TERSE, ("CMiniportMidiFM::Init Type = OPL2"));
  229. ntStatus = STATUS_NOT_IMPLEMENTED;
  230. }
  231. }
  232. else
  233. {
  234. _DbgPrintF(DEBUGLVL_TERSE, ("CMiniportMidiFM::Init SoundSynthPresent failed"));
  235. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  236. }
  237. KeReleaseSpinLock(&m_SpinLock,oldIrql);
  238. }
  239. else
  240. {
  241. _DbgPrintF(DEBUGLVL_TERSE, ("CMiniportMidiFM::Init ProcessResources failed"));
  242. }
  243. _DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM::Init returning 0x%X", ntStatus));
  244. if (!NT_SUCCESS(ntStatus))
  245. {
  246. //
  247. // clean up our mess
  248. //
  249. // release the port
  250. m_Port->Release();
  251. m_Port = NULL;
  252. }
  253. return ntStatus;
  254. }
  255. #pragma code_seg("PAGE")
  256. // ==============================================================================
  257. // NewStream()
  258. // Creates a new stream.
  259. // ==============================================================================
  260. STDMETHODIMP_(NTSTATUS)
  261. CMiniportMidiFM::
  262. NewStream
  263. (
  264. OUT PMINIPORTMIDISTREAM * Stream,
  265. IN PUNKNOWN OuterUnknown OPTIONAL,
  266. IN POOL_TYPE PoolType,
  267. IN ULONG Pin,
  268. IN BOOLEAN Capture,
  269. IN PKSDATAFORMAT DataFormat,
  270. OUT PSERVICEGROUP * ServiceGroup
  271. )
  272. {
  273. PAGED_CODE();
  274. NTSTATUS ntStatus = STATUS_SUCCESS;
  275. if (m_fStreamExists)
  276. {
  277. _DbgPrintF(DEBUGLVL_TERSE,("CMiniportMidiFM::NewStream stream already exists"));
  278. ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  279. }
  280. else
  281. {
  282. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::NewStream"));
  283. CMiniportMidiStreamFM *pStream =
  284. new(PoolType) CMiniportMidiStreamFM(OuterUnknown);
  285. if (pStream)
  286. {
  287. pStream->AddRef();
  288. ntStatus = pStream->Init(this,m_PortBase);
  289. if (NT_SUCCESS(ntStatus))
  290. {
  291. *Stream = PMINIPORTMIDISTREAM(pStream);
  292. (*Stream)->AddRef();
  293. *ServiceGroup = NULL;
  294. m_fStreamExists = TRUE;
  295. }
  296. pStream->Release();
  297. }
  298. else
  299. {
  300. _DbgPrintF(DEBUGLVL_TERSE,("CMiniportMidiFM::NewStream failed, no memory"));
  301. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  302. }
  303. }
  304. return ntStatus;
  305. }
  306. #pragma code_seg("PAGE")
  307. /*----------------------------------------------------------------------------
  308. FUNCTION NAME- CMiniportMidiFM::PowerChangeNotify()
  309. ENTRY --- IN POWER_STATE NewState
  310. power management status
  311. RETURN --- void
  312. *------------------------------------------------------------------------- */
  313. STDMETHODIMP_(void) CMiniportMidiFM::PowerChangeNotify(
  314. IN POWER_STATE PowerState
  315. )
  316. {
  317. PAGED_CODE();
  318. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::PowerChangeNotify(%d)",PowerState));
  319. switch (PowerState.DeviceState)
  320. {
  321. case PowerDeviceD0:
  322. if (m_PowerState.DeviceState != PowerDeviceD0) // check for power state delta
  323. {
  324. MiniportMidiFMResume();
  325. }
  326. break;
  327. case PowerDeviceD1:
  328. case PowerDeviceD2:
  329. case PowerDeviceD3:
  330. default:
  331. // Don't need to do anything special, we always remember where we are.
  332. break;
  333. }
  334. m_PowerState.DeviceState = PowerState.DeviceState;
  335. }
  336. #pragma code_seg()
  337. // ==========================================================================
  338. // ==========================================================================
  339. void
  340. CMiniportMidiFM::
  341. MiniportMidiFMResume()
  342. {
  343. KIRQL oldIrql;
  344. BYTE i;
  345. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::MiniportMidiFMResume"));
  346. KeAcquireSpinLock(&m_SpinLock,&oldIrql);
  347. // We never touch these--set them to the default value anyway.
  348. // AD_LSI
  349. SoundMidiSendFM(m_PortBase, AD_LSI, m_SavedRegValues[AD_LSI]);
  350. // AD_LSI2
  351. SoundMidiSendFM(m_PortBase, AD_LSI2, m_SavedRegValues[AD_LSI2]);
  352. // AD_TIMER1
  353. SoundMidiSendFM(m_PortBase, AD_TIMER1, m_SavedRegValues[AD_TIMER1]);
  354. // AD_TIMER2
  355. SoundMidiSendFM(m_PortBase, AD_TIMER2, m_SavedRegValues[AD_TIMER2]);
  356. // AD_MASK
  357. SoundMidiSendFM(m_PortBase, AD_MASK, m_SavedRegValues[AD_MASK]);
  358. // AD_CONNECTION
  359. SoundMidiSendFM(m_PortBase, AD_CONNECTION, m_SavedRegValues[AD_CONNECTION]);
  360. // AD_NEW
  361. SoundMidiSendFM(m_PortBase, AD_NEW, m_SavedRegValues[AD_NEW]);
  362. // AD_NTS
  363. SoundMidiSendFM(m_PortBase, AD_NTS, m_SavedRegValues[AD_NTS]);
  364. // AD_DRUM
  365. SoundMidiSendFM(m_PortBase, AD_DRUM, m_SavedRegValues[AD_DRUM]);
  366. for (i = 0; i <= 0x15; i++)
  367. {
  368. if ((i & 0x07) <= 0x05)
  369. {
  370. // AD_MULT
  371. // AD_MULT2
  372. SoundMidiSendFM(m_PortBase, AD_MULT + i, m_SavedRegValues[AD_MULT + i]);
  373. SoundMidiSendFM(m_PortBase, AD_MULT2 + i, m_SavedRegValues[AD_MULT2 + i]);
  374. // AD_LEVEL
  375. // AD_LEVEL2
  376. // turn off all the oscillators
  377. SoundMidiSendFM(m_PortBase, AD_LEVEL + i, m_SavedRegValues[AD_LEVEL + i]);
  378. SoundMidiSendFM(m_PortBase, AD_LEVEL2 + i, m_SavedRegValues[AD_LEVEL2 + i]);
  379. // AD_AD
  380. // AD_AD2
  381. SoundMidiSendFM(m_PortBase, AD_AD + i, m_SavedRegValues[AD_AD + i]);
  382. SoundMidiSendFM(m_PortBase, AD_AD2 + i, m_SavedRegValues[AD_AD2 + i]);
  383. // AD_SR
  384. // AD_SR2
  385. SoundMidiSendFM(m_PortBase, AD_SR + i, m_SavedRegValues[AD_SR + i]);
  386. SoundMidiSendFM(m_PortBase, AD_SR2 + i, m_SavedRegValues[AD_SR2 + i]);
  387. // AD_WAVE
  388. // AD_WAVE2
  389. SoundMidiSendFM(m_PortBase, AD_WAVE + i, m_SavedRegValues[AD_WAVE + i]);
  390. SoundMidiSendFM(m_PortBase, AD_WAVE2 + i, m_SavedRegValues[AD_WAVE2 + i]);
  391. }
  392. }
  393. for (i = 0; i <= 0x08; i++)
  394. {
  395. // AD_FNUMBER
  396. // AD_FNUMBER2
  397. SoundMidiSendFM(m_PortBase, AD_FNUMBER + i, m_SavedRegValues[AD_FNUMBER + i]);
  398. SoundMidiSendFM(m_PortBase, AD_FNUMBER2 + i, m_SavedRegValues[AD_FNUMBER2 + i]);
  399. // AD_FEEDBACK
  400. // AD_FEEDBACK2
  401. SoundMidiSendFM(m_PortBase, AD_FEEDBACK + i, m_SavedRegValues[AD_FEEDBACK + i]);
  402. SoundMidiSendFM(m_PortBase, AD_FEEDBACK2 + i, m_SavedRegValues[AD_FEEDBACK2 + i]);
  403. // AD_BLOCK
  404. // AD_BLOCK2
  405. SoundMidiSendFM(m_PortBase, AD_BLOCK + i, m_SavedRegValues[AD_BLOCK + i]);
  406. SoundMidiSendFM(m_PortBase, AD_BLOCK2 + i, m_SavedRegValues[AD_BLOCK2 + i]);
  407. }
  408. KeReleaseSpinLock(&m_SpinLock,oldIrql);
  409. _DbgPrintF(DEBUGLVL_VERBOSE,("Done with CMiniportMidiFM::MiniportMidiFMResume"));
  410. }
  411. #pragma code_seg()
  412. void
  413. CMiniportMidiFM::
  414. Opl3_BoardReset()
  415. {
  416. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  417. BYTE i;
  418. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::Opl3_BoardReset"));
  419. /* ---- silence the chip -------- */
  420. /* tell the FM chip to use 4-operator mode, and
  421. fill in any other random variables */
  422. SoundMidiSendFM(m_PortBase, AD_NEW, 0x01);
  423. SoundMidiSendFM(m_PortBase, AD_MASK, 0x60);
  424. SoundMidiSendFM(m_PortBase, AD_CONNECTION, 0x00);
  425. SoundMidiSendFM(m_PortBase, AD_NTS, 0x00);
  426. /* turn off the drums, and use high vibrato/modulation */
  427. SoundMidiSendFM(m_PortBase, AD_DRUM, 0xc0);
  428. /* turn off all the oscillators */
  429. for (i = 0; i <= 0x15; i++)
  430. {
  431. if ((i & 0x07) <= 0x05)
  432. {
  433. SoundMidiSendFM(m_PortBase, AD_LEVEL + i, 0x3f);
  434. SoundMidiSendFM(m_PortBase, AD_LEVEL2 + i, 0x3f);
  435. }
  436. };
  437. /* turn off all the voices */
  438. for (i = 0; i <= 0x08; i++)
  439. {
  440. SoundMidiSendFM(m_PortBase, AD_BLOCK + i, 0x00);
  441. SoundMidiSendFM(m_PortBase, AD_BLOCK2 + i, 0x00);
  442. };
  443. }
  444. // ==============================================================================
  445. // PinDataRangesStream
  446. // Structures indicating range of valid format values for streaming pins.
  447. // ==============================================================================
  448. static
  449. KSDATARANGE_MUSIC PinDataRangesStream[] =
  450. {
  451. {
  452. {
  453. sizeof(KSDATARANGE_MUSIC),
  454. 0,
  455. 0,
  456. 0,
  457. STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
  458. STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI),
  459. STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
  460. },
  461. STATICGUIDOF(KSMUSIC_TECHNOLOGY_FMSYNTH),
  462. NUM2VOICES,
  463. NUM2VOICES,
  464. 0xffffffff
  465. }
  466. };
  467. // ==============================================================================
  468. // PinDataRangePointersStream
  469. // List of pointers to structures indicating range of valid format values
  470. // for streaming pins.
  471. // ==============================================================================
  472. static
  473. PKSDATARANGE PinDataRangePointersStream[] =
  474. {
  475. PKSDATARANGE(&PinDataRangesStream[0])
  476. };
  477. // ==============================================================================
  478. // PinDataRangesBridge
  479. // Structures indicating range of valid format values for bridge pins.
  480. // ==============================================================================
  481. static
  482. KSDATARANGE PinDataRangesBridge[] =
  483. {
  484. {
  485. sizeof(KSDATARANGE),
  486. 0,
  487. 0,
  488. 0,
  489. STATICGUIDOF(KSDATAFORMAT_TYPE_MUSIC),
  490. STATICGUIDOF(KSDATAFORMAT_SUBTYPE_MIDI_BUS),
  491. STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE)
  492. }
  493. };
  494. // ==============================================================================
  495. // PinDataRangePointersBridge
  496. // List of pointers to structures indicating range of valid format values
  497. // for bridge pins.
  498. // ==============================================================================
  499. static
  500. PKSDATARANGE PinDataRangePointersBridge[] =
  501. {
  502. &PinDataRangesBridge[0]
  503. };
  504. // ==============================================================================
  505. // MiniportPins
  506. // List of pins.
  507. // ==============================================================================
  508. static
  509. PCPIN_DESCRIPTOR MiniportPins[] =
  510. {
  511. {
  512. 1,1,1, // InstanceCount
  513. NULL, // AutomationTable
  514. { // KsPinDescriptor
  515. 0, // InterfacesCount
  516. NULL, // Interfaces
  517. 0, // MediumsCount
  518. NULL, // Mediums
  519. SIZEOF_ARRAY(PinDataRangePointersStream), // DataRangesCount
  520. PinDataRangePointersStream, // DataRanges
  521. KSPIN_DATAFLOW_IN, // DataFlow
  522. KSPIN_COMMUNICATION_SINK, // Communication
  523. (GUID *) &KSCATEGORY_SYNTHESIZER, // Category
  524. NULL, // Name
  525. 0 // Reserved
  526. }
  527. },
  528. {
  529. 0,0,0, // InstanceCount
  530. NULL, // AutomationTable
  531. { // KsPinDescriptor
  532. 0, // InterfacesCount
  533. NULL, // Interfaces
  534. 0, // MediumsCount
  535. NULL, // Mediums
  536. SIZEOF_ARRAY(PinDataRangePointersBridge), // DataRangesCount
  537. PinDataRangePointersBridge, // DataRanges
  538. KSPIN_DATAFLOW_OUT, // DataFlow
  539. KSPIN_COMMUNICATION_NONE, // Communication
  540. (GUID *) &KSCATEGORY_AUDIO, // Category
  541. NULL, // Name
  542. 0 // Reserved
  543. }
  544. }
  545. };
  546. /*****************************************************************************
  547. * PropertiesVolume
  548. *****************************************************************************
  549. * Properties for volume controls.
  550. */
  551. static
  552. PCPROPERTY_ITEM PropertiesVolume[] =
  553. {
  554. {
  555. &KSPROPSETID_Audio,
  556. KSPROPERTY_AUDIO_VOLUMELEVEL,
  557. KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT,
  558. PropertyHandler_Level
  559. },
  560. {
  561. &KSPROPSETID_Audio,
  562. KSPROPERTY_AUDIO_CPU_RESOURCES,
  563. KSPROPERTY_TYPE_GET,
  564. PropertyHandler_CpuResources
  565. }
  566. };
  567. /*****************************************************************************
  568. * AutomationVolume
  569. *****************************************************************************
  570. * Automation table for volume controls.
  571. */
  572. DEFINE_PCAUTOMATION_TABLE_PROP(AutomationVolume,PropertiesVolume);
  573. // ==============================================================================
  574. // MiniportNodes
  575. // List of nodes.
  576. // ==============================================================================
  577. static
  578. PCNODE_DESCRIPTOR MiniportNodes[] =
  579. {
  580. {
  581. // synth node, #0
  582. 0, // Flags
  583. NULL, // AutomationTable
  584. &KSNODETYPE_SYNTHESIZER,// Type
  585. NULL // Name TODO: fill in with correct GUID
  586. },
  587. {
  588. // volume node, #1
  589. 0, // Flags
  590. &AutomationVolume, // AutomationTable
  591. &KSNODETYPE_VOLUME, // Type
  592. NULL // Name TODO: fill in with correct GUID
  593. }
  594. };
  595. // ==============================================================================
  596. // MiniportConnections
  597. // List of connections.
  598. // ==============================================================================
  599. /*****************************************************************************
  600. * Table of topology unit connections.
  601. *
  602. * Pin numbering is technically arbitrary, but the convention established here
  603. * is to number a solitary output pin 0 (looks like an 'o') and a solitary
  604. * input pin 1 (looks like an 'i'). Even destinations, which have no output,
  605. * have an input pin numbered 1 and no pin 0.
  606. *
  607. * Nodes are more likely to have multiple ins than multiple outs, so the more
  608. * general rule would be that inputs are numbered >=1. If a node has multiple
  609. * outs, none of these conventions apply.
  610. *
  611. * Nodes have at most one control value. Mixers are therefore simple summing
  612. * nodes with no per-pin levels. Rather than assigning a unique pin to each
  613. * input to a mixer, all inputs are connected to pin 1. This is acceptable
  614. * because there is no functional distinction between the inputs.
  615. *
  616. * There are no multiplexers in this topology, so there is no opportunity to
  617. * give an example of a multiplexer. A multiplexer should have a single
  618. * output pin (0) and multiple input pins (1..n). Its control value is an
  619. * integer in the range 1..n indicating which input is connected to the
  620. * output.
  621. *
  622. * In the case of connections to pins, as opposed to connections to nodes, the
  623. * node is identified as PCFILTER_NODE and the pin number identifies the
  624. * particular filter pin.
  625. *****************************************************************************
  626. */
  627. enum {
  628. eFMSynthNode = 0,
  629. eFMVolumeNode
  630. };
  631. enum {
  632. eFMNodeOutput = 0,
  633. eFMNodeInput = 1
  634. };
  635. enum {
  636. eFilterInput = eFMNodeOutput,
  637. eBridgeOutput = eFMNodeInput
  638. };
  639. static
  640. PCCONNECTION_DESCRIPTOR MiniportConnections[] =
  641. {
  642. // FromNode, FromPin, ToNode, ToPin
  643. { PCFILTER_NODE, eFilterInput, eFMSynthNode, eFMNodeInput }, // Stream in to synth.
  644. { eFMSynthNode, eFMNodeOutput, PCFILTER_NODE, eBridgeOutput } // Synth to bridge out.
  645. };
  646. // different connection struct for volume version
  647. static
  648. PCCONNECTION_DESCRIPTOR MiniportWithVolConnections[] =
  649. {
  650. // FromNode, FromPin, ToNode, ToPin
  651. { PCFILTER_NODE, eFilterInput, eFMSynthNode, eFMNodeInput }, // Stream in to synth.
  652. { eFMSynthNode, eFMNodeOutput, eFMVolumeNode, eFMNodeInput }, // Synth to volume.
  653. { eFMVolumeNode, eFMNodeOutput, PCFILTER_NODE, eBridgeOutput } // volume to bridge out.
  654. };
  655. ////////////////////////////////////////////////////////////////////////////////
  656. // MiniportCategories
  657. //
  658. // List of categories.
  659. static
  660. GUID MiniportCategories[] =
  661. {
  662. STATICGUIDOF(KSCATEGORY_AUDIO),
  663. STATICGUIDOF(KSCATEGORY_RENDER),
  664. STATICGUIDOF(KSCATEGORY_SYNTHESIZER)
  665. };
  666. // ==============================================================================
  667. // MiniportDescription
  668. // Complete description of the miniport.
  669. // ==============================================================================
  670. static
  671. PCFILTER_DESCRIPTOR MiniportFilterDescriptor =
  672. {
  673. 0, // Version
  674. NULL, // AutomationTable
  675. sizeof(PCPIN_DESCRIPTOR), // PinSize
  676. SIZEOF_ARRAY(MiniportPins), // PinCount
  677. MiniportPins, // Pins
  678. sizeof(PCNODE_DESCRIPTOR), // NodeSize
  679. 1, // NodeCount - no volume node
  680. MiniportNodes, // Nodes
  681. SIZEOF_ARRAY(MiniportConnections), // ConnectionCount
  682. MiniportConnections, // Connections
  683. SIZEOF_ARRAY(MiniportCategories), // CategoryCount
  684. MiniportCategories // Categories
  685. };
  686. static
  687. PCFILTER_DESCRIPTOR MiniportFilterWithVolDescriptor =
  688. {
  689. 0, // Version
  690. NULL, // AutomationTable
  691. sizeof(PCPIN_DESCRIPTOR), // PinSize
  692. SIZEOF_ARRAY(MiniportPins), // PinCount
  693. MiniportPins, // Pins
  694. sizeof(PCNODE_DESCRIPTOR), // NodeSize
  695. 2, // NodeCount - extra volume node
  696. MiniportNodes, // Nodes
  697. SIZEOF_ARRAY(MiniportWithVolConnections), // ConnectionCount
  698. MiniportWithVolConnections, // Connections
  699. 0, // CategoryCount
  700. NULL // Categories
  701. };
  702. #pragma code_seg("PAGE")
  703. // ==============================================================================
  704. // CMiniportMidiFM::GetDescription()
  705. // Gets the topology.
  706. // Pass back appropriate descriptor, depending on whether volume node is needed.
  707. // ==============================================================================
  708. STDMETHODIMP_(NTSTATUS)
  709. CMiniportMidiFM::
  710. GetDescription
  711. (
  712. OUT PPCFILTER_DESCRIPTOR * OutFilterDescriptor
  713. )
  714. {
  715. PAGED_CODE();
  716. ASSERT(OutFilterDescriptor);
  717. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiFM::GetDescription"));
  718. if (m_volNodeNeeded)
  719. {
  720. *OutFilterDescriptor = &MiniportFilterWithVolDescriptor;
  721. _DbgPrintF(DEBUGLVL_VERBOSE, ("Getting descriptor of new FM miniport with volume node"));
  722. }
  723. else
  724. {
  725. *OutFilterDescriptor = &MiniportFilterDescriptor;
  726. }
  727. return STATUS_SUCCESS;
  728. }
  729. #pragma code_seg("PAGE")
  730. // ==============================================================================
  731. // CMiniportMidiStreamFM::NonDelegatingQueryInterface()
  732. // Obtains an interface. This function works just like a COM QueryInterface
  733. // call and is used if the object is not being aggregated.
  734. // ==============================================================================
  735. STDMETHODIMP_(NTSTATUS) CMiniportMidiStreamFM::NonDelegatingQueryInterface
  736. (
  737. REFIID Interface,
  738. PVOID * Object
  739. )
  740. {
  741. PAGED_CODE();
  742. ASSERT(Object);
  743. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiStreamFM::NonDelegatingQueryInterface"));
  744. if (IsEqualGUIDAligned(Interface,IID_IUnknown))
  745. {
  746. *Object = PVOID(PUNKNOWN(PMINIPORT(this)));
  747. }
  748. else
  749. if (IsEqualGUIDAligned(Interface,IID_IMiniportMidiStream))
  750. {
  751. *Object = PVOID(PMINIPORTMIDISTREAM(this));
  752. }
  753. else
  754. {
  755. *Object = NULL;
  756. }
  757. if (*Object)
  758. {
  759. //
  760. // We reference the interface for the caller.
  761. //
  762. PUNKNOWN(PMINIPORT(*Object))->AddRef();
  763. return STATUS_SUCCESS;
  764. }
  765. return STATUS_INVALID_PARAMETER;
  766. }
  767. #pragma code_seg("PAGE")
  768. // ==============================================================================
  769. // CMiniportMidiStreamFM::~CMiniportMidiStreamFM()
  770. // Destructor.
  771. // ==============================================================================
  772. CMiniportMidiStreamFM::~CMiniportMidiStreamFM
  773. (
  774. void
  775. )
  776. {
  777. PAGED_CODE();
  778. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiStreamFM::~CMiniportMidiStreamFM"));
  779. Opl3_AllNotesOff();
  780. if (m_Miniport)
  781. {
  782. m_Miniport->m_fStreamExists = FALSE;
  783. m_Miniport->Release();
  784. }
  785. }
  786. #pragma code_seg("PAGE")
  787. // ==============================================================================
  788. // CMiniportMidiStreamFM::Init()
  789. // Initializes a the miniport.
  790. // ==============================================================================
  791. NTSTATUS
  792. CMiniportMidiStreamFM::
  793. Init
  794. (
  795. IN CMiniportMidiFM * Miniport,
  796. IN PUCHAR PortBase
  797. )
  798. {
  799. PAGED_CODE();
  800. ASSERT(Miniport);
  801. ASSERT(PortBase);
  802. int i;
  803. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiStreamFM::Init"));
  804. //
  805. // AddRef() is required because we are keeping this pointer.
  806. //
  807. m_Miniport = Miniport;
  808. m_Miniport->AddRef();
  809. m_PortBase = PortBase;
  810. // init some members
  811. m_dwCurTime = 1; /* for note on/off */
  812. /* volume */
  813. m_wSynthAttenL = 0; /* in 1.5dB steps */
  814. m_wSynthAttenR = 0; /* in 1.5dB steps */
  815. m_MinVolValue = 0xFFD0C000; // minimum -47.25(dB) * 0x10000
  816. m_MaxVolValue = 0x00000000; // maximum 0 (dB) * 0x10000
  817. m_VolStepDelta = 0x0000C000; // steps of 0.75 (dB) * 0x10000
  818. m_SavedVolValue[CHAN_LEFT] = m_SavedVolValue[CHAN_RIGHT] = 0;
  819. /* start attenuations at -3 dB, which is 90 MIDI level */
  820. for (i = 0; i < NUMCHANNELS; i++)
  821. {
  822. m_bChanAtten[i] = 4;
  823. m_bStereoMask[i] = 0xff;
  824. };
  825. return STATUS_SUCCESS;
  826. }
  827. #pragma code_seg("PAGE")
  828. // ==============================================================================
  829. // CMiniportMidiStreamFM::SetState()
  830. // Sets the transport state.
  831. // ==============================================================================
  832. STDMETHODIMP_(NTSTATUS)
  833. CMiniportMidiStreamFM::
  834. SetState
  835. (
  836. IN KSSTATE NewState
  837. )
  838. {
  839. PAGED_CODE();
  840. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiStreamFM::SetState"));
  841. NTSTATUS ntStatus = STATUS_SUCCESS;
  842. switch (NewState)
  843. {
  844. case KSSTATE_STOP:
  845. case KSSTATE_ACQUIRE:
  846. case KSSTATE_PAUSE:
  847. Opl3_AllNotesOff();
  848. break;
  849. case KSSTATE_RUN:
  850. break;
  851. }
  852. return ntStatus;
  853. }
  854. #pragma code_seg("PAGE")
  855. // ==============================================================================
  856. // CMiniportMidiStreamFM::SetFormat()
  857. // Sets the format.
  858. // ==============================================================================
  859. STDMETHODIMP_(NTSTATUS)
  860. CMiniportMidiStreamFM::
  861. SetFormat
  862. (
  863. IN PKSDATAFORMAT Format
  864. )
  865. {
  866. PAGED_CODE();
  867. ASSERT(Format);
  868. _DbgPrintF(DEBUGLVL_VERBOSE,("CMiniportMidiStreamFM::SetFormat"));
  869. return STATUS_SUCCESS;
  870. }
  871. #pragma code_seg("PAGE")
  872. /*****************************************************************************
  873. * BasicSupportHandler()
  874. *****************************************************************************
  875. * Assists in BASICSUPPORT accesses on level properties -
  876. * this is declared as a friend method in the header file.
  877. */
  878. static
  879. NTSTATUS BasicSupportHandler
  880. (
  881. IN PPCPROPERTY_REQUEST PropertyRequest
  882. )
  883. {
  884. PAGED_CODE();
  885. ASSERT(PropertyRequest);
  886. _DbgPrintF(DEBUGLVL_VERBOSE, ("BasicSupportHandler"));
  887. NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  888. if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION)))
  889. {
  890. // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it
  891. PKSPROPERTY_DESCRIPTION PropDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
  892. PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT |
  893. KSPROPERTY_TYPE_GET |
  894. KSPROPERTY_TYPE_SET;
  895. PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION) +
  896. sizeof(KSPROPERTY_MEMBERSHEADER) +
  897. sizeof(KSPROPERTY_STEPPING_LONG);
  898. PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
  899. PropDesc->PropTypeSet.Id = VT_I4;
  900. PropDesc->PropTypeSet.Flags = 0;
  901. PropDesc->MembersListCount = 1;
  902. PropDesc->Reserved = 0;
  903. // if return buffer can also hold a range description, return it too
  904. if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION) +
  905. sizeof(KSPROPERTY_MEMBERSHEADER) +
  906. sizeof(KSPROPERTY_STEPPING_LONG)))
  907. {
  908. // fill in the members header
  909. PKSPROPERTY_MEMBERSHEADER Members = PKSPROPERTY_MEMBERSHEADER(PropDesc + 1);
  910. Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
  911. Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
  912. Members->MembersCount = 1;
  913. Members->Flags = 0;
  914. // fill in the stepped range
  915. PKSPROPERTY_STEPPING_LONG Range = PKSPROPERTY_STEPPING_LONG(Members + 1);
  916. switch (PropertyRequest->Node)
  917. {
  918. case eFMVolumeNode:
  919. CMiniportMidiStreamFM *that = (CMiniportMidiStreamFM *)PropertyRequest->MinorTarget;
  920. if (that)
  921. {
  922. Range->Bounds.SignedMinimum = that->m_MinVolValue;
  923. Range->Bounds.SignedMaximum = that->m_MaxVolValue;
  924. Range->SteppingDelta = that->m_VolStepDelta;
  925. break;
  926. }
  927. else
  928. {
  929. return STATUS_INVALID_PARAMETER;
  930. }
  931. }
  932. Range->Reserved = 0;
  933. _DbgPrintF(DEBUGLVL_VERBOSE, ("---Node: %d Max: 0x%X Min: 0x%X Step: 0x%X",PropertyRequest->Node,
  934. Range->Bounds.SignedMaximum,
  935. Range->Bounds.SignedMinimum,
  936. Range->SteppingDelta));
  937. // set the return value size
  938. PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION) +
  939. sizeof(KSPROPERTY_MEMBERSHEADER) +
  940. sizeof(KSPROPERTY_STEPPING_LONG);
  941. }
  942. else
  943. {
  944. // set the return value size
  945. PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
  946. }
  947. ntStatus = STATUS_SUCCESS;
  948. }
  949. else if (PropertyRequest->ValueSize >= sizeof(ULONG))
  950. {
  951. // if return buffer can hold a ULONG, return the access flags
  952. PULONG AccessFlags = PULONG(PropertyRequest->Value);
  953. *AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT |
  954. KSPROPERTY_TYPE_GET |
  955. KSPROPERTY_TYPE_SET;
  956. // set the return value size
  957. PropertyRequest->ValueSize = sizeof(ULONG);
  958. ntStatus = STATUS_SUCCESS;
  959. }
  960. return ntStatus;
  961. }
  962. #pragma code_seg("PAGE")
  963. /*****************************************************************************
  964. * PropertyHandler_Level()
  965. *****************************************************************************
  966. * Accesses a KSAUDIO_LEVEL property.
  967. */
  968. static
  969. NTSTATUS PropertyHandler_Level
  970. (
  971. IN PPCPROPERTY_REQUEST PropertyRequest
  972. )
  973. {
  974. PAGED_CODE();
  975. ASSERT(PropertyRequest);
  976. _DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_Level"));
  977. NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
  978. LONG channel;
  979. // validate node
  980. if (PropertyRequest->Node == eFMVolumeNode)
  981. {
  982. if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
  983. {
  984. // get the instance channel parameter
  985. if (PropertyRequest->InstanceSize >= sizeof(LONG))
  986. {
  987. channel = *(PLONG(PropertyRequest->Instance));
  988. // only support get requests on either mono/left(0) or right(1) channels
  989. if ((channel == CHAN_LEFT) || (channel == CHAN_RIGHT))
  990. {
  991. // validate and get the output parameter
  992. if (PropertyRequest->ValueSize >= sizeof(LONG))
  993. {
  994. PLONG Level = (PLONG)PropertyRequest->Value;
  995. // check if volume property request
  996. if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
  997. {
  998. CMiniportMidiStreamFM *that = (CMiniportMidiStreamFM *)PropertyRequest->MinorTarget;
  999. if (that)
  1000. {
  1001. *Level = that->GetFMAtten(channel);
  1002. PropertyRequest->ValueSize = sizeof(LONG);
  1003. ntStatus = STATUS_SUCCESS;
  1004. }
  1005. // if (!that) return STATUS_INVALID_PARAMETER
  1006. } // (PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
  1007. } // (ValueSize >= sizeof(LONG))
  1008. } // ((channel == CHAN_LEFT) || (channel == CHAN_RIGHT))
  1009. } // (InstanceSize >= sizeof(LONG))
  1010. } // (Verb & KSPROPERTY_TYPE_GET)
  1011. else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
  1012. {
  1013. // get the instance channel parameter
  1014. if (PropertyRequest->InstanceSize >= sizeof(LONG))
  1015. {
  1016. channel = *(PLONG(PropertyRequest->Instance));
  1017. // only support get requests on either mono/left (0), right (1), or master (-1) channels
  1018. if ((channel == CHAN_LEFT) || (channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
  1019. {
  1020. // validate and get the input parameter
  1021. if (PropertyRequest->ValueSize == sizeof(LONG))
  1022. {
  1023. PLONG level = (PLONG)PropertyRequest->Value;
  1024. if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
  1025. {
  1026. CMiniportMidiStreamFM *that = (CMiniportMidiStreamFM *)PropertyRequest->MinorTarget;
  1027. if (that)
  1028. {
  1029. that->SetFMAtten(channel,*level);
  1030. ntStatus = STATUS_SUCCESS;
  1031. }
  1032. // if (!that) return STATUS_INVALID_PARAMETER
  1033. } // (PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
  1034. } // (ValueSize == sizeof(LONG))
  1035. } // ((channel == CHAN_LEFT) || (channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
  1036. } // (InstanceSize >= sizeof(LONG))
  1037. } // (Verb & KSPROPERTY_TYPE_SET)
  1038. else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
  1039. {
  1040. if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
  1041. {
  1042. ntStatus = BasicSupportHandler(PropertyRequest);
  1043. }
  1044. } // (Verb & KSPROPERTY_TYPE_BASICSUPPORT)
  1045. } // (Node == eFMVolumeNode)
  1046. return ntStatus;
  1047. }
  1048. #pragma code_seg()
  1049. // convert from 16.16 dB to [0,63], set m_wSynthAttenR
  1050. void
  1051. CMiniportMidiStreamFM::
  1052. SetFMAtten
  1053. (
  1054. IN LONG channel,
  1055. IN LONG level
  1056. )
  1057. {
  1058. KIRQL oldIrql;
  1059. if ((channel == CHAN_LEFT) || (channel == CHAN_MASTER))
  1060. {
  1061. m_SavedVolValue[CHAN_LEFT] = level;
  1062. if (level > m_MaxVolValue)
  1063. m_wSynthAttenL = 0;
  1064. else if (level < m_MinVolValue)
  1065. m_wSynthAttenL = 63;
  1066. else
  1067. m_wSynthAttenL = WORD(-level / (LONG)m_VolStepDelta);
  1068. }
  1069. if ((channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
  1070. {
  1071. m_SavedVolValue[CHAN_RIGHT] = level;
  1072. if (level > m_MaxVolValue)
  1073. m_wSynthAttenR = 0;
  1074. else if (level < m_MinVolValue)
  1075. m_wSynthAttenR = 63;
  1076. else
  1077. m_wSynthAttenR = WORD(-level / (LONG)m_VolStepDelta);
  1078. }
  1079. #ifdef USE_KDPRINT
  1080. KdPrint(("'StreamFM::SetFMAtten: channel: 0x%X, level: 0x%X, m_wSynthAttenL: 0x%X, m_wSynthAttenR: 0x%X \n",
  1081. channel, level, m_wSynthAttenL, m_wSynthAttenR));
  1082. #else // USE_KDPRINT
  1083. _DbgPrintF(DEBUGLVL_VERBOSE,("StreamFM::SetFMAtten: channel: 0x%X, level: 0x%X, m_wSynthAttenL: 0x%X, m_wSynthAttenR: 0x%X \n",
  1084. channel, level, m_wSynthAttenL, m_wSynthAttenR));
  1085. #endif // USE_KDPRINT
  1086. KeAcquireSpinLock(&m_Miniport->m_SpinLock,&oldIrql);
  1087. Opl3_SetVolume(0xFF); // 0xFF means all channels
  1088. KeReleaseSpinLock(&m_Miniport->m_SpinLock,oldIrql);
  1089. }
  1090. #pragma code_seg("PAGE")
  1091. /*****************************************************************************
  1092. * PropertyHandler_CpuResources()
  1093. *****************************************************************************
  1094. * Processes a KSPROPERTY_AUDIO_CPU_RESOURCES request
  1095. */
  1096. static
  1097. NTSTATUS PropertyHandler_CpuResources
  1098. (
  1099. IN PPCPROPERTY_REQUEST PropertyRequest
  1100. )
  1101. {
  1102. PAGED_CODE();
  1103. ASSERT(PropertyRequest);
  1104. _DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_CpuResources"));
  1105. NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  1106. // validate node
  1107. if(PropertyRequest->Node == eFMVolumeNode)
  1108. {
  1109. if(PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
  1110. {
  1111. if(PropertyRequest->ValueSize >= sizeof(LONG))
  1112. {
  1113. *(PLONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU;
  1114. PropertyRequest->ValueSize = sizeof(LONG);
  1115. ntStatus = STATUS_SUCCESS;
  1116. }
  1117. else
  1118. {
  1119. _DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_CpuResources failed, buffer too small"));
  1120. ntStatus = STATUS_BUFFER_TOO_SMALL;
  1121. }
  1122. }
  1123. }
  1124. return ntStatus;
  1125. }
  1126. #pragma code_seg()
  1127. // ==============================================================================
  1128. // SoundMidiSendFM
  1129. // Writes out to the device.
  1130. // Called from DPC code (Write->WriteMidiData->Opl3_NoteOn->Opl3_FMNote->here)
  1131. // ==============================================================================
  1132. void
  1133. CMiniportMidiFM::
  1134. SoundMidiSendFM
  1135. (
  1136. IN PUCHAR PortBase,
  1137. IN ULONG Address,
  1138. IN UCHAR Data
  1139. )
  1140. {
  1141. ASSERT(Address < 0x200);
  1142. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1143. // these delays need to be 23us at least for old opl2 chips, even
  1144. // though new chips can handle 1 us delays.
  1145. #ifdef USE_KDPRINT
  1146. KdPrint(("'SoundMidiSendFM(%02x %02x) \n",Address,Data));
  1147. #else // USE_KDPRINT
  1148. _DbgPrintF(DEBUGLVL_VERBOSE, ("%X\t%X", Address,Data));
  1149. #endif // USE_KDPRINT
  1150. WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 0 : 2), (UCHAR)Address);
  1151. KeStallExecutionProcessor(23);
  1152. WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 1 : 3), Data);
  1153. KeStallExecutionProcessor(23);
  1154. m_SavedRegValues[Address] = Data;
  1155. }
  1156. #pragma code_seg()
  1157. // ==============================================================================
  1158. // Service()
  1159. // DPC-mode service call from the port.
  1160. // ==============================================================================
  1161. STDMETHODIMP_(void)
  1162. CMiniportMidiFM::
  1163. Service
  1164. ( void
  1165. )
  1166. {
  1167. }
  1168. #pragma code_seg()
  1169. // ==============================================================================
  1170. // CMiniportMidiStreamFM::Read()
  1171. // Reads incoming MIDI data.
  1172. // ==============================================================================
  1173. STDMETHODIMP_(NTSTATUS)
  1174. CMiniportMidiStreamFM::
  1175. Read
  1176. (
  1177. IN PVOID BufferAddress,
  1178. IN ULONG Length,
  1179. OUT PULONG BytesRead
  1180. )
  1181. {
  1182. return STATUS_NOT_IMPLEMENTED;
  1183. }
  1184. #pragma code_seg()
  1185. // ==============================================================================
  1186. // CMiniportMidiStreamFM::Write()
  1187. // Writes outgoing MIDI data.
  1188. //
  1189. // N.B.!!!
  1190. // THIS DATA SINK ASSUMES THAT DATA COMES IN ONE MESSAGE AT A TIME!!!
  1191. // IF LENGTH IS MORE THAN THREE BYTES, SUCH AS SYSEX OR MULTIPLE MIDI
  1192. // MESSAGES, ALL THE DATA IS DROPPED UNCEREMONIOUSLY ON THE FLOOR!!!
  1193. // ALSO DOES NOT PLAY WELL WITH RUNNING STATUS!!!
  1194. //
  1195. // CLEARLY, THIS MINIPORT HAS SOME "ISSUES".
  1196. //
  1197. // ==============================================================================
  1198. STDMETHODIMP_(NTSTATUS)
  1199. CMiniportMidiStreamFM::
  1200. Write
  1201. (
  1202. IN PVOID BufferAddress, // pointer to Midi Data.
  1203. IN ULONG Length,
  1204. OUT PULONG BytesWritten
  1205. )
  1206. {
  1207. ASSERT(BufferAddress);
  1208. ASSERT(BytesWritten);
  1209. _DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiStreamFM::Write"));
  1210. BYTE statusByte = *(PBYTE)BufferAddress & 0xF0;
  1211. *BytesWritten = Length;
  1212. if (statusByte < 0x80)
  1213. {
  1214. _DbgPrintF(DEBUGLVL_TERSE, ("CMiniportMidiStreamFM::Write requires first byte to be status -- ignored"));
  1215. }
  1216. else if (statusByte == 0xF0)
  1217. {
  1218. _DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle System messages -- ignored"));
  1219. }
  1220. else if (statusByte == 0xA0)
  1221. {
  1222. _DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle Polyphonic key pressure/Aftertouch messages -- ignored"));
  1223. }
  1224. else if (statusByte == 0xD0)
  1225. {
  1226. _DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle Channel pressure/Aftertouch messages -- ignored"));
  1227. }
  1228. else if (Length < 4)
  1229. {
  1230. WriteMidiData(*(DWORD *)BufferAddress);
  1231. }
  1232. else
  1233. {
  1234. _DbgPrintF(DEBUGLVL_TERSE, ("StreamFM::Write doesn't handle Length > 3."));
  1235. }
  1236. return STATUS_SUCCESS;
  1237. }
  1238. // ==============================================================================
  1239. // ==============================================================================
  1240. // Private Methods of CMiniportMidiFM
  1241. // ==============================================================================
  1242. // ==============================================================================
  1243. #pragma code_seg()
  1244. // =================================================================
  1245. // SoundMidiIsOpl3
  1246. // Checks if the midi synthesizer is Opl3 compatible or just adlib-compatible.
  1247. // returns: TRUE if OPL3-compatible chip. FALSE otherwise.
  1248. //
  1249. // NOTE: This has been taken as is from the nt driver code.
  1250. // =================================================================
  1251. BOOL CMiniportMidiFM::
  1252. SoundMidiIsOpl3(void)
  1253. {
  1254. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1255. BOOL bIsOpl3 = FALSE;
  1256. /*
  1257. * theory: an opl3-compatible synthesizer chip looks
  1258. * exactly like two separate 3812 synthesizers (for left and right
  1259. * channels) until switched into opl3 mode. Then, the timer-control
  1260. * register for the right half is replaced by a channel connection register
  1261. * (among other changes).
  1262. *
  1263. * We can detect 3812 synthesizers by starting a timer and looking for
  1264. * timer overflow. So if we find 3812s at both left and right addresses,
  1265. * we then switch to opl3 mode and look again for the right-half. If we
  1266. * still find it, then the switch failed and we have an old synthesizer
  1267. * if the right half disappeared, we have a new opl3 synthesizer.
  1268. *
  1269. * NB we use either monaural base-level synthesis, or stereo opl3
  1270. * synthesis. If we discover two 3812s (as on early SB Pro and
  1271. * PAS), we ignore one of them.
  1272. */
  1273. /*
  1274. * nice theory - but wrong. The timer on the right half of the
  1275. * opl3 chip reports its status in the left-half status register.
  1276. * There is no right-half status register on the opl3 chip.
  1277. */
  1278. /* ensure base mode */
  1279. SoundMidiSendFM(m_PortBase, AD_NEW, 0x00);
  1280. KeStallExecutionProcessor(20);
  1281. /* look for right half of chip */
  1282. if (SoundSynthPresent(m_PortBase + 2, m_PortBase))
  1283. {
  1284. /* yes - is this two separate chips or a new opl3 chip ? */
  1285. /* switch to opl3 mode */
  1286. SoundMidiSendFM(m_PortBase, AD_NEW, 0x01);
  1287. KeStallExecutionProcessor(20);
  1288. if (!SoundSynthPresent(m_PortBase + 2, m_PortBase))
  1289. {
  1290. _DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM: In SoundMidiIsOpl3 right half disappeared"));
  1291. /* right-half disappeared - so opl3 */
  1292. bIsOpl3 = TRUE;
  1293. }
  1294. }
  1295. if (!bIsOpl3)
  1296. {
  1297. /* reset to 3812 mode */
  1298. SoundMidiSendFM(m_PortBase, AD_NEW, 0x00);
  1299. KeStallExecutionProcessor(20);
  1300. }
  1301. _DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM: In SoundMidiIsOpl3 returning bIsOpl3 = 0x%X", bIsOpl3));
  1302. return(bIsOpl3);
  1303. }
  1304. #pragma code_seg()
  1305. // ==============================================================================
  1306. // SoundSynthPresent
  1307. //
  1308. // Detect the presence or absence of a 3812 (opl2/adlib-compatible) synthesizer
  1309. // at the given i/o address by starting the timer and looking for an
  1310. // overflow. Can be used to detect left and right synthesizers separately.
  1311. //
  1312. // Returns: True if a synthesiser is present at that address and false if not.
  1313. //
  1314. // NOTE: This and has been taken as is from the nt driver code.
  1315. // ==============================================================================
  1316. BOOL
  1317. CMiniportMidiFM::
  1318. SoundSynthPresent
  1319. (
  1320. IN PUCHAR base,
  1321. IN PUCHAR inbase
  1322. )
  1323. {
  1324. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1325. UCHAR t1, t2;
  1326. // check if the chip is present
  1327. SoundMidiSendFM(base, 4, 0x60); // mask T1 & T2
  1328. SoundMidiSendFM(base, 4, 0x80); // reset IRQ
  1329. t1 = READ_PORT_UCHAR((PUCHAR)inbase); // read status register
  1330. SoundMidiSendFM(base, 2, 0xff); // set timer - 1 latch
  1331. SoundMidiSendFM(base, 4, 0x21); // unmask & start T1
  1332. // this timer should go off in 80 us. It sometimes
  1333. // takes more than 100us, but will always have expired within
  1334. // 200 us if it is ever going to.
  1335. KeStallExecutionProcessor(200);
  1336. t2 = READ_PORT_UCHAR((PUCHAR)inbase); // read status register
  1337. SoundMidiSendFM(base, 4, 0x60);
  1338. SoundMidiSendFM(base, 4, 0x80);
  1339. if (!((t1 & 0xE0) == 0) || !((t2 & 0xE0) == 0xC0))
  1340. {
  1341. _DbgPrintF(DEBUGLVL_VERBOSE, ("SoundSynthPresent: returning false"));
  1342. return(FALSE);
  1343. }
  1344. _DbgPrintF(DEBUGLVL_VERBOSE, ("SoundSynthPresent: returning true"));
  1345. return TRUE;
  1346. }
  1347. // ==============================================================================
  1348. // this array gives the offsets of the slots within an opl2
  1349. // chip. This is needed to set the attenuation for all slots to max,
  1350. // to ensure that the chip is silenced completely - switching off the
  1351. // voices alone will not do this.
  1352. // ==============================================================================
  1353. BYTE offsetSlot[] =
  1354. {
  1355. 0, 1, 2, 3, 4, 5,
  1356. 8, 9, 10, 11, 12, 13,
  1357. 16, 17, 18, 19, 20, 21
  1358. };
  1359. #pragma code_seg()
  1360. // =========================================================================
  1361. // WriteMidiData
  1362. // Converts a MIDI atom into the corresponding FM transaction.
  1363. // =========================================================================
  1364. void
  1365. CMiniportMidiStreamFM::
  1366. WriteMidiData(DWORD dwData)
  1367. {
  1368. BYTE bMsgType,bChannel, bVelocity, bNote;
  1369. WORD wTemp;
  1370. KIRQL oldIrql;
  1371. bMsgType = (BYTE) dwData & (BYTE)0xf0;
  1372. bChannel = (BYTE) dwData & (BYTE)0x0f;
  1373. bNote = (BYTE) ((WORD) dwData >> 8) & (BYTE)0x7f;
  1374. bVelocity = (BYTE) (dwData >> 16) & (BYTE)0x7f;
  1375. #ifdef USE_KDPRINT
  1376. KdPrint(("'StreamFM::WriteMidiData: (%x %x %x) \n",bMsgType+bChannel,bNote,bVelocity));
  1377. #else // USE_KDPRINT
  1378. _DbgPrintF(DEBUGLVL_VERBOSE,("StreamFM::WriteMidiData: (%x %x %x) \n",bMsgType+bChannel,bNote,bVelocity));
  1379. #endif // USE_KDPRINT
  1380. KeAcquireSpinLock(&m_Miniport->m_SpinLock,&oldIrql);
  1381. switch (bMsgType)
  1382. {
  1383. case 0x90: /* turn key on, or key off if volume == 0 */
  1384. if (bVelocity)
  1385. {
  1386. if (bChannel == DRUMCHANNEL)
  1387. {
  1388. Opl3_NoteOn((BYTE)(bNote + 128),bNote,bChannel,bVelocity,(short)m_iBend[bChannel]);
  1389. }
  1390. else
  1391. {
  1392. Opl3_NoteOn((BYTE)m_bPatch[bChannel],bNote,bChannel,bVelocity,(short) m_iBend[bChannel]);
  1393. }
  1394. break;
  1395. } // if bVelocity.
  1396. //NOTE: no break specified here. On an else case we want to continue through and turn key off
  1397. case 0x80:
  1398. /* turn key off */
  1399. // we don't care what the velocity is on note off
  1400. if (bChannel == DRUMCHANNEL)
  1401. {
  1402. Opl3_NoteOff((BYTE) (bNote + 128),bNote, bChannel, 0);
  1403. }
  1404. else
  1405. {
  1406. Opl3_NoteOff ((BYTE) m_bPatch[bChannel],bNote, bChannel, m_bSustain[ bChannel ]);
  1407. }
  1408. break;
  1409. case 0xb0:
  1410. /* change control */
  1411. switch (bNote)
  1412. {
  1413. case 7:
  1414. /* change channel volume */
  1415. Opl3_ChannelVolume(bChannel,gbVelocityAtten[bVelocity >> 1]);
  1416. break;
  1417. case 8:
  1418. case 10:
  1419. /* change the pan level */
  1420. Opl3_SetPan(bChannel, bVelocity);
  1421. break;
  1422. case 64:
  1423. /* Change the sustain level */
  1424. Opl3_SetSustain(bChannel, bVelocity);
  1425. break;
  1426. default:
  1427. if (bNote >= 120) /* Channel mode messages */
  1428. {
  1429. Opl3_ChannelNotesOff(bChannel);
  1430. }
  1431. // else unknown controller
  1432. };
  1433. break;
  1434. case 0xc0:
  1435. if (bChannel != DRUMCHANNEL)
  1436. {
  1437. m_bPatch[ bChannel ] = bNote ;
  1438. }
  1439. break;
  1440. case 0xe0: // pitch bend
  1441. wTemp = ((WORD) bVelocity << 9) | ((WORD) bNote << 2);
  1442. m_iBend[bChannel] = (short) (WORD) (wTemp + 0x8000);
  1443. Opl3_PitchBend(bChannel, m_iBend[bChannel]);
  1444. break;
  1445. };
  1446. KeReleaseSpinLock(&m_Miniport->m_SpinLock,oldIrql);
  1447. return;
  1448. }
  1449. // ========================= opl3 specific methods ============================
  1450. #pragma code_seg()
  1451. // ==========================================================================
  1452. // Opl3_AllNotesOff - turn off all notes
  1453. // ==========================================================================
  1454. void
  1455. CMiniportMidiStreamFM::
  1456. Opl3_AllNotesOff()
  1457. {
  1458. BYTE i;
  1459. KIRQL oldIrql;
  1460. KeAcquireSpinLock(&m_Miniport->m_SpinLock,&oldIrql);
  1461. for (i = 0; i < NUM2VOICES; i++)
  1462. {
  1463. Opl3_NoteOff(m_Voice[i].bPatch, m_Voice[i].bNote, m_Voice[i].bChannel, 0);
  1464. }
  1465. KeReleaseSpinLock(&m_Miniport->m_SpinLock,oldIrql);
  1466. }
  1467. #pragma code_seg()
  1468. // ==========================================================================
  1469. // void Opl3_NoteOff
  1470. //
  1471. // Description:
  1472. // This turns off a note, including drums with a patch
  1473. // # of the drum note + 128, but the first drum instrument is at MIDI note _35_.
  1474. //
  1475. // Parameters:
  1476. // BYTE bPatch
  1477. // MIDI patch
  1478. //
  1479. // BYTE bNote
  1480. // MIDI note
  1481. //
  1482. // BYTE bChannel
  1483. // MIDI channel
  1484. //
  1485. // Return Value:
  1486. // Nothing.
  1487. //
  1488. //
  1489. // ==========================================================================
  1490. void
  1491. CMiniportMidiStreamFM::
  1492. Opl3_NoteOff
  1493. (
  1494. BYTE bPatch,
  1495. BYTE bNote,
  1496. BYTE bChannel,
  1497. BYTE bSustain
  1498. )
  1499. {
  1500. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1501. patchStruct FAR *lpPS ;
  1502. WORD wOffset, wTemp ;
  1503. // Find the note slot
  1504. wTemp = Opl3_FindFullSlot( bNote, bChannel ) ;
  1505. if (wTemp != 0xffff)
  1506. {
  1507. if (bSustain)
  1508. {
  1509. // This channel is sustained, don't really turn the note off,
  1510. // just flag it.
  1511. //
  1512. m_Voice[ wTemp ].bSusHeld = 1;
  1513. return;
  1514. }
  1515. // get a pointer to the patch
  1516. lpPS = glpPatch + (BYTE) m_Voice[ wTemp ].bPatch ;
  1517. // shut off the note portion
  1518. // we have the note slot, turn it off.
  1519. wOffset = wTemp;
  1520. if (wTemp >= (NUM2VOICES / 2))
  1521. wOffset += (0x100 - (NUM2VOICES / 2));
  1522. m_Miniport->SoundMidiSendFM(m_PortBase, AD_BLOCK + wOffset,
  1523. (BYTE)(m_Voice[ wTemp ].bBlock[ 0 ] & 0x1f) ) ;
  1524. // Note this...
  1525. m_Voice[ wTemp ].bOn = FALSE ;
  1526. m_Voice[ wTemp ].bBlock[ 0 ] &= 0x1f ;
  1527. m_Voice[ wTemp ].bBlock[ 1 ] &= 0x1f ;
  1528. m_Voice[ wTemp ].dwTime = m_dwCurTime ;
  1529. }
  1530. }
  1531. #pragma code_seg()
  1532. // ==========================================================================
  1533. // WORD Opl3_FindFullSlot
  1534. //
  1535. // Description:
  1536. // This finds a slot with a specific note, and channel.
  1537. // If it is not found then 0xFFFF is returned.
  1538. //
  1539. // Parameters:
  1540. // BYTE bNote
  1541. // MIDI note number
  1542. //
  1543. // BYTE bChannel
  1544. // MIDI channel #
  1545. //
  1546. // Return Value:
  1547. // WORD
  1548. // note slot #, or 0xFFFF if can't find it
  1549. //
  1550. //
  1551. // ==========================================================================
  1552. WORD
  1553. CMiniportMidiStreamFM::
  1554. Opl3_FindFullSlot
  1555. (
  1556. BYTE bNote,
  1557. BYTE bChannel
  1558. )
  1559. {
  1560. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1561. WORD i ;
  1562. for (i = 0; i < NUM2VOICES; i++)
  1563. {
  1564. if ((bChannel == m_Voice[ i ].bChannel)
  1565. && (bNote == m_Voice[ i ].bNote)
  1566. && (m_Voice[ i ].bOn))
  1567. {
  1568. return ( i ) ;
  1569. }
  1570. // couldn't find it
  1571. }
  1572. return ( 0xFFFF ) ;
  1573. }
  1574. #pragma code_seg()
  1575. //------------------------------------------------------------------------
  1576. // void Opl3_FMNote
  1577. //
  1578. // Description:
  1579. // Turns on an FM-synthesizer note.
  1580. //
  1581. // Parameters:
  1582. // WORD wNote
  1583. // the note number from 0 to NUMVOICES
  1584. //
  1585. // noteStruct FAR *lpSN
  1586. // structure containing information about what
  1587. // is to be played.
  1588. //
  1589. // Return Value:
  1590. // Nothing.
  1591. //------------------------------------------------------------------------
  1592. void
  1593. CMiniportMidiStreamFM::
  1594. Opl3_FMNote
  1595. (
  1596. WORD wNote,
  1597. noteStruct FAR * lpSN
  1598. )
  1599. {
  1600. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1601. WORD i ;
  1602. WORD wOffset ;
  1603. operStruct FAR *lpOS ;
  1604. // write out a note off, just to make sure...
  1605. wOffset = wNote;
  1606. if (wNote >= (NUM2VOICES / 2))
  1607. wOffset += (0x100 - (NUM2VOICES / 2));
  1608. m_Miniport->SoundMidiSendFM(m_PortBase, AD_BLOCK + wOffset, 0 ) ;
  1609. // writing the operator information
  1610. // for (i = 0; i < (WORD)((wNote < NUM4VOICES) ? NUMOPS : 2); i++)
  1611. for (i = 0; i < 2; i++)
  1612. {
  1613. lpOS = &lpSN -> op[ i ] ;
  1614. wOffset = gw2OpOffset[ wNote ][ i ] ;
  1615. m_Miniport->SoundMidiSendFM( m_PortBase, 0x20 + wOffset, lpOS -> bAt20) ;
  1616. m_Miniport->SoundMidiSendFM( m_PortBase, 0x40 + wOffset, lpOS -> bAt40) ;
  1617. m_Miniport->SoundMidiSendFM( m_PortBase, 0x60 + wOffset, lpOS -> bAt60) ;
  1618. m_Miniport->SoundMidiSendFM( m_PortBase, 0x80 + wOffset, lpOS -> bAt80) ;
  1619. m_Miniport->SoundMidiSendFM( m_PortBase, 0xE0 + wOffset, lpOS -> bAtE0) ;
  1620. }
  1621. // write out the voice information
  1622. wOffset = (wNote < 9) ? wNote : (wNote + 0x100 - 9) ;
  1623. m_Miniport->SoundMidiSendFM(m_PortBase, 0xa0 + wOffset, lpSN -> bAtA0[ 0 ] ) ;
  1624. m_Miniport->SoundMidiSendFM(m_PortBase, 0xc0 + wOffset, lpSN -> bAtC0[ 0 ] ) ;
  1625. // Note on...
  1626. m_Miniport->SoundMidiSendFM(m_PortBase, 0xb0 + wOffset,
  1627. (BYTE)(lpSN -> bAtB0[ 0 ] | 0x20) ) ;
  1628. } // end of Opl3_FMNote()
  1629. #pragma code_seg()
  1630. //=======================================================================
  1631. // WORD Opl3_NoteOn
  1632. //
  1633. // Description:
  1634. // This turns on a note, including drums with a patch # of the
  1635. // drum note + 0x80. The first GM drum instrument is mapped to note 35 instead of zero, though, so
  1636. // we expect 0 as the first drum patch (acoustic kick) if note 35 comes in.
  1637. //
  1638. // Parameters:
  1639. // BYTE bPatch
  1640. // MIDI patch
  1641. //
  1642. // BYTE bNote
  1643. // MIDI note
  1644. //
  1645. // BYTE bChannel
  1646. // MIDI channel
  1647. //
  1648. // BYTE bVelocity
  1649. // velocity value
  1650. //
  1651. // short iBend
  1652. // current pitch bend from -32768 to 32767
  1653. //
  1654. // Return Value:
  1655. // WORD
  1656. // note slot #, or 0xFFFF if it is inaudible
  1657. //=======================================================================
  1658. void
  1659. CMiniportMidiStreamFM::
  1660. Opl3_NoteOn
  1661. (
  1662. BYTE bPatch,
  1663. BYTE bNote,
  1664. BYTE bChannel,
  1665. BYTE bVelocity,
  1666. short iBend
  1667. )
  1668. {
  1669. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1670. WORD wTemp, i, j ;
  1671. BYTE b4Op, bTemp, bMode, bStereo ;
  1672. patchStruct FAR *lpPS ;
  1673. DWORD dwBasicPitch, dwPitch[ 2 ] ;
  1674. noteStruct NS ;
  1675. // Get a pointer to the patch
  1676. lpPS = glpPatch + bPatch ;
  1677. // Find out the basic pitch according to our
  1678. // note value. This may be adjusted because of
  1679. // pitch bends or special qualities for the note.
  1680. dwBasicPitch = gdwPitch[ bNote % 12 ] ;
  1681. bTemp = bNote / (BYTE) 12 ;
  1682. if (bTemp > (BYTE) (60 / 12))
  1683. dwBasicPitch = AsLSHL( dwBasicPitch, (BYTE)(bTemp - (BYTE)(60/12)) ) ;
  1684. else if (bTemp < (BYTE) (60/12))
  1685. dwBasicPitch = AsULSHR( dwBasicPitch, (BYTE)((BYTE) (60/12) - bTemp) ) ;
  1686. // Copy the note information over and modify
  1687. // the total level and pitch according to
  1688. // the velocity, midi volume, and tuning.
  1689. RtlCopyMemory( (LPSTR) &NS, (LPSTR) &lpPS -> note, sizeof( noteStruct ) ) ;
  1690. b4Op = (BYTE)(NS.bOp != PATCH_1_2OP) ;
  1691. for (j = 0; j < 2; j++)
  1692. {
  1693. // modify pitch
  1694. dwPitch[ j ] = dwBasicPitch ;
  1695. bTemp = (BYTE)((NS.bAtB0[ j ] >> 2) & 0x07) ;
  1696. if (bTemp > 4)
  1697. dwPitch[ j ] = AsLSHL( dwPitch[ j ], (BYTE)(bTemp - (BYTE)4) ) ;
  1698. else if (bTemp < 4)
  1699. dwPitch[ j ] = AsULSHR( dwPitch[ j ], (BYTE)((BYTE)4 - bTemp) ) ;
  1700. wTemp = Opl3_CalcFAndB( Opl3_CalcBend( dwPitch[ j ], iBend ) ) ;
  1701. NS.bAtA0[ j ] = (BYTE) wTemp ;
  1702. NS.bAtB0[ j ] = (BYTE) 0x20 | (BYTE) (wTemp >> 8) ;
  1703. }
  1704. // Modify level for each operator, but only
  1705. // if they are carrier waves
  1706. bMode = (BYTE) ((NS.bAtC0[ 0 ] & 0x01) * 2 + 4) ;
  1707. for (i = 0; i < 2; i++)
  1708. {
  1709. wTemp = (BYTE)
  1710. Opl3_CalcVolume( (BYTE)(NS.op[ i ].bAt40 & (BYTE) 0x3f),
  1711. bChannel,
  1712. bVelocity,
  1713. (BYTE) i,
  1714. bMode ) ;
  1715. NS.op[ i ].bAt40 = (NS.op[ i ].bAt40 & (BYTE)0xc0) | (BYTE) wTemp ;
  1716. }
  1717. // Do stereo panning, but cutting off a left or
  1718. // right channel if necessary...
  1719. bStereo = Opl3_CalcStereoMask( bChannel ) ;
  1720. NS.bAtC0[ 0 ] &= bStereo ;
  1721. // Find an empty slot, and use it...
  1722. wTemp = Opl3_FindEmptySlot( bPatch ) ;
  1723. Opl3_FMNote(wTemp, &NS ) ;
  1724. m_Voice[ wTemp ].bNote = bNote ;
  1725. m_Voice[ wTemp ].bChannel = bChannel ;
  1726. m_Voice[ wTemp ].bPatch = bPatch ;
  1727. m_Voice[ wTemp ].bVelocity = bVelocity ;
  1728. m_Voice[ wTemp ].bOn = TRUE ;
  1729. m_Voice[ wTemp ].dwTime = m_dwCurTime++ ;
  1730. m_Voice[ wTemp ].dwOrigPitch[0] = dwPitch[ 0 ] ; // not including bend
  1731. m_Voice[ wTemp ].dwOrigPitch[1] = dwPitch[ 1 ] ; // not including bend
  1732. m_Voice[ wTemp ].bBlock[0] = NS.bAtB0[ 0 ] ;
  1733. m_Voice[ wTemp ].bBlock[1] = NS.bAtB0[ 1 ] ;
  1734. m_Voice[ wTemp ].bSusHeld = 0;
  1735. } // end of Opl3_NoteOn()
  1736. #pragma code_seg()
  1737. //=======================================================================
  1738. //Opl3_CalcFAndB - Calculates the FNumber and Block given a frequency.
  1739. //
  1740. //inputs
  1741. // DWORD dwPitch - pitch
  1742. //returns
  1743. // WORD - High byte contains the 0xb0 section of the
  1744. // block and fNum, and the low byte contains the
  1745. // 0xa0 section of the fNumber
  1746. //=======================================================================
  1747. WORD
  1748. CMiniportMidiStreamFM::
  1749. Opl3_CalcFAndB(DWORD dwPitch)
  1750. {
  1751. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1752. BYTE bBlock;
  1753. /* bBlock is like an exponential to dwPitch (or FNumber) */
  1754. for (bBlock = 1; dwPitch >= 0x400; dwPitch >>= 1, bBlock++)
  1755. ;
  1756. if (bBlock > 0x07)
  1757. bBlock = 0x07; /* we cant do anything about this */
  1758. /* put in high two bits of F-num into bBlock */
  1759. return ((WORD) bBlock << 10) | (WORD) dwPitch;
  1760. }
  1761. #pragma code_seg()
  1762. //=======================================================================
  1763. //Opl3_CalcBend - This calculates the effects of pitch bend
  1764. // on an original value.
  1765. //
  1766. //inputs
  1767. // DWORD dwOrig - original frequency
  1768. // short iBend - from -32768 to 32768, -2 half steps to +2
  1769. //returns
  1770. // DWORD - new frequency
  1771. //=======================================================================
  1772. DWORD
  1773. CMiniportMidiStreamFM::
  1774. Opl3_CalcBend (DWORD dwOrig, short iBend)
  1775. {
  1776. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1777. DWORD dw;
  1778. /* do different things depending upon positive or
  1779. negative bend */
  1780. if (iBend > 0)
  1781. {
  1782. dw = (DWORD)((iBend * (LONG)(256.0 * (EQUAL * EQUAL - 1.0))) >> 8);
  1783. dwOrig += (DWORD)(AsULMUL(dw, dwOrig) >> 15);
  1784. }
  1785. else if (iBend < 0)
  1786. {
  1787. dw = (DWORD)(((-iBend) * (LONG)(256.0 * (1.0 - 1.0 / EQUAL / EQUAL))) >> 8);
  1788. dwOrig -= (DWORD)(AsULMUL(dw, dwOrig) >> 15);
  1789. }
  1790. return dwOrig;
  1791. }
  1792. #pragma code_seg()
  1793. //=======================================================================
  1794. // Opl3_CalcVolume - This calculates the attenuation for an operator.
  1795. //
  1796. //inputs
  1797. // BYTE bOrigAtten - original attenuation in 0.75 dB units
  1798. // BYTE bChannel - MIDI channel
  1799. // BYTE bVelocity - velocity of the note
  1800. // BYTE bOper - operator number (from 0 to 3)
  1801. // BYTE bMode - voice mode (from 0 through 7 for
  1802. // modulator/carrier selection)
  1803. //returns
  1804. // BYTE - new attenuation in 0.75 dB units, maxing out at 0x3f.
  1805. //=======================================================================
  1806. BYTE
  1807. CMiniportMidiStreamFM::
  1808. Opl3_CalcVolume(BYTE bOrigAtten,BYTE bChannel,BYTE bVelocity,BYTE bOper,BYTE bMode)
  1809. {
  1810. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1811. BYTE bVolume;
  1812. WORD wTemp;
  1813. WORD wMin;
  1814. switch (bMode) {
  1815. case 0:
  1816. bVolume = (BYTE)(bOper == 3);
  1817. break;
  1818. case 1:
  1819. bVolume = (BYTE)((bOper == 1) || (bOper == 3));
  1820. break;
  1821. case 2:
  1822. bVolume = (BYTE)((bOper == 0) || (bOper == 3));
  1823. break;
  1824. case 3:
  1825. bVolume = (BYTE)(bOper != 1);
  1826. break;
  1827. case 4:
  1828. bVolume = (BYTE)((bOper == 1) || (bOper == 3));
  1829. break;
  1830. case 5:
  1831. bVolume = (BYTE)(bOper >= 1);
  1832. break;
  1833. case 6:
  1834. bVolume = (BYTE)(bOper <= 2);
  1835. break;
  1836. case 7:
  1837. bVolume = TRUE;
  1838. break;
  1839. default:
  1840. bVolume = FALSE;
  1841. break;
  1842. };
  1843. if (!bVolume)
  1844. return bOrigAtten; /* this is a modulator wave */
  1845. wMin =(m_wSynthAttenL < m_wSynthAttenR) ? m_wSynthAttenL : m_wSynthAttenR;
  1846. wTemp = bOrigAtten +
  1847. ((wMin << 1) +
  1848. m_bChanAtten[bChannel] +
  1849. gbVelocityAtten[bVelocity >> 1]);
  1850. return (wTemp > 0x3f) ? (BYTE) 0x3f : (BYTE) wTemp;
  1851. }
  1852. #pragma code_seg()
  1853. // ===========================================================================
  1854. // Opl3_ChannelNotesOff - turn off all notes on a channel
  1855. // ===========================================================================
  1856. void
  1857. CMiniportMidiStreamFM::
  1858. Opl3_ChannelNotesOff(BYTE bChannel)
  1859. {
  1860. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1861. int i;
  1862. for (i = 0; i < NUM2VOICES; i++)
  1863. {
  1864. if ((m_Voice[ i ].bOn) && (m_Voice[ i ].bChannel == bChannel))
  1865. {
  1866. Opl3_NoteOff(m_Voice[i].bPatch, m_Voice[i].bNote,m_Voice[i].bChannel, 0) ;
  1867. }
  1868. }
  1869. }
  1870. #pragma code_seg()
  1871. // ===========================================================================
  1872. /* Opl3_ChannelVolume - set the volume level for an individual channel.
  1873. *
  1874. * inputs
  1875. * BYTE bChannel - channel number to change
  1876. * WORD wAtten - attenuation in 1.5 db units
  1877. *
  1878. * returns
  1879. * none
  1880. */
  1881. // ===========================================================================
  1882. void
  1883. CMiniportMidiStreamFM::
  1884. Opl3_ChannelVolume(BYTE bChannel, WORD wAtten)
  1885. {
  1886. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1887. m_bChanAtten[bChannel] = (BYTE)wAtten;
  1888. Opl3_SetVolume(bChannel);
  1889. }
  1890. #pragma code_seg()
  1891. // ===========================================================================
  1892. // void Opl3_SetVolume
  1893. //
  1894. // Description:
  1895. // This should be called if a volume level has changed.
  1896. // This will adjust the levels of all the playing voices.
  1897. //
  1898. // Parameters:
  1899. // BYTE bChannel
  1900. // channel # of 0xFF for all channels
  1901. //
  1902. // Return Value:
  1903. // Nothing.
  1904. //
  1905. // ===========================================================================
  1906. void
  1907. CMiniportMidiStreamFM::
  1908. Opl3_SetVolume
  1909. (
  1910. BYTE bChannel
  1911. )
  1912. {
  1913. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1914. WORD i, j, wTemp, wOffset ;
  1915. noteStruct FAR *lpPS ;
  1916. BYTE bMode, bStereo ;
  1917. // Make sure that we are actually open...
  1918. if (!glpPatch)
  1919. return ;
  1920. // Loop through all the notes looking for the right
  1921. // channel. Anything with the right channel gets
  1922. // its pitch bent.
  1923. for (i = 0; i < NUM2VOICES; i++)
  1924. {
  1925. if ((m_Voice[ i ].bChannel == bChannel) || (bChannel == 0xff))
  1926. {
  1927. // Get a pointer to the patch
  1928. lpPS = &(glpPatch + m_Voice[ i ].bPatch) -> note ;
  1929. // Modify level for each operator, IF they are carrier waves...
  1930. bMode = (BYTE) ( (lpPS->bAtC0[0] & 0x01) * 2 + 4);
  1931. for (j = 0; j < 2; j++)
  1932. {
  1933. wTemp = (BYTE) Opl3_CalcVolume(
  1934. (BYTE) (lpPS -> op[j].bAt40 & (BYTE) 0x3f),
  1935. m_Voice[i].bChannel, m_Voice[i].bVelocity,
  1936. (BYTE) j, bMode ) ;
  1937. // Write new value.
  1938. wOffset = gw2OpOffset[ i ][ j ] ;
  1939. m_Miniport->SoundMidiSendFM(
  1940. m_PortBase, 0x40 + wOffset,
  1941. (BYTE) ((lpPS -> op[j].bAt40 & (BYTE)0xc0) | (BYTE) wTemp) ) ;
  1942. }
  1943. // Do stereo pan, but cut left or right channel if needed.
  1944. bStereo = Opl3_CalcStereoMask( m_Voice[ i ].bChannel ) ;
  1945. wOffset = i;
  1946. if (i >= (NUM2VOICES / 2))
  1947. wOffset += (0x100 - (NUM2VOICES / 2));
  1948. m_Miniport->SoundMidiSendFM(m_PortBase, 0xc0 + wOffset, (BYTE)(lpPS -> bAtC0[ 0 ] & bStereo) ) ;
  1949. }
  1950. }
  1951. } // end of Opl3_SetVolume
  1952. #pragma code_seg()
  1953. // ===========================================================================
  1954. // Opl3_SetPan - set the left-right pan position.
  1955. //
  1956. // inputs
  1957. // BYTE bChannel - channel number to alter
  1958. // BYTE bPan - 0-47 for left, 81-127 for right, or somewhere in the middle.
  1959. //
  1960. // returns - none
  1961. //
  1962. // As a side note, I think it's odd that (since 64 = CENTER, 127 = RIGHT and 0 = LEFT)
  1963. // there are 63 intermediate gradations for the left side, but 62 for the right.
  1964. // ===========================================================================
  1965. void
  1966. CMiniportMidiStreamFM::
  1967. Opl3_SetPan(BYTE bChannel, BYTE bPan)
  1968. {
  1969. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  1970. /* change the pan level */
  1971. if (bPan > (64 + 16))
  1972. m_bStereoMask[bChannel] = 0xef; /* let only right channel through */
  1973. else if (bPan < (64 - 16))
  1974. m_bStereoMask[bChannel] = 0xdf; /* let only left channel through */
  1975. else
  1976. m_bStereoMask[bChannel] = 0xff; /* let both channels */
  1977. /* change any curently playing patches */
  1978. Opl3_SetVolume(bChannel);
  1979. }
  1980. #pragma code_seg()
  1981. // ===========================================================================
  1982. // void Opl3_PitchBend
  1983. //
  1984. // Description:
  1985. // This pitch bends a channel.
  1986. //
  1987. // Parameters:
  1988. // BYTE bChannel
  1989. // channel
  1990. //
  1991. // short iBend
  1992. // values from -32768 to 32767, being -2 to +2 half steps
  1993. //
  1994. // Return Value:
  1995. // Nothing.
  1996. // ===========================================================================
  1997. void
  1998. CMiniportMidiStreamFM::
  1999. Opl3_PitchBend
  2000. (
  2001. BYTE bChannel,
  2002. short iBend
  2003. )
  2004. {
  2005. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  2006. WORD i, wTemp[ 2 ], wOffset, j ;
  2007. DWORD dwNew ;
  2008. // Remember the current bend..
  2009. m_iBend[ bChannel ] = iBend ;
  2010. // Loop through all the notes looking for
  2011. // the correct channel. Anything with the
  2012. // correct channel gets its pitch bent...
  2013. for (i = 0; i < NUM2VOICES; i++)
  2014. if (m_Voice[ i ].bChannel == bChannel)
  2015. {
  2016. j = 0 ;
  2017. dwNew = Opl3_CalcBend( m_Voice[ i ].dwOrigPitch[ j ], iBend ) ;
  2018. wTemp[ j ] = Opl3_CalcFAndB( dwNew ) ;
  2019. m_Voice[ i ].bBlock[ j ] =
  2020. (m_Voice[ i ].bBlock[ j ] & (BYTE) 0xe0) |
  2021. (BYTE) (wTemp[ j ] >> 8) ;
  2022. wOffset = i;
  2023. if (i >= (NUM2VOICES / 2))
  2024. wOffset += (0x100 - (NUM2VOICES / 2));
  2025. m_Miniport->SoundMidiSendFM(m_PortBase, AD_BLOCK + wOffset, m_Voice[ i ].bBlock[ 0 ] ) ;
  2026. m_Miniport->SoundMidiSendFM(m_PortBase, AD_FNUMBER + wOffset, (BYTE) wTemp[ 0 ] ) ;
  2027. }
  2028. } // end of Opl3_PitchBend
  2029. #pragma code_seg()
  2030. // ===========================================================================
  2031. // Opl3_CalcStereoMask - This calculates the stereo mask.
  2032. //
  2033. // inputs
  2034. // BYTE bChannel - MIDI channel
  2035. // returns
  2036. // BYTE mask (for register 0xc0-c8) for eliminating the
  2037. // left/right/both channels
  2038. // ===========================================================================
  2039. BYTE
  2040. CMiniportMidiStreamFM::
  2041. Opl3_CalcStereoMask(BYTE bChannel)
  2042. {
  2043. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  2044. WORD wLeft, wRight;
  2045. /* figure out the basic levels of the 2 channels */
  2046. wLeft = (m_wSynthAttenL << 1) + m_bChanAtten[bChannel];
  2047. wRight = (m_wSynthAttenR << 1) + m_bChanAtten[bChannel];
  2048. /* if both are too quiet then mask to nothing */
  2049. if ((wLeft > 0x3f) && (wRight > 0x3f))
  2050. return 0xcf;
  2051. /* if one channel is significantly quieter than the other than
  2052. eliminate it */
  2053. if ((wLeft + 8) < wRight)
  2054. return (BYTE)(0xef & m_bStereoMask[bChannel]); /* right is too quiet so eliminate */
  2055. else if ((wRight + 8) < wLeft)
  2056. return (BYTE)(0xdf & m_bStereoMask[bChannel]); /* left too quiet so eliminate */
  2057. else
  2058. return (BYTE)(m_bStereoMask[bChannel]); /* use both channels */
  2059. }
  2060. #pragma code_seg()
  2061. //------------------------------------------------------------------------
  2062. // WORD Opl3_FindEmptySlot
  2063. //
  2064. // Description:
  2065. // This finds an empty note-slot for a MIDI voice.
  2066. // If there are no empty slots then this looks for the oldest
  2067. // off note. If this doesn't work then it looks for the oldest
  2068. // on-note of the same patch. If all notes are still on then
  2069. // this finds the oldests turned-on-note.
  2070. //
  2071. // Parameters:
  2072. // BYTE bPatch
  2073. // MIDI patch that will replace it.
  2074. //
  2075. // Return Value:
  2076. // WORD
  2077. // note slot #
  2078. //
  2079. //
  2080. //------------------------------------------------------------------------
  2081. WORD
  2082. CMiniportMidiStreamFM::
  2083. Opl3_FindEmptySlot(BYTE bPatch)
  2084. {
  2085. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  2086. WORD i, found ;
  2087. DWORD dwOldest ;
  2088. // First, look for a slot with a time == 0
  2089. for (i = 0; i < NUM2VOICES; i++)
  2090. if (!m_Voice[ i ].dwTime)
  2091. return ( i ) ;
  2092. // Now, look for a slot of the oldest off-note
  2093. dwOldest = 0xffffffff ;
  2094. found = 0xffff ;
  2095. for (i = 0; i < NUM2VOICES; i++)
  2096. if (!m_Voice[ i ].bOn && (m_Voice[ i ].dwTime < dwOldest))
  2097. {
  2098. dwOldest = m_Voice[ i ].dwTime ;
  2099. found = i ;
  2100. }
  2101. if (found != 0xffff)
  2102. return ( found ) ;
  2103. // Now, look for a slot of the oldest note with
  2104. // the same patch
  2105. dwOldest = 0xffffffff ;
  2106. found = 0xffff ;
  2107. for (i = 0; i < NUM2VOICES; i++)
  2108. if ((m_Voice[ i ].bPatch == bPatch) && (m_Voice[ i ].dwTime < dwOldest))
  2109. {
  2110. dwOldest = m_Voice[ i ].dwTime ;
  2111. found = i ;
  2112. }
  2113. if (found != 0xffff)
  2114. return ( found ) ;
  2115. // Now, just look for the oldest voice
  2116. found = 0 ;
  2117. dwOldest = m_Voice[ found ].dwTime ;
  2118. for (i = (found + 1); i < NUM2VOICES; i++)
  2119. if (m_Voice[ i ].dwTime < dwOldest)
  2120. {
  2121. dwOldest = m_Voice[ i ].dwTime ;
  2122. found = i ;
  2123. }
  2124. return ( found ) ;
  2125. } // end of Opl3_FindEmptySlot()
  2126. #pragma code_seg()
  2127. //------------------------------------------------------------------------
  2128. // WORD Opl3_SetSustain
  2129. //
  2130. // Description:
  2131. // Set the sustain controller on the current channel.
  2132. //
  2133. // Parameters:
  2134. // BYTE bSusLevel
  2135. // The new sustain level
  2136. //
  2137. //
  2138. //------------------------------------------------------------------------
  2139. VOID
  2140. CMiniportMidiStreamFM::
  2141. Opl3_SetSustain(BYTE bChannel,BYTE bSusLevel)
  2142. {
  2143. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  2144. WORD i;
  2145. if (m_bSustain[ bChannel ] && !bSusLevel)
  2146. {
  2147. // Sustain has just been turned off for this channel
  2148. // Go through and turn off all notes that are being held for sustain
  2149. //
  2150. for (i = 0; i < NUM2VOICES; i++)
  2151. {
  2152. if ((bChannel == m_Voice[ i ].bChannel) &&
  2153. m_Voice[ i ].bSusHeld)
  2154. {
  2155. Opl3_NoteOff(m_Voice[i].bPatch, m_Voice[i].bNote, m_Voice[i].bChannel, 0);
  2156. }
  2157. }
  2158. }
  2159. m_bSustain[ bChannel ] = bSusLevel;
  2160. }