OpenHarmony_5.0.3_usb声卡调试
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;
}
更多推荐
所有评论(0)