Partage
  • Partager sur Facebook
  • Partager sur Twitter

Obtenir le niveau / volume d'un son

getLevel() de dataLine ne fontionne plus...

Sujet résolu
    21 février 2015 à 22:08:45

    Bonsoir amis Zéros,

    Je suis en train de suivre les différents tutoriels pour lire un fichier son en utilisant les outils "bas niveau" de JAVA Sound. Jusqu'à présent, ça ne se passe pas trop mal et j'arrive à lire un fichier wav, et connaître le temps écoulé. Cependant, je bloque sur la fonction getLevel() qui permet en principe de connaître le niveau sonore mais qui ne fonctionne plus, ce qui est confirmé à travers les différents forums que j'ai pu lire.

    Cependant, l'information de niveau sonore doit bien exister puisque les hauts parleurs du PC rendent physiquement le son et que le VUmetre de Windows ou de n'importe quel autre logiciel de son affiche le signal reçu. La question est donc : comment obtenir cette information ?

    Je ne veux pas juste copier un bout de code auquel je ne comprends rien dans ma classe. Je voudrais que quelqu'un m'explique pas à pas comment obtenir l'information à partir des bits qui défilent à la lecture et sont envoyés vers les hauts parleurs...

    Merci d'avance pour ceux et celles qui prendront le temps de m'aider. Je copie ci après la classe AudioPlayer qui me sert de base pour lire le son. Rien d'exceptionnel dans cette classe, on la retrouve dans tous les tutoriels permettant de démarrer avec JAVA Sound.

    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    
    import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.DataLine;
    import javax.sound.sampled.LineUnavailableException;
    import javax.sound.sampled.SourceDataLine;
    import javax.sound.sampled.UnsupportedAudioFileException;
    
    public class AudioPlayer implements Runnable, Observable {
    	protected SourceDataLine line;
    	private static File file;
    	protected AudioInputStream audioInputStream;
    	protected AudioFormat audioFormat;
    	protected long frames;
    	protected double durationInSeconds, elapsedTime, remainedTime;
    	protected boolean stop=false;
    	private ArrayList<Observateur> listObservateur = new ArrayList<Observateur>();
    	
    	public void stop() {
    		stop=true;
    	}
    
    	public void init () {
    		//Obtention d'un flux audio a partir d'un fichier (objet file)
    		try {
    			audioInputStream = AudioSystem.getAudioInputStream(file);
    		} catch (UnsupportedAudioFileException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    		//Il est necessaire de connaitre le format audio du fichier d'entree
    		//pour permettre a JAVA de creer l'objet DataLine adequat
    		audioFormat = audioInputStream.getFormat();
    		System.out.println(audioFormat);
    		
    		//On calcule la duree totale du morceau
    		//en divisant le nombre total de frames par le frame rate par secondes
    		frames = audioInputStream.getFrameLength();
    		durationInSeconds=frames/audioFormat.getFrameRate();
    		System.out.println("Duration : " + durationInSeconds);
    		
    		//En plus du format du flux audio d'entree,
    		//il est necessaire de specifier le type de DataLine que l'on veut
    		//Ici, le DataLine souhaite est un SourceDataLine
    		//qui permet la lecture (TargetDataLine permet l'enregistrement)
    		DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
    		System.out.println(info);
    		
    		//On recupere le DataLine et on l'ouvre
    		
    		try {
    			line=(SourceDataLine) AudioSystem.getLine(info);
    		} catch (LineUnavailableException e) {
    			e.printStackTrace();
    		}
    		
    	}
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		stop=false;
    		//Avant toute chose, il faut ouvrir la ligne avec le bon format audio
    		try {
    			line.open(audioFormat);
    		} catch (LineUnavailableException e) {
    			e.printStackTrace();
    		}
    		
    		//Pour que le flux soit effectivement redirige vers la carte son,
    		//il faut demarrer la ligne
    		line.start();
    		
    		//Il faut ensuite ecrire sur la ligne.
    		//On travaille comme sur un InputStream classique
    		try {
    			byte bytes[] = new byte[1024];
    			int bytesRead=0;
    			while (((bytesRead=audioInputStream.read(bytes, 0, bytes.length)) !=-1) && !stop) {
    				System.out.println("Level : ");
    				line.write(bytes, 0, bytesRead);
    				elapsedTime = line.getMicrosecondPosition()/(double)1000000;
    				this.updateObservateur();
    				
    				//remainedTime = durationInSeconds-elapsedTime;
    				//System.out.println(durationInSeconds + " - " + elapsedTime + " - " + remainedTime);
    			}
    					
    		} catch (IOException io) {
    			io.printStackTrace();
    		}
    		
    		//On ferme la ligne a la fin
    		line.close();		
    	}
    	
    	public void setFile (File file) {
    		this.file=file;
    	}
    	
    	public SourceDataLine getLine() {
    		return line;
    	}
    
    	public double getDurationInSeconds() {
    		return durationInSeconds;
    	}
    
    	@Override
    	public void addObservateur(Observateur obs) {
    		// TODO Auto-generated method stub
    		this.listObservateur.add(obs);
    		
    	}	
    
    	@Override
    	public void delObservateur() {
    		// TODO Auto-generated method stub
    		this.listObservateur = new ArrayList<Observateur>();
    		
    		
    	}
    	
    	@Override
    	public void updateObservateur() {
    		// TODO Auto-generated method stub
    		for(Observateur obs : this.listObservateur) {
    			obs.update(this.elapsedTime);
    		}
    		
    	}
    
    }
    



    -
    Edité par Dr_Click 21 février 2015 à 22:13:13

    • Partager sur Facebook
    • Partager sur Twitter
    Dr_Click
      23 février 2015 à 13:48:15

      J'avance un peu sur mon problème : en parcourant le tableau bytes[] (voir le morceau de code ci dessous) , j'obtiens des valeurs... Ces valeurs varient donc à priori, ce sont mes fameuses valeurs de niveau.

      Questions : Comment je peux avoir une idée ce l'échelle de ces valeurs ? J'ai ajouté une petite boucle de test qui me donne à chaque tour de boucle la valeur du bit maximum et du bit minimum et j'obtiens une plage de valeur entre -128 et +128. Je me serais attendu à une valeur entre -256 et +256 qui sont les valeurs de - ou + 8 bits, le son étant codé sur 16 bits...

      Ensuite, comment je retrouve les valeurs affectées à la voix gauche ou à la voix droite vue que le fichier est en stéréo ?

      Enfin, l'information de la dataline m'indique que le son est codé sur 16 bits, en stéréo avec 4 bytes par frame. Que sont ces frames ? Interviennent-elles dans le calcul du "niveau sonore" ?

      J'avance, mais je suis toujours bien largué...

      Voici la partie de code modifiée. Si quelqu'un peut m'aider, ça sera bienvenu... ;-)

      //Il faut ensuite ecrire sur la ligne.
      		//On travaille comme sur un InputStream classique
      		try {
      			byte bytes[] = new byte[1024];
      			int bytesRead=0;
      			
      			while (((bytesRead=audioInputStream.read(bytes, 0, bytes.length)) !=-1) && !stop) {
      				line.write(bytes, 0, bytesRead);
      				int sum=0;
      				int maxBit=0;
      				int minBit=0;
      				for (byte bit:bytes) {
      					//System.out.println("\t" + bit);
      					if (bit>maxBit) {
      						maxBit=bit;
      					} else if (bit<minBit) {
      						minBit=bit;
      					}
      				}
      				System.out.println("Min : "+minBit + " //// Max : "+maxBit);
      				elapsedTime = line.getMicrosecondPosition()/(double)1000000;
      				this.updateObservateur();
      
      			}
      			
      		} catch (IOException io) {
      			io.printStackTrace();
      		}



      -
      Edité par Dr_Click 23 février 2015 à 13:49:48

      • Partager sur Facebook
      • Partager sur Twitter
      Dr_Click
        23 février 2015 à 15:12:55

        Salut,

        je ne suis vraiment pas expert dans la programmation audio en java mais j'ai googlé vite fait et je suis tombé sur Java Sound Programmer Guide disponible à l'adresse http://www.oracle.com/technetwork/java/javase/sound-dev-guide-1-159173.pdf.

        Tu pourrais peut être trouvé ton bonheur dans le chapitre 6  dénommé Processing audio with controls !

        De même, une discussion sur le même sujet a apparemment porté ses fruits. Take a look at this !

        J'espère que c'est en phase avec ce que tu fait et qu'il te sera utile.

        Cordialement.

        -
        Edité par El Presidente 23 février 2015 à 15:22:58

        • Partager sur Facebook
        • Partager sur Twitter
          23 février 2015 à 18:02:46

          Merci pour cette première aide El Presidente. Tes liens vont beaucoup m'aider pour les étapes suivantes dans la "manipulation du son". Pour le moment, mon problème est de récupérer la valeur sonore du son pour faire un VUmetre comme on trouve sur n'importe quel programme audio.

          Je pense avoir encore un peu avancé mais j'ai besoin de valider ce que je crois avoir compris.

          Il semble que la taille de l'échantillon soit de 16 bits et il y a deux canaux. Il semblerait donc qu'un paquet de 16 bits jouent le "son à droite", les 16 suivants le "son à gauche", les 16 suivants de nouveau le son à droite etc etc. Ma "valeur de niveau" serait donc la somme de 16 bits.

          Je fais un tableau "buffer" de 1024 bits (sinon, le son est totalement haché en dessous d'un buffer de 256 bits) ce qui représente environ 6 millisecondes de lecture. Je vais donc essayer de faire la chose suivante : lire 16 bits, les ajouter entre eux, les stocker dans une variable appelée "canal gauche", lire les 16 bits suivants, les ajouter entre eux, les stocker dans une valiable "canal droit", et ça, 32 fois de suite. (16+16)*32 = 1024 soit la taille en bits de mon buffer. Je ferai ensuite la moyenne des 32 "canal de gauche" et celle des 32 "canal de droites" lus à chaque "tour de buffer", ce qui me donnera une valeur moyenne du signal gauche et du signal droit toutes les 6 ms, ce qui semble largement raisonnable pour donner faire un VUmetre.

          Maintenant, comment faire cette lecture de tableau avec le stockage adequat, ça va être un petit jeu bien prise de tête...

          PS : je ne sais pas si je suis clair dans ce que j'écris et si mon raisonnement tient la route mais n'hésitez pas à essayer de me corriger ou me donner votre avis...

          • Partager sur Facebook
          • Partager sur Twitter
          Dr_Click
            28 février 2015 à 20:32:36

            Bonsoir, j'ai finalement trouvé la solution, par essais successifs : 

            A chaque ligne, il y a 256 frames. Le buffer faisant 1024 valeurs, 1 frame = 4 valeurs. Il y 2 canaux dans le cas d'une stéréo =­> 2 valeurs font le canal gauche puis les 2 suivantes font celui de droite 256 fois de suite, ce qui correspond à 4*256 = 1024 valeurs, soit la taille du buffer.

            La valeur "pic" de chaque canal est donc comprise entre -32768 et +32768, soit 65636 valeurs, la valeur maximale de 16 bits qui est la qualité d'échantillonnage du signal. La valeur de chaque bit étant prise en valeur absolue, la valeur maximale ne pourra donc pas dépasser 32768

            J'espère que mon "explication" pourra servir à d'autres ayant eu le même problème.

            Bonne soirée et bon WE à tous.

            • Partager sur Facebook
            • Partager sur Twitter
            Dr_Click

            Obtenir le niveau / volume d'un son

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
            • Editeur
            • Markdown