home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 4 Drivers / 04-Drivers.zip / mpudev10.zip / mpudev.INF (.txt) < prev    next >
OS/2 Help File  |  1995-09-22  |  52KB  |  1,238 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Introduction ΓòÉΓòÉΓòÉ
  3.  
  4.                                 MPUDEV Driver 1.0
  5.  
  6. Please read this document in entirety.  I took the time to make it explicit, 
  7. clear, and very useful.  It took me longer to write it than it will ever take 
  8. you to read it.  Therefore, you owe it to both of us to read it.  OK? 
  9.  
  10. The MPUDEV.SYS driver is an OS/2 device driver for OS/2 2.X+ designed to work 
  11. with any audio/MIDI card containing an MPU-401 compatible MIDI interface. 
  12. (Only MPU-401 UART mode is required of that card).  It allows a program to do 
  13. MIDI input and output through that card.  Such cards include Roland's MPU-401 
  14. and SuperMPU, and MusicQuest's MQX-16 and MQX-32, as well as many other cards. 
  15. Many sound cards, including the Roland RAP-10 and SCC-1 also have an MPU-401 
  16. compatible interface, and also internally attach a built-in General MIDI sound 
  17. module to that interface.  So this driver can be used to send MIDI data to play 
  18. that card's built-in GM module, or adjust its parameters via System Exclusive 
  19. MIDI messages. 
  20.  
  21. MPUDEV.SYS is not an MMPM (ie, MultiMedia Presentation Manager) driver. 
  22. MPUDEV.SYS will not work with MMPM programs.  It requires a program written to 
  23. specifically follow my own protocol for dealing with MIDI input and output.  I 
  24. myself have written a number of such free programs including a System Exclusive 
  25. dump utility, a MIDI piano controller program, a utility to view incoming MIDI 
  26. data, etc.  Programming information is available in this manual for others to 
  27. write programs to use this driver, in languages such as C or even REXX.  There 
  28. is also information for making drivers and Dynamic Link Libraries for other 
  29. hardware, maintaining compatibility with MPUDEV.SYS and its DLLs, so that 
  30. programs which use MPUDEV can be used with other hardware as well. 
  31.  
  32.  
  33. Some of the words in this manual are highlighted in bold text, such as Simple 
  34. Approach.  These are words that refer to MPUDEV definitions.  Other words are 
  35. in colored text such as Channel Pressure.  These refer to MIDI messages (ie, 
  36. data).  Underlined words, such as Pitch Wheel, refer to hardware, such as if I 
  37. was referring to the Pitch Wheel on your MIDI unit.  Words that are in colored 
  38. text such as Read This are meant to be emphasized.  Words in italics refer to 
  39. aspects of OS/2. 
  40.  
  41.  
  42. ΓòÉΓòÉΓòÉ 2. Copyright ΓòÉΓòÉΓòÉ
  43.  
  44. This OS/2 Online Book and the related file MPUDEV.SYS are all copyright 1995 by 
  45. Jeff Glatt.  These files are freely redistributable, and may be used by and 
  46. distributed along with any software, be it commercial or otherwise, provided 
  47. that these files are not internally modified, nor specifically sold as a 
  48. complete product by themselves.  The only price that you have to pay is the one 
  49. that you're already paying by spending all of your time in front of a computer 
  50. instead of developing healthier outlets. 
  51.  
  52. NOT SO STANDARD DISCLAIMER: 
  53.  
  54. These programs are provided "as is" without warranty of any kind either 
  55. expressed or implied or tatooed in a place that only a few people have ever 
  56. seen, including but not limited to the implied warranties of merchantability, 
  57. fitness for a particular purpose, and the dubious assumption that the software 
  58. has been created by a sane individual who would never do anything that may hurt 
  59. you. The entire risk as to the results and performance of the programs is 
  60. assumed by you or someone who looks exactly like you.  Jeff Glatt does not 
  61. guarantee that the functions in these programs will meet your requirements, 
  62. especially if your requirements involve lots of latex and some docile, 
  63. domesticated animal.  Nor does Jeff Glatt warranty the programs to be 
  64. uninterruptable or error-free, although mercifully free of "General Protection 
  65. Faults".  If you use said programs, you can not say anything nasty about the 
  66. author, even if the programs inadvertently cause the erasure of your collection 
  67. of X-rated GIFs of a conservative, overweight and overrated TV "personality" 
  68. plooking himself vigorously with his royalty checks from some rancid paperback. 
  69. Jeff Glatt is not responsible for any damages as a result of anything that he 
  70. has done, or hasn't done, or was supposed to do but never got around to it, and 
  71. furthermore, he doesn't even care so leave him alone, ratface.  You may have 
  72. more or less protections in certain states of the union, depending upon how far 
  73. your local politician is willing to bend over for some bribe from a business 
  74. lobbyist.  Just remember that Jeff Glatt has no money, so don't bother suing 
  75. him as a result of any damages caused by this OS/2 program.  Tell your greasy 
  76. lawyer to go after IBM, and make sure that you pick 12 really stupid pinheads 
  77. for the jury.  If swallowed, induce vomiting immediately by contemplating the 
  78. asthetics of Microsoft Windows. 
  79.  
  80. OS/2 is a trademark (and mismarketed product) of International Business 
  81. Machines Corporation. 
  82.  
  83. Windows is a trademark of Microsoft Incorporated, and furthermore, Bill Gates 
  84. is to blame for it. 
  85.  
  86. If you have unreasonably presumptuous suggestions (ie, an enduser who expects 
  87. outrageous amounts of free support), snide comments, criticisms, and anything 
  88. else other than dollar bills, then send them to someone else because you got it 
  89. for free, and you know what you get for nothing?  On the other hand, any type 
  90. of positive contribution from other programmers is very much welcome and 
  91. encouraged as these are the only folks who can made things happen for OS/2. 
  92. IBM ain't gonna do it.  If you do need to contact the author, then either phone 
  93. some of the more prominent psychiatrict clinics in central New York state, or 
  94. try this: 
  95.  
  96. Jeff Glatt 
  97. 6 Sycamore Drive East 
  98. New Hartford, NY 13413 
  99. (315) 735-5350 
  100.  
  101. Sure, this copyright notice has attitude.  Get used to it, or kill yourself. 
  102.  
  103.  
  104. ΓòÉΓòÉΓòÉ 3. Setup ΓòÉΓòÉΓòÉ
  105.  
  106. First of all, in order to use MPUDEV.SYS, you need a card that offers MPU-401 
  107. hardware compatibility.  The card can't simply offer MPU-401 emulation through 
  108. a software driver or TSR.  It must have the 2 MPU-401 ports; STATUS and DATA. 
  109. It must implement MPU-401 UART mode (ie, Intelligent Mode isn't needed).  Bit 
  110. #6 (DRR) of the STATUS port must be clear when the card is ready to accept 
  111. another byte for output.  Bit #7 (DSR) of the STATUS port must be clear when 
  112. the card has an incoming MIDI byte waiting to be read.  The bi-directional DATA 
  113. port is used to output and input the MIDI data bytes.  The card may also have 
  114. the MPU-401's COMMAND port if it supports Intelligent mode, although MPUDEV.SYS 
  115. doesn't require this since Intelligent mode isn't used.  (Actually, the COMMAND 
  116. port is really half of a bi-directional STATUS port).  Check with your card's 
  117. manufacturer or literature to verify that it supports MPU-401 UART mode in 
  118. hardware.  If necessary, try to get ahold of a technician who knows something 
  119. about his own company's products (good luck), read this here paragraph to him, 
  120. and ask him if it's applicable to his company's audio/MIDI card. 
  121.  
  122. Next, you need to copy the driver to a convenient place where OS/2 can access 
  123. it when booting up.  You also have to add a line to your CONFIG.SYS file to 
  124. have OS/2 automatically setup the driver each time OS/2 boots.  The line begins 
  125. with the DEVICE= statement, followed by the driver's filename, and the driver's 
  126. arguments. 
  127.  
  128. The driver's filename is MPUDEV.SYS.  If you place the driver in some directory 
  129. where OS/2 normally looks for drivers, then you only need specify this as the 
  130. filename.  Otherwise, you need to tell OS/2 the complete path where to locate 
  131. the driver.  For example, if you place the driver in a directory called blort 
  132. on your D: drive, then your statement would begin as so: 
  133.  
  134. DEVICE=D:\BLORT\MPUDEV.SYS 
  135.  
  136. After this, upon the same line, you list arguments for the driver.  MPUDEV.SYS 
  137. can control up to 4 MPU-401 compatible devices.  These 4 units have the 
  138. internal names of MPUDEV1$, MPUDEV2$, MPUDEV3$, and MPUDEV4$.  You specify the 
  139. arguments for each unit within a set of brackets, for example: 
  140.  
  141. DEVICE=MPUDEV.SYS [the args for unit 1 go here] [the args for unit 2 go here] 
  142.  
  143. You don't need to specify args for all 4 units if you don't actually have 4 
  144. such cards.  The above example only specifies args for the first two units; 
  145. MPUDEV1$ and MPUDEV2$. 
  146.  
  147. Args are optional.  If you don't specify them, then defaults are used.  Even 
  148. you want want all default values, you still need to have at least the opening 
  149. and closing bracket for the unit's specification.  Args can usually be placed 
  150. in any order, as long as they remain within the brackets for their intended 
  151. unit.  The arguments are: 
  152.  
  153. Pxxx                     The base (ie, I/O) address where the card is 
  154.                          installed.  xxx is that address in hexadecimal.  If 
  155.                          you don't specify this arg, then MPUDEV assumes that 
  156.                          this unit is at a base address of 330.  Most cards 
  157.                          have jumpers that allow them to be set to various base 
  158.                          addresses.  You must make sure that your card is not 
  159.                          set to the same base address as any other card in your 
  160.                          system (regardless of what type of card it is). 
  161.  
  162. Ixx                      The Interrupt (ie, IRQ) number that the card uses. 
  163.                          xxx is that Interrupt number in decimal.  If you don't 
  164.                          specify this arg, then MPUDEV assumes that this unit 
  165.                          uses Interrupt 9.  Most cards have jumpers that allow 
  166.                          them to be set to various IRQ numbers.  You must make 
  167.                          sure that your card is not set to the same IRQ as any 
  168.                          other card in your system (regardless of what type of 
  169.                          card it is). 
  170.  
  171. /Q                       This suppresses informational messages about this unit 
  172.                          that the driver prints to the screen during OS/2's 
  173.                          boot process.  If you don't specify this arg, then the 
  174.                          driver prints out the Version and Revision numbers of 
  175.                          the unit. 
  176.  
  177. /U                       The driver skips setting the card into MPU-401 UART 
  178.                          mode.  If the card doesn't have the MPU-401's COMMAND 
  179.                          port (ie, Roland cards such as the MPU-401, RAP-10, 
  180.                          and SCC-1 do have this port), then you should specify 
  181.                          this option.  Undoubtably, the card will powerup in 
  182.                          MPU-401 UART mode.  If it doesn't, you'll need some 
  183.                          other software to force it (and keep it) in this mode 
  184.                          before any program can utilize MPUDEV.SYS to do MIDI 
  185.                          I/O.  Skipping this initialization means that MPUDEV's 
  186.                          Revision and Version commands do not return the actual 
  187.                          information supplied from the card itself, and default 
  188.                          to 0.  Furthermore, MPUDEV skips checking that the 
  189.                          card is indeed an MPU-401 UART compatible.  In fact, 
  190.                          if you see the message Unit X failed to respond. Bad? 
  191.                          where X is the unit number, then this means that 
  192.                          either you've specified the wrong base address, or the 
  193.                          card doesn't have a COMMAND port.  If you specify /U 
  194.                          and reboot, and the card appears to be working with 
  195.                          MPUDEV, then you've got a card without a COMMAND port. 
  196.                          If you don't specify this arg, then the driver 
  197.                          switches the card into MPU-401 UART mode, checks that 
  198.                          it has the COMMAND port (and therefore is definitely 
  199.                          an MPU-401 compatible), and retrieves the version and 
  200.                          revision numbers of the card. 
  201.  
  202. /R                       The card is a Roland RAP-10.  This causes the driver 
  203.                          to handle the card in a manner that is more flexible 
  204.                          and efficient for this card.  If you don't specify 
  205.                          this arg, then the driver assumes that the card is a 
  206.                          plain MPU-401 compatible. 
  207.  
  208. So, if you have one card installed at address 220 using interrupt 5, it's a 
  209. RAP-10, and you want it to be the first unit (ie, MPUDEV1$), then your line 
  210. would be: 
  211.  
  212. DEVICE=MPUDEV.SYS [P220 I5 /R] 
  213.  
  214. It's possible to skip some of the units.  For example, if you wanted to skip 
  215. the first two units (ie, MPUDEV1$ and MPUDEV2$) and set the above device to be 
  216. the third unit (ie, MPUDEV3$), then you'd include the brackets for the first 2 
  217. units, but set their base address to 0 which means to skip the unit. 
  218.  
  219. DEVICE=MPUDEV.SYS [P000] [P000] [P220 I5 /R] 
  220.  
  221.  
  222. ΓòÉΓòÉΓòÉ 4. Use ΓòÉΓòÉΓòÉ
  223.  
  224. Check the documentation with your software to see if can be made to work with 
  225. MPUDEV.  If the docs don't say outright that the program works with this 
  226. driver, it still may work if the program outputs MIDI data using DosWrite() and 
  227. inputs MIDI data using DosRead() and can be made to DosOpen() one of the MPUDEV 
  228. units (ie, MPUDEV1$, MPUDEV2$, MPUDEV3$, or MPUDEV4$).  The MIDI data must be 
  229. in raw binary format (ie, without any Time-stamp bytes like what MMPM adds to 
  230. the MIDI data). 
  231.  
  232.  
  233. ΓòÉΓòÉΓòÉ 5. Driver Sharing ΓòÉΓòÉΓòÉ
  234.  
  235. Some programs forbid other simultaneously running programs from reading and/or 
  236. writing MIDI data to the same driver.  This is referred to as not sharing the 
  237. driver.  Usually, such a program will open the driver, do its MIDI input or 
  238. output, and then close the driver so that other programs can subsequently use 
  239. the driver.  While the program has the driver open and is using it, other 
  240. programs are prevented from opening and using that driver.  In this case, 
  241. you'll have to wait for the program to complete its "MIDI task" (and close the 
  242. driver) before trying to use other programs which use the same driver, and vice 
  243. versa. 
  244.  
  245. Sometimes, a program that doesn't share the driver will open up the driver and 
  246. leave it open for the life of the program, thus preventing any other programs 
  247. from ever reading and/or writing to that driver while this program is running. 
  248. In this case, you'll have to terminate such a program when you want to use 
  249. another program to access the same driver. 
  250.  
  251. Obviously, a program's inability to share a driver could cause undue hassles 
  252. under a multi-tasking system, particularly with a program that never closes an 
  253. open driver. 
  254.  
  255. Some programs allow shared access to a driver.  Such a program allows other 
  256. programs to read and/or write MIDI data to that driver while this program is 
  257. running, and maybe even while this program is reading or writing MIDI data 
  258. itself.  But, there are caveats with such driver sharing.  Programs that share 
  259. access to a driver should either cooperate with each other to manage that 
  260. shared access, or carry out a complete MIDI operation with each call to the 
  261. driver, for example, not output only part of one MIDI message with one call to 
  262. DosWrite(), and then subsequently output the remainder of that one MIDI message 
  263. with another call to DosWrite(). 
  264.  
  265. You should avoid simultaneously running two programs that allow shared MIDI 
  266. input from the same driver unless at least one of the programs looks for MIDI 
  267. input ONLY when performing a specific operation, and otherwise doesn't do any 
  268. DosRead() to the driver while "sitting idle".  So, that one program may be left 
  269. "sitting idle" without interfering with the operation of the other program that 
  270. reads from the same driver.  On the other hand, if the other program doesn't 
  271. also stop reading MIDI data while it is "sitting idle", it could interfere with 
  272. the first program's input.  In that case, you'll be forced to run the second 
  273. program only when needed, let it do its thing, and then terminate it before 
  274. going back to use the first program for MIDI input.  In other words, you won't 
  275. be able to let that second program continue running, even though it allows 
  276. shared driver access, while operating the first program. 
  277.  
  278. Shared MIDI output is usually fine, since when programs are "sitting idle", 
  279. they usually aren't writing to the driver.  So, it's generally OK to 
  280. simultaneously run two programs that do MIDI output to the same driver, as long 
  281. as both programs aren't performing such operations simultaneously (ie, one 
  282. program is "sitting idle" while the other is performing an operation that 
  283. causes MIDI data to be output). 
  284.  
  285. On the other hand, it may be possible for both programs to simultaneously 
  286. output MIDI data to the same driver.  But both programs need to DosWrite() full 
  287. MIDI messages to the driver (ie, not break up one MIDI message into several 
  288. calls to DosWrite()) and resolve Running Status at the start of each write.  If 
  289. a program doesn't conform to these restriction, then its MIDI output may be 
  290. destroyed by another program attempting to output MIDI data simultaneously.  In 
  291. that case, stick to preceding precaution (ie, make sure that only one program 
  292. at a time is outputting MIDI data, and other simultaneously running programs 
  293. are "sitting idle" during that time).  Of course, even if the two programs 
  294. conform to this restriction, and therefore, their MIDI data can be successfully 
  295. "merged", this merge process will slow down the operations of each program, and 
  296. may cause each program to experience significantly longer delays in MIDI output 
  297. than if both programs weren't simultaneously outputting MIDI data.  This may or 
  298. may not make any difference to you, the program, and/or any external MIDI 
  299. devices connected to the computer. 
  300.  
  301. Some drivers, such as MPUDEV, support several independent "ports" or units (ie, 
  302. each unit has its own MIDI hardware interface card), and each unit has its own 
  303. shared status.  Therefore, it's perfectly acceptable to have two programs 
  304. running which both use MPUDEV simultaneously, and you won't have to worry about 
  305. any of the above sharing conditions as long as each program uses a different 
  306. unit, for example, one program uses MPUDEV1$ and the other program uses 
  307. MPUDEV2$. 
  308.  
  309.  
  310. ΓòÉΓòÉΓòÉ 6. Error Messages ΓòÉΓòÉΓòÉ
  311.  
  312. Here are the possible error messages that you may see when MPUDEV.SYS is being 
  313. booted.  Following each message is a description of likely causes for that 
  314. error and possible remedies. 
  315.  
  316.  
  317. ΓòÉΓòÉΓòÉ 6.1. Unit X failed to respond. Bad? ΓòÉΓòÉΓòÉ
  318.  
  319. Synopsis       The card didn't respond to an MPU-401 command, within a 
  320.                reasonable amount of time, that the driver issued to the card. 
  321.                Therefore, MPUDEV aborted the card's installation. 
  322.  
  323. Cause          The base address that you specified for this unit is not the 
  324.                same as the base address that the card is really set to. 
  325.  
  326. Cure           Check the base address jumpers on your card, and either set them 
  327.                to what you specified, or change your CONFIG.SYS DEVICE 
  328.                statement for MPUDEV to reflect the true base address. 
  329.  
  330. Cause          The card doesn't have an MPU-401 COMMAND port.  (It may have the 
  331.                STATUS and DATA ports, which is all that is really needed by 
  332.                MPUDEV). 
  333.  
  334. Cure           Specify the /U option in your CONFIG.SYS file as described in 
  335.                Setup so that MPUDEV assumes that the card is already in MPU-401 
  336.                UART mode and doesn't understand MPU-401 commands, and then 
  337.                reboot. 
  338.  
  339. Cause          The card isn't MPU-401 compatible, meaning that it doesn't have 
  340.                the MPU-401's STATUS and DATA ports. 
  341.  
  342. Cure           Throw out that junk, and buy an MPU-401 compatible. 
  343.  
  344. Result         The unit is not installed, and any program that tries to open 
  345.                this unit will get an error return. 
  346.  
  347.  
  348. ΓòÉΓòÉΓòÉ 6.2. Timer install failed! ΓòÉΓòÉΓòÉ
  349.  
  350. Synopsis       OS/2 didn't fulfill the driver's request to install a timer 
  351.                handler. 
  352.  
  353. Cause          There's some problem with OS/2's internal driver handling. 
  354.  
  355. Cure           Close down and try rebooting again.  Maybe comment out other 
  356.                drivers to see if there's some conflict (doubtful). 
  357.  
  358. Result         The driver fails to install, and any program that tries to open 
  359.                any of its units will get an error. 
  360.  
  361.  
  362. ΓòÉΓòÉΓòÉ 7. Programming ΓòÉΓòÉΓòÉ
  363.  
  364. The following sections deal with writing programs that use MPUDEV (or 
  365. compatible drivers), as well as developing MPUDEV compatible drivers for other 
  366. hardware (ie, besides MPU-401 UART mode cards). 
  367.  
  368. MIDI programs may be written in compiled languages or even REXX. 
  369.  
  370. Endusers do not need to read the next sections, and will probably be bored and 
  371. confused by such.  On the other hand, an enduser is encouraged to suggest to 
  372. OS/2 programmers that they check out such, especially if the enduser wants more 
  373. OS/2 MIDI software, or wants to use some MPUDEV compatible programs with his 
  374. hardware. 
  375.  
  376. Please read the preceding sections, particularly Driver Sharing first. 
  377.  
  378.  
  379. ΓòÉΓòÉΓòÉ 7.1. Programs ΓòÉΓòÉΓòÉ
  380.  
  381. There are two different approaches that a program can use to access MPUDEV, 
  382. depending upon whether you prefer simplicity or speed.  I'll arbitrarily call 
  383. these the Simple and Speed Approaches.  Don't worry.  This isn't nearly as bad 
  384. as MMPM, and I promise not to dish out the sort of poorly expressed, 
  385. excessively jargon-infested garble that you get from IBM's MMPM documentation. 
  386.  
  387. When I refer to sequencing, I'm referring to the process of recorded each MIDI 
  388. message along with the time that it is received, and then later playing back 
  389. all of those recorded MIDI messages at their relative times.  In other words, 
  390. the computer records the entire musical performance including not only the 
  391. actual notes and effects played (ie, MIDI data), but also the rhythms of that 
  392. performance (ie, the time interval inbetween each note or effect).  Then, the 
  393. computer later "plays back" all of those notes and effects, recreating the 
  394. original rhythm of the performance. 
  395.  
  396. The Speed Approach is faster than MMPM, while allowing for a LOT more 
  397. flexibility and better performance for the purposes of sequencing.  (Actually, 
  398. MMPM doesn't even support recording time-stamped MIDI data, and is therefore 
  399. useless for creating MIDI music). 
  400.  
  401. The Simple Approach is very easy and yet still can be faster than MMPM. 
  402.  
  403. Both approaches consume a whole lot less RAM overhead than installing and using 
  404. MMPM. 
  405.  
  406.  
  407. ΓòÉΓòÉΓòÉ 7.1.1. Simple Approach ΓòÉΓòÉΓòÉ
  408.  
  409. The Simple Approach consists of using the OS/2 API DosOpen() to open the driver 
  410. (ie, allow access to the card), DosWrite() to output MIDI data, DosRead() to 
  411. read incoming MIDI data, and DosClose() to close the driver.  In this case, the 
  412. driver serves simply as a means to read/write MIDI data to/from the card.  All 
  413. other considerations, if any, are left up to the program, for example, if the 
  414. program is sequencing playback of MIDI data, the program will have to manage 
  415. its own timing functions to determine when to write each MIDI message to the 
  416. driver (ie, when to output a MIDI message via DosWrite()). 
  417.  
  418. If you're working in REXX, then my File Rexx DLL can allow you to 
  419. open/read/write to the driver, or a combination of this DLL and Rexx Dialog DLL 
  420. can allow you to make such a REXX program with a Presentation Manager user 
  421. interface.  Alternately, you can use STREAM to open or close the driver, 
  422. CHAROUT to output MIDI data, and CHARIN to read incoming MIDI data. 
  423.  
  424. Since many MIDI utilities, such as patch editors and librarians, diagnostic 
  425. tools, and MIDI "software controllers" which simply output MIDI messages in 
  426. realtime as the user operates software "knobs and sliders", do not need to do 
  427. sequencing, the Simple Approach is very useful.  It's straightforward, and 
  428. offers as much of a realtime response as you're ever going to get under OS/2 
  429. without resorting to MUCH more complexity.  For patch editors and librarians, 
  430. you're mostly dealing with outputting and inputting System Exclusive messages. 
  431. You don't really care about optimum speed in inputting and outputting those 
  432. messages (ie, most MIDI devices require a delay or "handshake" inbetween each 
  433. message anyway).  You just want reliable transmission.  So too, with a software 
  434. controller that the user operates to generate MIDI messages in realtime, the 
  435. user's actions typically don't generate more MIDI messages than can be handled 
  436. in realtime via the Simple Approach. 
  437.  
  438. In order to cooperate with other simultaneously running programs using the same 
  439. driver, you'll probably want to follow a few guidelines covered in the next 
  440. sections.  This will ensure that your program can share the driver with other 
  441. simultaneously running programs and perhaps even do simultaneous MIDI output. 
  442.  
  443. If you've already used DosOpen(), DosWrite(), and DosRead() (and who hasn't?), 
  444. you know most of what you really need to know in order to use MPUDEV for MIDI 
  445. input and output.  Now, isn't that a LOT easier than MMPM? 
  446.  
  447.  
  448. ΓòÉΓòÉΓòÉ 7.1.1.1. Opening the driver ΓòÉΓòÉΓòÉ
  449.  
  450. Before reading and/or writing MIDI data, your program must first open the 
  451. driver for reading and/or writing.  You use the OS/2 API DosOpen() to do so. 
  452. In REXX, you can use STREAM, or the FileOpen() function of my File Rexx DLL (if 
  453. you want to also use other File Rexx functions such as FileWriteValue() or 
  454. FileReadValue() or use my Rexx Dialog DLL). 
  455.  
  456. For DosOpen()'s FileName arg, you specify the internal name of the driver unit 
  457. that you wish to open.  This name is not necessarily the same name as the 
  458. driver's actual filename.  If a driver supports several units (ie, cards), then 
  459. each will likely have its own internal name.  For example, MPUDEV.SYS supports 
  460. up to 4 units, named MPUDEV1$, MPUDEV2$, MPUDEV3$, and MPUDEV4$.  So, if you 
  461. wanted to open the first unit for reading and/or writing, then you would 
  462. specify the string MPUDEV1$. 
  463.  
  464. A program should always give the enduser the option of specifying the unit name 
  465. to be opened.  This is so that not only can he choose which one of MPUDEV's 4 
  466. units he wishes, but if he obtains an MPUDEV compatible driver for other 
  467. hardware, he can use the program with that driver too.  Preferably, the program 
  468. ought to allow the enduser to pass the driver name as an argument when invoking 
  469. the program (ie, OS/2 passes the arg to the program's main() routine in the 
  470. argv[] array).  In this way, the enduser can set that driver name in the 
  471. Desktop object's Parameters field to be automatically passed to the program 
  472. when launched.  It's also useful to allow the enduser to change the driver 
  473. while the program is running so that he can redirect the program's MIDI input 
  474. and output to different "ports". 
  475.  
  476. For DosOpen()'s OpenFlags arg, you'll specify OPEN_ACTION_OPEN_IF_EXISTS.  The 
  477. Attributes, FileSize, and Extended Attribute Buffer args are 0.  For File 
  478. Rexx's FileOpen(), the Flags arg is 'e', and the Attributes arg is 0.  Since 
  479. those are defaults, you can simply omit those two args. 
  480.  
  481. DosOpen()'s OpenMode arg requires you to specify whether you want to read 
  482. incoming MIDI data (ie, call DosRead()), output MIDI data (ie, call 
  483. DosWrite()), or do both.  To only read MIDI data (not output it), you specify 
  484. OPEN_ACCESS_READONLY.  To only output MIDI data (not read MIDI data), you 
  485. specify OPEN_ACCESS_WRITEONLY.  To be able to read and write MIDI data, you 
  486. specify OPEN_ACCESS_READWRITE.  For File Rexx's FileOpen(), you specify 'r' for 
  487. reading, 'w' for writing, and both ('rw') for reading and writing. 
  488.  
  489. DosOpen()'s OpenMode arg also requires you to specify whether you want to allow 
  490. other programs to open the same driver for reading and/or writing while your 
  491. program has the driver open.  If you don't want any other program to be able to 
  492. read from the driver (ie, call DosRead()) while you have it open, but writing 
  493. to the driver is OK, then specify OPEN_SHARE_DENYREAD.  If you don't want any 
  494. other program to be able to write to the driver (ie, call DosWrite()) while you 
  495. have it open, but reading from the driver is OK, then specify 
  496. OPEN_SHARE_DENYWRITE.  If you don't want any other program to be able to read 
  497. or write to the driver while you have it open, then specify 
  498. OPEN_SHARE_DENYREADWRITE.  If you don't mind letting other programs read and 
  499. write to the driver while you have it open, specify OPEN_SHARE_DENYNONE. 
  500. Depending upon which 1 of the 4 sharing options you choose, there are certain 
  501. restrictions that you should follow in order to avoid interfering with the 
  502. operation of other programs that share the driver.  These restrictions are 
  503. detailed in following sections. 
  504.  
  505. Examples 
  506.  
  507. Here's a C example of opening MPUDEV's first unit, for reading and writing, and 
  508. allowing both shared read and write access, and storing the resulting handle 
  509. (to be used with DosRead(), DosWrite(), and DosClose()) in the variable 
  510. myDriver: 
  511.  
  512. #define INCL_DOSFILEMGR 
  513. #include <os2.h> 
  514. #include <stdio.h> 
  515.  
  516.  HFILE  myDriver; 
  517.  ULONG  action; 
  518.  APIRET  rc; 
  519.  
  520.   rc = DosOpen("MPUDEV1$", &myDriver, &action, 0, 0, 
  521.          OPEN_ACTION_OPEN_IF_EXISTS, 
  522.          OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, 0}; 
  523.   if (rc) printf("MPUDEV1$ didn't open. Error #: 0x%08X", rc); 
  524.  
  525. Here's the exact same thing in REXX using File Rexx's FileOpen. 
  526.  
  527. CALL FileLoadFuncs 
  528.  
  529. myDriver = FileOpen("MPUDEV1$", 'rws') 
  530. IF myDriver = 0 THEN SAY "MPUDEV1$ didn't open." 
  531.  
  532.  
  533. ΓòÉΓòÉΓòÉ 7.1.1.2. Outputting MIDI data ΓòÉΓòÉΓòÉ
  534.  
  535. After opening the driver unit for writing, you can output data to that unit by 
  536. calling DosWrite(). 
  537.  
  538. For REXX, if you used STREAM to open the driver unit, then you can use CHAROUT 
  539. to output MIDI values.  But remember that REXX stores decimal values as ascii 
  540. strings, so you'll have to express each value in hexadecimal (ie, to express a 
  541. MIDI Clock status byte, it would be 'F8'X).  LINEOUT can't be used to output 
  542. MIDI values, because LINEOUT also appends line feed and carriage return 
  543. characters which the driver will misinterpret as 2 MIDI data bytes.  If you're 
  544. using File Rexx's FileOpen(), you'll use FileWrite() or FileWriteValue() to 
  545. output one or more MIDI bytes.  With FileWrite(), you need to express values as 
  546. hexadecimal (ie, in binary form) like with CHAROUT.  But if you have a string 
  547. that is already in binary form, then you can use FileWrite() to send it to the 
  548. driver.  Even better, FileWriteValue() allows you to send one or more values to 
  549. the driver without having to worry about expressing or converting each value 
  550. into its binary equivalent.  You just pass values to FileWriteValue() as you 
  551. would to most any other standard Rexx function, and File Rexx does the proper 
  552. data conversion for the driver. 
  553.  
  554. Note:  When I say 'binary' above, I don't mean REXX values such as '1011'. 
  555.        That isn't real binary.  That's an ascii representation of binary. 
  556.        Here's a real limitation of REXX's typeless data.  Sometimes, you really 
  557.        need data in a certain form, and REXX doesn't handle much beyond decimal 
  558.        values (expressed as ascii strings) very well.  File Rexx compensates 
  559.        for these limitations by offering ascii to binary conversion built into 
  560.        certain functions.
  561.  
  562. If you're going to allow shared write access (ie, OPEN_SHARE_DENYWRITE is not 
  563. specified), then your program should follow 2 restrictions in order to ensure 
  564. that it will work well with other programs, including being able to do 
  565. simultaneous MIDI output with other programs. 
  566.  
  567. The first restriction is that you should always output full MIDI messages with 
  568. every DosWrite().  For example, let's say that you wish to output a MIDI Note 
  569. On.  This MIDI message has 3 bytes; the Status, the Note Number, and the 
  570. Velocity.  Don't output the Status byte with a call to DosWrite(), and then 
  571. output the remaining bytes with one or two further calls to DosWrite(). 
  572. Instead, output all 3 bytes with one call to DosWrite().  This is a lot more 
  573. efficient anyway.  In fact, you could output more than one MIDI message with a 
  574. single call to DosWrite.  For example, maybe you have a buffer filled with 6, 
  575. complete MIDI messages, and you want them all to be output at the same time. 
  576. Output the whole buffer with one call to DosWrite().  In conclusion, what you 
  577. want to avoid doing is to split up any one MIDI message into multiple calls to 
  578. DosWrite().  This may only be a problem with a large System Exclusive message, 
  579. but most MIDI devices split up their "data dumps" into a series of smaller 
  580. System Exclusive messages (ie, packets) anyway, and besides, OS/2's virtual 
  581. memory system is suited for handling a potentially large, contiguous buffer of 
  582. data containing one System Exclusive message for output. 
  583.  
  584. The second restriction is that you should always resolve MIDI running status, 
  585. at least upon the first MIDI message contained within each buffer passed to 
  586. DosWrite().  In other words, the first MIDI message in that buffer must have a 
  587. Status byte.  If you have more than one MIDI message contained in a buffer that 
  588. you're passing in a single call to DosWrite(), it's OK for the subsequent MIDI 
  589. messages to implement running status; but the first message should have a 
  590. Status byte.  In fact, it's OK to not utilize MIDI running status at all (ie, 
  591. every MIDI message that you write has a Status byte).  MPUDEV implements 
  592. running status upon output anyway, so there is no loss of efficiency. 
  593.  
  594. It's possible to ignore the above restrictions (although ignoring the second 
  595. restriction could be particularly troublesome with shared access), but if you 
  596. do so, then you should either not allow shared write access, or else warn 
  597. endusers that they should not cause other running programs to do MIDI output at 
  598. the same time that your program is outputting MIDI data (ie, other programs 
  599. should be "sitting idle" while your program is outputting MIDI data).  Of 
  600. course, by not allowing shared write access at all, then OS/2 itself prevents 
  601. other programs from opening and writing to the driver.  There are 2 potential 
  602. problems with not allowing shared write access.  First, if another program 
  603. already has the driver open, even with shared write access allowed, then your 
  604. attempt to open the driver will fail (ie, because you won't allow shared write 
  605. access with that other program).  Secondly, while you have the driver open, no 
  606. other program can output MIDI data, even if that program is designed to 
  607. properly share access.  If you absolutely must prevent shared write access, 
  608. then you should open the driver immediately before you need to output MIDI 
  609. data, output that data, and then immediately close the driver.  Don't keep the 
  610. driver open longer than necessary. 
  611.  
  612. Ideally, you should attempt to follow the above 2 restrictions, and then you 
  613. can always allow shared write access.  If it's not feasible to follow these 
  614. restrictions, then my own preference is to allow shared write access, but tell 
  615. the enduser to avoid causing other running programs to do simultaneous MIDI 
  616. output to the same driver to which your program is outputting data.  It's true 
  617. that this leaves the possibility of an enduser causing 2 programs to mess up 
  618. each other's output.  But, it also gives an enduser the option of 
  619. simultaneously running two programs that use the same driver, and switching 
  620. between them at will.  Besides, garbled MIDI output usually isn't associated 
  621. with disasterous consequences such as the loss of irreplaceable data.  On the 
  622. other hand, preventing shared write access will prevent the enduser from even 
  623. starting a program that opens that driver upon startup.  It's better to let the 
  624. user make the choices of when those programs are given the opportunity to do 
  625. MIDI output. 
  626.  
  627. Note:  MPUDEV.SYS doesn't buffer MIDI data.  Therefore, a call to DosWrite() 
  628.        won't return until all MIDI bytes are output.  For hardware like a 
  629.        RAP-10 or SuperMPU, the hardware buffered output of the card make 
  630.        DosWrite() much quicker.
  631.  
  632. Examples 
  633.  
  634. Note:  The following examples assume that the driver has already been opened 
  635.        for writing, and the file handle was stored in the variable myDriver.
  636.  
  637. Here's a C example of outputting a MIDI Note On.  It's on the first MIDI 
  638. channel, it's a middle C note, and the velocity is 64.  Therefore, the 3 data 
  639. bytes are (in hexadecimal) 0x90, 0x3C, and 0x40. 
  640.  
  641. #define INCL_DOSFILEMGR 
  642. #include <os2.h> 
  643. #include <stdio.h> 
  644.  
  645. #define MIDI_DATA_LEN 3 
  646.  
  647.  ULONG  bytesWritten; 
  648.  APIRET  rc; 
  649.  UCHAR buffer[MIDI_DATA_LEN] = {0x90, 0x3C, 0x40}; 
  650.  
  651.   rc = DosWrite(myDriver, &buffer[0], MIDI_DATA_LEN, &bytesWritten); 
  652.   if (rc) printf("Error outputting MIDI data. Error #: 0x%08X", rc); 
  653.   else if (bytesWritten != MIDI_DATA_LEN) printf("Only %ld data bytes were 
  654. output.", bytesWritten); 
  655.  
  656. Here's a variation on the above. We now have 2 MIDI messages in our buffer, and 
  657. are going to output them with one call to DosWrite().  The second MIDI message 
  658. is also a Note On upon the same MIDI channel.  Therefore, we can implement 
  659. running status. 
  660.  
  661. #define MIDI_DATA_LEN 5 
  662.  
  663.  UCHAR buffer[MIDI_DATA_LEN] = {0x90, 0x3C, 0x40, 0x39, 0x40}; 
  664.  
  665.   rc = DosWrite(myDriver, &buffer[0], MIDI_DATA_LEN, &bytesWritten); 
  666.  
  667. Here are the above two examples in REXX using File Rexx's FileWriteValue(). 
  668. For the second example, I'm passing a variable to describe the MIDI values, 
  669. just for the sake of illustration.  Note that the values are expressed in 
  670. decimal (ie, the Status byte of 90 hexadecimal is 144 decimal). 
  671.  
  672. actual = FileWriteValue(myDriver, 144 60 64) 
  673. IF actual <> 3 THEN SAY "An error in outputting the MIDI data." 
  674.  
  675. myData = "144 60 64 57 64" 
  676. actual = FileWriteValue(myDriver, myData) 
  677. IF actual <> 5 THEN SAY "An error in outputting the MIDI data." 
  678.  
  679.  
  680. ΓòÉΓòÉΓòÉ 7.1.1.3. Inputting MIDI data ΓòÉΓòÉΓòÉ
  681.  
  682. After opening the driver unit for reading, you can read data from that unit by 
  683. calling DosRead(). 
  684.  
  685. For REXX, if you used STREAM to open the driver unit, then you can use CHARIN 
  686. to read MIDI values.  If you're using File Rexx, then the FileRead() function 
  687. does the same.  But remember that MIDI data is binary data, and yet REXX 
  688. expresses numeric values as ascii strings, so when using CHARIN or FileRead(), 
  689. you'll have to convert each MIDI value (ie, each character) in the returned 
  690. string to its equivalent ascii numeric value before you can perform 
  691. mathematical operations upon it.  For this reason, you're much better off using 
  692. File Rexx's FileReadValue() or FileReadBlock() to input one or more MIDI bytes. 
  693. These functions automatically convert each MIDI data byte to its equivalent 
  694. ascii string for REXX.  If you're using Rexx Dialog, then you won't be able to 
  695. use CHARIN, FileRead(), nor FileReadValue().  You'll need to use Rexx Dialog's 
  696. RXDEV command, which not only has a feature for converting each binary value to 
  697. ascii form, but can also read, convert, and return numerous MIDI data bytes all 
  698. in one string, or even separated into individual stem variables. 
  699.  
  700. Note:  When I say 'binary' above, I don't mean REXX values such as '1011'. 
  701.        That isn't real binary.  That's an ascii representation of binary. 
  702.        Here's a real limitation of REXX's typeless data.  Sometimes, you really 
  703.        need data in a certain form, and REXX doesn't handle much beyond decimal 
  704.        values (expressed as ascii strings) very well.  File Rexx compensates 
  705.        for these limitations by offering binary to ascii conversion built into 
  706.        certain functions.
  707.  
  708. MPUDEV always resolves MIDI running status on all incoming messages (ie, all 
  709. incoming MIDI messages are given a Status byte). 
  710.  
  711. Realtime messages are filtered from the input stream.  It just doesn't make any 
  712. sense to try to do any kind of sync via OS/2's DosRead() what with all of the 
  713. OS overhead associated with such.  If you need to do MIDI sequencing, that's 
  714. what the Speed Approach is for. 
  715.  
  716. Being that MIDI transmits data within "messages", MPUDEV's DosRead() processing 
  717. has been enhanced to allow a program to easily read one complete MIDI message 
  718. with a single call to DosRead().  The way to do this is to supply a buffer that 
  719. is as large as the largest MIDI message that you intend to be able to receive 
  720. intact, and then pass the size of this buffer to DosRead().  If MPUDEV can fit 
  721. the next incoming MIDI message into this buffer intact, then it will do so, and 
  722. return only as many bytes as are in that one MIDI message.  Note that you'll 
  723. have to check the returned BytesRead from DosRead() in order to determine how 
  724. many bytes have been returned in your buffer.  If the next MIDI message is too 
  725. large for the buffer, then as many bytes as possible as returned in the buffer. 
  726. The next DosRead() call will return as many more bytes of that same message as 
  727. will fit into the buffer again.  This will continue until all of the bytes of 
  728. that message are read.  Note that since MPUDEV resolves running status, you can 
  729. always tell when you're receiving back a new MIDI message (as opposed to a 
  730. continuation of a message that is larger than your supplied buffer) by checking 
  731. if the first byte in the buffer is a MIDI Status byte (ie, greater than 0x7F) 
  732. but not 0xF7 (ie, the ending mark of a System Exclusive message).  If so, then 
  733. you've got the start of the next MIDI message, and in fact, all of it if the 
  734. returned BytesRead is less than the size of your supplied buffer. 
  735.  
  736. On the other hand, if you only read one byte at a time with DosRead(), then 
  737. obviously it can only return one byte at a time anyway, so it works as one 
  738. would normally expect a DosRead() to any driver to work. 
  739.  
  740. Note:  The Delta Music Systems MPU401 driver, while mostly compatible with 
  741.        MPUDEV (in terms of the Simple Approach), doesn't resolve MIDI running 
  742.        status.  It also doesn't implement MPUDEV's enhanced feature of reading 
  743.        one complete MIDI message with each DosRead() call.  But if you're going 
  744.        to be using an MPU-401 in UART mode anyway, it makes sense to use MPUDEV 
  745.        instead of the DMS driver.  On the other hand, if your program only ever 
  746.        reads 1 byte with each DosRead(), then it will work with the DMS driver too.
  747.  
  748. If using RXDEV, you can specify a large buffer, and therefore take advantage of 
  749. MPUDEV's enhanced reading feature.  For File Rexx, you could use FileRead() to 
  750. read a MIDI message containing upto 256 bytes, but then you need to translate 
  751. each character of the returned string from its binary format into an ascii 
  752. numeric string in order to use it in REXX mathematical expressions. 
  753. FileReadBlock() can allow you to read already converted values, without 
  754. limitations upon the number of values returned. 
  755.  
  756. If you're going to allow shared read access (ie, OPEN_SHARE_DENYREAD is not 
  757. specified), then your program should follow 1 restriction in order to ensure 
  758. that it will work well with other programs. 
  759.  
  760. This restriction is that you should never call DosRead() while the program is 
  761. "sitting idle" (ie, when it's not necessary to be looking for incoming MIDI 
  762. data).  If your program always calls DosRead(), then it could potentially steal 
  763. part or all of a MIDI message that another program needs to see.  For example, 
  764. if two programs are simultaneously calling DosRead() to read one data byte, 
  765. it's possible that one program may grab the Note Number byte of a particular 
  766. MIDI Note On message, and while that's being returned to this one program, the 
  767. other program grabs the subsequent Velocity byte of that same Note On message. 
  768. Obviously, that's not good.  So, the solution is to only call DosRead() when 
  769. the program needs to read MIDI data or is made active by the enduser, and stop 
  770. calling DosRead() when the program no longer needs to read MIDI data or is made 
  771. inactive by the enduser.  Furthermore, the enduser should be warned not to 
  772. cause 2 programs to simultaneously read from the same driver.  Doing otherwise 
  773. could potentially result in garbled input for both programs, but the 
  774. alternative of no shared read access could result in eliminating the enduser's 
  775. options of simultaneously running those 2 programs, and switching between them 
  776. at will. 
  777.  
  778. If a program needs to prevent shared read access, that program should open the 
  779. driver immediately before getting input, read all the desired input via 
  780. DosRead(), and then immediately close the driver.  But there is a drawback 
  781. here.  If another program already has the driver open, even with read sharing 
  782. allowed, your attempt to open the driver will fail (ie, because you won't allow 
  783. shared read access with that other program). 
  784.  
  785. When a program calls DosRead() and there's no MIDI data waiting to be read, the 
  786. driver will "put the program to sleep" until such time as some data arrives. 
  787. This is perfectly OK for a Rexx script launched from the OS/2 command line, or 
  788. a non-PM program (although while the program is asleep, it has no way of 
  789. interacting with the enduser, which could be deemed bad).  But a Presentation 
  790. Manager program can never be put to sleep, or it will likely lockup the OS/2 
  791. single message queue (ie, a much-deservedly criticized design limitation of 
  792. OS/2).  So, a PM program (or a non-PM program that wishes to leave its main 
  793. thread free to manage user interaction) must create a second thread which does 
  794. all of the calls to DosRead() to input MIDI bytes.  Rexx Dialog's RXDEV already 
  795. implements this, so there's no problem with a script which uses Rexx Dialog for 
  796. its PM user interface. 
  797.  
  798. Sometimes, when a thread is put to sleep within a call to DosRead(), it may be 
  799. necessary for the main thread in that program to cause the other thread to wake 
  800. up and return from its call to DosRead().  There are 2 ways to do this.  First, 
  801. you can set some flag variable that your read thread can test to verify that 
  802. the main thread does not want any more calls to DosRead(), and then DosClose() 
  803. the driver.  DosClose() automatically causes a flush all of the unit's read 
  804. requests including your own thread's call to DosRead to abort (as well as 
  805. DosRead() calls made by other programs), and may return a BytesRead count of 0 
  806. (ie, no bytes returned).  If you don't want to actually close the driver, you 
  807. can substitute the DosClose() for a DosDevIOCtl() to issue the command to flush 
  808. all read requests. 
  809.  
  810. Because another program can always abort one of your program's calls to 
  811. DosRead(), you should always be prepared to ignore a DosRead() that returns 0 
  812. BytesRead. 
  813.  
  814. Examples 
  815.  
  816. Note:  The following examples assume that the driver has already been opened 
  817.        for reading, and the file handle was stored in the variable myDriver. 
  818.        If this were a PM program, this code would not be in the main thread.
  819.  
  820. Here's a C example of inputting 1 MIDI data byte. 
  821.  
  822. #define INCL_DOSFILEMGR 
  823. #include <os2.h> 
  824. #include <stdio.h> 
  825.  
  826.  ULONG  bytesRead; 
  827.  APIRET  rc; 
  828.  UCHAR buffer[1]; 
  829.  
  830.   rc = DosRead(myDriver, &buffer[0], 1, &bytesRead); 
  831.   if (rc) printf("Error inputting MIDI data. Error #: 0x%08X", rc); 
  832.   else if (!bytesRead) printf("No MIDI input. Possibly an input flush 
  833. occurred."); 
  834.  
  835. Here's a C example that inputs an entire MIDI message upto 256 bytes with each 
  836. call to DosRead().  It keeps looping around to pick up each message, and 
  837. identify whether it is the next MIDI message, or a continuation of one MIDI 
  838. message. 
  839.  
  840. #define MIDI_DATA_LEN 256 
  841.  
  842. UCHAR buffer[MIDI_DATA_LEN]; 
  843.  
  844. for (;;) 
  845.   rc = DosRead(myDriver, &buffer[0], MIDI_DATA_LEN, &bytesRead); 
  846.   if (rc) printf("Error inputting MIDI data. Error #: 0x%08X", rc); 
  847.   else if (!bytesRead) printf("No MIDI input. Possibly an input flush 
  848. occurred."); 
  849.   else 
  850.   { 
  851.     if (buffer[0] > 0x7F && buffer[0] != 0xF7) 
  852.       printf("Received the next MIDI message. Its size is %ld.", bytesRead); 
  853.     else printf("Received a continuation of a MIDI message for %ld more 
  854. bytes.", bytesRead); 
  855.   } 
  856.  
  857. Here's an example of reading one byte in REXX using File Rexx's 
  858. FileReadValue(). 
  859.  
  860. bytes = FileReadValue(myDriver) 
  861. IF bytes == "" THEN SAY "An error in inputting the MIDI data or a flush." 
  862.  
  863.  
  864. ΓòÉΓòÉΓòÉ 7.1.1.4. Closing the driver ΓòÉΓòÉΓòÉ
  865.  
  866. After you're done reading/writing to the driver, you need to close it by 
  867. calling DosClose().  In REXX, if you used STREAM to open the driver, use STREAM 
  868. to close it.  If you used FileOpen() to open the driver, use FileClose() to 
  869. close it (unless you also used Rexx Dialog's RXDEV to read from the device, in 
  870. which case, use RXDEV to close the device). 
  871.  
  872. This will cause all current calls to DosRead() to be aborted, and perhaps 
  873. return BytesRead = 0.  Furthermore, when the last program with a given unit 
  874. still open, closes that unit, any queued write requests are flushed (as a 
  875. failsafe mechanism). 
  876.  
  877.  
  878. ΓòÉΓòÉΓòÉ 7.1.1.5. Issuing commands ΓòÉΓòÉΓòÉ
  879.  
  880. Commands are sent to the driver via OS/2's API DosDevIOCtl() using a catagory 
  881. of 128 or 11 and where the function argument is the command number.  For REXX, 
  882. you can use File Rexx's FileDevIOCtl(). 
  883.  
  884. Here are the available command numbers that my MPUDEV driver understands for a 
  885. catagory of 128, and what each command does. 
  886.  
  887. 0              Resets the card (and driver) so that doing a DosRead() will 
  888.                return incoming MIDI bytes, and doing a DosWrite() will output 
  889.                MIDI bytes.  (Note that this doesn't flush input and output 
  890.                buffers.  There are separate commands below to do that).  This 
  891.                command exists because certain hardware sometimes needs to be 
  892.                placed into a certain mode in order to pass incoming MIDI data 
  893.                through to the computer.  For example, MPUDEV.SYS needs an 
  894.                MPU-401 to be in Uart mode (ie, as opposed to Intelligent mode). 
  895.                If an MPU-401 is not in intelligent mode, then it will not 
  896.                supply incoming MIDI bytes to MPUDEV.SYS.  A program should 
  897.                normally issue this command once upon startup (perhaps giving 
  898.                the enduser the option to not issue this command) to make sure 
  899.                that the driver and hardware are prepared to handle DosRead() 
  900.                and DosWrite() calls to process incoming and outgoing MIDI data 
  901.                respectively. 
  902.  
  903.                Unless you specify the /U option for a unit, MPUDEV 
  904.                automatically does a reset, placing the MPU-401 into UART mode, 
  905.                upon bootup. 
  906.  
  907. 1              Returns the version and revision numbers of the unit.  These are 
  908.                two bytes that are returned within the Data Packet that you pass 
  909.                to DosDevIOCtl().  If you specify the /U option for a unit, the 
  910.                version and revision numbers default to 0 and may not reflect 
  911.                the true version of the unit.  The MPU-401 returns its version 
  912.                number as a BCD (Binary Coded Digit). 
  913.  
  914.                The only real purpose of this command is if your program wishes 
  915.                to check certain capabilities of specific hardware.  For 
  916.                example, later versions of the MPU-401 (for example, the RAP-10 
  917.                and SuperMPU) have hardware buffered output, whereas earlier 
  918.                units don't. 
  919.  
  920. Here are the available command numbers that my MPUDEV driver understands for a 
  921. catagory of 11, and what each command does. 
  922.  
  923. 1              Flushes (ie, aborts) all queued read requests (ie, calls to 
  924.                DosRead()) for this unit.  All current DosRead()'s are aborted, 
  925.                returning a BytesRead of however many bytes were already read 
  926.                (ie, could be 0 if no bytes were read before the read was 
  927.                aborted).  It does not return an error condition for a given 
  928.                read request unless no bytes were returned.  This is so that if 
  929.                one program is doing an Enhanced Read, and another program 
  930.                causes the abort of this Enhanced Read, the first program will 
  931.                still successfully receive however many bytes were able to be 
  932.                read before the abort.  In other words, this flush tries to be 
  933.                as minimally disruptive as possible to all programs other than 
  934.                the one which caused the flush. 
  935.  
  936.                A flush of all read requests for a given unit is automatically 
  937.                done (as a failsafe operation) whenever any program calls 
  938.                DosClose() to close that unit. 
  939.  
  940. 2              Flushes (ie, aborts) all queued write requests (ie, calls to 
  941.                DosWrite()) for this unit.  All current DosWrite()'s are 
  942.                aborted, returning an error condition indicating that the write 
  943.                was aborted.  Normally, you should not do this as all calls to 
  944.                MPUDEV's DosWrite() always return within a reasonable amount of 
  945.                time unless the hardware is malfunctioning. 
  946.  
  947. Examples 
  948.  
  949. Note:  The following examples assume that the driver has already been opened, 
  950.        and the file handle was stored in the variable myDriver.
  951.  
  952. Here's how to flush all read requests in C. 
  953.  
  954. #define INCL_DOSFILEMGR 
  955. #define INCL_DOSDEVICES 
  956. #include <os2.h> 
  957. #include <stdio.h> 
  958.  
  959. APIRET rc; 
  960.  
  961. if( (rc = DosDevIOCtl(myDriver, 11, 1, 0, 0, 0, 0, 0, 0)) ) 
  962.   print("Error flushing read requests: %ld", rc); 
  963.  
  964. Here's how to get the version number in C. 
  965.  
  966. UCHAR version[2]; 
  967. ULONG len; 
  968.  
  969. len = 0; 
  970. if( (rc = DosDevIOCtl(myDriver, 128, 1, 0, 0, 0, &version, 2, &len)) ) 
  971.   print("Error getting version and revision: %ld", rc); 
  972. else 
  973.   printf("Version is %d. Revision is %d.", version[0], version[1]); 
  974.  
  975. Here's how to flush all read requests in REXX. 
  976.  
  977. error = FileDevIOCtl(myDriver, 11, 1) 
  978. IF error <> 0 THEN SAY "An error flushing read requests." 
  979.  
  980. Here's a REXX snippet on how to get the version and revision, and display it. 
  981.  
  982. /* Get MPU-401 version */ 
  983. DATA.0 = 2 
  984. DATA.1 = '1.1' 
  985. DATA.2 = '1.1' 
  986. err = FileDevIOCtl(handle, 128, 1, , 'DATA', 'd') 
  987. IF err <> 0 THEN SAY "ERROR getting MPU Version and Revision:" err 
  988. ELSE DO 
  989.   /* MPU-401 version is encoded in BCD. We need to convert the decimal return 
  990. to hex, and then get the first digit */ 
  991.   DATA.1 = LEFT(D2X(DATA.1), 1) 
  992.   SAY 'MPU Version =' DATA.1 
  993.   SAY 'MPU Revision =' DATA.2 
  994. END 
  995.  
  996.  
  997. ΓòÉΓòÉΓòÉ 7.1.2. Speed Approach ΓòÉΓòÉΓòÉ
  998.  
  999. This is not yet implemented for MPUDEV.  Look for an update to offer such, with 
  1000. details of what this entails. 
  1001.  
  1002.  
  1003. ΓòÉΓòÉΓòÉ 7.2. Driver ΓòÉΓòÉΓòÉ
  1004.  
  1005. If you want to use software that utilizes MPUDEV with other hardware, then you 
  1006. need to write an MPUDEV.SYS compatible driver for that hardware.  At a minimum, 
  1007. you should support the Simple Approach outlined in the previous sections.  This 
  1008. at least allows the driver to be used with a variety of existing OS/2 MIDI 
  1009. programs and REXX scripts. 
  1010.  
  1011. Make sure that you read the preceding sections on writing programs which use 
  1012. MPUDEV. 
  1013.  
  1014.  
  1015. ΓòÉΓòÉΓòÉ 7.2.1. Simple Approach ΓòÉΓòÉΓòÉ
  1016.  
  1017. For Simple Approach, your driver's Strategy routine will need to process Open 
  1018. (13), Close (14), Read (4), Write (8), and IOCtl (16) commands.  Of course, you 
  1019. can process additional commands if you wish (for example, you'll likely have an 
  1020. Init handler). 
  1021.  
  1022. The following sections detail what each command's handler must do. 
  1023.  
  1024.  
  1025. ΓòÉΓòÉΓòÉ 7.2.1.1. Open ΓòÉΓòÉΓòÉ
  1026.  
  1027. The Open handler can do any kind of initialization that it needs to do for the 
  1028. unit that has been requested to be opened.  The driver doesn't need to manage 
  1029. shared read and write access.  That's already done by the OS/2 kernal before 
  1030. any program can ever call your Open, Read, Write and IoCtl handlers (assuming 
  1031. that you set the Shared bit of your Device Header's Attributes).  Typically, if 
  1032. the unit is not already currently open, the Open handler may set up the IRQ and 
  1033. interrupt handler for the hardware (if it doesn't do so once only during its 
  1034. Init handler). 
  1035.  
  1036. Since the driver will offer a program commands to flush the unit's input and 
  1037. output queues, and reset the unit's card into the proper mode to input and 
  1038. output MIDI data, it's not necessary for the Open handler to do these things. 
  1039.  
  1040.  
  1041. ΓòÉΓòÉΓòÉ 7.2.1.2. Write ΓòÉΓòÉΓòÉ
  1042.  
  1043. The Write handler must output all of the bytes sent in each request packet (in 
  1044. the order that the request packets are received from OS/2).  It's up to the 
  1045. Write handler whether it wants to return immediately before all of the bytes 
  1046. are actually output, or whether it wants to return only after all bytes are 
  1047. output.  It's usually safer to do the latter so that if any errors are 
  1048. encountered during output, they can be reported back to the calling program 
  1049. (ie, when the Write handler sets the Error Code field of the request packet 
  1050. before returning it to OS/2).  In this case, since the hardware may accept data 
  1051. bytes much slower than the computer CPU can output them, it may be necessary to 
  1052. put a request to sleep (ie, via the Block DevHlp service) whenever the hardware 
  1053. says that it's not yet ready to accept the next byte to be output.  If the 
  1054. Write handler Blocks a request, then another request wanting to write some data 
  1055. could then call the Write handler.  (ie, Remember that OS/2 is a multi-tasking 
  1056. system.  Multi-tasking is disabled while a Write handler is executing 
  1057. unless/until the handler calls Block.  Whenever the Write handler Blocks, that 
  1058. means that OS/2 can now call it again with another request.  Therefore, your 
  1059. Write handler should regard itself as a potentially reentrant routine if it 
  1060. calls Block).  The Write handler should be prepared to queue this second 
  1061. request packet into a list, and then block this second request too. 
  1062. (Obviously, the Write handler will either call Block with a time-out value 
  1063. equal to how long it expects the hardware to be ready for another byte -- it 
  1064. takes 320 microseconds to send one byte over MIDI -- or will wait for some 
  1065. interrupt handler triggered by the card to call the Run DevHlp service).  After 
  1066. the first request is finally output, and its request packet is ready to be 
  1067. returned, the Write handler can then remove that queued, second request packet 
  1068. and use the DevHlp service Run to set that second request to start up as soon 
  1069. as the first request packet has been returned to OS/2.  In this way, the Write 
  1070. handler manages requests in the order that they're received, and yet allows for 
  1071. putting requests to sleep while waiting for the hardware to be ready to accept 
  1072. output bytes. 
  1073.  
  1074. Alternately, the Write handler could quickly copy all of the bytes of a given 
  1075. request to some internal buffer, to be output by some interrupt handler, and 
  1076. then immediately return that request packet before those bytes are actually 
  1077. output.  The handler still needs to make sure that bytes are output in the 
  1078. order that they're received from OS/2.  Of course, a situation may arise where 
  1079. programs will be sending bytes faster than they can be output, resulting in the 
  1080. internal buffer eventually being filled.  In that case, the handler will 
  1081. probably have to eventually block and queue requests as described above.  But, 
  1082. such a buffering scheme may allow programs to do other useful work that they 
  1083. couldn't do if they were otherwise Blocked as in the preceding approach.  For 
  1084. hardware that doesn't have a hardware buffered MIDI output, this software 
  1085. buffering could be a good approach.  Of course, when the Write handler returns 
  1086. the request packet, it should have indicated that all of the requested bytes 
  1087. have been written, even though they may not actually have been output yet. 
  1088.  
  1089. The Write handler should be prepared to implement running status for output 
  1090. since many programs may follow the restriction of always providing a Status 
  1091. byte with each DosWrite() regardless of whether that Status is needed. 
  1092.  
  1093.  
  1094. ΓòÉΓòÉΓòÉ 7.2.1.3. Read ΓòÉΓòÉΓòÉ
  1095.  
  1096. The Read handler must resolve running status (ie, all received MIDI messages 
  1097. must be given a Status byte).  If an interrupt handler is actually grabbing the 
  1098. incoming MIDI bytes from the hardware, and placing them into some "input 
  1099. buffer" from which the Read handler extracts data to fill requests, it's 
  1100. probably better to have the interrupt handler resolve running status so that 
  1101. any potential buffer overruns won't result in running status being messed up 
  1102. (even if some data is lost). 
  1103.  
  1104. Realtime messages should be filtered out.  It just doesn't make any sense to 
  1105. feed an application Realtime messages through the driver's Read handler given 
  1106. all of the OS overhead associated with such.  If an application needs to do 
  1107. MIDI sequencing, it will have to use the Speed Approach.  Filtering Realtime, 
  1108. especially MIDI Clock and Active Sense, helps stream-line the passing of data 
  1109. between the driver and program, and can help reduce possible buffer overruns 
  1110. (since Clock and Sense can fill up a buffer very quickly). 
  1111.  
  1112. The Read handler must do 1 of 2 things depending upon how many bytes a given 
  1113. read request desires, and whether or not the driver has bytes still unread of 
  1114. some MIDI message. 
  1115.  
  1116. If the request is for only 1 byte, the Read handler should return the next MIDI 
  1117. input byte waiting to be read.  This could be the Status of the next MIDI 
  1118. message, or another data byte of that message. 
  1119.  
  1120. If the request is for more than 1 byte, then the Read handler should assume 
  1121. that the calling program wants one complete MIDI message placed into its 
  1122. buffer, if possible.  In other words, the calling program doesn't necessarily 
  1123. expect that it will get back as many bytes as it has requested, but rather, 
  1124. only as many bytes as are in the complete MIDI message which the Read handler 
  1125. will be placing into the request packet's buffer.  The Read handler should then 
  1126. check the next MIDI byte waiting to be read.  If it's not a Status byte (ie, 
  1127. running status has already been resolved at this point) or it's an 0xF7 marking 
  1128. the end of a System Exclusive message, then the Read handler obviously still 
  1129. has unread bytes from a MIDI message waiting to be read (ie, the previous 
  1130. request grabbed only part of a MIDI message, leaving behind some data bytes 
  1131. which this next request has encountered).  If that's the case, then the Read 
  1132. handler should place as many of those remaining data bytes as possible into the 
  1133. request packet's buffer (as much as all of the remaining data bytes for that 
  1134. MIDI message), and return this "remainder of a MIDI message".  In this case, 
  1135. the Read handler will NOT be returning a complete MIDI message, but rather, 
  1136. part of a MIDI message.  (The program will notice this when it checks the first 
  1137. byte of the returned data and sees that it is not a Status byte or it's the 
  1138. 0xF7 ending mark of a System Exclusive message).  On the other hand, if the 
  1139. next byte waiting to be read is indeed a Status other than 0xF7, then this 
  1140. means that the Read handler is prepared to place a complete MIDI message into 
  1141. the request packet's buffer.  It should attempt to do so, copying all data 
  1142. bytes up to the next MIDI message's Status byte, or until the request packet's 
  1143. buffer is full.  If the request packet's buffer is too small to fit the entire 
  1144. message, the Read request should fill that buffer and return this portion of 
  1145. the MIDI message.  Obviously, what that means is that the next read request(s) 
  1146. will pick up the remaining data bytes of that MIDI message.  Hopefully, this 
  1147. will be the same program following up with more calls to DosRead().  In this 
  1148. way, the Read handler implements an Enhanced Read which returns complete MIDI 
  1149. messages if possible, but which also allows for the situation where, if the 
  1150. program passes a buffer that is too small to contain a particular MIDI message, 
  1151. the program can still get the remaining bytes of that message with subsequent 
  1152. calls to DosRead().  This scheme also allows the driver to be able to deal with 
  1153. a program that requests being fed 1 byte at a time as well as a program that 
  1154. wants to grab an entire MIDI message at a time, without needing the driver to 
  1155. be switched between 2 "modes". 
  1156.  
  1157. Needless to say, the enhanced read feature makes it a lot easier and even more 
  1158. importantly, faster, for a program to read MIDI messages, or at least large 
  1159. pieces of MIDI messages, in particular, typically large messages such as System 
  1160. Exclusive.  And that increased speed which the program grabs MIDI bytes from 
  1161. the driver lessens the risk of a buffer overrun which could happen if the MIDI 
  1162. card receives MIDI bytes faster than the driver and program can process those 
  1163. bytes. 
  1164.  
  1165. The Read handler must satisfy each request packet in the order that the request 
  1166. packets are received from OS/2.  Of course, a request packet must not be 
  1167. returned until the Read handler has satisfied its request for bytes (ie, either 
  1168. by placing a complete MIDI message in the request packet's buffer, or filling 
  1169. that buffer).  Because incoming MIDI bytes may not be received as fast as a 
  1170. program requests them, it may be necessary to put a request to sleep until 
  1171. those incoming bytes are available (ie, Block the request).  See the comments 
  1172. about Block and Run in Write. 
  1173.  
  1174.  
  1175. ΓòÉΓòÉΓòÉ 7.2.1.4. IoCtl ΓòÉΓòÉΓòÉ
  1176.  
  1177. The IoCtl handler must recognize those catagory 11 commands (ie, 1 and 2) 
  1178. described in Issuing commands.  It's important to allow a program to be able to 
  1179. flush any queued Read and Write requests. 
  1180.  
  1181. The IoCtl handler may also implement whichever of those catagory 128 commands 
  1182. that it deems applicable.  For example, if it wishes to return version and 
  1183. revision numbers, it should implement command 1.  It's probably a good idea to 
  1184. implement the Reset command (0) if the hardware has to be setup in any way 
  1185. before it will input and output MIDI data.  A driver should probably recognize 
  1186. the Reset command, even if the hardware does not need to be setup in order to 
  1187. input/output MIDI data, if only to ignore this command (ie, as opposed to 
  1188. returning some error).  After all, a program will likely issue a Reset command 
  1189. upon startup. 
  1190.  
  1191.  
  1192. ΓòÉΓòÉΓòÉ 7.2.1.5. Close ΓòÉΓòÉΓòÉ
  1193.  
  1194. The Close handler normally should undo everything done in the Open handler. 
  1195. For example, if an interrupt handler for the unit was installed in the Open 
  1196. handler, it should be uninstalled in the Close handler.  It's best for the Open 
  1197. handler to increment a count of the number of times that the driver is open, 
  1198. and the Close handler will decrement this count.  If the Close handler detects 
  1199. that this count is 0 (ie, nobody has the driver open anymore), then the Close 
  1200. handler should make sure that all queued Read and Write requests are flushed 
  1201. (ie, in the event that some program's main thread has called DosClose() while a 
  1202. second thread is "sleeping", (ie, its request packet is queued and Blocked, in 
  1203. a call to DosWrite() or DosRead()).  Even if the count is not 0, the driver 
  1204. should do a flush of all read requests. 
  1205.  
  1206.  
  1207. ΓòÉΓòÉΓòÉ 7.2.2. Speed Approach ΓòÉΓòÉΓòÉ
  1208.  
  1209. The docs for this are not yet available. I initially designed a "Speed 
  1210. Approach" that was closely tied to the architecture of the Roland RAP-10. This 
  1211. yielded high resolution MIDI playback/recording with synced digital audio using 
  1212. OS/2 native software. But of course, it was tied to the RAP-10's hardware. 
  1213. Other programmers have expressed an interest in a more general approach, so I'm 
  1214. currently redefining my protocol with such in mind. Programmers interested in 
  1215. joint efforts are welcome to contact me about such. I'm also aware of IBM's 
  1216. "Realtime MIDI" subsystem and would like to take a look at that, but if it 
  1217. doesn't meet my specific performance requirements, I'll be going ahead with my 
  1218. own protocol anyway. What really makes me shy away from "IBM solutions" is that 
  1219. I've seen firsthand that IBM is a very short-term, bottom-line company whose 
  1220. only interest is in its multi-million-dollar corporate accounts. IBM simply is 
  1221. not very successful, nor interested, in the consumer market, particularly a 
  1222. vertical market like the music market which is of no interest to IBM's big buck 
  1223. corporate accounts and therefore can't offer IBM those short-term, big buck 
  1224. returns. As such, I feel that any MIDI subsystem owned by IBM will be treated 
  1225. to same lack of development and promotion that we saw with MMPM, and will be 
  1226. handled in IBM's usual manner which means that it will be overpriced and poorly 
  1227. distributed in the consumer market, and fail to somehow offer something over 
  1228. Windows' MIDI competition. I feel that a cooperative, non-commercial 
  1229. development venture, such as how Linux or GNU operate, will offer at least as 
  1230. good a result as IBM will ever be willing to fund, and will be more affordable, 
  1231. better distributed, and less pervious to suffering development and promotion 
  1232. problems due a single, controlling entity being obsessed with short-term, big 
  1233. buck profits. That's no way to get OS/2 MIDI off of the ground. I'd prefer to 
  1234. see OS/2's MIDI subsystem owned and controlled by absolutely every OS/2 
  1235. programmer who feels like contributing to such, even those for whom profit 
  1236. motive is not the only factor that determines its fate like with IBM.