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;