diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt deleted file mode 100644 index 9065093..0000000 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukas.music.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.lukas.music.R -import com.lukas.music.databinding.FragmentInstrumentBinding - -class InstrumentFragment : Fragment(R.layout.fragment_instrument) { - private lateinit var binding: FragmentInstrumentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentInstrumentBinding.inflate(inflater) - return binding.root - } -} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt deleted file mode 100644 index 9065093..0000000 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukas.music.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.lukas.music.R -import com.lukas.music.databinding.FragmentInstrumentBinding - -class InstrumentFragment : Fragment(R.layout.fragment_instrument) { - private lateinit var binding: FragmentInstrumentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentInstrumentBinding.inflate(inflater) - return binding.root - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_instrument.xml b/app/src/main/res/layout/fragment_instrument.xml index 1065e57..13222a3 100644 --- a/app/src/main/res/layout/fragment_instrument.xml +++ b/app/src/main/res/layout/fragment_instrument.xml @@ -4,14 +4,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="edit this instrument's properties" tools:context=".ui.fragments.InstrumentFragment"> - + + diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt deleted file mode 100644 index 9065093..0000000 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukas.music.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.lukas.music.R -import com.lukas.music.databinding.FragmentInstrumentBinding - -class InstrumentFragment : Fragment(R.layout.fragment_instrument) { - private lateinit var binding: FragmentInstrumentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentInstrumentBinding.inflate(inflater) - return binding.root - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_instrument.xml b/app/src/main/res/layout/fragment_instrument.xml index 1065e57..13222a3 100644 --- a/app/src/main/res/layout/fragment_instrument.xml +++ b/app/src/main/res/layout/fragment_instrument.xml @@ -4,14 +4,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="edit this instrument's properties" tools:context=".ui.fragments.InstrumentFragment"> - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4a9d97..f55f3ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,11 @@ this app was created by Lukas Eisenhauer start or stop the song placeholder text... + active + select the instrument waveform + edit this instrument\'s properties + + Sine + Sawtooth + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt deleted file mode 100644 index 9065093..0000000 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukas.music.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.lukas.music.R -import com.lukas.music.databinding.FragmentInstrumentBinding - -class InstrumentFragment : Fragment(R.layout.fragment_instrument) { - private lateinit var binding: FragmentInstrumentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentInstrumentBinding.inflate(inflater) - return binding.root - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_instrument.xml b/app/src/main/res/layout/fragment_instrument.xml index 1065e57..13222a3 100644 --- a/app/src/main/res/layout/fragment_instrument.xml +++ b/app/src/main/res/layout/fragment_instrument.xml @@ -4,14 +4,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="edit this instrument's properties" tools:context=".ui.fragments.InstrumentFragment"> - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4a9d97..f55f3ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,11 @@ this app was created by Lukas Eisenhauer start or stop the song placeholder text... + active + select the instrument waveform + edit this instrument\'s properties + + Sine + Sawtooth + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index cd0519b..0dee8cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,23 +1,4 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library android.nonTransitiveRClass=true \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8df1b08..4ba42b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.0' - implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/cpp/AudioHost.h b/app/src/main/cpp/AudioHost.h index d5309c4..d84ccda 100644 --- a/app/src/main/cpp/AudioHost.h +++ b/app/src/main/cpp/AudioHost.h @@ -3,7 +3,7 @@ class AudioHost; -#include "SineWave.h" +#include "waveforms/Sine.h" #include "Instrument.h" #include #include diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d1e0f76..6d9f302 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -7,7 +7,8 @@ SHARED - SineWave.cpp + waveforms/Sine.cpp + waveforms/Sawtooth.cpp JavaFunctions.cpp AudioHost.cpp Instrument.cpp diff --git a/app/src/main/cpp/Envelope.cpp b/app/src/main/cpp/Envelope.cpp index 8b9d12a..be0ae8b 100644 --- a/app/src/main/cpp/Envelope.cpp +++ b/app/src/main/cpp/Envelope.cpp @@ -6,8 +6,6 @@ attackIncrement = 1 / attack / host->sampleRate; delayIncrement = 1 / delay / host->sampleRate; releaseIncrement = 1 / release / host->sampleRate; - __android_log_print(ANDROID_LOG_DEBUG, "Envelope", "increments: %f %f %f", attackIncrement, - delayIncrement, releaseIncrement); } void Envelope::startNote() { diff --git a/app/src/main/cpp/Instrument.cpp b/app/src/main/cpp/Instrument.cpp index 6dacc78..5be6f92 100644 --- a/app/src/main/cpp/Instrument.cpp +++ b/app/src/main/cpp/Instrument.cpp @@ -1,6 +1,8 @@ #include "Instrument.h" +#include "waveforms/Sawtooth.h" Instrument::Instrument(AudioHost *host) { + this->host = host; wave->initialize(host); envelope->initialize(host); } @@ -15,7 +17,6 @@ void Instrument::render(float *buffer, uint32_t count) { float *modulation = envelope->render(count); wave->render(buffer, count); - multiply(buffer, modulation, count); } void Instrument::startNote(float frequency) { @@ -25,4 +26,17 @@ void Instrument::endNote() { envelope->endNote(); +} + +void Instrument::setWaveform(WaveformType waveform) { + // delete &wave; + switch (waveform) { + case SINE: + wave = new Sine(); + break; + case SAWTOOTH: + wave = new Sawtooth(); + break; + } + wave->initialize(host); } \ No newline at end of file diff --git a/app/src/main/cpp/Instrument.h b/app/src/main/cpp/Instrument.h index 8e4b402..191c084 100644 --- a/app/src/main/cpp/Instrument.h +++ b/app/src/main/cpp/Instrument.h @@ -3,22 +3,25 @@ class Instrument; -#include "SineWave.h" #include "AudioHost.h" #include "Envelope.h" class Instrument { +private: + AudioHost *host; public: Instrument(AudioHost *host); Envelope *const envelope = new Envelope(); - SineWave *const wave = new SineWave(); + Waveform *wave = new Sine(); void render(float *buffer, uint32_t count); void startNote(float frequency); void endNote(); + + void setWaveform(WaveformType waveform); }; #endif diff --git a/app/src/main/cpp/JavaFunctions.cpp b/app/src/main/cpp/JavaFunctions.cpp index 04e4757..6ebd2b0 100644 --- a/app/src/main/cpp/JavaFunctions.cpp +++ b/app/src/main/cpp/JavaFunctions.cpp @@ -63,4 +63,12 @@ Java_com_lukas_music_ui_fragments_PlayFragment_setMasterVolume(JNIEnv *env, jobject thiz, jdouble volume) { audioHost->masterVolume = volume; +} +extern "C" +JNIEXPORT void JNICALL +Java_com_lukas_music_instruments_InternalInstrument_setInstrumentWaveform(JNIEnv *env, jobject thiz, + jint id, jint waveform) { + Instrument *instrument = static_cast(listGet(audioHost->instruments->begin(), + id)); + instrument->setWaveform(static_cast(waveform)); } \ No newline at end of file diff --git a/app/src/main/cpp/SineWave.cpp b/app/src/main/cpp/SineWave.cpp deleted file mode 100644 index ac14843..0000000 --- a/app/src/main/cpp/SineWave.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AudioHost.h" -#include "SineWave.h" -#include -#include - -void SineWave::initialize(AudioHost *host) { - this->host = host; -} - -void SineWave::setFrequency(float freq) { - frequency = freq; - phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; -} - -void SineWave::render(float *data, uint32_t frameCount) { - for (uint32_t i = 0; i < frameCount; i++) { - data[i] += (float)(sin(phase) * amplitude); - phase += phaseStep; - if (phase > 2*M_PI) { - phase -= 2*M_PI; - } - } -} diff --git a/app/src/main/cpp/SineWave.h b/app/src/main/cpp/SineWave.h deleted file mode 100644 index 3d8df12..0000000 --- a/app/src/main/cpp/SineWave.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MUSIC_SINEWAVE_H -#define MUSIC_SINEWAVE_H - -class SineWave; - -#include -#include "AudioHost.h" - -class SineWave { -private: - float phaseStep, phase = 0; - float frequency; - AudioHost *host; -public: - void initialize(AudioHost *host); - float amplitude = 0.0f; - void render(float *data, uint32_t frameCount); - void setFrequency(float freq); -}; - -#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Sawtooth.cpp b/app/src/main/cpp/waveforms/Sawtooth.cpp new file mode 100644 index 0000000..630d335 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.cpp @@ -0,0 +1,19 @@ +#include "Sawtooth.h" + +void Sawtooth::initialize(AudioHost *host) { + this->host = host; +} + +void Sawtooth::setFrequency(float frequency) { + step = frequency / (double) host->sampleRate; +} + +void Sawtooth::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += value * amplitude; + value += step; + if (value > 1) { + value = 0; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sawtooth.h b/app/src/main/cpp/waveforms/Sawtooth.h new file mode 100644 index 0000000..b307371 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sawtooth.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SAWTOOTH_H +#define MUSIC_SAWTOOTH_H + + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sawtooth : public Waveform { +private: + float value = 0, step = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + + +#endif diff --git a/app/src/main/cpp/waveforms/Sine.cpp b/app/src/main/cpp/waveforms/Sine.cpp new file mode 100644 index 0000000..f692542 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.cpp @@ -0,0 +1,22 @@ +#include "../AudioHost.h" +#include "Sine.h" +#include +#include + +void Sine::initialize(AudioHost *host) { + this->host = host; +} + +void Sine::setFrequency(float frequency) { + phaseStep = (2 * M_PI * frequency) / (double) host->sampleRate; +} + +void Sine::render(float *data, uint32_t frameCount) { + for (uint32_t i = 0; i < frameCount; i++) { + data[i] += (float) (sin(phase) * amplitude); + phase += phaseStep; + if (phase > 2 * M_PI) { + phase -= 2 * M_PI; + } + } +} diff --git a/app/src/main/cpp/waveforms/Sine.h b/app/src/main/cpp/waveforms/Sine.h new file mode 100644 index 0000000..93dc620 --- /dev/null +++ b/app/src/main/cpp/waveforms/Sine.h @@ -0,0 +1,21 @@ +#ifndef MUSIC_SINE_H +#define MUSIC_SINE_H + +class Sine; + +#include +#include "../AudioHost.h" +#include "Waveform.h" + +class Sine : public Waveform { +private: + float phaseStep, phase = 0; +public: + void initialize(AudioHost *host); + + void render(float *data, uint32_t frameCount); + + void setFrequency(float freq); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/waveforms/Waveform.h b/app/src/main/cpp/waveforms/Waveform.h new file mode 100644 index 0000000..4f94b8e --- /dev/null +++ b/app/src/main/cpp/waveforms/Waveform.h @@ -0,0 +1,23 @@ +#ifndef MUSIC_WAVEFORM_H +#define MUSIC_WAVEFORM_H + +enum WaveformType { + SINE = 0, + SAWTOOTH = 1, +}; + +class Waveform { +protected: + AudioHost *host; +public: + float amplitude = 0.3f; + + virtual void initialize(AudioHost *host) = 0; + + virtual void render(float *data, uint32_t frameCount) = 0; + + virtual void setFrequency(float freq) = 0; +}; + + +#endif diff --git a/app/src/main/java/com/lukas/music/instruments/Instrument.kt b/app/src/main/java/com/lukas/music/instruments/Instrument.kt index 75307c3..f96edaa 100644 --- a/app/src/main/java/com/lukas/music/instruments/Instrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/Instrument.kt @@ -8,10 +8,11 @@ abstract class Instrument(private var name: String) { private var active = false + abstract var waveform: Waveform fun applyToView(binding: FragmentInstrumentBinding) { binding.instrumentNameText.text = name - binding.floatingActionButton2.setOnClickListener { + binding.editInstrumentButton.setOnClickListener { println("click instrument $name") } binding.activeSwitch.setOnCheckedChangeListener { _, newActive -> diff --git a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt index 1a4d91b..d524f58 100644 --- a/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/InternalInstrument.kt @@ -3,12 +3,18 @@ class InternalInstrument { private val id = createInstrument() var active: Boolean = false - get() = active set(value) { field = value setInstrumentActive(id, value) } + var waveform: Waveform = Waveform.SINE + set(value) { + field = value + setInstrumentWaveform(id, value.id) + println("set instrument to $value") + } + fun startNote(frequency: Double) { startNote(id, frequency) } @@ -19,6 +25,7 @@ private external fun createInstrument(): Int private external fun setInstrumentActive(id: Int, isActive: Boolean) + private external fun setInstrumentWaveform(id: Int, waveform: Int) private external fun startNote(id: Int, frequency: Double) private external fun endNote(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt index c3c8acc..100c0f9 100644 --- a/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/MonoInstrument.kt @@ -5,6 +5,12 @@ class MonoInstrument(name: String) : Instrument(name) { private val internalInstrument = InternalInstrument() + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + internalInstrument.waveform = value + } + override fun startNote(note: Note) { internalInstrument.startNote(note.frequency) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index 923c1f6..fd6bfc2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -6,6 +6,14 @@ private val internalInstruments = Array(3) { InternalInstrument() } private val playing = Array(3) { false } + override var waveform: Waveform = Waveform.SINE + set(value) { + field = value + for (internalInstrument in internalInstruments) { + internalInstrument.waveform = value + } + } + override fun startNote(note: Note) { for ((index, instrumentPlaying) in playing.withIndex()) { if (!instrumentPlaying) { diff --git a/app/src/main/java/com/lukas/music/instruments/Waveform.kt b/app/src/main/java/com/lukas/music/instruments/Waveform.kt new file mode 100644 index 0000000..a7d5dd1 --- /dev/null +++ b/app/src/main/java/com/lukas/music/instruments/Waveform.kt @@ -0,0 +1,16 @@ +package com.lukas.music.instruments + +enum class Waveform(val id: Int, private val identifier: String) { + SINE(0, "sine"), + SAWTOOTH(1, "sawtooth"), + ; + + override fun toString(): String { + return identifier + } + + companion object { + val VALUES = values() + val DEFAULT = SINE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/Chord.kt b/app/src/main/java/com/lukas/music/song/Chord.kt index 807ce6c..36ad846 100644 --- a/app/src/main/java/com/lukas/music/song/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/Chord.kt @@ -2,7 +2,7 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, val chordType: ChordType) { +class Chord(val note: Int, private val chordType: ChordType) { fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } } diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index 09879f0..cd59930 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -2,7 +2,6 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class Song( private val root: Note, @@ -14,9 +13,7 @@ fun step() { val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { - if (beat in voice.steps) { voice.step(root, chordNotes) - } } beat++ if (beat > 4) { @@ -27,7 +24,7 @@ companion object { var currentSong = Song( - Note.of(NoteName.F, 4), + Note.NOTES[69], ChordProgression( listOf( Chord(0, ChordType.Major), @@ -37,9 +34,5 @@ ) ) ) - - init { - println("root: ${currentSong.root.frequency}") - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index b2111a7..cd3171f 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -2,10 +2,10 @@ import kotlin.math.pow -class Note(val id: Int) { - private val noteName = NoteName.VALUES[id % NoteName.VALUES.size] - val octave = id / NoteName.VALUES.size - 1 - val frequency = noteName.baseFrequency * 2.0.pow(id / NoteName.VALUES.size - 5) +class Note(private val id: Int) { + private val noteName = NoteName.VALUES[id % 12] + val octave = id / 12 - 1 + val frequency = 440 * 2.0.pow((id - 69) / 12.0) operator fun plus(other: Int): Note { if (id + other < 0 || id + other > 127) { @@ -22,7 +22,7 @@ val NOTES = Array(128) { Note(it) } fun of(noteName: NoteName, octave: Int): Note { - return NOTES[NoteName.VALUES.size * (octave + 1) + noteName.index] + return NOTES[12 * (octave + 1) + noteName.index] } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/NoteName.kt b/app/src/main/java/com/lukas/music/song/note/NoteName.kt index 5b58671..49c19b2 100644 --- a/app/src/main/java/com/lukas/music/song/note/NoteName.kt +++ b/app/src/main/java/com/lukas/music/song/note/NoteName.kt @@ -2,7 +2,7 @@ import kotlin.math.pow -const val A4 = 261.63 +const val A4 = 440.0 enum class NoteName(val index: Int, val asString: String) { C(0, "C"), diff --git a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt index ae7e16b..b67965d 100644 --- a/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt +++ b/app/src/main/java/com/lukas/music/song/voice/BassVoice.kt @@ -2,12 +2,11 @@ import com.lukas.music.instruments.Instrument import com.lukas.music.song.note.Note -import com.lukas.music.song.note.NoteName class BassVoice(instrument: Instrument) : Voice(instrument) { override val steps = listOf(1, 3) override fun step(root: Note, chord: Array) { - instrument.startNote(chord[0] - NoteName.VALUES.size * 2) + instrument.startNote(chord[0] - 24) } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt index 79cc4ce..d6900a7 100644 --- a/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt +++ b/app/src/main/java/com/lukas/music/ui/InstrumentAdapter.kt @@ -1,15 +1,42 @@ package com.lukas.music.ui import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import com.lukas.music.databinding.FragmentInstrumentBinding import com.lukas.music.instruments.Instrument +import com.lukas.music.instruments.Waveform class InstrumentAdapter : RecyclerView.Adapter() { class InstrumentViewHolder(val binding: FragmentInstrumentBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), AdapterView.OnItemSelectedListener { + lateinit var instrument: Instrument + + init { + val adapter = ArrayAdapter( + binding.root.context, + android.R.layout.simple_spinner_dropdown_item, Waveform.VALUES + ) + binding.waveformSelection.adapter = adapter + binding.waveformSelection.onItemSelectedListener = this + } + + override fun onItemSelected( + adapterView: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + instrument.waveform = Waveform.VALUES[position] + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) { + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstrumentViewHolder { val context = parent.context @@ -19,8 +46,9 @@ } override fun onBindViewHolder(holder: InstrumentViewHolder, position: Int) { - val internalInstrument = Instrument.instruments[position] - internalInstrument.applyToView(holder.binding) + val instrument = Instrument.instruments[position] + holder.instrument = instrument + instrument.applyToView(holder.binding) } override fun getItemCount(): Int { diff --git a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt deleted file mode 100644 index 9065093..0000000 --- a/app/src/main/java/com/lukas/music/ui/fragments/InstrumentFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukas.music.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.lukas.music.R -import com.lukas.music.databinding.FragmentInstrumentBinding - -class InstrumentFragment : Fragment(R.layout.fragment_instrument) { - private lateinit var binding: FragmentInstrumentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentInstrumentBinding.inflate(inflater) - return binding.root - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_instrument.xml b/app/src/main/res/layout/fragment_instrument.xml index 1065e57..13222a3 100644 --- a/app/src/main/res/layout/fragment_instrument.xml +++ b/app/src/main/res/layout/fragment_instrument.xml @@ -4,14 +4,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="edit this instrument's properties" tools:context=".ui.fragments.InstrumentFragment"> - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4a9d97..f55f3ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,11 @@ this app was created by Lukas Eisenhauer start or stop the song placeholder text... + active + select the instrument waveform + edit this instrument\'s properties + + Sine + Sawtooth + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index cd0519b..0dee8cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,23 +1,4 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library android.nonTransitiveRClass=true \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e67cead..12d0f8b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,4 +13,4 @@ } } rootProject.name = "Music" -include ':app' +include ':app' \ No newline at end of file