2016-01-12 54 views
0

我在蟒蛇新我嘗試構建一個知道解碼和編碼雙音多頻(DTMF)用於撥打電話信號的程序。 對於現在的編碼部分工作不錯,但由於某些原因,編碼不工作,我得到的follwoing例外屬性錯誤,而讀wav文件

Traceback (most recent call last): 
     File "C:\Users\matant\workspace\dialer2\dialer.py", line 239, in <module> 
     x = d.decoder() 
     File "C:\Users\matant\workspace\dialer2\dialer.py", line 218, in decoder 
     data = self.read_wav() 
     File "C:\Users\matant\workspace\dialer2\dialer.py", line 201, in read_wav 
     n = fin.getnframes() 
    AttributeError: 'file' object has no attribute 'getnframes' 

,你可以看到我寫幀到文件,所以我不明白爲什麼它happend: 這是我的代碼:

''' 
Created on Jan 10, 2016 

@author: matant 
''' 
import json 
from math import pi, sin 
import wave 
import logging 
import struct 
import os 

ROW_FREQ = (697, 770, 852, 941) 
COL_FREQ = (1209, 1336, 1477, 1633) 
SAMPLE_RATE = 44100 
SAMPLE_WIDTH = 2 
NUMBER_OF_CHANNELS = 1 
COMPRESSION_TYPE = "NONE" 
COMPRESSION_NAME = "Uncompressed" 
PI2 = 6.283185306 
scale = 32767 #16-bit unsigned short 

keys= '1','2','3','A',\ 
    '4','5','6','B',\ 
    '7','8','9','C',\ 
    '*','0','#','D' 

FREQUENCY_MAP = dict() 
FREQUENCY_MAP['1'] = (697, 1209) 
FREQUENCY_MAP['2'] = (697, 1336) 
FREQUENCY_MAP['3'] = (697, 1477) 
FREQUENCY_MAP['A'] = (697, 1633) 
FREQUENCY_MAP['4'] = (770, 1209) 
FREQUENCY_MAP['5'] = (770, 1336) 
FREQUENCY_MAP['6'] = (770, 1477) 
FREQUENCY_MAP['B'] = (770, 1633) 
FREQUENCY_MAP['7'] = (852, 1209) 
FREQUENCY_MAP['8'] = (852, 1336) 
FREQUENCY_MAP['9'] = (852, 1477) 
FREQUENCY_MAP['C'] = (852, 1633) 
FREQUENCY_MAP['*'] = (941, 1209) 
FREQUENCY_MAP['0'] = (941, 1336) 
FREQUENCY_MAP['#'] = (941, 1477) 
FREQUENCY_MAP['D'] = (941, 1633) 
FREQUENCY_MAP['S'] = (0, 0) 

logging.basicConfig(level=logging.DEBUG, 
        format='%(asctime)s <%(levelname)s> %(module)s.%(funcName)s() %(message)s', 
        datefmt='%Y-%m-%d %H:%M:%S') 

log = logging.getLogger(__name__) 


class DTMF: 
    VALID_SEQUENCE_TYPES = [list, tuple, set] 

    def __init__(self, input_string=None, input_list=None): 
     """ 
     Initializes a DTMF instance with an option DTMF sequence. This can be a list of lists or a json string. 
     If both are supplied, it tries to parse the json_string. If it does, it uses that. If there are errors, it 
     validates the list and tries to use that. Basically input_string takes precedence. 
     General workflow would be setting dtmf_sequence and calling generate_raw_data. This data can then be saved to a 
     .wav file or compressed and saved as other, smaller, file formats. 
     :param input_list: list of lists or tuples of the form [['A', 100], ['S', 50], ['2', 100], ['S', 50]] 
     :param input_string: json_string of the form '[["A", 100], ["S", 50], ["2", 100], ["S", 50]]' 
     """ 
     log.debug("Creating instance of DTMF") 
     log.debug("input_string = {}".format(input_string)) 
     log.debug("input_list = {}".format(input_list)) 

     self._dtmf_sequence = None 
     self._raw_data = None 

     if input_string is not None: 
      converted_json_sequence = self.parse_json_string(input_string) 
      self._dtmf_sequence = converted_json_sequence 
     elif input_list is not None: 
      self._dtmf_sequence = input_list 


    @property 
    def dtmf_sequence(self): 
     return self._dtmf_sequence 

    @dtmf_sequence.setter 
    def dtmf_sequence(self, input_sequence): 
     if type(input_sequence) == str: 
      input_sequence = self.parse_json_string(input_sequence) 
     if type(input_sequence) == list: 
      if self._dtmf_sequence_is_valid(input_sequence): 
       self._dtmf_sequence = input_sequence 
     log.debug("Set _dtmf_sequence to {}".format(self._dtmf_sequence)) 

    def parse_json_string(self, input_string): 
     return json.loads(input_string) 


    def generate_raw_data(self): 
     """ 
     Generates raw data that can be saved into a .wav file. This can take some time to generate. 
     :raise AttributeError: If no dtmf sequence has been set 
     """ 
     _data = list() 
     if self._dtmf_sequence is None: 
      raise AttributeError("No dtmf sequence set") 

     for tone_tuple in self._dtmf_sequence: 
      key = tone_tuple[0] 
      tone_duration = tone_tuple[1] 
      f1 = FREQUENCY_MAP[key][0] 
      f2 = FREQUENCY_MAP[key][1] 
      _data += (self.generate_tone(f1, f2, tone_duration)) 
     self._raw_data = _data 

    def save_wave_file(self, file_path): 
     if self._raw_data is None or len(self._raw_data) < 1: 
      self.generate_raw_data() 

     f = wave.open(file_path, 'w') 
     f.setnchannels(NUMBER_OF_CHANNELS) 
     f.setsampwidth(SAMPLE_WIDTH) 
     f.setframerate(SAMPLE_RATE) 
     f.setnframes(len(self._raw_data)) 
     f.setcomptype(COMPRESSION_TYPE, COMPRESSION_NAME) 
     log.info("Saving wav file {} THIS MAY TAKE A WHILE".format(file_path)) 
     for i in self._raw_data: 
      f.writeframes(struct.pack('i', i)) 
     log.info("Saved file to {0}".format(file_path)) 
     f.close() 

    @staticmethod 
    def dtmf_sequence_is_valid(input_list): 
     """ 
     Validates an input sequence for proper structure and contents. 
     :param input_list: 
     :return: 
     """ 
     if type(input_list) is not list: 
      log.warning('input_list must be a list instance') 
      return False 

     if [(type(item) in DTMF.VALID_SEQUENCE_TYPES) for item in input_list].count(False) != 0: 
      log.warning('input_list contains invalid sequence type') 
      return False 

     for item in input_list: 
      if type(item[0]) != str or type(item[1]) != int: 
       log.debug("Type list[0]: {}".format(type(item[0]))) 
       log.debug("Type list[1]: {}".format(type(item[1]))) 
       log.warning('input_list must contain a list of sequences of [str, int]') 
       return False 
     return True 

    @staticmethod 
    def generate_tone(f1, f2, _duration_in_ms): 
     """ 
     Generates a single value representing a sample of two combined frequencies. 
     :param f1: 
     :param f2: 
     :param _duration_in_ms: 
     :return: 
     """ 
     assert f1 in ROW_FREQ or f1 == 0 
     assert f2 in COL_FREQ or f2 == 0 
     number_of_samples = int(SAMPLE_RATE * _duration_in_ms/1000) 
     scale = 32767 # signed int/2 

     result = list() 
     for i in range(number_of_samples): 
      p = i * 1.0/SAMPLE_RATE 
      result.append(int((sin(p * f1 * pi * 2) + sin(p * f2 * pi * 2))/2 * scale)) 
     log.info(
      "Generated {0}ms tone of {1} samples with F1: {2} F2: {3}".format(_duration_in_ms, number_of_samples, f1, 
                       f2)) 
     return result 

    def create_dtmf_wave_file(self, input_sequence, file_path, dump_to_csv=False): 
     """ 
     A convenience method. Validates and assigns a dtmf_sequence, then generates data and saves to a .wav 
     :param input_sequence: list of lists or tuples of the form [['A', 100], ['S', 50], ['2', 100], ['S', 50]] or json_string of the form '[["A", 100], ["S", 50], ["2", 100], ["S", 50]]' 
     :param file_path: the full path of the wav file that will be saved 
     """ 
     self._dtmf_sequence = input_sequence 
     self.generate_raw_data() 

     try: 
      os.remove('dtmf_dump.csv') 
     except: 
      pass # file doesn't exist 

     if dump_to_csv: 
      with open('dtmf_dump.csv', 'w') as f: 
       for d in self._raw_data: 
        f.write(str(d)) 
        f.write(",") 

     self.save_wave_file(file_path) 

    def read_wav(self): 
     fin = open('testNum.wav','r') 
     n = fin.getnframes() 
     d = fin.readframes(n) 
     fin.close() 

     data = [] 
     for i in range(n): 
      #LS8bit = inv_endian(ord(d[2*i])) 
      #MS8bit = inv_endian(ord(d[2*i+1])) 
      LS8bit, MS8bit = ord(d[2*i]),ord(d[2*i+1]) 
      data.append((MS8bit<<8)+LS8bit) 
     return data 


# Decoder takes a DTMF signal file (.wav), sampled at 44,000 
# 16-bit samples per second, and decode the corresponding symbol X. 

    def decoder(self): 
     data = self.read_wav() 
     temp = []  
     for f1 in ROW_FREQ: 
      for f2 in COL_FREQ: 
       diff = 0 
       for i in range(SAMPLE_RATE): #assume phase has not shifted dramatically  
        p = i*1.0/SAMPLE_RATE 
        S=int(scale+scale*(sin(p*f1*PI2)+sin(p*f2*PI2))/2) 
        diff += abs(S-data[i]) 
       temp.append((diff,f1,f2)) 
     f1,f2 = min(temp)[1:] #retrieve the frequency of minimum signal distortion 
     i, j = ROW_FREQ.index(f1), COL_FREQ.index(f2)  
     X = keys[4*i+j] 
     print 'Decoded key is: ', X 
     return X 

if __name__ == '__main__': 
    d = 100 
    sample_input = [('0', d), ('5', d), ('0', d), ('8', d), ('6', d), ('9', d), ('0',d), ('1',d) , ('8',d),('6',d)] 
    d = DTMF() 
    d.create_dtmf_wave_file(sample_input, file_path='testNum.wav', dump_to_csv=True) 
    x = d.decoder() 
+0

Scipy讓大量的wave文件編輯變得簡單。要閱讀wave文件,請查看scipy.io.wavfile.read。 Numpy還有很多功能強大的工具可用於wavefile編輯。 – Roman

回答

1
fin = open('testNum.wav','r') 

看起來像你使用內置open功能,而不是從wave模塊之一。嘗試:

fin = wave.open('testNum.wav','r') 
+0

非常感謝你!現在我有其他問題,它返回我錯誤的輸出,我需要弄清楚。 –

0

您有wave read object可能從wave.open返回來操作,該方法將返回文件屬性與你的代碼試圖訪問。

而且你添加新的from wave import open語句,所以以這種方式,你將覆蓋默認open方法,但它更好地訪問波開法認爲點作爲游泳,要麼wave.open

+0

如何解決此問題: '----------------------------------------- ---------------------------------- AttributeError Traceback(最近呼叫的最後一個) in () 7 file ='/home/vivek/Documents/VivekProjects/Silence/silence.wav' ----> 9 with wave.open(file,'r')as wav_file: 10#從Wav文件中提取原始音頻 11 signal = wav_file.readframes(-1) AttributeError:Wave_read實例沒有屬性'__exit __' –