傅里叶变换与音频处理

举报
Jack20 发表于 2025/07/04 11:31:17 2025/07/04
【摘要】 傅里叶变换作为一种线性积分变换,可用于音频信号在时域和频域间的转换。1. 傅里叶变换的线性性质傅里叶变换满足线性变换的定义:对于任意信号 f(t) 和 g(t),以及常数 a 和 b,有:F{a⋅f(t)+b⋅g(t)}=a⋅F{f(t)}+b⋅F{g(t)}这一性质保证了其在信号处理中的易用性,例如可同时处理多个频率成分的叠加信号。2. 积分变换的本质傅里叶变换的数学表达式为:F{f(t)...

傅里叶变换作为一种线性积分变换,可用于音频信号在时域和频域间的转换。

1. 傅里叶变换的线性性质

傅里叶变换满足线性变换的定义:对于任意信号 f(t)g(t),以及常数 ab,有:
F{a⋅f(t)+b⋅g(t)}=a⋅F{f(t)}+b⋅F{g(t)}
这一性质保证了其在信号处理中的易用性,例如可同时处理多个频率成分的叠加信号。

2. 积分变换的本质

傅里叶变换的数学表达式为:F{f(t)}=−∞+∞f(t)⋅etdt
其中 et 是核函数,通过积分运算将时域信号 f(t) 转换为频域函数 F(ω)。其逆变换则通过反向积分实现:f(t)=2π1−∞+∞F(ω)⋅etdω
这明确体现了其作为积分变换的特性。

3. 音频信号的时频转换应用

音频信号是典型的时域信号(如麦克风采集的声波随时间的振幅变化),而傅里叶变换可将其分解为不同频率成分的叠加(频域表示)。例如:

  • 时域→频域:通过傅里叶变换,可分析音频中的频率成分(如某段音乐包含哪些音符的频率)。
  • 频域→时域:通过逆傅里叶变换,可从频域特征重建原始音频信号(如音频压缩算法中的解码过程)。

  1. 时域是描述数学函数或物理信号对时间的关系
  2. 频域是在对函数或信号进行分析时,分析其和频率有关的部分,而不是和时间有关的部分

一、时域(Time Domain)

  • 定义:以时间为横轴,描述信号在时间维度上的变化规律,即信号值随时间的变化关系。
  • 举例
    • 音频波形图中,横轴为时间(秒),纵轴为声音振幅,直观展示声音的强弱随时间的变化。
    • 物理信号(如温度传感器数据)随时间的波动曲线。
  • 关键特征:关注信号的时间依赖性,如持续时间、上升沿 / 下降沿、周期性等。

二、频域(Frequency Domain)

  • 定义:通过傅里叶变换等方法,将信号从时域转换为频率维度的表示,分析信号中各频率成分的强度、相位等特征。
  • 举例
    • 音频的频谱图中,横轴为频率(Hz),纵轴为能量幅度,展示不同频率(如低音、高音)的分布情况。
    • 图像的频域分析可提取边缘、纹理等频率特征(如高频对应细节,低频对应整体轮廓)。
  • 关键特征:忽略时间维度,关注信号的频率组成,如主频、谐波分量、带宽等。

 

傅里叶变换的核心原理是将时域信号分解为不同频率的正弦 / 余弦波的叠加,从而得到信号的频域表示。在音频处理中,通常会将连续的音频信号分割成若干短时长的 “帧”(如每帧 20-50 毫秒),对每一帧独立进行傅里叶变换(或其快速算法 FFT)。
  1. 频域信息的完整性
    每一帧音频信号的傅里叶变换结果会包含该帧内所有频率成分的幅度和相位信息。例如,对于一段语音,傅里叶变换会精确计算出其中每个频率分量(如基频、泛音等)的强度,从而完整记录频域特征。
  2. 帧处理的必要性
    音频信号是时域上的连续信号,但由于频率成分可能随时间变化(如语音中的音调、乐器的音色变化),对整段信号直接做傅里叶变换会丢失时间维度的细节。因此,分帧处理既能保证每一帧内信号近似平稳(满足傅里叶变换的前提假设),又能通过连续帧的处理捕捉频域随时间的变化。
  3. 应用场景的支持
    在语音识别、音乐分析、音频压缩等领域,傅里叶变换的分帧处理是基础步骤。例如,梅尔频率倒谱系数(MFCC)的计算就依赖于分帧后的傅里叶变换结果,以获取频域特征。

提取需存储在数据库中供检索识别的音频文件的特征信息(音频处理的类有三种方法:载入数据、傅里叶变换、播放音乐)。

# coding=utf8
import os
import re
import wave

import numpy as np
import pyaudio


class voice():
    def loaddata(self, filepath):
        if type(filepath) != str:
            raise TypeError
        p1 = re.compile('\.wav')
        if p1.findall(filepath) is None:
            raise IOError
        try:
            f = wave.open(filepath, 'rb')
            params = f.getparams()
            self.nchannels, self.sampwidth, self.framerate, self.nframes = params[:4]
            str_data = f.readframes(self.nframes)
            #self.wave_data = np.fromstring(str_data, dtype=np.short)
            self.wave_data = np.frombuffer(str_data, dtype=np.short)
            self.wave_data.shape = -1, self.sampwidth
            self.wave_data = self.wave_data.T
            f.close()
            self.name = os.path.basename(filepath)  
            return True
        except:
            raise IOError

    def fft(self, frames=40):
        block = []
        fft_blocks = []
        self.high_point = []
        blocks_size = self.framerate // frames  
        blocks_num = self.nframes / blocks_size  
        for i in range(0, len(self.wave_data[0]) - blocks_size, blocks_size):
            block.append(self.wave_data[0][i:i + blocks_size])
            fft_blocks.append(np.abs(np.fft.fft(self.wave_data[0][i:i + blocks_size])))
            self.high_point.append((np.argmax(fft_blocks[-1][:40]),
                                    np.argmax(fft_blocks[-1][40:80]) + 40,
                                    np.argmax(fft_blocks[-1][80:120]) + 80,
                                    np.argmax(fft_blocks[-1][120:180]) + 120,
                                    # np.argmax(fft_blocks[-1][180:300]) + 180,
                                    ))

    def play(self, filepath):
        chunk = 1024
        wf = wave.open(filepath, 'rb')
        p = pyaudio.PyAudio()
        stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                        channels=wf.getnchannels(),
                        rate=wf.getframerate(),
                        output=True)
        while True:
            data = wf.readframes(chunk)
            if data == "": break
            stream.write(data)
            stream.close()
            p.terminate()

if __name__ == '__main__':
    p = voice()
    p.loaddata("./music01.wav")
    # p.play('temp/1.wav')
print(p.name)

将提取到的音频文件特质特征关联到新建数据库里的数据表中(即声音指纹数据) 

                                
# coding=utf-8
import pymysql as MySQLdb


class memory():
    def __init__(self, host, port, user, passwd, db):
        self.host = host
        self.port = port
        self.user = user
        self.passwd = passwd
        self.db = db

    def addsong(self, path):
        if type(path) != str:
            raise TypeError  # , 'path need string'
        basename = os.path.basename(path)
        try:
            conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.passwd, db=self.db,
                                   charset='utf8')
        except:
            print('DataBase error')
            return None
        cur = conn.cursor()
        namecount = cur.execute("select * from fingerprint WHERE song_name = '%s'" % basename)
        if namecount > 0:
            print('the song has been record!')
            return None
        v = voice()
        v.loaddata(path)
        v.fft()
        cur.execute("insert into fingerprint VALUES('%s','%s')" % (basename, v.high_point.__str__()))
        conn.commit()
        print('Added to database:%s' % (basename))
        cur.close()
        conn.close()

    def fp_compare(self, search_fp, match_fp):
        if len(search_fp) > len(match_fp):
            return 0
        max_similar = 0
        search_fp_len = len(search_fp)
        match_fp_len = len(match_fp)
        for i in range(match_fp_len - search_fp_len):
            temp = 0
            for j in range(search_fp_len):
                if match_fp[i + j] == search_fp[j]:
                    temp += 1
            if temp > max_similar:
                max_similar = temp
        return max_similar

    def search(self, path):
        v = voice()
        v.loaddata(path)
        v.fft()
        try:
            conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.passwd, db=self.db,
                                   charset='utf8')
        except:
            raise IOError
        cur = conn.cursor()
        cur.execute("SELECT * FROM fingerprint")
        result = cur.fetchall()
        compare_res = []
        for i in result:
            compare_res.append((self.fp_compare(v.high_point[:-1], eval(i[1])), i[0]))
        compare_res.sort(reverse=True)
        cur.close()
        conn.close()
        print(compare_res)
        return compare_res

    def search_and_play(self, path):
        v = voice()
        v.loaddata(path)
        v.fft()
        try:
            conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.passwd, db=self.db,
                                   charset='utf8')
        except:
            print('DataBase error')
            return None
        cur = conn.cursor()
        cur.execute("SELECT * FROM fingerprint")
        result = cur.fetchall()
        compare_res = []
        for i in result:
            compare_res.append((self.fp_compare(v.high_point[:-1], eval(i[1])), i[0]))
        compare_res.sort(reverse=True)
        cur.close()
        conn.close()
        print(compare_res)
        # v.play(compare_res[0][1])
        return compare_res

sss.addsong('./music01.wav')
sss.addsong('./music02.wav')
sss.addsong('./music03.wav')
sss.addsong('./music04.wav')
                            
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。