home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / msdos / sndbords / proaudio / tp_int94 / tp_int94.arj / MVINT94.PAS < prev    next >
Pascal/Delphi Source File  |  1993-12-13  |  22KB  |  499 lines

  1. UNIT MVInt94;
  2. {$I mvInt94.def}
  3. {-------------------------------------------------------------------------}
  4. { Media Vision Pro Audio Spectrum routines for Borland Pascal v7.  This   }
  5. { is a simple little unit designed to access the Media Vision sound board }
  6. { through PCM.COM's INT 94h driver.  This software has been written from  }
  7. { documentation supplied in the PCM.ZIP file downloaded from the Media    }
  8. { Vision BBS, and a lot of answers from Bart Crane at Media Vision, who   }
  9. { wrote the PCM.COM driver in the first place.                            }
  10. {                                                                         }
  11. { Copyright 1993, Peter W. Cervasio.                                      }
  12. { This software may be freely used, so long as my copyright is maintained }
  13. {-------------------------------------------------------------------------}
  14. { Revision Data:                                                          }
  15. {  1.0   02 Dec 93  pwc     Initial coding                                }
  16. {  1.1   10 Dec 93  pwc     Added SetPCMInfo and .WAV header info         }
  17. {-------------------------------------------------------------------------}
  18. { Written/Translated by Peter W. Cervasio                                 }
  19. { Contact me via the Media Vision BBS,  (Pete Cervasio)                   }
  20. {   or via CompuServe: 73443,1426                                         }
  21. {-------------------------------------------------------------------------}
  22. INTERFACE
  23. USES Dos;
  24.  
  25. CONST
  26.     BoardAddress : Word = $0388;                  { default board address }
  27.  
  28.     ThunderBoard : Word = 0;         { ThunderBoard bit.  Set to $4000 if }
  29.             { using the Thunderboard or Soundblaster portion of the card. }
  30.  
  31.   { we really don't use these next two, though they're here in case we do }
  32.                                    { something with them at a later time. }
  33.     IRQ          : Byte = $07;                        { default board irq }
  34.     DMA          : Byte = $03;                { default board dma channel }
  35.  
  36. { These are the bits returned by GetHWVersionBits.  To use them, AND the  }
  37. { value returned with the specified constant.  For example: to find out   }
  38. { if we have the MVA508 mixer or the National one, do the following:      }
  39. {                                                                         }
  40. {    HWStuff := GetHWVersionBits;                                         }
  41. {    IF HWStuff and bMVA508 = bMVA508 then                                }
  42. {        Writeln ('We have the MVA508 mixer')                             }
  43. {    else                                                                 }
  44. {        Writeln ('We have the National mixer')';                         }
  45. {                                                                         }
  46.  
  47.     bMVA508     = $0001;  { MVA508(1) or National(0) mixer }
  48.     bMVPS2      = $0002;  { PS2 bus stuff }
  49.     bMVSLAVE    = $0004;  { CDPC Slave device is present }
  50.     bMVSCSI     = $0008;  { SCSI interface }
  51.     bMVENHSCSI  = $0010;  { Enhanced SCSI interface }
  52.     bMVSONY     = $0020;  { Sony 535 interface }
  53.     bMVDAC16    = $0040;  { 16 bit DAC }
  54.     bMVSBEMUL   = $0080;  { SB h/w emulation }
  55.     bMVMPUEMUL  = $0100;  { MPU h/w emulation }
  56.     bMVOPL3     = $0200;  { OPL3(1) or 3812(0) }
  57.     bMV101      = $0400;  { MV101 ASIC }
  58.     bMV101_REV  = $7800;  { MV101 Revision }
  59.  
  60. TYPE
  61.  
  62. {-----------------------------------------------------------------------------}
  63. { The "WaveHeaderType" structure holds the format of a .WAV file's header.    }
  64. { This is for informational purposes only, really.  There aren't any routines }
  65. { in this unit to open .WAV files.  Some programs stick their own junk in the }
  66. { middle of a .WAV's header, too.  GoldWave is one.  You just have to open    }
  67. { file and search for the various header markers ('RIFF', 'fmt ', 'data',     }
  68. { etc.) before reading the data in.  I believe all the Media Vision recording }
  69. { utilities create a .WAV header in this format, though, so you should be     }
  70. { safe reading the beginning of those as a record.                            }
  71. {-----------------------------------------------------------------------------}
  72. WaveHeaderType = RECORD
  73.     Riff        : Array[0..3] of char;    { 'RIFF' }
  74.     RiffLength  : LongInt;
  75.     Wave        : Array[0..3] of char;    { 'WAVE' }
  76.     Fmt         : Array[0..3] of char;    { 'fmt ' }
  77.     FmtLength   : LongInt;                { length of the info block (16)   }
  78.     FormatTag   : Word;                   { format tag??                    }
  79.     Channels    : Word;                   { 1 = mono, 2 = stereo            }
  80.     SampleRate  : LongInt;                { samples per second              }
  81.     SampPerSec  : LongInt;                { channels * sample rate          }
  82.     BlockAlign  : Word;                   { block alignment (1=byte)        }
  83.     SampleSize  : Word;                   { bits per sample (8/16)          }
  84.     DataHeader  : Array[0..3] of char;    { 'data'                          }
  85.     WaveLength  : LongInt;                { length of PCM data in the file  }
  86. END;
  87.  
  88.  
  89. {-------------------------------------------------------------------------}
  90. { The "MVState" structure holds the hardware state table returned by      }
  91. { InitMVSound().  I don't understand this.. I just translated information }
  92. { that was in a C header file.                                            }
  93. {-------------------------------------------------------------------------}
  94. MVState = RECORD
  95.     SysSpkrTmr  : Byte;         {   42 System Speaker Timer Address  }
  96.     SysTmrCllr  : Byte;         {   43 System Timer Control          }
  97.     SysSpkrReg  : Byte;         {   61 System Speaker Register       }
  98.     Joystick    : Byte;         {  201 Joystick Register             }
  99.     LFMAddr     : Byte;         {  388 Left  FM Synth Address        }
  100.     LFMData     : Byte;         {  389 Left  FM Synth Data           }
  101.     RFMAddr     : Byte;         {  38A Right FM Synth Address        }
  102.     RFMData     : Byte;         {  38B Right FM Synth Data           }
  103.     DFMAddr     : Byte;         {  788 Dual  FM Synth Address        }
  104.     DFMData     : Byte;         {  789 Dual  FM Synth Data           }
  105.     RESRVD1     : Byte;         {      reserved                      }
  106.     PAudioMixr  : Byte;         {  78B Paralllel Audio Mixer Control }
  107.     AudioMixr   : Byte;         {  B88 Audio Mixer Control           }
  108.     IntrCtlrSt  : Byte;         {  B89 Interrupt Status              }
  109.     AudioFilt   : Byte;         {  B8A Audio Filter Control          }
  110.     IntrCtlr    : Byte;         {  B8B Interrupt Control             }
  111.     PcmData     : Byte;         {  F88 PCM Data I/O Register         }
  112.     RESRVD2     : Byte;         {      reserved                      }
  113.     CrossChannel: Byte;         {  F8A Cross Channel                 }
  114.     RESRVD3     : Byte;         {      reserved                      }
  115.     SampleRate  : Word;         { 1388 Sample Rate Timer             }
  116.     SampleCnt   : Word;         { 1389 Sample Count Register         }
  117.     SpkrTmr     : Word;         { 138A Shadow Speaker Timer Count    }
  118.     TmrCtlr     : Byte;         { 138B Shadow Speaker Timer Control  }
  119.     MdIrqVect   : Byte;         { 1788 MIDI IRQ Vector Register      }
  120.     MdSysCtlr   : Byte;         { 1789 MIDI System Control Register  }
  121.     MdSysStat   : Byte;         { 178A MIDI IRQ Status Register      }
  122.     MdIrqClr    : Byte;         { 178B MIDI IRQ Clear Register       }
  123.     MdGroup1    : Byte;         { 1B88 MIDI Group #1 Register        }
  124.     MdGroup2    : Byte;         { 1B89 MIDI Group #2 Register        }
  125.     MdGroup3    : Byte;         { 1B8A MIDI Group #3 Register        }
  126.     MdGroup4    : Byte;         { 1B8B MIDI Group #4 Register        }
  127. END;
  128. MVStatePtr = ^MVState;
  129.  
  130. {-------------------------------------------------------------------------}
  131. { The PCMINFOTYPE record is used to set the sample rate, bit size and     }
  132. { the mono/stereo setting.  The sample rate can be up to 88200 for mono   }
  133. { and 44100 for stereo.  Bit size is either 8 or 16.  StereoFlag is set   }
  134. { to 0 for mono, or 1 for stereo.                                         }
  135. {-------------------------------------------------------------------------}
  136. PCMInfoType = RECORD                                { Passed to PCMInfo() }
  137.     SampleRate : LongInt;          { 100 - 88200 (200 - 44100 for stereo) }
  138.     StereoFlag : Integer;                          { 0 = mono, 1 = stereo }
  139.                  { I added these two... don't know if they're right - pwc }
  140.     UnKnown    : Integer;                                      { set to 0 }
  141.     BitSize    : Integer;                       { 8 or 16 bits per sample }
  142. end;
  143.  
  144. {-------------------------------------------------------------------------}
  145. { The DMABUFTYPE record is used to inform the TSR of the DMA buffer's     }
  146. { address, it's size in kilobytes, and the number of partitions it is     }
  147. { to be split into.  The DMA buffer must be contained completely within   }
  148. { a 64k block.  i.e.: it must not go from 2xxx:xxxx into 3xxx:xxxx.  You  }
  149. { WILL experience lockups if it does so.  There is a function in the      }
  150. { unit called AllocateDMABuffer that takes a word parameter telling how   }
  151. { many kb you want to allocate.  It will return a pointer to a block of   }
  152. { memory that will satisfy the conditions.  See the comments near the     }
  153. { function for more information.                                          }
  154. {-------------------------------------------------------------------------}
  155. DMABufType = RECORD                               { Passed to DMABuffer() }
  156.     DmaBuffer   : Pointer;                        { address of DMA buffer }
  157.     BufKBSize   : Word;                        { size of DMA buffer in KB }
  158.     Partitions  : Word;                            { number of partitions }
  159. end;
  160.  
  161. {-------------------------------------------------------------------------}
  162. { The FDMABUFTYPE record is used with the FindDMABuffer function.  The    }
  163. { DMABuf element should point to a block of memory that is at least twice }
  164. { as large as the DMA buffer you desire, and the BufKBSize element should }
  165. { be set to the size (in kilobytes) of the DMA buffer you desire.  The    }
  166. { FindDMABuffer function will return a valid DMA buffer address out of    }
  167. { the block of memory you passed to it.  This functionality is in the     }
  168. { Int $94 driver.                                                         }
  169. {-------------------------------------------------------------------------}
  170. FDMABufType = RECORD                          { Passed to FindDMABuffer() }
  171.     DmaBuf      : Pointer;                  { address of memory allocated }
  172.     BufKBSize   : Word;                            { size of memory in KB }
  173. end;
  174.  
  175. { This only sucks up six bytes from your Data Segment... pretty cheap, huh? }
  176. VAR
  177.     DMACounter : Word;   { Used by EzDMACounter - incremented every time the }
  178.                                            { DMA Callback routine is called. }
  179.     EZDMA      : Pointer;             { Pointer set to point to EZDMACounter }
  180.  
  181.  
  182. { ------- Functions and Procedures defined in the unit -------- }
  183.  
  184. FUNCTION InitMVSound: Pointer;
  185. FUNCTION InitPCM: Integer;
  186. FUNCTION SetPCMInfo(DataSize, MonoStereo: Word; SampleRate: LongInt): Integer;
  187. FUNCTION PCMInfo(VAR P_Info: PCMInfoType): Integer;
  188. FUNCTION UserFunc(VAR S): Pointer;
  189. FUNCTION DMABuffer(VAR D_Info: DMABufType): Pointer;
  190. FUNCTION PCMRecord: Integer;
  191. FUNCTION PCMPlay: Integer;
  192.  
  193. FUNCTION GetHWVersionBits: Word;
  194. FUNCTION FindDMABuffer(VAR S: FDmaBufType): Pointer;
  195. FUNCTION GetDMAAddress: Pointer;
  196. FUNCTION GetDMASize: LongInt;
  197.  
  198. PROCEDURE ResumePCM;
  199. PROCEDURE PausePCM;
  200. PROCEDURE StopPCM;
  201. PROCEDURE RemovePCM;
  202. PROCEDURE AllocateDMABuffer(VAR TheBuffer: Pointer; Size: Word);
  203. PROCEDURE EzDMACallback;
  204.  
  205. IMPLEMENTATION
  206.  
  207. VAR
  208.     Int94Ptr : Pointer;
  209.  
  210. { In Turbo Pascal, we are free to modify all the microprocessor's registers }
  211. { with the exception of SS, DS, BP and SP.
  212.  
  213. { This call is not really necessary, since it is done by the PCM.COM tsr upon  }
  214. { loading.  Returns the address of the hardware state table (MVState above) or }
  215. { a nil pointer if it fails                                                    }
  216. FUNCTION InitMVSound: Pointer; assembler;
  217. asm
  218.     mov     si, BoardAddress
  219.     or      si, ThunderBoard
  220.     shl     si, 4
  221.     int     $94
  222. end;
  223.  
  224. { Initialize PCM State Tables }
  225. { Returns 0 if failed, otherwise the library version }
  226. FUNCTION InitPCM: Integer; assembler;
  227. asm
  228.     mov     ax, 1
  229.     mov     si, BoardAddress
  230.     or      si, ThunderBoard
  231.     shl     si, 4
  232.     or      si, ax
  233.     int     $94
  234. end;
  235.  
  236. { Set sample rate, size, and mono/stereo }
  237. { Returns 0 if okay, -1 if Sample Rate outside valid range }
  238. FUNCTION SetPCMInfo(DataSize, MonoStereo: Word; SampleRate: LongInt): Integer; assembler;
  239. asm
  240.     mov     ax, datasize
  241.     push    ax
  242.     mov     ax, 0
  243.     push    ax
  244.     mov     ax, monostereo
  245.     push    ax
  246.     mov     ax, word ptr samplerate[2]
  247.     push    ax
  248.     mov     ax, word ptr samplerate[0]
  249.     push    ax
  250.     mov     ax, 2
  251.     mov     bx, ss
  252.     mov     es, bx
  253.     mov     bx, sp
  254.     mov     si, BoardAddress
  255.     or      si, ThunderBoard
  256.     shl     si, 4
  257.     or      si, ax
  258.     int     $94
  259.     pop     bx
  260.     pop     bx
  261.     pop     bx
  262.     pop     bx
  263.     pop     bx
  264. end;
  265.  
  266. { Same as SetPCMInfo, but using a record to hold the info. }
  267. FUNCTION PCMInfo(VAR P_Info: PCMInfoType): Integer; assembler;
  268. asm
  269.     les     bx, p_info
  270.     mov     ax, 2
  271.     mov     si, BoardAddress
  272.     or      si, ThunderBoard
  273.     shl     si, 4
  274.     or      si, ax
  275.     int     $94
  276. end;
  277.  
  278. { Set DMA Buffer address, size, and partitions }
  279. { NOTE:  The DMA buffer _CANNOT_ cross a 64k boundary.  See the sample program }
  280. {        for an easy way of getting it to start at the beginning of one }
  281. { Returns nil pointer if it failed }
  282. FUNCTION DMABuffer(VAR D_Info: DMABufType): Pointer; assembler;
  283. asm
  284.     les     bx, d_info
  285.     mov     ax, 3
  286.     mov     si, BoardAddress
  287.     or      si, ThunderBoard
  288.     shl     si, 4
  289.     or      si, ax
  290.     int     $94
  291. end;
  292.  
  293. { Set the address of the callback procedure.  Note that the DS register will }
  294. { probably not be set to our data segment when it gets called.  Using the one }
  295. { in this unit, EzDMACallback, and the variable DMACounter is recommended. }
  296. { Returns nil pointer if it failed to set the address correctly }
  297. FUNCTION UserFunc(VAR S): Pointer; assembler;
  298. asm
  299.     les     bx, S
  300.     mov     ax, 4
  301.     mov     si, BoardAddress
  302.     or      si, ThunderBoard
  303.     shl     si, 4
  304.     or      si, ax
  305.     int     $94
  306. end;
  307.  
  308. { Sets the PCM routines into the Play state.  Upon the next call to ResumePCM }
  309. { the digital audio will start.  This function returns 0 if successful        }
  310. FUNCTION PCMPlay: Integer; assembler;
  311. asm
  312.     mov     ax, 5
  313.     mov     si, BoardAddress
  314.     or      si, ThunderBoard
  315.     shl     si, 4
  316.     or      si, ax
  317.     int     $94
  318. end;
  319.  
  320. { Sets the PCM routines into the Record state.  On the next call to ResumePCM }
  321. { the recording will start.  This function returns 0 if successful            }
  322. FUNCTION PCMRecord: Integer; assembler;
  323. asm
  324.     mov     ax, 6
  325.     mov     si, BoardAddress
  326.     or      si, ThunderBoard
  327.     shl     si, 4
  328.     or      si, ax
  329.     int     $94
  330. end;
  331.  
  332. { This procedure sets the PCM routines into the pause state.  Use this before  }
  333. { calling PCMRecord or PCMPlay to avoid the DMA handler starting until you are }
  334. { ready for it.  Also used while recording/playing PCM audio to provide a      }
  335. { pause.                                                                       }
  336. PROCEDURE PausePCM; assembler;
  337. asm
  338.     mov     ax, 7
  339.     mov     si, BoardAddress
  340.     or      si, ThunderBoard
  341.     shl     si, 4
  342.     or      si, ax
  343.     int     $94
  344. end;
  345.  
  346.  
  347. { This procedure starts the Play / Record of PCM audio data after a call to }
  348. { PCMPlay or PCMRecord, or it resumes the operation that was paused with a  }
  349. { call to PausePCM.                                                         }
  350. PROCEDURE ResumePCM; assembler;
  351. asm
  352.     mov     ax, 8
  353.     mov     si, BoardAddress
  354.     or      si, ThunderBoard
  355.     shl     si, 4
  356.     or      si, ax
  357.     int     $94
  358. end;
  359.  
  360. { This procedure stops the Play / Record of PCM data. }
  361. PROCEDURE StopPCM; assembler;
  362. asm
  363.     mov     ax, 9
  364.     mov     si, BoardAddress
  365.     or      si, ThunderBoard
  366.     shl     si, 4
  367.     or      si, ax
  368.     int     $94
  369. end;
  370.  
  371. { Remove the PCM hook to Int $94 and reset internal DMA variables }
  372. PROCEDURE RemovePCM; assembler;
  373. asm
  374.     mov     ax, 10
  375.     mov     si, BoardAddress
  376.     or      si, ThunderBoard
  377.     shl     si, 4
  378.     or      si, ax
  379.     int     $94
  380. end;
  381.  
  382. { find an area from memory within a 64k block }
  383. { returns nil if failed, or a valid DMA buffer address }
  384. FUNCTION FindDMABuffer(VAR S: FDmaBufType): Pointer; assembler;
  385. asm
  386.     les     bx, S
  387.     mov     ax, 11
  388.     mov     si, BoardAddress
  389.     or      si, ThunderBoard
  390.     shl     si, 4
  391.     or      si, ax
  392.     int     $94
  393. end;
  394.  
  395. { This function doesn't always work, according to Bart Crane of Media Vision. }
  396. { It is supposed to return the address of the DMA buffer that was set using   }
  397. { DMABuffer().  Certain versions of PCM.COM have an invalid segment override  }
  398. { in them, though, and return the wrong information.  Since the DMABuffer()   }
  399. { routine is supposed to always work, you don't really need this.             }
  400. FUNCTION GetDMAAddress: Pointer; assembler;
  401. asm
  402.     mov     ax, $8000
  403.     mov     si, BoardAddress
  404.     or      si, ThunderBoard
  405.     shl     si, 4
  406.     or      si, ax
  407.     int     $94
  408. end;
  409.  
  410. { This function doesn't always work, according to Bart Crane of Media Vision. }
  411. { It is supposed to return the size and # of partitons of the DMA buffer that }
  412. { was set using DMABuffer().  See GetDMAAddress above for the reasons it may  }
  413. { not work.  The number of partitions is returned in the high word of the     }
  414. { long integer while the size of the buffer is returned in the low word.      }
  415. FUNCTION GetDMASize: LongInt; assembler;
  416. asm
  417.     mov     ax, $8001
  418.     mov     si, BoardAddress
  419.     or      si, ThunderBoard
  420.     shl     si, 4
  421.     or      si, ax
  422.     int     $94
  423. end;
  424.  
  425. { This function returns a word describing the hardware that is detected at }
  426. { the current board address.  The bit meanings are described at the top of }
  427. { the unit.                                                                }
  428. FUNCTION GetHWVersionBits: Word; assembler;
  429. asm
  430.     mov     ax, 12
  431.     mov     si, BoardAddress
  432.     or      si, ThunderBoard
  433.     shl     si, 4
  434.     or      si, ax
  435.     int     $94
  436. end;
  437.  
  438. { This is a quick and easy DMA callback routine that you can use.  It will  }
  439. { increment the word variable DMACounter once every time a DMA partition is }
  440. { used.  Your main program loop should monitor this variable after calling  }
  441. { ResumePCM to know when a DMA partition should be filled with new data.    }
  442. { See the TESTPCM.PAS program that came with this unit for an example of    }
  443. { what I'm talking about.                                                   }
  444. PROCEDURE EzDMACallback; assembler;
  445. asm
  446.     push    ds
  447.     mov     ax, SEG @data
  448.     mov     ds, ax
  449.     inc     DMACounter
  450.     pop     ds
  451. end;
  452.  
  453. { Allocate memory for the DMA buffer.  This memory must _NOT_ cross a 64k  }
  454. { boundary.  We take care of that by finding out where the heap pointer is }
  455. { and how far it is to the next 64k boundary.  If there is enough space to }
  456. { allocate the DMA buffer without crossing the boundary, we go ahead and   }
  457. { put it where it will go.  Otherwise, we temporarily suck up enough heap  }
  458. { space to put the heap pointer on the next 64k boundary and allocate the  }
  459. { DMA buffer there, freeing the temporary space after we're done.  GetMem  }
  460. { is used for the memory allocation, so you need to call FreeMem with the  }
  461. { correct size if you want to free the DMA buffer for some reason.         }
  462. PROCEDURE AllocateDMABuffer(VAR TheBuffer: Pointer; Size: Word);
  463. VAR
  464.     J, JJ : Pointer;
  465.     K : Array[1..2] of Word absolute J;
  466.     M : Word;
  467. BEGIN
  468.     { Take the parameter as either the number of K, or the actual size of   }
  469.     { the requested buffer.  If it's 64 or less, assume it's in kb, if more }
  470.     { then assume it's the number of bytes requested.                       }
  471.     if Size <= 64 then Size := Size * 1024;
  472.     J := HeapPtr;                      { Find out where our heap pointer is }
  473.     M := $1000 - (K[2] AND $FFF);   { Take high word of it and find out how }
  474.                                               { far it is to the next $x000 }
  475.     if M = $1000 then M := 0;            { deal with it being there already }
  476.     if Size < M * 16 then M := 0;        { We have room where we are if so. }
  477.     if M > 0 then GetMem (JJ, M * 16);   { if it isn't, suck some memory up }
  478.     GetMem (TheBuffer, Size);               { okay, allocate our DMA buffer }
  479.     if M > 0 then FreeMem(JJ, M * 16);       { and free our temporary stuff }
  480. END;
  481.  
  482.  
  483. { Unit initialization code.  This just checks to see that PCM.COM has been }
  484. { loaded into memory and halts with a message if not.  If you want to try  }
  485. { and automatically load it if it isn't found, then it is up to you to get }
  486. { it working... I think it would be okay to do a SwapVectors and exec PCM  }
  487. { because I don't think SwapVectors bothers with int $94.  I don't need it }
  488. { working that way, so I haven't bothered with it.                         }
  489. BEGIN
  490.     GetIntVec($94, Int94Ptr);
  491.     if not assigned(Int94Ptr) then
  492.     begin
  493.          writeln ('ERROR: Sound driver not installed properly.');
  494.          writeln ('Please load PCM.COM and re-run this program.');
  495.          Halt(1);
  496.     end;
  497.     EZDma := @EZDMACallback;
  498. END.
  499.