diff --git a/inc/KeyBoard.h b/inc/KeyBoard.h new file mode 100644 index 0000000..98bd90f --- /dev/null +++ b/inc/KeyBoard.h @@ -0,0 +1,80 @@ +#pragma once +#include "Settings.h" +#include +#include +#include +#include + +class KeyBoard +{ +private: + /* data */ + static int get_semitone_shift_internal(char* root_note, char* target_note) { + char* pitch_classes[12] = + { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; + + // Extract the note number and pitch class for the root note + int root_note_num = (int)root_note[strlen(root_note) - 1] - '0'; + + char* root_pitch_class_str = (char*)malloc((strlen(root_note) - 1) * sizeof(char)); + strncpy(root_pitch_class_str, root_note, strlen(root_note) - 1); + + int root_pitch_class = -1; + + for (int i = 0; i < 12; i++) { + if (strcmp(pitch_classes[i], root_pitch_class_str) == 0) { + root_pitch_class = i; + break; + } + } + + free(root_pitch_class_str); + + // Extract the note number and pitch class for the target note + int target_note_num = (int)target_note[strlen(target_note) - 1] - '0'; + + char* target_pitch_class_str = + (char*)malloc((strlen(target_note) - 1) * sizeof(char)); + strncpy(target_pitch_class_str, target_note, strlen(target_note) - 1); + + int target_pitch_class = -1; + + for (int i = 0; i < 12; i++) { + if (strcmp(pitch_classes[i], target_pitch_class_str) == 0) { + target_pitch_class = i; + break; + } + } + + free(target_pitch_class_str); + + // Calculate the semitone shift using the formula + return (target_note_num - root_note_num) * 12 + + (target_pitch_class - root_pitch_class); + } +public: + KeyBoard(/* args */); + ~KeyBoard(); + + static float GetHzBySemitone(int semitone) { + return PITCH_STANDARD * powf(powf(2.f, (1.f / 12.f)), semitone); + } + + static int GetSemitoneShift(const std::string& target_note) { + char* target_note_cstr = new char[target_note.length() + 1]; + strcpy(target_note_cstr, target_note.c_str()); + + int result = get_semitone_shift_internal("A4", target_note_cstr); + + delete target_note_cstr; + return result; + } +}; + +KeyBoard::KeyBoard(/* args */) +{ +} + +KeyBoard::~KeyBoard() +{ +} diff --git a/inc/Oscillator.h b/inc/Oscillator.h index 0e53dbb..24d54f5 100644 --- a/inc/Oscillator.h +++ b/inc/Oscillator.h @@ -7,6 +7,7 @@ typedef float (Oscillator::*DtFunction)(float); class Oscillator { + private: OscillatorType m_osc; float m_freq; diff --git a/inc/Synth.h b/inc/Synth.h new file mode 100644 index 0000000..9b10b48 --- /dev/null +++ b/inc/Synth.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "Oscillator.h" +#include "Note.h" + +class Synth +{ +private: + std::vector m_oscillators; + //OscillatorUI* ui_oscillators; + Note m_current_note; + std::vector m_out_signal; + std::vector & sum_oscillators(float duration); + std::vector & note(int semitone, float beats); + +public: + Synth(/* args */); + ~Synth(); + std::vector & GetNoteSound(Note input) +}; + +Synth::Synth(/* args */) +{ + m_oscillators.push_back(new Oscillator(OscillatorType.Sine, 440.f, VOLUME)); +} + +Synth::~Synth() +{ +} \ No newline at end of file diff --git a/src/Oscillator.cpp b/src/Oscillator.cpp index 37bf1a0..f3d63e9 100644 --- a/src/Oscillator.cpp +++ b/src/Oscillator.cpp @@ -56,47 +56,56 @@ float Oscillator::GenerateSample(float duration) return m_osc_function() * m_volume; } -void Oscillator::sine_osc_phase_incr() { +void Oscillator::sine_osc_phase_incr() +{ m_phase += m_phase_dt; if (m_phase >= TWO_PI) m_phase -= TWO_PI; } -void Oscillator::saw_osc_phase_incr() { +void Oscillator::saw_osc_phase_incr() +{ m_phase += m_phase_dt; if (m_phase >= 1.0f) m_phase -= 1.0f; } -float Oscillator::calc_saw_phase_delta(float freq) { +float Oscillator::calc_saw_phase_delta(float freq) +{ return freq / SAMPLE_RATE; } -float Oscillator::calc_sine_phase_delta(float freq) { +float Oscillator::calc_sine_phase_delta(float freq) +{ return (TWO_PI * freq) / SAMPLE_RATE; } -float Oscillator::sineosc() { +float Oscillator::sineosc() +{ float result = sinf(m_phase); sine_osc_phase_incr(); return result; } -float Oscillator::sign(float v) { +float Oscillator::sign(float v) +{ return (v > 0.0) ? 1.f : -1.f; } -float Oscillator::squareosc() { +float Oscillator::squareosc() +{ return sign(sineosc()); } -float Oscillator::triangleosc() { +float Oscillator::triangleosc() +{ float result = 1.f - fabsf(m_phase - 0.5f) * 4.f; saw_osc_phase_incr(); return result; } -float Oscillator::sawosc() { +float Oscillator::sawosc() +{ float result = m_phase * 2.f - 1.f; saw_osc_phase_incr(); return result; diff --git a/src/Synth.cpp b/src/Synth.cpp new file mode 100644 index 0000000..02fa8b6 --- /dev/null +++ b/src/Synth.cpp @@ -0,0 +1,45 @@ +#include "Synth.h" +#include "Settings.h" +#include "KeyBoard.h" +#include "OscillatorType.h" +#include "Note.h" + +std::vector & Synth::sum_oscillators(float duration) +{ + size_t sample_count = (size_t)(duration * SAMPLE_RATE); + + std::vector output;// = new std::vector(); + output.reserve(sample_count); + + for (size_t i = 0; i < sample_count; i++) + { + float sample = 0.0f; + for (Oscillator& osc : m_oscillators) + { + sample += osc.GenerateSample(duration); + } + + output.push_back(sample); + } + + return output; +} + +std::vector & Synth::note(int semitone, float beats) +{ + float hz = KeyBoard::GetHzBySemitone(semitone); + float duration = beats * BEAT_DURATION; + + // will change after oscillator starts to be more autonomous + for (size_t i = 0; i < m_oscillators.size(); i++) { + m_oscillators[i].SetFreq(hz); + } + + return sum_oscillators(duration); +} + +std::vector & Synth::GetNoteSound(Note input) { + float length = 1.f / input.length; + int semitone_shift = KeyBoard::GetSemitoneShift(input.name); + return note(semitone_shift, length); +}