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?

romano
fonte

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')
Alex Martelli
fonte
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)
nak
fonte
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 
PatriceG
fonte
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 .

Matthias
fonte
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

ch3rryc0ke
fonte
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.

vokimon
fonte
1

Se você for realizar transferências nos dados da forma de onda, talvez deva usar o SciPy , especificamente scipy.io.wavfile.

Ignacio Vazquez-Abrams
fonte
2
ESTÁ BEM. Acabei de instalar o SciPy, mas não consigo encontrar nenhum exemplo de uso de scipy.io.wavfile.
Romano
6
Nada como o intérprete interativo para descobrir como as coisas funcionam! Ser ambicioso!
Ignacio Vazquez-Abrams
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

ProgJos
fonte
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)
sanguessuga
fonte
0

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

Yunus
fonte
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.

wanaryytel
fonte
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

kjldfg
fonte