const byte sintable[256] = { // According to a post on AVRfreaks, a sine table in PROGMEM is 5x slower to access than an array in ram.
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,
176,179,182,184,187,190,193,195,198,200,203,205,208,210,213,215,
217,219,221,224,226,228,229,231,233,235,236,238,239,241,242,244,
245,246,247,248,249,250,251,251,252,253,253,254,254,254,254,254,
255,254,254,254,254,254,253,253,252,251,251,250,249,248,247,246,
245,244,242,241,239,238,236,235,233,231,229,228,226,224,221,219,
217,215,213,210,208,205,203,200,198,195,193,190,187,184,182,179,
176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,
127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,
78,75,72,70,67,64,61,59,56,54,51,49,46,44,41,39,
37,35,33,30,28,26,25,23,21,19,18,16,15,13,12,10,
9,8,7,6,5,4,3,3,2,1,1,0,0,0,0,0,
0,0,0,0,0,0,1,1,2,3,3,4,5,6,7,8,
9,10,12,13,15,16,18,19,21,23,25,26,28,30,33,35,
37,39,41,44,46,49,51,54,56,59,61,64,67,70,72,75,
78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124};
void updateSoundBuffer() {
const byte phaseAccumulators = 10;
const word phaseFrequencyBase[phaseAccumulators] = {4429/2, 8860/2, 1930, 3942, 5933, 7884, 9835, 11786, 2, 1}; // 2000, 1000
const word phaseStepBase[phaseAccumulators] = {(phaseFrequencyBase[0]/31250.0)*65536, // sine_a1
(phaseFrequencyBase[1]/31250.0)*65536, // sine_a2
(phaseFrequencyBase[2]/31250.0)*65536, // sine_b1
(phaseFrequencyBase[3]/31250.0)*65536, // sine_b2
(phaseFrequencyBase[4]/31250.0)*65536, // sine_b3
(phaseFrequencyBase[5]/31250.0)*65536, // sine_b4
(phaseFrequencyBase[6]/31250.0)*65536, // sine_b5
(phaseFrequencyBase[7]/31250.0)*65536, // sine_b6
(phaseFrequencyBase[8]/31250.0)*65536, // volume oscillation 25%-100% at 2hz-1hz.
(phaseFrequencyBase[9]/31250.0)*65536 // pitch oscillation +-50hz at 1hz.
};
static word phase[phaseAccumulators]; // Phase accumulator. Used as index into sine table with >> 8. [0..65535]
word phaseStep[phaseAccumulators]; // Step value by which to increment phase[] each sample.
const int pitchChange = 2000;//1400*1.5; // How much the frequency of the sound effect should rise between 0% extension and 100% extension.
const int pitchStepChangeMax = (pitchChange / 31250.0) * 65536; // This value, scaled by the current PKE speed, is added to the step values for the phase accumulators which generate the sine waves used to recreate the PKE's sound. It is another way of representing the frequency change when the PKE speeds up.
int pitchStepChange; // The calculated amount by which we should adjust the step values for all the frequency phase accumulators.
const int pitchOscillation = 50; // How much the frequency of the sound effect should rise/fall. (100hz)
const int pitchStepOscillationMax = (pitchOscillation / 31250.0) * 65536;
int pitchStepOscillation;
const int volumeOscillationChange = 10; // How much the frequency of the volume oscillation changes as the speed of the PKE increases.
const int volumePhaseStepChangeMax = (volumeOscillationChange / 31250.0) * 65536;
byte volumePhaseMultiplier = 0;
int difference; // The number of samples which need to be updated this loop.
byte sine; // Temp variable used to hold a value read from the sine table.
static byte noiseIndex = 0; // Index into noise table incremented once per reading. Automatically loops.
static byte oldnoise; // Used to high-pass filter noise.
byte noise; // Temp variable used to hold a value read from the noise table.
byte pkeVolumeFixed; // 0.256 fixed-point representation of current PKE volume level.
byte volume; // 0.256 fixed-point representation of current volume level after 1-2hz oscillation has been applied, and just before it is applied to the current sample being rendered.
const word sectionLength[4] = {256, 128, 256, 384}; // The number of samples in each of the four sections which make up the sound effect. (5 sines, 5 sines w/ noise, 5 sines, 2 sines)
static double sectionDivider = 1; // Section length randomly varies between /1 /2 and /4. This value signifies the number of bits to shift the values by to accomplish that. [0..2]
static word sectionIndex = 0; // Incremented as we step through each section.
static byte section; // The section currently being rendered.
static byte pulseCount; // A pulse is a group of 5 sections. We count how many have been rendered so we can change the timing between pulses every second or so.
static int nextIndex = 0; // The next sample in the buffer which needs to be updated.
long sample = 0; // The new sample to be placed into the buffer. We may be able to reduce this to a byte or a word.
byte i, n; // Temp variables used for loops.
//static long largest;
// Calculate volume change based on current PKE speed.
//pkeVolume = pkeVolume * (0.25 + 0.75*(pkeSpeed*pkeSpeed));
pkeVolume = pkeVolume * (0.2 + 0.8*(pkeSpeed*pkeSpeed));
pkeVolumeFixed = pkeVolume * 255;
// Update the step values for the phase accumulators:
// We need not calculate these each sample because they only change as the speed of the PKE changes, which can only happen during the main loop.
// Calculate base step values for sines, using current PKE speed:
pitchStepChange = pitchStepChangeMax * pkeSpeed;
for (n = 0; n < 8; n++) { // Adjust pitch.
phaseStep[n] = phaseStepBase[n] + pitchStepChange;
}
// Calculate step value for volume oscillator, using current PKE speed. The volume oscillates more slowly as the PKE speeds up, so volumePhaseStepChangeMax is a negative number.
phaseStep[8] = phaseStepBase[8] + (volumePhaseStepChangeMax * pkeSpeed);
// Calculate step value for pitch oscillator:
phaseStep[9] = phaseStepBase[9];
difference = soundBufferIndex - nextIndex; // Determine how many samples behind we are.
if (difference < 0) { difference = SOUNDBUFFERSIZE + difference; } // Handle case where sample playing is before next sample to be updated in the buffer.
if (difference >= 128) { difference = 128; } // Temp code to prevent rendering too many samples at once.