HarmonyOS 鸿蒙Next系统音频3A效果很差

发布于 1周前 作者 caililin 来自 鸿蒙OS

HarmonyOS 鸿蒙Next系统音频3A效果很差 音频系统3A效果很差,请问有3A能实际生效的demo吗?在音频采集时使用ndk,调用OH_AudioStreamBuilder_SetCapturerInfo(),并设置属性为AUDIOSTREAM_SOURCE_TYPE_VOICE_COMMUNICATION,实际测试3A效果很差,噪声和回声非常明显。

2 回复

这边测试了下录音播放,回音噪声都在正常范围,没有出现3A效果很差的情况。

给个链接,参考下再试试呢。

https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/Audio

非通话场景下,不支持开启3A算法,可参考:

https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-audio-6-V5

可以通过返回值OH_AudioStream_Result 判断是否执行成功,

1)AUDIOCOMMON_RESULT_SUCCESS:函数执行成功。

2)AUDIOSTREAM_ERROR_INVALID_PARAM:

  • 参数builder为nullptr;
  • 参数sourceType无效。

参考链接:
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/_o_h_audio-V5#oh_audiostreambuilder_setcapturerinfo

参考一下这份代码:

// Created on 2024/7/13.

// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "ohaudio_controller.h"
#include <cstddef>
#include <cstdint>
#include <ctime>
#define LOG_TAG "LatencyTest"

#include <ohaudio/native_audiostream_base.h>
#include <ohaudio/native_audiostreambuilder.h>
#include <ohaudio/native_audiorenderer.h>
#include <ohaudio/native_audiocapturer.h>
#include <hilog/log.h>
#include <mutex>
#include <sys/time.h>

static OH_AudioRenderer* audioRenderer = nullptr;
static OH_AudioCapturer* audioCapturer = nullptr;

#define CACHE_ARRAY_SIZE 10* 1024 // 10k

class RingCache {
private:
    uint8_t mem_[CACHE_ARRAY_SIZE];
    size_t readIndex_ = 0;
    size_t availableToRead_ = 0;
    size_t writeIndex_ = 0;
    size_t availableToWrite_ = CACHE_ARRAY_SIZE;
    std::mutex mutex_;
    int32_t appLatencyMs = 0;
    int64_t lastWriteMs;
    int64_t lastReadTs;
    int32_t sampleRate_ = 16000;
    int32_t channels_ = 2;
    int32_t bitWidth_ = 2;

public:
    void SetSampleRate(int32_t sampleRate)
    {
        sampleRate_ = sampleRate;
    }
    void SetChannels(int32_t channels)
    {
        channels_ = channels;
    }
    void SetBitWidth(int32_t bitWidth)
    {
        bitWidth_ = bitWidth;
    }
    int32_t GetLatencyMs()
    {
        return appLatencyMs;;
    }
    int32_t Write(uint8_t* src, size_t contentSize)
    {
        std::lock_guard<std::mutex> guard(mutex_);
        OH_LOG_INFO(LOG_APP, "Write availableToWrite_ %zu contentSize %zu availableToRead_ %zu", availableToWrite_, contentSize, availableToRead_);
        if (availableToWrite_ < contentSize) {
            OH_LOG_ERROR(LOG_APP, "exceeds, no available space to write availableToWrite_ %zu contentSize %zu", availableToWrite_, contentSize);
            return 0;
        }
        timeval tv;
        gettimeofday(&tv, nullptr);
        lastWriteMs = tv.tv_sec * 1000 + tv.tv_usec / 1000;
        if (writeIndex_ + contentSize < CACHE_ARRAY_SIZE) {
            memcpy(mem_ + writeIndex_, src, contentSize);
            writeIndex_ += contentSize;
        } else {
            memcpy(mem_ + writeIndex_, src, CACHE_ARRAY_SIZE - writeIndex_);
            memcpy(mem_, src + CACHE_ARRAY_SIZE - writeIndex_, writeIndex_ + contentSize - CACHE_ARRAY_SIZE);
            writeIndex_ = (writeIndex_ + contentSize) % CACHE_ARRAY_SIZE;
        }
        availableToWrite_ -= contentSize;
        availableToRead_ += contentSize;
        return contentSize;
    }
    int32_t Read(uint8_t* dest, size_t requestSize)
    {
        OH_LOG_INFO(LOG_APP, "Read availableToWrite_ %zu requestSize %zu availableToRead_ %zu", availableToWrite_, requestSize, availableToRead_);
        std::lock_guard<std::mutex> guard(mutex_);
        if (availableToRead_ < requestSize) {
            OH_LOG_ERROR(LOG_APP, "exceeds, no available data to read");
            return 0;
        }
        timeval tv;
        gettimeofday(&tv, nullptr);
        int64_t offset = tv.tv_sec * 1000 + tv.tv_usec / 1000 - lastWriteMs;
        appLatencyMs = offset + (availableToRead_ - requestSize) * 1000 / sampleRate_ / channels_ / bitWidth_;
        if (readIndex_ + requestSize < CACHE_ARRAY_SIZE) {
            memcpy(dest, mem_ + readIndex_, requestSize);
            readIndex_ += requestSize;
        } else {
            memcpy(dest, mem_ + readIndex_, CACHE_ARRAY_SIZE - readIndex_);
            memcpy(dest + CACHE_ARRAY_SIZE - readIndex_, mem_, requestSize - (CACHE_ARRAY_SIZE - readIndex_));
            readIndex_ = (readIndex_ + requestSize) % CACHE_ARRAY_SIZE;
        }
        availableToWrite_ += requestSize;
        availableToRead_ -= requestSize;
        return requestSize;
    }
};

static RingCache rc;

static int32_t CaptureCallabck(OH_AudioCapturer *capturer, void *userData, void *buffer, int32_t lenth)
{
    rc.Write(static_cast<uint8_t*>(buffer), lenth);
    return 0;
}

static int32_t CapturerOnInterrupt(OH_AudioCapturer *capturer, void *userData, OH_AudioInterrupt_ForceType type,
    OH_AudioInterrupt_Hint hint)
{
    OH_LOG_INFO(LOG_APP, "%s OH_AudioInterrupt_ForceType %d, OH_AudioInterrupt_Hint %d", __FUNCTION__, type, hint );
    return 0;
}

static int32_t CaptureOnError(OH_AudioCapturer *capturer, void *userData, OH_AudioStream_Result error)
{
    OH_LOG_ERROR(LOG_APP, "capture error %d", error);
    return 0;
}

static OH_AudioCapturer_Callbacks captureCallback = {
    .OH_AudioCapturer_OnReadData = CaptureCallabck,
    .OH_AudioCapturer_OnStreamEvent = nullptr, // 不使用时,必须置空
    .OH_AudioCapturer_OnInterruptEvent = CapturerOnInterrupt,
    .OH_AudioCapturer_OnError = CaptureOnError,
};

static bool CreateAudioCapturer(int32_t sampleRate, int32_t mode)
{
    OH_LOG_INFO(LOG_APP, "create audio capture with samplerate %d, mode: %s", sampleRate, mode == 0? "VoIP": "Normal");
    OH_AudioStreamBuilder *builder;
    OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER);
    OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
    OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
    OH_AudioStreamBuilder_SetChannelCount(builder, 2);
    if (mode == 0) {
        OH_AudioStreamBuilder_SetCapturerInfo(builder, AUDIOSTREAM_SOURCE_TYPE_VOICE_COMMUNICATION);
    } else {
        OH_AudioStreamBuilder_SetCapturerInfo(builder, AUDIOSTREAM_SOURCE_TYPE_MIC);
    }
    OH_AudioStreamBuilder_SetCapturerCallback(builder, captureCallback, nullptr);
    OH_AudioStream_Result res = AUDIOSTREAM_SUCCESS;
    if ((res = OH_AudioStreamBuilder_GenerateCapturer(builder, &audioCapturer)) != AUDIOSTREAM_SUCCESS) {
        audioCapturer = nullptr;
        OH_LOG_ERROR(LOG_APP, "create audio capture failed %d", res);
        return false;
    }
    return true;
}

static int32_t RenderCallback(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t lenth)
{
    rc.Read(static_cast<uint8_t*>(buffer), lenth);
    return 0;
}

static int32_t RenderOnError(OH_AudioRenderer *render, void *userData, OH_AudioStream_Result error) {
    OH_LOG_ERROR(LOG_APP, "render error %d", error);
    return 0;
}

static OH_AudioRenderer_Callbacks renderCallback = {
    .OH_AudioRenderer_OnWriteData = RenderCallback,
    .OH_AudioRenderer_OnStreamEvent = nullptr, // 不使用时,必须置空
    .OH_AudioRenderer_OnInterruptEvent = nullptr,// 不使用时,必须置空
    .OH_AudioRenderer_OnError = RenderOnError,
};

static OH_AudioRenderer_Callbacks renderCallback5;

static bool CreateAudioRenderer(int32_t sampleRate, int32_t mode)
{
    OH_LOG_INFO(LOG_APP, "create audio render with samplerate %d, mode: %s", sampleRate, mode == 0? "VoIP": "Normal");
    OH_AudioStreamBuilder* builder;
    OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
    OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
    if (mode == 0) {
        OH_AudioStreamBuilder_SetRendererInfo(builder, AUDIOSTREAM_USAGE_VOICE_COMMUNICATION);
    } else {
        OH_AudioStreamBuilder_SetRendererInfo(builder, AUDIOSTREAM_USAGE_MOVIE);
    }
    OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
    OH_AudioStreamBuilder_SetChannelCount(builder, 2);
    OH_AudioStreamBuilder_SetFrameSizeInCallback(builder, 20 * sampleRate / 1000);
    OH_AudioStreamBuilder_SetRendererCallback(builder, renderCallback, nullptr);
    OH_AudioStream_Result res = AUDIOSTREAM_SUCCESS;
    if ((res = OH_AudioStreamBuilder_GenerateRenderer(builder, &audioRenderer)) != AUDIOSTREAM_SUCCESS) {
        audioRenderer = nullptr;
        OH_LOG_ERROR(LOG_APP, "create audio render failed %d", res);
        return false;
    }
    return true;
}

void Start(int32_t sampleRate, int32_t mode)
{
    rc.SetSampleRate(sampleRate);
    Stop();
    if (audioRenderer == nullptr) {
        CreateAudioRenderer(sampleRate, mode);    
    }
    if (audioCapturer == nullptr) {
        CreateAudioCapturer(sampleRate, mode);
    }
    if (audioRenderer != nullptr) {
        OH_AudioRenderer_Start(audioRenderer);
    }
    if (audioCapturer != nullptr) {
        OH_AudioCapturer_Start(audioCapturer);
    }
}

void Stop()
{
    if (audioRenderer != nullptr) {
        OH_AudioRenderer_Stop(audioRenderer);
        OH_AudioRenderer_Release(audioRenderer);
        audioRenderer = nullptr;
    }
    if (audioCapturer != nullptr) {
        OH_AudioCapturer_Stop(audioCapturer);
        OH_AudioCapturer_Release(audioCapturer);
        audioCapturer = nullptr;
    }
}

int32_t GetAppLatency()
{
    return rc.GetLatencyMs();
}

更多关于HarmonyOS 鸿蒙Next系统音频3A效果很差的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


针对“HarmonyOS 鸿蒙Next系统音频3A(自动增益控制AGC、自动噪声消除ANC、回声消除AEC)效果很差”的问题,以下是一些可能的解决方案及原因概述:

  1. 系统版本与驱动更新

    • 确保你的鸿蒙Next系统为最新版本,因为新版本可能修复了旧版本中的音频处理bug。
    • 检查音频驱动是否最新,过时的驱动可能导致音频处理性能不佳。
  2. 硬件兼容性

    • 某些硬件设备可能与鸿蒙Next系统的音频处理算法不完全兼容。尝试在不同设备上测试,以确定是否为硬件问题。
  3. 应用与设置

    • 检查音频应用的设置,确保3A效果已正确开启并调整至适合的场景模式。
    • 关闭不必要的后台应用,以减少对音频处理资源的占用。
  4. 系统优化

    • 尝试清理系统缓存,以释放更多资源给音频处理。
    • 如果可能,重启设备以刷新系统状态。
  5. 特定场景问题

    • 在某些特定环境(如嘈杂、回声严重的场景)下,3A效果可能受限。尝试在不同环境下测试以评估性能。

如果上述方法均未能改善音频3A效果,可能是由于系统或硬件层面的深层次问题。此时,建议直接联系官网客服以获取更专业的技术支持。官网地址是: https://www.itying.com/category-93-b0.html

回到顶部