package memory.chants;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import memory.lecteur.LecteurEvent;
import memory.lecteur.LecteurException;
import memory.lecteur.LecteurListener;
import memory.lecteur.LecteurMP3;
/**
* @author patrice
* Une classe pour représenter graphiquement un lecteur de MP3
* Implémentations :
* ActionListener pour réagir aux clics sur des boutons
* ChangeListener pour réagir au JSlider (volume)
* LecteurListener pour réagir aux modifications du lecteur
* Observable pour permettre à la sonothèque de passer automatiquement au chant
* suivant (pour les autres modules ce n'est pas nécessaire)
*/
public class JLecteurMP3 extends JPanel implements ActionListener, ChangeListener, LecteurListener, Observable
{
//un lecteur de MP3
private LecteurMP3 lecteur;
//les fichiers son, le nom de l'oiseau, le message "quel est cet oiseau"
private String mp3, nom, titre, nomLatin;
//une barre de progression du chant de l'oiseau
private JProgressBar progression = new JProgressBar(0,100);
//les boutons play, pause, stop
private JButton btnPlay,btnPause,btnArret;
//des booléens qui indiquent si on a appuyé sur pause, sur stop
//ou si le chant doit boucler (reprise à la fin du morceau)
//et aussi pour afficher ou non la réponse (jeu ou galerie, sonothèque ...)
//et enfin pour savoir si on a cliqué sur la barre de progression pour redémarrer le chant
//à un endroit particulier (en attente de réalisation)
private boolean paused = false, stopped = true, boucle = true, cache = false;
//l'état des trois boutons sur 3 bits (001 = btnArret disabled, btnPause disabled, btnPlay enabled)
private int etatBoutons;
//un contrôle de volume
private JSlider volume;
//le panneau pour contenir les boutons
private JPanel boutons;
//un JLabel pour contenir l'image
private JLabel lblImage;
//les diverses images pour le jeu et pour les boutons
private ImageIcon image, //l'image de l'oiseau
// defaut, //l'image à afficher pendant le jeu (avant de connaître le nom de l'oiseau)
//l'icone du bouton play
iconPlay = new ImageIcon("." + File.separator + "icones" + File.separator + "play.png"),
// iconPlay = new ImageIcon(JLecteurMP3.class.getResource ("play.png")),
//l'icone du bouton pause
iconPause = new ImageIcon("." + File.separator + "icones" + File.separator + "pause.png"),
//l'icone du bouton arret
iconStop = new ImageIcon("." + File.separator + "icones" + File.separator + "arret.png");
private static ImageIcon defaut;
//le JPanel pour contenir le JLabel
private JPanel panImage;
//la bordure
private BordureDouble bordure;
//la liste des observateurs du lecteur (la sonothèque ... )
private List<Observateur> observateurs = new ArrayList<Observateur>();
/************** LE CONSTRUCTEUR ***************************************************************/
/**
* constructeur du lecteur avec un oiseau
* @param oiseau l'oiseau concerné
*/
public JLecteurMP3(Oiseau oiseau)
{
//le lecteur de base
this.lecteur = new LecteurMP3();
//le lecteur est écouté par l'interface
this.lecteur.addLecteurListener(this);
//les données : le chant, l'image, le nom
this.mp3 = oiseau.getChant();
this.image = oiseau.getImage();
this.nom = oiseau.getNom();
this.nomLatin = oiseau.getNomLatin();
// if (nomLatin.length() == 0) System.out.println(nom);
//l'image anonyme
initImageDefaut();
//le panneau image
this.panImage = new JPanel();
this.bordure = new BordureDouble(this.cache ? this.titre : this.nom,
this.cache ? this.titre : this.nomLatin);
//on applique la bordure au panneau
this.panImage.setBorder (bordure);
//on donne un FlowLayout au panneau
this.panImage.setLayout(new FlowLayout());
//l'image, si elle est cachée, est remplacée par une image anonyme
this.lblImage = new JLabel(this.cache ? defaut : this.image);
this.panImage.add(this.lblImage);
//le bouton de volume
this.volume = new JSlider(JSlider.VERTICAL,0,100,85);
this.volume.setMajorTickSpacing(20);
this.volume.setMinorTickSpacing(5);
this.volume.setPaintTicks(true);
this.volume.setPaintLabels(true);
//... que l'on écoute
this.volume.addChangeListener(this);
//les boutons d'action
//démarrer la lecture
this.btnPlay = new JButton();
this.btnPlay.setIcon(this.iconPlay);
this.btnPlay.addActionListener(this);
this.btnPlay.setEnabled(true);
//faire une pause dans la lecture
this.btnPause = new JButton();
this.btnPause.setIcon(this.iconPause);
this.btnPause.addActionListener(this);
this.btnPause.setEnabled(false);
//arrêter la lecture
this.btnArret = new JButton();
this.btnArret.setIcon(this.iconStop);
this.btnArret.addActionListener(this);
this.btnArret.setEnabled(false);
//play = enabled, pause = disabled, stop = disabled ===> etatBoutons = 001
this.etatBoutons = 1;
//le panneau comportant les boutons d'action
this.boutons = new JPanel();
this.boutons.add(this.btnPlay);
this.boutons.add(this.btnPause);
this.boutons.add(this.btnArret);
this.boutons.setBorder (BorderFactory.createLineBorder(Color.black,3));
//on construit le JLecteurMP3
//un BorderLayout
this.setLayout(new BorderLayout());
//la barre de progression
this.add(this.progression,BorderLayout.NORTH);
setForeground(Color.CYAN);
//le voulume
this.add(this.volume,BorderLayout.EAST);
this.add(this.boutons,BorderLayout.SOUTH);
//le panneau image
this.add(this.panImage,BorderLayout.CENTER);
}
/************** LES ASSESSEURS ***************************************************************/
//on construit l'image anonyme
private static void initImageDefaut()
{
if (defaut == null)
{
ImageIcon image = new ImageIcon("." + File.separator + "icones" + File.separator + "defaut1.jpg");
int largeur = image.getIconWidth ();
int hauteur = image.getIconHeight ();
int largeurImage = 250;
if (largeur > hauteur)
{
hauteur = (hauteur * largeurImage)/largeur;
largeur = largeurImage ;
}
else
{
largeur = (largeur * largeurImage)/hauteur;
hauteur = largeurImage;
}
defaut = new ImageIcon(image.getImage().getScaledInstance(largeur,hauteur,Image.SCALE_DEFAULT));
}
}
/************** LES MODIFICATEURS ************************************************************/
/**
* On change la bordure car quand on change de langue, le nom change
*/
public void changeBordure()
{
if (this.cache)
{
this.bordure.setTitreHaut(this.titre);
this.bordure.setTitreBas("");
}
else
{
this.bordure.setTitreHaut(this.nom);
this.bordure.setTitreBas(this.nomLatin);
}
this.panImage.repaint();
}
/************** LES ACTIONS LIEES AU LECTEUR **************************************************/
/**
* on lance le chant d'oiseau
* Pas d'argument donc donc lance le chant au début
*/
private void joueChant()
{
// this.stopped = false;
try
{
this.lecteur.open(new File(this.mp3));
this.lecteur.play();
}
catch (LecteurException e)
{
e.printStackTrace();
}
}
/**
* on lance le chant d'oiseau
* si x = 0, on lance le chant au début sinon on cemmence à x%
* @param x pourcentage de chant qu'il faut jouer (en fait on joue (1-x) secondes du morceau)
*/
private void joueChant (double x)
{
try
{
// this.lecteur.open(Fichier.chargerChant(mp3));
this.lecteur.open(new File(this.mp3));
this.lecteur.skip (x);
this.lecteur.play ();
}
catch (LecteurException e)
{
e.printStackTrace();
}
}
/**
* on arrête le chant
*/
public void stopChant()
{
this.progression.setValue(0);
this.lecteur.stop();
}
/**
* les actions sur les boutons
* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*
* Chaque clic sur un des boutons déclenche un évènement du Lecteur qui en retour agit sur
* le JLecteur. Il faudrait que ces évènements soient filtrés via un booléen pour ne pas
* mélanger, par exemple, un arrêt du lecteur du fait de la fin du chant et un arrêt de
* l'utilisateur
*/
@Override
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == this.btnPlay)
{
joue();
return;
}
if (evt.getSource() == this.btnPause)
{
this.stopped = false;
this.paused = ! this.paused;
if (this.paused)
{
this.btnPause.setIcon(this.iconPlay);
if (this.etatBoutons != 2) gererBoutons(false,true,false);
pauseChant();
}
else
{
this.btnPause.setIcon(this.iconPause);
if (this.etatBoutons != 6) gererBoutons(false,true,true);
resumeChant();
}
return;
}
if (evt.getSource() == this.btnArret)
{
stop();
return;
}
}
//on joue le chant depuis le début
public void joue()
{
this.stopped = false;
this.paused = false;
this.btnPause.setIcon(this.iconPause);
if (this.etatBoutons != 6) gererBoutons(false,true,true);
joueChant();
}
//on joue le chant à partir de x (un nombre entre 0 et 1)
public void joue (double x)
{
this.stopped = false;
this.paused = false;
this.btnPause.setIcon(this.iconPause);
if (this.etatBoutons != 6) gererBoutons(false,true,true);
joueChant(x);
}
public void stop()
{
this.stopped = true;
this.paused = false;
this.btnPause.setIcon(this.iconPause);
if (this.etatBoutons != 1) gererBoutons(true, false, false);
stopChant();
}
/**
* reprise après pause
*/
public void resumeChant()
{
this.lecteur.resume();
}
/**
* pause dans la lecture
*/
public void pauseChant()
{
this.lecteur.pause();
}
/**
* on modifie le volume
* (non-Javadoc)
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
@Override
public void stateChanged(ChangeEvent evt)
{
if (evt.getSource() == this.volume)
{
double valeur = (double) this.volume.getValue() / 100 ;
try
{
this.lecteur.setGain(valeur);
}
catch (LecteurException e)
{
e.printStackTrace();
}
}
}
/**
* un test
* @param args
*/
public static void main(String[] args)
{
GestionDonnees gd = new GestionDonnees();
Oiseau oiseau = new Oiseau(gd.getNom(0), gd.getNomLatin(0), gd.getFichierSon(0), gd.getFichierImage(0), 300);
final JLecteurMP3 lecteurMP3 = new JLecteurMP3(oiseau);
lecteurMP3.setCache(true);
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame fenetre = new JFrame();
fenetre.add(lecteurMP3);
fenetre.pack();
fenetre.setVisible(true);
fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
lecteurMP3.setBoucle (true);
lecteurMP3.joue(0.90);
}
/**
* ici les échanges LecteurMP3 vers JlecteurMP3
* En fait seuls les codes EOM et STOPPED sont à traiter car tous les autres codes soit n'ont
* pas d'influence sur le composant (OPENED, PLAYING) soit sont commandés par lui (OPENED,
* PAUSED, RESUMED, GAIN)
* Seul STOPPED est soit commandé par le composant (ne rien faire) soit est lié à la
* fin du morceau. Dans ce cas soit on rejoue le morceau, soit on passe à un autre.
*/
@Override
public void stateUpdated(LecteurEvent event)
{
if ((event.getCode () == LecteurEvent.STOPPED) && (! this.stopped))
{
if (boucle)
{
try
{
this.lecteur.play();
}
catch (LecteurException e)
{
e.printStackTrace();
}
}
else updateObservateurs();
}
}
private void gererBoutons(final boolean etatRun, final boolean etatPause, final boolean etatStop)
{
this.etatBoutons = (etatStop ? 4 : 0) + (etatPause ? 2 : 0) + (etatRun ? 1 : 0);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JLecteurMP3.this.btnPlay.setEnabled(etatRun) ;
JLecteurMP3.this.btnPause.setEnabled(etatPause);
JLecteurMP3.this.btnArret.setEnabled(etatStop);
revalidate();
}
});
}
/**
* on modifie le statut de l'image : cachée elle est remplacée par une image anonyme et le nom
* par "quel est le nom de cet oiseau ?" (dans la langue choisie)
* il faut penser à changer l'affichage
* @param b
*/
public void setCache(boolean b)
{
//on change la valeur de cache et l'affichage
this.cache = b;
//si l'image est cachée
if (b)
{
this.lblImage.setIcon(defaut);
this.bordure.setTitreHaut(titre);
this.bordure.setTitreBas("");
}
else
{
this.lblImage.setIcon(this.image);
this.bordure.setTitreHaut(this.nom);
this.bordure.setTitreBas(this.nomLatin);
}
}
/**
* on change d'oiseau
* @param oiseau l'oiseau
*/
public void change(Oiseau oiseau)
{
this.image = oiseau.getImage();
this.mp3 = oiseau.getChant();
this.nom = oiseau.getNom();
this.nomLatin = oiseau.getNomLatin();
}
/**
* On change le nom (changement de langue)
* @param nom2 le nom de l'oiseau
*/
public void setNom(String nom2)
{
this.nom = nom2;
}
/**
* On change le titre si l'image est cachée (changement de langue)
* @param titre la question "quel est cet oiseau ?" dans diverses langues
*/
public void setTitre(String titre)
{
this.titre = titre;
}
/**
* On ajoute un observateur
* (non-Javadoc)
* @see memory.chants.Observable#addObservateur(memory.chants.Observateur)
*/
@Override
public void addObservateur (Observateur obs)
{
this.observateurs.add (obs);
}
/**
* On prévient les observateurs de l'arrêt du chant
* (non-Javadoc)
* @see memory.chants.Observable#updateObservateurs()
*/
@Override
public void updateObservateurs ()
{
for (Observateur obs : this.observateurs)
{
obs.update ();
}
}
/**
* On retire tous les observateurs
* (non-Javadoc)
* @see memory.chants.Observable#delObservateurs()
*/
@Override
public void delObservateurs ()
{
this.observateurs = new ArrayList<Observateur>();
}
/**
* @param boucle on boucle ou non sur le chant d'oiseau
*/
public void setBoucle (boolean boucle)
{
this.boucle = boucle ;
}
/**
* progression dans la lecture
* @see memory.lecteur.LecteurListener#progress(java.lang.Long)
*/
@Override
public void progress (Long microseconds)
{
final int progres = (int)(microseconds/1000000);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JLecteurMP3.this.progression.setValue(progres) ;
}
});
}
/* (non-Javadoc)
* @see memory.lecteur.LecteurListener#opened(java.lang.Long)
*/
@Override
public void opened (Long microsecondes)
{
final int duree = (int)(microsecondes/1000000);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JLecteurMP3.this.progression.setMaximum(duree);
}
});
}
}