Ver. 1.0 Beta, Don't have a compiler? Now you can write simple scripts ONLINE! This is a basic version -does not include all the Pascal statements. Only to learn basics

Tworzenie muzyki w Turbo Pascalu, muzyka z karty muzycznej. Odtwarzanie Wave i MIDI

Pascal

Back to previous articleprevious VESA, tworzenie szybkiej grafiki
Go to main menuup Zaawansowany kurs pascala
Next articlenext Tworzenie gier w Turbo Pascalu

Tworzenie muzyki w Turbo Pascalu, muzyka z karty muzycznej. Odtwarzanie Wave i MIDI

O muzyce i d¼wiêkach


W tych artyku³ach szczegó³owo przedstawiono próby odtwarzania muzyki z karty muzycznej a tak¿e z g³o¶niczka wbudowanego w komputer (za pomoc± instrukcji Sound).


Tre¶æ tworz± g³ównie kody ¼ród³owe, dlatego wymagana jest choæ czê¶ciowa umiejêtno¶æ Pascala

Muzyka i d¼wiêk w Turbo Pascalu
Poni¿ej prezentacja modu³u Wavplay -znajdziesz go w internecie jako FreeWare. Mo¿na siê na jego podstawie nauczyæ programowania kart d¼wiêkowych.

Polecam ksi±¿kê: Programowanie gier 2D i 3D w jêzyku Turbo Pascal


Ten modu³ zatrzymuje dzia³anie programu podczas odtwarzania d¼wiêku, ale da siê go przerobiæ by tak nie by³o. Powodzenia...

Odtwarzanie plików wave w pascalu


   W pascalu nie ma du¿ych problemów z odtworzeniem d¼wiêków. Poni¿szy modu³ pozwala na odtworzenie wave'ów 8-bitowych, o czêstotliwo¶ci próbkowania do 11khz. ¯eby nagraæ taki d¼wiêk najlepiej pos³u¿yæ siê programem CoolEdit2000. W internecie powinna byæ jego darmowa wersja Shareware.
   Mo¿na oczywi¶cie samemu programowo wytworzyæ falê, któr± pó¼niej odtworzy komputer.

Wykorzystanie modu³u


   Najpierw trzeba wywo³aæ funkcjê Init('nazwapliku.wav');
Pó¼niej tylko Play;
   Je¶li Twoja karta muzyczna spe³nia standardy Sound Blastera powinienne¶ us³yszeæ d¼wiêk.


{$G+}
Unit wavplay;

{$M 4096,0,65500}

Interface
Uses DOS,Crt;

Procedure Init(GN:STRING);
Procedure Play;
Implementation
Const dma    = 4096;
Type  id_t   = Array[1..4] of Char;
     riff_t = Record
               R_Ident : id_t;
               length  : Longint;
               C_Ident : id_t;
               S_Ident : id_t;
               s_length: Longint;
               Format  ,
               Modus   : Word;
               freq    ,
               byte_p_s: LongInt;
               byte_sam,
               bit_sam : Word;
               D_Ident : id_t;
               d_length: LongInt;
             End;
     blaster_T = Record
                   port : Word;
                   dmac ,
                   hdmac,
                   irq  : Byte;
                 End;
     buffer_T = Array[1..dma] of Byte;

Var id       : riff_T;
   fn       : String;
   wav      : File;
   sbb      : Word;
   Ende     : Boolean;
   blaster  : Blaster_T;
   alt_irq  : Pointer;
   dma_buf_1,
   dma_buf_2,
   zwi      : ^Buffer_T;
   Channel  : Byte;

Const RIFF : id_t = ('R','I','F','F');
     WAVE : id_t = ('W','A','V','E');
     FMT_ : id_t = ('f','m','t',' ');
     DATA : id_t = ('d','a','t','a');

     DMA_Dat : Array [0..7,1..6] of Byte=
                 (($A,$C,$B,$0,$87,$1),
                  ($A,$C,$B,$2,$83,$3),
                  ($A,$C,$B,$4,$81,$5),
                  ($A,$C,$B,$6,$82,$7),
                  ($D4,$D8,$D6,$C0,$8F,$C2),
                  ($D4,$D8,$D6,$C4,$8B,$C6),
                  ($D4,$D8,$D6,$C8,$89,$CA),
                  ($D4,$D8,$D6,$CC,$8A,$CE));

Procedure Blaster_Command(c :Byte); Assembler;
Asm
   Mov dx,Word Ptr sbb
   Add dx,$c
@t:In al,dx
   And al,128
   Jnz @t
   Mov al,c
   Out dx,al
End;

Procedure Init_SB(base : Word);
Var w,w2:Word;
Begin
 sbb:=base;
 Port[base+6]:=1; Delay(4); Port[base+6]:=0; w:=0; w2:=0;
 Repeat
   Repeat Inc(w); Until ((Port[base+$e] and 128)=128) or (w>29);
   Inc(w2);
 Until (Port[base+$a]=$AA) or (W2>30);
 If w2>30 then
   Begin
     WriteLn('Failed to ReSet Blaster');
     Halt(128);
   End;
 Blaster_Command($d1);
End;

Procedure Set_Stereo; Assembler;
Asm
 Mov dx,Word Ptr sbb
 Add dx,$4
 Mov al,$e
 Out dx,al
 Inc dx
 In al,dx
 And al,253
 Or al,2
 Out dx,al
End;

Procedure Clear_Stereo; Assembler;
Asm
 Mov dx,Word Ptr sbb
 Add dx,$4
 Mov al,$e
 Out dx,al
 Inc dx
 In al,dx
 And al,253
 Out dx,al
End;

Function No_Wave(Var id:riff_T):Boolean;
Begin
 With id do
   No_Wave:=(R_Ident<>RIFF) or
            (C_Ident<>WAVE) or
            (S_Ident<>FMT_) or
            (D_Ident<>DATA);
End;

Procedure Init(GN:STRING);
Var b : Byte;
Begin
{  WriteLn;
 WriteLn('ABo WAV-Player (16bit Test)      (p) 27.11.94 ABo');}
 Blaster.Port:=0;
 Blaster.dmac:=0;
 Blaster.hdmac:=0;
 Blaster.irq:=0;
 fn:=GetEnv('BLASTER');
 If fn='' then
   Begin
     WriteLn('BLASTER must be set...');
     Halt(100);
   End;
 b:=1;
 Repeat
   Case fn[b] of
     'A' : Repeat
             Inc(b);
             Blaster.Port:=Blaster.Port*16+Ord(fn[b])-48;
           Until Fn[b+1]=' ';
     'D' : Begin
             Blaster.DMAc:=Ord(fn[b+1])-48;
             Inc(b,2);
           End;
     'I' : Repeat
             Inc(b);
             Blaster.IRQ:=Blaster.IRQ*16+Ord(fn[b])-48;
           Until Fn[b+1]=' ';
     'H' : Begin
             Blaster.hDMAc:=Ord(fn[b+1])-48;
             Inc(b,2);
           End;
       End;
   Inc(b);
 Until b>Length(fn);
{  With Blaster do
   WriteLn('Blaster : P',Port,'  I',irq,'  D',dmac,'  H',hdmac);}
 Init_SB(Blaster.Port);
 FN:=GN;
 Assign(wav,fN);
 {$I-} ReSet(wav,1); {$I+}
 If IOResult<>0 then
   Begin
     WriteLn('File "',fn,'" not found!');
     Halt(2);
   End;
 BlockRead(wav,id,Sizeof(id));
 If no_Wave(id) then
   Begin
     WriteLn('"',fn,'" seems to be no WAVE-File...');
     Halt(128);
   End;
{  Write('Wave    : ',id.bit_sam,'bit ');}
 If id.Modus=2 then
   Begin
     Set_Stereo;
{      Write('stereo ');}
   End
 Else
   Begin
     Clear_Stereo;
{      Write('mono    ');}
   End;
 If (id.bit_sam>8) and (Blaster.hdmac>3) then
   Channel:=Blaster.hdmac
 Else Channel:=Blaster.dmac;
{  WriteLn(id.freq,' Hz  ',id.byte_p_s,' Bytes/Sec');
 WriteLn('Length  : ',id.d_length,' Bytes    ',id.d_length div id.byte_p_s, ' Sec');
 WriteLn('Playing : ',fn);}
End;

{$F+}
Procedure Stelle_DMA(Freq: Word;Var size : Word);
Var PageNr,PageAdress,DMALength: Word;
Begin
 Inline($FA);
 Asm
   Mov ax,Word Ptr DMA_Buf_1[2]
   Shr ax,12
   Mov Word Ptr PageNr,ax
   Mov ax,Word Ptr DMA_Buf_1[2]
   Shl ax,4
   Mov Word Ptr PageAdress,ax
   Mov ax,Word Ptr DMA_Buf_1
   Add Word Ptr PageAdress,ax
   Adc Word Ptr PageNr,0
 End;
 DMALength:=Size;
 Freq:=256-Trunc(1000000/Freq);
 If Channel>3 then
   Begin
     DMALength:=DMALength div 2;
     PageAdress:=PageAdress div 2;
     If Odd(PageNr) then
       Begin
         Dec(PageNr);
         PageAdress:=PageAdress+$8000
       End;
   End;
 If id.Modus=2 then
   Begin
     If id.bit_sam=16
       then Blaster_Command($A4)
       Else Blaster_Command($A8);
   End
 Else
   If id.bit_sam=16
     then Blaster_Command($A4);

 Dec(DMALength);

 Port[DMA_dat[Channel,1]]:=$4 or (Channel and $3);
 Port[DMA_dat[Channel,2]]:=$0;
 Port[DMA_dat[Channel,3]]:=$49;
 Port[DMA_dat[Channel,4]]:=lo(PageAdress);
 Port[DMA_dat[Channel,4]]:=hi(PageAdress);
 Port[DMA_dat[Channel,5]]:=lo(PageNr);
 Port[DMA_dat[Channel,6]]:=lo(DMALength);
 Port[DMA_dat[Channel,6]]:=hi(DMALength);
 Port[DMA_dat[Channel,1]]:=(Channel and $3);

 Blaster_Command($40);
 Blaster_Command(Lo(Freq));
 Blaster_Command($48);
 Blaster_Command(lo(DMALength));
 Blaster_Command(hi(DMALength));
 Blaster_Command($91);
 Inline($FB);
End;

Procedure Ausgabe_IRQ; Interrupt;
Var test : Byte;
Begin
 Inline($FA);
 Port[$20]:=$20;
 test:=Port[sbb+$e];
 Ende:=True;
 Inline($fB);
End;
{$F-}

Procedure Play;
Var  p,s,s2 : Word;
   w      : LongInt;
Begin
 GetMem(zwi,16);
 GetMem(dma_buf_1,dma);
 p:=16;
 While (Seg(dma_buf_1^[1]) mod 4096)>(4096-(dma*2 div 16)) do
   Begin
     FreeMem(dma_buf_1,dma);
     FreeMem(zwi,p);
     p:=p+16;
     If p>65525 then halt(111);
     GetMem(zwi,p);
     GetMem(dma_buf_1,dma);
   End;
 GetMem(dma_buf_2,dma);
 FreeMem(zwi,p);
 port[$21]:=Port[$21] and (255 xor (1 shl Blaster.IRQ));
 GetIntVec(Blaster.IRQ+8,Alt_irq);
 SetIntVec(Blaster.IRQ+8,@Ausgabe_IRQ);
 w:=id.freq*id.modus;
 BlockRead(wav,dma_buf_1^[1],dma,s);
 Repeat
   Ende:=False;
   Stelle_DMA(w,s);
   BlockRead(wav,dma_buf_2^[1],dma,s2);
   Repeat Until Ende;
   s:=s2;
   zwi:=dma_buf_1;
   dma_buf_1:=dma_buf_2;
   dma_buf_2:=zwi;
 Until EoF(wav) or Keypressed;
 While KeyPressed do w:=Ord(ReadKey);
 If EoF(wav) then
   Begin
     Ende:=False;
     Stelle_DMA(w,s);
     Repeat Until Ende;
   End;
 SetintVec(Blaster.IRQ+8,Alt_IRQ);
 FreeMem(dma_buf_1,dma);
 FreeMem(dma_buf_2,dma);
 Port[$21]:=Port[$21] or (1 shl Blaster.IRQ);
 Blaster_Command($d3);
 Close(wav);
End;

Begin
End.
Nieoficjalne ¼ród³o prób z kart± sound blaster
Poniewa¿ zainteresowanie d¼wiêkiem w turbo pascal'u jest na najwiêkszym poziomie, postanowi³em opublikowaæ swoje stare kody zwi±zane z muzyk±. Najprawdopodobniej nie zadzia³aj± od razu, bo wymagaj± pliku wave o okre¶lonej nazwie, ale mog± byæ cenn± pomoc±.

W kodzie
- jak resetowaæ kartê Sound blaster,
- wczytywanie pliku wave


{$N+}
program muzyka;


Uses Dos;

Const Freq:Word=8000;

Type TTWav=^TWave;
    TWave=array[1..$FFFF] of Byte;

Var
 Dane, D2, D3:TTWav;
 Porty:Array[1..5] of Word;
 S:Array[1..8] of Longint;
 t, FT:Byte;
 Int1CSave:pointer;
 S182:Word;
 B1, OK:Boolean;

Procedure Podstaw(i:Byte);
Begin
 Porty[1]:=$206+i shl 4;
 Porty[2]:=$20A+i shl 4;
 Porty[3]:=$20C+i shl 4;
 Porty[4]:=$20C+i shl 4;
 Porty[5]:=$20E+i shl 4;
End;

Function Zapisz_do_SB(b:Byte):Boolean;
Begin
 {While (Port[Porty[3]] and 128=0) DO ;}
  IF Port[Porty[3]] and 128=128 THEN
  BEGIN
      port[Porty[3]]:=B;
      Zapisz_do_SB:=TRUE;
  END
  ELSE Zapisz_do_SB:=FALSE;

   { End Else
     WriteLn('7 Bit portu 2xCh nie wyzerowany');}
End;

Function Odczyt_z_SB(Var b:Byte):Boolean;
Begin
 {While (Port[Porty[5]] and 128=128) DO ;}
 IF Port[Porty[5]] and 128=0 THEN
 BEGIN
   b:=port[Porty[2]];
   Odczyt_z_SB:=TRUE;
 END
 Else Odczyt_z_SB:=FALSE;

End;

procedure RobFale(Fn, Fn2:String);
Var F:File;
a:word;
Begin
 Assign(F, Fn);
 reset(F, 1);
 Seek(F, $2c);
 Blockread(F, Dane^, $FFFF);
 Close(F);

 {Assign(F, Fn2);
 reset(F, 1);
 Seek(F, $2c);
 Blockread(F, D2^, $FFFF);
 Close(F);}

 For a:=1 to $FFFF do
 D3^[a]:=Dane^[a]{ Else D3^[a]:=D2^[Round(22/8*a)]};

End;

procedure Krok(B:Byte);
Begin
{  Case B Of
 End;}
End;

procedure GrajNowe(VAR Buf; rozmiar:Word);
Var SCzas:Byte;
   Adres, strona:Word;

Begin
 Zapisz_do_SB($D0);

 adres:=Seg(BUF) shl 4+Ofs(Buf);
 strona:=(seg(Buf)+Ofs(Buf) shr 4) shr 12;


      Port[$00A]:=5;
      Port[$00B]:=$49;
      port[$83]:=strona;
      port[$00c]:=0;
      port[$002]:=lo(adres);
      port[$002]:=Hi(adres);
      port[$00c]:=0;
      port[$003]:=lo(rozmiar);
      port[$003]:=Hi(rozmiar);
      port[$00a]:=$01;

      SCzas:=256-(1000000 div Freq);
      Zapisz_do_SB($40);
      Zapisz_do_SB(SCzas);

      Zapisz_do_SB($14);
      Zapisz_do_SB(lo(rozmiar));
      Zapisz_do_SB(Hi(rozmiar));
End;
procedure WczytajFale(P:Pointer; Fn:String; rozmiar:Word);
Var F:File;
Size:Longint;
Begin
 FT:=1;
 Assign(F, Fn);
 reset(F, 1);
 Size:=FileSize(F);
 {Blockread(F, D3^, $FFFF);

 S[Ft]:=S[Ft]+$FFFF;}
 If Size>$2c+S[FT]+rozmiar Then
 Begin
   Seek(F, $2c+S[FT]);
   Blockread(F, P^, rozmiar);
 End
 Else
 Begin
   Seek(F, $2c+S[FT]);
   rozmiar:=Size-($2c+S[FT]);
   Blockread(F, P^, rozmiar);
 End;

 S[Ft]:=S[Ft]+rozmiar;

 Close(F);

 {Move(Dane^, D3^, rozmiar);}

End;


{$F+,S-,W-}


procedure Graj;interrupt;
Begin
 B1:=NOT B1;
 IF B1 THEN GrajNowe(Dane^, S182) ELSE GrajNowe(D3^, S182);
 IF B1 THEN WczytajFale(D3, '35-2.wav', S182) ELSE
            WczytajFale(Dane, '35-2.wav', S182)
End;
{$F-,S+}





Var T1, t2:Word;
   rozmiar:word;
   SP:Byte;
   l:longint;

begin
  New(Dane);
  S[FT]:=0;
  {New(D2);}
  New(D3);
  writeln('podaj nr portu (1)=210h (2)=220h..');
  readln(t);
  Podstaw(t);
  Port[Porty[1]]:=1;
  {Delay(5);}
  for l:=1 to 1 shl 18 do t1:=t1*t2;
  Port[Porty[1]]:=0;
  for l:=1 to 1 shl 18 do t1:=t1*t2;
  {Delay(5);}
  T1:=Port[Porty[5]];
  T2:=Port[Porty[2]];

  If not ((T1 and 128=128) and (T2=$AA)) Then
    WriteLn('Reset karty nie udal sie')  Else
    Begin
      {GetMem(Dane, $FFFF);}


      Zapisz_do_SB($D1);
      Zapisz_do_SB($D0);
      WriteLn('Reset karty pomyslnie');

      S182:=Round(Freq/18.2);
      rozmiar:=$FFFF;
      {WczytajFale(D3, '35-2.wav', S182);}
      GetIntVec($1C,Int1CSave);
      SetIntVec($1C,Addr(Graj));


  repeat
     {If (Port[Porty[3]] And 128)<>128 Then
     Begin
     GrajNowe(rozmiar);
     WczytajFale('MO.wav', rozmiar);
     End;

   If (SP=28) and (port[$60]=156) Then
   Begin}
     {WriteLn(port[8]);{

   End;
   Sp:=Port[$60];
   WriteLn(Port[porty[1]], ' ',Port[porty[2]], ' ',Port[porty[3]], ' ',Port[porty[5]], ' ');}
 until port[$60]=1;
 {FreeMem(Dane, $FFFF);}
 Dispose(Dane);
 Dispose(D3);
 {Dispose(D2);}
 Zapisz_do_SB($D3);
  SetIntVec($1C,Int1CSave);
    End;


end.
Muzyka z PC Speaker'a

Przyk³ad demonstruje odtwarzanie gamy d¼wiêków za pomoc± pascal'a.


Tworzenie Gamy d¼wiêków, czêstotliwo¶ci nut


W du¿ym przybli¿eniu: C = 538Hz, D=615Hz, (nagra³em widmo d¼wiêku C granego na flecie i zbada³em czêstotliwo¶æ najg³o¶niejszego tonu) E = D * D / C, F = E * D / C itd..


Ka¿dy d¼wiêk gamy ma w³asn± czêstotliwo¶æ, nastêpny jest po prostu kolejnym elementem ci±gu geometrycznego. Ten sam d¼wiêk wy¿szej oktawy ma dok³adnie dwukrotnie wy¿sz± czêstotliwo¶æ. D¼wiêków w oktawie jest 8. Jest 8 d¼wiêków granych normalnie, s± te¿ bemole i krzy¿yki. Ka¿dy kolejny d¼wiêk jest wy¿szy czêstotliwo¶ci± od poprzedniego o oko³o 71/65. Taki pomiar mo¿na uzyskaæ mierz±c 2 s±siednie progi gitary. Ich stosunek da nam ró¿nicê wysoko¶ci tonów. Za pierwszy d¼wiêk np. C mo¿na przyj±æ dowoln± czêstotliwo¶æ (tak jakby przestroiæ gitarê), ale lepiej przyj±æ standardow± warto¶æ, ok 538Hz


A teraz w praktyce:




{$N+}
PROGRAM Organki;
USES Crt;
CONST MaxNut=20;
     FirstK1 = 16;
     FirstK2 = 2;
     C       = 538;
     D       = 615;
VAR
 Temp  : Single;
 Temp2 : Single;
 Temp3 : Single;

 Tab:ARRAY [0..MaxNut] OF Word;

PROCEDURE Generate;
VAR i:Integer;
BEGIN
 Temp:=D;
 Temp2:=C;

 FOR i:=0 TO MaxNut DO
 BEGIN
   Temp3:=Temp+(Temp-Temp2)*65/71;
   Temp2:=Temp;Temp:=Temp3;
   Tab[i]:=Round(Temp);
 END;
END;


BEGIN
 Generate;
 REPEAT
   Sound(Tab[PORT[$60]-FirstK1]);
   IF KeyPressed THEN ReadKey;
   IF PORT[$60]>127 THEN NoSound;
 UNTIL PORT[$60]=1;
END.

ON-LINE scripts!

This website uses cookie files in order to provide Google services (advertisements, analitycs) and Facebook. If you want to block using cookies, turn them off in your browser settings. Need a help? Click me