1.回路治具设计:
2.Ui界面:
文档说明:
1.读取声卡信息:
国产化读取的输入设装置名称:
Input(输入):alsa_input、analog-input-mic、analog-input-linein
Output(输出):alsa_output、analog-output-headphones、analog-output-headphones
`pactl list sources |grep alsa_input |grep $strInputDev |grep -iv echo-cancel |grep '名称'` #查找默认输入设备
使用 sox 命令分析左右声道的这些值。以下是具体的操作步骤:
分解音频文件为左右声道: 假设你有一个立体声音频文件 stereo.wav,首先将其分解为左右声道的单声道文件
sox stereo.wav left.wav remix 1
sox stereo.wav right.wav remix 2
分析左声道: 使用 sox 命令分析左声道 left.wav 的统计信息:
sox left.wav -n stat 2>&1
分析右声道: 使用 sox 命令分析右声道 right.wav 的统计信息:
sox right.wav -n stat 2>&1
解释分析结果: 根据 sox 命令的输出,分别检查左声道和右声道的最大振幅、最小振幅、中线振幅、均方根振幅、粗略频率、最大变化量和音量调整值。确保这些值在合理范围内,并根据以下标准进行评估:
最大振幅和最小振幅:
o最大振幅应接近1,最小振幅应接近-1,但不应频繁达到这些极限值,以避免剪辑失真(clipping)。
中线振幅:
o该值应接近零,表示音频信号是平衡的,没有明显的直流偏移。
均方根振幅(RMS amplitude):
oRMS振幅是音频信号的平均功率,应在0.2到0.5之间,反映音频的总体音量水平。
粗略频率:
o粗略频率反映音频信号的主要频率成分。语音信号通常在300Hz到3400Hz之间,音乐信号频率范围更广。
最大变化量:
o如果最大变化量接近2,表示音频信号中存在很大的动态变化,可能是正常的,也可能表示信号中有突然的噪声或脉冲。
音量调整:
o表示音量是否需要调整。该值应接近1,如果远离1,可能需要进行音量归一化处理。
3.源代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'SoundTestWinFrm_New.ui'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
#sox下载安装
#sudo apt-get install --download-only sox
#3. 在UOS系统上安装SOX
#将文件传输到UOS系统后,在该系统的终端中导航到包含.deb文件的目录。使用以下命令安装这些包:
#cd /path/to/deb-files
#sudo dpkg -i *.deb
#这个命令会下载sox和它的所有依赖,但不会安装它们。下载的包通常存储在/var/cache/apt/archives/目录下。
#pulseaudio下载安装:
#1.删除PulseAudio及其配置:
#sudo apt-get remove --purge pulseaudio
#sudo rm -rf ~/.config/pulse
#2.重新安装PulseAudio:
#sudo apt-get install pulseaudio
# 首先更新本地的包列表
#sudo apt-get update
# 然后下载PulseAudio包及其依赖项
#sudo apt-get download pulseaudio
#下载完成后,你将会在当前目录中看到下载的.deb包文件。你可以使用dpkg命令手动安装这些包:
#sudo dpkg -i pulseaudio_*.deb
import json
import configparser
import platform
import re
import subprocess
import sys
import os
import logging
import wave
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QFrame
from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem
from PyQt5.QtGui import QColor
from datetime import datetime
#from subprocess import Popen
#import pyaudio
#import wave
#import librosa #pip install pyaudio librosa
#import numpy as np
import threading
import glob
from PyQt5.QtCore import QTimer, pyqtSignal
import signal
#pip install pyaudio librosa
#import pygame #pip install pygame
#import alsaaudio #pip install pyalsaaudio pip install pyalsaaudio
#sudo apt-get install portaudio19-dev
#pip install pyaudio librosa
#import pulsectl #pip install pulsectl
#apt-get install sox
class AnalysisThread(QtCore.QThread):
def __init__(self, filename,type, parent=None):
super(AnalysisThread, self).__init__(parent)
self.filename = filename
self.type=type
self.is_running = True
def run(self):
while self.is_running:
# 在这里执行音频分析
self.parent().AnalysisSoundFile_Frequency(self.filename)
self.parent().standard_frequency=int(self.parent().AnalysisSound_FrequencySample)#标准的频响
self.stop()
def stop(self):
self.is_running = False # 或者使用更安全的停止逻辑
class RecordPlayThread(QtCore.QThread):
finished_signal = QtCore.pyqtSignal() # 添加完成信号
set_default_output_device_signal = QtCore.pyqtSignal(str)
set_default_input_device_signal = QtCore.pyqtSignal(str)
#analysisSoundFile_Frequency_signal=QtCore.pyqtSignal(str)
def __init__(self,output_filename,input_filename,duration_seconds,indevice_name,outdevice_name, parent=None):
super(RecordPlayThread, self).__init__(parent)
self.output_filename=output_filename
self.input_filename=input_filename
self.duration_seconds=duration_seconds#录放时间
self.indevice_name=indevice_name
self.outdevice_name=outdevice_name
# 初始化其他变量
if parent is not None:
self.set_default_output_device_signal.connect(parent.set_default_port_and_volume)
self.set_default_input_device_signal.connect(parent.set_default_port_and_volume)
#self.analysisSoundFile_Frequency_signal.connect(parent.AnalysisSoundFile_Frequency)
self.is_running = True
def run(self):
while self.is_running:# 执行录放音代码,确保在循环中频繁检查 self.is_running
#检查录音文件是否存在,如果存在就删除
if os.path.isfile(self.input_filename):
os.remove(self.input_filename)
#执行设置默认音频设备并同步录放音
# 发出信号而不是直接调用方法
self.set_default_output_device_signal.emit(self.outdevice_name)
self.set_default_input_device_signal.emit(self.indevice_name)
# 录音线程
record_thread = threading.Thread(target=self.parent().rec_record_audio, args=(self.input_filename, self.duration_seconds))
# 播放线程
play_thread = threading.Thread(target=self.parent().play_audio_for_duration, args=(self.output_filename, self.duration_seconds))
# 启动线程
record_thread.start()
play_thread.start()
# 等待线程完成
record_thread.join()
play_thread.join()
self.parent().AnalysisSoundFile_Frequency(self.input_filename) # 解析声响
# # 使用字典将测试端口映射到对应的属性和标准
# test_ports = {
# 'front_Left': ('front_Left', 'standard_Left'),
# 'front_Right': ('front_Right', 'standard_Right'),
# 'rear_Left': ('rear_Left', 'standard_Left'),
# 'rear_Right': ('rear_Right', 'standard_Right')
# }
# 使用字典将测试端口映射到对应的属性和标准
test_ports = {
'front_Left': ('front_Left', 'standard_Left', 'Mean_norm_Left'),
'front_Right': ('front_Right', 'standard_Right', 'Mean_norm_Right'),
'rear_Left': ('rear_Left', 'standard_Left', 'Mean_norm_Left'),
'rear_Right': ('rear_Right', 'standard_Right', 'Mean_norm_Right')
}
current_port = self.parent().currentTestPort
epsilon = 0.1 # 容差值
mean_norm_epsilon = 0.004 # 上下限范围
if current_port in test_ports:
attribute, standard, mean_norm_standard = test_ports[current_port]
standard_value = getattr(self.parent(), standard)
mean_norm_standard_value = getattr(self.parent(), mean_norm_standard)
# 检查 standard_frequency 是否在 standard_value ± epsilon 范围内
is_within_range = abs(self.parent().AnalysisSound_FrequencySample - standard_value) < epsilon
# 检查 Mean_norm 是否在 mean_norm_standard_value ± mean_norm_epsilon 范围内
is_mean_norm_within_range = abs(self.parent().Mean_norm - mean_norm_standard_value) < mean_norm_epsilon
# 结合两个条件进行判断
is_within_range = is_within_range and is_mean_norm_within_range
setattr(self.parent(), attribute, is_within_range)
else:
self.parent().infoListAddData(f"未知的测试端口: {current_port}", 3)
self.finished_signal.emit() # 线程完成时发射信号
self.stop()
def stop(self):
self.is_running = False # 或者使用更安全的停止逻辑
class SoundTest(QWidget):
updateTimer = pyqtSignal(bool) # 定义一个信号
# 定义一个新的信号,用于传递日志消息
log_message_signal = pyqtSignal(str, int)
# 定义颜色代码
COLOR_SUCCESS = "\033[1;32;43m"
COLOR_ERROR = "\033[1;31m"
COLOR_RESET = "\033[0m"
def __init__(self):
super().__init__()
# 连接信号和槽
self.log_message_signal.connect(self.add_log_message)
# ... 现有的初始化代码 ...
self.analysis_threads = [] # 存储所有的 AnalysisThread 实例
self.record_play_threads = [] # 存储所有的 RecordPlayThread 实例
self.threads = []
# 设置环境变量
os.environ["XDG_RUNTIME_DIR"] = "/run/user/{}".format(os.getuid())
self.setupUi()
self.testArgs = [] # 测试参数信息
self.soundFile = [] # 音频文件
self.itemName = '' # 项目名称
self.testStandardArgs = '' # 测试标准参数
self.logger = None
self.config = None
self.AnalysisSound_FrequencySample = 0#解析的音频频率标准值
self.Mean_norm=0 #平均归一化值(振幅绝对值的平均值,显示音频信号的平均能量水平)
self.Device_1AnalysisSound_Frequency = 0 # 硬件设备1解析音频频率值
self.Device_2AnalysisSound_Frequency = 0 # 硬件设备2解析音频频率值
self.standard_Left=0.2
self.standard_Right=0.2
self.Mean_norm_Left=0.01
self.Mean_norm_Right=0.01
self.standard_frequency=0
self.tempTestPassItem = 0 # 临时测试PASS项
self.output_devices = [] # 输出音频设备名称
self.input_devices = [] # 输入音频设备名称
self.inputDevName='' # 输入音频设备名称
self.inputPort='' # 输入音频端口
self.outputDevName='' # 输出音频设备名称
self.outPort='' # 输出音频端口
self.testPortCount=0 # 测试接口数
self.testPassItem=0 # 测试PASS项
self.front_Device = False
self.rear_Device = False
self.front_Left=False #前置左声道
self.front_Right=False #前置右声道
self.rear_Left=False #后置左声道
self.rear_Right=False #后置右声道
self.isInstall_pulseaudio=False#是否安装pulseaudio文件
self.isUninstall_pulseaudio=False#是否需要移除pulseaudio文件
self.currentTestPort = 'front_Left' # 默认测试接口包含front_Left(前置左声道)、front_Left(前置右声道)、rear_Left(后置左声道)、rear_Right(后置右声道)
self.testStandardArgs = '' # 测试标准参数
self.loggerrinfo() # 创建日志文件
self.getitemName() # 获取项目名称
# 读取系统信息
self.system_name = self.get_system_name().lower()
# 读取CPU框架
self.cpu_architecture = self.get_cpu_architecture()
# 读取测试配置
# self.itemName = self.config.get('TestItemNameArrays', 'SoundTestName')
# self.itemFailSleepExit = int(self.config.get('TestItemWinFrmSleepExit', 'SoundTestName')) # 项目测试Fail延时退出
# self.testArgs = self.readJsonInfo('./Conf/TestArgs.json') # 读取测试参数信息
#禁用HDMI
self.disable_hdmi_audio()
if self.install_sox():# 检查安装测试sox环境
if self.readJsonTestArgs():#读取测试参数
if self.analytic_Test_Parameter():#解析测试参数
if self.install_pulseaudio():#检测安装插件
# self.ReadPortCount = int(self.testStandardArgs) # 读取测试参数 #读取标准值
# 读取音频文件
self.soundFile.append('./' + self.config.get('SoundHd', 'F_L')) # 高音左声道
self.soundFile.append('./' + self.config.get('SoundHd', 'F_R')) # 高音左声道
if self.getTestPortCount() == True:
self.infoListAddData(f'读取音频:{str(self.testPortCount)}.个端口-实际配置端口:{self.ReadPortCount}', 1)
# self.lbl_MesInfo.setText('标准音频文件频率解析中...')
self.delete_wav_files('.') # 删除目录生成的所有音频文件
# 创建一个定时器来检查视频是否播放结束
self.timer_1 = QTimer(self)
self.timer_1.setInterval(1000) # 每秒检查一次
self.timer_1.timeout.connect(self.check_Test)
self.timer_1.start()
# 连接信号到槽
self.updateTimer.connect(self.handleTimer)
else:
self.infoListAddData(f'读取音频:{str(self.testPortCount)}.个端口-实际配置端口:{self.ReadPortCount}', 3)
else:
sys.exit(1)
# 解析测试参数
def analytic_Test_Parameter(self):
try:
if self.testArgs:
if isinstance(self.testArgs, str):
arrays = self.testArgs.split('|')
if len(arrays) < 4:
self.infoListAddData("Expected at least 4 sections in the test arguments string",3)
return False
# 最大频响参数
maximumfrequency = arrays[0].split(',')
if len(maximumfrequency) < 2:
self.infoListAddData("Expected 2 frequency parameters in the first section",3)