6
votes

Analyser le son "sifflet" pour la hauteur / la note

J'essaie de créer un système qui sera en mesure de traiter un enregistrement de notes de sifflement et de sortie de quelqu'un.

Quelqu'un peut-il recommander une plate-forme open source que je peux utiliser comme base pour la reconnaissance et l'analyse de la note / de la hauteur des fichiers d'ondes?

Merci d'avance


0 commentaires

7 Réponses :


2
votes

Eh bien, vous pouvez toujours utiliser FFTW pour effectuer la transformée de Fourier rapide. C'est un cadre très respecté. Une fois que vous avez un FFT de votre signal, vous pouvez analyser le tableau résultant pour les pics. Une analyse de style histogramme simple devrait vous donner les fréquences avec le plus grand volume. Ensuite, il vous suffit de comparer ces fréquences aux fréquences qui correspondent à différents emplacements.


1 commentaires

Notez que la recherche de la bonne fréquence pour un sifflet est terriblement facile après que vous ayez fait la FFT, car le sifflet aura généralement une grande pointe à la fréquence que vous sifflez.



1
votes

Vous voudrez peut-être envisager python (x, y) . C'est un cadre de programmation scientifique pour Python dans l'esprit de Matlab, et il a des fonctions faciles à travailler dans le domaine FFT.


0 commentaires

1
votes

En plus des autres excellentes options:


0 commentaires

10
votes

Autant d'autres l'ont déjà dit, FFT est la voie à suivre ici. J'ai écrit un petit exemple en Java à l'aide du code FFT de http: //www.cs.princeton .edu / introcs / 97Data / . Afin de l'exécuter, vous aurez besoin de la classe complexe de cette page également (voir la source de l'URL exacte).

Le code se lit dans un fichier, passe la fenêtre sur la fenêtre et fait une FFT sur chaque fenêtre. Pour chaque FFT, il cherche le coefficient maximum et génère la fréquence correspondante. Cela fonctionne très bien pour des signaux propres comme une onde sinusoïdale, mais pour un son de sifflet réel, vous devez probablement ajouter plus. J'ai testé avec quelques fichiers avec sifflement, j'ai créé moi-même (en utilisant le micro intégré de mon ordinateur portable), le code obtient l'idée de ce qui se passe, mais afin d'obtenir des notes réelles plus besoin de faire. p>

1) Vous pourriez avoir besoin d'une technique de fenêtre plus intelligente. Ce que mon code utilise maintenant est une fenêtre rectangulaire simple. Étant donné que la FFT suppose que le singulateur d'entrée peut être poursuivi périodiquement, des fréquences supplémentaires sont détectées lorsque le premier et le dernier échantillon de la fenêtre ne correspondent pas. Ceci est connu sous le nom de fuite spectrale ( http://en.wikipedia.org/wiki/spectral_leakage ) , généralement une fenêtre utilise une fenêtre qui pèse-àbout des échantillons au début et à la fin de la fenêtre ( http: // en.wikipedia.org/wiki/window_function ). Bien que les fuites ne soient pas détectées de la fréquence incorrecte que le maximum, l'utilisation d'une fenêtre augmente la qualité de détection. P>

2) pour correspondre aux fréquences aux notes réelles, vous pouvez utiliser un tableau contenant le fréquences (comme 440 Hz pour A '), puis recherchez la fréquence la plus proche de celle qui a été identifiée. Cependant, si le sifflement est désactivé le réglage standard, cela ne fonctionnera plus. Étant donné que le sifflement est toujours correct, mais seulement ajusté différemment (comme une guitare ou un autre instrument de musique peut être réglé différemment et toujours au son "bon", tant que le réglage est effectué de manière cohérente pour toutes les cordes), vous pouvez toujours trouver des notes en regardant aux ratios des fréquences identifiées. Vous pouvez lire http://en.wikipedia.org/wiki/pitch_%28Music%29 comme point de départ à ce sujet. Ceci est également intéressant: http://fr.wikipedia.org/wiki/piano_key_frequences P >

3) De plus, il pourrait être intéressant de détecter les points à temps lorsque chaque tonalité démarre et s'arrête. Cela pourrait être ajouté comme une étape de pré-traitement. Vous pouvez faire une FFT pour chaque note individuelle alors. Cependant, si le sifflement ne s'arrête pas mais se penche simplement entre des notes, ce ne serait pas aussi facile. P>

Regardez définitivement les bibliothèques suggérées. Je ne connais aucun d'entre eux, mais peut-être qu'ils contiennent déjà des fonctionnalités pour faire ce que j'ai décrit ci-dessus. P>

et maintenant au code. S'il vous plaît laissez-moi savoir ce qui a fonctionné pour vous, je trouve ce sujet assez intéressant. P>

Modifier: b> J'ai mis à jour le code pour inclure le chevauchement et une simple mappeuse de fréquences aux notes. Cela ne fonctionne que pour "Tuned" Whistlers, comme mentionné ci-dessus. P>

package de.ahans.playground;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

public class FftMaxFrequency {

    // taken from http://www.cs.princeton.edu/introcs/97data/FFT.java.html
    // (first hit in Google for "java fft"
    // needs Complex class from http://www.cs.princeton.edu/introcs/97data/Complex.java
    public static Complex[] fft(Complex[] x) {
        int N = x.length;

        // base case
        if (N == 1) return new Complex[] { x[0] };

        // radix 2 Cooley-Tukey FFT
        if (N % 2 != 0) { throw new RuntimeException("N is not a power of 2"); }

        // fft of even terms
        Complex[] even = new Complex[N/2];
        for (int k = 0; k < N/2; k++) {
            even[k] = x[2*k];
        }
        Complex[] q = fft(even);

        // fft of odd terms
        Complex[] odd  = even;  // reuse the array
        for (int k = 0; k < N/2; k++) {
            odd[k] = x[2*k + 1];
        }
        Complex[] r = fft(odd);

        // combine
        Complex[] y = new Complex[N];
        for (int k = 0; k < N/2; k++) {
            double kth = -2 * k * Math.PI / N;
            Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
            y[k]       = q[k].plus(wk.times(r[k]));
            y[k + N/2] = q[k].minus(wk.times(r[k]));
        }
        return y;
    }   

    static class AudioReader {
        private AudioFormat audioFormat;

        public AudioReader() {}

        public double[] readAudioData(File file) throws UnsupportedAudioFileException, IOException {
            AudioInputStream in = AudioSystem.getAudioInputStream(file);
            audioFormat = in.getFormat();
            int depth = audioFormat.getSampleSizeInBits();
            long length = in.getFrameLength();
            if (audioFormat.isBigEndian()) {
                throw new UnsupportedAudioFileException("big endian not supported");
            }
            if (audioFormat.getChannels() != 1) {
                throw new UnsupportedAudioFileException("only 1 channel supported");
            }

            byte[] tmp = new byte[(int) length];
            byte[] samples = null;      
            int bytesPerSample = depth/8;
            int bytesRead;
            while (-1 != (bytesRead = in.read(tmp))) {
                if (samples == null) {
                    samples = Arrays.copyOf(tmp, bytesRead);
                } else {
                    int oldLen = samples.length;
                    samples = Arrays.copyOf(samples, oldLen + bytesRead);
                    for (int i = 0; i < bytesRead; i++) samples[oldLen+i] = tmp[i];
                }
            }

            double[] data = new double[samples.length/bytesPerSample];

            for (int i = 0; i < samples.length-bytesPerSample; i += bytesPerSample) {
                int sample = 0;
                for (int j = 0; j < bytesPerSample; j++) sample += samples[i+j] << j*8;
                data[i/bytesPerSample] = (double) sample / Math.pow(2, depth);
            }

            return data;
        }

        public AudioFormat getAudioFormat() {
            return audioFormat;
        }
    }

    public class FrequencyNoteMapper {
        private final String[] NOTE_NAMES = new String[] {
                "A", "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"
            };
        private final double[] FREQUENCIES;
        private final double a = 440;
        private final int TOTAL_OCTAVES = 6;
        private final int START_OCTAVE = -1; // relative to A

        public FrequencyNoteMapper() {
            FREQUENCIES = new double[TOTAL_OCTAVES*12];
            int j = 0;
            for (int octave = START_OCTAVE; octave < START_OCTAVE+TOTAL_OCTAVES; octave++) {
                for (int note = 0; note < 12; note++) {
                    int i = octave*12+note;
                    FREQUENCIES[j++] = a * Math.pow(2, (double)i / 12.0);
                }
            }
        }

        public String findMatch(double frequency) {
            if (frequency == 0)
                return "none";

            double minDistance = Double.MAX_VALUE;
            int bestIdx = -1;

            for (int i = 0; i < FREQUENCIES.length; i++) {
                if (Math.abs(FREQUENCIES[i] - frequency) < minDistance) {
                    minDistance = Math.abs(FREQUENCIES[i] - frequency);
                    bestIdx = i;
                }
            }

            int octave = bestIdx / 12;
            int note = bestIdx % 12;

            return NOTE_NAMES[note] + octave;
        }
    }

    public void run (File file) throws UnsupportedAudioFileException, IOException {
        FrequencyNoteMapper mapper = new FrequencyNoteMapper();

        // size of window for FFT
        int N = 4096;
        int overlap = 1024;
        AudioReader reader = new AudioReader(); 
        double[] data = reader.readAudioData(file);

        // sample rate is needed to calculate actual frequencies
        float rate = reader.getAudioFormat().getSampleRate();

        // go over the samples window-wise
        for (int offset = 0; offset < data.length-N; offset += (N-overlap)) {
            // for each window calculate the FFT
            Complex[] x = new Complex[N];
            for (int i = 0; i < N; i++) x[i] = new Complex(data[offset+i], 0);
            Complex[] result = fft(x);

            // find index of maximum coefficient
            double max = -1;
            int maxIdx = 0;
            for (int i = result.length/2; i >= 0; i--) {
                if (result[i].abs() > max) {
                    max = result[i].abs();
                    maxIdx = i;
                }
            }
            // calculate the frequency of that coefficient
            double peakFrequency = (double)maxIdx*rate/(double)N;
            // and get the time of the start and end position of the current window
            double windowBegin = offset/rate;
            double windowEnd = (offset+(N-overlap))/rate;
            System.out.printf("%f s to %f s:\t%f Hz -- %s\n", windowBegin, windowEnd, peakFrequency, mapper.findMatch(peakFrequency));
        }       
    }

    public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
        new FftMaxFrequency().run(new File("/home/axr/tmp/entchen.wav"));
    }
}


1 commentaires

J'ai testé à nouveau, monte mon premier record de sifflement était mauvais parce qu'il incluait beaucoup de bruit sifflant (j'étais trop près du micro). Maintenant, avec un nouvel enregistrement, cela fonctionne assez bien. Avoir également le chevauchement de la fenêtre un peu contribue à obtenir de meilleurs résultats. J'ajouterai que plus tard au code.



4
votes

Je pense que cette plate-forme open source vous convient http://code.google.com/p/musicg-sound-api/


0 commentaires

0
votes

3 commentaires

Bonjour, je voulais savoir comment la traversée zéro peut m'aider à détecter les sifflets et les boutons-pression des doigts.


Le ton que les produits de sifflement peuvent être considérés comme assez purs, donc zéro Cross fonctionnera bien pour cela. Les pinces à doigts peuvent être considérées comme un signal très court et très transitoire. Vous pouvez utiliser une méthode de détection de seuil très simple pour déclencher quand elle est entendue. Vous devrez jeter des sons si l'amplitude ne revient pas à un niveau de base après une courte période de temps sinon, il s'agirait simplement d'un détecteur de "bruit". L'analyse de la longueur d'un programme d'enfance moyen dans un programme audio vous donnerait une réponse dans la SP que vous pouvez utiliser comme guide pour rejeter les sons plus longs que cela.


@ @RonNied Optimisations pré-matures?



1
votes

Si vous utilisez Java, consultez bibliothèque TARSOSDSP . Il a un très bon détecteur de hauteur prêt à faire.

ici est un exemple pour Android, mais je pense que cela ne nécessite pas trop de modifications pour l'utiliser ailleurs.


0 commentaires