正弦波生成及频谱分析
正弦波公式
- 诊断系统(Diag)会通过播放一段指定频率、采样率、时长及振幅的正弦音,以此对Audio测试。
- 正弦波的公式如下,其中 A是振幅、x是时间、F是频率。
y = A ∗ sin ( 2 ∗ π ∗ x ∗ F ) y = A* \sin \lparen 2 * \pi * x * F \rparen y=A∗sin(2∗π∗x∗F)
当振幅是1.0 ,频率是1.0(频率指一秒钟震几次)。正弦波公式及其图像为
y = sin ( 2 π x ) y = \sin \lparen 2 \pi x \rparen y=sin(2πx)
- 考虑到相偏移和Y轴偏移量(比如为了方便计算振幅不存在负数,那么Y轴向上偏移),其公式为
y = A ∗ sin ( 2 ∗ π ∗ x ∗ F + θ ) + D y = A* \sin \lparen 2 * \pi * x * F + \theta \rparen + D y=A∗sin(2∗π∗x∗F+θ)+D
C++生成正弦波
- WebRTC中提供了一段正弦波的生成函数,生成精度比较高。可以借鉴其代码,编写正弦波函数(改造后的实际应用代码不便放出)。这里分析一下WebRTC 正弦波生成源码。
// webrtc/modules/audio_mixer/sine_wave_generator.h
/** Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.** Use of this source code is governed by a BSD-style license* that can be found in the LICENSE file in the root of the source* tree. An additional intellectual property rights grant can be found* in the file PATENTS. All contributing project authors may* be found in the AUTHORS file in the root of the source tree.*/#ifndef MODULES_AUDIO_MIXER_SINE_WAVE_GENERATOR_H_
#define MODULES_AUDIO_MIXER_SINE_WAVE_GENERATOR_H_#include <stdint.h>#include "api/audio/audio_frame.h"
#include "rtc_base/checks.h"namespace webrtc {class SineWaveGenerator {public:SineWaveGenerator(float wave_frequency_hz, int16_t amplitude): wave_frequency_hz_(wave_frequency_hz), amplitude_(amplitude) {RTC_DCHECK_GT(wave_frequency_hz, 0);}// Produces appropriate output based on frame->num_channels_,// frame->sample_rate_hz_.// 通过这个函数生成正弦波void GenerateNextFrame(AudioFrame* frame);private:float phase_ = 0.f;// 正弦波频率const float wave_frequency_hz_;// 振幅const int16_t amplitude_;
};} // namespace webrtc#endif // MODULES_AUDIO_MIXER_SINE_WAVE_GENERATOR_H_// webrtc/modules/audio_mixer/sine_wave_generator.cc
/** Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.** Use of this source code is governed by a BSD-style license* that can be found in the LICENSE file in the root of the source* tree. An additional intellectual property rights grant can be found* in the file PATENTS. All contributing project authors may* be found in the AUTHORS file in the root of the source tree.*/#include "modules/audio_mixer/sine_wave_generator.h"#include <math.h>
#include <stddef.h>#include "rtc_base/numerics/safe_conversions.h"namespace webrtc {namespace {
constexpr float kPi = 3.14159265f;
} // namespacevoid SineWaveGenerator::GenerateNextFrame(AudioFrame* frame) {RTC_DCHECK(frame);// 获取用来保存数据的指针地址(一段Buffer)int16_t* frame_data = frame->mutable_data();// samples_per_channel_ 表示每个声道采样多少个样本for (size_t i = 0; i < frame->samples_per_channel_; ++i) {// 计算每个声道(Channel)的样本值for (size_t ch = 0; ch < frame->num_channels_; ++ch) {// 比如双声道(两个Channel),i = 0时就是frame_data[0] 和frame_data[1]// frame_data[ 2 * 0 + 0 ] --> Frame_data[0]// frame_data[ 2 * 0 + 1 ] --> Frame_data[1]// amplitude_ 指振幅,sinf(phase_) 是对 phase_ 其sin函数值frame_data[frame->num_channels_ * i + ch] =rtc::saturated_cast<int16_t>(amplitude_ * sinf(phase_));}// 这段是重点。 phase_ 就是正弦函数中的 变量值。// wave_frequency_hz_ 表示采样频率// Kpi 为π// 2∗ Kpi * wave_frequency_hz_ 是一个完整点的采样周期。// frame->sample_rate_hz_ 这个参数表示采样率// 采样率理解为在一个采样周期内,采多少个点。// 所以每个点的步长,例如 [ 0 0.1 0.2 ... 1.0 ], 0.1就是步长。// 采样的步长应为 采样周期 / 采样率。phase_ += wave_frequency_hz_ * 2 * kPi / frame->sample_rate_hz_;}
}
} // namespace webrtc
- 上面的代码中,针对多通道、特定频率、特定采样率、特定时长(样本点),生成了一段正弦波数据。关于AudioFrame,实际上就是记录了一些设定项,以及保存数据的数组,其源码可以参考(webrtc/api/audio/audio_frame.h)
- WebRTC中提供的Testsample
// The audio level ranges linearly [0,32767].
// audio_level 振幅
// duration_ms 时间
// sample_rate_hz 采样频率
// num_channels 通道数
std::unique_ptr<AudioFrame> CreateAudioFrame1kHzSineWave(int16_t audio_level,int duration_ms,int sample_rate_hz,size_t num_channels) {// 根据采样频率,计算样本数size_t samples_per_channel = sample_rate_hz / (1000 / duration_ms);// 创建对象,保存样本数据std::vector<int16_t> audio_data(samples_per_channel * num_channels, 0);// 给audioFrame设置相关参数std::unique_ptr<AudioFrame> audio_frame = std::make_unique<AudioFrame>();audio_frame->UpdateFrame(0 /* RTP timestamp */, &audio_data[0],samples_per_channel, sample_rate_hz,AudioFrame::SpeechType::kNormalSpeech,AudioFrame::VADActivity::kVadUnknown, num_channels);// 生成频率是1000(1kHZ)的正弦波SineWaveGenerator wave_generator(1000.0, audio_level);wave_generator.GenerateNextFrame(audio_frame.get());return audio_frame;
}
- 需要注意一点,正弦波频率 和 采样频率不是一个事情。正弦波频率是指正弦波一秒钟震几下,在人耳辨别的范围内,频率越高越刺耳。采样频率指的是将连续信号转化为离散信号时,采样周期的选择,也就是一秒钟采几次(一般为44.1kHz)
频谱分析
- 利用一些音频软件,可以对生成的数据进行分析。比如通上述代码,生成300HZ的一段正弦波数据。通过频谱分析可以看出,其峰值为311。其误差值较小。