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.
źródło
Odpowiedzi:
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.
źródło