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
classe abstrata que representa uma mensagem MIDI pura
3 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 SMF, contem, por
exemplo, letra da música
Classe MidiEvent
MidiEvent
Mensagens MIDI + momento em que
ocorre
Métodos para especificar e também
obter informação de tempo:
long getTick ()
void setTick (long tick)
Classe Sequence
Sequence
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
São capazes de enviar e receber mensagens
MIDI
Métodos para reservar (open) e liberar (close)
o uso do dispositivo
MidiDevice.Info
fornece uma descrição textual do dispositivo;
Objetos transmissores implementam a
interface Transmitter e objetos receptores
implementam a interface Receiver
Interface Sequencer
Dispositivo para captura e execução de
seqüências de eventos MIDI
Responsável por manter os dados
sincronizados
Possui transmissores e receptores
Sequencer é uma subinterface de
MidiDevice
Interface Synthesizer
Representa o dispositivo que
efetivamente produz a onda sonora
Subinterface de MidiDevice
Um sintetizador possui canais
Interface MidiChannel
Possui apenas receptores
Acessando Recursos MIDI
Classe MidiSystem
Similar ao AudioSystem do pacote sampled
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.)
Métodos para obter dispositivos padrão:
static
static
static
static
Sequencer getSequencer ();
Synthesizer getSynthesizer ();
Receiver getReceiver ();
Transmitter getTransmitter ();
Exemplo 1
Obtendo informações sobre os MidiDevices
Exercício 1: Listar apenas os Sintetizadores
Carregando Seqüências MIDI
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
Um MidiDevice possui um ou mais
objetos com interface Transmitter
e/ou Receiver;
Cada transmissor só pode ser conectado a
um receptor e vice-versa;
Conectando-se a um Dispositivo
Interface Transmitter
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;
Exercício 2
Abrir e tocar um arquivo MIDI
Como enviar a mesma mensagem
para diferentes dispositivos ?
Usando 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 2: Conectando uma 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
}
Enviando Mensagem para um
Receptor sem Usar um Transmissor
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 3:
Enviando uma Mensagem sem Usar um
Transmissor
Exercício 3
FileToMIDI : Lê arquivo qualquer e toca seus bytes
como Notas
Monofônico
Para cada note ON, existe um note OFF correspondente
As notas tem a mesma duração
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 agrupa informações
que possuem forte relação entre si
Notas de determinado instrumento
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 em ticks,
que é o menor intervalo de tempo em um SMF
O tamanho de um tick é um valor relativo pode ser
dado em duas unidades:
Pulsos por semínima (PPQ)
Ticks por frame (SMPTE)
O valor absoluto do tick é calculado no momento do
sequenciamento
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 Sequence com o Sequencer usando:
Sequencer.setSequence(Sequence sequence)
6.
7.
8.
9.
Chame o método Sequencer.recordEnable ();
Chame o método Sequence.startRecording ();
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
Métodos de Track
boolean add (MidiEvent event)
MidiEvent get (int index)
boolean remove (MidiEvent event)
int size ()
long ticks ()
Exercício 4
Gravar Exercício 3 em arquivo
Formato 1
120 PPQ
Tick é acumulativo
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 andamento (tempo)
Pode-se mudar o andamento 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)
Exercício 5
Adicionar código ausente no MidiPlayer
Pause e Parar
Acelerar 2x
Aplicar Mute e Solo em alguma track
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
Soundbank é um repositório de Instruments
Instruments são classes responsáveis pela síntese do som
O Patch tem dois componentes: o Bank e o Program que
funcionam como índices (2 dimensões) na memória do
sintetizador
Além de determinar seu Patch (posição na memória do
Sintetizador)
Cada bank possui até 128 programs
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:
Através do seu Receiver
Interagir direta com objetos MidiChannel
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)
void programChange(int bank,int program)
OBS.: Instrumento deve estar carregado no sintetizador !
Exercício 6
Adicionar código ao programa TocaNotas para 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;