Skip to content

Instantly share code, notes, and snippets.

@SignalWhisperer
Created June 12, 2020 21:47
Show Gist options
  • Save SignalWhisperer/9620f04486dae45ac7332872fce289b0 to your computer and use it in GitHub Desktop.
Save SignalWhisperer/9620f04486dae45ac7332872fce289b0 to your computer and use it in GitHub Desktop.
Audio LFO example in C++11 using a sine table
#include <cmath>
#include <cstdint>
#include <fstream>
#include <vector>
#include <array>
#include <string>
#include <limits>
const unsigned int SINE_TABLE_NUM_ENTRIES = 1024;
std::array<double, SINE_TABLE_NUM_ENTRIES> sine_table;
void build_sine_table(void)
{
const double initial_phase = 0;
const double dphase = 2 * M_PI / SINE_TABLE_NUM_ENTRIES;
for (unsigned int i = 0; i < SINE_TABLE_NUM_ENTRIES; ++i) {
sine_table[i] = sin(initial_phase + dphase * i);
}
}
double sine_from_table(double phase)
{
// clip to (-2pi, 2pi)
phase = std::fmod(phase, 2 * M_PI);
// bring negative phase as positive [0, 2pi)
if (phase < 0) {
phase += 2 * M_PI;
}
// get a normalized value between [0, 1)
phase /= 2 * M_PI;
// get the index from the table [0, SINE_TABLE_NUM_ENTRIES)
unsigned int index = static_cast<unsigned int>(phase * SINE_TABLE_NUM_ENTRIES);
return sine_table[index];
}
void work(const std::int16_t* signal, std::int16_t* new_signal, std::size_t num_samples)
{
// Parameters
const double LFO_FREQUENCY = 5; // LFO frequency [Hz]
const double SAMPLE_RATE = 44100; // Sample rate [Hz]
const double CONVERSION_FACTOR = 32768.0;
double phase = 0; // Initial phase [rad]
double dphase = 2 * M_PI * LFO_FREQUENCY / SAMPLE_RATE; // Phase rate of change [rad/sample]
const double LFO_WEIGHT = 0.3; // Maximum amplitude variation of the LFO
const double LFO_OFFSET = 0.5; // Offset of the LFO amplitude
std::size_t n;
for (n = 0; n < num_samples; ++n) {
// Convert the input signal to a double and multiply by the LFO output
double sample = (signal[n] / CONVERSION_FACTOR) * (sine_from_table(phase) * LFO_WEIGHT + LFO_OFFSET);
// Increment the phase of the LFO by 1 sample, keep it within 2pi
phase = std::fmod(phase + dphase, 2*M_PI);
// Convert the sample to be used in the audio output
new_signal[n] = sample * CONVERSION_FACTOR;
}
}
int main(int, char**)
{
build_sine_table();
std::string in_path("sine.raw");
std::string out_path("lfo.raw");
std::ifstream fin(in_path, std::ifstream::binary);
// get the file size
fin.ignore(std::numeric_limits<std::streamsize>::max());
std::streamsize in_size = fin.gcount();
fin.clear();
fin.seekg(0, std::ios_base::beg);
// prepare the data arrays
std::size_t num_samples = in_size / sizeof(std::int16_t);
std::vector<std::int16_t> in(in_size / sizeof(std::int16_t));
std::vector<std::int16_t> out(in_size / sizeof(std::int16_t));
// read the contents
fin.read(reinterpret_cast<char*>(in.data()), in_size);
fin.close();
work(in.data(), out.data(), in.size());
std::ofstream fout(out_path, std::ofstream::binary);
fout.write(reinterpret_cast<const char*>(out.data()), out.size() * sizeof(std::int16_t));
fout.close();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment