游戏引擎学习第19天
介绍这段内容描述了开发者在进行游戏开发时,对于音频同步和平台层的明白和调解的过程。以下是更具体的复述:
开发者表达了他渴望今天继承进行的工作内容。他提到,昨天他讲解了一些关于音频的内容,今天他想稍微深入讲解一下他正在做的工作。他承认,当讨论音频同步等概念时,每每会让人感到困惑,尤其是这些概念在代码中表现出来时,明白起来非常复杂。固然他通过绘制图表(像昨天做的那样)能较为清晰地表达这些问题,但当转向代码时,问题就变得复杂,因为代码量大且涉及内容广泛。
开发者指出,固然他们在课程中已经通过记录大量代码(大约一千行)来保持平台层的简便性,但这并不意味着这部分代码很少,现实被骗你走到这一步时,你会发现有很多细节需要添补,整个过程有时可能会变得令人困惑,尤其是在将全部这些细节与图表或时间同步的思索结合起来时。
他强调,固然现在的工作只是原型制作层,并不是终极的正式平台层,所以他们并不需要解决全部的bug,也不需要寻求百分百的美满。然而,他们的目标是将平台层打造成一个良好的原型制作基础,这样做可以确保在未来的开发中,平台层能够有效支持起整个游戏的运行。
开发者还提到,即便这个原型层还不美满,它的构建应该能与未来的全功率平台层相似,且能够为未来的开发和调试提供有力的支持。总的来说,他的目标是创建一个有效的原型层,以便在此基础上继承构建更完整、功能更强大的平台层,而无需担心美满的解决全部问题。
音频同步对话开始(总结)
使用 DirectSound 技术来处理音频。具体来说,他们正在处理每秒 48,000 个音频样本。每个样本包罗左声道和右声道的振幅值。简而言之,这些样本代表了在某一时刻的音频振幅。通过这些样本,音频信号可以被重新构建,并通过扬声器播放出来。
48,000 个样本的收集和处理是为了重建通过扬声器传出的音频波形。固然在音频信号传递到扬声器的过程中还会有其他很多处理步骤,但基本的目标是通过这些样原来尽可能准确地还原声音波形。
为什么音频同步是一个问题
音频同步的问题,现实上是由于多个原因造成的,首先,从技术层面来看,完全同步音频和视频是不可能的。这里提到的延迟是由于各种因素,包罗硬件和电路设计等。比如,即使我们保证了音频和视频信号通过HDMI线输出并同步,在信号进入接收设备后,音频和视频信号可能会被分别处理:音频会传送到扬声器,而视频会传送到电视。这些处理可能因为差别电路的响应时间差异导致音频和视频的同步出现问题。
别的,现代硬件的局限性也使得完全同步成为不现实的目标,尽管我们渴望它能美满实现。换句话说,固然无法做到完全的同步,但我们的目标是确保在音频同步方面不会造成严峻的问题。我们并不寻求美满的同步,而是努力避免在我们自己的体系中制造过多的同步问题。我们渴望通过控制音频和视频信号的输出机遇,尽量避免出现严峻的同步偏差,尤其是在信号传输到终极输出设备之前,尽可能控制好输出的时间。
总的来说,音频同步问题是由多种因素导致的,现代硬件的限制使得完全同步不可能实现,而我们要做的工作是尽量淘汰同步问题的严峻性,并保证体系输出的音频和视频信号能够尽量协调。
尽量淘汰音频同步的问题
为了尽量淘汰音频同步的问题,首先,我们的目标是确保音频数据能够在正确的时间点被播放出来。具体来说,我们渴望音频数据能够与我们渴望它们播放的时间帧(frame)对齐,这意味着我们需要确保音频在该帧的对应时间段内被播放出来。
然而,现实情况是,由于硬件和软件的限制,我们在同步音频和视频时的本领非常有限,尤其是在某些平台上,比如Windows的DirectSound。这是我们当前使用的音频播放接口。为了尽可能淘汰同步问题,我们需要使用DirectSound中的一个功能,叫做“get current position”(获取当前播放位置)。这个功能的作用是答应我们从DirectSound中获取当前正在播放的音频的位置,也就是当前音频在硬件上的播放进度。
通过这个功能,我们可以获取一个被称为“播放光标”(play cursor)的值,它告诉我们音频当前播放的位置。这样,我们就能相识音频信号的具体播放进度,从而帮助我们对音频的输出进行更准确的控制。
尽管如此,遗憾的是,这种方法的效果依然受到平台的限制,尤其是在某些硬件和软件情况下,我们的控制本领非常有限,无法做到完全精准的同步。因此,固然我们可以通过“get current position”来获取当前播放的位置并实验调解音频输出的机遇,但仍然存在肯定的同步误差。
为了更好地调试这个问题,接下来我将使用Windows平台上的工具进行调试,并绘制一个调试图来帮助更好地明白和分析这个过程。
代码目标
展示播放光标(playback cursor)相对于音频缓冲区的运行情况。这个调试工具的重要目标是帮助可视化播放光标在差别时间点的位置,从而更好地分析息争决音频同步问题。
完成音频同步的debug代码
从你的描述来看,这段代码或逻辑好像重要关注音频同步的调试和可视化工作,尤其是如何将音频缓冲区中的播放光标位置映射到屏幕上显示的一维像素范围中。以下是具体复述:
1. 目标描述
[*]音频同步调试:当前的目标是调试音频同步代码。
[*]光标索引与可视化:需要将音频缓冲区的播放光标索引逐一绘制到窗口上,以便检察和分析具体的播放进度。
2. 基本概念
[*]次级缓冲区大小:指音频缓冲区的总大小,通常以字节为单元。
[*]每样本字节数:音频数据中每个样本的大小,用于从总字节数盘算样本数目。
[*]播放光标:音频播放的当前位置,以字节为单元。
3. 映射逻辑
[*]宽度与次级缓冲区的映射:
[*]缓冲区大小定义了光标的可能范围。
[*]窗口的宽度定义了显示的范围,需要将光标位置映射到像素范围内。
[*]比例系数:
[*]盘算映射的比例因子,用以将缓冲区中的字节位置换算成屏幕像素位置。
[*]公式基于“像素/缓冲区字节数”,以确保单元一致性。
4. 实现细节
[*]绘制逻辑:
[*]遍历缓冲区中的光标索引,将每个位置绘制为小矩形(或其他形状)。
[*]为确保完整显示,在窗口左右各留出16个像素作为边距。
[*]通过浮点数乘法盘算光标对应的像素坐标。
[*]量纲分析:
[*]通过分析单元(如字节、像素)进行验证,确保映射的正确性。
[*]单元变换中“字节”被抵消,仅剩目标单元“像素”。
5. 可视化参数调解
[*]窗口范围:
[*]根据现实需要,将显树模围限制在缓冲区的某一部分。
[*]为避免边界问题,对映射范围进行微调。
6. 后续改进
[*]代码进一步优化方向:
[*]动态调解缓冲区与窗口范围之间的映射,以支持差别分辨率和缓冲区大小。
[*]增长更多可视化工具,比方实时更新的播放位置标记。
通过上述步骤和逻辑,你应该能够在窗口中直观地看到音频播放的现实进度,并通过调解参数优化显示效果。
解释和调试调试代码
看得出来,你正在 debug 一段代码,目标是分析息争决绘制图形时出现的问题。以下是你提供的内容中的一些重要步骤和改进思路,帮助更好地明白 debug 的过程:
问题描述
你正在处理的是一个简单的像素绘制程序,目标是绘制垂直线。程序的问题重要是由于内存访问不正确或指针盘算出错导致的崩溃。具体表现为,当到达某个位置时,程序崩溃。
Debug 过程分解
1. 开端验证代码逻辑
[*]你通过循环访问保存的光标位置,并实验在滚动缓冲区中绘制图形。
[*]代码的重要逻辑是:调解像素指针的位置(通过音高 pitch 和像素的 X/Y 坐标),然后写入颜色值。
2. 观察变量和盘算效果
[*]验证像素指针和后缓冲区指针的值。
[*]盘算内存大小时,将后缓冲区的起始指针与当前像素指针进行比较。
[*]检查音高是否为负,或者在某些情况下被误用。
3. 确定崩溃原因
[*]崩溃的原因可能是指针越界:每次盘算可能错误地导致指针移动“过远”。
[*]验证是否有意外的加减操纵,比方,错误的步进逻辑导致每次循环后的偏移量超出预期。
改进与验证建议
1. 变量范围检查
确保像素指针不会超出缓冲区范围:
if (pixelPointer < backBufferMemory || pixelPointer >= backBufferMemory + backBufferSize) {
// 打印日志,确认超出范围的指针值
std::cerr << "Pixel pointer out of bounds!" << std::endl;
return; // 或者抛出异常
}
2. 渐渐排查音高盘算问题
检查音高值是否符合预期,比方:
[*]打印音高值:std::cout << "Pitch: " << pitch << std::endl;
[*]验证音高值是否为负值或者意外的非整数值。
3. 定位崩溃点
在崩溃前插入断点或者日记输出:
if (y == 123) { // 崩溃点之前的值
std::cout << "Y at crash: " << y << std::endl;
// 进一步输出 pixelPointer 和相关内存地址
}
4. 确保内存对齐
假如程序崩溃与步进操纵相关,可能涉及对齐问题。比方,音高或像素步进没有正确对齐到字节边界。
5. 分步测试
不妨将代码拆分为更小的单元进行测试。比方,仅测试 X 坐标的指针步进,确保没有误差;然后测试 Y 坐标移动。
调试心得
[*]关注核心变量:特别是音高、像素步进和内存边界。
[*]渐渐简化问题:在小范围内验证每个盘算的正确性。
[*]善用日记和断点:相识代码的现实行为而非仅依靠假设。
https://i-blog.csdnimg.cn/direct/bd5e04b9525446e5beb177be199d8dc3.png#pic_center
在这段代码中,OutputDebugStringA 的输出显示了 PlayCursor 和 WriteCursor 的值,它们对应音频缓冲区的当前播放位置和写入位置。每次循环打印一行,分别是:
[*]PC 表现 播放光标 (PlayCursor) 的位置。
[*]WC 表现 写入光标 (WriteCursor) 的位置。
比方,第一行显示:
PC:30720,WC:36480
这表现当前 PlayCursor 是 30720,而 WriteCursor 是 36480。
打印之间的差解释
打印的差异重要是为了观察:
[*]PlayCursor 的厘革:表现音频播放的渴望。
[*]WriteCursor 的厘革:表现音频数据的写入渴望。
[*]两者之间的差值是否稳固:播放和写入需要肯定的缓冲区差异,避免写入太靠近播放位置而造成“音频爆音”或延迟。
以输出为例:
PC:30720,WC:36480
PC:32640,WC:38400 厘革:
[*]PlayCursor 从 30720 到 32640,差为 32640 - 30720 = 1920。
[*]WriteCursor 从 36480 到 38400,差为 38400 - 36480 = 1920。
下一行:
PC:34560,WC:40320
[*]PlayCursor 从 32640 到 34560,差为 34560 - 32640 = 1920。
[*]WriteCursor 从 38400 到 40320,差为 40320 - 38400 = 1920。
差值代表的意义
[*]稳固的差值 表现缓冲区的数据添补和消费是同步的,体系正常工作。
[*]差值太小 表现缓冲区可能耗尽(导致音频停止)。
[*]差值太大 表现缓冲区可能过满(导致写入滞后)。
这些打印可以用来实时调试音频引擎,确保光标间距保持在安全范围。
通过这些方法,你可以渐渐缩小问题的范围,并终极找到导致崩溃的根本原因。假如有具体代码片断需要改进,可以随时提供,我可以进一步优化!s
在调试代码中发现 bug
#define MonitorRefreshHz 60
#define GameUpdateHz ((MonitorRefreshHz) / 2)
real32 TargetSecondsPerFrame = 1.0f / (real32)GameUpdateHz;
SoundOutput.RunningSampleIndex = 0; // 样本索引
SoundOutput.SamplesPerSecond = 48000; // 采样率:每秒采样48000次
SoundOutput.BytesPerSample = sizeof(int16) * 2;// 一个样本的大小
SoundOutput.SecondaryBufferSize =
SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample;// 缓冲区大小
SoundOutput.LatencySampleCount =
FramesOfAudioLatency * (SoundOutput.SamplesPerSecond / GameUpdateHz);
ByteToLock =((SoundOutput.RunningSampleIndex * SoundOutput.BytesPerSample) %
SoundOutput.SecondaryBufferSize);
TargetCursor = (LastPlayCursor + (SoundOutput.LatencySampleCount *
SoundOutput.BytesPerSample)) %SoundOutput.SecondaryBufferSize;
DWORD PlayCursor;
DWORD WriteCursor;
GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor);
char TextBuffer;
_snprintf_s(TextBuffer, sizeof(TextBuffer), //
"LPC:%u,BTL:%u,TC%u,BTW:%u - PC:%u WC:%u\n",//
LastPlayCursor, //
ByteToLock, //
TargetCursor, //
BytesToWrite, //
PlayCursor, //
WriteCursor //
);
OutputDebugStringA(TextBuffer);
输出的结果是
LPC:上一帧的PlayCursor
BTL:写的位置
TC:目标写入位置
BTW:需要写入的字节数
PC:92160 当前的PlayCursor
WC:97920 WriteCursor
输出打印的结果
LPC:105600,BTL:112640,TC118400,BTW:5760 - PC:107520 WC:113280
LPC:113280,BTL:118400,TC126080,BTW:7680 - PC:113280 WC:119040
LPC:120960,BTL:126080,TC133760,BTW:7680 - PC:120960 WC:126720
LPC:126720,BTL:133760,TC139520,BTW:5760 - PC:126720 WC:132480
LPC:134400,BTL:139520,TC147200,BTW:7680 - PC:134400 WC:140160
LPC:140160,BTL:147200,TC152960,BTW:5760 - PC:140160 WC:145920
LPC:147840,BTL:152960,TC160640,BTW:7680 - PC:147840 WC:153600
LPC:153600,BTL:160640,TC166400,BTW:5760 - PC:155520 WC:161280
LPC:161280,BTL:166400,TC174080,BTW:7680 - PC:161280 WC:167040
LPC:167040,BTL:174080,TC179840,BTW:5760 - PC:167040 WC:172800
LPC:174720,BTL:179840,TC187520,BTW:7680 - PC:174720 WC:180480
怎么算出来FramesOfAudioLatency是多少合适 上面FramesOfAudioLatency 有问题
要解决音频断续问题并找到合适的 FramesOfAudioLatency,需要明白并调试以下几个关键点:
关键概念
[*] FramesOfAudioLatency:
[*]表现音频延迟的帧数。延迟过小可能导致播放时数据不敷,从而断续;延迟过大则会引入显着的延迟,影响实时性。
[*]通常由以下公式盘算:
L a t e n c y S a m p l e C o u n t = F r a m e s O f A u d i o L a t e n c y × ( S a m p l e s P e r S e c o n d G a m e U p d a t e H z ) LatencySampleCount = FramesOfAudioLatency \times \left(\frac{SamplesPerSecond}{GameUpdateHz}\right) LatencySampleCount=FramesOfAudioLatency×(GameUpdateHzSamplesPerSecond)
[*] PlayCursor 和 WriteCursor:
[*]PlayCursor 是播放位置指针,表现当前播放到音频缓冲区的哪个字节。
[*]WriteCursor 是写入位置指针,表现音频设备答应你写入数据的起始位置。
[*]写入的数据应当保持与播放的隔断为 LatencySampleCount,否则可能出现数据不敷或覆盖征象。
[*] ByteToLock 和 TargetCursor:
[*]ByteToLock 表现在音频缓冲区中,当前帧需要开始写入的字节位置。
[*]TargetCursor 是渴望写入的目标字节位置,通常应领先 PlayCursor 一个 LatencySampleCount。
[*] BytesToWrite:
[*]写入的字节数盘算为缓冲区中需要添补的地域大小。
当前代码的埋伏问题
根据打印效果,音频可能断续的原因是 FramesOfAudioLatency 不敷,导致写入位置和播放位置之间的隔断过近,出现播放数据不敷(数据覆盖可能性较小)。可以通过调解 FramesOfAudioLatency 来增长写入和播放之间的缓冲区隔断。
盘算 FramesOfAudioLatency
理论盘算
[*]根据游戏的更新频率 GameUpdateHz 和音频采样率 SamplesPerSecond,每帧产生的音频样本数为:
S a m p l e s P e r F r a m e = S a m p l e s P e r S e c o n d G a m e U p d a t e H z SamplesPerFrame = \frac{SamplesPerSecond}{GameUpdateHz} SamplesPerFrame=GameUpdateHzSamplesPerSecond
[*]抱负的 LatencySampleCount 应保证写入和播放之间的隔断大于音频播放设备的最小缓冲要求,同时留有肯定裕量:
L a t e n c y S a m p l e C o u n t = F r a m e s O f A u d i o L a t e n c y × S a m p l e s P e r F r a m e LatencySampleCount = FramesOfAudioLatency \times SamplesPerFrame LatencySampleCount=FramesOfAudioLatency×SamplesPerFrame
调试和调解
[*] 监控缓冲区状态:
[*]打印 PlayCursor 和 WriteCursor 的实时差值:
C u r s o r D i s t a n c e = ( W r i t e C u r s o r − P l a y C u r s o r + S e c o n d a r y B u f f e r S i z e ) % S e c o n d a r y B u f f e r S i z e CursorDistance = (WriteCursor - PlayCursor + SecondaryBufferSize) \% SecondaryBufferSize CursorDistance=(WriteCursor−PlayCursor+SecondaryBufferSize)%SecondaryBufferSize
[*]确保 CursorDistance 在公道范围(比如 LatencySampleCount)内。
[*] 选择初始值:
[*]设置初始 FramesOfAudioLatency 为 3(通常公道出发点),盘算对应的 LatencySampleCount:FramesOfAudioLatency = 3;
LatencySampleCount = FramesOfAudioLatency * (SamplesPerSecond / GameUpdateHz);
[*]检查是否仍有断续问题。
[*] 渐渐调解:
[*]假如断续,渐渐增长 FramesOfAudioLatency 的值,直到缓冲区内 CursorDistance 在目标范围内稳固。
示例代码调解
在调试时,可以打印一些关键信息来帮助分析:
DWORD CursorDistance = (WriteCursor - PlayCursor + SoundOutput.SecondaryBufferSize) %
SoundOutput.SecondaryBufferSize;
_snprintf_s(TextBuffer, sizeof(TextBuffer),
"LPC:%u,BTL:%u,TC:%u,BTW:%u - PC:%u WC:%u Distance:%u\n",
LastPlayCursor, ByteToLock, TargetCursor, BytesToWrite, PlayCursor, WriteCursor,
CursorDistance);
OutputDebugStringA(TextBuffer);
调试效果预期
正常情况下:
[*]CursorDistance 应该稳固在一个范围(比如 LatencySampleCount)。
[*]BytesToWrite 应该小于缓冲区的剩余大小(SecondaryBufferSize - CursorDistance)。
问题情况下:
[*]假如 CursorDistance 小于 LatencySampleCount,阐明延迟不敷,可以增大 FramesOfAudioLatency。
[*]假如 CursorDistance 大于缓冲区大小,阐明延迟过大,需要减小 FramesOfAudioLatency。
通过以上方法,你可以渐渐找到合适的 FramesOfAudioLatency,确保音频播放平稳无断续。
https://i-blog.csdnimg.cn/direct/2519ca6aad9c45b69596a939ed5a518f.png#pic_center
https://i-blog.csdnimg.cn/direct/2a62e716463a4a4d936996e393e20841.png#pic_center
https://i-blog.csdnimg.cn/direct/84e0f02bee19465f9f700868d3cea1f7.png#pic_center
https://i-blog.csdnimg.cn/direct/67292e92e8bd4751a9a345f770b156c5.png#pic_center
https://i-blog.csdnimg.cn/direct/5da3d4522bc148c49000c7652c4fc013.png#pic_center
https://i-blog.csdnimg.cn/direct/061cba9dc662401c83107a9327b9b905.png#pic_center
https://i-blog.csdnimg.cn/direct/29d2e0c4116b4ac098adfa72c975657e.png#pic_center
https://i-blog.csdnimg.cn/direct/82e14b1aec244add9b406ffbf169a3b3.png#pic_center
https://i-blog.csdnimg.cn/direct/c64ecb965e724ef9b79218c554cebdc5.png#pic_center
https://i-blog.csdnimg.cn/direct/1fb4ee2168944530940bc2eab002edbd.png#pic_center
https://i-blog.csdnimg.cn/direct/f842bad8a8884fe0abb48b7b057b6f0f.png
问题和回复部分
你准备好使用ASI声音驱动了吗?
在讨论关于声音驱动的问题时,提到了一些对声音体系的渴望和波折。首先,提到了“ASI Sound Drivers”(辅助声音驱动),并扣问是否已经为其做好了设置。接着,作者表现自己并不打算在当前的机器上进行任何特别的设置或调解,机器上的声音体系仅是默认安装的声卡配置。
尽管如此,作者表达了一种困惑——他曾渴望,在进入Windows 7这个操纵体系时,声音体系应该已经有了更为稳固和高效的解决方案。作者认为,随着技术的进步,到Windows 7时,声音体系应该能够做到低延迟,且能够很容易地通过简单的硬件接口(如插头)进行毗连。
然而,作者却指出,尽管现代操纵体系好像应该具备更好的声音体系支持,现实情况却并非如此。他感叹,声音体系仍然需要“特殊的凤凰”,可能是指一些特殊的配置或驱动,才能正常工作。
接着,作者提到,可能是因为操纵体系或声卡驱动不再完全支持“DirectSound”,这导致假如切换到“XAudio”或其他音频框架时,可能会引入额外的复杂性或延迟。作者以为这有点令人沮丧,因为抱负情况下,声音体系应该能够直接支持这些尺度而不需要过多的调试或替换方案。
最后,作者总结道,写下这篇文章并没有特定的原因,只是因为这种理应“顺畅运行”的体系,竟然仍然存在不必要的复杂性,感觉是件令人遗憾的事,尤其是在今天的技术情况下,应该能够轻松解决这些问题。
总结来说,作者对于现代操纵体系和声音驱动的渴望与现实情况之间的差距感到失望,特别是在声音延迟和驱动兼容性方面,认为这些技术问题应该更容易解决。
问题: 你的音频指针代码是否独立于可变固定的视觉帧率进行盘算?
这段对话讨论了音频指针代码如何独立于视觉帧率进行盘算,特别是如何管理音频和视频的同步问题。以下是具体的复述:
[*] 音频指针独立性:
[*]这段代码的音频指针盘算是独立于固定或可变的视觉帧率进行的。这意味着音频的盘算与视频的帧率不完全同步。
[*] 解耦音频和视频:
[*]目前,音频和视频的盘算已经解耦,这意味着音频和视频的更新不再直接依靠彼此。
[*] 音频时钟的寻衅:
[*]需要解耦音频和视频的原因在于,音频时钟无法保证与盘算机的时钟同步。
[*]也就是说,音频的输出时钟不肯定与墙上的时钟匹配,可能会出现分歧,即便是很小的差异,随着时间推移,这种差异会渐渐累积,导致较大的错误。
[*] 音频写入位置的管理:
[*]为了避免这种情况,开发者管理了自己的帧率,并通过检查音频输出的位置来调解写入。
[*]当视频帧翻转时,开发者可以确认音频的写入位置,从而确定何时写入音频。
[*] 帧边界:
[*]抱负情况下,音频应该写入特定的帧边界。需要预测未来的帧边界,并确保在合适的时间写入音频数据。
[*] 音频延迟的处理:
[*]由于音频的延迟,音频可能会在已往的帧延迟之后才写入。开发者通过调解来确保音频延迟不会影响游戏的表现。
[*] 延迟优化:
[*]在某些情况下,开发者可以选择进一步淘汰音频延迟,但这会将额外的负担转移到游戏代码中。开发者提到,假如音频延迟非常关键(比方在节奏匹配游戏中),则可以做出差别的衡量来优化延迟。
[*]然而,开发者不渴望使游戏代码变得复杂,因此更倾向于将音频写入设置为帧边界,以避免过多的复杂性。
[*] 差别的选择:
[*]开发者认为,是否选择优化延迟或让游戏代码更复杂,完全取决于游戏的目标。假如想要淘汰延迟,比如在音频游戏中,可能会接纳差别的策略,但并不能说某种方法是绝对正确或错误的。
[*]对于当前的游戏,开发者认为,最好的做法是保持音频写入在帧边界上,这是一种简单且有效的方式。
总结而言,开发者讨论了音频与视频帧同步的问题,如何通过解耦这两者来管理音频的输出位置,避免因时钟差别步而导致的错误。终极,选择是否淘汰音频延迟依靠于游戏的具体需求和目标。
问题: 高音频延迟可能引发什么问题
以下是对原文的具体复述:
[*] 音频延迟问题:高音频延迟自己并不总是带来严峻的问题。它可能只是让体验感觉上不如抱负状态下那么顺畅,特别是在音频和视觉的同步上。抱负情况下,开发者渴望音频延迟为一帧,以便视觉效果和音频能够险些完全同步,这意味着音频和视频的节奏应该匹配,带来更加流通的体验。
[*] 同步音视频的寻衅:在最佳情况下,音频和视频应该是完全同步的。但现实中,通常没有那么抱负的情况。在某些情况下,开发者可能会选择在视频延迟上做出妥协,以换取更好的音频延迟。这种做法的方式通常是通过缓存渲染帧来实现,也就是提前渲染几个帧,等候音频同步到正确的位置,然后再进行视频翻转。这样做的目标是为了保证音频能在适当的时刻播放,但也带来了一些控制延迟,特别是在视频处理方面。
[*] 音频和视觉的延迟妥协:尽管有时这样做会引入视频方面的延迟,但有时间为了避免音频的差别步问题,开发者还是需要担当肯定的音频延迟。音频延迟意味着在按下按钮或进行某个动作时,音效不会立即播放,这种情况在打枪或雷同的情境下尤其显着。
[*] 人类对延迟的容忍度:人类在这方面的容忍度较高,固然按下按钮和听到相应的音效之间可能存在延迟,人们通常会认为它们是同步发生的,即使它们之间存在肯定的时间差。只要游戏自己不依靠于音频的即时反应,比方节奏匹配类游戏,玩家通常不会感到不适。
[*] 大脑的同步处理:尽管大脑能够明白音频和视频并不是美满同步的,但它依然会将两个变乱明白为同步发生,这表明人类的感知本领在肯定程度上容忍延迟。对于不依靠音频即时反馈的游戏,延迟可能不至于成为问题。
[*] 音频延迟对游戏体验的影响:尽管大脑能处理延迟并将其明白为同步,淘汰音频延迟仍然能让游戏感觉更好玩。过大的音频延迟会影响游戏的陶醉感和反应速度。抱负情况下,音频延迟应该低于肯定的阈值,高出该阈值时,玩家可能会感到不自然或不惬意。
[*] 阈值问题:对于大多数人来说,音频延迟高出100毫秒可能会影响体验,尤其是在需要即时反应的游戏中。但假如延迟在30毫秒以内,大多数人并不会察觉到显着的差别,因此这并不构成严峻问题。尽管如此,开发者通常渴望能够进一步淘汰音频延迟,从而提供更加准确和自然的用户体验。
[*] 硬件限制:尽管抱负情况下硬件和操纵体系能够更好地同步音频和视频,但现实上,硬件和操纵体系之间的管道可能存在一些不敷,导致音频延迟高于预期,这也是开发者面对的寻衅之一。
总体来说,固然音频延迟在某些情况下可能不会对游戏体验造成严峻影响,但它依然是一个需要在性能和体验之间做出妥协的因素。淘汰音频延迟是提拔游戏质量的紧张方面。
问题: 你能使用匿名团结结构体本领来更方便地访问位图信息头结构体的成员吗?
struct win32_offscreen_buffer {
BITMAPINFO Info;
void *Memory;
// 后备缓冲区的宽度和高度
int Width;
int Height;
int Pitch;
int BytesPerPixel;
};
这段话讨论了是否可以使用匿名团结结构体本领来更方便地访问位图信息头结构体的成员。下面是具体复述:
[*] 提问者的问题:提问者想知道是否可以通过使用匿名团结本领来更方便地访问位图信息头(Bitmap Info Header)结构体中的成员。
[*] 回复:
[*]答案是“不是真的”——也就是说,匿名团结本领并不能直接应用于这种情况。
[*]尽管有些结构体成员位于内层结构中,但无法直接通过匿名团结结构体的本领来访问这些成员。
[*] 解释:
[*]为了实现这个目标,必须做一些额外的工作。比方,可能需要将位图信息头结构体酿成它的一个子女结构体(即继承自它)。简单地说,就是必须进行结构体的派生,而不能简单地将匿名团结结构体插入到另一个结构中。
[*]根据尺度C语言的定义,匿名团结体不能直接嵌入到其他结构体中。固然有一些扩展可能支持这种做法,但不确定哪些编译器支持这些扩展。
[*] 扩展支持:
[*]该回复者提到,固然他知道有扩展可以实现这一点,但不确定这些扩展在哪些编译器中得到支持。
[*]他认为,尽管有扩展可以实现匿名团结体的这种嵌入操纵,但在尺度C中不直接支持,且不知道这些扩展是否被很多编译器普遍支持。
[*] 总结:
[*]终极的结论是,固然理论上可能有方法通过扩展来实现,但在尺度C中并没有直接的办法,而这些扩展是否被广泛支持也存在疑问。
问题: 关于切换音频API的问题
上面这段对话涉及切换音频API的讨论,重要围绕使用DirectSound和其他API(如XAudio或WDM)来优化音频延迟,以下是更具体的复述:
[*] 问题背景:
[*]开发者讨论的是在他们的项目中,是否应该切换音频API(如DirectSound,XAudio,WDM等),并且是否可以通过更换API来改善音频延迟问题。
[*]他们的目标是确保音频播放的兼容性,并且渴望在现有的兼容性框架下保持稳固。
[*] 现状和考虑:
[*]开发者的项目目标是与Windows XP兼容,因此他们选择了DirectSound,因为它在XP上默认受支持,并且使用DirectSound运行得非常顺利。
[*]开发者表现,他们不倾向于立即切换到其他API,尤其是在原型阶段,因为目前的设置已经稳固且符合兼容性要求。
[*] 未来的可能性:
[*]固然他们目前没有计划切换API,但在未来的版本中,他们可能会考虑测试XAudio等API,并评估其是否能改善音频延迟问题。
[*]开发者提到,尽管XAudio被很多人推崇为更现代的API,他们并不确定XAudio是否能带来更好的音频延迟。现实的效果可能并不如预期,因为音频延迟的根本原因可能与硬件或驱动程序有关,而不但仅是API自己。
[*] 对API的见解:
[*]开发者强调,很多人对差别的音频API过于冲动,可能是基于推测而非现实履历。尽管有些人认为应该使用某个特定的API,但现实上他们可能没有在特定硬件上进行过测试,因此无法确定哪个API能有效改善音频延迟。
[*]开发者特别提到,他们可能不会对XAudio或其他API过于兴奋,直到他们进行现实测试并得到确凿的数据为止。
[*] 硬件和驱动程序的影响:
[*]开发者认为,音频延迟问题可能更多与音频芯片和驱动程序的特性有关,而不是API的选择。差别的API可能产生相似的延迟,终极延迟的根本原因可能是硬件自己的限制。
[*] 关于“循环音频”的疑问:
[*]对话的最后,开发者不明白或人提到“声音是循环的”是什么意思,尤其是当循环结束后导致“超等循环”时。此处可能指代音频信号处理中的某些技术性细节,但开发者并不完全明白这方面的内容。
总结:
[*]开发者当前选择使用DirectSound,因为它与Windows XP的兼容性很好,并且它在他们的项目中工作得很好。尽管他们没有打算立即切换到其他API,但在未来可能会考虑测试其他API来看看是否能改善音频延迟问题。然而,开发者指出,音频延迟的根本问题可能与硬件或驱动程序自己有关,而不但仅是API选择。因此,他们认为在没有现实数据支持之前,做出关于API选择的推测是不正确的。
问题:我们为什么忽略写光标?
在这段对话中,重要讨论了关于“写光标”(write cursor)和“播放光标”(play cursor)的概念,尤其是在音频处理中的应用。下面是具体复述:
[*] 写光标的作用:
[*]文档中提到,不应在写光标之进步行写操纵,然而,这段话表现,现实上在某些情况下可以忽略这一规则。
[*]写光标只是指示“不应该在它之前写”,但并不会逼迫执行“锁定”操纵。换句话说,写光标并不肯定意味着它会影响我们的现实写入操纵。
[*] 为什么忽略写光标:
[*]重要原因是,写光标的位置并不总是与我们的需求直接相关,重点是播放光标的位置。播放光标才是我们真正关心的,因为它表现音频的现实播放位置。
[*]播放光标通常与写光标是分开的,且通常在技术上是“锁定”的,但并不总是如此。换句话说,播放光标更能反映音频的现实状态,而不是写光标。
[*] 现实操纵:
[*]现实上,我们渴望根据播放光标的位置进行写入操纵,即抱负情况下,我们会在播放光标之前肯定的隔断进行写入,以确保音频数据能够实时播放。
[*]假如写光标的位置比预期的要远,那么体系会检查并确保不会高出预计的延迟,保证音频的同步。
[*] 写光标的作用:
[*]写光标(write cursor)现实上并不对我们有太大帮助,它只是用来标示一个位置,告诉我们在此位置之前不应该写入数据。
[*]然而,写光标并不能提供关于音频播放状态的关键信息。我们关心的实在是播放光标,它能帮助我们同步音频输出。
[*] 总结:
[*]综上所述,固然写光标提供了一些位置指示,但它并不是我们现实操纵中最关心的。真正需要关注的是播放光标的位置,因为它决定了音频何时被播放。
[*]因此,忽略写光标并不会影响我们的操纵,反而可能简化流程,会合精力关注音频的同步和延迟控制。
这段对话强调了写光标与播放光标之间的区别,并解释了在音频处理和同步过程中,为什么现实操纵中更多关注播放光标,而不是写光标。
问题 将相关功能拆分到差别的文件中会有帮助吗?
具体复述:
[*] 拆分功能的影响:
[*]问题:提问者问是否将相关功能(如声音处理)拆分到差别的文件中,会使调试工作更容易。
[*]回复:回复者认为将相关功能拆分到单独的文件中并不会让调试变得更容易,反而可能使情况变得更糟。
[*] 原因:
[*]代码耦合性:回复者指出,在这些代码循环中,相关功能每每非常紧密地耦合。比方,声音检查通常需要在“flip”(翻转)操纵发生时同步进行。假如将这些操纵拆分到差别的文件中,耦合关系就会丧失,导致无法在本来的上下文中直接操纵。
[*]增长复杂性:将耦合的代码拆分成多个文件可能使得调试更为复杂,因为开发者需要通过多个文件查找相关代码,并推测每个函数的作用,这会增长阅读和明白代码的难度。
[*] 拆分文件的后果:
[*]固然将代码拆分到独立的文件中是可行的,但回复者不认为这样会带来显著的利益。反而,这可能增长工作量,并且对代码的可读性没有太大改善。
[*] 个人偏好:
[*]这部分决定终极取决于个人的偏好。假如开发者坚持要将功能拆分到单独的文件中,回复者表现可以明白,但他并不认为这样做会对调试过程产生实质性的改善。
总结起来,回复者认为拆分相关功能并不会使调试更容易,可能会使得代码更加难以明白和管理,除非有明白的理由,否则这种拆分可能并不值得实验。
问题: 假如我们使用播放光标作为偏移量,是否就可以避免覆盖之前写入的音频数据?
这段话讨论了如何使用播放光标作为偏移量来避免覆盖之前写入的音频数据。以下是更具体的复述:
首先,讨论者表现,他们考虑过使用播放光标作为偏移量,理论上这样做可以避免覆盖之前的音频数据。但他们指出,这个想法可能让事变变得更复杂,而不是更简单。因此,他们决定画出一个表示图来帮助更好地明白这一点。
接下来,讨论者描述了如安在30帧每秒的帧速率下工作,并且提到,每一帧的时间大约是33毫秒。通过查询DirectSound,他们可以获取到播放光标的位置以及右边的光标的位置。根据他们的盘算,播放光标和右边光标之间的差异大约是32毫秒。
然后,他们讨论了如何知道在那边可以安全地写入音频数据。他们指出,抱负情况下,他们渴望尽可能靠近播放光标的位置进行音频数据的写入。然而,由于帧速率和音频延迟大致雷同,所以这使得他们处于一种困难的位置。为了确保写入不覆盖之前的数据,他们需要一些延迟,这个延迟大约是三帧。
讨论者进一步解释了,在现实操纵中,他们无法直接写入到播放光标后的位置,因为光标更新的粒度和同步的细节使得这变得不可行。换句话说,即使他们想在播放光标前写入音频数据,现实上可能会发生错误,因为播放光标已经更新并移动到了新的位置。
他们还讨论了差别的延迟条件。比方,假如音频卡的延迟较低,问题可能会得到改善。相反,假如音频卡的延迟较高,这种同步问题可能会更加严峻。
最后,讨论者指出,他们不关心右光标的位置。关键是要关注播放光标的位置,并确保同步。通过检查播放光标的延迟和安全边界,他们可以盘算出一个适当的时间窗口来避免覆盖音频数据。
总体而言,讨论者认为,固然理论上可以用播放光标作为偏移量,但由于复杂的延迟和同步问题,现实操纵时可能不会取得抱负的效果。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]