# 原始音频数据 (/zh-CN/realtime-media/rtc/build/audio/raw-audio-data)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="android" />
## 技术原理 [#技术原理]

本文介绍如何对采集到的原始音频数据进行前处理和后处理。音频传输过程中，我们可以对采集到的音频数据进行前处理和后处理，获取想要的播放效果。

对于有自行处理音频数据需求的场景，声网提供原始数据功能。你可以在将数据发送给编码器前进行前处理，对捕捉到的音频信号进行修改；也可以在将数据发送给解码器后进行后处理，对接收到的音频信号进行修改。

下图展示使用原始音频数据的基础流程：

![实现流程](https://web-cdn.agora.io/docs-portal/img/rtc/raw-audio-data.svg)

## 前提条件 [#前提条件]

在进行操作之前，请确保你已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方法 [#实现方法]

参考如下步骤，在你的项目中实现原始音频数据功能：

1. 加入频道前，创建一个 `IAudioFrameObserver` 实例，并调用 `registerAudioFrameObserver` 方法注册音频观测器。
2. 调用 `setRecordingAudioFrameParameters`、`setPlaybackAudioFrameParameters` 和 `setMixedAudioFrameParameters` 为前缀的方法配置音频帧格式。
3. 实现 `onRecordAudioFrame`，`onPlaybackAudioFrame`，`onPlaybackAudioFrameBeforeMixing` 和 `onMixedAudioFrame` 回调。以上回调会对音频帧进行采集和处理。如果以上回调的返回值是 `false`，则说明对音频帧的处理无效。

```java
// 调用 registerAudioFrameObserver 注册音频观测器，并传入 IAudioFrameObserve 实例
engine.registerAudioFrameObserver(new IAudioFrameObserver() {

    // 实现 onRecordAudioFrame 回调
    @Override
    public boolean onRecordAudioFrame(byte[] samples, int numOfSamples, int bytesPerSample, int channels, int samplesPerSec) {
        if(isEnableLoopBack){
            mAudioPlayer.play(samples, 0, numOfSamples * bytesPerSample);
        }

        return false;
    }

    // 实现 onPlaybackAudioFrame 回调
    @Override
    public boolean onPlaybackAudioFrame(byte[] samples, int numOfSamples, int bytesPerSample, int channels, int samplesPerSec) {
        return false;
    }

    // 实现 onPlaybackAudioFrameBeforeMixing 回调
    @Override
    public boolean onPlaybackAudioFrameBeforeMixing(byte[] samples, int numOfSamples, int bytesPerSample, int channels, int samplesPerSec, int uid) {
        return false;
    }

    // 实现 onMixedAudioFrame 回调
    @Override
    public boolean onMixedAudioFrame(byte[] samples, int numOfSamples, int bytesPerSample, int channels, int samplesPerSec) {
        return false;
    }

    // 调用 set 为前缀的方法，配置每个回调采集到的音频帧
    engine.setRecordingAudioFrameParameters(SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, Constants.RAW_AUDIO_FRAME_OP_MODE_READ_WRITE,SAMPLES_PER_CALL);
    engine.setMixedAudioFrameParameters(SAMPLE_RATE, SAMPLES_PER_CALL);
    engine.setPlaybackAudioFrameParameters(SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, Constants.RAW_AUDIO_FRAME_OP_MODE_READ_WRITE,SAMPLES_PER_CALL);
});
```

## 参考信息 [#参考信息]

### 注意事项 [#注意事项]

声网实时互动 SDK 提供的音频裸数据采用同步回调机制。当你需要对原始音频数据进行保存或回写等操作时，请考虑以下建议：

- 为确保音频流的连续性，请不要阻塞 SDK 线程，建议不要直接在回调函数中处理数据。你可以先对接收的音频数据进行深拷贝，然后将复制的数据转移到另一个线程进行处理。

- 如果你决定在回调函数内直接同步处理音频数据，请严格控制处理时长。例如，若回调函数每 10 毫秒触发一次，那么在回调内的处理时间必须小于 10 毫秒，防止造成音频流的延迟或中断。

### 示例项目 [#示例项目]

声网提供了开源的原始音频数据示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">ProcessAudioRawData</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/API-Examples/blob/main/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessAudioRawData.java" />
  <Card title={<RepositoryCardTitle provider="github">ProcessAudioRawData</RepositoryCardTitle>} href="https://github.com/Shengwang-Community/API-Examples/blob/main/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/ProcessAudioRawData.java" />
</Cards>

### API 参考 [#api-参考]

- [`registerAudioFrameObserver`](/zh-CN/api-reference/rtc/android/audio/audio-raw#registeraudioframeobserver)
- [`setRecordingAudioFrameParameters`](/zh-CN/api-reference/rtc/android/audio/audio-raw#api_irtcengine_setrecordingaudioframeparameters)
- [`setPlaybackAudioFrameParameters`](/zh-CN/api-reference/rtc/android/audio/audio-raw#api_irtcengine_setplaybackaudioframeparameters)
- [`setMixedAudioFrameParameters`](/zh-CN/api-reference/rtc/android/audio/audio-raw#api_irtcengine_setmixedaudioframeparameters)
- [`onRecordAudioFrame`](/zh-CN/api-reference/rtc/android/audio/audio-raw#onrecordaudioframe)
- [`onPlaybackAudioFrame`](/zh-CN/api-reference/rtc/android/audio/audio-raw#onplaybackaudioframe)
- [`onPlaybackAudioFrameBeforeMixing`](/zh-CN/api-reference/rtc/android/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframebeforemixing)
- [`onMixedAudioFrame`](/zh-CN/api-reference/rtc/android/audio/audio-raw#onmixedaudioframe)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="ios" />
本文介绍如何对采集到的原始音频数据进行前处理和后处理。
## 技术原理 [#技术原理-1]

在实时音视频互动过程中，你可以对音视频数据进行前处理和后处理，并对它们进行修改，获取想要的播放效果。

对于有自行处理音频数据需求的场景，声网提供原始数据功能。你可以在将数据发送给编码器前进行前处理，对捕捉到的音频信号进行修改；也可以在将数据发送给解码器后进行后处理，对接收到的音频信号进行修改。

原始音频数据功能的 CPU 占用率高，可能会影响性能。

### 数据传输 [#数据传输]

下图展示原始音频数据前处理和后处理过程中的数据传输：

![img](https://web-cdn.agora.io/docs-files/1638261086824)

通过 `onRecordAudioFrame`，`onPlaybackAudioFrame`，`onPlaybackAudioFrameBeforeMixing` 或者 `onMixedAudioFrame` 回调，你可以：

- 获取原始音频帧。
- 处理原始数据，并返回至 SDK。

下图展示使用原始音频数据的基础流程：

![实现流程](https://web-cdn.agora.io/docs-portal/img/rtc/raw-audio-data.svg)

## 前提条件 [#前提条件-1]

在进行操作之前，请确保你已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方法 [#实现方法-1]

参考如下步骤，在你的项目中实现原始音频数据功能：

1. 加入频道前，调用 `setAudioFrameDelegate` 设置音频帧 delegate。
2. 调用 `setRecordingAudioFrameParametersWithSampleRate`、`setPlaybackAudioFrameParametersWithSampleRate` 和 `setMixedAudioFrameParametersWithSampleRate` 为前缀的方法设置音频帧格式。
3. 实现 `onRecordAudioFrame`，`onPlaybackAudioFrame`，`onPlaybackAudioFrameBeforeMixing` 和 `onMixedAudioFrame` 回调。以上回调会对音频帧进行采集和处理。如果以上回调的返回值是 `false`，则说明对音频帧的处理无效。

### 示例代码 [#示例代码]

```swift
class RawAudioDataMain: BaseViewController {
    var localVideo = Bundle.loadVideoView(type: .local, audioOnly: true)
    var remoteVideo = Bundle.loadVideoView(type: .remote, audioOnly: true)

    @IBOutlet weak var container: AGEVideoContainer!
    // 定义 agoraKit 变量
    var agoraKit: AgoraRtcEngineKit!

    // ...
    // 初始化 agoraKit，并注册相应回调
    agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)

    // 调用 setAudioFrameDelegate 设置音频帧 delegate。你需要在该方法中实现 AgoraAudioFrameDelegate 协议
    agoraKit.setAudioFrameDelegate(self)

    // 调用 set 为前缀的方法，配置每个回调采集到的音频帧
    agoraKit.setRecordingAudioFrameParametersWithSampleRate(44100, channel: 1, mode: .readWrite, samplesPerCall: 4410)
    agoraKit.setMixedAudioFrameParametersWithSampleRate(44100, samplesPerCall: 4410)
    agoraKit.setPlaybackAudioFrameParametersWithSampleRate(44100, channel: 1, mode: .readWrite, samplesPerCall: 4410)

    // ...

    // 在当前类下，实现 AgoraAudioFrameDelegat 协议的 extension
    extension RawAudioDataMain: AgoraAudioFrameDelegate {
        // 实现 onRecordAudioFrame 回调
        func onRecordAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onPlaybackAudioFrame 回调
        onPlaybackAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onMixedAudioFrame 回调
        func onMixedAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onPlaybackAudioFrameBeforeMixing 回调
        func onPlaybackAudioFrame(beforeMixing frame: AgoraAudioFrame, channelId: String, uid: UInt) -> Bool {
            return true
        }
    }
}
```

## 参考信息 [#参考信息-1]

### 注意事项 [#注意事项-1]

声网实时互动 SDK 提供的音频裸数据采用同步回调机制。当你需要对原始音频数据进行保存或回写等操作时，请考虑以下建议：

- 为确保音频流的连续性，请不要阻塞 SDK 线程，建议不要直接在回调函数中处理数据。你可以先对接收的音频数据进行深拷贝，然后将复制的数据转移到另一个线程进行处理。

- 如果你决定在回调函数内直接同步处理音频数据，请严格控制处理时长。例如，若回调函数每 10 毫秒触发一次，那么在回调内的处理时间必须小于 10 毫秒，防止造成音频流的延迟或中断。

### 示例项目 [#示例项目-1]

声网提供了开源的原始音频数据示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">RawAudioData</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/API-Examples/blob/main/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift" />
  <Card title={<RepositoryCardTitle provider="github">RawAudioData</RepositoryCardTitle>} href="https://github.com/Shengwang-Community/API-Examples/blob/main/iOS/APIExample/APIExample/Examples/Advanced/RawAudioData/RawAudioData.swift" />
</Cards>

### API 参考 [#api-参考-1]

- [`setAudioFrameDelegate`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#api_irtcengine_registeraudioframeobserver)
- [`setRecordingAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#api_irtcengine_setrecordingaudioframeparameters)
- [`setPlaybackAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#api_irtcengine_setplaybackaudioframeparameters)
- [`setMixedAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#api_irtcengine_setmixedaudioframeparameters)
- [`onRecordAudioFrame`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#callback_iaudioframeobserver_onrecordaudioframe)
- [`onPlaybackAudioFrame`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframe)
- [`onPlaybackAudioFrameBeforeMixing`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframebeforemixing)
- [`onMixedAudioFrame`](/zh-CN/api-reference/rtc/ios/audio/audio-raw#callback_iaudioframeobserver_onmixedaudioframe)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="macos" />
本文介绍如何对采集到的原始音频数据进行前处理和后处理。
## 技术原理 [#技术原理-2]

在实时音视频互动过程中，你可以对音视频数据进行前处理和后处理，并对它们进行修改，获取想要的播放效果。

对于有自行处理音频数据需求的场景，声网提供原始数据功能。你可以在将数据发送给编码器前进行前处理，对捕捉到的音频信号进行修改；也可以在将数据发送给解码器后进行后处理，对接收到的音频信号进行修改。

原始音频数据功能的 CPU 占用率高，可能会影响性能。

### 数据传输 [#数据传输-1]

下图展示原始音频数据前处理和后处理过程中的数据传输：

![img](https://web-cdn.agora.io/docs-files/1638261086824)

通过 `onRecordAudioFrame`，`onPlaybackAudioFrame`，`onPlaybackAudioFrameBeforeMixing` 或者 `onMixedAudioFrame` 回调，你可以：

- 获取原始音频帧。
- 处理原始数据，并返回至 SDK。

下图展示使用原始音频数据的基础流程：

![实现流程](https://web-cdn.agora.io/docs-portal/img/rtc/raw-audio-data.svg)

## 前提条件 [#前提条件-2]

在进行操作之前，请确保你已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方法 [#实现方法-2]

参考如下步骤，在你的项目中实现原始音频数据功能：

1. 加入频道前，调用 `setAudioFrameDelegate` 设置音频帧 delegate。
2. 调用 `setRecordingAudioFrameParametersWithSampleRate`、`setPlaybackAudioFrameParametersWithSampleRate` 和 `setMixedAudioFrameParametersWithSampleRate` 为前缀的方法设置音频帧格式。
3. 实现 `onRecordAudioFrame`，`onPlaybackAudioFrame`，`onPlaybackAudioFrameBeforeMixing` 和 `onMixedAudioFrame` 回调。以上回调会对音频帧进行采集和处理。如果以上回调的返回值是 `false`，则说明对音频帧的处理无效。

### 示例代码 [#示例代码-1]

```swift
class RawAudioDataMain: BaseViewController {
    var localVideo = Bundle.loadVideoView(type: .local, audioOnly: true)
    var remoteVideo = Bundle.loadVideoView(type: .remote, audioOnly: true)

    @IBOutlet weak var container: AGEVideoContainer!
    // 定义 agoraKit 变量
    var agoraKit: AgoraRtcEngineKit!

    // ...
    // 初始化 agoraKit，并注册相应回调
    agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)

    // 调用 setAudioFrameDelegate 设置音频帧 delegate。你需要在该方法中实现 AgoraAudioFrameDelegate 协议
    agoraKit.setAudioFrameDelegate(self)

    // 调用 set 为前缀的方法，配置每个回调采集到的音频帧
    agoraKit.setRecordingAudioFrameParametersWithSampleRate(44100, channel: 1, mode: .readWrite, samplesPerCall: 4410)
    agoraKit.setMixedAudioFrameParametersWithSampleRate(44100, samplesPerCall: 4410)
    agoraKit.setPlaybackAudioFrameParametersWithSampleRate(44100, channel: 1, mode: .readWrite, samplesPerCall: 4410)

    // ...

    // 在当前类下，实现 AgoraAudioFrameDelegat 协议的 extension
    extension RawAudioDataMain: AgoraAudioFrameDelegate {
        // 实现 onRecordAudioFrame 回调
        func onRecordAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onPlaybackAudioFrame 回调
        onPlaybackAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onMixedAudioFrame 回调
        func onMixedAudioFrame(_ frame: AgoraAudioFrame, channelId: String) -> Bool {
            return true
        }

        // 实现 onPlaybackAudioFrameBeforeMixing 回调
        func onPlaybackAudioFrame(beforeMixing frame: AgoraAudioFrame, channelId: String, uid: UInt) -> Bool {
            return true
        }
    }
}
```

## 参考信息 [#参考信息-2]

### 注意事项 [#注意事项-2]

声网实时互动 SDK 提供的音频裸数据采用同步回调机制。当你需要对原始音频数据进行保存或回写等操作时，请考虑以下建议：

- 为确保音频流的连续性，请不要阻塞 SDK 线程，建议不要直接在回调函数中处理数据。你可以先对接收的音频数据进行深拷贝，然后将复制的数据转移到另一个线程进行处理。

- 如果你决定在回调函数内直接同步处理音频数据，请严格控制处理时长。例如，若回调函数每 10 毫秒触发一次，那么在回调内的处理时间必须小于 10 毫秒，防止造成音频流的延迟或中断。

### 示例项目 [#示例项目-2]

声网提供了开源的原始音频数据示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">RawAudioData</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/API-Examples/tree/main/macOS/APIExample/Examples/Advanced/RawAudioData" />
  <Card title={<RepositoryCardTitle provider="github">RawAudioData</RepositoryCardTitle>} href="https://github.com/Shengwang-Community/API-Examples/tree/main/macOS/APIExample/Examples/Advanced/RawAudioData" />
</Cards>

### API 参考 [#api-参考-2]

- [`setAudioFrameDelegate`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#api_irtcengine_registeraudioframeobserver)
- [`setRecordingAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#api_irtcengine_setrecordingaudioframeparameters)
- [`setPlaybackAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#api_irtcengine_setplaybackaudioframeparameters)
- [`setMixedAudioFrameParametersWithSampleRate`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#api_irtcengine_setmixedaudioframeparameters)
- [`onRecordAudioFrame`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#callback_iaudioframeobserver_onrecordaudioframe)
- [`onPlaybackAudioFrame`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframe)
- [`onPlaybackAudioFrameBeforeMixing`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframebeforemixing)
- [`onMixedAudioFrame`](/zh-CN/api-reference/rtc/macos/audio/audio-raw#callback_iaudioframeobserver_onmixedaudioframe)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="windows" />
本文介绍如何对采集到的原始音频数据进行前处理和后处理。
## 技术原理 [#技术原理-3]

音频传输过程中，我们可以对采集到的音频数据进行前处理和后处理，获取想要的播放效果。

对于有自行处理音频数据需求的场景，声网提供原始数据功能。你可以在将数据发送给编码器前进行前处理，对捕捉到的音频信号进行修改；也可以在将数据发送给解码器后进行后处理，对接收到的音频信号进行修改。

下图展示使用原始音频数据的基础流程：

![实现流程](https://web-cdn.agora.io/docs-portal/img/rtc/raw-audio-data.svg)

## 前提条件 [#前提条件-3]

在进行操作之前，请确保你已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方法 [#实现方法-3]

参考如下步骤，在你的项目中实现原始音频数据功能：

1. 加入频道前，创建一个 `IAudioFrameObserver` 实例，并调用 `registerAudioFrameObserver` 方法注册音频观测器。
2. 调用 `setRecordingAudioFrameParameters`、`setPlaybackAudioFrameParameters` 和 `setMixedAudioFrameParameters` 为前缀的方法配置音频帧格式。
3. 实现 `onRecordAudioFrame`、`onPlaybackAudioFrame`、`onPlaybackAudioFrameBeforeMixing` 和 `onMixedAudioFrame` 回调。以上回调会对音频帧进行采集和处理。如果以上回调的返回值是 `false`，则说明对音频帧的处理无效。

```cpp
BOOL CAgoraOriginalAudioDlg::RegisterAudioFrameObserver(BOOL bEnable, IAudioFrameObserver *audioFrameObserver)
{
    agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
    // 查询 AGORA_IID_MEDIA_ENGINE 接口
    mediaEngine.queryInterface(m_rtcEngine, agora::rtc::AGORA_IID_MEDIA_ENGINE);
    int nRet = 0;
    if (mediaEngine.get() == NULL)
        return FALSE;

    if (bEnable)
        // 注册音频观测器并传入 IAudioFrameObserver 对象
        nRet = mediaEngine->registerAudioFrameObserver(audioFrameObserver);
    else
        // 取消注册音频观测器
        nRet = mediaEngine->registerAudioFrameObserver(NULL);

    return nRet == 0 ? TRUE : FALSE;
}

// 实现 onRecordAudioFrame 回调
bool COriginalAudioProcFrameObserver::onRecordAudioFrame(const char* channelId, AudioFrame& audioFrame)
{
    SIZE_T nSize = audioFrame.channels * audioFrame.samplesPerChannel * 2;
    unsigned int readByte = 0;
    int timestamp = GetTickCount();
    short *pBuffer = (short *)audioFrame.buffer;
    for (SIZE_T i = 0; i < nSize / 2; i++)
    {
        if (pBuffer[i] * 2 > 32767) {
            pBuffer[i] = 32767;
        }
        else if (pBuffer[i] * 2 < -32768) {
            pBuffer[i] = -32768;
        }
        else {
            pBuffer[i] *= 2;
        }
    }
#ifdef _DEBUG
    CString strInfo;
    strInfo.Format(_T("audio Frame buffer size:%d, timestamp:%d \n"), nSize, timestamp);
    OutputDebugString(strInfo);
    audioFrame.renderTimeMs = timestamp;
#endif
    return true;
}

// 实现 onPlaybackAudioFrame 回调
bool COriginalAudioProcFrameObserver::onPlaybackAudioFrame(const char* channelId, AudioFrame& audioFrame)
{
    return true;
}

// 实现 onMixedAudioFrame 回调
bool COriginalAudioProcFrameObserver::onMixedAudioFrame(const char* channelId, AudioFrame& audioFrame)
{
    return true;
}

// 实现 onPlaybackAudioFrameBeforeMixing 回调
bool COriginalAudioProcFrameObserver::onPlaybackAudioFrameBeforeMixing(const char* channelId, rtc::uid_t uid, AudioFrame& audioFrame)
{
    return true;
}

// 调用 set 为前缀的方法，配置每个回调采集到的音频帧
m_rtcEngine->setRecordingAudioFrameParameters(44100, 2, RAW_AUDIO_FRAME_OP_MODE_READ_WRITE, 1024);
m_rtcEngine->setPlaybackAudioFrameParameters(44100, 2, RAW_AUDIO_FRAME_OP_MODE_READ_WRITE, 1024);
m_rtcEngine->setPlaybackAudioFrameBeforeMixingParameters(44100, 2);
m_rtcEngine->setMixedAudioFrameParameters(44100, 2, 1024);
```

## 参考信息 [#参考信息-3]

### 注意事项 [#注意事项-3]

声网实时互动 SDK 提供的音频裸数据采用同步回调机制。当你需要对原始音频数据进行保存或回写等操作时，请考虑以下建议：

- 为确保音频流的连续性，请不要阻塞 SDK 线程，建议不要直接在回调函数中处理数据。你可以先对接收的音频数据进行深拷贝，然后将复制的数据转移到另一个线程进行处理。

- 如果你决定在回调函数内直接同步处理音频数据，请严格控制处理时长。例如，若回调函数每 10 毫秒触发一次，那么在回调内的处理时间必须小于 10 毫秒，防止造成音频流的延迟或中断。

### 示例项目 [#示例项目-3]

声网提供了开源的原始音频数据示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">OriginalAudio</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/API-Examples/tree/main/windows/APIExample/APIExample/Advanced/OriginalAudio" />
  <Card title={<RepositoryCardTitle provider="github">OriginalAudio</RepositoryCardTitle>} href="https://github.com/Shengwang-Community/API-Examples/tree/main/windows/APIExample/APIExample/Advanced/OriginalAudio" />
</Cards>

### API 参考 [#api-参考-3]

- [`registerAudioFrameObserver`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#registeraudioframeobserver)
- [`setRecordingAudioFrameParameters`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#api_irtcengine_setrecordingaudioframeparameters)
- [`setPlaybackAudioFrameParameters`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#api_irtcengine_setplaybackaudioframeparameters)
- [`setMixedAudioFrameParameters`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#api_irtcengine_setmixedaudioframeparameters)
- [`setPlaybackAudioFrameBeforeMixingParameters`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#api_irtcengine_setplaybackaudioframebeforemixingparameters)
- [`onRecordAudioFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#onrecordaudioframe)
- [`onPlaybackAudioFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#onplaybackaudioframe)
- [`onPlaybackAudioFrameBeforeMixing`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#onplaybackaudioframebeforemixing)
- [`onMixedAudioFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/audio/audio-raw#onmixedaudioframe)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="unity" />
本文介绍如何对采集到的原始音频数据进行前处理和后处理。
## 技术原理 [#技术原理-4]

音频传输过程中，我们可以对采集到的音频数据进行前处理和后处理，获取想要的播放效果。

对于有自行处理音频数据需求的场景，声网提供原始数据功能。你可以在将数据发送给编码器前进行前处理，对捕捉到的音频信号进行修改；也可以在将数据发送给解码器后进行后处理，对接收到的音频信号进行修改。

下图展示使用原始音频数据的基础流程：

![实现流程](https://web-cdn.agora.io/docs-portal/img/rtc/raw-audio-data.svg)

## 前提条件 [#前提条件-4]

在进行操作之前，请确保你已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方法 [#实现方法-4]

参考如下步骤，在你的项目中实现原始音频数据功能：

1. 加入频道前，创建一个 `IAudioFrameObserver` 实例，并调用 `RegisterAudioFrameObserver` 方法注册音频观测器。如果要对音频进行处理并且回写数据，需要将 `mode` 设为 INTPTR。
2. 根据需求调用 `SetRecordingAudioFrameParameters`、`SetPlaybackAudioFrameParameters` 或 `SetMixedAudioFrameParameters` 方法配置音频帧格式。
3. 实现 `OnRecordAudioFrame`、`OnPlaybackAudioFrame`、`OnPlaybackAudioFrameBeforeMixing` 和 `OnMixedAudioFrame` 回调。以上回调会对音频帧进行采集和处理。

```csharp
// 注册音频观测器
//如果你希望能对音频进行处理并且回写数据，需要将 mode 设为 INTPTR
RtcEngine.RegisterAudioFrameObserver(new AudioFrameObserver(this),
    AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_PLAYBACK |
    AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_RECORD |
    AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_MIXED |
    AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_BEFORE_MIXING |
    AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_EAR_MONITORING,
    OBSERVER_MODE.INTPTR);

    int CHANNEL = 2;
    int SAMPLE_RATE = 48000;
    // 设置播放的原始音频数据格式
    RtcEngine.SetPlaybackAudioFrameParameters(SAMPLE_RATE, CHANNEL, RAW_AUDIO_FRAME_OP_MODE_TYPE.RAW_AUDIO_FRAME_OP_MODE_READ_WRITE, 1024);
    // 设置采集的原始音频数据格式
    RtcEngine.SetRecordingAudioFrameParameters(SAMPLE_RATE, CHANNEL, RAW_AUDIO_FRAME_OP_MODE_TYPE.RAW_AUDIO_FRAME_OP_MODE_READ_WRITE, 1024);
    // 设置采集和播放音频混音后的音频数据格式
    RtcEngine.SetMixedAudioFrameParameters(SAMPLE_RATE, CHANNEL, 1024);

internal class AudioFrameObserver : IAudioFrameObserver
{   // 实现 OnRecordAudioFrame 回调
    public override bool OnRecordAudioFrame(string channelId, AudioFrame audioFrame)
    {
        ProcessAudioData(audioFrame);
        return true;
    }
    // 实现 OnPlaybackAudioFrame 回调
    public override bool OnPlaybackAudioFrame(string channelId, AudioFrame audioFrame)
    {
        ProcessAudioData(audioFrame);
        return true;
    }
    // 实现 OnPlaybackAudioFrameBeforeMixing 回调
    public override bool OnPlaybackAudioFrameBeforeMixing(string channelId,
                                                        uint uid,
                                                        AudioFrame audioFrame)
    {
        ProcessAudioData(audioFrame);
        return true;
    }
    // 实现 OnMixedAudioFrame 回调
    public override bool OnMixedAudioFrame(string channelId, AudioFrame audioFrame)
    {
        ProcessAudioData(audioFrame);
        return true;
    }

    // 从 audioFrame.buffer 指针读取音频数据，对音频数据进行处理并将数据回写
    public void ProcessAudioData(AudioFrame audioFrame)
    {
        long length = (long)audioFrame.bytesPerSample * audioFrame.channels * audioFrame.samplesPerChannel;
        for (long i = 0; i < length; i += (long)audioFrame.bytesPerSample)
        {
            IntPtr pos = (IntPtr)(i + (long)audioFrame.buffer);
            System.Runtime.InteropServices.Marshal.WriteByte(pos, 0);
        }
    }
}
```

## 参考信息 [#参考信息-4]

### 注意事项 [#注意事项-4]

声网实时互动 SDK 提供的音频裸数据采用同步回调机制。当你需要对原始音频数据进行保存或回写等操作时，请考虑以下建议：

- 为确保音频流的连续性，请不要阻塞 SDK 线程，建议不要直接在回调函数中处理数据。你可以先对接收的音频数据进行深拷贝，然后将复制的数据转移到另一个线程进行处理。

- 如果你决定在回调函数内直接同步处理音频数据，请严格控制处理时长。例如，若回调函数每 10 毫秒触发一次，那么在回调内的处理时间必须小于 10 毫秒，防止造成音频流的延迟或中断。

### 示例项目 [#示例项目-4]

声网提供了开源的原始音频数据示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">ProcessAudioRawData</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/Agora-Unity-Quickstart/blob/main/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessAudioRawData/ProcessAudioRawData.cs" />
  <Card title={<RepositoryCardTitle provider="github">ProcessAudioRawData</RepositoryCardTitle>} href="https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/blob/main/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessAudioRawData/ProcessAudioRawData.cs" />
</Cards>

### API 参考 [#api-参考-4]

- [`RegisterAudioFrameObserver`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#api_imediaengine_registeraudioframeobserver)
- [`SetRecordingAudioFrameParameters`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#api_irtcengine_setrecordingaudioframeparameters)
- [`SetPlaybackAudioFrameParameters`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#api_irtcengine_setplaybackaudioframeparameters)
- [`SetMixedAudioFrameParameters`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#api_irtcengine_setmixedaudioframeparameters)
- [`SetPlaybackAudioFrameBeforeMixingParameters`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#api_irtcengine_setplaybackaudioframebeforemixingparameters)
- [`OnRecordAudioFrame`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#callback_iaudioframeobserverbase_onrecordaudioframe)
- [`OnPlaybackAudioFrame`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#callback_iaudioframeobserverbase_onplaybackaudioframe)
- [`OnPlaybackAudioFrameBeforeMixing`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#callback_iaudioframeobserver_onplaybackaudioframebeforemixing)
- [`OnMixedAudioFrame`](/zh-CN/api-reference/rtc/unity/audio/audio-raw#callback_iaudioframeobserverbase_onmixedaudioframe)
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>
