zoukankan      html  css  js  c++  java
  • 2019 WebRtc AudioMixer混音流程

    本文简要说明最新版WebRtc AudioMixer混音流程。

    本程序使用4个16KHz 单声道时长均大于10秒的Wav文件作为混音源,只合成前10秒的音频,输出也是16KHz单声道音频。

    输入和输出的采样率都是16000,每10ms音频长度采样点数为160,每个采样点为16bit,两字节大小。

    使用的WebRTC代码日期为2019-05-08。

    代码如下:

      1 #include "stdafx.h"
      2 #include <string>
      3 #include <iostream>
      4 #include <memory>
      5 #include <thread>
      6 #include <chrono>
      7 #include "webrtcmodulesaudio_mixeraudio_mixer_impl.h"
      8 #include "webrtcapiaudioaudio_frame.h"
      9 #include "webrtcmodulesaudio_mixeroutput_rate_calculator.h"
     10 
     11 using namespace std::literals::chrono_literals;
     12 
     13 #define WAV_HEADER_SIZE 44
     14 #define SAMPLE_RATE 16000
     15 
     16 int16_t s_buf[160] = { 0 };
     17 
     18 class AudioSrc : public webrtc::AudioMixer::Source
     19 {
     20 public:
     21     AudioSrc(int ssrc, int sample, const std::string& file)
     22         : mSsrc(ssrc)
     23         , mSample(sample)
     24         , mFile(nullptr)
     25     {
     26         fopen_s(&mFile, file.c_str(), "rb");
     27         
     28         if (mFile)
     29         {
     30             fseek(mFile, 44, SEEK_SET);
     31         }
     32     }
     33 
     34     virtual ~AudioSrc()
     35     {
     36         if (mFile)
     37         {
     38             fclose(mFile);
     39             mFile = nullptr;
     40         }
     41     }
     42 
     43     virtual AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz,
     44         webrtc::AudioFrame* audio_frame)
     45     {
     46         if (mFile)
     47         {
     48             fread(s_buf, 160 * 2, 1, mFile);//16KHz, 10ms buf
     49         }
     50         
     51         std::thread::id tid = std::this_thread::get_id();
     52         std::cout << "thread id ";
     53         tid._To_text(std::cout);
     54 
     55         //copy s_buf to audio_frame inner buf
     56         audio_frame->UpdateFrame(0, s_buf, 160, SAMPLE_RATE, webrtc::AudioFrame::SpeechType::kNormalSpeech, webrtc::AudioFrame::VADActivity::kVadUnknown, 1);
     57         printf(",ssrc %d get audio frame, muted: %d, n %d, s %d
    ", mSsrc, int(audio_frame->muted()), audio_frame->num_channels_, audio_frame->sample_rate_hz_);
     58         
     59         return AudioFrameInfo::kNormal;
     60     }
     61 
     62     // A way for a mixer implementation to distinguish participants.
     63     virtual int Ssrc() const
     64     {
     65         return mSsrc;
     66     }
     67 
     68     // A way for this source to say that GetAudioFrameWithInfo called
     69     // with this sample rate or higher will not cause quality loss.
     70     virtual int PreferredSampleRate() const
     71     {
     72         return mSample;
     73     }
     74 
     75 private:
     76     int mSsrc;
     77     int mSample;
     78     FILE* mFile;
     79 };
     80 
     81 
     82 class AudioOutput : public webrtc::OutputRateCalculator
     83 {
     84     virtual int CalculateOutputRate(
     85         const std::vector<int>& preferred_sample_rates)
     86     {
     87         return SAMPLE_RATE;
     88     }
     89 
     90 };
     91 
     92 int MixText()
     93 {
     94     auto mixptr = webrtc::AudioMixerImpl::Create(std::make_unique<AudioOutput>(), true);
     95     AudioSrc src1(1, SAMPLE_RATE, "e:\Media\Audio\16k_1.wav");
     96     AudioSrc src2(2, SAMPLE_RATE, "e:\Media\Audio\16k_2.wav");
     97     AudioSrc src3(3, SAMPLE_RATE, "e:\Media\Audio\16k_3.wav");
     98     AudioSrc src4(4, SAMPLE_RATE, "e:\Media\Audio\16k_4.wav");
     99 
    100     mixptr->AddSource(&src1);
    101     mixptr->AddSource(&src2);
    102     mixptr->AddSource(&src3);
    103     mixptr->AddSource(&src4);
    104 
    105     std::thread::id tid = std::this_thread::get_id();
    106     std::cout << "main thread id: ";
    107     tid._To_text(std::cout);
    108     std::cout << std::endl;
    109 
    110     webrtc::AudioFrame frame;
    111 
    112     FILE* fOut = nullptr;
    113     fopen_s(&fOut, "f:/download/outmix.pcm", "wb");
    114     
    115     for (int i = 0; i < 100*10; ++i) // only mix first 10 seconds audio
    116     {
    117         mixptr->Mix(1, &frame);
    118         if (fOut)
    119         {
    120             fwrite(frame.data(), 160 * 2, 1, fOut);
    121         }
    122         std::this_thread::sleep_for(10ms);
    123     }
    124     
    125     fclose(fOut);
    126     fOut = nullptr;
    127 
    128     mixptr->RemoveSource(&src1);
    129     mixptr->RemoveSource(&src2);
    130     mixptr->RemoveSource(&src3);
    131     mixptr->RemoveSource(&src4);
    132 
    133     tid = std::this_thread::get_id();
    134     std::cout << "exit main thread id: ";
    135     tid._To_text(std::cout);
    136     std::cout << std::endl;
    137 
    138     return 0;
    139 }
    140 
    141 int main(int argc, char* argv[])
    142 {
    143     MixText();
    144 
    145     return 0;
    146 }

    代码大体介绍:

     class AudioSrc : public webrtc::AudioMixer::Source 定义了混音音频源的类,

    AudioSrc必须实现基类的三个virtual函数,

     virtual int Ssrc() const;  返回混音源的ID, AudioMixer调用此函数用于区分每个音频源,每个音频源的Ssrc必须返回不同的值。

     virtual int PreferredSampleRate() const; 返回混音源的音频采样率,这里都是16000,AudioMixer调用此函数得到每个音频源的音频采样率。

     virtual AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz, webrtc::AudioFrame* audio_frame) 获取混音源的10ms长度音频,混音时AudioMixer依次对混音源调用此函数,

    AudioSrc先从wav文件读取10ms音频到s_buf中,用AudioFrame的UpdateFrame函数把刚获取的s_buf中内容更新到AudioFrame中,AudioMixer内部完成混音。

     class AudioOutput : public webrtc::OutputRateCalculator  是用于计算混音输出音频采样率的类,必须实现虚函数CalculateOutputRate,

    参数const std::vector<int>& preferred_sample_rates中元素为每个混音源的音频采样率,返回值为输出音频采样率,这里输出和输入的采样率一样,返回16000。

    函数MixText是调用的代码

    auto mixptr = webrtc::AudioMixerImpl::Create(std::make_unique<AudioOutput>(), true);

    创建了一个AudioMixer智能指针对象mixptr ,

    AudioSrc src1(1, SAMPLE_RATE, "e:\Media\Audio\16k_1.wav");
    AudioSrc src2(2, SAMPLE_RATE, "e:\Media\Audio\16k_2.wav");
    AudioSrc src3(3, SAMPLE_RATE, "e:\Media\Audio\16k_3.wav");
    AudioSrc src4(4, SAMPLE_RATE, "e:\Media\Audio\16k_4.wav");

    创建了4个混音源。

    mixptr->AddSource(&src1);
    mixptr->AddSource(&src2);
    mixptr->AddSource(&src3);
    mixptr->AddSource(&src4);

    把4个混音源添加到AudioMixer中,

    webrtc::AudioFrame frame;

    创建一个AudioFrame 对象,用于接收混音输出。

    FILE* fOut = nullptr;
    fopen_s(&fOut, "f:/download/outmix.pcm", "wb");

    打开一个文件,用于写入混音输出。

        for (int i = 0; i < 100*10; ++i) // only mix first 10 seconds audio
        {
            mixptr->Mix(1, &frame);
            if (fOut)
            {
                fwrite(frame.data(), 160 * 2, 1, fOut);
            }
            std::this_thread::sleep_for(10ms);
        }

    此循环调用1000次,每次混音10毫秒长度音频,总共混音10秒钟长度的音频,

    mixptr->Mix(1, &frame);

    Mix是实际执行混音的函数,
    它内部再依次调用AudioSrc::GetAudioFrameWithInfo再完成混音,把混音输出到frame中。

    然后用fwrite再把frame.data()中的混音输出写入到输出文件。

    std::this_thread::sleep_for(10ms);

    sleep等待10毫秒。

    混音完成再调用

        mixptr->RemoveSource(&src1);
        mixptr->RemoveSource(&src2);
        mixptr->RemoveSource(&src3);
        mixptr->RemoveSource(&src4);

    移除混音源。

    以上就是混音流程。

    注意:由于混音输出写的是PCM文件,没有文件头,一般播放器不能播放,必须用 CoolEdit 或 Audacity 打开PCM播放。

  • 相关阅读:
    【C++clock()函数学习(计算自己代码运行时间)】
    YCOJ 1041113【最近的回文数】
    计蒜客【汉诺塔II】
    YCOJ【汉诺塔】
    【常用算法总结——递归】
    YCOJ【查找】
    【常用算法总结——分治】
    Redis哨兵机制
    Redis主从复制
    SpringBoot集成Redis
  • 原文地址:https://www.cnblogs.com/Yinkaisheng/p/11105005.html
Copyright © 2011-2022 走看看