home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / diverace.zip / SOURCE.ZIP / source / work / sounddrv.pas < prev   
Pascal/Delphi Source File  |  1995-08-28  |  33KB  |  975 lines

  1. (*******************************************************)
  2. (*               Voice-Mixer Unit                      *)
  3. (*******************************************************)
  4.  
  5. unit sounddrv;
  6.  
  7. (************************************************************************)
  8. interface
  9.  
  10. {$S-}
  11.  
  12. uses Os2Def, Os2Base, Os2PmApi, Use32;
  13.  
  14. const
  15.   currentblock :integer=0;
  16.   SoundWatchdog:longint=0; (*This count HAS to be increased by an external
  17.                              process at least BLOCKS_PER_SEC times per second
  18.                              to indicate an eventual sound-mixing overload.*)
  19.  
  20.   SoundPaused  :boolean=FALSE; (*Indicates Paused Sound, do not changed directly*)
  21.  
  22.  
  23. Procedure SoundInit;
  24.  
  25. Function  LoadWav(FileName:String;var WavIdx:Integer):boolean;
  26. Function  StartPlayBack:boolean;
  27. Function  StopPlayBack:boolean;
  28. Function  PausePlayback:boolean;
  29. Function  ResumePlayback:boolean;
  30.  
  31. Procedure InitVoice(VoiceIdx,WavIDx,Prio:Integer;Loop:Boolean);
  32. Procedure SetVoiceVol(VoiceIdx,LVolume,RVolume:Integer); (*Set Volume*)
  33. Procedure SetVoiceRate(VoiceIdx,Rate:Integer); (*Playback Samples Per Sec*)
  34. Function  VoiceIsPlaying(VoiceIdx:Integer):boolean;
  35. Procedure PlayVoice(VoiceIdx:Integer);
  36. Procedure StopVoice(VoiceIdx:Integer);
  37.  
  38. Procedure SetGlobalVolume(Volume:Integer);
  39.  
  40. Procedure SoundDone;
  41.  
  42. Procedure ShowWave(var videobuffer;col,xmax,ymax:integer);
  43.  
  44.  
  45. (************************************************************************)
  46. implementation
  47.  
  48. uses MciOS2;
  49.  
  50. (************************************************************************)
  51. (* Needed Structures and Constants                                      *)
  52. const
  53.   MIX_RATE                =11025; (*Mixed & Playback Samples per Sec*)
  54.   MIX_BITS                =8;     (*You have to change the Mix-Voices *)
  55.                                   (*Function to enable other Formats  *)
  56.  
  57.   MIX_VOICES              =4;     (*Maximum Number of Voices actually mixed*)
  58.  
  59.   BLOCKS_PER_SEC          =10; (*Set Block-Length*)
  60.   CUE_POS                 =50; (*Cuepoint Position in Buffer in % *)
  61.   BLOCK_LENGTH            =MIX_RATE*(MIX_BITS div 8) div BLOCKS_PER_SEC;
  62.   NUMBER_OF_BLOCKS        =2;  (*Data-Blocks in the Playlist*)
  63.   PLAYLIST_COMMANDS       =NUMBER_OF_BLOCKS*3; (*SETCUEP,DATA_OP,BRANCH*)
  64.  
  65.   MAX_VOICES              =16; (*Max-Number of Processed (not mixed) voices*)
  66.   MAX_WAV                 =16; (*Max-Number of loaded Wav-Files*)
  67.  
  68. (************************************************************************)
  69.  
  70. type
  71.   (* SoundInfo holds information about a Wav *)
  72.   SoundInfo = record
  73.     sFileName       :String;
  74.     Address         :^LONG;
  75.     ulSize          :ULONG;
  76.     ulSamplesPerSec :ULONG;
  77.     usBitsPerSample :USHORT;
  78.     ulLoopOfs       :ULONG; (*-1 for no Looping*)
  79.   end;
  80.  
  81.   (* SoundDevice holds information about the Sound Device *)
  82.   Sound_Device=record
  83.     usSoundDeviceID:USHORT;
  84.     ulSamplesPerSec:ULONG;
  85.     usBitsPerSample:USHORT;
  86.   end;
  87.  
  88.  
  89.   (* A Playlist is a sequence of machine-code like instructions for mmpm *)
  90.   (* In this case it alternatively sends notification-messages and plays *)
  91.   (* parts of the wave-buffer                                            *)
  92.   play_list_structure = record      (* playlist structure *)
  93.     ulCommand:ULONG;
  94.     ulOperandOne:ULONG;
  95.     ulOperandTwo:ULONG;
  96.     ulOperandThree:ULONG;
  97.   end;
  98.  
  99.   (* 32Bit unsigned Fixed-Point Number *)
  100.   fix = record case boolean of
  101.           true:(fix:ULong);             (* Whole Number             *)
  102.           false:(f:system.word;i:system.word);
  103.                  (*fract       int*)
  104.         end;
  105.  
  106.   (* Info about a certain Voice to be mixed by the Unit*)
  107.   Voice_Info = record
  108.     MyWave            : Integer; (* Associated Wave Idx      *)
  109.     StartAddr         : Integer; (* Copied Wave information  *)
  110.     Size              : Integer;
  111.     origSamplesPerSec : Integer;
  112.     LoopPos           : Integer;
  113.  
  114.     origHz            : Integer; (*Sample Frequency in Hertz (to come)*)
  115.  
  116.     Playing           : Boolean; (*Application currently wants to play Voice*)
  117.     Mixed             : Boolean; (*This Voice is actually Playing*)
  118.     Priority          : Integer; (*Voice Playback Priority*)
  119.  
  120.     VolumeLeft,
  121.     VolumeRight       : Integer; (*Volumes*)
  122.  
  123.     nowSamplesPerSec  : Integer; (*Set Playback SamplesPerSeconds*)
  124.     dx                : Fix;     (*Stepwidth for current SamplesPerSec*)
  125.     SourceOfs         : Fix;     (*Current Offset in Source*)
  126.   end;
  127.  
  128. (************************************************************************)
  129. const
  130.   hwndSound : HWND =0;   (* Window-Handle of Sound-Message Receiver*)
  131.   LastWatchDog : LONGINT = 0;
  132.  
  133. var
  134.   habSound  : HAB;       (* Anchor Block Handle                    *)
  135.   tidSound  : TID;       (* Sound Thread-ID                        *)
  136.  
  137.   h_mq:HMQ;
  138.   q_msg:qmsg;
  139.  
  140.   DriverOpen     : boolean;
  141.   PlaybackActive : boolean;
  142.  
  143.   GlobalVolume : Integer;
  144.                          (*Playback-Buffer, mixing goes here*)
  145.   Buffer : array[0..BLOCK_LENGTH*NUMBER_OF_BLOCKS] of byte;
  146.                          (*Info about Voices to Play*)
  147.   Voice : array [0..MAX_VOICES-1] of Voice_Info;
  148.                          (*Info about Wav-Files loaded*)
  149.   WavFiles : array [0..MAX_WAV-1] of SoundInfo;
  150.  
  151.   playlist: array [0..PLAYLIST_COMMANDS] of play_list_structure;
  152.  
  153.   NumWavs     : integer;   (* # of loaded Wav-Files*)
  154.   NumVoices   : integer;   (* # of currently enabled Voices*)
  155.                            (* # of Entries in Playlist*)
  156.   Playlist_Entries : Integer;
  157. {  BlockLen         : Integer; (*Length of one Playback Data-Block*)}
  158.  
  159.                          (*MCI Info Structures*)
  160.   SoundDevice: Sound_Device;
  161.   mciOpenParameters:MCI_OPEN_PARMS;  (* Open structure.       *)
  162.   mmAudio_Header:MMAUDIOHEADER;      (* Contains info for SET *)
  163.  
  164.   ulError : Ulong; (*Variable for error processing*)
  165.  
  166.   (*************************************************************************)
  167.   (* Private Functions *)
  168.  
  169.   Procedure MixVoices(PrevBlock:Integer);forward;
  170.   Function  SoundWndProc(Window: HWnd; Msg: ULong; Mp1,Mp2: MParam): MResult;cdecl;forward;
  171.   Procedure MCIERROR;forward;
  172.   Procedure InitPlaylist;forward;
  173.   Procedure SoundThread(param1:Ulong);forward;
  174.   Function  OpenSoundDevice:Boolean;forward;
  175.   Procedure AdjustSoundDevice;forward;
  176.   Procedure CloseSoundDevice;forward;
  177.  
  178.   (*************************************************************************)
  179.  
  180.  
  181. (*********** Tool Functions**********)
  182.  
  183. Function Strint(x:longint):string;
  184. var s:string[20];
  185. begin
  186.   Str(x,s);
  187.   StrInt:=s;
  188. end;
  189.  
  190. Procedure SetPSZLen(var s:string);
  191. var i:integer;
  192. begin
  193.   i:=1;
  194.   while (i<256) and (s[i]<>#0) do
  195.     inc(i);
  196.   if (i<256) then
  197.     s[0]:=chr(i);
  198. end;
  199.  
  200.  
  201.  
  202. (*****************************************************************************
  203.  *                     Init SoundDriver Unit                                 *
  204.  ****************************************************************************)
  205.  
  206.  
  207. Procedure ShowWave(var videobuffer;col,xmax,ymax:integer);
  208. var
  209.   x:integer;
  210. type
  211.   buffarr = array[0..65000] of byte;
  212.  
  213. begin
  214.   if xmax>BLOCK_LENGTH div 2 then xmax:=BLOCK_LENGTH div 2;
  215.   for x:=0 to xmax-1 do
  216.     buffarr(videobuffer)[x+xmax*(buffer[currentblock*BLOCK_LENGTH+(x*2)] div 2)]:=col;
  217. end;
  218.  
  219.  
  220. Procedure SoundInit;
  221. var
  222.   i:integer;
  223. begin
  224.   GlobalVolume:=100;
  225.   NumWavs:=0;
  226.   NumVoices:=0;
  227.   PlayBackActive:=FALSE;
  228.   DriverOpen:=FALSE;
  229.   tidSound:=0;
  230.   Fillchar(Buffer,SizeOf(Buffer),#0);
  231.  
  232.   InitPlaylist;
  233.  
  234.   FillChar(Voice,sizeof(Voice),#0);
  235.  
  236.   if hwndSound=0 then begin
  237.     habSound := WinInitialize (0);
  238.     h_mq := WinCreateMsgQueue(habSound, 0);
  239.     WinRegisterClass(habSound,
  240.                      'Sound Driver',
  241.                      SoundWndProc,
  242.                      CS_SAVEBITS or CS_MOVENOTIFY or CS_CLIPCHILDREN
  243.                      or CS_CLIPSIBLINGS,
  244.                      0);
  245.     hwndSound := WinCreateWindow(HWND_OBJECT,
  246.                                  'Sound Driver',
  247.                                  '',0, 0, 0, 0, 0,
  248.                                  HWND_OBJECT,
  249.                                  HWND_BOTTOM,
  250.                                  0, Nil, Nil);
  251.  
  252.     DosCreateThread ( tidSound,@SoundThread,0, 0, 16384);
  253.     DosSetPriority ( PRTYS_THREAD, (*PRTYC_TIMECRITICAL*)PRTYC_FOREGROUNDSERVER,
  254.                         0, tidSound );
  255.   end;
  256.   If OpenSoundDevice then begin
  257.     DriverOpen:=TRUE;
  258.   end;
  259. end;
  260.  
  261. (*****************************************************************************
  262.  *                     Close SoundDriver                                     *
  263.  ****************************************************************************)
  264.  
  265.  
  266. Procedure SoundDone;
  267. begin
  268.   If PlaybackActive then
  269.     StopPlayBack;
  270.  
  271.   If DriverOpen then begin
  272.   end;
  273.  
  274.   If hwndSound<>0 then
  275.     WinPostMsg( hWndSound, WM_QUIT, 0, 0 );
  276. end;
  277.  
  278. (*****************************************************************************
  279.  *                     InitVoice, Initialize a Voice Structure               *
  280.  ****************************************************************************)
  281.  
  282. Procedure InitVoice(VoiceIdx,WavIDx,Prio:Integer;Loop:Boolean);
  283. begin
  284.   If (WavIdx<NumWavs) and (VoiceIdx<Max_Voices) then
  285.     With Voice[VoiceIdx] do begin
  286.       Playing:=FALSE;
  287.       Mixed:=FALSE;
  288.  
  289.       MyWave:=WavIdx;
  290.  
  291.  
  292.       With WavFiles[MyWave] do begin
  293.         StartAddr:=Ulong(Address);
  294.         Size:=ulSize;
  295.         origSamplesPerSec:=ulSamplesPerSec;
  296.         If Loop then
  297.           LoopPos:=0
  298.         else
  299.           LoopPos:=-1;
  300.       end;
  301.  
  302.       Priority:=Prio; (*Currently Priority is simply = 1/VoiceIdx*)
  303.                       (*Lowest idx = highest priority            *)
  304.       VolumeLeft:=256;
  305.       VolumeRight:=256;
  306.       nowSamplesPerSec:=origSamplesPerSec;
  307.  
  308.       SourceOfs.Fix:=0;
  309.       dx.f:=0;
  310.       dx.i:=1;
  311.  
  312.     end;
  313. end;
  314.  
  315. (*****************************************************************************
  316.  *                     SetVoiceVol, Set Voice Volume                         *
  317.  ****************************************************************************)
  318. Procedure SetVoiceVol(VoiceIdx,LVolume,RVolume:Integer);
  319. begin
  320.   If (VoiceIdx<Max_Voices) then
  321.     With Voice[VoiceIdx] do begin
  322.       if lVolume>100 then
  323.         lVolume:=100;
  324.       if lVolume<0 then
  325.         lVolume:=0;
  326.  
  327.       if RVolume>100 then
  328.         RVolume:=100;
  329.       if RVolume<0 then
  330.         RVolume:=0;
  331.  
  332.  
  333.       VolumeLeft:=(256*LVolume) div 100;
  334.       VolumeRight:=(256*RVolume) div 100;
  335.     end;
  336. end;
  337.  
  338. (*****************************************************************************
  339.  *                     SetVoiceRate, Set Voice Playback Rate                 *
  340.  ****************************************************************************)
  341. Procedure SetVoiceRate(VoiceIdx,Rate:Integer); (*Playback Samples Per Sec*)
  342. var adjust:integer;
  343. begin
  344.   If (VoiceIdx<Max_Voices) then
  345.     if Voice[VoiceIdx].origSamplesPerSec>0 then
  346.       With Voice[VoiceIdx] do begin
  347.         adjust:=0;
  348.         if Rate<50 then
  349.           Rate:=50;
  350.                      (*adjust anything bigger 32755*)
  351.         while (rate>=1 shl (adjust+15)) and (adjust<16) do
  352.           inc(adjust);
  353.  
  354.         nowSamplesPerSec:=Rate;
  355.  
  356.         dx.i:=Rate shr adjust; (*should put a crit-sec here ;-)  *)
  357.         dx.f:=0;
  358.  
  359.         dx.fix:=(dx.fix div origSamplesPerSec) shl adjust; (**)
  360.       end;
  361. end;
  362.  
  363. (*****************************************************************************
  364.  *                     Return, whether Voice is currently playing or not     *
  365.  ****************************************************************************)
  366.  
  367. Function VoiceIsPlaying(VoiceIdx:Integer):boolean;
  368. begin
  369.  If (VoiceIdx<Max_Voices) then
  370.    VoiceIsPlaying:=Voice[VoiceIdx].Playing
  371.  else
  372.    VoiceIsPlaying:=FALSE;
  373. end;
  374.  
  375. (*****************************************************************************
  376.  *                     Start Playing Voice                                   *
  377.  ****************************************************************************)
  378.  
  379. Procedure PlayVoice(VoiceIdx:Integer);
  380. begin
  381.   If (VoiceIdx<Max_Voices) then
  382.     With Voice[VoiceIdx] do begin
  383.       If not Voice[VoiceIdx].Playing then begin
  384.         Playing:=TRUE;
  385.         Inc(NumVoices);
  386.       end;
  387.       SourceOfs.fix:=0;
  388.     end;
  389. end;
  390.  
  391. (*****************************************************************************
  392.  *                     Stop Playing Voice                                    *
  393.  ****************************************************************************)
  394.  
  395. Procedure StopVoice(VoiceIdx:Integer);
  396. begin
  397.   If (VoiceIdx<Max_Voices) then
  398.     With Voice[VoiceIdx] do
  399.       If  Voice[VoiceIdx].Playing then begin
  400.         Playing:=FALSE;
  401.         Dec(NumVoices);
  402.       end;
  403. end;
  404.  
  405.  
  406.  
  407. (****************************************************************************)
  408. Procedure SetGlobalVolume; (*not yet implemented*)
  409. begin
  410. end;
  411.  
  412.  
  413. (****************************************************************************
  414.  * LoadWav       Reads a Wavefile into Memory and Sets up the Info-Structure*
  415.  ***************************************************************************)
  416. Function LoadWav(FileName:String;var WavIdx:Integer):boolean;
  417. var
  418.   hmmioFileHandle:HMMIO;            (* Handle to the audio file handle.    *)
  419.   ulRC:ULONG;                       (* Return code from mmioGetHeader.     *)
  420.   ulBytesRead:ULONG;                (* Returned from mmioGetHeader.        *)
  421.   lMMIOResults:LONG;                (* Results from the MMIO calls.        *)
  422.  
  423.   usSoundFileID:USHORT;
  424.   Error:Boolean;
  425. (*  StrName:PChar;*)
  426.  
  427.   i:integer;
  428. begin
  429.   WavIdx:=NumWavs;
  430.   Inc(NumWavs);
  431.   Error:=FALSE;
  432.   With WavFiles[WavIdx] do begin
  433.     sFileName:=FileName+#0;
  434.  
  435.  
  436. (*    StrName:=@SoundFiles[usSoundFileID].sFileName[1];*)
  437.     hmmioFileHandle:=mmioOpen(@SFileName[1],Nil,MMIO_READ or MMIO_DENYNONE );
  438.  
  439.     Error:=hmmioFileHandle=0;
  440.     if not Error then begin
  441.         (* Get the header information from the waveform file. *)
  442.       ulRC:=mmioGetHeader (hmmioFileHandle,mmAudio_Header,sizeof (MMAUDIOHEADER),ulBytesRead,0,0);
  443.       Error:=ulRc<>MMIO_SUCCESS;
  444.       if not Error then begin  (*Assigning Infos from Audio file*)
  445.         ulSize:=mmAudio_Header.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes;
  446.         ulSamplesPerSec:=mmAudio_Header.mmXWAVHeader.WAVEHeader.ulSamplesPerSec;
  447.         usBitsPerSample:=mmAudio_Header.mmXWAVHeader.WAVEHeader.usBitsPerSample;
  448.  
  449.         (* Get Mem-Buffer for Wav-File*)
  450.         GetMem(Address,ulSize);
  451.  
  452.         FillChar(Address^,ulSize,#0);
  453.  
  454.           (* Move the data from the wave file into the buffer. *)
  455.         lMMIOResults:=mmioRead(hmmioFileHandle,
  456.                                char(Address^),
  457.                                ulSize);
  458.  
  459.         Error:=lMMIOResults=MMIO_ERROR;
  460.  
  461.         if not Error then begin
  462.  
  463.           for i:=0 to ulSize-1 do  (*Prepare Wav for Mixing, shift it *)
  464.             Mem[ulong(Address)+i]:=Mem[ulong(Address)+i] div MIX_VOICES;
  465.         end                        (*Perhaps it's better (but slower),
  466.                                      to shift it after mixing*)
  467.         else
  468.           FreeMem(Address,ulSize);
  469.  
  470.       end;
  471.       lMMIOResults:=mmioClose(hmmioFileHandle,0 );
  472.     end;
  473.  
  474.     If Error then begin
  475.       LoadWav:=FALSE;
  476.       WavIdx:=-1;
  477.       Dec(NumWavs);
  478.     end;
  479.  
  480.   end;
  481. end;
  482.  
  483. (*****************************************************************************
  484.  *                     InitPlaylist                                          *
  485.  ****************************************************************************)
  486.  
  487. Procedure InitPlaylist;
  488. var
  489.   block           :integer;
  490.  
  491. begin
  492.   FillChar(Playlist,sizeof(Playlist),#0);
  493.  
  494. (*  blocklen:=SizeOf(Buffer) div round(MIX_BUFFER_SECS*BLOCKS_PER_SEC);*)
  495.  
  496. (*  PlayList_Entries:=;*)
  497.  
  498.   for block:=0 to NUMBER_OF_BLOCKS-1 do begin
  499.     With playlist[block*3] do begin
  500. {      ulCommand:=MESSAGE_OPERATION;   (*Send Message to WindowProc*)
  501.  
  502.       ulOperandTwo:=block; (*signals next played data_block*)}
  503.       ulCommand:=CUEPOINT_OPERATION;
  504. (*      ulCommand:=NOP_OPERATION;*)
  505.       ulOperandOne:=Block;          (*Cuepoint at 3/4 played buffer*)
  506.       ulOperandTwo:=(3000*CUE_POS) div (BLOCKS_PER_SEC*100); (*MMTIME Unit = 1/3000 sec*)
  507.     end;
  508.  
  509.     With playlist[block*3 + 1] do begin
  510.       ulCommand:=DATA_OPERATION; (*Datablock, indicating what to play*)
  511.  
  512.       ulOperandOne:=Ulong(@Buffer)+(Block*BLOCK_LENGTH); (*Address of Buffer*)
  513.       ulOperandTwo:=BLOCK_LENGTH;
  514.     end;
  515.  
  516.     With playlist[block*3 + 2] do begin
  517.       ulCommand:=BRANCH_OPERATION;
  518.       If block=NUMBER_OF_BLOCKS-1 then
  519.         ulOperandTwo:=0            (*Jump to Start of List*)
  520.       else
  521.         ulOperandTwo:=(block+1)*3; (*Jump to Next Block (redundant)     *)
  522.                                    (*I hope, this will enable the next  *)
  523.                                    (*Data block to be edited while this *)
  524.                                    (*Block is played.*)
  525.     end;
  526.  
  527.   end;
  528. end;
  529.  
  530. (*****************************************************************************
  531.  *                     Error Handling                                        *
  532.  ****************************************************************************)
  533.  
  534.  
  535. Procedure MCIERROR;
  536. var s:string;
  537. begin
  538.  if ulError<>0 then begin
  539.    mciGetErrorString(ulError,@s[1],255 );
  540.    SetPSZLen(s);
  541. (*   ShowMessageBox (s);*)
  542.    s:=s;
  543. (*   ShowMessageBox ('Error '+Strint(ulError));*)
  544.  end;
  545. end;
  546.  
  547.  
  548.  
  549.  
  550. (*******************************************************)
  551. (*                    SoundThread                      *)
  552. (*******************************************************)
  553. Procedure SoundThread(param1:Ulong);
  554. begin
  555.  
  556.   while WinGetMsg ( habSound, q_msg, 0, 0, 0 ) do
  557.     WinDispatchMsg ( habSound, q_msg );
  558.  
  559.   WinDestroyWindow( hwndSound );
  560.   WinDestroyMsgQueue( h_mq );
  561.   WinTerminate (habSound);
  562. end;
  563.  
  564. (*******************************************************)
  565. (*                SoundWndProc                         *)
  566. (*******************************************************)
  567.  
  568. function SoundWndProc(Window: HWnd; Msg: ULong;
  569.                                    Mp1,Mp2: MParam): MResult;
  570. begin
  571. (*  if msg=MM_MCIPLAYLISTMESSAGE then
  572.     MixVoices(mp2)*)
  573.   if msg=MM_MCICUEPOINT then begin
  574.     if SoundWatchDog-LastWatchDog>=-10 then begin
  575.       if LastWatchDog<SoundWatchDog then
  576.         LastWatchDog:=SoundWatchDog;
  577.       Inc(LastWatchDog);
  578.  
  579.       MixVoices(Mp2);
  580.     end
  581.     else
  582.       PausePlayback;
  583.   end
  584.   else begin
  585.     SoundWndProc:=WinDefWindowProc ( Window, msg, mp1, mp2 );
  586.     exit;
  587.   end;
  588.   SoundWndProc:=0;
  589. end;
  590.  
  591.  
  592. (*************************************************************************)
  593. (* MixVoices, Mixes Voices for the next Data-Block                       *)
  594. (*            This Routine uses Intel Assembler to Speed up Mixing a bit *)
  595. (*                                                                       *)
  596. (*            The Assembler part adds each Voice, frequency-scaled to    *)
  597. (*            the Data-Block. There are (currently) two almost identical *)
  598. (*            parts. One applies the volume setting (scaling down) and   *)
  599. (*            the other does not (=full volume).                         *)
  600. (*                                                                       *)
  601. (*            Frequency Scaling is done with fixed point-numbers:        *)
  602. (*            The Field OfsTemp (fixed-point) is incremented by the      *)
  603. (*            pre-calculated field DXTEMP (delta-x). The Integer-Part of *)
  604. (*            OfsTemp ([OfsTemp+2]) is taken for the address calculation *)
  605. (*                                                                       *)
  606. (*************************************************************************)
  607. var
  608.   DxTemp,                      (*Global Variables speed up Considerably*)
  609.   LoopTemp,OfsTemp:Ulong;      (*because of direct addressing.         *)
  610.                                (*(Local Vars do [bp+idx])              *)
  611.   VolTemp,SizeTemp:SmallWord;
  612.   EndAddr:Ulong;
  613.  
  614. const
  615.   PreviousBlock:integer=1;
  616.  
  617. Procedure MixVoices(PrevBlock:Integer);
  618. var
  619.   VoiceIdx:Integer;
  620.   MixNum:Integer;
  621.   BlockNum:Integer;
  622.   SourceAddr,BuffAddr,DestAddr:Ulong;
  623.   p:PChar;
  624.  
  625. begin
  626.   PrevBlock:=PreviousBlock;
  627.   If PreviousBlock=1 then
  628.     PreviousBlock:=0
  629.   else
  630.     PreviousBlock:=1;
  631.  
  632.   currentblock:=PrevBlock; (*global (debugging) info *)
  633.  
  634.   If PrevBlock=NUMBER_OF_BLOCKS-1 then
  635.     BlockNum:=0
  636.   else
  637.     BlockNum:=PrevBlock+1;
  638.  
  639.   BuffAddr:=Ulong(@Buffer)+(BlockNum*BLOCK_LENGTH);
  640.   p:=Pointer(BuffAddr);
  641.  
  642.   MixNum:=0;
  643.   VoiceIdx:=0;
  644.  
  645.   FillChar(p^,BLOCK_LENGTH,#0);
  646.  
  647.   While (MixNum<Mix_Voices) and (VoiceIdx<Max_Voices) do begin
  648.     With Voice[VoiceIdx] do begin
  649.       If Playing then begin
  650. (*        If VoiceIdx=2 then begin
  651.           DxTemp:=1;
  652.         end;*)
  653.         DestAddr:=BuffAddr;
  654.         EndAddr:=BuffAddr+BLOCK_LENGTH;
  655.  
  656.         OfsTemp:=SourceOfs.Fix;
  657.         SourceAddr:=StartAddr;
  658.         DxTemp:=dx.Fix;
  659.         SizeTemp:=Size;
  660.         LoopTemp:=LoopPos;
  661.         VolTemp:=VolumeLeft;
  662.  
  663.         If VolTemp=256 then begin (*Full Volume, no scaling*)
  664.           (*While DestAddr<EndAddr do begin*)
  665.           asm
  666.             mov edi,[DestAddr]
  667.             mov ebx,[DxTemp]
  668.             mov dx,[SizeTemp]
  669.  
  670.           @loop1:
  671.             cmp edi,[EndAddr]
  672.             jge @endloop
  673.  
  674.             mov esi,[SourceAddr]
  675.             movsx ecx,Word Ptr [OfsTemp+2]
  676.             add esi,ecx
  677.             (*SourceAddr:=StartAddr+SourceOfs.i;*)
  678.  
  679.             mov al,[esi]
  680.             add [edi],al
  681.             (*Inc(Mem[DestAddr],Mem[StartAddr+SourceOfs.i]);*)
  682.             inc edi
  683.             (*Inc(DestAddr);*)
  684.             add [OfsTemp],ebx
  685.             (*Inc(SourceOfs.Fix,dx.fix);*)
  686.             cmp Word Ptr [OfsTemp+2],dx
  687.             (*If SourceOfs.i>=Size then begin*)
  688.             jl @loop1
  689.             mov ecx,[LoopTemp]
  690.             cmp ecx,-1
  691.             (*If LoopPos<>-1 then begin*)
  692.             je @endloop
  693.             mov cx,[SizeTemp]
  694.             sub cx,Word Ptr [LoopTemp]
  695.             sub Word Ptr [OfsTemp+2],cx
  696.             (*Dec( SourceOfs.i,(SizeTemp-LoopTemp)*)
  697.             cmp Word Ptr [OfsTemp+2],dx
  698.             (*just to Avoid gpfs if delta gets bigger than sample length*)
  699.             jl @loop1
  700.             mov [OfsTemp],0
  701.  
  702.             jmp @loop1
  703.           @endloop:
  704.             mov [DestAddr],edi
  705.           end;
  706.         end
  707.         else begin
  708.  
  709.           asm  (*With Volume Scaling*)
  710.             mov edi,[DestAddr]
  711.             mov ebx,[DxTemp]
  712.             mov dx,[SizeTemp]
  713.  
  714.           @loop1:
  715.             cmp edi,[EndAddr]
  716.             jge @endloop
  717.  
  718.             mov esi,[SourceAddr]
  719.             movsx ecx,Word Ptr [OfsTemp+2]
  720.             add esi,ecx
  721.             (*SourceAddr:=StartAddr+SourceOfs.i;*)
  722.  
  723.             xor ax,ax
  724.             mov al,[esi]
  725.  
  726.             db 66h,0Fh,0AFh,05h
  727.             dd VolTemp
  728.             (*imul ax,[VolTemp]*) (* (ax*volume) div 256 *)
  729.             shr ax,8
  730.  
  731.             add [edi],al
  732.             (*Inc(Mem[DestAddr],Mem[StartAddr+SourceOfs.i]);*)
  733.             inc edi
  734.             (*Inc(DestAddr);*)
  735.             add [OfsTemp],ebx
  736.             (*Inc(SourceOfs.Fix,dx.fix);*)
  737.             cmp Word Ptr [OfsTemp+2],dx
  738.             (*If SourceOfs.i>=Size then begin*)
  739.             jl @loop1
  740.             mov ecx,[LoopTemp]
  741.             cmp ecx,-1
  742.             (*If LoopPos<>-1 then begin*)
  743.             je @endloop
  744.             mov cx,[SizeTemp]
  745.             sub cx,Word Ptr [LoopTemp]
  746.             sub Word Ptr [OfsTemp+2],cx
  747.             (*Dec( SourceOfs.i,(SizeTemp-LoopTemp)*)
  748.             cmp Word Ptr [OfsTemp+2],dx
  749.             (*just to Avoid gpfs if delta gets bigger than sample length*)
  750.             jl @loop1
  751.             mov [OfsTemp],0
  752.  
  753.             jmp @loop1
  754.           @endloop:
  755.             mov [DestAddr],edi
  756.           end;
  757.         end;
  758.         Voice[VoiceIdx].SourceOfs.Fix:=OfsTemp;
  759.  
  760.         If DestAddr<>EndAddr then begin
  761.           Playing:=false;
  762.           DestAddr:=EndAddr;
  763.         end;
  764.  
  765.         Inc(MixNum);
  766.       end;
  767.     end;
  768.     Inc(VoiceIdx);
  769.   end;
  770. end;
  771.  
  772.  
  773. (******************************************************************************
  774.  *                     OpenSoundDevice                                        *
  775.  *****************************************************************************)
  776. Function OpenSoundDevice:boolean;
  777. const
  778.   ulOpenFlags:ULONG = MCI_WAIT         or MCI_OPEN_PLAYLIST or
  779.                       MCI_OPEN_TYPE_ID or MCI_OPEN_SHAREABLE;
  780.  
  781. begin
  782.   (* Open the correct waveform device for the waves with MCI_OPEN *)
  783.  
  784.   mciOpenParameters.pszDeviceType := PSZ(MCI_DEVTYPE_WAVEFORM_AUDIO);
  785. (*  mciOpenParameters.pszDeviceType:= @DevType[1];*)
  786.  
  787.   (* The address of the buffer containing the waveform file. *)
  788.   mciOpenParameters.pszElementName := PSZ(@PlayList[0]);
  789.  
  790.   mciOpenParameters.hwndCallback  := hwndSound(*HWND(0)*);
  791.   mciOpenParameters.pszAlias      := Nil;
  792.  
  793.   (* Open the waveform file in the playlist mode. *)
  794.   ulError:=mciSendCommand(0,                  (* We don't know the device yet.        *)
  795.                           MCI_OPEN,           (* MCI message.                         *)
  796.                           ulOpenFlags,        (* Flags for the MCI message.           *)
  797.                           mciOpenParameters,  (* Parameters for the message.          *)
  798.                           0 );                (* Parameter for notify message.        *)
  799.   (* save device ID *)
  800.   SoundDevice.usSoundDeviceID := mciOpenParameters.usDeviceID;
  801.  
  802.   OpenSoundDevice:=ulError=0;
  803. end;
  804.  
  805.  
  806. (*****************************************************************************
  807.  *                     AdjustSoundDevice                                     *
  808.  ****************************************************************************)
  809. Procedure AdjustSoundDevice;
  810. var
  811.   mwspWaveFormParameters:MCI_WAVE_SET_PARMS; (* Waveform parameters.       *)
  812. begin
  813.   (* Fill the structure with zeros. *)
  814.   Fillchar(mwspWaveFormParameters,sizeof(mwspWaveFormParameters),#0);
  815.  
  816.   SoundDevice.ulSamplesPerSec := MIX_RATE;
  817.   SoundDevice.usBitsPerSample := MIX_BITS;
  818.  
  819.   (* copy samps/sec *)
  820.   mwspWaveFormParameters.ulSamplesPerSec := SoundDevice.ulSamplesPerSec;
  821.   mwspWaveFormParameters.usBitsPerSample := SoundDevice.usBitsPerSample;
  822.   mwspWaveFormParameters.ulAudio         := MCI_SET_AUDIO_ALL;
  823.  
  824.   ulError:=mciSendCommand(SoundDevice.usSoundDeviceID, (* Device to play the waves.     *)
  825.                           MCI_SET,                     (* MCI message.                  *)
  826.                           MCI_WAIT or
  827.                           MCI_WAVE_SET_SAMPLESPERSEC or(* Flags for the MCI message.    *)
  828.                           MCI_WAVE_SET_BITSPERSAMPLE ,
  829.                           mwspWaveFormParameters,      (* Parameters for the message.   *)
  830.                           0);                          (* Parameter for notify message. *)
  831.  
  832.   MCIERROR;
  833. end;
  834.  
  835. (*****************************************************************************
  836.  *   PausePlayBack. Pauses the Sound playback.
  837.  ****************************************************************************)
  838. Function PausePlayback:boolean;
  839. var ulError:ULONG;
  840. begin
  841.   PausePlayBack:=FALSE;
  842.   if PlayBackActive then begin
  843.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID, (* Device to play the waves.     *)
  844.                             MCI_PAUSE,                   (* MCI message.                  *)
  845.                             0,                           (* Flags for the MCI message.    *)
  846.                             mciOpenParameters,           (* Parameters for the message.   *)
  847.                             0);              (* Parameter for notify message. *)
  848.     If ulError=0 then begin
  849.       PausePlayBack:=TRUE;
  850.       SoundPaused:=TRUE;
  851.     end;
  852.   end;
  853. end;
  854.  
  855. (*****************************************************************************
  856.  *   ResumePlayBack. Resumes the Sound playback.
  857.  ****************************************************************************)
  858. Function ResumePlayback:boolean;
  859. var ulError:ULONG;
  860. begin
  861.   ResumePlayBack:=FALSE;
  862.   if PlayBackActive and SoundPaused then begin
  863.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID, (* Device to play the waves.     *)
  864.                             MCI_RESUME,                  (* MCI message.                  *)
  865.                             0,                           (* Flags for the MCI message.    *)
  866.                             mciOpenParameters,           (* Parameters for the message.   *)
  867.                             0);              (* Parameter for notify message. *)
  868.     If ulError=0 then begin
  869.       ResumePlayBack:=TRUE;
  870.       SoundPaused:=FALSE;
  871.     end;
  872.   end;
  873. end;
  874.  
  875. (*****************************************************************************
  876.  *   StartPlayBack. Starts Playing the Playlist (&Mixing)                    *
  877.  ****************************************************************************)
  878. Function StartPlayback:boolean;
  879. begin
  880.   StartPlayBack:=FALSE;
  881.   If DriverOpen then begin
  882.     AdjustSoundDevice;
  883.     PlayBackActive:=TRUE;
  884.     SoundPaused:=FALSE;
  885.  
  886.                 (*Pre-Mix the First Block (signal for previous=last block)*)
  887.     WinPostMsg(hwndSound,MM_MCICUEPOINT,
  888.                              NUMBER_OF_BLOCKS-1,NUMBER_OF_BLOCKS-1);
  889.     (*Stop Previous Playing*)
  890.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID, (* Device to play the waves.     *)
  891.                             MCI_STOP,                    (* MCI message.                  *)
  892.                             MCI_WAIT,                    (* Flags for the MCI message.    *)
  893.                             mciOpenParameters,           (* Parameters for the message.   *)
  894.                             0);              (* Parameter for notify message. *)
  895.  
  896.     (* rewind sound *)
  897.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID,
  898.                             MCI_SEEK,
  899.                             MCI_TO_START,
  900.                             mciOpenParameters,
  901.                             0);
  902.     (* play sound *)
  903.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID,
  904.                             MCI_PLAY,
  905.                             (*MCI_WAIT*)0,
  906.                             mciOpenParameters,
  907.                             0);
  908.     If ulError=0 then
  909.       StartPlayBack:=TRUE;
  910.   end
  911.   else
  912.     PlayBackActive:=FALSE;
  913. end;
  914.  
  915. (*****************************************************************************
  916.  *   StopPlayBack. Stops Playing the Playlist (& Mixing)                     *
  917.  ****************************************************************************)
  918. Function StopPlayback:boolean;
  919. begin
  920.   StopPlayBack:=FALSE;
  921.   PlayBackActive:=FALSE;
  922.   If DriverOpen then begin
  923.     (*Stop Previous Playing*)
  924.     SoundPaused:=FALSE;
  925.     ulError:=mciSendCommand(SoundDevice.usSoundDeviceID, (* Device to play the waves.     *)
  926.                             MCI_STOP,                    (* MCI message.                  *)
  927.                             MCI_WAIT,                    (* Flags for the MCI message.    *)
  928.                             mciOpenParameters,           (* Parameters for the message.   *)
  929.                             0);              (* Parameter for notify message. *)
  930.     If ulError=0 then
  931.       StopPlayBack:=TRUE;
  932.   end;
  933. end;
  934.  
  935. (******************************************************************************)
  936. (*                          CloseSoundDevice                                  *)
  937. (******************************************************************************)
  938. Procedure CloseSoundDevice;
  939. var
  940.   usCounter:SmallInt;
  941.   NilPtr:Pointer;
  942. begin
  943.   NilPtr:=Nil;
  944.  
  945.   (* close sound device *)
  946.   ulError:=mciSendCommand(SoundDevice.usSoundDeviceID,  (* Device to play the chimes.    *)
  947.                           MCI_CLOSE,                    (* MCI message.                  *)
  948.                           MCI_WAIT,                     (* Flags for the MCI message.    *)
  949.                           NilPtr^,                      (* Parameters for the message.   *)
  950.                           0);                           (* Parameter for notify message. *)
  951.   MCIERROR;
  952. end;
  953.  
  954.  
  955. (*******************************************************)
  956. (*                 ShowMessageBox                      *)
  957. (*******************************************************)
  958. Procedure ShowMessageBox (message:string);
  959. begin
  960.   if message[length(message)]<>#0 then
  961.     message:=message+#0;
  962.   WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
  963.                  @message,'Sound Driver',0,MB_OK);
  964. end;
  965.  
  966. begin
  967.   GlobalVolume:=100;
  968.   NumWavs:=0;
  969.   NumVoices:=0;
  970.   PlayBackActive:=FALSE;
  971.   DriverOpen:=FALSE;
  972.   tidSound:=0;
  973.   FillChar(Voice,sizeof(Voice),#0);
  974. end.
  975.