正弦波生成及频谱分析
正弦波公式
- 诊断系统(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;
} // namespace
void 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。其误差值较小。