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

Changing Sine Wave frequencies in the same AVAudioPCMBuffer

我一直在努力获得一种干净的正弦波声音,这种声音可以在播放不同的音符时改变频率。据我了解,我需要相对于频率调整缓冲区的 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);
}

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

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

感谢任何建议!

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

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