Skip to content

Instantly share code, notes, and snippets.

@chbtoys
Created July 17, 2022 09:45
Show Gist options
  • Save chbtoys/c12da6644a0294f30a838d3cf9da9ff4 to your computer and use it in GitHub Desktop.
Save chbtoys/c12da6644a0294f30a838d3cf9da9ff4 to your computer and use it in GitHub Desktop.
audio programming - beginner level
// 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