[Back to COMM SWAG index] [Back to Main SWAG index] [Original]
program SBDial;
{ This program demonstrates how to use an Adlib or SoundBlaster card
for automatically dialing a phone number. You just have to hold the
microphone close to the speakers and run the program. If you have
implemented your own address list you no longer have to read the
phone number from the screen and dial it on the phone. }
{ This program was written by Jes R. Klinke, if you include it in your
own creations a credit would be nice. }
uses
Crt;
const
NoteLength = 100; { Length of each note in ms }
NoteDelay = 50; { Delay between two notes }
LongDelay = 1000; { Long delay between two notes indicated by
a hyphen in number }
{ Constants for MiscFlags }
mfAmpMod = $80; { Amplitude modulation }
mfVibrato = $40;
mfShortNote = $20; { No sustain, jump directly from decay to release }
mfShorten = $10; { Shorten the note as its pitch rises }
{ Constants for ScalingLevel }
{ Causes output levels to decrease as the frequency rises }
slNo = $00; { no change }
sl15db = $40; { 1,5 db per octave }
sl3db = $80; { 3 db per octave }
sl6db = $C0; { 6 db per octave }
{ Sets specified FM register }
procedure SetSBReg(Address, Value: Byte); assembler;
asm
MOV DX,0388h
MOV AL,Address
OUT DX,AL
MOV CX,6
@@0:IN AL,DX
LOOP @@0
INC DX
MOV AL,Value
OUT DX,AL
DEC DX
MOV CX,35
@@1:IN AL,DX
LOOP @@1
end;
{ Reads then FM status register }
function GetSBStatus: Byte; assembler;
asm
MOV DX,0388h
IN AL,DX
end;
{ Resets the FM chips }
procedure ResetSB;
var
Adr: Byte;
begin
for Adr := 0 to $F5 do
SetSBReg(Adr, 0);
end;
{ Calculates offset of the control byte for the given channel and operator }
function OperatorOffset(Channel, Operator: Byte): Byte;
begin
OperatorOffset := Operator * 3 + Channel mod 3 + (Channel div 3) * 8;
end;
{ Sets miscellaneous flags and parameters }
procedure SetMiscParam(Channel, Operator, MiscFlag, FreqvFaktor: Byte);
begin
SetSBReg($20 + OperatorOffset(Channel, Operator), MiscFlag or FreqvFaktor);
end;
{ Sets volume and scaling level }
procedure SetVolume(Channel, Operator, ScalingLevel, Volume: Byte);
begin
SetSBReg($40 + OperatorOffset(Channel, Operator), ScalingLevel or Volume);
end;
{ Sets attack, decay, sustain and release rates }
procedure SetADSR(Channel, Operator, Attack, Decay, Sustain, Release: Byte);
begin
SetSBReg($60 + OperatorOffset(Channel, Operator), Attack shl 4 or Decay);
SetSBReg($80 + OperatorOffset(Channel, Operator), Sustain shl 4 or Release);
end;
{ Sets feedback 0..7, 0 being no feedback, 7 the most }
{ Also sets whether operator 0 modulates operator 2 or
they produce sound individually }
procedure SetFeedback(Channel, Feedback: Byte; Separate: Boolean);
begin
SetSBReg($C0 + Channel, Feedback shl 1 or Byte(Separate));
end;
{ Starts playing the note given by Octave and Freq }
procedure PlayNote(Channel, Octave: Byte; Freq: Word);
begin
SetSBReg($A0 + Channel, Lo(Freq));
SetSBReg($B0 + Channel, $20 or Octave shl 2 or Hi(Freq) and 3);
end;
{ Stops playing }
procedure StopNote(Channel, Octave: Byte; Freq: Word);
begin
SetSBReg($A0 + Channel, Lo(Freq));
SetSBReg($B0 + Channel, Octave shl 2 or Hi(Freq) and 3);
end;
{ Detect if FM-chips i present }
function DetectSB: Boolean;
var
Result1, Result2: Byte;
begin
SetSBReg($4, $60);
SetSBReg($4, $80);
Result1 := GetSBStatus;
SetSBReg($2, $FF);
SetSBReg($4, $21);
Delay (10);
Result2 := GetSBStatus;
DetectSB := ((Result1 and $E0) = 0) and ((Result2 and $E0) = $C0);
end;
{ Dials the specified phone number }
procedure Dial(const No: string);
const
{ Frequency combinations for the keys on a phone }
Tone: array [0..11, 0..1] of Byte = (
(3, 1),
(0, 0),
(0, 1),
(0, 2),
(1, 0),
(1, 1),
(1, 2),
(2, 0),
(2, 1),
(2, 2),
(3, 0),
(3, 2));
{ Low frequency of the pair }
LoFreq: array [0..3] of
record
O: Byte;
F: Word;
end = (
(O: 5; F: 457),
(O: 5; F: 505),
(O: 5; F: 558),
(O: 5; F: 617));
{ High frequency of the pair }
HiFreq: array [0..2] of
record
O: Byte;
F: Word;
end = (
(O: 6; F: 396),
(O: 6; F: 438),
(O: 6; F: 484));
var
I, ToneNo: Integer;
begin
SetMiscParam(0, 0, 0, 1);
SetVolume(0, 0, 0, 8);
SetADSR(0, 0, 15, 1, 7, 15);
SetFeedBack(0, 0, True);
SetMiscParam(1, 0, 0, 1);
SetVolume(1, 0, 0, 8);
SetADSR(1, 0, 15, 1, 7, 15);
SetFeedBack(1, 0, True);
SetMiscParam(0, 1, 0, 1);
SetVolume(0, 1, 0, 8);
SetADSR(0, 1, 15, 1, 7, 15);
SetMiscParam(1, 1, 0, 1);
SetVolume(1, 1, 0, 8);
SetADSR(1, 1, 15, 1, 7, 15);
for I := 1 to Length(No) do
begin
if No[I] in ['0'..'9', '*', '#'] then
begin
{ Determine the number of the key }
if No[I] = '*' then
ToneNo := 10
else if No[I] = '#' then
ToneNo := 11
else
ToneNo := Byte(No[I]) - Byte('0');
{ Play the two frequencies in channel 0 and 1}
PlayNote(0, LoFreq[Tone[ToneNo, 0]].O, LoFreq[Tone[ToneNo, 0]].F);
PlayNote(1, HiFreq[Tone[ToneNo, 1]].O, HiFreq[Tone[ToneNo, 1]].F);
Delay(NoteLength);
{ The contral ought to have had enough time to recognize the keypress }
StopNote(0, LoFreq[Tone[ToneNo, 0]].O, LoFreq[Tone[ToneNo, 0]].F);
StopNote(1, HiFreq[Tone[ToneNo, 1]].O, HiFreq[Tone[ToneNo, 1]].F);
Delay(NoteDelay);
end;
{ a hyphen in the number indicates a longer delay between the keypresses }
if No[I] = '-' then
Delay(LongDelay);
end;
end;
begin
if not DetectSB then
begin
WriteLn('This program requires an Adlib or compatible soundcard');
Halt;
end;
Dial('875 8133');
end.
[Back to COMM SWAG index] [Back to Main SWAG index] [Original]