DirectX 因素:构建用于 Windows 8 的音频振荡器

Charles
Petzold

下载代码示例

我一直在制造电子乐器作为一种爱好现在大约
35 年。 我开始在晚 20 世纪 70 年代布线了 TTL 和 CMOS 芯片,于是晚得多的软件路由 — — 第一次与多媒体扩展到 1991 年的 Windows 和 Windows 演示文稿基础
(WPF) 中,和在 Silverlight 和 Windows Phone 7 的 MediaStreamSource 类 NAudio 图书馆最近。 就在去年,我专门讨论我触摸的一对夫妇分期付款
& 列去为 Windows Phone 应用程序播放声音和音乐。

我也许应该厌倦了这个时候,,或许不愿意探索又一次的声音代 API。 但我没有,因为我认为
Windows 8 可能是的最佳 Windows 平台尚未制作乐器。 Windows 8 将高性能的音频 API 结合在一起 — — XAudio2 的 DirectX 组件 — — 与上手持平板电脑触摸屏。这种结合提供了很大的潜力,而且我特别感兴趣探索如何可以作为一个微妙和亲密到乐器完全在软件中实现接口利用触摸。

振荡器、 样本及频率

任何音乐合成器的声音发电设施的核心是多个振荡器,所以称为,因为它们生成更多或更少周期的振荡波形,在特定的频率和数量。 在生成音乐的声音,通常创建不会发生变化的周期波形的振荡器声音相当无聊。 更有趣的振荡器纳入颤音,颤音或不断变化的音色,而且他们只有大约定期。

一种程序,希望创建使用 XAudio2 振荡器开始通过调用 XAudio2Create 函数。 这提供了一个实现
IXAudio2 接口的对象。 从该对象中,您可以调用 CreateMasteringVoice 只需一次获取实例的 IXAudio2MasteringVoice,其中主要的音频混音器的作用。 只有一个
IXAudio2MasteringVoice 存在的任何时候。 相反,你会一般 CreateSourceVoice 多次调用创建的 IXAudio2SourceVoice 接口的多个实例。 每个这些
IXAudio2SourceVoice 实例可以作为独立的振荡器。 合并多个振荡器伴唱的文书、 合奏或一个完整的乐团。

IXAudio2SourceVoice 对象的创建和提交缓冲区包含一个描述波形的数字序列生成的声音。 这些数字通常称为样本。 他们常常以恒定速率
16 位宽 (CD 音频的标准),和他们来 — — 通常 44,100 Hz (也为 CD 音频标准) 左右。 这种技术具有脉冲代码调制或 PCM 别致的名称。

虽然此序列的样本可以描述十分复杂的波形,往往一个合成器生成一个相当简单的样本流 — — 最常用的方波、 三角波或锯齿 — — 与对应的波形频率 (视为螺距) 和被看作是卷的平均振幅周期。

例如,如果采样速率是 44,100 Hz,而且每个周期的 100 个样本得到逐步较大、 然后较小、 然后负,和背为零的值,由此产生的声音的频率是除以 100 或 441 Hz 的 44,100 — — 频率接近人类的听觉范围的知觉中心。 (440
Hz 的频率是中间 C 以上 A 和被用作一种优化的标准。

IXAudio2SourceVoice 接口继承一个名为 IXAudio2Voice 的 SetVolume 方法并定义其自己命名的 SetFrequencyRatio 的方法。 我特别好奇的这后一种方法,因为它似乎可以提供一种方法来创建生成在变频和麻烦最少的特定周期波形的振荡器。


1 显示一个名为实现这一技术的 SawtoothOscillator1 类的大部分。 虽然我使用熟悉的 16 位整数样品用于定义波形,内部 XAudio2 使用 32 位浮点点样品。 对于性能关键的应用程序,您可能需要探索存在浮点和整数之间的性能差异。

图 1 SawtoothOscillator1 类的多

SawtoothOscillator1::SawtoothOscillator1(IXAudio2* pXAudio2)
{
  // Create a source voice
  WAVEFORMATEX waveFormat;
  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
  waveFormat.
nChannels = 1;
  waveFormat.
nSamplesPerSec = 44100;
  waveFormat.
nAvgBytesPerSec = 44100 * 2;
  waveFormat.
nBlockAlign = 2;
  waveFormat.wBitsPerSample = 16;
  waveFormat.cbSize = 0;
  HRESULT hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &waveFormat,
                                           0, XAUDIO2_MAX_FREQ_RATIO);
  if (FAILED(hr))
    throw ref new COMException(hr, "CreateSourceVoice failure");
  // Initialize the waveform buffer
  for (int sample = 0; sample < BUFFER_LENGTH; sample++)
    waveformBuffer[sample] =
      (short)(65535 * sample / BUFFER_LENGTH - 32768);
  // Submit the waveform buffer
  XAUDIO2_BUFFER buffer = {0};
  buffer.AudioBytes = 2 * BUFFER_LENGTH;
  buffer.pAudioData = (byte *)waveformBuffer;
  buffer.Flags = XAUDIO2_END_OF_STREAM;
  buffer.PlayBegin = 0;
  buffer.PlayLength = BUFFER_LENGTH;
  buffer.LoopBegin = 0;
  buffer.LoopLength = BUFFER_LENGTH;
  buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
  hr = pSourceVoice->SubmitSourceBuffer(&buffer);
  if (FAILED(hr))
    throw ref new COMException(hr, "SubmitSourceBuffer failure");
  // Start the voice playing
  pSourceVoice->Start();
}
void SawtoothOscillator1::SetFrequency(float freq)
{
  pSourceVoice->SetFrequencyRatio(freq / BASE_FREQ);
}
void SawtoothOscillator1::SetAmplitude(float amp)
{
  pSourceVoice->SetVolume(amp);
}

在头文件中,基频设置干净地将分为 44,100 的采样率。 从,可这就是该频率的波形的一个周期的长度计算缓冲区的大小:

static const int BASE_FREQ = 441;
static const int BUFFER_LENGTH = (44100 / BASE_FREQ);

此外在页眉中文件作为字段该缓冲区的定义是:

short waveformBuffer[BUFFER_LENGTH];

后创建 IXAudio2SourceVoice 对象,Sawtooth-Oscillator1 构造函数与一个周期的锯齿波形缓冲区已满 — — 简单的波形振幅的-32768 从延伸到振幅的 32,767。 指示应永远重复
IXAudio2SourceVoice 提交此缓冲区。

不需要任何进一步的代码,这是永远扮演 441 Hz 锯齿波的振荡器。 这就是伟大的但它不是非常多的用途。为了让
SawtoothOscillator1 有点更多功能,我还包含了 SetFrequency 的一种方法。 此参数是类使用调用 SetFrequencyRatio 的频率。 传递给
SetFrequencyRatio 的值的范围可以从 XAUDIO2_MIN_FREQ_RATIO (或 1/1,024.0) 的浮点型值超过较早前为 CreateSourceVoice 参数指定的最大值。 我用
XAUDIO2_MAX_FREQ_RATIO (或 1,024.0) 这一论点。 人的听觉范围 — — 约 20 Hz 至 20,000 Hz — — 是由这些应用到 441 基频的两个常量定义的边界内好。

缓冲区和回调

我必须承认我是 SetFrequencyRatio 方法的最初有点怀疑。 数字增加和减少的波形频率不是一个简单的任务。 我觉得必须要与通过算法生成的波形进行比较结果。 这是
OscillatorCompare 项目,这是此列的可下载代码背后的动力。

OscillatorCompare 项目包括我已经描述以及一个 SawtoothOscillator2 类的 SawtoothOscillator1 类。 这第二类有一个
SetFrequency 方法,控件类如何动态生成定义波形的样本。 此波形是不断建造在缓冲区中,并提交实时响应回调中的 IXAudio2SourceVoice 对象。

通过实现 IXAudio2VoiceCallback 接口,类可以从 IXAudio2SourceVoice 接收回调。 实现此接口的类的实例然后作为参数传递给
CreateSourceVoice 方法。 SawtoothOscillator2 类实现此接口本身和它传递给 CreateSourceVoice,也表明它不会做出的 SetFrequencyRatio
使用它自己的实例:

pXAudio2->CreateSourceVoice(&pSourceVoice, &waveFormat,
        XAUDIO2_VOICE_NOPITCH, 1.0f,
        this);

实现 IXAudio2VoiceCallback 的类,可以使用 OnBufferStart 方法时它是提交一个新的波形数据缓冲区的时候收到通知。 一般时使用
OnBufferStart 来使波形数据保持最新,您会想要保持一双缓冲区和替换它们。 如果您从另一个源例如音频文件获得音频数据,这可能是最好的解决办法。 目标是不要让音频处理器成为"饿"。保持前处理缓冲区有助于防止饥饿,但并不能保证它。

但我被吸引向由 IXAudio2VoiceCallback 定义的另一种方法 — — OnVoiceProcessingPassStart。 除非您正在用很小的缓冲区,一般
OnVoiceProcessingPassStart 比 OnBufferStart 更频繁调用,并指示时将要处理的音频数据区块和需要多少字节。 在 XAudio2 文档中,此回调方法被促进作为一个最低的延迟,这通常是非常可取的交互式的电子音乐仪器。 你不想按下一个键,听说明之间的延迟

SawtoothOscillator2 头文件定义两个常量:

static const int BUFFER_LENGTH = 1024;
static const int WAVEFORM_LENGTH = 8192;

第一个常数是缓冲区的用来提交波形数据的长度。 这里它充当一个环形缓冲区。 对
OnVoiceProcessingPassStart 方法的调用请求特定数目的字节为单位)。 该方法通过将这些字节放在缓冲区 (从它掉最后一次离开的地方开始) 和 SubmitSourceBuffer
只呼吁该更新部分缓冲区的响应。 您想要此缓冲区,必须足够大,因此您的程序代码不覆盖仍正在发挥在后台缓冲区的一部分。

原来与 44,100 Hz 采样率的声音,对 OnVoiceProcessingPassStart 的调用总是请求 882 字节或 441 16 位样品。 换句话说,OnVoiceProcessingPassStart
被称为恒定速度的 100 倍每秒,或每 10 毫秒。 虽然没有记载,此 10 ms 持续时间可以视为 XAudio2 音频处理"量程,",它是一个好的数字,要牢记。 因此,对于这种方法编写的代码不能磨蹭。 避免
API 调用和运行时库调用。

第二个常数是波形的所需的一个周期的长度。 它可能是一个数组,包含的波形,样本的大小,但在
SawtoothOscillator2 中它仅用于计算。

SawtoothOscillator2 中的 SetFrequency 方法使用该常量来计算所需波形的频率成正比的角度增量:

angleIncrement = (int)(65536.0
                * WAVEFORM_LENGTH
                * freq / 44100.0);

虽然 angleIncrement 是一个整数,它被处理,就好像它包括整数和小数部分组成的字。 这是波形的用来确定每个连续示例的值。

例如,假设 SetFrequency 的参数是 440 Hz。 AngleIncrement
将计算为 5,356,535。 以十六进制格式,这是 0x51BBF7,这被视为整数 0x51 (或十进制的 81),与 0xBBF7,相当于 0.734 小数部分。 如果波形的完整周期是
8,192 字节和使用唯一的整数部分和跳过 81 字节每个样本的由此产生的频率是约 436.05 hz 的频率。 (那是除以 8,192 44,100 倍 81)。如果您跳过 82 字节,由此产生的频率是
441.43 Hz。 你想要这两个频率之间的事。

这就是为什么还需要输入计算分数的部分。 整件事情可能会更容易在浮点数,和浮动点甚至可能更快一些现代的处理器,但图
2 显示更多的"传统"仅使用整数的方法。 每次调用 SubmitSourceBuffer 指定的环形缓冲区条,更新的通知。

图 2 在 SawtoothOscillator2 OnVoiceProcessingPassStart

void _stdcall SawtoothOscillator2::OnVoiceProcessingPassStart(UINT32 bytesRequired)
{
  if (bytesRequired == 0)
      return;
  int startIndex = index;
  int endIndex = startIndex + bytesRequired / 2;
  if (endIndex <= BUFFER_LENGTH)
  {
    FillAndSubmit(startIndex, endIndex - startIndex);
  }
  else
  {
    FillAndSubmit(startIndex, BUFFER_LENGTH - startIndex);
    FillAndSubmit(0, endIndex % BUFFER_LENGTH);
  }
  index = (index + bytesRequired / 2) % BUFFER_LENGTH;
}
void SawtoothOscillator2::FillAndSubmit(int startIndex, int count)
{
  for (int i = startIndex; i < startIndex + count; i++)
  {
    pWaveformBuffer[i] = (short)(angle / WAVEFORM_LENGTH - 32768);
    angle = (angle + angleIncrement) % (WAVEFORM_LENGTH * 65536);
  }
  XAUDIO2_BUFFER buffer = {0};
  buffer.AudioBytes = 2 * BUFFER_LENGTH;
  buffer.pAudioData = (byte *)pWaveformBuffer;
  buffer.Flags = 0;
  buffer.PlayBegin = startIndex;
  buffer.PlayLength = count;
  HRESULT hr = pSourceVoice->SubmitSourceBuffer(&buffer);
  if (FAILED(hr))
    throw ref new COMException(hr, "SubmitSourceBuffer");
}

SawtoothOscillator1 和 SawtoothOscillator2 可以在 OscillatorCompare 程序并行相比。 网页有两对滑块控件更改的频率和每个振荡器的卷。 该频率的滑块控件生成仅
24 至 132 的整数值。 我借来的音乐设备数字接口 (MIDI) 标准用于表示球场的代码从这些值。 24
的值对应于 C 低于中间 C,变桨科学记数法叫做 C 1 (八度 1 C),并有约 32.7 赫兹频率,其中的三个八度。 132 的值对应于 C 10、 中东-C,以上六个八度和约 16,744
Hz 的频率。 关于这些滑块工具提示转换器科学音调符号和等效频率显示的当前值。

因为我在尝试用这些两个振子,我听不清的区别。 我还在直观地检查所产生的波形,另一台计算机上安装软件示波器和我也看不到任何差异。 这表明我的
SetFrequency-比率方法实现智能化,当然我们应该期望在一个系统中复杂的 DirectX。 我怀疑插上重新取样后的波形数据以移频正在执行。 如果你感到紧张,您可以设置
BASE_FREQ 非常低 — — 例如,至 20 赫兹 — — 和类将生成详细的波形,由组成的 2,205 样品。 您还可以尝试以较高的值:例如,8,820 Hz 将导致波形的只是五个样品要生成
! 当然,这有一个有些不同的声音,因为插值的波形介于之间锯齿波和三角波,但由此产生的波形是仍平稳无"锯齿"。

这并不意味着一切正常福。 与任一锯齿振荡器、
顶几个八度获得相当混乱。 波形的采样往往会发出高过之前,听到的一种低频率色彩和,打算在将来更充分调查。

压低音量 !

SetVolume 方法定义的 IXAudio2Voice 和 IXAudio2SourceVoice 的继承记录作为浮点乘数,可以设置为值范围从-2 ^24 至 2 ^24,这等于 16777216。

现实生活中,但是,您可能要将音量上一个 IXAudio2SourceVoice 对象保持为 0 和 1 之间的值。 值对应于沉默的
0 和 1 对应于没有增益或衰减。 请记住无论波形的源关联的 IXAudio2SourceVoice — — 正在通过算法生成还是来自的音频文件 — — 它可能已很有可能接近的-32768
和 32767 的最小和最大值的 16 位样本。 如果您尝试放大这些波形音量级别大于 1 时,样品将超过一个 16 位整数的宽度和将剪切的最小和最大值。 将导致失真和噪声。

当你开始组合 IXAudio2SourceVoice 的多个实例时,这一点非常重要。 这些多个实例的波形都被加在一起的混合。 如果您允许每个这些实例以拥有量的
1,声音的总和很可能导致超出 16 位整数的大小的样本。 偶尔可能发生此错误 — — 只间歇性的失真导致 — — 或慢性病,结果一件很麻烦的。

当使用多个生成完整 16 位宽波形的 IXAudio2SourceVoice 实例,一项安全措施将每个振荡器的卷设置为的声音数除以 1。 保证总和不能超过
16 位的值。 此外可以通过掌握的声音作出整体的卷调整。 你还可能想要看看
XAudio2CreateVolumeMeter 函数,它使您可以创建一个音频处理对象,可以帮助监视卷用于调试目的。

我们第一次的乐器

它是常见的乐器上片有钢琴式键盘,但我过了很久最近由类型的按钮键盘手风琴等俄罗斯巴彦 (其中我所熟悉的俄罗斯作曲家 Sofia Gubaidulina 工作) 上找到。 因为每个键是一个按钮,而不是长的杠杆,太多钥匙能装在
tablet 屏幕,在有限的空间内所示图 3。

图 3 ChromaticButtonKeyboard 程序

底部两行重复前两行上的键和提供,以纾缓共同和弦和旋律序列的指法。 否则,每个组的前三行中的
12 键提供所有备注的倍频程,一般按升序从左到右。 在这里的总范围是大小的四个八度,这是大小的两次什么你与钢琴键盘相同。

真正的巴彦有额外的倍频程,但我不能使按钮太小,不适合。 源代码中允许您设置常数来尝试这额外的倍频程,或消除另一个倍频程,并使按钮甚至更大。

因为我不能说这个计划听起来像是在现实世界中存在的任何文书,我只是叫它 ChromaticButton-键盘。 密钥是为
Key,从 ContentControl 派生,但执行一些触摸处理,以维持一个 IsPressed 属性,生成一个 IsPressedChanged 事件的自定义控件的实例。 当你扫你的手指在键盘处理此控件中的触摸和触摸处理的普通按钮
(其中也有一个 IsPressed 属性) 之间的差异是明显:标准按钮将设置 IsPressed 属性设置为 true 只手指按时发生在表面的按钮,此自定义键控制认为如果手指扫在从一侧按键。

该程序创建六个是从较早的项目几乎完全相同的 SawtoothOscillator1 类的 SawtoothOscillator 类的实例。如果您的触摸屏支持,您可以播放六同时注意到。 有没有回调,并由调用
SetFrequencyRatio 方法控制振荡器的频率。

要跟踪哪些振荡器可用以及哪个振荡器正在玩的 MainPage.xaml.h 文件定义了两个标准的集合对象作为字段:

std::vector<SawtoothOscillator *> availableOscillators;
std::map<int, SawtoothOscillator *> playingOscillators;

每个键对象原本的标记属性设置为我前面讨论的 MIDI 注释代码。 这就是
IsPressedChanged 处理程序如何确定哪个键被按下,和什么频率来计算。 MIDI 代码也被用作 playingOscillators 集合的映射键。 直到我玩了重复说明已在播放,导致重复键和异常的底部两个行的一份说明,它运转正常。 通过将一个值,指示关键所在的行的
Tag 属性纳入轻松地解决了这个问题:标记现在等于 MIDI 注代码加行号的 1000 倍。


4 显示关键实例的 IsPressedChanged 处理程序。 当按下键时,振荡器是从 availableOscillators 集合中移除,给定频率和非零的卷,并投入
playingOscillators 集合。 当释放某个键时,振荡器提供零卷,并搬回了 availableOscillators。

图 4 为关键实例的 IsPressedChanged 处理程序

void MainPage::OnKeyIsPressedChanged(Object^ sender, bool isPressed)
{
  Key^ key = dynamic_cast<Key^>(sender);
  int keyNum = (int)key->Tag;
  if (isPressed)
  {
    if (availableOscillators.size() > 0)
    {
      SawtoothOscillator* pOscillator = availableOscillators.back();
      availableOscillators.pop_back();
      double freq = 440 * pow(2, (keyNum % 1000 - 69) / 12.0);
      pOscillator->SetFrequency((float)freq);
      pOscillator->SetAmplitude(1.0f / NUM_OSCILLATORS);
      playingOscillators[keyNum] = pOscillator;
    }
  }
  else
  {
    SawtoothOscillator * pOscillator = playingOscillators[keyNum];
    if (pOscillator != nullptr)
    {
      pOscillator->SetAmplitude(0);
      availableOscillators.push_back(pOscillator);
      playingOscillators.erase(keyNum);
    }
  }
}

这就是简单以及 multi-voice 的文书可以,当然它有缺陷:声音应该不会关闭和打开交换机相似。 卷应滑行起来迅速顺利时注意启动,而且后退时,它将停止。 许多真实文书也说明随着卷和音色的变化。 还有很多改进的余地。

但考虑到代码的简洁性,它竟然行之有效和响应迅速。 如果您编译为
ARM 处理器的程序,可以将它部署上的基于 ARM 的 Microsoft Surface 和周围玩上它与另一只手,而我必须说是有点兴奋时搂着一只手臂在无约束平板电脑走。

Charles
Petzold 是 MSDN 杂志和作者的"Windows 编程,第 6 版"长期贡献 (O‘Reilly 媒体,2012年),一本关于编写应用程序的 Windows 8 书。 他的网站是 charlespetzold.com

衷心感谢以下技术专家对本文的审阅:Tom Mathews and Thomas Petchel

时间: 2024-10-06 19:11:43

DirectX 因素:构建用于 Windows 8 的音频振荡器的相关文章

DirectX 因素:在 Windows 8 中流式载入和处理音频文件

Charles Petzold 下载代码示例 如今,许多 Windows 用户的硬盘中都有一个音乐库,其中包含多达数千甚至上万个 MP3 和 WMA 文件. 若要在电脑上播放此音乐,这类用户一般运行 Windows Media Player 或 Windows 8 Music 应用程序. 但对于程序员来说,知道我们可以编写自己的程序来播放这些文件再好不过了. Windows 8 提供编程接口,用来访问音乐库,获取各个音乐文件的信息(如艺术家.标题和播放时长)以及用 MediaElement 播放

如何创建证书用于windows Azure 服务

向 Windows Azure 上载映像,都需要你创建证书,在创建证书后,您必须将其添加到 Windows Azure 中您的订阅. 一种方法是使用IIS管理器,然后在其中建立自我签署证书. 当您建立证书之后,需要导出该证书两次 - 一次含有私钥.pfx格式,另一次则没有.cer格式.您必须这样做两次的原因是因为您需要将不含私钥的证书上传至 Azure.含私钥的证书则需要导入至当前用户上的个人证书存储.当您在 [IIS 管理器] 中建立凭证时,它会将凭证放置本地计算机的个人存放区,这就是为什么您

Win8交互UX——用于 Windows 的触摸交互

用于 Windows 的触摸交互 Windows 8.1 提供一组在整个系统中使用的简单触摸交互功能.一致地应用此触摸语言可让用户对你的应用感觉已经很熟悉.通过让你的应用更容易学习和使用,可提高用户的信心.有关触摸语言实现的信息,请参阅手势.操作和交互. 在本文中: 触控设计原则 触控语言 触控目标 姿势和握法 相关主题 触控设计原则 提供即时反馈 在用户每次触摸屏幕时均立即提供视觉反馈, 可提高用户的信心.可互动的内容都应表现出某种反应 - 改变颜色.改变大小或发生移动.不可互动的内容 只有在

Journal of Proteomics Research | 构建用于鉴定蓖麻毒素的串联质谱库

文章题目:Constructing a Tandem Mass Spectral Library for Forensic Ricin Identification 构建用于鉴定蓖麻毒素的串联质谱库 解读人:马臻 Doi号:https://doi.org/10.1021/acs.jproteome.9b00377 文章链接:https://pubs.acs.org/doi/10.1021/acs.jproteome.9b00377 文章的实验室和主要参与人员: 实验室:美国西北太平洋国家实验室化

构建用于C#应用程序的应用商店(一)

我在就职的公司开发工具型软件,桌面版的,我们公司有各种工具软件的需求.现在我已经记不清我生产了多少了.我相信再过一段时间,也许几个月,也许一年后,我也会记不住之前开发过什么,或许有一定的类别的印象,但是具体是什么,运行的样子就会像现在记不起以前开发的软件一样,都记不清了. 这些工具没有完整的统计功能,也或许只有单机的统计,但他们核心功能大多是需要联网的. 我无法得知工具用户的使用情况,无法向上级出具有关使用情况的报告,我只知道我做个这个东西,有人曾经用过,或者曾经提过bug. 我相信很多做桌面工

DirectX 因素:了解 XAudio2 中的筛选器

Charles Petzold 下载代码示例 在著名的波形的万神殿,简单的正弦曲线至高无上. 只是看着它,您可以看到其精髓的顺利起伏性质 - - 当它达到其峰值. 几乎停止,它冠,然后逐步加快速度减慢,达到它的最大速度横渡水平轴开始另一个经济放缓. 这种视觉印象更深的数学分析所证实. 正弦曲线在任意点的瞬时速度是对曲线切线. 图的那些速度,并可以得到另一个正弦曲线,由四分之一周期从原始偏移. 不要再使用这第二条曲线,它和那是显示加速度,偏移量从原来的半个周期,如中所示的正弦曲线图 1. 图 1

DirectX 因素:音频处理对象简介

中文原文地址:https://msdn.microsoft.com/zh-cn/magazine/dn201755.aspx 英文原文地址:https://msdn.microsoft.com/en-us/magazine/dn201755.aspx?utm_source=tuicool&utm_medium=referral DirectX 的 XAudio2 组件更多只是方式在 Windows 8 应用程序中播放声音和音乐. 我是来查看它而是作为一多功能工程组的声音处理. 通过使用多个 IX

Android基于jenkins全自动构建打包---------Windows版本(Android,Jenkins,360加固,Email,QRcode,参数构建,蒲公英)

Android打包喝咖啡系列(Windows版) 这篇博客主要讲述的内容: 1.windows上部署Jenkins https://jenkins.io 2.基于SVN或Git https://github.com/Codetroupe/JenKinsTestAPP 3.Android项目参数化自动构建 4.自动将APK上传至蒲公英 https://www.pgyer.com   5.自动发Email到指定邮箱提示构建结果 6.自动本地360插件加固apk包http://jiagu.360.cn

DirectX 因素:模拟合成器的仿真

Charles Petzold 下载代码示例 大约在 50 年前,一名物理学家和工程师名叫罗伯特 · 穆格电子音乐合成器功能创建的颇不寻常:器官型键盘. 一些电子音乐的作曲家轻视这种平淡和老式的控制设备,同时其他作曲家 - - 和特别是表演者 - - 对此发展表示欢迎. 1960 年代末,由温卡洛斯的 Switched-On 巴赫已成为最畅销的古典专辑的所有时间,和 Moog 合成器进入了主流. 早期的 Moog 合成器是模块化的编程与跳线. 1970 年,然而,Minimoog 被释放 - -