Here is a simple example of how frequency modulation works in audio.
The frequency of one tone (carrier) is altered with another oscillator (modulator). At high speeds, the resulting combination will bring out extra harmonics.
There are no dependencies in the example.
Play with the sliders and see how the different tones are generated.

SuperStrict
Framework brl.basic
Import pub.freeaudio
Import brl.glmax2d
Import brl.retro

'// If you get clicking in the audio, try restarting. Freeaudio is weird.

AppTitle = "Frequency Modulaton Example"

Const N% = 512
Const TWOPI:Double = Pi * 2
Const TWOPOS:Double = (2.0 * Pi) / N

Global Buff#[N]
Global Bp%
Global CarFreq# = 440
Global CarPhase#
Global ModFreq# = 0
Global ModPhase#
Global ModAmp# = 1
Global tim%
Global CFslider:tSlider = New tSlider(20, 10, 350, 20, "Carrier Freq")
Global MFslider:tSlider = New tSlider(20, 40, 350, 20, "Modulator Freq", 0)
Global MAslider:tSlider = New tSlider(20, 70, 350, 20, "Modulator Amp", 0)
	
Graphics 512, 500

While Not KeyHit(KEY_ESCAPE)
	Cls
		UpdateAudio()
		DrawWave()
		DrawSpeccy()
		
		CFslider.Update(MouseX(), MouseY())
		CarFreq = 20 + Lerp(20, 3000, CFslider.value() ^ 2)
		CFslider.Draw()
		
		MFslider.Update(MouseX(), MouseY())
		ModFreq = Lerp(1, 3000, MFslider.value())
		MFslider.Draw()
		
		MAslider.Update(MouseX(), MouseY())
		ModAmp = Lerp(1, 2000, MAslider.value())
		MAslider.Draw()
		DrawText("Mod/Car Ratio: " + (ModFreq / CarFreq), 300, 480)
	Flip
	tim:+1
Wend

'------------------------------------------------------------------------------------------
Function GetAudio#()
	'//Modulator
	ModPhase:+ModFreq * TWOPI / 44100.0
	If ModPhase > Pi Then ModPhase:-TWOPI
	Local ModVal# = Sine(ModPhase) * ModAmp
	
	'//Carrier
	CarPhase:+((CarFreq + ModVal)) * TWOPI / 44100.0
	If CarPhase > Pi Then CarPhase:-TWOPI

	Buff[Bp & 511] = Sine(CarPhase)
	Bp:+1

	Return Sine(CarPhase) * 0.2
End Function
'------------------------------------------------------------------------------------------
Function UpdateAudio()
	Global First:Int = True
	Const FRAG:Int = 2048 * 4
	Global buffer:Short[FRAG * 8]
	Global writepos:Int	
	Global sound:Byte Ptr
	Global channel:Int
	Global streaming:Int
	
	If First Then
		fa_Init(0)
		sound = fa_CreateSound(FRAG * 8, 16, 1, 44100, buffer, $80000000)
		channel = fa_PlaySound(sound, FA_CHANNELSTATUS_STREAMING, 0)
		fa_SetChannelPaused(channel, False)
		First = False
		Return
	End If
	
	Local readpos:Int = fa_ChannelPosition(channel)
	Local Write:Int = readpos + FRAG * 1 - writepos
	Local frags:Int = Write / FRAG
	
	While frags > 0
		Local Pos:Int = writepos Mod (FRAG * 8)
		For Local f:Int = 0 Until FRAG
		buffer[Pos + f] = GetAudio() * 30000
		Next
		writepos:+FRAG
		frags:-1
	Wend
End Function
'------------------------------------------------------------------------------------------
Function DrawWave()
	SetColor 255, 255, 255
	Local prev% = Buff[0]
	For Local i% = 0 Until N
		DrawRect(i, 150 + Buff[i] * 50, 2, 2)
		prev = Buff[i]
	Next
	DrawRect(0, 210, N, 1)
End Function
'------------------------------------------------------------------------------------------
Function DrawSpeccy()
	Global cp#[N]
	Global sp#[N]
	MemClear(Varptr cp[0], SizeOf(1#) * N)
	MemClear(Varptr sp[0], SizeOf(1#) * N)
	For Local b% = 0 Until N / 4
		For Local k% = 0 Until 256
			Local a# = 2.0 * b * Pi * k / 256
			sp[b]:+Buff[k] * - 1 * Sine(a)
			cp[b]:+Buff[k] * Cosine(a)
		Next
	Next
	Local prev# = 0
	For Local i% = 1 Until N / 8
		Local v# = (Sqr((sp[i] * sp[i]) + (cp[i] * cp[i])) / 64)
		DrawLine(i * 12, 500 - prev * 100, 12 + i * 12, 500 - v * 100)
		prev = v
	Next
End Function









'------------------------------------------------------------------------------------------
Function Sine:Double(x:Double) Inline
	Return Sin(x / (Pi / 180.0))
End Function
'------------------------------------------------------------------------------------------
Function Cosine:Double(x:Double) Inline
	Return Sine((3.1415926535897932 / 2.0) - x)
End Function
'------------------------------------------------------------------------------------------
Function Lerp:Float(a:Float, b:Float, x:Float)
    Return a*(1-x)+b*x
End Function
'------------------------------------------------------------------------------------------
Function invLerp#(a#, b#, v#)
    Return (v-a)/(b-a)
End Function
'------------------------------------------------------------------------------------------
Type tSlider
	Field x:Int, y:Int, w:Int, h:Int
	Field val:Float = 0.5
	Field Name$ = ""
	
	Method New(x%, y%, w%, h%, Name$, defv# = 0.5)
		Self.x = x
		Self.y = y
		Self.w = w
		Self.h = h
		Self.Name = Name
		val = defv
	End Method
	
	Method MouseOver:Int(mx:Float, my:Float)
		Return InsideRect(Mx, My, x, y, w, h)
	End Method
	
	Method Update(mx:Float, my:Float)
		If Not MouseOver(mx, my) Then Return
		If Not MouseDown(1) Then Return
		Local v:Float = Normalize(mx, 0, 1, x, x + w)
		val = Clamp(v, 0, 1)
	End Method
	
	Method value#()
		Return val ^ 2'0.3333
	End Method
	
	Method Draw()
		SetColor 32, 32, 32
		DrawRect x, y, w, h
		SetColor 90, 64, 64
		DrawText(Left(val, 4), (x + w) / 2, y + 1)
		SetColor 255, 255, 255
		DrawRectHollow(x, y, w, h)
		Local sv:Float = Normalize(val, x + 1, x + w - 1, 0, 1)
		SetColor 0, 255, 0
		DrawRect(sv, y, 2, h)
		DrawText(Name, x + w + 5, y)
	End Method
	
	Function DrawRectHollow(x:Float, y:Float, w:Int, h:Int)
		DrawLine(x, y, x + w, y)
		DrawLine(x,y,x,y+h)
		DrawLine(x+w,y,x+w,y+h)
		DrawLine(x,y+h,x+w,y+h)
	End Function
	
	Function InsideRect:Int(x:Float, y:Float, x2:Float, y2:Float, w:Float, h:Float)
		Return _pointinrect(x, y, x2, y2, x2 + w, y2 + h)
	End Function
	
	Function _pointinrect:Int(iPointX:Int, iPointY:Int, iXPos1:Int, iYPos1:Int, iXPos2:Int, iYPos2:Int)
		Return  ((((iPointX-iXPos1) ~ (iPointX-iXPos2)) & ((iPointY-iYPos1) ~ (iPointY-iYPos2))) & $80000000)
	End Function
	
	Function Normalize:Float(val:Float, desmin:Float, desmax:Float, natmin:Float, natmax:Float)
		Return desmin + (val - natmin) * (desmax - desmin) / (natmax - natmin)
	End Function
	
	Function Clamp:Float(x:Float, a:Float, b:Float)
        If x < a Then Return a
        If x > b Then Return b
        Return x
	End Function
End Type