{ SPX Library Version 2.0 Copyright 1993 Scott D. Ramsay } The SPX_SND units it the direct sound unit. It allows the playing of digital sound through a Sound Blaster compatible card, a DAC device on the LPT port (Covox), or the PC speaker. It also maintains eight clock timers to use for game precision. s_clk : array[0..3] of word; { slow counters } f_clk : array[0..3] of word; { fase counters } The unit takes control of the computer's clock interrupt and allows the changing of its interrupt frequency. From its regular 18.2 times per second. The slow counters always count down at 18.2 times a second. Regardless of the clock interrupt's rate. So, for example, the loop below will wait for 5 seconds: s_clk[0] := 5*18; repeat until s_clk[0]=0; Notice that there is nothing in the repeat loop. The counters are automatically decremented. The counter also do not roll over. They will decrement until they reach zero then stop. The fast counters count down according to the clock rate. The clock rate can be changed by the following procedure: procedure setrate(cycles:word); The variable cycles indicates the number of cycles(interrupts) to generate per second. So setrate(18); will restore the clock to its original rate. Setrate(1000); will generate an interrupt 1000 times per second. Warning some slow computers (8086/286) can not handle high clock rates. Also conflicts may occur if running under MS Windows or OS/2. Here's another example: setrate(2048); { set the clock rate } f_clk[0] := 4096; repeat until f_clk[0]=0; The above example will loop for 2 seconds. A nice thing about the counters is that you can use them to regulate the speed of your program/game. Lets make a VERY rough example. Lets suppose you wrote a game that you want each 'frame' or pass to take 1 second. Your game loop would look something like this: repeat s_clk[0] := 18; { grab user inputs } { do game calcuations } { setup objects for display } { update visual screen } while s_clk[0]<>0 do; until game_over; If one pass of the loop is faster than one second, then the game will wait in the while loop until a second it up. Now depending on the speed of the computer, the number of calcuations, speed of displaying the sprites, the wait time may vary. But the above loop will always wait at MOST one second. Now if one pass takes longer than a second then the counter is already reached zero so there is no delay. So the loop is running at maximum speed. Now the event loops for games tend to run alot faster than 1 sec. So we would use a fast clock counter. setrate(3000); repeat f_clk[0] := 80; { grab user inputs } { do game calcuations } { setup objects for display } { update visual screen } while f_clk[0]<>0 do; until game_over; Now were all set. We can even get fancy. We can even automatically adjust the rate according to the cabilities of the machine; var crate : word; setrate(3000); crate := 80; repeat f_clk[0] := crate; { grab user inputs } { do game calcuations } { setup objects for display } { update visual screen } if (crate>0) and (f_clk[0]<10) then dec(crate); while f_clk[0]<>0 do; until game_over; Now if the computer can't keep up with the rate (The clock comes close to timing out). The rate is speeded up. The unit uses the variable clock speeds for the playing of the audio. To play an audio segment, the unit sets the clock rate to the sample rate of the sound. Then at each interrupt it outputs a sample byte to directed sound port. Here are some ports you can use. $42 - PC speaker $378 - LPT1 sound devices such as Covox $210-$260 - Sound Blaster compatible cards Here's how to play an 8khz raw sound file: Uses SPX_SND; var sound : Tsound; begin setrate(8192); { Set clock to sound sample } sound.init('Mysound.raw',$42,false); { load the sound, use PC speaker } sound.play(true); { play the sound } repeat until not playing; { wait for completion } sound.done; { do cleanup } end. NOTES: This unit will not work in protected mode because of changing the clock rate. As a result the the f_clk counters will act as s_clk counters. Such in a Windows DOS box. etc. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ GLOBAL VARIABLES: cs: Can stop flag, FALSE - if the current audio playing does not want to be interupted playing: TRUE - if a sound is currently playing f_clk: Fast clock counters s_clk: Slow clock counters rate: Current fast clock rate cntime: Reserved counter, Do not modify -------------------------------------------------------- f_userclk, s_userclk : userproc; F_USERCLK and S_USERCLK allows you to add your own custom routines at each clock interrupt. Since these procedures are called at every interrupt you have to avoid a few things: 1. You routine should be fast as possible. Since it will be called many times. if it does to many calculations, it will slow the program down. 2. Do not call any interupts. You are already in one. 3. Avoid disk access. It may or may not work. Don't try it! 4. Your routine must be declared as a far procedure with no parameters. Example: Uses spx_tim; var l,s : longint; procedure MyUserClk; far; begin inc(l); end; begin l := 0; s_userclk := MyUserClk; readln; s := l; writeln('My function was called ',s,' times while waiting'); end. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ type Psound = ^Tsound; Tsound = object Base sound object. Used for playing digitized sound. VARIABLES: sblk:pointer Buffer to the sound data; size:word Size of sound buffer; sport:word Port address for sound; sb_play:boolean Set to TRUE if (sport) is a sound blaster port METHODS: --------------------------------------------------- constructor tsound.init(sndfile:string;prt:word;_sb:boolean); Sets up the sound object. By loading the the sound file SNDFILE: DOS file name of raw sound file; PRT: Sound port to use for the object; _SB: Set to TRUE if PRT is a sound blaster port --------------------------------------------------- function tsound.loadsnd(sndfile:string;prt:word;_sb:boolean):boolean; virtual; Load a raw sound file into the object. SNDFILE: DOS file name of the raw sound file; PRT: Sound port to use for the object; _SB: Set to TRUE if PRT is a Sound Blaster port --------------------------------------------------- function tsound.filesnd(var fil:file;bsize,prt:word;_sb:boolean):boolean; virtual; Loads a data from an open file. FIL: File that contains sound data; BSIZE: Size of sound data in file; PRT: Sound port to use for the object; _SB: Set to TRUE if PRT is a Sound Blaster port NOTE: Does not close file --------------------------------------------------- procedure tsound.cleansnd; virtual; Deallocates heap memory space for the sound. --------------------------------------------------- procedure tsound.play(canstop:boolean); virtual; Plays the sound. CANSTOP: Set to TRUE is allow other sounds to interrupt its playing. --------------------------------------------------- procedure tsound.stop; virtual; Stop the playing of the sound. --------------------------------------------------- destructor tsound.done; virtual; Preforms and deallocation of the object; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ type PEmsSound = ^TEmsSound; TEmsSound = object(Tsound) Expanded memory sound object. Used for playing digitized sound. Stores the sound in expanded memory. VARIABLES: EMSseg: Segment address of EMS window handle: Handle to the ems memory block EMSok: Status of the EMS block, TRUE if okay METHODS: --------------------------------------------------- constructor TEmsSound.init(sndfile:string;prt:word;_sb:boolean); Sets up the sound object. By loading the the sound file SNDFILE: DOS file name of raw sound file; PRT: Sound port to use for the object; _SB: Set to TRUE if PRT is a sound blaster port --------------------------------------------------- function TEmsSound.loadsnd(sndfile:string;prt:word;_sb:boolean):boolean; virtual; Load a raw sound file into the object. SNDFILE: DOS file name of the raw sound file; PRT: Sound port to use for the object; _SB: Set to TRUE if PRT is a Sound Blaster port --------------------------------------------------- procedure TEmsSound.cleansnd; virtual; Deallocates expanded memory space for the sound. --------------------------------------------------- procedure TEmsSound.play(canstop:boolean); virtual; Plays the sound. CANSTOP: Set to TRUE is allow other sounds to interrupt its playing. --------------------------------------------------- destructor TEmsSound.done; virtual; Preforms and deallocation of the object. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ function SBFindBase:word; Searches for a Sound Blaster compatible card. If found, returns the port address of the card. Returns 0 if not found. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ function SBReset(BaseAddr : word) : boolean; Resets the sound card. Returns TRUE if successful. BASEADDR: Port address of sound card ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure globalstop; Stops playback of sound. Note: Same as Tsound.stop and TEmsSound.stop ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure setrate(cycles:word); Changes the clock rate of the computer. CYCLES: Number of interrupts to generate per second ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure wait(seconds,which:integer); Wait for a specified number of seconds. SECONDS: Number of seconds to wait; WHICH: Index number of slow clock counter to use. (0..3) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ