【Audio】正弦波生成原理及C++代码

正弦波生成及频谱分析

正弦波公式

  • 诊断系统(Diag)会通过播放一段指定频率、采样率、时长及振幅的正弦音,以此对Audio测试。
  • 正弦波的公式如下,其中 A是振幅、x是时间、F是频率。
    y = A ∗ sin ⁡ ( 2 ∗ π ∗ x ∗ F ) y = A* \sin \lparen 2 * \pi * x * F \rparen y=Asin(2πxF)

当振幅是1.0 ,频率是1.0(频率指一秒钟震几次)。正弦波公式及其图像为
y = sin ⁡ ( 2 π x ) y = \sin \lparen 2 \pi x \rparen y=sin(2πx)
在这里插入图片描述

  • 考虑到相偏移θ和Y轴偏移量D(比如为了方便计算振幅不存在负数,那么Y轴向上偏移),其公式为
    y = A ∗ sin ⁡ ( 2 ∗ π ∗ x ∗ F + θ ) + D y = A* \sin \lparen 2 * \pi * x * F + \theta \rparen + D y=Asin(2πxF+θ)+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。其误差值较小。
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zxc024000/article/details/133610853