之前写过一个音频切割的工具,就是将通话录音切成一句一句的小音频。现在我要将这些小音频加上静音段再合并成大的音频使得每句话的间隔更大。
先讲一下wav音频的格式,wav音频由44位头信息+pcm组成,头信息包括采样率,位数,比特率,声道,音频长度等,pcm就是我们的语音信息,都是由很小的采样点组成的
其实音频组成很简单就是去掉所有音频的44为头信息,将他们的pcm拼接在一起,再根据总的pcm和数据格式写一个头出来就行了。
第一次写先将所有的wav路径读了出来,再根据路径读取wav的pcm,存到一个大的list中,之后统计list长度当作音频长度就好了。但是当我读一个1小时的音频时因为采样点太多了,所以list被撑爆了。
第二次写直接就是边读边写,不用list存储了seek跳过44字节头,直接读一个wav的pcm然后写入新的wav,到最后seek返回来根据音频长度写wav头。每次只读一个小wav保证了内存不浪费,这个可行。但是结束了吗?这多没有意思啊
第三种方法最近看dpark,知道了RDD弹性分布式数据集合,就想拿这个写一个,RDD就是你的数据源,而RDD又由好多Split块组成,在这些音频合并中你可以看作新组成的大音频是RDD,而那些要合并的小音频是Split块。而我们需要小音频的pcm,和pcm长度所以Split类我们这样设定
1 class Split(object): 2 3 def __init__(self, wavfile): 4 self.file_path = wavfile 5 self.pcm_data = [] 6 self.pcm_len = 0 7 8 def get_split(self): 9 return self.pcm_data 10 11 def get_split_len(self): 12 return self.pcm_len 13 14 def compute_split(self): 15 if not os.path.exists(self.file_path): 16 print self.file_path,'not exists !' 17 return False 18 19 fp = open(self.file_path, 'ab') 20 fp.read(44) 21 22 sample = fp.read(2) 23 while sample: 24 yield struct.unpack("i", sample) 25 sample = fp.read(2) 26 self.pcm_data.append(struct.unpack("i", sample)) 27 28 self.pcm_len = len(self.pcm_data)
说一下compute_split这个方法,对于每个小音频我们要copy他们的pcm的值写入文件,所以就涉及到了遍历,一种可以是之间返回pcm_data在其他类中随便遍历,另一种是自己写好遍历方法,只返回遍历的值。我倾向这种方法,因为后期如果有大量写遍历,某天如果要改遍历方式就要改大量代码。所以封装成方法方便修改。好了Split类就写完了,下面是RDD类
1 class RDD(object): 2 3 def __init__(self): 4 self._split = [] 5 self.pcm_len = 0 6 7 def wav_join(self, wav_src): 8 if os.path.isdir(wav_src, list): 9 self._split = [Split(os.path.join(wav_path)) for wav_path in os.listdir(wav_src)] 10 else: 11 print "目录错误" 12 return False 13 return True 14 15 def create_wav(self, wav_dec): 16 fp = open(wav_dec, 'w') 17 18 fp.seek(44) 19 for wavSplit in self._split: 20 for wavByte in wavSplit: 21 fp.write(struct.pack('h', wavByte)) 22 self.pcm_len += wavSplit.get_pcm_len() 23 24 fp.seek(0) 25 '''写44位的头''' 26 fp.close()
wav_join方法就是将每个小wav文件变成Split块,create_wav方法是遍历Split块,读取每一个块的pcm写入新的wav文件去,这种思想就是分布式的思想,而且每一个Split数据块不冲突,在理想情况下可以每个Split都开一个线程进行并发处理。而且它又符合分治思想,将一个大问题分解为和大问题结构相同的小问题,小问题解决后,合并起来大问题就解决了,这种结构也方便编程,每个Split处理方法都一样。实际处理我们可以建立一个任务队列,将要处理的Split放进队列,然后多线程取,处理,再加个任务调度处理那些出现问题的Split,再加个网络通信,还有一些计算方法,是不是一个分布式框架就出现了,写不出来只能yy了,哈哈...而且这份代码只是大概思路,还有一些问题,自己动手丰衣足食。反正我是写完了