03 novembro 2008

Tocando arquivos MIDI com o padrão Proxy


Há um tempo atrás (especificamente 2004), publiquei um tutorial no site da comunidade do JavaFree.org, cujo título era Desenvolvendo um sequenciador MIDI com java. Agora, estudando um pouco mais o padrão de projeto Proxy, e fazendo alguns testes, vi que a api java.sound possui um recurso para desabilitar/habilitar trilhas em uma música.

Vamos ver como isso funciona?

O padrão funciona de forma a definir um objeto transparente que procure outro objeto, através de uma implementação interna. Com isso temos benefícios como desacloplamento, controle de acesso e cache, por exemplo.

Utilizaremos este padrão para executar uma música MIDI, e disponibilizar a opção de tocar as faixas da mesma separadamente. Primeiramente temos a classe que é o RealSubject da nossa pequena aplicação, ela é responsável para interação com o dispositivo MIDI do sistema operacional. Ela contém todas as funções para a execução, parada ou desabilitação das suas trilhas.

RealMidi.java

import java.io.File;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;

public class RealMidi implements IMidi {

private Sequencer sequencer;
private Sequence sequence;

public RealMidi(String arquivo) {
try {
sequencer = MidiSystem.getSequencer();
sequence = MidiSystem.getSequence(new File(arquivo));
sequencer.open();
sequencer.setSequence(sequence);
} catch (Exception e) {
e.printStackTrace();
}
}

public int getTracks(){
return sequence.getTracks().length;
}

@Override
public void enable(int numero) {
sequencer.setTrackMute(numero, !sequencer.getTrackMute(numero));
if (sequencer.getTrackMute(numero)) {
System.out.println(numero + " [OFF]");
} else {
System.out.println(numero + " [ON]");
}
}

@Override
public void start() {
sequencer.start();
}

@Override
public boolean isRunning() {
return sequencer.isRunning();
}

@Override
public void stop() {
sequencer.stop();
sequencer.close();
}

}

Percebemos que esta classe implementa uma interface, que também é implementada pela classe que será o nosso proxy.

IMidi.java

public interface IMidi {

void enable(int numero);

void start();

boolean isRunning();

void stop();

}

Temos a classe que será o proxy e que irá, além de repassar chamadas para a classe
RealMidi, fazer um cache do arquivo MIDI. Isso evitará que o mesmo seja acessado diversas vezes desnecessariamente. isto pode ser verificado nas operações start, stop e isRunnig.

ProxyMidi.java

public class ProxyMidi implements IMidi{

private RealMidi realMidi;
private String arquivo;

public ProxyMidi(String arquivo) {
this.arquivo = arquivo;
}

@Override
public void enable(int numero) {
if (numero < realmidi ="=" realmidi =" new">

Agora temos um sequenciador implementado com uma função a mais, que é a de escolher as trilhas que devem ser desabilitadas. Agora é só fazer uma classe que seja o cliente do padrão.

JPlayer.java

import java.util.Scanner;

public class JPlayer {

public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);
IMidi midi = new ProxyMidi("sua_musica.mid");
midi.start();

System.out.println("Informe o número da trilha para habilitar/desabilitar a saída de som:");
int numero;
while (midi.isRunning()) {
numero = teclado.nextInt();
midi.enable(numero);
}
midi.stop();
}
}

Bom, como o resultado disso não dá pra colocar aqui (só ouvindo mesmo), então fica o convite para que você mesmo teste, ok ?

Indicações:

[1] http://dofactory.com/Patterns/PatternProxy.aspx
[2] http://www.javafree.org/javabb/topic-3102-Desenvolvendo+um+sequenciador+MID+com+java
[3] http://www.midinet.com.br/ (Para quem não tem arquivos MIDI)

Um comentário:

Fernando Scherrer disse...

Haw!

  Só uma sugestão: coloca os códigos entre tags <pre></pre>, assim não fica com a identação zuada (se você copiar de um lugar [eclipse, netbeans, etc] que esteja identado) fica mais fácil de ler.
  (exemplo aqui)
  E se quiser deixar mais pro tem uns esquemas (google it!) pra syntax highlight.

Valewz!? Gostei do post.