home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / midifil2.zip / midifile.INF (.txt) < prev    next >
OS/2 Help File  |  1995-07-21  |  38KB  |  908 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Preface ΓòÉΓòÉΓòÉ
  3.  
  4.                           MIDIFILE Programmer's Manual
  5.  
  6. MIDI files are a very popular format for storing "musical performances" and 
  7. other related data.  Most professional music programs that deal with such data 
  8. use this standardized format. 
  9.  
  10. Note:  Although the description of this file format is available from the MIDI 
  11.        Manufacturers Association, I wrote a more explicit and "down to earth" 
  12.        explanation in the form of a nicely organized, online OS/2 book.  Please 
  13.        read The MIDI File Format section of my book, The MIDI Book before 
  14.        reading this document and using the MIDIFILE.DLL.  This document does 
  15.        not detail the MIDI File Format.
  16.  
  17. Although there are some code examples of reading and writing MIDI files, I 
  18. perceived a need for an OS/2 Dynamic Link library that handles all of the 
  19. generic "bookkeeping" required to read and write MIDI files.  Since there are 
  20. certain things that all programs must do when reading or writing a MIDI file 
  21. (ie, open it, read in or write out headers with a ChunkSize, read and write 
  22. variable length quantities, etc), it makes sense to have the code to do these 
  23. things shared by all applications.  A Dynamic Link Library (ie, DLL) makes this 
  24. possible under OS/2.  DLL's can also simplify application development since a 
  25. programmer need not compile and link this generic MIDI file reading/writing 
  26. code for each program.  For this reason, I created such a DLL.  It was designed 
  27. to be flexible enough to accomodate a variety of needs and programming 
  28. algorithms.  This manual describes the DLL, and the accompanying C examples 
  29. demonstrate how to use it in applications. 
  30.  
  31. MIDIFILE.DLL is an OS/2 2.X Dynamic Link Library (DLL) that a programmer can 
  32. use to simplify writing OS/2 applications that read and write MIDI files. 
  33. MIDIFILE.DLL does most of the generic "bookkeeping" duties of parsing MIDI 
  34. files in a way that allows some flexibility as to how the application 
  35. implements the particulars of reading in data or writing out data. 
  36.  
  37. Many programs can be using that one copy of the DLL simultaneously, thus 
  38. reducing redundant code that eats up RAM needlessly.  Furthermore, the DLL 
  39. helps to eliminate discrepancies and incompatibilities in the way that two 
  40. programs read and write MIDI files if both programs use this DLL, since both 
  41. will be using the same reader/writer "manager". 
  42.  
  43. My intent is to provide OS/2 programmers with a versatile development aid that 
  44. will hopefully enjoy widespread use, and thus make OS/2 MIDI development more 
  45. standardized.  As long as they don't impede development, standards are very 
  46. important.  Applications that work well together make each other more useful. 
  47. Typically, an end-user will use two such programs with the same data files, 
  48. utilizing the strengths of one product to compensate for the weaknesses of the 
  49. other product.  He ends up with a more powerful system.  That helps everyone 
  50. from the end-users to the folks who sell software.  It's a simple concept, but 
  51. you'd be surprised how many businessmen latch onto the self-destructive notion 
  52. that they must fight and thwart their "competitors".  Often, this makes things 
  53. worse for even the end-users. 
  54.  
  55. To avoid name conflicts with DLL functions and data, avoid naming any of your 
  56. functions and data starting with the letters Midi. 
  57.  
  58. It's common practice (although not mandated by the format itself) to name MIDI 
  59. files with a .mid extension.  But, since the MIDI file format is an explicitly 
  60. defined format, the surest way to tell that you're dealing with a MIDI file is 
  61. to look at the data inside of it for the mandatory chunk headers. 
  62.  
  63. Some of the words in this manual are highlighted in colored text, such as 
  64. Channel Pressure. These are words that refer to types of MIDI messages (ie, 
  65. streams of bytes that are defined by the MIDI spec itself).  Of course, MIDI 
  66. messages (ie, data bytes) are usually found inside of a MIDI file.  Other words 
  67. are in italics such as Format.  These refer to data bytes inside of a MIDI file 
  68. that aren't defined by the MIDI spec itself, but are nevertheless found inside 
  69. of a MIDI file because they are important to a sequencer program.  Underlined 
  70. words, such as GM Sound Module, refer to hardware.  Of course, audio hardware 
  71. is used to "play" the music data stored in a MIDI file, but the MIDI file 
  72. itself isn't tied to any particular piece of hardware.  (ie, Most of the data 
  73. in a MIDI file could be played on an analog synth with oscillators just as 
  74. easily as a sample-playing computer sound card, although there is a provision 
  75. to store data for specific units).  Words that are in colored text such as Read 
  76. This are meant to be emphasized, or are headings in this manual. 
  77.  
  78.  
  79. ΓòÉΓòÉΓòÉ 2. Copyright ΓòÉΓòÉΓòÉ
  80.  
  81. This OS/2 Online Book and the related files, MIDIFILE.DLL, MIDIFILE.H, 
  82. MIDIFILE.LIB, and examples for using MIDIFILE.DLL are all copyright 1995 by 
  83. Jeff Glatt.  These files are freely redistributable, and may be used by and 
  84. distributed along with any software, be it commercial or otherwise, provided 
  85. that these files are not internally modified, nor specifically sold as a 
  86. complete product by themselves.  The only price that you have to pay is the one 
  87. that you're already paying by spending all of your time in front of a computer 
  88. instead of developing healthier outlets. 
  89.  
  90. NOT SO STANDARD DISCLAIMER: 
  91.  
  92. These programs are provided "as is" without warranty of any kind either 
  93. expressed or implied or tatooed in a place that only a few people have ever 
  94. seen, including but not limited to the implied warranties of merchantability, 
  95. fitness for a particular purpose, and the dubious assumption that the software 
  96. has been created by a sane individual who would never do anything that may hurt 
  97. you. The entire risk as to the results and performance of the programs is 
  98. assumed by you or someone who looks exactly like you.  Jeff Glatt does not 
  99. guarantee that the functions in these programs will meet your requirements, 
  100. especially if your requirements involve lots of latex and some docile, 
  101. domesticated animal.  Nor does Jeff Glatt warranty the programs to be 
  102. uninterruptable or error-free, although mercifully free of "General Protection 
  103. Faults".  If you use said programs, you can not say anything nasty about the 
  104. author, even if the programs inadvertently cause the erasure of your collection 
  105. of X-rated GIFs of a conservative, overweight and overrated TV "personality" 
  106. plooking himself vigorously with his royalty checks from some rancid paperback. 
  107. Jeff Glatt is not responsible for any damages as a result of anything that he 
  108. has done, or hasn't done, or was supposed to do but never got around to it, and 
  109. furthermore, he doesn't even care so leave him alone, ratface.  You may have 
  110. more or less protections in certain states of the union, depending upon how far 
  111. your local politician is willing to bend over for some bribe from a business 
  112. lobbyist.  Just remember that Jeff Glatt has no money, so don't bother suing 
  113. him as a result of any damages caused by this OS/2 program.  Tell your greasy 
  114. lawyer to go after IBM, and make sure that you pick 12 really stupid pinheads 
  115. for the jury.  If swallowed, induce vomiting immediately by contemplating the 
  116. asthetics of Microsoft Windows. 
  117.  
  118. OS/2 is a trademark of International Business Machines Corporation. 
  119.  
  120. Windows is a trademark of Microsoft Incorporated, and furthermore, Bill Gates 
  121. is to blame for it. 
  122.  
  123. If you have suggestions, comments, criticisms, and anything else other than 
  124. dollar bills, then send them to someone else because you got it for free, and 
  125. you know what you get for nothing?  But, if you do need to contact the author, 
  126. then either phone some of the more prominent psychiatrict clinics in central 
  127. New York state, or try this: 
  128.  
  129. Jeff Glatt 
  130. 6 Sycamore Drive East 
  131. New Hartford, NY 13413 
  132. (315) 735-5350 
  133.  
  134.  
  135. ΓòÉΓòÉΓòÉ 2.1. Archive ΓòÉΓòÉΓòÉ
  136.  
  137. The MIDIFILE archive consists of the DLL and several example applications with 
  138. source code.  The DLL itself is named MIDIFILE.DLL, and must be copied to one 
  139. of the directories specified by the LIBPATH statement in your config.sys file. 
  140. Usually, one of the specified directories is .;, which means that the DLL can 
  141. be placed in the same directory as the applications.  The applications will not 
  142. run if the DLL isn't placed somewhere in your LIBPATH.  These applications are 
  143. designed to be run from an OS/2 command line. 
  144.  
  145. For compiling applications, you'll need MIDIFILE.H and MIDIFILE.LIB copied to 
  146. where you keep your C include files and C link libraries respectively, although 
  147. these files aren't needed by the executables you produce.  Of course, 
  148. MIDIFILE.DLL should be included with your executable if you distribute that 
  149. executable, and the user should be instructed to copy MIDIFILE.DLL to his 
  150. LIBPATH. 
  151.  
  152. The applications are as follows.  In the Syntax, note that arguments within [ ] 
  153. are optional. 
  154.  
  155. MfRead      Reads a MIDI file and displays information about its contents.  It 
  156.             lists the chunks found in a MIDI file, and the contents of MThd and 
  157.             MTrk chunks.  For MTrk chunks, it can list information about every 
  158.             event in the track, or just a count of how many events of each type 
  159.             are found (if the /i switch is specified).  You can use this 
  160.             utility to discover what Format a MIDI file is, how many tracks are 
  161.             contained therein, what kind of events are within the MTrks, etc. 
  162.             You can also use it to check MIDI files that your own programs 
  163.             create to ensure that these files comply with the MIDI file spec. 
  164.             This is an example of how to write a program that uses MIDIFILE.DLL 
  165.             to read in a MIDI file. 
  166.  
  167.             Syntax: MfRead.exe filename [/i] 
  168.  
  169. MfWrite     Writes a dummy MIDI file in one of the 3 MIDI Formats.  This 
  170.             example has no practical use.  It exists just to show you how to 
  171.             write a program that uses MIDIFILE.DLL to save a MIDI file. 
  172.  
  173.             Syntax: MfWrite.exe filename [0, 1, or 2 (for the Format)] 
  174.  
  175. MfToVlq     Takes the values typed in as numeric args, and shows each one as a 
  176.             variable length quantity (ie, a series of bytes).  For example, if 
  177.             you type: 
  178.  
  179.             MfToVlq 13 0x4000 
  180.  
  181.             then MfToVlq will show you 13 expressed as a variable length 
  182.             quantity, and then hex 4000 as a variable length quantity.  Note 
  183.             that you indicate a hex value by prefacing it with a 0x. 
  184.  
  185.             Syntax: MfToVlq.exe Value1 [Value2...] 
  186.  
  187. MfVlq       Takes a variable length quantity (ie, a series of bytes) typed in 
  188.             as numeric args, and combines them into one value.  This is the 
  189.             opposite of MfToVlq.  To express a byte as hex, preface it with a 
  190.             0x. 
  191.  
  192.             Syntax: MfVlq.exe variable length quantity bytes... 
  193.  
  194. Each application has a MAKE file to compile it into an executable.  These 
  195. executables were made with IBM's C/Set2 compiler, Link386, and NMAKE.EXE (the 
  196. latter 2 come with the ToolKit).  The .def and .dep files are used by these 
  197. development tools.  The MIDIFILE.LIB file is a link library that gets linked 
  198. with your C application.  You should tell the linker that it needs to link with 
  199. this library.  Note that the make files specify it as a link library to 
  200. Link386. 
  201.  
  202. Also, your C source code must include a reference to the C include file found 
  203. in this archive, like so: 
  204.  
  205. #include <midifile.h> 
  206.  
  207. Put the above line after the reference to the standard os2.h include file. 
  208.  
  209. Note:  If you need to make a MIDI file for test purposes, use MfWrite.
  210.  
  211.  
  212. ΓòÉΓòÉΓòÉ 3. MidiReadFile() and MidiWriteFile() ΓòÉΓòÉΓòÉ
  213.  
  214. The DLL has 2 functions to read and write a complete MIDI file, MidiReadFile() 
  215. and MidiWriteFile() respectively.  These 2 functions initiate and carry out 
  216. their respective operations with one call.  When you return from one of these 
  217. functions, you will have either read or written a complete MIDI file.  For 
  218. example, calling MidiWriteFile() creates and writes out all of the data to a 
  219. MIDI file. 
  220.  
  221. Before you call either of these functions, you must allocate and initialize 
  222. certain fields of 2 special structures of my own design.  These structures are 
  223. described in midifile.h. 
  224.  
  225.  
  226. ΓòÉΓòÉΓòÉ 4. Callbacks ΓòÉΓòÉΓòÉ
  227.  
  228. Let's say that you have a secretary.  You ask her (or him) to prepare a letter 
  229. to a certain customer.  From this request, the secretary automatically knows to 
  230. get a sheet of paper, type the return address, and type the customer's address, 
  231. because all letters have these standard features.  But, beyond this, the 
  232. secretary needs to get more feedback from you as to what to put into the body 
  233. of the letter. 
  234.  
  235. So too, the DLL automatically does certain things that all applications will 
  236. need to do with MIDI files.  For example, all MIDI files must be opened before 
  237. data can be read or written to them.  So, it makes sense to have the DLL do 
  238. that.  But, the DLL doesn't know what to do with data that it reads in, nor 
  239. does it know what data to write out to a MIDI file.  So, the DLL periodically 
  240. needs to get feedback from your program while it's reading or writing a file. 
  241. It does this using 2 special data structures, and callbacks. 
  242.  
  243. What's a callback?  A callback is simply a function in your C program that the 
  244. DLL itself calls.  You will most likely have several callbacks, and each will 
  245. be called by the DLL at a certain point while the DLL is reading or writing a 
  246. file. 
  247.  
  248. For example, your program calls MidiReadFile() to read in a MIDI file.  The DLL 
  249. automatically opens the MIDI file, locates the MThd chunk within it, and loads 
  250. the information contained in it.  All applications that read a MIDI file would 
  251. want to do that.  But now, the DLL doesn't know what to do with that MThd 
  252. information.  After all, it's likely that different programs will have 
  253. different "needs" for that data, and some programs might want to store that 
  254. data in global variables within the program.  So, the DLL calls a function in 
  255. your program, passing the loaded MThd data.  Since the function is part of your 
  256. program, you can assess all of your global variables, and call other functions 
  257. within your program (or functions in a DLL or OS/2 system functions, etc) just 
  258. like any other function in your program.  Perhaps this callback might inspect 
  259. the passed MThd data, and set certain global variables as a result.  When the 
  260. callback returns, control is returned to the DLL, and the DLL carries on with 
  261. the next generic step of reading in a MIDI file.  For example, at this point, 
  262. it makes sense that any application would want to read in the header for the 
  263. next chunk to see what kind of chunk it is.  So, the DLL does that.  Let's say 
  264. that the DLL finds an MTrk header.  Once again, the DLL doesn't know what the 
  265. application wants to do with this MTrk, so the DLL calls another function in 
  266. your program that expects to be called when an MTrk chunk is encountered. 
  267.  
  268. This process of the DLL calling your callback functions continues until the DLL 
  269. has read in all of the data within the MIDI file, and then MidiReadFile() 
  270. finally returns to where you made that call in your program. 
  271.  
  272. As a further illustration, look at the following code. 
  273.  
  274.  
  275. ULONG value; 
  276.  
  277.  
  278. VOID parent() 
  279.   child(); 
  280.   return; 
  281.  
  282.  
  283. VOID child() 
  284.   baby1(); 
  285.   baby2(); 
  286.   return; 
  287.  
  288.  
  289. VOID baby1() 
  290.   value=1; 
  291.   return; 
  292.  
  293.  
  294. VOID baby2() 
  295.   return; 
  296.  
  297. parent() calls child() which then calls baby1() and baby2().  This is fairly 
  298. obvious.  The call to child() doesn't return until child() itself returns, and 
  299. that's after baby1() and baby2() get executed.  Now let's assume that you put 
  300. child() in another source code module.  Does that mean that parent() can't call 
  301. child(), or that child() can't call baby1() and baby2()?  No.  Is the logic 
  302. changed at all?  No.  child() is simply in a different module.  Now think of 
  303. child() as being MidiReadFile().  Just because it's in the DLL doesn't mean 
  304. that parent() can't call it.  Neither does that mean that MidiReadFile() can't 
  305. call baby1() and baby2(), which I refer to as callbacks. 
  306.  
  307. All of your callbacks must be declared to be of type EXPENTRY and returning a 
  308. LONG (which is an error code).  For example, our baby2() callback would be: 
  309.  
  310.  
  311. LONG EXPENTRY baby2(MIDIFILE * mf) 
  312.   return; 
  313.  
  314. Usually, the DLL passes a MIDIFILE structure to your callback (ie, the same one 
  315. that you pass to MidiReadFile or MidiWriteFile) which will contain pertinent 
  316. data. 
  317.  
  318. The concept of callbacks is really quite simple, but since you can't "see" the 
  319. code to MidiReadFile() calling your callbacks, it may be harder to visualize 
  320. the logic.  Just remember that one of your functions calls MidiReadFile() which 
  321. then calls a series of your callbacks until the MIDI file is read in, and then 
  322. MidiReadFile() finally returns to your function that called it. 
  323.  
  324.  
  325. ΓòÉΓòÉΓòÉ 5. CALLBACK and MIDIFILE structures ΓòÉΓòÉΓòÉ
  326.  
  327. So, how does the DLL know which of your program's callbacks to call?  Well, 
  328. that's what one of these special structures is for.  You place pointers to your 
  329. functions into the CALLBACK structure.  Each of the fields in the CALLBACK is 
  330. for a pointer to a function in your program.  There's a field where you place a 
  331. pointer to a function that the DLL should call after it has located and loaded 
  332. an MThd chunk.  There's a field where you place a pointer to a function that 
  333. the DLL should call when it encounters a Time Signature Meta-Event within an 
  334. MTrk.  Etc.  For some of the CALLBACK fields, you could set them to 0 instead 
  335. of supplying a pointer to a callback.  In this case, the DLL will instead 
  336. perform some default action when it gets to the point where it should call that 
  337. "missing" callback. 
  338.  
  339. The other structure is a MIDIFILE.  Mostly, the MIDIFILE is used to maintain 
  340. variables that are used during the reading and writing of MIDI files.  Also, 
  341. while reading a MIDI file, it is often used to contain loaded data from the 
  342. MIDI file, which is then passed to one of your callbacks.  While writing a MIDI 
  343. file, your callbacks often place data into the MIDIFILE structure and then upon 
  344. return, the DLL writes that data out to the MIDI file. 
  345.  
  346. The midifile.h has comments that describe the fields of the MIDIFILE and 
  347. CALLBACK structures. 
  348.  
  349.  
  350. ΓòÉΓòÉΓòÉ 6. Initialization ΓòÉΓòÉΓòÉ
  351.  
  352. Before calling MidiReadFile() or MidiWriteFile(), you must setup certain fields 
  353. of the CALLBACK and MIDIFILE structures. 
  354.  
  355. All of the CALLBACK fields must be set either to a pointer to some callback, or 
  356. 0 (if you want the DLL's default action for that callback).  I'll discuss when 
  357. each callback gets called, and what it should do, later in the discussion of 
  358. reading and writing MIDI files.  The CALLBACK fields only need to be 
  359. initialized once when you first create the CALLBACK.  If your CALLBACK is a 
  360. declared global in your program, you could initialize the fields within the 
  361. declaration.  Unless you wish to later change a CALLBACK field to point to a 
  362. different function, you don't need to ever reset the CALLBACK fields.  It 
  363. should be noted that you can change a CALLBACK field at any time, even within a 
  364. callback function. 
  365.  
  366. Note:  Unless you want your program to do the actual reading and writing of 
  367.        bytes to disk, perhaps in order to use buffered file I/O routines such 
  368.        as fread(), fwrite(), etc, you'll set the CALLBACK's OpenMidi, 
  369.        ReadWriteMidi, SeekMidi, and CloseMidi fields to 0.
  370.  
  371. Not all of the MIDIFILE fields need to be initialized.  Many fields are setup 
  372. and maintained by the DLL.  The fields that you must setup are Handle, Flags, 
  373. and Callbacks. 
  374.  
  375. Callbacks is simply a pointer to your CALLBACK structure.  (ie, You pass the 
  376. MIDIFILE structure to MidiReadFile() or MidiWriteFile() and the DLL gets your 
  377. CALLBACK from the MIDIFILE). 
  378.  
  379. Flags is a USHORT.  There are 16 bits in this USHORT, each one being a flag 
  380. that is either "on" (set) or "off" (clear).  These flags give you choices as to 
  381. how the DLL processes data, or sometimes indicate the "state" of something. 
  382. See midifile.h for details.  Typically, you will simply clear these bits for 
  383. default operation. 
  384.  
  385. Finally, Handle is a pointer to the null-terminated MIDI filename that you want 
  386. the DLL to open for reading or writing.  For example, you might initialize it 
  387. as so: 
  388.  
  389.  struct MIDIFILE mfs; 
  390.  UCHAR name[] = "C\:MyFile.mid"; 
  391.  
  392.  mfs.Handle = (ULONG)&name[0]; 
  393.  
  394. The ULONG cast is just to prevent a compiler warning message.  (After the DLL 
  395. opens the file, it then uses the Handle field to store the file handle). 
  396.  
  397.  
  398. ΓòÉΓòÉΓòÉ 7. Fixed/Variable Length Events ΓòÉΓòÉΓòÉ
  399.  
  400. Within an MTrk chunk, I differentiate between fixed and variable length events. 
  401.  
  402. What's a fixed length event ?  That's simply an event that always has the same 
  403. number of bytes in it.  For example, a MIDI Note-On event always has 3 bytes; 
  404. the status, the note number (ie, pitch), and the velocity.  (The DLL resolves 
  405. running status, always providing you with a Status byte for each event).  A 
  406. MIDI Program Change event always has 2 bytes; the status, and the program 
  407. number.  A Tempo Meta-Event always has 6 bytes; 0xFF, 0x51, 0x03, and the 3 
  408. bytes that comprise the micros per quarter note.  The following are considered 
  409. to be fixed length events: 
  410.  
  411.  1. MIDI events with a Status less than 0xF0.  These include MIDI Note-Off, 
  412.     Note-On, Program Change, Aftertouch, Polyphonic Pressure, Controllers, and 
  413.     Pitch Wheel events on any of the 16 channels.  All of these have 3 bytes, 
  414.     except Program and Pressure which have 2. 
  415.  
  416.  2. MIDI REALTIME and SYSTEM COMMON events except for SYSTEM EXCLUSIVE.  These 
  417.     include events with a Status of 0xF1 to 0xFE, except 0xF7.  (0xFF is 
  418.     interpreted as a Meta-Event status). 
  419.  
  420.  3. The Meta-Events of Sequence Number, End Of Track, Tempo, SMPTE Offset, Time 
  421.     Signature, and Key Signature.  Each one has its own defined length. 
  422.  
  423. What's a variable length event ?  That's simply an event that may have any 
  424. number of bytes in it.  For example, a MIDI SYSTEM EXCLUSIVE event for a Roland 
  425. RAP-10 audio card may be a different length than a SYSEX event for an EMU 
  426. Proteus.  Furthermore, the following Meta-Events all can be various lengths 
  427. depending upon the text stored in each: Text, Copyright, Track Name, Instrument 
  428. Name, Lyric, Marker, and Cue Point. 
  429.  
  430.  
  431. ΓòÉΓòÉΓòÉ 8. Reading a file ΓòÉΓòÉΓòÉ
  432.  
  433. Let's discuss the procedure for reading a file.  First, you'll initialize the 
  434. CALLBACK and MIDIFILE structures as described in Initialization, and then call 
  435. MidiReadFile(), passing it your MIDIFILE structure.  The DLL will open the 
  436. file, locate the MThd chunk, and read that info into the MIDIFILE's Format, 
  437. NumTracks, and Division fields.  These fields directly correspond to the values 
  438. in an MThd, except that these fields are arranged in Intel reverse byte order. 
  439. (ie, Your C program can read the Format as a USHORT, and not have to flip the 
  440. two bytes).  The MIDIWRITE bit of Flags will be cleared by the DLL. 
  441.  
  442. Then, the DLL will call your StartMThd callback if supplied, passing a pointer 
  443. to your MIDIFILE structure.  Your callback might want to inspect the Format, 
  444. and perhaps adjust certain global variables (or CALLBACK pointers) depending 
  445. upon whether Format 0, 1, or 2.  Or, you might need to allocate as many 
  446. structures as are needed to hold NumTrack number of MTrks.  The Format, 
  447. NumTracks, and Division of the MIDIFILE remain in the structure all of the way 
  448. through MidiReadFile(), so you don't necessarily need to copy these values 
  449. anywhere else.  The MIDIFILE's FileSize always reflects how many more bytes 
  450. still need to be read from the file.  This is maintained by the DLL and should 
  451. not be altered.  Likewise, the ChunkSize field reflects how many more bytes 
  452. still need to be read from the current chunk.  (This will be 0 at this point 
  453. since the entire MThd was read in).  The TrackNum field always tells you what 
  454. MTrk number has last been read in.  (This will be 0xFF right now since no MTrk 
  455. chunks have yet been read).  This number is also maintained by the DLL (ie, it 
  456. is incremented each time that the DLL encounters an MTrk).  The DLL maintains 
  457. the Handle field, and this should not be altered.  Your callback should return 
  458. a 0 for success (to let the DLL continue reading the file).  Otherwise, your 
  459. DLL should return a non-zero number to abort the read.  This number will then 
  460. be returned from MidiReadFile(). 
  461.  
  462. Note:  All callbacks return 0 for success, or non-zero for an error.  All are 
  463.        passed one argument; your MIDIFILE (which is sometimes redeclared to be 
  464.        another structure similiar to the MIDIFILE, but it's really always a MIDIFILE).
  465.  
  466. After your StartMThd callback successfully returns, the DLL reads in the next 
  467. chunk.  If this is not an MTrk chunk, your UnknownChunk callback is called. 
  468. The ID of the chunk is in the MIDIFILE's ID.  Note that the 4 bytes are stored 
  469. as a ULONG for easy comparison to a CONSTANT, but the byte order is Big Endian 
  470. (ie, Motorola).  You could use the DLL function MidiFlipLong() to switch it to 
  471. Intel order.  ChunkSize indicates the number of data bytes contained in the 
  472. chunk.  The DLL has not loaded these bytes.  If you want to load them, your 
  473. callback must do that now.  You must use the DLL function MidiReadBytes() to 
  474. load data from the MIDI file.  If you don't supply an UnknownChunk, the DLL 
  475. skips unknown chunks. 
  476.  
  477. Note:  Never read bytes using some OS or C library function.  Always use 
  478.        MidiReadBytes(), which is used in much the same manner as you might use DosRead().
  479.  
  480. Alternately, if you simply want the DLL to skip over this chunk's data, your 
  481. callback need do nothing more than return.  The DLL will skip ahead to the next 
  482. chunk.  You could even read some, but not all, bytes from the chunk, and then 
  483. return to let the DLL skip the remaining bytes. 
  484.  
  485. On the other hand, if the chunk is an MTrk, then the DLL begins the process of 
  486. calling a series of your callbacks to read in the events within the chunk. 
  487.  
  488. First, the DLL calls your StartMTrk callback, if supplied.  The DLL will have 
  489. incremented the MIDIFILE's TrackNum.  (For the first MTrk, this will be 0). 
  490. You might use the StartMTrk callback to initialize global variables for reading 
  491. in a track.  If you want the library to read in the entire MTrk chunk into a 
  492. buffer, and then return this to you, your callback should allocate a buffer to 
  493. contain ChunkSize bytes, and store the pointer in the MIDIFILE's Time field. 
  494. In this case, upon return, the DLL will load the chunk, and then call your 
  495. StandardEvt callback (which you must supply in this case), which can then 
  496. process the buffer as you see fit.  If you want the DLL to load one event at a 
  497. time, your StartMTrk should return with the Time field left at 0 (ie, the DLL 
  498. initially sets it to 0).  At this point, the DLL will load the first event, and 
  499. then call one of your callbacks, depending upon what type of event it is (and 
  500. if you have supplied a callback for that type of event). 
  501.  
  502. The following discussion of the individual callbacks is for when the DLL loads 
  503. one event at a time.  The basic idea is that the DLL loads one event (or for a 
  504. variable length event, just its Status and Length), figures out what type of 
  505. event it is, and calls your respective callback which is expected to store the 
  506. data somewhere.  When your callback returns, the DLL loads the next event, etc, 
  507. until all events in the MTrk have been loaded.  If you don't supply a callback 
  508. for that event type, the DLL skips that one event. 
  509.  
  510. For all event types, the DLL always stores the event's time in the MIDIFILE's 
  511. Time field.  In other words, the DLL converts the event's variable length 
  512. quantity timestamp into 1 ULONG and stores it in the MIDIFILE's Time.  This 
  513. time is referenced from 0, rather than the previous event like within the MIDI 
  514. file, unless you set the MIDIDELTA Flag. 
  515.  
  516. For MIDI events with status 0x80 to 0xEF, the DLL will load the event entirely 
  517. into the MIDIFILE.  The MIDI Status byte will be placed into the MIDIFILE's 
  518. Status field, and the 1 or 2 MIDI data bytes will be placed in MIDIFILE's Data1 
  519. and Data2.  (If the event is a PROGRAM CHANGE or POLYPHONIC PRESSURE, which 
  520. have only 1 data byte, the second byte is set to 0xFF).  The DLL always 
  521. provides a Status, resolving MIDI running status on your behalf.  Then, the DLL 
  522. calls your StandardEvt callback, if supplied.  Typically, your callback will 
  523. extract those fields, along with the Time, and store the bytes in some "track 
  524. memory". 
  525.  
  526. If the event is a SYSEX, SYSEX CONTINUATION, or ESCAPE, then the DLL calls your 
  527. SysexEvt callback, if supplied.  Since these events can have an arbitrary 
  528. number of data bytes, the DLL only loads the Status (ie, 0xF0 or 0xF7) into the 
  529. MIDIFILE's Status, and sets MIDIFILE's EventSize to the number of data bytes 
  530. that need to be read in.  Your callback is expected to read in those data bytes 
  531. using MidiReadBytes().  Typically, you'll load/store the data in some allocated 
  532. memory.  Alternately, you can choose to have the DLL skip over those bytes by 
  533. simply returning.  The DLL sets the MIDISYSEX Flag when it first encounters an 
  534. F0 event, and clears this flag when it later encounters an event that would 
  535. definitely indicate that no more SYSEX CONTINUATION "packets" follow. 
  536.  
  537. For a Sequence Number Meta-Event, the DLL redefines the MIDIFILE as a METASEQ 
  538. structure.  If you compare the 2 structures, you'll note that they're both the 
  539. same size, and have most of the same fields.  They really are the same.  It's 
  540. just that the MIDIFILE's Data fields have been redefined specifically for a 
  541. Sequence Number Meta-Event.  (Many of the other type of Meta-Events also use 
  542. some sort of redefinition of a MIDIFILE).  The DLL loads the sequence number 
  543. into the METASEQ's SeqNum field.  The byte order is Intel form, so the field 
  544. can be read as a USHORT.  Then, the DLL calls your MetaSeqNum callback if 
  545. supplied, passing the METASEQ. 
  546.  
  547. For a Tempo Meta-Event, the DLL redefines the MIDIFILE as a METATEMPO 
  548. structure.  The DLL loads the micros per second into Tempo using Intel byte 
  549. order so that it can be read as a ULONG.  The DLL also calculates the tempo in 
  550. beats per minute and stores that in the METATEMPO's TempoBPM.  Then, the DLL 
  551. calls your MetaTempo callback if supplied. 
  552.  
  553. For a SMPTE Offset Meta-Event, the DLL redefines the MIDIFILE as a METASMPTE 
  554. structure.  The DLL loads the hours, minutes, seconds, frames, and subframes 
  555. fields into the respective METASMPTE fields.  Then, the DLL calls your 
  556. MetaSmpte callback if supplied. 
  557.  
  558. For a Time Signature Meta-Event, the DLL redefines the MIDIFILE as a METATIME 
  559. structure.  The DLL loads the nom, denom, etc, into the respective METATIME 
  560. fields.  The DLL will express the denom as the actual denom of the time 
  561. signature (instead of a power of 2) if you have set your MIDIFILE's MIDIDENOM 
  562. Flag.  Then, the DLL calls your MetaTimeSig callback if supplied. 
  563.  
  564. For a Key Signature Meta-Event, the DLL redefines the MIDIFILE as a METAKEY 
  565. structure.  The DLL loads the key and minor fields into the METAKEY.  Then, the 
  566. DLL calls your MetaKeySig callback if supplied. 
  567.  
  568. For an End of Track Meta-Event, the DLL redefines the MIDIFILE as a METAEND 
  569. structure.  Then, the DLL calls your MetaEOT callback if supplied.  Note that 
  570. this will always be the last event in an MTrk. 
  571.  
  572. For Meta-Events of types 0x01 to 0x0F (ie, Text, Copyright, Instrument, Track 
  573. Name, Marker, Lyric, Cue Point), 0x7F (Proprietary), and any other currently 
  574. undefined Meta-Events, these can be of arbitrary length.  So, the DLL loads the 
  575. meta Type byte into the MIDIFILE's Status field, and sets the MIDIFILE's 
  576. EventSize to the number of data bytes that need to be read in.  Then, the DLL 
  577. calls your MetaText callback if supplied.  Your callback is expected to read in 
  578. those data bytes using MidiReadBytes().  Typically, you'll load/store the data 
  579. in some allocated memory.  Alternately, you can choose to have the DLL skip 
  580. over those bytes by simply returning. 
  581.  
  582. If you didn't supply a callback for a particular event type (ie, you set the 
  583. respective CALLBACK field to 0), then the DLL will skip loading that event. 
  584. For example, if you set the CALLBACK's MetaTempo field to 0, then the DLL will 
  585. skip over all Tempo Meta-Events. 
  586.  
  587. The DLL continues reading in the next event (unless your callback returns 
  588. non-zero to abort), and calls your respective callback for that event, until 
  589. all events, including the End Of Track, are read in. 
  590.  
  591. Then the DLL reads in the next chunk header, and the process repeats, until the 
  592. remainder of the file has been parsed, at which time MidiReadFile() returns. 
  593. MidiReadFile() itself returns a 0 if there were no errors reading the file. 
  594.  
  595. Note:  The DLL resolves MIDI running status, always supplying a status byte in 
  596.        the MIDIFILE's Status field for each event.  If you don't want to store 
  597.        those extra Status bytes (ie, you want to implement running status in 
  598.        your track memory), then you can compare the MIDIFILE's RunStatus field 
  599.        with the Status field in your StandardEvt callback.  If both are the 
  600.        same, then you've got an event whose status was resolved (ie, it should 
  601.        be running status).  Note that Meta-Events have nothing to do with 
  602.        running status since these aren't real MIDI events.  Furthermore SYSEX 
  603.        never have running status.  So, don't check for running status in any 
  604.        Meta callback or the SysexEvt callback.
  605.  
  606.  
  607. ΓòÉΓòÉΓòÉ 9. Writing a file ΓòÉΓòÉΓòÉ
  608.  
  609. Let's discuss the procedure for writing (creating) a file.  First, you'll 
  610. initialize the CALLBACK and MIDIFILE structures as described in Initialization, 
  611. and then call MidiWriteFile(), passing it your MIDIFILE structure.  The DLL 
  612. will open the file.  It also sets the MIDIWRITE Flag. 
  613.  
  614. Then, the DLL will call your StartMThd callback, passing a pointer to your 
  615. MIDIFILE structure.  Your callback should initialize the MIDIFILE's Format, 
  616. NumTracks, and Division fields.  These fields directly correspond to the values 
  617. in an MThd, except that these fields are arranged in Intel reverse byte order. 
  618. (ie, Your C program can write the Format as a USHORT, and not have to flip the 
  619. two bytes to Big Endian).  Upon return, the DLL will write out the MThd chunk. 
  620. Alternately, you could initialize these MIDIFILE fields before calling 
  621. MidiWriteFile(), in which case you don't need the StartMThd callback (ie, set 
  622. that CALLBACK field to 0). 
  623.  
  624. Then, the DLL starts calling various callbacks to write out the MTrk chunks. 
  625. It repeats the following process for as many times as your MIDIFILE's 
  626. NumTracks.  Your callbacks should return a 0 for success (to let the DLL 
  627. continue writing the file).  Otherwise, your DLL should return a non-zero 
  628. number to abort the write.  This number will then be returned from 
  629. MidiWriteFile(). 
  630.  
  631. The DLL increments the TrackNum field for each MTrk to be written out.  (The 
  632. first MTrk is 0).  It also writes out the MTrk header.  (The DLL takes care of 
  633. setting the chunk's size properly).  The DLL maintains the FileSize field as 
  634. the number of bytes written out to the file (including chunk headers).  This 
  635. should not be altered.  The DLL also maintains the ChunkSize field, which 
  636. should not be altered (and not used as a reflection of how many bytes have been 
  637. written to a chunk).  The DLL maintains the Handle field, and this should not 
  638. be altered. 
  639.  
  640. First, the DLL calls your StartMTrk callback, if supplied.  Typically, you 
  641. might use this to set up some globals associated with the MTrk to be written. 
  642. Optionally, you could write out some proprietary chunks before this MTrk, using 
  643. MidiWriteHeader(), 1 or more calls to MidiWriteBytes(), and MidiCloseChunk(). 
  644. If you want to supply a buffer that is already formatted with an entire MTrk's 
  645. data (ie, minus the header), place a pointer to the buffer in the MIDIFILE's 
  646. Time field.  Upon return, the DLL will write out that entire buffer, and then 
  647. call your StandardEvt callback once, if supplied.  Alternately, if you want to 
  648. write out the MTrk data one event at a time, then return with Time left at 0. 
  649.  
  650. The following discussion of the individual callbacks is for when the DLL writes 
  651. one event at a time.  The basic idea is that the DLL calls your StandardEvt 
  652. callback for each event to be written out.  Your callback stuffs the data for 
  653. one event into the MIDIFILE (or for a variable length event, just its Status 
  654. and Length), and returns to the DLL which writes out that event (perhaps 
  655. calling another callback for a variable length event).  The DLL continues 
  656. calling StandardEvt until you return an End Of Track event to write out.  Then, 
  657. the process repeats for the next MTrk chunk. 
  658.  
  659. Before calling StandardEvt initially, the DLL calls your MetaSeqNum callback 
  660. once, if supplied.  Typically, this is used to write out a Sequence Number 
  661. Meta-Event (which must be first in the MTrk if desired), and any other events 
  662. that need to written once at the start of the MTrk.  The DLL redefines the 
  663. MIDIFILE as a METASEQ, passing that to your callback.  At the least, your 
  664. callback should return the METASEQ with the SeqNum and NamePtr set as desired. 
  665. SeqNum is in Intel format, and can be written as a USHORT.  If you also want 
  666. the DLL to write out a Sequence Name event, place a pointer to the 
  667. null-terminated name in the METASEQ's NamePtr field.  Otherwise, if you don't 
  668. want a name event, set this to 0.  Upon return, the DLL writes out that 
  669. event(s).  Optionally, your callback can write additional events before 
  670. returning, using MidiWriteEvt(), but the callback must return at least one 
  671. event in the METASEQ for the DLL to write out.  You need to set up the METASEQ 
  672. (which is really a MIDIFILE and so can be recast as any of the structures in 
  673. midifile.h) before calling MidiWriteEvt(), and the manner that you do so is the 
  674. same as your StandardEvt callback described below. 
  675.  
  676. For all events that you wish to return to the DLL for writing out, StandardEvt 
  677. must store the event's time in the MIDIFILE's Time field.  This time is 
  678. referenced from 0, rather than the previous event like within the MIDI file, 
  679. unless you set the MIDIDELTA Flag.  The DLL will write out the event's time as 
  680. a variable length quantity timestamp to the MIDI file. 
  681.  
  682. For MIDI events with status 0x80 to 0xEF, 0xF1, 0xF2, 0xF3, 0xF6, 0xF8, 0xFA, 
  683. 0xFB, 0xFC, or 0xFE, your callback stores the event entirely into the MIDIFILE. 
  684. The MIDI Status byte must be placed into the MIDIFILE's Status field, and any 
  685. MIDI data bytes must be placed in MIDIFILE's Data[0] and Data[1].  Upon return, 
  686. the DLL writes out the entire event. 
  687.  
  688. If the event is a SYSEX or SYSEX CONTINUATION (0xF0 or 0xF7), then you must 
  689. store that Status in the MIDIFILE's Status, and set the EventSize to the number 
  690. of data bytes that will follow the Status.  Finally, set the ULONG starting at 
  691. Data[2] to 0.  Upon return, the DLL writes out the Status and the size as a 
  692. variable length quantity.  Then it calls your SysexEvt callback, which should 
  693. write out the expected data bytes using MidiWriteBytes(). 
  694.  
  695. Note:  Never write bytes using some OS or C library function.  Always use 
  696.        MidiWriteBytes(), which is used in much the same manner as you might use DosWrite().
  697.  
  698. Alternately, StandardEvt could store the F0 or F7 Status, the EventSize, and 
  699. then set the ULONG at Data[2] to be a pointer to a buffer to write out.  Upon 
  700. return, the DLL will write out the event entirely, with the data in the buffer. 
  701. Your SysexEvt callback will not be called (and need not be supplied). 
  702.  
  703. For a Tempo Meta-Event, your StandardEvt callback should redefine the MIDIFILE 
  704. as a METATEMPO, store an 0xFF in the Type field, store 0x51 in the WriteType 
  705. field, and store the micros per quarter in the Tempo field.  Tempo is a ULONG 
  706. in Intel format (ie, the DLL writes out the proper Big Endian bytes). 
  707. Alternately, if you've set the MIDIBPM Flag, you set the TempoBPM field 
  708. instead, and the DLL will calculate the Tempo field. 
  709.  
  710. For a SMPTE Offset Meta-Event, your StandardEvt callback should redefine the 
  711. MIDIFILE as a METASMPTE, store an 0xFF in the Type field, store 0x54 in the 
  712. WriteType field, and store the hours, minutes, seconds, frames, and subframes 
  713. fields. 
  714.  
  715. For a Time Signature Meta-Event, your StandardEvt callback should redefine the 
  716. MIDIFILE as a METATIME, store an 0xFF in the Type field, store 0x58 in the 
  717. WriteType field, and store the nom, denom, etc, fields.  If you've set the 
  718. MIDIDENOM Flag, you store the denom field as the true time signature 
  719. denominator, and the DLL will convert it to a power of 2. 
  720.  
  721. For a Key Signature Meta-Event, your StandardEvt callback should redefine the 
  722. MIDIFILE as a METAKEY, store an 0xFF in the Type field, store 0x59 in the 
  723. WriteType field, and store the key and minor fields. 
  724.  
  725. For an End Of Track Meta-Event, your StandardEvt callback should redefine the 
  726. MIDIFILE as a METAEND, store an 0xFF in the Type field, and store 0x2F in the 
  727. WriteType field.  Note that this will be the last event in the MTrk.  Returning 
  728. this event to the DLL for writing signals that the MTrk chunk is finished. 
  729.  
  730. For a Sequence Number Meta-Event, your StandardEvt callback should redefine the 
  731. MIDIFILE as a METASEQ, store an 0xFF in the Type field, store 0x00 in the 
  732. WriteType field, and store the SeqNum field.  Set the NamePtr field to 0 if you 
  733. don't want a Sequence Name event also written out.  Otherwise, set this to 
  734. point to a null-terminated name. 
  735.  
  736. For Meta-Events of types 0x01 to 0x0F (ie, Text, Copyright, Instrument, Track 
  737. Name, Marker, Lyric, Cue Point), 0x7F (Proprietary), and any other currently 
  738. undefined Meta-Events, set the MIDIFILE's Status to 0xFF, and set Data[0] to 
  739. the meta type.  If you want the DLL to write out the subsequent data bytes, set 
  740. the ULONG at Data[2] to point to a buffer containing the data to write out, and 
  741. set EventSize to the number of data bytes to write.  If you set EventSize to 0, 
  742. then the DLL assumes that the buffer contains a null-terminated string, and 
  743. calculates the length.  In these cases, the DLL writes out the event entirely. 
  744. Alternately, if you set the ULONG at Data[2] to 0, then you must set the 
  745. EventSize to the number of subsequent bytes that you intend to write out.  In 
  746. this case, the DLL calls your MetaText callback which will write out those 
  747. bytes using MidiWriteBytes(). 
  748.  
  749. The DLL continues calling StandardEvt (and perhaps SysexEvt or MetaText) to 
  750. write out each event (unless your callback returns non-zero to abort), until an 
  751. End Of Track event is written. 
  752.  
  753. Then the DLL starts the process again with the next MTrk chunk, until all MTrks 
  754. have been written (ie, NumTracks). 
  755.  
  756. Lastly, the DLL calls your UnknownChunk callback, if supplied.  This callback 
  757. could write out some proprietary chunks at the end of the file, using 
  758. MidiWriteHeader(), 1 or more calls to MidiWriteBytes(), and MidiCloseChunk(). 
  759.  
  760. After this, MidiWriteFile() returns.  MidiWriteFile() itself returns a 0 if 
  761. there were no errors writing the file. 
  762.  
  763. Note:  MidiWriteFile() will typically alter the MIDIFILE's Time and Status 
  764.        fields after your StandardEvt callback returns.  So, never make the 
  765.        assumption that these 2 fields will have the same values that you set 
  766.        them to on the previous call to StandardEvt.  For that matter, your 
  767.        other callbacks (such as MetaText and SysexEvt) should not make the same assumption.
  768.  
  769.  
  770. ΓòÉΓòÉΓòÉ 10. Errors ΓòÉΓòÉΓòÉ
  771.  
  772. Your callbacks return 0 for success, or non-zero for an error.  The error 
  773. numbers that might be returned by the DLL itself are listed in midifile.h. 
  774. Note that the DLL returns positive numbers for any errors.  You may wish to 
  775. have your callbacks return negative numbers to help you differentiate between 
  776. an error that happened within the DLL, and an error occuring within your 
  777. callbacks.  The only limitation that is imposed is that, if you supply a 
  778. MidiReadWrite callback, it can't return -1. 
  779.  
  780. The DLL has a routine, MidiGetErr(), which can return a null-terminated string 
  781. that describes one of the error numbers defined in midifile.h (ie, for display 
  782. to the end-user).  It copies the string to a buffer that you supply, and 
  783. returns the string length.  If the error number that you pass is not one of the 
  784. ones defined by the DLL, the buffer is nulled, and 0 returned.  In this case, 
  785. the error was obviously one that a callback returned. 
  786.  
  787.  
  788. ΓòÉΓòÉΓòÉ 11. Dual function callbacks ΓòÉΓòÉΓòÉ
  789.  
  790. Both MidiReadFile() and MidiWriteFile() require MIDIFILE and CALLBACK 
  791. structures.  Both functions call some of the same callbacks.  For example, 
  792. MidiWriteFile() calls your StandardEvt callback, expecting it to return info 
  793. about the next event to write out.  MidiReadFile() also calls your StandardEvt 
  794. callback, but it expects it to store the info that the DLL has loaded into the 
  795. MIDIFILE.  If your application both reads and writes MIDI files, you could use 
  796. the same MIDIFILE structure for calls to MidiReadFile() and MidiWriteFile(), 
  797. but you would need 2 CALLBACK structures.  One would have pointers to callbacks 
  798. that expect to be writing out a MIDI file.  Before calling MidiWriteFile(), you 
  799. would place a pointer to this CALLBACK into the MIDIFILE's Callbacks.  The 
  800. other CALLBACK would have pointers to callbacks that expect to be reading a 
  801. MIDI file.  Before calling MidiReadFile(), you would place a pointer to this 
  802. CALLBACK into the MIDIFILE's Callbacks. 
  803.  
  804. Alternately, you could use one CALLBACK and dual function callbacks.  These are 
  805. simply callbacks that inspect the MIDIWRITE bit of MIDIFILE's Flags in order to 
  806. decide whether a read or write operation needs to be performed.  Remember that 
  807. the DLL always sets this for a write operation, and clears it for a read 
  808. operation.  So, for example, your StandardEvt callback could inspect this Flag, 
  809. and know whether it was being called via MidiReadFile() or MidiWriteFile().  In 
  810. this case, you only need to initialize the MIDIFILE's Callbacks once at the 
  811. start of the program. 
  812.  
  813. Not all callbacks would need to inspect this flag.  Although MidiReadFile() 
  814. might call all of your supplied callbacks, MidiWriteFile() never calls the 
  815. MetaTempo, MetaSmpte, MetaTimeSig, MetaKeySig, and MetaEOT callbacks.  These 
  816. callbacks should always assume that they're doing a read operation. 
  817. Furthermore, MidiWriteFile() may never call your SysexEvt and MetaText 
  818. callbacks if your StandardEvt callback always supplies a pointer to a data 
  819. buffer for SYSEX and variable length meta events. 
  820.  
  821.  
  822. ΓòÉΓòÉΓòÉ 12. File I/O ΓòÉΓòÉΓòÉ
  823.  
  824. Usually, the DLL takes care of actually reading and writing bytes to a file. 
  825. The DLL's reading and writing routines are not buffered (although the DLL has 
  826. been designed to implement as efficient an unbuffered scheme as possible with 
  827. MIDI files).  If you wish to instead use your own, buffered file I/O routines 
  828. to read/write to the file (such as fread(), fwrite(), fseek(), etc), instead of 
  829. letting the DLL do that, then you need to supply the callbacks for MidiOpen, 
  830. MidiReadWrite, MidiSeek, and MidiClose. 
  831.  
  832. When initializing the MIDIFILE before calling MidiReadFile() or 
  833. MidiWriteFile(), you don't need to place a pointer to the filename in the 
  834. Handle.  Instead, when your MidiOpen callback is called, it should open the 
  835. user's filename and store the fileHandle for later use.  (You'll probably use 
  836. the MIDIFILE's Handle field for storage).  Remember that the MIDIWRITE Flag is 
  837. set if the DLL is writing out a MIDI file, and clear if the DLL is loading a 
  838. file.  So, your callback can determine whether it needs to open a file for 
  839. reading or writing.  For loading, you should also set the MIDIFILE's FileSize 
  840. to the number of bytes that the DLL has to parse (ie, usually the size of the 
  841. file).  For writing, you should set the FileSize to where the current file 
  842. pointer is located (ie, 0 if you're at the beginning of the file).  If you're 
  843. using the DLL to load a MIDI file that is buried inside of a larger file that 
  844. you've already read some initial data from, you should set FileSize to the 
  845. remaining bytes after the current file pointer position.  If you're writing out 
  846. a MIDI file inside of a larger file that you've already written some initial 
  847. data to, you should set FileSize to the amount of bytes already written out. 
  848.  
  849. Note:  It's possible to open the file, and setup the MIDIFILE's FileSize field 
  850.        before calling MidiReadFile() or MidiWriteFile(), but you still need a 
  851.        MidiOpen callback, which should simply return 0.
  852.  
  853. Your MidiReadWrite callback is passed a pointer to your MIDIFILE structure, a 
  854. pointer to a buffer, and a length argument.  The first thing that your callback 
  855. should do is examine the MIDIWRITE Flag bit to see if you need to perform a 
  856. read or write operation.  For a write operation, the buffer pointer contains 
  857. the data to be written out, and the length is the number of bytes to write. 
  858. For a read operation, the length is the number of bytes to read in, and the 
  859. buffer is where to read those bytes. 
  860.  
  861. Note:  None of your other callbacks should ever call your MidiReadWrite 
  862.        callback directly.  Your callbacks should use either MidiReadBytes() or 
  863.        MidiWriteBytes(), which indirectly call your MidiReadWrite callback to 
  864.        do the actual reading/writing of bytes.
  865.  
  866. Your MidiSeek callback should expect to do a seek.  It is passed a pointer to 
  867. the MIDIFILE, a signed long representing the amount of bytes to seek, and the 
  868. ulMoveType as described by OS/2's DosSetFilePtr().  (The DLL only ever passes 
  869. FILE_CURRENT as the movement type). 
  870.  
  871. Note:  None of your other callbacks should ever call your MidiSeek callback 
  872.        directly.  Your callbacks should use the DLL's MidiSeek(), which 
  873.        indirectly calls your MidiSeek callback to do the actual seek operation. 
  874.        MidiSeek() only allows seeking from the current file pointer position.
  875.  
  876. Your MidiClose callback should expect to close the file that you opened.  Of 
  877. course, you could chose to not close it at this time, and then after returning 
  878. from MidiWriteFile() or MidiReadFile(), do some more reading/writing to the 
  879. file.  But, it's recommended that you instead read/write any final bytes in 
  880. your UnknownChunk callback.  If your MidiClose callback closes the file, then 
  881. you can be assured that the file will be properly closed, even if an error 
  882. occurs and the read/write is aborted. 
  883.  
  884. Note:  If you supply your own file I/O routines, the MIDIFILE's Handle field 
  885.        will not be touched by the DLL, and so you can use it for whatever 
  886.        purpose you decide.
  887.  
  888.  
  889. ΓòÉΓòÉΓòÉ 13. Limitations ΓòÉΓòÉΓòÉ
  890.  
  891. Since MidiReadFile() and MidiWriteFile() carry out the desired operation in 
  892. entirety before returning, it's not possible to have one thread read in data 
  893. from one MIDI file at the same time that it's writing out data to another MIDI 
  894. file.  You'd have to read in one file entirely into memory, and then write it 
  895. back out.  Alternately, you could have two threads; one to call MidiReadFile() 
  896. and one to call MidiWriteFile(), and have the callbacks of one thread wait for 
  897. semaphores or messages from the other thread's respective callbacks to 
  898. synchronize reading/writing.  You'd need separate MIDIFILE structures.