diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 8f055d0..70ba98a 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -81,4 +81,12 @@ instrument.applyEnvelope(envelope) } } + + override fun updateEffects() { + for (instrument in internalInstruments) { + for (effect in effects) { + instrument.applyEffectAttributes(effect) + } + } + } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 8f055d0..70ba98a 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -81,4 +81,12 @@ instrument.applyEnvelope(envelope) } } + + override fun updateEffects() { + for (instrument in internalInstruments) { + for (effect in effects) { + instrument.applyEffectAttributes(effect) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index ebc64d1..8a3c4eb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -10,4 +10,17 @@ package com.lukas.music.instruments.effect -class Effect(val type: EffectType) \ No newline at end of file +import com.lukas.music.instruments.Instrument + +class Effect(val type: EffectType, private val instrument: Instrument) { + var parameter1: Float = 1.0f + set(value) { + field = value + instrument.updateEffects() + } + var parameter1Percent: Int + get() = (parameter1 * 100).toInt() + set(value) { + parameter1 = value.toFloat() / 100f + } +} \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 8f055d0..70ba98a 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -81,4 +81,12 @@ instrument.applyEnvelope(envelope) } } + + override fun updateEffects() { + for (instrument in internalInstruments) { + for (effect in effects) { + instrument.applyEffectAttributes(effect) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index ebc64d1..8a3c4eb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -10,4 +10,17 @@ package com.lukas.music.instruments.effect -class Effect(val type: EffectType) \ No newline at end of file +import com.lukas.music.instruments.Instrument + +class Effect(val type: EffectType, private val instrument: Instrument) { + var parameter1: Float = 1.0f + set(value) { + field = value + instrument.updateEffects() + } + var parameter1Percent: Int + get() = (parameter1 * 100).toInt() + set(value) { + parameter1 = value.toFloat() / 100f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8b7767b..98edbbb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -10,8 +10,13 @@ package com.lukas.music.instruments.effect -enum class EffectType(val description: String, val parameter1Name: String) { - LowPass("low pass filter", "cutoff frequency"), +import kotlin.math.roundToInt + +enum class EffectType( + val description: String, + val parameter1Text: (Int) -> String, +) { + LowPass("low pass filter", { "cutoff: ${(it.toFloat() / 100f).roundToInt()} octaves" }), ; companion object { diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 8f055d0..70ba98a 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -81,4 +81,12 @@ instrument.applyEnvelope(envelope) } } + + override fun updateEffects() { + for (instrument in internalInstruments) { + for (effect in effects) { + instrument.applyEffectAttributes(effect) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index ebc64d1..8a3c4eb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -10,4 +10,17 @@ package com.lukas.music.instruments.effect -class Effect(val type: EffectType) \ No newline at end of file +import com.lukas.music.instruments.Instrument + +class Effect(val type: EffectType, private val instrument: Instrument) { + var parameter1: Float = 1.0f + set(value) { + field = value + instrument.updateEffects() + } + var parameter1Percent: Int + get() = (parameter1 * 100).toInt() + set(value) { + parameter1 = value.toFloat() / 100f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8b7767b..98edbbb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -10,8 +10,13 @@ package com.lukas.music.instruments.effect -enum class EffectType(val description: String, val parameter1Name: String) { - LowPass("low pass filter", "cutoff frequency"), +import kotlin.math.roundToInt + +enum class EffectType( + val description: String, + val parameter1Text: (Int) -> String, +) { + LowPass("low pass filter", { "cutoff: ${(it.toFloat() / 100f).roundToInt()} octaves" }), ; companion object { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index b040f9d..c004620 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -17,6 +17,7 @@ import androidx.fragment.app.Fragment import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect +import com.lukas.music.util.smartSetup class EffectFragment(private val effect: Effect) : Fragment() { lateinit var binding: FragmentEffectBinding @@ -27,6 +28,9 @@ ): View? { binding = FragmentEffectBinding.inflate(inflater) binding.effectName.text = effect.type.description + binding.parameter1SeekBar.smartSetup(-100, 300, effect::parameter1Percent) { + binding.parameter1Text.text = effect.type.parameter1Text(it) + } return binding.root } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bf86c48..1a41dfa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/cpp/AudioHost.cpp b/app/src/main/cpp/AudioHost.cpp index 8e98b44..6727405 100644 --- a/app/src/main/cpp/AudioHost.cpp +++ b/app/src/main/cpp/AudioHost.cpp @@ -1,7 +1,9 @@ #include "AudioHost.h" +#include "Instrument.h" #include #include #include +#include const uint32_t bufferSize = 2; aaudio_data_callback_result_t dataCallback( diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index f439fdc..97446ea 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,8 @@ class AudioHost; -#include "Instrument.h" +class Instrument; + #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b94b511..8588bed 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,8 @@ Instrument.cpp effects/Envelope.cpp effects/Processable.cpp + effects/Effect.cpp + effects/LowPass.cpp ) find_library( diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index d7263ed..985e93e 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -9,6 +9,7 @@ wave = new Sine(); wave->host = host; envelope->initialize(host); + lowPass->host = host; } void multiply(float *target, float *modulation, uint32_t size) { @@ -27,12 +28,16 @@ float *modulation = envelope->render(count); float *waveform = wave->render(count); multiply(waveform, modulation, count); - add(buffer, waveform, count); + lowPass->input = waveform; + float *filtered = lowPass->render(count); + add(buffer, filtered, count); } void Instrument::startNote(float frequency) { wave->setFrequency(frequency); envelope->startNote(); + lowPass->frequency = frequency; + lowPass->update(); } void Instrument::endNote() { diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index f15d631..3ab90a8 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -6,6 +6,7 @@ #include "effects/Envelope.h" #include "waveforms/Waveform.h" #include "AudioHost.h" +#include "effects/LowPass.h" class Instrument { private: @@ -15,6 +16,7 @@ Envelope *const envelope = new Envelope(); Waveform *wave; + LowPass *lowPass = new LowPass(); void render(float *buffer, uint32_t count); void startNote(float frequency); diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index d26ee2b..cb732f2 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -1,6 +1,7 @@ #include #include #include "AudioHost.h" +#include "Instrument.h" #include #include #include @@ -92,4 +93,19 @@ envelope->release = release; envelope->update(); } + +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_applyEffectAttributes(JNIEnv *env, jobject thiz, + jint id, + jint effect_number, + jfloat parameter1) { + Instrument *instrument = getInstrument(id); + Effect *effect; + switch (effect_number) { + case 0: + effect = instrument->lowPass; + break; + } + effect->parameter1 = parameter1; +} } \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.cpp b/app/src/main/cpp/effects/Effect.cpp new file mode 100644 index 0000000..881e413 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.cpp @@ -0,0 +1 @@ +#include "Effect.h" \ No newline at end of file diff --git a/app/src/main/cpp/effects/Effect.h b/app/src/main/cpp/effects/Effect.h new file mode 100644 index 0000000..d4777f9 --- /dev/null +++ b/app/src/main/cpp/effects/Effect.h @@ -0,0 +1,19 @@ +#ifndef MUSIC_EFFECT_H +#define MUSIC_EFFECT_H + +#include +#include "Processable.h" +#include "../AudioHost.h" + +class Effect : public Processable { +public: + float parameter1, frequency; + float *input; + AudioHost *host; + + virtual void doRender(uint32_t sampleCount) = 0; + + virtual void update() = 0; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.cpp b/app/src/main/cpp/effects/LowPass.cpp new file mode 100644 index 0000000..8336798 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.cpp @@ -0,0 +1,18 @@ +#include "../Instrument.h" +#include + +// direct model of a RC-filter, R = 1 Ohm for convenience + +void LowPass::update() { + charge = 0; + inverseCapacitance = 2 * M_PI * frequency * pow(2, parameter1); + capacitance = 1 / inverseCapacitance; + timeStep = 1 / (float) host->sampleRate; +} + +void LowPass::doRender(uint32_t sampleCount) { + for (uint32_t i = 0; i < sampleCount; i++) { + charge += (input[i] - charge * inverseCapacitance) * timeStep; + buffer[i] = charge * inverseCapacitance; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/effects/LowPass.h b/app/src/main/cpp/effects/LowPass.h new file mode 100644 index 0000000..a285154 --- /dev/null +++ b/app/src/main/cpp/effects/LowPass.h @@ -0,0 +1,18 @@ +#ifndef MUSIC_LOWPASS_H +#define MUSIC_LOWPASS_H + +#include "Effect.h" + +class LowPass : public Effect { +private: + float charge = 0; + float capacitance = 0; + float inverseCapacitance = 0; + float timeStep = 0; +public: + void update(); + + void doRender(uint32_t sampleCount); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 53ec4ad..46957ee 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -20,7 +20,7 @@ var voice: Voice = BassVoice(this) var envelope = Envelope(this) val effects = Array(EffectType.VALUES.size) { - Effect(EffectType.VALUES[it]) + Effect(EffectType.VALUES[it], this) } abstract var waveform: Waveform @@ -32,6 +32,7 @@ abstract fun stopNote(note: Note) abstract fun destroy() abstract fun updateEnvelope() + abstract fun updateEffects() companion object { val instruments = mutableListOf() diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 946e653..f7983ad 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -10,6 +10,7 @@ package com.lukas.music.instruments +import com.lukas.music.instruments.effect.Effect import com.lukas.music.song.note.Note class InternalInstrument { @@ -77,6 +78,10 @@ ) } + fun applyEffectAttributes(effect: Effect) { + applyEffectAttributes(id, effect.type.ordinal, effect.parameter1) + } + private external fun createInstrument(): Int private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) @@ -90,4 +95,6 @@ sustain: Float, release: Float ) + + private external fun applyEffectAttributes(id: Int, effectNumber: Int, parameter1: Float) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index a9b5c53..3526e60 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -57,4 +57,10 @@ override fun updateEnvelope() { internalInstrument.applyEnvelope(envelope) } + + override fun updateEffects() { + for (effect in effects) { + internalInstrument.applyEffectAttributes(effect) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 8f055d0..70ba98a 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -81,4 +81,12 @@ instrument.applyEnvelope(envelope) } } + + override fun updateEffects() { + for (instrument in internalInstruments) { + for (effect in effects) { + instrument.applyEffectAttributes(effect) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt index ebc64d1..8a3c4eb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/Effect.kt @@ -10,4 +10,17 @@ package com.lukas.music.instruments.effect -class Effect(val type: EffectType) \ No newline at end of file +import com.lukas.music.instruments.Instrument + +class Effect(val type: EffectType, private val instrument: Instrument) { + var parameter1: Float = 1.0f + set(value) { + field = value + instrument.updateEffects() + } + var parameter1Percent: Int + get() = (parameter1 * 100).toInt() + set(value) { + parameter1 = value.toFloat() / 100f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt index 8b7767b..98edbbb 100644 --- a/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt +++ b/app/src/main/java/com/lukas/music/instruments/effect/EffectType.kt @@ -10,8 +10,13 @@ package com.lukas.music.instruments.effect -enum class EffectType(val description: String, val parameter1Name: String) { - LowPass("low pass filter", "cutoff frequency"), +import kotlin.math.roundToInt + +enum class EffectType( + val description: String, + val parameter1Text: (Int) -> String, +) { + LowPass("low pass filter", { "cutoff: ${(it.toFloat() / 100f).roundToInt()} octaves" }), ; companion object { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt index b040f9d..c004620 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EffectFragment.kt @@ -17,6 +17,7 @@ import androidx.fragment.app.Fragment import com.lukas.music.databinding.FragmentEffectBinding import com.lukas.music.instruments.effect.Effect +import com.lukas.music.util.smartSetup class EffectFragment(private val effect: Effect) : Fragment() { lateinit var binding: FragmentEffectBinding @@ -27,6 +28,9 @@ ): View? { binding = FragmentEffectBinding.inflate(inflater) binding.effectName.text = effect.type.description + binding.parameter1SeekBar.smartSetup(-100, 300, effect::parameter1Percent) { + binding.parameter1Text.text = effect.type.parameter1Text(it) + } return binding.root } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_effect.xml b/app/src/main/res/layout/fragment_effect.xml index ff90aa8..7e88115 100644 --- a/app/src/main/res/layout/fragment_effect.xml +++ b/app/src/main/res/layout/fragment_effect.xml @@ -11,13 +11,10 @@ - - + + + +