OpenHarmony 5.0.3 usb 声卡配置记录

项目目标是引用外部USB声卡作为唯一的声卡、MIC使用,在OpenHarmony 5.0.3下配置成功。由于声卡型号众多,这个配置仅测试了公司选定的型号,如遇到其他声卡请自行变通

一 配置声卡驱动,确保alsa-util工具可以录音与播放

1 内核配置

打开CONFIG_SOUND,CONFIG_SND关闭CONFIG_DRIVERS_HDF_AUDIO

--- a/linux-5.10/rk3568/arch/arm64_defconfig
+++ b/linux-5.10/rk3568/arch/arm64_defconfig
@@ -3622,8 +3622,8 @@ CONFIG_DUMMY_CONSOLE_ROWS=25
 # CONFIG_LOGO is not set
 # end of Graphics support
 
-# CONFIG_SOUND is not set
-# CONFIG_SND is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
 CONFIG_SND_TIMER=y
 CONFIG_SND_PCM=y
 CONFIG_SND_PCM_ELD=y
@@ -5407,11 +5407,11 @@ CONFIG_DRIVERS_HDF_SENSOR_ACCEL=y
 CONFIG_DRIVERS_HDF_SENSOR_ACCEL_MXC6655XA=y
 # CONFIG_DRIVERS_HDF_STORAGE is not set
 CONFIG_DRIVERS_HDF_USB_F_GENERIC=y
-CONFIG_DRIVERS_HDF_AUDIO_HDMI=y
-CONFIG_DRIVERS_HDF_AUDIO_USB=y
+#CONFIG_DRIVERS_HDF_AUDIO_HDMI is not set
+#CONFIG_DRIVERS_HDF_AUDIO_USB is not set
 # CONFIG_DRIVERS_HDF_AUDIO_CAP_REPORT is not set
-CONFIG_DRIVERS_HDF_AUDIO_RK3568=y
-CONFIG_DRIVERS_HDF_AUDIO_ANA_HEADSET=y
+#CONFIG_DRIVERS_HDF_AUDIO_RK3568 is not set
+#CONFIG_DRIVERS_HDF_AUDIO_ANA_HEADSET=y
 CONFIG_DRIVERS_HDF_VIBRATOR_LINEAR=y

2 产品化编译开关配置

# vendor/hihope/rk3568

index 9a03495..f053583 100644
--- a/rk3568/config.json
+++ b/rk3568/config.json
@@ -84,7 +84,7 @@
           "component": "drivers_peripheral_audio",
           "features": [
             "drivers_peripheral_audio_feature_full_test_suite = true",
-            "drivers_peripheral_audio_feature_alsa_lib = false",
+            "drivers_peripheral_audio_feature_alsa_lib = true",
             "drivers_peripheral_audio_feature_effect = true"
           ]
         },
diff --git a/rk3568/hals/audio/product.gni b/rk3568/hals/audio/product.gni
index cea922b..9db8ce8 100644
--- a/rk3568/hals/audio/product.gni
+++ b/rk3568/hals/audio/product.gni
@@ -11,10 +11,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-enable_audio_alsa_mode = false
+enable_audio_alsa_mode = true
 drivers_peripheral_audio_feature_hdf_proxy_stub = true
 drivers_peripheral_audio_feature_hal_notsupport_pathselect = false
 enable_audio_analog_headset = true
 drivers_peripheral_audio_feature_policy_config = true
-drivers_peripheral_audio_feature_alsa_lib = false
+drivers_peripheral_audio_feature_alsa_lib = true
 drivers_peripheral_audio_feature_effect = true
# drivers/peripheral/audio

--- a/audio/audio.gni
+++ b/audio/audio.gni
@@ -13,7 +13,7 @@ declare_args() {
   drivers_peripheral_audio_feature_user_mode = false
   drivers_peripheral_audio_feature_full_test_suite = false
   drivers_peripheral_audio_feature_policy_config = true
-  drivers_peripheral_audio_feature_alsa_lib = false
+  drivers_peripheral_audio_feature_alsa_lib = true
   drivers_peripheral_audio_feature_rich_device = false

3 alsa-lib和alsa-utils组件编译依赖添加

有了alsa_utils工具才能使用aplay和arecord等命令控制声卡播放和录音。
在drivers\peripheral\audio\bundle.json中配置alsa_lib和alsa_utils库

##drivers/peripheral/audio 仓库
"third_party": [
        "alsa-lib"
      ]
      
  "sub_component": [
        "//third_party/alsa-utils:alsa-utils"
      ],

由于"alsa-lib"已经存在,所以仅配置alsa-utils即可。实际的补丁是下面这样:

--- a/audio/bundle.json
+++ b/audio/bundle.json
@@ -48,6 +48,7 @@
             "sub_component": [
                 "//drivers/peripheral/audio:hdi_audio",
                 "//drivers/peripheral/audio/effect:effect_model",
+                "//third_party/alsa-utils:alsa-utils",
                 "//drivers/peripheral/audio:libaudio_header_static"
             ],

4 vendor层配置

alsa_adapter.json 添加USB声卡信息,这个根据/proc/asound/cards文件内容配置。这个文件需要前面的配置(内核配置以及产品化编译开关)生效后才会生成。 audio_policy_config.xml 添加USB声卡播放录制属性。文件有两个,分别对应32位和64位,根据自己的系统配置。本文以32位做例子,这也是默认的。

--- a/rk3568/hals/audio/alsa_adapter.json
+++ b/rk3568/hals/audio/alsa_adapter.json
@@ -7,8 +7,13 @@
         },
         {
             "name": "hdmi",
-            "cardId": 1,
+            "cardId": 2,
             "cardName": "rockchiphdmi"
+        },
+        {
+            "name": "usb",
+            "cardId": 1,
+            "cardName": "C"
         }
     ]
-}



diff --git a/rk3568/hals/audio/config/arm/audio_policy_config.xml b/rk3568/hals/audio/config/arm/audio_policy_config.xml
index 7f6e4d1..ae880dc 100644
--- a/rk3568/hals/audio/config/arm/audio_policy_config.xml
+++ b/rk3568/hals/audio/config/arm/audio_policy_config.xml
@@ -75,6 +75,29 @@
                 <device name="Distributed_In" type="DEVICE_TYPE_MIC" pin="PIN_IN_DAUDIO_DEFAULT" role="input" supportPipes="distributed_input"/>
             </devices>
         </adapter>
+        <adapter name="usb">
+            <pipes>
+                <pipe name="usb_output" role="output">
+                    <paProp lib="libmodule-hdi-sink.z.so" role="sink" fixed_latency="0" render_in_idle_state="1" moduleName="Usb_arm_speaker"/>
+                    <streamProps>
+                        <streamProp format="s16le" sampleRates="44100" channelLayout="CH_LAYOUT_STEREO" bufferSize="16384"/>
+                    </streamProps>
+                    <attributes>
+                        <attribute name="preload" value="true"/>
+                    </attributes>
+                </pipe>
+                <pipe name="usb_input" role="input">
+                    <paProp lib="libmodule-hdi-source.z.so" role="source" moduleName="Usb_arm_mic"/>
+                    <streamProps>
+                        <streamProp format="s16le" sampleRates="44100" channelLayout="CH_LAYOUT_MONO" bufferSize="19200"/>
+                    </streamProps>
+                </pipe>
+            </pipes>
+            <devices>
+                <device name="Usb_Headset_Out" type="DEVICE_TYPE_USB_HEADSET" pin="PIN_OUT_USB_HEADSET" role="output" supportPipes="usb_output"/>
+                <device name="Usb_Headset_In" type="DEVICE_TYPE_USB_HEADSET" pin="PIN_IN_USB_HEADSET" role="input" supportPipes="usb_input"/>
+            </devices>
+        </adapter>
     </adapters>
     <volumeGroups>
         <groups>
@@ -91,7 +114,7 @@
         </groups>
     </interruptGroups>
     <globalConfigs>
-        <defaultOutput adapter="primary" pipe="primary_output" device="Speaker"/>
+        <defaultOutput adapter="usb" pipe="usb_output" device="Usb_arm_speaker"/>
         <commonConfigs>

上面这些做好就可以用alsa-util工具(aplay arecord)来播放wav音频、播放wav音频了。

录音 -- 指定设备 hw:1,0开始录音,保存在mm.wav文件
本例,序号0的设备是一个HDMI Speaker,序号1才是USB声卡(MIC + Speaker)

# arecord -D hw:1,0 -r 48000 -f S16_LE mm.wav
Recording WAVE 'mm.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono
Aborted by signal Interrupt...
#

这个播放实际在card 0(hdmi)输出。card 1(USB)的输出是双声道,直接指定usb声卡播放失败了。
# aplay mm.wav
Playing WAVE 'mm.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Mono
#

这个播放实际card 1(USB)的输,文件是从windows 11下找的,双声道--播放成功。
播放
# aplay -D hw:1,0 test_44100_2.wav
Playing WAVE 'test_44100_2.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
#

二 异常处理,配置USB声卡作为默认声卡

1 snd_ctl_open执行失败的问题

这个问题只在32的系统出现,在alsa_lib里面加日志会发现是找不到system/etc下面的conf文件,系统需要配置一下沙箱路径; 补丁如下:

#base/startup/init

--- a/services/sandbox/chipset-sandbox.json
+++ b/services/sandbox/chipset-sandbox.json
@@ -65,6 +65,10 @@
             "src-path" : "/chip_prod",
             "sandbox-path" : "/chip_prod",
             "sandbox-flags" : [ "bind", "rec", "private" ]
+        }, {
+            "src-path" : "/system/etc",
+            "sandbox-path" : "/system/etc",
+            "sandbox-flags" : [ "bind", "rec", "private" ]
         }

2 将usb声卡设为默认声卡

音频适配器支持USB声卡

## drivers/peripheral/audio

--- a/audio/hdi_service/primary_impl/src/audio_adapter.c
+++ b/audio/hdi_service/primary_impl/src/audio_adapter.c
@@ -94,11 +94,11 @@ int32_t CheckParaDesc(const struct AudioDeviceDescriptor *desc, const char *type
 
     enum AudioPortPin pins = desc->pins;
     if (!strcmp(type, TYPE_CAPTURE)) {
-        if (pins == PIN_IN_MIC || pins == PIN_IN_HS_MIC || pins == PIN_IN_LINEIN) {
+        if (pins == PIN_IN_MIC || pins == PIN_IN_HS_MIC || pins == PIN_IN_LINEIN || PIN_IN_USB_HEADSET) {
             return HDF_SUCCESS;
         }
     } else if (!strcmp(type, TYPE_RENDER)) {
-        if (pins == PIN_OUT_SPEAKER || pins == PIN_OUT_HEADSET || pins == PIN_OUT_LINEOUT || pins == PIN_OUT_HDMI
+        if (pins == PIN_OUT_SPEAKER || pins == PIN_OUT_HEADSET || pins == PIN_OUT_LINEOUT || pins == PIN_OUT_HDMI || PIN_OUT_USB_HEADSET
             || pins == (PIN_OUT_SPEAKER | PIN_OUT_HEADSET)) {
             return HDF_SUCCESS;
         }
默认USB声卡:
## foundation/multimedia/audio_framework

--- a/services/audio_policy/server/include/service/manager/audio_adapter_manager.h
+++ b/services/audio_policy/server/include/service/manager/audio_adapter_manager.h
@@ -287,7 +287,7 @@ private:
     std::mutex systemSoundMutex_;
     std::unordered_map<std::string, std::string> systemSoundUriMap_;
     StreamVolumeInfoMap streamVolumeInfos_;
-    DeviceType currentActiveDevice_ = DeviceType::DEVICE_TYPE_SPEAKER;
+    DeviceType currentActiveDevice_ = DeviceType::DEVICE_TYPE_USB_ARM_HEADSET;
     AudioRingerMode ringerMode_;
     int32_t safeVolume_ = 0;
     SafeStatus safeStatus_ = SAFE_ACTIVE;
--- a/services/audio_policy/server/src/service/audio_policy_service.cpp
+++ b/services/audio_policy/server/src/service/audio_policy_service.cpp
@@ -4042,6 +4042,7 @@ int32_t AudioPolicyService::HandleSpecialDeviceType(DeviceType &devType, bool &i
 
         IPCSkeleton::SetCallingIdentity(identity);
         AUDIO_INFO_LOG("get value %{public}s  from hal when usb device connect", value.c_str());
+        devType = DEVICE_TYPE_USB_ARM_HEADSET;
         if (isConnected) {
             bool isArmConnect = (value == "false" || hasHifiUsbDevice_);
             if (isArmConnect) {
@@ -4700,7 +4701,7 @@ bool AudioPolicyService::OpenPortAndAddDeviceOnServiceConnected(AudioModuleInfo
         }
     }
 
-    if (devType == DEVICE_TYPE_MIC) {
+    if (devType == DEVICE_TYPE_USB_ARM_HEADSET) {
         primaryMicModuleInfo_ = moduleInfo;
     }

完成上述工作后在USB声卡已经成为默认的声卡,后面这些也是配置默认声卡为USB的。后面这段也是配置USB为默认声卡的,确认正常工作--未确认是否必要。

diff --git a/services/audio_policy/server/src/service/audio_policy_service.cpp b/services/audio_policy/server/src/service/audio_policy_service.cpp
index 00e941708..ce846c14c 100644
--- a/services/audio_policy/server/src/service/audio_policy_service.cpp
+++ b/services/audio_policy/server/src/service/audio_policy_service.cpp
@@ -1703,10 +1703,10 @@ std::string AudioPolicyService::GetSourcePortName(InternalDeviceType deviceType)
     std::string portName = PORT_NONE;
     switch (deviceType) {
         case InternalDeviceType::DEVICE_TYPE_MIC:
-        case InternalDeviceType::DEVICE_TYPE_USB_HEADSET:
         case InternalDeviceType::DEVICE_TYPE_BLUETOOTH_SCO:
             portName = PRIMARY_MIC;
             break;
+        case InternalDeviceType::DEVICE_TYPE_USB_HEADSET:
         case InternalDeviceType::DEVICE_TYPE_USB_ARM_HEADSET:
             portName = USB_MIC;
             break;
@@ -5552,6 +5552,10 @@ InternalDeviceType AudioPolicyService::GetDeviceType(const std::string &deviceNa
         devType = DEVICE_TYPE_FILE_SINK;
     } else if (deviceName == "file_source") {
         devType = DEVICE_TYPE_FILE_SOURCE;
+    } else if (deviceName == "Usb_arm_speaker") {
+        devType = DEVICE_TYPE_USB_ARM_HEADSET;
+    } else if (deviceName == "Usb_arm_mic") {
+        devType = DEVICE_TYPE_USB_ARM_HEADSET;
     }
 
     return devType;
@@ -6754,7 +6758,8 @@ AudioModuleInfo AudioPolicyService::ConstructOffloadAudioModuleInfo(DeviceType d
     audioModuleInfo.fileName = "offload_dump_file";
     audioModuleInfo.offloadEnable = "1";
 
-    audioModuleInfo.channels = "2";
+    // audioModuleInfo.channels = "2";
+    audioModuleInfo.channels = "1";
     audioModuleInfo.rate = "48000";
     audioModuleInfo.bufferSize = "7680";

完成上述工作后在USB声卡已经成为默认的声卡,但是录音功能不正常--录制的数据只有实际设定的1/3-1/4,播放出来也是很奇怪的声音--速度变快了。

3 解决声音变快的问题

查看日志发现声道参数为2,实际USB的MIC只有一个channel。这里强制给它写1。
另外有两个参数修改是解决录音变快问题的。

diff --git a/services/audio_service/server/src/capturer_in_server.cpp b/services/audio_service/server/src/capturer_in_server.cpp
index 819de8d3b..db05d84d1 100644
--- a/services/audio_service/server/src/capturer_in_server.cpp
+++ b/services/audio_service/server/src/capturer_in_server.cpp
@@ -34,7 +34,7 @@ namespace OHOS {
 namespace AudioStandard {
 namespace {
     static constexpr int32_t VOLUME_SHIFT_NUMBER = 16; // 1 >> 16 = 65536, max volume
-    static const size_t CAPTURER_BUFFER_DEFAULT_NUM = 4;
+    static const size_t CAPTURER_BUFFER_DEFAULT_NUM = 5;
     static const size_t CAPTURER_BUFFER_WAKE_UP_NUM = 100;
     static const uint32_t OVERFLOW_LOG_LOOP_COUNT = 100;
 }
diff --git a/services/audio_service/server/src/pa_adapter_manager.cpp b/services/audio_service/server/src/pa_adapter_manager.cpp
index 4e0412f42..c2dc9a02f 100644
--- a/services/audio_service/server/src/pa_adapter_manager.cpp
+++ b/services/audio_service/server/src/pa_adapter_manager.cpp
@@ -33,7 +33,7 @@ namespace OHOS {
 namespace AudioStandard {
 const uint32_t CHECK_UTIL_SUCCESS = 0;
 const uint64_t BUF_LENGTH_IN_MSEC = 20;
-static const uint32_t PA_RECORD_MAX_LENGTH_NORMAL = 4;
+static const uint32_t PA_RECORD_MAX_LENGTH_NORMAL = 5;
 static const uint32_t PA_RECORD_MAX_LENGTH_WAKEUP = 30;
 static const int32_t CONNECT_STREAM_TIMEOUT_IN_SEC = 5; // 5S

4 音乐播放暂停后再恢复播放没有声音

原因是暂停后FadeoutState进入DONE_FADE状态未能正确恢复。打如下补丁解决。

#foundation/multimedia/audio_framework

--- a/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c
+++ b/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c
@@ -1173,7 +1173,7 @@ static void PreparePrimaryFading(pa_sink_input *sinkIn, pa_mix_info *infoIn, pa_
         pa_memchunk_make_writable(&infoIn->chunk, 0);
         void *data = pa_memblock_acquire_chunk(&infoIn->chunk);
         DoFading(data + infoIn->chunk.length - fadeLenth, fadeLenth, format, (uint32_t)u->ss.channels, 1);
-        if (fadeStrategy == FADE_STRATEGY_DEFAULT) { SetFadeoutState(streamIndex, DONE_FADE); }
+        if (fadeStrategy == FADE_STRATEGY_DEFAULT) { SetFadeoutState(streamIndex, NO_FADE); }
         pa_memblock_release(infoIn->chunk.memblock);
     }
 }

5 无法进行第二次录音

首次录音正常,二次录音失败。原因是录音结束后pcm状态没还原,环境未清理干净导致后续参数设置失败。
需要写一个函数将pcm重置为open状态,在录音结束时调用,做好清理与状态还原。补丁如下:

#drivers/peripheral/audio

--- a/audio/supportlibs/alsa_adapter/src/alsa_snd_capture.c
+++ b/audio/supportlibs/alsa_adapter/src/alsa_snd_capture.c
@@ -789,12 +789,83 @@ static int32_t CaptureStartImpl(struct AlsaCapture *captureIns)
     AUDIO_FUNC_LOGE("Not yet realized");
     return HDF_SUCCESS;
 }
-
+
+/**
+ * @brief 将PCM设备重置到OPEN状态,使其可以重新配置硬件参数
+ * @param pcm PCM设备句柄
+ * @return 成功返回0,失败返回负的错误代码
+ */
+int reset_pcm_to_open_state(snd_pcm_t *pcm) {
+    snd_pcm_state_t state = snd_pcm_state(pcm);
+    int err = 0;
+
+    AUDIO_FUNC_LOGE("当前设备状态: %{public}d - ", state);
+    switch (state) {
+        case SND_PCM_STATE_OPEN:
+            AUDIO_FUNC_LOGE("设备已在OPEN状态,无需重置。\n");
+            return 0; // 已经是目标状态
+        case SND_PCM_STATE_SETUP:
+        case SND_PCM_STATE_PREPARED:
+            AUDIO_FUNC_LOGE("SETUP/PREPARED状态,使用snd_pcm_drop重置。\n");
+            err = snd_pcm_drop(pcm);
+            if (err < 0) {
+                AUDIO_FUNC_LOGE("snd_pcm_drop失败: %s\n", snd_strerror(err));
+                return err;
+            }
+            break;
+        case SND_PCM_STATE_RUNNING:
+        case SND_PCM_STATE_XRUN:
+        case SND_PCM_STATE_DRAINING:
+        case SND_PCM_STATE_PAUSED:
+            AUDIO_FUNC_LOGE("RUNNING/XRUN/DRAINING/PAUSED状态,尝试恢复或重置。\n");
+            // 先尝试温和的恢复
+            err = snd_pcm_recover(pcm, -EPIPE, 0); // 静默模式
+            if (err < 0) {
+                // 恢复失败,使用强硬重置
+                AUDIO_FUNC_LOGE("恢复失败,使用snd_pcm_drop强硬重置。\n");
+                err = snd_pcm_drop(pcm);
+                if (err < 0) {
+                    AUDIO_FUNC_LOGE("snd_pcm_drop失败: %{public}s\n", snd_strerror(err));
+                    return err;
+                }
+            }
+            break;
+        case SND_PCM_STATE_SUSPENDED:
+            AUDIO_FUNC_LOGE("SUSPENDED状态,尝试恢复。\n");
+            err = snd_pcm_resume(pcm);
+            if (err < 0) {
+                AUDIO_FUNC_LOGE("snd_pcm_resume失败: %{public}s\n", snd_strerror(err));
+                // 恢复失败,尝试drop
+                err = snd_pcm_drop(pcm);
+                if (err < 0) {
+                    AUDIO_FUNC_LOGE("snd_pcm_drop也失败: %{public}s\n", snd_strerror(err));
+                    return err;
+                }
+            }
+            break;
+        case SND_PCM_STATE_DISCONNECTED:
+            AUDIO_FUNC_LOGE("DISCONNECTED状态,设备已断开,无法重置。\n");
+            return -ENODEV;
+        default:
+            AUDIO_FUNC_LOGE("未知状态,尝试通用重置。\n");
+            err = snd_pcm_drop(pcm);
+            if (err < 0){
+                AUDIO_FUNC_LOGE("snd_pcm_drop失败: %{public}s\n", snd_strerror(err));
+                return err;
+            }
+            break;
+    }
+    return 0;
+}

static int32_t CaptureStopImpl(struct AlsaCapture *captureIns)
{
    AUDIO_FUNC_LOGE("Not yet realized");
+   struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)captureIns;
+   cardIns->mmapFlag = false;
+   reset_pcm_to_open_state(cardIns->pcmHandle);
    return HDF_SUCCESS;
}

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐