Chapter 22 – Java Media Framework and Java Sound

Download Report

Transcript Chapter 22 – Java Media Framework and Java Sound

Chapter 22 – Java Media Framework
and Java Sound
Outline
22.1
22.2
22.3
22.4
22.5
22.6
22.7
22.9
Introduction
Playing Media
Formatting and Saving Captured Media
RTP Streaming
Java Sound
Playing Sampled Audio
Musical Instrument Digital Interface (MIDI)
22.7.1 MIDI Playback
22.7.2 MIDI Recording
22.7.3 MIDI Synthesis
22.7.4 Class MidiDemo
(Optional Case Study) Thinking About Objects: Animation
and Sound in the View
 2002 Prentice Hall, Inc. All rights reserved.
22.1 Introduction
• Java Media Framework (JMF) API
– Play, edit, stream and capture many popular media formats
– Latest version is JMF 2.1.1
 2002 Prentice Hall, Inc. All rights reserved.
22.2 Playing Media
• Playing a media clip
– An object that implements Player interface
• Specify media source
• Create a Player for the media
• Obtain output media and Player controls
• Display the media and controls
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.1: SimplePlayer.java
// Opens and plays a media file from
// local computer, public URL, or an RTP session
// Java core packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
// Java extension packages
import javax.swing.*;
import javax.media.*;
public class SimplePlayer extends JFrame {
// Java media player
private Player player;
// visual content component
private Component visualMedia;
// controls component for media
private Component mediaControl;
Import
JMF
Outline
extension packages
Fig. 22.1 Playing
Declare Player
media with interface
object to play media
Player.
files
Line 13
Declare visual
content
Line
18 component
Declare controls
Line 21
component
Line 24
Lines
30-31
Declare
media file
and media locations
// main container
private Container container;
// media file and media locations
private File mediaFile;
private URL fileURL;
 2002 Prentice Hall, Inc.
All rights reserved.
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
// constructor for SimplePlayer
public SimplePlayer()
{
super( "Simple Java Media Player" );
container = getContentPane();
// panel containing buttons
JPanel buttonPanel = new JPanel();
container.add( buttonPanel, BorderLayout.NORTH );
// opening file from directory button
JButton openFile = new JButton( "Open File" );
buttonPanel.add( openFile );
// register an ActionListener for openFile events
openFile.addActionListener(
// anonymous inner class to handle openFile events
new ActionListener() {
// open and create player for file
public void actionPerformed( ActionEvent event )
{
mediaFile = getFile();
Outline
Fig. 22.1 Playing
ActionListener
media
with interface
for openFile
Player
(Part 2).
events opens file and
creates52-78
player for file
Lines
When
Line
57 user clicks
button, call method
getFile
which
Line
63
prompts user to select
a file
Call method toURL
to get a URL
reference to the file
if ( mediaFile != null ) {
// obtain URL from file
try {
fileURL = mediaFile.toURL();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// file path unresolvable
catch ( MalformedURLException badURL ) {
badURL.printStackTrace();
showErrorMessage( "Bad URL" );
}
makePlayer( fileURL.toString() );
}
}
}
// end actionPerformed
// end ActionListener
); // end call to method addActionListener
// URL opening button
JButton openURL = new JButton( "Open Locator" );
buttonPanel.add( openURL );
// register an ActionListener for openURL events
openURL.addActionListener(
// anonymous inner class to handle openURL events
new ActionListener() {
// open and create player for media locator
public void actionPerformed( ActionEvent event )
{
String addressName = getMediaLocation();
Call method
Outline
makePlayer to
create a player for the
Fig. 22.1file
Playing
media with interface
Player
(Part 3).
ActionListener
for openURL events
Line
72 and creates
opens
player for media
Lines 90-101
locator
Line
95 method
Calling
getMediaLocation
Line 98
prompts
user for a string
for location of media
Call method
makePlayer to
create a player for
media locator
if ( addressName != null )
makePlayer( addressName );
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
}
// end ActionListener
); // end call to method addActionListener
// turn on lightweight rendering on players to enable
// better compatibility with lightweight GUI components
Manager.setHint( Manager.LIGHTWEIGHT_RENDERER,
Boolean.TRUE );
}
// end SimplePlayer constructor
// utility method for pop-up error messages
public void showErrorMessage( String error )
{
JOptionPane.showMessageDialog( this, error, "Error",
JOptionPane.ERROR_MESSAGE );
}
Outline
Use lightweight
rendering on players
Fig. 22.1 Playing
media with interface
Player
4).
Method (Part
getFile
gets a file from the
Lines computer
107-108
Lines 120-134
// get file from computer
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();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// get media location from user input
public String getMediaLocation()
{
String input = JOptionPane.showInputDialog(
this, "Enter URL" );
// if user presses OK with no input
if ( input != null && input.length() == 0 )
return null;
return input;
}
// create player using media's location
public void makePlayer( String mediaLocation )
{
// reset player and window if previous player exists
if ( player != null )
removePlayerComponents();
// location of media source
MediaLocator mediaLocator =
new MediaLocator( mediaLocation );
Method
Outline
getMediaLocation
gets media location
from
userPlaying
input
Fig.
22.1
media with interface
Method
Player
(Part 5).
makePlayer
creates137-147
a Player for
Lines
a media clip
Lines 150-187
Invoked method
removePlayerComponents
Line 154
to remove previous player
Lines 157-158
Create new
MediaLocator for
media source
if ( mediaLocator == null ) {
showErrorMessage( "Error opening file" );
return;
}
// create a player from MediaLocator
try {
player = Manager.createPlayer( mediaLocator );
 2002 Prentice Hall, Inc.
All rights reserved.
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
// register ControllerListener to handle Player events
player.addControllerListener(
new PlayerEventHandler() );
// call realize to enable rendering of player's media
player.realize();
}
// no player exists or format is unsupported
catch ( NoPlayerException noPlayerException ) {
noPlayerException.printStackTrace();
}
// file input error
catch ( IOException ioException ) {
ioException.printStackTrace();
}
Register
Outline
ControllerListener
to handle Player events
Fig. 22.1 Playing
Invoke
method
media
with
interface
realize
to enable
Player
(Part
6).
realization of media
Lines 170-171
Method
Line 174
removePlayerComponents
clears player GUI and reset
Lines 191-203
media resources
and controls
Line 202
}
// end makePlayer method
// return player to system resources and
// reset media and controls
public void removePlayerComponents()
{
// remove previous video component if there is one
if ( visualMedia != null )
container.remove( visualMedia );
Invoke method
close to stop player
and return allocated
resources
// remove previous media control if there is one
if ( mediaControl != null )
container.remove( mediaControl );
// stop player and return allocated resources
player.close();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Method
Outline
getMediaComponents
// obtain visual media and player controls
public void getMediaComponents()
gets visual media and
{
player
Fig.
22.1controls
Playing
// get visual component from player
visualMedia = player.getVisualComponent();
media with interface
Invoke method
Player
(Part 7).
// add visual component if present
getVisualComponent
if ( visualMedia != null )
to get
visual
component
container.add( visualMedia, BorderLayout.CENTER );
Lines
206-222
from player
// get player control GUI
Line 209
mediaControl = player.getControlPanelComponent();
Method
// add controls component if present
getControlPanelComponent
Line 216
if ( mediaControl != null )
returns player control GUI
container.add( mediaControl, BorderLayout.SOUTH );
Lines
225-255
Inner
class
}
// end method getMediaComponents
Invoke method
// handler for player's ControllerEvents
getMediaComponents
private class PlayerEventHandler extends ControllerAdapter {
to show GUI
// prefetch media feed once player is realized
public void realizeComplete(
Method
RealizeCompleteEvent realizeDoneEvent
)
prefetchComplete
{
displays player GUI
player.prefetch();
}
controls after media is
realized
// player can start showing media after prefetching
public void prefetchComplete(
PrefetchCompleteEvent prefetchDoneEvent )
{
getMediaComponents();
PlayerEventHandler
Lines
228-232
handles
player’s
ControllerEvents
Lines 235-246
Method
realizeComplete
Line 238
invokes method
prefetchComplete when
RealizeCompleteEvent
generated
 2002 Prentice Hall, Inc.
All rights reserved.
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
// ensure valid layout of frame
validate();
// start playing media
player.start();
}
// end prefetchComplete method
// if end of media, reset to beginning, stop play
public void endOfMedia( EndOfMediaEvent mediaEndEvent )
{
player.setMediaTime( new Time( 0 ) );
player.stop();
}
}
// end PlayerEventHandler inner class
// execute application
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 );
Invoke
method
Outline
validate to ensure
proper frame layout
Fig. 22.1 Playing
media
with
interface
Invoke
method
Player
8).
start to(Part
play media
Line 241
Method
endOfMedia resets
Line
244
media
to beginning
when
EndOfMediaEvent
Lines 249-253
event generated
Line 251
Invoke method
setMediaTime
to
Line
252
set time to 0
Invoke method stop
to stop player
}
}
// end class SimplePlayer
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.1 Playing
media with interface
Player (Part 9).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.1 Playing
media with interface
Player (Part 10).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.1 Playing
media with interface
Player (Part 11).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.1 Playing
media with interface
Player (Part 12).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
22.3 Formatting and Saving Captured
Media
• Capture devices
– Microphones and video cameras
– JMF converts analog signal to digital media
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.2: CapturePlayer.java
// Presents and saves captured media
// Java core packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
// Java extension packages
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 {
// capture and save button
private JButton captureButton;
// component for save capture GUI
private Component saveProgress;
// formats of device's media, user-chosen format
private Format formats[], selectedFormat;
// controls of device's media formats
private FormatControl formatControls[];
// specification information of device
private CaptureDeviceInfo deviceInfo;
Import
JMF
Outline
extension packages
for media control and
CapturePlayer.ja
device formatting
va
Import JMF package
Lines
for13-16
outputting
formatted data
Line 17
Array formats
contains
Line
28 references to
all Formats
by a
Linesupported
31
capture device
Line 34
Array
formatControls
contains controls for
each format
supported by device
Object
deviceInfo
contains information
about capture device
 2002 Prentice Hall, Inc.
All rights reserved.
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
// vector containing all devices' information
private Vector deviceList;
// input and output data sources
private DataSource inSource, outSource;
// file writer for captured media
private DataSink dataSink;
// processor to render and save captured media
private Processor processor;
// constructor for CapturePlayer
public CapturePlayer()
{
super( "Capture Player" );
// panel containing buttons
JPanel buttonPanel = new JPanel();
getContentPane().add( buttonPanel );
// button for accessing and initializing capture devices
captureButton = new JButton( "Capture and Save File" );
buttonPanel.add( captureButton, BorderLayout.CENTER );
// register an ActionListener for captureButton events
captureButton.addActionListener( new CaptureHandler() );
// turn on light rendering to enable compatibility
// with lightweight GUI components
Manager.setHint( Manager.LIGHTWEIGHT_RENDERER,
Boolean.TRUE );
// register a WindowListener to frame events
addWindowListener(
Outline
CapturePlayer.ja
vaDeclare data input
and output objects
inSource
and
Line
40
outSource
Line 43
Object dataSink
Linewrites
46 captured
media to a file
Line
62processor
Object
controls and
processes flow of
media data
Register
ActionListener
for
captureButton
events
 2002 Prentice Hall, Inc.
All rights reserved.
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
// anonymous inner class to handle WindowEvents
new WindowAdapter() {
// dispose processor
public void windowClosing(
WindowEvent windowEvent )
{
if ( processor != null )
processor.close();
}
}
// end WindowAdapter
); // end call to method addWindowListener
}
// end constructor
// action handler class for setting up device
private class CaptureHandler implements ActionListener {
// initialize and configure capture device
public void actionPerformed( ActionEvent actionEvent )
{
// put available devices' information into vector
deviceList =
CaptureDeviceManager.getDeviceList( null );
Outline
CapturePlayer.ja
va Inner class
CaptureHandler
Lines
90-173
sets
up device
Method
Lines 93-171
actionPerformed
Lines
96-97 and
initializes
configures capture
device
Method
getDeviceList
returns a complete
list of available
capture devices
// if no devices found, display error message
if ( ( deviceList == null ) ||
( deviceList.size() == 0 ) ) {
showErrorMessage( "No capture devices found!" );
return;
 2002 Prentice Hall, Inc.
All rights reserved.
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
}
// array of device names
String deviceNames[] = new String[ deviceList.size() ];
// store all device names into array of
// string for display purposes
for ( int i = 0; i < deviceList.size(); i++ ){
deviceInfo =
( CaptureDeviceInfo ) deviceList.elementAt(
Call method
Lines 109-119
getSelectedDeviceIndex
to get vector index of selected
i );
Lines
122-123
device
deviceNames[ i ] = deviceInfo.getName();
}
// get vector index of selected device
int selectDeviceIndex =
getSelectedDeviceIndex( deviceNames );
if ( selectDeviceIndex == -1 )
return;
// get device information of selected device
deviceInfo = ( CaptureDeviceInfo )
deviceList.elementAt( selectDeviceIndex );
formats = deviceInfo.getFormats();
// if previous capture device opened, disconnect it
if ( inSource != null )
inSource.disconnect();
// obtain device and set its format
try {
Copy names
of all
Outline
capture devices into a
String array for
CapturePlayer.ja
display purposes
va
device
LinesGet
129-130
information of
Lineselected
132 device
method
LinesCall
135-136
getFormats to
display format
information
Call method
disconnect if
previous capture
device open
 2002 Prentice Hall, Inc.
All rights reserved.
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
// create data source from MediaLocator of device
inSource = Manager.createDataSource(
deviceInfo.getLocator() );
// get format setting controls for device
formatControls = ( ( CaptureDevice )
inSource ).getFormatControls();
// get user's desired device format setting
selectedFormat = getSelectedFormat( formats );
if ( selectedFormat == null )
return;
setDeviceFormat( selectedFormat );
captureSaveFile();
}
// end try
// unable to find DataSource from MediaLocator
catch ( NoDataSourceException noDataException ) {
noDataException.printStackTrace();
}
// device connection error
catch ( IOException ioException ) {
ioException.printStackTrace();
}
}
}
Outline
CapturePlayer.ja
vaCreate data source
from
MediaLocator of
Lines 142-143
device
Lines 146-147
Call method
getFormatControls
Line 150
to get format settings
controls
Line 157for device
Call method
getSelectedFormat
to get user’s device
format setting
Call method
captureSaveFile
to save captured data
in a file
// end method actionPerformed
// end inner class CaptureHandler
 2002 Prentice Hall, Inc.
All rights reserved.
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
// set output format of device-captured media
public void setDeviceFormat( Format currentFormat )
{
// set desired format through all format controls
for ( int i = 0; i < formatControls.length; i++ ) {
// make sure format control is configurable
if ( formatControls[ i ].isEnabled() ) {
formatControls[ i ].setFormat( currentFormat );
System.out.println (
"Presentation output format currently set as
formatControls[ i ].getFormat() );
}
}
Outline
CapturePlayer.ja
va
Method
setDeviceFormat
sets output
format of
Lines
176-192
captured media
Lines 194-210
" +
Method
Lines
197-201
getSelectedDeviceIndex
gets selected device vector index
// end for loop
}
// get selected device vector index
public int getSelectedDeviceIndex( String[] names )
{
// get device name from dialog box of device choices
String name = ( String ) JOptionPane.showInputDialog(
this, "Select a device:", "Device Selection",
JOptionPane.QUESTION_MESSAGE,
null, names, names[ 0 ] );
Call method
showInputDialog
to display dialog box
of device choices
// if format selected, get index of name in array names
if ( name != null )
return Arrays.binarySearch( names, name );
// else return bad selection value
else
return -1;
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
}
// return user-selected format for device
public Format getSelectedFormat( Format[] showFormats )
{
return ( Format ) JOptionPane.showInputDialog( this,
"Select a format: ", "Format Selection",
JOptionPane.QUESTION_MESSAGE,
null, showFormats, null );
}
// pop up error messages
public void showErrorMessage( String error )
{
JOptionPane.showMessageDialog( this, error, "Error",
JOptionPane.ERROR_MESSAGE );
}
// get desired file for saved captured media
public File getSaveFile()
{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(
JFileChooser.FILES_ONLY );
int result = fileChooser.showSaveDialog( this );
CapturePlayer.ja
va Method
getSelectedFormat
returns
user-selected
Lines 213-219
format for device
Lines 221-226
Method
Lines 229-242
getSaveFile gets
chosen file for saved
captured media
Method
getSaveFile gets
desired file for saved
captured media
if ( result == JFileChooser.CANCEL_OPTION )
return null;
else
return fileChooser.getSelectedFile();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// show saving monitor of captured media
public void showSaveMonitor()
{
// show saving monitor dialog
int result = JOptionPane.showConfirmDialog( this,
saveProgress, "Save capture in progress...",
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE );
// terminate saving if user presses "OK" or closes dialog
if ( ( result == JOptionPane.OK_OPTION ) ||
( result == JOptionPane.CLOSED_OPTION ) ) {
processor.stop();
processor.close();
System.out.println ( "Capture closed." );
Outline
CapturePlayer.ja
va
Method
showSaveMonitor
shows
a saving
Lines
245-262
monitor for captured
media
Lines 254-261
Terminate
save if
Lines
265-322
user presses OK or
closes
Line
268dialog box
}
}
// process captured media and save to file
public void captureSaveFile()
{
// array of desired saving formats supported by tracks
Create a new
Format outFormats[] = new Format[ 1 ];
outFormats[ 0 ] = selectedFormat;
descriptor in
Quicktime format
// file output format
FileTypeDescriptor outFileType =
new FileTypeDescriptor( FileTypeDescriptor.QUICKTIME );
// set up and start processor and monitor capture
try {
Method
Line 270
captureSaveFile
processes
captured
Lines
273-274
media and saves it to
file
Create array of
possible formats of
each track of media
Set default format to
first element of array
 2002 Prentice Hall, Inc.
All rights reserved.
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
// create processor from processor model
// of specific data source, track output formats,
// and file output format
processor = Manager.createRealizedProcessor(
new ProcessorModel( inSource, outFormats,
outFileType ) );
// try to make a data writer for media output
if ( !makeDataWriter() )
return;
// call start on processor to start captured feed
processor.start();
// get monitor control for capturing and encoding
MonitorControl monitorControl =
( MonitorControl ) processor.getControl(
"javax.media.control.MonitorControl" );
// get GUI component of monitoring control
saveProgress = monitorControl.getControlComponent();
showSaveMonitor();
}
Outline
CapturePlayer.ja
vaInstantiate a new
Processor with
specific
data source,
Lines
282-284
track output formats,
and file
Line
287output format
Invoke
method
Lines
294-296
makeDataWriter
to create a
Line 299
DataSink object
that301
can save file
Line
Get monitor control
for capturing and
encoding
// end try
// no processor could be found for specific
// data source
catch ( NoProcessorException processorException ) {
processorException.printStackTrace();
Call method
}
showSaveMonitor
to display save
monitor dialog
Invoke method
getControlComponents
to get GUI component of
monitoring controls
 2002 Prentice Hall, Inc.
All rights reserved.
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
// unable to realize through
// createRealizedProcessor method
catch ( CannotRealizeException realizeException ) {
realizeException.printStackTrace();
}
// device connection error
catch ( IOException ioException ) {
ioException.printStackTrace();
}
}
// end method captureSaveFile
// method initializing media file writer
public boolean makeDataWriter()
{
File saveFile = getSaveFile();
if ( saveFile == null )
return false;
// get output data source from processor
outSource = processor.getDataOutput();
if ( outSource == null ) {
showErrorMessage( "No output from processor!" );
return false;
}
Outline
CapturePlayer.ja
Method
va
makeDataWriter
initializes
media file
Lines
325-399
writer
Lines
327-330
Invoke
method
getSaveFile to
Line
get 333
File object to
save to
Lines 344-345
Invoke method
getDataOutput
to get output data
source
Create new
MediaLocator for
saveFile URL
// start data writing process
try {
// create new MediaLocator from saveFile URL
MediaLocator saveLocator =
new MediaLocator ( saveFile.toURL() );
 2002 Prentice Hall, Inc.
All rights reserved.
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
// create DataSink from output data source
// and user-specified save destination file
dataSink = Manager.createDataSink(
outSource, saveLocator );
// register a DataSinkListener for DataSinkEvents
dataSink.addDataSinkListener(
// anonymous inner class to handle DataSinkEvents
new DataSinkListener () {
// if end of media, close data writer
public void dataSinkUpdate(
DataSinkEvent dataEvent )
{
// if capturing stopped, close DataSink
if ( dataEvent instanceof EndOfStreamEvent )
dataSink.close();
}
}
// end DataSinkListener
); // end call to method addDataSinkListener
// start saving
dataSink.open();
dataSink.start();
}
Outline
CapturePlayer.ja
Create dataSink
va
object from output
data source
and save
Lines
349-350
file
LinesRegister
353-369
a
DataSinkListener
359-365
forLines
DataSinkEvents
Lines 372-373
Method
dataSinkUpdate
called when
DataSinkEvent
occurs
Open dataSink
and save file
// end try
 2002 Prentice Hall, Inc.
All rights reserved.
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
// DataSink could not be found for specific
// save file and data source
catch ( NoDataSinkException noDataSinkException ) {
noDataSinkException.printStackTrace();
return false;
}
Outline
CapturePlayer.ja
va
// violation while accessing MediaLocator
// destination
catch ( SecurityException securityException ) {
securityException.printStackTrace();
return false;
}
// problem opening and starting DataSink
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
return true;
}
// end method makeDataWriter
// main method
public static void main( String args[] )
{
CapturePlayer testPlayer = new CapturePlayer();
 2002 Prentice Hall, Inc.
All rights reserved.
406
407
408
409
410
411
412
testPlayer.setSize( 200, 70 );
testPlayer.setLocation( 300, 300 );
testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE );
testPlayer.setVisible( true );
}
}
Outline
CapturePlayer.ja
va
// end class CapturePlayer
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
CapturePlayer.ja
va
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
22.4 RTP Streaming
• Streaming media
– Transfer data in a continuous stream of bytes
– Allows client to view part of media while rest downloads
• JMF streaming media package
– Uses Real-Time Transfer Protocol (RTF)
• Industry standard for streaming media
• Designed specifically for real-time media data
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.3: RTPServer.java
// Provides configuration and sending capabilities
// for RTP-supported media files
// Java core packages
import java.io.*;
import java.net.*;
Outline
RTPServer.java
// Java extension packages
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.*;
import javax.media.rtp.*;
import javax.media.format.*;
public class RTPServer {
// IP address, file or medialocator name, port number
private String ipAddress, fileName;
private int port;
// processor controlling data flow
private Processor processor;
// data output from processor to be sent
private DataSource outSource;
// media tracks' configurable controls
private TrackControl tracks[];
// RTP session manager
private RTPManager rtpManager[];
 2002 Prentice Hall, Inc.
All rights reserved.
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
// constructor for RTPServer
public RTPServer( String locator, String ip, int portNumber )
{
fileName = locator;
port = portNumber;
ipAddress = ip;
}
// initialize and set up processor
// return true if successful, false if not
public boolean beginSession()
{
// get MediaLocator from specific location
MediaLocator mediaLocator = new MediaLocator( fileName );
if ( mediaLocator == null ) {
System.err.println(
"No MediaLocator found for " + fileName );
return false;
}
// create processor from MediaLocator
try {
processor = Manager.createProcessor( mediaLocator );
// register a ControllerListener for processor
// to listen for state and transition events
Invoke method
processor.addControllerListener(
new ProcessorEventHandler() );configure to place
processor in
configuring state
System.out.println( "Processor configuring..." );
// configure processor before setting it up
processor.configure();
Outline
RTPServer.java
Constructor takes
media
location, IP
Lines
35-40
address and port
number
as arguments
Lines
44-86
Line 47Method
beginSession
sets up Processor
Line 58
that controls data
flow
Lines 62-63
Initialize
Line
68
mediaLocator
with fileName
Create processor
for data specified by
mediaLocator
Register a
ControllerListener
to listen for state and
transition
events
 2002 Prentice
Hall, Inc.
All rights reserved.
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
Outline
}
// source connection error
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
// exception thrown when no processor could
// be found for specific data source
catch ( NoProcessorException noProcessorException ) {
noProcessorException.printStackTrace();
return false;
}
return true;
}
// end method beginSession
// ControllerListener handler for processor
private class ProcessorEventHandler
extends ControllerAdapter {
// set output format and realize
// configured processor
public void configureComplete(
ConfigureCompleteEvent configureCompleteEvent )
{
System.out.println( "\nProcessor configured." );
RTPServer.java
Private class
ProcessEventHandler
Lines 89-127
controls media setup as
processor
changes states
Lines 94-104
Method
Line
99
configureComplete
invoked
when
Line 103
ConfigureCompleteEvent
occurs
Invoke method
setOutputFormat
to set output format
Invoke method
realize to realize
processor
setOutputFormat();
System.out.println( "\nRealizing Processor...\n" );
processor.realize();
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
}
// start sending when processor is realized
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." );
}
// stop RTP session when there is no media to send
public void endOfMedia( EndOfMediaEvent mediaEndEvent )
{
stopTransmission();
System.out.println ( "Transmission completed." );
}
}
// end inner class ProcessorEventHandler
// set output format of all tracks in media
public void setOutputFormat()
{
// set output content type to RTP capable format
processor.setContentDescriptor(
new ContentDescriptor( ContentDescriptor.RAW_RTP ) );
// get all track controls of processor
tracks = processor.getTrackControls();
RTPServer.java
Method
realizeComplete
Lines 107-118
invoked when
RealizeCompleteEvent
Lines 130-175
occurs
Lines 133-134
Method
setOutputFormat
Line 137
sets output format of
all tracks in media
Invoke method
setContentDescriptor
on processor object
Invoke method
getTrackControls
to get controls for
processor
 2002 Prentice Hall, Inc.
All rights reserved.
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
// supported RTP formats of a track
Format rtpFormats[];
// set each track to first supported RTP format
// found in that track
for ( int i = 0; i < tracks.length; i++ ) {
System.out.println( "\nTrack #" +
( i + 1 ) +
" supports " );
Outline
RTPServer.java
Set each track to first
supported
RTP
Lines
144-173
format found in that
track
if ( tracks[ i ].isEnabled() ) {
rtpFormats = tracks[ i ].getSupportedFormats();
//
//
//
if
if supported formats of track exist,
display all supported RTP formats and set
track format to be first supported format
( 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!" );
}
}
// end if
// end for loop
 2002 Prentice Hall, Inc.
All rights reserved.
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
}
Outline
// end method setOutputFormat
// send media with boolean success value
public boolean transmitMedia()
{
outSource = processor.getDataOutput();
if ( outSource == null ) {
System.out.println ( "No data source from media!" );
return false;
}
// rtp stream managers for each track
rtpManager = new RTPManager[ tracks.length ];
// destination and local RTP session addresses
SessionAddress localAddress, remoteAddress;
// RTP stream being sent
SendStream sendStream;
// IP address
InetAddress ip;
The try block sends
out each track as an
RTP stream
// initialize transmission addresses and send out media
Invoke method
try {
// transmit every track in media
for ( int i = 0; i < tracks.length;
newInstance to
instantiate a
i++ ) {
RTPManager
// instantiate a RTPManager
rtpManager[ i ] = RTPManager.newInstance();
RTPServer.java
Method
transmitMedia
Lines 178-271
creates structures
needed
Line
180 to transmit
media
Line 189
Obtain
DataSource from
Line 192
processor
LineCreate
195 array of
RTPManagers to
Lines
201-248
control
sessions
Line 207
Declare destination and
local RTP
SessionAddresses
Object
sendStream
performs the RTP
streaming
 2002 Prentice Hall, Inc.
All rights reserved.
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
// add 2 to specify next control port number;
// (RTP Session Manager uses 2 ports)
port += ( 2 * i );
// get IP address of host from ipAddress string
ip = InetAddress.getByName( ipAddress );
// encapsulate pair of IP addresses for control and
// data with 2 ports into local session address
localAddress = new SessionAddress(
ip.getLocalHost(), port );
// get remoteAddress session address
remoteAddress = new SessionAddress( ip, port );
// initialize the session
rtpManager[ i ].initialize( localAddress );
// open RTP session for destination
rtpManager[ i ].addTarget( remoteAddress );
System.out.println( "\nStarted RTP session: "
+ ipAddress + " " + port);
// create send stream in RTP session
sendStream =
rtpManager[ i ].createSendStream( outSource, i );
// start sending the stream
sendStream.start();
System.out.println( "Transmitting Track #" +
( i + 1 ) + " ... " );
}
// end for loop
Outline
Increment
port
number variable
RTPServer.java
Instantiate a new
localAddress
Line 211
Instantiate client
Lines 218-219
session address
LineInvoke
222 method
initialize to
Line
225 session
initialize
LineInvoke
228 method
addTarget to open
session
LinesRTP
234-235
Invoke
Line
238 method
createSendStream
to create RTP send
stream
Invoke method
start to start
sending the stream
 2002 Prentice Hall, Inc.
All rights reserved.
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
// start media feed
processor.start();
}
// end try
// unknown local or unresolvable remote address
catch ( InvalidSessionAddressException addressError ) {
addressError.printStackTrace();
return false;
}
Invoke
start
Outline
method to start media
feed
RTPServer.java
Line 246
Lines 274-303
// DataSource connection error
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
// format not set or invalid format set on stream source
catch ( UnsupportedFormatException formatException ) {
formatException.printStackTrace();
return false;
}
// transmission initialized successfully
return true;
}
Method
stopTransmission
stops and closes
Processor
// end method transmitMedia
// stop transmission and close resources
public void stopTransmission()
{
if ( processor != null ) {
 2002 Prentice Hall, Inc.
All rights reserved.
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
// stop processor
processor.stop();
// dispose processor
processor.close();
if ( rtpManager != null )
// close destination targets
// and dispose RTP managers
for ( int i = 0; i < rtpManager.length; i++ ) {
// close streams to all destinations
// with a reason for termination
rtpManager[ i ].removeTargets(
"Session stopped." );
Stop and
dispose of
Outline
processor for
media
RTPServer.java
Invoke method
removeTargets
Lines
279-282
to close streams to a
target
Lines 292-293
LineInvoke
296 method
dispose to release
RTP session
resources
// release RTP session resources
rtpManager[ i ].dispose();
}
}
// end if
System.out.println ( "Transmission stopped." );
}
}
// end method stopTransmission
// end class RTPServer
 2002 Prentice Hall, Inc.
All rights reserved.
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
// Fig. 22.4: RTPServerTest.java
// Test class for RTPServer
// Java core packages
import java.awt.event.*;
import java.io.*;
import java.net.*;
// Java extension packages
import javax.swing.*;
public class RTPServerTest extends JFrame {
Outline
RTPServerTest1.j
IP addresses and port
ava
numbers
Lines 18-20
Lines 26-67
// object handling RTP streaming
private RTPServer rtpServer;
// media sources and destination locations
private int port;
private String ip, mediaLocation;
private File mediaFile;
// GUI buttons
private JButton transmitFileButton, transmitUrlButton;
// constructor for RTPServerTest
public RTPServerTest()
{
super( "RTP Server Test" );
Constructor sets up
GUI
// register a WindowListener for frame events
addWindowListener(
// anonymous inner class to handle WindowEvents
new WindowAdapter() {
 2002 Prentice Hall, Inc.
All rights reserved.
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
public void windowClosing(
WindowEvent windowEvent )
{
if ( rtpServer != null )
rtpServer.stopTransmission();
}
}
Outline
RTPServerTest1.j
ava
// end WindowAdpater
); // end call to method addWindowListener
// panel containing button GUI
JPanel buttonPanel = new JPanel();
getContentPane().add( buttonPanel );
// transmit file button GUI
transmitFileButton = new JButton( "Transmit File" );
buttonPanel.add( transmitFileButton );
// register ActionListener for transmitFileButton events
transmitFileButton.addActionListener(
new ButtonHandler() );
// transmit URL button GUI
transmitUrlButton = new JButton( "Transmit Media" );
buttonPanel.add( transmitUrlButton );
// register ActionListener for transmitURLButton events
transmitUrlButton.addActionListener(
new ButtonHandler() );
}
// end constructor
 2002 Prentice Hall, Inc.
All rights reserved.
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
// inner class handles transmission button events
private class ButtonHandler implements ActionListener {
// open and try to send file to user-input destination
public void actionPerformed( ActionEvent actionEvent )
{
// if transmitFileButton invoked, get file URL string
if ( actionEvent.getSource() == transmitFileButton ) {
Private class
Outline
ButtonHandler
handles button events
RTPServerTest1.j
ava
Lines 70-129
mediaFile = getFile();
if ( mediaFile != null )
// obtain URL string from file
try {
mediaLocation = mediaFile.toURL().toString();
}
// file path unresolvable
catch ( MalformedURLException badURL ) {
badURL.printStackTrace();
}
else
return;
}
// end if
// else transmitMediaButton invoked, get location
else
mediaLocation = getMediaLocation();
if ( mediaLocation == null )
return;
 2002 Prentice Hall, Inc.
All rights reserved.
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
// get IP address
ip = getIP();
if ( ip == null )
return;
// get port number
port = getPort();
Outline
RTPServerTest1.j
ava
Lines 132-146
// check for valid positive port number and input
if ( port <= 0 ) {
if ( port != -999 )
System.err.println( "Invalid port number!" );
return;
}
// instantiate new RTP streaming server
rtpServer = new RTPServer( mediaLocation, ip, port );
rtpServer.beginSession();
}
}
// end method actionPeformed
Method getFile
gets the file from the
computer
// end inner class ButtonHandler
// get file from computer
public File getFile()
{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(
JFileChooser.FILES_ONLY );
 2002 Prentice Hall, Inc.
All rights reserved.
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
int result = fileChooser.showOpenDialog( this );
if ( result == JFileChooser.CANCEL_OPTION )
return null;
else
return fileChooser.getSelectedFile();
}
Method
Outline
getMediaLocation
gets media location
from user
RTPServerTest1.j
ava
Lines 149-161
// get media location from user
public String getMediaLocation()
{
String input = JOptionPane.showInputDialog(
this, "Enter MediaLocator" );
// if user presses OK with no input
if ( input != null && input.length() == 0 ) {
System.err.println( "No input!" );
return null;
}
Lines 164-176
Method getIP gets
IP address string
form user
return input;
}
// method getting IP string from user
public String getIP()
{
String input = JOptionPane.showInputDialog(
this, "Enter IP Address: " );
// if user presses OK with no input
if ( input != null && input.length() == 0 ) {
System.err.println( "No input!" );
return null;
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
return input;
}
// get port number
public int getPort()
{
String input = JOptionPane.showInputDialog(
this, "Enter Port Number: " );
Method
getPort
Outline
gets port number
from user
RTPServerTest1.j
ava
Lines 179-197
// return flag value if user clicks OK with no input
if ( input != null && input.length() == 0 ) {
System.err.println( "No input!" );
return -999;
}
// return flag value if user clicked CANCEL
if ( input == null )
return -999;
// else return input
return Integer.parseInt( input );
}
// end method getPort
// execute application
public static void main( String args[] )
{
RTPServerTest serverTest = new RTPServerTest();
serverTest.setSize( 250, 70 );
serverTest.setLocation( 300, 300 );
serverTest.setDefaultCloseOperation( EXIT_ON_CLOSE );
serverTest.setVisible( true );
}
 2002 Prentice Hall, Inc.
All rights reserved.
209
210
}
// end class RTPServerTest
Outline
RTPServerTest1.j
ava
Program output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
RTPServerTest1.j
ava
Program output
 2002 Prentice Hall, Inc.
All rights reserved.
22.5 Java Sound
• Sound common in today’s applications
• Java Sound API
– Allows sound to be incorporated into Java applications
 2002 Prentice Hall, Inc. All rights reserved.
22.6 Playing Sampled Audio
• Introduces javax.sound.sampled package
– Plays popular music formats
– Provides a line through which audio data flows
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.5: ClipPlayer.java
// Plays sound clip files of type WAV, AU, AIFF
Outline
Implements
LineListener
// Java core packages
import java.io.*;
ClipPlayer.java
// Java extension packages
import javax.sound.sampled.*;
Line 10
public class ClipPlayer implements LineListener {
Lines 25-28
// audio input stream
private AudioInputStream soundStream;
// audio sample clip line
private Clip clip;
// Audio clip file
private File soundFile;
// boolean indicating replay of audio
private boolean replay = false;
// constructor for ClipPlayer
public ClipPlayer( File audioFile )
{
soundFile = audioFile;
}
Constructor takes an
audio file as an
argument
 2002 Prentice Hall, Inc.
All rights reserved.
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
// open music file, returning true if successful
public boolean openFile()
{
// get audio stream from file
try {
soundStream =
AudioSystem.getAudioInputStream( soundFile );
}
// audio file not supported by JavaSound
catch ( UnsupportedAudioFileException audioException ) {
audioException.printStackTrace();
return false;
}
// I/O error attempting to get stream
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
// invoke loadClip, returning true if load successful
return loadClip();
}
// end method openFile
MethodOutline
openFile
opens a music file
ClipPlayer.java
Invoke
method
getAudioInputStream
Linesstream
31-54from file
to get audio
Lines
35-36
Invoke
method
loadClip and
Line return
52 true if
successful
Lines 57-106
Method loadClip
Line
63 sound clip
loads
Invoke method
getFormat to get
audio format of
sound file
// load sound clip
public boolean loadClip ()
{
// get clip line for file
try {
// get audio format of sound file
AudioFormat audioFormat = soundStream.getFormat();
 2002 Prentice Hall, Inc.
All rights reserved.
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
// define line information based on line type,
// encoding and frame sizes of audio file
DataLine.Info dataLineInfo = new DataLine.Info(
Clip.class, AudioSystem.getTargetFormats(
AudioFormat.Encoding.PCM_SIGNED, audioFormat ),
audioFormat.getFrameSize(),
audioFormat.getFrameSize() * 2 );
// make sure sound system supports data line
if ( !AudioSystem.isLineSupported( dataLineInfo ) ) {
System.err.println( "Unsupported Clip File!" );
return false;
}
// get clip line resource
clip = ( Clip ) AudioSystem.getLine( dataLineInfo );
// listen to clip line for events
clip.addLineListener( this );
// open audio clip and get required system resources
clip.open( soundStream );
}
// end try
// line resource unavailable
catch ( LineUnavailableException noLineException ) {
noLineException.printStackTrace();
return false;
}
Invoke method
open
to open audio clip
Outline
ClipPlayer.java
Invoke method
getTargetFormats
Lines 68-69
to get formats for audio
Line 70 file
LineInvoke
74 method
getFrameSize to
get size
Line
81 of frame of
audio file
Line 84
Make sure sound
system
Line
87 supports data
line
Invoke method
getLine to get clip
line resource
Register a
LineListener to
listen for clip line
events
 2002 Prentice Hall, Inc.
All rights reserved.
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
// I/O error during interpretation of audio data
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
// clip file loaded successfully
return true;
}
// end method loadClip
// start playback of audio clip
public void play()
{
clip.start();
}
// line event listener method to stop or replay at clip end
public void update( LineEvent lineEvent )
{
// if clip reaches end, close clip
if ( lineEvent.getType() == LineEvent.Type.STOP &&
!replay )
close();
Outline
ClipPlayer.java
Method play starts
audio
playback
Lines
109-112
Method
update
Lines
115-133
listens for line events
Line 120
Invoke method
close if clip
Line 131
reaches end
Invoke method loop
to play clip forever
// if replay set, replay forever
else
if ( lineEvent.getType() == LineEvent.Type.STOP &&
replay ) {
System.out.println( "replay" );
// replay clip forever
clip.loop( Clip.LOOP_CONTINUOUSLY );
 2002 Prentice Hall, Inc.
All rights reserved.
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
}
}
// set replay of clip
public void setReplay( boolean value )
{
replay = value;
}
Outline
ClipPlayer.java
Method close stops
clip 142-148
and recovers
Lines
resources
// stop and close clip, returning system resources
public void close()
{
if ( clip != null ) {
clip.stop();
clip.close();
}
}
}
// end class ClipPlayer
 2002 Prentice Hall, Inc.
All rights reserved.
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
// Fig. 22.6: ClipPlayerTest.java
// Test file for ClipPlayer
// Java core packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
Outline
ClipPlayerTest.j
ava
// Java extension packages
import javax.swing.*;
public class ClipPlayerTest extends JFrame {
// object to play audio clips
private ClipPlayer clipPlayer;
// constructor for ClipPlayerTest
public ClipPlayerTest()
{
super( "Clip Player" );
// panel containing buttons
JPanel buttonPanel = new JPanel();
getContentPane().add( buttonPanel );
// open file button
JButton openFile = new JButton( "Open Audio Clip" );
buttonPanel.add( openFile, BorderLayout.CENTER );
// register ActionListener for openFile events
openFile.addActionListener(
// inner anonymous class to handle openFile ActionEvent
new ActionListener() {
 2002 Prentice Hall, Inc.
All rights reserved.
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
// try to open and play an audio clip file
public void actionPerformed( ActionEvent event )
{
File mediaFile = getFile();
if ( mediaFile != null ) {
// instantiate new clip player with mediaFile
clipPlayer = new ClipPlayer( mediaFile );
// if clip player opened correctly
if ( clipPlayer.openFile() == true ) {
// play loaded clip
clipPlayer.play();
// no replay
clipPlayer.setReplay( false );
}
}
}
}
// end if mediaFile
// end actionPerformed
// end ActionListener
); // end call to addActionListener
}
// end constructor
// get file from computer
public File getFile()
{
JFileChooser fileChooser = new JFileChooser();
Method
Outline
actionPerformed
prompts user to select
a file to play
ClipPlayerTest.j
ava
Invoke method
getFile to prompt
Lines 37-58
user to select an
audio file
Line 39
Instantiate new clip
Line 44
player with
mediaFile
Line 47
Invoke method
Line
50
openFile
to open
the media file
Line 53
Invoke method play
to play media clip
Invoke method
setReplay to set
no replay
 2002 Prentice Hall, Inc.
All rights reserved.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
fileChooser.setFileSelectionMode(
JFileChooser.FILES_ONLY );
int result = fileChooser.showOpenDialog( this );
if ( result == JFileChooser.CANCEL_OPTION )
return null;
Outline
ClipPlayerTest.j
ava
else
return fileChooser.getSelectedFile();
}
// execute application
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 );
}
}
// end class ClipPlayerTest
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
ClipPlayer.java
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
22.7 Musical Instrument Digital Interface
(MIDI)
• MIDI
– Standard format for digital music
– Can be created via digital instrument
– MIDI synthesizer
• Device that produces MIDI sounds and music
• Interpretation of MIDI data differs among synthesizers
 2002 Prentice Hall, Inc. All rights reserved.
22.7.1 MIDI Playback
• Interpreting MIDI file contents
– MIDI data often referred to as a sequence
• MIDI composed as a sequence of events
• Playing MIDI files
– Three step process
• Accessing sequencer
• Loading MIDI sequence
• Starting sequence
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.7: MidiData.java
// Contains MIDI sequence information
// with accessor methods and MIDI playback methods
// Java core package
import java.io.*;
// Java extension package
import javax.sound.midi.*;
Outline
Fig. 22.7 MidiData
loads MIDI files for
playback.
Line 17
public class MidiData {
// MIDI track data
private Track track;
// player for MIDI sequences
private Sequencer sequencer;
// MIDI sequence
private Sequence sequence;
Line 20
MIDI Sequencer
MIDI Sequence
// MIDI events containing time and MidiMessages
private MidiEvent currentEvent, nextEvent;
// MIDI message usually containing sounding messages
private ShortMessage noteMessage;
// short, meta, or sysex MIDI messages
private MidiMessage message;
// index of MIDI event in track, command in MIDI message
private int eventIndex = 0, command;
 2002 Prentice Hall, Inc.
All rights reserved.
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
// method to play MIDI sequence via sequencer
public void play()
{
// initiate default sequencer
try {
// get sequencer from MidiSystem
sequencer = MidiSystem.getSequencer();
// open sequencer resources
sequencer.open();
// load MIDI into sequencer
sequencer.setSequence( sequence );
// play sequence
sequencer.start();
}
Outline
Fig. 22.7 MidiData
loads MIDI files for
playback (Part 2).
Obtain Sequencer
to play Sequence
Line 41
Open Sequencer
Line 44
Load Sequence into Sequencer
Line 47
Begin playing MIDI Sequence
Line 50
// MIDI resource availability error
catch ( MidiUnavailableException noMidiException ) {
noMidiException.printStackTrace();
}
Lines 54-56
Exception thrown if program is
59-62 object
using sameLines
Sequencer
// corrupted MIDI or invalid MIDI file encountered
catch ( InvalidMidiDataException badMidiException ) {
badMidiException.printStackTrace();
}
}
Exception thrown if Sequencer
detect unrecognizable Sequence
// end method play
 2002 Prentice Hall, Inc.
All rights reserved.
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
// method returning adjusted tempo/resolution of MIDI
public int getResolution()
{
return 500 / sequence.getResolution();
}
// obtain MIDI and prepare track in MIDI to be accessed
public boolean initialize( File file )
{
// get valid MIDI from file into sequence
try {
sequence = MidiSystem.getSequence( file );
}
Outline
Fig. 22.7 MidiData
loads MIDI files for
playback (Part 3).
Line 77
Obtain Sequence from file
// unreadable MIDI file or unsupported MIDI
catch ( InvalidMidiDataException badMIDI ) {
badMIDI.printStackTrace();
return false;
}
// I/O error generated during file reading
catch ( IOException ioException ) {
ioException.printStackTrace();
return false;
}
return true;
}
// end method initialize
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// prepare longest track to be read and get first MIDI event
public boolean initializeTrack()
{
// get all tracks from sequence
22.7 Sequence
MidiData
Track tracks[] = sequence.getTracks();
Obtain all TracksFig.
in MIDI
if ( tracks.length == 0 ) {
System.err.println( "No tracks in MIDI sequence!" );
loads MIDI files for
playback (Part 4).
Line 100
return false;
}
Determine longest
Track in
Lines 108-114
MIDI and set it as the one to play
Line 117
track = tracks[ 0 ];
// find longest track
for ( int i = 0; i < tracks.length; i++ )
Line 120
if ( tracks[ i ].size() > track.size() )
track = tracks[ i ];
// set current MIDI event to first event in track
currentEvent = track.get( eventIndex );
// get MIDI message from event
message = currentEvent.getMessage();
Obtain first MIDI event in Track
Obtain MidiMessage from MIDI event
// track initialization successful
return true;
}
// end method initializeTrack
 2002 Prentice Hall, Inc.
All rights reserved.
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
// move to next event in track
public void goNextEvent()
Traverse
{
eventIndex++;
currentEvent = track.get( eventIndex );
message = currentEvent.getMessage();
}
each event in the Tracks
// get time interval between events
public int getEventDelay()
{
// first event's time interval is its duration
if ( eventIndex == 0 )
return ( int ) currentEvent.getTick();
Outline
Fig. 22.7 MidiData
loads MIDI files for
Return first MidiEvent’s
time stamp as playback
event’s (Part 5).
duration
Lines
128-133
Return duration
of MidiEvent
as the time difference between
two events inLines
MIDI139-140
sequence
// time difference between current and next event
return ( int ) ( track.get( eventIndex + 1 ).getTick() currentEvent.getTick() );
Lines 143-144
Lines 148-155
}
// return if track has ended
public boolean isTrackEnd()
Provide
{
// if eventIndex is less than track's number of events
if ( eventIndex + 1 < track.size() )
return false;
Lines 158-168
indication of end of a
track
return true;
}
// get current ShortMessage command from event
public int getEventCommand()
{
if ( message instanceof ShortMessage ) {
Determine command number
representing command
instruction
 2002 Prentice Hall, Inc.
All rights reserved.
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
Outline
// obtain MidiMessage for accessing purposes
noteMessage = ( ShortMessage ) message;
return noteMessage.getCommand();
}
return -1;
}
// get note number of current event
public int getNote()
{
if ( noteMessage != null )
return noteMessage.getData1();
Fig. 22.7 MidiData
loads MIDI files for
playback (Part 5).
171-176
Obtain number of noteLines
for current
event
Lines 180-183
return -1;
}
// get volume of current event
public int getVolume()
{
return noteMessage.getData2();
}
}
Return
volume
// end class MidiData
 2002 Prentice Hall, Inc.
All rights reserved.
22.7.2 MIDI Recording
• MIDI Recording
– Transmitter sends MIDI messages to MIDI device
• MIDI device class implements interface Receiver
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.8: MidiRecord.java
// Allows for recording and playback
// of synthesized MIDI
// Java core packages
import java.io.*;
// Java extension package
import javax.sound.midi.*;
Outline
Fig. 22.8
MidiRecord
enables a program
to record a MIDI
sequence.
public class MidiRecord {
// MIDI track
private Track track;
// MIDI sequencer to play and access music
private Sequencer sequencer;
// MIDI sequence
private Sequence sequence;
// receiver of MIDI events
private Receiver receiver;
// transmitter for transmitting MIDI messages
private Transmitter transmitter;
Line 17
Line 20
MIDI Sequencer
Lines 23-26
MIDI Sequence
Transmitter will send MIDI
messages to Receiver
// constructor for MidiRecord
public MidiRecord( Transmitter transmit )
{
transmitter = transmit;
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Set up Outline
sequencer for
recording
Fig. 22.8
MidiRecord
beat
enables a program
to record a MIDI
Instantiates empty sequence
sequence
(Partto2).
(MidiRecord
records data
// initialize recording sequencer, set up recording sequence
public boolean initialize()
{
// create empty MIDI sequence and set up sequencer wiring
try {
// create tempo-based sequence of 10 pulses per
sequence = new Sequence( Sequence.PPQ, 10 );
// obtain sequencer and open it
sequencer = MidiSystem.getSequencer();
sequencer.open();
// get receiver of sequencer
receiver = sequencer.getReceiver();
if ( receiver == null ) {
System.err.println(
"Receiver unavailable for sequencer" );
return false;
}
this sequence when the
Lines 35-77
transmitter connects to
receiver)
Line 41
Obtain recording
sequencer’s Receiver
Lines 48-57
and specify that
Transmitter will send its
messages to Receiver
// set receiver for transmitter to send MidiMessages
transmitter.setReceiver( receiver );
makeTrack();
}
// invalid timing division specification for new sequence
catch ( InvalidMidiDataException invalidMidiException ) {
invalidMidiException.printStackTrace();
return false;
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// sequencer or receiver unavailable
catch ( MidiUnavailableException noMidiException ) {
noMidiException.printStackTrace();
return false;
}
// MIDI recorder initialization successful
return true;
}
// end method initialize
// make new empty track for sequence
public void makeTrack()
{
// if previous track exists, delete it first
if ( track != null )
sequence.deleteTrack( track );
Outline
Fig. 22.8
MidiRecord
enables a program
to record a MIDI
sequence (Part 3).
Delete previous
Lines existing
80-88
Track and create empty
Track
Lines 97-115
Line 101
// create track in sequence
track = sequence.createTrack();
}
// start playback of loaded sequence
public void play()
{
sequencer.start();
}
// start recording into sequence
public void startRecord()
{
// load sequence into recorder and start recording
try {
sequencer.setSequence( sequence );
Start recording
process
Load empty Sequence
into Sequencer
 2002 Prentice Hall, Inc.
All rights reserved.
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
// set track to recording-enabled and default channel
sequencer.recordEnable( track, 0 );
sequencer.startRecording();
}
// sequence contains bad MIDI data
catch ( InvalidMidiDataException badMidiException ) {
badMidiException.printStackTrace();
Outline
Enable recording on
Track
Start recording
Fig. 22.8
of MIDI
MidiRecord
events sent
from
Transmitter
enables a program
to record a MIDI
sequence (Part 4).
}
}
Line 104
// end method startRecord
// stop MIDI recording
public void stopRecord()
{
sequencer.stopRecording();
}
Line 106
Line
127file types
Obtain array of
MIDI
supported by the system for
Line 136to files
writing Sequences
// save MIDI sequence to file
public void saveSequence( File file )
{
// get all MIDI supported file types
int[] fileTypes = MidiSystem.getMidiFileTypes( sequence );
if ( fileTypes.length == 0 ) {
System.err.println( "No supported MIDI file format!" );
return;
}
// write recorded sequence into MIDI file
try {
MidiSystem.write( sequence, fileTypes[ 0 ], file );
}
Write Sequence
to specified
File
 2002 Prentice Hall, Inc.
All rights reserved.
138
139
140
141
142
143
144
145
146
// error writing to file
catch ( IOException ioException ) {
ioException.printStackTrace();
}
}
}
// end method saveSequence
// end class MidiRecord
Outline
Fig. 22.8
MidiRecord
enables a program
to record a MIDI
sequence (Part 5).
 2002 Prentice Hall, Inc.
All rights reserved.
22.7.3 MIDI Synthesis
• Interface Synthesizer
– Sub-interface of MidiDevice
– Accesses default synthesizer’s:
• Sound generation
• Instruments
– instructs computer on how to make sound of specific
note
• Channel resources
– MidiChannel plays different notes made by
Instruments
• Sound banks.
– Container for various Instruments
 2002 Prentice Hall, Inc. All rights reserved.
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
// Fig. 22.9: MidiSynthesizer.java
// Accessing synthesizer resources
// Java extension package
import javax.sound.midi.*;
public class MidiSynthesizer {
// main synthesizer accesses resources
private Synthesizer synthesizer;
// available instruments for synthesis use
private Instrument instruments[];
Outline
Fig. 22.9
MidiSynthesizer
can generate notes
and send them to
another MIDI
device.
Lines 29-72
// channels through which notes sound
private MidiChannel channels[];
private MidiChannel channel;
// current channel
Line 34
// transmitter for transmitting messages
private Transmitter transmitter;
// receiver end of messages
private Receiver receiver;
// short message containing sound commands, note, volume
private ShortMessage message;
// constructor for MidiSynthesizer
public MidiSynthesizer()
{
// open synthesizer, set receiver,
// obtain channels and instruments
try {
synthesizer = MidiSystem.getSynthesizer();
Acquire Synthesizer
and initialize related
resources
Obtain Synthesizer
 2002 Prentice Hall, Inc.
All rights reserved.
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
if ( synthesizer != null ) {
Outline
synthesizer.open();
Open
Fig. 22.9
Synthesizer
// get transmitter of synthesizer
MidiSynthesizer
transmitter = synthesizer.getTransmitter();
Obtain Transmitter
can generate notes
if ( transmitter == null )
and Receiver of the
and send them to
System.err.println( "Transmitter unavailable" );
Synthesizer to
another MIDI device
enable
sounds to be
// get receiver of synthesizer
(Part 2).
receiver = synthesizer.getReceiver();
played and recorded
simultaneously
if ( receiver == null )
Line 38
System.out.println( "Receiver unavailable" );
Obtain all available
Instruments
from
Lines
41-47
// get all available instruments in default
Synthesizer
// soundbank or synthesizer
instruments = synthesizer.getAvailableInstruments();
Line 54
// get all 16 channels from synthesizer
channels = synthesizer.getChannels();
// assign first channel as default channel
channel = channels[ 0 ];
Obtain all 16
Line 57
channels from
Synthesizer
}
else
System.err.println( "No Synthesizer" );
}
// synthesizer, receiver or transmitter unavailable
catch ( MidiUnavailableException noMidiException ) {
noMidiException.printStackTrace();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
}
// return available instruments
public Instrument[] getInstruments()
{
return instruments;
}
// return synthesizer's transmitter
public Transmitter getTransmitter()
{
return transmitter;
}
// sound note on through channel
public void midiNoteOn( int note, int volume )
{
channel.noteOn( note, volume );
}
// sound note off through channel
public void midiNoteOff( int note )
{
channel.noteOff( note );
}
// change to selected instrument
public void changeInstrument( int index )
{
Patch patch = instruments[ index ].getPatch();
channel.programChange( patch.getBank(),
patch.getProgram() );
}
Outline
// end constructor
Fig. 22.9
MidiSynthesizer
can generate notes
and send them to
another MIDI device
(Part 3).
Lines 87-90
Lines 93-96
Sound
note
Lines 99-105
Sound note
off
Allow for changing
from default
Instrument
 2002 Prentice Hall, Inc.
All rights reserved.
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// send custom MIDI messages through transmitter
public void sendMessage( int command, int note, int volume )
{
// send a MIDI ShortMessage using this method's parameters
try {
message = new ShortMessage();
Outline
// set new message of command (NOTE_ON, NOTE_OFF),
// note number, volume
message.setMessage( command, note, volume );
Fig. 22.9
MidiSynthesizer
can generate notes
and send them to
another MIDI device
(Part 4).
// send message through receiver
receiver.send( message, -1 );
Lines 112-119
}
// invalid message values set
catch ( InvalidMidiDataException badMidiException ) {
badMidiException.printStackTrace();
}
}
}
// end method sendMessage
// end class MidiSynthesizer
Create ShortMessage from parameters
of method sendMessage and send
message to Synthesizer’s Receiver
 2002 Prentice Hall, Inc.
All rights reserved.
22.7.4 Class MidiDemo
• MidiDemo (Fig. 22.10)
– GUI-driven “piano player” application
 2002 Prentice Hall, Inc. All rights reserved.
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
//
//
//
//
Fig. 22.10: MidiDemo.java
Simulates a musical keyboard with various
instruments to play, also featuring recording, MIDI file
playback and simulating MIDI playback with the keyboard
// Java core packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
// Java extension packages
import javax.swing.*;
import javax.swing.event.*;
import javax.sound.midi.*;
public class MidiDemo extends JFrame {
// recording MIDI data
private MidiRecord midiRecord;
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application.
Line 16
Create GUI-driven MIDI demo
application
// synthesize MIDI functioning
private MidiSynthesizer midiSynthesizer;
// MIDI data in MIDI file
private MidiData midiData;
// timer for simulating MIDI on piano
private Timer pianoTimer;
// piano keys
private JButton noteButton[];
// volume, tempo sliders
private JSlider volumeSlider, resolutionSlider;
 2002 Prentice Hall, Inc.
All rights reserved.
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
// containers and panels holding GUI
private Container container;
private JPanel controlPanel, buttonPanel;
// instrument selector and buttons GUI
private JComboBox instrumentBox;
private JButton playButton, recordButton,
saveButton, pianoPlayerButton, listenButton;
// tempo, last piano key invoked, volume of MIDI
private int resolution, lastKeyOn = -1, midiVolume = 40;
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application (Part 2).
// boolean value indicating if program is in recording mode
private boolean recording = false;
// first note number of first piano key, max number of keys
private static int FIRST_NOTE = 32, MAX_KEYS = 64;
// constructor for MidiDemo
public MidiDemo()
{
super( "MIDI Demo" );
container = getContentPane();
container.setLayout( new BorderLayout() );
// synthesizer must be instantiated to enable synthesis
midiSynthesizer = new MidiSynthesizer();
// make piano keys
makeKeys();
// add control panel to frame
controlPanel = new JPanel( new BorderLayout() );
container.add( controlPanel, BorderLayout.NORTH );
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
makeConfigureControls();
// add button panel to frame
buttonPanel = new JPanel( new GridLayout( 5, 1 ) );
controlPanel.add( buttonPanel, BorderLayout.EAST );
// make GUI
makePlaySaveButtons();
makeRecordButton();
makePianoPlayerButton();
}
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application (Part 3).
// end constructor
Lines 86-93
Create 64 JButtons that represent 64
different piano keys (when mouse hovers
over a key, program sounds designated
note)
// utility method making piano keys
private void makeKeys()
{
// panel containing keys
JPanel keyPanel = new JPanel( null );
container.add( keyPanel, BorderLayout.CENTER );
// piano keys
noteButton = new JButton[ MAX_KEYS ];
// add MAX_KEYS buttons and what note they sound
for ( int i = 0; i < MAX_KEYS; i++ ) {
final int note = i;
noteButton[ i ] = new JButton();
// setting white keys
noteButton[ i ].setBackground( Color.white );
 2002 Prentice Hall, Inc.
All rights reserved.
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
// set correct spacing for buttons
noteButton[ i ].setBounds( ( i * 11 ), 1, 11, 40 );
keyPanel.add( noteButton[ i ] );
// register a mouse listener for mouse events
noteButton[ i ].addMouseListener(
// anonymous inner class to handle mouse events
new MouseAdapter() {
Outline
Register
Fig. 22.10 for each
MouseListeners
MidiDemo
provides
piano-key
JButton
Method
mouseEntered
the
GUI that
is invoked
when
theto
enables
users
mouseinteract
hovers over
with each
the
JButton (Part 4).
application
// invoke key note when mouse touches key
public void mouseEntered( MouseEvent mouseEvent )
{
// if recording, send message to receiver
if ( recording )
midiSynthesizer.sendMessage(
ShortMessage.NOTE_ON,
note + FIRST_NOTE, midiVolume );
// else just sound the note
else
midiSynthesizer.midiNoteOn(
note + FIRST_NOTE, midiVolume );
// turn key color to blue
noteButton[ note ].setBackground(
Color.blue );
}
Line 110
If program is in
recording mode, access
Lines 116-132
channels in
MidiSynthesizer to
Lines 119-122
sound note
If program is not in
Lines 125-127
recording mode,
send a note
Lines 130-131
message to
Synthesizer and
to recording device
Set JButton’s background color to
blue, indicating that note is being
played.
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// turn key note off when mouse leaves key
public void mouseExited( MouseEvent mouseEvent )
{
if ( recording )
Method Fig.
mouseExited
is
22.10
midiSynthesizer.sendMessage(
invoked MidiDemo
when mouse
is no
provides
ShortMessage.NOTE_OFF,
longerthe
hovering
over
note + FIRST_NOTE, midiVolume );
GUI that
else
JButtonusers to
enables
midiSynthesizer.midiNoteOff(
note + FIRST_NOTE );
interact with the
noteButton[ note ].setBackground(
Color.white );
Lines 135-147
}
}
application (Part 5).
// end MouseAdapter
Lines 158-237
); // end call to addMouseListener
}
}
// end for loop
// end method makeKeys
// set up configuration controls
private void makeConfigureControls()
{
JPanel configurePanel =
new JPanel( new GridLayout( 5, 1 ) );
Setup MIDI controls,Lines
which165-166
consist of
instrument selector JComboBox, usersynthesis volume changer JSlider and
“piano player” tempo changer JSlider
controlPanel.add( configurePanel, BorderLayout.WEST );
instrumentBox = new JComboBox(
midiSynthesizer.getInstruments() );
configurePanel.add( instrumentBox );
Instantiate instrument
selector JComboBox
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
actionPerformed is
invoked when user selects
Fig. 22.10
// anonymous inner class to handle instrument selector instrument
MidiDemo provides
new ActionListener() {
the GUI that
// change current instrument program
enables users to
public void actionPerformed( ActionEvent event )
{
interact with the
// change instrument in synthesizer
changeapplication
to selected (Part 6).
// register an ActionListener for instrumentBox Method
events
instrumentBox.addActionListener(
midiSynthesizer.changeInstrument(
instrumentBox.getSelectedIndex() );
instrument
Lines 177-182
program
}
}
// end ActionListener
Lines 180-181
); // end call to method addActionListener
Lines 191-192
JLabel volumeLabel = new JLabel( "volume" );
configurePanel.add( volumeLabel );
volumeSlider = new JSlider(
SwingConstants.HORIZONTAL, 5, 80, 30 );
Instantiate user-synthesis
volume changer JSlider
// register a ChangeListener for slider change events
volumeSlider.addChangeListener(
// anonymous inner class to handle volume slider events
new ChangeListener() {
 2002 Prentice Hall, Inc.
All rights reserved.
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
// change volume
public void stateChanged( ChangeEvent changeEvent )
{
midiVolume = volumeSlider.getValue();
}
}
// end class ChangeListener
); // end call to method addChangeListener
configurePanel.add( volumeSlider );
JLabel tempLabel = new JLabel( "tempo" );
configurePanel.add( tempLabel );
resolutionSlider = new JSlider(
SwingConstants.HORIZONTAL, 1, 10, 1 );
// register a ChangeListener slider for change events
resolutionSlider.addChangeListener(
Outline
Change volume when user
accesses
Fig. 22.10
JSlider
MidiDemo provides
the GUI that
enables users to
interact with the
application (Part 7).
Lines 201-204
Instantiate
player”
Lines “piano
215-216
tempo changer JSlider
Lines 225-228
// anonymous inner class to handle tempo slider events
new ChangeListener() {
// change resolution if value changed
public void stateChanged( ChangeEvent changeEvent )
{
resolution = resolutionSlider.getValue();
}
}
Set tempo when user
accesses JSlider
// end ChangeListener
); // end call to method addChangeListener
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
resolutionSlider.setEnabled( false );
configurePanel.add( resolutionSlider );
}
// end method makeConfigureControls
// set up play and save buttons
private void makePlaySaveButtons()
{
playButton = new JButton( "Playback" );
// register an ActionListener for playButton
playButton.addActionListener(
Fig. 22.10
Setup Playback, MidiDemo
Play MIDI provides
the GUI that
and Save buttons.
enables users to
interact with the
events
application (Part 8).
// anonymous inner class to handle playButton event
new ActionListener() {
// playback last recorded MIDI
public void actionPerformed( ActionEvent event )
{
if ( midiRecord != null )
midiRecord.play();
}
}
// end ActionListener
Lines 240-320
Lines 251-255
When user presses
Playback button,
play recorded
MIDI
); // end call to method addActionListener
buttonPanel.add( playButton );
playButton.setEnabled( false );
listenButton = new JButton( "Play MIDI" );
// register an ActionListener for listenButton events
listenButton.addActionListener(
 2002 Prentice Hall, Inc.
All rights reserved.
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
// anonymous inner class to handle listenButton events
new ActionListener() {
// playback MIDI file
public void actionPerformed( ActionEvent event )
{
File midiFile = getFile();
if ( midiFile == null )
return;
midiData = new MidiData();
// prepare MIDI track
if ( midiData.initialize( midiFile ) == false )
return;
Outline
When user presses
Play MIDI button,
Fig.class
22.10
use
MidiData
MidiDemo
provides
to playback
an
the GUIMIDI
that file in
opened
enables
users to
its entirety
interact
with the
Get MIDI
file specified
by
(Part 9).
user,application
then use class
MidiData to play MIDI file
Lines 273-288
Lines 275-287
// play MIDI data
midiData.play();
}
}
// end ActionListener
); // end call to method addActionListener
buttonPanel.add( listenButton );
saveButton = new JButton( "Save MIDI" );
// register an ActionListener for saveButton events
saveButton.addActionListener(
// anonymous inner class to handle saveButton events
new ActionListener() {
 2002 Prentice Hall, Inc.
All rights reserved.
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
// get save file and save recorded MIDI
public void actionPerformed( ActionEvent event )
{
File saveFile = getSaveFile();
if ( saveFile != null )
midiRecord.saveSequence( saveFile );
}
}
// end ActionListener
); // end call to method addActionListener
buttonPanel.add( saveButton );
saveButton.setEnabled( false );
}
Outline
Fig. 22.10
WhenMidiDemo
user pressesprovides
Save
button,
thesave
GUI recorded
that
sequence
to file to
enables users
interact with the
application
(Part 10).
Lines 305-311
// end method makePlaySaveButtons
Line 323-325
// make recording button
private void makeRecordButton()
{
recordButton = new JButton( "Record" );
Create Record
Lines 334-379
button
// register an ActionListener for recordButton events
recordButton.addActionListener(
// anonymous inner class to handle recordButton events
new ActionListener() {
// start or stop recording
public void actionPerformed( ActionEvent event )
{
// record MIDI when button is "record" button
if ( recordButton.getText().equals("Record") ) {
Method actionPerformed invoked
when user presses
Record button
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
if ( midiRecord == null ) {
// create new instance of recorder
// by passing in synthesizer transmitter
midiRecord = new MidiRecord(
midiSynthesizer.getTransmitter() );
Fig.
22.10
Use
class
MidiDemo
provides
MidiRecord
to
the
GUI
that
create recorder
if ( midiRecord.initialize() == false )
enables users to
return;
}
interact with the
application
else
If a recorder has already
been
(Part
11).created,
midiRecord.makeTrack();
make new track for object midiRecord
midiRecord.startRecord();
Lines 343-344
// disable playback during recording
playButton.setEnabled( false );
// change recording button to stop
recordButton.setText( "Stop" );
recording = true;
}
When recording
turn the
Linesstarts,
350-351
Record button into a Stop
button and
disable
the Play
Lines
353-360
MIDI button temporarily.
Lines 365-373
// end if
// stop recording when button is "stop" button
else {
midiRecord.stopRecord();
recordButton.setText( "Record" );
recording = false;
playButton.setEnabled( true );
saveButton.setEnabled( true );
}
When users stop recording,
GUI maintains state prior to
that of recording (i.e., user can
playback and save MIDI
sequence)
 2002 Prentice Hall, Inc.
All rights reserved.
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
}
}
// end method actionPerformed
// end ActionListener
); // end call to method addActionListener
buttonPanel.add( recordButton );
} // end method makeRecordButton
// create Piano Player button and functionality
private void makePianoPlayerButton()
{
pianoPlayerButton = new JButton( "Piano Player" );
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
12).
Create(Part
Piano
Player
button
Lines 386-388
// register an ActionListener for pianoPlayerButton events
pianoPlayerButton.addActionListener(
Lines 397-428
Method
// anonymous inner class to handle pianoPlayerButton actionPerformed
Lines 399-408
new ActionListener() {
invoked when user presses
Piano Player button
// initialize MIDI data and piano player timer
public void actionPerformed( ActionEvent event )
{
File midiFile = getFile();
if ( midiFile == null )
return;
midiData = new MidiData();
// prepare MIDI track
if ( midiData.initialize( midiFile ) == false )
return;
Open file from file
dialog box and
load MIDI data
from file
 2002 Prentice Hall, Inc.
All rights reserved.
409
410
411
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
if ( midiData.initializeTrack() == false )
return;
// set initial resolution from MIDI
resolution = midiData.getResolution();
// new instance of timer for handling
// piano sounds and key pressing with tempo
pianoTimer = new Timer(
midiData.getEventDelay() * resolution,
new TimerHandler() );
listenButton.setEnabled( false );
pianoPlayerButton.setEnabled( false );
resolutionSlider.setEnabled( true );
pianoTimer.start();
}
}
// method end actionPerformed
Obtain longest Outline
track from
loaded MIDI and obtain first
MIDI event message from
Fig.
22.10
track.
MidiDemo provides
Obtain
the
GUIpiano
that
player’s
enablesdefault
users to
tempowith the
interact
application
(Part 13).
Instantiate Timer
thatLines
plays410-411
notes at
time of each MIDI
Line
414
event.
Lines 418-420
// end ActionListener
); // end call to method addActionListener
buttonPanel.add( pianoPlayerButton );
}
// end method makePianoPlayerButton
// inner class handles MIDI timed events
private class TimerHandler implements ActionListener {
// simulate key note of event if present, jump to next
// event in track and set next delay interval of timer
// method invoked when timer reaches next event time
 2002 Prentice Hall, Inc.
All rights reserved.
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
471
472
473
474
475
476
477
478
Outline
public void actionPerformed( ActionEvent actionEvent )
{
// if valid last key on, set it white
When pianoTimer reaches
if ( lastKeyOn != -1 )
Fig. 22.10
noteButton[ lastKeyOn ].setBackground(
next event,
change
MidiDemo provides
Color.white );
noteAction();
midiData.goNextEvent();
// stop piano player when end of MIDI track
if ( midiData.isTrackEnd() == true ) {
if ( lastKeyOn != -1 )
noteButton[ lastKeyOn ].setBackground(
Color.white );
pianoTimer.stop();
listenButton.setEnabled( true );
pianoPlayerButton.setEnabled( true );
resolutionSlider.setEnabled( false );
return;
}
}
Lines 457-459
Lines 472-473
// end if isTrackEnd
// set interval before next sounding event
pianoTimer.setDelay(
midiData.getEventDelay() * resolution );
}
background of last pressed
that
pianothe
keyGUI
to white
enables users to
Transition to next event in
interact with the
track
application
(Part 14).
When pianoTimer reaches
next Lines
event,447-449
change
background of last pressed
piano
key452
to white
Line
// end actionPerformed method
// end inner class TimerHandler
Reset Timer’s delay to
next MidiEvent’s
duration
 2002 Prentice Hall, Inc.
All rights reserved.
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
// determine which note to sound
// according to MIDI messages
private void noteAction()
{
// during Note On message, sound note and press key
if ( midiData.getEventCommand() ==
ShortMessage.NOTE_ON ) {
// make sure valid note is in range of keys
if ( ( midiData.getNote() >= FIRST_NOTE ) &&
( midiData.getNote() < FIRST_NOTE + MAX_KEYS ) ) {
lastKeyOn = midiData.getNote() - FIRST_NOTE;
// set key color to red
noteButton[ lastKeyOn ].setBackground( Color.red );
Outline
Sound note and
change color of
Fig.specific
22.10 piano
MidiDemo
keyprovides
the GUI that
enables users to
interact with the
application
(Part 15).
Lines 481-494
// send and sound note through synthesizer
midiSynthesizer.sendMessage( 144,
midiData.getNote(), midiData.getVolume() );
}
// end if
// else no last key pressed
else
lastKeyOn = -1;
}
// end if
// receiving Note Off message will sound off note
// and change key color back to white
else
 2002 Prentice Hall, Inc.
All rights reserved.
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
// if message command is note off
if ( midiData.getEventCommand() ==
ShortMessage.NOTE_OFF ) {
if ( ( midiData.getNote() >= FIRST_NOTE ) &&
( midiData.getNote() < FIRST_NOTE + MAX_KEYS ) ) {
// set appropriate key to white
noteButton[ midiData.getNote() FIRST_NOTE ].setBackground( Color.white );
// send note off message to receiver
midiSynthesizer.sendMessage( 128,
midiData.getNote(), midiData.getVolume() );
}
}
}
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables
users
Sound note
off to
interact
with
the
and
change
color
application
of
specific piano
(Part 16).
key
Lines 520-521
// end if
// end method noteAction
// get save file from computer
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();
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// get file from computer
public File getFile()
{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(
JFileChooser.FILES_ONLY );
int result = fileChooser.showOpenDialog( this );
if ( result == JFileChooser.CANCEL_OPTION )
return null;
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 17).
else
return fileChooser.getSelectedFile();
}
// execute application
public static void main( String args[] )
{
MidiDemo midiTest = new MidiDemo();
midiTest.setSize( 711, 225 );
midiTest.setDefaultCloseOperation ( EXIT_ON_CLOSE );
midiTest.setVisible( true );
}
}
// end class MidiDemo
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 18).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 19).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 20).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 21).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 22).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
Outline
Fig. 22.10
MidiDemo provides
the GUI that
enables users to
interact with the
application
(Part 23).
Program Output
 2002 Prentice Hall, Inc.
All rights reserved.
22.9 (Optional Case Study) Thinking About
Objects: Animation and Sound in the View
• ImagePanel
– Used for objects that are stationary in model
• e.g., Floor, ElevatorShaft
• MovingPanel
– Used for objects that “move” in model
• e.g., Elevator
• AnimatedPanel
– Used for objects that “animate” in model
• e.g., Person, Door, Button, Bell, Light
 2002 Prentice Hall, Inc. All rights reserved.
22.9 (Optional Case Study) Thinking About
Objects: Animation and Sound in the View
(cont.)
Ima gePa nel
1..*
1
1
M ovingPane l
Elev atorM usic
1
Ele va torView
a udio
1..* 1
1
Anima tedPanel
grap hic s
1
1..*
1
SoundEffec ts
view
Fig 22.11 Class diagram of elevator simulation view.
 2002 Prentice Hall, Inc. All rights reserved.
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
// ImagePanel.java
// JPanel subclass for positioning and displaying ImageIcon
package com.deitel.jhtp4.elevator.view;
// Java core packages
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// Java extension packages
import javax.swing.*;
public class ImagePanel extends JPanel {
// identifier
private int ID;
// on-screen position
private Point2D.Double position;
// imageIcon to paint on screen
private ImageIcon imageIcon;
Fig. 22.12 Class
ImagePanel extends
ImagePanel
JPanel, so
represents and
ImagePanel can be
displays a
displayed on screen
stationary object
from the model.
Each ImagePanel
has a unique Line 13
identifier
Line 16
Point2D.Double
offers precision
for xLine 19
y position coordinate
// stores all ImagePanel children
private Set panelChildren;
// constructor initializes position and image
public ImagePanel( int identifier, String imageName )
{
super( null ); // specify null layout
setOpaque( false ); // make transparent
// set unique identifier
ID = identifier;
Outline
Line 25
Set of ImagePanel
children
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// set location
position = new Point2D.Double( 0, 0 );
setLocation( 0, 0 );
// create ImageIcon with given imageName
imageIcon = new ImageIcon(
getClass().getResource( imageName ) );
Image image = imageIcon.getImage();
setSize(
image.getWidth( this ), image.getHeight( this ) );
// create Set to store Panel children
panelChildren = new HashSet();
} // end ImagePanel constructor
// paint Panel to screen
public void paintComponent( Graphics g )
{
super.paintComponent( g );
Fig. 22.12 Class
ImagePanel
Use imageName
argument
represents
and
to instantiate ImageIcon
a on
that will displays
be displayed
stationary
object
screen
from the model
(Part 2).
Lines 44-46
Lines 54-60
Display ImagePanel
(and its ImageIcon) to
Lines 63-67
screen
// if image is ready, paint it to screen
imageIcon.paintIcon( this, g, 0, 0 );
}
// add ImagePanel child to ImagePanel
public void add( ImagePanel panel )
{
panelChildren.add( panel );
super.add( panel );
}
Override method add
to add ImagePanel
child
 2002 Prentice Hall, Inc.
All rights reserved.
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
// add ImagePanel child to ImagePanel at given index
public void add( ImagePanel panel, int index )
{
panelChildren.add( panel );
super.add( panel, index );
}
// remove ImagePanel child from ImagePanel
public void remove( ImagePanel panel )
{
panelChildren.remove( panel );
super.remove( panel );
}
// sets current ImageIcon to be displayed
public void setIcon( ImageIcon icon )
{
imageIcon = icon;
}
// set on-screen position
public void setPosition( double x, double y )
{
position.setLocation( x, y );
setLocation( ( int ) x, ( int ) y );
}
Outline
Overload method
add
to add ImagePanel
child
Fig. 22.12 Class
ImagePanel
represents and
Override method
remove
displays
a
to removestationary
ImagePanel
object
child the model
from
(Part 3).
Lines 70-74
Lines 77-81
Lines 84-100
Accessor
methods
// return ImagePanel identifier
public int getID()
{
return ID;
}
 2002 Prentice Hall, Inc.
All rights reserved.
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// get position of ImagePanel
public Point2D.Double getPosition()
{
return position;
}
// get imageIcon
public ImageIcon getImageIcon()
{
return imageIcon;
}
// get Set of ImagePanel children
public Set getChildren()
{
return panelChildren;
}
Outline
Fig. 22.12 Class
ImagePanel
represents and
displays a
stationary object
Accessor
from the model
methods
(Part 4).
Lines 103-118
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// MovingPanel.java
// JPanel subclass with on-screen moving capabilities
package com.deitel.jhtp4.elevator.view;
// Java core packages
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// Java extension packages
import javax.swing.*;
Fig. 22.13 Class
MovingPanel
represents and
displays a moving
MovingPanel
object
from the
represents moving
object
model.
in model
public class MovingPanel extends ImagePanel {
Line 13
// should MovingPanel change position?
private boolean moving;
Lines 20-21
// number of pixels MovingPanel moves in both x and y values
// per animationDelay milliseconds
private double xVelocity;
Use double
private double yVelocity;
to represent
velocity with highprecision
image
// constructor initializes position, velocity and
public MovingPanel( int identifier, String imageName )
{
super( identifier, imageName );
// set MovingPanel velocity
xVelocity = 0;
yVelocity = 0;
} // end MovingPanel constructor
 2002 Prentice Hall, Inc.
All rights reserved.
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
// update MovingPanel position and animation frame
public void animate()
{
// update position according to MovingPanel velocity
if ( isMoving() ) {
double oldXPosition = getPosition().getX();
double oldYPosition = getPosition().getY();
setPosition( oldXPosition + xVelocity,
oldYPosition + yVelocity );
}
// update all children of MovingPanel
Iterator iterator = getChildren().iterator();
while ( iterator.hasNext() ) {
MovingPanel panel = ( MovingPanel ) iterator.next();
panel.animate();
}
} // end method animate
// is MovingPanel moving on screen?
public boolean isMoving()
{
return moving;
}
// set MovingPanel to move on screen
public void setMoving( boolean move )
{
moving = move;
}
Outline
If MovingPanel
moving,
Fig. 22.13is Class
update
MovingPanel
MovingPanel
position,represents
as well as position
and
of MovingPanel’s
children
displays a moving
object from the
model (Part 2).
Lines 38-52
Lines 56-65
Accessor
methods
 2002 Prentice Hall, Inc.
All rights reserved.
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// set MovingPanel x and y velocity
public void setVelocity( double x, double y )
{
xVelocity = x;
yVelocity = y;
}
// return MovingPanel x velocity
public double getXVelocity()
{
return xVelocity;
}
// return MovingPanel y velocity
public double getYVelocity()
{
return yVelocity;
}
Outline
Fig. 22.13 Class
MovingPanel
represents and
displays a moving
object from the
model (Part 3).
Accessor
Lines 68-84
methods
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// AnimatedPanel.java
// MovingPanel subclass with animation capabilities
package com.deitel.jhtp4.elevator.view;
// Java core packages
import java.awt.*;
import java.util.*;
// Java extension packages
import javax.swing.*;
public class AnimatedPanel extends MovingPanel {
Fig. 22.14 Class
AnimatedPanel
AnimatedPanel
representsrepresents
moving
and
object in model
and an
displays
has several animated
frames of object
animation
from the model.
// should ImageIcon cycle frames
private boolean animating;
// frame cycle rate (i.e., rate advancing to next frame)
private int animationRate;
private int animationRateCounter;
private boolean cycleForward = true;
// individual ImageIcons used for animation frames
private ImageIcon imageIcons[];
// storage for all frame sequences
private java.util.List frameSequences;
private int currentAnimation;
Line 12
ImageIcon array
Line 23images in
that stores
an animation
Lines
18-33
sequence
Variables to control
animation rate and
determine which
frame of animation to
display
// should loop (continue) animation at end of cycle?
private boolean loop;
// should animation display last frame at end of animation?
private boolean displayLastFrame;
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// helps determine next displayed frame
private int currentFrameCounter;
// constructor takes array of filenames and screen position
public AnimatedPanel( int identifier, String imageName[] )
{
super( identifier, imageName[ 0 ] );
// creates ImageIcon objects from imageName string
imageIcons = new ImageIcon[ imageName.length ];
for ( int i = 0; i < imageIcons.length; i++ ) {
imageIcons[ i ] = new ImageIcon(
getClass().getResource( imageName[ i ] ) );
}
frameSequences = new ArrayList();
Fig. 22.14 Class
AnimatedPanel
represents and
displays an
array
animated object
AnimatedPanel
from theconstructor
model
creates (Part
ImageIcon
array
2).
from String array argument,
which contains
names of
Lines 44-49
image files
Lines 56-70
} // end AnimatedPanel constructor
// update icon position and animation frame
public void animate()
{
super.animate();
Override method animate
Lines 61-69
of class MovingPanel to
update AnimatedPanel
position and current frame of
animation
// play next animation frame if counter > animation rate
if ( frameSequences != null && isAnimating() ) {
if ( animationRateCounter > animationRate ) {
animationRateCounter = 0;
determineNextFrame();
}
else
animationRateCounter++;
}
Play next frame of
animation
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
} // end method animate
// determine next animation frame
Utility method that determines
private void determineNextFrame()
Fig.
22.14 Class
next frame of
animation
to
{
AnimatedPanel
int frameSequence[] =
display
( int[] ) frameSequences.get( currentAnimation );
represents and
displays an
animated object
purposes from
in animation:
the model
If
loop is false, animation terminates
(Part 3). after one
animation
iteration
73-99is
Last frame inLines
sequence
displayed if displayLastFrame
84-93in
is true, andLines
first frame
sequence is displayed if
Lines 88-91
displayLastFrame
is false
sequence
// if no more animation frames, determine final frame,
// unless loop is specified
if ( currentFrameCounter >= frameSequence.length
) {
Used for looping
currentFrameCounter = 0;
// if loop is false, terminate
if ( !isLoop() ) {
setAnimating( false );
if ( isDisplayLastFrame() )
// display last frame in
currentFrameCounter = frameSequence.length - 1;
}
Lines 96-97
}
// set current animation frame
setCurrentFrame( frameSequence[ currentFrameCounter ] );
currentFrameCounter++;
} // end method determineNextFrame
Call method setCurrentFrame to set ImageIcon
(current image displayed) to
the ImageIcon returned
 2002 Prentice
Hall, Inc.
from the current
frame
All rights reserved.
sequence.
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
// add frame sequence (animation) to frameSequences ArrayList
public void addFrameSequence( int frameSequence[] )
{
frameSequences.add( frameSequence );
}
// ask if AnimatedPanel is animating (cycling frames)
public boolean isAnimating()
{
return animating;
}
// set AnimatedPanel to animate
public void setAnimating( boolean animate )
{
animating = animate;
}
// set current ImageIcon
public void setCurrentFrame( int frame )
{
setIcon( imageIcons[ frame ] );
}
Outline
Fig. 22.14 Class
AnimatedPanel
represents and
displays an
animated object
from the model
(Part 4).
Lines 102-135
Accessor
methods
// set animation rate
public void setAnimationRate( int rate )
{
animationRate = rate;
}
// get animation rate
public int getAnimationRate()
{
return animationRate;
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
// set whether animation should loop
public void setLoop( boolean loopAnimation
{
loop = loopAnimation;
}
Outline
)
// get whether animation should loop
public boolean isLoop()
{
return loop;
}
// get whether to display last frame at animation end
private boolean isDisplayLastFrame()
{
return displayLastFrame;
}
Fig. 22.14 Class
AnimatedPanel
represents and
displays an
animated object
from the model
Accessor
(Part 5).
methods
Lines 138-159
Lines 162-167
// set whether to display last frame at animation end
public void setDisplayLastFrame( boolean displayFrame )
{
displayLastFrame = displayFrame;
}
// start playing animation sequence of given index
public void playAnimation( int frameSequence )
{
currentAnimation = frameSequence;
currentFrameCounter = 0;
setAnimating( true );
}
Begin animation
}
 2002 Prentice Hall, Inc.
All rights reserved.
22.9 (Optional Case Study) Thinking About
Objects: Animation and Sound in the View
fram eSequenc es
ima geIc o ns
A
B
C
D
0
1
2
3
0= 0
1
2
1= 0
1
3
2= 2
1
0
3= 3
2
2
1
0
0
ima ge se quenc es
A
B C
A
B
D
C
B
A
D C
C
B
A
A
22.15 Relationship between array imageIcons and List frameSequences.
 2002 Prentice Hall, Inc. All rights reserved.
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
// SoundEffects.java
// Returns AudioClip objects
package com.deitel.jhtp4.elevator.view;
// Java core packages
import java.applet.*;
Outline
Creates sound effects
(AudioClips) for view
public class SoundEffects {
// location of sound files
private String prefix = "";
public SoundEffects() {}
Fig. 22.16 Class
SoundEffects
returns AudioClip
objects.
Pass soundFile
parameter to
static method newAudioClip
Line 8
(of class
java.applet.Applet) to
Lines 19-20
return AudioClip
object
// get AudioClip associated with soundFile
public AudioClip getAudioClip( String soundFile )
{
try {
return Applet.newAudioClip( getClass().getResource(
prefix + soundFile ) );
}
// return null if soundFile does not exist
catch ( NullPointerException nullPointerException ) {
return null;
}
}
// set prefix for location of soundFile
public void setPathPrefix( String string )
{
prefix = string;
}
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// ElevatorMusic.java
// Allows for MIDI playing capabilities
package com.deitel.jhtp4.elevator.view;
Fig. 22.17 Class
ElevatorMusic
Creates “elevator
music”
plays
music
(music heard when when a
Person
rides in the
Person rides
Elevator)
forElevator.
view
// Java core packages
import java.io.*;
import java.net.*;
// Java extension packages
import javax.sound.midi.*;
public class ElevatorMusic implements MetaEventListener {
// MIDI sequencer
private Sequencer sequencer;
Line 12
Lines 31-33
// should music stop playing?
private boolean endOfMusic;
// sound file name
private String fileName;
// sequence associated with sound file
private Sequence soundSequence;
// constructor opens a MIDI file to play
public ElevatorMusic( String file )
{
// set sequencer
try {
sequencer = MidiSystem.getSequencer();
sequencer.addMetaEventListener( this );
fileName = file;
}
Initialize system’s MIDI sequencer
and registers class ElevatorMusic
for MetaMessage events from
sequencer
 2002 Prentice Hall, Inc.
All rights reserved.
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
// handle exception if MIDI is unavailable
catch ( MidiUnavailableException midiException ) {
midiException.printStackTrace();
}
} // end ElevatorMusic constructor
// open music file
public boolean open()
{
try {
// get URL for media file
URL url = getClass().getResource( fileName );
Outline
Fig. 22.17 Class
ElevatorMusic
plays music when a
Person rides in the
Elevator (Part 2).
Lines 54-55
// get valid MIDI file
soundSequence = MidiSystem.getSequence ( url );
// open sequencer for specified file
sequencer.open();
sequencer.setSequence( soundSequence );
}
Open sequencer for
specified file and validate
MIDI data
// handle exception if URL does not exist
catch ( NullPointerException nullPointerException ) {
nullPointerException.printStackTrace();
return false;
}
// handle exception if MIDI data is invalid
catch ( InvalidMidiDataException midiException ) {
midiException.printStackTrace();
soundSequence = null;
return false;
}
 2002 Prentice Hall, Inc.
All rights reserved.
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
Outline
// handle IO exception
catch ( java.io.IOException ioException ) {
ioException.printStackTrace();
soundSequence = null;
return false;
}
// handle exception if MIDI is unavailable
catch ( MidiUnavailableException midiException ) {
midiException.printStackTrace();
return false;
}
Fig. 22.17 Class
ElevatorMusic
plays music when a
Person rides in the
Elevator (Part 3).
Lines 88-92
return true;
}
// play MIDI track
public void play()
{
sequencer.start();
endOfMusic = false;
}
Start sequencer and play MIDI
file
// get sequencer
public Sequencer getSequencer()
{
return sequencer;
}
 2002 Prentice Hall, Inc.
All rights reserved.
100
101
102
103
104
105
106
107
108
// handle end of track
public void meta( MetaMessage message )
{
if ( message.getType() == 47 ) {
endOfMusic = true;
sequencer.stop();
}
}
}
Outline
Fig. 22.17 Class
ElevatorMusic
plays music when a
Person rides in the
Elevator (Part 4).
 2002 Prentice Hall, Inc.
All rights reserved.