如何使用python提取一段音频文件?

时间:2017-07-08 12:10:36

标签: python audio

我正在尝试从20-30秒记录的心跳音频的.wav文件中提取单个心跳。每次心跳都以高强度声音(峰值)开始,称为" lub"然后另一个心跳从下一个峰值重复。

与使用的库类似,已有的任何算法,可用的教程等等。

1 个答案:

答案 0 :(得分:1)

使用标准库波形模块加载音频。然后将数据转换为numpy数组,然后使用scipy或某些scikits中的峰值检测算法。

一些代码:

import wave
wf = wave.open("file.wav", "r")
fs = wf.getframerate()
nc = wf.getnchannels()
sw = wf.getsampwidth()
nf = wf.getnframes()
data = wf.readframes(nf)
wf.close()

import numpy as np
dt = {1: np.int8, 2: np.int16, 4: np.int32}
a = np.fromstring(data, dtype=dt[sw])

现在您在阵列a上执行峰值检测。您可以使用scikits talkbox中的算法(这是函数的复制粘贴):



def findpeaks (x, neighbours=50):
    """
    Peak seeking algorithm from scikits talkbox.
    Returns indexes of peaks in the x curve.
    """
    peaks = []
    nx = x.size

    assert 2 * neighbours + 1 <= nx

    if nx == 1:
        return [0]
    elif nx == 2:
        if x[0] > x[1]:
            peaks.append(0)
        else:
            peaks.append(1)
        return peaks

    # Handle points which have less than neighs samples on their left
    for i in xrange(neighbours):
        cur = x[i]
        m = x[i+1]
        # look at the left of the current position
        for j in xrange(i):
            if m < x[j]:
                m = x[j]
        # look at the right of the current position
        for j in xrange(i+1, i+neighbours):
            if m < x[j]:
                m = x[j]

        if cur > m:
            peaks.append(i)
            #assert(pkcnt <= (nx / neighbours + 1))

    # Handle points which have at least neighs samples on both their left
    # and right
    for i in xrange(neighbours, nx - neighbours):
        cur = x[i]
        m = x[i+1]
        # look at the left
        for j in xrange(i - neighbours, i):
            if m < x[j]:
                m = x[j]
        # look at the right
        for j in xrange(i+1, i+neighbours):
            if m < x[j]:
                m = x[j]

        if cur > m:
            peaks.append(i)
            #assert(pkcnt <= (nx / neighbours + 1))

    # Handle points which have less than neighs samples on their right
    for i in xrange(nx - neighbours, nx):
        cur = x[i]
        m = x[i-1]
        # look at the left
        for j in xrange(i - neighbours, i):
            if m < x[j]:
                m = x[j]

        # look at the right
        for j in xrange(i+1, nx):
            if m < x[j]:
                m = x[j]

        if cur > m:
            peaks.append(i)
            #assert(pkcnt <= (nx / neighbours + 1))

    return peaks

peaks = findpeaks(a)
# And now you get the segment of a signal by slicing it:
# For example (assuming you checked what peaks you want and if they were found at all):
s = a[peaks[0]:peaks[1]] # Let say you want first two peaks regardless the sign

# As you can see, this concrete algo is not strictly numpy dependant. You just have to change line nx = x.size into nx = len(x) to avoid it completely.
# So you can "throw" numpy away by loading wave data into a list like this:

import struct
dt = {1: "c", 2: "h", 4: "l"}
a = struct.unpack("<"+dt[sw], data)
# And then you use a as demonstrated above i.e. as an array. But numpy will give you more flexibility, speed and options to work on.