La multimedialità: le immagini, le animazioni e i suoni

CAPITOLO 8
La multimedialità: le immagini,
le animazioni e i suoni
Obiettivi
•
•
•
•
Imparare a visualizzare le immagini
Creare animazioni da sequenze di immagini
Creare mappe di immagini
Imparare a riprodurre, fermare e riprodurre continuamente suoni usando
un oggetto AudioClip
8.1 Introduzione
Con l’evoluzione dell’informatica, i computer hanno smesso di essere degli strumenti utili
soltanto a eseguire velocemente calcoli matematici, e sono diventati strumenti per la manipolazione dei dati e delle informazioni. Una delle caratteristiche più interessanti di Java è sicuramente la multimedialità, ovvero la capacità di usare suoni, immagini, grafica e video nelle
applicazioni.
La programmazione multimediale offre molte sfide nuove. I nuovi computer vengono
ormai venduti già provvisti di unità CD-ROM o DVD, schede audio e speciali funzionalità
video. Sempre più utenti, poi, vogliono avere la possibilità di realizzare grafica tridimensionale e ad alta risoluzione. Nei prossimi dieci anni, lo sviluppo nella grafica tridimensionale
renderà possibili nuove applicazioni: immaginate di avere una televisione tridimensionale
nella vostra casa, in cui seguire gli eventi sportivi e gli spettacoli come se avvenissero nel
vostro salotto! Gli studenti di medicina potranno vedere operazioni che si stanno svolgendo
a migliaia di chilometri di distanza come se avvenissero nella stessa stanza. Le persone potranno imparare a guidare usando simulatori di automobile estremamente realistici prima ancora
di salire in auto. Le possibilità sono eccitanti e potenzialmente senza fine.
La multimedialità richiede ai computer un’enorme quantità di potenza. Fino a poco
tempo fa, non erano disponibili dei computer economici con la potenza necessaria. I processori
veloci ora disponibili, come SPARC Ultra di Sun Microsystems, Pentium e Itanium di Intel,
Alpha di Hewlett-Packard, e i processori di MIPS/Silicon Graphics (per citarne alcuni) rendono possibile la multimedialità. A trarre beneficio da questa rivoluzione multimediale saranno principalmente l’industria informatica e il mondo delle comunicazioni, in quanto gli
utenti saranno disposti ad acquistare processori più veloci, memorie e ampiezze di banda più
grandi per le comunicazioni, tutte cose necessarie per poter supportare le applicazioni
19 - Multimedialità.p65
437
19/12/2003, 9.36
438
CAPITOLO 8
multimediali. Paradossalmente, poi, il costo di tutto ciò non sarà eccessivamente elevato, in
quanto la competizione sarà altissima e, quindi, i prezzi tenderanno a scendere sempre più.
Per creare applicazioni multimediali efficaci, però, sono necessari linguaggi di programmazione che facilitino la creazione di contenuti multimediali. La maggior parte dei linguaggi
attualmente esistenti, infatti, non possiede capacità multimediali di tipo nativo. Al contrario
di questi linguaggi, Java offre funzionalità multimediali complete, che permettono ai programmatori di sviluppare da subito applicazioni multimediali estremamente interessanti e
potenti.
In questo capitolo, verranno presentati vari esempi delle funzionalità multimediali più
importanti per la realizzazione di applicazioni efficaci, tra cui:
•
•
•
•
come manipolare le immagini
come creare animazioni fluide
come riprodurre suoni con l’interfaccia AudioClip
come creare mappe di immagini in grado di “sentire” quando il puntatore del mouse si
trova sopra di loro, anche senza un clic da parte dell’utente.
Gli esercizi alla fine del capitolo proporranno decine di progetti interessanti, che scateneranno la vostra creatività.
8.2 Caricare, visualizzare e scalare immagini
Le capacità multimediali di Java consentono di gestire grafica, immagini, suoni e video. Iniziamo la nostra trattazione con le immagini.
L’applet della figura 8.1 mostra il caricamento di un oggetto Image (package java.awt)
e di un oggetto ImageIcon (package javax.swing). L’applet visualizza l’oggetto Image nella
sua dimensione originale e poi ingrandita, usando due versioni del metodo drawImage della
classe Graphics. L’applet disegna inoltre l’oggetto ImageIcon usando il suo metodo paintIcon.
La classe ImageIcon è più semplice da usare della classe Image, perché il suo costruttore può
ricevere argomenti di formati diversi, tra cui un array di byte contente i byte dell’immagine,
un oggetto Image già caricato in memoria e una stringa o un URL che rappresenta la posizione
dell’immagine.
1
2
3
4
5
6
7
8
9
10
11
12
13
Figura 8.1
19 - Multimedialità.p65
// Fig. 8.1: LoadImageAndScale.java
// Carica un’immagine e la visualizza nella sua dimensione originale e
// raddoppiata. Carica e visualizza la stessa immagine come un ImageIcon
import java.applet.Applet;
import java.awt.*;
import javax.swing.*;
public class LoadImageAndScale extends JApplet {
private Image logo1;
private ImageIcon logo2;
// carica immagine quando l’applet viene caricata
public void init()
Caricare e visualizzare un’immagine in un applet (continua)
438
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Figura 8.1
439
{
logo1 = getImage( getDocumentBase(), “logo.gif” );
logo2 = new ImageIcon( “logo.gif” );
}
// visualizza immagine
public void paint( Graphics g )
{
g.drawImage( logo1, 0, 0, this ); // disegna immagine originale
// disegna immagine che riempie larghezza e altezza meno
120 pixel
g.drawImage( logo1, 0, 120, getWidth(), getHeight() – 120, this );
// disegna icona usando il suo metodo paintIcon
logo2.paintIcon( this, g, 180, 0 );
}
} // fine classe LoadImageAndScale
Caricare e visualizzare un’immagine in un applet
Le righe 9 e 10 dichiarano rispettivamente un riferimento Image e un riferimento ImageIcon.
La classe Image è una classe abstract; quindi, non è possibile creare direttamente un oggetto della classe Image. È necessario invece che l’applet chiami un metodo che fa in modo che
il contenitore dell’applet carichi e ritorni l’oggetto Image da usare nel programma. La classe
Applet (la superclasse di JApplet) fornisce il metodo getImage (riga 15 del metodo init)
per caricare un Image nell’applet. Questa versione di getImage prende due argomenti: la
posizione e il nome del file dell’immagine. Nel primo argomento, il metodo getDocumentBase
della classe Applet viene utilizzato per ritornare un URL che rappresenta la posizione dell’immagine in Internet (o sul computer, se è da qui che proviene l’applet). In questo caso, si parte
19 - Multimedialità.p65
439
19/12/2003, 9.36
440
CAPITOLO 8
dal presupposto che l’immagine da caricare si trovi nella stessa directory del file HTML che
ha invocato l’applet. Il metodo getDocumentBase ritorna la posizione in Internet del file
HTML come un oggetto della classe URL. Il secondo argomento specifica un nome di file per
l’immagine. Java supporta attualmente parecchi formati per le immagini, tra cui Graphics
Interchange Format (GIF), Joint Photographic Experts Group (JPEG) e Portable Network Graphics
(PNG). I nomi di file di ognuno di questi tipi terminano rispettivamente con l’estensione
.gif, .jpg (o .jpeg) e .png.
Obiettivo portabilità 8.1
La classe Image è una classe abstract, quindi gli oggetti Image non possono essere creati direttamente. Per ottenere l’indipendenza dalla piattaforma, l’implementazione Java
su ognuna delle piattaforme fornisce la propria sottoclasse di Image per memorizzare le
informazioni relative alle immagini.
La riga 15 inizia a caricare l’immagine dal computer locale (o a scaricare l’immagine da
Internet). Quando l’immagine viene richiesta dal programma, viene caricata in un thread
separato di esecuzione. Questo permette al programma di continuare la propria esecuzione
mentre l’immagine viene caricata. [Nota: Se il file richiesto non è disponibile, il metodo
getImage non segnala un errore.]
La classe ImageIcon non è una classe abstract, quindi è possibile creare un oggetto
ImageIcon. La riga 16 del metodo init dell’applet crea un oggetto ImageIcon, che carica la
stessa immagine logo.gif. La classe ImageIcon fornisce vari costruttori che permettono al
programma di inizializzare un oggetto ImageIcon con un’immagine proveniente dal computer locale, oppure con un’immagine conservata in Internet.
Il metodo paint dell’applet (righe 20-29) visualizza l’immagine. La riga 22 usa il metodo drawImage di Graphics per visualizzare un oggetto Image. Il metodo drawImage riceve
quattro argomenti. Il primo argomento è un riferimento all’oggetto Image da visualizzare
(logo1). Il secondo e il terzo argomento sono le coordinate x e y in cui l’immagine deve essere
visualizzata all’interno dell’applet (le coordinate corrispondono all’angolo superiore sinistro
dell’immagine). L’ultimo argomento è un riferimento a un oggetto ImageObserver. Questo
argomento è importante se si visualizzano immagini di grandi dimensioni, che richiedono
molto tempo per essere scaricate da Internet. È possibile dunque che il programma esegua il
codice che visualizza l’immagine prima che questa sia stata scaricata completamente. L’oggetto ImageObserver viene avvisato di aggiornare l’immagine visualizzata mano a mano che il
resto dell’immagine viene scaricato. Normalmente, l’ImageObserver è l’oggetto su cui viene
visualizzata l’immagine. Un ImageObserver può essere un qualsiasi oggetto che implementa
l’interfaccia ImageObserver, per esempio la classe Component (una delle superclassi indirette della classe Applet); tutti i Component, quindi (incluso il nostro applet), possono essere
degli ImageObserver. Quando eseguite questo applet, osservate attentamente la visualizzazione
delle varie parti dell’immagine, via via che questa viene caricata. [Nota: Con i computer più
veloci, questo effetto potrebbe non essere visibile.]
La riga 25 usa un’altra versione del metodo drawImage di Graphics per mostrare una
versione scalata dell’immagine. Il quarto e quinto argomento specificano la larghezza e l’altezza dell’immagine ai fini della visualizzazione. L’immagine viene automaticamente scalata
in base alla larghezza e all’altezza specificate. In questo esempio, il quarto argomento indica
che la larghezza dell’immagine scalata deve essere pari alla larghezza dell’applet, mentre il
19 - Multimedialità.p65
440
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
441
quinto argomento indica che l’altezza deve essere l’altezza dell’applet meno 120 pixel. La
larghezza e l’altezza dell’applet sono determinate con i metodi getWidth e getHeight (ereditati dalla classe Component).
La riga 28 usa il metodo paintIcon di ImageIcon per visualizzare l’immagine. Il metodo richiede quattro argomenti: un riferimento al Component sul quale l’immagine verrà
visualizzata, un riferimento all’oggetto Graphics che verrà utilizzato per rappresentare l’immagine sullo schermo, la coordinata x dell’angolo superiore sinistro dell’immagine, e la coordinata y dell’angolo superiore sinistro dell’immagine.
Se confrontate i due modi in cui abbiamo caricato e visualizzato le immagini in questo
esempio, vedrete che l’impiego di ImageIcon è più semplice. È possibile creare oggetti della
classe ImageIcon direttamente, senza alcun bisogno di usare un riferimento ImageObserver
durante la visualizzazione dell’immagine. È questo il motivo per cui utilizzeremo la classe
ImageIcon per tutto il resto del capitolo. [Nota: Il metodo paintIcon della classe ImageIcon
non permette di scalare un’immagine. Questa classe, però, fornisce il metodo getImage, che
ritorna un riferimento Image da utilizzare con il metodo drawImage di Graphics per visualizzare
un’immagine scalata.]
8.3 Animare una sequenza di immagini
Il prossimo esempio mostra l’animazione di una sequenza di immagini che sono conservate
all’interno di un array di ImageIcon. L’animazione presentata nella figura 8.2 è stata progettata come una sottoclasse di JPanel (chiamata LogoAnimator), che può essere attaccata a
una finestra di applicazione, oppure a un JApplet. La classe LogoAnimator definisce inoltre
un metodo main (righe 85-99) per eseguire l’animazione sotto forma di applicazione. Il metodo main definisce un’istanza della classe JFrame, aggiungendo un oggetto LogoAnimator
al JFrame, per visualizzare l’animazione.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Figura 8.2
19 - Multimedialità.p65
// Fig. 8.2: LogoAnimator.java
// Animazione di una sequenza di immagini
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LogoAnimator extends JPanel implements ActionListener {
private final static String IMAGE_NAME = “deitel”; // nome base
immagine
protected ImageIcon images[];
// array di immagini
private int totalImages = 30;
// numero di immagini
private int currentImage = 0;
// indice corrente di immagine
private int animationDelay = 50; // ritardo in millisecondi
private int width;
// larghezza dell’immagine
private int height;
// altezza dell’immagine
private Timer animationTimer; // Timer che controlla l’animazione
L’animazione di una sequenza di immagini (continua)
441
19/12/2003, 9.36
442
CAPITOLO 8
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Figura 8.2
19 - Multimedialità.p65
// inizializza LogoAnimator caricando le immagini
public LogoAnimator()
{
images = new ImageIcon[ totalImages ];
// carica immagini
for ( int count = 0; count < images.length; ++count )
images[ count ] = new ImageIcon( getClass().getResource(
“images/” + IMAGE_NAME + count + “.gif” ) );
// questo esempio assume che le immagini abbiano le stesse
dimensioni
width = images[ 0 ].getIconWidth(); // ottiene larghezza icona
height = images[ 0 ].getIconHeight(); // ottiene altezza icona
}
// visualizza immagine corrente
public void paintComponent( Graphics g )
{
super.paintComponent( g );
images[ currentImage ].paintIcon( this, g, 0, 0 );
// si sposta alla prossima immagine solo se il timer è partito
if ( animationTimer.isRunning() )
currentImage = ( currentImage + 1 ) % totalImages;
}
// risponde a eventi del timer
public void actionPerformed( ActionEvent actionEvent )
{
repaint(); // ridisegna animator
}
// fa partire o ripartire l’animazione
public void startAnimation()
{
if ( animationTimer == null ) {
currentImage = 0;
animationTimer = new Timer( animationDelay, this );
animationTimer.start();
}
else // continua dall’ultima immagine visualizzata
if ( ! animationTimer.isRunning() )
animationTimer.restart();
}
// ferma timer dell’animazione
public void stopAnimation()
L’animazione di una sequenza di immagini (continua)
442
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
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
Figura 8.2
443
{
animationTimer.stop();
}
// ritorna la dimensione minima di animation
public Dimension getMinimumSize()
{
return getPreferredSize();
}
// ritorna la dimensione preferenziale di animation
public Dimension getPreferredSize()
{
return new Dimension( width, height );
}
// esegue animation in un JFrame
public static void main( String args[] )
{
LogoAnimator animation = new LogoAnimator(); // crea LogoAnimator
JFrame window = new JFrame( “Animator test” ); // imposta finestra
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Container container = window.getContentPane();
container.add( animation );
window.pack();
// rende la finestra abbastanza larga
per la sua GUI
window.setVisible( true );
// visualizza finestra
animation.startAnimation(); // inizia animazione
} // fine metodo main
} // fine classe LogoAnimator
L’animazione di una sequenza di immagini
La classe LogoAnimator mantiene un array di ImageIcon che vengono caricate nel costruttore
(righe 21-33). Le righe 26-32 creano ogni oggetto ImageIcon e caricano le 30 immagini
dell’animazione. L’argomento del costruttore usa la concatenazione delle stringhe per assemblare
il nome del file dai vari pezzi “images/”, IMAGE_NAME, count e “.gif”. Ognuna delle
immagini dell’animazione è un file chiamato “deitel#.gif”, dove # è un valore 0-29 specificato dalla variabile di controllo del ciclo count. Le righe 31-32 determinano la larghezza e
19 - Multimedialità.p65
443
19/12/2003, 9.36
444
CAPITOLO 8
l’altezza dell’animazione dalle dimensioni della prima immagine nell’array images. Assumeremo che tutte le immagini abbiano la stessa larghezza e altezza.
Dopo che il costruttore di LogoAnimator ha caricato le immagini, il metodo main
imposta la finestra in cui apparirà l’animazione (righe 89-96) e la riga 97 chiama il metodo
startAnimation della classe LogoAnimator (definito alle righe 54-64) per dare inizio
all’animazione. L’animazione è guidata da un’istanza della classe Timer (package javax.swing).
Un oggetto della classe Timer genera degli ActionEvent ad un intervallo di tempo fissato
in millisecondi (normalmente specificato come argomento del costruttore di Timer) e
notifica dell’evento tutti i suoi ActionListener registrati. Le righe 56-60 determinano se
il riferimento animationTimer di tipo Timer è null. In caso affermativo, currentImage
è impostato su 0 (riga 57), per indicare che l’animazione dovrebbe iniziare con l’immagine
che si trova nel primo elemento dell’array images. La riga 58 assegna un nuovo oggetto
Timer ad animationTimer. Il costruttore di Timer riceve due argomenti: l’attesa in millisecondi
(in questo esempio, animationDelay è 50, come specificato dalla riga 14) e l’ActionListener
che risponderà agli ActionEvent di Timer. La classe LogoAnimator implementa ActionListener,
quindi la riga 58 specifica this come ascoltatore. La riga 59 avvia l’oggetto Timer; una
volta avviato, animationTimer genererà un ActionEvent ogni 50 millisecondi. Le righe
62-63 permettono al programma di interrompere e riavviare l’animazione. Per ottimizzare
un’animazione all’interno di un applet, per esempio, questa dovrebbe essere interrotta quando
l’utente passa a un’altra pagina Web. Se l’utente ritorna alla pagina Web contenente l’animazione, il metodo startAnimation può essere chiamato per riavviarla. La condizione
if alla riga 62 usa il metodo isRunning di Timer per determinare se Timer è attualmente
in esecuzione (ovvero, se sta generando degli eventi). Se non è in esecuzione, la riga 63
chiama il metodo restart di Timer per indicare che Timer dovrebbe ricominciare a generare eventi.
In risposta a ognuno degli eventi di Timer di questo esempio, il programma chiama il
metodo actionPerformed (righe 48-51). La riga 50 chiama il metodo repaint di LogoAnimator
che programma una chiamata al metodo paintComponent di LogoAnimator (righe 36-45).
Ricordate che qualsiasi sottoclasse di JComponent che esegue dei disegni deve farlo nel suo
metodo paintComponent. Come visto nel capitolo 3, la prima istruzione di qualsiasi metodo
paintComponent dovrebbe essere una chiamata al metodo paintComponent della superclasse,
per assicurarsi che i componenti Swing siano visualizzati correttamente.
La riga 40 disegna l’ImageIcon dell’elemento currentImage all’interno dell’array. Le
righe 43-44 determinano se animationTimer è in esecuzione e, in caso affermativo, si preparano a visualizzare l’immagine successiva aumentando di 1 currentImage. Notate il calcolo
del resto per impostare il valore di currentImage su 0 quando si supera il valore 29 (l’ultimo
elemento dell’array). L’istruzione if ci assicura che, se paintComponent viene chiamato quando
Timer viene fermato, verrà visualizzata la stessa immagine. Questo potrebbe essere utile in
una GUI che consente all’utente di far partire e fermare l’animazione. Per esempio, se l’animazione viene fermata e l’utente la “copre” con un’altra finestra e poi la “scopre” nuovamente, verrà chiamato il metodo paintComponent. In questo caso, non vogliamo che l’animazione mostri l’immagine successiva.
Il metodo stopAnimation (righe 67-70) interrompe l’animazione chiamando il metodo
della classe Timer per indicare che il Timer dovrebbe interrompere la generazione di
eventi. Questo fatto, a sua volta, impedisce ad actionPerformed di chiamare repaint per
avviare la visualizzazione dell’immagine successiva all’interno dell’array.
stop
19 - Multimedialità.p65
444
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
445
Ingegneria del software 8.1
Quando si crea un’animazione da utilizzare all’interno di un applet, è necessario fornire un meccanismo che la disattivi quando l’utente passa a una pagina Web diversa dalla
pagina in cui si trova l’animazione.
I metodi getMinimumSize (righe 73-76) e getPreferredSize (righe 79-82) sovrascrivono
i corrispondenti metodi ereditati dalla classe Component per aiutare il layout manager a determinare la corretta dimensione di un LogoAnimator all’interno di un layout. In questo
esempio, le immagini sono larghe 160 pixel e alte 80 pixel, quindi il metodo getPreferredSize
ritorna un oggetto Dimension contenente i numeri 160 e 80. Il metodo getMinimumSize
chiama semplicemente getPreferredSize (una normale abitudine di programmazione) per
indicare che la dimensione minima e quella preferenziale sono uguali. Alcuni layout manager
ignorano le dimensioni specificate da questi metodi. Per esempio, le regioni NORTH e SOUTH
di BorderLayout usano solo l’altezza preferenziale di un componente.
8.4 Mappe di immagini
Una tecnica molto comune per creare pagine Web interattive è il ricorso alle mappe di immagini. Una mappa di immagini è un’immagine che possiede delle aree attive su cui l’utente può
fare clic al fine di eseguire un’attività, come il caricamento di una diversa pagina Web all’interno del browser. Quando l’utente posiziona il puntatore del mouse su una di queste aree,
appare normalmente un messaggio descrittivo nella barra di stato del browser o in un tool
tip.
La figura 8.3 carica un’immagine contenente varie icone utilizzate all’interno del libro. Il
programma permette all’utente di posizionare il puntatore del mouse su di un’icona, visualizzando
un messaggio descrittivo relativo a questa icona. Il gestore di eventi mouseMoved (righe 3741) prende le coordinate del mouse e le passa al metodo translateLocation (righe 59-70).
Il metodo translateLocation analizza le coordinate per determinare l’icona su cui è stato
posizionato il mouse quando è stato chiamato il metodo mouseMoved; il metodo ritorna
quindi un messaggio che indica ciò che l’icona rappresenta; questo messaggio viene visualizzato
nella barra di stato del contenitore dell’applet.
Facendo clic all’interno dell’applet della figura 8.3, non viene eseguita alcuna azione. Nel
capitolo 7, abbiamo parlato delle tecniche necessarie per caricare un’altra pagina Web all’interno di un browser usando oggetti URL e l’interfaccia AppletContext. Usando queste tecniche, è possibile modificare quest’applet affinché ogni icona sia associata a un URL diverso
che il browser può visualizzare quando l’utente fa clic sull’icona.
1
2
3
4
5
6
7
8
9
Figura 8.3
19 - Multimedialità.p65
// Fig. 8.3: ImageMap.java
// Dimostrazione di una mappa di immagini
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ImageMap extends JApplet {
private ImageIcon mapImage;
Una mappa di immagini (continua)
445
19/12/2003, 9.36
446
CAPITOLO 8
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Figura 8.3
19 - Multimedialità.p65
private static final String captions[] = { “Common Programming
Error”,
“Good Programming Practice”, “Graphical User Interface Tip”,
“Performance Tip”, “Portability Tip”,
“Software Engineering Observation”, “Error-Prevention Tip” };
// imposta ascoltatori del mouse
public void init()
{
addMouseListener(
new MouseAdapter() { // classe interna anonima
// indica quando il puntatore del mouse esce dall’area
dell’applet
public void mouseExited( MouseEvent event )
{
showStatus( “Pointer outside applet” );
}
} // fine classe interna anonima
); // fine chiamata a addMouseListener
addMouseMotionListener(
new MouseMotionAdapter() { // classe interna anonima
// determina l’icona su cui il mouse è posizionato
public void mouseMoved( MouseEvent event )
{
showStatus( translateLocation(
event.getX(), event.getY() ) );
}
} // fine classe interna anonima
); // fine chiamata a addMouseMotionListener
mapImage = new ImageIcon( “icons.png” );
}
// ottiene immagine
// fine metodo init
// visualizza mapImage
public void paint( Graphics g )
{
super.paint( g );
mapImage.paintIcon( this, g, 0, 0 );
}
Una mappa di immagini (continua)
446
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Figura 8.3
19 - Multimedialità.p65
447
// ritorna testo descrittivo associato con l’icona
public String translateLocation( int x, int y )
{
// se le coordinate sono fuori dall’immagine, ritorna
immediatamente
if ( x >= mapImage.getIconWidth() || y >=
mapImage.getIconHeight() )
return “”;
// determina il numero dell’icona (0 - 6)
int iconWidth = mapImage.getIconWidth() / 7;
int iconNumber = x / iconWidth;
return captions[ iconNumber ]; // ritorna testo appropriato
}
} // fine classe ImageMap
Una mappa di immagini (continua)
447
19/12/2003, 9.36
448
CAPITOLO 8
Figura 8.3
Una mappa di immagini
8.5 Caricare e riprodurre i clip audio
I programmi Java sono in grado di utilizzare e riprodurre i clip audio, che Internet offre in
grande abbondanza. Per poter riprodurre questi clip audio, il sistema deve disporre dell’apposito hardware (altoparlanti e una scheda audio).
Java offre diversi meccanismi per la riproduzione dei suoni all’interno di un applet. I due
più semplici sono il metodo play di Applet e il metodo play dell’interfaccia AudioClip.
Funzionalità aggiuntive sono disponibili nelle librerie Java Media Framework e Java Sound.
Se volete riprodurre un suono una volta all’interno di un programma, il metodo play di
Applet permette di caricare questo suono e di riprodurlo una sola volta, dopodiché il suono
viene contrassegnato per la garbage collection. Il metodo play di Applet ha due forme:
public void play( URL location, String soundFileName );
public void play( URL soundURL );
La prima versione carica il clip audio memorizzato nel file soundFileName, alla posizione
location, e ne riproduce il suono. Il primo argomento è normalmente una chiamata al
19 - Multimedialità.p65
448
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
449
metodo getDocumentBase o getCodeBase dell’applet. Il metodo getDocumentBase ritorna la posizione del file HTML che ha caricato l’applet (se l’applet è in un package, il metodo
ritorna la posizione del package o del file JAR che contiene il package). Il metodo getCodeBase
indica dove si trova il file .class dell’applet. La seconda versione del metodo play, invece,
prende un URL che contiene la posizione e il nome del file del clip audio. L’istruzione
play( getDocumentBase(), “hi.au” );
carica il clip audio memorizzato nel file hi.au, riproducendolo una volta.
Il motore sonoro che riproduce i clip audio supporta vari formati di file, tra cui Sun Audio
(estensione .au), Windows Wave (.wav), Macintosh AIFF (.aif o .aiff), Musical Instrument
Digital Interface (MIDI) (.mid o .rmi). Le librerie Java Media Framework (JMF) e Java
Sound supportano anche altri formati.
Il programma della figura 8.4 mostra il caricamento e la riproduzione di un AudioClip
(package java.applet). Questa tecnica è più flessibile rispetto al metodo play di Applet,
in quanto permette di memorizzare il clip audio all’interno del programma, così da poterlo
riutilizzare più volte durante l’esecuzione. Il metodo getAudioClip di Applet ha due forme,
che prendono gli stessi argomenti del metodo play visto poco fa. Il metodo getAudioClip
ritorna un riferimento a un AudioClip; una volta caricato un AudioClip, è possibile invocare tre metodi: play, loop e stop. Il metodo play riproduce il clip una volta; il metodo loop
riproduce il clip in continuazione; il metodo stop interrompe la riproduzione di un clip. Nel
programma, ognuno di questi metodi è associato a un pulsante dell’applet.
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
Figura 8.4
19 - Multimedialità.p65
// Fig. 8.4: LoadAudioAndPlay.java
// Carica un clip audio e lo riproduce
import
import
import
import
java.applet.*;
java.awt.*;
java.awt.event.*;
javax.swing.*;
public class LoadAudioAndPlay extends JApplet {
private AudioClip sound1, sound2, currentSound;
private JButton playSound, loopSound, stopSound;
private JComboBox chooseSound;
// carica il suono quando l’applet inizia l’esecuzione
public void init()
{
Container container = getContentPane();
container.setLayout( new FlowLayout() );
String choices[] = { “Welcome”, “Hi” };
chooseSound = new JComboBox( choices );
chooseSound.addItemListener(
new ItemListener() {
Caricare e riprodurre un AudioClip (continua)
449
19/12/2003, 9.36
450
CAPITOLO 8
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Figura 8.4
19 - Multimedialità.p65
// ferma il suono e imposta il suono sulla selezione
dell’utente
public void itemStateChanged( ItemEvent e )
{
currentSound.stop();
currentSound =
chooseSound.getSelectedIndex() == 0 ? sound1 : sound2;
}
} // fine classe interna anonima
); // fine chiamata a addItemListener
container.add( chooseSound );
// imposta i pulsanti e il loro gestore degli eventi
ButtonHandler handler = new ButtonHandler();
playSound = new JButton( “Play” );
playSound.addActionListener( handler );
container.add( playSound );
loopSound = new JButton( “Loop” );
loopSound.addActionListener( handler );
container.add( loopSound );
stopSound = new JButton( “Stop” );
stopSound.addActionListener( handler );
container.add( stopSound );
// carica suoni e imposta currentSound
sound1 = getAudioClip( getDocumentBase(), “welcome.wav” );
sound2 = getAudioClip( getDocumentBase(), “hi.au” );
currentSound = sound1;
} // fine metodo init
// ferma il suono quando l’utente carica un’altra pagina Web
public void stop()
{
currentSound.stop();
}
// classe interna privata per gestire eventi dei pulsanti
private class ButtonHandler implements ActionListener {
// elabora eventi dei pulsanti play, loop e stop
Caricare e riprodurre un AudioClip (continua)
450
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Figura 8.4
451
public void actionPerformed( ActionEvent actionEvent )
{
if ( actionEvent.getSource() == playSound )
currentSound.play();
else if ( actionEvent.getSource() == loopSound )
currentSound.loop();
else if ( actionEvent.getSource() == stopSound )
currentSound.stop();
}
} // fine classe ButtonHandler
} // fine classe LoadAudioAndPlay
Caricare e riprodurre un AudioClip
Le righe 58-59 del metodo init dell’applet usano getAudioClip per caricare due file audio:
un file Windows Wave (welcome.wav) e un file Sun Audio (hi.au). L’utente può selezionare
quale clip audio riprodurre dal JComboBox chooseSound. Notate come il metodo stop
dell’applet venga sovrascritto alle righe 65-68. Quando l’utente cambia pagina Web, viene
chiamato il metodo stop; questo garantisce che la riproduzione del clip audio venga interrotta, altrimenti continuerebbe in sottofondo, anche se l’applet non viene visualizzato nel browser.
Questo potrebbe non essere un problema, ma potrebbe disturbare l’utente.
L’esperienza dell’utente 8.1
Durante la riproduzione dei clip audio in un applet o in un’applicazione, fornite sempre
all’utente un meccanismo per disabilitare l’audio.
8.6 Risorse in Internet e World Wide Web
www.nasa.gov/gallery/index.html
La galleria multimediale della NASA contiene un’ampia varietà di immagini, clip audio e
video che possono essere scaricati e utilizzati per testare i propri programmi Java di tipo
multimediale.
sunsite.sut.ac.jp/multimed/
Il sito Web Sunsite Japan Multimedia Collection fornisce un’ampia varietà di immagini, clip
audio e video che possono essere scaricati e utilizzati a scopi educativi.
www.anbg.gov.au/anbg/index.html
19 - Multimedialità.p65
451
19/12/2003, 9.36
452
CAPITOLO 8
Il sito Web Australian National Botanic Gardens offre collegamenti ai suoni di molti animali.
Provate il collegamento Common Birds.
www.thefreesite.com
Offre collegamenti a suoni e immagini gratuiti.
www.soundcentral.com
Offre audio clip nei formati WAV, AU, AIFF e MIDI.
www.animationfactory.com
Offre migliaia di animazioni GIF gratuite per uso personale.
www.clipart.com
Servizio (basato su iscrizione) che fornisce immagini e suoni.
www.pngart.com
Fornisce oltre 50 mila immagini gratuite in formato PNG.
developer.java.sun.com/developer/techDocs/hi/repository
Il Java Look-and-Feel Graphics Repository fornisce immagini standard da usare in una GUI
Swing
Riferimenti alle librerie Java Multimedia
java.sun.com/products/java-media/jmf/
Home page di Java Media Framework (JMF) API. Qui potete scaricare l’ultima implementazione
del JMF e la relativa documentazione.
java.sun.com/products/java-media/sound/
Home page di Java Sound API. Fornisce funzionalità per riprodurre e registrare audio.
java.sun.com/products/java-media/3D/
Home page di Java 3D API. Fornisce funzionalità per produrre immagini tridimensionali.
developer.java.sun.com/developer/onlineTraining/java3d/
Fornisce un corso di Java 3D API.
java.sun.com/products/java-media/jai/
Home page di Java Advanced Imaging API. Fornisce funzionalità per elaborare immagini,
come aumento del contrasto, ritaglio, scalatura e trasformazioni geometriche.
freetts.sourceforge.net/docs/index.php
FreeTTS è un’implementazione di Java Speech API.
java.sun.com/products/java-media/2D/
Home page di Java 2D API. Questa API (introdotta nel capitolo 1) fornisce funzionalità
complesse per le immagini bidimensionali.
java.sun.com/j2se/1.4.1/docs/guide/imageio/
19 - Multimedialità.p65
452
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
453
Contiene una rassegna di Java Image I/O API, che permette ai programmi di definire il
caricamento e il salvataggio di formati di immagini che non sono attualmente supportati
dalle librerie Java.
8.7 (Caso di studio facoltativo) Pensare a oggetti:
animazioni e suoni nella vista
Questo caso di studio si è focalizzato principalmente sul modello MVC della nostra simulazione di ascensore. Ora che abbiamo completato la progettazione del modello, ci occupiamo
della vista, che fornisce la presentazione visiva del modello. Nel nostro caso di studio, la vista,
incapsulata nella classe ElevatorView, è un oggetto JPanel contenente altri oggetti JPanel
“figli”, ognuno rappresentante un oggetto unico del modello MVC (per esempio, un oggetto
Person, un oggetto Button, ecc.). La classe ElevatorView è la classe più grande nel caso di
studio. In questa sezione, discutiamo le classi per la grafica e il suono usate da ElevatorView.
Presenteremo e spiegheremo il resto del codice sul sito Web www.apogeonline.com/libri/
02097/allegati/.
Nella sezione 3.7 del volume Tecniche di Base, abbiamo costruito il diagramma di classe
del nostro modello individuando sostantivi e frasi dalla specifica del problema. Abbiamo
ignorato parecchi di questi sostantivi, perché non erano associati con il modello MVC. Ora,
elenchiamo i sostantivi e le frasi che sono relativi alla visualizzazione del modello MVC:
•
•
•
visualizzazione
audio
musica dell’ascensore.
Il sostantivo “visualizzazione” corrisponde alla vista, o alla presentazione visuale, del modello
MVC. Come descritto nella sezione 2.7, la classe ElevatorView aggrega diverse classi. Il
sostantivo “audio” si riferisce agli effetti sonori che la nostra simulazione genera quando
accadono varie azioni; creeremo la classe SoundEffects per generare tali effetti sonori. La
frase “musica dell’ascensore” si riferisce alla musica che viene riprodotta quando una persona
viaggia sull’ascensore; useremo la classe SoundEffects anche per riprodurre questa musica.
La vista visualizza gli oggetti del modello MVC. Creeremo la classe ImagePanel per
rappresentare gli oggetti stazionari del modello, come ElevatorShaft. Creeremo la classe
MovingPanel, che estende ImagePanel, per rappresentare gli oggetti in movimento, come
Elevator. Infine, creeremo la classe AnimatedPanel, che estende MovingPanel, per rappresentare gli oggetti in movimento la cui immagine corrispondente cambia continuamente,
come Person (useremo diverse immagini per mostrare la persona che cammina e preme un
pulsante). Usando queste classi, presentiamo il diagramma di classe della vista per la nostra
simulazione nella figura 8.5.
Le note indicano i ruoli che hanno le classi nel sistema. Secondo il diagramma di classe,
la classe ElevatorView rappresenta la vista, le classi ImagePanel, MovingPanel e AnimatedPanel
si riferiscono alla grafica e la classe SoundEffects si riferisce ai suoni. La classe ElevatorView
contiene diverse istanze delle classi ImagePanel, MovingPanel e AnimatedPanel e un’istanza della classe SoundEffects. Nella versione finale del caso di studio, assoceremo ogni oggetto del modello con una classe corrispondente nella vista.
19 - Multimedialità.p65
453
19/12/2003, 9.36
454
CAPITOLO 8
javax.swing.JPanel
ImagePanel
1..*
1
ElevatorView
MovingPanel
1..*
1
1
AnimatedPanel
Grafica
Figura 8.5
Audio
1
1..*
1
SoundEffects
Vista
Diagramma di classe della vista del simulatore di ascensore
In questa sezione, discutiamo le classi ImagePanel, MovingPanel e AnimatedPanel per
spiegare la grafica e le animazioni. Poi, discuteremo la classe SoundEffects per spiegare le
funzionalità audio.
ImagePanel
La classe ElevatorView usa oggetti di sottoclassi di JPanel per rappresentare e visualizzare
ogni oggetto nel modello (come Elevator, Person, ecc.). La classe ImagePanel (figura 8.6)
è una sottoclasse di JPanel in grado di visualizzare un’immagine in una data posizione dello
schermo. La classe ElevatorView usa oggetti ImagePanel per rappresentare oggetti immobili nel modello, come ElevatorShaft e i due Floor. La classe ImagePanel contiene un
attributo intero, ID (riga 16), che dichiara un identificatore unico per tenere traccia
dell’ImagePanel nella vista. Ciò è utile quando diversi oggetti della stessa classe sono presenti nel modello, come diversi oggetti Person. La classe ImagePanel contiene l’oggetto position
della classe Point2D.Double (riga 19) per rappresentare la sua posizione sullo schermo. Vedremo più avanti che MovingPanel, che estende ImagePanel, dichiara la velocità con valori
double. Convertiremo le coordinate position in valori int per posizionare l’oggetto ImagePanel
sullo schermo (Java rappresenta le coordinate dello schermo come interi) nel metodo
setPosition (righe 90-94). La classe ImagePanel contiene anche un oggetto ImageIcon
chiamato imageIcon (riga 22), il cui metodo paintComponent (righe 54-60) visualizza l’icona
sullo schermo. Le righe 41-42 inizializzano ImageIcon usando un parametro stringa che
contiene il nome dell’immagine. Infine, la classe ImagePanel contiene l’oggetto panelChildren
della classe Set (riga 25) che memorizza tutti gli oggetti JPanel figli (o oggetti di una sottoclasse
di JPanel). Gli oggetti figli vengono visualizzati sopra il loro ImagePanel genitore: per
esempio, un oggetto Person dentro un Elevator. Il primo metodo add (righe 63-67) aggiunge un oggetto a panelChildren. Il secondo metodo add (righe 70-74) inserisce un
oggetto in panelChildren all’indice specificato. Il metodo setIcon (righe 84-87) imposta
imageIcon con una nuova immagine. Gli oggetti della classe AnimatedPanel usano
19 - Multimedialità.p65
454
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
455
ripetutamente il metodo setIcon per cambiare l’immagine visualizzata, in modo da eseguire
l’animazione.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Figura 8.6
19 - Multimedialità.p65
// ImagePanel.java
// sottoclasse di JPanel per posizionare e visualizzare ImageIcon
package com.deitel.jhtp5.elevator.view;
// package Java di base Java
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// package Java di estensione
import javax.swing.*;
public class ImagePanel extends JPanel {
// identificatore
private int ID;
// posizione sullo schermo
private Point2D.Double position;
// imageIcon da disegnare sullo schermo
private ImageIcon imageIcon;
// memorizza tutti i figli di ImagePanel
private Set panelChildren;
// costruttore che inizializza posizione e immagine
public ImagePanel( int identifier, String imageName )
{
super( null ); // specifica layout null
setOpaque( false ); // rende trasparente
// imposta identificatore unico
ID = identifier;
// imposta posizione
position = new Point2D.Double( 0, 0 );
setLocation( 0, 0 );
// crea ImageIcon con imageName dato
imageIcon = new ImageIcon(
getClass().getResource( imageName ) );
Image image = imageIcon.getImage();
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello (continua)
455
19/12/2003, 9.36
456
CAPITOLO 8
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Figura 8.6
19 - Multimedialità.p65
setSize(
image.getWidth( this ), image.getHeight( this ) );
// crea Set per memorizzare i figli di Panel
panelChildren = new HashSet();
} // fine costruttore di ImagePanel
// disegna Panel sullo schermo
public void paintComponent( Graphics g )
{
super.paintComponent( g );
// se l’immagine e’ pronta, disegnala sullo schermo
imageIcon.paintIcon( this, g, 0, 0 );
}
// aggiunge figlio di ImagePanel a ImagePanel
public void add( ImagePanel panel )
{
panelChildren.add( panel );
super.add( panel );
}
// aggiunge figlio di ImagePanel a ImagePanel all’indice dato
public void add( ImagePanel panel, int index )
{
panelChildren.add( panel );
super.add( panel, index );
}
// rimuove figlio di ImagePanel da ImagePanel
public void remove( ImagePanel panel )
{
panelChildren.remove( panel );
super.remove( panel );
}
// imposta ImageIcon corrente da visualizzare
public void setIcon( ImageIcon icon )
{
imageIcon = icon;
}
// imposta posizione sullo schermo
public void setPosition( double x, double y )
{
position.setLocation( x, y );
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello (continua)
456
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Figura 8.6
setLocation( ( int ) x,
457
( int ) y );
}
// ritorna identificatore di ImagePanel
public int getID()
{
return ID;
}
// ottiene posizione di ImagePanel
public Point2D.Double getPosition()
{
return position;
}
// ottiene imageIcon
public ImageIcon getImageIcon()
{
return imageIcon;
}
// ottiene Set di figli di ImagePanel
public Set getChildren()
{
return panelChildren;
}
}
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello
MovingPanel
La classe MovingPanel (figura 8.7) è una sottoclasse di ImagePanel in grado di cambiare la
sua posizione sullo schermo in base ai valori xVelocity e yVelocity (righe 20-21). La classe
ElevatorView usa oggetti MovingPanel per rappresentare oggetti mobili del modello, come
Elevator.
1
2
3
4
5
6
7
8
9
10
Figura 8.7
19 - Multimedialità.p65
// MovingPanel.java
// Sottoclasse di JPanel con funzionalità di movimento sullo schermo
package com.deitel.jhtp5.elevator.view;
// package Java di base
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// package Java di estensione
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello (continua)
457
19/12/2003, 9.36
458
CAPITOLO 8
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Figura 8.7
19 - Multimedialità.p65
import javax.swing.*;
public class MovingPanel extends ImagePanel {
// MovingPanel dovrebbe cambiare posizione?
private boolean moving;
// numero di pixel di cui MovingPanel si muove nelle direzioni
x e y
// per animationDelay millisecondi
private double xVelocity;
private double yVelocity;
// costruttore che inizializza posizione, velocità e immagine
public MovingPanel( int identifier, String imageName )
{
super( identifier, imageName );
// imposta velocità di MovingPanel
xVelocity = 0;
yVelocity = 0;
} // fine costruttore di MovingPanel
// aggiorna posizione e animazione di MovingPanel
public void animate()
{
// aggiorna posizione a seconda della velocità
if ( isMoving() ) {
double oldXPosition = getPosition().getX();
double oldYPosition = getPosition().getY();
setPosition( oldXPosition + xVelocity,
oldYPosition + yVelocity );
}
// aggiorna tutti i figli di MovingPanel
Iterator iterator = getChildren().iterator();
while ( iterator.hasNext() ) {
MovingPanel panel = ( MovingPanel ) iterator.next();
panel.animate();
}
} // fine metodo animate
// MovingPanel si sta muovendo sullo schermo?
public boolean isMoving()
{
return moving;
}
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello (continua)
458
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Figura 8.7
459
// imposta MovingPanel per muoversi sullo schermo
public void setMoving( boolean move )
{
moving = move;
}
// imposta le velocità x e y di MovingPanel
public void setVelocity( double x, double y )
{
xVelocity = x;
yVelocity = y;
}
// ritorna velocità x di MovingPanel
public double getXVelocity()
{
return xVelocity;
}
// ritorna velocità y di MovingPanel
public double getYVelocity()
{
return yVelocity;
}
}
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello
Il metodo animate (righe 35-53) muove MovingPanel secondo i valori attuali dei campi
e yVelocity. Se la variabile boolean moving (riga 16) è true, le righe 38-44
usano i campi xVelocity e yVelocity per determinare la prossima posizione di movingPanel.
Le righe 47-52 ripetono il procedimento per tutti i figli. Nella nostra simulazione, ElevatorView
invoca il metodo animate e il metodo paintComponent della classe ImagePanel ogni 50
millisecondi. Queste chiamate in rapida successione spostano l’oggetto MovingPanel.
xVelocity
AnimatedPanel
La classe AnimatedPanel (figura 8.8), che estende la classe MovingPanel, rappresenta un
oggetto animato del modello (cioè, oggetti in movimento la cui immagine corrispondente
cambia continuamente), come Person. La classe ElevatorView anima un AnimatedPanel
cambiando l’immagine associata con imageIcon.
1
2
3
4
Figura 8.8
19 - Multimedialità.p65
// AnimatedPanel.java
// Sottoclasse di Panel con funzionalità di animazione
package com.deitel.jhtp5.elevator.view;
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
459
19/12/2003, 9.36
460
CAPITOLO 8
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Figura 8.8
19 - Multimedialità.p65
// package Java di base
import java.awt.*;
import java.util.*;
// package Java di estensione
import javax.swing.*;
public class AnimatedPanel extends MovingPanel {
// ImageIcon deve essere animato?
private boolean animating;
// frequenza di cambio immagine
private int animationRate;
private int animationRateCounter;
private boolean cycleForward = true;
// ImageIcon individuali usati per l’animazione
private ImageIcon imageIcons[];
// memorizza tutte le sequenze di frame
private java.util.List frameSequences;
private int currentAnimation;
// deve continuare l’animazione alla fine del ciclo?
private boolean loop;
// deve visualizzare l’ultima immagine alla fine dell’animazione?
private boolean displayLastFrame;
// determina la prossima immagine da visualizzare
private int currentFrameCounter;
// costruttore che prende un array di nomi di file
public AnimatedPanel( int identifier, String imageName[] )
{
super( identifier, imageName[0] );
// crea oggetti ImageIcon dall’array di stringhe imageName
imageIcons = new ImageIcon[ imageName.length ];
for ( int i = 0; i < imageIcons.length; i++ ) {
imageIcons[i] = new ImageIcon(
getClass().getResource( imageName[i] ) );
}
frameSequences = new ArrayList();
} // fine costruttore di AnimatedPanel
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
460
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
Figura 8.8
19 - Multimedialità.p65
461
// aggiorna posizione dell’icona e immagine dell’animazione
public void animate()
{
super.animate();
// visualizza la prossima immagine se counter > frequenza
di animazione
if ( frameSequences != null && isAnimating() ) {
if ( animationRateCounter > animationRate ) {
animationRateCounter = 0;
determineNextFrame();
}
else
animationRateCounter++;
}
} // fine metodo animate
// determina prossima immagine dell’animazione
private void determineNextFrame()
{
int frameSequence[] =
( int[] ) frameSequences.get( currentAnimation );
// se non ci sono più immagini, determina immagine finale
// a meno che non si debba ripetere l’animazione
if ( currentFrameCounter >= frameSequence.length ) {
currentFrameCounter = 0;
// se loop è false, termina animazione
if ( !isLoop() ) {
setAnimating( false );
if ( isDisplayLastFrame() )
// visualizza ultima immagine della sequenza
currentFrameCounter = frameSequence.length - 1;
}
}
// imposta immagine corrente dell’animazione
setCurrentFrame( frameSequence[ currentFrameCounter ] );
currentFrameCounter++;
} // fine metodo determineNextFrame
// aggiunge animazione all’ ArrayList frameSequences
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
461
19/12/2003, 9.36
462
CAPITOLO 8
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
Figura 8.8
19 - Multimedialità.p65
public void addFrameSequence( int frameSequence[] )
{
frameSequences.add( frameSequence );
}
// chiede se AnimatedPanel sta eseguendo l’animazione
public boolean isAnimating()
{
return animating;
}
// imposta AnimatedPanel per eseguire l’animazione
public void setAnimating( boolean animate )
{
animating = animate;
}
// imposta ImageIcon corrente
public void setCurrentFrame( int frame )
{
setIcon( imageIcons[ frame ] );
}
// imposta frequenza di animazione
public void setAnimationRate( int rate )
{
animationRate = rate;
}
// ottiene frequenza di animazione
public int getAnimationRate()
{
return animationRate;
}
// imposta se l’animazione deve ripetersi
public void setLoop( boolean loopAnimation
{
loop = loopAnimation;
}
)
// ottiene se l’animazione deve ripetersi
public boolean isLoop()
{
return loop;
}
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
462
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
149
463
// ottiene se visualizzare l’ultima immagine alla fine
dell’animazione
private boolean isDisplayLastFrame()
{
return displayLastFrame;
}
150
151
152
153
154
155
// imposta se visualizzare l’ultima immagine alla fine
dell’animazione
public void setDisplayLastFrame( boolean displayFrame )
{
displayLastFrame = displayFrame;
}
156
157
158
159
160
161
162
163
164
165
166
167
168
// inizia ad eseguire la sequenza di animazione all’indice dato
public void playAnimation( int frameSequence )
{
currentAnimation = frameSequence;
currentFrameCounter = 0;
setAnimating( true );
}
}
Figura 8.8
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello
La classe AnimatedPanel sceglie un oggetto ImageIcon da disegnare sullo schermo tra
diversi oggetti ImageIcon memorizzati nell’array imageIcons (riga 23). La classe AnimatedPanel
determina l’oggetto ImageIcon secondo una sequenza di riferimenti, memorizzata nella lista
frameSequences (riga 26), che è un array di interi che memorizza la sequenza appropriata
per visualizzare gli oggetti ImageIcon: in particolare, ogni intero rappresenta l’indice di un
oggetto ImageIcon nell’array imageIcons. La figura 8.9 mostra la relazione tra imageIcons
e frameSequences (questo non è un diagramma UML). Per esempio, la sequenza numero
2 = { 2, 1, 0 }
si riferisce a { imageIcon[2], imageIcon[1], imageIcon[0] }, che porta alla sequenza
di immagini { C, B, A }. Nella vista, ogni immagine è un file .png unico. Il metodo
frameSequences
imageIcons
A
B
C
D
0
1
2
3
Figura 8.9
19 - Multimedialità.p65
0=
0
1
2
1=
0
1
3
2=
2
1
0
3=
3
2
2
1
0
0
image sequences
A
B C
A
B
D
C
B
A
D C
C
Relazione tra l’array imageIcons e la lista FrameSequences
463
19/12/2003, 9.36
B
A
A
464
CAPITOLO 8
addFrameSequences (righe 102-105) aggiunge una sequenza di animazione alla lista
frameSequences. Il metodo playAnimation (righe 162-167) fa partire l’animazione associata con il parametro frameSequences. Per esempio, supponiamo di avere un oggetto
AnimatedPanel chiamato personAnimatedPanel nella classe ElevatorView. Il frammen-
to di codice
animatedPanel.playAnimation( 1 );
genererebbe la sequenza di immagini { A, B, D, B, A } se usiamo la figura 8.9 come
riferimento.
Il metodo animate (righe 56-70) sovrascrive il metodo animate della superclasse
MovingPanel. Le righe 61-69 determinano la successiva immagine per l’animazione a seconda del campo animationRate, che è inversamente proporzionale alla velocità dell’animazione: un valore più alto per animationRate comporta un’animazione più lenta. Per esempio,
se animationRate vale 5, animate si sposta alla successiva immagine dell’animazione ogni
cinque volte viene invocato. Usando questa logica, la frequenza di animazione viene massimizzata
quando animationRate ha un valore 1, perché la successiva immagine viene determinata
ogni volta che viene chiamato animate.
Il metodo animate chiama determineNextFrame (righe 73-99) per determinare la successiva immagine da visualizzare: in particolare, viene chiamato il metodo setCurrentFrame
(righe 120-123) che imposta imageIcon (l’immagine correntemente visualizzata) all’immagine ritornata dalla corrente sequenza di animazione. Le righe 84-92 di determineNextFrame
vengono usate per ripetere l’animazione. Se loop vale false, l’animazione termina dopo una
iterazione. L’ultima immagine della sequenza viene visualizzata se displayLastFrame vale
true, e, se vale false, viene visualizzata la prima immagine della sequenza. Se loop vale
true, l’animazione si ripete finché non viene fermata esplicitamente.
Effetti sonori
Vediamo ora come generare dei suoni nella nostra simulazione. La classe SoundEffects
(figura 8.10) trasforma file audio (.au), wave (.wav) e MIDI (.mid) contenenti suoni come
il campanello, i passi della persona e la musica dell’ascensore, in oggetti java.applet.AudioClip.
L’oggetto ElevatorView riprodurrà gli oggetti AudioClip per generare i suoni. Tutti i file
dei suoni sono nella struttura di directory
com/deitel/jhtp5/elevator/view/sounds
Nella nostra simulazione, useremo suoni e file MIDI disponibili gratuitamente al sito Web di
Microsoft:
msdn.microsoft.com/downloads/default.asp
Per scaricare questi suoni, fate clic su “Graphics and Multimedia”, “Multimedia (General)”, e
poi “Sounds”.
La classe SoundEffects contiene i metodi getAudioClip (righe 16-27), che usa il metodo statico newAudioClip (della classe java.applet.Applet) per ritornare un oggetto
AudioClip usando il parametro soundFile. Il metodo setPathPrefix (righe 30-33) permette di cambiare directory del file audio (utile se vogliamo dividere i nostri file sonori tra
directory diverse).
19 - Multimedialità.p65
464
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
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
Figura 8.10
465
// SoundEffects.java
// Ritorna oggetti AudioClip
package com.deitel.jhtp5.elevator.view;
// package Java di base
import java.applet.*;
public class SoundEffects {
// posizione dei file audio
private String prefix = “”;
public SoundEffects() {}
// ottiene AudioClip associato con soundFile
public AudioClip getAudioClip( String soundFile )
{
try {
return Applet.newAudioClip( getClass().getResource(
prefix + soundFile ) );
}
// ritorna null se il file audio non esiste
catch ( NullPointerException nullPointerException ) {
return null;
}
}
// imposta prefisso per la posizione di soundFile
public void setPathPrefix( String string )
{
prefix = string;
}
}
La classe SoundEffects ritorna oggetti AudioClip
Conclusione
Avete appena completato un procedimento di progettazione orientata agli oggetti che aveva
lo scopo di prepararvi per le sfide dei progetti di livello industriale. Speriamo che abbiate
trovato le sezioni “Pensare a oggetti” informative e utili come complemento al materiale
presentato nei vari capitoli. Inoltre, speriamo vi siate divertiti a progettare il sistema usando
UML. Il linguaggio UML è stato adottato come standard dall’industria del software mondiale per la modellazione di software orientato agli oggetti.
Anche se avete completato la fase di progettazione, avete solo sfiorato il processo di
implementazione. Consultate il sito Web www.apogeonline.com/libri/02097/allegati/ per la completa implementazione del progetto, e le traduzioni dei diagrammi UML in
un programma Java completo per la simulazione dell’ascensore. Studiare l’implementazione
19 - Multimedialità.p65
465
19/12/2003, 9.36
466
CAPITOLO 8
rafforzerà le abilità nella programmazione che avete sviluppato leggendo il libro e migliorerà la vostra comprensione del processo di progettazione.
Esercizi di autovalutazione
8.1
Completate le seguenti frasi:
a) Il metodo ____________ di Applet carica un’immagine all’interno di un applet.
b) Il metodo ____________ di Applet ritorna, come un oggetto della classe URL, la posizione in Internet del file HTML che ha invocato l’applet.
c) Il metodo ____________ di Graphics visualizza un’immagine all’interno di un applet.
d) Java fornisce due meccanismi per riprodurre suoni all’interno di un applet: il metodo play
di Applet e il metodo play dell’interfaccia ____________.
e) Una ____________ è un’immagine che presenta delle aree attive, sulle quali l’utente può
fare clic per portare a termine un’attività, come per esempio il caricamento di una diversa
pagina Web.
f ) Il metodo ____________ della classe ImageIcon visualizza l’immagine di ImageIcon.
g) Java supporta diversi formati di immagini, tra cui ____________, ____________ e
____________
8.2
Determinate se le seguenti affermazioni sono vere o false. Se sono false, spiegate il perché.
a) Un suono viene eliminato quando ha finito di essere riprodotto.
b) La classe ImageIcon fornisce dei costruttori che permettono ad un oggetto ImageIcon di
essere inizializzato solo con un’immagine dal computer locale.
Risposte agli esercizi di autovalutazione
8.1
a)
getImage.
b) getDocumentBase. c) drawImage. d) AudioClip. e) mappa di immagini. f )
g) Graphics Interchange Format (GIF), Joint Photographic Experts Group (JPEG),
Portable Network Graphics (PNG).
paintIcon.
8.2
a) Falso. Il suono verrà contrassegnato per essere eliminato (se non è referenziato da un AudioClip)
e verrà eliminato quando il garbage collector è in grado di essere eseguito.
b) Falso. ImageIcon può caricare anche immagini da Internet.
Esercizi
8.3
Spiegate come sia possibile rendere un’animazione “browser friendly”.
8.4
Descrivete i metodi Java per riprodurre e utilizzare i clip audio.
8.5
gini.
Spiegate l’utilizzo delle mappe di immagini. Elencate diverse applicazioni di mappe di imma-
8.6
(Cancellazione casuale di un’immagine) Supponete che un’immagine venga visualizzata in un’area
rettangolare dello schermo. Un modo per cancellare l’immagine è quello di impostare immediatamente ogni pixel sullo stesso colore, ma questo produce un effetto visivo noioso. Scrivete un programma
Java che visualizzi un’immagine e poi la cancelli, utilizzando la generazione casuale dei numeri per
selezionare i singoli pixel da cancellare. Dopo che gran parte dell’immagine è stata cancellata, cancellate tutti i pixel rimanenti in una sola volta. È possibile fare riferimento ai singoli pixel attraverso una riga
che inizia e finisce allo stesso punto. Potete provare diverse varianti di questo problema. Per esempio,
potreste visualizzare le righe in modo casuale, oppure potreste visualizzare le figure in modo casuale per
cancellare intere regioni dello schermo.
19 - Multimedialità.p65
466
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
467
8.7
(Text Flasher) Create un programma Java che lampeggi ripetutamente del testo sullo schermo.
Completate il testo con un’immagine di sfondo di un unico colore. Permettete all’utente di controllare
la velocità di lampeggiamento e di scegliere il colore o il motivo di sfondo.
8.8
(Image Flasher) Create un programma Java che lampeggi ripetutamente un’immagine sullo
schermo. Completate l’immagine con un’immagine di sfondo di un unico colore.
8.9
(Orologio digitale) Implementate un programma che visualizzi un orologio digitale sullo schermo. Potete aggiungere delle opzioni per mostrare il giorno, il mese e l’anno, inserire una funzione di
allarme, riprodurre determinati suoni in momenti predefiniti, e così via.
8.10 (Richiamare l’attenzione su di un’immagine) Se volete enfatizzare un’immagine, potete posizionare intorno a questa immagine una fila di luci, che lampeggino all’unisono, oppure che si accendano
una dopo l’altra in sequenza.
8.11
(Zooming) Create un programma che permetta di eseguire lo zoom di un’immagine.
Sezione speciale: progetti multimediali avanzati
Gli esercizi precedenti servono a verificare la comprensione da parte del lettore dei concetti fondamentali del capitolo. Questa sezione contiene una collezione di progetti multimediali avanzati che il lettore
dovrebbe trovare stimolanti e divertenti. La difficoltà dei problemi è considerevolmente varia. Alcuni
richiedono un’ora o due di progettazione e implementazione. Altri sono utili come progetti di laboratorio che possono richiedere due o tre settimane di studio e implementazione.
8.12 (Animazione) Create un programma generico di animazione Java. Il programma dovrebbe permettere all’utente di specificare la sequenza di frame da visualizzare, la velocità con cui le immagini
devono essere visualizzate, i suoni da riprodurre mentre l’animazione è in esecuzione, e così via.
8.13 (Transizione casuale di un’immagine) Questo è un effetto visivo molto interessante. Se state
visualizzando un’immagine in una data area dello schermo, e volete passare a un’altra immagine nella
stessa area dello schermo, memorizzate la nuova immagine in un buffer separato, e copiate in modo
casuale i pixel della nuova immagine in modo da visualizzare l’area che copre i pixel che si trovavano
precedentemente in quelle posizioni. Quando la maggior parte dei pixel sono stati copiati, copiate
l’intera nuova immagine nell’area di visualizzazione, così da assicurarvi che state visualizzando la nuova
immagine completa. Per implementare questo programma potrebbe essere necessario usare le classi
PixelGrabber e MemoryImageSource (consultate la documentazione Java API per la descrizione di
queste classi). Potete provare diverse varianti di questo problema. Per esempio, potreste selezionare
tutti i pixel in una riga selezionata a caso, oppure creare la nuova immagine e sovrapporre questi pixel
alle posizioni corrispondenti nella vecchia immagine.
8.14 (Audio in sottofondo) Aggiungete dell’audio di sottofondo a una delle vostre applicazioni preferite, utilizzando il metodo loop della classe AudioClip per riprodurre il suono di sottofondo mentre
interagite con l’applicazione in modo normale.
8.15 (Marquee scorrevole) Create un programma Java che faccia scorrere dei caratteri puntati da
destra verso sinistra (o da sinistra verso destra) all’interno di una fascia di tipo Marquee. Se volete,
visualizzate il testo a ciclo continuo, così che quando il testo esce da una parte dello schermo riappaia
dalla parte opposta.
8.16 (Immagine scorrevole) Create un programma Java che faccia scorrere un’immagine lungo uno
schermo di tipo Marquee.
8.17 (Orologio analogico) Create un programma Java che visualizzi un orologio analogico con le
lancette dell’ora, dei minuti e dei secondi. Le lancette si devono spostare in modo corretto in base allo
scorrere del tempo.
19 - Multimedialità.p65
467
19/12/2003, 9.36
468
CAPITOLO 8
8.18 (Caleidoscopio dinamico) Sviluppate un programma caleidoscopio che visualizzi delle immagini
che simulano il popolare giocattolo per bambini. Incorporate degli effetti sonori che corrispondano
alle immagini che variano in modo dinamico.
8.19 (Generatore di puzzle) Create un generatore di puzzle Java. L’utente specifica un’immagine, che
il programma carica e visualizza. Il programma poi suddivide l’immagine in tante forme selezionate in
modo casuale, e le mischia. L’utente usa il mouse per spostare i vari pezzi del puzzle, al fine di ricomporre
l’immagine. Aggiungete i suoni appropriati via via che i pezzi vengono spostati e rimessi al loro posto.
Potete tenere delle etichette su ogni pezzo con l’esatta posizione, e poi usare effetti sonori per aiutare
l’utente a rimettere i pezzi nella corretta posizione.
8.20 (Generatore di labirinti) Sviluppate un generatore di labirinti multimediale e un programma di
attraversamento del labirinto. Permettete all’utente di personalizzare il labirinto, specificando il numero di righe e di colonne, e indicando il livello di difficoltà. Create un topolino animato che percorra il
labirinto. Utilizzate l’audio per enfatizzare i movimenti del topolino.
8.21 (Slot machine) Sviluppate una simulazione multimediale di una slot machine. Create tre ruote
che girano, ognuna con disegni di frutti e di altri simboli. Utilizzate la generazione casuale dei numeri
per simulare il giro di ogni ruota e la sua interruzione su uno dei simboli.
8.22 (Corsa dei cavalli) Create una simulazione Java di una corsa dei cavalli, con più partecipanti.
Usate l’audio per simulare un annunciatore che descrive la gara e annuncia i risultati finali. Riproducete i suoni corretti, per indicare la posizione di ognuno dei concorrenti durante la gara. Provate a
simulare il tipo di gare che si vedono spesso nelle feste di paese. I giocatori possono, a turno, fare
avanzare il proprio cavallo con il mouse.
8.23 (Shuffleboard) Sviluppate una simulazione multimediale del gioco shuffleboard. [Nota: Questo
gioco, in origine americano, è spesso giocato a bordo delle navi di crociera, e consiste nello spingere,
con apposite stecche, dei dischi di legno entro figure geometriche numerate.] Usate i corretti effetti
audio e video.
8.24 (Biliardo) Create una simulazione multimediale del gioco del biliardo. Ogni giocatore usa a
turno il mouse, per posizionare la stecca e colpire la palla con la giusta angolazione, affinché finisca in
una delle buche. Il programma deve tenere aggiornati i punteggi.
8.25 (Artista) Progettate un programma Java che offra a un artista una grande quantità di funzionalità per disegnare, utilizzare immagini, usare animazioni e così via, al fine di creare opere multimediali
dinamiche.
8.26 (Fuochi d’artificio) Create un programma Java che possa essere utilizzato per creare uno spettacolo di fuochi d’artificio. Create varie dimostrazioni e cercate di ottenere gli effetti più spettacolari.
8.27 (Arredatore) Sviluppate un programma Java che aiuti a disporre l’arredamento di una casa.
Aggiungete delle caratteristiche che permettano di ottenere la migliore disposizione possibile.
8.28 (Parole crociate) Sviluppate un programma di parole crociate, che permetta al giocatore di
inserire e cancellare le parole in modo semplice. Collegate il programma a un grande dizionario su
computer. Il programma dovrebbe essere in grado di suggerire le parole in base alle lettere che sono già
state inserite.
8.29 (Gioco del 15) Scrivete un programma Java multimediale che permetta all’utente di giocare il
gioco del 15. Il gioco si compone di una scacchiera 4 × 4, per un totale di 16 caselle. Una di queste
caselle è vuota, mentre le altre sono occupate da 15 numeri (dall’1 al 15). Ogni casella vicina a quella
vuota può essere spostata al posto di quella vuota facendovi clic. Il programma deve creare la scacchiera
con le caselle in ordine sparso. L’obiettivo è quello di sistemare tutte le caselle in ordine sequenziale,
riga per riga.
19 - Multimedialità.p65
468
19/12/2003, 9.36
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
469
8.30 (Tempi di reazione) Create un programma Java che sposti una figura creata in modo casuale
all’interno dello schermo. L’utente sposta il mouse per cercare di fermare la figura. La velocità e la
dimensione della figura possono essere modificate. Producete delle statistiche circa il tempo tipicamente impiegato dall’utente per catturare una figura di una data dimensione. L’utente avrà probabilmente più difficoltà a catturare le figure veloci di piccole dimensioni.
8.31 (Calendario/Scadenziario) Create un calendario generico con uno scadenziario. Utilizzare suoni
e immagini. Per esempio, il programma dovrebbe suonare “Tanti Auguri” quando l’utente lo apre il
giorno del suo compleanno. Il programma deve riprodurre immagini e suoni associati a eventi importanti, e dovrebbe ricordare in anticipo questi eventi all’utente.
8.32 (Immagini rotanti) Create un programma Java che permetta di ruotare un’immagine per un
determinato numero di gradi (al massimo 360). Il programma deve permettere di specificare che l’immagine dovrebbe ruotare in continuazione. Il programma deve permettere di regolare la velocità di
rotazione in modo dinamico.
8.33 (Colorare immagini in bianco e nero) Create un programma Java che permetta di colorare delle
immagini in bianco e nero. Fornite una paletta di colori per la selezione dei colori. Il programma deve
permettere di applicare colori diversi alle varie regioni di un’immagine.
8.34 (Simulatore Simpletron multimediale) Modificate il simulatore Simpletron sviluppato nel volume Tecniche di base, al fine di includervi delle funzionalità multimediali. Aggiungete suoni simili a
quelli di un computer, per indicare che il Simpletron sta eseguendo le istruzioni. Aggiungete il suono
di un vetro che si rompe quando avviene un errore grave. Utilizzate delle luci lampeggianti per indicare
quali celle della memoria e/o quali registri sono attualmente in uso. Usate anche altre tecniche multimediali
per rendere il simulatore più utile ai fini educativi.
19 - Multimedialità.p65
469
19/12/2003, 9.36