Created
April 5, 2018 03:28
-
-
Save MCJack123/02ef260f5a742dca7df2d902ce05c7ec to your computer and use it in GitHub Desktop.
Equation to WAV converter (requires exprtk)
This file contains 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 <iostream> | |
#include <fstream> | |
#include <string> | |
#include <cstdint> | |
#include "exprtk.hpp" | |
typedef struct { | |
char sGroupID[4]; | |
uint32_t dwFileLength; | |
char sRiffType[4]; | |
} wav_header_t; | |
typedef struct { | |
char sGroupID[4]; | |
uint32_t dwChunkSize; | |
uint16_t wFormatTag; | |
uint16_t wChannels; | |
uint32_t dwSamplesPerSec; | |
uint32_t dwAvgBytesPerSec; | |
uint16_t wBlockAlign; | |
uint16_t dwBitsPerSample; | |
} wav_fmt_t; | |
typedef struct { | |
char sGroupID[4]; | |
uint32_t dwChunkSize; | |
int16_t * sampleData; | |
} wav_data_t; | |
typedef struct { | |
int size; | |
char * data; | |
} wav_t; | |
int length = 15; | |
int samplerate = 44100; | |
double x = 0.0; | |
double y; | |
wav_header_t wav_header; | |
wav_fmt_t wav_format; | |
wav_data_t wav_data; | |
double cutoff(double num) { | |
if (num > 1.0) return 1.0; | |
else if (num < -1.0) return -1.0; | |
else return num; | |
} | |
void pushValue(char * a, uint16_t data, int off) { | |
//std::cout << "Pushing value " << data << " at offset " << off << "\n"; | |
*((uint16_t*)(&a[off])) = data; | |
} | |
void pushValue(char * a, uint32_t data, int off) { | |
//std::cout << "Pushing value " << data << " at offset " << off << "\n"; | |
*((uint32_t*)(&a[off])) = data; | |
} | |
void pushArray(char * a, int16_t data[], int length, int off) { | |
//std::cout << "Pushing array at offset " << off << "\n"; | |
for (int i = 0; i < length; i++) { | |
//std::cout << i << " to offset " << off+(i*2) << "\n"; | |
*((int16_t*)(&a[off+(i*2)])) = data[i]; | |
} | |
} | |
void pushString(char * a, char * data, int off) { | |
//std::cout << "Pushing string " << data << " at offset " << off << "\n"; | |
for (int i = 0; i < 4; i++) { | |
*(&a[off+i]) = data[i]; | |
} | |
} | |
wav_t prepareWav(wav_header_t header, wav_fmt_t format, wav_data_t datav) { | |
wav_t wave; | |
wave.size = header.dwFileLength + 8; | |
char * data = (char*)malloc(wave.size); | |
//std::cout << wave.size << "\n"; | |
pushString(data, header.sGroupID, 0); | |
pushValue(data, header.dwFileLength, 4); | |
pushString(data, header.sRiffType, 8); | |
pushString(data, format.sGroupID, 12); | |
pushValue(data, format.dwChunkSize, 16); | |
pushValue(data, format.wFormatTag, 20); | |
pushValue(data, format.wChannels, 22); | |
pushValue(data, format.dwSamplesPerSec, 24); | |
pushValue(data, format.dwAvgBytesPerSec, 28); | |
pushValue(data, format.wBlockAlign, 32); | |
pushValue(data, format.dwBitsPerSample, 34); | |
pushString(data, datav.sGroupID, 36); | |
pushValue(data, datav.dwChunkSize, 40); | |
pushArray(data, datav.sampleData, datav.dwChunkSize / 2, 44); | |
wave.data = data; | |
return wave; | |
} | |
int main(int argc, const char * argv[]) { | |
if (argc < 2) { | |
std::cout << "Usage: " << argv[0] << " <file.wav> [-t time] [-s samplerate]\n"; | |
return 1; | |
} | |
std::cout << "Enter an equation (example: \"y := sin(440 * x)\"): "; | |
std::string equation; | |
std::getline(std::cin, equation); | |
for (int i = 2; i < argc; i++) { | |
if (std::string(argv[i]) == "-t") { | |
length = std::stoi(std::string(argv[i])); | |
} else if (std::string(argv[i]) == "-s") { | |
samplerate = std::stoi(std::string(argv[i])); | |
} | |
} | |
int samplenum = samplerate * length; | |
int16_t * samples = (int16_t*)malloc(samplenum*2); | |
double sampletable[samplenum]; | |
// Calculate samples | |
std::cout << "Calculating...\n"; | |
exprtk::symbol_table<double> symbol_table; | |
symbol_table.add_variable("x", x); | |
symbol_table.add_variable("y", y); | |
exprtk::expression<double> expression; | |
expression.register_symbol_table(symbol_table); | |
exprtk::parser<double> parser; | |
parser.compile(equation, expression); | |
//std::cout << "Getting values...\n"; | |
double greatest = 0.0; | |
for (int i = 0; i < samplenum; i++) { | |
//std::cout << x; | |
//std::cout.flush(); | |
x = (M_PI / samplerate) * i * (440 / 217); | |
y = expression.value(); | |
//std::cout << " = " << y << " = " << (cutoff(y) * 32767) << "\n"; | |
sampletable[i] = y; | |
if (y > greatest) greatest = y; | |
} | |
//std::cout << "Reducing...\n"; | |
for (int i = 0; i < samplenum; i++) { | |
samples[i] = (int16_t)(cutoff(sampletable[i] / greatest) * 32767); | |
} | |
// Write WAV | |
std::cout << "Creating WAV...\n"; | |
memcpy(wav_header.sGroupID, "RIFF", 4); | |
memcpy(wav_header.sRiffType, "WAVE", 4); | |
memcpy(wav_format.sGroupID, "fmt ", 4); | |
wav_format.dwChunkSize = 16; | |
wav_format.wFormatTag = 1; | |
wav_format.wChannels = 1; | |
wav_format.dwSamplesPerSec = samplerate; | |
wav_format.dwBitsPerSample = 16; | |
wav_format.wBlockAlign = wav_format.wChannels * (wav_format.dwBitsPerSample / 8); | |
wav_format.dwAvgBytesPerSec = wav_format.dwSamplesPerSec * wav_format.wBlockAlign; | |
memcpy(wav_data.sGroupID, "data", 4); | |
wav_data.dwChunkSize = samplenum * 2; | |
wav_data.sampleData = samples; | |
wav_header.dwFileLength = wav_format.dwChunkSize + 16 + wav_data.dwChunkSize; | |
//std::cout << "Writing WAV...\n"; | |
wav_t waveout = prepareWav(wav_header, wav_format, wav_data); | |
//std::cout << waveout.size << "\n"; | |
std::ofstream out; | |
out.open(argv[1]); | |
out.write(waveout.data, waveout.size); | |
out.close(); | |
std::cout << "Written to " << argv[1] << "\n"; | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment