[feat]: Oscillator fine-tune #22
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
*.wav
|
*.wav
|
||||||
*.dSYM
|
*.dSYM
|
||||||
/lib
|
/lib
|
||||||
|
/build
|
||||||
37
CMakeLists.txt
Normal file
37
CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(SeeSynth)
|
||||||
|
#set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
|
# Adding Raylib
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_QUIET FALSE)
|
||||||
|
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples
|
||||||
|
set(BUILD_GAMES OFF CACHE BOOL "" FORCE) # don't build the supplied example games
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
raylib
|
||||||
|
GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
|
||||||
|
GIT_TAG "4.5.0"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(raylib)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
# Declaring our executable
|
||||||
|
add_executable(${PROJECT_NAME})
|
||||||
|
set_target_properties(
|
||||||
|
${PROJECT_NAME} PROPERTIES
|
||||||
|
CXX_STANDARD 17
|
||||||
|
CXX_STANDARD_REQUIRED ON)
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCES})
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE})
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE raylib)
|
||||||
|
|
||||||
|
# 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="./assets") # Set the asset path macro in release mode to a relative path that assumes the assets folder is in the same directory as the game executable
|
||||||
14
inc/ADSR.h
14
inc/ADSR.h
@@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Effect.h"
|
#include "IEffect.h"
|
||||||
#include "Ramp.h"
|
#include "Ramp.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
class ADSR : public Effect {
|
class ADSR : public IEffect {
|
||||||
enum ADSRState { sOff, sAttack, sDecay, sSustain, sRelease };
|
enum ADSRState { sOff, sAttack, sDecay, sSustain, sRelease };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -14,11 +14,11 @@ class ADSR : public Effect {
|
|||||||
ADSRState m_state;
|
ADSRState m_state;
|
||||||
Ramp* m_ramp;
|
Ramp* m_ramp;
|
||||||
|
|
||||||
void process_sample(float* sample);
|
void ProcessSample(float* sample);
|
||||||
bool is_attack_elapsed();
|
bool IsAttackElapsed();
|
||||||
bool is_decay_elapsed();
|
bool IsDecayElapsed();
|
||||||
bool is_release_elapsed();
|
bool IsReleaseElapsed();
|
||||||
void recheck_state();
|
void RecheckState();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ADSR(/* args */);
|
ADSR(/* args */);
|
||||||
|
|||||||
12
inc/Effect.h
12
inc/Effect.h
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
class Effect {
|
|
||||||
private:
|
|
||||||
/* data */
|
|
||||||
public:
|
|
||||||
Effect(/* args */){};
|
|
||||||
~Effect(){};
|
|
||||||
virtual void Trigger(){};
|
|
||||||
virtual void Release(){};
|
|
||||||
virtual void Process(std::vector<float>& samples){};
|
|
||||||
};
|
|
||||||
10
inc/Filter.h
10
inc/Filter.h
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Effect.h"
|
#include "IEffect.h"
|
||||||
|
|
||||||
enum FilterType {
|
enum FilterType {
|
||||||
LowPass,
|
LowPass,
|
||||||
@@ -7,7 +7,7 @@ enum FilterType {
|
|||||||
HighPass
|
HighPass
|
||||||
};
|
};
|
||||||
|
|
||||||
class Filter : public Effect {
|
class Filter : public IEffect {
|
||||||
protected:
|
protected:
|
||||||
float m_freq; // cutoff frequency
|
float m_freq; // cutoff frequency
|
||||||
float m_q; // filter quantity (resonance)
|
float m_q; // filter quantity (resonance)
|
||||||
@@ -23,10 +23,10 @@ class Filter : public Effect {
|
|||||||
public:
|
public:
|
||||||
Filter(/* args */);
|
Filter(/* args */);
|
||||||
virtual ~Filter();
|
virtual ~Filter();
|
||||||
void Trigger() override;
|
void Trigger() override final;
|
||||||
void Release() override;
|
void Release() override final;
|
||||||
float Process(float in);
|
float Process(float in);
|
||||||
void Process(std::vector<float>& samples) override;
|
void Process(std::vector<float>& samples) override final;
|
||||||
void SetParameters(float freq, float res, float q);
|
void SetParameters(float freq, float res, float q);
|
||||||
float GetFreq() { return m_freq; }
|
float GetFreq() { return m_freq; }
|
||||||
float GetRes() { return m_q; }
|
float GetRes() { return m_q; }
|
||||||
|
|||||||
10
inc/IEffect.h
Normal file
10
inc/IEffect.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
class IEffect {
|
||||||
|
private:
|
||||||
|
/* data */
|
||||||
|
public:
|
||||||
|
virtual void Trigger() = 0;
|
||||||
|
virtual void Release() = 0;
|
||||||
|
virtual void Process(std::vector<float>& samples) = 0;
|
||||||
|
};
|
||||||
@@ -54,10 +54,9 @@ class KeyBoard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KeyBoard(/* args */);
|
|
||||||
~KeyBoard();
|
|
||||||
|
|
||||||
static float GetHzBySemitone(int semitone) {
|
static float GetHzBySemitone(float semitone) {
|
||||||
|
//440 * Math.Pow(2, (note - 69) / 12.0) would it be better?
|
||||||
return PITCH_STANDARD * powf(powf(2.f, (1.f / 12.f)), semitone);
|
return PITCH_STANDARD * powf(powf(2.f, (1.f / 12.f)), semitone);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +70,3 @@ class KeyBoard {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyBoard::KeyBoard(/* args */) {}
|
|
||||||
|
|
||||||
KeyBoard::~KeyBoard() {}
|
|
||||||
|
|||||||
20
inc/LFO.h
Normal file
20
inc/LFO.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Oscillator.h"
|
||||||
|
|
||||||
|
class LFO: public Oscillator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/* data */
|
||||||
|
public:
|
||||||
|
LFO(/* args */);
|
||||||
|
~LFO();
|
||||||
|
void SetFreq(float freq) { m_phase_dt = (this->*m_dt_function)(freq); }
|
||||||
|
};
|
||||||
|
|
||||||
|
LFO::LFO(/* args */): Oscillator(Sine, 0.f, 0.5f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LFO::~LFO()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -5,31 +5,41 @@
|
|||||||
class Oscillator {
|
class Oscillator {
|
||||||
private:
|
private:
|
||||||
OscillatorType m_osc;
|
OscillatorType m_osc;
|
||||||
float m_freq;
|
float m_fine;
|
||||||
|
float m_key;
|
||||||
float m_volume;
|
float m_volume;
|
||||||
float m_phase;
|
float m_phase;
|
||||||
float m_phase_dt;
|
|
||||||
float (Oscillator::*m_osc_function)(void);
|
float (Oscillator::*m_osc_function)(void);
|
||||||
|
void SineOscPhaseIncr();
|
||||||
|
void SawOscPhaseIncr();
|
||||||
|
float CalcSawPhaseDelta(float freq);
|
||||||
|
float CalcSinePhaseDelta(float freq);
|
||||||
|
float SawOsc();
|
||||||
|
float TriangleOsc();
|
||||||
|
float SquareOsc();
|
||||||
|
float Sign(float v);
|
||||||
|
float SineOsc();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float m_phase_dt;
|
||||||
float (Oscillator::*m_dt_function)(float freq);
|
float (Oscillator::*m_dt_function)(float freq);
|
||||||
void sine_osc_phase_incr();
|
|
||||||
void saw_osc_phase_incr();
|
|
||||||
float calc_saw_phase_delta(float freq);
|
|
||||||
float calc_sine_phase_delta(float freq);
|
|
||||||
float sawosc();
|
|
||||||
float triangleosc();
|
|
||||||
float squareosc();
|
|
||||||
float sign(float v);
|
|
||||||
float sineosc();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Oscillator(OscillatorType osc, float freq, float volume);
|
Oscillator(OscillatorType osc, float fine, float volume);
|
||||||
~Oscillator();
|
~Oscillator();
|
||||||
OscillatorType GetType() { return m_osc; }
|
OscillatorType GetType() { return m_osc; }
|
||||||
void SetType(OscillatorType osc);
|
void SetType(OscillatorType osc);
|
||||||
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 GetFreq() { return m_freq; }
|
float GetKey() { return m_key; }
|
||||||
void SetFreq(float freq);
|
void SetKey(float key);
|
||||||
|
float GetFine() { return m_fine; }
|
||||||
|
void SetFine(float fine) {
|
||||||
|
if (fine != m_fine) {
|
||||||
|
assert(fine >= -2.f && fine <= 2.f);
|
||||||
|
m_fine = fine;
|
||||||
|
}
|
||||||
|
}
|
||||||
void Reset();
|
void Reset();
|
||||||
float Process();
|
float Process();
|
||||||
};
|
};
|
||||||
|
|||||||
20
inc/Synth.h
20
inc/Synth.h
@@ -3,7 +3,7 @@
|
|||||||
#include "ADSR.h"
|
#include "ADSR.h"
|
||||||
#include "Filter.h"
|
#include "Filter.h"
|
||||||
#include "Adder.h"
|
#include "Adder.h"
|
||||||
#include "Effect.h"
|
#include "IEffect.h"
|
||||||
#include "Note.h"
|
#include "Note.h"
|
||||||
#include "Oscillator.h"
|
#include "Oscillator.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
@@ -13,23 +13,23 @@ class Synth {
|
|||||||
private:
|
private:
|
||||||
bool is_note_triggered;
|
bool is_note_triggered;
|
||||||
std::vector<Oscillator*> m_oscillators;
|
std::vector<Oscillator*> m_oscillators;
|
||||||
std::vector<Effect*> m_effects;
|
std::vector<IEffect*> m_effects;
|
||||||
std::vector<float> m_out_signal;
|
std::vector<float> m_out_signal;
|
||||||
Oscillator* m_lfo;
|
Oscillator* m_lfo;
|
||||||
void zero_signal();
|
void ZeroSignal();
|
||||||
void get_note();
|
void GetNote();
|
||||||
void trigger_note_on_effects();
|
void TriggerNoteOnEffects();
|
||||||
void untrigger_note_on_effects();
|
void UntriggerNoteOnEffects();
|
||||||
void apply_effects();
|
void ApplyEffects();
|
||||||
void add_oscillator();
|
void AddOscillator();
|
||||||
void apply_filter_lfo();
|
void ApplyFilterLfo();
|
||||||
public:
|
public:
|
||||||
Synth(/* args */);
|
Synth(/* args */);
|
||||||
~Synth();
|
~Synth();
|
||||||
void Trigger(Note input);
|
void Trigger(Note input);
|
||||||
void Process();
|
void Process();
|
||||||
void Release();
|
void Release();
|
||||||
void AddEffect(Effect* fx);
|
void AddEffect(IEffect* fx);
|
||||||
const std::vector<float>& GetOutSignal() { return m_out_signal; }
|
const std::vector<float>& GetOutSignal() { return m_out_signal; }
|
||||||
const std::vector<Oscillator*>& GetOscillators() { return m_oscillators; }
|
const std::vector<Oscillator*>& GetOscillators() { return m_oscillators; }
|
||||||
const bool& GetIsNoteTriggered() { return is_note_triggered; }
|
const bool& GetIsNoteTriggered() { return is_note_triggered; }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
struct OscillatorGuiState {
|
struct OscillatorGuiState {
|
||||||
float volume;
|
float volume;
|
||||||
float freq; // todo: remove or change to pitch shift
|
float fine;
|
||||||
OscillatorType waveshape;
|
OscillatorType waveshape;
|
||||||
bool is_dropdown_open;
|
bool is_dropdown_open;
|
||||||
Rectangle shape_dropdown_rect;
|
Rectangle shape_dropdown_rect;
|
||||||
|
|||||||
20
src/ADSR.cpp
20
src/ADSR.cpp
@@ -12,33 +12,33 @@ ADSR::ADSR(/* args */) {
|
|||||||
|
|
||||||
ADSR::~ADSR() { delete m_ramp; }
|
ADSR::~ADSR() { delete m_ramp; }
|
||||||
|
|
||||||
bool ADSR::is_attack_elapsed() {
|
bool ADSR::IsAttackElapsed() {
|
||||||
return m_state == sAttack && m_ramp->IsCompleted();
|
return m_state == sAttack && m_ramp->IsCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ADSR::is_decay_elapsed() {
|
bool ADSR::IsDecayElapsed() {
|
||||||
return m_state == sDecay && m_ramp->IsCompleted();
|
return m_state == sDecay && m_ramp->IsCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ADSR::is_release_elapsed() {
|
bool ADSR::IsReleaseElapsed() {
|
||||||
return m_state == sRelease && m_ramp->IsCompleted();
|
return m_state == sRelease && m_ramp->IsCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSR::recheck_state() {
|
void ADSR::RecheckState() {
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case sAttack:
|
case sAttack:
|
||||||
if (is_attack_elapsed()) {
|
if (IsAttackElapsed()) {
|
||||||
m_state = sDecay;
|
m_state = sDecay;
|
||||||
m_ramp->RampTo(m_sustain_level, m_decay_time);
|
m_ramp->RampTo(m_sustain_level, m_decay_time);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case sDecay:
|
case sDecay:
|
||||||
if (is_decay_elapsed()) {
|
if (IsDecayElapsed()) {
|
||||||
m_state = sSustain;
|
m_state = sSustain;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case sRelease:
|
case sRelease:
|
||||||
if (is_release_elapsed()) {
|
if (IsReleaseElapsed()) {
|
||||||
m_state = sOff;
|
m_state = sOff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -47,7 +47,7 @@ void ADSR::recheck_state() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSR::process_sample(float* sample) {
|
void ADSR::ProcessSample(float* sample) {
|
||||||
if (m_state == sOff) {
|
if (m_state == sOff) {
|
||||||
(*sample) = 0;
|
(*sample) = 0;
|
||||||
} else if (m_state == sAttack) {
|
} else if (m_state == sAttack) {
|
||||||
@@ -80,8 +80,8 @@ void ADSR::Release() {
|
|||||||
|
|
||||||
void ADSR::Process(std::vector<float>& samples) {
|
void ADSR::Process(std::vector<float>& samples) {
|
||||||
for (std::size_t i = 0; i < samples.size(); i++) {
|
for (std::size_t i = 0; i < samples.size(); i++) {
|
||||||
recheck_state();
|
RecheckState();
|
||||||
process_sample(&samples[i]);
|
ProcessSample(&samples[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void Application::InitSynth() {
|
|||||||
assert(osc);
|
assert(osc);
|
||||||
|
|
||||||
OscillatorGuiState* ui =
|
OscillatorGuiState* ui =
|
||||||
new OscillatorGuiState{.freq = osc->GetFreq(),
|
new OscillatorGuiState{.fine = osc->GetFine(),
|
||||||
.waveshape = osc->GetType(),
|
.waveshape = osc->GetType(),
|
||||||
.volume = osc->GetVolume()};
|
.volume = osc->GetVolume()};
|
||||||
m_synth_gui_state.oscillators.push_back(ui);
|
m_synth_gui_state.oscillators.push_back(ui);
|
||||||
@@ -94,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
#include "Oscillator.h"
|
#include "Oscillator.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "KeyBoard.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
#define TWO_PI 2 * SYNTH_PI
|
#define TWO_PI 2 * SYNTH_PI
|
||||||
|
|
||||||
Oscillator::Oscillator(OscillatorType osc, float freq, float volume) {
|
Oscillator::Oscillator(OscillatorType osc, float fine, float volume) {
|
||||||
|
assert(fine >= -2.f && fine <= 2.f);
|
||||||
|
assert(volume >= 0.f && volume <= 1.f);
|
||||||
SetType(osc);
|
SetType(osc);
|
||||||
m_freq = freq;
|
m_fine = fine;
|
||||||
m_volume = volume;
|
m_volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,26 +25,27 @@ void Oscillator::SetType(OscillatorType osc) {
|
|||||||
m_osc = osc;
|
m_osc = osc;
|
||||||
switch (m_osc) {
|
switch (m_osc) {
|
||||||
case Sine:
|
case Sine:
|
||||||
m_osc_function = &Oscillator::sineosc;
|
m_osc_function = &Oscillator::SineOsc;
|
||||||
m_dt_function = &Oscillator::calc_sine_phase_delta;
|
m_dt_function = &Oscillator::CalcSinePhaseDelta;
|
||||||
break;
|
break;
|
||||||
case Triangle:
|
case Triangle:
|
||||||
m_osc_function = &Oscillator::triangleosc;
|
m_osc_function = &Oscillator::TriangleOsc;
|
||||||
m_dt_function = &Oscillator::calc_saw_phase_delta;
|
m_dt_function = &Oscillator::CalcSawPhaseDelta;
|
||||||
break;
|
break;
|
||||||
case Square:
|
case Square:
|
||||||
m_osc_function = &Oscillator::squareosc;
|
m_osc_function = &Oscillator::SquareOsc;
|
||||||
m_dt_function = &Oscillator::calc_sine_phase_delta;
|
m_dt_function = &Oscillator::CalcSinePhaseDelta;
|
||||||
break;
|
break;
|
||||||
case Saw:
|
case Saw:
|
||||||
m_osc_function = &Oscillator::sawosc;
|
m_osc_function = &Oscillator::SawOsc;
|
||||||
m_dt_function = &Oscillator::calc_saw_phase_delta;
|
m_dt_function = &Oscillator::CalcSawPhaseDelta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oscillator::SetFreq(float freq) {
|
void Oscillator::SetKey(float key) {
|
||||||
m_freq = freq;
|
m_key = key;
|
||||||
|
float freq = KeyBoard::GetHzBySemitone(m_key + m_fine);
|
||||||
m_phase = 0;
|
m_phase = 0;
|
||||||
m_phase_dt = (this->*m_dt_function)(freq);
|
m_phase_dt = (this->*m_dt_function)(freq);
|
||||||
}
|
}
|
||||||
@@ -49,44 +54,44 @@ float Oscillator::Process() {
|
|||||||
return (this->*m_osc_function)() * m_volume;
|
return (this->*m_osc_function)() * m_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oscillator::sine_osc_phase_incr() {
|
void Oscillator::SineOscPhaseIncr() {
|
||||||
m_phase += m_phase_dt;
|
m_phase += m_phase_dt;
|
||||||
if (m_phase >= TWO_PI)
|
if (m_phase >= TWO_PI)
|
||||||
m_phase -= TWO_PI;
|
m_phase -= TWO_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oscillator::saw_osc_phase_incr() {
|
void Oscillator::SawOscPhaseIncr() {
|
||||||
m_phase += m_phase_dt;
|
m_phase += m_phase_dt;
|
||||||
if (m_phase >= 1.0f)
|
if (m_phase >= 1.0f)
|
||||||
m_phase -= 1.0f;
|
m_phase -= 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Oscillator::calc_saw_phase_delta(float freq) {
|
float Oscillator::CalcSawPhaseDelta(float freq) {
|
||||||
return freq / SAMPLE_RATE;
|
return freq / SAMPLE_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Oscillator::calc_sine_phase_delta(float freq) {
|
float Oscillator::CalcSinePhaseDelta(float freq) {
|
||||||
return (TWO_PI * freq) / SAMPLE_RATE;
|
return (TWO_PI * freq) / SAMPLE_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Oscillator::sineosc() {
|
float Oscillator::SineOsc() {
|
||||||
float result = sinf(m_phase);
|
float result = sinf(m_phase);
|
||||||
sine_osc_phase_incr();
|
SineOscPhaseIncr();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Oscillator::sign(float v) { return (v > 0.0) ? 1.f : -1.f; }
|
float Oscillator::Sign(float v) { return (v > 0.0) ? 1.f : -1.f; }
|
||||||
|
|
||||||
float Oscillator::squareosc() { return sign(sineosc()); }
|
float Oscillator::SquareOsc() { return Sign(SineOsc()); }
|
||||||
|
|
||||||
float Oscillator::triangleosc() {
|
float Oscillator::TriangleOsc() {
|
||||||
float result = 1.f - fabsf(m_phase - 0.5f) * 4.f;
|
float result = 1.f - fabsf(m_phase - 0.5f) * 4.f;
|
||||||
saw_osc_phase_incr();
|
SawOscPhaseIncr();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Oscillator::sawosc() {
|
float Oscillator::SawOsc() {
|
||||||
float result = m_phase * 2.f - 1.f;
|
float result = m_phase * 2.f - 1.f;
|
||||||
saw_osc_phase_incr();
|
SawOscPhaseIncr();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ float Renderer::draw_oscillators_panels(
|
|||||||
|
|
||||||
// Draw Oscillator Panel
|
// Draw Oscillator Panel
|
||||||
const int osc_panel_width = panel_bounds.width - 20;
|
const int osc_panel_width = panel_bounds.width - 20;
|
||||||
const int osc_panel_height = has_shape_param ? 130 : 100;
|
const int osc_panel_height = has_shape_param ? 150 : 120;
|
||||||
const int osc_panel_x = panel_bounds.x + 10;
|
const int osc_panel_x = panel_bounds.x + 10;
|
||||||
const int osc_panel_y = panel_bounds.y + 50 + panel_y_offset;
|
const int osc_panel_y = panel_bounds.y + 50 + panel_y_offset;
|
||||||
panel_y_offset += osc_panel_height + 5;
|
panel_y_offset += osc_panel_height + 5;
|
||||||
@@ -112,13 +112,23 @@ float Renderer::draw_oscillators_panels(
|
|||||||
decibels =
|
decibels =
|
||||||
GuiSlider(el_rect, amp_slider_label, "", decibels, -60.0f, 0.0f);
|
GuiSlider(el_rect, amp_slider_label, "", decibels, -60.0f, 0.0f);
|
||||||
ui_osc->volume = powf(10.f, decibels * (1.f / 20.f));
|
ui_osc->volume = powf(10.f, decibels * (1.f / 20.f));
|
||||||
osc->SetVolume(ui_osc->volume);
|
el_rect.y += el_rect.height + el_spacing;
|
||||||
|
|
||||||
|
// Fine slider
|
||||||
|
float fine = osc->GetFine();
|
||||||
|
char fine_slider_label[10];
|
||||||
|
snprintf(fine_slider_label, 9, "%.3f u", fine);
|
||||||
|
fine = GuiSlider(el_rect, fine_slider_label, "", fine, -2.f, 2.f);
|
||||||
|
ui_osc->fine = fine;
|
||||||
el_rect.y += el_rect.height + el_spacing;
|
el_rect.y += el_rect.height + el_spacing;
|
||||||
|
|
||||||
// Defer shape drop-down box.
|
// Defer shape drop-down box.
|
||||||
ui_osc->shape_dropdown_rect = el_rect;
|
ui_osc->shape_dropdown_rect = el_rect;
|
||||||
el_rect.y += el_rect.height + el_spacing;
|
el_rect.y += el_rect.height + el_spacing;
|
||||||
|
|
||||||
|
// Apply values to real
|
||||||
|
osc->SetVolume(ui_osc->volume);
|
||||||
|
osc->SetFine(ui_osc->fine);
|
||||||
}
|
}
|
||||||
|
|
||||||
return panel_y_offset;
|
return panel_y_offset;
|
||||||
@@ -233,8 +243,8 @@ float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
|
|||||||
// Shape select
|
// Shape select
|
||||||
int shape_index = (int)(gui_filter.type);
|
int shape_index = (int)(gui_filter.type);
|
||||||
bool is_dropdown_click =
|
bool is_dropdown_click =
|
||||||
GuiDropdownBox(el_rect, FILTER_TYPE_OPTIONS,
|
GuiDropdownBox(el_rect, FILTER_TYPE_OPTIONS, &shape_index,
|
||||||
&shape_index, gui_filter.is_dropdown_open);
|
gui_filter.is_dropdown_open);
|
||||||
|
|
||||||
if (is_dropdown_click) {
|
if (is_dropdown_click) {
|
||||||
write_log("Dropdown clicked!\n");
|
write_log("Dropdown clicked!\n");
|
||||||
@@ -247,7 +257,8 @@ float Renderer::DrawFilterPanel(Synth& synth, FilterGuiState& gui_filter,
|
|||||||
// apply values to real one
|
// apply values to real one
|
||||||
// todo: thrid (order) parameter
|
// todo: thrid (order) parameter
|
||||||
// todo: why resonance changing does not work?
|
// todo: why resonance changing does not work?
|
||||||
filter->SetParameters(gui_filter.freq, filter->GetRes(), filter->GetPeakGain());
|
filter->SetParameters(gui_filter.freq, filter->GetRes(),
|
||||||
|
filter->GetPeakGain());
|
||||||
|
|
||||||
return panel_y_offset;
|
return panel_y_offset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,19 @@
|
|||||||
#include "OscillatorType.h"
|
#include "OscillatorType.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "FilterFactory.h"
|
#include "FilterFactory.h"
|
||||||
|
#include "LFO.h"
|
||||||
|
|
||||||
Synth::Synth(/* args */) {
|
Synth::Synth(/* args */) {
|
||||||
m_lfo = new Oscillator(OscillatorType::Sine, 5.f, VOLUME);
|
m_lfo = new LFO();
|
||||||
add_oscillator();
|
AddOscillator();
|
||||||
add_oscillator();
|
AddOscillator();
|
||||||
AddEffect(new ADSR());
|
AddEffect(new ADSR());
|
||||||
AddEffect(FilterFactory::GetDefaultFilter());
|
AddEffect(FilterFactory::GetDefaultFilter());
|
||||||
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
||||||
float sample = 0.0f;
|
float sample = 0.0f;
|
||||||
m_out_signal.push_back(sample);
|
m_out_signal.push_back(sample);
|
||||||
}
|
}
|
||||||
zero_signal();
|
ZeroSignal();
|
||||||
}
|
}
|
||||||
|
|
||||||
Synth::~Synth() {
|
Synth::~Synth() {
|
||||||
@@ -24,54 +25,53 @@ Synth::~Synth() {
|
|||||||
m_out_signal.clear();
|
m_out_signal.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::zero_signal() {
|
void Synth::ZeroSignal() {
|
||||||
float sample = 0.0f;
|
float sample = 0.0f;
|
||||||
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
for (size_t i = 0; i < STREAM_BUFFER_SIZE; i++) {
|
||||||
m_out_signal[i] = sample;
|
m_out_signal[i] = sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::get_note() {
|
void Synth::GetNote() {
|
||||||
zero_signal();
|
ZeroSignal();
|
||||||
Adder::SumOscillators(m_oscillators, m_out_signal);
|
Adder::SumOscillators(m_oscillators, m_out_signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::apply_effects() {
|
void Synth::ApplyEffects() {
|
||||||
for (Effect* effect : m_effects) {
|
for (IEffect* effect : m_effects) {
|
||||||
effect->Process(m_out_signal);
|
effect->Process(m_out_signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::trigger_note_on_effects() {
|
void Synth::TriggerNoteOnEffects() {
|
||||||
for (Effect* effect : m_effects) {
|
for (IEffect* effect : m_effects) {
|
||||||
effect->Trigger();
|
effect->Trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::untrigger_note_on_effects() {
|
void Synth::UntriggerNoteOnEffects() {
|
||||||
for (Effect* effect : m_effects) {
|
for (IEffect* effect : m_effects) {
|
||||||
effect->Release();
|
effect->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::add_oscillator() {
|
void Synth::AddOscillator() {
|
||||||
m_oscillators.push_back(
|
m_oscillators.push_back(
|
||||||
new Oscillator(OscillatorType::Sine, 440.f, VOLUME));
|
new Oscillator(OscillatorType::Sine, 0.0f, VOLUME));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Trigger(Note input) {
|
void Synth::Trigger(Note input) {
|
||||||
int semitone_shift = KeyBoard::GetSemitoneShift(input.name);
|
int semitone_shift = KeyBoard::GetSemitoneShift(input.name);
|
||||||
float hz = KeyBoard::GetHzBySemitone(semitone_shift);
|
|
||||||
|
|
||||||
for (Oscillator* osc : m_oscillators) {
|
for (Oscillator* osc : m_oscillators) {
|
||||||
osc->SetFreq(hz);
|
osc->SetKey(semitone_shift);
|
||||||
}
|
}
|
||||||
is_note_triggered = true;
|
is_note_triggered = true;
|
||||||
trigger_note_on_effects();
|
TriggerNoteOnEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: fix this
|
// todo: fix this
|
||||||
void Synth::apply_filter_lfo() {
|
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();
|
||||||
@@ -82,18 +82,18 @@ void Synth::apply_filter_lfo() {
|
|||||||
void Synth::Process() {
|
void Synth::Process() {
|
||||||
//todo: on each sample.
|
//todo: on each sample.
|
||||||
//in order to do that, we need to move to per-sample processing
|
//in order to do that, we need to move to per-sample processing
|
||||||
apply_filter_lfo();
|
//ApplyFilterLfo();
|
||||||
get_note();
|
GetNote();
|
||||||
apply_effects();
|
ApplyEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Release() {
|
void Synth::Release() {
|
||||||
zero_signal();
|
ZeroSignal();
|
||||||
is_note_triggered = false;
|
is_note_triggered = false;
|
||||||
untrigger_note_on_effects();
|
UntriggerNoteOnEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::AddEffect(Effect* fx) { m_effects.push_back(fx); }
|
void Synth::AddEffect(IEffect* fx) { m_effects.push_back(fx); }
|
||||||
|
|
||||||
void Synth::SetFilter(FilterType type) {
|
void Synth::SetFilter(FilterType type) {
|
||||||
Filter* old_filter = this->GetFilter();
|
Filter* old_filter = this->GetFilter();
|
||||||
|
|||||||
Reference in New Issue
Block a user