9
$\begingroup$

Based on what I read, I've made an algorithm for FM sound synthesis. I'm not sure if I did it right. When creating a software synth instrument a function is used to generate an oscillator and a modulator can be used to module the frequency of this oscillator. I don't know if FM synthesis is supposed to only work for modulating sine waves?

The algorithm takes the instruments wave function and the modulator index and ratio for the frequency modulator. For each note it takes the frequency and stores the phase value for the carrier and modulator oscillators. The modulator always uses a sine wave.

This is the algorithm in 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 

So if the note's frequency is at 100Hz, the FMRatio is set at 0.5 and the FMIndex is 0.1 it should produce frequencies going between 95Hz and 105Hz in a 50Hz cycle. Is this the correct way of doing it. My tests show that it doesn't always sound right, especially when modulating saw and square waves. Is it OK to modulate saw and square waves like this or is it for sine waves only?

This is the implementation in C and 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; } 

Answers are very much appreciated.

$\endgroup$
2
  • 3
    $\begingroup$ I would suggest that you read the discussions following this question. While here you are not making abrupt transitions in frequency as in the other question, maintaining phase continuity in the FM signal is very important, and ensuring that the FM signal is phase-continuous regardless of the modulating waveform, whether sinusoidal or sawtooth or square wave (there's the abrupt change in frequency!), will help you avoid lots of problems. $\endgroup$ Commented Dec 23, 2011 at 22:11
  • 3
    $\begingroup$ Without reading your big pile of code, it's worth asking: what exactly is the problem? You say you're not sure whether it works or not. What specifically makes you think it isn't working? $\endgroup$ Commented Dec 24, 2011 at 3:50

1 Answer 1

2
$\begingroup$

What you are doing here is phase modulation. This is how 'FM' synths like the Yamaha DX-7 operate. Often synth oscillators are tuned on a musical scale, not a straight linear Hz scale. So modulating the pitch directly results in an unwanted pitch shift, that's why phase modulation is more suitable. You can modulate any wave shape, however the more complex shapes will alias more readily. Even a modulated sin can alias though, so it's not forbidden.

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.