Created
July 17, 2022 09:45
-
-
Save chbtoys/c12da6644a0294f30a838d3cf9da9ff4 to your computer and use it in GitHub Desktop.
audio programming - beginner level
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
// compile: clang++ -std=c++20 -lpng 07-pd.cpp -o 07-pd | |
#define _USE_MATH_DEFINES | |
#include <cmath> | |
#include <vector> | |
#include <random> | |
#include <filesystem> | |
#include "indicators.hpp" | |
#include <png++/png.hpp> | |
#include "AudioFile/AudioFile.h" | |
#include "Envelope.hpp" | |
namespace Render | |
{ | |
void fillbackground(png::image<png::rgb_pixel>& image); | |
void drawpx(png::image<png::rgb_pixel>& image, int x, int y); | |
void drawline(png::image<png::rgb_pixel>& image, int x1, int y1, int x2, int y2); | |
void drawwave(png::image<png::rgb_pixel>& image,std::vector<uint32_t>& signalY); | |
void normalizedtoimg(png::image<png::rgb_pixel>& image, std::vector<float>& v1,std::vector<uint32_t>& v2); | |
void renderimage(png::image<png::rgb_pixel>& image,std::vector<uint32_t>& v); | |
void saveimagefile(std::vector<uint32_t>& v, std::string filename); | |
void saveimagefile(std::vector<float>& v2, std::string filename); | |
void normalizedenvelopetoimg(png::image<png::rgb_pixel>& image, std::vector<float>& v1,std::vector<uint32_t>& v2); | |
void saveenvelopeimage(std::vector<uint32_t>& v, std::string filename); | |
void saveenvelopeimage(std::vector<float>& v2, std::string filename); | |
} | |
namespace SignalGenerators | |
{ | |
void gain(std::vector<float>& v, double gain); | |
void normalize(std::vector<float>& v); | |
void addwaves(std::vector<float>& v1,std::vector<float>& v2,std::vector<float>& v3); | |
void genereratephasedistortionwave(std::vector<float>& v, int duration, float frequencyInHz, float x1, float y1, float x2, bool isCosine); | |
void saveaudiofile(std::vector<float>& v, std::string filename); | |
} | |
int main() | |
{ | |
namespace fs = std::filesystem; | |
fs::create_directory("pd"); | |
const int duration=1; | |
const double sampleRate=44100.0; | |
std::vector<float> pd; | |
std::vector<float> envelope; | |
using namespace indicators; | |
// Hide cursor | |
show_console_cursor(false); | |
// Setup ProgressBar | |
ProgressBar bar{ | |
option::BarWidth{50}, | |
option::Start{"["}, | |
option::Fill{"■"}, | |
option::Lead{"■"}, | |
option::Remainder{"-"}, | |
option::End{" ]"}, | |
option::PostfixText{"Generate Phase Distortion 1/2"}, | |
option::ForegroundColor{Color::cyan}, | |
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}} | |
}; | |
// Update progress | |
bar.set_progress(0); | |
SignalGenerators::genereratephasedistortionwave(pd,duration,440,0.2,0.5,0.7,true); | |
SignalGenerators::saveaudiofile(pd,"01-pd.aiff"); | |
Render::saveimagefile(pd,"02-pd.png"); | |
// Update progress | |
bar.set_progress(50); | |
bar.set_option(option::PostfixText{"Final Phase Distortion 2/2"}); | |
ADSR::Envelope env(sampleRate,duration); | |
env.generateenvelope(envelope); | |
Render::saveenvelopeimage(envelope,"03-envelope.png"); | |
env.applyenvelope(pd,envelope); | |
SignalGenerators::normalize(pd); | |
SignalGenerators::saveaudiofile(pd,"04-final-pd.aiff"); | |
Render::saveimagefile(pd,"05-final-pd.png"); | |
// Update progress | |
bar.set_progress(100); | |
bar.set_option(option::PostfixText{"Done 2/2"}); | |
// Show cursor | |
show_console_cursor(true); | |
return 0; | |
} | |
namespace SignalGenerators | |
{ | |
void gain(std::vector<float>& v, double gain) | |
{ | |
for (uint32_t i=0;i<v.size();++i) { | |
v[i]=v[i]*gain; | |
} | |
} | |
void normalize(std::vector<float>& v) | |
{ | |
float max=0.0,value=0.0; | |
for (uint32_t i=0;i<v.size();++i) { | |
value=v[i]; | |
if (value > max) {max=value;} | |
} | |
// max=std::ceil(max); | |
for (uint32_t i=0;i<v.size();++i) { | |
// v[i]=v[i]/max; | |
v[i]=(v[i]/max)*0.707; | |
} | |
} | |
void addwaves(std::vector<float>& v1,std::vector<float>& v2,std::vector<float>& v3) | |
{ | |
for (uint32_t i=0;i<v1.size();++i) | |
{ | |
v3[i]=v1[i]+v2[i]; | |
} | |
} | |
void genereratephasedistortionwave(std::vector<float>& v, int duration, float frequencyInHz, float x1, float y1, float x2, bool isCosine) | |
{ | |
const double sampleRate=44100.0; | |
float delta=frequencyInHz/(sampleRate*duration); | |
float pos=0; | |
float xDelta=(x2-x1)/(sampleRate*duration); | |
float warpedPos; | |
float m; | |
float b; | |
float x=x1; | |
for (uint32_t i=0;i<sampleRate*duration;++i) | |
{ | |
if (pos < x) | |
{ | |
m=y1/x; | |
warpedPos=m*pos; | |
} else { | |
m=(1.0-y1)/(1.0-x); | |
b=1.0-m; | |
warpedPos=m*pos+b; | |
} | |
if (isCosine) { | |
v.push_back(cos(2.0*M_PI*warpedPos)); | |
} else { | |
v.push_back(sin(2.0*M_PI*warpedPos)); | |
} | |
pos += delta; | |
while (pos >= 1.0) {pos-=1.0;} | |
x+=xDelta; | |
} | |
} | |
void saveaudiofile(std::vector<float>& v, std::string filename) | |
{ | |
const std::string path="pd/"; | |
// Setup the audio file | |
AudioFile<float> a; | |
a.setNumChannels(1); | |
a.setBitDepth(24); | |
a.setNumSamplesPerChannel(44100); | |
for (int i=0;i<a.getNumSamplesPerChannel();++i) | |
{ | |
for (int channel=0;channel<a.getNumChannels();++channel) | |
{ | |
a.samples[channel][i]=v[i]; | |
} | |
} | |
a.save(path+filename,AudioFileFormat::Aiff); | |
} | |
} | |
namespace Render | |
{ | |
void fillbackground(png::image<png::rgb_pixel>& image) | |
{ | |
png::rgb_pixel px(0x04,0x13,0x31); | |
for (uint32_t y=0;y<image.get_height();y++) { | |
for (uint32_t x=0;x<image.get_width();++x) { | |
image.set_pixel(x,y,px); | |
} | |
} | |
} | |
void drawpx(png::image<png::rgb_pixel>& image, int x, int y) | |
{ | |
if (((x >= 0) && (x < image.get_width())) && ((y >= 0) && (y < image.get_height()))) | |
{ | |
png::rgb_pixel px(0x7a,0xb1,0xe3); | |
image.set_pixel(x,y,px); | |
} | |
} | |
void drawline(png::image<png::rgb_pixel>& image, int x1, int y1, int x2, int y2) | |
{ | |
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; | |
dx = x2 - x1; dy = y2 - y1; | |
if (dx == 0) | |
{ | |
if (y2 < y1) std::swap(y1, y2); | |
for (y = y1; y <= y2; y++) | |
drawpx(image, x1, y); | |
return; | |
} | |
if (dy == 0) | |
{ | |
if (x2 < x1) std::swap(x1, x2); | |
for (x = x1; x <= x2; x++) | |
drawpx(image, x, y1); | |
return; | |
} | |
dx1 = abs(dx); dy1 = abs(dy); | |
px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; | |
if (dy1 <= dx1) | |
{ | |
if (dx >= 0) | |
{ | |
x = x1; y = y1; xe = x2; | |
} | |
else | |
{ | |
x = x2; y = y2; xe = x1; | |
} | |
drawpx(image, x, y); | |
for (i = 0; x<xe; i++) | |
{ | |
x = x + 1; | |
if (px<0) | |
px = px + 2 * dy1; | |
else | |
{ | |
if ((dx<0 && dy<0) || (dx>0 && dy>0)) y = y + 1; else y = y - 1; | |
px = px + 2 * (dy1 - dx1); | |
} | |
drawpx(image, x, y); | |
} | |
} | |
else | |
{ | |
if (dy >= 0) | |
{ | |
x = x1; y = y1; ye = y2; | |
} | |
else | |
{ | |
x = x2; y = y2; ye = y1; | |
} | |
drawpx(image, x, y); | |
for (i = 0; y<ye; i++) | |
{ | |
y = y + 1; | |
if (py <= 0) | |
py = py + 2 * dx1; | |
else | |
{ | |
if ((dx<0 && dy<0) || (dx>0 && dy>0)) x = x + 1; else x = x - 1; | |
py = py + 2 * (dx1 - dy1); | |
} | |
drawpx(image, x, y); | |
} | |
} | |
} | |
void drawwave(png::image<png::rgb_pixel>& image,std::vector<uint32_t>& signalY) | |
{ | |
uint32_t y=0,ox=0,oy=0; | |
for (uint32_t x=0;x<image.get_width();++x) | |
{ | |
y=signalY[x]; | |
if (x == 0) {ox=x;oy=y;} | |
drawline(image,x,y,ox,oy); | |
ox=x;oy=y; | |
} | |
} | |
void normalizedtoimg(png::image<png::rgb_pixel>& image, std::vector<float>& v1,std::vector<uint32_t>& v2) | |
{ | |
uint32_t halfHeight=image.get_height()/2; | |
double value=0.0; | |
if (v2.size() == 0 || v2.size() > v1.size()) { | |
v2.resize(v1.size()); | |
} | |
for (uint32_t i=0;i<v1.size();++i) | |
{ | |
value=v1[i]; | |
if (value >= 0.0) { | |
v2[i]=halfHeight-(halfHeight*value); | |
} else if (value < 0.0) | |
{ | |
v2[i]=halfHeight+(halfHeight*fabs(value)); | |
} | |
} | |
} | |
void renderimage(png::image<png::rgb_pixel>& image,std::vector<uint32_t>& v) | |
{ | |
fillbackground(image); | |
drawwave(image,v); | |
} | |
void saveimagefile(std::vector<uint32_t>& v, std::string filename) | |
{ | |
const std::string path="pd/"; | |
png::image<png::rgb_pixel> image(44100,600); | |
renderimage(image,v); | |
image.write(path+filename); | |
} | |
void saveimagefile(std::vector<float>& v2, std::string filename) | |
{ | |
const std::string path="pd/"; | |
png::image<png::rgb_pixel> image(44100,600); | |
std::vector<uint32_t> v; | |
normalizedtoimg(image,v2,v); | |
renderimage(image,v); | |
image.write(path+filename); | |
} | |
void normalizedenvelopetoimg(png::image<png::rgb_pixel>& image, std::vector<float>& v1,std::vector<uint32_t>& v2) | |
{ | |
uint32_t height=image.get_height(); | |
if (v2.size() == 0 || v2.size() > v1.size()) { | |
v2.resize(v1.size()); | |
} | |
for (uint32_t i=0;i<v1.size();++i) | |
{ | |
v2[i]=height-(height*v1[i]); | |
} | |
} | |
void saveenvelopeimage(std::vector<uint32_t>& v, std::string filename) | |
{ | |
const std::string path="pd/"; | |
png::image<png::rgb_pixel> image(44100,600); | |
renderimage(image,v); | |
image.write(path+filename); | |
} | |
void saveenvelopeimage(std::vector<float>& v2, std::string filename) | |
{ | |
const std::string path="pd/"; | |
png::image<png::rgb_pixel> image(44100,600); | |
std::vector<uint32_t> v; | |
normalizedenvelopetoimg(image,v2,v); | |
renderimage(image,v); | |
image.write(path+filename); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment