Muusika

MIDI faili mahamängimine

... on lihtne, sest vastavad vahendid on küllaltki valmiskujul kättesaadavad. Kui arvutis on MIDI väljund (helikaardi kaudu) kõlaritesse või kõrvaklappidesse saadetud ning muusikafail ilusti kettal olemas, siis peaks all näha oleva nelja käsu abil olema võimalik etteantud nimega failist muusikat kuulata. Klassi MidiSystem käsk getSecuence võimaldab sekventsi lugeda failist (või mõnest muust voost). Kui avatud sekventser määrata vastavat sekventsi mängima, siis võimegi kuulata, mis faili salvestatud on.

import javax.sound.midi.*;
public class Midimangija1 {
  public static void main(String argumendid[]) throws Exception{
    Sequencer sekventser=MidiSystem.getSequencer();
    sekventser.open();
    sekventser.setSequence(MidiSystem.getSequence(new java.io.File("koduke.mid")));
    sekventser.start();
  }
}

	MIDI failis paiknevate andmete kohta võib muudki teada saada: andmete pikkust mikrosekundites ning tiksudes, radade arvu ning teated ükshaaval radade kaupa. Rajalt saab käsuga get kätte järjekorranumbri järgi MidiEvent'i. Sealt edasi getMessage väljastab teate sisu ning getTick tiksu (ajahetke), millal vastav teade süntesaatorile saadetakse. Tuleb vaadata, millist tüüpi teatega on tegemist ning vastavalt käituda. Helidega seotud teated on tüübist ShortMessage. Iga teate juures on täisarvuna kirjas kanal, käsklus ning kaks teatebaiti. Nende baitide interpretatsioon sõltub sellest, millise käsuga on tegemist. 

import javax.sound.midi.*;
import java.util.*;
public class Midimangija2 {
  public static void main(String argumendid[]) throws Exception{
    Sequence sekvents=MidiSystem.getSequence(new java.io.File("koduke.mid"));
    System.out.println(sekvents.getDivisionType()+" pikkus: "+sekvents.getMicrosecondLength()/1000000.0+
                   " sekundit, "+sekvents.getTickLength()+" tiksu");
    Track[] rajad=sekvents.getTracks();
    System.out.println(rajad.length+" rada");
    for(int nr=0; nr<rajad.length; nr++){
      System.out.println("Rada "+nr);
      for(int t=0; t<rajad[nr].size(); t++){
        MidiEvent me=rajad[nr].get(t);
        long tiks=me.getTick();
        MidiMessage teade=me.getMessage();
        if(teade instanceof ShortMessage){
          ShortMessage sm=(ShortMessage)teade;
          System.out.println(tiks+" ShortMessage "+
            "kanal: "+sm.getChannel()+
            " teade: "+sm.getCommand()+  //näiteks noteOff
            " bait1: "+sm.getData1()+    //kõrgus
            " bait2: "+sm.getData2()     //valjus
          );
        }
        if(teade instanceof MetaMessage){
          MetaMessage mm=(MetaMessage)teade;
          System.out.print(tiks+" MetaMessage"+
           " tyyp: "+mm.getType());
          byte[] b=mm.getData();
          for(int i=0; i<b.length; i++)System.out.print(" "+b[i]);
          System.out.println();
        }
      }
    }
  }
}

/*

Väljund:

0.0 pikkus: 15.5 sekundit, 2976 tiksu
2 rada
Rada 0
0 MetaMessage tyyp: 88 4 2 24 8
0 MetaMessage tyyp: 89 0 0
0 MetaMessage tyyp: 81 7 -95 32
2976 MetaMessage tyyp: 47
Rada 1
0 ShortMessage kanal: 0 teade: 192 bait1: 1 bait2: 0
0 ShortMessage kanal: 0 teade: 176 bait1: 7 bait2: 80
0 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
96 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
96 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
192 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
192 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 80
288 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 0
288 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
384 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
384 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
480 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
480 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
576 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
576 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
672 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
768 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
864 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
864 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
960 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
960 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 80
1056 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 0
1056 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
1152 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
1152 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
1248 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
1248 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
1344 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
1344 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
1440 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
1536 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
1632 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
1632 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
1728 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
1728 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
1824 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
1824 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
1920 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
1920 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 80
2016 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 0
2016 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 80
2112 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 0
2112 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 80
2208 ShortMessage kanal: 0 teade: 144 bait1: 65 bait2: 0
2304 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
2400 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
2400 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
2496 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
2496 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 80
2592 ShortMessage kanal: 0 teade: 144 bait1: 62 bait2: 0
2592 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 80
2688 ShortMessage kanal: 0 teade: 144 bait1: 64 bait2: 0
2688 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
2784 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
2784 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
2880 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
2880 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 80
2976 ShortMessage kanal: 0 teade: 144 bait1: 60 bait2: 0
2976 MetaMessage tyyp: 47

*/
	Nagu näha, seda MIDI faili loonud programm on kasutanud noodi kõlamise alustamiseks ning lõpetamiseks sama käsku (NOTE_ON, 144), vaid mängimise lõpetamise puhul on noodile määratud valjus 0. Kanalile 0 pole muusika kõlamisega seotud noote pandud. 

MIDI faili loomine

Sekventsi võib faili kirjutada ühe käsuga, andes ette MIDI formaadi (0 või 1) ning voo, kuhu andmed saata.
MidiSystem.write(sekvents, 1, new FileOutputStream("koduke2.mid"));

Nii võib rahumeeli lasta programmil loo välja mõtelda või kasutaja käest küsida ning siis kettale salvestada, et seda edaspidi kuulata või uuesti muuta. All näites on võetud ette lugu (koduke.mid), sellele lisatud rada ning algse viimase (siin ainukese) raja nootide järgi pandud uuele rajale samad noodid  väikese tertsi (3 pooltooni) jagu allapoole. ShortMessage'd on ümber arvutatud, muud muutumatuna üle kantud.

import javax.sound.midi.*;
import java.io.*;
public class Midimangija3 {
  public static void main(String argumendid[]) throws Exception{
    Sequence sekvents=MidiSystem.getSequence(new File("koduke.mid"));
    Track algrada=sekvents.getTracks()[sekvents.getTracks().length-1]; //viimane rada
    Track uusrada=sekvents.createTrack();
    int nihe=-3;
    for(int nr=0; nr<algrada.size(); nr++){
      MidiEvent me=algrada.get(nr);
      if(me.getMessage() instanceof ShortMessage){
        ShortMessage sm=(ShortMessage)me.getMessage();
        ShortMessage sm2=new ShortMessage();
        if(sm.getCommand()==ShortMessage.NOTE_ON ||
             sm.getCommand()==ShortMessage.NOTE_OFF){
          sm2.setMessage(
            sm.getCommand(), sm.getChannel(), sm.getData1()+nihe, sm.getData2()
          );
        } else {
          sm2=(ShortMessage)sm.clone();
        }
        uusrada.add(new MidiEvent(sm2, me.getTick()));
      } else {
        uusrada.add(me);
      }
    }
    MidiSystem.write(sekvents, 1, new FileOutputStream("koduke2.mid"));
  }
}

Saateautomaat

	Küllap olete näinud ja kuulnud süntesaatorit lugusid saatmas. Mõni koputab lihtsalt rütmi taha, teisele tuleb ette anda helistik ning selle peale mängitakse saateakordi. Leidub ka selliseid saateprogramme, mis vastavalt etteantud helistikule ise saatenoote juurde mõtlevad või lisaks sellele juba mängitava viisi pealt harmoonia ennustavad. Viimase võimaluse jätame siin välja, kuid juurde kuuluva pikema näite ja seletuse läbi töötanud lugeja peaks mõningase pusimise järel esimese kolme osaga küll hakkama saama. 
	Näites mängitakse saadet Juhansonide Unenäo laulule. Kasutaja saab määrata helistiku ning kuulata bassi, akorde ning taustaks mängitavat rahvalikku viiulipartiid, samuti määrata tempot. Koodi lühiduse ja kompaktsuse huvides pole viisi lisatud, kuid olles seletustest aru saanud, peaks see täiesti jõukohane olema. 
	Üheaegselt mängitavad rajad luuakse valmis ja pannakse kokku üheks sekventsiks. Rajad, mille heli parajasti kuulda ei soovita, pannakse vaikima. Kuna ühes helistikus mängimisel piirdub lugu kolme duuriga, siis arvutatakse nii bassi kui akordi tarvis välja rajad kõigi kolme duuri jaoks, kuid kõlada lastakse vaid siis, kui kasutaja vastavat instrumenti soovib kõlamas kuulda ning parajasti mängitav duur ja rada kokku langevad. Nii pääseb pidevast nootide arvutamisest  ning uued rajad tuleb luua vaid helistiku vahetamisel. Vaid viiulipartii luuakse iga takti algul uuesti, et see tunduks värske ja kordumatuna. 
	Bassi partii on kõige lihtsam, sestap alustan selle loomise seletamisest. Takti jooksul on bassipartiis vaid üks noot, mängitava akordi esimene aste, mis kõlab kogu takti jooksul. Takt koosneb kolmest löögist, iga löök neljast tiksust (see määrati sekventsi loomisel). Nii peab bass alustama kõlamist tiksust 0 ning lõpetama selle viimase tiksu järel ehk kaheteistkümnenda tiksu alguses. Selle ühe noodi kõlamiseks on vaja rajale kolme teadet: tuleb määrata kanalil kõlama hakkav pill, kõlamise algus ja lõpp. Bass pannakse mängima etteantud põhinoodist 2 oktaavi ehk 24 pooltooni allpoolt.

void looBass(Track t, int toonika){
  try{

Kõigepealt tehakse tühjad teated valmis

   ShortMessage m[]=new ShortMessage[3];
   for(int i=0; i<m.length; i++){
     m[i]=new ShortMessage();  
   }  

seejärel antakse neile väärtused

   m[0].setMessage(ShortMessage.PROGRAM_CHANGE, 1, 39, 0); 
   m[1].setMessage(ShortMessage.NOTE_ON, 1, toonika-24, 60); 
   m[2].setMessage(ShortMessage.NOTE_OFF, 1, toonika-24, 60); 

ning lõpuks pannakse rajale, lisades juurde, mitmenda tiksu juures nad aktiivseks peavad muutuma.

   t.add(new MidiEvent(m[0], 0));
   t.add(new MidiEvent(m[1], 0));
   t.add(new MidiEvent(m[2], 12));
  }catch(Exception e){e.printStackTrace();}
 }

	Kolmkõla loomine toimub tehniliselt sarnaselt bassiga võrrelduna. Kui bassi partii koosnes takti jooksul vaid ühest noodist, siis kolmkõla tarvis kõlavad takti jooksul kolm nooti. Esimesel löögil alustatakse põhinoodiga, teisel kolmanda ning kolmandal viienda astmega. Kolmanda löögi lõpus lõpetatakse kõikide nootide kõlamine. Endise kolme teate asemel on nüüd vaja seitset: instrumendi määramiseks ning kõigi kolme noodi kõlamise alustamiseks ja lõpetamiseks. 
	Et võiks korraga välja arvutada ühe helistiku juures vaja minevad kolmkõla- ning bassirajad, selleks sai loodud meetod, millele antakse ette toonika ning mis väljastab sekventsi kummagi esituse radadega esimese, neljanda ning viienda astme tarvis. Kolmkõlade juures võetakse nii neljas kui viies aste (vastavalt viis ja seitse pooltooni) toonikast üles, bassi puhul alla. Tegemist on täiesti sisetundega, milliselt poolt võtta, kuid siin tundus, et helilõigu keskele sobib sügav mahlakas bass paremini ning lõpus kõrgemas toonikas justkui saabub rahu ja tasakaal. Põhjalikumate saadete puhul tuleb leidmise algoritm tõenäoliselt keerulisemaks teha, et arvestataks ilusti kõlavaid absoluutkõrgusi ning miks mitte ka viisi kulgemist. 

Sequence pohikolmkolad(int toonika) throws InvalidMidiDataException{
   Sequence s1=new Sequence(Sequence.PPQ, 4);  
   looKolmkola(s1.createTrack(), toonika);
   looKolmkola(s1.createTrack(), toonika+5); //kvart
   looKolmkola(s1.createTrack(), toonika+7); //kvint
   looBass(s1.createTrack(), toonika);
   looBass(s1.createTrack(), toonika-7); //IV ehk kvint alla
   looBass(s1.createTrack(), toonika-5); 
   return s1;
 }

	Et parasjagu valitud helistiku põhikolmkõladeks saatehääled kätte saada, ka selleks on eraldi — kuigi lühike — alamprogramm.
 
 Sequence looSekvents() throws InvalidMidiDataException{
   return pohikolmkolad(helikorgused[helistik.getSelectedIndex()]);
 }

	Nime all helistik peitub valik ning helistik.getSelectedIndex() annab järjekorranumbri, mitmenda kasutaja oli loetelust valinud. Massiivis helistikud on tähtedega määratud, mitmendal kohal mingi helistik asetseb ning massiiv helikorgused näitab vastavate helistike toonikate asukohad MIDI skaalal. Sõnes jooksevHelistik hoitakse meeles viimati valitud helistiku nimi, et edaspidi oleks võrdlemisel teada, millal kasutaja on helistikku vahetanud, et oleks põhjust saatesekvents uuesti arvutada. 

 JComboBox helistik=new JComboBox();
 String[] helistikud={"Bb", "F", "C", "G", "D", "A", "E"};
  int[] helikorgused={58, 53, 60, 55, 50, 57, 52};
  String jooksevHelistik="";

Mängitav sekvents ning mängiv sektventser,
  Sequence sequence;
  Sequencer sequencer;
graafilised vahendid töö juhtimiseks,
  JButton nupp=new JButton("Mängi");
  JCheckBox ruut=new JCheckBox("Korda");
  JCheckBox bass=new JCheckBox("Bass");
  JCheckBox akord=new JCheckBox("Akord");
  JCheckBox taust=new JCheckBox("Taust");
  JRadioButton[] raadionupud=new JRadioButton[3];
  String[] raadionupustring={"I", "IV", "V"};
  JScrollBar tempo=new JScrollBar(JScrollBar.HORIZONTAL, 190, 5, 40, 320);
rada viiulipartii paigutamiseks.
  Track muutuvRada;
Diatoonilise duuri astmetele vastavate pooltoonide arv, et hiljem oleks võimalik määrata, mitmendale astmele liikuda ning see arvutile pooltoonides selgeks teha. 
static final int[] noodivahed={-1, 0, 2, 4, 5, 7, 9, 11, 12};  
  //toonika kohal 1 väärtus 0
Konkreetse loo duuride järjestus.
  int[] duurid={0, 1, 2, 0, 1, 2, 0, 0};  // Juhansonide unenäo laul
                                          // 0- toonika, 1-IV, 2-V
  int duurinr=0; //mängitava takti järjekorranumber

	Taustaks mängib viiul lihtsa algoritmi järgi: alustatakse kõlava duuri esimeselt astmelt ning liigutakse iga noodiga sellelt ühe võrra kas üles või alla juhuslikult võetuna. Sellisena satub rõhuline noot kokku kõlava akordiga ning taustaks kõlab ühtlane meloodiline saagimine. Veidi parandades ning ebaloogilisi järske üleminekuid vältides annaks loomulikkust suurendada, kuid ka sellisel kujul ei riiva hullusti kõrva ning viiulimängu algusest alustades tuleks tükk harjutada, et selliselegi tulemusele jõuda. 
	Et algoritm arvestab diatooniliste astmetega, MIDI aga loeb kõik pooltoonid samaväärseteks siis on loodud ümberarvutamiseks eraldi alamprogramm, millele antakse ette algne aste ja soovitav nihe ning mis väljastab vahe pooltoonides, lubades ka ühe oktaavi piirest välja nihkuda. 

 static int pooltoonid(int aste, int muutus){
    int loppaste=aste+muutus;
    int okt=loppaste/7;
    if(loppaste<0)okt--;
    loppaste=loppaste-7*okt;
    int vahe=noodivahed[loppaste]-noodivahed[aste];
    return vahe+12*okt;
 }

	Meetodile enesele antakse ette rada, kuhu teated panna, toonikanoodinumber ning aste, millelt mängimist alustada. 

 void looTaustaviiul(Track t, int toonika, int aste){
  try{

Kõigepealt luuakse mäluruum arvutustulemuste paigutamiseks soovitud arvu leitud nootide tarvis,
   int nootidearv=6;
   int[] samm=new int[nootidearv];
   int i=0;  
   samm[i++]=0;
siis arvutatakse eelpool kirjeldatud algoritmi järgi väärtused nootidele, kusjuures iga järgmine asub eelmise kõrval
   while(i<nootidearv){
     if(Math.random()<0.5){
       samm[i]=samm[i-1]-1;
     } else {
       samm[i]=samm[i-1]+1;     
     }
     i++;
   }
ning lõpuks asendatakse leitud astmed MIDI teadetega.  Ette nagu ikka pilli määramine, sedakorda kanalile number 2.
   ShortMessage m=new ShortMessage();
   m.setMessage(ShortMessage.PROGRAM_CHANGE, 2, 41, 0);
   t.add(new MidiEvent(m, 0));
   for(i=0; i<nootidearv; i++){
     m=new ShortMessage();
Iga helikõrguse arvutamiseks liidetakse kokku helistiku põhikõrgus (toonika), pooltoonide arv kõlava duuri põhitoonini jõudmiseks (noodivahed[aste]) ning takti jooksul sellest nihkutud astmete arv pooltoonidena (pooltoonid(aste, samm[i])).
     m.setMessage(ShortMessage.NOTE_ON, 2, 
        toonika+noodivahed[aste]+pooltoonid(aste, samm[i]), 60);
     t.add(new MidiEvent(m, i*2));
     m=new ShortMessage();
     m.setMessage(ShortMessage.NOTE_OFF, 2, 
        toonika+noodivahed[aste]+pooltoonid(aste, samm[i]), 60);
     t.add(new MidiEvent(m, (i+1)*2));
   }
  }catch(Exception e){e.printStackTrace();}
 }

	Hoolitsemaks, et mängitaks vaid nendel radadel, mille kuulamist kasutaja ootab, tuleb ülejäänud vaikima määrata. Sobivat duuri näitab raadionupp. Siin näites määrab selle valitu programm, kuid kergesti võib ka inimesel lasta määrata, mitmenda astme kolmkõla ta soovib saateks kuulata. 

void paneSoolo(){
   for(int i=0; i<raadionupud.length; i++){
     sequencer.setTrackMute(i, !raadionupud[i].isSelected() || !akord.isSelected());
     sequencer.setTrackMute(3+i, !raadionupud[i].isSelected() || !bass.isSelected());
   }
Taustaviiul jäetakse kõlama vaid siis, kui vastav ruut on märgitud.
   sequencer.setTrackMute(6, !taust.isSelected());
 }
  
Iga uue takti mängimisel tuleb teha mõned ettevalmistused.

 void alusta(){
  try{
Kui kasutaja on helistikku vahetanud, tuleb kogu sekvents uuesti arvutada. 
  if(!jooksevHelistik.equals(helistik.getSelectedItem())){
   sequence=looSekvents();
   jooksevHelistik=helistik.getSelectedItem().toString();
  }
Kui tegemist pole loo algusega, siis tuleb kustutada eelmises taktis mängitud viiulipartii
   if(muutuvRada!=null)sequence.deleteTrack(muutuvRada);
ning luua uus rada uue partii paigutamiseks.
   muutuvRada=sequence.createTrack();
   int pohitoon=helikorgused[helistik.getSelectedIndex()];  
   int aste=1;
   if (duurid[duurinr]==1)aste=4;
   if (duurid[duurinr]==2)aste=5;
ja arvutada sinna noodid.
   looTaustaviiul(muutuvRada, pohitoon, aste);
   sequencer.setSequence(sequence);
Mängitava duuri raadionupp märgistada,
   raadionupud[duurid[duurinr]].setSelected(true);
leida järgmise korra jaoks järgneva takti number või suunata lugu algusse tagasi
   duurinr=(duurinr+1)%duurid.length;
ning lõpuks väljundisse lugu mängima hakata.
   mangi();
  }catch(Exception e){e.printStackTrace(); System.out.println(e);}
 }

Mängimise tarvis tuleb 
void mangi(){
kõigepealt sekventser tööle panna
   sequencer.start();
alles seejärel saab määrata, millised rajad vaikivad
   paneSoolo();
ning kui kiiresti muusika liigub.
   sequencer.setTempoInBPM(tempo.getValue());
 }

	Siia meetodisse saadetakse teade sekventsi lõppemise kohta. Kui kasutaja soovib jätkamist, hakatakse uuesti otsast peale, muul juhul lõpetatakse mängimine ning vabastatakse ressursid teiste programmide tarvis. 
  public void meta(MetaMessage m){
    if(m.getType()==47 && ruut.isSelected()){
      alusta();
    } else {
      sequencer.close();
    }
  }

	Töölesaamise käivitusprogramm on ikka standardne. Tuleb vaid hoolitseda, et init-meetod käima pandaks. Rakendina käivitamisel jääb siinne staatiline meetod sootuks täitmata. Sõltuvalt masina jõudlusest ja konfiguratsioonist võib mõnikord kohalikult kettalt iseseisva programmi või rakendina siinse saateprogrammi käivitamine sujuvama tulemuse ande kui üle võrgu tööle panek, sest sel juhul kipub vähemalt aeglasemate masinate korral õiguste kontrolliks kuuldavalt aega kuluma.

  public static void main(String argumendid[]){
    JFrame f=new JFrame("Saade");
    Noodirakend9a ap=new Noodirakend9a();
    ap.init();
    f.getContentPane().add(ap);
    f.pack();
    f.setVisible(true);
  }

	Siit lehekülgedelt aga peaks olema võimalik leida piisavalt ideid ja soovitusi süntesaatori programmeerimiseks, et mõningane maitse suhu saada ning muusikateadmisi ja keerulisematel juhtudel ka raamatuid appi võttes saaks midagi täiesti kasutatavat kokku panna. 

import javax.sound.midi.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Noodirakend9a extends JApplet implements ActionListener, MetaEventListener{
  Sequence sequence;
  Sequencer sequencer;
  JButton nupp=new JButton("Mängi");
  JCheckBox ruut=new JCheckBox("Korda");
  JCheckBox bass=new JCheckBox("Bass");
  JCheckBox akord=new JCheckBox("Akord");
  JCheckBox taust=new JCheckBox("Taust");
  JComboBox helistik=new JComboBox();
  JRadioButton[] raadionupud=new JRadioButton[3];
  String[] raadionupustring={"I", "IV", "V"};
  JScrollBar tempo=new JScrollBar(JScrollBar.HORIZONTAL, 190, 5, 40, 320);
  Track muutuvRada;
  String[] helistikud={"Bb", "F", "C", "G", "D", "A", "E"};
  int[] helikorgused={58, 53, 60, 55, 50, 57, 52};
  String jooksevHelistik="";
  static final int[] noodivahed={-1, 0, 2, 4, 5, 7, 9, 11, 12};  //toonika kohal 1 väärtus 0
  int[] duurid={0, 1, 2, 0, 1, 2, 0, 0};  // Juhansonide unenäo laul
                                          // 0- toonika, 1-IV, 2-V
  int duurinr=0; //takti järjekorranumber
   
  void looKolmkola(Track t, int toonika){
  try{
   ShortMessage m[]=new ShortMessage[7];
   for(int i=0; i<m.length; i++){
     m[i]=new ShortMessage();           // Tühjad teated valmis
   }  
   m[0].setMessage(ShortMessage.PROGRAM_CHANGE, 0, 25, 0);  //pill nr 25 rajale 0
   m[1].setMessage(ShortMessage.NOTE_ON, 0, toonika, 60);
   m[2].setMessage(ShortMessage.NOTE_ON, 0, toonika+4, 60); //terts rajale 0 valjusega 60
   m[3].setMessage(ShortMessage.NOTE_ON, 0, toonika+7, 60); //kvint
   m[4].setMessage(ShortMessage.NOTE_OFF, 0, toonika, 60);
   m[5].setMessage(ShortMessage.NOTE_OFF, 0, toonika+4, 60);
   m[6].setMessage(ShortMessage.NOTE_OFF, 0, toonika+7, 60);
    t.add(new MidiEvent(m[0], 0));
   for(int i=1; i<4; i++){
     t.add(new MidiEvent(m[i], (i-1)*4));  // nelja tiksu (ühe löögi) tagant
   }                                       // noodid sisse
   for(int i=4; i<7; i++){
     t.add(new MidiEvent(m[i], 12));       // 12nda tiksu juures vaikus
   }
  }catch(Exception e){
    e.printStackTrace();
  }
 }

 void looBass(Track t, int toonika){
  try{
   ShortMessage m[]=new ShortMessage[3];
   for(int i=0; i<m.length; i++){
     m[i]=new ShortMessage();  
   }  
   m[0].setMessage(ShortMessage.PROGRAM_CHANGE, 1, 39, 0); 
   m[1].setMessage(ShortMessage.NOTE_ON, 1, toonika-24, 60); 
   m[2].setMessage(ShortMessage.NOTE_OFF, 1, toonika-24, 60); 
   t.add(new MidiEvent(m[0], 0));
   t.add(new MidiEvent(m[1], 0));
   t.add(new MidiEvent(m[2], 12));
  }catch(Exception e){e.printStackTrace();}
 }
 
 static int pooltoonid(int aste, int muutus){
    int loppaste=aste+muutus;
    int okt=loppaste/7;
    if(loppaste<0)okt--;
    loppaste=loppaste-7*okt;
    int vahe=noodivahed[loppaste]-noodivahed[aste];
    return vahe+12*okt;
 }

 void looTaustaviiul(Track t, int toonika, int aste){
  try{
   int nootidearv=6;
   int[] samm=new int[nootidearv];
   int i=0;  
   samm[i++]=0;
   while(i<nootidearv){
     if(Math.random()<0.5){
       samm[i]=samm[i-1]-1;
     } else {
       samm[i]=samm[i-1]+1;     
     }
     i++;
   }
   ShortMessage m=new ShortMessage();
   m.setMessage(ShortMessage.PROGRAM_CHANGE, 2, 41, 0);
   t.add(new MidiEvent(m, 0));
   for(i=0; i<nootidearv; i++){
     m=new ShortMessage();
     m.setMessage(ShortMessage.NOTE_ON, 2, toonika+noodivahed[aste]+pooltoonid(aste, samm[i]), 60);
     t.add(new MidiEvent(m, i*2));
     m=new ShortMessage();
     m.setMessage(ShortMessage.NOTE_OFF, 2, toonika+noodivahed[aste]+pooltoonid(aste, samm[i]), 60);
     t.add(new MidiEvent(m, (i+1)*2));
   }
  }catch(Exception e){e.printStackTrace();}
 }

 Sequence pohikolmkolad(int toonika) throws InvalidMidiDataException{
   Sequence s1=new Sequence(Sequence.PPQ, 4);  
   looKolmkola(s1.createTrack(), toonika);
   looKolmkola(s1.createTrack(), toonika+5); //kvart
   looKolmkola(s1.createTrack(), toonika+7); //kvint
   looBass(s1.createTrack(), toonika);
   looBass(s1.createTrack(), toonika-7); //IV ehk kvint alla
   looBass(s1.createTrack(), toonika-5); 
   return s1;
 }
 
 Sequence looSekvents() throws InvalidMidiDataException{
   return pohikolmkolad(helikorgused[helistik.getSelectedIndex()]);
 }
  
 void paneSoolo(){
   for(int i=0; i<raadionupud.length; i++){
     sequencer.setTrackMute(i, !raadionupud[i].isSelected() || !akord.isSelected());
     sequencer.setTrackMute(3+i, !raadionupud[i].isSelected() || !bass.isSelected());
   }
   sequencer.setTrackMute(6, !taust.isSelected());
 }
  
 void mangi(){
   sequencer.start();
   paneSoolo();
   sequencer.setTempoInBPM(tempo.getValue());
 }
 
 void alusta(){
  try{
  if(!jooksevHelistik.equals(helistik.getSelectedItem())){
   sequence=looSekvents();
   jooksevHelistik=helistik.getSelectedItem().toString();
  }
   if(muutuvRada!=null)sequence.deleteTrack(muutuvRada);
   muutuvRada=sequence.createTrack();
   int pohitoon=helikorgused[helistik.getSelectedIndex()];  
   int aste=1;
   if (duurid[duurinr]==1)aste=4;
   if (duurid[duurinr]==2)aste=5;
   looTaustaviiul(muutuvRada, pohitoon, aste);
   sequencer.setSequence(sequence);
   raadionupud[duurid[duurinr]].setSelected(true);
   duurinr=(duurinr+1)%duurid.length;
   mangi();
  }catch(Exception e){e.printStackTrace(); System.out.println(e);}
 }
 
 public void init(){
      Panel nupupaneel=new Panel(new GridLayout(1, 4));
      helistik=new JComboBox(helistikud);
      helistik.setSelectedIndex(3);
      nupupaneel.add(helistik);
      ButtonGroup nupugrupp=new ButtonGroup();
      for(int i=0; i<raadionupud.length; i++){
        raadionupud[i]=new JRadioButton(raadionupustring[i]);
        nupugrupp.add(raadionupud[i]);
        nupupaneel.add(raadionupud[i]);
      }
      raadionupud[0].setSelected(true);
      ruut.setSelected(true);
      Panel mangupaneel=new Panel(new GridLayout(1, 2));
      mangupaneel.add(nupp);
      mangupaneel.add(ruut);
      JPanel tempopaneel=new JPanel(new BorderLayout());
      tempopaneel.add(new JLabel("Tempo"), BorderLayout.WEST);
      tempopaneel.add(tempo, BorderLayout.CENTER);
      JPanel valikupaneel=new JPanel(new GridLayout(1, 3));
      valikupaneel.add(bass);
      valikupaneel.add(akord);
      valikupaneel.add(taust);
      akord.setSelected(true); 
      JPanel alumine=new JPanel(new GridLayout(3, 1));
      alumine.add(tempopaneel);
      alumine.add(mangupaneel);
      alumine.add(valikupaneel);
      getContentPane().add(alumine, BorderLayout.SOUTH);
      getContentPane().add(nupupaneel, BorderLayout.NORTH);
      nupp.addActionListener(this);
 }

  public void actionPerformed(ActionEvent e){
   try{
         sequencer=MidiSystem.getSequencer();
         sequencer.open();
         sequencer.addMetaEventListener(this);
         jooksevHelistik="";
         alusta();
   }catch(Exception ex){
     ex.printStackTrace();
     System.out.println(ex);
   }
  }
  
  public void meta(MetaMessage m){
    if(m.getType()==47 && ruut.isSelected()){
      alusta();
    } else {
      sequencer.close();
    }
  }
  public static void main(String argumendid[]){
    JFrame f=new JFrame("Saade");
    Noodirakend9a ap=new Noodirakend9a();
    ap.init();
    f.getContentPane().add(ap);
    f.pack();
    f.setVisible(true);
  }

}

Digitaalheli

	MIDI abil võib kord kokku pandud pillide helisid uuesti esitada, mängida saab vaid helikõrgusega ning kestusega. Olematute pillide hääli aga sünteesida ei õnnestu, samuti tuleb loobuda muudest heliefektidest. Kui palju kõlari membraan mingil hetkel välja venitatud on, seda otsustab meie eest helikaart või vastav süsteemiprogramm ning programmeerijal selle koha pealt kuigi palju kaasa rääkimist ei ole. "Hariliku" muusika loomise puhul ongi nii hea, sest saame rahumeeli noodikõrgustele ja -kestustele mõelda ning ei pruugi tehniliste andmete peale üleliia oma energiat kulutada. Kui aga tahta olemasolevat häält moonutada, kaja tekitada või hoopis uusi kõlasid luua, siis tuleb hakata mõtlema helilainete kuju peale. Edasi tuleb koostatud kuju kvantida, et saaks kusagil arvumassiivis hoida igale ajahetkele vastavat heligraafiku väärtust sarnaselt nagu matemaatikaski võime esitada funktsiooni x-idele vastavate y-ite massiivina, kus x-teljel oleks aeg ning y-il helirõhu kõrvalekalle tasakaaluasendist. Kvantimissagedusest (kandesagedusest) sõltub helikõvera edasiandmise kvaliteet. Mängitav kõver luuakse punkte ühendavatest sirgete järgi ning mida tihedamalt on punkte, seda lähedasemalt õnnestub säilitada esialgse lindistatud või välja arvutatud kõvera kuju. 
	Järgnevalt on püütud kokku panna võimalikult lihtne heli, mida ka kõrvaga kuulda oleks. Kvantimissageduseks on määratud 1000 mõõtmist sekundis. Helirõhud on kordamööda määratud maksimumile ja miinimumile. Sedasi on väljastatava heli sageduseks pool kvantimissagedusest ehk 500 hertsi.  Mõõtmistäpsuseks on üks bait ning heli väljastatakse ühe kanalina (mono, mitte stereo). Nõnda saab mõõtmistulemused panna ilusti baidimassiivi, kus igale kvandile vastab üks bait. AudioFormat'i abil määratakse, millist tüüpi andmed tulemas on. AudioSystem'i abil küsitakse operatsioonisüsteemilt SourceDataLine, kuhu saadetud heliandmed jõuavad lõpuks kasutaja kõrvadeni. Pärast voo avamist võime sinna andmeid saata kirjutades andmeid loodud voogu.

import javax.sound.sampled.*;
public class Piiks1{
 public static void main(String[] args) throws Exception {
   int     kandesagedus =1000;
   byte[] andmed=new byte[5*kandesagedus]; //5 sekundit
   for(int i=0; i<andmed.length; i++){
     if(i%2==0)andmed[i]=(byte) 127;
     else      andmed[i]=(byte)-128;                  
   }
   AudioFormat formaat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
               kandesagedus, 8, 1, 1, kandesagedus, false); //8bitine heli, 1, kanal, 1 bait raami kohta
   SourceDataLine line = (SourceDataLine) AudioSystem.getLine(
       new DataLine.Info(SourceDataLine.class, formaat,  AudioSystem.NOT_SPECIFIED)
   );
   line.open(formaat);
   line.start();
   line.write(andmed, 0, andmed.length);
   System.exit(0);
 }
}

Soovides koostada keerukama kujuga helilaineid, peab ka kandesagedus suurem olema. Kui soovida helilainele anda siinuse lainjat kuju, peab ühe täisvõnke kohta rohkem mõõtmisi olema kui et ainult üks laine harja ning teine põhja tarvis. All näites ~20 mõõtmist täisvõnke kohta peaks juba silmaga kaugelt vaadates enamjaolt sinusoidi sarnase kuju andma ning ka kõrvaga kuulates ei tohiks karedus kuigivõrd tunda anda. Liiatigi kui nii tehniliste vahendite kui inimkõrva poolsed tasandused juurde arvata. Ka suurema kvantimissageduse kuid üheaegsete mitmete keerulisemate helide korral ei mõõdeta ühe heli andmeid oluliselt täpsemalt. 
	Veidi seletusi arvutamise kohta. Kui iga baidi järjekorranumbrist võtta siinus ning selle väärtus panna kvandile, siis saaksime sinusoidi, mille lainepikkus oleks 2*Pi baiti ehk 10000 Hz kandesageduse puhul tuleks helisageduseks 10000/6,28  ehk ~ 1600 Hz. Helitugevus oleks aga imetilluke, sest kasutada olevast 256-ühikulisest (8-bitisest) piirkonnast on tarvitatud vaid vahemikku  miinust ühest üheni ehk siinuse väljundväärtust. Vahemikku saab kergesti suurendada, korrutades tulemuse kordajaga (siin juhul 100). Sel juhul kõigub väärtus miinus saja ning +100 vahel ning kasutatakse suurem osa (200/256) väljundpiirkonnast. Soovides helilainet kiiremini võnkuma panna, tuleb sama aja (baitide) jooksul suurendada arvu, millest siinust võetakse, kiiremini. Suurema kandesageduse puhul on aga ühe baidi jaoks eraldatud aeg väiksem. Baidi järjekorranumbri ja kandesageduse suhe aga näitab aega sekundites ning kui soovime, et kandesageduse muutumisel jääks helisagedus samaks, siis peab siinuse arvutamisel kandesagedus olema murrujoone all. Math.sin(2*Math.PI*nr/kandesagedus) puhul tehtaks parajasti üks võnge sekundis, et saaksime kõrgemat (kuuldavat) heli, selleks tuleb siinuse parameetriks olev väärtus soovitud sagedusega läbi korrutada. Nii saamegi lainekujulise heli arvutamiseks valemi
andmed[nr]=(byte)(100*Math.sin(2*Math.PI*nr*sagedus/kandesagedus));

import javax.sound.sampled.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class Piiks2Rakend extends Applet implements ActionListener{
 Button nupp=new Button("Piiksu");
 public Piiks2Rakend(){
   setLayout(new BorderLayout());
   add(nupp);
   nupp.addActionListener(this);
 }
 public void actionPerformed(ActionEvent e){
  try{
   int  kandesagedus =10000;
   int  sagedus=440;  //440 hertsi
   int  nr=0; 
   byte[] andmed=new byte[5*kandesagedus]; //5 sekundit
   while(nr<andmed.length){
     andmed[nr]=(byte)(100*Math.sin(2*Math.PI*nr*sagedus/kandesagedus));
     nr++;
   }
   AudioFormat formaat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
               kandesagedus, 8, 1, 1, kandesagedus, false); //8bitine heli, 1, kanal, 1 bait raami kohta
   SourceDataLine line = (SourceDataLine) AudioSystem.getLine(
       new DataLine.Info(SourceDataLine.class, formaat,  AudioSystem.NOT_SPECIFIED)
   );
   line.open(formaat);
   line.start();
   line.write(andmed, 0, andmed.length);
   line.close();
  }catch(Exception ex){ex.printStackTrace();}
 }
 public static void main(String[] argumendid){
   Frame f=new Frame("Piiks");
   Piiks2Rakend p=new Piiks2Rakend();
   f.add(p);
   f.setSize(200, 100);
   f.setVisible(true);
   p.actionPerformed(null);
 }
}

	Soovides heli panna ühtlaselt tõusma, peab selle sagedust püsiva ajavahemiku järel (kahe)kordistama. Kaks korda suurem helisagedus annab oktaavi jagu kõrgema heli. Nõnda võib heli arvutamisel siinuse parameetrina aja (andmebaidi järjekorranumbri) kordajana kasutada astmefunktsiooni, mille astendajat pidevalt kasvatada.

andmed[nr]=(byte)(100*Math.sin(2*Math.PI*nr*
          Math.pow(2, nr*tous_oktaavites/andmed .length)*sagedus/kandesagedus));

	Nõnda esineb aega tähistav number valemis kahes kohas. Esmalt annab sin (nr*tegur), kus tegur=2*Pi*sagedus/kandesagedus välja ühtlase sinusoidi, mille järgi võib pidevat samal kõrgusel püsivat tooni kuulata. Tooni pidevaks kergitamiseks tuleb siinuse parameeter läbi korrutada teisegi, nüüd juba ajast sõltuva koefitsiendiga, mille tulemusena saigi kokku eelpool toodud valem.  

import javax.sound.sampled.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class Piiks2aRakend extends Applet implements ActionListener{
 Button nupp=new Button("Piiksu");
 public Piiks2aRakend(){
   setLayout(new BorderLayout());
   add(nupp);
   nupp.addActionListener(this);
 }
 public void actionPerformed(ActionEvent e){
  try{
   int  kandesagedus =10000;
   int  sagedus=100;
   int  nr=0; 
   double tous_oktaavites=2;
   byte[] andmed=new byte[5*kandesagedus]; 
   while(nr<andmed.length){
     andmed[nr]=(byte)(100*Math.sin(2*Math.PI*nr*
          Math.pow(2, nr*tous_oktaavites/andmed.length)*sagedus/kandesagedus));
     nr++;
   }
   AudioFormat formaat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
               kandesagedus, 8, 1, 1, kandesagedus, false); 
   SourceDataLine line = (SourceDataLine) AudioSystem.getLine(
       new DataLine.Info(SourceDataLine.class, formaat,  AudioSystem.NOT_SPECIFIED)
   );
   line.open(formaat);
   line.start();
   line.write(andmed, 0, andmed.length);
   line.close();
  }catch(Exception ex){ex.printStackTrace();}
 }
 public static void main(String[] argumendid){
   Frame f=new Frame("Tõusev toon");
   Piiks2aRakend p=new Piiks2aRakend();
   f.add(p);
   f.setSize(200, 100);
   f.setVisible(true);
   p.actionPerformed(null);
 }
}

	Kvaliteetsema heli edastamiseks kaheksabitisest kvandist ei piisa. Jutu ning lihtsama heli saab selle abil küllalt hästi edasi anda, kuid linnuhäälte või orkestrimuusika puhul läheb märgatav kõrvale kuuldav osa kaduma. Kui mõõtmistäpsust suurendada, jääb rohkem algsest helist alles. Arvutusvalemite põhimõte jääb ikka samaks, kuid kui enne kaheksabitise heli korral oli kvandi suurimaks võimalikuks väärtuseks 127, siis kuueteistbitise juures annab maksimumi 32767. Seda tuleb valemite juures arvestada, muidu jääb hääl väga vaikseks või pole seda lihtsama helitehnika juures üldse kuulda. 
	Suurema mõõtmistäpsuse puhul tuleb lõivu maksta suurema mahuga. Kui ennist sai läbi aetud iga kvandi juures ühe baidiga, siis nüüd läheb vaja kahte. 

byte[] andmed=new byte[5*kandesagedus*mitmeBitineHeli/8]; //5 sekundit

	Kvandile vastavat numbrit arvutatakse sama valemi järgi, vaid valjust on niivõrd suurendatud, et see jääks parajalt kuulatavatesse piiridesse

int tulemus=(int)(valjus*Math.sin(Math.PI*nr*
       Math.pow(2, 0.5*Math.sin(nr*Math.PI/andmed.length))*sagedus/kandesagedus));

Baidimassiivis säilitamiseks ning edasiandmiseks tuleb täisarv kahe baidi vahel jagada. Siinses kodeeringus pannakse ettepoole viimane (madalam) bait, taha esimene (kõrgem) bait, kuid sõltuvalt formaadist võib järjekord ka teistpidine olla. Viimase baidi kättesaamiseks jäetakse alles vaid sellele baidile vastavad bitid, muud asendatakse nullidega. Tüübimuunduse abil baidiks muundamise järel jõuabki soovitud väärtus sihtmassiivi kohale. Täisarvust teise baidi kättesaamiseks nihutatakse see kõigepealt kaheksa biti jagu paremale esimese baidi kohale ning tehakse eelpool toodud tehe. Arvus 0xFF ehk 255 on esimese baidi kõik bitid ühed ning & tehtega algsest arvust vaid need ühed alles jättes, millele 255 ühed vastu panna on, tulebki kokku ühebaidiline väärtus. Nüüd piisab see vaid andmemassiivi üle kanda, et seda pärast kuulata annaks. 

     andmed[nr++]=(byte)(tulemus & 0xFF);       //viimane bait
     andmed[nr++]=(byte)(tulemus >> 8 & 0xFF);  //eelviimane bait

Ning näide ise:

import javax.sound.sampled.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class Piiks3Rakend extends Applet implements ActionListener, Runnable{
 Button nupp=new Button("Piiksu");
 Checkbox korda=new Checkbox("Korda");
 public Piiks3Rakend(){
   setLayout(new BorderLayout());
   add(nupp);
   add(korda, BorderLayout.SOUTH);
   korda.setState(true);
   nupp.addActionListener(this);
 }
 public void actionPerformed(ActionEvent e){
   new Thread(this).start();
 }
 public void run(){
  try{
   int  kandesagedus =44100;
   int  sagedus=400;
   int  mitmeBitineHeli=16;
   int  kanaliteArv=1;
   int  valjus=7000;  //max 32767
   int  nr=0; 
   byte[] andmed=new byte[5*kandesagedus*mitmeBitineHeli/8]; //5 sekundit
   while(nr<andmed.length){
     int tulemus=(int)(valjus*Math.sin(Math.PI*nr*
       Math.pow(2, 0.5*Math.sin(nr*Math.PI/andmed.length))*sagedus/kandesagedus));
     andmed[nr++]=(byte)(tulemus & 0xFF);       //viimane bait
     andmed[nr++]=(byte)(tulemus >> 8 & 0xFF);  //eelviimane bait
        }
   AudioFormat formaat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
               kandesagedus, mitmeBitineHeli, kanaliteArv, 
               kanaliteArv*mitmeBitineHeli/8, kandesagedus, false); 
   SourceDataLine line = (SourceDataLine) AudioSystem.getLine(
       new DataLine.Info(SourceDataLine.class, formaat,  AudioSystem.NOT_SPECIFIED)
   );
   line.open(formaat);
   line.start();
   do{
     line.write(andmed, 0, andmed.length);
   }while(korda.getState());
   line.close();
  }catch(Exception ex){ex.printStackTrace();}
 }
 public static void main(String[] argumendid){
   Frame f=new Frame("Piiks");
   Piiks3Rakend p=new Piiks3Rakend();
   f.add(p);
   f.setSize(200, 100);
   f.setVisible(true);
   p.actionPerformed(null);
 }
}

Kokkuvõte

	Java muusikavõimalused on tasapisi laienenud ning alates versioonist 1.3 õnnestub nii MIDI vahenditega orkestrilugusid kokku panna kui ise digitaalhelisid luua ja töödelda. Nagu mujal, nii ka siin pole programmeerimiskeel imevahend, selle kaudu lihtsalt õnnestub arvuti vastavatele seadmetele ligi pääseda ning soovitud helisid salvestada või kuuldavale tuua. Muusikalised ja matemaatilised tagamaad tuleb ikka enesel läbi mõelda ning pärast esmavahenditega tutvumist tulebki rakenduse loomisel enam sellele pühenduda. Lugudele saate koostamisel tuleb mõelda harmoonia ning taustalõikude peale neid sobivasse helistikku paigutades. Olenevalt muusikastiilist saab sageli kolme põhiduuriga enamikuga soovitust toime ning alles ilustuste ja kaunistuste juures tuleb muud oskused ja vahendid meelde  tuletada ning arvutile selgeks teha. Olemasolevat noodifaili muutes tuleb arvestada, millistel radadel asuvate häältega me mida ette võtta soovime. 
	Digitaalhelis tuleb helilaine kuju ise välja arvutada. Nõnda kulub küll juba lihtsate häälte tekitamiseks paras kogus matemaatikat ning vähegi ilusamate helide loomiseks tuleb hulga arvutada ning tarkadest raamatutestki tarkust juurde ammutada, kuid põhimõtteliselt on võimalik luua või olemasolevatest kokku panna pea iga heli, mis vähegi ettekujutatav võiks olla.