Tutoriel Java Sound - transformer le son du microphone en images temps réel

Publié le 18/11/2025 Source : sfeir.dev

Le son dans une application n’est pas toujours qu’une simple lecture d’un fichier MP3. Et si on pouvait voir la musique ? Ou analyser les bruits ambiants captés par notre micro ? La bonne nouvelle, c’est que Java, via son API Java Sound, nous donne tous les outils nécessaires pour le faire.

Dans cet article, nous allons construire une application Swing qui capture le son du microphone en temps réel et affiche son spectre de fréquences. C’est ce que l’on appelle un visualiseur de spectre audio.

Allez, on plonge !

Le projet : Capturer / Analyser / Dessiner

Notre objectif est simple en apparence, mais il se décompose en trois grandes étapes techniques :

  1. Capturer l’audio : Nous allons ouvrir une ligne d’entrée audio (le microphone) et lire les données sonores brutes en continu.
  2. Analyser les fréquences : C’est le cœur du projet. Les données audio brutes sont une onde dans le temps. Pour savoir quelles fréquences (basses, médiums, aigus) la composent, nous devons la transformer. Pour cela, nous utiliserons un algorithme puissant : la Transformée de Fourier Rapide (FFT).
  3. Dessiner le spectre : Une fois les intensités de chaque fréquence obtenues, nous les dessinerons à l’écran sous forme de barres colorées et dynamiques à l’aide de Swing.

L’API Java Sound : votre boîte à outils audio

Avant de coder, comprenons nos outils. L’API Java Sound (javax.sound.sampled) peut sembler intimidante, mais elle s’articule autour de quelques concepts clés, un peu comme un studio d’enregistrement.

En résumé, le workflow est presque toujours le même :

  1. On définit l’AudioFormat que l’on souhaite.
  2. On demande à AudioSystem de nous donner une **Line** (par exemple, une TargetDataLine) compatible avec ce format.
  3. On ouvre la ligne, on la démarre, puis on lit (read()) ou on écrit (write()) les données audio.

Capturer le son du micro

La première étape consiste à accéder au microphone. Avec notre connaissance de l’API Java Sound, le code devient plus facile à lire. Le travail se fera dans un Thread séparé pour ne pas bloquer l’interface graphique.

Thread captureThread = new Thread(() -> {
    try {
        // 1. Définir le format audio
        AudioFormat format = new AudioFormat(AudioConstants.SAMPLE_RATE, 16, 1, true, false);
        
        // 2. Obtenir la ligne du micro
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
        TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
        line.open(format, AudioConstants.SAMPLE_COUNT * AudioConstants.BYTES_PER_SAMPLE * 2);
        line.start();

        byte[] buffer = new byte[AudioConstants.SAMPLE_COUNT * AudioConstants.BYTES_PER_SAMPLE];

        // 3. Boucle de lecture
        while (running.get()) {
            int bytesRead = line.read(buffer, 0, buffer.length);
            if (bytesRead <= 0) continue;

            // ... traitement des données ...
        }

        line.stop();
        line.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
});
captureThread.start();

Une fois les octets (byte[]) lus, il faut les transformer en valeurs numériques manipulables (double[]). Chaque échantillon 16 bits est composé de deux octets que nous combinons et normalisons entre -1.0 et 1.0.

// Conversion de bytes en samples (doubles)
for (int i = 0, s = 0; s < samplesRead; i += 2, s++) {
    int low = buffer[i] & 0xFF;
    int high = buffer[i + 1];
    int value = (high << 8) | low; // Combiner les deux octets
    samples[s] = value / 32768.0;   // Normaliser
}

La magie de la FFT : Voir les fréquences cachées

Maintenant que nous avons un tableau de “samples” représentant l’onde sonore, comment en extraire les fréquences ?

Imaginez un accord de piano. Votre oreille entend une note unique, mais en réalité, c’est un mélange complexe d’une fréquence fondamentale et de plusieurs harmoniques. La Transformée de Fourier est un outil mathématique qui agit comme un prisme : elle prend un signal complexe (la lumière blanche, l’onde sonore) et le décompose en ses constituants simples (les couleurs de l’arc-en-ciel, les fréquences).

La FFT (Fast Fourier Transform) est un algorithme qui calcule cette transformation de manière très efficace.

// Dans la boucle de capture
double[] newMagnitudes = computeFFT(samples);

La méthode computeFFT est une implémentation de cet algorithme. Il est inutile de comprendre chaque ligne de calcul, mais il faut retenir ce qu’elle fait :

Le premier élément du tableau de sortie correspond aux fréquences les plus basses (les basses), et le dernier aux plus hautes (les aigus).

Mettre en scène : la visualisation avec Swing

Maintenant que nous avons nos magnitudes de fréquences, il ne reste “plus qu’à” les dessiner ! Notre classe hérite de JPanel, nous allons donc surcharger la méthode paintComponent.

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (magnitudes == null) return;

    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    int w = getWidth();
    int h = getHeight();
    g2.setColor(Color.BLACK);
    g2.fillRect(0, 0, w, h);

    int len = magnitudes.length;
    double max = Arrays.stream(magnitudes).max().orElse(1);

    // Using a logarithmic scale for the x-axis
    double logBase = Math.log(len);

    for (int i = 1; i < len; i++) { // Start from 1 to avoid log(0)
        double norm = magnitudes[i] / max;
        int barHeight = (int) (norm * h);

        // Calculate x position on a logarithmic scale
        double log_i = Math.log(i);
        int x = (int) (w * log_i / logBase);

        // Couleur dynamique (vert → jaune → rouge)
        float hue = (float) (0.33 - norm * 0.33); // 0.33=vert, 0.0=rouge
        g2.setColor(Color.getHSBColor(hue, 1.0f, 1.0f));

        g2.drawLine(x, h, x, h - barHeight);
    }

    g2.setColor(Color.WHITE);
    g2.drawString("Spectre Audio (Log) — " + (int) (AudioConstants.SAMPLE_RATE / 2) + " Hz", 10, 20);
}

Un Timer Swing appelle la méthode repaint() à une fréquence régulière (ici, environ 30 fois par seconde), ce qui redessine le composant et crée l’animation.

Les petits plus qui font la différence

Notre code de rendu contient deux astuces très importantes :

// Calcul de la position x sur une échelle logarithmique
double log_i = Math.log(i);
int x = (int) (w * log_i / logBase);
// Couleur dynamique (vert → jaune → rouge)
float hue = (float) (0.33 - norm * 0.33);
g2.setColor(Color.getHSBColor(hue, 1.0f, 1.0f));
smoothedMagnitudes[i] = 0.8 * smoothedMagnitudes[i] + 0.2 * newMagnitudes[i];

Et ça donne quoi ?

Maintenant il nous reste plus qu’à lancer notre programme, mettre de la musique et voir notre nostalgie de Windows Media Player refaire surface.

notre visualiseur en action

Conclusion

Nous avons vu comment, avec un peu de code, on peut passer de la simple capture audio à une visualisation de données complexe et esthétique. Nous avons manipulé l’API Java Sound, implémenté une FFT et utilisé Swing pour un rendu dynamique.

Que faire ensuite ?

Le champ des possibles est immense. Alors, à vous de jouer !