// Sweetdreams: a toy softsynth in C++.
/* To run with sox:
make -k sweetdreams-c++ LDFLAGS=-lm && ./sweetdreams-c++ | play -t raw -r 48000 -s -b 16 -L -c 2 -
*/
// This program is in the public domain. To the extent possible under
// law, Kragen Javier Sitaker has waived all copyright and related or
// neighboring rights to Sweetdreams softsynth. This work is published
// from Argentina; see
// for details.
// That awesome synth sound in Eurythmics’ “Sweet Dreams” — is it just
// a flanged triangle wave? Or does it maybe have some AM and/or FM
// in it? I wanted to find out, so I wrote this stateless modular
// synth library in C++. It turns out to be a bit more complicated
// than that; basically you take some harmonic-rich carrier wave,
// such as a sawtooth (not a triangle wave!),
// flange it so the harmonics change over time, and amplitude-modulate
// it at a lower frequency. That isn’t the whole story, but that’s
// the basic timbre.
// The signal flow graph here to generate the Eurythmics synth
// consists of 29 nodes. Executing it interpretively takes about 669
// instructions per output sample, about 23 instructions per node.
// Emits 48ksps (16-bit little-endian 2-channel signed, like DAT).
// (I started writing this in C, but man, you know what? Fuck C. I’m
// going to use C++.)
// Currently 43,854,340 instrutions and 32,093,296 data refs to
// produce only 65536 samples. For shame!
// After batching up the output into 64-sample chunks: 35,662,215 and
// 28,883,826.
// After making Compose batch: 35,254,223 and 28,448,004 (1.2%
// faster).
// After making Amplify batch: 35,362,767 (!) and 28,085,508. No
// improvement, at least.
// After making Constant batch: 34,826,191 and 27,684,100. 1.2%
// faster than after Compose.
// After making Identity batch: 34,355,151 and 27,282,692. 1.3%
// faster.
// After fixing the test in Compose to allow rounding errors:
// 34,371,368 and 27,269,996.
// After making Sum batch: 34,290,603 and 27,016,662. However, it
// only batched 96 times.
// After loosening the rounding error test to 0.5%: 31,448,455 and
// 15,648,626. 9% better!
// After making Repeat batch: 25,137,245 and 10,311,366. Awesome!
// 20% better!
// After making Triangle batch: 23,778,067 and 9,529,450. 6.4%
// better.
// Changing buffer_size from 64 to 32 or 128 didn’t help; it slightly
// worsened performance, by like 3% or so.
// After manually strength-reducing the base-class get loop:
// 23,776,979 and 9,873,352. This is a tiny win, but I think it shows
// that the compiler isn’t doing the strength-reduction automatically,
// maybe because float addition isn’t a safe substitute for
// multiplication in general (though in this case it is).
// After eliminating the second buffer in Sum, 23,771,191.
// After eliminating the second buffer in Amplify, 23,761,975.
// These are not improvements in performance but they do simplify the
// code.
#include
#include
#include
#include