Created
November 9, 2019 08:15
-
-
Save tripulse/0b39d1fd5007b2fbb0516d76480edb91 to your computer and use it in GitHub Desktop.
Synthesizer of periodic waveforms
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 <cstdio> | |
#include "utils.hpp" | |
#include "tone.hpp" | |
#include <vector> | |
#include <cmath> | |
#define FLOAT_SIZE sizeof(float) | |
/** | |
* Enumeration of perodic functions to generate a waveform. | |
* Since, computers cannot generate infinite sequence of periods | |
* so these functions are constrained under a finite number of | |
* digital stored (quantized) samples. | |
*/ | |
extern enum WaveFunction { | |
Sine, | |
Square, | |
Triangle, | |
Sawtooth, | |
None | |
}; | |
/* Archimedes constant of PI (180deg in radians). */ | |
constexpr float PI = 3.142857074737549; | |
/** | |
* Snythesizes a waveform with a arbitary frequency. | |
* Output samples always have 32-bit floating point | |
* precision with signed magnitude. | |
*/ | |
void synth(std::vector<float>& output, WaveFunction wavefn, double freq) { | |
/* Samplerate of the audio, the rate is the same as | |
the vector's size. */ | |
float samplerate = (float)output.size(); | |
// NOTE: | |
// Equations without trigonometric functions | |
// used might take more time to compute. | |
// On highend machines the time among | |
// equations may not be even distinguishable. | |
/** | |
* x = the frequency of the waveform. | |
* p = the period of the waveform. | |
*/ | |
switch (wavefn) { | |
/* sin(2πx / p) */ | |
case Sine: | |
for (auto s = 0.0f; s < samplerate; ++s) { | |
float p = s / samplerate; | |
output[(size_t)s] = | |
std::sin((2.0f * PI * freq) / p); | |
} | |
break; | |
/* sgn(sin(2πx / p)) */ | |
case Square: | |
for (auto s = 0.0f; s < samplerate; ++s) { | |
float p = s / samplerate; | |
output[(size_t)s] = | |
(float)std::signbit(std::sin((2.0f * PI * freq) / p)); | |
} | |
break; | |
/* 2(x/p - (0.5 + x/p)) */ | |
case Sawtooth: | |
for (auto s = 0.0f; s < samplerate; ++s) { | |
float p = s / samplerate; | |
output[(size_t)s] = | |
2.0f * (freq / p - (0.5f + freq / p)); | |
} | |
break; | |
/* 4/p * (x - p/2 * (2x/p + 0.5)) */ | |
case Triangle: | |
for (auto s = 0.0f; s < samplerate; ++s) { | |
float p = s / samplerate; | |
output[(size_t)s] = | |
4.0f / p * (freq - p / 2.0f * (2.0f * freq / p + 0.5f)); | |
} | |
break; | |
/* 0 */ | |
case None: | |
for (auto s = 0; s < samplerate; ++s) { | |
output[s] = 0.0f; | |
} | |
break; | |
} | |
} | |
// This is similar to the Rust's "loop" keyword. | |
// Makes sense because we're looping until infinity. | |
#define loop while(1) | |
/* Calculates the length of a C-based array. | |
Uses some, C++ magic to do so. */ | |
template<typename T> | |
size_t get_c_array_length(T& element) { | |
return sizeof(element) / sizeof(element[0]); | |
} | |
/* Stores information about a tones. | |
Links the frequency, generator function, | |
and correspoding charecter in it.*/ | |
struct ToneMap { | |
float freq; | |
WaveFunction wavef; | |
char letter; | |
}; | |
/* Iterate over list of tones find the one, which | |
has the following charecter in it. */ | |
ToneMap* find_tone_by_letter(ToneMap tones[], char letter) { | |
for (auto t = 0; t < get_c_array_length(tones); ++t) | |
if (tones[t].letter == letter) return &tones[t]; | |
} | |
/* Defined list of tones for charecters | |
occured in files. */ | |
ToneMap tone_maps[] = { | |
{12.0f, Sawtooth, 'a'}, | |
{24.0f, Square, 'b'}, | |
{150.0f, Sine, 'c'} | |
}; | |
int main(int argc, char* argv[]) { | |
// Synthesize tones of charecters from the file. | |
FILE* tone_in = std::fopen(argv[1], "rb"); | |
FILE* samples_out = std::fopen(argv[2], "wb"); | |
/* Snythesized sampledata of tones generated from | |
different perodic functions. */ | |
std::vector<float> tone_samples; | |
loop{ | |
char tone_char = (char)fgetc(tone_in); | |
if (tone_char == EOF) break; | |
ToneMap* tone = find_tone_by_letter(tone_maps, (char)fgetc(tone_in)); | |
if (tone == NULL) | |
*tone = { 0.0f, None, '\0'}; | |
synth(tone_samples, tone->wavef, tone->freq); | |
fwrite(tone_samples.data(), FLOAT_SIZE, tone_samples.size(), samples_out); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment