home *** CD-ROM | disk | FTP | other *** search
/ Chip: Special Sound & MIDI / Chip-Special_Sound-und-Midi-auf-dem-PC.bin / dosprog / voc.pas < prev    next >
Pascal/Delphi Source File  |  1993-11-21  |  29KB  |  774 lines

  1. (***C*H*I*P***S*P*E*C*I*A*L**************************************************)
  2. (*                                                                          *)
  3. (*     Unit VOC: Sound-Wiedergabe über den digitalen Audiokanal per DMA     *)
  4. (*                                                                          *)
  5. (*   (c) 1993 Rainer Reusch & Vogel Verlag München                          *)
  6. (*                                                                          *)
  7. (*   Turbo Pascal 7.0                                                       *)
  8. (*                                                                          *)
  9. (***V0.1*********************************************************************)
  10.  
  11. {
  12. Die Unit bietet die Möglichkeit der Wiedergabe von Samples per DMA.
  13. Während der Wiedergabe wird das Programm also nicht angehalten.
  14.  
  15. Die Unit erwartet die Environmentvariable "BLASTER" (zu setzen mit dem SET-
  16. Befehl), damit eine Sound-Karte erkannt wird. Beispiel:
  17.  
  18. SET BLASTER=A220 I7 D1 T1
  19.  
  20. Aus dieser Variablen ermittelt die Unit die Basisportadresse, den Hardware-
  21. Interrupt und den DMA-Kanal.
  22.  
  23. Die einzelnen Schritte für die Anwendung der Unit:
  24. - SoundBoardInstalled   (optional: Abfrage, ob Sound-Karte vorhanden)
  25. - SetVolume             (optional an jeder Programmstelle: Lautstärkeeinst.)
  26. - LoadVOCFile           (ein oder mehrere VOC-Sound-Dateien laden)
  27. - GetMemVOCHandle       (Alternative. Daten müssen manuell geladen werden.)
  28. -   PlaySound           (Sound-Wiedergabe, beliebig oft)
  29. -   PauseSound          (optional: Wiedergabe anhalten)
  30. -   RestartSound        (optional: angehaltene Wiedergabe fortsetzen)
  31. -   StopSound           (optional: Wiedergabe abbrechen)
  32. -   SoundPlaying        (optional: Abfrage, ob Wiedergabe gerade läuft
  33. - UnloadVOCFile         (geladene Samples aus dem Speicher entfernen)
  34.  
  35. Jedem geladenen Sound ist ein Handle zugeordnet, den man über LoadVOCFile
  36. oder GetMemVOCHandle erhält. Er wird bei PlaySound und UnloadVOCFile benötigt.
  37.  
  38. Die einzelnen Routinen dürfen auch aufgerufen werden, wenn keine Sound-Karte
  39. installiert ist. Sie haben in diesem Fall keine Wirkung.
  40. Hinweise:
  41. - Beim Beenden des Programms muß der Programmierer dafür sorgen, daß eine
  42.   eventuell noch laufende Wiedergabe abgebrochen wird.
  43. - Bei älteren Sound Blastern und dazu kompatiblen Karten funktioniert die
  44.   Lautstärkeeinstellung nicht (dort fehlt noch der Mixer).
  45. - Für die Sample-Daten benötigt die Unit eine eigene Memory Page (wegen des
  46.   DMA-Transfer). Bei einem ausgiebig genutzten und stark fragmentierten Heap
  47.   kann es schon vorkommen, daß kein Speicher allokiert werden kann, obwohl
  48.   MaxAvail genügend anbietet. Wenn diese Gefahr besteht, sollte man sich
  49.   gleich am Anfang mit GetMemVOCHandle den nötigen Platz reservieren.
  50.  
  51. Besonderheiten der Unit:
  52. - jeder 8-Bit-DMA-Kanal wird unterstützt (Kanäle 0..3)
  53. - jeder Hardware-Interrupt wird unterstützt (IRQ 0..15)
  54. - jede Abtastrate wird unterstützt
  55.  
  56. Die vorliegende Programmversion hat aber auch noch ein paar Einschränkungen:
  57. - maximale Sample-Datengröße 65528 Byte
  58. - nur umkomprimierte 8-bit-Samples in mono werden unterstützt
  59. - die 16-Bit-DMA-Kanäle 4..7 dürfen nicht verwendet werden (wird nicht geprüft!);
  60. - die Samples werden im knappen konventionellen Speicher gehalten
  61. }
  62.  
  63. {$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+}  { bei Vers. <7.0 überflüssige entfernen }
  64.  
  65. unit VOC;
  66.  
  67. interface
  68.  
  69. uses
  70.   Dos;
  71.  
  72. type
  73.   tHandle = record   { Handle für Sample-Daten }
  74.               Buffer  : pointer;   { Zeiger auf Datenpuffer }
  75.               BufSize : word;      { Größe des Puffers }
  76.               SR      : byte;      { SB-spezifischer Wert für Abtastrate }
  77.             end { record };
  78.  
  79.  
  80. { -------------------------------------------------------------------------- }
  81. function SoundBoardInstalled : boolean;
  82. { -------------------------------------------------------------------------- }
  83. { Liefert true, wenn eine Sound-Karte gefunden wurde }
  84.  
  85. { -------------------------------------------------------------------------- }
  86. function LoadVOCFile(FileName : string; var Sample : tHandle) : boolean;
  87. { -------------------------------------------------------------------------- }
  88. { Laden von Voice-Daten aus einer VOC-Datei
  89.   Bedeutung der Parameter:
  90.   FileName: Dateiname, ggf. mit Pfad
  91.   Sample  : Enthält Angaben zu den Voice-Daten
  92.   Funktionsergebnis:
  93.   true, wenn Ladeaktion erfolgreich
  94.   Die Routine hat in der vorliegenden Version folgende Einschränkungen:
  95.   - Es wird nur ein Datenblock und nur der Typ 1 ausgewertet.
  96.   - Es werden maximal 65528 Byte geladen.
  97.   - Nur 8-Bit-Samples in mono werden unterstützt. }
  98.  
  99. { -------------------------------------------------------------------------- }
  100. function GetMemVOCHandle(BufSize, SampleRate : word; var Sample : tHandle) : boolean;
  101. { -------------------------------------------------------------------------- }
  102. { Die Routine reserviert nur Speicher im Heap der gewünschten Größe BufSize
  103.   (max. 65528 Byte) und präpariert den Handle. Die Freigabe erfolgt ganz
  104.   normal mit UnloadVOCFile.
  105.   Das Funktionsergebnis ist true, wenn alle Parameter gültig und Aktion
  106.   erfolgreich. }
  107.  
  108. { -------------------------------------------------------------------------- }
  109. procedure SetVolume(Value : byte);
  110. { -------------------------------------------------------------------------- }
  111. { Lautstärke des Voice-Kanals einstellen
  112.   Value = 0..255, wird auf 4 Bit reduziert
  113.   Wert wird für linken und rechten Kanal gesetzt.
  114.   Funktioniert leider nicht auf jeder kompatiblen Karte. }
  115.  
  116. { -------------------------------------------------------------------------- }
  117. function PlaySound(var Sample : tHandle) : boolean;
  118. { -------------------------------------------------------------------------- }
  119. { Sound-Wiedergabe starten
  120.   Funktionsergebnis true, wenn Aktion erfolgreich.
  121.   Nur ein Sound kann zu einem bestimmten Zeitpunkt wiedergegeben werden. }
  122.  
  123. { -------------------------------------------------------------------------- }
  124. function PauseSound : boolean;
  125. { -------------------------------------------------------------------------- }
  126. { Sound-Wiedergabe anhalten
  127.   Funktionsergebnis true, wenn laufende Wiedergabe erfolgreich angehalten. }
  128.  
  129. { -------------------------------------------------------------------------- }
  130. function RestartSound : boolean;
  131. { -------------------------------------------------------------------------- }
  132. { Angehaltene Sound-Wiedergabe fortsetzen
  133.   Funktionsergebnis true, wenn Aktion erfolgreich. }
  134.  
  135. { -------------------------------------------------------------------------- }
  136. function StopSound : boolean;
  137. { -------------------------------------------------------------------------- }
  138. { Laufende Sound-Wiedergabe abbrechen
  139.   Funktionsergebnis true, wenn laufende Wiedergabe erfolgreich gestoppt. }
  140.  
  141. { -------------------------------------------------------------------------- }
  142. function SoundPlaying : boolean;
  143. { -------------------------------------------------------------------------- }
  144. { Abfrage, ob im Moment eine Sound-Wiedergabe läuft.
  145.   Funktionergebnis true, wenn ja (auch wenn pausierend). }
  146.  
  147. { -------------------------------------------------------------------------- }
  148. procedure UnloadVOCFile(var Sample : tHandle);
  149. { -------------------------------------------------------------------------- }
  150. { Speicher, welcher durch Sample-Daten belegt wurde, freigeben.
  151.   Der Handle darf für eine Wiedergabe nicht mehr verwendet werden. }
  152.  
  153. implementation
  154.  
  155. const
  156.   SoundBoardAvailable : boolean = false;   { true, wenn Soundkarte installiert }
  157.   IsPlaying           : boolean = false;   { true, wenn Wiedergabe läuft }
  158.   IsPaused            : boolean = false;   { true, wenn Wiedergabe angehalten }
  159.   { Indizes für Sound-Blaster-Register }
  160.   SB_MixReg = $04;   { Mixer Registerselektion (0/W) }
  161.   SB_MixData= $05;   { Mixer Registerzugriff (R/W) }
  162.   SB_Reset  = $06;   { DSP-Initialisierung (0/W) }
  163.   SB_Data   = $0A;   { DSP-Datenregister (R/0) }
  164.   SB_Write  = $0C;   { DSP-Befehlsregister (R/W) }
  165.   SB_Status = $0E;   { DSP-Statusregister (R/0) }
  166.   Ready     : boolean = false;
  167.  
  168. type
  169.   { Sound-Kartenparameter
  170.     (Reihenfolge nicht ändern, nichts einfügen, neue Parameter nur anhängen!) }
  171.   tBoardParams   = record
  172.                      PortAdr,            { Portadresse }
  173.                      IRQ,                { Hardware-Interrupt }
  174.                      DMA,                { DMA-Kanal }
  175.                      BoardType : word;   { Kartentyp }
  176.                    end { record };
  177.  
  178. var
  179.   Board     : tBoardParams;  { Sound-Karten-Parameter }
  180.   OrgInt    : pointer;       { für internen Gebrauch }
  181.  
  182.  
  183. { -------------------------------------------------------------------------- }
  184. function SoundBoardInstalled : boolean;
  185. { -------------------------------------------------------------------------- }
  186.  
  187. { Liefert true, wenn eine Sound-Karte gefunden wurde }
  188.  
  189. begin
  190.   SoundBoardInstalled:=SoundBoardAvailable;
  191. end { SoundBoardInstalled };
  192.  
  193.  
  194. { -------------------------------------------------------------------------- }
  195. function GetBoardParams(var BoardParams : tBoardParams) : boolean;
  196. { -------------------------------------------------------------------------- }
  197.  
  198. { Diese Funktion sucht nach der Environmentvariablen "BLASTER" und ermittelt
  199.   daraus die Sound-Kartenkonfiguration. Das Funktionsergebnis ist true, wenn
  200.   die Variable gefunden wurde und alle vier Parameter (siehe Typvereinbarung
  201.   tBoardParams) angegeben sind. }
  202.  
  203.   function GetParamValue(var ParamLine : string; ParamCode : char;
  204.                          var Value : word; IsHex : boolean) : boolean;
  205.   { Ermittelt den Parameter aus einem String. Ergebnis true, wenn gefunden.
  206.     Value bleibt unverändert, wenn nicht gefunden. IsHex=true: Parameter ist
  207.     als Hexzahl aufzufassen. }
  208.   var
  209.     p  : byte;
  210.     ss : string[5];
  211.     v  : word;
  212.     e  : integer;
  213.   begin
  214.     p:=Pos(ParamCode,ParamLine);
  215.     if p>0 then
  216.     begin   { gewünschter Parameter gefunden }
  217.       GetParamValue:=true;
  218.       ss:=copy(ParamLine,p+1,5);   { Ziffernstring extrahieren }
  219.       p:=Pos(#$20,ss);
  220.       if p>0 then ss[0]:=chr(p-1);
  221.       if IsHex then
  222.       begin   { Parameter ist als Hexzahl aufzufassen }
  223.         if length(ss)>0 then
  224.         begin
  225.           v:=0;
  226.           e:=0;
  227.           for p:=1 to length(ss) do
  228.           begin
  229.             v:=v shl 4;
  230.             if (ss[p]>='0') and (ss[p]<='9') then v:=v+ord(ss[p])-48
  231.             else
  232.             if (ss[p]>='A') and (ss[p]<='F') then v:=v+ord(ss[p])-55
  233.             else inc(e);
  234.           end;
  235.         end
  236.         else GetParamValue:=false;
  237.       end
  238.       else val(ss,v,e);   { Parameter ist als Dezimalzahl aufzufassen }
  239.       if e=0 then Value:=v
  240.              else GetParamValue:=false;
  241.     end
  242.     else GetParamValue:=false;
  243.   end { GetParamValue };
  244.  
  245. const
  246.   VarName = 'BLASTER';   { Name der zu suchenden Environmentvariablen }
  247.  
  248. var
  249.   Params  : string[62];
  250.   Result  : boolean;
  251.  
  252. begin
  253.   Params:=GetEnv(VarName);
  254.   Result:=GetParamValue(Params,'A',BoardParams.PortAdr,true);               { Basisadresse }
  255.   Result:=Result and GetParamValue(Params,'I',BoardParams.IRQ,false);       { Interrupt }
  256.   Result:=Result and GetParamValue(Params,'D',BoardParams.DMA,false);       { DMA-Kanal }
  257.   Result:=Result and GetParamValue(Params,'T',BoardParams.BoardType,false); { Kartentyp }
  258.   GetBoardParams:=Result;
  259. end { GetBoardParams };
  260.  
  261.  
  262. { -------------------------------------------------------------------------- }
  263. function GetMemBuffer(MemSize : word) : pointer;
  264. { -------------------------------------------------------------------------- }
  265.  
  266. { Speicher der Größe MemSize (max. 65528 Byte) an einer Segmentgrenze
  267.   (z.B. 6000h:0000h) für DMA-Transfer allokieren. Schlägt der Versuch fehl,
  268.   ist das Funktionsergebnis nil.
  269.   Die Freigabe des Speichers erfolgt ganz normal mit FreeMem. }
  270.  
  271. type
  272.   tPtr = record
  273.            case integer of
  274.              0 : (p : pointer);
  275.              1 : (Ofs_, Seg_ : word);
  276.          end { record };
  277.  
  278. var
  279.   Dummy, Result : tPtr;
  280.   a, d : longint;
  281.  
  282. begin
  283.   Result.p:=nil;
  284.   GetMem(Dummy.p,MemSize);
  285.   if (Dummy.Ofs_<>0) or ((Dummy.Seg_ and $0FFF)<>0) then
  286.   begin   { Zeiger liegt nicht auf Segmentgrenze }
  287.     Result.Seg_:=(Dummy.Seg_ and $8000)+$1000; { benötigter Zeigerwert }
  288.     Result.Ofs_:=0;
  289.     a:=16*longint(Dummy.Seg_)+Dummy.Ofs_;      { lineare phys. Adresse }
  290.     d:=16*longint(Result.Seg_)-a;              { erforderliche Größe der Hilfsvariablen }
  291.     FreeMem(Dummy.p,MemSize);
  292.     GetMem(Dummy.p,word(d));                   { Füller }
  293.     GetMem(Result.p,MemSize);                  { Speicher an Segmentgrenze allokieren }
  294.     FreeMem(Dummy.p,word(d));                  { den Füller wieder freigeben }
  295.   end
  296.   else Result.p:=Dummy.p;   { Zeiger liegt zufällig schon auf Segmentgrenze }
  297.   { zur Sicherheit wird alles nochmal geprüft }
  298.   if ((Result.Seg_ and $0FFF)<>0) or (Result.Ofs_<>0) then Result.p:=nil;
  299.   GetMemBuffer:=Result.p;
  300. end { GetMemBuffer };
  301.  
  302.  
  303. { -------------------------------------------------------------------------- }
  304. function LoadVOCFile(FileName : string; var Sample : tHandle) : boolean;
  305. { -------------------------------------------------------------------------- }
  306.  
  307. { Laden von Voice-Daten aus einer VOC-Datei
  308.   Bedeutung der Parameter:
  309.   FileName: Dateiname, ggf. mit Pfad
  310.   Sample  : Enthält Angaben zu den Voice-Daten
  311.   Funktionsergebnis:
  312.   true, wenn Ladeaktion erfolgreich
  313.  
  314.   Die Routine hat in der vorliegenden Version folgende Einschränkungen:
  315.   - Es wird nur ein Datenblock und nur der Typ 1 ausgewertet.
  316.   - Es werden maximal 65528 Byte geladen.
  317.   - Nur 8-Bit-Samples in mono werden unterstützt. }
  318.  
  319. type
  320.   tHeader = record   { Header einer VOC-Datei }
  321.               case integer of
  322.                 0 : (IDText  : array[0..19] of char;   { Identifikationstext }
  323.                      Data    : word;                   { Index, wo die Daten beginnen }
  324.                      Version : word;                   { Version }
  325.                      cVers   : word);                  { Version komplementär }
  326.                 1 : (b : array[0..25] of byte);
  327.             end { record };
  328.  
  329. var
  330.   f : file of byte;
  331.   g : file;
  332.   i : longint;
  333.   m, n : byte;
  334.   w : word;
  335.   h : tHeader;
  336.   FilePtr   : longint;
  337.   BlockType : byte;
  338.   BlockSize : longint;
  339.   Ready,
  340.   Closed,
  341.   Result    : boolean;
  342.  
  343.   function GetBlockSize : longint;
  344.   var
  345.     nn : byte;
  346.     rr : longint;
  347.   begin
  348.     read(f,nn); rr:=nn;
  349.     read(f,nn); rr:=rr+256*longint(nn);
  350.     read(f,nn); GetBlockSize:=rr+65536*longint(nn);
  351.   end { GetBlockSize };
  352.  
  353. begin
  354.   { Voreinstellungen }
  355.   with Sample do
  356.   begin
  357.     Buffer:=nil;
  358.     BufSize:=0;
  359.     SR:=0;
  360.   end;
  361.   Result:=false;
  362.   { Analyse }
  363.   if (length(FileName)>0) and SoundBoardAvailable then
  364.   begin
  365.     assign(f,FileName);
  366.     reset(f);
  367.     if IOResult=0 then
  368.     begin
  369.       Closed:=false;
  370.       { Header einlesen }
  371.       seek(f,0);
  372.       for n:=0 to SizeOf(tHeader)-1 do read(f,h.b[n]);
  373.       for n:=0 to 19 do if h.IDText[n]=#$1A then h.IDText[n]:=#$00;
  374.       if h.IDText='Creative Voice File'+#0 then
  375.       begin   { ID-Text deutet auf VOC-Datei hin }
  376.         FilePtr:=h.Data;
  377.         Ready:=false;
  378.         repeat
  379.           seek(f,FilePtr);   { Dateizeiger auf Datenblock setzen }
  380.           read(f,BlockType);
  381.           case BlockType of
  382.             0 : Ready:=true;   { Terminator }
  383.             1 : begin   { Voice Data }
  384.                   BlockSize:=GetBlockSize-2;   { Blockgröße }
  385.                   read(f,n);   { Abtastrate }
  386.                   read(f,m);   { Format der Komprimierung }
  387.                   if (BlockSize>0) and (n>0) and (m=0) then
  388.                   begin
  389.                     if BlockSize>65528 then Sample.BufSize:=65528
  390.                                        else Sample.BufSize:=BlockSize;
  391.                     Sample.SR:=n;
  392.                     Sample.Buffer:=GetMemBuffer(Sample.BufSize);
  393.                     if Sample.Buffer<>nil then
  394.                     begin   { Speicher ist vorhanden }
  395.                       close(f); Closed:=true;
  396.                       assign(g,FileName);
  397.                       reset(g,1);
  398.                       seek(g,FilePtr+6);
  399.                       BlockRead(g,Sample.Buffer^,Sample.BufSize,w);
  400.                       if w=Sample.BufSize then Result:=true;
  401.                       close(g);
  402.                     end;
  403.                   end;
  404.                   Ready:=true;
  405.                 end;
  406.             2 : FilePtr:=FilePtr+GetBlockSize+4;
  407.             3 : FilePtr:=FilePtr+7;
  408.             4 : FilePtr:=FilePtr+6;
  409.             5 : FilePtr:=FilePtr+GetBlockSize+4;
  410.             6 : FilePtr:=FilePtr+6;
  411.             7 : FilePtr:=FilePtr+6;
  412.             8 : Ready:=true;
  413.             9 : Ready:=true;
  414.            else Ready:=true;
  415.           end { case };
  416.           writeln;
  417.         until Ready or (IOResult<>0);
  418.       end;
  419.       if not Closed then close(f);
  420.     end;
  421.   end;
  422.   LoadVocFile:=Result;
  423. end { LoadVOCFile };
  424.  
  425.  
  426. { -------------------------------------------------------------------------- }
  427. function GetMemVOCHandle(BufSize, SampleRate : word; var Sample : tHandle) : boolean;
  428. { -------------------------------------------------------------------------- }
  429.  
  430. { Die Routine reserviert nur Speicher im Heap der gewünschten Größe BufSize
  431.   (max. 65528 Byte) und präpariert den Handle. Die Freigabe erfolgt ganz
  432.   normal mit UnloadVOCFile.
  433.   Das Funktionsergebnis ist true, wenn alle Parameter gültig und Aktion
  434.   erfolgreich. }
  435.  
  436. begin
  437.   GetMemVOCHandle:=false;
  438.   if (BufSize>0) and (BufSize<65529) and (SampleRate>0) and SoundBoardAvailable then
  439.   begin
  440.     Sample.Buffer:=GetMemBuffer(BufSize);
  441.     if Sample.Buffer<>nil then
  442.     begin
  443.       Sample.BufSize:=BufSize;
  444.       Sample.SR:=256 - 1000000 div SampleRate;
  445.       GetMemVOCHandle:=true;
  446.     end;
  447.   end;
  448. end;
  449.  
  450.  
  451. { -------------------------------------------------------------------------- }
  452. function SBInit(var BoardParams : tBoardParams) : boolean; assembler;
  453. { -------------------------------------------------------------------------- }
  454.  
  455. { DSP der Sound Blaster initialisieren
  456.   Funktionsergebnis true, wenn Aktion erfolgreich. }
  457.  
  458. asm
  459.   push es
  460.   les  bx,BoardParams   { Adresse der Kartenparameter in ES:BX }
  461.   mov  cx,es:[bx]       { 1. Record-Variable (hier PortAdr) in CX }
  462.   mov  dx,cx            { und CX }
  463.   add  dx,SB_Reset      { Index für Reset-Register dazuaddieren }
  464.   mov  al,1             { eine "1" auf Reset-Register ausgeben }
  465.   out  dx,al
  466.   xor  al,al            { ein bißchen warten (mind. 3µs) }
  467.   @1:
  468.   nop
  469.   dec  al
  470.   jnz  @1
  471.   out  dx,al            { eine "0" auf Reset-Register ausgeben }
  472.   mov  ax,2000h         { nochmal ein bißchen warten }
  473.   @2:
  474.   nop
  475.   dec  ax
  476.   jnz  @2
  477.   mov  dx,cx            { Status ermitteln }
  478.   add  dx,SB_Status
  479.   in   al,dx
  480.   test al,80h           { Bit 7 gesetzt? }
  481.   jz   @NotOk           { Sprung, wenn nein }
  482.   mov  dx,cx            { zusätzliche Prüfung }
  483.   add  dx,SB_Data       { aus dem DSP-Datenregister muß AAh gelesen werden }
  484.   in   al,dx
  485.   cmp  al,0AAh
  486.   jne  @NotOk
  487.   mov  ax,1             { Funktionsergebnis true }
  488.   jmp  @Ready
  489.   @NotOk:
  490.   xor  ax,ax            { Funktionsergebnis false }
  491.   @Ready:
  492.   pop  es
  493. end { SBInit };
  494.  
  495.  
  496. { -------------------------------------------------------------------------- }
  497. procedure SBWrite(PortAdr : word; Data : byte); assembler;
  498. { -------------------------------------------------------------------------- }
  499.  
  500. { Übergabe eines Kommando- oder Datenbytes an DSP }
  501.  
  502. asm
  503.   mov  dx,PortAdr    { Basisadresse der Sound-Karte }
  504.   add  dx,SB_Write   { Index für Ausgaberegister hinzuaddieren }
  505.   @1:
  506.   in   al,dx         { Prüfen, ob der DSP zur Datenaufnahme bereits ist }
  507.   and  al,80h
  508.   jnz  @1
  509.   mov  al,[Data]     { Kommando bzw. Datenbyte ausgeben }
  510.   out  dx,al
  511. end { SBWrite };
  512.  
  513.  
  514. { -------------------------------------------------------------------------- }
  515. procedure SetIntProc(var BoardParams : tBoardParams; IntProc : pointer);
  516. { -------------------------------------------------------------------------- }
  517.  
  518. { Interruptroutine anhand des eingestellten IRQ's festlegen
  519.   Der Originalvektor wird in der globalen Variablen OrgInt festgehalten. }
  520.  
  521. begin
  522.   with BoardParams do
  523.   begin
  524.     if IRQ<8 then
  525.     begin   { IRQ 0..7 --> Int 8..15 }
  526.       GetIntVec(IRQ+$08,OrgInt);
  527.       SetIntVec(IRQ+$08,IntProc);
  528.       Port[$21]:=Port[$21] and not(1 shl IRQ);       { IRQ aktivieren (Master PIC) }
  529.     end
  530.     else
  531.     begin   { IRQ 8..15 --> Int 112..119 (AT) }
  532.       GetIntVec(IRQ+$68,OrgInt);
  533.       SetIntVec(IRQ+$68,IntProc);
  534.       Port[$A1]:=Port[$A1] and not(1 shl (IRQ-8));   { IRQ aktivieren (Slave PIC) }
  535.     end;
  536.   end;
  537. end { SetIntProc };
  538.  
  539.  
  540. { -------------------------------------------------------------------------- }
  541. procedure RestoreIntProc(var BoardParams : tBoardParams);
  542. { -------------------------------------------------------------------------- }
  543.  
  544. { Originalen Interruptvektor wieder setzen
  545.   Inhalt von OrgInt wird verwendet. SetIntProc muß daher vorher aufgerufen
  546.   worden sein! }
  547.  
  548. begin
  549.   with BoardParams do
  550.   begin
  551.     if IRQ<8 then
  552.     begin
  553.       SetIntVec(IRQ+$08,OrgInt);               { IRQ 0..7 --> Int 8..15 }
  554.       Port[$21]:=Port[$21] or (1 shl IRQ);     { IRQ sperren }
  555.     end
  556.     else
  557.     begin
  558.       SetIntVec(IRQ+$68,OrgInt);               { IRQ 8..15 --> Int 112..119 (AT) }
  559.       Port[$A1]:=Port[$A1] or (1 shl (IRQ-8)); { IRQ sperren }
  560.     end;
  561.   end;
  562. end { RestoreIntProc };
  563.  
  564. {$F+}
  565. { -------------------------------------------------------------------------- }
  566. procedure SndIntProc; interrupt;
  567. { -------------------------------------------------------------------------- }
  568.  
  569. { Interruptroutine
  570.   Wird automatisch aufgerufen, wenn Sound-Wiedergabe beendet ist. }
  571.  
  572. var
  573.   Status : byte;
  574.  
  575. begin
  576.   Status:=Port[Board.PortAdr+SB_Status];   { Int-Bestätigung für DSP }
  577.   Port[$20]:=$20;                          { EOI an Master-PIC }
  578.   Port[$A0]:=$20;                          { EOI an Slave-PIC }
  579.   SBWrite(Board.PortAdr,$D3);              { Lautsprecher ausschalten }
  580.   RestoreIntProc(Board);                   { Interruptvektor restaurieren }
  581.   IsPlaying:=false;   { Mitteilung nach Außen, daß DMA-Transfer abgeschlossen ist }
  582. end { SndIntProc };
  583. {$F-}
  584.  
  585. { -------------------------------------------------------------------------- }
  586. procedure InitDMA(var BoardParams : tBoardParams;
  587.                   Buffer : pointer; BufSize : word);
  588. { -------------------------------------------------------------------------- }
  589.  
  590. { DMA für Sound-Wiedergabe vorbereiten
  591.   Buffer MUSS auf den Anfang einer Memory Page (z.B. $7000:$0000) zeigen. }
  592.  
  593. type
  594.   tPtr = record
  595.            case integer of
  596.              0 : (p : pointer);
  597.              1 : (Ofs_, Seg_ : word);
  598.          end { record };
  599.  
  600. var
  601.   b : tPtr;
  602.   x : byte;
  603.  
  604. begin
  605.   with BoardParams do
  606.   begin
  607.     Port[$0A]:=$04+DMA;               { DMA-Kanal (0..3) deaktivieren }
  608.     Port[$0C]:=$00;                   { Flip-Flop f. 16-Reg. zurücksetzen }
  609.     Port[$0B]:=$48+DMA;               { Modus für entsprechenden Kanal }
  610.     Port[DMA shl 1]:=$00;             { LoByte Adreßregister (Offset) }
  611.     Port[DMA shl 1]:=$00;             { HiByte Adreßregister (Offset) }
  612.     dec(BufSize);
  613.     Port[(DMA shl 1)+1]:=Lo(BufSize); { LoByte Datenpuffergröße }
  614.     Port[(DMA shl 1)+1]:=Hi(BufSize); { HiByte Datenpuffergröße }
  615.     b.p:=Buffer;                      { physikalische Speicherseite }
  616.     case DMA of
  617.       0 : x:=$87;                     { Page-Register-Portadresse für Kanal 0 }
  618.       1 : x:=$83;                     { Page-Register-Portadresse für Kanal 1 }
  619.       2 : x:=$81;                     { Page-Register-Portadresse für Kanal 2 }
  620.       3 : x:=$82;                     { Page-Register-Portadresse für Kanal 3 }
  621.     end { case };
  622.     Port[x]:=b.Seg_ shr 12;
  623.     Port[$0A]:=DMA;                   { DMA-Kanal (0..3) aktivieren }
  624.     Port[$08]:=$10;                   { DMA-Controller aktivieren (zur Sicherheit) }
  625.   end;
  626. end { InitDMA };
  627.  
  628.  
  629. { -------------------------------------------------------------------------- }
  630. procedure SetVolume(Value : byte);
  631. { -------------------------------------------------------------------------- }
  632.  
  633. { Lautstärke des Voice-Kanals einstellen
  634.   Value = 0..255, wird auf 4 Bit reduziert
  635.   Wert wird für linken und rechten Kanal gesetzt.
  636.   Funktioniert leider nicht auf jeder kompatiblen Karte. }
  637.  
  638. begin
  639.   with Board do
  640.   begin
  641.     Value:=Value shr 4;             { nur 16 Laustärkestufen möglich }
  642.     Value:=(Value shl 4)+Value;     { für beide Kanäle das Gleiche }
  643.     Port[PortAdr+SB_MixReg]:=$04;   { gewünschtes Mixer-Register (hier: Voice Volume) }
  644.     Port[PortAdr+SB_MixData]:=Value;{ gewünschte Lautstärke setzen }
  645.   end;
  646. end { SetVolume };
  647.  
  648.  
  649. { -------------------------------------------------------------------------- }
  650. function PlaySound(var Sample : tHandle) : boolean;
  651. { -------------------------------------------------------------------------- }
  652.  
  653. { Sound-Wiedergabe starten
  654.   Funktionsergebnis true, wenn Aktion erfolgreich }
  655.  
  656. var
  657.   w : word;
  658.  
  659. begin
  660.   PlaySound:=false;
  661.   if SoundBoardAvailable and (Sample.Buffer<>nil) and (Sample.BufSize>0)
  662.     and (not IsPlaying) then
  663.   begin   { Voraussetzungen für Wiedergabe erfüllt }
  664.     if SBInit(Board) then
  665.     begin   { Initialisierung des DSP ok }
  666.       SetIntProc(Board,@SndIntProc);               { Int-Routine setzen }
  667.       SBWrite(Board.PortAdr,$D1);                  { Lautsprecher einschalten }
  668.       SBWrite(Board.PortAdr,$40);                  { Abtastrate einstellen }
  669.       SBWrite(Board.PortAdr,Sample.SR);
  670.       InitDMA(Board,Sample.Buffer,Sample.BufSize); { DMA vorbereiten }
  671.       w:=Sample.BufSize-1;
  672.       SBWrite(Board.PortAdr,$14);                  { normaler 8-Bit-DMA }
  673.       SBWrite(Board.PortAdr,Lo(w));                { LoByte Datengröße }
  674.       SBWrite(Board.PortAdr,Hi(w));                { HiByte Datengröße }
  675.       IsPlaying:=true;
  676.       PlaySound:=true;
  677.     end;
  678.   end;
  679. end { PlaySound };
  680.  
  681.  
  682. { -------------------------------------------------------------------------- }
  683. function PauseSound : boolean;
  684. { -------------------------------------------------------------------------- }
  685.  
  686. { Sound-Wiedergabe anhalten
  687.   Funktionsergebnis true, wenn laufende Wiedergabe erfolgreich angehalten. }
  688.  
  689. begin
  690.   PauseSound:=false;
  691.   if IsPlaying and (not IsPaused) then
  692.   begin
  693.     SBWrite(Board.PortAdr,$D0);                  { DMA-Transfer anhalten }
  694.     IsPaused:=true;
  695.     PauseSound:=true;
  696.   end;
  697. end { PauseSound };
  698.  
  699.  
  700. { -------------------------------------------------------------------------- }
  701. function RestartSound : boolean;
  702. { -------------------------------------------------------------------------- }
  703.  
  704. { Angehaltene Sound-Wiedergabe fortsetzen
  705.   Funktionsergebnis true, wenn Aktion erfolgreich. }
  706.  
  707. begin
  708.   RestartSound:=false;
  709.   if IsPlaying and IsPaused then
  710.   begin
  711.     SBWrite(Board.PortAdr,$D4);                  { DMA-Transfer fortsetzen }
  712.     IsPaused:=false;
  713.     RestartSound:=true;
  714.   end;
  715. end { RestartSound };
  716.  
  717.  
  718. { -------------------------------------------------------------------------- }
  719. function StopSound : boolean;
  720. { -------------------------------------------------------------------------- }
  721.  
  722. { Laufende Sound-Wiedergabe abbrechen
  723.   Funktionsergebnis true, wenn laufende Wiedergabe erfolgreich gestoppt. }
  724.  
  725. begin
  726.   StopSound:=false;
  727.   if IsPlaying then
  728.   begin
  729.     SBWrite(Board.PortAdr,$D0);                  { DMA-Transfer anhalten }
  730.     SBWrite(Board.PortAdr,$D3);                  { Lautsprecher ausschalten }
  731.     RestoreIntProc(Board);                       { Interruptvektor restaurieren }
  732.     SBInit(Board);                               { DSP initialisieren }
  733.     IsPlaying:=false;
  734.     IsPaused:=false;
  735.     StopSound:=true;
  736.   end;
  737. end { StopSound };
  738.  
  739.  
  740. { -------------------------------------------------------------------------- }
  741. function SoundPlaying : boolean;
  742. { -------------------------------------------------------------------------- }
  743.  
  744. { Abfrage, ob im Moment eine Sound-Wiedergabe läuft.
  745.   Funktionergebnis true, wenn ja (auch wenn pausierend). }
  746.  
  747. begin
  748.   SoundPlaying:=IsPlaying;
  749. end { SoundPlaying };
  750.  
  751.  
  752. { -------------------------------------------------------------------------- }
  753. procedure UnloadVOCFile(var Sample : tHandle);
  754. { -------------------------------------------------------------------------- }
  755.  
  756. { Speicher, welcher durch Sample-Daten belegt wurde, freigeben }
  757.  
  758. begin
  759.   if SoundBoardAvailable then
  760.   with Sample do
  761.     if (Buffer<>nil) and (BufSize>0) then
  762.     begin
  763.       FreeMem(Buffer,BufSize);
  764.       Buffer:=nil;
  765.       BufSize:=0;
  766.       SR:=0;
  767.     end;
  768. end { UnloadVOCFile };
  769.  
  770.  
  771. begin   { Hauptprogramm }
  772.   SoundBoardAvailable:=GetBoardParams(Board);   { Prüfung, ob Soundkarte installiert }
  773. end { Unit VOC }.
  774.