Basierend auf dem, was ich gelesen habe, habe ich einen Algorithmus für die FM-Klangsynthese entwickelt. Ich bin mir nicht sicher, ob ich es richtig gemacht habe. Beim Erstellen eines Software-Synthesizer-Instruments wird eine Funktion zum Erzeugen eines Oszillators verwendet, und ein Modulator kann verwendet werden, um die Frequenz dieses Oszillators zu modulieren. Ich weiß nicht, ob die FM-Synthese nur zur Modulation von Sinuswellen funktionieren soll.
Der Algorithmus übernimmt die Wellenfunktion des Instruments sowie den Modulatorindex und das Verhältnis für den Frequenzmodulator. Für jede Note wird die Frequenz genommen und der Phasenwert für die Träger- und Modulatoroszillatoren gespeichert. Der Modulator verwendet immer eine Sinuswelle.
Dies ist der Algorithmus im 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
Wenn also die Frequenz der Note bei 100 Hz liegt, das FMRatio auf 0,5 und der FMIndex auf 0,1 eingestellt sind, sollten Frequenzen zwischen 95 Hz und 105 Hz in einem 50-Hz-Zyklus erzeugt werden. Ist das die richtige Art, es zu tun? Meine Tests zeigen, dass es nicht immer richtig klingt, besonders wenn Säge- und Rechteckwellen moduliert werden. Ist es in Ordnung, Säge- und Rechteckwellen wie diese zu modulieren, oder ist es nur für Sinuswellen?
Dies ist die Implementierung in C und 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;
}
Antworten werden sehr geschätzt.
quelle
Antworten:
Was Sie hier tun, ist Phasenmodulation. So funktionieren FM-Synthesizer wie der Yamaha DX-7. Oft werden Synth-Oszillatoren auf einer Musikskala gestimmt, nicht auf einer geraden linearen Hz-Skala. Die direkte Modulation der Tonhöhe führt also zu einer unerwünschten Tonhöhenverschiebung. Deshalb ist die Phasenmodulation besser geeignet. Sie können jede Wellenform modulieren, die komplexeren Formen werden jedoch leichter aliasisiert. Sogar eine modulierte Sünde kann einen Alias haben, daher ist dies nicht verboten.
quelle