O Pacote Java MIDI
Roteiro

Principais Classes e Interfaces do Pacote MIDI

Acessando Recursos MIDI

Carregando Seqüências MIDI

Transmitindo e Recebendo Mensagens MIDI

Gravando e Editando Seqüências MIDI

Recursos Avançados de Sequencer

Sintetizando Som
Principais Classes e
Interfaces do Pacote MIDI
Classe MidiMessage

MidiMessage é uma classe abstrata que representa
uma mensagem MIDI pura;

Possui três subclasses:

ShortMessages : tipo mais comum, possui um byte de
status e no máximo dois de dados (Ex.: Note On);

SysexMessages: podem possuir muito bytes e contém
instruções especificas do fabricante;

MetaMessages: ocorre apenas em arquivos MIDI, contem,
por exemplo, configurações de tempo;
Classe MidiEvent

MidiEvent é uma classe que engloba mensagens
MIDI puras junto com informação de tempo;

A classe MidiEvent possui métodos para especificar e
também obter informação de timestamp:
long getTick ()
void setTick (long tick)
Classe Sequence


Sequence representa uma composição musical que
pode ser lida de um arquivo ou criada em tempo
real;
É composto por uma coleção de Tracks onde estão
armazenados os MidiEvents;
Sequence
Tracks
MidiEvents
Interface MidiDevice



Objetos que implementem a interface MidiDevice são
capazes de enviar e receber mensagens MIDI;
Possui métodos para abrir e fechar um dispositivo;
Inclui uma classe interna, MidiDevice.Info, que
fornece uma descrição textual do dispositivo;
Interfaces Transmitter e Receiver


Dispositivos MIDI possuem objetos transmissores e
objetos receptores;
Objetos transmissores implementam a interface
Transmitter e objetos receptores implementam a
interface Receiver;
Interface Sequencer



Um sequenciador é um dispositivo para captura e
execução de seqüências de eventos MIDI;
Normalmente, possui tanto transmissores quanto
receptores;
Sequenciadores implementam a interface Sequencer
que é uma subinterface de MidiDevice;
Interface Synthesizer



Sintetizadores implementam a interface Synthesizer
(subinterface de MidiDevice);
Um sintetizador possui canais, representados por
objetos que implementem a interface MidiChannel;
Normalmente um sintetizador gera sons em resposta
a mensagens enviadas aos seus receptores;
Acessando Recursos MIDI
Classe MidiSystem


Assim como a classe AudioSystem, permite descobrir
e acessar os dispositivos instalados no sistema;
Permite obter os seguintes recursos:






Sequenciadores;
Sintetizadores;
Transmissores;
Receptores;
Dados a partir de arquivos MIDI;
Dados a partir de arquivos de soundbank;
Obtendo Dispositivos Padrões


Uma aplicação típica inicia obtendo os dispositivos
necessários (sequenciadores, sintetizadores, etc.);
A classe MidiSystem inclui métodos para obter
dispositivos padrão:




static
static
static
static
Sequencer getSequencer ();
Synthesizer getSynthesizer ();
Receiver getReceiver ();
Transmitter getTransmitter ();
Obtendo Dispositivos Instalados

A classe MidiSystem possui um método para obter
informações sobre os dispositivos instalados:
static MidiDevice.Info[] getMidiDeviceInfo ()

Para obter um dispositivo deve-se utilizar o método:
static MidiDevice getMidiDevice (MidiDevice.Info
info)
Exemplo 1:
Obtendo os sintetizadores instalados
Vector synthInfo;
MidiDevice device;
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo ();
for (int i = 0; i < infos.length; i++) {
try {
device = MidiSystem.getMidiDevice (infos[i]);
} catch (MidiUnavailableException e) {
// tratar ou throw a exceção
}
if (device instanceof Synthesizer) {
synthInfos.add (infos[i]);
}
}
Abrindo Dispositivos

Para reservar um dispositivo : método open()
da interface MidiDevice:
if (!device.isOpen ()) {
try {
device.open ();
} catch (MidiUnavailableException e) {
// tratar ou throw a exceção
}
}
Carregando Seqüências MIDI
Carregando um Arquivo MIDI

Duas maneiras:

Carregar um arquivo MIDI em um InputStream e
depois usar:
Sequencer.setSequence (InputStream stream)

Criar, explicitamente, um objeto Sequence e
depois carregá-lo em um Sequencer
(Necessário quando se deseja editar uma
seqüência MIDI)
Exemplo 2: Criando e Carregando um
Objeto Sequence
try {
File midiFile = new File (“seq1.mid”);
Sequence seq = MidiSystem.getSequence (midiFile);
sequencer.setSequence (seq);
} catch (Exception e) {
// tratar ou throw a exceção
}
Transmitindo e Recebendo
Mensagens MIDI
Dispositivos, Receptores e
Transmissores



Diferentes MidiDevice podem ser interconectados
possibilitando que dados fluam entre eles;
MidiDevice possui um ou mais objetos auxiliares que
implementam as interfaces Transmitter ou
Receiver;
Cada transmissor só pode ser conectado a um receptor
e vice-versa;
Conectando-se a um Dispositivo

A interface Transmitter possui um método que
permite estabelecer a conexão com o Receiver para
o qual serão enviadas as mensagens:
void setReceiver (Receiver receiver)

Após terminada a conexão deve-se invocar o método
close() de Transmitter e Receiver para que os
mesmos sejam liberados;
Exemplo 2: Conectando um Sequenciador
a um Sintetizador
Sequencer seq;
Transmitter seqTrans;
Synthesizer synth;
Receiver synthRcvr;
try {
seq = MidiSystem.getSequencer ();
seqTrans = seq.getTransmitter ();
synth = MidiSystem.getSynthesizer ();
synthRcvr = synth.getReceiver ();
seqTrans.setReceiver (synthRcvr);
} catch (MidiUnavailableException e) {
// tratar ou throw a exceção
}
Exercício 1

Abrir e tocar um arquivo MIDI;
Como enviar a mesma mensagem
para diferentes dispositivos ?


Basta usar mais de um transmissor e mais de um
receptor;
MidiDevice possui métodos para descobrir quantos
transmissores e receptores um dispositivo suporta:
int getMaxTransmitters ()
int getMaxReceivers ()
Exemplo 3: Conectando um Porta de Entrada a
um Sequenciador e a um Sintetizador
Sequencer seq;
Synthesizer synth;
MidiDevice inputPort;
// Obter e abrir os três dispositivos
Transmitter inPortTrans1, inPortTrans2;
Receiver synthRcvr, seqReceiver;
try {
inPortTrans = inputPort.getTransmitter ();
synthRcvr = synth.getReceiver ();
inPortTrans1.setReceiver (synthRcvr);
inPortTrans2 = inputPort.getTransmitter ();
seqRcvr = seq.getReceiver ();
inPortTrans2.setReceiver (seqRcvr);
} catch (MidiUnavailableException e) {
// tratar ou throw a exceção
}
Quando Usar um Sequenciador


É possível enviar mensagens MIDI diretamente para
um dispositivo sem usar um sequenciador;
Essa técnica pode ser utilizada quando o próprio
programa cria as mensagens em tempo real;


Ex.: Programa que simula um piano
Sequenciadores são utilizados para lidar com dados
lidos de um arquivo MIDI e também para gravar
dados MIDI;
Enviando Mensagem para um
Receptor sem Usar um Transmissor

A interface Receiver contém um método que envia
mensagens para o receptor:
void send (MidiMessage message, long timeStamp)

Criar um objeto ShortMessage e usar o método:
Void setMessage (int command, int channel,
int data1, int data2)
Exemplo 4: Enviando uma Mensagem
sem Usar um Transmissor
ShortMessage msg = new ShortMessage ();
// Tocar a nota Middle C (60) com velocidade = 93
msg.setMessage (ShortMessage.NOTE_ON, 0, 60, 93);
long timeStamp = -1;
Receiver rcvr = MidiSystem.getReceiver ();
Rcvr.send (msg, timeStamp);
Exercício 2

Enviar mensagens MIDI diretamente
para um sintetizador sem usar um
Sequenciador
Roteiro

Principais Classes e Interfaces do Pacote MIDI

Acessando Recursos MIDI

Carregando Seqüências MIDI

Transmitindo e Recebendo Mensagens MIDI

Gravando e Editando Seqüências MIDI

Recursos Avançados de Sequencer

Sintetizando Som
Gravando e Editando
Seqüências MIDI
Tracks



Arquivos MIDI são organizados em tracks;
Normalmente cada track contem as notas de um
único instrumento (não é obrigado pelo padrão);
Arquivos MIDI são organizados em uma hierarquia de
três níveis:



Sequence;
Track;
MidiEvents;
Sequence
Tracks
MidiEvents
MidiEvents e Ticks



Em um MidiEvent o tempo é expresso tendo como base o
conceito de tick;
A duração de um tick varia de acordo com a sequência MIDI e
seu valor é armazenado no cabeçalho de um arquivo MIDI;
O tamanho de um tick pode ser dado em duas unidades:



Pulsos por quarto de nota (PPQ)
Ticks por frame (SMPTE)
Na API Java os valores de tick medem tempo cumulativo;
Gravando e Salvando
Sequences (1/2)
1.
2.
3.
Obtenha um Sequencer através de MidiSystem;
Estabeleça a conexão entre o Sequencer e o objeto que
transmitirá as mensagens MIDI;
Crie um novo objeto Sequence:
Sequence (float divisionType, int resolution )
Sequence (float divisionType, int resolution,
int numTracks )
4.
Crie um objeto Track caso isso não seja feito no construtor:
Sequence.createTrack();
Gravando e Salvando
Sequences (2/2)
5.
Relacione o objeto Sequence criado com o Sequencer usado:
Sequencer.setSequence(Sequence sequence)
6.
Chame o método Sequencer.recordEnable ();
7.
Chame o método Sequence.startRecording ();
8.
9.
Quando terminar, chame Sequencer.stop () ou
Sequencer.stopRecording ();
Salve o objeto Sequence gravado usando
MididSystem.write();
Editando uma Seqüência


Objetos do tipo Sequence permitem que sejam
adicionados ou removidos Tracks:

Track createTrack ()

Boolean deleteTrack (Track track)
As Tracks são armazenadas em um objeto Sequence
através de um Vector;
Editando uma Seqüência


Os MidiEvents contidos em uma Track também são
armazenados em um Vector;
Os métodos de Track são:





boolean add (MidiEvent event)
MidiEvent get (int index)
boolean remove (MidiEvent event)
int size ()
long ticks ()
Exercício 3

Criar alguns eventos MIDI, com mensagens
Note On e Note Off, e gravá-los em um SMF
(Usar PPQ com, por exemplo, resolução 12;
Recursos Avançados de
Sequencer
Posição de uma Sequence

Obtendo a posição corrente do Sequencer em um
Sequence:



Long getTickPosition ()
Long getMicrosecondPosition ()
Movendo para um ponto arbitrário em um objeto
Sequence:


void setTickPosition (long Tick)
void setMicrosecondPosition (long
microsecond)
Mudando a Velocidade de Execução


A velocidade de uma seqüência é indicada pelo seu
tempo;
Pode-se mudar o tempo de uma seqüência através de
eventos MIDI ou através da chamada de métodos de
Sequencer:



void setTempoInBPM (float bpm)
void setTempoInMPQ (float mpq)
void setTempoFactor (float factor)
Mute e Solo em Tracks

Pode-se escolher que Tracks irão contribuir para o stream
de mensagens MIDI gerados pelo Sequencer;
void setTrackMute (int track, boolean mute)
void setTrackSolo (int track, boolean solo)

Para verificar o status de uma Track:


boolean getTrackMute (int track)
boolean getTrackSolo (int track)
Sincronizando com Outros Dispositivos

Sequencer possui uma classe interna Sequencer.SyncMode
que representa os modos como um Sequencer pode ser
sincronizado;

Um Sequencer pode ser sincronizado como Master e/ou
Slave:


void setMasterSyncMode (Sequencer.SyncMode sync)
void setSlaveSyncMode (Sequencer.SyncMode sync)
Listeners par Eventos Especiais

Especificando Listeners para eventos especiais:


Int[] addControllerEventListener
(ControllerEventListener listener, int[]
controllers)
Boolean addMetaEventListener (MetaEventListener
listener)
Exercício 4

Alterar o tocador MIDI de modo que seja
possível executar Mute e Solo em Tracks
especificas;
Sintetizando Som
A Síntese de Sons em Java

A arquitetura para síntese de sons em Java é
composta de três interfaces




Synthesizer
MidiChannel
Soundbank
E quatro classes:




Instrument
Patch
SoundbankResource
VoiceStatus
Verificando Quais Instrumentos
Estão Carregados

Para carregar o Soundbank padrão deve-se usar o
seguinte método de Synthesizer:
Soundbank getDefaultSoundbank ()

Para descobrir quais instrumentos estão atualmente
carregados deve-se usar o seguinte método de
Synthesizer:
Instrument[] getLoadedInstruments()
Carregando Instrumentos

Para descobrir quais instrumentos pertencem ao
Sintetizador:
Instrument[] getAvailableInstruments ()

Um instrumento pode ser carregado usando:
boolean loadInstrument (Intrument instrument)

O instrumento será carregado na posição
especificada pelo seu objeto Patch;
Carregando Instrumentos



Cada objeto do tipo Instrument possui um objeto Patch que
especifica onde o instrumento deverá ser carregado;
Esse local é definido pelo número do banco e número do programa;
É possível carregar o instrumento em um outro local através do
seguinte método de Synthesizer:
boolean remapIntrument (Intrument from, Instrument to)
Descarregando Instrumentos

Existem três métodos para descarregar instrumentos:



void unloadAllInstruments (Soundbank soundbank)
void unloadInstrument(Instrument instrument)
void unloadInstruments(Soundbank soundbank,
Patch[] patchList)
Acessando Canais

Existem 2 maneiras para controlar o sintetizador sem
usar um sequenciador:


Como já foi visto, pode-se enviar uma mensagem
diretamente a um receptor do sintetizador através
do método send ();
Outra opção seria interagir diretamente com
objetos MidiChannel;
Exemplo 6: Exemplo 4 Usando Canais
Synthesizer synth = MidiSystem.getSynthesizer ();
// Obtem todos os canais do sintetizador
MidiChannel chan[] = synth.getChannels ();
// Checa se o canal existe
if (chan[4] != null) {
chan[4].noteOn (60, 93);
}
Mute e Solo em Canais



A API Java Sound adiciona a noção de Solo e Mute
por canais;
Essa operação é similar as de Mute e Solo para
Tracks;
São fornecidos quatro métodos:




boolean getMute ()
boolean getSolo ()
void setMute (boolean muteState)
void setSolo (boolean soloState)
Alterando o Instrumento
de um Canal


Para descobrir qual o instrumento atualmente alocado a um
canal deve-se usar:
int getProgram ()
Para modificar o instrumento associado:
void programChange (int program)
OBS.: Instrumento deve estar carregado no sintetizador !
Exercício 5

Enviar mensagens Note On e Note Off diretamente
para o canal de um sintetizador (sem usar
MidiMessages). Permitir também a mudança de
Instrumentos;
Download

Apresentação do PowerPoint