주파수 변조 합성 알고리즘


9

내가 읽은 것을 기반으로 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;
}

답변은 대단히 감사합니다.


3
이 질문에 대한 토론을 읽어 보시기 바랍니다 . 여기서 다른 질문과 같이 주파수에서 급격한 전환을하지는 않지만 FM 신호에서 위상 연속성을 유지하는 것이 매우 중요하며 정현파, 톱니파 또는 구형파에 상관없이 변조 파형에 관계없이 FM 신호가 위상 연속성 이되도록합니다. (주파수의 급격한 변화가 있습니다!)는 많은 문제를 피하는 데 도움이됩니다.
Dilip Sarwate

3
큰 코드 더미를 읽지 않으면 물어볼 가치가 있습니다. 정확히 무엇이 문제입니까? 당신은 그것이 작동하는지 확실하지 않다고 말합니다. 왜 작동하지 않는다고 생각합니까?
Jason R

답변:


2

여기서하는 일은 위상 변조입니다. Yamaha DX-7과 같은 'FM'신디사이저의 작동 방식입니다. 신디사이저 발진기는 종종 직선 선형 Hz 스케일이 아닌 음악 스케일로 튜닝됩니다. 따라서 피치를 직접 변조하면 원하지 않는 피치 이동이 발생하기 때문에 위상 변조가 더 적합합니다. 모든 파형을 변조 할 수 있지만 복잡한 형상 일수록 더 쉽게 앨리어싱됩니다. 변조 된 죄조차도 별명을 가질 수 있으므로 금지되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.