一段音频音调谱线的绘制

音调含义

基本频率(或简称基频,fundamental frequency),当发声体由于震动而发出声音时,声音一般可以分解为许多单纯的正弦波,也就是说所有的自然声音基本都是由许多频率不同的正弦波组成的,其中频率最低的正弦波即为基音,而其他频率较高的正弦波则为泛音。

音调(pitch)在音乐领域里指的是人类心理对音符基本频率的感受。

基本频率音调存在一个转换关系:
frequency2pitch

声音的量化(PCM)

在数字信号处理领域,量化指将信号的连续取值(或者大量可能的离散取值)近似为有限多个(或较少的)离散值的过程。量化主要应用于从连续信号到数字信号的转换中。连续信号经过采样成为离散信号,离散信号经过量化即成为数字信号。注意离散信号并不需要经过量化的过程。信号的采样和量化通常都是由ADC实现的。

例如CD音频信号就是按照44110Hz的频率采样,按16位元量化为有着65536(=2^{16})个可能取值的数字信号。

量化就是将模拟声音的波形转换为数字,表示采样值的二进制位数决定了量化的精度。量化的过程是先将整个幅度划分成有限个小幅度(量化阶距)的集合,把落入某个阶距内的样值归为一类,并赋予相同的量化值。

音频文件中存储的一般都是压缩过的PCM数据. 解码后就变成了可处理的PCM数据.

傅立叶变换

傅里叶变换是一种线性的积分变换,常在将信号在时域和频域之间变换时使用.

就在声音音调识别这个方面来说,傅立叶变换可以将以时间为自变量的振幅函数 vol(t) 变换到以频率为自变量的振幅函数 vol(f).

音调识别流程

  1. 规定一个Δt, 假设在Δt时间段内, 音调是不变的.
  2. 对vol(t)进行分割, 分成[0, Δt], [Δt, 2Δt] … [t-Δt, t] 多个区间.
  3. 分别对vol(t) 的多个区间进行傅立叶变换. 得出频率分布谱. 找到最大振幅对应的频率(一般情况下音量最大的频率即为基本频率)更好的方法.
  4. 利用基本频率音调变换关系, 对得出来的频率进行变换.
  5. 得到的pitch(t) 即为歌曲的音调变化谱线.

这种变形的傅立叶变换即为短时傅立叶变换能够观察出信号瞬时频率的信息.

编程实现

<!-- lang: js -->
function pitch()
    %读取音频文件
    [stream, sample_rate] = audioread('test.wma');
    %处理左声道
    stream_left = stream(1:length(stream),1);
    %假定4096个取样点内, 音调不变. 大约0.1s
    frame_size = 4096;
    %获取音频帧数
    frame_count = floor(length(stream_left) / frame_size);

    pitch = zeros(1, frame_count);

    for i = 1 : frame_count
        %区间
        pbeg = (i - 1) * frame_size + 1;
        pend = i * frame_size;

        stream_sub = stream_left(pbeg : pend);
        fft_result = fft(stream_sub, frame_size);
        [~, frequency] = max(abs(fft_result));

        pitch(i) = ceil(69 + 12 * log(frequency * sample_rate /frame_size / 440) / log(2));

    end

    pitch = medfilt1(pitch, 8);

    plot(1 : frame_count, pitch);
end

test.wma 是用系统自带录音工具录的一段 do re mi fa so la xi do 的哼唱.
运行结果如下图

matlab_pitch

附上一个快速傅立叶变换, 中值滤波的 C#实现


//Algorithm.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;

namespace System
{
    class Algorithm
    {
        public static Complex[] fft(Complex[] data)
        {
            if (data.Length == 1)
            {
                return data;
            }
            else
            {
                int length = data.Length / 2;

                Complex[] eveData = new Complex[length];
                Complex[] oddData = new Complex[length];

                for (int i = 0; i < length; i++)
                {
                    eveData[i] = data[2 * i];
                    oddData[i] = data[2 * i + 1];
                }

                Complex[] eveResult = fft(eveData);
                Complex[] oddResult = fft(oddData);

                Complex[] Result = new Complex[data.Length];

                double fac = 2 * Math.PI / data.Length;
                for (int i = 0; i < length; i++)
                {
                    double fack = fac * i;
                    Complex oddPart = oddResult[i] * new Complex(Math.Cos(fack), Math.Sin(fack));
                    Result[i] = eveResult[i] + oddPart;
                    Result[length + i] = eveResult[i] - oddPart;
                }
                return Result;
            }

        }

        public static double[] MedianFilter(double[] data, int winSize)
        {
            winSize = winSize / 2;
            double[] tmp = new double[winSize * 2 + 1];
            double[] result = new double[data.Length];
            for (int i = 0; i < data.Length; i++)
            {

                for (int j = 0; j < tmp.Length; j++)
                {
                    int k = i + j - winSize;
                    if (k < 0)
                    {
                        k = j % (i + 1);
                    }
                    else if (k >= data.Length)
                    {
                        k = i + j % (data.Length - i);
                    }
                    tmp[j] = data[k];
                }
                Array.Sort(tmp);
                result[i] = tmp[winSize + 1];

            }
            return result;
        }
    }
}

你可能感兴趣的:(matlab,音频,音调)