游戏引擎学习第九天

embedded/2024/11/16 22:00:14/

视频参考:https://www.bilibili.com/video/BV1ouUPYAErK/

修改之前的方波数据,改播放正弦波

在这里插入图片描述

下面主要讲关于浮点数

1. char(字符类型)

  • 大小:1 字节(8 位)
  • 表示方式char 存储的是一个字符的 ASCII 值。由于它是一个整数类型,它在内存中是以 二进制 形式存储的。
    • 有符号 char:表示的值范围通常是 -128127,其符号位决定了值的正负。
    • 无符号 char:范围从 0255

存储示例

  • char c = 'A'; 对应的 ASCII 值是 65,其二进制表示为 01000001

内存示意:

  字符 'A'(ASCII 65)十六进制: 0x41二进制:   01000001

3. int(整数类型)

  • 大小:4 字节(32 位)
  • 表示方式int 存储的是一个整数,采用 二进制补码 表示。
    • 有符号 int:范围通常为 -2,147,483,6482,147,483,647,最高位为符号位。
    • 无符号 int:表示的范围从 04,294,967,295

存储示例

  • int i = -123456; 使用 32 位二进制补码表示:
    • -123456 的二进制补码表示:11111111 11111110 00011101 11000000
      123456 0b00000000 00000001 11100010 01000000
      ~123456 0b11111111 11111110 00011101 10111111 取反
      ~123456+1 0b11111111 11111110 00011101 11000000 +1

在这里插入图片描述

内存示意:

  int i = -123456;二进制补码表示: 11111111 11111110 00011101 11000000十六进制表示: 0xFFFE1DC0

负数补码表示,按位取反+1

5. float(单精度浮点数)

  • 大小:4 字节(32 位)
  • 表示方式float 按照 IEEE 754 标准表示为 32 位浮点数,包括 符号位指数部分尾数(有效数字) 部分。表示方法如下:
    • 符号位(1 位):表示数字的正负。
    • 指数部分(8 位):表示数字的范围,通过偏移量调整。
    • 尾数部分(23 位):表示数字的精度。

表示为:
( − 1 ) 符号 × 1. 尾数 × 2 指数 − 127 (-1)^{\text{符号}} \times 1.\text{尾数} \times 2^{\text{指数}-127} (1)符号×1.尾数×2指数127

存储示例

  • float f = 3.14f; 的 IEEE 754 单精度表示:
    • 3.14 的二进制表示为:0 10000000 10010001111010111000011
#include <cstring>
#include <iostream>void printBinary(float num) {// 将 float 转换为 int(按字节复制)int bits;std::memcpy(&bits, &num, sizeof(bits));// 打印二进制表示for (int i = 31; i >= 0; --i) {std::cout << ((bits >> i) & 1); // 逐位打印if (i == 31 || i == 23)std::cout << " "; // 分隔符,用于区分符号位、指数位、尾数位}std::cout << std::endl;
}int main() {float num = 3.14f;std::cout << "Binary representation of " << num << " is: ";printBinary(num);return 0;
}

在这里插入图片描述

float 32 位

内存示意:

  float f = 3.14f;符号位:0指数部分:10000000尾数部分:10010001111010111000011十六进制表示: 0x4048F5C3

在这里插入图片描述

6. double(双精度浮点数)

在这里插入图片描述

  • 大小:8 字节(64 位)
  • 表示方式double 也按照 IEEE 754 标准表示,但它使用 64 位,包括 符号位指数部分尾数部分。它的精度比 float 更高,具体结构如下:
    • 符号位(1 位)
    • 指数部分(11 位)
    • 尾数部分(52 位)

表示为:
( − 1 ) 符号 × 1. 尾数 × 2 指数 − 1023 (-1)^{\text{符号}} \times 1.\text{尾数} \times 2^{\text{指数}-1023} (1)符号×1.尾数×2指数1023

存储示例

  • double d = 3.141592653589793; 的 IEEE 754 双精度表示:
    • 3.141592653589793 的二进制表示为:0 10000000000 1001001000011111101101010100010001000010110100011000

内存示意:

  double d = 3.141592653589793;符号位:0指数部分:10000000000尾数部分:1001001000011111101101010100010001000010110100011000

double类似只是表示的位数更多了

继续上面修改写入正弦数据

在这里插入图片描述

示例:

假设采样率为 44100 Hz(即每秒采样 44100 个样本),频率为 440 Hz(比如这是标准 A 音符的频率)。

  • 每秒钟你会采集 44100 个样本。
  • 每秒钟会有 440 个周期。

那么一个周期的持续时间(即波周期)就是:

W a v e P e r i o d = 44100 440 ≈ 100.23 毫秒 WavePeriod = \frac{44100}{440} \approx 100.23 \text{ 毫秒} WavePeriod=44044100100.23 毫秒

这意味着,每一个周期(即波的一个完整振荡)需要约 100.23 毫秒的时间。

  • 波周期(WavePeriod) 是波形完成一个周期所需的时间,单位是秒(s)。
  • 采样率(S) 是每秒钟采样多少个点,单位是 样本/秒(Hz)
  • 频率(f) 是波形每秒钟振荡多少次,单位是 赫兹(Hz)

波周期 = 采样率 / 频率 表示了 一个周期占用的时间,即波形完成一个振荡所需要的时间,反映了波形的 频率与采样细节的关系

在这里插入图片描述

优化一下代码把声音写缓存相关代码提出来

在这里插入图片描述

vs watch技巧

在这里插入图片描述

添加模拟手柄改变音频调试

在这里插入图片描述

模拟手柄软件

因为我没有手柄只能用模拟器进行调试
游戏手柄模拟器Gaming Keyboard Splitter
可以到这个完整下载对应的软件Gaming Keyboard Splitter
https://softlookup.com/download.asp?id=280311

我已经传到CSDN
https://download.csdn.net/download/TM1695648164/89982708

软件第一次运行会安装驱动会重启电脑

  1. debug运行程序
  2. 打开模拟手柄软件Gaming Keyboard Splitter
    在这里插入图片描述

在这里插入图片描述

上面测试会听到撕裂的声音切换时

“相位跳变”是指在切换频率时,波形的相位发生了不连续的变化,导致声音出现不自然的突变。为了理解这一点,我们需要从正弦波的相位频率切换时的平滑性角度来分析。

1. 什么是相位?

在波形中,相位指的是波形在某个时间点的状态,通常用一个角度来表示。例如,对于正弦波 sin(θ),其中 θ 表示相位,θ 通常是通过时间、频率和波速来计算的。

对于音频信号,波形的相位描述的是波形的位置(比如峰值、零交点等),以及波形如何在时间轴上前进。不同频率的正弦波具有不同的波长(周期),而相位描述的是这些波形的“开始位置”。

2. 什么是相位跳变?

相位跳变是指在两个连续的波形之间,相位发生了突然变化。当你在播放音频时切换频率时,如果没有处理好相位,就可能会导致新的波形与旧的波形在相位上的不对齐。

  • 相位对齐:指的是两个不同频率的波形在切换时,其相位(起始位置)要相同,或者在一个平滑的过渡过程中对齐。相位对齐确保了波形的过渡是平滑的,没有断裂感。

  • 相位跳变:如果你在切换频率时,新的波形从一个不对齐的相位开始(比如,原本的波形处于一个正半周期,而新的波形从零交点开始),那么这两个波形之间会出现不连续性。这种不连续性会导致音频信号的突变或毛刺声。

3. 为什么频率切换会导致相位跳变?

在频率切换时,如果频率的变化过于突兀,那么相位可能会发生跳变。例如:

  • 假设你正在播放一个频率为 256Hz 的正弦波,这意味着每秒钟播放 256 个完整的波形周期。如果你突然切换到 512Hz 的频率,意味着每秒钟播放的周期数是原来的两倍。如果没有对相位进行平滑过渡,新的频率波形的起始位置可能会不一致(比如一个正弦波的峰值和下一个波形的零交点不对齐),导致两个波形之间的过渡不连贯。

  • 当波形的相位不一致时,你就会听到一种不自然的“呲”声或毛刺声,因为音频信号的突然变化让耳朵感受到强烈的突变。

4. 如何理解和避免相位跳变?

  • 平滑过渡:为了避免相位跳变,最常见的做法是在切换频率时,确保新的频率从平滑过渡的相位开始。这意味着你可以在切换频率之前记录当前频率的相位,并确保新的频率从当前的相位状态开始播放。

  • 相位对齐:如果频率变化较大(比如从 256Hz 跳到 512Hz),你可以通过计算当前的相位并将其对齐到新频率来避免跳变。这样,新的波形从一个平滑的相位开始,不会有突然的相位变化。

  • 渐变过渡:另一种方法是通过渐变的方式逐步调整频率,而不是直接跳到新的频率。通过线性插值或其他平滑过渡方法,可以在一段时间内平滑地过渡到目标频率,从而避免突如其来的波形跳跃。

5. 示例:

假设你有一个频率为 256Hz 的正弦波:

y = sin(2π * 256 * t)

在某一时刻,t = 0 时,sin(0) = 0t = 1/256 时,sin(2π) = 0,等等。

如果你突然从 256Hz 切换到 512Hz:

y = sin(2π * 512 * t)

这时,相位的变化会更加快速。假如没有正确处理相位,那么切换时,新的频率可能从一个不连续的位置开始。例如,之前的正弦波刚刚完成一个周期,而新的频率波形从零交点开始,从而导致两个波形之间的断裂感(即“呲”声)。

6. 避免相位跳变的方法:

  • 记录当前的相位:在频率切换时,记录下当前时刻的相位,然后确保新的频率从该相位继续播放。例如,如果你在 t = 0.1s 时切换频率,可以将当前的相位(例如 2π * 256 * 0.1)保存下来,切换到新的频率时,新的波形从相同的相位位置继续计算。

  • 平滑过渡:在频率变化的过程中,使用渐变方式逐步过渡到目标频率,这样可以避免相位的突变。例如,可以在每个采样周期调整频率增量,而不是直接跳到新频率。

通过这些方法,可以避免频率切换时产生不自然的“呲”声或毛刺声。

在这里插入图片描述

为了解决这个问题
在这里插入图片描述

下面用模拟手柄 StickY 对 上面代码进行测试

基于 AButton 按钮的操作来改变音频频率(ToneHz)和音频波形的周期(WavePeriod),并可能结合了控制器的 Y 轴摇杆 (StickY) 来微调频率。

更新 ToneHz(音频频率)

 // 获取摇杆的 X 和 Y 坐标值(-32768 到 32767)int16 StickX = Pad->sThumbLX;int16 StickY = Pad->sThumbLY;// 根据摇杆的 Y 坐标值调整音调和声音xOffset += StickX >> 12;yOffset += StickY >> 12;// 更新音调频率 (ToneHz),通过摇杆的 Y 值来调节// 这里是将 StickY 映射到频率范围内,使得频率与摇杆的上下运动相关。// 512 是基准频率,StickY 值影响音频频率的变化范围。SoundOutput.ToneHz =512 + (int)(256.0f * ((real32)StickY / 30000.0f));// 计算波周期,基于频率,决定波形的周期SoundOutput.WavePeriod =SoundOutput.SamplesPerSecond / SoundOutput.ToneHz;std::cout << "ToneHz " << SoundOutput.ToneHz << " sThumbLY "<< StickY << std::endl; // 输出音调频率和摇杆值
  • SoundOutput.ToneHz = 512 + (int)(256.0f * ((real32)StickY / 30000.0f));

    • 这行代码根据摇杆的 Y 值 (StickY) 动态计算音频的频率 ToneHz
    • StickY 的值范围是 -32768 到 32767(即摇杆的 Y 轴范围)。将它除以 30000.0f 来归一化到[-1, 1],然后乘以 256.0f 来决定音频频率变化的幅度。
    • 512 是基准频率,通过摇杆的上下动作来改变频率。
  • SoundOutput.WavePeriod = SoundOutput.SamplesPerSecond / SoundOutput.ToneHz;

    • 这行代码计算音频波的周期。波周期是根据音频的采样率 (SamplesPerSecond) 和音调频率 (ToneHz) 来决定的。波周期越短,频率越高,声音听起来越尖锐。

在这里插入图片描述

上面的测试中会出现延时的现象,键盘按下到音频改变有延时

在这里插入图片描述


http://www.ppmy.cn/embedded/138110.html

相关文章

【Docker】Mac安装Docker Desktop导致磁盘剩余空间较少问题如何解决?

目录 一、背景描述 二、解决办法 三、清理效果 四、理论参考 解决方法 1. 清理未使用的 Docker 镜像、容器和卷 2. 查看 Docker 使用的磁盘空间 3. 调整 Docker 的存储位置 4. 增加磁盘空间 5. 调整 Docker Desktop 配置 6. 使用 Docker 清理工具&#xff08;例如 D…

PC上浏览器是如何查询DNS 缓存的呢?

通过 ipconfig /displaydns 的显示结果可以获取本机的 DNS 缓存信息&#xff0c;那么浏览器是如何获取本机的 DNS 缓存。 答案是&#xff1a;浏览器获取本机的 DNS 缓存主要是通过操作系统提供的接口来获取&#xff0c;。 具体的获取途径如下&#xff1a; 先查询自身缓存&am…

【鸿蒙开发】第十五章 H5与端侧交互、Cookies以及Web调试

目录 1. H5与端侧交互 1.1 应用侧调用前端页面函数 1.2 前端页面调用应用侧函数 1.2.1 复杂类型使用方法 1.3 建立应用侧与前端页面数据通道 2 管理页面跳转及浏览记录导航 2.1 历史记录导航 2.2 页面跳转 2.3 跨应用跳转 3 管理Cookie及数据存储 3.1 Cookie管理 3…

路漫漫其修远兮,吾将上下而求索---第一次使用github的过程记录和个人感受

文章目录 1.仓库位置2.新建仓库3.配置仓库4.克隆和上传5.推荐文章和我的感受 1.仓库位置 这个仓库的位置就是在我们的这个个人主页的右上角&#xff1b;如果是第一次注册账号的话&#xff0c;这个主页里面肯定是不存在仓库的&#xff0c;需要我们自己手动的进行创建&#xff1…

Rust 建造者模式

在DDD中&#xff0c;DTO(数据传输对象)->BO(业务对象)、BO(业务对象)->PO(持久化对象&#xff0c;有的叫DO&#xff0c;即和数据表映射的实体)等等情况要做转换&#xff0c;这里提供以下转换方式 1、from或者try_from trait实现对象转换 需要转换对象满足接收对象的所有…

Python酷库之旅-第三方库Pandas(218)

目录 一、用法精讲 1021、pandas.DatetimeIndex.inferred_freq属性 1021-1、语法 1021-2、参数 1021-3、功能 1021-4、返回值 1021-5、说明 1021-6、用法 1021-6-1、数据准备 1021-6-2、代码示例 1021-6-3、结果输出 1022、pandas.DatetimeIndex.indexer_at_time方…

Android笔记(三十七):封装一个RecyclerView Item曝光工具——用于埋点上报

背景 项目中首页列表页需要统计每个item的曝光情况&#xff0c;给产品运营提供数据报表分析用户行为&#xff0c;于是封装了一个通用的列表Item曝光工具&#xff0c;方便曝光埋点上报 源码分析 核心就是监听RecyclerView的滚动&#xff0c;在滚动状态为SCROLL_STATE_IDLE的时…

HTML5实现俄罗斯方块小游戏

文章目录 1.设计来源1.1 主界面1.2 皮肤风格1.2 游戏中界面1.3 游戏结束界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/143788449 HTML5实现俄罗斯方块小游戏&#x…