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", "__string": "cpp",
"compare": "cpp", "compare": "cpp",
"concepts": "cpp", "concepts": "cpp",
"numeric": "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"
}, },
"FSharp.suggestGitignore": false, "FSharp.suggestGitignore": false,
} }

View File

@@ -17,10 +17,6 @@ FetchContent_Declare(
FetchContent_MakeAvailable(raylib) 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 # 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 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 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_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE}) target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE})
target_link_libraries(${PROJECT_NAME} PRIVATE raylib) target_link_libraries(${PROJECT_NAME} PRIVATE raylib)
# target_link_libraries(${PROJECT_NAME} PRIVATE json)
# Setting ASSETS_PATH # 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 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. 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

View File

@@ -23,5 +23,4 @@ class Application {
Application(/* args */); Application(/* args */);
~Application(); ~Application();
void Run(); 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; } float GetPeakGain() { return m_drive; }
virtual bool IsSameFilterType(FilterType type) { return false; }; 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 #pragma once
#include "Filter.h" #include "Filter.h"
#include "LowPassFilter.h"
#include "BandPassFilter.h"
#include "HighPassFilter.h"
struct FilterFactory { struct FilterFactory {
static Filter* CreateFilter(Filter* old_filter, FilterType new_type) { 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(); ~Oscillator();
OscillatorType GetType() { return m_osc; } OscillatorType GetType() { return m_osc; }
void SetType(OscillatorType osc); void SetType(OscillatorType osc);
void SetType(std::string const& osc_name);
float GetVolume() { return m_volume; } float GetVolume() { return m_volume; }
void SetVolume(float volume) { m_volume = volume; } void SetVolume(float volume) { m_volume = volume; }
float GetKey() { return m_key; } float GetKey() { return m_key; }

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,7 @@
#include "Application.h" #include "Application.h"
#include "Logger.h" #include "Logger.h"
#include "Settings.h" #include "Settings.h"
#include <cmath> // log10f
#include <fstream> // read file
#include <iostream> // log error
#include <nlohmann/json.hpp>
#include <string> #include <string>
using json = nlohmann::json;
Application::Application(/* args */) { Application::Application(/* args */) {
InitSynth(); InitSynth();
@@ -99,7 +94,7 @@ void Application::UpdateOnNoteInput() {
if (!m_synth.GetIsNoteTriggered()) { if (!m_synth.GetIsNoteTriggered()) {
m_synth.Trigger((*m_current_note)); 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()) { } else if (is_note_up() && m_synth.GetIsNoteTriggered()) {
m_synth.Release(); m_synth.Release();
} }
@@ -123,67 +118,3 @@ void Application::Run() {
m_renderer.Draw(m_synth, m_synth_gui_state); 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" #include "Settings.h"
BandPassFilter::BandPassFilter() { BandPassFilter::BandPassFilter() {

View File

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

View File

@@ -1,4 +1,4 @@
#include "Filter.h" #include "LowPassFilter.h"
#include "Settings.h" #include "Settings.h"
LowPassFilter::LowPassFilter() { 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) { void Oscillator::SetKey(float key) {
m_key = key; m_key = key;
float freq = KeyBoard::GetHzBySemitone(m_key + m_fine); 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-but-set-variable"
#pragma clang diagnostic ignored "-Wunused-variable" #pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "raysan/raygui.h" #include "raygui.h"
#pragma clang diagnostic pop #pragma clang diagnostic pop
Renderer::Renderer(/* args */) { Renderer::Renderer(/* args */) {

View File

@@ -1,57 +1,7 @@
#include "Application.h" #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(); Application* app = new Application();
if (!patch_file_path.empty()) {
app->ParsePatch(patch_file_path);
}
app->Run(); app->Run();
delete app; delete app;

View File

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