当前位置: 首页 > news >正文

构建 Audio Unit 应用程序

构建 Audio Unit 应用程序

  • 构建 Audio Unit 应用程序
    • 从选择设计模式开始
      • I/O Pass Through
      • I/O Without a Render Callback Function
      • I/O with a Render Callback Function
      • Output-Only with a Render Callback Function
      • 其他设计模式
    • 构建应用程序
      • 配置 audio session
      • 指定 audio unit
      • 创建 audio processing graph
      • 配置 audio unit
      • 编写并绑定渲染回调函数
      • 连接 audio unit nodes
      • 提供用户界面
      • 初始化然后开启 audio processing graph
    • Debug 小技巧

构建 Audio Unit 应用程序

使用 Audio Unit 构建应用程序主要步骤是选择一个设计模式,然后编写代码来实现该模式。

从选择设计模式开始

在 iOS 应用程序中,Audio Unit 有很多基本设计模式。每种模式都有的共同特征:

  • 仅仅只有一个 I/O 单元。

  • 在整个 audio processing graph 中使用单一的音频流格式,尽管该格式可能存在变化。

  • 要求在特定位置设置流格式或部分流格式。

正确设置流格式对于建立音频数据流至关重要。这些模式大多依赖于音频单元连接提供的音频流格式从源到目的地的自动传播。合理利用这种传播的特性可以减少了编写和维护的代码量。同时,必须保证清楚了解每种模式需要如何进行设置。例如,必须在 iPod EQ 单元的输入和输出上设置完整的流格式。

在大多数情况下,设计模式都会使用 AUGraph。虽然可以在不使用 graph 的情况下实现这些模式中的任何一种,但使用 graph 可以简化代码并支持动态重新配置。

I/O Pass Through

I/O Pass Through 模式将传入的音频直接发送到输出硬件,没有处理音频数据的选项。虽然这没有什么实际价值,但基于这种模式构建 Audio Unit 应用程序是验证和巩固对 Audio Unit 概念的理解的好方法。图 2-1 说明了这种模式。

请添加图片描述

如图所示,音频输入硬件将其流格式强加在 Remote I/O unit 的 input element 的外向一侧。开发者需要指定要在此元素的内侧使用的格式, 音频单元内部将根据需要执行格式转换。为了避免不必要的采样率转换,在定义流格式时最好使用音频硬件的采样率。我们也不必指定 Remote I/O unit 的 output element 的流格式,因为流格式会通过连接从 input element 传给 output element。同理,传给硬件的流将会根据硬件需要完成一次自动转换。

I/O Without a Render Callback Function

可以在 Remote I/O unit 的元素之间添加一个或多个其他音频单元,例如,使用多通道混音器单元将传入的麦克风音频定位在立体声域中,或提供输出音量控制。在这个设计模式中,仍然没有渲染回调函数,如图 2-2 所示。这简化了模式,但限制了其效用。如果没有渲染回调函数,就无法直接操作音频。

请添加图片描述

在此模式中,可以像在 I/O Pass Through 模式中一样配置 Remote I/O unit 的两个元素。要设置多通道混音器单元,必须在混音器输出上设置流格式的采样率。混音器的输入流格式通过音频单元连接从 Remote I/O unit 的 input element 的输出中传播,自动建立。同样,Remote I/O unit 的 output element 输入范围的流格式由音频单元连接建立,这要归功于混音器单元输出的传播。

在这种模式的任何情况下,每当使用 I/O unit 以外的其他音频单元时,必须设置 kAudioUnitProperty_MaximumFramesPerSlice 属性。与 I/O Without a Render Callback Function 模式一样,无需配置任何音频数据缓冲区。

I/O with a Render Callback Function

通过在 Remote I/O unit 的输入和输出元素之间放置渲染回调函数,可以在传入音频到达输出硬件之前进行操作,例如:使用渲染回调函数来调整输出音量,还可以添加颤音、环调制、回声或其他效果。这种模式如图 2-3 所示。

请添加图片描述

如图所示,此模式使用 Remote I/O unit 的两个元素。将渲染回调函数附加到 output element 的input scope。当该元素需要另一组音频数据时,系统会触发回调。反过来,回调通过调用 Remote I/O unit 的 input element 的渲染回调函数来获得新的音频数据。

与其他 I/O 模式一样,您必须在 Remote I/O unit 上明确启用输入,因为默认情况下,输入是禁用的。而且无需配置任何音频数据缓冲区。

请注意,当使用渲染回调函数建立从一个音频单元到另一个音频单元的音频路径时,回调取代了音频单元连接。

Output-Only with a Render Callback Function

在最简单的情况下,这种模式涉及一个直接连接到 Remote I/O unit 的 output element 的 input scope 的渲染回调函数,如图 2-4 所示。

请添加图片描述

可以利用此模式完成复杂的音频结构。例如,将几个声音混合在一起,然后通过设备的输出硬件播放它们。图 2-5 显示了这种情况。

请添加图片描述

在图中,需要在 iPod EQ 的输入和输出上设置完整的流格式,多通道混音器只需要在其输出上设置正确的采样率。正如前面说到的,完整的音频流格式信息会在传递的过程中自动赋值。

对于每个多通道混合器单元输入,要设置完整的流格式。对于 input 0,需要显式设置它的流格式。对于 input 1,流格式由音频单元连接从 iPod EQ 单元的输出传播。一般来说,必须单独考虑每个音频单元的流格式需求。

其他设计模式

Audio Unit 还有另外两种主要设计模式:

  1. Input-only with a Render Callback Function:回调函数由应用程序调用,将音频数据传给 Remote I/O unit 的 input element。然而,在大多数情况下,对于这样的应用程序来说,更好的选择是使用输入音频队列对象(使用 AudioQueueNewInput 函数实例化的 AudioQueueRef 类型),使用音频队列对象提供了更大的灵活性,因为它的渲染回调功能不在实时线程上。

  2. Generic Output unit:离线音频处理。与 Remote I/O unit 不同,该音频单元无法连接到设备的音频硬件。当使用它向应用程序发送音频时,它仅仅取决于应用程序调用其渲染方法。

构建应用程序

无论选择哪种设计模式,构建 Audio Unit 应用程序的步骤基本相同:

  1. 配置 audio session。
  2. 指定 audio unit。
  3. 创建 audio processing graph,然后获取 audio unit。
  4. 配置 audio unit。
  5. 连接 audio unit nodes。
  6. 提供用户界面。
  7. 初始化然后开启 audio processing graph。

配置 audio session

构建 Audio Unit 应用程序的第一步与任何 iOS 音频应用程序的步骤相同:配置音频会话。音频会话是应用程序和硬件交互的中介,它的特征在很大程度上决定了应用程序的音频功能及其与系统其他部分的交互性。首先指定要在应用程序中使用的采样率,如下所示:

self.graphSampleRate = 44100.0; // 单位:赫兹

接下来,使用音频会话对象请求系统使用指定采样率作为设备硬件采样率。这里的目的是避免硬件和应用程序之间的采样率转换。这可以最大限度地提高 CPU 性能和音质,并最大限度地减少功耗。

NSError *audioSessionError = nil;
// 获取 audio session 单例对象
AVAudioSession *mySession = [AVAudioSession sharedInstance];
// 请求当前设备硬件使用的采样率
[mySession setPreferredHardwareSampleRate: graphSampleRateerror: &audioSessionError];
// 设置音频分类,AVAudioSessionCategoryPlayAndRecord 指的是支持音频输入与输出
[mySession setCategory: AVAudioSessionCategoryPlayAndRecorderror: &audioSessionError];
// 激活 audio session
[mySession setActive: YES error: &audioSessionError];
// 激活会话后更新采样率
self.graphSampleRate = [mySession currentHardwareSampleRate];

还可以配置其他硬件特征:音频硬件 I/O 缓冲区持续时间。采样率在 44.1kHz 的延时约为 23ms,相当于每次采集 1024 个采样点。如果 I/O 延迟在应用程序中至关重要,可以设置更短的 duration,低至约 0.005ms(相当于 256 个采样点),如下所示:

self.ioBufferDuration = 0.005;
[mySession setPreferredIOBufferDuration: ioBufferDurationerror: &audioSessionError];

有关如何配置和使用音频会话对象的完整说明,请参阅:Audio Session Programming Guide。

指定 audio unit

在运行时,配置音频会话后,应用程序尚未获取音频单元。可以使用 AudioComponentDescription 结构来得到一个指定的音频单元,然后根据音频单元说明符和选择的设计模式构建一个 audio processing graph。

创建 audio processing graph

在此步骤中,将创建设计模式的骨架。具体来说,有以下几步:

  1. 实例化 AUGraph 对象,该实例代表 audio processing graph。
  2. 实例化一个或多个 AUNode 对象,每个对象代表 graph 中的一个音频单元。
  3. 添加 nodes 到 graph。
  4. 打开 graph 并且实例化 audio units。
  5. 获得 audio units 引用。

下面代码显示了如何对包含 Remote I/O unit 和多通道混合器单元的 graph 执行这些步骤。假设已经为每个音频单元定义了 AudioComponentDescription 结构。

AUGraph processingGraph;
NewAUGraph (&processingGraph);AUNode ioNode;
AUNode mixerNode;// 已经为每个音频单元定义了 AudioComponentDescription 结构
AUGraphAddNode(processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode(processingGraph, &mixerDesc, &mixerNode);

AUGraphAddNode 函数调用使用音频单元说明符 ioUnitDesc 和 mixerDesc。此时,图形被实例化,并拥有您将在应用程序中使用的节点。要打开 graph 并实例化音频单元,请调用 AUGraphOpen:

AUGraphOpen (processingGraph);

然后,通过 AUGraphNodeInfo 函数获取对音频单元实例的引用,如下所示:

AudioUnit ioUnit;
AudioUnit mixerUnit;AUGraphNodeInfo(processingGraph, ioNode, NULL, &ioUnit);
AUGraphNodeInfo(processingGraph, mixerNode, NULL, &mixerUnit);

ioUnit 和 mixerUnit 变量现在保存对图形中音频单元实例的引用,允许对它们进行配置和互连音频单元。

配置 audio unit

每个 iOS 音频单元都需要自己的配置,这里介绍一些常见的配置:

  • 默认情况下,Remote I/O unit 启用输出并禁用输入。如果同时执行 I/O,或仅使用输入,必须相应地重新配置 I/O unit。

  • 除 Remote I/O unit 和 Voice-Processing I/O unit 外,所有 iOS 音频单元都需要配置其 kAudioUnitProperty_MaximumFramesPerSlice 属性。此属性确保音频单元准备好生成足够数量的音频数据帧,以响应渲染调用。

  • 所有音频单元都需要在输入、输出或两者上定义其音频流格式。

编写并绑定渲染回调函数

对于使用渲染回调函数的设计模式,必须编写这些函数,然后在正确的点添加它们。

可以在音频数据不流动时,使用 audio unit API 立即添加渲染回调:

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc        = &renderCallback;
callbackStruct.inputProcRefCon  = soundStructArray;AudioUnitSetProperty (myIOUnit,kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Input,0,                 // output element&callbackStruct,sizeof (callbackStruct)
);

也可以使用 audio processing graph API 以线程安全的方式附加渲染回调,即使在音频数据流动时:

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc        = &renderCallback;
callbackStruct.inputProcRefCon  = soundStructArray;AUGraphSetNodeInputCallback (processingGraph,myIONode,0,                 // output element&callbackStruct
);
// ... some time later
Boolean graphUpdated;
AUGraphUpdate (processingGraph, &graphUpdated);

连接 audio unit nodes

在大多数情况下,最好使用 audio processing graph API 中的 AUGraphConnectNodeInput 和 AUGraphDisconnectNodeInput 函数建立或断开音频单元之间的连接。这些函数是线程安全的,避免了显式定义连接的编码开销。

AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;AUGraphConnectNodeInput (processingGraph,mixerNode,           // source nodemixerUnitOutputBus,  // source node busiONode,              // destination nodeioUnitOutputElement  // desinatation node element
);

或者,可以使用音频单元属性机制直接建立和断开音频单元之间的连接。要做到这一点,请使用 AudioUnitSetProperty 函数以及 kAudioUnitProperty_MakeConnection 属性。这种方法要求为每个连接定义一个 AudioUnitConnection 结构,作为其属性值。

AudioUnitElement mixerUnitOutputBus  = 0;
AudioUnitElement ioUnitOutputElement = 0;AudioUnitConnection mixerOutToIoUnitIn;
mixerOutToIoUnitIn.sourceAudioUnit    = mixerUnitInstance;
mixerOutToIoUnitIn.sourceOutputNumber = mixerUnitOutputBus;
mixerOutToIoUnitIn.destInputNumber    = ioUnitOutputElement;AudioUnitSetProperty (ioUnitInstance,                     // connection destinationkAudioUnitProperty_MakeConnection,  // property keykAudioUnitScope_Input,              // destination scopeioUnitOutputElement,                // destination element&mixerOutToIoUnitIn,                // connection definitionsizeof (mixerOutToIoUnitIn)
);

提供用户界面

在许多情况下,需要提供一个用户界面,允许用户调整特定的音频单元参数,并在某些特殊情况下调整音频单元属性,比如:要更改 iPod EQ单元的活动均衡曲线,需要更改 kAudioUnitProperty_PresentPreset 属性的值。无论哪种情况,用户界面还应提供有关当前设置的视觉反馈。

初始化然后开启 audio processing graph

在开始音频流之前,必须通过调用 AUGraphInitialize 函数来初始化 audio processing graph。这个关键步骤:

  1. 通过为每个单元单独自动调用 AudioUnitInitialize 函数来初始化 graph 拥有的音频单元(如果要在不使用 graph 的情况下构建处理链,则必须依次显式初始化每个音频单元)。
  2. 验证 graph 的连接和音频数据流格式。
  3. 跨音频单元连接,传播流格式。
OSStatus result = AUGraphInitialize(processingGraph);
// Check for error. On successful initialization, start the graph...
AUGraphStart(processingGraph);
// Some time later
AUGraphStop(processingGraph);

Debug 小技巧

  1. 通过函数返回值可以检查调用是否成功。
  2. 注意函数调用之间的依赖性。例如,只有在成功初始化 audio processing graph 后,才能启动它。检查 AUGraphInitialize 的返回值。如果函数成功返回,就可以启动图表。如果失败了,利用 CAShow 函数将 graph 的状态打印到控制台。
  3. 确保您将每个 AudioStreamBasicDescription 结构初始化为 0,如下所示:AudioStreamBasicDescription stereoStreamFormat = {0};。将 ASBD 的字段初始化为 0 可确保没有字段包含垃圾数据(注意:作为类声明中的实例变量,其字段会自动初始化为 0,无需自己初始化它们)。
  4. 可以将 AudioStreamBasicDescription 结构的字段值打印到 Xcode 控制台,这在开发过程中非常有用。
    - (void) printASBD: (AudioStreamBasicDescription) asbd {char formatIDString[5];UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);bcopy(&formatID, formatIDString, 4);formatIDString[4] = '\0';NSLog (@"  Sample Rate:         %10.0f",  asbd.mSampleRate);NSLog (@"  Format ID:           %10s",    formatIDString);NSLog (@"  Format Flags:        %10X",    asbd.mFormatFlags);NSLog (@"  Bytes per Packet:    %10d",    asbd.mBytesPerPacket);NSLog (@"  Frames per Packet:   %10d",    asbd.mFramesPerPacket);NSLog (@"  Bytes per Frame:     %10d",    asbd.mBytesPerFrame);NSLog (@"  Channels per Frame:  %10d",    asbd.mChannelsPerFrame);NSLog (@"  Bits per Channel:    %10d",    asbd.mBitsPerChannel);
    }
    
    这种方法可以快速揭示 ASBD 中的问题。

相关文章:

构建 Audio Unit 应用程序

构建 Audio Unit 应用程序 构建 Audio Unit 应用程序从选择设计模式开始I/O Pass ThroughI/O Without a Render Callback FunctionI/O with a Render Callback FunctionOutput-Only with a Render Callback Function其他设计模式 构建应用程序配置 audio session指定 audio uni…...

JavaScript 实用技巧

1. 使用 const 和 let 替代 var 在 ES6 之前,我们通常使用 var 声明变量。但如今,推荐使用 const 和 let,因为它们具有块级作用域,可以避免很多潜在的问题。 const PI 3.14; // 常量,无法重新赋值 let age 25; // …...

Python协作运动机器人刚体力学解耦模型

🎯要点 🎯腿式或固定式机器人模型 | 🎯网格、点云和体素网格碰撞检测 | 🎯正反向运动学和动力学 | 🎯机器人刚体力学计算 | 🎯编辑参考系姿势和路径 | 🎯软件接口实体机器人模拟 | &#x1f3a…...

可重入锁思想,设计MQ迁移方案

如果你的MQ消息要从Kafka切换到RocketMQ且不停机,怎么做?在让这个MQ消息调用第三方发奖接口,但无幂等字段又怎么处理?今天小傅哥就给大家分享一个关于MQ消息在这样的场景中的处理手段。 这是一种比较特例的场景,需要保…...

Redis安装与使用

目录 1、介绍 1、redis的特点: 2、缓存 2、安装Redis 1、安装单机版redis 2、redis-cli命令参数 3、清空数据库的两种方式和作用域: 4、redis的增删查改命令 5、redis的查看所有分类命令 6、redis过期时间与控制键的行为 7、redis的相关工具 1、介绍 r…...

base64字符串空格问题

客户端使用的Content-Type为application/x-www-form-urlencoded时,字符串中出现了空格,base64解码时出错了,因为原来的字符有号, Spring Boot 对于Content-Type为application/x-www-form-urlencoded的HTTP请求,默认情…...

【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 同步与通信篇 -- 消息队列和邮箱处理 --(四)

💌 所属专栏:【BES2500x系列】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &#x1f49…...

电信NR零流量小区处理

【摘要】随着目前网络建设逐步完善,5G用户的不断发展,针对零流量小区的分析及处理存在着必要性,零流量小区的出现既是用户分布及行为的直观体现,也是发展用户的一个指引,同时也能发现设备的一些故障。一个站点的能够带…...

ArcTs布局入门03——层叠布局(Stack)

如果你也对鸿蒙开发感兴趣,加入“Harmony自习室”吧! 扫描下面的二维码关注公众号。 1、概述 叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。层叠布局通过Stack容器组件实…...

C语言之线程的学习

线程属于某一个进程 共同点:都能并发 线程共享变量,进程不共享。 多线程任务中,其中某一个线程调用了exit了,其他线程会跟着一起退出 如果是特定的线程就调用pthread_exit 失败返回的是错误号 下面也是...

HT8691 内置升压模块的D类音频功率放大器芯片IC

一般描述 HT8691是一款内置升压模块的D类音频功率放大器。内置的升压模块可通过外置电阻调节升压值,即使是锂电池供电,在升压至6.5V时,10%THDN,4Ω负载条件下能连续输出5.5W功率;升压至7V,3Ω负载条件下则能连续输出7.0W功率。其支持外部设置…...

和小红书一起参会! 了解大模型与大数据融合的技术趋势

在过去的两年中,“大模型”无疑成为互联网行业的焦点话题,曾经炙手可热的大数据架构似乎淡出公众视野。然而,大数据领域并未停滞不前,反而快速演进,传统依赖众多开源组件的大数据平台正逐步过渡到以融合与简化为核心特…...

【vocabulary in use (elementary)】7 Feeling

happy 高兴 sad 伤心 angry 生气 upset 丧气 cold 冷 hot 热 thirsty 口渴 hungry 饿 well 很好 ill 生病 tired 累了 surprised 惊讶 关于喜欢的表达: like to do 偶尔一次喜欢 like doing 一直喜欢的 outdoor activities 户外运动 be keep on doing 坚持做 be fo…...

Keil5 ST-LINK setting闪退问题解决

1. 官网下载新版驱动文件 MDK uVision crashes when using ST-Link debugger 2. 解压替换 STLinkUSBDriver6.1.2.0Signed 我的库文件目录: D:\Tool\Keil5\ARM\STLink...

熟练掌握Docker及linux常用命令排查线上问题。熟悉Git, Maven等项目管理及构建工具,熟悉微服务中基于Jenkins的CI/CD

掌握Docker、Linux命令、项目管理及构建工具,以及CI/CD流程是现代软件开发和运维的关键技能。以下是对这些技能的概述和一些实践建议: ### Docker - **概述**:Docker是一个开源的容器化平台,允许开发者打包应用及其依赖到一个可移…...

78.Vue 3 重用性模态框组件

模态框是大多数 Web 应用程序中的基本构建块。虽然最初实现起来可能看起来有点棘手,但实际上,使用 Vue 和一些 Flexbox 技巧,这不仅可行,而且非常简单。 让我们一起实现一个基础的模态框组件。 架构如下: AppModal.vue…...

《昇思25天学习打卡营第9天|onereal》

继续学习昨天的 基于MindNLPMusicGen生成自己的个性化音乐 生成音乐 MusicGen支持两种生成模式:贪心(greedy)和采样(sampling)。在实际执行过程中,采样模式得到的结果要显著优于贪心模式。因此我们默认启…...

Wireshark - tshark支持iptables提供数据包

tshark现在的数据包获取方式有两种,分别是读文件、网口监听(af-packet原始套接字)。两种方式在包获取上,都是通过读文件的形式;存在文件io操作,在专门处理大流量的情境下, 我们复用wireshark去做…...

快团团团长如何批量退款可自定义退款金额(批量退差价)?

快团团团长如何批量退款可自定义退款金额(批量退差价)? 在售后处理中,经常会出现需要给某一商品退差价的场景,因此在批量退款时需要自定义退款金额。现快团团已支持批量退自定义金额,操作方法和注意事项如…...

MySQL——事务ACID原则、脏读、不可重复读、幻读

什么是事务 要么都成功,要么都失败 一一一一一一一 1. SQL执行:A给B转账 A 1000 ---->200 B 200 2. SQL执行:B收到A的钱 A 800 B 400 一一一一一一一 将一组SQL放在一个批次中去执行~ 事务原则:ACI…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...