Algoritmo de síntesis de modulación de frecuencia

9

Basado en lo que leí, hice un algoritmo para la síntesis de sonido FM. No estoy seguro si lo hice bien. Al crear un instrumento de sintetizador de software, se utiliza una función para generar un oscilador y se puede usar un modulador para modular la frecuencia de este oscilador. No sé si se supone que la síntesis de FM solo funciona para modular las ondas sinusoidales.

El algoritmo toma la función de onda del instrumento y el índice y relación del modulador para el modulador de frecuencia. Para cada nota, toma la frecuencia y almacena el valor de fase para la portadora y los osciladores del modulador. El modulador siempre usa una onda sinusoidal.

Este es el algoritmo en pseudocódigo:

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 

Entonces, si la frecuencia de la nota es de 100Hz, FMRatio se establece en 0.5 y FMIndex es 0.1, debería producir frecuencias que van entre 95Hz y 105Hz en un ciclo de 50Hz. ¿Es esta la forma correcta de hacerlo? Mis pruebas muestran que no siempre suena bien, especialmente al modular la sierra y las ondas cuadradas. ¿Está bien modular la sierra y las ondas cuadradas de esta manera o es solo para ondas sinusoidales?

Esta es la implementación en C y 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;
}

Las respuestas son muy apreciadas.

Matthew Mitchell
fuente
3
Le sugiero que lea las discusiones después de esta pregunta . Si bien aquí no está haciendo transiciones bruscas en frecuencia como en la otra pregunta, mantener la continuidad de fase en la señal FM es muy importante y garantizar que la señal FM sea continua en fase independientemente de la forma de onda de modulación, ya sea sinusoidal, diente de sierra o onda cuadrada (¡hay un cambio abrupto en la frecuencia!), lo ayudará a evitar muchos problemas.
Dilip Sarwate
3
Sin leer su gran pila de código, vale la pena preguntarse: ¿cuál es exactamente el problema? Dices que no estás seguro de si funciona o no. ¿Qué te hace pensar específicamente que no está funcionando?
Jason R

Respuestas:

2

Lo que estás haciendo aquí es la modulación de fase. Así es como funcionan los sintetizadores 'FM' como el Yamaha DX-7. A menudo, los osciladores de sintetizador se sintonizan en una escala musical, no en una escala lineal lineal de Hz. Por lo tanto, modular el tono directamente da como resultado un cambio de tono no deseado, por eso la modulación de fase es más adecuada. Puede modular cualquier forma de onda, sin embargo, las formas más complejas se alias más fácilmente. Sin embargo, incluso un pecado modulado puede ser un alias, por lo que no está prohibido.

Jeff McClintock
fuente