Lendo arquivos * .wav em Python


92

Preciso analisar o som escrito em um arquivo .wav. Para isso preciso transformar esse arquivo em um conjunto de números (arrays, por exemplo). Acho que preciso usar o pacote de ondas. No entanto, não sei exatamente como funciona. Por exemplo, eu fiz o seguinte:

import wave
w = wave.open('/usr/share/sounds/ekiga/voicemail.wav', 'r')
for i in range(w.getnframes()):
    frame = w.readframes(i)
    print frame

Como resultado desse código, esperava ver a pressão sonora em função do tempo. Em contraste, vejo muitos símbolos estranhos e misteriosos (que não são números hexadecimais). Alguém pode, por favor, me ajudar com isso?

Respostas:


114

De acordo com a documentação , scipy.io.wavfile.read(somefile)retorna uma tupla de dois itens: o primeiro é a taxa de amostragem em amostras por segundo, o segundo é uma numpymatriz com todos os dados lidos do arquivo:

from scipy.io import wavfile
samplerate, data = wavfile.read('./output/audio.wav')

Você pode combinar isso com ferramentas de conversão de linha de comando para abrir outros formatos.
endólito

11
Falta seriamente o número de canais. Como trabalhar com áudio sem saber o número de canais?
bastibe

lança alguns erros de descompactação de estrutura estranhos no meu computador. Acho que está usando struct.unpack ('<i', data) em vez do nak de struct.unpack ('<h', data) usado abaixo.
Alex S

1
Esta biblioteca funciona? Tive vários problemas: scipy.io.wavfile.read ('/ usr / lib / python2.7 / dist-packages / pygame / examples / data / house_lo.wav') -> Sem dados. scipy.io.wavfile.read ('/ usr / lib / python2.7 / dist-packages / pygame / examples / data / secosmic_lo.wav') -> ZeroDivisionError: divisão inteira ou módulo por zero
Finn Årup Nielsen

7
@bastibe dataé uma matriz numpy 2-D, portanto, data.shaperetorna uma tupla de (num_samples, num_channels)
hobs

63

Usando o structmódulo , você pode pegar os quadros de onda (que estão no binário complementar de 2 entre -32768 e 32767 (ou seja, 0x8000e 0x7FFF). Isso lê um arquivo MONO, 16-BIT, WAVE. Achei esta página da Web bastante útil para formular isso:

import wave, struct

wavefile = wave.open('sine.wav', 'r')

length = wavefile.getnframes()
for i in range(0, length):
    wavedata = wavefile.readframes(1)
    data = struct.unpack("<h", wavedata)
    print(int(data[0]))

Este trecho lê 1 quadro. Para ler mais de um quadro (por exemplo, 13), use

wavedata = wavefile.readframes(13)
data = struct.unpack("<13h", wavedata)

2
como lidar com arquivos estéreo 24bits?
Basj

14
isso me dá o erro: "struct.error: unpack requer um argumento de string de comprimento 2"
Coder404

1
Se você executar este pedaço de código com um arquivo de áudio muito grande. Seu computador irá morrer devido à necessidade de memória deste programa. Necessidade de processar arquivo de áudio por bloco para arquivo de áudio grande
ArthurLambert

@ Coder404 Você provavelmente tem um arquivo de onda estéreo ou uma profundidade de bits diferente.
jmilloy de

3
Para aqueles que, como eu, estão se perguntando o que é binário complementar 2s, veja aqui stackoverflow.com/questions/1049722/what-is-2s-complement
Dennis Golomazov

34

Módulos Python diferentes para ler wav:

Existem pelo menos as seguintes bibliotecas para ler arquivos de áudio wave:

O exemplo mais simples:

Este é um exemplo simples com SoundFile:

import soundfile as sf
data, samplerate = sf.read('existing_file.wav') 

Formato da saída:

Atenção, os dados nem sempre estão no mesmo formato, isso depende da biblioteca. Por exemplo:

from scikits import audiolab
from scipy.io import wavfile
from sys import argv
for filepath in argv[1:]:
    x, fs, nb_bits = audiolab.wavread(filepath)
    print('Reading with scikits.audiolab.wavread:', x)
    fs, x = wavfile.read(filepath)
    print('Reading with scipy.io.wavfile.read:', x)

Resultado:

Reading with scikits.audiolab.wavread: [ 0.          0.          0.         ..., -0.00097656 -0.00079346 -0.00097656]
Reading with scipy.io.wavfile.read: [  0   0   0 ..., -32 -26 -32]

O retorno de SoundFile e Audiolab flutua entre -1 e 1 (como o matab, que é a convenção para sinais de áudio). Scipy e números inteiros de retorno de onda, que você pode converter em flutuantes de acordo com o número de bits de codificação, por exemplo:

from scipy.io.wavfile import read as wavread
samplerate, x = wavread(audiofilename)  # x is a numpy array of integers, representing the samples 
# scale to -1.0 -- 1.0
if x.dtype == 'int16':
    nb_bits = 16  # -> 16-bit wav files
elif x.dtype == 'int32':
    nb_bits = 32  # -> 32-bit wav files
max_nb_bit = float(2 ** (nb_bits - 1))
samples = x / (max_nb_bit + 1)  # samples is a numpy array of floats representing the samples 

14

IMHO, a maneira mais fácil de obter dados de áudio de um arquivo de som em uma matriz NumPy é SoundFile :

import soundfile as sf
data, fs = sf.read('/usr/share/sounds/ekiga/voicemail.wav')

Isso também oferece suporte a arquivos de 24 bits prontos para uso.

Existem muitas bibliotecas de arquivos de som disponíveis, eu escrevi uma visão geral onde você pode ver alguns prós e contras. Ele também apresenta uma página que explica como ler um arquivo wav de 24 bits com o wavemódulo .


Observação: soundfile.read () normaliza por 2 ^ (n_bits - 1) como no exemplo scipy.io.wavfile de sandoval
Quetzalcoatl

9

Você pode fazer isso usando o módulo scikits.audiolab . Requer o NumPy e o SciPy para funcionar, e também o libsndfile.

Note, eu só consegui fazer funcionar no Ubunutu e não no OSX.

from scikits.audiolab import wavread

filename = "testfile.wav"

data, sample_frequency,encoding = wavread(filename)

Agora você tem os dados wav


scikits.audiolabnão foi atualizado desde 2010 e provavelmente é apenas o Python 2.
Boris,

4

Se você quiser processar um áudio bloco por bloco, algumas das soluções fornecidas são bastante ruins no sentido de que implicam carregar todo o áudio na memória, produzindo muitos erros de cache e tornando o programa lento. python-wavefile fornece algumas construções pythônicas para fazer o processamento bloco a bloco do NumPy usando gerenciamento de bloco eficiente e transparente por meio de geradores. Outras sutilezas pythônicas são o gerenciador de contexto para arquivos, metadados como propriedades ... e se você quiser toda a interface do arquivo, porque você está desenvolvendo um protótipo rápido e não se preocupa com a eficiência, toda a interface do arquivo ainda está lá.

Um exemplo simples de processamento seria:

import sys
from wavefile import WaveReader, WaveWriter

with WaveReader(sys.argv[1]) as r :
    with WaveWriter(
            'output.wav',
            channels=r.channels,
            samplerate=r.samplerate,
            ) as w :

        # Just to set the metadata
        w.metadata.title = r.metadata.title + " II"
        w.metadata.artist = r.metadata.artist

        # This is the prodessing loop
        for data in r.read_iter(size=512) :
            data[1] *= .8     # lower volume on the second channel
            w.write(data)

O exemplo reutiliza o mesmo bloco para ler todo o arquivo, mesmo no caso do último bloco que geralmente é menor que o tamanho necessário. Neste caso, você obtém uma fatia do bloco. Portanto, confie no comprimento do bloco retornado em vez de usar um tamanho 512 codificado para qualquer processamento posterior.



1

Eu precisava ler um arquivo WAV de 24 bits de 1 canal. O post acima de Nak foi muito útil. No entanto, conforme mencionado acima por basj 24 bits não é simples. Finalmente consegui fazer funcionar usando o seguinte snippet:

from scipy.io import wavfile
TheFile = 'example24bit1channelFile.wav'
[fs, x] = wavfile.read(TheFile)

# convert the loaded data into a 24bit signal

nx = len(x)
ny = nx/3*4    # four 3-byte samples are contained in three int32 words

y = np.zeros((ny,), dtype=np.int32)    # initialise array

# build the data left aligned in order to keep the sign bit operational.
# result will be factor 256 too high

y[0:ny:4] = ((x[0:nx:3] & 0x000000FF) << 8) | \
  ((x[0:nx:3] & 0x0000FF00) << 8) | ((x[0:nx:3] & 0x00FF0000) << 8)
y[1:ny:4] = ((x[0:nx:3] & 0xFF000000) >> 16) | \
  ((x[1:nx:3] & 0x000000FF) << 16) | ((x[1:nx:3] & 0x0000FF00) << 16)
y[2:ny:4] = ((x[1:nx:3] & 0x00FF0000) >> 8) | \
  ((x[1:nx:3] & 0xFF000000) >> 8) | ((x[2:nx:3] & 0x000000FF) << 24)
y[3:ny:4] = (x[2:nx:3] & 0x0000FF00) | \
  (x[2:nx:3] & 0x00FF0000) | (x[2:nx:3] & 0xFF000000)

y = y/256   # correct for building 24 bit data left aligned in 32bit words

Algum escalonamento adicional é necessário se você precisar de resultados entre -1 e +1. Talvez alguns de vocês possam achar isso útil


0

se forem apenas dois arquivos e a taxa de amostragem for significativamente alta, você pode apenas intercalá-los.

from scipy.io import wavfile
rate1,dat1 = wavfile.read(File1)
rate2,dat2 = wavfile.read(File2)

if len(dat2) > len(dat1):#swap shortest
    temp = dat2
    dat2 = dat1
    dat1 = temp

output = dat1
for i in range(len(dat2)/2): output[i*2]=dat2[i*2]

wavfile.write(OUTPUT,rate,dat)

0

Você também pode usar uma import waviobiblioteca simples. Você também precisa ter alguns conhecimentos básicos de som.


0

PyDub ( http://pydub.com/ ) não foi mencionado e isso deve ser corrigido. IMO, esta é a biblioteca mais abrangente para leitura de arquivos de áudio em Python atualmente, embora tenha seus defeitos. Lendo um arquivo wav:

from pydub import AudioSegment

audio_file = AudioSegment.from_wav('path_to.wav')
# or
audio_file = AudioSegment.from_file('path_to.wav')

# do whatever you want with the audio, change bitrate, export, convert, read info, etc.
# Check out the API docs http://pydub.com/

PS. O exemplo é sobre a leitura de um arquivo wav, mas o PyDub pode lidar com vários formatos de fábrica. A ressalva é que ele é baseado no suporte nativo de wav do Python e ffmpeg, então você precisa ter o ffmpeg instalado e muitos dos recursos do pydub dependem da versão do ffmpeg. Normalmente, se o ffmpeg pode fazer isso, o pydub também pode (que é bastante poderoso).

Isenção de responsabilidade: não sou relacionado ao projeto, mas sou um usuário pesado.


0

Aqui está uma solução Python 3 usando o módulo de onda embutido [1], que funciona para n canais e 8,16,24 ... bits.

import sys
import wave

def read_wav(path):
    with wave.open(path, "rb") as wav:
        nchannels, sampwidth, framerate, nframes, _, _ = wav.getparams()
        print(wav.getparams(), "\nBits per sample =", sampwidth * 8)

        signed = sampwidth > 1  # 8 bit wavs are unsigned
        byteorder = sys.byteorder  # wave module uses sys.byteorder for bytes

        values = []  # e.g. for stereo, values[i] = [left_val, right_val]
        for _ in range(nframes):
            frame = wav.readframes(1)  # read next frame
            channel_vals = []  # mono has 1 channel, stereo 2, etc.
            for channel in range(nchannels):
                as_bytes = frame[channel * sampwidth: (channel + 1) * sampwidth]
                as_int = int.from_bytes(as_bytes, byteorder, signed=signed)
                channel_vals.append(as_int)
            values.append(channel_vals)

    return values, framerate

Você pode transformar o resultado em uma matriz NumPy.

import numpy as np

data, rate = read_wav(path)
data = np.array(data)

Observe, tentei torná-lo legível em vez de rápido. Descobri que ler todos os dados de uma vez foi quase 2x mais rápido. Por exemplo

with wave.open(path, "rb") as wav:
    nchannels, sampwidth, framerate, nframes, _, _ = wav.getparams()
    all_bytes = wav.readframes(-1)

framewidth = sampwidth * nchannels
frames = (all_bytes[i * framewidth: (i + 1) * framewidth]
            for i in range(nframes))

for frame in frames:
    ...

Embora python-soundfile seja cerca de 2 ordens de magnitude mais rápido (difícil aproximar essa velocidade com CPython puro).

[1] https://docs.python.org/3/library/wave.html

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.