Algorytm syntezy modulacji częstotliwości

9

Na podstawie tego, co przeczytałem, stworzyłem algorytm syntezy dźwięku FM. Nie jestem pewien, czy zrobiłem to dobrze. Podczas tworzenia programowego instrumentu syntezatora używana jest funkcja do generowania oscylatora, a modulator może służyć do modulowania częstotliwości tego oscylatora. Nie wiem, czy synteza FM ma działać tylko do modulowania fal sinusoidalnych?

Algorytm przyjmuje funkcję fali instrumentów oraz indeks i stosunek modulatora modulatora częstotliwości. Dla każdej nuty pobiera częstotliwość i przechowuje wartość fazy dla oscylatorów nośnej i modulatora. Modulator zawsze używa fali sinusoidalnej.

Oto algorytm w pseudokodzie:

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 

Więc jeśli częstotliwość nuty wynosi 100 Hz, FMRatio jest ustawiony na 0,5, a FMIndex wynosi 0,1, powinien wytwarzać częstotliwości między 95 Hz a 105 Hz w cyklu 50 Hz. Czy to właściwy sposób na zrobienie tego? Moje testy pokazują, że nie zawsze brzmi to dobrze, szczególnie podczas modulacji piły i fal prostokątnych. Czy można modulować fale piły i kwadratu w ten sposób, czy tylko dla fal sinusoidalnych?

To jest implementacja w C i 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;
}

Odpowiedzi są bardzo mile widziane.

Matthew Mitchell
źródło
3
Sugerowałbym przeczytanie dyskusji po tym pytaniu . Chociaż tutaj nie robią nagłe przejścia w częstotliwością jak w drugim pytaniu, utrzymanie ciągłości fazy w sygnale FM jest bardzo ważne, i zapewnienie, że sygnał FM jest fazą ciągłą , niezależnie od przebiegu modulującego, czy sinusoidalny lub piłę lub prostokątnym (nagła zmiana częstotliwości!), pomoże ci uniknąć wielu problemów.
Dilip Sarwate
3
Bez czytania dużego stosu kodu warto zapytać: na czym dokładnie polega problem? Mówisz, że nie jesteś pewien, czy to działa, czy nie. Co konkretnie sprawia, że ​​myślisz, że to nie działa?
Jason R

Odpowiedzi:

2

To, co tu robisz, to modulacja fazowa. Tak działają syntezatory FM, takie jak Yamaha DX-7. Często oscylatory syntezowane są na skalę muzyczną, a nie prostą liniową skalę Hz. Zatem bezpośrednie modulowanie wysokości tonu powoduje niepożądane przesunięcie wysokości tonu, dlatego modulacja fazowa jest bardziej odpowiednia. Możesz modulować dowolny kształt fali, jednak bardziej złożone kształty łatwiej będą aliasować. Nawet zmodulowany grzech może być alias, więc nie jest to zabronione.

Jeff McClintock
źródło