2013-07-18 35 views
2

我用Pyaudio從我的麥克風中捕獲音頻並嘗試使用opus編解碼器對它進行編碼/解碼。我使用SvartalF製作的libopus綁定(https://github.com/svartalf/python-opus)。Python:PortAudio + Opus編碼/解碼

這裏是我的代碼:

import pyaudio 
from opus import encoder, decoder 

def streaming(p): 
    chunk = 960 
    FORMAT = pyaudio.paInt16 
    CHANNELS = 1 
    RATE = 48000 
    streamin = p.open(format = FORMAT, 
      channels = CHANNELS, 
      rate = RATE, 
      input = True, 
      input_device_index = 7, 
      frames_per_buffer = chunk) 
    streamout = p.open(format = FORMAT, 
      channels = CHANNELS, 
      rate = 48000, 
      output = True, 
      output_device_index = p.get_default_input_device_info()["index"], 
      frames_per_buffer = chunk) 
    enc = encoder.Encoder(RATE,CHANNELS,'voip') 
    dec = decoder.Decoder(RATE,CHANNELS) 
    data = [] 
    for i in xrange(100): 
     data.append(streamin.read(chunk*2)) 
    streamout.write(''.join(data)) 
    encdata = [] 
    for x in data: 
     encdata.append(enc.encode(x,chunk)) 
    print "DATA LENGTH :", len(''.join(data)) 
    print "ENCDATA LENGTH :", len(''.join(encdata)) 
    decdata = '' 
    for x in encdata: 
     decdata += dec.decode(x,chunk) 
    print "DECDATA LENGTH :", len(decdata) 
    streamout.write(decdata) 
    streamin.close() 
    streamout.close() 


p = pyaudio.PyAudio() 
streaming(p) 
p.terminate() 

我必須把chunk*2而不是chunkdata.append(streamin.read(chunk*2))DECDATA LENGTH == DATA LENGTH*2,我不知道爲什麼。

輸出:

DATA LENGTH : 384000 
ENCDATA LENGTH : 12865 
DECDATA LENGTH : 384000 

沒有編碼/解碼中,第一streamout.write(''.join(data))完美。有了編碼/解碼,streamout.write(decdata)有點不錯,但有很多混雜的脆皮。

我在做什麼錯在這裏?

+0

你好!我現在沒有時間去支持python-opus,但是你可以爲它分配和貢獻,所以我可以用一個固定版本來更新PyPI回購。 – SvartalF

回答

1

這似乎是由解碼方法中的python-opus中的錯誤引起的。

根據Opus API,opus_decode返回解碼的樣本數。 python綁定假設它將完全填充它傳入的結果緩衝區,所以每個解碼樣本集都會有一個沉默。這種沉默導致低幀尺寸下的開裂和更高幀尺寸下的缺口。儘管文檔沒有提及任何內容,但看起來返回的數字是每個頻道。

更改線路150 of opus/api/decoder.py以下修復它爲我:

return array.array('h', pcm[:result*channels]).tostring() 

decode_float方法可能需要同樣的變化,如果你需要使用。

-1

只需將輸出減半並取第一部分即可。 通過試驗和錯誤我發現這個解決方案令人滿意。

from opus import decoder as opus_decoder 
from opus import encoder as opus_encoder 

class OpusCodec(): 
    def __init__(self, *args, **kwargs): 
     self.chunk = 960 
     self.channels = 1 
     self.rate = 48000 
     self.encoder = opus_encoder.Encoder(
      self.rate, 
      self.channels, 
      opus_encoder.APPLICATION_TYPES_MAP['voip'] 
     ) 
     self.decoder = opus_decoder.Decoder(
      self.rate, 
      self.channels, 
     ) 

    def encode(self, data, **kwargs): 
     if not 'frame_size' in kwargs: 
      kwargs['frame_size'] = self.chunk 
     out = self.encoder.encode(data, frame_size=self.chunk) 
     return out 

    def decode(self, data, **kwargs): 
     if not 'frame_size' in kwargs: 
      kwargs['frame_size'] = self.chunk 
     out = self.decoder.decode(data, **kwargs) 
     return out[0:int(len(out)/2)] # hackety hack :D