Created
June 12, 2020 21:47
-
-
Save SignalWhisperer/9620f04486dae45ac7332872fce289b0 to your computer and use it in GitHub Desktop.
Audio LFO example in C++11 using a sine table
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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