Java Midi Player





Die auf dieser Seite vorgestellten MIDI Player sind keine Applets, sondern Java Anwendungen. Sie müssen sich also das ganze Paket (8 MB, einschliesslich Source Code und drei Soundfont Dateien) herunterladen und auf Ihrem Rechner installieren. Sie laden es von einem Linux Server herunter, insofern ist die Virus Gefahr relativ klein. Trotzdem kann ich keine Garantie übernehmen. Wenn Sie irgendwas aus dem Internet herunterladen, sollten Sie es immer mit einem Antivirus Programm prüfen. Nach Auspacken der Datei mit z.B. Winzip oder gzip werden Sie 8 Subdirectories vorfinden:

Player1 der einfachste Player
Player2 schon etwas komfortabler
Player3 das HarfeSoft Flaggschiff, der kann (fast) alles
Player4 Ein von mir bearbeiteter Midi Player aus den Java Sun Demos
JavaSound Komplette Sound Maschine einschliesslich Recorder, AudioPlayer und vieles andere aus den Java Sun Demos.
Midi Eine kleine Sammlung von Midi Files aus Jazz und Klassik
Soundfonts Die drei Soundfonts aus der Beatnik Headspace Maschine
Doc Dokumentation, Sie befinden sich gerade in dieser Datei.

Der Aufruf der Anwendung erfolgt aus dem MSDOS Eingabefenster und besteht aus einer einzigen Zeile. Hierzu muss auf Ihrem Rechner ein Java2 Runtime Enviroment (JRE) installiert sein. Wenn Sie einen Java fähigen Browser haben, dann ist dieses JRE bei Ihnen bereits installiert. Auf meinem Window Rechner ist es unter C:\Programme\Java\j2re1.4.2_02\ abgelegt. Daher heisst die Befehlszeile bei mir:

C:\Programme\Java\j2re1.4.2_02\bin\java MidiPlayer1

Falls Sie kein JRE finden, müssen Sie es bei Sun herunterladen und installieren. In Linux und Unix Systemem befindet sich das JRE meistens im Pfad /usr/local/java/.

Wer sich für Java Sound Applikationen interessiert, sollte auch unbedingt die WEB Seite von Matthias Pfisterer besuchen. Hier können Sie wirklich alles lernen, was im Bereich Java Sound möglich ist. Ich habe es auch beim ihm gelernt.

Unser erster und einfachster Player besteht nur aus 4 Buttons. Beim Laden des Programmes importieren wir das Midi System:

import javax.sound.midi.MidiSystem;

Ein typisches System ist in der Abbildung gezeigt. Ein Midi File ist eine Datei, in dem jeder Ton durch zwei Bytes dargestellt wird. Enthalten in diesen zwei Bytes sind Angaben über die Frequenz des Tones, die Lautstärke, das Anschlagverhalten, das Ausschwingverhalten, das Instrument und die Länge des Tones. Eine Pause ist übrigens auch ein Ton mit Lautstärke null. Dieser Befehl wird vom Sequencer analysiert und über die Kombination von Transmitter und Receiver an den Synthesizer weitergegeben. Dieser Synthesizer benutzt die Soundfont Dateien, um aus diesen Informationen einen Ton zu erzeugen und auf den Ausgang (Verstärker, Lautsprecher) zu geben. Wer sich für weitere tiefergehende Erläuterungen über das Midi Format interessiert, sei auf unsere Midi Seite verwiesen. Die im folgenden angegebenen Java Befehle sind natürlich nur symbolisch gemeint. Um die Details zu erfahren, müssen Sie im Source nachblättern. Wir erzeugen uns den Sequencer durch

sequencer = MidiSystem.getSequencer();

und öffnen ihn

sequencer.open();

Als nächstes Objekt definieren wir den File Chooser aus dem Java Swing Paket. Dieser File Chooser wurde mit einer Dialog- Box installiert, sodass die Auswahl eines Files nochmal bestätigt werden muss

chooser = new JFileChooser();

Wir wählen ein File aus und veranlassen das Midi System, daraus eine Sequence herzustellen:

file = chooser.getSelectedFile();

sequence = MidiSystem.getSequence(file);

Wir übergeben diese Sequence dem Sequencer und starten den Sequencer:

sequencer.setSequence(sequence);

sequencer.start();

Bei dieser soeben beschriebenen Verfahrensweise ist der Sequencer ein abgeleitetes Objekt vom Synthesizer. Man braucht den Synthesizer dann nicht extra zu definieren. Ist das nicht der Fall, dann muss der Synthesizer extra instanziiert werden und Sequencer und Synthesizer miteinander verbunden werden. Das kann mit dem folgenden kleinen Source Code gemacht werden (siehe auch die entsprechende Abbildung oben auf dieser Seite):

synthesizer = MidiSystem.getSynthesizer();

synthesizer.open();

receiver = synthesizer.getReceiver();

transmitter = sequencer.getTransmitter();

transmitter.setReceiver(receiver);

sequencer.start();

Beim Drücken des Stop- und Exit- Buttons wird beide Male der Sequencer gestoppt

sequencer.stop();

Bei der Exit- Taste wird ausserdem der Sequencer geschlossen,

sequencer.close();

Schliessen Sie den Midi Player also grundsätzlich über den Exit- Button. Eine weitere Schwierigkeit tritt auf, wenn das Playback des Files beendet ist. Der Sequencer stoppt nicht von alleine. Der Benutzer muss jedes Mal noch den Stop- Button betätigen. Passiert dieses nicht und wir starten den Sequencer, ohne ihn vorher gestoppt zu haben, kann das auf einigen Systemen zu Problemen führen. Der übrige Code des Programms erzeugt den Aufbau des Fensters und dient insbesondere für die Behandlung der möglichen Fehler im Ablauf des Programms (Exceptions). Ohne eine derartige Fehlerabfrage würde Ihr Betriebssystem bei jeder kleinsten Gelegenheit den Löffel aus der Hand legen. Hierbei müssen Sie bedenken, dass das Programm tief in die Hardware Ihres Rechners eingreift.

Im MidiPlayer2 haben wir die Funktionalität um einiges erweitert. Bei Drücken des Suspend Buttons wird das Playback des Files unterbrochen und die Zeit notiert

timePosition = sequencer.getMicrosecondPosition();

sequencer.stop();

Bei Resume findet der umgekehrte Prozess statt:

sequencer.setMicrosecondPosition(timePosition);

sequencer.start();

Dieses führt dazu, dass man nach einer Unterbrechung mit mehrmaligem Drücken der Resume- Funktion immer wieder an die gleiche Stelle des Files zurückkommt. Weiterhin sehen wir, dass der Sequencer die Midi Befehle an den Synthesizer nicht einfach nur weitergibt, sondern er muss sie auch zur richtigen Zeit weitergeben. Ob die Uhr des Sequencers wirklich eine Genauigkeit von Mikrosekunden hat, hängt vom Betriebssystem ab. Millisekunden reichen allerdings bei nicht zu langen Midi Files im allgemeinen aus.

Eine Midi Datei besteht nicht nur aus Befehlen zur Erzeugung von Tönen, sondern beinhaltet auch sogenannte MetaEvents. Diese dienen zur Dokumentation allgemeinerer Art wie Autor des Files, Copyright Informationen, Beschreibung des Files usw. Wieviele solcher Informationen auf dem File sind, hängt vom Autor ab. Einige dieser Informationen werden vom Sequencer oder der Sequence gespeichert,

fileInfo[1] = sequencer.getMicrosecondLength();

fileInfo[2] = sequence.getDivisionType();

fileInfo[3] = sequence.getResolution();

andere Informationen müssen von den einzelnen Spuren (Tracks) des Files extrahiert werden.

Ein Midi File besteht im allgemeinen aus mehreren Tracks, wobei oft auf einer Track alle Noten eines bestimmten Instruments gespeichert werden. Dieses entspricht in etwa den professionellen Mehrspuraufnahmen mit mehreren Mikrofonen. Der Sequencer kann dann jede Track auf einen Kanal des Synthesizers geben. Mit der folgenden kleinen Loop analysieren wir die ersten Befehle auf jeder Spur und suchen insbesondere nach MetaMessages.

  • Track[] tracks = sequence.getTracks();
  • for(int nTrack=0; nTrack<tracks.length(); nTrack++){
  • Track track = tracks(nTrack);
  • for(int nEvent=0; nEvent<track.get(nEvent);{
  • MidiEvent event = track.get(nEvent);
  • MidiMessage message = event.getMessage();
  • if(message instanceof MetaMessage){
  • MetaMessage meta = (MetaMessage)message;
  • fileInfo[4] = meta;
  • ..........
  • }}}

Diese Loop wird abgebrochen, wenn unser Informationsarray vollgeschrieben ist.

Zumindest im Prinzip ist im Java Sound Paket vorgesehen, dass man seinen Output auf jeden im Computer vorhandenen Midi Device geben kann. Die Information über alle vorhandenen Devices bekommen wir mit

MidiDevice.Info[] aInfos = MidiSystem.getMidiDeviceInfo();

Einzelne Informationen erhält man mit der kleinen Loop über alle Devices. Wir können einige Informationen herausziehen und bei Selektion in der Liste uns diesen info merken.

  • for(int i=0; i<aInfos.getLength(); i++){
  • MidiDevice device = MidiSystem.getDevice(aInfos[i]);
  • name = aInfos[i].getName();
  • vendor = aInfos[i].getVendor();
  • .............;
  • if(selected) MidiDevice.Info info = aInfos[i];
  • }

Diesen Device kann man jetzt als Standard Midi Device benutzen. Wie man sieht, benutzen wir den Java Sequencer und verbinden den Transmitter des Sequencers mit dem Receiver des MidiDevices.

MidiDevice midiDevice = MidiSystem.getMidiDevice(info);

midiDevice.open();

Receiver receiver = midiDevice.getReceiver();

Transmitter transmitter = sequencer.getTransmitter();

transmitter.setReceiver(receiver);

sequencer.start();

Dieses Verahren wird vom Compiler anstandslos geschluckt und auch der Interpreter hat nichts auszusetzen. Leider funktioniert es noch nicht. Egal welchen Device ich auswähle, man hört leider immer nur den Java Synthesizer. Ob das ein Bug im Java Sound Paket oder in meinem Programm ist, weiss ich noch nicht.

Endlich kommen wir zu unserem Flaggschiff, dem MidiPlayer3 oder auch HarfePlayer. Zunächst haben wir die Buttons statt mit Text durch ImageIcons verziert. Damit weiterhin die Bedeutung der Buttons eindeutig ist, wurden sie mit einem ToolTipText versehen. Dieser Text wird nur angezeigt, wenn die Maus über den Button bewegt wird. Hinter dem Violinschlüssel verbirgt sich ein weiterer FileChooser, mit dem man aus den drei von Sun zur Auswahl gestellten Soundbanks eine auswählen kann.

Die Qualität dieser Soundfonts hängt extrem von der musiklaischen Stilrichtung ab. Für Jazz und Unterhaltungsmusik sind sie meiner Meinung nach nicht so schlecht, für die klassische Musik, insbesondere mit Solo- Streichern, sind sie völlig unbrauchbar. Das Klavier überdeckt in der Lautstärke die Streicher komplett, die Violinen und Violas klingen wie Mundharmonikas. Das Cello erinnert zumindest in der Maxi- Soundbank an Kloakengeräusche. Hier ist man einfach von einigen hervorragenden SF2- Soundbanks der Creative- Soundkarten weit bessere Qualität gewohnt. Wir arbeiten hart an der SF2- Unterstützung des Java Sound Paketes. Ob Sie daher mit der jetzigen Form von Java Sound Anwendungen zufrieden sind, muss ich Ihnen überlassen. Die Soundbank middle.gm ist insgesamt gesehen noch die beste. Das Laden der Soundfonts ist denkbar einfach.

gmChooser = new JFileChooser();

Soundbank soundbank;

File file = gmChooser.getSelectedFile();

soundbank = MidiSystem.getSoundbank(file);

Die wichtigste Erweiterung gegenüber dem MidiPlayer2 ist die VolumeControl, die sich hinter dem nach oben zeigenden Pfeil versteckt

Der Java Synthesizer arbeitet mit 16 Kanälen. Daher haben wir je 16 Slider für die Lautstärke Einstellung der Kanäle (untere Reihe) und für die Stereo Einstellung eingeführt (obere Reihe). Der Slider ganz rechts wirkt gleichermassen auf die Lautstärke Einstellung aller Kanäle. Der horizontale Slider ganz oben zeigt den Fortschritt des Playbacks an. Die exakte Zeit in Sekunden wird auch noch auf dem nebenstehenden Button angegeben. Diese Informationen werden allerdings nur aktualisiert, wenn der Button gedrückt wird. Bedenken Sie, dass Java nur einen Interpreter hat. Im Vergleich zu wirklich compilierten Programmen erwartet man eine Reduzierung der CPU Power um einen Faktor 20. Bei älteren Rechnern führt daher ein permanenter Update des Zeitablaufes zu unangenehmen Störungen des Playbacks, da der Sequencer jedes mal in seiner Arbeit gestört wird. Dasselbe gilt natürlich, wenn andere CPU intensive Anwendungen parallel auf Ihrem Rechner aktiv sind. Diesen Slider können Sie natürlich auch benutzen, um in Ihrem Midi File vor- und zurückzublättern. Beim Start des Midi Files setzen wir die Werte der Slider auf die Default Werte

textButton.setText(" 0");

progSlider.setValue(0);

MidiChannel channels[];

channels = synthesizer.getChannels();

  • for(int i=0; i<16;i++){
  • gainSlider[i].setValue((int)(channels[i].getController(7)*100./127));
  • panSlider[i].setValue((int)(channels[i].getController(10)*100./127));
  • }

Sämtlichen Slidern wurde ein ChangeListener zugeordnet. Falls dann während des Playbacks die Slider betätigt werden, teilt uns der ChangeListener dieses mit und wir können reagieren mit

channels[i].controlChange(7,(int)(slider.getValue()*127./100));

channels[i].controlChange(10,(int)(slider .getValue()*127./100));

Wir verwenden hierbei die in der Midi Spezifikation vorgesehenen ControllerMessages. Der Controller mit der Nummer 7 regelt die Lautstärke eines speziellen Kanals, der Kontroller mit der Nummer 10 die Stereo- Einstellung. Die Werte dieser Controller können zwischen 0 (Lautstärke null bzw Stereo links) und 127 (Lautstärke maximal bzw Stereo rechts) liegen. Derartige Controller Events kommen in guten Midi Files natürlich häufig vor. Diese Lautstärke- und Stereo- Regelung funktioniert bei mir im MSWindow 98 und XP einwandfrei, in Linux- Systemen dagegen nur beim ersten Laden nach dem Booten. Offensichtlich werden hier die Midi Resourcen beim Exit aus dem Player nicht vollständig zerstört. Genaueres allerdings weiss ich selbst nicht. Für weitere Details müssen wir in diesem Zusammenhang auf den Source Code verweisen. Dieser Source Code enthält keine Kommentare, ist aber, wie ich meine, übersichtlich und geradeaus programmiert und sollte keine Verständnisschwierigkeiten bieten.

Die beiden restlichen Player in diesem Paket sind ursprünglich aus den Java Sun Demos. Die Bedienung ist selbsterklärend, in der Programmierung sind sie so anspruchsvoll, dass wir sie hier nicht diskutieren können. Im Prinzip sind es natürlich die gleichen Algorithmen, die wir schon diskutiert haben. Nicht alles funktioniert auf jedem System. Im Java Sound Demo ist nicht nur ein Multimedia Player enthalten (Juke Box), der (fast) alle Formate versteht, sondern auch ein Recorder, ein Programm zum Testen des Synthesizers und eine Groove Box zum Austesten von Schlagzeugbegleitung. Wie Sie in den Abbildungen sehen, ist das Layout dieser Player sehr gross ausgelegt, es überdeckt fast den ganzen Bildschirm.

Und nun wünsche ich viel Spass mit den Java Multimedia Playern. Falls Sie Fehler finden oder wichtige Erweiterungen entwickeln, lassen Sie es mich wissen. Vielen Dank im voraus.

Copyright 2003 by HarfeSoft (Harm Fesefeldt Software) <Harm.Fesefeldt@physik.rwth-aachen.de>