Created
July 17, 2022 09:46
-
-
Save chbtoys/02da87aff752f0c42110a6ce5049f149 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 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