欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

黑苹果 Catalina 15 声卡定制

程序员文章站 2022-05-11 11:13:30
...

黑苹果 Catalina 15 声卡定制

说明

​ 使用黑苹果一段时间, 声卡驱动问题较多(如: 机箱前置扬声器无效), 本文总结网上一些定制方法, 简化操作。由于d对中间某些原理不是完全了解, 本方法定制的驱动在我的机器使用了几个月没有异常, 其他机器仅供参考,。水平有限,欢迎指出错误。

a). 定制流程步骤

1.提取codec信息
2.推导configdata & 节点路径
3.修改AppleALC文件
AppleALC-master/Resources/ALC892/Info.plist
同上目录/layoutXX.xml
同上目录/PlatformsXX.xml
AppleALC-master/Resources/PinConfigs.kext/Contents/Info.plist
4.编译驱动
5.放入clover目录并注入ID

b). 环境信息

  • 系统 macOS Catalina 15.x
  • AppleALC v1.4.3
  • xcode(AppStore下载即可)
  • python3.5或以上(安装XCode后默认会下载, 或去官网下载安装包)
  • Clover Configurator
  • PlistEdit Pro

一: Clover提取声卡codec

参见: https://blog.daliansky.net/Use-AppleALC-sound-card-to-drive-the-correct-posture-of-AppleHDA.html

a). 通过Linux提取

b). Clover引导界面按F8提取

黑苹果 Catalina 15 声卡定制

c). 重命名文件名为codec.txt

二: 推导ConfigData & 节点路径

high-definition-audio-specification.pdf中178页有详细规范描述,可以参考

​ 按黑果小兵和简书上教程, 有些信息好像不必要(如: EAPD节点最后添加一个XXX70C02 & 修正configdata)

如果无效, 请手动修正configdata

verbit.sh分析Clover提取的codec一直报错& codecgraph一直没有安装成功,因此琢磨了自己的脚本, 不一定完全正确,但是有用就够了

https://blog.daliansky.net/Use-AppleALC-sound-card-to-drive-the-correct-posture-of-AppleHDA.html#more

https://www.jianshu.com/p/29a74f0664f1

a). 执行python3 verbit.py codec.txt 生成configdata

黑苹果 Catalina 15 声卡定制

​ 脚本生成结果为markdown格式, 粘贴进在线markdown网站, 如:https://www.mdeditor.com/

黑苹果 Catalina 15 声卡定制

​ 空格隔开每组数据, 因此本例中configdata为:

01471c10 01471d40 01471e01 01471f01 01871c20 01871d90 01871ea1 01871f01 01971c30 01971d90 01971ea1 01971f02 01a71c2f 01a71d30 01a71e81 01a71f01 01b71c50 01b71d40 01b71e21 01b71f02

b). 推导节点路径

​ 本脚本参考简书 https://www.jianshu.com/p/29a74f0664f1 手动推导原理 而写,简化操作

执行python3 FindNodePath.py codec.txt输入节点如: 0x14

黑苹果 Catalina 15 声卡定制

在已选节点不重复的前提下,优先选择数字小(不知道是不是必须满足, 尽量找不一样的和短的)

节点 路径 十进制路径
0x14 0x14 → 0x0c → 0x02 20→12 → 2
0x18 0x09 → 0x22 → 0x18 9→ 34 → 24
0x19 0x08 → 0x23 → 0x19 8→ 35 → 25
0x1a 0x09 → 0x22 → 0x1a 9→ 34→ 26
0x1b 0x1b → 0x0d → 0x03 27→ 13→ 3

三: 修改AppleALC源码

我们只需要修改2个文件PinConfigs.kext/Contents/Info.plist(放configdata)和AppleALC-master/Resources/ALC892/PlatformsXX.xml(放节点路径信息), 但是额外两个文件信息来推导PlatformsXX.xml

a). 在AppleALC-master/Resources/PinConfigs.kext/Contents/Info.plist中修改congfigdata并找到要修改节点路径的文件

  1. 根据codecID(codec.txt开头Vendor Id对应数据转换为10进制)搜索, 定位到第一个找到的位置

    记住该LayoutID的值 ,用来查找后面的文件

黑苹果 Catalina 15 声卡定制
2. 定位到AppleALC-master/Resources/ALC892(你的声卡型号)/Info.plist

  • 根据之前的LayoutID,在Files->Layouts下面找到ID值和之前LayoutID值一样的节点,记下Layout.xml的路径
  • 根据之前的LayoutID,在Files->Platforms下面找到ID值和之前LayoutID值一样的节点,记下Platform.xml的路径

黑苹果 Catalina 15 声卡定制
3. 根据上一步找到的layout1.xml记录下PathMapID

黑苹果 Catalina 15 声卡定制
4. 根据步骤2中找到的Platform.xml和步骤3中的PathMapID定位到了我们要修改的节点路径地方了

黑苹果 Catalina 15 声卡定制

b). 节点路径修改

  • 大致位置顺序

黑苹果 Catalina 15 声卡定制

  • 输入和输出类详细

​ 输入类型的如麦克风第一个节点含有AMP,可以在原有配置上复制过来一个再修改数字就是了

黑苹果 Catalina 15 声卡定制

​ 输出类型的如HeadPhone除第一个节点外其他都含有AMP,可以在原有配置上复制过来一个再修改数字就是了

黑苹果 Catalina 15 声卡定制

c). 编译AppleALC, 并将结果放入EFI中Clover对应的目录

需要下载LiluDEBUG版本的 放入AppleALC目录

四: 在Colver中注入步骤三->a).->1.中得到的LayoutID

黑苹果 Catalina 15 声卡定制
HDMI接口休眠再唤醒声卡驱动可能会导致系统死机, 可以用该补丁

636F6D2E 6170706C 65005F5F 6B65726E 656C5F5F 00替换为6E6F742E 6170706C 65005F5F 6B65726E 656C5F5F 00

黑苹果 Catalina 15 声卡定制

五: 最终效果

节点路径不同分组和位置, 会影响最终的结果, 请按需求自己尝试放位置和组

黑苹果 Catalina 15 声卡定制

六: 脚本源代码

FindNodePath.py

#!/usr/bin/python3
"""
    @Python:   Version 3.5
    @File:     FindNodePath.py
    @Author:   Leo
    @Date:     2019/11/8
    @license: BSD, see LICENSE for more details.
    @Desc:  根据codec.txt找到给定节点所有可能的正反向路径
"""

import sys
import re


class Node(object):
    """
    name: 节点字符串, 如: 0x14
    desc: 节点类型描述
    parent_list: 父节点对象列表,如: [<Node 1>, <Node 2>, ...]
    child_name_list: 子节点字符串列表, 如: ['0x13', '0x14', ...]
    child_obj_list: 子节点对象列表,如: [<Node 1>, <Node 2>, ...]
    """
    def __init__(self, name, desc):
        self.name = name
        self.desc = desc

        self.parent_list = []
        self.child_name_list = []
        self.child_obj_list = []

    def add_parent(self, node):
        self.parent_list.append(node)

    def get_parent_paths(self):
        if len(self.parent_list) == 0:
            print('\033[33m节点: %s 未检索到父节点, 因此无反向路径\033[0m' % self.name)
            return
        paths = []

        def find_path(node, path):
            if node.name in path:   # 回路, 抛弃该路径
                return
            path.append(node.name)
            if len(node.parent_list) == 0:
                path.reverse()
                paths.append(path)
                return
            for parent in node.parent_list:
                _path_list_copy = list(path)
                find_path(parent, _path_list_copy)

        find_path(self, [])
        paths.sort(key=lambda x: len(x))    # 排序,最短的放前面打印
        print('\033[32m节点: %s 找到以下反向路径(输入类: 如mic, line in), 共 %d 条路径, 仅显示路径长度小等于4的路径' %
              (self.name, len(paths)))
        for _ in paths:
            if len(_) <= 4:
                print(' → '.join(_))
        print('\033[0m')

    def get_child_paths(self):
        if len(self.child_obj_list) == 0:
            print('\033[33m节点: %s 未检索到子节点, 因此无正向路径\033[0m' % self.name)
            return
        paths = []

        def find_path(node, path):
            if node.name in path:   # 回路, 抛弃该路径
                return
            path.append(node.name)
            if len(node.child_obj_list) == 0:
                paths.append(path)
                return
            for child in node.child_obj_list:
                _path_list_copy = list(path)
                find_path(child, _path_list_copy)

        find_path(self, [])
        paths.sort(key=lambda x: len(x))    # 排序,最短的放前面打印
        print('\033[32m节点: %s 找到以下正向路径(输出类, 如: Line Out, HeadPhone), 共 %d 条路径, 仅显示路径长度小等于4的路径' %
              (self.name, len(paths)))
        for _ in paths:
            if len(_) <= 4:     # 仅显示路径长度小等于4的路径
                print(' → '.join(_))
        print('\033[0m')

    def __repr__(self):
        return '<Node %s>' % self.name


nodes = {}


def init_nodes():
    """
    读取codec.txt每行文本, 初始化节点对象, 并建立父子关系列表
        1. 找到有一个节点初始化一个节点对象放入 nodes 里, 如: {'0x14': <Node 0x14>}
           在每个节点段里面找到该节点连接的子节点信息, 并填充当前节点的子节点信息
        2. 所有节点都读取完毕, 遍历每一个节点, 根据该节点的子节点列表, 为每一个子节点列表中添加一个父节点(该节点自身)
    :return: None
    """
    try:
        f = open(sys.argv[1])
    except (IndexError, IOError):
        print('请提供正确的文件路径名!')
        f = None    # Bypass pycharm warning "Local variable 'f' might be referenced before assignment"
        exit(1)
    line = f.readline()
    current_node = None
    while line != '':
        match = re.match(r'Node (\w+) (\[.+\])', line, re.IGNORECASE)
        if match:
            current_node = Node(match.group(1).lower(), match.group(2))
            nodes[match.group(1)] = current_node
            print('Debug: 找到节点 %s %s' % (current_node.name, current_node.desc))
            line = f.readline()
            continue
        match = re.match(r'\s+Connection: \d+', line, re.IGNORECASE)
        if match:
            line = f.readline()
            if 'In-driver Connection' in line:  # HDMI codec
                line = f.readline()
            current_node.child_name_list = re.findall(r'\w+', line)
            print('\t\t节点 %s 下连接到以下节点 %s' % (current_node.name, ' '.join(current_node.child_name_list)))
            line = f.readline()
            continue
        line = f.readline()
    # 初始化各节点的子节点
    for node in nodes.values():
        for child_name in node.child_name_list:
            child_name = child_name.lower()
            child_node = nodes[child_name]
            node.child_obj_list.append(child_node)
            # 为子节点添加父节点
            child_node.add_parent(node)


init_nodes()
_name = input('\n\033[33m请输入节点名称, 如0x10\033[0m\n:').lower()
try:
    _node = nodes[_name]
    _node.get_parent_paths()
    _node.get_child_paths()
except KeyError:
    print('找不到节点: %s , 节点名称输入是否有误?' % _name)
    exit(1)

verbit.py

#!/bin/python3
"""
    HackintoshTools verbit.py
    ~~~~~
    @Description:
        整理提取到的codec.txt节点信息, 生成configdata
        参考: https://github.com/daliansky/Dell-Inspiron-7560-Hackintosh/blob/master/hda-tools/verbit.sh
    @CreateTime: 2019/12/16 8:15 下午
    @Author: leo
    @License: MIT
"""

import os
import sys
import re
import traceback


BASE_DIR = os.path.dirname(__file__)
CODEC_TXT = ' '.join(sys.argv[1:])

try:
    with open(CODEC_TXT, 'r') as f:
        content = f.read()
        NAME = re.findall(r'^Codec: (.+)$', content, re.MULTILINE)[0]
        ADDRESS = re.findall(r'Address: (\d+)$', content, re.MULTILINE)[0]
except IOError:
    print('ERROR: 打开codec文件失败, 路径: %s' % CODEC_TXT)
    exit(1)
except IndexError:
    print('ERROR: codec文件格式是否不正确?')
    exit(1)


class Node(object):
    __slots__ = ['Jack', 'Color', 'Description', 'Node', 'PinDefault', 'Verbs']

    def __getattr__(self, item):
        if item == 'Verbs':
            return '%s%s71c%s %s%s71d%s %s%s71e%s %s%s71f%s' % (ADDRESS, self.Node, self.PinDefault[-2:],
                                                                ADDRESS, self.Node, self.PinDefault[-4:-2],
                                                                ADDRESS, self.Node, self.PinDefault[-6:-4],
                                                                ADDRESS, self.Node, self.PinDefault[-8:-6])


def init_nodes(text: str) -> [Node]:
    """
    分析codec.txt, 提取有效节点信息(含有Jack, Color, ... 等信息的节点)
        a). 先将文本分割为以Node 0x...隔开的段, 并剔除不包含"Pin Default"的段
            Node 0x02 [Audio Output] wcaps 0x41d: Stereo Amp-Out
            ...
            ------------------------------------------------------
            Node  0x03 [Audio Output] wcaps 0x41d: Stereo Amp-Out
            ...
            ------------------------------------------------------
        b). 依次解析每个文本段, 并初始化Node对象, 放入一个列表中, 所有解析完毕, 返回该数组
    :param text: str , codec.txt 文本内容
    :return [Node<1>, Node<2>, ...]
    """
    # 先添加一个特殊前缀'----------', 再切割, 以防止切割后Node 0x丢失
    text = re.sub(r'^Node 0x', '----------Node 0x', text, flags=re.MULTILINE)
    node_texts = re.split(r'----------', text)
    node_texts = [_ for _ in node_texts if 'Pin Default ' in _]
    node_list = []
    for node_text in node_texts:
        node = Node()
        try:
            node.Node = re.findall(r'^Node 0x(\w+) ', node_text, re.MULTILINE)[0]
            node.Jack = re.findall(r'Conn = (.+),', node_text)[0]
            node.Color = re.findall(r'Color = (\w+)', node_text)[0]
            node.PinDefault, node.Description = re.findall(r'Pin Default (\w+): (.+)$', node_text, re.MULTILINE)[0]
            if '[N/A]' in node.Description:
                print('WARN: 忽略无效节点信息: Node 0x%s\n%s' % (node.Node, node.Description))
                continue
            print('INFO: 找到节点信息: Node 0x%s\n%s' % (node.Node, node.Description))
            node_list.append(node)
        except IndexError:
            print('ERROR: 搜索节点信息出错, 错误信息如下:\n%s\n 文本内容如下:\n%s' % (traceback.format_exc(), node_text))
    return node_list


nodes = init_nodes(content)
markdwon = '|%s|\n|%s\n' % ('|'.join(Node.__slots__), ':---:|' * len(Node.__slots__))
for _node in nodes:
    markdwon += '|%s|%s|%s|0x%s|%s|%s|\n' % (_node.Jack, _node.Color, _node.Description,
                                             _node.Node, _node.PinDefault, _node.Verbs)
print('\n\n\n%s' % markdwon)

相关标签: 脚本