Algorithme de synthèse de modulation de fréquence

9

Sur la base de ce que j'ai lu, j'ai créé un algorithme de synthèse sonore FM. Je ne sais pas si je l'ai bien fait. Lors de la création d'un instrument synthétiseur logiciel, une fonction est utilisée pour générer un oscillateur et un modulateur peut être utilisé pour moduler la fréquence de cet oscillateur. Je ne sais pas si la synthèse FM est censée fonctionner uniquement pour moduler les ondes sinusoïdales?

L'algorithme prend la fonction d'onde des instruments et l'indice et le rapport du modulateur pour le modulateur de fréquence. Pour chaque note, il prend la fréquence et stocke la valeur de phase pour les oscillateurs de la porteuse et du modulateur. Le modulateur utilise toujours une onde sinusoïdale.

Voici l'algorithme en pseudocode:

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 

Donc, si la fréquence de la note est à 100 Hz, le FMRatio est réglé à 0,5 et le FMIndex à 0,1, il devrait produire des fréquences allant de 95 Hz à 105 Hz dans un cycle de 50 Hz. Est-ce la bonne façon de procéder? Mes tests montrent qu'il ne sonne pas toujours correctement, en particulier lors de la modulation des ondes de scie et carrées. Est-il correct de moduler les ondes de scie et carrées comme ceci ou est-ce uniquement pour les ondes sinusoïdales?

Voici l'implémentation en C et 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;
}

Les réponses sont très appréciées.

Matthew Mitchell
la source
3
Je vous suggère de lire les discussions qui suivent cette question . Alors qu'ici, vous ne faites pas de transitions brusques de fréquence comme dans l'autre question, le maintien de la continuité de phase dans le signal FM est très important et veille à ce que le signal FM soit continu en phase indépendamment de la forme d'onde modulante, qu'elle soit sinusoïdale ou en dents de scie ou carrée (il y a le changement brusque de fréquence!), vous aidera à éviter beaucoup de problèmes.
Dilip Sarwate
3
Sans lire votre gros tas de code, il vaut la peine de se demander: quel est exactement le problème? Vous dites que vous n'êtes pas sûr que cela fonctionne ou non. Qu'est-ce qui vous fait penser que cela ne fonctionne pas?
Jason R

Réponses:

2

Ce que vous faites ici, c'est la modulation de phase. C'est ainsi que fonctionnent les synthés 'FM' comme le Yamaha DX-7. Souvent, les oscillateurs de synthé sont accordés sur une échelle musicale, et non sur une échelle linéaire linéaire Hz. Donc, la modulation de la hauteur entraîne directement un décalage de hauteur indésirable, c'est pourquoi la modulation de phase est plus appropriée. Vous pouvez moduler n'importe quelle forme d'onde, mais les formes plus complexes s'aliaseront plus facilement. Même un péché modulé peut alias, donc ce n'est pas interdit.

Jeff McClintock
la source