How do you separate each channel of a two channel wav file into two different files using wavio? or another library?
Solution 1
I don't know what is wrong with the wavio
code, but here's how you can separate WAV channel using standard Python module wave
(which is also used by wavio) and numpy:
import wave
import numpy as np
def save_wav_channel(fn, wav, channel):
'''
Take Wave_read object as an input and save one of its
channels into a separate .wav file.
'''
# Read data
nch = wav.getnchannels()
depth = wav.getsampwidth()
wav.setpos(0)
sdata = wav.readframes(wav.getnframes())
# Extract channel data (24-bit data not supported)
typ = { 1: np.uint8, 2: np.uint16, 4: np.uint32 }.get(depth)
if not typ:
raise ValueError("sample width {} not supported".format(depth))
if channel >= nch:
raise ValueError("cannot extract channel {} out of {}".format(channel+1, nch))
print ("Extracting channel {} out of {} channels, {}-bit depth".format(channel+1, nch, depth*8))
data = np.fromstring(sdata, dtype=typ)
ch_data = data[channel::nch]
# Save channel to a separate file
outwav = wave.open(fn, 'w')
outwav.setparams(wav.getparams())
outwav.setnchannels(1)
outwav.writeframes(ch_data.tostring())
outwav.close()
wav = wave.open(WAV_FILENAME)
save_wav_channel('ch1.wav', wav, 0)
save_wav_channel('ch2.wav', wav, 1)
Solution 2
Try ffmpeg on the command line:
Output each channel in stereo input to individual mono files:
ffmpeg -i stereo.wav -map_channel 0.0.0 left.wav -map_channel 0.0.1 right.wav
I tried it on your file and it seems to work.
If you need it to be in a python program just use os.system(cmd)
Solution 3
I think that you are trying to make it much more complicated than it needs to be. If you don't mind using scipy
than separating the channels comes down to:
from scipy.io import wavfile
fs, data = wavfile.read('guitarup_full.wav') # reading the file
wavfile.write('guitar_channel_1.wav', fs, data[:, 0]) # saving first column which corresponds to channel 1
wavfile.write('guitar_channel_2.wav', fs, data[:, 1]) # saving second column which corresponds to channel 2
Solution 4
Method 1 (Using ffmpeg)
You can use ffmpeg to split a n-channeled .wav like this:
ffmpeg -i input.wav -map 0:1 1.wav -map 0:2 2.wav -map 0:3 3.wav........
You can run this is python using:
import os
os.system(ffmpeg -i 14ch.mov -map 0:1 1.wav -map 0:2 2.wav -map 0:3 3.wav........)
Method 2(Using sox)
Install sox from here
Then try this:
sox infile.wav outfile_left.wav remix 1
sox infile.wav outfile_right.wav remix 2
Again you can run this in python using os.system
Solution 5
Thanks to everyone for taking the time to read and run my scripts. I finally found a solution. Follow the steps in this order:
First, uninstall wavio by:
pip uninstall wavio
Second, uninstall numpy too:
pip uninstall numpy
Finally, install wavio again. numpy will be installed as a dependency:
pip install wavio
numpy will be installed as a dependency.
Installing collected packages: numpy, wavio
Successfully installed numpy-1.14.5 wavio-0.0.4
Then my original script will deliver the solution needed.
The use of ffmpeg as @Marichyasana suggested is a good workaround. However python libraries (such as pyglet, python audio tools, etc) have problems reading these output files back.
I hope people find this solution helpful !
Related videos on Youtube
mm_
One part scientist, one part entrepreneur, one part programmer.
Updated on September 15, 2022Comments
-
mm_ almost 2 years
The following script plays the original file ok. I try to separate each channel in the obvious way, but it does not work.
import os import wavio import numpy import pyglet file_name = "guitarup_full.wav" # I get the file ! File = wavio.read(file_name) rate = File.rate # it looks good print File.data.shape print rate # and then the channels: channel_1 = File.data[:,0] channel_2 = File.data[:,1] wavio.write("guitar_channel_1.wav", channel_1, rate ) wavio.write("guitar_channel_2.wav", channel_2, rate ) # now we try to play: source = pyglet.resource.media(file_name, streaming=False) source_ch1 = pyglet.resource.media("guitar_channel_1.wav", streaming=False) source_ch2 = pyglet.resource.media("guitar_channel_2.wav", streaming=False) #uncomment the one you want to listen. source.play() #source_ch1.play() #source_ch2.play() pyglet.app.run()
The first sounds like a guitar, the second and third like Gaussian noise. Can someone tell me what is wrong with it?
The audio file I used is: https://www.freesounds.info/global/wav.php?fileid=11
The shape of the data is: (88471, 2) rate is: 44100
Also, if I play the file in another player, I get the same: Gaussian Noise.
Note: The use of pyglet is superfluous for the problem. If you use it to investigate this issue, make sure the files are in a folder registered in the resources folder. To do that:
pyglet.resource.path.append("your_sounds_location") pyglet.resource.reindex()
-
mm_ almost 6 yearsdid you check this script with the sound file I sent? still Gaussian noise.
-
Andriy Makukha almost 6 years@mm_, I did. And it worked well for me. What is the size of the files that you get?
-
Andriy Makukha almost 6 yearsJust confirmed: it works well with Python 2.x as well as Python 3. Numpy versions are 1.11.2 and 1.14.0.
-
mm_ almost 6 yearsnumpy version '1.14.5', python 2.7.15rc1, File.data.shape = (88471, 2), not working for me.
-
Andriy Makukha almost 6 years@mm_, I mean, what are the sizes of resulting files? They are named
ch1.wav
andch2.wav
in my script. -
Andriy Makukha almost 6 yearsOh, the sizes are actually not helpful because they are the same as produced by your script.
md5sum
's of the correct resulting files area00c..5c75
anda934..ff63
. md5sum of the input file isc98f..3a12
. MD5 hashes of the files produced by your wavio code are8c84..ee17
and3827..c478
. They are the same for both versions of Python on my machine. -
mm_ almost 6 yearsyou might had hit the core of the problem. "md5sum's of the correct resulting files are a00c..5c75 and a934..ff63. md5sum of the input file is c98f..3a12. MD5 hashes of the files produced by your wavio code are 8c84..ee17 and 3827..c478." what do you mean?
-
mm_ almost 6 yearsThis is a great work around. However I ultimately want to manipulate the data or each channel. This method does not tell me how each channel is assembled from the original.
-
mm_ almost 6 yearsmore importantly, how can I use md5sum to solve my problem?
-
Andriy Makukha almost 6 yearsYour script writes file
guitar_channel_1.wav
to your machine. You can runmd5sum guitar_channel_1.wav
on Linux to find the MD5 hash. My script writes filech1.wav
. You can runmd5sum ch1.wav
to find the MD5 hash. This will show whether your machine produces the correct files and explain why this code doesn't work for you. -
mm_ almost 6 yearsI see. What can I do with this info? How can I produce the correct file?
-
mm_ almost 6 yearsThanks for your answer. Unfortunately, I can't open the left.wav and right.wav with pyglet, don't know why. Do you know if ffmpeg return compressed wav files? Can I specify the output format explicitly?
-
mm_ almost 6 yearsThe output are sound files alright, but not uncompressed wav files. How can I indicate the output format? the documentation ffmpeg.org/ffmpeg.html#Video-and-Audio-grabbing is not clear on this point unfortunately.
-
Andriy Makukha almost 6 yearsI also cannot play the resulting WAV files from the ffmpeg command with pyglet. I get en exception
pyglet.media.sources.riff.WAVEFormatException: Not a WAVE file
. I think, pyglet just cannot recognize the WAV file header because ffmpeg adds some metadata. But the files are certainly uncompressed .wav and can be read by Python'swave
(as in my script). Maybe there is an option to avoid adding metadata to WAV? -
Andriy Makukha almost 6 yearsIt's a standard debugging process. We cannot tell why this Python code doesn't work for you if we don't know what's going on on your machine. Meanwhile I checked again: my script works correctly on another machine with three different Python versions.
-
mm_ almost 6 yearsJust tested this method. It works of course. For my app I need both the arrays and files.
-
machnic almost 6 yearsThen probably it's the simplest solution you can get. You have both channels stored as columns inside
data
(numpy array) and you can change them in any way you need - before of after saving -
mm_ almost 6 yearsyou are right. That's how I implemented my solution.
-
ERJAN almost 4 years@machnic, how to send that channel as data to another function without saving? pls help
-
machnic almost 4 years@ERJAN Exactly the same way as in the example above. You can use variable data with any function you need. I'm not sure what's unclear about it.