分离然后加入 .wav 立体声通道的断断续续的音频
Choppy audio from separating and then joining .wav stereo channels
我目前正在使用 python 处理 .wav 文件,使用 Pyaudio 流式传输音频,并使用 python wave 库加载文件数据。
我计划稍后包括处理各个立体声通道,关于信号的振幅和立体声信号的声相,但现在我只是想分离波形文件的两个通道,然后将它们拼接回去在一起 - 希望最终得到与输入数据相同的数据。
下面是我的代码。
getRawSample 方法工作得很好,我可以通过该函数流式传输音频。
问题是我的 getSample 方法。沿线的某个地方,我将两个音频通道分开,然后将它们重新连接在一起,音频会失真。我什至注释掉了我进行振幅和平移调整的部分,所以理论上它是数据输入 -> 数据输出。
下面是我的代码示例:
class Sample(threading.Thread) :
def __init__(self, filepath, chunk):
super(Sample, self).__init__()
self.CHUNK = chunk
self.filepath = filepath
self.wave = wave.open(self.filepath, 'rb')
self.amp = 0.5 # varies from 0 to 1
self.pan = 0 # varies from -pi to pi
self.WIDTH = self.wave.getsampwidth()
self.CHANNELS = self.wave.getnchannels()
self.RATE = self.wave.getframerate()
self.MAXFRAMEFEEDS = self.wave.getnframes()/self.CHUNK # maximum even number of chunks
self.unpstr = '<{0}h'.format(self.CHUNK*self.WIDTH) # format for unpacking the sample byte string
self.pckstr = '<{0}h'.format(self.CHUNK*self.WIDTH) # format for unpacking the sample byte string
self.framePos = 0 # keeps track of how many chunks of data fed
# panning and amplitude adjustment of input sample data
def panAmp(self, data, panVal, ampVal): # when panning, using constant power panning
[left, right] = self.getChannels(data)
#left = np.multiply(0.5, left) #(np.sqrt(2)/2)*(np.cos(panVal) + np.sin(panVal))
#right = np.multiply(0.5, right) # (np.sqrt(2)/2)*(np.cos(panVal) - np.sin(panVal))
outputList = self.combineChannels(left, right)
dataResult = struct.pack(self.pckstr, *outputList)
return dataResult
def getChannels(self, data):
dataPrepare = list(struct.unpack(self.unpstr, data))
left = dataPrepare[0::self.CHANNELS]
right = dataPrepare[1::self.CHANNELS]
return [left, right]
def combineChannels(self, left, right):
stereoData = left
for i in range(0, self.CHUNK/self.WIDTH):
index = i*2+1
stereoData = np.insert(stereoData, index, right[i*self.WIDTH:(i+1)*self.WIDTH])
return stereoData
def getSample(self, panVal, ampVal):
data = self.wave.readframes(self.CHUNK)
self.framePos += 1
if self.framePos > self.MAXFRAMEFEEDS: # if no more audio samples to process
self.wave.rewind()
data = self.wave.readframes(self.CHUNK)
self.framePos = 1
return self.panAmp(data, panVal, ampVal)
def getRawSample(self): # for debugging, bypasses pan and amp functions
data = self.wave.readframes(self.CHUNK)
self.framePos += 1
if self.framePos > self.MAXFRAMEFEEDS: # if no more audio samples to process
self.wave.rewind()
data = self.wave.readframes(self.CHUNK)
self.framePos = 1
return data
我怀疑我拼接左右声道的方式有问题,但不确定。
我用 16 位 44100khz .wav 文件加载项目。
下面是音频文件的 link,以便您可以听到生成的音频输出。
第一部分是 运行 两个文件(都是两个通道),通过 getSample 方法,而下一部分是 运行 这些相同的文件,通过 getRawSample 方法。
https://dl.dropboxusercontent.com/u/24215404/pythonaudiosample.wav
根据音频,如前所述,立体声文件似乎失真了。看上面文件的波形,好像经过getSample方法后左右声道完全一样
如果需要,我也可以 post 我的代码包括主要功能。
希望我的问题不是太含糊,但我非常感谢任何帮助或意见!
就像经常发生的那样,我睡了一觉,第二天醒来发现了一个解决方案。
问题出在 combineChannels 函数中。
以下是工作代码:
def combineChannels(self, left, right):
stereoData = left
for i in range(0, self.CHUNK):
index = i*2+1
stereoData = np.insert(stereoData, index, right[i:(i+1)])
return stereoData
变化是
- 对于循环边界:因为我在列表 left 和 right 中有 1024 个项目(与我的块大小相同),我当然需要遍历每一个。
- 索引:索引定义保持不变
- stereoData:再次,我记得我在处理列表,每个列表都包含一帧音频。问题中的代码假定我的列表存储为字节串,但事实并非如此。如您所见,生成的代码要简单得多。
我目前正在使用 python 处理 .wav 文件,使用 Pyaudio 流式传输音频,并使用 python wave 库加载文件数据。 我计划稍后包括处理各个立体声通道,关于信号的振幅和立体声信号的声相,但现在我只是想分离波形文件的两个通道,然后将它们拼接回去在一起 - 希望最终得到与输入数据相同的数据。
下面是我的代码。
getRawSample 方法工作得很好,我可以通过该函数流式传输音频。
问题是我的 getSample 方法。沿线的某个地方,我将两个音频通道分开,然后将它们重新连接在一起,音频会失真。我什至注释掉了我进行振幅和平移调整的部分,所以理论上它是数据输入 -> 数据输出。
下面是我的代码示例:
class Sample(threading.Thread) :
def __init__(self, filepath, chunk):
super(Sample, self).__init__()
self.CHUNK = chunk
self.filepath = filepath
self.wave = wave.open(self.filepath, 'rb')
self.amp = 0.5 # varies from 0 to 1
self.pan = 0 # varies from -pi to pi
self.WIDTH = self.wave.getsampwidth()
self.CHANNELS = self.wave.getnchannels()
self.RATE = self.wave.getframerate()
self.MAXFRAMEFEEDS = self.wave.getnframes()/self.CHUNK # maximum even number of chunks
self.unpstr = '<{0}h'.format(self.CHUNK*self.WIDTH) # format for unpacking the sample byte string
self.pckstr = '<{0}h'.format(self.CHUNK*self.WIDTH) # format for unpacking the sample byte string
self.framePos = 0 # keeps track of how many chunks of data fed
# panning and amplitude adjustment of input sample data
def panAmp(self, data, panVal, ampVal): # when panning, using constant power panning
[left, right] = self.getChannels(data)
#left = np.multiply(0.5, left) #(np.sqrt(2)/2)*(np.cos(panVal) + np.sin(panVal))
#right = np.multiply(0.5, right) # (np.sqrt(2)/2)*(np.cos(panVal) - np.sin(panVal))
outputList = self.combineChannels(left, right)
dataResult = struct.pack(self.pckstr, *outputList)
return dataResult
def getChannels(self, data):
dataPrepare = list(struct.unpack(self.unpstr, data))
left = dataPrepare[0::self.CHANNELS]
right = dataPrepare[1::self.CHANNELS]
return [left, right]
def combineChannels(self, left, right):
stereoData = left
for i in range(0, self.CHUNK/self.WIDTH):
index = i*2+1
stereoData = np.insert(stereoData, index, right[i*self.WIDTH:(i+1)*self.WIDTH])
return stereoData
def getSample(self, panVal, ampVal):
data = self.wave.readframes(self.CHUNK)
self.framePos += 1
if self.framePos > self.MAXFRAMEFEEDS: # if no more audio samples to process
self.wave.rewind()
data = self.wave.readframes(self.CHUNK)
self.framePos = 1
return self.panAmp(data, panVal, ampVal)
def getRawSample(self): # for debugging, bypasses pan and amp functions
data = self.wave.readframes(self.CHUNK)
self.framePos += 1
if self.framePos > self.MAXFRAMEFEEDS: # if no more audio samples to process
self.wave.rewind()
data = self.wave.readframes(self.CHUNK)
self.framePos = 1
return data
我怀疑我拼接左右声道的方式有问题,但不确定。 我用 16 位 44100khz .wav 文件加载项目。 下面是音频文件的 link,以便您可以听到生成的音频输出。 第一部分是 运行 两个文件(都是两个通道),通过 getSample 方法,而下一部分是 运行 这些相同的文件,通过 getRawSample 方法。
https://dl.dropboxusercontent.com/u/24215404/pythonaudiosample.wav
根据音频,如前所述,立体声文件似乎失真了。看上面文件的波形,好像经过getSample方法后左右声道完全一样
如果需要,我也可以 post 我的代码包括主要功能。 希望我的问题不是太含糊,但我非常感谢任何帮助或意见!
就像经常发生的那样,我睡了一觉,第二天醒来发现了一个解决方案。
问题出在 combineChannels 函数中。
以下是工作代码:
def combineChannels(self, left, right):
stereoData = left
for i in range(0, self.CHUNK):
index = i*2+1
stereoData = np.insert(stereoData, index, right[i:(i+1)])
return stereoData
变化是
- 对于循环边界:因为我在列表 left 和 right 中有 1024 个项目(与我的块大小相同),我当然需要遍历每一个。
- 索引:索引定义保持不变
- stereoData:再次,我记得我在处理列表,每个列表都包含一帧音频。问题中的代码假定我的列表存储为字节串,但事实并非如此。如您所见,生成的代码要简单得多。