5
votes

Lire un fichier wav avec scipy et librosa en python

J'essaye de charger un fichier .wav en Python en utilisant le dossier scipy. Mon objectif final est de créer le spectrogramme de ce fichier audio. Le code de lecture du fichier peut être résumé comme suit:

(rate1, sig1) = wav.read(spec_file) # rate1 = 16000
sig, rate = librosa.load(spec_file) # rate 22050
sig = np.array(α*sig, dtype = "int16") 

Pour certains fichiers .wav , je reçois l'erreur suivante:

WavFileWarning: Chunk (non-data) non compris, saute. WavFileWarning) ** ValueError: bloc wav incomplet.

Par conséquent, j'ai décidé d'utiliser librosa pour lire les fichiers en utilisant:

import librosa
(sig, rate) = librosa.load(_wav_file_, sr=None)

Cela fonctionne correctement dans tous les cas, cependant, j'ai remarqué une différence aux couleurs du spectrogramme. Bien que ce soit le même chiffre exact, cependant, les couleurs ont été inversées. Plus précisément, j'ai remarqué qu'en conservant la même fonction pour le calcul des spécifications et en changeant uniquement la façon dont je lis le .wav , il y avait cette différence. Une idée de ce qui peut produire cette chose? Y a-t-il une différence par défaut entre la façon dont les deux approches lisent le fichier .wav ?

EDIT:

import scipy.io.wavfile as wav
(sig, rate) = wav.read(_wav_file_)


0 commentaires

3 Réponses :


5
votes

Cela ressemble à un problème de quantification. Si les échantillons du fichier wave sont stockés en tant que float et que la librosa effectue simplement un cast direct en un int , et une valeur inférieure à 1 sera tronquée à 0. Plus que probable , c'est pourquoi sig est un tableau de tous les zéros. Le float doit être mis à l'échelle pour le mapper dans la plage d'un int . Par exemple,

sig, rate = librosa.load(spec_file, mono=True)
sig = sig × 32767

Convertit a en type int sans mise à l'échelle

>>> c = b/32767.0
>>> c
array([-0.04248177,  0.24408704,  0.64476455, -0.36655782, -0.28360851,
       -0.27805414, -0.0766625 , -1.31043428,  0.9525132 , -0.56776635])

Convertit a en int avec mise à l'échelle pour un entier 16 bits

>>> b = (a* 32767).astype(int)
>>> b
array([ -1392,   7998,  21127, -12011,  -9293,  -9111,  -2512, -42939,
        31211, -18604])

Conversion de int mis à l'échelle en float

>>> a.astype(int)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

c et b ne sont égaux qu'à environ 3 ou 4 décimales en raison de la quantification en int .

Si la librosa renvoie un float , vous pouvez le mettre à l'échelle de 2 ** 15 et le transtyper en un int code > pour obtenir la même plage de valeurs que celle renvoyée par le lecteur scipy wave. Puisque la librosa renvoie un float , il est probable que les valeurs se trouvent dans une plage beaucoup plus petite, telle que [-1, +1] , qu'un entier de 16 bits qui sera dans [-32768, +32767] . Vous devez donc en mettre un à l'échelle pour que les plages correspondent. Par exemple,

>>> a = sp.randn(10)
>>> a
array([-0.04250369,  0.244113  ,  0.64479281, -0.3665814 , -0.2836227 ,
       -0.27808428, -0.07668698, -1.3104602 ,  0.95253315, -0.56778205])


9 commentaires

Mais pour faire la mise à l'échelle, je dois trouver le minimum et le maximum de chaque ensemble de données. C'est un peu impossible pour moi.


Le facteur d'échelle est plus que probablement constant. Si ce n'était pas le cas, le volume du fichier wave changerait pour chaque bloc de données lu.


Quelle est la source des données et qu'est-ce qui est utilisé pour créer le fichier wav?


Cette information est malheureusement quelque chose auquel je n'ai pas accès. Les fichiers wav font partie d'une base de données qui peut être trouvée ici: zenodo.org/record/1188976#. XFmYoVxKi73


Oui, j'ai essayé mais de manière un peu arbitraire. Je viens de mettre à l'échelle mais j'ai trouvé le max et le min dans les deux cas dans 20-30 fichiers wav.


De cette façon, les spectrogrammes sont un peu similaires. Il reste encore un problème, je suppose, c'est que les deux méthodes ont une fréquence d'échantillonnage différente. Je pensais que la fréquence d'échantillonnage était définie par le fichier .wav. Cependant, il semble que ce ne soit pas le cas.


La fréquence d'échantillonnage doit être la même. Quelles valeurs obtenez-vous pour les taux d'échantillonnage?


Pour le scipy wav.read, il est de 16.000 alors que pour la librosa 22050.


Il semble que librosa et scipy utilisent différents formats pour le fichier wav.



3
votes
  • Si vous ne souhaitez pas effectuer vous-même la quantification, vous pouvez utiliser pylab en utilisant la fonction pylab.specgram , pour le faire pour vous. Vous pouvez regarder à l'intérieur de la fonction et voir comment elle utilise vmin et vmax .

  • Ce que vous voulez réaliser n'est pas tout à fait clair dans votre message (du moins pour moi) (car il n'y a pas non plus de fichier d'entrée d'exemple ni de script de votre part). Mais de toute façon, pour vérifier si le spectrogramme d'un fichier wave présente des différences significatives selon le cas où les données de signal renvoyées par l'une des fonctions de lecture sont float32 ou int , je testé les 3 fonctions suivantes.

  • Script Python:

    _wav_file_ = "africa-toto.wav"
    
    def spectogram_librosa(_wav_file_):
        import librosa
        import pylab
        import numpy as np
        
        (sig, rate) = librosa.load(_wav_file_, sr=None, mono=True,  dtype=np.float32)
        pylab.specgram(sig, Fs=rate)
        pylab.savefig('spectrogram3.png')
    
    def graph_spectrogram_wave(wav_file):
        import wave
        import pylab
        def get_wav_info(wav_file):
            wav = wave.open(wav_file, 'r')
            frames = wav.readframes(-1)
            sound_info = pylab.fromstring(frames, 'int16')
            frame_rate = wav.getframerate()
            wav.close()
            return sound_info, frame_rate
        sound_info, frame_rate = get_wav_info(wav_file)
        pylab.figure(num=3, figsize=(10, 6))
        pylab.title('spectrogram pylab with wav_file')
        pylab.specgram(sound_info, Fs=frame_rate)
        pylab.savefig('spectrogram2.png')
    
    
    def graph_wavfileread(_wav_file_):
        import matplotlib.pyplot as plt
        from scipy import signal
        from scipy.io import wavfile
        import numpy as np   
        sample_rate, samples = wavfile.read(_wav_file_)   
        frequencies, times, spectrogram = signal.spectrogram(samples,sample_rate,nfft=1024)
        plt.pcolormesh(times, frequencies, 10*np.log10(spectrogram))
        plt.ylabel('Frequency [Hz]')
        plt.xlabel('Time [sec]')
        plt.savefig("spectogram1.png")
        
    
    spectogram_librosa(_wav_file_)
    #graph_wavfileread(_wav_file_)
    #graph_spectrogram_wave(_wav_file_)
    
    • qui a produit les 3 résultats suivants:

     entrez la description de l'image ici

     entrez la description de l'image ici

     entrez la description de l'image here

    qui, mis à part les différences mineures de taille et d'intensité semblent assez similaires, peu importe la méthode de lecture, la bibliothèque ou le type de données, ce qui me fait un peu questionner, dans quel but besoin les sorties sont «exactement» les mêmes et à quel point elles devraient être exactes.

    • Je trouve étrange cependant que la fonction librosa.load () offre un paramètre dtype mais ne fonctionne de toute façon qu'avec des valeurs float . Google à cet égard ne m'a conduit qu'à ce problème qui n'a pas beaucoup aidé et ce problème indique que c'est ainsi qu'il restera avec librosa, car en interne, il semble seulement utilisez des flotteurs.

1 commentaires

Je souhaite lire l'audio puis calculer le spectrogramme en suivant les exemples suivants: haythamfayek.com/2016/04/21/... . J'ai remarqué qu'avec librosa et scipy waveread, il y avait une différence dans les couleurs obtenues.



0
votes

Pour ajouter à ce qui a été dit, Librosa a un utilitaire pour convertir des tableaux d'entiers en floats.

def buf_to_float(x, n_bytes=2, dtype=np.float32):
    """Convert an integer buffer to floating point values.
    This is primarily useful when loading integer-valued wav data
    into numpy arrays.
    See Also
    --------
    buf_to_float
    Parameters
    ----------
    x : np.ndarray [dtype=int]
        The integer-valued data buffer
    n_bytes : int [1, 2, 4]
        The number of bytes per sample in `x`
    dtype : numeric type
        The target output type (default: 32-bit float)
    Returns
    -------
    x_float : np.ndarray [dtype=float]
        The input data buffer cast to floating point
    """

    # Invert the scale of the data
    scale = 1./float(1 << ((8 * n_bytes) - 1))

    # Construct the format string
    fmt = '<i{:d}'.format(n_bytes)

    # Rescale and format the data buffer
    return scale * np.frombuffer(x, fmt).astype(dtype)

J'utilise ceci avec beaucoup de succès lors de la production de spectrogrammes de segments audio Pydub. Gardez à l'esprit que l'un de ses arguments est le nombre d'octets par échantillon. La valeur par défaut est 2. Vous pouvez en savoir plus dans la documentation ici . Voici le code source :

float_audio = librosa.util.buf_to_float(sig)


0 commentaires