Python 读取wav文件时出现属性错误

Python 读取wav文件时出现属性错误,python,Python,我是python新手,我尝试构建一个程序,该程序能够解码和编码用于拨打电话的双音多频(DTMF)信号。 目前,编码部分工作正常,但由于某种原因,编码不工作,我得到以下异常 Traceback (most recent call last): File "C:\Users\matant\workspace\dialer2\dialer.py", line 239, in <module> x = d.decoder() File "C:\Use

我是python新手,我尝试构建一个程序,该程序能够解码和编码用于拨打电话的双音多频(DTMF)信号。 目前,编码部分工作正常,但由于某种原因,编码不工作,我得到以下异常

 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'
回溯(最近一次呼叫最后一次):
文件“C:\Users\matant\workspace\dialer2\dialer.py”,第239行,在
x=d.解码器()
文件“C:\Users\matant\workspace\dialer2\dialer.py”,第218行,在解码器中
data=self.read_wav()
文件“C:\Users\matant\workspace\dialer2\dialer.py”,第201行,以read\U wav格式
n=fin.getnframes()
AttributeError:“文件”对象没有属性“getnframes”
如你所见,我正在将帧写入文件,因此我不明白为什么会发生: 这是我的代码:

    '''
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()
“”
创建于2016年1月10日
@作者:matant
'''
导入json
从数学输入pi,sin
输入波
导入日志记录
导入结构
导入操作系统
行频率=(69770852941)
COL_FREQ=(1209133614771633)
抽样率=44100
样本宽度=2
信道数=1
压缩类型=“无”
压缩\u NAME=“未压缩”
PI2=6.283185306
比例=32767#16位无符号短
键='1'、'2'、'3'、'A'\
‘4’、‘5’、‘6’、‘B’\
‘7’、‘8’、‘9’、‘C’\
“*”、“0”、“#”和“D”
频率映射=dict()
频率映射['1']=(6971209)
频率图['2']=(6971336)
频率映射['3']=(6971477)
频率映射['A']=(6971633)
频率映射['4']=(7701209)
频率映射['5']=(7701336)
频率映射['6']=(7701477)
频率映射['B']=(7701633)
频率映射['7']=(8521209)
频率图['8']=(8521336)
频率图['9']=(8521477)
频率映射['C']=(8521633)
频率映射['*']=(9411209)
频率映射['0']=(9411336)
频率映射['#']=(9411477)
频率映射['D']=(94111633)
频率映射['S']=(0,0)
logging.basicConfig(级别=logging.DEBUG,
格式='(asctime)s%(模块)s.%(funcName)s()%(消息)s',
datefmt=“%Y-%m-%d%H:%m:%S”)
log=logging.getLogger(_名称__)
类DTMF:
有效的\u序列\u类型=[列表、元组、集合]
定义初始化(self,输入字符串=None,输入列表=None):
"""
使用选项DTMF序列初始化DTMF实例。这可以是列表列表或json字符串。
如果两者都提供了,它将尝试解析json_字符串。如果提供了,它将使用该字符串。如果有错误,它将
验证列表并尝试使用它。基本上输入字符串优先。
一般的工作流程是设置dtmf_序列并调用generate_raw_data。然后可以将这些数据保存到
.wav文件或压缩并保存为其他较小的文件格式。
:param input_list:列表或元组的列表,格式为[[A',100],[S',50],[2',100],[S',50]]
:param input_string:json_string,格式为“[[A”,100],“S”,50],“2”,100],“S”,50]”
"""
调试(“创建DTMF实例”)
调试(“输入字符串={}”。格式(输入字符串))
调试(“输入列表={}”。格式(输入列表))
self.\u dtmf\u序列=无
自身。\原始\数据=无
如果输入字符串不是“无”:
转换的_json_序列=self.parse_json_字符串(输入_字符串)
self.\u dtmf\u序列=转换的\u json\u序列
elif输入列表不是无:
self.\u dtmf\u序列=输入\u列表
@财产
def dtmf_序列(自):
返回自.\u dtmf\u序列
@dtmf_序列设置器
def dtmf_序列(自、输入_序列):
如果类型(输入顺序)=str:
input\u sequence=self.parse\u json\u字符串(input\u sequence)
如果类型(输入顺序)=列表:
如果self.\u dtmf\u序列\u有效(输入\u序列):
self.\u dtmf\u序列=输入\u序列
调试(“将_dtmf_序列设置为{}”。格式(self._dtmf_序列))
def parse_json_字符串(self,input_字符串):
返回json.loads(输入字符串)
def生成原始数据(自身):
"""
生成可以保存到.wav文件中的原始数据。这可能需要一些时间才能生成。
:raise AttributeError:如果未设置dtmf序列
"""
_数据=列表()
如果self.\u dtmf\u序列为无:
raise AttributeError(“未设置dtmf序列”)
对于self.\u dtmf\u序列中的音调\u元组:
key=tone\u元组[0]
音调持续时间=音调元组[1]
f1=频率图[键][0]
f2=频率图[键][1]
_数据+=(自生成音调(f1、f2、音调持续时间))
self.\u原始数据=\u数据
def保存波形文件(自身,文件路径):
如果self.\u原始数据为None或len(self.\u原始数据)<1:
self.generate_raw_data()
f=wave.open(文件路径“w”)
f、 设置通道(通道数)
f、 设置采样宽度(采样宽度)
f、 设置帧率(采样率)
f、 设置帧(len(自身原始数据))
f、 setcomptype(压缩类型、压缩名称)
log.info(“保存wav文件{}这可能需要一段时间”。格式化(文件路径))
对于我自己的原始数据:
f、 writeframes(结构包('i',i))
log.info(“将文件保存到{0}”。格式(文件路径))
f、 关闭()
@静力学方法
def dtmf_序列_有效(输入列表):
"""
验证输入序列的结构和内容是否正确。
:参数输入列表:
:返回:
"""
如果类型(输入列表)不是列表:
log.warning('输入\列表必须是列表实例')
返回错误
如果输入列表中项目的[(DTMF中的类型(项目)。有效的\u序列\u类型)]。计数(False)!=0:
log.warning('input_list包含无效的序列类型')
返回错误
对于输入列表中的项目:
如果类型(项目[0])!=str或类型(项目[1])!=int:
log.debug(“类型列表[0]:{}”。格式(类型(项[0]))
L
    fin = open('testNum.wav','r')
    fin = wave.open('testNum.wav','r')