การอ่านไฟล์ * .wav ใน Python


92

ฉันต้องการวิเคราะห์เสียงที่เขียนในไฟล์. wav ด้วยเหตุนี้ฉันจึงต้องแปลงไฟล์นี้เป็นชุดตัวเลข (อาร์เรย์เป็นต้น) คิดว่าต้องใช้ wave package อย่างไรก็ตามฉันไม่รู้ว่ามันทำงานอย่างไร ตัวอย่างเช่นฉันทำสิ่งต่อไปนี้:

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

จากผลของรหัสนี้ฉันคาดว่าจะเห็นความดันเสียงตามหน้าที่ของเวลา ในทางตรงกันข้ามฉันเห็นสัญลักษณ์แปลก ๆ ลึกลับมากมาย (ซึ่งไม่ใช่เลขฐานสิบหก) ใครสามารถขอร้องช่วยฉันด้วย?

คำตอบ:


114

ต่อเอกสาร , scipy.io.wavfile.read(somefile)ส่งกลับ tuple ของสองรายการ: ครั้งแรกเป็นอัตราการสุ่มตัวอย่างในตัวอย่างต่อวินาทีที่สองเป็นnumpyอาร์เรย์กับทุกข้อมูลที่อ่านจากไฟล์:

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

คุณสามารถรวมสิ่งนี้กับเครื่องมือการแปลงบรรทัดคำสั่งเพื่อเปิดรูปแบบอื่น ๆ
endolith

11
แม้ว่าจะขาดจำนวนช่องอย่างจริงจัง คุณจะทำงานกับเสียงโดยไม่ทราบจำนวนช่องได้อย่างไร?
bastibe

ทำให้เกิดข้อผิดพลาดในการคลายโครงสร้างแปลก ๆ ในคอมพิวเตอร์ของฉัน ฉันคิดว่ามันใช้ struct.unpack ('<i', data) แทน struct.unpack ('<h', data) nak ที่ใช้ด้านล่าง
Alex S

1
ห้องสมุดนี้ใช้งานได้หรือไม่ ฉันพบปัญหาหลายประการ: scipy.io.wavfile.read ('/ usr / lib / python2.7 / dist-package / pygame / samples / data / house_lo.wav') -> ไม่มีข้อมูล scipy.io.wavfile.read ('/ usr / lib / python2.7 / dist-package / pygame / samples / data / secosmic_lo.wav') -> ZeroDivisionError: การหารจำนวนเต็มหรือโมดูโลโดยศูนย์
Finn Årup Nielsen

7
@bastibe dataเป็นอาร์เรย์ตัวเลข2 มิติดังนั้นจึงdata.shapeส่งคืนทูเปิลของ (num_samples, num_channels)
เตา

63

เมื่อใช้structโมดูลนี้คุณสามารถใช้ wave frames (ซึ่งอยู่ในไบนารีเสริมของ 2ระหว่าง -32768 และ 32767 (เช่น0x8000และ0x7FFF) สิ่งนี้อ่านไฟล์ MONO, 16-BIT, WAVE ฉันพบว่าหน้าเว็บนี้มีประโยชน์มากในการกำหนดสิ่งนี้:

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]))

ตัวอย่างข้อมูลนี้อ่าน 1 เฟรม หากต้องการอ่านมากกว่าหนึ่งเฟรม (เช่น 13) ให้ใช้

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

2
วิธีจัดการไฟล์สเตอริโอ 24 บิต
Basj

14
สิ่งนี้ทำให้ฉันมีข้อผิดพลาด: "struct.error: unpack ต้องการอาร์กิวเมนต์สตริงที่มีความยาว 2"
Coder404

1
หากคุณเรียกใช้โค้ดชิ้นนี้ด้วยไฟล์เสียงขนาดใหญ่มาก คอมพิวเตอร์ของคุณจะตายเนื่องจากโปรแกรมนี้ต้องการหน่วยความจำ ต้องประมวลผลไฟล์เสียงทีละบล็อกสำหรับไฟล์เสียงขนาดใหญ่
ArthurLambert

@ Coder404 คุณอาจมีไฟล์คลื่นสเตอริโอหรือความลึกของบิตที่แตกต่างกัน
jmilloy

3
สำหรับผู้ที่สงสัยว่าไบนารีเสริม 2s คืออะไรดูที่นี่stackoverflow.com/questions/1049722/what-is-2s-complement
Dennis Golomazov

34

โมดูล Python ต่างๆเพื่ออ่าน wav:

อย่างน้อยมีไลบรารีต่อไปนี้เพื่ออ่านไฟล์เสียง wave:

  • SoundFile
  • scipy.io.wavfile (จากscipy )
  • คลื่น (เพื่ออ่านสตรีมรวมอยู่ใน Python 2 และ 3)
  • scikits.audiolab (ไม่ได้รับการดูแลตั้งแต่ปี 2010)
  • อุปกรณ์เสียง (เล่นและบันทึกเสียงเหมาะสำหรับสตรีมและเรียลไทม์)
  • pyglet
  • librosa (การวิเคราะห์เพลงและเสียง)
  • madmom (เน้นหนักในงานการดึงข้อมูลเพลง (MIR))

ตัวอย่างที่ง่ายที่สุด:

นี่เป็นตัวอย่างง่ายๆด้วย SoundFile:

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

รูปแบบของผลลัพธ์:

คำเตือนข้อมูลไม่ได้อยู่ในรูปแบบเดียวกันเสมอไปทั้งนี้ขึ้นอยู่กับไลบรารี ตัวอย่างเช่น:

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)

เอาท์พุต:

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]

ผลตอบแทนของ SoundFile และ Audiolab จะลอยระหว่าง -1 ถึง 1 (เช่นเดียวกับ matab นั่นคือรูปแบบของสัญญาณเสียง) จำนวนเต็ม Scipy และ wave return ซึ่งคุณสามารถแปลงเป็น float ตามจำนวนบิตของการเข้ารหัสตัวอย่างเช่น:

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 วิธีที่ง่ายที่สุดในการรับข้อมูลเสียงจากไฟล์เสียงไปยังอาร์เรย์ NumPy คือSoundFile :

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

นอกจากนี้ยังรองรับไฟล์ 24 บิตนอกกรอบ

มีไลบรารีไฟล์เสียงมากมายฉันได้เขียนภาพรวมซึ่งคุณจะเห็นข้อดีข้อเสียบางประการ นอกจากนี้ยังมีหน้าอธิบายวิธีการอ่านไฟล์ wav 24 บิตกับwaveโมดูล


หมายเหตุ: soundfile.read () ทำให้เป็นมาตรฐานโดย 2 ^ (n_bits - 1) เช่นเดียวกับในตัวอย่าง scipy.io.wavfile ของ sandoval
Quetzalcoatl

9

คุณสามารถทำได้โดยใช้โมดูลscikits.audiolab ต้องใช้ NumPy และ SciPy เพื่อทำงานและ libsndfile

โปรดทราบว่าฉันสามารถทำให้มันทำงานบน Ubunutu เท่านั้นไม่ใช่บน OSX

from scikits.audiolab import wavread

filename = "testfile.wav"

data, sample_frequency,encoding = wavread(filename)

ตอนนี้คุณมีข้อมูล wav แล้ว


scikits.audiolabไม่ได้รับการอัปเดตตั้งแต่ปี 2010และน่าจะเป็น Python 2 เท่านั้น
Boris

4

หากคุณต้องการประมวลผลบล็อกเสียงทีละบล็อกวิธีแก้ปัญหาบางอย่างที่ให้มานั้นค่อนข้างแย่ในแง่ที่บ่งบอกถึงการโหลดเสียงทั้งหมดลงในหน่วยความจำทำให้แคชจำนวนมากพลาดและทำให้โปรแกรมของคุณช้าลง python-wavefileมีโครงสร้าง pythonic เพื่อทำการประมวลผลแบบ NumPy แบบบล็อกโดยใช้การจัดการบล็อกที่มีประสิทธิภาพและโปร่งใสโดยใช้เครื่องกำเนิดไฟฟ้า สิ่งที่น่าสนใจอื่น ๆ ของ pythonic คือตัวจัดการบริบทสำหรับไฟล์ข้อมูลเมตาเป็นคุณสมบัติ ... และหากคุณต้องการอินเทอร์เฟซไฟล์ทั้งหมดเนื่องจากคุณกำลังพัฒนาต้นแบบอย่างรวดเร็วและคุณไม่สนใจเรื่องประสิทธิภาพอินเทอร์เฟซไฟล์ทั้งหมดยังคงอยู่ที่นั่น

ตัวอย่างง่ายๆของการประมวลผลคือ:

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)

ตัวอย่างนำบล็อกเดียวกันมาใช้ซ้ำเพื่ออ่านทั้งไฟล์แม้ในกรณีของบล็อกสุดท้ายที่มักจะมีขนาดน้อยกว่าที่ต้องการ ในกรณีนี้คุณจะได้รับชิ้นส่วนของบล็อก ดังนั้นวางใจความยาวบล็อกที่ส่งคืนแทนที่จะใช้ขนาด 512 แบบฮาร์ดโค้ดสำหรับการประมวลผลเพิ่มเติม


1

หากคุณกำลังจะดำเนินการถ่ายโอนข้อมูลในรูปแบบของคลื่นแล้วบางทีคุณควรใช้SciPyscipy.io.wavfileเฉพาะ


2
ตกลง. ฉันเพิ่งติดตั้ง SciPy แต่ไม่พบตัวอย่างการใช้งาน scipy.io.wavfile
โรมัน

6
ไม่มีอะไรเหมือนกับล่ามเชิงโต้ตอบในการค้นหาว่าสิ่งต่างๆทำงานอย่างไร! ทะเยอทะยาน!
Ignacio Vazquez-Abrams

1

ฉันต้องการอ่านไฟล์ WAV แบบ 24 บิต 1 ช่อง โพสต์ข้างบนโดยNakมีประโยชน์มาก อย่างไรก็ตามตามที่กล่าวไว้ข้างต้นโดยbasj 24 บิตนั้นไม่ตรงไปตรงมา ในที่สุดฉันก็ใช้งานได้โดยใช้ตัวอย่างต่อไปนี้:

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

จำเป็นต้องมีการปรับมาตราส่วนเพิ่มเติมหากคุณต้องการผลลัพธ์ระหว่าง -1 ถึง +1 บางทีพวกคุณบางคนอาจคิดว่าสิ่งนี้มีประโยชน์


0

หากไฟล์มีเพียงสองไฟล์และอัตราการสุ่มตัวอย่างสูงมากคุณก็สามารถแทรกไฟล์ได้

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

คุณยังสามารถใช้import wavioไลบรารีอย่างง่ายคุณต้องมีความรู้พื้นฐานเกี่ยวกับเสียงด้วย


0

PyDub ( http://pydub.com/ ) ไม่ได้รับการกล่าวถึงและควรได้รับการแก้ไข IMO นี่คือไลบรารีที่ครอบคลุมที่สุดสำหรับการอ่านไฟล์เสียงใน Python ในขณะนี้แม้ว่าจะไม่มีข้อบกพร่องก็ตาม การอ่านไฟล์ 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/

ปล. ตัวอย่างนี้เกี่ยวกับการอ่านไฟล์ wav แต่ PyDub สามารถจัดการรูปแบบต่างๆได้มากมายนอกกรอบ ข้อแม้คือมันขึ้นอยู่กับการสนับสนุน Python wav ดั้งเดิมและ ffmpeg ดังนั้นคุณต้องติดตั้ง ffmpeg และความสามารถของ pydub จำนวนมากต้องอาศัยเวอร์ชัน ffmpeg โดยปกติถ้า ffmpeg ทำได้ pydub ก็ทำได้เช่นกัน (ซึ่งค่อนข้างทรงพลัง)

การไม่ปฏิเสธความรับผิดชอบ: ฉันไม่เกี่ยวข้องกับโครงการนี้ แต่ฉันเป็นผู้ใช้งานหนัก


0

นี่คือโซลูชัน Python 3 โดยใช้โมดูลคลื่นในตัว [1] ซึ่งใช้ได้กับ n ช่องทางและ 8,16,24 ... บิต

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

คุณสามารถเปลี่ยนผลลัพธ์ให้เป็นอาร์เรย์ NumPy

import numpy as np

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

หมายเหตุฉันพยายามทำให้อ่านได้แทนที่จะเร็ว ฉันพบว่าการอ่านข้อมูลทั้งหมดในครั้งเดียวเร็วขึ้นเกือบ 2 เท่า เช่น

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:
    ...

แม้ว่าpython-soundfileจะเร็วกว่าประมาณ 2 คำสั่ง (ยากที่จะเข้าถึงความเร็วนี้ด้วย CPython บริสุทธิ์)

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.