7

根据我阅读的内容,我制作了一个用于 FM 声音合成的算法。我不确定我是否做得对。在创建软件合成器乐器时,可以使用一个函数来生成振荡器,并且可以使用调制器来调制该振荡器的频率。我不知道 FM 合成是否应该只用于调制正弦波?

该算法采用仪器波函数和频率调制器的调制器指数和比率。对于每个音符,它获取频率并存储载波和调制器振荡器的相位值。调制器始终使用正弦波。

这是伪代码中的算法:

function ProduceSample(instrument, notes_playing)
    for each note in notes_playing
        if note.isPlaying()
            # Calculate signal
            if instrument.FMIndex != 0 # Apply FM
                FMFrequency = note.frequency*instrument.FMRatio; # FM frequency is factor of note frequency.
                note.FMPhase = note.FMPhase + FMFrequency / kGraphSampleRate # Phase of modulator.
                frequencyDeviation = sin(note.FMPhase * PI)*instrument.FMIndex*FMFrequency # Frequency deviation. Max deviation is a factor of the FM frequency. Modulation is done by a sine wave. 
                note.phase = note.phase + (note.frequency + frequencyDeviation) / kGraphSampleRate # Adjust phase with deviation
                # Reset the phase value to prevent the float from overflowing
                if note.FMPhase >= 1
                    note.FMPhase = note.FMPhase - 1
                end if
            else # No FM applied
                note.phase = note.phase + note.frequency / kGraphSampleRate # Adjust phase without deviation
            end if
            # Calculate the next sample
            signal = signal + instrument.waveFunction(note.phase,instrument.waveParameter)*note.amplitude
            # Reset the phase value to prevent the float from overflowing
            if note.phase >= 1
                note.phase = note.phase - 1
            end if
        end if
    end loop
    return signal
end function 

因此,如果音符的频率为 100Hz,则 FMRatio 设置为 0.5,FMIndex 为 0.1,它应该在 50Hz 周期中产生 95Hz 和 105Hz 之间的频率。这是正确的做法吗。我的测试表明它听起来并不总是正确的,尤其是在调制锯齿波和方波时。可以像这样调制锯齿波和方波还是仅用于正弦波?

这是 C 和 CoreAudio 中的实现:

static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
    AudioSynthesiser * audioController = (AudioSynthesiser *)inRefCon;
    // Get a pointer to the dataBuffer of the AudioBufferList
    AudioSampleType * outA = (AudioSampleType *) ioData->mBuffers[0].mData;
    if(!audioController->playing){
        for (UInt32 i = 0; i < inNumberFrames; ++i){
            outA[i] = (SInt16)0;
        }
        return noErr;
    }
    Track * track = &audioController->tracks[inBusNumber];
    SynthInstrument * instrument = (SynthInstrument *)track;
    float frequency_deviation;
    float FMFrequency;
    // Loop through the callback buffer, generating samples
    for (UInt32 i = 0; i < inNumberFrames; ++i){
        float signal = 0;
        for (int x = 0; x < 10; x++) {
            Note * note = track->notes_playing[x];
            if(note){
                //Envelope code removed
                //Calculate signal
                if (instrument->FMIndex) { //Apply FM
                    FMFrequency = note->frequency*instrument->FMRatio; //FM frequency is factor of note frequency.
                    note->FMPhase += FMFrequency / kGraphSampleRate; //Phase of modulator.
                    frequency_deviation = sinf(note->FMPhase * M_PI)*instrument->FMIndex*FMFrequency; //Frequency deviation. Max deviation is a factor of the FM frequency. Modulation is done by a sine wave. 
                    note->phase += (note->frequency + frequency_deviation) / kGraphSampleRate; //Adjust phase with deviation
                    // Reset the phase value to prevent the float from overflowing
                    if (note->FMPhase >= 1){
                        note->FMPhase--;
                    }
                }else{
                    note->phase += note->frequency/ kGraphSampleRate; //Adjust phase without deviation
                }
                // Calculate the next sample
                signal += instrument->wave_function(note->phase,instrument->wave_parameter)*track->note_amplitude[x];
                // Reset the phase value to prevent the float from overflowing
                if (note->phase >= 1){
                    note->phase--;
                }
            } //Else nothing added
        }
        if(signal > 1.0){
            signal = 1;
        }else if(signal < -1.0){
            signal = -1.0;
        }
        audioController->wave[audioController->wave_last] = signal;
        if (audioController->wave_last == 499) {
            audioController->wave_last = 0;
        }else{
            audioController->wave_last++;
        }
        outA[i] = (SInt16)(signal * 32767.0f);
    }
    return noErr;
}

非常感谢答案。

4

3 回答 3

8

Redeye:

To answer your main question, yes it's absolutely fine to modulate waveforms other than sine waves. In fact, that's what FM is best at. Modulating sine waves gives a very boring sounding output, but when you input more complex waveforms with the same modulation, you get much more interesting results.

This is at best an oversimplification and possibly totally false. Modulating sine waves with sine waves is perfectly capable of creating a wide range of complex and not "boring" sounds.

In contrast, complex waveforms multiply the number of resulting sidebands massively and make predictable results much more hard to achieve. Most documentation about FM - which is actually the almost-equivalent PHASE modulation (PM) in many common cases including "the" "FM" by Yamaha - concerns sine waves only.

FYI (in case you don't already know), the most famous FM synth is probably the Yamaha DX7 which was revolutionary in its day (and also one of the first ever synths with MIDI).

The other thing to mention is that FM synthesis was the start of the digital age so the waveforms were generated digitally and hence used more sophisticated waveforms than sine/square/triangle waves to create the interesting sounds."

This is totally false without a doubt. The DX7 and many early FM - in reality, PM - synths by Yamaha offered ONLY sine waves, and yet, as I indicated above, they are still capable of many, many non-"boring" sounds. No "more sophisticated waveforms" were involved.

Only later did Yamaha add other waveforms, and their utility is somewhat questionable when compared to the predictability of the sidebands created by sine waves, as I stated above.

This might be what you need to do to get a better sound - rather than just generate a sine wave to modulate, use complex waveforms."

Or just use sine waves with good arrangements and combinations of parameters (ratio, index, etc.)

The fact that FM/PM with sine waves does not immediately produce studio-quality - or maybe just analogue-like - results for many users does not indicate whatsoever that it is incapable of doing so.

于 2013-09-07T13:10:59.343 回答
4

最后我决定使用相位调制。我发现许多合成器即使标有 FM 也使用相位调制。

实现起来很简单:

signal += wave_function(note_phase * note_frequency / sample_rate + fm_index * sin(note_phase * fm_frequency * pi / sample_rate))*note_amplitude
于 2012-01-20T20:01:47.027 回答
3

好问题,我会尝试提供一些想法/想法......

要回答您的主要问题,是的,调制除正弦波以外的波形绝对没问题。事实上,这正是 FM 最擅长的。调制正弦波会产生非常无聊的输出,但是当您使用相同的调制输入更复杂的波形时,您会得到更有趣的结果。仅供参考(如果您还不知道),最著名的 FM 合成器可能是Yamaha DX7,它在当时是革命性的(也是最早使用 MIDI 的合成器之一)。

另一件要提到的是,FM 合成是数字时代的开始,因此波形是数字生成的,因此使用比正弦波/方波/三角波更复杂的波形来创建有趣的声音。这可能是您需要做的以获得更好的声音 - 而不是仅仅生成正弦波进行调制,而是使用复杂的波形。

查看您的代码,看起来您正在正确执行 FM。但是,我认为调制频率通常是固定的,而不是代码中的音符频率的一小部分。可能值得尝试一下,看看它是否听起来更像你正在寻找的东西。

我希望这会有所帮助。

于 2011-12-23T21:51:56.950 回答