在同一个 AVAudioPCMBuffer 中改变正弦波频率

Posted

技术标签:

【中文标题】在同一个 AVAudioPCMBuffer 中改变正弦波频率【英文标题】:Changing Sine Wave frequencies in the same AVAudioPCMBuffer 【发布时间】:2017-12-11 13:05:33 【问题描述】:

我一直在努力获得一种干净的正弦波声音,它可以在播放不同音符时改变频率。据我了解,我需要调整缓冲区的 frameLength 相对于频率的大小,以避免在帧结束于正弦峰值时引起的爆裂声。

所以在每次迭代中,我都会设置 frameLength,然后用信号填充缓冲区。

AVAudioPlayerNode *audioPlayer = [[AVAudioPlayerNode alloc] init];
AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:[audioPlayer outputFormatForBus:0] frameCapacity:44100*10];`

while(YES)
    AVAudioFrameCount frameCount = ceil(44100.0/osc.frequency);
    [buffer setFrameLength:frameCount];
    [audioPlayer scheduleBuffer:buffer atTime:0 options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
    for(int i = 0; i < [buffer frameLength]; i++)
        for (int channelNumber = 0; channelNumber < channelCount ; channelNumber++) 
            float * const channelBuffer = floatChannelData[channelNumber];
            channelBuffer[i] = [self getSignalOnFrame:i];
        
    

信号从哪里产生:

(float)getSignalOnFrame:(int)i 
     float sampleRate = 44100.0;
     return [osc amplitude] * sinf([osc frequency] * i * 2.0 * M_PI / sampleRate);

开始音听起来不错,当音符变化时没有爆音,但音符本身听起来像是变成了锯齿波之类的东西。

关于我可能在这里遗漏的任何想法? 或者我应该为每个播放的音符创建一个全新的音频播放器吗?

感谢您的建议!

【问题讨论】:

【参考方案1】:

如果缓冲区是连续的,那么在生成正弦波时没有不连续性的更好方法是记住一个缓冲区结束时正弦波的相位,并使用该相位作为起点(角度)来生成下一个缓冲区。

如果缓冲区不连续,那么避免点击的常用方法是逐渐将每个缓冲区的前几毫秒和最后几毫秒从完全增益逐渐减小到零。线性增益锥度可以,但升余弦锥度是稍微平滑的锥度。

【讨论】:

以上是关于在同一个 AVAudioPCMBuffer 中改变正弦波频率的主要内容,如果未能解决你的问题,请参考以下文章

斯威夫特:AVAudioPCMBuffer vs AVAudioBuffer vs AVAudioCompressedBuffer

使用 Swift 中的 Accelerate 框架来自 AVAudioPCMBuffer 的频谱图

访问 AVAudioPCMBuffer 的浮点样本进行处理

将 Int16 写入 AVAudioPCMBuffer swift

将 AVAudioPCMBuffer 写入压缩的 AVAudioFile

将 pcm 加载到 AVAudioPCMBuffer