FM Synth and Plotting with PowerShell

Published October 24, 2017
Advertisement

Beep boop, making progress on my FM synthesizer. Had a nasty popping sound with my phase-feedback logic and needed some way to rapidly graph out the logic. Powershell to the rescue:
 


# plot-sample.ps1
$samples = 1024;
$width   = 2048;
$height  = 512;

[Reflection.Assembly]::LoadWithPartialName("System.Drawing");

$bmp = new-object Drawing.Bitmap $width, $height;
$g = [Drawing.Graphics]::FromImage($bmp);
$g.SmoothingMode = "AntiAlias";

$penAxis  = new-object Drawing.Pen 0xFF808080, 2;
$penLineA = new-object Drawing.Pen 0xFF0000F0, 2;
$penLineB = new-object Drawing.Pen 0xFFF00000, 2;
$ptsA     = new-object Drawing.PointF[] $samples;
$ptsB     = new-object Drawing.PointF[] $samples;

0..($samples-1) |% {
  $Q = 4*[Math]::pi/$samples;
  $feedback = 0;
} {
  $ampA = [Math]::sin($_*$Q + $feedback); # A[t] = sin(t + 1.05*A[t-1])
  $ampB = [Math]::sin($_*$Q);             # B[t] = sin(t) $feedback = $ampA * 1.05;

  $x = $bmp.Width * $_/$samples;
  $yA = $bmp.Height * (-$ampA/2.1 + .5);
  $yB = $bmp.Height * (-$ampB/2.1 + .5);
  $ptsA[$_] = new-object Drawing.PointF $x, $yA;
  $ptsB[$_] = new-object Drawing.PointF $x, $yB;
} {
  $g.Clear([Drawing.Color]::White);
  $g.DrawLine($penAxis,0,$bmp.Height/2,$bmp.Width,$bmp.Height/2);
  $g.DrawLines($penLineB,$ptsB);
  $g.DrawLines($penLineA,$ptsA);
  $bmp.Save("$pwd\plot.png");
}

plot.png
Looks like the logic should be sound. I did find that the phase-feedback wasn't being sin'd in the synthesizer, and that was causing the popping noise. And here's a sample of what it sounds like right now:
fm-sample.ogg (36.4KB)

And here's the instrument used to generate that sound:


/*
unit: 
	mul   frequency multiplier
	ofs   additive frequency
	amp   volume
	feed  phase feedback

envelope: 
	A     attack rate, in degrees
	D1    decay rate to sustain level, in degrees
	D2    decay rate to silence, in degrees
	R     release rate, in degrees
	S     sustain level

program: 
	u     unit to render
	in    phase buffer input slot, 0 for null input
	out   phase buffer output slot, 0 for audio data
*/

instrument i;
int idx = 0;
//                 mul    ofs    amp    feed    A     D1    D2    R     S
i.units[idx++] = { 2.00f, 0.00f, 0.50f, 1.00f, {80.0, 20.0, 10.0, 45.0, 0.75 }};
i.units[idx++] = { 1.00f, 0.00f, 0.25f, 0.00f, {85.0, 45.0,  5.0, 60.0, 0.50 }};
i.units[idx++] = { 7.00f, 0.33f, 0.12f, 0.00f, {88.5, 60.0,  5.0, 60.0, 0.25 }};
i.units[idx++] = { 1.00f, 0.33f, 0.50f, 0.00f, {87.0, 30.0,  5.0, 60.0, 0.67 }};
i.ct_units = idx;


// program:
// ┌─┐   ┌─┐
// │0├──►│1├──┐
// └─┘   └─┘  │   ┌─┐
//            ├──►│3├──►out
//       ┌─┐  │   └─┘
//       │2├──┘
//       └─┘

idx = 0; 
//                   u  in out
i.program[idx++] = { 0, 0, 2 };
i.program[idx++] = { 1, 2, 1 };
i.program[idx++] = { 2, 0, 1 };
i.program[idx++] = { 3, 1, 0 };
i.ct_program = idx;

Hue.

4 likes 1 comments

Comments

fastcall22

(Cleaned up from IPBoard upgrade.  Publishing it seems to have bumped it up to the top.)

October 24, 2017 10:22 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement