e-mail    Debatní kniha    Mapa stránek    Hlavní  
 perličky 
 

Zahrajte si na piánu...ehm...na klávesnici

Zbyšek Hlinka

Máte doma malé dítě, které vám hrabe do klávesnice, když pracujete? Já ano. Udělal jsem tedy pro něj prográmek, kterým nelze mnoho poničit -- projevoval se zvukově při stisku klávesy. Dítě zalehne klávesnici, počítač všelijak pípá, ale není to až tak zábavné, aby ho to zaujalo na delší dobu. Princip je velice jednoduchý -- cyklus čtení klávesy, zapnutí zvuku v nějaké frekvenci, pauza, vypnutí zvuku.

Po čase jsem došel k závěru, že v této podobě to není ono, a začal hledat způsob, jak zajistit tón po celou dobu stisku klávesy. Stačí tedy zjistit moment stisku klávesy a moment jejího puštění.

Jak ale na to -- při každém stisku klávesy se volá přerušení číslo 9, a to dvakrát. Jednou při stlačení klávesy a podruhé při jejím puštění. Jestliže klávesu podržíte delší dobu, opakuje se v určitých intervalech volání přerušení 9 jako při stlačení klávesy (o tom se v Bajtu už několikrát psalo). Ale podle čeho lze poznat, co se s klávesou právě děje?

Pro můj případ stačil poznatek, že port 60H vrací při stlačení klávesy její pořadové číslo (scan code), což je zajímavé, a při jejím puštění jiné číslo, větší než pořadové číslo poslední klávesy (>130), které svou vlastní hodnotou už pro tento případ zajímavé není. Stačí tedy testovat port 60H při průchodu přes přerušení 9. Číslo klávesy se nám bude hodit -- v tabulce mu přiřadíme nějakou frekvenci (co klávesa, to jiný tón). Při indikaci stlačená klávesa pak zapne zvuk nechá ho běžet do té doby, než port 60H vrátí indikaci puštěné klávesy.

Vzhledem k tomu, že základní klávesnice dovoluje rozsah tří oktáv včetně půltónů, nemá smysl přiřazovat klávesám náhodnou frekvenci, ale můžeme si vytvořit malý syntezátor. Každé klávese přiřadíme jeden tón z harmonické stupnice, přičemž první a třetí řadu kláves lze použít pro půltóny -- druhou a čtvrtou pro celé tóny -- s rozložením asi jako u piána. K tomu je dobré nakreslit si někam rozložení tónů na klávesách, nejlépe na monitor.

Příklad takového jednoduchého programu v Pascalu je v připojeném výpisu. Po jeho spuštění můžete dítě zanechat u klávesnice (dokud slyšíte zvuk počítače, je vše v pořádku).

program Piano;
uses Crt,Dos,Graph;
type
  Klavesa=record
    S:byte;       {pořadové č. klávesy - scan code}
    Z:string[3];  {znaková hodnota klávesy}
    F:word;       {frekvenční hodnota klávesy v Hz}
    D:byte;       {celý tón, půltón, nic}
    N:string[4];  {notová hodnota klávesy}
  end;

const    {konstanty pro rozměry obrazovky VGA 640 x 480}
  kx=40;      {x-rozměr klávesy}
  ky=50;      {y-rozměr klávesy}
  Mezera=2;   {mezera mezi klávesami}
  PocetKl=51; {počet inicializovaných kláves}
  cN=0;       {Nulový tón}
  cC=1;       {Celý tón}
  cP=2;       {Půltón}

  Kl:array[1..PocetKl] of Klavesa=( 
          {obsazení kláves notami - pro klávesnici US}
  (S: 2; Z:'1';  F:   0; D:cN; N:''),
  (S: 3; Z:'2';  F: 139; D:cP; N:'cis'),
  (S: 4; Z:'3';  F: 156; D:cP; N:'dis'),
  (S: 5; Z:'4';  F:   0; D:cN; N:''),
  (S: 6; Z:'5';  F: 185; D:cP; N:'fis'),
  (S: 7; Z:'6';  F: 208; D:cP; N:'gis'),
  (S: 8; Z:'7';  F: 233; D:cP; N:'ais'),
  (S: 9; Z:'8';  F:   0; D:cN; N:''),
  (S:10; Z:'9';  F: 277; D:cP; N:'cis1'),
  (S:11; Z:'0';  F: 311; D:cP; N:'dis1'),
  (S:12; Z:'-';  F:   0; D:cN; N:''),
  (S:13; Z:'=';  F: 370; D:cP; N:'fis1'),
  (S:14; Z:'BS'; F:   0; D:cN; N:''),
  (S:15; Z:'Tab';F:   0; D:cN; N:''),
  (S:16; Z:'Q';  F: 131; D:cC; N:'c'),
  (S:17; Z:'W';  F: 147; D:cC; N:'d'),
  (S:18; Z:'E';  F: 165; D:cC; N:'e'),
  (S:19; Z:'R';  F: 175; D:cC; N:'f'),
  (S:20; Z:'T';  F: 196; D:cC; N:'g'),
  (S:21; Z:'Y';  F: 220; D:cC; N:'a'),
  (S:22; Z:'U';  F: 247; D:cC; N:'h'),
  (S:23; Z:'I';  F: 262; D:cC; N:'c1'),
  (S:24; Z:'O';  F: 294; D:cC; N:'d1'),
  (S:25; Z:'P';  F: 330; D:cC; N:'e1'),
  (S:26; Z:'[';  F: 349; D:cC; N:'f1'),
  (S:27; Z:']';  F: 392; D:cC; N:'g1'),
  (S:28; Z:'CR'; F:   0; D:cN; N:''),
  (S:30; Z:'A';  F: 415; D:cP; N:'gis1'),
  (S:31; Z:'S';  F: 466; D:cP; N:'ais1'),
  (S:32; Z:'D';  F:   0; D:cN; N:''),
  (S:33; Z:'F';  F: 554; D:cP; N:'cis2'),
  (S:34; Z:'G';  F: 622; D:cP; N:'dis2'),
  (S:35; Z:'H';  F:   0; D:cN; N:''),
  (S:36; Z:'J';  F: 740; D:cP; N:'fis2'),
  (S:37; Z:'K';  F: 831; D:cP; N:'gis2'),
  (S:38; Z:'L';  F: 932; D:cP; N:'ais2'),
  (S:39; Z:';';  F:   0; D:cN; N:''),
  (S:40; Z:'"';  F:1109; D:cP; N:'cis3'),
  (S:41; Z:'~';  F:   0; D:cN; N:''),
  (S:43; Z:'\';  F:   0; D:cN; N:''),
  (S:44; Z:'Z';  F: 440; D:cC; N:'a1'),
  (S:45; Z:'X';  F: 494; D:cC; N:'h1'),
  (S:46; Z:'C';  F: 523; D:cC; N:'c2'),
  (S:47; Z:'V';  F: 587; D:cC; N:'d2'),
  (S:48; Z:'B';  F: 659; D:cC; N:'e2'),
  (S:49; Z:'N';  F: 698; D:cC; N:'f2'),
  (S:50; Z:'M';  F: 784; D:cC; N:'g2'),
  (S:51; Z:',';  F: 880; D:cC; N:'a2'),
  (S:52; Z:'.';  F: 988; D:cC; N:'h2'),
  (S:53; Z:'/';  F:1046; D:cC; N:'c3'),
  (S:57; Z:' ';  F:   0; D:cN; N:''));

type
  KlavRec=record
    x,y,dx,dy:integer;
    Klav:^Klavesa;
  end;
  KlavArray=array[1..60] of KlavRec;

  Obrazovka=object
    GraphOk:boolean;
    c,c1:char;
    Tlac:byte;
    Hraje:boolean;
    Klavesy:KlavArray;
    constructor Init;
    destructor Done;
    procedure NamalujKlavesy;
    procedure Tlacitko(i:byte;Aktivni:boolean);
    procedure Run;
  end;

var
  R:registers;
  p:pointer;        {ukazatel na interrupt}
  pr:byte;          {uchová port 60H}
  Down:boolean;     {stav klávesy down/up}
  Delej:Obrazovka;  {objektová metoda}

constructor Obrazovka.Init;
var
  gd,gm:integer;
begin
  c:=#0;
  c1:=#0;
  Tlac:=0;
  Hraje:=false;
  Down:=false;
  gd:=Detect;
  InitGraph(gd,gm,'');   {nezapomeňte na cestu k *.bgi}
  GraphOk:=GraphResult=grOk;
  NamalujKlavesy;
end;

destructor Obrazovka.Done;
begin
  if GraphOk then CloseGraph;
  Nosound;
end;

procedure Obrazovka.NamalujKlavesy;
var
  i:byte;
  var x,y:integer;

procedure Aktivni(Cislo:byte;var x1,y1:integer;
                               dx1,dy1:integer);
begin
  with Klavesy[Cislo] do
  begin x:=x1; y:=y1; dx:=dx1; dy:=dy1; end;
  x1:=x1+dx1+Mezera;
end;

procedure Pasivni(var x1,y1:integer
                    ;dx1,dy1:integer;Str:string);
begin
  Rectangle(x1,y1,x1+dx1,y1+dy1);
  OutTextXY(x1+5,y1+5,Str);
  x1:=x1+dx1+Mezera;
end;

begin
  FillChar(Klavesy,SizeOf(Klavesy),0);
  for i:=1 to PocetKl do with Klavesy[Kl[i].S] do
  begin
    New(Klav);
    Klav^:=Kl[i];
  end;
  x:=0; y:=5;
  Aktivni(41,x,y,kx,ky);
  for i:=2 to 13 do Aktivni(i,x,y,kx,ky);
  Aktivni(43,x,y,kx,ky);
  Aktivni(14,x,y,kx,ky);
  x:=0; y:=y+ky+Mezera;
  Aktivni(15,x,y,3*kx div 2,ky);
  for i:=16 to 27 do Aktivni(i,x,y,kx,ky);
  Aktivni(28,x,y,2+3*kx div 2,2*ky+2);
  x:=0; y:=y+ky+Mezera;
  Pasivni(x,y,2*kx+Mezera,ky,'Caps Lock');
  for i:=30 to 40 do Aktivni(i,x,y,kx,ky);
  x:=0; y:=y+ky+Mezera;
  Pasivni(x,y,2+5*kx div 2,ky,'Shift');
  for i:=44 to 53 do Aktivni(i,x,y,kx,ky);
  Pasivni(x,y,4+5*kx div 2,ky,'Shift');
  x:=0; y:=y+ky+Mezera;
  Pasivni(x,y,2+3*kx div 2,ky,'Ctrl');
  Inc(x,kx);
  Pasivni(x,y,3*kx div 2,ky,'Alt');
  Aktivni(57,x,y,7*(Mezera+kx),ky);
  Pasivni(x,y,3*kx div 2,ky,'Alt');
  Inc(x,kx+Mezera);
  Pasivni(x,y,2+3*kx div 2,ky,'Ctrl');
  for i:=1 to 60 do Tlacitko(i,false);
end;

procedure Obrazovka.Tlacitko(i:byte;Aktivni:boolean);
begin
  with Klavesy[i] do if dx>0 then
  begin
    if Aktivni then
    begin
      SetFillStyle(1,12);
      Bar(x,y,x+dx,y+dy);
      SetColor(0);
    end else
    begin
      case Klav^.D of
        cN:SetFillStyle(1,0);
        cP:SetFillStyle(1,1);
        cC:SetFillStyle(1,7);
      end;
      Bar(x,y,x+dx,y+dy);
      SetColor(15);
      Rectangle(x,y,x+dx,y+dy);
    end;
    OutTextXY(x+5,y+5,Klav^.Z);
    OutTextXY(x+5,y+30,Klav^.N);
  end;
end;

procedure Obrazovka.Run;
begin
  if not GraphOk then Exit;
  begin
    repeat
      if KeyPressed then  {vyprázdni buffer klávesnice}
      begin c:=ReadKey; if c=#0 then c1:=ReadKey; end;
      if Down then        {klavesa stlacena}
      if not Hraje then   {pokud je ticho}
      begin
        Tlac:=pr;
        Tlacitko(Tlac,true);
        with Klavesy[Tlac] do if Klav<>nil
                              then Sound(Klav^.F);
        Hraje:=true;
      end else else
      begin      {klavesa pustena, udelej konecne ticho}
        Nosound;
        if Hraje then Tlacitko(Tlac,false);
        Hraje:=false;
      end;
    until (c=#0) and (c1=#16);  {Alt-Q - konec divadla}
  end;
end;
procedure Int9; interrupt;
begin
  Intr($90,r);
  pr:=port[$60];  {uschova port pro dalsi pouziti}
  Down:=pr<61;    {<61 - klavesa stlacena, 
                   jinak pustena, nebo nezajimave}
end;

begin
  GetIntVec($9,p);
  SetIntVec($90,p);
  SetIntVec($9,@Int9);
  Delej.Init;
  Delej.Run;
  Delej.Done;
  SetIntVec($9,p);
end.

Vlastní jádro je přitom uloženo v procedurách Run, Int9 a částečně v constructoru Init, v konstantě Kl (hodnotách S a F) je seznam přiřazení not (frekvencí) klávesám a zbytek je vata zajišťující kreslení na monitor. Bez této vaty lze program srazit na podstatně méně než maximálních sto řádek pro perličky.

Rozsah použitelné klávesnice jsem nastavil na [1..60], což postačuje pro základní klávesnici. Budete-li chtít použít i funkční nebo číselnou klávesnici, je nutno rozsah patřičně rozšířit, a to i v proceduře Int9. Funkce ReadKey v proceduře Run slouží pouze pro vyprázdnění bufferu, jinak nemá žádný význam. Program je napsán pro VGA 640 x 480, pro jiné rozlišení je třeba upravit příslušné konstanty (zejména kx a ky).

I tento program, stejně jako mé předchozí perličky (Bajty 3/92 a 5/91) je vlastně jen náznakem nápadu (byť fungujícím). Pro širší a bezpečnější použití je nutno dále programovat. Pokud budete chtít z tohoto programu vytvořit samoběžící verzi pomocí programů MakeDemo a DoDemo (Bajt 3/92), jistě se vám to nepovede, protože demoprogramy hlídají jen část toho, co se děje s klávesnicí. Tohle je zrovna případ, kam tyto programy nesahají (samozřejmě v podobě, v jaké byly publikovány -- jinak je nutno opět dále programovat). Telefonoval mi jeden čtenář, že zkoušel demoprogramy na hrách a nefungovalo to. I tam se zjevně děje něco, co demoprogramy nehlídají.

S případnými dotazy a náměty na vylepšení se můžete obracet na adresu:

Zbyšek Hlinka, Kryštofova 1016, 149 00 Praha 4, tel. (02) 795 29 56




Pascal - hlavní
Překladače
Vlastní články
Převzaté články
Věci na stáhnutí
Odkazy k tématu
BP7 buglist
Chyba Run-time 200

BASIC