Você está na página 1de 94

22

Java Media Framework e Java Sound (no CD)


Objetivos
Entender para que serve o Java Media Framework (JMF). Entender para que serve a API Java Sound. Ser capaz de reproduzir mdia de udio e vdeo com JMF. Ser capaz de transmitir fluxos de mdia atravs de uma rede. Ser capaz de capturar, formatar e salvar mdia. Ser capaz de reproduzir sons com a API Java Sound. Ser capaz de reproduzir, gravar e sintetizar MIDI com a API Java Sound. A tev fornece a todos uma imagem, mas o rdio d luz a um milho de imagens em um milho de mentes. Peggy Noonan O barulho no prova nada. A galinha que bota um ovo grita como se tivesse botado um asteride. Mark Twain, Following the Equator A tela grande s faz o filme ficar duas vezes pior. Samuel Goldwyn A vida no uma srie de imagens que mudam medida que se repetem. Andy Warhol

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1113

Sumrio do captulo
22.1 22.2 22.3 22.4 22.5 22.6 22.7 Introduo Reproduzindo mdia Formatando e salvando mdia capturada Streaming com RTP Java Sound Reproduzindo amostras de udio Musical Instrument Digital Interface (MIDI) 22.7.1 Reproduo de MIDI 22.7.2 Gravao de MIDI 22.7.3 Sntese de MIDI 22.7.4 A classe MidiDemo Recursos na Internet e na World Wide Web (Estudo de caso opcional) Pensando em objetos: animao e som na viso

22.8 22.9

Resumo Terminologia Exerccios de auto-reviso Respostas aos exerccios de auto-reviso Exerccios Seo especial: construindo seu prprio compilador

22.1 Introduo
Este captulo d continuidade s nossas discusses sobre multimdia do Captulo 18, apresentando algumas das APIs de Java para multimdia que permitem aos programadores melhorar os aplicativos com recursos de vdeo e udio. Em anos recentes, o segmento de multimdia digital do setor de computadores experimentou um crescimento enorme, como demonstra a enorme quantidade de contedo de multimdia disponvel na Internet. Os sites da Web foram transformados de pginas HTML baseadas em texto em experincias intensas em multimdia. Os avanos nas tecnologias de hardware e de software permitiram que os desenvolvedores integrassem multimdia aos aplicativos mais simples. Na faixa superior dos aplicativos de multimdia, o setor de vdeo games usou a programao com multimdia para tirar proveito das mais recentes tecnologias de hardware, como placas de vdeo 3D que criam experincias de realidade virtual para os usurios. Reconhecendo que os aplicativos Java deveriam suportar recursos de vdeo e udio digitais, a Sun Microsystems, a Intel e a Silicon Graphics trabalharam juntas para produzir uma API para multimdia que conhecida como a Java Multimedia Framework (JMF). A API JMF uma das vrias APIs para multimdia em Java. Usando a API JMF, os programadores podem criar aplicativos Java que reproduzem, editam e capturam muitos tipos populares de mdia. A primeira metade deste captulo discute a API JMF. A IBM e a Sun desenvolveram a mais recente especificao de JMF verso 2.0. A Sun oferece uma implementao de referncia JMF 2.1.1 da especificao de JMF que suporta tipos de arquivo de mdia como arquivos Microsoft Audio/Video Interleave (.avi), Macromedia Flash 2 movies (.swf), Future Splash (.spl), MPEG Layer 3 Audio (.mp3), Musical Instrument Digital Interface (MIDI; .mid), vdeos MPEG-1 (.mpeg, .mpg), QuickTime (.mov), Sun Audio (.au), udio Wave (.wav), AIFF (.aiff) e GSM (.gsm). Alm dos exemplos de clipes de mdia fornecidos com os exemplos deste captulo no CD, muitos sites oferecem um suprimento grande de clipes de udio e vdeo que podem ser baixados gratuitamente. Voc pode baixar clipes de mdia destes sites (e muitos outros na Internet) e us-los para testar os exemplos deste captulo. Apresentamos uma lista de sites aqui para que voc possa comear. O Free Audio Clips (www.freeaudioclips.com) um excelente site para diversos tipos de arquivos de udio. O site 13 Even (www.13-even.com.media.html) fornece clipes de udio e vdeo em muitos formatos para seu uso pessoal. Se voc estiver procurando arquivos de udio MIDI para uso na Seo 22.7, d uma olhada nos clipes MIDI gratuitos no endereo www.freestuffgalore.commidi.asp. O Funny Video Clips (www.video-clips.co.uk) oferece material de entretenimen-

1114

JAVA COMO PROGRAMAR

to. O site de downloads da Microsoft (msdn.microsoft.com/downloads) contm uma seo de multimdia que fornece clipes de udio e outros tipos de mdia. Atualmente, o JMF est disponvel como pacote de extenso separado do Java 2 Software Development Kit. O CD que acompanha este livro contm o JMF 2.1.1. A implementao mais recente de JMF pode ser baixada do site oficial de JMF:
java.sun.com/products/java-media/jmf

O site da Web para o JMF fornece verses de JMF que tiram proveito das caractersticas de desempenho da plataforma na qual o JMF est sendo executado. Por exemplo, o Windows Performance Pack de JMF fornece amplo suporte a mdias e dispositivos para programas Java que esto executados em plataformas Microsoft Windows (Windows 95/98/NT 4.0/2000). O site oficial da Web para o JMF tambm fornece suporte, informaes e recursos atualizados continuamente para os programadores de JMF.
Dica de portabilidade 22.1 Escrever programas com o Windows Performance Pack de JMF reduz a portabilidade daqueles programas para outros sistemas operacionais.

O restante deste captulo discute a API Java Sound e seus extensos recursos para processamento de som. Internamente, o JMF usa Java Sound para suas funes de udio. Nas Sees 22.5 a 22.7, demonstraremos a reproduo de amostras de udio e as funcionalidades para MIDI com o Java Sound, uma extenso-padro do Java 2 Software Development Kit.

22.2 Reproduzindo mdia


O JMF normalmente usado para reproduzir clipes de mdia em aplicativos Java. Muitos aplicativos, como os gerenciadores financeiros, as enciclopdias e os jogos, usam multimdia para ilustrar os recursos do aplicativo, apresentar contedo educacional e divertir os usurios. O JMF oferece diversos mecanismos para reproduzir mdia, e o mais simples deles atravs de objetos que implementam a interface Player. A interface Player (pacote javax.media) estende Controller, que um tratador para mdia suportada por JMF. As seguintes etapas so necessrias para reproduzir um clipe de mdia: 1. Especificar a fonte da mdia; 2. Criar um Player para a mdia; 3. Obter a mdia de sada e os controles de Player; 4. Exibir a mdia e os controles. A classe SimplePlayer (Fig. 22.1) um programa simples em Java que reproduz mdia que demonstra diversos recursos comuns de reprodutores de mdia populares. A demonstrao SimplePlayer pode reproduzir a maioria dos arquivos de mdia suportados por JMF, possivelmente com exceo das verses mais recentes dos formatos. Este aplicativo permite acessar arquivos no computador local que contm tipos de mdia suportados, clicando-se no boto Open File. Clicar no boto Open Location e especificar um URL de mdia permite acessar mdia de uma fonte de mdia, como um dispositivo de captura, um servidor da Web ou uma fonte de streaming. Um dispositivo de captura (discutido na Seo 22.3) l mdia a partir de dispositivos de udio e vdeo como microfones, reprodutores de CD e cmeras. O stream Real-Time Transport Protocol (RTP) um stream de bytes enviados atravs de uma rede a partir de um servidor de streaming. O aplicativo guarda no buffer e reproduz a mdia de streaming no computador cliente.
1 2 3 4

// Fig. 22.1: SimplePlayer.java // Abre e reproduz um arquivo de mdia a partir de um // computador local, um URL pblico ou uma sesso RTP

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 1 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1115

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

// Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; // Pacotes de extenso de Java import javax.swing.*; import javax.media.*; public class SimplePlayer extends JFrame { // reprodutor de mdia em Java private Player player; // componente para contedo visual private Component visualMedia; // componentes de controle para a mdia private Component mediaControl; // continer principal private Container container; // endereos do arquivo de mdia e da mdia private File mediaFile; private URL fileURL; // construtor para SimplePlayer public SimplePlayer() { super( "Simple Java Media Player" ); container = getContentPane(); // painel que contm botes JPanel buttonPanel = new JPanel(); container.add( buttonPanel, BorderLayout.NORTH ); // abrindo um arquivo a partir do boto de diretrio JButton openFile = new JButton( "Open File" ); buttonPanel.add( openFile ); // registra um ActionListener para eventos de openFile openFile.addActionListener( // classe interna annima para tratar eventos de openFile new ActionListener() { // abre e cria "player" para o arquivo public void actionPerformed( ActionEvent event ) { mediaFile = getFile(); if ( mediaFile != null ) { // obtm URL a partir do arquivo try { fileURL = mediaFile.toURL(); }

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 2 de 8).

1116
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

JAVA COMO PROGRAMAR

// caminho para o arquivo no pode ser encontrado catch ( MalformedURLException badURL ) { badURL.printStackTrace(); showErrorMessage( "Bad URL" ); } makePlayer( fileURL.toString() ); } } } // fim do mtodo actionPerformed

// fim do mtodo ActionListener

); // fim da chamada para o mtodo addActionListener // boto de abertura do URL JButton openURL = new JButton( "Open Locator" ); buttonPanel.add( openURL ); // registra um ActionListener para eventos de openURL openURL.addActionListener( // classe interna annima para tratar de eventos de openURL new ActionListener() { // abre e cria um "player" para o localizador de mdia public void actionPerformed( ActionEvent event ) { String addressName = getMediaLocation(); if ( addressName != null ) makePlayer( addressName ); } } // fim do mtodo ActionListener

); // fim da chamada para o mtodo addActionListener // liga a gerao leve nos players para permitir // melhor compatibilidade com componentes GUI de peso leve Manager.setHint( Manager.LIGHTWEIGHT_RENDERER, Boolean.TRUE ); } // fim do construtor SimplePlayer

// mtodo utilitrio para mensagens de erro "pop-up" public void showErrorMessage( String error ) { JOptionPane.showMessageDialog( this, error, "Error", JOptionPane.ERROR_MESSAGE ); } // obtm arquivo do computador public File getFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 3 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1117

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // obtm endereo da mdia digitado pelo usurio public String getMediaLocation() { String input = JOptionPane.showInputDialog( this, "Enter URL" ); // se o usurio pressionar OK sem digitar dados if ( input != null && input.length() == 0 ) return null; return input; } // cria player com o endereo da mdia public void makePlayer( String mediaLocation ) { // restaura o player e a janela se houver player anterior if ( player != null ) removePlayerComponents(); // endereo da origem da mdia MediaLocator mediaLocator = new MediaLocator( mediaLocation ); if ( mediaLocator == null ) { showErrorMessage( "Error opening file" ); return; } // cria um player a partir de MediaLocator try { player = Manager.createPlayer( mediaLocator ); // registra ControllerListener para tratar de eventos do Player player.addControllerListener( new PlayerEventHandler() ); // chama realize para permitir a gerao da mdia do player player.realize(); } // no existe nenhum player ou o formato no suportado catch ( NoPlayerException noPlayerException ) { noPlayerException.printStackTrace(); } // erro na leitura do arquivo catch ( IOException ioException ) { ioException.printStackTrace();

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 4 de 8).

1118
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243

JAVA COMO PROGRAMAR

} } // fim do mtodo makePlayer

// devolve o player para os recursos do sistema // e restaura a mdia e os controles public void removePlayerComponents() { // remove componente de vdeo anterior, se existe um if ( visualMedia != null ) container.remove( visualMedia ); // remove controle de mdia anterior, se existe um if ( mediaControl != null ) container.remove( mediaControl ); // faz parar o player e devolve os recursos alocados player.close(); } // obtm controles visuais para mdia e player public void getMediaComponents() { // obtm componente visual do player visualMedia = player.getVisualComponent(); // adiciona componente visual, se estiver presente if ( visualMedia != null ) container.add( visualMedia, BorderLayout.CENTER ); // obtm a GUI de controle do player mediaControl = player.getControlPanelComponent(); // adiciona componente de controles, se estiver presente if ( mediaControl != null ) container.add( mediaControl, BorderLayout.SOUTH ); } // fim do mtodo getMediaComponents

// tratador para os eventos ControllerEvents do player private class PlayerEventHandler extends ControllerAdapter { // carrega antecipadamente a mdia assim que o player realizado public void realizeComplete( RealizeCompleteEvent realizeDoneEvent ) { player.prefetch(); } // player pode comear a mostrar a mdia aps a carga antecipada public void prefetchComplete( PrefetchCompleteEvent prefetchDoneEvent ) { getMediaComponents(); // assegura um leiaute vlido para a frame validate(); // comea a reproduzir a mdia

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 5 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1119

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

player.start(); } // fim do mtodo prefetchComplete

// se fim da mdia, restaura para o incio e pra de reproduzir public void endOfMedia( EndOfMediaEvent mediaEndEvent ) { player.setMediaTime( new Time( 0 ) ); player.stop(); } } // fim da classe interna PlayerEventHandler

// executa o aplicativo public static void main( String args[] ) { SimplePlayer testPlayer = new SimplePlayer(); testPlayer.setSize( 300, 300 ); testPlayer.setLocation( 300, 300 ); testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE ); testPlayer.setVisible( true ); } } // fim da classe SimplePlayer

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 6 de 8).

1120

JAVA COMO PROGRAMAR

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 7 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1121

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 8 de 8).

O clipe de mdia precisa ser processado antes de ser reproduzido. Para processar um clipe de mdia, o programa precisa acessar uma fonte de mdia, criar um Controller para aquela fonte e enviar a mdia para a sada. Antes de enviar para a sada, os usurios podem fazer formatao opcional, como mudar um vdeo AVI para um vdeo QuickTime. Embora o JMF oculte do programador o processamento de mdia de baixo nvel (por exemplo, verificar se o arquivo compatvel), programadores e usurios podem configurar como um Player apresenta a mdia. A Seo 22.3 e a Seo 22.4 revelam que capturar e fazer streaming de mdia seguem as mesmas diretrizes. A Seo 22.8 lista diversos sites da Web que tm contedo de mdia suportado por JMF. A Fig. 22.1 apresenta alguns objetos fundamentais para reproduzir mdia. O pacote de extenso de JMF javax.media importado na linha 13 contm a interface Player e outras classes e interfaces necessrias para eventos. A linha 18 declara um objeto Player para reproduzir clipes de mdia. As linhas 30 a 31 declaram os endereos destes clipes como referncias File e URL. As linhas 21 e 24 declaram objetos Component para a exibio do vdeo e para manter os controles. O Component mediaControl permite reproduzir, fazer pausa e parar o clipe de mdia. O Component visualMedia exibe a parte de vdeo de um clipe de mdia (se o clipe de mdia for um vdeo). O JMF fornece geradores de vdeo peso-leve que so compatveis com os componentes peso-leve do Swing (ver Captulo 13). As linhas 107 e 108 no construtor de SimplePlayer especificam que o Player deve desenhar seus componentes GUI e sua parte de vdeo (se existir uma) usando geradores peso-leve, de modo que o reprodutor de mdia ter aparncia semelhante de outros componentes GUI com componentes Swing. Antes de reproduzir a mdia, SimplePlayer exibe uma GUI inicial que consiste em dois botes, Open File e Open Locator, que permitem especificar o endereo da mdia. Os tratadores de eventos para estes dois botes (linhas 52 a 78 e linhas 90 a 101) executam funes semelhantes. Cada boto pede aos usurios que informem um recurso de mdia, como um clipe de udio ou de vdeo, depois cria um Player para a mdia especificada. Quando

1122

JAVA COMO PROGRAMAR

o usuria clica em Open File, a linha 57 chama o mtodo getFile (linhas 120 a 134) para pedir aos usurios que selecionem um arquivo de mdia do computador local. A linha 63 chama o mtodo toURL de File para obter uma representao como URL do nome e endereo do arquivo selecionado. A linha 72 chama o mtodo makePlayer de SimplePlayer (linhas 150 a 187) para criar um Player para a mdia selecionada pelo usurio. Quando os usurios clicam em Open Locator, a linha 95 invoca o mtodo getMedialocation (linhas 137 a 147), pedindo aos usurios que digitem um string que especifica o endereo da mdia. A linha 98 chama o mtodo makePlayer de SimplePlayer para criar um Player para a mdia que est no endereo especificado. O mtodo makePlayer (linhas 150 a 187) faz os preparativos necessrios para criar um Player de clipes de mdia. O argumento String indica o endereo da mdia. As linhas 153 e 154 invocam o mtodo removePlayerComponents (linhas 191 a 203) para remover da frame o componente visual e os controles da GUI do Player anterior, antes de criar um novo Player. A linha 202 invoca o mtodo close de Player para parar toda a atividade do Player e liberar recursos do sistema mantidos pelo Player anterior. O mtodo makePlayer exige um ponteiro para a fonte da qual a mdia recuperada, o que feito instanciando-se um novo MediaLocator para o valor dado pelo argumento String (linhas 157 e 158). O MediaLocator especifica o endereo de uma fonte de mdia, de forma muito parecida como um URL geralmente especifica o endereo de uma pgina da Web. O MediaLocator pode acessar mdia a partir de dispositivos de captura e sesses RTP e a partir de endereos de arquivos. O construtor de MediaLocator exige o endereo da mdia como um String, de modo que todos os URLs devem ser convertidos para Strings como na linha 72. O mtodo makePlayer instancia um novo Player com uma chamada ao mtodo createPlayer de Manager. A classe Manager fornece mtodos static que permitem acessar a maioria dos recursos do JMF. O mtodo createPlayer abre a fonte de mdia especificada e determina o Player apropriado para a fonte de mdia. O mtodo createPlayer dispara uma NoPlayerException se no puder ser encontrado um Player apropriado para o clipe de mdia. Se ocorrerem problemas na conexo com a fonte de mdia, uma IOException disparada. Os ControllerListeners esperam os ControllerEvents que os Players geram, para monitorar o progresso de um Player no processo de tratamento da mdia. As linhas 170 e 171 registram uma instncia da classe interna PlayerEventHandler (linhas 225 a 255) para esperar certos eventos que player gera. A classe PlayerEventHandler estende a classe ControllerAdapter, que fornece implementaes vazias dos mtodos da interface ControllerListener. A classe ControllerAdapter facilita a implementao de ControllerListener para as classes que precisam tratar de apenas alguns tipos de ControllerEvent. Os Players confirmam seu progresso durante o processamento da mdia com base em suas transies de estado. A linha 174 invoca o mtodo realize de Player no estado Realizing para indicar que ele est se conectando sua fonte de mdia e interagindo com ela. Quando um Player completa a realizao, ele gera um RealizeCompleteEvent um tipo de ControllerEvent que ocorre quando o Player completa sua transio para o estado Realized. Este estado indica que o Player completou todos os preparativos necessrios para comear a processar a mdia. O programa invoca o mtodo realizeComplete (linhas 228 a 232) quando o Player gera um RealizeCompleteEvent. A maioria dos reprodutores de mdia tm um recurso de uso de buffer, que armazena localmente um pedao da mdia baixada, de modo que os usurios no precisem esperar que um clipe inteiro seja baixado antes de reproduzilo, j que a leitura de dados da mdia pode levar muito tempo. Invocando o mtodo prefetch de Player, a linha 231 faz a transio de player para o estado Prefetching. Quando o Player carrega antecipadamente um clipe de mdia, o Player obtm controle exclusivo sobre certos recursos do sistema necessrios para reproduzir o clipe. O Player tambm comea a colocar dados da mdia no buffer para reduzir o atraso antes da reproduo do clipe de mdia. Quando o Player completa a carga antecipada, ele passa para o estado Prefetched e est pronto para reproduzir a mdia. Durante esta transio, o Player gera um ControllerEvent do tipo PrefetchCompleteEvent, para indicar que est pronto para exibir a mdia. O Player invoca o mtodo prefetchComplete de PlayerEventHandler (linhas 235 a 246), que exibe a GUI do Player na frame. Aps obter os recursos de hardware, o programa pode obter os componentes de mdia que ele exige. A linha 238 invoca o mtodo getMediaComponents (linhas 206 a 222) para obter os controles da GUI e o componente visual da mdia (se a mdia for um clipe de vdeo) e os anexa ao painel de contedo da janela do aplicativo. O mtodo getVisualComponent de Player (linha 209) obtm o componente visual do clipe de vdeo. Similarmente, a linha 216 invoca o mtodo getControlPanelComponent para devolver os controles da GUI. A GUI (Fig. 22.1) normalmente fornece os seguintes controles:

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1123

1. Um controle deslizante de posicionamento, para pular para certos pontos no clipe de mdia; 2. Um boto de pausa; 3. Um boto de volume que fornece os controles de volume quando se clicar nele com o boto direito do mouse e uma funo de emudecer quando se clicar nele com o boto esquerdo; 4. Um boto de propriedades da mdia que fornece informaes detalhadas sobre a mdia quando se clicar nele com o boto direito do mouse e a taxa de exibio de quadros quando se clicar nele com o boto esquerdo.
Observao de aparncia e comportamento 22.1 Invocar o mtodo getVisualComponent de Player devolve null para arquivos de udio, porque no h componente visual a exibir. Observao de aparncia e comportamento 22.2 Invocar o mtodo getControlPanelComponent de Player devolve conjuntos diferentes de controles de GUI, dependendo do tipo de mdia. Por exemplo, o contedo de mdia com stream diretamente de uma conferncia ao vivo no tem uma barra de andamento porque o comprimento da mdia no predeterminado.

Depois de validar o novo leiaute da frame (linha 241), a linha 244 invoca o mtodo start de Player (linha 239) para comear a reproduzir o clipe de mdia.
Observao de engenharia de software 22.1 Se o Player no fez a carga antecipada nem realizou a mdia, invocar o mtodo start faz a carga antecipada e realiza a mdia. Dica de desempenho 22.1 Iniciar o Player consome menos tempo se o Player j tiver carregado a mdia antecipadamente antes de start ser chamado.

Quando o clipe de mdia terminar, o Player gera um ControllerEvent do tipo EndOfMediaEvent. A maioria dos reprodutores de mdia reenrolam o clipe de mdia depois de chegar ao fim, de modo que os usurios possam ver ou ouvir novamente a partir do incio. O mtodo endOfMedia (linhas 249 a 253) trata o EndOfMediaEvent e restaura o clipe de mdia para sua posio inicial invocando o mtodo setMediaTime de Player com um novo Time (pacote javax.media) de 0 (linha 251). O mtodo setMediaTime ajusta a posio da mdia para uma posio especfica de tempo e til para pular para uma parte diferente da mdia. A linha 252 invoca o mtodo stop de Player, que termina o processamento da mdia e coloca o Player no estado Stopped. Invocar o mtodo start para um Player Stopped que no tenha sido fechado retoma a reproduo da mdia. Freqentemente, desejvel configurar a mdia antes da apresentao. Na prxima seo, discutimos a interface Processor, que tem mais capacidades de configurao do que a interface Player. Processor permite que um programa formate a mdia e a salve em um arquivo.

22.3 Formatando e salvando mdia capturada


O Java Media Framework consegue reproduzir e salvar a mdia a partir de dispositivos de captura como microfones e cmeras de vdeo. Este tipo de mdia conhecido como mdia capturada. Os dispositivos de captura convertem mdia analgica em mdia digitalizada. Por exemplo, o programa que captura uma voz analgica a partir de um microfone ligado ao computador pode criar um arquivo digitalizado a partir daquela gravao. O JMF pode acessar dispositivos de captura de vdeo que usam drivers Video for Windows. Alm disso, o JMF suporta dispositivos de captura de udio que usam a Direct Sound Interface do Windows ou a Java Sound Interface. O driver Video for Windows fornece interfaces que permitem aos aplicativos Windows acessar e processar mdia a partir de dispositivos de vdeo. Similarmente, Direct Sound e Java Sound so interfaces que permitem acessar dispositivos de som como placas de som em hardware. O Solaris Performance Pack fornece suporte para dispositivos de captura Java Sound e Sun Video na plataforma Solaris. Para obter uma lista completa dos dispositivos suportados pelo JMF, visite o site oficial do JMF na Web.

1124

JAVA COMO PROGRAMAR

O aplicativo SimplePlayer apresentado na Fig. 22.1 permitiu aos usurios reproduzir mdia de um dispositivo de captura. Um string localizador especifica a localizao do dispositivo de captura que a demonstrao SimplePlayer acessa. Por exemplo, para testar as capacidades de captura de SimplePlayer, conecte um microfone ao plug de entrada de microfone de uma placa de som. Digitar o string localizador javasound:// no dilogo de entrada Open Location especifica que a entrada da mdia deve ser feita a partir do dispositivo de captura habilitado para Java Sound. O string localizador inicializa o MediaLocator de que o Player necessita para o material de udio capturado do microfone. Embora SimplePlayer fornea acesso a dispositivos de captura, ele no formata a mdia nem salva os dados capturados. A Fig. 22.2 apresenta um programa que executa estas duas novas tarefas. A classe CapturePlayer fornece mais controle sobre as propriedades da mdia atravs da classe DataSource (pacote javax.media.protocol). A classe DataSource fornece a conexo fonte de mdia, depois abstrai esta conexo para permitir que os usurios a manipulem. Este programa usa uma DataSource para formatar a mdia de entrada e de sada. A DataSource passa a mdia de sada formatada para um Controller, que ir format-la adicionalmente para que ela possa ser salva em um arquivo. O Controller que manipula a mdia um Processor, que estende a interface Player. Finalmente, o objeto que implementa a interface DataSink salva a mdia capturada e formatada. O objeto Processor trata do fluxo de dados da DataSource para o objeto DataSink.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

// Fig. 22.2: CapturePlayer.java // Apresenta e salva a mdia capturada. // Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; // Pacotes de extenso de Java import javax.swing.*; import javax.swing.event.*; import javax.media.*; import javax.media.protocol.*; import javax.media.format.*; import javax.media.control.*; import javax.media.datasink.*; public class CapturePlayer extends JFrame { // botes de captura e salvamento private JButton captureButton; // componente GUI para salvar os dados capturados private Component saveProgress; // formatos da mdia do dispositivo, formato escolhido pelo usurio private Format formats[], selectedFormat; // controles dos formatos de mdia do dispositivo private FormatControl formatControls[]; // informaes de especificao do dispositivo private CaptureDeviceInfo deviceInfo; // Vector contendo todas as informaes do dispositivo private Vector deviceList;

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 1 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1125

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

// fontes de dados de entrada e sada private DataSource inSource, outSource; // gravador do arquivo para a mdia capturada private DataSink dataSink; // Processor para gerar e salvar a mdia capturada private Processor processor; // construtor para CapturePlayer public CapturePlayer() { super( "Capture Player" ); // painel contendo botes JPanel buttonPanel = new JPanel(); getContentPane().add( buttonPanel ); // boto para acessar e inicializar dispositivos de captura captureButton = new JButton( "Capture and Save File" ); buttonPanel.add( captureButton, BorderLayout.CENTER ); // registra um ActionListener para eventos do captureButton captureButton.addActionListener( new CaptureHandler() ); // ativa a gerao leve para tornar // compatvel com componentes GUI peso leve Manager.setHint( Manager.LIGHTWEIGHT_RENDERER, Boolean.TRUE ); // registra um WindowListener para eventos da frame addWindowListener( // classe interna annima para tratar WindowEvents new WindowAdapter() { // descarta o Processor public void windowClosing( WindowEvent windowEvent ) { if ( processor != null ) processor.close(); } } // fim de WindowAdapter

); // fim da chamada para o mtodo addWindowListener } // fim do construtor

// classe tratadora de aes para configurar dispositivo private class CaptureHandler implements ActionListener { // inicializa e configura dispositivo de captura public void actionPerformed( ActionEvent actionEvent ) { // coloca as informaes disponveis do dispositivo no Vector deviceList = CaptureDeviceManager.getDeviceList( null );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 2 de 9).

1126
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

JAVA COMO PROGRAMAR

// se nenhum dispositivo for encontrado, exibe mensagem de erro if ( ( deviceList == null ) | | ( deviceList.size() == 0 ) ) { showErrorMessage( "No capture devices found!" ); return; } // array de nomes de dispositivos String deviceNames[] = new String[ deviceList.size() ]; // armazena todos os nomes de dispositivos em um // array de strings, para fins de exibio for ( int i = 0; i < deviceList.size(); i++ ){ deviceInfo = ( CaptureDeviceInfo ) deviceList.elementAt( i ); deviceNames[ i ] = deviceInfo.getName(); } // obtm o ndice no Vector do dispositivo selecionado int selectDeviceIndex = getSelectedDeviceIndex( deviceNames ); if ( selectDeviceIndex == 1 ) return; // obtm as informaes de dispositivo do dispositivo selecionado deviceInfo = ( CaptureDeviceInfo ) deviceList.elementAt( selectDeviceIndex ); formats = deviceInfo.getFormats(); // se o dispositivo de captura anterior estiver aberto, desconecta-o if ( inSource != null ) inSource.disconnect(); // obtm dispositivo e configura seu formato try { // cria fonte de dados a partir do MediaLocator do dispositivo inSource = Manager.createDataSource( deviceInfo.getLocator() ); // obtm controles de configurao de formato para o dispositivo formatControls = ( ( CaptureDevice ) inSource ).getFormatControls();
// obtm a configurao de formato do dispositivo desejada pelo usurio selectedFormat = getSelectedFormat( formats );

if ( selectedFormat == null ) return; setDeviceFormat( selectedFormat );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 3 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1127

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

captureSaveFile(); } // fim do try

// no consegue encontrar DataSource a partir do MediaLocator catch ( NoDataSourceException noDataException ) { noDataException.printStackTrace(); } // erro de conexo ao dispositivo catch ( IOException ioException ) { ioException.printStackTrace(); } } } // fim do mtodo actionPerformed

// fim da classe interna CaptureHandler

// configura o formato de sada da mdia capturada pelo dispositivo public void setDeviceFormat( Format currentFormat ) { // configura formato desejado em todos os controles de formato for ( int i = 0; i < formatControls.length; i++ ) { // assegura que o controle de formato pode ser configurado if ( formatControls[ i ].isEnabled() ) { formatControls[ i ].setFormat( currentFormat ); System.out.println ( "Presentation output format currently set as " + formatControls[ i ].getFormat() ); } } } // obtm o ndice no Vector do dispositivo selecionado public int getSelectedDeviceIndex( String[] names ) { // obtm o nome do dispositivo da caixa de dilogo de seleo de dispositivo String name = ( String ) JOptionPane.showInputDialog( this, "Select a device:", "Device Selection", JOptionPane.QUESTION_MESSAGE, null, names, names[ 0 ] ); // se o formato foi selecionado, obtm ndice do nome no array names if ( name != null ) return Arrays.binarySearch( names, name ); // seno, devolve valor indicando seleo invlida else return 1; } // devolve formato selecionado pelo usurio para o dispositivo public Format getSelectedFormat( Format[] showFormats ) { // fim do lao for

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 4 de 9).

1128
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

JAVA COMO PROGRAMAR

return ( Format ) JOptionPane.showInputDialog( this, "Select a format: ", "Format Selection", JOptionPane.QUESTION_MESSAGE, null, showFormats, null ); } // exibe mensagens de erro public void showErrorMessage( String error ) { JOptionPane.showMessageDialog( this, error, "Error", JOptionPane.ERROR_MESSAGE ); } // obtm arquivo desejado para salvar a mdia capturada public File getSaveFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showSaveDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // mostra o monitor de salvamento dos dados capturados public void showSaveMonitor() { // mostra o dilogo do monitor de salvamento int result = JOptionPane.showConfirmDialog( this, saveProgress, "Save capture in progress...", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE ); // termina salvamento se o usurio pressiona "OK" ou fecha dilogo if ( ( result == JOptionPane.OK_OPTION ) | | ( result == JOptionPane.CLOSED_OPTION ) ) { processor.stop(); processor.close(); System.out.println ( "Capture closed." ); } } // processa mdia capturada e salvada no arquivo public void captureSaveFile() { // array de formatos desejados de salvamento suportados pelas trilhas Format outFormats[] = new Format[ 1 ]; outFormats[ 0 ] = selectedFormat; // formato de sada no arquivo FileTypeDescriptor outFileType = new FileTypeDescriptor( FileTypeDescriptor.QUICKTIME );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 5 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1129

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

// configura e inicia processador e monitora a captura try { // cria processador a partir do modelo de processador // da fonte de dados especfica, formatos de sada nas trilhas // e formato de sada no arquivo processor = Manager.createRealizedProcessor( new ProcessorModel( inSource, outFormats, outFileType ) ); // tenta criar um gravador de dados para a sada de mdia if ( !makeDataWriter() ) return;
// chama start do processador para iniciar alimentao dos dados capturados processor.start();

// obtm controle de monitor para captura e codificao MonitorControl monitorControl = ( MonitorControl ) processor.getControl( "javax.media.control.MonitorControl" ); // obtm componente GUI do controle de monitorao saveProgress = monitorControl.getControlComponent(); showSaveMonitor(); } // fim do try

// nenhum processador pode ser encontrado // para uma fonte de dados especfica catch ( NoProcessorException processorException ) { processorException.printStackTrace(); } // incapaz de realizar atravs do // mtodo createRealizedProcessor catch ( CannotRealizeException realizeException ) { realizeException.printStackTrace(); } // erro de conexo ao dispositivo catch ( IOException ioException ) { ioException.printStackTrace(); } } // fim do mtodo captureSaveFile

// mtodo que inicializa o gravador do arquivo de mdia public boolean makeDataWriter() { File saveFile = getSaveFile(); if ( saveFile == null ) return false; // obtm fonte de dados de sada a partir do processador outSource = processor.getDataOutput();

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 6 de 9).

1130
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

JAVA COMO PROGRAMAR

if ( outSource == null ) { showErrorMessage( "No output from processor!" ); return false; } // inicia o processo de gravao de dados try { // cria um novo MediaLocator a partir do URL saveFile MediaLocator saveLocator = new MediaLocator ( saveFile.toURL() ); // cria DataSink a partir da fonte de dados de sada e do // arquivo de destino de salvamento especificado pelo usurio dataSink = Manager.createDataSink( outSource, saveLocator ); // registra um DataSinkListener para DataSinkEvents dataSink.addDataSinkListener( // classe interna annima para tratar DataSinkEvents new DataSinkListener () { // se fim da mdia, fecha o gravador de dados public void dataSinkUpdate( DataSinkEvent dataEvent ) { // se a captura foi parada, fecha DataSink if ( dataEvent instanceof EndOfStreamEvent ) dataSink.close(); } } // fim de DataSinkListener

); // fim da chamada ao mtodo addDataSinkListener // comea a salvar dataSink.open(); dataSink.start(); } // fim do try

// DataSink no pode ser encontrada para o arquivo de // salvamento e a fonte de dados especficos catch ( NoDataSinkException noDataSinkException ) { noDataSinkException.printStackTrace(); return false; } // violao enquanto acessava // destino de MediaLocator catch ( SecurityException securityException ) { securityException.printStackTrace(); return false; } // problema na abertura e inicializao de DataSink catch ( IOException ioException ) { ioException.printStackTrace();

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 7 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1131

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

return false; } return true; } // fim do mtodo makeDataWriter

// mtodo main public static void main( String args[] ) { CapturePlayer testPlayer = new CapturePlayer(); testPlayer.setSize( 200, 70 ); testPlayer.setLocation( 300, 300 ); testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE ); testPlayer.setVisible( true ); } } // fim da classe CapturePlayer

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 8 de 9).

1132

JAVA COMO PROGRAMAR

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 9 de 9).

JMF e Java Sound usam fontes de mdia extensivamente, de modo que os programadores entender a disposio dos dados na mdia. O cabealho em uma fonte de dados especifica o formato da mdia e outras informaes essenciais necessrias para reproduzir a mdia. O contedo da mdia normalmente consiste em trilhas de dados, semelhantes s trilhas de msica em um CD. As fontes de mdia podem ter uma ou mais trilhas que contm diversos dados. Por exemplo, um clipe de cinema pode conter uma trilha para vdeo, uma trilha para udio e uma terceira trilha para closed captioning, para os deficientes auditivos. A classe CapturePlayer (Fig. 22.2) ilustra a captura, a configurao de formatos de mdia e o salvamento de mdia a partir de dispositivos de captura suportados pelo JMF. O teste mais simples do programa usa um microfone para entrada de udio. Inicialmente, a GUI tem apenas um boto, no qual o usurio clica para iniciar o processo de configurao. Depois, o usurio seleciona um dispositivo de captura em um dilogo de menu escamotevel. A caixa de dilogo seguinte tem opes para o formato do dispositivo de captura e da sada no arquivo. A terceira caixa de dilogo pede aos usurios para salvar a mdia em um arquivo especfico. A caixa de dilogo final oferece um controle de volume e a opo de monitorar os dados. A monitorao permite que os usurios escutem ou vejam a mdia medida que capturada e salvado, sem modific-la de nenhuma maneira. Muitas tecnologias de captura de mdia oferecem recursos de monitorao. Por exemplo, muitos gravadores de vdeo tm uma tela acoplada para deixar os usurios ver o que a cmera est capturando sem olhar atravs do visor. Em um estdio de gravaes, os produtores podem escutar de uma outra sala a msica ao vivo que est sendo gravada, atravs de fones de ouvido. Monitorar dados diferente de reproduzir dados e no provoca nenhuma alterao no formato da mdia ou afeta os dados que esto sendo enviados para o Processor. No programa CapturePlayer, as linhas 14 a 16 importam os pacotes de extenso de Java javax.media.protocol, javax.media.format e javax.media.control, que contm classes e interfaces para controle de mdia e formatao de dispositivos. A linha 17 importa o pacote de JMF javax.media.datasink, que contm classes para enviar mdia formatada para a sada. O programa usa as classes e interfaces fornecidas por estes pacotes para obter as informaes sobre o dispositivo de captura desejado, configurar o formato do dispositivo de captura, criar um Processor para tratar os dados de mdia capturados, criar um DataSink para gravar os dados de mdia em um arquivo e monitorar o processo de salvamento. CapturePlayer pode configurar o formato da mdia. O JMF fornece a classe Format para descrever os atributos de um formato de mdia, como a taxa de amostragem (que controla a qualidade do som) ou se a mdia deve ser em formato estreo ou mono. Cada formato de mdia codificado de maneira diferente e pode ser reproduzido somente com um tratador de mdia que suporte seu formato particular. A linha 28 declara um array com os Formats que o dispositivo de captura suporta e uma referncia Format para o formato selecionado pelo usurio (selectedFormat). Depois de obter os objetos Format, o programa precisa ter acesso aos controles de formatao do dispositivo de captura. A linha 31 declara um array para guardar os FormatControls que iro configurar o formato do dispositivo de captura. A classe CapturePlayer configura o Format desejado para a fonte de mdia atravs dos FormatControls do dispositivo (linha 31). A referncia CaptureDeviceInfo deviceInfo (linha 34) armazena as informaes do dispositivo de captura, que sero colocadas em um Vector que contm todas as informaes do dispositivo. A classe DataSource conecta os programas a fontes de dados de mdia, como dispositivos de captura. O SimplePlayer da Fig. 22.1 acessou um objeto DataSource invocando o mtodo createPlayer de Manager, passando-lhe um MediaLocator. Entretanto, a classe CapturePlayer acessa a DataSource diretamente. A linha 40 declara duas DataSources inSource conecta com a mdia do dispositivo de captura e outSource conecta fonte de dados de sada na qual a mdia capturada ser salva. O objeto que implementa a interface Processor fornece a funo bsica que controla e processa o fluxo de dados de mdia na classe CapturePlayer (linha 46). A classe tambm cria um objeto que implementa a interface DataSink para gravar os dados capturados em um arquivo (linha 43).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1133

Clicar no boto Capture and Save File configura o dispositivo de captura invocando o mtodo actionPerformed (linhas 93 a 171) na classe interna private CaptureHandler (linhas 90 a 173). Uma instncia da classe interna CaptureHandler registrada para esperar ActionEvents de captureButton (linha 62). O programa fornece aos usurios uma lista dos dispositivos de captura disponveis quando as linhas 96 e 97 invocam o mtodo static getDeviceList de CaptureDeviceManager. O mtodo getDeviceList obtm todos os dispositivos disponveis no computador que suportam o Format especificado. Especificar null como parmetro Format devolve uma lista completa de dispositivos disponveis. A classe CaptureDeviceManager permite que um programa acesse esta lista de dispositivos. As linhas 109 a 119 copiam os nomes de todos os dispositivos de captura para um array de Strings (deviceNames) para fins de exibio. As linhas 122 e 123 invocam o mtodo getSelectedDeviceIndex de CapturePlayer (linhas 195 a 210) para mostrar um dilogo de seleo com uma lista de todos os nomes de dispositivos armazenados no array deviceNames. A chamada para o mtodo showInputDialog (linhas 198 a 201) tem uma lista de parmetros diferente da de exemplos anteriores. Os quatro primeiros parmetros so o componente-pai do dilogo, a mensagem, o ttulo e o tipo de mensagem, como usado em captulos anteriores. Os ltimos trs, que so novos, especificam o cone (neste caso, null), a lista de valores apresentada para o usurio (deviceNames) e a seleo default (o primeiro elemento de deviceNames). Quando o usurio seleciona um dispositivo, o dilogo devolve o string, que usado para devolver o ndice em deviceNames do nome selecionado. Este elemento, que uma instncia de CaptureDeviceInfo, cria e configura um novo dispositivo a partir do qual a mdia desejada pode ser gravada. O objeto CaptureDeviceInfo encapsula as informaes de que o programa precisa para acessar e configurar um dispositivo de captura, como preferncias de localizao e formato. Chamar os mtodos getLocator (linha 143) e getFormats (linha 132) acessa estas pores de informaes. As linhas 129 e 130 acessam o novo CaptureDeviceInfo que o usurio especificou na deviceList. A seguir, as linhas 135 e 136 chamam o mtodo disconnect de inSource para desconectar quaisquer dispositivos de captura abertos anteriormente antes de conectar o novo dispositivo. As linhas 142 e 143 invocam o mtodo createDataSource de Manager para obter o objeto DataSource que se conecta fonte de mdia do dispositivo de captura, passando-lhe o objeto MediaLocator do dispositivo de captura como argumento. O mtodo getLocator de CaptureDeviceInfo devolve o MediaLocator do dispositivo de captura. O mtodo createDataSource, por sua vez, invoca o mtodo connect de DataSource, que estabelece uma conexo com o dispositivo de captura. O mtodo createDataSource dispara uma NoDataSourceException se no pode localizar uma DataSource para o dispositivo de captura. Se houver um erro durante a abertura do dispositivo, ocorre uma IOException. Antes de capturar a mdia, o programa precisa formatar a DataSource como especificado pelo usurio no dilogo de seleo de formato. As linhas 146 e 147 usam o mtodo getFormatControls de CaptureDevice para obter um array de FormatControls para a DataSource inSource. O objeto que implementa a interface FormatControl especifica o formato da DataSource. Os objetos DataSource podem representar outras fontes de mdia, que no dispositivos de captura, de modo que para este exemplo o operador de coero na linha 146 manipula o objeto inSource como um CaptureDevice e acessa mtodos de captura de dispositivo como getFormatControls. A linha 150 invoca o mtodo getSelectedFormat (linhas 213 a 219) para exibir um dilogo de entrada a partir do qual os usurios podem selecionar um dos formatos disponveis. As linhas 176 a 192 chamam o mtodo setDeviceFormat para configurar o formato da mdia de sada para o dispositivo de acordo com o Format selecionado pelo usurio. Cada dispositivo de captura pode ter diversos FormatControls, de modo que setDeviceFormat usa o mtodo setFormat de FormatControl para especificar o formato para cada objeto FormatControl. Formatar a DataSource completa a configurao do dispositivo de captura. O Processor (objeto inSource) converte os dados para o formato de arquivo no qual eles sero salvos. O Processor funciona como conector entre a outSource e o mtodo captureSaveFile, j que a DataSource inSource no reproduz nem salva a mdia: ela serve somente para configurar o dispositivo de captura. A linha 157 invoca o mtodo captureSaveFile (linhas 265 a 322) para executar as etapas necessrias para salvar a mdia capturada em um formato de arquivo reconhecvel. Para criar um Processor, este programa cria primeiro um ProcessorModel, um gabarito para o Processor. O ProcessorModel determina os atributos de um Processor atravs de um conjunto de informaes que inclui um DataSource ou MediaLocator, os formatos desejados para as trilhas de mdia que o Processor ir manipular e um ContentDescriptor que indica o tipo de contedo da sada. A linha 268 cria um novo array de Format (outFormats) que representa os formatos possveis para cada trilha na mdia. A linha 270 configura o formato default para o primeiro elemento do array. Para salvar a sada capturada em um arquivo, o Pro-

1134

JAVA COMO PROGRAMAR

cessor precisa primeiro converter os dados que ele recebe para um formato habilitado pelo arquivo. Um novo FileTypeDescriptor QuickTime (pacote javax.media.format) criado para armazenar uma descrio do tipo de contedo da sada do Processor e armazena em outFileType (linhas 273 e 274). As linhas 282 a 284 usam a DataSource inSource, o array outFormats e o tipo de arquivo outFileType para instanciar um novo ProcessorModel (linhas 283 e 284). Em geral, os Processors precisam ser configurados antes que eles possam processar mdia, mas o deste aplicativo no precisa, j que as linhas 282 a 284 invocam o mtodo createRealizedProcessor de Manager. Este mtodo cria um Processor realizado e configurado, com base no objeto ProcessorModel que lhe foi passado como argumento. O mtodo createRealizedProcessor dispara uma NoProcessorException se o programa no conseguir localizar um Processor para a mdia ou se o JMF no suportar o tipo de mdia. O mtodo dispara uma CannotRealizeException se o Processor no puder ser realizado. Isto pode ocorrer se um outro programa j estiver usando a mdia, bloqueando, portanto, a comunicao com a fonte de mdia.
Erro comum de programao 22.1 Tenha cuidado ao especificar formatos de trilhas. Formatos incompatveis para tipos de arquivos de sada especficos impedem que o programa realize o Processor. Observao de engenharia de software 22.2 Lembre-se de que o Processor faz transies atravs de diversos estados antes de ser realizado. O mtodo createProcessor de Manager permite que um programa fornea configurao mais personalizada antes de um Processor ser realizado. Dica de desempenho 22.2 Quando o mtodo createRealizedProcessor configura o Processor, o mtodo bloqueia at que o Processor seja realizado. Isto pode impedir que outras partes do programa sejam executadas. Em alguns casos, usar um ControllerListener para atender aos ControllerEvents pode habilitar um programa a operar de maneira mais eficiente. Quando o Processor est realizado, o ouvinte notificado, de modo que o programa possa prosseguir com o processamento da mdia.

Tendo obtido os dados de mdia do Processor em um formato de arquivo, o programa pode criar um gravador de dados para gravar a sada da mdia em um arquivo. O objeto que implementa a interface DataSink habilita os dados de mdia a serem enviados para a sada em um endereo especfico na maior parte das vezes um arquivo. A linha 287 invoca o mtodo makeDataWriter (linhas 325 a 399) para criar um objeto DataSink que pode salvar o arquivo. O mtodo createDataSink de Manager exige como argumentos a DataSource do Processor e o MediaLocator para o novo arquivo. Dentro de makeDataWriter, as linhas 229 a 242 invocam o mtodo getSaveFile para pedir aos usurios que especifiquem o nome e o endereo do arquivo no qual a mdia deve ser salva. O objeto File saveFile armazena as informaes. O mtodo getDataOutput de Processor (linha 333) devolve a DataSource da qual ele recebeu a mdia. As linhas 344 e 345 criam um novo MediaLocator para saveFile. Usando este MediaLocator e a DataSource, as linhas 349 e 350 criam um objeto DataSink que grava a mdia de sada a partir da DataSource para o arquivo no qual os dados sero salvos, como especificado pelo MediaLocator. O mtodo createDataSink dispara uma NoDataSinkException se ele no puder criar um DataSink que possa ler os dados da DataSource e envi-los para a sada no endereo especificado pelo MediaLocator. Esta falha pode ocorrer como resultado de uma mdia invlida ou um MediaLocator invlido. O programa precisa saber quando parar de enviar dados para a sada, de modo que as linhas 333 a 369 registram um DataSinkListener para esperar por DataSinkEvents. O mtodo dataSinkUpdate de DataSinkListener (linhas 359 a 365) chamado quando ocorre DataSinkEvent. Se o DataSinkEvent um EndOfStreamEvent, indicando que o Processor foi fechado porque a conexo com o fluxo de captura fechou, a linha 364 fecha a DataSink. Chamar o mtodo close de DataSink faz parar a transferncia de dados. O DataSink fechado no pode ser usado novamente.
Erro comum de programao 22.2 A sada no arquivo de mdia com um DataSink ficar corrompida se o DataSink no for fechado adequadamente.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD) Observao de engenharia de software 22.3

1135

A mdia capturada pode no gerar um EndOfMediaEvent se o ponto final da mdia no puder ser determinado.

Depois de configurar o DataSink e registrar o ouvinte, a linha 372 chama o mtodo open de DataSink para conectar o DataSink ao destino que o MediaLocator especifica. O mtodo open dispara uma SecurityException se o DataSink tentar gravar em um destino para o qual o programa no tem permisso de gravao, como um arquivo somente de leitura. A linha 373 chama o mtodo start de DataSink para iniciar a transferncia de dados. Neste ponto, o programa retorna do mtodo makeDataWriter de volta para o mtodo captureSaveFile (linhas 265 a 322). Embora o DataSink prepare a si mesmo para receber a transferncia e indique que est pronto chamando start, a transferncia no comea efetivamente enquanto o mtodo start do Processor no chamado. A invocao do mtodo start de Processor comea o fluxo de dados do dispositivo de captura, formata os dados e os transfere para o DataSink. O DataSink grava a mdia no arquivo, o que completa o processo executado pela classe CapturePlayer. Enquanto o Processor codifica os dados e o DataSink os salva em um arquivo, CapturePlayer monitora o processo. O monitoramento fornece um mtodo de supervisionar os dados enquanto o dispositivo de captura os coleta. As linhas 294 a 296 obtm um objeto que implementa a interface MonitorControl (pacote javax.media.control) a partir do Processor chamando o mtodo getControl. A linha 299 chama o mtodo getControlComponent de MonitorControl para obter o componente de GUI que exibe a mdia monitorada. Os MonitorControls normalmente tm uma caixa de marcao para habilitar ou desabilitar a exibio da mdia. Alm disso, os dispositivos de udio tm um controle de volume e os dispositivos de vdeo tm um controle para ajustar a taxa de exibio das frames. A linha 301 invoca o mtodo showSaveMonitor (linhas 245 a 261) para exibir a GUI de monitorao em uma caixa de dilogo. Para terminar a captura, os usurios podem pressionar o boto OK ou fechar a caixa de dilogo (linhas 254 a 261). Para alguns dispositivos de captura de vdeo, o Processor precisa estar em um estado Stopped para habilitar a monitorao do processo de salvamento e captura. At aqui, discutimos os recursos do JMF para acessar, apresentar e salvar contedo de mdia. Nosso exemplo final de JMF demonstra como enviar mdia entre computadores com as capacidades de streaming do JMF.

22.4 Streaming com RTP


A mdia de streaming se refere mdia que transferida de um servidor para um cliente em um fluxo contnuo de bytes. O cliente pode comear a reproduzir a mdia enquanto ainda est baixando a mdia do servidor. Os arquivos de mdia de udio e vdeo freqentemente tm vrios megabytes de tamanho. Eventos ao vivo, como transmisses de concertos ou de jogos de futebol, podem ter tamanhos indeterminados. Os usurios podem esperar at que a gravao de um concerto ou jogo seja divulgada, depois baixar a gravao inteira. Entretanto, com as velocidades atuais de conexo da Internet, baixar uma transmisso destas poderia levar dias e normalmente os usurios preferem escutar transmisses ao vivo enquanto elas acontecem. A mdia de streaming permite que os aplicativos clientes reproduzam a mdia atravs da Internet ou atravs de uma rede, sem baixar o arquivo de mdia inteiro de uma vez s. Em um aplicativo com mdia de streaming, o cliente normalmente se conecta a um servidor que envia um fluxo de bytes contendo a mdia de volta para o cliente. O aplicativo cliente coloca em um buffer (i.e., armazena localmente) uma parte da mdia, a qual o cliente comea a reproduzir depois que uma certa quantidade tenha sido recebida. O cliente coloca continuamente mais mdia no buffer, fornecendo aos usurios um clipe ininterrupto, enquanto o trfego na rede no impedir o aplicativo cliente de colocar bytes adicionais no buffer. Com o uso de buffer, o usurio reproduz a mdia segundos depois que inicia o streaming, embora todo o material ainda no tenha sido recebido.
Dica de desempenho 22.3 Fazer streaming de mdia para um cliente permite que o cliente reproduza a mdia mais rapidamente do que se o cliente esperar para baixar um arquivo de mdia inteiro.

A demanda por multimdia poderosa em tempo real est aumentando drasticamente, medida que aumenta a velocidade das conexes na Internet. O acesso Internet com banda larga, que fornece conexes de rede em alta velocidade com a Internet para tantos usurios, est se tornando mais popular, embora o nmero de usurios perma-

1136

JAVA COMO PROGRAMAR

nea relativamente pequeno em comparao ao nmero total de usurios da Internet. Com conexes mais rpidas, a mdia de streaming pode possibilitar uma experincia melhor com multimdia. Os usurios com conexes mais lentas ainda podem experimentar a multimdia, mas com qualidade inferior. A ampla variedade de aplicativos que usam mdia de streaming est crescendo. Os aplicativos que fazem streaming de clipes de vdeo para os clientes se expandiram para fornecer transmisses em tempo real. Milhares de estaes de rdio transmitem msica em stream continuamente atravs da Internet. Os aplicativos clientes como o RealPlayer se concentraram no contedo de mdia de streaming com transmisses de rdio ao vivo. Os aplicativos no se limitam ao streaming de udio e vdeo de servidor para cliente. Por exemplo, os aplicativos de teleconferncia e videoconferncia aumentam a eficincia nos negcios do dia-a-dia, reduzindo a necessidade de se viajar grandes distncias para se participar de reunies. O JMF fornece um pacote de mdia de streaming em alguns dos formatos discutidos antes neste captulo. Para obter uma lista completa de formatos, consulte o site oficial do JMF:
java.sun.com/products/java-media/jmf/2.1.1/formats.html

O JMF usa o padro de mercado Real-Time Transport Protocol (RTP) para controlar a transmisso de mdia. O RTP foi projetado especificamente para transmitir dados de mdia em tempo real. Os dois mecanismos para fazer streaming de mdia suportada por RTP so pass-la atravs de um DataSink e coloc-la em um buffer. O mecanismo mais fcil de se usar um DataSink, que escreve o contedo de um stream em um host de destino (i.e., um computador cliente), atravs das mesmas tcnicas mostradas na Fig. 22.2 para salvar mdia capturada em um arquivo. Neste caso, entretanto, o URL do MediaLocator do destino seria especificado no seguinte formato:
rtp://host:porta/tipoDeContedo

onde host o endereo IP ou nome de host do servidor, porta o nmero da porta na qual o servidor est fazendo streaming da mdia e tipoDeContedo audio ou video. Usar um DataSink como especificado permite que somente um stream seja enviado de cada vez. Para enviar vrios streams (por exemplo, como seria enviado um vdeo de karaok com trilhas separadas para vdeo e udio) para vrios hosts, um aplicativo servidor precisa usar gerenciadores de sesso de RTP. O RTPManager (pacote javax.media.rtp) permite maior controle sobre o processo de streaming, permitindo a especificao de tamanhos de buffers, verificao de erros e relatrios de streaming sobre o atraso de propagao (o tempo que leva para os dados chegarem a seu destino). O programa nas Figs. 22.3 e 22.4 demonstra o streaming com o gerenciador de sesso de RTP. Este exemplo suporta o envio de vrios streams em paralelo, de modo que clientes separados precisam ser abertos para cada stream. Este exemplo no mostra um cliente que pode receber o stream de RTP. O programa na Fig. 22.1 (SimplePlayer) pode testar o servidor RTP especificando um endereo de sesso de RTP
rtp://127.0.0.1:4000/audio

como o endereo que o SimplePlayer deve abrir para comear a reproduzir udio. Para executar o servidor de mdia de streaming em um computador diferente, substitua 127.0.0.1 pelo endereo IP ou pelo nome de host do computador servidor. O objetivo da classe RTPServer fazer streaming de contedo de mdia. Como ocorre em exemplos anteriores, o RTPServer (Fig. 22.3) configura a mdia, processa-a e formata-a, e depois a envia para a sada. Os ControllerEvents e os diversos estados do processo de streaming conduzem este processo. O processamento da mdia tem trs partes distintas inicializao do Processor, configurao do Format e transmisso dos dados. O cdigo neste exemplo contm inmeras mensagens de confirmao exibidas no prompt de comando e uma nfase na verificao de erros. Um problema durante o streaming provavelmente terminar o processo inteiro e ser necessrio recomear.

1 2 3 4 5 6

// Fig. 22.3: RTPServer.java // Oferece recursos de configurao e transmisso // para arquivos de mdia suportados por RTP // Pacotes do ncleo de Java import java.io.*;

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 1 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1137

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

import java.net.*; // Pacotes de extenso de Java import javax.media.*; import javax.media.protocol.*; import javax.media.control.*; import javax.media.rtp.*; import javax.media.format.*; public class RTPServer { // endereo IP, arquivo ou nome do MediaLocator, nmero da porta private String ipAddress, fileName; private int port; // processador que controla o fluxo de dados private Processor processor; // dados de sada do processador a serem enviados private DataSource outSource; // controles configurveis das trilhas de mdia private TrackControl tracks[]; // gerenciador de sesso de RTP private RTPManager rtpManager[]; // construtor para RTPServer public RTPServer( String locator, String ip, int portNumber ) { fileName = locator; port = portNumber; ipAddress = ip; } // inicializa e configura o processador // devolve true se bem-sucedido, false caso contrrio public boolean beginSession() { // obtm MediaLocator de endereo especfico MediaLocator mediaLocator = new MediaLocator( fileName ); if ( mediaLocator == null ) { System.err.println( "No MediaLocator found for " + fileName ); return false; } // cria processador a partir de MediaLocator try { processor = Manager.createProcessor( mediaLocator ); // registra um ControllerListener para o processador // para esperar eventos de transio de estado processor.addControllerListener( new ProcessorEventHandler() ); System.out.println( "Processor configuring..." );

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 2 de 6).

1138
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

JAVA COMO PROGRAMAR

// configura o processador antes de ajust-lo processor.configure(); } // erro de conexo com a fonte catch ( IOException ioException ) { ioException.printStackTrace(); return false; } // exceo disparada quando nenhum processador // pode ser encontrado para fonte de dados especfica catch ( NoProcessorException noProcessorException ) { noProcessorException.printStackTrace(); return false; } return true; } // fim do mtodo beginSession

// tratador ControllerListener para o processador private class ProcessorEventHandler extends ControllerAdapter { // configura formato de sada e realiza // o processador configurado public void configureComplete( ConfigureCompleteEvent configureCompleteEvent ) { System.out.println( "\nProcessor configured." ); setOutputFormat(); System.out.println( "\nRealizing Processor...\n" ); processor.realize(); } // comea a enviar quando o processador est realizado public void realizeComplete( RealizeCompleteEvent realizeCompleteEvent ) { System.out.println( "\nInitialization successful for " + fileName ); if ( transmitMedia() == true ) System.out.println( "\nTransmission setup OK" ); else System.out.println( "\nTransmission failed." ); } // faz parar a sesso de RTP quando no h mdia a enviar public void endOfMedia( EndOfMediaEvent mediaEndEvent ) { stopTransmission(); System.out.println ( "Transmission completed." ); }

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 3 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1139

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

// fim da classe interna ProcessorEventHandler

// configura o formato de sada de todas as trilhas na mdia public void setOutputFormat() { // configura o tipo de contedo da sada para formato suportado por RTP processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW_RTP ) ); // obtm todos os controles de trilha do processador tracks = processor.getTrackControls(); // formatos de uma trilha suportados por RTP Format rtpFormats[]; // configura cada trilha para o primeiro formato suportado // por RTP encontrado naquela trilha for ( int i = 0; i < tracks.length; i++ ) { System.out.println( "\nTrack #" + ( i + 1 ) + " supports " ); if ( tracks[ i ].isEnabled() ) { rtpFormats = tracks[ i ].getSupportedFormats(); // // // if se existirem formatos da trilha suportados, exibe todos os formatos suportados por RTP e configura o formato de trilha para o primeiro formato suportado ( rtpFormats.length > 0 ) { for ( int j = 0; j < rtpFormats.length; j++ ) System.out.println( rtpFormats[ j ] ); tracks[ i ].setFormat( rtpFormats[ 0 ] ); System.out.println ( "Track format set to " + tracks[ i ].getFormat() ); } else System.err.println ( "No supported RTP formats for track!" ); } } } // fim do if

// fim do lao for

// fim do mtodo setOutputFormat

// envia mdia com valor booleano indicando sucesso public boolean transmitMedia() { outSource = processor.getDataOutput(); if ( outSource == null ) { System.out.println ( "No data source from media!" ); return false;

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 4 de 6).

1140
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

JAVA COMO PROGRAMAR

} // gerenciadores de stream de RTP para cada trilha rtpManager = new RTPManager[ tracks.length ]; // endereos de sesso de RTP de destino e local SessionAddress localAddress, remoteAddress; // stream RTP que est sendo enviado SendStream sendStream; // endereo IP InetAddress ip; // inicializa endereos de transmisso e envia a mdia para a sada try { // transmite todas as trilhas na mdia for ( int i = 0; i < tracks.length; i++ ) { // instancia um RTPManager rtpManager[ i ] = RTPManager.newInstance(); // adiciona 2 para especificar o nmero de porta do prximo controle; // (o RTP Session Manager usa 2 portas) port += ( 2 * i ); // obtm endereo IP do host a partir do string ipAddress ip = InetAddress.getByName( ipAddress ); // encapsula par de endereos IP para controle e // dados com duas portas dentro do endereo de sesso local localAddress = new SessionAddress( ip.getLocalHost(), port ); // obtm o endereo de sesso remoteAddress remoteAddress = new SessionAddress( ip, port ); // inicializa a sesso rtpManager[ i ].initialize( localAddress ); // abre a sesso de RTP para o destino rtpManager[ i ].addTarget( remoteAddress ); System.out.println( "\nStarted RTP session: " + ipAddress + " " + port); // cria stream de envio na sesso de RTP sendStream = rtpManager[ i ].createSendStream( outSource, i ); // comea a enviar o stream sendStream.start(); System.out.println( "Transmitting Track #" + ( i + 1 ) + " ... " ); } // fim do lao for

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 5 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1141

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

// comea carga da mdia processor.start(); } // fim de try

// endereo local desconhecido ou endereo remoto no pode ser resolvido catch ( InvalidSessionAddressException addressError ) { addressError.printStackTrace(); return false; } // erro de conexo com a DataSource catch ( IOException ioException ) { ioException.printStackTrace(); return false; }
// formato no configurado ou formato invlido configurado na fonte de stream catch ( UnsupportedFormatException formatException ) { formatException.printStackTrace(); return false; }

// transmisso inicializada com sucesso return true; } // fim do mtodo transmitMedia

// faz parar a transmisso e fecha recursos public void stopTransmission() { if ( processor != null ) { // faz parar o processador processor.stop(); // descarta o processador processor.close(); if ( rtpManager != null ) // fecha alvos de destino // e descarta gerenciadores de RTP for ( int i = 0; i < rtpManager.length; i++ ) { // fecha streams para todos os destinos // com um motivo para terminar rtpManager[ i ].removeTargets( "Session stopped." ); // libera os recursos da sesso de RTP rtpManager[ i ].dispose(); } } // fim de if

System.out.println ( "Transmission stopped." ); } } // fim do mtodo stopTransmission

// fim da classe RTPServer

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 6 de 6).

1142

JAVA COMO PROGRAMAR

Para testar RTPServer, a classe RTPServerTest (Fig. 22.4) cria um novo objeto RTPServer e passa para o seu construtor (linhas 35 a 40) trs argumentos um String que representa o endereo da mdia, um String que representa o endereo IP do cliente e um nmero de porta para o contedo de streaming. Estes argumentos contm as informaes que a classe RTPServer precisa para obter a mdia e configurar o processo de streaming. Seguindo a abordagem genrica delineada em SimplePlayer (Fig. 22.1) e CapturePlayer (Fig. 22.2), a classe RTPServer obtm uma fonte de mdia, configura a fonte atravs de um tipo de Controller e envia os dados de sada para um destino especificado. RTPServerTest chama o mtodo beginSession de RTPServer (linhas 44 a 86) para configurar o Processor que controla o fluxo de dados. A linha 47 cria um MediaLocator e o inicializa com o endereo da mdia armazenado em fileName. A linha 58 cria um Processor para os dados especificados por aquele MediaLocator. Diferentemente do programa na Fig. 22.2, este Processor no configurado previamente por um Manager. At que a classe RTPServer configure e realize o Processor, a mdia no pode ser formatada. As linhas 62 e 63 registram um ProcessorEventHandler para reagir aos ControllerEvents de processor. Os mtodos da classe ProcessorEventHandler (linhas 89 a 127) controlam a configurao da mdia enquanto o Processor muda de estados. A linha 68 invoca o mtodo configure de Processor para colocar o Processor no estado Configuring. A configurao ocorre quando o Processor pede ao sistema e mdia as informaes necessrias para programar o Processor para executar a tarefa correta. Ocorre um ConfigureCompleteEvent quando o Processor completa a configurao. O mtodo configureComplete de ProcessEventHandler (linhas 94 a 104) responde a esta transio. O mtodo configureComplete chama o mtodo setOutputFormat (linhas 130 a 175) e depois realiza o Processor (linha 103). Quando a linha 99 invoca o mtodo setOutputFormat, ela configura cada trilha de mdia para um formato de mdia de streaming de RTP. As linhas 133 e 134 no mtodo setOutpuFormat especificam o tipo de contedo da sada chamando o mtodo setContentDescriptor de Processor. O mtodo recebe como argumento um ContentDescriptor inicializado com a constante ContentDescriptor.RAW_RTP. O tipo de contedo da sada de RTP restringe o Processor para suportar somente formatos de trilhas de mdia preparadas para RTP. O tipo de contedo da sada do Processor deve ser configurado antes que as trilhas da mdia sejam configuradas. Uma vez que o Processor esteja configurado, os formatos das trilhas de mdia devem ser ajustados. A linha 137 invoca o mtodo getTrackControls de Processor para obter um array que contm o objeto TrackControl correspondente (pacote javax.nedia.control) para cada trilha da mdia. Para cada TrackControl habilitado, as linhas 144 a 173 obtm um array de todos os Formats de mdia RTP suportados (linha 151) e depois configura o primeiro formato RTP suportado como o formato preferencial para streaming com RTP para aquela trilha (linha 161). Quando o mtodo setOutputFormat retorna, a linha 103 no mtodo configureComplete realiza o Processor. Como ocorre em qualquer realizao de controlador, o Processor pode enviar mdia para a sada assim que ele tenha terminado de realizar a si mesmo. Quando o Processor passa para o estado Realized, o ProcessorEventHandler invoca o mtodo realizeComplete (linhas 107 a 118). A linha 113 invoca o mtodo transmitMedia (linhas 178 a 271), que cria as estruturas necessrias para transmitir a mdia para o Processor. Este mtodo obtm a DataSource do Processor (linha 180), depois declara um array de RTPManagers que so capazes de iniciar e controlar uma sesso de RTP (linha 189). RTPManagers usam um par de objetos SessionAddress com endereos IP idnticos, mas nmeros de porta diferentes um para controle do stream e um para dados da mdia de streaming. O RTPManager recebe cada endereo IP e nmero de porta como um objeto SessionAddress. A linha 192 declara os objetos SessionAddress usados no processo de streaming. O objeto que implementa a interface SendStream (linha 195) faz o streaming com RTP.
Observao de engenharia de software 22.4 Para os vdeos que tm mltiplas trilhas, cada SendStream deve ter seu prprio RTPManager gerenciando sua sesso. Cada Track tem seu prprio SendStream.

O bloco try (linhas 201 a 248) do mtodo transmitMedia envia para a sada cada trilha da mdia como um stream de RTP. Primeiro, devem ser criados gerenciadores para as sesses. A linha 207 invoca o mtodo newInstance de RTPManager para instanciar um RTPManager para cada stream de trilha. A linha 211 atribui ao nmero de porta um valor 2 a mais do que o nmero de porta anterior, porque cada trilha usa um nmero de porta para o controle do stream e um para realmente fazer streaming dos dados. As linhas 218 e 219 instanciam um novo

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1143

endereo local de sesso para onde o stream est localizado (i.e., o endereo de RTP que os clientes usam para obter o stream de mdia) com o endereo IP local e um nmero de porta como parmetros. A linha 219 invoca o mtodo getLocalHost de InetAddress para obter o endereo IP local. A linha 222 instancia o endereo de sesso do cliente, que o RTPManager usa como o alvo de destino do stream. Quando a linha 225 chama o mtodo initialize de RTPManager, o mtodo direciona a sesso de streaming local para usar o endereo de sesso local. Usando o objeto remoteAddress como parmetro, a linha 228 chama o mtodo addTarget de RTPManager para abrir a sesso de destino no endereo especificado. Para fazer stream de mdia para vrios clientes, chama o mtodo addTarget de RTPManager para cada endereo de destino. O mtodo addTarget deve ser chamado aps a sesso ser inicializada e antes que qualquer stream seja criado durante a sesso. Agora o programa pode criar os streams na sesso e comear a enviar dados. Os streams so criados na sesso de RTP atual com a DataSource outSource (obtida na linha 180) e o ndice de fonte de stream (i.e., ndice de trilha da mdia) nas linhas 234 e 235. Invocar o mtodo start sobre o SendStream (linha 238) e sobre o Processor (linha 246) inicia a transmisso dos streams de mdia, o que pode provocar excees. Ocorre uma InvalidSessionAddressException quando o endereo de sesso especificado invlido. Ocorre uma UnsupportedFormatException quando um formato de mdia no-suportado especificado ou se o Format da DataSource no foi configurado. Ocorre uma IOException se o aplicativo encontra problemas na rede. Durante o processo de streaming, RTPManagers podem ser usados com classes relacionadas dos pacotes javax.media.rtp e javax.media.rtp.event para controlar o processo de streaming e enviar relatrios para o aplicativo. O programa deve fechar as conexes e parar a transmisso em streaming quando ele atinge o fim da mdia de streaming ou quando o programa termina. Quando o Processor encontra o fim da mdia, ele gera um EndOfMediaEvent. Em resposta, o programa chama o mtodo endOfMedia (linhas 121 a 125). A linha 123 invoca o mtodo stopTransmission (linhas 274 a 303) para parar e fechar o Processor (linhas 279 a 282). Depois de chamar stopTransmission, no possvel retomar o streaming porque ele descarta o Processor e os recursos da sesso de RTP. As linhas 288 a 297 invocam o mtodo removeTargets de RTPManager (linhas 292 e 293) para fechar o streaming para todos os destinos. O mtodo dispose de RTPManager (linha 296) tambm invocado, liberando os recursos mantidos pelas sesses de RTP. A classe RTPServerTest (Fig. 22.4) invoca explicitamente o mtodo stopTransmission quando o usurio termina o aplicativo servidor (linha 40).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // Fig. 22.4: RTPServerTest.java // Testa a classe para RTPServer // Pacotes do ncleo de Java import java.awt.event.*; import java.io.*; import java.net.*; // Pacotes de extenso de Java import javax.swing.*; public class RTPServerTest extends JFrame { // objeto que trata de streaming com RTP private RTPServer rtpServer; // fontes de mdia e endereos de destino private int port; private String ip, mediaLocation; private File mediaFile; // botes da GUI private JButton transmitFileButton, transmitUrlButton; // construtor para RTPServerTest public RTPServerTest() {

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 1 de 5).

1144
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

JAVA COMO PROGRAMAR

super( "RTP Server Test" ); // registra um WindowListener para eventos da frame addWindowListener( // classe interna annima para tratar WindowEvents new WindowAdapter() { public void windowClosing( WindowEvent windowEvent ) { if ( rtpServer != null ) rtpServer.stopTransmission(); } } // fim de WindowAdpater

); // fim da chamada para o mtodo addWindowListener // painel contendo GUI de boto JPanel buttonPanel = new JPanel(); getContentPane().add( buttonPanel ); // GUI de boto para transmitir arquivo transmitFileButton = new JButton( "Transmit File" ); buttonPanel.add( transmitFileButton ); // registra ActionListener para eventos de transmitFileButton transmitFileButton.addActionListener( new ButtonHandler() ); // GUI para boto de URL de transmisso transmitUrlButton = new JButton( "Transmit Media" ); buttonPanel.add( transmitUrlButton ); // registra ActionListener para eventos de transmitURLButton transmitUrlButton.addActionListener( new ButtonHandler() ); } // fim do construtor

// classe interna que trata eventos do boto de transmisso private class ButtonHandler implements ActionListener { // abre e tenta enviar arquivo para destino digitado pelo usurio public void actionPerformed( ActionEvent actionEvent ) { // se transmitFileButton foi invocado, obtm string de URL do arquivo if ( actionEvent.getSource() == transmitFileButton ) { mediaFile = getFile(); if ( mediaFile != null ) // obtm string de URL do arquivo try { mediaLocation = mediaFile.toURL().toString(); } // caminho para o arquivo no pode ser resolvido catch ( MalformedURLException badURL ) {

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 2 de 5).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1145

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

badURL.printStackTrace(); } else return; } // fim do if

// seno, transmitMediaButton foi invocado, obtm endereo else mediaLocation = getMediaLocation(); if ( mediaLocation == null ) return; // obtm endereo IP ip = getIP(); if ( ip == null ) return; // obtm nmero de porta port = getPort(); // verifica se o nmero de porta positivo e vlido if ( port <= 0 ) { if ( port != 999 ) System.err.println( "Invalid port number!" ); return; } // instancia novo servidor de streaming RTP rtpServer = new RTPServer( mediaLocation, ip, port ); rtpServer.beginSession(); } } // fim do mtodo actionPeformed

// fim da classe interna ButtonHandler

// obtm arquivo do computador public File getFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // obtm endereo da mdia do usurio public String getMediaLocation() {

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 3 de 5).

1146
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

JAVA COMO PROGRAMAR

String input = JOptionPane.showInputDialog( this, "Enter MediaLocator" ); // se o usurio pressiona OK sem digitar algo if ( input != null && input.length() == 0 ) { System.err.println( "No input!" ); return null; } return input; } // mtodo que obtm string de IP do usurio public String getIP() { String input = JOptionPane.showInputDialog( this, "Enter IP Address: " ); // se o usurio pressiona OK sem digitar algo if ( input != null && input.length() == 0 ) { System.err.println( "No input!" ); return null; } return input; } // obtm nmero de porta public int getPort() { String input = JOptionPane.showInputDialog( this, "Enter Port Number: " ); // devolve valor indicador se o usurio clica em OK sem digitar algo if ( input != null && input.length() == 0 ) { System.err.println( "No input!" ); return 999; } // devolve valor indicador se o usurio clicou em CANCEL if ( input == null ) return 999; // seno, devolve dados digitados return Integer.parseInt( input ); } // fim do mtodo getPort

// executa o aplicativo public static void main( String args[] ) { RTPServerTest serverTest = new RTPServerTest(); serverTest.setSize( 250, 70 ); serverTest.setLocation( 300, 300 );

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 4 de 5).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1147

206 207 208 209 210

serverTest.setDefaultCloseOperation( EXIT_ON_CLOSE ); serverTest.setVisible( true ); } } // fim da classe RTPServerTest

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 5 de 5).

1148

JAVA COMO PROGRAMAR

22.5 Java Sound


Muitos dos programas de computador de hoje em dia atraem a ateno do usurio com recursos de udio. At mesmo applets e aplicativos bsicos podem melhorar a experincia do usurio com sons ou clipes simples de msica. Com interfaces de programao de som, os desenvolvedores podem criar aplicativos que reproduzem sons em resposta s interaes do usurio. Por exemplo, em muitos aplicativos, quando ocorre um erro e uma caixa de dilogo aparece na tela, o dilogo freqentemente acompanhado por um som. Os usurios, portanto, recebem tanto indicaes visuais quanto auditivas de que ocorreu um problema. Como outro exemplo, os programadores de jogos usam recursos de udio extensivos para melhorar a experincia dos jogadores. A API Java Sound uma maneira de incorporar mdia de udio em aplicativos mais simples do que o Java Media Framework. A API Java Sound vem junto com o Java 2 Software Development Kit verso 1.3. A API consiste em quatro pacotes javax.sound.midi, javax.sound.midi.spi, javax.sound.sampled e javax.sound.sampled.spi. As prximas duas sees se concentram nos pacotes javax.sound.midi e javax.sound.sampled, que fornecem classes e interfaces para acessar, manipular e reproduzir udio Musical Instrument Digital Interface (MIDI) e como amostra. Os pacotes que terminam com .spi fornecem aos desenvolvedores ferramentas para adicionar o suporte ao Java Sound a formatos de udio adicionais que esto alm do escopo deste livro. A API Java Sound fornece acesso Java Sound Engine, que cria udio digitalizado e captura mdia a partir dos dispositivos de som discutidos na Seo 22.3. O Java Sound exige uma placa de som para reproduzir udio. Um programa que usa Java Sound ir disparar uma exceo se ele acessar recursos de udio do sistema em um computador que no tem uma placa de som.

22.6 Reproduzindo amostras de udio


Esta seo apresenta os recursos do pacote javax.sound.sampled para reproduzir formatos de arquivos de amostras de som, que incluem Sun Audio (.au), Windows Waveform (.wav) e Macintosh Audio Interchange File Format (.aiff). O programa nas Fgs. 22.5 e 22.6 mostra como se reproduz udio como estes formatos de arquivo. Ao se processar dados de udio, uma linha fornece o caminho atravs do qual o udio flui em um sistema. Um exemplo de uma linha um par de fones de ouvido conectados a um reprodutor de CD. A classe ClipPlayer (Fig. 22.5) um exemplo de como as linhas podem ser usadas. Ela contm um objeto que implementa a interface Clip, que por sua vez estende a interface DataLine. O Clip uma linha que processa um arquivo de udio inteiro em vez de ler continuamente de um stream de udio. As DataLines melhoram as Lines fornecendo mtodos adicionais (como start e stop) para controlar o fluxo de dados, e os Clips melhoram as DataLines fornecendo mtodos para abrir Clips e mtodos para controle preciso sobre reproduo e repetio do udio.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Fig. 22.5: ClipPlayer.java // Reproduz arquivos de clipes de udio dos tipos WAV, AU, AIFF // Pacotes do ncleo de Java import java.io.*; // Pacotes de extenso de Java import javax.sound.sampled.*; public class ClipPlayer implements LineListener { // stream de entrada de udio private AudioInputStream soundStream; // linha de clipe de amostra de udio private Clip clip;

Fig. 22.5

ClipPlayer reproduz um arquivo de udio (parte 1 de 4).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1149

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

// arquivo de clipe de udio private File soundFile; // booleana que indica repetio do udio private boolean replay = false; // construtor para ClipPlayer public ClipPlayer( File audioFile ) { soundFile = audioFile; } // abre arquivo de msica, devolvendo true se bem-sucedido public boolean openFile() { // obtm stream de udio do arquivo try { soundStream = AudioSystem.getAudioInputStream( soundFile ); } // arquivo de udio no-suportado por JavaSound catch ( UnsupportedAudioFileException audioException ) { audioException.printStackTrace(); return false; } // erro de E/S quando tentava obter stream catch ( IOException ioException ) { ioException.printStackTrace(); return false; } // invoca loadClip, devolvendo true se carga bem-sucedida return loadClip(); } // fim do mtodo openFile

// carrega clipe de som public boolean loadClip () { // obtm linha de clipe para o arquivo try { // obtm formato de udio do arquivo de som AudioFormat audioFormat = soundStream.getFormat(); // define informaes da linha com base no tipo de linha, // codificao e tamanhos de frame do arquivo de udio DataLine.Info dataLineInfo = new DataLine.Info( Clip.class, AudioSystem.getTargetFormats( AudioFormat.Encoding.PCM_SIGNED, audioFormat ), audioFormat.getFrameSize(), audioFormat.getFrameSize() * 2 ); // assegura que o sistema de som suporta linha de dados if ( !AudioSystem.isLineSupported( dataLineInfo ) ) { System.err.println( "Unsupported Clip File!" ); return false;

Fig. 22.5

ClipPlayer reproduz um arquivo de udio (parte 2 de 4).

1150
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

JAVA COMO PROGRAMAR

} // obtm recurso para a linha de clipe clip = ( Clip ) AudioSystem.getLine( dataLineInfo ); // espera por eventos da linha de clipe clip.addLineListener( this ); // abre clipe de udio e obtm os recursos necessrios do sistema clip.open( soundStream ); } // fim do try

// recurso de linha no-disponvel catch ( LineUnavailableException noLineException ) { noLineException.printStackTrace(); return false; } // erro de E/S durante interpretao dos dados de udio catch ( IOException ioException ) { ioException.printStackTrace(); return false; } // arquivo de clipe carregado com sucesso return true; } // fim do mtodo loadClip

// inicia reproduo do clipe de udio public void play() { clip.start(); } // mtodo listener de eventos de linha para parar ou repetir no fim do clipe public void update( LineEvent lineEvent ) { // se o clipe chega ao fim, fecha o clipe if ( lineEvent.getType() == LineEvent.Type.STOP && !replay ) close(); // se repetio ligada, repete para sempre else if ( lineEvent.getType() == LineEvent.Type.STOP && replay ) { System.out.println( "replay" ); // repete o clipe para sempre clip.loop( Clip.LOOP_CONTINUOUSLY ); } } // configura repetio do clipe public void setReplay( boolean value )

Fig. 22.5

ClipPlayer reproduz um arquivo de udio (parte 3 de 4).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1151

137 138 139 140 141 142 143 144 145 146 147 148 149 150

{ replay = value; } // pra e fecha o clipe, devolvendo recursos do sistema public void close() { if ( clip != null ) { clip.stop(); clip.close(); } } } // fim da classe ClipPlayer

Fig. 22.5

ClipPlayer reproduz um arquivo de udio (parte 4 de 4).

Todas as Lines geram LineEvents, que podem ser tratados por LineListeners. Os LineEvents ocorrem quando se est iniciando, parando, reproduzindo e fechando um objeto Line. Embora uma Line pare a reproduo automaticamente quando ela atinge o fim de um arquivo de udio, a classe ClipPlayer implementa a interface LineListener (linha 10) e pode fechar o Clip permanentemente ou repetir o Clip (discutido em seguida). Os LineListeners so teis para as tarefas que precisam ser sincronizadas com os estados de LineEvent de uma linha. O Clip l dados de udio de um AudioInputStream (uma subclasse de InputStream), que fornece acesso ao contedo de dados do stream. Este exemplo carrega os clipes dos dados de udio antes de tentar reproduzi-lo e, portanto, capaz de determinar o tamanho do clipe em frames. Cada frame representa dados em um intervalo de tempo especfico no arquivo de udio. Para reproduzir os arquivos de amostra de udio com o Java Sound, o programa precisa obter um AudioInputStream de um arquivo de udio, obter uma linha de Clip formatada, carregar o AudioInputStream na linha de Clip e iniciar o fluxo de dados na linha de Clip. Para reproduzir a amostra de udio, o stream de udio deve ser obtido de um arquivo de udio. O mtodo openFile de ClipPlayer (linhas 31 a 54) obtm udio do soundFile (inicializado no construtor de ClipPlayer nas linhas 25 a 28). As linhas 35 e 36 chamam o mtodo static getAudioInputStream de AudioSystem para obter um AudioInputStream para soundFile. A classe AudioSystem facilita o acesso a muitos dos recursos necessrios para reproduzir e manipular arquivos de som. O mtodo getAudioInputStream dispara uma UnsupportedAudioFileException se o arquivo de som especificado no for um arquivo de udio ou se ele contiver um formato que no suportado por Java Sound. A seguir, o programa precisa fornecer uma linha atravs da qual os dados de udio podem ser processados. A linha 52 invoca o mtodo loadClip (linhas 57 a 106) para abrir uma linha de Clip e carregar o stream de udio para reproduo. A linha 81 invoca o mtodo static getLine de AudioSystem para obter uma linha de Clip para reproduo de udio. O mtodo getLine exige um objeto Line.Info como um argumento, para especificar os atributos da linha que o AudioSystem deve devolver. A linha deve ser capaz de processar clipes de udio de todos os formatos de amostra de udio suportados, de modo que o objeto DataLine.Info deve especificar uma linha de dados de Clip e um formato de codificao genrica. O intervalo de buffer tambm deve ser especificado para que o programa possa determinar o melhor tamanho de buffer. O construtor de DataLine.Info recebe quatro argumentos. Os dois primeiros so o formato (do tipo AudioFormat.Encoding) para o qual o programa deve converter os dados de udio e o AudioFormat da fonte de udio. O AudioFormat configura o formato suportado pela linha, de acordo com o formato de udio do stream. A linha 63 obtm o AudioFormat do AudioInputStream, que contm especificaes de formato que o sistema subjacente usa para traduzir os dados em sons. As linhas 68 e 69 chamam o mtodo getTargetFormats de AudioSystem para obter um array com os AudioFormats suportados. O terceiro argumento do construtor DataLine.Info, que especifica o tamanho mnimo do buffer, configurado com o nmero de bytes em cada frame do stream de udio. A linha 70 invoca o mtodo getFrameSize de AudioFormat para obter o tamanho de cada frame no stream de udio. O tamanho mximo do buffer deve ser equivalente a duas frames do stream de udio (linha 71). Usando o objeto DataLine.Info, a linha 74 verifica se o sistema de udio subjacente suporta a linha especificada. Se suportar, a linha 81 obtm

1152

JAVA COMO PROGRAMAR

a linha do sistema de udio. Quando um clipe de udio comea a ser reproduzido e termina, o programa precisa ser alertado. A linha 84 registra um Linelistener para os LineEvents do Clip. Se ocorre um LineEvent, o programa chama o mtodo update de LineListener (linhas 115 a 133) para process-lo. Os quatro tipos de LineEvent, como definidos na classe LineEvent.Type, so OPEN, CLOSE, START e STOP. Quando o tipo de evento LineEvent.Type.STOP e a varivel replay false, a linha 120 chama o mtodo close de ClipPlayer (linhas 142 a 148) para parar a reproduo de udio e fechar o Clip. Todos os recursos de udio obtidos anteriormente pelo Clip so liberados quando a reproduo do udio pra. Quando o tipo de evento LineEvent.Type.STOP e a varivel replay true, a linha 131 chama o mtodo loop de Clip com o parmetro Clip.LOOP_CONTONUOUSLY, fazendo com que o Clip seja repetido at que o usurio termine o aplicativo. Invocar o mtodo stop da interface Clip para a atividade de dados na Line. Invocar o mtodo start retoma a atividade de dados. Assim que o programa termina de validar o Clip, a linha 87 chama o mtodo open de Clip com o AudioInputStream soundStream como argumento. O Clip obtm os recursos do sistema necessrios para reproduo de udio. O mtodo getLine de AudioSystem e o mtodo open de Clip disparam LineUnavailableExceptions se outro aplicativo est usando o recurso de udio solicitado. O mtodo open de Clip tambm dispara uma IOException se o Clip no consegue ler o AudioInputStream especificado. Quando o programa de teste (Fig. 22.6) chama o mtodo play de ClipPlayer (linhas 109 a 112), o mtodo start de Clip inicia a reproduo do udio. A classe ClipPlayerTest (Fig. 22.6) permite especificar um arquivo de udio a ser reproduzido clicando no boto Open Audio Clip. Quando os usurios clicam no boto, o mtodo actionPerformed (linhas 37 a 58) solicita nome e endereo de arquivo de udio (linha 39) e cria um ClipPlayer para o arquivo de udio especificado (linha 44). A linha 47 invoca o mtodo openFile de ClipPlayer, que devolve true se o ClipPlayer consegue abrir o arquivo de udio. Se for assim, a linha 50 chama o mtodo play de ClipPlayer para reproduzir o udio e a linha 53 chama o mtodo setReplay de CliPlayer para indicar que o udio no deve ser repetido continuamente.
Dica de desempenho 22.4 Os arquivos grandes de udio exigem um tempo longo para carregar, dependendo da velocidade do computador. Uma maneira alternativa de reproduzir colocar o udio em um buffer, carregando uma parte dos dados para comear a reproduo e continuando a carregar o restante medida que o udio reproduzido. Isto semelhante capacidade de streaming fornecida pelo JMF.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

// Fig. 22.6: ClipPlayerTest.java // Testa arquivo para ClipPlayer // Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; // Pacotes de extenso de Java import javax.swing.*; public class ClipPlayerTest extends JFrame { // objeto para reproduzir clipes de udio private ClipPlayer clipPlayer; // construtor para ClipPlayerTest public ClipPlayerTest() { super( "Clip Player" );

Fig. 22.6

ClipPlayerTest permite especificar o nome e o endereo do udio a ser reproduzido com ClipPlayer (parte 1 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1153

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

// painel contendo botes JPanel buttonPanel = new JPanel(); getContentPane().add( buttonPanel ); // boto de abrir arquivo JButton openFile = new JButton( "Open Audio Clip" ); buttonPanel.add( openFile, BorderLayout.CENTER ); // registra ActionListener para eventos de openFile openFile.addActionListener( // classe interna annima para tratar o ActionEvent de openFile new ActionListener() { // tenta abrir e reproduzir um arquivo de clipe de udio public void actionPerformed( ActionEvent event ) { File mediaFile = getFile(); if ( mediaFile != null ) { // instancia novo ClipPlayer com mediaFile clipPlayer = new ClipPlayer( mediaFile ); // se o ClipPlayer foi aberto corretamente if ( clipPlayer.openFile() == true ) { // reproduz o clipe carregado clipPlayer.play(); // sem repetio clipPlayer.setReplay( false ); } } } } // fim do if mediaFile

// fim de actionPerformed

// fim de ActionListener

); // fim da chamada para addActionListener } // fim do construtor

// obtm o arquivo do computador public File getFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); }

Fig. 22.6

ClipPlayerTest permite especificar o nome e o endereo do udio a ser reproduzido com ClipPlayer (parte 2 de 3).

1154
81 82 83 84 85 86 87 88 89 90 91 92 93

JAVA COMO PROGRAMAR

// executa o aplicativo public static void main( String args[] ) { ClipPlayerTest test = new ClipPlayerTest(); test.setSize( 150, 70 ); test.setLocation( 300, 300 ); test.setDefaultCloseOperation( EXIT_ON_CLOSE ); test.setVisible( true ); } } // fim da classe ClipPlayerTest

Fig. 22.6

ClipPlayerTest permite especificar o nome e o endereo do udio a ser reproduzido com ClipPlayer (parte 3 de 3).

22.7 Musical Instrument Digital Interface (MIDI)


A Musical Instrument Digital Interface (MIDI) o formato-padro para a msica eletrnica. Pode-se criar msica MIDI atravs de um instrumento digital, como um teclado eletrnico, ou atravs de software. A interface MIDI permite criar msica digital sintetizada que reproduz a verdadeira msica. Os msicos podem compartilhar suas criaes musicais com aficionados por msica do mundo inteiro. Um sintetizador MIDI um dispositivo que pode produzir sons e msica MIDI. Os programas podem manipular facilmente dados MIDI. Como ocorre com outros tipos de udio, os dados MIDI tm um formato bem-definido que os reprodutores de MIDI podem interpretar, reproduzir e usar para criar novos dados MIDI. A Complete Detailed MIDI 1.0 Specification fornece informaes detalhadas sobre os arquivos MIDI. Visite o site oficial sobre MIDI na Web em www.midi.org para obter informaes sobre MIDI e sua especificao. Os pacotes para MIDI de Java Sound (javax.sound.midi e javax.sound.midi.spi) permitem acessar dados MIDI. A interpretao de dados MIDI varia entre os sintetizadores, de modo que um arquivo pode soar bem diferente quando reproduzido em sintetizadores diferentes daquele no qual foram criados. Os sintetizadores suportam tipos e nmeros de sons instrumentais que variam e quantidades diferentes de sons simultneos. Usualmente, os sintetizadores baseados em hardware so capazes de produzir msica sintetizada de qualidade mais alta do que os sintetizadores baseados em software. Muitos sites da Web e jogos usam MIDI para reproduo de msica, pois ele permite que os desenvolvedores divirtam os usurios com arquivos enormes de msica digitalizada, que no exigem muita memria. Em compara-

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1155

o, arquivos de amostra de udio podem se tornar bastante grandes. O pacote javax.sound.midi permite manipular, reproduzir e sintetizar MIDI. O Java Sound suporta arquivos MIDI com extenses mid e rmf (Rich Music Format ou RMF). O exemplo apresentado nas Sees 22.7.1 a 22.7.4 abrange a sntese, a reproduo, a gravao e o salvamento de MIDI. A classe MidiDemo (Fig. 22.10) a classe principal do aplicativo, que usa as classes MidiData (Fig. 22.7), MidiRecord (Fig. 22.8) e MidiSynthesizer (Fig. 22.9). A classe MidiSynthesizer fornece recursos para gerar sons e transmiti-los para outros dispositivos MIDI, como gravadores. A classe MidiData trata da reproduo de MIDI, da inicializao de trilhas e das informaes sobre eventos. A classe MidiRecord fornece recursos para gravao de MIDI. A classe MidiDemo rene as outras classes com uma GUI interativa que inclui um teclado de piano simulado, botes de reproduzir e gravar, e um painel de controle para configurar opes de MIDI. A classe MidiDemo tambm usa a sincronizao de eventos MIDI para reproduzir um arquivo MIDI e destacar as teclas apropriadas no piano, imitando algum tocando no teclado. Uma parte integrante deste exemplo MIDI a sua GUI, que permite aos usurios tocar notas musicais em um teclado do piano simulado (ver captura de tela na Fig. 22.10). Quando o mouse passa sobre uma tecla do piano, o programa reproduz a nota correspondente. Nesta seo, referimo-nos a isto como sntese pelo usurio. O boto Play MIDI na GUI permite selecionar um arquivo MIDI a ser reproduzido. O boto Record grava as notas tocadas no piano (sntese pelo usurio). Os usurios podem salvar o MIDI gravado em um arquivo usando o boto Save MIDI e reproduzir o MIDI gravado o boto Playback. Os usurios podem clicar no boto Piano Player para abrir um arquivo MIDI, depois reproduzir aquele arquivo de volta atravs de um sintetizador. O programa indica a sincronizao de nota e das teclas do piano destacando a tecla que corresponde ao nmero da nota. Esta capacidade de reproduo e sincronizao se chama tocador de piano. Enquanto o tocador de piano est sendo executado, os usurios podem sintetizar as notas adicionais e gravar tanto o material de udio antigo quanto as novas notas sintetizadas pelo usurio, clicando no boto Record. A JComboBox no canto superior esquerdo da GUI permite selecionar um instrumento para sntese. Os componentes adicionais da GUI incluem um controle de volume para notas sintetizadas pelo usurio e um controle de tempo para controlar a velocidade do tocador de piano.
Dica de teste e depurao 22.1 Para testar as funes de reproduo de arquivos MIDI so necessrios uma placa de som e um arquivo de udio no formato MIDI.

22.7.1 Reproduo de MIDI


Esta seo discute como reproduzir arquivos MIDI e como acessar e interpretar o contedo de arquivos MIDI. A classe MidiData (Fig. 22.7) contm os mtodos que carregam um arquivo MIDI para reproduo. A classe tambm fornece as informaes sobre as trilhas MIDI exigidas pelo recurso de tocador de piano. Usa-se um seqenciador MIDI para reproduzir e manipular os dados de udio. Freqentemente, os dados MIDI so conhecidos como seqncia, porque os dados musicais em um arquivo MIDI so compostos por uma seqncia de eventos. As etapas executadas na reproduo de MIDI so acessar um seqenciador, carregar uma seqncia ou arquivo MIDI naquele seqenciador e iniciar o seqenciador.

1 2 3 4 5 6 7 8 9 10 11 12 13

// Fig. 22.7: MidiData.java // Contm informaes sobre seqncias de MIDI // com mtodo de acesso e mtodos de reproduo de MIDI // Pacotes do ncleo de Java import java.io.*; // Pacotes de extenso de Java import javax.sound.midi.*; public class MidiData { // dados das trilhas MIDI

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 1 de 4).

1156
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

JAVA COMO PROGRAMAR

private Track track; // reprodutor para seqncias MIDI private Sequencer sequencer; // seqncia MIDI private Sequence sequence; // eventos MIDI contendo tempo e MidiMessages private MidiEvent currentEvent, nextEvent; // mensagem MIDI usualmente contendo mensagens de som private ShortMessage noteMessage; // mensagens MIDI short, meta ou sysex private MidiMessage message; // ndice do evento MIDI na trilha, comando em uma mensagem MIDI private int eventIndex = 0, command; // mtodo para reproduzir seqncia MIDI atravs de um seqenciador public void play() { // inicia seqenciador-padro try { // obtm seqenciador de MidiSystem sequencer = MidiSystem.getSequencer(); // abre recursos do seqenciador sequencer.open(); // carrega MIDI no seqenciador sequencer.setSequence( sequence ); // reproduz seqncia sequencer.start(); } // erro de disponibilidade de recurso MIDI catch ( MidiUnavailableException noMidiException ) { noMidiException.printStackTrace(); } // encontrado arquivo MIDI corrompido ou invlido catch ( InvalidMidiDataException badMidiException ) { badMidiException.printStackTrace(); } } // fim do mtodo play

// mtodo que devolve tempo/resoluo ajustados do MIDI public int getResolution() { return 500 / sequence.getResolution(); } // obtm MIDI e prepara trilha em MIDI para ser acessada public boolean initialize( File file )

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 2 de 4).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1157

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

{ // coloca MIDI vlido do arquivo na seqncia try { sequence = MidiSystem.getSequence( file ); } // arquivo MIDI ilegvel ou MIDI no-suportado catch ( InvalidMidiDataException badMIDI ) { badMIDI.printStackTrace(); return false; } // erro de E/S gerado durante a leitura do arquivo catch ( IOException ioException ) { ioException.printStackTrace(); return false; } return true; } // fim do mtodo initialize

// prepara trilha mais longa para ser lida e obtm primeiro evento MIDI public boolean initializeTrack() { // obtm todas as trilhas da seqncia Track tracks[] = sequence.getTracks(); if ( tracks.length == 0 ) { System.err.println( "No tracks in MIDI sequence!" ); return false; } track = tracks[ 0 ]; // encontra trilha mais longa for ( int i = 0; i < tracks.length; i++ ) if ( tracks[ i ].size() > track.size() ) track = tracks[ i ]; // configura evento MIDI corrente para primeiro evento na trilha currentEvent = track.get( eventIndex ); // obtm menssagem MIDI do evento message = currentEvent.getMessage(); // inicializao de trilha bem sucedida return true; } // fim do mtodo initializeTrack

// prossegue para prximo evento na trilha public void goNextEvent() { eventIndex++; currentEvent = track.get( eventIndex ); message = currentEvent.getMessage(); }

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 3 de 4).

1158
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

JAVA COMO PROGRAMAR

// obtm intervalo de tempo entre eventos public int getEventDelay() { // o primeiro intervalo de tempo do evento a sua durao if ( eventIndex == 0 ) return ( int ) currentEvent.getTick(); // diferena de tempo entre evento corrente e o prximo return ( int ) ( track.get( eventIndex + 1 ).getTick() currentEvent.getTick() ); } // retorna se a trilha terminou public boolean isTrackEnd() { // se eventIndex menor do que o nmero de eventos da trilha if ( eventIndex + 1 < track.size() ) return false; return true; } // obtm comando ShortMessage corrente do evento public int getEventCommand() { if ( message instanceof ShortMessage ) { // obtm MidiMessage para fins de acesso noteMessage = ( ShortMessage ) message; return noteMessage.getCommand(); } return 1; } // obtm o nmero da nota do evento corrente public int getNote() { if ( noteMessage != null ) return noteMessage.getData1(); return 1; } // obtm o volume do evento corrente public int getVolume() { return noteMessage.getData2(); } } // fim da classe MidiData

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 4 de 4).

Para reproduzir um arquivo MIDI com uma seqncia, o programa precisa obter a seqncia MIDI e verificar os aspectos de compatibilidade. O mtodo initialize de MidiData (linhas 73 a 94) obtm uma Sequence de dados MIDI de um arquivo com o mtodo getSequence de MidiSystem (linha 77). A Sequence contm trilhas MIDI, as quais, por sua vez, contm eventos MIDI. Cada evento encapsula uma mensagem MIDI de instrues para os dispositivos MIDI. As trilhas individuais de uma seqncia MIDI so anlogas s trilhas em um CD.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1159

Entretanto, enquanto as trilhas de CD so reproduzidas na ordem, as trilhas MIDI so reproduzidas em paralelo. A trilha MIDI uma seqncia de dados gravada. As MIDIs normalmente contm mltiplas trilhas. O mtodo getSequence tambm pode obter uma seqncia MIDI de um URL ou um InputStream. O mtodo getSequence dispara uma InvalidMidiDataException se o sistema MIDI detecta um arquivo MIDI incompatvel.
Dica de portabilidade 22.2 Devido incompatibilidade entre os reconhecedores de arquivos em sistemas operacionais diferentes, os seqenciadores podem no conseguir reproduzir arquivos RMF.

Aps obter uma seqncia MIDI vlida, o programa precisa obter um seqenciador e carregar a seqncia no seqenciador. O mtodo play (linhas 35 a 64) na classe MidiData chama o mtodo getSequencer de MidiSystem (linha 41) para obter um Sequencer para reproduzir a Sequence. A interface Sequencer, que estende a interface MidiDevice (a super-interface para todos os dispositivos MIDI), fornece o dispositivo seqenciador padro para reproduzir os dados MIDI. Se um outro programa est usando o mesmo objeto Sequencer, o mtodo getSequencer dispara uma MidiUnavailableException. A linha 44 chama o mtodo open de Sequencer para se preparar para reproduzir uma Sequence. O mtodo setSequence de Sequencer (linha 47) carrega uma Sequence MIDI no Sequencer e dispara uma InvalidMididException se o Sequencer detectar uma seqncia MIDI irreconhecvel. A linha 50 comea a reproduzir a seqncia MIDI chamando o mtodo start do Sequencer. Alm dos mtodos de reproduo de MIDI, a classe MidiData tambm fornece mtodos que permitem a um programa acessar os eventos e as mensagens de uma seqncia MIDI. Como veremos, a classe MidiDemo (Fig. 22.10) usa a classe MidiData para acessar os dados em um arquivo MIDI para sincronizar o destaque das teclas do piano. Os eventos MIDI so armazenados nas trilhas de MIDI, que so instncias da classe Track (pacote javax.sound.midi). Os eventos MIDI em trilhas MIDI so representados pela classe MidiEvent (pacote javax.sound.midi). Cada evento MIDI contm uma instruo e o tempo em que ela deve ocorrer. Os eventos individuais em uma trilha contm mensagens do tipo MidiMessage que especificam as instrues MIDI para um MidiDevice. Existem trs tipos de mensagens MIDI ShortMessage, SysexMessage e MetaMessage. ShortMessages so instrues musicais explcitas, como as notas especficas a tocar e mudanas de freqncia. As outras duas mensagens, menos usadas, so as SysexMessages, mensagens de uso exclusivo do sistema para dispositivos MIDI, e MetaMessages, que podem indicar para um dispositivo MIDI que o MIDI atingiu o fim de uma trilha. Esta seo trata exclusivamente de ShortMessages que reproduzem notas especficas. A seguir, o programa precisa obter as trilhas e ler seus eventos. O mtodo initializeTrack de MidiData (linhas 97 a 125) invoca o mtodo getTracks da Sequence (linha 100) para obter todas as trilhas na seqncia MIDI. As linhas 108 a 114 determinam a trilha mais longa no MIDI e a configuram como aquela a ser reproduzida. A linha 117 obtm o primeiro evento MIDI na Track invocando seu mtodo get com o ndice do evento na trilha como o parmetro. Neste ponto, eventIndex ajustado para 0 (linha 32). A linha 120 obtm a mensagem MIDI do evento MIDI usando o mtodo getMessage da classe MidiEvent. Para ajudar um programa a passar por cada evento nas trilhas, o programa pode chamar o mtodo goNextEvent de MidiData (linhas 128 a 133) para carregar o prximo evento e a mensagem. O mtodo goNextEvent incrementa eventIndex na Track MIDI carregada e encontra a prxima MidiMessage do evento. Alm de ler os eventos, o programa tambm precisa determinar quanto tempo cada evento dura e o espaamento entre eventos. O mtodo getEventDelay (linhas 136 a 145) devolve a durao de um MidiEvent como a diferena de tempo entre dois eventos na seqncia MIDI (linhas 143 e 144). O mtodo getTick de MidiEvent fornece o tempo especfico em que o evento ocorre (tambm chamado de time stamp). As linhas 139 e 140 devolvem a time stamp do primeiro MidiEvent como a durao do evento. A classe MidiData fornece outros mtodos para devolver os comandos, os nmeros das notas e o volume de ShortMessages relacionadas com notas. O mtodo getEventCommand (linhas 158 a 168) determina o nmero do comando que representa a instruo de comando. A linha 160 do mtodo getEventCommand indica se a MidiMessage atualmente carregada uma ShortMessage. Se for, a linha 163 atribui a ShortMessage ao objeto noteMessage e a linha 164 devolve o byte de estado de comando da ShortMessage invocando o mtodo getCommand de ShortMessage. O mtodo getEventCommand devolve 1 se o evento no contiver uma ShortMessage. O mtodo getNote de MidiData (linhas 171 a 177) invoca o mtodo getData1 de ShortMessage (linha 174) para devolver o nmero da nota. O mtodo getVolume (linhas 180 a 183) invoca o mtodo getData2 de ShortMessage para devolver o volume. A classe MidiData tambm fornece uma indicao do

1160

JAVA COMO PROGRAMAR

fim de uma trilha no mtodo isTrackEnd (linhas 148 a 155), que determina se o ndice de evento ultrapassou o nmero de eventos na trilha (linha 151).

22.7.2 Gravao de MIDI


O programa pode gravar MIDI usando um seqenciador. A classe MidiRecord (fig. 22.8) trata das funes de gravao desta demonstrao de MIDI usando um objeto que implementa a interface Sequencer como gravador MIDI. Desde que os dispositivos MIDI estejam configurados corretamente, a interface Sequencer fornece mtodos simples para gravao. A classe MidiRecord tem um construtor (linhas 29 a 32) que recebe como argumento um objeto que implementa a interface Transmitter. O Transmitter envia mensagens MIDI para um dispositivo MIDI que implementa a interface Receiver. Pense nos Transmitters e Receivers como portas de sada e de entrada, respectivamente, para dispositivos MIDI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

// Fig. 22.8: MidiRecord.java // Permite gravao e reproduo // de MIDI sintetizado // Pacotes do ncleo de Java import java.io.*; // Pacotes de extenso de Java import javax.sound.midi.*; public class MidiRecord { // trilha MIDI private Track track; // seqenciador MIDI para reproduzir e acessar msica private Sequencer sequencer; // seqncia MIDI private Sequence sequence; // receptor de eventos MIDI private Receiver receiver; // transmissor para transmitir mensagens MIDI private Transmitter transmitter; // construtor para MidiRecord public MidiRecord( Transmitter transmit ) { transmitter = transmit; } // inicializa seqenciador de gravao, configura seqncia de gravao public boolean initialize() { // cria seqncia MIDI vazia e configura fiao do seqenciador try { // cria seqncia baseada em tempo de 10 pulsos por compasso sequence = new Sequence( Sequence.PPQ, 10 ); // obtm seqncia e a abre sequencer = MidiSystem.getSequencer();

Fig. 22.8

MidiRecord permite a um programa gravar uma seqncia MIDI (parte 1 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1161

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

sequencer.open(); // obtm receptor do seqenciador receiver = sequencer.getReceiver(); if ( receiver == null ) { System.err.println( "Receiver unavailable for sequencer" ); return false; } // configura receptor para o transmissor enviar MidiMessages transmitter.setReceiver( receiver ); makeTrack(); } // especificao de diviso de temporizao invlida para nova seqncia catch ( InvalidMidiDataException invalidMidiException ) { invalidMidiException.printStackTrace(); return false; } // seqenciador ou receptor indisponvel catch ( MidiUnavailableException noMidiException ) { noMidiException.printStackTrace(); return false; } // inicializao do gravador MIDI bem-sucedida return true; } // fim do mtodo initialize

// cria nova trilha vazia para seqncia public void makeTrack() { // se existe trilha anterior, primeiro a apaga if ( track != null ) sequence.deleteTrack( track ); // cria trilha na seqncia track = sequence.createTrack(); } // inicia reproduo da seqncia carregada public void play() { sequencer.start(); } // comea gravao para a seqncia public void startRecord() { // carrega seqncia no gravador e comea a gravar try { sequencer.setSequence( sequence ); // configura a trilha como habilitada para gravao e canal padro

Fig. 22.8

MidiRecord permite a um programa gravar uma seqncia MIDI (parte 2 de 3).

1162
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

JAVA COMO PROGRAMAR

sequencer.recordEnable( track, 0 ); sequencer.startRecording(); } // seqncia contm dados MIDI invlidos catch ( InvalidMidiDataException badMidiException ) { badMidiException.printStackTrace(); } } // fim do mtodo startRecord

// pra gravao MIDI public void stopRecord() { sequencer.stopRecording(); } // salva seqncia MIDI no arquivo public void saveSequence( File file ) { // obtm todos os tipos de arquivos suportados por MIDI int[] fileTypes = MidiSystem.getMidiFileTypes( sequence ); if ( fileTypes.length == 0 ) { System.err.println( "No supported MIDI file format!" ); return; } // escreve seqncia gravada no arquivo MIDI try { MidiSystem.write( sequence, fileTypes[ 0 ], file ); } // erro durante gravao no arquivo catch ( IOException ioException ) { ioException.printStackTrace(); } } } // fim do mtodo saveSequence

// fim da classe MidiRecord

Fig. 22.8

MidiRecord permite a um programa gravar uma seqncia MIDI (parte 3 de 3).

A primeira etapa da gravao de dados MIDI semelhante do mecanismo de reproduo na classe MidiData. Alm de obter uma seqncia vazia e um seqenciador, o programa de gravao de MIDI precisa conectar os transmissores e receptores. Depois de ligar os fios do receptor do seqenciador com a porta de entrada, o gravador carrega a seqncia vazia no seqenciador para comear a gravar uma nova trilha na seqncia. A discusso a seguir abrange estas etapas. O mtodo initialize (linhas 35 a 77) da classe MidiRecord configura o seqenciador para gravao. A linha 41 do mtodo initialize instancia uma seqncia vazia. MidiRecord ir gravar dados na seqncia vazia assim que o transmissor seja conectado ao receptor. A linha 48 obtm o receiver do seqenciador de gravao e a linha 57 especifica que transmitter vai enviar suas mensagens para receiver. As mensagens MIDI precisam ser colocadas em uma trilha, de modo que o mtodo initialize invoca o mtodo makeTrack (linhas 80 a 88) para apagar a track anterior existente (linha 84) e para criar uma Track vazia (linha 87). O mtodo makeTrack tambm pode ser chamado de uma classe externa para gravar uma nova seqncia sem instanciar novos seqenciadores e uma nova seqncia.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1163

Depois de configurar um seqenciador e uma seqncia vazia, chamar o mtodo startRecord de MidiRecord (linhas 97 a 115) inicia o processo de gravao. A linha 101 carrega uma seqncia vazia no seqenciador. O mtodo recordEnable de Sequencer chamado e lhe so passados o objeto track e um nmero de canal como argumentos (linha 104), o que habilita a gravao naquela track. A linha 106 invoca o mtodo startRecording do Sequencer para iniciar a gravao de eventos MIDI enviados do transmissor. O mtodo stopRecording do Sequencer faz parar a gravao e chamado no mtodo stopRecord de MidiRecord (linhas 118 a 121). A classe MidiRecord tambm pode suportar o salvamento de uma seqncia gravada em um arquivo MIDI com seu mtodo saveSequence (linhas 124 a 144). Embora a maioria das seqncias MIDI possam suportar arquivos MIDI tipo 0 (o tipo mais comum de arquivo MIDI), deve-se verificar a seqncia para saber que outros tipos de arquivos so suportados. A linha 127 obtm um array de tipos de arquivos MIDI suportados pelo sistema para gravar uma seqncia em um arquivo. Os tipos de arquivos MIDI so representados por valores inteiros 0, 1 ou 2. Usando o primeiro tipo de arquivo suportado, o MidiSystem grava a seqncia em um File especificado (linha 136) passado para o mtodo saveSequence como argumento. O mtodo play de MidiRecord (linhas 91 a 94) habilita o programa a reproduzir a seqncia recm-gravada.

22.7.3 Sntese de MIDI


Este programa MidiDemo fornece um piano interativo que gera notas de acordo com as teclas pressionadas pelo usurio. A classe MidiSynthesizer (Fig. 22.9) gera estas notas diretamente e as envia para outro dispositivo. Especificamente, ela envia as notas para um receiver de um seqenciador atravs de um transmitter para gravar a seqncia MIDI. A classe MidiSynthesizer usa um objeto que implementa a interface Synthesizer (uma subinterface de MidiDevice) para acessar a gerao de som, instrumentos, recursos de canal e bancos de som padro do sintetizador. O SoundBank o continer para diversos Instruments, que instrui o computador sobre como fazer o som para uma nota especfica. Notas diferentes feitas por vrios instrumentos so tocadas atravs de um MidiChannel em trilhas diferentes simultaneamente para produzir melodias sinfnicas.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

// Fig. 22.9: MidiSynthesizer.java // Acessando recursos do sintetizador // Pacotes de extenso de Java import javax.sound.midi.*; public class MidiSynthesizer { // sintetizador principal acessa recursos private Synthesizer synthesizer; // instrumentos disponveis para uso na sntese private Instrument instruments[]; // canais atravs dos quais as notas soam private MidiChannel channels[]; private MidiChannel channel; // canal atual // transmissor para transmitir mensagens private Transmitter transmitter; // lado receptor de mensagens private Receiver receiver; // mensagem curta contendo comandos de som, nota, volume private ShortMessage message;

Fig. 22.9

MidiSynthesizer pode gerar notas e envi-las para um outro dispositivo MIDI (parte 1 de 3).

1164
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

JAVA COMO PROGRAMAR

// construtor para MidiSynthesizer public MidiSynthesizer() { // abre sintetizador, configura receptor, // obtm canais e instrumentos try { synthesizer = MidiSystem.getSynthesizer(); if ( synthesizer != null ) { synthesizer.open(); // obtm transmissor do sintetizador transmitter = synthesizer.getTransmitter(); if ( transmitter == null ) System.err.println( "Transmitter unavailable" ); // obtm receptor do sintetizador receiver = synthesizer.getReceiver(); if ( receiver == null ) System.out.println( "Receiver unavailable" ); // obtm todos os instrumentos disponveis no // banco de sons ou sintetizador padro instruments = synthesizer.getAvailableInstruments(); // obtm todos os 16 canais do sintetizador channels = synthesizer.getChannels(); // atribui primeiro canal como canal padro channel = channels[ 0 ]; } else System.err.println( "No Synthesizer" ); } // sintetizador, receptor ou transmissor no-disponvel catch ( MidiUnavailableException noMidiException ) { noMidiException.printStackTrace(); } } // fim do construtor

// devolve instrumentos disponveis public Instrument[] getInstruments() { return instruments; } // devolve transmissor do sintetizador public Transmitter getTransmitter() { return transmitter; } // ativa nota de som atravs do canal

Fig. 22.9

MidiSynthesizer pode gerar notas e envi-las para um outro dispositivo MIDI (parte 2 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1165

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

public void midiNoteOn( int note, int volume ) { channel.noteOn( note, volume ); } // desliga nota de som atravs do canal public void midiNoteOff( int note ) { channel.noteOff( note ); } // muda para o instrumento selecionado public void changeInstrument( int index ) { Patch patch = instruments[ index ].getPatch(); channel.programChange( patch.getBank(), patch.getProgram() ); } // envia mensagens MIDI de praxe atravs do transmissor public void sendMessage( int command, int note, int volume ) { // envia uma ShortMessage MIDI usando os parmetros deste mtodo try { message = new ShortMessage(); // configura nova mensagem de comando (NOTE_ON, NOTE_OFF), // nmero da nota, volume message.setMessage( command, note, volume ); // envia mensagem atravs do receptor receiver.send( message, 1 ); } // valores de mensagem invlidos configurados catch ( InvalidMidiDataException badMidiException ) { badMidiException.printStackTrace(); } } } // fim do mtodo sendMessage

// fim da classe MidiSynthesizer

Fig. 22.9

MidiSynthesizer pode gerar notas e envi-las para um outro dispositivo MIDI (parte 3 de 3).

O construtor de MidiSynthesizer (linhas 29 a 72) adquire o sintetizador e inicializa recursos relacionados. A linha 34 obtm um objeto Synthesizer do MidiSystem e a linha 38 abre o Synthesizer. Para permitir que sons sejam reproduzidos e gravados ao mesmo tempo, as linhas 41 a 47 obtm o Transmitter e o Receiver do Synthesizer. Quando uma mensagem MIDI enviada para o receiver do synthesizer, o synthesizer executa a instruo da mensagem, gerando notas, e o transmitter envia aquela mensagem para Receivers designados de outros dispositivos MIDI.
Erro comum de programao 22.3 Ocorre uma MidiUnavailableException quando o programa tenta adquirir recursos de MidiDevice indisponveis, como sintetizadores e transmissores.

As mensagens MIDI so enviadas de MidiDemo para o MidiSynthesizer aps se pressionar uma tecla do piano ou um MidiEvent na trilha previamente carregada de MidiData. Pode-se gerar uma nota acessando os

1166

JAVA COMO PROGRAMAR

channels do synthesizer diretamente. Para simplificar, MidiSynthesizer usa somente o primeiro canal (de 16 possveis) para emitir notas. A linha 57 invoca o mtodo getChannels de Synthesizer para obter todos os 16 canais de synthesizer e a linha 60 configura o canal default para o primeiro canal. O MidiChannel emite uma nota chamando seu mtodo noteOn com o nmero da nota (0 a 127) e um nmero de volume como argumentos. O mtodo noteOff de MidiChannel desliga uma nota apenas com o nmero da nota como argumento. O MidiSynthesizer acessa estes mtodos de MidiChannel atravs dos mtodos midiNoteOn (linhas 87 a 90) e midiNoteOff (linhas 93 a 96), respectivamente. O sintetizador pode usar seus instrumentos default para emitir notas. A linha 54 obtm o instrumento default disponvel atravs do sintetizador ou atravs de um banco de sons default invocando o mtodo getAvailableInstruments de Synthesizer. Banco de sons normalmente tem 128 instrumentos. Os instrumentos em uso podem ser trocados com o mtodo changeInstrument de MidiSynthesizer (linhas 99 a 105). As linhas 103 e 104 invocam o mtodo programChange do MidiChannel para carregar o programa do instrumento desejado, com o nmero do banco e do programa obtidos de patch (linha 104) como parmetros. O Patch o endereo de um instrumento carregado.
Dica de desempenho 22.5 Um programa pode importar mais instrumentos carregando um banco de sons personalizado atravs do mtodo loadAllInstruments de Synthesizer com um objeto SoundBank.

Enviando MidiMessages para o Receiver de um Synthesizer, o programa pode invocar o sintetizador para emitir notas sem usar seus canais. Enviar MidiMessages para um Receiver de um MidiDevice tambm permite aos Transmitters do dispositivo enviar estas mensagens para o Receiver de um outro MidiDevice. No mtodo sendMessage de MidiSynthesizer (linhas 108 a 127), as linhas 112 a 116 criam uma nova ShortMessage a partir dos parmetros do mtodo sendMessage e enviam a mensagem para o receptor do sintetizador (linha 119). A linha 116 do mtodo sendMessage invoca o mtodo setMessage de ShortMessage para configurar o contedo das instrues da mensagem usando trs argumentos int: um comando, a nota a ser tocada e o volume da nota. O mtodo setMessage dispara uma InvalidMidiDataException se os valores de comando e os parmetros designados forem invlidos. Ao se criar uma nova ShortMessage com o mtodo setMessage, o significado do segundo e terceiro argumentos variam dependendo do comando. O comando ShortMessage.NOTE_ON designa o segundo parmetro como o nmero da nota e o terceiro argumento como a velocidade (i.e., volume) da nota. O comando ShortMessage.PROGRAM_CHANGE designa o segundo argumento como o programa do instrumento a usar e ignora o terceiro argumento. A linha 119 envia a ShortMessage criada para o receiver do synthesizer chamando o mtodo send de Receiver com a MidiMessage e um time stamp como argumentos. O MidiSynthesizer no se envolve com a complexidade da temporizao na sntese de MIDI. O receptor envia um valor 1 para o parmetro time stamp para indicar que o time stamp deve ser ignorado. O gravador de seqncia na classe MidiRecord toma conta dos aspectos de temporizao quando ele recebe as mensagens. At este ponto, discutimos as ferramentas necessrias para criar nosso piano MIDI. Resumidamente, a classe MidiDemo (Fig. 22.10) utiliza a classe MidiSynthesizer para gerar sons e acessar canais e instrumentos. MidiDemo utiliza MidiData para reproduzir arquivos MIDI e acessar as informaes de trilhas de MIDI. MidiRecord fornece a funo de gravao para MidiDemo, que recebe mensagens de MidiSynthesizer.

22.7.4 A classe MidiDemo


Apresentamos agora a classe MidiDemo (Fig. 22.10), que fornece a GUI para nosso piano, e outros componentes GUI para controlar os recursos deste exemplo. Usando um lao for, o mtodo utilitrio makeKeys (linhas 86 a 155) na classe MidiDemo cria 64 botes que representam 64 teclas diferentes de piano. Sempre que o mouse passa sobre uma tecla, o programa emite a nota designada. O mtodo makeKeys organiza as teclas no fundo da frame usando o mtodo setBounds de cada boto (linha 106) para indicar a posio e o tamanho dos botes. O programa organiza os botes horizontalmente de acordo com seu ndice no array noteButton.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1167

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

// // // //

Fig. 22.10: MidiDemo.java Simula um teclado musical para tocar diversos instrumentos, permitindo tambm gravao, reproduo de arquivos MIDI e simulao de reproduo MIDI com o teclado

// Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; // Pacotes de extenso de Java import javax.swing.*; import javax.swing.event.*; import javax.sound.midi.*; public class MidiDemo extends JFrame { // gravando dados MIDI private MidiRecord midiRecord; // funes de sintetizao de MIDI private MidiSynthesizer midiSynthesizer; // dados MIDI em arquivo MIDI private MidiData midiData; // timer para simular MIDI no piano private Timer pianoTimer; // teclas do piano private JButton noteButton[]; // controles deslizantes de tempo e volume private JSlider volumeSlider, resolutionSlider; // contineres e painel contendo GUI private Container container; private JPanel controlPanel, buttonPanel; // seletor de instrumento e GUI de botes private JComboBox instrumentBox; private JButton playButton, recordButton, saveButton, pianoPlayerButton, listenButton; // tempo, ltima tecla do piano invocada, volume de MIDI private int resolution, lastKeyOn = 1, midiVolume = 40; // valor booleano que indica se o programa est no modo de gravao private boolean recording = false; // nmero da primeira nota da primeira tecla do piano, nmero mximo de teclas private static int FIRST_NOTE = 32, MAX_KEYS = 64; // construtor para MidiDemo public MidiDemo() { super( "MIDI Demo" );

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 1 de 13).

1168
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

JAVA COMO PROGRAMAR

container = getContentPane(); container.setLayout( new BorderLayout() ); // sintetizador precisa ser instanciado para habilitar sntese midiSynthesizer = new MidiSynthesizer(); // cria as teclas do piano makeKeys(); // adiciona painel de controle frame controlPanel = new JPanel( new BorderLayout() ); container.add( controlPanel, BorderLayout.NORTH ); makeConfigureControls(); // adiciona painel de botes frame buttonPanel = new JPanel( new GridLayout( 5, 1 ) ); controlPanel.add( buttonPanel, BorderLayout.EAST ); // cria a GUI makePlaySaveButtons(); makeRecordButton(); makePianoPlayerButton(); } // fim do construtor

// mtodo utilitrio criando teclas do piano private void makeKeys() { // painel contendo as teclas JPanel keyPanel = new JPanel( null ); container.add( keyPanel, BorderLayout.CENTER ); // teclas do piano noteButton = new JButton[ MAX_KEYS ]; // adiciona botes MAX_KEYS e as notas que eles emitem for ( int i = 0; i < MAX_KEYS; i++ ) { final int note = i; noteButton[ i ] = new JButton(); // configurando as teclas brancas noteButton[ i ].setBackground( Color.white ); // configurando o espaamento correto para os botes noteButton[ i ].setBounds( ( i * 11 ), 1, 11, 40 ); keyPanel.add( noteButton[ i ] ); // registra um ouvinte de mouse para eventos do mouse noteButton[ i ].addMouseListener( // classe interna annima para tratar eventos do mouse new MouseAdapter() { // invoca nota da tecla quando o mouse toca na tecla public void mouseEntered( MouseEvent mouseEvent ) {

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 2 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1169

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

// se estiver gravando, envia mensagem para o receptor if ( recording ) midiSynthesizer.sendMessage( ShortMessage.NOTE_ON, note + FIRST_NOTE, midiVolume ); // seno, s emite a nota else midiSynthesizer.midiNoteOn( note + FIRST_NOTE, midiVolume ); // muda a cor da tecla para azul noteButton[ note ].setBackground( Color.blue ); } // desliga a nota da tecla quando o mouse sai da tecla public void mouseExited( MouseEvent mouseEvent ) { if ( recording ) midiSynthesizer.sendMessage( ShortMessage.NOTE_OFF, note + FIRST_NOTE, midiVolume ); else midiSynthesizer.midiNoteOff( note + FIRST_NOTE ); noteButton[ note ].setBackground( Color.white ); } } // fim de MouseAdapter

); // fim da chamada para addMouseListener } } // fim do lao for

// fim do mtodo makeKeys

// ajusta controles de configurao private void makeConfigureControls() { JPanel configurePanel = new JPanel( new GridLayout( 5, 1 ) ); controlPanel.add( configurePanel, BorderLayout.WEST ); instrumentBox = new JComboBox( midiSynthesizer.getInstruments() ); configurePanel.add( instrumentBox ); // registra um ActionListener para eventos de instrumentBox instrumentBox.addActionListener( // classe interna annima para tratar seletor de instrumentos new ActionListener() { // troca o programa de instrumento atual

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 3 de 13).

1170
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

JAVA COMO PROGRAMAR

public void actionPerformed( ActionEvent event ) { // troca instrumento no sintetizador midiSynthesizer.changeInstrument( instrumentBox.getSelectedIndex() ); } } // fim de ActionListener

); // fim da chamada para o mtodo addActionListener JLabel volumeLabel = new JLabel( "volume" ); configurePanel.add( volumeLabel ); volumeSlider = new JSlider( SwingConstants.HORIZONTAL, 5, 80, 30 ); // registra um ChangeListener para eventos de controles deslizantes volumeSlider.addChangeListener( // classe interna annima para tratar de eventos do controle de volume new ChangeListener() { // muda volume public void stateChanged( ChangeEvent changeEvent ) { midiVolume = volumeSlider.getValue(); } } // fim da classe ChangeListener

); // fim da chamada para o mtodo addChangeListener configurePanel.add( volumeSlider ); JLabel tempLabel = new JLabel( "tempo" ); configurePanel.add( tempLabel ); resolutionSlider = new JSlider( SwingConstants.HORIZONTAL, 1, 10, 1 ); // registra um ChangeListener para eventos do controle de tempo resolutionSlider.addChangeListener( // classe interna annima para tratar eventos do controle de tempo new ChangeListener() { // muda resoluo se o valor mudou public void stateChanged( ChangeEvent changeEvent ) { resolution = resolutionSlider.getValue(); } } // fim de ChangeListener

); // fim da chamada para o mtodo addChangeListener resolutionSlider.setEnabled( false );

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 4 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1171

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

configurePanel.add( resolutionSlider ); } // fim do mtodo makeConfigureControls

// configura botes de tocar e salvar private void makePlaySaveButtons() { playButton = new JButton( "Playback" ); // registra um ActionListener para eventos do playButton playButton.addActionListener( // classe interna annima para tratar de eventos do playButton new ActionListener() { // reproduz o ltimo MIDI gravado public void actionPerformed( ActionEvent event ) { if ( midiRecord != null ) midiRecord.play(); } } // fim de ActionListener

); // fim da chamada para o mtodo addActionListener buttonPanel.add( playButton ); playButton.setEnabled( false ); listenButton = new JButton( "Play MIDI" ); // registra um ActionListener para eventos do listenButton listenButton.addActionListener( // classe interna annima para tratar de eventos do listenButton new ActionListener() { // reproduz arquivo MIDI public void actionPerformed( ActionEvent event ) { File midiFile = getFile(); if ( midiFile == null ) return; midiData = new MidiData(); // prepara trilha MIDI if ( midiData.initialize( midiFile ) == false ) return; // reproduz dados MIDI midiData.play(); } } // fim de ActionListener

); // fim da chamada para o mtodo addActionListener

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 5 de 13).

1172
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

JAVA COMO PROGRAMAR

buttonPanel.add( listenButton ); saveButton = new JButton( "Save MIDI" ); // registra um ActionListener para eventos do saveButton saveButton.addActionListener( // classe interna annima para tratar de eventos do saveButton new ActionListener() { // obtm arquivo de salvamento e salva MIDI gravado public void actionPerformed( ActionEvent event ) { File saveFile = getSaveFile(); if ( saveFile != null ) midiRecord.saveSequence( saveFile ); } } // fim de ActionListener

); // fim da chamada para o mtodo addActionListener buttonPanel.add( saveButton ); saveButton.setEnabled( false ); } // fim do mtodo makePlaySaveButtons

// cria boto de gravao private void makeRecordButton() { recordButton = new JButton( "Record" ); // registra um ActionListener para eventos do recordButton recordButton.addActionListener( // classe interna annima para tratar de eventos do recordButton new ActionListener() { // iniciar ou parar a gravao public void actionPerformed( ActionEvent event ) { // grava MIDI quando o boto "record" if ( recordButton.getText().equals("Record") ) { if ( midiRecord == null ) { // cria nova instncia do gravador passando // a ela o transmissor do sintetizador midiRecord = new MidiRecord( midiSynthesizer.getTransmitter() ); if ( midiRecord.initialize() == false ) return; } else midiRecord.makeTrack();

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 6 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1173

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

midiRecord.startRecord(); // inibe reproduo durante a gravao playButton.setEnabled( false ); // muda boto de gravao para "stop" recordButton.setText( "Stop" ); recording = true; } // fim do if

// parar a gravao quando o boto "stop" else { midiRecord.stopRecord(); recordButton.setText( "Record" ); recording = false; playButton.setEnabled( true ); saveButton.setEnabled( true ); } } } // fim do mtodo actionPerformed

// fim de ActionListener

); // fim da chamada para o mtodo addActionListener buttonPanel.add( recordButton ); } // fim do mtodo makeRecordButton

// cria boto e funcionalidade do Tocador de Piano private void makePianoPlayerButton() { pianoPlayerButton = new JButton( "Piano Player" ); // registra um ActionListener para eventos do pianoPlayerButton pianoPlayerButton.addActionListener( // classe interna annima para tratar do pianoPlayerButton new ActionListener() { // inicializa dados MIDI e timer do tocador de piano public void actionPerformed( ActionEvent event ) { File midiFile = getFile(); if ( midiFile == null ) return; midiData = new MidiData(); // prepara trilha MIDI if ( midiData.initialize( midiFile ) == false ) return; if ( midiData.initializeTrack() == false ) return;

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 7 de 13).

1174
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470

JAVA COMO PROGRAMAR

// ajusta resoluo inicial de MIDI resolution = midiData.getResolution(); // nova instncia de timer para tratar sons do // piano e pressionamento de teclas com tempo pianoTimer = new Timer( midiData.getEventDelay() * resolution, new TimerHandler() ); listenButton.setEnabled( false ); pianoPlayerButton.setEnabled( false ); resolutionSlider.setEnabled( true ); pianoTimer.start(); } } // fim do mtodo actionPerformed

// fim de ActionListener

); // fim da chamada para o mtodo addActionListener buttonPanel.add( pianoPlayerButton ); } // fim do mtodo makePianoPlayerButton

// classe interna trata de eventos MIDI temporizados private class TimerHandler implements ActionListener { // simula a nota da tecla do evento se presente, pula para o // prximo evento na trilha e ajusta novo intervalo de retardo do // mtodo do timer quando o timer atinge a hora do prximo evento public void actionPerformed( ActionEvent actionEvent ) { // se uma ltima tecla vlida estiver ligada, muda-a para branco if ( lastKeyOn != 1 ) noteButton[ lastKeyOn ].setBackground( Color.white ); noteAction(); midiData.goNextEvent(); // faz parar o tocador de piano quando acha o fim da trilha MIDI if ( midiData.isTrackEnd() == true ) { if ( lastKeyOn != 1 ) noteButton[ lastKeyOn ].setBackground( Color.white ); pianoTimer.stop(); listenButton.setEnabled( true ); pianoPlayerButton.setEnabled( true ); resolutionSlider.setEnabled( false ); return; } // fim de if isTrackEnd

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 8 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1175

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

// ajusta intervalo antes do prximo evento de som pianoTimer.setDelay( midiData.getEventDelay() * resolution ); } } // fim do mtodo actionPerformed

// fim da classe interna TimerHandler

// determina que nota emitir, de // acordo com as mensagens MIDI private void noteAction() { // durante a mensagem Note On, emite a nota e pressiona tecla if ( midiData.getEventCommand() == ShortMessage.NOTE_ON ) { // assegura que nota vlida est no intervalo das teclas if ( ( midiData.getNote() >= FIRST_NOTE ) && ( midiData.getNote() < FIRST_NOTE + MAX_KEYS ) ) { lastKeyOn = midiData.getNote() FIRST_NOTE; // ajusta a cor da tecla para vermelho noteButton[ lastKeyOn ].setBackground( Color.red ); // envia e faz soar a nota atravs do sintetizador midiSynthesizer.sendMessage( 144, midiData.getNote(), midiData.getVolume() ); } // fim do if

// seno, no h ltima tecla pressionada else lastKeyOn = 1; } // fim do if

// receber a mensagem Note Off faz parar de emitir // a nota e mudar a cor da tecla de volta para branco else // se o comando da mensagem for desligar a nota if ( midiData.getEventCommand() == ShortMessage.NOTE_OFF ) { if ( ( midiData.getNote() >= FIRST_NOTE ) && ( midiData.getNote() < FIRST_NOTE + MAX_KEYS ) ) { // ajusta a tecla apropriada para branco noteButton[ midiData.getNote() FIRST_NOTE ].setBackground( Color.white ); // envia mensagem de desligar a nota para o receptor midiSynthesizer.sendMessage( 128, midiData.getNote(), midiData.getVolume() ); } } // fim do if

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 9 de 13).

1176
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

JAVA COMO PROGRAMAR

// fim do mtodo noteAction

// obtm arquivo de salvamento do computador public File getSaveFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showSaveDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // obtm arquivo do computador public File getFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // executa o aplicativo public static void main( String args[] ) { MidiDemo midiTest = new MidiDemo(); midiTest.setSize( 711, 225 ); midiTest.setDefaultCloseOperation ( EXIT_ON_CLOSE ); midiTest.setVisible( true ); } } // fim da classe MidiDemo

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 10 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1177

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 11 de 13).

1178

JAVA COMO PROGRAMAR

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 12 de 13).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1179

Fig. 22.10

MidiDemo fornece a GUI que permite que os usurios interajam com o aplicativo (parte 13 de 13).

Observao de aparncia e comportamento 22.3 Para dispor componentes GUI em posies especficas sem a ajuda de gerenciadores de leiaute, configure o leiaute do painel que contm os componentes como null. Por default, o JPanel configura o LayoutManager FlowLayout quando o painel instanciado sem argumentos.

As linhas 110 a 151 registram MouseListeners para cada boto de tecla do piano. O programa chama o mtodo mouseEntered (linhas 116 a 132) quando o mouse passa sobre aquele boto. Se o programa no estiver no modo de gravao, o mtodo mouseEntered acessa diretamente os canais na classe MidiSynthesizer para emitir a nota (linhas 125 a 127). Caso contrrio, o mtodo mouseEntered invoca o mtodo sendMessage do MidiSynthesizer para enviar uma mensagem de nota para o sintetizador e para o dispositivo de gravao (linhas 119 a 122). As linhas 130 e 131 ajustam a cor de fundo do boto para azul para indicar que a nota est sendo tocada. Quando o mouse no est mais passando sobre o boto, o programa chama o mtodo mouseExited (linhas 135 a 147) para desligar a nota e mudar o fundo do boto para sua cor original. Das 128 notas possveis, somente as 64 notas do meio so acessveis atravs do piano no exemplo. O intervalo de notas pode ser mudado modificando-se as constantes na linha 52. A constante FIRST_NOTE o valor da primeira tecla e a soma de FIRST_NOTE e MAX_KEYS o valor da ltima tecla. A constante MAX_KEYS especifica o nmero de teclas do piano a criar. A classe MidiDemo invoca o mtodo makeConfigureControls (linhas 158 a 237) para configurar os controles de MIDI do programa, que consistem em uma JComboBox seletora de instrumento, um JSlider para mudar o volume de sntese pelo usurio e um JSlider para mudar o tempo do tocador de piano. Quando os usurios selecionam um instrumento, o programa chama o mtodo actionPerformed de instrumentBox (linhas 177 a 182) para mudar o programa do instrumento selecionado invocando o mtodo changeInstrument de MidiSynthesizer com o ndice do instrumento selecionado como parmetro. Quando os usurios arrastam o controle deslizante de volume, o programa chama o mtodo stateChanged de volumeSlider (linhas 201 a 204) para mudar o volume. Observe que mudar o volume afeta somente o volume das notas MIDI sintetizadas pelo usurio. Quando os usurios arrastam o controle deslizante de tempo, o programa chama o mtodo stateChanged de resolutionSlider (linhas 225 a 228) para ajustar o tempo.

1180

JAVA COMO PROGRAMAR

Invocar o mtodo makePlaySaveButtons (linhas 240 a 320) configura os botes Play MIDI, Playback e Save. Clicar em Play MIDI invoca o mtodo actionPerformed do listenButton (linhas 273 a 288) para reproduzir em sua totalidade um arquivo MIDI aberto com a classe MidiData. A linha 275 obtm um arquivo de um dilogo de escolha de arquivo com o mtodo getFile de MidiDemo (linhas 549 a 562). As linhas 280 a 284 inicializam e reproduzem o arquivo MIDI com o objeto midiData instanciado. Quando o usurio clica em Playback, a linha 254 reproduz o MIDI gravado. Este boto fica habilitado somente se alguma gravao foi feita. Clicar no boto Save permite salvar a seqncia gravada em um arquivo (linhas 307 a 310). O mtodo makeRecordButton (linhas 323 a 383) cria o boto Record e um ouvinte para ele. Clicar no boto quando ele est ajustado para modo de gravao (linha 337) cria um novo gravador com a classe MidiRecord (linhas 339 a 348). Se j foi criado um gravador, a linha 351 invoca o mtodo makeTrack de MidiRecord para criar uma nova trilha para o objeto midiRecord. Quando a gravao comea (linha 353), as linhas 356 a 360 mudam o boto de gravao para um boto de parar e inibem o playButton temporariamente. Quando os usurios param a gravao clicando no boto de gravao novamente, a GUI retorna para seu estado anterior gravao e o usurio pode reproduzir e salvar a seqncia MIDI (linhas 365 a 373). Tocador de piano A funo tocador de piano deste exemplo sincroniza a reproduo de uma nota com o destaque da tecla correspondente. O programa primeiro obtm uma trilha MIDI de um arquivo MIDI especificado pelo usurio com a classe MidiData. O Timer sincroniza os eventos MIDI na seqncia MIDI. Quando o Timer atinge o time stamp de um evento MIDI, ele restaura seu retardo para o time stamp do prximo evento a ser sincronizado. O retardo de um timer o perodo de tempo que ele espera antes de provocar um evento. O acionador do tocador de piano responsvel pela sincronizao est localizado na classe principal MidiDemo. O mtodo makePianoPlayerButton (linhas 386 a 436) carrega o arquivo MIDI e inicializa o Timer que controla a temporizao das notas musicais. Assim como ocorre com o listenButton, o mtodo actionPerformed (linhas 397 a 428) de makePianoPlayerButton usa a classe MidiData para carregar dados MIDI. As linhas 399 a 408 abrem um arquivo a partir de uma caixa de dilogo de arquivo e carregam os dados MIDI do arquivo. A linha 410 invoca o mtodo initializeTrack de MidiData para obter a trilha mais longa do MIDI carregado e a primeira mensagem de evento MIDI da trilha. A linha 414 invoca o mtodo getResolution de MidiData (linhas 67 a 70 na Fig. 22.7) para obter o tempo default do tocador de piano. As linhas 418 a 420 instanciam um novo objeto Timer que aciona a reproduo das notas no momento de cada evento MIDI. A linha 419 ajusta o retardo do timer para ser a durao do primeiro MidiEvent na trilha, invocando o mtodo getEventDelay de MidiData. Para permitir mudanas de tempo especificadas pelo usurio para o tocador de piano, a linha 424 habilita o controle deslizante de tempo e a linha 419 ajusta o retardo do timer para ser multiplicado pelo valor de resolution. A varivel resolution, que especifica o tempo do tocador de piano, multiplicada pelo intervalo de tempo do evento para se obter um novo intervalo de tempo usado por pianoTimer. O retardo de pianoTimer ajustado somente para a durao do primeiro evento a disparar o timer. Mais tarde, o mtodo actionPerformed (linhas 444 a 475) da classe interna TimerHandler (linhas 439 a 477) restaura o retardo do timer para a durao do prximo MidiEvent (linhas 472 e 473). A linha 426 dispara o pianoTimer. No momento do prximo evento MIDI, o mtodo actionPerformed da classe TimerHandler sintetiza uma nota da trilha e pressiona uma tecla no piano. Usando pianoTimer e seu tratador de eventos, o programa percorre a trilha, simulando eventos de notas quando ele encontra MidiEvents apropriados. A classe interna TimerHandler aciona a sincronizao iterando atravs de todos os MidiEvents em uma dada trilha e chamando o mtodo actionPerformed para tocar o piano. A linha 451 do mtodo actionPerformed invoca o mtodo utilitrio noteAction (linhas 481 a 530) para emitir o som da nota e mudar a cor da tecla do piano especfica, desde que o evento contenha uma mensagem de nota e que a nota esteja dentro do intervalo designado para as teclas do piano neste exemplo. As linhas 484 e 485 do mtodo noteAction determinam se o MidiEvent atual contm um comando de nota invocando o mtodo getEventCommand da classe MidiData. As linhas 488 e 489 invocam o mtodo getNote de MidiData para determinar se o nmero da nota especificado na noteMessage de uma nota vlida e dentro do intervalo de teclas possveis do piano, especificado nas constantes FIRST_NOTE e MAX_KEYS (linha 52). Se o comando for ShortMessage.NOTE_ON e estiver dentro do intervalo das teclas do piano, o sintetizador recebe a mensagem de nota especificada (linhas 497 e 498) e a cor da tecla do piano correspondente fica verme-

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1181

lha pela durao do evento (linha 494). A linha 491 obtm o nmero de boto do piano que deve ficar vermelho de forma correspondente (lastKeyOn). Se o comando for ShortMessage.NOTE_OFF e estiver dentro do intervalo permitido para as teclas do piano (linhas 513 a 517), as linhas 520 e 521 mudam o fundo da tecla do piano especfica de volta para branco. As linhas 524 e 525 enviam a ShortMessage para o sintetizador para que a nota pare de soar. Como nem todas as ShortMessages NOTE_ON so seguidas por uma ShortMessage NOTE_OFF, como se poderia esperar, o programa precisa mudar a tecla da ltima NOTE_ON de volta para sua cor original no momento do prximo evento. Para este fim, o mtodo noteAction atribui a lastKeyOn o valor do ltimo boto do piano invocado. O objeto lastKeyOn inicializado com 1 permanece 1 se a nota do comando NOTE_ON estiver fora do intervalo. Isto limita o acesso somente teclas no intervalo de nosso teclado simulado. Quando pianoTimer atinge o prximo evento, o programa muda o fundo da ltima tecla do piano pressionada de volta para branco (linhas 447 a 449). Quando o programa termina de executar o mtodo noteAction, a linha 452 invoca o mtodo goNextEvent de MidiData para passar para o prximo evento na trilha. Toda vez que o mtodo actionPerformed do tratador termina de carregar o prximo evento, a linha 455 determina se o prximo evento o ltimo evento na trilha invocando o mtodo isTrackEnd de MidiData, assumindo-se que o ltimo evento o MetaEvent fim de trilha. Se o prximo evento for o ltimo evento, as linhas 457 a 459 mudam a cor de fundo da ltima tecla pressionada para branco e a linha 461 faz parar o pianoTimer. As linhas 463 a 465 reabilitam os botes que foram desabilitados durante a funo tocador de piano.

22.8 Recursos na Internet e na World Wide Web


Esta seo apresenta diversos recursos na Internet e na Web para a Java Media Framework e outros sites relacionados com multimdia.
java.sun.com/products/java-media/jmf/

A homepage da Java Media Framework no site de Java na Web. Aqui voc pode baixar a ltima implementao pela Sun do JMF. O site tambm contm a documentao para o JMF.
www.nasa.gov/gallery/index.html

A NASA multimedia gallery contm uma ampla variedade de imagens, clipes de udio e videoclipes que voc pode baixar e usar para testar seus programas de multimdia em Java.
sunsite.sut.ac.jp/multimed/

A Sunsite Japan Multimedia Collection tambm fornece uma ampla variedade de imagens, clipes de udio e videoclipes que voc pode baixar para fins educacionais.
www.anbg.gov.au/anbg/index.html

O site na Web do Australian National Botanic Gardens fornece links para sons de muitos animais. Experimente o link Common Birds.
www.midi.com

Midi.com um site de recursos para MIDI com um mecanismo de busca de MIDI, links para outros sites, uma lista de livros relacionados com MIDI e outras informaes sobre MIDI.
www.streamingmedia.com

Streamingmedia.com fornece muitos artigos do setor de mdia de streaming e informaes tcnicas sobre a tecnologia de mdia de streaming.
www.harmony-central.com/MIDI/

A seo de recursos para MIDI da Harmony Central contm muitos documentos teis sobre MIDI, links e fruns que podem ser teis para um programador MIDI.

22.9 (Estudo de caso opcional) Pensando em objetos: animao e som na viso


Este estudo de caso se concentrou principalmente no modelo do sistema do elevador. Agora que completamos nosso projeto do modelo, voltamos nossa ateno para a viso, que fornece a apresentao visual do modelo. Em nosso estudo de caso, a viso chamada de ElevatorView um objeto JPanel que contm outros objetos JPanel filhos, cada um representando um objeto nico no modelo (por exemplo, uma Person, um Button, o

1182

JAVA COMO PROGRAMAR

Elevator). A classe ElevatorView a maior classe no estudo de caso. Nesta seo, discutimos as classes de grficos e som usadas pela classe ElevatorView. Apresentamos e explicamos o resto do cdigo nesta classe no Apndice I. Na Seo 3.7, construmos o diagrama de classes para nosso modelo localizando os substantivos e as frases com substantivos da definio do problema da Seo 2.7. Ignoramos diversos destes substantivos, porque eles no estavam associados com o modelo. Agora, listamos os substantivos e as frases com substantivos que se aplicam exibio do modelo: exibio udio msica de elevador O substantivo exibio corresponde viso, ou a apresentao visual do modelo. Como descrito na Seo 13.17, a classe ElevatorView agrega diversas classes que compreendem a viso. O udio se refere aos efeitos de som que nossa simulao gera quando ocorrem diversas aes criamos a classe SoundEffects para gerar estes efeitos de som. A frase msica de elevador se refere msica tocada enquanto a Person anda no Elevator criamos a classe ElevatorMusic para tocar esta msica. A viso deve exibir todos os objetos no modelo. Criamos a classe ImagePanel para representar objetos imveis no modelo, como o ElevatorShaft. Criamos a classe MovingPanel, que estende ImagePanel, para representar objetos em movimento, como o Elevator. Finalmente, criamos a classe AnimatedPanel, que estende MovingPanel, para representar objetos que se movem cujas imagens correspondentes mudam continuamente, como uma Person (usamos diversas frames de animao para mostrar a Person caminhando e depois pressionando um boto). Usando estas classes, apresentamos o diagrama de classes da viso para nossa simulao na Fig. 22.11. As anotaes indicam o papel que as classes desempenham no sistema. De acordo com o diagrama de classes, a classe ElevatorView representa a viso, as classes ImagePanel, MovingPanel e AnimatedPanel esto relacionadas com os grficos e as classes SoundEffects e ElevatorMusic esto relacionados com o udio. A classe ElevatorView contm diversas instncias das classes ImagePanel, MovingPanel e AnimatedPanel e uma instncia de cada uma das classes SoundEffects e ElevatorMusic. No Apndice I, associamos cada objeto no modelo com uma classe correspondente na viso. Nesta seo, discutimos as classes ImagePanel, MovingPanel e AnimatedPanel para explicar os grficos e a animao. Depois discutimos as classes SoundEffects e ElevatorMusic para explicar a funcionalidade de udio. Image Panel A ElevatorView usa objetos de subclasses de JPanel para representar e exibir cada objeto no modelo (como o Elevator, uma Person, o ElevatorShaft, etc.). A classe ImagePanel (Fig. 22.12) uma subclasse de JPanel capaz de exibir uma imagem em uma posio da tela dada. A ElevatorView usa objetos ImagePanel para representar objetos imveis no modelo, como o ElevatorShaft e os dois Floors. A classe ImagePanel contm um atributo inteiro ID (linha 16) que define um identificador nico usado para monitorar o ImagePanel na viso se necessrio. Este monitoramento til quando diversos objetos da mesma classe existem no modelo, como diversos objetos Person. A classe ImagePanel contm o objeto Point2D.Double position (linha 19) para representar a posio na tela do ImagePanel. Veremos mais tarde que MovingPanel, que estende ImagePanel, define a velocidade com doubles usar o tipo double garante velocidade e posio altamente precisas. Convertemos as coordenadas de position para ints para posicionar o ImagePanel na tela (Java representa as coordenadas na tela como ints) no mtodo setPosition (linhas 90 a 94). A classe ImagePanel tambm contm um objeto ImageIcon chamado imageIcon (linha 22) o mtodo paintComponent (linhas 54 a 60) exibe imageIcon na tela. As linhas 41 e 42 inicializam imageIcon com um parmetro String que contm o nome da imagem. Finalmente, a classe ImagePanel contm o Set panelChildren (linha 25) que armazena quaisquer objetos-filhos da classe ImagePanel (ou objetos de uma subclasse de ImagePanel). Os objetos-filhos so exibidos sobre o ImagePanel de seu pai por exemplo, uma Person andando dentro do Elevator. O primeiro mtodo add (linhas 63 a 67) anexa um objeto a panelChildren. O segundo mtodo add (linhas 70 a 74) insere um objeto em panelChildren em um ndice determinado. O mtodo setIcon (linhas 84 a 87) configura imageIcon com uma nova imagem. Os objetos da classe AnimatedPanel usam

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1183

o mtodo setIcon repetidamente para mudar a imagem exibida, o que provoca a animao para a viso discutimos a animao mais tarde na seo.

ImagePanel

1..* 1

ElevatorMusic

1 MovingPanel 1..* 1 1 ElevatorView

1 udio 1

AnimatedPanel

1..*

SoundEffects

grficos

viso

Fig. 22.11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

Diagrama de classes da viso da simulao do elevador.

// ImagePanel.java // Subclasse de JPanel para posicionar e exibir ImageIcon package com.deitel.jhtp4.elevator.view; // Pacotes do ncleo de Java import java.awt.*; import java.awt.geom.*; import java.util.*; // Pacotes de extenso de Java import javax.swing.*; public class ImagePanel extends JPanel { // identificador private int ID; // posio na tela private Point2D.Double position; // imageIcon para pintar na tela private ImageIcon imageIcon; // armazena todos os filhos de ImagePanel private Set panelChildren; // construtor inicializa posio e imagem public ImagePanel( int identifier, String imageName )

Fig. 22.12

A classe ImagePanel representa e exibe um objeto imvel do modelo (parte 1 de 3).

1184
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

JAVA COMO PROGRAMAR

{ super( null ); // especifica leiaute nulo setOpaque( false ); // torna transparente // configura identificador nico ID = identifier; // configura posio position = new Point2D.Double( 0, 0 ); setLocation( 0, 0 ); // cria ImageIcon com o imageName dado imageIcon = new ImageIcon( getClass().getResource( imageName ) ); Image image = imageIcon.getImage(); setSize( image.getWidth( this ), image.getHeight( this ) ); // cria Set para armazenar filhos do Panel panelChildren = new HashSet(); } // fim do construtor ImagePanel

// pinta Panel na tela public void paintComponent( Graphics g ) { super.paintComponent( g ); // se a imagem est pronta, pinta-a na tela imageIcon.paintIcon( this, g, 0, 0 ); } // adiciona filho de ImagePanel a ImagePanel public void add( ImagePanel panel ) { panelChildren.add( panel ); super.add( panel ); } // adiciona filho de ImagePanel a ImagePanel em um ndice determinado public void add( ImagePanel panel, int index ) { panelChildren.add( panel ); super.add( panel, index ); } // remove filho de ImagePanel do ImagePanel public void remove( ImagePanel panel ) { panelChildren.remove( panel ); super.remove( panel ); } // configura ImageIcon corrente para ser exibido public void setIcon( ImageIcon icon ) { imageIcon = icon; }

Fig. 22.12

A classe ImagePanel representa e exibe um objeto imvel do modelo (parte 2 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1185

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

// configura posio na tela public void setPosition( double x, double y ) { position.setLocation( x, y ); setLocation( ( int ) x, ( int ) y ); } // devolve identificador do ImagePanel public int getID() { return ID; } // obtm posio do ImagePanel public Point2D.Double getPosition() { return position; } // obtm imageIcon public ImageIcon getImageIcon() { return imageIcon; } // obtm Set de filhos de ImagePanel public Set getChildren() { return panelChildren; } }

Fig. 22.12

A classe ImagePanel representa e exibe um objeto imvel do modelo (parte 3 de 3).

Moving Panel A classe MovingPanel (Fig. 22.13) uma subclasse de ImagePanel capaz de mudar sua posio na tela de acordo com sua xVelocity e yVelocity (linhas 20 e 21). A ElevatorView utiliza objetos MovingPanel para representar objetos do modelo que se movimentam, como o Elevator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// MovingPanel.java // Subclasse de JPanel com capacidade de se mover na tela package com.deitel.jhtp4.elevator.view; // Pacotes do ncleo de Java import java.awt.*; import java.awt.geom.*; import java.util.*; // Pacotes de extenso de Java import javax.swing.*; public class MovingPanel extends ImagePanel { // o MovingPanel deve mudar de posio? private boolean moving;

Fig. 22.13

A classe MovingPanel representa e exibe um objeto do modelo em movimento (parte 1 de 3).

1186
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

JAVA COMO PROGRAMAR

// nmero de pixels que MovingPanel se move na direo das // coordenadas x e y por milissegundos de animationDelay private double xVelocity; private double yVelocity; // construtor inicializa posio, velocidade e imagem public MovingPanel( int identifier, String imageName ) { super( identifier, imageName ); // configura velocidade do MovingPanel xVelocity = 0; yVelocity = 0; } // fim do construtor MovingPanel

// atualiza posio e frame de animao do MovingPanel public void animate() { // atualiza posio de acordo com a velocidade do MovingPanel if ( isMoving() ) { double oldXPosition = getPosition().getX(); double oldYPosition = getPosition().getY(); setPosition( oldXPosition + xVelocity, oldYPosition + yVelocity ); } // atualiza todos os filhos de MovingPanel Iterator iterator = getChildren().iterator(); while ( iterator.hasNext() ) { MovingPanel panel = ( MovingPanel ) iterator.next(); panel.animate(); } // fim do mtodo animate

// o MovingPanel est se movendo na tela? public boolean isMoving() { return moving; } // configura o MovingPanel para se mover na tela public void setMoving( boolean move ) { moving = move; } // configura as velocidades em x e y de MovingPanel public void setVelocity( double x, double y ) { xVelocity = x; yVelocity = y; } // devolve a velocidade em x de MovingPanel public double getXVelocity() {

Fig. 22.13

A classe MovingPanel representa e exibe um objeto do modelo em movimento (parte 2 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1187

77 78 79 80 81 82 83 84 85

return xVelocity; } // devolve a velocidade em y de MovingPanel public double getYVelocity() { return yVelocity; } }

Fig. 22.13

A classe MovingPanel representa e exibe um objeto do modelo em movimento (parte 3 de 3).

O mtodo animate (linhas 35 a 53) move o MovingPanel de acordo com os valores atuais dos atributos xVelocity e yVelocity. Se a varivel booleana moving (linha 16) for true, as linhas 38 a 44 usam os atributos xVelocity e yVelocity para determinar a prxima posio para o MovingPanel. As linhas 47 a 52 repetem o processo para qualquer filho. Em nossa simulao, ElevatorView invoca o mtodo animate e o mtodo paintComponent da classe ImagePanel a cada 50 milissegundos. Estas chamadas em rpida sucesso movem o objeto MovingPanel. Animated Panel A classe AnimatedPanel (Fig. 22.14), que estende a classe MovingPanel, representa um objeto animado do modelo (i.e., objetos em movimento cuja imagem correspondente muda continuamente), como uma Person. A ElevatorView anima um objeto AnimatedPanel mudando a imagem associada com imageIcon.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

// AnimatedPanel.java // Subclasse de MovingPanel com capacidade de animao package com.deitel.jhtp4.elevator.view; // Pacotes do ncleo de Java import java.awt.*; import java.util.*; // Pacotes de extenso de Java import javax.swing.*; public class AnimatedPanel extends MovingPanel { // o ImageIcon deve reciclar as frames private boolean animating; // taxa de reciclagem das frames (i.e., taxa de avano para a prxima frame) private int animationRate; private int animationRateCounter; private boolean cycleForward = true; // ImageIcons individuais usados para as frames de animao private ImageIcon imageIcons[]; // armazenamento para todas as seqncias de frames private java.util.List frameSequences; private int currentAnimation;

Fig. 22.14

A classe AnimatedPanel representa e exibe um objeto animado do modelo (parte 1 de 4).

1188
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

JAVA COMO PROGRAMAR

// deve repetir (continuar) animao no fim do ciclo? private boolean loop; // a animao deve exibir a ltima frame no fim da animao? private boolean displayLastFrame; // ajuda a determinar a prxima frame a exibir private int currentFrameCounter; // construtor recebe um array de nomes de arquivos e posies na tela public AnimatedPanel( int identifier, String imageName[] ) { super( identifier, imageName[ 0 ] ); // cria objetos ImageIcon a partir do array de strings imageName imageIcons = new ImageIcon[ imageName.length ]; for ( int i = 0; i < imageIcons.length; i++ ) { imageIcons[ i ] = new ImageIcon( getClass().getResource( imageName[ i ] ) ); } frameSequences = new ArrayList(); } // fim do construtor AnimatedPanel

// atualiza posio do cone e frame da animao public void animate() { super.animate(); // reproduz prxima frame da animao se countador > taxa de animao if ( frameSequences != null && isAnimating() ) { if ( animationRateCounter > animationRate ) { animationRateCounter = 0; determineNextFrame(); } else animationRateCounter++; } } // fim do mtodo animate

// determina a prxima frame da animao private void determineNextFrame() { int frameSequence[] = ( int[] ) frameSequences.get( currentAnimation ); // se no h mais frames de animao, determina a frame final, // a menos que seja especificada repetio if ( currentFrameCounter >= frameSequence.length ) { currentFrameCounter = 0; // se isLoop for false, termina animao if ( !isLoop() ) { setAnimating( false );

Fig. 22.14

A classe AnimatedPanel representa e exibe um objeto animado do modelo (parte 2 de 4).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1189

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

if ( isDisplayLastFrame() ) // exibe ltima frame da seqncia currentFrameCounter = frameSequence.length 1; } } // configura frame de animao atual setCurrentFrame( frameSequence[ currentFrameCounter ] ); currentFrameCounter++; } // fim do mtodo determineNextFrame

// adiciona seqncia de frames (animao) ArrayList frameSequences public void addFrameSequence( int frameSequence[] ) { frameSequences.add( frameSequence ); } // pergunta se AnimatedPanel est sendo animado (reciclando frames) public boolean isAnimating() { return animating; } // configura AnimatedPanel para fazer animao public void setAnimating( boolean animate ) { animating = animate; } // configura o ImageIcon atual public void setCurrentFrame( int frame ) { setIcon( imageIcons[ frame ] ); } // configura a taxa de animao public void setAnimationRate( int rate ) { animationRate = rate; } // obtm a taxa de animao public int getAnimationRate() { return animationRate; } // configura se a animao deve ser repetida public void setLoop( boolean loopAnimation ) { loop = loopAnimation; } // verifica se a animao deve ser repetida public boolean isLoop() { return loop;

Fig. 22.14

A classe AnimatedPanel representa e exibe um objeto animado do modelo (parte 3 de 4).

1190
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

JAVA COMO PROGRAMAR

} // verifica se deve exibir ltima frame no fim da animao private boolean isDisplayLastFrame() { return displayLastFrame; } // configura se deve exibir ltima frame no fim da animao public void setDisplayLastFrame( boolean displayFrame ) { displayLastFrame = displayFrame; } // comea a reproduzir a seqncia de animao do ndice determinado public void playAnimation( int frameSequence ) { currentAnimation = frameSequence; currentFrameCounter = 0; setAnimating( true ); } }

Fig. 22.14

A classe AnimatedPanel representa e exibe um objeto animado do modelo (parte 4 de 4).

A classe AnimatedPanel escolhe o objeto ImageIcon a ser desenhado na tela entre diversos objetos ImageIcon armazenados no array imageIcons (linha 23). A classe AnimatedPanel determina o objeto ImageIcon de acordo com uma srie de referncias seqncia de frames, armazenadas na List frameSequences (linha 26). A seqncia de frames um array de inteiros que guarda a seqncia apropriada para exibir os objetos ImageIcon; especificamente, cada inteiro representa o ndice de um objeto ImageIcon em imageIcons. A Fig. 22.15 demonstra o relacionamento entre imageIcons e frameSequences (isto no um diagrama da UML). Por exemplo, a seqncia de frames nmero
2 = { 2, 1, 0 }

se refere a { imageIcon[ 2 ], imageIcon[ 1 ], imageIcon[ 0 ]}, que leva seqncia de imagens { C, B, A }. Na viso, cada imagem um arquivo .png nico. O mtodo addFrameSequence (linhas 102 a 105) adiciona uma seqncia de frames List frameSequences. O mtodo playAnimation (linhas 162 a 167) inicia a animao associada com o parmetro frameSequence. Por exemplo, considere um objeto AnimatedPanel chamado personAnimatedPanel na classe ElevatorView. O segmento de cdigo
animatedPanel.playAnimation( 1 );

geraria a seqncia de imagens { A, B, D, B, A } a Fig. 22.15 como referncia. O mtodo animate (linhas 56 a 70) sobrescreve o mtodo animate da superclasse MovingPanel. As linhas 61 a 69 determinam a prxima frame da animao dependendo do atributo animationRate, que inversamente proporcional velocidade de animao um valor mais alto para animationRate leva a uma taxa de frames mais lenta. Por exemplo, se animationRate for 5, animate passa para a prxima frame da animao a cada cinco vezes em que invocado. Usando esta lgica, a taxa de animao maximizada quando animationRate tem um valor de 1, porque a prxima frame determinada cada vez que animate executado. O mtodo animate chama determineNextFrame (linhas 73 a 99) para determinar a prxima frame (imagem) a ser exibida especificamente, ele chama o mtodo setCurrentFrame (linhas 120 a 123), que configura imageIcon (a imagem exibida atual) para a imagem devolvida da seqncia de frames atual. As linhas 84 a 92 de determineNextFrame so usadas para fins de repetio na animao. Se loop for false, a animao termina aps uma iterao. A ltima frame na seqncia exibida se displayLastFrame for true, e a primeira frame na seqncia exibida se displayLastFrame for false. Explicamos em maiores detalhes no Apndice I como ElevatorView usa displayLastFrame para os AnimatedPanels Person e Door

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1191

para assegurar a exibio apropriada da imagem. Se loop for true, a animao repetida at ser explicitamente parada. Sound Effects Discutimos agora como geramos udio em nossa simulao do elevador. Dividimos a funcionalidade de udio entre duas classes SoundEffects e ElevatorMusic (estas classes no fazem parte dos pacotes de Java, embora SoundEffects use o pacote java.applet e ElevatorMusic use o pacote javax.sound.midi). A classe SoundEffects (Fig. 22.16) transforma os arquivos audio (.au) e wave (.wav), que contm sons como o toque da campainha e os passos da pessoa, em objetos java.applet.AudioClip. No Apndice I, listamos todos os AudioClips usados em nossa simulao. A classe ElevatorMusic (Fig. 22.17) reproduz um arquivo MIDI (.mid) quando a pessoa anda no elevador. O objeto ElevatorView ir reproduzir os objetos AudioClip e ElevatorMusic para gerar som. Todos os arquivos de som esto na estrutura do diretrio
com/deitel/jhtp4/elevator/view/sounds

(i.e., o diretrio sounds no qual as classes para a viso esto localizadas no sistema de arquivos). Em nossa simulao, usamos sons e arquivos MIDI fornecidos gratuitamente para download pela Microsoft no site:
msdn.microsoft.com/downloads/default.asp

Para baixar estes sons, clique em Graphics and Multimedia, Multimedia (General) e, depois, Sounds.

frameSequences

seqncias de imagens A B C B B D A A B A

imageIcons A 0 B 1 C 2 D 3

0= 0 1= 0 2= 2 3= 3

1 1 1 2

2 3 0 2 0 1 0

A C

D C C

Fig. 22.15

Relacionamento entre o array imageIcons e a List frameSequences.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// SoundEffects.java // Devolve objetos AudioClip package com.deitel.jhtp4.elevator.view; // Pacotes do ncleo de Java import java.applet.*; public class SoundEffects { // localizao dos arquivos de som private String prefix = ""; public SoundEffects() {} // obtm AudioClip associado com soundFile public AudioClip getAudioClip( String soundFile ) { try {

Fig. 22.16

A classe SoundEffects devolve objetos AudioClip (parte 1 de 2).

1192
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

JAVA COMO PROGRAMAR

return Applet.newAudioClip( getClass().getResource( prefix + soundFile ) ); } // devolve null se soundFile no existe catch ( NullPointerException nullPointerException ) { return null; } } // configura o prefixo do endereo de soundFile public void setPathPrefix( String string ) { prefix = string; } }

Fig. 22.16

A classe SoundEffects devolve objetos Courier (parte 2 de 2).

A classe SoundEffects contm o mtodo getAudioClip (linhas 16 a 27), que usa o mtodo static newAudioClip (da classe java.applet.Applet) para devolver um objeto AudioClip com o parmetro soundFile. O mtodo setPrefix (linhas 30 a 33) permite que se mude o diretrio de um arquivo de som (til se quisermos dividir nossos sons entre diversos diretrios).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // ElevatorMusic.java // Permite usar recursos de reproduo de MIDI package com.deitel.jhtp4.elevator.view; // Pacotes do ncleo de Java import java.io.*; import java.net.*; // Pacotes de extenso de Java import javax.sound.midi.*; public class ElevatorMusic implements MetaEventListener { // seqenciador MIDI private Sequencer sequencer; // a msica deve parar de tocar? private boolean endOfMusic; // nome do arquivo de som private String fileName; // seqncia associada com o arquivo de som private Sequence soundSequence; // construtor abre um arquivo MIDI para reproduzir public ElevatorMusic( String file ) { // configura sequenciador try { sequencer = MidiSystem.getSequencer(); sequencer.addMetaEventListener( this ); fileName = file;

Fig. 22.17

A classe ElevatorMusic toca msica quando uma Person anda no Elevator (parte 1 de 3).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1193

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

} // trata exceo se MIDI no estiver disponvel catch ( MidiUnavailableException midiException ) { midiException.printStackTrace(); } // fim do construtor ElevatorMusic

// abre arquivo de msica public boolean open() { try { // obtm URL para o arquivo de mdia URL url = getClass().getResource( fileName ); // obtm arquivo MIDI vlido soundSequence = MidiSystem.getSequence ( url ); // abre seqenciador para arquivo especificado sequencer.open(); sequencer.setSequence( soundSequence ); } // trata exceo se URL no existir catch ( NullPointerException nullPointerException ) { nullPointerException.printStackTrace(); return false; } // trata exceo se os dados MIDI forem invlidos catch ( InvalidMidiDataException midiException ) { midiException.printStackTrace(); soundSequence = null; return false; } // trata exceo de E/S catch ( java.io.IOException ioException ) { ioException.printStackTrace(); soundSequence = null; return false; } // trata exceo se MIDI no estiver disponvel catch ( MidiUnavailableException midiException ) { midiException.printStackTrace(); return false; } return true; } // reproduz trilha MIDI public void play() { sequencer.start(); endOfMusic = false; }

Fig. 22.17

A classe ElevatorMusic toca msica quando uma Person anda no Elevator (parte 2 de 3).

1194
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

JAVA COMO PROGRAMAR

// obtm sequenciador public Sequencer getSequencer() { return sequencer; } // trata fim de trilha public void meta( MetaMessage message ) { if ( message.getType() == 47 ) { endOfMusic = true; sequencer.stop(); } } }

Fig. 22.17

A classe ElevatorMusic toca msica quando uma Person anda no Elevator (parte 3 de 3).

Como discutimos na Seo 22.7, Java 2 oferece suporte a MIDI. A classe ElevatorMusic usa o pacote javax.sound.midi para reproduzir o arquivo MIDI. A classe ElevatorMusic espera por um evento MetaMessage do arquivo MIDI. O seqenciador gera um evento MetaMessage. O construtor da classe ElevatorMusic (linhas 27 a 40) inicializa o seqenciador MIDI do sistema e registra a classe ElevatorMusic para eventos MetaMessage do seqenciador. O mtodo open (linhas 43 a 85) abre o seqenciador para um arquivo especificado e assegura que os dados MIDI so vlidos. O mtodo play (linhas 88 a 92) inicia o seqenciador e reproduz o arquivo MIDI. Concluso Voc completou um processo de projeto orientado a objetos (OOD) substancial que tinha por objetivo ajudar a prepar-lo para os desafios dos projetos empresariais. Esperamos que voc tenha achado as sees opcionais Pensando em objetos informativas e teis como suplemento para o material apresentado nos captulos. Alm disso, esperamos que voc tenha gostado de projetar o sistema do elevador com a UML. A indstria mundial de software adotou a UML como padro de facto para a modelagem de software orientado a objetos. Embora tenhamos completado o processo de projeto, simplesmente arranhamos a superfcie do processo de implementao. Recomendamos enfaticamente que voc leia os Apndices G, H e I no CD que acompanha o livro, que implementam completamente o projeto. Estes apndices traduzem os diagramas da UML em um programa Java de 3.465 linhas para a simulao do elevador. Nestes apndices, apresentamos todo o cdigo que no cobrimos nas sees Pensando em objetos. 1. O Apndice G apresenta os arquivos Java que implementam eventos e ouvintes; 2. O Apndice H apresenta os arquivos Java que implementam o modelo; 3. O Apndice I apresenta os arquivos Java que implementam a viso. No apresentamos muitas novidades nem muito projeto em UML nestes apndices eles simplesmente servem para implementar os diagramas baseados em UML que apresentamos nos captulos anteriores como um programa completo e que funciona. Estudar a implementao nos apndices deve aprimorar as habilidades em programao que voc desenvolveu ao longo do livro e reforar sua compreenso do processo de projeto.

Resumo
Atravs da API JMF, os programadores podem criar aplicativos Java que reproduzem, editam, fazem streaming e capturam muitos tipos de mdia populares e de alta qualidade. O JMF 2.1.1 suporta tipos de arquivos de mdia populares, como Microsoft Audio/Video Interleave (.avi), filmes Macromedia Flash 2 (.swf), udio MPEG Layer 3 (.mp3), Musical Intrument Digital Interface (MIDI; .mid), vdeos MPEG-1 (.mpeg, .mpg), QuickTime (.mov) e Sun Audio (.au).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1195

A API Java Sound e seus extensos recursos de processamento de som. Java Sound uma API de nvel mais baixo que suporta muitos dos recursos de udio internos do JMF. O Player um tipo de Controller em JMF que pode processar e reproduzir clipes de mdia. Reproduzir clipes de mdia com a interface Player pode ser to simples quanto especificar a fonte de mdia, criar um Player para a mdia, obter os componentes GUI para a mdia de sada e controles de Player e exibi-los. Alm disso, os Players podem acessar mdia de um dispositivo de captura como um microfone e de um fluxo de Real-time Transport Protocol (RTP) um fluxo de bytes enviados atravs de uma rede que pode ser colocado em um buffer e reproduzido no computador cliente. Reproduzir mdia envolve acess-la, criar-lhe um Controller e envi-la para a sada. Antes de enviar a mdia para a sada, existe a opo de format-la. JMF fornece geradores de vdeo peso-leve, compatveis com os componentes Swing, usando o mtodo setHint de Manager com os parmetros Manager.LIGHTWEIGHT_RENDERER e Boolean.TRUE. O MediaLocator semelhante a um URL, mas ele tambm suporta endereos de sesso de streaming em RTP e endereos de dispositivos de captura. Invoque o mtodo createPlayer de Manager para criar um objeto Player que faz referncia a um reprodutor de mdia. O mtodo createPlayer abre a fonte de mdia especificada e determina o reprodutor apropriado para a fonte de mdia. Ocorre uma NoPlayerException se nenhum reprodutor apropriado para o clipe de mdia puder ser encontrado. A classe Manager fornece mtodos static que habilitam os programas a acessar a maioria dos recursos do JMF. Ao longo de todo o processo de manipulao de mdia, os Players geram ControllerEvents que so esperados pelos ControllerListeners. A classe ControllerAdapter implementa os mtodos da interface ControllerListener. Os Controllers usam transies de estado para confirmar sua posio no algoritmo de processamento de mdia. O mtodo realize de Player confirma todos os recursos necessrios para reproduzir mdia. O mtodo realize coloca o Player em um estado Realizing, no qual o Player interage com suas fontes de mdia. Quando o Player completa a realizao, ele gera um RealizeCompletedEvent um tipo de ControllerEvent que ocorre quando o Player completa sua transio para o estado Realized. O mtodo prefetch de Player faz com que o Player obtenha recursos de hardware para reproduzir a mdia e comece a colocar os dados da mdia no buffer. Colocar os dados da mdia em um buffer reduz o retardo antes que o clipe de mdia seja reproduzido, porque a leitura da mdia pode consumir um tempo muito longo. Invoque o mtodo getVisualComponent de Player para obter o componente visual de um vdeo. Invoque o mtodo getControlPanelComponent de Player para obter de volta os componentes GUI de controle do Player. Quando o clipe de mdia termina, o Player gera um ControllerEvent do tipo EndOfMediaEvent. O mtodo de Player setMediaTime configura a posio da mdia para um tempo especfico na mdia. Invocar o mtodo start de Player inicia a reproduo da mdia. e tambm coloca dados no buffer e realiza o Player se isto ainda no foi feito. Os dispositivos de captura como microfones tm a capacidade de converter mdia analgica digitalizada. Este tipo de mdia conhecida como mdia capturada. A classe DataSource abstrai a fonte de mdia para permitir que um programa a manipule e fornece uma conexo com a fonte de mdia. A interface Processor permite que um programa manipule dados nos diversos estgios de processamento. Ela estende a interface Player e fornece mais controle sobre o processamento da mdia. A monitorao permite que voc escute ou veja a mdia capturada medida que capturada e salva. O MonitorControl e outros objetos de controle podem ser obtidos de Controller invocando o mtodo getControl. JMF fornece a classe Format para descrever os atributos de um formato de mdia, como a taxa de amostragem (que controla a qualidade do som) e se a mdia deve ser em formato mono ou estreo. Os objetos FormatControl nos permitem formatar os objetos que suportam controles de formato. A classe CaptureDeviceManager permite que um programa acesse informaes sobre o dispositivo de captura. O objeto CaptureDeviceInfo fornece as informaes essenciais necessrias sobre a Datasource de um dispositivo de captura. Invoque o mtodo createDataSource de Manager para obter o objeto DataSource daquele endereo de mdia. O mtodo createRealizedProcessor cria um objeto Processor realizado que pode comear a processar dados da mdia. O mtodo exige como argumento um objeto ProcessorModel que contm as especificaes do Processor.

1196

JAVA COMO PROGRAMAR

Use um ContentDescriptor para descrever o tipo de contedo de sada de um Processor. FileTypeDescriptor especifica o contedo de um arquivo de mdia. Chame o mtodo getTrackControls de Processor para obter os controles de cada trilha. O objeto que implementa a interface DataSink permite que os dados de mdia sejam enviados para a sada em um endereo especfico na maioria das vezes um arquivo. O mtodo createDataSink de Manager recebe a DataSource e o MediaLocator como argumentos para criar um objeto DataSink. Registre um DataSinkListener para esperar DataSinkEvents gerados por um DataSink. O programa pode chamar o mtodo dataSinkUpdate de DataSinkListener quando ocorre cada DataSinkEvent. O DataSink causa um EndOfStreamEvent quando a conexo com o stream de captura fecha. Mdia de streaming se refere mdia que transferida de um servidor para um cliente em um fluxo contnuo de bytes. A tecnologia de mdia de streaming carrega dados da mdia em buffers antes de exibir a mdia. O JMF fornece um pacote para mdia de streaming que permite aos aplicativos Java enviar e receber streams de mdia nos formatos discutidos anteriormente neste captulo. JMF usa o padro Real-Time Transport Protocol (RTP) para controlar a transmisso da mdia. O RTP foi projetado especificamente para transmitir dados de mdia em tempo real. Use um DataSink ou um RTPManager para fazer streaming de mdia. Os RTPManagers oferecem mais controle e versatilidade para a transmisso. Se um aplicativo envia mltiplos streams, o aplicativo deve ter um RTPManager para cada sesso de streaming separada. Ambos requerem a DataSource obtida do mtodo getOutput de Processor. O URL de streams de RTP est no formato: rtp://<host>:<porta>/<tipoDeContedo> A formatao da mdia s pode ser feita quando o Processor foi configurado. Para notificar o programa quando ele completa a configurao do Processor, registre um ControllerListener para notificar o programa de que ele completou a configurao. Ocorre um ConfigureCompleteEvent quando o Processor completa a configurao. O mtodo setContentDescriptor de Processor configura o stream para um formato habilitado para RTP com o parmetro ContentDescriptor.RAW_RTP. A interface TrackControl permite que os formatos das trilhas da mdia sejam configurados. SessionAddress contm o endereo IP e o nmero de porta usados no processo de streaming. Os RTPManagers usam SessionAddresses para fazer streaming de mdia. Invoque o mtodo initialize de RTPManager para inicializar a sesso de streaming local com o endereo de sesso local como parmetro. Invoque o mtodo addTarget de RTPManager para adicionar o endereo de sesso de destino como cliente que recebe o stream de mdia. Para fazer streaming de mdia para vrios clientes, chame o mtodo addTarget de RTPManager para cada endereo de destino. O mtodo removeTargets de RTPManager fecha o streaming para destinos especficos. O mtodo dispose de RTPManager libera os recursos mantidos pelas sesses de RTP. A API Java Sound fornece classes e interfaces para acessar, manipular e reproduzir udio Musical Instrument Digital Interface (MIDI) e udio amostrado. necessria uma placa de som para reproduzir udio com o Java Sound. o Java Sound dispara excees quando ela acessa recursos de udio do sistema para processar udio em um computador que no tem uma placa de som. Os programadores podem usar o pacote javax.sound.sampled para reproduzir os arquivos em formatos de amostra de udio, que incluem Sun Audio (.au), Wave (.wav) e AIFF (.aiff). Para processar dados de udio, podemos usar uma linha de Clip que permite o fluxo de dados digitais brutos para dados de udio que podemos escutar. O objeto AudioInputStream aponta para o stream de udio. A classe AudioInputStream (uma subclasse de InputStream) fornece acesso ao contedo do stream de udio. O tamanho de clipes de udio e vdeo medido em frames. Cada frame representa dados em um intervalo de tempo especfico no arquivo de udio. O algoritmo para reproduzir amostra de udio suportado por Java Sound o seguinte: obter um AudioInputStream de um arquivo de udio, obter uma linha de Clip formatada, carregar o AudioInputStream na linha de Clip, iniciar o fluxo de dados na linha de Clip. Todas as Lines geram LineEvents que podem ser tratados por LineListener. A primeira etapa para reproduzir amostras de udio envolve obter o stream de udio a partir de um arquivo de udio. A classe AudioSystem permite que um programa acesse muitos recursos de udio do sistema necessrios para reproduzir e manipular arquivos de som. O mtodo getAudioInputStream dispara uma UnsupportedAudioFileException se o arquivo de som especificado no for um arquivo de udio ou contiver um formato de clipe de som que no suportado por Java Sound.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1197

O mtodo getLine exige um objeto Line.Info como argumento, que especifica os atributos da linha que o AudioSystem deve obter. Podemos usar um objeto DataLine.Info que especifica uma linha de dados Clip, um formato genrico de codificao e um intervalo de buffer. Precisamos especificar um intervalo de buffer para que o programa possa determinar o melhor tamanho de buffer a partir de um intervalo preferencial dado. Os objetos DataLine.Info especificam informaes sobre uma linha de Clip, como os formatos suportados pelo Clip. O construtor do objeto DataLine.Info recebe como argumentos a classe Line, os AudioFormats suportados pela linha, o tamanho de buffer mnimo e o tamanho de buffer mximo, em bytes. O mtodo getLine de AudioSystem e o mtodo open de Clip disparam LineUnavailableExceptions se outro aplicativo estiver usando o recurso de udio solicitado. O mtodo open de Clip tambm dispara uma IOException se open no consegue ler o AudioInputStream especificado. Invoque o mtodo start de Clip para iniciar a reproduo de udio. Quando ocorre um LineEvent, o programa chama o mtodo update de LineListener para processar o evento. Os quatro tipos de LineEvent esto definidos na classe LineEvent.Type. Os tipos de eventos so CLOSE, OPEN, START e STOP. O mtodo close da classe Line suspende a atividade de udio e fecha a linha o que libera quaisquer recursos para udio obtidos anteriormente pela Line. O mtodo loop de Clip pode ser chamado com o parmetro Clip.LOOP_CONTINUOUSLY para repetir o clipe de udio para sempre. Msica MIDI (Musical Instrument Digital Interface) pode ser criada atravs de um instrumento digital, como um teclado eletrnico (sintetizador) ou atravs de sintetizadores em pacotes de software. O sintetizador MIDI um dispositivo que pode produzir sons e msica MIDI. A especificao MIDI fornece informaes detalhadas sobre os formatos de um arquivo MIDI. Para obter informaes detalhadas sobre MIDI e sua especificao, visite o seu site oficial na Web, no endereo www.midi.org. O pacote MIDI de Java Sound permite que os desenvolvedores acessem os dados que especificam o MIDI, mas ele no fornece suporte para a especificao. A interpretao de dados MIDI varia entre sintetizadores e ir soar diferente com instrumentos diferentes. O pacote javax.sound.midi permite que o programa manipule, reproduza e sintetize MIDI. Existem trs tipos de MIDI 0 (o mais comum), 1 e 2. O Java Sound suporta arquivos MIDI com as extenses .mid e .rmf (Rich Music Format). Alguns reconhecedores de arquivos em diversos sistemas operacionais so incapazes de interpretar o arquivo MIDI como um arquivo MIDI que Java pode reproduzir. A reproduo de MIDI executada por um seqenciador MIDI. Especificamente, os seqenciadores podem reproduzir e manipular uma seqncia MIDI, que a frmula de dados que diz a um dispositivo como tratar os dados MIDI. Freqentemente, o MIDI conhecido como uma seqncia, porque os dados musicais em MIDI so compostos por uma seqncia de eventos. A simplicidade dos dados de MIDI nos permite visualizar cada evento individualmente e aprender o propsito de cada evento. O processo de reproduo de MIDI envolve acessar um seqenciador, carregar uma seqncia MIDI ou um arquivo MIDI em um seqenciador e iniciar o seqenciador. O mtodo getSequence tambm pode obter uma seqncia MIDI de um URL ou um InputStream. O mtodo getSequence dispara uma InvalidMidiDataException se o sistema MIDI detectar um arquivo MIDI incompatvel. A interface Sequencer, que estende a interface MidiDevice (a super-interface para todos os dispositivos MIDI), representa o dispositivo-padro para reproduzir dados MIDI. O mtodo open de Sequencer prepara a reproduo de uma Sequence. O mtodo setSequence de Sequencer carrega uma Sequence MIDI no Sequencer e dispara uma InvalidMidiException se o Sequencer detectar uma seqncia MIDI irreconhecvel. O mtodo play de Sequencer inicia a reproduo da seqncia MIDI. A trilha MIDI uma seqncia de dados gravada; as MIDIs normalmente contm mltiplas trilhas. As trilhas MIDI so semelhantes s trilhas de CD, exceto pelo fato de que os dados de msica em MIDI so reproduzidos simultaneamente. A classe Track (pacote javax.sound.midi) fornece acesso aos dados de msica MIDI armazenados nas trilhas MIDI. Os dados MIDI em trilhas MIDI so representados por eventos MIDI. Os eventos MIDI guardam a ao de MIDI e o tempo em que o comando MIDI deve ocorrer. Existem trs tipos de mensagens MIDI ShortMessage, SysexMessage e MetaMessage. ShortMessages fornecem instrues, como notas especficas a tocar, e podem configurar opes, como quando inicia uma MIDI. As outras duas mensagens, menos usadas, so mensagens exclusivas do sistema chamadas SysexMessage e MetaMessages, que podem dizer a um dispositivo que a MIDI atingiu o

1198

JAVA COMO PROGRAMAR

fim de uma trilha. Esta seo trata exclusivamente de ShortMessages que tocam notas especficas. Cada MidiMessage encapsulada em um MidiEvent e uma seqncia de MidiEvents forma uma trilha MIDI. Cada mtodo getTick de MidiEvent fornece o tempo em que o evento acontece (time stamp). O mtodo getCommand de ShortMessage devolve o inteiro que representa o comando da mensagem. O mtodo getData1 de ShortMessage devolve o primeiro byte de estado da mensagem. O mtodo getData2 de ShortMessage devolve o segundo byte de estado. O primeiro e o segundo bytes de estado variam de interpretao de acordo com o tipo de comando na ShortMessage. A gravao de MIDI genrica executada atravs de um seqenciador. A interface Sequencer fornece mtodos simples para gravar supondo que os transmissores e os receptores dos dispositivos MIDI estejam conectados corretamente. Depois de configurar um seqenciador e uma seqncia vazia, o objeto Sequencer pode invocar seu mtodo startRecording para habilitar e iniciar a gravao na trilha vazia. O mtodo recordEnable da interface Sequencer recebe um objeto Track e um nmero de canal como parmetros para permitir a gravao em uma trilha. O mtodo write da classe MidiSystem escreve a seqncia em um arquivo especificado. Um mtodo alternativo para gravar MIDI sem ter que lidar com transmissores e receptores criar eventos a partir de ShortMessages. Os eventos devem ser adicionados a uma Track de uma Sequence. A interface Synthesizer uma interface MidiDevice que habilita o acesso gerao de som MIDI, aos instrumentos, aos recursos de canais e aos bancos de sons. O SoundBank o continer para diversos Instruments, que dizem ao computador como emitir uma nota especfica e so algoritmos programados com instrues. Notas diferentes em diversos instrumentos so reproduzidas atravs de um MidiChannel em diferentes trilhas simultaneamente, para produzir melodias sinfnicas. A aquisio de quaisquer recursos MIDI dispara uma MidiUnavailableException se o recurso no estiver disponvel. Invoque o mtodo getChannels de Synthesizer para obter todos os 16 canais do sintetizador. O MidiChannel pode emitir uma nota chamando seu mtodo noteOn com o nmero da nota (0 a 127) e o volume como parmetros. O mtodo noteOff de MidiChannel desliga uma nota apenas com o nmero da nota como parmetro. O mtodo getAvailableInstruments de Synthesizer obtm os programas do instrumento default de um sintetizador. Tambm se pode importar mais instrumentos carregando um banco de sons personalizado atravs do mtodo loadAllInstruments (SoundBank) na interface Synthesizer. O banco de sons normalmente tem 128 instrumentos. O mtodo programChange de MidiChannel carrega o programa do instrumento desejado no sintetizador. Invoque o mtodo send de um Receiver com uma MidiMessage e um time stamp como parmetros, para enviar a mensagem MIDI para todos os transmissores.

Terminologia
addControllerListener, mtodo de Controller addDataSinkListener, mtodo de DataSink addLineListener, mtodo de Line addMetaEventListener, mtodo de Sequencer addTarget, mtodo de RTPManager AudioFormat, classe AudioFormat.Encoding.PCM_SIGNED AudioInputStream, classe AudioSystem, classe banda larga Boolean.TRUE, codificao CannotRealizeException, classe captura CaptureDevice, interface CaptureDeviceInfo, classe CaptureDeviceManger, classe Clip Clip.class Clip.LOOP_CONTINUOUSLY clipe de mdia Clock, interface close, mtodo de Controller close, mtodo de Line close, mtodo de MidiDevice colocar previamente em buffer configure, mtodo de Processor configureComplete, mtodo de ControllerAdapter ConfigureCompleteEvent, classe Controller.Prefetching Controller.Prefeteched Controller.Realized Controller.Realizing Controller.Started Controller.Stopped ControllerAdapter, classe ControllerEvent, classe ControllerListener, interface

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD) createDataSink, mtodo de Manager createDataSource, mtodo de Manager createPlayer, mtodo de Manager createProcessor, mtodo de Manager createRealizedProcessor, mtodo de Manager createSendStream, mtodo de RTPManager createTrack, mtodo de Sequence dados empacotados DataLine, interface DataLine.Info, classe DataSink, interface DataSinkEvent, classe DataSinkListener, interface dataSinkUpdate, mtodo de DataSinkListener DataSource, classe deleteTrack, mtodo de Sequence Direct Sound dispose, mtodo de RTPManager dispositivo de captura endereo de mdia endOfMedia, mtodo de ControllerAdapter EndofMediaEvent, classe EndofStreamEvent, classe FileTypeDescriptor, classe FileTypeDescriptor.QUICKTIME Format, classe FormatControl, interface formato de sada frames get, mtodo de Track getAudioInputStream, mtodo de AudioSystem getBank, mtodo de Patch getChannels, mtodo de Synthesizer getCommand, mtodo de ShortMessage getControlComponent, mtodo de Control getControlPanelComponent, mtodo de Player getData1, mtodo de ShortMessage getData2, mtodo de ShortMessage getDataOutput, mtodo de Processor getDeviceList, mtodo de CaptureDeviceManager getFormat, mtodo de AudioFormat getFormat, mtodo de FormatControl getFormatControls, mtodo de CaptureDevice getFrameLength, mtodo de AudioInputStream getFrameSize, mtodo de AudioFormat getLine, mtodo de AudioSystem getLocator, mtodo de CaptureDeviceInfo getMessage, mtodo de MidiEvent getMidiFileTypes, mtodo de MidiSystem getPatch, mtodo de Instrument getProgram, mtodo de Patch getReceiver, mtodo de MidiDevice getResolution, mtodo de Sequence getSequence, mtodo de MidiSystem getSequencer, mtodo de MidiSystem getSupportedFormats, mtodo de FormatControl getSynthesizer, mtodo de MidiSystem getTargetFormat, mtodo de AudioSystem getTick, mtodo de MidiEvent getTrackControls, mtodo de Processor getTracks, mtodo de Sequence getTransmitter, mtodo de MidiDevice getType, mtodo de LineEvent getVisualComponent, mtodo de Player initialize, mtodo de RTPManager Instrument, classe interface InvalidMidiException, classe InvalidSessionAddress, classe isEnabled, mtodo de FormatControl isLineSupported, mtodo de AudioSystem Java Media Framework Java Sound javax.media, pacote javax.media.control, pacote javax.media.datasink, pacote javax.media.format, pacote javax.media.protocol, pacote javax.media.rtp, pacote javax.sound.midi, pacote javax.sound.sampled, pacote JOptionPane.CLOSED_OPTION JOptionPane.DEFAULT_OPTION JOptionPane.OK_OPTION Line, interface LineEvent, classe LineEvent.Type.STOP LineListener, interface LineUnavailableException, classe loop, mtodo de Clip Manager, classe Manger.LIGHTWEIGHT_RENDERER MediaLocator, classe MIDI (Musical Instrument Digital Interface) MIDI, especificao mdia mdia capturada mdia de streaming MidiChannel, interface MidiEvent, classe MidiMessage, classe MidiSystem, classe MidiUnavailableException, classe monitorao MonitorControl, interface MP3 MPEG-1 newInstance, mtodo de RTPManager NoDataSinkException, classe NoDataSourceException, classe NoPlayerException, classe NoProcessorException, classe noteOff, mtodo de MidiChannel noteOn, mtodo de MidiChannel open, mtodo de Clip

1199

1200

JAVA COMO PROGRAMAR setHint, mtodo de Manager setMediaTime, mtodo de Clock setMessage, mtodo de ShortMessage setReceiver, mtodo de Transmitter setSequence, mtodo de Sequencer ShortMessage, classe ShortMessage.NOTE_OFF ShortMessage.NOTE_ON ShortMessage.PROGRAM_CHANGE simulao sincronizao sntese size, mtodo de track SoundBank, interface start, mtodo de DataLine start, mtodo de DataSink start, mtodo de Player start, mtodo de SendStream start, mtodo de Sequencer startRecord, mtodo de Sequencer stop, mtodo de DataLine stop, mtodo de DataSink stop, mtodo de Sequencer stopRecord, mtodo de Sequencer streams Synthesizer, interface teleconferncia tempo time stamp Track, classe TrackControl, interface Transmitter, interface trilhas de mdia UnsupportedAudioFileException, classe UnsupportedFormatException, classe videoconferncia Video for Windows write, mtodo de MidiSystem

open, mtodo de DataSink open, mtodo de MidiDevice Patch, classe pitch Player, interface portas de dispositivo portas de rede prefetchComplete, mtodo de ControllerAdapter PrefetchCompleteEvent, classe processar previamente Processor, interface Processor.Configured Processor.Configuring ProcessorModel, classe protocolo QuickTime realize, mtodo de Controller realizeComplete, mtodo de ControllerAdapter RealizeCompleteEvent, classe Receiver, interface recordEnable, mtodo de Sequencer removeTargets, mtodo de RTPManager retardo de propagao RMF (Rich Music Format) RTP (Real-time Transport Protocol) RTPManger, classe SecurityException, classe send, mtodo de Receiver SendStream, interface Sequence, classe Sequence.PPQ Sequencer, interface SessionAddress, classe SessionAddress, classe SessionEvent, classe SessionListener, interface setContentDescriptor, mtodo de Processor setFormat, mtodo de FormatControl

Exerccios de auto-reviso
22.1
Preencha as lacunas em cada uma das frases seguintes: a) A classe __________ fornece acesso a muitos recursos de JMF. b) Alm de endereos de arquivos de mdia armazenados no computador local, o __________ tambm pode especificar o endereo de dispositivos de captura e sesses de RTP. c) A classe __________ fornece acesso a recursos do sistema para amostra de udio, enquanto a classe __________ fornece acesso a recursos do sistema para MIDI. d) O evento do tipo __________ indica que um Controller j estabeleceu comunicao com a fonte de mdia. e) O mtodo createRealizedProcessor recebe um __________ como argumento. f) Em ordem, os estados de Processor so: Unrealized, __________, __________, __________, __________, __________, __________ e Started. g) A constante __________ especifica que o Processor deve enviar mdia para a sada no formato QuickTime. h) Para fazer streaming de mdia, podemos usar um __________ ou um __________.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1201

i) j)

22.2

Os objetos __________ configuram os formatos de streams para dispositivos de captura. Invocar o mtodo __________ de Clip com a constante __________ como argumento repete um arquivo de amostra de udio continuamente. k) O __________ MIDI contm mltiplas trilhas, as quais contm uma seqncia de __________ MIDI, e cada uma deles encapsula uma __________ MIDI. Diga se cada uma das seguintes afirmaes verdadeira ou falsa. Se for falsa, explique por qu. a) O mtodo setHint de Manager pode ser usado para especificar que o componente visual de um clipe de mdia deve ser gerado com componentes GUI peso leve. b) O ControllerListener trata de eventos gerados por um DataSink. c) Somente objetos que implementam a interface Processor podem reproduzir mdia. d) O Player no pode acessar mdia de dispositivos de captura; o Processor deve ser usado para esta finalidade. e) O Clip reproduz Sequences de MIDI. f) A reproduo de MIDI pra automaticamente quando o Sequencer atinge o fim de uma seqncia MIDI. g) O RTPManger pode fazer streaming de um arquivo de mdia inteiro, independentemente do nmero de trilhas no arquivo. h) O mtodo createPlayer dispara uma NoDataSourceException se ele for incapaz de localizar a fonte de dados de mdia especificada.

Respostas aos exerccios de auto-reviso


22.1 a) Manager. b) MediaLocator. c) AudioSystem, MidiSystem. d) RealizeCompleteEvent. e) ProcessorModel. f) Configuring, Configured, Realizing, Realized, Prefetching, Prefetched. g) FileTypeDescriptor.QUICKTIME. h) DataSink, RTPManager. i) FormatControl. j) loop, Clip.LOOP_CONTINUOUSLY. k) Sequence, eventos, mensagem. 22.2 As respostas do exerccio de auto-reviso 3.2 so as seguintes: a) Verdadeira. b) Falsa. O DataSinkListener trata de DataSinkEvents gerados por um DataSink. c) Falsa. Os objetos que implementam Player ou Processor podem reproduzir mdia. d) Falsa. Tanto um Processor quanto um Player podem acessar mdia de dispositivos de captura. e) Falsa. O Sequencer reproduz seqncias MIDI. f) Verdadeira. g) Falsa. Cada RTPManager pode fazer streaming de apenas uma trilha. h) Falsa. O mtodo createPlayer dispara uma NoPlayerException se ele no for capaz de localizar a fonte de dados de mdia especificada.

Exerccios
22.3 Os clipes de udio Wave so comumente usados para reproduzir os sons que alertam o usurio sobre um problema em um programa. Normalmente, tais sons so acompanhados por dilogos de mensagem de erro. Modifique o exemplo DivideByZeroTest da Fig. 14.1 para reproduzir um som de mensagem de erro (alm de exibir um dilogo de mensagem de erro) se o usurio digitar um inteiro invlido ou tentar dividir por zero. Carregue previamente um clipe de som compatvel, usando uma linha de Clip como demonstrado na Fig. 22.5. A linha de Clip precisa suportar s o formato do clipe de som escolhido. Deve haver um mtodo separado que invoca a reproduo do clipe. Quando o programa detecta uma exceo, ele deve chamar este mtodo para reproduzir o som da mensagem de erro. Aps cada reproduo do clipe, o programa precisa reenrolar o clipe invocando o mtodo setFramePosition de Clip com a posio da frame como argumento, de modo que o clipe possa ser repetido a partir de sua posio inicial. 22.4 Incorpore recursos de reproduo de arquivos MIDI, como demonstrado na classe MidiData (Fig. 22.7), demonstrao ClipPlayer. A classe ClipPlayer deve ter mtodos separados para obter os dados da seqncia MIDI e reproduzir a seqncia com um seqenciador. 22.5 A demonstrao SimplePlayer (Fig. 22.7) demonstrou os recursos de JMF para reproduo de mdia (vdeos, mdia capturada) com a interface Player. Usando a demonstrao SimplePlayer como diretriz, desenvolva um aplicativo de karaok no qual uma parte do programa reproduz um arquivo de msica/vdeo (de preferncia sem voz) en-

1202

JAVA COMO PROGRAMAR

quanto outra parte do programa simultaneamente captura a voz do usurio. O programa deve iniciar a reproduo e a captura assim que obtiver a mdia. importante que o programa permita ao usurio controlar tanto a captura quanto a msica, de modo que as GUIs de controle de cada mdia devem ser exibidas. Quando o arquivo de mdia termina de ser reproduzido, a captura de voz deve terminar e o programa deve posicionar a msica novamente no incio. O programa deve fechar todos os recursos relacionados com o Player quando o usurio termina o programa. 22.6 Modifique a soluo do Exerccio 3.5 implementando o programa com a interface Processor. Crie um Processor que est pronto para exibir a mdia sem especificaes de formato ou sada. A captura de voz no deve terminar e a mdia no deve voltar para o incio quando a reproduo da mdia termina. Libere recursos relacionados com o Processor quando o usurio abrir um novo arquivo ou fechar o programa. Todos os outros detalhes do programa permanecem os mesmos, como especificado no Exerccio 3.5. 22.7 Usando como referncia o processo de salvamento de arquivo demonstrado na classe CapturePlayer (Fig. 22.2), modifique a soluo do Exerccio 22.6 salvando os dois streams de udio em dois arquivos QuickTime separados. Especifique que as trilhas de mdia devem estar no formato de codificao AudioFormat.IMA4. O programa deve exibir dilogos de salvamento de arquivo para cada salvamento de arquivo de udio e uma mensagem quando o salvamento for completado ou parar. Devem existir gravadores da dados separados para cada stream de udio. O programa deve fechar os gravadores de dados quando no houver mais dados para processar ou quando o usurio terminar o programa. O mtodo getSourceDataSink de DataSinkEvent est disponvel para obter o DataSink que est gerando o DataSinkEvent. Use MonitorControls para monitorar os dois streams de udio, de modo que no haja necessidade de exibir controles de vdeo ou controles default do usurio. O mtodo setEnabled de MonitorControl est disponvel para habilitar o monitoramento dos streams de udio. Exiba um dos MonitorControls em uma caixa de dilogo. Assegure-se de fechar os recursos do Processor quando no houver mais dados no arquivo de mdia ou quando o usurio abrir outro arquivo ou terminar o programa. 22.8 Modifique a classe MidiSynthesizer (Fig. 22.9) para um aplicativo em que o usurio pode tocar notas de violino pressionando as teclas do teclado do computador. Use o cdigo virtual das teclas para especificar o nmero da nota que o sintetizador deve tocar. O nmero do programa de violino 40. Use o primeiro e o nono canais para emitir as notas ao mesmo tempo. O nono canal pode gerar uma verso diferente das notas do violino.

Seo especial: projetos desafiadores de multimdia


Os exerccios precedentes so vinculados ao texto e projetados pata testar a compreenso do usurio dos conceitos fundamentais de JMF e Java Sound. Esta seo inclui uma coleo de projetos avanados de multimdia. O leitor deve achar estes problemas desafiadores, mas ainda assim divertidos. Os problemas variam consideravelmente em grau de dificuldade. Alguns exigem uma hora ou duas para codificao do programa e implementao. Outros so teis para trabalhos prticos que podem exigir duas ou trs semanas de estudo e implementao. Alguns so projetos desafiadores para um semestre. [Nota: no so fornecidas as solues para estes exerccios.] 22.9 Modifique a demonstrao ClipPlayer (Fig. 22.5 e Fig. 22.6) para fornecer uma caixa de marcao de repetio que permita ao usurio repetir a reproduo do arquivo de amostra de udio. 22.10 Modifique a demonstrao RTPServer (Fig. 22.3) para permitir a transmisso das partes de udio dos arquivos de mdia para dois clientes. A classe de teste do aplicativo deve ter o dobro do nmero de caixas de dilogo de pedido de IP e nmero da porta. O programa pode verificar os formatos de udio fazendo com que os formatos de controle de trilha da mdia sejam instncias de AudioFormat (classe AudioFormat). 22.11 Muitos sites da Web so capazes de reproduzir clipes de vdeo. O programa SimplePlayer (Fig. 22.1) pode ser um applet. Simplifique o programa SimplePlayer para um applet que reproduz um clipe de mdia previamente carregado em uma pgina Web. Insira a marca deste applet em seu arquivo HTML:
<applet code = NomeDoApplet.class width = # height = # > <param name = arquivo value = "exemplo.mov" > </applet>

22.12 Modifique a classe MidiRecord (Fig. 22.8) e a classe MidiData (Fig. 22.7) para criar uma nova classe de
aplicativo que duplica arquivos MIDI gravando a seqncia em um novo arquivo. Reproduza a seqncia usando MidiData e use seu Transmitter para transmitir informaes MIDI para o Receiver de MidiRecord. 22.13 Modifique a soluo do Exerccio 22.6 para criar um aplicativo de karaok com streaming no qual o aplicativo faz streaming somente da parte de vdeo de um vdeo de msica e o stream de som substitudo por um stream de captura de voz. [Nota: se o formato de vdeo contiver somente uma trilha tanto para udio como para vdeo, o aplicativo no pode optar por fazer streaming apenas da parte de vdeo da trilha.]

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1203

22.14 Modifique a classe MidiData (Fig. 22.7) para carregar todas as trilhas de um arquivo MIDI e revise a classe
MidiDemo (Fig. 22.10) para habilitar o usurio a selecionar a reproduo de cada trilha exibida em um painel seletor JList. Permita que o usurio repita a seqncia para sempre. 22.15 Implemente um reprodutor de MP3 com uma janela com uma lista de arquivos usando Vectors e uma JList. 22.16 Modifique a classe MidiRecord (Fig. 22.8) e a classe MidiDemo (Fig. 22.10) para permitir que o usurio grave MIDI em trilhas armazenadas em um Vector. A reproduo do MIDI gravado deve reproduzir todas as trilhas MIDI simultaneamente. 22.17 Atualmente, o programa MidiDemo (Seo 22.7) grava msica sintetizada com o primeiro instrumento disponvel (i.e., Grand Piano). Modifique a classe MidiDemo de modo que a msica seja gravada com um instrumento selecionado pelo usurio e permita mudar o instrumento durante a gravao. Tambm permita ao usurio importar seus prprios bancos de som. Faa mudanas nas classes MidiSynthesizer, MidiData e MidiRecord conforme necessrio. (Dica: o parmetro de comando para mudar o instrumento ShortMessage.PROGRAM_CHANGE). 22.18 Modifique a demonstrao SimplePlayer (Fig. 22.1) para suportar vrios reprodutores de mdia. Apresente cada clipe de mdia em seu prprio JInternalFrame. O programa precisa criar Players separados para cada clipe de mdia e deve registrar um ControllerListener para cada Player. O mtodo getSourceController de ControllerEvent est disponvel para obter o controlador que gerou o ControllerEvent. Implemente o programa com uma estrutura dinmica de dados, como Vector, para armazenar os vrios Players. 22.19 Modifique a soluo do Exerccio 3.7 para salvar os dois streams de mdia em um arquivo e reproduzir o stream combinado. Use o mtodo createMergingDataSource de Manager, que recebe um array de objetos DataSource, para salvar tanto o stream de captura quanto o stream de msica em um s stream, cujo tipo de contedo ser MIXED. O programa deve obter as DataSources de sada dos Processors como os objetos DataSource a serem intercalados. O programa tambm deve obter uma DataSource duplicata (da DataSource intercalada) para criar o Player para aquela DataSource. Para fazer isto, use o mtodo createCloneableDataSource de Manager para criar uma DataSource Cloneable com a DataSource intercalada como argumento. Duplique a DataSource para o Player invocando o mtodo createClone da interface Cloneable sobre a DataSource (semelhante a se obter os FormatControls de uma DataSource de CaptureDevice na demonstrao CapturePlayer (Fig. 22.2)). 22.20 Um programa pode gravar MIDI, sem o uso de transmissores e receptores, criando MidiMessages, colocando-as em MidiEvents e adicionando estes eventos a uma Track. Alm do argumento MidiMessage, um programa deve especificar um time stamp para criar o MidiEvent, expresso em ticks (isto , milissegundos do tipo long), de modo que o programa deve obter a hora atual do sistema em milissegundos, que pode ser obtida do mtodo currentTimeMillis de System. O mtodo add de Track est disponvel para adicionar eventos a uma trilha. Crie uma mesa acstica (por exemplo, tambores, pratos, etc.) na qual o usurio pode selecionar uma seqncia de instrumentos a tocar. Permita que o usurio salve a seqncia MIDI gravada em um arquivo. 22.21 Crie um kit de teleconferncia peer-to-peer que habilita os usurios a conversar e ouvir um ao outro. Para ouvir um ao outro, cada usurio deve abrir uma sesso de RTP para o stream de captura. O programa pode abrir um stream de RTP com um MediaLocator especificando o endereo de sesso RTP. A seguir, o programa pode usar o MediaLocator para criar um Player para o stream de RTP. O programa pode enviar a captura de voz como demonstrado na demonstrao RTPManager (Fig. 22.3). Para enviar a captura de voz para mais de duas pessoas, use RTPManagers para cada sesso separada e chame seu mtodo addTarget para adicionar o endereo de sesso de cada receptor como o destino do stream de captura. Isto chamado de sesses multicast-unicast. 22.22 Usando o acionador de tocador de piano (a Seo 22.7 discute o acionador) da classe MidiDemo (Fig. 22.10) e as funes de animao de imagens do Captulo 18, escreva um programa que exibe uma bola que salta cuja altura mxima especificada pelos nmeros de notas (de uma ShortMessage em um MidiEvent) de uma trilha carregada em um arquivo MIDI. O programa deve usar os pulsos de durao do MidiEvent como durao do salto. 22.23 A maioria dos vdeos de msica de karaok est no formato MPEG, para o qual o JMF fornece uma interface MpegAudioControl para controlar os canais de udio. Para vdeos MPEG em mais de uma lngua e karaok, os canais direcionam o udio principal para um de dois streams de udio (por exemplo, stream de udio dublado em ingls) ou os dois streams de udio (por exemplo, o canal de msica e o de voz ligados em vdeos de karaok). Como recurso adicional para vdeos MPEG em sua soluo do Exerccio 3.6, obtenha e exiba o componente GUI de controle de um MpegAudioControl, se houver, ou exiba uma GUI personalizada com boto de rdio seletor, para deixar o usurio selecionar o leiaute de canal do udio MPEG. A interface MpegAudioControl fornece o mtodo setChannelLayout para configurar o leiaute do canal de udio para vdeos MPEG, com o leiaute do canal como argumento (como referncia, veja o uso de MonitorControl na Fig. 22.2).

1204

JAVA COMO PROGRAMAR

22.24 Crie um jogo-da-velha com multimdia que reproduza sons de amostra de udio quando o jogador faz um movimento vlido ou faz um movimento invlido. Use um Vector para armazenar AudioInputStreams carregados previamente que representam cada clipe de udio. Use uma linha de Clip para reproduzir sons em resposta s interaes do usurio com o jogo. Reproduza msica de fundo MIDI continuamente enquanto o jogo est em andamento. Use a interface Player de JMF para reproduzir um vdeo quando um jogador vence. 22.25 O pacote MIDI de Java Sound pode acessar dispositivos MIDI de software e de hardware. Se voc tem um teclado MIDI que o computador pode detectar ou que possa ser ligado a uma porta MIDI IN de uma placa de som, o Java Sound pode acessar aquele teclado. Use sintetizadores, receptores, transmissores e seqncias para permitir que o usurio grave MIDI sintetizado atravs do teclado eletrnico. O mtodo getMidiDeviceInfo de MidiSystem est disponvel para obter as informaes sobre todos os dispositivos MIDI detectveis (array de objetos MidiDevice.Info). Use o mtodo getMidiDevice de MidiSystem com um argumento MidiDevice.Info para obter o recurso do dispositivo MIDI especificado. 22.26 Melhore o programa MidiDemo para permitir mais configuraes, como tempo, controle e repetio. Sequencers podem implementar MetaEventListeners para tratar de MetaMessages e ControlEventListeners para tratar comandos ShortMessage.CONTROL_CHANGE (consulte a especificao de MIDI para os tipos de mudanas e os diversos MetaEvents). A interface Sequencer tambm oferece muitos mtodos de configurao e controle que afetam a reproduo no seqenciador. 22.27 Melhore o programa RTPServer (Fig. 22.3 e Fig. 22.4) para um servidor de distribuio de vdeo em que uma entrada de vdeo ao vivo de um dispositivo de captura de vdeo (por exemplo, uma placa VFW TV, cmeras digitais) transmitida para todos na rede. Use o endereo IP terminando com .255 (i.e., para criar um SessionAddress) para transmitir para todos na sub-rede da rede. Um programa servidor deve ter acesso ao estado da transmisso do stream, aos controles de erros e ao gerenciamento de retardo. O pacote javax.media.rtp fornece muitas interfaces para tratar da configurao de streams e estatsticas. O pacote javax.media.rtcp permite acessar relatrios sobre a sesso de RTP. O pacote javax.media.rtp.event contm muitos eventos gerados durante uma sesso de RTP que podem ser usados para executar melhorias em RTP naqueles estgios da sesso. O pacote javax.media.control fornece diversas interfaces de controle teis em sesses de RTP. 22.28 Melhore a soluo do aplicativo reprodutor de mdia do Exerccio 3.18 adicionando recursos de edio ao programa. Primeiro adicione uma caixa de marcao para repetio. Embora um Processor seja mais adequado para tarefas de controle do que um Player, tambm se pode usar as duas interfaces com um Player para a DataSource de sada do Processor. Os recursos de controle devem incluir formatao de trilha, ajuste de controle de frames (interface FrameRateControl), controle de buffer (interface BufferControl) e controle de qualidade (interface QualityControl). Inclua uma opo de programa que permita salvar clipes de mdia dados os ajustes destes controles. Para dispositivos de captura, existe uma interface PortControl disponvel para controlar suas portas de dispositivos. Estas interfaces esto no pacote javax.media.control. No pacote javax.media, existem outras interfaces, como Codec e Effect, que permitem a gerao e o processamento adicionais da mdia para formatos de mdia especficos. Permita ao usurio importar novos codecs. Implemente tambm um recurso de edio que habilite o usurio a extrair certas partes de um clipe de mdia (Dica: ajuste a posio da mdia de um clipe de mdia e obtenha a sada do Processor nas posies marcadas). 22.29 O pacote javax.media.sound oferece muitos recursos de udio do sistema. Use este pacote para criar um programa de captura de som que permita salvar o stream de captura nos formatos, as taxas de bits, as freqncias e codificaes desejadas. 22.30 Crie um estdio de visualizao que exiba barras grficas que se sincronizem com a reproduo de amostra de udio. 22.31 Estenda o suporte reproduo de MP3 para a classe ClipPlayer (Fig. 22.5) usando as classes do pacote javax.media.sound.spi. O processo de codificao de MP3 usa o algoritmo de Huffman. 22.32 (Contador de histrias) Grave em udio um grande nmero de substantivos, verbos, artigos, preposies, etc. Depois utilize gerao de nmeros aleatrios para formar frases e fazer seu programa pronunciar as frases. 22.33 (Projeto: sistema de autoria multimdia) Desenvolva um sistema de autoria multimdia de uso geral. O programa deve permitir que o usurio componha apresentaes em multimdia que consistam em texto, udio, imagens, animaes e at vdeo. O programa deixa o usurio compor uma apresentao que consiste em qualquer um desses elementos de multimdia que so selecionados de um catlogo que o programa exibe. Fornea controles para permitir que o usurio personalize dinamicamente a apresentao enquanto ela feita.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1205

22.34 (Vdeo games) Os vdeo games tornaram-se incrivelmente populares. Desenvolva seu prprio programa Java de vdeo game. Faa uma competio com seus colegas para desenvolver o melhor vdeo game original. 22.35 (Demo de fsica: bola que salta) Desenvolva um programa animado que mostre uma bola que salta. Atribua uma velocidade horizontal constante bola. Permita que o usurio especifique o coeficiente de restituio, por exemplo, um coeficiente de restituio de 75% significa que, depois que a bola bate no cho, a ela retorna somente 75% da sua altura atingida antes de ser rebatida. A demonstrao deve levar em conta o efeito da gravidade isso far com que a bola trace um caminho parablico. Capture um som boing (como uma mola saltando) e reproduza o som toda vez que a bola atinge o cho. 22.36 (Demo de fsica: cintica) Se voc estudou fsica, implemente um programa em Java que demonstrar conceitos como energia, inrcia, quantidade de movimento (ou momentum), velocidade, acelerao, atrito, coeficiente de restituio, gravidade e outros. Crie efeitos visuais e utilize udio para nfase e realismo. 22.37 (Projeto: simulador de vo) Desenvolva seu prprio programa simulador de vo em Java. Este um projeto muito desafiador. Tambm um excelente candidato para uma competio com seus colegas. 22.38 (Torres de Hani) Escreva uma verso animada do problema das Torres de Hani que apresentamos no Exerccio 6.37. medida que cada disco retirado de um pino ou encaixado sobre outro, reproduza um som de atrito de metal. Quando cada disco pousar sobre a pilha, emita um som de choque de metal. Reproduza alguma msica de fundo apropriada. 22.39 (A lebre e a tartaruga) Desenvolva uma verso multimdia da simulao da lebre e da tartaruga que apresentamos no Exerccio 7.41. Voc poderia gravar voz de um apresentador descrevendo a corrida, Os competidores esto na linha de partida., Foi dada a largada!, A lebre dispara na frente., A tartaruga est avanando com determinao., etc. medida que a corrida prossegue, reproduza os udios gravados apropriados. Reproduza sons para simular a corrida dos animais e no esquea dos aplausos da torcida! Faa uma animao dos animais correndo para cima da montanha escorregadia. 22.40 (Percorrendo o passeio do cavalo) Desenvolva verses baseadas em multimdia dos programas do passeio do cavalo que voc escreveu nos Exerccios 7.22 e 7.23. 22.41 (Mquina de fliperama) Eis outro problema de competio. Desenvolva um programa Java que simula uma mquina de fliperama que voc prprio projetou. Realize uma competio com seus colegas para desenvolver a melhor mquina de fliperama multimdia original. Utilize todos os truques possveis de multimdia que voc possa imaginar para incrementar seu jogo de fliperama. Tente manter os mecanismos do jogo semelhantes queles dos jogos reais de fliperama. 22.42 (Roleta) Estude as regras para o jogo de roleta e implemente uma verso do jogo baseada em multimdia. Crie uma roleta giratria animada. Utilize udio para simular o som da bola pulando os vrios compartimentos que correspondem a cada um dos nmeros. Utilize um udio para simular o som da bola caindo em sua posio final. Enquanto a roleta est girando, permita que vrios jogadores faam apostas. Quando a bola cair na posio final, voc deve atualizar os saldos de cada um dos jogadores na banca com ganhos ou perdas adequadas. 22.43 (Jogo de Craps) Simule um jogo de craps completo. Utilize uma representao grfica de uma mesa de jogo de craps. Permita que vrios jogadores faam suas apostas. Utilize uma animao do jogador que est lanando os dados e mostre os dados animados rolando at parar. Utilize udio para simular um pouco da conversa em volta da mesa do jogo de craps. Aps cada lanamento, o sistema deve atualizar os saldos de cada um dos jogadores na banca de acordo com as apostas que eles fizeram. 22.44 (Cdigo Morse) Modifique a soluo do Exerccio 10.26 para gerar como sada o cdigo Morse com clipes de udio. Utilize dois clipes de udio diferentes para os caracteres de ponto e trao no cdigo Morse.