系类往期文章:
PyQt5实战——多脚本集合包,前言与环境配置(一)
PyQt5实战——多脚本集合包,UI以及工程布局(二)
PyQt5实战——多脚本集合包,程序入口QMainWindow(三)
PyQt5实战——操作台打印重定向,主界面以及stacklayout使用(四)
PyQt5实战——UTF-8编码器UI页面设计以及按钮连接(五)
PyQt5实战——UTF-8编码器功能的实现(六)
PyQt5实战——翻译器的UI页面设计以及代码实现(七)
PyQt5实战——翻译的实现,第一次爬取微软翻译经验总结(八)
PyQt5实战——翻译的实现,成功爬取微软翻译(可长期使用)经验总结(九)
PyQt实战——使用python提取JSON数据(十)
PyQt实战——随机涂格子的特色进度条(十一)
PyQt实战——实现编码器与进度条之间的通信,使进度条反映编码进度(十二)
PyQt实战——实现可视化音频播放器(十三)
前言
在我们上两个功能模块中,音频编解码模块与音频播放模块,都在操作音频数据,在音频数据解码后生成的pcm数据以文本格式保存,无法直接给音频播放模块播放,因此在本文中,我们将实现pcm二进制文件生成模块,将pcm数据文本转换成.pcm格式的二进制文件
UI布局展示
非常简单,仅一个文件选择区以及一个处理按钮,选择相应的pcm数据文本,开始执行即可转换为.pcm格式二进制文件
代码展示
PcmBUilderClass.py
下面给出UI界面代码
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtSvg import *from component.btnStyle import *
from component.editStyle import *
from component.getPath import *
from tools.pcmBuilder import *class WPcmBuilder(QWidget):def __init__(self):super().__init__()self.filePaths = ['']self.initUI()def initUI(self):self.pcmBuildlayout = QVBoxLayout()self.setLayout(self.pcmBuildlayout)# 文件选择self.filelayout = QHBoxLayout()self.lable = QLabel("请选择文件",self)self.filelayout.addWidget(self.lable)self.input = QLineEdit(self)LineEditStyle(self.input)self.filelayout.addWidget(self.input)self.choosebtn = QPushButton("选择文件",self)btnReleaseStyleA(self.choosebtn)self.choosebtn.clicked.connect(self.getpath)self.filelayout.addWidget(self.choosebtn)self.pcmBuildlayout.addLayout(self.filelayout)# 开始处理self.exebtn = QPushButton("开始处理",self)btnReleaseStyleA(self.exebtn)self.exebtn.clicked.connect(self.exebtn_press_clicked)self.pcmBuildlayout.addWidget(self.exebtn)def getpath(self):get_path(self)self.input.setText(self.filePaths[0])def exebtn_press_clicked(self):pcmBuilder(self.filePaths[0])
pcmBuilder.py
下面给出pcm格式转换脚本
import structdef pcmBuilder(filepath):samples = []with open(filepath, 'r') as file:lines = file.readlines()for line in lines:samples = samples + [int(data.strip(),16) - 0x10000 if int(data.strip(),16) >= 0x8000 else int(data.strip(),16) for data in line.split()]# print(samples)with open('workspaces/output.pcm', 'wb') as pcm_file:for sample in samples:pcm_file.write(struct.pack('<h', sample))print("转换完成,输出文件为 output.pcm")
这里我们解释一下:
int(data.strip(),16) - 0x10000 if int(data.strip(),16) >= 0x8000 else int(data.strip(),16) for data in line.split()
这行代码的作用是处理一行十六进制数据,并根据一定的条件将其转换为整数列表。下面我将逐步解析代码。
line.split()
: 这将字符串line
按照空格(包括空格、制表符、换行符等)分割成一个字符串列表,每个元素代表一个十六进制数字。data.strip()
: 这会去掉每个data
子字符串的前后空白字符。int(data.strip(), 16)
: 这会将处理过的data
字符串从十六进制(基数 16)转换成整数。例如,'FF'
会转换为255
,'8000'
会转换为32768
。- 条件逻辑:
if int(data.strip(), 16) >= 0x8000
: 这检查该十六进制数(转换成整数后)是否大于等于0x8000
(即 32768 十进制)。- 如果 是,则会减去
0x10000
(即 65536 十进制)。这通常用于将 16 位无符号数转换为有符号数,表示范围从-32768
到32767
(假设是 16 位二进制补码格式)。 - 如果 否,则直接使用该整数值。
- 如果 是,则会减去
samples = samples + [...]
: 这表示将处理后的结果(即这行数据的整数列表)添加到现有的samples
列表中。
示例
假设 line
是如下的内容:
pythonCopy Codeline = "7FFF 8000 0001"
逐步处理
- 第一个元素:
'7FFF'
data.strip()
->'7FFF'
int('7FFF', 16)
->32767
(十进制)- 因为
32767 < 32768
,没有修改,保持32767
。
- 第二个元素:
'8000'
data.strip()
->'8000'
int('8000', 16)
->32768
(十进制)- 因为
32768 >= 32768
,需要减去0x10000
(65536):32768 - 65536
->-32768
。
- 第三个元素:
'0001'
data.strip()
->'0001'
int('0001', 16)
->1
(十进制)- 因为
1 < 32768
,保持1
。
最终列表
经过处理后,samples
列表将会是:
samples = [32767, -32768, 1]
这段代码的作用是读取十六进制数字,并根据需要将其转换为有符号的 16 位整数(如果需要的话)。如果十六进制数大于或等于 0x8000
,它会被视为一个负数(通过二进制补码表示)。然后,这些处理后的数字会被添加到 samples
列表中。