4 Commits

Author SHA1 Message Date
2cfc49bac3 feat: all filter types 2024-01-17 08:56:59 +07:00
29ceabca74 wip: State Variable Filter (FILTER LFO WORKS NOW!!!) 2024-01-17 08:56:58 +07:00
4fd0cb279d wip: moog filter mentioned 2024-01-17 08:56:58 +07:00
61c0d3b787 wip: state-variable filter doc 2024-01-17 08:56:58 +07:00
22 changed files with 62 additions and 24971 deletions

15
.vscode/settings.json vendored
View File

@@ -64,20 +64,7 @@
"__string": "cpp",
"compare": "cpp",
"concepts": "cpp",
"numeric": "cpp",
"__tree": "cpp",
"any": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"iomanip": "cpp",
"map": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"stack": "cpp",
"valarray": "cpp",
"ranges": "cpp",
"iostream": "cpp"
"numeric": "cpp"
},
"FSharp.suggestGitignore": false,
}

View File

@@ -17,10 +17,6 @@ FetchContent_Declare(
FetchContent_MakeAvailable(raylib)
# Add nlohmann_json
# FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz)
# FetchContent_MakeAvailable(json)
# Adding our source files
file(GLOB_RECURSE PROJECT_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp") # Define PROJECT_SOURCES as a list of all source files
set(PROJECT_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/inc/") # Define PROJECT_INCLUDE to be the path to the include directory of the project
@@ -35,7 +31,6 @@ set_target_properties(
target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE})
target_link_libraries(${PROJECT_NAME} PRIVATE raylib)
# target_link_libraries(${PROJECT_NAME} PRIVATE json)
# Setting ASSETS_PATH
target_compile_definitions(${PROJECT_NAME} PUBLIC ASSETS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/assets/") # Set the asset path macro to the absolute path on the dev machine

View File

@@ -35,7 +35,4 @@ for (n = 0; n < totalSamples; n++) {
}
We can replace the sin function with any periodic function that returns an amplitude for a given phase angle. Thus, this small piece of code can be used to produce a very wide range of sounds. Functionally, it is the software equivalent of an oscillator, the basic building block of almost all synthesizers.
https://ccrma.stanford.edu/software/stk/index.html
We can replace the sin function with any periodic function that returns an amplitude for a given phase angle. Thus, this small piece of code can be used to produce a very wide range of sounds. Functionally, it is the software equivalent of an oscillator, the basic building block of almost all synthesizers.

View File

@@ -23,5 +23,4 @@ class Application {
Application(/* args */);
~Application();
void Run();
void ParsePatch(std::string file_path);
};

17
inc/BandPassFilter.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include "Filter.h"
class BandPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
BandPassFilter();
BandPassFilter(Filter* filter);
BandPassFilter(float freq, float res, float q);
~BandPassFilter();
bool IsSameFilterType(FilterType type) override { return type == BandPass; };
};

View File

@@ -34,39 +34,3 @@ class Filter : public IEffect {
float GetPeakGain() { return m_drive; }
virtual bool IsSameFilterType(FilterType type) { return false; };
};
class BandPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
BandPassFilter();
BandPassFilter(Filter* filter);
BandPassFilter(float freq, float res, float q);
~BandPassFilter();
bool IsSameFilterType(FilterType type) override { return type == BandPass; };
};
class HighPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
HighPassFilter();
HighPassFilter(Filter* filter);
HighPassFilter(float freq, float res, float q);
~HighPassFilter();
bool IsSameFilterType(FilterType type) override { return type == HighPass; };
};
class LowPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
LowPassFilter();
LowPassFilter(Filter* filter);
LowPassFilter(float freq, float res, float q);
~LowPassFilter();
bool IsSameFilterType(FilterType type) override { return type == LowPass; };
};

View File

@@ -1,5 +1,8 @@
#pragma once
#include "Filter.h"
#include "LowPassFilter.h"
#include "BandPassFilter.h"
#include "HighPassFilter.h"
struct FilterFactory {
static Filter* CreateFilter(Filter* old_filter, FilterType new_type) {

14
inc/HighPassFilter.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "Filter.h"
class HighPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
HighPassFilter();
HighPassFilter(Filter* filter);
HighPassFilter(float freq, float res, float q);
~HighPassFilter();
bool IsSameFilterType(FilterType type) override { return type == HighPass; };
};

16
inc/LowPassFilter.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "Filter.h"
class LowPassFilter : public Filter {
protected:
float GetSampleForFilterType() override;
public:
LowPassFilter();
LowPassFilter(Filter* filter);
LowPassFilter(float freq, float res, float q);
~LowPassFilter();
bool IsSameFilterType(FilterType type) override { return type == LowPass; };
};

View File

@@ -29,7 +29,6 @@ class Oscillator {
~Oscillator();
OscillatorType GetType() { return m_osc; }
void SetType(OscillatorType osc);
void SetType(std::string const& osc_name);
float GetVolume() { return m_volume; }
void SetVolume(float volume) { m_volume = volume; }
float GetKey() { return m_key; }

View File

@@ -17,7 +17,6 @@ class Synth {
std::vector<IEffect*> m_effects;
std::vector<float> m_out_signal;
LFO* m_lfo;
float m_lfo_level;
void ZeroSignal();
void GetNote();
void TriggerNoteOnEffects();
@@ -37,8 +36,5 @@ class Synth {
const bool& GetIsNoteTriggered() { return is_note_triggered; }
ADSR* GetADSR() { return (ADSR*)m_effects[0]; }
Filter* GetFilter() { return (Filter*)m_effects[1]; }
LFO* GetLFO() { return m_lfo; }
const float& GetLFOLevel() { return m_lfo_level; }
void SetLFOLevel(float lvl) { assert(0.f >= lvl <= 1.f); m_lfo_level = lvl; }
void SetFilter(FilterType type);
};

View File

@@ -1,6 +1,6 @@
#pragma once
#include "OscillatorType.h"
#include "raysan/raygui.h"
#include "raygui.h"
#include <vector>
#include "Filter.h"

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,7 @@
#include "Application.h"
#include "Logger.h"
#include "Settings.h"
#include <cmath> // log10f
#include <fstream> // read file
#include <iostream> // log error
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
Application::Application(/* args */) {
InitSynth();
@@ -99,7 +94,7 @@ void Application::UpdateOnNoteInput() {
if (!m_synth.GetIsNoteTriggered()) {
m_synth.Trigger((*m_current_note));
}
// write_log("Note played: %s\n", m_current_note->name.c_str());
//write_log("Note played: %s\n", m_current_note->name.c_str());
} else if (is_note_up() && m_synth.GetIsNoteTriggered()) {
m_synth.Release();
}
@@ -123,67 +118,3 @@ void Application::Run() {
m_renderer.Draw(m_synth, m_synth_gui_state);
}
}
void Application::ParsePatch(std::string file_path) {
std::ifstream f(file_path);
if (!f.is_open()) {
std::cerr << "[ERR] failed to open " << file_path << '\n';
} else {
json data = json::parse(f);
auto oscillators = m_synth.GetOscillators();
for (int i = 0; i < oscillators.size(); i++) {
auto osc = oscillators[i];
auto gui_osc = m_synth_gui_state.oscillators[i];
std::string type =
data["Oscillators"][i]["Wave"].template get<std::string>();
float fine = data["Oscillators"][i]["Tune"].template get<float>();
float volume =
data["Oscillators"][i]["Volume"].template get<float>();
osc->SetType(type);
osc->SetFine(fine);
osc->SetVolume(volume);
gui_osc->waveshape = osc->GetType();
gui_osc->fine = fine;
gui_osc->volume = volume;
}
auto adsr = m_synth.GetADSR();
auto gui_adsr = m_synth_gui_state.adsr;
auto adsr_params = data["ADSR"];
float attack = adsr_params["Attack"].template get<float>();
float decay = adsr_params["Decay"].template get<float>();
float sustain = adsr_params["Sustain"].template get<float>();
float release = adsr_params["Release"].template get<float>();
adsr->SetParameters(attack, decay, sustain, release);
gui_adsr.attack = attack;
gui_adsr.decay = decay;
gui_adsr.sustain = sustain;
gui_adsr.release = release;
auto lfo = m_synth.GetLFO();
lfo->SetFreq(data["LFO"]["Freq"].template get<float>());
m_synth.SetLFOLevel(data["LFO"]["Level"].template get<float>());
auto filter_type =
(FilterType)data["Filter"]["Type"].template get<int>();
m_synth.SetFilter(filter_type);
auto filter = m_synth.GetFilter();
auto gui_filter = m_synth_gui_state.filter;
float filter_freq =
std::log10f(data["Filter"]["Cutoff"].template get<float>());
float filter_res = data["Filter"]["Res"].template get<float>();
float filter_drive = data["Filter"]["Drive"].template get<float>();
filter->SetParameters(filter_freq, filter_res, filter_drive);
gui_filter.freq = filter_freq;
gui_filter.type = filter_type;
}
}

View File

@@ -1,4 +1,4 @@
#include "Filter.h"
#include "BandPassFilter.h"
#include "Settings.h"
BandPassFilter::BandPassFilter() {

View File

@@ -1,4 +1,4 @@
#include "Filter.h"
#include "HighPassFilter.h"
#include "Settings.h"
HighPassFilter::HighPassFilter() {

View File

@@ -1,4 +1,4 @@
#include "Filter.h"
#include "LowPassFilter.h"
#include "Settings.h"
LowPassFilter::LowPassFilter() {

View File

@@ -43,19 +43,6 @@ void Oscillator::SetType(OscillatorType osc) {
}
}
void Oscillator::SetType(std::string const& osc_name) {
if (osc_name == "Sine")
m_osc = Sine;
else if (osc_name == "Triangle")
m_osc = Triangle;
else if (osc_name == "Saw")
m_osc = Saw;
else if (osc_name == "Square")
m_osc = Square;
SetType(m_osc);
}
void Oscillator::SetKey(float key) {
m_key = key;
float freq = KeyBoard::GetHzBySemitone(m_key + m_fine);

View File

@@ -7,7 +7,7 @@
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "raysan/raygui.h"
#include "raygui.h"
#pragma clang diagnostic pop
Renderer::Renderer(/* args */) {

View File

@@ -1,57 +1,7 @@
#include "Application.h"
#include <string_view>
#include <vector>
#include <string>
#include <iostream>
inline bool file_exists(const std::string& name) {
if (FILE *file = fopen(name.c_str(), "r")) {
fclose(file);
return true;
} else {
return false;
}
}
[[noreturn]] void print_help(char* const executable) {
std::cout << executable << " [options]\n" <<
"Options:\n" <<
" -h | --help Print this help\n" <<
" -p | --patch Path to a json file with a patch to apply\n" <<
" -v | --version Print a program version" << std::endl;
std::exit(0);
}
int main(int argc, char* argv[]) {
if (argc > 32) {
throw std::runtime_error("Too many input parameters!");
}
std::string patch_file_path;
for (int i = 0; i < argc; i++) {
auto arg = std::string(argv[i]);
if (arg == "-p" || arg == "--patch") {
patch_file_path = std::string(argv[i+1]);
if (patch_file_path.empty()) {
std::cerr << "no file path provided\n";
print_help(argv[0]);
}
else if (!file_exists(patch_file_path)) {
std::cerr << patch_file_path << ": no such file\n";
print_help(argv[0]);
}
}
else if (arg == "-h" || arg == "--help") {
print_help(argv[0]);
}
}
int main() {
Application* app = new Application();
if (!patch_file_path.empty()) {
app->ParsePatch(patch_file_path);
}
app->Run();
delete app;

View File

@@ -4,10 +4,11 @@
#include "Logger.h"
#include "OscillatorType.h"
#include "Settings.h"
#include "LowPassFilter.h"
Synth::Synth(/* args */) {
m_lfo = new LFO();
m_lfo->SetFreq(2.0);
m_lfo->SetFreq(5.0);
AddOscillator();
AddOscillator();
AddEffect(new ADSR());
@@ -79,7 +80,7 @@ void Synth::ApplyFilterLfo() {
float dt = m_lfo->Process();
Filter* filter = (Filter*)m_effects[1];
float freq = filter->GetFreq();
filter->SetParameters(freq + dt*0.5f, filter->GetRes(), filter->GetPeakGain());
filter->SetParameters(freq + dt, filter->GetRes(), filter->GetPeakGain());
}
void Synth::Process() {