diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ effect->parameter1 = parameter1;
+}
}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Triangle.cpp b/app/src/main/cpp/waveforms/Triangle.cpp
new file mode 100644
index 0000000..2ef7d1f
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.cpp
@@ -0,0 +1,19 @@
+#include "Triangle.h"
+
+void Triangle::setFrequency(float frequency) {
+ step = 4 * frequency / (double) host->sampleRate;
+}
+
+void Triangle::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = value;
+ value += step;
+ if (value > 1) {
+ step *= -1;
+ value = 1;
+ } else if (value < -1) {
+ step *= -1;
+ value = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Triangle.cpp b/app/src/main/cpp/waveforms/Triangle.cpp
new file mode 100644
index 0000000..2ef7d1f
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.cpp
@@ -0,0 +1,19 @@
+#include "Triangle.h"
+
+void Triangle::setFrequency(float frequency) {
+ step = 4 * frequency / (double) host->sampleRate;
+}
+
+void Triangle::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = value;
+ value += step;
+ if (value > 1) {
+ step *= -1;
+ value = 1;
+ } else if (value < -1) {
+ step *= -1;
+ value = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Triangle.h b/app/src/main/cpp/waveforms/Triangle.h
new file mode 100644
index 0000000..788dc2d
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.h
@@ -0,0 +1,17 @@
+#ifndef MUSIC_TRIANGLE_H
+#define MUSIC_TRIANGLE_H
+
+
+#include "Waveform.h"
+
+class Triangle : public Waveform {
+private:
+ float value = 0, step = 0;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+
+#endif
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Triangle.cpp b/app/src/main/cpp/waveforms/Triangle.cpp
new file mode 100644
index 0000000..2ef7d1f
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.cpp
@@ -0,0 +1,19 @@
+#include "Triangle.h"
+
+void Triangle::setFrequency(float frequency) {
+ step = 4 * frequency / (double) host->sampleRate;
+}
+
+void Triangle::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = value;
+ value += step;
+ if (value > 1) {
+ step *= -1;
+ value = 1;
+ } else if (value < -1) {
+ step *= -1;
+ value = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Triangle.h b/app/src/main/cpp/waveforms/Triangle.h
new file mode 100644
index 0000000..788dc2d
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.h
@@ -0,0 +1,17 @@
+#ifndef MUSIC_TRIANGLE_H
+#define MUSIC_TRIANGLE_H
+
+
+#include "Waveform.h"
+
+class Triangle : public Waveform {
+private:
+ float value = 0, step = 0;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Waveform.cpp b/app/src/main/cpp/waveforms/Waveform.cpp
index 64e0991..839ca10 100644
--- a/app/src/main/cpp/waveforms/Waveform.cpp
+++ b/app/src/main/cpp/waveforms/Waveform.cpp
@@ -2,7 +2,4 @@
void Waveform::doRender(uint32_t sampleCount) {
renderWaveform(sampleCount);
- for (uint32_t i = 0; i < sampleCount; i++) {
- buffer[i] *= amplitude;
- }
}
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Triangle.cpp b/app/src/main/cpp/waveforms/Triangle.cpp
new file mode 100644
index 0000000..2ef7d1f
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.cpp
@@ -0,0 +1,19 @@
+#include "Triangle.h"
+
+void Triangle::setFrequency(float frequency) {
+ step = 4 * frequency / (double) host->sampleRate;
+}
+
+void Triangle::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = value;
+ value += step;
+ if (value > 1) {
+ step *= -1;
+ value = 1;
+ } else if (value < -1) {
+ step *= -1;
+ value = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Triangle.h b/app/src/main/cpp/waveforms/Triangle.h
new file mode 100644
index 0000000..788dc2d
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.h
@@ -0,0 +1,17 @@
+#ifndef MUSIC_TRIANGLE_H
+#define MUSIC_TRIANGLE_H
+
+
+#include "Waveform.h"
+
+class Triangle : public Waveform {
+private:
+ float value = 0, step = 0;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Waveform.cpp b/app/src/main/cpp/waveforms/Waveform.cpp
index 64e0991..839ca10 100644
--- a/app/src/main/cpp/waveforms/Waveform.cpp
+++ b/app/src/main/cpp/waveforms/Waveform.cpp
@@ -2,7 +2,4 @@
void Waveform::doRender(uint32_t sampleCount) {
renderWaveform(sampleCount);
- for (uint32_t i = 0; i < sampleCount; i++) {
- buffer[i] *= amplitude;
- }
}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h
index a70ed39..34e1284 100644
--- a/app/src/main/cpp/waveforms/Waveform.h
+++ b/app/src/main/cpp/waveforms/Waveform.h
@@ -6,6 +6,8 @@
enum WaveformType {
SINE = 0,
SAWTOOTH = 1,
+ SQUARE = 2,
+ TRIANGLE = 3,
};
#include "../effects/Processable.h"
@@ -13,7 +15,6 @@
class Waveform : public Processable {
public:
- float amplitude = 0.0f;
AudioHost *host;
void doRender(uint32_t sampleCount);
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3d886c4..b121fe2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,8 +11,11 @@
+
+
-
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 97ec2a0..9de986d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,4 +65,5 @@
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.github.alanvan0502:segmented-control-group:v1.0'
}
\ No newline at end of file
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 e7286cb..39bdefe 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -10,11 +10,16 @@
waveforms/Sine.cpp
waveforms/Sawtooth.cpp
waveforms/Waveform.cpp
+ waveforms/Square.cpp
+ waveforms/Triangle.cpp
JavaFunctions.cpp
AudioHost.cpp
Instrument.cpp
effects/Envelope.cpp
effects/Processable.cpp
+ effects/Effect.cpp
+ effects/LowPass.cpp
+ effects/Noise.cpp
)
find_library(
diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp
index 3d9396d..7310a37 100644
--- a/app/src/main/cpp/Instrument.cpp
+++ b/app/src/main/cpp/Instrument.cpp
@@ -1,12 +1,16 @@
#include "Instrument.h"
#include "waveforms/Sawtooth.h"
#include "waveforms/Sine.h"
+#include "waveforms/Square.h"
+#include "waveforms/Triangle.h"
Instrument::Instrument(AudioHost *host) {
this->host = host;
wave = new Sine();
wave->host = host;
envelope->initialize(host);
+ lowPass->host = host;
+ noise->host = host;
}
void multiply(float *target, float *modulation, uint32_t size) {
@@ -15,22 +19,43 @@
}
}
+void multiply(float *target, float value, uint32_t size) {
+ for (uint32_t i = 0; i < size; i++) {
+ target[i] *= value;
+ }
+}
+
void add(float *target, float *other, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
target[i] += other[i];
}
}
+void processEffect(float *waveform, uint32_t count, Effect *effect) {
+ if (effect->influence < 0.01f) {
+ return;
+ }
+ effect->input = waveform;
+ float *effectOutput = effect->render(count);
+ multiply(effectOutput, effect->influence, count);
+ multiply(waveform, 1 - effect->influence, count);
+ add(waveform, effectOutput, count);
+}
+
void Instrument::render(float *buffer, uint32_t count) {
- float *modulation = envelope->render(count);
float *waveform = wave->render(count);
- multiply(waveform, modulation, count);
+ processEffect(waveform, count, lowPass);
+ processEffect(waveform, count, noise);
+ multiply(waveform, envelope->render(count), count);
+ multiply(waveform, volume, count);
add(buffer, waveform, count);
}
void Instrument::startNote(float frequency) {
wave->setFrequency(frequency);
envelope->startNote();
+ lowPass->frequency = frequency;
+ lowPass->update();
}
void Instrument::endNote() {
@@ -46,6 +71,11 @@
case SAWTOOTH:
wave = new Sawtooth();
break;
+ case SQUARE:
+ wave = new Square();
+ break;
+ case TRIANGLE:
+ wave = new Triangle();
}
wave->host = host;
delete old;
diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h
index f15d631..077bfe0 100644
--- a/app/src/main/cpp/Instrument.h
+++ b/app/src/main/cpp/Instrument.h
@@ -6,6 +6,8 @@
#include "effects/Envelope.h"
#include "waveforms/Waveform.h"
#include "AudioHost.h"
+#include "effects/LowPass.h"
+#include "effects/Noise.h"
class Instrument {
private:
@@ -15,10 +17,16 @@
Envelope *const envelope = new Envelope();
Waveform *wave;
+ LowPass *lowPass = new LowPass();
+ Noise *noise = new Noise();
+ float volume = 0;
void render(float *buffer, uint32_t count);
+
void startNote(float frequency);
+
void endNote();
+
void setWaveform(WaveformType waveform);
};
diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp
index 5505a35..7ccc60b 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
@@ -68,7 +69,7 @@
JNIEXPORT void JNICALL
Java_com_lukas_music_instruments_InternalInstrument_setVolume(JNIEnv *env, jobject thiz, jint id,
jfloat volume) {
- getInstrument(id)->wave->amplitude = volume;
+ getInstrument(id)->volume = volume;
}
JNIEXPORT void JNICALL
@@ -76,4 +77,40 @@
listSet(audioHost->instruments->begin(), id, nullptr);
delete getInstrument(id);
}
+
+JNIEXPORT void JNICALL
+Java_com_lukas_music_instruments_InternalInstrument_updateEnvelopeParameters(JNIEnv *env,
+ jobject thiz, jint id,
+ jfloat attack,
+ jfloat delay,
+ jfloat sustain,
+ jfloat release) {
+ Instrument *instrument = getInstrument((id));
+ Envelope *envelope = instrument->envelope;
+ envelope->attack = attack;
+ envelope->delay = delay;
+ envelope->sustain = sustain;
+ 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 influence,
+ jfloat parameter1) {
+ Instrument *instrument = getInstrument(id);
+ Effect *effect;
+ switch (effect_number) {
+ case 0:
+ effect = instrument->lowPass;
+ break;
+ case 1:
+ effect = instrument->noise;
+ break;
+ }
+ effect->influence = influence;
+ 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..80ba84c
--- /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, influence;
+ 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/Envelope.cpp b/app/src/main/cpp/effects/Envelope.cpp
index f8dfd61..5400548 100644
--- a/app/src/main/cpp/effects/Envelope.cpp
+++ b/app/src/main/cpp/effects/Envelope.cpp
@@ -3,6 +3,11 @@
#include "Envelope.h"
void Envelope::initialize(AudioHost *host) {
+ this->host = host;
+ update();
+}
+
+void Envelope::update() {
attackIncrement = 1 / attack / host->sampleRate;
delayIncrement = 1 / delay / host->sampleRate;
releaseIncrement = 1 / release / host->sampleRate;
diff --git a/app/src/main/cpp/effects/Envelope.h b/app/src/main/cpp/effects/Envelope.h
index 7076f8f..dbb5575 100644
--- a/app/src/main/cpp/effects/Envelope.h
+++ b/app/src/main/cpp/effects/Envelope.h
@@ -16,15 +16,16 @@
EnvelopePhase phase;
float attackIncrement, delayIncrement, releaseIncrement;
float value = 0;
+ AudioHost *host;
public:
- float attack = 0.05, delay = 0.2, sustain = 0.75, release = 1;
+ float attack = 0.05, delay = 0.05, sustain = 0.7, release = 0.1;
void initialize(AudioHost *host);
+ void update();
+
void startNote();
-
void endNote();
-
void doRender(uint32_t sampleCount);
};
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/cpp/effects/Noise.cpp b/app/src/main/cpp/effects/Noise.cpp
new file mode 100644
index 0000000..3ecfd2e
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.cpp
@@ -0,0 +1,21 @@
+#include "Noise.h"
+#include
+#include
+
+void Noise::update() {
+ srand(time(0));
+}
+
+const static int q = 15;
+const static float c1 = (1 << q) - 1;
+const static float c2 = ((int) (c1 / 3)) + 1;
+const static float c3 = 1.f / c1;
+
+void Noise::doRender(uint32_t sampleCount) {
+ for (uint32_t i = 0; i < sampleCount; i++) {
+ float random = ((float) rand() / (float) (RAND_MAX + 1));
+ buffer[i] =
+ (2.f * ((random * c2) + (random * c2) + (random * c2)) - 3.f * (c2 - 1.f)) * c3 /
+ 10;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/effects/Noise.h b/app/src/main/cpp/effects/Noise.h
new file mode 100644
index 0000000..287e4ac
--- /dev/null
+++ b/app/src/main/cpp/effects/Noise.h
@@ -0,0 +1,13 @@
+#ifndef MUSIC_NOISE_H
+#define MUSIC_NOISE_H
+
+#include "Effect.h"
+
+class Noise : public Effect {
+ void update();
+
+ void doRender(uint32_t sampleCount);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Square.cpp b/app/src/main/cpp/waveforms/Square.cpp
new file mode 100644
index 0000000..d136bca
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.cpp
@@ -0,0 +1,17 @@
+#include
+#include "Square.h"
+
+void Square::setFrequency(float frequency) {
+ period = 1 / frequency;
+ step = 1 / (double) host->sampleRate;
+}
+
+void Square::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = position > period / 2 ? 1 : -1;
+ position += step;
+ if (position > period) {
+ position = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Square.h b/app/src/main/cpp/waveforms/Square.h
new file mode 100644
index 0000000..abcc39a
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Square.h
@@ -0,0 +1,15 @@
+#ifndef MUSIC_SQUARE_H
+#define MUSIC_SQUARE_H
+
+#include "Waveform.h"
+
+class Square : public Waveform {
+private:
+ float position, period, step;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Triangle.cpp b/app/src/main/cpp/waveforms/Triangle.cpp
new file mode 100644
index 0000000..2ef7d1f
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.cpp
@@ -0,0 +1,19 @@
+#include "Triangle.h"
+
+void Triangle::setFrequency(float frequency) {
+ step = 4 * frequency / (double) host->sampleRate;
+}
+
+void Triangle::renderWaveform(uint32_t frameCount) {
+ for (uint32_t i = 0; i < frameCount; i++) {
+ buffer[i] = value;
+ value += step;
+ if (value > 1) {
+ step *= -1;
+ value = 1;
+ } else if (value < -1) {
+ step *= -1;
+ value = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Triangle.h b/app/src/main/cpp/waveforms/Triangle.h
new file mode 100644
index 0000000..788dc2d
--- /dev/null
+++ b/app/src/main/cpp/waveforms/Triangle.h
@@ -0,0 +1,17 @@
+#ifndef MUSIC_TRIANGLE_H
+#define MUSIC_TRIANGLE_H
+
+
+#include "Waveform.h"
+
+class Triangle : public Waveform {
+private:
+ float value = 0, step = 0;
+public:
+ void renderWaveform(uint32_t frameCount);
+
+ void setFrequency(float freq);
+};
+
+
+#endif
diff --git a/app/src/main/cpp/waveforms/Waveform.cpp b/app/src/main/cpp/waveforms/Waveform.cpp
index 64e0991..839ca10 100644
--- a/app/src/main/cpp/waveforms/Waveform.cpp
+++ b/app/src/main/cpp/waveforms/Waveform.cpp
@@ -2,7 +2,4 @@
void Waveform::doRender(uint32_t sampleCount) {
renderWaveform(sampleCount);
- for (uint32_t i = 0; i < sampleCount; i++) {
- buffer[i] *= amplitude;
- }
}
\ No newline at end of file
diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h
index a70ed39..34e1284 100644
--- a/app/src/main/cpp/waveforms/Waveform.h
+++ b/app/src/main/cpp/waveforms/Waveform.h
@@ -6,6 +6,8 @@
enum WaveformType {
SINE = 0,
SAWTOOTH = 1,
+ SQUARE = 2,
+ TRIANGLE = 3,
};
#include "../effects/Processable.h"
@@ -13,7 +15,6 @@
class Waveform : public Processable {
public:
- float amplitude = 0.0f;
AudioHost *host;
void doRender(uint32_t sampleCount);
diff --git a/app/src/main/java/com/lukas/music/EditVoiceFragment.kt b/app/src/main/java/com/lukas/music/EditVoiceFragment.kt
deleted file mode 100644
index 56f21e6..0000000
--- a/app/src/main/java/com/lukas/music/EditVoiceFragment.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 Lukas Eisenhauer
- *
- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with this program. If not, see