Read the data of a single channel from a stereo wave file in Python
Asked Answered
I

3

5

I have to read the data from just one channel in a stereo wave file in Python. For this I tried it with scipy.io:

import scipy.io.wavfile as wf
import numpy

def read(path):
    data = wf.read(path)
    for frame in data[1]:
        data = numpy.append(data, frame[0])
    return data

But this code is very slow, especially if I have to work with longer files. So does anybody know a faster way to do this? I thought about the standard wave module by using wave.readframes(), but how are the frames stored there?

Ivan answered 18/4, 2014 at 12:42 Comment(0)
H
18

scipy.io.wavfile.read returns the tuple (rate, data). If the file is stereo, data is a numpy array with shape (nsamples, 2). To get a specific channel, use a slice of data. For example,

rate, data = wavfile.read(path)
# data0 is the data from channel 0.
data0 = data[:, 0]
Hypothesize answered 18/4, 2014 at 15:26 Comment(4)
In a stereo file, "channel" refers to either the left or right signal. That is, left = data[:, 0], right = data[:, 1]. See also #13996436Hypothesize
this works great, however how do i pass this channel without saving as wav to another function?Apodosis
That depends on the format of the input expected by the other function. You should create a new stackoverflow question for this. The comments are not the place to solve new problems.Hypothesize
thx u sir, i created a question - could u pls look into it? #63467845Apodosis
T
7

The wave module returns the frames as a string of bytes, which can be converted to numbers with the struct module. For instance:

def oneChannel(fname, chanIdx):
""" list with specified channel's data from multichannel wave with 16-bit data """
    f = wave.open(fname, 'rb')
    chans = f.getnchannels()
    samps = f.getnframes()
    sampwidth = f.getsampwidth()
    assert sampwidth == 2
    s = f.readframes(samps) #read the all the samples from the file into a byte string
    f.close()
    unpstr = '<{0}h'.format(samps*chans) #little-endian 16-bit samples
    x = list(struct.unpack(unpstr, s)) #convert the byte string into a list of ints
    return x[chanIdx::chans] #return the desired channel

If your WAV file has some other sample size, you can use the (uglier) function in another answer I wrote here.

I've never used scipy's wavfile function so I can't compare speed, but the wave and struct approach I use here has always worked for me.

Testamentary answered 18/4, 2014 at 13:11 Comment(0)
U
2

rate, audio = wavfile.read(path)

audio = np.mean(audio, axis=1)

Utta answered 16/9, 2019 at 16:53 Comment(2)
could you please add some explanation to your answer and format it properly? The question already has an accepted answer.Rill
@Elijah: Please read community guidelines on writing good answer: https://stackoverflow.com/help/how-to-answerKeyway

© 2022 - 2024 — McMap. All rights reserved.