Custom audio source

Updated

Integrate a custom video or audio capture into your client

The default audio module of Voice SDK meets the need of using basic audio functions in your app. For adding advanced audio functions, Voice SDK supports using custom audio sources and custom audio rendering modules.

Video SDK uses the basic audio module on the device your app runs on by default. However, there are certain use-cases where you want to integrate a custom audio source into your app, such as:

  • Your app has its own audio module.
  • You need to process the captured audio with a pre-processing library for audio enhancement.
  • You need flexible device resource allocation to avoid conflicts with other services.

This page shows you how to capture and render audio from custom sources.

Understand the tech

To set an external audio source, you configure the Agora Engine before joining a channel. To manage the capture and processing of audio frames, you use methods from outside the Video SDK that are specific to your custom source. Video SDK enables you to push processed audio data to the subscribers in a channel.

Capture custom audio

The following figure illustrates the process of custom audio capture.

  • You implement the capture module using external methods provided by the SDK.

  • You call pushExternalAudioFrame to send the captured audio frames to the SDK.

Render custom audio

The following figure illustrates the process of custom audio rendering.

  • You implement the rendering module using external methods provided by the SDK.

  • You call pullPlaybackAudioFrame to retrieve the audio data sent by remote users.

Prerequisites

Ensure that you have implemented the SDK quickstart in your project.

Implementation

Custom audio capture

Refer to the following call sequence diagram to implement custom audio capture in your app:

Custom audio capture

Follow these steps to implement custom audio capture in your project:

  1. Enable and configure custom audio source:

    Before calling joinChannel to join the channel, call setExternalAudioSource to enable and configure custom audio capture.

    // Specify the custom audio source
    m_rtcEngine->setExternalAudioSource(true, m_capAudioInfo.sampleRate, m_capAudioInfo.channels);
    
    // Local user joins the channel
    ChannelMediaOptions option;
    option.autoSubscribeAudio = true;
    option.autoSubscribeVideo = true;
    m_rtcEngine->joinChannel("Your token", szChannelId.c_str(), 0, option);
  2. Implement audio capture and processing

    Use methods outside the SDK to implement audio capture and processing yourself.

  3. Send audio frames to SDK

    Call pushAudioFrame to send the captured audio frames to the SDK for later use.

    mediaEngine->pushAudioFrame(AUDIO_RECORDING_SOURCE, &m_audioFrame);

Custom audio rendering

This section shows you how to implement custom audio rendering. Refer to the following call sequence diagram to implement custom audio rendering in your app:

Custom audio rendering workflow

To implement custom audio rendering, use the following methods:

  1. Enable and configure custom audio sink

    Before calling joinChannel to join the channel, call setExternalAudioSink to enable and configure custom audio rendering.

    // Enable custom audio rendering
    // Sample rate (Hz) can be set to 16000, 32000, 44100 or 48000
    // Number of channels can be set to 1 or 2
    nRet = m_rtcEngine->setExternalAudioSink(m_renderAudioInfo.sampleRate, m_renderAudioInfo.channels);
  2. Pull and render remote audio data

    • After joining the channel, call pullAudioFrame to get the audio data sent by the remote user.
    • Use your own audio renderer to process the audio data and then play the rendered data.
    void CAgoraCaptureAduioDlg::PullAudioFrameThread(CAgoraCaptureAduioDlg * self)
    {
     int nRet = 0;
     agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
     mediaEngine.queryInterface(self->m_rtcEngine, AGORA_IID_MEDIA_ENGINE);
     IAudioFrameObserver::AudioFrame audioFrame;
     audioFrame.avsync_type = 0; // Reserved parameter 
     audioFrame.bytesPerSample = TWO_BYTES_PER_SAMPLE;
     audioFrame.type = agora::media::IAudioFrameObserver::FRAME_TYPE_PCM16;
     audioFrame.channels = self->m_renderAudioInfo.channels;
     audioFrame.samplesPerChannel = self->m_renderAudioInfo.sampleRate / 100 * self->m_renderAudioInfo.channels;
     audioFrame.samplesPerSec = self->m_renderAudioInfo.sampleRate;
     audioFrame.buffer = new BYTE[audioFrame.samplesPerChannel * audioFrame.bytesPerSample];
     while (self->m_extenalRenderAudio )
     {
       // Pull remote audio data
       nRet = mediaEngine->pullAudioFrame(&audioFrame);
       if (nRet != 0)
       {
         Sleep(10);
         continue;
       }
       SIZE_T nSize = audioFrame.samplesPerChannel * audioFrame.bytesPerSample;
       self->m_audioRender.Render((BYTE*)audioFrame.buffer, nSize);
     }
     delete audioFrame.buffer;
    }

Using raw audio data callback

This section explains how to implement custom audio rendering.

To retrieve audio data for playback, implement collection and processing of raw audio data. Refer to Raw audio processing.

Follow these steps to call the raw audio data API in your project for custom audio rendering:

  1. Retrieve audio data for playback using the onRecordAudioFrame, onPlaybackAudioFrame, onMixedAudioFrame, or onPlaybackAudioFrameBeforeMixing callback.

  2. Independently render and play the audio data.

Reference

This section explains how to implement different sound effects and audio mixing in your app, covering essential steps and code snippets.

Sample projects

Agora provides an open-source CustomAudioCapture project for your reference. Download the project or inspect the source code for a more detailed example.

API reference