Skip to content

Instantly share code, notes, and snippets.

@tripulse
Created November 9, 2019 08:15
Show Gist options
  • Save tripulse/0b39d1fd5007b2fbb0516d76480edb91 to your computer and use it in GitHub Desktop.
Save tripulse/0b39d1fd5007b2fbb0516d76480edb91 to your computer and use it in GitHub Desktop.
Synthesizer of periodic waveforms
#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