在同一AVAudioPCMBuffer中更改正弦波频率

时间:2017-12-11 13:05:33

标签: objective-c avfoundation avaudioplayer core-audio

我一直致力于获得干净的正弦波声音,可以在播放不同的音符时改变频率。根据我的理解,我需要调整缓冲区相对于频率的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,其中包含一个新的缓冲区?

感谢您的任何建议!

1 个答案:

答案 0 :(得分:2)

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

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