Skip to content

Instantly share code, notes, and snippets.

@chbtoys
Created July 17, 2022 09:46
Show Gist options
  • Save chbtoys/02da87aff752f0c42110a6ce5049f149 to your computer and use it in GitHub Desktop.
Save chbtoys/02da87aff752f0c42110a6ce5049f149 to your computer and use it in GitHub Desktop.
audio programming - beginner level
// compile: clang++ -std=c++20 -lpng 08-scanned.cpp -o 08-scanned
#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"
#include <iostream>
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 genereratescannedwave(std::vector<float>& v, int duration, float frequencyInHz);
void saveaudiofile(std::vector<float>& v, std::string filename);
}
int main()
{
namespace fs = std::filesystem;
fs::create_directory("scanned");
const int duration=1;
const double sampleRate=44100.0;
std::vector<float> scanned;
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 Scanned 1/2"},
option::ForegroundColor{Color::cyan},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
// Update progress
bar.set_progress(0);
SignalGenerators::genereratescannedwave(scanned,duration,440);
SignalGenerators::saveaudiofile(scanned,"01-scanned.aiff");
Render::saveimagefile(scanned,"02-scanned.png");
// Update progress
bar.set_progress(50);
bar.set_option(option::PostfixText{"Final Scanned 2/2"});
ADSR::Envelope env(sampleRate,duration);
env.generateenvelope(envelope);
env.applyenvelope(scanned,envelope);
SignalGenerators::normalize(scanned);
SignalGenerators::saveaudiofile(scanned,"03-final-scanned.aiff");
Render::saveimagefile(scanned,"04-final-scanned.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];
}
}
typedef struct {
float k, b;
float updateRate, updateSize;
} SystemDesc;
typedef struct {
int size;
float *position;
float *velocity;
} ScanState;
void scan(std::vector<float>& v,ScanState *state, SystemDesc *system, float frequencyInHz, float duration)
{
const double sampleRate=44100.0;
float *previousPosition=new float[128];
for (int i=0;i<sampleRate*duration;++i) {
auto scanPos = state->size * frequencyInHz * (float(i) / sampleRate);
int index = ((int) scanPos) % state->size;
v.push_back(state->position[index]);
float elapsed = 0;
while(elapsed < system->updateRate) {
previousPosition[state->size - 1] = state->position[state->size - 1];
for (int j = 0; j < state->size; j++) {
auto prevIndex = j > 0 ? j - 1 : state->size - 1;
auto nextIndex = j < state->size - 1 ? j + 1 : 0;
previousPosition[j] = state->position[j];
previousPosition[nextIndex] = state->position[nextIndex];
float prev = previousPosition[prevIndex];
float next = previousPosition[nextIndex];
float deltaX = (prev + next + 0) / 3 - state->position[j];
float force = system->k * deltaX - system->b * state->velocity[j];
state->velocity[j] += force * system->updateSize;
state->position[j] += state->velocity[j] * system->updateSize;
}
elapsed += system->updateSize;
}
}
delete [] previousPosition;
}
void genereratescannedwave(std::vector<float>& v, int duration, float frequencyInHz)
{
const double sampleRate=44100.0;
SystemDesc system = {.8, .01, .016, .01};
ScanState state;
state.size = 128;
state.position = new float[state.size];
state.velocity = new float[state.size];
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0.0, 1.0);
for (int i=0;i<state.size;++i) {
float randOffset=dis(gen);
float amplitude=0.1 + 0.5 * ((1+1) %2);
state.position[i]=sin(2*M_PI*i/state.size/4) * amplitude + randOffset;
state.velocity[i]=dis(gen);
}
scan(v,&state,&system,frequencyInHz,duration);
delete [] state.position;
delete [] state.velocity;
}
void saveaudiofile(std::vector<float>& v, std::string filename)
{
const std::string path="scanned/";
// 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="scanned/";
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="scanned/";
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="scanned/";
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="scanned/";
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